@cabane/companion 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -29
- package/dist/cli.js +662 -1823
- package/dist/pairing-config.js +29 -53
- package/dist/runtime.js +490 -1585
- package/dist/static/index.html +2 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Connect a **coding agent on your machine** to your [Cabane](https://cabane.ai) workspaces as a **responder** — it replies to messages inside Cabane while running as a full local AI client, so you can drive work against your own codebase, files, and MCP servers without putting any of it in Cabane. The model and tools run on your box; only the reply crosses back.
|
|
4
4
|
|
|
5
|
-
The Companion runs **[Claude Code](https://www.npmjs.com/package/@anthropic-ai/claude-code)**
|
|
5
|
+
The Companion runs your agents through a **harness** on your machine — **[Claude Code](https://www.npmjs.com/package/@anthropic-ai/claude-code)**, **[Codex](https://developers.openai.com/codex/cli)**, or **[opencode](https://opencode.ai)** — and you expose the ones you have. Install and sign in to a harness yourself; the Companion drives it. See [Harnesses](#harnesses).
|
|
6
6
|
|
|
7
7
|
This is **v0**. It's a small CLI — install it from npm with one command, pair the machine once, and run. It talks to Cabane only over Cabane's public API.
|
|
8
8
|
|
|
@@ -10,15 +10,31 @@ This is **v0**. It's a small CLI — install it from npm with one command, pair
|
|
|
10
10
|
|
|
11
11
|
## The model in one paragraph
|
|
12
12
|
|
|
13
|
-
The Companion runs on a **device you
|
|
13
|
+
The Companion runs on a **device you pair**, running **agents you assign to it**. Pair the device once with a short code, then `start` it. From then on it **pulls its assignments from Cabane at runtime** — every agent you've pointed at this device, across all your workspaces — and runs each one locally. Which agents to run, and how they're configured (mode, MCP servers, model), is owned by Cabane and delivered per turn. The machine only holds the bits that _must_ be local: the device token, each agent's working directory, and your secrets.
|
|
14
14
|
|
|
15
|
-
> **No account password or full-account token ever touches your machine.** The pairing
|
|
15
|
+
> **No account password or full-account token ever touches your machine.** The short-code pairing flow delivers a **device token** (`cabdev_…`) directly to the waiting CLI — good only for pulling this device's assignments and reporting liveness. Each agent the Companion runs gets its own **workspace-bound agent token**, delivered once when the agent is assigned and scoped to that one agent in that one workspace. Deactivating and removing a device — and assigning agents to it — all live in the Cabane app; the CLI only pairs the machine and runs the agents locally.
|
|
16
16
|
|
|
17
17
|
## Prerequisites
|
|
18
18
|
|
|
19
|
-
You need **Node 22+** (the Companion
|
|
19
|
+
You need **Node 22+** (the Companion itself runs on Node — check with `node --version`) and **at least one harness installed and signed in**. The Companion installs no harness and drives no login for you — bring your own, and set it up **before** starting the Companion. It refuses to `start` if no harness is exposed, because no turn could be routed to it.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Set up whichever you already use — one is enough, and a machine can expose several. [Harnesses](#harnesses) has the full per-harness wiring; the short version:
|
|
22
|
+
|
|
23
|
+
**Claude Code** — install it globally _and_ log in:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm i -g @anthropic-ai/claude-code
|
|
27
|
+
claude # complete the login, then quit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Both halves are load-bearing, for different reasons:
|
|
31
|
+
|
|
32
|
+
- **The install** puts `claude` on your PATH (`which claude` should resolve). That PATH probe is how the Companion exposes Claude Code — it needs no configuration, but without `claude` the device won't offer it.
|
|
33
|
+
- **The login** writes the credential the turn actually uses. The Companion does _not_ run your global `claude` to answer a turn — inference goes through the bundled Claude Agent SDK — but the SDK reads the credential your Claude Code login left on disk. So a machine with `claude` installed and never logged in looks fine (the Connector reads **Online**) and then fails every turn with **"Sign-in needed"**.
|
|
34
|
+
|
|
35
|
+
**Codex** — install the Codex CLI and log in (`codex login`, or set `CODEX_API_KEY` in Codex's own environment — Cabane never sees the key). Unlike Claude Code, which is auto-detected on the PATH, Codex is an explicit opt-in: a one-line `codex` block in `~/.cabane/config.json`, or one click in the [dashboard](#the-dashboard). → [full setup](https://app.cabane.ai/docs/reference/connectors/chatgpt)
|
|
36
|
+
|
|
37
|
+
**opencode** — install it, authenticate a provider through its own flow, and start its server (`opencode serve`); the Companion addresses it at the URL you give it. → [full setup](https://app.cabane.ai/docs/reference/connectors/opencode)
|
|
22
38
|
|
|
23
39
|
You also need **a Cabane account.** A device is registered under your user account; the agents it runs can live in any workspace you have access to.
|
|
24
40
|
|
|
@@ -32,12 +48,20 @@ That puts `cabane-companion` on your PATH. Confirm it with `cabane-companion --v
|
|
|
32
48
|
|
|
33
49
|
## First run
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
On your machine, start the short-code pairing flow:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
cabane-companion pair
|
|
55
|
+
# Enter this code at https://cabane.ai/settings/companions
|
|
56
|
+
#
|
|
57
|
+
# ABCD-EFGH
|
|
58
|
+
#
|
|
59
|
+
# Waiting for you to confirm it in cabane…
|
|
60
|
+
```
|
|
36
61
|
|
|
37
|
-
|
|
62
|
+
Open the printed URL, enter the code in **Settings → Connectors**, give the device a label, and confirm. The waiting command receives its device token directly and finishes:
|
|
38
63
|
|
|
39
64
|
```sh
|
|
40
|
-
cabane-companion pair # paste the pairing string at the hidden prompt
|
|
41
65
|
# ✓ Paired this device "MacBook" with https://app.cabane.ai.
|
|
42
66
|
# Run `cabane-companion start` — it will pull the agents assigned to this device and run them.
|
|
43
67
|
|
|
@@ -50,7 +74,7 @@ cabane-companion start
|
|
|
50
74
|
# [14:32:14] 4f6a93d2 — replied (8.4s)
|
|
51
75
|
```
|
|
52
76
|
|
|
53
|
-
|
|
77
|
+
A headless machine needs no browser of its own: run `cabane-companion pair` on it, then open **Settings → Connectors** in any browser you're signed in to and enter the short code it printed. The device token is delivered straight to the waiting CLI over the pairing channel — nothing is ever copied or pasted.
|
|
54
78
|
|
|
55
79
|
A freshly paired device runs nothing yet — it has no agents. In **Workspace settings → Agents**, set an agent's **executor** to this device. The running Companion picks the assignment up within a minute (no restart), and the next time you `@mention` that agent, the reply comes from your own machine. Assign agents in as many of your workspaces as you like — one running Companion serves them all.
|
|
56
80
|
|
|
@@ -58,7 +82,7 @@ A freshly paired device runs nothing yet — it has no agents. In **Workspace se
|
|
|
58
82
|
|
|
59
83
|
| Command | What it does |
|
|
60
84
|
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
-
| `pair [
|
|
85
|
+
| `pair [--server <url>]` | Pair this device using a short code confirmed in Settings → Connectors. The device token is delivered directly to the waiting CLI, then the identity is written to `~/.cabane/config.json` (mode 600). Re-pairing replaces the device token and **keeps** your local `agents` overrides + dashboard prefs. |
|
|
62
86
|
| `start [--open] [--no-open] [--daemon] [--port <p>]` | Pull this device's assigned agents from Cabane and run them, opening one SSE subscription per workspace and serving the localhost dashboard (default `http://127.0.0.1:7474`; URL printed on start). Re-pulls assignments every ~60s, so adding/removing an agent in Cabane lands without a restart. Does **not** auto-open a browser by default — pass `--open` (or set `autoOpen: true` in config); `--no-open` / `BRIDGE_NO_OPEN=1` force it off. Foreground by default; `--daemon` runs it detached (see below). |
|
|
63
87
|
| `stop` | Stops a running Companion (foreground or daemon) cleanly. Idempotent — "nothing running" is a success. |
|
|
64
88
|
| `status` | Prints local state — the paired device, the secret names it declares, the log path, and (when running) whether the Companion is background or foreground, its pid, uptime, and dashboard URL. The live list of assigned agents lives in the dashboard while the Companion runs. |
|
|
@@ -87,7 +111,7 @@ The prompt returns immediately and you can close the terminal — replies keep l
|
|
|
87
111
|
|
|
88
112
|
## The dashboard
|
|
89
113
|
|
|
90
|
-
`cabane-companion start` serves a small localhost dashboard (default `http://127.0.0.1:7474`) so the Companion is usable without tailing logs: live per-workspace connection status, the agents currently assigned to this device, a recent-dispatches feed, a log tail, and a few local-only controls (Stop / Restart / Reload-config). It binds to `127.0.0.1` only — same threat model as any localhost dev tool, no auth. It does **not** auto-open a browser by default — the URL is printed on start; pass `--open` (or set `autoOpen: true` in config) to open it automatically, and `--no-open` / `BRIDGE_NO_OPEN=1` force it off. It keeps working when Cabane itself is unreachable, showing a "⚠ Cabane unreachable" banner.
|
|
114
|
+
`cabane-companion start` serves a small localhost dashboard (default `http://127.0.0.1:7474`) so the Companion is usable without tailing logs: live per-workspace connection status, the agents currently assigned to this device, the [harnesses](#harnesses) it can see (with a one-click enable for one that's installed but not yet exposed), a recent-dispatches feed, a log tail, and a few local-only controls (Stop / Restart / Reload-config). It binds to `127.0.0.1` only — same threat model as any localhost dev tool, no auth. It does **not** auto-open a browser by default — the URL is printed on start; pass `--open` (or set `autoOpen: true` in config) to open it automatically, and `--no-open` / `BRIDGE_NO_OPEN=1` force it off. It keeps working when Cabane itself is unreachable, showing a "⚠ Cabane unreachable" banner.
|
|
91
115
|
|
|
92
116
|
## Configuring an agent: what's in Cabane vs. on this machine
|
|
93
117
|
|
|
@@ -155,34 +179,44 @@ When an agent's MCP servers in Cabane need a credential, that credential is writ
|
|
|
155
179
|
|
|
156
180
|
A `${VAR}` the store doesn't declare fails the turn loudly — it is **never** read from your shell environment, so a config can't smuggle out an ambient credential. The Companion reports the declared **names** (never values) to Cabane on its heartbeat, so the settings UI can warn "this agent needs `${GITHUB_TOKEN}`, this device doesn't expose it" before a turn ever runs.
|
|
157
181
|
|
|
158
|
-
##
|
|
182
|
+
## Harnesses
|
|
183
|
+
|
|
184
|
+
A **harness** is the local tool that runs a model — **[Claude Code](https://www.npmjs.com/package/@anthropic-ai/claude-code)**, **[Codex](https://developers.openai.com/codex/cli)**, or **[opencode](https://opencode.ai)** — together with the credential behind it. A harness connected to Cabane on this device is a **Connector**, and one device runs as many Connectors as it has harnesses. None is privileged: a device advertises exactly the harnesses it actually has.
|
|
185
|
+
|
|
186
|
+
Which harness a turn uses follows the agent's **model**, set in Cabane (Workspace settings → Agents): a Claude model runs on Claude Code, an OpenAI model on Codex, an opencode model on opencode. A device only offers a harness's models — and only accepts its turns — once that harness is exposed, so set it up first, then pick a matching model for the agent. A turn is never routed to a harness the machine can't run.
|
|
187
|
+
|
|
188
|
+
What "exposed" means differs per harness, because each is discoverable in a different way. Bring your own install and your own login in all three cases — the Companion never installs a binary and never drives a login:
|
|
189
|
+
|
|
190
|
+
| Harness | How the device exposes it | Setup |
|
|
191
|
+
| --------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
192
|
+
| **Claude Code** | `claude` is on your PATH — auto-detected, no configuration. | [Claude](https://app.cabane.ai/docs/reference/connectors/claude) |
|
|
193
|
+
| **Codex** | A `codex` block in `~/.cabane/config.json`. Unlike Claude Code, an explicit opt-in. | [ChatGPT (Codex)](https://app.cabane.ai/docs/reference/connectors/chatgpt) |
|
|
194
|
+
| **opencode** | An `opencode.serverUrl` in `~/.cabane/config.json` pointing at a reachable `opencode serve`. | [opencode](https://app.cabane.ai/docs/reference/connectors/opencode) |
|
|
159
195
|
|
|
160
|
-
Each
|
|
196
|
+
Each harness has a full setup page in the docs — install, login, enable, verify — linked above; this section is just the shape of it and the config the Companion reads.
|
|
161
197
|
|
|
162
|
-
|
|
198
|
+
**You usually don't hand-edit the config.** `cabane-companion start` serves a [localhost dashboard](#the-dashboard) listing every harness it can see, including ones installed but not yet exposed: a `codex` binary on your PATH shows up as detected-but-not-enabled with a **Use Codex** button, and opencode gets an **Add opencode** field for the `serve` URL (health-checked before it's written, so an unreachable server is caught in place). One click writes the block and advertises the Connector straight away — no restart. Editing `~/.cabane/config.json` yourself is the fallback, and what a headless device wants:
|
|
163
199
|
|
|
164
|
-
|
|
200
|
+
```jsonc
|
|
201
|
+
{
|
|
202
|
+
// …device identity, managed by `pair`; don't hand-edit those fields…
|
|
203
|
+
"codex": { "enabled": true },
|
|
204
|
+
"opencode": { "serverUrl": "http://127.0.0.1:4096" },
|
|
205
|
+
}
|
|
206
|
+
```
|
|
165
207
|
|
|
166
|
-
|
|
167
|
-
2. **Start its server:** `opencode serve --port 4096` (any free port). Run one `opencode serve` per Companion process.
|
|
168
|
-
3. **Point the Companion at it** — add a top-level `opencode` block to `~/.cabane/config.json`:
|
|
169
|
-
```jsonc
|
|
170
|
-
{
|
|
171
|
-
// …device identity, managed by `pair`; don't hand-edit those fields…
|
|
172
|
-
"opencode": { "serverUrl": "http://127.0.0.1:4096" },
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
4. **Restart the Companion.** Its next heartbeat advertises the `opencode` runtime, and an agent assigned to this device can now be set to an opencode model in Cabane. Without this block the device stays Claude-Code-only, exactly as before.
|
|
208
|
+
Either block is optional and independent — set only the harnesses you have. Codex is a local CLI the bundled `@openai/codex-sdk` spawns per turn, so it takes a flag and no URL; opencode is a long-lived server addressed by URL, so run one `opencode serve` per Companion process. A hand-edit needs a restart (`cabane-companion stop && cabane-companion start`); the dashboard's button doesn't.
|
|
176
209
|
|
|
177
|
-
Everything else about a Companion-run agent is
|
|
210
|
+
Everything else about a Companion-run agent is the same whichever harness runs it — same assignment, same working directory, same secrets, same dashboard. A harness is a way to execute a turn, not a different way to run the Companion.
|
|
178
211
|
|
|
179
212
|
## Files on disk
|
|
180
213
|
|
|
181
214
|
```
|
|
182
215
|
~/.cabane/
|
|
183
216
|
├── config.json # device identity: baseUrl + device token (cabdev_…) + device id/label,
|
|
184
|
-
│ # plus optional per-agent `agents` overrides (cwd / prepareHook),
|
|
185
|
-
│ # optional `opencode`
|
|
217
|
+
│ # plus optional per-agent `agents` overrides (cwd / prepareHook), the
|
|
218
|
+
│ # optional harness blocks (`codex` enabled, `opencode` serverUrl), and
|
|
219
|
+
│ # dashboard prefs. mode 600.
|
|
186
220
|
│ # No account password, no full-account token.
|
|
187
221
|
├── credentials.json # agentId → per-agent workspace-bound token, cached on first assignment
|
|
188
222
|
│ # pull (delivered once, then never re-sent). mode 600.
|
|
@@ -198,7 +232,7 @@ No SQLite, no embedded DB. Cabane is the source of truth for which agents to run
|
|
|
198
232
|
|
|
199
233
|
## Debugging a turn: transcripts
|
|
200
234
|
|
|
201
|
-
The dashboard shows the agent's final reply, but not _how_ it got there. When a turn misbehaves — an agent that "couldn't read the file", a tool that errored, MCP tools that didn't load, a missing secret — the full picture is on disk. Every dispatch writes the complete
|
|
235
|
+
The dashboard shows the agent's final reply, but not _how_ it got there. When a turn misbehaves — an agent that "couldn't read the file", a tool that errored, MCP tools that didn't load, a missing secret — the full picture is on disk. Every dispatch — on whichever harness ran it — writes the complete turn stream (the system init with its tool list, every tool call **and its result**, the assistant text, the outcome) to a JSONL file under `~/.cabane/transcripts/`. On a failed turn, the Companion log also prints the exact path.
|
|
202
236
|
|
|
203
237
|
Read one back in a readable form:
|
|
204
238
|
|