feat: 支持自托管的 remote-control-server (#214)

* feat: 支持自托管的 remote-control-server (#214)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
claude-code-best
2026-04-09 17:40:50 +08:00
committed by GitHub
parent f17b7c7163
commit 2da6514095
81 changed files with 9875 additions and 40 deletions

View File

@@ -4,6 +4,7 @@ import {
getDynamicConfig_CACHED_MAY_BE_STALE,
getFeatureValue_CACHED_MAY_BE_STALE,
} from '../services/analytics/growthbook.js'
import { isSelfHostedBridge } from './bridgeConfig.js'
// Namespace import breaks the bridgeEnabled → auth → config → bridgeEnabled
// cycle — authModule.foo is a live binding, so by the time the helpers below
// call it, auth.js is fully loaded. Previously used require() for the same
@@ -26,6 +27,11 @@ import { lt } from '../utils/semver.js'
* is only referenced when bridge mode is enabled at build time.
*/
export function isBridgeEnabled(): boolean {
// Self-hosted bridge: when the user has configured a custom server, bypass
// GrowthBook gates entirely.
if (feature('BRIDGE_MODE') && isSelfHostedBridge()) {
return true
}
// Positive ternary pattern — see docs/feature-gating.md.
// Negative pattern (if (!feature(...)) return) does not eliminate
// inline string literals from external builds.
@@ -48,6 +54,9 @@ export function isBridgeEnabled(): boolean {
* `isBridgeEnabled()` instead.
*/
export async function isBridgeEnabledBlocking(): Promise<boolean> {
if (feature('BRIDGE_MODE') && isSelfHostedBridge()) {
return true
}
return feature('BRIDGE_MODE')
? isClaudeAISubscriber() &&
(await checkGate_CACHED_OR_BLOCKING('tengu_ccr_bridge'))
@@ -69,6 +78,10 @@ export async function isBridgeEnabledBlocking(): Promise<boolean> {
*/
export async function getBridgeDisabledReason(): Promise<string | null> {
if (feature('BRIDGE_MODE')) {
// Self-hosted bridge: no subscription/scope/gate checks needed.
if (isSelfHostedBridge()) {
return null
}
if (!isClaudeAISubscriber()) {
return 'Remote Control requires a claude.ai subscription. Run `claude auth login` to sign in with your claude.ai account.'
}