fix(cli): frictionless relay init; teammates join with relay init --key
Merged
KJ
8d ago+488+488+012345678901234567890123456789-94-94-01234567890123456789
What
1. relay init asks nothing by default
Plain relay init asks no questions, in a terminal too. It picks:
- Server: this machine.
--hosteduses the hosted service, and--url/--keyuse your own server. - Tools: every installed agent tool.
- Scope: global.
It ends with a summary of what it chose and the flag that changes each choice:
Chosen without asking:
Server: hosted workspace "khaliqgant-rq8a-project" (--name to name it)
Tools: claude, codex, opencode, grok, devin (the ones installed here; --tools to choose)
Scope: global (--scope project for this project only)
To choose each step yourself, run: relay init --interactive
- The old prompts are behind
-i, --interactive. I chose this name over--choosebecause-i/--interactiveis the usual Unix spelling (rm -i,git add -i), it reads as "ask me", and it pairs with--yes. --interactivewithout a terminal is an error, not a silent fallback.--yesis still accepted and does nothing, so existing scripts and docs keep working.- A pipe or CI run behaves exactly like the default.
2. Joining a hosted workspace takes one command
relay init --key <key>joins the hosted workspace that key belongs to (HOSTED_ORIGIN).--urlstill selects your own server.- Naming.
--nameis optional: the workspace takes the nameGET /v1/workspacegives it, with a numeric suffix if another saved workspace already has that name. If the server gives no name, it falls back to the person and the folder. - Origin safety. A key already bound to another server is refused before anything is sent.
guardCredentialOriginstill binds the key after the check, and an explicit--urlstill moves the binding. - Join output. Joining prints
Joined hosted workspace "<name>" (https://cast.agentrelay.com).andYour key is only ever sent to the hosted service.It is recorded as anownsetup, so a name alone never confirms which workspace the core serves (review fix). relay init --hostedwithout--namenames the workspace after the person and the folder, unique among saved workspaces, and says how to change it. For teammates it prints:
The package spec isOn another machine, run this in the project to join it and connect its agent tools (treat this key like a password): npx -y agent-relay@next init --key '<key>'agent-relay@nextfor a-nextprerelease (or an unpublished build), and the exact version for a stable release. It needs no install on the teammate's machine.relay workspace joinstill only saves the workspace. It now says so and points atrelay init --key <key> --name <name>, adding--urlfor a configured or bound self-hosted server. It never echoes the key, and never points a self-hosted key at the hosted service (review fix).shellWordmoves tolib/agent-registration.tsand also quotes words that start with=, because zsh would expand them (review fix).- Docs.
docs/quickstart.mdis updated: the default,--interactive, flags, and "Across machines". The CHANGELOG is updated under[Unreleased - Major].
Tests
init.test.ts:- no prompts by default, even in a TTY, plus the chosen-without-asking summary
--interactiveprompts (every guided test now passes it) and fails without a terminal--yesis still accepted- the default hosted name, with a
-2suffix for a second one - the npx spec per CLI version
--keyalone joins hosted under the server's name- an explicit
--nameand--urlstill work - a key bound to a self-hosted server is refused without any request
- a joined hosted workspace isn't confirmed by name alone
workspace.test.ts: the join hint, a bound key, and quoting of=.vitest run init.test.ts workspace.test.ts: 137 passed.tsc --noEmit: clean. ESLint: 0 errors. Prettier: clean.
Live two-user e2e (macOS, Codex 0.163, isolated HOME/AGENT_RELAY_HOME/CODEX_HOME/XDG, CLI built from this branch, probe from #2029)
User A, in ~/rq8a-project:
$ relay init --hosted
Created hosted workspace "khaliqgant-rq8a-project".
Named after you and this folder; pass --name to choose another.
On another machine, run this in the project to join it and connect its agent tools (treat this key like a password):
npx -y agent-relay@14.0.0 init --key 'rk_…'
…
The source build's version is 14.0.0, so it prints that exact version. A published 15.0.0-next.* build prints agent-relay@next; this is covered by tests.
User B, in ~/rq8b-project, ran that line with the published npx -y agent-relay@… swapped for this branch's build:
$ relay init --key 'rk_…'
Joined hosted workspace "khaliqgant-rq8a-project" (https://cast.agentrelay.com).
Your key is only ever sent to the hosted service.
…
Chosen without asking:
Server: workspace "khaliqgant-rq8a-project" on https://cast.agentrelay.com
Tools: claude, codex, opencode, grok, devin (the ones installed here; --tools to choose)
Scope: global (--scope project for this project only)
Then Codex on both sides (with #2029's SessionStart preamble):
- A fresh A session (0 relayed messages before) was asked to DM B. It used the local
agent-relay-probe relay socket-request … --path '/send?to=codex-rq8b-project-session'with no approval prompt and no ChatGPT connector. Result: message242068751546728448. - B received it injected. B's reply, message
242069012398882816, was injected into A. relay init --uninstallin both removedhooks.json,rules/and every trust entry. Codex's own[tui]keys are the onlyconfig.tomlchange.
The hosted test workspace khaliqgant-rq8a-project remains on cast.agentrelay.com.
🤖 Generated with Claude Code
Agent Relay sessions
claudesession8ab900cc· opened viagh pr create· last active 2026-10-30