# Veskslop Phase 0 spikes Timeboxed proof-of-concept scripts, not production code. Project direction lives in [../AGENTS.md](../AGENTS.md). ## Setup ```sh pnpm install ``` ## Current query policy - Every command sent to the client is spaced by a random **500–2800 ms** and serialized through one queue (`src/pacing.ts`). - Identical concurrent reads are coalesced (`dedupe`), and state reads are cache-only. - `DEBUG=1` logs every command and its delay. ## Spikes ### 0. QR login / token acquisition — `src/qr-login.ts` ```sh pnpm poc:qr TOKEN_FILE=/path/to/token DEBUG=1 pnpm poc:qr ``` Connects to `wss://remote-auth-gateway.discord.gg/?v=2`, renders the approval QR directly in the terminal, waits for a phone scan, exchanges the login ticket and writes the token to `poc/.token` (mode 600, gitignored; override with `TOKEN_FILE`). This is the **puppet account's** token. No existing token required. If Discord responds with an hCaptcha challenge, the script prints the challenge and prompts for a `HCAPTCHA_KEY` (or takes it from the environment). The headless client consumes this file on startup via `VESKSLOP_TOKEN_FILE` (default `/token`, see [../AGENTS.md](../AGENTS.md) → Auth persistence). ### 1. Vesktop control bridge — `src/control-bridge.ts` The MCP daemon. Exposes Streamable HTTP and forwards tool calls over the JSON-lines protocol on Vesktop's control socket (default `$XDG_RUNTIME_DIR/vesktop.sock`, override with `VESKSLOP_SOCKET`). ```sh pnpm control:bridge # http://127.0.0.1:8788/mcp VESKSLOP_SOCKET=... MCP_PORT=... pnpm control:bridge ``` Tools: `get_state` (cache-only), `send_message`, `read_messages` (coalesced), `send_dm`. Vesktop side lives in this repo: - `src/main/ipcSocket.ts` — parses JSON lines `{ id, command, data }` and forwards them through `sendRendererCommand`. - `src/renderer/ipcCommands.ts` — handlers that use official client interactions (`sendMessage`, `openPrivateChannel`, guild/channel/message stores). Protocol commands: `ping`, `state:get`, `message:send`, `message:read`, `dm:send`. ### 2. Mock Vesktop — `src/mock-vesktop.ts` ```sh VESKSLOP_SOCKET=/tmp/veskslop-mock.sock pnpm control:mock ``` Stands in for the real Vesktop process so the bridge can be developed without Electron/Xvfb. ### 3. Live DM test — `src/test-dm.ts` ```sh pnpm poc:dm TEST_DM_CONTENT="hello" pnpm poc:dm ``` Talks to a **real** running Vesktop's control socket and sends one paced DM. Defaults to the only sanctioned live-send target, user **zzib** (`TEST_DM_USER_ID` overrides). ## Verified - QR login works end-to-end; the token in `.token` came from a phone scan. - MCP bridge path: `initialize`, `tools/list`, `get_state`, `send_message`, `read_messages`, `send_dm` all round-trip correctly through the mock. - Pacing observed live: consecutive commands reached the mock **592 ms**, **1862 ms** and **1439 ms** apart — inside the 500–2800 ms window. ## Deferred (not testable in this container) - Real renderer handlers against a live Discord session (needs Electron + display). - Headless display strategy (Xvfb/offscreen) and client lightening. - Token injection into the official client. - Voice join/leave and audio/video pipelines (need a voice channel; ffmpeg/PipeWire absent). - Remote client auth over LAN/Tailscale and the web debug UI. ## Findings - Official `discord.js` is bot-only (`/gateway/bot` → 401 with a user token), and `discord.js-selfbot-v13` is archived — do not build on either. The core is the real Vesktop client, puppeted through its own interactions. - MCP SDK `1.30.0` stateless Streamable HTTP works as used here. - Vesktop's existing `sendRendererCommand`/`onIpcCommand` bus is enough to puppet interactions; the socket only needed a JSON-lines framing and a command map. - pnpm 11: this folder is its own workspace root; build approvals live in `pnpm-workspace.yaml` (`allowBuilds`).