@primusclaw/connector 0.1.5 → 0.1.6
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 +106 -25
- package/dist/cli.js +1684 -356
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,10 +41,10 @@ If npm answers `EACCES`, point its global prefix at a directory you own (`npm co
|
|
|
41
41
|
```sh
|
|
42
42
|
claw-connector init # writes agents.toml and prints where: ~/.config/claw-connector/agents.toml unless XDG_CONFIG_HOME or CLAW_CONNECTOR_HOME is set
|
|
43
43
|
claw-connector status # after you edit it: prints a "problem:" line if the file is wrong
|
|
44
|
-
claw-connector doctor # starts each agent's adapter, opens a session (no prompt) and checks the modes it offers
|
|
44
|
+
claw-connector doctor # checks the machine (step 5), then starts each agent's adapter, opens a session (no prompt) and checks the modes it offers
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`init` runs the
|
|
47
|
+
`init` runs the adapter check of `doctor` at the end. `doctor` says `ok` for an agent whose phone sessions will open, and otherwise a
|
|
48
48
|
`problem:` line with the fix, for example an agent whose sessions would be refused with `mode_unavailable`.
|
|
49
49
|
|
|
50
50
|
By default every agent asks before it writes a file or runs a command, and your phone answers each question. To let phone sessions run
|
|
@@ -88,13 +88,32 @@ Only `claw-connector stop` (or stopping the service) stops it for good: a `kill`
|
|
|
88
88
|
with exit 75, and it is started again.
|
|
89
89
|
It uses `systemd --user` on Linux and launchd on macOS. On Linux, run `loginctl enable-linger $USER` once (`service install` says so
|
|
90
90
|
when it is needed) so it also runs while you are logged out. In a container without systemd, `service install` starts a small
|
|
91
|
-
supervisor
|
|
92
|
-
when the supervisor is up)
|
|
91
|
+
supervisor, writes `keepalive.sh` and adds it to your crontab when a cron daemon runs; when none does it says so, and the container's
|
|
92
|
+
startup command must run that script (it does nothing when the supervisor is up) for a restarted container to come back. Keys the
|
|
93
|
+
agents read from your shell (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`...) are kept for the service in a 0600 file, never in the unit.
|
|
94
|
+
See [Keeping it running](#keeping-it-running).
|
|
93
95
|
|
|
94
96
|
To try it in the foreground instead, `claw-connector run` prints `connecting claude to https://claw.example.com/claw-api as
|
|
95
97
|
"<machine name>"` and stays up until Ctrl-C, which stops it and every agent it started.
|
|
96
98
|
|
|
97
|
-
**5.
|
|
99
|
+
**5. Check the machine**
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
claw-connector doctor
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`doctor` checks what makes a first phone session fail far from the machine that caused it, and prints one fix per problem:
|
|
106
|
+
Node.js 22 or newer; each agent's program on `PATH` (and on the `PATH` the installed service starts it with, which is not your
|
|
107
|
+
shell's); whether Claude Code and Codex are signed in (`claude auth status`, `codex login status`, or the model provider's key
|
|
108
|
+
variable in `~/.codex/config.toml`); the `roots` writable; `HTTPS_PROXY` and `https_proxy` not set to different values; a service
|
|
109
|
+
installed (and, for `keepalive.sh`, something calling it); and Claw answering, including whether Node.js trusts its certificate
|
|
110
|
+
(Node does not read the system's CA store by default: behind a company CA set `NODE_USE_SYSTEM_CA=1` or `NODE_EXTRA_CA_CERTS`). Then it
|
|
111
|
+
runs the adapter check of step 2. It exits 1 when something will stop sessions (`--json`: `ok` is the adapters' verdict,
|
|
112
|
+
`machine_ok` and `environment` the machine's). The
|
|
113
|
+
running connector repeats the machine checks with its own environment (in a child process, so a stalled disk cannot stall
|
|
114
|
+
it) every 15 minutes, and at once when `doctor` runs (it signals the connector), and Clawy's `/agents` card shows what it found under the machine ("Codex is not signed in: run `codex login` on that machine").
|
|
115
|
+
|
|
116
|
+
**6. See it in Clawy**
|
|
98
117
|
|
|
99
118
|
In Clawy (or any chat client built on Claw), send `/agents`. The agents of this machine are listed, online while `run` is up.
|
|
100
119
|
A session started there runs on this machine, inside one of your `roots`, and each permission request comes back as a card in the chat.
|
|
@@ -122,7 +141,9 @@ id = "codex"
|
|
|
122
141
|
preset = "codex"
|
|
123
142
|
roots = ["~/work/repo-a", "~/work/repo-b"]
|
|
124
143
|
[agents.env] # passed to the adapter; the model settings live here, never in Claw
|
|
125
|
-
OPENAI_API_KEY = "..." # or leave your own environment / CODEX_HOME to supply it
|
|
144
|
+
OPENAI_API_KEY = "..." # or leave your own environment / CODEX_HOME to supply it; with only a key, the
|
|
145
|
+
# connector signs Codex in with it (Codex keeps it in its auth.json, like
|
|
146
|
+
# "codex login --with-api-key")
|
|
126
147
|
|
|
127
148
|
[[agents]]
|
|
128
149
|
id = "mine"
|
|
@@ -155,7 +176,10 @@ connector's state directory, and the install is tried again in the background te
|
|
|
155
176
|
`claude-attach` does not start new conversations: it takes over Claude Code conversations that already exist on this
|
|
156
177
|
machine (see [Taking over an existing conversation](#taking-over-an-existing-conversation)).
|
|
157
178
|
|
|
158
|
-
An agent with no `roots` is never offered. A configuration problem is reported at start-up, not skipped.
|
|
179
|
+
An agent with no `roots` is never offered. A configuration problem is reported at start-up, not skipped. A root that does
|
|
180
|
+
not exist, or whose disk does not answer (a network mount that is down or not mounted yet), is not a configuration problem:
|
|
181
|
+
`run` warns about it and keeps going, and that root's sessions are refused (`cwd_not_found`, `root_unavailable`) until it is
|
|
182
|
+
back. Reading `agents.toml` never touches a root.
|
|
159
183
|
|
|
160
184
|
## Running without per-step approval
|
|
161
185
|
|
|
@@ -222,7 +246,24 @@ All of these run on your machine and fail closed.
|
|
|
222
246
|
|
|
223
247
|
- **Directories.** A session's working directory is resolved (symlinks included) and must be inside one of the agent's
|
|
224
248
|
`roots`; otherwise no process is started and the session is closed with `cwd_outside_roots`. The agent runs in the
|
|
225
|
-
resolved path.
|
|
249
|
+
resolved path. A folder can be given the way people type it: `~` and `~/x` are the agent's home directory (its
|
|
250
|
+
`[agents.env]` `HOME`, else the connector's), and a relative name (`repo`, `repo/sub`) is looked up under each root and
|
|
251
|
+
taken when exactly one has it (`cwd_ambiguous` when several do); a root's own last segment names that root. A relative
|
|
252
|
+
name never climbs out (`..`). Every look at the disk on the way to opening a session is asynchronous with a 10-second
|
|
253
|
+
limit, so a root on a stalled network mount refuses its own sessions (`root_unavailable`), never those under a healthy
|
|
254
|
+
root, and never freezes the connector or its heartbeat. A restored session whose root does not answer is kept: its
|
|
255
|
+
commands are rejected `root_unavailable` until it does, and `close` still closes it.
|
|
256
|
+
- **Signed in before ready.** A new session of an agent that reports its sign-in (Claude Code does) and reports none is
|
|
257
|
+
refused at once with `agent_auth_required`, instead of opening and failing at its first message; a session that
|
|
258
|
+
already exists is kept and its prompts say why. A prompt refused for the same reason later
|
|
259
|
+
(an expired login) ends its turn with `error: "agent_auth_required"` and the fix, and the next prompt starts the agent
|
|
260
|
+
afresh, so a sign-in done on the machine meanwhile is picked up. An adapter that needs a sign-in and offers an
|
|
261
|
+
`api-key` method (Codex) is told to `authenticate` with it when its environment has `OPENAI_API_KEY` or `CODEX_API_KEY`;
|
|
262
|
+
the key itself is never sent, the adapter reads its own environment.
|
|
263
|
+
- **Why, in one sentence.** Every refusal (`session_closed`, `command_rejected`, and a `turn_end` that failed for a known
|
|
264
|
+
reason) carries its stable `reason` code and a `hint`: one English sentence of what to do about it (sign in with which
|
|
265
|
+
command, which folder to pick, `claw-connector doctor`), with no path and nothing secret, for a client to show beside
|
|
266
|
+
its own translation of the code.
|
|
226
267
|
- **Ask mode, read back.** After the adapter creates a session, the connector sets the agent's ask mode and reads the
|
|
227
268
|
mode back from the adapter. Your own agent settings may start an adapter in a mode that never asks (bypass, full
|
|
228
269
|
access); an adapter that does not confirm the ask mode, or that warns it will not invoke its permission callback, is
|
|
@@ -242,6 +283,9 @@ All of these run on your machine and fail closed.
|
|
|
242
283
|
which cannot write. `approval = "never"` selects `agent` and starts `agent --force acp` (`--force` is a root flag of the
|
|
243
284
|
Cursor CLI, "force allow commands unless explicitly denied"; it is accepted before or after `acp`). Cursor
|
|
244
285
|
has no `session/resume`; the connector uses `session/load` and marks the replayed history `replay: true`.
|
|
286
|
+
A Cursor agent that asks therefore answers and plans but changes no file: its capabilities say `edits: false` with an
|
|
287
|
+
`edits_hint`, `read_only_modes` lists `ask` and `plan` (each such mode in `session_started` carries `read_only: true`),
|
|
288
|
+
`run` prints a note, and the sample `agents.toml` says so above the Cursor block.
|
|
245
289
|
- **Permissions.** Each permission request becomes a card with the options **the adapter offered**, passed through with
|
|
246
290
|
their own ids and kinds (`allow_once`, `allow_always`, `reject_once`). An answer must be one of those ids; any other is refused
|
|
247
291
|
and the request stays open. An unanswered request is answered `cancelled` after 10 minutes, and a cancel,
|
|
@@ -371,6 +415,7 @@ session stops the agent process; it never deletes the conversation.
|
|
|
371
415
|
| `run [--supervised]` | Connect and serve. Exit code 78 means starting it again will not help: the connector was revoked, Claw or the identity service refused the sign-in (at start, or for two minutes in a row while running), or there is no usable `agents.toml` or sign-in; the reason is kept in `needs-login.json`, and until `login` removes it `run` exits 78 at once without contacting Claw. `--supervised` marks a run the service started. Any other non-zero exit is worth a restart. A unit written by hand for 0.1.2 and older (`RestartPreventExitStatus=3`): replace it with `claw-connector service install`, which writes the same file. |
|
|
372
416
|
| `service install\|uninstall\|status [--supervisor systemd\|launchd\|script] [--env NAME ...]` | Keep `run` up (see [Keeping it running](#keeping-it-running)). `install` stops a `run` started by hand so the service takes over; `status` also lists the last five disconnects and why. |
|
|
373
417
|
| `status [--json]` | Running or not, paused, signed in, agents, adapter processes, the last disconnect and its reason (`last down:`), a `problem:` line when a new sign-in is needed, and a `warning:` line for each agent whose sessions will be refused because Claude's own settings default to `bypassPermissions` (see [Running without per-step approval](#running-without-per-step-approval)). |
|
|
418
|
+
| `doctor [--agent ID] [--json]` | Check this machine and print one fix per problem (see step 5 of the quick start), then each agent's adapter (step 2); exit 1 when something stops sessions. The report is kept in `doctor.json`; a running connector is signalled to check again and sends what it finds to Claw (each agent's `capabilities.health`). |
|
|
374
419
|
| `pause` / `resume` | Stop every agent within a second and stay offline, or undo that. |
|
|
375
420
|
| `stop` | Stop the running connector and every agent it started. |
|
|
376
421
|
| `logout` | Stop, revoke this machine's connector at Claw (with a key, `DELETE /v1/remote-agents/connectors/:cid`; with a device sign-in, the connector-side `POST .../connectors/:cid/revoke`), ask the identity service to revoke the sign-in, and delete it from this machine. The next sign-in registers a new connector. When Claw cannot be reached or refuses (a Claw from before the connector-side route refuses a device sign-in), the sign-in is still deleted, but `logout` says the connector was not revoked, prints the `curl` that revokes it with a Claw API key of yours, keeps its id (so a later sign-in reuses it), and exits 1. |
|
|
@@ -383,12 +428,12 @@ the connector sees it within a heartbeat, stops every agent and exits without re
|
|
|
383
428
|
|
|
384
429
|
## Files
|
|
385
430
|
|
|
386
|
-
`~/.config/claw-connector/` (or `$XDG_CONFIG_HOME/claw-connector
|
|
387
|
-
refused). `~/.local/state/claw-connector/` (or `$XDG_STATE_HOME/claw-connector/`): `connector.json` (the connector's identity and the
|
|
431
|
+
`~/.config/claw-connector/` (or `$XDG_CONFIG_HOME/claw-connector/`, or `$CLAW_CONNECTOR_CONFIG_DIR`): `agents.toml`, `credentials.json`
|
|
432
|
+
(0600; a file other users can read is refused), and `service-env.json` (0600, the agents' sign-in variables `service install` kept). `~/.local/state/claw-connector/` (or `$XDG_STATE_HOME/claw-connector/`): `connector.json` (the connector's identity and the
|
|
388
433
|
commands handled), `sessions.json`, `outbox/` (events not yet acknowledged by Claw), `audit.jsonl` (spawn, directory
|
|
389
434
|
decisions, every command received, every permission request and answer, every automatic approval, mode changes),
|
|
390
|
-
`logs/`, `children.json`, `connector.pid`, `disconnects.jsonl`, `needs-login.json` (only after a run that needs a new sign-in),
|
|
391
|
-
the script supervisor `supervisor.pid`, `keepalive.sh` and `run.log`. `CLAW_CONNECTOR_HOME=/some/dir` keeps all of it under one directory.
|
|
435
|
+
`logs/`, `children.json`, `connector.pid`, `disconnects.jsonl`, `needs-login.json` (only after a run that needs a new sign-in), `doctor.json`
|
|
436
|
+
(the last `doctor` report), and with the script supervisor `supervisor.pid`, `keepalive.sh`, `keepalive-last` (when it last ran) and `run.log`. `CLAW_CONNECTOR_HOME=/some/dir` keeps all of it under one directory.
|
|
392
437
|
|
|
393
438
|
## Keeping it running
|
|
394
439
|
|
|
@@ -401,32 +446,68 @@ the script supervisor `supervisor.pid`, `keepalive.sh` and `run.log`. `CLAW_CONN
|
|
|
401
446
|
Log: `run.log` in the state directory.
|
|
402
447
|
- **script** (a container without systemd; an `systemctl` that answers "Running in chroot" counts as none): `claw-connector supervise`
|
|
403
448
|
runs in the background and restarts the connector 5 seconds after it dies; `keepalive.sh` in the state directory starts that
|
|
404
|
-
supervisor unless it is alive (recorded pid and start time) or a sign-in is needed.
|
|
405
|
-
|
|
449
|
+
supervisor unless it is alive (recorded pid and start time) or a sign-in is needed. Where a cron daemon runs, `install` adds an
|
|
450
|
+
`@reboot` and a 5-minute line that call `keepalive.sh` to your crontab (once; `uninstall` removes them). Where none runs (most
|
|
451
|
+
containers), `install` says so: nothing brings the connector back after a restart until the container's startup command (or
|
|
452
|
+
`~/.bashrc`) runs `sh keepalive.sh`. Log: `run.log`.
|
|
453
|
+
|
|
454
|
+
**A container whose home directory does not outlive it** (a dev pod): when `agents.toml` and the sign-in sit on the container's own
|
|
455
|
+
file system (overlay, tmpfs), `init` and `service install` say so. Keep them on storage that survives the container with
|
|
456
|
+
`CLAW_CONNECTOR_CONFIG_DIR=/mnt/<your persistent volume>/claw-connector` (only the configuration and sign-in move; pid files, the outbox
|
|
457
|
+
and logs stay on the local disk), set in your shell profile and in the container's startup command, which then runs
|
|
458
|
+
`claw-connector service ensure` (after installing the package again, if it lived on the container's file system too).
|
|
406
459
|
|
|
407
460
|
A successful `login` starts an installed service again, so after a revocation or a refused key signing in is the whole fix.
|
|
408
461
|
`keepalive.sh` and the script supervisor start nothing while only a person can help (no `agents.toml`, not signed in,
|
|
409
462
|
`needs-login.json`); under launchd every exit 78 is turned into a clean exit, which launchd does not restart. The service
|
|
410
463
|
installed for a state directory is recorded in its `service.json`.
|
|
411
464
|
|
|
412
|
-
The unit carries `PATH` (so the agents are found), `CLAW_CONNECTOR_HOME`, `
|
|
413
|
-
`CODEX_HOME`, the CA and proxy variables
|
|
414
|
-
|
|
415
|
-
|
|
465
|
+
The unit carries `PATH` (so the agents are found), `CLAW_CONNECTOR_HOME`, `CLAW_CONNECTOR_CONFIG_DIR`, `XDG_CONFIG_HOME`, `XDG_STATE_HOME`,
|
|
466
|
+
`CLAUDE_CONFIG_DIR`, `CODEX_HOME`, the CA and proxy variables (with `NODE_USE_SYSTEM_CA` and `NODE_USE_ENV_PROXY`), `LANG`, `IS_SANDBOX`, the npm registry and proxy settings, and every
|
|
467
|
+
plain setting of the agents (`ANTHROPIC_*`, `CLAUDE_CODE_*`, `OPENAI_*`, `CODEX_*`, `CURSOR_*` that is not named like a credential:
|
|
468
|
+
`ANTHROPIC_BASE_URL`, `ANTHROPIC_MODEL`, `CLAUDE_CODE_USE_BEDROCK`...) from the shell that ran `install` (a proxy URL with a password in
|
|
469
|
+
it only when named with `--env`, e.g. `--env HTTPS_PROXY`); `--env NAME` adds another.
|
|
470
|
+
|
|
471
|
+
The agents' own sign-in variables are never written into a unit. When the shell that runs `install` has `ANTHROPIC_API_KEY`,
|
|
472
|
+
`ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_CUSTOM_HEADERS`, `OPENAI_API_KEY`, `CODEX_API_KEY`, `CURSOR_API_KEY`, the
|
|
473
|
+
AWS keys, a variable Codex's `config.toml` names as a provider's `env_key`, or an `--env` name that looks like a credential (`KEY`,
|
|
474
|
+
`TOKEN`, `SECRET`, `PASSWORD`, `CREDENTIAL`), they are kept in `service-env.json` beside `credentials.json` (mode 0600) and every `run`
|
|
475
|
+
gives them to the agents that do not set them in `[agents.env]` (a configuration directory its group or others may write is first made 0700; one owned by another account is refused). `install` prints their names, never their values; `service status`
|
|
476
|
+
lists them; `uninstall` deletes the file. Without this a connector that works in a terminal loses its agents' sign-in as a service,
|
|
477
|
+
and each session fails at its first message. `CLAW_CONNECTOR_KEY` is never copied. Run `install` again after changing any of them, or
|
|
478
|
+
after moving Node. With a service installed, `claw-connector stop` stops the connector until the next login,
|
|
416
479
|
boot or keep-alive check; to stay offline use `pause`, or `service uninstall`.
|
|
417
480
|
|
|
418
481
|
**Why it went down.** Every disconnect is appended to `disconnects.jsonl` in the state directory: `server_closed` (a Claw release or
|
|
419
482
|
restart, `superseded` by another run, `token_expired`; reconnected at once), `stream_lost` (the network, with the error), `stopped`,
|
|
420
|
-
`revoked`, `auth_failed`, `failed`,
|
|
421
|
-
dated by its last heartbeat)
|
|
483
|
+
`revoked`, `auth_failed`, `failed`, `process_died` (the previous run never shut down: killed, crashed or the machine restarted;
|
|
484
|
+
dated by its last heartbeat), and `connector_limit_reached` (Claw already holds the most connectors one person may have: remove a
|
|
485
|
+
machine you no longer use, in the chat with `/agents` or with `claw-connector logout` on it; the run stays up and tries again every 10
|
|
486
|
+
minutes instead of being restarted every 5 seconds). `status` shows the latest, `service status` the last five.
|
|
422
487
|
|
|
423
488
|
## Restarts and crashes
|
|
424
489
|
|
|
425
|
-
A prompt counts as handled only when it reaches the agent (or is dropped by a cancel
|
|
426
|
-
dies with a prompt still queued is sent that prompt again by Claw instead of
|
|
427
|
-
for an answer lives in the adapter, which dies with the connector: the next run
|
|
428
|
-
`connector_restart`), so a phone is not left with a button that does nothing. Session
|
|
429
|
-
their sequence numbers are on disk; a session resumes in a fresh adapter on its next prompt
|
|
490
|
+
A prompt counts as handled only when it reaches the agent (or is dropped by a cancel or a close), so a connector that
|
|
491
|
+
dies, or is stopped or restarted by its supervisor, with a prompt still queued is sent that prompt again by Claw instead of
|
|
492
|
+
losing it. A permission request waiting for an answer lives in the adapter, which dies with the connector: the next run
|
|
493
|
+
closes such a card (`cancelled`, by `connector_restart`), so a phone is not left with a button that does nothing. Session
|
|
494
|
+
state, unacknowledged events and their sequence numbers are on disk; a session resumes in a fresh adapter on its next prompt
|
|
495
|
+
(Claude Code's `session/resume`, the conversation intact).
|
|
496
|
+
|
|
497
|
+
The turn an agent is running is on disk too, from the moment its prompt reaches the agent until its `turn_end`. The next run
|
|
498
|
+
(after a crash, a kill, a pod restart, `stop`, or an upgrade) finds it and:
|
|
499
|
+
|
|
500
|
+
- carries it on when the agent was waiting for a person's permission for a tool call (not a plan or a question card), the agent
|
|
501
|
+
always asks (no `approval = "never"`, no `allow_unsafe_modes`) and its session was in a mode that asks, the connector is not
|
|
502
|
+
paused, and it was away no longer than a card lives (10 minutes): the agent is told the connector restarted and to go on, so
|
|
503
|
+
it asks again and a new card comes; the prompt's own `turn_end` still ends it. It only asks, so nothing runs that nobody
|
|
504
|
+
approved. Nothing is carried on before the run has signed in (a revoked connector carries on nothing);
|
|
505
|
+
- otherwise ends it with `turn_end` `{"stopReason": "error", "error": "connector_restarted"}`, naming its prompt (`cmd_id`,
|
|
506
|
+
`cmd_seq`), so a phone stops waiting for it. The agent is not re-run by itself; the next message resumes the conversation.
|
|
507
|
+
|
|
508
|
+
Every session used in the last 4 hours, and every one with a turn found this way, then gets one `session_recovered` event
|
|
509
|
+
(`interrupted`: a turn was cut off; `continuing`: it is being carried on; `cmd_id` and `cmd_seq` of that turn), which a chat
|
|
510
|
+
shows as "recovered". A stop by the kill switch (`pause`, a revocation) still ends every turn and drops the queue.
|
|
430
511
|
|
|
431
512
|
Known limit: the working-directory check resolves the path and starts the adapter in the resolved path, but another process of
|
|
432
513
|
the same user that can rewrite a directory in that path could swap a symlink between the check and the start. The
|