@cortexkit/aft-opencode 0.17.0 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/cache.d.ts +12 -0
- package/dist/hooks/auto-update-checker/cache.d.ts.map +1 -0
- package/dist/hooks/auto-update-checker/checker.d.ts +13 -0
- package/dist/hooks/auto-update-checker/checker.d.ts.map +1 -0
- package/dist/hooks/auto-update-checker/constants.d.ts +10 -0
- package/dist/hooks/auto-update-checker/constants.d.ts.map +1 -0
- package/dist/hooks/auto-update-checker/index.d.ts +12 -0
- package/dist/hooks/auto-update-checker/index.d.ts.map +1 -0
- package/dist/hooks/auto-update-checker/types.d.ts +31 -0
- package/dist/hooks/auto-update-checker/types.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1125 -257
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/lsp-cache.d.ts.map +1 -1
- package/dist/lsp-github-install.d.ts +10 -1
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/lsp-github-probe.d.ts +10 -0
- package/dist/lsp-github-probe.d.ts.map +1 -1
- package/dist/onnx-runtime.d.ts +21 -3
- package/dist/onnx-runtime.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/lsp.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -7803,10 +7803,10 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
7803
7803
|
});
|
|
7804
7804
|
|
|
7805
7805
|
// src/index.ts
|
|
7806
|
-
import { existsSync as
|
|
7806
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
7807
7807
|
import { createRequire as createRequire2 } from "module";
|
|
7808
|
-
import { homedir as
|
|
7809
|
-
import { join as
|
|
7808
|
+
import { homedir as homedir10 } from "os";
|
|
7809
|
+
import { join as join19 } from "path";
|
|
7810
7810
|
|
|
7811
7811
|
// src/config.ts
|
|
7812
7812
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
@@ -21481,7 +21481,8 @@ var AftConfigSchema = exports_external.object({
|
|
|
21481
21481
|
lsp: LspConfigSchema.optional(),
|
|
21482
21482
|
url_fetch_allow_private: exports_external.boolean().optional(),
|
|
21483
21483
|
semantic: SemanticConfigSchema.optional(),
|
|
21484
|
-
max_callgraph_files: exports_external.number().int().positive().optional()
|
|
21484
|
+
max_callgraph_files: exports_external.number().int().positive().optional(),
|
|
21485
|
+
auto_update: exports_external.boolean().optional()
|
|
21485
21486
|
});
|
|
21486
21487
|
function normalizeLspExtension(extension) {
|
|
21487
21488
|
return extension.trim().replace(/^\.+/, "");
|
|
@@ -21665,6 +21666,8 @@ function getStrippedTopLevelKeys(override) {
|
|
|
21665
21666
|
stripped.push("url_fetch_allow_private");
|
|
21666
21667
|
if (override.max_callgraph_files !== undefined)
|
|
21667
21668
|
stripped.push("max_callgraph_files");
|
|
21669
|
+
if (override.auto_update !== undefined)
|
|
21670
|
+
stripped.push("auto_update");
|
|
21668
21671
|
return stripped;
|
|
21669
21672
|
}
|
|
21670
21673
|
function mergeConfigs(base, override) {
|
|
@@ -21869,52 +21872,565 @@ async function fetchLatestTag() {
|
|
|
21869
21872
|
}
|
|
21870
21873
|
}
|
|
21871
21874
|
|
|
21872
|
-
// src/
|
|
21875
|
+
// src/hooks/auto-update-checker/cache.ts
|
|
21876
|
+
var import_comment_json3 = __toESM(require_src2(), 1);
|
|
21873
21877
|
import { spawn } from "child_process";
|
|
21878
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
21879
|
+
import { basename, dirname as dirname2, join as join6 } from "path";
|
|
21880
|
+
|
|
21881
|
+
// src/hooks/auto-update-checker/checker.ts
|
|
21882
|
+
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
21883
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, statSync, writeFileSync } from "fs";
|
|
21884
|
+
import { homedir as homedir4 } from "os";
|
|
21885
|
+
import { dirname, isAbsolute, join as join5, resolve } from "path";
|
|
21886
|
+
import { fileURLToPath } from "url";
|
|
21887
|
+
|
|
21888
|
+
// src/hooks/auto-update-checker/constants.ts
|
|
21889
|
+
import { homedir as homedir3, platform } from "os";
|
|
21890
|
+
import { join as join4 } from "path";
|
|
21891
|
+
var PACKAGE_NAME = "@cortexkit/aft-opencode";
|
|
21892
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
21893
|
+
var NPM_FETCH_TIMEOUT = 1e4;
|
|
21894
|
+
function getOpenCodeCacheRoot() {
|
|
21895
|
+
if (platform() === "win32") {
|
|
21896
|
+
return join4(process.env.LOCALAPPDATA ?? homedir3(), "opencode");
|
|
21897
|
+
}
|
|
21898
|
+
return join4(homedir3(), ".cache", "opencode");
|
|
21899
|
+
}
|
|
21900
|
+
function getOpenCodeConfigRoot() {
|
|
21901
|
+
if (platform() === "win32") {
|
|
21902
|
+
return join4(process.env.APPDATA ?? join4(homedir3(), "AppData", "Roaming"), "opencode");
|
|
21903
|
+
}
|
|
21904
|
+
return join4(process.env.XDG_CONFIG_HOME ?? join4(homedir3(), ".config"), "opencode");
|
|
21905
|
+
}
|
|
21906
|
+
var CACHE_DIR = join4(getOpenCodeCacheRoot(), "packages");
|
|
21907
|
+
var USER_OPENCODE_CONFIG = join4(getOpenCodeConfigRoot(), "opencode.json");
|
|
21908
|
+
var USER_OPENCODE_CONFIG_JSONC = join4(getOpenCodeConfigRoot(), "opencode.jsonc");
|
|
21909
|
+
|
|
21910
|
+
// src/hooks/auto-update-checker/types.ts
|
|
21911
|
+
var NpmPackageEnvelopeSchema = exports_external.object({
|
|
21912
|
+
"dist-tags": exports_external.record(exports_external.string(), exports_external.string()).optional().default({})
|
|
21913
|
+
});
|
|
21914
|
+
var OpencodePluginTupleSchema = exports_external.tuple([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]);
|
|
21915
|
+
var OpencodeConfigSchema = exports_external.object({
|
|
21916
|
+
plugin: exports_external.array(exports_external.union([exports_external.string(), OpencodePluginTupleSchema])).optional()
|
|
21917
|
+
});
|
|
21918
|
+
var PackageJsonSchema = exports_external.object({
|
|
21919
|
+
name: exports_external.string().optional(),
|
|
21920
|
+
version: exports_external.string().optional(),
|
|
21921
|
+
dependencies: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
21922
|
+
}).passthrough();
|
|
21923
|
+
|
|
21924
|
+
// src/hooks/auto-update-checker/checker.ts
|
|
21925
|
+
function isString(value) {
|
|
21926
|
+
return typeof value === "string";
|
|
21927
|
+
}
|
|
21928
|
+
function pluginSpecifier(entry) {
|
|
21929
|
+
return typeof entry === "string" ? entry : entry[0];
|
|
21930
|
+
}
|
|
21931
|
+
function getPluginEntries(config2) {
|
|
21932
|
+
const parsed = OpencodeConfigSchema.safeParse(config2);
|
|
21933
|
+
if (!parsed.success)
|
|
21934
|
+
return [];
|
|
21935
|
+
return (parsed.data.plugin ?? []).map(pluginSpecifier).filter(isString);
|
|
21936
|
+
}
|
|
21937
|
+
function parseJsonConfig(content) {
|
|
21938
|
+
try {
|
|
21939
|
+
return import_comment_json2.parse(content);
|
|
21940
|
+
} catch (err) {
|
|
21941
|
+
warn(`[auto-update-checker] Failed to parse OpenCode config: ${String(err)}`);
|
|
21942
|
+
return null;
|
|
21943
|
+
}
|
|
21944
|
+
}
|
|
21945
|
+
function isPrereleaseVersion(version2) {
|
|
21946
|
+
return version2.includes("-");
|
|
21947
|
+
}
|
|
21948
|
+
function isDistTag(version2) {
|
|
21949
|
+
return !/^\d/.test(version2);
|
|
21950
|
+
}
|
|
21951
|
+
function extractChannel(version2) {
|
|
21952
|
+
if (!version2)
|
|
21953
|
+
return "latest";
|
|
21954
|
+
if (isDistTag(version2))
|
|
21955
|
+
return version2;
|
|
21956
|
+
if (isPrereleaseVersion(version2)) {
|
|
21957
|
+
const prereleasePart = version2.split("-")[1];
|
|
21958
|
+
const channelMatch = prereleasePart?.match(/^(alpha|beta|rc|canary|next)/);
|
|
21959
|
+
if (channelMatch?.[1])
|
|
21960
|
+
return channelMatch[1];
|
|
21961
|
+
}
|
|
21962
|
+
return "latest";
|
|
21963
|
+
}
|
|
21964
|
+
function getConfigPaths(directory) {
|
|
21965
|
+
return [
|
|
21966
|
+
join5(directory, ".opencode", "opencode.json"),
|
|
21967
|
+
join5(directory, ".opencode", "opencode.jsonc"),
|
|
21968
|
+
USER_OPENCODE_CONFIG,
|
|
21969
|
+
USER_OPENCODE_CONFIG_JSONC
|
|
21970
|
+
];
|
|
21971
|
+
}
|
|
21972
|
+
function resolvePathPluginSpec(spec, configPath) {
|
|
21973
|
+
if (spec.startsWith("file://")) {
|
|
21974
|
+
try {
|
|
21975
|
+
return fileURLToPath(spec);
|
|
21976
|
+
} catch {
|
|
21977
|
+
return spec.replace(/^file:\/\//, "");
|
|
21978
|
+
}
|
|
21979
|
+
}
|
|
21980
|
+
if (isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec))
|
|
21981
|
+
return spec;
|
|
21982
|
+
return resolve(dirname(configPath), spec);
|
|
21983
|
+
}
|
|
21984
|
+
function getLocalDevPath(directory) {
|
|
21985
|
+
for (const configPath of getConfigPaths(directory)) {
|
|
21986
|
+
try {
|
|
21987
|
+
if (!existsSync3(configPath))
|
|
21988
|
+
continue;
|
|
21989
|
+
const rawConfig = parseJsonConfig(readFileSync2(configPath, "utf-8"));
|
|
21990
|
+
const plugins = getPluginEntries(rawConfig);
|
|
21991
|
+
for (const entry of plugins) {
|
|
21992
|
+
if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
|
|
21993
|
+
continue;
|
|
21994
|
+
if (entry.startsWith("file://") || entry.startsWith(".") || isAbsolute(entry)) {
|
|
21995
|
+
const localPath = resolvePathPluginSpec(entry, configPath);
|
|
21996
|
+
const pkgPath = findPackageJsonUp(localPath);
|
|
21997
|
+
if (!pkgPath)
|
|
21998
|
+
continue;
|
|
21999
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync2(pkgPath, "utf-8")));
|
|
22000
|
+
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
22001
|
+
return localPath;
|
|
22002
|
+
}
|
|
22003
|
+
}
|
|
22004
|
+
} catch {}
|
|
22005
|
+
}
|
|
22006
|
+
return null;
|
|
22007
|
+
}
|
|
22008
|
+
function findPackageJsonUp(startPath) {
|
|
22009
|
+
try {
|
|
22010
|
+
const stat = statSync(startPath);
|
|
22011
|
+
let dir = stat.isDirectory() ? startPath : dirname(startPath);
|
|
22012
|
+
for (let i = 0;i < 10; i++) {
|
|
22013
|
+
const pkgPath = join5(dir, "package.json");
|
|
22014
|
+
if (existsSync3(pkgPath)) {
|
|
22015
|
+
try {
|
|
22016
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync2(pkgPath, "utf-8")));
|
|
22017
|
+
if (pkg.success && pkg.data.name === PACKAGE_NAME)
|
|
22018
|
+
return pkgPath;
|
|
22019
|
+
} catch {}
|
|
22020
|
+
}
|
|
22021
|
+
const parent = dirname(dir);
|
|
22022
|
+
if (parent === dir)
|
|
22023
|
+
break;
|
|
22024
|
+
dir = parent;
|
|
22025
|
+
}
|
|
22026
|
+
} catch {}
|
|
22027
|
+
return null;
|
|
22028
|
+
}
|
|
22029
|
+
function getLocalDevVersion(directory) {
|
|
22030
|
+
const localPath = getLocalDevPath(directory);
|
|
22031
|
+
if (!localPath)
|
|
22032
|
+
return null;
|
|
22033
|
+
try {
|
|
22034
|
+
const pkgPath = findPackageJsonUp(localPath);
|
|
22035
|
+
if (!pkgPath)
|
|
22036
|
+
return null;
|
|
22037
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync2(pkgPath, "utf-8")));
|
|
22038
|
+
return pkg.success ? pkg.data.version ?? null : null;
|
|
22039
|
+
} catch {
|
|
22040
|
+
return null;
|
|
22041
|
+
}
|
|
22042
|
+
}
|
|
22043
|
+
function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
|
|
22044
|
+
try {
|
|
22045
|
+
return findPackageJsonUp(dirname(fileURLToPath(currentModuleUrl)));
|
|
22046
|
+
} catch (err) {
|
|
22047
|
+
warn(`[auto-update-checker] Failed to resolve runtime package path: ${String(err)}`);
|
|
22048
|
+
return null;
|
|
22049
|
+
}
|
|
22050
|
+
}
|
|
22051
|
+
function findPluginEntry(directory) {
|
|
22052
|
+
for (const configPath of getConfigPaths(directory)) {
|
|
22053
|
+
try {
|
|
22054
|
+
if (!existsSync3(configPath))
|
|
22055
|
+
continue;
|
|
22056
|
+
const rawConfig = parseJsonConfig(readFileSync2(configPath, "utf-8"));
|
|
22057
|
+
const plugins = getPluginEntries(rawConfig);
|
|
22058
|
+
for (const entry of plugins) {
|
|
22059
|
+
if (entry === PACKAGE_NAME) {
|
|
22060
|
+
return { entry, isPinned: false, pinnedVersion: null, configPath };
|
|
22061
|
+
}
|
|
22062
|
+
if (entry.startsWith(`${PACKAGE_NAME}@`)) {
|
|
22063
|
+
const pinnedVersion = entry.slice(PACKAGE_NAME.length + 1);
|
|
22064
|
+
const isPinned = pinnedVersion !== "latest";
|
|
22065
|
+
return { entry, isPinned, pinnedVersion: isPinned ? pinnedVersion : null, configPath };
|
|
22066
|
+
}
|
|
22067
|
+
}
|
|
22068
|
+
} catch {}
|
|
22069
|
+
}
|
|
22070
|
+
return null;
|
|
22071
|
+
}
|
|
22072
|
+
var cachedPackageVersion = null;
|
|
22073
|
+
function getSpecCachePackageJsonPath(spec) {
|
|
22074
|
+
return join5(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
|
|
22075
|
+
}
|
|
22076
|
+
function getCachedVersion(spec) {
|
|
22077
|
+
if (!spec && cachedPackageVersion)
|
|
22078
|
+
return cachedPackageVersion;
|
|
22079
|
+
const candidates = [
|
|
22080
|
+
getCurrentRuntimePackageJsonPath(),
|
|
22081
|
+
spec ? getSpecCachePackageJsonPath(spec) : null,
|
|
22082
|
+
getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
|
|
22083
|
+
join5(homedir4(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
|
|
22084
|
+
].filter(isString);
|
|
22085
|
+
for (const packageJsonPath of candidates) {
|
|
22086
|
+
try {
|
|
22087
|
+
if (!existsSync3(packageJsonPath))
|
|
22088
|
+
continue;
|
|
22089
|
+
const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync2(packageJsonPath, "utf-8")));
|
|
22090
|
+
if (pkg.success && pkg.data.version) {
|
|
22091
|
+
if (!spec)
|
|
22092
|
+
cachedPackageVersion = pkg.data.version;
|
|
22093
|
+
return pkg.data.version;
|
|
22094
|
+
}
|
|
22095
|
+
} catch {}
|
|
22096
|
+
}
|
|
22097
|
+
return null;
|
|
22098
|
+
}
|
|
22099
|
+
function buildRegistryUrl(registryUrl) {
|
|
22100
|
+
return `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(PACKAGE_NAME).replace("%2F", "/")}`;
|
|
22101
|
+
}
|
|
22102
|
+
async function getLatestVersion(channel = "latest", options = {}) {
|
|
22103
|
+
const controller = new AbortController;
|
|
22104
|
+
const timeoutId = setTimeout(() => controller.abort(), options.timeoutMs ?? NPM_FETCH_TIMEOUT);
|
|
22105
|
+
const abortHandler = () => controller.abort();
|
|
22106
|
+
options.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
22107
|
+
try {
|
|
22108
|
+
if (options.signal?.aborted)
|
|
22109
|
+
return null;
|
|
22110
|
+
const response = await fetch(buildRegistryUrl(options.registryUrl ?? NPM_REGISTRY_URL), {
|
|
22111
|
+
signal: controller.signal,
|
|
22112
|
+
headers: { Accept: "application/json" }
|
|
22113
|
+
});
|
|
22114
|
+
if (!response.ok)
|
|
22115
|
+
return null;
|
|
22116
|
+
const data = NpmPackageEnvelopeSchema.safeParse(await response.json());
|
|
22117
|
+
if (!data.success)
|
|
22118
|
+
return null;
|
|
22119
|
+
return data.data["dist-tags"][channel] ?? data.data["dist-tags"].latest ?? null;
|
|
22120
|
+
} catch {
|
|
22121
|
+
return null;
|
|
22122
|
+
} finally {
|
|
22123
|
+
options.signal?.removeEventListener("abort", abortHandler);
|
|
22124
|
+
clearTimeout(timeoutId);
|
|
22125
|
+
}
|
|
22126
|
+
}
|
|
22127
|
+
|
|
22128
|
+
// src/hooks/auto-update-checker/cache.ts
|
|
22129
|
+
function stripPackageNameFromPath(pathValue, packageName) {
|
|
22130
|
+
let current = pathValue;
|
|
22131
|
+
for (const segment of [...packageName.split("/")].reverse()) {
|
|
22132
|
+
if (basename(current) !== segment)
|
|
22133
|
+
return null;
|
|
22134
|
+
current = dirname2(current);
|
|
22135
|
+
}
|
|
22136
|
+
return current;
|
|
22137
|
+
}
|
|
22138
|
+
function removeFromBunLock(installDir, packageName) {
|
|
22139
|
+
const lockPath = join6(installDir, "bun.lock");
|
|
22140
|
+
if (!existsSync4(lockPath))
|
|
22141
|
+
return false;
|
|
22142
|
+
try {
|
|
22143
|
+
const lock = import_comment_json3.parse(readFileSync3(lockPath, "utf-8"));
|
|
22144
|
+
let modified = false;
|
|
22145
|
+
if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
|
|
22146
|
+
delete lock.workspaces[""].dependencies[packageName];
|
|
22147
|
+
modified = true;
|
|
22148
|
+
}
|
|
22149
|
+
if (lock.packages?.[packageName]) {
|
|
22150
|
+
delete lock.packages[packageName];
|
|
22151
|
+
modified = true;
|
|
22152
|
+
}
|
|
22153
|
+
if (modified) {
|
|
22154
|
+
writeFileSync2(lockPath, JSON.stringify(lock, null, 2));
|
|
22155
|
+
log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
|
|
22156
|
+
}
|
|
22157
|
+
return modified;
|
|
22158
|
+
} catch {
|
|
22159
|
+
return false;
|
|
22160
|
+
}
|
|
22161
|
+
}
|
|
22162
|
+
function ensureDependencyVersion(packageJsonPath, packageName, version2) {
|
|
22163
|
+
if (!existsSync4(packageJsonPath))
|
|
22164
|
+
return false;
|
|
22165
|
+
try {
|
|
22166
|
+
const raw = import_comment_json3.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
22167
|
+
const pkgJson = PackageJsonSchema.safeParse(raw);
|
|
22168
|
+
if (!pkgJson.success)
|
|
22169
|
+
return false;
|
|
22170
|
+
const nextPackageJson = { ...pkgJson.data };
|
|
22171
|
+
const dependencies = { ...nextPackageJson.dependencies ?? {} };
|
|
22172
|
+
if (dependencies[packageName] === version2)
|
|
22173
|
+
return true;
|
|
22174
|
+
dependencies[packageName] = version2;
|
|
22175
|
+
nextPackageJson.dependencies = dependencies;
|
|
22176
|
+
writeFileSync2(packageJsonPath, JSON.stringify(nextPackageJson, null, 2));
|
|
22177
|
+
log(`[auto-update-checker] Updated dependency in package.json: ${packageName} \u2192 ${version2}`);
|
|
22178
|
+
return true;
|
|
22179
|
+
} catch (err) {
|
|
22180
|
+
warn(`[auto-update-checker] Failed to update package.json dependency: ${String(err)}`);
|
|
22181
|
+
return false;
|
|
22182
|
+
}
|
|
22183
|
+
}
|
|
22184
|
+
function removeInstalledPackage(installDir, packageName) {
|
|
22185
|
+
const packageDir = join6(installDir, "node_modules", packageName);
|
|
22186
|
+
if (!existsSync4(packageDir))
|
|
22187
|
+
return false;
|
|
22188
|
+
rmSync(packageDir, { recursive: true, force: true });
|
|
22189
|
+
log(`[auto-update-checker] Package removed: ${packageDir}`);
|
|
22190
|
+
return true;
|
|
22191
|
+
}
|
|
22192
|
+
function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
|
|
22193
|
+
if (runtimePackageJsonPath) {
|
|
22194
|
+
const packageDir = dirname2(runtimePackageJsonPath);
|
|
22195
|
+
const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
|
|
22196
|
+
if (nodeModulesDir && basename(nodeModulesDir) === "node_modules") {
|
|
22197
|
+
const installDir = dirname2(nodeModulesDir);
|
|
22198
|
+
const packageJsonPath = join6(installDir, "package.json");
|
|
22199
|
+
if (existsSync4(packageJsonPath))
|
|
22200
|
+
return { installDir, packageJsonPath };
|
|
22201
|
+
}
|
|
22202
|
+
return null;
|
|
22203
|
+
}
|
|
22204
|
+
const legacyPackageJsonPath = join6(dirname2(CACHE_DIR), "package.json");
|
|
22205
|
+
if (existsSync4(legacyPackageJsonPath)) {
|
|
22206
|
+
return { installDir: dirname2(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
|
|
22207
|
+
}
|
|
22208
|
+
return null;
|
|
22209
|
+
}
|
|
22210
|
+
function preparePackageUpdate(version2, packageName = PACKAGE_NAME, runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
|
|
22211
|
+
try {
|
|
22212
|
+
const installContext = resolveInstallContext(runtimePackageJsonPath);
|
|
22213
|
+
if (!installContext) {
|
|
22214
|
+
warn("[auto-update-checker] No install context found for auto-update");
|
|
22215
|
+
return null;
|
|
22216
|
+
}
|
|
22217
|
+
if (!ensureDependencyVersion(installContext.packageJsonPath, packageName, version2))
|
|
22218
|
+
return null;
|
|
22219
|
+
const packageRemoved = removeInstalledPackage(installContext.installDir, packageName);
|
|
22220
|
+
const lockRemoved = removeFromBunLock(installContext.installDir, packageName);
|
|
22221
|
+
if (!packageRemoved && !lockRemoved) {
|
|
22222
|
+
log(`[auto-update-checker] No cached package artifacts removed for ${packageName}; continuing with updated dependency spec`);
|
|
22223
|
+
}
|
|
22224
|
+
return installContext.installDir;
|
|
22225
|
+
} catch (err) {
|
|
22226
|
+
warn(`[auto-update-checker] Failed to prepare package update: ${String(err)}`);
|
|
22227
|
+
return null;
|
|
22228
|
+
}
|
|
22229
|
+
}
|
|
22230
|
+
async function runBunInstallSafe(installDir, options = {}) {
|
|
22231
|
+
let timeout = null;
|
|
22232
|
+
try {
|
|
22233
|
+
if (options.signal?.aborted)
|
|
22234
|
+
return false;
|
|
22235
|
+
const proc = spawn("bun", ["install"], {
|
|
22236
|
+
cwd: installDir,
|
|
22237
|
+
stdio: "pipe"
|
|
22238
|
+
});
|
|
22239
|
+
const abortProcess = () => {
|
|
22240
|
+
try {
|
|
22241
|
+
proc.kill();
|
|
22242
|
+
} catch {}
|
|
22243
|
+
};
|
|
22244
|
+
options.signal?.addEventListener("abort", abortProcess, { once: true });
|
|
22245
|
+
const exitPromise = new Promise((resolveExit) => {
|
|
22246
|
+
proc.on("error", () => resolveExit(false));
|
|
22247
|
+
proc.on("exit", (code) => resolveExit(code === 0));
|
|
22248
|
+
});
|
|
22249
|
+
const timeoutPromise = new Promise((resolveTimeout) => {
|
|
22250
|
+
timeout = setTimeout(() => resolveTimeout("timeout"), options.timeoutMs ?? 60000);
|
|
22251
|
+
});
|
|
22252
|
+
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
22253
|
+
options.signal?.removeEventListener("abort", abortProcess);
|
|
22254
|
+
if (result === "timeout" || options.signal?.aborted) {
|
|
22255
|
+
abortProcess();
|
|
22256
|
+
return false;
|
|
22257
|
+
}
|
|
22258
|
+
return result;
|
|
22259
|
+
} catch (err) {
|
|
22260
|
+
warn(`[auto-update-checker] bun install error: ${String(err)}`);
|
|
22261
|
+
return false;
|
|
22262
|
+
} finally {
|
|
22263
|
+
if (timeout)
|
|
22264
|
+
clearTimeout(timeout);
|
|
22265
|
+
}
|
|
22266
|
+
}
|
|
22267
|
+
|
|
22268
|
+
// src/hooks/auto-update-checker/index.ts
|
|
22269
|
+
function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
22270
|
+
const {
|
|
22271
|
+
enabled = true,
|
|
22272
|
+
showStartupToast = true,
|
|
22273
|
+
autoUpdate = true,
|
|
22274
|
+
npmRegistryUrl = NPM_REGISTRY_URL,
|
|
22275
|
+
fetchTimeoutMs = NPM_FETCH_TIMEOUT,
|
|
22276
|
+
signal = new AbortController().signal
|
|
22277
|
+
} = options;
|
|
22278
|
+
let hasChecked = false;
|
|
22279
|
+
return async ({ event }) => {
|
|
22280
|
+
if (!enabled)
|
|
22281
|
+
return;
|
|
22282
|
+
if (event.type !== "session.created")
|
|
22283
|
+
return;
|
|
22284
|
+
if (hasChecked)
|
|
22285
|
+
return;
|
|
22286
|
+
if (getParentId(event.properties))
|
|
22287
|
+
return;
|
|
22288
|
+
hasChecked = true;
|
|
22289
|
+
setTimeout(() => {
|
|
22290
|
+
runStartupCheck(ctx, {
|
|
22291
|
+
showStartupToast,
|
|
22292
|
+
autoUpdate,
|
|
22293
|
+
npmRegistryUrl,
|
|
22294
|
+
fetchTimeoutMs,
|
|
22295
|
+
signal
|
|
22296
|
+
}).catch((err) => {
|
|
22297
|
+
warn(`[auto-update-checker] Background update check failed: ${String(err)}`);
|
|
22298
|
+
});
|
|
22299
|
+
}, 0);
|
|
22300
|
+
};
|
|
22301
|
+
}
|
|
22302
|
+
function getParentId(properties) {
|
|
22303
|
+
if (!properties || typeof properties !== "object" || Array.isArray(properties))
|
|
22304
|
+
return null;
|
|
22305
|
+
const info = properties.info;
|
|
22306
|
+
if (!info || typeof info !== "object" || Array.isArray(info))
|
|
22307
|
+
return null;
|
|
22308
|
+
const parentID = info.parentID;
|
|
22309
|
+
return typeof parentID === "string" && parentID.length > 0 ? parentID : null;
|
|
22310
|
+
}
|
|
22311
|
+
async function runStartupCheck(ctx, options) {
|
|
22312
|
+
if (options.signal.aborted)
|
|
22313
|
+
return;
|
|
22314
|
+
const cachedVersion = getCachedVersion();
|
|
22315
|
+
const localDevVersion = getLocalDevVersion(ctx.directory);
|
|
22316
|
+
const displayVersion = localDevVersion ?? cachedVersion;
|
|
22317
|
+
if (localDevVersion) {
|
|
22318
|
+
if (options.showStartupToast) {
|
|
22319
|
+
showToast(ctx, `AFT ${displayVersion} (dev)`, "Running in local development mode.", "info");
|
|
22320
|
+
}
|
|
22321
|
+
log("[auto-update-checker] Local development mode");
|
|
22322
|
+
return;
|
|
22323
|
+
}
|
|
22324
|
+
if (options.showStartupToast) {
|
|
22325
|
+
showToast(ctx, `AFT ${displayVersion ?? "unknown"}`, "@cortexkit/aft-opencode is active.", "info");
|
|
22326
|
+
}
|
|
22327
|
+
await runBackgroundUpdateCheck(ctx, options);
|
|
22328
|
+
}
|
|
22329
|
+
async function runBackgroundUpdateCheck(ctx, options) {
|
|
22330
|
+
if (options.signal.aborted)
|
|
22331
|
+
return;
|
|
22332
|
+
const pluginInfo = findPluginEntry(ctx.directory);
|
|
22333
|
+
if (!pluginInfo) {
|
|
22334
|
+
log("[auto-update-checker] Plugin not found in config");
|
|
22335
|
+
return;
|
|
22336
|
+
}
|
|
22337
|
+
const cachedVersion = getCachedVersion(pluginInfo.entry);
|
|
22338
|
+
const currentVersion = cachedVersion ?? pluginInfo.pinnedVersion;
|
|
22339
|
+
if (!currentVersion) {
|
|
22340
|
+
log("[auto-update-checker] No version found (cached or pinned)");
|
|
22341
|
+
return;
|
|
22342
|
+
}
|
|
22343
|
+
const channel = extractChannel(pluginInfo.pinnedVersion ?? currentVersion);
|
|
22344
|
+
const latestVersion = await getLatestVersion(channel, {
|
|
22345
|
+
registryUrl: options.npmRegistryUrl,
|
|
22346
|
+
timeoutMs: options.fetchTimeoutMs,
|
|
22347
|
+
signal: options.signal
|
|
22348
|
+
});
|
|
22349
|
+
if (!latestVersion) {
|
|
22350
|
+
warn(`[auto-update-checker] Failed to fetch latest version for channel: ${channel}`);
|
|
22351
|
+
showToast(ctx, "AFT update check failed", "Could not check npm for @cortexkit/aft-opencode updates. Continuing with the cached version.", "warning", 8000);
|
|
22352
|
+
return;
|
|
22353
|
+
}
|
|
22354
|
+
if (currentVersion === latestVersion) {
|
|
22355
|
+
log(`[auto-update-checker] Already on latest version for channel: ${channel}`);
|
|
22356
|
+
return;
|
|
22357
|
+
}
|
|
22358
|
+
log(`[auto-update-checker] Update available (${channel}): ${currentVersion} \u2192 ${latestVersion}`);
|
|
22359
|
+
if (pluginInfo.isPinned) {
|
|
22360
|
+
showToast(ctx, `AFT ${latestVersion}`, `v${latestVersion} available. Version is pinned; update your OpenCode plugin config to upgrade.`, "info", 8000);
|
|
22361
|
+
log("[auto-update-checker] Version is pinned; skipping auto-update");
|
|
22362
|
+
return;
|
|
22363
|
+
}
|
|
22364
|
+
if (!options.autoUpdate) {
|
|
22365
|
+
showToast(ctx, `AFT ${latestVersion}`, `v${latestVersion} available. Auto-update is disabled.`, "info", 8000);
|
|
22366
|
+
log("[auto-update-checker] Auto-update disabled, notification only");
|
|
22367
|
+
return;
|
|
22368
|
+
}
|
|
22369
|
+
const installDir = preparePackageUpdate(latestVersion, PACKAGE_NAME);
|
|
22370
|
+
if (!installDir) {
|
|
22371
|
+
showToast(ctx, `AFT ${latestVersion}`, `v${latestVersion} available. Auto-update could not prepare the active install.`, "warning", 8000);
|
|
22372
|
+
warn("[auto-update-checker] Failed to prepare install root for auto-update");
|
|
22373
|
+
return;
|
|
22374
|
+
}
|
|
22375
|
+
const installSuccess = await runBunInstallSafe(installDir, { signal: options.signal });
|
|
22376
|
+
if (installSuccess) {
|
|
22377
|
+
showToast(ctx, "AFT Updated!", `v${currentVersion} \u2192 v${latestVersion}
|
|
22378
|
+
Restart OpenCode to apply.`, "success", 8000);
|
|
22379
|
+
log(`[auto-update-checker] Update installed: ${currentVersion} \u2192 ${latestVersion}`);
|
|
22380
|
+
return;
|
|
22381
|
+
}
|
|
22382
|
+
showToast(ctx, `AFT ${latestVersion}`, `v${latestVersion} available, but auto-update failed to install it. Check logs or retry manually.`, "error", 8000);
|
|
22383
|
+
warn("[auto-update-checker] bun install failed; update not installed");
|
|
22384
|
+
}
|
|
22385
|
+
function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
|
|
22386
|
+
ctx.client.tui.showToast({ body: { title, message, variant, duration: duration3 } }).catch(() => {});
|
|
22387
|
+
}
|
|
22388
|
+
|
|
22389
|
+
// src/lsp-auto-install.ts
|
|
22390
|
+
import { spawn as spawn2 } from "child_process";
|
|
21874
22391
|
import { createHash } from "crypto";
|
|
21875
|
-
import { createReadStream, statSync as
|
|
22392
|
+
import { createReadStream, statSync as statSync3 } from "fs";
|
|
21876
22393
|
|
|
21877
22394
|
// src/lsp-cache.ts
|
|
21878
22395
|
import {
|
|
21879
22396
|
closeSync,
|
|
21880
|
-
existsSync as existsSync3,
|
|
21881
22397
|
mkdirSync as mkdirSync2,
|
|
21882
22398
|
openSync,
|
|
21883
|
-
readFileSync as
|
|
21884
|
-
statSync,
|
|
22399
|
+
readFileSync as readFileSync4,
|
|
22400
|
+
statSync as statSync2,
|
|
21885
22401
|
unlinkSync as unlinkSync2,
|
|
21886
|
-
writeFileSync
|
|
22402
|
+
writeFileSync as writeFileSync3
|
|
21887
22403
|
} from "fs";
|
|
21888
|
-
import { homedir as
|
|
21889
|
-
import { join as
|
|
22404
|
+
import { homedir as homedir5 } from "os";
|
|
22405
|
+
import { join as join7 } from "path";
|
|
21890
22406
|
function aftCacheBase() {
|
|
21891
22407
|
const override = process.env.AFT_CACHE_DIR;
|
|
21892
22408
|
if (override && override.length > 0)
|
|
21893
22409
|
return override;
|
|
21894
22410
|
if (process.platform === "win32") {
|
|
21895
22411
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
21896
|
-
const base2 = localAppData ||
|
|
21897
|
-
return
|
|
22412
|
+
const base2 = localAppData || join7(homedir5(), "AppData", "Local");
|
|
22413
|
+
return join7(base2, "aft");
|
|
21898
22414
|
}
|
|
21899
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
21900
|
-
return
|
|
22415
|
+
const base = process.env.XDG_CACHE_HOME || join7(homedir5(), ".cache");
|
|
22416
|
+
return join7(base, "aft");
|
|
21901
22417
|
}
|
|
21902
22418
|
function lspCacheRoot() {
|
|
21903
|
-
return
|
|
22419
|
+
return join7(aftCacheBase(), "lsp-packages");
|
|
21904
22420
|
}
|
|
21905
22421
|
function lspPackageDir(npmPackage) {
|
|
21906
|
-
return
|
|
22422
|
+
return join7(lspCacheRoot(), encodeURIComponent(npmPackage));
|
|
21907
22423
|
}
|
|
21908
22424
|
function lspBinaryPath(npmPackage, binary) {
|
|
21909
|
-
return
|
|
22425
|
+
return join7(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
|
|
21910
22426
|
}
|
|
21911
22427
|
function lspBinDir(npmPackage) {
|
|
21912
|
-
return
|
|
22428
|
+
return join7(lspPackageDir(npmPackage), "node_modules", ".bin");
|
|
21913
22429
|
}
|
|
21914
22430
|
function isInstalled(npmPackage, binary) {
|
|
21915
22431
|
for (const candidate of lspBinaryCandidates(binary)) {
|
|
21916
22432
|
try {
|
|
21917
|
-
if (
|
|
22433
|
+
if (statSync2(join7(lspBinDir(npmPackage), candidate)).isFile())
|
|
21918
22434
|
return true;
|
|
21919
22435
|
} catch {}
|
|
21920
22436
|
}
|
|
@@ -21934,17 +22450,17 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
|
21934
22450
|
installedAt: new Date().toISOString(),
|
|
21935
22451
|
...sha256 ? { sha256 } : {}
|
|
21936
22452
|
};
|
|
21937
|
-
|
|
22453
|
+
writeFileSync3(join7(installDir, INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
|
|
21938
22454
|
} catch (err) {
|
|
21939
22455
|
log(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
|
|
21940
22456
|
}
|
|
21941
22457
|
}
|
|
21942
22458
|
function readInstalledMetaIn(installDir) {
|
|
21943
|
-
const path2 =
|
|
22459
|
+
const path2 = join7(installDir, INSTALLED_META_FILE);
|
|
21944
22460
|
try {
|
|
21945
|
-
if (!
|
|
22461
|
+
if (!statSync2(path2).isFile())
|
|
21946
22462
|
return null;
|
|
21947
|
-
const raw =
|
|
22463
|
+
const raw = readFileSync4(path2, "utf8");
|
|
21948
22464
|
const parsed = JSON.parse(raw);
|
|
21949
22465
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
21950
22466
|
return null;
|
|
@@ -21964,7 +22480,7 @@ function readInstalledMeta(packageKey) {
|
|
|
21964
22480
|
return readInstalledMetaIn(lspPackageDir(packageKey));
|
|
21965
22481
|
}
|
|
21966
22482
|
function lockPath(npmPackage) {
|
|
21967
|
-
return
|
|
22483
|
+
return join7(lspPackageDir(npmPackage), ".aft-installing");
|
|
21968
22484
|
}
|
|
21969
22485
|
var STALE_LOCK_MS = 30 * 60 * 1000;
|
|
21970
22486
|
function acquireInstallLock(lockKey) {
|
|
@@ -21974,7 +22490,7 @@ function acquireInstallLock(lockKey) {
|
|
|
21974
22490
|
try {
|
|
21975
22491
|
const fd = openSync(lock, "wx");
|
|
21976
22492
|
try {
|
|
21977
|
-
|
|
22493
|
+
writeFileSync3(fd, `${process.pid}
|
|
21978
22494
|
${new Date().toISOString()}
|
|
21979
22495
|
`);
|
|
21980
22496
|
} finally {
|
|
@@ -21994,12 +22510,12 @@ ${new Date().toISOString()}
|
|
|
21994
22510
|
let owningPid = null;
|
|
21995
22511
|
let lockMtimeMs = 0;
|
|
21996
22512
|
try {
|
|
21997
|
-
const raw =
|
|
22513
|
+
const raw = readFileSync4(lock, "utf8");
|
|
21998
22514
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
21999
22515
|
const parsed = Number.parseInt(firstLine, 10);
|
|
22000
22516
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
22001
22517
|
owningPid = parsed;
|
|
22002
|
-
lockMtimeMs =
|
|
22518
|
+
lockMtimeMs = statSync2(lock).mtimeMs;
|
|
22003
22519
|
} catch {
|
|
22004
22520
|
return tryClaim();
|
|
22005
22521
|
}
|
|
@@ -22030,11 +22546,34 @@ function isProcessAlive(pid) {
|
|
|
22030
22546
|
function releaseInstallLock(lockKey) {
|
|
22031
22547
|
const lock = lockPath(lockKey);
|
|
22032
22548
|
try {
|
|
22033
|
-
|
|
22549
|
+
let owningPid = null;
|
|
22550
|
+
try {
|
|
22551
|
+
const raw = readFileSync4(lock, "utf8");
|
|
22552
|
+
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
22553
|
+
const parsed = Number.parseInt(firstLine, 10);
|
|
22554
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
22555
|
+
owningPid = parsed;
|
|
22556
|
+
} catch (readErr) {
|
|
22557
|
+
const code = readErr.code;
|
|
22558
|
+
if (code === "ENOENT")
|
|
22559
|
+
return;
|
|
22560
|
+
warn(`[lsp] could not read install lock for ${lockKey} during release: ${readErr}`);
|
|
22561
|
+
return;
|
|
22562
|
+
}
|
|
22563
|
+
if (owningPid !== process.pid) {
|
|
22564
|
+
log(`[lsp] not releasing install lock for ${lockKey}: owned by pid ${owningPid ?? "unknown"} (we are ${process.pid})`);
|
|
22565
|
+
return;
|
|
22566
|
+
}
|
|
22567
|
+
try {
|
|
22034
22568
|
unlinkSync2(lock);
|
|
22569
|
+
} catch (unlinkErr) {
|
|
22570
|
+
const code = unlinkErr.code;
|
|
22571
|
+
if (code !== "ENOENT") {
|
|
22572
|
+
warn(`[lsp] failed to release install lock for ${lockKey}: ${unlinkErr}`);
|
|
22573
|
+
}
|
|
22035
22574
|
}
|
|
22036
22575
|
} catch (err) {
|
|
22037
|
-
warn(`[lsp]
|
|
22576
|
+
warn(`[lsp] unexpected error releasing install lock for ${lockKey}: ${err}`);
|
|
22038
22577
|
}
|
|
22039
22578
|
}
|
|
22040
22579
|
async function withInstallLock(lockKey, task) {
|
|
@@ -22048,9 +22587,9 @@ async function withInstallLock(lockKey, task) {
|
|
|
22048
22587
|
}
|
|
22049
22588
|
var VERSION_CHECK_FILE = ".aft-version-check";
|
|
22050
22589
|
function readVersionCheck(npmPackage) {
|
|
22051
|
-
const file2 =
|
|
22590
|
+
const file2 = join7(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
22052
22591
|
try {
|
|
22053
|
-
const raw =
|
|
22592
|
+
const raw = readFileSync4(file2, "utf8");
|
|
22054
22593
|
const parsed = JSON.parse(raw);
|
|
22055
22594
|
if (typeof parsed.last_checked === "string") {
|
|
22056
22595
|
return {
|
|
@@ -22065,12 +22604,12 @@ function readVersionCheck(npmPackage) {
|
|
|
22065
22604
|
}
|
|
22066
22605
|
function writeVersionCheck(npmPackage, latest) {
|
|
22067
22606
|
mkdirSync2(lspPackageDir(npmPackage), { recursive: true });
|
|
22068
|
-
const file2 =
|
|
22607
|
+
const file2 = join7(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
22069
22608
|
const record2 = {
|
|
22070
22609
|
last_checked: new Date().toISOString(),
|
|
22071
22610
|
latest_eligible: latest
|
|
22072
22611
|
};
|
|
22073
|
-
|
|
22612
|
+
writeFileSync3(file2, JSON.stringify(record2, null, 2));
|
|
22074
22613
|
}
|
|
22075
22614
|
function shouldRecheckVersion(record2, weeklyCheckIntervalMs = 7 * 24 * 60 * 60 * 1000) {
|
|
22076
22615
|
if (!record2)
|
|
@@ -22136,6 +22675,9 @@ function assertSafeVersion(version2) {
|
|
|
22136
22675
|
throw new Error(`unsafe version/tag string ${JSON.stringify(version2)}: must match ${SAFE_VERSION_RE.source}`);
|
|
22137
22676
|
}
|
|
22138
22677
|
}
|
|
22678
|
+
function isSafeVersion(version2) {
|
|
22679
|
+
return typeof version2 === "string" && version2.length > 0 && SAFE_VERSION_RE.test(version2);
|
|
22680
|
+
}
|
|
22139
22681
|
function stripTagV(tag) {
|
|
22140
22682
|
assertSafeVersion(tag);
|
|
22141
22683
|
return tag.startsWith("v") ? tag.slice(1) : tag;
|
|
@@ -22210,8 +22752,8 @@ var NPM_LSP_TABLE = [
|
|
|
22210
22752
|
];
|
|
22211
22753
|
|
|
22212
22754
|
// src/lsp-project-relevance.ts
|
|
22213
|
-
import { existsSync as
|
|
22214
|
-
import { join as
|
|
22755
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
22756
|
+
import { join as join8 } from "path";
|
|
22215
22757
|
var MAX_WALK_DIRS = 200;
|
|
22216
22758
|
var MAX_WALK_DEPTH = 4;
|
|
22217
22759
|
var NOISE_DIRS = new Set([
|
|
@@ -22228,7 +22770,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
|
|
|
22228
22770
|
if (!rootMarkers)
|
|
22229
22771
|
return false;
|
|
22230
22772
|
for (const marker of rootMarkers) {
|
|
22231
|
-
if (
|
|
22773
|
+
if (existsSync5(join8(projectRoot, marker)))
|
|
22232
22774
|
return true;
|
|
22233
22775
|
}
|
|
22234
22776
|
return false;
|
|
@@ -22254,7 +22796,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
|
|
|
22254
22796
|
for (const entry of entries) {
|
|
22255
22797
|
if (entry.isDirectory()) {
|
|
22256
22798
|
if (current.depth < MAX_WALK_DEPTH && !NOISE_DIRS.has(entry.name.toLowerCase())) {
|
|
22257
|
-
queue.push({ dir:
|
|
22799
|
+
queue.push({ dir: join8(current.dir, entry.name), depth: current.depth + 1 });
|
|
22258
22800
|
}
|
|
22259
22801
|
continue;
|
|
22260
22802
|
}
|
|
@@ -22362,13 +22904,14 @@ async function resolveTargetVersion(spec, config2, fetchImpl = fetch) {
|
|
|
22362
22904
|
}
|
|
22363
22905
|
const cached2 = readVersionCheck(spec.npm);
|
|
22364
22906
|
const weeklyMs = config2.graceDays * 24 * 60 * 60 * 1000;
|
|
22365
|
-
|
|
22907
|
+
const cachedSafe = isSafeVersion(cached2?.latest_eligible ?? null);
|
|
22908
|
+
if (cached2 && !shouldRecheckVersion(cached2, weeklyMs) && cachedSafe) {
|
|
22366
22909
|
return { version: cached2.latest_eligible, pinned: false, probe: null };
|
|
22367
22910
|
}
|
|
22368
22911
|
const probe = await probeRegistry(spec.npm, config2.graceDays, fetchImpl);
|
|
22369
22912
|
if (!probe) {
|
|
22370
22913
|
return {
|
|
22371
|
-
version: cached2?.latest_eligible ?? null,
|
|
22914
|
+
version: cachedSafe ? cached2?.latest_eligible ?? null : null,
|
|
22372
22915
|
pinned: false,
|
|
22373
22916
|
probe: null
|
|
22374
22917
|
};
|
|
@@ -22377,15 +22920,15 @@ async function resolveTargetVersion(spec, config2, fetchImpl = fetch) {
|
|
|
22377
22920
|
return { version: probe.version, pinned: false, probe };
|
|
22378
22921
|
}
|
|
22379
22922
|
function runInstall(spec, version2, cwd, signal) {
|
|
22380
|
-
return new Promise((
|
|
22923
|
+
return new Promise((resolve2) => {
|
|
22381
22924
|
const target = `${spec.npm}@${version2}`;
|
|
22382
22925
|
log(`[lsp] installing ${target} to ${cwd}`);
|
|
22383
22926
|
if (signal?.aborted) {
|
|
22384
22927
|
warn(`[lsp] install ${target} aborted before spawn`);
|
|
22385
|
-
|
|
22928
|
+
resolve2(false);
|
|
22386
22929
|
return;
|
|
22387
22930
|
}
|
|
22388
|
-
const child =
|
|
22931
|
+
const child = spawn2("bun", ["add", target, "--cwd", cwd, "--ignore-scripts", "--silent"], {
|
|
22389
22932
|
stdio: ["ignore", "pipe", "pipe"]
|
|
22390
22933
|
});
|
|
22391
22934
|
child.unref();
|
|
@@ -22402,7 +22945,7 @@ function runInstall(spec, version2, cwd, signal) {
|
|
|
22402
22945
|
return;
|
|
22403
22946
|
settled = true;
|
|
22404
22947
|
cleanup();
|
|
22405
|
-
|
|
22948
|
+
resolve2(ok);
|
|
22406
22949
|
};
|
|
22407
22950
|
const onAbort = () => {
|
|
22408
22951
|
warn(`[lsp] install ${target} aborted during shutdown`);
|
|
@@ -22503,7 +23046,7 @@ function cachedPackageDir(npmPackage) {
|
|
|
22503
23046
|
return lspBinDir(npmPackage).replace(/[\\/]node_modules[\\/]\.bin[\\/]?$/, "");
|
|
22504
23047
|
}
|
|
22505
23048
|
function hashInstalledBinary(spec) {
|
|
22506
|
-
return new Promise((
|
|
23049
|
+
return new Promise((resolve2, reject) => {
|
|
22507
23050
|
const candidates = process.platform === "win32" ? [
|
|
22508
23051
|
lspBinaryPath(spec.npm, spec.binary),
|
|
22509
23052
|
lspBinaryPath(spec.npm, `${spec.binary}.cmd`),
|
|
@@ -22513,7 +23056,7 @@ function hashInstalledBinary(spec) {
|
|
|
22513
23056
|
let pathToHash = null;
|
|
22514
23057
|
for (const p of candidates) {
|
|
22515
23058
|
try {
|
|
22516
|
-
if (
|
|
23059
|
+
if (statSync3(p).isFile()) {
|
|
22517
23060
|
pathToHash = p;
|
|
22518
23061
|
break;
|
|
22519
23062
|
}
|
|
@@ -22527,7 +23070,7 @@ function hashInstalledBinary(spec) {
|
|
|
22527
23070
|
const stream = createReadStream(pathToHash);
|
|
22528
23071
|
stream.on("error", reject);
|
|
22529
23072
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
22530
|
-
stream.on("end", () =>
|
|
23073
|
+
stream.on("end", () => resolve2(hash2.digest("hex")));
|
|
22531
23074
|
});
|
|
22532
23075
|
}
|
|
22533
23076
|
function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
|
|
@@ -22589,85 +23132,85 @@ import {
|
|
|
22589
23132
|
copyFileSync,
|
|
22590
23133
|
createReadStream as createReadStream2,
|
|
22591
23134
|
createWriteStream,
|
|
22592
|
-
existsSync as
|
|
23135
|
+
existsSync as existsSync6,
|
|
22593
23136
|
lstatSync,
|
|
22594
23137
|
mkdirSync as mkdirSync3,
|
|
22595
23138
|
readdirSync as readdirSync2,
|
|
22596
23139
|
readlinkSync,
|
|
22597
23140
|
realpathSync,
|
|
22598
23141
|
renameSync,
|
|
22599
|
-
rmSync,
|
|
22600
|
-
statSync as
|
|
23142
|
+
rmSync as rmSync2,
|
|
23143
|
+
statSync as statSync4,
|
|
22601
23144
|
unlinkSync as unlinkSync3
|
|
22602
23145
|
} from "fs";
|
|
22603
|
-
import { dirname, join as
|
|
23146
|
+
import { dirname as dirname3, join as join9, relative, resolve as resolve2 } from "path";
|
|
22604
23147
|
import { Readable } from "stream";
|
|
22605
23148
|
import { pipeline } from "stream/promises";
|
|
22606
23149
|
|
|
22607
23150
|
// src/lsp-github-table.ts
|
|
22608
|
-
function exe(
|
|
22609
|
-
return
|
|
23151
|
+
function exe(platform2, name) {
|
|
23152
|
+
return platform2 === "win32" ? `${name}.exe` : name;
|
|
22610
23153
|
}
|
|
22611
23154
|
var CLANGD = {
|
|
22612
23155
|
id: "clangd",
|
|
22613
23156
|
githubRepo: "clangd/clangd",
|
|
22614
23157
|
binary: "clangd",
|
|
22615
|
-
resolveAsset: (
|
|
22616
|
-
const platformName =
|
|
23158
|
+
resolveAsset: (platform2, _arch, version2) => {
|
|
23159
|
+
const platformName = platform2 === "darwin" ? "mac" : platform2 === "linux" ? "linux" : "windows";
|
|
22617
23160
|
return { name: `clangd-${platformName}-${version2}.zip`, archive: "zip" };
|
|
22618
23161
|
},
|
|
22619
|
-
binaryPathInArchive: (
|
|
23162
|
+
binaryPathInArchive: (platform2, _arch, version2) => `clangd_${version2}/bin/${exe(platform2, "clangd")}`
|
|
22620
23163
|
};
|
|
22621
23164
|
var LUA_LS = {
|
|
22622
23165
|
id: "lua-ls",
|
|
22623
23166
|
githubRepo: "LuaLS/lua-language-server",
|
|
22624
23167
|
binary: "lua-language-server",
|
|
22625
|
-
resolveAsset: (
|
|
22626
|
-
const ext =
|
|
22627
|
-
const platformName =
|
|
23168
|
+
resolveAsset: (platform2, arch, version2) => {
|
|
23169
|
+
const ext = platform2 === "win32" ? "zip" : "tar.gz";
|
|
23170
|
+
const platformName = platform2 === "darwin" ? "darwin" : platform2 === "linux" ? "linux" : "win32";
|
|
22628
23171
|
const archName = arch === "arm64" ? "arm64" : "x64";
|
|
22629
23172
|
return {
|
|
22630
23173
|
name: `lua-language-server-${version2}-${platformName}-${archName}.${ext}`,
|
|
22631
23174
|
archive: ext
|
|
22632
23175
|
};
|
|
22633
23176
|
},
|
|
22634
|
-
binaryPathInArchive: (
|
|
23177
|
+
binaryPathInArchive: (platform2, _arch, _version) => `bin/${exe(platform2, "lua-language-server")}`
|
|
22635
23178
|
};
|
|
22636
23179
|
var ZLS = {
|
|
22637
23180
|
id: "zls",
|
|
22638
23181
|
githubRepo: "zigtools/zls",
|
|
22639
23182
|
binary: "zls",
|
|
22640
|
-
resolveAsset: (
|
|
22641
|
-
const ext =
|
|
23183
|
+
resolveAsset: (platform2, arch, _version) => {
|
|
23184
|
+
const ext = platform2 === "win32" ? "zip" : "tar.xz";
|
|
22642
23185
|
const archName = arch === "arm64" ? "aarch64" : "x86_64";
|
|
22643
|
-
const platformName =
|
|
23186
|
+
const platformName = platform2 === "darwin" ? "macos" : platform2 === "linux" ? "linux" : "windows";
|
|
22644
23187
|
return { name: `zls-${archName}-${platformName}.${ext}`, archive: ext };
|
|
22645
23188
|
},
|
|
22646
|
-
binaryPathInArchive: (
|
|
23189
|
+
binaryPathInArchive: (platform2, _arch, _version) => exe(platform2, "zls")
|
|
22647
23190
|
};
|
|
22648
23191
|
var TINYMIST = {
|
|
22649
23192
|
id: "tinymist",
|
|
22650
23193
|
githubRepo: "Myriad-Dreamin/tinymist",
|
|
22651
23194
|
binary: "tinymist",
|
|
22652
|
-
resolveAsset: (
|
|
23195
|
+
resolveAsset: (platform2, arch, _version) => {
|
|
22653
23196
|
const archName = arch === "arm64" ? "aarch64" : "x86_64";
|
|
22654
|
-
const triple =
|
|
22655
|
-
const ext =
|
|
23197
|
+
const triple = platform2 === "darwin" ? "apple-darwin" : platform2 === "linux" ? "unknown-linux-gnu" : "pc-windows-msvc";
|
|
23198
|
+
const ext = platform2 === "win32" ? "zip" : "tar.gz";
|
|
22656
23199
|
return { name: `tinymist-${archName}-${triple}.${ext}`, archive: ext };
|
|
22657
23200
|
},
|
|
22658
|
-
binaryPathInArchive: (
|
|
23201
|
+
binaryPathInArchive: (platform2, _arch, _version) => exe(platform2, "tinymist")
|
|
22659
23202
|
};
|
|
22660
23203
|
var TEXLAB = {
|
|
22661
23204
|
id: "texlab",
|
|
22662
23205
|
githubRepo: "latex-lsp/texlab",
|
|
22663
23206
|
binary: "texlab",
|
|
22664
|
-
resolveAsset: (
|
|
23207
|
+
resolveAsset: (platform2, arch, _version) => {
|
|
22665
23208
|
const archName = arch === "arm64" ? "aarch64" : "x86_64";
|
|
22666
|
-
const platformName =
|
|
22667
|
-
const ext =
|
|
23209
|
+
const platformName = platform2 === "darwin" ? "macos" : platform2 === "linux" ? "linux" : "windows";
|
|
23210
|
+
const ext = platform2 === "win32" ? "zip" : "tar.gz";
|
|
22668
23211
|
return { name: `texlab-${archName}-${platformName}.${ext}`, archive: ext };
|
|
22669
23212
|
},
|
|
22670
|
-
binaryPathInArchive: (
|
|
23213
|
+
binaryPathInArchive: (platform2, _arch, _version) => exe(platform2, "texlab")
|
|
22671
23214
|
};
|
|
22672
23215
|
var GITHUB_LSP_TABLE = [
|
|
22673
23216
|
CLANGD,
|
|
@@ -22677,57 +23220,57 @@ var GITHUB_LSP_TABLE = [
|
|
|
22677
23220
|
TEXLAB
|
|
22678
23221
|
];
|
|
22679
23222
|
function detectHostPlatform() {
|
|
22680
|
-
const
|
|
22681
|
-
if (
|
|
23223
|
+
const platform2 = process.platform;
|
|
23224
|
+
if (platform2 !== "darwin" && platform2 !== "linux" && platform2 !== "win32")
|
|
22682
23225
|
return null;
|
|
22683
23226
|
const arch = process.arch;
|
|
22684
23227
|
if (arch === "x64")
|
|
22685
|
-
return { platform, arch: "x64" };
|
|
23228
|
+
return { platform: platform2, arch: "x64" };
|
|
22686
23229
|
if (arch === "arm64")
|
|
22687
|
-
return { platform, arch: "arm64" };
|
|
23230
|
+
return { platform: platform2, arch: "arm64" };
|
|
22688
23231
|
return null;
|
|
22689
23232
|
}
|
|
22690
23233
|
|
|
22691
23234
|
// src/lsp-github-install.ts
|
|
22692
23235
|
function ghCacheRoot() {
|
|
22693
|
-
return
|
|
23236
|
+
return join9(aftCacheBase(), "lsp-binaries");
|
|
22694
23237
|
}
|
|
22695
23238
|
function ghPackageDir(spec) {
|
|
22696
|
-
return
|
|
23239
|
+
return join9(ghCacheRoot(), spec.id);
|
|
22697
23240
|
}
|
|
22698
23241
|
function ghBinDir(spec) {
|
|
22699
|
-
return
|
|
23242
|
+
return join9(ghPackageDir(spec), "bin");
|
|
22700
23243
|
}
|
|
22701
23244
|
function ghExtractDir(spec) {
|
|
22702
|
-
return
|
|
23245
|
+
return join9(ghPackageDir(spec), "extracted");
|
|
22703
23246
|
}
|
|
22704
|
-
function ghBinaryPath(spec,
|
|
22705
|
-
const ext =
|
|
22706
|
-
return
|
|
23247
|
+
function ghBinaryPath(spec, platform2) {
|
|
23248
|
+
const ext = platform2 === "win32" ? ".exe" : "";
|
|
23249
|
+
return join9(ghBinDir(spec), `${spec.binary}${ext}`);
|
|
22707
23250
|
}
|
|
22708
|
-
function isGithubInstalled(spec,
|
|
22709
|
-
for (const candidate of ghBinaryCandidates(spec,
|
|
23251
|
+
function isGithubInstalled(spec, platform2) {
|
|
23252
|
+
for (const candidate of ghBinaryCandidates(spec, platform2)) {
|
|
22710
23253
|
try {
|
|
22711
|
-
if (
|
|
23254
|
+
if (statSync4(join9(ghBinDir(spec), candidate)).isFile())
|
|
22712
23255
|
return true;
|
|
22713
23256
|
} catch {}
|
|
22714
23257
|
}
|
|
22715
23258
|
return false;
|
|
22716
23259
|
}
|
|
22717
|
-
function ghBinaryCandidates(spec,
|
|
22718
|
-
if (
|
|
23260
|
+
function ghBinaryCandidates(spec, platform2) {
|
|
23261
|
+
if (platform2 !== "win32")
|
|
22719
23262
|
return [spec.binary];
|
|
22720
23263
|
return [spec.binary, `${spec.binary}.cmd`, `${spec.binary}.exe`, `${spec.binary}.bat`];
|
|
22721
23264
|
}
|
|
22722
23265
|
var MAX_DOWNLOAD_BYTES = 256 * 1024 * 1024;
|
|
22723
23266
|
var MAX_EXTRACT_BYTES = 1024 * 1024 * 1024;
|
|
22724
23267
|
function sha256OfFile(path2) {
|
|
22725
|
-
return new Promise((
|
|
23268
|
+
return new Promise((resolve3, reject) => {
|
|
22726
23269
|
const hash2 = createHash2("sha256");
|
|
22727
23270
|
const stream = createReadStream2(path2);
|
|
22728
23271
|
stream.on("error", reject);
|
|
22729
23272
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
22730
|
-
stream.on("end", () =>
|
|
23273
|
+
stream.on("end", () => resolve3(hash2.digest("hex")));
|
|
22731
23274
|
});
|
|
22732
23275
|
}
|
|
22733
23276
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
|
|
@@ -22814,8 +23357,10 @@ async function resolveTargetTag(spec, config2, fetchImpl, signal) {
|
|
|
22814
23357
|
}
|
|
22815
23358
|
const cached2 = readVersionCheck(spec.githubRepo);
|
|
22816
23359
|
const weeklyMs = config2.graceDays * 24 * 60 * 60 * 1000;
|
|
22817
|
-
|
|
22818
|
-
|
|
23360
|
+
const cachedTag = cached2?.latest_eligible ?? null;
|
|
23361
|
+
const cachedSafe = isSafeVersion(cachedTag);
|
|
23362
|
+
if (cached2 && !shouldRecheckVersion(cached2, weeklyMs) && cachedSafe) {
|
|
23363
|
+
const release = await fetchReleaseByTag(spec.githubRepo, cachedTag, fetchImpl);
|
|
22819
23364
|
if (release) {
|
|
22820
23365
|
return {
|
|
22821
23366
|
tag: release.tag,
|
|
@@ -22852,7 +23397,31 @@ function controlledTimeoutSignal(timeoutMs, parent) {
|
|
|
22852
23397
|
}
|
|
22853
23398
|
};
|
|
22854
23399
|
}
|
|
23400
|
+
var ALLOWED_DOWNLOAD_HOSTS = new Set([
|
|
23401
|
+
"github.com",
|
|
23402
|
+
"api.github.com",
|
|
23403
|
+
"objects.githubusercontent.com",
|
|
23404
|
+
"release-assets.githubusercontent.com",
|
|
23405
|
+
"raw.githubusercontent.com",
|
|
23406
|
+
"codeload.github.com"
|
|
23407
|
+
]);
|
|
23408
|
+
function assertAllowedDownloadUrl(rawUrl) {
|
|
23409
|
+
let parsed;
|
|
23410
|
+
try {
|
|
23411
|
+
parsed = new URL(rawUrl);
|
|
23412
|
+
} catch {
|
|
23413
|
+
throw new Error(`download url is not a valid URL: ${rawUrl}`);
|
|
23414
|
+
}
|
|
23415
|
+
if (parsed.protocol !== "https:") {
|
|
23416
|
+
throw new Error(`download url must be https (got ${parsed.protocol}): ${rawUrl}`);
|
|
23417
|
+
}
|
|
23418
|
+
if (!ALLOWED_DOWNLOAD_HOSTS.has(parsed.hostname.toLowerCase())) {
|
|
23419
|
+
throw new Error(`download url host ${parsed.hostname} is not in the GitHub allowlist: ${rawUrl}`);
|
|
23420
|
+
}
|
|
23421
|
+
return parsed;
|
|
23422
|
+
}
|
|
22855
23423
|
async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
23424
|
+
assertAllowedDownloadUrl(url2);
|
|
22856
23425
|
if (assetSize !== undefined && assetSize > MAX_DOWNLOAD_BYTES) {
|
|
22857
23426
|
throw new Error(`asset size ${assetSize} exceeds max ${MAX_DOWNLOAD_BYTES} (set lsp.versions to pin a smaller release if this is wrong)`);
|
|
22858
23427
|
}
|
|
@@ -22870,7 +23439,7 @@ async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
|
22870
23439
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES) {
|
|
22871
23440
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES}`);
|
|
22872
23441
|
}
|
|
22873
|
-
mkdirSync3(
|
|
23442
|
+
mkdirSync3(dirname3(destPath), { recursive: true });
|
|
22874
23443
|
let bytesWritten = 0;
|
|
22875
23444
|
const guard = new TransformStream({
|
|
22876
23445
|
transform(chunk, controller) {
|
|
@@ -22905,7 +23474,7 @@ function validateExtraction(stagingRoot) {
|
|
|
22905
23474
|
throw new Error(`failed to read staging dir ${dir}: ${err}`);
|
|
22906
23475
|
}
|
|
22907
23476
|
for (const entry of entries) {
|
|
22908
|
-
const full =
|
|
23477
|
+
const full = join9(dir, entry);
|
|
22909
23478
|
let lst;
|
|
22910
23479
|
try {
|
|
22911
23480
|
lst = lstatSync(full);
|
|
@@ -22926,7 +23495,7 @@ function validateExtraction(stagingRoot) {
|
|
|
22926
23495
|
throw new Error(`failed to realpath ${full}: ${err}`);
|
|
22927
23496
|
}
|
|
22928
23497
|
const rel = relative(realStagingRoot, realFull);
|
|
22929
|
-
if (rel.startsWith("..") ||
|
|
23498
|
+
if (rel.startsWith("..") || resolve2(realStagingRoot, rel) !== realFull) {
|
|
22930
23499
|
throw new Error(`archive entry escapes staging root: ${full} \u2192 ${realFull} (zip-slip defense)`);
|
|
22931
23500
|
}
|
|
22932
23501
|
if (lst.isDirectory()) {
|
|
@@ -22947,19 +23516,19 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
|
22947
23516
|
const suffix = randomBytes(8).toString("hex");
|
|
22948
23517
|
const stagingDir = `${destDir}.staging-${suffix}`;
|
|
22949
23518
|
try {
|
|
22950
|
-
|
|
23519
|
+
rmSync2(stagingDir, { recursive: true, force: true });
|
|
22951
23520
|
} catch {}
|
|
22952
23521
|
mkdirSync3(stagingDir, { recursive: true });
|
|
22953
23522
|
try {
|
|
22954
23523
|
runPlatformExtractor(archivePath, stagingDir, archiveType);
|
|
22955
23524
|
validateExtraction(stagingDir);
|
|
22956
23525
|
try {
|
|
22957
|
-
|
|
23526
|
+
rmSync2(destDir, { recursive: true, force: true });
|
|
22958
23527
|
} catch {}
|
|
22959
23528
|
renameSync(stagingDir, destDir);
|
|
22960
23529
|
} catch (err) {
|
|
22961
23530
|
try {
|
|
22962
|
-
|
|
23531
|
+
rmSync2(stagingDir, { recursive: true, force: true });
|
|
22963
23532
|
} catch {}
|
|
22964
23533
|
throw err;
|
|
22965
23534
|
}
|
|
@@ -22995,11 +23564,11 @@ function runPlatformExtractor(archivePath, destDir, archiveType) {
|
|
|
22995
23564
|
}
|
|
22996
23565
|
throw new Error(`unsupported archive type: ${archiveType}`);
|
|
22997
23566
|
}
|
|
22998
|
-
async function downloadAndInstall(spec, tag, assets,
|
|
23567
|
+
async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl, signal) {
|
|
22999
23568
|
const version2 = stripTagV(tag);
|
|
23000
|
-
const expected = spec.resolveAsset(
|
|
23569
|
+
const expected = spec.resolveAsset(platform2, arch, version2);
|
|
23001
23570
|
if (!expected) {
|
|
23002
|
-
warn(`[lsp] ${spec.id}: unsupported platform/arch combo ${
|
|
23571
|
+
warn(`[lsp] ${spec.id}: unsupported platform/arch combo ${platform2}/${arch}`);
|
|
23003
23572
|
return null;
|
|
23004
23573
|
}
|
|
23005
23574
|
const matchingAsset = assets.find((a) => a.name === expected.name);
|
|
@@ -23009,7 +23578,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
23009
23578
|
}
|
|
23010
23579
|
const pkgDir = ghPackageDir(spec);
|
|
23011
23580
|
const extractDir = ghExtractDir(spec);
|
|
23012
|
-
const archivePath =
|
|
23581
|
+
const archivePath = join9(pkgDir, expected.name);
|
|
23013
23582
|
log(`[lsp] downloading ${spec.id} ${tag} \u2192 ${matchingAsset.url}`);
|
|
23014
23583
|
try {
|
|
23015
23584
|
await downloadFile(matchingAsset.url, archivePath, fetchImpl, matchingAsset.size, signal);
|
|
@@ -23048,16 +23617,16 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
23048
23617
|
unlinkSync3(archivePath);
|
|
23049
23618
|
} catch {}
|
|
23050
23619
|
}
|
|
23051
|
-
const innerBinaryPath =
|
|
23052
|
-
if (!
|
|
23620
|
+
const innerBinaryPath = join9(extractDir, spec.binaryPathInArchive(platform2, arch, version2));
|
|
23621
|
+
if (!existsSync6(innerBinaryPath)) {
|
|
23053
23622
|
error48(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
|
|
23054
23623
|
return null;
|
|
23055
23624
|
}
|
|
23056
|
-
const targetBinary = ghBinaryPath(spec,
|
|
23057
|
-
mkdirSync3(
|
|
23625
|
+
const targetBinary = ghBinaryPath(spec, platform2);
|
|
23626
|
+
mkdirSync3(dirname3(targetBinary), { recursive: true });
|
|
23058
23627
|
try {
|
|
23059
23628
|
copyFileSync(innerBinaryPath, targetBinary);
|
|
23060
|
-
if (
|
|
23629
|
+
if (platform2 !== "win32") {
|
|
23061
23630
|
const { chmodSync: chmodSync2 } = await import("fs");
|
|
23062
23631
|
chmodSync2(targetBinary, 493);
|
|
23063
23632
|
}
|
|
@@ -23068,11 +23637,11 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
23068
23637
|
log(`[lsp] installed ${spec.id} ${tag} at ${targetBinary}`);
|
|
23069
23638
|
return archiveSha256;
|
|
23070
23639
|
}
|
|
23071
|
-
async function ensureGithubInstalled(spec, config2, fetchImpl,
|
|
23640
|
+
async function ensureGithubInstalled(spec, config2, fetchImpl, platform2, arch, signal) {
|
|
23072
23641
|
const outcome = await withInstallLock(spec.githubRepo, async () => {
|
|
23073
23642
|
const { tag, assets, blockedByGrace, reason } = await resolveTargetTag(spec, config2, fetchImpl, signal);
|
|
23074
23643
|
if (!tag) {
|
|
23075
|
-
const installed = isGithubInstalled(spec,
|
|
23644
|
+
const installed = isGithubInstalled(spec, platform2);
|
|
23076
23645
|
if (installed) {
|
|
23077
23646
|
warn(`[lsp] no eligible release of ${spec.githubRepo} (grace=${config2.graceDays}d); keeping existing install`);
|
|
23078
23647
|
return { started: false, reason: "kept existing install" };
|
|
@@ -23081,7 +23650,7 @@ async function ensureGithubInstalled(spec, config2, fetchImpl, platform, arch, s
|
|
|
23081
23650
|
warn(`[lsp] skipping ${spec.id}: ${fallbackReason}`);
|
|
23082
23651
|
return { started: false, reason: fallbackReason };
|
|
23083
23652
|
}
|
|
23084
|
-
if (isGithubInstalled(spec,
|
|
23653
|
+
if (isGithubInstalled(spec, platform2)) {
|
|
23085
23654
|
const installedMeta = readInstalledMetaIn(ghPackageDir(spec));
|
|
23086
23655
|
if (installedMeta && installedMeta.version === tag) {
|
|
23087
23656
|
return { started: false, reason: "already installed" };
|
|
@@ -23092,7 +23661,7 @@ async function ensureGithubInstalled(spec, config2, fetchImpl, platform, arch, s
|
|
|
23092
23661
|
log(`[lsp] reinstalling ${spec.id}@${tag}: no installed-version metadata recorded`);
|
|
23093
23662
|
}
|
|
23094
23663
|
}
|
|
23095
|
-
const archiveSha256 = await downloadAndInstall(spec, tag, assets,
|
|
23664
|
+
const archiveSha256 = await downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl, signal).catch((err) => {
|
|
23096
23665
|
error48(`[lsp] github install ${spec.id} crashed: ${err}`);
|
|
23097
23666
|
return null;
|
|
23098
23667
|
});
|
|
@@ -23130,7 +23699,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl = fetch) {
|
|
|
23130
23699
|
if (!host) {
|
|
23131
23700
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
23132
23701
|
try {
|
|
23133
|
-
if (
|
|
23702
|
+
if (existsSync6(ghBinDir(spec))) {
|
|
23134
23703
|
cachedBinDirs.push(ghBinDir(spec));
|
|
23135
23704
|
}
|
|
23136
23705
|
} catch {}
|
|
@@ -23290,9 +23859,9 @@ function normalizeToolMap(tools) {
|
|
|
23290
23859
|
}
|
|
23291
23860
|
|
|
23292
23861
|
// src/notifications.ts
|
|
23293
|
-
import { existsSync as
|
|
23294
|
-
import { homedir as
|
|
23295
|
-
import { join as
|
|
23862
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
23863
|
+
import { homedir as homedir6, platform as platform2 } from "os";
|
|
23864
|
+
import { join as join10 } from "path";
|
|
23296
23865
|
function isTuiMode() {
|
|
23297
23866
|
return process.env.OPENCODE_CLIENT === "cli";
|
|
23298
23867
|
}
|
|
@@ -23313,28 +23882,28 @@ var WARNING_MARKER = `${AFT_MARKER} \u26A0\uFE0F`;
|
|
|
23313
23882
|
var STATUS_MARKER = `${AFT_MARKER} \u2705`;
|
|
23314
23883
|
var WARNED_TOOLS_FILE = "warned_tools.json";
|
|
23315
23884
|
function getDesktopStatePath() {
|
|
23316
|
-
const os2 =
|
|
23317
|
-
const home =
|
|
23885
|
+
const os2 = platform2();
|
|
23886
|
+
const home = homedir6();
|
|
23318
23887
|
if (os2 === "darwin") {
|
|
23319
|
-
return
|
|
23888
|
+
return join10(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
|
|
23320
23889
|
}
|
|
23321
23890
|
if (os2 === "linux") {
|
|
23322
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ||
|
|
23323
|
-
return
|
|
23891
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join10(home, ".config");
|
|
23892
|
+
return join10(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
|
|
23324
23893
|
}
|
|
23325
23894
|
if (os2 === "win32") {
|
|
23326
|
-
const appData = process.env.APPDATA ||
|
|
23327
|
-
return
|
|
23895
|
+
const appData = process.env.APPDATA || join10(home, "AppData", "Roaming");
|
|
23896
|
+
return join10(appData, "ai.opencode.desktop", "opencode.global.dat");
|
|
23328
23897
|
}
|
|
23329
23898
|
return null;
|
|
23330
23899
|
}
|
|
23331
23900
|
function readDesktopState(directory) {
|
|
23332
23901
|
const statePath = getDesktopStatePath();
|
|
23333
|
-
if (!statePath || !
|
|
23902
|
+
if (!statePath || !existsSync7(statePath)) {
|
|
23334
23903
|
return { sessionId: null, serverUrl: null };
|
|
23335
23904
|
}
|
|
23336
23905
|
try {
|
|
23337
|
-
const raw =
|
|
23906
|
+
const raw = readFileSync5(statePath, "utf-8");
|
|
23338
23907
|
const state = JSON.parse(raw);
|
|
23339
23908
|
let serverUrl = null;
|
|
23340
23909
|
const serverStr = state.server;
|
|
@@ -23428,10 +23997,10 @@ async function sendWarning(opts, message) {
|
|
|
23428
23997
|
}
|
|
23429
23998
|
async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
|
|
23430
23999
|
if (storageDir) {
|
|
23431
|
-
const versionFile =
|
|
24000
|
+
const versionFile = join10(storageDir, "last_announced_version");
|
|
23432
24001
|
try {
|
|
23433
|
-
if (
|
|
23434
|
-
const lastVersion =
|
|
24002
|
+
if (existsSync7(versionFile)) {
|
|
24003
|
+
const lastVersion = readFileSync5(versionFile, "utf-8").trim();
|
|
23435
24004
|
if (lastVersion === version2)
|
|
23436
24005
|
return;
|
|
23437
24006
|
}
|
|
@@ -23452,16 +24021,16 @@ async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
|
|
|
23452
24021
|
if (storageDir) {
|
|
23453
24022
|
try {
|
|
23454
24023
|
mkdirSync4(storageDir, { recursive: true });
|
|
23455
|
-
|
|
24024
|
+
writeFileSync4(join10(storageDir, "last_announced_version"), version2);
|
|
23456
24025
|
} catch {}
|
|
23457
24026
|
}
|
|
23458
24027
|
}
|
|
23459
24028
|
function readWarnedTools(storageDir) {
|
|
23460
24029
|
try {
|
|
23461
|
-
const warnedToolsPath =
|
|
23462
|
-
if (!
|
|
24030
|
+
const warnedToolsPath = join10(storageDir, WARNED_TOOLS_FILE);
|
|
24031
|
+
if (!existsSync7(warnedToolsPath))
|
|
23463
24032
|
return {};
|
|
23464
|
-
const parsed = JSON.parse(
|
|
24033
|
+
const parsed = JSON.parse(readFileSync5(warnedToolsPath, "utf-8"));
|
|
23465
24034
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
23466
24035
|
return {};
|
|
23467
24036
|
const warned = {};
|
|
@@ -23478,8 +24047,8 @@ function readWarnedTools(storageDir) {
|
|
|
23478
24047
|
function writeWarnedTools(storageDir, warned) {
|
|
23479
24048
|
try {
|
|
23480
24049
|
mkdirSync4(storageDir, { recursive: true });
|
|
23481
|
-
const warnedToolsPath =
|
|
23482
|
-
|
|
24050
|
+
const warnedToolsPath = join10(storageDir, WARNED_TOOLS_FILE);
|
|
24051
|
+
writeFileSync4(warnedToolsPath, `${JSON.stringify(warned, null, 2)}
|
|
23483
24052
|
`);
|
|
23484
24053
|
} catch {}
|
|
23485
24054
|
}
|
|
@@ -23570,10 +24139,37 @@ async function cleanupWarnings(opts) {
|
|
|
23570
24139
|
}
|
|
23571
24140
|
|
|
23572
24141
|
// src/onnx-runtime.ts
|
|
23573
|
-
import {
|
|
23574
|
-
import {
|
|
24142
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
24143
|
+
import { createHash as createHash3 } from "crypto";
|
|
24144
|
+
import {
|
|
24145
|
+
chmodSync as chmodSync2,
|
|
24146
|
+
closeSync as closeSync2,
|
|
24147
|
+
copyFileSync as copyFileSync2,
|
|
24148
|
+
createWriteStream as createWriteStream2,
|
|
24149
|
+
existsSync as existsSync8,
|
|
24150
|
+
lstatSync as lstatSync2,
|
|
24151
|
+
mkdirSync as mkdirSync5,
|
|
24152
|
+
openSync as openSync2,
|
|
24153
|
+
readdirSync as readdirSync3,
|
|
24154
|
+
readFileSync as readFileSync6,
|
|
24155
|
+
readlinkSync as readlinkSync2,
|
|
24156
|
+
realpathSync as realpathSync2,
|
|
24157
|
+
rmSync as rmSync3,
|
|
24158
|
+
statSync as statSync5,
|
|
24159
|
+
symlinkSync,
|
|
24160
|
+
unlinkSync as unlinkSync4,
|
|
24161
|
+
writeFileSync as writeFileSync5
|
|
24162
|
+
} from "fs";
|
|
24163
|
+
import { dirname as dirname4, join as join11, relative as relative2, resolve as resolve3 } from "path";
|
|
24164
|
+
import { Readable as Readable2 } from "stream";
|
|
24165
|
+
import { pipeline as pipeline2 } from "stream/promises";
|
|
23575
24166
|
var ORT_VERSION = "1.24.4";
|
|
23576
24167
|
var ORT_REPO = "microsoft/onnxruntime";
|
|
24168
|
+
var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
|
|
24169
|
+
var MAX_EXTRACT_BYTES2 = 1 * 1024 * 1024 * 1024;
|
|
24170
|
+
var ONNX_LOCK_FILE = ".aft-onnx-installing";
|
|
24171
|
+
var ONNX_INSTALLED_META_FILE = ".aft-onnx-installed";
|
|
24172
|
+
var STALE_LOCK_MS2 = 30 * 60 * 1000;
|
|
23577
24173
|
var ORT_PLATFORM_MAP = {
|
|
23578
24174
|
darwin: {
|
|
23579
24175
|
arm64: {
|
|
@@ -23627,11 +24223,27 @@ function getManualInstallHint() {
|
|
|
23627
24223
|
}
|
|
23628
24224
|
async function ensureOnnxRuntime(storageDir) {
|
|
23629
24225
|
const info = getPlatformInfo();
|
|
23630
|
-
const ortDir =
|
|
23631
|
-
const libPath =
|
|
23632
|
-
if (
|
|
23633
|
-
|
|
23634
|
-
|
|
24226
|
+
const ortDir = join11(storageDir, "onnxruntime", ORT_VERSION);
|
|
24227
|
+
const libPath = join11(ortDir, info?.libName ?? "libonnxruntime.dylib");
|
|
24228
|
+
if (existsSync8(libPath)) {
|
|
24229
|
+
const meta3 = readOnnxInstalledMeta(ortDir);
|
|
24230
|
+
if (meta3?.sha256) {
|
|
24231
|
+
try {
|
|
24232
|
+
const currentHash = sha256File(libPath);
|
|
24233
|
+
if (currentHash !== meta3.sha256) {
|
|
24234
|
+
error48(`ONNX Runtime at ${ortDir}: TOFU sha256 mismatch \u2014 refusing to use ` + `tampered binary. Recorded ${meta3.sha256}, current ${currentHash}. ` + `Run \`aft doctor --clear\` to re-download from scratch.`);
|
|
24235
|
+
} else {
|
|
24236
|
+
log(`ONNX Runtime found at ${ortDir} (TOFU verified)`);
|
|
24237
|
+
return ortDir;
|
|
24238
|
+
}
|
|
24239
|
+
} catch (err) {
|
|
24240
|
+
warn(`Could not verify ONNX Runtime hash at ${ortDir}: ${err}`);
|
|
24241
|
+
return ortDir;
|
|
24242
|
+
}
|
|
24243
|
+
} else {
|
|
24244
|
+
log(`ONNX Runtime found at ${ortDir} (no recorded hash, accepting)`);
|
|
24245
|
+
return ortDir;
|
|
24246
|
+
}
|
|
23635
24247
|
}
|
|
23636
24248
|
const systemPath = findSystemOnnxRuntime(info?.libName);
|
|
23637
24249
|
if (systemPath) {
|
|
@@ -23642,7 +24254,18 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
23642
24254
|
warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
|
|
23643
24255
|
return null;
|
|
23644
24256
|
}
|
|
23645
|
-
|
|
24257
|
+
const onnxBaseDir = join11(storageDir, "onnxruntime");
|
|
24258
|
+
mkdirSync5(onnxBaseDir, { recursive: true });
|
|
24259
|
+
const lockPath2 = join11(onnxBaseDir, ONNX_LOCK_FILE);
|
|
24260
|
+
if (!acquireLock(lockPath2)) {
|
|
24261
|
+
warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath2}). Skipping.`);
|
|
24262
|
+
return null;
|
|
24263
|
+
}
|
|
24264
|
+
try {
|
|
24265
|
+
return await downloadOnnxRuntime(info, ortDir);
|
|
24266
|
+
} finally {
|
|
24267
|
+
releaseLock(lockPath2);
|
|
24268
|
+
}
|
|
23646
24269
|
}
|
|
23647
24270
|
function findSystemOnnxRuntime(libName) {
|
|
23648
24271
|
if (!libName)
|
|
@@ -23654,40 +24277,119 @@ function findSystemOnnxRuntime(libName) {
|
|
|
23654
24277
|
searchPaths.push("/usr/lib", "/usr/lib/x86_64-linux-gnu", "/usr/lib/aarch64-linux-gnu", "/usr/local/lib");
|
|
23655
24278
|
}
|
|
23656
24279
|
for (const dir of searchPaths) {
|
|
23657
|
-
if (
|
|
24280
|
+
if (existsSync8(join11(dir, libName))) {
|
|
23658
24281
|
return dir;
|
|
23659
24282
|
}
|
|
23660
24283
|
}
|
|
23661
24284
|
return null;
|
|
23662
24285
|
}
|
|
24286
|
+
async function downloadFileWithCap(url2, destPath) {
|
|
24287
|
+
const controller = new AbortController;
|
|
24288
|
+
const timeout = setTimeout(() => controller.abort(), 300000);
|
|
24289
|
+
try {
|
|
24290
|
+
const res = await fetch(url2, {
|
|
24291
|
+
headers: { accept: "application/octet-stream" },
|
|
24292
|
+
redirect: "follow",
|
|
24293
|
+
signal: controller.signal
|
|
24294
|
+
});
|
|
24295
|
+
if (!res.ok || !res.body) {
|
|
24296
|
+
throw new Error(`download failed (HTTP ${res.status})`);
|
|
24297
|
+
}
|
|
24298
|
+
const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
|
|
24299
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
24300
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
24301
|
+
}
|
|
24302
|
+
mkdirSync5(dirname4(destPath), { recursive: true });
|
|
24303
|
+
let bytesWritten = 0;
|
|
24304
|
+
const guard = new TransformStream({
|
|
24305
|
+
transform(chunk, transformController) {
|
|
24306
|
+
bytesWritten += chunk.byteLength;
|
|
24307
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES2) {
|
|
24308
|
+
transformController.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES2} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
24309
|
+
return;
|
|
24310
|
+
}
|
|
24311
|
+
transformController.enqueue(chunk);
|
|
24312
|
+
}
|
|
24313
|
+
});
|
|
24314
|
+
const guarded = res.body.pipeThrough(guard);
|
|
24315
|
+
const nodeStream = Readable2.fromWeb(guarded);
|
|
24316
|
+
await pipeline2(nodeStream, createWriteStream2(destPath), { signal: controller.signal });
|
|
24317
|
+
} catch (err) {
|
|
24318
|
+
try {
|
|
24319
|
+
unlinkSync4(destPath);
|
|
24320
|
+
} catch {}
|
|
24321
|
+
throw err;
|
|
24322
|
+
} finally {
|
|
24323
|
+
clearTimeout(timeout);
|
|
24324
|
+
}
|
|
24325
|
+
}
|
|
24326
|
+
function validateExtractedTree(stagingRoot) {
|
|
24327
|
+
const realRoot = realpathSync2(stagingRoot);
|
|
24328
|
+
let totalBytes = 0;
|
|
24329
|
+
const walk = (dir) => {
|
|
24330
|
+
const entries = readdirSync3(dir);
|
|
24331
|
+
for (const entry of entries) {
|
|
24332
|
+
const fullPath = join11(dir, entry);
|
|
24333
|
+
const lst = lstatSync2(fullPath);
|
|
24334
|
+
if (lst.isSymbolicLink()) {
|
|
24335
|
+
const linkTarget = readlinkSync2(fullPath);
|
|
24336
|
+
const resolvedTarget = resolve3(dirname4(fullPath), linkTarget);
|
|
24337
|
+
const rel2 = relative2(realRoot, resolvedTarget);
|
|
24338
|
+
if (rel2.startsWith("..") || process.platform !== "win32" && rel2.startsWith("/")) {
|
|
24339
|
+
throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
|
|
24340
|
+
}
|
|
24341
|
+
continue;
|
|
24342
|
+
}
|
|
24343
|
+
const rel = relative2(realRoot, fullPath);
|
|
24344
|
+
if (rel.startsWith("..") || process.platform !== "win32" && rel.startsWith("/")) {
|
|
24345
|
+
throw new Error(`extracted entry ${fullPath} escapes staging root`);
|
|
24346
|
+
}
|
|
24347
|
+
if (lst.isDirectory()) {
|
|
24348
|
+
walk(fullPath);
|
|
24349
|
+
continue;
|
|
24350
|
+
}
|
|
24351
|
+
if (lst.isFile()) {
|
|
24352
|
+
totalBytes += lst.size;
|
|
24353
|
+
if (totalBytes > MAX_EXTRACT_BYTES2) {
|
|
24354
|
+
throw new Error(`extracted size ${totalBytes} exceeds max ${MAX_EXTRACT_BYTES2} (decompression bomb defense)`);
|
|
24355
|
+
}
|
|
24356
|
+
}
|
|
24357
|
+
}
|
|
24358
|
+
};
|
|
24359
|
+
walk(realRoot);
|
|
24360
|
+
}
|
|
23663
24361
|
async function downloadOnnxRuntime(info, targetDir) {
|
|
23664
24362
|
const url2 = `https://github.com/${ORT_REPO}/releases/download/v${ORT_VERSION}/${info.assetName}.${info.archiveType === "tgz" ? "tgz" : "zip"}`;
|
|
23665
24363
|
log(`Downloading ONNX Runtime v${ORT_VERSION} for ${process.platform}/${process.arch}...`);
|
|
24364
|
+
const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
|
|
23666
24365
|
try {
|
|
23667
|
-
const tmpDir = `${targetDir}.tmp.${process.pid}`;
|
|
23668
24366
|
mkdirSync5(tmpDir, { recursive: true });
|
|
23669
|
-
const archivePath =
|
|
23670
|
-
|
|
23671
|
-
|
|
23672
|
-
|
|
23673
|
-
timeout: 120000
|
|
23674
|
-
});
|
|
24367
|
+
const archivePath = join11(tmpDir, `onnxruntime.${info.archiveType}`);
|
|
24368
|
+
await downloadFileWithCap(url2, archivePath);
|
|
24369
|
+
const archiveSha256 = sha256File(archivePath);
|
|
24370
|
+
log(`ONNX Runtime archive sha256=${archiveSha256}`);
|
|
23675
24371
|
if (info.archiveType === "tgz") {
|
|
23676
|
-
execFileSync2("tar", ["xzf", archivePath, "-C", tmpDir], {
|
|
24372
|
+
execFileSync2("tar", ["xzf", archivePath, "-C", tmpDir], {
|
|
24373
|
+
stdio: "pipe",
|
|
24374
|
+
timeout: 120000
|
|
24375
|
+
});
|
|
23677
24376
|
} else {
|
|
23678
24377
|
await extractZipArchive(archivePath, tmpDir);
|
|
23679
24378
|
}
|
|
23680
|
-
|
|
23681
|
-
|
|
24379
|
+
try {
|
|
24380
|
+
unlinkSync4(archivePath);
|
|
24381
|
+
} catch {}
|
|
24382
|
+
validateExtractedTree(tmpDir);
|
|
24383
|
+
const extractedDir = join11(tmpDir, info.assetName, "lib");
|
|
24384
|
+
if (!existsSync8(extractedDir)) {
|
|
23682
24385
|
throw new Error(`Expected directory not found: ${extractedDir}`);
|
|
23683
24386
|
}
|
|
23684
24387
|
mkdirSync5(targetDir, { recursive: true });
|
|
23685
24388
|
const libFiles = readdirSync3(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
|
|
23686
|
-
const { lstatSync: lstatSync2, symlinkSync, readlinkSync: readlinkSync2, copyFileSync: cpFile } = await import("fs");
|
|
23687
24389
|
const realFiles = [];
|
|
23688
24390
|
const symlinks = [];
|
|
23689
24391
|
for (const libFile of libFiles) {
|
|
23690
|
-
const src =
|
|
24392
|
+
const src = join11(extractedDir, libFile);
|
|
23691
24393
|
try {
|
|
23692
24394
|
const stat = lstatSync2(src);
|
|
23693
24395
|
log(`ORT extract: ${libFile} \u2014 isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
|
|
@@ -23702,10 +24404,10 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
23702
24404
|
}
|
|
23703
24405
|
}
|
|
23704
24406
|
for (const libFile of realFiles) {
|
|
23705
|
-
const src =
|
|
23706
|
-
const dst =
|
|
24407
|
+
const src = join11(extractedDir, libFile);
|
|
24408
|
+
const dst = join11(targetDir, libFile);
|
|
23707
24409
|
try {
|
|
23708
|
-
|
|
24410
|
+
copyFileSync2(src, dst);
|
|
23709
24411
|
if (process.platform !== "win32") {
|
|
23710
24412
|
chmodSync2(dst, 493);
|
|
23711
24413
|
}
|
|
@@ -23714,27 +24416,35 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
23714
24416
|
}
|
|
23715
24417
|
}
|
|
23716
24418
|
for (const link of symlinks) {
|
|
23717
|
-
const dst =
|
|
24419
|
+
const dst = join11(targetDir, link.name);
|
|
23718
24420
|
try {
|
|
23719
24421
|
unlinkSync4(dst);
|
|
23720
24422
|
} catch {}
|
|
23721
24423
|
symlinkSync(link.target, dst);
|
|
23722
24424
|
}
|
|
23723
|
-
const
|
|
23724
|
-
|
|
24425
|
+
const libPath = join11(targetDir, info.libName);
|
|
24426
|
+
let libHash = null;
|
|
24427
|
+
try {
|
|
24428
|
+
libHash = sha256File(libPath);
|
|
24429
|
+
} catch (err) {
|
|
24430
|
+
warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
|
|
24431
|
+
}
|
|
24432
|
+
writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
|
|
24433
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
23725
24434
|
log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
|
|
23726
24435
|
return targetDir;
|
|
23727
24436
|
} catch (err) {
|
|
23728
24437
|
error48(`Failed to download ONNX Runtime: ${err}`);
|
|
23729
24438
|
try {
|
|
23730
|
-
|
|
23731
|
-
|
|
24439
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
24440
|
+
} catch {}
|
|
24441
|
+
try {
|
|
24442
|
+
rmSync3(targetDir, { recursive: true, force: true });
|
|
23732
24443
|
} catch {}
|
|
23733
24444
|
return null;
|
|
23734
24445
|
}
|
|
23735
24446
|
}
|
|
23736
24447
|
async function extractZipArchive(archivePath, destinationDir) {
|
|
23737
|
-
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
23738
24448
|
if (process.platform === "win32") {
|
|
23739
24449
|
execFileSync2("tar.exe", ["-xf", archivePath, "-C", destinationDir], {
|
|
23740
24450
|
stdio: "pipe",
|
|
@@ -23747,11 +24457,138 @@ async function extractZipArchive(archivePath, destinationDir) {
|
|
|
23747
24457
|
timeout: 120000
|
|
23748
24458
|
});
|
|
23749
24459
|
}
|
|
24460
|
+
function writeOnnxInstalledMeta(installDir, version2, sha256, archiveSha256) {
|
|
24461
|
+
try {
|
|
24462
|
+
const meta3 = {
|
|
24463
|
+
version: version2,
|
|
24464
|
+
installedAt: new Date().toISOString(),
|
|
24465
|
+
...sha256 ? { sha256 } : {},
|
|
24466
|
+
archiveSha256
|
|
24467
|
+
};
|
|
24468
|
+
writeFileSync5(join11(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
|
|
24469
|
+
} catch (err) {
|
|
24470
|
+
log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
|
|
24471
|
+
}
|
|
24472
|
+
}
|
|
24473
|
+
function readOnnxInstalledMeta(installDir) {
|
|
24474
|
+
const path2 = join11(installDir, ONNX_INSTALLED_META_FILE);
|
|
24475
|
+
try {
|
|
24476
|
+
if (!statSync5(path2).isFile())
|
|
24477
|
+
return null;
|
|
24478
|
+
const raw = readFileSync6(path2, "utf8");
|
|
24479
|
+
const parsed = JSON.parse(raw);
|
|
24480
|
+
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
24481
|
+
return null;
|
|
24482
|
+
return {
|
|
24483
|
+
version: parsed.version,
|
|
24484
|
+
installedAt: typeof parsed.installedAt === "string" ? parsed.installedAt : "",
|
|
24485
|
+
...typeof parsed.sha256 === "string" && parsed.sha256.length > 0 ? { sha256: parsed.sha256 } : {},
|
|
24486
|
+
...typeof parsed.archiveSha256 === "string" && parsed.archiveSha256.length > 0 ? { archiveSha256: parsed.archiveSha256 } : {}
|
|
24487
|
+
};
|
|
24488
|
+
} catch {
|
|
24489
|
+
return null;
|
|
24490
|
+
}
|
|
24491
|
+
}
|
|
24492
|
+
function sha256File(path2) {
|
|
24493
|
+
const hash2 = createHash3("sha256");
|
|
24494
|
+
hash2.update(readFileSync6(path2));
|
|
24495
|
+
return hash2.digest("hex");
|
|
24496
|
+
}
|
|
24497
|
+
function acquireLock(lockPath2) {
|
|
24498
|
+
const tryClaim = () => {
|
|
24499
|
+
try {
|
|
24500
|
+
const fd = openSync2(lockPath2, "wx");
|
|
24501
|
+
try {
|
|
24502
|
+
writeFileSync5(fd, `${process.pid}
|
|
24503
|
+
${new Date().toISOString()}
|
|
24504
|
+
`);
|
|
24505
|
+
} finally {
|
|
24506
|
+
closeSync2(fd);
|
|
24507
|
+
}
|
|
24508
|
+
return true;
|
|
24509
|
+
} catch (err) {
|
|
24510
|
+
const code = err.code;
|
|
24511
|
+
if (code === "EEXIST")
|
|
24512
|
+
return false;
|
|
24513
|
+
warn(`[onnx] unexpected error acquiring lock ${lockPath2}: ${err}`);
|
|
24514
|
+
return false;
|
|
24515
|
+
}
|
|
24516
|
+
};
|
|
24517
|
+
if (tryClaim())
|
|
24518
|
+
return true;
|
|
24519
|
+
let owningPid = null;
|
|
24520
|
+
let lockMtimeMs = 0;
|
|
24521
|
+
try {
|
|
24522
|
+
const raw = readFileSync6(lockPath2, "utf8");
|
|
24523
|
+
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
24524
|
+
const parsed = Number.parseInt(firstLine, 10);
|
|
24525
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
24526
|
+
owningPid = parsed;
|
|
24527
|
+
lockMtimeMs = statSync5(lockPath2).mtimeMs;
|
|
24528
|
+
} catch {
|
|
24529
|
+
return tryClaim();
|
|
24530
|
+
}
|
|
24531
|
+
const age = Date.now() - lockMtimeMs;
|
|
24532
|
+
const ageWithinFresh = Math.abs(age) < STALE_LOCK_MS2;
|
|
24533
|
+
const skipLiveness = process.platform === "win32";
|
|
24534
|
+
const ownerAlive = !skipLiveness && owningPid !== null && isProcessAlive2(owningPid);
|
|
24535
|
+
if (skipLiveness ? ageWithinFresh : ownerAlive && ageWithinFresh) {
|
|
24536
|
+
return false;
|
|
24537
|
+
}
|
|
24538
|
+
log(`[onnx] reclaiming install lock (owner_pid=${owningPid ?? "unknown"}, alive=${ownerAlive}, age_ms=${age})`);
|
|
24539
|
+
try {
|
|
24540
|
+
unlinkSync4(lockPath2);
|
|
24541
|
+
} catch {}
|
|
24542
|
+
return tryClaim();
|
|
24543
|
+
}
|
|
24544
|
+
function releaseLock(lockPath2) {
|
|
24545
|
+
try {
|
|
24546
|
+
let owningPid = null;
|
|
24547
|
+
try {
|
|
24548
|
+
const raw = readFileSync6(lockPath2, "utf8");
|
|
24549
|
+
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
24550
|
+
const parsed = Number.parseInt(firstLine, 10);
|
|
24551
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
24552
|
+
owningPid = parsed;
|
|
24553
|
+
} catch (readErr) {
|
|
24554
|
+
const code = readErr.code;
|
|
24555
|
+
if (code === "ENOENT")
|
|
24556
|
+
return;
|
|
24557
|
+
warn(`[onnx] could not read lock ${lockPath2} during release: ${readErr}`);
|
|
24558
|
+
return;
|
|
24559
|
+
}
|
|
24560
|
+
if (owningPid !== process.pid) {
|
|
24561
|
+
log(`[onnx] not releasing lock ${lockPath2}: owned by pid ${owningPid ?? "unknown"} (we are ${process.pid})`);
|
|
24562
|
+
return;
|
|
24563
|
+
}
|
|
24564
|
+
try {
|
|
24565
|
+
unlinkSync4(lockPath2);
|
|
24566
|
+
} catch (unlinkErr) {
|
|
24567
|
+
const code = unlinkErr.code;
|
|
24568
|
+
if (code !== "ENOENT") {
|
|
24569
|
+
warn(`[onnx] failed to release lock ${lockPath2}: ${unlinkErr}`);
|
|
24570
|
+
}
|
|
24571
|
+
}
|
|
24572
|
+
} catch (err) {
|
|
24573
|
+
warn(`[onnx] unexpected error releasing lock ${lockPath2}: ${err}`);
|
|
24574
|
+
}
|
|
24575
|
+
}
|
|
24576
|
+
function isProcessAlive2(pid) {
|
|
24577
|
+
try {
|
|
24578
|
+
process.kill(pid, 0);
|
|
24579
|
+
return true;
|
|
24580
|
+
} catch (err) {
|
|
24581
|
+
const code = err.code;
|
|
24582
|
+
if (code === "ESRCH")
|
|
24583
|
+
return false;
|
|
24584
|
+
return true;
|
|
24585
|
+
}
|
|
24586
|
+
}
|
|
23750
24587
|
|
|
23751
24588
|
// src/bridge.ts
|
|
23752
|
-
import { spawn as
|
|
23753
|
-
import { homedir as
|
|
23754
|
-
import { join as
|
|
24589
|
+
import { spawn as spawn3 } from "child_process";
|
|
24590
|
+
import { homedir as homedir7 } from "os";
|
|
24591
|
+
import { join as join12 } from "path";
|
|
23755
24592
|
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
23756
24593
|
var SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5000;
|
|
23757
24594
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
@@ -23893,14 +24730,14 @@ class BinaryBridge {
|
|
|
23893
24730
|
const line = `${JSON.stringify(request)}
|
|
23894
24731
|
`;
|
|
23895
24732
|
const effectiveTimeoutMs = options?.timeoutMs ?? this.timeoutMs;
|
|
23896
|
-
return new Promise((
|
|
24733
|
+
return new Promise((resolve4, reject) => {
|
|
23897
24734
|
const timer = setTimeout(() => {
|
|
23898
24735
|
this.pending.delete(id);
|
|
23899
24736
|
warn(`Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms \u2014 restarting bridge`);
|
|
23900
24737
|
reject(new Error(`[aft-plugin] Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
23901
24738
|
this.handleTimeout();
|
|
23902
24739
|
}, effectiveTimeoutMs);
|
|
23903
|
-
this.pending.set(id, { resolve:
|
|
24740
|
+
this.pending.set(id, { resolve: resolve4, reject, timer });
|
|
23904
24741
|
if (!this.process?.stdin?.writable) {
|
|
23905
24742
|
this.pending.delete(id);
|
|
23906
24743
|
clearTimeout(timer);
|
|
@@ -23943,15 +24780,15 @@ class BinaryBridge {
|
|
|
23943
24780
|
if (this.process) {
|
|
23944
24781
|
const proc = this.process;
|
|
23945
24782
|
this.process = null;
|
|
23946
|
-
return new Promise((
|
|
24783
|
+
return new Promise((resolve4) => {
|
|
23947
24784
|
const forceKillTimer = setTimeout(() => {
|
|
23948
24785
|
proc.kill("SIGKILL");
|
|
23949
|
-
|
|
24786
|
+
resolve4();
|
|
23950
24787
|
}, 5000);
|
|
23951
24788
|
proc.once("exit", () => {
|
|
23952
24789
|
clearTimeout(forceKillTimer);
|
|
23953
24790
|
log("Process exited during shutdown");
|
|
23954
|
-
|
|
24791
|
+
resolve4();
|
|
23955
24792
|
});
|
|
23956
24793
|
proc.kill("SIGTERM");
|
|
23957
24794
|
});
|
|
@@ -23993,19 +24830,19 @@ class BinaryBridge {
|
|
|
23993
24830
|
})();
|
|
23994
24831
|
const useFastembedBackend = semanticBackend === undefined || semanticBackend === "fastembed" || semanticBackend === "";
|
|
23995
24832
|
const ortDir = typeof this.configOverrides._ort_dylib_dir === "string" && useFastembedBackend ? this.configOverrides._ort_dylib_dir : null;
|
|
23996
|
-
const ortLibraryPath = ortDir == null ? null :
|
|
24833
|
+
const ortLibraryPath = ortDir == null ? null : join12(ortDir, process.platform === "win32" ? "onnxruntime.dll" : process.platform === "darwin" ? "libonnxruntime.dylib" : "libonnxruntime.so");
|
|
23997
24834
|
const envPath = process.platform === "win32" && ortDir ? `${ortDir};${process.env.PATH ?? ""}` : process.env.PATH;
|
|
23998
24835
|
const env = {
|
|
23999
24836
|
...process.env,
|
|
24000
24837
|
...envPath ? { PATH: envPath } : {}
|
|
24001
24838
|
};
|
|
24002
24839
|
if (useFastembedBackend) {
|
|
24003
|
-
env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ?
|
|
24840
|
+
env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join12(this.configOverrides.storage_dir, "semantic", "models") : join12(homedir7() || "", ".cache", "fastembed"));
|
|
24004
24841
|
if (ortLibraryPath) {
|
|
24005
24842
|
env.ORT_DYLIB_PATH = ortLibraryPath;
|
|
24006
24843
|
}
|
|
24007
24844
|
}
|
|
24008
|
-
const child =
|
|
24845
|
+
const child = spawn3(this.binaryPath, [], {
|
|
24009
24846
|
cwd: this.cwd,
|
|
24010
24847
|
stdio: ["pipe", "pipe", "pipe"],
|
|
24011
24848
|
env
|
|
@@ -24266,10 +25103,10 @@ function normalizeKey(projectRoot) {
|
|
|
24266
25103
|
|
|
24267
25104
|
// src/resolver.ts
|
|
24268
25105
|
import { execSync, spawnSync } from "child_process";
|
|
24269
|
-
import { chmodSync as chmodSync3, copyFileSync as
|
|
25106
|
+
import { chmodSync as chmodSync3, copyFileSync as copyFileSync3, existsSync as existsSync9, mkdirSync as mkdirSync6, renameSync as renameSync2 } from "fs";
|
|
24270
25107
|
import { createRequire } from "module";
|
|
24271
|
-
import { homedir as
|
|
24272
|
-
import { join as
|
|
25108
|
+
import { homedir as homedir8 } from "os";
|
|
25109
|
+
import { join as join13 } from "path";
|
|
24273
25110
|
function copyToVersionedCache(npmBinaryPath) {
|
|
24274
25111
|
try {
|
|
24275
25112
|
const result = spawnSync(npmBinaryPath, ["--version"], {
|
|
@@ -24283,14 +25120,14 @@ function copyToVersionedCache(npmBinaryPath) {
|
|
|
24283
25120
|
const version2 = rawVersion.replace(/^aft\s+/, "");
|
|
24284
25121
|
const tag = version2.startsWith("v") ? version2 : `v${version2}`;
|
|
24285
25122
|
const cacheDir = getCacheDir();
|
|
24286
|
-
const versionedDir =
|
|
25123
|
+
const versionedDir = join13(cacheDir, tag);
|
|
24287
25124
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
24288
|
-
const cachedPath =
|
|
24289
|
-
if (
|
|
25125
|
+
const cachedPath = join13(versionedDir, `aft${ext}`);
|
|
25126
|
+
if (existsSync9(cachedPath))
|
|
24290
25127
|
return cachedPath;
|
|
24291
25128
|
mkdirSync6(versionedDir, { recursive: true });
|
|
24292
25129
|
const tmpPath = `${cachedPath}.tmp`;
|
|
24293
|
-
|
|
25130
|
+
copyFileSync3(npmBinaryPath, tmpPath);
|
|
24294
25131
|
if (process.platform !== "win32") {
|
|
24295
25132
|
chmodSync3(tmpPath, 493);
|
|
24296
25133
|
}
|
|
@@ -24302,14 +25139,14 @@ function copyToVersionedCache(npmBinaryPath) {
|
|
|
24302
25139
|
return null;
|
|
24303
25140
|
}
|
|
24304
25141
|
}
|
|
24305
|
-
function platformKey(
|
|
24306
|
-
const archMap = PLATFORM_ARCH_MAP[
|
|
25142
|
+
function platformKey(platform3 = process.platform, arch = process.arch) {
|
|
25143
|
+
const archMap = PLATFORM_ARCH_MAP[platform3];
|
|
24307
25144
|
if (!archMap) {
|
|
24308
|
-
throw new Error(`Unsupported platform: ${
|
|
25145
|
+
throw new Error(`Unsupported platform: ${platform3} (arch: ${arch}). ` + `Supported platforms: ${Object.keys(PLATFORM_ARCH_MAP).join(", ")}`);
|
|
24309
25146
|
}
|
|
24310
25147
|
const key = archMap[arch];
|
|
24311
25148
|
if (!key) {
|
|
24312
|
-
throw new Error(`Unsupported architecture: ${arch} on platform ${
|
|
25149
|
+
throw new Error(`Unsupported architecture: ${arch} on platform ${platform3}. ` + `Supported architectures for ${platform3}: ${Object.keys(archMap).join(", ")}`);
|
|
24313
25150
|
}
|
|
24314
25151
|
return key;
|
|
24315
25152
|
}
|
|
@@ -24333,7 +25170,7 @@ function findBinarySync() {
|
|
|
24333
25170
|
const packageBin = `@cortexkit/aft-${key}/bin/aft${ext}`;
|
|
24334
25171
|
const req = createRequire(import.meta.url);
|
|
24335
25172
|
const resolved = req.resolve(packageBin);
|
|
24336
|
-
if (
|
|
25173
|
+
if (existsSync9(resolved)) {
|
|
24337
25174
|
const copied = copyToVersionedCache(resolved);
|
|
24338
25175
|
return copied ?? resolved;
|
|
24339
25176
|
}
|
|
@@ -24347,8 +25184,8 @@ function findBinarySync() {
|
|
|
24347
25184
|
if (result)
|
|
24348
25185
|
return result;
|
|
24349
25186
|
} catch {}
|
|
24350
|
-
const cargoPath =
|
|
24351
|
-
if (
|
|
25187
|
+
const cargoPath = join13(homedir8(), ".cargo", "bin", `aft${ext}`);
|
|
25188
|
+
if (existsSync9(cargoPath))
|
|
24352
25189
|
return cargoPath;
|
|
24353
25190
|
return null;
|
|
24354
25191
|
}
|
|
@@ -24384,20 +25221,20 @@ async function findBinary() {
|
|
|
24384
25221
|
|
|
24385
25222
|
// src/shared/rpc-server.ts
|
|
24386
25223
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
24387
|
-
import { mkdirSync as mkdirSync7, renameSync as renameSync3, unlinkSync as unlinkSync5, writeFileSync as
|
|
25224
|
+
import { mkdirSync as mkdirSync7, renameSync as renameSync3, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
24388
25225
|
import { createServer } from "http";
|
|
24389
|
-
import { dirname as
|
|
25226
|
+
import { dirname as dirname5 } from "path";
|
|
24390
25227
|
|
|
24391
25228
|
// src/shared/rpc-utils.ts
|
|
24392
|
-
import { createHash as
|
|
24393
|
-
import { join as
|
|
25229
|
+
import { createHash as createHash4 } from "crypto";
|
|
25230
|
+
import { join as join14 } from "path";
|
|
24394
25231
|
function projectHash(directory) {
|
|
24395
25232
|
const normalized = directory.replace(/\/+$/, "");
|
|
24396
|
-
return
|
|
25233
|
+
return createHash4("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
24397
25234
|
}
|
|
24398
25235
|
function rpcPortFilePath(storageDir, directory) {
|
|
24399
25236
|
const hash2 = projectHash(directory);
|
|
24400
|
-
return
|
|
25237
|
+
return join14(storageDir, "rpc", hash2, "port");
|
|
24401
25238
|
}
|
|
24402
25239
|
|
|
24403
25240
|
// src/shared/rpc-server.ts
|
|
@@ -24414,7 +25251,7 @@ class AftRpcServer {
|
|
|
24414
25251
|
this.handlers.set(method, handler);
|
|
24415
25252
|
}
|
|
24416
25253
|
async start() {
|
|
24417
|
-
return new Promise((
|
|
25254
|
+
return new Promise((resolve4, reject) => {
|
|
24418
25255
|
const server = createServer((req, res) => this.dispatch(req, res));
|
|
24419
25256
|
server.on("error", (err) => {
|
|
24420
25257
|
warn(`RPC server error: ${err.message}`);
|
|
@@ -24430,16 +25267,16 @@ class AftRpcServer {
|
|
|
24430
25267
|
this.token = randomBytes2(32).toString("hex");
|
|
24431
25268
|
this.server = server;
|
|
24432
25269
|
try {
|
|
24433
|
-
const dir =
|
|
25270
|
+
const dir = dirname5(this.portFilePath);
|
|
24434
25271
|
mkdirSync7(dir, { recursive: true });
|
|
24435
25272
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
24436
|
-
|
|
25273
|
+
writeFileSync6(tmpPath, JSON.stringify({ port: this.port, token: this.token }), "utf-8");
|
|
24437
25274
|
renameSync3(tmpPath, this.portFilePath);
|
|
24438
25275
|
log(`RPC server listening on 127.0.0.1:${this.port}`);
|
|
24439
25276
|
} catch (err) {
|
|
24440
25277
|
warn(`Failed to write RPC port file: ${err}`);
|
|
24441
25278
|
}
|
|
24442
|
-
|
|
25279
|
+
resolve4(this.port);
|
|
24443
25280
|
});
|
|
24444
25281
|
server.unref();
|
|
24445
25282
|
});
|
|
@@ -24672,22 +25509,22 @@ function formatStatusMarkdown(status) {
|
|
|
24672
25509
|
}
|
|
24673
25510
|
|
|
24674
25511
|
// src/shared/tui-config.ts
|
|
24675
|
-
var
|
|
24676
|
-
import { existsSync as
|
|
24677
|
-
import { dirname as
|
|
25512
|
+
var import_comment_json4 = __toESM(require_src2(), 1);
|
|
25513
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
25514
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
24678
25515
|
|
|
24679
25516
|
// src/shared/opencode-config-dir.ts
|
|
24680
|
-
import { homedir as
|
|
24681
|
-
import { join as
|
|
25517
|
+
import { homedir as homedir9 } from "os";
|
|
25518
|
+
import { join as join15, resolve as resolve4 } from "path";
|
|
24682
25519
|
function getCliConfigDir() {
|
|
24683
25520
|
const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
24684
25521
|
if (envConfigDir) {
|
|
24685
|
-
return
|
|
25522
|
+
return resolve4(envConfigDir);
|
|
24686
25523
|
}
|
|
24687
25524
|
if (process.platform === "win32") {
|
|
24688
|
-
return
|
|
25525
|
+
return join15(homedir9(), ".config", "opencode");
|
|
24689
25526
|
}
|
|
24690
|
-
return
|
|
25527
|
+
return join15(process.env.XDG_CONFIG_HOME || join15(homedir9(), ".config"), "opencode");
|
|
24691
25528
|
}
|
|
24692
25529
|
function getOpenCodeConfigDir2(_options) {
|
|
24693
25530
|
return getCliConfigDir();
|
|
@@ -24696,10 +25533,10 @@ function getOpenCodeConfigPaths(options) {
|
|
|
24696
25533
|
const configDir = getOpenCodeConfigDir2(options);
|
|
24697
25534
|
return {
|
|
24698
25535
|
configDir,
|
|
24699
|
-
configJson:
|
|
24700
|
-
configJsonc:
|
|
24701
|
-
packageJson:
|
|
24702
|
-
omoConfig:
|
|
25536
|
+
configJson: join15(configDir, "opencode.json"),
|
|
25537
|
+
configJsonc: join15(configDir, "opencode.jsonc"),
|
|
25538
|
+
packageJson: join15(configDir, "package.json"),
|
|
25539
|
+
omoConfig: join15(configDir, "magic-context.jsonc")
|
|
24703
25540
|
};
|
|
24704
25541
|
}
|
|
24705
25542
|
|
|
@@ -24708,11 +25545,11 @@ var PLUGIN_NAME = "@cortexkit/aft-opencode";
|
|
|
24708
25545
|
var PLUGIN_ENTRY = `${PLUGIN_NAME}@latest`;
|
|
24709
25546
|
function resolveTuiConfigPath() {
|
|
24710
25547
|
const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
|
|
24711
|
-
const jsoncPath =
|
|
24712
|
-
const jsonPath =
|
|
24713
|
-
if (
|
|
25548
|
+
const jsoncPath = join16(configDir, "tui.jsonc");
|
|
25549
|
+
const jsonPath = join16(configDir, "tui.json");
|
|
25550
|
+
if (existsSync10(jsoncPath))
|
|
24714
25551
|
return jsoncPath;
|
|
24715
|
-
if (
|
|
25552
|
+
if (existsSync10(jsonPath))
|
|
24716
25553
|
return jsonPath;
|
|
24717
25554
|
return jsonPath;
|
|
24718
25555
|
}
|
|
@@ -24720,8 +25557,8 @@ function ensureTuiPluginEntry() {
|
|
|
24720
25557
|
try {
|
|
24721
25558
|
const configPath = resolveTuiConfigPath();
|
|
24722
25559
|
let config2 = {};
|
|
24723
|
-
if (
|
|
24724
|
-
config2 =
|
|
25560
|
+
if (existsSync10(configPath)) {
|
|
25561
|
+
config2 = import_comment_json4.parse(readFileSync7(configPath, "utf-8")) ?? {};
|
|
24725
25562
|
}
|
|
24726
25563
|
const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((value) => typeof value === "string") : [];
|
|
24727
25564
|
if (plugins.some((plugin) => plugin === PLUGIN_NAME || plugin.startsWith(`${PLUGIN_NAME}@`) || plugin.includes("opencode-plugin") || plugin.includes("aft-opencode"))) {
|
|
@@ -24729,8 +25566,8 @@ function ensureTuiPluginEntry() {
|
|
|
24729
25566
|
}
|
|
24730
25567
|
plugins.push(PLUGIN_ENTRY);
|
|
24731
25568
|
config2.plugin = plugins;
|
|
24732
|
-
mkdirSync8(
|
|
24733
|
-
|
|
25569
|
+
mkdirSync8(dirname6(configPath), { recursive: true });
|
|
25570
|
+
writeFileSync7(configPath, `${import_comment_json4.stringify(config2, null, 2)}
|
|
24734
25571
|
`);
|
|
24735
25572
|
log(`[aft-plugin] added TUI plugin entry to ${configPath}`);
|
|
24736
25573
|
return true;
|
|
@@ -24741,33 +25578,33 @@ function ensureTuiPluginEntry() {
|
|
|
24741
25578
|
}
|
|
24742
25579
|
|
|
24743
25580
|
// src/shared/url-fetch.ts
|
|
24744
|
-
import { createHash as
|
|
25581
|
+
import { createHash as createHash5 } from "crypto";
|
|
24745
25582
|
import { lookup } from "dns/promises";
|
|
24746
25583
|
import {
|
|
24747
|
-
existsSync as
|
|
25584
|
+
existsSync as existsSync11,
|
|
24748
25585
|
mkdirSync as mkdirSync9,
|
|
24749
25586
|
readdirSync as readdirSync4,
|
|
24750
|
-
readFileSync as
|
|
25587
|
+
readFileSync as readFileSync8,
|
|
24751
25588
|
unlinkSync as unlinkSync6,
|
|
24752
|
-
writeFileSync as
|
|
25589
|
+
writeFileSync as writeFileSync8
|
|
24753
25590
|
} from "fs";
|
|
24754
25591
|
import { isIP } from "net";
|
|
24755
|
-
import { join as
|
|
25592
|
+
import { join as join17 } from "path";
|
|
24756
25593
|
var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
24757
25594
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
24758
25595
|
var FETCH_TIMEOUT_MS = 30000;
|
|
24759
25596
|
var MAX_REDIRECTS = 5;
|
|
24760
25597
|
function cacheDir(storageDir) {
|
|
24761
|
-
return
|
|
25598
|
+
return join17(storageDir, "url_cache");
|
|
24762
25599
|
}
|
|
24763
25600
|
function hashUrl(url2) {
|
|
24764
|
-
return
|
|
25601
|
+
return createHash5("sha256").update(url2).digest("hex").slice(0, 16);
|
|
24765
25602
|
}
|
|
24766
25603
|
function metaPath(storageDir, hash2) {
|
|
24767
|
-
return
|
|
25604
|
+
return join17(cacheDir(storageDir), `${hash2}.meta.json`);
|
|
24768
25605
|
}
|
|
24769
25606
|
function contentPath(storageDir, hash2, extension) {
|
|
24770
|
-
return
|
|
25607
|
+
return join17(cacheDir(storageDir), `${hash2}${extension}`);
|
|
24771
25608
|
}
|
|
24772
25609
|
function _isPrivateIpv4(address) {
|
|
24773
25610
|
const parts = address.split(".").map((part) => Number.parseInt(part, 10));
|
|
@@ -24929,12 +25766,12 @@ async function fetchUrlToTempFile(url2, storageDir, options = {}) {
|
|
|
24929
25766
|
mkdirSync9(dir, { recursive: true });
|
|
24930
25767
|
const hash2 = hashUrl(url2);
|
|
24931
25768
|
const metaFile = metaPath(storageDir, hash2);
|
|
24932
|
-
if (
|
|
25769
|
+
if (existsSync11(metaFile)) {
|
|
24933
25770
|
try {
|
|
24934
|
-
const meta4 = JSON.parse(
|
|
25771
|
+
const meta4 = JSON.parse(readFileSync8(metaFile, "utf8"));
|
|
24935
25772
|
const age = Date.now() - meta4.fetchedAt;
|
|
24936
25773
|
const cached2 = contentPath(storageDir, hash2, meta4.extension);
|
|
24937
|
-
if (age < CACHE_TTL_MS &&
|
|
25774
|
+
if (age < CACHE_TTL_MS && existsSync11(cached2)) {
|
|
24938
25775
|
log(`URL cache hit: ${url2} (${Math.round(age / 1000)}s old)`);
|
|
24939
25776
|
return cached2;
|
|
24940
25777
|
}
|
|
@@ -24979,7 +25816,7 @@ async function fetchUrlToTempFile(url2, storageDir, options = {}) {
|
|
|
24979
25816
|
const body = Buffer.concat(chunks);
|
|
24980
25817
|
const contentFile = contentPath(storageDir, hash2, extension);
|
|
24981
25818
|
const tmpContent = `${contentFile}.tmp-${process.pid}`;
|
|
24982
|
-
|
|
25819
|
+
writeFileSync8(tmpContent, body);
|
|
24983
25820
|
const { renameSync: renameSync4 } = await import("fs");
|
|
24984
25821
|
renameSync4(tmpContent, contentFile);
|
|
24985
25822
|
const meta3 = {
|
|
@@ -24989,28 +25826,28 @@ async function fetchUrlToTempFile(url2, storageDir, options = {}) {
|
|
|
24989
25826
|
fetchedAt: Date.now()
|
|
24990
25827
|
};
|
|
24991
25828
|
const tmpMeta = `${metaFile}.tmp-${process.pid}`;
|
|
24992
|
-
|
|
25829
|
+
writeFileSync8(tmpMeta, JSON.stringify(meta3));
|
|
24993
25830
|
renameSync4(tmpMeta, metaFile);
|
|
24994
25831
|
log(`URL cached (${total} bytes): ${url2}`);
|
|
24995
25832
|
return contentFile;
|
|
24996
25833
|
}
|
|
24997
25834
|
function cleanupUrlCache(storageDir) {
|
|
24998
25835
|
const dir = cacheDir(storageDir);
|
|
24999
|
-
if (!
|
|
25836
|
+
if (!existsSync11(dir))
|
|
25000
25837
|
return;
|
|
25001
25838
|
let removed = 0;
|
|
25002
25839
|
try {
|
|
25003
25840
|
for (const entry of readdirSync4(dir)) {
|
|
25004
25841
|
if (!entry.endsWith(".meta.json"))
|
|
25005
25842
|
continue;
|
|
25006
|
-
const metaFile =
|
|
25843
|
+
const metaFile = join17(dir, entry);
|
|
25007
25844
|
try {
|
|
25008
|
-
const meta3 = JSON.parse(
|
|
25845
|
+
const meta3 = JSON.parse(readFileSync8(metaFile, "utf8"));
|
|
25009
25846
|
const age = Date.now() - meta3.fetchedAt;
|
|
25010
25847
|
if (age > CACHE_TTL_MS) {
|
|
25011
25848
|
const hash2 = entry.slice(0, -".meta.json".length);
|
|
25012
25849
|
const content = contentPath(storageDir, hash2, meta3.extension);
|
|
25013
|
-
if (
|
|
25850
|
+
if (existsSync11(content))
|
|
25014
25851
|
unlinkSync6(content);
|
|
25015
25852
|
unlinkSync6(metaFile);
|
|
25016
25853
|
removed++;
|
|
@@ -26056,11 +26893,11 @@ LSP errors detected, please fix:
|
|
|
26056
26893
|
};
|
|
26057
26894
|
}
|
|
26058
26895
|
function getEditDescription(writeToolName) {
|
|
26059
|
-
return `Edit a file by finding and replacing text, or by targeting named symbols.
|
|
26896
|
+
return `Edit a file by finding and replacing text, or by targeting named symbols. To write or overwrite a whole file, use the \`${writeToolName}\` tool \u2014 \`edit\` requires an explicit edit mode and will not silently overwrite a file from \`content\` alone.
|
|
26060
26897
|
|
|
26061
26898
|
**Modes** (determined by which parameters you provide):
|
|
26062
26899
|
|
|
26063
|
-
Mode priority: operations > edits > symbol (without oldString) > oldString (find/replace)
|
|
26900
|
+
Mode priority: operations > edits > symbol (without oldString) > oldString (find/replace). If none match, the call is rejected \u2014 there is no implicit "write" fallback.
|
|
26064
26901
|
|
|
26065
26902
|
1. **Multi-file transaction** \u2014 pass \`operations\` array
|
|
26066
26903
|
Edits across multiple files with checkpoint-based rollback on failure.
|
|
@@ -26123,6 +26960,10 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
26123
26960
|
dryRun: z3.boolean().optional().describe("Preview changes without applying (returns diff, default: false)")
|
|
26124
26961
|
},
|
|
26125
26962
|
execute: async (args, context) => {
|
|
26963
|
+
const argsRecord = args;
|
|
26964
|
+
if (argsRecord.startLine !== undefined || argsRecord.endLine !== undefined) {
|
|
26965
|
+
throw new Error("edit: 'startLine'/'endLine' are not top-level parameters. For line-range edits, nest them inside the `edits` array: `edits: [{ startLine: N, endLine: M, content: \"...\" }]`. For find/replace, use `oldString`/`newString` instead.");
|
|
26966
|
+
}
|
|
26126
26967
|
if (Array.isArray(args.operations)) {
|
|
26127
26968
|
const ops = args.operations;
|
|
26128
26969
|
const files = ops.map((op) => op.file).filter(Boolean);
|
|
@@ -26185,12 +27026,9 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
26185
27026
|
params.replace_all = args.replaceAll;
|
|
26186
27027
|
if (args.occurrence !== undefined)
|
|
26187
27028
|
params.occurrence = args.occurrence;
|
|
26188
|
-
} else if (typeof args.content === "string") {
|
|
26189
|
-
command = "write";
|
|
26190
|
-
params.content = args.content;
|
|
26191
|
-
params.create_dirs = true;
|
|
26192
27029
|
} else {
|
|
26193
|
-
|
|
27030
|
+
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', 'edits' array, or 'operations' array.";
|
|
27031
|
+
throw new Error(`edit: no edit mode resolved from arguments.${hint}`);
|
|
26194
27032
|
}
|
|
26195
27033
|
if (args.dryRun)
|
|
26196
27034
|
params.dry_run = true;
|
|
@@ -26590,7 +27428,29 @@ function aftPrefixedTools(ctx) {
|
|
|
26590
27428
|
...aftEditTool,
|
|
26591
27429
|
execute: async (args, context) => {
|
|
26592
27430
|
const argRecord = args;
|
|
26593
|
-
const normalizedArgs = argRecord.mode !== undefined && argRecord.filePath === undefined && typeof argRecord.file === "string" ? { ...argRecord, filePath: argRecord.file } : argRecord;
|
|
27431
|
+
const normalizedArgs = argRecord.mode !== undefined && argRecord.filePath === undefined && typeof argRecord.file === "string" ? { ...argRecord, filePath: argRecord.file } : { ...argRecord };
|
|
27432
|
+
if (normalizedArgs.mode === "write" && typeof normalizedArgs.filePath === "string" && typeof normalizedArgs.content === "string") {
|
|
27433
|
+
const file2 = normalizedArgs.filePath;
|
|
27434
|
+
const filePath = path4.isAbsolute(file2) ? file2 : path4.resolve(context.directory, file2);
|
|
27435
|
+
const relPath = path4.relative(context.worktree, filePath);
|
|
27436
|
+
await context.ask({
|
|
27437
|
+
permission: "edit",
|
|
27438
|
+
patterns: [relPath],
|
|
27439
|
+
always: ["*"],
|
|
27440
|
+
metadata: { filepath: filePath }
|
|
27441
|
+
});
|
|
27442
|
+
const writeParams = {
|
|
27443
|
+
file: filePath,
|
|
27444
|
+
content: normalizedArgs.content,
|
|
27445
|
+
create_dirs: normalizedArgs.create_dirs !== false,
|
|
27446
|
+
diagnostics: true
|
|
27447
|
+
};
|
|
27448
|
+
if (normalizedArgs.dryRun === true || normalizedArgs.dry_run === true) {
|
|
27449
|
+
writeParams.dry_run = true;
|
|
27450
|
+
}
|
|
27451
|
+
const data = await callBridge(ctx, context, "write", writeParams);
|
|
27452
|
+
return JSON.stringify(data);
|
|
27453
|
+
}
|
|
26594
27454
|
return aftEditTool.execute(normalizedArgs, context);
|
|
26595
27455
|
}
|
|
26596
27456
|
},
|
|
@@ -26692,7 +27552,8 @@ function lspTools(ctx) {
|
|
|
26692
27552
|
` + `}
|
|
26693
27553
|
` + `
|
|
26694
27554
|
` + `**Reading the response honestly:**
|
|
26695
|
-
` + "- `total: 0, complete: true, lsp_servers_used: [{status: 'pull_ok'}]` \u2192 file is genuinely clean.\n" + "- `total: 0, lsp_servers_used: []` \u2192 no server registered for this extension
|
|
27555
|
+
` + "- `total: 0, complete: true, lsp_servers_used: [{status: 'pull_ok'}]` \u2192 file is genuinely clean.\n" + "- `total: 0, lsp_servers_used: []` \u2192 **nothing was checked** (no server registered for this extension). Tell the user, don't claim 'no errors'.\n" + "- `lsp_servers_used: [{status: 'binary_not_installed: <name>'}]` \u2192 server matched the extension but its binary isn't on PATH. Tell the user to install it.\n" + "- `lsp_servers_used: [{status: 'no_root_marker (...)'}]` \u2192 server is registered but couldn't find a workspace root marker walking up from this file. The user's project layout doesn't match what the server expects.\n" + "- `complete: false` (directory mode) \u2192 some files in the directory weren't checked; see `unchecked_files`.\n" + `
|
|
27556
|
+
` + "**When this tool gives an unhelpful answer**, run `bunx --bun @cortexkit/aft doctor lsp <filePath>` from a terminal to get a full per-server breakdown (registered servers, binary resolution, root-marker resolution, spawn outcome).",
|
|
26696
27557
|
args: {
|
|
26697
27558
|
filePath: z5.string().optional().describe("Path to a file to check. Mutually exclusive with 'directory'. Omit both to dump all cached diagnostics."),
|
|
26698
27559
|
directory: z5.string().optional().describe("Path to a directory. Returns cached diagnostics + workspace pull from active servers; lists files we have no info for in 'unchecked_files'. Capped at 200 walked files."),
|
|
@@ -26776,7 +27637,7 @@ function navigationTools(ctx) {
|
|
|
26776
27637
|
|
|
26777
27638
|
// src/tools/reading.ts
|
|
26778
27639
|
import { readdir } from "fs/promises";
|
|
26779
|
-
import { extname as extname2, join as
|
|
27640
|
+
import { extname as extname2, join as join18, resolve as resolve8 } from "path";
|
|
26780
27641
|
import { tool as tool7 } from "@opencode-ai/plugin";
|
|
26781
27642
|
var OUTLINE_EXTENSIONS = new Set([
|
|
26782
27643
|
".ts",
|
|
@@ -26854,7 +27715,7 @@ function readingTools(ctx) {
|
|
|
26854
27715
|
if (!dirArg && typeof args.filePath === "string" && !Array.isArray(args.files)) {
|
|
26855
27716
|
try {
|
|
26856
27717
|
const { stat } = await import("fs/promises");
|
|
26857
|
-
const resolved =
|
|
27718
|
+
const resolved = resolve8(context.directory, args.filePath);
|
|
26858
27719
|
const st = await stat(resolved);
|
|
26859
27720
|
if (st.isDirectory()) {
|
|
26860
27721
|
dirArg = args.filePath;
|
|
@@ -26862,7 +27723,7 @@ function readingTools(ctx) {
|
|
|
26862
27723
|
} catch {}
|
|
26863
27724
|
}
|
|
26864
27725
|
if (dirArg) {
|
|
26865
|
-
const dirPath =
|
|
27726
|
+
const dirPath = resolve8(context.directory, dirArg);
|
|
26866
27727
|
const files = await discoverSourceFiles(dirPath);
|
|
26867
27728
|
if (files.length === 0) {
|
|
26868
27729
|
return JSON.stringify({
|
|
@@ -26970,12 +27831,12 @@ async function discoverSourceFiles(dir, maxFiles = 200) {
|
|
|
26970
27831
|
return;
|
|
26971
27832
|
if (entry.isDirectory()) {
|
|
26972
27833
|
if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
26973
|
-
await walk(
|
|
27834
|
+
await walk(join18(current, entry.name));
|
|
26974
27835
|
}
|
|
26975
27836
|
} else if (entry.isFile()) {
|
|
26976
27837
|
const ext = extname2(entry.name).toLowerCase();
|
|
26977
27838
|
if (OUTLINE_EXTENSIONS.has(ext)) {
|
|
26978
|
-
files.push(
|
|
27839
|
+
files.push(join18(current, entry.name));
|
|
26979
27840
|
}
|
|
26980
27841
|
}
|
|
26981
27842
|
}
|
|
@@ -27548,6 +28409,7 @@ var ANNOUNCEMENT_FEATURES = [];
|
|
|
27548
28409
|
var plugin = async (input) => {
|
|
27549
28410
|
const binaryPath = await findBinary();
|
|
27550
28411
|
const aftConfig = loadAftConfig(input.directory);
|
|
28412
|
+
const autoUpdateAbort = new AbortController;
|
|
27551
28413
|
const configOverrides = {};
|
|
27552
28414
|
if (aftConfig.format_on_edit !== undefined)
|
|
27553
28415
|
configOverrides.format_on_edit = aftConfig.format_on_edit;
|
|
@@ -27568,8 +28430,8 @@ var plugin = async (input) => {
|
|
|
27568
28430
|
if (aftConfig.max_callgraph_files !== undefined)
|
|
27569
28431
|
configOverrides.max_callgraph_files = aftConfig.max_callgraph_files;
|
|
27570
28432
|
const isFastembedSemanticBackend = (aftConfig.semantic?.backend ?? "fastembed") === "fastembed";
|
|
27571
|
-
const dataHome = process.env.XDG_DATA_HOME ||
|
|
27572
|
-
configOverrides.storage_dir =
|
|
28433
|
+
const dataHome = process.env.XDG_DATA_HOME || join19(homedir10(), ".local", "share");
|
|
28434
|
+
configOverrides.storage_dir = join19(dataHome, "opencode", "storage", "plugin", "aft");
|
|
27573
28435
|
if (aftConfig.experimental_semantic_search && isFastembedSemanticBackend) {
|
|
27574
28436
|
const storageDir2 = configOverrides.storage_dir;
|
|
27575
28437
|
const ortDylibDir = await ensureOnnxRuntime(storageDir2).catch((err) => {
|
|
@@ -27703,10 +28565,10 @@ ${lines}
|
|
|
27703
28565
|
return { show: false };
|
|
27704
28566
|
}
|
|
27705
28567
|
if (storageDir) {
|
|
27706
|
-
const versionFile =
|
|
28568
|
+
const versionFile = join19(storageDir, "last_announced_version");
|
|
27707
28569
|
try {
|
|
27708
|
-
if (
|
|
27709
|
-
const lastVersion =
|
|
28570
|
+
if (existsSync13(versionFile)) {
|
|
28571
|
+
const lastVersion = readFileSync9(versionFile, "utf-8").trim();
|
|
27710
28572
|
if (lastVersion === ANNOUNCEMENT_VERSION)
|
|
27711
28573
|
return { show: false };
|
|
27712
28574
|
}
|
|
@@ -27718,7 +28580,7 @@ ${lines}
|
|
|
27718
28580
|
if (storageDir && ANNOUNCEMENT_VERSION) {
|
|
27719
28581
|
try {
|
|
27720
28582
|
mkdirSync10(storageDir, { recursive: true });
|
|
27721
|
-
|
|
28583
|
+
writeFileSync9(join19(storageDir, "last_announced_version"), ANNOUNCEMENT_VERSION);
|
|
27722
28584
|
} catch {}
|
|
27723
28585
|
}
|
|
27724
28586
|
return { success: true };
|
|
@@ -27801,6 +28663,11 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
27801
28663
|
}
|
|
27802
28664
|
return {
|
|
27803
28665
|
tool: allTools,
|
|
28666
|
+
event: createAutoUpdateCheckerHook(input, {
|
|
28667
|
+
enabled: true,
|
|
28668
|
+
autoUpdate: aftConfig.auto_update ?? true,
|
|
28669
|
+
signal: autoUpdateAbort.signal
|
|
28670
|
+
}),
|
|
27804
28671
|
"command.execute.before": async (commandInput, _output) => {
|
|
27805
28672
|
if (isTuiMode2() || commandInput.command !== STATUS_COMMAND) {
|
|
27806
28673
|
return;
|
|
@@ -27849,6 +28716,7 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
27849
28716
|
};
|
|
27850
28717
|
},
|
|
27851
28718
|
dispose: async () => {
|
|
28719
|
+
autoUpdateAbort.abort();
|
|
27852
28720
|
unregisterShutdown();
|
|
27853
28721
|
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|
|
27854
28722
|
rpcServer.stop();
|