Download src/bridge/codeSessionApi.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 4.84 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/bridge/codeSessionApi.ts
- Command line
-
hf download hf://chenbhao/codev/src/bridge/codeSessionApi.ts
-
curl -L -o codeSessionApi.ts https://huggingface.co/chenbhao/codev/resolve/main/src/bridge/codeSessionApi.ts
4.84 kB
| /** | |
| * Thin HTTP wrappers for the CCR v2 code-session API. | |
| * | |
| * Separate file from remoteBridgeCore.ts so the SDK /bridge subpath can | |
| * export createCodeSession + fetchRemoteCredentials without bundling the | |
| * heavy CLI tree (analytics, transport, etc.). Callers supply explicit | |
| * accessToken + baseUrl — no implicit auth or config reads. | |
| */ | |
| import axios from 'axios' | |
| import { logForDebugging } from '../utils/debug.js' | |
| import { errorMessage } from '../utils/errors.js' | |
| import { jsonStringify } from '../utils/slowOperations.js' | |
| import { extractErrorDetail } from './debugUtils.js' | |
| const ANTHROPIC_VERSION = '2023-06-01' | |
| function oauthHeaders(accessToken: string): Record<string, string> { | |
| return { | |
| Authorization: `Bearer ${accessToken}`, | |
| 'Content-Type': 'application/json', | |
| 'anthropic-version': ANTHROPIC_VERSION, | |
| } | |
| } | |
| export async function createCodeSession( | |
| baseUrl: string, | |
| accessToken: string, | |
| title: string, | |
| timeoutMs: number, | |
| tags?: string[], | |
| ): Promise<string | null> { | |
| const url = `${baseUrl}/v1/code/sessions` | |
| let response | |
| try { | |
| response = await axios.post( | |
| url, | |
| // bridge: {} is the positive signal for the oneof runner — omitting it | |
| // (or sending environment_id: "") now 400s. BridgeRunner is an empty | |
| // message today; it's a placeholder for future bridge-specific options. | |
| { title, bridge: {}, ...(tags?.length ? { tags } : {}) }, | |
| { | |
| headers: oauthHeaders(accessToken), | |
| timeout: timeoutMs, | |
| validateStatus: s => s < 500, | |
| }, | |
| ) | |
| } catch (err: unknown) { | |
| logForDebugging( | |
| `[code-session] Session create request failed: ${errorMessage(err)}`, | |
| ) | |
| return null | |
| } | |
| if (response.status !== 200 && response.status !== 201) { | |
| const detail = extractErrorDetail(response.data) | |
| logForDebugging( | |
| `[code-session] Session create failed ${response.status}${detail ? `: ${detail}` : ''}`, | |
| ) | |
| return null | |
| } | |
| const data: unknown = response.data | |
| if ( | |
| !data || | |
| typeof data !== 'object' || | |
| !('session' in data) || | |
| !data.session || | |
| typeof data.session !== 'object' || | |
| !('id' in data.session) || | |
| typeof data.session.id !== 'string' || | |
| !data.session.id.startsWith('cse_') | |
| ) { | |
| logForDebugging( | |
| `[code-session] No session.id (cse_*) in response: ${jsonStringify(data).slice(0, 200)}`, | |
| ) | |
| return null | |
| } | |
| return data.session.id | |
| } | |
| /** | |
| * Credentials from POST /bridge. JWT is opaque — do not decode. | |
| * Each /bridge call bumps worker_epoch server-side (it IS the register). | |
| */ | |
| export type RemoteCredentials = { | |
| worker_jwt: string | |
| api_base_url: string | |
| expires_in: number | |
| worker_epoch: number | |
| } | |
| export async function fetchRemoteCredentials( | |
| sessionId: string, | |
| baseUrl: string, | |
| accessToken: string, | |
| timeoutMs: number, | |
| trustedDeviceToken?: string, | |
| ): Promise<RemoteCredentials | null> { | |
| const url = `${baseUrl}/v1/code/sessions/${sessionId}/bridge` | |
| const headers = oauthHeaders(accessToken) | |
| if (trustedDeviceToken) { | |
| headers['X-Trusted-Device-Token'] = trustedDeviceToken | |
| } | |
| let response | |
| try { | |
| response = await axios.post( | |
| url, | |
| {}, | |
| { | |
| headers, | |
| timeout: timeoutMs, | |
| validateStatus: s => s < 500, | |
| }, | |
| ) | |
| } catch (err: unknown) { | |
| logForDebugging( | |
| `[code-session] /bridge request failed: ${errorMessage(err)}`, | |
| ) | |
| return null | |
| } | |
| if (response.status !== 200) { | |
| const detail = extractErrorDetail(response.data) | |
| logForDebugging( | |
| `[code-session] /bridge failed ${response.status}${detail ? `: ${detail}` : ''}`, | |
| ) | |
| return null | |
| } | |
| const data: unknown = response.data | |
| if ( | |
| data === null || | |
| typeof data !== 'object' || | |
| !('worker_jwt' in data) || | |
| typeof data.worker_jwt !== 'string' || | |
| !('expires_in' in data) || | |
| typeof data.expires_in !== 'number' || | |
| !('api_base_url' in data) || | |
| typeof data.api_base_url !== 'string' || | |
| !('worker_epoch' in data) | |
| ) { | |
| logForDebugging( | |
| `[code-session] /bridge response malformed (need worker_jwt, expires_in, api_base_url, worker_epoch): ${jsonStringify(data).slice(0, 200)}`, | |
| ) | |
| return null | |
| } | |
| // protojson serializes int64 as a string to avoid JS precision loss; | |
| // Go may also return a number depending on encoder settings. | |
| const rawEpoch = data.worker_epoch | |
| const epoch = typeof rawEpoch === 'string' ? Number(rawEpoch) : rawEpoch | |
| if ( | |
| typeof epoch !== 'number' || | |
| !Number.isFinite(epoch) || | |
| !Number.isSafeInteger(epoch) | |
| ) { | |
| logForDebugging( | |
| `[code-session] /bridge worker_epoch invalid: ${jsonStringify(rawEpoch)}`, | |
| ) | |
| return null | |
| } | |
| return { | |
| worker_jwt: data.worker_jwt, | |
| api_base_url: data.api_base_url, | |
| expires_in: data.expires_in, | |
| worker_epoch: epoch, | |
| } | |
| } | |