diff --git a/.github/workflows/wiki-sync.yml b/.github/workflows/wiki-sync.yml new file mode 100644 index 00000000..2f830a60 --- /dev/null +++ b/.github/workflows/wiki-sync.yml @@ -0,0 +1,109 @@ +name: Sync Wiki + +# Publishes docs/wiki/ to the repository's GitHub wiki. +# +# The wiki is a separate git repo with no CI and no review, so the source of truth +# lives in docs/wiki/ and this workflow mirrors it. Browser edits to the wiki are +# overwritten by the next sync; fix pages with a PR against docs/wiki/ instead. +# +# One-time setup: GitHub only creates .wiki.git once the first page has been +# saved in the browser. Save a stub page at /wiki/_new before the first run. +# +# Token: GITHUB_TOKEN can push to the wiki on most repos but not all. If a run fails +# with 403, add a fine-grained PAT with wiki write access as the WIKI_TOKEN secret; +# it is preferred automatically when present. Note the 403 usually surfaces on the +# PUSH, not the clone: this repo is public, so a read-only token still clones the +# wiki fine. Both steps carry the hint. + +on: + push: + branches: [master] + paths: + - 'docs/wiki/**' + - '.github/workflows/wiki-sync.yml' + workflow_dispatch: + +concurrency: ${{ github.workflow }} + +jobs: + sync: + name: Push docs/wiki to the wiki + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout repo + uses: actions/checkout@v6 + + - name: Clone wiki + env: + WIKI_TOKEN: ${{ secrets.WIKI_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + if ! git clone "https://x-access-token:${WIKI_TOKEN}@github.com/${GITHUB_REPOSITORY}.wiki.git" wiki 2>"${RUNNER_TEMP}/clone-err.txt"; then + cat "${RUNNER_TEMP}/clone-err.txt" + echo "::error::Could not clone ${GITHUB_REPOSITORY}.wiki.git. If this says 'Repository not found', the wiki has never had a page: save one at https://github.com/${GITHUB_REPOSITORY}/wiki/_new and re-run. If it says 403, add a WIKI_TOKEN secret." + exit 1 + fi + + - name: Mirror pages + run: | + set -euo pipefail + + # The mirror deletes before it copies, so an empty source would wipe + # every published page and the commit step would happily push that. A + # MISSING directory already fails safely (cp aborts under set -e); an + # empty one does not, so check explicitly. This is the one failure mode + # here that destroys something a browser edit cannot get back. + if [ ! -d docs/wiki ]; then + echo "::error::docs/wiki does not exist. Refusing to mirror, which would delete the entire published wiki." + exit 1 + fi + pages=$(find docs/wiki -maxdepth 1 -name '*.md' | wc -l) + if [ "$pages" -eq 0 ]; then + echo "::error::docs/wiki contains no .md pages. Refusing to mirror, which would delete the entire published wiki." + exit 1 + fi + echo "Mirroring ${pages} pages." + + find wiki -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} + + cp -R docs/wiki/. wiki/ + + - name: Stamp the documented version + run: | + set -euo pipefail + # _Footer.md renders on every page and used to carry a hand-written + # version, which went stale on every release because nothing refreshed + # it. It carries {{VERSION}} instead and the series is stamped here. + series="$(node -p "require('./package.json').version.split('.').slice(0,2).join('.') + '.x'")" + # grep exits 1 when it matches nothing, which under `set -o pipefail` + # would fail the step instead of warning, so test before substituting. + if grep -rlq '{{VERSION}}' wiki/; then + grep -rlZ '{{VERSION}}' wiki/ | xargs -0 -r sed -i "s/{{VERSION}}/${series}/g" + else + echo "::warning::No {{VERSION}} placeholder found in docs/wiki. The published version line can no longer be refreshed automatically." + fi + if grep -rq '{{VERSION}}' wiki/; then + echo "::error::A {{VERSION}} placeholder survived substitution and would be published verbatim." + exit 1 + fi + echo "Stamped version ${series}." + + - name: Commit and push + run: | + set -euo pipefail + cd wiki + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add -A + if git diff --quiet --cached; then + echo "Wiki already up to date." + exit 0 + fi + git commit -m "docs: sync wiki from docs/wiki @ ${GITHUB_SHA:0:7}" + if ! git push 2>"${RUNNER_TEMP}/push-err.txt"; then + cat "${RUNNER_TEMP}/push-err.txt" + echo "::error::Could not push to ${GITHUB_REPOSITORY}.wiki.git. A 403 here means the token can read the wiki but not write it, which is the usual GITHUB_TOKEN case: add a fine-grained PAT with wiki write access as the WIKI_TOKEN secret." + exit 1 + fi diff --git a/docs/wiki/Agent-CLIs.md b/docs/wiki/Agent-CLIs.md new file mode 100644 index 00000000..6c34f274 --- /dev/null +++ b/docs/wiki/Agent-CLIs.md @@ -0,0 +1,207 @@ +# Agent CLIs + +Codeman drives seven run modes: six agent CLIs plus a plain shell. This page covers picking +one, setting it up, and the differences that actually change how you work. + +## The seven modes + +| Mode | CLI | Get it | +| -------------------- | ---------------------------- | ---------------------------------------------------------------------- | +| **Claude Code** | `claude` | [docs.anthropic.com](https://docs.anthropic.com/en/docs/claude-code) | +| **OpenCode** | `opencode` | [opencode.ai](https://opencode.ai) | +| **Codex** | `codex` | [developers.openai.com/codex/cli](https://developers.openai.com/codex/cli) | +| **Gemini** | `gemini` | [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) | +| **Antigravity** | `agy` | [antigravity.google](https://antigravity.google) | +| **Pi** | `pi` | [pi.dev](https://pi.dev) | +| **Terminal / Shell** | your `$SHELL` | Already installed. | + +Any combination works, including all of them. The run mode is chosen per session from the +arrow beside the **Run** button, so one case can have a Claude session and a Codex session +open side by side. + +## Codeman does not manage your logins + +Install each CLI yourself and log it in once by hand. Codeman never collects, stores, or +refreshes your CLI credentials. It launches the binary and attaches to the result. + +The one place credentials are touched is [Docker Cases](Docker-Cases), where host +credentials are copied into a container read-only at launch so you do not have to log in +again inside it. Even there, the container keeps its own copies and never writes back to +your host credential stores. + +## Making a CLI visible to Codeman + +Codeman resolves each binary from the environment the **server** runs in, which is not +necessarily the shell you tested in. + +```bash +codeman doctor # what Codeman can actually see +codeman doctor --json +``` + +If a CLI is installed but a Run button for it never appears: + +1. Check `which ` in a plain login shell, not just your interactive one. +2. If Codeman runs as a service, remember that launchd hands a job + `/usr/bin:/bin:/usr/sbin:/sbin`. `codeman service install` bakes your PATH into the unit + precisely to avoid this; a hand-written plist or unit will not. +3. Restart the server after installing a new CLI. + +`pi` is additionally version-probed rather than trusted by name, because `pi` is a generic +enough command that something else on your PATH may answer to it. + +## Claude is the reference mode + +A number of Codeman features exist only for Claude sessions. This is structural, not a +backlog: they depend on Claude Code's hook system, or on parsing Claude's specific terminal +output. The other CLIs expose no equivalent. + +| Feature | Claude | Other CLIs | +| ------------------------------------------------ | ------ | --------------------------------------------------- | +| Sessions, tabs, scrollback, exactly-once input | Yes | Yes | +| Respawn cycling and unattended runs | Yes | Yes | +| Cron jobs | Yes | Yes | +| Docker cases, remote SSH cases | Yes | Yes | +| Precise idle detection (hook-driven) | Yes | Output-stabilization fallback, coarser | +| Auto-resume when a usage limit resets | Yes | No | +| Plan usage chip | Yes | No | +| Approvals Inbox | Yes | No | +| Read My Mind | Yes | No | +| Ralph loop and its task tracker | Yes | No | +| Subagent and team windows | Yes | No | +| Model, effort, and ultracode controls | Yes | No | +| `stop` and `blocked` wait signals | Yes | 400 if you ask for them explicitly | +| The bundled agent skill | Yes | No | + +Everything that makes a session a session works everywhere. What is Claude-only is mostly +the machinery that needs to know *what* the agent is doing rather than *that* it is doing +something. + +## Per-CLI notes + +### Claude Code + +The defaults you will care about, all under **App Settings**: + +- **Model** (Models section). Written into the case's `.claude/settings.local.json` as a + soft default, so `/model` still works mid-session. The 1M-context Opus variant is a + switch on the model card rather than a separate model. +- **Effort** (`low` through `max`) or **ultracode** for dynamic multi-agent workflows. Also + a soft default: `/effort` overrides it any time. Effort is deliberately not passed as an + environment variable, because that would hard-lock it and block in-session switching. +- **Startup permission mode** (Agents & CLIs section). The default is + `--dangerously-skip-permissions`, which is why the security model matters. You can switch + new sessions to Anthropic's classifier-guarded `auto` mode, normal prompting, or an + explicit allowed-tools list. + +**Separate Claude accounts per session.** Set `CLAUDE_CONFIG_DIR` in a session's environment +overrides to point it at a different Claude config directory, which is how you run one +session on a client's subscription and another on your own. One caveat: a relocated config +directory writes transcripts outside `~/.claude/projects`, which blinds the response viewer, +subagent windows, ultracode panel, and Read My Mind for that session. Symlink `projects` +back into the shared tree to keep them working: + +```bash +ln -s ~/.claude/projects /projects +``` + +### OpenCode + +Renders its own TUI, so Codeman treats readiness as output stabilization rather than +watching for a prompt marker. Requires tmux, with no direct-PTY fallback, because its +environment is injected through socket-scoped `tmux setenv` rather than the command line. + +Integration detail: [`docs/opencode-integration.md`](https://github.com/Ark0N/Codeman/blob/master/docs/opencode-integration.md). + +### Codex + +Two behaviours that are deliberate and worth knowing: + +- **Predictive echo instead of buffered echo.** Codex's composer reacts to every keystroke, + a `/` opens a live-filtering picker, arrows edit server-side state. Buffering keystrokes + until Enter starved it, so Codex paints each keystroke at the predicted cell while the + bytes on the wire stay byte-identical to what you typed. +- **The wheel is not forwarded** into its transcript. Codex ignores the mouse reports + Codeman would send, so forwarding produced a dead wheel. Scrolling in a Codex session is + local scrollback. + +### Gemini + +Enterprise only, since Google's June 2026 consumer cutover. Its environment allowlist +includes the broad `GOOGLE_*` namespace, deliberately, because Vertex AI authentication +needs `GOOGLE_CLOUD_PROJECT`, `GOOGLE_APPLICATION_CREDENTIALS`, and +`GOOGLE_GENAI_USE_VERTEXAI`. That is the loosest allowlist entry in Codeman and it affects +only the CLI you spawned yourself. + +### Antigravity + +Google's successor to the consumer Gemini CLI, invoked as `agy`. It keeps all of its state +in `~/.gemini/antigravity-cli/`, so the credential handling that applies to Gemini applies +to it as well. + +### Pi + +Pi needs the opposite instincts from every other CLI here. + +- **It has no permission prompts and no sandbox.** There is no bypass flag to send, and + Codeman does not invent one. +- **Its privileged setting is project trust**, a three-way `--approve` / `--no-approve` / + unset. Approving trust makes Pi **execute repo-local `.pi/extensions` TypeScript**, so + point it at a repository you trust. In multi-user mode, a user without an explicit grant + gets `--no-approve` even when no configuration exists. +- **Authentication is `/login` inside the session**, or the server process's own + environment. Pi's roughly 34 provider keys (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, + `HF_TOKEN`, and so on) share no common prefix, and the environment allowlist is global + rather than per mode, so admitting them for Pi would widen the allowlist for every mode at + once. They stay out. + +Guide: [`docs/pi-integration.md`](https://github.com/Ark0N/Codeman/blob/master/docs/pi-integration.md). + +### Terminal / Shell + +A plain shell in a tmux session. No agent, no hooks, no idle detection. + +On phones a shell session automatically swaps the keyboard accessory bar for terminal +controls: Ctrl, Esc, Tab, arrows, paste. **Ctrl is a one-shot modifier**: tap it, then tap a +letter, and the control byte is sent. It disarms on use, on a second tap, on any other +accessory key, on a session switch, and when the keyboard closes. Details in +[Mobile Guide](Mobile-Guide). + +## Environment overrides + +Per-session environment variables are set when creating a session and persist across +respawns. Which variables are accepted depends on the mode: + +| Mode | Allowed prefixes | +| ----------- | --------------------------------- | +| Claude | `CLAUDE_CODE_*`, plus the exact key `CLAUDE_CONFIG_DIR` | +| OpenCode | `OPENCODE_*` | +| Codex | `CODEX_*` | +| Gemini | `GEMINI_*`, `GOOGLE_*` | +| Antigravity | `ANTIGRAVITY_*` | +| Pi | `PI_*` | + +Anything outside the allowlist is rejected at the schema. This is intentional: the allowlist +is one global list, so widening it for one CLI widens it for all of them. + +Two things that deliberately do **not** travel as environment variables: **effort**, because +an environment variable hard-locks it and blocks `/effort`, and **model**, which is written +into the case's `.claude/settings.local.json` so that `/model` keeps working. + +## Choosing a mode + +- **Claude Code** if you want every Codeman feature. Unattended overnight runs, usage-limit + auto-resume, the Approvals Inbox, and subagent visualization all assume it. +- **Codex, OpenCode, Gemini, Antigravity** when you prefer that agent or that model. You get + the session layer, respawn, cron, Docker, and remote SSH; you do not get the hook-driven + features. +- **Pi** if you want a fast, unsandboxed agent and you understand what project trust does. +- **Shell** for the times you want a terminal on your phone with no agent at all. It is a + genuinely useful mode, not a fallback. + +## Read next + +- [Core Concepts](Core-Concepts) - run modes versus location overlays. +- [Settings Reference](Settings-Reference) - model, effort, and permission-mode settings. +- [Keeping Agents Running](Keeping-Agents-Running) - what idle detection does per mode. +- [Security](Security) - what skipping permission prompts actually means. diff --git a/docs/wiki/Autonomous-Loops.md b/docs/wiki/Autonomous-Loops.md new file mode 100644 index 00000000..54243e33 --- /dev/null +++ b/docs/wiki/Autonomous-Loops.md @@ -0,0 +1,97 @@ +# Autonomous Loops + +Two features that go further than "keep the session going": the **Ralph loop**, which works +a task list to completion in one session, and the **Orchestrator**, which turns a goal into +a phased plan and drives it across agents. + +Both are Claude-only, both are off by default, and neither is where to start. If what you +want is an agent that keeps working overnight, that is +[Keeping Agents Running](Keeping-Agents-Running), and it is simpler, better understood, and +what most people actually use. + +## Which one, if either + +| You have | Use | +| ------------------------------------------------- | ------------------------------------------------------------ | +| A session that stops too early | [Respawn](Keeping-Agents-Running) | +| A written task list to grind through | Ralph loop | +| One large goal that needs planning and checkpoints | Orchestrator | +| Work that should start at a certain time | [Cron Jobs](Cron-Jobs) | +| Several workers to fan out and supervise | [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) | + +## The Ralph loop + +Named after the Ralph Wiggum pattern: keep feeding the agent its own task list until the +list is empty. + +The shape of it: + +- The task list lives in a plan file in the case, conventionally `fix_plan.md`. +- Each cycle the agent reads the plan, works the next incomplete task, and marks progress. +- Codeman watches the file, tracks todos, and detects stalls. +- The loop ends when the agent signals completion, when the iteration cap is reached, or + when you stop it. + +Start it from **Session Options → Ralph / Todo**, or from the wizard on the welcome screen. + +| Setting | What it does | +| ---------------------- | ------------------------------------------------------------------------ | +| Max iterations | Hard ceiling on cycles. | +| Max todos | Cap on tracked tasks, default 500, oldest evicted first. | +| Todo expiration | Auto-expiry for stale todos, default 60 minutes. | +| Plan file | Which file holds the task list. | + +A **circuit breaker** sits behind it to stop respawn thrashing: it moves from closed to +half-open to open, and is reset explicitly from the session's Ralph controls. + +Honest assessment: Ralph is functional but is not where development attention goes. It +predates the respawn presets, which cover most of what people originally used it for with +less ceremony. Treat it as a specialised tool rather than the headline feature. + +Full background, including the upstream pattern it is based on: +[`docs/ralph-wiggum-guide.md`](https://github.com/Ark0N/Codeman/blob/master/docs/ralph-wiggum-guide.md). + +## The Orchestrator + +A state machine that turns one goal into a phased plan and drives it to completion: + +``` +idle → planning → approval → executing → verifying → (replanning) → completed / failed +``` + +- **Planning** turns your goal into phases. +- **Approval** is yours. You see the plan before anything runs. +- **Executing** runs each phase, using team agents and the task queue. +- **Verifying** gates each phase before the next one starts. A failed gate can send it back + to replanning rather than forward. + +Open it from the Orchestrator panel in the toolbar. State persists in `state.json`, so a +server restart does not lose an in-flight plan. + +Where it differs from Ralph: Ralph is one session grinding a list, the Orchestrator +coordinates phases and agents with verification between them. It suits work that has a +natural shape ("migrate this, then update callers, then update the tests") rather than a +flat backlog. + +Architecture: [`docs/orchestrator-loop-architecture.md`](https://github.com/Ark0N/Codeman/blob/master/docs/orchestrator-loop-architecture.md). + +## Running any of this safely + +Autonomous loops are the features most able to spend money and change code while you are not +looking. Some habits that pay off: + +- **Run them in a case that is a git repository**, on a branch you are willing to throw + away. Being able to read the diff afterwards is the whole safety net. +- **Consider a container.** [Docker Cases](Docker-Cases) gives the agent its own filesystem + and network, and one checkbox is all it costs. +- **Set the iteration cap deliberately.** It is the ceiling on the spend. +- **Turn on notifications** so a blocked loop reaches you: see + [Notifications And Approvals](Notifications-And-Approvals). +- **Read the run summary and lifecycle log afterwards**, not just the final diff. They show + where it went sideways and recovered. + +## Read next + +- [Keeping Agents Running](Keeping-Agents-Running) - the simpler feature that usually fits better. +- [Watching Agents Work](Watching-Agents-Work) - seeing what a loop is doing while it runs. +- [Docker Cases](Docker-Cases) - a sandbox for unattended work. diff --git a/docs/wiki/Contributing.md b/docs/wiki/Contributing.md new file mode 100644 index 00000000..f99e6796 --- /dev/null +++ b/docs/wiki/Contributing.md @@ -0,0 +1,118 @@ +# Contributing + +The full guide lives in +[CONTRIBUTING.md](https://github.com/Ark0N/Codeman/blob/master/.github/CONTRIBUTING.md). +This page is the short orientation, plus how to fix a page in this wiki. + +## Where things go + +| You have | Send it to | +| --------------------------- | ---------------------------------------------------------------------------------------------- | +| A bug | An [issue](https://github.com/Ark0N/Codeman/issues), with OS, install method, browser, and which CLI the session was running. | +| A question or setup problem | [Discussions](https://github.com/Ark0N/Codeman/discussions). | +| An idea | [Ideas](https://github.com/Ark0N/Codeman/discussions/categories/ideas), where it gets voted on. | +| A small fix | Straight to a PR. | +| A bigger feature | An issue or Discussion first, then build once the design has a nod. | +| A security problem | Never a public issue. See [SECURITY.md](https://github.com/Ark0N/Codeman/blob/master/.github/SECURITY.md). | + +Issues usually get a response within a day, and every release credits its contributors and +bug reporters by name. + +## Dev setup + +```bash +git clone https://github.com/Ark0N/Codeman.git +cd Codeman +npm install # postinstall builds the vendored xterm addon bundles +npm run dev # http://localhost:3000 +``` + +Requirements: Node 22+, tmux, and at least one agent CLI on your PATH. + +The frontend is plain JavaScript with no bundler in dev: edit a `.js` or `.css` file and +reload. The exception is `index.html`, which is read once at server start, so markup changes +need a restart. + +## Before you push + +CI runs all of these, so running them locally saves a round trip: + +```bash +npm run typecheck +npm run lint +npm run format:check +npm run check:frontend-syntax +npm test -- test/.test.ts # one file, the normal way +npm run test:ci # the full CI sweep +``` + +**Never run bare `npm test`.** The default configuration includes browser-driven Playwright +suites that need a live server, Chromium, and environment-specific baselines; they hang or +fail on a normal machine. `test:ci` is the honest "run everything". + +Tests are tmux-safe by design: under vitest the tmux layer becomes an in-memory mock, so +tests cannot touch real sessions. If you add a test that binds a port, pick a unique one at +3150 or above, and never 3000. + +## Finding your way around + +- Every source file opens with a `@fileoverview` block. Read it before the file; it is the + map. +- [`CLAUDE.md`](https://github.com/Ark0N/Codeman/blob/master/CLAUDE.md) at the repo root is + the densest architecture primer there is. It is written for AI coding agents, but its + invariants apply identically to humans, and most review feedback traces back to something + already written there. +- [`docs/architecture-invariants.md`](https://github.com/Ark0N/Codeman/blob/master/docs/architecture-invariants.md) + holds the deep mechanisms and the history behind each rule. + +## Good first contributions + +- **A theme skin.** A skin is four things kept in sync, and a static test checks the sync, so + if the test passes your skin works. +- **A language.** The i18n module is dependency-free, English is canonical, and Simplified + Chinese is a complete example to copy. +- **Docs.** If you got stuck and then figured it out, the sentence that would have unstuck + you is a pull request. +- Anything labelled + [good first issue](https://github.com/Ark0N/Codeman/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). + +Worth discussing first: new CLI backends, and real-device testing reports, especially +mobile, which always find things emulation cannot. + +## PR expectations + +- One change per PR. Small and focused reviews fast; a grab bag stalls. +- Target `master`. +- **Keep your branch mergeable.** A PR with conflicts silently gets no CI runs at all, which + is a GitHub quirk rather than a Codeman one. Rebase when conflicts appear. +- Include or update tests when you change behaviour. +- Do not bump versions or edit the changelog; releases are handled after merge. +- AI-assisted contributions are welcome, with one condition: understand what you are + submitting, and actually run it. "The model said it works" is not a test. + +## Fixing this wiki + +These pages are generated from +[`docs/wiki/`](https://github.com/Ark0N/Codeman/tree/master/docs/wiki) in the main +repository, and pushed here automatically when master changes. + +**Editing a page in the browser will be overwritten by the next sync.** Send a pull request +against `docs/wiki/` instead. It is plain markdown, and a documentation PR is a genuinely +useful contribution. + +Conventions for wiki pages: + +- Links between pages use the wiki form: `[Remote Access](Remote-Access)`, no `.md`. +- Links into the repository are absolute `https://github.com/Ark0N/Codeman/blob/master/...` + URLs. +- Images are referenced from the main repository over raw URLs rather than being copied into + the wiki. +- Say what the default is, especially when it is off. Most of Codeman is opt-in. +- Label Claude-only behaviour every time it appears. Six of the seven run modes are not + Claude. + +## Conduct + +Be kind, be direct, assume good faith. Report unacceptable behaviour privately via the +contact in +[SECURITY.md](https://github.com/Ark0N/Codeman/blob/master/.github/SECURITY.md). diff --git a/docs/wiki/Core-Concepts.md b/docs/wiki/Core-Concepts.md new file mode 100644 index 00000000..062d00c5 --- /dev/null +++ b/docs/wiki/Core-Concepts.md @@ -0,0 +1,183 @@ +# Core Concepts + +The five ideas the rest of the manual assumes: cases, sessions, run modes, location +overlays, and tmux. Plus what actually persists, and where it lives on disk. + +## Case + +A **case** is a named working directory that Codeman remembers. It is the unit you pick in +the toolbar before hitting Run, and every session belongs to exactly one. + +A case is not a container or a sandbox. It is a folder plus a name plus a little +Codeman-side configuration: + +- Which CLI the Run button should default to. +- Per-case toggles (Agent Teams, 1M Opus context). +- Where it runs, if it is not the local filesystem: see [Location overlays](#location-overlays). + +Three ways to get one, all under **+** next to the case picker: + +| How | Result | +| ----------------- | ------------------------------------------------------------------------------------------------------ | +| **Create New** | A fresh `~/codeman-cases/` with a scaffolded `CLAUDE.md`. | +| **Clone Repo** | A public repo cloned into `~/codeman-cases/` and registered as a case. | +| **Link Existing** | An existing folder anywhere on disk, registered in place. Nothing is copied or moved. | + +Linked cases keep living where they are. Deleting a case in Codeman removes the +registration, and for a linked case that is all it removes. + +**Cases created from scratch are the only copy of that code.** Uninstalling Codeman does not +delete `~/codeman-cases/`, but treat that directory as real work, not scratch space. + +## Session + +A **session** is one CLI process running in one tmux session, streamed to your browser. + +Sessions are named `w-`, so `w1-myproject` is the first worker in the `myproject` +case. Each has a stable id, and that id is what the API, the wait primitives, and every +event use. + +Several sessions can share one case. That is the normal way to parallelize: three workers +in the same repo, three tabs, one case. + +A session carries state the case does not: + +- Its run mode, model, effort level, and environment overrides. +- Its respawn configuration and Ralph loop state. +- Its terminal scrollback. +- Its owner, in [Multi-User Mode](Multi-User-Mode). + +## Run mode + +The **run mode** is which CLI the session runs: `claude`, `opencode`, `codex`, `gemini`, +`antigravity`, `pi`, or `shell`. It is chosen at start and does not change afterwards; to +switch, start another session. + +Claude is the reference mode. Six of the seven are not Claude, and a number of Codeman +features are Claude-only for structural reasons rather than missing effort: they depend on +Claude Code's hook system or on parsing its terminal output. Every such feature is labelled +Claude-only where it appears, and [Agent CLIs](Agent-CLIs) lists them in one place. + +## Location overlays + +Where a case runs is **separate from** which CLI it runs. There are three locations: + +| Location | What happens | +| -------------- | ------------------------------------------------------------------------------------------------------------ | +| **Local** | The default. tmux and the CLI run on the Codeman host. | +| **Docker** | One long-lived container per case; sessions `docker exec` into it. See [Docker Cases](Docker-Cases). | +| **Remote SSH** | A durable tmux server on the remote host, fronted by a local pane running `ssh`. See [Remote SSH Sessions](Remote-SSH-Sessions). | + +This matters because it is a common source of confusion: Docker is **not** an eighth run +mode. All seven run modes work in all three locations. A case is docker-backed or +ssh-backed; a session is claude or codex or shell. + +**Web tabs** are the other thing that is not a session. A saved dashboard URL renders as a +tab beside your agents, but there is no PTY, no tmux, and no respawn behind it. See +[Web Tabs](Web-Tabs). + +## Why tmux + +tmux is a hard requirement, and it is the reason Codeman behaves the way it does. + +The agent runs inside a tmux session. Codeman attaches to it, the same way your terminal +would. That indirection buys: + +- **Survival.** The agent outlives your browser tab, your network, your laptop lid, and a + restart of the Codeman server itself. +- **Real scrollback.** History is held by tmux, so reconnecting replays what happened while + you were gone instead of starting from blank. +- **Attach from anywhere else.** The same session is reachable from a terminal over SSH + with the `sc` chooser, or plain `tmux -L codeman attach`. +- **Secrets off the command line.** Environment overrides are injected with socket-scoped + `tmux setenv` rather than being visible in the spawn command. + +The socket is `tmux -L codeman`, separate from your personal tmux server, so Codeman +sessions never appear in a bare `tmux ls`. + +## What persists + +| Survives | Does not survive | +| -------------------------------------------- | --------------------------------------------------- | +| Closing the browser | `tmux -L codeman kill-server` | +| Losing the network | A machine reboot (tmux dies with it) | +| Restarting the Codeman server | Killing the session from the UI | +| `codeman web --stop` | | +| A dropped SSH link, for remote cases | | +| A container restart, for docker cases | | + +Conversation history is a separate question: Claude transcripts live in `~/.claude/`, so a +conversation can be resumed even after the tmux session is gone. That is what the welcome +screen's **Resume Conversation** list offers. + +## State on disk + +Everything Codeman knows lives under `~/.codeman/`: + +| File | Holds | +| ---------------------------------------- | -------------------------------------------------------------------- | +| `state.json` | Sessions, settings, respawn config, orchestrator state, cron jobs. | +| `settings.json` | User preferences that sync across your devices. | +| `mux-sessions.json` | tmux recovery data. | +| `session-lifecycle.jsonl` | Append-only audit log of session starts, exits, and kills. | +| `linked-cases.json` | Registered cases. | +| `remote-hosts.json`, `docker-hosts.json` | Location overlay configuration. | +| `webviews.json` | Saved dashboard URLs. | +| `users.json` | Multi-user accounts, mode 0600. | +| `push-*.json` | Web push keys and subscriptions. | +| `certs/` | Self-signed TLS for `--https`. | + +None of it needs root, none of it leaves the machine, and deleting `~/.codeman/` resets +Codeman to a fresh install without touching your code. + +## Instances + +The data directory and the tmux socket are both **process wide**. Two Codeman servers +started on one machine share them, which means the second one discovers the first one's +live sessions and attaches to them, resizing and mutating sessions you did not expect it to +touch. + +To run two on purpose, give each its own instance name: + +```bash +CODEMAN_INSTANCE=beta CODEMAN_PORT=5000 codeman web +``` + +That scopes the data directory and the tmux socket together, which is the only safe way to +do it. `CODEMAN_DATA_DIR` and `CODEMAN_TMUX_SOCKET` can be set individually if you need +them apart, but setting only one of the two reproduces exactly the problem you were trying +to avoid. + +## Hooks + +For Claude sessions, Codeman writes a hooks configuration into the case so Claude Code can +report events back: a permission prompt appeared, the turn finished, the agent went idle, a +task completed. Those events drive tab alerts, the Approvals Inbox, notifications, and the +wait primitives. + +This is why some features are Claude-only. The other CLIs have no equivalent hook system, +so for them Codeman falls back to watching terminal output, which is coarser: it can see +that something happened, not what it was. + +See [Hooks And Integrations](Hooks-And-Integrations). + +## Vocabulary + +| Term | Means | +| --------------- | ---------------------------------------------------------------------------- | +| **Case** | Named working directory. | +| **Session** | One CLI in one tmux session. | +| **Run mode** | Which CLI: claude, opencode, codex, gemini, antigravity, pi, shell. | +| **Respawn** | Restarting the CLI on idle to keep an unattended run going. | +| **Ralph loop** | An autonomous single-session task loop. | +| **Orchestrator**| A phased plan driven across multiple agents. | +| **Subagent** | An agent the CLI spawned itself, shown live in its own window. | +| **Web tab** | A saved dashboard URL rendered as a tab. Not a session. | +| **Instance** | One Codeman server with its own data directory and tmux socket. | + +## Read next + +- [The Dashboard](The-Dashboard) - what the UI is showing you. +- [Agent CLIs](Agent-CLIs) - the seven run modes in detail. +- [Keeping Agents Running](Keeping-Agents-Running) - respawn, idle detection, usage limits. +- [`docs/architecture-invariants.md`](https://github.com/Ark0N/Codeman/blob/master/docs/architecture-invariants.md) - the mechanisms behind all of this, for contributors. diff --git a/docs/wiki/Cron-Jobs.md b/docs/wiki/Cron-Jobs.md new file mode 100644 index 00000000..699c89ee --- /dev/null +++ b/docs/wiki/Cron-Jobs.md @@ -0,0 +1,161 @@ +# Cron Jobs + +Saved, named jobs that start a session and send it a prompt on a schedule. Cron for agent +sessions: *every weekday at 03:00, open a Claude session in `~/proj` and tell it to update +dependencies and open a PR.* + +The ⏰ **Cron** header button is opt-in. Turn it on in +**App Settings → Header & Panels**. + +## Creating a job + +1. Click **⏰ Cron**, then **+ New Job**. +2. Give it a name, pick the agent type and working directory. +3. Write the prompt, or point at a file containing it. +4. Choose a schedule and leave **Enabled** on. +5. **Save**. The job appears with its computed next run. + +**Run Now** fires it immediately without touching the schedule, which is the fastest way to +find out whether the prompt does what you meant. + +## The fields + +| Field | Notes | +| ------------------------ | ------------------------------------------------------------------------------------------- | +| **Name** | Also used as the created session's name. | +| **Agent type** | Any run mode, including `shell`. | +| **Working directory** | Validated when you save **and** again when the job fires. Blocked system trees are refused. | +| **Launch command** | Shell jobs only. Sent as the first line once the shell is up, before the prompt. | +| **Prompt** | Inline text, or a path to a file read at fire time. | +| **Input mode** | `typed` behaves like a human typing. `paste` writes directly. | +| **Schedule** | `once`, `interval`, `daily`, or `weekly`. | +| **Enabled** | Disabled jobs never fire on their own. **Run Now** still works. | +| **Concurrency policy** | What to do if sessions of the same type are already running. | +| **Auto-close previous** | Recurring jobs only. Closes the session the previous run created. Default on. | +| **Notes** | Free text for you. | + +## Schedules + +All wall-clock times are in the **server's local timezone**, not your browser's. A job set +for 03:00 fires at 03:00 where the server is. + +| Type | Behaviour | +| ---------- | -------------------------------------------------------------------------------------------------- | +| `once` | Fires at an absolute time, then disables itself. A job missed because the server was down still fires once on the next tick. | +| `interval` | Every N minutes, from 1 minute to a year. | +| `daily` | At `HH:MM` every day. If today's time has passed, the next run is tomorrow. | +| `weekly` | At `HH:MM` on the weekdays you pick. | + +Interval jobs re-anchor to when they actually fired, not to an ideal cadence, so a slow tick +or a server restart shifts later runs slightly. That drift is accepted rather than corrected. + +## Prompts are single line + +This is the rule people trip over. Programmatic input into an agent session is single line +everywhere in Codeman, because the terminal UIs these CLIs use treat a newline as submit. A +multi-line prompt would be silently mangled, so it is **rejected** instead: the form refuses +it, and a prompt file whose contents are multi-line fails the run with a clear message. + +For anything longer than a sentence, put the instructions in a file and make the prompt tell +the agent to read it: + +``` +read TASKS.md and work through it +``` + +That is also easier to edit than a job field. + +### Prompt files + +Reading the prompt from a file at fire time is useful when the instructions change more +often than the schedule. The path is confined to the job's working directory, symlinks are +resolved before the check, sensitive trees are refused, and the file has to be a regular +file under 1 MiB. + +If any of that fails, the run is recorded as failed and **no session is created**. + +## Concurrency + +Applies to scheduled runs only, never to **Run Now**: + +| Policy | Behaviour | +| ------------------------------- | -------------------------------------------------------------------------------------- | +| `warn_only` | Always launch. The count of live same-type sessions is shown but does not block. | +| `skip_if_same_agent_running` | Skip this fire if another live session of that mode exists. | + +The skip policy has the details you would want it to have: + +- Only **live** sessions block. A tab whose CLI already exited does not count. +- Sessions the job created on its own previous runs never block it, otherwise a recurring + job would deadlock on itself after the first fire. +- A skipped `once` job is not consumed. It stays armed and fires when the blocker goes away. +- Consecutive skips are collapsed into one record per streak, so a perpetually skipped job + cannot bloat your state file. + +## Run history + +Every fire is recorded per job, with a status: + +| Status | Meaning | +| --------- | -------------------------------------------------------------------- | +| `created` | The run started and a session was created. | +| `skipped` | The concurrency policy blocked it. Not counted as a run. | +| `failed` | The prompt could not be resolved, or the working directory was gone. | + +The schedule is advanced **before** the session launches, so a slow start cannot cause the +same job to re-trigger. + +## Cron versus the other autonomy features + +| Want | Use | +| --------------------------------------------------- | ------------------------------------------------------- | +| Start work at a specific time | Cron | +| Keep an existing session working | [Keeping Agents Running](Keeping-Agents-Running) | +| Drive one goal to completion across phases | [Autonomous Loops](Autonomous-Loops) | + +There is also an older, deliberately separate `ScheduledRun` concept behind +`/api/scheduled`: a run-now, duration-bounded loop with no recurrence and no saved jobs. The +two systems never interact, and Cron is the one you want. + +## From the API + +```bash +API=http://localhost:3000 + +curl -s -X POST "$API/api/cron/jobs" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "nightly-deps", + "agentType": "claude", + "workingDir": "/home/me/proj", + "promptMode": "inline_text", + "promptText": "Update dependencies and open a PR", + "inputMode": "typed", + "scheduleType": "daily", + "dailyTime": "03:00", + "enabled": true, + "concurrencyPolicy": "warn_only" + }' | jq + +curl -s "$API/api/cron/jobs" | jq +curl -s -X POST "$API/api/cron/jobs//run" | jq +curl -s "$API/api/cron/jobs//runs" | jq +``` + +Add `-u admin:"$CODEMAN_PASSWORD"` when a password is set, and `-k` with the `https://` URL +on an HTTPS install. + +## Gotchas + +- **Times are the server's, not yours.** Obvious until you are travelling. +- **A `pi` job starts slowly.** The readiness poll looks for markers pi does not print, so it + burns its poll budget before sending the prompt. The job still works. +- **A deleted working directory fails the run**, by design, rather than creating a session + somewhere unexpected. +- **Auto-close only touches sessions this job created.** Your own tabs are never closed. + +## Read next + +- [Keeping Agents Running](Keeping-Agents-Running) - continuing work rather than starting it. +- [Notifications And Approvals](Notifications-And-Approvals) - hearing about a job that got stuck. +- [`docs/cron-guide.md`](https://github.com/Ark0N/Codeman/blob/master/docs/cron-guide.md) - the complete reference, including the API and SSE events. diff --git a/docs/wiki/Docker-Cases.md b/docs/wiki/Docker-Cases.md new file mode 100644 index 00000000..76c226d1 --- /dev/null +++ b/docs/wiki/Docker-Cases.md @@ -0,0 +1,177 @@ +# Docker Cases + +Run a case inside its own container instead of directly on your host: for isolation, for a +reproducible toolchain, and for the ability to pick the whole environment up and move it to +another machine. + +A docker case is a **location overlay**, not a run mode. All seven run modes work inside a +container. See [Core Concepts](Core-Concepts). + +## One-time setup: the base image + +The container needs an image carrying the agent toolchain (node, the CLIs, git, tmux). It +builds itself on first use with progress streamed to the UI, or you can build it ahead of +time: + +```bash +node scripts/build-agent-image.mjs --no-cache +``` + +**Always pass `--no-cache`.** The CLIs are installed in a single `npm install -g` layer, so +a plain rebuild reuses that layer from the cache and the CLIs stay frozen at whatever +versions the image was *first* built with. This has shipped a broken CLI while reporting a +successful build. + +A zero exit code proves the layers ran, not that the toolchain works. Verify: + +```bash +docker run --rm codeman/agent:base bash -lc \ + 'for c in claude codex gemini opencode agy pi; do printf "%-9s " $c; $c --version 2>&1 | head -1; done' +``` + +The image is secret-free. Credentials are delivered at runtime, never baked in, so exports +never leak them. A full image lands around 1.6GB. + +Prerequisite: Docker or Podman with a reachable daemon. + +## The quick way + +On **Add Case → Create New**, tick **🐳 Run in an isolated Docker container**. That alone is +enough: Codeman creates the case folder, spins up a hardened container with sensible +defaults, and starts the session inside it. + +Expanding **Container settings** offers a template: + +| Template | Memory | CPUs | GPUs | +| ----------------- | ------ | ---- | ------------------------------------- | +| Small | 2 GB | 1 | none | +| Medium (default) | 4 GB | 2 | none | +| Large | 8 GB | 4 | none | +| GPU | 8 GB | 4 | all (needs the NVIDIA container toolkit) | + +Disk is elastic: storage grows as data arrives, bounded only by host disk. Changing any +setting creates a dedicated host profile for that case, so it never mutates the shared +default. + +## The full way + +**Add Case → Docker** exposes everything: + +| Field | Meaning | +| -------------------- | ----------------------------------------------------------------------------------------------- | +| **Case name** | As usual. | +| **Workspace path** | A real host directory, bind-mounted into the container at the **same absolute path**. | +| **Host ID** | A reusable profile (image, network, resources). Share one across cases to share settings. | +| **Network** | `bridge` (internet on, default), `none` (fully isolated), or a custom bridge. | +| **Advanced** | Memory and CPU caps, host credential seeding, and whether to resume the last conversation on relaunch. | + +The same-absolute-path bind mount is what keeps the File Viewer, attachments, and watchers +operating on real host bytes rather than a copy. + +## One container per case + +Exactly one long-lived container per case, shared by every session in it. + +- Killing one session kills only that session's in-container tmux. Siblings keep running and + the container stays up. +- Reconnecting after a Codeman restart lands back in the same live agent. +- A container stop or a host reboot restarts the container and **resumes the last + conversation** from the bind-mounted transcript. +- Deleting the case removes the container. The workspace on the host survives. + +## Credentials + +Your existing host logins work inside the container without logging in again. Credentials +are **seeded**: mounted read-only and copied in once at launch, so in-container CLIs never +write refreshed tokens back to your host credential stores. Onboarding and trust prompts are +pre-answered so no wizard appears. + +Turn seeding **off** for a sealed sandbox: no host credentials, and with `network: none`, no +outbound access either. That is the profile for genuinely untrusted work; you log in inside +the container instead. + +Bind mounts are excluded from image capture, so exports stay secret-free. + +One consequence worth knowing: Pi's credentials are seeded per file rather than as a whole +directory, because that directory also holds sessions, extensions, and installed packages, +which can be gigabytes. So in-container Pi sessions are invisible from the host, and `pi -c` +inside a docker case sees only that container's history. + +## Isolation + +Every container runs hardened by default: + +- `--cap-drop ALL` +- `--security-opt no-new-privileges` +- Non-root, running as your host uid so workspace files stay host-owned +- PID limit, memory cap with swap pinned to it, `--init` +- **Never** `--privileged`, and **never** the docker socket + +Rootless engines without cgroup-v2 systemd delegation cannot enforce resource caps; linking +such a host warns that the caps are advisory. + +## Configuration drift is refused, not ignored + +Editing a docker host's configuration (image, memory, network) after a container exists is +detected on the next launch by comparing a configuration hash against the container's label. +A mismatch **refuses the launch** and offers to recreate rather than silently running with +stale configuration. + +Recreating is refused while sessions of that case are live. The workspace and the +conversation both survive it. + +## Moving a case to another machine + +**Export**, from the Docker tab: + +| Option | Contents | +| -------------------------- | ------------------------------------------------------------------------- | +| **Full image + workspace** | The whole toolchain, installed packages, and files, in one `.tgz`. | +| **Workspace only** | Just the project files. Fast and small. | + +The container is paused across the capture so image and workspace are consistent, free space +is checked first, and the intermediate image is cleaned up. Exports run in the background +and notify you when the bundle is ready. + +**Import** on the other machine: copy the `.tgz` into `~/.codeman/docker-exports/` and +import it into a new case. The manifest and per-member checksums are verified, the workspace +tar is extracted with a traversal guard, and the image is loaded under a **quarantined tag** +so it can never overwrite a local image. The destination supplies its own credentials, so +nothing secret crosses machines. + +## Hooks need to reach the server + +In-container hooks (permission events, idle and stop notifications) call back to Codeman +over the docker bridge gateway. If Codeman binds **loopback only**, which is the default and +the production configuration, the container cannot reach it and **in-container hooks do not +fire**. + +The session still works fully: idle detection falls back to output-based detection through +the exec PTY, and with permission prompts skipped there is nothing to forward anyway. + +To enable them: + +```bash +CODEMAN_DOCKER_BRIDGE_HOOKS=1 +``` + +Codeman then starts a second listener bound to the docker bridge gateway that serves **only** +the hook endpoints and rejects everything else with a 403. The bridge is host-internal, so +this does not widen your network exposure. Add it to the service unit and restart. + +## Limits + +- Per-session environment overrides, effort, and per-CLI configuration are **rejected** for + docker cases, because they do not cross into the container. Configure the container through + the docker host's per-mode command override instead. +- tmux must exist in the base image. It is a hard prerequisite and is probed when linking a + host. +- On macOS, Docker Desktop takes a dedicated uid path, and memory caps are subject to the + VM's own ceiling. + +## Read next + +- [Core Concepts](Core-Concepts) - why this is an overlay rather than a run mode. +- [Security](Security) - where containers fit in the model. +- [Remote SSH Sessions](Remote-SSH-Sessions) - the other overlay. +- [`docs/docker-cases.md`](https://github.com/Ark0N/Codeman/blob/master/docs/docker-cases.md) - the full reference. diff --git a/docs/wiki/Driving-Codeman-From-An-Agent.md b/docs/wiki/Driving-Codeman-From-An-Agent.md new file mode 100644 index 00000000..0f595193 --- /dev/null +++ b/docs/wiki/Driving-Codeman-From-An-Agent.md @@ -0,0 +1,188 @@ +# Driving Codeman From An Agent + +Everything the dashboard does is HTTP, so an agent can do it too. This page is for the case +that makes Codeman interesting: **Claude Code running inside a Codeman session, spawning and +supervising other sessions.** + +Two routes. Start with the skill. + +## The agent skill + +A Claude Code skill that teaches the agent the whole API, so you ask in plain English +instead of pasting endpoint documentation into prompts. + +### Install it + +| How | Command | Scope | +| ------------ | ----------------------------------------------------------- | ----------------------------------------------------------- | +| Skills CLI | `npx skills add Ark0N/Codeman --skill codeman -g` | Global, any skills-aware agent. | +| Bundled CLI | `codeman skill install` | Global, at `~/.claude/skills/codeman`. | +| Bundled CLI | `codeman skill install --case ` | One case. | +| Web UI | **App Settings → Agents & CLIs → Claude → Agent Skill** | Injects into each case when a Claude session is created. Off by default. | + +`codeman skill uninstall [--case ]` reverses the CLI installs, and never touches a +`skills/codeman` you wrote yourself. + +### Then just ask + +| You say | What happens | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| "What sessions are running right now?" | Lists them with name, mode, and status. Read-only. | +| "Start a shell worker on the `myapp` case, run the test suite, tell me if it passes." | Spawns, waits on a completion marker, reads the exit code, cleans up. | +| "Spin up 3 workers for lint, typecheck and tests, run them in parallel, report failures." | One session per task, all started first, then gathered as each finishes. | +| "Have a claude worker summarize `src/session.ts`, then close it." | Spawns, runs the readiness ladder, sends and waits, reads the answer, deletes the session. | +| "Watch session w4 and tell me if it gets stuck on a permission prompt." | Blocks on the `blocked` signal and surfaces the question to **you**. | + +Sessions the agent creates get deleted when it is done. You can watch the tabs appear and +disappear in the dashboard while it works. + +### What it will and will not do + +- **It self-gates.** Outside a Codeman session it refuses to act and does not guess an API + URL, so a global install costs an unrelated Claude Code session nothing. +- **Unprompted, it may only** spawn sessions, prompt them, and delete ones **it created in + that conversation, by exact id**, behind a guard that refuses to delete the agent's own + session. +- **It will not** answer another session's permission prompt on your behalf. It surfaces the + question instead. +- **Deleting a case** (which erases a real directory of your code), bulk kills, respawn, + Ralph, cron, orchestrator, and settings writes all require you to ask, naming the target. + +Turning the setting back off **does not remove already-injected copies**, because a +create-time sweep would yank the skill out from under other live sessions sharing that +directory. Remove them per case with `codeman skill uninstall --case `. + +The skill ships with the verb index always loaded, plus on-demand references for the verbs, +worked multi-worker recipes, endpoint tables, and cross-session messaging. + +## The manual path + +The same operations as raw HTTP, for a CI bot, a shell script, or an agent without skill +support. + +### Detect that you are inside Codeman + +These are set in every managed session. Read them rather than hardcoding anything: + +| Variable | Meaning | +| -------------------------- | ------------------------------------------------------------------------ | +| `CODEMAN_MUX=1` | You are in a managed tmux session. Never `tmux kill-session`, `pkill claude`, or `pkill tmux`: you will kill yourself or a sibling. | +| `CODEMAN_API_URL` | Base URL, with the correct scheme. | +| `CODEMAN_SESSION_ID` | Your own session id. Use it to avoid acting on yourself. | +| `CODEMAN_HOOK_SECRET_FILE` | Path to the hook secret. | + +### Rules of the road + +Read these before writing any code. Each one has cost somebody an afternoon. + +1. **Input is single line and must end with `\r`.** Enter fires only when the payload + contains a carriage return. Without it the text sits unsubmitted on the prompt, the + request still succeeds, and a combined wait burns its full timeout on a turn that never + started. Embedded newlines are stripped rather than rejected, so `"echo A\necho B\r"` runs + the joined `echo Aecho B`. One line per call. +2. **Make input idempotent.** Send a stable `clientId` and a monotonic per-session `seq`. The + server deduplicates, so a retry after a dropped connection cannot double-deliver. +3. **Auth.** With `CODEMAN_PASSWORD` set, use HTTP Basic or the session cookie. A missing + `Origin` is allowed, so plain curl works. A `401` replies with the bare string + `Unauthorized`, **not** the JSON envelope, so piping it into `jq` throws a parse error + instead of showing the failure. Check the status before parsing. +4. **Envelope.** Most endpoints return `{ "success": true, "data": ... }`. A few legacy GETs + return bare bodies, so handle both: `body.data ?? body`. +5. **Wait instead of polling, and a timeout is not an error.** The wait endpoints answer + `200` with `wait.timedOut: true`. Loop over short waits rather than one long call, because + tunnels cut idle connections. +6. **Only `claude` sessions emit `stop` and `blocked`.** They come from Claude Code hooks. + Shell and the external CLIs accept only `idle`, `working`, and `exit`; asking for `stop` + explicitly there is a `400`, while omitting `until` is always safe. On a shell session + `idle` fires **once at startup and never again**, so synchronize hook-less sessions with an + output marker instead. +7. **Nothing reports "ready", so wait for it explicitly.** A new session answers + `{"signal":"exit","immediate":true}` until its PID exists, and that means *not started*, + not *crashed*. A Claude worker in a fresh case then sits on the CLI's trust dialog; prompt + it there and the wait resolves on idle in about two seconds looking exactly like a finished + turn, while your text sits stuck in the dialog. + +### Recipes + +```bash +API="${CODEMAN_API_URL:-http://localhost:3000}" +# Add -u admin:"$CODEMAN_PASSWORD" if a password is set, and -k on an HTTPS install. + +# What is running +curl -s "$API/api/sessions" | jq '.data[] | {id, name, mode, status}' + +# Spawn a worker in a case +curl -s -X POST "$API/api/quick-start" \ + -H 'Content-Type: application/json' \ + -d '{"caseName":"myapp","mode":"shell"}' | jq + +# Send a prompt (note the \r) +curl -s -X POST "$API/api/sessions/$ID/input" \ + -H 'Content-Type: application/json' \ + -d '{"input":"run the tests\r","clientId":"my-agent","seq":1}' | jq + +# Send and block until the turn finishes (registers the wait BEFORE writing) +curl -s -X POST "$API/api/sessions/$ID/input" \ + -H 'Content-Type: application/json' \ + -d '{"input":"summarize src/session.ts\r","wait":["stop"],"waitTimeout":120000}' | jq + +# Or wait for a marker in the output, which works on shell sessions too +curl -s "$API/api/sessions/$ID/wait-output?contains=DONE_17909&from=buffer" | jq + +# Read the terminal back +curl -s "$API/api/sessions/$ID/terminal?tail=4000" | jq -r '.data.output' + +# Clean up, by exact id +curl -s -X DELETE "$API/api/sessions/$ID" | jq +``` + +Use `POST /api/quick-start` rather than `POST /api/sessions` when a case might be remote: +the plain create endpoint validates the working directory locally and has no case concept. + +### The split-marker trick + +For hook-less sessions, synchronize on a marker in the output. The catch: your own +keystrokes echo into the output stream, so an unsplit marker matches **before the command +has run**. + +Split it so the typed line never contains the string you are waiting for: + +```bash +M=DONE; R=17909 +# typed: echo ${M}_${R} → output contains DONE_17909, the typed line does not +``` + +Make it unique per call, because tmux repaints replay old screen text. + +### Reading output + +Use `terminal?tail=`, not `/output`. The latter's text field is empty for every tmux-backed +session, which is every interactive session. `tail` counts **bytes**, and what comes back is +terminal data with ANSI sequences included. + +## Fan-out, and why it needs care + +Wait signals are **edge triggered with no history**. A signal that fires with no waiter +registered is unobservable afterwards. + +So a fan-out must register its waits before or as it dispatches: use send-and-wait per +worker, or latched output markers. Dispatching all the workers and then waiting on them one +at a time loses the signals of everyone who finished early. + +Send-and-wait registers the waiter **before** the write for the same reason. A separate POST +followed by a wait races, and reports the previous turn's state. + +## Lineage + +A create request can name the session that spawned it, through a body field or a header, and +the dashboard then draws a lineage arc from parent to child. The skill sets it automatically. + +It is resolved rather than trusted: an unresolvable parent is dropped silently rather than +failing the spawn, because a cosmetic field must never break a worker. + +## Read next + +- [HTTP API](HTTP-API) - the endpoint map and the envelope. +- [Hooks And Integrations](Hooks-And-Integrations) - events flowing the other way. +- [Watching Agents Work](Watching-Agents-Work) - seeing the fan-out in the UI. +- [`skills/codeman/SKILL.md`](https://github.com/Ark0N/Codeman/blob/master/skills/codeman/SKILL.md) - the skill itself. diff --git a/docs/wiki/FAQ.md b/docs/wiki/FAQ.md new file mode 100644 index 00000000..66708101 --- /dev/null +++ b/docs/wiki/FAQ.md @@ -0,0 +1,250 @@ +# FAQ + +The questions that keep arriving in +[Discussions](https://github.com/Ark0N/Codeman/discussions) and issues. For "why is it +doing that", go to [Troubleshooting](Troubleshooting) instead. + +## The basics + +### What is Codeman, in one sentence? + +A self-hosted dashboard that runs AI coding agents in persistent tmux sessions on your own +machine and lets you drive them from any browser, including a phone. + +### Is it free? What is the licence? + +MIT, free, and open source. There is no paid tier and no account. + +### Do I need an API key? + +No. Codeman drives agent CLIs you have already installed and logged in yourself. Whatever +subscription or key that CLI uses is what pays for the tokens. Codeman never collects, +stores, or refreshes your credentials. + +### Does Codeman send my code or prompts anywhere? + +No. There is no telemetry, no analytics, and no phone-home. The only network traffic +Codeman itself makes is between your browser and your server. + +Your agent CLI is a separate matter: Claude Code talks to Anthropic, Codex talks to OpenAI, +and so on. That traffic is the CLI's, on your own account, exactly as it would be in a +terminal. + +Two features do send data outward, both off by default and both stated where they appear: +voice dictation through your own Claude login, and the Read My Mind prediction call. + +### Does it work on Windows? + +Through WSL2. Codeman requires tmux. Install it inside WSL, run your agent CLI inside WSL, +and `http://localhost:3000` works from your Windows browser. Work in the Linux filesystem +rather than `/mnt/c/...`, which is dramatically slower for file watching and git. + +### Is there a mobile app? + +The web UI is built for phones and installs as a PWA. There is no App Store or Play Store +app. + +## Sessions and persistence + +### Do my agents keep running when I close the browser? + +Yes. Agents run in tmux on the server, not in your browser. Close the tab, close the laptop, +lose the network. When you come back, the session is still there with its scrollback. + +The same holds when the Codeman server itself restarts. What does end a session is killing +the tmux server or rebooting the machine. + +### What happens after a reboot? + +tmux dies with the machine, so the sessions are gone. Conversations are not: Claude +transcripts persist on disk, and the welcome screen's **Resume Conversation** list picks +them back up. Install Codeman as a service and the server itself comes back on boot. + +### How many sessions can I run at once? + +The design target is 20 sessions and 50 agent windows at 60fps. The hard cap is higher, and +what you will actually hit first is the CPU and memory of the machine running the agents. + +### Can I run Claude Code and Codex side by side? + +Yes, that is a normal setup. The run mode is per session, so one case can have a Claude tab, +a Codex tab, and a shell tab open at the same time, each with its own colour. Some Codeman +features are Claude-only; [Agent CLIs](Agent-CLIs) lists exactly which. + +### Can I attach to a session from a terminal instead of the browser? + +Yes. `sc` is an interactive chooser (`sc 2` attaches directly, `sc -l` lists), or use tmux +directly on the `codeman` socket. Detach with `Ctrl+A D`. + +## Running unattended + +### I hit my Claude usage limit overnight. Can Codeman resume automatically? + +Yes, and it is the reason the feature exists. Turn on auto-resume at the top of the Respawn +tab for that session. When Claude halts on a subscription limit, Codeman parses the reset +time from the message, waits until two minutes past it, and continues the conversation. + +Respawn cycles are blocked while a session is limit-paused, which is what stops a `/clear` +from wiping the conversation you are waiting to resume. Claude-only. + +### Will it keep prompting my agent forever? + +Only if you configure it to. Respawn cycling is per session and off unless you turn it on, +and it has presets ranging from a 60 minute solo session to an 8 hour overnight run. There +are circuit breakers to stop a thrashing session from spinning indefinitely. See +[Keeping Agents Running](Keeping-Agents-Running). + +### Does an idle session cost tokens? + +No. An idle agent is a process waiting for input. Tokens are spent when a turn runs, so what +costs money is the re-prompting you configured, not the session sitting there. + +### Can I schedule work for a specific time? + +Yes. [Cron Jobs](Cron-Jobs) saves named jobs on a `once`, `interval`, `daily`, or `weekly` +schedule; each spins up a session and sends a prompt when due, with per-job run history. + +## Access + +### How do I reach Codeman from my phone when I am away from home? + +Tailscale is the recommended answer: your devices join a private network, Codeman keeps its +loopback bind, and you get real HTTPS. The installer sets it up, and `install.sh tailscale` +retrofits it onto an existing install. + +A Cloudflare tunnel gives a public URL faster, and requires `CODEMAN_PASSWORD`. Full +comparison in [Remote Access](Remote-Access). + +### Why can't other devices reach Codeman? + +Because the default bind is `127.0.0.1`, on purpose. Codeman starts agents with permission +prompts skipped, so whoever reaches the dashboard can run code on your machine. Exposing it +is a deliberate step, and [Remote Access](Remote-Access) covers the safe ways. + +### My reverse proxy domain is rejected with `403 host not allowed` + +The always-on Host-header allowlist blocks DNS rebinding, and it does not know your domain. +Add it: + +```bash +CODEMAN_ALLOWED_HOSTS='codeman.example.com,.internal.example.com' +``` + +A leading dot matches subdomains. Also make sure the proxy forwards WebSocket upgrades. + +### Do I have to type a password on my phone? + +No. Scan the QR code shown on the desktop dashboard. Tokens are single use and rotate every +60 seconds. The password remains the fallback. + +## Multiple people, multiple instances + +### Can several people share one Codeman? + +Yes, with `codeman web --multiuser`. Each person gets a login and their own case space, and +sessions, cases, search, and events are scoped to their owner. + +Be clear about what that is: it separates **workspaces**, not operating system accounts. +Every session still runs as the same OS user, so a determined user's agent can reach another +user's files. For real isolation, pair users with Docker cases or run separate instances +under separate OS accounts. See [Multi-User Mode](Multi-User-Mode). + +### How do I run a second instance, a beta beside my main one? + +Give it its own instance name, which scopes the data directory and the tmux socket together: + +```bash +CODEMAN_INSTANCE=beta CODEMAN_PORT=5000 codeman web +``` + +Do not skip this. The data directory and tmux socket are process wide, so a second server on +the defaults discovers and attaches your live sessions. + +## Updating and maintenance + +### What is the right way to update Codeman? + +| Install route | Update with | +| ------------- | --------------------------------------------------------------------------- | +| Installer | Re-run the install one-liner, or **App Settings → System → Updates**. | +| npm | `npm update -g aicodeman` | +| git clone | `git pull && npm install && npm run build`, then restart the service. | + +The in-app updater covers git-clone installs supervised by systemd or launchd. It stashes a +dirty tree rather than discarding it, and streams progress across the restart. npm installs +report as non-updatable. + +### Will updating kill my running sessions? + +No. Sessions live in tmux, so restarting the server reattaches to them. + +### Where is my data? + +Everything under `~/.codeman/`, with cases created from scratch in `~/codeman-cases/`. +Nothing needs root and nothing leaves the machine. Uninstalling does not delete either +directory. + +## Features + +### What is the difference between respawn, Ralph, and the orchestrator? + +- **Respawn** restarts a session's CLI when it goes idle, to keep a long run going. It is the + one most people want. +- **Ralph loop** is an autonomous single-session task loop with its own tracker. +- **Orchestrator** turns one goal into a phased plan and drives it across agents. + +[Keeping Agents Running](Keeping-Agents-Running) and [Autonomous Loops](Autonomous-Loops) +cover them properly. + +### Can agents start and supervise other agents? + +Yes. Codeman ships an agent skill that lets an agent inside a session drive the HTTP API: +list sessions, spawn workers, send prompts, and block until a worker's turn finishes. It is +off by default and enabled per case. + +See [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent). + +### Can I run a case in a container? + +Yes. One container per case, shared by all its sessions, non-root and capability-dropped by +default, with your host CLI logins seeded in so nothing asks you to log in again. You can +export a container plus its workspace and move it to another machine. See +[Docker Cases](Docker-Cases). + +### Can the agent run on a different machine? + +Yes. Point a case at a remote host over SSH and the agent runs there, inside a durable +remote tmux, so a dropped connection does not kill the run. See +[Remote SSH Sessions](Remote-SSH-Sessions). + +### Can I put my Grafana or other dashboards in here? + +Yes. Saved URLs render as tabs beside your sessions, proxied through Codeman's own origin so +that mixed content and frame-blocking headers do not break them. See [Web Tabs](Web-Tabs). + +### Why is a feature I read about not on screen? + +Most of Codeman's UI is opt-in and defaults to off, so a stock install stays small. Check +**App Settings → Header & Panels**. [Settings Reference](Settings-Reference) lists the +defaults. + +## Contributing + +### How do I request a feature? + +Open an [Idea](https://github.com/Ark0N/Codeman/discussions/categories/ideas) and it gets +voted on. Roadmap decisions happen there. + +### How do I contribute code? + +[CONTRIBUTING.md](https://github.com/Ark0N/Codeman/blob/master/.github/CONTRIBUTING.md) has +the full map. Small fixes can go straight to a PR; anything larger starts as an issue or +Discussion so the design gets a nod first. Skins, translations, and docs are good first +contributions. + +### How do I fix a mistake in this wiki? + +These pages are generated from +[`docs/wiki/`](https://github.com/Ark0N/Codeman/tree/master/docs/wiki) in the main +repository. Editing a page in the browser gets overwritten on the next sync, so send a PR +against that directory instead. diff --git a/docs/wiki/HTTP-API.md b/docs/wiki/HTTP-API.md new file mode 100644 index 00000000..325b3521 --- /dev/null +++ b/docs/wiki/HTTP-API.md @@ -0,0 +1,164 @@ +# HTTP API + +Codeman's HTTP and SSE API is a **stable contract**. Everything the dashboard does goes +through it, so anything the dashboard can do, a script can do. + +This page is the orientation. The complete specification, including every wait semantic and +the SSE catalogue, is +[`docs/api-reference.md`](https://github.com/Ark0N/Codeman/blob/master/docs/api-reference.md). + +## What is stable + +Covered by semantic versioning: endpoint paths under `/api/v1`, the response envelope, +`errorCode` values, and SSE event names. + +Not covered, and free to change in a patch release: on-disk state files, internal modules, +and anything marked experimental. The full statement is in +[Versioning](Versioning). + +`/api/v1/*` is a versioned alias of `/api/*`. Prefer the versioned form in anything you +intend to keep. + +## The envelope + +```json +{ "success": true, "data": { } } +``` + +```json +{ "success": false, "error": "human readable", "errorCode": "NOT_FOUND" } +``` + +A few legacy GET handlers return bare bodies rather than the envelope, so a robust client +reads `body.data ?? body`. + +Branch on `errorCode`, which is stable. The HTTP status is reliable too: + +| `errorCode` | HTTP | Meaning | +| ------------------ | ---- | ------------------------------------------------ | +| `INVALID_INPUT` | 400 | Malformed request or failed validation. | +| `UNAUTHORIZED` | 401 | Authentication required or failed. | +| `NOT_FOUND` | 404 | No such resource. | +| `SESSION_BUSY` | 409 | The session is busy. | +| `CONFLICT` | 409 | Conflicts with current state. | +| `ALREADY_EXISTS` | 409 | Resource already exists. | +| `OPERATION_FAILED` | 422 | Well formed, could not be completed. | +| `RATE_LIMITED` | 429 | Too many requests. | +| `INTERNAL_ERROR` | 500 | Unexpected server error. | + +New error codes are non-breaking. Removing or renaming one is a major change. + +**A `401` is the bare string `Unauthorized`, not the envelope.** Piping it into `jq` throws +a parse error rather than showing the failure, so check the status first. + +## Authentication + +With no password set, and the default loopback bind, there is none. With `CODEMAN_PASSWORD` +set, use HTTP Basic or the session cookie: + +```bash +curl -s -u admin:"$CODEMAN_PASSWORD" "$API/api/sessions" +``` + +A **missing** `Origin` header is allowed, so curl and CLI tools work unchanged. A +present-but-foreign origin is rejected by the CSRF guard. On an HTTPS install with the +self-signed certificate, add `-k`. + +## Endpoint map + +Roughly 200 handlers across 24 route modules. By domain: + +| Domain | Handlers | Covers | +| ------------------- | -------- | --------------------------------------------------- | +| System | 45 | Status, settings, search, digest, updates. | +| Sessions | 34 | Create, input, terminal, wait, kill. | +| Cases | 29 | Create, link, clone, remote and docker cases. | +| Files | 16 | Preview, edit, raw, attachments, path picker. | +| Orchestrator | 10 | Plans and phases. | +| Ralph | 9 | Loop control and configuration. | +| Cron | 9 | Jobs and run history. | +| Admin | 8 | Multi-user administration. | +| Plan | 8 | Plan orchestration. | +| Respawn | 7 | Respawn configuration and presets. | +| Webviews | 6 | Saved dashboards, plus the proxy. | +| Mux | 5 | tmux operations. | +| Push | 4 | Web push subscriptions. | +| Read My Mind | 4 | Intent profiles and prediction. | +| Scheduled | 4 | The legacy scheduled-run concept. | +| Approvals | 3 | The inbox and answering. | +| Teams, me, search, hooks, clipboard, telemetry, voice, ws | 1-2 each | | + +Each route module documents its own endpoints in its file header. + +## Long-polling instead of polling + +Three calls block until something happens, so an agent driving Codeman from a shell can wait +rather than spin: + +| Call | Blocks until | +| ----------------------------------------- | -------------------------------------------------------- | +| `GET /api/v1/sessions/:id/wait` | One of a set of lifecycle signals fires. | +| `GET /api/v1/sessions/:id/wait-output` | A literal string appears in the session's output. | +| `POST /api/v1/sessions/:id/input` + `wait`| The input is delivered **and then** a signal fires. | + +Three semantics that break callers who assume otherwise: + +1. **A timeout is `200`, not an error.** It answers with `wait.timedOut: true`. Loop over + short waits; a single long call gets cut by tunnels and proxies. +2. **Send-and-wait is not a POST followed by a wait.** It registers the waiter *before* + writing, which closes the window where a separate wait sees the session still idle from + the previous turn and answers instantly about the wrong turn. +3. **Signals are edge triggered with no history.** One that fires with no waiter registered + is unobservable afterwards. Fan-outs must register their waits as they dispatch. + +`wait-output` matches a **literal substring, never a regex.** That is deliberate: no regex +means no catastrophic backtracking on attacker-influenced output. + +Only `claude` sessions emit `stop` and `blocked`, because those come from Claude Code hooks. +Shell and external CLI sessions accept `idle`, `working`, and `exit`. + +## SSE + +`GET /api/events` is the live event stream. 155 event names, kept in sync between server and +client with a test that fails on drift. + +The heartbeat is a **named** `sse:heartbeat` event rather than an SSE comment, because +comments are invisible to `EventSource` by specification and a client could not observe +them. That is what lets the browser detect a stream that has silently stopped delivering. + +```js +const es = new EventSource('/api/events'); +es.addEventListener('session:created', (e) => console.log(JSON.parse(e.data))); +``` + +## Quick examples + +```bash +API="${CODEMAN_API_URL:-http://localhost:3000}" + +curl -s "$API/api/status" | jq # whole-system snapshot +curl -s "$API/api/sessions" | jq '.data[].name' # live sessions +curl -s "$API/api/sessions/unified" | jq # live + historical, deduped +curl -s "$API/api/subagents" | jq # background agents +curl -s "$API/api/search?q=deploy" | jq # cross-session search +``` + +## Limits + +| Limit | Default | +| --------------------- | ------------------------------------------ | +| Max sessions | 50 | +| Max agent windows | 500 | +| Max SSE clients | 100 | +| Terminal buffer | 32 MB per session | +| Text payload | 1 MB | +| Wait timeout ceiling | 600 s, and the response tells you what was applied | + +Most are environment-overridable. See `src/config/`. + +## Read next + +- [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) - the practical version, with recipes. +- [Hooks And Integrations](Hooks-And-Integrations) - events flowing back into Codeman. +- [Versioning](Versioning) - what the version number promises. +- [`docs/api-reference.md`](https://github.com/Ark0N/Codeman/blob/master/docs/api-reference.md) - the full specification. diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md new file mode 100644 index 00000000..1153d61b --- /dev/null +++ b/docs/wiki/Home.md @@ -0,0 +1,136 @@ +

+ Codeman +

+ +

Mission control for AI coding agents

+ +Codeman runs your coding agents on your own machine and puts them behind one dashboard you +can open from any device. It spawns Claude Code, OpenCode, Codex, Antigravity, Gemini, or +Pi inside persistent tmux sessions, streams the real terminal to the browser, and keeps +working while you are away from the keyboard: it re-prompts idle agents, resumes when a +subscription limit resets, runs jobs on a schedule, and shows every background subagent +live. + +This wiki is the manual. The [README](https://github.com/Ark0N/Codeman) is the overview, +and the deep internals live in +[`docs/`](https://github.com/Ark0N/Codeman/tree/master/docs). + +```bash +curl -fsSL https://getcodeman.com/install | bash +codeman web # then open http://localhost:3000 +``` + +--- + +## Start here + +**New to Codeman** + +1. [Installation](Installation) - requirements, the installer, npm and git clone routes, updating. +2. [Quick Start](Quick-Start) - from a running server to a working agent in five minutes. +3. [Core Concepts](Core-Concepts) - cases, sessions, run modes, and what survives a restart. +4. [The Dashboard](The-Dashboard) - reading the tab strip, the status dots, and the alerts. + +**Already running it** + +- [Agent CLIs](Agent-CLIs) - the seven run modes, their setup, and which features are Claude-only. +- [Mobile Guide](Mobile-Guide) - phone and tablet use, QR login, the touch keyboard bar. +- [Remote Access](Remote-Access) - Tailscale, Cloudflare tunnel, LAN plus password, QR login. +- [Keeping Agents Running](Keeping-Agents-Running) - idle detection, respawn cycling, auto-resume on usage limits. +- [Troubleshooting](Troubleshooting) - symptom-first index of things that actually break. + +**Driving it from code** + +- [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) - the bundled skill, worker sessions, wait primitives. +- [HTTP API](HTTP-API) - the envelope, auth, the endpoint map, SSE events. +- [Hooks And Integrations](Hooks-And-Integrations) - events flowing back into Codeman. + +--- + +## Everything in the manual + +### Getting started + +| Page | What it answers | +| ------------------------------- | --------------------------------------------------- | +| [Installation](Installation) | How do I install it, update it, and remove it? | +| [Quick Start](Quick-Start) | How do I get one agent working right now? | +| [Core Concepts](Core-Concepts) | What is a case, a session, a run mode? | + +### Using it + +| Page | What it answers | +| ------------------------------------------ | ---------------------------------------------------------- | +| [The Dashboard](The-Dashboard) | What is the UI telling me? | +| [Agent CLIs](Agent-CLIs) | Which agent should this session run, and how do I set it up? | +| [Working With Files](Working-With-Files) | How do I read, edit, and attach files? | +| [Input And Voice](Input-And-Voice) | How do I talk to an agent, including by voice? | +| [Mobile Guide](Mobile-Guide) | How well does this work on a phone? | +| [Keyboard Shortcuts](Keyboard-Shortcuts) | What can I drive from the keyboard? | +| [Settings Reference](Settings-Reference) | What does this setting do, and why did it not follow me to my phone? | + +### Keeping agents running + +| Page | What it answers | +| ------------------------------------------------------------- | --------------------------------------------------- | +| [Keeping Agents Running](Keeping-Agents-Running) | How does it run unattended overnight? | +| [Notifications And Approvals](Notifications-And-Approvals) | How do I know an agent needs me, and answer from my phone? | +| [Cron Jobs](Cron-Jobs) | How do I run an agent on a schedule? | +| [Autonomous Loops](Autonomous-Loops) | What are the Ralph and Orchestrator loops for? | +| [Watching Agents Work](Watching-Agents-Work) | How do I see what the subagents are doing? | + +### Where it runs + +| Page | What it answers | +| --------------------------------------------- | -------------------------------------------- | +| [Docker Cases](Docker-Cases) | How do I sandbox a project in a container? | +| [Remote SSH Sessions](Remote-SSH-Sessions) | How do I run the agent on another machine? | +| [Web Tabs](Web-Tabs) | Can my Grafana live in here too? | +| [Multi-User Mode](Multi-User-Mode) | Can several people share one Codeman? | + +### Access and security + +| Page | What it answers | +| ------------------------------- | ---------------------------------------------------------- | +| [Remote Access](Remote-Access) | How do I reach it from outside this machine, safely? | +| [Security](Security) | What is exposed, what protects it, what do I have to do? | + +### Automation and integration + +| Page | What it answers | +| ----------------------------------------------------------------- | -------------------------------------------- | +| [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) | How does an agent spawn and drive workers? | +| [HTTP API](HTTP-API) | What can I call, and what comes back? | +| [Hooks And Integrations](Hooks-And-Integrations) | How do I wire Codeman into something else? | + +### Operating it + +| Page | What it answers | +| --------------------------------------------- | -------------------------------------------------- | +| [Running As A Service](Running-As-A-Service) | How do I keep it up across reboots, and update it? | +| [Troubleshooting](Troubleshooting) | Why is it doing that? | +| [FAQ](FAQ) | The questions that keep coming up. | +| [Contributing](Contributing) | How do I send a fix? | +| [Versioning](Versioning) | What does the version number promise? | + +--- + +## Requirements at a glance + +| Thing | Needed | +| ------------ | --------------------------------------------------------------------------- | +| OS | macOS or Linux. Windows works through WSL2. | +| Node.js | 22 or newer. | +| tmux | Required. Sessions live in tmux, which is what makes them survive restarts. | +| An agent CLI | At least one of Claude Code, OpenCode, Codex, Gemini, Antigravity, Pi. Plain shell sessions need none. | +| Network | Binds to `127.0.0.1` by default. Reaching it from another device is a deliberate step: see [Remote Access](Remote-Access). | + +Codeman is MIT licensed, self-hosted, and sends no telemetry. Everything runs on your +machine. + +## Getting help + +- **Questions and setup help**: [Discussions](https://github.com/Ark0N/Codeman/discussions), especially [Q&A](https://github.com/Ark0N/Codeman/discussions/categories/q-a). +- **Bugs**: [Issues](https://github.com/Ark0N/Codeman/issues). Include your OS, install method, browser, and which CLI the session was running. +- **Ideas and roadmap**: [Ideas](https://github.com/Ark0N/Codeman/discussions/categories/ideas). +- **Security**: never a public issue. See [SECURITY.md](https://github.com/Ark0N/Codeman/blob/master/.github/SECURITY.md). diff --git a/docs/wiki/Hooks-And-Integrations.md b/docs/wiki/Hooks-And-Integrations.md new file mode 100644 index 00000000..08c1efa8 --- /dev/null +++ b/docs/wiki/Hooks-And-Integrations.md @@ -0,0 +1,97 @@ +# Hooks and Integrations + +Events flowing **back** into Codeman, and the four seams a third party can build against. + +## Hooks + +Claude Code can run a command when something happens in a session. Codeman writes a hooks +configuration into each Claude case so those events post back to it, which is what turns a +terminal into something that can notify you. + +| Event | Fires when | Drives | +| ---------------------- | ----------------------------------------------- | --------------------------------------------- | +| `permission_prompt` | The agent asks for permission. | Red tab alert, Approvals Inbox, push. | +| `idle_prompt` | The agent is waiting for input. | Yellow tab alert, the `idle` wait signal. | +| `stop` | A turn ends. | The `stop` wait signal, idle detection. | +| `elicitation_dialog` | A dialog opens. | Approvals Inbox. | +| `elicitation_complete` | The dialog closes. | Clearing the alert. | +| `elicitation_response` | The dialog is answered. | Clearing the alert. | +| `teammate_idle` | An agent-team member goes idle. | Team surfaces. | +| `task_completed` | A task finishes. | Task tracking, run summary. | + +This is why several Codeman features are Claude-only. The other CLIs have no hook system, so +for them Codeman watches terminal output, which reveals that something happened but not what +it was. + +### How hooks get installed + +Codeman writes them into the case when a Claude session is created. Hook blocks are +**marker-owned**: Codeman only ever updates a block it wrote, and never touches +configuration you added yourself. + +If tab alerts and approvals never fire in a particular case, that case is missing its hook +block. Recreating the case rewrites it. + +### The hook secret + +`/api/hook-event` and `/api/status-telemetry` skip HTTP Basic authentication, because they +are called from localhost by the CLI itself. When authentication is on, that bypass +additionally requires a per-instance hook secret, because Codeman cannot tell a genuine +loopback call from a request arriving through your own loopback reverse proxy. + +The secret lives in the data directory, and its path is exported into every managed session. + +### Two things that break hooks + +- **HTTPS.** Hook callbacks must accept the self-signed certificate. Recent versions + self-heal existing cases; older cases need recreating. +- **Docker cases on a loopback bind.** A container cannot reach `127.0.0.1` on the host, so + in-container hooks silently do not fire. Set `CODEMAN_DOCKER_BRIDGE_HOOKS=1` to open a + hooks-only listener on the bridge gateway. See [Docker Cases](Docker-Cases). + +## Integration seams + +Codeman has **no plugin runtime**, and that is a decision rather than a gap. A plugin runtime +means running third-party code inside a process that spawns agents with your credentials, on +a server people routinely expose over a tunnel. Codeman's security posture is one of its +reasons to exist, so it does not trade that away for an extension mechanism. + +What exists instead is four documented seams. + +### 1. Web tabs + +Anything with a web UI can live inside Codeman as a tab, proxied through Codeman's own +origin. The lowest-effort integration by a wide margin: if your tool has a dashboard, it can +sit beside the agents with no code at all. See [Web Tabs](Web-Tabs). + +### 2. SSE events + +`GET /api/events` streams everything Codeman knows: session lifecycle, output, agent +activity, approvals, cron runs. 155 named events, stable under semantic versioning. + +This is the seam for anything that reacts. A bot that pings your chat channel when an agent +needs a human is a short script over this stream. + +### 3. HTTP API and CLI + +Everything the dashboard does. Create sessions, send input, block on wait primitives, read +terminals, manage cron. See [HTTP API](HTTP-API) and +[Driving Codeman From An Agent](Driving-Codeman-From-An-Agent). + +### 4. Hooks + +The seam above, in the other direction: your own hook commands can run alongside Codeman's +in a case, as long as you leave Codeman's marker-owned block alone. + +## Publishing an integration + +There is no registry to submit to. Share it in +[Show and tell](https://github.com/Ark0N/Codeman/discussions/300), and if it needs a change +in Codeman to work properly, open an issue or a Discussion first. + +## Read next + +- [HTTP API](HTTP-API) - the endpoint map and envelope. +- [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) - the agent-facing path. +- [`docs/extending-codeman.md`](https://github.com/Ark0N/Codeman/blob/master/docs/extending-codeman.md) - the seams in full, with examples. +- [`docs/claude-code-hooks-reference.md`](https://github.com/Ark0N/Codeman/blob/master/docs/claude-code-hooks-reference.md) - upstream hook semantics. diff --git a/docs/wiki/Input-And-Voice.md b/docs/wiki/Input-And-Voice.md new file mode 100644 index 00000000..f5778efd --- /dev/null +++ b/docs/wiki/Input-And-Voice.md @@ -0,0 +1,135 @@ +# Input and Voice + +Getting words into an agent: typing, dictating, and letting Codeman guess. Plus the input +machinery that only shows up when it goes wrong. + +## Typing + +Click into the terminal and type. It is a real terminal, so everything the CLI supports +works, slash commands included. + +| Key | Effect | +| ---------------------------- | --------------------------------------------- | +| `Enter` | Send. | +| `Shift+Enter` / `Ctrl+Enter` | Newline without sending. | +| `Ctrl+C` | Copy if text is selected, otherwise interrupt. | +| `Ctrl+Shift+C` | Copy, never interrupts. | +| `Ctrl+L` | Clear the terminal. | + +### Exactly-once delivery + +Browser input goes through a durable layer rather than a plain socket write. Each prompt +carries a stable client id and a per-session sequence number, held in local storage until +the server acknowledges it. + +The result is the property you want on a phone: a connection that drops mid-prompt never +loses the prompt and never delivers it twice. Two browser tabs on the same session coexist, +and only a reconnect from the *same* tab supersedes the old connection. + +## Zero-lag local echo + +On touch devices, keystrokes are painted in the terminal immediately and sent when you press +Enter, instead of waiting for each character to round-trip to the server and back. Over a +mobile connection that is the difference between usable and not. + +![Zero-lag input](https://raw.githubusercontent.com/Ark0N/Codeman/master/docs/images/zerolag-demo-20260728.gif) + +The consequence to remember: **text on screen has not necessarily reached the agent yet.** +It is flushed on Enter. If a prompt appears to have been ignored, press Enter, or the phone +toolbar's **Enter** button. + +Default on for touch devices, off for desktop, and switchable in +**App Settings → Terminal & Input**. + +### Codex is different on purpose + +Codex's composer reacts to every keystroke: `/` opens a live-filtering picker, arrows edit +state on its side, the composer grows as text wraps. Buffering until Enter starved it, so +Codex sessions use **predictive echo** instead: each keystroke is painted at its predicted +position while the bytes actually sent stay identical to what you typed. Predictions +reconcile against the real buffer and only apply while the cursor is on the composer row. + +## CJK input + +Chinese, Japanese, and Korean input needs an IME, and an IME needs a real text field. +Turning on CJK input in **App Settings → Terminal & Input** puts an always-visible textarea +below the terminal that owns composition, then delivers the composed text to the session. + +## Voice dictation + +`Ctrl+Shift+V`, or the microphone button. There are three providers and the default is +`auto`, which prefers them in this order: + +| Provider | Needs | Notes | +| ------------------ | ---------------------------------------------- | ------------------------------------------------------------ | +| **Claude** | Claude Code logged in on the server. Opt-in. | Uses this machine's existing Claude login. No extra key. | +| **Deepgram** | A Deepgram API key. | Nova-3, with automatic silence detection. | +| **Web Speech** | Nothing. | Browser-provided, quality varies. | + +### Dictating through your Claude login + +Off by default; enable it in **App Settings → Voice**. + +Claude Code has its own voice mode, but it opens the **host's** microphone, and in Codeman +the CLI runs headless in a tmux pane while you are in a browser somewhere else entirely. So +Codeman captures audio in your browser and borrows only the backend: audio goes browser to +Codeman to Anthropic, and the page never sees the OAuth token. + +Two deliberate limits: + +- **Credentials are read only.** Codeman never refreshes your Claude token, because a + refresh rotates the refresh token and could sign you out of your own CLI. An expired token + is reported as expired rather than silently renewed. +- **Capture is raw PCM** at 16 kHz mono, which requires an AudioWorklet rather than the + usual browser recorder. + +## Read My Mind + +**Claude only, off by default.** Turn it on in **App Settings**, and a 🧠 button appears in +the header (on phones, in the keyboard bar instead). + +It keeps a per-case **intent profile**: goals you or your agent write down, plus the prompts +you actually submitted in that case. Pressing 🧠 feeds that profile plus live session signals +to a single model call and shows a predicted next prompt. + +What you can do with the result: + +- **Send** it, **Insert** it into the composer, or edit it first. +- Pick one of the alternate suggestions, which swaps into the editable field without losing + your edits. +- **Rethink**, optionally with a steer note, to reject the whole set and try again. + +**Nothing is ever sent automatically.** Every path requires a click. + +Where the data lives: the profile is keyed by owner and the resolved working directory, so +it survives `/clear` and respawns. Prompts can contain secrets, so the store is written +0600 and is deliberately excluded from cross-session search. + +Guide: [`docs/readmymind.md`](https://github.com/Ark0N/Codeman/blob/master/docs/readmymind.md). + +## Programmatic input + +Sending prompts over the API has one rule that catches everyone: **the payload must end with +`\r`** or Enter is never sent. The request still succeeds, the text sits unsubmitted in the +composer, and any wait burns its whole timeout on a turn that never started. + +Input is also **single line**. Embedded newlines are stripped rather than rejected, so +`"echo A\necho B\r"` runs the joined `echo Aecho B`. Put multi-line content in a file and +tell the agent to read it. + +See [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent). + +## Gotchas + +- **Typed text sitting on screen has not been sent.** Press Enter. +- **`Ctrl+C` with a selection copies.** Clear the selection to interrupt. +- **Voice needs HTTPS.** Microphone access requires a secure context, same as push + notifications. +- **Read My Mind goes blind for sessions using a relocated Claude config directory**, along + with the other transcript-backed features. See [Agent CLIs](Agent-CLIs). + +## Read next + +- [Mobile Guide](Mobile-Guide) - the keyboard bar and touch input. +- [Keyboard Shortcuts](Keyboard-Shortcuts) - the full list. +- [Working With Files](Working-With-Files) - images and attachments as input. diff --git a/docs/wiki/Installation.md b/docs/wiki/Installation.md new file mode 100644 index 00000000..476a528c --- /dev/null +++ b/docs/wiki/Installation.md @@ -0,0 +1,234 @@ +# Installation + +Getting Codeman onto a machine, verifying it works, updating it, and removing it. + +## Requirements + +| Requirement | Notes | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| **macOS or Linux** | Windows works through WSL2. See [Windows](#windows-wsl) below. | +| **Node.js 22+** | The installer offers to install it if missing. | +| **tmux** | Not optional. Sessions live inside tmux, which is what makes them survive a server restart, a dropped connection, or a closed laptop. | +| **An agent CLI** | At least one of [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [OpenCode](https://opencode.ai), [Codex](https://developers.openai.com/codex/cli), [Antigravity](https://antigravity.google), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Pi](https://pi.dev). Plain shell sessions need none. See [Agent CLIs](Agent-CLIs). | + +Codeman itself sends no telemetry and phones no home. The only network traffic is your +browser to your server, and whatever the agent CLI you chose does on its own. + +## Route A: the installer (recommended) + +```bash +curl -fsSL https://getcodeman.com/install | bash +``` + +This installs Node.js and tmux if they are missing, clones Codeman into `~/.codeman/app`, +and builds it. + +What it asks you: + +1. **Permission for every system change.** Package installs and agent CLI downloads are + prompted individually. Nothing is installed silently. +2. **How the dashboard should be reachable.** Three choices: + - **Tailscale** (recommended for phone access): keeps the loopback bind and walks you + through `tailscale serve`, including the tailnet HTTPS toggle, then verifies the result + end to end. + - **Your local network** (`0.0.0.0`): prompts for a password. Skipping the password takes + an explicit confirmation and ends on a loud warning. + - **This machine only** (`127.0.0.1`): the safest option, and the default for a bare + `codeman web` regardless of what you pick here. + + Which one is preselected depends on what the installer finds. A fresh install defaults to + the local network, unless Tailscale is already connected, in which case it defaults to + Tailscale. An existing loopback install defaults to keeping loopback, or to Tailscale when + a serve mapping for Codeman is already there. A bare Enter never pulls in new software, + and a non-interactive run always keeps the safe loopback default. +3. **What to do when it finishes.** Run in this terminal, install as a background service + that starts on boot, or do nothing yet. + +Re-running the same one-liner **updates an existing install in place**. Local changes in +`~/.codeman/app` are stashed rather than discarded, a running service is restarted and +verified, and your existing network binding is preserved. An interrupted first install +resumes instead of restarting. + +Two other entry points exist: + +```bash +install.sh update # update only +install.sh uninstall # remove +install.sh tailscale # retrofit Tailscale access onto an existing install +``` + +**Automation and CI**: with no terminal attached, any step that would change the system +aborts with instructions instead of running silently. Set `CODEMAN_NONINTERACTIVE=1` to +approve those steps. `CODEMAN_TAILSCALE=1` preselects the Tailscale answer, and never +installs Tailscale itself non-interactively. + +## Route B: npm + +```bash +npm install -g aicodeman +codeman web +``` + +The npm package is named `aicodeman`; the product is Codeman. Both `codeman` and +`aicodeman` are installed as commands. + +The trade-off against Route A: no guided network setup, and the in-app self-updater does +not apply. npm installs report as non-updatable in **App Settings → System → Updates**, and +you update with `npm update -g aicodeman`. + +## Route C: git clone + +For contributing, or for running unreleased code. + +```bash +git clone https://github.com/Ark0N/Codeman.git +cd Codeman +npm install # postinstall builds the vendored xterm addon bundles +npm run dev # dev server on http://localhost:3000 +``` + +For a production run from a clone: + +```bash +npm run build +npm run start +``` + +`npm run dev` runs TypeScript directly through `tsx` with no build step. The frontend is +plain JavaScript served from `src/web/public/` with no bundler, so editing a `.js` or `.css` +file and reloading the page is enough. The one exception is `index.html`, which is read once +at server start, so markup changes need a restart. + +See [Contributing](Contributing) for the rest of the development loop. + +## Installing an agent CLI + +Codeman drives CLIs, it does not bundle them. Install at least one: + +| CLI | Install | Notes | +| --------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------- | +| **Claude Code** | `npm i -g @anthropic-ai/claude-code` | The primary target. Some Codeman features are Claude-only: see [Agent CLIs](Agent-CLIs). | +| **OpenCode** | See [opencode.ai](https://opencode.ai) | | +| **Codex** | See [developers.openai.com/codex/cli](https://developers.openai.com/codex/cli) | | +| **Antigravity** | See [antigravity.google](https://antigravity.google) | Google's successor to the consumer Gemini CLI. | +| **Gemini CLI** | See [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) | Enterprise only since Google's June 2026 consumer cutover. | +| **Pi** | See [pi.dev](https://pi.dev) | No permission prompts and no sandbox by design. Read [Agent CLIs](Agent-CLIs) before using it on a repo you care about. | + +Log each CLI in once, by hand, before pointing Codeman at it. Codeman never collects or +stores your CLI credentials. + +## Verify the install + +```bash +codeman doctor # checks Node, tmux, the agent CLIs, document converters +codeman --version +codeman web # then open http://localhost:3000 +``` + +`codeman doctor --json` gives machine-readable output, and `--category core` narrows it to +the things a session cannot start without. + +If the dashboard loads and **+ New Session** opens, you are done. Continue to +[Quick Start](Quick-Start). + +## Where things live + +| Path | What | +| ----------------------- | ------------------------------------------------------------------------------------------------ | +| `~/.codeman/app` | The installed code (installer route only). | +| `~/.codeman/` | All state: `state.json`, settings, session history, push keys, TLS certs. See [Core Concepts](Core-Concepts). | +| `~/codeman-cases/` | Cases created from scratch. Linked cases stay wherever they already are. | +| `~/.codeman/web.log` | Log for a detached (`-d`) server. | + +Everything is under your home directory, and nothing needs root. + +## Keeping it running + +A bare `codeman web` dies with the shell that started it. Two ways to outlive that: + +```bash +codeman web -d # detached; --status and --stop manage it +codeman service install # systemd user unit or macOS LaunchAgent; survives reboots +``` + +Full detail, including logs and the self-updater, is in +[Running As A Service](Running-As-A-Service). + +## Updating + +| Install route | How to update | +| ------------- | ----------------------------------------------------------------- | +| Installer | Re-run the one-liner, or **App Settings → System → Updates** in the UI. | +| npm | `npm update -g aicodeman` | +| git clone | `git pull && npm install && npm run build`, then restart. | + +The in-app updater covers git-clone installs supervised by systemd or launchd. It restarts +the process that is running it, so the actual work happens in a detached script and the +browser polls across the restart. Progress appears in the UI. + +## Uninstalling + +```bash +install.sh uninstall # installer route +npm uninstall -g aicodeman # npm route +``` + +Neither removes `~/.codeman/` or `~/codeman-cases/`. Delete those by hand if you want the +state and your case folders gone as well, and check `~/codeman-cases/` first: linked cases +point at directories you already had, but cases created from scratch have their only copy +there. + +Running tmux sessions are not killed by an uninstall. `tmux -L codeman kill-server` ends +them. + +## Windows (WSL) + +```powershell +wsl bash -c "curl -fsSL https://getcodeman.com/install | bash" +``` + +Codeman requires tmux, so Windows runs it inside +[WSL2](https://learn.microsoft.com/en-us/windows/wsl/install). If you do not have WSL yet: +run `wsl --install` in an admin PowerShell, reboot, open Ubuntu, and install your agent CLI +*inside* WSL. `http://localhost:3000` then works from your Windows browser. + +Work inside the Linux filesystem (`~/project`), not `/mnt/c/...`. Filesystem watching and +git are both dramatically slower across the Windows mount, and agents notice. + +## macOS notes + +**`Error: posix_spawnp failed.` on every session start.** node-pty publishes its macOS +`spawn-helper` without the executable bit, and macOS launches every PTY through it. Codeman +detects this and repairs it automatically on the first failure. If you hit it on a clone +install and want to fix it by hand: + +```bash +npm run fix:node-pty +``` + +This is a `chmod`, not a rebuild. Look in `prebuilds/darwin-/`, not +`build/Release/`, which does not exist on macOS. Linux cannot reproduce this. + +**launchd and PATH.** A LaunchAgent gets `/usr/bin:/bin:/usr/sbin:/sbin`, which finds +neither a Homebrew or nvm `node` nor `tmux` or `claude`. `codeman service install` bakes +your current PATH into the unit for exactly this reason, so prefer it over a hand-written +plist. + +## Gotchas + +- **`tmux: command not found` after a successful install.** The installer asks before + installing packages, and a declined prompt is a valid answer it remembers. Install tmux + and re-run. +- **Port 3000 in use.** `codeman web --port 8080`, or set `CODEMAN_PORT`. +- **Two Codemans on one machine.** The data directory and the tmux socket are both process + wide, so a second instance discovers and attaches the first one's live sessions. Give each + a distinct `CODEMAN_INSTANCE` before starting a second. See [Core Concepts](Core-Concepts). +- **The dashboard is not reachable from your phone.** That is the default, not a fault. The + server binds `127.0.0.1`. See [Remote Access](Remote-Access). + +## Read next + +- [Quick Start](Quick-Start) - your first working session. +- [Agent CLIs](Agent-CLIs) - picking and setting up a run mode. +- [Remote Access](Remote-Access) - reaching it from another device. +- [Troubleshooting](Troubleshooting) - when the above did not go as written. diff --git a/docs/wiki/Keeping-Agents-Running.md b/docs/wiki/Keeping-Agents-Running.md new file mode 100644 index 00000000..8ba04e8a --- /dev/null +++ b/docs/wiki/Keeping-Agents-Running.md @@ -0,0 +1,159 @@ +# Keeping Agents Running + +Codeman exists for the hours you are not at the keyboard. This page covers how it notices an +agent has stopped, what it does about it, and how to run a session overnight without +babysitting it. + +Everything here is **per session and off by default**. A session you never configure just +sits there when it finishes, which is usually what you want. + +## How Codeman knows an agent is idle + +Harder than it sounds, and worth understanding, because it is what every other feature here +is built on. + +**For Claude sessions**, the naive signal does not work. Claude redraws its prompt marker +roughly once a second all the way through a turn, so "saw a prompt, waited two seconds, +called it idle" flipped working sessions to idle a couple of seconds into every turn. Its +real working indicator is an animated line whose glyph and wording both change, and terminal +repaints arrive in partial fragments, so matching it in the output stream does not work +either. + +So Codeman waits for the pane to go quiet, then **asks the screen** what is on it before +believing the session is idle. Turn-start detection works the same way in reverse: a +sustained run of repaints marks a turn as started, with the same screen check vetoing mere +keystroke echo. Idle now lands a few seconds after a turn genuinely ends. + +There are several layers stacked on that: a completion message from the CLI, an AI check, +output silence, and token stability. + +**For every other CLI**, there are no hooks to lean on, so detection is output +stabilization: the session is idle when output stops changing. Coarser, and it is why the +features further down this page are Claude-only. + +## The Respawn Controller + +Respawn keeps a session working past the point where the agent would otherwise stop. When +the session goes idle, Codeman runs a cycle and starts it again. + +A cycle is up to four steps, each optional: + +1. **Update prompt.** Ask the agent to write down where it got to, so the next round can pick + it up. +2. **`/clear`.** Reset the context window. +3. **`/init`.** Re-read the project's `CLAUDE.md`. +4. **Kickstart prompt.** Tell it to continue. + +Steps 2 and 3 are what make long runs possible: without a context reset, a multi-hour +session eventually spends its whole window on its own history. + +Configure it in **Session Options → Respawn**, then press **Enable**. It repeats until the +duration you set runs out. + +| Setting | What it controls | +| ---------------------- | ----------------------------------------------------------------------- | +| **Idle timeout** | How long the session must be quiet before a cycle starts. | +| **Duration** | How long the whole arrangement stays armed. | +| **Inter-step delay** | Pause between the steps above, so a step is not sent into a busy pane. | +| **`/clear` + `/init`** | Whether the context reset happens at all. | +| **Update prompt** | What the agent is asked to record before the reset. | +| **Kickstart prompt** | What starts the next round. | +| **Auto-accept prompts**| Answer routine confirmation dialogs automatically. | + +### Presets + +Five built-ins, and the numbers matter more than the names. The idle timeout is the main +difference: a lead session coordinating subagents is legitimately silent for a minute at a +time, and a three second timeout would interrupt it constantly. + +| Preset | Idle timeout | Duration | Built for | +| -------------- | ------------ | -------- | --------------------------------------------------------------- | +| **Solo** | 3s | 60 min | One agent working alone, fast cycles with a context reset. | +| **Subagents** | 45s | 240 min | A lead session running Task subagents; tolerates their silences. | +| **Team** | 90s | 480 min | Leading an agent team; tolerates long silences. | +| **Ralph/Todo** | 8s | 480 min | Working through a task list with progress tracking. | +| **Overnight** | 10s | 480 min | Unattended overnight runs with a full reset between cycles. | + +Start from the preset that matches your shape of work and adjust the idle timeout first. +Presets you build yourself can be saved alongside these. + +### What it costs + +Every cycle is real tokens: the update prompt, the reset, and the kickstart, plus whatever +work follows. An overnight run is a deliberate spend, not a background nicety. The duration +setting is the ceiling, and it is worth setting honestly. + +## Auto-resume when a usage limit resets + +**Claude only.** At the top of the Respawn tab. + +When Claude halts on a subscription limit, the message names the time the limit resets. +Codeman parses it, arms a timer for two minutes after that, then sends Escape followed by +`continue`. + +The important part is what it does **not** do: respawn cycles are blocked while a session is +limit-paused. Without that, the next cycle would fire `/clear` and wipe the conversation you +are waiting to resume. This is the single most useful setting for overnight runs on a +subscription plan. + +## The plan usage chip + +**Claude only.** A header chip showing live subscription usage, on by default on desktop and +off on phones. + +It works by installing a status line exporter into Claude Code, which posts Claude's own +rate limit data back to Codeman. The exporter is marker-identified, so it only ever touches +a status line Codeman installed, never one you wrote yourself, and it prints your footer +through so the in-terminal status line still works. + +The chip and the exporter are the same setting. Turning the chip on without the exporter +would leave it showing a dash forever, so resolve it in one place: **App Settings**. + +## Circuit breakers + +Two, and they are unrelated: + +- **The Ralph breaker** stops respawn thrashing. It moves from closed to half-open to open, + and is reset from the session's Ralph controls. +- **The PTY-exit breaker** trips when a session's process exits repeatedly and quickly, and + blocks automatic restarts so a broken configuration cannot spin forever. + +The PTY-exit breaker resets **only** on an explicit clear. Reattaching to the session does +not clear it, deliberately, so a UI reconnect cannot paper over a session that is genuinely +failing to start. + +## A working overnight setup + +1. Start a Claude session in the case you want worked on. +2. Give it a clear goal and let it start. Respawn continues work, it does not invent it. +3. **Session Options → Respawn → Overnight preset.** +4. Turn on **auto-resume on usage limit**. +5. Set the duration to how long you actually want it running. +6. Press **Enable**. +7. Optionally turn on push notifications so a blocking question reaches your phone: see + [Notifications And Approvals](Notifications-And-Approvals). + +In the morning, the **Away Digest** summarizes what happened while you were gone, and the +run summary and lifecycle log carry the detail. + +## Gotchas + +- **Respawn without a context reset stalls eventually.** The window fills with history and + the agent gets less useful every cycle. +- **An idle timeout that is too short interrupts real work.** If the agent runs long tool + calls or coordinates subagents, raise it. That is what the Subagents and Team presets are. +- **The update prompt is what makes a reset survivable.** After `/clear`, everything the + agent knows comes from that summary and the project files. A vague update prompt produces + a vague next cycle. +- **Non-Claude sessions can respawn**, but with output-based idle detection and no + usage-limit auto-resume. +- **Do not run respawn on a session you are actively typing in.** It will send prompts + underneath you. + +## Read next + +- [Autonomous Loops](Autonomous-Loops) - Ralph and the orchestrator, for structured + autonomous work rather than "keep going". +- [Cron Jobs](Cron-Jobs) - starting work on a schedule instead of continuing it. +- [Notifications And Approvals](Notifications-And-Approvals) - being told when it needs you. +- [`docs/respawn-state-machine.md`](https://github.com/Ark0N/Codeman/blob/master/docs/respawn-state-machine.md) - the state machine itself. diff --git a/docs/wiki/Keyboard-Shortcuts.md b/docs/wiki/Keyboard-Shortcuts.md new file mode 100644 index 00000000..55b355ed --- /dev/null +++ b/docs/wiki/Keyboard-Shortcuts.md @@ -0,0 +1,72 @@ +# Keyboard Shortcuts + +Every binding, and how to change them. `Ctrl` also accepts `Cmd` on macOS. + +Press `Ctrl+?` in the app for the same list in a floating overlay. + +## Sessions and tabs + +| Shortcut | Action | +| ------------------------------- | --------------------------------------------------------------- | +| `Ctrl+K` (also `Cmd+K`, `Alt+K`)| Find an open session or start a new one. | +| `Ctrl+W` | Kill the active session. | +| `Ctrl+Tab` | Next session. | +| `Alt+[` / `Alt+]` | Previous / next tab. | +| `Alt+1` to `Alt+9` | Switch to tab N. Physical keys, so macOS Option layouts work. | +| `Ctrl+Shift+{` / `Ctrl+Shift+}` | Move the active tab left / right. | +| `Alt+B` | Collapse / expand the session sidebar, when that layout is on. | + +## Terminal + +| Shortcut | Action | +| ----------------------- | --------------------------------------------------------------- | +| `Enter` | Send. | +| `Shift+Enter` | Insert a newline without sending. | +| `Ctrl+Enter` | Same. | +| `Ctrl+C` | Copy the selection, or interrupt when nothing is selected. | +| `Ctrl+Shift+C` | Copy the selection. Never interrupts. | +| `Ctrl+L` | Clear the terminal. | +| `Ctrl+Shift+R` | Restore terminal size. | +| `Ctrl` `+` / `Ctrl` `-` | Font size. | +| `Shift+Wheel` | Scroll the local buffer, even where the wheel is forwarded to the CLI. | + +## Everything else + +| Shortcut | Action | +| -------------- | ------------------------------- | +| `Ctrl+Shift+V` | Toggle voice input. | +| `Ctrl+?` | Shortcut reference overlay. | +| `Escape` | Close panels and modals. | + +## Rebinding + +**App Settings → Shortcuts.** Bindings live in a registry with per-user overrides, so a +rebind is stored as an override on top of the default rather than replacing the table. + +Two things are deliberately not rebindable: + +- **`Ctrl+C` smart copy.** The generic dispatch loop calls `preventDefault()` on every + shortcut it handles, and doing that to `Ctrl+C` would swallow the interrupt when nothing + is selected. It is handled separately for that reason. +- **`Escape`**, which closes whatever is open. + +## Why some chords behave oddly + +The terminal sees keystrokes before the app does. Any chord the app claims has to also be +swallowed at the terminal layer, or xterm writes the control byte into the session as well +as triggering the action. If you rebind something to a chord the terminal cares about +(`Ctrl+D`, say), expect the CLI to see it too. + +`Alt+1` through `Alt+9` are matched on **physical key position** rather than the character +produced, so macOS Option layouts that produce `¡™£` still switch tabs. + +## On phones + +There is no physical keyboard, so the equivalents live in the keyboard accessory bar: `Esc`, +`Ctrl` as a one-shot modifier, `Tab`, arrows, and quick actions. See +[Mobile Guide](Mobile-Guide). + +## Read next + +- [The Dashboard](The-Dashboard) - what the shortcuts are navigating. +- [Settings Reference](Settings-Reference) - where the overrides are stored. diff --git a/docs/wiki/Mobile-Guide.md b/docs/wiki/Mobile-Guide.md new file mode 100644 index 00000000..e6d4567f --- /dev/null +++ b/docs/wiki/Mobile-Guide.md @@ -0,0 +1,145 @@ +# Mobile Guide + +Codeman on a phone is not a shrunken desktop UI. It is the surface most of its design +attention has gone into, because checking on an agent from a bus is the thing this software +is for. + +

+ Answering an agent prompt on a phone +

+ +## Getting there + +1. **Set up access.** Tailscale is the recommended route and gives you real HTTPS. See + [Remote Access](Remote-Access). +2. **Log in by QR.** Open the dashboard on your desktop and scan the code. No password + typing. Tokens are single use and rotate every 60 seconds. +3. **Install it to your home screen.** On iOS this is mandatory for push notifications; + Safari does not deliver push to tabs. On Android it makes the app full screen. + +HTTPS matters for more than security here: microphone access and push notifications both +require a secure context. + +## The layout + +| Element | Where | +| -------------------- | --------------------------------------------------------------------- | +| Header | Fixed at the top, deliberately minimal. Desktop-only controls never appear. | +| Tab strip | Scrolls horizontally. The active tab is always scrolled into view. | +| Terminal | The rest of the screen. | +| Toolbar | Bottom: Run, Stop, **Enter**, case picker, voice, settings. | +| Keyboard bar | Above the on-screen keyboard when it is open. | + +Layout respects notch and home-indicator safe areas, touch targets are 44px, and the case +picker is a bottom sheet rather than a dropdown. + +**Swipe left and right** on the terminal to switch sessions. + +## The home screen + +Tapping the "C" logo gives a session overview rather than a welcome page: + +1. **NEEDS YOU** first: sessions blocked on a question, with answer strips so you can + resolve them without opening the session. +2. **CURRENT SESSIONS** with live status. +3. **PAST SESSIONS**, resumable. + +Row status uses the same language as the tabs: green when fine, pulsing while working, +yellow when waiting for input, red when a question is pending. + +The split Run button carries the same per-backend colours as the desktop toolbar, and its +picker mirrors the desktop run-mode menu. + +On by default; it can be turned off in settings. + +## The keyboard accessory bar + +A row of keys above the virtual keyboard, and what it contains depends on the session. + +**Agent sessions** get quick actions: `/init`, `/clear`, `/compact`, a clipboard key, `Esc`, +a path picker, an image key, and 🧠 when Read My Mind is on. Destructive commands need a +double press, so you cannot fire `/clear` with a stray thumb. + +**Shell sessions** automatically swap it for terminal controls: `Ctrl`, `Esc`, `Tab`, four +arrows, paste, and dismiss. Your normal preference is remembered and restored when you +switch back to an agent session, so a settings change during a shell session cannot strip +the bar away permanently. + +### One-shot Ctrl + +`Ctrl` on the shell bar is a **one-shot modifier**: tap `Ctrl`, then tap `c`, and the +control byte is sent. It disarms on use, on a second tap, on any other accessory key, on a +session switch, and when the keyboard closes. + +That list matters. A modifier left armed turns your next innocent keystroke into a control +byte, so it is deliberately eager to disarm. Keys with no control equivalent pass through +unchanged, exactly like a hardware keyboard. + +## The Enter button + +The toolbar's dedicated **Enter** button exists because of local echo. On a phone, the +characters you type are painted locally and have not reached the agent yet; Enter flushes +them and then submits. + +It replays the keypress through the terminal rather than sending a bare carriage return. +Sending a bare `\r` would submit an empty line and strand your typed text on screen, which +looks exactly like a dead button. + +On phones this button replaces the desktop's **Run Shell** control; starting a shell moved +into the Run dropdown. + +## Scrolling and the keyboard + +- The terminal and toolbar shift up when the keyboard opens, tracked through the browser's + visual viewport rather than guessed. +- **Two ways to dismiss the keyboard**: tap outside the terminal on inert space, or tap twice + on inert terminal content. Tapping a control never dismisses it, and tapping the prompt row + keeps focus so you can place the caret. +- A scroll is never mistaken for a tap: travel is measured from the start of the gesture, and + multi-touch never counts. + +## Voice + +The microphone button, or the keyboard bar. Providers and setup are covered in +[Input And Voice](Input-And-Voice). Dictating is often faster than typing a prompt on a +phone, and it is the main reason the feature exists. + +## Notifications + +Push notifications reach you with no tab open, and with the Approvals Inbox on they carry +**Approve** and **Deny** buttons handled by the service worker, so you can unblock an agent +from the lock screen. + +Setup in [Notifications And Approvals](Notifications-And-Approvals). + +## Reading long answers + +The terminal viewport is small. **Last Response** (opt-in header button) renders the agent's +last answer as scrollable text instead, with a **More** button for additional context. + +The [File Viewer](Working-With-Files) works on phones too, including edit mode, which is +enough to fix a typo an agent introduced while you are away from your desk. + +## What is deliberately not on phones + +- Extra header buttons. New header controls are kept off phones by policy, with a test that + enforces it. +- The Approvals bell. Phones get the NEEDS YOU strips on the home screen instead. +- The desktop home tab rail, which needs a wide window. +- Lineage arcs, which are a desktop overlay. + +## Gotchas + +- **Typed text sitting on screen has not been sent.** Press Enter. +- **iOS needs the home screen install for push**, not just a bookmark. +- **iOS Safari can serve stale JavaScript after an update** until the tab is fully closed. + Close it and reopen. +- **Plain HTTP over a LAN address disables voice and push.** Use HTTPS. +- **An armed `Ctrl` is visibly highlighted.** If it looks the same as a resting key, you are + on an old version, on a light skin. + +## Read next + +- [Remote Access](Remote-Access) - getting the phone connected in the first place. +- [Notifications And Approvals](Notifications-And-Approvals) - being told when you are needed. +- [Input And Voice](Input-And-Voice) - local echo, dictation, and the input rules. diff --git a/docs/wiki/Multi-User-Mode.md b/docs/wiki/Multi-User-Mode.md new file mode 100644 index 00000000..7299f9f1 --- /dev/null +++ b/docs/wiki/Multi-User-Mode.md @@ -0,0 +1,97 @@ +# Multi-User Mode + +Share one Codeman with a small trusted team. Each person gets their own login and workspace, +and sessions, cases, search, and live events are scoped to their owner. + +**Off by default.** Without the flag, behaviour is identical to single-user Codeman, because +every scoping check short-circuits. + +## Read this before enabling it + +**Multi-user mode separates workspaces. It does not sandbox users from each other.** + +Every session still runs as the **same operating system account**. A determined user's agent +can reach another user's files, because at the OS level they are the same user. This is a +convenience and organization feature, not a security boundary. + +If you need real isolation: + +- Pair each user with [Docker Cases](Docker-Cases), which gives their work its own + filesystem and network. +- Or run separate Codeman instances under separate OS accounts, each with its own + `CODEMAN_INSTANCE`. + +"Small trusted team" is the honest description of who this is for. + +## Enabling it + +```bash +codeman users add alice --admin # create the first admin, prompts for a password +codeman web --multiuser # or CODEMAN_MULTIUSER=1 +``` + +Then manage users from the CLI or the **Users** entry in App Settings: + +```bash +codeman users add bob # a regular user +codeman users list +codeman users passwd bob # reset to a one-time password +codeman users rm bob +``` + +`--password-stdin` reads the password from standard input, for scripts. + +Accounts live in `~/.codeman/users.json` with scrypt-hashed passwords, mode 0600. +Administrative actions are audited to `~/.codeman/admin-audit.jsonl`. + +## What each user gets + +| Thing | Scope | +| ------------------- | ---------------------------------------------------------------------------- | +| **Case space** | `~/codeman-users//cases`, their own. | +| **Sessions** | Only theirs are listed, reachable, or controllable. | +| **Events** | Live event routing is per owner, and fails closed. | +| **Search** | Scoped on read, including historical results. | +| **File previews** | Scoped to sessions they own. | +| **Path picker** | Only their own user space as a root, not the whole home directory. | + +Admins see everything. + +Ownership threads through every list endpoint, the session lookup helper, the WebSocket +layer, and file previews. A user cannot address another user's session even by id. + +## Safer defaults for regular users + +Non-admins get tighter defaults, and lifting them is an explicit per-user grant: + +| Default | Meaning | +| ---------------------------------- | ------------------------------------------------------------------------ | +| Claude runs in `auto` permission mode | Anthropic's classifier-guarded mode instead of skip-prompts. | +| Raw shell sessions require a grant | A plain shell is unmediated machine access. | +| Skip-permissions requires a grant | Same reasoning. | +| Cron `launchCommand` requires a grant | It is an arbitrary command on a schedule. | +| Pi project trust defaults to off | Trust makes Pi execute repo-local TypeScript. | + +These exist because the OS boundary is shared. They narrow what a normal account can do +casually; they do not make the account a sandbox. + +## Accounts and sessions + +Each user authenticates with their own name and password rather than the shared +`CODEMAN_PASSWORD`. Logins are individually revocable: disable, reset, or delete an account +at any time, and existing browser sessions can be revoked. + +## Gotchas + +- **Enabling it does not migrate existing cases** into a user space. They stay where they + are, owned by whoever the ownership rules resolve them to. +- **Admins see everything**, including other users' sessions. Choose admins accordingly. +- **The audit log is append-only and local.** Ship it somewhere if you care about it. +- **It is not a substitute for OS accounts.** Restating this because it is the one thing + people get wrong. + +## Read next + +- [Security](Security) - where this fits in the model, and what it does not cover. +- [Docker Cases](Docker-Cases) - the isolation story that actually isolates. +- [`docs/multi-user-plan.md`](https://github.com/Ark0N/Codeman/blob/master/docs/multi-user-plan.md) - the design. diff --git a/docs/wiki/Notifications-And-Approvals.md b/docs/wiki/Notifications-And-Approvals.md new file mode 100644 index 00000000..ed7de7f5 --- /dev/null +++ b/docs/wiki/Notifications-And-Approvals.md @@ -0,0 +1,147 @@ +# Notifications and Approvals + +An agent that stops to ask a question, with nobody watching, is a run that quietly wasted an +hour. This page covers every way Codeman tells you it needs you, and how to answer without +opening the session. + +## The signals, cheapest first + +| Surface | Reaches you | Default | +| ---------------------- | ------------------------------------------------- | ------- | +| Tab alert | While the dashboard is open | On | +| Browser title flash | Another tab in the same browser | On | +| Desktop notification | Another window on the same machine | Opt-in | +| Push notification | Anywhere, even with no tab open | Opt-in | +| Approvals Inbox | One queue across every session | Opt-in | +| Phone overview | Phone home screen, NEEDS YOU section | On | +| Away Digest | Afterwards, as a summary | Opt-in | + +## Tab alerts + +The tab itself changes state: + +| State | Meaning | +| -------------------- | ---------------------------------------------------------- | +| Yellow, blinking | The agent is waiting for input from you. | +| Red, blinking | A question or permission prompt is blocking the session. | + +These are a steady colour with a pulse layered on top, not a blink to transparent, so a tab +needing attention looks that way at every point in the cycle. + +They survive a reload. The alert state is re-seeded from the server on page load, so +reloading the dashboard while a permission dialog is blocking a session does not leave you +with a normal-looking tab. + +For Claude sessions, these come from Claude Code's hooks and are precise about *why* the +session stopped. For other CLIs there are no hooks, so you get the coarser output-based +signal. + +## Window title and OS notifications + +The browser tab title is prefixed `codeman:`, so several Codeman instances across +several machines stay distinguishable at a glance. Override the hostname with +`codeman web --title-hostname `. + +Desktop notifications use the same prefix. Enable them in **App Settings → Notifications**. + +## Push notifications + +Push reaches your phone with **no Codeman tab open at all**, which is the only option that +works while you are actually away. + +Setup: + +1. Open Codeman over **HTTPS**. Web push requires a secure context. Tailscale gives you real + HTTPS; `--https` gives you a self-signed certificate; plain HTTP over a LAN address will + not work. +2. **App Settings → Notifications → Subscribe**, and accept the browser prompt. +3. On **iOS**, add Codeman to your home screen first. Safari only delivers web push to + installed web apps, not to tabs. + +Once subscribed, a blocking prompt reaches your phone even from a locked screen. + +## The Approvals Inbox + +**Opt-in, off by default. Claude sessions only.** + +One queue of every prompt currently waiting on a human, across all your sessions, answerable +in place. When you have eight workers running, this is the difference between checking eight +tabs and checking one list. + +Turn it on in **App Settings**. Surfaces: + +- **A header bell** with a count, hidden entirely while the count is zero. Never shown on + phones. +- **A drawer** listing each waiting card. +- **NEEDS YOU strips** at the top of the phone overview home screen. + +Each card shows the session, the case, and the captured prompt with its options. Answering +sends the keystroke into the session for you: a digit for a menu choice, Escape to decline, +or free text for an idle prompt. + +Behaviour worth knowing: + +- **One item per session.** A newer prompt supersedes the older one, because the older one + is no longer on screen. +- **Menu answers are validated against the live screen.** Codeman re-captures the pane before + sending, and refuses with a conflict if the dialog is no longer there. Otherwise your + keystroke would land in the composer as stray text. +- **Permission and question items clear only on definitive signals**: the turn ending, the + dialog completing, an answer, a supersede, the session exiting, or a 12 hour timeout. They + do not clear on a heuristic "looks busy again" signal, because that signal is wrong often + enough to lose a real prompt. +- **In memory only.** Restarting the server clears the queue; the prompts themselves are + still sitting in the sessions. + +### Approve and Deny from the notification + +With the inbox enabled, push notifications carry **Approve** and **Deny** buttons. Those are +handled by the service worker directly, so they work with no tab open: tap Approve on a +locked phone and the agent continues. + +With the inbox off, the buttons are stripped from the notification payload entirely rather +than being shown and failing. + +## The phone overview + +On phones, tapping the "C" logo gives a session overview with **NEEDS YOU** first, then +current sessions, then past ones. Rows use the same language as the tab strip: a green dot +when fine, pulsing while working, yellow when waiting for input, red when a question is +pending. + +Answer strips let you resolve a prompt straight from the home screen without opening the +session. + +## The Away Digest + +Retrospective rather than live: what happened while you were gone, aggregated from the +lifecycle log, run summaries, live sessions, token statistics, and recent subagents. + +It is the morning-after view for an overnight run. Enable its header button in +**App Settings → Header & Panels**. + +## Recommended setup for unattended runs + +1. HTTPS access, ideally Tailscale. See [Remote Access](Remote-Access). +2. Push notifications subscribed, with Codeman installed to the home screen on iOS. +3. Approvals Inbox on. +4. Auto-resume on usage limit on, for each session you leave running. See + [Keeping Agents Running](Keeping-Agents-Running). + +That combination means a blocking question wakes your phone and can be answered in two taps +from the lock screen. + +## Gotchas + +- **No push over plain HTTP.** It is a browser requirement, not a Codeman one. +- **iOS needs the home screen install.** A Safari tab will never receive push. +- **The bell is invisible at zero.** That is deliberate, not a broken setting. +- **Approvals are Claude-only.** They are built on hook events the other CLIs do not emit. +- **A stale menu answer is refused, not sent.** If you answer a card for a dialog that has + since gone away, Codeman declines rather than typing a digit into the composer. + +## Read next + +- [Keeping Agents Running](Keeping-Agents-Running) - what to configure before walking away. +- [Mobile Guide](Mobile-Guide) - the phone surfaces in full. +- [Settings Reference](Settings-Reference) - where each of these toggles lives. diff --git a/docs/wiki/Quick-Start.md b/docs/wiki/Quick-Start.md new file mode 100644 index 00000000..91e1fc04 --- /dev/null +++ b/docs/wiki/Quick-Start.md @@ -0,0 +1,153 @@ +# Quick Start + +From an installed Codeman to a working agent, in about five minutes. If you have not +installed yet, start at [Installation](Installation). + +## 1. Start the server + +```bash +codeman web +``` + +It prints a URL, `http://localhost:3000` by default. Open it. + +The server binds `127.0.0.1` only, so this URL works from the machine running it and +nowhere else. That is deliberate: Codeman starts agents with permission prompts skipped by +default, so anyone who can reach the dashboard can run code on this machine. Reaching it +from your phone is a separate, deliberate step covered in [Remote Access](Remote-Access). + +To keep it alive after you close the terminal, use `codeman web -d` instead, or install it +as a service. See [Running As A Service](Running-As-A-Service). + +## 2. Meet the welcome screen + +With no sessions running you get the welcome screen: + +- **Run buttons** for each agent CLI Codeman found on your PATH. If you expected one and it + is missing, its binary is not visible to the server; see [Agent CLIs](Agent-CLIs). +- **A QR code**, if a password is set. Scanning it logs a phone in without typing anything. +- **Resume Conversation**, a list of past sessions, including Claude conversations started + outside Codeman. Empty on a fresh install. +- **Search**, across sessions, events, and files. + +You can click a Run button right now and get a working agent in your current case. The rest +of this page is the deliberate version. + +## 3. Pick or create a case + +A **case** is a named working directory that Codeman remembers. Every session runs inside +one. The case picker is in the bottom toolbar. + +To make a new one, click **+** next to the picker. The Add Case dialog has three tabs: + +| Tab | Use it when | +| ----------------- | ------------------------------------------------------------------------------------------------------------------ | +| **Create New** | Starting a fresh project. Creates `~/codeman-cases/` and scaffolds a `CLAUDE.md` into it. | +| **Clone Repo** | Working on an existing public repo. Paste the URL; Codeman preflights it as you type, offers the repo's real branches and tags, and fills in the case name. | +| **Link Existing** | The code is already on disk. Point at the folder, with **Browse** if you would rather click than type. | + +The gear next to the picker holds two per-case toggles: **Agent Teams** and +**1M Opus Context**. Both are off by default and both are safe to ignore for now. + +**Create New** also has a checkbox for running the case inside a Docker container, and a +**Remote** panel for running it over SSH on another machine. Those are +[Docker Cases](Docker-Cases) and [Remote SSH Sessions](Remote-SSH-Sessions); skip them for +your first session. + +## 4. Pick a run mode and hit Run + +The **Run** button starts an agent in the selected case. The arrow next to it picks which +one: + +| Mode | What starts | +| -------------------- | -------------------------------------------------------------- | +| **Claude Code** | The default, and the mode every Codeman feature supports. | +| **OpenCode** | | +| **Codex** | OpenAI's CLI. | +| **Gemini** | Enterprise only since Google's consumer cutover. | +| **Antigravity** | Google's successor to the consumer Gemini CLI. | +| **Pi** | No permission prompts and no sandbox by design. | +| **Terminal / Shell** | A plain shell, no agent. Also the **Run Shell** button. | + +The dropdown also lists any saved dashboard URLs ([Web Tabs](Web-Tabs)) and your recent +sessions. Those do not change the run mode: Run always means "start an agent". + +Click **Run**. A tab appears, and Codeman spawns the CLI on a real PTY inside a tmux +session and streams it to your browser. + +The number spinner beside the button starts several sessions at once, up to 20. Useful for +fanning the same case out across parallel workers; unnecessary for a first run. + +## 5. Talk to the agent + +Click into the terminal and type. It is a real terminal (xterm.js over a real PTY), so full +TUIs render properly and everything the CLI supports works, slash commands included. + +| Key | Effect | +| ---------------------------- | --------------------------------------------- | +| `Enter` | Send. | +| `Shift+Enter` / `Ctrl+Enter` | Newline without sending. | +| `Ctrl+C` | Copy if text is selected, otherwise interrupt. | +| `Ctrl+Shift+V` | Voice input. | + +You can also paste or drag an image straight into the session, and register external files +as attachments. See [Working With Files](Working-With-Files) and +[Input And Voice](Input-And-Voice). + +Input is delivered **exactly once**, even if your connection drops mid-prompt. A dropped +link never loses a prompt and never sends it twice. + +## 6. Read the tab + +The tab tells you what the session is doing without opening it: + +| Signal | Meaning | +| --------------------- | ---------------------------------------------------------- | +| Green dot | Alive and idle. | +| Pulsing green dot | Working on a turn. | +| Yellow, blinking | Waiting for you to type something. | +| Red, blinking | A question or permission prompt is blocking the agent. | + +Full tour in [The Dashboard](The-Dashboard). If you want a phone notification when an agent +needs you, that is [Notifications And Approvals](Notifications-And-Approvals). + +## 7. Leave, and come back + +Close the browser tab. Close the laptop. The agent keeps running, because it lives in tmux +and not in your browser. + +Reopen the dashboard and the session is still there with its scrollback intact. First load +of a session pulls the full tmux scrollback, so you get the history, not just what arrived +after you reconnected. + +This also survives restarting the Codeman server itself. What does not survive is killing +the tmux server or rebooting the machine. + +## 8. Stop things + +| To do this | Do that | +| ------------------------- | ------------------------------------------------------------------- | +| Interrupt the current turn | `Ctrl+C` with nothing selected, or the **Stop** button. | +| Close one session | `Ctrl+W`, or the tab's close control. | +| Stop the server, keep agents | `codeman web --stop`. The tmux sessions stay alive. | +| Stop everything | `tmux -L codeman kill-server`. | + +If you are working *inside* a Codeman-managed session (`echo $CODEMAN_MUX` prints `1`), +never run `tmux kill-session` or `pkill claude` by hand. You will kill the session you are +sitting in, along with its siblings. + +## Where to go next + +**Make it run without you.** [Keeping Agents Running](Keeping-Agents-Running) covers idle +detection, respawn cycling, and auto-resume when a subscription limit resets. That is the +feature Codeman exists for. + +**Get it on your phone.** [Remote Access](Remote-Access), then +[Mobile Guide](Mobile-Guide). + +**Understand what you just used.** [Core Concepts](Core-Concepts) explains cases, sessions, +run modes, and what state lives where. + +**Automate it.** [Cron Jobs](Cron-Jobs) for scheduled work, +[Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) for agents that spawn and +supervise other agents. diff --git a/docs/wiki/Remote-Access.md b/docs/wiki/Remote-Access.md new file mode 100644 index 00000000..b3b00bb3 --- /dev/null +++ b/docs/wiki/Remote-Access.md @@ -0,0 +1,209 @@ +# Remote Access + +Reaching your Codeman from a phone, a laptop on the other side of the house, or a hotel +network. This is the page to read carefully, because Codeman's dashboard is a +remote-code-execution surface by design: it starts agents with permission prompts skipped, +so whoever can reach it can run code on your machine. + +## Start from the default + +`codeman web` binds `127.0.0.1`. It is reachable from the machine running it and nothing +else, which is why the no-password default is safe out of the box. Every option below is a +deliberate step away from that. + +Two rules that make the rest of this page simple: + +1. **Never expose Codeman on a network without `CODEMAN_PASSWORD`.** Binding a non-loopback + host without one starts, but prints a loud warning with the fixes. +2. **Prefer keeping the loopback bind** and putting an authenticated tunnel in front of it, + over binding wide and relying on a password alone. + +## Pick an approach + +| Approach | Good for | Cost | +| --------------------- | ----------------------------------------------------- | --------------------------------------------------------- | +| **Tailscale** | Phone access, permanently. The recommended setup. | Install Tailscale on both devices. | +| **Cloudflare tunnel** | A public URL, quickly, from anywhere. | Public URL, so a password is mandatory. | +| **LAN + password** | Home network only, no extra software. | Every device on your LAN can reach the login page. | +| **SSH port forward** | You already SSH to the box. | Manual, per session, terminal-bound. | + +## Tailscale (recommended) + +Your devices join a private network, and Codeman stays bound to loopback. Nothing is +published to the internet, and you get real HTTPS with a real certificate. + +The installer sets this up for you, including installing Tailscale, logging in, enabling +tailnet HTTPS, and verifying the result end to end. To retrofit it onto an existing +install: + +```bash +install.sh tailscale +``` + +By hand: + +```bash +tailscale serve --bg 3000 +tailscale serve status +``` + +Then open `https://..ts.net` from any device on your tailnet. + +Notes: + +- Keep the loopback bind. `tailscale serve` connects to `127.0.0.1:3000` locally, so + binding wider adds exposure and buys nothing. +- Your tailnet is the authentication boundary. Setting `CODEMAN_PASSWORD` as well is + reasonable defence in depth, especially if other people have devices on your tailnet. +- Codeman's Host-header allowlist already accepts `.ts.net`, so no extra configuration is + needed. +- The installer never resets or rewrites `serve` mappings other than the one pointing at + Codeman's port, so unrelated serve configuration is left alone. + +## Cloudflare tunnel + +A free [quick tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) +gives you a public HTTPS URL with no port forwarding, no DNS, and no static IP: + +``` +Browser → Cloudflare edge (HTTPS) → cloudflared → localhost:3000 +``` + +Prerequisites: [`cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) +installed, and `CODEMAN_PASSWORD` set. + +```bash +./scripts/tunnel.sh start # starts the tunnel, prints the public URL +./scripts/tunnel.sh url +./scripts/tunnel.sh status +./scripts/tunnel.sh stop +``` + +The quick-tunnel URL is a random `*.trycloudflare.com` address that changes every time the +tunnel restarts. For a stable hostname, `./scripts/tunnel.sh named setup` walks through a +named tunnel. + +To survive reboots: + +```bash +systemctl --user enable codeman-tunnel +loginctl enable-linger $USER +``` + +There is also a toggle in **App Settings → System → Remote access**. + +**The tunnel refuses to start without a password.** That is on purpose: a public URL with no +authentication is a terminal on your machine handed to the internet. Acknowledging the risk +explicitly is possible from the UI toggle, and only from there; the API will not do it for +you. + +## LAN plus password + +```bash +export CODEMAN_PASSWORD='something long' +codeman web -H 0.0.0.0 --https +``` + +Every device on your local network can now reach the login page. `--https` generates a +self-signed certificate into `~/.codeman/certs/`, which your browser will warn about once. + +`CODEMAN_USERNAME` defaults to `admin`. + +The installer offers this path and prompts for the password. On re-runs it preserves +whichever binding you already chose. + +## SSH port forward + +No configuration at all, if you already have SSH access: + +```bash +ssh -L 3000:localhost:3000 you@your-box +``` + +Then open `http://localhost:3000` on the local machine. Codeman keeps its loopback bind and +sees a local connection. Good for occasional access, awkward as a permanent arrangement +because it dies with the SSH session. + +## Logging in from a phone + +Typing a long password on a phone keyboard is miserable, so Codeman issues **single-use QR +tokens**. The desktop dashboard shows a QR code; scan it and the phone is authenticated. + +How it behaves: + +- The code rotates every 60 seconds, with a 90 second grace window so scanning during a + rotation still works. +- Each token is **single use**. The moment a phone consumes it, a new one is generated. +- The URL contains a 6-character lookup code, not the secret, so it does not leak through + browser history, `Referer` headers, or the tunnel provider's logs. +- The desktop shows a toast naming the device and browser that just authenticated, with a + one-click revoke. +- QR attempts are rate limited separately from password attempts, so a mistyped password + cannot lock out your QR login and vice versa. + +Someone holding only the tunnel URL still meets the normal password prompt. The QR is the +fast path, not a bypass. + +Design detail and the threat analysis it is built against: +[`docs/qr-auth-plan.md`](https://github.com/Ark0N/Codeman/blob/master/docs/qr-auth-plan.md). + +## Behind a reverse proxy + +Codeman enforces a Host-header allowlist on every request to block DNS rebinding, and the +same allowlist gates the cross-site Origin check. It accepts `localhost`, IP literals, the +bind host, `.ts.net`, `.trycloudflare.com`, `.cfargotunnel.com`, and the active managed +tunnel. + +**Your own domain is not on that list.** Add it: + +```bash +CODEMAN_ALLOWED_HOSTS='codeman.example.com,.internal.example.com' +``` + +A bare entry matches that exact host; a leading dot matches subdomains. Without this, a +correctly configured proxy still gets `403 host not allowed`, which reads like a proxy bug +and is not one. + +Also make sure the proxy forwards WebSocket upgrades. The terminal is a WebSocket, and the +upgrade runs the same Host and Origin checks, closing with code `4003` on failure. + +## Session cookies and rate limits + +The first request prompts for HTTP Basic credentials. On success the server issues an opaque +`codeman_session` cookie (24 hour lifetime, extended on activity, validated server-side so +it cannot be forged offline). Ten failed attempts from one IP produce a `429` with a 15 +minute decay. + +A valid cookie or a correct password recovers immediately even while an attacker is hammering +the same IP, which matters because all tunnel traffic arrives from one loopback address. + +## Terminal alternatives + +You do not have to use a browser. `sc` is a thumb-friendly session chooser for SSH clients +like Termius or Blink: + +```bash +sc # interactive chooser +sc 2 # attach to session 2 +sc -l # list +``` + +Detach with `Ctrl+A D`. The sessions are the same ones the dashboard shows. + +## Common problems + +| Symptom | Cause and fix | +| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `403 host not allowed` | Your domain is not in the allowlist. Set `CODEMAN_ALLOWED_HOSTS`. | +| Phone shows the login page but the terminal never connects | The proxy is not forwarding WebSocket upgrades. | +| Browser warns about the certificate | Expected with `--https` and its self-signed certificate. Tailscale gives you a real one instead. | +| LAN IP does not respond, but a tunnel to the same box works | The server is bound to loopback. That is the default. A tunnel reaches it; a LAN browser cannot. | +| Hooks stopped working after switching to HTTPS | Hook callbacks need `-k` for the self-signed certificate. Recent versions self-heal existing cases; if yours predates that, recreate the case's hooks. | +| Everything is slow over the tunnel | Quick tunnels route through Cloudflare's edge. Tailscale is usually a direct connection and much faster. | + +## Read next + +- [Security](Security) - the whole model, and the hardening checklist. +- [Mobile Guide](Mobile-Guide) - once you can reach it from the phone. +- [Running As A Service](Running-As-A-Service) - keeping server and tunnel up across reboots. +- [`docs/security-architecture.md`](https://github.com/Ark0N/Codeman/blob/master/docs/security-architecture.md) - the full model. diff --git a/docs/wiki/Remote-SSH-Sessions.md b/docs/wiki/Remote-SSH-Sessions.md new file mode 100644 index 00000000..77b8f67a --- /dev/null +++ b/docs/wiki/Remote-SSH-Sessions.md @@ -0,0 +1,101 @@ +# Remote SSH Sessions + +Point a case at another machine and the agent runs **there**, with the same dashboard, +mobile UI, and autonomy features. Your laptop becomes a window onto a session living on the +remote host. + +Like Docker, this is a **location overlay** on a case, not a run mode. All seven run modes +work remotely. See [Core Concepts](Core-Concepts). + +## Why bother + +The agent runs where the work is: a build server, a NAS, a GPU box, a machine reachable only +through a jump host. Your laptop can sleep, change networks, or close, and the run continues. + +## Setting it up + +**Add Case → Remote**: + +| Field | Notes | +| --------------------- | -------------------------------------------------------------------- | +| **Host** | Hostname or IP. | +| **Username** | The SSH user. | +| **Port** | Defaults to 22. | +| **Identity file** | `~` and `$HOME` are expanded for you. | +| **Jump host** | The `-J` equivalent, `[user@]host[:port]`. | +| **SOCKS proxy** | For hosts reachable only through a proxy. | +| **Extra SSH options** | Any `KEY=VALUE` options your normal connection needs. | +| **Remote path** | The working directory on that machine. | + +Hosts are saved and reusable, so a second case on the same machine is just a path. Host +profiles can also carry per-run-mode launch command overrides, for when the binary lives +somewhere unusual on that host. + +The remote host needs **tmux**. Codeman probes for it when you link the host rather than +failing later at launch. + +## What actually runs + +The agent lives inside a dedicated tmux server on the **remote** host, and Codeman fronts it +with a local tmux pane running `ssh`. + +That two-layer arrangement is what makes it durable: a dropped SSH connection, a network +change, or a closed laptop kills the local pane, not the remote session. Reconnecting lands +back in the same live conversation. + +The remote session name is deliberately chosen so that a Codeman **running on the target +host** will not adopt it as one of its own. Two Codemans, one host, no interference. + +## Auto-reconnect + +A watcher with bounded backoff notices a dead SSH pane and quietly reattaches to the still +running remote session. On by default; the kill switch is in +**App Settings → Agents & CLIs → Remote auto-reconnect**. + +Intentional kills are never revived. Closing a session means closing it. + +## Discover and attach + +Codeman can list the `codeman-*` sessions already running on a host, whether that machine's +own Codeman started them or another operator did, and attach to one. + +The distinction that matters: + +| Session | On tab close | +| ------------ | ------------------------------------------------ | +| **Launched** | Killed, like any local session. | +| **Attached** | **Detached, never killed.** | + +Attaching to someone else's session and closing your tab must not end their run, so it does +not. Several clients can attach the same remote session at different window sizes without +clamping each other, and discovery shows a shared badge with the client count. + +## Security + +Every SSH command line in Codeman flows through one builder that shell-escapes every +user-supplied field: identity paths, jump hosts, proxy commands, and extra options. That is +the entire injection surface, and it is deliberately a single function rather than string +concatenation spread across the codebase. + +Host, path, and identity fields are schema-validated on top of that. + +Codeman does not store SSH passwords. Use keys, as you would for any other automation. + +## Gotchas + +- **The remote host needs tmux.** Probed at link time, so you find out immediately. +- **The local working directory is meaningless** for a remote session, and is not used. +- **Run flows must go through the quick-start path** for remote cases. This matters if you + are driving Codeman over the API: the plain session-create endpoint validates the working + directory locally and has no case concept, so it will reject or misroute a remote case. +- **Latency is SSH latency.** Local echo helps the typing feel, but a slow link is a slow + link. +- **Transcript-backed features follow the transcript.** Subagent windows and similar surfaces + read files on the machine where the agent runs. + +## Read next + +- [Core Concepts](Core-Concepts) - overlays versus run modes. +- [Docker Cases](Docker-Cases) - the other overlay. +- [Security](Security) - the wider model. +- [`docs/remote-sessions.md`](https://github.com/Ark0N/Codeman/blob/master/docs/remote-sessions.md) - the full design. diff --git a/docs/wiki/Running-As-A-Service.md b/docs/wiki/Running-As-A-Service.md new file mode 100644 index 00000000..17d7c49f --- /dev/null +++ b/docs/wiki/Running-As-A-Service.md @@ -0,0 +1,197 @@ +# Running As A Service + +Keeping Codeman up: past the shell you started it in, past a logout, past a reboot. Plus +logs, updates, and running more than one instance. + +## Three levels + +| Level | Survives | Command | +| -------------------- | ----------------------------------------- | ------------------------- | +| Foreground | Nothing. Dies with the terminal. | `codeman web` | +| Detached | Closing the shell and logging out. | `codeman web -d` | +| Service | Reboots. | `codeman service install` | + +Agents themselves survive all three, because they live in tmux. Stopping the server never +stops the agents. + +## Detached mode + +```bash +codeman web -d # start detached; logs to ~/.codeman/web.log +codeman web --status # is it up, and on which pid +codeman web --stop # graceful stop; agents keep running +``` + +`-d` waits until the server actually answers before reporting success, so a port clash never +reads as a successful start. + +Two implementation details that explain the behaviour: + +- It relaunches the same entry script detached, so there is no controlling terminal and no + shell job entry. `nohup` is **not** what makes this work: Node re-arms the hangup signal to + its default even when it inherits "ignore", and Codeman handles that signal with a graceful + shutdown, so a delivered hangup would still stop the server. +- `--stop` verifies the process still looks like a Codeman server before signalling it, + because process ids get recycled. + +**It refuses to start a second server on the same data directory.** Two servers sharing a +tmux socket attach to each other's live sessions. + +## Installing as a service + +```bash +codeman service install # systemd user unit on Linux, LaunchAgent on macOS +codeman service status +codeman service uninstall +``` + +The installer's final menu offers this too. + +Notable behaviours: + +- **Your PATH is baked into the unit.** launchd hands a job + `/usr/bin:/bin:/usr/sbin:/sbin`, which finds neither a Homebrew or nvm `node` nor `tmux` + or `claude`. This is the single most common cause of a hand-written unit that starts and + immediately dies. +- **`CODEMAN_PASSWORD` is never written into the unit file.** Add it yourself if the service + needs authentication. +- **It refuses when a server is already running** on that data directory, for the same reason + detached mode does. +- **It verifies rather than assumes.** `launchctl load` and a clean spawn are both silent + about a server that starts and immediately exits, so the parent polls until the child + answers or dies. + +On Linux, if you want the service running while you are not logged in: + +```bash +loginctl enable-linger $USER +``` + +### Writing the unit by hand + +**Linux (systemd user unit):** + +```bash +mkdir -p ~/.config/systemd/user +cat > ~/.config/systemd/user/codeman-web.service << EOF +[Unit] +Description=Codeman Web Server +After=network.target + +[Service] +Type=simple +ExecStart=$(which node) $HOME/.codeman/app/dist/index.js web +Restart=always +RestartSec=10 + +[Install] +WantedBy=default.target +EOF +systemctl --user daemon-reload +systemctl --user enable --now codeman-web +loginctl enable-linger $USER +``` + +**macOS (LaunchAgent):** + +```bash +mkdir -p ~/Library/LaunchAgents +cat > ~/Library/LaunchAgents/com.codeman.web.plist << EOF + + + + + Label + com.codeman.web + ProgramArguments + + $(which node) + $HOME/.codeman/app/dist/index.js + web + + RunAtLoad + KeepAlive + StandardOutPath + /tmp/codeman.log + StandardErrorPath + /tmp/codeman.log + + +EOF +launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.codeman.web.plist +``` + +Prefer `codeman service install` where you can. It handles the PATH problem for you. + +## Logs + +```bash +journalctl --user -u codeman-web -f # systemd +tail -f ~/.codeman/web.log # detached mode +log stream --predicate 'process == "node"' # macOS, noisy +``` + +## Updating + +| Install route | Update with | +| ------------- | ------------------------------------------------------------------------ | +| Installer | Re-run the one-liner, or **App Settings → System → Updates**. | +| npm | `npm update -g aicodeman` | +| git clone | `git pull && npm install && npm run build`, then restart. | + +### The in-app updater + +**App Settings → System → Updates**, for git-clone installs supervised by systemd or +launchd. npm installs report as non-updatable, and an unsupervised install is told to +restart manually. + +The interesting part is that the update restarts the very process running it. So the real +work runs in a **detached script that outlives the restart** and writes progress to a status +file, which the browser polls across the connection drop. A dirty tree is stashed rather +than discarded. + +### After updating + +Sessions are unaffected: they live in tmux and the server reattaches. If the UI looks stale, +reload; on iOS Safari, close the tab completely and reopen. + +## Running two instances + +The data directory and the tmux socket are process wide, so a second server on the defaults +will discover and attach the first one's sessions. Scope both together: + +```bash +CODEMAN_INSTANCE=beta CODEMAN_PORT=5000 codeman web +``` + +Service unit names are instance-scoped too, so a beta instance can be installed as its own +service without colliding with the main one. `CODEMAN_DATA_DIR` and `CODEMAN_TMUX_SOCKET` +exist for the rare case where they need to differ, but setting only one of them recreates +exactly the problem you were avoiding. + +## The tunnel as a service + +```bash +systemctl --user enable codeman-tunnel +loginctl enable-linger $USER +``` + +Or the toggle in **App Settings → System → Remote access**. See +[Remote Access](Remote-Access). + +## Health checks + +```bash +curl -s localhost:3000/api/status | jq '.version, .uptime' +codeman web --status +codeman doctor +``` + +Add `-k` and the `https://` URL on an HTTPS install. + +## Read next + +- [Installation](Installation) - the routes and what each supports. +- [Remote Access](Remote-Access) - exposing it once it stays up. +- [Troubleshooting](Troubleshooting) - when it does not. diff --git a/docs/wiki/Security.md b/docs/wiki/Security.md new file mode 100644 index 00000000..2e16511e --- /dev/null +++ b/docs/wiki/Security.md @@ -0,0 +1,120 @@ +# Security + +The honest version first: **Codeman's dashboard is a remote code execution surface, by +design.** It starts agents with permission prompts skipped by default, so anyone who can +reach it can run arbitrary code as your user, on your machine. Every protection in Codeman +exists to control who that is. + +That is not a flaw to be fixed. It is what "run my coding agent for me" means. The job is to +make sure the set of people who can reach it is exactly the set you intended. + +## The default is safe + +A bare `codeman web` binds `127.0.0.1`. Only processes on that machine can reach it, which +is why shipping with no password by default is defensible. Everything risky starts when you +expose it. + +## Hardening checklist + +In order of how much they matter: + +1. **Do not expose it without `CODEMAN_PASSWORD`.** Binding a non-loopback host without one + starts, but warns loudly. A tunnel refuses outright unless you acknowledge the exposure + in the UI. +2. **Prefer Tailscale over a public tunnel.** Keeping the loopback bind and putting a + private network in front of it removes the public attack surface entirely, and gives you + real HTTPS. See [Remote Access](Remote-Access). +3. **Use a long password.** It is the only thing between a public URL and your shell. +4. **Consider the permission mode.** **App Settings → Agents & CLIs → Claude → Startup + Mode** can switch new sessions from skip-prompts to Anthropic's classifier-guarded `auto` + mode, to normal prompting, or to an explicit allowed-tools list. +5. **Use Docker cases for untrusted work.** If you are pointing an autonomous loop at a repo + you did not write, [Docker Cases](Docker-Cases) gives it its own filesystem and network + for the cost of one checkbox. +6. **Keep it updated.** Browser-driven attack paths were closed in 0.9.x and hardening is + ongoing. + +## What protects what + +These run on **every** request, including on a default no-password loopback install: + +| Layer | What it stops | +| ---------------------------- | ----------------------------------------------------------------------------------------------- | +| **Host-header allowlist** | DNS rebinding. A domain rebound to `127.0.0.1` is rejected before any handler runs. Add your own domains with `CODEMAN_ALLOWED_HOSTS`. | +| **Cross-site Origin guard** | CSRF on state-changing requests. A *missing* Origin is allowed so curl, the CLI, and hooks keep working; a foreign or opaque one is rejected. | +| **Raw `text/plain` bodies** | The CORS simple-request CSRF vector, where a cross-site form could smuggle JSON into a write route with no preflight. | +| **WebSocket origin check** | Cross-site WebSocket hijacking. The terminal upgrade closes with code `4003` on failure. | +| **Output escaping** | Stored XSS from agent-derived strings: tool names, command arguments, subagent descriptions. | +| **Security headers** | A strict content security policy, `nosniff`, frame options, and HSTS over HTTPS. CORS is reflected only for loopback origins. | + +When authentication is enabled: + +| Layer | Behaviour | +| ------------------- | ------------------------------------------------------------------------------------------------ | +| **HTTP Basic** | `CODEMAN_USERNAME` (default `admin`) and `CODEMAN_PASSWORD`. | +| **Session cookie** | A 256-bit opaque token validated server side, so it cannot be forged offline. 24 hours, extended on activity, with a device-context audit trail. | +| **Rate limiting** | Ten failed attempts per IP produce a `429` with a 15 minute decay. A correct password or valid cookie recovers immediately even under attack, which matters because all tunnel traffic shares one loopback address. | +| **QR auth** | Single-use 60-second tokens with their own separate rate limiter, so a mistyped password cannot lock out QR login. | +| **Hook endpoints** | The hook and telemetry endpoints skip Basic auth because they are called from localhost by the CLI, but when auth is on, that bypass additionally requires a per-instance hook secret. | + +## File access + +Three separate file surfaces, each confined differently, because a single shared rule would +be wrong for at least one of them: + +| Surface | Rules | +| -------------------- | -------------------------------------------------------------------------------------------- | +| **File Viewer** | Real path resolution before boundary checks, so symlinks cannot escape. Sensitive trees blocked. Edit mode adds an extension allowlist, a size cap, `.git` denial, and optimistic concurrency. It never creates files. | +| **Attachments** | An id-based registry, so browser requests never carry absolute paths. The magic-link scanner is prompt-injectable by nature and is therefore force-confined to the session's workspace. Extension allowlist, not a blocklist. | +| **Path picker** | Its own root allowlist rather than the workspace confinement. In multi-user mode a non-admin gets only their own user space, because per-user spaces live inside the home directory. | + +Downloads block sensitive paths outright (`.env`, credentials files, `~/.ssh`, AWS +credentials), and SVG and HTML are served as downloads with `nosniff` so they cannot execute +in the page. + +## Supply chain and isolation + +- Security-sensitive transitive dependencies are pinned to patched versions, and lockfile + integrity is checked on every push and pull request: every entry must resolve to the public + registry with a hash. +- Public assets are scanned for NUL bytes and syntax-checked in CI. +- `CODEMAN_INSTANCE` scopes the tmux socket and the data directory together, so two + instances never attach each other's live sessions. + +## What Codeman does not protect against + +Stated plainly, because a security page that only lists strengths is not useful: + +- **Multi-user mode is not a sandbox.** It separates workspaces. Every session still runs as + the same OS account, so a determined user's agent can reach another user's files. For real + isolation, pair users with Docker cases or run separate instances under separate OS + accounts. +- **An agent you gave shell access can do anything you can.** Permission modes narrow this; + they do not remove it. +- **A tunnel makes your machine reachable from the internet.** The password is the whole + boundary. Treat it accordingly. +- **Codeman cannot detect your own loopback reverse proxy**, which is why the hook-endpoint + bypass requires a secret unconditionally when auth is on. +- **The agent CLIs have their own trust models.** Pi's project trust executes repo-local + TypeScript, for instance. See [Agent CLIs](Agent-CLIs). + +## Privacy + +No telemetry, no analytics, no phone-home. Codeman's only network traffic is between your +browser and your server. Your agent CLI's traffic is its own, on your account. + +Two features send data outward, both off by default and both stated where they appear: voice +dictation through your Claude login, and the Read My Mind prediction call. + +## Reporting a vulnerability + +**Never in a public issue.** +[SECURITY.md](https://github.com/Ark0N/Codeman/blob/master/.github/SECURITY.md) has the +private disclosure process and the current list of known limitations. + +## Read next + +- [Remote Access](Remote-Access) - the safe ways to expose it. +- [Multi-User Mode](Multi-User-Mode) - what it does and does not separate. +- [Docker Cases](Docker-Cases) - real isolation for untrusted work. +- [`docs/security-architecture.md`](https://github.com/Ark0N/Codeman/blob/master/docs/security-architecture.md) - the complete model. diff --git a/docs/wiki/Settings-Reference.md b/docs/wiki/Settings-Reference.md new file mode 100644 index 00000000..3eec0d2d --- /dev/null +++ b/docs/wiki/Settings-Reference.md @@ -0,0 +1,172 @@ +# Settings Reference + +Two settings surfaces, and the rule that explains why a setting you changed on your laptop +did not follow you to your phone. + +| Surface | Scope | Opened from | +| ------------------- | ------------------------------ | ---------------------------- | +| **App Settings** | Global, this Codeman install. | The header gear. | +| **Session Options** | One session. | The session's tab. | + +App Settings is a single scrolling document with a rail acting as a table of contents; +clicking a rail entry scrolls rather than switching. Session Options genuinely switches +panels. + +## Per-device versus synced + +Some settings live on the server and follow you to every device. Others are stored in the +browser and stay put. This is deliberate, not an oversight: your phone wants a different +font size, a different keyboard bar, and a different set of header buttons than your +desktop. + +| Category | Examples | +| ----------------------- | ------------------------------------------------------------------------------- | +| **Per-device, local** | Skin, WebGL renderer, local echo, CJK input, extended keyboard bar, File Viewer and Cron header buttons. Never sent to the server at all. | +| **Per-device policy** | Most `show*` toggles, plan usage chip, language. Stored server-side, but a device only takes the server value when it has no local one of its own. | +| **Synced** | Models, effort, CLI options, notification preferences, voice settings, display name, the agent skill and approvals toggles. | + +The practical rule: **appearance and input are per device, behaviour is shared.** If a change +did not follow you, it is in one of the first two rows, and you change it again on that +device. + +## App Settings + +### Updates + +Current version, a manual check, and the in-app updater. Covers git-clone installs +supervised by systemd or launchd; npm installs report as non-updatable. See +[Running As A Service](Running-As-A-Service). + +### Terminal & Input + +| Setting | Default | Notes | +| ----------------------------- | -------------------- | --------------------------------------------------------------------- | +| Local Echo | On for touch devices | Paints keystrokes locally and flushes on Enter. See [Input And Voice](Input-And-Voice). | +| CJK Input | Off | IME composition through a dedicated text field. | +| Extended Keyboard Bar | Per device | Which accessory bar phones get. Shell sessions override it while they are active. | +| Wheel Scrolls Local History | Off | Keeps the wheel on the local buffer instead of forwarding it to the CLI. | +| WebGL Renderer | On | With a GPU-stall watchdog that falls back to DOM rendering. | +| Gesture Control | Off | Camera hand tracking. Also needs `CODEMAN_GESTURE=1` on the server. | + +### Header & Panels + +Chips for every optional header control, with a live preview of the resulting header: + +Run, Font Size, System Stats, Redraw Terminal, Response Viewer, Away Digest, Session +Manager, Attachments, File Viewer, Multi-monitor, Plan Usage, Lifecycle Log, Monitor, +Project Insights, File Browser, Subagents, Approvals Inbox, Read My Mind, Ultracode Agents, +Ultracode Windows, Cron. + +Most default to off. The stock desktop header is system stats, File Viewer, and the gear. +New header controls never appear on phones. + +This section also holds background-agent tracking, including whether to track agents for +every session or only the active tab. + +### Appearance + +| Setting | Notes | +| ---------------------- | ----------------------------------------------------------------------------------------- | +| Skin | Theme palettes, light ones included. Applied before first paint, so no flash of the wrong theme. | +| Entrance Animations | Per-surface animation styles for tabs, terminals, windows, and lineage lines. All default to the legacy no-animation behaviour. | +| Display Name | Your name in the UI. Cosmetic only; it never renames the package, CLI, API, or storage. | +| Interface Language | English or Simplified Chinese. Per device. | +| Session List Layout | Header tab strip (default) or a collapsible left sidebar. See [The Dashboard](The-Dashboard#session-list-layout). | +| Tall Tabs | Taller tab strip. | +| Pop-out Button on Tabs | Adds the detach control to tabs, with a per-tab override. | +| Spawn Lineage Lines | Arcs from a parent tab to sessions it spawned. Desktop only, on by default. | +| Overview Home Screen | The phone home screen. On by default. | + +### Models + +Claude model cards, the 1M context window switch, and the thinking effort segment. The cards +and the switch compose into one model choice, so there is no separate "which one wins" +question. + +Model and effort are both **soft defaults**: the model is written into the case's +`.claude/settings.local.json` and effort is passed at start, so `/model` and `/effort` +inside a session override them at any time. + +### Agents & CLIs + +| Setting | Notes | +| -------------------------------- | -------------------------------------------------------------------------------------------- | +| Startup Mode | Claude's permission mode for new sessions. Default skips prompts; `auto` uses Anthropic's classifier-guarded mode; `normal` prompts; or give an explicit allowed-tools list. | +| Allowed Tools | The list used by the explicit mode. | +| Ralph / Todo Tracker | Enables the Ralph loop surfaces. | +| Agent Teams | Experimental teams. Also needs the CLI's own environment flag. | +| Codeman Agent Skill | Injects the agent skill into new Claude sessions per case. Off by default. See [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent). | +| Remote auto-reconnect | Reattaches dropped remote SSH sessions. On by default. | +| Nice priority / value | Runs agent processes at a lower CPU priority. | +| Bypass approvals and sandbox | Pi's project trust. Read [Agent CLIs](Agent-CLIs) before enabling. | +| Animated status effects | Cosmetic. | + +### Notifications + +Master toggle, browser notifications, push subscription, audio alerts, and the idle +threshold that decides when a quiet session counts as needing you. See +[Notifications And Approvals](Notifications-And-Approvals). + +### Voice + +Active provider and the engine behind it, insert mode, language, domain keywords to bias +recognition, the Deepgram API key, and the opt-in switch for transcribing through this +server's Claude login, with its live credential status. See +[Input And Voice](Input-And-Voice). + +### Shortcuts + +Rebinding for the shortcut registry. See [Keyboard Shortcuts](Keyboard-Shortcuts). + +### System + +`CLAUDE.md` template for new cases, default working directory, the image watcher, and +Cloudflare tunnel controls including the tunnel and upload URLs. In multi-user mode, the +**Users** administration entry is injected here. + +## Session Options + +Per session, from the tab. + +| Panel | Contains | +| ---------------- | ------------------------------------------------------------------------------------------- | +| **Respawn** | Auto-resume on usage limit, the respawn cycle configuration, presets, duration. See [Keeping Agents Running](Keeping-Agents-Running). | +| **Session** | Name, working directory, environment overrides, per-tab pop-out override. | +| **Ralph / Todo** | Loop configuration, iteration and todo caps, circuit breaker reset. See [Autonomous Loops](Autonomous-Loops). | +| **Summary** | What this session has done: tokens, activity, run summary. | + +Panels that only make sense for Claude are hidden for other run modes rather than shown and +failing. + +## Environment variables + +Some things are configured before the server starts, not in the UI: + +| Variable | Effect | +| ----------------------------------- | ---------------------------------------------------------------------- | +| `CODEMAN_PORT` | Listen port. | +| `CODEMAN_HOST` | Bind address. Loopback by default. | +| `CODEMAN_PASSWORD` / `CODEMAN_USERNAME` | HTTP Basic credentials. Username defaults to `admin`. | +| `CODEMAN_ALLOWED_HOSTS` | Extra Host and Origin allowlist entries for a reverse proxy. | +| `CODEMAN_INSTANCE` | Scopes the data directory and tmux socket together. Required for a second instance. | +| `CODEMAN_MULTIUSER` | Enables multi-user mode. | +| `CODEMAN_GESTURE` | Makes gesture control available to be enabled. | +| `CODEMAN_DOCKER_BRIDGE_HOOKS` | Lets in-container hooks reach the host on a loopback bind. | +| `CODEMAN_FILE_PICKER_ROOTS` | Extra roots for the path picker. | +| `CODEMAN_ALLOW_UNAUTHENTICATED_NETWORK` | Acknowledges exposing the server with no password. | + +## Gotchas + +- **A setting that did not sync is per device.** Change it again on that device. +- **The plan usage chip and its telemetry exporter are one setting.** Enabling the chip + without the exporter would leave it blank forever, so it is deliberately not separable. +- **Toggling a header button does nothing on a phone.** Phones deliberately ignore most of + the header chips. +- **Enabling a feature does not retroactively configure existing sessions.** The agent skill + injection, for instance, applies at session creation. + +## Read next + +- [The Dashboard](The-Dashboard) - what each control does once visible. +- [Keeping Agents Running](Keeping-Agents-Running) - the Respawn panel in depth. +- [Agent CLIs](Agent-CLIs) - model, effort, and permission modes. diff --git a/docs/wiki/The-Dashboard.md b/docs/wiki/The-Dashboard.md new file mode 100644 index 00000000..5a0cac5d --- /dev/null +++ b/docs/wiki/The-Dashboard.md @@ -0,0 +1,204 @@ +# The Dashboard + +What the interface is telling you, and which parts of it are hidden until you turn them on. + +Most of Codeman's UI is **opt-in**. A stock install shows a deliberately small header, and a +feature you read about here may simply not be on screen yet. Where that is the case, this +page says so and names the setting. + +![Codeman dashboard](https://raw.githubusercontent.com/Ark0N/Codeman/master/docs/images/codeman-tour-20260724.png) + +## Layout + +| Region | What lives there | +| ------------------ | -------------------------------------------------------------------------------------- | +| **Header, left** | The "C" logo (goes home) and the session list, unless you moved it to the sidebar. | +| **Header, right** | Status chips and panel buttons, most of them off by default. | +| **Center** | The terminal for the active session, or the home screen when nothing is selected. | +| **Bottom toolbar** | Run, Stop, Run Shell, the case picker, and the instance counters. | +| **Overlays** | Panels and modals: Respawn, Cron, Subagents, File Viewer, Settings. | + +## Session list layout + +The session list lives in the header as a horizontal strip by default. With a lot of +sessions open that strip stops being scannable, so **App Settings → Appearance → Tabs → +Session List Layout** can move it into a vertical sidebar on the left instead. + +| Layout | Behaviour | +| -------------------- | --------------------------------------------------------------------------------- | +| **Header tab strip** | The default. Wraps to a second row on desktop, scrolls sideways on a phone. | +| **Left sidebar** | A vertical list with a filter box and a live session count. `Alt+B` collapses it to a narrow rail that keeps the status dots and task badges visible. On a phone it is an off-canvas drawer rather than a docked rail. | + +It is the same list either way, just re-hosted: tab order, drag-to-reorder, the `Alt+1` +to `Alt+9` numbers and every status colour below behave identically in both. The setting is +per device, so a sidebar on your desktop does not force one onto your phone. + +## Session tabs + +One tab per session, in your order, and that order syncs across your devices. + +**Status is carried by the dot and the tab's own styling:** + +| Look | Meaning | +| ----------------------------- | ----------------------------------------------------------------------- | +| Green dot | Alive, not currently working. | +| Pulsing green dot with a ring | Working on a turn. | +| Yellow tab, blinking | The agent is waiting for input from you. | +| Red tab, blinking | A question or permission prompt is blocking the session. | +| No dot | The session is not running. | + +![Tab alerts](https://raw.githubusercontent.com/Ark0N/Codeman/master/docs/images/tab-alerts-20260815.png) + +The alert states are steady colour with a pulse layered on top, not a blink between the +alert colour and nothing, so a tab that needs you looks like it needs you at every point in +the cycle. They survive a page reload: the state is re-seeded from the server on load, so +reloading while a permission prompt is blocking does not lose the red tab. + +**Navigation:** + +| Action | Keys | +| ------------------------------- | ------------------------------------------------------- | +| Jump to tab N | `Alt+1` to `Alt+9` (the number on the tab) | +| Next / previous | `Ctrl+Tab`, `Alt+[`, `Alt+]` | +| Move the active tab | `Ctrl+Shift+{`, `Ctrl+Shift+}` | +| Close | `Ctrl+W` | +| Find any session, open or past | `Ctrl+K` (also `Cmd+K` and `Alt+K`) | + +Tabs can also be dragged to reorder. + +On phones the strip scrolls horizontally instead of wrapping, and the active tab is always +scrolled into view. It is not reordered to the front, so the `Alt+N` numbering stays stable. + +### Lineage arcs + +When one session spawns another (an agent starting a worker through the API), Codeman draws +a coloured arc under the strip connecting parent to child, with one colour per child. It is +how a fan-out of eight workers stays readable. + +Desktop only, and on by default. Turn it off in **App Settings → Appearance**. Arcs are +skipped for tabs scrolled out of the strip. + +## Header controls + +The right side of the header. Almost all of these are off until you enable them in +**App Settings → Header & Panels**. + +| Control | Default | What it does | +| ---------------------- | ------------------ | ------------------------------------------------------------------------------- | +| Connection dot | Always on | SSE connection health. Green is connected. | +| Font size `-` / `+` | Always on | `Ctrl +` / `Ctrl -` do the same. | +| CPU / MEM bars | On | Server resource use. | +| File Viewer | On | Toggles the file browser panel. | +| Settings gear | Always on | App Settings. | +| Plan usage chip | On, desktop only | Live Claude subscription usage. Claude-only, and needs its telemetry exporter, which the same setting installs. | +| Session Manager | Off | The full session list, live and historical. | +| Approvals bell | Off | Cross-session queue of prompts waiting on a human. Appears only when the count is above zero. Never shown on phones. | +| Read My Mind 🧠 | Off | Predicts your next prompt for this case. Claude-only. | +| Attachments | Off | Registered external files. | +| Away Digest | Off | What happened while you were gone. | +| Last Response | Off | Readable view of the agent's last answer, useful on phones. | +| Ultracode / Workflow | Off | Live workflow-run agents. | +| Notifications | Off | Notification history and settings. | +| Lifecycle Log | Off | Session start, exit, and kill audit trail. | +| Cron ⏰ | Off | Scheduled jobs. | +| Multi-monitor | Off, macOS | Opens a window spanning every display. | +| Tunnel indicator | When a tunnel runs | Cloudflare tunnel status. | +| Admin panel | Multi-user only | User administration. | + +New header controls never appear on phones. Phone layout is deliberately minimal and is +covered in [Mobile Guide](Mobile-Guide). + +## Connection state + +The dot in the header is the quick read. Two louder surfaces exist because a cached page +with no server behind it used to look identical to a page with no sessions: + +- **A full-screen overlay** when the page has never loaded server state. There is nothing + behind it worth preserving. +- **A banner** when the connection drops after state had loaded, so your scrollback stays + readable. + +Both wait about 2.5 seconds before appearing, so a deploy that restarts the server does not +flash a warning at you every time. If the browser reports itself offline, the grace period +is skipped. + +There is also a watchdog for the case where the connection stops delivering without +erroring. If the server's heartbeat stops arriving, Codeman reconnects on its own rather +than sitting on a green dot showing frozen data. + +## The terminal + +A real terminal: xterm.js in the browser, a real PTY on the server, tmux in between. Full +TUIs render correctly. + +Worth knowing: + +- **Scrollback.** The first time you open a session, Codeman pulls the entire tmux + scrollback, not just the recent tail. Scrolling to the very top pulls again on demand. +- **Wheel and touch scrolling** are forwarded into Claude's own transcript on recent Claude + versions, so the wheel scrolls the conversation rather than the terminal. `Shift+Wheel` is + always local scrollback. Other CLIs scroll locally. +- **Selection copy.** `Ctrl+C` copies when text is selected and interrupts when it is not. + `Ctrl+Shift+C` always copies. +- **Zero-lag input.** On touch devices, keystrokes paint locally before the round trip. See + [Input And Voice](Input-And-Voice). +- **Renderer.** WebGL by default, with a watchdog that falls back to DOM rendering if the + GPU stalls. `?nowebgl` forces DOM rendering for one page load. + +## The home screen + +With no session selected you get the welcome screen: run buttons for the CLIs Codeman +found, a QR code when a password is set, cross-session search, and **Resume Conversation**, +which lists past sessions including Claude conversations started outside Codeman entirely. + +Two extras depending on the device: + +- **Desktop, wide windows**: your open tabs appear as a rail docked to the left edge, in tab + order, with created and last-active stamps. It needs at least 1180px of width; below that + it is hidden so it cannot overlap the search panel. +- **Phones**: tapping the "C" logo gives a session overview instead: NEEDS YOU first, then + current sessions, then past ones. On by default. + +## Panels + +| Panel | Opened from | Covered in | +| ---------------- | --------------------------------- | ---------------------------------------------------------------- | +| Respawn | Session Options | [Keeping Agents Running](Keeping-Agents-Running) | +| Ralph | Session Options | [Autonomous Loops](Autonomous-Loops) | +| Orchestrator | Toolbar | [Autonomous Loops](Autonomous-Loops) | +| Cron | Header ⏰ (opt-in) | [Cron Jobs](Cron-Jobs) | +| Subagents | Automatic while agents run | [Watching Agents Work](Watching-Agents-Work) | +| Ultracode | Header (opt-in) | [Watching Agents Work](Watching-Agents-Work) | +| File Viewer | Header | [Working With Files](Working-With-Files) | +| Attachments | Header (opt-in) | [Working With Files](Working-With-Files) | +| Approvals | Header bell (opt-in) | [Notifications And Approvals](Notifications-And-Approvals) | +| App Settings | Header gear | [Settings Reference](Settings-Reference) | + +Session-specific configuration lives in **Session Options**, reachable from the tab. App +Settings is global; Session Options is per session. + +## Search and the session palette + +`Ctrl+K` opens the session palette: every session, live or historical, filtered as you +type. Picking a past one resumes its conversation. + +The search box on the home screen is wider in scope. It federates over session metadata, +run-summary events, and attachment history, filtered by type, case, status, and date. It +does substring matching over data already in memory, with no regex and no filesystem reads, +so it is fast and cannot be turned into a traversal. + +## Appearance + +**App Settings → Appearance** carries the theme skins, including light ones. The choice is +applied before the first paint, so there is no flash of the wrong theme on load. + +The same section has the entrance animations for tabs, terminals, agent windows, and +lineage lines. All of them default to the legacy no-animation behaviour, so an untouched +install animates nothing. + +## Read next + +- [Keyboard Shortcuts](Keyboard-Shortcuts) - the full list, and how to rebind. +- [Settings Reference](Settings-Reference) - every setting, and why some follow you across devices and others do not. +- [Mobile Guide](Mobile-Guide) - what changes on a phone. +- [Watching Agents Work](Watching-Agents-Work) - subagent windows and workflow runs. diff --git a/docs/wiki/Troubleshooting.md b/docs/wiki/Troubleshooting.md new file mode 100644 index 00000000..75b3c768 --- /dev/null +++ b/docs/wiki/Troubleshooting.md @@ -0,0 +1,290 @@ +# Troubleshooting + +Symptom first. Find the line that matches what you are seeing. + +Before anything else, check what version you are on and whether the problem is already +fixed: + +```bash +codeman --version +codeman doctor +``` + +## Installing and starting + +### `Failed to start claude: error: posix_spawnp failed` on macOS + +node-pty ships its macOS `spawn-helper` without the executable bit, and macOS launches +every PTY through it. Codeman detects this and repairs it on the first failure, so updating +usually fixes it outright. To repair by hand on a clone install: + +```bash +npm run fix:node-pty +``` + +It is a `chmod`, not a rebuild, so it does not need Xcode command line tools. The helper +lives in `prebuilds/darwin-/`, not `build/Release/`, which does not exist on macOS. +Linux never sees this. + +### `tmux: command not found` + +The installer asks before installing packages and remembers a declined answer. Install tmux +and start again. There is no tmux-free mode: sessions live in tmux. + +### The port is already in use + +```bash +codeman web --port 8080 # or set CODEMAN_PORT +``` + +If you believe nothing is on 3000, check for a Codeman you already started: + +```bash +codeman web --status +``` + +### The terminal area is blank, and the console mentions a missing vendor file + +Clone installs build the vendored xterm addon bundles in `postinstall`. If `npm install` +was interrupted or run with `--ignore-scripts`, those bundles are missing: + +```bash +npm install +``` + +They are intentionally not committed to the repository. + +### `Case path not found` when clicking Run + +The case points at a directory that no longer exists, usually because it was deleted or +moved outside Codeman. Re-link the case, or create it again. + +### The server starts but nothing is reachable + +That is the default behaviour, not a failure. Codeman binds `127.0.0.1`. See +[Remote Access](Remote-Access). + +## Reaching the interface + +### The dashboard will not load from another device + +Check, in order: the bind (loopback by default), a firewall, and then +[Remote Access](Remote-Access) for a supported way to expose it. + +### `403 host not allowed` + +The Host header is not in the allowlist, which is the DNS-rebinding guard doing its job. Add +your domain: + +```bash +CODEMAN_ALLOWED_HOSTS='codeman.example.com,.internal.example.com' +``` + +A leading dot matches subdomains. + +### The page loads but the terminal never connects + +The terminal is a WebSocket. Behind a reverse proxy, the upgrade must be forwarded. The +upgrade also runs the Host and Origin checks and closes with code `4003` when they fail. + +### The UI looks stale after updating + +The app shell is cached by a service worker, and static assets are served with a long cache +lifetime. `index.html` is not cached, and every asset reference is version-stamped, so a +normal reload picks up a new build. + +Two exceptions worth knowing: + +- **iOS Safari** can keep serving old JavaScript until the tab is fully closed, not just + reloaded. Close the tab and reopen it. +- If you edit files in dev, changes to `index.html` need a server restart. Changes to `.js` + and `.css` do not. + +### A full-screen "cannot reach the server" overlay appears + +The server is genuinely unreachable, or the connection dropped. Codeman waits about 2.5 +seconds before showing it, so a quick restart does not flash it. Retry re-arms both the +event stream and the terminal socket. + +## Sessions + +### A session shows idle while it is clearly working + +Update. Claude redraws its prompt roughly once a second throughout a turn, and older idle +detection treated that as the end of the turn, flipping working sessions to idle a couple of +seconds in. Current versions confirm against the actual screen before believing it. + +### A session is stuck showing busy + +For non-Claude CLIs, idle detection is output-based and coarser by necessity: those CLIs +expose no hooks. A session that has genuinely gone quiet will settle. If it never does, +interrupt it (`Ctrl+C` with nothing selected). + +### The agent asks about bypass permissions every time + +That prompt comes from Claude Code, not Codeman. Codeman's default is to start with +permission prompts skipped, which is what the security model is built around. If you would +rather it prompted, change **App Settings → Agents & CLIs → Claude → Startup Mode**. + +### Sessions vanished after a reboot + +Expected. tmux does not survive a reboot, so the sessions are gone. Conversations are not: +Claude transcripts persist, so the welcome screen's **Resume Conversation** list can pick +them back up. + +### A session restarts, then refuses to restart again + +That is the PTY-exit circuit breaker. Repeated rapid PTY exits trip it, and it blocks +automatic restarts so a broken configuration does not spin forever. Reset it explicitly from +the session's controls. Reattaching does not clear it, deliberately. + +### Sessions I did not create appeared, or my session resized itself + +Two Codeman servers are running against the same data directory and tmux socket. The second +one discovers and attaches the first one's sessions. Give each instance its own scope: + +```bash +CODEMAN_INSTANCE=beta CODEMAN_PORT=5000 codeman web +``` + +`codeman web -d` and `codeman service install` both refuse to start a second server on one +data directory for exactly this reason. + +## The terminal + +### I cannot scroll back through history + +Scrollback behaviour depends on the CLI, and Codeman adjusts what it strips per mode. +Things to try: + +- `Shift+Wheel` always scrolls the local buffer, whatever else is going on. +- On Claude sessions with a recent CLI, the wheel is forwarded into Claude's own transcript, + so it scrolls the conversation rather than the terminal buffer. That is intended. +- Scrolling to the very top pulls the full tmux scrollback again on demand. + +### The wheel does nothing in a Codex session + +Codex ignores the mouse reports that forwarding would send, so Codeman does not forward +there. Scrolling is local, and `Shift+Wheel` behaves the same way. + +### `Ctrl+C` copies when I wanted to interrupt + +With a selection, `Ctrl+C` copies. With no selection, it interrupts. Clear the selection +first, or use the **Stop** button. `Ctrl+Shift+C` always copies and never interrupts. + +### I typed a prompt but nothing was sent + +On touch devices, keystrokes are painted locally and flushed when you press Enter, so text +on screen has not necessarily reached the agent yet. Press Enter, or the phone toolbar's +**Enter** button. + +If you are sending input over the API instead, your payload must end with `\r` or no Enter +is ever sent. The request still succeeds and the text sits unsubmitted in the composer. See +[Driving Codeman From An Agent](Driving-Codeman-From-An-Agent). + +## Mobile + +### The keyboard covers the terminal, or scroll position jumps + +Update first; several rounds of fixes have gone into keyboard resize and scroll restoration. + +### I cannot reach the rightmost tabs + +The strip scrolls horizontally on phones and the active tab is scrolled into view +automatically. Swipe the strip itself. If a background render snaps you back, update. + +### The space key does nothing on Android + +A long-standing Android keyboard bug, fixed some time ago. Update. + +### The keyboard will not close + +Tap outside the terminal, or tap twice on inert terminal content. Tapping a control does not +dismiss it, by design. + +## Agents and CLIs + +### A CLI is installed but Codeman does not offer it + +Codeman resolves binaries from the environment the **server** runs in. + +```bash +codeman doctor +``` + +If it runs as a service, launchd gives the job a minimal PATH. `codeman service install` +bakes your PATH into the unit; a hand-written plist does not. Restart the server after +installing a new CLI. + +### Hooks stopped working after switching to HTTPS + +Hook callbacks have to accept the self-signed certificate. Recent versions self-heal +existing cases; if yours predates that, recreate the case so its hooks are rewritten. + +### The model or effort I chose is not being used + +Both are **soft defaults**, on purpose. The model is written into the case's +`.claude/settings.local.json` and effort is passed on the command line at start, so `/model` +and `/effort` inside the session override them at any time. Effort is deliberately never +passed as an environment variable, because that hard-locks it. + +### Tab alerts and approvals never fire in one of my repos + +That case is missing its hooks block. Recreating the case rewrites it. + +## Docker and remote + +### Docker sessions do not detect idle + +On a loopback-only bind, a container cannot reach `127.0.0.1` on the host, so in-container +hooks have nothing to call. Set `CODEMAN_DOCKER_BRIDGE_HOOKS=1` to open a hooks-only +listener on the docker bridge gateway. Without it, idle detection falls back to output +watching. + +### A rebuilt agent image still has old CLI versions + +Always rebuild with `--no-cache`: + +```bash +node scripts/build-agent-image.mjs --no-cache +``` + +A plain rebuild reuses the cached `npm install -g` layer and keeps the CLIs frozen at their +original versions while reporting success. + +### A remote SSH session dropped and did not come back + +A bounded-backoff watcher reattaches dropped sessions, and it is on by default. Intentional +kills are never revived. Check the host is reachable and that the remote tmux server is +still running. + +## Gathering diagnostics + +```bash +codeman doctor # dependency check +curl -s localhost:3000/api/status | jq # full app state +tmux -L codeman list-sessions # what tmux thinks is alive +journalctl --user -u codeman-web -f # service logs (Linux) +tail -f ~/.codeman/web.log # detached mode logs +``` + +On an HTTPS install, add `-k` to the curl commands and use the `https://` URL. + +## Filing a good bug report + +Open an [issue](https://github.com/Ark0N/Codeman/issues) with: + +- OS and version. +- Install method: installer, npm, or git clone. +- `codeman --version`. +- Browser and version, if the problem is in the UI. +- Which CLI the session was running, and its version. +- What you did, what happened, what you expected. + +Reports usually get a response within a day, and every release credits its reporters by +name. + +Questions and setup help fit better in +[Discussions](https://github.com/Ark0N/Codeman/discussions). Security problems never go in a +public issue; see +[SECURITY.md](https://github.com/Ark0N/Codeman/blob/master/.github/SECURITY.md). diff --git a/docs/wiki/Versioning.md b/docs/wiki/Versioning.md new file mode 100644 index 00000000..455d49ff --- /dev/null +++ b/docs/wiki/Versioning.md @@ -0,0 +1,72 @@ +# Versioning + +Codeman follows [semantic versioning](https://semver.org/). This page says what the version +number actually promises, which matters if you are building anything against Codeman. + +## Covered by the version number + +Breaking any of these after 1.0 requires a **major** bump: + +1. **The CLI.** Command names, documented flags, and their behaviour. The npm package is + `aicodeman` and installs both the `aicodeman` and `codeman` commands; renaming either is + breaking. +2. **The HTTP API and SSE channel**, served under `/api/v1` with the uniform envelope and + conventional status codes. Endpoint paths, the envelope, `errorCode` values, and SSE event + names are all stable. +3. **Documented deployment environment variables**: `CODEMAN_PASSWORD`, `CODEMAN_USERNAME`, + `CODEMAN_HOST`, `CODEMAN_PORT`, `CODEMAN_INSTANCE`, `CODEMAN_ALLOWED_HOSTS`, + `CODEMAN_DATA_DIR`, `CODEMAN_TMUX_SOCKET`, plus the `--host`, `--port`, and `--https` + flags. +4. **The published `xterm-zerolag-input` library**, on its own independent version line. + Codeman reaching 1.0 says nothing about that package's version. + +Additive changes are **not** breaking: new endpoints, new optional fields, new error codes, +new SSE events. Genuinely breaking API changes would ship under a new prefix rather than +changing `/api/v1`. + +## Not covered + +These can change in a minor or even patch release: + +1. **The `~/.codeman/` state file formats.** Migrations are made on a best-effort basis and + have been done across renames, but the on-disk shape is not a contract. Do not write + tooling against it. +2. **Internal TypeScript modules.** The npm package is CLI-only. There is no stable library + entry point, and importing it programmatically is unsupported. +3. **Experimental and opt-in features**, whatever the app's version: gesture control, agent + teams, and anything labelled experimental in the UI or docs. + +## Deprecation + +- Additive changes are preferred over breaking ones. +- A covered surface slated for removal is deprecated first: it keeps working for at least one + minor release, with a runtime warning and a changelog note pointing at the replacement, + then is removed in the next major. +- Backwards-compatibility shims are kept until a major boundary. + +## Releases + +Releases are managed with changesets. Every release: + +- Bumps the version and updates + [`CHANGELOG.md`](https://github.com/Ark0N/Codeman/blob/master/CHANGELOG.md). +- Publishes to npm as `aicodeman`. +- Cuts a GitHub release, tagged `codeman@X.Y.Z`. +- **Credits its contributors and bug reporters by name** in the release notes. + +There is no fixed cadence. Patches ship when fixes are ready, which in practice is often. + +## Which version am I on? + +```bash +codeman --version +``` + +Or **App Settings → Updates**, which also checks for a newer one and can install it. See +[Running As A Service](Running-As-A-Service). + +## Read next + +- [HTTP API](HTTP-API) - the stable API surface itself. +- [Contributing](Contributing) - how changes get made. +- [`docs/versioning-policy.md`](https://github.com/Ark0N/Codeman/blob/master/docs/versioning-policy.md) - the authoritative statement. diff --git a/docs/wiki/Watching-Agents-Work.md b/docs/wiki/Watching-Agents-Work.md new file mode 100644 index 00000000..c61eec48 --- /dev/null +++ b/docs/wiki/Watching-Agents-Work.md @@ -0,0 +1,112 @@ +# Watching Agents Work + +Modern agents fan out. A single Claude session can be running six subagents, and the parent +terminal shows you almost none of it. Codeman surfaces that hidden work as live windows, +panels, and after-the-fact summaries. + +Everything on this page is Claude-only. It reads Claude Code's transcripts and team state; +the other CLIs expose no equivalent. + +![Subagent windows](https://raw.githubusercontent.com/Ark0N/Codeman/master/docs/images/subagent-windows-20260724.png) + +## Subagent windows + +When a Claude session spawns subagents, each one gets its own floating window with a live +transcript: what it was asked to do, what it is doing, and what it returned. + +- Windows are draggable and resizable, and their positions persist across reloads. +- A connection line links each window to the session tab that spawned it, so with four + sessions running you can still tell whose worker is whose. +- Closing a window does not stop the subagent. It only stops you watching it. + +This is the feature that makes a fan-out legible. Without it, a lead session that spawned +eight workers looks like a stalled terminal for several minutes. + +## Session lineage arcs + +The tab strip draws a coloured arc from a parent tab to any tab it spawned, one colour per +child. That covers the other direction of fan-out: not subagents inside one session, but +whole sessions started by an agent through the API. + +Desktop only, on by default, and toggled in **App Settings → Appearance**. Arcs are skipped +for tabs scrolled out of view. + +See [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) for the spawning side. + +## Agent teams + +Claude Code's experimental agent teams appear as teammates alongside subagents. Enable them +in the CLI's own environment: + +```bash +CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 +``` + +and turn the per-case **Agent Teams** toggle on in the case settings gear. + +Codeman watches the team directory and matches teammates to the session leading them. +Teammates are in-process threads rather than separate CLI processes, so they show up as +windows, not tabs. + +Notes and the experiment log: +[`docs/agent-teams/`](https://github.com/Ark0N/Codeman/tree/master/docs/agent-teams). + +## Ultracode and workflow runs + +When Claude runs a Workflow, dozens of agents can be in flight at once. The completion +artifact for a run is only written at the **end**, so a live run would otherwise be +invisible until it finished. Codeman synthesizes the in-flight view from the transcripts and +lets the real artifact supersede it when it lands. + +Two independent toggles, both off by default: + +| Setting | Shows | +| ---------------------- | ----------------------------------------- | +| Ultracode panel | A docked panel listing the run's agents. | +| Ultracode windows | Floating windows, like subagents. | + +Turning on either starts the watcher. + +## Reading the answer, not the terminal + +**Last Response** (header button, opt-in) renders the agent's last answer as scrollable text +rather than terminal output. It exists mostly for phones, where reading a long answer in a +terminal viewport is painful. **More** loads additional context. + +## After the fact + +| Surface | Answers | +| ------------------ | -------------------------------------------------------------- | +| **Away Digest** | What happened while I was gone? | +| **Run summary** | What did this run actually do? | +| **Lifecycle log** | When did sessions start, exit, or get killed, and why? | +| **Token stats** | What did it cost? | + +The Away Digest aggregates the lifecycle log, run summary events, live sessions, token +statistics, and recent subagents into one view. It is the right first thing to open in the +morning after an overnight run. + +All of these header buttons are opt-in: **App Settings → Header & Panels**. + +## Performance + +The design target is 20 sessions and 50 agent windows at 60fps. If you routinely run more +than that, expect the browser rather than the server to be the limit, and close windows you +are not reading. + +## Gotchas + +- **A session pointed at a relocated Claude config directory goes blind here.** Transcripts + written outside `~/.claude/projects` are invisible to the watchers, so subagent windows, + the ultracode panel, the response viewer, and Read My Mind all stop working for that + session. Symlink `projects` back into the shared tree to fix it. See + [Agent CLIs](Agent-CLIs). +- **Closing a window does not cancel the agent.** Nothing on this page controls agents; it + observes them. +- **Windows are opt-in for ultracode, automatic for subagents.** + +## Read next + +- [The Dashboard](The-Dashboard) - where these surfaces live. +- [Driving Codeman From An Agent](Driving-Codeman-From-An-Agent) - the other kind of fan-out. +- [Autonomous Loops](Autonomous-Loops) - the loops that generate this much activity. diff --git a/docs/wiki/Web-Tabs.md b/docs/wiki/Web-Tabs.md new file mode 100644 index 00000000..76744dad --- /dev/null +++ b/docs/wiki/Web-Tabs.md @@ -0,0 +1,101 @@ +# Web Tabs + +Open any dashboard you run, Grafana, Uptime Kuma, Portainer, a status page on port 4000, as +a tab beside your agent sessions. Codeman becomes one mission control instead of Codeman +plus a pile of browser tabs. + +A web tab is **not a session**. There is no PTY, no tmux, and no respawn behind it, the same +way a docker case is not a run mode. + +## Adding one + +1. Click the chevron next to **Run**. +2. Under **Web / URL**, pick **Add URL**. +3. Name it, paste the URL, optionally hit **Test**, and **Save**. + +It opens immediately and appears in the dropdown from then on. Web tabs share the tab strip +with sessions, continue the same `Alt+1` to `Alt+9` numbering, and carry a globe icon so +they never read as a running agent. + +**Closing a tab is not deleting it.** The tab's `x` closes; the `x` on its **dropdown row** +deletes the saved dashboard. Each dropdown row also has a gear for editing the URL. + +Switching tabs does not reload a dashboard. Frames stay alive in the background, so one that +took a while to authenticate is still there when you come back. Past six live frames, the +least recently viewed is dropped to bound memory. + +## Why dashboards are proxied + +A plain cross-origin iframe fails three ways at once in the setup Codeman actually ships in: + +| Blocker | What happens | +| ------------------- | ----------------------------------------------------------------------------------------------- | +| **Mixed content** | Production is HTTPS, and browsers hard-block `http://` iframes on an HTTPS page. No override, and none at all on iOS Safari. | +| **Framing refusal** | Grafana, Portainer, Home Assistant and many others send `X-Frame-Options: DENY`. | +| **Codeman's CSP** | `default-src 'self'` blocks a cross-origin frame before it starts. | + +So by default the dashboard is served **through Codeman's own origin**: the browser loads a +path on Codeman, and Codeman relays to the dashboard, stripping the framing refusal, +rewriting redirects, cookies and root-absolute URLs, and relaying WebSockets so live panels +still update. + +A useful side effect: the dashboard is fetched **by the Codeman server**, so a tailnet-only +or localhost-only dashboard works from any device that can reach Codeman, including a phone +that is not on your tailnet. + +There is also a `direct` mode, a plain cross-origin iframe, which is cheaper but only works +for an HTTPS dashboard that permits framing. + +## The Test button, and what it does not test + +**Test** probes from the server and tells you which mode applies. It verifies +**server-to-upstream reachability and nothing else**. It does not exercise the browser +sandbox, cookies, CORS, CSP, or any reverse proxy in front of Codeman. + +A passing Test does not guarantee the embedded page renders. + +## The sandbox, and when to turn it off + +Because a proxied dashboard is served from Codeman's own address, the browser considers it +same-origin with Codeman. Unchecked, its JavaScript could read the Codeman page and call the +API that spawns agents. + +So the frame is sandboxed **without** same-origin access by default. The page runs in an +opaque origin: it cannot touch Codeman, and it gets no cookies or local storage of its own. + +Unchecking **Open sandboxed** grants a real origin. Do that only for a dashboard you fully +trust, and only when you need it, which in practice means one with its own login that stores +a session in a cookie. + +Either way, Codeman never forwards its own credentials upstream. The `Authorization` header +and the `codeman_session` cookie are stripped on the way out, so `CODEMAN_PASSWORD` cannot +leak into a dashboard. + +## Known incompatibility: cookie-authenticated reverse proxies + +If Codeman itself sits behind Cloudflare Access, Authelia, oauth2-proxy, or similar, a +**sandboxed** tab may render unstyled or broken while the Codeman page around it works fine. + +The reason: an opaque-origin frame's stylesheet, script, and API requests do not carry the +proxy's authentication cookie. The proxy redirects them to the login provider, and CORS or +CSP kills them there. + +Trusted mode keeps a real origin and the cookie, so it works. Test cannot catch this, because +it checks the server's reach, not the browser's. + +## Security notes + +The proxy authenticates on an in-memory capability embedded in the path, which is why it is +exempt from the cookie and Origin checks that every API route enforces. That exemption is +fenced to safe methods and non-API paths, and there is a test pinning it in place. + +Two failure modes that only appear inside a sandboxed frame, and that curl can never +reproduce, are handled: runtime-built root-absolute URLs escaping the injected base, and +same-host requests being CORS-checked with a null origin. Both present as the dashboard's own +"Failed to fetch" while the page itself renders fine. + +## Read next + +- [The Dashboard](The-Dashboard) - the tab strip these share. +- [Security](Security) - why the sandbox default is what it is. +- [`docs/web-tabs.md`](https://github.com/Ark0N/Codeman/blob/master/docs/web-tabs.md) - the full reference. diff --git a/docs/wiki/Working-With-Files.md b/docs/wiki/Working-With-Files.md new file mode 100644 index 00000000..532c155e --- /dev/null +++ b/docs/wiki/Working-With-Files.md @@ -0,0 +1,159 @@ +# Working With Files + +Reading, editing, attaching, and previewing files without leaving the dashboard. Useful on +a desktop; on a phone it is the difference between reviewing an agent's work and waiting +until you get home. + +## The File Viewer + +A panel that browses the active session's working directory. Its header button is on by +default; if it is missing, re-enable it in **App Settings → Header & Panels**. + +It renders what it can: + +| Kind | Behaviour | +| ------------------------ | ------------------------------------------------------------------------- | +| Text and code | Syntax-aware preview. Long files are truncated in plain preview. | +| Images | Inline. | +| Audio and video | Inline with a working scrub bar, because range requests are supported. | +| PDF and Office documents | Converted for preview when a converter is available. | +| Anything else | Download. | + +Caps: 10 MB for text preview, 50 MB for raw and download. Sensitive paths (`.env`, anything +matching credentials, `~/.ssh`, AWS credentials) are blocked from download, and SVG and HTML +are served as downloads rather than rendered, so they cannot execute in the page. + +Closing the preview pauses and unloads any playing media. A video that keeps playing after +you close the panel means you are on an old version. + +## Editing in place + +Text files can be edited and saved directly in the viewer. Click the pencil in the preview +header, edit, **Save**. + +The guardrails are worth knowing, because they are what makes editing safe rather than +convenient: + +- **Extension allowlist**, not a blocklist. Code, docs, config, and markup are editable. + Anything not on the list is not. +- **512 KB cap** on both read and write. +- **Edit mode never truncates.** The plain preview does truncate long files, and saving a + truncated buffer would silently delete the rest, so the editor loads the whole file or + refuses. +- **Optimistic concurrency.** The save carries a hash of what you started from. If the file + changed underneath you (likely, when an agent is working in the same repo), the save is + rejected rather than clobbering their work. +- **No file creation.** Writes go to a temporary file and are renamed over the original, and + the open never creates. Editing in place is structural, not a rule. +- **Line endings are preserved** server-side, so editing two lines of a CRLF file does not + produce a whole-file diff. +- **`.git/` is denied outright.** Hooks are executable code, and a corrupted index looks + unrecoverable to someone who wanted to fix a typo. +- **Non-UTF-8 content is refused**, verified by a round-trip comparison. + +## Attachments + +Attachments are live references to files **outside** the session's workspace: a spec on your +desktop, a PDF in Downloads, a design document elsewhere on the machine. + +Register one from the CLI: + +```bash +codeman attach /path/to/spec.pdf +``` + +An attachment card appears in the session, and the file can be previewed inline. The +attachment gets a stable id, and browser requests use that id rather than carrying absolute +paths around. + +Agents can register attachments too, by emitting a `codeman://attach?...` link in their +output. That path is **prompt-injectable by nature**, so it is force-confined to the +session's workspace: a hostile prompt cannot use it to pull arbitrary host files into the +event stream. The gate is an extension allowlist rather than a blocklist. + +Document conversion for previews is globally rate limited. Without that, ten large documents +detected at once would fork ten multi-minute converter processes. + +## Clicking a path + +File paths in a session are links. That works in two places: + +- **In the terminal**, on any absolute path an agent prints. +- **In the response viewer**, where paths are usually written as prose or in backticks. They + render as underlined monospace links. + +Clicking one opens it in the preview: images and PDFs render, video and audio play with a +working scrub bar, documents convert, text and Markdown show inline. Log-shaped files open in +the tail viewer instead, which follows a file that is still being written. + +Paths **outside** the session's workspace work too, which matters because that is where most +of an agent's output lands: a screenshot in `/tmp`, a capture in its own scratchpad, a file in +another checkout. Those are served through the attachment routes rather than the workspace +ones, so the same rules apply as to any other attachment: secret trees are blocked, the +extension allowlist decides what can be opened, and symlinks are resolved before either check. + +Outside the workspace the allowlist is images, video, audio, PDF, Office documents, and text +files, where "text" is the same list the viewer will let you edit: code, config, logs, csv, +markdown. The reasoning is that a session can already `cat` any of those, so the file suffix +was never what kept anything secret; the path guard is. Types outside the list (`.svg`, +`.bmp`) say so rather than failing silently, and `.html` previews as source rather than being +rendered, so nothing served this way can execute in the page. + +Text previews are capped at the first 500 lines, fetched as a partial read, so clicking a +one-gigabyte log does not try to paint one. + +Log-shaped files inside the workspace still open in the tail viewer, which follows a file as +it is written. Outside the workspace they open in the preview instead: the tail viewer runs +`tail -f`, and that is deliberately restricted to the workspace, `/var/log` and `~/logs`. + +Nothing is registered until you click. Opening a file this way does not add an attachment card. + +## The path picker + +For choosing a path rather than typing one. It appears in two places: + +- **Browse** in **Add Case → Link Existing**. +- The **📁 Path** key on the mobile keyboard bar. + +It browses one directory at a time and can show hidden entries on request. The picker +inserts the path into your prompt **without** pressing Enter, so nothing is submitted by +accident. Its sibling **⌫ All** key clears the unsent prompt, and never sends the agent's +`/clear` command. + +This is a separate file-serving surface from the viewer, with its own rules: it allowlists +your home directory, the cases directory, and anything in `CODEMAN_FILE_PICKER_ROOTS`, and +blocks sensitive trees. In multi-user mode a non-admin gets only their own user space as a +root, because per-user spaces live inside the home directory and a home-directory root would +expose everyone. + +## Images into a session + +Paste from the clipboard or drag and drop straight onto the terminal. The image is written +where the agent can read it and the reference is inserted into your prompt. On a phone, the +image key in the keyboard bar opens the camera or photo library. + +HEIC images from an iPhone are converted to JPEG on the way in. + +## Generated artifacts + +When an agent produces a file the UI can show (a chart, a diagram, a document), it can +surface as an artifact attachment rather than a path you have to go and find. + +## Gotchas + +- **The viewer follows the active session's workspace.** Switching tabs changes what you are + browsing. +- **A save can be rejected, and that is the feature.** It means the agent edited the file + while you were typing. Re-open, re-apply, save again. +- **Attachments live outside the workspace on purpose.** For files inside it, just use the + viewer. +- **`.env` files are readable in the viewer if the extension policy allows the preview, but + never downloadable.** Do not treat the viewer as a secrets boundary; treat the machine as + the boundary. + +## Read next + +- [The Dashboard](The-Dashboard) - where the panels live. +- [Input And Voice](Input-And-Voice) - other ways to get content into a session. +- [Security](Security) - how the file surfaces are confined. +- [`docs/file-viewer-edit-plan.md`](https://github.com/Ark0N/Codeman/blob/master/docs/file-viewer-edit-plan.md) - the edit-mode design. diff --git a/docs/wiki/_Footer.md b/docs/wiki/_Footer.md new file mode 100644 index 00000000..200025eb --- /dev/null +++ b/docs/wiki/_Footer.md @@ -0,0 +1,9 @@ +Documents Codeman **{{VERSION}}**. Something wrong or missing on this page? These pages are +generated from [`docs/wiki/`](https://github.com/Ark0N/Codeman/tree/master/docs/wiki) in +the main repository, so browser edits here are overwritten on the next sync. Send a pull +request against that directory instead, or open a +[Discussion](https://github.com/Ark0N/Codeman/discussions). + + diff --git a/docs/wiki/_Sidebar.md b/docs/wiki/_Sidebar.md new file mode 100644 index 00000000..af84a756 --- /dev/null +++ b/docs/wiki/_Sidebar.md @@ -0,0 +1,53 @@ +### [Codeman Wiki](Home) + +[README](https://github.com/Ark0N/Codeman) + +**Getting started** + +- [Installation](Installation) +- [Quick Start](Quick-Start) +- [Core Concepts](Core-Concepts) + +**Using it** + +- [The Dashboard](The-Dashboard) +- [Agent CLIs](Agent-CLIs) +- [Working With Files](Working-With-Files) +- [Input And Voice](Input-And-Voice) +- [Mobile Guide](Mobile-Guide) +- [Keyboard Shortcuts](Keyboard-Shortcuts) +- [Settings Reference](Settings-Reference) + +**Keeping agents running** + +- [Unattended Runs](Keeping-Agents-Running) +- [Notifications & Approvals](Notifications-And-Approvals) +- [Cron Jobs](Cron-Jobs) +- [Autonomous Loops](Autonomous-Loops) +- [Watching Agents Work](Watching-Agents-Work) + +**Where it runs** + +- [Docker Cases](Docker-Cases) +- [Remote SSH Sessions](Remote-SSH-Sessions) +- [Web Tabs](Web-Tabs) +- [Multi-User Mode](Multi-User-Mode) + +**Access & security** + +- [Remote Access](Remote-Access) +- [Security](Security) + +**Automation** + +- [Driving It From An Agent](Driving-Codeman-From-An-Agent) +- [HTTP API](HTTP-API) +- [Hooks & Integrations](Hooks-And-Integrations) + +**Operating it** + +- [Running As A Service](Running-As-A-Service) +- [Troubleshooting](Troubleshooting) +- [FAQ](FAQ) +- [Contributing](Contributing) +- [Versioning](Versioning)