Files
orca/src/preload/api/pty-bridge-stream-and-serialization.ts
T
Brennan Benson 8fdad2a3af fix(claude): activate account profiles and remove credential replay (Step 4 of 4) (#24434)
Each saved Claude account now runs in its own CLAUDE_CONFIG_DIR folder, so Claude renews each login itself and switching no longer replays a copied credential. A claude shell function routes every launch to the selected account; the terminal daemon protocol moves to 42 so new terminals get it. After updating, each saved account signs in once: typed claude shows Claude's own sign-in, old terminals show an in-app banner, chats and the status-bar menu offer Sign in, and a one-time toast explains it. Orca prints nothing into terminals.
2026-10-07 22:51:34 -04:00

167 lines
7.1 KiB
TypeScript

import { ipcRenderer } from 'electron'
import type { PtyModelRestoreNeededEvent } from '../../shared/pty-model-restore-marker'
import type { TerminalSideEffectBatch } from '../../shared/terminal-side-effect-facts'
import type { PreloadApi } from '../api-types'
import type { TerminalProcessInspection } from '../../shared/terminal-process-inspection'
import type { CodexSharedServerStatus } from '../../shared/codex-shared-server-command'
export const ptyStreamAndSerializationApi = {
inspectProcess: (
id: string,
options?: {
expectedIncarnationId?: string
scanChildProcesses?: boolean
steadyState?: boolean
}
): Promise<TerminalProcessInspection> =>
ipcRenderer.invoke('pty:inspectProcess', { id, ...options }),
confirmForegroundProcess: (id: string): Promise<string | null> =>
ipcRenderer.invoke('pty:confirmForegroundProcess', { id }),
isCodexOnSharedServer: (id: string): Promise<CodexSharedServerStatus> =>
ipcRenderer.invoke('pty:isCodexOnSharedServer', { id }),
disableCodexSharedServerAutoStart: (id: string): Promise<boolean> =>
ipcRenderer.invoke('pty:disableCodexSharedServerAutoStart', { id }),
stopCodexSharedServer: (id: string): Promise<boolean> =>
ipcRenderer.invoke('pty:stopCodexSharedServer', { id }),
openedBeforeClaudeAccounts: (id: string): Promise<boolean> =>
ipcRenderer.invoke('pty:openedBeforeClaudeAccounts', { id }),
getCwd: (id: string): Promise<string> => ipcRenderer.invoke('pty:getCwd', { id }),
getSize: (id: string): Promise<{ cols: number; rows: number } | null> =>
ipcRenderer.invoke('pty:getSize', { id }),
onData: (
callback: (data: {
id: string
data: string
seq?: number
rawLength?: number
transformed?: boolean
background?: boolean
droppedOutput?: boolean
}) => void
): (() => void) => {
const listener = (
_event: Electron.IpcRendererEvent,
data: {
id: string
data: string
seq?: number
rawLength?: number
transformed?: boolean
background?: boolean
droppedOutput?: boolean
}
) => callback(data)
ipcRenderer.on('pty:data', listener)
return () => ipcRenderer.removeListener('pty:data', listener)
},
onReplay: (callback: (data: { id: string; data: string }) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: { id: string; data: string }) =>
callback(data)
ipcRenderer.on('pty:replay', listener)
return () => ipcRenderer.removeListener('pty:replay', listener)
},
onModelRestoreNeeded: (callback: (event: PtyModelRestoreNeededEvent) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, event: PtyModelRestoreNeededEvent) =>
callback(event)
ipcRenderer.on('pty:modelRestoreNeeded', listener)
return () => ipcRenderer.removeListener('pty:modelRestoreNeeded', listener)
},
onSideEffect: (callback: (batch: TerminalSideEffectBatch) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, batch: TerminalSideEffectBatch) =>
callback(batch)
ipcRenderer.on('pty:sideEffect', listener)
return () => ipcRenderer.removeListener('pty:sideEffect', listener)
},
getSideEffectSnapshot: (id: string): Promise<TerminalSideEffectBatch | null> =>
ipcRenderer.invoke('pty:sideEffectSnapshot', { id }),
onExit: (
callback: (data: {
id: string
code: number
preserveRendererBinding?: boolean
/** Which lifetime of `id` died; absent when the execution host predates the field. */
incarnationId?: string
/** Set only when the owning relay disowned this id; never a claim that the process died. */
ptySourceDisowned?: true
/** Main stopped this PTY so a new process could take its pane; the pane is not dying. */
replacedByRestart?: true
}) => void
): (() => void) => {
const listener = (
_event: Electron.IpcRendererEvent,
data: {
id: string
code: number
preserveRendererBinding?: boolean
incarnationId?: string
ptySourceDisowned?: true
replacedByRestart?: true
}
) => callback(data)
ipcRenderer.on('pty:exit', listener)
return () => ipcRenderer.removeListener('pty:exit', listener)
},
onSpawned: (callback: (data: { id: string }) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: { id: string }) => callback(data)
ipcRenderer.on('pty:spawned', listener)
return () => ipcRenderer.removeListener('pty:spawned', listener)
},
onSerializeBufferRequest: (
callback: (data: {
requestId: string
ptyId: string
opts?: { scrollbackRows?: number }
}) => void
): (() => void) => {
const listener = (
_event: Electron.IpcRendererEvent,
data: {
requestId: string
ptyId: string
opts?: { scrollbackRows?: number }
}
) => callback(data)
ipcRenderer.on('pty:serializeBuffer:request', listener)
return () => ipcRenderer.removeListener('pty:serializeBuffer:request', listener)
},
onClearBufferRequest: (callback: (data: { ptyId: string }) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: { ptyId: string }) => callback(data)
ipcRenderer.on('pty:clearBuffer:request', listener)
return () => ipcRenderer.removeListener('pty:clearBuffer:request', listener)
},
onResetInputModesRequest: (callback: (data: { ptyId: string }) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: { ptyId: string }) => callback(data)
ipcRenderer.on('pty:resetInputModes:request', listener)
return () => ipcRenderer.removeListener('pty:resetInputModes:request', listener)
},
sendSerializedBuffer: (
requestId: string,
snapshot: {
data: string
cols: number
rows: number
seq?: number
lastTitle?: string
kittyKeyboardFlags?: number
} | null
): void => {
ipcRenderer.send('pty:serializeBuffer:response', { requestId, snapshot })
},
declarePendingPaneSerializer: (paneKey: string): Promise<number> =>
ipcRenderer.invoke('pty:declarePendingPaneSerializer', { paneKey }),
settlePaneSerializer: (paneKey: string, gen: number): Promise<void> =>
ipcRenderer.invoke('pty:settlePaneSerializer', { paneKey, gen }),
clearPendingPaneSerializer: (paneKey: string, gen: number): Promise<void> =>
ipcRenderer.invoke('pty:clearPendingPaneSerializer', { paneKey, gen }),
reportRendererSerializerReady: (ptyId: string): Promise<void> =>
ipcRenderer.invoke('pty:reportRendererSerializerReady', { ptyId }),
management: {
listSessions: () => ipcRenderer.invoke('pty:management:listSessions'),
killAll: () => ipcRenderer.invoke('pty:management:killAll'),
killOne: (args: { sessionId: string }) => ipcRenderer.invoke('pty:management:killOne', args),
restart: () => ipcRenderer.invoke('pty:management:restart'),
macTccAttribution: () => ipcRenderer.invoke('pty:management:macTccAttribution'),
resetFolderAccess: () => ipcRenderer.invoke('pty:management:resetFolderAccess')
}
} satisfies Partial<PreloadApi['pty']>