89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
/*
|
|
* Veskslop Phase 0 spike — mock Vesktop control socket.
|
|
*
|
|
* Stands in for the real Vesktop main process so the MCP control bridge can be
|
|
* developed and tested without Electron. Implements the JSON-lines protocol.
|
|
*
|
|
* Usage:
|
|
* VESKSLOP_SOCKET=/tmp/veskslop-mock.sock pnpm control:mock
|
|
*/
|
|
|
|
import { rmSync } from "node:fs";
|
|
import { createServer } from "node:net";
|
|
|
|
import { socketPath } from "./vesktop-socket.js";
|
|
|
|
const FAKE_GUILDS = [
|
|
{
|
|
id: "100000000000000001",
|
|
name: "Mock Guild",
|
|
channels: [
|
|
{ id: "200000000000000001", name: "general", type: 0 },
|
|
{ id: "200000000000000002", name: "voice", type: 2 }
|
|
]
|
|
}
|
|
];
|
|
|
|
const FAKE_MESSAGES = [
|
|
{ id: "300000000000000001", author: { id: "1", username: "mockuser" }, content: "hello from the mock", timestamp: new Date().toISOString() }
|
|
];
|
|
|
|
function handle(command: string | undefined, data: any) {
|
|
switch (command) {
|
|
case "ping":
|
|
return "pong";
|
|
case "state:get":
|
|
return { user: { id: "1", username: "mockuser" }, guilds: FAKE_GUILDS };
|
|
case "message:send":
|
|
return { channelId: data?.channelId, content: data?.content, sent: true };
|
|
case "message:read":
|
|
return { channelId: data?.channelId, cached: FAKE_MESSAGES.length, messages: FAKE_MESSAGES };
|
|
case "dm:send":
|
|
return { userId: data?.userId, channelId: `dm-${data?.userId}`, content: data?.content, sent: true };
|
|
default:
|
|
throw new Error(`Unknown command: ${command}`);
|
|
}
|
|
}
|
|
|
|
rmSync(socketPath, { force: true });
|
|
|
|
const server = createServer(socket => {
|
|
socket.setEncoding("utf-8");
|
|
let buffer = "";
|
|
|
|
socket.on("data", (chunk: string) => {
|
|
buffer += chunk;
|
|
|
|
let newline: number;
|
|
while ((newline = buffer.indexOf("\n")) !== -1) {
|
|
const line = buffer.slice(0, newline).trim();
|
|
buffer = buffer.slice(newline + 1);
|
|
if (!line) continue;
|
|
|
|
const start = Date.now();
|
|
try {
|
|
const request = JSON.parse(line);
|
|
const result = handle(request.command, request.data);
|
|
console.log(`[mock] ${new Date().toISOString()} ${request.command} (${Date.now() - start}ms)`);
|
|
socket.write(JSON.stringify({ id: request.id, ok: true, data: result }) + "\n");
|
|
} catch (e) {
|
|
const error = e instanceof Error ? e.message : String(e);
|
|
console.log(`[mock] ${new Date().toISOString()} ERROR ${error}`);
|
|
socket.write(JSON.stringify({ ok: false, error }) + "\n");
|
|
}
|
|
}
|
|
});
|
|
|
|
socket.on("error", () => socket.destroy());
|
|
});
|
|
|
|
server.listen(socketPath, () => {
|
|
console.log(`[mock] listening on ${socketPath}`);
|
|
});
|
|
|
|
process.on("SIGINT", () => {
|
|
server.close();
|
|
rmSync(socketPath, { force: true });
|
|
process.exit(0);
|
|
});
|