Files
orca/tests/e2e/helpers/agent-hook-endpoint.ts
T
Jinwoo Hong 9bd47de3cf feat(sidebar): after a restart, the host clears sort tiers nothing backs any more (dark) (#30259)
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)

* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's

* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows

* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear

* chore(sidebar): merge origin/main; locality helper moved to shared
2026-10-18 01:09:06 -07:00

148 lines
4.3 KiB
TypeScript

import { expect, type ElectronApplication } from '@stablyai/playwright-test'
import { existsSync, readdirSync, readFileSync } from 'node:fs'
import path from 'node:path'
import {
isAgentHookEndpointFileName,
parseAgentHookEndpointFile,
type AgentHookEndpoint
} from '../../../src/shared/agent-hook-endpoint-file'
function findEndpointEnvFile(root: string): string | null {
if (!existsSync(root)) {
return null
}
const entries = readdirSync(root, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(root, entry.name)
if (entry.isFile() && isAgentHookEndpointFileName(entry.name)) {
return fullPath
}
if (entry.isDirectory()) {
const nested = findEndpointEnvFile(fullPath)
if (nested) {
return nested
}
}
}
return null
}
export async function readHookEndpoint(app: ElectronApplication): Promise<AgentHookEndpoint> {
const userDataPath = await app.evaluate(({ app: electronApp }) => electronApp.getPath('userData'))
const hookRoot = path.join(userDataPath, 'agent-hooks')
let endpointPath: string | null = null
await expect
.poll(
() => {
endpointPath = findEndpointEnvFile(hookRoot)
return endpointPath
},
{
timeout: 15_000,
message: `Agent hook endpoint file not found under ${hookRoot}`
}
)
.not.toBeNull()
if (!endpointPath) {
throw new Error(`Agent hook endpoint file not found under ${hookRoot}`)
}
return parseAgentHookEndpointFile(readFileSync(endpointPath, 'utf8'))
}
export async function emitCodexHookStatus(
endpoint: AgentHookEndpoint,
status: {
paneKey: string
worktreeId: string
state: 'working' | 'done'
prompt?: string
lastAssistantMessage?: string
transcriptPath?: string | null
sessionId?: string
}
): Promise<void> {
const [tabId] = status.paneKey.split(':')
const payload =
status.state === 'working'
? {
hook_event_name: 'UserPromptSubmit',
prompt: status.prompt
}
: {
hook_event_name: 'Stop',
last_assistant_message: status.lastAssistantMessage
}
const response = await fetch(`http://127.0.0.1:${endpoint.port}/hook/codex`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Orca-Agent-Hook-Token': endpoint.token
},
body: JSON.stringify({
paneKey: status.paneKey,
tabId,
worktreeId: status.worktreeId,
env: endpoint.env,
version: endpoint.version,
payload: {
...payload,
...(status.transcriptPath !== undefined ? { transcript_path: status.transcriptPath } : {}),
...(status.sessionId ? { session_id: status.sessionId } : {})
}
})
})
if (response.status !== 204) {
throw new Error(`Codex hook POST returned ${response.status}`)
}
}
export async function emitGrokHookPayload(
endpoint: AgentHookEndpoint,
event: {
paneKey: string
worktreeId: string
payload: Record<string, unknown>
}
): Promise<void> {
const [tabId] = event.paneKey.split(':')
const response = await fetch(`http://127.0.0.1:${endpoint.port}/hook/grok`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Orca-Agent-Hook-Token': endpoint.token
},
body: JSON.stringify({
paneKey: event.paneKey,
tabId,
worktreeId: event.worktreeId,
env: endpoint.env,
version: endpoint.version,
payload: event.payload
})
})
if (response.status !== 204) {
throw new Error(`Grok hook POST returned ${response.status}`)
}
}
/** One raw Claude hook event for a pane, as the agent's hook script posts it. */
export async function postClaudeHook(
endpoint: AgentHookEndpoint,
pane: { paneKey: string; worktreeId: string },
payload: Record<string, unknown>
): Promise<void> {
const response = await fetch(`http://127.0.0.1:${endpoint.port}/hook/claude`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Orca-Agent-Hook-Token': endpoint.token },
body: JSON.stringify({
paneKey: pane.paneKey,
tabId: pane.paneKey.split(':')[0],
worktreeId: pane.worktreeId,
env: endpoint.env,
version: endpoint.version,
payload: { session_id: 'e2e-claude-session', ...payload }
})
})
expect(response.status).toBe(204)
}