Files
orca/src/main/network/http-client.ts
T
OrcaWinandNeil 16eaceb6a3 fix(orcad): orca serve owns its Claude and Codex accounts again (#29209)
* fix(orcad): orca serve owns its Claude and Codex accounts again (#28962)

orcad never wired account services, so every accounts.* RPC failed once
orca serve moved onto orcad. Build the same account services the desktop
builds (now in provider-account-services.ts), made Electron-free, and
install them in orcad except on a WSL host, whose desktop owns its accounts.

* test(orcad): keep account-services test off the real userData when XDG_CONFIG_HOME is set

* fix(orcad): structured Codex chats and session discovery follow this host's managed accounts

Create orcad's account services before the runtime so its structured Codex launch/read resolvers and catalog-probe prep are constructor deps, as on the desktop. A client-sent account home still wins, and a WSL host keeps them unset so the desktop-pushed CODEX_HOME applies. AI Vault session discovery now includes this host's managed Codex homes.

* fix(orcad): automatic Codex resumes follow the account home their session was recorded in

Share the desktop's resume launch prep as a factory and wire it into orcad's PTY registration and AI Vault resume, so a pane restored after an account switch resumes from its origin home. A client-sent account home keeps resuming from itself.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-15 17:38:52 -07:00

55 lines
2.1 KiB
TypeScript

import type { Session } from 'electron'
/**
* Outbound HTTP for main-process integrations.
*
* Why a port: the desktop uses Electron's Chromium-backed network stack — it follows
* session/proxy state, avoids undici's stale keep-alive sockets after a VPN path change,
* and sends a Chrome user agent that some APIs (Jira's XSRF check) depend on. None of
* that exists on a host with no Chromium.
*
* The Node default is the platform global. That is a real behavioural difference, not a
* transparent swap, which is why this is a named port rather than a silent fallback:
* a Node host reads proxy configuration from the environment instead of from Chromium,
* and sends Node's user agent.
*
* Body safety (orca#8695): the global uses undici, where an unread response body can
* crash the process. This port hands the Response straight to its caller and never
* inspects it, so the consume/cancel obligation stays exactly where it already was —
* with the caller, unchanged from when they called Electron's net directly.
*/
export type MainHttpClient = {
fetch(url: string, init?: RequestInit): Promise<Response>
/** The Chromium session whose proxy state applies, or null on a host without one. */
proxySession(): Session | null
/** An isolated Chromium cookie jar; absent on a host without Chromium. */
partitionSession?(partition: string): Session
}
const nodeHttpClient: MainHttpClient = {
fetch: (url, init) => globalThis.fetch(url, init),
proxySession: () => null
}
let current: MainHttpClient = nodeHttpClient
export function setMainHttpClient(client: MainHttpClient | null): void {
current = client ?? nodeHttpClient
}
export function getMainHttpClient(): MainHttpClient {
return current
}
/** A provider's isolated cookie jar; throws where no Chromium session exists (orcad). */
export function requirePartitionSession(partition: string, providerLabel: string): Session {
const client = getMainHttpClient()
if (!client.partitionSession) {
throw new Error(
`${providerLabel} usage from a session cookie needs the Orca desktop app and is not available on this server`
)
}
return client.partitionSession(partition)
}