| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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, |
| |
| |
| |
| { 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 |
| } |
|
|
| |
| |
| |
| |
| 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 |
| } |
| |
| |
| 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, |
| } |
| } |
|
|