@cortexkit/aft-opencode 0.30.1 → 0.30.2
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/bg-notifications.d.ts +2 -0
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/index.d.ts +1 -1
- package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +829 -481
- package/dist/notifications.d.ts +8 -5
- package/dist/notifications.d.ts.map +1 -1
- package/dist/shared/last-assistant-model.d.ts +5 -6
- package/dist/shared/last-assistant-model.d.ts.map +1 -1
- package/dist/shared/live-server-client.d.ts +24 -22
- package/dist/shared/live-server-client.d.ts.map +1 -1
- package/dist/shared/rpc-client.d.ts +10 -3
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/storage-paths.d.ts +2 -0
- package/dist/shared/storage-paths.d.ts.map +1 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash_watch.d.ts +4 -0
- package/dist/tools/bash_watch.d.ts.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tui.js +222 -123
- package/package.json +8 -8
- package/src/shared/last-assistant-model.ts +9 -27
- package/src/shared/live-server-client.ts +80 -48
- package/src/shared/rpc-client.ts +132 -28
- package/src/shared/storage-paths.ts +22 -0
- package/src/tui/index.tsx +39 -17
- package/src/tui/sidebar.tsx +94 -27
package/dist/index.js
CHANGED
|
@@ -12399,7 +12399,7 @@ var require_snapshot_utils = __commonJS((exports, module) => {
|
|
|
12399
12399
|
// ../../node_modules/.bun/undici@7.25.0/node_modules/undici/lib/mock/snapshot-recorder.js
|
|
12400
12400
|
var require_snapshot_recorder = __commonJS((exports, module) => {
|
|
12401
12401
|
var { writeFile, readFile, mkdir } = __require("node:fs/promises");
|
|
12402
|
-
var { dirname:
|
|
12402
|
+
var { dirname: dirname4, resolve: resolve2 } = __require("node:path");
|
|
12403
12403
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
|
|
12404
12404
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
12405
12405
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -12594,7 +12594,7 @@ var require_snapshot_recorder = __commonJS((exports, module) => {
|
|
|
12594
12594
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
12595
12595
|
}
|
|
12596
12596
|
const resolvedPath = resolve2(path);
|
|
12597
|
-
await mkdir(
|
|
12597
|
+
await mkdir(dirname4(resolvedPath), { recursive: true });
|
|
12598
12598
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
12599
12599
|
hash,
|
|
12600
12600
|
snapshot
|
|
@@ -29810,10 +29810,10 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
29810
29810
|
replacer = null;
|
|
29811
29811
|
indent = EMPTY;
|
|
29812
29812
|
};
|
|
29813
|
-
var
|
|
29813
|
+
var join9 = (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;
|
|
29814
29814
|
var join_content = (inside, value, gap) => {
|
|
29815
29815
|
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
|
|
29816
|
-
return
|
|
29816
|
+
return join9(comment, inside, gap);
|
|
29817
29817
|
};
|
|
29818
29818
|
var stringify_string = (holder, key, value) => {
|
|
29819
29819
|
const raw = get_raw_string_literal(holder, key);
|
|
@@ -29835,13 +29835,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
29835
29835
|
if (i !== 0) {
|
|
29836
29836
|
inside += COMMA;
|
|
29837
29837
|
}
|
|
29838
|
-
const before =
|
|
29838
|
+
const before = join9(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
|
|
29839
29839
|
inside += before || LF + deeper_gap;
|
|
29840
29840
|
inside += stringify(i, value, deeper_gap) || STR_NULL;
|
|
29841
29841
|
inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
|
|
29842
29842
|
after_comma = process_comments(value, AFTER(i), deeper_gap);
|
|
29843
29843
|
}
|
|
29844
|
-
inside +=
|
|
29844
|
+
inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
29845
29845
|
return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
|
|
29846
29846
|
};
|
|
29847
29847
|
var object_stringify = (value, gap) => {
|
|
@@ -29862,13 +29862,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
29862
29862
|
inside += COMMA;
|
|
29863
29863
|
}
|
|
29864
29864
|
first = false;
|
|
29865
|
-
const before =
|
|
29865
|
+
const before = join9(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
|
|
29866
29866
|
inside += before || LF + deeper_gap;
|
|
29867
29867
|
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);
|
|
29868
29868
|
after_comma = process_comments(value, AFTER(key), deeper_gap);
|
|
29869
29869
|
};
|
|
29870
29870
|
keys.forEach(iteratee);
|
|
29871
|
-
inside +=
|
|
29871
|
+
inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
29872
29872
|
return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
|
|
29873
29873
|
};
|
|
29874
29874
|
function stringify(key, holder, gap) {
|
|
@@ -33691,9 +33691,9 @@ var require_xterm_headless = __commonJS((exports) => {
|
|
|
33691
33691
|
});
|
|
33692
33692
|
|
|
33693
33693
|
// src/index.ts
|
|
33694
|
-
import { existsSync as
|
|
33694
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "node:fs";
|
|
33695
33695
|
import { createRequire as createRequire3 } from "node:module";
|
|
33696
|
-
import {
|
|
33696
|
+
import { dirname as dirname14 } from "node:path";
|
|
33697
33697
|
|
|
33698
33698
|
// ../aft-bridge/dist/active-logger.js
|
|
33699
33699
|
var ACTIVE_LOGGER_SYMBOL = Symbol.for("aft-bridge-active-logger");
|
|
@@ -33982,6 +33982,11 @@ class BinaryBridge {
|
|
|
33982
33982
|
if (requestSessionId && options?.configureWarningClient !== undefined) {
|
|
33983
33983
|
this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
|
|
33984
33984
|
}
|
|
33985
|
+
const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
|
|
33986
|
+
const implicitTransportOptions = {
|
|
33987
|
+
...options?.transportTimeoutMs !== undefined || options?.timeoutMs !== undefined ? { transportTimeoutMs: effectiveTimeoutMs } : {},
|
|
33988
|
+
markConfiguredOnSuccess: false
|
|
33989
|
+
};
|
|
33985
33990
|
if (!this.configured) {
|
|
33986
33991
|
if (command !== "configure" && command !== "version") {
|
|
33987
33992
|
if (!this._configurePromise) {
|
|
@@ -33992,12 +33997,12 @@ class BinaryBridge {
|
|
|
33992
33997
|
project_root: this.cwd,
|
|
33993
33998
|
...this.configOverrides,
|
|
33994
33999
|
...sessionIdForConfigure ? { session_id: sessionIdForConfigure } : {}
|
|
33995
|
-
});
|
|
34000
|
+
}, implicitTransportOptions);
|
|
33996
34001
|
if (configResult.success === false) {
|
|
33997
34002
|
throw new Error(`${this.errorPrefix} Configure failed: ${configResult.message ?? "unknown error"}`);
|
|
33998
34003
|
}
|
|
33999
34004
|
await this.deliverConfigureWarnings(configResult, params, options);
|
|
34000
|
-
await this.checkVersion();
|
|
34005
|
+
await this.checkVersion(implicitTransportOptions);
|
|
34001
34006
|
if (!this.isAlive()) {
|
|
34002
34007
|
throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${this.getLogFilePathVia()}`);
|
|
34003
34008
|
}
|
|
@@ -34027,9 +34032,8 @@ class BinaryBridge {
|
|
|
34027
34032
|
}
|
|
34028
34033
|
const line = `${JSON.stringify(request)}
|
|
34029
34034
|
`;
|
|
34030
|
-
const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
|
|
34031
34035
|
const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
|
|
34032
|
-
|
|
34036
|
+
const response = await new Promise((resolve, reject) => {
|
|
34033
34037
|
const timer = setTimeout(() => {
|
|
34034
34038
|
this.pending.delete(id);
|
|
34035
34039
|
const restartSuffix = keepBridgeOnTimeout ? "" : " — restarting bridge";
|
|
@@ -34062,6 +34066,10 @@ class BinaryBridge {
|
|
|
34062
34066
|
}
|
|
34063
34067
|
});
|
|
34064
34068
|
});
|
|
34069
|
+
if (command === "configure" && response.success === true && options?.markConfiguredOnSuccess !== false) {
|
|
34070
|
+
this.configured = true;
|
|
34071
|
+
}
|
|
34072
|
+
return response;
|
|
34065
34073
|
} catch (err) {
|
|
34066
34074
|
if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
|
|
34067
34075
|
this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
|
|
@@ -34152,11 +34160,11 @@ class BinaryBridge {
|
|
|
34152
34160
|
});
|
|
34153
34161
|
}
|
|
34154
34162
|
}
|
|
34155
|
-
async checkVersion() {
|
|
34163
|
+
async checkVersion(options) {
|
|
34156
34164
|
if (!this.minVersion)
|
|
34157
34165
|
return;
|
|
34158
34166
|
try {
|
|
34159
|
-
const resp = await this.send("version");
|
|
34167
|
+
const resp = await this.send("version", {}, options);
|
|
34160
34168
|
if (resp.success === false) {
|
|
34161
34169
|
throw new Error(`Binary version check failed: ${String(resp.code ?? "unknown")} — likely too old`);
|
|
34162
34170
|
}
|
|
@@ -34231,10 +34239,14 @@ class BinaryBridge {
|
|
|
34231
34239
|
...process.env,
|
|
34232
34240
|
...envPath ? { PATH: envPath } : {}
|
|
34233
34241
|
};
|
|
34242
|
+
this.logVia(`bridge.spawnProcess: useFastembedBackend=${useFastembedBackend}, ` + `parentORT=${process.env.ORT_DYLIB_PATH ?? "(unset)"}, ` + `ortLibraryPath=${ortLibraryPath ?? "(none)"}`);
|
|
34234
34243
|
if (useFastembedBackend) {
|
|
34235
34244
|
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"));
|
|
34236
|
-
if (
|
|
34245
|
+
if (process.env.ORT_DYLIB_PATH) {
|
|
34246
|
+
this.logVia(`ORT_DYLIB_PATH inherited from parent env: ${process.env.ORT_DYLIB_PATH}`);
|
|
34247
|
+
} else if (ortLibraryPath) {
|
|
34237
34248
|
env.ORT_DYLIB_PATH = ortLibraryPath;
|
|
34249
|
+
this.logVia(`ORT_DYLIB_PATH set from managed ONNX Runtime: ${ortLibraryPath}`);
|
|
34238
34250
|
}
|
|
34239
34251
|
}
|
|
34240
34252
|
const child = spawn(this.binaryPath, [], {
|
|
@@ -34480,8 +34492,9 @@ class BinaryBridge {
|
|
|
34480
34492
|
}
|
|
34481
34493
|
}
|
|
34482
34494
|
// ../aft-bridge/dist/downloader.js
|
|
34483
|
-
import {
|
|
34484
|
-
import {
|
|
34495
|
+
import { spawnSync } from "node:child_process";
|
|
34496
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
34497
|
+
import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
34485
34498
|
import { homedir as homedir2 } from "node:os";
|
|
34486
34499
|
import { join as join2 } from "node:path";
|
|
34487
34500
|
import { Readable } from "node:stream";
|
|
@@ -34509,6 +34522,34 @@ var LATEST_TAG_TIMEOUT_MS = 30000;
|
|
|
34509
34522
|
var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
|
|
34510
34523
|
var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
|
|
34511
34524
|
var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
|
|
34525
|
+
function readBinaryVersion(binaryPath) {
|
|
34526
|
+
try {
|
|
34527
|
+
const result = spawnSync(binaryPath, ["--version"], {
|
|
34528
|
+
encoding: "utf-8",
|
|
34529
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
34530
|
+
timeout: 5000
|
|
34531
|
+
});
|
|
34532
|
+
const stdoutVersion = result.stdout?.trim();
|
|
34533
|
+
const stderrVersion = result.stderr?.trim();
|
|
34534
|
+
const rawVersion = stdoutVersion || stderrVersion;
|
|
34535
|
+
if (!rawVersion)
|
|
34536
|
+
return null;
|
|
34537
|
+
return rawVersion.replace(/^aft\s+/, "");
|
|
34538
|
+
} catch {
|
|
34539
|
+
return null;
|
|
34540
|
+
}
|
|
34541
|
+
}
|
|
34542
|
+
function expectedVersionFromTag(tag) {
|
|
34543
|
+
return tag.startsWith("v") ? tag.slice(1) : tag;
|
|
34544
|
+
}
|
|
34545
|
+
function isExpectedCachedBinary(binaryPath, tag) {
|
|
34546
|
+
const expected = expectedVersionFromTag(tag);
|
|
34547
|
+
const actual = readBinaryVersion(binaryPath);
|
|
34548
|
+
if (actual === expected)
|
|
34549
|
+
return true;
|
|
34550
|
+
warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; refreshing cache entry`);
|
|
34551
|
+
return false;
|
|
34552
|
+
}
|
|
34512
34553
|
function getCacheDir() {
|
|
34513
34554
|
if (process.platform === "win32") {
|
|
34514
34555
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
@@ -34544,7 +34585,7 @@ async function downloadBinary(version) {
|
|
|
34544
34585
|
const versionedCacheDir = join2(getCacheDir(), tag);
|
|
34545
34586
|
const binaryName = getBinaryName();
|
|
34546
34587
|
const binaryPath = join2(versionedCacheDir, binaryName);
|
|
34547
|
-
if (existsSync(binaryPath)) {
|
|
34588
|
+
if (existsSync(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
|
|
34548
34589
|
return binaryPath;
|
|
34549
34590
|
}
|
|
34550
34591
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
|
|
@@ -34562,7 +34603,7 @@ async function downloadBinary(version) {
|
|
|
34562
34603
|
mkdirSync(versionedCacheDir, { recursive: true });
|
|
34563
34604
|
}
|
|
34564
34605
|
releaseLock = await acquireDownloadLock(lockPath);
|
|
34565
|
-
if (existsSync(binaryPath)) {
|
|
34606
|
+
if (existsSync(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
|
|
34566
34607
|
return binaryPath;
|
|
34567
34608
|
}
|
|
34568
34609
|
binaryController = new AbortController;
|
|
@@ -34651,7 +34692,7 @@ async function ensureBinary(version) {
|
|
|
34651
34692
|
if (version) {
|
|
34652
34693
|
const tag = version.startsWith("v") ? version : `v${version}`;
|
|
34653
34694
|
const versionCached = getCachedBinaryPath(tag);
|
|
34654
|
-
if (versionCached) {
|
|
34695
|
+
if (versionCached && isExpectedCachedBinary(versionCached, tag)) {
|
|
34655
34696
|
log(`Found cached binary for ${tag}: ${versionCached}`);
|
|
34656
34697
|
return versionCached;
|
|
34657
34698
|
}
|
|
@@ -34665,13 +34706,17 @@ async function acquireDownloadLock(lockPath) {
|
|
|
34665
34706
|
const startedAt = Date.now();
|
|
34666
34707
|
while (true) {
|
|
34667
34708
|
try {
|
|
34709
|
+
const owner = `${process.pid}:${Date.now()}:${randomUUID()}`;
|
|
34668
34710
|
const fd = openSync(lockPath, "wx");
|
|
34711
|
+
writeSync(fd, owner);
|
|
34669
34712
|
return () => {
|
|
34670
34713
|
try {
|
|
34671
34714
|
closeSync(fd);
|
|
34672
34715
|
} catch {}
|
|
34673
34716
|
try {
|
|
34674
|
-
|
|
34717
|
+
if (readFileSync(lockPath, "utf-8") === owner) {
|
|
34718
|
+
rmSync(lockPath, { force: true });
|
|
34719
|
+
}
|
|
34675
34720
|
} catch {}
|
|
34676
34721
|
};
|
|
34677
34722
|
} catch (err) {
|
|
@@ -34727,34 +34772,35 @@ async function fetchLatestTag() {
|
|
|
34727
34772
|
}
|
|
34728
34773
|
// ../aft-bridge/dist/migration.js
|
|
34729
34774
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
34730
|
-
import { existsSync as
|
|
34775
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
34731
34776
|
import { homedir as homedir4, tmpdir } from "node:os";
|
|
34732
|
-
import { dirname, join as
|
|
34777
|
+
import { dirname as dirname2, join as join5 } from "node:path";
|
|
34778
|
+
|
|
34779
|
+
// ../aft-bridge/dist/paths.js
|
|
34780
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync as renameSync2 } from "node:fs";
|
|
34781
|
+
import { dirname, join as join3 } from "node:path";
|
|
34782
|
+
function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
|
|
34783
|
+
return join3(storageRoot, harness, ...segments);
|
|
34784
|
+
}
|
|
34785
|
+
function repairRootScopedStorageFile(storageRoot, harness, fileName) {
|
|
34786
|
+
const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
|
|
34787
|
+
const rootPath = join3(storageRoot, fileName);
|
|
34788
|
+
if (existsSync2(harnessPath) || !existsSync2(rootPath))
|
|
34789
|
+
return harnessPath;
|
|
34790
|
+
try {
|
|
34791
|
+
mkdirSync2(dirname(harnessPath), { recursive: true });
|
|
34792
|
+
renameSync2(rootPath, harnessPath);
|
|
34793
|
+
} catch {}
|
|
34794
|
+
return harnessPath;
|
|
34795
|
+
}
|
|
34733
34796
|
|
|
34734
34797
|
// ../aft-bridge/dist/resolver.js
|
|
34735
|
-
import { execSync
|
|
34736
|
-
import { chmodSync as chmodSync2, copyFileSync, existsSync as
|
|
34798
|
+
import { execSync } from "node:child_process";
|
|
34799
|
+
import { chmodSync as chmodSync2, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync3 } from "node:fs";
|
|
34737
34800
|
import { createRequire as createRequire2 } from "node:module";
|
|
34738
34801
|
import { homedir as homedir3 } from "node:os";
|
|
34739
|
-
import { join as
|
|
34802
|
+
import { join as join4 } from "node:path";
|
|
34740
34803
|
var ensureBinaryForResolver = ensureBinary;
|
|
34741
|
-
function readBinaryVersion(binaryPath) {
|
|
34742
|
-
try {
|
|
34743
|
-
const result = spawnSync(binaryPath, ["--version"], {
|
|
34744
|
-
encoding: "utf-8",
|
|
34745
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
34746
|
-
timeout: 5000
|
|
34747
|
-
});
|
|
34748
|
-
const stdoutVersion = result.stdout?.trim();
|
|
34749
|
-
const stderrVersion = result.stderr?.trim();
|
|
34750
|
-
const rawVersion = stdoutVersion || stderrVersion;
|
|
34751
|
-
if (!rawVersion)
|
|
34752
|
-
return null;
|
|
34753
|
-
return rawVersion.replace(/^aft\s+/, "");
|
|
34754
|
-
} catch {
|
|
34755
|
-
return null;
|
|
34756
|
-
}
|
|
34757
|
-
}
|
|
34758
34804
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
34759
34805
|
try {
|
|
34760
34806
|
const version = knownVersion ?? readBinaryVersion(npmBinaryPath);
|
|
@@ -34762,22 +34808,22 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
34762
34808
|
return null;
|
|
34763
34809
|
const tag = version.startsWith("v") ? version : `v${version}`;
|
|
34764
34810
|
const cacheDir = getCacheDir();
|
|
34765
|
-
const versionedDir =
|
|
34811
|
+
const versionedDir = join4(cacheDir, tag);
|
|
34766
34812
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
34767
|
-
const cachedPath =
|
|
34768
|
-
if (
|
|
34813
|
+
const cachedPath = join4(versionedDir, `aft${ext}`);
|
|
34814
|
+
if (existsSync3(cachedPath)) {
|
|
34769
34815
|
const cachedVersion = readBinaryVersion(cachedPath);
|
|
34770
34816
|
if (cachedVersion === version)
|
|
34771
34817
|
return cachedPath;
|
|
34772
34818
|
warn(`Cached binary at ${cachedPath} reports ${cachedVersion ?? "no version"}, expected ${version}; refreshing from npm package`);
|
|
34773
34819
|
}
|
|
34774
|
-
|
|
34820
|
+
mkdirSync3(versionedDir, { recursive: true });
|
|
34775
34821
|
const tmpPath = `${cachedPath}.${process.pid}.${Date.now()}.tmp`;
|
|
34776
34822
|
copyFileSync(npmBinaryPath, tmpPath);
|
|
34777
34823
|
if (process.platform !== "win32") {
|
|
34778
34824
|
chmodSync2(tmpPath, 493);
|
|
34779
34825
|
}
|
|
34780
|
-
|
|
34826
|
+
renameSync3(tmpPath, cachedPath);
|
|
34781
34827
|
log(`Copied npm binary to versioned cache: ${cachedPath}`);
|
|
34782
34828
|
return cachedPath;
|
|
34783
34829
|
} catch (err) {
|
|
@@ -34793,17 +34839,17 @@ function homeDirFromEnv(env) {
|
|
|
34793
34839
|
}
|
|
34794
34840
|
function cacheDirFromEnv(env) {
|
|
34795
34841
|
if (process.platform === "win32") {
|
|
34796
|
-
const base2 = env.LOCALAPPDATA || env.APPDATA ||
|
|
34797
|
-
return
|
|
34842
|
+
const base2 = env.LOCALAPPDATA || env.APPDATA || join4(homeDirFromEnv(env), "AppData", "Local");
|
|
34843
|
+
return join4(base2, "aft", "bin");
|
|
34798
34844
|
}
|
|
34799
|
-
const base = env.XDG_CACHE_HOME ||
|
|
34800
|
-
return
|
|
34845
|
+
const base = env.XDG_CACHE_HOME || join4(homeDirFromEnv(env), ".cache");
|
|
34846
|
+
return join4(base, "aft", "bin");
|
|
34801
34847
|
}
|
|
34802
34848
|
function cachedBinaryPathFromEnv(version, env, ext) {
|
|
34803
|
-
const binaryPath =
|
|
34804
|
-
return
|
|
34849
|
+
const binaryPath = join4(cacheDirFromEnv(env), version, `aft${ext}`);
|
|
34850
|
+
return existsSync3(binaryPath) ? binaryPath : null;
|
|
34805
34851
|
}
|
|
34806
|
-
function
|
|
34852
|
+
function isExpectedCachedBinary2(binaryPath, expectedVersion) {
|
|
34807
34853
|
const expected = normalizeBareVersion(expectedVersion);
|
|
34808
34854
|
const actual = readBinaryVersion(binaryPath);
|
|
34809
34855
|
if (actual === expected)
|
|
@@ -34811,6 +34857,21 @@ function isExpectedCachedBinary(binaryPath, expectedVersion) {
|
|
|
34811
34857
|
warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; skipping cache candidate`);
|
|
34812
34858
|
return false;
|
|
34813
34859
|
}
|
|
34860
|
+
function probeBinaryCandidate(binaryPath, source, expectedVersion) {
|
|
34861
|
+
const actual = readBinaryVersion(binaryPath);
|
|
34862
|
+
if (actual === null) {
|
|
34863
|
+
warn(`${source} binary at ${binaryPath} did not report a version; skipping`);
|
|
34864
|
+
return null;
|
|
34865
|
+
}
|
|
34866
|
+
if (expectedVersion && actual !== normalizeBareVersion(expectedVersion)) {
|
|
34867
|
+
warn(`${source} binary at ${binaryPath} reports ${actual}, expected ${normalizeBareVersion(expectedVersion)}; skipping`);
|
|
34868
|
+
return null;
|
|
34869
|
+
}
|
|
34870
|
+
return binaryPath;
|
|
34871
|
+
}
|
|
34872
|
+
function parsePathLookupOutput(output) {
|
|
34873
|
+
return output.split(/\r?\n/).map((candidate) => candidate.trim()).filter(Boolean);
|
|
34874
|
+
}
|
|
34814
34875
|
function platformKey(platform = process.platform, arch = process.arch) {
|
|
34815
34876
|
const archMap = PLATFORM_ARCH_MAP[platform];
|
|
34816
34877
|
if (!archMap) {
|
|
@@ -34836,7 +34897,7 @@ function findBinarySync(expectedVersion) {
|
|
|
34836
34897
|
if (pluginVersion) {
|
|
34837
34898
|
const tag = pluginVersion.startsWith("v") ? pluginVersion : `v${pluginVersion}`;
|
|
34838
34899
|
const versionCached = cachedBinaryPathFromEnv(tag, env, ext);
|
|
34839
|
-
if (versionCached &&
|
|
34900
|
+
if (versionCached && isExpectedCachedBinary2(versionCached, pluginVersion))
|
|
34840
34901
|
return versionCached;
|
|
34841
34902
|
}
|
|
34842
34903
|
try {
|
|
@@ -34844,7 +34905,7 @@ function findBinarySync(expectedVersion) {
|
|
|
34844
34905
|
const packageBin = `@cortexkit/aft-${key}/bin/aft${ext}`;
|
|
34845
34906
|
const req = createRequire2(import.meta.url);
|
|
34846
34907
|
const resolved = req.resolve(packageBin);
|
|
34847
|
-
if (
|
|
34908
|
+
if (existsSync3(resolved)) {
|
|
34848
34909
|
const npmVersion = readBinaryVersion(resolved);
|
|
34849
34910
|
if (npmVersion === null) {
|
|
34850
34911
|
warn(`npm platform package binary at ${resolved} did not report a version; skipping (continuing to PATH lookup)`);
|
|
@@ -34863,12 +34924,18 @@ function findBinarySync(expectedVersion) {
|
|
|
34863
34924
|
env,
|
|
34864
34925
|
stdio: ["pipe", "pipe", "pipe"]
|
|
34865
34926
|
}).trim();
|
|
34866
|
-
|
|
34867
|
-
|
|
34927
|
+
for (const candidate of parsePathLookupOutput(result)) {
|
|
34928
|
+
const usable = probeBinaryCandidate(candidate, "PATH", expectedVersion);
|
|
34929
|
+
if (usable)
|
|
34930
|
+
return usable;
|
|
34931
|
+
}
|
|
34868
34932
|
} catch {}
|
|
34869
|
-
const cargoPath =
|
|
34870
|
-
if (
|
|
34871
|
-
|
|
34933
|
+
const cargoPath = join4(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
|
|
34934
|
+
if (existsSync3(cargoPath)) {
|
|
34935
|
+
const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
|
|
34936
|
+
if (usable)
|
|
34937
|
+
return usable;
|
|
34938
|
+
}
|
|
34872
34939
|
return null;
|
|
34873
34940
|
}
|
|
34874
34941
|
async function findBinary(expectedVersion) {
|
|
@@ -34909,9 +34976,9 @@ function dataHome() {
|
|
|
34909
34976
|
if (process.env.XDG_DATA_HOME)
|
|
34910
34977
|
return process.env.XDG_DATA_HOME;
|
|
34911
34978
|
if (process.platform === "win32") {
|
|
34912
|
-
return process.env.LOCALAPPDATA || process.env.APPDATA ||
|
|
34979
|
+
return process.env.LOCALAPPDATA || process.env.APPDATA || join5(homeDir(), "AppData", "Local");
|
|
34913
34980
|
}
|
|
34914
|
-
return
|
|
34981
|
+
return join5(homeDir(), ".local", "share");
|
|
34915
34982
|
}
|
|
34916
34983
|
function homeDir() {
|
|
34917
34984
|
if (process.platform === "win32")
|
|
@@ -34920,11 +34987,11 @@ function homeDir() {
|
|
|
34920
34987
|
}
|
|
34921
34988
|
function resolveLegacyStorageRoot(harness) {
|
|
34922
34989
|
if (harness === "pi")
|
|
34923
|
-
return
|
|
34924
|
-
return
|
|
34990
|
+
return join5(homeDir(), ".pi", "agent", "aft");
|
|
34991
|
+
return join5(dataHome(), "opencode", "storage", "plugin", "aft");
|
|
34925
34992
|
}
|
|
34926
34993
|
function resolveCortexKitStorageRoot() {
|
|
34927
|
-
return
|
|
34994
|
+
return join5(dataHome(), "cortexkit", "aft");
|
|
34928
34995
|
}
|
|
34929
34996
|
function tail(value) {
|
|
34930
34997
|
if (!value)
|
|
@@ -34938,26 +35005,26 @@ function spawnErrorLabel(error2) {
|
|
|
34938
35005
|
return [code, error2.message].filter(Boolean).join(": ");
|
|
34939
35006
|
}
|
|
34940
35007
|
function migrationLogPath(newRoot, harness, logger) {
|
|
34941
|
-
const desired =
|
|
35008
|
+
const desired = join5(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
|
|
34942
35009
|
try {
|
|
34943
|
-
|
|
35010
|
+
mkdirSync4(dirname2(desired), { recursive: true });
|
|
34944
35011
|
return desired;
|
|
34945
35012
|
} catch (err) {
|
|
34946
|
-
const fallback =
|
|
34947
|
-
logger?.warn?.(`Failed to create AFT migration log directory ${
|
|
35013
|
+
const fallback = join5(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
|
|
35014
|
+
logger?.warn?.(`Failed to create AFT migration log directory ${dirname2(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
|
|
34948
35015
|
return fallback;
|
|
34949
35016
|
}
|
|
34950
35017
|
}
|
|
34951
35018
|
async function ensureStorageMigrated(opts) {
|
|
34952
35019
|
const legacyRoot = resolveLegacyStorageRoot(opts.harness);
|
|
34953
35020
|
const newRoot = resolveCortexKitStorageRoot();
|
|
34954
|
-
const targetMarker =
|
|
35021
|
+
const targetMarker = resolveHarnessStoragePath(newRoot, opts.harness, TARGET_MARKER);
|
|
34955
35022
|
const info = opts.logger?.info ?? opts.logger?.log;
|
|
34956
|
-
if (
|
|
35023
|
+
if (existsSync4(targetMarker)) {
|
|
34957
35024
|
info?.(`AFT storage already migrated for ${opts.harness}; using ${newRoot}`);
|
|
34958
35025
|
return;
|
|
34959
35026
|
}
|
|
34960
|
-
if (!
|
|
35027
|
+
if (!existsSync4(legacyRoot)) {
|
|
34961
35028
|
info?.(`AFT storage migration skipped for ${opts.harness}: no legacy data at ${legacyRoot}; ` + `using ${newRoot} for fresh install`);
|
|
34962
35029
|
return;
|
|
34963
35030
|
}
|
|
@@ -35007,8 +35074,8 @@ async function ensureStorageMigrated(opts) {
|
|
|
35007
35074
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
35008
35075
|
import { execFileSync } from "node:child_process";
|
|
35009
35076
|
import { createHash as createHash2 } from "node:crypto";
|
|
35010
|
-
import { chmodSync as chmodSync3, closeSync as closeSync2, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as
|
|
35011
|
-
import { dirname as
|
|
35077
|
+
import { chmodSync as chmodSync3, closeSync as closeSync2, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, openSync as openSync2, readdirSync, readFileSync as readFileSync2, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
35078
|
+
import { basename, dirname as dirname3, join as join6, relative, resolve } from "node:path";
|
|
35012
35079
|
import { Readable as Readable2 } from "node:stream";
|
|
35013
35080
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
35014
35081
|
var ORT_VERSION = "1.24.4";
|
|
@@ -35074,9 +35141,9 @@ function getManualInstallHint() {
|
|
|
35074
35141
|
}
|
|
35075
35142
|
async function ensureOnnxRuntime(storageDir) {
|
|
35076
35143
|
const info = getPlatformInfo();
|
|
35077
|
-
const ortDir =
|
|
35078
|
-
const libPath =
|
|
35079
|
-
if (
|
|
35144
|
+
const ortDir = join6(storageDir, "onnxruntime", ORT_VERSION);
|
|
35145
|
+
const libPath = join6(ortDir, info?.libName ?? "libonnxruntime.dylib");
|
|
35146
|
+
if (existsSync5(libPath)) {
|
|
35080
35147
|
const meta = readOnnxInstalledMeta(ortDir);
|
|
35081
35148
|
if (meta?.sha256) {
|
|
35082
35149
|
try {
|
|
@@ -35105,9 +35172,9 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
35105
35172
|
warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
|
|
35106
35173
|
return null;
|
|
35107
35174
|
}
|
|
35108
|
-
const onnxBaseDir =
|
|
35109
|
-
|
|
35110
|
-
const lockPath =
|
|
35175
|
+
const onnxBaseDir = join6(storageDir, "onnxruntime");
|
|
35176
|
+
mkdirSync5(onnxBaseDir, { recursive: true });
|
|
35177
|
+
const lockPath = join6(onnxBaseDir, ONNX_LOCK_FILE);
|
|
35111
35178
|
cleanupAbandonedStagingDirs(onnxBaseDir);
|
|
35112
35179
|
if (!acquireLock(lockPath)) {
|
|
35113
35180
|
warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
|
|
@@ -35126,7 +35193,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
35126
35193
|
for (const entry of entries) {
|
|
35127
35194
|
if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
|
|
35128
35195
|
continue;
|
|
35129
|
-
const stagingDir =
|
|
35196
|
+
const stagingDir = join6(onnxBaseDir, entry);
|
|
35130
35197
|
const parts = entry.split(".");
|
|
35131
35198
|
const pidStr = parts[parts.length - 2];
|
|
35132
35199
|
const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
|
|
@@ -35158,7 +35225,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
35158
35225
|
}
|
|
35159
35226
|
function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
35160
35227
|
try {
|
|
35161
|
-
if (
|
|
35228
|
+
if (existsSync5(ortDir) && !existsSync5(join6(ortDir, ONNX_INSTALLED_META_FILE))) {
|
|
35162
35229
|
log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
|
|
35163
35230
|
rmSync2(ortDir, { recursive: true, force: true });
|
|
35164
35231
|
}
|
|
@@ -35168,6 +35235,14 @@ function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
|
35168
35235
|
}
|
|
35169
35236
|
var REQUIRED_ORT_MAJOR = 1;
|
|
35170
35237
|
var REQUIRED_ORT_MIN_MINOR = 20;
|
|
35238
|
+
var INVALID_ORT_VERSION = "<invalid>";
|
|
35239
|
+
function parseOnnxVersionFromPath(value) {
|
|
35240
|
+
const name = basename(value);
|
|
35241
|
+
const semverish = name.match(/\.(\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?)(?:\.dylib)?$/);
|
|
35242
|
+
if (semverish)
|
|
35243
|
+
return semverish[1].split(/[-+]/, 1)[0];
|
|
35244
|
+
return /\d+\.\d+\.\d+/.test(name) ? INVALID_ORT_VERSION : null;
|
|
35245
|
+
}
|
|
35171
35246
|
function detectOnnxVersion(libDir, libName) {
|
|
35172
35247
|
try {
|
|
35173
35248
|
const entries = readdirSync(libDir);
|
|
@@ -35175,23 +35250,23 @@ function detectOnnxVersion(libDir, libName) {
|
|
|
35175
35250
|
for (const entry of entries) {
|
|
35176
35251
|
if (!entry.startsWith(barePrefix))
|
|
35177
35252
|
continue;
|
|
35178
|
-
const
|
|
35179
|
-
if (
|
|
35180
|
-
return
|
|
35253
|
+
const version = parseOnnxVersionFromPath(entry);
|
|
35254
|
+
if (version)
|
|
35255
|
+
return version;
|
|
35181
35256
|
}
|
|
35182
|
-
const base =
|
|
35183
|
-
if (
|
|
35257
|
+
const base = join6(libDir, libName);
|
|
35258
|
+
if (existsSync5(base)) {
|
|
35184
35259
|
try {
|
|
35185
35260
|
const real = realpathSync(base);
|
|
35186
|
-
const
|
|
35187
|
-
if (
|
|
35188
|
-
return
|
|
35261
|
+
const version = parseOnnxVersionFromPath(real);
|
|
35262
|
+
if (version)
|
|
35263
|
+
return version;
|
|
35189
35264
|
} catch {}
|
|
35190
35265
|
try {
|
|
35191
35266
|
const target = readlinkSync(base);
|
|
35192
|
-
const
|
|
35193
|
-
if (
|
|
35194
|
-
return
|
|
35267
|
+
const version = parseOnnxVersionFromPath(target);
|
|
35268
|
+
if (version)
|
|
35269
|
+
return version;
|
|
35195
35270
|
} catch {}
|
|
35196
35271
|
}
|
|
35197
35272
|
} catch {}
|
|
@@ -35216,7 +35291,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
35216
35291
|
searchPaths.push("/usr/lib", "/usr/lib/x86_64-linux-gnu", "/usr/lib/aarch64-linux-gnu", "/usr/local/lib");
|
|
35217
35292
|
}
|
|
35218
35293
|
for (const dir of searchPaths) {
|
|
35219
|
-
if (!
|
|
35294
|
+
if (!existsSync5(join6(dir, libName)))
|
|
35220
35295
|
continue;
|
|
35221
35296
|
const version = detectOnnxVersion(dir, libName);
|
|
35222
35297
|
if (version && !isOnnxVersionCompatible(version)) {
|
|
@@ -35243,7 +35318,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
35243
35318
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
35244
35319
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
35245
35320
|
}
|
|
35246
|
-
|
|
35321
|
+
mkdirSync5(dirname3(destPath), { recursive: true });
|
|
35247
35322
|
let bytesWritten = 0;
|
|
35248
35323
|
const guard = new TransformStream({
|
|
35249
35324
|
transform(chunk, transformController) {
|
|
@@ -35273,11 +35348,11 @@ function validateExtractedTree(stagingRoot) {
|
|
|
35273
35348
|
const walk = (dir) => {
|
|
35274
35349
|
const entries = readdirSync(dir);
|
|
35275
35350
|
for (const entry of entries) {
|
|
35276
|
-
const fullPath =
|
|
35351
|
+
const fullPath = join6(dir, entry);
|
|
35277
35352
|
const lst = lstatSync(fullPath);
|
|
35278
35353
|
if (lst.isSymbolicLink()) {
|
|
35279
35354
|
const linkTarget = readlinkSync(fullPath);
|
|
35280
|
-
const resolvedTarget = resolve(
|
|
35355
|
+
const resolvedTarget = resolve(dirname3(fullPath), linkTarget);
|
|
35281
35356
|
const rel2 = relative(realRoot, resolvedTarget);
|
|
35282
35357
|
if (rel2.startsWith("..") || process.platform !== "win32" && rel2.startsWith("/")) {
|
|
35283
35358
|
throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
|
|
@@ -35307,8 +35382,8 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
35307
35382
|
log(`Downloading ONNX Runtime v${ORT_VERSION} for ${process.platform}/${process.arch}...`);
|
|
35308
35383
|
const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
|
|
35309
35384
|
try {
|
|
35310
|
-
|
|
35311
|
-
const archivePath =
|
|
35385
|
+
mkdirSync5(tmpDir, { recursive: true });
|
|
35386
|
+
const archivePath = join6(tmpDir, `onnxruntime.${info.archiveType}`);
|
|
35312
35387
|
await downloadFileWithCap(url, archivePath);
|
|
35313
35388
|
const archiveSha256 = sha256File(archivePath);
|
|
35314
35389
|
log(`ONNX Runtime archive sha256=${archiveSha256}`);
|
|
@@ -35324,16 +35399,16 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
35324
35399
|
unlinkSync2(archivePath);
|
|
35325
35400
|
} catch {}
|
|
35326
35401
|
validateExtractedTree(tmpDir);
|
|
35327
|
-
const extractedDir =
|
|
35328
|
-
if (!
|
|
35402
|
+
const extractedDir = join6(tmpDir, info.assetName, "lib");
|
|
35403
|
+
if (!existsSync5(extractedDir)) {
|
|
35329
35404
|
throw new Error(`Expected directory not found: ${extractedDir}`);
|
|
35330
35405
|
}
|
|
35331
|
-
|
|
35406
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
35332
35407
|
const libFiles = readdirSync(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
|
|
35333
35408
|
const realFiles = [];
|
|
35334
35409
|
const symlinks = [];
|
|
35335
35410
|
for (const libFile of libFiles) {
|
|
35336
|
-
const src =
|
|
35411
|
+
const src = join6(extractedDir, libFile);
|
|
35337
35412
|
try {
|
|
35338
35413
|
const stat = lstatSync(src);
|
|
35339
35414
|
log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
|
|
@@ -35348,7 +35423,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
35348
35423
|
}
|
|
35349
35424
|
}
|
|
35350
35425
|
copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
|
|
35351
|
-
const libPath =
|
|
35426
|
+
const libPath = join6(targetDir, info.libName);
|
|
35352
35427
|
let libHash = null;
|
|
35353
35428
|
try {
|
|
35354
35429
|
libHash = sha256File(libPath);
|
|
@@ -35373,8 +35448,8 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
35373
35448
|
function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync2) {
|
|
35374
35449
|
const requiredLibs = new Set([info.libName]);
|
|
35375
35450
|
for (const libFile of realFiles) {
|
|
35376
|
-
const src =
|
|
35377
|
-
const dst =
|
|
35451
|
+
const src = join6(extractedDir, libFile);
|
|
35452
|
+
const dst = join6(targetDir, libFile);
|
|
35378
35453
|
try {
|
|
35379
35454
|
copyFile(src, dst);
|
|
35380
35455
|
if (process.platform !== "win32") {
|
|
@@ -35389,7 +35464,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
35389
35464
|
}
|
|
35390
35465
|
}
|
|
35391
35466
|
for (const link of symlinks) {
|
|
35392
|
-
const dst =
|
|
35467
|
+
const dst = join6(targetDir, link.name);
|
|
35393
35468
|
try {
|
|
35394
35469
|
unlinkSync2(dst);
|
|
35395
35470
|
} catch {}
|
|
@@ -35403,8 +35478,8 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
35403
35478
|
log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
|
|
35404
35479
|
}
|
|
35405
35480
|
}
|
|
35406
|
-
const requiredPath =
|
|
35407
|
-
if (!
|
|
35481
|
+
const requiredPath = join6(targetDir, info.libName);
|
|
35482
|
+
if (!existsSync5(requiredPath)) {
|
|
35408
35483
|
rmSync2(targetDir, { recursive: true, force: true });
|
|
35409
35484
|
throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
|
|
35410
35485
|
}
|
|
@@ -35430,17 +35505,17 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
35430
35505
|
...sha256 ? { sha256 } : {},
|
|
35431
35506
|
archiveSha256
|
|
35432
35507
|
};
|
|
35433
|
-
writeFileSync(
|
|
35508
|
+
writeFileSync(join6(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
|
|
35434
35509
|
} catch (err) {
|
|
35435
35510
|
log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
|
|
35436
35511
|
}
|
|
35437
35512
|
}
|
|
35438
35513
|
function readOnnxInstalledMeta(installDir) {
|
|
35439
|
-
const path =
|
|
35514
|
+
const path = join6(installDir, ONNX_INSTALLED_META_FILE);
|
|
35440
35515
|
try {
|
|
35441
35516
|
if (!statSync2(path).isFile())
|
|
35442
35517
|
return null;
|
|
35443
|
-
const raw =
|
|
35518
|
+
const raw = readFileSync2(path, "utf8");
|
|
35444
35519
|
const parsed = JSON.parse(raw);
|
|
35445
35520
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
35446
35521
|
return null;
|
|
@@ -35456,7 +35531,7 @@ function readOnnxInstalledMeta(installDir) {
|
|
|
35456
35531
|
}
|
|
35457
35532
|
function sha256File(path) {
|
|
35458
35533
|
const hash = createHash2("sha256");
|
|
35459
|
-
hash.update(
|
|
35534
|
+
hash.update(readFileSync2(path));
|
|
35460
35535
|
return hash.digest("hex");
|
|
35461
35536
|
}
|
|
35462
35537
|
function acquireLock(lockPath) {
|
|
@@ -35484,7 +35559,7 @@ ${new Date().toISOString()}
|
|
|
35484
35559
|
let owningPid = null;
|
|
35485
35560
|
let lockMtimeMs = 0;
|
|
35486
35561
|
try {
|
|
35487
|
-
const raw =
|
|
35562
|
+
const raw = readFileSync2(lockPath, "utf8");
|
|
35488
35563
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
35489
35564
|
const parsed = Number.parseInt(firstLine, 10);
|
|
35490
35565
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -35510,7 +35585,7 @@ function releaseLock(lockPath) {
|
|
|
35510
35585
|
try {
|
|
35511
35586
|
let owningPid = null;
|
|
35512
35587
|
try {
|
|
35513
|
-
const raw =
|
|
35588
|
+
const raw = readFileSync2(lockPath, "utf8");
|
|
35514
35589
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
35515
35590
|
const parsed = Number.parseInt(firstLine, 10);
|
|
35516
35591
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -35556,6 +35631,7 @@ var DEFAULT_MAX_POOL_SIZE = 8;
|
|
|
35556
35631
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
35557
35632
|
class BridgePool {
|
|
35558
35633
|
bridges = new Map;
|
|
35634
|
+
staleBridges = new Set;
|
|
35559
35635
|
binaryPath;
|
|
35560
35636
|
maxPoolSize;
|
|
35561
35637
|
idleTimeoutMs;
|
|
@@ -35578,6 +35654,7 @@ class BridgePool {
|
|
|
35578
35654
|
onConfigureWarnings: options.onConfigureWarnings,
|
|
35579
35655
|
onBashCompletion: options.onBashCompletion,
|
|
35580
35656
|
onBashLongRunning: options.onBashLongRunning,
|
|
35657
|
+
onBashPatternMatch: options.onBashPatternMatch,
|
|
35581
35658
|
errorPrefix: options.errorPrefix,
|
|
35582
35659
|
logger: options.logger
|
|
35583
35660
|
};
|
|
@@ -35629,6 +35706,12 @@ class BridgePool {
|
|
|
35629
35706
|
this.bridges.delete(dir);
|
|
35630
35707
|
}
|
|
35631
35708
|
}
|
|
35709
|
+
for (const bridge of this.staleBridges) {
|
|
35710
|
+
if (bridge.hasPendingRequests())
|
|
35711
|
+
continue;
|
|
35712
|
+
bridge.shutdown().catch((err) => this.error("stale cleanup shutdown failed:", err));
|
|
35713
|
+
this.staleBridges.delete(bridge);
|
|
35714
|
+
}
|
|
35632
35715
|
}
|
|
35633
35716
|
evictLRU() {
|
|
35634
35717
|
let oldestDir = null;
|
|
@@ -35652,14 +35735,21 @@ class BridgePool {
|
|
|
35652
35735
|
clearInterval(this.cleanupTimer);
|
|
35653
35736
|
this.cleanupTimer = null;
|
|
35654
35737
|
}
|
|
35655
|
-
const shutdowns =
|
|
35738
|
+
const shutdowns = [
|
|
35739
|
+
...Array.from(this.bridges.values(), (e) => e.bridge.shutdown()),
|
|
35740
|
+
...Array.from(this.staleBridges.values(), (bridge) => bridge.shutdown())
|
|
35741
|
+
];
|
|
35656
35742
|
this.bridges.clear();
|
|
35743
|
+
this.staleBridges.clear();
|
|
35657
35744
|
await Promise.allSettled(shutdowns);
|
|
35658
35745
|
}
|
|
35659
35746
|
async replaceBinary(newPath) {
|
|
35660
35747
|
this.binaryPath = newPath;
|
|
35748
|
+
for (const entry of this.bridges.values()) {
|
|
35749
|
+
this.staleBridges.add(entry.bridge);
|
|
35750
|
+
}
|
|
35661
35751
|
this.bridges.clear();
|
|
35662
|
-
this.log(`Binary path updated to ${newPath}.
|
|
35752
|
+
this.log(`Binary path updated to ${newPath}. Active bridges marked stale — next calls will use the new binary.`);
|
|
35663
35753
|
return newPath;
|
|
35664
35754
|
}
|
|
35665
35755
|
log(message, meta) {
|
|
@@ -35709,11 +35799,11 @@ function normalizeKey(projectRoot) {
|
|
|
35709
35799
|
}
|
|
35710
35800
|
}
|
|
35711
35801
|
// ../aft-bridge/dist/url-fetch.js
|
|
35712
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
35802
|
+
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
35713
35803
|
import { lookup } from "node:dns/promises";
|
|
35714
|
-
import { existsSync as
|
|
35804
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync2, readFileSync as readFileSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
35715
35805
|
import { isIP } from "node:net";
|
|
35716
|
-
import { join as
|
|
35806
|
+
import { join as join7 } from "node:path";
|
|
35717
35807
|
|
|
35718
35808
|
// ../../node_modules/.bun/undici@7.25.0/node_modules/undici/index.js
|
|
35719
35809
|
var __filename = "/home/runner/work/aft/aft/node_modules/.bun/undici@7.25.0/node_modules/undici/index.js";
|
|
@@ -35863,16 +35953,16 @@ var FETCH_TIMEOUT_MS = 30000;
|
|
|
35863
35953
|
var BODY_CHUNK_TIMEOUT_MS = 15000;
|
|
35864
35954
|
var MAX_REDIRECTS = 5;
|
|
35865
35955
|
function cacheDir(storageDir) {
|
|
35866
|
-
return
|
|
35956
|
+
return join7(storageDir, "url_cache");
|
|
35867
35957
|
}
|
|
35868
35958
|
function hashUrl(url) {
|
|
35869
35959
|
return createHash3("sha256").update(url).digest("hex").slice(0, 16);
|
|
35870
35960
|
}
|
|
35871
35961
|
function metaPath(storageDir, hash) {
|
|
35872
|
-
return
|
|
35962
|
+
return join7(cacheDir(storageDir), `${hash}.meta.json`);
|
|
35873
35963
|
}
|
|
35874
35964
|
function contentPath(storageDir, hash, extension) {
|
|
35875
|
-
return
|
|
35965
|
+
return join7(cacheDir(storageDir), `${hash}${extension}`);
|
|
35876
35966
|
}
|
|
35877
35967
|
function _isPrivateIpv4(address) {
|
|
35878
35968
|
const parts = address.split(".").map((part) => Number.parseInt(part, 10));
|
|
@@ -35997,13 +36087,21 @@ function resolveRedirectUrl(currentUrl, location) {
|
|
|
35997
36087
|
}
|
|
35998
36088
|
return new URL(location, currentUrl);
|
|
35999
36089
|
}
|
|
36090
|
+
var isBunRuntime = typeof globalThis.Bun !== "undefined";
|
|
36000
36091
|
async function fetchWithRedirects(startUrl, allowPrivate, deps = {}) {
|
|
36001
36092
|
let currentUrl = startUrl;
|
|
36002
|
-
const fetchImpl2 = deps.fetchImpl ?? $fetch;
|
|
36093
|
+
const fetchImpl2 = deps.fetchImpl ?? (isBunRuntime ? globalThis.fetch : $fetch);
|
|
36003
36094
|
const dnsLookup = deps.lookup ?? lookup;
|
|
36004
36095
|
for (let redirectCount = 0;redirectCount <= MAX_REDIRECTS; redirectCount++) {
|
|
36005
36096
|
const validatedIp = await assertPublicUrl(currentUrl, allowPrivate, dnsLookup);
|
|
36006
|
-
|
|
36097
|
+
let dispatcher;
|
|
36098
|
+
if (validatedIp) {
|
|
36099
|
+
if (deps.dispatcherFactory) {
|
|
36100
|
+
dispatcher = deps.dispatcherFactory(validatedIp);
|
|
36101
|
+
} else if (!isBunRuntime) {
|
|
36102
|
+
dispatcher = createPinnedDispatcher(validatedIp);
|
|
36103
|
+
}
|
|
36104
|
+
}
|
|
36007
36105
|
const controller = new AbortController;
|
|
36008
36106
|
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
36009
36107
|
let response;
|
|
@@ -36014,7 +36112,7 @@ async function fetchWithRedirects(startUrl, allowPrivate, deps = {}) {
|
|
|
36014
36112
|
dispatcher,
|
|
36015
36113
|
headers: {
|
|
36016
36114
|
"user-agent": "aft-opencode-plugin",
|
|
36017
|
-
accept: "application/vnd.github.raw, text/markdown, text/x-markdown, text/html;q=0.9, text/plain;q=0.5"
|
|
36115
|
+
accept: "application/vnd.github.raw, text/markdown, text/x-markdown, text/html;q=0.9, application/json;q=0.8, text/plain;q=0.5"
|
|
36018
36116
|
}
|
|
36019
36117
|
});
|
|
36020
36118
|
} catch (err) {
|
|
@@ -36055,6 +36153,9 @@ function resolveExtension(contentType) {
|
|
|
36055
36153
|
if (lower === "text/plain") {
|
|
36056
36154
|
return ".md";
|
|
36057
36155
|
}
|
|
36156
|
+
if (lower === "application/json" || lower === "application/ld+json" || lower.endsWith("+json")) {
|
|
36157
|
+
return ".json";
|
|
36158
|
+
}
|
|
36058
36159
|
return null;
|
|
36059
36160
|
}
|
|
36060
36161
|
async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
@@ -36070,15 +36171,15 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
36070
36171
|
const allowPrivate = options.allowPrivate === true;
|
|
36071
36172
|
await assertPublicUrl(parsed, allowPrivate, options.lookup);
|
|
36072
36173
|
const dir = cacheDir(storageDir);
|
|
36073
|
-
|
|
36174
|
+
mkdirSync6(dir, { recursive: true });
|
|
36074
36175
|
const hash = hashUrl(url);
|
|
36075
36176
|
const metaFile = metaPath(storageDir, hash);
|
|
36076
|
-
if (
|
|
36177
|
+
if (existsSync6(metaFile)) {
|
|
36077
36178
|
try {
|
|
36078
|
-
const meta2 = JSON.parse(
|
|
36179
|
+
const meta2 = JSON.parse(readFileSync3(metaFile, "utf8"));
|
|
36079
36180
|
const age = Date.now() - meta2.fetchedAt;
|
|
36080
36181
|
const cached = contentPath(storageDir, hash, meta2.extension);
|
|
36081
|
-
if (age < CACHE_TTL_MS &&
|
|
36182
|
+
if (age < CACHE_TTL_MS && existsSync6(cached)) {
|
|
36082
36183
|
log(`URL cache hit: ${url} (${Math.round(age / 1000)}s old)`);
|
|
36083
36184
|
return cached;
|
|
36084
36185
|
}
|
|
@@ -36144,39 +36245,40 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
36144
36245
|
}
|
|
36145
36246
|
const body = Buffer.concat(chunks);
|
|
36146
36247
|
const contentFile = contentPath(storageDir, hash, extension);
|
|
36147
|
-
const
|
|
36248
|
+
const nonce = randomBytes(8).toString("hex");
|
|
36249
|
+
const tmpContent = `${contentFile}.tmp-${process.pid}-${nonce}`;
|
|
36148
36250
|
writeFileSync2(tmpContent, body);
|
|
36149
|
-
const { renameSync:
|
|
36150
|
-
|
|
36251
|
+
const { renameSync: renameSync4 } = await import("node:fs");
|
|
36252
|
+
renameSync4(tmpContent, contentFile);
|
|
36151
36253
|
const meta = {
|
|
36152
36254
|
url,
|
|
36153
36255
|
contentType,
|
|
36154
36256
|
extension,
|
|
36155
36257
|
fetchedAt: Date.now()
|
|
36156
36258
|
};
|
|
36157
|
-
const tmpMeta = `${metaFile}.tmp-${process.pid}`;
|
|
36259
|
+
const tmpMeta = `${metaFile}.tmp-${process.pid}-${nonce}`;
|
|
36158
36260
|
writeFileSync2(tmpMeta, JSON.stringify(meta));
|
|
36159
|
-
|
|
36261
|
+
renameSync4(tmpMeta, metaFile);
|
|
36160
36262
|
log(`URL cached (${total} bytes): ${url}`);
|
|
36161
36263
|
return contentFile;
|
|
36162
36264
|
}
|
|
36163
36265
|
function cleanupUrlCache(storageDir) {
|
|
36164
36266
|
const dir = cacheDir(storageDir);
|
|
36165
|
-
if (!
|
|
36267
|
+
if (!existsSync6(dir))
|
|
36166
36268
|
return;
|
|
36167
36269
|
let removed = 0;
|
|
36168
36270
|
try {
|
|
36169
36271
|
for (const entry of readdirSync2(dir)) {
|
|
36170
36272
|
if (!entry.endsWith(".meta.json"))
|
|
36171
36273
|
continue;
|
|
36172
|
-
const metaFile =
|
|
36274
|
+
const metaFile = join7(dir, entry);
|
|
36173
36275
|
try {
|
|
36174
|
-
const meta = JSON.parse(
|
|
36276
|
+
const meta = JSON.parse(readFileSync3(metaFile, "utf8"));
|
|
36175
36277
|
const age = Date.now() - meta.fetchedAt;
|
|
36176
36278
|
if (age > CACHE_TTL_MS) {
|
|
36177
36279
|
const hash = entry.slice(0, -".meta.json".length);
|
|
36178
36280
|
const content = contentPath(storageDir, hash, meta.extension);
|
|
36179
|
-
if (
|
|
36281
|
+
if (existsSync6(content))
|
|
36180
36282
|
unlinkSync3(content);
|
|
36181
36283
|
unlinkSync3(metaFile);
|
|
36182
36284
|
removed++;
|
|
@@ -36247,7 +36349,7 @@ function formatZoomText(targetLabel, response) {
|
|
|
36247
36349
|
`);
|
|
36248
36350
|
}
|
|
36249
36351
|
// src/bg-notifications.ts
|
|
36250
|
-
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
36352
|
+
import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
|
|
36251
36353
|
|
|
36252
36354
|
// src/logger.ts
|
|
36253
36355
|
import * as fs from "node:fs";
|
|
@@ -36346,11 +36448,6 @@ function extractMessages(response) {
|
|
|
36346
36448
|
return response.data;
|
|
36347
36449
|
return [];
|
|
36348
36450
|
}
|
|
36349
|
-
function getRole(message) {
|
|
36350
|
-
if (!isRecord(message) || !isRecord(message.info))
|
|
36351
|
-
return;
|
|
36352
|
-
return typeof message.info.role === "string" ? message.info.role : undefined;
|
|
36353
|
-
}
|
|
36354
36451
|
function extractFromMessage(message) {
|
|
36355
36452
|
if (!isRecord(message) || !isRecord(message.info))
|
|
36356
36453
|
return null;
|
|
@@ -36401,16 +36498,6 @@ async function resolvePromptContext(client, sessionId) {
|
|
|
36401
36498
|
if (messages.length === 0)
|
|
36402
36499
|
return null;
|
|
36403
36500
|
let result = {};
|
|
36404
|
-
for (let i = messages.length - 1;i >= 0; i -= 1) {
|
|
36405
|
-
if (getRole(messages[i]) !== "assistant")
|
|
36406
|
-
continue;
|
|
36407
|
-
const ctx = extractFromMessage(messages[i]);
|
|
36408
|
-
if (!ctx)
|
|
36409
|
-
continue;
|
|
36410
|
-
result = mergeContexts(result, ctx);
|
|
36411
|
-
if (isComplete(result))
|
|
36412
|
-
return result;
|
|
36413
|
-
}
|
|
36414
36501
|
for (let i = messages.length - 1;i >= 0; i -= 1) {
|
|
36415
36502
|
const ctx = extractFromMessage(messages[i]);
|
|
36416
36503
|
if (!ctx)
|
|
@@ -36430,6 +36517,13 @@ var clientCache = new Map;
|
|
|
36430
36517
|
function cacheKey(serverUrl, directory) {
|
|
36431
36518
|
return `${serverUrl}|${directory}`;
|
|
36432
36519
|
}
|
|
36520
|
+
function normalizeServerUrl(serverUrl) {
|
|
36521
|
+
try {
|
|
36522
|
+
return new URL(serverUrl).toString();
|
|
36523
|
+
} catch {
|
|
36524
|
+
return serverUrl;
|
|
36525
|
+
}
|
|
36526
|
+
}
|
|
36433
36527
|
function serverAuthHeaders() {
|
|
36434
36528
|
const password = process.env.OPENCODE_SERVER_PASSWORD;
|
|
36435
36529
|
if (!password)
|
|
@@ -36453,30 +36547,46 @@ function getLiveServerClient(serverUrl, directory) {
|
|
|
36453
36547
|
clientCache.set(key, client);
|
|
36454
36548
|
return client;
|
|
36455
36549
|
}
|
|
36550
|
+
var liveServerWakeAvailableByServerUrl = new Map;
|
|
36551
|
+
var legacyLiveServerWakeAvailable = false;
|
|
36456
36552
|
async function probeServerReachable(serverUrl, timeoutMs = 1500) {
|
|
36457
36553
|
if (!serverUrl)
|
|
36458
36554
|
return false;
|
|
36555
|
+
const normalizedServerUrl = normalizeServerUrl(serverUrl);
|
|
36459
36556
|
const controller = new AbortController;
|
|
36460
36557
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
36558
|
+
let reachable = false;
|
|
36461
36559
|
try {
|
|
36462
36560
|
const probeUrl = new URL("/session", serverUrl).toString();
|
|
36463
36561
|
const res = await globalThis.fetch(probeUrl, {
|
|
36464
36562
|
method: "GET",
|
|
36563
|
+
headers: serverAuthHeaders(),
|
|
36465
36564
|
signal: controller.signal
|
|
36466
36565
|
});
|
|
36467
|
-
|
|
36566
|
+
reachable = res.ok || res.status === 401 || res.status === 403;
|
|
36468
36567
|
} catch {
|
|
36469
|
-
|
|
36568
|
+
reachable = false;
|
|
36470
36569
|
} finally {
|
|
36471
36570
|
clearTimeout(timer);
|
|
36571
|
+
liveServerWakeAvailableByServerUrl.set(normalizedServerUrl, reachable);
|
|
36472
36572
|
}
|
|
36573
|
+
return reachable;
|
|
36473
36574
|
}
|
|
36474
|
-
|
|
36475
|
-
|
|
36476
|
-
|
|
36575
|
+
function setLiveServerWakeAvailable(serverUrlOrAvailable, available) {
|
|
36576
|
+
if (typeof serverUrlOrAvailable === "boolean") {
|
|
36577
|
+
legacyLiveServerWakeAvailable = serverUrlOrAvailable;
|
|
36578
|
+
return;
|
|
36579
|
+
}
|
|
36580
|
+
if (!serverUrlOrAvailable) {
|
|
36581
|
+
legacyLiveServerWakeAvailable = available ?? false;
|
|
36582
|
+
return;
|
|
36583
|
+
}
|
|
36584
|
+
liveServerWakeAvailableByServerUrl.set(normalizeServerUrl(serverUrlOrAvailable), available ?? false);
|
|
36477
36585
|
}
|
|
36478
|
-
function useLiveServerWake() {
|
|
36479
|
-
|
|
36586
|
+
function useLiveServerWake(serverUrl) {
|
|
36587
|
+
if (!serverUrl)
|
|
36588
|
+
return legacyLiveServerWakeAvailable;
|
|
36589
|
+
return liveServerWakeAvailableByServerUrl.get(normalizeServerUrl(serverUrl)) ?? false;
|
|
36480
36590
|
}
|
|
36481
36591
|
|
|
36482
36592
|
// src/bg-notifications.ts
|
|
@@ -36571,7 +36681,7 @@ function markExplicitControl(sessionID, taskId, trackOutstanding = true) {
|
|
|
36571
36681
|
if (idx >= 0) {
|
|
36572
36682
|
const completion = state.pendingCompletions[idx];
|
|
36573
36683
|
state.pendingCompletions.splice(idx, 1);
|
|
36574
|
-
queuePendingPatternMatch(state, completionToExitPattern(completion));
|
|
36684
|
+
queuePendingPatternMatch(state, completionToExitPattern(completion, true));
|
|
36575
36685
|
state.wakeDeferredTaskIds.delete(taskId);
|
|
36576
36686
|
}
|
|
36577
36687
|
}
|
|
@@ -36599,7 +36709,7 @@ function routeExplicitControlCompletions(state) {
|
|
|
36599
36709
|
state.outstandingTaskIds.delete(completion.task_id);
|
|
36600
36710
|
state.explicitControlTasks.delete(completion.task_id);
|
|
36601
36711
|
state.wakeDeferredTaskIds.delete(completion.task_id);
|
|
36602
|
-
queuePendingPatternMatch(state, completionToExitPattern(completion));
|
|
36712
|
+
queuePendingPatternMatch(state, completionToExitPattern(completion, true));
|
|
36603
36713
|
} else {
|
|
36604
36714
|
remaining.push(completion);
|
|
36605
36715
|
}
|
|
@@ -36626,7 +36736,7 @@ function ingestBgCompletions(sessionID, completions) {
|
|
|
36626
36736
|
if (state.explicitControlTasks.has(completion.task_id)) {
|
|
36627
36737
|
state.outstandingTaskIds.delete(completion.task_id);
|
|
36628
36738
|
state.explicitControlTasks.delete(completion.task_id);
|
|
36629
|
-
queuePendingPatternMatch(state, completionToExitPattern(completion));
|
|
36739
|
+
queuePendingPatternMatch(state, completionToExitPattern(completion, true));
|
|
36630
36740
|
continue;
|
|
36631
36741
|
}
|
|
36632
36742
|
if (!state.outstandingTaskIds.has(completion.task_id)) {
|
|
@@ -36666,6 +36776,8 @@ async function appendInTurnBgCompletions(drainContext, output) {
|
|
|
36666
36776
|
if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0 && state.pendingPatternMatches.length === 0)
|
|
36667
36777
|
return;
|
|
36668
36778
|
const deliveredCompletions = [...state.pendingCompletions];
|
|
36779
|
+
const deliveredPatternMatches = [...state.pendingPatternMatches];
|
|
36780
|
+
const completionAcks = completionAcksForDelivery(deliveredCompletions, deliveredPatternMatches);
|
|
36669
36781
|
const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning, state.pendingPatternMatches);
|
|
36670
36782
|
output.output = appendReminder(output.output ?? "", reminder);
|
|
36671
36783
|
sessionLog(drainContext.sessionID, `${LOG_PREFIX} in-turn append`, {
|
|
@@ -36683,7 +36795,7 @@ async function appendInTurnBgCompletions(drainContext, output) {
|
|
|
36683
36795
|
state.pendingPatternMatches = [];
|
|
36684
36796
|
state.wakeRetryAttempts = 0;
|
|
36685
36797
|
state.wakeHardStopped = false;
|
|
36686
|
-
await ackCompletions(drainContext,
|
|
36798
|
+
await ackCompletions(drainContext, completionAcks);
|
|
36687
36799
|
if (state.debounceTimer) {
|
|
36688
36800
|
clearTimeout(state.debounceTimer);
|
|
36689
36801
|
state.debounceTimer = null;
|
|
@@ -36705,86 +36817,112 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
36705
36817
|
if (!hasWakeEligiblePending(state, includeDeferredCompletions))
|
|
36706
36818
|
return;
|
|
36707
36819
|
scheduleWake(state, async (reminder, deliveredCompletions) => {
|
|
36708
|
-
|
|
36709
|
-
|
|
36710
|
-
if (useLiveServerWake() && drainContext.serverUrl) {
|
|
36711
|
-
client = getLiveServerClient(drainContext.serverUrl, drainContext.directory);
|
|
36712
|
-
clientPath = "live-server";
|
|
36713
|
-
} else {
|
|
36820
|
+
const taskIDs = deliveredCompletions.map((completion) => completion.task_id);
|
|
36821
|
+
const getInProcessClient = () => {
|
|
36714
36822
|
if (!drainContext.client) {
|
|
36715
36823
|
sessionWarn(drainContext.sessionID, `${LOG_PREFIX} wake client unavailable`, {
|
|
36716
36824
|
event: "bash_completion_wake_client_unavailable",
|
|
36717
|
-
task_ids:
|
|
36825
|
+
task_ids: taskIDs,
|
|
36718
36826
|
directory: drainContext.directory,
|
|
36719
36827
|
attempt: state.wakeRetryAttempts + 1
|
|
36720
36828
|
});
|
|
36721
36829
|
throw new Error("no wake transport available: live-server unreachable and input.client absent");
|
|
36722
36830
|
}
|
|
36723
|
-
|
|
36724
|
-
clientPath = "in-process-fallback";
|
|
36725
|
-
}
|
|
36726
|
-
if (typeof client.session?.promptAsync !== "function") {
|
|
36727
|
-
throw new Error(`wake client.session.promptAsync is unavailable (path=${clientPath})`);
|
|
36728
|
-
}
|
|
36729
|
-
const promptContext = await resolvePromptContext(client, drainContext.sessionID);
|
|
36730
|
-
const body = {
|
|
36731
|
-
noReply: false,
|
|
36732
|
-
parts: [{ type: "text", text: reminder }]
|
|
36831
|
+
return drainContext.client;
|
|
36733
36832
|
};
|
|
36734
|
-
|
|
36735
|
-
|
|
36736
|
-
|
|
36737
|
-
|
|
36738
|
-
|
|
36739
|
-
|
|
36833
|
+
const sendPrompt = async (client, clientPath) => {
|
|
36834
|
+
if (typeof client.session?.promptAsync !== "function") {
|
|
36835
|
+
throw new Error(`wake client.session.promptAsync is unavailable (path=${clientPath})`);
|
|
36836
|
+
}
|
|
36837
|
+
const promptContext = await resolvePromptContext(client, drainContext.sessionID);
|
|
36838
|
+
const body = {
|
|
36839
|
+
noReply: false,
|
|
36840
|
+
parts: [{ type: "text", text: reminder }]
|
|
36740
36841
|
};
|
|
36741
|
-
|
|
36742
|
-
|
|
36743
|
-
|
|
36744
|
-
|
|
36745
|
-
const taskIDs = deliveredCompletions.map((c) => c.task_id);
|
|
36746
|
-
const wakeMeta = {
|
|
36747
|
-
delivery_id: deliveryID,
|
|
36748
|
-
attempt: state.wakeRetryAttempts + 1,
|
|
36749
|
-
task_ids: taskIDs,
|
|
36750
|
-
directory: drainContext.directory,
|
|
36751
|
-
reminder_sha256: hashReminder(reminder),
|
|
36752
|
-
reminder_chars: reminder.length,
|
|
36753
|
-
wake_client_path: clientPath,
|
|
36754
|
-
prompt_context: promptContext ? {
|
|
36755
|
-
agent: promptContext.agent,
|
|
36756
|
-
model: promptContext.model ? {
|
|
36842
|
+
if (promptContext?.agent)
|
|
36843
|
+
body.agent = promptContext.agent;
|
|
36844
|
+
if (promptContext?.model) {
|
|
36845
|
+
body.model = {
|
|
36757
36846
|
providerID: promptContext.model.providerID,
|
|
36758
36847
|
modelID: promptContext.model.modelID
|
|
36759
|
-
}
|
|
36760
|
-
|
|
36761
|
-
|
|
36762
|
-
|
|
36763
|
-
|
|
36764
|
-
|
|
36765
|
-
|
|
36766
|
-
|
|
36767
|
-
|
|
36768
|
-
|
|
36769
|
-
|
|
36770
|
-
|
|
36848
|
+
};
|
|
36849
|
+
}
|
|
36850
|
+
if (promptContext?.variant)
|
|
36851
|
+
body.variant = promptContext.variant;
|
|
36852
|
+
const deliveryID2 = `aftdel_${randomUUID2()}`;
|
|
36853
|
+
const wakeMeta = {
|
|
36854
|
+
delivery_id: deliveryID2,
|
|
36855
|
+
attempt: state.wakeRetryAttempts + 1,
|
|
36856
|
+
task_ids: taskIDs,
|
|
36857
|
+
directory: drainContext.directory,
|
|
36858
|
+
reminder_sha256: hashReminder(reminder),
|
|
36859
|
+
reminder_chars: reminder.length,
|
|
36860
|
+
wake_client_path: clientPath,
|
|
36861
|
+
prompt_context: promptContext ? {
|
|
36862
|
+
agent: promptContext.agent,
|
|
36863
|
+
model: promptContext.model ? {
|
|
36864
|
+
providerID: promptContext.model.providerID,
|
|
36865
|
+
modelID: promptContext.model.modelID
|
|
36866
|
+
} : null,
|
|
36867
|
+
variant: promptContext.variant ?? null
|
|
36868
|
+
} : null
|
|
36869
|
+
};
|
|
36870
|
+
sessionLog(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync start`, {
|
|
36871
|
+
event: "bash_completion_wake_prompt_async_start",
|
|
36872
|
+
...wakeMeta
|
|
36771
36873
|
});
|
|
36772
|
-
|
|
36773
|
-
|
|
36774
|
-
|
|
36775
|
-
|
|
36874
|
+
try {
|
|
36875
|
+
await client.session.promptAsync({
|
|
36876
|
+
path: { id: drainContext.sessionID },
|
|
36877
|
+
body
|
|
36878
|
+
});
|
|
36879
|
+
} catch (err) {
|
|
36880
|
+
sessionWarn(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync error`, {
|
|
36881
|
+
event: "bash_completion_wake_prompt_async_error",
|
|
36882
|
+
delivery_id: deliveryID2,
|
|
36883
|
+
attempt: state.wakeRetryAttempts + 1,
|
|
36884
|
+
task_ids: taskIDs,
|
|
36885
|
+
wake_client_path: clientPath,
|
|
36886
|
+
error: err instanceof Error ? err.message : String(err)
|
|
36887
|
+
});
|
|
36888
|
+
throw err;
|
|
36889
|
+
}
|
|
36890
|
+
sessionLog(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync ok`, {
|
|
36891
|
+
event: "bash_completion_wake_prompt_async_ok",
|
|
36892
|
+
delivery_id: deliveryID2,
|
|
36776
36893
|
attempt: state.wakeRetryAttempts + 1,
|
|
36777
36894
|
task_ids: taskIDs,
|
|
36778
|
-
|
|
36895
|
+
wake_client_path: clientPath
|
|
36779
36896
|
});
|
|
36780
|
-
|
|
36897
|
+
return deliveryID2;
|
|
36898
|
+
};
|
|
36899
|
+
if (useLiveServerWake(drainContext.serverUrl) && drainContext.serverUrl) {
|
|
36900
|
+
try {
|
|
36901
|
+
const liveClient = getLiveServerClient(drainContext.serverUrl, drainContext.directory);
|
|
36902
|
+
const deliveryID2 = await sendPrompt(liveClient, "live-server");
|
|
36903
|
+
await ackCompletions(drainContext, deliveredCompletions, deliveryID2);
|
|
36904
|
+
return;
|
|
36905
|
+
} catch (err) {
|
|
36906
|
+
setLiveServerWakeAvailable(drainContext.serverUrl, false);
|
|
36907
|
+
sessionWarn(drainContext.sessionID, `${LOG_PREFIX} live-server wake failed; falling back`, {
|
|
36908
|
+
event: "bash_completion_wake_live_server_fallback",
|
|
36909
|
+
task_ids: taskIDs,
|
|
36910
|
+
directory: drainContext.directory,
|
|
36911
|
+
server_url: drainContext.serverUrl,
|
|
36912
|
+
attempt: state.wakeRetryAttempts + 1,
|
|
36913
|
+
error: err instanceof Error ? err.message : String(err)
|
|
36914
|
+
});
|
|
36915
|
+
const fallbackClient2 = getInProcessClient();
|
|
36916
|
+
const deliveryID2 = await sendPrompt(fallbackClient2, "in-process-fallback");
|
|
36917
|
+
state.retryDelayMs = null;
|
|
36918
|
+
state.wakeRetryAttempts = 0;
|
|
36919
|
+
state.wakeHardStopped = false;
|
|
36920
|
+
await ackCompletions(drainContext, deliveredCompletions, deliveryID2);
|
|
36921
|
+
return;
|
|
36922
|
+
}
|
|
36781
36923
|
}
|
|
36782
|
-
|
|
36783
|
-
|
|
36784
|
-
delivery_id: deliveryID,
|
|
36785
|
-
attempt: state.wakeRetryAttempts + 1,
|
|
36786
|
-
task_ids: taskIDs
|
|
36787
|
-
});
|
|
36924
|
+
const fallbackClient = getInProcessClient();
|
|
36925
|
+
const deliveryID = await sendPrompt(fallbackClient, "in-process-fallback");
|
|
36788
36926
|
await ackCompletions(drainContext, deliveredCompletions, deliveryID);
|
|
36789
36927
|
}, (err, hardStopped) => {
|
|
36790
36928
|
sessionWarn(drainContext.sessionID, hardStopped ? `${LOG_PREFIX} wake send failed ${MAX_WAKE_SEND_ATTEMPTS} times; stopping retries: ${err instanceof Error ? err.message : String(err)}` : `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -36965,7 +37103,8 @@ function scheduleWake(state, sendWake, onSendFailure, sessionID, includeDeferred
|
|
|
36965
37103
|
state.wakeDeferredTaskIds.delete(taskId);
|
|
36966
37104
|
state.pendingLongRunning = [];
|
|
36967
37105
|
state.pendingPatternMatches = [];
|
|
36968
|
-
|
|
37106
|
+
const completionAcks = completionAcksForDelivery(pending, pendingPatternMatches);
|
|
37107
|
+
sendWake(reminder, completionAcks).then(() => {
|
|
36969
37108
|
state.retryDelayMs = null;
|
|
36970
37109
|
state.wakeRetryAttempts = 0;
|
|
36971
37110
|
state.wakeHardStopped = false;
|
|
@@ -37030,7 +37169,7 @@ function ingestDrainedBgCompletions(sessionID, completions) {
|
|
|
37030
37169
|
state.outstandingTaskIds.delete(completion.task_id);
|
|
37031
37170
|
if (state.explicitControlTasks.has(completion.task_id)) {
|
|
37032
37171
|
state.explicitControlTasks.delete(completion.task_id);
|
|
37033
|
-
queuePendingPatternMatch(state, completionToExitPattern(completion));
|
|
37172
|
+
queuePendingPatternMatch(state, completionToExitPattern(completion, true));
|
|
37034
37173
|
continue;
|
|
37035
37174
|
}
|
|
37036
37175
|
if (state.consumedTaskIds.has(completion.task_id))
|
|
@@ -37066,10 +37205,10 @@ function bufferUnknownCompletion(state, completion) {
|
|
|
37066
37205
|
function pruneUnknownCompletions(state, now) {
|
|
37067
37206
|
state.unknownCompletions = state.unknownCompletions.filter((entry) => now - entry.receivedAt <= UNKNOWN_COMPLETION_TTL_MS);
|
|
37068
37207
|
}
|
|
37069
|
-
function completionToExitPattern(completion) {
|
|
37208
|
+
function completionToExitPattern(completion, ackCompletionOnDelivery = false) {
|
|
37070
37209
|
const status = formatStatus(completion);
|
|
37071
37210
|
const preview = formatOutputPreview(completion).replace(/^ {4}/gm, "").slice(-300);
|
|
37072
|
-
|
|
37211
|
+
const entry = {
|
|
37073
37212
|
task_id: completion.task_id,
|
|
37074
37213
|
session_id: "",
|
|
37075
37214
|
watch_id: "exit",
|
|
@@ -37080,6 +37219,20 @@ ${preview}` : `task ${completion.task_id} exited (${status})`,
|
|
|
37080
37219
|
once: true,
|
|
37081
37220
|
reason: "task_exit"
|
|
37082
37221
|
};
|
|
37222
|
+
if (ackCompletionOnDelivery)
|
|
37223
|
+
entry.ackCompletionOnDelivery = true;
|
|
37224
|
+
return entry;
|
|
37225
|
+
}
|
|
37226
|
+
function completionAcksForDelivery(completions, patternMatches) {
|
|
37227
|
+
const acks = [...completions];
|
|
37228
|
+
const ackedTaskIds = new Set(acks.map((completion) => completion.task_id));
|
|
37229
|
+
for (const match of patternMatches) {
|
|
37230
|
+
if (!match.ackCompletionOnDelivery || ackedTaskIds.has(match.task_id))
|
|
37231
|
+
continue;
|
|
37232
|
+
acks.push({ task_id: match.task_id, status: "unknown", exit_code: null, command: "" });
|
|
37233
|
+
ackedTaskIds.add(match.task_id);
|
|
37234
|
+
}
|
|
37235
|
+
return acks;
|
|
37083
37236
|
}
|
|
37084
37237
|
function isBgCompletion(value) {
|
|
37085
37238
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -37147,9 +37300,9 @@ function formatDuration(completion) {
|
|
|
37147
37300
|
|
|
37148
37301
|
// src/config.ts
|
|
37149
37302
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
37150
|
-
import { existsSync as
|
|
37303
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
37151
37304
|
import { homedir as homedir5 } from "node:os";
|
|
37152
|
-
import { join as
|
|
37305
|
+
import { join as join9 } from "node:path";
|
|
37153
37306
|
|
|
37154
37307
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
37155
37308
|
var exports_external = {};
|
|
@@ -51032,13 +51185,13 @@ function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
|
|
|
51032
51185
|
return movedKeys;
|
|
51033
51186
|
}
|
|
51034
51187
|
function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
|
|
51035
|
-
if (!
|
|
51188
|
+
if (!existsSync7(configPath)) {
|
|
51036
51189
|
return { migrated: false, oldKeys: [] };
|
|
51037
51190
|
}
|
|
51038
51191
|
let tmpPath = null;
|
|
51039
51192
|
let oldKeys = [];
|
|
51040
51193
|
try {
|
|
51041
|
-
const content =
|
|
51194
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
51042
51195
|
const rawConfig = import_comment_json.parse(content);
|
|
51043
51196
|
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
51044
51197
|
return { migrated: false, oldKeys: [] };
|
|
@@ -51056,7 +51209,7 @@ function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
|
|
|
51056
51209
|
${serialized}` : serialized;
|
|
51057
51210
|
tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
51058
51211
|
writeFileSync3(tmpPath, nextContent, "utf-8");
|
|
51059
|
-
|
|
51212
|
+
renameSync4(tmpPath, configPath);
|
|
51060
51213
|
logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
|
|
51061
51214
|
return { migrated: true, oldKeys };
|
|
51062
51215
|
} catch (err) {
|
|
@@ -51076,10 +51229,10 @@ ${serialized}` : serialized;
|
|
|
51076
51229
|
function detectConfigFile(basePath) {
|
|
51077
51230
|
const jsoncPath = `${basePath}.jsonc`;
|
|
51078
51231
|
const jsonPath = `${basePath}.json`;
|
|
51079
|
-
if (
|
|
51232
|
+
if (existsSync7(jsoncPath)) {
|
|
51080
51233
|
return { format: "jsonc", path: jsoncPath };
|
|
51081
51234
|
}
|
|
51082
|
-
if (
|
|
51235
|
+
if (existsSync7(jsonPath)) {
|
|
51083
51236
|
return { format: "json", path: jsonPath };
|
|
51084
51237
|
}
|
|
51085
51238
|
return { format: "none", path: jsonPath };
|
|
@@ -51112,10 +51265,10 @@ function parseConfigPartially(rawConfig) {
|
|
|
51112
51265
|
}
|
|
51113
51266
|
function loadConfigFromPath(configPath) {
|
|
51114
51267
|
try {
|
|
51115
|
-
if (!
|
|
51268
|
+
if (!existsSync7(configPath)) {
|
|
51116
51269
|
return null;
|
|
51117
51270
|
}
|
|
51118
|
-
const content =
|
|
51271
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
51119
51272
|
const rawConfig = import_comment_json.parse(content);
|
|
51120
51273
|
migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
|
|
51121
51274
|
const result = AftConfigSchema.safeParse(rawConfig);
|
|
@@ -51276,17 +51429,17 @@ function getOpenCodeConfigDir() {
|
|
|
51276
51429
|
if (envDir) {
|
|
51277
51430
|
return envDir;
|
|
51278
51431
|
}
|
|
51279
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ||
|
|
51280
|
-
return
|
|
51432
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join9(homedir5(), ".config");
|
|
51433
|
+
return join9(xdgConfig, "opencode");
|
|
51281
51434
|
}
|
|
51282
51435
|
function loadAftConfig(projectDirectory) {
|
|
51283
51436
|
const configDir = getOpenCodeConfigDir();
|
|
51284
|
-
const userBasePath =
|
|
51437
|
+
const userBasePath = join9(configDir, "aft");
|
|
51285
51438
|
migrateAftConfigFile(`${userBasePath}.jsonc`);
|
|
51286
51439
|
migrateAftConfigFile(`${userBasePath}.json`);
|
|
51287
51440
|
const userDetected = detectConfigFile(userBasePath);
|
|
51288
51441
|
const userConfigPath = userDetected.format !== "none" ? userDetected.path : `${userBasePath}.json`;
|
|
51289
|
-
const projectBasePath =
|
|
51442
|
+
const projectBasePath = join9(projectDirectory, ".opencode", "aft");
|
|
51290
51443
|
migrateAftConfigFile(`${projectBasePath}.jsonc`);
|
|
51291
51444
|
migrateAftConfigFile(`${projectBasePath}.json`);
|
|
51292
51445
|
const projectDetected = detectConfigFile(projectBasePath);
|
|
@@ -51311,44 +51464,53 @@ function loadAftConfig(projectDirectory) {
|
|
|
51311
51464
|
}
|
|
51312
51465
|
|
|
51313
51466
|
// src/hooks/auto-update-checker/index.ts
|
|
51314
|
-
import {
|
|
51315
|
-
|
|
51467
|
+
import {
|
|
51468
|
+
closeSync as closeSync3,
|
|
51469
|
+
existsSync as existsSync10,
|
|
51470
|
+
mkdirSync as mkdirSync7,
|
|
51471
|
+
openSync as openSync3,
|
|
51472
|
+
readFileSync as readFileSync7,
|
|
51473
|
+
renameSync as renameSync5,
|
|
51474
|
+
rmSync as rmSync4,
|
|
51475
|
+
writeFileSync as writeFileSync6
|
|
51476
|
+
} from "node:fs";
|
|
51477
|
+
import { dirname as dirname6 } from "node:path";
|
|
51316
51478
|
|
|
51317
51479
|
// src/hooks/auto-update-checker/cache.ts
|
|
51318
51480
|
var import_comment_json3 = __toESM(require_src2(), 1);
|
|
51319
51481
|
import { spawn as spawn2 } from "node:child_process";
|
|
51320
|
-
import { cpSync, existsSync as
|
|
51482
|
+
import { cpSync, existsSync as existsSync9, mkdtempSync, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
51321
51483
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
51322
|
-
import { basename, dirname as
|
|
51484
|
+
import { basename as basename2, dirname as dirname5, join as join12 } from "node:path";
|
|
51323
51485
|
|
|
51324
51486
|
// src/hooks/auto-update-checker/checker.ts
|
|
51325
51487
|
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
51326
|
-
import { existsSync as
|
|
51488
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, statSync as statSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
51327
51489
|
import { homedir as homedir7 } from "node:os";
|
|
51328
|
-
import { dirname as
|
|
51490
|
+
import { dirname as dirname4, isAbsolute, join as join11, resolve as resolve2 } from "node:path";
|
|
51329
51491
|
import { fileURLToPath } from "node:url";
|
|
51330
51492
|
|
|
51331
51493
|
// src/hooks/auto-update-checker/constants.ts
|
|
51332
51494
|
import { homedir as homedir6, platform } from "node:os";
|
|
51333
|
-
import { join as
|
|
51495
|
+
import { join as join10 } from "node:path";
|
|
51334
51496
|
var PACKAGE_NAME = "@cortexkit/aft-opencode";
|
|
51335
51497
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
51336
51498
|
var NPM_FETCH_TIMEOUT = 1e4;
|
|
51337
51499
|
function getOpenCodeCacheRoot() {
|
|
51338
51500
|
if (platform() === "win32") {
|
|
51339
|
-
return
|
|
51501
|
+
return join10(process.env.LOCALAPPDATA ?? homedir6(), "opencode");
|
|
51340
51502
|
}
|
|
51341
|
-
return
|
|
51503
|
+
return join10(homedir6(), ".cache", "opencode");
|
|
51342
51504
|
}
|
|
51343
51505
|
function getOpenCodeConfigRoot() {
|
|
51344
51506
|
if (platform() === "win32") {
|
|
51345
|
-
return
|
|
51507
|
+
return join10(process.env.APPDATA ?? join10(homedir6(), "AppData", "Roaming"), "opencode");
|
|
51346
51508
|
}
|
|
51347
|
-
return
|
|
51509
|
+
return join10(process.env.XDG_CONFIG_HOME ?? join10(homedir6(), ".config"), "opencode");
|
|
51348
51510
|
}
|
|
51349
|
-
var CACHE_DIR =
|
|
51350
|
-
var USER_OPENCODE_CONFIG =
|
|
51351
|
-
var USER_OPENCODE_CONFIG_JSONC =
|
|
51511
|
+
var CACHE_DIR = join10(getOpenCodeCacheRoot(), "packages");
|
|
51512
|
+
var USER_OPENCODE_CONFIG = join10(getOpenCodeConfigRoot(), "opencode.json");
|
|
51513
|
+
var USER_OPENCODE_CONFIG_JSONC = join10(getOpenCodeConfigRoot(), "opencode.jsonc");
|
|
51352
51514
|
|
|
51353
51515
|
// src/hooks/auto-update-checker/types.ts
|
|
51354
51516
|
var NpmPackageEnvelopeSchema = exports_external.object({
|
|
@@ -51406,8 +51568,8 @@ function extractChannel(version2) {
|
|
|
51406
51568
|
}
|
|
51407
51569
|
function getConfigPaths(directory) {
|
|
51408
51570
|
return [
|
|
51409
|
-
|
|
51410
|
-
|
|
51571
|
+
join11(directory, ".opencode", "opencode.json"),
|
|
51572
|
+
join11(directory, ".opencode", "opencode.jsonc"),
|
|
51411
51573
|
USER_OPENCODE_CONFIG,
|
|
51412
51574
|
USER_OPENCODE_CONFIG_JSONC
|
|
51413
51575
|
];
|
|
@@ -51422,14 +51584,14 @@ function resolvePathPluginSpec(spec, configPath) {
|
|
|
51422
51584
|
}
|
|
51423
51585
|
if (isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec))
|
|
51424
51586
|
return spec;
|
|
51425
|
-
return resolve2(
|
|
51587
|
+
return resolve2(dirname4(configPath), spec);
|
|
51426
51588
|
}
|
|
51427
51589
|
function getLocalDevPath(directory) {
|
|
51428
51590
|
for (const configPath of getConfigPaths(directory)) {
|
|
51429
51591
|
try {
|
|
51430
|
-
if (!
|
|
51592
|
+
if (!existsSync8(configPath))
|
|
51431
51593
|
continue;
|
|
51432
|
-
const rawConfig = parseJsonConfig(
|
|
51594
|
+
const rawConfig = parseJsonConfig(readFileSync5(configPath, "utf-8"));
|
|
51433
51595
|
const plugins = getPluginEntries(rawConfig);
|
|
51434
51596
|
for (const entry of plugins) {
|
|
51435
51597
|
if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
|
|
@@ -51439,7 +51601,7 @@ function getLocalDevPath(directory) {
|
|
|
51439
51601
|
const pkgPath = findPackageJsonUp(localPath);
|
|
51440
51602
|
if (!pkgPath)
|
|
51441
51603
|
continue;
|
|
51442
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
51604
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync5(pkgPath, "utf-8")));
|
|
51443
51605
|
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
51444
51606
|
return localPath;
|
|
51445
51607
|
}
|
|
@@ -51451,17 +51613,17 @@ function getLocalDevPath(directory) {
|
|
|
51451
51613
|
function findPackageJsonUp(startPath) {
|
|
51452
51614
|
try {
|
|
51453
51615
|
const stat = statSync3(startPath);
|
|
51454
|
-
let dir = stat.isDirectory() ? startPath :
|
|
51616
|
+
let dir = stat.isDirectory() ? startPath : dirname4(startPath);
|
|
51455
51617
|
for (let i = 0;i < 10; i++) {
|
|
51456
|
-
const pkgPath =
|
|
51457
|
-
if (
|
|
51618
|
+
const pkgPath = join11(dir, "package.json");
|
|
51619
|
+
if (existsSync8(pkgPath)) {
|
|
51458
51620
|
try {
|
|
51459
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
51621
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync5(pkgPath, "utf-8")));
|
|
51460
51622
|
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
51461
51623
|
return pkgPath;
|
|
51462
51624
|
} catch {}
|
|
51463
51625
|
}
|
|
51464
|
-
const parent =
|
|
51626
|
+
const parent = dirname4(dir);
|
|
51465
51627
|
if (parent === dir)
|
|
51466
51628
|
break;
|
|
51467
51629
|
dir = parent;
|
|
@@ -51477,7 +51639,7 @@ function getLocalDevVersion(directory) {
|
|
|
51477
51639
|
const pkgPath = findPackageJsonUp(localPath);
|
|
51478
51640
|
if (!pkgPath)
|
|
51479
51641
|
return null;
|
|
51480
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
51642
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync5(pkgPath, "utf-8")));
|
|
51481
51643
|
return pkg.success ? pkg.data.version ?? null : null;
|
|
51482
51644
|
} catch {
|
|
51483
51645
|
return null;
|
|
@@ -51485,7 +51647,7 @@ function getLocalDevVersion(directory) {
|
|
|
51485
51647
|
}
|
|
51486
51648
|
function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
|
|
51487
51649
|
try {
|
|
51488
|
-
return findPackageJsonUp(
|
|
51650
|
+
return findPackageJsonUp(dirname4(fileURLToPath(currentModuleUrl)));
|
|
51489
51651
|
} catch (err) {
|
|
51490
51652
|
warn2(`[auto-update-checker] Failed to resolve runtime package path: ${String(err)}`);
|
|
51491
51653
|
return null;
|
|
@@ -51494,9 +51656,9 @@ function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
|
|
|
51494
51656
|
function findPluginEntry(directory) {
|
|
51495
51657
|
for (const configPath of getConfigPaths(directory)) {
|
|
51496
51658
|
try {
|
|
51497
|
-
if (!
|
|
51659
|
+
if (!existsSync8(configPath))
|
|
51498
51660
|
continue;
|
|
51499
|
-
const rawConfig = parseJsonConfig(
|
|
51661
|
+
const rawConfig = parseJsonConfig(readFileSync5(configPath, "utf-8"));
|
|
51500
51662
|
const plugins = getPluginEntries(rawConfig);
|
|
51501
51663
|
for (const entry of plugins) {
|
|
51502
51664
|
if (entry === PACKAGE_NAME) {
|
|
@@ -51514,7 +51676,7 @@ function findPluginEntry(directory) {
|
|
|
51514
51676
|
}
|
|
51515
51677
|
var cachedPackageVersion = null;
|
|
51516
51678
|
function getSpecCachePackageJsonPath(spec) {
|
|
51517
|
-
return
|
|
51679
|
+
return join11(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
|
|
51518
51680
|
}
|
|
51519
51681
|
function getCachedVersion(spec) {
|
|
51520
51682
|
if (!spec && cachedPackageVersion)
|
|
@@ -51523,13 +51685,13 @@ function getCachedVersion(spec) {
|
|
|
51523
51685
|
getCurrentRuntimePackageJsonPath(),
|
|
51524
51686
|
spec ? getSpecCachePackageJsonPath(spec) : null,
|
|
51525
51687
|
getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
|
|
51526
|
-
|
|
51688
|
+
join11(homedir7(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
|
|
51527
51689
|
].filter(isString);
|
|
51528
51690
|
for (const packageJsonPath of candidates) {
|
|
51529
51691
|
try {
|
|
51530
|
-
if (!
|
|
51692
|
+
if (!existsSync8(packageJsonPath))
|
|
51531
51693
|
continue;
|
|
51532
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
51694
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync5(packageJsonPath, "utf-8")));
|
|
51533
51695
|
if (pkg.success && pkg.data.version) {
|
|
51534
51696
|
if (!spec)
|
|
51535
51697
|
cachedPackageVersion = pkg.data.version;
|
|
@@ -51571,17 +51733,17 @@ async function getLatestVersion(channel = "latest", options = {}) {
|
|
|
51571
51733
|
// src/hooks/auto-update-checker/cache.ts
|
|
51572
51734
|
var pendingSnapshots = new Map;
|
|
51573
51735
|
function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
|
|
51574
|
-
const packageDir =
|
|
51575
|
-
const lockfilePath =
|
|
51576
|
-
const tempDir = mkdtempSync(
|
|
51577
|
-
const stagedPackageDir =
|
|
51736
|
+
const packageDir = join12(installDir, "node_modules", packageName);
|
|
51737
|
+
const lockfilePath = join12(installDir, "package-lock.json");
|
|
51738
|
+
const tempDir = mkdtempSync(join12(tmpdir3(), "aft-auto-update-"));
|
|
51739
|
+
const stagedPackageDir = existsSync9(packageDir) ? join12(tempDir, "package") : null;
|
|
51578
51740
|
if (stagedPackageDir)
|
|
51579
51741
|
cpSync(packageDir, stagedPackageDir, { recursive: true });
|
|
51580
51742
|
return {
|
|
51581
51743
|
packageJsonPath,
|
|
51582
|
-
packageJson:
|
|
51744
|
+
packageJson: existsSync9(packageJsonPath) ? readFileSync6(packageJsonPath, "utf-8") : null,
|
|
51583
51745
|
lockfilePath,
|
|
51584
|
-
lockfile:
|
|
51746
|
+
lockfile: existsSync9(lockfilePath) ? readFileSync6(lockfilePath, "utf-8") : null,
|
|
51585
51747
|
packageDir,
|
|
51586
51748
|
stagedPackageDir,
|
|
51587
51749
|
tempDir
|
|
@@ -51608,18 +51770,18 @@ function restoreAutoUpdateSnapshot(snapshot) {
|
|
|
51608
51770
|
function stripPackageNameFromPath(pathValue, packageName) {
|
|
51609
51771
|
let current = pathValue;
|
|
51610
51772
|
for (const segment of [...packageName.split("/")].reverse()) {
|
|
51611
|
-
if (
|
|
51773
|
+
if (basename2(current) !== segment)
|
|
51612
51774
|
return null;
|
|
51613
|
-
current =
|
|
51775
|
+
current = dirname5(current);
|
|
51614
51776
|
}
|
|
51615
51777
|
return current;
|
|
51616
51778
|
}
|
|
51617
51779
|
function removeFromPackageLock(installDir, packageName) {
|
|
51618
|
-
const lockPath =
|
|
51619
|
-
if (!
|
|
51780
|
+
const lockPath = join12(installDir, "package-lock.json");
|
|
51781
|
+
if (!existsSync9(lockPath))
|
|
51620
51782
|
return false;
|
|
51621
51783
|
try {
|
|
51622
|
-
const lock = import_comment_json3.parse(
|
|
51784
|
+
const lock = import_comment_json3.parse(readFileSync6(lockPath, "utf-8"));
|
|
51623
51785
|
let modified = false;
|
|
51624
51786
|
if (lock.packages) {
|
|
51625
51787
|
const key = `node_modules/${packageName}`;
|
|
@@ -51642,10 +51804,10 @@ function removeFromPackageLock(installDir, packageName) {
|
|
|
51642
51804
|
}
|
|
51643
51805
|
}
|
|
51644
51806
|
function ensureDependencyVersion(packageJsonPath, packageName, version2) {
|
|
51645
|
-
if (!
|
|
51807
|
+
if (!existsSync9(packageJsonPath))
|
|
51646
51808
|
return false;
|
|
51647
51809
|
try {
|
|
51648
|
-
const raw = import_comment_json3.parse(
|
|
51810
|
+
const raw = import_comment_json3.parse(readFileSync6(packageJsonPath, "utf-8"));
|
|
51649
51811
|
const pkgJson = PackageJsonSchema.safeParse(raw);
|
|
51650
51812
|
if (!pkgJson.success)
|
|
51651
51813
|
return false;
|
|
@@ -51664,8 +51826,8 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
|
|
|
51664
51826
|
}
|
|
51665
51827
|
}
|
|
51666
51828
|
function removeInstalledPackage(installDir, packageName) {
|
|
51667
|
-
const packageDir =
|
|
51668
|
-
if (!
|
|
51829
|
+
const packageDir = join12(installDir, "node_modules", packageName);
|
|
51830
|
+
if (!existsSync9(packageDir))
|
|
51669
51831
|
return false;
|
|
51670
51832
|
rmSync3(packageDir, { recursive: true, force: true });
|
|
51671
51833
|
log2(`[auto-update-checker] Package removed: ${packageDir}`);
|
|
@@ -51673,19 +51835,19 @@ function removeInstalledPackage(installDir, packageName) {
|
|
|
51673
51835
|
}
|
|
51674
51836
|
function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
|
|
51675
51837
|
if (runtimePackageJsonPath) {
|
|
51676
|
-
const packageDir =
|
|
51838
|
+
const packageDir = dirname5(runtimePackageJsonPath);
|
|
51677
51839
|
const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
|
|
51678
|
-
if (nodeModulesDir &&
|
|
51679
|
-
const installDir =
|
|
51680
|
-
const packageJsonPath =
|
|
51681
|
-
if (
|
|
51840
|
+
if (nodeModulesDir && basename2(nodeModulesDir) === "node_modules") {
|
|
51841
|
+
const installDir = dirname5(nodeModulesDir);
|
|
51842
|
+
const packageJsonPath = join12(installDir, "package.json");
|
|
51843
|
+
if (existsSync9(packageJsonPath))
|
|
51682
51844
|
return { installDir, packageJsonPath };
|
|
51683
51845
|
}
|
|
51684
51846
|
return null;
|
|
51685
51847
|
}
|
|
51686
|
-
const legacyPackageJsonPath =
|
|
51687
|
-
if (
|
|
51688
|
-
return { installDir:
|
|
51848
|
+
const legacyPackageJsonPath = join12(dirname5(CACHE_DIR), "package.json");
|
|
51849
|
+
if (existsSync9(legacyPackageJsonPath)) {
|
|
51850
|
+
return { installDir: dirname5(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
|
|
51689
51851
|
}
|
|
51690
51852
|
return null;
|
|
51691
51853
|
}
|
|
@@ -51835,36 +51997,76 @@ function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
|
51835
51997
|
async function maybeRunCheck(ctx, options) {
|
|
51836
51998
|
if (options.signal.aborted)
|
|
51837
51999
|
return;
|
|
51838
|
-
|
|
52000
|
+
const checkSlot = claimCheckSlot(options.storageDir, options.checkIntervalMs);
|
|
52001
|
+
if (!checkSlot) {
|
|
51839
52002
|
log2("[auto-update-checker] Skipping check (another instance ran one recently)");
|
|
51840
52003
|
return;
|
|
51841
52004
|
}
|
|
51842
|
-
|
|
52005
|
+
try {
|
|
52006
|
+
await runStartupCheck(ctx, options);
|
|
52007
|
+
} finally {
|
|
52008
|
+
checkSlot.release();
|
|
52009
|
+
}
|
|
51843
52010
|
}
|
|
51844
52011
|
function claimCheckSlot(storageDir, intervalMs) {
|
|
51845
52012
|
if (!storageDir)
|
|
51846
|
-
return
|
|
52013
|
+
return { release: () => {} };
|
|
52014
|
+
const file2 = repairRootScopedStorageFile(storageDir, "opencode", TIMESTAMP_FILENAME);
|
|
51847
52015
|
try {
|
|
51848
|
-
|
|
51849
|
-
|
|
51850
|
-
|
|
51851
|
-
|
|
51852
|
-
|
|
51853
|
-
|
|
51854
|
-
|
|
51855
|
-
|
|
51856
|
-
|
|
52016
|
+
if (hasRecentCheckTimestamp(file2, intervalMs))
|
|
52017
|
+
return null;
|
|
52018
|
+
mkdirSync7(dirname6(file2), { recursive: true });
|
|
52019
|
+
const lockPath = `${file2}.lock`;
|
|
52020
|
+
let lockFd;
|
|
52021
|
+
try {
|
|
52022
|
+
lockFd = openSync3(lockPath, "wx");
|
|
52023
|
+
writeFileSync6(lockFd, JSON.stringify({ pid: process.pid, startedMs: Date.now() }));
|
|
52024
|
+
} catch (err) {
|
|
52025
|
+
if (err.code !== "EEXIST") {
|
|
52026
|
+
warn2(`[auto-update-checker] Could not acquire update lock: ${String(err)}`);
|
|
52027
|
+
}
|
|
52028
|
+
return null;
|
|
52029
|
+
}
|
|
52030
|
+
const lock = {
|
|
52031
|
+
release: () => {
|
|
52032
|
+
try {
|
|
52033
|
+
closeSync3(lockFd);
|
|
52034
|
+
} catch {}
|
|
52035
|
+
rmSync4(lockPath, { force: true });
|
|
52036
|
+
}
|
|
52037
|
+
};
|
|
52038
|
+
try {
|
|
52039
|
+
if (hasRecentCheckTimestamp(file2, intervalMs)) {
|
|
52040
|
+
lock.release();
|
|
52041
|
+
return null;
|
|
52042
|
+
}
|
|
52043
|
+
writeCheckTimestamp(file2);
|
|
52044
|
+
return lock;
|
|
52045
|
+
} catch (err) {
|
|
52046
|
+
lock.release();
|
|
52047
|
+
throw err;
|
|
51857
52048
|
}
|
|
51858
|
-
mkdirSync6(dirname5(file2), { recursive: true });
|
|
51859
|
-
const tmp = `${file2}.tmp.${process.pid}`;
|
|
51860
|
-
writeFileSync6(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
|
|
51861
|
-
renameSync4(tmp, file2);
|
|
51862
|
-
return true;
|
|
51863
52049
|
} catch (err) {
|
|
51864
52050
|
warn2(`[auto-update-checker] Could not coordinate via timestamp file: ${String(err)}`);
|
|
51865
|
-
return
|
|
52051
|
+
return null;
|
|
51866
52052
|
}
|
|
51867
52053
|
}
|
|
52054
|
+
function hasRecentCheckTimestamp(file2, intervalMs) {
|
|
52055
|
+
if (!existsSync10(file2))
|
|
52056
|
+
return false;
|
|
52057
|
+
try {
|
|
52058
|
+
const raw = JSON.parse(readFileSync7(file2, "utf-8"));
|
|
52059
|
+
const last = typeof raw.lastCheckedMs === "number" ? raw.lastCheckedMs : 0;
|
|
52060
|
+
return Number.isFinite(last) && Date.now() - last < intervalMs;
|
|
52061
|
+
} catch {
|
|
52062
|
+
return false;
|
|
52063
|
+
}
|
|
52064
|
+
}
|
|
52065
|
+
function writeCheckTimestamp(file2) {
|
|
52066
|
+
const tmp = `${file2}.tmp.${process.pid}`;
|
|
52067
|
+
writeFileSync6(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
|
|
52068
|
+
renameSync5(tmp, file2);
|
|
52069
|
+
}
|
|
51868
52070
|
async function runStartupCheck(ctx, options) {
|
|
51869
52071
|
if (options.signal.aborted)
|
|
51870
52072
|
return;
|
|
@@ -51944,21 +52146,24 @@ ${installResult.stderrTail}` : "";
|
|
|
51944
52146
|
warn2(`[auto-update-checker] npm install failed; update not installed${failureDetail}${stderrDetail}`);
|
|
51945
52147
|
}
|
|
51946
52148
|
function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
|
|
51947
|
-
ctx.client.tui
|
|
52149
|
+
const tui = ctx.client.tui;
|
|
52150
|
+
if (typeof tui?.showToast !== "function")
|
|
52151
|
+
return;
|
|
52152
|
+
tui.showToast({ body: { title, message, variant, duration: duration3 } }).catch(() => {});
|
|
51948
52153
|
}
|
|
51949
52154
|
|
|
51950
52155
|
// src/lsp-auto-install.ts
|
|
51951
52156
|
import { spawn as spawn3 } from "node:child_process";
|
|
51952
52157
|
import { createHash as createHash5 } from "node:crypto";
|
|
51953
|
-
import { createReadStream, mkdirSync as
|
|
52158
|
+
import { createReadStream, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync5, statSync as statSync5 } from "node:fs";
|
|
51954
52159
|
import { join as join15 } from "node:path";
|
|
51955
52160
|
|
|
51956
52161
|
// src/lsp-cache.ts
|
|
51957
52162
|
import {
|
|
51958
|
-
closeSync as
|
|
51959
|
-
mkdirSync as
|
|
51960
|
-
openSync as
|
|
51961
|
-
readFileSync as
|
|
52163
|
+
closeSync as closeSync4,
|
|
52164
|
+
mkdirSync as mkdirSync8,
|
|
52165
|
+
openSync as openSync4,
|
|
52166
|
+
readFileSync as readFileSync8,
|
|
51962
52167
|
statSync as statSync4,
|
|
51963
52168
|
unlinkSync as unlinkSync5,
|
|
51964
52169
|
writeFileSync as writeFileSync7
|
|
@@ -52006,7 +52211,7 @@ function lspBinaryCandidates(binary) {
|
|
|
52006
52211
|
var INSTALLED_META_FILE = ".aft-installed";
|
|
52007
52212
|
function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
52008
52213
|
try {
|
|
52009
|
-
|
|
52214
|
+
mkdirSync8(installDir, { recursive: true });
|
|
52010
52215
|
const meta3 = {
|
|
52011
52216
|
version: version2,
|
|
52012
52217
|
installedAt: new Date().toISOString(),
|
|
@@ -52022,7 +52227,7 @@ function readInstalledMetaIn(installDir) {
|
|
|
52022
52227
|
try {
|
|
52023
52228
|
if (!statSync4(path2).isFile())
|
|
52024
52229
|
return null;
|
|
52025
|
-
const raw =
|
|
52230
|
+
const raw = readFileSync8(path2, "utf8");
|
|
52026
52231
|
const parsed = JSON.parse(raw);
|
|
52027
52232
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
52028
52233
|
return null;
|
|
@@ -52046,17 +52251,17 @@ function lockPath(npmPackage) {
|
|
|
52046
52251
|
}
|
|
52047
52252
|
var STALE_LOCK_MS2 = 30 * 60 * 1000;
|
|
52048
52253
|
function acquireInstallLock(lockKey) {
|
|
52049
|
-
|
|
52254
|
+
mkdirSync8(lspPackageDir(lockKey), { recursive: true });
|
|
52050
52255
|
const lock = lockPath(lockKey);
|
|
52051
52256
|
const tryClaim = () => {
|
|
52052
52257
|
try {
|
|
52053
|
-
const fd =
|
|
52258
|
+
const fd = openSync4(lock, "wx");
|
|
52054
52259
|
try {
|
|
52055
52260
|
writeFileSync7(fd, `${process.pid}
|
|
52056
52261
|
${new Date().toISOString()}
|
|
52057
52262
|
`);
|
|
52058
52263
|
} finally {
|
|
52059
|
-
|
|
52264
|
+
closeSync4(fd);
|
|
52060
52265
|
}
|
|
52061
52266
|
return true;
|
|
52062
52267
|
} catch (err) {
|
|
@@ -52072,7 +52277,7 @@ ${new Date().toISOString()}
|
|
|
52072
52277
|
let owningPid = null;
|
|
52073
52278
|
let lockMtimeMs = 0;
|
|
52074
52279
|
try {
|
|
52075
|
-
const raw =
|
|
52280
|
+
const raw = readFileSync8(lock, "utf8");
|
|
52076
52281
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
52077
52282
|
const parsed = Number.parseInt(firstLine, 10);
|
|
52078
52283
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -52110,7 +52315,7 @@ function releaseInstallLock(lockKey) {
|
|
|
52110
52315
|
try {
|
|
52111
52316
|
let owningPid = null;
|
|
52112
52317
|
try {
|
|
52113
|
-
const raw =
|
|
52318
|
+
const raw = readFileSync8(lock, "utf8");
|
|
52114
52319
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
52115
52320
|
const parsed = Number.parseInt(firstLine, 10);
|
|
52116
52321
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -52151,7 +52356,7 @@ var VERSION_CHECK_FILE = ".aft-version-check";
|
|
|
52151
52356
|
function readVersionCheck(npmPackage) {
|
|
52152
52357
|
const file2 = join13(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
52153
52358
|
try {
|
|
52154
|
-
const raw =
|
|
52359
|
+
const raw = readFileSync8(file2, "utf8");
|
|
52155
52360
|
const parsed = JSON.parse(raw);
|
|
52156
52361
|
if (typeof parsed.last_checked === "string") {
|
|
52157
52362
|
return {
|
|
@@ -52165,7 +52370,7 @@ function readVersionCheck(npmPackage) {
|
|
|
52165
52370
|
}
|
|
52166
52371
|
}
|
|
52167
52372
|
function writeVersionCheck(npmPackage, latest) {
|
|
52168
|
-
|
|
52373
|
+
mkdirSync8(lspPackageDir(npmPackage), { recursive: true });
|
|
52169
52374
|
const file2 = join13(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
52170
52375
|
const record2 = {
|
|
52171
52376
|
last_checked: new Date().toISOString(),
|
|
@@ -52328,7 +52533,7 @@ var NPM_LSP_TABLE = [
|
|
|
52328
52533
|
];
|
|
52329
52534
|
|
|
52330
52535
|
// src/lsp-project-relevance.ts
|
|
52331
|
-
import { existsSync as
|
|
52536
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
|
|
52332
52537
|
import { join as join14 } from "node:path";
|
|
52333
52538
|
var MAX_WALK_DIRS = 200;
|
|
52334
52539
|
var MAX_WALK_DEPTH = 4;
|
|
@@ -52346,7 +52551,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
|
|
|
52346
52551
|
if (!rootMarkers)
|
|
52347
52552
|
return false;
|
|
52348
52553
|
for (const marker of rootMarkers) {
|
|
52349
|
-
if (
|
|
52554
|
+
if (existsSync11(join14(projectRoot, marker)))
|
|
52350
52555
|
return true;
|
|
52351
52556
|
}
|
|
52352
52557
|
return false;
|
|
@@ -52370,7 +52575,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
|
|
|
52370
52575
|
}
|
|
52371
52576
|
function readPackageJson(projectRoot) {
|
|
52372
52577
|
try {
|
|
52373
|
-
const raw =
|
|
52578
|
+
const raw = readFileSync9(join14(projectRoot, "package.json"), "utf8");
|
|
52374
52579
|
const parsed = JSON.parse(raw);
|
|
52375
52580
|
if (typeof parsed !== "object" || parsed === null)
|
|
52376
52581
|
return null;
|
|
@@ -52697,16 +52902,16 @@ function installedBinaryPath(spec) {
|
|
|
52697
52902
|
return null;
|
|
52698
52903
|
}
|
|
52699
52904
|
function sha256OfFileSync(path2) {
|
|
52700
|
-
return createHash5("sha256").update(
|
|
52905
|
+
return createHash5("sha256").update(readFileSync10(path2)).digest("hex");
|
|
52701
52906
|
}
|
|
52702
52907
|
function quarantineCachedNpmInstall(spec, reason) {
|
|
52703
52908
|
const packageDir = lspPackageDir(spec.npm);
|
|
52704
52909
|
const dest = join15(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
|
|
52705
52910
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
52706
52911
|
try {
|
|
52707
|
-
|
|
52708
|
-
|
|
52709
|
-
|
|
52912
|
+
mkdirSync9(join15(dest, ".."), { recursive: true });
|
|
52913
|
+
rmSync5(dest, { recursive: true, force: true });
|
|
52914
|
+
renameSync6(packageDir, dest);
|
|
52710
52915
|
} catch (err) {
|
|
52711
52916
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
|
|
52712
52917
|
}
|
|
@@ -52782,25 +52987,25 @@ function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
|
|
|
52782
52987
|
|
|
52783
52988
|
// src/lsp-github-install.ts
|
|
52784
52989
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
52785
|
-
import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
52990
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
52786
52991
|
import {
|
|
52787
52992
|
copyFileSync as copyFileSync3,
|
|
52788
52993
|
createReadStream as createReadStream2,
|
|
52789
52994
|
createWriteStream as createWriteStream3,
|
|
52790
|
-
existsSync as
|
|
52995
|
+
existsSync as existsSync12,
|
|
52791
52996
|
lstatSync as lstatSync2,
|
|
52792
|
-
mkdirSync as
|
|
52997
|
+
mkdirSync as mkdirSync10,
|
|
52793
52998
|
readdirSync as readdirSync4,
|
|
52794
|
-
readFileSync as
|
|
52999
|
+
readFileSync as readFileSync11,
|
|
52795
53000
|
readlinkSync as readlinkSync2,
|
|
52796
53001
|
realpathSync as realpathSync3,
|
|
52797
|
-
renameSync as
|
|
52798
|
-
rmSync as
|
|
53002
|
+
renameSync as renameSync7,
|
|
53003
|
+
rmSync as rmSync6,
|
|
52799
53004
|
statSync as statSync6,
|
|
52800
53005
|
unlinkSync as unlinkSync6,
|
|
52801
53006
|
writeFileSync as writeFileSync8
|
|
52802
53007
|
} from "node:fs";
|
|
52803
|
-
import { dirname as
|
|
53008
|
+
import { dirname as dirname7, join as join16, relative as relative2, resolve as resolve3 } from "node:path";
|
|
52804
53009
|
import { Readable as Readable3 } from "node:stream";
|
|
52805
53010
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
52806
53011
|
|
|
@@ -52925,7 +53130,7 @@ function readGithubInstalledMetaIn(installDir) {
|
|
|
52925
53130
|
const path2 = join16(installDir, INSTALLED_META_FILE2);
|
|
52926
53131
|
if (!statSync6(path2).isFile())
|
|
52927
53132
|
return null;
|
|
52928
|
-
const parsed = JSON.parse(
|
|
53133
|
+
const parsed = JSON.parse(readFileSync11(path2, "utf8"));
|
|
52929
53134
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
52930
53135
|
return null;
|
|
52931
53136
|
return {
|
|
@@ -52941,7 +53146,7 @@ function readGithubInstalledMetaIn(installDir) {
|
|
|
52941
53146
|
}
|
|
52942
53147
|
function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveSha256) {
|
|
52943
53148
|
try {
|
|
52944
|
-
|
|
53149
|
+
mkdirSync10(installDir, { recursive: true });
|
|
52945
53150
|
const meta3 = {
|
|
52946
53151
|
version: version2,
|
|
52947
53152
|
installedAt: new Date().toISOString(),
|
|
@@ -52966,7 +53171,7 @@ function sha256OfFile(path2) {
|
|
|
52966
53171
|
});
|
|
52967
53172
|
}
|
|
52968
53173
|
function sha256OfFileSync2(path2) {
|
|
52969
|
-
return createHash6("sha256").update(
|
|
53174
|
+
return createHash6("sha256").update(readFileSync11(path2)).digest("hex");
|
|
52970
53175
|
}
|
|
52971
53176
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
|
|
52972
53177
|
const candidates = [];
|
|
@@ -53142,7 +53347,7 @@ async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
|
|
|
53142
53347
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
|
|
53143
53348
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
|
|
53144
53349
|
}
|
|
53145
|
-
|
|
53350
|
+
mkdirSync10(dirname7(destPath), { recursive: true });
|
|
53146
53351
|
let bytesWritten = 0;
|
|
53147
53352
|
const guard = new TransformStream({
|
|
53148
53353
|
transform(chunk, controller) {
|
|
@@ -53263,23 +53468,23 @@ function precheckArchiveContents(archivePath, archiveType) {
|
|
|
53263
53468
|
}
|
|
53264
53469
|
}
|
|
53265
53470
|
function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
53266
|
-
const suffix =
|
|
53471
|
+
const suffix = randomBytes2(8).toString("hex");
|
|
53267
53472
|
const stagingDir = `${destDir}.staging-${suffix}`;
|
|
53268
53473
|
try {
|
|
53269
|
-
|
|
53474
|
+
rmSync6(stagingDir, { recursive: true, force: true });
|
|
53270
53475
|
} catch {}
|
|
53271
|
-
|
|
53476
|
+
mkdirSync10(stagingDir, { recursive: true });
|
|
53272
53477
|
try {
|
|
53273
53478
|
precheckArchiveContents(archivePath, archiveType);
|
|
53274
53479
|
runPlatformExtractor(archivePath, stagingDir, archiveType);
|
|
53275
53480
|
validateExtraction(stagingDir);
|
|
53276
53481
|
try {
|
|
53277
|
-
|
|
53482
|
+
rmSync6(destDir, { recursive: true, force: true });
|
|
53278
53483
|
} catch {}
|
|
53279
|
-
|
|
53484
|
+
renameSync7(stagingDir, destDir);
|
|
53280
53485
|
} catch (err) {
|
|
53281
53486
|
try {
|
|
53282
|
-
|
|
53487
|
+
rmSync6(stagingDir, { recursive: true, force: true });
|
|
53283
53488
|
} catch {}
|
|
53284
53489
|
throw err;
|
|
53285
53490
|
}
|
|
@@ -53289,9 +53494,9 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
53289
53494
|
const dest = join16(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
|
|
53290
53495
|
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
53291
53496
|
try {
|
|
53292
|
-
|
|
53293
|
-
|
|
53294
|
-
|
|
53497
|
+
mkdirSync10(dirname7(dest), { recursive: true });
|
|
53498
|
+
rmSync6(dest, { recursive: true, force: true });
|
|
53499
|
+
renameSync7(packageDir, dest);
|
|
53295
53500
|
} catch (err) {
|
|
53296
53501
|
warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
53297
53502
|
}
|
|
@@ -53418,12 +53623,12 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl2
|
|
|
53418
53623
|
} catch {}
|
|
53419
53624
|
}
|
|
53420
53625
|
const innerBinaryPath = join16(extractDir, spec.binaryPathInArchive(platform2, arch, version2));
|
|
53421
|
-
if (!
|
|
53626
|
+
if (!existsSync12(innerBinaryPath)) {
|
|
53422
53627
|
error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
|
|
53423
53628
|
return null;
|
|
53424
53629
|
}
|
|
53425
53630
|
const targetBinary = ghBinaryPath(spec, platform2);
|
|
53426
|
-
|
|
53631
|
+
mkdirSync10(dirname7(targetBinary), { recursive: true });
|
|
53427
53632
|
try {
|
|
53428
53633
|
copyFileSync3(innerBinaryPath, targetBinary);
|
|
53429
53634
|
if (platform2 !== "win32") {
|
|
@@ -53502,7 +53707,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl2 = fetch) {
|
|
|
53502
53707
|
if (!host) {
|
|
53503
53708
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
53504
53709
|
try {
|
|
53505
|
-
if (
|
|
53710
|
+
if (existsSync12(ghBinDir(spec))) {
|
|
53506
53711
|
cachedBinDirs.push(ghBinDir(spec));
|
|
53507
53712
|
}
|
|
53508
53713
|
} catch {}
|
|
@@ -53665,9 +53870,9 @@ function normalizeToolMap(tools) {
|
|
|
53665
53870
|
}
|
|
53666
53871
|
|
|
53667
53872
|
// src/notifications.ts
|
|
53668
|
-
import { existsSync as
|
|
53873
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
|
|
53669
53874
|
import { homedir as homedir9, platform as platform2 } from "node:os";
|
|
53670
|
-
import { join as join17 } from "node:path";
|
|
53875
|
+
import { dirname as dirname8, join as join17 } from "node:path";
|
|
53671
53876
|
function isTuiMode() {
|
|
53672
53877
|
return process.env.OPENCODE_CLIENT === "cli";
|
|
53673
53878
|
}
|
|
@@ -53702,13 +53907,13 @@ function getDesktopStatePath() {
|
|
|
53702
53907
|
}
|
|
53703
53908
|
return null;
|
|
53704
53909
|
}
|
|
53705
|
-
function readDesktopState(
|
|
53910
|
+
function readDesktopState() {
|
|
53706
53911
|
const statePath = getDesktopStatePath();
|
|
53707
|
-
if (!statePath || !
|
|
53708
|
-
return {
|
|
53912
|
+
if (!statePath || !existsSync13(statePath)) {
|
|
53913
|
+
return { serverUrl: null };
|
|
53709
53914
|
}
|
|
53710
53915
|
try {
|
|
53711
|
-
const raw =
|
|
53916
|
+
const raw = readFileSync12(statePath, "utf-8");
|
|
53712
53917
|
const state = JSON.parse(raw);
|
|
53713
53918
|
let serverUrl = null;
|
|
53714
53919
|
const serverStr = state.server;
|
|
@@ -53720,19 +53925,43 @@ function readDesktopState(directory) {
|
|
|
53720
53925
|
}
|
|
53721
53926
|
} catch {}
|
|
53722
53927
|
}
|
|
53723
|
-
|
|
53724
|
-
const layoutPage = state["layout.page"];
|
|
53725
|
-
if (typeof layoutPage === "string") {
|
|
53726
|
-
const parsed = JSON.parse(layoutPage);
|
|
53727
|
-
const lastProjectSession = parsed.lastProjectSession;
|
|
53728
|
-
if (lastProjectSession) {
|
|
53729
|
-
const entry = lastProjectSession[directory];
|
|
53730
|
-
sessionId = entry?.id ?? null;
|
|
53731
|
-
}
|
|
53732
|
-
}
|
|
53733
|
-
return { sessionId, serverUrl };
|
|
53928
|
+
return { serverUrl };
|
|
53734
53929
|
} catch {
|
|
53735
|
-
return {
|
|
53930
|
+
return { serverUrl: null };
|
|
53931
|
+
}
|
|
53932
|
+
}
|
|
53933
|
+
var MAX_PENDING_DESKTOP_NOTIFICATIONS = 20;
|
|
53934
|
+
var pendingDesktopNotifications = new Map;
|
|
53935
|
+
function getExplicitSessionId(opts) {
|
|
53936
|
+
const sessionId = opts.sessionId?.trim();
|
|
53937
|
+
return sessionId ? sessionId : null;
|
|
53938
|
+
}
|
|
53939
|
+
function enqueuePendingDesktopNotification(directory, notification) {
|
|
53940
|
+
if (!directory)
|
|
53941
|
+
return;
|
|
53942
|
+
const pending = pendingDesktopNotifications.get(directory) ?? [];
|
|
53943
|
+
if (pending.some((item) => item.key === notification.key))
|
|
53944
|
+
return;
|
|
53945
|
+
pending.push(notification);
|
|
53946
|
+
if (pending.length > MAX_PENDING_DESKTOP_NOTIFICATIONS) {
|
|
53947
|
+
pending.splice(0, pending.length - MAX_PENDING_DESKTOP_NOTIFICATIONS);
|
|
53948
|
+
}
|
|
53949
|
+
pendingDesktopNotifications.set(directory, pending);
|
|
53950
|
+
}
|
|
53951
|
+
async function flushPendingDesktopNotifications(opts, sessionId) {
|
|
53952
|
+
const pending = pendingDesktopNotifications.get(opts.directory);
|
|
53953
|
+
if (!pending?.length)
|
|
53954
|
+
return;
|
|
53955
|
+
pendingDesktopNotifications.delete(opts.directory);
|
|
53956
|
+
for (const notification of pending) {
|
|
53957
|
+
if (notification.shouldSkip?.())
|
|
53958
|
+
continue;
|
|
53959
|
+
const delivered = await sendIgnoredMessage(opts.client, sessionId, notification.text);
|
|
53960
|
+
if (delivered) {
|
|
53961
|
+
notification.onDelivered?.();
|
|
53962
|
+
} else {
|
|
53963
|
+
enqueuePendingDesktopNotification(opts.directory, notification);
|
|
53964
|
+
}
|
|
53736
53965
|
}
|
|
53737
53966
|
}
|
|
53738
53967
|
function getServerAuth() {
|
|
@@ -53797,52 +54026,72 @@ async function sendWarning(opts, message) {
|
|
|
53797
54026
|
const toastSent = await showTuiToast(opts.client, "AFT Warning", message, "warning", 1e4);
|
|
53798
54027
|
if (toastSent)
|
|
53799
54028
|
return;
|
|
53800
|
-
const { sessionId } = readDesktopState(opts.directory);
|
|
53801
|
-
if (!sessionId)
|
|
53802
|
-
return;
|
|
53803
54029
|
const text = `${WARNING_MARKER} ${message}`;
|
|
54030
|
+
const sessionId = getExplicitSessionId(opts);
|
|
54031
|
+
if (!sessionId) {
|
|
54032
|
+
enqueuePendingDesktopNotification(opts.directory, { key: `warning:${message}`, text });
|
|
54033
|
+
return;
|
|
54034
|
+
}
|
|
54035
|
+
await flushPendingDesktopNotifications(opts, sessionId);
|
|
53804
54036
|
sessionLog(sessionId, `[aft-plugin] sending warning to session ${sessionId}`);
|
|
53805
54037
|
await sendIgnoredMessage(opts.client, sessionId, text);
|
|
53806
54038
|
}
|
|
53807
54039
|
async function sendFeatureAnnouncement(opts, version2, features, footer, storageDir) {
|
|
53808
|
-
if (storageDir)
|
|
53809
|
-
|
|
53810
|
-
try {
|
|
53811
|
-
if (existsSync12(versionFile)) {
|
|
53812
|
-
const lastVersion = readFileSync11(versionFile, "utf-8").trim();
|
|
53813
|
-
if (lastVersion === version2)
|
|
53814
|
-
return;
|
|
53815
|
-
}
|
|
53816
|
-
} catch {}
|
|
53817
|
-
}
|
|
54040
|
+
if (hasAnnouncedVersion(storageDir, version2))
|
|
54041
|
+
return;
|
|
53818
54042
|
const hasFooter = typeof footer === "string" && footer.trim().length > 0;
|
|
53819
54043
|
const featureText = hasFooter ? [features.map((f) => `• ${f}`).join(`
|
|
53820
54044
|
`), "", footer].join(`
|
|
53821
54045
|
`) : features.map((f) => `• ${f}`).join(`
|
|
53822
54046
|
`);
|
|
53823
54047
|
const toastSent = await showTuiToast(opts.client, `AFT v${version2}`, featureText, "info", 12000);
|
|
53824
|
-
if (
|
|
53825
|
-
|
|
53826
|
-
|
|
53827
|
-
|
|
53828
|
-
|
|
53829
|
-
|
|
53830
|
-
|
|
53831
|
-
|
|
53832
|
-
if (hasFooter)
|
|
53833
|
-
sections.push("", footer);
|
|
53834
|
-
const text = sections.join(`
|
|
54048
|
+
if (toastSent) {
|
|
54049
|
+
persistAnnouncedVersion(storageDir, version2);
|
|
54050
|
+
return;
|
|
54051
|
+
}
|
|
54052
|
+
const sections = [`${FEATURE_MARKER} v${version2}:`, ...features.map((f) => ` • ${f}`)];
|
|
54053
|
+
if (hasFooter)
|
|
54054
|
+
sections.push("", footer);
|
|
54055
|
+
const text = sections.join(`
|
|
53835
54056
|
`);
|
|
53836
|
-
|
|
53837
|
-
|
|
54057
|
+
const pending = {
|
|
54058
|
+
key: `feature:${version2}`,
|
|
54059
|
+
text,
|
|
54060
|
+
shouldSkip: () => hasAnnouncedVersion(storageDir, version2),
|
|
54061
|
+
onDelivered: () => persistAnnouncedVersion(storageDir, version2)
|
|
54062
|
+
};
|
|
54063
|
+
const sessionId = getExplicitSessionId(opts);
|
|
54064
|
+
if (!sessionId) {
|
|
54065
|
+
enqueuePendingDesktopNotification(opts.directory, pending);
|
|
54066
|
+
return;
|
|
53838
54067
|
}
|
|
53839
|
-
|
|
53840
|
-
|
|
53841
|
-
|
|
53842
|
-
|
|
53843
|
-
|
|
54068
|
+
await flushPendingDesktopNotifications(opts, sessionId);
|
|
54069
|
+
if (hasAnnouncedVersion(storageDir, version2))
|
|
54070
|
+
return;
|
|
54071
|
+
sessionLog(sessionId, `[aft-plugin] sending feature announcement for v${version2}`);
|
|
54072
|
+
if (await sendIgnoredMessage(opts.client, sessionId, text)) {
|
|
54073
|
+
persistAnnouncedVersion(storageDir, version2);
|
|
53844
54074
|
}
|
|
53845
54075
|
}
|
|
54076
|
+
function hasAnnouncedVersion(storageDir, version2) {
|
|
54077
|
+
if (!storageDir)
|
|
54078
|
+
return false;
|
|
54079
|
+
const versionFile = repairRootScopedStorageFile(storageDir, "opencode", "last_announced_version");
|
|
54080
|
+
try {
|
|
54081
|
+
return existsSync13(versionFile) && readFileSync12(versionFile, "utf-8").trim() === version2;
|
|
54082
|
+
} catch {
|
|
54083
|
+
return false;
|
|
54084
|
+
}
|
|
54085
|
+
}
|
|
54086
|
+
function persistAnnouncedVersion(storageDir, version2) {
|
|
54087
|
+
if (!storageDir)
|
|
54088
|
+
return;
|
|
54089
|
+
try {
|
|
54090
|
+
const versionFile = repairRootScopedStorageFile(storageDir, "opencode", "last_announced_version");
|
|
54091
|
+
mkdirSync11(dirname8(versionFile), { recursive: true });
|
|
54092
|
+
writeFileSync9(versionFile, version2);
|
|
54093
|
+
} catch {}
|
|
54094
|
+
}
|
|
53846
54095
|
async function readWarnedTools(bridge) {
|
|
53847
54096
|
try {
|
|
53848
54097
|
const resp = await bridge.send("db_get_state", { key: "warned_tools" });
|
|
@@ -53909,6 +54158,9 @@ function formatConfigureWarning(warning) {
|
|
|
53909
54158
|
${warning.hint}`;
|
|
53910
54159
|
}
|
|
53911
54160
|
async function deliverConfigureWarnings(opts, warnings) {
|
|
54161
|
+
if (opts.projectRoot) {
|
|
54162
|
+
await flushPendingDesktopNotifications({ client: opts.client, directory: opts.projectRoot }, opts.sessionId);
|
|
54163
|
+
}
|
|
53912
54164
|
if (warnings.length === 0)
|
|
53913
54165
|
return;
|
|
53914
54166
|
for (const warning of warnings) {
|
|
@@ -53924,10 +54176,10 @@ async function deliverConfigureWarnings(opts, warnings) {
|
|
|
53924
54176
|
async function cleanupWarnings(opts) {
|
|
53925
54177
|
if (isTuiMode())
|
|
53926
54178
|
return;
|
|
53927
|
-
const
|
|
54179
|
+
const sessionId = getExplicitSessionId(opts);
|
|
53928
54180
|
if (!sessionId)
|
|
53929
54181
|
return;
|
|
53930
|
-
const effectiveServerUrl = opts.serverUrl ||
|
|
54182
|
+
const effectiveServerUrl = opts.serverUrl || readDesktopState().serverUrl;
|
|
53931
54183
|
if (!effectiveServerUrl)
|
|
53932
54184
|
return;
|
|
53933
54185
|
const messages = await getSessionMessages(opts.client, sessionId);
|
|
@@ -54061,10 +54313,10 @@ function writeTerminal(terminal, data) {
|
|
|
54061
54313
|
}
|
|
54062
54314
|
|
|
54063
54315
|
// src/shared/rpc-server.ts
|
|
54064
|
-
import { randomBytes as
|
|
54065
|
-
import { mkdirSync as
|
|
54316
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
54317
|
+
import { mkdirSync as mkdirSync12, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "node:fs";
|
|
54066
54318
|
import { createServer } from "node:http";
|
|
54067
|
-
import { dirname as
|
|
54319
|
+
import { dirname as dirname9, join as join19 } from "node:path";
|
|
54068
54320
|
|
|
54069
54321
|
// src/shared/rpc-utils.ts
|
|
54070
54322
|
import { createHash as createHash7 } from "node:crypto";
|
|
@@ -54089,7 +54341,7 @@ class AftRpcServer {
|
|
|
54089
54341
|
instanceId;
|
|
54090
54342
|
constructor(storageDir, directory) {
|
|
54091
54343
|
this.portsDir = rpcPortFileDir(storageDir, directory);
|
|
54092
|
-
this.instanceId =
|
|
54344
|
+
this.instanceId = randomBytes3(8).toString("hex");
|
|
54093
54345
|
this.portFilePath = join19(this.portsDir, `${this.instanceId}.json`);
|
|
54094
54346
|
}
|
|
54095
54347
|
handle(method, handler) {
|
|
@@ -54109,17 +54361,17 @@ class AftRpcServer {
|
|
|
54109
54361
|
return;
|
|
54110
54362
|
}
|
|
54111
54363
|
this.port = addr.port;
|
|
54112
|
-
this.token =
|
|
54364
|
+
this.token = randomBytes3(32).toString("hex");
|
|
54113
54365
|
this.server = server;
|
|
54114
54366
|
try {
|
|
54115
|
-
const dir =
|
|
54116
|
-
|
|
54367
|
+
const dir = dirname9(this.portFilePath);
|
|
54368
|
+
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
54117
54369
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
54118
54370
|
writeFileSync10(tmpPath, JSON.stringify({ port: this.port, token: this.token }), {
|
|
54119
54371
|
encoding: "utf-8",
|
|
54120
54372
|
mode: 384
|
|
54121
54373
|
});
|
|
54122
|
-
|
|
54374
|
+
renameSync8(tmpPath, this.portFilePath);
|
|
54123
54375
|
log2(`RPC server listening on 127.0.0.1:${this.port}`);
|
|
54124
54376
|
} catch (err) {
|
|
54125
54377
|
warn2(`Failed to write RPC port file: ${err}`);
|
|
@@ -54428,8 +54680,8 @@ function formatStatusMarkdown(status) {
|
|
|
54428
54680
|
|
|
54429
54681
|
// src/shared/tui-config.ts
|
|
54430
54682
|
var import_comment_json4 = __toESM(require_src2(), 1);
|
|
54431
|
-
import { existsSync as
|
|
54432
|
-
import { dirname as
|
|
54683
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
|
|
54684
|
+
import { dirname as dirname10, join as join21 } from "node:path";
|
|
54433
54685
|
|
|
54434
54686
|
// src/shared/opencode-config-dir.ts
|
|
54435
54687
|
import { homedir as homedir10 } from "node:os";
|
|
@@ -54465,9 +54717,9 @@ function resolveTuiConfigPath() {
|
|
|
54465
54717
|
const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
|
|
54466
54718
|
const jsoncPath = join21(configDir, "tui.jsonc");
|
|
54467
54719
|
const jsonPath = join21(configDir, "tui.json");
|
|
54468
|
-
if (
|
|
54720
|
+
if (existsSync14(jsoncPath))
|
|
54469
54721
|
return jsoncPath;
|
|
54470
|
-
if (
|
|
54722
|
+
if (existsSync14(jsonPath))
|
|
54471
54723
|
return jsonPath;
|
|
54472
54724
|
return jsonPath;
|
|
54473
54725
|
}
|
|
@@ -54475,8 +54727,8 @@ function ensureTuiPluginEntry() {
|
|
|
54475
54727
|
try {
|
|
54476
54728
|
const configPath = resolveTuiConfigPath();
|
|
54477
54729
|
let config2 = {};
|
|
54478
|
-
if (
|
|
54479
|
-
config2 = import_comment_json4.parse(
|
|
54730
|
+
if (existsSync14(configPath)) {
|
|
54731
|
+
config2 = import_comment_json4.parse(readFileSync13(configPath, "utf-8")) ?? {};
|
|
54480
54732
|
}
|
|
54481
54733
|
const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((value) => typeof value === "string") : [];
|
|
54482
54734
|
if (plugins.some((plugin) => plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`) || plugin.includes("opencode-plugin") || plugin.includes("aft-opencode"))) {
|
|
@@ -54484,7 +54736,7 @@ function ensureTuiPluginEntry() {
|
|
|
54484
54736
|
}
|
|
54485
54737
|
plugins.push(PLUGIN_ENTRY);
|
|
54486
54738
|
config2.plugin = plugins;
|
|
54487
|
-
|
|
54739
|
+
mkdirSync13(dirname10(configPath), { recursive: true });
|
|
54488
54740
|
writeFileSync11(configPath, `${import_comment_json4.stringify(config2, null, 2)}
|
|
54489
54741
|
`);
|
|
54490
54742
|
log2(`[aft-plugin] added TUI plugin entry to ${configPath}`);
|
|
@@ -55538,6 +55790,10 @@ function createBashTool(ctx) {
|
|
|
55538
55790
|
return rendered2;
|
|
55539
55791
|
}
|
|
55540
55792
|
if (Date.now() - startedAt >= waitTimeoutMs) {
|
|
55793
|
+
if (subagentForcedForeground) {
|
|
55794
|
+
await sleep(FOREGROUND_POLL_INTERVAL_MS);
|
|
55795
|
+
continue;
|
|
55796
|
+
}
|
|
55541
55797
|
const promoted = await callBridge(ctx, context, "bash_promote", { task_id: taskId });
|
|
55542
55798
|
if (promoted.success === false) {
|
|
55543
55799
|
throw new Error(promoted.message ?? "bash_promote failed");
|
|
@@ -55770,12 +56026,13 @@ var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
|
55770
56026
|
var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
|
|
55771
56027
|
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 300000;
|
|
55772
56028
|
var BASH_TRANSPORT_TIMEOUT_MS2 = 30000;
|
|
56029
|
+
var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
|
|
55773
56030
|
function createBashWatchTool(ctx) {
|
|
55774
56031
|
return {
|
|
55775
56032
|
description: "Block on a background bash task until a pattern matches, it exits, or timeout elapses; or register an async pattern notification with background:true.",
|
|
55776
56033
|
args: {
|
|
55777
56034
|
taskId: z5.string().describe("Background task ID returned by bash({ background: true })."),
|
|
55778
|
-
pattern: z5.union([z5.string(), z5.object({ regex: z5.string() })]).optional().describe("Substring or regex pattern. Optional in sync mode; required with background:true."),
|
|
56035
|
+
pattern: z5.union([z5.string(), z5.object({ regex: z5.string() })]).optional().describe("Substring or regex pattern. Optional in sync mode; required with background:true. Sync substring watches keep only the overlap tail needed for boundary matches; sync regex watches use a 64 KB rolling output window."),
|
|
55779
56036
|
background: z5.boolean().optional().describe("When true, register an async watch and return immediately. Defaults to false (sync wait)."),
|
|
55780
56037
|
timeoutMs: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS).describe("Sync-only timeout in milliseconds. Default 30000, max 300000."),
|
|
55781
56038
|
once: z5.boolean().optional().describe("Async-only. Defaults true; false keeps the watch sticky until task exit.")
|
|
@@ -55867,7 +56124,7 @@ async function bashStatusSnapshot2(ctx, runtime, taskId, outputMode, options) {
|
|
|
55867
56124
|
async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effectiveWaitMs) {
|
|
55868
56125
|
const startedAt = Date.now();
|
|
55869
56126
|
const deadline = startedAt + effectiveWaitMs;
|
|
55870
|
-
let spillCursor = 0;
|
|
56127
|
+
let spillCursor = { output: 0, stderr: 0, combined: 0 };
|
|
55871
56128
|
let scanText = "";
|
|
55872
56129
|
let scanBaseOffset = 0;
|
|
55873
56130
|
const bridgeOptions = {
|
|
@@ -55875,14 +56132,11 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
|
|
|
55875
56132
|
transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS2
|
|
55876
56133
|
};
|
|
55877
56134
|
markTaskWaiting(runtime.sessionID, taskId);
|
|
56135
|
+
let sawTerminal = false;
|
|
55878
56136
|
try {
|
|
55879
56137
|
while (true) {
|
|
55880
56138
|
const data = await bashStatusSnapshot2(ctx, runtime, taskId, outputMode, bridgeOptions);
|
|
55881
|
-
|
|
55882
|
-
consumeBgCompletion(runtime.sessionID, taskId);
|
|
55883
|
-
await markBgCompletionDelivered({ ctx, directory: projectRootFor(runtime), sessionID: runtime.sessionID }, taskId);
|
|
55884
|
-
return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
|
|
55885
|
-
}
|
|
56139
|
+
const terminal = isTerminalStatus2(data.status);
|
|
55886
56140
|
if (waitFor) {
|
|
55887
56141
|
const scan = await readNewTaskOutput(runtime, taskId, data, spillCursor);
|
|
55888
56142
|
if (scan) {
|
|
@@ -55892,7 +56146,11 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
|
|
|
55892
56146
|
scanText += scan.text;
|
|
55893
56147
|
const match = findWaitMatch(scanText, waitFor);
|
|
55894
56148
|
if (match) {
|
|
55895
|
-
|
|
56149
|
+
if (terminal) {
|
|
56150
|
+
sawTerminal = true;
|
|
56151
|
+
consumeBgCompletion(runtime.sessionID, taskId);
|
|
56152
|
+
await markBgCompletionDelivered({ ctx, directory: projectRootFor(runtime), sessionID: runtime.sessionID }, taskId);
|
|
56153
|
+
}
|
|
55896
56154
|
return withWaited(data, {
|
|
55897
56155
|
reason: "matched",
|
|
55898
56156
|
elapsed_ms: Date.now() - startedAt,
|
|
@@ -55900,31 +56158,61 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
|
|
|
55900
56158
|
match_offset: scanBaseOffset + Buffer.byteLength(scanText.slice(0, match.index), "utf8")
|
|
55901
56159
|
});
|
|
55902
56160
|
}
|
|
56161
|
+
const trimmed = trimWaitScanBuffer(scanText, scanBaseOffset, waitFor);
|
|
56162
|
+
scanText = trimmed.text;
|
|
56163
|
+
scanBaseOffset = trimmed.baseOffset;
|
|
55903
56164
|
}
|
|
55904
56165
|
}
|
|
56166
|
+
if (terminal) {
|
|
56167
|
+
sawTerminal = true;
|
|
56168
|
+
consumeBgCompletion(runtime.sessionID, taskId);
|
|
56169
|
+
await markBgCompletionDelivered({ ctx, directory: projectRootFor(runtime), sessionID: runtime.sessionID }, taskId);
|
|
56170
|
+
return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
|
|
56171
|
+
}
|
|
55905
56172
|
if (Date.now() >= deadline) {
|
|
55906
|
-
unmarkTaskWaiting(runtime.sessionID, taskId);
|
|
55907
56173
|
return withWaited(data, { reason: "timeout", elapsed_ms: Date.now() - startedAt });
|
|
55908
56174
|
}
|
|
55909
56175
|
await sleep2(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
|
|
55910
56176
|
}
|
|
55911
|
-
}
|
|
55912
|
-
|
|
55913
|
-
|
|
56177
|
+
} finally {
|
|
56178
|
+
if (!sawTerminal)
|
|
56179
|
+
unmarkTaskWaiting(runtime.sessionID, taskId);
|
|
56180
|
+
await disposePtyTerminal(watchPtyCacheKey(runtime, taskId));
|
|
55914
56181
|
}
|
|
55915
56182
|
}
|
|
55916
56183
|
async function readNewTaskOutput(runtime, taskId, data, cursor) {
|
|
55917
56184
|
const outputPath = data.output_path;
|
|
55918
|
-
if (!outputPath)
|
|
55919
|
-
return;
|
|
55920
56185
|
if (data.mode === "pty") {
|
|
55921
|
-
|
|
56186
|
+
if (!outputPath)
|
|
56187
|
+
return;
|
|
56188
|
+
const state = await getOrCreatePtyTerminal(watchPtyCacheKey(runtime, taskId), outputPath);
|
|
55922
56189
|
const baseOffset = state.offset;
|
|
55923
|
-
const
|
|
55924
|
-
|
|
56190
|
+
const bytes = await readPtyBytes(state);
|
|
56191
|
+
if (bytes.length === 0)
|
|
56192
|
+
return;
|
|
56193
|
+
return {
|
|
56194
|
+
text: bytes.toString("utf8"),
|
|
56195
|
+
baseOffset,
|
|
56196
|
+
nextCursor: { output: state.offset, stderr: 0, combined: state.offset }
|
|
56197
|
+
};
|
|
55925
56198
|
}
|
|
55926
|
-
const
|
|
55927
|
-
|
|
56199
|
+
const stderrPath = data.stderr_path;
|
|
56200
|
+
if (!outputPath && !stderrPath)
|
|
56201
|
+
return;
|
|
56202
|
+
const stdoutBytes = outputPath ? await readFileBytesFrom(outputPath, cursor.output) : Buffer.alloc(0);
|
|
56203
|
+
const stderrBytes = stderrPath ? await readFileBytesFrom(stderrPath, cursor.stderr) : Buffer.alloc(0);
|
|
56204
|
+
const bytesRead = stdoutBytes.length + stderrBytes.length;
|
|
56205
|
+
if (bytesRead === 0)
|
|
56206
|
+
return;
|
|
56207
|
+
return {
|
|
56208
|
+
text: Buffer.concat([stdoutBytes, stderrBytes]).toString("utf8"),
|
|
56209
|
+
baseOffset: cursor.combined,
|
|
56210
|
+
nextCursor: {
|
|
56211
|
+
output: cursor.output + stdoutBytes.length,
|
|
56212
|
+
stderr: cursor.stderr + stderrBytes.length,
|
|
56213
|
+
combined: cursor.combined + bytesRead
|
|
56214
|
+
}
|
|
56215
|
+
};
|
|
55928
56216
|
}
|
|
55929
56217
|
async function readFileBytesFrom(outputPath, cursor) {
|
|
55930
56218
|
const handle = await fs5.open(outputPath, "r");
|
|
@@ -55965,6 +56253,34 @@ function findWaitMatch(text, pattern) {
|
|
|
55965
56253
|
const match = pattern.value.exec(text);
|
|
55966
56254
|
return match ? { text: match[0], index: match.index } : undefined;
|
|
55967
56255
|
}
|
|
56256
|
+
function trimWaitScanBuffer(text, baseOffset, pattern) {
|
|
56257
|
+
const keepFrom = pattern.kind === "substring" ? substringKeepStart(text, pattern.value) : regexKeepStart(text, REGEX_WAIT_SCAN_WINDOW_BYTES);
|
|
56258
|
+
if (keepFrom <= 0)
|
|
56259
|
+
return { text, baseOffset };
|
|
56260
|
+
return {
|
|
56261
|
+
text: text.slice(keepFrom),
|
|
56262
|
+
baseOffset: baseOffset + Buffer.byteLength(text.slice(0, keepFrom), "utf8")
|
|
56263
|
+
};
|
|
56264
|
+
}
|
|
56265
|
+
function substringKeepStart(text, pattern) {
|
|
56266
|
+
const keepChars = Math.max(0, pattern.length - 1);
|
|
56267
|
+
return text.length > keepChars ? text.length - keepChars : 0;
|
|
56268
|
+
}
|
|
56269
|
+
function regexKeepStart(text, maxBytes) {
|
|
56270
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes)
|
|
56271
|
+
return 0;
|
|
56272
|
+
let low = 0;
|
|
56273
|
+
let high = text.length;
|
|
56274
|
+
while (low < high) {
|
|
56275
|
+
const mid = Math.floor((low + high) / 2);
|
|
56276
|
+
if (Buffer.byteLength(text.slice(mid), "utf8") > maxBytes) {
|
|
56277
|
+
low = mid + 1;
|
|
56278
|
+
} else {
|
|
56279
|
+
high = mid;
|
|
56280
|
+
}
|
|
56281
|
+
}
|
|
56282
|
+
return low;
|
|
56283
|
+
}
|
|
55968
56284
|
function withWaited(data, waited) {
|
|
55969
56285
|
return { ...data, waited };
|
|
55970
56286
|
}
|
|
@@ -55974,6 +56290,9 @@ function isTerminalStatus2(status) {
|
|
|
55974
56290
|
function ptyCacheKey2(runtime, taskId) {
|
|
55975
56291
|
return `${projectRootFor(runtime)}::${runtime.sessionID ?? "__default__"}::${taskId}`;
|
|
55976
56292
|
}
|
|
56293
|
+
function watchPtyCacheKey(runtime, taskId) {
|
|
56294
|
+
return `${ptyCacheKey2(runtime, taskId)}::watch`;
|
|
56295
|
+
}
|
|
55977
56296
|
function sleep2(ms) {
|
|
55978
56297
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
55979
56298
|
}
|
|
@@ -57816,6 +58135,39 @@ function normalizeGlob(pattern) {
|
|
|
57816
58135
|
}
|
|
57817
58136
|
return pattern;
|
|
57818
58137
|
}
|
|
58138
|
+
function absoluteSearchPath(context, target) {
|
|
58139
|
+
return path6.isAbsolute(target) ? target : path6.resolve(context.directory, target);
|
|
58140
|
+
}
|
|
58141
|
+
function searchPathExists(context, target) {
|
|
58142
|
+
return fs7.existsSync(absoluteSearchPath(context, target));
|
|
58143
|
+
}
|
|
58144
|
+
function splitSearchPathArg(context, raw) {
|
|
58145
|
+
if (searchPathExists(context, raw) || !/\s/.test(raw)) {
|
|
58146
|
+
return [raw];
|
|
58147
|
+
}
|
|
58148
|
+
const fragments = raw.trim().split(/\s+/).filter(Boolean);
|
|
58149
|
+
if (fragments.length < 2 || !fragments.every((fragment) => searchPathExists(context, fragment))) {
|
|
58150
|
+
return [raw];
|
|
58151
|
+
}
|
|
58152
|
+
return fragments;
|
|
58153
|
+
}
|
|
58154
|
+
function searchPathKind(context, target, defaultKind) {
|
|
58155
|
+
try {
|
|
58156
|
+
const stat = fs7.lstatSync(absoluteSearchPath(context, target));
|
|
58157
|
+
if (defaultKind === "file") {
|
|
58158
|
+
return stat.isDirectory() ? "directory" : "file";
|
|
58159
|
+
}
|
|
58160
|
+
return stat.isFile() ? "file" : "directory";
|
|
58161
|
+
} catch {
|
|
58162
|
+
return defaultKind;
|
|
58163
|
+
}
|
|
58164
|
+
}
|
|
58165
|
+
function searchPathTargets(context, raw, defaultKind) {
|
|
58166
|
+
return splitSearchPathArg(context, raw).map((target) => ({
|
|
58167
|
+
target,
|
|
58168
|
+
kind: searchPathKind(context, target, defaultKind)
|
|
58169
|
+
}));
|
|
58170
|
+
}
|
|
57819
58171
|
function splitIncludeArg(raw) {
|
|
57820
58172
|
const out = [];
|
|
57821
58173
|
let depth = 0;
|
|
@@ -57865,17 +58217,13 @@ function searchTools(ctx) {
|
|
|
57865
58217
|
if (grepDenied)
|
|
57866
58218
|
return permissionDeniedResponse(grepDenied);
|
|
57867
58219
|
if (pathArg) {
|
|
57868
|
-
|
|
57869
|
-
|
|
57870
|
-
|
|
57871
|
-
|
|
57872
|
-
|
|
57873
|
-
|
|
57874
|
-
|
|
57875
|
-
kind
|
|
57876
|
-
});
|
|
57877
|
-
if (externalDenied)
|
|
57878
|
-
return permissionDeniedResponse(externalDenied);
|
|
58220
|
+
for (const target of searchPathTargets(context, pathArg, "file")) {
|
|
58221
|
+
const externalDenied = await assertExternalDirectoryPermission(context, target.target, {
|
|
58222
|
+
kind: target.kind
|
|
58223
|
+
});
|
|
58224
|
+
if (externalDenied)
|
|
58225
|
+
return permissionDeniedResponse(externalDenied);
|
|
58226
|
+
}
|
|
57879
58227
|
}
|
|
57880
58228
|
const response = await callBridge(ctx, context, "grep", {
|
|
57881
58229
|
pattern,
|
|
@@ -57907,23 +58255,22 @@ function searchTools(ctx) {
|
|
|
57907
58255
|
globPath = globPattern.slice(0, lastSlash);
|
|
57908
58256
|
globPattern = `**/${globPattern.slice(lastSlash + 1)}`;
|
|
57909
58257
|
}
|
|
58258
|
+
} else if (metaIdx === -1) {
|
|
58259
|
+
globPath = path6.dirname(globPattern);
|
|
58260
|
+
globPattern = path6.basename(globPattern);
|
|
57910
58261
|
}
|
|
57911
58262
|
}
|
|
57912
58263
|
const globDenied = await askGlobPermission(context, globPattern, { path: globPath });
|
|
57913
58264
|
if (globDenied)
|
|
57914
58265
|
return permissionDeniedResponse(globDenied);
|
|
57915
58266
|
if (globPath) {
|
|
57916
|
-
|
|
57917
|
-
|
|
57918
|
-
|
|
57919
|
-
|
|
57920
|
-
|
|
57921
|
-
|
|
57922
|
-
|
|
57923
|
-
kind
|
|
57924
|
-
});
|
|
57925
|
-
if (externalDenied)
|
|
57926
|
-
return permissionDeniedResponse(externalDenied);
|
|
58267
|
+
for (const target of searchPathTargets(context, globPath, "directory")) {
|
|
58268
|
+
const externalDenied = await assertExternalDirectoryPermission(context, target.target, {
|
|
58269
|
+
kind: target.kind
|
|
58270
|
+
});
|
|
58271
|
+
if (externalDenied)
|
|
58272
|
+
return permissionDeniedResponse(externalDenied);
|
|
58273
|
+
}
|
|
57927
58274
|
}
|
|
57928
58275
|
const response = await callBridge(ctx, context, "glob", {
|
|
57929
58276
|
pattern: globPattern,
|
|
@@ -58507,10 +58854,10 @@ ${lines}
|
|
|
58507
58854
|
return { show: false };
|
|
58508
58855
|
}
|
|
58509
58856
|
if (storageDir) {
|
|
58510
|
-
const versionFile = join23(storageDir, "last_announced_version");
|
|
58511
58857
|
try {
|
|
58512
|
-
|
|
58513
|
-
|
|
58858
|
+
const versionFile = repairRootScopedStorageFile(storageDir, "opencode", "last_announced_version");
|
|
58859
|
+
if (existsSync17(versionFile)) {
|
|
58860
|
+
const lastVersion = readFileSync14(versionFile, "utf-8").trim();
|
|
58514
58861
|
if (lastVersion === ANNOUNCEMENT_VERSION)
|
|
58515
58862
|
return { show: false };
|
|
58516
58863
|
}
|
|
@@ -58526,8 +58873,9 @@ ${lines}
|
|
|
58526
58873
|
rpcServer.handle("mark-announced", async () => {
|
|
58527
58874
|
if (storageDir && ANNOUNCEMENT_VERSION) {
|
|
58528
58875
|
try {
|
|
58529
|
-
|
|
58530
|
-
|
|
58876
|
+
const versionFile = repairRootScopedStorageFile(storageDir, "opencode", "last_announced_version");
|
|
58877
|
+
mkdirSync14(dirname14(versionFile), { recursive: true });
|
|
58878
|
+
writeFileSync12(versionFile, ANNOUNCEMENT_VERSION);
|
|
58531
58879
|
} catch {}
|
|
58532
58880
|
}
|
|
58533
58881
|
return { success: true };
|