agent-console complained about the two-step workflow: kubectl exec a
script to spawn an agent, separate terminal to watch it. Root cause
was hub being a separate Go sidecar with no access to the pi binary
or its config, so it could only watch, never trigger.
hub.js now runs inside the pi container itself (same filesystem, same
PATH) and exposes POST /run, which spawns `pi -p --mode json` directly
and streams it over the same /console WebSocket everyone else is
already watching. agent-console gained a `run <agent> <prompt>`
subcommand that POSTs then watches -- no separate exec step, no
separate terminal. Drops the golang:1.25-alpine sidecar container and
agent-run-configmap.yaml entirely (both superseded).
ConfigMaps regenerated via local-harness/hub/sync-to-homelab.sh
instead of hand-copied, so hub.js/config.json/settings.json each have
exactly one source of truth.
JSON.stringify({id, event: line}) treated the already-JSON `line` as a
plain string, so every event landed in the hub double-encoded (a JSON
string containing escaped JSON, not an object) -- agent-console's
json.Unmarshal into a struct silently failed on every single event.
Now the raw JSON line is spliced directly into the request body.
Also await all in-flight event posts before posting /agent/end, since
those POSTs were fire-and-forget and could reorder past it on the wire.
Hub relays pi's own --mode json session protocol (session/agent_start/
turn_start/message_*/turn_end/agent_end -- the same event shape Claude
Code sessions use) to agent-console over WebSocket, so concurrent pi
runs inside the pod are observable as real transcripts instead of log
tails. agent-run.js wraps `pi -p --mode json` and relays its stdout
lines to the hub; each invocation gets its own session id, so N
concurrent agents show up as N sessions. Source mounted via ConfigMap
and run with `go run .` (no registry yet, same as the pi container).
api.riotpiao.com has no in-cluster DNS record (getaddrinfo ENOTFOUND
from inside the pod, confirmed against google.com resolving fine and
kong-proxy.api.svc.cluster.local resolving fine) -- it only resolves
via the home network's own DNS. Node's fetch/undici also silently
drops a manually-set Host header (forbidden header per WHATWG fetch
spec), so overriding Host per-request isn't viable either. hostAliases
pinning the hostname to ingress-nginx-controller's ClusterIP lets
pi's models.json baseUrl work completely unchanged -- TLS SNI/Host
still say api.riotpiao.com so cert validation and Kong's Host-based
routing both still work.
pi-coding-agent requires node >=22.19.0 (undici's
webidl.util.markAsUncloneable, added in Node 22) -- node:20-slim
installed fine but crashed on every invocation.
0.1.23 was pi's own internal version string (from local pi --version),
not the npm package's semver -- npm has no such release, so the
container crash-looped on ETARGET. Latest published is 0.84.2.
Namespace + Deployment (2-4 CPU, 4-8Gi mem) running node:20-slim with
pi installed at startup, wired to the homelab-ornith/reasoning/qwen
model providers via the existing model-invoke-apikey. Placeholder
node:20-slim image for now, real harness image to follow.