Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[Windows] Three `detached: true` spawns missing `windowsHide` — telemetry flushSync opens a console window on nearly every CLI exit

オープン
#3,476 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

  • #3478 @kvnloo による — マージされずにクローズ
  • #3823 @lorenzozanee による — マージされずにクローズ
  • #3851 @dajiaohuang による — マージされずにクローズ

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
78/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
node.js, typescript
領域
cli

調査の方向性

Inspect the detached spawns in src/telemetry/transport.ts, src/utils/openBrowser.ts, and src/commands/previewLifecycle.ts, comparing their options with the existing windowsHide handling in src/utils/autoUpdate.ts. Verify on Windows that telemetry flushes, background preview, and browser launches no longer open console windows while preserving their existing detached behavior.

索引モデルが issue の本文から書いたものです。

説明

triage/needs-triage
Describe the bug

On Windows, console windows pop up during ordinary CLI use — not just during renders. This is a follow-up to #3379 (ffmpeg, fixed) and is distinct from #3430 (chrome-headless-shell, still open): those two cover console-subsystem binaries, whereas this one is about detached: true spawns of Node itself.

Node's docs are explicit about the mechanism:

On Windows, setting options.detached to true makes it possible for the child process to continue running after the parent exits. The child will have its own console window.

child_process.spawn() defaults windowsHide to false, so any detached: true spawn opens a visible console window on Windows unless windowsHide: true is passed.

Root cause

packages/cli v0.8.13 has four detached: true spawns. Three are missing windowsHide: true:

Source What it spawns windowsHide
src/telemetry/transport.ts (flushSync) node -e "fetch(...)" → PostHog ❌ missing
src/utils/openBrowser.ts (openBrowser) user's browser ❌ missing
src/commands/previewLifecycle.ts background preview server ❌ missing
src/utils/autoUpdate.ts update check ✅ present

autoUpdate.ts already gets this right, which makes the other three look like oversights rather than intent.

The worst offender: telemetry flushSync

flushSync() is the process-exit flush path. Because it can't await an HTTP POST during shutdown, it hands the payload to a detached Node process to deliver after the parent dies:

function flushSync() {
  const payload = buildPayload(eventQueue);
  if (payload == null) return;
  eventQueue = [];
  try {
    const child = spawn(
      process.execPath,
      ["-e", `fetch(${JSON.stringify(`${POSTHOG_HOST}/batch/`)},{...})`],
      { detached: true, stdio: "ignore" }   // <-- no windowsHide
    );
    child.unref();
  } catch {}
}

The strategy itself is sound — the only thing missing is windowsHide: true.

Impact is amplified by how the CLI is actually used. Agent-driven workflows invoke many short-lived commands (check, lint, keyframes, capture, snapshot, beats, preview), and each invocation that exits with queued events opens its own console window. My ~/.hyperframes/config.json shows commandCount: 1350, which is a lot of windows. Unlike #3430 this isn't render-only — it fires on nearly every command, including read-only ones.

Steps to reproduce
  1. On Windows, ensure telemetry is on (default): telemetryEnabled: true in ~/.hyperframes/config.json
  2. Run any short command, e.g. npx hyperframes check . or npx hyperframes lint .
  3. Watch the desktop/taskbar as the command exits — a console window flashes open
  4. Run several commands in sequence; you get one window per exit

Setting HYPERFRAMES_NO_TELEMETRY=1 (or DO_NOT_TRACK=1) makes the flashing stop, which isolates the telemetry spawn as the cause.

For the other two sites: hyperframes preview without --foreground spawns the detached background server (one window), and without --no-open spawns the browser via options.browserPath (one more).

Expected behavior

No console window appears for any of these background spawns.

Actual behavior

A console window opens per detached spawn — in the telemetry case, on nearly every CLI invocation.

Suggested fix

Add windowsHide: true at the three call sites, matching what autoUpdate.ts already does:

// src/telemetry/transport.ts
const child = spawn(process.execPath, ["-e", script], {
  detached: true,
  stdio: "ignore",
  windowsHide: true,
});

// src/utils/openBrowser.ts
const child = spawn7(options.browserPath, args, {
  detached: true,
  stdio: "ignore",
  windowsHide: true,
});

// src/commands/previewLifecycle.ts
child = spawn17(execPath, args, {
  detached: true,
  stdio: ["ignore", logFd, logFd],
  env: process.env,
  windowsHide: true,
});

windowsHide is a no-op on macOS/Linux, so it's safe to apply unconditionally — same rationale as the #3379 fix.

Given that #3379, #3430, and this issue are all the same class of defect, it may be worth a small shared spawn helper that applies windowsHide: true by default, plus a lint rule that flags a bare detached: true without it.

Environment
hyperframes  0.8.13 (latest)
Node.js      v24.15.0 (win32 x64)
OS           Microsoft Windows 10 Home 10.0.19045 (build 19045)
Install      npx (_npx cache)
Additional context

Verified by static analysis of the shipped dist/cli.js in [email protected] — all four detached: true sites were inspected directly, along with every windowsHide occurrence. All ffmpeg/ffprobe spawns correctly carry windowsHide: true with the explanatory comment added by the #3379 fix, so that regression has not returned.

Related: #3379 (ffmpeg, closed/fixed) · #3430 (chrome-headless-shell, open)

主要言語
TypeScript
スター
54.1k
フォーク
4.9k
平均マージ
7時間 18分
マージ済み PR(30日)
784

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

heygen-com/hyperframes のほかの issue

heygen-com/hyperframes の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。