Loading...
Loading...
Debug Node.js via --inspect + Chrome DevTools Protocol CLI.
npx skill4agent add nousresearch/hermes-agent node-inspect-debuggerconsole.lognode inspectndbchrome-remote-interfacenode inspect_SlashWorkerconsole.logconsole.lognode inspectnode inspect path/to/script.js
# or with tsx
node --inspect-brk $(which tsx) path/to/script.tsdebug>| Command | Action |
|---|---|
| continue |
| step over |
| step into |
| step out |
| pause running code |
| set breakpoint at file.js line 42 |
| set breakpoint at line 42 of current file |
| break when function is called |
| clear breakpoint |
| list all breakpoints |
| backtrace (call stack) |
| show 5 lines of source around current position |
| evaluate expr on every pause |
| show watched expressions |
| drop into REPL in current scope (Ctrl+C to exit REPL) |
| evaluate expression once |
| restart script |
| kill the script |
| quit debugger |
replCtrl+Cdebug># 1. Send SIGUSR1 to enable the inspector on an existing process
kill -SIGUSR1 <pid>
# Node prints: Debugger listening on ws://127.0.0.1:9229/<uuid>
# 2. Attach the debugger CLI
node inspect -p <pid>
# or by URL
node inspect ws://127.0.0.1:9229/<uuid>node --inspect script.js # listen on 127.0.0.1:9229, keep running
node --inspect-brk script.js # listen AND pause on first line
node --inspect=0.0.0.0:9230 script.js # custom host:portnode --inspect-brk --import tsx script.ts
# or older tsx
node --inspect-brk -r tsx/cjs script.tschrome-remote-interfacenpm i -g chrome-remote-interface # or project-local
# Start your target:
node --inspect-brk=9229 target.js &/tmp/cdp-debug.jsconst CDP = require('chrome-remote-interface');
(async () => {
const client = await CDP({ port: 9229 });
const { Debugger, Runtime } = client;
Debugger.paused(async ({ callFrames, reason }) => {
const top = callFrames[0];
console.log(`PAUSED: ${reason} @ ${top.url}:${top.location.lineNumber + 1}`);
// Walk scopes for locals
for (const scope of top.scopeChain) {
if (scope.type === 'local' || scope.type === 'closure') {
const { result } = await Runtime.getProperties({
objectId: scope.object.objectId,
ownProperties: true,
});
for (const p of result) {
console.log(` ${scope.type}.${p.name} =`, p.value?.value ?? p.value?.description);
}
}
}
// Evaluate an expression in the paused frame
const { result } = await Debugger.evaluateOnCallFrame({
callFrameId: top.callFrameId,
expression: 'typeof state !== "undefined" ? JSON.stringify(state) : "n/a"',
});
console.log('state =', result.value ?? result.description);
await Debugger.resume();
});
await Runtime.enable();
await Debugger.enable();
// Set a breakpoint by URL regex + line
await Debugger.setBreakpointByUrl({
urlRegex: '.*app\\.tsx$',
lineNumber: 119, // 0-indexed
columnNumber: 0,
});
await Runtime.runIfWaitingForDebugger();
})();node /tmp/cdp-debug.jschrome-remote-interfaceui-tui/package.jsonmkdir -p /tmp/cdp-tools && cd /tmp/cdp-tools && npm i chrome-remote-interface
NODE_PATH=/tmp/cdp-tools/node_modules node /tmp/cdp-debug.jsui-tui/package.jsonnpm run dev--inspect-brkcd /home/bb/hermes-agent/ui-tui
npm run build # produce dist/ once so transpile isn't needed on first load
node --inspect-brk dist/entry.js
# In another terminal:
node inspect -p <node pid>debug>sb('dist/app.js', 220) # or wherever the suspect render is
contreplpropsuseInputhermes --tui# 1. Launch TUI
hermes --tui &
TUI_PID=$(pgrep -f 'ui-tui/dist/entry' | head -1)
# 2. Enable inspector on that Node PID
kill -SIGUSR1 "$TUI_PID"
# 3. Find the WS URL
curl -s http://127.0.0.1:9229/json/list | jq -r '.[0].webSocketDebuggerUrl'
# 4. Attach
node inspect ws://127.0.0.1:9229/<uuid>sb(...)_SlashWorkerpython-debugpyui-tui/cd /home/bb/hermes-agent/ui-tui
# Run a single test file paused on entry
node --inspect-brk ./node_modules/vitest/vitest.mjs run --no-file-parallelism src/app/foo.test.tsxnode inspect -p <pid>sb('src/app/foo.tsx', 42)cont--no-file-parallelism--runInBandHeapProfilerProfiler// CPU profile for 5 seconds
await client.Profiler.enable();
await client.Profiler.start();
await new Promise(r => setTimeout(r, 5000));
const { profile } = await client.Profiler.stop();
require('fs').writeFileSync('/tmp/cpu.cpuprofile', JSON.stringify(profile));
// Open /tmp/cpu.cpuprofile in Chrome DevTools → Performance tab// Heap snapshot
await client.HeapProfiler.enable();
const chunks = [];
client.HeapProfiler.addHeapSnapshotChunk(({ chunk }) => chunks.push(chunk));
await client.HeapProfiler.takeHeapSnapshot({ reportProgress: false });
require('fs').writeFileSync('/tmp/heap.heapsnapshot', chunks.join(''));.tsdist/*.jsnode --enable-source-mapssb('src/app.tsx', N)node inspect--inspect--inspect-brk--inspect--inspect-brk9229--inspect=0/json/listcurl -s http://127.0.0.1:9229/json/list # lists all inspectable targets on the host--inspectNODE_OPTIONS='--inspect-brk' node parent.jsNODE_OPTIONS='--inspect'Ctrl+Cnode inspectcontkillnode inspectterminal(pty=true)background=trueprocess(action='submit', data='...')--inspect=0.0.0.0:9229127.0.0.1curl -s http://127.0.0.1:9229/json/list--inspect-brkexec process.pidreplnode --inspect-brk script.js &
node inspect -p $!
# debug>
sb('script.js', X)
cont
# paused. Now:
repl
> myVariable
> Object.keys(this)debug> sb('suspectFn')
debug> cont
# paused on entry
debug> bt# Start with --inspect (no -brk), let it run to the hang, then:
debug> pause
debug> bt
# Now you see the stuck frame