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

@@ -1,6 +1,7 @@
import axios, { type AxiosError } from 'axios'
import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js'
import { logForDebugging } from '../../utils/debug.js'
import { rcLog } from '../../bridge/rcDebugLog.js'
import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js'
import { getSessionIngressAuthToken } from '../../utils/sessionIngressAuth.js'
import { SerialBatchEventUploader } from './SerialBatchEventUploader.js'
@@ -241,6 +242,10 @@ export class HybridTransport extends WebSocketTransport {
response.status < 500 &&
response.status !== 429
) {
rcLog(
`Hybrid POST ${response.status}: url=${this.postUrl.replace(/token=[^&]+/, 'token=***')}` +
` events=${events.length} body=${JSON.stringify(response.data).slice(0, 200)}`,
)
logForDebugging(
`HybridTransport: POST returned ${response.status} (permanent), dropping`,
)

View File

@@ -1,6 +1,7 @@
import axios, { type AxiosError } from 'axios'
import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js'
import { logForDebugging } from '../../utils/debug.js'
import { rcLog } from '../../bridge/rcDebugLog.js'
import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js'
import { errorMessage } from '../../utils/errors.js'
import { getSessionIngressAuthHeaders } from '../../utils/sessionIngressAuth.js'
@@ -468,6 +469,12 @@ export class SSETransport implements Transport {
* Handle connection errors with exponential backoff and time budget.
*/
private handleConnectionError(): void {
rcLog(
`SSE handleConnectionError: state=${this.state}` +
` lastSeqNum=${this.getLastSequenceNum()}` +
` reconnectAttempts=${this.reconnectAttempts}` +
` msSinceLastActivity=${this.lastActivityTime > 0 ? Date.now() - this.lastActivityTime : -1}`,
)
this.clearLivenessTimer()
if (this.state === 'closing' || this.state === 'closed') return
@@ -541,6 +548,11 @@ export class SSETransport implements Transport {
*/
private readonly onLivenessTimeout = (): void => {
this.livenessTimer = null
rcLog(
`SSE liveness timeout (${LIVENESS_TIMEOUT_MS}ms)` +
` lastSeqNum=${this.getLastSequenceNum()}` +
` state=${this.state}`,
)
logForDebugging('SSETransport: Liveness timeout, reconnecting', {
level: 'error',
})

View File

@@ -3,6 +3,7 @@ import type WsWebSocket from 'ws'
import { logEvent } from '../../services/analytics/index.js'
import { CircularBuffer } from '../../utils/CircularBuffer.js'
import { logForDebugging } from '../../utils/debug.js'
import { rcLog } from '../../bridge/rcDebugLog.js'
import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { getWebSocketTLSOptions } from '../../utils/mtls.js'
@@ -25,7 +26,7 @@ const DEFAULT_MAX_RECONNECT_DELAY = 30000
/** Time budget for reconnection attempts before giving up (10 minutes). */
const DEFAULT_RECONNECT_GIVE_UP_MS = 600_000
const DEFAULT_PING_INTERVAL = 10000
const DEFAULT_KEEPALIVE_INTERVAL = 300_000 // 5 minutes
const DEFAULT_KEEPALIVE_INTERVAL = 120_000 // 2 minutes — must be under Bun's 255s idleTimeout
/**
* Threshold for detecting system sleep/wake. If the gap between consecutive
@@ -395,6 +396,13 @@ export class WebSocketTransport implements Transport {
}
private handleConnectionError(closeCode?: number): void {
rcLog(
`WS handleConnectionError: code=${closeCode}` +
` state=${this.state}` +
` url=${this.url.href.replace(/token=[^&]+/, 'token=***')}` +
` msSinceLastActivity=${this.lastActivityTime > 0 ? Date.now() - this.lastActivityTime : -1}` +
` reconnectAttempts=${this.reconnectAttempts}`,
)
logForDebugging(
`WebSocketTransport: Disconnected from ${this.url.href}` +
(closeCode != null ? ` (code ${closeCode})` : ''),