reflexive 0.1.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/CLAUDE.md +77 -0
- package/FAILURES.md +245 -0
- package/README.md +264 -0
- package/Screenshot 2026-01-22 at 6.31.27/342/200/257AM.png +0 -0
- package/dashboard.html +620 -0
- package/demo-ai-features.js +571 -0
- package/demo-app.js +210 -0
- package/demo-inject.js +212 -0
- package/demo-instrumented.js +272 -0
- package/docs/BREAKPOINT-AUDIT.md +293 -0
- package/docs/GENESIS.md +110 -0
- package/docs/HN-LAUNCH-PLAN-V2.md +631 -0
- package/docs/HN-LAUNCH-PLAN.md +492 -0
- package/docs/TODO.md +69 -0
- package/docs/V8-INSPECTOR-RESEARCH.md +1231 -0
- package/logo-carbon.png +0 -0
- package/logo0.jpg +0 -0
- package/logo1.jpg +0 -0
- package/logo2.jpg +0 -0
- package/new-ui-template.html +435 -0
- package/one-shot.js +1109 -0
- package/package.json +47 -0
- package/play-story.sh +10 -0
- package/src/demo-inject.js +3 -0
- package/src/inject.cjs +474 -0
- package/src/reflexive.js +6214 -0
- package/story-game-reflexive.js +1246 -0
- package/story-game-web.js +1030 -0
- package/story-mystery-1769171430377.js +162 -0
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "reflexive",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-powered introspection for Node.js apps. Monitor external processes or instrument your own with Claude Agent SDK.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/reflexive.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"reflexive": "./src/reflexive.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/reflexive.js",
|
|
12
|
+
"./inject": "./src/inject.cjs"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"demo": "node demo-instrumented.js",
|
|
16
|
+
"demo:app": "node src/reflexive.js demo-app.js",
|
|
17
|
+
"demo:inject": "node src/reflexive.js --inject demo-inject.js",
|
|
18
|
+
"demo:eval": "node src/reflexive.js --eval demo-inject.js",
|
|
19
|
+
"demo:debug": "node src/reflexive.js --debug demo-app.js",
|
|
20
|
+
"demo:ai": "node demo-ai-features.js",
|
|
21
|
+
"demo:ai:inject": "node src/reflexive.js --inject demo-ai-features.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"ai",
|
|
25
|
+
"claude",
|
|
26
|
+
"anthropic",
|
|
27
|
+
"introspection",
|
|
28
|
+
"debugging",
|
|
29
|
+
"agent",
|
|
30
|
+
"reflexive",
|
|
31
|
+
"llm",
|
|
32
|
+
"claude-code",
|
|
33
|
+
"claude-agent-sdk",
|
|
34
|
+
"cli"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
|
|
40
|
+
"express": "^5.2.1",
|
|
41
|
+
"ws": "^8.19.0",
|
|
42
|
+
"zod": "^3.24.1"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/play-story.sh
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
echo "š® Launching Reflexive AI Story Game..."
|
|
3
|
+
echo ""
|
|
4
|
+
echo "š” Reflexive Dashboard will be at: http://localhost:3100"
|
|
5
|
+
echo "šØ Story AI Dashboard at: http://localhost:3098/reflexive"
|
|
6
|
+
echo ""
|
|
7
|
+
echo "Press Ctrl+C to exit"
|
|
8
|
+
echo ""
|
|
9
|
+
|
|
10
|
+
npx reflexive -i story-game-reflexive.js
|
package/src/inject.cjs
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reflexive Injection Module
|
|
3
|
+
*
|
|
4
|
+
* This module is injected into child processes via --require
|
|
5
|
+
* It provides deep instrumentation without the app needing to import reflexive.
|
|
6
|
+
*
|
|
7
|
+
* Usage: node --require reflexive/inject ./app.js
|
|
8
|
+
* Or via CLI: reflexive --inject ./app.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Only run if we're in a child process spawned by reflexive
|
|
12
|
+
if (!process.send || !process.env.REFLEXIVE_INJECT) {
|
|
13
|
+
// Not running under reflexive, silently no-op
|
|
14
|
+
module.exports = {};
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const originalConsole = {
|
|
19
|
+
log: console.log.bind(console),
|
|
20
|
+
info: console.info.bind(console),
|
|
21
|
+
warn: console.warn.bind(console),
|
|
22
|
+
error: console.error.bind(console),
|
|
23
|
+
debug: console.debug.bind(console)
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Send message to parent reflexive process
|
|
27
|
+
function sendToParent(type, data) {
|
|
28
|
+
try {
|
|
29
|
+
process.send({ reflexive: true, type, data, timestamp: Date.now() });
|
|
30
|
+
} catch (e) {
|
|
31
|
+
// Parent may have disconnected
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Intercept console methods
|
|
36
|
+
function interceptConsole() {
|
|
37
|
+
console.log = (...args) => {
|
|
38
|
+
sendToParent('log', { level: 'info', message: args.map(String).join(' ') });
|
|
39
|
+
originalConsole.log(...args);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
console.info = (...args) => {
|
|
43
|
+
sendToParent('log', { level: 'info', message: args.map(String).join(' ') });
|
|
44
|
+
originalConsole.info(...args);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
console.warn = (...args) => {
|
|
48
|
+
sendToParent('log', { level: 'warn', message: args.map(String).join(' ') });
|
|
49
|
+
originalConsole.warn(...args);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
console.error = (...args) => {
|
|
53
|
+
sendToParent('log', { level: 'error', message: args.map(String).join(' ') });
|
|
54
|
+
originalConsole.error(...args);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
console.debug = (...args) => {
|
|
58
|
+
sendToParent('log', { level: 'debug', message: args.map(String).join(' ') });
|
|
59
|
+
originalConsole.debug(...args);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Capture uncaught exceptions and rejections
|
|
64
|
+
function interceptErrors() {
|
|
65
|
+
process.on('uncaughtException', (err) => {
|
|
66
|
+
sendToParent('error', {
|
|
67
|
+
type: 'uncaughtException',
|
|
68
|
+
message: err.message,
|
|
69
|
+
stack: err.stack,
|
|
70
|
+
name: err.name
|
|
71
|
+
});
|
|
72
|
+
// Print error and exit gracefully instead of re-throwing
|
|
73
|
+
// (re-throwing adds inject.cjs to the stack trace which is confusing)
|
|
74
|
+
originalConsole.error('\n' + err.stack);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
79
|
+
sendToParent('error', {
|
|
80
|
+
type: 'unhandledRejection',
|
|
81
|
+
message: reason?.message || String(reason),
|
|
82
|
+
stack: reason?.stack,
|
|
83
|
+
name: reason?.name
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Set up diagnostics_channel subscriptions if available
|
|
89
|
+
function setupDiagnostics() {
|
|
90
|
+
let dc;
|
|
91
|
+
try {
|
|
92
|
+
dc = require('diagnostics_channel');
|
|
93
|
+
} catch (e) {
|
|
94
|
+
return; // Not available in this Node version
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// HTTP client requests
|
|
98
|
+
const httpClientStart = dc.channel('http.client.request.start');
|
|
99
|
+
if (httpClientStart.hasSubscribers !== false) {
|
|
100
|
+
httpClientStart.subscribe((message) => {
|
|
101
|
+
sendToParent('diagnostic', {
|
|
102
|
+
channel: 'http.client.request.start',
|
|
103
|
+
request: {
|
|
104
|
+
method: message.request?.method,
|
|
105
|
+
host: message.request?.host,
|
|
106
|
+
path: message.request?.path
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// HTTP server requests
|
|
113
|
+
const httpServerStart = dc.channel('http.server.request.start');
|
|
114
|
+
if (httpServerStart.hasSubscribers !== false) {
|
|
115
|
+
httpServerStart.subscribe((message) => {
|
|
116
|
+
sendToParent('diagnostic', {
|
|
117
|
+
channel: 'http.server.request.start',
|
|
118
|
+
request: {
|
|
119
|
+
method: message.request?.method,
|
|
120
|
+
url: message.request?.url
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Set up perf_hooks for GC and event loop stats
|
|
128
|
+
function setupPerfHooks() {
|
|
129
|
+
let perf;
|
|
130
|
+
try {
|
|
131
|
+
perf = require('perf_hooks');
|
|
132
|
+
} catch (e) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// GC stats
|
|
137
|
+
const obs = new perf.PerformanceObserver((list) => {
|
|
138
|
+
for (const entry of list.getEntries()) {
|
|
139
|
+
if (entry.entryType === 'gc') {
|
|
140
|
+
sendToParent('perf', {
|
|
141
|
+
type: 'gc',
|
|
142
|
+
kind: entry.detail?.kind,
|
|
143
|
+
duration: entry.duration,
|
|
144
|
+
flags: entry.detail?.flags
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
obs.observe({ entryTypes: ['gc'] });
|
|
152
|
+
} catch (e) {
|
|
153
|
+
// GC observation might not be available
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Event loop utilization (periodic)
|
|
157
|
+
if (perf.monitorEventLoopDelay) {
|
|
158
|
+
const h = perf.monitorEventLoopDelay({ resolution: 20 });
|
|
159
|
+
h.enable();
|
|
160
|
+
|
|
161
|
+
setInterval(() => {
|
|
162
|
+
sendToParent('perf', {
|
|
163
|
+
type: 'eventLoop',
|
|
164
|
+
min: h.min,
|
|
165
|
+
max: h.max,
|
|
166
|
+
mean: h.mean,
|
|
167
|
+
stddev: h.stddev,
|
|
168
|
+
p50: h.percentile(50),
|
|
169
|
+
p99: h.percentile(99)
|
|
170
|
+
});
|
|
171
|
+
h.reset();
|
|
172
|
+
}, 10000).unref(); // Don't keep process alive
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Breakpoint management
|
|
177
|
+
const breakpoints = new Map();
|
|
178
|
+
let breakpointIdCounter = 0;
|
|
179
|
+
let activeBreakpoint = null;
|
|
180
|
+
let breakpointResolve = null;
|
|
181
|
+
|
|
182
|
+
// Create process.reflexive API
|
|
183
|
+
function createReflexiveAPI() {
|
|
184
|
+
const state = {};
|
|
185
|
+
|
|
186
|
+
process.reflexive = {
|
|
187
|
+
// Set custom state that the agent can query
|
|
188
|
+
setState(key, value) {
|
|
189
|
+
state[key] = value;
|
|
190
|
+
sendToParent('state', { key, value });
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// Get current state
|
|
194
|
+
getState(key) {
|
|
195
|
+
return key ? state[key] : { ...state };
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
// Log with custom level
|
|
199
|
+
log(level, message, meta = {}) {
|
|
200
|
+
sendToParent('log', { level, message, meta });
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// Emit custom event
|
|
204
|
+
emit(event, data) {
|
|
205
|
+
sendToParent('event', { event, data });
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// Set a breakpoint that pauses execution until resumed by the agent
|
|
209
|
+
async breakpoint(label = 'breakpoint', context = {}) {
|
|
210
|
+
const id = ++breakpointIdCounter;
|
|
211
|
+
const stack = new Error().stack.split('\n').slice(2).join('\n');
|
|
212
|
+
|
|
213
|
+
activeBreakpoint = { id, label, context, stack, timestamp: Date.now() };
|
|
214
|
+
|
|
215
|
+
sendToParent('breakpoint', {
|
|
216
|
+
action: 'hit',
|
|
217
|
+
id,
|
|
218
|
+
label,
|
|
219
|
+
context: serializeResult(context),
|
|
220
|
+
stack,
|
|
221
|
+
state: process.reflexive.getState()
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
originalConsole.log(`\nš“ BREAKPOINT [${label}] - Execution paused. Waiting for agent to resume...\n`);
|
|
225
|
+
|
|
226
|
+
// Wait for resume signal from parent
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
breakpointResolve = resolve;
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// List all breakpoints (for programmatic use)
|
|
233
|
+
getBreakpoints() {
|
|
234
|
+
return Array.from(breakpoints.values());
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
// Mark a span for tracing
|
|
238
|
+
span(name, fn) {
|
|
239
|
+
const start = Date.now();
|
|
240
|
+
sendToParent('span', { name, phase: 'start', timestamp: start });
|
|
241
|
+
|
|
242
|
+
const finish = (error) => {
|
|
243
|
+
const end = Date.now();
|
|
244
|
+
sendToParent('span', {
|
|
245
|
+
name,
|
|
246
|
+
phase: 'end',
|
|
247
|
+
timestamp: end,
|
|
248
|
+
duration: end - start,
|
|
249
|
+
error: error?.message
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
if (fn.constructor.name === 'AsyncFunction') {
|
|
254
|
+
return fn().then(
|
|
255
|
+
(result) => { finish(); return result; },
|
|
256
|
+
(error) => { finish(error); throw error; }
|
|
257
|
+
);
|
|
258
|
+
} else {
|
|
259
|
+
try {
|
|
260
|
+
const result = fn();
|
|
261
|
+
finish();
|
|
262
|
+
return result;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
finish(error);
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Handle messages from parent
|
|
273
|
+
function setupParentMessageHandler() {
|
|
274
|
+
process.on('message', (msg) => {
|
|
275
|
+
if (!msg || !msg.reflexive) return;
|
|
276
|
+
|
|
277
|
+
switch (msg.type) {
|
|
278
|
+
case 'getState':
|
|
279
|
+
sendToParent('stateResponse', { state: process.reflexive.getState() });
|
|
280
|
+
break;
|
|
281
|
+
|
|
282
|
+
case 'eval':
|
|
283
|
+
// Execute code in the app context
|
|
284
|
+
// DANGEROUS: Only enabled with explicit --eval flag
|
|
285
|
+
if (!process.env.REFLEXIVE_EVAL) {
|
|
286
|
+
sendToParent('evalResponse', {
|
|
287
|
+
id: msg.id,
|
|
288
|
+
error: 'Eval not enabled. Run with --eval flag.',
|
|
289
|
+
success: false
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
// Use indirect eval to run in global scope
|
|
296
|
+
const evalInGlobal = eval;
|
|
297
|
+
const result = evalInGlobal(msg.code);
|
|
298
|
+
|
|
299
|
+
// Handle promises
|
|
300
|
+
if (result && typeof result.then === 'function') {
|
|
301
|
+
result
|
|
302
|
+
.then((resolved) => {
|
|
303
|
+
sendToParent('evalResponse', {
|
|
304
|
+
id: msg.id,
|
|
305
|
+
result: serializeResult(resolved),
|
|
306
|
+
success: true
|
|
307
|
+
});
|
|
308
|
+
})
|
|
309
|
+
.catch((err) => {
|
|
310
|
+
sendToParent('evalResponse', {
|
|
311
|
+
id: msg.id,
|
|
312
|
+
error: err.message,
|
|
313
|
+
stack: err.stack,
|
|
314
|
+
success: false
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
sendToParent('evalResponse', {
|
|
319
|
+
id: msg.id,
|
|
320
|
+
result: serializeResult(result),
|
|
321
|
+
success: true
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} catch (err) {
|
|
325
|
+
sendToParent('evalResponse', {
|
|
326
|
+
id: msg.id,
|
|
327
|
+
error: err.message,
|
|
328
|
+
stack: err.stack,
|
|
329
|
+
success: false
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
|
|
334
|
+
case 'getGlobals':
|
|
335
|
+
// List available global variables
|
|
336
|
+
const globals = Object.keys(global).filter(k => !k.startsWith('_'));
|
|
337
|
+
sendToParent('globalsResponse', { globals });
|
|
338
|
+
break;
|
|
339
|
+
|
|
340
|
+
case 'resumeBreakpoint':
|
|
341
|
+
// Resume from a breakpoint
|
|
342
|
+
if (activeBreakpoint && breakpointResolve) {
|
|
343
|
+
const bp = activeBreakpoint;
|
|
344
|
+
originalConsole.log(`\nš¢ RESUMED [${bp.label}] - Continuing execution...\n`);
|
|
345
|
+
sendToParent('breakpoint', {
|
|
346
|
+
action: 'resumed',
|
|
347
|
+
id: bp.id,
|
|
348
|
+
label: bp.label,
|
|
349
|
+
pauseDuration: Date.now() - bp.timestamp
|
|
350
|
+
});
|
|
351
|
+
activeBreakpoint = null;
|
|
352
|
+
breakpointResolve(msg.returnValue);
|
|
353
|
+
breakpointResolve = null;
|
|
354
|
+
} else {
|
|
355
|
+
sendToParent('breakpointError', { error: 'No active breakpoint to resume' });
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
|
|
359
|
+
case 'getActiveBreakpoint':
|
|
360
|
+
// Get info about current breakpoint
|
|
361
|
+
if (activeBreakpoint) {
|
|
362
|
+
sendToParent('activeBreakpointResponse', {
|
|
363
|
+
active: true,
|
|
364
|
+
breakpoint: {
|
|
365
|
+
id: activeBreakpoint.id,
|
|
366
|
+
label: activeBreakpoint.label,
|
|
367
|
+
context: serializeResult(activeBreakpoint.context),
|
|
368
|
+
stack: activeBreakpoint.stack,
|
|
369
|
+
pausedFor: Date.now() - activeBreakpoint.timestamp
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
} else {
|
|
373
|
+
sendToParent('activeBreakpointResponse', { active: false });
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
|
|
377
|
+
case 'triggerBreakpoint':
|
|
378
|
+
// Remotely triggered breakpoint from dashboard
|
|
379
|
+
if (!activeBreakpoint) {
|
|
380
|
+
// Trigger breakpoint asynchronously so it doesn't block the message handler
|
|
381
|
+
setImmediate(async () => {
|
|
382
|
+
await process.reflexive.breakpoint(msg.label || 'remote', {
|
|
383
|
+
triggeredRemotely: true,
|
|
384
|
+
timestamp: new Date().toISOString()
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
} else {
|
|
388
|
+
sendToParent('breakpointError', { error: 'Already at a breakpoint' });
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Serialize result for IPC (handle circular refs, functions, etc.)
|
|
396
|
+
function serializeResult(value, depth = 0) {
|
|
397
|
+
if (depth > 3) return '[Max depth reached]';
|
|
398
|
+
|
|
399
|
+
if (value === undefined) return 'undefined';
|
|
400
|
+
if (value === null) return null;
|
|
401
|
+
if (typeof value === 'function') return `[Function: ${value.name || 'anonymous'}]`;
|
|
402
|
+
if (typeof value === 'symbol') return value.toString();
|
|
403
|
+
if (typeof value === 'bigint') return value.toString() + 'n';
|
|
404
|
+
|
|
405
|
+
if (value instanceof Error) {
|
|
406
|
+
return { __type: 'Error', name: value.name, message: value.message, stack: value.stack };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (value instanceof Map) {
|
|
410
|
+
return { __type: 'Map', entries: Array.from(value.entries()).slice(0, 20) };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (value instanceof Set) {
|
|
414
|
+
return { __type: 'Set', values: Array.from(value.values()).slice(0, 20) };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (Buffer.isBuffer(value)) {
|
|
418
|
+
return { __type: 'Buffer', length: value.length, preview: value.slice(0, 50).toString('hex') };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (Array.isArray(value)) {
|
|
422
|
+
if (value.length > 100) {
|
|
423
|
+
return { __type: 'Array', length: value.length, preview: value.slice(0, 20).map(v => serializeResult(v, depth + 1)) };
|
|
424
|
+
}
|
|
425
|
+
return value.map(v => serializeResult(v, depth + 1));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (typeof value === 'object') {
|
|
429
|
+
try {
|
|
430
|
+
const keys = Object.keys(value);
|
|
431
|
+
if (keys.length > 50) {
|
|
432
|
+
const preview = {};
|
|
433
|
+
keys.slice(0, 20).forEach(k => { preview[k] = serializeResult(value[k], depth + 1); });
|
|
434
|
+
return { __type: 'Object', keyCount: keys.length, preview };
|
|
435
|
+
}
|
|
436
|
+
const result = {};
|
|
437
|
+
keys.forEach(k => { result[k] = serializeResult(value[k], depth + 1); });
|
|
438
|
+
return result;
|
|
439
|
+
} catch (e) {
|
|
440
|
+
return `[Object: ${value.constructor?.name || 'unknown'}]`;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return value;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Initialize everything
|
|
448
|
+
function init() {
|
|
449
|
+
createReflexiveAPI();
|
|
450
|
+
interceptConsole();
|
|
451
|
+
interceptErrors();
|
|
452
|
+
setupDiagnostics();
|
|
453
|
+
setupPerfHooks();
|
|
454
|
+
setupParentMessageHandler();
|
|
455
|
+
|
|
456
|
+
// Notify parent that injection is complete
|
|
457
|
+
sendToParent('ready', {
|
|
458
|
+
pid: process.pid,
|
|
459
|
+
nodeVersion: process.version,
|
|
460
|
+
platform: process.platform
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
init();
|
|
465
|
+
|
|
466
|
+
module.exports = {
|
|
467
|
+
// Export for programmatic use if someone imports this directly
|
|
468
|
+
setState: (key, value) => process.reflexive?.setState(key, value),
|
|
469
|
+
getState: (key) => process.reflexive?.getState(key),
|
|
470
|
+
log: (level, message, meta) => process.reflexive?.log(level, message, meta),
|
|
471
|
+
emit: (event, data) => process.reflexive?.emit(event, data),
|
|
472
|
+
span: (name, fn) => process.reflexive?.span(name, fn),
|
|
473
|
+
breakpoint: (label, context) => process.reflexive?.breakpoint(label, context)
|
|
474
|
+
};
|