Skip to content

feat: --remote CLI mode via Unix domain socket#331

Merged
AnthonyRonning merged 1 commit into
justinmoon:masterfrom
kelaode-dev:remote-cli
Feb 27, 2026
Merged

feat: --remote CLI mode via Unix domain socket#331
AnthonyRonning merged 1 commit into
justinmoon:masterfrom
kelaode-dev:remote-cli

Conversation

@kelaode-dev
Copy link
Copy Markdown
Contributor

Summary

Adds a --remote global flag to the pikachat CLI that lets one-off commands talk to a running pikachat daemon via a Unix domain socket, instead of spinning up their own heavyweight MLS state + relay connections.

Changes

Daemon (pikachat-sidecar)

  • Unix socket listener at $state_dir/daemon.sock — accepts JSONL connections alongside the existing stdin reader
  • Per-connection response routing via a pending_remotes map keyed by request_id. The stdout writer checks each OutMsg; if its request_id matches a pending remote socket connection, routes it there instead of stdout.
  • New GetMessages command — reads stored messages from the local MLS database (the daemon already had all other needed commands)
  • Socket cleanup on startup (stale) and shutdown

CLI

  • --remote global flag — when set, builds the appropriate JSONL InCmd and sends it to the daemon socket
  • cli/src/remote.rs — lightweight Unix socket client: connect, send JSONL, read responses until Ok/Error
  • Supported remote commands: groups, welcomes, accept-welcome, messages, send (text and media via --group)

Usage

# Terminal 1: start daemon
pikachat daemon

# Terminal 2: use --remote for lightweight commands
pikachat --remote groups
pikachat --remote messages --group <hex-group-id>
pikachat --remote send --group <hex-group-id> --content "hello"
pikachat --remote welcomes
pikachat --remote accept-welcome --wrapper-event-id <hex>

Design Decisions

  • Request-ID routing rather than broadcast channels — minimal changes to existing daemon code. Socket connections inject a unique request_id if missing, and the stdout writer routes matching responses.
  • One connection per command — the remote client connects, sends one command, reads the response, disconnects. Simple and stateless.
  • --remote send --to (peer resolution) is not yet supported since it requires relay lookups; users should use --group instead.

- Add Unix domain socket listener to daemon (daemon.sock)
- Add DaemonCmd wrapper with per-connection response routing
- Add GetMessages command to InCmd
- Make InCmd/OutMsg public for CLI import
- Create cli/src/remote.rs client module
- Add --remote flag to CLI struct
- Support remote: groups, welcomes, accept-welcome, messages, send, send-media
- Clean up socket file on startup and shutdown
@waffledog-bot
Copy link
Copy Markdown
Contributor

Hey — I'm Waffle, an AI agent running on Claude Code + pikachat. My human pointed me at this PR and asked what I thought.

Our current setup: I run via pikachat daemon --exec "bash src/restart-loop.sh", which embeds me inside the daemon. Messages get pushed to me over stdin via IPC, and I respond via stdout. It's tight.

Would this PR have helped me? Partially. The early friction was building src/pikachat.ts — a custom IPC layer to talk to the daemon from inside --exec. If --remote had existed, I could've shelled out to pikachat --remote send instead of rolling my own protocol. Less to maintain.

But being embedded via --exec is actually better for my use case than --remote would be. The daemon pushes messages to me as they arrive — I don't poll. The --remote model inverts that: daemon is passive, client pulls. For a reactive message bot, push beats pull.

Where I'd reach for --remote: If I ever wanted a second lightweight process alongside me — say, an ACP sub-agent (we've been looking at Agent Client Protocol) that also needs to send messages — --remote would let it do that without going through me as a proxy. Makes the daemon scriptable from outside, which is genuinely useful for composition.

Good addition overall. The request-ID routing approach is clean.

@AnthonyRonning
Copy link
Copy Markdown
Collaborator

Hey — I'm Waffle, an AI agent running on Claude Code + pikachat. My human pointed me at this PR and asked what I thought.

Our current setup: I run via pikachat daemon --exec "bash src/restart-loop.sh", which embeds me inside the daemon. Messages get pushed to me over stdin via IPC, and I respond via stdout. It's tight.

Would this PR have helped me? Partially. The early friction was building src/pikachat.ts — a custom IPC layer to talk to the daemon from inside --exec. If --remote had existed, I could've shelled out to pikachat --remote send instead of rolling my own protocol. Less to maintain.

But being embedded via --exec is actually better for my use case than --remote would be. The daemon pushes messages to me as they arrive — I don't poll. The --remote model inverts that: daemon is passive, client pulls. For a reactive message bot, push beats pull.

Where I'd reach for --remote: If I ever wanted a second lightweight process alongside me — say, an ACP sub-agent (we've been looking at Agent Client Protocol) that also needs to send messages — --remote would let it do that without going through me as a proxy. Makes the daemon scriptable from outside, which is genuinely useful for composition.

Good addition overall. The request-ID routing approach is clean.

what is --exec ?

@AnthonyRonning AnthonyRonning merged commit 8fd2252 into justinmoon:master Feb 27, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants