Skip to content

feat: enable Remote Control (BRIDGE_MODE) with stub completions#60

Open
amDosion wants to merge 5 commits intoclaude-code-best:mainfrom
amDosion:feat/rc-clean
Open

feat: enable Remote Control (BRIDGE_MODE) with stub completions#60
amDosion wants to merge 5 commits intoclaude-code-best:mainfrom
amDosion:feat/rc-clean

Conversation

@amDosion
Copy link
Copy Markdown

@amDosion amDosion commented Apr 2, 2026

Summary

Enable the Remote Control feature (BRIDGE_MODE) by completing stub implementations, fixing type definitions, and correcting feature gate defaults.

Remote Control registers the local CLI as a bridge environment, generates a shareable URL (https://claude.ai/code/session_xxx), and allows real-time two-way communication from browser, mobile, or other devices.

Changes

File Change
scripts/dev.ts Add BRIDGE_MODE to DEFAULT_FEATURES
src/bridge/peerSessions.ts Stub → full implementation: cross-session peer messaging via bridge API
src/bridge/webhookSanitizer.ts Stub → full implementation: redact 8 secret patterns from webhook payloads
src/entrypoints/sdk/controlTypes.ts 12 any stubs → z.infer types from existing Zod schemas
src/hooks/useReplBridge.tsx tengu_bridge_system_init default falsetrue
DEV-LOG.md Add development log entry for this feature

Security

peerSessions.ts — Three-layer target validation

  1. target.trim() — normalize input
  2. validateBridgeId() — allowlist /^[a-zA-Z0-9_-]+$/ (same pattern as bridgeApi.ts)
  3. encodeURIComponent() — URL encode before path interpolation

peerSessions.ts — Authentication

  • Uses getBridgeAccessToken() for OAuth Bearer token (matches bridgeApi.ts / codeSessionApi.ts pattern)

webhookSanitizer.ts — Fail-closed design

  • Redact before truncate (prevents secret split at 100KB boundary)
  • On error: return [webhook content redacted due to sanitization error] (never raw content)
  • 8 regex patterns: GitHub PAT/OAuth/App tokens, Anthropic keys, Bearer tokens, AWS keys/secrets, generic API keys, npm tokens, Slack tokens

Key Decisions

  1. tengu_bridge_system_init default change: Anthropic pushes true via GrowthBook to subscribers, but our build doesn't receive the push. Without this, app stays stuck on "Remote Control connecting..." instead of showing "active"
  2. controlTypes.ts: Schemas already exist in controlSchemas.ts / coreSchemas.ts — types now use z.infer<ReturnType<typeof XxxSchema>> instead of any
  3. No existing code logic changed — only stubs completed, defaults corrected, compile switch enabled

Commits

  1. 74e51e7 feat: enable Remote Control (BRIDGE_MODE) with stub completions
  2. 1d38eae fix: address CodeRabbit review findings
  3. 8645d37 fix: add Authorization header to peer message requests
  4. e784f23 fix: validate and encode target sessionId in peer messages
  5. 67caa5d docs: add Remote Control (BRIDGE_MODE) entry to DEV-LOG

Test plan

  • bun test src/bridge/__tests__/ — 37 pass, 0 fail
  • /remote-control command visible and enabled in bun run dev
  • CLI connects to Anthropic CCR, generates shareable URL
  • App (claude.ai/code) shows "Remote Control active"
  • Mobile (Claude iOS app) connects via URL, two-way messaging works
  • Disconnect and reconnect cycle works correctly

🤖 Generated with Claude Code

- Add BRIDGE_MODE to DEFAULT_FEATURES in dev.ts
- Implement peerSessions.ts: cross-session messaging via bridge API
- Implement webhookSanitizer.ts: redact secrets from webhook payloads
- Replace any stubs in controlTypes.ts with Zod schema-inferred types
- Fix tengu_bridge_system_init default to true for app "active" status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Implements real bridge peer messaging and inbound webhook sanitization, tightens SDK control protocol types to schema-derived TypeScript, adds BRIDGE_MODE to dev defaults, and enables the tengu_bridge_system_init feature by default.

Changes

Cohort / File(s) Summary
Bridge peer messaging
src/bridge/peerSessions.ts
Replaced stub with postInterClaudeMessage async implementation: fetches bridge handle (sender, ingress URL, token), trims/validates/compat-converts target id, POSTs { type: 'peer_message', from, content } to ingress with 10s timeout and validateStatus: s < 500; returns { ok: true } on 200/204, otherwise { ok: false, error }, catching exceptions.
Webhook sanitization
src/bridge/webhookSanitizer.ts
Added sanitizeInboundWebhookContent(content: string): string: returns early on falsy input, applies multiple regex-based redactions (resetting each regex lastIndex), truncates outputs >100KB with a marker, and catches errors to return a fixed redaction-error placeholder (never throws).
Control protocol types
src/entrypoints/sdk/controlTypes.ts
Replaced loose any/generic control types with schema-derived TypeScript types using z.infer<ReturnType<...Schema>> for ~12 exported control-related types.
Dev / feature flags
scripts/dev.ts, src/hooks/useReplBridge.tsx
Added BRIDGE_MODE to DEFAULT_FEATURES; changed tengu_bridge_system_init feature check to default true, causing the system-init path to run by default when feature value is absent.

Sequence Diagram

sequenceDiagram
    participant App as Calling Code
    participant PostFunc as postInterClaudeMessage()
    participant Bridge as Bridge Handle
    participant HTTP as axios
    participant Ingress as Ingress Endpoint

    App->>PostFunc: postInterClaudeMessage(target, message)
    PostFunc->>Bridge: fetch handle (get sender, ingress URL, token)
    alt no handle / missing token / empty target
        PostFunc-->>App: { ok: false, error }
    else
        PostFunc->>PostFunc: validate/convert target id
        PostFunc->>HTTP: POST ${ingressUrl}/v1/sessions/<encodedTarget>/messages (body, headers, timeout)
        HTTP->>Ingress: deliver peer_message
        Ingress-->>HTTP: response (200/204 or non-5xx)
        alt 200/204
            PostFunc-->>App: { ok: true }
        else
            PostFunc-->>App: { ok: false, error }
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through code, a bridge to mend,
Redacted secrets, messages I send.
Types aligned, no any in sight,
Features flipped on — the defaults light.
Hooray — a rabbit’s dev delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: enable Remote Control (BRIDGE_MODE) with stub completions' accurately describes the main change: enabling BRIDGE_MODE and implementing stub completions for bridge functionality. It is specific, clear, and directly related to the primary objectives of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/rc-clean

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/bridge/peerSessions.ts (2)

1-5: Consider using src/ path alias for imports.

As per coding guidelines, imports should use the src/ path alias via tsconfig mapping instead of relative paths. However, this appears to be a common pattern in this codebase for same-package imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/bridge/peerSessions.ts` around lines 1 - 5, The imports at the top of
peerSessions.ts use relative paths (axios and local modules like
logForDebugging, errorMessage, getReplBridgeHandle, toCompatSessionId); update
these to use the project's src/ path alias (e.g., import { logForDebugging }
from 'src/utils/debug' etc.) so TS path mapping is respected, keeping the same
named symbols (logForDebugging, errorMessage, getReplBridgeHandle,
toCompatSessionId) and ensuring imports resolve under tsconfig paths.

18-21: Consider using a discriminated union for better type safety.

The return type { ok: boolean; error?: string } permits invalid states (e.g., { ok: true, error: '...' }). A discriminated union ensures callers handle both cases correctly.

🔧 Suggested type refinement
 export async function postInterClaudeMessage(
   target: string,
   message: string,
-): Promise<{ ok: boolean; error?: string }> {
+): Promise<{ ok: true } | { ok: false; error: string }> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/bridge/peerSessions.ts` around lines 18 - 21, The return type of
postInterClaudeMessage allows invalid states like { ok: true, error: '...' };
change its signature to a discriminated union (e.g., Promise<{ ok: true } | {
ok: false; error: string }>) and update all return sites inside
postInterClaudeMessage to return exactly one of those shapes (success returns {
ok: true } and all failure paths return { ok: false, error: <string> }). Also
export or define a named type (e.g., PostInterClaudeResult) if used elsewhere to
keep callers type-safe and update any callers to handle both union branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/bridge/webhookSanitizer.ts`:
- Line 8: The sanitizer currently "fails open" by returning the original payload
on error; change sanitizeWebhook (and the same logic around lines 53-55) so that
it never returns the raw input when an internal error occurs — instead catch any
exception synchronously and return a fully redacted/placeholder payload (or an
empty safe structure) that preserves the expected return type, and optionally
log the error; ensure sanitizeWebhook remains synchronous and never throws, and
update all paths that currently return the original content on error to return
the safe redacted value instead.
- Around line 40-50: The code currently truncates the payload (sanitized =
sanitized.slice(...)) before applying SECRET_PATTERNS, which can break regex
matches that span the truncation boundary; run the redaction loop over
SECRET_PATTERNS on the full sanitized input first (ensure pattern.lastIndex = 0
before each replace), then apply the MAX_CONTENT_LENGTH truncation step
afterwards (optionally reset lastIndex and run one final pass if you want to be
extra safe). Update the order around the variables sanitized, SECRET_PATTERNS,
and MAX_CONTENT_LENGTH in webhookSanitizer.ts accordingly.

---

Nitpick comments:
In `@src/bridge/peerSessions.ts`:
- Around line 1-5: The imports at the top of peerSessions.ts use relative paths
(axios and local modules like logForDebugging, errorMessage,
getReplBridgeHandle, toCompatSessionId); update these to use the project's src/
path alias (e.g., import { logForDebugging } from 'src/utils/debug' etc.) so TS
path mapping is respected, keeping the same named symbols (logForDebugging,
errorMessage, getReplBridgeHandle, toCompatSessionId) and ensuring imports
resolve under tsconfig paths.
- Around line 18-21: The return type of postInterClaudeMessage allows invalid
states like { ok: true, error: '...' }; change its signature to a discriminated
union (e.g., Promise<{ ok: true } | { ok: false; error: string }>) and update
all return sites inside postInterClaudeMessage to return exactly one of those
shapes (success returns { ok: true } and all failure paths return { ok: false,
error: <string> }). Also export or define a named type (e.g.,
PostInterClaudeResult) if used elsewhere to keep callers type-safe and update
any callers to handle both union branches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0a1f7a5-70c7-4be8-a7f9-2662dc84fec8

📥 Commits

Reviewing files that changed from the base of the PR and between 1f0a2e4 and 74e51e7.

📒 Files selected for processing (5)
  • scripts/dev.ts
  • src/bridge/peerSessions.ts
  • src/bridge/webhookSanitizer.ts
  • src/entrypoints/sdk/controlTypes.ts
  • src/hooks/useReplBridge.tsx

- webhookSanitizer: redact before truncate to avoid split secrets at boundary
- webhookSanitizer: return safe placeholder on error instead of raw content
- peerSessions: use discriminated union return type for type safety

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/bridge/peerSessions.ts (1)

1-5: Use src/ path alias instead of relative imports.

The imports use relative paths (../utils/..., ./...) instead of the src/ path alias configured in tsconfig.

♻️ Suggested refactor
 import axios from 'axios'
-import { logForDebugging } from '../utils/debug.js'
-import { errorMessage } from '../utils/errors.js'
-import { getReplBridgeHandle } from './replBridgeHandle.js'
-import { toCompatSessionId } from './sessionIdCompat.js'
+import { logForDebugging } from 'src/utils/debug.js'
+import { errorMessage } from 'src/utils/errors.js'
+import { getReplBridgeHandle } from 'src/bridge/replBridgeHandle.js'
+import { toCompatSessionId } from 'src/bridge/sessionIdCompat.js'

As per coding guidelines: "Import src/ path alias via tsconfig mapping instead of relative paths in imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/bridge/peerSessions.ts` around lines 1 - 5, Update the imports in
peerSessions.ts to use the tsconfig "src/" path alias instead of relative paths:
replace "../utils/debug.js" import of logForDebugging, "../utils/errors.js"
import of errorMessage, "./replBridgeHandle.js" import of getReplBridgeHandle,
and "./sessionIdCompat.js" import of toCompatSessionId with equivalent "src/..."
aliased imports; ensure the module specifiers match the tsconfig paths (and keep
the default axios import unchanged) so TypeScript path mapping resolves
correctly and local build/IDE tooling recognizes the alias.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/bridge/peerSessions.ts`:
- Around line 38-53: The POST to the peer message endpoint in
postInterClaudeMessage is missing an Authorization header because
ReplBridgeHandle doesn't expose the access token; fix by either adding
getAccessToken to the ReplBridgeHandle interface and using it inside
postInterClaudeMessage or by changing postInterClaudeMessage signature to accept
an accessToken parameter, then include "Authorization: Bearer <token>" in the
axios headers alongside Content-Type and anthropic-version; mirror how other
bridge calls (see remoteBridgeCore.ts / codeSessionApi.ts / bridgeApi.ts) attach
the Bearer token so this request is authenticated.

---

Nitpick comments:
In `@src/bridge/peerSessions.ts`:
- Around line 1-5: Update the imports in peerSessions.ts to use the tsconfig
"src/" path alias instead of relative paths: replace "../utils/debug.js" import
of logForDebugging, "../utils/errors.js" import of errorMessage,
"./replBridgeHandle.js" import of getReplBridgeHandle, and
"./sessionIdCompat.js" import of toCompatSessionId with equivalent "src/..."
aliased imports; ensure the module specifiers match the tsconfig paths (and keep
the default axios import unchanged) so TypeScript path mapping resolves
correctly and local build/IDE tooling recognizes the alias.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cff0d28c-d025-4836-b89b-c094fff9242f

📥 Commits

Reviewing files that changed from the base of the PR and between 74e51e7 and 1d38eae.

📒 Files selected for processing (2)
  • src/bridge/peerSessions.ts
  • src/bridge/webhookSanitizer.ts

getBridgeAccessToken() provides the OAuth Bearer token, matching
the auth pattern used by bridgeApi.ts and codeSessionApi.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/bridge/peerSessions.ts (1)

2-6: Use src/ path aliases instead of relative imports

These imports should use the configured src/ alias for consistency with repo standards.

Suggested fix
-import { logForDebugging } from '../utils/debug.js'
-import { errorMessage } from '../utils/errors.js'
-import { getBridgeAccessToken } from './bridgeConfig.js'
-import { getReplBridgeHandle } from './replBridgeHandle.js'
-import { toCompatSessionId } from './sessionIdCompat.js'
+import { logForDebugging } from 'src/utils/debug.js'
+import { errorMessage } from 'src/utils/errors.js'
+import { getBridgeAccessToken } from 'src/bridge/bridgeConfig.js'
+import { getReplBridgeHandle } from 'src/bridge/replBridgeHandle.js'
+import { toCompatSessionId } from 'src/bridge/sessionIdCompat.js'

As per coding guidelines, "Import src/ path alias via tsconfig mapping instead of relative paths in imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/bridge/peerSessions.ts` around lines 2 - 6, Replace the relative imports
at the top of peerSessions.ts with the project alias imports using the src/ path
mapping: update import statements that reference logForDebugging, errorMessage,
getBridgeAccessToken, getReplBridgeHandle, and toCompatSessionId so they import
from "src/utils/debug", "src/utils/errors", "src/bridge/bridgeConfig",
"src/bridge/replBridgeHandle", and "src/bridge/sessionIdCompat" respectively
(preserving the same exported identifiers) to align with the tsconfig path alias
convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/bridge/peerSessions.ts`:
- Around line 29-43: The URL path is built with compatTarget unescaped which can
break routing for reserved chars or slashes; update the code that computes
compatTarget (use the existing toCompatSessionId(target)) and then normalize and
percent-encode it before interpolating into url (e.g., trim/normalize any
leading/trailing slashes and apply an encodeURIComponent-style encoding) so the
constructed url =
`${handle.sessionIngressUrl}/v1/sessions/${encodedCompatTarget}/messages` is
safe; ensure you reference toCompatSessionId, compatTarget (or its encoded
variant) and handle.sessionIngressUrl when making the change.

---

Nitpick comments:
In `@src/bridge/peerSessions.ts`:
- Around line 2-6: Replace the relative imports at the top of peerSessions.ts
with the project alias imports using the src/ path mapping: update import
statements that reference logForDebugging, errorMessage, getBridgeAccessToken,
getReplBridgeHandle, and toCompatSessionId so they import from
"src/utils/debug", "src/utils/errors", "src/bridge/bridgeConfig",
"src/bridge/replBridgeHandle", and "src/bridge/sessionIdCompat" respectively
(preserving the same exported identifiers) to align with the tsconfig path alias
convention.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 905ffc73-8b03-4f9d-ac5d-9cf7df50215b

📥 Commits

Reviewing files that changed from the base of the PR and between 1d38eae and 8645d37.

📒 Files selected for processing (1)
  • src/bridge/peerSessions.ts

- Trim and normalize target before use
- Validate with validateBridgeId allowlist (same as bridgeApi.ts)
- URL-encode compatTarget to prevent path traversal/injection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude-code-best
Copy link
Copy Markdown
Owner

麻烦补充一下变更到 DEV-LOG.md 文件, 然后需要测试的截图哈 @amDosion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@DEV-LOG.md`:
- Line 33: The markdown references a missing image
"docs/images/remote-control-mobile.png" in DEV-LOG.md; fix by either adding the
requested screenshot file named remote-control-mobile.png into the docs/images/
directory (use the original "测试的截图" image) or remove/replace the markdown image
line `![Remote Control on Mobile](docs/images/remote-control-mobile.png)` from
DEV-LOG.md so the broken reference is eliminated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb2c4e3a-fe8e-4636-b33f-15d3a136661d

📥 Commits

Reviewing files that changed from the base of the PR and between e784f23 and 67caa5d.

📒 Files selected for processing (1)
  • DEV-LOG.md

- App 端(claude.ai/code)显示 "Remote Control active"
- 手机端(Claude iOS app)通过 URL 连接,双向消息正常

![Remote Control on Mobile](docs/images/remote-control-mobile.png)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify the referenced image exists and check for related test screenshots

# Check if the referenced image exists
fd -t f "remote-control-mobile.png" docs/images/

# List all images in docs/images to see what test screenshots are available
echo "=== All images in docs/images/ ==="
fd -t f . docs/images/ --exec ls -lh {}

Repository: claude-code-best/claude-code

Length of output: 839


Add the missing screenshot or remove the broken image reference.

The documentation at line 33 references docs/images/remote-control-mobile.png, but this file does not exist in the repository. Either add the screenshot to docs/images/ as requested in the original review ("测试的截图"), or remove the broken image reference from the documentation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DEV-LOG.md` at line 33, The markdown references a missing image
"docs/images/remote-control-mobile.png" in DEV-LOG.md; fix by either adding the
requested screenshot file named remote-control-mobile.png into the docs/images/
directory (use the original "测试的截图" image) or remove/replace the markdown image
line `![Remote Control on Mobile](docs/images/remote-control-mobile.png)` from
DEV-LOG.md so the broken reference is eliminated.

@amDosion
Copy link
Copy Markdown
Author

amDosion commented Apr 3, 2026

QQ20260403-113348 截屏 2026-04-03 上午11 33 13(1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants