107 lines
4.2 KiB
Markdown
107 lines
4.2 KiB
Markdown
# 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
|
||
`<DATA_DIR>/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.
|
||
- **Live end-to-end (2026-09-11)**: a real Vesktop client was run headless under Xvfb with the
|
||
token seeded from `~/.config/veskslop/token`. `get_state` returned the real account and 7 guilds,
|
||
and `send_dm` delivered a real DM to zzib (`sent: true`). Cache-only `read_messages` on that
|
||
never-opened DM returned zero messages, as expected.
|
||
- 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`).
|