-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Expand file tree
/
Copy pathsave-hook.ts
More file actions
86 lines (72 loc) · 2.44 KB
/
Copy pathsave-hook.ts
File metadata and controls
86 lines (72 loc) · 2.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* Save Hook - PostToolUse
*
* Pure HTTP client - sends data to worker, worker handles all database operations
* including privacy checks. This allows the hook to run under any runtime
* (Node.js or Bun) since it has no native module dependencies.
*/
import { stdin } from 'process';
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
import { logger } from '../utils/logger.js';
import { ensureWorkerRunning, getWorkerPort, fetchWithRetry } from '../shared/worker-utils.js';
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
export interface PostToolUseInput {
session_id: string;
cwd: string;
tool_name: string;
tool_input: any;
tool_response: any;
}
/**
* Save Hook Main Logic - Fire-and-forget HTTP client
*/
async function saveHook(input?: PostToolUseInput): Promise<void> {
// Ensure worker is running before any other logic
await ensureWorkerRunning();
if (!input) {
throw new Error('saveHook requires input');
}
const { session_id, cwd, tool_name, tool_input, tool_response } = input;
const port = getWorkerPort();
const toolStr = logger.formatTool(tool_name, tool_input);
logger.dataIn('HOOK', `PostToolUse: ${toolStr}`, {
workerPort: port
});
// Validate required fields before sending to worker
if (!cwd) {
throw new Error(`Missing cwd in PostToolUse hook input for session ${session_id}, tool ${tool_name}`);
}
// Send to worker - worker handles privacy check and database operations
// Uses fetchWithRetry to handle transient network errors like ECONNRESET
const response = await fetchWithRetry(
`http://127.0.0.1:${port}/api/sessions/observations`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contentSessionId: session_id,
tool_name,
tool_input,
tool_response,
cwd
})
}
);
if (!response.ok) {
throw new Error(`Observation storage failed: ${response.status}`);
}
logger.debug('HOOK', 'Observation sent successfully', { toolName: tool_name });
console.log(STANDARD_HOOK_RESPONSE);
}
// Entry Point
let input = '';
stdin.on('data', (chunk) => input += chunk);
stdin.on('end', async () => {
let parsed: PostToolUseInput | undefined;
try {
parsed = input ? JSON.parse(input) : undefined;
} catch (error) {
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
}
await saveHook(parsed);
});