lazycodex-ai 5.0.0-beta.49 → 5.0.0-beta.50
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/cli/index.js +4682 -14969
- package/dist/cli-node/index.js +4727 -15014
- package/package.json +1 -1
- package/packages/lsp-daemon/dist/cli.js +181 -181
- package/packages/lsp-daemon/dist/client.js +232 -232
- package/packages/lsp-daemon/dist/index.js +132 -132
- package/packages/lsp-tools-mcp/dist/cli.js +103 -101
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +14 -14
- package/packages/lsp-tools-mcp/dist/mcp.js +129 -129
- package/packages/lsp-tools-mcp/dist/tools.js +47 -47
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/README.md +4 -0
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +151 -102
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/directive.md +2 -0
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +35 -24
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +31 -21
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/types.d.ts +3 -0
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +31 -23
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/types.ts +3 -0
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/cli.test.ts +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +133 -7
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-doctor/SKILL.md +3 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +298 -298
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/README.md +2 -0
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-6.md +123 -0
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +38 -30
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +6 -0
- package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +38 -2
- package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +24 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +66 -357
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +66 -358
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/README.md +2 -0
- package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-low.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +63 -63
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/model-catalog.json +16 -7
- package/packages/omo-codex/plugin/package-lock.json +16 -16
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +16 -7
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +8 -4
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +18 -90
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +1 -1
- package/packages/omo-codex/plugin/shared/package.json +1 -1
- package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +3 -1
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +12 -12
- package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +14 -4
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +4 -4
- package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +25 -1
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +35 -35
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +107 -15
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +318 -569
|
@@ -687,7 +687,7 @@ class JsonRpcConnection {
|
|
|
687
687
|
let settled = false;
|
|
688
688
|
const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
|
|
689
689
|
let rejectAfterCancelWrite;
|
|
690
|
-
const responsePromise = new Promise((
|
|
690
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
691
691
|
const cleanup = () => {
|
|
692
692
|
options.signal?.removeEventListener("abort", onAbort);
|
|
693
693
|
};
|
|
@@ -717,7 +717,7 @@ class JsonRpcConnection {
|
|
|
717
717
|
resolve(result) {
|
|
718
718
|
settled = true;
|
|
719
719
|
cleanup();
|
|
720
|
-
|
|
720
|
+
resolve(result);
|
|
721
721
|
},
|
|
722
722
|
reject(error) {
|
|
723
723
|
settled = true;
|
|
@@ -876,13 +876,13 @@ class JsonRpcConnection {
|
|
|
876
876
|
const payload = `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
877
877
|
\r
|
|
878
878
|
${body}`;
|
|
879
|
-
return new Promise((
|
|
879
|
+
return new Promise((resolve, reject) => {
|
|
880
880
|
this.writer.write(payload, (error) => {
|
|
881
881
|
if (error) {
|
|
882
882
|
reject(error);
|
|
883
883
|
return;
|
|
884
884
|
}
|
|
885
|
-
|
|
885
|
+
resolve();
|
|
886
886
|
});
|
|
887
887
|
});
|
|
888
888
|
}
|
|
@@ -969,9 +969,9 @@ function validateCwd(cwd) {
|
|
|
969
969
|
}
|
|
970
970
|
}
|
|
971
971
|
function wrap(proc) {
|
|
972
|
-
const exitedPromise = new Promise((
|
|
973
|
-
proc.once("close", (code) =>
|
|
974
|
-
proc.once("error", () =>
|
|
972
|
+
const exitedPromise = new Promise((resolve) => {
|
|
973
|
+
proc.once("close", (code) => resolve(code ?? 0));
|
|
974
|
+
proc.once("error", () => resolve(1));
|
|
975
975
|
});
|
|
976
976
|
if (!proc.stdin || !proc.stdout || !proc.stderr) {
|
|
977
977
|
throw new LspProcessSpawnError("Spawned process is missing one of stdin/stdout/stderr pipes");
|
|
@@ -1133,9 +1133,9 @@ class LspClientNotStartedError extends Error {
|
|
|
1133
1133
|
}
|
|
1134
1134
|
|
|
1135
1135
|
class LspClientTransport {
|
|
1136
|
-
constructor(root,
|
|
1136
|
+
constructor(root, server, timeouts = {}) {
|
|
1137
1137
|
this.root = root;
|
|
1138
|
-
this.server =
|
|
1138
|
+
this.server = server;
|
|
1139
1139
|
this.proc = null;
|
|
1140
1140
|
this.connection = null;
|
|
1141
1141
|
this.stderrBuffer = [];
|
|
@@ -1317,8 +1317,8 @@ class LspClientTransport {
|
|
|
1317
1317
|
try {
|
|
1318
1318
|
proc.kill();
|
|
1319
1319
|
let timeoutId;
|
|
1320
|
-
const timeoutPromise = new Promise((
|
|
1321
|
-
timeoutId = setTimeout(
|
|
1320
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
1321
|
+
timeoutId = setTimeout(resolve, STOP_HARD_KILL_TIMEOUT_MS);
|
|
1322
1322
|
});
|
|
1323
1323
|
await Promise.race([
|
|
1324
1324
|
proc.exited.then(() => {
|
|
@@ -1334,7 +1334,7 @@ class LspClientTransport {
|
|
|
1334
1334
|
proc.kill("SIGKILL");
|
|
1335
1335
|
await Promise.race([
|
|
1336
1336
|
proc.exited,
|
|
1337
|
-
new Promise((
|
|
1337
|
+
new Promise((resolve) => setTimeout(resolve, STOP_SIGKILL_GRACE_MS))
|
|
1338
1338
|
]);
|
|
1339
1339
|
} catch (error) {
|
|
1340
1340
|
reportBestEffortCleanupError("hard process kill", error instanceof Error ? error : String(error));
|
|
@@ -2328,14 +2328,14 @@ function parseSinglePathResource(input, kind) {
|
|
|
2328
2328
|
return;
|
|
2329
2329
|
}
|
|
2330
2330
|
if (kind === "create") {
|
|
2331
|
-
const
|
|
2331
|
+
const options = parseOptions(change["options"], ["overwrite", "ignoreIfExists"], changeIndex);
|
|
2332
2332
|
target.operations.push({
|
|
2333
2333
|
kind,
|
|
2334
2334
|
changeIndex,
|
|
2335
2335
|
path: resolvedPath.path,
|
|
2336
2336
|
reportedPath: resolvedPath.requestedPath,
|
|
2337
|
-
overwrite:
|
|
2338
|
-
ignoreIfExists:
|
|
2337
|
+
overwrite: options["overwrite"] ?? false,
|
|
2338
|
+
ignoreIfExists: options["ignoreIfExists"] ?? false,
|
|
2339
2339
|
followedSymbolicLink: resolvedPath.followedSymbolicLink
|
|
2340
2340
|
});
|
|
2341
2341
|
return;
|
|
@@ -2805,11 +2805,11 @@ class PlanPathIndex {
|
|
|
2805
2805
|
}
|
|
2806
2806
|
}
|
|
2807
2807
|
}
|
|
2808
|
-
add(path,
|
|
2808
|
+
add(path, reportedPath, changeIndex) {
|
|
2809
2809
|
if (!this.firstChangeByPath.has(path))
|
|
2810
2810
|
this.firstChangeByPath.set(path, changeIndex);
|
|
2811
2811
|
if (!this.reportedPathByCanonical.has(path))
|
|
2812
|
-
this.reportedPathByCanonical.set(path,
|
|
2812
|
+
this.reportedPathByCanonical.set(path, reportedPath);
|
|
2813
2813
|
}
|
|
2814
2814
|
}
|
|
2815
2815
|
function fingerprintWorkspaceEdit(edit, workspaceRoot) {
|
|
@@ -2921,15 +2921,15 @@ class WorkspaceMutationController {
|
|
|
2921
2921
|
};
|
|
2922
2922
|
}
|
|
2923
2923
|
lease.phase = "applying";
|
|
2924
|
-
lease.applyCompletion = new Promise((
|
|
2925
|
-
lease.resolveApply =
|
|
2924
|
+
lease.applyCompletion = new Promise((resolve) => {
|
|
2925
|
+
lease.resolveApply = resolve;
|
|
2926
2926
|
});
|
|
2927
2927
|
const edit = isRecord4(params) ? params["edit"] : undefined;
|
|
2928
|
-
const
|
|
2929
|
-
lease.serverApply =
|
|
2928
|
+
const record = edit === undefined ? { fingerprint: null, result: failure("workspace/applyEdit params.edit is required", 0) } : await this.applyEdit(edit, lease);
|
|
2929
|
+
lease.serverApply = record;
|
|
2930
2930
|
lease.phase = "settled";
|
|
2931
2931
|
lease.resolveApply?.();
|
|
2932
|
-
return responseFor(
|
|
2932
|
+
return responseFor(record.result);
|
|
2933
2933
|
}
|
|
2934
2934
|
async reconcileRename(leaseToken, edit) {
|
|
2935
2935
|
const lease = this.requireActiveLease(leaseToken);
|
|
@@ -2945,16 +2945,16 @@ class WorkspaceMutationController {
|
|
|
2945
2945
|
const applied = await this.applyEdit(edit, lease);
|
|
2946
2946
|
return { edit, apply: applied.result };
|
|
2947
2947
|
}
|
|
2948
|
-
reconcileServerApply(
|
|
2948
|
+
reconcileServerApply(record, edit) {
|
|
2949
2949
|
if (!edit)
|
|
2950
|
-
return { edit, apply:
|
|
2950
|
+
return { edit, apply: record.result };
|
|
2951
2951
|
const fingerprint = fingerprintWorkspaceEdit(edit, this.workspaceRoot);
|
|
2952
|
-
if (fingerprint.success &&
|
|
2953
|
-
return { edit, apply:
|
|
2952
|
+
if (fingerprint.success && record.fingerprint !== null && fingerprint.fingerprint === record.fingerprint) {
|
|
2953
|
+
return { edit, apply: record.result };
|
|
2954
2954
|
}
|
|
2955
2955
|
return {
|
|
2956
2956
|
edit,
|
|
2957
|
-
apply: failure("rename result conflicts with server-applied workspace edit", 0,
|
|
2957
|
+
apply: failure("rename result conflicts with server-applied workspace edit", 0, record.result)
|
|
2958
2958
|
};
|
|
2959
2959
|
}
|
|
2960
2960
|
async applyEdit(edit, lease) {
|
|
@@ -2995,8 +2995,8 @@ var DIAGNOSTICS_FRESHNESS_TIMEOUT_MS = 3000;
|
|
|
2995
2995
|
var VERSIONLESS_PUBLISH_QUIESCENCE_MS = 250;
|
|
2996
2996
|
|
|
2997
2997
|
class LspClient extends LspClientConnection {
|
|
2998
|
-
constructor(root,
|
|
2999
|
-
super(root,
|
|
2998
|
+
constructor(root, server, options = {}) {
|
|
2999
|
+
super(root, server, options);
|
|
3000
3000
|
this.diagnosticPullErrors = [];
|
|
3001
3001
|
this.diagnosticsFreshnessTimeoutMs = options.diagnosticsFreshnessTimeoutMs ?? DIAGNOSTICS_FRESHNESS_TIMEOUT_MS;
|
|
3002
3002
|
this.documents = new WorkspaceDocumentState((method, params) => this.sendNotification(method, params), (uri) => this.diagnosticsStore.delete(uri), {
|
|
@@ -3117,13 +3117,13 @@ class LspClient extends LspClientConnection {
|
|
|
3117
3117
|
if (!pushFallbackOnly) {
|
|
3118
3118
|
const cached = this.documents.getPullCache(snapshot);
|
|
3119
3119
|
try {
|
|
3120
|
-
const
|
|
3121
|
-
if (
|
|
3120
|
+
const remainingMs = deadlineAt - this.timerProvider.now();
|
|
3121
|
+
if (remainingMs <= 0)
|
|
3122
3122
|
return this.freshnessTimeout(absPath);
|
|
3123
3123
|
const result = await this.sendRequest("textDocument/diagnostic", {
|
|
3124
3124
|
textDocument: { uri },
|
|
3125
3125
|
...cached?.resultId === undefined ? {} : { previousResultId: cached.resultId }
|
|
3126
|
-
}, { timeoutMs:
|
|
3126
|
+
}, { timeoutMs: remainingMs, ...signal === undefined ? {} : { signal } });
|
|
3127
3127
|
if (!this.documents.isCurrentSnapshot(snapshot))
|
|
3128
3128
|
continue;
|
|
3129
3129
|
const report = this.parseDiagnosticPullReport(result);
|
|
@@ -3204,7 +3204,7 @@ function waitForDiagnosticsActivity(wait, signal) {
|
|
|
3204
3204
|
return wait;
|
|
3205
3205
|
if (signal.aborted)
|
|
3206
3206
|
return Promise.reject(abortError2(signal));
|
|
3207
|
-
return new Promise((
|
|
3207
|
+
return new Promise((resolve, reject) => {
|
|
3208
3208
|
const onAbort = () => {
|
|
3209
3209
|
signal.removeEventListener("abort", onAbort);
|
|
3210
3210
|
reject(abortError2(signal));
|
|
@@ -3212,7 +3212,7 @@ function waitForDiagnosticsActivity(wait, signal) {
|
|
|
3212
3212
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
3213
3213
|
wait.then(() => {
|
|
3214
3214
|
signal.removeEventListener("abort", onAbort);
|
|
3215
|
-
|
|
3215
|
+
resolve();
|
|
3216
3216
|
}, (error) => {
|
|
3217
3217
|
signal.removeEventListener("abort", onAbort);
|
|
3218
3218
|
reject(error);
|
|
@@ -3280,7 +3280,7 @@ async function stopClientBestEffort(client) {
|
|
|
3280
3280
|
function awaitWithSignal(promise, signal) {
|
|
3281
3281
|
if (!signal)
|
|
3282
3282
|
return promise;
|
|
3283
|
-
return new Promise((
|
|
3283
|
+
return new Promise((resolve, reject) => {
|
|
3284
3284
|
let settled = false;
|
|
3285
3285
|
const onAbort = () => {
|
|
3286
3286
|
if (settled)
|
|
@@ -3298,7 +3298,7 @@ function awaitWithSignal(promise, signal) {
|
|
|
3298
3298
|
return;
|
|
3299
3299
|
settled = true;
|
|
3300
3300
|
signal.removeEventListener("abort", onAbort);
|
|
3301
|
-
|
|
3301
|
+
resolve(value);
|
|
3302
3302
|
}, (err) => {
|
|
3303
3303
|
if (settled)
|
|
3304
3304
|
return;
|
|
@@ -3321,7 +3321,7 @@ class LspManager {
|
|
|
3321
3321
|
this.initTimeoutMs = options.initTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
3322
3322
|
this.maxResidentClients = options.maxResidentClients ?? MAX_RESIDENT_CLIENTS;
|
|
3323
3323
|
this.reaperIntervalMs = options.reaperIntervalMs ?? REAPER_INTERVAL_MS;
|
|
3324
|
-
this.clientFactory = options.clientFactory ?? ((root,
|
|
3324
|
+
this.clientFactory = options.clientFactory ?? ((root, server) => new LspClient(root, server));
|
|
3325
3325
|
this.now = options.now ?? (() => Date.now());
|
|
3326
3326
|
this.startReaper();
|
|
3327
3327
|
this.signalDisposer = installProcessSignalCleanup(() => this.stopAll());
|
|
@@ -3414,12 +3414,12 @@ class LspManager {
|
|
|
3414
3414
|
await this.tombstoneStop(key, managed.client);
|
|
3415
3415
|
}
|
|
3416
3416
|
}
|
|
3417
|
-
async getClient(root,
|
|
3417
|
+
async getClient(root, server, signal) {
|
|
3418
3418
|
if (this.disposed) {
|
|
3419
3419
|
throw new Error("LspManager has been disposed");
|
|
3420
3420
|
}
|
|
3421
3421
|
signal?.throwIfAborted();
|
|
3422
|
-
const key = this.getKey(root,
|
|
3422
|
+
const key = this.getKey(root, server.id);
|
|
3423
3423
|
for (;; ) {
|
|
3424
3424
|
const pendingStop = this.pendingStops.get(key);
|
|
3425
3425
|
if (pendingStop === undefined)
|
|
@@ -3461,7 +3461,7 @@ class LspManager {
|
|
|
3461
3461
|
this.clients.delete(key);
|
|
3462
3462
|
}
|
|
3463
3463
|
await this.tombstoneStop(key, managed.client);
|
|
3464
|
-
return this.getClient(root,
|
|
3464
|
+
return this.getClient(root, server, signal);
|
|
3465
3465
|
}
|
|
3466
3466
|
this.markHealthyGeneration(key);
|
|
3467
3467
|
managed.refCount++;
|
|
@@ -3469,8 +3469,8 @@ class LspManager {
|
|
|
3469
3469
|
return managed.client;
|
|
3470
3470
|
}
|
|
3471
3471
|
this.evictForAdmission();
|
|
3472
|
-
this.ensureRespawnAllowed(key, root,
|
|
3473
|
-
const client = this.clientFactory(root,
|
|
3472
|
+
this.ensureRespawnAllowed(key, root, server.id);
|
|
3473
|
+
const client = this.clientFactory(root, server);
|
|
3474
3474
|
const initStartedAt = this.now();
|
|
3475
3475
|
const initPromise = (async () => {
|
|
3476
3476
|
await client.start();
|
|
@@ -3510,7 +3510,7 @@ class LspManager {
|
|
|
3510
3510
|
this.clients.delete(key);
|
|
3511
3511
|
}
|
|
3512
3512
|
await this.tombstoneStop(key, client);
|
|
3513
|
-
return this.getClient(root,
|
|
3513
|
+
return this.getClient(root, server, signal);
|
|
3514
3514
|
}
|
|
3515
3515
|
this.markHealthyGeneration(key);
|
|
3516
3516
|
newManaged.refCount++;
|
|
@@ -3535,19 +3535,19 @@ class LspManager {
|
|
|
3535
3535
|
this.clients.delete(key);
|
|
3536
3536
|
this.tombstoneStop(key, managed.client);
|
|
3537
3537
|
}
|
|
3538
|
-
warmupClient(root,
|
|
3538
|
+
warmupClient(root, server) {
|
|
3539
3539
|
if (this.disposed)
|
|
3540
3540
|
return;
|
|
3541
|
-
const key = this.getKey(root,
|
|
3541
|
+
const key = this.getKey(root, server.id);
|
|
3542
3542
|
if (this.clients.has(key) || this.pendingStops.has(key))
|
|
3543
3543
|
return;
|
|
3544
3544
|
this.evictForAdmission();
|
|
3545
3545
|
try {
|
|
3546
|
-
this.ensureRespawnAllowed(key, root,
|
|
3546
|
+
this.ensureRespawnAllowed(key, root, server.id);
|
|
3547
3547
|
} catch {
|
|
3548
3548
|
return;
|
|
3549
3549
|
}
|
|
3550
|
-
const client = this.clientFactory(root,
|
|
3550
|
+
const client = this.clientFactory(root, server);
|
|
3551
3551
|
const initStartedAt = this.now();
|
|
3552
3552
|
const initPromise = (async () => {
|
|
3553
3553
|
await client.start();
|
|
@@ -3955,10 +3955,10 @@ function getMergedServers() {
|
|
|
3955
3955
|
}
|
|
3956
3956
|
if (seen.has(id))
|
|
3957
3957
|
continue;
|
|
3958
|
-
const
|
|
3959
|
-
if (!
|
|
3958
|
+
const server = createServerFromEntry(id, entry, source);
|
|
3959
|
+
if (!server)
|
|
3960
3960
|
continue;
|
|
3961
|
-
servers.push(
|
|
3961
|
+
servers.push(server);
|
|
3962
3962
|
seen.add(id);
|
|
3963
3963
|
}
|
|
3964
3964
|
}
|
|
@@ -3990,7 +3990,7 @@ function createServerFromEntry(id, entry, source) {
|
|
|
3990
3990
|
if (source === "project") {
|
|
3991
3991
|
if (!builtin)
|
|
3992
3992
|
return null;
|
|
3993
|
-
const
|
|
3993
|
+
const server = createServer({
|
|
3994
3994
|
id,
|
|
3995
3995
|
command: builtin.command,
|
|
3996
3996
|
extensions: entry.extensions ?? builtin.extensions,
|
|
@@ -3998,35 +3998,35 @@ function createServerFromEntry(id, entry, source) {
|
|
|
3998
3998
|
source
|
|
3999
3999
|
});
|
|
4000
4000
|
if (entry.initialization !== undefined) {
|
|
4001
|
-
|
|
4001
|
+
server.initialization = entry.initialization;
|
|
4002
4002
|
}
|
|
4003
|
-
return
|
|
4003
|
+
return server;
|
|
4004
4004
|
}
|
|
4005
4005
|
if (entry.command && entry.extensions) {
|
|
4006
|
-
const
|
|
4006
|
+
const server = createServer({
|
|
4007
4007
|
id,
|
|
4008
4008
|
command: entry.command,
|
|
4009
4009
|
extensions: entry.extensions,
|
|
4010
4010
|
priority: entry.priority ?? 0,
|
|
4011
4011
|
source
|
|
4012
4012
|
});
|
|
4013
|
-
applyOptionalServerFields(
|
|
4014
|
-
return
|
|
4013
|
+
applyOptionalServerFields(server, entry);
|
|
4014
|
+
return server;
|
|
4015
4015
|
}
|
|
4016
4016
|
if (!builtin)
|
|
4017
4017
|
return null;
|
|
4018
|
-
const
|
|
4018
|
+
const server = createServer({
|
|
4019
4019
|
id,
|
|
4020
4020
|
command: entry.command ?? builtin.command,
|
|
4021
4021
|
extensions: entry.extensions ?? builtin.extensions,
|
|
4022
4022
|
priority: entry.priority ?? 0,
|
|
4023
4023
|
source
|
|
4024
4024
|
});
|
|
4025
|
-
applyOptionalServerFields(
|
|
4026
|
-
return
|
|
4025
|
+
applyOptionalServerFields(server, entry);
|
|
4026
|
+
return server;
|
|
4027
4027
|
}
|
|
4028
4028
|
function createServer(input) {
|
|
4029
|
-
const
|
|
4029
|
+
const server = {
|
|
4030
4030
|
id: input.id,
|
|
4031
4031
|
command: input.command,
|
|
4032
4032
|
extensions: input.extensions,
|
|
@@ -4034,19 +4034,19 @@ function createServer(input) {
|
|
|
4034
4034
|
source: input.source
|
|
4035
4035
|
};
|
|
4036
4036
|
if (input.env !== undefined) {
|
|
4037
|
-
|
|
4037
|
+
server.env = input.env;
|
|
4038
4038
|
}
|
|
4039
4039
|
if (input.initialization !== undefined) {
|
|
4040
|
-
|
|
4040
|
+
server.initialization = input.initialization;
|
|
4041
4041
|
}
|
|
4042
|
-
return
|
|
4042
|
+
return server;
|
|
4043
4043
|
}
|
|
4044
|
-
function applyOptionalServerFields(
|
|
4044
|
+
function applyOptionalServerFields(server, entry) {
|
|
4045
4045
|
if (entry.env !== undefined) {
|
|
4046
|
-
|
|
4046
|
+
server.env = entry.env;
|
|
4047
4047
|
}
|
|
4048
4048
|
if (entry.initialization !== undefined) {
|
|
4049
|
-
|
|
4049
|
+
server.initialization = entry.initialization;
|
|
4050
4050
|
}
|
|
4051
4051
|
}
|
|
4052
4052
|
function isConfigJson(value) {
|
|
@@ -4237,24 +4237,24 @@ function withResolvedCommand(command, binaryPath) {
|
|
|
4237
4237
|
function findServerForExtension(ext) {
|
|
4238
4238
|
const servers = getMergedServers();
|
|
4239
4239
|
const workingDirectory = contextCwd();
|
|
4240
|
-
for (const
|
|
4241
|
-
if (!
|
|
4240
|
+
for (const server of servers) {
|
|
4241
|
+
if (!server.extensions.includes(ext))
|
|
4242
4242
|
continue;
|
|
4243
|
-
const binaryPath = resolveServerBinary(
|
|
4243
|
+
const binaryPath = resolveServerBinary(server.command, workingDirectory);
|
|
4244
4244
|
if (binaryPath !== null) {
|
|
4245
4245
|
const resolvedServer = {
|
|
4246
|
-
id:
|
|
4247
|
-
command: withResolvedCommand(
|
|
4248
|
-
extensions:
|
|
4249
|
-
priority:
|
|
4246
|
+
id: server.id,
|
|
4247
|
+
command: withResolvedCommand(server.command, binaryPath),
|
|
4248
|
+
extensions: server.extensions,
|
|
4249
|
+
priority: server.priority
|
|
4250
4250
|
};
|
|
4251
|
-
if (
|
|
4251
|
+
if (server.env !== undefined) {
|
|
4252
4252
|
return {
|
|
4253
4253
|
status: "found",
|
|
4254
4254
|
server: {
|
|
4255
4255
|
...resolvedServer,
|
|
4256
|
-
env:
|
|
4257
|
-
...
|
|
4256
|
+
env: server.env,
|
|
4257
|
+
...server.initialization === undefined ? {} : { initialization: server.initialization }
|
|
4258
4258
|
}
|
|
4259
4259
|
};
|
|
4260
4260
|
}
|
|
@@ -4262,20 +4262,20 @@ function findServerForExtension(ext) {
|
|
|
4262
4262
|
status: "found",
|
|
4263
4263
|
server: {
|
|
4264
4264
|
...resolvedServer,
|
|
4265
|
-
...
|
|
4265
|
+
...server.initialization === undefined ? {} : { initialization: server.initialization }
|
|
4266
4266
|
}
|
|
4267
4267
|
};
|
|
4268
4268
|
}
|
|
4269
4269
|
}
|
|
4270
|
-
for (const
|
|
4271
|
-
if (
|
|
4272
|
-
const installHint = LSP_INSTALL_HINTS[
|
|
4270
|
+
for (const server of servers) {
|
|
4271
|
+
if (server.extensions.includes(ext)) {
|
|
4272
|
+
const installHint = LSP_INSTALL_HINTS[server.id] ?? `Install '${server.command[0]}' and ensure it's in your PATH`;
|
|
4273
4273
|
return {
|
|
4274
4274
|
status: "not_installed",
|
|
4275
4275
|
server: {
|
|
4276
|
-
id:
|
|
4277
|
-
command:
|
|
4278
|
-
extensions:
|
|
4276
|
+
id: server.id,
|
|
4277
|
+
command: server.command,
|
|
4278
|
+
extensions: server.extensions
|
|
4279
4279
|
},
|
|
4280
4280
|
installHint
|
|
4281
4281
|
};
|
|
@@ -4294,18 +4294,18 @@ function getAllServers() {
|
|
|
4294
4294
|
const workingDirectory = contextCwd();
|
|
4295
4295
|
const result = [];
|
|
4296
4296
|
const seen = new Set;
|
|
4297
|
-
for (const
|
|
4298
|
-
if (seen.has(
|
|
4297
|
+
for (const server of servers) {
|
|
4298
|
+
if (seen.has(server.id))
|
|
4299
4299
|
continue;
|
|
4300
4300
|
result.push({
|
|
4301
|
-
id:
|
|
4302
|
-
installed: isServerInstalled(
|
|
4303
|
-
extensions:
|
|
4301
|
+
id: server.id,
|
|
4302
|
+
installed: isServerInstalled(server.command, workingDirectory),
|
|
4303
|
+
extensions: server.extensions,
|
|
4304
4304
|
disabled: false,
|
|
4305
|
-
source:
|
|
4306
|
-
priority:
|
|
4305
|
+
source: server.source,
|
|
4306
|
+
priority: server.priority
|
|
4307
4307
|
});
|
|
4308
|
-
seen.add(
|
|
4308
|
+
seen.add(server.id);
|
|
4309
4309
|
}
|
|
4310
4310
|
for (const id of disabled) {
|
|
4311
4311
|
if (seen.has(id))
|
|
@@ -4453,20 +4453,20 @@ function formatInstallOptions(serverId, installHint) {
|
|
|
4453
4453
|
];
|
|
4454
4454
|
}
|
|
4455
4455
|
function formatNotInstalled(result) {
|
|
4456
|
-
const { server
|
|
4457
|
-
const extensions =
|
|
4458
|
-
const decision = loadInstallDecision(
|
|
4456
|
+
const { server, installHint } = result;
|
|
4457
|
+
const extensions = server.extensions.join(", ");
|
|
4458
|
+
const decision = loadInstallDecision(server.id)?.decision;
|
|
4459
4459
|
const context = lspRequestContext();
|
|
4460
4460
|
if (decision === "declined") {
|
|
4461
|
-
return `LSP server '${
|
|
4461
|
+
return `LSP server '${server.id}' (${extensions}) is NOT INSTALLED; user previously declined installation — proceed without LSP.`;
|
|
4462
4462
|
}
|
|
4463
4463
|
const header = [
|
|
4464
|
-
`LSP server '${
|
|
4464
|
+
`LSP server '${server.id}' for ${extensions} is NOT INSTALLED.`,
|
|
4465
4465
|
"",
|
|
4466
|
-
`Command not found: ${
|
|
4466
|
+
`Command not found: ${server.command[0]}`,
|
|
4467
4467
|
""
|
|
4468
4468
|
];
|
|
4469
|
-
const installOptions = formatInstallOptions(
|
|
4469
|
+
const installOptions = formatInstallOptions(server.id, installHint);
|
|
4470
4470
|
if (decision === "allowed") {
|
|
4471
4471
|
return [
|
|
4472
4472
|
...header,
|
|
@@ -4492,7 +4492,7 @@ function formatNotInstalled(result) {
|
|
|
4492
4492
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4493
4493
|
"- If the user agrees: run the install command above, then retry this tool.",
|
|
4494
4494
|
"- If the user declines, OR has NOT explicitly asked for LSP installation:",
|
|
4495
|
-
` call lsp_install_decision { server_id: "${
|
|
4495
|
+
` call lsp_install_decision { server_id: "${server.id}", decision: "declined" },`,
|
|
4496
4496
|
` which writes to ${context.installDecisionsPath},`,
|
|
4497
4497
|
" then ignore this message and proceed WITHOUT LSP."
|
|
4498
4498
|
].join(`
|
|
@@ -4516,26 +4516,26 @@ async function withLspClient(filePath, fn, toolName, options = {}) {
|
|
|
4516
4516
|
if (result.status !== "found") {
|
|
4517
4517
|
throw new LspServerLookupError(formatServerLookupError(result), result);
|
|
4518
4518
|
}
|
|
4519
|
-
const
|
|
4519
|
+
const server = result.server;
|
|
4520
4520
|
const root = findWorkspaceRoot(absPath);
|
|
4521
4521
|
const manager = options.manager ?? getLspManager();
|
|
4522
4522
|
const acquireAndCall = async (allowRetry) => {
|
|
4523
|
-
const client = await manager.getClient(root,
|
|
4523
|
+
const client = await manager.getClient(root, server, options.signal);
|
|
4524
4524
|
try {
|
|
4525
4525
|
return await fn(client, root, absPath);
|
|
4526
4526
|
} catch (err) {
|
|
4527
4527
|
if (allowRetry && READ_ONLY_TOOLS.has(toolName) && isLspDeadConnectionError(err)) {
|
|
4528
|
-
manager.invalidateClient(root,
|
|
4528
|
+
manager.invalidateClient(root, server.id, client);
|
|
4529
4529
|
return acquireAndCall(false);
|
|
4530
4530
|
}
|
|
4531
4531
|
if (err instanceof LspRequestTimeoutError) {
|
|
4532
|
-
if (manager.isServerInitializing(root,
|
|
4532
|
+
if (manager.isServerInitializing(root, server.id)) {
|
|
4533
4533
|
throw new LspServerInitializingError(err);
|
|
4534
4534
|
}
|
|
4535
4535
|
}
|
|
4536
4536
|
throw err;
|
|
4537
4537
|
} finally {
|
|
4538
|
-
manager.releaseClient(root,
|
|
4538
|
+
manager.releaseClient(root, server.id);
|
|
4539
4539
|
}
|
|
4540
4540
|
};
|
|
4541
4541
|
return acquireAndCall(true);
|
|
@@ -4558,10 +4558,10 @@ function uriToPath(uri) {
|
|
|
4558
4558
|
}
|
|
4559
4559
|
function formatLocation(loc) {
|
|
4560
4560
|
if ("targetUri" in loc) {
|
|
4561
|
-
const
|
|
4562
|
-
const
|
|
4563
|
-
const
|
|
4564
|
-
return `${
|
|
4561
|
+
const uri = uriToPath(loc.targetUri);
|
|
4562
|
+
const line = loc.targetRange.start.line + 1;
|
|
4563
|
+
const char = loc.targetRange.start.character;
|
|
4564
|
+
return `${uri}:${line}:${char}`;
|
|
4565
4565
|
}
|
|
4566
4566
|
const uri = uriToPath(loc.uri);
|
|
4567
4567
|
const line = loc.range.start.line + 1;
|
|
@@ -4706,7 +4706,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4706
4706
|
if (serverResult.status !== "found") {
|
|
4707
4707
|
throw new LspServerLookupError(formatServerLookupError(serverResult));
|
|
4708
4708
|
}
|
|
4709
|
-
const
|
|
4709
|
+
const server = serverResult.server;
|
|
4710
4710
|
const allFiles = (options.listFiles ?? collectFilesWithExtension)(absDir, extension, maxFiles + 1);
|
|
4711
4711
|
const wasCapped = allFiles.length > maxFiles;
|
|
4712
4712
|
const filesToProcess = allFiles.slice(0, maxFiles);
|
|
@@ -4726,7 +4726,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4726
4726
|
const fileErrors = [];
|
|
4727
4727
|
const maxConcurrency = Math.max(1, options.maxConcurrency ?? DIRECTORY_DIAGNOSTICS_MAX_CONCURRENCY);
|
|
4728
4728
|
options.signal?.throwIfAborted();
|
|
4729
|
-
const client = await manager.getClient(root,
|
|
4729
|
+
const client = await manager.getClient(root, server, options.signal);
|
|
4730
4730
|
try {
|
|
4731
4731
|
let nextIndex = 0;
|
|
4732
4732
|
const workers = Array.from({ length: Math.min(maxConcurrency, filesToProcess.length) }, async () => {
|
|
@@ -4754,7 +4754,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4754
4754
|
});
|
|
4755
4755
|
await Promise.all(workers);
|
|
4756
4756
|
} finally {
|
|
4757
|
-
manager.releaseClient(root,
|
|
4757
|
+
manager.releaseClient(root, server.id);
|
|
4758
4758
|
}
|
|
4759
4759
|
const displayDiagnostics = allDiagnostics.slice(0, DEFAULT_MAX_DIAGNOSTICS);
|
|
4760
4760
|
const wasDiagCapped = allDiagnostics.length > DEFAULT_MAX_DIAGNOSTICS;
|
|
@@ -4963,8 +4963,8 @@ function clientOptions(signal) {
|
|
|
4963
4963
|
}
|
|
4964
4964
|
|
|
4965
4965
|
// ../lsp-core/src/tools/result.ts
|
|
4966
|
-
function text(
|
|
4967
|
-
return { content: [{ type: "text", text
|
|
4966
|
+
function text(text, details, isError = false) {
|
|
4967
|
+
return { content: [{ type: "text", text }], details, isError };
|
|
4968
4968
|
}
|
|
4969
4969
|
|
|
4970
4970
|
// ../lsp-core/src/tools/diagnostics.ts
|
|
@@ -4984,7 +4984,7 @@ async function executeLspDiagnostics(params, signal) {
|
|
|
4984
4984
|
const extension = inferExtensionFromDirectory(absPath);
|
|
4985
4985
|
if (!extension) {
|
|
4986
4986
|
const message = `No supported source files found in directory: ${absPath}`;
|
|
4987
|
-
const
|
|
4987
|
+
const details = {
|
|
4988
4988
|
filePath,
|
|
4989
4989
|
severity,
|
|
4990
4990
|
mode: "directory",
|
|
@@ -4994,24 +4994,24 @@ async function executeLspDiagnostics(params, signal) {
|
|
|
4994
4994
|
error: message,
|
|
4995
4995
|
errorKind: "no_files"
|
|
4996
4996
|
};
|
|
4997
|
-
return text(message,
|
|
4997
|
+
return text(message, details);
|
|
4998
4998
|
}
|
|
4999
|
-
const
|
|
5000
|
-
const
|
|
4999
|
+
const output = await aggregateDiagnosticsForDirectory(absPath, extension, severity, undefined, signal === undefined ? {} : { signal });
|
|
5000
|
+
const details = {
|
|
5001
5001
|
filePath,
|
|
5002
5002
|
severity,
|
|
5003
5003
|
mode: "directory",
|
|
5004
5004
|
diagnostics: [],
|
|
5005
|
-
totalDiagnostics:
|
|
5005
|
+
totalDiagnostics: output.totalDiagnostics,
|
|
5006
5006
|
truncated: false,
|
|
5007
|
-
fileFailures: [...
|
|
5007
|
+
fileFailures: [...output.fileFailures]
|
|
5008
5008
|
};
|
|
5009
|
-
return text(
|
|
5009
|
+
return text(output.output, details);
|
|
5010
5010
|
}
|
|
5011
5011
|
const result = await withLspClient(filePath, async (client, _workspaceRoot, resolvedFilePath) => client.diagnostics(resolvedFilePath, signal), "diagnostics", clientOptions(signal));
|
|
5012
5012
|
if (result.transientError) {
|
|
5013
5013
|
const message = result.transientError.message;
|
|
5014
|
-
const
|
|
5014
|
+
const details = {
|
|
5015
5015
|
filePath,
|
|
5016
5016
|
severity,
|
|
5017
5017
|
mode: "file",
|
|
@@ -5021,7 +5021,7 @@ async function executeLspDiagnostics(params, signal) {
|
|
|
5021
5021
|
error: message,
|
|
5022
5022
|
errorKind: result.transientError.kind
|
|
5023
5023
|
};
|
|
5024
|
-
return text(message,
|
|
5024
|
+
return text(message, details, true);
|
|
5025
5025
|
}
|
|
5026
5026
|
const diagnostics = filterDiagnosticsBySeverity(asDiagnosticArray(result), severity);
|
|
5027
5027
|
const total = diagnostics.length;
|
|
@@ -5152,7 +5152,7 @@ async function executeLspInstallDecision(params) {
|
|
|
5152
5152
|
if (!isInstallDecision(decision)) {
|
|
5153
5153
|
return text(`Invalid decision '${String(decision)}'. Expected "declined" or "allowed".`, { serverId, errorKind: "invalid_decision" }, true);
|
|
5154
5154
|
}
|
|
5155
|
-
const serverIds = [...new Set(getMergedServers().map((
|
|
5155
|
+
const serverIds = [...new Set(getMergedServers().map((server) => server.id))];
|
|
5156
5156
|
if (!serverIds.includes(serverId)) {
|
|
5157
5157
|
const preview = serverIds.slice(0, 20).join(", ");
|
|
5158
5158
|
return text(`Unknown LSP server '${serverId}'. Known servers: ${preview}${serverIds.length > 20 ? "..." : ""}`, { serverId, errorKind: "unknown_server" }, true);
|
|
@@ -5292,10 +5292,10 @@ function objectSchema(properties, required = []) {
|
|
|
5292
5292
|
async function executeLspStatus() {
|
|
5293
5293
|
const servers = getAllServers();
|
|
5294
5294
|
const snapshots = getLspManager().getSnapshot();
|
|
5295
|
-
const installed = servers.filter((
|
|
5296
|
-
const configuredLines = servers.map((
|
|
5297
|
-
const state =
|
|
5298
|
-
return `- ${
|
|
5295
|
+
const installed = servers.filter((server) => server.installed && !server.disabled);
|
|
5296
|
+
const configuredLines = servers.map((server) => {
|
|
5297
|
+
const state = server.disabled ? "disabled" : server.installed ? "installed" : "missing";
|
|
5298
|
+
return `- ${server.id}: ${state}; source=${server.source}; extensions=${server.extensions.join(", ")}`;
|
|
5299
5299
|
});
|
|
5300
5300
|
const activeLines = snapshots.map((snapshot) => {
|
|
5301
5301
|
const state = snapshot.alive ? snapshot.isInitializing ? "initializing" : "alive" : "dead";
|
|
@@ -5338,8 +5338,8 @@ async function executeLspSymbols(params, signal) {
|
|
|
5338
5338
|
errorKind: "missing_query"
|
|
5339
5339
|
});
|
|
5340
5340
|
}
|
|
5341
|
-
const
|
|
5342
|
-
return formatSymbolsResult(filePath, scope,
|
|
5341
|
+
const symbols = await withLspClient(filePath, async (client) => client.workspaceSymbols(query, signal), "workspaceSymbols", clientOptions(signal));
|
|
5342
|
+
return formatSymbolsResult(filePath, scope, symbols, limit, query);
|
|
5343
5343
|
}
|
|
5344
5344
|
const symbols = await withLspClient(filePath, async (client, _workspaceRoot, resolvedFilePath) => client.documentSymbols(resolvedFilePath, signal), "documentSymbols", clientOptions(signal));
|
|
5345
5345
|
return formatSymbolsResult(filePath, scope, symbols, limit);
|
|
@@ -5628,26 +5628,26 @@ function daemonFailureResult(paths, error) {
|
|
|
5628
5628
|
return daemonUnreachableResult(paths, error);
|
|
5629
5629
|
}
|
|
5630
5630
|
function daemonCancelledResult(paths) {
|
|
5631
|
-
const
|
|
5631
|
+
const text = [
|
|
5632
5632
|
"LSP daemon request cancelled: the caller aborted this request (for example, the turn was interrupted).",
|
|
5633
5633
|
"The daemon stays available; no LSP work was applied. Retry when you are ready.",
|
|
5634
5634
|
`Socket: ${paths.socket}`
|
|
5635
5635
|
].join(`
|
|
5636
5636
|
`);
|
|
5637
|
-
return { content: [{ type: "text", text
|
|
5637
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
5638
5638
|
}
|
|
5639
5639
|
function daemonTimedOutResult(paths, timeoutMs) {
|
|
5640
|
-
const
|
|
5640
|
+
const text = [
|
|
5641
5641
|
`LSP daemon request timed out after ${timeoutMs}ms: the daemon did not respond in time.`,
|
|
5642
5642
|
"The daemon stays available but may be busy. Retry when you are ready.",
|
|
5643
5643
|
`Socket: ${paths.socket}`,
|
|
5644
5644
|
`Logs: ${paths.log}`
|
|
5645
5645
|
].join(`
|
|
5646
5646
|
`);
|
|
5647
|
-
return { content: [{ type: "text", text
|
|
5647
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
5648
5648
|
}
|
|
5649
5649
|
function daemonUnreachableResult(paths, error) {
|
|
5650
|
-
const
|
|
5650
|
+
const text = [
|
|
5651
5651
|
`LSP daemon unreachable: ${errorText(error)}.`,
|
|
5652
5652
|
"The MCP server is a thin proxy and never runs language servers in-process.",
|
|
5653
5653
|
`Socket: ${paths.socket}`,
|
|
@@ -5655,7 +5655,7 @@ function daemonUnreachableResult(paths, error) {
|
|
|
5655
5655
|
"The daemon is auto-started on demand and will be retried on the next request."
|
|
5656
5656
|
].join(`
|
|
5657
5657
|
`);
|
|
5658
|
-
return { content: [{ type: "text", text
|
|
5658
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
5659
5659
|
}
|
|
5660
5660
|
function errorText(error) {
|
|
5661
5661
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -6092,7 +6092,7 @@ async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
|
6092
6092
|
return await pingDaemon(paths, token, timeoutMs, signal) !== null;
|
|
6093
6093
|
}
|
|
6094
6094
|
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
6095
|
-
return new Promise((
|
|
6095
|
+
return new Promise((resolve) => {
|
|
6096
6096
|
const socket = new Socket;
|
|
6097
6097
|
let settled = false;
|
|
6098
6098
|
let timer;
|
|
@@ -6104,7 +6104,7 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
|
6104
6104
|
clearTimeout(timer);
|
|
6105
6105
|
signal?.removeEventListener("abort", onAbort);
|
|
6106
6106
|
socket.destroy();
|
|
6107
|
-
|
|
6107
|
+
resolve(value);
|
|
6108
6108
|
};
|
|
6109
6109
|
const onAbort = () => finish(null);
|
|
6110
6110
|
const decoder = createLineDecoder((message) => {
|
|
@@ -6167,7 +6167,7 @@ function defaultEnsureDaemonDeps() {
|
|
|
6167
6167
|
};
|
|
6168
6168
|
}
|
|
6169
6169
|
function sleepWithSignal(ms, signal) {
|
|
6170
|
-
return new Promise((
|
|
6170
|
+
return new Promise((resolve) => {
|
|
6171
6171
|
let settled = false;
|
|
6172
6172
|
const finish = () => {
|
|
6173
6173
|
if (settled)
|
|
@@ -6175,7 +6175,7 @@ function sleepWithSignal(ms, signal) {
|
|
|
6175
6175
|
settled = true;
|
|
6176
6176
|
clearTimeout(timer);
|
|
6177
6177
|
signal?.removeEventListener("abort", finish);
|
|
6178
|
-
|
|
6178
|
+
resolve();
|
|
6179
6179
|
};
|
|
6180
6180
|
const timer = setTimeout(finish, ms);
|
|
6181
6181
|
if (signal?.aborted) {
|
|
@@ -6190,7 +6190,7 @@ function awaitWithSignal2(promise, signal) {
|
|
|
6190
6190
|
return promise;
|
|
6191
6191
|
if (signal.aborted)
|
|
6192
6192
|
return Promise.reject(abortError3(signal));
|
|
6193
|
-
return new Promise((
|
|
6193
|
+
return new Promise((resolve, reject) => {
|
|
6194
6194
|
let settled = false;
|
|
6195
6195
|
const finish = (run) => {
|
|
6196
6196
|
if (settled)
|
|
@@ -6201,7 +6201,7 @@ function awaitWithSignal2(promise, signal) {
|
|
|
6201
6201
|
};
|
|
6202
6202
|
const onAbort = () => finish(() => reject(abortError3(signal)));
|
|
6203
6203
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
6204
|
-
promise.then((value) => finish(() =>
|
|
6204
|
+
promise.then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
|
|
6205
6205
|
});
|
|
6206
6206
|
}
|
|
6207
6207
|
function throwIfAborted(signal) {
|
|
@@ -6230,18 +6230,18 @@ function parsePingResponse(message) {
|
|
|
6230
6230
|
return null;
|
|
6231
6231
|
if (!endpoint || typeof endpoint !== "object" || Array.isArray(endpoint))
|
|
6232
6232
|
return null;
|
|
6233
|
-
const
|
|
6233
|
+
const path = Reflect.get(endpoint, "path");
|
|
6234
6234
|
const kind = Reflect.get(endpoint, "kind");
|
|
6235
|
-
if (typeof
|
|
6235
|
+
if (typeof path !== "string")
|
|
6236
6236
|
return null;
|
|
6237
6237
|
if (kind === "windows")
|
|
6238
|
-
return { pid, nonce, startedAt, endpoint: { kind, path
|
|
6238
|
+
return { pid, nonce, startedAt, endpoint: { kind, path } };
|
|
6239
6239
|
if (kind === "missing")
|
|
6240
|
-
return { pid, nonce, startedAt, endpoint: { kind, path
|
|
6240
|
+
return { pid, nonce, startedAt, endpoint: { kind, path } };
|
|
6241
6241
|
const dev = Reflect.get(endpoint, "dev");
|
|
6242
6242
|
const ino = Reflect.get(endpoint, "ino");
|
|
6243
6243
|
if (kind === "unix" && typeof dev === "number" && typeof ino === "number") {
|
|
6244
|
-
return { pid, nonce, startedAt, endpoint: { kind, path
|
|
6244
|
+
return { pid, nonce, startedAt, endpoint: { kind, path, dev, ino } };
|
|
6245
6245
|
}
|
|
6246
6246
|
return null;
|
|
6247
6247
|
}
|
|
@@ -6394,7 +6394,7 @@ function withContext(args, context) {
|
|
|
6394
6394
|
function ensureDaemonAvailable(paths, ensure, signal) {
|
|
6395
6395
|
if (!signal)
|
|
6396
6396
|
return ensure(paths);
|
|
6397
|
-
return new Promise((
|
|
6397
|
+
return new Promise((resolve, reject) => {
|
|
6398
6398
|
let settled = false;
|
|
6399
6399
|
const finish = (run) => {
|
|
6400
6400
|
if (settled)
|
|
@@ -6409,11 +6409,11 @@ function ensureDaemonAvailable(paths, ensure, signal) {
|
|
|
6409
6409
|
return;
|
|
6410
6410
|
}
|
|
6411
6411
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
6412
|
-
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() =>
|
|
6412
|
+
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
|
|
6413
6413
|
});
|
|
6414
6414
|
}
|
|
6415
6415
|
function sendToolCall(paths, token, name, args, options) {
|
|
6416
|
-
return new Promise((
|
|
6416
|
+
return new Promise((resolve, reject) => {
|
|
6417
6417
|
const socket = connect(paths.socket);
|
|
6418
6418
|
const requestId = allocateProxyRequestId();
|
|
6419
6419
|
let settled = false;
|
|
@@ -6466,7 +6466,7 @@ function sendToolCall(paths, token, name, args, options) {
|
|
|
6466
6466
|
}
|
|
6467
6467
|
const result = toToolResult(message, requestId);
|
|
6468
6468
|
if (result)
|
|
6469
|
-
finish(() =>
|
|
6469
|
+
finish(() => resolve(result));
|
|
6470
6470
|
else
|
|
6471
6471
|
finish(() => reject(new DaemonRequestError("invalid daemon response", requestWritten)));
|
|
6472
6472
|
});
|
|
@@ -6638,10 +6638,10 @@ function canonicalizePath(value) {
|
|
|
6638
6638
|
return value;
|
|
6639
6639
|
return realpathSync6(value);
|
|
6640
6640
|
}
|
|
6641
|
-
function projectRootFromOpenCodeConfigPath(
|
|
6642
|
-
if (basename4(
|
|
6641
|
+
function projectRootFromOpenCodeConfigPath(path) {
|
|
6642
|
+
if (basename4(path) !== "lsp.json" && basename4(path) !== "lsp-client.json")
|
|
6643
6643
|
return;
|
|
6644
|
-
const configDir = dirname11(
|
|
6644
|
+
const configDir = dirname11(path);
|
|
6645
6645
|
const configDirName = basename4(configDir);
|
|
6646
6646
|
if (configDirName !== ".opencode" && configDirName !== ".omo")
|
|
6647
6647
|
return;
|
|
@@ -6711,9 +6711,9 @@ function reapStaleLock(lockPath) {
|
|
|
6711
6711
|
unlinkQuietly(lockPath);
|
|
6712
6712
|
return true;
|
|
6713
6713
|
}
|
|
6714
|
-
function unlinkQuietly(
|
|
6714
|
+
function unlinkQuietly(path) {
|
|
6715
6715
|
try {
|
|
6716
|
-
unlinkSync3(
|
|
6716
|
+
unlinkSync3(path);
|
|
6717
6717
|
} catch (error) {
|
|
6718
6718
|
if (error instanceof Error)
|
|
6719
6719
|
return;
|
|
@@ -6876,19 +6876,19 @@ function parseEndpoint(value) {
|
|
|
6876
6876
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
6877
6877
|
return null;
|
|
6878
6878
|
const kind = Reflect.get(value, "kind");
|
|
6879
|
-
const
|
|
6880
|
-
if (typeof
|
|
6879
|
+
const path = Reflect.get(value, "path");
|
|
6880
|
+
if (typeof path !== "string")
|
|
6881
6881
|
return null;
|
|
6882
6882
|
if (kind === undefined)
|
|
6883
|
-
return endpointIdentity(
|
|
6883
|
+
return endpointIdentity(path);
|
|
6884
6884
|
if (kind === "windows")
|
|
6885
|
-
return { kind, path
|
|
6885
|
+
return { kind, path };
|
|
6886
6886
|
if (kind === "missing")
|
|
6887
|
-
return { kind, path
|
|
6887
|
+
return { kind, path };
|
|
6888
6888
|
const dev = Reflect.get(value, "dev");
|
|
6889
6889
|
const ino = Reflect.get(value, "ino");
|
|
6890
6890
|
if (kind === "unix" && typeof dev === "number" && typeof ino === "number")
|
|
6891
|
-
return { kind, path
|
|
6891
|
+
return { kind, path, dev, ino };
|
|
6892
6892
|
return null;
|
|
6893
6893
|
}
|
|
6894
6894
|
|
|
@@ -6922,7 +6922,7 @@ async function reapStaleDaemonVersions(ownPaths, deps = {}) {
|
|
|
6922
6922
|
const attest = deps.attest ?? ((pid) => attestDaemonCliProcess(pid, platform));
|
|
6923
6923
|
const sendSignal = deps.sendSignal ?? defaultSendSignal;
|
|
6924
6924
|
const waitForExit = deps.waitForExit ?? defaultWaitForExit;
|
|
6925
|
-
const removeDir = deps.removeDir ?? ((
|
|
6925
|
+
const removeDir = deps.removeDir ?? ((path) => rm(path, { recursive: true, force: true }));
|
|
6926
6926
|
const readOwner = deps.readOwner ?? readDaemonOwner;
|
|
6927
6927
|
const log = deps.log ?? defaultLog;
|
|
6928
6928
|
const termGraceMs = deps.termGraceMs ?? TERM_GRACE_MS;
|
|
@@ -7033,17 +7033,17 @@ function isNodeCliDaemonArgv(argv) {
|
|
|
7033
7033
|
function isNodeCliDaemonCommand(command) {
|
|
7034
7034
|
return /\bnode(?:\.exe)?\b/i.test(command) && /\bcli\.js\b/.test(command) && /\bdaemon\b/.test(command);
|
|
7035
7035
|
}
|
|
7036
|
-
function defaultReadProcFile(
|
|
7037
|
-
return readFile(
|
|
7036
|
+
function defaultReadProcFile(path) {
|
|
7037
|
+
return readFile(path);
|
|
7038
7038
|
}
|
|
7039
7039
|
function defaultExecuteForStdout(file, args) {
|
|
7040
|
-
return new Promise((
|
|
7040
|
+
return new Promise((resolve) => {
|
|
7041
7041
|
execFile(file, [...args], { encoding: "utf8", maxBuffer: 1024 * 1024, timeout: 1000, windowsHide: true }, (error, stdout) => {
|
|
7042
7042
|
if (error !== null) {
|
|
7043
|
-
|
|
7043
|
+
resolve(null);
|
|
7044
7044
|
return;
|
|
7045
7045
|
}
|
|
7046
|
-
|
|
7046
|
+
resolve(stdout);
|
|
7047
7047
|
});
|
|
7048
7048
|
});
|
|
7049
7049
|
}
|
|
@@ -7062,7 +7062,7 @@ async function defaultWaitForExit(pid, timeoutMs) {
|
|
|
7062
7062
|
return true;
|
|
7063
7063
|
if (Date.now() >= deadline)
|
|
7064
7064
|
return false;
|
|
7065
|
-
await new Promise((
|
|
7065
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
7066
7066
|
}
|
|
7067
7067
|
}
|
|
7068
7068
|
function defaultLog(message) {
|
|
@@ -7083,7 +7083,7 @@ async function startDaemonServer(paths, options = {}) {
|
|
|
7083
7083
|
lastActiveAt = Date.now();
|
|
7084
7084
|
};
|
|
7085
7085
|
let routeOwner = lease.owner;
|
|
7086
|
-
const
|
|
7086
|
+
const server = createServer2((socket) => {
|
|
7087
7087
|
connections.add(socket);
|
|
7088
7088
|
const activeRequests = new Map;
|
|
7089
7089
|
touch();
|
|
@@ -7101,10 +7101,10 @@ async function startDaemonServer(paths, options = {}) {
|
|
|
7101
7101
|
touch();
|
|
7102
7102
|
});
|
|
7103
7103
|
});
|
|
7104
|
-
|
|
7104
|
+
server.on("error", (error) => logServerError(error));
|
|
7105
7105
|
let owner;
|
|
7106
7106
|
try {
|
|
7107
|
-
await listen(
|
|
7107
|
+
await listen(server, paths.socket);
|
|
7108
7108
|
if (process.platform !== "win32")
|
|
7109
7109
|
chmodSync2(paths.socket, 384);
|
|
7110
7110
|
owner = { ...lease.owner, endpoint: endpointIdentity(paths.socket) };
|
|
@@ -7126,7 +7126,7 @@ async function startDaemonServer(paths, options = {}) {
|
|
|
7126
7126
|
for (const socket of connections)
|
|
7127
7127
|
socket.destroy();
|
|
7128
7128
|
connections.clear();
|
|
7129
|
-
await closeServer(
|
|
7129
|
+
await closeServer(server);
|
|
7130
7130
|
removeDaemonMetadataForOwner(paths, owner);
|
|
7131
7131
|
await disposeDefaultLspManager();
|
|
7132
7132
|
};
|
|
@@ -7147,7 +7147,7 @@ async function startDaemonServer(paths, options = {}) {
|
|
|
7147
7147
|
}, idleCheckIntervalMs);
|
|
7148
7148
|
idleTimer.unref();
|
|
7149
7149
|
installSignalHandlers(close);
|
|
7150
|
-
return { server
|
|
7150
|
+
return { server, close };
|
|
7151
7151
|
}
|
|
7152
7152
|
async function respond(socket, message, token, owner, activeRequests) {
|
|
7153
7153
|
try {
|
|
@@ -7168,18 +7168,18 @@ async function assertSelfProbe(paths, token, owner) {
|
|
|
7168
7168
|
if (!ping || ping.nonce !== owner.nonce)
|
|
7169
7169
|
throw new DaemonStartupDeferredError("self_probe_failed");
|
|
7170
7170
|
}
|
|
7171
|
-
function listen(
|
|
7172
|
-
return new Promise((
|
|
7171
|
+
function listen(server, socketPath) {
|
|
7172
|
+
return new Promise((resolve, reject) => {
|
|
7173
7173
|
const onError = (error) => reject(error);
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7174
|
+
server.once("error", onError);
|
|
7175
|
+
server.listen(socketPath, () => {
|
|
7176
|
+
server.removeListener("error", onError);
|
|
7177
|
+
resolve();
|
|
7178
7178
|
});
|
|
7179
7179
|
});
|
|
7180
7180
|
}
|
|
7181
|
-
function closeServer(
|
|
7182
|
-
return new Promise((
|
|
7181
|
+
function closeServer(server) {
|
|
7182
|
+
return new Promise((resolve) => server.close(() => resolve()));
|
|
7183
7183
|
}
|
|
7184
7184
|
function installSignalHandlers(close) {
|
|
7185
7185
|
const handler = () => {
|