{/ Эта страница автоматически создается на основе файла SKILL.md навыка с помощью сайта site/scripts/generate-skill-docs.py. Редактируйте исходный код SKILL.md, а не эту страницу. /}

Отладчик проверки узла

Отладка Node.js с помощью --inspect + CLI протокола Chrome DevTools.

Метаданные навыков

Источник В комплекте (устанавливается по умолчанию)
Путь навыки/разработка программного обеспечения/отладчик-проверка узла
Версия 1.0.0
Автор Агент Гермес
Лицензия Массачусетский технологический институт
Платформы Linux, MacOS, Windows
Теги отладка, nodejs, node-inspect, cdp, breakpoints, ui-tui
Сопутствующие навыки systematic-debugging, python-debugpy, debugging-hermes-tui-commands

Ссылка: полная версия SKILL.md:::информация

Ниже приведено полное определение навыка, которое Гермес загружает при активации этого навыка. Это то, что агент видит в качестве инструкций, когда навык активен.

Проверка отладчика Node.js

Обзор

Если console.log недостаточно, запустите встроенный в Node инспектор V8 программно с терминала. Вы получаете настоящие точки останова, вход/переход/выход, обход стека вызовов, дампы локальной области/закрытия и оценку произвольного выражения в приостановленном кадре.

Два инструмента, выберите один:

Сначала предпочтите проверку узла. Она всегда доступна, а REPL выполняется быстро.

Когда использовать

Не используйте для: задач, которые console.log решает менее чем за минуту. Отладка с использованием точек останова сложнее; используйте его, когда выигрыш реален.

Краткий справочник: node Inspect REPL

Запуск приостановлен на первой строке:

node inspect path/to/script.js
# or with tsx
node --inspect-brk $(which tsx) path/to/script.ts

Приглашение debug> принимает:

Команда Действие
c или продолжение продолжить
n или следующий перешагнуть
s или шаг шагнуть в
o или out выйти
пауза приостановить выполнение кода
sb('file.js', 42) установить точку останова в строке 42 файла file.js
сб(42) установить точку останова в строке 42 текущего файла
sb('имя_функции') прерывание при вызове функции
cb('file.js', 42) очистить точку останова
точки останова список всех точек останова
БТ обратная трассировка (стек вызовов)
список(5) показать 5 строк источника вокруг текущей позиции
смотреть('выражение') оценивать expr на каждой паузе
наблюдатели показать просмотренные выражения
репл перейти в REPL в текущей области (Ctrl+C для выхода из REPL)
исполнительное выражение оценить выражение один раз
перезапустить сценарий перезапуска
убить убить скрипт
.exit выйти из отладчика

В подрежиме repl: введите любое выражение JS, включая доступ к локальным переменным и переменным закрытия. Ctrl+C возвращает обратно в debug>.

Подключение к запущенному процессу

Когда процесс уже запущен (например, долгоживущий сервер разработки или шлюз TUI):

# 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:port

Для TypeScript через tsx:

node --inspect-brk --import tsx script.ts
# or older tsx
node --inspect-brk -r tsx/cjs script.ts

Programmatic CDP (скрипты с терминала)

Если вы хотите автоматизировать — установить множество точек останова, захватить состояние области, создать сценарий воспроизведения — используйте chrome-remote-interface:

npm i -g chrome-remote-interface        # or project-local
# Start your target:
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}`);

    // 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.js

Примечание, касающееся Hermes: 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

Отладка Hermes ui-tui

TUI построен Ink + tsx. Два распространенных сценария:

Отладка одного компонента Ink в dev

ui-tui/package.json имеет npm run dev (tsx --watch). Добавьте --inspect-brk, запустив tsx напрямую:

cd /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
cont

Когда он приостанавливается, repl → проверяет props, ссылки на состояние, значения обработчика useInput и т. д.

Отладка работающего hermes --tui

TUI порождает Node из CLI Python. Самый простой путь:

# 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>

Взаимодействие с TUI (ввод текста в его окне) продолжает ускорять выполнение; ваш отладчик может приостановить его на точке останова в любом sb(...).

Отладка дочерних процессов _SlashWorker/PTY

Это Python, а не Node — используйте для них навык python-debugpy. Этот навык используется только в узлах Node (пользовательский интерфейс Ink, клиент tui_gateway, тесты, запускаемые с помощью tsx под ui-tui/).

Запуск тестов Vitest под отладчиком

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.tsx

В другом терминале: node Inspect -p <pid>, затем sb('src/app/foo.tsx', 42), cont.

Используйте --no-file-parallelism (vitest) или --runInBand (jest), чтобы существовал только один рабочий процесс — отладка пула является болезненной задачей.

Снимки кучи и профили ЦП (неинтерактивные)

В приведенном выше драйвере CDP замените Debugger на HeapProfiler/Profiler:

// 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(''));

Распространенные ошибки

  1. Неправильные номера строк в исходном коде TS. Точки останова попадают в созданный JS, а не в .ts. Либо (а) внесите изменения во встроенный dist/*.js, либо (б) включите исходные карты (node --enable-source-maps) и используйте sb('src/app.tsx', N) — но только с клиентами CDP, которые следуют исходным картам. node Inspect CLI этого не делает.

  2. --inspect против --inspect-brk. --inspect запускает инспектор, но не приостанавливает его; ваш скрипт пройдет мимо первой точки останова, если вы присоединитесь слишком поздно. Используйте --inspect-brk, когда вам нужно установить точки останова перед запуском любого кода.

  3. Коллизии портов. Значение по умолчанию — «9229». Если проверку выполняют несколько процессов Node, передайте --inspect=0 (случайный порт) и прочитайте фактический URL-адрес из /json/list: bash curl -s http://127.0.0.1:9229/json/list # lists all inspectable targets on the host

  4. Дочерние процессы. --inspect родительского процесса НЕ проверяет его дочерние процессы. Используйте NODE_OPTIONS='--inspect-brk' nodeparent.jsдля распространения на каждого дочернего элемента; имейте в виду, что всем им нужны уникальные порты (узел автоматически увеличивается при наследованииNODE_OPTIONS='--inspect'`).

  5. Фоновые убийства. Если вы нажмете «Ctrl+C» в режиме «проверка узла», когда цель приостановлена, цель останется на паузе. Либо сначала cont, либо явно уничтожьте цель.

  6. Выполнение проверки узла через терминал агента. Это REPL, поддерживающий PTY. В Hermes запустите его с помощью terminal(pty=true) или background=true + process(action='submit', data='...'). Режим переднего плана без PTY будет работать для одноразовых команд, но не для интерактивного пошагового выполнения.

  7. Security. --inspect=0.0.0.0:9229 обеспечивает выполнение произвольного кода. Всегда привязывайтесь к 127.0.0.1 (по умолчанию), если у вас нет изолированной сети.

Контрольный список проверки

После настройки сеанса отладки проверьте:

Одноразовые рецепты

"Почему эта переменная не определена в строке X?"

node --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