Compare commits
2 Commits
1b4603ed3b
...
3e5461df9b
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e5461df9b | |||
| ce8f0dfd2b |
@@ -48,6 +48,7 @@ Removed in this repository:
|
|||||||
- Datadog analytics and Anthropic 1P event-logging egress.
|
- Datadog analytics and Anthropic 1P event-logging egress.
|
||||||
- GrowthBook remote evaluation/network fetches; local env/config overrides and cached values remain available for compatibility.
|
- GrowthBook remote evaluation/network fetches; local env/config overrides and cached values remain available for compatibility.
|
||||||
- OpenTelemetry initialization and event export paths.
|
- OpenTelemetry initialization and event export paths.
|
||||||
|
- Perfetto local trace-file output paths that could persist request/tool metadata to disk.
|
||||||
- Extra dead telemetry scaffolding tied to the removed egress paths, including startup/session analytics fanout, logout telemetry flush, and remote GrowthBook metadata collectors.
|
- Extra dead telemetry scaffolding tied to the removed egress paths, including startup/session analytics fanout, logout telemetry flush, and remote GrowthBook metadata collectors.
|
||||||
|
|
||||||
Still present:
|
Still present:
|
||||||
|
|||||||
@@ -23,16 +23,6 @@ type BridgeApiDeps = {
|
|||||||
* tokens don't refresh, so 401 goes straight to BridgeFatalError.
|
* tokens don't refresh, so 401 goes straight to BridgeFatalError.
|
||||||
*/
|
*/
|
||||||
onAuth401?: (staleAccessToken: string) => Promise<boolean>
|
onAuth401?: (staleAccessToken: string) => Promise<boolean>
|
||||||
/**
|
|
||||||
* Returns the trusted device token to send as X-Trusted-Device-Token on
|
|
||||||
* bridge API calls. Bridge sessions have SecurityTier=ELEVATED on the
|
|
||||||
* server (CCR v2); when the server's enforcement flag is on,
|
|
||||||
* ConnectBridgeWorker requires a trusted device at JWT-issuance.
|
|
||||||
* Optional — when absent or returning undefined, the header is omitted
|
|
||||||
* and the server falls through to its flag-off/no-op path. The CLI-side
|
|
||||||
* gate is tengu_sessions_elevated_auth_enforcement (see trustedDevice.ts).
|
|
||||||
*/
|
|
||||||
getTrustedDeviceToken?: () => string | undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BETA_HEADER = 'environments-2025-11-01'
|
const BETA_HEADER = 'environments-2025-11-01'
|
||||||
@@ -74,18 +64,13 @@ export function createBridgeApiClient(deps: BridgeApiDeps): BridgeApiClient {
|
|||||||
const EMPTY_POLL_LOG_INTERVAL = 100
|
const EMPTY_POLL_LOG_INTERVAL = 100
|
||||||
|
|
||||||
function getHeaders(accessToken: string): Record<string, string> {
|
function getHeaders(accessToken: string): Record<string, string> {
|
||||||
const headers: Record<string, string> = {
|
return {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'anthropic-version': '2023-06-01',
|
'anthropic-version': '2023-06-01',
|
||||||
'anthropic-beta': BETA_HEADER,
|
'anthropic-beta': BETA_HEADER,
|
||||||
'x-environment-runner-version': deps.runnerVersion,
|
'x-environment-runner-version': deps.runnerVersion,
|
||||||
}
|
}
|
||||||
const deviceToken = deps.getTrustedDeviceToken?.()
|
|
||||||
if (deviceToken) {
|
|
||||||
headers['X-Trusted-Device-Token'] = deviceToken
|
|
||||||
}
|
|
||||||
return headers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveAuth(): string {
|
function resolveAuth(): string {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import { createTokenRefreshScheduler } from './jwtUtils.js'
|
|||||||
import { getPollIntervalConfig } from './pollConfig.js'
|
import { getPollIntervalConfig } from './pollConfig.js'
|
||||||
import { toCompatSessionId, toInfraSessionId } from './sessionIdCompat.js'
|
import { toCompatSessionId, toInfraSessionId } from './sessionIdCompat.js'
|
||||||
import { createSessionSpawner, safeFilenameId } from './sessionRunner.js'
|
import { createSessionSpawner, safeFilenameId } from './sessionRunner.js'
|
||||||
import { getTrustedDeviceToken } from './trustedDevice.js'
|
|
||||||
import {
|
import {
|
||||||
BRIDGE_LOGIN_ERROR,
|
BRIDGE_LOGIN_ERROR,
|
||||||
type BridgeApiClient,
|
type BridgeApiClient,
|
||||||
@@ -2344,7 +2343,6 @@ export async function bridgeMain(args: string[]): Promise<void> {
|
|||||||
runnerVersion: MACRO.VERSION,
|
runnerVersion: MACRO.VERSION,
|
||||||
onDebug: logForDebugging,
|
onDebug: logForDebugging,
|
||||||
onAuth401: handleOAuth401Error,
|
onAuth401: handleOAuth401Error,
|
||||||
getTrustedDeviceToken,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// When resuming a session via --session-id, fetch it to learn its
|
// When resuming a session via --session-id, fetch it to learn its
|
||||||
@@ -2877,7 +2875,6 @@ export async function runBridgeHeadless(
|
|||||||
runnerVersion: MACRO.VERSION,
|
runnerVersion: MACRO.VERSION,
|
||||||
onDebug: log,
|
onDebug: log,
|
||||||
onAuth401: opts.onAuth401,
|
onAuth401: opts.onAuth401,
|
||||||
getTrustedDeviceToken,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let environmentId: string
|
let environmentId: string
|
||||||
|
|||||||
@@ -95,20 +95,15 @@ export async function fetchRemoteCredentials(
|
|||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
timeoutMs: number,
|
timeoutMs: number,
|
||||||
trustedDeviceToken?: string,
|
|
||||||
): Promise<RemoteCredentials | null> {
|
): Promise<RemoteCredentials | null> {
|
||||||
const url = `${baseUrl}/v1/code/sessions/${sessionId}/bridge`
|
const url = `${baseUrl}/v1/code/sessions/${sessionId}/bridge`
|
||||||
const headers = oauthHeaders(accessToken)
|
|
||||||
if (trustedDeviceToken) {
|
|
||||||
headers['X-Trusted-Device-Token'] = trustedDeviceToken
|
|
||||||
}
|
|
||||||
let response
|
let response
|
||||||
try {
|
try {
|
||||||
response = await axios.post(
|
response = await axios.post(
|
||||||
url,
|
url,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
headers,
|
headers: oauthHeaders(accessToken),
|
||||||
timeout: timeoutMs,
|
timeout: timeoutMs,
|
||||||
validateStatus: s => s < 500,
|
validateStatus: s => s < 500,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import { buildCCRv2SdkUrl } from './workSecret.js'
|
|||||||
import { toCompatSessionId } from './sessionIdCompat.js'
|
import { toCompatSessionId } from './sessionIdCompat.js'
|
||||||
import { FlushGate } from './flushGate.js'
|
import { FlushGate } from './flushGate.js'
|
||||||
import { createTokenRefreshScheduler } from './jwtUtils.js'
|
import { createTokenRefreshScheduler } from './jwtUtils.js'
|
||||||
import { getTrustedDeviceToken } from './trustedDevice.js'
|
|
||||||
import {
|
import {
|
||||||
getEnvLessBridgeConfig,
|
getEnvLessBridgeConfig,
|
||||||
type EnvLessBridgeConfig,
|
type EnvLessBridgeConfig,
|
||||||
@@ -925,9 +924,8 @@ import {
|
|||||||
} from './codeSessionApi.js'
|
} from './codeSessionApi.js'
|
||||||
import { getBridgeBaseUrlOverride } from './bridgeConfig.js'
|
import { getBridgeBaseUrlOverride } from './bridgeConfig.js'
|
||||||
|
|
||||||
// CLI-side wrapper that applies the CLAUDE_BRIDGE_BASE_URL dev override and
|
// CLI-side wrapper that applies the CLAUDE_BRIDGE_BASE_URL dev override while
|
||||||
// injects the trusted-device token (both are env/GrowthBook reads that the
|
// keeping the SDK-facing codeSessionApi.ts export free of CLI config reads.
|
||||||
// SDK-facing codeSessionApi.ts export must stay free of).
|
|
||||||
export async function fetchRemoteCredentials(
|
export async function fetchRemoteCredentials(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
@@ -939,7 +937,6 @@ export async function fetchRemoteCredentials(
|
|||||||
baseUrl,
|
baseUrl,
|
||||||
accessToken,
|
accessToken,
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
getTrustedDeviceToken(),
|
|
||||||
)
|
)
|
||||||
if (!creds) return null
|
if (!creds) return null
|
||||||
return getBridgeBaseUrlOverride()
|
return getBridgeBaseUrlOverride()
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import {
|
|||||||
} from './workSecret.js'
|
} from './workSecret.js'
|
||||||
import { toCompatSessionId, toInfraSessionId } from './sessionIdCompat.js'
|
import { toCompatSessionId, toInfraSessionId } from './sessionIdCompat.js'
|
||||||
import { updateSessionBridgeId } from '../utils/concurrentSessions.js'
|
import { updateSessionBridgeId } from '../utils/concurrentSessions.js'
|
||||||
import { getTrustedDeviceToken } from './trustedDevice.js'
|
|
||||||
import { HybridTransport } from '../cli/transports/HybridTransport.js'
|
import { HybridTransport } from '../cli/transports/HybridTransport.js'
|
||||||
import {
|
import {
|
||||||
type ReplBridgeTransport,
|
type ReplBridgeTransport,
|
||||||
@@ -314,7 +313,6 @@ export async function initBridgeCore(
|
|||||||
runnerVersion: MACRO.VERSION,
|
runnerVersion: MACRO.VERSION,
|
||||||
onDebug: logForDebugging,
|
onDebug: logForDebugging,
|
||||||
onAuth401,
|
onAuth401,
|
||||||
getTrustedDeviceToken,
|
|
||||||
})
|
})
|
||||||
// Ant-only: interpose so /bridge-kick can inject poll/register/heartbeat
|
// Ant-only: interpose so /bridge-kick can inject poll/register/heartbeat
|
||||||
// failures. Zero cost in external builds (rawApi passes through unchanged).
|
// failures. Zero cost in external builds (rawApi passes through unchanged).
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ export type ReplBridgeTransport = {
|
|||||||
* (user watches the REPL locally); multi-session worker callers do.
|
* (user watches the REPL locally); multi-session worker callers do.
|
||||||
*/
|
*/
|
||||||
reportState(state: SessionState): void
|
reportState(state: SessionState): void
|
||||||
/** PUT /worker external_metadata (v2 only; v1 is a no-op). */
|
|
||||||
reportMetadata(metadata: Record<string, unknown>): void
|
|
||||||
/**
|
/**
|
||||||
* POST /worker/events/{id}/delivery (v2 only; v1 is a no-op). Populates
|
* POST /worker/events/{id}/delivery (v2 only; v1 is a no-op). Populates
|
||||||
* CCR's processing_at/processed_at columns. `received` is auto-fired by
|
* CCR's processing_at/processed_at columns. `received` is auto-fired by
|
||||||
@@ -96,7 +94,6 @@ export function createV1ReplTransport(
|
|||||||
return hybrid.droppedBatchCount
|
return hybrid.droppedBatchCount
|
||||||
},
|
},
|
||||||
reportState: () => {},
|
reportState: () => {},
|
||||||
reportMetadata: () => {},
|
|
||||||
reportDelivery: () => {},
|
reportDelivery: () => {},
|
||||||
flush: () => Promise.resolve(),
|
flush: () => Promise.resolve(),
|
||||||
}
|
}
|
||||||
@@ -324,9 +321,6 @@ export async function createV2ReplTransport(opts: {
|
|||||||
reportState(state) {
|
reportState(state) {
|
||||||
ccr.reportState(state)
|
ccr.reportState(state)
|
||||||
},
|
},
|
||||||
reportMetadata(metadata) {
|
|
||||||
ccr.reportMetadata(metadata)
|
|
||||||
},
|
|
||||||
reportDelivery(eventId, status) {
|
reportDelivery(eventId, status) {
|
||||||
ccr.reportDelivery(eventId, status)
|
ccr.reportDelivery(eventId, status)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,35 +2,19 @@ import { logForDebugging } from '../utils/debug.js'
|
|||||||
import { getSecureStorage } from '../utils/secureStorage/index.js'
|
import { getSecureStorage } from '../utils/secureStorage/index.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trusted device token source for bridge (remote-control) sessions.
|
* Trusted-device compatibility helpers for bridge (remote-control) sessions.
|
||||||
*
|
*
|
||||||
* Bridge sessions have SecurityTier=ELEVATED on the server (CCR v2).
|
* This fork disables trusted-device enrollment and header emission. The
|
||||||
* The server gates ConnectBridgeWorker on its own flag
|
* remaining helpers only clear any previously stored token during login/logout
|
||||||
* (sessions_elevated_auth_enforcement in Anthropic Main); this CLI-side
|
* so old state is not carried forward.
|
||||||
* flag controls whether the CLI sends X-Trusted-Device-Token at all.
|
|
||||||
* Two flags so rollout can be staged: flip CLI-side first (headers
|
|
||||||
* start flowing, server still no-ops), then flip server-side.
|
|
||||||
*
|
|
||||||
* Enrollment (POST /auth/trusted_devices) is gated server-side by
|
|
||||||
* account_session.created_at < 10min, so it must happen during /login.
|
|
||||||
* Token is persistent (90d rolling expiry) and stored in keychain.
|
|
||||||
*
|
|
||||||
* See anthropics/anthropic#274559 (spec), #310375 (B1b tenant RPCs),
|
|
||||||
* #295987 (B2 Python routes), #307150 (C1' CCR v2 gate).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function getTrustedDeviceToken(): string | undefined {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearTrustedDeviceTokenCache(): void {
|
export function clearTrustedDeviceTokenCache(): void {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the stored trusted device token from secure storage and the memo cache.
|
* Clear any stored trusted-device token from secure storage.
|
||||||
* Called during /login so a stale token from the previous account isn't sent
|
|
||||||
* as X-Trusted-Device-Token after account switches.
|
|
||||||
*/
|
*/
|
||||||
export function clearTrustedDeviceToken(): void {
|
export function clearTrustedDeviceToken(): void {
|
||||||
const secureStorage = getSecureStorage()
|
const secureStorage = getSecureStorage()
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -2226,11 +2226,8 @@ async function run(): Promise<CommanderCommand> {
|
|||||||
resetUserCache();
|
resetUserCache();
|
||||||
// Refresh GrowthBook after login to get updated feature flags (e.g., for claude.ai MCPs)
|
// Refresh GrowthBook after login to get updated feature flags (e.g., for claude.ai MCPs)
|
||||||
refreshGrowthBookAfterAuthChange();
|
refreshGrowthBookAfterAuthChange();
|
||||||
// Clear any stale trusted device token then enroll for Remote Control.
|
// Clear any stale trusted-device token, then run the no-op enrollment
|
||||||
// Both self-gate on tengu_sessions_elevated_auth_enforcement internally
|
// stub so the disabled bridge path stays consistent after login.
|
||||||
// — enrollTrustedDevice() via checkGate_CACHED_OR_BLOCKING (awaits
|
|
||||||
// the GrowthBook reinit above), clearTrustedDeviceToken() via the
|
|
||||||
// sync cached check (acceptable since clear is idempotent).
|
|
||||||
void import('./bridge/trustedDevice.js').then(m => {
|
void import('./bridge/trustedDevice.js').then(m => {
|
||||||
m.clearTrustedDeviceToken();
|
m.clearTrustedDeviceToken();
|
||||||
return m.enrollTrustedDevice();
|
return m.enrollTrustedDevice();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user