repo_description: Chrome DevTools for coding agents
repo_topics: browser chrome chrome-devtools debugging devtools mcp mcp-server puppeteer
code: /**
   * @license
   * Copyright 2026 Google LLC
   * SPDX-License-Identifier: Apache-2.0
   */
  
  import {spawn, type ChildProcess} from 'node:child_process';
  import {fileURLToPath} from 'node:url';
  
  import {logger} from '../logger.js';
  
  import type {WatchdogMessage, OsType} from './types.js';
  
  export class WatchdogClient {
    #childProcess: ChildProcess;
  
    constructor(
      config: {
        parentPid: number;
        appVersion: string;
        osType: OsType;
        logFile?: string;
        clearcutEndpoint?: string;
        clearcutForceFlushIntervalMs?: number;
        clearcutIncludePidHeader?: boolean;
      },
      options?: {spawn?: typeof spawn},
    ) {
      const watchdogPath = fileURLToPath(
        new URL('./watchdog/main.js', import.meta.url),
      );
  
      const args = [
        watchdogPath,
        `--parent-pid=${config.parentPid}`,
        `--app-version=${config.appVersion}`,
        `--os-type=${config.osType}`,
      ];
  
      if (config.logFile) {
        args.push(`--log-file=${config.logFile}`);
      }
      if (config.clearcutEndpoint) {
        args.push(`--clearcut-endpoint=${config.clearcutEndpoint}`);
      }
      if (config.clearcutForceFlushIntervalMs) {
        args.push(
          `--clearcut-force-flush-interval-ms=${config.clearcutForceFlushIntervalMs}`,
        );
      }
      if (config.clearcutIncludePidHeader) {
        args.push('--clearcut-include-pid-header');
      }
  
      const spawner = options?.spawn ?? spawn;
      this.#childProcess = spawner(process.execPath, args, {
        stdio: ['pipe', 'ignore', 'ignore'],
        detached: true,
      });
      this.#childProcess.unref();
      this.#childProcess.on('error', err => {
        logger('Watchdog process error:', err);
      });
      this.#childProcess.on('exit', (code, signal) => {
        logger(`Watchdog exited with code ${code} and signal ${signal}`);
      });
    }
  
    send(message: WatchdogMessage): void {
      if (
        this.#childProcess.stdin &&
        !this.#childProcess.stdin.destroyed &&
        this.#childProcess.pid
      ) {
        try {
          const line = JSON.stringify(message) + '\n';
          this.#childProcess.stdin.write(line);
        } catch (err) {
          logger('Failed to write to watchdog stdin', err);
        }
      } else {
        logger('Watchdog stdin not available, dropping message');
      }
    }
  }
  