fix(cli): name the host in messages when mounted as agent-relay file - #510
Conversation
Closes #509. `agent-relay file` mounts this binary through @relayfile/sdk's Relay CLI surface. Someone who arrived that way installed `agent-relay`, not `relayfile`, so six messages sent them to a binary they do not have: $ agent-relay file writeback status error: credentials not found at ~/.relayfile/credentials.json; run relayfile login --api-key for self-hosted credentials … Help never had this problem — it is rendered from the declared command spec rather than forwarded, so `agent-relay file --help` cannot print `Usage: relayfile …`. Error text is written by the Go binary at runtime and was not covered by that. The binary cannot know how it was reached, so the surface tells it: RELAYFILE_PROGRAM_NAME, set on the child env, read by programName(), which falls back to "relayfile" when unset or blank. Direct users keep seeing the name they typed. Fixed at all five user-facing sites rather than only the reachable two, so a message that becomes reachable later is already correct. The test drives a real spawn through the real resolver with a fixture binary that echoes the variable back, rather than stubbing the spawn: the value has to survive the env the surface actually builds, including when a caller supplies its own. Mutation-verified — dropping the env fails both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Session-Id: d458bd97-53d8-4f02-be9c-48b67b93c916
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| func programName() string { | ||
| if name := strings.TrimSpace(os.Getenv(programNameEnv)); name != "" { | ||
| return name | ||
| } | ||
| return "relayfile" |
There was a problem hiding this comment.
🟡 Mounted hints name missing binary
When mounted, several runtime hints still print relayfile, bypassing programName(). Examples include invalid workspace, setup completion, integration connection, and dev guidance. Users without the standalone binary receive commands they cannot run.
Learn more
The mounted TypeScript surface sets RELAYFILE_PROGRAM_NAME to agent-relay file. The Go binary reads that value through programName(), but only the newly edited call sites use it. Other reachable runtime guidance still embeds relayfile, so the environment contract does not fix those messages.
Example: A user installs only agent-relay and runs agent-relay file integration connect github. After connecting, runIntegrationConnect prints Run relayfile mount ...; that command is unavailable, while agent-relay file mount ... works.
Recommended fix: Route every user-facing command hint through a shared formatter based on programName(). At minimum, update the empty-name branch in agentRelayMessagingOnlyWorkspaceError.Error, agentRelayInvalidWorkspaceKeyError.Error, runSetupWithOptions, runIntegrationConnect, runDev, mount/status guidance, and stuck-event guidance. Preserve hard-coded agent-relay commands where they intentionally invoke the host rather than Relayfile.
Was this helpful? React with 👍 or 👎 to provide feedback.
Relayfile Eval ReviewRun: Passed: 4 | Needs human: 0 | Reviewable: 0 | Missing output: 0 | Failed: 0 | Skipped: 0 Human Review CasesNo reviewable human-review cases captured Relayfile output. |
Review found the previous commit's fix was a fraction of the problem. It
corrected five sites located by grepping for "run relayfile"; Devin and
Bugbot both pointed out that the same advice appears in other phrasings —
usage lines, setup completion, integration connect, dev guidance, the
supervisor help block, and the nameless branch of the messaging-only error,
which still hardcoded "relayfile setup" while the same sentence's login
advice used programName().
The lesson is that a grep for one phrasing cannot bound this. So:
- 44 `Usage: relayfile …` prints and 38 `usage: relayfile …` errors now
format through programName()
- 8 multi-line raw-string help blocks templated, including the top-level
usage, `listen`, and `supervisor`
- the remaining instruction sites: setup completion, integration connect,
dev guidance, writeback stuck guidance, the login deprecation warning,
and the empty-name setup branch
Deliberately untouched, because they are not instructions:
- `relayfile-listen.service` and `com.relayfile.listen.plist` are real
paths on disk, identical whoever invokes us; a blanket rename would
have broken them
- "delegated relayfile credentials" and "relayfile workspace id" are
nouns describing what is missing
The test asserts the class rather than any message. It runs every usage
printer with the host name set and fails on a pattern, because the leak was
never in one place. It also pins the two service filenames, and that a
direct user still sees "relayfile".
That test immediately found five more leaks than review had named, and then
a sixth: a bare `relayfile` with no verb, in the top-level usage, which both
my pattern and the test's first pattern missed. The test regex now covers a
bare mention too. Mutation-verified in both directions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Session-Id: d458bd97-53d8-4f02-be9c-48b67b93c916
|
Both right, and the finding is bigger than either comment. Fixed in 0f03ead. Bugbot — the nameless branch of Devin — the broader point is the correct one, and it invalidates how I found the original five. I grepped for What was actually wrong
All now format through Deliberately untouched
The test is the actual deliverableAsserting any single message would have been useless here — the leak was never in one place. It runs every usage printer with the host name set and fails on a pattern, plus pins the two service filenames and that a direct user still sees It earned that immediately: it found five more leaking printers than review had named ( VerifiedA full sweep of all 23 mounted subcommands, with and without bad args, leaves only 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0f03ead. Configure here.
Three suites spawn the binary directly and compare it to the surface. They
exist to prove argv routes through unchanged — but they used whole-output
equality as the proxy, so they failed on the one difference the mount is
supposed to make: mounted, the binary names the host rather than itself.
expected 'Usage: agent-relay file mount [WORKSP…'
to be 'Usage: relayfile mount [WORKSPACE] [L…'
Give the baseline spawn the same RELAYFILE_PROGRAM_NAME the surface sets, so
the comparison isolates routing again. The surface test that asserted the
top-level banner now expects the mounted name, which makes it a stronger
check: it proves both that a leading flag reaches the binary and that the
program name arrived with it.
Not fixed here, and not caused by this branch: client.test.ts's "ErrorEvent
is undefined (Node)" fails identically on origin/main, verified in a clean
worktree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Session-Id: d458bd97-53d8-4f02-be9c-48b67b93c916

Closes #509. Found while testing the published
agent-relay@12.2.4mount end to end.The problem
relayfileisn't on their PATH — they installedagent-relay. The advice is unfollowable.Help never had this problem: it's rendered from the declared command spec rather than forwarded, so
agent-relay file --helpcannot printUsage: relayfile .... Error text is written by the Go binary at runtime and isn't covered by that.The fix
The binary has no way to know it was reached through a host, so the surface tells it —
RELAYFILE_PROGRAM_NAMEon the child env, read byprogramName(), defaulting torelayfilewhen unset or blank.Fixed at all five user-facing sites in
cmd/relayfile-cli/main.go, not just the two reachable today (writeback status,seed), so a message that becomes reachable later is already right.On the test
It drives a real spawn through the real resolver with a fixture binary that echoes the variable back, rather than stubbing the spawn — following the pattern in
binary-output.test.ts. The value has to survive the env the surface actually constructs, and there's a second case for a caller supplying its ownenv, since a host passing one must not silently drop the name.Mutation-verified — reverting the spawn to plain
envfails both:go vetclean,gofmtclean,tsc --noEmitclean, and theCredential|Workspace|Messaging|BackgroundGo suites pass.Alternative considered
Rewriting the strings in the surface's io layer. Cheaper, but string-matching error text is brittle and stops working silently the moment a message is reworded. Putting the name where the messages are written means new messages are correct by construction.
🤖 Generated with Claude Code
Note
Low Risk
User-facing strings and spawn env only; no auth, sync, or data-path changes. Direct
relayfileinvocations keep the default name.Overview
When the Relayfile CLI runs behind
agent-relay file, user-facing text no longer hardcodesrelayfilefor “run this next” guidance.The Go binary reads
RELAYFILE_PROGRAM_NAMEvia a newprogramName()helper (defaultrelayfilewhen unset). Usage strings, help, setup/login/mount/writeback errors, and similar messages now use that name. The TypeScript relay CLI surface sets the env toagent-relay fileon every child spawn so mounted users get followable commands likeagent-relay file login.Tests lock the contract: Go usage printers must not mention bare
relayfilewhen the env is set (while keeping literal service paths likerelayfile-listen.service), TS tests verify the env survives surface spawns and align byte-for-byte comparisons with the mounted naming behavior.Reviewed by Cursor Bugbot for commit 167a64d. Bugbot is set up for automated code reviews on this repo. Configure here.