refactor(jcode): fold three reverse-scan copies into one, reuse the shared hook POST

The jcode journal reader, the Claude transcript reader and the Command Code
transcript reader each carried their own copy of the same reverse chunked line
scan; they now share one tested helper. jcode's managed hook script drops its
hand-rolled curl for buildPosixAgentHookPostCommand, which also gains it the
raw-JSON transport and the --noproxy guard the bespoke copy was missing.

Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
This commit is contained in:
Neil
2026-10-03 01:49:40 -07:00
committed by Neil
co-authored by czzczz
parent 7fb201f60f
commit 0a45ad5ec4
17 changed files with 335 additions and 357 deletions
+3 -6
View File
@@ -3,11 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const { spawnProcessMock } = vi.hoisted(() => ({ spawnProcessMock: vi.fn() }))
vi.mock('../../shared/child-process/run-process', () => ({ spawnProcess: spawnProcessMock }))
import {
prewarmJcodeDaemon,
resetJcodeDaemonPrewarmForTests,
shouldPrewarmJcodeDaemon
} from './daemon-prewarm'
import { prewarmJcodeDaemon, resetJcodeDaemonPrewarmForTests } from './daemon-prewarm'
function stubChild() {
return { unref: vi.fn(), on: vi.fn() }
@@ -59,12 +55,13 @@ describe('jcode daemon pre-warm', () => {
it('stays out of the way on Windows, which has no runtime dir', () => {
expect(
shouldPrewarmJcodeDaemon({
prewarmJcodeDaemon({
launchAgent: 'jcode',
runtimeDir: 'C:/tmp/orca-jcode/abc',
platform: 'win32'
})
).toBe(false)
expect(spawnProcessMock).not.toHaveBeenCalled()
})
it('reports failure instead of throwing when the binary is missing', () => {
+29 -43
View File
@@ -9,33 +9,27 @@
import { spawnProcess } from '../../shared/child-process/run-process'
import { getTuiAgentLaunchCommand, TUI_AGENT_CONFIG } from '../../shared/tui-agent-config'
/** Runtime dirs already pre-warmed in this Orca process; the daemon outlives one pane. */
const prewarmedRuntimeDirs = new Set<string>()
export type JcodeDaemonPrewarm = {
launchAgent?: string
runtimeDir?: string
cwd?: string
env?: Record<string, string>
platform?: NodeJS.Platform
}
/** Runtime dirs already warmed in this Orca process; the daemon outlives one pane. */
const warmed = new Set<string>()
export function resetJcodeDaemonPrewarmForTests(): void {
prewarmedRuntimeDirs.clear()
warmed.clear()
}
/** The runtime dir to warm, or null when this pane is not a local jcode launch. */
export function resolveJcodePrewarmRuntimeDir(args: {
launchAgent?: string
runtimeDir?: string
platform?: NodeJS.Platform
}): string | null {
// Why non-Windows only: the runtime dir is a unix-socket directory, and Orca
// only stamps it off Windows (see shouldInjectJcodeRuntimeDir).
if (args.launchAgent !== 'jcode' || (args.platform ?? process.platform) === 'win32') {
return null
}
return args.runtimeDir !== undefined && args.runtimeDir.length > 0 ? args.runtimeDir : null
}
export function shouldPrewarmJcodeDaemon(args: {
launchAgent?: string
runtimeDir?: string
platform?: NodeJS.Platform
}): boolean {
return resolveJcodePrewarmRuntimeDir(args) !== null
/** The runtime dir to warm, or null when this pane is not a local jcode launch.
* Why non-Windows only: the runtime dir is a unix-socket directory, and Orca only
* stamps it off Windows (see shouldInjectJcodeRuntimeDir). */
function prewarmTarget({ launchAgent, runtimeDir, platform }: JcodeDaemonPrewarm): string | null {
const unsupported = launchAgent !== 'jcode' || (platform ?? process.platform) === 'win32'
return unsupported || !runtimeDir ? null : runtimeDir
}
/**
@@ -45,39 +39,31 @@ export function shouldPrewarmJcodeDaemon(args: {
* listening, so a pre-warm that fails costs nothing beyond the cold start Orca
* already had. Never throws, and never blocks the spawn path.
*/
export function prewarmJcodeDaemon(args: {
launchAgent?: string
runtimeDir?: string
cwd?: string
env?: Record<string, string>
platform?: NodeJS.Platform
}): boolean {
const runtimeDir = resolveJcodePrewarmRuntimeDir(args)
if (runtimeDir === null || prewarmedRuntimeDirs.has(runtimeDir)) {
export function prewarmJcodeDaemon(args: JcodeDaemonPrewarm): boolean {
const runtimeDir = prewarmTarget(args)
if (runtimeDir === null || warmed.has(runtimeDir)) {
return false
}
prewarmedRuntimeDirs.add(runtimeDir)
warmed.add(runtimeDir)
// A missing binary or a spawn refusal just means no head start; forget the dir so
// the next pane on it can try again.
const giveUp = (): boolean => (warmed.delete(runtimeDir), false)
try {
const child = spawnProcess({
program: getTuiAgentLaunchCommand(TUI_AGENT_CONFIG.jcode, args.platform ?? process.platform),
// Why --no-update: an update check on the pre-warm path would delay the very
// socket the client is about to wait on.
// Why --no-update: an update check here would delay the very socket the client
// is about to wait on. Why stdio ignore + unref: the daemon is jcode's to own
// and must outlive this spawn, so Orca keeps no handle on it.
args: ['--no-update', 'serve'],
cwd: args.cwd,
env: { ...args.env, JCODE_RUNTIME_DIR: runtimeDir },
detached: true,
stdio: 'ignore'
})
// Why unref: the daemon is jcode's to own and must outlive this spawn; keeping a
// handle would tie Orca's event loop to it.
child.unref()
child.on('error', () => {
// A missing binary or a spawn refusal just means no head start.
prewarmedRuntimeDirs.delete(runtimeDir)
})
child.on('error', giveUp)
return true
} catch {
prewarmedRuntimeDirs.delete(runtimeDir)
return false
return giveUp()
}
}
+3 -3
View File
@@ -105,10 +105,10 @@ describe.runIf(process.platform !== 'win32')('jcode managed hook as jcode runs i
const script = readFileSync(scriptPath, 'utf8')
const gateBranch = script.slice(script.indexOf('if [ "$JCODE_HOOK_EVENT" = pre_tool ]'))
expect(gateBranch).toContain('orca_post_jcode_event >/dev/null 2>&1 &')
// The observer path keeps the plain call, so a slow POST cannot be lost to
// a script that exited first.
// The observer path keeps the foreground call, so a slow POST cannot be lost
// to a script that exited first.
expect(gateBranch).toContain('orca_post_jcode_event >/dev/null 2>&1 || :')
expect(script.trimEnd().endsWith('exit 0')).toBe(true)
expect(script).toContain('\norca_post_jcode_event\n')
} finally {
cleanup()
}
+1 -1
View File
@@ -60,7 +60,7 @@ describe('JcodeHookService', () => {
expect(script).toContain('payload@-')
// Why: the payload is jcode's own JCODE_HOOK_PAYLOAD, forwarded verbatim.
expect(script).toContain('$JCODE_HOOK_PAYLOAD')
expect(script).toContain('hook_event_name=${JCODE_HOOK_EVENT}')
expect(script).toContain('payload="$JCODE_HOOK_PAYLOAD"')
})
it('preserves unrelated config tables when installing hooks', () => {
+15 -26
View File
@@ -7,6 +7,7 @@ import {
writeManagedScript
} from '../agent-hooks/installer-utils'
import { refreshManagedScriptIfPresent } from '../agent-hooks/managed-hook-script-refresh'
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
import {
readTextFileRemote,
writeManagedScriptRemote,
@@ -58,43 +59,31 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
// Why: pre_tool is jcode's gate. It writes the tool input to our stdin and
// waits for us, so drain stdin before any exit — a tool input larger than
// the pipe buffer would otherwise stall the agent mid-write.
// Why before the env guard: pre_tool is jcode's gate — it writes the tool input to
// our stdin and waits for us, so stdin must be drained before ANY exit path or a
// tool input larger than the pipe buffer stalls the agent mid-write.
'if [ "$JCODE_HOOK_EVENT" = pre_tool ]; then',
` ${POSIX_HOOK_STDIN_DRAIN_COMMAND}`,
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
// Why: jcode already supplies JCODE_HOOK_PAYLOAD as a JSON object (capped
// at 16 KB), so Orca forwards it verbatim instead of hand-building JSON in
// shell (unsafe for arbitrary text). The event name is also posted as a
// top-level form field for old payloads that omit it.
// Why the env var rather than a stdin capture: jcode hands the hook its payload as
// a ready JSON object (capped at 16 KB), so Orca forwards it verbatim instead of
// hand-building JSON in shell, which is unsafe for arbitrary text.
'payload="$JCODE_HOOK_PAYLOAD"',
'orca_post_jcode_event() {',
' printf \'%s\' "$JCODE_HOOK_PAYLOAD" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/jcode" \\',
' --connect-timeout 0.5 --max-time 1.5 \\',
' -H "Content-Type: application/x-www-form-urlencoded" \\',
' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\',
' --data-urlencode "paneKey=${ORCA_PANE_KEY}" \\',
' --data-urlencode "tabId=${ORCA_TAB_ID}" \\',
' --data-urlencode "launchToken=${ORCA_AGENT_LAUNCH_TOKEN}" \\',
' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\',
' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\',
' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\',
' --data-urlencode "hook_event_name=${JCODE_HOOK_EVENT}" \\',
' --data-urlencode "session_id=${JCODE_HOOK_SESSION_ID}" \\',
' --data-urlencode "cwd=${JCODE_HOOK_CWD}" \\',
' --data-urlencode "payload@-" >/dev/null 2>&1 || true',
...buildPosixAgentHookPostCommand('jcode').map((line) => ` ${line}`),
'}',
// Why: jcode reads this gate's stderr to EOF before releasing the tool call, so
// the POST runs detached with both pipes closed. Orca observes the tool live and
// adds no latency; the gate always allows (Orca never blocks a jcode tool).
// Why detached on the gate: jcode reads this script's stderr to EOF before it
// releases the tool call, so an inherited pipe would hold the tool open for as long
// as the POST ran. Orca observes the tool live and adds no latency; the gate always
// allows, because Orca never blocks a jcode tool.
'if [ "$JCODE_HOOK_EVENT" = pre_tool ]; then',
' orca_post_jcode_event >/dev/null 2>&1 &',
' exit 0',
'else',
' orca_post_jcode_event >/dev/null 2>&1 || :',
'fi',
'orca_post_jcode_event',
'exit 0',
''
].join('\n')