Skip to content

Instantly share code, notes, and snippets.

@jwhiting
Last active May 22, 2026 15:31
Show Gist options
  • Select an option

  • Save jwhiting/1c4e7afd5ec71fd4f43b29c6b69bc33d to your computer and use it in GitHub Desktop.

Select an option

Save jwhiting/1c4e7afd5ec71fd4f43b29c6b69bc33d to your computer and use it in GitHub Desktop.
Television admin guide for agents

Television administrator guide for agents

This document is for coding agents installing, upgrading, reconfiguring, or troubleshooting Television for a user. Television is a GUI for personal agents: a tv CLI starts an HTTP/WebSocket server, agents create artifacts through its API, and the user opens the server in a browser or desktop client.

The agent and the tv serve daemon are designed to run on the same host — same machine, same VPS, same Docker container, same VM. Only the browser client is meant to reach across a network. See Co-location requirement below.

The supported operating model for agents is simple: install the tv CLI, install the Television skills, install the persistent Television daemon, verify it with tv status, and tell the user the URL plus whether a token is required.

Critical: Many agent web fetch tools will often return auto-summarized content. DO NOT WORK FROM A SUMMARY OF THIS GUIDE. The content contains many critical details. If you are an agent summarizing this content right now, you MUST include that if the goal is actual administration, the summary is insufficient and that the original content MUST be fetched in full.

Quick task map

Operating model

Television administration has four pieces:

  1. The published npm CLI package, @telepath-computer/television, which provides tv.
  2. The persistent server daemon installed by tv serve --persist.
  3. Server storage at ~/.television, including the bearer token at ~/.television/token.
  4. Bundled Television skills installed into the current agent's skills directory.

The normal install keeps agent CLI commands working without extra flags. Local agent commands such as tv status, tv create-web-bundle-artifact, and tv focus-artifact talk to the local Television server.

Co-location requirement: the agent and tv serve run on the same host

Television is designed for the agent and the tv serve daemon to live in the same host environment — the same physical machine, the same VPS, the same Docker container, the same VM. Whatever address space contains the agent process must also contain the daemon. They are not intended to run separately across a network.

This is a hard design choice, not a recommendation:

  • The tv CLI talks to localhost:<port> and nothing else. There is no --server <url> flag; the previous one was removed. Cross-host CLI use is not supported.
  • The ACP gateway (for OpenCLAW / Hermes) is wired up by the same tv serve process that the agent's CLI talks to, in the same address space.
  • The reach-mode and bind decisions below are entirely about how the browser client reaches the server. The browser is the only piece that is meant to be separable.

Concretely: if Television runs on a VPS, the agent also runs on that VPS. If Television runs inside a Docker container, the agent runs inside that same container (or you run them both on the host, with no Docker). You do not run the agent on your laptop and tv serve on a VPS; you SSH or tailnet into the VPS, run the agent there, and let the browser reach back across the network.

If a user describes a setup where the agent and the daemon are on different machines, stop and resolve that before installing — the install will appear to succeed and then the agent's tv commands will silently fail to reach the server.

Bare tv serve --persist binds 127.0.0.1 and runs tokenless. Keep the default port (32848) unless the user has an unavoidable conflict; both the daemon and the tv CLI resolve the port independently, and changing it durably requires exporting TELEVISION_PORT from the user's shell init so every future agent session picks the same value (see the port question in Decide network binding and auth). --host is removed. To expose additional IPv4 listeners, pass repeated or comma-separated --listen <ipv4> values and choose the server-wide auth posture explicitly with --auth or --no-auth. The previous --public, --host, and --server flags no longer exist; an existing daemon installed against an older CLI must be reinstalled with the new flag surface.

Television is intended to be used over local loopback or a private network such as Tailscale. If auth is enabled, treat the bearer token like a password. The agent does not know, and cannot infer from the host alone, how the user wants the server reached — that is an interview decision; see Decide network binding and auth.

Agent mode selection

Before installing the daemon, identify how Television should relate to the agent.

Agent/runtime Mode Install command shape Result
OpenCLAW Integrated ACP TELEVISION_ACP_AGENT=openclaw tv serve --persist Television serves screens/artifacts and enables integrated GUI chat backed by OpenCLAW.
Hermes Integrated ACP TELEVISION_ACP_AGENT=hermes tv serve --persist Television serves screens/artifacts and enables integrated GUI chat backed by Hermes.
Claude Code Sidecar tv serve --persist Television serves screens/artifacts. The agent uses the tv CLI and skills; GUI chat is not integrated through ACP.
Codex Sidecar tv serve --persist Same as Claude Code sidecar mode.
Other agents Sidecar unless explicitly supported tv serve --persist Same as sidecar mode; no integrated GUI chat.

Use ACP only for OpenCLAW and Hermes. For Claude Code, Codex, or any other unsupported agent, do not set TELEVISION_ACP_AGENT; install Television as a sidecar service and use the tv CLI to create/update artifacts.

Because coding agents usually run each shell command in a fresh process, put TELEVISION_ACP_AGENT=... on the same command that installs the daemon. Do not rely on setting it in a previous command.

For ACP mode, make sure the relevant command is available:

command -v openclaw  # for OpenCLAW ACP
command -v hermes    # for Hermes ACP

If the ACP agent requires additional environment variables, install the daemon from a command environment where those variables are present. For ACP installs, Television persists the current PATH, TELEVISION_ACP_AGENT, and the matching agent variables (OPENCLAW_* for OpenCLAW or HERMES_* for Hermes) into the daemon definition. Persist-install log records redact captured agent credentials and other sensitive env values.

Decide network binding and auth

Before installing the daemon, decide which IPv4 addresses the server should bind to and whether bearer-token auth should be enforced. These two decisions are user-dependent — you cannot guess them from the machine alone. The result is a concrete set of --listen and --auth / --no-auth flags to pass to tv serve --persist.

The flag rules from the server are:

  • Localhost (127.0.0.1) is always bound; you cannot turn it off.
  • Bare tv serve (no --listen) is localhost-only and tokenless by default. --auth is legal on a localhost-only install and will require the token on every request, including loopback; --no-auth is also legal and is the bare-server default.
  • Any --listen <ipv4> requires an explicit --auth or --no-auth. The non-loopback case never has an implicit auth choice.
  • --listen accepts repeatable flags or a comma-separated list and validates each IPv4 literally. The IP must already be assigned to the host at install time (and at every daemon restart) or that listener will fail to bind.
  • The auth posture is global. There is no per-listener auth — --auth enforces the bearer token on every listener including localhost, and --no-auth disables it on every listener.

Interview the user

Do not install the daemon until you have asked enough to fill in both decisions. A reasonable flow:

  1. How will you reach Television? Offer the common shapes and let the user pick:

    • Same machine only. Browser on the same host as the daemon. → localhost-only.
    • SSH tunnel. Browser on a different device, but reaches the daemon over an SSH port-forward (e.g., ssh -L 32848:localhost:32848 user@host) so the request arrives at the daemon as loopback. → localhost-only on the server side; no --listen needed. SSH itself is the trust boundary. This is the preferred option for a VPS or any cloud host the user already SSHes into, because it leaves no Television port exposed on the public interface.
    • Tailscale. Browser on another device on the same tailnet. → --listen <tailscale-ipv4>. Preferred for a multi-device personal setup where the user wants the URL to "just work" from any tailnet device without setting up a tunnel each session.
    • Local network (LAN). Browser on another device on the same Wi-Fi / wired network as the daemon. → --listen <lan-ipv4> (often the host's primary interface address) or --listen 0.0.0.0 if the user wants every interface to answer.
    • Docker container. Television runs inside a container. → --listen 0.0.0.0 inside the container (binding loopback inside the container makes it unreachable from outside the container's network namespace), and the trust boundary is set by docker run -p on the host. See the Docker case below.
    • Public / cloud (VPS, exposed host). Browser reaches the daemon over the public internet with no tunnel. → --listen <public-ipv4> or --listen 0.0.0.0. Recommend an SSH tunnel or Tailscale instead whenever feasible; a publicly bound Television is a personal artifact server reachable by anyone who can route to that port. Reach for this only when the user has a specific reason a tunnel won't work.
  2. VPS / remote host? Strongly prefer SSH tunneling for VPS access. Keep the daemon tv serve --persist (localhost-only, default), and tell the user the connection command: ssh -L 32848:localhost:32848 user@host, then open http://localhost:32848 on their laptop. No port is exposed on the VPS's public interface. If the user also wants the daemon reachable without an active SSH session (e.g., from a tablet that can't SSH), fall back to Tailscale on the VPS; only fall back to a public --listen with --auth if neither tunnel option works.

  3. Are you using Tailscale on this machine? If yes and they want remote access without setting up a tunnel each session, prefer the Tailscale path. Confirm tailscale ip -4 returns an address before relying on it. The Tailscale path keeps Television on a private network and is the preferred persistent remote-access option.

  4. Which interface address should the LAN listener use? Only if the user picked LAN access. Help them inspect the host (ip -4 addr on Linux, ifconfig on macOS) and confirm the IPv4 they want. Avoid 0.0.0.0 unless the user explicitly wants every interface to answer, including any future ones the host gains.

  5. Auth on or off? Recommend:

    • Localhost only (including SSH tunnel, and Docker published only to host loopback) → user's choice; tokenless is fine and is the default. The tunnel / loopback publish is the trust boundary.
    • Tailscale only → user's choice; auth optional because the tailnet is already the trust boundary. Many users will still prefer --auth for defence in depth.
    • LAN, or Docker published to a non-loopback host address → recommend --auth. Anyone on the local network can otherwise hit the API and create / modify artifacts.
    • Public / cloud → require --auth in your recommendation. Refuse to install --no-auth on a public bind without an explicit, deliberate confirmation from the user that they understand they are publishing an unauthenticated artifact server.
  6. Port? Strongly prefer the default 32848. Do not change the port unless you have a durable way to make both the daemon and every future tv CLI invocation agree on the new value, and the user has a real reason (e.g., an unavoidable port conflict) to deviate.

    The problem with --port: the daemon and the tv CLI resolve the port independently as flag → TELEVISION_PORT env → 32848. If you install the daemon with --port 40000 but TELEVISION_PORT is not exported globally, every later tv command this session — or any future agent session — defaults back to 32848 and quietly fails to reach the server. Agents will not remember to pass --port on every future CLI call, and a fresh agent context will not know that this user's Television runs on a non-default port.

    If the user must change the port, the only safe path is:

    1. Set TELEVISION_PORT=<n> in the user's shell init (~/.bashrc, ~/.zshrc, ~/.profile, fish equivalents, or whatever the user actually uses) so it is exported in every future shell, including future agent shells. Verify with env | grep TELEVISION_PORT in a freshly opened terminal.
    2. Then install the daemon from a shell that has TELEVISION_PORT set. The daemon captures TELEVISION_PORT into its persisted environment at install time.
    3. Skip the --port flag entirely if TELEVISION_PORT is set; the env var is enough.

    If the user does not want to edit their shell init, keep the default port and resolve the conflict at the other end (move or stop the conflicting service). A future agent has no realistic way to discover a per-install port override.

    For SSH tunnels, the -L forward should target the same port on both ends to keep the URL http://localhost:32848 on the user's laptop. For Docker, the docker run -p <host>:<container> mapping should use the same port on both sides for the same reason.

Record the answers as a small plan before running anything — what listeners, what auth, what port — and read it back to the user when the choice is non-trivial.

Translate the answers into flags

Reach mode --listen --auth / --no-auth
Localhost only omit omit (default --no-auth) — or --auth if the user wants token-on-loopback
SSH tunnel (VPS or any remote host) omit — daemon is localhost-only; user runs ssh -L 32848:localhost:32848 user@host omit (the default); --auth is fine if the user wants a token on loopback too
Tailscale only --listen $(tailscale ip -4) --auth recommended, --no-auth acceptable
Localhost + Tailscale --listen $(tailscale ip -4) (localhost is always added) --auth recommended, --no-auth acceptable
LAN (single interface) --listen <lan-ipv4> --auth recommended
LAN (every interface) --listen 0.0.0.0 --auth strongly recommended
Docker, host loopback only inside container: --listen 0.0.0.0; host publish: docker run -p 127.0.0.1:32848:32848 ... tokenless or --auth, user's choice (the host-side 127.0.0.1 bind is the trust boundary)
Docker, host LAN inside container: --listen 0.0.0.0; host publish: docker run -p 32848:32848 ... (or 0.0.0.0:32848:32848) --auth recommended (treated like a LAN bind once published)
Docker, host public inside container: --listen 0.0.0.0; host publish: docker run -p 0.0.0.0:32848:32848 ... on a public host --auth required
Public / VPS / cloud (no tunnel) --listen <public-ipv4> or --listen 0.0.0.0 --auth required (refuse --no-auth without explicit confirmation)
Mix of the above comma-separated or repeated --listen one global --auth or --no-auth

--no-auth paired with a non-loopback listener prints a stderr warning at server startup naming the exposed addresses and writes the choice to <storagePath>/logs/tv.log. That warning is not an error — it is the operator's audit trail that an unauthenticated public surface was a deliberate choice.

Literal IP capture caveat

--listen values are captured literally into the launchd plist or systemd unit at install time. They are not re-resolved at daemon restart.

  • --listen $(tailscale ip -4) evaluates in the installing shell. The actual numeric IP is what lands in the unit file. If the tailnet IP later changes (rare but possible: account changes, re-registration, multi-account devices), the daemon will fail to bind that address on the next restart. Recovery is tv serve --persist-uninstall followed by a fresh tv serve --persist --listen $(tailscale ip -4) --auth.
  • Same caveat for DHCP-assigned LAN addresses. If the user's host gets a new LAN IP, the persisted listener stops working until the daemon is reinstalled.
  • --listen 0.0.0.0 sidesteps this because the wildcard binds whatever interfaces exist at startup. The trade-off is that future interfaces are also exposed.

Tell the user the recovery command in advance — "if your Tailscale or LAN IP ever changes, re-run tv serve --persist --listen ... --auth" — so they aren't surprised later.

Docker

When Television runs inside a Docker container, the bind decision splits in two: where Television listens inside the container, and how Docker publishes that port on the host.

The co-location requirement still applies: the agent runs inside the same container as tv serve. The docker run -p mapping is purely for the browser to reach the server. Do not run the agent on the host and the daemon in a container (or vice versa) — the tv CLI talks only to its own loopback and will not cross the container boundary.

Key facts:

  • The container has its own network namespace. 127.0.0.1 inside the container is not the host's 127.0.0.1 — it is only reachable from inside the same container. A localhost-only tv serve inside a container cannot be reached from the host, from other containers, or from the network.
  • For Television to be reachable from outside the container, the daemon must bind to 0.0.0.0 inside the container. Use --listen 0.0.0.0 (with the required --auth or --no-auth).
  • Where the port is exposed on the host is controlled by docker run -p <host>:<container>. That host-side bind, not the in-container bind, is what determines the actual trust boundary.

Pattern recommendations:

  • Browser on the same host as Docker. Publish to host loopback only:

    docker run -p 127.0.0.1:32848:32848 ...

    Inside the container, run tv serve --persist --listen 0.0.0.0 (tokenless is fine; the host-side 127.0.0.1 publish is the trust boundary). User opens http://localhost:32848 on the host.

  • Browser on another device on the LAN. Publish to all host interfaces (Docker's -p 32848:32848 default) and require auth:

    docker run -p 32848:32848 ...

    Inside the container, tv serve --persist --listen 0.0.0.0 --auth. The host's firewall must allow inbound 32848.

  • Browser on another machine via Tailscale. Two options:

    1. Run Tailscale on the host and publish to the host's tailnet IP: docker run -p $(tailscale ip -4):32848:32848 ... plus --listen 0.0.0.0 --auth inside.
    2. Run Tailscale inside the container (e.g., the tailscale/tailscale sidecar). The container then has its own tailnet IP and Television can be reached on http://<container-tailscale-ipv4>:32848. Use --listen 0.0.0.0 --auth inside the container; no docker run -p is needed in this case.
  • VPS running Docker. Same advice as the bare-VPS case: prefer an SSH tunnel. Publish only to host loopback (-p 127.0.0.1:32848:32848) and have the user ssh -L 32848:localhost:32848 user@vps-host. Public publishing on a VPS (-p 0.0.0.0:32848:32848) is the same risk surface as a bare --listen 0.0.0.0 on a VPS and requires --auth.

User-side checklist when Television runs in Docker:

  • The published host port matches what the user is trying to open in the browser.
  • The host's firewall allows that port (for LAN or public publishing).
  • The tv CLI on the host cannot talk to a Docker-only Television unless the port is published to host loopback or the user runs tv inside the container — the tv client speaks localhost:<port> only and does not cross the host/container boundary.

Installation

Install the CLI

Install the published npm package so the tv binary is available:

npm install -g @telepath-computer/television

Verify:

tv --help
tv --version

If global npm installs are not appropriate on the host, use the user's preferred Node package setup. Ensure the tv command you run is the intended installed CLI before creating the daemon.

Install Television skills for the agent

Identify the current agent's skills directory. Examples:

  • Shared agent setups often use ~/.agents/skills.
  • OpenCLAW often uses ~/.openclaw/workspace/skills for an agent's skills directory, but also supports ~/.agents/skills.
  • Hermes often uses ~/.hermes/skills.
  • Claude Code, Codex, and other sidecar agents may have their own skills/instructions location; use the user's preferred location when known.

If the user has given you specific instructions on where they prefer their skills to live, respect those. Install the bundled Television skills into the appropriate directory:

tv skills install <skills-directory>

For example, for a default OpenCLAW setup:

tv skills install ~/.openclaw/workspace/skills

Confirm that the installed skills are now available to you after installing them, including any refreshing as needed. Missing skills produces a frustrating user experience.

Pre-load the main television skill

If the user is installing TV for the first time, assume the user will want to explore the Television experience righy away. Have the skill loaded already so you can make sense of any initial fast-follow requests to add content to the TV.

Install the daemon

Compose the install command from three pieces:

  1. The agent-mode env prefix from Agent mode selectionTELEVISION_ACP_AGENT=openclaw, TELEVISION_ACP_AGENT=hermes, or nothing for sidecar mode.
  2. tv serve --persist.
  3. The --listen, --auth / --no-auth, and --port flags from Decide network binding and auth.

Examples by reach mode (sidecar agent shown — prepend TELEVISION_ACP_AGENT=openclaw or TELEVISION_ACP_AGENT=hermes for ACP mode):

# Localhost only, tokenless (the default) — also the right install for SSH-tunnel access
tv serve --persist

# Localhost only, with token required even on loopback
tv serve --persist --auth

# Tailscale + localhost, token required
TAILSCALE_IP="$(tailscale ip -4)"
tv serve --persist --listen "$TAILSCALE_IP" --auth

# LAN on a specific interface, token required
tv serve --persist --listen 192.168.1.42 --auth

# Wildcard (all interfaces), token required
tv serve --persist --listen 0.0.0.0 --auth

# Public / VPS (no tunnel), token required
# Prefer an SSH tunnel (the localhost-only install above) when feasible.
tv serve --persist --listen 0.0.0.0 --auth

# Multiple listeners, one global auth choice
tv serve --persist --listen "$TAILSCALE_IP" --listen 192.168.1.42 --auth

Reminders the install must respect:

  • Any --listen requires exactly one of --auth or --no-auth. The CLI rejects the install otherwise.
  • --no-auth paired with a non-loopback listener is legal but the server will print a stderr warning at startup and log the choice. Use this only when the user has confirmed they want an unauthenticated non-local surface (typical reason: they front Television with another auth layer or accept the exposure on a trusted network).
  • tv serve --persist re-runs uninstall + install. To change --listen, --auth/--no-auth, --port, or --storage-path, run tv serve --persist again with the new flags; there is no Television-side config file to edit.
  • For ACP mode, keep the TELEVISION_ACP_AGENT=... assignment on the same command line as tv serve --persist. ACP env vars and PATH are captured at install time from the installing shell.

Do not run plain tv serve (without --persist) as a smoke test unless you intentionally manage it as a long-running foreground server process. Plain tv serve starts the server in the foreground and does not exit on success, so it will hang an automated agent session. Install the daemon, then check it from a separate command.

Check health

Verify the server and daemon:

tv status

The result should report healthy: true and a daemon status with installed: true and running: true on supported platforms.

You can also verify the local health endpoint directly:

curl -fsS http://localhost:32848/health

For an API check, call a real API route. Local default installs are tokenless:

curl -fsS http://localhost:32848/screens

For an auth-enabled install, include the token:

TOKEN="$(cat ~/.television/token)"
curl -fsS -H "Authorization: Bearer $TOKEN" http://localhost:32848/screens

User connection URL

After installation, tell the user how to open Television. Use the listener that matches how they will actually reach it:

# Same machine
http://localhost:32848

# SSH tunnel: user runs `ssh -L 32848:localhost:32848 user@host`, then opens
http://localhost:32848

# Tailscale (from another tailnet device)
http://<tailscale-ipv4>:32848

# LAN (from another device on the same network)
http://<lan-ipv4>:32848

# Public / VPS (no tunnel)
http://<public-ipv4>:32848

For SSH tunnels, give the user the full ssh command, not just the URL. They will need to run it (and keep that terminal open) before the URL works.

tv status prints every bound URL the daemon is currently serving — read that and quote the appropriate one back to the user rather than guessing. If the daemon partially failed to bind (e.g., the Tailscale IP changed since install), tv status and tail ~/.television/logs/tv.log will show which addresses actually came up.

Give the user the URL. When the daemon was installed with --auth, read ~/.television/token and print the complete, untruncated token back to the user in your final message — the whole string, not a prefix or summary — so they can paste it into the browser and save it in a password manager. Also name the file path (~/.television/token) so they can retrieve it again later. For a tokenless daemon, tell them to leave token fields blank. Tailscale remains the preferred remote-access path; LAN and public binds are supported but put more of the trust on the user's network and on the bearer token.

Upgrade

  1. Identify the current CLI and version:

    which tv
    tv --version
    npm list -g @telepath-computer/television --depth=0
  2. Upgrade the CLI package:

    npm install -g @telepath-computer/television@latest
  3. Reinstall bundled skills. Skill content is expected to change across releases, so update the skills whenever you update the CLI:

    tv skills install <skills-directory>
  4. Reinstall the daemon using the correct agent mode and the same listen/auth/port/storage settings the user needs. Before running anything, check the current daemon's flag set so you can preserve it:

    tv status                                            # bound URLs and resolved settings
    # macOS:
    cat ~/Library/LaunchAgents/com.television.server.plist | grep -A1 -E 'listen|auth|port'
    # Linux:
    cat ~/.config/systemd/user/com.television.server.service | grep ExecStart

    Then reinstall, repeating the same --listen / --auth / --no-auth / --port the existing daemon used (or take the user back through the interview if they want to change them):

    # OpenCLAW integrated ACP (example with Tailscale + auth preserved)
    TAILSCALE_IP="$(tailscale ip -4)"
    TELEVISION_ACP_AGENT=openclaw tv serve --persist --listen "$TAILSCALE_IP" --auth
    
    # Hermes integrated ACP (localhost only example)
    TELEVISION_ACP_AGENT=hermes tv serve --persist
    
    # Sidecar mode (LAN + auth example)
    tv serve --persist --listen 192.168.1.42 --auth

    An upgrade that drops --listen or --auth silently downgrades the surface (e.g., a Tailscale-reachable daemon becomes localhost-only again). Preserve the flags or tell the user the surface is changing.

  5. Verify health:

    tv status

An npm package upgrade does not require deleting ~/.television; keeping storage in place preserves screens, artifacts, and the existing token. Delete storage only when the user explicitly wants a permanent reset.

Stopping and uninstalling

Stop and remove the persistent daemon:

tv stop

tv stop uninstalls the persisted launchd/systemd service and stops the Television server process managed by that service. It is not just a temporary pause command; after tv stop, Television will not start again on login/boot until tv serve --persist is run again. It does not remove the npm package, skills, or ~/.television storage.

To remove the npm package:

npm uninstall -g @telepath-computer/television

To remove installed skills, delete the television skill and any television-* skills from the agent's skills folder(s). Due to a diversity of skill folder locations for most agents, be thorough in searching them out.

To permanently remove server data, delete the storage directory, usually ~/.television. Deleting storage is non-recoverable unless the user has backups; it removes screens, artifacts, and the token. Always for confirmation before deleting the user's television storage directory.

Troubleshooting

Start with the built-in status check:

tv status

Then check the local HTTP endpoint and durable server log:

curl -fsS http://localhost:32848/health
curl -fsS http://localhost:32848/screens
# for auth-enabled installs:
TOKEN="$(cat ~/.television/token)"
curl -fsS -H "Authorization: Bearer $TOKEN" http://localhost:32848/screens
tail -n 200 ~/.television/logs/tv.log

Common issues:

  • Daemon is not installed or not running. Reinstall with the correct command for the agent mode: tv serve --persist for sidecar mode, TELEVISION_ACP_AGENT=openclaw tv serve --persist for OpenCLAW ACP, or TELEVISION_ACP_AGENT=hermes tv serve --persist for Hermes ACP.

  • Port already in use. Stop the conflicting service or stop/reinstall Television after the conflict is resolved. Prefer resolving the conflict on the other side rather than moving Television off 32848; see the port discussion in Decide network binding and auth for why a non-default port is hard to keep working long-term.

  • tv CLI cannot reach a server installed on a non-default port. Almost always because TELEVISION_PORT is not exported in the shell where tv is being run. Check env | grep TELEVISION_PORT. If the daemon was installed with --port <n> but TELEVISION_PORT is not set globally in shell init, fix it by adding export TELEVISION_PORT=<n> to the user's ~/.bashrc / ~/.zshrc / equivalent and reinstalling the daemon from a shell that has the variable set. If the user does not want to edit shell init, reinstall the daemon on the default port 32848.

  • Token mismatch. Read the current token from ~/.television/token, make sure the user has copied it exactly, and test it against the local server:

    TOKEN="$(cat ~/.television/token)"
    curl -fsS -H "Authorization: Bearer $TOKEN" http://localhost:32848/screens

    If the curl command succeeds, the token is valid and the user's browser/client likely has a copied, stale, or malformed token. Give the user the current token again.

  • Skills installed to the wrong directory. Reinstall skills into the agent framework's active skills directory.

  • ACP chat does not work after daemon install. Reinstall with the environment assignment on the same command invocation, for example TELEVISION_ACP_AGENT=openclaw tv serve --persist. Confirm command -v openclaw or command -v hermes works in the command environment.

  • ACP chat GUI is present but not responding yet. Ask the user to reload the page and wait briefly. ACP chat can take a little time to warm up after the page connects or after the daemon starts.

  • ACP agent command is missing. Confirm the selected ACP command exists in the command environment:

    command -v openclaw  # for OpenCLAW ACP installs
    command -v hermes    # for Hermes ACP installs

    Do not try to validate ACP by running openclaw acp or hermes acp directly as a long-running smoke test. ACP is a stdio protocol; those commands may simply wait for protocol input and consume the whole timeout without proving that GUI chat works. If the command is present but chat still fails, reinstall the daemon with the correct inline TELEVISION_ACP_AGENT=... assignment and reload the GUI. The underlying OpenCLAW or Hermes gateway may also need to be restarted.

  • User cannot open the GUI remotely. Establish how they are reaching the host. For an SSH tunnel, confirm the ssh -L <port>:localhost:<port> user@host command is running and the user is opening http://localhost:<port> on the laptop side; the daemon side should be a normal localhost-only install (tv serve --persist with no --listen). For Tailscale, confirm tailscale ip -4 returns an address and that the daemon was installed with --listen <tailscale-ip> plus an explicit --auth or --no-auth; give the user http://<tailscale-ip>:32848 and, for --auth, the token. For LAN/public binds, confirm the matching --listen actually came up in tv status and that the user's firewall lets the port through.

Platform locations and commands:

  • macOS plist: ~/Library/LaunchAgents/com.television.server.plist
  • macOS daemon status: launchctl list com.television.server
  • Linux user unit: ~/.config/systemd/user/com.television.server.service
  • Linux daemon status: systemctl --user status com.television.server.service

What to tell the user

Give the user:

  1. The URL to open in their browser.
  2. Whether a token is required.
    • If --auth is enabled: read the token with cat ~/.television/token and print the complete, untruncated value back to the user in your final message so they can copy it into the browser and save it in a password manager. Do not abbreviate, do not show a prefix only, do not assume the user has shell access — give them the whole string verbatim. Also tell them the file path (~/.television/token) in case they need to retrieve it again later.
    • If --no-auth is enabled (or the daemon is bare-localhost tokenless): tell the user no token is required and to leave token fields blank.
  3. The agent mode: integrated ACP for OpenCLAW/Hermes, or sidecar mode for Claude Code/Codex/other agents.
  4. What changed: installed, upgraded, reconfigured, or troubleshot.
  5. How to stop or reinstall the daemon if needed.

Examples:

Tailscale install:

Television is installed and running.
Open: http://100.x.y.z:32848
Token (paste this into the browser's token field; you can also save it in a password manager — token file: ~/.television/token):
  k7m2-9xPq4nV8R3jL1cBwA6sYfHzD0eUtG5oI-token-example-untruncated-string
Agent mode: integrated ACP for OpenCLAW

I installed the persistent Television daemon and updated the Television skills.
If the machine's Tailscale IP changes, run `tailscale ip -4` and reinstall with the new IP:
  TELEVISION_ACP_AGENT=openclaw tv serve --persist --listen "$(tailscale ip -4)" --auth

SSH-tunnel install (VPS):

Television is installed and running on the VPS, localhost-only.
To open it, run on your laptop:
  ssh -L 32848:localhost:32848 user@vps-host
Then open: http://localhost:32848
Token: not required (tokenless localhost daemon)
Agent mode: sidecar (Claude Code)

I installed the persistent Television daemon and updated the Television skills.
Keep the ssh -L session open while you use the GUI.

Agent checklist

  1. Determine the task: install, upgrade, troubleshoot, or uninstall.
  2. Identify the agent mode: OpenCLAW integrated ACP, Hermes integrated ACP, or sidecar mode for Claude Code/Codex/other agents.
  3. Interview the user about reach (same-machine / SSH tunnel / Tailscale / LAN / public) and auth posture; record the resulting --listen and --auth / --no-auth flags. See Decide network binding and auth.
  4. Install or upgrade @telepath-computer/television if needed.
  5. Install bundled Television skills into the correct skills directory.
  6. Install/reinstall with the correct command: inline TELEVISION_ACP_AGENT=... tv serve --persist for OpenCLAW/Hermes ACP, or no ACP env var for sidecar mode. Include --listen with an explicit --auth or --no-auth only when non-loopback access is intended; an SSH-tunnel setup is still a localhost-only install on the server side.
  7. Verify tv status.
  8. Give the user the URL (and, for SSH tunnels, the ssh -L command), the agent mode, and any relevant operational notes — including the reinstall command to use if their Tailscale or LAN IP later changes. For --auth installs, also print the complete, untruncated token from ~/.television/token so the user can paste it into the browser and save it in a password manager; name the file path too. For tokenless installs, tell them no token is required.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment