Add install-beeper.sh: one-command Beeper to Edison MCP gateway installer#15
Conversation
Port the Beeper installer into the app monorepo (the standalone Edison-Watch/ stdiod repo was archived). Wires Beeper into the Edison Watch MCP gateway on macOS via the shipped device-authorization flow: browser device login, submit the beeper stdio server for approval, discover the Beeper token, and bind it via the admin /env route (bind-token). Includes --demo/--release backend shortcuts and follows the device's authorized backend by default. Agent-friendly: flags/env for every input, --dry-run, fail-fast errors with the exact fix. Passes crates/stdiod/scripts/check_ai_writing.sh and shellcheck. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
There was a problem hiding this comment.
4 issues found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="crates/stdiod/scripts/install-beeper.sh">
<violation number="1" location="crates/stdiod/scripts/install-beeper.sh:217">
P2: The one-command `install --install-deps --yes` flow still fails on machines without Rust because Cargo is neither provisioned nor checked with a usable setup fix. Handle `cargo` as a prerequisite before attempting this install.</violation>
<violation number="2" location="crates/stdiod/scripts/install-beeper.sh:238">
P2: Beeper on 23374–23378 is reported reachable, but spawned `@beeper/desktop-mcp` still connects only to 23373. Limit this check to 23373 or fail with the documented port-conflict fix before submitting a server that cannot connect.</violation>
<violation number="3" location="crates/stdiod/scripts/install-beeper.sh:479">
P2: Rerunning `install` while the `beeper` request is still pending can fail instead of recognizing the existing request. The duplicate detection searches the CLI output for response-body text that `edison-stdiod` deliberately redacts, so the pending-request path should be handled by an explicit status/list API or by exposing a reliable machine-readable result.</violation>
<violation number="4" location="crates/stdiod/scripts/install-beeper.sh:624">
P2: Tokens containing JSON-special characters cannot be bound reliably because the request body is assembled with raw string interpolation. Serialize `BEEPER_ACCESS_TOKEN` with a JSON encoder rather than `printf` substitution.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| [ -n "$code" ] && [ "$code" != "000" ] && { printf '%s' "$BEEPER_API_URL"; return 0; } | ||
| fi | ||
| for h in 127.0.0.1 localhost "[::1]"; do | ||
| for p in 23373 23374 23375 23376 23377 23378; do |
There was a problem hiding this comment.
P2: Beeper on 23374–23378 is reported reachable, but spawned @beeper/desktop-mcp still connects only to 23373. Limit this check to 23373 or fail with the documented port-conflict fix before submitting a server that cannot connect.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 238:
<comment>Beeper on 23374–23378 is reported reachable, but spawned `@beeper/desktop-mcp` still connects only to 23373. Limit this check to 23373 or fail with the documented port-conflict fix before submitting a server that cannot connect.</comment>
<file context>
@@ -0,0 +1,747 @@
+ [ -n "$code" ] && [ "$code" != "000" ] && { printf '%s' "$BEEPER_API_URL"; return 0; }
+ fi
+ for h in 127.0.0.1 localhost "[::1]"; do
+ for p in 23373 23374 23375 23376 23377 23378; do
+ url="http://$h:$p"
+ code="$(curl -s -m 2 -o /dev/null -w '%{http_code}' "${url}${wk}" 2>/dev/null || true)"
</file context>
| brew install --quiet node | ||
| ensure_tool edison-stdiod \ | ||
| "run: cargo install --path crates/edison-stdiod (or re-run with --install-deps)" \ | ||
| cargo install --path "$stdiod_src" |
There was a problem hiding this comment.
P2: The one-command install --install-deps --yes flow still fails on machines without Rust because Cargo is neither provisioned nor checked with a usable setup fix. Handle cargo as a prerequisite before attempting this install.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 217:
<comment>The one-command `install --install-deps --yes` flow still fails on machines without Rust because Cargo is neither provisioned nor checked with a usable setup fix. Handle `cargo` as a prerequisite before attempting this install.</comment>
<file context>
@@ -0,0 +1,747 @@
+ brew install --quiet node
+ ensure_tool edison-stdiod \
+ "run: cargo install --path crates/edison-stdiod (or re-run with --install-deps)" \
+ cargo install --path "$stdiod_src"
+ if [ "$DRY_RUN" -eq 1 ]; then
+ info "deps: preview only (nothing was installed)"
</file context>
| local code | ||
| code="$(curl -s -o /dev/null -w '%{http_code}' -m 60 --connect-timeout 5 -X POST "$url" \ | ||
| -H "Authorization: Bearer ${EW_API_KEY}" -H "Content-Type: application/json" \ | ||
| --data "$(printf '{"env":{"BEEPER_ACCESS_TOKEN":"%s"}}' "$tok")" 2>/dev/null || true)" |
There was a problem hiding this comment.
P2: Tokens containing JSON-special characters cannot be bound reliably because the request body is assembled with raw string interpolation. Serialize BEEPER_ACCESS_TOKEN with a JSON encoder rather than printf substitution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 624:
<comment>Tokens containing JSON-special characters cannot be bound reliably because the request body is assembled with raw string interpolation. Serialize `BEEPER_ACCESS_TOKEN` with a JSON encoder rather than `printf` substitution.</comment>
<file context>
@@ -0,0 +1,747 @@
+ local code
+ code="$(curl -s -o /dev/null -w '%{http_code}' -m 60 --connect-timeout 5 -X POST "$url" \
+ -H "Authorization: Bearer ${EW_API_KEY}" -H "Content-Type: application/json" \
+ --data "$(printf '{"env":{"BEEPER_ACCESS_TOKEN":"%s"}}' "$tok")" 2>/dev/null || true)"
+ [ -z "$code" ] && code="000"
+ case "$code" in
</file context>
| --command npx --arg=-y --arg="$MCP_PKG" 2>&1)" && rc=0 || rc=$? | ||
| printf '%s\n' "$out" | grep -viE '^[[:space:]]*$' >&2 || true | ||
| if [ "$rc" -ne 0 ]; then | ||
| if printf '%s' "$out" | grep -qiE 'already (exists|submitted|pending)|duplicate'; then |
There was a problem hiding this comment.
P2: Rerunning install while the beeper request is still pending can fail instead of recognizing the existing request. The duplicate detection searches the CLI output for response-body text that edison-stdiod deliberately redacts, so the pending-request path should be handled by an explicit status/list API or by exposing a reliable machine-readable result.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/stdiod/scripts/install-beeper.sh, line 479:
<comment>Rerunning `install` while the `beeper` request is still pending can fail instead of recognizing the existing request. The duplicate detection searches the CLI output for response-body text that `edison-stdiod` deliberately redacts, so the pending-request path should be handled by an explicit status/list API or by exposing a reliable machine-readable result.</comment>
<file context>
@@ -0,0 +1,747 @@
+ --command npx --arg=-y --arg="$MCP_PKG" 2>&1)" && rc=0 || rc=$?
+ printf '%s\n' "$out" | grep -viE '^[[:space:]]*$' >&2 || true
+ if [ "$rc" -ne 0 ]; then
+ if printf '%s' "$out" | grep -qiE 'already (exists|submitted|pending)|duplicate'; then
+ ok "a request for '$SERVER_NAME' is already pending; approve it in the dashboard"
+ return 0
</file context>
Real end-to-end test surfaced two things: - @beeper/desktop-mcp runs its `execute` tool (the main Beeper surface) in a local Deno sandbox, so Deno is a hard runtime dep. Add it to ensure_deps and doctor. - The prior "headless Beeper has no MCP, Desktop app required" note was wrong. @beeper/desktop-mcp is the MCP bridge over Beeper's local Client API, which a headless `beeper` server serves too (verified against a headless server on a real Mac). Correct the header and the beeper-reachability guidance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…JSON, dry-run) Fixes the substantive findings from the cubic review on the PR: - P1 security: restrict every URL that receives a Beeper bearer token to a loopback host. A hostile BEEPER_API_URL or a userinfo_endpoint injected into the well-known document could otherwise send each scraped token candidate to a remote server. New is_loopback_url guard; non-loopback overrides are ignored and a non-loopback userinfo_endpoint falls back to the local default. - An EW_BACKEND supplied via the environment now sets EW_BACKEND_SET, so resolve_backend no longer silently overrides it with the device's saved session (could point install/bind-token at the wrong environment). - JSON safety: --json output and the bind-token request body now escape interpolated values, so tokens/labels/backends containing quotes, backslashes, or newlines stay valid JSON. - --dry-run is treated as non-mutating: `uninstall --dry-run` previews instead of refusing, and `bind-token --dry-run` no longer requires an admin key before the preview branch. - doctor and status now report the Beeper "Client API" and mention the headless server option, matching the headless support added earlier. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011CRjwx1BeWkTiS97Acyjvx
What
Adds
crates/stdiod/scripts/install-beeper.sh, an agent-friendly installer that wires Beeper into the Edison Watch MCP gateway on macOS. It was developed in the now-archivedEdison-Watch/stdiodrepo (PR #15 there, frozen by the archive); this ports the final, reviewed version into the monorepo.What it does
Automates the Edison side end to end using the shipped device-authorization flow:
Subcommands:
install,doctor,status,token,bind-token,mcp-url,uninstall. Agent-friendly throughout: every input is a flag orUPPER_SNAKEenv var, plus--dry-run,--yes,--json,--verbose, TTY-aware colors, and fail-fast errors that print the exact fix.--demo/--releaseshortcuts, and with neither set it follows the backend the device is already authorized to.Steps that still need a human (printed as explicit
action:lines)Beeper serves MCP only from the Desktop app, and the device-auth server flow is HITL by design, so three steps are human-gated and the script guides each:
beeperserver once in the dashboard (Servers page / Overview). A "not verified" badge before the token is set is expected and does not block approval.bind-token --ew-api-key <admin-key>pushesBEEPER_ACCESS_TOKENvia the admin/envroute.Upstream blocker:
beeper_execute(does NOT block this PR)The full Edison chain is proven end to end on a real Mac: device auth -> server approval -> token bind -> gateway proxy ->
@beeper/desktop-mcploads, andbeeper_search_docsreturns live data. The one tool that does not work is Beeper's ownbeeper_execute, and the cause is entirely upstream:@beeper/desktop-mcp5.0.0 spawns a Deno subprocess forexecutethat opens a Unix-socket control channel viaDeno.serve()but launches Deno without--allow-net. On Deno 2.9.x this throwsNotCapable, so the sandbox dies before any code runs.fix(mcp): remove Stainless sandbox execution modein their 5.0.0 release).Nothing on the Edison side needs to change for this; the installer and gateway path are complete. This is noted so reviewers know the
executelimitation is an upstream Beeper bug, not a gap in this script.Known gaps surfaced during real-device testing (not fixed here)
server adddeclares no env, so the dashboard shows no field forBEEPER_ACCESS_TOKEN;bind-tokenpushes it viaPOST /servers/{name}/envinstead. A cleaner fix would letserver adddeclare env-var names so the value has a dashboard field.@beeper/desktop-mcptargets:23373with no base-URL override, so Beeper Desktop must be on the default port (a stray headlessbeeperserver can bump it to:23374).Test plan
crates/stdiod/scripts/check_ai_writing.shpassesshellcheckclean (bar one intentional[ ] && ... || trueidiom)bash -n;install --dry-runruns end to end and exits 0; flag guards,--demo, and backend auto-follow exercisedinstallsubmit the server; approval +bind-tokenverified end to end; gateway proxies to@beeper/desktop-mcpandbeeper_search_docsreturns live databeeper_executeblocked upstream by execute fails on Deno 2.9.x: NotCapable net access to unix control socket (Deno.serve spawned without --allow-net) beeper/desktop-api-js#42 (not an Edison-side issue)🤖 Generated with Claude Code
Generated by Claude Code