@cortexkit/aft-opencode 0.39.4 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +12 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/configure-warnings.d.ts +3 -1
- package/dist/configure-warnings.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1285 -1218
- package/dist/lsp-cache.d.ts +3 -3
- package/dist/lsp-github-install.d.ts +5 -5
- package/dist/lsp-github-probe.d.ts +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/patch-parser.d.ts.map +1 -1
- package/dist/shared/rpc-notifications.d.ts +45 -0
- package/dist/shared/rpc-notifications.d.ts.map +1 -0
- package/dist/shared/rpc-utils.d.ts +10 -3
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/subc-tool-schemas.d.ts +20 -0
- package/dist/subc-tool-schemas.d.ts.map +1 -0
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash_watch.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/refactoring.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/tui.js +1351 -81
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +11 -10
- package/src/shared/rpc-notifications.ts +121 -0
- package/src/shared/rpc-utils.ts +12 -7
- package/src/tui/index.tsx +118 -38
- package/src/tui/sidebar.tsx +12 -6
package/dist/index.js
CHANGED
|
@@ -11594,11 +11594,6 @@ function error(message, meta) {
|
|
|
11594
11594
|
}
|
|
11595
11595
|
}
|
|
11596
11596
|
// ../aft-bridge/dist/bash-format.js
|
|
11597
|
-
function appendPipeStripNote(output, note) {
|
|
11598
|
-
return note ? `${output}
|
|
11599
|
-
|
|
11600
|
-
${note}` : output;
|
|
11601
|
-
}
|
|
11602
11597
|
function formatSeconds(ms) {
|
|
11603
11598
|
return `${Number((ms / 1000).toFixed(1))}s`;
|
|
11604
11599
|
}
|
|
@@ -11676,12 +11671,16 @@ function maybeAppendGrepSearchHint(output, command, aftSearchRegistered, project
|
|
|
11676
11671
|
${hint}`;
|
|
11677
11672
|
}
|
|
11678
11673
|
function shouldSuppressGrepSearchHint(command, projectRoot) {
|
|
11679
|
-
const root = projectRoot?.trim();
|
|
11680
|
-
if (!root)
|
|
11681
|
-
return false;
|
|
11682
11674
|
const statements = splitTopLevelStatements(command);
|
|
11683
11675
|
if (statements === null)
|
|
11684
11676
|
return false;
|
|
11677
|
+
if (allSearchPathOperandsAreDynamic(statements))
|
|
11678
|
+
return true;
|
|
11679
|
+
const root = projectRoot?.trim();
|
|
11680
|
+
if (!root)
|
|
11681
|
+
return false;
|
|
11682
|
+
const resolvedRoot = path.resolve(root);
|
|
11683
|
+
let effectiveCwd = resolvedRoot;
|
|
11685
11684
|
let sawCodeSearchStatement = false;
|
|
11686
11685
|
for (const statement of statements) {
|
|
11687
11686
|
const firstStage = firstPipelineStage(statement);
|
|
@@ -11690,19 +11689,84 @@ function shouldSuppressGrepSearchHint(command, projectRoot) {
|
|
|
11690
11689
|
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
11691
11690
|
if (firstToken === null)
|
|
11692
11691
|
continue;
|
|
11693
|
-
|
|
11692
|
+
const head = firstToken.token;
|
|
11693
|
+
if (head === "cd") {
|
|
11694
|
+
effectiveCwd = nextCwdAfterCd(effectiveCwd, firstStage, firstToken.end);
|
|
11695
|
+
continue;
|
|
11696
|
+
}
|
|
11697
|
+
if (head !== "grep" && head !== "rg")
|
|
11694
11698
|
continue;
|
|
11695
11699
|
sawCodeSearchStatement = true;
|
|
11700
|
+
if (effectiveCwd === null)
|
|
11701
|
+
continue;
|
|
11702
|
+
if (!isDirInsideProject(resolvedRoot, effectiveCwd))
|
|
11703
|
+
continue;
|
|
11696
11704
|
const operands = collectPathOperands(firstStage, firstToken.end);
|
|
11697
11705
|
if (operands.length === 0)
|
|
11698
11706
|
return false;
|
|
11699
11707
|
for (const operand of operands) {
|
|
11700
|
-
if (
|
|
11708
|
+
if (isDynamicPathOperand(operand))
|
|
11709
|
+
continue;
|
|
11710
|
+
if (isPathInsideProject(resolvedRoot, effectiveCwd, operand))
|
|
11701
11711
|
return false;
|
|
11702
11712
|
}
|
|
11703
11713
|
}
|
|
11704
11714
|
return sawCodeSearchStatement;
|
|
11705
11715
|
}
|
|
11716
|
+
function allSearchPathOperandsAreDynamic(statements) {
|
|
11717
|
+
let sawDynamicOnlySearch = false;
|
|
11718
|
+
for (const statement of statements) {
|
|
11719
|
+
const firstStage = firstPipelineStage(statement);
|
|
11720
|
+
if (firstStage === null)
|
|
11721
|
+
continue;
|
|
11722
|
+
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
11723
|
+
if (firstToken === null)
|
|
11724
|
+
continue;
|
|
11725
|
+
const head = firstToken.token;
|
|
11726
|
+
if (head !== "grep" && head !== "rg")
|
|
11727
|
+
continue;
|
|
11728
|
+
const operands = collectPathOperands(firstStage, firstToken.end);
|
|
11729
|
+
if (operands.length === 0)
|
|
11730
|
+
return false;
|
|
11731
|
+
if (operands.some((operand) => !isDynamicPathOperand(operand)))
|
|
11732
|
+
return false;
|
|
11733
|
+
sawDynamicOnlySearch = true;
|
|
11734
|
+
}
|
|
11735
|
+
return sawDynamicOnlySearch;
|
|
11736
|
+
}
|
|
11737
|
+
function nextCwdAfterCd(currentCwd, firstStage, afterCd) {
|
|
11738
|
+
let index = skipSpaces(firstStage, afterCd);
|
|
11739
|
+
let target = null;
|
|
11740
|
+
while (index < firstStage.length) {
|
|
11741
|
+
const tokenResult = readShellToken(firstStage, index);
|
|
11742
|
+
if (tokenResult === null)
|
|
11743
|
+
break;
|
|
11744
|
+
const { token, end } = tokenResult;
|
|
11745
|
+
if (end <= index)
|
|
11746
|
+
break;
|
|
11747
|
+
index = skipSpaces(firstStage, end);
|
|
11748
|
+
if (token === "-")
|
|
11749
|
+
return null;
|
|
11750
|
+
if (token.length > 1 && token.startsWith("-"))
|
|
11751
|
+
continue;
|
|
11752
|
+
target = token;
|
|
11753
|
+
break;
|
|
11754
|
+
}
|
|
11755
|
+
if (target === null)
|
|
11756
|
+
return path.resolve(os.homedir());
|
|
11757
|
+
if (target.includes("$") || target.includes("`"))
|
|
11758
|
+
return null;
|
|
11759
|
+
const expanded = expandTilde(target);
|
|
11760
|
+
if (path.isAbsolute(expanded))
|
|
11761
|
+
return path.resolve(expanded);
|
|
11762
|
+
if (currentCwd === null)
|
|
11763
|
+
return null;
|
|
11764
|
+
return path.resolve(currentCwd, expanded);
|
|
11765
|
+
}
|
|
11766
|
+
function isDirInsideProject(resolvedRoot, dir) {
|
|
11767
|
+
const rel = path.relative(resolvedRoot, path.resolve(dir));
|
|
11768
|
+
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
11769
|
+
}
|
|
11706
11770
|
function collectPathOperands(firstStage, startAfterCommand) {
|
|
11707
11771
|
const operands = [];
|
|
11708
11772
|
let index = skipSpaces(firstStage, startAfterCommand);
|
|
@@ -11724,6 +11788,9 @@ function collectPathOperands(firstStage, startAfterCommand) {
|
|
|
11724
11788
|
function looksLikePathOperand(token) {
|
|
11725
11789
|
return token.includes("/") || token.startsWith("~") || token.startsWith("./") || token.startsWith("../");
|
|
11726
11790
|
}
|
|
11791
|
+
function isDynamicPathOperand(token) {
|
|
11792
|
+
return token.startsWith("~") || token.includes("$") || token.includes("`");
|
|
11793
|
+
}
|
|
11727
11794
|
function expandTilde(target) {
|
|
11728
11795
|
if (!target.startsWith("~"))
|
|
11729
11796
|
return target;
|
|
@@ -11736,10 +11803,9 @@ function resolvePathOperand(projectRoot, operand) {
|
|
|
11736
11803
|
const expanded = expandTilde(operand);
|
|
11737
11804
|
return path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(projectRoot, expanded);
|
|
11738
11805
|
}
|
|
11739
|
-
function isPathInsideProject(
|
|
11740
|
-
const
|
|
11741
|
-
const
|
|
11742
|
-
const rel = path.relative(root, resolved);
|
|
11806
|
+
function isPathInsideProject(resolvedRoot, baseCwd, operand) {
|
|
11807
|
+
const resolved = resolvePathOperand(baseCwd, operand);
|
|
11808
|
+
const rel = path.relative(resolvedRoot, resolved);
|
|
11743
11809
|
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
11744
11810
|
}
|
|
11745
11811
|
function splitTopLevelStatements(command) {
|
|
@@ -11932,6 +11998,7 @@ var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
|
|
|
11932
11998
|
trace_to_symbol: 60000,
|
|
11933
11999
|
trace_data: 60000,
|
|
11934
12000
|
impact: 60000,
|
|
12001
|
+
inspect: 60000,
|
|
11935
12002
|
grep: 60000,
|
|
11936
12003
|
glob: 60000,
|
|
11937
12004
|
semantic_search: 60000
|
|
@@ -11995,7 +12062,6 @@ ${formatStatusBar(counts)}`;
|
|
|
11995
12062
|
// ../aft-bridge/dist/bridge.js
|
|
11996
12063
|
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
11997
12064
|
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
11998
|
-
var SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5000;
|
|
11999
12065
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
12000
12066
|
var STDOUT_BUFFER_COMPACT_THRESHOLD = 64 * 1024;
|
|
12001
12067
|
var TERMINAL_BASH_STATUSES = new Set([
|
|
@@ -12068,28 +12134,19 @@ function compareSemver(a, b) {
|
|
|
12068
12134
|
}
|
|
12069
12135
|
return 0;
|
|
12070
12136
|
}
|
|
12071
|
-
function
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
const maxSemanticTimeoutMs = semanticTransportBudgetMs > SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS ? semanticTransportBudgetMs - SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS : Math.max(1, semanticTransportBudgetMs - 1);
|
|
12082
|
-
if (timeoutMs <= maxSemanticTimeoutMs) {
|
|
12083
|
-
return configOverrides;
|
|
12084
|
-
}
|
|
12085
|
-
warn(`semantic.timeout_ms=${timeoutMs} exceeds the semantic transport budget; clamping to ${maxSemanticTimeoutMs}ms (budget: ${semanticTransportBudgetMs}ms)`);
|
|
12086
|
-
return {
|
|
12087
|
-
...configOverrides,
|
|
12088
|
-
semantic: {
|
|
12089
|
-
...semantic,
|
|
12090
|
-
timeout_ms: maxSemanticTimeoutMs
|
|
12137
|
+
function coerceConfigureDroppedKeys(value) {
|
|
12138
|
+
if (!Array.isArray(value))
|
|
12139
|
+
return [];
|
|
12140
|
+
const dropped = [];
|
|
12141
|
+
for (const item of value) {
|
|
12142
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
12143
|
+
continue;
|
|
12144
|
+
const record = item;
|
|
12145
|
+
if (typeof record.key === "string" && typeof record.tier === "string" && typeof record.reason === "string") {
|
|
12146
|
+
dropped.push({ key: record.key, tier: record.tier, reason: record.reason });
|
|
12091
12147
|
}
|
|
12092
|
-
}
|
|
12148
|
+
}
|
|
12149
|
+
return dropped;
|
|
12093
12150
|
}
|
|
12094
12151
|
|
|
12095
12152
|
class BridgeReplacedDuringVersionCheck extends Error {
|
|
@@ -12159,7 +12216,7 @@ class BinaryBridge {
|
|
|
12159
12216
|
this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
|
|
12160
12217
|
this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
12161
12218
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
12162
|
-
this.configOverrides =
|
|
12219
|
+
this.configOverrides = configOverrides ?? {};
|
|
12163
12220
|
this.minVersion = options?.minVersion;
|
|
12164
12221
|
this.onVersionMismatch = options?.onVersionMismatch;
|
|
12165
12222
|
this.onConfigureWarnings = options?.onConfigureWarnings;
|
|
@@ -12396,18 +12453,24 @@ class BinaryBridge {
|
|
|
12396
12453
|
}
|
|
12397
12454
|
}
|
|
12398
12455
|
async deliverConfigureWarnings(configResult, params, options) {
|
|
12399
|
-
if (!this.onConfigureWarnings
|
|
12456
|
+
if (!this.onConfigureWarnings)
|
|
12400
12457
|
return;
|
|
12401
|
-
|
|
12458
|
+
const warnings = Array.isArray(configResult.warnings) ? configResult.warnings : [];
|
|
12459
|
+
const configDroppedKeys = coerceConfigureDroppedKeys(configResult.config_dropped_keys);
|
|
12460
|
+
if (warnings.length === 0 && configDroppedKeys.length === 0)
|
|
12402
12461
|
return;
|
|
12403
12462
|
const sessionId = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
12463
|
+
const context = {
|
|
12464
|
+
projectRoot: this.cwd,
|
|
12465
|
+
sessionId,
|
|
12466
|
+
client: options?.configureWarningClient ?? (sessionId ? this.configureWarningClients.get(sessionId) : undefined),
|
|
12467
|
+
warnings
|
|
12468
|
+
};
|
|
12469
|
+
if (configDroppedKeys.length > 0) {
|
|
12470
|
+
context.configDroppedKeys = configDroppedKeys;
|
|
12471
|
+
}
|
|
12404
12472
|
try {
|
|
12405
|
-
await this.onConfigureWarnings(
|
|
12406
|
-
projectRoot: this.cwd,
|
|
12407
|
-
sessionId,
|
|
12408
|
-
client: options?.configureWarningClient ?? (sessionId ? this.configureWarningClients.get(sessionId) : undefined),
|
|
12409
|
-
warnings: configResult.warnings
|
|
12410
|
-
});
|
|
12473
|
+
await this.onConfigureWarnings(context);
|
|
12411
12474
|
} catch (err) {
|
|
12412
12475
|
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
12413
12476
|
} finally {
|
|
@@ -13114,6 +13177,62 @@ function coerceStringArray(value) {
|
|
|
13114
13177
|
}
|
|
13115
13178
|
return [];
|
|
13116
13179
|
}
|
|
13180
|
+
function coerceTargetParam(value) {
|
|
13181
|
+
if (Array.isArray(value)) {
|
|
13182
|
+
return value.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
13183
|
+
}
|
|
13184
|
+
if (typeof value === "string") {
|
|
13185
|
+
const trimmed = value.trim();
|
|
13186
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
13187
|
+
try {
|
|
13188
|
+
const parsed = JSON.parse(trimmed);
|
|
13189
|
+
if (Array.isArray(parsed)) {
|
|
13190
|
+
return parsed.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
13191
|
+
}
|
|
13192
|
+
} catch {}
|
|
13193
|
+
}
|
|
13194
|
+
return value;
|
|
13195
|
+
}
|
|
13196
|
+
return value;
|
|
13197
|
+
}
|
|
13198
|
+
function coerceBoolean(value, defaultValue = false) {
|
|
13199
|
+
if (defaultValue) {
|
|
13200
|
+
if (value === undefined)
|
|
13201
|
+
return true;
|
|
13202
|
+
if (typeof value === "boolean")
|
|
13203
|
+
return value;
|
|
13204
|
+
if (typeof value === "number")
|
|
13205
|
+
return value !== 0;
|
|
13206
|
+
if (typeof value === "string") {
|
|
13207
|
+
const normalized = value.trim().toLowerCase();
|
|
13208
|
+
return normalized !== "false" && normalized !== "0";
|
|
13209
|
+
}
|
|
13210
|
+
return true;
|
|
13211
|
+
}
|
|
13212
|
+
if (typeof value === "boolean")
|
|
13213
|
+
return value;
|
|
13214
|
+
if (typeof value === "number")
|
|
13215
|
+
return value === 1;
|
|
13216
|
+
if (typeof value === "string") {
|
|
13217
|
+
const normalized = value.trim().toLowerCase();
|
|
13218
|
+
return normalized === "true" || normalized === "1";
|
|
13219
|
+
}
|
|
13220
|
+
return false;
|
|
13221
|
+
}
|
|
13222
|
+
function coerceOptionalInt(v, paramName, min, max) {
|
|
13223
|
+
if (v === undefined || v === null || v === "")
|
|
13224
|
+
return;
|
|
13225
|
+
if (typeof v === "number" && (!Number.isFinite(v) || v === 0 && min > 0))
|
|
13226
|
+
return;
|
|
13227
|
+
const n = typeof v === "string" ? Number(v) : v;
|
|
13228
|
+
if (typeof n !== "number" || !Number.isInteger(n)) {
|
|
13229
|
+
throw new Error(`${paramName} must be an integer between ${min} and ${max}`);
|
|
13230
|
+
}
|
|
13231
|
+
if (n < min || n > max) {
|
|
13232
|
+
throw new Error(`${paramName} must be between ${min} and ${max}`);
|
|
13233
|
+
}
|
|
13234
|
+
return n;
|
|
13235
|
+
}
|
|
13117
13236
|
function isEmptyParam(value) {
|
|
13118
13237
|
if (value === undefined || value === null)
|
|
13119
13238
|
return true;
|
|
@@ -13125,10 +13244,43 @@ function isEmptyParam(value) {
|
|
|
13125
13244
|
return Object.keys(value).length === 0;
|
|
13126
13245
|
return false;
|
|
13127
13246
|
}
|
|
13247
|
+
// ../aft-bridge/dist/config-tiers.js
|
|
13248
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13249
|
+
import { resolve as resolve2 } from "node:path";
|
|
13250
|
+
function readConfigTiers(opts) {
|
|
13251
|
+
const tiers = [];
|
|
13252
|
+
try {
|
|
13253
|
+
if (existsSync(opts.userConfigPath)) {
|
|
13254
|
+
const doc = readFileSync(opts.userConfigPath, "utf-8");
|
|
13255
|
+
tiers.push({
|
|
13256
|
+
tier: "user",
|
|
13257
|
+
source: resolve2(opts.userConfigPath),
|
|
13258
|
+
doc
|
|
13259
|
+
});
|
|
13260
|
+
}
|
|
13261
|
+
} catch {}
|
|
13262
|
+
try {
|
|
13263
|
+
if (existsSync(opts.projectConfigPath)) {
|
|
13264
|
+
const doc = readFileSync(opts.projectConfigPath, "utf-8");
|
|
13265
|
+
tiers.push({
|
|
13266
|
+
tier: "project",
|
|
13267
|
+
source: resolve2(opts.projectConfigPath),
|
|
13268
|
+
doc
|
|
13269
|
+
});
|
|
13270
|
+
}
|
|
13271
|
+
} catch {}
|
|
13272
|
+
return tiers;
|
|
13273
|
+
}
|
|
13274
|
+
function formatDroppedKeyWarnings(dropped) {
|
|
13275
|
+
if (!dropped || !Array.isArray(dropped)) {
|
|
13276
|
+
return [];
|
|
13277
|
+
}
|
|
13278
|
+
return dropped.map((item) => `Ignoring ${item.key} from ${item.tier} config (${item.reason})`);
|
|
13279
|
+
}
|
|
13128
13280
|
// ../aft-bridge/dist/downloader.js
|
|
13129
13281
|
import { spawnSync } from "node:child_process";
|
|
13130
13282
|
import { createHash, randomUUID } from "node:crypto";
|
|
13131
|
-
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
13283
|
+
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync2, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
13132
13284
|
import { homedir as homedir4 } from "node:os";
|
|
13133
13285
|
import { join as join3 } from "node:path";
|
|
13134
13286
|
import { Readable } from "node:stream";
|
|
@@ -13200,7 +13352,7 @@ function getCachedBinaryPath(version) {
|
|
|
13200
13352
|
if (!version)
|
|
13201
13353
|
return null;
|
|
13202
13354
|
const binaryPath = join3(getCacheDir(), version, getBinaryName());
|
|
13203
|
-
return
|
|
13355
|
+
return existsSync2(binaryPath) ? binaryPath : null;
|
|
13204
13356
|
}
|
|
13205
13357
|
async function downloadBinary(version) {
|
|
13206
13358
|
const archMap = PLATFORM_ARCH_MAP[process.platform] ?? {};
|
|
@@ -13219,7 +13371,7 @@ async function downloadBinary(version) {
|
|
|
13219
13371
|
const versionedCacheDir = join3(getCacheDir(), tag);
|
|
13220
13372
|
const binaryName = getBinaryName();
|
|
13221
13373
|
const binaryPath = join3(versionedCacheDir, binaryName);
|
|
13222
|
-
if (
|
|
13374
|
+
if (existsSync2(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
|
|
13223
13375
|
return binaryPath;
|
|
13224
13376
|
}
|
|
13225
13377
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
|
|
@@ -13233,11 +13385,11 @@ async function downloadBinary(version) {
|
|
|
13233
13385
|
let checksumTimeout = null;
|
|
13234
13386
|
const tmpPath = `${binaryPath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
13235
13387
|
try {
|
|
13236
|
-
if (!
|
|
13388
|
+
if (!existsSync2(versionedCacheDir)) {
|
|
13237
13389
|
mkdirSync(versionedCacheDir, { recursive: true });
|
|
13238
13390
|
}
|
|
13239
13391
|
releaseLock = await acquireDownloadLock(lockPath);
|
|
13240
|
-
if (
|
|
13392
|
+
if (existsSync2(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
|
|
13241
13393
|
return binaryPath;
|
|
13242
13394
|
}
|
|
13243
13395
|
binaryController = new AbortController;
|
|
@@ -13302,7 +13454,7 @@ async function downloadBinary(version) {
|
|
|
13302
13454
|
renameSync(tmpPath, binaryPath);
|
|
13303
13455
|
}
|
|
13304
13456
|
try {
|
|
13305
|
-
if (
|
|
13457
|
+
if (existsSync2(tmpPath))
|
|
13306
13458
|
unlinkSync(tmpPath);
|
|
13307
13459
|
} catch {
|
|
13308
13460
|
warn(`Could not clean up temporary download file ${tmpPath} — it can be removed manually.`);
|
|
@@ -13312,7 +13464,7 @@ async function downloadBinary(version) {
|
|
|
13312
13464
|
} catch (err) {
|
|
13313
13465
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13314
13466
|
error(`Failed to download AFT binary: ${msg}`);
|
|
13315
|
-
if (
|
|
13467
|
+
if (existsSync2(tmpPath)) {
|
|
13316
13468
|
try {
|
|
13317
13469
|
unlinkSync(tmpPath);
|
|
13318
13470
|
} catch {}
|
|
@@ -13356,7 +13508,7 @@ async function acquireDownloadLock(lockPath) {
|
|
|
13356
13508
|
closeSync(fd);
|
|
13357
13509
|
} catch {}
|
|
13358
13510
|
try {
|
|
13359
|
-
if (
|
|
13511
|
+
if (readFileSync2(lockPath, "utf-8") === owner) {
|
|
13360
13512
|
rmSync(lockPath, { force: true });
|
|
13361
13513
|
}
|
|
13362
13514
|
} catch {}
|
|
@@ -13377,7 +13529,7 @@ async function acquireDownloadLock(lockPath) {
|
|
|
13377
13529
|
if (Date.now() - startedAt > DOWNLOAD_LOCK_TIMEOUT_MS) {
|
|
13378
13530
|
throw new Error(`Timed out waiting for download lock: ${lockPath}`);
|
|
13379
13531
|
}
|
|
13380
|
-
await new Promise((
|
|
13532
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
13381
13533
|
}
|
|
13382
13534
|
}
|
|
13383
13535
|
}
|
|
@@ -13474,20 +13626,71 @@ function stripJsoncSymbols(value) {
|
|
|
13474
13626
|
}
|
|
13475
13627
|
// ../aft-bridge/dist/migration.js
|
|
13476
13628
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13477
|
-
import { existsSync as
|
|
13478
|
-
import { homedir as
|
|
13479
|
-
import { dirname as dirname2, join as join6 } from "node:path";
|
|
13629
|
+
import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync4, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
13630
|
+
import { homedir as homedir7, tmpdir } from "node:os";
|
|
13631
|
+
import { basename, dirname as dirname2, join as join6, resolve as resolve4 } from "node:path";
|
|
13480
13632
|
|
|
13481
13633
|
// ../aft-bridge/dist/paths.js
|
|
13482
|
-
import { existsSync as
|
|
13483
|
-
import {
|
|
13634
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync } from "node:fs";
|
|
13635
|
+
import { homedir as homedir5 } from "node:os";
|
|
13636
|
+
import { dirname, isAbsolute as isAbsolute2, join as join4, resolve as resolve3 } from "node:path";
|
|
13637
|
+
function homeDir() {
|
|
13638
|
+
if (process.platform === "win32")
|
|
13639
|
+
return process.env.USERPROFILE || process.env.HOME || homedir5();
|
|
13640
|
+
return process.env.HOME || homedir5();
|
|
13641
|
+
}
|
|
13642
|
+
function configHome() {
|
|
13643
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
13644
|
+
if (xdg && isAbsolute2(xdg))
|
|
13645
|
+
return xdg;
|
|
13646
|
+
return join4(homeDir(), ".config");
|
|
13647
|
+
}
|
|
13648
|
+
function legacyOpenCodeConfigDir() {
|
|
13649
|
+
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
13650
|
+
if (envDir)
|
|
13651
|
+
return resolve3(envDir);
|
|
13652
|
+
return join4(configHome(), "opencode");
|
|
13653
|
+
}
|
|
13654
|
+
function legacyPiAgentDir() {
|
|
13655
|
+
return join4(homeDir(), ".pi", "agent");
|
|
13656
|
+
}
|
|
13657
|
+
function legacySources(basePath, label, harness) {
|
|
13658
|
+
return [
|
|
13659
|
+
{ path: `${basePath}.jsonc`, label: `${label} aft.jsonc`, harness },
|
|
13660
|
+
{ path: `${basePath}.json`, label: `${label} aft.json`, harness }
|
|
13661
|
+
];
|
|
13662
|
+
}
|
|
13663
|
+
function resolveCortexKitUserConfigPath() {
|
|
13664
|
+
return join4(configHome(), "cortexkit", "aft.jsonc");
|
|
13665
|
+
}
|
|
13666
|
+
function resolveCortexKitProjectConfigPath(projectDirectory) {
|
|
13667
|
+
return join4(projectDirectory, ".cortexkit", "aft.jsonc");
|
|
13668
|
+
}
|
|
13669
|
+
function resolveCortexKitConfigPaths(projectDirectory) {
|
|
13670
|
+
return {
|
|
13671
|
+
userConfigPath: resolveCortexKitUserConfigPath(),
|
|
13672
|
+
projectConfigPath: resolveCortexKitProjectConfigPath(projectDirectory)
|
|
13673
|
+
};
|
|
13674
|
+
}
|
|
13675
|
+
function resolveLegacyAftConfigSources(projectDirectory) {
|
|
13676
|
+
return {
|
|
13677
|
+
user: [
|
|
13678
|
+
...legacySources(join4(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
|
|
13679
|
+
...legacySources(join4(legacyPiAgentDir(), "aft"), "Pi user", "pi")
|
|
13680
|
+
],
|
|
13681
|
+
project: [
|
|
13682
|
+
...legacySources(join4(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
|
|
13683
|
+
...legacySources(join4(projectDirectory, ".pi", "aft"), "Pi project", "pi")
|
|
13684
|
+
]
|
|
13685
|
+
};
|
|
13686
|
+
}
|
|
13484
13687
|
function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
|
|
13485
13688
|
return join4(storageRoot, harness, ...segments);
|
|
13486
13689
|
}
|
|
13487
13690
|
function repairRootScopedStorageFile(storageRoot, harness, fileName) {
|
|
13488
13691
|
const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
|
|
13489
13692
|
const rootPath = join4(storageRoot, fileName);
|
|
13490
|
-
if (
|
|
13693
|
+
if (existsSync3(harnessPath) || !existsSync3(rootPath))
|
|
13491
13694
|
return harnessPath;
|
|
13492
13695
|
try {
|
|
13493
13696
|
mkdirSync2(dirname(harnessPath), { recursive: true });
|
|
@@ -13501,8 +13704,8 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
|
|
|
13501
13704
|
const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
|
|
13502
13705
|
let lastVersion = "";
|
|
13503
13706
|
try {
|
|
13504
|
-
if (
|
|
13505
|
-
lastVersion =
|
|
13707
|
+
if (existsSync3(versionFile)) {
|
|
13708
|
+
lastVersion = readFileSync3(versionFile, "utf-8").trim();
|
|
13506
13709
|
}
|
|
13507
13710
|
} catch {
|
|
13508
13711
|
return false;
|
|
@@ -13530,9 +13733,9 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
13530
13733
|
|
|
13531
13734
|
// ../aft-bridge/dist/resolver.js
|
|
13532
13735
|
import { execSync } from "node:child_process";
|
|
13533
|
-
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as
|
|
13736
|
+
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
13534
13737
|
import { createRequire as createRequire2 } from "node:module";
|
|
13535
|
-
import { homedir as
|
|
13738
|
+
import { homedir as homedir6 } from "node:os";
|
|
13536
13739
|
import { join as join5 } from "node:path";
|
|
13537
13740
|
var ensureBinaryForResolver = ensureBinary;
|
|
13538
13741
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
@@ -13545,7 +13748,7 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
13545
13748
|
const versionedDir = join5(cacheDir, tag);
|
|
13546
13749
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
13547
13750
|
const cachedPath = join5(versionedDir, `aft${ext}`);
|
|
13548
|
-
if (
|
|
13751
|
+
if (existsSync4(cachedPath)) {
|
|
13549
13752
|
const cachedVersion = readBinaryVersion(cachedPath);
|
|
13550
13753
|
if (cachedVersion === version)
|
|
13551
13754
|
return cachedPath;
|
|
@@ -13557,7 +13760,7 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
13557
13760
|
if (process.platform !== "win32") {
|
|
13558
13761
|
chmodSync2(tmpPath, 493);
|
|
13559
13762
|
}
|
|
13560
|
-
if (process.platform === "win32" &&
|
|
13763
|
+
if (process.platform === "win32" && existsSync4(cachedPath)) {
|
|
13561
13764
|
try {
|
|
13562
13765
|
unlinkSync2(cachedPath);
|
|
13563
13766
|
} catch {}
|
|
@@ -13574,7 +13777,7 @@ function normalizeBareVersion(version) {
|
|
|
13574
13777
|
return version.startsWith("v") ? version.slice(1) : version;
|
|
13575
13778
|
}
|
|
13576
13779
|
function homeDirFromEnv(env) {
|
|
13577
|
-
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) ||
|
|
13780
|
+
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir6();
|
|
13578
13781
|
}
|
|
13579
13782
|
function cacheDirFromEnv(env) {
|
|
13580
13783
|
if (process.platform === "win32") {
|
|
@@ -13586,7 +13789,7 @@ function cacheDirFromEnv(env) {
|
|
|
13586
13789
|
}
|
|
13587
13790
|
function cachedBinaryPathFromEnv(version, env, ext) {
|
|
13588
13791
|
const binaryPath = join5(cacheDirFromEnv(env), version, `aft${ext}`);
|
|
13589
|
-
return
|
|
13792
|
+
return existsSync4(binaryPath) ? binaryPath : null;
|
|
13590
13793
|
}
|
|
13591
13794
|
function isExpectedCachedBinary2(binaryPath, expectedVersion) {
|
|
13592
13795
|
const expected = normalizeBareVersion(expectedVersion);
|
|
@@ -13675,7 +13878,7 @@ function findBinarySync(expectedVersion) {
|
|
|
13675
13878
|
const packageBin = `@cortexkit/aft-${key}/bin/aft${ext}`;
|
|
13676
13879
|
const req = createRequire2(import.meta.url);
|
|
13677
13880
|
const resolved = req.resolve(packageBin);
|
|
13678
|
-
if (
|
|
13881
|
+
if (existsSync4(resolved)) {
|
|
13679
13882
|
const npmVersion = readBinaryVersion(resolved);
|
|
13680
13883
|
if (npmVersion === null) {
|
|
13681
13884
|
warn(`npm platform package binary at ${resolved} did not report a version; skipping (continuing to PATH lookup)`);
|
|
@@ -13705,7 +13908,7 @@ function findBinarySync(expectedVersion) {
|
|
|
13705
13908
|
}
|
|
13706
13909
|
} catch {}
|
|
13707
13910
|
const cargoPath = join5(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
|
|
13708
|
-
if (
|
|
13911
|
+
if (existsSync4(cargoPath)) {
|
|
13709
13912
|
const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
|
|
13710
13913
|
if (usable)
|
|
13711
13914
|
return usable;
|
|
@@ -13750,23 +13953,343 @@ function dataHome() {
|
|
|
13750
13953
|
if (process.env.XDG_DATA_HOME)
|
|
13751
13954
|
return process.env.XDG_DATA_HOME;
|
|
13752
13955
|
if (process.platform === "win32") {
|
|
13753
|
-
return process.env.LOCALAPPDATA || process.env.APPDATA || join6(
|
|
13956
|
+
return process.env.LOCALAPPDATA || process.env.APPDATA || join6(homeDir2(), "AppData", "Local");
|
|
13754
13957
|
}
|
|
13755
|
-
return join6(
|
|
13958
|
+
return join6(homeDir2(), ".local", "share");
|
|
13756
13959
|
}
|
|
13757
|
-
function
|
|
13960
|
+
function homeDir2() {
|
|
13758
13961
|
if (process.platform === "win32")
|
|
13759
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
13760
|
-
return process.env.HOME ||
|
|
13962
|
+
return process.env.USERPROFILE || process.env.HOME || homedir7();
|
|
13963
|
+
return process.env.HOME || homedir7();
|
|
13761
13964
|
}
|
|
13762
13965
|
function resolveLegacyStorageRoot(harness) {
|
|
13763
13966
|
if (harness === "pi")
|
|
13764
|
-
return join6(
|
|
13967
|
+
return join6(homeDir2(), ".pi", "agent", "aft");
|
|
13765
13968
|
return join6(dataHome(), "opencode", "storage", "plugin", "aft");
|
|
13766
13969
|
}
|
|
13767
13970
|
function resolveCortexKitStorageRoot() {
|
|
13768
13971
|
return join6(dataHome(), "cortexkit", "aft");
|
|
13769
13972
|
}
|
|
13973
|
+
function stripJsoncForParse(input) {
|
|
13974
|
+
let out = "";
|
|
13975
|
+
let inString = false;
|
|
13976
|
+
let escaped = false;
|
|
13977
|
+
for (let i = 0;i < input.length; i++) {
|
|
13978
|
+
const ch = input[i];
|
|
13979
|
+
const next = input[i + 1];
|
|
13980
|
+
if (inString) {
|
|
13981
|
+
out += ch;
|
|
13982
|
+
if (escaped) {
|
|
13983
|
+
escaped = false;
|
|
13984
|
+
} else if (ch === "\\") {
|
|
13985
|
+
escaped = true;
|
|
13986
|
+
} else if (ch === '"') {
|
|
13987
|
+
inString = false;
|
|
13988
|
+
}
|
|
13989
|
+
continue;
|
|
13990
|
+
}
|
|
13991
|
+
if (ch === '"') {
|
|
13992
|
+
inString = true;
|
|
13993
|
+
out += ch;
|
|
13994
|
+
continue;
|
|
13995
|
+
}
|
|
13996
|
+
if (ch === "/" && next === "/") {
|
|
13997
|
+
while (i < input.length && input[i] !== `
|
|
13998
|
+
`)
|
|
13999
|
+
i++;
|
|
14000
|
+
out += `
|
|
14001
|
+
`;
|
|
14002
|
+
continue;
|
|
14003
|
+
}
|
|
14004
|
+
if (ch === "/" && next === "*") {
|
|
14005
|
+
i += 2;
|
|
14006
|
+
while (i < input.length && !(input[i] === "*" && input[i + 1] === "/"))
|
|
14007
|
+
i++;
|
|
14008
|
+
i++;
|
|
14009
|
+
out += " ";
|
|
14010
|
+
continue;
|
|
14011
|
+
}
|
|
14012
|
+
out += ch;
|
|
14013
|
+
}
|
|
14014
|
+
let withoutTrailingCommas = "";
|
|
14015
|
+
inString = false;
|
|
14016
|
+
escaped = false;
|
|
14017
|
+
for (let i = 0;i < out.length; i++) {
|
|
14018
|
+
const ch = out[i];
|
|
14019
|
+
if (inString) {
|
|
14020
|
+
withoutTrailingCommas += ch;
|
|
14021
|
+
if (escaped) {
|
|
14022
|
+
escaped = false;
|
|
14023
|
+
} else if (ch === "\\") {
|
|
14024
|
+
escaped = true;
|
|
14025
|
+
} else if (ch === '"') {
|
|
14026
|
+
inString = false;
|
|
14027
|
+
}
|
|
14028
|
+
continue;
|
|
14029
|
+
}
|
|
14030
|
+
if (ch === '"') {
|
|
14031
|
+
inString = true;
|
|
14032
|
+
withoutTrailingCommas += ch;
|
|
14033
|
+
continue;
|
|
14034
|
+
}
|
|
14035
|
+
if (ch === ",") {
|
|
14036
|
+
let j = i + 1;
|
|
14037
|
+
while (j < out.length && /\s/.test(out[j]))
|
|
14038
|
+
j++;
|
|
14039
|
+
if (out[j] === "}" || out[j] === "]")
|
|
14040
|
+
continue;
|
|
14041
|
+
}
|
|
14042
|
+
withoutTrailingCommas += ch;
|
|
14043
|
+
}
|
|
14044
|
+
return withoutTrailingCommas;
|
|
14045
|
+
}
|
|
14046
|
+
function sortJson(value) {
|
|
14047
|
+
if (Array.isArray(value))
|
|
14048
|
+
return value.map(sortJson);
|
|
14049
|
+
if (value && typeof value === "object") {
|
|
14050
|
+
const sorted = {};
|
|
14051
|
+
for (const key of Object.keys(value).sort()) {
|
|
14052
|
+
sorted[key] = sortJson(value[key]);
|
|
14053
|
+
}
|
|
14054
|
+
return sorted;
|
|
14055
|
+
}
|
|
14056
|
+
return value;
|
|
14057
|
+
}
|
|
14058
|
+
function normalizedJsoncSemantics(content) {
|
|
14059
|
+
return JSON.stringify(sortJson(JSON.parse(stripJsoncForParse(content))));
|
|
14060
|
+
}
|
|
14061
|
+
function fileSemanticsMatch(a, b) {
|
|
14062
|
+
try {
|
|
14063
|
+
return normalizedJsoncSemantics(a) === normalizedJsoncSemantics(b);
|
|
14064
|
+
} catch {
|
|
14065
|
+
return a === b;
|
|
14066
|
+
}
|
|
14067
|
+
}
|
|
14068
|
+
function sleepSync(ms) {
|
|
14069
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
14070
|
+
}
|
|
14071
|
+
function acquireConfigMigrationLock(lockDir) {
|
|
14072
|
+
const deadline = Date.now() + 30000;
|
|
14073
|
+
while (true) {
|
|
14074
|
+
try {
|
|
14075
|
+
mkdirSync4(lockDir, { recursive: false });
|
|
14076
|
+
return () => {
|
|
14077
|
+
try {
|
|
14078
|
+
rmSync2(lockDir, { recursive: true, force: true });
|
|
14079
|
+
} catch {}
|
|
14080
|
+
};
|
|
14081
|
+
} catch (err) {
|
|
14082
|
+
const code = err?.code;
|
|
14083
|
+
if (code !== "EEXIST")
|
|
14084
|
+
throw err;
|
|
14085
|
+
try {
|
|
14086
|
+
const ageMs = Date.now() - statSync2(lockDir).mtimeMs;
|
|
14087
|
+
if (ageMs > 60000) {
|
|
14088
|
+
rmSync2(lockDir, { recursive: true, force: true });
|
|
14089
|
+
continue;
|
|
14090
|
+
}
|
|
14091
|
+
} catch {}
|
|
14092
|
+
if (Date.now() >= deadline) {
|
|
14093
|
+
throw new Error(`timed out waiting for config migration lock ${lockDir}`);
|
|
14094
|
+
}
|
|
14095
|
+
sleepSync(25);
|
|
14096
|
+
}
|
|
14097
|
+
}
|
|
14098
|
+
}
|
|
14099
|
+
function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
14100
|
+
mkdirSync4(dirname2(targetPath), { recursive: true });
|
|
14101
|
+
const tmpPath = join6(dirname2(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
14102
|
+
let fd = null;
|
|
14103
|
+
try {
|
|
14104
|
+
fd = openSync3(tmpPath, "wx", 384);
|
|
14105
|
+
writeFileSync2(fd, readFileSync4(sourcePath));
|
|
14106
|
+
closeSync3(fd);
|
|
14107
|
+
fd = null;
|
|
14108
|
+
renameSync4(tmpPath, targetPath);
|
|
14109
|
+
} catch (err) {
|
|
14110
|
+
if (fd !== null) {
|
|
14111
|
+
try {
|
|
14112
|
+
closeSync3(fd);
|
|
14113
|
+
} catch {}
|
|
14114
|
+
}
|
|
14115
|
+
try {
|
|
14116
|
+
unlinkSync3(tmpPath);
|
|
14117
|
+
} catch {}
|
|
14118
|
+
throw err;
|
|
14119
|
+
}
|
|
14120
|
+
}
|
|
14121
|
+
function atomicWriteConfigFile(targetPath, content) {
|
|
14122
|
+
mkdirSync4(dirname2(targetPath), { recursive: true });
|
|
14123
|
+
const tmpPath = join6(dirname2(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
14124
|
+
let fd = null;
|
|
14125
|
+
try {
|
|
14126
|
+
fd = openSync3(tmpPath, "wx", 384);
|
|
14127
|
+
writeFileSync2(fd, content);
|
|
14128
|
+
closeSync3(fd);
|
|
14129
|
+
fd = null;
|
|
14130
|
+
renameSync4(tmpPath, targetPath);
|
|
14131
|
+
} catch (err) {
|
|
14132
|
+
if (fd !== null) {
|
|
14133
|
+
try {
|
|
14134
|
+
closeSync3(fd);
|
|
14135
|
+
} catch {}
|
|
14136
|
+
}
|
|
14137
|
+
try {
|
|
14138
|
+
unlinkSync3(tmpPath);
|
|
14139
|
+
} catch {}
|
|
14140
|
+
throw err;
|
|
14141
|
+
}
|
|
14142
|
+
}
|
|
14143
|
+
var MOVED_MARKER_SUFFIX = ".MOVED_READPLEASE";
|
|
14144
|
+
function nonClobberingSidecarPath(desiredPath) {
|
|
14145
|
+
if (!existsSync5(desiredPath))
|
|
14146
|
+
return desiredPath;
|
|
14147
|
+
for (let index = 1;index < Number.MAX_SAFE_INTEGER; index++) {
|
|
14148
|
+
const candidate = `${desiredPath}.${index}`;
|
|
14149
|
+
if (!existsSync5(candidate))
|
|
14150
|
+
return candidate;
|
|
14151
|
+
}
|
|
14152
|
+
throw new Error(`could not find available preservation sidecar path for ${desiredPath}`);
|
|
14153
|
+
}
|
|
14154
|
+
function movedMarkerContent(targetPath, originalName, originalContent) {
|
|
14155
|
+
const header = [
|
|
14156
|
+
"// AFT configuration moved.",
|
|
14157
|
+
"//",
|
|
14158
|
+
"// AFT now reads its configuration from one shared CortexKit location",
|
|
14159
|
+
"// instead of a per-agent path. The settings that were in this file have",
|
|
14160
|
+
"// been moved to:",
|
|
14161
|
+
"//",
|
|
14162
|
+
`// ${targetPath}`,
|
|
14163
|
+
"//",
|
|
14164
|
+
"// Edit that file to change AFT settings. This location is no longer read",
|
|
14165
|
+
"// by AFT.",
|
|
14166
|
+
"//",
|
|
14167
|
+
`// To undo, rename this file back to "${originalName}" (and remove the`,
|
|
14168
|
+
"// CortexKit copy above if you want this location to take precedence).",
|
|
14169
|
+
"//",
|
|
14170
|
+
"// Your original settings are preserved below for reference.",
|
|
14171
|
+
"",
|
|
14172
|
+
""
|
|
14173
|
+
].join(`
|
|
14174
|
+
`);
|
|
14175
|
+
return `${header}${originalContent}`;
|
|
14176
|
+
}
|
|
14177
|
+
function markLegacySourcesMovedAside(sources, targetPath, logger) {
|
|
14178
|
+
const warnings = [];
|
|
14179
|
+
const info = logger?.info ?? logger?.log;
|
|
14180
|
+
for (const source of sources) {
|
|
14181
|
+
const desiredMarkerPath = `${source.path}${MOVED_MARKER_SUFFIX}`;
|
|
14182
|
+
const markerPath = nonClobberingSidecarPath(desiredMarkerPath);
|
|
14183
|
+
try {
|
|
14184
|
+
const original = readFileSync4(source.path, "utf-8");
|
|
14185
|
+
if (markerPath !== desiredMarkerPath) {
|
|
14186
|
+
logger?.warn?.(`Preserving existing legacy AFT marker ${desiredMarkerPath}; writing new marker to ${markerPath}`);
|
|
14187
|
+
}
|
|
14188
|
+
atomicWriteConfigFile(markerPath, movedMarkerContent(targetPath, basename(source.path), original));
|
|
14189
|
+
unlinkSync3(source.path);
|
|
14190
|
+
info?.(`Moved legacy AFT config ${source.path} aside to ${markerPath}; AFT now reads ${targetPath}`);
|
|
14191
|
+
} catch (err) {
|
|
14192
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14193
|
+
warnings.push(`AFT could not move legacy config ${source.path} aside (${msg}); it is now stale and ignored. Delete it manually — AFT reads ${targetPath}.`);
|
|
14194
|
+
logger?.warn?.(`Could not move legacy AFT config ${source.path} aside (${msg}); AFT reads ${targetPath}`);
|
|
14195
|
+
}
|
|
14196
|
+
}
|
|
14197
|
+
return warnings;
|
|
14198
|
+
}
|
|
14199
|
+
var PRESERVED_OLD_SUFFIX = "_OLD";
|
|
14200
|
+
function preservedOldContent(losingHarness, winningHarness, targetPath, originalContent) {
|
|
14201
|
+
const winnerLine = winningHarness ? `// configs differed, so ${winningHarness}'s config won (first harness to run the` : "// configs differed, so the config that was migrated first won and is the";
|
|
14202
|
+
const header = [
|
|
14203
|
+
`// AFT config from ${losingHarness} — preserved, NOT read.`,
|
|
14204
|
+
"//",
|
|
14205
|
+
"// AFT now reads one shared config for all harnesses. Your OpenCode and Pi",
|
|
14206
|
+
winnerLine,
|
|
14207
|
+
winningHarness ? "// migration) and is now the active config at:" : "// active config at:",
|
|
14208
|
+
"//",
|
|
14209
|
+
`// ${targetPath}`,
|
|
14210
|
+
"//",
|
|
14211
|
+
`// This file holds ${losingHarness}'s previous settings. Merge anything you`,
|
|
14212
|
+
"// want to keep into the active config above, then delete this file. AFT does",
|
|
14213
|
+
"// not read this path.",
|
|
14214
|
+
"",
|
|
14215
|
+
""
|
|
14216
|
+
].join(`
|
|
14217
|
+
`);
|
|
14218
|
+
return `${header}${originalContent}`;
|
|
14219
|
+
}
|
|
14220
|
+
function preserveDifferingSourceAsOld(source, winningHarness, targetPath, logger) {
|
|
14221
|
+
const losingHarness = source.harness ?? "pi";
|
|
14222
|
+
const desiredOldPath = `${targetPath}.${losingHarness}${PRESERVED_OLD_SUFFIX}`;
|
|
14223
|
+
const oldPath = nonClobberingSidecarPath(desiredOldPath);
|
|
14224
|
+
try {
|
|
14225
|
+
if (oldPath !== desiredOldPath) {
|
|
14226
|
+
logger?.warn?.(`Preserving existing ${losingHarness} config sidecar ${desiredOldPath}; writing new preserved config to ${oldPath}`);
|
|
14227
|
+
}
|
|
14228
|
+
atomicWriteConfigFile(oldPath, preservedOldContent(losingHarness, winningHarness, targetPath, source.content));
|
|
14229
|
+
} catch (err) {
|
|
14230
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14231
|
+
logger?.warn?.(`Could not preserve ${losingHarness} config as ${oldPath} (${msg})`);
|
|
14232
|
+
}
|
|
14233
|
+
const usingDesc = winningHarness ? `${winningHarness}'s config` : "the existing shared config";
|
|
14234
|
+
return `AFT found different OpenCode and Pi configs during config unification. ` + `Using ${usingDesc} at ${targetPath}; preserved ${losingHarness}'s previous ` + `config at ${oldPath} — merge any settings you want to keep, then delete it.`;
|
|
14235
|
+
}
|
|
14236
|
+
function visibleConfigMigrationWarning(scope, targetPath, paths, reason) {
|
|
14237
|
+
const uniquePaths = [...new Set([targetPath, ...paths])];
|
|
14238
|
+
return `AFT ${scope} config migration refused: ${reason}. ` + `Legacy and CortexKit config paths collapse to one file, but AFT will not overwrite or merge them automatically. ` + `Please consolidate manually into ${targetPath}. Paths: ${uniquePaths.join(" ; ")}`;
|
|
14239
|
+
}
|
|
14240
|
+
function migrateAftConfigFile(opts) {
|
|
14241
|
+
const warnings = [];
|
|
14242
|
+
const resolvedTarget = resolve4(opts.targetPath);
|
|
14243
|
+
const existingSources = opts.legacySources.filter((source) => existsSync5(source.path) && resolve4(source.path) !== resolvedTarget);
|
|
14244
|
+
const info = opts.logger?.info ?? opts.logger?.log;
|
|
14245
|
+
if (existingSources.length === 0) {
|
|
14246
|
+
return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
|
|
14247
|
+
}
|
|
14248
|
+
mkdirSync4(dirname2(opts.targetPath), { recursive: true });
|
|
14249
|
+
const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
|
|
14250
|
+
try {
|
|
14251
|
+
const sources = existingSources.map((source) => ({
|
|
14252
|
+
...source,
|
|
14253
|
+
content: readFileSync4(source.path, "utf-8")
|
|
14254
|
+
}));
|
|
14255
|
+
if (existsSync5(opts.targetPath)) {
|
|
14256
|
+
const targetContent = readFileSync4(opts.targetPath, "utf-8");
|
|
14257
|
+
for (const source of sources) {
|
|
14258
|
+
if (fileSemanticsMatch(source.content, targetContent))
|
|
14259
|
+
continue;
|
|
14260
|
+
warnings.push(preserveDifferingSourceAsOld(source, undefined, opts.targetPath, opts.logger));
|
|
14261
|
+
}
|
|
14262
|
+
info?.(`AFT ${opts.scope} config already present at ${opts.targetPath}; reconciled ${sources.length} legacy source(s)`);
|
|
14263
|
+
warnings.push(...markLegacySourcesMovedAside(sources, opts.targetPath, opts.logger));
|
|
14264
|
+
return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
|
|
14265
|
+
}
|
|
14266
|
+
const winner = (opts.operatingHarness ? sources.find((source) => source.harness === opts.operatingHarness) : undefined) ?? sources[0];
|
|
14267
|
+
atomicCopyConfigFile(winner.path, opts.targetPath);
|
|
14268
|
+
info?.(`Migrated AFT ${opts.scope} config from ${winner.path} to ${opts.targetPath}`);
|
|
14269
|
+
for (const source of sources) {
|
|
14270
|
+
if (source.path === winner.path)
|
|
14271
|
+
continue;
|
|
14272
|
+
if (fileSemanticsMatch(source.content, winner.content))
|
|
14273
|
+
continue;
|
|
14274
|
+
warnings.push(preserveDifferingSourceAsOld(source, winner.harness, opts.targetPath, opts.logger));
|
|
14275
|
+
}
|
|
14276
|
+
warnings.push(...markLegacySourcesMovedAside(sources, opts.targetPath, opts.logger));
|
|
14277
|
+
return {
|
|
14278
|
+
migrated: true,
|
|
14279
|
+
conflict: false,
|
|
14280
|
+
sourcePath: winner.path,
|
|
14281
|
+
targetPath: opts.targetPath,
|
|
14282
|
+
warnings
|
|
14283
|
+
};
|
|
14284
|
+
} catch (err) {
|
|
14285
|
+
const message = visibleConfigMigrationWarning(opts.scope, opts.targetPath, existingSources.map((source) => source.path), `migration failed (${err instanceof Error ? err.message : String(err)})`);
|
|
14286
|
+
warnings.push(message);
|
|
14287
|
+
opts.logger?.warn?.(message);
|
|
14288
|
+
return { migrated: false, conflict: true, targetPath: opts.targetPath, warnings };
|
|
14289
|
+
} finally {
|
|
14290
|
+
release();
|
|
14291
|
+
}
|
|
14292
|
+
}
|
|
13770
14293
|
function tail(value) {
|
|
13771
14294
|
if (!value)
|
|
13772
14295
|
return "";
|
|
@@ -13794,11 +14317,11 @@ async function ensureStorageMigrated(opts) {
|
|
|
13794
14317
|
const newRoot = resolveCortexKitStorageRoot();
|
|
13795
14318
|
const targetMarker = resolveHarnessStoragePath(newRoot, opts.harness, TARGET_MARKER);
|
|
13796
14319
|
const info = opts.logger?.info ?? opts.logger?.log;
|
|
13797
|
-
if (
|
|
14320
|
+
if (existsSync5(targetMarker)) {
|
|
13798
14321
|
info?.(`AFT storage already migrated for ${opts.harness}; using ${newRoot}`);
|
|
13799
14322
|
return;
|
|
13800
14323
|
}
|
|
13801
|
-
if (!
|
|
14324
|
+
if (!existsSync5(legacyRoot)) {
|
|
13802
14325
|
info?.(`AFT storage migration skipped for ${opts.harness}: no legacy data at ${legacyRoot}; ` + `using ${newRoot} for fresh install`);
|
|
13803
14326
|
return;
|
|
13804
14327
|
}
|
|
@@ -13846,14 +14369,14 @@ async function ensureStorageMigrated(opts) {
|
|
|
13846
14369
|
throw new Error(`AFT storage migration failed (${detail}). ` + `Harness: ${opts.harness}. Legacy: ${legacyRoot}. Target: ${newRoot}. ` + `See log: ${logPath}. ` + `Plugin load aborted to prevent legacy/new state divergence.` + (stderrTail ? ` Stderr tail: ${stderrTail}` : "") + (stdoutTail ? ` Stdout tail: ${stdoutTail}` : ""));
|
|
13847
14370
|
}
|
|
13848
14371
|
// ../aft-bridge/dist/npm-resolver.js
|
|
13849
|
-
import { readdirSync, statSync as
|
|
13850
|
-
import { homedir as
|
|
13851
|
-
import { delimiter, dirname as dirname3, isAbsolute as
|
|
14372
|
+
import { readdirSync, statSync as statSync3 } from "node:fs";
|
|
14373
|
+
import { homedir as homedir8 } from "node:os";
|
|
14374
|
+
import { delimiter, dirname as dirname3, isAbsolute as isAbsolute3, join as join7 } from "node:path";
|
|
13852
14375
|
function defaultDeps() {
|
|
13853
14376
|
return {
|
|
13854
14377
|
platform: process.platform,
|
|
13855
14378
|
env: process.env,
|
|
13856
|
-
home:
|
|
14379
|
+
home: homedir8(),
|
|
13857
14380
|
execPath: process.execPath
|
|
13858
14381
|
};
|
|
13859
14382
|
}
|
|
@@ -13862,7 +14385,7 @@ function npmBinaryName(platform) {
|
|
|
13862
14385
|
}
|
|
13863
14386
|
function isFile(p) {
|
|
13864
14387
|
try {
|
|
13865
|
-
return
|
|
14388
|
+
return statSync3(p).isFile();
|
|
13866
14389
|
} catch {
|
|
13867
14390
|
return false;
|
|
13868
14391
|
}
|
|
@@ -13872,7 +14395,7 @@ function npmFromPath(deps) {
|
|
|
13872
14395
|
const raw = deps.env.PATH ?? deps.env.Path ?? "";
|
|
13873
14396
|
for (const entry of raw.split(delimiter)) {
|
|
13874
14397
|
const dir = entry.trim().replace(/^"|"$/g, "");
|
|
13875
|
-
if (!dir || !
|
|
14398
|
+
if (!dir || !isAbsolute3(dir))
|
|
13876
14399
|
continue;
|
|
13877
14400
|
if (isFile(join7(dir, name)))
|
|
13878
14401
|
return dir;
|
|
@@ -13965,8 +14488,8 @@ function isNpmAvailable(deps = defaultDeps()) {
|
|
|
13965
14488
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
13966
14489
|
import { execFileSync } from "node:child_process";
|
|
13967
14490
|
import { createHash as createHash2 } from "node:crypto";
|
|
13968
|
-
import { chmodSync as chmodSync3, closeSync as
|
|
13969
|
-
import { basename, dirname as dirname4, isAbsolute as
|
|
14491
|
+
import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, readlinkSync, realpathSync, rmSync as rmSync3, statSync as statSync4, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
14492
|
+
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute4, join as join8, relative as relative2, resolve as resolve5, win32 } from "node:path";
|
|
13970
14493
|
import { Readable as Readable2 } from "node:stream";
|
|
13971
14494
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
13972
14495
|
var ORT_VERSION = "1.24.4";
|
|
@@ -14036,7 +14559,7 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
14036
14559
|
const libName = info?.libName ?? "libonnxruntime.dylib";
|
|
14037
14560
|
const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
|
|
14038
14561
|
const libPath = join8(resolvedOrtDir, libName);
|
|
14039
|
-
if (
|
|
14562
|
+
if (existsSync6(libPath)) {
|
|
14040
14563
|
const meta = readOnnxInstalledMeta(ortVersionDir);
|
|
14041
14564
|
if (meta?.sha256) {
|
|
14042
14565
|
try {
|
|
@@ -14098,7 +14621,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14098
14621
|
abandoned = true;
|
|
14099
14622
|
} else {
|
|
14100
14623
|
try {
|
|
14101
|
-
const ageMs = Date.now() -
|
|
14624
|
+
const ageMs = Date.now() - statSync4(stagingDir).mtimeMs;
|
|
14102
14625
|
abandoned = ageMs > STALE_LOCK_MS;
|
|
14103
14626
|
} catch {
|
|
14104
14627
|
abandoned = true;
|
|
@@ -14113,7 +14636,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14113
14636
|
if (abandoned) {
|
|
14114
14637
|
log(`[onnx] removing abandoned staging dir ${stagingDir}`);
|
|
14115
14638
|
try {
|
|
14116
|
-
|
|
14639
|
+
rmSync3(stagingDir, { recursive: true, force: true });
|
|
14117
14640
|
} catch (err) {
|
|
14118
14641
|
warn(`[onnx] failed to remove ${stagingDir}: ${err}`);
|
|
14119
14642
|
}
|
|
@@ -14123,9 +14646,9 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14123
14646
|
}
|
|
14124
14647
|
function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
14125
14648
|
try {
|
|
14126
|
-
if (
|
|
14649
|
+
if (existsSync6(ortDir) && !existsSync6(join8(ortDir, ONNX_INSTALLED_META_FILE))) {
|
|
14127
14650
|
log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
|
|
14128
|
-
|
|
14651
|
+
rmSync3(ortDir, { recursive: true, force: true });
|
|
14129
14652
|
}
|
|
14130
14653
|
} catch (err) {
|
|
14131
14654
|
warn(`[onnx] failed to sweep ${ortDir}: ${err}`);
|
|
@@ -14135,7 +14658,7 @@ var REQUIRED_ORT_MAJOR = 1;
|
|
|
14135
14658
|
var REQUIRED_ORT_MIN_MINOR = 20;
|
|
14136
14659
|
var INVALID_ORT_VERSION = "<invalid>";
|
|
14137
14660
|
function parseOnnxVersionFromPath(value) {
|
|
14138
|
-
const name =
|
|
14661
|
+
const name = basename2(value);
|
|
14139
14662
|
const semverish = name.match(/(?:^|[._-])(\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?)(?:\.(?:dylib|dll))?$/);
|
|
14140
14663
|
if (semverish)
|
|
14141
14664
|
return semverish[1].split(/[-+]/, 1)[0];
|
|
@@ -14152,7 +14675,7 @@ function parseOnnxVersionFromDirectoryPath(value) {
|
|
|
14152
14675
|
}
|
|
14153
14676
|
function isPathInsideRoot(root, candidate) {
|
|
14154
14677
|
const rel = relative2(root, candidate);
|
|
14155
|
-
return rel === "" || !rel.startsWith("../") && !rel.startsWith("..\\") && rel !== ".." && !
|
|
14678
|
+
return rel === "" || !rel.startsWith("../") && !rel.startsWith("..\\") && rel !== ".." && !isAbsolute4(rel) && !win32.isAbsolute(rel);
|
|
14156
14679
|
}
|
|
14157
14680
|
function detectOnnxVersion(libDir, libName) {
|
|
14158
14681
|
try {
|
|
@@ -14168,7 +14691,7 @@ function detectOnnxVersion(libDir, libName) {
|
|
|
14168
14691
|
return version;
|
|
14169
14692
|
}
|
|
14170
14693
|
const base = join8(libDir, libName);
|
|
14171
|
-
if (
|
|
14694
|
+
if (existsSync6(base)) {
|
|
14172
14695
|
try {
|
|
14173
14696
|
const real = realpathSync(base);
|
|
14174
14697
|
const version = parseOnnxVersionFromPath(real) ?? parseOnnxVersionFromDirectoryPath(real);
|
|
@@ -14203,7 +14726,7 @@ function pathEntriesForPlatform() {
|
|
|
14203
14726
|
return pathEnvValue().split(delimiter2).map((entry) => entry.trim().replace(/^"|"$/g, "")).filter((entry) => {
|
|
14204
14727
|
if (!entry || entry === "." || entry.includes("\x00"))
|
|
14205
14728
|
return false;
|
|
14206
|
-
return
|
|
14729
|
+
return isAbsolute4(entry) || win32.isAbsolute(entry);
|
|
14207
14730
|
});
|
|
14208
14731
|
}
|
|
14209
14732
|
function directoryContainsLibrary(dir, libName) {
|
|
@@ -14219,10 +14742,10 @@ function directoryContainsLibrary(dir, libName) {
|
|
|
14219
14742
|
}
|
|
14220
14743
|
}
|
|
14221
14744
|
function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
|
|
14222
|
-
if (
|
|
14745
|
+
if (existsSync6(join8(ortVersionDir, libName)))
|
|
14223
14746
|
return ortVersionDir;
|
|
14224
14747
|
const libSubdir = join8(ortVersionDir, "lib");
|
|
14225
|
-
if (
|
|
14748
|
+
if (existsSync6(join8(libSubdir, libName)))
|
|
14226
14749
|
return libSubdir;
|
|
14227
14750
|
return ortVersionDir;
|
|
14228
14751
|
}
|
|
@@ -14243,7 +14766,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
14243
14766
|
if (!userProfile)
|
|
14244
14767
|
return nugetPaths;
|
|
14245
14768
|
const nugetPackageDir = join8(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
|
|
14246
|
-
if (!
|
|
14769
|
+
if (!existsSync6(nugetPackageDir))
|
|
14247
14770
|
return nugetPaths;
|
|
14248
14771
|
try {
|
|
14249
14772
|
for (const entry of readdirSync2(nugetPackageDir, { withFileTypes: true })) {
|
|
@@ -14263,7 +14786,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
14263
14786
|
const normalizeCase = process.platform === "win32" || process.platform === "darwin";
|
|
14264
14787
|
const seen = new Set;
|
|
14265
14788
|
const uniquePaths = searchPaths.filter((p) => {
|
|
14266
|
-
let key =
|
|
14789
|
+
let key = resolve5(p).replace(/[/\\]+$/, "");
|
|
14267
14790
|
if (normalizeCase)
|
|
14268
14791
|
key = key.toLowerCase();
|
|
14269
14792
|
if (seen.has(key))
|
|
@@ -14277,7 +14800,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
14277
14800
|
if (process.platform === "win32") {
|
|
14278
14801
|
if (!directoryContainsLibrary(dir, libName))
|
|
14279
14802
|
continue;
|
|
14280
|
-
} else if (!
|
|
14803
|
+
} else if (!existsSync6(libPath)) {
|
|
14281
14804
|
continue;
|
|
14282
14805
|
}
|
|
14283
14806
|
const version = detectOnnxVersion(dir, libName);
|
|
@@ -14326,7 +14849,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
14326
14849
|
await pipeline2(nodeStream, createWriteStream2(destPath), { signal: controller.signal });
|
|
14327
14850
|
} catch (err) {
|
|
14328
14851
|
try {
|
|
14329
|
-
|
|
14852
|
+
unlinkSync4(destPath);
|
|
14330
14853
|
} catch {}
|
|
14331
14854
|
throw err;
|
|
14332
14855
|
} finally {
|
|
@@ -14343,15 +14866,15 @@ function validateExtractedTree(stagingRoot) {
|
|
|
14343
14866
|
const lst = lstatSync(fullPath);
|
|
14344
14867
|
if (lst.isSymbolicLink()) {
|
|
14345
14868
|
const linkTarget = readlinkSync(fullPath);
|
|
14346
|
-
const resolvedTarget =
|
|
14869
|
+
const resolvedTarget = resolve5(dirname4(fullPath), linkTarget);
|
|
14347
14870
|
const rel2 = relative2(realRoot, resolvedTarget);
|
|
14348
|
-
if (rel2.startsWith("..") ||
|
|
14871
|
+
if (rel2.startsWith("..") || isAbsolute4(rel2) || win32.isAbsolute(rel2)) {
|
|
14349
14872
|
throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
|
|
14350
14873
|
}
|
|
14351
14874
|
continue;
|
|
14352
14875
|
}
|
|
14353
14876
|
const rel = relative2(realRoot, fullPath);
|
|
14354
|
-
if (rel.startsWith("..") ||
|
|
14877
|
+
if (rel.startsWith("..") || isAbsolute4(rel) || win32.isAbsolute(rel)) {
|
|
14355
14878
|
throw new Error(`extracted entry ${fullPath} escapes staging root`);
|
|
14356
14879
|
}
|
|
14357
14880
|
if (lst.isDirectory()) {
|
|
@@ -14387,11 +14910,11 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
14387
14910
|
await extractZipArchive(archivePath, tmpDir);
|
|
14388
14911
|
}
|
|
14389
14912
|
try {
|
|
14390
|
-
|
|
14913
|
+
unlinkSync4(archivePath);
|
|
14391
14914
|
} catch {}
|
|
14392
14915
|
validateExtractedTree(tmpDir);
|
|
14393
14916
|
const extractedDir = join8(tmpDir, info.assetName, "lib");
|
|
14394
|
-
if (!
|
|
14917
|
+
if (!existsSync6(extractedDir)) {
|
|
14395
14918
|
throw new Error(`Expected directory not found: ${extractedDir}`);
|
|
14396
14919
|
}
|
|
14397
14920
|
mkdirSync5(targetDir, { recursive: true });
|
|
@@ -14422,16 +14945,16 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
14422
14945
|
warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
|
|
14423
14946
|
}
|
|
14424
14947
|
writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
|
|
14425
|
-
|
|
14948
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
14426
14949
|
log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
|
|
14427
14950
|
return targetDir;
|
|
14428
14951
|
} catch (err) {
|
|
14429
14952
|
error(`Failed to download ONNX Runtime: ${err}`);
|
|
14430
14953
|
try {
|
|
14431
|
-
|
|
14954
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
14432
14955
|
} catch {}
|
|
14433
14956
|
try {
|
|
14434
|
-
|
|
14957
|
+
rmSync3(targetDir, { recursive: true, force: true });
|
|
14435
14958
|
} catch {}
|
|
14436
14959
|
return null;
|
|
14437
14960
|
}
|
|
@@ -14448,7 +14971,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
14448
14971
|
}
|
|
14449
14972
|
} catch (copyErr) {
|
|
14450
14973
|
if (requiredLibs.has(libFile)) {
|
|
14451
|
-
|
|
14974
|
+
rmSync3(targetDir, { recursive: true, force: true });
|
|
14452
14975
|
throw copyErr;
|
|
14453
14976
|
}
|
|
14454
14977
|
log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
|
|
@@ -14458,14 +14981,14 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
14458
14981
|
for (const link of symlinks) {
|
|
14459
14982
|
const dst = join8(targetDir, link.name);
|
|
14460
14983
|
try {
|
|
14461
|
-
|
|
14984
|
+
unlinkSync4(dst);
|
|
14462
14985
|
} catch {}
|
|
14463
14986
|
const dstForContainment = join8(targetRoot, link.name);
|
|
14464
|
-
const resolvedTarget =
|
|
14987
|
+
const resolvedTarget = resolve5(dirname4(dstForContainment), link.target);
|
|
14465
14988
|
if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
|
|
14466
14989
|
const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
|
|
14467
14990
|
if (requiredLibs.has(link.name)) {
|
|
14468
|
-
|
|
14991
|
+
rmSync3(targetDir, { recursive: true, force: true });
|
|
14469
14992
|
throw new Error(message);
|
|
14470
14993
|
}
|
|
14471
14994
|
log(`ORT extract: skipping optional symlink ${link.name}: ${message}`);
|
|
@@ -14475,15 +14998,15 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
14475
14998
|
symlinkSync(link.target, dst);
|
|
14476
14999
|
} catch (symlinkErr) {
|
|
14477
15000
|
if (requiredLibs.has(link.name)) {
|
|
14478
|
-
|
|
15001
|
+
rmSync3(targetDir, { recursive: true, force: true });
|
|
14479
15002
|
throw symlinkErr;
|
|
14480
15003
|
}
|
|
14481
15004
|
log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
|
|
14482
15005
|
}
|
|
14483
15006
|
}
|
|
14484
15007
|
const requiredPath = join8(targetDir, info.libName);
|
|
14485
|
-
if (!
|
|
14486
|
-
|
|
15008
|
+
if (!existsSync6(requiredPath)) {
|
|
15009
|
+
rmSync3(targetDir, { recursive: true, force: true });
|
|
14487
15010
|
throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
|
|
14488
15011
|
}
|
|
14489
15012
|
}
|
|
@@ -14508,7 +15031,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
14508
15031
|
...sha256 ? { sha256 } : {},
|
|
14509
15032
|
archiveSha256
|
|
14510
15033
|
};
|
|
14511
|
-
|
|
15034
|
+
writeFileSync3(join8(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
|
|
14512
15035
|
} catch (err) {
|
|
14513
15036
|
log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
|
|
14514
15037
|
}
|
|
@@ -14516,9 +15039,9 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
14516
15039
|
function readOnnxInstalledMeta(installDir) {
|
|
14517
15040
|
const path2 = join8(installDir, ONNX_INSTALLED_META_FILE);
|
|
14518
15041
|
try {
|
|
14519
|
-
if (!
|
|
15042
|
+
if (!statSync4(path2).isFile())
|
|
14520
15043
|
return null;
|
|
14521
|
-
const raw =
|
|
15044
|
+
const raw = readFileSync5(path2, "utf8");
|
|
14522
15045
|
const parsed = JSON.parse(raw);
|
|
14523
15046
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
14524
15047
|
return null;
|
|
@@ -14534,19 +15057,19 @@ function readOnnxInstalledMeta(installDir) {
|
|
|
14534
15057
|
}
|
|
14535
15058
|
function sha256File(path2) {
|
|
14536
15059
|
const hash = createHash2("sha256");
|
|
14537
|
-
hash.update(
|
|
15060
|
+
hash.update(readFileSync5(path2));
|
|
14538
15061
|
return hash.digest("hex");
|
|
14539
15062
|
}
|
|
14540
15063
|
function acquireLock(lockPath) {
|
|
14541
15064
|
const tryClaim = () => {
|
|
14542
15065
|
try {
|
|
14543
|
-
const fd =
|
|
15066
|
+
const fd = openSync4(lockPath, "wx");
|
|
14544
15067
|
try {
|
|
14545
|
-
|
|
15068
|
+
writeFileSync3(fd, `${process.pid}
|
|
14546
15069
|
${new Date().toISOString()}
|
|
14547
15070
|
`);
|
|
14548
15071
|
} finally {
|
|
14549
|
-
|
|
15072
|
+
closeSync4(fd);
|
|
14550
15073
|
}
|
|
14551
15074
|
return true;
|
|
14552
15075
|
} catch (err) {
|
|
@@ -14562,12 +15085,12 @@ ${new Date().toISOString()}
|
|
|
14562
15085
|
let owningPid = null;
|
|
14563
15086
|
let lockMtimeMs = 0;
|
|
14564
15087
|
try {
|
|
14565
|
-
const raw =
|
|
15088
|
+
const raw = readFileSync5(lockPath, "utf8");
|
|
14566
15089
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
14567
15090
|
const parsed = Number.parseInt(firstLine, 10);
|
|
14568
15091
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
14569
15092
|
owningPid = parsed;
|
|
14570
|
-
lockMtimeMs =
|
|
15093
|
+
lockMtimeMs = statSync4(lockPath).mtimeMs;
|
|
14571
15094
|
} catch {
|
|
14572
15095
|
return tryClaim();
|
|
14573
15096
|
}
|
|
@@ -14579,7 +15102,7 @@ ${new Date().toISOString()}
|
|
|
14579
15102
|
}
|
|
14580
15103
|
log(`[onnx] reclaiming install lock (owner_pid=${owningPid ?? "unknown"}, alive=${ownerAlive}, age_ms=${age})`);
|
|
14581
15104
|
try {
|
|
14582
|
-
|
|
15105
|
+
unlinkSync4(lockPath);
|
|
14583
15106
|
} catch {}
|
|
14584
15107
|
return tryClaim();
|
|
14585
15108
|
}
|
|
@@ -14587,7 +15110,7 @@ function releaseLock(lockPath) {
|
|
|
14587
15110
|
try {
|
|
14588
15111
|
let owningPid = null;
|
|
14589
15112
|
try {
|
|
14590
|
-
const raw =
|
|
15113
|
+
const raw = readFileSync5(lockPath, "utf8");
|
|
14591
15114
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
14592
15115
|
const parsed = Number.parseInt(firstLine, 10);
|
|
14593
15116
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -14599,601 +15122,93 @@ function releaseLock(lockPath) {
|
|
|
14599
15122
|
warn(`[onnx] could not read lock ${lockPath} during release: ${readErr}`);
|
|
14600
15123
|
return;
|
|
14601
15124
|
}
|
|
14602
|
-
if (owningPid !== process.pid) {
|
|
14603
|
-
log(`[onnx] not releasing lock ${lockPath}: owned by pid ${owningPid ?? "unknown"} (we are ${process.pid})`);
|
|
14604
|
-
return;
|
|
14605
|
-
}
|
|
14606
|
-
try {
|
|
14607
|
-
unlinkSync3(lockPath);
|
|
14608
|
-
} catch (unlinkErr) {
|
|
14609
|
-
const code = unlinkErr.code;
|
|
14610
|
-
if (code !== "ENOENT") {
|
|
14611
|
-
warn(`[onnx] failed to release lock ${lockPath}: ${unlinkErr}`);
|
|
14612
|
-
}
|
|
14613
|
-
}
|
|
14614
|
-
} catch (err) {
|
|
14615
|
-
warn(`[onnx] unexpected error releasing lock ${lockPath}: ${err}`);
|
|
14616
|
-
}
|
|
14617
|
-
}
|
|
14618
|
-
function tasklistPidFromCsvLine(line) {
|
|
14619
|
-
const quoted = line.match(/"([^"]*)"/g);
|
|
14620
|
-
if (quoted && quoted.length >= 2)
|
|
14621
|
-
return quoted[1].slice(1, -1);
|
|
14622
|
-
const cells = line.split(",").map((cell) => cell.trim().replace(/^"|"$/g, ""));
|
|
14623
|
-
return cells[1] ?? null;
|
|
14624
|
-
}
|
|
14625
|
-
function isWindowsProcessAlive(pid) {
|
|
14626
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
14627
|
-
return false;
|
|
14628
|
-
try {
|
|
14629
|
-
const output = execFileSync("tasklist.exe", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
|
|
14630
|
-
encoding: "utf8",
|
|
14631
|
-
timeout: 2000,
|
|
14632
|
-
windowsHide: true
|
|
14633
|
-
});
|
|
14634
|
-
const expected = String(pid);
|
|
14635
|
-
return output.split(/\r?\n/).some((line) => tasklistPidFromCsvLine(line) === expected);
|
|
14636
|
-
} catch {
|
|
14637
|
-
return false;
|
|
14638
|
-
}
|
|
14639
|
-
}
|
|
14640
|
-
function isProcessAlive(pid) {
|
|
14641
|
-
if (process.platform === "win32")
|
|
14642
|
-
return isWindowsProcessAlive(pid);
|
|
14643
|
-
try {
|
|
14644
|
-
process.kill(pid, 0);
|
|
14645
|
-
return true;
|
|
14646
|
-
} catch (err) {
|
|
14647
|
-
const code = err.code;
|
|
14648
|
-
if (code === "ESRCH")
|
|
14649
|
-
return false;
|
|
14650
|
-
return true;
|
|
14651
|
-
}
|
|
14652
|
-
}
|
|
14653
|
-
// ../aft-bridge/dist/pipe-strip.js
|
|
14654
|
-
var NOISE_FILTERS = new Set([
|
|
14655
|
-
"grep",
|
|
14656
|
-
"rg",
|
|
14657
|
-
"head",
|
|
14658
|
-
"tail",
|
|
14659
|
-
"cat",
|
|
14660
|
-
"less",
|
|
14661
|
-
"more",
|
|
14662
|
-
"sed",
|
|
14663
|
-
"awk",
|
|
14664
|
-
"cut",
|
|
14665
|
-
"sort",
|
|
14666
|
-
"uniq",
|
|
14667
|
-
"tr",
|
|
14668
|
-
"column",
|
|
14669
|
-
"fold"
|
|
14670
|
-
]);
|
|
14671
|
-
var GREP_GUARD_FLAGS = new Set([
|
|
14672
|
-
"c",
|
|
14673
|
-
"count",
|
|
14674
|
-
"q",
|
|
14675
|
-
"quiet",
|
|
14676
|
-
"o",
|
|
14677
|
-
"only-matching",
|
|
14678
|
-
"l",
|
|
14679
|
-
"files-with-matches"
|
|
14680
|
-
]);
|
|
14681
|
-
function maybeStripCompressorPipe(command, compressionEnabled) {
|
|
14682
|
-
if (!compressionEnabled)
|
|
14683
|
-
return { command, stripped: false };
|
|
14684
|
-
const chain = splitTopLevelCommandChain(command);
|
|
14685
|
-
if (chain === null)
|
|
14686
|
-
return { command, stripped: false };
|
|
14687
|
-
let stripped = false;
|
|
14688
|
-
const droppedFilterChains = [];
|
|
14689
|
-
const rebuilt = chain.map(({ segment, separator }) => {
|
|
14690
|
-
const result = stripSinglePipelineSegment(segment);
|
|
14691
|
-
if (result.stripped) {
|
|
14692
|
-
stripped = true;
|
|
14693
|
-
droppedFilterChains.push(result.filters);
|
|
14694
|
-
}
|
|
14695
|
-
return `${result.segment}${separator}`;
|
|
14696
|
-
}).join("");
|
|
14697
|
-
if (!stripped)
|
|
14698
|
-
return { command, stripped: false };
|
|
14699
|
-
return {
|
|
14700
|
-
command: rebuilt,
|
|
14701
|
-
stripped: true,
|
|
14702
|
-
note: formatStripNote(droppedFilterChains)
|
|
14703
|
-
};
|
|
14704
|
-
}
|
|
14705
|
-
function stripSinglePipelineSegment(segment) {
|
|
14706
|
-
const leading = /^\s*/.exec(segment)?.[0] ?? "";
|
|
14707
|
-
const trailing = /\s*$/.exec(segment)?.[0] ?? "";
|
|
14708
|
-
const coreStart = leading.length;
|
|
14709
|
-
const coreEnd = segment.length - trailing.length;
|
|
14710
|
-
if (coreEnd <= coreStart)
|
|
14711
|
-
return { segment, stripped: false };
|
|
14712
|
-
const core = segment.slice(coreStart, coreEnd);
|
|
14713
|
-
if (containsUnsplittableConstruct(core))
|
|
14714
|
-
return { segment, stripped: false };
|
|
14715
|
-
if (hasUnquotedBackground(core))
|
|
14716
|
-
return { segment, stripped: false };
|
|
14717
|
-
const stages = splitTopLevelPipeline(core);
|
|
14718
|
-
if (stages.length < 2)
|
|
14719
|
-
return { segment, stripped: false };
|
|
14720
|
-
const firstStage = stages[0]?.trim() ?? "";
|
|
14721
|
-
if (!isCompressorHandledRunner(firstStage))
|
|
14722
|
-
return { segment, stripped: false };
|
|
14723
|
-
const filterStages = stages.slice(1).map((stage) => stage.trim());
|
|
14724
|
-
for (const stage of filterStages) {
|
|
14725
|
-
if (!filterStageIsSafeToDrop(stage))
|
|
14726
|
-
return { segment, stripped: false };
|
|
14727
|
-
}
|
|
14728
|
-
return {
|
|
14729
|
-
segment: `${leading}${firstStage}${trailing}`,
|
|
14730
|
-
stripped: true,
|
|
14731
|
-
filters: filterStages.join(" | ")
|
|
14732
|
-
};
|
|
14733
|
-
}
|
|
14734
|
-
function formatStripNote(droppedFilterChains) {
|
|
14735
|
-
const filters = droppedFilterChains.map((filter) => `\`| ${filter}\``).join(", ");
|
|
14736
|
-
return `[AFT dropped ${filters} (compressed:false to keep)]`;
|
|
14737
|
-
}
|
|
14738
|
-
function splitTopLevelCommandChain(command) {
|
|
14739
|
-
const segments = [];
|
|
14740
|
-
let start = 0;
|
|
14741
|
-
let quote = null;
|
|
14742
|
-
let escaped = false;
|
|
14743
|
-
let inBacktick = false;
|
|
14744
|
-
let parenDepth = 0;
|
|
14745
|
-
for (let index = 0;index < command.length; index++) {
|
|
14746
|
-
const char = command[index];
|
|
14747
|
-
const next = command[index + 1];
|
|
14748
|
-
if (escaped) {
|
|
14749
|
-
escaped = false;
|
|
14750
|
-
continue;
|
|
14751
|
-
}
|
|
14752
|
-
if (inBacktick) {
|
|
14753
|
-
if (char === "\\")
|
|
14754
|
-
escaped = true;
|
|
14755
|
-
else if (char === "`")
|
|
14756
|
-
inBacktick = false;
|
|
14757
|
-
continue;
|
|
14758
|
-
}
|
|
14759
|
-
if (char === "\\" && quote !== "'") {
|
|
14760
|
-
escaped = true;
|
|
14761
|
-
continue;
|
|
14762
|
-
}
|
|
14763
|
-
if (quote) {
|
|
14764
|
-
if (char === quote)
|
|
14765
|
-
quote = null;
|
|
14766
|
-
continue;
|
|
14767
|
-
}
|
|
14768
|
-
if (char === "'" || char === '"') {
|
|
14769
|
-
quote = char;
|
|
14770
|
-
continue;
|
|
14771
|
-
}
|
|
14772
|
-
if (char === "`") {
|
|
14773
|
-
inBacktick = true;
|
|
14774
|
-
continue;
|
|
14775
|
-
}
|
|
14776
|
-
if (char === "(") {
|
|
14777
|
-
parenDepth++;
|
|
14778
|
-
continue;
|
|
14779
|
-
}
|
|
14780
|
-
if (char === ")") {
|
|
14781
|
-
if (parenDepth === 0)
|
|
14782
|
-
return null;
|
|
14783
|
-
parenDepth--;
|
|
14784
|
-
continue;
|
|
14785
|
-
}
|
|
14786
|
-
if (parenDepth > 0)
|
|
14787
|
-
continue;
|
|
14788
|
-
if (char === `
|
|
14789
|
-
` || char === "\r")
|
|
14790
|
-
return null;
|
|
14791
|
-
if (char === "#" && (index === 0 || command[index - 1] === " " || command[index - 1] === "\t"))
|
|
14792
|
-
return null;
|
|
14793
|
-
if (char === "&" && next === "&") {
|
|
14794
|
-
segments.push({ segment: command.slice(start, index), separator: "&&" });
|
|
14795
|
-
start = index + 2;
|
|
14796
|
-
index++;
|
|
14797
|
-
continue;
|
|
14798
|
-
}
|
|
14799
|
-
if (char === "|" && next === "|") {
|
|
14800
|
-
segments.push({ segment: command.slice(start, index), separator: "||" });
|
|
14801
|
-
start = index + 2;
|
|
14802
|
-
index++;
|
|
14803
|
-
continue;
|
|
14804
|
-
}
|
|
14805
|
-
if (char === ";") {
|
|
14806
|
-
segments.push({ segment: command.slice(start, index), separator: ";" });
|
|
14807
|
-
start = index + 1;
|
|
14808
|
-
}
|
|
14809
|
-
}
|
|
14810
|
-
if (escaped || quote || inBacktick || parenDepth !== 0)
|
|
14811
|
-
return null;
|
|
14812
|
-
segments.push({ segment: command.slice(start), separator: "" });
|
|
14813
|
-
return segments;
|
|
14814
|
-
}
|
|
14815
|
-
function splitTopLevelPipeline(command) {
|
|
14816
|
-
const stages = [];
|
|
14817
|
-
let start = 0;
|
|
14818
|
-
let quote = null;
|
|
14819
|
-
let escaped = false;
|
|
14820
|
-
for (let index = 0;index < command.length; index++) {
|
|
14821
|
-
const char = command[index];
|
|
14822
|
-
const next = command[index + 1];
|
|
14823
|
-
const previous = command[index - 1];
|
|
14824
|
-
if (escaped) {
|
|
14825
|
-
escaped = false;
|
|
14826
|
-
continue;
|
|
14827
|
-
}
|
|
14828
|
-
if (char === "\\" && quote !== "'") {
|
|
14829
|
-
escaped = true;
|
|
14830
|
-
continue;
|
|
14831
|
-
}
|
|
14832
|
-
if (quote) {
|
|
14833
|
-
if (char === quote)
|
|
14834
|
-
quote = null;
|
|
14835
|
-
continue;
|
|
14836
|
-
}
|
|
14837
|
-
if (char === "'" || char === '"') {
|
|
14838
|
-
quote = char;
|
|
14839
|
-
continue;
|
|
14840
|
-
}
|
|
14841
|
-
if (char === "|" && previous !== "|" && next !== "|") {
|
|
14842
|
-
stages.push(command.slice(start, index));
|
|
14843
|
-
start = index + 1;
|
|
14844
|
-
}
|
|
14845
|
-
}
|
|
14846
|
-
stages.push(command.slice(start));
|
|
14847
|
-
return stages;
|
|
14848
|
-
}
|
|
14849
|
-
function isCompressorHandledRunner(stage) {
|
|
14850
|
-
const tokens = tokenizeStage(stage);
|
|
14851
|
-
if (tokens.length === 0)
|
|
14852
|
-
return false;
|
|
14853
|
-
if (tokens.some((token) => token === "&&" || token === "||" || token.includes(";"))) {
|
|
14854
|
-
return false;
|
|
14855
|
-
}
|
|
14856
|
-
let tokenOffset = 0;
|
|
14857
|
-
while (tokenOffset < tokens.length && isEnvAssignment(tokens[tokenOffset])) {
|
|
14858
|
-
tokenOffset++;
|
|
14859
|
-
}
|
|
14860
|
-
const first = runnerName(tokens[tokenOffset]);
|
|
14861
|
-
const runnerArgs = tokens.slice(tokenOffset + 1);
|
|
14862
|
-
const second = runnerArgs[0];
|
|
14863
|
-
const third = runnerArgs[1];
|
|
14864
|
-
const rest = runnerArgs;
|
|
14865
|
-
if (!first)
|
|
14866
|
-
return false;
|
|
14867
|
-
if (first === "bun") {
|
|
14868
|
-
let args = rest;
|
|
14869
|
-
if (args[0] === "--cwd")
|
|
14870
|
-
args = args.slice(2);
|
|
14871
|
-
else if (args[0]?.startsWith("--cwd="))
|
|
14872
|
-
args = args.slice(1);
|
|
14873
|
-
const sub = args[0];
|
|
14874
|
-
const subNext = args[1];
|
|
14875
|
-
return sub === "test" || sub === "run" && isJsVerificationScript(subNext);
|
|
14876
|
-
}
|
|
14877
|
-
if (first === "npm" || first === "pnpm") {
|
|
14878
|
-
return second === "test" || second === "run" && isJsVerificationScript(third);
|
|
14879
|
-
}
|
|
14880
|
-
if (first === "yarn") {
|
|
14881
|
-
return isJsVerificationScript(second) || second === "run" && isJsVerificationScript(third);
|
|
14882
|
-
}
|
|
14883
|
-
if (first === "deno")
|
|
14884
|
-
return ["test", "lint", "check", "bench"].includes(second ?? "");
|
|
14885
|
-
if (first === "npx") {
|
|
14886
|
-
return ["tsc", "eslint", "vitest", "jest", "playwright", "biome"].includes(second ?? "");
|
|
14887
|
-
}
|
|
14888
|
-
if (first === "playwright")
|
|
14889
|
-
return second === "test";
|
|
14890
|
-
if (first === "cargo") {
|
|
14891
|
-
return ["test", "build", "check", "clippy", "nextest"].includes(second ?? "");
|
|
14892
|
-
}
|
|
14893
|
-
if (first === "go")
|
|
14894
|
-
return ["test", "build", "vet"].includes(second ?? "");
|
|
14895
|
-
if (first === "gradle" || first === "gradlew") {
|
|
14896
|
-
return hasBuildTask(rest, ["test", "check", "build", "assemble", "clean"]);
|
|
14897
|
-
}
|
|
14898
|
-
if (first === "mvn" || first === "mvnw") {
|
|
14899
|
-
return hasBuildTask(rest, ["test", "verify", "package", "install", "clean"]);
|
|
14900
|
-
}
|
|
14901
|
-
if (first === "dotnet")
|
|
14902
|
-
return ["test", "build"].includes(second ?? "");
|
|
14903
|
-
if (first === "rspec")
|
|
14904
|
-
return true;
|
|
14905
|
-
if (first === "rake") {
|
|
14906
|
-
const positionals = rest.filter((a) => !a.startsWith("-"));
|
|
14907
|
-
if (positionals.length === 0)
|
|
14908
|
-
return false;
|
|
14909
|
-
if (positionals.some((a) => a.includes("/") || a.includes(".") || a.includes("=")))
|
|
14910
|
-
return false;
|
|
14911
|
-
return positionals.some((a) => a === "test" || a === "spec");
|
|
14912
|
-
}
|
|
14913
|
-
if (first === "phpunit" || first === "pest")
|
|
14914
|
-
return true;
|
|
14915
|
-
if (first === "xcodebuild")
|
|
14916
|
-
return xcodebuildHasBuildAction(rest);
|
|
14917
|
-
if (first === "swift")
|
|
14918
|
-
return second === "test" || second === "build";
|
|
14919
|
-
if (first === "make" || first === "gmake") {
|
|
14920
|
-
return hasBuildTask(rest, ["test", "check", "lint", "clean"]);
|
|
14921
|
-
}
|
|
14922
|
-
return [
|
|
14923
|
-
"vitest",
|
|
14924
|
-
"jest",
|
|
14925
|
-
"pytest",
|
|
14926
|
-
"tsc",
|
|
14927
|
-
"eslint",
|
|
14928
|
-
"biome",
|
|
14929
|
-
"ruff",
|
|
14930
|
-
"mypy",
|
|
14931
|
-
"tox",
|
|
14932
|
-
"nox"
|
|
14933
|
-
].includes(first);
|
|
14934
|
-
}
|
|
14935
|
-
function isEnvAssignment(token) {
|
|
14936
|
-
if (!token)
|
|
14937
|
-
return false;
|
|
14938
|
-
return /^[a-zA-Z_][a-zA-Z0-9_]*=/.test(token);
|
|
14939
|
-
}
|
|
14940
|
-
function runnerName(token) {
|
|
14941
|
-
if (!token)
|
|
14942
|
-
return "";
|
|
14943
|
-
const slash = token.lastIndexOf("/");
|
|
14944
|
-
return slash === -1 ? token : token.slice(slash + 1);
|
|
14945
|
-
}
|
|
14946
|
-
function hasBuildTask(args, tasks) {
|
|
14947
|
-
const isAllowedTask = (arg) => tasks.some((task) => arg === task || arg.endsWith(`:${task}`));
|
|
14948
|
-
const isFlagOrProperty = (arg) => arg.startsWith("-") || arg.includes("=");
|
|
14949
|
-
let sawAllowed = false;
|
|
14950
|
-
for (const arg of args) {
|
|
14951
|
-
if (isFlagOrProperty(arg))
|
|
14952
|
-
continue;
|
|
14953
|
-
if (!isAllowedTask(arg))
|
|
14954
|
-
return false;
|
|
14955
|
-
sawAllowed = true;
|
|
14956
|
-
}
|
|
14957
|
-
return sawAllowed;
|
|
14958
|
-
}
|
|
14959
|
-
function isJsVerificationScript(token) {
|
|
14960
|
-
if (!token)
|
|
14961
|
-
return false;
|
|
14962
|
-
return token.startsWith("test") || token === "typecheck" || token.startsWith("typecheck:");
|
|
14963
|
-
}
|
|
14964
|
-
var XCODEBUILD_VALUE_FLAGS = new Set([
|
|
14965
|
-
"-scheme",
|
|
14966
|
-
"-target",
|
|
14967
|
-
"-project",
|
|
14968
|
-
"-workspace",
|
|
14969
|
-
"-configuration",
|
|
14970
|
-
"-sdk",
|
|
14971
|
-
"-destination",
|
|
14972
|
-
"-arch",
|
|
14973
|
-
"-derivedDataPath",
|
|
14974
|
-
"-resultBundlePath",
|
|
14975
|
-
"-xcconfig",
|
|
14976
|
-
"-toolchain"
|
|
14977
|
-
]);
|
|
14978
|
-
var XCODEBUILD_BUILD_ACTIONS = new Set([
|
|
14979
|
-
"build",
|
|
14980
|
-
"test",
|
|
14981
|
-
"build-for-testing",
|
|
14982
|
-
"test-without-building",
|
|
14983
|
-
"analyze"
|
|
14984
|
-
]);
|
|
14985
|
-
function xcodebuildHasBuildAction(args) {
|
|
14986
|
-
for (let i = 0;i < args.length; i++) {
|
|
14987
|
-
const arg = args[i];
|
|
14988
|
-
if (arg.startsWith("-")) {
|
|
14989
|
-
if (XCODEBUILD_VALUE_FLAGS.has(arg))
|
|
14990
|
-
i++;
|
|
14991
|
-
continue;
|
|
14992
|
-
}
|
|
14993
|
-
if (XCODEBUILD_BUILD_ACTIONS.has(arg))
|
|
14994
|
-
return true;
|
|
14995
|
-
}
|
|
14996
|
-
return false;
|
|
14997
|
-
}
|
|
14998
|
-
function containsUnsplittableConstruct(command) {
|
|
14999
|
-
let quote = null;
|
|
15000
|
-
let escaped = false;
|
|
15001
|
-
for (let i = 0;i < command.length; i++) {
|
|
15002
|
-
const char = command[i];
|
|
15003
|
-
if (escaped) {
|
|
15004
|
-
escaped = false;
|
|
15005
|
-
continue;
|
|
15006
|
-
}
|
|
15007
|
-
if (char === "\\" && quote !== "'") {
|
|
15008
|
-
escaped = true;
|
|
15009
|
-
continue;
|
|
15010
|
-
}
|
|
15011
|
-
if (quote === "'") {
|
|
15012
|
-
if (char === "'")
|
|
15013
|
-
quote = null;
|
|
15014
|
-
continue;
|
|
15015
|
-
}
|
|
15016
|
-
if (quote === '"') {
|
|
15017
|
-
if (char === '"')
|
|
15018
|
-
quote = null;
|
|
15019
|
-
else if (char === "`")
|
|
15020
|
-
return true;
|
|
15021
|
-
else if (char === "$" && command[i + 1] === "(")
|
|
15022
|
-
return true;
|
|
15023
|
-
continue;
|
|
15125
|
+
if (owningPid !== process.pid) {
|
|
15126
|
+
log(`[onnx] not releasing lock ${lockPath}: owned by pid ${owningPid ?? "unknown"} (we are ${process.pid})`);
|
|
15127
|
+
return;
|
|
15024
15128
|
}
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15129
|
+
try {
|
|
15130
|
+
unlinkSync4(lockPath);
|
|
15131
|
+
} catch (unlinkErr) {
|
|
15132
|
+
const code = unlinkErr.code;
|
|
15133
|
+
if (code !== "ENOENT") {
|
|
15134
|
+
warn(`[onnx] failed to release lock ${lockPath}: ${unlinkErr}`);
|
|
15135
|
+
}
|
|
15028
15136
|
}
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
if (char === "(" || char === ")")
|
|
15032
|
-
return true;
|
|
15137
|
+
} catch (err) {
|
|
15138
|
+
warn(`[onnx] unexpected error releasing lock ${lockPath}: ${err}`);
|
|
15033
15139
|
}
|
|
15034
|
-
return false;
|
|
15035
15140
|
}
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
if (/[<>]/.test(stage))
|
|
15141
|
+
function tasklistPidFromCsvLine(line) {
|
|
15142
|
+
const quoted = line.match(/"([^"]*)"/g);
|
|
15143
|
+
if (quoted && quoted.length >= 2)
|
|
15144
|
+
return quoted[1].slice(1, -1);
|
|
15145
|
+
const cells = line.split(",").map((cell) => cell.trim().replace(/^"|"$/g, ""));
|
|
15146
|
+
return cells[1] ?? null;
|
|
15147
|
+
}
|
|
15148
|
+
function isWindowsProcessAlive(pid) {
|
|
15149
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
15046
15150
|
return false;
|
|
15047
|
-
|
|
15151
|
+
try {
|
|
15152
|
+
const output = execFileSync("tasklist.exe", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
|
|
15153
|
+
encoding: "utf8",
|
|
15154
|
+
timeout: 2000,
|
|
15155
|
+
windowsHide: true
|
|
15156
|
+
});
|
|
15157
|
+
const expected = String(pid);
|
|
15158
|
+
return output.split(/\r?\n/).some((line) => tasklistPidFromCsvLine(line) === expected);
|
|
15159
|
+
} catch {
|
|
15048
15160
|
return false;
|
|
15049
|
-
const args = tokenizeStage(stage).slice(1);
|
|
15050
|
-
const hasFlag = (...names) => args.some((a) => names.some((n) => a === n || a.startsWith(`${n}=`)));
|
|
15051
|
-
if (head === "grep" || head === "rg") {
|
|
15052
|
-
if (hasIntentChangingGrepFlag(args))
|
|
15053
|
-
return false;
|
|
15054
|
-
if (countBareOperands(args) > 1)
|
|
15055
|
-
return false;
|
|
15056
|
-
return true;
|
|
15057
|
-
}
|
|
15058
|
-
if (head === "head" || head === "tail") {
|
|
15059
|
-
if (bareOperands(args).some((op) => !/^\d+$/.test(op)))
|
|
15060
|
-
return false;
|
|
15061
|
-
return true;
|
|
15062
|
-
}
|
|
15063
|
-
if (READS_FILE_OPERAND.has(head)) {
|
|
15064
|
-
if (countBareOperands(args) > 0)
|
|
15065
|
-
return false;
|
|
15066
|
-
return true;
|
|
15067
15161
|
}
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
15162
|
+
}
|
|
15163
|
+
function isProcessAlive(pid) {
|
|
15164
|
+
if (process.platform === "win32")
|
|
15165
|
+
return isWindowsProcessAlive(pid);
|
|
15166
|
+
try {
|
|
15167
|
+
process.kill(pid, 0);
|
|
15073
15168
|
return true;
|
|
15074
|
-
}
|
|
15075
|
-
|
|
15076
|
-
if (
|
|
15169
|
+
} catch (err) {
|
|
15170
|
+
const code = err.code;
|
|
15171
|
+
if (code === "ESRCH")
|
|
15077
15172
|
return false;
|
|
15078
15173
|
return true;
|
|
15079
15174
|
}
|
|
15080
|
-
if (head === "sort" && hasFlag("-o", "--output"))
|
|
15081
|
-
return false;
|
|
15082
|
-
if (bareOperands(args).some((op) => op.includes("/") || op.includes(".")))
|
|
15083
|
-
return false;
|
|
15084
|
-
return true;
|
|
15085
15175
|
}
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15176
|
+
// ../aft-bridge/dist/project-identity.js
|
|
15177
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15178
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
15179
|
+
import { resolve as resolve6 } from "node:path";
|
|
15180
|
+
function canonicalizeProjectRoot(dir) {
|
|
15181
|
+
const trimmed = dir.replace(/[/\\]+$/, "");
|
|
15182
|
+
let canonical;
|
|
15183
|
+
try {
|
|
15184
|
+
canonical = realpathSync2(trimmed);
|
|
15185
|
+
} catch {
|
|
15186
|
+
canonical = resolve6(trimmed);
|
|
15097
15187
|
}
|
|
15098
|
-
return
|
|
15188
|
+
return normalizeWindowsRoot(canonical);
|
|
15099
15189
|
}
|
|
15100
|
-
function
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
if (escaped) {
|
|
15109
|
-
escaped = false;
|
|
15110
|
-
continue;
|
|
15111
|
-
}
|
|
15112
|
-
if (char === "\\" && quote !== "'") {
|
|
15113
|
-
escaped = true;
|
|
15114
|
-
continue;
|
|
15115
|
-
}
|
|
15116
|
-
if (quote) {
|
|
15117
|
-
if (char === quote)
|
|
15118
|
-
quote = null;
|
|
15119
|
-
continue;
|
|
15120
|
-
}
|
|
15121
|
-
if (char === "'" || char === '"') {
|
|
15122
|
-
quote = char;
|
|
15123
|
-
continue;
|
|
15124
|
-
}
|
|
15125
|
-
if (char === "&") {
|
|
15126
|
-
const prev = stage[i - 1];
|
|
15127
|
-
const next = stage[i + 1];
|
|
15128
|
-
if (prev === "&" || next === "&" || prev === ">" || next === ">")
|
|
15129
|
-
continue;
|
|
15130
|
-
return true;
|
|
15131
|
-
}
|
|
15190
|
+
function normalizeWindowsRoot(p) {
|
|
15191
|
+
if (process.platform !== "win32")
|
|
15192
|
+
return p;
|
|
15193
|
+
let s = p;
|
|
15194
|
+
if (s.startsWith("\\\\?\\UNC\\")) {
|
|
15195
|
+
s = `\\\\${s.slice("\\\\?\\UNC\\".length)}`;
|
|
15196
|
+
} else if (s.startsWith("\\\\?\\")) {
|
|
15197
|
+
s = s.slice("\\\\?\\".length);
|
|
15132
15198
|
}
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
if (arg === "--")
|
|
15138
|
-
return false;
|
|
15139
|
-
if (!arg.startsWith("-") || arg === "-")
|
|
15140
|
-
continue;
|
|
15141
|
-
if (arg.startsWith("--")) {
|
|
15142
|
-
const flag = arg.slice(2).split("=", 1)[0];
|
|
15143
|
-
if (GREP_GUARD_FLAGS.has(flag))
|
|
15144
|
-
return true;
|
|
15145
|
-
continue;
|
|
15146
|
-
}
|
|
15147
|
-
for (const flag of arg.slice(1)) {
|
|
15148
|
-
if (GREP_GUARD_FLAGS.has(flag))
|
|
15149
|
-
return true;
|
|
15199
|
+
if (s.length >= 2 && s[1] === ":") {
|
|
15200
|
+
const drive = s.charCodeAt(0);
|
|
15201
|
+
if (drive >= 97 && drive <= 122) {
|
|
15202
|
+
s = s[0].toUpperCase() + s.slice(1);
|
|
15150
15203
|
}
|
|
15151
15204
|
}
|
|
15152
|
-
return
|
|
15205
|
+
return s;
|
|
15153
15206
|
}
|
|
15154
|
-
function
|
|
15155
|
-
|
|
15156
|
-
let current = "";
|
|
15157
|
-
let quote = null;
|
|
15158
|
-
let escaped = false;
|
|
15159
|
-
for (let index = 0;index < stage.length; index++) {
|
|
15160
|
-
const char = stage[index];
|
|
15161
|
-
if (escaped) {
|
|
15162
|
-
current += char;
|
|
15163
|
-
escaped = false;
|
|
15164
|
-
continue;
|
|
15165
|
-
}
|
|
15166
|
-
if (char === "\\" && quote !== "'") {
|
|
15167
|
-
escaped = true;
|
|
15168
|
-
continue;
|
|
15169
|
-
}
|
|
15170
|
-
if (quote) {
|
|
15171
|
-
if (char === quote) {
|
|
15172
|
-
quote = null;
|
|
15173
|
-
} else {
|
|
15174
|
-
current += char;
|
|
15175
|
-
}
|
|
15176
|
-
continue;
|
|
15177
|
-
}
|
|
15178
|
-
if (char === "'" || char === '"') {
|
|
15179
|
-
quote = char;
|
|
15180
|
-
continue;
|
|
15181
|
-
}
|
|
15182
|
-
if (/\s/.test(char)) {
|
|
15183
|
-
if (current.length > 0) {
|
|
15184
|
-
tokens.push(current);
|
|
15185
|
-
current = "";
|
|
15186
|
-
}
|
|
15187
|
-
continue;
|
|
15188
|
-
}
|
|
15189
|
-
current += char;
|
|
15190
|
-
}
|
|
15191
|
-
if (current.length > 0)
|
|
15192
|
-
tokens.push(current);
|
|
15193
|
-
return tokens;
|
|
15207
|
+
function projectRootKeyHash(dir) {
|
|
15208
|
+
return createHash3("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
|
|
15194
15209
|
}
|
|
15210
|
+
|
|
15195
15211
|
// ../aft-bridge/dist/pool.js
|
|
15196
|
-
import { realpathSync as realpathSync2 } from "node:fs";
|
|
15197
15212
|
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
15198
15213
|
var DEFAULT_MAX_POOL_SIZE = 8;
|
|
15199
15214
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
@@ -15364,12 +15379,7 @@ class BridgePool {
|
|
|
15364
15379
|
}
|
|
15365
15380
|
}
|
|
15366
15381
|
function normalizeKey(projectRoot) {
|
|
15367
|
-
|
|
15368
|
-
try {
|
|
15369
|
-
return realpathSync2(stripped);
|
|
15370
|
-
} catch {
|
|
15371
|
-
return stripped;
|
|
15372
|
-
}
|
|
15382
|
+
return canonicalizeProjectRoot(projectRoot);
|
|
15373
15383
|
}
|
|
15374
15384
|
// ../aft-bridge/dist/tool-format.js
|
|
15375
15385
|
function asPlainObject(value) {
|
|
@@ -15560,7 +15570,7 @@ function unwrapRustZoomBatchEnvelope(response) {
|
|
|
15560
15570
|
return { names, responses };
|
|
15561
15571
|
}
|
|
15562
15572
|
// src/bg-notifications.ts
|
|
15563
|
-
import { createHash as
|
|
15573
|
+
import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
|
|
15564
15574
|
|
|
15565
15575
|
// src/logger.ts
|
|
15566
15576
|
import * as fs from "node:fs";
|
|
@@ -15724,7 +15734,7 @@ async function resolvePromptContext(client, sessionId) {
|
|
|
15724
15734
|
|
|
15725
15735
|
// src/bg-notifications.ts
|
|
15726
15736
|
function hashReminder(text) {
|
|
15727
|
-
return
|
|
15737
|
+
return createHash4("sha256").update(text).digest("hex").slice(0, 16);
|
|
15728
15738
|
}
|
|
15729
15739
|
var CONSUMED_TASKIDS_CAP = 256;
|
|
15730
15740
|
var sessionBgStates = new Map;
|
|
@@ -16398,9 +16408,7 @@ function formatDuration(completion) {
|
|
|
16398
16408
|
}
|
|
16399
16409
|
|
|
16400
16410
|
// src/config.ts
|
|
16401
|
-
import { existsSync as
|
|
16402
|
-
import { homedir as homedir8 } from "node:os";
|
|
16403
|
-
import { join as join10 } from "node:path";
|
|
16411
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
16404
16412
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
16405
16413
|
|
|
16406
16414
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
@@ -30062,89 +30070,7 @@ var AftConfigSchema = exports_external.object({
|
|
|
30062
30070
|
auto_update: exports_external.boolean().optional(),
|
|
30063
30071
|
bridge: BridgeConfigSchema.optional()
|
|
30064
30072
|
}).strict();
|
|
30065
|
-
|
|
30066
|
-
return extension.trim().replace(/^\.+/, "");
|
|
30067
|
-
}
|
|
30068
|
-
function resolveLspConfigForConfigure(config2) {
|
|
30069
|
-
const overrides = {};
|
|
30070
|
-
const disabled = new Set(config2.lsp?.disabled ?? []);
|
|
30071
|
-
let experimentalTy = config2.experimental?.lsp_ty;
|
|
30072
|
-
switch (config2.lsp?.python ?? "auto") {
|
|
30073
|
-
case "ty":
|
|
30074
|
-
experimentalTy = true;
|
|
30075
|
-
disabled.add("python");
|
|
30076
|
-
break;
|
|
30077
|
-
case "pyright":
|
|
30078
|
-
experimentalTy = false;
|
|
30079
|
-
disabled.add("ty");
|
|
30080
|
-
break;
|
|
30081
|
-
case "auto":
|
|
30082
|
-
break;
|
|
30083
|
-
}
|
|
30084
|
-
if (experimentalTy !== undefined) {
|
|
30085
|
-
overrides.experimental_lsp_ty = experimentalTy;
|
|
30086
|
-
}
|
|
30087
|
-
const servers = Object.entries(config2.lsp?.servers ?? {}).map(([id, server]) => {
|
|
30088
|
-
const entry = {
|
|
30089
|
-
id,
|
|
30090
|
-
args: server.args,
|
|
30091
|
-
root_markers: server.root_markers,
|
|
30092
|
-
disabled: server.disabled
|
|
30093
|
-
};
|
|
30094
|
-
if (server.extensions && server.extensions.length > 0) {
|
|
30095
|
-
entry.extensions = server.extensions.map(normalizeLspExtension);
|
|
30096
|
-
}
|
|
30097
|
-
if (server.binary) {
|
|
30098
|
-
entry.binary = server.binary;
|
|
30099
|
-
}
|
|
30100
|
-
if (server.env && Object.keys(server.env).length > 0) {
|
|
30101
|
-
entry.env = server.env;
|
|
30102
|
-
}
|
|
30103
|
-
if (server.initialization_options !== undefined) {
|
|
30104
|
-
entry.initialization_options = server.initialization_options;
|
|
30105
|
-
}
|
|
30106
|
-
return entry;
|
|
30107
|
-
});
|
|
30108
|
-
if (servers.length > 0) {
|
|
30109
|
-
overrides.lsp_servers = servers;
|
|
30110
|
-
}
|
|
30111
|
-
if (disabled.size > 0) {
|
|
30112
|
-
overrides.disabled_lsp = [...disabled];
|
|
30113
|
-
}
|
|
30114
|
-
return overrides;
|
|
30115
|
-
}
|
|
30116
|
-
function resolveProjectOverridesForConfigure(config2) {
|
|
30117
|
-
const overrides = {};
|
|
30118
|
-
if (config2.format_on_edit !== undefined)
|
|
30119
|
-
overrides.format_on_edit = config2.format_on_edit;
|
|
30120
|
-
if (config2.formatter_timeout_secs !== undefined)
|
|
30121
|
-
overrides.formatter_timeout_secs = config2.formatter_timeout_secs;
|
|
30122
|
-
if (config2.validate_on_edit !== undefined)
|
|
30123
|
-
overrides.validate_on_edit = config2.validate_on_edit;
|
|
30124
|
-
if (config2.formatter !== undefined)
|
|
30125
|
-
overrides.formatter = config2.formatter;
|
|
30126
|
-
if (config2.checker !== undefined)
|
|
30127
|
-
overrides.checker = config2.checker;
|
|
30128
|
-
overrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
|
|
30129
|
-
if (config2.search_index !== undefined)
|
|
30130
|
-
overrides.search_index = config2.search_index;
|
|
30131
|
-
if (config2.semantic_search !== undefined)
|
|
30132
|
-
overrides.semantic_search = config2.semantic_search;
|
|
30133
|
-
if (config2.callgraph_store !== undefined)
|
|
30134
|
-
overrides.callgraph_store = config2.callgraph_store;
|
|
30135
|
-
if (config2.callgraph_chunk_size !== undefined)
|
|
30136
|
-
overrides.callgraph_chunk_size = config2.callgraph_chunk_size;
|
|
30137
|
-
Object.assign(overrides, resolveExperimentalConfigForConfigure(config2));
|
|
30138
|
-
Object.assign(overrides, resolveLspConfigForConfigure(config2));
|
|
30139
|
-
if (config2.semantic !== undefined)
|
|
30140
|
-
overrides.semantic = config2.semantic;
|
|
30141
|
-
if (config2.inspect !== undefined)
|
|
30142
|
-
overrides.inspect = config2.inspect;
|
|
30143
|
-
if (config2.max_callgraph_files !== undefined)
|
|
30144
|
-
overrides.max_callgraph_files = config2.max_callgraph_files;
|
|
30145
|
-
return overrides;
|
|
30146
|
-
}
|
|
30147
|
-
var FOREGROUND_WAIT_WINDOW_DEFAULT_MS = 8000;
|
|
30073
|
+
var FOREGROUND_WAIT_WINDOW_DEFAULT_MS = 15000;
|
|
30148
30074
|
var FOREGROUND_WAIT_WINDOW_MIN_MS = 5000;
|
|
30149
30075
|
function resolveBashConfig(config2) {
|
|
30150
30076
|
const top = config2.bash;
|
|
@@ -30203,23 +30129,6 @@ function resolveBashConfig(config2) {
|
|
|
30203
30129
|
background: surfaceDefaultEnabled
|
|
30204
30130
|
};
|
|
30205
30131
|
}
|
|
30206
|
-
function resolveExperimentalConfigForConfigure(config2) {
|
|
30207
|
-
const overrides = {};
|
|
30208
|
-
const bash = resolveBashConfig(config2);
|
|
30209
|
-
overrides.experimental_bash_rewrite = bash.rewrite;
|
|
30210
|
-
overrides.experimental_bash_compress = bash.compress;
|
|
30211
|
-
overrides.experimental_bash_background = bash.background;
|
|
30212
|
-
if (bash.long_running_reminder_enabled !== undefined) {
|
|
30213
|
-
overrides.bash_long_running_reminder_enabled = bash.long_running_reminder_enabled;
|
|
30214
|
-
}
|
|
30215
|
-
if (bash.long_running_reminder_interval_ms !== undefined) {
|
|
30216
|
-
overrides.bash_long_running_reminder_interval_ms = bash.long_running_reminder_interval_ms;
|
|
30217
|
-
}
|
|
30218
|
-
if (config2.experimental?.lsp_ty !== undefined) {
|
|
30219
|
-
overrides.experimental_lsp_ty = config2.experimental.lsp_ty;
|
|
30220
|
-
}
|
|
30221
|
-
return overrides;
|
|
30222
|
-
}
|
|
30223
30132
|
var CONFIG_MIGRATIONS = [
|
|
30224
30133
|
{ oldKey: "experimental_search_index", newPath: ["search_index"] },
|
|
30225
30134
|
{ oldKey: "experimental_semantic_search", newPath: ["semantic_search"] },
|
|
@@ -30329,14 +30238,14 @@ function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
|
|
|
30329
30238
|
}
|
|
30330
30239
|
return movedKeys;
|
|
30331
30240
|
}
|
|
30332
|
-
function
|
|
30333
|
-
if (!
|
|
30241
|
+
function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 }) {
|
|
30242
|
+
if (!existsSync7(configPath)) {
|
|
30334
30243
|
return { migrated: false, oldKeys: [] };
|
|
30335
30244
|
}
|
|
30336
30245
|
let tmpPath = null;
|
|
30337
30246
|
let oldKeys = [];
|
|
30338
30247
|
try {
|
|
30339
|
-
const content =
|
|
30248
|
+
const content = readFileSync6(configPath, "utf-8");
|
|
30340
30249
|
const rawConfig = import_comment_json.parse(content);
|
|
30341
30250
|
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
30342
30251
|
return { migrated: false, oldKeys: [] };
|
|
@@ -30353,14 +30262,14 @@ function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
|
|
|
30353
30262
|
`)}
|
|
30354
30263
|
${serialized}` : serialized;
|
|
30355
30264
|
tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
30356
|
-
|
|
30357
|
-
|
|
30265
|
+
writeFileSync4(tmpPath, nextContent, "utf-8");
|
|
30266
|
+
renameSync5(tmpPath, configPath);
|
|
30358
30267
|
logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
|
|
30359
30268
|
return { migrated: true, oldKeys };
|
|
30360
30269
|
} catch (err) {
|
|
30361
30270
|
if (tmpPath) {
|
|
30362
30271
|
try {
|
|
30363
|
-
|
|
30272
|
+
unlinkSync5(tmpPath);
|
|
30364
30273
|
} catch {}
|
|
30365
30274
|
}
|
|
30366
30275
|
if (isWritableMigrationError(err)) {
|
|
@@ -30371,17 +30280,6 @@ ${serialized}` : serialized;
|
|
|
30371
30280
|
return { migrated: false, oldKeys: [] };
|
|
30372
30281
|
}
|
|
30373
30282
|
}
|
|
30374
|
-
function detectConfigFile(basePath) {
|
|
30375
|
-
const jsoncPath = `${basePath}.jsonc`;
|
|
30376
|
-
const jsonPath = `${basePath}.json`;
|
|
30377
|
-
if (existsSync6(jsoncPath)) {
|
|
30378
|
-
return { format: "jsonc", path: jsoncPath };
|
|
30379
|
-
}
|
|
30380
|
-
if (existsSync6(jsonPath)) {
|
|
30381
|
-
return { format: "json", path: jsonPath };
|
|
30382
|
-
}
|
|
30383
|
-
return { format: "none", path: jsonPath };
|
|
30384
|
-
}
|
|
30385
30283
|
function parseConfigPartially(rawConfig) {
|
|
30386
30284
|
const fullResult = AftConfigSchema.safeParse(rawConfig);
|
|
30387
30285
|
if (fullResult.success) {
|
|
@@ -30421,10 +30319,10 @@ function recordConfigParseFailure(configPath, errorMessage) {
|
|
|
30421
30319
|
}
|
|
30422
30320
|
function loadConfigFromPath(configPath) {
|
|
30423
30321
|
try {
|
|
30424
|
-
if (!
|
|
30322
|
+
if (!existsSync7(configPath)) {
|
|
30425
30323
|
return null;
|
|
30426
30324
|
}
|
|
30427
|
-
const content =
|
|
30325
|
+
const content = readFileSync6(configPath, "utf-8");
|
|
30428
30326
|
const rawConfig = import_comment_json.parse(content);
|
|
30429
30327
|
migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
|
|
30430
30328
|
const cleanConfig = stripJsoncSymbols(rawConfig);
|
|
@@ -30627,27 +30525,43 @@ function resolveBridgePoolTransportOptions(config2) {
|
|
|
30627
30525
|
hangThreshold: config2.bridge?.hang_threshold ?? DEFAULT_BRIDGE_HANG_THRESHOLD
|
|
30628
30526
|
};
|
|
30629
30527
|
}
|
|
30630
|
-
function
|
|
30631
|
-
const
|
|
30632
|
-
|
|
30633
|
-
|
|
30634
|
-
|
|
30635
|
-
|
|
30636
|
-
|
|
30528
|
+
function migrateAftConfigLocations(projectDirectory, logger = { log: log2, warn: warn2 }) {
|
|
30529
|
+
const paths = resolveCortexKitConfigPaths(projectDirectory);
|
|
30530
|
+
const legacy = resolveLegacyAftConfigSources(projectDirectory);
|
|
30531
|
+
return [
|
|
30532
|
+
migrateAftConfigFile({
|
|
30533
|
+
scope: "user",
|
|
30534
|
+
targetPath: paths.userConfigPath,
|
|
30535
|
+
legacySources: legacy.user,
|
|
30536
|
+
operatingHarness: "opencode",
|
|
30537
|
+
logger
|
|
30538
|
+
}),
|
|
30539
|
+
migrateAftConfigFile({
|
|
30540
|
+
scope: "project",
|
|
30541
|
+
targetPath: paths.projectConfigPath,
|
|
30542
|
+
legacySources: legacy.project,
|
|
30543
|
+
operatingHarness: "opencode",
|
|
30544
|
+
logger
|
|
30545
|
+
})
|
|
30546
|
+
];
|
|
30547
|
+
}
|
|
30548
|
+
function resolveAftConfigPaths(projectDirectory) {
|
|
30549
|
+
const paths = resolveCortexKitConfigPaths(projectDirectory);
|
|
30550
|
+
migrateAftConfigFile2(paths.userConfigPath);
|
|
30551
|
+
migrateAftConfigFile2(paths.projectConfigPath);
|
|
30552
|
+
return paths;
|
|
30553
|
+
}
|
|
30554
|
+
function buildConfigTierConfigureParams(projectDirectory, processState = {}) {
|
|
30555
|
+
const paths = resolveAftConfigPaths(projectDirectory);
|
|
30556
|
+
return {
|
|
30557
|
+
...processState,
|
|
30558
|
+
cortexkit_user_config_path: paths.userConfigPath,
|
|
30559
|
+
config: readConfigTiers(paths)
|
|
30560
|
+
};
|
|
30637
30561
|
}
|
|
30638
30562
|
function loadAftConfig(projectDirectory) {
|
|
30639
30563
|
configLoadErrors = [];
|
|
30640
|
-
const
|
|
30641
|
-
const userBasePath = join10(configDir, "aft");
|
|
30642
|
-
migrateAftConfigFile(`${userBasePath}.jsonc`);
|
|
30643
|
-
migrateAftConfigFile(`${userBasePath}.json`);
|
|
30644
|
-
const userDetected = detectConfigFile(userBasePath);
|
|
30645
|
-
const userConfigPath = userDetected.format !== "none" ? userDetected.path : `${userBasePath}.json`;
|
|
30646
|
-
const projectBasePath = join10(projectDirectory, ".opencode", "aft");
|
|
30647
|
-
migrateAftConfigFile(`${projectBasePath}.jsonc`);
|
|
30648
|
-
migrateAftConfigFile(`${projectBasePath}.json`);
|
|
30649
|
-
const projectDetected = detectConfigFile(projectBasePath);
|
|
30650
|
-
const projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : `${projectBasePath}.json`;
|
|
30564
|
+
const { userConfigPath, projectConfigPath } = resolveAftConfigPaths(projectDirectory);
|
|
30651
30565
|
let config2 = loadConfigFromPath(userConfigPath) ?? {};
|
|
30652
30566
|
const projectConfig = loadConfigFromPath(projectConfigPath);
|
|
30653
30567
|
if (projectConfig) {
|
|
@@ -30668,9 +30582,9 @@ function loadAftConfig(projectDirectory) {
|
|
|
30668
30582
|
}
|
|
30669
30583
|
|
|
30670
30584
|
// src/notifications.ts
|
|
30671
|
-
import { existsSync as
|
|
30585
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
|
|
30672
30586
|
import { homedir as homedir9, platform } from "node:os";
|
|
30673
|
-
import { join as
|
|
30587
|
+
import { join as join10 } from "node:path";
|
|
30674
30588
|
function isTuiMode() {
|
|
30675
30589
|
return process.env.OPENCODE_CLIENT === "cli";
|
|
30676
30590
|
}
|
|
@@ -30693,25 +30607,25 @@ function getDesktopStatePath() {
|
|
|
30693
30607
|
const os3 = platform();
|
|
30694
30608
|
const home = homedir9();
|
|
30695
30609
|
if (os3 === "darwin") {
|
|
30696
|
-
return
|
|
30610
|
+
return join10(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
|
|
30697
30611
|
}
|
|
30698
30612
|
if (os3 === "linux") {
|
|
30699
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ||
|
|
30700
|
-
return
|
|
30613
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join10(home, ".config");
|
|
30614
|
+
return join10(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
|
|
30701
30615
|
}
|
|
30702
30616
|
if (os3 === "win32") {
|
|
30703
|
-
const appData = process.env.APPDATA ||
|
|
30704
|
-
return
|
|
30617
|
+
const appData = process.env.APPDATA || join10(home, "AppData", "Roaming");
|
|
30618
|
+
return join10(appData, "ai.opencode.desktop", "opencode.global.dat");
|
|
30705
30619
|
}
|
|
30706
30620
|
return null;
|
|
30707
30621
|
}
|
|
30708
30622
|
function readDesktopState() {
|
|
30709
30623
|
const statePath = getDesktopStatePath();
|
|
30710
|
-
if (!statePath || !
|
|
30624
|
+
if (!statePath || !existsSync8(statePath)) {
|
|
30711
30625
|
return { serverUrl: null };
|
|
30712
30626
|
}
|
|
30713
30627
|
try {
|
|
30714
|
-
const raw =
|
|
30628
|
+
const raw = readFileSync7(statePath, "utf-8");
|
|
30715
30629
|
const state = JSON.parse(raw);
|
|
30716
30630
|
let serverUrl = null;
|
|
30717
30631
|
const serverStr = state.server;
|
|
@@ -30972,9 +30886,13 @@ function warningTitle(warning) {
|
|
|
30972
30886
|
return "LSP binary is missing";
|
|
30973
30887
|
case "config_parse_failed":
|
|
30974
30888
|
return "Config failed to parse";
|
|
30889
|
+
case "config_key_dropped":
|
|
30890
|
+
return "Config key ignored";
|
|
30975
30891
|
}
|
|
30976
30892
|
}
|
|
30977
30893
|
function formatConfigureWarningLine(warning) {
|
|
30894
|
+
if (warning.kind === "config_key_dropped")
|
|
30895
|
+
return `• ${warning.hint}`;
|
|
30978
30896
|
const details = [];
|
|
30979
30897
|
if (warning.language)
|
|
30980
30898
|
details.push(`language: ${warning.language}`);
|
|
@@ -30990,6 +30908,8 @@ function formatConfigureWarningLine(warning) {
|
|
|
30990
30908
|
${warning.hint}`;
|
|
30991
30909
|
}
|
|
30992
30910
|
function formatConfigureWarningChat(warning) {
|
|
30911
|
+
if (warning.kind === "config_key_dropped")
|
|
30912
|
+
return `${WARNING_MARKER} ${warning.hint}`;
|
|
30993
30913
|
return `${WARNING_MARKER} ${formatConfigureWarningLine(warning).replace(/^• /, "")}`;
|
|
30994
30914
|
}
|
|
30995
30915
|
function formatConfigureWarningsBatch(warnings) {
|
|
@@ -31099,7 +31019,7 @@ function isConfigureWarning(value) {
|
|
|
31099
31019
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
31100
31020
|
return false;
|
|
31101
31021
|
const warning = value;
|
|
31102
|
-
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing" || warning.kind === "config_parse_failed") && typeof warning.hint === "string";
|
|
31022
|
+
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing" || warning.kind === "config_parse_failed" || warning.kind === "config_key_dropped") && typeof warning.hint === "string";
|
|
31103
31023
|
}
|
|
31104
31024
|
function configParseWarningsFromErrors(errors3) {
|
|
31105
31025
|
return errors3.map((entry) => ({
|
|
@@ -31127,6 +31047,20 @@ function drainPendingConfigParseWarnings(projectRoot) {
|
|
|
31127
31047
|
function coerceConfigureWarnings(warnings) {
|
|
31128
31048
|
return warnings.filter(isConfigureWarning);
|
|
31129
31049
|
}
|
|
31050
|
+
function isDroppedConfigKey(value) {
|
|
31051
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
31052
|
+
return false;
|
|
31053
|
+
const dropped = value;
|
|
31054
|
+
return typeof dropped.key === "string" && typeof dropped.tier === "string" && typeof dropped.reason === "string";
|
|
31055
|
+
}
|
|
31056
|
+
function coerceDroppedKeyWarnings(droppedKeys) {
|
|
31057
|
+
if (!Array.isArray(droppedKeys))
|
|
31058
|
+
return [];
|
|
31059
|
+
return formatDroppedKeyWarnings(droppedKeys.filter(isDroppedConfigKey)).map((hint) => ({
|
|
31060
|
+
kind: "config_key_dropped",
|
|
31061
|
+
hint
|
|
31062
|
+
}));
|
|
31063
|
+
}
|
|
31130
31064
|
function drainPendingEagerWarnings(projectRoot) {
|
|
31131
31065
|
const pending = pendingEagerWarnings.get(projectRoot) ?? [];
|
|
31132
31066
|
pendingEagerWarnings.delete(projectRoot);
|
|
@@ -31135,7 +31069,8 @@ function drainPendingEagerWarnings(projectRoot) {
|
|
|
31135
31069
|
function enqueueConfigureWarningsForSession(context) {
|
|
31136
31070
|
const validWarnings = [
|
|
31137
31071
|
...drainPendingConfigParseWarnings(context.projectRoot),
|
|
31138
|
-
...coerceConfigureWarnings(context.warnings)
|
|
31072
|
+
...coerceConfigureWarnings(context.warnings),
|
|
31073
|
+
...coerceDroppedKeyWarnings(context.configDroppedKeys)
|
|
31139
31074
|
];
|
|
31140
31075
|
if (!context.sessionId) {
|
|
31141
31076
|
if (validWarnings.length === 0)
|
|
@@ -31185,52 +31120,52 @@ async function flushConfigureWarningsOnIdle(sessionId) {
|
|
|
31185
31120
|
|
|
31186
31121
|
// src/hooks/auto-update-checker/index.ts
|
|
31187
31122
|
import {
|
|
31188
|
-
closeSync as
|
|
31189
|
-
existsSync as
|
|
31123
|
+
closeSync as closeSync5,
|
|
31124
|
+
existsSync as existsSync11,
|
|
31190
31125
|
mkdirSync as mkdirSync6,
|
|
31191
|
-
openSync as
|
|
31192
|
-
readFileSync as
|
|
31193
|
-
renameSync as
|
|
31194
|
-
rmSync as
|
|
31195
|
-
writeFileSync as
|
|
31126
|
+
openSync as openSync5,
|
|
31127
|
+
readFileSync as readFileSync10,
|
|
31128
|
+
renameSync as renameSync6,
|
|
31129
|
+
rmSync as rmSync5,
|
|
31130
|
+
writeFileSync as writeFileSync7
|
|
31196
31131
|
} from "node:fs";
|
|
31197
31132
|
import { dirname as dirname7 } from "node:path";
|
|
31198
31133
|
|
|
31199
31134
|
// src/hooks/auto-update-checker/cache.ts
|
|
31200
31135
|
import { spawn as spawn2 } from "node:child_process";
|
|
31201
|
-
import { cpSync, existsSync as
|
|
31136
|
+
import { cpSync, existsSync as existsSync10, mkdtempSync, readFileSync as readFileSync9, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
31202
31137
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
31203
|
-
import { basename as
|
|
31138
|
+
import { basename as basename3, dirname as dirname6, join as join13 } from "node:path";
|
|
31204
31139
|
var import_comment_json3 = __toESM(require_src2(), 1);
|
|
31205
31140
|
|
|
31206
31141
|
// src/hooks/auto-update-checker/checker.ts
|
|
31207
31142
|
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
31208
|
-
import { existsSync as
|
|
31143
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, statSync as statSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
31209
31144
|
import { homedir as homedir11 } from "node:os";
|
|
31210
|
-
import { dirname as dirname5, isAbsolute as
|
|
31145
|
+
import { dirname as dirname5, isAbsolute as isAbsolute5, join as join12, resolve as resolve7 } from "node:path";
|
|
31211
31146
|
import { fileURLToPath } from "node:url";
|
|
31212
31147
|
|
|
31213
31148
|
// src/hooks/auto-update-checker/constants.ts
|
|
31214
31149
|
import { homedir as homedir10, platform as platform2 } from "node:os";
|
|
31215
|
-
import { join as
|
|
31150
|
+
import { join as join11 } from "node:path";
|
|
31216
31151
|
var PACKAGE_NAME = "@cortexkit/aft-opencode";
|
|
31217
31152
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
31218
31153
|
var NPM_FETCH_TIMEOUT = 1e4;
|
|
31219
31154
|
function getOpenCodeCacheRoot() {
|
|
31220
31155
|
if (platform2() === "win32") {
|
|
31221
|
-
return
|
|
31156
|
+
return join11(process.env.LOCALAPPDATA ?? homedir10(), "opencode");
|
|
31222
31157
|
}
|
|
31223
|
-
return
|
|
31158
|
+
return join11(homedir10(), ".cache", "opencode");
|
|
31224
31159
|
}
|
|
31225
31160
|
function getOpenCodeConfigRoot() {
|
|
31226
31161
|
if (platform2() === "win32") {
|
|
31227
|
-
return
|
|
31162
|
+
return join11(process.env.APPDATA ?? join11(homedir10(), "AppData", "Roaming"), "opencode");
|
|
31228
31163
|
}
|
|
31229
|
-
return
|
|
31164
|
+
return join11(process.env.XDG_CONFIG_HOME ?? join11(homedir10(), ".config"), "opencode");
|
|
31230
31165
|
}
|
|
31231
|
-
var CACHE_DIR =
|
|
31232
|
-
var USER_OPENCODE_CONFIG =
|
|
31233
|
-
var USER_OPENCODE_CONFIG_JSONC =
|
|
31166
|
+
var CACHE_DIR = join11(getOpenCodeCacheRoot(), "packages");
|
|
31167
|
+
var USER_OPENCODE_CONFIG = join11(getOpenCodeConfigRoot(), "opencode.json");
|
|
31168
|
+
var USER_OPENCODE_CONFIG_JSONC = join11(getOpenCodeConfigRoot(), "opencode.jsonc");
|
|
31234
31169
|
|
|
31235
31170
|
// src/hooks/auto-update-checker/types.ts
|
|
31236
31171
|
var NpmPackageEnvelopeSchema = exports_external.object({
|
|
@@ -31288,8 +31223,8 @@ function extractChannel(version2) {
|
|
|
31288
31223
|
}
|
|
31289
31224
|
function getConfigPaths(directory) {
|
|
31290
31225
|
return [
|
|
31291
|
-
|
|
31292
|
-
|
|
31226
|
+
join12(directory, ".opencode", "opencode.json"),
|
|
31227
|
+
join12(directory, ".opencode", "opencode.jsonc"),
|
|
31293
31228
|
USER_OPENCODE_CONFIG,
|
|
31294
31229
|
USER_OPENCODE_CONFIG_JSONC
|
|
31295
31230
|
];
|
|
@@ -31302,26 +31237,26 @@ function resolvePathPluginSpec(spec, configPath) {
|
|
|
31302
31237
|
return spec.replace(/^file:\/\//, "");
|
|
31303
31238
|
}
|
|
31304
31239
|
}
|
|
31305
|
-
if (
|
|
31240
|
+
if (isAbsolute5(spec) || /^[A-Za-z]:[\\/]/.test(spec))
|
|
31306
31241
|
return spec;
|
|
31307
|
-
return
|
|
31242
|
+
return resolve7(dirname5(configPath), spec);
|
|
31308
31243
|
}
|
|
31309
31244
|
function getLocalDevPath(directory) {
|
|
31310
31245
|
for (const configPath of getConfigPaths(directory)) {
|
|
31311
31246
|
try {
|
|
31312
|
-
if (!
|
|
31247
|
+
if (!existsSync9(configPath))
|
|
31313
31248
|
continue;
|
|
31314
|
-
const rawConfig = parseJsonConfig(
|
|
31249
|
+
const rawConfig = parseJsonConfig(readFileSync8(configPath, "utf-8"));
|
|
31315
31250
|
const plugins = getPluginEntries(rawConfig);
|
|
31316
31251
|
for (const entry of plugins) {
|
|
31317
31252
|
if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
|
|
31318
31253
|
continue;
|
|
31319
|
-
if (entry.startsWith("file://") || entry.startsWith(".") ||
|
|
31254
|
+
if (entry.startsWith("file://") || entry.startsWith(".") || isAbsolute5(entry)) {
|
|
31320
31255
|
const localPath = resolvePathPluginSpec(entry, configPath);
|
|
31321
31256
|
const pkgPath = findPackageJsonUp(localPath);
|
|
31322
31257
|
if (!pkgPath)
|
|
31323
31258
|
continue;
|
|
31324
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
31259
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
|
|
31325
31260
|
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
31326
31261
|
return localPath;
|
|
31327
31262
|
}
|
|
@@ -31332,13 +31267,13 @@ function getLocalDevPath(directory) {
|
|
|
31332
31267
|
}
|
|
31333
31268
|
function findPackageJsonUp(startPath) {
|
|
31334
31269
|
try {
|
|
31335
|
-
const stat =
|
|
31270
|
+
const stat = statSync5(startPath);
|
|
31336
31271
|
let dir = stat.isDirectory() ? startPath : dirname5(startPath);
|
|
31337
31272
|
for (let i = 0;i < 10; i++) {
|
|
31338
|
-
const pkgPath =
|
|
31339
|
-
if (
|
|
31273
|
+
const pkgPath = join12(dir, "package.json");
|
|
31274
|
+
if (existsSync9(pkgPath)) {
|
|
31340
31275
|
try {
|
|
31341
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
31276
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
|
|
31342
31277
|
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
31343
31278
|
return pkgPath;
|
|
31344
31279
|
} catch {}
|
|
@@ -31359,7 +31294,7 @@ function getLocalDevVersion(directory) {
|
|
|
31359
31294
|
const pkgPath = findPackageJsonUp(localPath);
|
|
31360
31295
|
if (!pkgPath)
|
|
31361
31296
|
return null;
|
|
31362
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
31297
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(pkgPath, "utf-8")));
|
|
31363
31298
|
return pkg.success ? pkg.data.version ?? null : null;
|
|
31364
31299
|
} catch {
|
|
31365
31300
|
return null;
|
|
@@ -31376,9 +31311,9 @@ function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
|
|
|
31376
31311
|
function findPluginEntry(directory) {
|
|
31377
31312
|
for (const configPath of getConfigPaths(directory)) {
|
|
31378
31313
|
try {
|
|
31379
|
-
if (!
|
|
31314
|
+
if (!existsSync9(configPath))
|
|
31380
31315
|
continue;
|
|
31381
|
-
const rawConfig = parseJsonConfig(
|
|
31316
|
+
const rawConfig = parseJsonConfig(readFileSync8(configPath, "utf-8"));
|
|
31382
31317
|
const plugins = getPluginEntries(rawConfig);
|
|
31383
31318
|
for (const entry of plugins) {
|
|
31384
31319
|
if (entry === PACKAGE_NAME) {
|
|
@@ -31396,7 +31331,7 @@ function findPluginEntry(directory) {
|
|
|
31396
31331
|
}
|
|
31397
31332
|
var cachedPackageVersion = null;
|
|
31398
31333
|
function getSpecCachePackageJsonPath(spec) {
|
|
31399
|
-
return
|
|
31334
|
+
return join12(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
|
|
31400
31335
|
}
|
|
31401
31336
|
function getCachedVersion(spec) {
|
|
31402
31337
|
if (!spec && cachedPackageVersion)
|
|
@@ -31405,13 +31340,13 @@ function getCachedVersion(spec) {
|
|
|
31405
31340
|
getCurrentRuntimePackageJsonPath(),
|
|
31406
31341
|
spec ? getSpecCachePackageJsonPath(spec) : null,
|
|
31407
31342
|
getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
|
|
31408
|
-
|
|
31343
|
+
join12(homedir11(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
|
|
31409
31344
|
].filter(isString);
|
|
31410
31345
|
for (const packageJsonPath of candidates) {
|
|
31411
31346
|
try {
|
|
31412
|
-
if (!
|
|
31347
|
+
if (!existsSync9(packageJsonPath))
|
|
31413
31348
|
continue;
|
|
31414
|
-
const pkg = PackageJsonSchema.safeParse(JSON.parse(
|
|
31349
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync8(packageJsonPath, "utf-8")));
|
|
31415
31350
|
if (pkg.success && pkg.data.version) {
|
|
31416
31351
|
if (!spec)
|
|
31417
31352
|
cachedPackageVersion = pkg.data.version;
|
|
@@ -31453,17 +31388,17 @@ async function getLatestVersion(channel = "latest", options = {}) {
|
|
|
31453
31388
|
// src/hooks/auto-update-checker/cache.ts
|
|
31454
31389
|
var pendingSnapshots = new Map;
|
|
31455
31390
|
function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
|
|
31456
|
-
const packageDir =
|
|
31457
|
-
const lockfilePath =
|
|
31458
|
-
const tempDir = mkdtempSync(
|
|
31459
|
-
const stagedPackageDir =
|
|
31391
|
+
const packageDir = join13(installDir, "node_modules", packageName);
|
|
31392
|
+
const lockfilePath = join13(installDir, "package-lock.json");
|
|
31393
|
+
const tempDir = mkdtempSync(join13(tmpdir3(), "aft-auto-update-"));
|
|
31394
|
+
const stagedPackageDir = existsSync10(packageDir) ? join13(tempDir, "package") : null;
|
|
31460
31395
|
if (stagedPackageDir)
|
|
31461
31396
|
cpSync(packageDir, stagedPackageDir, { recursive: true });
|
|
31462
31397
|
return {
|
|
31463
31398
|
packageJsonPath,
|
|
31464
|
-
packageJson:
|
|
31399
|
+
packageJson: existsSync10(packageJsonPath) ? readFileSync9(packageJsonPath, "utf-8") : null,
|
|
31465
31400
|
lockfilePath,
|
|
31466
|
-
lockfile:
|
|
31401
|
+
lockfile: existsSync10(lockfilePath) ? readFileSync9(lockfilePath, "utf-8") : null,
|
|
31467
31402
|
packageDir,
|
|
31468
31403
|
stagedPackageDir,
|
|
31469
31404
|
tempDir
|
|
@@ -31472,36 +31407,36 @@ function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
|
|
|
31472
31407
|
function restoreAutoUpdateSnapshot(snapshot) {
|
|
31473
31408
|
try {
|
|
31474
31409
|
if (snapshot.packageJson === null)
|
|
31475
|
-
|
|
31410
|
+
rmSync4(snapshot.packageJsonPath, { force: true });
|
|
31476
31411
|
else
|
|
31477
|
-
|
|
31412
|
+
writeFileSync6(snapshot.packageJsonPath, snapshot.packageJson);
|
|
31478
31413
|
if (snapshot.lockfile === null)
|
|
31479
|
-
|
|
31414
|
+
rmSync4(snapshot.lockfilePath, { force: true });
|
|
31480
31415
|
else
|
|
31481
|
-
|
|
31482
|
-
|
|
31416
|
+
writeFileSync6(snapshot.lockfilePath, snapshot.lockfile);
|
|
31417
|
+
rmSync4(snapshot.packageDir, { recursive: true, force: true });
|
|
31483
31418
|
if (snapshot.stagedPackageDir) {
|
|
31484
31419
|
cpSync(snapshot.stagedPackageDir, snapshot.packageDir, { recursive: true });
|
|
31485
31420
|
}
|
|
31486
31421
|
} finally {
|
|
31487
|
-
|
|
31422
|
+
rmSync4(snapshot.tempDir, { recursive: true, force: true });
|
|
31488
31423
|
}
|
|
31489
31424
|
}
|
|
31490
31425
|
function stripPackageNameFromPath(pathValue, packageName) {
|
|
31491
31426
|
let current = pathValue;
|
|
31492
31427
|
for (const segment of [...packageName.split("/")].reverse()) {
|
|
31493
|
-
if (
|
|
31428
|
+
if (basename3(current) !== segment)
|
|
31494
31429
|
return null;
|
|
31495
31430
|
current = dirname6(current);
|
|
31496
31431
|
}
|
|
31497
31432
|
return current;
|
|
31498
31433
|
}
|
|
31499
31434
|
function removeFromPackageLock(installDir, packageName) {
|
|
31500
|
-
const lockPath =
|
|
31501
|
-
if (!
|
|
31435
|
+
const lockPath = join13(installDir, "package-lock.json");
|
|
31436
|
+
if (!existsSync10(lockPath))
|
|
31502
31437
|
return false;
|
|
31503
31438
|
try {
|
|
31504
|
-
const lock = import_comment_json3.parse(
|
|
31439
|
+
const lock = import_comment_json3.parse(readFileSync9(lockPath, "utf-8"));
|
|
31505
31440
|
let modified = false;
|
|
31506
31441
|
if (lock.packages) {
|
|
31507
31442
|
const key = `node_modules/${packageName}`;
|
|
@@ -31515,7 +31450,7 @@ function removeFromPackageLock(installDir, packageName) {
|
|
|
31515
31450
|
modified = true;
|
|
31516
31451
|
}
|
|
31517
31452
|
if (modified) {
|
|
31518
|
-
|
|
31453
|
+
writeFileSync6(lockPath, JSON.stringify(lock, null, 2));
|
|
31519
31454
|
log2(`[auto-update-checker] Removed from package-lock.json: ${packageName}`);
|
|
31520
31455
|
}
|
|
31521
31456
|
return modified;
|
|
@@ -31524,10 +31459,10 @@ function removeFromPackageLock(installDir, packageName) {
|
|
|
31524
31459
|
}
|
|
31525
31460
|
}
|
|
31526
31461
|
function ensureDependencyVersion(packageJsonPath, packageName, version2) {
|
|
31527
|
-
if (!
|
|
31462
|
+
if (!existsSync10(packageJsonPath))
|
|
31528
31463
|
return false;
|
|
31529
31464
|
try {
|
|
31530
|
-
const raw = import_comment_json3.parse(
|
|
31465
|
+
const raw = import_comment_json3.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
31531
31466
|
const pkgJson = PackageJsonSchema.safeParse(raw);
|
|
31532
31467
|
if (!pkgJson.success)
|
|
31533
31468
|
return false;
|
|
@@ -31537,7 +31472,7 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
|
|
|
31537
31472
|
return true;
|
|
31538
31473
|
dependencies[packageName] = version2;
|
|
31539
31474
|
nextPackageJson.dependencies = dependencies;
|
|
31540
|
-
|
|
31475
|
+
writeFileSync6(packageJsonPath, JSON.stringify(nextPackageJson, null, 2));
|
|
31541
31476
|
log2(`[auto-update-checker] Updated dependency in package.json: ${packageName} → ${version2}`);
|
|
31542
31477
|
return true;
|
|
31543
31478
|
} catch (err) {
|
|
@@ -31546,10 +31481,10 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
|
|
|
31546
31481
|
}
|
|
31547
31482
|
}
|
|
31548
31483
|
function removeInstalledPackage(installDir, packageName) {
|
|
31549
|
-
const packageDir =
|
|
31550
|
-
if (!
|
|
31484
|
+
const packageDir = join13(installDir, "node_modules", packageName);
|
|
31485
|
+
if (!existsSync10(packageDir))
|
|
31551
31486
|
return false;
|
|
31552
|
-
|
|
31487
|
+
rmSync4(packageDir, { recursive: true, force: true });
|
|
31553
31488
|
log2(`[auto-update-checker] Package removed: ${packageDir}`);
|
|
31554
31489
|
return true;
|
|
31555
31490
|
}
|
|
@@ -31557,16 +31492,16 @@ function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackage
|
|
|
31557
31492
|
if (runtimePackageJsonPath) {
|
|
31558
31493
|
const packageDir = dirname6(runtimePackageJsonPath);
|
|
31559
31494
|
const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
|
|
31560
|
-
if (nodeModulesDir &&
|
|
31495
|
+
if (nodeModulesDir && basename3(nodeModulesDir) === "node_modules") {
|
|
31561
31496
|
const installDir = dirname6(nodeModulesDir);
|
|
31562
|
-
const packageJsonPath =
|
|
31563
|
-
if (
|
|
31497
|
+
const packageJsonPath = join13(installDir, "package.json");
|
|
31498
|
+
if (existsSync10(packageJsonPath))
|
|
31564
31499
|
return { installDir, packageJsonPath };
|
|
31565
31500
|
}
|
|
31566
31501
|
return null;
|
|
31567
31502
|
}
|
|
31568
|
-
const legacyPackageJsonPath =
|
|
31569
|
-
if (
|
|
31503
|
+
const legacyPackageJsonPath = join13(dirname6(CACHE_DIR), "package.json");
|
|
31504
|
+
if (existsSync10(legacyPackageJsonPath)) {
|
|
31570
31505
|
return { installDir: dirname6(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
|
|
31571
31506
|
}
|
|
31572
31507
|
return null;
|
|
@@ -31652,7 +31587,7 @@ async function runNpmInstallSafe(installDir, options = {}) {
|
|
|
31652
31587
|
if (!result.ok && snapshot) {
|
|
31653
31588
|
restoreAutoUpdateSnapshot(snapshot);
|
|
31654
31589
|
} else if (snapshot) {
|
|
31655
|
-
|
|
31590
|
+
rmSync4(snapshot.tempDir, { recursive: true, force: true });
|
|
31656
31591
|
}
|
|
31657
31592
|
if (!result.ok) {
|
|
31658
31593
|
warnNpmInstallFailure(result.reason ?? "npm install failed", stderrTail);
|
|
@@ -31745,8 +31680,8 @@ function claimCheckSlot(storageDir, intervalMs) {
|
|
|
31745
31680
|
const lockPath = `${file2}.lock`;
|
|
31746
31681
|
let lockFd;
|
|
31747
31682
|
try {
|
|
31748
|
-
lockFd =
|
|
31749
|
-
|
|
31683
|
+
lockFd = openSync5(lockPath, "wx");
|
|
31684
|
+
writeFileSync7(lockFd, JSON.stringify({ pid: process.pid, startedMs: Date.now() }));
|
|
31750
31685
|
} catch (err) {
|
|
31751
31686
|
if (err.code !== "EEXIST") {
|
|
31752
31687
|
warn2(`[auto-update-checker] Could not acquire update lock: ${String(err)}`);
|
|
@@ -31756,9 +31691,9 @@ function claimCheckSlot(storageDir, intervalMs) {
|
|
|
31756
31691
|
const lock = {
|
|
31757
31692
|
release: () => {
|
|
31758
31693
|
try {
|
|
31759
|
-
|
|
31694
|
+
closeSync5(lockFd);
|
|
31760
31695
|
} catch {}
|
|
31761
|
-
|
|
31696
|
+
rmSync5(lockPath, { force: true });
|
|
31762
31697
|
}
|
|
31763
31698
|
};
|
|
31764
31699
|
try {
|
|
@@ -31778,10 +31713,10 @@ function claimCheckSlot(storageDir, intervalMs) {
|
|
|
31778
31713
|
}
|
|
31779
31714
|
}
|
|
31780
31715
|
function hasRecentCheckTimestamp(file2, intervalMs) {
|
|
31781
|
-
if (!
|
|
31716
|
+
if (!existsSync11(file2))
|
|
31782
31717
|
return false;
|
|
31783
31718
|
try {
|
|
31784
|
-
const raw = JSON.parse(
|
|
31719
|
+
const raw = JSON.parse(readFileSync10(file2, "utf-8"));
|
|
31785
31720
|
const last = typeof raw.lastCheckedMs === "number" ? raw.lastCheckedMs : 0;
|
|
31786
31721
|
return Number.isFinite(last) && Date.now() - last < intervalMs;
|
|
31787
31722
|
} catch {
|
|
@@ -31790,8 +31725,8 @@ function hasRecentCheckTimestamp(file2, intervalMs) {
|
|
|
31790
31725
|
}
|
|
31791
31726
|
function writeCheckTimestamp(file2) {
|
|
31792
31727
|
const tmp = `${file2}.tmp.${process.pid}`;
|
|
31793
|
-
|
|
31794
|
-
|
|
31728
|
+
writeFileSync7(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
|
|
31729
|
+
renameSync6(tmp, file2);
|
|
31795
31730
|
}
|
|
31796
31731
|
async function runStartupCheck(ctx, options) {
|
|
31797
31732
|
if (options.signal.aborted)
|
|
@@ -31885,59 +31820,59 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
|
|
|
31885
31820
|
|
|
31886
31821
|
// src/lsp-auto-install.ts
|
|
31887
31822
|
import { spawn as spawn3 } from "node:child_process";
|
|
31888
|
-
import { createHash as
|
|
31823
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
31889
31824
|
import {
|
|
31890
31825
|
createReadStream,
|
|
31891
|
-
existsSync as
|
|
31826
|
+
existsSync as existsSync13,
|
|
31892
31827
|
mkdirSync as mkdirSync8,
|
|
31893
|
-
readFileSync as
|
|
31894
|
-
renameSync as
|
|
31895
|
-
rmSync as
|
|
31896
|
-
statSync as
|
|
31897
|
-
writeFileSync as
|
|
31828
|
+
readFileSync as readFileSync13,
|
|
31829
|
+
renameSync as renameSync7,
|
|
31830
|
+
rmSync as rmSync6,
|
|
31831
|
+
statSync as statSync7,
|
|
31832
|
+
writeFileSync as writeFileSync9
|
|
31898
31833
|
} from "node:fs";
|
|
31899
|
-
import { join as
|
|
31834
|
+
import { join as join16 } from "node:path";
|
|
31900
31835
|
|
|
31901
31836
|
// src/lsp-cache.ts
|
|
31902
31837
|
import {
|
|
31903
|
-
closeSync as
|
|
31838
|
+
closeSync as closeSync6,
|
|
31904
31839
|
mkdirSync as mkdirSync7,
|
|
31905
|
-
openSync as
|
|
31906
|
-
readFileSync as
|
|
31907
|
-
statSync as
|
|
31908
|
-
unlinkSync as
|
|
31909
|
-
writeFileSync as
|
|
31840
|
+
openSync as openSync6,
|
|
31841
|
+
readFileSync as readFileSync11,
|
|
31842
|
+
statSync as statSync6,
|
|
31843
|
+
unlinkSync as unlinkSync6,
|
|
31844
|
+
writeFileSync as writeFileSync8
|
|
31910
31845
|
} from "node:fs";
|
|
31911
31846
|
import { homedir as homedir12 } from "node:os";
|
|
31912
|
-
import { join as
|
|
31847
|
+
import { join as join14 } from "node:path";
|
|
31913
31848
|
function aftCacheBase() {
|
|
31914
31849
|
const override = process.env.AFT_CACHE_DIR;
|
|
31915
31850
|
if (override && override.length > 0)
|
|
31916
31851
|
return override;
|
|
31917
31852
|
if (process.platform === "win32") {
|
|
31918
31853
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
31919
|
-
const base2 = localAppData ||
|
|
31920
|
-
return
|
|
31854
|
+
const base2 = localAppData || join14(homedir12(), "AppData", "Local");
|
|
31855
|
+
return join14(base2, "aft");
|
|
31921
31856
|
}
|
|
31922
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
31923
|
-
return
|
|
31857
|
+
const base = process.env.XDG_CACHE_HOME || join14(homedir12(), ".cache");
|
|
31858
|
+
return join14(base, "aft");
|
|
31924
31859
|
}
|
|
31925
31860
|
function lspCacheRoot() {
|
|
31926
|
-
return
|
|
31861
|
+
return join14(aftCacheBase(), "lsp-packages");
|
|
31927
31862
|
}
|
|
31928
31863
|
function lspPackageDir(npmPackage) {
|
|
31929
|
-
return
|
|
31864
|
+
return join14(lspCacheRoot(), encodeURIComponent(npmPackage));
|
|
31930
31865
|
}
|
|
31931
31866
|
function lspBinaryPath(npmPackage, binary) {
|
|
31932
|
-
return
|
|
31867
|
+
return join14(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
|
|
31933
31868
|
}
|
|
31934
31869
|
function lspBinDir(npmPackage) {
|
|
31935
|
-
return
|
|
31870
|
+
return join14(lspPackageDir(npmPackage), "node_modules", ".bin");
|
|
31936
31871
|
}
|
|
31937
31872
|
function isInstalled(npmPackage, binary) {
|
|
31938
31873
|
for (const candidate of lspBinaryCandidates(binary)) {
|
|
31939
31874
|
try {
|
|
31940
|
-
if (
|
|
31875
|
+
if (statSync6(join14(lspBinDir(npmPackage), candidate)).isFile())
|
|
31941
31876
|
return true;
|
|
31942
31877
|
} catch {}
|
|
31943
31878
|
}
|
|
@@ -31957,17 +31892,17 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
|
31957
31892
|
installedAt: new Date().toISOString(),
|
|
31958
31893
|
...sha256 ? { sha256 } : {}
|
|
31959
31894
|
};
|
|
31960
|
-
|
|
31895
|
+
writeFileSync8(join14(installDir, INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
|
|
31961
31896
|
} catch (err) {
|
|
31962
31897
|
log2(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
|
|
31963
31898
|
}
|
|
31964
31899
|
}
|
|
31965
31900
|
function readInstalledMetaIn(installDir) {
|
|
31966
|
-
const path3 =
|
|
31901
|
+
const path3 = join14(installDir, INSTALLED_META_FILE);
|
|
31967
31902
|
try {
|
|
31968
|
-
if (!
|
|
31903
|
+
if (!statSync6(path3).isFile())
|
|
31969
31904
|
return null;
|
|
31970
|
-
const raw =
|
|
31905
|
+
const raw = readFileSync11(path3, "utf8");
|
|
31971
31906
|
const parsed = JSON.parse(raw);
|
|
31972
31907
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
31973
31908
|
return null;
|
|
@@ -31987,7 +31922,7 @@ function readInstalledMeta(packageKey) {
|
|
|
31987
31922
|
return readInstalledMetaIn(lspPackageDir(packageKey));
|
|
31988
31923
|
}
|
|
31989
31924
|
function lockPath(npmPackage) {
|
|
31990
|
-
return
|
|
31925
|
+
return join14(lspPackageDir(npmPackage), ".aft-installing");
|
|
31991
31926
|
}
|
|
31992
31927
|
var STALE_LOCK_MS2 = 30 * 60 * 1000;
|
|
31993
31928
|
function acquireInstallLock(lockKey) {
|
|
@@ -31995,13 +31930,13 @@ function acquireInstallLock(lockKey) {
|
|
|
31995
31930
|
const lock = lockPath(lockKey);
|
|
31996
31931
|
const tryClaim = () => {
|
|
31997
31932
|
try {
|
|
31998
|
-
const fd =
|
|
31933
|
+
const fd = openSync6(lock, "wx");
|
|
31999
31934
|
try {
|
|
32000
|
-
|
|
31935
|
+
writeFileSync8(fd, `${process.pid}
|
|
32001
31936
|
${new Date().toISOString()}
|
|
32002
31937
|
`);
|
|
32003
31938
|
} finally {
|
|
32004
|
-
|
|
31939
|
+
closeSync6(fd);
|
|
32005
31940
|
}
|
|
32006
31941
|
return true;
|
|
32007
31942
|
} catch (err) {
|
|
@@ -32017,12 +31952,12 @@ ${new Date().toISOString()}
|
|
|
32017
31952
|
let owningPid = null;
|
|
32018
31953
|
let lockMtimeMs = 0;
|
|
32019
31954
|
try {
|
|
32020
|
-
const raw =
|
|
31955
|
+
const raw = readFileSync11(lock, "utf8");
|
|
32021
31956
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
32022
31957
|
const parsed = Number.parseInt(firstLine, 10);
|
|
32023
31958
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
32024
31959
|
owningPid = parsed;
|
|
32025
|
-
lockMtimeMs =
|
|
31960
|
+
lockMtimeMs = statSync6(lock).mtimeMs;
|
|
32026
31961
|
} catch {
|
|
32027
31962
|
return tryClaim();
|
|
32028
31963
|
}
|
|
@@ -32035,7 +31970,7 @@ ${new Date().toISOString()}
|
|
|
32035
31970
|
}
|
|
32036
31971
|
log2(`[lsp] reclaiming install lock for ${lockKey} (owner_pid=${owningPid ?? "unknown"}, alive=${ownerAlive}, age_ms=${age})`);
|
|
32037
31972
|
try {
|
|
32038
|
-
|
|
31973
|
+
unlinkSync6(lock);
|
|
32039
31974
|
} catch {}
|
|
32040
31975
|
return tryClaim();
|
|
32041
31976
|
}
|
|
@@ -32055,7 +31990,7 @@ function releaseInstallLock(lockKey) {
|
|
|
32055
31990
|
try {
|
|
32056
31991
|
let owningPid = null;
|
|
32057
31992
|
try {
|
|
32058
|
-
const raw =
|
|
31993
|
+
const raw = readFileSync11(lock, "utf8");
|
|
32059
31994
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
32060
31995
|
const parsed = Number.parseInt(firstLine, 10);
|
|
32061
31996
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -32072,7 +32007,7 @@ function releaseInstallLock(lockKey) {
|
|
|
32072
32007
|
return;
|
|
32073
32008
|
}
|
|
32074
32009
|
try {
|
|
32075
|
-
|
|
32010
|
+
unlinkSync6(lock);
|
|
32076
32011
|
} catch (unlinkErr) {
|
|
32077
32012
|
const code = unlinkErr.code;
|
|
32078
32013
|
if (code !== "ENOENT") {
|
|
@@ -32094,9 +32029,9 @@ async function withInstallLock(lockKey, task) {
|
|
|
32094
32029
|
}
|
|
32095
32030
|
var VERSION_CHECK_FILE = ".aft-version-check";
|
|
32096
32031
|
function readVersionCheck(npmPackage) {
|
|
32097
|
-
const file2 =
|
|
32032
|
+
const file2 = join14(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
32098
32033
|
try {
|
|
32099
|
-
const raw =
|
|
32034
|
+
const raw = readFileSync11(file2, "utf8");
|
|
32100
32035
|
const parsed = JSON.parse(raw);
|
|
32101
32036
|
if (typeof parsed.last_checked === "string") {
|
|
32102
32037
|
return {
|
|
@@ -32111,12 +32046,12 @@ function readVersionCheck(npmPackage) {
|
|
|
32111
32046
|
}
|
|
32112
32047
|
function writeVersionCheck(npmPackage, latest) {
|
|
32113
32048
|
mkdirSync7(lspPackageDir(npmPackage), { recursive: true });
|
|
32114
|
-
const file2 =
|
|
32049
|
+
const file2 = join14(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
32115
32050
|
const record2 = {
|
|
32116
32051
|
last_checked: new Date().toISOString(),
|
|
32117
32052
|
latest_eligible: latest
|
|
32118
32053
|
};
|
|
32119
|
-
|
|
32054
|
+
writeFileSync8(file2, JSON.stringify(record2, null, 2));
|
|
32120
32055
|
}
|
|
32121
32056
|
function shouldRecheckVersion(record2, weeklyCheckIntervalMs = 7 * 24 * 60 * 60 * 1000) {
|
|
32122
32057
|
if (!record2)
|
|
@@ -32273,8 +32208,8 @@ var NPM_LSP_TABLE = [
|
|
|
32273
32208
|
];
|
|
32274
32209
|
|
|
32275
32210
|
// src/lsp-project-relevance.ts
|
|
32276
|
-
import { existsSync as
|
|
32277
|
-
import { join as
|
|
32211
|
+
import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync12 } from "node:fs";
|
|
32212
|
+
import { join as join15 } from "node:path";
|
|
32278
32213
|
var MAX_WALK_DIRS = 200;
|
|
32279
32214
|
var MAX_WALK_DEPTH = 4;
|
|
32280
32215
|
var NOISE_DIRS = new Set([
|
|
@@ -32291,7 +32226,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
|
|
|
32291
32226
|
if (!rootMarkers)
|
|
32292
32227
|
return false;
|
|
32293
32228
|
for (const marker of rootMarkers) {
|
|
32294
|
-
if (
|
|
32229
|
+
if (existsSync12(join15(projectRoot, marker)))
|
|
32295
32230
|
return true;
|
|
32296
32231
|
}
|
|
32297
32232
|
return false;
|
|
@@ -32315,7 +32250,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
|
|
|
32315
32250
|
}
|
|
32316
32251
|
function readPackageJson(projectRoot) {
|
|
32317
32252
|
try {
|
|
32318
|
-
const raw =
|
|
32253
|
+
const raw = readFileSync12(join15(projectRoot, "package.json"), "utf8");
|
|
32319
32254
|
const parsed = JSON.parse(raw);
|
|
32320
32255
|
if (typeof parsed !== "object" || parsed === null)
|
|
32321
32256
|
return null;
|
|
@@ -32345,7 +32280,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
|
|
|
32345
32280
|
for (const entry of entries) {
|
|
32346
32281
|
if (entry.isDirectory()) {
|
|
32347
32282
|
if (current.depth < MAX_WALK_DEPTH && !NOISE_DIRS.has(entry.name.toLowerCase())) {
|
|
32348
|
-
queue.push({ dir:
|
|
32283
|
+
queue.push({ dir: join15(current.dir, entry.name), depth: current.depth + 1 });
|
|
32349
32284
|
}
|
|
32350
32285
|
continue;
|
|
32351
32286
|
}
|
|
@@ -32472,9 +32407,9 @@ async function resolveTargetVersion(spec, config2, fetchImpl = fetch) {
|
|
|
32472
32407
|
}
|
|
32473
32408
|
function ensureInstallAnchor(cwd) {
|
|
32474
32409
|
try {
|
|
32475
|
-
const stub =
|
|
32476
|
-
if (!
|
|
32477
|
-
|
|
32410
|
+
const stub = join16(cwd, "package.json");
|
|
32411
|
+
if (!existsSync13(stub)) {
|
|
32412
|
+
writeFileSync9(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
|
|
32478
32413
|
`);
|
|
32479
32414
|
}
|
|
32480
32415
|
} catch (err) {
|
|
@@ -32482,18 +32417,18 @@ function ensureInstallAnchor(cwd) {
|
|
|
32482
32417
|
}
|
|
32483
32418
|
}
|
|
32484
32419
|
function runInstall(spec, version2, cwd, signal) {
|
|
32485
|
-
return new Promise((
|
|
32420
|
+
return new Promise((resolve8) => {
|
|
32486
32421
|
const target = `${spec.npm}@${version2}`;
|
|
32487
32422
|
log2(`[lsp] installing ${target} to ${cwd}`);
|
|
32488
32423
|
if (signal?.aborted) {
|
|
32489
32424
|
warn2(`[lsp] install ${target} aborted before spawn`);
|
|
32490
|
-
|
|
32425
|
+
resolve8(false);
|
|
32491
32426
|
return;
|
|
32492
32427
|
}
|
|
32493
32428
|
const npm = resolveNpm();
|
|
32494
32429
|
if (!npm) {
|
|
32495
32430
|
warn2(`[lsp] npm not found on PATH or known locations; cannot install ${target}`);
|
|
32496
|
-
|
|
32431
|
+
resolve8(false);
|
|
32497
32432
|
return;
|
|
32498
32433
|
}
|
|
32499
32434
|
ensureInstallAnchor(cwd);
|
|
@@ -32516,7 +32451,7 @@ function runInstall(spec, version2, cwd, signal) {
|
|
|
32516
32451
|
return;
|
|
32517
32452
|
settled = true;
|
|
32518
32453
|
cleanup();
|
|
32519
|
-
|
|
32454
|
+
resolve8(ok);
|
|
32520
32455
|
};
|
|
32521
32456
|
const onAbort = () => {
|
|
32522
32457
|
warn2(`[lsp] install ${target} aborted during shutdown`);
|
|
@@ -32617,7 +32552,7 @@ function cachedPackageDir(npmPackage) {
|
|
|
32617
32552
|
return lspBinDir(npmPackage).replace(/[\\/]node_modules[\\/]\.bin[\\/]?$/, "");
|
|
32618
32553
|
}
|
|
32619
32554
|
function hashInstalledBinary(spec) {
|
|
32620
|
-
return new Promise((
|
|
32555
|
+
return new Promise((resolve8, reject) => {
|
|
32621
32556
|
const candidates = process.platform === "win32" ? [
|
|
32622
32557
|
lspBinaryPath(spec.npm, spec.binary),
|
|
32623
32558
|
lspBinaryPath(spec.npm, `${spec.binary}.cmd`),
|
|
@@ -32627,7 +32562,7 @@ function hashInstalledBinary(spec) {
|
|
|
32627
32562
|
let pathToHash = null;
|
|
32628
32563
|
for (const p of candidates) {
|
|
32629
32564
|
try {
|
|
32630
|
-
if (
|
|
32565
|
+
if (statSync7(p).isFile()) {
|
|
32631
32566
|
pathToHash = p;
|
|
32632
32567
|
break;
|
|
32633
32568
|
}
|
|
@@ -32637,11 +32572,11 @@ function hashInstalledBinary(spec) {
|
|
|
32637
32572
|
reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
|
|
32638
32573
|
return;
|
|
32639
32574
|
}
|
|
32640
|
-
const hash2 =
|
|
32575
|
+
const hash2 = createHash5("sha256");
|
|
32641
32576
|
const stream = createReadStream(pathToHash);
|
|
32642
32577
|
stream.on("error", reject);
|
|
32643
32578
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
32644
|
-
stream.on("end", () =>
|
|
32579
|
+
stream.on("end", () => resolve8(hash2.digest("hex")));
|
|
32645
32580
|
});
|
|
32646
32581
|
}
|
|
32647
32582
|
function installedBinaryPath(spec) {
|
|
@@ -32653,23 +32588,23 @@ function installedBinaryPath(spec) {
|
|
|
32653
32588
|
] : [lspBinaryPath(spec.npm, spec.binary)];
|
|
32654
32589
|
for (const candidate of candidates) {
|
|
32655
32590
|
try {
|
|
32656
|
-
if (
|
|
32591
|
+
if (statSync7(candidate).isFile())
|
|
32657
32592
|
return candidate;
|
|
32658
32593
|
} catch {}
|
|
32659
32594
|
}
|
|
32660
32595
|
return null;
|
|
32661
32596
|
}
|
|
32662
32597
|
function sha256OfFileSync(path3) {
|
|
32663
|
-
return
|
|
32598
|
+
return createHash5("sha256").update(readFileSync13(path3)).digest("hex");
|
|
32664
32599
|
}
|
|
32665
32600
|
function quarantineCachedNpmInstall(spec, reason) {
|
|
32666
32601
|
const packageDir = lspPackageDir(spec.npm);
|
|
32667
|
-
const dest =
|
|
32602
|
+
const dest = join16(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
|
|
32668
32603
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
32669
32604
|
try {
|
|
32670
|
-
mkdirSync8(
|
|
32671
|
-
|
|
32672
|
-
|
|
32605
|
+
mkdirSync8(join16(dest, ".."), { recursive: true });
|
|
32606
|
+
rmSync6(dest, { recursive: true, force: true });
|
|
32607
|
+
renameSync7(packageDir, dest);
|
|
32673
32608
|
} catch (err) {
|
|
32674
32609
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
|
|
32675
32610
|
}
|
|
@@ -32745,25 +32680,25 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
|
|
|
32745
32680
|
|
|
32746
32681
|
// src/lsp-github-install.ts
|
|
32747
32682
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
32748
|
-
import { createHash as
|
|
32683
|
+
import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
32749
32684
|
import {
|
|
32750
32685
|
copyFileSync as copyFileSync4,
|
|
32751
32686
|
createReadStream as createReadStream2,
|
|
32752
32687
|
createWriteStream as createWriteStream3,
|
|
32753
|
-
existsSync as
|
|
32688
|
+
existsSync as existsSync14,
|
|
32754
32689
|
lstatSync as lstatSync2,
|
|
32755
32690
|
mkdirSync as mkdirSync9,
|
|
32756
32691
|
readdirSync as readdirSync4,
|
|
32757
|
-
readFileSync as
|
|
32692
|
+
readFileSync as readFileSync14,
|
|
32758
32693
|
readlinkSync as readlinkSync2,
|
|
32759
32694
|
realpathSync as realpathSync3,
|
|
32760
|
-
renameSync as
|
|
32761
|
-
rmSync as
|
|
32762
|
-
statSync as
|
|
32763
|
-
unlinkSync as
|
|
32764
|
-
writeFileSync as
|
|
32695
|
+
renameSync as renameSync8,
|
|
32696
|
+
rmSync as rmSync7,
|
|
32697
|
+
statSync as statSync8,
|
|
32698
|
+
unlinkSync as unlinkSync7,
|
|
32699
|
+
writeFileSync as writeFileSync10
|
|
32765
32700
|
} from "node:fs";
|
|
32766
|
-
import { dirname as dirname8, join as
|
|
32701
|
+
import { dirname as dirname8, join as join17, relative as relative3, resolve as resolve8 } from "node:path";
|
|
32767
32702
|
import { Readable as Readable3 } from "node:stream";
|
|
32768
32703
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
32769
32704
|
|
|
@@ -32853,26 +32788,26 @@ function detectHostPlatform() {
|
|
|
32853
32788
|
|
|
32854
32789
|
// src/lsp-github-install.ts
|
|
32855
32790
|
function ghCacheRoot() {
|
|
32856
|
-
return
|
|
32791
|
+
return join17(aftCacheBase(), "lsp-binaries");
|
|
32857
32792
|
}
|
|
32858
32793
|
function ghPackageDir(spec) {
|
|
32859
|
-
return
|
|
32794
|
+
return join17(ghCacheRoot(), spec.id);
|
|
32860
32795
|
}
|
|
32861
32796
|
function ghBinDir(spec) {
|
|
32862
|
-
return
|
|
32797
|
+
return join17(ghPackageDir(spec), "bin");
|
|
32863
32798
|
}
|
|
32864
32799
|
function ghExtractDir(spec) {
|
|
32865
|
-
return
|
|
32800
|
+
return join17(ghPackageDir(spec), "extracted");
|
|
32866
32801
|
}
|
|
32867
32802
|
var INSTALLED_META_FILE2 = ".aft-installed";
|
|
32868
32803
|
function ghBinaryPath(spec, platform3) {
|
|
32869
32804
|
const ext = platform3 === "win32" ? ".exe" : "";
|
|
32870
|
-
return
|
|
32805
|
+
return join17(ghBinDir(spec), `${spec.binary}${ext}`);
|
|
32871
32806
|
}
|
|
32872
32807
|
function isGithubInstalled(spec, platform3) {
|
|
32873
32808
|
for (const candidate of ghBinaryCandidates(spec, platform3)) {
|
|
32874
32809
|
try {
|
|
32875
|
-
if (
|
|
32810
|
+
if (statSync8(join17(ghBinDir(spec), candidate)).isFile())
|
|
32876
32811
|
return true;
|
|
32877
32812
|
} catch {}
|
|
32878
32813
|
}
|
|
@@ -32885,10 +32820,10 @@ function ghBinaryCandidates(spec, platform3) {
|
|
|
32885
32820
|
}
|
|
32886
32821
|
function readGithubInstalledMetaIn(installDir) {
|
|
32887
32822
|
try {
|
|
32888
|
-
const path3 =
|
|
32889
|
-
if (!
|
|
32823
|
+
const path3 = join17(installDir, INSTALLED_META_FILE2);
|
|
32824
|
+
if (!statSync8(path3).isFile())
|
|
32890
32825
|
return null;
|
|
32891
|
-
const parsed = JSON.parse(
|
|
32826
|
+
const parsed = JSON.parse(readFileSync14(path3, "utf8"));
|
|
32892
32827
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
32893
32828
|
return null;
|
|
32894
32829
|
return {
|
|
@@ -32912,7 +32847,7 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
|
|
|
32912
32847
|
binarySha256,
|
|
32913
32848
|
...archiveSha256 ? { archiveSha256 } : {}
|
|
32914
32849
|
};
|
|
32915
|
-
|
|
32850
|
+
writeFileSync10(join17(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
|
|
32916
32851
|
} catch (err) {
|
|
32917
32852
|
warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
|
|
32918
32853
|
}
|
|
@@ -32920,16 +32855,16 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
|
|
|
32920
32855
|
var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
|
|
32921
32856
|
var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
|
|
32922
32857
|
function sha256OfFile(path3) {
|
|
32923
|
-
return new Promise((
|
|
32924
|
-
const hash2 =
|
|
32858
|
+
return new Promise((resolve9, reject) => {
|
|
32859
|
+
const hash2 = createHash6("sha256");
|
|
32925
32860
|
const stream = createReadStream2(path3);
|
|
32926
32861
|
stream.on("error", reject);
|
|
32927
32862
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
32928
|
-
stream.on("end", () =>
|
|
32863
|
+
stream.on("end", () => resolve9(hash2.digest("hex")));
|
|
32929
32864
|
});
|
|
32930
32865
|
}
|
|
32931
32866
|
function sha256OfFileSync2(path3) {
|
|
32932
|
-
return
|
|
32867
|
+
return createHash6("sha256").update(readFileSync14(path3)).digest("hex");
|
|
32933
32868
|
}
|
|
32934
32869
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
|
|
32935
32870
|
const candidates = [];
|
|
@@ -33122,7 +33057,7 @@ async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
|
33122
33057
|
await pipeline3(nodeStream, createWriteStream3(destPath), { signal: timeout.signal });
|
|
33123
33058
|
} catch (err) {
|
|
33124
33059
|
try {
|
|
33125
|
-
|
|
33060
|
+
unlinkSync7(destPath);
|
|
33126
33061
|
} catch {}
|
|
33127
33062
|
throw err;
|
|
33128
33063
|
} finally {
|
|
@@ -33161,7 +33096,7 @@ function validateExtraction(stagingRoot) {
|
|
|
33161
33096
|
throw new Error(`failed to read staging dir ${dir}: ${err}`);
|
|
33162
33097
|
}
|
|
33163
33098
|
for (const entry of entries) {
|
|
33164
|
-
const full =
|
|
33099
|
+
const full = join17(dir, entry);
|
|
33165
33100
|
let lst;
|
|
33166
33101
|
try {
|
|
33167
33102
|
lst = lstatSync2(full);
|
|
@@ -33182,7 +33117,7 @@ function validateExtraction(stagingRoot) {
|
|
|
33182
33117
|
throw new Error(`failed to realpath ${full}: ${err}`);
|
|
33183
33118
|
}
|
|
33184
33119
|
const rel = relative3(realStagingRoot, realFull);
|
|
33185
|
-
if (rel.startsWith("..") ||
|
|
33120
|
+
if (rel.startsWith("..") || resolve8(realStagingRoot, rel) !== realFull) {
|
|
33186
33121
|
throw new Error(`archive entry escapes staging root: ${full} → ${realFull} (zip-slip defense)`);
|
|
33187
33122
|
}
|
|
33188
33123
|
if (lst.isDirectory()) {
|
|
@@ -33229,7 +33164,7 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
|
33229
33164
|
const suffix = randomBytes(8).toString("hex");
|
|
33230
33165
|
const stagingDir = `${destDir}.staging-${suffix}`;
|
|
33231
33166
|
try {
|
|
33232
|
-
|
|
33167
|
+
rmSync7(stagingDir, { recursive: true, force: true });
|
|
33233
33168
|
} catch {}
|
|
33234
33169
|
mkdirSync9(stagingDir, { recursive: true });
|
|
33235
33170
|
try {
|
|
@@ -33237,24 +33172,24 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
|
33237
33172
|
runPlatformExtractor(archivePath, stagingDir, archiveType);
|
|
33238
33173
|
validateExtraction(stagingDir);
|
|
33239
33174
|
try {
|
|
33240
|
-
|
|
33175
|
+
rmSync7(destDir, { recursive: true, force: true });
|
|
33241
33176
|
} catch {}
|
|
33242
|
-
|
|
33177
|
+
renameSync8(stagingDir, destDir);
|
|
33243
33178
|
} catch (err) {
|
|
33244
33179
|
try {
|
|
33245
|
-
|
|
33180
|
+
rmSync7(stagingDir, { recursive: true, force: true });
|
|
33246
33181
|
} catch {}
|
|
33247
33182
|
throw err;
|
|
33248
33183
|
}
|
|
33249
33184
|
}
|
|
33250
33185
|
function quarantineCachedGithubInstall(spec, reason) {
|
|
33251
33186
|
const packageDir = ghPackageDir(spec);
|
|
33252
|
-
const dest =
|
|
33187
|
+
const dest = join17(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
|
|
33253
33188
|
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
33254
33189
|
try {
|
|
33255
33190
|
mkdirSync9(dirname8(dest), { recursive: true });
|
|
33256
|
-
|
|
33257
|
-
|
|
33191
|
+
rmSync7(dest, { recursive: true, force: true });
|
|
33192
|
+
renameSync8(packageDir, dest);
|
|
33258
33193
|
} catch (err) {
|
|
33259
33194
|
warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
33260
33195
|
}
|
|
@@ -33262,9 +33197,9 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
33262
33197
|
function validateCachedGithubInstall(spec, platform3) {
|
|
33263
33198
|
const packageDir = ghPackageDir(spec);
|
|
33264
33199
|
const meta3 = readGithubInstalledMetaIn(packageDir);
|
|
33265
|
-
const binaryPath = ghBinaryCandidates(spec, platform3).map((candidate) =>
|
|
33200
|
+
const binaryPath = ghBinaryCandidates(spec, platform3).map((candidate) => join17(ghBinDir(spec), candidate)).find((candidate) => {
|
|
33266
33201
|
try {
|
|
33267
|
-
return
|
|
33202
|
+
return statSync8(candidate).isFile();
|
|
33268
33203
|
} catch {
|
|
33269
33204
|
return false;
|
|
33270
33205
|
}
|
|
@@ -33340,7 +33275,7 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
|
|
|
33340
33275
|
}
|
|
33341
33276
|
const pkgDir = ghPackageDir(spec);
|
|
33342
33277
|
const extractDir = ghExtractDir(spec);
|
|
33343
|
-
const archivePath =
|
|
33278
|
+
const archivePath = join17(pkgDir, expected.name);
|
|
33344
33279
|
log2(`[lsp] downloading ${spec.id} ${tag} → ${matchingAsset.url}`);
|
|
33345
33280
|
try {
|
|
33346
33281
|
await downloadFile(matchingAsset.url, archivePath, fetchImpl, matchingAsset.size, signal);
|
|
@@ -33354,7 +33289,7 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
|
|
|
33354
33289
|
} catch (err) {
|
|
33355
33290
|
error2(`[lsp] hash ${spec.id} failed: ${err}`);
|
|
33356
33291
|
try {
|
|
33357
|
-
|
|
33292
|
+
unlinkSync7(archivePath);
|
|
33358
33293
|
} catch {}
|
|
33359
33294
|
return null;
|
|
33360
33295
|
}
|
|
@@ -33365,7 +33300,7 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
|
|
|
33365
33300
|
if (previousArchiveSha256 !== archiveSha256) {
|
|
33366
33301
|
error2(`[lsp] ${spec.id} ${tag}: TOFU sha256 mismatch — refusing install. ` + `Previously installed archive sha256=${previousArchiveSha256}, downloaded sha256=${archiveSha256}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding. ` + `Run \`aft doctor --clear\` to wipe the cache and force a fresh install if you've verified the change.`);
|
|
33367
33302
|
try {
|
|
33368
|
-
|
|
33303
|
+
unlinkSync7(archivePath);
|
|
33369
33304
|
} catch {}
|
|
33370
33305
|
return null;
|
|
33371
33306
|
}
|
|
@@ -33377,11 +33312,11 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
|
|
|
33377
33312
|
return null;
|
|
33378
33313
|
} finally {
|
|
33379
33314
|
try {
|
|
33380
|
-
|
|
33315
|
+
unlinkSync7(archivePath);
|
|
33381
33316
|
} catch {}
|
|
33382
33317
|
}
|
|
33383
|
-
const innerBinaryPath =
|
|
33384
|
-
if (!
|
|
33318
|
+
const innerBinaryPath = join17(extractDir, spec.binaryPathInArchive(platform3, arch, version2));
|
|
33319
|
+
if (!existsSync14(innerBinaryPath)) {
|
|
33385
33320
|
error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
|
|
33386
33321
|
return null;
|
|
33387
33322
|
}
|
|
@@ -33465,7 +33400,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl = fetch) {
|
|
|
33465
33400
|
if (!host) {
|
|
33466
33401
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
33467
33402
|
try {
|
|
33468
|
-
if (
|
|
33403
|
+
if (existsSync14(ghBinDir(spec))) {
|
|
33469
33404
|
cachedBinDirs.push(ghBinDir(spec));
|
|
33470
33405
|
}
|
|
33471
33406
|
} catch {}
|
|
@@ -33703,33 +33638,77 @@ function renderScreen(state, rows = state.rows, cols = state.cols) {
|
|
|
33703
33638
|
`);
|
|
33704
33639
|
}
|
|
33705
33640
|
function writeTerminal(terminal, data) {
|
|
33706
|
-
return new Promise((
|
|
33641
|
+
return new Promise((resolve9) => terminal.write(data, resolve9));
|
|
33642
|
+
}
|
|
33643
|
+
|
|
33644
|
+
// src/shared/rpc-notifications.ts
|
|
33645
|
+
var queue = [];
|
|
33646
|
+
var nextNotificationId = 1;
|
|
33647
|
+
var lastDrainAtBySession = new Map;
|
|
33648
|
+
var lastDrainAtAny = 0;
|
|
33649
|
+
var TUI_CONNECTED_WINDOW_MS = 3000;
|
|
33650
|
+
function pushNotification(type, payload, sessionId) {
|
|
33651
|
+
queue.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
33652
|
+
if (queue.length > 100) {
|
|
33653
|
+
const newestPerSession = new Map;
|
|
33654
|
+
for (const notification of queue) {
|
|
33655
|
+
const previous = newestPerSession.get(notification.sessionId);
|
|
33656
|
+
if (previous === undefined || notification.id > previous) {
|
|
33657
|
+
newestPerSession.set(notification.sessionId, notification.id);
|
|
33658
|
+
}
|
|
33659
|
+
}
|
|
33660
|
+
const mustKeep = new Set(newestPerSession.values());
|
|
33661
|
+
const byNewest = [...queue].sort((a, b) => b.id - a.id);
|
|
33662
|
+
const kept = [];
|
|
33663
|
+
for (const notification of byNewest) {
|
|
33664
|
+
if (kept.length < 50 || mustKeep.has(notification.id))
|
|
33665
|
+
kept.push(notification);
|
|
33666
|
+
}
|
|
33667
|
+
queue = kept.sort((a, b) => a.id - b.id);
|
|
33668
|
+
}
|
|
33669
|
+
}
|
|
33670
|
+
function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
33671
|
+
const now = Date.now();
|
|
33672
|
+
lastDrainAtAny = now;
|
|
33673
|
+
if (sessionId !== undefined)
|
|
33674
|
+
lastDrainAtBySession.set(sessionId, now);
|
|
33675
|
+
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
33676
|
+
if (lastReceivedId > 0) {
|
|
33677
|
+
queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
|
|
33678
|
+
}
|
|
33679
|
+
return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
|
|
33680
|
+
}
|
|
33681
|
+
function isTuiConnected(sessionId) {
|
|
33682
|
+
const now = Date.now();
|
|
33683
|
+
if (sessionId !== undefined) {
|
|
33684
|
+
const at = lastDrainAtBySession.get(sessionId) ?? 0;
|
|
33685
|
+
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
|
|
33686
|
+
}
|
|
33687
|
+
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
|
|
33707
33688
|
}
|
|
33708
33689
|
|
|
33709
33690
|
// src/shared/rpc-server.ts
|
|
33710
33691
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
33711
33692
|
import {
|
|
33712
|
-
existsSync as
|
|
33693
|
+
existsSync as existsSync15,
|
|
33713
33694
|
mkdirSync as mkdirSync10,
|
|
33714
33695
|
readdirSync as readdirSync5,
|
|
33715
|
-
readFileSync as
|
|
33716
|
-
renameSync as
|
|
33717
|
-
unlinkSync as
|
|
33718
|
-
writeFileSync as
|
|
33696
|
+
readFileSync as readFileSync15,
|
|
33697
|
+
renameSync as renameSync9,
|
|
33698
|
+
unlinkSync as unlinkSync8,
|
|
33699
|
+
writeFileSync as writeFileSync11
|
|
33719
33700
|
} from "node:fs";
|
|
33720
33701
|
import { createServer } from "node:http";
|
|
33721
|
-
import { dirname as dirname9, join as
|
|
33702
|
+
import { dirname as dirname9, join as join19 } from "node:path";
|
|
33722
33703
|
|
|
33723
33704
|
// src/shared/rpc-utils.ts
|
|
33724
|
-
import {
|
|
33725
|
-
import { join as join19 } from "node:path";
|
|
33705
|
+
import { join as join18 } from "node:path";
|
|
33726
33706
|
function projectHash(directory) {
|
|
33727
|
-
|
|
33728
|
-
return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
33707
|
+
return projectRootKeyHash(directory);
|
|
33729
33708
|
}
|
|
33730
33709
|
function rpcPortFileDir(storageDir, directory) {
|
|
33731
33710
|
const hash2 = projectHash(directory);
|
|
33732
|
-
return
|
|
33711
|
+
return join18(storageDir, "rpc", hash2, "ports");
|
|
33733
33712
|
}
|
|
33734
33713
|
function isPidAlive(pid) {
|
|
33735
33714
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
@@ -33782,13 +33761,13 @@ class AftRpcServer {
|
|
|
33782
33761
|
constructor(storageDir, directory) {
|
|
33783
33762
|
this.portsDir = rpcPortFileDir(storageDir, directory);
|
|
33784
33763
|
this.instanceId = randomBytes2(8).toString("hex");
|
|
33785
|
-
this.portFilePath =
|
|
33764
|
+
this.portFilePath = join19(this.portsDir, `${this.instanceId}.json`);
|
|
33786
33765
|
}
|
|
33787
33766
|
handle(method, handler) {
|
|
33788
33767
|
this.handlers.set(method, handler);
|
|
33789
33768
|
}
|
|
33790
33769
|
async start() {
|
|
33791
|
-
return new Promise((
|
|
33770
|
+
return new Promise((resolve9, reject) => {
|
|
33792
33771
|
const server = createServer((req, res) => this.dispatch(req, res));
|
|
33793
33772
|
server.on("error", (err) => {
|
|
33794
33773
|
warn2(`RPC server error: ${err.message}`);
|
|
@@ -33807,13 +33786,13 @@ class AftRpcServer {
|
|
|
33807
33786
|
const dir = dirname9(this.portFilePath);
|
|
33808
33787
|
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
33809
33788
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
33810
|
-
|
|
33789
|
+
writeFileSync11(tmpPath, JSON.stringify({
|
|
33811
33790
|
port: this.port,
|
|
33812
33791
|
token: this.token,
|
|
33813
33792
|
pid: process.pid,
|
|
33814
33793
|
started_at: Date.now()
|
|
33815
33794
|
}), { encoding: "utf-8", mode: 384 });
|
|
33816
|
-
|
|
33795
|
+
renameSync9(tmpPath, this.portFilePath);
|
|
33817
33796
|
log2(`RPC server listening on 127.0.0.1:${this.port}`);
|
|
33818
33797
|
this.heartbeatTimer = setInterval(() => this.ensurePortFile(), PORT_FILE_HEARTBEAT_MS);
|
|
33819
33798
|
this.heartbeatTimer.unref?.();
|
|
@@ -33821,7 +33800,7 @@ class AftRpcServer {
|
|
|
33821
33800
|
} catch (err) {
|
|
33822
33801
|
warn2(`Failed to write RPC port file: ${err}`);
|
|
33823
33802
|
}
|
|
33824
|
-
|
|
33803
|
+
resolve9(this.port);
|
|
33825
33804
|
});
|
|
33826
33805
|
server.unref();
|
|
33827
33806
|
});
|
|
@@ -33836,17 +33815,17 @@ class AftRpcServer {
|
|
|
33836
33815
|
for (const entry of entries) {
|
|
33837
33816
|
if (!entry.endsWith(".json"))
|
|
33838
33817
|
continue;
|
|
33839
|
-
const filePath =
|
|
33818
|
+
const filePath = join19(this.portsDir, entry);
|
|
33840
33819
|
if (filePath === this.portFilePath)
|
|
33841
33820
|
continue;
|
|
33842
33821
|
try {
|
|
33843
|
-
const record2 = parseRpcPortRecord(
|
|
33822
|
+
const record2 = parseRpcPortRecord(readFileSync15(filePath, "utf-8"));
|
|
33844
33823
|
if (record2 === null) {
|
|
33845
|
-
|
|
33824
|
+
unlinkSync8(filePath);
|
|
33846
33825
|
continue;
|
|
33847
33826
|
}
|
|
33848
33827
|
if (record2.pid !== undefined && !isPidAlive(record2.pid)) {
|
|
33849
|
-
|
|
33828
|
+
unlinkSync8(filePath);
|
|
33850
33829
|
}
|
|
33851
33830
|
} catch {}
|
|
33852
33831
|
}
|
|
@@ -33855,16 +33834,16 @@ class AftRpcServer {
|
|
|
33855
33834
|
if (!this.server || this.token === null)
|
|
33856
33835
|
return;
|
|
33857
33836
|
try {
|
|
33858
|
-
if (
|
|
33837
|
+
if (existsSync15(this.portFilePath))
|
|
33859
33838
|
return;
|
|
33860
33839
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
33861
|
-
|
|
33840
|
+
writeFileSync11(tmpPath, JSON.stringify({
|
|
33862
33841
|
port: this.port,
|
|
33863
33842
|
token: this.token,
|
|
33864
33843
|
pid: process.pid,
|
|
33865
33844
|
started_at: Date.now()
|
|
33866
33845
|
}), { encoding: "utf-8", mode: 384 });
|
|
33867
|
-
|
|
33846
|
+
renameSync9(tmpPath, this.portFilePath);
|
|
33868
33847
|
log2(`RPC port file was missing; rewrote ${this.portFilePath}`);
|
|
33869
33848
|
} catch {}
|
|
33870
33849
|
}
|
|
@@ -33879,7 +33858,7 @@ class AftRpcServer {
|
|
|
33879
33858
|
}
|
|
33880
33859
|
this.token = null;
|
|
33881
33860
|
try {
|
|
33882
|
-
|
|
33861
|
+
unlinkSync8(this.portFilePath);
|
|
33883
33862
|
} catch {}
|
|
33884
33863
|
}
|
|
33885
33864
|
dispatch(req, res) {
|
|
@@ -34240,33 +34219,33 @@ function formatStatusMarkdown(status) {
|
|
|
34240
34219
|
|
|
34241
34220
|
// src/shared/tui-config.ts
|
|
34242
34221
|
var import_comment_json4 = __toESM(require_src2(), 1);
|
|
34243
|
-
import { existsSync as
|
|
34244
|
-
import { dirname as dirname10, join as
|
|
34222
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "node:fs";
|
|
34223
|
+
import { dirname as dirname10, join as join21 } from "node:path";
|
|
34245
34224
|
|
|
34246
34225
|
// src/shared/opencode-config-dir.ts
|
|
34247
34226
|
import { homedir as homedir13 } from "node:os";
|
|
34248
|
-
import { join as
|
|
34227
|
+
import { join as join20, resolve as resolve9 } from "node:path";
|
|
34249
34228
|
function getCliConfigDir() {
|
|
34250
34229
|
const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
34251
34230
|
if (envConfigDir) {
|
|
34252
|
-
return
|
|
34231
|
+
return resolve9(envConfigDir);
|
|
34253
34232
|
}
|
|
34254
34233
|
if (process.platform === "win32") {
|
|
34255
|
-
return
|
|
34234
|
+
return join20(homedir13(), ".config", "opencode");
|
|
34256
34235
|
}
|
|
34257
|
-
return
|
|
34236
|
+
return join20(process.env.XDG_CONFIG_HOME || join20(homedir13(), ".config"), "opencode");
|
|
34258
34237
|
}
|
|
34259
|
-
function
|
|
34238
|
+
function getOpenCodeConfigDir(_options) {
|
|
34260
34239
|
return getCliConfigDir();
|
|
34261
34240
|
}
|
|
34262
34241
|
function getOpenCodeConfigPaths(options) {
|
|
34263
|
-
const configDir =
|
|
34242
|
+
const configDir = getOpenCodeConfigDir(options);
|
|
34264
34243
|
return {
|
|
34265
34244
|
configDir,
|
|
34266
|
-
configJson:
|
|
34267
|
-
configJsonc:
|
|
34268
|
-
packageJson:
|
|
34269
|
-
omoConfig:
|
|
34245
|
+
configJson: join20(configDir, "opencode.json"),
|
|
34246
|
+
configJsonc: join20(configDir, "opencode.jsonc"),
|
|
34247
|
+
packageJson: join20(configDir, "package.json"),
|
|
34248
|
+
omoConfig: join20(configDir, "magic-context.jsonc")
|
|
34270
34249
|
};
|
|
34271
34250
|
}
|
|
34272
34251
|
|
|
@@ -34275,11 +34254,11 @@ var PLUGIN_NAME = "@cortexkit/aft-opencode";
|
|
|
34275
34254
|
var PLUGIN_ENTRY = `${PLUGIN_NAME}@latest`;
|
|
34276
34255
|
function resolveTuiConfigPath() {
|
|
34277
34256
|
const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
|
|
34278
|
-
const jsoncPath =
|
|
34279
|
-
const jsonPath =
|
|
34280
|
-
if (
|
|
34257
|
+
const jsoncPath = join21(configDir, "tui.jsonc");
|
|
34258
|
+
const jsonPath = join21(configDir, "tui.json");
|
|
34259
|
+
if (existsSync16(jsoncPath))
|
|
34281
34260
|
return jsoncPath;
|
|
34282
|
-
if (
|
|
34261
|
+
if (existsSync16(jsonPath))
|
|
34283
34262
|
return jsonPath;
|
|
34284
34263
|
return jsonPath;
|
|
34285
34264
|
}
|
|
@@ -34287,8 +34266,8 @@ function ensureTuiPluginEntry() {
|
|
|
34287
34266
|
try {
|
|
34288
34267
|
const configPath = resolveTuiConfigPath();
|
|
34289
34268
|
let config2 = {};
|
|
34290
|
-
if (
|
|
34291
|
-
config2 = import_comment_json4.parse(
|
|
34269
|
+
if (existsSync16(configPath)) {
|
|
34270
|
+
config2 = import_comment_json4.parse(readFileSync16(configPath, "utf-8")) ?? {};
|
|
34292
34271
|
}
|
|
34293
34272
|
const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((value) => typeof value === "string") : [];
|
|
34294
34273
|
if (plugins.some((plugin) => plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`) || plugin.includes("opencode-plugin") || plugin.includes("aft-opencode"))) {
|
|
@@ -34297,7 +34276,7 @@ function ensureTuiPluginEntry() {
|
|
|
34297
34276
|
plugins.push(PLUGIN_ENTRY);
|
|
34298
34277
|
config2.plugin = plugins;
|
|
34299
34278
|
mkdirSync11(dirname10(configPath), { recursive: true });
|
|
34300
|
-
|
|
34279
|
+
writeFileSync12(configPath, `${import_comment_json4.stringify(config2, null, 2)}
|
|
34301
34280
|
`);
|
|
34302
34281
|
log2(`[aft-plugin] added TUI plugin entry to ${configPath}`);
|
|
34303
34282
|
return true;
|
|
@@ -34365,8 +34344,8 @@ function installProcessHandlers() {
|
|
|
34365
34344
|
}
|
|
34366
34345
|
signalShutdownStarted = true;
|
|
34367
34346
|
process.exitCode = SIGNAL_EXIT_CODES[sig];
|
|
34368
|
-
const timeout = new Promise((
|
|
34369
|
-
setTimeout(
|
|
34347
|
+
const timeout = new Promise((resolve10) => {
|
|
34348
|
+
setTimeout(resolve10, SIGNAL_CLEANUP_TIMEOUT_MS);
|
|
34370
34349
|
});
|
|
34371
34350
|
Promise.race([runCleanups(sig), timeout]).finally(() => {
|
|
34372
34351
|
process.exit(SIGNAL_EXIT_CODES[sig]);
|
|
@@ -34477,7 +34456,6 @@ function instrumentToolMap(tools) {
|
|
|
34477
34456
|
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
34478
34457
|
|
|
34479
34458
|
// src/tools/_shared.ts
|
|
34480
|
-
import * as fs3 from "node:fs";
|
|
34481
34459
|
import * as os3 from "node:os";
|
|
34482
34460
|
import * as path3 from "node:path";
|
|
34483
34461
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
@@ -34485,12 +34463,7 @@ var z2 = tool2.schema;
|
|
|
34485
34463
|
var optionalInt = (min, max) => z2.number().int().min(min).max(max).optional();
|
|
34486
34464
|
var BASH_TRANSPORT_TIMEOUT_MS = 30000;
|
|
34487
34465
|
function canonicalizeDirectory(dir) {
|
|
34488
|
-
|
|
34489
|
-
try {
|
|
34490
|
-
return fs3.realpathSync(trimmed);
|
|
34491
|
-
} catch {
|
|
34492
|
-
return path3.resolve(trimmed);
|
|
34493
|
-
}
|
|
34466
|
+
return canonicalizeProjectRoot(dir);
|
|
34494
34467
|
}
|
|
34495
34468
|
function projectRootFor(runtime) {
|
|
34496
34469
|
const cached2 = getSessionDirectoryCached(runtime.sessionID);
|
|
@@ -34559,7 +34532,7 @@ async function callBashBridge(ctx, runtime, command, params = {}, options) {
|
|
|
34559
34532
|
}
|
|
34560
34533
|
|
|
34561
34534
|
// src/tools/permissions.ts
|
|
34562
|
-
import * as
|
|
34535
|
+
import * as fs3 from "node:fs";
|
|
34563
34536
|
import * as path4 from "node:path";
|
|
34564
34537
|
var UNSUPPORTED_ASK_HOST = "AFT requires OpenCode 1.15.5 or newer for permission asks; please upgrade OpenCode";
|
|
34565
34538
|
var RESTRICT_NOTICE_THROTTLE_MS = 5 * 60 * 1000;
|
|
@@ -34646,7 +34619,7 @@ function normalizePath(p) {
|
|
|
34646
34619
|
return p;
|
|
34647
34620
|
const resolved = path4.resolve(windowsPath(p));
|
|
34648
34621
|
try {
|
|
34649
|
-
return
|
|
34622
|
+
return fs3.realpathSync.native(resolved);
|
|
34650
34623
|
} catch {
|
|
34651
34624
|
return resolved;
|
|
34652
34625
|
}
|
|
@@ -34828,8 +34801,9 @@ function astTools(ctx) {
|
|
|
34828
34801
|
params.paths = paths;
|
|
34829
34802
|
if (!isEmptyParam(args.globs))
|
|
34830
34803
|
params.globs = args.globs;
|
|
34831
|
-
|
|
34832
|
-
|
|
34804
|
+
const contextLines = coerceOptionalInt(args.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
34805
|
+
if (contextLines !== undefined)
|
|
34806
|
+
params.context = contextLines;
|
|
34833
34807
|
const response = await callBridge(ctx, context, "ast_search", params);
|
|
34834
34808
|
if (response.success === false) {
|
|
34835
34809
|
throw new Error(response.message || "ast_search failed");
|
|
@@ -34913,7 +34887,7 @@ ${hint}`;
|
|
|
34913
34887
|
dryRun: z3.boolean().optional().describe("Preview changes without applying (default: false)")
|
|
34914
34888
|
},
|
|
34915
34889
|
execute: async (args, context) => {
|
|
34916
|
-
const isDryRun = args.dryRun
|
|
34890
|
+
const isDryRun = coerceBoolean(args.dryRun);
|
|
34917
34891
|
const paths = await resolveAstPaths(ctx, context, args.paths);
|
|
34918
34892
|
const externalDenied = await checkAstPathsPermission(ctx, context, paths);
|
|
34919
34893
|
if (externalDenied)
|
|
@@ -34945,7 +34919,7 @@ ${hint}`;
|
|
|
34945
34919
|
params.paths = paths;
|
|
34946
34920
|
if (!isEmptyParam(args.globs))
|
|
34947
34921
|
params.globs = args.globs;
|
|
34948
|
-
params.dry_run = args.dryRun
|
|
34922
|
+
params.dry_run = coerceBoolean(args.dryRun);
|
|
34949
34923
|
const response = await callBridge(ctx, context, "ast_replace", params);
|
|
34950
34924
|
if (response.success === false) {
|
|
34951
34925
|
throw new Error(response.message || "ast_replace failed");
|
|
@@ -35118,8 +35092,8 @@ function resolveForegroundWaitMs(configured) {
|
|
|
35118
35092
|
}
|
|
35119
35093
|
function bashToolDescription(aftSearchRegistered, compressionOn, backgroundOn) {
|
|
35120
35094
|
const searchSteer = aftSearchRegistered ? "use aft_search (concepts, identifiers, regex, literals), read, aft_outline, or aft_zoom instead" : "use the grep tool, read, aft_outline, or aft_zoom instead";
|
|
35121
|
-
const compression = compressionOn ? " Output is compressed by default; pass compressed: false for raw output." : "";
|
|
35122
|
-
const tasks = backgroundOn ? '
|
|
35095
|
+
const compression = compressionOn ? " Output is compressed by default; pass compressed: false for raw output. Piped commands run verbatim and show the pipeline's output; for AFT's test/build summary, run the runner without | head, | tail, or | grep." : "";
|
|
35096
|
+
const tasks = backgroundOn ? ' Commands run in the foreground and return inline; a long-running one auto-promotes to background and delivers a completion reminder when it finishes — so for the common "I am waiting on this result" case, just run it and wait, no flags needed. Use background: true yourself ONLY when you have other useful work to do while it runs; then bash_watch waits on the task (sync blocks until exit/pattern, async notifies) and bash_status peeks at it — never background a command and immediately bash_watch it (that wastes a turn for what foreground returns in one), and never loop bash_status to wait. pty: true runs interactive programs (REPLs, TUIs), implies background, and is driven with bash_status({ outputMode: "screen" }) plus bash_write.' : " Commands run in the foreground to completion; timeout is the hard kill cap (default 30 minutes).";
|
|
35123
35097
|
return `Execute shell commands.${compression}${tasks}
|
|
35124
35098
|
|
|
35125
35099
|
DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}.`;
|
|
@@ -35200,14 +35174,12 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35200
35174
|
const description = args2.description;
|
|
35201
35175
|
const metadata = context.metadata;
|
|
35202
35176
|
const rawCommand = args2.command;
|
|
35203
|
-
const
|
|
35204
|
-
const pipeStrip = maybeStripCompressorPipe(rawCommand, compressionEnabled);
|
|
35205
|
-
const command = pipeStrip.command;
|
|
35177
|
+
const command = rawCommand;
|
|
35206
35178
|
const cwd = args2.workdir ?? context.directory;
|
|
35207
35179
|
const isSubagent = await resolveIsSubagent(ctx.client, context.sessionID, context.directory);
|
|
35208
35180
|
const backgroundDisabled = !bashCfg.background;
|
|
35209
|
-
const requestedPty = !backgroundDisabled && args2.pty
|
|
35210
|
-
const requestedBackground = !backgroundDisabled && (args2.background
|
|
35181
|
+
const requestedPty = !backgroundDisabled && coerceBoolean(args2.pty);
|
|
35182
|
+
const requestedBackground = !backgroundDisabled && (coerceBoolean(args2.background) || requestedPty);
|
|
35211
35183
|
if (requestedPty && isSubagent) {
|
|
35212
35184
|
throw new Error("PTY mode is not available in subagent sessions; subagents cannot drive interactive terminals.");
|
|
35213
35185
|
}
|
|
@@ -35215,7 +35187,10 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35215
35187
|
const subagentForcedForeground = isSubagent && !allowSubagentBg;
|
|
35216
35188
|
const blockToCompletion = subagentForcedForeground || backgroundDisabled;
|
|
35217
35189
|
const effectiveBackground = blockToCompletion ? false : requestedBackground;
|
|
35218
|
-
const rawTimeout = args2.timeout;
|
|
35190
|
+
const rawTimeout = coerceOptionalInt(args2.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
|
|
35191
|
+
const ptyRows = coerceOptionalInt(args2.ptyRows, "ptyRows", 1, 60);
|
|
35192
|
+
const ptyCols = coerceOptionalInt(args2.ptyCols, "ptyCols", 1, 140);
|
|
35193
|
+
const compressed = coerceBoolean(args2.compressed, true);
|
|
35219
35194
|
const effectiveTimeout = effectiveBackground || backgroundDisabled ? rawTimeout : resolveBashKillTimeout(rawTimeout, bashCfg.foreground_wait_window_ms);
|
|
35220
35195
|
if (subagentForcedForeground && requestedBackground) {
|
|
35221
35196
|
sessionLog(context.sessionID, "[bash] subagent + background:true → converting to foreground (subagent would lose task_id)");
|
|
@@ -35229,10 +35204,10 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35229
35204
|
description,
|
|
35230
35205
|
background: effectiveBackground,
|
|
35231
35206
|
notify_on_completion: effectiveBackground,
|
|
35232
|
-
compressed
|
|
35207
|
+
compressed,
|
|
35233
35208
|
pty: requestedPty,
|
|
35234
|
-
pty_rows:
|
|
35235
|
-
pty_cols:
|
|
35209
|
+
pty_rows: ptyRows,
|
|
35210
|
+
pty_cols: ptyCols,
|
|
35236
35211
|
permissions_requested: true
|
|
35237
35212
|
}, callBashBridge, {
|
|
35238
35213
|
onProgress: ({ text }) => {
|
|
@@ -35251,7 +35226,6 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35251
35226
|
let startedLine = formatBackgroundLaunch(taskId, requestedPty);
|
|
35252
35227
|
if (isSubagent && allowSubagentBg)
|
|
35253
35228
|
startedLine += subagentGuidance(taskId);
|
|
35254
|
-
startedLine = appendPipeStripNote(startedLine, pipeStrip.note);
|
|
35255
35229
|
const metadataPayload2 = { description, output: startedLine, status: "running", taskId };
|
|
35256
35230
|
metadata?.(metadataPayload2);
|
|
35257
35231
|
return { output: startedLine, title: uiTitle, metadata: metadataPayload2 };
|
|
@@ -35265,7 +35239,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35265
35239
|
throw new Error(status.message ?? "bash_status failed");
|
|
35266
35240
|
}
|
|
35267
35241
|
if (isTerminalStatus(status.status)) {
|
|
35268
|
-
const rendered2 = maybeAppendGrepSearchHint(
|
|
35242
|
+
const rendered2 = maybeAppendGrepSearchHint(formatForegroundResult(status), command, aftSearchRegistered, projectRootFor(context));
|
|
35269
35243
|
const metadataPayload2 = foregroundMetadata(description, status, rendered2);
|
|
35270
35244
|
metadata?.(metadataPayload2);
|
|
35271
35245
|
return { output: rendered2, title: uiTitle, metadata: metadataPayload2 };
|
|
@@ -35285,7 +35259,6 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35285
35259
|
let message = formatPromotionMessage(taskId, effectiveTimeout, foregroundWaitMs);
|
|
35286
35260
|
if (isSubagent && allowSubagentBg)
|
|
35287
35261
|
message += subagentGuidance(taskId);
|
|
35288
|
-
message = appendPipeStripNote(message, pipeStrip.note);
|
|
35289
35262
|
const metadataPayload2 = { description, output: message, status: "running", taskId };
|
|
35290
35263
|
metadata?.(metadataPayload2);
|
|
35291
35264
|
return { output: message, title: uiTitle, metadata: metadataPayload2 };
|
|
@@ -35320,7 +35293,6 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35320
35293
|
rendered += `
|
|
35321
35294
|
[exit code: ${exit}]`;
|
|
35322
35295
|
}
|
|
35323
|
-
rendered = appendPipeStripNote(rendered, pipeStrip.note);
|
|
35324
35296
|
return {
|
|
35325
35297
|
output: rendered,
|
|
35326
35298
|
title: description ?? shortenCommand(command),
|
|
@@ -35501,7 +35473,7 @@ function conflictTools(ctx) {
|
|
|
35501
35473
|
}
|
|
35502
35474
|
|
|
35503
35475
|
// src/tools/hoisted.ts
|
|
35504
|
-
import * as
|
|
35476
|
+
import * as fs5 from "node:fs";
|
|
35505
35477
|
import * as path5 from "node:path";
|
|
35506
35478
|
import { tool as tool8 } from "@opencode-ai/plugin";
|
|
35507
35479
|
|
|
@@ -35641,6 +35613,16 @@ function normalizeUnicode(str) {
|
|
|
35641
35613
|
function normalizeIndent(str) {
|
|
35642
35614
|
return str.replace(/^[\t ]+/, (m) => " ".repeat(m.length));
|
|
35643
35615
|
}
|
|
35616
|
+
var REFLOW_NON_WS_TOLERANCE = 8;
|
|
35617
|
+
function normalizeReflowWhitespace(str) {
|
|
35618
|
+
return str.replace(/\s+/gu, " ").trim();
|
|
35619
|
+
}
|
|
35620
|
+
function stripReflowWhitespace(str) {
|
|
35621
|
+
return str.replace(/\s+/gu, "");
|
|
35622
|
+
}
|
|
35623
|
+
function hasReflowContent(str) {
|
|
35624
|
+
return /\S/u.test(str);
|
|
35625
|
+
}
|
|
35644
35626
|
function tryMatch(lines, pattern, startIndex, compare, eof) {
|
|
35645
35627
|
if (eof) {
|
|
35646
35628
|
const fromEnd = lines.length - pattern.length;
|
|
@@ -35655,6 +35637,7 @@ function tryMatch(lines, pattern, startIndex, compare, eof) {
|
|
|
35655
35637
|
if (matches)
|
|
35656
35638
|
return fromEnd;
|
|
35657
35639
|
}
|
|
35640
|
+
return -1;
|
|
35658
35641
|
}
|
|
35659
35642
|
for (let i = startIndex;i <= lines.length - pattern.length; i++) {
|
|
35660
35643
|
let matches = true;
|
|
@@ -35669,24 +35652,69 @@ function tryMatch(lines, pattern, startIndex, compare, eof) {
|
|
|
35669
35652
|
}
|
|
35670
35653
|
return -1;
|
|
35671
35654
|
}
|
|
35655
|
+
function findReflowMatch(lines, pattern, startIndex) {
|
|
35656
|
+
const needleText = pattern.join(`
|
|
35657
|
+
`);
|
|
35658
|
+
const normalizedNeedle = normalizeReflowWhitespace(needleText);
|
|
35659
|
+
const needleNonWhitespace = stripReflowWhitespace(needleText);
|
|
35660
|
+
if (normalizedNeedle.length === 0 || needleNonWhitespace.length === 0)
|
|
35661
|
+
return null;
|
|
35662
|
+
const minNonWhitespace = Math.max(0, needleNonWhitespace.length - REFLOW_NON_WS_TOLERANCE);
|
|
35663
|
+
const maxNonWhitespace = needleNonWhitespace.length + REFLOW_NON_WS_TOLERANCE;
|
|
35664
|
+
const matches = [];
|
|
35665
|
+
const seen = new Set;
|
|
35666
|
+
for (let start = Math.max(0, startIndex);start < lines.length; start++) {
|
|
35667
|
+
if (!hasReflowContent(lines[start]))
|
|
35668
|
+
continue;
|
|
35669
|
+
let windowNonWhitespaceLen = 0;
|
|
35670
|
+
for (let end = start + 1;end <= lines.length; end++) {
|
|
35671
|
+
const line = lines[end - 1];
|
|
35672
|
+
windowNonWhitespaceLen += stripReflowWhitespace(line).length;
|
|
35673
|
+
if (windowNonWhitespaceLen > maxNonWhitespace)
|
|
35674
|
+
break;
|
|
35675
|
+
if (windowNonWhitespaceLen < minNonWhitespace)
|
|
35676
|
+
continue;
|
|
35677
|
+
if (!hasReflowContent(line))
|
|
35678
|
+
continue;
|
|
35679
|
+
const windowText = lines.slice(start, end).join(`
|
|
35680
|
+
`);
|
|
35681
|
+
const windowNonWhitespace = stripReflowWhitespace(windowText);
|
|
35682
|
+
if (windowNonWhitespace !== needleNonWhitespace)
|
|
35683
|
+
continue;
|
|
35684
|
+
if (normalizeReflowWhitespace(windowText) !== normalizedNeedle)
|
|
35685
|
+
continue;
|
|
35686
|
+
const key = `${start}:${end}`;
|
|
35687
|
+
if (!seen.has(key)) {
|
|
35688
|
+
seen.add(key);
|
|
35689
|
+
matches.push({ found: start, lineCount: end - start });
|
|
35690
|
+
}
|
|
35691
|
+
}
|
|
35692
|
+
}
|
|
35693
|
+
return matches.length === 1 ? matches[0] : null;
|
|
35694
|
+
}
|
|
35672
35695
|
function seekSequenceTiered(lines, pattern, startIndex, eof = false) {
|
|
35673
35696
|
if (pattern.length === 0)
|
|
35674
35697
|
return null;
|
|
35675
35698
|
const exact = tryMatch(lines, pattern, startIndex, (a, b) => a === b, eof);
|
|
35676
35699
|
if (exact !== -1)
|
|
35677
|
-
return { found: exact, tier: "exact" };
|
|
35700
|
+
return { found: exact, tier: "exact", lineCount: pattern.length };
|
|
35678
35701
|
const rstrip = tryMatch(lines, pattern, startIndex, (a, b) => a.trimEnd() === b.trimEnd(), eof);
|
|
35679
35702
|
if (rstrip !== -1)
|
|
35680
|
-
return { found: rstrip, tier: "rstrip" };
|
|
35703
|
+
return { found: rstrip, tier: "rstrip", lineCount: pattern.length };
|
|
35681
35704
|
const trim = tryMatch(lines, pattern, startIndex, (a, b) => a.trim() === b.trim(), eof);
|
|
35682
35705
|
if (trim !== -1)
|
|
35683
|
-
return { found: trim, tier: "trim" };
|
|
35706
|
+
return { found: trim, tier: "trim", lineCount: pattern.length };
|
|
35684
35707
|
const indent = tryMatch(lines, pattern, startIndex, (a, b) => normalizeIndent(a).trimEnd() === normalizeIndent(b).trimEnd(), eof);
|
|
35685
35708
|
if (indent !== -1)
|
|
35686
|
-
return { found: indent, tier: "indent" };
|
|
35709
|
+
return { found: indent, tier: "indent", lineCount: pattern.length };
|
|
35687
35710
|
const unicode = tryMatch(lines, pattern, startIndex, (a, b) => normalizeUnicode(a.trim()) === normalizeUnicode(b.trim()), eof);
|
|
35688
35711
|
if (unicode !== -1)
|
|
35689
|
-
return { found: unicode, tier: "unicode" };
|
|
35712
|
+
return { found: unicode, tier: "unicode", lineCount: pattern.length };
|
|
35713
|
+
if (eof)
|
|
35714
|
+
return null;
|
|
35715
|
+
const reflow = findReflowMatch(lines, pattern, startIndex);
|
|
35716
|
+
if (reflow)
|
|
35717
|
+
return { ...reflow, tier: "reflow" };
|
|
35690
35718
|
return null;
|
|
35691
35719
|
}
|
|
35692
35720
|
function seekSequence(lines, pattern, startIndex, eof = false) {
|
|
@@ -35732,11 +35760,11 @@ function applyUpdateChunks(originalContent, filePath, chunks) {
|
|
|
35732
35760
|
let lineIndex = 0;
|
|
35733
35761
|
for (const chunk of chunks) {
|
|
35734
35762
|
if (chunk.change_context) {
|
|
35735
|
-
const
|
|
35736
|
-
if (
|
|
35763
|
+
const contextMatch = seekSequenceTiered(originalLines, [chunk.change_context], lineIndex);
|
|
35764
|
+
if (!contextMatch) {
|
|
35737
35765
|
throw new Error(`Failed to find context '${chunk.change_context}' in ${filePath}`);
|
|
35738
35766
|
}
|
|
35739
|
-
lineIndex =
|
|
35767
|
+
lineIndex = contextMatch.found + contextMatch.lineCount;
|
|
35740
35768
|
}
|
|
35741
35769
|
if (chunk.old_lines.length === 0) {
|
|
35742
35770
|
let insertionIdx;
|
|
@@ -35752,17 +35780,17 @@ function applyUpdateChunks(originalContent, filePath, chunks) {
|
|
|
35752
35780
|
}
|
|
35753
35781
|
let pattern = chunk.old_lines;
|
|
35754
35782
|
let newSlice = chunk.new_lines;
|
|
35755
|
-
let
|
|
35756
|
-
if (
|
|
35783
|
+
let match = seekSequenceTiered(originalLines, pattern, lineIndex, chunk.is_end_of_file);
|
|
35784
|
+
if (!match && pattern.length > 0 && pattern[pattern.length - 1] === "") {
|
|
35757
35785
|
pattern = pattern.slice(0, -1);
|
|
35758
35786
|
if (newSlice.length > 0 && newSlice[newSlice.length - 1] === "") {
|
|
35759
35787
|
newSlice = newSlice.slice(0, -1);
|
|
35760
35788
|
}
|
|
35761
|
-
|
|
35789
|
+
match = seekSequenceTiered(originalLines, pattern, lineIndex, chunk.is_end_of_file);
|
|
35762
35790
|
}
|
|
35763
|
-
if (
|
|
35764
|
-
replacements.push([found,
|
|
35765
|
-
lineIndex = found +
|
|
35791
|
+
if (match) {
|
|
35792
|
+
replacements.push([match.found, match.lineCount, newSlice]);
|
|
35793
|
+
lineIndex = match.found + match.lineCount;
|
|
35766
35794
|
} else {
|
|
35767
35795
|
const newSliceTrimmed = newSlice.filter((line) => line.trim().length > 0);
|
|
35768
35796
|
const alreadyApplied = newSliceTrimmed.length > 0 && seekSequence(originalLines, newSliceTrimmed, 0, chunk.is_end_of_file) !== -1;
|
|
@@ -35779,7 +35807,7 @@ Closest match starts at line ${closest.lineNumber} ` + `(${closest.matchedLines}
|
|
|
35779
35807
|
` + ` expected: ${JSON.stringify(expectedLine)}
|
|
35780
35808
|
` + ` actual: ${JSON.stringify(actualLine)}`;
|
|
35781
35809
|
}
|
|
35782
|
-
const triedTiers = "exact, trimEnd, trim, indent (tab/space), unicode";
|
|
35810
|
+
const triedTiers = "exact, trimEnd, trim, indent (tab/space), unicode, reflow (whitespace-normalized)";
|
|
35783
35811
|
const alreadyAppliedHint = alreadyApplied ? `
|
|
35784
35812
|
|
|
35785
35813
|
Hint: the replacement content for this hunk already appears in the file. ` + "The patch may have been partially applied in a prior turn — re-read the file " + "to confirm which hunks still need to apply." : "";
|
|
@@ -35804,7 +35832,7 @@ ${chunk.old_lines.join(`
|
|
|
35804
35832
|
}
|
|
35805
35833
|
|
|
35806
35834
|
// src/tools/bash_watch.ts
|
|
35807
|
-
import * as
|
|
35835
|
+
import * as fs4 from "node:fs/promises";
|
|
35808
35836
|
import { tool as tool6 } from "@opencode-ai/plugin";
|
|
35809
35837
|
var z6 = tool6.schema;
|
|
35810
35838
|
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
@@ -35826,7 +35854,7 @@ function createBashWatchTool(ctx) {
|
|
|
35826
35854
|
},
|
|
35827
35855
|
execute: async (args, context) => {
|
|
35828
35856
|
const taskId = args.taskId;
|
|
35829
|
-
const requestedAsync = args.background
|
|
35857
|
+
const requestedAsync = coerceBoolean(args.background);
|
|
35830
35858
|
const waitFor = parseWaitPattern(args.pattern);
|
|
35831
35859
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
35832
35860
|
const isSubagent = await resolveIsSubagent(ctx.client, context.sessionID, context.directory);
|
|
@@ -35838,7 +35866,7 @@ function createBashWatchTool(ctx) {
|
|
|
35838
35866
|
}
|
|
35839
35867
|
const notifyParams = {
|
|
35840
35868
|
task_id: taskId,
|
|
35841
|
-
once: args.once
|
|
35869
|
+
once: coerceBoolean(args.once, true)
|
|
35842
35870
|
};
|
|
35843
35871
|
if (waitFor.kind === "regex")
|
|
35844
35872
|
notifyParams.regex = waitFor.source;
|
|
@@ -35865,11 +35893,11 @@ function createBashWatchTool(ctx) {
|
|
|
35865
35893
|
return `Watch registered: ${registered.watch_id} on task ${taskId}
|
|
35866
35894
|
A notification will fire when the pattern matches or the task exits.`;
|
|
35867
35895
|
}
|
|
35868
|
-
const effectiveWaitMs = subagentForcedSync ? MAX_BASH_STATUS_WAIT_TIMEOUT_MS : Math.min(args.timeoutMs ?? DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS, MAX_BASH_STATUS_WAIT_TIMEOUT_MS);
|
|
35896
|
+
const effectiveWaitMs = subagentForcedSync ? MAX_BASH_STATUS_WAIT_TIMEOUT_MS : Math.min(coerceOptionalInt(args.timeoutMs, "timeoutMs", 1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS) ?? DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS, MAX_BASH_STATUS_WAIT_TIMEOUT_MS);
|
|
35869
35897
|
const data = await waitForBashStatus(ctx, context, taskId, undefined, waitFor, effectiveWaitMs);
|
|
35870
35898
|
const waited = data.waited;
|
|
35871
35899
|
if (waited?.reason === "user_message") {
|
|
35872
|
-
const convertedText = await convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, args.once
|
|
35900
|
+
const convertedText = await convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, coerceBoolean(args.once, true));
|
|
35873
35901
|
const metadata2 = context.metadata;
|
|
35874
35902
|
metadata2?.({ taskId, status: data.status, waited, convertedToAsync: true });
|
|
35875
35903
|
return convertedText;
|
|
@@ -36068,7 +36096,7 @@ async function readNewTaskOutput(runtime, taskId, data, cursor) {
|
|
|
36068
36096
|
};
|
|
36069
36097
|
}
|
|
36070
36098
|
async function readFileBytesFrom(outputPath, cursor) {
|
|
36071
|
-
const handle = await
|
|
36099
|
+
const handle = await fs4.open(outputPath, "r");
|
|
36072
36100
|
try {
|
|
36073
36101
|
const chunks = [];
|
|
36074
36102
|
let offset = cursor;
|
|
@@ -36396,7 +36424,7 @@ function diagnosticsOnEditDefault(ctx) {
|
|
|
36396
36424
|
}
|
|
36397
36425
|
async function readCurrentFileForPreview(filePath) {
|
|
36398
36426
|
try {
|
|
36399
|
-
return await
|
|
36427
|
+
return await fs5.promises.readFile(filePath, "utf-8");
|
|
36400
36428
|
} catch (error50) {
|
|
36401
36429
|
if (error50 && typeof error50 === "object" && "code" in error50 && error50.code === "ENOENT") {
|
|
36402
36430
|
return "";
|
|
@@ -36426,7 +36454,7 @@ async function readPatchPreviewContent(virtualFiles, filePath, action, patchPath
|
|
|
36426
36454
|
return virtualContent;
|
|
36427
36455
|
}
|
|
36428
36456
|
try {
|
|
36429
|
-
return await
|
|
36457
|
+
return await fs5.promises.readFile(filePath, "utf-8");
|
|
36430
36458
|
} catch (error50) {
|
|
36431
36459
|
throw new Error(`Failed to ${action} ${patchPath}: ${formatError2(error50)}`);
|
|
36432
36460
|
}
|
|
@@ -36442,7 +36470,7 @@ async function buildApplyPatchPreview(hunks, projectRoot) {
|
|
|
36442
36470
|
switch (hunk.type) {
|
|
36443
36471
|
case "add": {
|
|
36444
36472
|
const virtualContent = virtualPatchContent(virtualFiles, filePath);
|
|
36445
|
-
const exists = virtualContent !== undefined ? virtualContent !== null :
|
|
36473
|
+
const exists = virtualContent !== undefined ? virtualContent !== null : fs5.existsSync(filePath);
|
|
36446
36474
|
if (exists) {
|
|
36447
36475
|
throw new Error(`Failed to create ${hunk.path}: file already exists. Use *** Update File: to modify, or *** Delete File: first if you want to replace it entirely.`);
|
|
36448
36476
|
}
|
|
@@ -36550,7 +36578,7 @@ function createReadTool(ctx) {
|
|
|
36550
36578
|
const label = isImage ? "Image" : "PDF";
|
|
36551
36579
|
let fileSize = 0;
|
|
36552
36580
|
try {
|
|
36553
|
-
const stat = await import("node:fs/promises").then((
|
|
36581
|
+
const stat = await import("node:fs/promises").then((fs6) => fs6.stat(filePath));
|
|
36554
36582
|
fileSize = stat.size;
|
|
36555
36583
|
} catch {}
|
|
36556
36584
|
const sizeStr = fileSize > 1048576 ? `${(fileSize / 1048576).toFixed(1)}MB` : fileSize > 1024 ? `${(fileSize / 1024).toFixed(0)}KB` : `${fileSize} bytes`;
|
|
@@ -36566,12 +36594,16 @@ function createReadTool(ctx) {
|
|
|
36566
36594
|
}
|
|
36567
36595
|
};
|
|
36568
36596
|
}
|
|
36569
|
-
|
|
36570
|
-
|
|
36571
|
-
|
|
36572
|
-
|
|
36573
|
-
|
|
36574
|
-
|
|
36597
|
+
const rawStartLine = coerceOptionalInt(args.startLine, "startLine", 1, Number.MAX_SAFE_INTEGER);
|
|
36598
|
+
const rawEndLine = coerceOptionalInt(args.endLine, "endLine", 1, Number.MAX_SAFE_INTEGER);
|
|
36599
|
+
const rawLimit = coerceOptionalInt(args.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
36600
|
+
const rawOffset = coerceOptionalInt(args.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
|
|
36601
|
+
let startLine = rawStartLine;
|
|
36602
|
+
let endLine = rawEndLine;
|
|
36603
|
+
if (startLine === undefined && rawOffset !== undefined) {
|
|
36604
|
+
startLine = rawOffset;
|
|
36605
|
+
if (rawLimit !== undefined) {
|
|
36606
|
+
endLine = rawOffset + rawLimit - 1;
|
|
36575
36607
|
}
|
|
36576
36608
|
}
|
|
36577
36609
|
const params = { file: filePath };
|
|
@@ -36579,8 +36611,8 @@ function createReadTool(ctx) {
|
|
|
36579
36611
|
params.start_line = startLine;
|
|
36580
36612
|
if (endLine !== undefined)
|
|
36581
36613
|
params.end_line = endLine;
|
|
36582
|
-
if (
|
|
36583
|
-
params.limit =
|
|
36614
|
+
if (rawLimit !== undefined && rawOffset === undefined)
|
|
36615
|
+
params.limit = rawLimit;
|
|
36584
36616
|
const data = await callBridge(ctx, context, "read", params);
|
|
36585
36617
|
if (data.success === false) {
|
|
36586
36618
|
throw new Error(data.message || "read failed");
|
|
@@ -36598,7 +36630,7 @@ function createReadTool(ctx) {
|
|
|
36598
36630
|
return { output: data.message, title: dp, metadata: { title: dp } };
|
|
36599
36631
|
}
|
|
36600
36632
|
let output = data.content;
|
|
36601
|
-
const agentSpecifiedRange =
|
|
36633
|
+
const agentSpecifiedRange = rawStartLine !== undefined || rawEndLine !== undefined || rawOffset !== undefined || rawLimit !== undefined;
|
|
36602
36634
|
const footer = formatReadFooter2(agentSpecifiedRange, data);
|
|
36603
36635
|
if (footer)
|
|
36604
36636
|
output += footer;
|
|
@@ -36779,6 +36811,7 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
36779
36811
|
return permissionDeniedResponse(denial2);
|
|
36780
36812
|
}
|
|
36781
36813
|
const params = { file: filePath };
|
|
36814
|
+
const occurrence = coerceOptionalInt(args.occurrence, "occurrence", 0, Number.MAX_SAFE_INTEGER);
|
|
36782
36815
|
let command;
|
|
36783
36816
|
if (typeof args.appendContent === "string") {
|
|
36784
36817
|
command = "edit_match";
|
|
@@ -36812,10 +36845,10 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
36812
36845
|
command = "edit_match";
|
|
36813
36846
|
params.match = args.oldString;
|
|
36814
36847
|
params.replacement = args.newString ?? "";
|
|
36815
|
-
if (args.replaceAll
|
|
36816
|
-
params.replace_all =
|
|
36817
|
-
if (
|
|
36818
|
-
params.occurrence =
|
|
36848
|
+
if (coerceBoolean(args.replaceAll))
|
|
36849
|
+
params.replace_all = true;
|
|
36850
|
+
if (occurrence !== undefined)
|
|
36851
|
+
params.occurrence = occurrence;
|
|
36819
36852
|
} else {
|
|
36820
36853
|
const hint = typeof args.content === "string" ? ` To write the whole file, use the '${writeToolName}' tool. To edit existing content, provide 'oldString' (and optionally 'newString'), 'symbol' + 'content', or an 'edits' array.` : " Provide 'oldString' (+ optional 'newString'), 'symbol' + 'content', or an 'edits' array.";
|
|
36821
36854
|
throw new Error(`edit: no edit mode resolved from arguments.${hint}`);
|
|
@@ -36979,7 +37012,7 @@ function createApplyPatchTool(ctx) {
|
|
|
36979
37012
|
const rememberAffectedPath = (abs) => {
|
|
36980
37013
|
affectedAbs.add(abs);
|
|
36981
37014
|
if (!initiallyExistsAbs.has(abs)) {
|
|
36982
|
-
initiallyExistsAbs.set(abs,
|
|
37015
|
+
initiallyExistsAbs.set(abs, fs5.existsSync(abs));
|
|
36983
37016
|
}
|
|
36984
37017
|
};
|
|
36985
37018
|
for (const h of hunks) {
|
|
@@ -37041,7 +37074,7 @@ function createApplyPatchTool(ctx) {
|
|
|
37041
37074
|
const filePath = resolvePathFromProjectRoot(projectRoot, hunk.path);
|
|
37042
37075
|
switch (hunk.type) {
|
|
37043
37076
|
case "add": {
|
|
37044
|
-
if (
|
|
37077
|
+
if (fs5.existsSync(filePath)) {
|
|
37045
37078
|
const msg = `Failed to create ${hunk.path}: file already exists. Use *** Update File: to modify, or *** Delete File: first if you want to replace it entirely.`;
|
|
37046
37079
|
results.push(msg);
|
|
37047
37080
|
failures.push({ index: hunkIndex, path: hunk.path });
|
|
@@ -37082,9 +37115,9 @@ function createApplyPatchTool(ctx) {
|
|
|
37082
37115
|
results.push(msg);
|
|
37083
37116
|
failures.push({ index: hunkIndex, path: hunk.path });
|
|
37084
37117
|
const filePath2 = resolvePathFromProjectRoot(projectRoot, hunk.path);
|
|
37085
|
-
if (
|
|
37118
|
+
if (fs5.existsSync(filePath2)) {
|
|
37086
37119
|
try {
|
|
37087
|
-
|
|
37120
|
+
fs5.rmSync(filePath2, { force: true });
|
|
37088
37121
|
} catch {}
|
|
37089
37122
|
}
|
|
37090
37123
|
}
|
|
@@ -37092,7 +37125,7 @@ function createApplyPatchTool(ctx) {
|
|
|
37092
37125
|
}
|
|
37093
37126
|
case "delete": {
|
|
37094
37127
|
try {
|
|
37095
|
-
const before = await
|
|
37128
|
+
const before = await fs5.promises.readFile(filePath, "utf-8").catch(() => "");
|
|
37096
37129
|
const deleteResult = await callBridge(ctx, context, "delete_file", {
|
|
37097
37130
|
file: filePath
|
|
37098
37131
|
});
|
|
@@ -37118,7 +37151,7 @@ function createApplyPatchTool(ctx) {
|
|
|
37118
37151
|
}
|
|
37119
37152
|
case "update": {
|
|
37120
37153
|
try {
|
|
37121
|
-
const original = await
|
|
37154
|
+
const original = await fs5.promises.readFile(filePath, "utf-8");
|
|
37122
37155
|
const newContent = applyUpdateChunks(original, filePath, hunk.chunks);
|
|
37123
37156
|
const targetPath = hunk.move_path ? resolvePathFromProjectRoot(projectRoot, hunk.move_path) : filePath;
|
|
37124
37157
|
const writeResult = await callBridge(ctx, context, "write", {
|
|
@@ -37183,7 +37216,7 @@ ${diagLines}`);
|
|
|
37183
37216
|
if (rollbackResult.success === false) {
|
|
37184
37217
|
throw new Error(rollbackResult.message ?? "checkpoint restore failed");
|
|
37185
37218
|
}
|
|
37186
|
-
if (newlyCreatedAbs.has(targetPath) &&
|
|
37219
|
+
if (newlyCreatedAbs.has(targetPath) && fs5.existsSync(targetPath)) {
|
|
37187
37220
|
const cleanupResult = await callBridge(ctx, context, "delete_file", {
|
|
37188
37221
|
file: targetPath
|
|
37189
37222
|
});
|
|
@@ -37312,7 +37345,7 @@ function createDeleteTool(ctx) {
|
|
|
37312
37345
|
if (inputs.length === 0) {
|
|
37313
37346
|
throw new Error("delete: `files` must be a non-empty array of paths");
|
|
37314
37347
|
}
|
|
37315
|
-
const recursive = args.recursive
|
|
37348
|
+
const recursive = coerceBoolean(args.recursive);
|
|
37316
37349
|
const projectRoot = await resolveProjectRoot(ctx, context);
|
|
37317
37350
|
const absolutePaths = inputs.map((f) => resolvePathFromProjectRoot(projectRoot, f));
|
|
37318
37351
|
{
|
|
@@ -37708,7 +37741,7 @@ function createInspectTier2IdleScheduler(options) {
|
|
|
37708
37741
|
}
|
|
37709
37742
|
function inspectTools(ctx) {
|
|
37710
37743
|
const inspectTool = {
|
|
37711
|
-
description: "Codebase health snapshot. One call returns summary stats for: TODOs, diagnostics, file/symbol metrics, dead code, unused exports, code duplicates. Pass `sections` for per-category drill-down details.\n\n" + "Categories run in tiers — Tier 1 (todos, metrics) return synchronously from cache. Tier 2 (dead_code, unused_exports, duplicates)
|
|
37744
|
+
description: "Codebase health snapshot. One call returns summary stats for: TODOs, diagnostics, file/symbol metrics, dead code, unused exports, code duplicates. Pass `sections` for per-category drill-down details.\n\n" + "Categories run in tiers — Tier 1 (todos, metrics) return synchronously from cache. Tier 2 (dead_code, unused_exports, duplicates) waits for a fresh reuse scan up to a short deadline; if a category is still scanning the response reports `complete: false` with `pending_categories: [...]` rather than a fabricated clean count.\n\n" + `Use when: starting work on unfamiliar code, after multi-edit batches to check diagnostics, before a refactor, before review, or to verify cleanup completeness.
|
|
37712
37745
|
|
|
37713
37746
|
` + "Treat `dead_code` as a hint, not proof: reachability is call-based, so symbols reached only via method dispatch or referenced only in type position may be false positives — verify before deleting.",
|
|
37714
37747
|
args: {
|
|
@@ -37723,7 +37756,9 @@ function inspectTools(ctx) {
|
|
|
37723
37756
|
return permissionDeniedResponse(scoped.denial);
|
|
37724
37757
|
const scope = scoped.scope;
|
|
37725
37758
|
const topK = args.topK === undefined || args.topK === null ? undefined : args.topK;
|
|
37726
|
-
const response = await callBridge(ctx, context, "inspect", { sections, scope, topK }
|
|
37759
|
+
const response = await callBridge(ctx, context, "inspect", { sections, scope, topK }, {
|
|
37760
|
+
keepBridgeOnTimeout: true
|
|
37761
|
+
});
|
|
37727
37762
|
if (response.success === false) {
|
|
37728
37763
|
throw new Error(response.message || "inspect failed");
|
|
37729
37764
|
}
|
|
@@ -37800,8 +37835,9 @@ function navigationTools(ctx) {
|
|
|
37800
37835
|
file: filePath,
|
|
37801
37836
|
symbol: args.symbol
|
|
37802
37837
|
};
|
|
37803
|
-
|
|
37804
|
-
|
|
37838
|
+
const depth = coerceOptionalInt(args.depth, "depth", 1, Number.MAX_SAFE_INTEGER);
|
|
37839
|
+
if (depth !== undefined)
|
|
37840
|
+
params.depth = depth;
|
|
37805
37841
|
if (!isEmptyParam(args.expression))
|
|
37806
37842
|
params.expression = args.expression;
|
|
37807
37843
|
if (!isEmptyParam(args.toSymbol))
|
|
@@ -37859,8 +37895,8 @@ function readingTools(ctx) {
|
|
|
37859
37895
|
files: z12.boolean().optional().describe("Directory-only mode: when true, target must be a directory or array of directories and the result is a flat file tree with path, language, symbol count, and byte size instead of a symbol outline.")
|
|
37860
37896
|
},
|
|
37861
37897
|
execute: async (args, context) => {
|
|
37862
|
-
const target = args.target;
|
|
37863
|
-
const filesMode = args.files
|
|
37898
|
+
const target = coerceTargetParam(args.target);
|
|
37899
|
+
const filesMode = coerceBoolean(args.files);
|
|
37864
37900
|
const hasUrl = typeof target === "string" && (target.startsWith("http://") || target.startsWith("https://"));
|
|
37865
37901
|
const isArray = Array.isArray(target) && target.length > 0;
|
|
37866
37902
|
if (filesMode) {
|
|
@@ -37988,7 +38024,8 @@ function readingTools(ctx) {
|
|
|
37988
38024
|
const hasUrl = !isEmptyParam(args.url);
|
|
37989
38025
|
const hasTargets = hasTargetsProvided(args.targets);
|
|
37990
38026
|
const hasSymbols = !isEmptyParam(args.symbols);
|
|
37991
|
-
const wantCallgraph = args.callgraph
|
|
38027
|
+
const wantCallgraph = coerceBoolean(args.callgraph);
|
|
38028
|
+
const contextLines = coerceOptionalInt(args.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
37992
38029
|
const zoomTitle = buildZoomTitle(args);
|
|
37993
38030
|
const zoomDisplay = { title: zoomTitle };
|
|
37994
38031
|
if (hasFilePath)
|
|
@@ -38000,8 +38037,8 @@ function readingTools(ctx) {
|
|
|
38000
38037
|
}
|
|
38001
38038
|
if (hasTargets)
|
|
38002
38039
|
zoomDisplay.targets = JSON.stringify(args.targets);
|
|
38003
|
-
if (
|
|
38004
|
-
zoomDisplay.contextLines =
|
|
38040
|
+
if (contextLines !== undefined)
|
|
38041
|
+
zoomDisplay.contextLines = contextLines;
|
|
38005
38042
|
if (wantCallgraph)
|
|
38006
38043
|
zoomDisplay.callgraph = true;
|
|
38007
38044
|
const withMeta = (output) => ({
|
|
@@ -38034,8 +38071,8 @@ function readingTools(ctx) {
|
|
|
38034
38071
|
file: resolvedTargets[index],
|
|
38035
38072
|
symbol: t.symbol
|
|
38036
38073
|
};
|
|
38037
|
-
if (
|
|
38038
|
-
params2.context_lines =
|
|
38074
|
+
if (contextLines !== undefined)
|
|
38075
|
+
params2.context_lines = contextLines;
|
|
38039
38076
|
if (wantCallgraph)
|
|
38040
38077
|
params2.callgraph = true;
|
|
38041
38078
|
return callBridge(ctx, context, "zoom", params2).catch((err) => ({
|
|
@@ -38067,8 +38104,8 @@ function readingTools(ctx) {
|
|
|
38067
38104
|
if (symbolsArray) {
|
|
38068
38105
|
const results = await Promise.all(symbolsArray.map((sym) => {
|
|
38069
38106
|
const params2 = { file: file2, symbol: sym };
|
|
38070
|
-
if (
|
|
38071
|
-
params2.context_lines =
|
|
38107
|
+
if (contextLines !== undefined)
|
|
38108
|
+
params2.context_lines = contextLines;
|
|
38072
38109
|
if (wantCallgraph)
|
|
38073
38110
|
params2.callgraph = true;
|
|
38074
38111
|
return callBridge(ctx, context, "zoom", params2).catch((err) => ({
|
|
@@ -38091,8 +38128,8 @@ function readingTools(ctx) {
|
|
|
38091
38128
|
return withMeta(formatZoomBatchResult(targetLabel, symbolsArray, results).text);
|
|
38092
38129
|
}
|
|
38093
38130
|
const params = { file: file2 };
|
|
38094
|
-
if (
|
|
38095
|
-
params.context_lines =
|
|
38131
|
+
if (contextLines !== undefined)
|
|
38132
|
+
params.context_lines = contextLines;
|
|
38096
38133
|
if (wantCallgraph)
|
|
38097
38134
|
params.callgraph = true;
|
|
38098
38135
|
const data = await callBridge(ctx, context, "zoom", params);
|
|
@@ -38274,6 +38311,9 @@ function refactoringTools(ctx) {
|
|
|
38274
38311
|
},
|
|
38275
38312
|
execute: async (args, context) => {
|
|
38276
38313
|
const op = args.op;
|
|
38314
|
+
const startLine = coerceOptionalInt(args.startLine, "startLine", 1, Number.MAX_SAFE_INTEGER);
|
|
38315
|
+
const endLine = coerceOptionalInt(args.endLine, "endLine", 1, Number.MAX_SAFE_INTEGER);
|
|
38316
|
+
const callSiteLine = coerceOptionalInt(args.callSiteLine, "callSiteLine", 1, Number.MAX_SAFE_INTEGER);
|
|
38277
38317
|
if ((op === "move" || op === "inline") && isEmptyParam(args.symbol)) {
|
|
38278
38318
|
throw new Error(`'symbol' is required for '${op}' op`);
|
|
38279
38319
|
}
|
|
@@ -38283,12 +38323,12 @@ function refactoringTools(ctx) {
|
|
|
38283
38323
|
if (op === "extract") {
|
|
38284
38324
|
if (isEmptyParam(args.name))
|
|
38285
38325
|
throw new Error("'name' is required for 'extract' op");
|
|
38286
|
-
if (
|
|
38326
|
+
if (startLine === undefined)
|
|
38287
38327
|
throw new Error("'startLine' is required for 'extract' op");
|
|
38288
|
-
if (
|
|
38328
|
+
if (endLine === undefined)
|
|
38289
38329
|
throw new Error("'endLine' is required for 'extract' op");
|
|
38290
38330
|
}
|
|
38291
|
-
if (op === "inline" &&
|
|
38331
|
+
if (op === "inline" && callSiteLine === undefined) {
|
|
38292
38332
|
throw new Error("'callSiteLine' is required for 'inline' op");
|
|
38293
38333
|
}
|
|
38294
38334
|
const filePath = await resolvePathArg(ctx, context, args.filePath);
|
|
@@ -38329,12 +38369,15 @@ function refactoringTools(ctx) {
|
|
|
38329
38369
|
break;
|
|
38330
38370
|
case "extract":
|
|
38331
38371
|
params.name = args.name;
|
|
38332
|
-
|
|
38333
|
-
|
|
38372
|
+
if (startLine === undefined || endLine === undefined) {
|
|
38373
|
+
throw new Error("'startLine' and 'endLine' are required for 'extract' op");
|
|
38374
|
+
}
|
|
38375
|
+
params.start_line = startLine;
|
|
38376
|
+
params.end_line = endLine + 1;
|
|
38334
38377
|
break;
|
|
38335
38378
|
case "inline":
|
|
38336
38379
|
params.symbol = args.symbol;
|
|
38337
|
-
params.call_site_line =
|
|
38380
|
+
params.call_site_line = callSiteLine;
|
|
38338
38381
|
break;
|
|
38339
38382
|
}
|
|
38340
38383
|
const hints = await queryLspHints(ctx.client, args.symbol ?? args.name, undefined, context.sessionID);
|
|
@@ -38497,7 +38540,7 @@ function safetyTools(ctx) {
|
|
|
38497
38540
|
}
|
|
38498
38541
|
|
|
38499
38542
|
// src/tools/search.ts
|
|
38500
|
-
import * as
|
|
38543
|
+
import * as fs6 from "node:fs";
|
|
38501
38544
|
import * as path7 from "node:path";
|
|
38502
38545
|
function arg2(schema) {
|
|
38503
38546
|
return schema;
|
|
@@ -38533,7 +38576,7 @@ function absoluteSearchPath(projectRoot, target) {
|
|
|
38533
38576
|
return resolvePathFromProjectRoot(projectRoot, expandTilde2(target));
|
|
38534
38577
|
}
|
|
38535
38578
|
function searchPathExists(projectRoot, target) {
|
|
38536
|
-
return
|
|
38579
|
+
return fs6.existsSync(absoluteSearchPath(projectRoot, target));
|
|
38537
38580
|
}
|
|
38538
38581
|
function splitSearchPathArg(projectRoot, raw) {
|
|
38539
38582
|
if (searchPathExists(projectRoot, raw) || !/\s/.test(raw)) {
|
|
@@ -38576,7 +38619,7 @@ ${note}` : note;
|
|
|
38576
38619
|
}
|
|
38577
38620
|
function searchPathKind(projectRoot, target, defaultKind) {
|
|
38578
38621
|
try {
|
|
38579
|
-
const stat =
|
|
38622
|
+
const stat = fs6.lstatSync(absoluteSearchPath(projectRoot, target));
|
|
38580
38623
|
if (defaultKind === "file") {
|
|
38581
38624
|
return stat.isDirectory() ? "directory" : "file";
|
|
38582
38625
|
}
|
|
@@ -38771,7 +38814,8 @@ function semanticTools(ctx) {
|
|
|
38771
38814
|
args: {
|
|
38772
38815
|
query: arg3(z15.string().describe("Concept, regex, literal text, filename, or capability to find. Examples: 'fuzzy match with whitespace tolerance', '^export', 'Cargo.lock'.")),
|
|
38773
38816
|
topK: arg3(optionalInt(1, 100).describe("Number of results (default: 10, max: 100)")),
|
|
38774
|
-
hint: arg3(z15.enum(["regex", "literal", "semantic", "auto"]).optional().describe("Optional routing hint. Defaults to 'auto'."))
|
|
38817
|
+
hint: arg3(z15.enum(["regex", "literal", "semantic", "auto"]).optional().describe("Optional routing hint. Defaults to 'auto'.")),
|
|
38818
|
+
includeTests: arg3(z15.boolean().optional().describe("Include test files (*.test.*, *_test.rs, __tests__/, …) plus test-support, fixture, mock, snapshot, and corpus files. Defaults to false."))
|
|
38775
38819
|
},
|
|
38776
38820
|
execute: async (args, context) => {
|
|
38777
38821
|
if (isEmptyParam(args.query) || typeof args.query !== "string" || args.query.trim().length === 0) {
|
|
@@ -38786,10 +38830,12 @@ function semanticTools(ctx) {
|
|
|
38786
38830
|
}
|
|
38787
38831
|
const bridgeParams = {
|
|
38788
38832
|
query,
|
|
38789
|
-
top_k: args.topK ?? 10
|
|
38833
|
+
top_k: coerceOptionalInt(args.topK, "topK", 1, 100) ?? 10
|
|
38790
38834
|
};
|
|
38791
38835
|
if (hint)
|
|
38792
38836
|
bridgeParams.hint = hint;
|
|
38837
|
+
if (typeof args.includeTests === "boolean")
|
|
38838
|
+
bridgeParams.include_tests = args.includeTests;
|
|
38793
38839
|
const response = await callBridge(ctx, context, "semantic_search", bridgeParams);
|
|
38794
38840
|
if (response.success === false) {
|
|
38795
38841
|
const message = typeof response.message === "string" && response.message.length > 0 ? response.message : "semantic_search failed";
|
|
@@ -38827,7 +38873,7 @@ function buildWorkflowHints(opts) {
|
|
|
38827
38873
|
const hasBgBash = hasBash && opts.bashBackgroundEnabled && !opts.disabledTools.has(bashStatusName);
|
|
38828
38874
|
if (hasBash && opts.bashCompressionEnabled) {
|
|
38829
38875
|
sections.push([
|
|
38830
|
-
"**Test/build output**: bash output is auto-compressed
|
|
38876
|
+
"**Test/build output**: bash output is auto-compressed for non-piped commands. Piped commands run verbatim and show the pipeline's output. For AFT's test/build summary, run the runner without filters:",
|
|
38831
38877
|
"- `bun test | grep fail` → run `bun test`",
|
|
38832
38878
|
"- `cargo test 2>&1 | tail -20` → run `cargo test`",
|
|
38833
38879
|
"- `npm run build | head -50` → run `npm run build`"
|
|
@@ -38866,11 +38912,9 @@ function buildWorkflowHints(opts) {
|
|
|
38866
38912
|
}
|
|
38867
38913
|
if (hasBash && hasBgBash) {
|
|
38868
38914
|
sections.push([
|
|
38869
|
-
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({
|
|
38870
|
-
"
|
|
38871
|
-
"
|
|
38872
|
-
"3. Want to react to a specific early output line → async `bash_watch` (background:true + pattern).",
|
|
38873
|
-
"Never loop `bash_status` to wait — it's a one-shot inspector, not a wait primitive."
|
|
38915
|
+
`**Long-running commands** (builds, installs, full test suites): run them in the FOREGROUND — \`${bashName}({ command })\` waits and returns the result in ONE step, and if it outlives the short wait window it auto-promotes to background and delivers a completion reminder when it finishes. Don't reach for \`background: true\` for the common "I'm waiting on this result" case — that costs an extra turn.`,
|
|
38916
|
+
"- `background: true` is ONLY for when you have OTHER useful work to do while it runs: start it, do the other work, and the completion reminder delivers the result (or spawn a subagent for the side work). Do NOT background a command and then immediately `bash_watch` it — that spends a whole extra turn waiting for something foreground returns in one.",
|
|
38917
|
+
"- `bash_watch` is for blocking on an ALREADY-backgrounded task once you've run out of parallel work (sync — the user can interrupt), or reacting to a specific early output line (async: background:true + pattern). Never loop `bash_status` to wait — it's a one-shot inspector."
|
|
38874
38918
|
].join(`
|
|
38875
38919
|
`));
|
|
38876
38920
|
sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`${bashStatusName}({ taskId, outputMode: "screen" })\`, and send input with \`${bashWriteName}({ taskId, input: "..." })\`.`);
|
|
@@ -38902,11 +38946,20 @@ function buildHintsFromConfig(config2, disabledTools) {
|
|
|
38902
38946
|
setActiveLogger(bridgeLogger);
|
|
38903
38947
|
var STATUS_COMMAND = "aft-status";
|
|
38904
38948
|
var SENTINEL_PREFIX = "__AFT_STATUS_";
|
|
38905
|
-
|
|
38906
|
-
|
|
38907
|
-
|
|
38949
|
+
var HTTP_SERVER_RESPONSE_TYPE_ID = "~effect/http/HttpServerResponse";
|
|
38950
|
+
var HTTP_COOKIES_TYPE_ID = "~effect/http/Cookies";
|
|
38951
|
+
var HTTP_BODY_TYPE_ID = "~effect/http/HttpBody";
|
|
38952
|
+
var ERROR_REPORTER_IGNORE = "~effect/ErrorReporter/ignore";
|
|
38908
38953
|
function throwSentinel(command) {
|
|
38909
|
-
|
|
38954
|
+
const sentinel = new Error(`${SENTINEL_PREFIX}${command.toUpperCase().replace(/-/g, "_")}_HANDLED__`);
|
|
38955
|
+
sentinel[HTTP_SERVER_RESPONSE_TYPE_ID] = HTTP_SERVER_RESPONSE_TYPE_ID;
|
|
38956
|
+
sentinel[ERROR_REPORTER_IGNORE] = true;
|
|
38957
|
+
sentinel.status = 204;
|
|
38958
|
+
sentinel.statusText = undefined;
|
|
38959
|
+
sentinel.headers = {};
|
|
38960
|
+
sentinel.cookies = { [HTTP_COOKIES_TYPE_ID]: HTTP_COOKIES_TYPE_ID, cookies: {} };
|
|
38961
|
+
sentinel.body = { [HTTP_BODY_TYPE_ID]: HTTP_BODY_TYPE_ID, _tag: "Empty" };
|
|
38962
|
+
throw sentinel;
|
|
38910
38963
|
}
|
|
38911
38964
|
var PLUGIN_VERSION = (() => {
|
|
38912
38965
|
try {
|
|
@@ -38916,30 +38969,35 @@ var PLUGIN_VERSION = (() => {
|
|
|
38916
38969
|
return "0.0.0";
|
|
38917
38970
|
}
|
|
38918
38971
|
})();
|
|
38919
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
38972
|
+
var ANNOUNCEMENT_VERSION = "0.40.0";
|
|
38920
38973
|
var ANNOUNCEMENT_FEATURES = [
|
|
38921
|
-
"
|
|
38922
|
-
"
|
|
38923
|
-
"
|
|
38924
|
-
"
|
|
38974
|
+
"Groundwork for Subconscious, the CortexKit daemon: configuration now lives in one place under ~/.config/cortexkit/ and <project>/.cortexkit/ (auto-migrated). The daemon integration is dormant — nothing changes in how you run AFT today.",
|
|
38975
|
+
"Bash output never hides a failure: piped commands run exactly as written with their real exit status, cancelled tasks report as failed, and a failing run is never compressed to a clean-looking summary.",
|
|
38976
|
+
"The top aft_search result is now the complete, ready-to-edit symbol (no re-read needed); test files are hidden from results by default.",
|
|
38977
|
+
"format_on_edit is now off by default, and bash commands default to the foreground."
|
|
38925
38978
|
];
|
|
38926
38979
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
38927
38980
|
var plugin = async (input) => initializePluginForDirectory(input);
|
|
38928
38981
|
async function initializePluginForDirectory(input) {
|
|
38929
38982
|
const binaryPath = await findBinary(PLUGIN_VERSION);
|
|
38930
38983
|
await ensureStorageMigrated({ harness: "opencode", binaryPath, logger: bridgeLogger });
|
|
38984
|
+
const deliverConfigMigrationWarnings = (directory, messages) => {
|
|
38985
|
+
for (const message of messages) {
|
|
38986
|
+
sendWarning({ client: input.client, directory }, message).catch((err) => {
|
|
38987
|
+
warn2(`[config] failed to deliver migration warning: ${err}`);
|
|
38988
|
+
});
|
|
38989
|
+
}
|
|
38990
|
+
};
|
|
38991
|
+
deliverConfigMigrationWarnings(input.directory, migrateAftConfigLocations(input.directory, bridgeLogger).flatMap((result) => result.warnings));
|
|
38931
38992
|
const aftConfig = loadAftConfig(input.directory);
|
|
38932
38993
|
enqueueConfigParseWarnings(input.directory, getConfigLoadErrors());
|
|
38933
38994
|
const autoUpdateAbort = new AbortController;
|
|
38934
|
-
const
|
|
38935
|
-
|
|
38936
|
-
bash_permissions: true
|
|
38937
|
-
|
|
38938
|
-
|
|
38939
|
-
configOverrides.url_fetch_allow_private = aftConfig.url_fetch_allow_private;
|
|
38940
|
-
}
|
|
38995
|
+
const storageDir = resolveCortexKitStorageRoot();
|
|
38996
|
+
const configOverrides = buildConfigTierConfigureParams(input.directory, {
|
|
38997
|
+
bash_permissions: true,
|
|
38998
|
+
storage_dir: storageDir
|
|
38999
|
+
});
|
|
38941
39000
|
const isFastembedSemanticBackend = (aftConfig.semantic?.backend ?? "fastembed") === "fastembed";
|
|
38942
|
-
configOverrides.storage_dir = resolveCortexKitStorageRoot();
|
|
38943
39001
|
let onnxRuntimePromise = null;
|
|
38944
39002
|
if (aftConfig.semantic_search && isFastembedSemanticBackend) {
|
|
38945
39003
|
const storageDir2 = configOverrides.storage_dir;
|
|
@@ -39019,11 +39077,10 @@ ${lines}
|
|
|
39019
39077
|
minVersion: PLUGIN_VERSION,
|
|
39020
39078
|
projectConfigLoader: (projectRoot) => {
|
|
39021
39079
|
try {
|
|
39022
|
-
|
|
39023
|
-
|
|
39024
|
-
return resolveProjectOverridesForConfigure(projectConfig);
|
|
39080
|
+
deliverConfigMigrationWarnings(projectRoot, migrateAftConfigLocations(projectRoot, bridgeLogger).flatMap((result) => result.warnings));
|
|
39081
|
+
return buildConfigTierConfigureParams(projectRoot);
|
|
39025
39082
|
} catch (err) {
|
|
39026
|
-
warn2(`
|
|
39083
|
+
warn2(`readConfigTiers(${projectRoot}) failed; falling back to plugin-init config: ${err instanceof Error ? err.message : String(err)}`);
|
|
39027
39084
|
return {};
|
|
39028
39085
|
}
|
|
39029
39086
|
},
|
|
@@ -39055,7 +39112,7 @@ ${lines}
|
|
|
39055
39112
|
versionUpgradePromises.set(minVersion, upgradePromise);
|
|
39056
39113
|
return upgradePromise;
|
|
39057
39114
|
},
|
|
39058
|
-
onConfigureWarnings: ({ projectRoot, sessionId, client, warnings }) => {
|
|
39115
|
+
onConfigureWarnings: ({ projectRoot, sessionId, client, warnings, configDroppedKeys }) => {
|
|
39059
39116
|
const bridge = pool.getActiveBridgeForRoot(projectRoot);
|
|
39060
39117
|
if (!bridge)
|
|
39061
39118
|
return;
|
|
@@ -39066,6 +39123,7 @@ ${lines}
|
|
|
39066
39123
|
client,
|
|
39067
39124
|
bridge,
|
|
39068
39125
|
warnings,
|
|
39126
|
+
configDroppedKeys,
|
|
39069
39127
|
fallbackClient: input.client,
|
|
39070
39128
|
storageDir: configOverrides.storage_dir,
|
|
39071
39129
|
pluginVersion: PLUGIN_VERSION,
|
|
@@ -39175,7 +39233,12 @@ ${lines}
|
|
|
39175
39233
|
}
|
|
39176
39234
|
return { ...response, served_directory: servedDirectory };
|
|
39177
39235
|
});
|
|
39178
|
-
|
|
39236
|
+
rpcServer.handle("consume-notifications", async (params) => {
|
|
39237
|
+
const rawLastReceivedId = Number(params.lastReceivedId ?? 0);
|
|
39238
|
+
const sessionId = typeof params.sessionId === "string" && params.sessionId.length > 0 ? params.sessionId : undefined;
|
|
39239
|
+
const messages = drainNotifications(Number.isFinite(rawLastReceivedId) ? rawLastReceivedId : 0, sessionId);
|
|
39240
|
+
return { messages };
|
|
39241
|
+
});
|
|
39179
39242
|
rpcServer.handle("get-announcement", async () => {
|
|
39180
39243
|
if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0) {
|
|
39181
39244
|
return { show: false };
|
|
@@ -39374,9 +39437,13 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
39374
39437
|
inspectTier2Idle.clear(toolInput.sessionID);
|
|
39375
39438
|
},
|
|
39376
39439
|
"command.execute.before": async (commandInput, _output) => {
|
|
39377
|
-
if (
|
|
39440
|
+
if (commandInput.command !== STATUS_COMMAND) {
|
|
39378
39441
|
return;
|
|
39379
39442
|
}
|
|
39443
|
+
if (isTuiConnected(commandInput.sessionID)) {
|
|
39444
|
+
pushNotification("action", { action: "show-status-dialog", sessionId: commandInput.sessionID }, commandInput.sessionID);
|
|
39445
|
+
throwSentinel(commandInput.command);
|
|
39446
|
+
}
|
|
39380
39447
|
const sessionDir = await getSessionDirectory(input.client, commandInput.sessionID, input.directory) ?? input.directory;
|
|
39381
39448
|
const bridge = ctx.pool.getActiveBridgeForRoot(sessionDir) ?? ctx.pool.getBridge(sessionDir);
|
|
39382
39449
|
const cached2 = bridge.getCachedStatus();
|