mirror of
https://github.com/sanbuphy/claude-code-source-code.git
synced 2026-04-03 11:34:54 +08:00
从 npm 包 @anthropic-ai/claude-code 2.1.88 版本提取的反编译源码 包含 src/ 目录下的 TypeScript 源文件及 vendor/ 原生模块源码 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
203 lines
8.2 KiB
TypeScript
203 lines
8.2 KiB
TypeScript
import { feature } from 'bun:bundle'
|
|
import {
|
|
checkGate_CACHED_OR_BLOCKING,
|
|
getDynamicConfig_CACHED_MAY_BE_STALE,
|
|
getFeatureValue_CACHED_MAY_BE_STALE,
|
|
} from '../services/analytics/growthbook.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
|
|
// deferral, but require() hits a CJS cache that diverges from the ESM
|
|
// namespace after mock.module() (daemon/auth.test.ts), breaking spyOn.
|
|
import * as authModule from '../utils/auth.js'
|
|
import { isEnvTruthy } from '../utils/envUtils.js'
|
|
import { lt } from '../utils/semver.js'
|
|
|
|
/**
|
|
* Runtime check for bridge mode entitlement.
|
|
*
|
|
* Remote Control requires a claude.ai subscription (the bridge auths to CCR
|
|
* with the claude.ai OAuth token). isClaudeAISubscriber() excludes
|
|
* Bedrock/Vertex/Foundry, apiKeyHelper/gateway deployments, env-var API keys,
|
|
* and Console API logins — none of which have the OAuth token CCR needs.
|
|
* See github.com/deshaw/anthropic-issues/issues/24.
|
|
*
|
|
* The `feature('BRIDGE_MODE')` guard ensures the GrowthBook string literal
|
|
* is only referenced when bridge mode is enabled at build time.
|
|
*/
|
|
export function isBridgeEnabled(): boolean {
|
|
// Positive ternary pattern — see docs/feature-gating.md.
|
|
// Negative pattern (if (!feature(...)) return) does not eliminate
|
|
// inline string literals from external builds.
|
|
return feature('BRIDGE_MODE')
|
|
? isClaudeAISubscriber() &&
|
|
getFeatureValue_CACHED_MAY_BE_STALE('tengu_ccr_bridge', false)
|
|
: false
|
|
}
|
|
|
|
/**
|
|
* Blocking entitlement check for Remote Control.
|
|
*
|
|
* Returns cached `true` immediately (fast path). If the disk cache says
|
|
* `false` or is missing, awaits GrowthBook init and fetches the fresh
|
|
* server value (slow path, max ~5s), then writes it to disk.
|
|
*
|
|
* Use at entitlement gates where a stale `false` would unfairly block access.
|
|
* For user-facing error paths, prefer `getBridgeDisabledReason()` which gives
|
|
* a specific diagnostic. For render-body UI visibility checks, use
|
|
* `isBridgeEnabled()` instead.
|
|
*/
|
|
export async function isBridgeEnabledBlocking(): Promise<boolean> {
|
|
return feature('BRIDGE_MODE')
|
|
? isClaudeAISubscriber() &&
|
|
(await checkGate_CACHED_OR_BLOCKING('tengu_ccr_bridge'))
|
|
: false
|
|
}
|
|
|
|
/**
|
|
* Diagnostic message for why Remote Control is unavailable, or null if
|
|
* it's enabled. Call this instead of a bare `isBridgeEnabledBlocking()`
|
|
* check when you need to show the user an actionable error.
|
|
*
|
|
* The GrowthBook gate targets on organizationUUID, which comes from
|
|
* config.oauthAccount — populated by /api/oauth/profile during login.
|
|
* That endpoint requires the user:profile scope. Tokens without it
|
|
* (setup-token, CLAUDE_CODE_OAUTH_TOKEN env var, or pre-scope-expansion
|
|
* logins) leave oauthAccount unpopulated, so the gate falls back to
|
|
* false and users see a dead-end "not enabled" message with no hint
|
|
* that re-login would fix it. See CC-1165 / gh-33105.
|
|
*/
|
|
export async function getBridgeDisabledReason(): Promise<string | null> {
|
|
if (feature('BRIDGE_MODE')) {
|
|
if (!isClaudeAISubscriber()) {
|
|
return 'Remote Control requires a claude.ai subscription. Run `claude auth login` to sign in with your claude.ai account.'
|
|
}
|
|
if (!hasProfileScope()) {
|
|
return 'Remote Control requires a full-scope login token. Long-lived tokens (from `claude setup-token` or CLAUDE_CODE_OAUTH_TOKEN) are limited to inference-only for security reasons. Run `claude auth login` to use Remote Control.'
|
|
}
|
|
if (!getOauthAccountInfo()?.organizationUuid) {
|
|
return 'Unable to determine your organization for Remote Control eligibility. Run `claude auth login` to refresh your account information.'
|
|
}
|
|
if (!(await checkGate_CACHED_OR_BLOCKING('tengu_ccr_bridge'))) {
|
|
return 'Remote Control is not yet enabled for your account.'
|
|
}
|
|
return null
|
|
}
|
|
return 'Remote Control is not available in this build.'
|
|
}
|
|
|
|
// try/catch: main.tsx:5698 calls isBridgeEnabled() while defining the Commander
|
|
// program, before enableConfigs() runs. isClaudeAISubscriber() → getGlobalConfig()
|
|
// throws "Config accessed before allowed" there. Pre-config, no OAuth token can
|
|
// exist anyway — false is correct. Same swallow getFeatureValue_CACHED_MAY_BE_STALE
|
|
// already does at growthbook.ts:775-780.
|
|
function isClaudeAISubscriber(): boolean {
|
|
try {
|
|
return authModule.isClaudeAISubscriber()
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
function hasProfileScope(): boolean {
|
|
try {
|
|
return authModule.hasProfileScope()
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
function getOauthAccountInfo(): ReturnType<
|
|
typeof authModule.getOauthAccountInfo
|
|
> {
|
|
try {
|
|
return authModule.getOauthAccountInfo()
|
|
} catch {
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runtime check for the env-less (v2) REPL bridge path.
|
|
* Returns true when the GrowthBook flag `tengu_bridge_repl_v2` is enabled.
|
|
*
|
|
* This gates which implementation initReplBridge uses — NOT whether bridge
|
|
* is available at all (see isBridgeEnabled above). Daemon/print paths stay
|
|
* on the env-based implementation regardless of this gate.
|
|
*/
|
|
export function isEnvLessBridgeEnabled(): boolean {
|
|
return feature('BRIDGE_MODE')
|
|
? getFeatureValue_CACHED_MAY_BE_STALE('tengu_bridge_repl_v2', false)
|
|
: false
|
|
}
|
|
|
|
/**
|
|
* Kill-switch for the `cse_*` → `session_*` client-side retag shim.
|
|
*
|
|
* The shim exists because compat/convert.go:27 validates TagSession and the
|
|
* claude.ai frontend routes on `session_*`, while v2 worker endpoints hand out
|
|
* `cse_*`. Once the server tags by environment_kind and the frontend accepts
|
|
* `cse_*` directly, flip this to false to make toCompatSessionId a no-op.
|
|
* Defaults to true — the shim stays active until explicitly disabled.
|
|
*/
|
|
export function isCseShimEnabled(): boolean {
|
|
return feature('BRIDGE_MODE')
|
|
? getFeatureValue_CACHED_MAY_BE_STALE(
|
|
'tengu_bridge_repl_v2_cse_shim_enabled',
|
|
true,
|
|
)
|
|
: true
|
|
}
|
|
|
|
/**
|
|
* Returns an error message if the current CLI version is below the
|
|
* minimum required for the v1 (env-based) Remote Control path, or null if the
|
|
* version is fine. The v2 (env-less) path uses checkEnvLessBridgeMinVersion()
|
|
* in envLessBridgeConfig.ts instead — the two implementations have independent
|
|
* version floors.
|
|
*
|
|
* Uses cached (non-blocking) GrowthBook config. If GrowthBook hasn't
|
|
* loaded yet, the default '0.0.0' means the check passes — a safe fallback.
|
|
*/
|
|
export function checkBridgeMinVersion(): string | null {
|
|
// Positive pattern — see docs/feature-gating.md.
|
|
// Negative pattern (if (!feature(...)) return) does not eliminate
|
|
// inline string literals from external builds.
|
|
if (feature('BRIDGE_MODE')) {
|
|
const config = getDynamicConfig_CACHED_MAY_BE_STALE<{
|
|
minVersion: string
|
|
}>('tengu_bridge_min_version', { minVersion: '0.0.0' })
|
|
if (config.minVersion && lt(MACRO.VERSION, config.minVersion)) {
|
|
return `Your version of Claude Code (${MACRO.VERSION}) is too old for Remote Control.\nVersion ${config.minVersion} or higher is required. Run \`claude update\` to update.`
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Default for remoteControlAtStartup when the user hasn't explicitly set it.
|
|
* When the CCR_AUTO_CONNECT build flag is present (ant-only) and the
|
|
* tengu_cobalt_harbor GrowthBook gate is on, all sessions connect to CCR by
|
|
* default — the user can still opt out by setting remoteControlAtStartup=false
|
|
* in config (explicit settings always win over this default).
|
|
*
|
|
* Defined here rather than in config.ts to avoid a direct
|
|
* config.ts → growthbook.ts import cycle (growthbook.ts → user.ts → config.ts).
|
|
*/
|
|
export function getCcrAutoConnectDefault(): boolean {
|
|
return feature('CCR_AUTO_CONNECT')
|
|
? getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_harbor', false)
|
|
: false
|
|
}
|
|
|
|
/**
|
|
* Opt-in CCR mirror mode — every local session spawns an outbound-only
|
|
* Remote Control session that receives forwarded events. Separate from
|
|
* getCcrAutoConnectDefault (bidirectional Remote Control). Env var wins for
|
|
* local opt-in; GrowthBook controls rollout.
|
|
*/
|
|
export function isCcrMirrorEnabled(): boolean {
|
|
return feature('CCR_MIRROR')
|
|
? isEnvTruthy(process.env.CLAUDE_CODE_CCR_MIRROR) ||
|
|
getFeatureValue_CACHED_MAY_BE_STALE('tengu_ccr_mirror', false)
|
|
: false
|
|
}
|