oh-my-opencode-slim 0.8.3 → 0.8.4
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/README.md +33 -7
- package/dist/cli/config-io.d.ts +1 -1
- package/dist/cli/custom-skills.d.ts +2 -2
- package/dist/cli/index.js +108 -53
- package/dist/cli/paths.d.ts +12 -0
- package/dist/cli/providers.d.ts +4 -4
- package/dist/cli/types.d.ts +2 -0
- package/dist/config/loader.d.ts +2 -1
- package/dist/config/schema.d.ts +3 -0
- package/dist/hooks/foreground-fallback/index.d.ts +72 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.js +1247 -175
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/lsp/config-store.d.ts +29 -0
- package/dist/tools/lsp/constants.d.ts +18 -2
- package/dist/tools/lsp/index.d.ts +1 -0
- package/dist/tools/lsp/types.d.ts +7 -0
- package/dist/tools/lsp/utils.d.ts +14 -1
- package/oh-my-opencode-slim.schema.json +8 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -3037,7 +3037,7 @@ var require_main = __commonJS((exports) => {
|
|
|
3037
3037
|
var ril_1 = require_ril();
|
|
3038
3038
|
ril_1.default.install();
|
|
3039
3039
|
var path6 = __require("path");
|
|
3040
|
-
var
|
|
3040
|
+
var os3 = __require("os");
|
|
3041
3041
|
var crypto_1 = __require("crypto");
|
|
3042
3042
|
var net_1 = __require("net");
|
|
3043
3043
|
var api_1 = require_api();
|
|
@@ -3180,7 +3180,7 @@ var require_main = __commonJS((exports) => {
|
|
|
3180
3180
|
if (XDG_RUNTIME_DIR) {
|
|
3181
3181
|
result = path6.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
|
|
3182
3182
|
} else {
|
|
3183
|
-
result = path6.join(
|
|
3183
|
+
result = path6.join(os3.tmpdir(), `vscode-${randomSuffix}.sock`);
|
|
3184
3184
|
}
|
|
3185
3185
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3186
3186
|
if (limit !== undefined && result.length > limit) {
|
|
@@ -3277,6 +3277,243 @@ var require_main = __commonJS((exports) => {
|
|
|
3277
3277
|
exports.createMessageConnection = createMessageConnection;
|
|
3278
3278
|
});
|
|
3279
3279
|
|
|
3280
|
+
// node_modules/isexe/dist/commonjs/index.min.js
|
|
3281
|
+
var require_index_min = __commonJS((exports) => {
|
|
3282
|
+
var a = (t, e) => () => (e || t((e = { exports: {} }).exports, e), e.exports);
|
|
3283
|
+
var _ = a((i) => {
|
|
3284
|
+
Object.defineProperty(i, "__esModule", { value: true });
|
|
3285
|
+
i.sync = i.isexe = undefined;
|
|
3286
|
+
var M = __require("fs"), x = __require("fs/promises"), q = async (t, e = {}) => {
|
|
3287
|
+
let { ignoreErrors: r = false } = e;
|
|
3288
|
+
try {
|
|
3289
|
+
return d(await (0, x.stat)(t), e);
|
|
3290
|
+
} catch (s) {
|
|
3291
|
+
let n = s;
|
|
3292
|
+
if (r || n.code === "EACCES")
|
|
3293
|
+
return false;
|
|
3294
|
+
throw n;
|
|
3295
|
+
}
|
|
3296
|
+
};
|
|
3297
|
+
i.isexe = q;
|
|
3298
|
+
var m = (t, e = {}) => {
|
|
3299
|
+
let { ignoreErrors: r = false } = e;
|
|
3300
|
+
try {
|
|
3301
|
+
return d((0, M.statSync)(t), e);
|
|
3302
|
+
} catch (s) {
|
|
3303
|
+
let n = s;
|
|
3304
|
+
if (r || n.code === "EACCES")
|
|
3305
|
+
return false;
|
|
3306
|
+
throw n;
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
i.sync = m;
|
|
3310
|
+
var d = (t, e) => t.isFile() && A(t, e), A = (t, e) => {
|
|
3311
|
+
let r = e.uid ?? process.getuid?.(), s = e.groups ?? process.getgroups?.() ?? [], n = e.gid ?? process.getgid?.() ?? s[0];
|
|
3312
|
+
if (r === undefined || n === undefined)
|
|
3313
|
+
throw new Error("cannot get uid or gid");
|
|
3314
|
+
let u = new Set([n, ...s]), c = t.mode, S = t.uid, P = t.gid, f = parseInt("100", 8), l = parseInt("010", 8), j = parseInt("001", 8), C = f | l;
|
|
3315
|
+
return !!(c & j || c & l && u.has(P) || c & f && S === r || c & C && r === 0);
|
|
3316
|
+
};
|
|
3317
|
+
});
|
|
3318
|
+
var g = a((o) => {
|
|
3319
|
+
Object.defineProperty(o, "__esModule", { value: true });
|
|
3320
|
+
o.sync = o.isexe = undefined;
|
|
3321
|
+
var T = __require("fs"), I = __require("fs/promises"), D = __require("path"), F = async (t, e = {}) => {
|
|
3322
|
+
let { ignoreErrors: r = false } = e;
|
|
3323
|
+
try {
|
|
3324
|
+
return y(await (0, I.stat)(t), t, e);
|
|
3325
|
+
} catch (s) {
|
|
3326
|
+
let n = s;
|
|
3327
|
+
if (r || n.code === "EACCES")
|
|
3328
|
+
return false;
|
|
3329
|
+
throw n;
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
o.isexe = F;
|
|
3333
|
+
var L = (t, e = {}) => {
|
|
3334
|
+
let { ignoreErrors: r = false } = e;
|
|
3335
|
+
try {
|
|
3336
|
+
return y((0, T.statSync)(t), t, e);
|
|
3337
|
+
} catch (s) {
|
|
3338
|
+
let n = s;
|
|
3339
|
+
if (r || n.code === "EACCES")
|
|
3340
|
+
return false;
|
|
3341
|
+
throw n;
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3344
|
+
o.sync = L;
|
|
3345
|
+
var B = (t, e) => {
|
|
3346
|
+
let { pathExt: r = process.env.PATHEXT || "" } = e, s = r.split(D.delimiter);
|
|
3347
|
+
if (s.indexOf("") !== -1)
|
|
3348
|
+
return true;
|
|
3349
|
+
for (let n of s) {
|
|
3350
|
+
let u = n.toLowerCase(), c = t.substring(t.length - u.length).toLowerCase();
|
|
3351
|
+
if (u && c === u)
|
|
3352
|
+
return true;
|
|
3353
|
+
}
|
|
3354
|
+
return false;
|
|
3355
|
+
}, y = (t, e, r) => t.isFile() && B(e, r);
|
|
3356
|
+
});
|
|
3357
|
+
var p = a((h) => {
|
|
3358
|
+
Object.defineProperty(h, "__esModule", { value: true });
|
|
3359
|
+
});
|
|
3360
|
+
var v = exports && exports.__createBinding || (Object.create ? function(t, e, r, s) {
|
|
3361
|
+
s === undefined && (s = r);
|
|
3362
|
+
var n = Object.getOwnPropertyDescriptor(e, r);
|
|
3363
|
+
(!n || ("get" in n ? !e.__esModule : n.writable || n.configurable)) && (n = { enumerable: true, get: function() {
|
|
3364
|
+
return e[r];
|
|
3365
|
+
} }), Object.defineProperty(t, s, n);
|
|
3366
|
+
} : function(t, e, r, s) {
|
|
3367
|
+
s === undefined && (s = r), t[s] = e[r];
|
|
3368
|
+
});
|
|
3369
|
+
var G = exports && exports.__setModuleDefault || (Object.create ? function(t, e) {
|
|
3370
|
+
Object.defineProperty(t, "default", { enumerable: true, value: e });
|
|
3371
|
+
} : function(t, e) {
|
|
3372
|
+
t.default = e;
|
|
3373
|
+
});
|
|
3374
|
+
var w = exports && exports.__importStar || function() {
|
|
3375
|
+
var t = function(e) {
|
|
3376
|
+
return t = Object.getOwnPropertyNames || function(r) {
|
|
3377
|
+
var s = [];
|
|
3378
|
+
for (var n in r)
|
|
3379
|
+
Object.prototype.hasOwnProperty.call(r, n) && (s[s.length] = n);
|
|
3380
|
+
return s;
|
|
3381
|
+
}, t(e);
|
|
3382
|
+
};
|
|
3383
|
+
return function(e) {
|
|
3384
|
+
if (e && e.__esModule)
|
|
3385
|
+
return e;
|
|
3386
|
+
var r = {};
|
|
3387
|
+
if (e != null)
|
|
3388
|
+
for (var s = t(e), n = 0;n < s.length; n++)
|
|
3389
|
+
s[n] !== "default" && v(r, e, s[n]);
|
|
3390
|
+
return G(r, e), r;
|
|
3391
|
+
};
|
|
3392
|
+
}();
|
|
3393
|
+
var X = exports && exports.__exportStar || function(t, e) {
|
|
3394
|
+
for (var r in t)
|
|
3395
|
+
r !== "default" && !Object.prototype.hasOwnProperty.call(e, r) && v(e, t, r);
|
|
3396
|
+
};
|
|
3397
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3398
|
+
exports.sync = exports.isexe = exports.posix = exports.win32 = undefined;
|
|
3399
|
+
var E = w(_());
|
|
3400
|
+
exports.posix = E;
|
|
3401
|
+
var O = w(g());
|
|
3402
|
+
exports.win32 = O;
|
|
3403
|
+
X(p(), exports);
|
|
3404
|
+
var H = process.env._ISEXE_TEST_PLATFORM_ || process.platform;
|
|
3405
|
+
var b = H === "win32" ? O : E;
|
|
3406
|
+
exports.isexe = b.isexe;
|
|
3407
|
+
exports.sync = b.sync;
|
|
3408
|
+
});
|
|
3409
|
+
|
|
3410
|
+
// node_modules/which/lib/index.js
|
|
3411
|
+
var require_lib = __commonJS((exports, module) => {
|
|
3412
|
+
var { isexe, sync: isexeSync } = require_index_min();
|
|
3413
|
+
var { join: join11, delimiter, sep, posix } = __require("path");
|
|
3414
|
+
var isWindows = process.platform === "win32";
|
|
3415
|
+
var rSlash = new RegExp(`[${posix.sep}${sep === posix.sep ? "" : sep}]`.replace(/(\\)/g, "\\$1"));
|
|
3416
|
+
var rRel = new RegExp(`^\\.${rSlash.source}`);
|
|
3417
|
+
var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
3418
|
+
var getPathInfo = (cmd, {
|
|
3419
|
+
path: optPath = process.env.PATH,
|
|
3420
|
+
pathExt: optPathExt = process.env.PATHEXT,
|
|
3421
|
+
delimiter: optDelimiter = delimiter
|
|
3422
|
+
}) => {
|
|
3423
|
+
const pathEnv = cmd.match(rSlash) ? [""] : [
|
|
3424
|
+
...isWindows ? [process.cwd()] : [],
|
|
3425
|
+
...(optPath || "").split(optDelimiter)
|
|
3426
|
+
];
|
|
3427
|
+
if (isWindows) {
|
|
3428
|
+
const pathExtExe = optPathExt || [".EXE", ".CMD", ".BAT", ".COM"].join(optDelimiter);
|
|
3429
|
+
const pathExt = pathExtExe.split(optDelimiter).flatMap((item) => [item, item.toLowerCase()]);
|
|
3430
|
+
if (cmd.includes(".") && pathExt[0] !== "") {
|
|
3431
|
+
pathExt.unshift("");
|
|
3432
|
+
}
|
|
3433
|
+
return { pathEnv, pathExt, pathExtExe };
|
|
3434
|
+
}
|
|
3435
|
+
return { pathEnv, pathExt: [""] };
|
|
3436
|
+
};
|
|
3437
|
+
var getPathPart = (raw, cmd) => {
|
|
3438
|
+
const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
|
|
3439
|
+
const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
|
|
3440
|
+
return prefix + join11(pathPart, cmd);
|
|
3441
|
+
};
|
|
3442
|
+
var which = async (cmd, opt = {}) => {
|
|
3443
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
3444
|
+
const found = [];
|
|
3445
|
+
for (const envPart of pathEnv) {
|
|
3446
|
+
const p = getPathPart(envPart, cmd);
|
|
3447
|
+
for (const ext of pathExt) {
|
|
3448
|
+
const withExt = p + ext;
|
|
3449
|
+
const is = await isexe(withExt, { pathExt: pathExtExe, ignoreErrors: true });
|
|
3450
|
+
if (is) {
|
|
3451
|
+
if (!opt.all) {
|
|
3452
|
+
return withExt;
|
|
3453
|
+
}
|
|
3454
|
+
found.push(withExt);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
if (opt.all && found.length) {
|
|
3459
|
+
return found;
|
|
3460
|
+
}
|
|
3461
|
+
if (opt.nothrow) {
|
|
3462
|
+
return null;
|
|
3463
|
+
}
|
|
3464
|
+
throw getNotFoundError(cmd);
|
|
3465
|
+
};
|
|
3466
|
+
var whichSync = (cmd, opt = {}) => {
|
|
3467
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
3468
|
+
const found = [];
|
|
3469
|
+
for (const pathEnvPart of pathEnv) {
|
|
3470
|
+
const p = getPathPart(pathEnvPart, cmd);
|
|
3471
|
+
for (const ext of pathExt) {
|
|
3472
|
+
const withExt = p + ext;
|
|
3473
|
+
const is = isexeSync(withExt, { pathExt: pathExtExe, ignoreErrors: true });
|
|
3474
|
+
if (is) {
|
|
3475
|
+
if (!opt.all) {
|
|
3476
|
+
return withExt;
|
|
3477
|
+
}
|
|
3478
|
+
found.push(withExt);
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
if (opt.all && found.length) {
|
|
3483
|
+
return found;
|
|
3484
|
+
}
|
|
3485
|
+
if (opt.nothrow) {
|
|
3486
|
+
return null;
|
|
3487
|
+
}
|
|
3488
|
+
throw getNotFoundError(cmd);
|
|
3489
|
+
};
|
|
3490
|
+
module.exports = which;
|
|
3491
|
+
which.sync = whichSync;
|
|
3492
|
+
});
|
|
3493
|
+
|
|
3494
|
+
// src/cli/paths.ts
|
|
3495
|
+
import { homedir } from "os";
|
|
3496
|
+
import { dirname, join } from "path";
|
|
3497
|
+
function getDefaultOpenCodeConfigDir() {
|
|
3498
|
+
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
3499
|
+
return join(userConfigDir, "opencode");
|
|
3500
|
+
}
|
|
3501
|
+
function getCustomOpenCodeConfigDir() {
|
|
3502
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
3503
|
+
return configDir || undefined;
|
|
3504
|
+
}
|
|
3505
|
+
function getConfigDir() {
|
|
3506
|
+
const customConfigDir = getCustomOpenCodeConfigDir();
|
|
3507
|
+
if (customConfigDir) {
|
|
3508
|
+
return customConfigDir;
|
|
3509
|
+
}
|
|
3510
|
+
return getDefaultOpenCodeConfigDir();
|
|
3511
|
+
}
|
|
3512
|
+
function getOpenCodeConfigPaths() {
|
|
3513
|
+
const configDir = getDefaultOpenCodeConfigDir();
|
|
3514
|
+
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3280
3517
|
// src/cli/custom-skills.ts
|
|
3281
3518
|
var CUSTOM_SKILLS = [
|
|
3282
3519
|
{
|
|
@@ -3378,10 +3615,10 @@ var SUBAGENT_DELEGATION_RULES = {
|
|
|
3378
3615
|
var DEFAULT_MODELS = {
|
|
3379
3616
|
orchestrator: undefined,
|
|
3380
3617
|
oracle: "openai/gpt-5.4",
|
|
3381
|
-
librarian: "openai/gpt-5-
|
|
3382
|
-
explorer: "openai/gpt-5-
|
|
3383
|
-
designer: "
|
|
3384
|
-
fixer: "openai/gpt-5-
|
|
3618
|
+
librarian: "openai/gpt-5.4-mini",
|
|
3619
|
+
explorer: "openai/gpt-5.4-mini",
|
|
3620
|
+
designer: "openai/gpt-5.4-mini",
|
|
3621
|
+
fixer: "openai/gpt-5.4-mini"
|
|
3385
3622
|
};
|
|
3386
3623
|
var POLL_INTERVAL_BACKGROUND_MS = 2000;
|
|
3387
3624
|
var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
@@ -3389,21 +3626,8 @@ var MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
|
3389
3626
|
var FALLBACK_FAILOVER_TIMEOUT_MS = 15000;
|
|
3390
3627
|
// src/config/loader.ts
|
|
3391
3628
|
import * as fs from "fs";
|
|
3392
|
-
import * as os from "os";
|
|
3393
3629
|
import * as path from "path";
|
|
3394
3630
|
|
|
3395
|
-
// src/cli/paths.ts
|
|
3396
|
-
import { homedir } from "os";
|
|
3397
|
-
import { join } from "path";
|
|
3398
|
-
function getConfigDir() {
|
|
3399
|
-
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
3400
|
-
return join(userConfigDir, "opencode");
|
|
3401
|
-
}
|
|
3402
|
-
function getOpenCodeConfigPaths() {
|
|
3403
|
-
const configDir = getConfigDir();
|
|
3404
|
-
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
3405
|
-
}
|
|
3406
|
-
|
|
3407
3631
|
// src/config/agent-mcps.ts
|
|
3408
3632
|
var DEFAULT_AGENT_MCPS = {
|
|
3409
3633
|
orchestrator: ["websearch"],
|
|
@@ -5682,7 +5906,7 @@ class Doc {
|
|
|
5682
5906
|
var version = {
|
|
5683
5907
|
major: 4,
|
|
5684
5908
|
minor: 3,
|
|
5685
|
-
patch:
|
|
5909
|
+
patch: 6
|
|
5686
5910
|
};
|
|
5687
5911
|
|
|
5688
5912
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -6968,7 +7192,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
6968
7192
|
if (keyResult instanceof Promise) {
|
|
6969
7193
|
throw new Error("Async schemas not supported in object keys currently");
|
|
6970
7194
|
}
|
|
6971
|
-
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length
|
|
7195
|
+
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length;
|
|
6972
7196
|
if (checkNumericKey) {
|
|
6973
7197
|
const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);
|
|
6974
7198
|
if (retryResult instanceof Promise) {
|
|
@@ -14339,7 +14563,7 @@ function finalize(ctx, schema) {
|
|
|
14339
14563
|
}
|
|
14340
14564
|
}
|
|
14341
14565
|
}
|
|
14342
|
-
if (refSchema.$ref) {
|
|
14566
|
+
if (refSchema.$ref && refSeen.def) {
|
|
14343
14567
|
for (const key in schema2) {
|
|
14344
14568
|
if (key === "$ref" || key === "allOf")
|
|
14345
14569
|
continue;
|
|
@@ -17049,10 +17273,12 @@ var BackgroundTaskConfigSchema = exports_external.object({
|
|
|
17049
17273
|
var FailoverConfigSchema = exports_external.object({
|
|
17050
17274
|
enabled: exports_external.boolean().default(true),
|
|
17051
17275
|
timeoutMs: exports_external.number().min(0).default(15000),
|
|
17276
|
+
retryDelayMs: exports_external.number().min(0).default(500),
|
|
17052
17277
|
chains: FallbackChainsSchema.default({})
|
|
17053
17278
|
});
|
|
17054
17279
|
var PluginConfigSchema = exports_external.object({
|
|
17055
17280
|
preset: exports_external.string().optional(),
|
|
17281
|
+
setDefaultAgent: exports_external.boolean().optional(),
|
|
17056
17282
|
scoringEngineVersion: exports_external.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
17057
17283
|
balanceProviderUsage: exports_external.boolean().optional(),
|
|
17058
17284
|
manualPlan: ManualPlanSchema.optional(),
|
|
@@ -17066,9 +17292,6 @@ var PluginConfigSchema = exports_external.object({
|
|
|
17066
17292
|
|
|
17067
17293
|
// src/config/loader.ts
|
|
17068
17294
|
var PROMPTS_DIR_NAME = "oh-my-opencode-slim";
|
|
17069
|
-
function getUserConfigDir() {
|
|
17070
|
-
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
17071
|
-
}
|
|
17072
17295
|
function loadConfigFromPath(configPath) {
|
|
17073
17296
|
try {
|
|
17074
17297
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
@@ -17116,7 +17339,7 @@ function deepMerge(base, override) {
|
|
|
17116
17339
|
return result;
|
|
17117
17340
|
}
|
|
17118
17341
|
function loadPluginConfig(directory) {
|
|
17119
|
-
const userConfigBasePath = path.join(
|
|
17342
|
+
const userConfigBasePath = path.join(getConfigDir(), "oh-my-opencode-slim");
|
|
17120
17343
|
const projectConfigBasePath = path.join(directory, ".opencode", "oh-my-opencode-slim");
|
|
17121
17344
|
const userConfigPath = findConfigPath(userConfigBasePath);
|
|
17122
17345
|
const projectConfigPath = findConfigPath(projectConfigBasePath);
|
|
@@ -17149,7 +17372,7 @@ function loadPluginConfig(directory) {
|
|
|
17149
17372
|
}
|
|
17150
17373
|
function loadAgentPrompt(agentName, preset) {
|
|
17151
17374
|
const presetDirName = preset && /^[a-zA-Z0-9_-]+$/.test(preset) ? preset : undefined;
|
|
17152
|
-
const promptsDir = path.join(
|
|
17375
|
+
const promptsDir = path.join(getConfigDir(), PROMPTS_DIR_NAME);
|
|
17153
17376
|
const promptSearchDirs = presetDirName ? [path.join(promptsDir, presetDirName), promptsDir] : [promptsDir];
|
|
17154
17377
|
const result = {};
|
|
17155
17378
|
const readFirstPrompt = (fileName, errorPrefix) => {
|
|
@@ -17700,9 +17923,9 @@ function getAgentConfigs(config2) {
|
|
|
17700
17923
|
|
|
17701
17924
|
// src/utils/logger.ts
|
|
17702
17925
|
import * as fs2 from "fs";
|
|
17703
|
-
import * as
|
|
17926
|
+
import * as os from "os";
|
|
17704
17927
|
import * as path2 from "path";
|
|
17705
|
-
var logFile = path2.join(
|
|
17928
|
+
var logFile = path2.join(os.tmpdir(), "oh-my-opencode-slim.log");
|
|
17706
17929
|
function log(message, data) {
|
|
17707
17930
|
try {
|
|
17708
17931
|
const timestamp = new Date().toISOString();
|
|
@@ -18191,14 +18414,23 @@ class BackgroundTaskManager {
|
|
|
18191
18414
|
await this.client.session.prompt(args);
|
|
18192
18415
|
return;
|
|
18193
18416
|
}
|
|
18194
|
-
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
|
|
18199
|
-
|
|
18200
|
-
|
|
18201
|
-
|
|
18417
|
+
const sessionId = args.path.id;
|
|
18418
|
+
let timer;
|
|
18419
|
+
try {
|
|
18420
|
+
const promptPromise = this.client.session.prompt(args);
|
|
18421
|
+
promptPromise.catch(() => {});
|
|
18422
|
+
await Promise.race([
|
|
18423
|
+
promptPromise,
|
|
18424
|
+
new Promise((_, reject) => {
|
|
18425
|
+
timer = setTimeout(() => {
|
|
18426
|
+
this.client.session.abort({ path: { id: sessionId } }).catch(() => {});
|
|
18427
|
+
reject(new Error(`Prompt timed out after ${timeoutMs}ms`));
|
|
18428
|
+
}, timeoutMs);
|
|
18429
|
+
})
|
|
18430
|
+
]);
|
|
18431
|
+
} finally {
|
|
18432
|
+
clearTimeout(timer);
|
|
18433
|
+
}
|
|
18202
18434
|
}
|
|
18203
18435
|
calculateToolPermissions(agentName) {
|
|
18204
18436
|
const allowedSubagents = this.getSubagentRules(agentName);
|
|
@@ -18242,11 +18474,15 @@ class BackgroundTaskManager {
|
|
|
18242
18474
|
});
|
|
18243
18475
|
const fallbackEnabled = this.config?.fallback?.enabled ?? true;
|
|
18244
18476
|
const timeoutMs = fallbackEnabled ? this.config?.fallback?.timeoutMs ?? FALLBACK_FAILOVER_TIMEOUT_MS : 0;
|
|
18477
|
+
const retryDelayMs = this.config?.fallback?.retryDelayMs ?? 500;
|
|
18245
18478
|
const chain = fallbackEnabled ? this.resolveFallbackChain(task.agent) : [];
|
|
18246
18479
|
const attemptModels = chain.length > 0 ? chain : [undefined];
|
|
18247
18480
|
const errors3 = [];
|
|
18248
18481
|
let succeeded = false;
|
|
18249
|
-
|
|
18482
|
+
const sessionId = session.data.id;
|
|
18483
|
+
for (let i = 0;i < attemptModels.length; i++) {
|
|
18484
|
+
const model = attemptModels[i];
|
|
18485
|
+
const modelLabel = model ?? "default-model";
|
|
18250
18486
|
try {
|
|
18251
18487
|
const body = {
|
|
18252
18488
|
...basePromptBody,
|
|
@@ -18259,8 +18495,11 @@ class BackgroundTaskManager {
|
|
|
18259
18495
|
}
|
|
18260
18496
|
body.model = ref;
|
|
18261
18497
|
}
|
|
18498
|
+
if (i > 0) {
|
|
18499
|
+
log(`[background-manager] fallback attempt ${i + 1}/${attemptModels.length}: ${modelLabel}`, { taskId: task.id });
|
|
18500
|
+
}
|
|
18262
18501
|
await this.promptWithTimeout({
|
|
18263
|
-
path: { id:
|
|
18502
|
+
path: { id: sessionId },
|
|
18264
18503
|
body,
|
|
18265
18504
|
query: promptQuery
|
|
18266
18505
|
}, timeoutMs);
|
|
@@ -18268,10 +18507,17 @@ class BackgroundTaskManager {
|
|
|
18268
18507
|
break;
|
|
18269
18508
|
} catch (error48) {
|
|
18270
18509
|
const msg = error48 instanceof Error ? error48.message : String(error48);
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18510
|
+
errors3.push(`${modelLabel}: ${msg}`);
|
|
18511
|
+
log(`[background-manager] model failed: ${modelLabel} \u2014 ${msg}`, {
|
|
18512
|
+
taskId: task.id
|
|
18513
|
+
});
|
|
18514
|
+
if (i < attemptModels.length - 1) {
|
|
18515
|
+
try {
|
|
18516
|
+
await this.client.session.abort({
|
|
18517
|
+
path: { id: sessionId }
|
|
18518
|
+
});
|
|
18519
|
+
await new Promise((r) => setTimeout(r, retryDelayMs));
|
|
18520
|
+
} catch {}
|
|
18275
18521
|
}
|
|
18276
18522
|
}
|
|
18277
18523
|
}
|
|
@@ -18640,16 +18886,16 @@ class TmuxSessionManager {
|
|
|
18640
18886
|
import * as fs3 from "fs";
|
|
18641
18887
|
import * as path4 from "path";
|
|
18642
18888
|
// src/hooks/auto-update-checker/constants.ts
|
|
18643
|
-
import * as
|
|
18889
|
+
import * as os2 from "os";
|
|
18644
18890
|
import * as path3 from "path";
|
|
18645
18891
|
var PACKAGE_NAME = "oh-my-opencode-slim";
|
|
18646
18892
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
|
|
18647
18893
|
var NPM_FETCH_TIMEOUT = 5000;
|
|
18648
18894
|
function getCacheDir() {
|
|
18649
18895
|
if (process.platform === "win32") {
|
|
18650
|
-
return path3.join(process.env.LOCALAPPDATA ??
|
|
18896
|
+
return path3.join(process.env.LOCALAPPDATA ?? os2.homedir(), "opencode");
|
|
18651
18897
|
}
|
|
18652
|
-
return path3.join(
|
|
18898
|
+
return path3.join(os2.homedir(), ".cache", "opencode");
|
|
18653
18899
|
}
|
|
18654
18900
|
var CACHE_DIR = getCacheDir();
|
|
18655
18901
|
var INSTALLED_PACKAGE_JSON = path3.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
|
|
@@ -19186,6 +19432,216 @@ ${buildRetryGuidance(detected)}`;
|
|
|
19186
19432
|
}
|
|
19187
19433
|
};
|
|
19188
19434
|
}
|
|
19435
|
+
// src/hooks/foreground-fallback/index.ts
|
|
19436
|
+
var RATE_LIMIT_PATTERNS = [
|
|
19437
|
+
/\b429\b/,
|
|
19438
|
+
/rate.?limit/i,
|
|
19439
|
+
/too many requests/i,
|
|
19440
|
+
/quota.?exceeded/i,
|
|
19441
|
+
/usage.?exceeded/i,
|
|
19442
|
+
/usage limit/i,
|
|
19443
|
+
/overloaded/i,
|
|
19444
|
+
/resource.?exhausted/i,
|
|
19445
|
+
/insufficient.?quota/i,
|
|
19446
|
+
/high concurrency/i,
|
|
19447
|
+
/reduce concurrency/i
|
|
19448
|
+
];
|
|
19449
|
+
function isRateLimitError(error48) {
|
|
19450
|
+
if (!error48 || typeof error48 !== "object")
|
|
19451
|
+
return false;
|
|
19452
|
+
const err = error48;
|
|
19453
|
+
const text = [
|
|
19454
|
+
err.message ?? "",
|
|
19455
|
+
String(err.data?.statusCode ?? ""),
|
|
19456
|
+
err.data?.message ?? "",
|
|
19457
|
+
err.data?.responseBody ?? ""
|
|
19458
|
+
].join(" ");
|
|
19459
|
+
return RATE_LIMIT_PATTERNS.some((p) => p.test(text));
|
|
19460
|
+
}
|
|
19461
|
+
function parseModel(model) {
|
|
19462
|
+
const slash = model.indexOf("/");
|
|
19463
|
+
if (slash <= 0 || slash >= model.length - 1)
|
|
19464
|
+
return null;
|
|
19465
|
+
return { providerID: model.slice(0, slash), modelID: model.slice(slash + 1) };
|
|
19466
|
+
}
|
|
19467
|
+
var DEDUP_WINDOW_MS = 5000;
|
|
19468
|
+
|
|
19469
|
+
class ForegroundFallbackManager {
|
|
19470
|
+
client;
|
|
19471
|
+
chains;
|
|
19472
|
+
enabled;
|
|
19473
|
+
sessionModel = new Map;
|
|
19474
|
+
sessionAgent = new Map;
|
|
19475
|
+
sessionTried = new Map;
|
|
19476
|
+
inProgress = new Set;
|
|
19477
|
+
lastTrigger = new Map;
|
|
19478
|
+
constructor(client, chains, enabled) {
|
|
19479
|
+
this.client = client;
|
|
19480
|
+
this.chains = chains;
|
|
19481
|
+
this.enabled = enabled;
|
|
19482
|
+
}
|
|
19483
|
+
async handleEvent(rawEvent) {
|
|
19484
|
+
if (!this.enabled)
|
|
19485
|
+
return;
|
|
19486
|
+
const event = rawEvent;
|
|
19487
|
+
if (!event?.type)
|
|
19488
|
+
return;
|
|
19489
|
+
switch (event.type) {
|
|
19490
|
+
case "message.updated": {
|
|
19491
|
+
const info = event.properties?.info;
|
|
19492
|
+
if (!info)
|
|
19493
|
+
break;
|
|
19494
|
+
const sessionID = info.sessionID;
|
|
19495
|
+
if (!sessionID)
|
|
19496
|
+
break;
|
|
19497
|
+
if (typeof info.agent === "string") {
|
|
19498
|
+
this.sessionAgent.set(sessionID, info.agent);
|
|
19499
|
+
}
|
|
19500
|
+
if (typeof info.providerID === "string" && typeof info.modelID === "string") {
|
|
19501
|
+
this.sessionModel.set(sessionID, `${info.providerID}/${info.modelID}`);
|
|
19502
|
+
}
|
|
19503
|
+
if (info.error && isRateLimitError(info.error)) {
|
|
19504
|
+
await this.tryFallback(sessionID);
|
|
19505
|
+
}
|
|
19506
|
+
break;
|
|
19507
|
+
}
|
|
19508
|
+
case "session.error": {
|
|
19509
|
+
const props = event.properties;
|
|
19510
|
+
if (props?.sessionID && props.error && isRateLimitError(props.error)) {
|
|
19511
|
+
await this.tryFallback(props.sessionID);
|
|
19512
|
+
}
|
|
19513
|
+
break;
|
|
19514
|
+
}
|
|
19515
|
+
case "session.status": {
|
|
19516
|
+
const props = event.properties;
|
|
19517
|
+
if (!props?.sessionID || props.status?.type !== "retry")
|
|
19518
|
+
break;
|
|
19519
|
+
const msg = props.status.message?.toLowerCase() ?? "";
|
|
19520
|
+
if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
|
|
19521
|
+
await this.tryFallback(props.sessionID);
|
|
19522
|
+
}
|
|
19523
|
+
break;
|
|
19524
|
+
}
|
|
19525
|
+
case "subagent.session.created": {
|
|
19526
|
+
const props = event.properties;
|
|
19527
|
+
if (props?.sessionID && typeof props.agentName === "string") {
|
|
19528
|
+
this.sessionAgent.set(props.sessionID, props.agentName);
|
|
19529
|
+
}
|
|
19530
|
+
break;
|
|
19531
|
+
}
|
|
19532
|
+
case "session.deleted": {
|
|
19533
|
+
const props = event.properties;
|
|
19534
|
+
const id = props?.info?.id ?? props?.sessionID;
|
|
19535
|
+
if (id) {
|
|
19536
|
+
this.sessionModel.delete(id);
|
|
19537
|
+
this.sessionAgent.delete(id);
|
|
19538
|
+
this.sessionTried.delete(id);
|
|
19539
|
+
this.inProgress.delete(id);
|
|
19540
|
+
this.lastTrigger.delete(id);
|
|
19541
|
+
}
|
|
19542
|
+
break;
|
|
19543
|
+
}
|
|
19544
|
+
}
|
|
19545
|
+
}
|
|
19546
|
+
async tryFallback(sessionID) {
|
|
19547
|
+
if (!sessionID)
|
|
19548
|
+
return;
|
|
19549
|
+
if (this.inProgress.has(sessionID))
|
|
19550
|
+
return;
|
|
19551
|
+
const now = Date.now();
|
|
19552
|
+
if (now - (this.lastTrigger.get(sessionID) ?? 0) < DEDUP_WINDOW_MS)
|
|
19553
|
+
return;
|
|
19554
|
+
this.lastTrigger.set(sessionID, now);
|
|
19555
|
+
this.inProgress.add(sessionID);
|
|
19556
|
+
try {
|
|
19557
|
+
const currentModel = this.sessionModel.get(sessionID);
|
|
19558
|
+
const agentName = this.sessionAgent.get(sessionID);
|
|
19559
|
+
const chain = this.resolveChain(agentName, currentModel);
|
|
19560
|
+
if (!chain.length) {
|
|
19561
|
+
log("[foreground-fallback] no chain configured", { sessionID, agentName });
|
|
19562
|
+
return;
|
|
19563
|
+
}
|
|
19564
|
+
if (!this.sessionTried.has(sessionID)) {
|
|
19565
|
+
this.sessionTried.set(sessionID, new Set);
|
|
19566
|
+
}
|
|
19567
|
+
const tried = this.sessionTried.get(sessionID);
|
|
19568
|
+
if (currentModel)
|
|
19569
|
+
tried.add(currentModel);
|
|
19570
|
+
const nextModel = chain.find((m) => !tried.has(m));
|
|
19571
|
+
if (!nextModel) {
|
|
19572
|
+
log("[foreground-fallback] fallback chain exhausted", {
|
|
19573
|
+
sessionID,
|
|
19574
|
+
agentName,
|
|
19575
|
+
tried: [...tried]
|
|
19576
|
+
});
|
|
19577
|
+
return;
|
|
19578
|
+
}
|
|
19579
|
+
tried.add(nextModel);
|
|
19580
|
+
const ref = parseModel(nextModel);
|
|
19581
|
+
if (!ref) {
|
|
19582
|
+
log("[foreground-fallback] invalid model format", {
|
|
19583
|
+
sessionID,
|
|
19584
|
+
nextModel
|
|
19585
|
+
});
|
|
19586
|
+
return;
|
|
19587
|
+
}
|
|
19588
|
+
const result = await this.client.session.messages({
|
|
19589
|
+
path: { id: sessionID }
|
|
19590
|
+
});
|
|
19591
|
+
const messages = result.data ?? [];
|
|
19592
|
+
const lastUser = [...messages].reverse().find((m) => m.info.role === "user");
|
|
19593
|
+
if (!lastUser) {
|
|
19594
|
+
log("[foreground-fallback] no user message found", { sessionID });
|
|
19595
|
+
return;
|
|
19596
|
+
}
|
|
19597
|
+
try {
|
|
19598
|
+
await this.client.session.abort({ path: { id: sessionID } });
|
|
19599
|
+
} catch {}
|
|
19600
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
19601
|
+
const sessionClient = this.client.session;
|
|
19602
|
+
await sessionClient.promptAsync({
|
|
19603
|
+
path: { id: sessionID },
|
|
19604
|
+
body: { parts: lastUser.parts, model: ref }
|
|
19605
|
+
});
|
|
19606
|
+
this.sessionModel.set(sessionID, nextModel);
|
|
19607
|
+
log("[foreground-fallback] switched to fallback model", {
|
|
19608
|
+
sessionID,
|
|
19609
|
+
agentName,
|
|
19610
|
+
from: currentModel,
|
|
19611
|
+
to: nextModel
|
|
19612
|
+
});
|
|
19613
|
+
} catch (err) {
|
|
19614
|
+
log("[foreground-fallback] fallback attempt failed", {
|
|
19615
|
+
sessionID,
|
|
19616
|
+
error: err instanceof Error ? err.message : String(err)
|
|
19617
|
+
});
|
|
19618
|
+
} finally {
|
|
19619
|
+
this.inProgress.delete(sessionID);
|
|
19620
|
+
}
|
|
19621
|
+
}
|
|
19622
|
+
resolveChain(agentName, currentModel) {
|
|
19623
|
+
if (agentName) {
|
|
19624
|
+
return this.chains[agentName] ?? [];
|
|
19625
|
+
}
|
|
19626
|
+
if (currentModel) {
|
|
19627
|
+
for (const chain of Object.values(this.chains)) {
|
|
19628
|
+
if (chain.includes(currentModel))
|
|
19629
|
+
return chain;
|
|
19630
|
+
}
|
|
19631
|
+
}
|
|
19632
|
+
const all = [];
|
|
19633
|
+
const seen = new Set;
|
|
19634
|
+
for (const chain of Object.values(this.chains)) {
|
|
19635
|
+
for (const m of chain) {
|
|
19636
|
+
if (!seen.has(m)) {
|
|
19637
|
+
seen.add(m);
|
|
19638
|
+
all.push(m);
|
|
19639
|
+
}
|
|
19640
|
+
}
|
|
19641
|
+
}
|
|
19642
|
+
return all;
|
|
19643
|
+
}
|
|
19644
|
+
}
|
|
19189
19645
|
// src/hooks/json-error-recovery/hook.ts
|
|
19190
19646
|
var JSON_ERROR_TOOL_EXCLUDE_LIST = [
|
|
19191
19647
|
"bash",
|
|
@@ -31656,12 +32112,12 @@ var {spawn: spawn3 } = globalThis.Bun;
|
|
|
31656
32112
|
// src/tools/ast-grep/constants.ts
|
|
31657
32113
|
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
31658
32114
|
import { createRequire as createRequire2 } from "module";
|
|
31659
|
-
import { dirname as
|
|
32115
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
31660
32116
|
|
|
31661
32117
|
// src/tools/ast-grep/downloader.ts
|
|
31662
32118
|
import { chmodSync, existsSync as existsSync4, mkdirSync, unlinkSync } from "fs";
|
|
31663
32119
|
import { createRequire } from "module";
|
|
31664
|
-
import { homedir as
|
|
32120
|
+
import { homedir as homedir3 } from "os";
|
|
31665
32121
|
import { join as join7 } from "path";
|
|
31666
32122
|
var REPO = "ast-grep/ast-grep";
|
|
31667
32123
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -31686,11 +32142,11 @@ var PLATFORM_MAP = {
|
|
|
31686
32142
|
function getCacheDir2() {
|
|
31687
32143
|
if (process.platform === "win32") {
|
|
31688
32144
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
31689
|
-
const base2 = localAppData || join7(
|
|
32145
|
+
const base2 = localAppData || join7(homedir3(), "AppData", "Local");
|
|
31690
32146
|
return join7(base2, "oh-my-opencode-slim", "bin");
|
|
31691
32147
|
}
|
|
31692
32148
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
31693
|
-
const base = xdgCache || join7(
|
|
32149
|
+
const base = xdgCache || join7(homedir3(), ".cache");
|
|
31694
32150
|
return join7(base, "oh-my-opencode-slim", "bin");
|
|
31695
32151
|
}
|
|
31696
32152
|
function getBinaryName() {
|
|
@@ -31713,8 +32169,8 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
31713
32169
|
if (existsSync4(binaryPath)) {
|
|
31714
32170
|
return binaryPath;
|
|
31715
32171
|
}
|
|
31716
|
-
const { arch, os:
|
|
31717
|
-
const assetName = `app-${arch}-${
|
|
32172
|
+
const { arch, os: os3 } = platformInfo;
|
|
32173
|
+
const assetName = `app-${arch}-${os3}.zip`;
|
|
31718
32174
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${version3}/${assetName}`;
|
|
31719
32175
|
console.log(`[oh-my-opencode-slim] Downloading ast-grep binary...`);
|
|
31720
32176
|
try {
|
|
@@ -31813,7 +32269,7 @@ function findSgCliPathSync() {
|
|
|
31813
32269
|
try {
|
|
31814
32270
|
const require2 = createRequire2(import.meta.url);
|
|
31815
32271
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
31816
|
-
const cliDir =
|
|
32272
|
+
const cliDir = dirname3(cliPkgPath);
|
|
31817
32273
|
const sgPath = join8(cliDir, binaryName);
|
|
31818
32274
|
if (existsSync5(sgPath) && isValidBinary(sgPath)) {
|
|
31819
32275
|
return sgPath;
|
|
@@ -31824,7 +32280,7 @@ function findSgCliPathSync() {
|
|
|
31824
32280
|
try {
|
|
31825
32281
|
const require2 = createRequire2(import.meta.url);
|
|
31826
32282
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
31827
|
-
const pkgDir =
|
|
32283
|
+
const pkgDir = dirname3(pkgPath);
|
|
31828
32284
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
31829
32285
|
const binaryPath = join8(pkgDir, astGrepName);
|
|
31830
32286
|
if (existsSync5(binaryPath) && isValidBinary(binaryPath)) {
|
|
@@ -32314,7 +32770,7 @@ var {spawn: spawn4 } = globalThis.Bun;
|
|
|
32314
32770
|
// src/tools/grep/constants.ts
|
|
32315
32771
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
32316
32772
|
import { existsSync as existsSync8 } from "fs";
|
|
32317
|
-
import { dirname as
|
|
32773
|
+
import { dirname as dirname4, join as join10 } from "path";
|
|
32318
32774
|
|
|
32319
32775
|
// src/tools/grep/downloader.ts
|
|
32320
32776
|
import {
|
|
@@ -32359,7 +32815,7 @@ function getDataDir() {
|
|
|
32359
32815
|
}
|
|
32360
32816
|
function getOpenCodeBundledRg() {
|
|
32361
32817
|
const execPath = process.execPath;
|
|
32362
|
-
const execDir =
|
|
32818
|
+
const execDir = dirname4(execPath);
|
|
32363
32819
|
const isWindows = process.platform === "win32";
|
|
32364
32820
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
32365
32821
|
const candidates = [
|
|
@@ -32632,17 +33088,47 @@ var grep = tool({
|
|
|
32632
33088
|
// src/tools/lsp/client.ts
|
|
32633
33089
|
var import_node = __toESM(require_main(), 1);
|
|
32634
33090
|
import { readFileSync as readFileSync4 } from "fs";
|
|
32635
|
-
import { extname, resolve } from "path";
|
|
33091
|
+
import { extname, resolve as resolve2 } from "path";
|
|
32636
33092
|
import { Readable, Writable } from "stream";
|
|
32637
33093
|
import { pathToFileURL } from "url";
|
|
32638
33094
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
32639
33095
|
|
|
32640
33096
|
// src/tools/lsp/config.ts
|
|
32641
|
-
|
|
32642
|
-
import {
|
|
33097
|
+
var import_which = __toESM(require_lib(), 1);
|
|
33098
|
+
import { existsSync as existsSync10 } from "fs";
|
|
33099
|
+
import { homedir as homedir4 } from "os";
|
|
32643
33100
|
import { join as join11 } from "path";
|
|
32644
33101
|
|
|
33102
|
+
// src/tools/lsp/config-store.ts
|
|
33103
|
+
var userConfig = new Map;
|
|
33104
|
+
function setUserLspConfig(config3) {
|
|
33105
|
+
userConfig.clear();
|
|
33106
|
+
if (config3) {
|
|
33107
|
+
for (const [id, server] of Object.entries(config3)) {
|
|
33108
|
+
if (server && typeof server === "object") {
|
|
33109
|
+
const s = server;
|
|
33110
|
+
userConfig.set(id, {
|
|
33111
|
+
id,
|
|
33112
|
+
command: s.command,
|
|
33113
|
+
extensions: s.extensions,
|
|
33114
|
+
disabled: s.disabled,
|
|
33115
|
+
env: s.env,
|
|
33116
|
+
initialization: s.initialization
|
|
33117
|
+
});
|
|
33118
|
+
}
|
|
33119
|
+
}
|
|
33120
|
+
}
|
|
33121
|
+
}
|
|
33122
|
+
function getAllUserLspConfigs() {
|
|
33123
|
+
return new Map(userConfig);
|
|
33124
|
+
}
|
|
33125
|
+
function hasUserLspConfig() {
|
|
33126
|
+
return userConfig.size > 0;
|
|
33127
|
+
}
|
|
33128
|
+
|
|
32645
33129
|
// src/tools/lsp/constants.ts
|
|
33130
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
33131
|
+
import { dirname as dirname5, resolve } from "path";
|
|
32646
33132
|
var SEVERITY_MAP = {
|
|
32647
33133
|
1: "error",
|
|
32648
33134
|
2: "warning",
|
|
@@ -32651,22 +33137,76 @@ var SEVERITY_MAP = {
|
|
|
32651
33137
|
};
|
|
32652
33138
|
var DEFAULT_MAX_REFERENCES = 200;
|
|
32653
33139
|
var DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
33140
|
+
var LOCK_FILE_PATTERNS = [
|
|
33141
|
+
"package-lock.json",
|
|
33142
|
+
"bun.lockb",
|
|
33143
|
+
"bun.lock",
|
|
33144
|
+
"pnpm-lock.yaml",
|
|
33145
|
+
"yarn.lock"
|
|
33146
|
+
];
|
|
33147
|
+
function* walkUpDirectories(start, stop) {
|
|
33148
|
+
let dir = resolve(start);
|
|
33149
|
+
try {
|
|
33150
|
+
if (!statSync3(dir).isDirectory()) {
|
|
33151
|
+
dir = dirname5(dir);
|
|
33152
|
+
}
|
|
33153
|
+
} catch {
|
|
33154
|
+
dir = dirname5(dir);
|
|
33155
|
+
}
|
|
33156
|
+
let prevDir = "";
|
|
33157
|
+
while (dir !== prevDir && dir !== "/") {
|
|
33158
|
+
yield dir;
|
|
33159
|
+
prevDir = dir;
|
|
33160
|
+
if (dir === stop)
|
|
33161
|
+
break;
|
|
33162
|
+
dir = dirname5(dir);
|
|
33163
|
+
}
|
|
33164
|
+
}
|
|
33165
|
+
function NearestRoot(includePatterns, excludePatterns) {
|
|
33166
|
+
return (file3) => {
|
|
33167
|
+
const cwd = process.cwd();
|
|
33168
|
+
if (excludePatterns) {
|
|
33169
|
+
for (const dir of walkUpDirectories(file3, cwd)) {
|
|
33170
|
+
for (const pattern of excludePatterns) {
|
|
33171
|
+
if (existsSync9(`${dir}/${pattern}`)) {
|
|
33172
|
+
return;
|
|
33173
|
+
}
|
|
33174
|
+
}
|
|
33175
|
+
}
|
|
33176
|
+
}
|
|
33177
|
+
for (const dir of walkUpDirectories(file3, cwd)) {
|
|
33178
|
+
for (const pattern of includePatterns) {
|
|
33179
|
+
if (pattern.includes("*")) {
|
|
33180
|
+
try {
|
|
33181
|
+
const entries = readdirSync2(dir);
|
|
33182
|
+
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
|
|
33183
|
+
if (entries.some((entry) => regex.test(entry))) {
|
|
33184
|
+
return dir;
|
|
33185
|
+
}
|
|
33186
|
+
} catch {}
|
|
33187
|
+
} else if (existsSync9(`${dir}/${pattern}`)) {
|
|
33188
|
+
return dir;
|
|
33189
|
+
}
|
|
33190
|
+
}
|
|
33191
|
+
}
|
|
33192
|
+
return;
|
|
33193
|
+
};
|
|
33194
|
+
}
|
|
32654
33195
|
var BUILTIN_SERVERS = {
|
|
33196
|
+
deno: {
|
|
33197
|
+
command: ["deno", "lsp"],
|
|
33198
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs"],
|
|
33199
|
+
root: NearestRoot(["deno.json", "deno.jsonc"])
|
|
33200
|
+
},
|
|
32655
33201
|
typescript: {
|
|
32656
33202
|
command: ["typescript-language-server", "--stdio"],
|
|
32657
|
-
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]
|
|
33203
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
33204
|
+
root: NearestRoot(LOCK_FILE_PATTERNS, ["deno.json", "deno.jsonc"])
|
|
32658
33205
|
},
|
|
32659
33206
|
vue: {
|
|
32660
33207
|
command: ["vue-language-server", "--stdio"],
|
|
32661
|
-
extensions: [".vue"]
|
|
32662
|
-
|
|
32663
|
-
svelte: {
|
|
32664
|
-
command: ["svelteserver", "--stdio"],
|
|
32665
|
-
extensions: [".svelte"]
|
|
32666
|
-
},
|
|
32667
|
-
astro: {
|
|
32668
|
-
command: ["astro-ls", "--stdio"],
|
|
32669
|
-
extensions: [".astro"]
|
|
33208
|
+
extensions: [".vue"],
|
|
33209
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
32670
33210
|
},
|
|
32671
33211
|
eslint: {
|
|
32672
33212
|
command: ["vscode-eslint-language-server", "--stdio"],
|
|
@@ -32677,54 +33217,316 @@ var BUILTIN_SERVERS = {
|
|
|
32677
33217
|
".jsx",
|
|
32678
33218
|
".mjs",
|
|
32679
33219
|
".cjs",
|
|
33220
|
+
".mts",
|
|
33221
|
+
".cts",
|
|
33222
|
+
".vue"
|
|
33223
|
+
],
|
|
33224
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33225
|
+
},
|
|
33226
|
+
oxlint: {
|
|
33227
|
+
command: ["oxlint", "--lsp"],
|
|
33228
|
+
extensions: [
|
|
33229
|
+
".ts",
|
|
33230
|
+
".tsx",
|
|
33231
|
+
".js",
|
|
33232
|
+
".jsx",
|
|
33233
|
+
".mjs",
|
|
33234
|
+
".cjs",
|
|
33235
|
+
".mts",
|
|
33236
|
+
".cts",
|
|
32680
33237
|
".vue",
|
|
33238
|
+
".astro",
|
|
32681
33239
|
".svelte"
|
|
32682
|
-
]
|
|
33240
|
+
],
|
|
33241
|
+
root: NearestRoot([
|
|
33242
|
+
".oxlintrc.json",
|
|
33243
|
+
...LOCK_FILE_PATTERNS,
|
|
33244
|
+
"package.json"
|
|
33245
|
+
])
|
|
32683
33246
|
},
|
|
32684
|
-
|
|
32685
|
-
command: ["
|
|
32686
|
-
extensions: [
|
|
33247
|
+
biome: {
|
|
33248
|
+
command: ["biome", "lsp-proxy", "--stdio"],
|
|
33249
|
+
extensions: [
|
|
33250
|
+
".ts",
|
|
33251
|
+
".tsx",
|
|
33252
|
+
".js",
|
|
33253
|
+
".jsx",
|
|
33254
|
+
".mjs",
|
|
33255
|
+
".cjs",
|
|
33256
|
+
".mts",
|
|
33257
|
+
".cts",
|
|
33258
|
+
".json",
|
|
33259
|
+
".jsonc",
|
|
33260
|
+
".vue",
|
|
33261
|
+
".astro",
|
|
33262
|
+
".svelte",
|
|
33263
|
+
".css",
|
|
33264
|
+
".graphql",
|
|
33265
|
+
".gql",
|
|
33266
|
+
".html"
|
|
33267
|
+
],
|
|
33268
|
+
root: NearestRoot(["biome.json", "biome.jsonc", ...LOCK_FILE_PATTERNS])
|
|
32687
33269
|
},
|
|
32688
33270
|
gopls: {
|
|
32689
33271
|
command: ["gopls"],
|
|
32690
|
-
extensions: [".go"]
|
|
33272
|
+
extensions: [".go"],
|
|
33273
|
+
root: NearestRoot(["go.work", "go.mod", "go.sum"])
|
|
32691
33274
|
},
|
|
32692
|
-
|
|
32693
|
-
command: ["
|
|
32694
|
-
extensions: [".
|
|
33275
|
+
ruby_lsp: {
|
|
33276
|
+
command: ["rubocop", "--lsp"],
|
|
33277
|
+
extensions: [".rb", ".rake", ".gemspec", ".ru"],
|
|
33278
|
+
root: NearestRoot(["Gemfile"])
|
|
32695
33279
|
},
|
|
32696
|
-
|
|
32697
|
-
command: ["
|
|
32698
|
-
extensions: [".py", ".pyi"]
|
|
33280
|
+
ty: {
|
|
33281
|
+
command: ["ty", "server"],
|
|
33282
|
+
extensions: [".py", ".pyi"],
|
|
33283
|
+
root: NearestRoot([
|
|
33284
|
+
"pyproject.toml",
|
|
33285
|
+
"ty.toml",
|
|
33286
|
+
"setup.py",
|
|
33287
|
+
"setup.cfg",
|
|
33288
|
+
"requirements.txt",
|
|
33289
|
+
"Pipfile",
|
|
33290
|
+
"pyrightconfig.json"
|
|
33291
|
+
])
|
|
32699
33292
|
},
|
|
32700
33293
|
pyright: {
|
|
32701
33294
|
command: ["pyright-langserver", "--stdio"],
|
|
32702
|
-
extensions: [".py", ".pyi"]
|
|
33295
|
+
extensions: [".py", ".pyi"],
|
|
33296
|
+
root: NearestRoot([
|
|
33297
|
+
"pyproject.toml",
|
|
33298
|
+
"setup.py",
|
|
33299
|
+
"setup.cfg",
|
|
33300
|
+
"requirements.txt",
|
|
33301
|
+
"Pipfile",
|
|
33302
|
+
"pyrightconfig.json"
|
|
33303
|
+
])
|
|
32703
33304
|
},
|
|
32704
|
-
|
|
32705
|
-
command: ["
|
|
32706
|
-
extensions: [".
|
|
33305
|
+
elixir_ls: {
|
|
33306
|
+
command: ["elixir-ls"],
|
|
33307
|
+
extensions: [".ex", ".exs"],
|
|
33308
|
+
root: NearestRoot(["mix.exs", "mix.lock"])
|
|
32707
33309
|
},
|
|
32708
33310
|
zls: {
|
|
32709
33311
|
command: ["zls"],
|
|
32710
|
-
extensions: [".zig"]
|
|
33312
|
+
extensions: [".zig", ".zon"],
|
|
33313
|
+
root: NearestRoot(["build.zig"])
|
|
33314
|
+
},
|
|
33315
|
+
csharp: {
|
|
33316
|
+
command: ["csharp-ls"],
|
|
33317
|
+
extensions: [".cs"],
|
|
33318
|
+
root: NearestRoot([".slnx", ".sln", ".csproj", "global.json"])
|
|
33319
|
+
},
|
|
33320
|
+
fsharp: {
|
|
33321
|
+
command: ["fsautocomplete"],
|
|
33322
|
+
extensions: [".fs", ".fsi", ".fsx", ".fsscript"],
|
|
33323
|
+
root: NearestRoot([".slnx", ".sln", ".fsproj", "global.json"])
|
|
33324
|
+
},
|
|
33325
|
+
sourcekit_lsp: {
|
|
33326
|
+
command: ["sourcekit-lsp"],
|
|
33327
|
+
extensions: [".swift", ".objc", ".objcpp"],
|
|
33328
|
+
root: NearestRoot(["Package.swift", "*.xcodeproj", "*.xcworkspace"])
|
|
33329
|
+
},
|
|
33330
|
+
rust: {
|
|
33331
|
+
command: ["rust-analyzer"],
|
|
33332
|
+
extensions: [".rs"],
|
|
33333
|
+
root: NearestRoot(["Cargo.toml", "Cargo.lock"])
|
|
33334
|
+
},
|
|
33335
|
+
clangd: {
|
|
33336
|
+
command: ["clangd", "--background-index", "--clang-tidy"],
|
|
33337
|
+
extensions: [
|
|
33338
|
+
".c",
|
|
33339
|
+
".cpp",
|
|
33340
|
+
".cc",
|
|
33341
|
+
".cxx",
|
|
33342
|
+
".c++",
|
|
33343
|
+
".h",
|
|
33344
|
+
".hpp",
|
|
33345
|
+
".hh",
|
|
33346
|
+
".hxx",
|
|
33347
|
+
".h++"
|
|
33348
|
+
],
|
|
33349
|
+
root: NearestRoot([
|
|
33350
|
+
"compile_commands.json",
|
|
33351
|
+
"compile_flags.txt",
|
|
33352
|
+
".clangd",
|
|
33353
|
+
"CMakeLists.txt",
|
|
33354
|
+
"Makefile"
|
|
33355
|
+
])
|
|
33356
|
+
},
|
|
33357
|
+
svelte: {
|
|
33358
|
+
command: ["svelteserver", "--stdio"],
|
|
33359
|
+
extensions: [".svelte"],
|
|
33360
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33361
|
+
},
|
|
33362
|
+
astro: {
|
|
33363
|
+
command: ["astro-ls", "--stdio"],
|
|
33364
|
+
extensions: [".astro"],
|
|
33365
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33366
|
+
},
|
|
33367
|
+
jdtls: {
|
|
33368
|
+
command: ["jdtls"],
|
|
33369
|
+
extensions: [".java"],
|
|
33370
|
+
root: NearestRoot([
|
|
33371
|
+
"pom.xml",
|
|
33372
|
+
"build.gradle",
|
|
33373
|
+
"build.gradle.kts",
|
|
33374
|
+
".project",
|
|
33375
|
+
".classpath"
|
|
33376
|
+
])
|
|
33377
|
+
},
|
|
33378
|
+
kotlin_ls: {
|
|
33379
|
+
command: ["kotlin-lsp", "--stdio"],
|
|
33380
|
+
extensions: [".kt", ".kts"],
|
|
33381
|
+
root: NearestRoot([
|
|
33382
|
+
"settings.gradle.kts",
|
|
33383
|
+
"settings.gradle",
|
|
33384
|
+
"gradlew",
|
|
33385
|
+
"build.gradle.kts",
|
|
33386
|
+
"build.gradle",
|
|
33387
|
+
"pom.xml"
|
|
33388
|
+
])
|
|
33389
|
+
},
|
|
33390
|
+
yaml_ls: {
|
|
33391
|
+
command: ["yaml-language-server", "--stdio"],
|
|
33392
|
+
extensions: [".yaml", ".yml"],
|
|
33393
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33394
|
+
},
|
|
33395
|
+
lua_ls: {
|
|
33396
|
+
command: ["lua-language-server"],
|
|
33397
|
+
extensions: [".lua"],
|
|
33398
|
+
root: NearestRoot([
|
|
33399
|
+
".luarc.json",
|
|
33400
|
+
".luarc.jsonc",
|
|
33401
|
+
".luacheckrc",
|
|
33402
|
+
"stylua.toml",
|
|
33403
|
+
"selene.toml",
|
|
33404
|
+
"selene.yml"
|
|
33405
|
+
])
|
|
33406
|
+
},
|
|
33407
|
+
php_intelephense: {
|
|
33408
|
+
command: ["intelephense", "--stdio"],
|
|
33409
|
+
extensions: [".php"],
|
|
33410
|
+
root: NearestRoot(["composer.json", "composer.lock", ".php-version"])
|
|
33411
|
+
},
|
|
33412
|
+
prisma: {
|
|
33413
|
+
command: ["prisma", "language-server"],
|
|
33414
|
+
extensions: [".prisma"],
|
|
33415
|
+
root: NearestRoot(["schema.prisma", "prisma/schema.prisma", "prisma"])
|
|
33416
|
+
},
|
|
33417
|
+
dart: {
|
|
33418
|
+
command: ["dart", "language-server", "--lsp"],
|
|
33419
|
+
extensions: [".dart"],
|
|
33420
|
+
root: NearestRoot(["pubspec.yaml", "analysis_options.yaml"])
|
|
33421
|
+
},
|
|
33422
|
+
ocaml_lsp: {
|
|
33423
|
+
command: ["ocamllsp"],
|
|
33424
|
+
extensions: [".ml", ".mli"],
|
|
33425
|
+
root: NearestRoot(["dune-project", "dune-workspace", ".merlin", "opam"])
|
|
33426
|
+
},
|
|
33427
|
+
bash: {
|
|
33428
|
+
command: ["bash-language-server", "start"],
|
|
33429
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"],
|
|
33430
|
+
root: undefined
|
|
33431
|
+
},
|
|
33432
|
+
terraform_ls: {
|
|
33433
|
+
command: ["terraform-ls", "serve"],
|
|
33434
|
+
extensions: [".tf", ".tfvars"],
|
|
33435
|
+
root: NearestRoot([".terraform.lock.hcl", "terraform.tfstate", "*.tf"])
|
|
33436
|
+
},
|
|
33437
|
+
texlab: {
|
|
33438
|
+
command: ["texlab"],
|
|
33439
|
+
extensions: [".tex", ".bib"],
|
|
33440
|
+
root: NearestRoot([".latexmkrc", "latexmkrc", ".texlabroot", "texlabroot"])
|
|
33441
|
+
},
|
|
33442
|
+
dockerfile: {
|
|
33443
|
+
command: ["docker-langserver", "--stdio"],
|
|
33444
|
+
extensions: [".dockerfile", "Dockerfile"],
|
|
33445
|
+
root: undefined
|
|
33446
|
+
},
|
|
33447
|
+
gleam: {
|
|
33448
|
+
command: ["gleam", "lsp"],
|
|
33449
|
+
extensions: [".gleam"],
|
|
33450
|
+
root: NearestRoot(["gleam.toml"])
|
|
33451
|
+
},
|
|
33452
|
+
clojure_lsp: {
|
|
33453
|
+
command: ["clojure-lsp", "listen"],
|
|
33454
|
+
extensions: [".clj", ".cljs", ".cljc", ".edn"],
|
|
33455
|
+
root: NearestRoot([
|
|
33456
|
+
"deps.edn",
|
|
33457
|
+
"project.clj",
|
|
33458
|
+
"shadow-cljs.edn",
|
|
33459
|
+
"bb.edn",
|
|
33460
|
+
"build.boot"
|
|
33461
|
+
])
|
|
33462
|
+
},
|
|
33463
|
+
nixd: {
|
|
33464
|
+
command: ["nixd"],
|
|
33465
|
+
extensions: [".nix"],
|
|
33466
|
+
root: NearestRoot(["flake.nix"])
|
|
33467
|
+
},
|
|
33468
|
+
tinymist: {
|
|
33469
|
+
command: ["tinymist"],
|
|
33470
|
+
extensions: [".typ", ".typc"],
|
|
33471
|
+
root: NearestRoot(["typst.toml"])
|
|
33472
|
+
},
|
|
33473
|
+
haskell_language_server: {
|
|
33474
|
+
command: ["haskell-language-server-wrapper", "--lsp"],
|
|
33475
|
+
extensions: [".hs", ".lhs"],
|
|
33476
|
+
root: NearestRoot(["stack.yaml", "cabal.project", "hie.yaml", "*.cabal"])
|
|
33477
|
+
},
|
|
33478
|
+
julials: {
|
|
33479
|
+
command: [
|
|
33480
|
+
"julia",
|
|
33481
|
+
"--startup-file=no",
|
|
33482
|
+
"--history-file=no",
|
|
33483
|
+
"-e",
|
|
33484
|
+
"using LanguageServer; runserver()"
|
|
33485
|
+
],
|
|
33486
|
+
extensions: [".jl"],
|
|
33487
|
+
root: NearestRoot(["Project.toml", "Manifest.toml", "*.jl"])
|
|
32711
33488
|
}
|
|
32712
33489
|
};
|
|
32713
33490
|
var LSP_INSTALL_HINTS = {
|
|
33491
|
+
deno: "Install Deno: https://deno.land/#installation",
|
|
32714
33492
|
typescript: "npm install -g typescript-language-server typescript",
|
|
32715
33493
|
vue: "npm install -g @vue/language-server",
|
|
32716
|
-
svelte: "npm install -g svelte-language-server",
|
|
32717
|
-
astro: "npm install -g @astrojs/language-server",
|
|
32718
33494
|
eslint: "npm install -g vscode-langservers-extracted",
|
|
32719
|
-
|
|
33495
|
+
oxlint: "npm install -g oxlint or install via package manager",
|
|
33496
|
+
biome: "npm install -g @biomejs/biome",
|
|
32720
33497
|
gopls: "go install golang.org/x/tools/gopls@latest",
|
|
32721
|
-
|
|
32722
|
-
|
|
33498
|
+
ruby_lsp: "gem install rubocop (Ruby LSP runs via rubocop --lsp)",
|
|
33499
|
+
ty: "pip install ty or see https://github.com/astral-sh/ty",
|
|
32723
33500
|
pyright: "pip install pyright",
|
|
32724
|
-
|
|
32725
|
-
zls: "
|
|
33501
|
+
elixir_ls: "Download from https://github.com/elixir-lsp/elixir-ls/releases or build from source",
|
|
33502
|
+
zls: "Install via your package manager or build from source: https://github.com/zigtools/zls",
|
|
33503
|
+
csharp: "dotnet tool install --global csharp-ls",
|
|
33504
|
+
fsharp: "dotnet tool install --global fsautocomplete",
|
|
33505
|
+
sourcekit_lsp: "Install via Xcode or Swift toolchain (included with Xcode)",
|
|
33506
|
+
rust: "rustup component add rust-analyzer",
|
|
33507
|
+
clangd: "Install clangd via your system package manager or LLVM",
|
|
33508
|
+
svelte: "npm install -g svelte-language-server",
|
|
33509
|
+
astro: "npm install -g @astrojs/language-server",
|
|
33510
|
+
jdtls: "See https://github.com/eclipse-jdtls/eclipse.jdt.ls for installation",
|
|
33511
|
+
kotlin_ls: "Download from https://github.com/Kotlin/kotlin-lsp/releases",
|
|
33512
|
+
yaml_ls: "npm install -g yaml-language-server",
|
|
33513
|
+
lua_ls: "Download from https://github.com/LuaLS/lua-language-server/releases",
|
|
33514
|
+
php_intelephense: "npm install -g intelephense",
|
|
33515
|
+
prisma: "npm install -g @prisma/language-server or use npx",
|
|
33516
|
+
dart: "dart pub global activate language_server",
|
|
33517
|
+
ocaml_lsp: "opam install ocaml-lsp-server",
|
|
33518
|
+
bash: "npm install -g bash-language-server",
|
|
33519
|
+
terraform_ls: "Download from https://github.com/hashicorp/terraform-ls/releases or install via tfenv",
|
|
33520
|
+
texlab: "Download from https://github.com/latex-lsp/texlab/releases",
|
|
33521
|
+
dockerfile: "npm install -g dockerfile-language-server-nodejs",
|
|
33522
|
+
gleam: "Install Gleam: https://gleam.run/getting-started/",
|
|
33523
|
+
clojure_lsp: "Install via deps.edn, project.clj, or: clj -M -m clojure-lsp.main",
|
|
33524
|
+
nixd: "Install via nix-env or your system package manager",
|
|
33525
|
+
tinymist: "cargo install tinymist or download from releases",
|
|
33526
|
+
haskell_language_server: "Install Haskell Tool Stack or Cabal, then language-server",
|
|
33527
|
+
julials: "Install Julia: https://julialang.org/downloads/"
|
|
32726
33528
|
};
|
|
32727
|
-
var
|
|
33529
|
+
var LANGUAGE_EXTENSIONS = {
|
|
32728
33530
|
".ts": "typescript",
|
|
32729
33531
|
".tsx": "typescriptreact",
|
|
32730
33532
|
".mts": "typescript",
|
|
@@ -32733,35 +33535,180 @@ var EXT_TO_LANG = {
|
|
|
32733
33535
|
".jsx": "javascriptreact",
|
|
32734
33536
|
".mjs": "javascript",
|
|
32735
33537
|
".cjs": "javascript",
|
|
33538
|
+
".ets": "typescript",
|
|
32736
33539
|
".vue": "vue",
|
|
32737
33540
|
".svelte": "svelte",
|
|
32738
33541
|
".astro": "astro",
|
|
32739
33542
|
".html": "html",
|
|
33543
|
+
".htm": "html",
|
|
33544
|
+
".xml": "xml",
|
|
33545
|
+
".xsl": "xsl",
|
|
32740
33546
|
".css": "css",
|
|
32741
33547
|
".scss": "scss",
|
|
33548
|
+
".sass": "sass",
|
|
32742
33549
|
".less": "less",
|
|
32743
33550
|
".json": "json",
|
|
33551
|
+
".jsonc": "json",
|
|
33552
|
+
".graphql": "graphql",
|
|
33553
|
+
".gql": "graphql",
|
|
33554
|
+
".dockerfile": "dockerfile",
|
|
33555
|
+
".sh": "shellscript",
|
|
33556
|
+
".bash": "shellscript",
|
|
33557
|
+
".zsh": "shellscript",
|
|
33558
|
+
".ksh": "shellscript",
|
|
32744
33559
|
".go": "go",
|
|
32745
33560
|
".rs": "rust",
|
|
32746
33561
|
".py": "python",
|
|
32747
33562
|
".pyi": "python",
|
|
33563
|
+
".rb": "ruby",
|
|
33564
|
+
".rake": "ruby",
|
|
33565
|
+
".gemspec": "ruby",
|
|
33566
|
+
".ru": "ruby",
|
|
32748
33567
|
".c": "c",
|
|
32749
33568
|
".cpp": "cpp",
|
|
32750
33569
|
".cc": "cpp",
|
|
32751
33570
|
".cxx": "cpp",
|
|
33571
|
+
".c++": "cpp",
|
|
32752
33572
|
".h": "c",
|
|
32753
33573
|
".hpp": "cpp",
|
|
32754
|
-
".
|
|
33574
|
+
".hh": "cpp",
|
|
33575
|
+
".hxx": "cpp",
|
|
33576
|
+
".h++": "cpp",
|
|
33577
|
+
".java": "java",
|
|
33578
|
+
".kt": "kotlin",
|
|
33579
|
+
".kts": "kotlin",
|
|
33580
|
+
".cs": "csharp",
|
|
33581
|
+
".fs": "fsharp",
|
|
33582
|
+
".fsi": "fsharp",
|
|
33583
|
+
".fsx": "fsharp",
|
|
33584
|
+
".fsscript": "fsharp",
|
|
33585
|
+
".swift": "swift",
|
|
33586
|
+
".m": "objective-c",
|
|
33587
|
+
".mm": "objective-cpp",
|
|
33588
|
+
".zig": "zig",
|
|
33589
|
+
".zon": "zig",
|
|
33590
|
+
".ex": "elixir",
|
|
33591
|
+
".exs": "elixir",
|
|
33592
|
+
".clj": "clojure",
|
|
33593
|
+
".cljs": "clojure",
|
|
33594
|
+
".cljc": "clojure",
|
|
33595
|
+
".edn": "clojure",
|
|
33596
|
+
".hs": "haskell",
|
|
33597
|
+
".lhs": "haskell",
|
|
33598
|
+
".ml": "ocaml",
|
|
33599
|
+
".mli": "ocaml",
|
|
33600
|
+
".scala": "scala",
|
|
33601
|
+
".php": "php",
|
|
33602
|
+
".lua": "lua",
|
|
33603
|
+
".dart": "dart",
|
|
33604
|
+
".yaml": "yaml",
|
|
33605
|
+
".yml": "yaml",
|
|
33606
|
+
".tf": "terraform",
|
|
33607
|
+
".tfvars": "terraform-vars",
|
|
33608
|
+
".hcl": "hcl",
|
|
33609
|
+
".nix": "nix",
|
|
33610
|
+
".typ": "typst",
|
|
33611
|
+
".typc": "typst",
|
|
33612
|
+
".tex": "latex",
|
|
33613
|
+
".latex": "latex",
|
|
33614
|
+
".bib": "bibtex",
|
|
33615
|
+
".bibtex": "bibtex",
|
|
33616
|
+
".prisma": "prisma",
|
|
33617
|
+
".jl": "julia",
|
|
33618
|
+
".gleam": "gleam",
|
|
33619
|
+
".md": "markdown",
|
|
33620
|
+
".markdown": "markdown",
|
|
33621
|
+
".d": "d",
|
|
33622
|
+
".pas": "pascal",
|
|
33623
|
+
".pascal": "pascal",
|
|
33624
|
+
".diff": "diff",
|
|
33625
|
+
".patch": "diff",
|
|
33626
|
+
".erl": "erlang",
|
|
33627
|
+
".hrl": "erlang",
|
|
33628
|
+
".groovy": "groovy",
|
|
33629
|
+
".handlebars": "handlebars",
|
|
33630
|
+
".hbs": "handlebars",
|
|
33631
|
+
".ini": "ini",
|
|
33632
|
+
".makefile": "makefile",
|
|
33633
|
+
makefile: "makefile",
|
|
33634
|
+
".pug": "jade",
|
|
33635
|
+
".jade": "jade",
|
|
33636
|
+
".r": "r",
|
|
33637
|
+
".cshtml": "razor",
|
|
33638
|
+
".razor": "razor",
|
|
33639
|
+
".erb": "erb",
|
|
33640
|
+
".html.erb": "erb",
|
|
33641
|
+
".js.erb": "erb",
|
|
33642
|
+
".css.erb": "erb",
|
|
33643
|
+
".json.erb": "erb",
|
|
33644
|
+
".shader": "shaderlab",
|
|
33645
|
+
".sql": "sql",
|
|
33646
|
+
".perl": "perl",
|
|
33647
|
+
".pl": "perl",
|
|
33648
|
+
".pm": "perl",
|
|
33649
|
+
".pm6": "perl6",
|
|
33650
|
+
".ps1": "powershell",
|
|
33651
|
+
".psm1": "powershell",
|
|
33652
|
+
".coffee": "coffeescript",
|
|
33653
|
+
".bat": "bat",
|
|
33654
|
+
".abap": "abap",
|
|
33655
|
+
".gitcommit": "git-commit",
|
|
33656
|
+
".gitrebase": "git-rebase"
|
|
32755
33657
|
};
|
|
32756
33658
|
|
|
32757
33659
|
// src/tools/lsp/config.ts
|
|
32758
|
-
function
|
|
33660
|
+
function buildMergedServers() {
|
|
33661
|
+
const servers = new Map;
|
|
32759
33662
|
for (const [id, config3] of Object.entries(BUILTIN_SERVERS)) {
|
|
33663
|
+
servers.set(id, {
|
|
33664
|
+
id,
|
|
33665
|
+
command: config3.command,
|
|
33666
|
+
extensions: config3.extensions,
|
|
33667
|
+
root: config3.root,
|
|
33668
|
+
env: config3.env,
|
|
33669
|
+
initialization: config3.initialization
|
|
33670
|
+
});
|
|
33671
|
+
}
|
|
33672
|
+
if (hasUserLspConfig()) {
|
|
33673
|
+
for (const [id, userConfig2] of getAllUserLspConfigs()) {
|
|
33674
|
+
if (userConfig2.disabled === true) {
|
|
33675
|
+
servers.delete(id);
|
|
33676
|
+
continue;
|
|
33677
|
+
}
|
|
33678
|
+
const existing = servers.get(id);
|
|
33679
|
+
if (existing) {
|
|
33680
|
+
servers.set(id, {
|
|
33681
|
+
...existing,
|
|
33682
|
+
id,
|
|
33683
|
+
command: userConfig2.command ?? existing.command,
|
|
33684
|
+
extensions: userConfig2.extensions ?? existing.extensions,
|
|
33685
|
+
root: existing.root,
|
|
33686
|
+
env: userConfig2.env ?? existing.env,
|
|
33687
|
+
initialization: userConfig2.initialization ?? existing.initialization
|
|
33688
|
+
});
|
|
33689
|
+
} else {
|
|
33690
|
+
servers.set(id, {
|
|
33691
|
+
id,
|
|
33692
|
+
command: userConfig2.command ?? [],
|
|
33693
|
+
extensions: userConfig2.extensions ?? [],
|
|
33694
|
+
root: undefined,
|
|
33695
|
+
env: userConfig2.env,
|
|
33696
|
+
initialization: userConfig2.initialization
|
|
33697
|
+
});
|
|
33698
|
+
}
|
|
33699
|
+
}
|
|
33700
|
+
}
|
|
33701
|
+
return servers;
|
|
33702
|
+
}
|
|
33703
|
+
function findServerForExtension(ext) {
|
|
33704
|
+
const servers = buildMergedServers();
|
|
33705
|
+
for (const [, config3] of servers) {
|
|
32760
33706
|
if (config3.extensions.includes(ext)) {
|
|
32761
33707
|
const server = {
|
|
32762
|
-
id,
|
|
33708
|
+
id: config3.id,
|
|
32763
33709
|
command: config3.command,
|
|
32764
33710
|
extensions: config3.extensions,
|
|
33711
|
+
root: config3.root,
|
|
32765
33712
|
env: config3.env,
|
|
32766
33713
|
initialization: config3.initialization
|
|
32767
33714
|
};
|
|
@@ -32771,39 +33718,37 @@ function findServerForExtension(ext) {
|
|
|
32771
33718
|
return {
|
|
32772
33719
|
status: "not_installed",
|
|
32773
33720
|
server,
|
|
32774
|
-
installHint: LSP_INSTALL_HINTS[id] || `Install '${config3.command[0]}' and add to PATH`
|
|
33721
|
+
installHint: LSP_INSTALL_HINTS[config3.id] || `Install '${config3.command[0]}' and add to PATH`
|
|
32775
33722
|
};
|
|
32776
33723
|
}
|
|
32777
33724
|
}
|
|
32778
33725
|
return { status: "not_configured", extension: ext };
|
|
32779
33726
|
}
|
|
32780
33727
|
function getLanguageId(ext) {
|
|
32781
|
-
return
|
|
33728
|
+
return LANGUAGE_EXTENSIONS[ext] || "plaintext";
|
|
32782
33729
|
}
|
|
32783
33730
|
function isServerInstalled(command) {
|
|
32784
33731
|
if (command.length === 0)
|
|
32785
33732
|
return false;
|
|
32786
33733
|
const cmd = command[0];
|
|
32787
33734
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
32788
|
-
return
|
|
33735
|
+
return existsSync10(cmd);
|
|
32789
33736
|
}
|
|
32790
33737
|
const isWindows = process.platform === "win32";
|
|
32791
33738
|
const ext = isWindows ? ".exe" : "";
|
|
32792
|
-
const
|
|
32793
|
-
const
|
|
32794
|
-
const
|
|
32795
|
-
|
|
32796
|
-
|
|
32797
|
-
|
|
32798
|
-
|
|
33739
|
+
const opencodeBin = join11(homedir4(), ".config", "opencode", "bin");
|
|
33740
|
+
const searchPath = (process.env.PATH ?? "") + (isWindows ? ";" : ":") + opencodeBin;
|
|
33741
|
+
const result = import_which.default.sync(cmd, {
|
|
33742
|
+
path: searchPath,
|
|
33743
|
+
pathExt: isWindows ? process.env.PATHEXT : undefined,
|
|
33744
|
+
nothrow: true
|
|
33745
|
+
});
|
|
33746
|
+
if (result !== null) {
|
|
33747
|
+
return true;
|
|
32799
33748
|
}
|
|
32800
33749
|
const cwd = process.cwd();
|
|
32801
33750
|
const localBin = join11(cwd, "node_modules", ".bin", cmd);
|
|
32802
|
-
if (
|
|
32803
|
-
return true;
|
|
32804
|
-
}
|
|
32805
|
-
const globalBin = join11(homedir5(), ".config", "opencode", "bin", cmd);
|
|
32806
|
-
if (existsSync9(globalBin) || existsSync9(globalBin + ext)) {
|
|
33751
|
+
if (existsSync10(localBin) || existsSync10(localBin + ext)) {
|
|
32807
33752
|
return true;
|
|
32808
33753
|
}
|
|
32809
33754
|
return false;
|
|
@@ -32816,6 +33761,7 @@ class LSPServerManager {
|
|
|
32816
33761
|
cleanupInterval = null;
|
|
32817
33762
|
IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
32818
33763
|
constructor() {
|
|
33764
|
+
log("[lsp] manager initialized");
|
|
32819
33765
|
this.startCleanupTimer();
|
|
32820
33766
|
this.registerProcessCleanup();
|
|
32821
33767
|
}
|
|
@@ -32872,16 +33818,31 @@ class LSPServerManager {
|
|
|
32872
33818
|
const managed = this.clients.get(key);
|
|
32873
33819
|
if (managed) {
|
|
32874
33820
|
if (managed.initPromise) {
|
|
33821
|
+
log("[lsp] getClient: waiting for init", { key, server: server.id });
|
|
32875
33822
|
await managed.initPromise;
|
|
32876
33823
|
}
|
|
32877
33824
|
if (managed.client.isAlive()) {
|
|
32878
33825
|
managed.refCount++;
|
|
32879
33826
|
managed.lastUsedAt = Date.now();
|
|
33827
|
+
log("[lsp] getClient: reuse pooled client", {
|
|
33828
|
+
key,
|
|
33829
|
+
server: server.id,
|
|
33830
|
+
refCount: managed.refCount
|
|
33831
|
+
});
|
|
32880
33832
|
return managed.client;
|
|
32881
33833
|
}
|
|
33834
|
+
log("[lsp] getClient: client dead, recreating", {
|
|
33835
|
+
key,
|
|
33836
|
+
server: server.id
|
|
33837
|
+
});
|
|
32882
33838
|
await managed.client.stop();
|
|
32883
33839
|
this.clients.delete(key);
|
|
32884
33840
|
}
|
|
33841
|
+
log("[lsp] getClient: creating new client", {
|
|
33842
|
+
key,
|
|
33843
|
+
server: server.id,
|
|
33844
|
+
root
|
|
33845
|
+
});
|
|
32885
33846
|
const client = new LSPClient(root, server);
|
|
32886
33847
|
const initPromise2 = (async () => {
|
|
32887
33848
|
await client.start();
|
|
@@ -32901,7 +33862,13 @@ class LSPServerManager {
|
|
|
32901
33862
|
m.initPromise = undefined;
|
|
32902
33863
|
m.isInitializing = false;
|
|
32903
33864
|
}
|
|
33865
|
+
log("[lsp] getClient: client ready", { key, server: server.id });
|
|
32904
33866
|
} catch (err) {
|
|
33867
|
+
log("[lsp] getClient: init failed", {
|
|
33868
|
+
key,
|
|
33869
|
+
server: server.id,
|
|
33870
|
+
error: String(err)
|
|
33871
|
+
});
|
|
32905
33872
|
this.clients.delete(key);
|
|
32906
33873
|
throw err;
|
|
32907
33874
|
}
|
|
@@ -32913,6 +33880,11 @@ class LSPServerManager {
|
|
|
32913
33880
|
if (managed && managed.refCount > 0) {
|
|
32914
33881
|
managed.refCount--;
|
|
32915
33882
|
managed.lastUsedAt = Date.now();
|
|
33883
|
+
log("[lsp] releaseClient", {
|
|
33884
|
+
key,
|
|
33885
|
+
server: serverId,
|
|
33886
|
+
refCount: managed.refCount
|
|
33887
|
+
});
|
|
32916
33888
|
}
|
|
32917
33889
|
}
|
|
32918
33890
|
isServerInitializing(root, serverId) {
|
|
@@ -32921,14 +33893,19 @@ class LSPServerManager {
|
|
|
32921
33893
|
return managed?.isInitializing ?? false;
|
|
32922
33894
|
}
|
|
32923
33895
|
async stopAll() {
|
|
32924
|
-
|
|
33896
|
+
log("[lsp] stopAll: shutting down all clients", {
|
|
33897
|
+
count: this.clients.size
|
|
33898
|
+
});
|
|
33899
|
+
for (const [key, managed] of this.clients) {
|
|
32925
33900
|
await managed.client.stop();
|
|
33901
|
+
log("[lsp] stopAll: client stopped", { key });
|
|
32926
33902
|
}
|
|
32927
33903
|
this.clients.clear();
|
|
32928
33904
|
if (this.cleanupInterval) {
|
|
32929
33905
|
clearInterval(this.cleanupInterval);
|
|
32930
33906
|
this.cleanupInterval = null;
|
|
32931
33907
|
}
|
|
33908
|
+
log("[lsp] stopAll: complete");
|
|
32932
33909
|
}
|
|
32933
33910
|
}
|
|
32934
33911
|
var lspManager = LSPServerManager.getInstance();
|
|
@@ -32947,6 +33924,11 @@ class LSPClient {
|
|
|
32947
33924
|
this.server = server;
|
|
32948
33925
|
}
|
|
32949
33926
|
async start() {
|
|
33927
|
+
log("[lsp] LSPClient.start: spawning server", {
|
|
33928
|
+
server: this.server.id,
|
|
33929
|
+
command: this.server.command.join(" "),
|
|
33930
|
+
root: this.root
|
|
33931
|
+
});
|
|
32950
33932
|
this.proc = spawn5(this.server.command, {
|
|
32951
33933
|
stdin: "pipe",
|
|
32952
33934
|
stdout: "pipe",
|
|
@@ -33016,13 +33998,19 @@ class LSPClient {
|
|
|
33016
33998
|
this.processExited = true;
|
|
33017
33999
|
});
|
|
33018
34000
|
this.connection.listen();
|
|
33019
|
-
await new Promise((
|
|
34001
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
33020
34002
|
if (this.proc.exitCode !== null) {
|
|
33021
34003
|
const stderr = this.stderrBuffer.join(`
|
|
33022
34004
|
`);
|
|
34005
|
+
log("[lsp] LSPClient.start: server exited immediately", {
|
|
34006
|
+
server: this.server.id,
|
|
34007
|
+
exitCode: this.proc.exitCode,
|
|
34008
|
+
stderr: stderr.slice(0, 500)
|
|
34009
|
+
});
|
|
33023
34010
|
throw new Error(`LSP server exited immediately with code ${this.proc.exitCode}` + (stderr ? `
|
|
33024
34011
|
stderr: ${stderr}` : ""));
|
|
33025
34012
|
}
|
|
34013
|
+
log("[lsp] LSPClient.start: server spawned", { server: this.server.id });
|
|
33026
34014
|
}
|
|
33027
34015
|
startStderrReading() {
|
|
33028
34016
|
if (!this.proc)
|
|
@@ -33048,6 +34036,10 @@ stderr: ${stderr}` : ""));
|
|
|
33048
34036
|
async initialize() {
|
|
33049
34037
|
if (!this.connection)
|
|
33050
34038
|
throw new Error("LSP connection not established");
|
|
34039
|
+
log("[lsp] LSPClient.initialize: sending initialize request", {
|
|
34040
|
+
server: this.server.id,
|
|
34041
|
+
root: this.root
|
|
34042
|
+
});
|
|
33051
34043
|
const rootUri = pathToFileURL(this.root).href;
|
|
33052
34044
|
await this.connection.sendRequest("initialize", {
|
|
33053
34045
|
processId: process.pid,
|
|
@@ -33079,14 +34071,22 @@ stderr: ${stderr}` : ""));
|
|
|
33079
34071
|
});
|
|
33080
34072
|
this.connection.sendNotification("initialized");
|
|
33081
34073
|
await new Promise((r) => setTimeout(r, 300));
|
|
34074
|
+
log("[lsp] LSPClient.initialize: complete", { server: this.server.id });
|
|
33082
34075
|
}
|
|
33083
34076
|
async openFile(filePath) {
|
|
33084
|
-
const absPath =
|
|
33085
|
-
if (this.openedFiles.has(absPath))
|
|
34077
|
+
const absPath = resolve2(filePath);
|
|
34078
|
+
if (this.openedFiles.has(absPath)) {
|
|
34079
|
+
log("[lsp] openFile: already open, skipping", { filePath: absPath });
|
|
33086
34080
|
return;
|
|
34081
|
+
}
|
|
33087
34082
|
const text = readFileSync4(absPath, "utf-8");
|
|
33088
34083
|
const ext = extname(absPath);
|
|
33089
34084
|
const languageId = getLanguageId(ext);
|
|
34085
|
+
log("[lsp] openFile: opening document", {
|
|
34086
|
+
filePath: absPath,
|
|
34087
|
+
languageId,
|
|
34088
|
+
size: text.length
|
|
34089
|
+
});
|
|
33090
34090
|
this.connection?.sendNotification("textDocument/didOpen", {
|
|
33091
34091
|
textDocument: {
|
|
33092
34092
|
uri: pathToFileURL(absPath).href,
|
|
@@ -33099,7 +34099,7 @@ stderr: ${stderr}` : ""));
|
|
|
33099
34099
|
await new Promise((r) => setTimeout(r, 1000));
|
|
33100
34100
|
}
|
|
33101
34101
|
async definition(filePath, line, character) {
|
|
33102
|
-
const absPath =
|
|
34102
|
+
const absPath = resolve2(filePath);
|
|
33103
34103
|
await this.openFile(absPath);
|
|
33104
34104
|
return this.connection?.sendRequest("textDocument/definition", {
|
|
33105
34105
|
textDocument: { uri: pathToFileURL(absPath).href },
|
|
@@ -33107,7 +34107,7 @@ stderr: ${stderr}` : ""));
|
|
|
33107
34107
|
});
|
|
33108
34108
|
}
|
|
33109
34109
|
async references(filePath, line, character, includeDeclaration = true) {
|
|
33110
|
-
const absPath =
|
|
34110
|
+
const absPath = resolve2(filePath);
|
|
33111
34111
|
await this.openFile(absPath);
|
|
33112
34112
|
return this.connection?.sendRequest("textDocument/references", {
|
|
33113
34113
|
textDocument: { uri: pathToFileURL(absPath).href },
|
|
@@ -33116,7 +34116,7 @@ stderr: ${stderr}` : ""));
|
|
|
33116
34116
|
});
|
|
33117
34117
|
}
|
|
33118
34118
|
async diagnostics(filePath) {
|
|
33119
|
-
const absPath =
|
|
34119
|
+
const absPath = resolve2(filePath);
|
|
33120
34120
|
const uri = pathToFileURL(absPath).href;
|
|
33121
34121
|
await this.openFile(absPath);
|
|
33122
34122
|
await new Promise((r) => setTimeout(r, 500));
|
|
@@ -33131,7 +34131,7 @@ stderr: ${stderr}` : ""));
|
|
|
33131
34131
|
return { items: this.diagnosticsStore.get(uri) ?? [] };
|
|
33132
34132
|
}
|
|
33133
34133
|
async rename(filePath, line, character, newName) {
|
|
33134
|
-
const absPath =
|
|
34134
|
+
const absPath = resolve2(filePath);
|
|
33135
34135
|
await this.openFile(absPath);
|
|
33136
34136
|
return this.connection?.sendRequest("textDocument/rename", {
|
|
33137
34137
|
textDocument: { uri: pathToFileURL(absPath).href },
|
|
@@ -33143,6 +34143,7 @@ stderr: ${stderr}` : ""));
|
|
|
33143
34143
|
return this.proc !== null && !this.processExited && this.proc.exitCode === null;
|
|
33144
34144
|
}
|
|
33145
34145
|
async stop() {
|
|
34146
|
+
log("[lsp] LSPClient.stop: stopping", { server: this.server.id });
|
|
33146
34147
|
try {
|
|
33147
34148
|
if (this.connection) {
|
|
33148
34149
|
await this.connection.sendRequest("shutdown");
|
|
@@ -33155,45 +34156,24 @@ stderr: ${stderr}` : ""));
|
|
|
33155
34156
|
this.connection = null;
|
|
33156
34157
|
this.processExited = true;
|
|
33157
34158
|
this.diagnosticsStore.clear();
|
|
34159
|
+
log("[lsp] LSPClient.stop: complete", { server: this.server.id });
|
|
33158
34160
|
}
|
|
33159
34161
|
}
|
|
33160
34162
|
// src/tools/lsp/utils.ts
|
|
33161
34163
|
import {
|
|
33162
|
-
existsSync as
|
|
34164
|
+
existsSync as existsSync11,
|
|
33163
34165
|
readFileSync as readFileSync5,
|
|
33164
|
-
statSync as
|
|
34166
|
+
statSync as statSync4,
|
|
33165
34167
|
unlinkSync as unlinkSync3,
|
|
33166
34168
|
writeFileSync as writeFileSync3
|
|
33167
34169
|
} from "fs";
|
|
33168
|
-
import { dirname as
|
|
34170
|
+
import { dirname as dirname6, extname as extname2, join as join12, resolve as resolve3 } from "path";
|
|
33169
34171
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
33170
|
-
function
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
if (!statSync3(dir).isDirectory()) {
|
|
33174
|
-
dir = dirname4(dir);
|
|
33175
|
-
}
|
|
33176
|
-
} catch {
|
|
33177
|
-
dir = dirname4(dir);
|
|
33178
|
-
}
|
|
33179
|
-
const markers = [
|
|
33180
|
-
".git",
|
|
33181
|
-
"package.json",
|
|
33182
|
-
"pyproject.toml",
|
|
33183
|
-
"Cargo.toml",
|
|
33184
|
-
"go.mod"
|
|
33185
|
-
];
|
|
33186
|
-
let prevDir = "";
|
|
33187
|
-
while (dir !== prevDir) {
|
|
33188
|
-
for (const marker of markers) {
|
|
33189
|
-
if (existsSync10(join12(dir, marker))) {
|
|
33190
|
-
return dir;
|
|
33191
|
-
}
|
|
33192
|
-
}
|
|
33193
|
-
prevDir = dir;
|
|
33194
|
-
dir = dirname4(dir);
|
|
34172
|
+
function findServerProjectRoot(filePath, server) {
|
|
34173
|
+
if (server.root) {
|
|
34174
|
+
return server.root(filePath) ?? dirname6(resolve3(filePath));
|
|
33195
34175
|
}
|
|
33196
|
-
return
|
|
34176
|
+
return dirname6(resolve3(filePath));
|
|
33197
34177
|
}
|
|
33198
34178
|
function uriToPath(uri) {
|
|
33199
34179
|
return fileURLToPath2(uri);
|
|
@@ -33212,24 +34192,42 @@ function formatServerLookupError(result) {
|
|
|
33212
34192
|
return `No LSP server configured for extension: ${result.extension}`;
|
|
33213
34193
|
}
|
|
33214
34194
|
async function withLspClient(filePath, fn) {
|
|
33215
|
-
const absPath =
|
|
34195
|
+
const absPath = resolve3(filePath);
|
|
33216
34196
|
const ext = extname2(absPath);
|
|
33217
34197
|
const result = findServerForExtension(ext);
|
|
33218
34198
|
if (result.status !== "found") {
|
|
34199
|
+
log("[lsp] withLspClient: server not found", {
|
|
34200
|
+
filePath: absPath,
|
|
34201
|
+
extension: ext
|
|
34202
|
+
});
|
|
33219
34203
|
throw new Error(formatServerLookupError(result));
|
|
33220
34204
|
}
|
|
33221
34205
|
const server = result.server;
|
|
33222
|
-
const root =
|
|
34206
|
+
const root = findServerProjectRoot(absPath, server) ?? dirname6(absPath);
|
|
34207
|
+
log("[lsp] withLspClient: acquiring client", {
|
|
34208
|
+
filePath: absPath,
|
|
34209
|
+
server: server.id,
|
|
34210
|
+
root
|
|
34211
|
+
});
|
|
33223
34212
|
const client = await lspManager.getClient(root, server);
|
|
33224
34213
|
try {
|
|
33225
|
-
|
|
34214
|
+
const result2 = await fn(client);
|
|
34215
|
+
log("[lsp] withLspClient: operation complete", { server: server.id });
|
|
34216
|
+
return result2;
|
|
33226
34217
|
} catch (e) {
|
|
33227
34218
|
if (e instanceof Error && e.message.includes("timeout")) {
|
|
33228
34219
|
const isInitializing = lspManager.isServerInitializing(root, server.id);
|
|
33229
34220
|
if (isInitializing) {
|
|
34221
|
+
log("[lsp] withLspClient: timeout during init", {
|
|
34222
|
+
server: server.id
|
|
34223
|
+
});
|
|
33230
34224
|
throw new Error(`LSP server is still initializing. Please retry in a few seconds.`);
|
|
33231
34225
|
}
|
|
33232
34226
|
}
|
|
34227
|
+
log("[lsp] withLspClient: operation failed", {
|
|
34228
|
+
server: server.id,
|
|
34229
|
+
error: String(e)
|
|
34230
|
+
});
|
|
33233
34231
|
throw e;
|
|
33234
34232
|
} finally {
|
|
33235
34233
|
lspManager.releaseClient(root, server.id);
|
|
@@ -33313,6 +34311,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
33313
34311
|
}
|
|
33314
34312
|
function applyWorkspaceEdit(edit) {
|
|
33315
34313
|
if (!edit) {
|
|
34314
|
+
log("[lsp] applyWorkspaceEdit: no edit provided");
|
|
33316
34315
|
return {
|
|
33317
34316
|
success: false,
|
|
33318
34317
|
filesModified: [],
|
|
@@ -33320,6 +34319,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
33320
34319
|
errors: ["No edit provided"]
|
|
33321
34320
|
};
|
|
33322
34321
|
}
|
|
34322
|
+
const changeCount = (edit.changes ? Object.keys(edit.changes).length : 0) + (edit.documentChanges ? edit.documentChanges.length : 0);
|
|
34323
|
+
log("[lsp] applyWorkspaceEdit: applying", { changeCount });
|
|
33323
34324
|
const result = {
|
|
33324
34325
|
success: true,
|
|
33325
34326
|
filesModified: [],
|
|
@@ -33386,6 +34387,12 @@ function applyWorkspaceEdit(edit) {
|
|
|
33386
34387
|
}
|
|
33387
34388
|
}
|
|
33388
34389
|
}
|
|
34390
|
+
log("[lsp] applyWorkspaceEdit: complete", {
|
|
34391
|
+
success: result.success,
|
|
34392
|
+
filesModified: result.filesModified.length,
|
|
34393
|
+
totalEdits: result.totalEdits,
|
|
34394
|
+
errors: result.errors.length
|
|
34395
|
+
});
|
|
33389
34396
|
return result;
|
|
33390
34397
|
}
|
|
33391
34398
|
function formatApplyResult(result) {
|
|
@@ -33534,6 +34541,28 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33534
34541
|
modelArrayMap[agentDef.name] = agentDef._modelArray;
|
|
33535
34542
|
}
|
|
33536
34543
|
}
|
|
34544
|
+
const runtimeChains = {};
|
|
34545
|
+
for (const agentDef of agentDefs) {
|
|
34546
|
+
if (agentDef._modelArray?.length) {
|
|
34547
|
+
runtimeChains[agentDef.name] = agentDef._modelArray.map((m) => m.id);
|
|
34548
|
+
}
|
|
34549
|
+
}
|
|
34550
|
+
if (config3.fallback?.enabled !== false) {
|
|
34551
|
+
const chains = config3.fallback?.chains ?? {};
|
|
34552
|
+
for (const [agentName, chainModels] of Object.entries(chains)) {
|
|
34553
|
+
if (!chainModels?.length)
|
|
34554
|
+
continue;
|
|
34555
|
+
const existing = runtimeChains[agentName] ?? [];
|
|
34556
|
+
const seen = new Set(existing);
|
|
34557
|
+
for (const m of chainModels) {
|
|
34558
|
+
if (!seen.has(m)) {
|
|
34559
|
+
seen.add(m);
|
|
34560
|
+
existing.push(m);
|
|
34561
|
+
}
|
|
34562
|
+
}
|
|
34563
|
+
runtimeChains[agentName] = existing;
|
|
34564
|
+
}
|
|
34565
|
+
}
|
|
33537
34566
|
const tmuxConfig = {
|
|
33538
34567
|
enabled: config3.tmux?.enabled ?? false,
|
|
33539
34568
|
layout: config3.tmux?.layout ?? "main-vertical",
|
|
@@ -33560,6 +34589,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33560
34589
|
const chatHeadersHook = createChatHeadersHook(ctx);
|
|
33561
34590
|
const delegateTaskRetryHook = createDelegateTaskRetryHook(ctx);
|
|
33562
34591
|
const jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
|
|
34592
|
+
const foregroundFallback = new ForegroundFallbackManager(ctx.client, runtimeChains, config3.fallback?.enabled !== false && Object.keys(runtimeChains).length > 0);
|
|
33563
34593
|
return {
|
|
33564
34594
|
name: "oh-my-opencode-slim",
|
|
33565
34595
|
agent: agents,
|
|
@@ -33575,7 +34605,11 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33575
34605
|
},
|
|
33576
34606
|
mcp: mcps,
|
|
33577
34607
|
config: async (opencodeConfig) => {
|
|
33578
|
-
|
|
34608
|
+
const lspConfig = opencodeConfig.lsp;
|
|
34609
|
+
setUserLspConfig(lspConfig);
|
|
34610
|
+
if (config3.setDefaultAgent !== false && !opencodeConfig.default_agent) {
|
|
34611
|
+
opencodeConfig.default_agent = "orchestrator";
|
|
34612
|
+
}
|
|
33579
34613
|
if (!opencodeConfig.agent) {
|
|
33580
34614
|
opencodeConfig.agent = { ...agents };
|
|
33581
34615
|
} else {
|
|
@@ -33594,36 +34628,73 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33594
34628
|
}
|
|
33595
34629
|
}
|
|
33596
34630
|
const configAgent = opencodeConfig.agent;
|
|
33597
|
-
|
|
34631
|
+
const fallbackChainsEnabled = config3.fallback?.enabled !== false;
|
|
34632
|
+
const fallbackChains = fallbackChainsEnabled ? config3.fallback?.chains ?? {} : {};
|
|
34633
|
+
const effectiveArrays = {};
|
|
34634
|
+
for (const [agentName, models] of Object.entries(modelArrayMap)) {
|
|
34635
|
+
effectiveArrays[agentName] = [...models];
|
|
34636
|
+
}
|
|
34637
|
+
for (const [agentName, chainModels] of Object.entries(fallbackChains)) {
|
|
34638
|
+
if (!chainModels || chainModels.length === 0)
|
|
34639
|
+
continue;
|
|
34640
|
+
if (!effectiveArrays[agentName]) {
|
|
34641
|
+
const entry = configAgent[agentName];
|
|
34642
|
+
const currentModel = typeof entry?.model === "string" ? entry.model : undefined;
|
|
34643
|
+
effectiveArrays[agentName] = currentModel ? [{ id: currentModel }] : [];
|
|
34644
|
+
}
|
|
34645
|
+
const seen = new Set(effectiveArrays[agentName].map((m) => m.id));
|
|
34646
|
+
for (const chainModel of chainModels) {
|
|
34647
|
+
if (!seen.has(chainModel)) {
|
|
34648
|
+
seen.add(chainModel);
|
|
34649
|
+
effectiveArrays[agentName].push({ id: chainModel });
|
|
34650
|
+
}
|
|
34651
|
+
}
|
|
34652
|
+
}
|
|
34653
|
+
if (Object.keys(effectiveArrays).length > 0) {
|
|
33598
34654
|
const providerConfig = opencodeConfig.provider ?? {};
|
|
33599
|
-
const
|
|
33600
|
-
for (const [agentName, modelArray] of Object.entries(
|
|
34655
|
+
const hasProviderConfig = Object.keys(providerConfig).length > 0;
|
|
34656
|
+
for (const [agentName, modelArray] of Object.entries(effectiveArrays)) {
|
|
34657
|
+
if (modelArray.length === 0)
|
|
34658
|
+
continue;
|
|
33601
34659
|
let resolved = false;
|
|
33602
|
-
|
|
33603
|
-
const
|
|
33604
|
-
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
|
|
33608
|
-
const
|
|
33609
|
-
if (
|
|
33610
|
-
entry
|
|
33611
|
-
if (
|
|
33612
|
-
entry.
|
|
34660
|
+
if (hasProviderConfig) {
|
|
34661
|
+
const configuredProviders = Object.keys(providerConfig);
|
|
34662
|
+
for (const modelEntry of modelArray) {
|
|
34663
|
+
const slashIdx = modelEntry.id.indexOf("/");
|
|
34664
|
+
if (slashIdx === -1)
|
|
34665
|
+
continue;
|
|
34666
|
+
const providerID = modelEntry.id.slice(0, slashIdx);
|
|
34667
|
+
if (configuredProviders.includes(providerID)) {
|
|
34668
|
+
const entry = configAgent[agentName];
|
|
34669
|
+
if (entry) {
|
|
34670
|
+
entry.model = modelEntry.id;
|
|
34671
|
+
if (modelEntry.variant) {
|
|
34672
|
+
entry.variant = modelEntry.variant;
|
|
34673
|
+
}
|
|
33613
34674
|
}
|
|
34675
|
+
log("[plugin] resolved model fallback", {
|
|
34676
|
+
agent: agentName,
|
|
34677
|
+
model: modelEntry.id,
|
|
34678
|
+
variant: modelEntry.variant
|
|
34679
|
+
});
|
|
34680
|
+
resolved = true;
|
|
34681
|
+
break;
|
|
33614
34682
|
}
|
|
33615
|
-
log("[plugin] resolved model fallback", {
|
|
33616
|
-
agent: agentName,
|
|
33617
|
-
model: modelEntry.id,
|
|
33618
|
-
variant: modelEntry.variant
|
|
33619
|
-
});
|
|
33620
|
-
resolved = true;
|
|
33621
|
-
break;
|
|
33622
34683
|
}
|
|
33623
34684
|
}
|
|
33624
34685
|
if (!resolved) {
|
|
33625
|
-
|
|
33626
|
-
|
|
34686
|
+
const firstModel = modelArray[0];
|
|
34687
|
+
const entry = configAgent[agentName];
|
|
34688
|
+
if (entry) {
|
|
34689
|
+
entry.model = firstModel.id;
|
|
34690
|
+
if (firstModel.variant) {
|
|
34691
|
+
entry.variant = firstModel.variant;
|
|
34692
|
+
}
|
|
34693
|
+
}
|
|
34694
|
+
log("[plugin] resolved model from array (no provider config)", {
|
|
34695
|
+
agent: agentName,
|
|
34696
|
+
model: firstModel.id,
|
|
34697
|
+
variant: firstModel.variant
|
|
33627
34698
|
});
|
|
33628
34699
|
}
|
|
33629
34700
|
}
|
|
@@ -33657,6 +34728,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33657
34728
|
}
|
|
33658
34729
|
},
|
|
33659
34730
|
event: async (input) => {
|
|
34731
|
+
await foregroundFallback.handleEvent(input.event);
|
|
33660
34732
|
await autoUpdateChecker.event(input);
|
|
33661
34733
|
await tmuxSessionManager.onSessionCreated(input.event);
|
|
33662
34734
|
await backgroundManager.handleSessionStatus(input.event);
|