@relaymessenger/cli 0.3.0
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/LICENSE +21 -0
- package/README.md +139 -0
- package/claude-plugin/marketplace/.claude-plugin/marketplace.json +14 -0
- package/claude-plugin/marketplace/plugins/relay/.claude-plugin/plugin.json +18 -0
- package/claude-plugin/marketplace/plugins/relay/LICENSE +21 -0
- package/claude-plugin/marketplace/plugins/relay/README.md +173 -0
- package/claude-plugin/marketplace/plugins/relay/commands/configure.md +71 -0
- package/claude-plugin/marketplace/plugins/relay/runtime/server.mjs +26107 -0
- package/dist/api.d.ts +74 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +129 -0
- package/dist/api.js.map +1 -0
- package/dist/claude-settings.d.ts +14 -0
- package/dist/claude-settings.d.ts.map +1 -0
- package/dist/claude-settings.js +47 -0
- package/dist/claude-settings.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +162 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex.d.ts +25 -0
- package/dist/codex.d.ts.map +1 -0
- package/dist/codex.js +227 -0
- package/dist/codex.js.map +1 -0
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +122 -0
- package/dist/doctor.js.map +1 -0
- package/dist/engine/acp.d.ts +69 -0
- package/dist/engine/acp.d.ts.map +1 -0
- package/dist/engine/acp.js +402 -0
- package/dist/engine/acp.js.map +1 -0
- package/dist/engine/catalog.d.ts +26 -0
- package/dist/engine/catalog.d.ts.map +1 -0
- package/dist/engine/catalog.js +41 -0
- package/dist/engine/catalog.js.map +1 -0
- package/dist/engine/process.d.ts +12 -0
- package/dist/engine/process.d.ts.map +1 -0
- package/dist/engine/process.js +54 -0
- package/dist/engine/process.js.map +1 -0
- package/dist/engine/types.d.ts +57 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +8 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/flags.d.ts +9 -0
- package/dist/flags.d.ts.map +1 -0
- package/dist/flags.js +34 -0
- package/dist/flags.js.map +1 -0
- package/dist/install.d.ts +69 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +510 -0
- package/dist/install.js.map +1 -0
- package/dist/mcp.d.ts +16 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +127 -0
- package/dist/mcp.js.map +1 -0
- package/dist/pair.d.ts +20 -0
- package/dist/pair.d.ts.map +1 -0
- package/dist/pair.js +135 -0
- package/dist/pair.js.map +1 -0
- package/dist/permissions.d.ts +87 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +342 -0
- package/dist/permissions.js.map +1 -0
- package/dist/receive.d.ts +54 -0
- package/dist/receive.d.ts.map +1 -0
- package/dist/receive.js +353 -0
- package/dist/receive.js.map +1 -0
- package/dist/store.d.ts +260 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +550 -0
- package/dist/store.js.map +1 -0
- package/openclaw-plugin/relaymessenger-openclaw-plugin-0.1.0.tgz +0 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Companion Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# relaymessenger
|
|
2
|
+
|
|
3
|
+
Message your local coding agent from your phone. `relaymessenger` bridges a Relay
|
|
4
|
+
agent conversation to Claude Code, Codex, or Hermes Agent running on this
|
|
5
|
+
machine. It also installs native Relay channels for Claude Code and OpenClaw.
|
|
6
|
+
Texts become prompts, replies come back as messages, and tool approvals arrive
|
|
7
|
+
as Allow/Deny cards you answer with a tap.
|
|
8
|
+
|
|
9
|
+
Relay is messaging for agents — https://relayapp.im. API reference:
|
|
10
|
+
https://docs.relayapp.im.
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install -g @relaymessenger/cli
|
|
16
|
+
|
|
17
|
+
# 1. Pair this machine with the Relay app (QR + short code, ~30 s)
|
|
18
|
+
relaymessenger pair
|
|
19
|
+
|
|
20
|
+
# 2. Start the bridge in the repo you want the agent to work in
|
|
21
|
+
cd ~/code/my-project
|
|
22
|
+
relaymessenger start --engine claude # or: --engine codex | --engine hermes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Now text the agent from the Relay app. Each message (or quick burst of
|
|
26
|
+
messages) becomes one engine turn; the bridge shows a typing indicator while
|
|
27
|
+
the engine works and posts one finalized reply per turn.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
| Command | What it does |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `relaymessenger pair` | `POST /v1/pairings`, shows a terminal QR + code, long-polls until you claim it in the app, stores the Agent Token in `~/.relaymessenger/config.json` (chmod 600) and pins your user id as the bridge owner (from `GET /v1/agents/me`; override with `RELAY_OWNER_USER_ID`). If owner lookup is interrupted after the token is saved, running the command again resumes that saved token without creating another agent. The token never appears on the phone. |
|
|
34
|
+
| `relaymessenger start` | Receive loop: long-polls `GET /v1/events`, drives the engine over ACP, replies via `POST /v1/messages` with an `Idempotency-Key`. Flags: `--engine claude\|codex\|hermes`, `--dir <path>`. Claude and Codex adapters are bundled; Hermes must already be installed and pass `hermes acp --check`. |
|
|
35
|
+
| `relaymessenger install-codex` | Run from a project root to opt in that project only. Merges — never clobbers — `[mcp_servers.relay]` + `notify` into `~/.codex/config.toml` (comments preserved; a `.bak` of the original is kept) and a `PermissionRequest` hook into `~/.codex/hooks.json`. Other projects are suppressed until installed separately. Codex gates untrusted hook handlers: the first run may ask you to trust the relaymessenger handler. |
|
|
36
|
+
| `relaymessenger install-claude` | After pairing, strictly validates the Claude plugin bundled in the installed npm package, persists its local marketplace under the paired account's private runtime directory, installs `relay@relaymessenger-bundled`, and writes the token/API origin/owner pin to `~/.claude/channels/relay/.env` with mode 600 without printing the token. It refuses to overwrite a different configured identity. |
|
|
37
|
+
| `relaymessenger install-openclaw` | After pairing, persists and installs the OpenClaw plugin archive bundled in the npm package, adds only Relay's plugin/channel fields to `~/.openclaw/openclaw.json`, and writes the paired token to an owner-only file. Existing unrelated config is preserved and a different configured identity is refused. |
|
|
38
|
+
| `relaymessenger doctor` | Checks Node, pairing, token file permissions, API reachability, installed adapter pins, and durable-state health. |
|
|
39
|
+
|
|
40
|
+
## How the wire works
|
|
41
|
+
|
|
42
|
+
Everything rides Relay's public agent API — the same surface you can drive
|
|
43
|
+
with curl:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
# what the bridge polls (agent bearer auth; long-poll, cursor acks ≤ N)
|
|
47
|
+
curl -H "Authorization: Bearer $AGENT_TOKEN" \
|
|
48
|
+
"https://api.relayapp.im/v1/events?cursor=0&timeout=25"
|
|
49
|
+
|
|
50
|
+
# what the bridge sends per finished turn
|
|
51
|
+
curl -X POST https://api.relayapp.im/v1/messages \
|
|
52
|
+
-H "Authorization: Bearer $AGENT_TOKEN" \
|
|
53
|
+
-H "Idempotency-Key: relay-turn-<sha256(conversation,event-id-batch)>" \
|
|
54
|
+
-H "Content-Type: application/json" \
|
|
55
|
+
-d '{"conversation_id":"cnv_…","parts":[{"type":"text","text":"done"}]}'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- **Engines**: Claude and Codex are spawned as ACP adapters over
|
|
59
|
+
stdio (`@agentclientprotocol/claude-agent-acp`,
|
|
60
|
+
`@agentclientprotocol/codex-acp`). Both adapters are exact runtime
|
|
61
|
+
dependencies resolved from the installed package; the bridge never runs
|
|
62
|
+
mutable registry `latest` code. Adapter subprocesses receive platform and
|
|
63
|
+
engine/provider variables, not the complete parent environment. Hermes is
|
|
64
|
+
launched shell-free through its installed `hermes acp` stdio server and is
|
|
65
|
+
checked with `hermes acp --check`. Conversation → session bindings persist
|
|
66
|
+
in the paired account's runtime directory, so a conversation keeps its
|
|
67
|
+
engine context. OpenClaw is deliberately separate: `install-openclaw`
|
|
68
|
+
installs a native channel plugin into OpenClaw rather than pretending it is
|
|
69
|
+
an ACP coding-engine preset.
|
|
70
|
+
- **Approvals**: an engine `session/request_permission` becomes a Relay
|
|
71
|
+
message with a text part plus an `agent_permission_request` data part
|
|
72
|
+
(origin-tagged Allow/Deny options and quick-reply chips). Tap a chip or text
|
|
73
|
+
`yes <id>` / `no <id>`. The full security-relevant tool input and affected
|
|
74
|
+
paths must fit in the card; an operation that cannot be represented in full
|
|
75
|
+
is denied instead of shown partially. No answer within 10 minutes → deny.
|
|
76
|
+
- **Owner gate**: only the user pinned at pair time can prompt the engine or
|
|
77
|
+
answer an approval card; messages from anyone else are ignored before their
|
|
78
|
+
content is interpreted.
|
|
79
|
+
- **Reliability**: the receive cursor advances only in the same atomic
|
|
80
|
+
(fsync + rename) write that persists the event queue
|
|
81
|
+
(`~/.relaymessenger/accounts/<origin-agent-hash>/state.json`), event ids are deduped, rapid messages debounce
|
|
82
|
+
~800 ms into one turn, and the poll loop restarts with capped exponential
|
|
83
|
+
backoff + jitter. Each pending approval is its own create-once file under
|
|
84
|
+
that account's `approvals/`, so a bridge restart cannot lose one and no two
|
|
85
|
+
processes ever rewrite a shared snapshot. Engine/tool turns are at-most-once:
|
|
86
|
+
an attempt marker is durable before execution, so a crash never silently
|
|
87
|
+
repeats a deploy, deletion, command, or external send. Completed replies use
|
|
88
|
+
a durable outbox and stable idempotency key, so delivery can retry without
|
|
89
|
+
rerunning those tools. An interrupted turn is reported and must be retried
|
|
90
|
+
explicitly by the owner.
|
|
91
|
+
- Long-poll is exclusive: an enabled webhook endpoint or a second poller gets
|
|
92
|
+
`409` (Telegram semantics). One consumer per token. A `401` stops the loop
|
|
93
|
+
with re-pair guidance instead of retrying.
|
|
94
|
+
- **Codex notification privacy**: `install-codex` stores an explicit local
|
|
95
|
+
allowlist entry for the current project root in
|
|
96
|
+
`~/.relaymessenger/codex-notify.json`. A completed turn from any other project is
|
|
97
|
+
suppressed. For an allowed project, Relay receives the project directory's
|
|
98
|
+
basename plus Codex's complete `last-assistant-message`; input messages and
|
|
99
|
+
the absolute working-directory path are not sent. That text is retained in
|
|
100
|
+
Relay message history. There is no global-all-projects opt-in; run
|
|
101
|
+
`install-codex` in each
|
|
102
|
+
project you choose to disclose.
|
|
103
|
+
- **Codex MCP sends**: `relay_send_message` requires a caller-chosen stable
|
|
104
|
+
`send_id`. Reuse the same `send_id`, conversation, and text only after an
|
|
105
|
+
unknown outcome; a changed payload is rejected. The mapping and
|
|
106
|
+
idempotency key live in the paired account's private runtime directory, so
|
|
107
|
+
a process restart cannot turn one logical send into two messages.
|
|
108
|
+
|
|
109
|
+
## Development and testing
|
|
110
|
+
|
|
111
|
+
- `RELAY_API_ORIGIN` points `pair`, `start`, and `doctor` at a
|
|
112
|
+
non-production Relay API origin, e.g. a local dev server:
|
|
113
|
+
`RELAY_API_ORIGIN=http://127.0.0.1:8787 relaymessenger pair`. This is a
|
|
114
|
+
development/testing mechanism only — production
|
|
115
|
+
(`https://api.relayapp.im`) stays the default, the value must be an
|
|
116
|
+
origin with no path/query/credentials, and plain HTTP is accepted only
|
|
117
|
+
for loopback hosts (same rule as every other origin the bridge uses).
|
|
118
|
+
Durable bridge state is scoped per effective origin, so an override
|
|
119
|
+
never replays or advances production cursors and ledgers.
|
|
120
|
+
- With `--engine claude`, the bundled adapter inherits your Claude Code
|
|
121
|
+
settings. If the resolved `permissions.defaultMode` is
|
|
122
|
+
`bypassPermissions`, the engine never asks for approval, so phone
|
|
123
|
+
Allow/Deny cards will not appear; `relaymessenger start` and `relaymessenger doctor`
|
|
124
|
+
print a warning when they detect this.
|
|
125
|
+
|
|
126
|
+
## Files
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
~/.relaymessenger/config.json agent token, API origin, pinned owner (chmod 600)
|
|
130
|
+
~/.relaymessenger/codex-notify.json locally allowed Codex project roots (not sent)
|
|
131
|
+
~/.relaymessenger/accounts/<hash>/state.json cursor, queued events/replies,
|
|
132
|
+
owner conversation (start-only)
|
|
133
|
+
~/.relaymessenger/accounts/<hash>/approvals/ one file per pending approval
|
|
134
|
+
~/.relaymessenger/accounts/<hash>/sessions.json conversation → session bindings
|
|
135
|
+
~/.relaymessenger/accounts/<hash>/mcp-sends/ durable Codex MCP logical sends
|
|
136
|
+
~/.relaymessenger/accounts/<hash>/installed-plugins/ stable bundled plugin sources
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Requires Node >= 22.18.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "relaymessenger-bundled",
|
|
3
|
+
"description": "Bundled Relay channel plugin for Claude Code",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Relay"
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
{
|
|
9
|
+
"name": "relay",
|
|
10
|
+
"source": "./plugins/relay",
|
|
11
|
+
"description": "Text your Claude Code from Relay: chat bridge and phone Allow/Deny permission relay"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "relay",
|
|
3
|
+
"description": "Relay channel for Claude Code — message your Claude Code session from your Relay agent conversation, with remote Allow/Deny permission relay. Run /relay:configure to set up.",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"author": { "name": "Relay" },
|
|
6
|
+
"keywords": ["relay", "messaging", "channel", "mcp"],
|
|
7
|
+
"mcpServers": {
|
|
8
|
+
"relay": {
|
|
9
|
+
"command": "node",
|
|
10
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/runtime/server.mjs"]
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"channels": [
|
|
14
|
+
{
|
|
15
|
+
"server": "relay"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Companion Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Relay channel for Claude Code
|
|
2
|
+
|
|
3
|
+
Message a running Claude Code session through your Relay agent conversation.
|
|
4
|
+
The plugin is a self-contained MCP stdio server for Claude Code's experimental
|
|
5
|
+
[channels contract](https://code.claude.com/docs/en/channels-reference).
|
|
6
|
+
|
|
7
|
+
- Owner-authenticated Relay messages enter the session as
|
|
8
|
+
`notifications/claude/channel` events.
|
|
9
|
+
- The `reply` tool sends a logical message back with retry-safe idempotency.
|
|
10
|
+
- Permission prompts can be reviewed and denied from Relay. Remote Allow is
|
|
11
|
+
available only when Claude supplies a complete, verifiable tool-input JSON;
|
|
12
|
+
otherwise approval remains local.
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Node.js 20.11 or newer
|
|
17
|
+
- Claude Code with channel support (channels remain a research preview)
|
|
18
|
+
- A Relay agent and Agent Token
|
|
19
|
+
- No webhook enabled for that agent: Relay permits one event consumer, so
|
|
20
|
+
long-polling and webhook delivery are mutually exclusive
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
Install the published CLI, pair once, and install its bundled local
|
|
25
|
+
marketplace:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install -g @relaymessenger/cli
|
|
29
|
+
relaymessenger pair
|
|
30
|
+
relaymessenger install-claude
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`install-claude` strictly validates the bundled source, copies it to a stable
|
|
34
|
+
content-addressed directory under the paired account's private Relay runtime,
|
|
35
|
+
registers the local `relaymessenger-bundled` marketplace, and installs
|
|
36
|
+
`relay@relaymessenger-bundled`. It does not depend on this GitHub repository or on
|
|
37
|
+
the npm package remaining at its original install path. The installed plugin
|
|
38
|
+
already contains `runtime/server.mjs` with all runtime dependencies bundled;
|
|
39
|
+
do not locate a plugin cache or run `npm install` after installation.
|
|
40
|
+
|
|
41
|
+
## Configure
|
|
42
|
+
|
|
43
|
+
The install command also configures the channel without exposing the token:
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
relaymessenger install-claude
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That command writes the paired token, API origin, and owner pin to the
|
|
50
|
+
platform-equivalent of:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
~/.claude/channels/relay/.env
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
with owner-only permissions and these values:
|
|
57
|
+
|
|
58
|
+
```dotenv
|
|
59
|
+
RELAY_AGENT_TOKEN=<your agent token>
|
|
60
|
+
RELAY_BASE_URL=https://api.relayapp.im
|
|
61
|
+
# Optional explicit owner pin. Otherwise GET /v1/agents/me must return one.
|
|
62
|
+
#RELAY_OWNER_USER_ID=usr_...
|
|
63
|
+
# Optional stable session namespace. Defaults to the Claude project directory.
|
|
64
|
+
#RELAY_CHANNEL_SESSION_ID=my-repository
|
|
65
|
+
# Explicit opt-in for a private agent only: trust the first user sender.
|
|
66
|
+
#RELAY_ALLOW_TOFU=1
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The file is mode 600/current-user-only. The command never prints the token and
|
|
70
|
+
refuses to overwrite a different existing channel identity. Run
|
|
71
|
+
`/relay:configure` for verification or for manual setup when relaymessenger is not
|
|
72
|
+
available and you already have a token through another secure route.
|
|
73
|
+
|
|
74
|
+
`RELAY_BASE_URL` must be an HTTPS origin with no path, query, fragment, or
|
|
75
|
+
embedded credentials. Plain HTTP is accepted only for `localhost`,
|
|
76
|
+
`127.0.0.1`, or `::1` development servers.
|
|
77
|
+
|
|
78
|
+
Verify without printing the token:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
node <installed-plugin-directory>/runtime/server.mjs --check
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Run
|
|
85
|
+
|
|
86
|
+
Custom channels require Claude Code's development-channel flag during the
|
|
87
|
+
research preview:
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
claude --dangerously-load-development-channels plugin:relay@relaymessenger-bundled
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use `server:relay` instead when registered as a bare MCP server.
|
|
94
|
+
|
|
95
|
+
Only one live Claude session may consume an agent's event stream. A second
|
|
96
|
+
session fails closed instead of stealing the long-poll consumer. Use a
|
|
97
|
+
different Relay agent when two sessions must receive messages concurrently.
|
|
98
|
+
|
|
99
|
+
## Delivery and retry contract
|
|
100
|
+
|
|
101
|
+
Channel notifications are unacknowledged at the Claude transport layer. This
|
|
102
|
+
plugin therefore stages each inbound event in an atomic local ledger before
|
|
103
|
+
notifying Claude and includes `delivery_id="evt_..."` on the channel tag.
|
|
104
|
+
Claude calls the `acknowledge` tool after fully handling it. Until then, the
|
|
105
|
+
delivery is re-notified every 30 seconds and replayed after a channel restart.
|
|
106
|
+
This is **at-least-once**, not exactly-once, delivery.
|
|
107
|
+
|
|
108
|
+
A crash after an external side effect but before acknowledgement can replay the
|
|
109
|
+
request. Before repeating a deploy, deletion, payment, shell command, or other
|
|
110
|
+
non-idempotent action, reconcile whether it already succeeded. The bridge
|
|
111
|
+
cannot make arbitrary tools exactly-once.
|
|
112
|
+
|
|
113
|
+
Outbound `reply` calls require a `send_id`. Reuse the same `send_id`,
|
|
114
|
+
conversation, and text for an unknown-outcome retry; use a new `send_id` for an
|
|
115
|
+
intentional repeat. The mapping is persisted before the HTTP request, and
|
|
116
|
+
reusing an id with different content is rejected.
|
|
117
|
+
|
|
118
|
+
## Permission safety and data boundary
|
|
119
|
+
|
|
120
|
+
Claude's channel permission notification exposes `input_preview`, documented
|
|
121
|
+
as JSON truncated at 200 characters. The plugin no longer truncates or folds
|
|
122
|
+
that field: it sends every character it receives and makes invisible controls
|
|
123
|
+
visible. When the value is shorter than the truncation boundary and parses as
|
|
124
|
+
complete JSON, the Relay card offers Allow and Deny. Otherwise it clearly says
|
|
125
|
+
the input may be incomplete, offers only Deny, and requires local-terminal
|
|
126
|
+
review to approve. This prevents a hidden destructive suffix from being
|
|
127
|
+
approved remotely.
|
|
128
|
+
|
|
129
|
+
Permission cards cross a data boundary: tool names, descriptions, shell
|
|
130
|
+
commands, local paths, and prefixes or complete contents supplied in the input
|
|
131
|
+
are uploaded to the configured Relay API and retained in Relay message history.
|
|
132
|
+
Do not enable permission relay for repositories or commands whose details must
|
|
133
|
+
not leave the machine. The Agent Token itself remains only in the local `.env`.
|
|
134
|
+
|
|
135
|
+
Only the agent owner's Relay user id can inject messages or verdicts. The owner
|
|
136
|
+
comes from `RELAY_OWNER_USER_ID` or `GET /v1/agents/me`; without one, startup
|
|
137
|
+
fails closed unless `RELAY_ALLOW_TOFU=1` was explicitly set. TOFU is suitable
|
|
138
|
+
only for an agent no one else can message.
|
|
139
|
+
|
|
140
|
+
## Durable state
|
|
141
|
+
|
|
142
|
+
State lives below `~/.claude/channels/relay/state/`, namespaced first by the
|
|
143
|
+
canonical API origin and Relay agent id. The account directory contains:
|
|
144
|
+
|
|
145
|
+
- `consumer-state.json` for the shared cursor and TOFU owner pin
|
|
146
|
+
- `consumer-ledger.json` for unacknowledged deliveries and recent event ids
|
|
147
|
+
|
|
148
|
+
Those two files are account-scoped so a later Claude session inherits the
|
|
149
|
+
consumer position instead of replaying retained history. Each hashed session
|
|
150
|
+
subdirectory separately contains `routing.json` and `session-ledger.json` for
|
|
151
|
+
last-conversation routing, permission registrations, and logical outbound
|
|
152
|
+
sends. A new session cannot answer an old session's permission card.
|
|
153
|
+
|
|
154
|
+
Writes use owner-only files and atomic rename. If consumer cursor state or
|
|
155
|
+
either security-critical ledger is corrupt, startup fails closed and preserves
|
|
156
|
+
a block marker plus the quarantined file instead of resetting the cursor or
|
|
157
|
+
silently replaying old events. Session routing state may be quarantined and
|
|
158
|
+
reset because it does not guard event delivery or external side effects.
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
npm ci
|
|
164
|
+
npm run check
|
|
165
|
+
npm test
|
|
166
|
+
npm run build
|
|
167
|
+
npm run pack:smoke
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`npm run build` produces the checked-in, self-contained
|
|
171
|
+
`runtime/server.mjs`. `npm pack` runs that build again through `prepack`. The
|
|
172
|
+
root release checks also run Claude Code's pinned `plugin validate --strict`
|
|
173
|
+
against both the plugin and marketplace before npm publication.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Configure and verify the self-contained Relay channel
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Configure Relay without asking the user to paste or echo a secret in chat.
|
|
6
|
+
|
|
7
|
+
1. Use the paired relaymessenger flow. Ask the user to run these locally if needed:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
npm install -g @relaymessenger/cli
|
|
11
|
+
relaymessenger pair
|
|
12
|
+
relaymessenger install-claude
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`install-claude` installs the bundled local marketplace as
|
|
16
|
+
`relay@relaymessenger-bundled`, then copies the paired token, API origin, and
|
|
17
|
+
pinned owner into the channel `.env` with current-user-only permissions. It
|
|
18
|
+
never prints the token and refuses to overwrite a different configured
|
|
19
|
+
channel identity.
|
|
20
|
+
|
|
21
|
+
2. Determine the user's channel directory using their platform conventions:
|
|
22
|
+
`~/.claude/channels/relay` on macOS/Linux or
|
|
23
|
+
`%USERPROFILE%\.claude\channels\relay` on Windows. Create it with access
|
|
24
|
+
restricted to the current user.
|
|
25
|
+
|
|
26
|
+
Verify that `.env` exists. Do not read or display its contents.
|
|
27
|
+
|
|
28
|
+
3. If relaymessenger is unavailable and the user already obtained an Agent Token
|
|
29
|
+
through another secure route, they may create `.env` themselves with
|
|
30
|
+
current-user-only access:
|
|
31
|
+
|
|
32
|
+
```dotenv
|
|
33
|
+
RELAY_AGENT_TOKEN=
|
|
34
|
+
RELAY_BASE_URL=https://api.relayapp.im
|
|
35
|
+
#RELAY_OWNER_USER_ID=usr_...
|
|
36
|
+
#RELAY_CHANNEL_SESSION_ID=my-repository
|
|
37
|
+
#RELAY_ALLOW_TOFU=1
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`RELAY_ALLOW_TOFU=1` is an explicit fallback only for an agent no one else
|
|
41
|
+
can message. Normally the owner comes from `GET /v1/agents/me`.
|
|
42
|
+
|
|
43
|
+
Never request, print, or place the token in a command argument.
|
|
44
|
+
|
|
45
|
+
4. Do not run `npm install`. The installed plugin's
|
|
46
|
+
`runtime/server.mjs` already contains its runtime dependencies.
|
|
47
|
+
|
|
48
|
+
5. After the user confirms the file is ready, run this from the installed
|
|
49
|
+
plugin directory using a platform-native path:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
node runtime/server.mjs --check
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Report only success, agent id, or the sanitized error. Never display the
|
|
56
|
+
`.env` file or token. The check also rejects non-HTTPS remote origins.
|
|
57
|
+
|
|
58
|
+
6. Explain how to start the research-preview channel:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
claude --dangerously-load-development-channels plugin:relay@relaymessenger-bundled
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use `server:relay` for a bare MCP registration. The agent must not have a
|
|
65
|
+
webhook enabled, and only one live Claude session can consume that agent.
|
|
66
|
+
|
|
67
|
+
7. Ask the user to message the agent from Relay. Explain that messages are
|
|
68
|
+
delivered at least once until Claude acknowledges them. Permission cards
|
|
69
|
+
upload the displayed tool details to Relay history; an incomplete
|
|
70
|
+
200-character Claude preview can be denied remotely but must be approved at
|
|
71
|
+
the local terminal.
|