Node Inspect Debugger
Отладка Node.js через --inspect + Chrome DevTools Protocol CLI.
Метаданные навыка
| Источник | Встроенный (устанавливается по умолчанию) |
| Путь | skills/software-development/node-inspect-debugger |
| Версия | 1.0.0 |
| Автор | VibeOS |
| Лицензия | MIT |
| Платформы | linux, macos, windows |
| Теги | debugging, nodejs, node-inspect, cdp, breakpoints, ui-tui |
| Связанные навыки | systematic-debugging, python-debugpy, debugging-vibeos-tui-commands |
Справочник: полный SKILL.md
Ниже приведено полное описание навыка, которое VibeOS загружает при его активации. Это те инструкции, которые видит агент, когда навык активен.
Node.js Inspect Debugger
Обзор
Когда console.log недостаточно, управляйте встроенным инспектором V8 в Node программно из терминала. Вы получаете реальные точки останова, шаг с заходом/через/выходом, просмотр стека вызовов, дампы локальных/замыкающих областей видимости и вычисление произвольных выражений в приостановленном фрейме.
Два инструмента, выбирайте:
node inspect— встроенный, без установки, REPL в CLI. Лучше всего для быстрых проверок.ndb/ CDP черезchrome-remote-interface— скриптуемый из Node/Python; лучше всего, когда нужно автоматизировать много точек останова, собрать состояние между запусками или отлаживать неинтерактивно из цикла агента.
Предпочитайте node inspect в первую очередь. Он всегда доступен, и REPL работает быстро.
Когда использовать
- Тест Node падает, и нужно увидеть промежуточное состояние
- ui-tui падает или ведёт себя неправильно, и нужно проверить состояние React/Ink до рендеринга
- Дочерние процессы tui_gateway (
_SlashWorker, PTY bridge workers) работают некорректно - Нужно проверить значение в замыкании, до которого
console.logне может добраться без патчинга - Производительность: подключиться к работающему процессу, чтобы захватить CPU-профиль или снимок кучи
Не используйте для: того, что console.log решает меньше чем за минуту. Отладка с точками останова более ресурсоёмка; используйте её, когда выгода реальна.
Краткий справочник: REPL node inspect
Запуск с остановкой на первой строке:
node inspect path/to/script.js
# или с tsx
node --inspect-brk $(which tsx) path/to/script.ts
Приглашение debug> принимает:
| Команда | Действие |
|---|---|
c или cont | продолжить |
n или next | шаг через |
s или step | шаг с заходом |
o или out | шаг с выходом |
pause | приостановить выполняющийся код |
sb('file.js', 42) | установить точку останова в file.js, строка 42 |
sb(42) | установить точку останова на строке 42 текущего файла |
sb('functionName') | остановиться при вызове функции |
cb('file.js', 42) | удалить точку останова |
breakpoints | список всех точек останова |
bt | backtrace (стек вызовов) |
list(5) | показать 5 строк исходного кода вокруг текущей позиции |
watch('expr') | вычислять выражение при каждой остановке |
watchers | показать отслеживаемые выражения |
repl | войти в REPL в текущей области видимости (Ctrl+C для выхода из REPL) |
exec expr | вычислить выражение один раз |
restart | перезапустить скрипт |
kill | завершить скрипт |
.exit | выйти из отладчика |
В подрежиме repl: введите любое JS-выражение, включая доступ к локальным переменным и переменным замыкания. Ctrl+C возвращает в debug>.
Подключение к работающему процессу
Когда процесс уже запущен (например, долгоживущий dev-сервер или шлюз TUI):
# 1. Отправить SIGUSR1, чтобы включить инспектор в существующем процессе
kill -SIGUSR1 <pid>
# Node выведет: Debugger listening on ws://127.0.0.1:9229/<uuid>
# 2. Подключить отладчик CLI
node inspect -p <pid>
# или по URL
node inspect ws://127.0.0.1:9229/<uuid>
Чтобы запустить процесс с инспектором с самого начала:
node --inspect script.js # слушать на 127.0.0.1:9229, продолжить выполнение
node --inspect-brk script.js # слушать И остановиться на первой строке
node --inspect=0.0.0.0:9230 script.js # пользовательский хост:порт
Для TypeScript через tsx:
node --inspect-brk --import tsx script.ts
# или старая версия tsx
node --inspect-brk -r tsx/cjs script.ts
Программный CDP (скриптинг из терминала)
Когда нужно автоматизировать — установить много точек останова, захватить состояние области видимости, написать скрипт для воспроизведения — используйте chrome-remote-interface:
npm i -g chrome-remote-interface # или локально в проекте
# Запустите вашу цель:
node --inspect-brk=9229 target.js &
Скрипт-драйвер (сохраните как /tmp/cdp-debug.js):
const 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}`);
// Обход областей видимости для локальных переменных
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);
}
}
}
// Вычислить выражение в приостановленном фрейме
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();
// Установить точку останова по URL-регулярному выражению + строка
await Debugger.setBreakpointByUrl({
urlRegex: '.*app\\.tsx$',
lineNumber: 119, // с нуля
columnNumber: 0,
});
await Runtime.runIfWaitingForDebugger();
})();
Запустите:
node /tmp/cdp-debug.js
Примечание для VibeOS: chrome-remote-interface НЕТ в ui-tui/package.json. Установите его во временную директорию, если не хотите засорять проект:
mkdir -p /tmp/cdp-tools && cd /tmp/cdp-tools && npm i chrome-remote-interface
NODE_PATH=/tmp/cdp-tools/node_modules node /tmp/cdp-debug.js
Отладка VibeOS ui-tui
TUI построен на Ink + tsx. Два распространённых сценария:
Отладка одного компонента Ink в режиме разработки
В ui-tui/package.json есть npm run dev (tsx --watch). Добавьте --inspect-brk, запустив tsx напрямую:
cd /home/bb/vibeos-agent/ui-tui
npm run build # собрать dist/ один раз, чтобы транспиляция не требовалась при первой загрузке
node --inspect-brk dist/entry.js
# В другом терминале:
node inspect -p <pid node>
Затем внутри debug>:
sb('dist/app.js', 220) # или там, где подозреваемый рендер
cont
Когда остановится, repl → проверьте props, ссылки на состояние, значения обработчиков useInput и т.д.
Отладка работающего vibeos --tui
TUI запускает Node из Python CLI. Самый простой путь:
# 1. Запустить TUI
vibeos --tui &
TUI_PID=$(pgrep -f 'ui-tui/dist/entry' | head -1)
# 2. Включить инспектор на этом PID Node
kill -SIGUSR1 "$TUI_PID"
# 3. Найти WS URL
curl -s http://127.0.0.1:9229/json/list | jq -r '.[0].webSocketDebuggerUrl'
# 4. Подключиться
node inspect ws://127.0.0.1:9229/<uuid>
Взаимодействие с TUI (ввод в его окне) продолжает выполнение; ваш отладчик может остановить его на точке останова по любому sb(...).
Отладка _SlashWorker / PTY дочерних процессов
Это Python, а не Node — используйте навык python-debugpy для них. Только части на Node (Ink UI, клиент tui_gateway, тесты tsx-run в ui-tui/) используют этот навык.
Запуск тестов Vitest под отладчиком
cd /home/bb/vibeos-agent/ui-tui
# Запустить один тестовый файл с остановкой на входе
node --inspect-brk ./node_modules/vitest/vitest.mjs run --no-file-parallelism src/app/foo.test.tsx
В другом терминале: node inspect -p <pid>, затем sb('src/app/foo.tsx', 42), cont.
Используйте --no-file-parallelism (vitest) или --runInBand (jest), чтобы существовал только один воркер — отладка пула болезненна.
Снимки кучи и CPU-профили (неинтерактивные)
В драйвере CDP выше замените Debugger на HeapProfiler / Profiler:
// CPU-профиль на 5 секунд
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));
// Откройте /tmp/cpu.cpuprofile в Chrome DevTools → вкладка Performance
// Снимок кучи
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(''));
Частые ошибки
-
Неверные номера строк в исходном TS-коде. Точки останова срабатывают на скомпилированном JS, а не на
.ts. Либо (а) ставьте точки останова в собранномdist/*.js, либо (б) включите sourcemaps (node --enable-source-maps) и используйтеsb('src/app.tsx', N)— но только с CDP-клиентами, которые поддерживают sourcemaps. CLInode inspectне поддерживает. -
--inspectvs--inspect-brk.--inspectзапускает инспектор, но не останавливается; ваш скрипт может проскочить мимо первой точки останова, если вы подключитесь слишком поздно. Используйте--inspect-brk, когда нужно установить точки останова до выполнения любого кода. -
Конфликты портов. По умолчанию используется
9229. Если несколько процессов Node используют инспектор, передайте--inspect=0(случайный порт) и прочитайте фактический URL из/json/list:curl -s http://127.0.0.1:9229/json/list # список всех целей для инспектирования на хосте -
Дочерние процессы.
--inspectна родительском процессе НЕ инспектирует его дочерние процессы. ИспользуйтеNODE_OPTIONS='--inspect-brk' node parent.js, чтобы распространить на каждый дочерний процесс; учтите, что всем нужны уникальные порты (Node автоматически увеличивает порт, когда наследуетсяNODE_OPTIONS='--inspect'). -
Фоновые завершения. Если вы нажмёте
Ctrl+Cвnode inspect, пока цель приостановлена, цель останется приостановленной. Либо сначала выполнитеcont, либо явно завершите цель черезkill. -
Запуск
node inspectчерез терминал агента. Это PTY-совместимый REPL. В VibeOS запускайте его сterminal(pty=true)илиbackground=true+process(action='submit', data='...'). Режим переднего плана без PTY подойдёт для одноразовых команд, но не для интерактивного пошагового выполнения. -
Безопасность.
--inspect=0.0.0.0:9229открывает возможность произвольного выполнения кода. Всегда привязывайтесь к127.0.0.1(по умолчанию), если только у вас не изолированная сеть.
Контрольный список проверки
После настройки сессии отладки проверьте:
-
curl -s http://127.0.0.1:9229/json/listвозвращает именно ту цель, которую вы ожидаете - Первая точка останова действительно срабатывает (если нет, вы, вероятно, пропустили
--inspect-brkили подключились после завершения выполнения) - Просмотр исходного кода на паузе показывает правильный файл (несоответствие = проблема с sourcemaps, см. ошибку 1)
-
exec process.pidвreplвозвращает PID, к которому вы хотели подключиться
Одноразовые рецепты
«Почему эта переменная undefined на строке X?»
node --inspect-brk script.js &
node inspect -p $!
# debug>
sb('script.js', X)
cont
# остановлено. Теперь:
repl
> myVariable
> Object.keys(this)
«Какой путь вызова в эту функцию?»
debug> sb('suspectFn')
debug> cont
# остановлено на входе
debug> bt
«Эта асинхронная цепочка зависает — где?»
# Запустите с --inspect (без -brk), дайте выполниться до зависания, затем:
debug> pause
debug> bt
# Теперь вы видите застрявший фрейм