fix(agent-hooks): parse endpoint file and keep hook secrets off argv

The managed shell/cmd hook scripts sourced the endpoint file
(`. "$file"` / `call "%file%"`), so any shell/batch written into that
path executed with the agent's privileges. They also passed the hook
token and the raw payload as curl command-line arguments (readable by
other same-UID processes via `ps`/`/proc/<pid>/cmdline`) and did not
disable proxying for the loopback POST.

Add a shared builder (`agent-hooks/managed-hook-script.ts`) used by every
shell/cmd hook:

- Parse only the four known ORCA_AGENT_HOOK_* keys from the endpoint
  file; never execute it. Values are assigned as literals, so a command
  substitution in the file is stored, not run.
- Stream the token into curl as a header from stdin (`-H @-`) and read
  the payload from a private temp file (`--data-urlencode payload@<file>`),
  so neither appears on the command line.
- Add `--noproxy 127.0.0.1` so a configured http_proxy/ALL_PROXY cannot
  route the token and prompt off-box.
- Validate the port as digits-only; keep the Claude hook silent on stdout.

The JS, Python, and PowerShell hooks already parsed the file and sent the
token as an in-process header, so they are unchanged. On Windows cmd the
token stays on argv (no mktemp; stdin is taken by the payload) — a
documented, same-user-only exposure matching the already-ACL'd endpoint
file.

STA-1284

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-07-03 18:09:48 -07:00
co-authored by Orca
parent 748bced2e6
commit 82ef739ea3
22 changed files with 610 additions and 462 deletions
+1
View File
@@ -6,6 +6,7 @@
"../src/main/agent-hooks/installer-utils.ts",
"../src/main/agent-hooks/installer-utils-remote.ts",
"../src/main/agent-hooks/managed-agent-hook-controls.ts",
"../src/main/agent-hooks/managed-hook-script.ts",
"../src/main/amp/hook-service.ts",
"../src/main/antigravity/hook-service.ts",
"../src/main/claude/hook-settings.ts",
+11
View File
@@ -185,8 +185,16 @@ export function buildWindowsAgentHookPostCommand(source: AgentHookSource): strin
// Why: Codex runs these hooks inline on every turn. PowerShell startup alone
// makes trusted Windows hooks visibly slow, so mirror the POSIX curl path.
// Qualify curl so a repo-local curl.exe cannot hijack hook payloads.
// Note: the payload is read from stdin (`payload@-`), off the command line.
// The token stays on argv here because cmd.exe has no mktemp to stage a
// header file and stdin is already taken by the payload; the endpoint.cmd
// file that holds the same token is already ACL'd to the user, so this
// matches the existing same-user exposure rather than widening it.
return [
`"%SystemRoot%\\System32\\curl.exe" -sS -X POST "http://127.0.0.1:%ORCA_AGENT_HOOK_PORT%/hook/${source}" ^`,
// Why: never let a configured http_proxy/ALL_PROXY route the loopback POST
// (token + payload) off-box.
' --noproxy 127.0.0.1 ^',
' --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%" ^',
@@ -210,6 +218,9 @@ export function buildWindowsAgentHookCurlPostCommand(source: AgentHookSource): s
return [
'"%SystemRoot%\\System32\\curl.exe" -sS -X POST',
`"http://127.0.0.1:%ORCA_AGENT_HOOK_PORT%/hook/${source}"`,
// Why: never let a configured http_proxy/ALL_PROXY route the loopback POST
// (token + payload) off-box.
'--noproxy 127.0.0.1',
'--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%"',
@@ -0,0 +1,249 @@
import { spawn } from 'node:child_process'
import { createServer, type Server } from 'node:http'
import { mkdtempSync, rmSync, writeFileSync, existsSync, readdirSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import {
buildPosixManagedHookScript,
buildWindowsEndpointSubroutineLines,
buildWindowsEndpointLoadLine
} from './managed-hook-script'
describe('buildPosixManagedHookScript — static invariants', () => {
const script = buildPosixManagedHookScript({ source: 'claude' })
it('parses the endpoint file instead of sourcing it', () => {
// Sourcing (`. "$file"`) executes arbitrary shell in that path; the managed
// script must never do that.
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
expect(script).toContain('done < "$ORCA_AGENT_HOOK_ENDPOINT"')
})
it('keeps the token off the command line (streamed as a header via stdin)', () => {
expect(script).not.toContain('X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}')
expect(script).toContain('printf \'X-Orca-Agent-Hook-Token: %s\\n\' "$ORCA_AGENT_HOOK_TOKEN" |')
expect(script).toContain('-H @-')
})
it('keeps the payload off the command line (read from a temp file)', () => {
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
})
it('bypasses any configured proxy for the loopback POST', () => {
expect(script).toContain('--noproxy 127.0.0.1')
})
it('validates the port as digits-only before using it in the URL', () => {
expect(script).toContain('""|*[!0-9]*) exit 0 ;;')
})
})
describe('buildWindowsEndpointSubroutineLines — static invariants', () => {
it('parses the endpoint file with for/f rather than call-executing it', () => {
const lines = buildWindowsEndpointSubroutineLines().join('\r\n')
expect(buildWindowsEndpointLoadLine()).toBe('call :__orcaLoadEndpoint')
expect(lines).toContain('for /f "usebackq eol=# tokens=1,* delims=="')
// The load path must never `call` the endpoint file (which would run it).
expect(lines).not.toContain('call "%ORCA_AGENT_HOOK_ENDPOINT%"')
})
})
// Functional round-trip: the generated POSIX script must actually deliver the
// hook to a loopback listener, and must NOT execute the endpoint file. Requires
// /bin/sh + mktemp + curl, so it is POSIX-only.
describe.skipIf(process.platform === 'win32')('buildPosixManagedHookScript — behavior', () => {
let server: Server
let port: number
let dir: string
let scriptPath: string
let received: {
path?: string
token?: string | undefined
payload?: string
hookEventName?: string
}
beforeEach(async () => {
dir = mkdtempSync(join(tmpdir(), 'managed-hook-'))
scriptPath = join(dir, 'hook.sh')
writeFileSync(scriptPath, buildPosixManagedHookScript({ source: 'claude' }), { mode: 0o755 })
received = {}
server = createServer((req, res) => {
const chunks: Buffer[] = []
req.on('data', (c) => chunks.push(c))
req.on('end', () => {
const body = new URLSearchParams(Buffer.concat(chunks).toString())
received = {
path: req.url,
token: req.headers['x-orca-agent-hook-token'] as string | undefined,
payload: body.get('payload') ?? undefined,
hookEventName: body.get('hook_event_name') ?? undefined
}
res.writeHead(200)
res.end('ok')
})
})
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))
port = (server.address() as { port: number }).port
})
afterEach(() => {
server.close()
rmSync(dir, { recursive: true, force: true })
})
function writeEndpoint(extraLines: string[] = []): string {
const p = join(dir, 'endpoint.env')
writeFileSync(
p,
[
`ORCA_AGENT_HOOK_PORT=${port}`,
'ORCA_AGENT_HOOK_TOKEN=tok-DEADBEEF-42',
'ORCA_AGENT_HOOK_ENV=production',
'ORCA_AGENT_HOOK_VERSION=1.2.3',
...extraLines,
''
].join('\n')
)
return p
}
// Why: drive the script with async spawn, not execFileSync — a synchronous
// child blocks node's event loop so the in-process HTTP listener could never
// accept the loopback POST, and curl would spuriously time out.
function run(env: Record<string, string>, payload: string): Promise<{ stdout: string }> {
return new Promise((resolve, reject) => {
const child = spawn('/bin/sh', [scriptPath], { env: { ...process.env, ...env } })
let stdout = ''
child.stdout.on('data', (c) => {
stdout += String(c)
})
child.on('error', reject)
child.on('close', () => resolve({ stdout }))
child.stdin.end(payload)
})
}
it('delivers the payload and streams the token as a header', async () => {
const endpoint = writeEndpoint()
const { stdout } = await run(
{ ORCA_AGENT_HOOK_ENDPOINT: endpoint, ORCA_PANE_KEY: 'pane-1' },
'{"secret":"hi there"}'
)
expect(received.path).toBe('/hook/claude')
expect(received.token).toBe('tok-DEADBEEF-42')
expect(received.payload).toBe('{"secret":"hi there"}')
// Claude appends UserPromptSubmit hook stdout to the prompt — the claude
// script (no prelude) must print nothing on stdout.
expect(stdout).toBe('')
})
it('prints nothing on stdout even when it cannot create the temp file', async () => {
const endpoint = writeEndpoint()
// Point TMPDIR at a non-writable/nonexistent dir so mktemp fails; the hook
// must fail open silently, never leaking mktemp's error into Claude's prompt.
const { stdout } = await run(
{ ORCA_AGENT_HOOK_ENDPOINT: endpoint, ORCA_PANE_KEY: 'pane-x', TMPDIR: join(dir, 'nope') },
'{"a":1}'
)
expect(stdout).toBe('')
expect(received.path).toBeUndefined()
})
it('parses the endpoint file without executing shell inside it', async () => {
// If the file were sourced, this command substitution would create a marker.
const marker = join(dir, 'PWNED')
const endpoint = writeEndpoint([
`ORCA_AGENT_HOOK_ENV=$(touch ${marker})`,
`\`touch ${marker}2\``
])
await run({ ORCA_AGENT_HOOK_ENDPOINT: endpoint, ORCA_PANE_KEY: 'pane-2' }, '{"x":1}')
expect(existsSync(marker)).toBe(false)
expect(existsSync(`${marker}2`)).toBe(false)
// Junk lines are ignored, but the known keys still drive a successful post.
expect(received.path).toBe('/hook/claude')
})
it('lets the endpoint file override stale PTY coordinates', async () => {
const endpoint = writeEndpoint()
await run(
{
ORCA_AGENT_HOOK_ENDPOINT: endpoint,
ORCA_AGENT_HOOK_PORT: '9',
ORCA_AGENT_HOOK_TOKEN: 'STALE',
ORCA_PANE_KEY: 'pane-3'
},
'{"y":2}'
)
expect(received.path).toBe('/hook/claude')
expect(received.token).toBe('tok-DEADBEEF-42')
})
it('does not post when the port is non-numeric', async () => {
await run(
{ ORCA_AGENT_HOOK_PORT: '12ab', ORCA_AGENT_HOOK_TOKEN: 't', ORCA_PANE_KEY: 'p' },
'{"z":3}'
)
expect(received.path).toBeUndefined()
})
it('does not post when the payload is empty', async () => {
const endpoint = writeEndpoint()
await run({ ORCA_AGENT_HOOK_ENDPOINT: endpoint, ORCA_PANE_KEY: 'pane-5' }, '')
expect(received.path).toBeUndefined()
})
it('bypasses a configured http_proxy for the loopback POST', async () => {
const endpoint = writeEndpoint()
await run(
{
ORCA_AGENT_HOOK_ENDPOINT: endpoint,
ORCA_PANE_KEY: 'pane-6',
http_proxy: 'http://127.0.0.1:1',
HTTPS_PROXY: 'http://127.0.0.1:1',
ALL_PROXY: 'http://127.0.0.1:1'
},
'{"p":6}'
)
expect(received.path).toBe('/hook/claude')
})
it('does not leave the payload temp file behind', async () => {
const endpoint = writeEndpoint()
// Pin TMPDIR so the mktemp file lands in our dir and the assertion is exact.
await run(
{ ORCA_AGENT_HOOK_ENDPOINT: endpoint, ORCA_PANE_KEY: 'pane-7', TMPDIR: dir },
'{"q":7}'
)
const leftovers = readdirSync(dir).filter((n) => n.startsWith('orca-agent-hook.'))
expect(leftovers).toEqual([])
})
// Exercise the Antigravity-shaped options: an extra data field plus a literal
// fallback when the agent sends no stdin.
it('substitutes the empty-payload literal and sends extra fields', async () => {
writeFileSync(
scriptPath,
buildPosixManagedHookScript({
source: 'antigravity',
extraDataFields: [{ name: 'hook_event_name', envVar: 'ORCA_ANTIGRAVITY_EVENT' }],
emptyPayload: { literal: '{}' }
}),
{ mode: 0o755 }
)
const endpoint = writeEndpoint()
await run(
{
ORCA_AGENT_HOOK_ENDPOINT: endpoint,
ORCA_PANE_KEY: 'pane-8',
ORCA_ANTIGRAVITY_EVENT: 'Stop'
},
''
)
expect(received.path).toBe('/hook/antigravity')
expect(received.payload).toBe('{}')
expect(received.hookEventName).toBe('Stop')
})
})
+176
View File
@@ -0,0 +1,176 @@
import type { AgentHookSource } from '../../shared/agent-hook-relay'
// Shared builders for the managed agent-hook scripts. Centralizing the
// security-critical steps (reading the endpoint file, and posting to the
// loopback listener) keeps a single audited implementation instead of the same
// shell/batch repeated across every agent's hook service.
//
// Two properties this module guarantees for every generated script:
// 1. The endpoint file is *parsed*, never executed. Earlier scripts sourced
// it (`. "$file"` / `call "%file%"`), so any shell/batch written into that
// path ran with the agent's privileges. We only ever copy the four known
// `ORCA_AGENT_HOOK_*` values out of it.
// 2. The hook token and the raw payload never appear on the process command
// line. `ps`/`/proc/<pid>/cmdline` are readable by other same-UID (and, on
// many systems, cross-UID) processes, so the token went to a header read
// from stdin and the payload to a private temp file read via `@file`.
// `--noproxy 127.0.0.1` keeps the loopback POST from being redirected through a
// configured `http_proxy`/`ALL_PROXY`, which would send the token and payload
// off-box.
export type PosixEmptyPayloadBehavior = 'exit' | { readonly literal: string }
export type PosixManagedHookScriptOptions = {
readonly source: AgentHookSource
/** Lines emitted right after the shebang, before the endpoint is parsed.
* Used for agents that must print a JSON response to stdout (Gemini,
* Antigravity, Copilot) or bail early (Devin import guard) regardless of
* whether the post ultimately fires. */
readonly preludeLines?: readonly string[]
/** Extra form fields appended to the curl post, e.g. Antigravity's
* `hook_event_name`. `envVar` is read verbatim as `${envVar}`. */
readonly extraDataFields?: readonly { readonly name: string; readonly envVar: string }[]
/** What to do when the agent sent no stdin. Default `exit` (skip the post);
* Antigravity substitutes `{}` so a status row still appears. */
readonly emptyPayload?: PosixEmptyPayloadBehavior
}
// Why: `< "$file"` (not `cat "$file" |`) keeps the loop in the current shell so
// the assignments survive it. Each value is copied as a literal — no `eval`, no
// command substitution on the assignment RHS — so `TOKEN=$(rm -rf ~)` in the
// file is stored as that literal string, never executed. The `set ` strip and
// trailing-CR strip tolerate an `endpoint.cmd` (Windows) file that reached a
// POSIX shell via a cross-platform userData copy.
export function buildPosixEndpointParseLines(): string[] {
return [
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
" __orca_cr=$(printf '\\r')",
' while IFS= read -r __orca_line || [ -n "$__orca_line" ]; do',
' __orca_line=${__orca_line%"$__orca_cr"}',
' case "$__orca_line" in',
' "set "*) __orca_line=${__orca_line#set } ;;',
' esac',
' case "$__orca_line" in',
' ORCA_AGENT_HOOK_PORT=*) ORCA_AGENT_HOOK_PORT=${__orca_line#*=} ;;',
' ORCA_AGENT_HOOK_TOKEN=*) ORCA_AGENT_HOOK_TOKEN=${__orca_line#*=} ;;',
' ORCA_AGENT_HOOK_ENV=*) ORCA_AGENT_HOOK_ENV=${__orca_line#*=} ;;',
' ORCA_AGENT_HOOK_VERSION=*) ORCA_AGENT_HOOK_VERSION=${__orca_line#*=} ;;',
' esac',
' done < "$ORCA_AGENT_HOOK_ENDPOINT"',
'fi'
]
}
// Why: guards run after the parse so refreshed coordinates are in effect. The
// port is validated as digits-only (defense-in-depth against a malformed
// endpoint file) before it is interpolated into the request URL.
export function buildPosixHookGuardLines(): string[] {
return [
'case "$ORCA_AGENT_HOOK_PORT" in',
' ""|*[!0-9]*) exit 0 ;;',
'esac',
'if [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi'
]
}
// Why: capture the payload into a private temp file (mktemp is 0600) and post
// it with `--data-urlencode payload@<file>` so the raw prompt never lands on
// argv. This also keeps tens-of-KB tool output off the curl command line, which
// avoids EDR command-line-length false positives (the concern #4475 fixed via a
// stdin pipe). We use a temp file rather than that stdin pipe because stdin is
// needed for the token: it is streamed into curl as a header via `-H @-`, so the
// secret stays off argv AND off disk. Non-secret identifiers
// (paneKey/tabId/worktreeId/…) stay as argv fields — they are not sensitive and
// keeping them there avoids escaping filesystem paths into the temp file.
export function buildPosixSecureCurlPostLines(options: PosixManagedHookScriptOptions): string[] {
const { source, extraDataFields = [], emptyPayload = 'exit' } = options
const emptyLines =
emptyPayload === 'exit'
? ['if [ ! -s "$__orca_payload_file" ]; then', ' exit 0', 'fi']
: [
'if [ ! -s "$__orca_payload_file" ]; then',
` printf '%s' '${emptyPayload.literal}' > "$__orca_payload_file"`,
'fi'
]
return [
// Why: fail open (skip the status post) if the temp file cannot be created,
// matching the rest of the hook's best-effort behavior. Suppress mktemp's
// stderr so a failure never leaks into hook output — Claude appends
// UserPromptSubmit hook stdout to the prompt, and other agents parse stdout.
'__orca_payload_file=$(mktemp "${TMPDIR:-/tmp}/orca-agent-hook.XXXXXX" 2>/dev/null) || exit 0',
'trap \'rm -f "$__orca_payload_file"\' EXIT',
'cat > "$__orca_payload_file"',
...emptyLines,
'printf \'X-Orca-Agent-Hook-Token: %s\\n\' "$ORCA_AGENT_HOOK_TOKEN" | ' +
`curl -sS -X POST "http://127.0.0.1:\${ORCA_AGENT_HOOK_PORT}/hook/${source}" \\`,
' --noproxy 127.0.0.1 \\',
' --connect-timeout 0.5 --max-time 1.5 \\',
' -H "Content-Type: application/x-www-form-urlencoded" \\',
' -H @- \\',
' --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}" \\',
...extraDataFields.map((field) => ` --data-urlencode "${field.name}=\${${field.envVar}}" \\`),
' --data-urlencode "payload@${__orca_payload_file}" >/dev/null 2>&1 || true'
]
}
/** Full POSIX `.sh` managed hook script for a simple agent (shebang, optional
* prelude, endpoint parse, guards, secure post). Agents with bespoke recovery
* logic (e.g. Command Code) compose the pieces above directly instead. */
export function buildPosixManagedHookScript(options: PosixManagedHookScriptOptions): string {
return [
'#!/bin/sh',
...(options.preludeLines ?? []),
...buildPosixEndpointParseLines(),
...buildPosixHookGuardLines(),
...buildPosixSecureCurlPostLines(options),
'exit 0',
''
].join('\n')
}
// ─── Windows (cmd) endpoint parsing ─────────────────────────────────────────
/** Main-body line that loads endpoint coordinates by *parsing* the file (never
* `call`-ing it). Pair with `buildWindowsEndpointSubroutineLines()`, placed
* after the script's final `exit /b`. */
export function buildWindowsEndpointLoadLine(): string {
return 'call :__orcaLoadEndpoint'
}
// Why: `for /f` reads the file line by line and hands each key/value to a
// subroutine that assigns only the four known variables — the file is parsed,
// not executed, so batch written into endpoint.cmd never runs. Both the
// `set `-prefixed (endpoint.cmd) and bare (cross-copied endpoint.env) shapes
// are matched. `%~1`/`%~2` strip the quotes `for /f` leaves on tokens.
// `:__orcaParseEndpointFile` takes an explicit file path so agents that search
// multiple candidate endpoint files (e.g. Command Code) can reuse it.
export function buildWindowsEndpointSubroutineLines(): string[] {
return [
':__orcaLoadEndpoint',
'if not defined ORCA_AGENT_HOOK_ENDPOINT exit /b 0',
'call :__orcaParseEndpointFile "%ORCA_AGENT_HOOK_ENDPOINT%"',
'exit /b 0',
':__orcaParseEndpointFile',
'if not exist "%~1" exit /b 0',
'for /f "usebackq eol=# tokens=1,* delims==" %%A in ("%~1") do call :__orcaSetEndpoint "%%A" "%%B"',
'exit /b 0',
':__orcaSetEndpoint',
'set "__orca_k=%~1"',
'if /i "%__orca_k%"=="set ORCA_AGENT_HOOK_PORT" set "ORCA_AGENT_HOOK_PORT=%~2"',
'if /i "%__orca_k%"=="ORCA_AGENT_HOOK_PORT" set "ORCA_AGENT_HOOK_PORT=%~2"',
'if /i "%__orca_k%"=="set ORCA_AGENT_HOOK_TOKEN" set "ORCA_AGENT_HOOK_TOKEN=%~2"',
'if /i "%__orca_k%"=="ORCA_AGENT_HOOK_TOKEN" set "ORCA_AGENT_HOOK_TOKEN=%~2"',
'if /i "%__orca_k%"=="set ORCA_AGENT_HOOK_ENV" set "ORCA_AGENT_HOOK_ENV=%~2"',
'if /i "%__orca_k%"=="ORCA_AGENT_HOOK_ENV" set "ORCA_AGENT_HOOK_ENV=%~2"',
'if /i "%__orca_k%"=="set ORCA_AGENT_HOOK_VERSION" set "ORCA_AGENT_HOOK_VERSION=%~2"',
'if /i "%__orca_k%"=="ORCA_AGENT_HOOK_VERSION" set "ORCA_AGENT_HOOK_VERSION=%~2"',
'exit /b 0'
]
}
+10 -10
View File
@@ -1,6 +1,5 @@
/* eslint-disable max-lines -- Why: this suite exercises the full hook HTTP surface (Claude/Codex/Gemini parsing, transcript chunked scan, paneKey dispatch) and keeping the scenarios co-located avoids fixture drift across files. */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { execFileSync } from 'node:child_process'
import {
existsSync,
mkdirSync,
@@ -5663,7 +5662,7 @@ describe('Endpoint file lifecycle', () => {
}
})
it('endpoint file contents are re-parseable by /bin/sh', async () => {
it('endpoint file stays a clean KEY=VALUE shape (no shell metacharacters)', async () => {
if (process.platform === 'win32') {
return
}
@@ -5672,14 +5671,15 @@ describe('Endpoint file lifecycle', () => {
try {
const filePath = server.endpointFilePath!
const expectedPort = server.buildPtyEnv().ORCA_AGENT_HOOK_PORT
// Why: sources the file in a subshell and echoes the resulting env var,
// exactly as the managed hook script does at runtime. If the file shape
// ever drifts from `KEY=VALUE` (e.g. someone adds shell metacharacters
// without quoting), this test catches it before users do.
const out = execFileSync('/bin/sh', ['-c', `. "${filePath}" && echo "$ORCA_AGENT_HOOK_PORT"`])
.toString()
.trim()
expect(out).toBe(expectedPort)
// Why: the managed hook script parses this file (it does not source it),
// reading only the four known ORCA_AGENT_HOOK_* keys. This asserts the
// writer keeps each line as a bare `KEY=VALUE` so that line-based parse
// extracts the value verbatim — catching drift before users do.
const contents = readFileSync(filePath, 'utf-8')
const portLine = contents
.split(/\r?\n/)
.find((line) => line.startsWith('ORCA_AGENT_HOOK_PORT='))
expect(portLine).toBe(`ORCA_AGENT_HOOK_PORT=${expectedPort}`)
} finally {
server.stop()
}
+5 -8
View File
@@ -93,14 +93,11 @@ describe('AntigravityHookService', () => {
expect(script).not.toContain('[string]::IsNullOrWhiteSpace($inputData)) { exit 0 }')
} else {
expect(script).toContain('hook_event_name=${ORCA_ANTIGRAVITY_EVENT}')
expect(script).toContain('payload=$(cat)')
expect(script).toContain("payload='{}'")
expect(script).not.toContain('if [ -z "$payload" ]; then\n exit 0\nfi')
// Why: payload is piped to curl via stdin (`payload@-`) so it never lands
// on the curl command line (EDR oversized-command-line false positive).
expect(script).toContain('printf \'%s\' "$payload" | curl')
expect(script).toContain('--data-urlencode "payload@-"')
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
// Payload delivered from a private temp file (off argv), with `{}`
// substituted when the agent sent no stdin. Endpoint file parsed, not sourced.
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
expect(script).toContain("printf '%s' '{}' > \"$__orca_payload_file\"")
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
}
expect(script).toContain('{"decision":""}')
})
+29 -45
View File
@@ -19,6 +19,11 @@ import {
type HookDefinition,
type HooksConfig
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
writeHooksJsonRemote,
@@ -91,60 +96,39 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
') else (',
' echo {}',
')',
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// Why: reload the endpoint file's live coordinates after an Orca restart.
// Parsed, never executed, so untrusted content in that path cannot run.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAntigravityHookPostCommand(),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
'case "$ORCA_ANTIGRAVITY_EVENT" in',
' Stop)',
' printf \'{"decision":""}\\n\'',
' ;;',
' *)',
// Why: Antigravity accepts an empty JSON object for passive status hooks;
// returning allow/ask/deny from PreToolUse would change the user's tool
// permission policy.
' printf "{}\\n"',
' ;;',
'esac',
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
// Why: some Antigravity hook events can arrive without stdin. Still post
// the event name so Orca shows a status row instead of silently dropping it.
" payload='{}'",
'fi',
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/antigravity" \\',
' --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=${ORCA_ANTIGRAVITY_EVENT}" \\',
' --data-urlencode "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
return buildPosixManagedHookScript({
source: 'antigravity',
preludeLines: [
'case "$ORCA_ANTIGRAVITY_EVENT" in',
' Stop)',
' printf \'{"decision":""}\\n\'',
' ;;',
' *)',
// Why: Antigravity accepts an empty JSON object for passive status hooks;
// returning allow/ask/deny from PreToolUse would change the user's tool
// permission policy.
' printf "{}\\n"',
' ;;',
'esac'
],
extraDataFields: [{ name: 'hook_event_name', envVar: 'ORCA_ANTIGRAVITY_EVENT' }],
// Why: some Antigravity hook events arrive without stdin. Still post the
// event name so Orca shows a status row instead of silently dropping it.
emptyPayload: { literal: '{}' }
})
}
function getWindowsWrapperScript(eventName: string): string {
+7 -5
View File
@@ -269,12 +269,14 @@ describe('ClaudeHookService.installRemote', () => {
const script = fs.files.get('/home/dev/.orca/agent-hooks/claude-hook.sh')
expect(script).toContain('#!/bin/sh')
expect(script).toContain('DEVIN_PROJECT_DIR')
// Why: payload is piped to curl via stdin (`payload@-`) so it never lands
// on the curl command line (EDR oversized-command-line false positive),
// matching the Windows curl.exe hook post.
expect(script).toContain('printf \'%s\' "$payload" | curl')
expect(script).toContain('--data-urlencode "payload@-"')
// Why: payload posts from a private temp file and the token streams in as a
// header via stdin, so neither the prompt nor the token lands on the curl
// command line (also avoids EDR oversized-command-line false positives).
// The endpoint file is parsed, never sourced.
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
expect(script).toContain('-H @-')
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
expect(fs.modes.get('/home/dev/.orca/agent-hooks/claude-hook.sh')).toBe(0o755)
})
+15 -54
View File
@@ -6,6 +6,11 @@ import {
writeHooksJson,
writeManagedScript
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
writeHooksJsonRemote,
@@ -55,11 +60,10 @@ function getManagedScript(
: []),
// Why: the endpoint file holds the *live* port/token for this Orca
// install. A PTY that survived an Orca restart has stale PORT/TOKEN
// baked into its env from the old instance — loading `endpoint.cmd`
// (`set KEY=VALUE` lines) via `call` refreshes them so the hook
// reaches the current server. Falls through to PTY env if the file
// is missing (first run / pre-endpoint-file / running outside Orca).
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// baked into its env from the old instance — reloading `endpoint.cmd`
// refreshes them so the hook reaches the current server. Parsed, never
// executed, so untrusted content in that path cannot run.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
@@ -70,13 +74,14 @@ function getManagedScript(
// curl works the same here as for the POSIX/Codex hooks.
buildWindowsAgentHookCurlPostCommand('claude'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
...(options.skipWhenDevinImportsClaude
return buildPosixManagedHookScript({
source: 'claude',
preludeLines: options.skipWhenDevinImportsClaude
? [
// Why: Devin imports .claude hooks by default. Skip Orca's managed
// Claude hook there so status posts stay attributed to Devin.
@@ -84,52 +89,8 @@ function getManagedScript(
' exit 0',
'fi'
]
: []),
// Why: the endpoint file holds the *live* port/token for this Orca
// install. PTYs that survive an Orca restart have stale PORT/TOKEN
// baked into their env from the old instance — sourcing the file here
// lets us reach the new server. Falls back to PTY env if the file is
// missing (first-run / pre-endpoint-file scripts / running outside Orca).
// Why: suppress stderr on the `.` builtin. A TOCTOU race (endpoint unlinked
// between the `[ -r ]` test and the source) or a malformed line (e.g. CRLF
// bled in from a cross-platform userData copy) would otherwise print a
// parse error that agent transcripts could surface. Stale coords → dead
// port → silent-fail is the documented fail-open path anyway — the env-var
// guards below handle the empty PORT/TOKEN case — so swallowing the noise
// here is strictly better than leaking shell errors into the hook output.
// `|| :` defends against an eventual `set -e` in an outer script context
// (not present today) aborting the hook on a parse error.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX
// shell is not safe once a path contains quotes or newlines. Post the raw
// hook payload plus metadata as form fields and let the receiver parse it.
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/claude" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
: []
})
}
export class ClaudeHookService {
+10 -39
View File
@@ -19,6 +19,11 @@ import {
type HookCommandConfig,
type HookDefinition
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
readTextFileRemote,
@@ -680,55 +685,21 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
'@echo off',
'setlocal',
// Why: see claude/hook-service.ts for rationale. The endpoint file holds
// the live port/token for this Orca install; sourcing it here lets a
// the live port/token for this Orca install; reloading it here lets a
// surviving PTY reach the current server even though its env points at
// the prior Orca's coordinates.
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// the prior Orca's coordinates. Parsed, never executed.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAgentHookCurlPostCommand('codex'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
// Why: see claude/hook-service.ts for rationale. Sourcing refreshes
// PORT/TOKEN/ENV/VERSION from the current Orca so a surviving PTY keeps
// reporting after a restart.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX
// shell is not safe once a path contains quotes or newlines. Post the raw
// hook payload plus metadata as form fields and let the receiver parse it.
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/codex" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
return buildPosixManagedHookScript({ source: 'codex' })
}
export class CodexHookService {
@@ -1,4 +1,8 @@
import { buildWindowsAgentHookPostCommand } from '../agent-hooks/installer-utils'
import {
buildPosixSecureCurlPostLines,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
export type CommandCodeManagedScriptTarget = 'local' | 'posix'
@@ -9,7 +13,11 @@ export function buildCommandCodeManagedScript(
return [
'@echo off',
'setlocal',
'if "%ORCA_AGENT_HOOK_PORT%"=="" if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// Why: the endpoint file(s) are parsed, never executed, so untrusted
// content in that path cannot run. Command Code sanitizes the hook env, so
// when the primary endpoint is missing we still search %APPDATA% for the
// endpoint.cmd whose port matches the recovered loopback port.
'if "%ORCA_AGENT_HOOK_PORT%"=="" call :__orcaLoadEndpoint',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" if not "%ORCA_AGENT_HOOK_PORT%"=="" call :sourceEndpointByPort',
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
@@ -23,8 +31,9 @@ export function buildCommandCodeManagedScript(
'exit /b 0',
':maybeSourceEndpoint',
'if not "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'for /f "tokens=2 delims==" %%P in (\'findstr /b /c:"set ORCA_AGENT_HOOK_PORT=" "%~1" 2^>nul\') do if "%%P"=="%ORCA_AGENT_HOOK_PORT%" call "%~1" 2>nul',
'for /f "tokens=2 delims==" %%P in (\'findstr /b /c:"set ORCA_AGENT_HOOK_PORT=" "%~1" 2^>nul\') do if "%%P"=="%ORCA_AGENT_HOOK_PORT%" call :__orcaParseEndpointFile "%~1"',
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
@@ -116,25 +125,7 @@ export function buildCommandCodeManagedScript(
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/command-code" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
...buildPosixSecureCurlPostLines({ source: 'command-code' }),
'exit 0',
''
].join('\n')
+7 -32
View File
@@ -16,6 +16,7 @@ import {
writeManagedScript,
type HookDefinition
} from '../agent-hooks/installer-utils'
import { buildPosixManagedHookScript } from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
writeHooksJsonRemote,
@@ -151,39 +152,13 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
].join('\r\n')
}
return [
'#!/bin/sh',
"printf '{}\\n'",
// Why: Copilot consumes stdout for some hooks, so stdout is emitted before
return buildPosixManagedHookScript({
source: 'copilot',
// Why: Copilot consumes stdout for some hooks, so `{}` is emitted before
// endpoint refresh, stdin parsing, or the network POST can fail.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/copilot" \\',
' --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 "hookEventName=${ORCA_COPILOT_HOOK_EVENT}" \\',
' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\',
' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\',
' --data-urlencode "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
preludeLines: ["printf '{}\\n'"],
extraDataFields: [{ name: 'hookEventName', envVar: 'ORCA_COPILOT_HOOK_EVENT' }]
})
}
export class CopilotHookService {
+4 -6
View File
@@ -77,12 +77,10 @@ describe('CursorHookService', () => {
if (process.platform === 'win32') {
expect(script).toContain('%SystemRoot%\\System32\\curl.exe')
} else {
// Why: payload is piped to curl via stdin (`payload@-`) so it never lands
// on the curl command line (EDR oversized-command-line false positive).
expect(script).toContain('payload=$(cat)')
expect(script).toContain('printf \'%s\' "$payload" | curl')
expect(script).toContain('--data-urlencode "payload@-"')
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
// Payload goes to a private temp file (off argv); token is streamed as a
// header via `-H @-`; the endpoint file is parsed, never sourced.
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
}
})
+10 -39
View File
@@ -15,6 +15,11 @@ import {
writeManagedScript,
type HookDefinition
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
writeHooksJsonRemote,
@@ -72,55 +77,21 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
'@echo off',
'setlocal',
// Why: see claude/hook-service.ts for rationale. The endpoint file holds
// the live port/token for this Orca install; sourcing it here lets a
// the live port/token for this Orca install; reloading it here lets a
// surviving PTY reach the current server even though its env points at
// the prior Orca's coordinates.
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// the prior Orca's coordinates. Parsed, never executed.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAgentHookPostCommand('cursor'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
// Why: see claude/hook-service.ts for rationale. Sourcing refreshes
// PORT/TOKEN/ENV/VERSION from the current Orca so a surviving PTY keeps
// reporting after a restart.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX
// shell is not safe once a path contains quotes or newlines. Post the raw
// hook payload plus metadata as form fields and let the receiver parse it.
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/cursor" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
return buildPosixManagedHookScript({ source: 'cursor' })
}
export class CursorHookService {
+6 -4
View File
@@ -64,11 +64,13 @@ describe('DevinHookService', () => {
}
const script = readFileSync(getDevinManagedScriptPath(), 'utf8')
expect(script).toContain('/hook/devin')
// Why: payload is piped to curl via stdin (`payload@-`) so it never lands
// on the curl command line (EDR oversized-command-line false positive).
expect(script).toContain('printf \'%s\' "$payload" | curl')
expect(script).toContain('--data-urlencode "payload@-"')
// Why: payload posts from a private temp file and the token streams in as a
// header via stdin, so neither lands on the curl command line (also avoids
// EDR oversized-command-line false positives). Endpoint file parsed, not sourced.
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
expect(script).toContain('-H @-')
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
})
it('preserves unrelated keys in Devin config when installing hooks', () => {
+11 -52
View File
@@ -5,6 +5,11 @@ import {
writeHooksJson,
writeManagedScript
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readTextFileRemote,
writeHooksJsonRemote,
@@ -36,67 +41,21 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
'setlocal',
// Why: the endpoint file holds the *live* port/token for this Orca
// install. A PTY that survived an Orca restart has stale PORT/TOKEN
// baked into its env from the old instance — loading `endpoint.cmd`
// (`set KEY=VALUE` lines) via `call` refreshes them so the hook
// reaches the current server. Falls through to PTY env if the file
// is missing (first run / pre-endpoint-file / running outside Orca).
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// baked into its env from the old instance — reloading `endpoint.cmd`
// refreshes them so the hook reaches the current server. Parsed, never
// executed, so untrusted content in that path cannot run.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAgentHookPostCommand('devin'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
// Why: the endpoint file holds the *live* port/token for this Orca
// install. PTYs that survive an Orca restart have stale PORT/TOKEN
// baked into their env from the old instance — sourcing the file here
// lets us reach the new server. Falls back to PTY env if the file is
// missing (first-run / pre-endpoint-file scripts / running outside Orca).
// Why: suppress stderr on the `.` builtin. A TOCTOU race (endpoint unlinked
// between the `[ -r ]` test and the source) or a malformed line (e.g. CRLF
// bled in from a cross-platform userData copy) would otherwise print a
// parse error that agent transcripts could surface. Stale coords → dead
// port → silent-fail is the documented fail-open path anyway — the env-var
// guards below handle the empty PORT/TOKEN case — so swallowing the noise
// here is strictly better than leaking shell errors into the hook output.
// `|| :` defends against an eventual `set -e` in an outer script context
// (not present today) aborting the hook on a parse error.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX
// shell is not safe once a path contains quotes or newlines. Post the raw
// hook payload plus metadata as form fields and let the receiver parse it.
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/devin" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
return buildPosixManagedHookScript({ source: 'devin' })
}
export class DevinHookService {
+10 -31
View File
@@ -14,6 +14,11 @@ import {
writeManagedScript,
type HookDefinition
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
// Why: SessionStart is installed (not just listened for) so that resuming a
// droid session via `droid --resume` resets the per-pane prompt/tool caches
@@ -69,46 +74,20 @@ function getManagedScript(): string {
return [
'@echo off',
'setlocal',
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// Why: see claude/hook-service.ts. The endpoint file is parsed (never
// executed) to refresh live coordinates after an Orca restart.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAgentHookPostCommand('droid'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/droid" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
return buildPosixManagedHookScript({ source: 'droid' })
}
export class DroidHookService {
+13 -40
View File
@@ -16,6 +16,11 @@ import {
writeManagedScript,
type HookDefinition
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
writeHooksJsonRemote,
@@ -61,59 +66,27 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
// output, even if the env-var guards below cause an early exit.
'echo {}',
// Why: see claude/hook-service.ts for rationale. The endpoint file holds
// the live port/token for this Orca install; sourcing it here lets a
// the live port/token for this Orca install; reloading it here lets a
// surviving PTY reach the current server even though its env points at
// the prior Orca's coordinates.
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// the prior Orca's coordinates. Parsed, never executed.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAgentHookPostCommand('gemini'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
return buildPosixManagedHookScript({
source: 'gemini',
// Why: Gemini expects valid JSON on stdout even when the hook has nothing
// to return. Emit `{}` first so the agent never stalls parsing our output,
// even if the env-var guards below cause an early exit.
'printf "{}\\n"',
// Why: see claude/hook-service.ts for rationale. Sourcing refreshes
// PORT/TOKEN/ENV/VERSION from the current Orca so a surviving PTY keeps
// reporting after a restart.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX
// shell is not safe once a path contains quotes or newlines. Post the raw
// hook payload plus metadata as form fields and let the receiver parse it.
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/gemini" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
preludeLines: ['printf "{}\\n"']
})
}
export class GeminiHookService {
+4 -6
View File
@@ -76,12 +76,10 @@ describe('GrokHookService', () => {
if (process.platform === 'win32') {
expect(script).toContain('%SystemRoot%\\System32\\curl.exe')
} else {
// Why: payload is piped to curl via stdin (`payload@-`) so it never lands
// on the curl command line (EDR oversized-command-line false positive).
expect(script).toContain('payload=$(cat)')
expect(script).toContain('printf \'%s\' "$payload" | curl')
expect(script).toContain('--data-urlencode "payload@-"')
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
// Payload goes to a private temp file (off argv); the endpoint file is
// parsed, never sourced.
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
}
})
+10 -31
View File
@@ -15,6 +15,11 @@ import {
writeManagedScript,
type HookDefinition
} from '../agent-hooks/installer-utils'
import {
buildPosixManagedHookScript,
buildWindowsEndpointLoadLine,
buildWindowsEndpointSubroutineLines
} from '../agent-hooks/managed-hook-script'
import {
readHooksJsonRemote,
writeHooksJsonRemote,
@@ -67,46 +72,20 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
return [
'@echo off',
'setlocal',
'if defined ORCA_AGENT_HOOK_ENDPOINT if exist "%ORCA_AGENT_HOOK_ENDPOINT%" call "%ORCA_AGENT_HOOK_ENDPOINT%" 2>nul',
// Why: see claude/hook-service.ts. The endpoint file is parsed (never
// executed) to refresh live coordinates after an Orca restart.
buildWindowsEndpointLoadLine(),
'if "%ORCA_AGENT_HOOK_PORT%"=="" exit /b 0',
'if "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
buildWindowsAgentHookPostCommand('grok'),
'exit /b 0',
...buildWindowsEndpointSubroutineLines(),
''
].join('\r\n')
}
return [
'#!/bin/sh',
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Timeout caps best-effort hook posts if the local listener stalls.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/grok" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
return buildPosixManagedHookScript({ source: 'grok' })
}
function buildInstalledConfig(
+6 -4
View File
@@ -55,11 +55,13 @@ describe('KimiHookService', () => {
// The managed script must exist and POST to the Kimi hook endpoint.
const script = readFileSync(scriptPath(), 'utf-8')
expect(script).toContain('/hook/kimi')
// Why: payload is piped to curl via stdin (`payload@-`) so it never lands
// on the curl command line (EDR oversized-command-line false positive).
expect(script).toContain('printf \'%s\' "$payload" | curl')
expect(script).toContain('--data-urlencode "payload@-"')
// Why: payload posts from a private temp file and the token streams in as a
// header via stdin, so neither lands on the curl command line (also avoids
// EDR oversized-command-line false positives). Endpoint file parsed, not sourced.
expect(script).toContain('--data-urlencode "payload@${__orca_payload_file}"')
expect(script).toContain('-H @-')
expect(script).not.toContain('--data-urlencode "payload=${payload}"')
expect(script).not.toContain('. "$ORCA_AGENT_HOOK_ENDPOINT"')
// The command Kimi runs points at the managed script via sh.
expect(config).toContain('agent-hooks/kimi-hook.sh')
})
+4 -35
View File
@@ -18,6 +18,7 @@ import {
wrapPosixHookCommand,
writeManagedScript
} from '../agent-hooks/installer-utils'
import { buildPosixManagedHookScript } from '../agent-hooks/managed-hook-script'
import {
readTextFileRemote,
writeManagedScriptRemote,
@@ -57,41 +58,9 @@ function getManagedCommand(scriptPath: string): string {
}
function getManagedScript(): string {
return [
'#!/bin/sh',
// Why: refresh PORT/TOKEN/ENV/VERSION from the current Orca install so a PTY
// that survived an Orca restart still reaches the live listener. See
// claude/hook-service.ts for the full rationale.
'if [ -n "$ORCA_AGENT_HOOK_ENDPOINT" ] && [ -r "$ORCA_AGENT_HOOK_ENDPOINT" ]; then',
' . "$ORCA_AGENT_HOOK_ENDPOINT" 2>/dev/null || :',
'fi',
'if [ -z "$ORCA_AGENT_HOOK_PORT" ] || [ -z "$ORCA_AGENT_HOOK_TOKEN" ] || [ -z "$ORCA_PANE_KEY" ]; then',
' exit 0',
'fi',
'payload=$(cat)',
'if [ -z "$payload" ]; then',
' exit 0',
'fi',
// Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX
// shell is not safe once a path contains quotes or newlines. Post the raw
// hook payload plus metadata as form fields and let the receiver parse it.
// Why: pipe payload to curl's stdin (`payload@-`) instead of an inline
// `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl
// command line (EDR command-line false positives). Wire body is identical.
'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/kimi" \\',
' --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 "payload@-" >/dev/null 2>&1 || true',
'exit 0',
''
].join('\n')
// Kimi always runs a POSIX `.sh` body (Git Bash even on Windows), so the
// shared builder covers every platform here.
return buildPosixManagedHookScript({ source: 'kimi' })
}
// Returns the file text, '' when the config does not exist yet (Kimi creates it