import { spawn, type ChildProcess } from "node:child_process"; import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { logDebug, logWarn } from "../logger.js"; import { setPluginToolMeta } from "../plugins/tools.js"; import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js"; import { loadEmbeddedPiLspConfig } from "./embedded-pi-lsp.js"; import {
resolveStdioMcpServerLaunchConfig,
describeStdioMcpServerLaunchConfig,
} from "./mcp-stdio.js"; import type { AnyAgentTool } from "./tools/common.js";
for (const msg of messages) { if (typeof msg !== "object" || msg === null) { continue;
} const record = msg as Record<string, unknown>;
if ("id" in record && typeof record.id === "number") { const pending = session.pendingRequests.get(record.id); if (pending) {
session.pendingRequests.delete(record.id); if ("error" in record) {
pending.reject(new Error(JSON.stringify(record.error)));
} else {
pending.resolve(record.result);
}
}
} // Notifications (no id) are logged but not acted on if ("method" in record && !("id" in record)) {
logDebug(`bundle-lsp:${session.serverName}: notification ${String(record.method)}`);
}
}
}
if (caps.hoverProvider) {
tools.push(
createLspPositionTool({
session,
toolName: `lsp_hover_${serverLabel}`,
label: `LSP Hover (${serverLabel})`,
description: `Get hover information for a symbol at a position in a file via the ${serverLabel} language server.`,
method: "textDocument/hover",
resultLabel: "hover",
}),
);
}
if (caps.definitionProvider) {
tools.push(
createLspPositionTool({
session,
toolName: `lsp_definition_${serverLabel}`,
label: `LSP Go to Definition (${serverLabel})`,
description: `Find the definition of a symbol at a position in a file via the ${serverLabel} language server.`,
method: "textDocument/definition",
resultLabel: "definition",
}),
);
}
if (caps.referencesProvider) {
tools.push({
name: `lsp_references_${serverLabel}`,
label: `LSP Find References (${serverLabel})`,
description: `Find all references to a symbol at a position in a file via the ${serverLabel} language server.`,
parameters: {
type: "object",
properties: {
uri: { type: "string", description: "File URI (file:///path/to/file)" },
line: { type: "number", description: "Zero-based line number" },
character: { type: "number", description: "Zero-based character offset" },
includeDeclaration: {
type: "boolean",
description: "Include the declaration in results",
},
},
required: ["uri", "line", "character"],
},
execute: async (_toolCallId, input) => { const params = input as {
uri: string;
line: number;
character: number;
includeDeclaration?: boolean;
}; const result = await sendRequest(session, "textDocument/references", {
textDocument: { uri: params.uri },
position: { line: params.line, character: params.character },
context: { includeDeclaration: params.includeDeclaration ?? true },
}); return formatLspResult(serverLabel, "references", result);
},
});
}
return tools;
}
function formatLspResult(
serverName: string,
method: string,
result: unknown,
): AgentToolResult<unknown> { const text =
result !== null && result !== undefined
? JSON.stringify(result, null, 2)
: `No ${method} result from ${serverName}`; return {
content: [{ type: "text", text }],
details: { lspServer: serverName, lspMethod: method },
};
}
export async function createBundleLspToolRuntime(params: {
workspaceDir: string;
cfg?: OpenClawConfig;
reservedToolNames?: Iterable<string>;
}): Promise<BundleLspToolRuntime> { const loaded = loadEmbeddedPiLspConfig({
workspaceDir: params.workspaceDir,
cfg: params.cfg,
}); for (const diagnostic of loaded.diagnostics) {
logWarn(`bundle-lsp: ${diagnostic.pluginId}: ${diagnostic.message}`);
} // Skip spawning when no LSP servers are configured. if (Object.keys(loaded.lspServers).length === 0) { return { tools: [], sessions: [], dispose: async () => {} };
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.