Files

92 lines
3.4 KiB
JavaScript
Raw Permalink Normal View History

/* eslint-disable no-undef */
function createConfigAndCleanupApi(ctx) {
with (ctx) {
function resolveMcpServerRuntimeCommand() {
const runtimeCommand = process.execPath;
const runtimeEnv = [];
if (runtimeCommand && existsSync(runtimeCommand)) {
const basename = path.basename(runtimeCommand).toLowerCase();
const isNodeBinary = basename === "node" || basename.startsWith("node.");
if (!isNodeBinary) {
runtimeEnv.push({ name: "ELECTRON_RUN_AS_NODE", value: "1" });
}
return { command: runtimeCommand, env: runtimeEnv };
}
return { command: "node", env: runtimeEnv };
}
function buildMcpServerConfig(port, scopedSessionIds, chatSessionId) {
// Use provided scoped IDs, or resolve them from chatSessionId.
const effectiveIds = (scopedSessionIds && scopedSessionIds.length > 0)
? scopedSessionIds
: getScopedSessionIds(chatSessionId);
const runtimePath = toUnpackedAsarPath(
path.join(__dirname, "..", "mcp", "netcatty-mcp-server.cjs"),
);
const runtime = resolveMcpServerRuntimeCommand();
const env = [
...runtime.env,
{ name: "NETCATTY_MCP_PORT", value: String(port) },
];
if (authToken) {
env.push({ name: "NETCATTY_MCP_TOKEN", value: authToken });
}
if (DEBUG_MCP) {
env.push({ name: "NETCATTY_MCP_DEBUG", value: "1" });
}
// When chatSessionId is present, the MCP subprocess resolves scope dynamically
// through main-process metadata, so avoid freezing session IDs at spawn time.
if (!chatSessionId && effectiveIds && effectiveIds.length > 0) {
env.push({ name: "NETCATTY_MCP_SESSION_IDS", value: effectiveIds.join(",") });
}
// Pass chatSessionId so MCP server can scope getContext responses
if (chatSessionId) {
env.push({ name: "NETCATTY_MCP_CHAT_SESSION_ID", value: chatSessionId });
}
// Pass permission mode so MCP server can enforce it locally (defense-in-depth)
env.push({ name: "NETCATTY_MCP_PERMISSION_MODE", value: permissionMode });
return {
name: "netcatty-remote-hosts",
type: "stdio",
command: runtime.command,
args: [runtimePath],
env,
};
}
// ── Cleanup ──
async function cleanupScopedMetadata(chatSessionId) {
if (chatSessionId) {
scopedMetadata.delete(chatSessionId);
scopedAttachments.delete(chatSessionId);
preserveIdleSessionCleanup?.(chatSessionId);
clearOpenedSessionScope?.(chatSessionId);
cancelledChatSessions.delete(chatSessionId);
cancelBackgroundJobsForSession(chatSessionId);
cancelWorkerBackgroundJobsForSession(chatSessionId);
// Resolve any in-flight approval requests so dispatch()'s finally block
// releases its pendingSessionWriteApprovals entry. Without this, a chat
// deleted while an approval was pending would leave the per-session
// write lock held until the approval timeout expires.
clearPendingApprovals(chatSessionId);
await cancelSftpOpsForSession(chatSessionId);
sftpBridge.clearSftpEncodingStateByPrefix?.(`chat:${chatSessionId}:session:`);
}
}
return { resolveMcpServerRuntimeCommand, buildMcpServerConfig, cleanupScopedMetadata };
}
}
module.exports = { createConfigAndCleanupApi };