@tracehound/core 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/dist/core/agent.d.ts +89 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +141 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/audit-chain.d.ts +39 -0
- package/dist/core/audit-chain.d.ts.map +1 -0
- package/dist/core/audit-chain.js +87 -0
- package/dist/core/audit-chain.js.map +1 -0
- package/dist/core/cold-storage.d.ts +87 -0
- package/dist/core/cold-storage.d.ts.map +1 -0
- package/dist/core/cold-storage.js +53 -0
- package/dist/core/cold-storage.js.map +1 -0
- package/dist/core/evidence-factory.d.ts +85 -0
- package/dist/core/evidence-factory.d.ts.map +1 -0
- package/dist/core/evidence-factory.js +96 -0
- package/dist/core/evidence-factory.js.map +1 -0
- package/dist/core/evidence.d.ts +48 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +135 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/fail-safe.d.ts +149 -0
- package/dist/core/fail-safe.d.ts.map +1 -0
- package/dist/core/fail-safe.js +217 -0
- package/dist/core/fail-safe.js.map +1 -0
- package/dist/core/hound-ipc.d.ts +91 -0
- package/dist/core/hound-ipc.d.ts.map +1 -0
- package/dist/core/hound-ipc.js +196 -0
- package/dist/core/hound-ipc.js.map +1 -0
- package/dist/core/hound-pool.d.ts +157 -0
- package/dist/core/hound-pool.d.ts.map +1 -0
- package/dist/core/hound-pool.js +337 -0
- package/dist/core/hound-pool.js.map +1 -0
- package/dist/core/hound-process.d.ts +14 -0
- package/dist/core/hound-process.d.ts.map +1 -0
- package/dist/core/hound-process.js +112 -0
- package/dist/core/hound-process.js.map +1 -0
- package/dist/core/hound-worker.d.ts +14 -0
- package/dist/core/hound-worker.d.ts.map +1 -0
- package/dist/core/hound-worker.js +112 -0
- package/dist/core/hound-worker.js.map +1 -0
- package/dist/core/lane-queue.d.ts +121 -0
- package/dist/core/lane-queue.d.ts.map +1 -0
- package/dist/core/lane-queue.js +181 -0
- package/dist/core/lane-queue.js.map +1 -0
- package/dist/core/license-manager.d.ts +128 -0
- package/dist/core/license-manager.d.ts.map +1 -0
- package/dist/core/license-manager.js +219 -0
- package/dist/core/license-manager.js.map +1 -0
- package/dist/core/notification-emitter.d.ts +140 -0
- package/dist/core/notification-emitter.d.ts.map +1 -0
- package/dist/core/notification-emitter.js +197 -0
- package/dist/core/notification-emitter.js.map +1 -0
- package/dist/core/process-adapter.d.ts +146 -0
- package/dist/core/process-adapter.d.ts.map +1 -0
- package/dist/core/process-adapter.js +174 -0
- package/dist/core/process-adapter.js.map +1 -0
- package/dist/core/quarantine.d.ts +95 -0
- package/dist/core/quarantine.d.ts.map +1 -0
- package/dist/core/quarantine.js +221 -0
- package/dist/core/quarantine.js.map +1 -0
- package/dist/core/rate-limiter.d.ts +94 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js +156 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/s3-cold-storage.d.ts +116 -0
- package/dist/core/s3-cold-storage.d.ts.map +1 -0
- package/dist/core/s3-cold-storage.js +198 -0
- package/dist/core/s3-cold-storage.js.map +1 -0
- package/dist/core/scheduler.d.ts +126 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +138 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/security-state.d.ts +170 -0
- package/dist/core/security-state.d.ts.map +1 -0
- package/dist/core/security-state.js +156 -0
- package/dist/core/security-state.js.map +1 -0
- package/dist/core/tier-capacity.d.ts +58 -0
- package/dist/core/tier-capacity.d.ts.map +1 -0
- package/dist/core/tier-capacity.js +89 -0
- package/dist/core/tier-capacity.js.map +1 -0
- package/dist/core/tracehound.d.ts +85 -0
- package/dist/core/tracehound.d.ts.map +1 -0
- package/dist/core/tracehound.js +90 -0
- package/dist/core/tracehound.js.map +1 -0
- package/dist/core/trust-boundary.d.ts +85 -0
- package/dist/core/trust-boundary.d.ts.map +1 -0
- package/dist/core/trust-boundary.js +71 -0
- package/dist/core/trust-boundary.js.map +1 -0
- package/dist/core/watcher.d.ts +153 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +141 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/dist/types/audit.d.ts +45 -0
- package/dist/types/audit.d.ts.map +1 -0
- package/dist/types/audit.js +5 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/common.d.ts +12 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +5 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/config.d.ts +98 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +58 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/errors.d.ts +118 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +266 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/evidence.d.ts +102 -0
- package/dist/types/evidence.d.ts.map +1 -0
- package/dist/types/evidence.js +5 -0
- package/dist/types/evidence.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +9 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/result.d.ts +62 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +34 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/scent.d.ts +55 -0
- package/dist/types/scent.d.ts.map +1 -0
- package/dist/types/scent.js +5 -0
- package/dist/types/scent.js.map +1 -0
- package/dist/types/signature.d.ts +47 -0
- package/dist/types/signature.d.ts.map +1 -0
- package/dist/types/signature.js +68 -0
- package/dist/types/signature.js.map +1 -0
- package/dist/types/threat.d.ts +38 -0
- package/dist/types/threat.d.ts.map +1 -0
- package/dist/types/threat.js +18 -0
- package/dist/types/threat.js.map +1 -0
- package/dist/utils/binary-codec.d.ts +225 -0
- package/dist/utils/binary-codec.d.ts.map +1 -0
- package/dist/utils/binary-codec.js +266 -0
- package/dist/utils/binary-codec.js.map +1 -0
- package/dist/utils/compare.d.ts +26 -0
- package/dist/utils/compare.d.ts.map +1 -0
- package/dist/utils/compare.js +44 -0
- package/dist/utils/compare.js.map +1 -0
- package/dist/utils/encode.d.ts +39 -0
- package/dist/utils/encode.d.ts.map +1 -0
- package/dist/utils/encode.js +124 -0
- package/dist/utils/encode.js.map +1 -0
- package/dist/utils/hash.d.ts +19 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +25 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/id.d.ts +20 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +47 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/runtime.d.ts +24 -0
- package/dist/utils/runtime.d.ts.map +1 -0
- package/dist/utils/runtime.js +68 -0
- package/dist/utils/runtime.js.map +1 -0
- package/dist/utils/serialize.d.ts +14 -0
- package/dist/utils/serialize.d.ts.map +1 -0
- package/dist/utils/serialize.js +27 -0
- package/dist/utils/serialize.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-Safe Panic - Threshold-triggered emergency callbacks.
|
|
3
|
+
*
|
|
4
|
+
* Provides hooks for emergency situations:
|
|
5
|
+
* - Memory threshold exceeded
|
|
6
|
+
* - Quarantine capacity critical
|
|
7
|
+
* - Error rate exceeded
|
|
8
|
+
* - Manual panic trigger
|
|
9
|
+
*
|
|
10
|
+
* DESIGN:
|
|
11
|
+
* - Panic levels: warning, critical, emergency
|
|
12
|
+
* - Each level can have multiple callbacks
|
|
13
|
+
* - Emergency triggers immediate flush and cleanup
|
|
14
|
+
* - All callbacks are non-blocking (fire-and-forget)
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Default thresholds.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_FAIL_SAFE_CONFIG = {
|
|
20
|
+
memory: {
|
|
21
|
+
warning: 0.7, // 70%
|
|
22
|
+
critical: 0.85, // 85%
|
|
23
|
+
emergency: 0.95, // 95%
|
|
24
|
+
},
|
|
25
|
+
quarantine: {
|
|
26
|
+
warning: 0.7,
|
|
27
|
+
critical: 0.85,
|
|
28
|
+
emergency: 0.95,
|
|
29
|
+
},
|
|
30
|
+
errorRate: {
|
|
31
|
+
warning: 10, // 10 errors/min
|
|
32
|
+
critical: 50, // 50 errors/min
|
|
33
|
+
emergency: 100, // 100 errors/min
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
// Implementation
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Fail-Safe Panic system.
|
|
41
|
+
*/
|
|
42
|
+
export class FailSafe {
|
|
43
|
+
config;
|
|
44
|
+
callbacks = new Map([
|
|
45
|
+
['warning', []],
|
|
46
|
+
['critical', []],
|
|
47
|
+
['emergency', []],
|
|
48
|
+
]);
|
|
49
|
+
panicHistory = [];
|
|
50
|
+
maxHistory = 100;
|
|
51
|
+
constructor(config = DEFAULT_FAIL_SAFE_CONFIG) {
|
|
52
|
+
this.config = config;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Register a callback for a panic level.
|
|
56
|
+
*
|
|
57
|
+
* @param level - Panic level to listen for
|
|
58
|
+
* @param callback - Callback function
|
|
59
|
+
*/
|
|
60
|
+
on(level, callback) {
|
|
61
|
+
this.callbacks.get(level).push(callback);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Register a callback for all panic levels.
|
|
65
|
+
*
|
|
66
|
+
* @param callback - Callback function
|
|
67
|
+
*/
|
|
68
|
+
onAny(callback) {
|
|
69
|
+
this.on('warning', callback);
|
|
70
|
+
this.on('critical', callback);
|
|
71
|
+
this.on('emergency', callback);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check memory usage and trigger panic if needed.
|
|
75
|
+
*
|
|
76
|
+
* @param usedBytes - Current memory usage
|
|
77
|
+
* @param totalBytes - Total available memory
|
|
78
|
+
*/
|
|
79
|
+
checkMemory(usedBytes, totalBytes) {
|
|
80
|
+
const ratio = usedBytes / totalBytes;
|
|
81
|
+
const level = this.determineLevel(ratio, this.config.memory);
|
|
82
|
+
if (level) {
|
|
83
|
+
this.trigger({
|
|
84
|
+
level,
|
|
85
|
+
reason: 'memory_threshold',
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
context: {
|
|
88
|
+
current: ratio,
|
|
89
|
+
threshold: this.config.memory[level],
|
|
90
|
+
details: `Memory usage: ${(ratio * 100).toFixed(1)}%`,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check quarantine capacity and trigger panic if needed.
|
|
97
|
+
*
|
|
98
|
+
* @param current - Current quarantine count
|
|
99
|
+
* @param max - Maximum quarantine capacity
|
|
100
|
+
*/
|
|
101
|
+
checkQuarantine(current, max) {
|
|
102
|
+
const ratio = current / max;
|
|
103
|
+
const level = this.determineLevel(ratio, this.config.quarantine);
|
|
104
|
+
if (level) {
|
|
105
|
+
this.trigger({
|
|
106
|
+
level,
|
|
107
|
+
reason: 'quarantine_capacity',
|
|
108
|
+
timestamp: Date.now(),
|
|
109
|
+
context: {
|
|
110
|
+
current: ratio,
|
|
111
|
+
threshold: this.config.quarantine[level],
|
|
112
|
+
details: `Quarantine: ${current}/${max}`,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check error rate and trigger panic if needed.
|
|
119
|
+
*
|
|
120
|
+
* @param errorsPerMinute - Current error rate
|
|
121
|
+
*/
|
|
122
|
+
checkErrorRate(errorsPerMinute) {
|
|
123
|
+
const level = this.determineLevel(errorsPerMinute, this.config.errorRate);
|
|
124
|
+
if (level) {
|
|
125
|
+
this.trigger({
|
|
126
|
+
level,
|
|
127
|
+
reason: 'error_rate',
|
|
128
|
+
timestamp: Date.now(),
|
|
129
|
+
context: {
|
|
130
|
+
current: errorsPerMinute,
|
|
131
|
+
threshold: this.config.errorRate[level],
|
|
132
|
+
details: `Error rate: ${errorsPerMinute}/min`,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Manually trigger a panic.
|
|
139
|
+
*
|
|
140
|
+
* @param level - Panic level
|
|
141
|
+
* @param details - Optional details
|
|
142
|
+
*/
|
|
143
|
+
panic(level, details) {
|
|
144
|
+
this.trigger({
|
|
145
|
+
level,
|
|
146
|
+
reason: 'manual',
|
|
147
|
+
timestamp: Date.now(),
|
|
148
|
+
context: details ? { details } : {},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Trigger a panic event.
|
|
153
|
+
*
|
|
154
|
+
* @param event - Panic event
|
|
155
|
+
*/
|
|
156
|
+
trigger(event) {
|
|
157
|
+
// Add to history
|
|
158
|
+
this.panicHistory.push(event);
|
|
159
|
+
if (this.panicHistory.length > this.maxHistory) {
|
|
160
|
+
this.panicHistory.shift();
|
|
161
|
+
}
|
|
162
|
+
// Fire callbacks (non-blocking)
|
|
163
|
+
const callbacks = this.callbacks.get(event.level);
|
|
164
|
+
for (const callback of callbacks) {
|
|
165
|
+
try {
|
|
166
|
+
const result = callback(event);
|
|
167
|
+
if (result instanceof Promise) {
|
|
168
|
+
result.catch(() => {
|
|
169
|
+
// Swallow async errors - fail-safe must not throw
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Swallow sync errors - fail-safe must not throw
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get panic history.
|
|
180
|
+
*/
|
|
181
|
+
get history() {
|
|
182
|
+
return this.panicHistory;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get last panic event.
|
|
186
|
+
*/
|
|
187
|
+
get lastPanic() {
|
|
188
|
+
return this.panicHistory[this.panicHistory.length - 1];
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Determine panic level based on value and thresholds.
|
|
192
|
+
*/
|
|
193
|
+
determineLevel(value, thresholds) {
|
|
194
|
+
if (value >= thresholds.emergency)
|
|
195
|
+
return 'emergency';
|
|
196
|
+
if (value >= thresholds.critical)
|
|
197
|
+
return 'critical';
|
|
198
|
+
if (value >= thresholds.warning)
|
|
199
|
+
return 'warning';
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Create a fail-safe instance.
|
|
205
|
+
*
|
|
206
|
+
* @param config - Optional configuration
|
|
207
|
+
* @returns FailSafe instance
|
|
208
|
+
*/
|
|
209
|
+
export function createFailSafe(config) {
|
|
210
|
+
const mergedConfig = {
|
|
211
|
+
memory: { ...DEFAULT_FAIL_SAFE_CONFIG.memory, ...config?.memory },
|
|
212
|
+
quarantine: { ...DEFAULT_FAIL_SAFE_CONFIG.quarantine, ...config?.quarantine },
|
|
213
|
+
errorRate: { ...DEFAULT_FAIL_SAFE_CONFIG.errorRate, ...config?.errorRate },
|
|
214
|
+
};
|
|
215
|
+
return new FailSafe(mergedConfig);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=fail-safe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fail-safe.js","sourceRoot":"","sources":["../../src/core/fail-safe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hound IPC - Binary length-prefixed protocol for child process communication.
|
|
3
|
+
*
|
|
4
|
+
* RFC-0000 REQUIREMENTS:
|
|
5
|
+
* - Length-prefixed binary over stdio
|
|
6
|
+
* - JSON encoding is explicitly forbidden
|
|
7
|
+
* - No retry semantics
|
|
8
|
+
* - Fire-and-forget
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Message types that can be sent over IPC.
|
|
12
|
+
* Matches RFC-0000 HoundMessage types.
|
|
13
|
+
*/
|
|
14
|
+
export type HoundMessageType = 'status' | 'metrics';
|
|
15
|
+
export type HoundStatus = 'processing' | 'complete' | 'error';
|
|
16
|
+
export interface HoundStatusMessage {
|
|
17
|
+
type: 'status';
|
|
18
|
+
state: HoundStatus;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface HoundMetricsMessage {
|
|
22
|
+
type: 'metrics';
|
|
23
|
+
processingTime: number;
|
|
24
|
+
memoryUsed: number;
|
|
25
|
+
}
|
|
26
|
+
export type HoundMessage = HoundStatusMessage | HoundMetricsMessage;
|
|
27
|
+
/**
|
|
28
|
+
* Parsed message from IPC stream.
|
|
29
|
+
*/
|
|
30
|
+
export interface ParsedMessage {
|
|
31
|
+
payload: ArrayBuffer;
|
|
32
|
+
bytesConsumed: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Encode a payload with length prefix.
|
|
36
|
+
*
|
|
37
|
+
* Format: [4 bytes length BE][N bytes payload]
|
|
38
|
+
*
|
|
39
|
+
* @param payload - Raw payload bytes
|
|
40
|
+
* @returns Length-prefixed buffer
|
|
41
|
+
*/
|
|
42
|
+
export declare function encodeMessage(payload: ArrayBuffer): Buffer;
|
|
43
|
+
/**
|
|
44
|
+
* Encode a HoundMessage to binary.
|
|
45
|
+
*
|
|
46
|
+
* @param message - Message to encode
|
|
47
|
+
* @returns Length-prefixed buffer
|
|
48
|
+
*/
|
|
49
|
+
export declare function encodeHoundMessage(message: HoundMessage): Buffer;
|
|
50
|
+
/**
|
|
51
|
+
* Try to parse a message from a buffer.
|
|
52
|
+
* Handles partial buffers gracefully.
|
|
53
|
+
*
|
|
54
|
+
* @param buffer - Input buffer (may contain partial message)
|
|
55
|
+
* @returns Parsed message or null if incomplete
|
|
56
|
+
*/
|
|
57
|
+
export declare function tryParseMessage(buffer: Buffer): ParsedMessage | null;
|
|
58
|
+
/**
|
|
59
|
+
* Decode a HoundMessage from binary payload.
|
|
60
|
+
*
|
|
61
|
+
* @param payload - Raw payload bytes (without length prefix)
|
|
62
|
+
* @returns Decoded message
|
|
63
|
+
*/
|
|
64
|
+
export declare function decodeHoundMessage(payload: ArrayBuffer): HoundMessage;
|
|
65
|
+
/**
|
|
66
|
+
* Streaming message parser.
|
|
67
|
+
* Handles partial buffers and backpressure.
|
|
68
|
+
*/
|
|
69
|
+
export interface MessageParser {
|
|
70
|
+
/**
|
|
71
|
+
* Feed data into parser.
|
|
72
|
+
* @param chunk - New data chunk
|
|
73
|
+
* @returns Array of complete messages
|
|
74
|
+
*/
|
|
75
|
+
feed(chunk: Buffer): ArrayBuffer[];
|
|
76
|
+
/**
|
|
77
|
+
* Get remaining buffered bytes.
|
|
78
|
+
*/
|
|
79
|
+
readonly bufferedBytes: number;
|
|
80
|
+
/**
|
|
81
|
+
* Reset parser state.
|
|
82
|
+
*/
|
|
83
|
+
reset(): void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a streaming message parser.
|
|
87
|
+
*
|
|
88
|
+
* @returns Message parser instance
|
|
89
|
+
*/
|
|
90
|
+
export declare function createMessageParser(): MessageParser;
|
|
91
|
+
//# sourceMappingURL=hound-ipc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hound-ipc.d.ts","sourceRoot":"","sources":["../../src/core/hound-ipc.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEnD,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,CAAA;AAE7D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,WAAW,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,mBAAmB,CAAA;AAEnE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAgBD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAiB1D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CA8BhE;AAmBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA2BpE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAqCrE;AAmBD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAAA;IAElC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAE9B;;OAEG;IACH,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CA8BnD"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hound IPC - Binary length-prefixed protocol for child process communication.
|
|
3
|
+
*
|
|
4
|
+
* RFC-0000 REQUIREMENTS:
|
|
5
|
+
* - Length-prefixed binary over stdio
|
|
6
|
+
* - JSON encoding is explicitly forbidden
|
|
7
|
+
* - No retry semantics
|
|
8
|
+
* - Fire-and-forget
|
|
9
|
+
*/
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
// Constants
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
/** Length prefix size in bytes (32-bit BE) */
|
|
14
|
+
const LENGTH_PREFIX_SIZE = 4;
|
|
15
|
+
/** Maximum message size (1MB) */
|
|
16
|
+
const MAX_MESSAGE_SIZE = 1024 * 1024;
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
// Encoding
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Encode a payload with length prefix.
|
|
22
|
+
*
|
|
23
|
+
* Format: [4 bytes length BE][N bytes payload]
|
|
24
|
+
*
|
|
25
|
+
* @param payload - Raw payload bytes
|
|
26
|
+
* @returns Length-prefixed buffer
|
|
27
|
+
*/
|
|
28
|
+
export function encodeMessage(payload) {
|
|
29
|
+
const payloadBuffer = Buffer.from(payload);
|
|
30
|
+
const length = payloadBuffer.length;
|
|
31
|
+
if (length > MAX_MESSAGE_SIZE) {
|
|
32
|
+
throw new Error(`Message too large: ${length} > ${MAX_MESSAGE_SIZE}`);
|
|
33
|
+
}
|
|
34
|
+
const result = Buffer.allocUnsafe(LENGTH_PREFIX_SIZE + length);
|
|
35
|
+
// Write length as 32-bit BE
|
|
36
|
+
result.writeUInt32BE(length, 0);
|
|
37
|
+
// Copy payload
|
|
38
|
+
payloadBuffer.copy(result, LENGTH_PREFIX_SIZE);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Encode a HoundMessage to binary.
|
|
43
|
+
*
|
|
44
|
+
* @param message - Message to encode
|
|
45
|
+
* @returns Length-prefixed buffer
|
|
46
|
+
*/
|
|
47
|
+
export function encodeHoundMessage(message) {
|
|
48
|
+
// Use minimal binary encoding (not JSON)
|
|
49
|
+
// Format: [1 byte type][payload bytes]
|
|
50
|
+
if (message.type === 'status') {
|
|
51
|
+
// Type 0x01 = status
|
|
52
|
+
// [1 byte type][1 byte state][optional error string]
|
|
53
|
+
const stateCode = encodeStatusState(message.state);
|
|
54
|
+
const errorBytes = message.error ? Buffer.from(message.error, 'utf8') : Buffer.alloc(0);
|
|
55
|
+
const payload = Buffer.allocUnsafe(2 + errorBytes.length);
|
|
56
|
+
payload[0] = 0x01; // type: status
|
|
57
|
+
payload[1] = stateCode;
|
|
58
|
+
errorBytes.copy(payload, 2);
|
|
59
|
+
return encodeMessage(payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.length));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Type 0x02 = metrics
|
|
63
|
+
// [1 byte type][8 bytes processingTime][8 bytes memoryUsed]
|
|
64
|
+
const payload = Buffer.allocUnsafe(17);
|
|
65
|
+
payload[0] = 0x02; // type: metrics
|
|
66
|
+
payload.writeDoubleBE(message.processingTime, 1);
|
|
67
|
+
payload.writeDoubleBE(message.memoryUsed, 9);
|
|
68
|
+
return encodeMessage(payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.length));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function encodeStatusState(state) {
|
|
72
|
+
switch (state) {
|
|
73
|
+
case 'processing':
|
|
74
|
+
return 0x01;
|
|
75
|
+
case 'complete':
|
|
76
|
+
return 0x02;
|
|
77
|
+
case 'error':
|
|
78
|
+
return 0x03;
|
|
79
|
+
default:
|
|
80
|
+
return 0x00;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
84
|
+
// Decoding
|
|
85
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Try to parse a message from a buffer.
|
|
88
|
+
* Handles partial buffers gracefully.
|
|
89
|
+
*
|
|
90
|
+
* @param buffer - Input buffer (may contain partial message)
|
|
91
|
+
* @returns Parsed message or null if incomplete
|
|
92
|
+
*/
|
|
93
|
+
export function tryParseMessage(buffer) {
|
|
94
|
+
// Need at least length prefix
|
|
95
|
+
if (buffer.length < LENGTH_PREFIX_SIZE) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const length = buffer.readUInt32BE(0);
|
|
99
|
+
// Validate length
|
|
100
|
+
if (length > MAX_MESSAGE_SIZE) {
|
|
101
|
+
throw new Error(`Invalid message length: ${length}`);
|
|
102
|
+
}
|
|
103
|
+
const totalSize = LENGTH_PREFIX_SIZE + length;
|
|
104
|
+
// Check if we have full message
|
|
105
|
+
if (buffer.length < totalSize) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
// Extract payload
|
|
109
|
+
const payloadSlice = buffer.subarray(LENGTH_PREFIX_SIZE, totalSize);
|
|
110
|
+
return {
|
|
111
|
+
payload: new Uint8Array(payloadSlice).buffer,
|
|
112
|
+
bytesConsumed: totalSize,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Decode a HoundMessage from binary payload.
|
|
117
|
+
*
|
|
118
|
+
* @param payload - Raw payload bytes (without length prefix)
|
|
119
|
+
* @returns Decoded message
|
|
120
|
+
*/
|
|
121
|
+
export function decodeHoundMessage(payload) {
|
|
122
|
+
const buffer = Buffer.from(payload);
|
|
123
|
+
if (buffer.length < 1) {
|
|
124
|
+
throw new Error('Empty message payload');
|
|
125
|
+
}
|
|
126
|
+
const type = buffer[0];
|
|
127
|
+
if (type === 0x01) {
|
|
128
|
+
// Status message
|
|
129
|
+
if (buffer.length < 2) {
|
|
130
|
+
throw new Error('Invalid status message: too short');
|
|
131
|
+
}
|
|
132
|
+
const stateCode = buffer[1] ?? 0;
|
|
133
|
+
const state = decodeStatusState(stateCode);
|
|
134
|
+
const errorStr = buffer.length > 2 ? buffer.subarray(2).toString('utf8') : undefined;
|
|
135
|
+
const result = { type: 'status', state };
|
|
136
|
+
if (errorStr) {
|
|
137
|
+
result.error = errorStr;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
else if (type === 0x02) {
|
|
142
|
+
// Metrics message
|
|
143
|
+
if (buffer.length < 17) {
|
|
144
|
+
throw new Error('Invalid metrics message: too short');
|
|
145
|
+
}
|
|
146
|
+
const processingTime = buffer.readDoubleBE(1);
|
|
147
|
+
const memoryUsed = buffer.readDoubleBE(9);
|
|
148
|
+
return { type: 'metrics', processingTime, memoryUsed };
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
throw new Error(`Unknown message type: ${type}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function decodeStatusState(code) {
|
|
155
|
+
switch (code) {
|
|
156
|
+
case 0x01:
|
|
157
|
+
return 'processing';
|
|
158
|
+
case 0x02:
|
|
159
|
+
return 'complete';
|
|
160
|
+
case 0x03:
|
|
161
|
+
return 'error';
|
|
162
|
+
default:
|
|
163
|
+
throw new Error(`Unknown status state: ${code}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Create a streaming message parser.
|
|
168
|
+
*
|
|
169
|
+
* @returns Message parser instance
|
|
170
|
+
*/
|
|
171
|
+
export function createMessageParser() {
|
|
172
|
+
let buffer = Buffer.alloc(0);
|
|
173
|
+
return {
|
|
174
|
+
feed(chunk) {
|
|
175
|
+
// Append chunk to buffer
|
|
176
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
177
|
+
const messages = [];
|
|
178
|
+
// Parse all complete messages
|
|
179
|
+
while (true) {
|
|
180
|
+
const result = tryParseMessage(buffer);
|
|
181
|
+
if (!result)
|
|
182
|
+
break;
|
|
183
|
+
messages.push(result.payload);
|
|
184
|
+
buffer = buffer.subarray(result.bytesConsumed);
|
|
185
|
+
}
|
|
186
|
+
return messages;
|
|
187
|
+
},
|
|
188
|
+
get bufferedBytes() {
|
|
189
|
+
return buffer.length;
|
|
190
|
+
},
|
|
191
|
+
reset() {
|
|
192
|
+
buffer = Buffer.alloc(0);
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=hound-ipc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hound-ipc.js","sourceRoot":"","sources":["../../src/core/hound-ipc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hound Pool - Process-based isolation pool for evidence processing.
|
|
3
|
+
*
|
|
4
|
+
* RFC-0000 CRITICAL INVARIANTS:
|
|
5
|
+
* - activate() returns void, NOT Promise (Agent NEVER awaits)
|
|
6
|
+
* - OS-level process isolation (child processes, not threads)
|
|
7
|
+
* - Pre-spawned processes with jittered rotation
|
|
8
|
+
* - Timeout + SIGKILL for stuck processes
|
|
9
|
+
* - Binary IPC over stdio (no JSON)
|
|
10
|
+
*
|
|
11
|
+
* INVARIANT: activeProcesses <= totalProcesses
|
|
12
|
+
* activeProcesses reflects OS-level active child processes
|
|
13
|
+
*/
|
|
14
|
+
import type { Evidence } from './evidence.js';
|
|
15
|
+
import { createMockAdapter, type HoundProcessConstraints, type IHoundProcessAdapter } from './process-adapter.js';
|
|
16
|
+
/**
|
|
17
|
+
* Hound execution result.
|
|
18
|
+
* Delivered via internal queue, NOT via Promise to Agent.
|
|
19
|
+
*/
|
|
20
|
+
export interface HoundResult {
|
|
21
|
+
/** Evidence signature */
|
|
22
|
+
signature: string;
|
|
23
|
+
/** Execution status */
|
|
24
|
+
status: 'processed' | 'timeout' | 'error';
|
|
25
|
+
/** Processing duration in ms */
|
|
26
|
+
durationMs: number;
|
|
27
|
+
/** Process ID that processed this */
|
|
28
|
+
processId: string;
|
|
29
|
+
/** Error message if status is 'error' */
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Hound pool statistics (immutable snapshot).
|
|
34
|
+
*
|
|
35
|
+
* INVARIANT: activeProcesses <= totalProcesses
|
|
36
|
+
*/
|
|
37
|
+
export interface HoundPoolStats {
|
|
38
|
+
/** Number of active processes (OS-level) */
|
|
39
|
+
activeProcesses: number;
|
|
40
|
+
/** Total processes in pool */
|
|
41
|
+
totalProcesses: number;
|
|
42
|
+
/** Total activations */
|
|
43
|
+
totalActivations: number;
|
|
44
|
+
/** Total timeouts */
|
|
45
|
+
totalTimeouts: number;
|
|
46
|
+
/** Total errors */
|
|
47
|
+
totalErrors: number;
|
|
48
|
+
/** Average processing time in ms */
|
|
49
|
+
avgProcessingMs: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pool exhaustion action.
|
|
53
|
+
*/
|
|
54
|
+
export type PoolExhaustedAction = 'drop' | 'escalate' | 'defer';
|
|
55
|
+
/**
|
|
56
|
+
* Hound pool configuration.
|
|
57
|
+
*/
|
|
58
|
+
export interface HoundPoolConfig {
|
|
59
|
+
/** Number of pre-spawned processes */
|
|
60
|
+
poolSize: number;
|
|
61
|
+
/** Timeout per process in ms */
|
|
62
|
+
timeout: number;
|
|
63
|
+
/** Jitter range for rotation in ms */
|
|
64
|
+
rotationJitterMs: number;
|
|
65
|
+
/** Action when pool exhausted (default: 'drop') */
|
|
66
|
+
onPoolExhausted?: PoolExhaustedAction;
|
|
67
|
+
/** Max queue size for 'defer' action (default: 100) */
|
|
68
|
+
deferQueueLimit?: number;
|
|
69
|
+
/** Process constraints (declarative, best-effort) */
|
|
70
|
+
processConstraints?: Partial<HoundProcessConstraints>;
|
|
71
|
+
/** Path to hound process script */
|
|
72
|
+
processScriptPath?: string;
|
|
73
|
+
/** Custom process adapter (for testing) */
|
|
74
|
+
adapter?: IHoundProcessAdapter;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Hound pool interface.
|
|
78
|
+
*
|
|
79
|
+
* CRITICAL: activate() returns void.
|
|
80
|
+
* Agent NEVER awaits Hound Pool.
|
|
81
|
+
*/
|
|
82
|
+
export interface IHoundPool {
|
|
83
|
+
/**
|
|
84
|
+
* Activate hound for evidence processing.
|
|
85
|
+
* Returns immediately. Result via onResult callback.
|
|
86
|
+
*
|
|
87
|
+
* @param evidence - Evidence to process
|
|
88
|
+
*/
|
|
89
|
+
activate(evidence: Evidence): void;
|
|
90
|
+
/**
|
|
91
|
+
* Force terminate specific hound by signature.
|
|
92
|
+
*
|
|
93
|
+
* @param signature - Evidence signature to terminate
|
|
94
|
+
*/
|
|
95
|
+
terminate(signature: string): void;
|
|
96
|
+
/**
|
|
97
|
+
* Get pool statistics (immutable snapshot).
|
|
98
|
+
*/
|
|
99
|
+
readonly stats: Readonly<HoundPoolStats>;
|
|
100
|
+
/**
|
|
101
|
+
* Register result handler.
|
|
102
|
+
* Internal use only - NOT exposed to Agent.
|
|
103
|
+
*/
|
|
104
|
+
onResult(handler: (result: HoundResult) => void): void;
|
|
105
|
+
/**
|
|
106
|
+
* Shutdown all processes.
|
|
107
|
+
*/
|
|
108
|
+
shutdown(): void;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Hound Pool implementation.
|
|
112
|
+
*
|
|
113
|
+
* Uses child process isolation per RFC-0000 amendment.
|
|
114
|
+
*/
|
|
115
|
+
export declare class HoundPool implements IHoundPool {
|
|
116
|
+
private readonly config;
|
|
117
|
+
private readonly processes;
|
|
118
|
+
private readonly pendingQueue;
|
|
119
|
+
private readonly resultHandlers;
|
|
120
|
+
private readonly processingTimes;
|
|
121
|
+
private readonly adapter;
|
|
122
|
+
private readonly processScriptPath;
|
|
123
|
+
private readonly onPoolExhausted;
|
|
124
|
+
private readonly deferQueueLimit;
|
|
125
|
+
private _totalActivations;
|
|
126
|
+
private _totalTimeouts;
|
|
127
|
+
private _totalErrors;
|
|
128
|
+
constructor(config: HoundPoolConfig);
|
|
129
|
+
/**
|
|
130
|
+
* Process constraints (declarative, best-effort).
|
|
131
|
+
*/
|
|
132
|
+
static readonly DEFAULT_CONSTRAINTS: Readonly<HoundProcessConstraints>;
|
|
133
|
+
activate(evidence: Evidence): void;
|
|
134
|
+
private handlePoolExhausted;
|
|
135
|
+
terminate(signature: string): void;
|
|
136
|
+
get stats(): Readonly<HoundPoolStats>;
|
|
137
|
+
onResult(handler: (result: HoundResult) => void): void;
|
|
138
|
+
shutdown(): void;
|
|
139
|
+
private createProcessState;
|
|
140
|
+
private findAvailableProcess;
|
|
141
|
+
private assignToProcess;
|
|
142
|
+
private handleProcessMessage;
|
|
143
|
+
private handleProcessExit;
|
|
144
|
+
private handleTimeout;
|
|
145
|
+
private terminateProcess;
|
|
146
|
+
private completeProcessing;
|
|
147
|
+
private processNextInQueue;
|
|
148
|
+
private emitResult;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create a Hound Pool instance.
|
|
152
|
+
*
|
|
153
|
+
* @param config - Pool configuration
|
|
154
|
+
*/
|
|
155
|
+
export declare function createHoundPool(config: HoundPoolConfig): IHoundPool;
|
|
156
|
+
export { createMockAdapter };
|
|
157
|
+
//# sourceMappingURL=hound-pool.d.ts.map
|