Files
AgentHub/.env.example
nntrivi2001 c7dbaf2d9f refactor: finish the AgentHub rename and drop the MCP server
Two leftovers from the rebrand, done together because they touch the same
docs and manifests.

**Data directory.** `agent-dashboard` could not simply be renamed in the
source: it names a real directory holding the live SQLite database. Startup
now moves it once, via `migrateLegacyDataDir()` in `server/lib/claude-home.js`
— a same-filesystem rename, so it is instant even at multiple gigabytes and
cannot leave two divergent copies. `getDataDir()` stays PURE and resolves to
the legacy path until the move happens; the first version of this put the
rename inside the getter, which meant `npm run test:server` relocated the
developer's real database as a side effect. The call sits behind
`require.main === module` and ahead of the router requires, because those pull
in db.js, which opens SQLite at module load. A failed rename is silent and the
old directory keeps being used: an unfashionable directory name beats a blank
database beside an unreachable one. Same treatment for the port-discovery file
(`.agent-dashboard.json` → `.agenthub.json`, old one dropped on write).

Not renamed on purpose: the Sagi localStorage keys (renaming silently resets
everyone's prefs) and the PWA manifest `id` (changing it orphans an installed
app).

**MCP server.** 4 tool calls in 46 days across 577,132 events, against 4,556
lines of TypeScript, a 2 MB committed build artifact, and a pre-commit guard
that had already blocked commits twice. Deleted: `mcp/`, the maintainer skills
and `mcp-reviewer` agent, the build check, `plugins/agenthub/.mcp.json`,
`docs/MCP.md`, 12 npm scripts, 5 Makefile targets, and every doc section
describing it.

Kept, because they are unrelated things sharing the name: the Config
Explorer's MCP tab and its API, per-lane Playwright MCP, the agenthub-config
plugin's MCP audit, and the historical `mcp__plugin_agenthub_*` events that
analytics must keep attributing.

Verified: server 1432/1432, client 459/459, tsc clean, prettier clean. Both
directions of the migration proven — a sandbox-HOME test run leaves the legacy
directory untouched, and a real boot moves it with its contents intact.
2026-09-08 09:48:40 +07:00

117 lines
6.3 KiB
Bash

# Claude Code home directory (default: ~/.claude)
# Override this if your Claude Code data is in a different location, e.g.:
# CLAUDE_HOME=~/.codefuse/engine/cc
# CLAUDE_HOME=~/.claude
# ── Server / network ────────────────────────────────────────────────────────
# Port to listen on (default: 4820)
# DASHBOARD_PORT=4820
# Directory the built client is served from in production (default: client/dist).
# The `agenthub` plugin install points this at ~/.claude/agenthub/runtime/client-dist
# because the plugin cache directory is read-only and replaced on every update.
# DASHBOARD_CLIENT_DIST=
# Interface to bind. SECURITY: defaults to 127.0.0.1 (loopback) so the dashboard
# is NOT reachable from the network out of the box (GHSA-gr74-4xfh-6jw9). The
# server reads transcripts, exports all data, and can spawn `claude`, so binding
# it to the network without auth exposes all of that. Only widen the bind if you
# understand the risk — and set DASHBOARD_TOKEN when you do.
# DASHBOARD_HOST=0.0.0.0 # bind all interfaces (LAN-reachable)
# Optional auth token. When set, every /api/* request and the WebSocket must
# present it (Authorization: Bearer <token>, x-dashboard-token header, or
# ?token=). Strongly recommended whenever DASHBOARD_HOST is non-loopback. Unset
# by default — the loopback bind is the trust boundary.
# DASHBOARD_TOKEN=change-me-to-a-long-random-string
# Extra Host-header names allowed besides loopback (comma-separated). Needed when
# you bind to a LAN and reach the dashboard by hostname/IP — entries here pass
# the anti-DNS-rebinding Host allowlist. Also required when Prometheus-in-Docker
# scrapes a host-native dashboard:
# DASHBOARD_ALLOWED_HOSTS=host.docker.internal
# DASHBOARD_ALLOWED_HOSTS=dashboard.internal,192.168.1.50
# ── Background sweeps / sync ─────────────────────────────────────────────────
# Minutes of inactivity before an active session is marked "abandoned" by the
# periodic maintenance sweep (default: 180 = 3h).
# DASHBOARD_STALE_MINUTES=180
# Idle-working timeout (seconds) for recovering a turn cancelled with Esc BEFORE
# any output (which leaves no transcript marker). When the main agent has been
# "working" with no tool in flight and neither a hook nor the transcript has
# advanced for this long, the watchdog moves the session to Waiting (default: 120).
# DASHBOARD_WORKING_IDLE_SECONDS=120
# Dead-session liveness reap. The watchdog lists running `claude` CLI processes
# (ps + lsof on macOS, /proc on Linux) and completes any active session whose
# cwd has no live claude process — recovering sessions whose SessionEnd hook
# was lost because the dashboard was down when the user quit (e.g. Ctrl+C).
# Set to 0 to disable (do this when hooks arrive from another machine); it is
# auto-disabled on Windows and inside containers (default: enabled).
# DASHBOARD_LIVENESS_PROBE=1
# Idle gate (seconds) for WATCHDOG-TICK liveness reaps: a session is only
# completed when its transcript hasn't been written for at least this long
# (the last hook write is the fallback clock when no transcript exists on
# disk), so mid-turn or just-resumed sessions never flicker out on a transient
# probe miss. The startup passes skip this gate entirely — at boot the probe
# alone decides, so sessions quit moments before launch clear immediately
# (default: 60).
# DASHBOARD_LIVENESS_IDLE_SECONDS=60
# Poll interval (ms) for the continuous ~/.claude/projects sync that surfaces
# projects added after startup whose sessions never flow through hooks. The
# fs.watch watcher fires near-instantly regardless; this poll is the safety net.
# Set to 0 to disable the poll while leaving the watcher running (default: 30000).
# DASHBOARD_SESSION_SYNC_MS=30000
# ── Remote Data Sources (SSH multi-machine collection) ───────────────────────
# The dashboard can pull Claude Code history from other machines over SSH:
# it rsyncs each enabled remote's ~/.claude/projects into a sandboxed staging
# dir, feeds it through the same importer used for local history, and tags the
# imported sessions with the source. Authentication defers entirely to the host's
# own SSH stack (~/.ssh/config, ssh-agent, keys, known_hosts) — no passwords or
# secrets are stored here.
# Poll interval (ms) for the background poller that syncs each enabled remote
# source. Set to 0 to disable the poller (manual / on-demand syncs still work)
# (default: 15000 = 15s).
# DASHBOARD_REMOTE_SYNC_MS=15000
# Per-source timeout (ms) for a single remote sync (rsync pull + import) before
# it is aborted (default: 600000 = 10min).
# DASHBOARD_REMOTE_SYNC_TIMEOUT_MS=600000
# Timeout (ms) for the Test SSH probe that verifies a remote source is
# reachable (default: 15000 = 15s).
# DASHBOARD_REMOTE_TEST_TIMEOUT_MS=15000
# Freshness window (ms) for a remote source session's live status. On each sync,
# a remote session whose mirrored transcript changed within this window is kept
# active; once the mirror stops advancing for longer, it is reconciled to
# completed. Remote sessions get no live hooks, so this replaces the local
# liveness/stale sweeps (which skip them). (default: 600000 = 10min).
# DASHBOARD_REMOTE_ACTIVE_WINDOW_MS=600000
# ── Lanes (durable parallel-work units and managed git worktrees) ────────────
# Directory dashboard-managed worktrees are provisioned under. It is also the
# boundary safety check 2 resolves every destructive lane path against: nothing
# outside it can be reset or removed. Keep it readable only by the service user
# (default: ~/.claude/agenthub-lanes).
# LANES_ROOT=~/.claude/agenthub-lanes
# Default base branch a new managed worktree resolves against when the request
# omits `base`. An explicit --base / request body `base` always wins
# (default: main).
# LANE_BASE_BRANCH=main
# Prefix for a new managed worktree's feature branch name, as <prefix><slug>
# (default: feat/).
# LANE_BRANCH_PREFIX=feat/
# Seconds of silence after which a lane whose stage matches /watch|poll/i flips
# from `active` to `dead` liveness. A silent *idle* lane is at rest, not dead
# (default: 300 = 5min).
# LANE_DEAD_SEC=300