mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(engine): a render on Windows opens no console windows (#6613)
* ci(windows): probe that a render opens no visible window * ci(windows): the probe names what opened each window * ci(windows): the probe spawns each binary alone, with and without detached * ci(windows): the probe logs the processes started during the render * ci(windows): the probe also spawns Chrome one consoleless hop down * ci(windows): the probe logs each child started without windowsHide * ci(windows): the probe logs every child process with its windowsHide and detached * fix(engine): Chrome keeps the CLI's console on Windows, so a render opens no window * fix(cli): every child the CLI starts on Windows is hidden unless it opts out * ci(windows): the probe bisects Chrome's flags and keeps each command's output * ci(windows): the probe names the Chrome process behind each window * ci(windows): the probe loads a page in Chrome directly and one hidden hop down * ci(windows): the probe prints the Chrome main command line and renders with the reporter's flags * ci(windows): the probe narrows the CLI's Chrome arguments to the ones that open a window * ci(windows): the probe launches the CLI's Chrome arguments one hidden hop down * ci(windows): the probe launches Chrome through the engine's browser pool * ci(windows): the probe varies what the engine passes to Puppeteer * fix(engine): renders on Windows launch Chrome with a Puppeteer that keeps it attached * refactor(cli): hideChildConsoles copies its arguments and keeps one comment line
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# Runs agent-facing commands from a launcher with no console (like an app that starts an agent), and fails if any
|
||||
# of them opens a visible top-level window.
|
||||
param([Parameter(Mandatory)][string]$Cli, [Parameter(Mandatory)][string]$Project)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
Add-Type @'
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
public static class Windows {
|
||||
delegate bool EnumProc(IntPtr h, IntPtr p);
|
||||
[DllImport("user32.dll")] static extern bool EnumWindows(EnumProc f, IntPtr p);
|
||||
[DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
|
||||
[DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowText(IntPtr h, StringBuilder s, int n);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetClassName(IntPtr h, StringBuilder s, int n);
|
||||
public static List<string[]> Visible() {
|
||||
var rows = new List<string[]>();
|
||||
EnumWindows((h, p) => {
|
||||
if (!IsWindowVisible(h)) return true;
|
||||
uint pid; GetWindowThreadProcessId(h, out pid);
|
||||
var t = new StringBuilder(256); var c = new StringBuilder(256);
|
||||
GetWindowText(h, t, 256); GetClassName(h, c, 256);
|
||||
rows.Add(new[] { h.ToString(), pid.ToString(), c.ToString(), t.ToString() });
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
function Snapshot {
|
||||
$seen = @{}
|
||||
foreach ($w in [Windows]::Visible()) {
|
||||
$proc = Get-Process -Id ([int]$w[1]) -ErrorAction SilentlyContinue
|
||||
$seen[$w[0]] = [pscustomobject]@{ Process = $proc.ProcessName; Id = $w[1]; Class = $w[2]; Title = $w[3]; Cmd = '' }
|
||||
}
|
||||
$seen
|
||||
}
|
||||
|
||||
# Starts `node launcher.mjs <cli> <cwd> <seconds> <args...>` and reports every window that appears meanwhile.
|
||||
function Watch([string]$Name, [int]$Seconds, [string[]]$CliArgs) {
|
||||
$baseline = Snapshot
|
||||
$p = Start-Process -FilePath node -ArgumentList (@($launcher, $Cli, $Project, $Seconds) + $CliArgs) -WindowStyle Hidden -PassThru
|
||||
$found = @{}
|
||||
while (-not $p.HasExited) {
|
||||
foreach ($entry in (Snapshot).GetEnumerator()) {
|
||||
if (-not $baseline.ContainsKey($entry.Key) -and -not $found.ContainsKey($entry.Key)) {
|
||||
$row = $entry.Value
|
||||
if ($row.Process -match 'chrome') {
|
||||
$q = Get-CimInstance Win32_Process -Filter "ProcessId=$($row.Id)" -ErrorAction SilentlyContinue
|
||||
$parent = if ($q) { Get-CimInstance Win32_Process -Filter "ProcessId=$($q.ParentProcessId)" -ErrorAction SilentlyContinue }
|
||||
$row.Cmd = "pid=$($row.Id) parent=[$($parent.Name) $($parent.ProcessId) main=$((($parent.CommandLine -replace '^.*chrome-headless-shell.exe"? ',''))) ] cmd=[$((("$($q.CommandLine)") -replace '^.*chrome-headless-shell.exe"? ','').Substring(0, [Math]::Min(260, ("$($q.CommandLine)" -replace '^.*chrome-headless-shell.exe"? ','').Length)))]"
|
||||
}
|
||||
$found[$entry.Key] = $row
|
||||
}
|
||||
}
|
||||
Start-Sleep -Milliseconds 50
|
||||
}
|
||||
$p.WaitForExit()
|
||||
Write-Host ("{0,-10} exit={1} new windows={2}" -f $Name, $p.ExitCode, $found.Count)
|
||||
if ($p.ExitCode -ne 0) { Get-Content (Join-Path $env:RUNNER_TEMP "command.log") -Tail 12 | ForEach-Object { Write-Host " | $_" } }
|
||||
$found.Values | ForEach-Object { Write-Host " $($_.Process) class=$($_.Class) title=[$($_.Title)] $($_.Cmd)" }
|
||||
[pscustomobject]@{ Name = $Name; Exit = $p.ExitCode; Windows = $found.Count }
|
||||
}
|
||||
|
||||
# The launcher has no console and no window; windowsHide only reaches the CLI itself, never what the CLI starts.
|
||||
$launcher = Join-Path $env:RUNNER_TEMP 'launch-cli.mjs'
|
||||
@'
|
||||
import { spawn } from "node:child_process";
|
||||
import { openSync } from "node:fs";
|
||||
const [cli, cwd, seconds, ...args] = process.argv.slice(2);
|
||||
const log = openSync(process.env.RUNNER_TEMP + "\\command.log", "w");
|
||||
const child = spawn(process.execPath, [cli, ...args], { cwd, windowsHide: true, stdio: ["ignore", log, log] });
|
||||
const stop = Number(seconds) > 0 ? setTimeout(() => child.kill(), Number(seconds) * 1000) : undefined;
|
||||
child.on("exit", (code) => { clearTimeout(stop); process.exit(code ?? 0); });
|
||||
'@ | Set-Content -Encoding utf8 $launcher
|
||||
|
||||
|
||||
$runs = @(
|
||||
(Watch 'render' 0 @('render', '--fps', '30', '--quality', 'draft', '--workers', '2', '--output', 'renders/probe.mp4')),
|
||||
(Watch 'check' 0 @('check')),
|
||||
(Watch 'check-warm' 0 @('check')),
|
||||
(Watch 'snapshot' 0 @('snapshot', '--frames', '2')),
|
||||
(Watch 'doctor' 0 @('doctor')),
|
||||
(Watch 'preview' 20 @('preview', '--port', '3571'))
|
||||
)
|
||||
$bad = $runs | Where-Object { $_.Windows -gt 0 }
|
||||
if ($bad) { throw "windows opened by: $(($bad | ForEach-Object Name) -join ', ')" }
|
||||
$failed = $runs | Where-Object { $_.Name -in @('render', 'snapshot') -and $_.Exit -ne 0 }
|
||||
if ($failed) { throw "commands failed: $(($failed | ForEach-Object Name) -join ', ')" }
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Windows window probe
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
HYPERFRAMES_NO_TELEMETRY: "1"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/windows-window-probe.yml
|
||||
- .github/scripts/windows-window-probe.ps1
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
probe:
|
||||
name: Agent commands open no window on windows-latest
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
- uses: ./.github/actions/install-ffmpeg-windows
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22
|
||||
- run: bun install --frozen-lockfile --linker=hoisted
|
||||
shell: pwsh
|
||||
- name: Build the CLI
|
||||
shell: pwsh
|
||||
run: |
|
||||
bun run --filter '@hyperframes/{parsers,lint,studio-server}' build
|
||||
bun run --cwd packages/core build
|
||||
bun run --filter @hyperframes/player build
|
||||
bun run --filter @hyperframes/studio build
|
||||
bun run --filter @hyperframes/inspector build
|
||||
bun run --filter @hyperframes/cli build
|
||||
- name: Scaffold the canary
|
||||
shell: pwsh
|
||||
env:
|
||||
HYPERFRAMES_SKIP_SKILLS: "1"
|
||||
run: |
|
||||
$dir = "$env:RUNNER_TEMP\probe"
|
||||
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
||||
Set-Location $dir
|
||||
node "$env:GITHUB_WORKSPACE\packages\cli\dist\cli.js" init canary --example blank --non-interactive --skip-skills
|
||||
Copy-Item "$env:GITHUB_WORKSPACE\.github\workflows\fixtures\windows-canary.html" "canary\index.html" -Force
|
||||
- name: Install Chrome before watching
|
||||
shell: pwsh
|
||||
run: |
|
||||
Set-Location "$env:RUNNER_TEMP\probe\canary"
|
||||
node "$env:GITHUB_WORKSPACE\packages\cli\dist\cli.js" browser ensure
|
||||
- name: Run the commands while watching for windows
|
||||
shell: pwsh
|
||||
run: .github/scripts/windows-window-probe.ps1 -Cli "$env:GITHUB_WORKSPACE\packages\cli\dist\cli.js" -Project "$env:RUNNER_TEMP\probe\canary"
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"packages/aws-lambda": {
|
||||
"name": "@hyperframes/aws-lambda",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-ecr": "^3.1146.0",
|
||||
"@aws-sdk/client-lambda": "^3.1146.0",
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@hyperframes/cli",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"bin": {
|
||||
"hyperframes": "./bin/hyperframes.mjs",
|
||||
"hyperframes-localize-fonts": "./bin/hyperframes-localize-fonts.mjs",
|
||||
@@ -118,7 +118,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@hyperframes/core",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@chenglou/pretext": "^0.0.5",
|
||||
"@hyperframes/lint": "workspace:*",
|
||||
@@ -163,15 +163,15 @@
|
||||
},
|
||||
"packages/engine": {
|
||||
"name": "@hyperframes/engine",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^2.0.5",
|
||||
"@hyperframes/core": "workspace:^",
|
||||
"@hyperframes/parsers": "workspace:^",
|
||||
"hono": "^4.6.0",
|
||||
"linkedom": "^0.18.12",
|
||||
"puppeteer": "^25.8.0",
|
||||
"puppeteer-core": "^25.8.0",
|
||||
"puppeteer": "~25.11.0",
|
||||
"puppeteer-core": "^25.11.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.10",
|
||||
@@ -182,7 +182,7 @@
|
||||
},
|
||||
"packages/gcp-cloud-run": {
|
||||
"name": "@hyperframes/gcp-cloud-run",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@google-cloud/storage": "^7.14.0",
|
||||
"@google-cloud/workflows": "^4.2.0",
|
||||
@@ -203,7 +203,7 @@
|
||||
},
|
||||
"packages/inspector": {
|
||||
"name": "@hyperframes/inspector",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"bin": {
|
||||
"hyperframes-inspector": "./dist/server.js",
|
||||
},
|
||||
@@ -226,7 +226,7 @@
|
||||
},
|
||||
"packages/lint": {
|
||||
"name": "@hyperframes/lint",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@hyperframes/parsers": "workspace:*",
|
||||
"acorn": "^8.17.0",
|
||||
@@ -246,7 +246,7 @@
|
||||
},
|
||||
"packages/parsers": {
|
||||
"name": "@hyperframes/parsers",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.0",
|
||||
"acorn": "^8.17.0",
|
||||
@@ -267,7 +267,7 @@
|
||||
},
|
||||
"packages/player": {
|
||||
"name": "@hyperframes/player",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@hyperframes/core": "workspace:*",
|
||||
},
|
||||
@@ -282,7 +282,7 @@
|
||||
},
|
||||
"packages/producer": {
|
||||
"name": "@hyperframes/producer",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^2.0.5",
|
||||
"@hyperframes/core": "workspace:*",
|
||||
@@ -293,8 +293,8 @@
|
||||
"hono": "^4.6.0",
|
||||
"linkedom": "^0.18.12",
|
||||
"postcss": "^8.4.0",
|
||||
"puppeteer": "^25.2.1",
|
||||
"puppeteer-core": "^25.2.1",
|
||||
"puppeteer": "~25.11.0",
|
||||
"puppeteer-core": "^25.11.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hyperframes/aws-lambda": "workspace:*",
|
||||
@@ -308,7 +308,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@hyperframes/sdk",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@hyperframes/core": "workspace:*",
|
||||
"@hyperframes/parsers": "workspace:*",
|
||||
@@ -338,7 +338,7 @@
|
||||
},
|
||||
"packages/shader-transitions": {
|
||||
"name": "@hyperframes/shader-transitions",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"html2canvas": "^1.4.1",
|
||||
},
|
||||
@@ -351,7 +351,7 @@
|
||||
},
|
||||
"packages/studio": {
|
||||
"name": "@hyperframes/studio",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@base-ui/react": "1.7.0",
|
||||
"@codemirror/autocomplete": "^6.20.1",
|
||||
@@ -405,7 +405,7 @@
|
||||
},
|
||||
"packages/studio-server": {
|
||||
"name": "@hyperframes/studio-server",
|
||||
"version": "0.8.194",
|
||||
"version": "0.8.210",
|
||||
"dependencies": {
|
||||
"@hyperframes/core": "workspace:*",
|
||||
"@hyperframes/parsers": "workspace:*",
|
||||
@@ -1553,7 +1553,7 @@
|
||||
|
||||
"es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="],
|
||||
|
||||
"esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="],
|
||||
"esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
@@ -1955,7 +1955,7 @@
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"puppeteer": ["puppeteer@25.8.0", "", { "dependencies": { "@puppeteer/browsers": "3.2.1", "chromium-bidi": "17.0.2", "devtools-protocol": "0.0.1666840", "lilconfig": "^3.1.3", "puppeteer-core": "25.8.0", "typed-query-selector": "^2.12.2" }, "bin": { "puppeteer": "lib/puppeteer/node/cli.js" } }, "sha512-3gcUJ+Jfodb5zNa/lWLZukBUwYiRIwAc8WRICoqfi+ZYmNWqpsPFyanTU3Gw/lhgII9aotVbAmhT6/NKHWgyUA=="],
|
||||
"puppeteer": ["puppeteer@25.11.0", "", { "dependencies": { "@puppeteer/browsers": "3.2.2", "chromium-bidi": "17.0.2", "devtools-protocol": "0.0.1680125", "lilconfig": "^3.1.3", "puppeteer-core": "25.11.0", "typed-query-selector": "^2.12.2" }, "bin": { "puppeteer": "lib/puppeteer/node/cli.js" } }, "sha512-n+psxZFH/qlJ3gwTZMxTLUZoE98FAOEjT/BSxHXvPmNCGAfN/kq0UnIZ8m9RB4rAVvJr7zRHUnKKg98GLMUyyQ=="],
|
||||
|
||||
"puppeteer-core": ["puppeteer-core@25.11.0", "", { "dependencies": { "@puppeteer/browsers": "3.2.2", "chromium-bidi": "17.0.2", "devtools-protocol": "0.0.1680125", "typed-query-selector": "^2.12.2", "webdriver-bidi-protocol": "0.4.3", "ws": "^8.21.3" } }, "sha512-Ujym7JbxoAqanaxGcHgmTomfhC01HfDCSuGZ8Hs9boHrDjzbNVMnFRJo2KtbDT9LCRgeJoFnkn9JQZ41eEUrLw=="],
|
||||
|
||||
@@ -2343,12 +2343,6 @@
|
||||
|
||||
"path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers": ["@puppeteer/browsers@3.2.1", "", { "dependencies": { "modern-tar": "^0.8.0", "yargs": "^18.0.0" }, "peerDependencies": { "proxy-agent": ">=8.0.1", "yauzl": "^2.10.0 || ^3.4.0" }, "optionalPeers": ["proxy-agent", "yauzl"], "bin": { "browsers": "lib/main-cli.js" } }, "sha512-KDz+3qDRdBAlRlMjmKyj6dEs33YHTk/xRHEENSXq6TNnhgoU15ruSHtEBeVF6OZ9tBDY55Se4P0nFMNsipzU9A=="],
|
||||
|
||||
"puppeteer/devtools-protocol": ["devtools-protocol@0.0.1666840", "", {}, "sha512-gCcO42XCHKEs7Ag0S7aGYsnJ7hlgrO3qderYqeiY0Eqk+0GFfuvT13IA0hHreJTa2KCdDVyGMeOhdMNmrrTjVg=="],
|
||||
|
||||
"puppeteer/puppeteer-core": ["puppeteer-core@25.8.0", "", { "dependencies": { "@puppeteer/browsers": "3.2.1", "chromium-bidi": "17.0.2", "devtools-protocol": "0.0.1666840", "typed-query-selector": "^2.12.2", "webdriver-bidi-protocol": "0.4.2", "ws": "^8.21.1" } }, "sha512-LDOrawV8vfCVk+yLj2ozvajNP4Sv3OV9y3Tpiyy2g2Z+aQlbcozP6KJfI4iSBq7YQER+86ihEtPa5ioiZyWxMQ=="],
|
||||
|
||||
"puppeteer-core/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
|
||||
|
||||
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
@@ -2447,12 +2441,6 @@
|
||||
|
||||
"node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="],
|
||||
|
||||
"puppeteer/puppeteer-core/webdriver-bidi-protocol": ["webdriver-bidi-protocol@0.4.2", "", {}, "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA=="],
|
||||
|
||||
"puppeteer/puppeteer-core/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
|
||||
|
||||
"tsup/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="],
|
||||
|
||||
"tsup/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="],
|
||||
@@ -2633,12 +2621,6 @@
|
||||
|
||||
"google-gax/retry-request/teeny-request/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
|
||||
|
||||
"@puppeteer/browsers/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"@puppeteer/browsers/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
@@ -2650,19 +2632,5 @@
|
||||
"google-gax/retry-request/teeny-request/http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"google-gax/retry-request/teeny-request/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/cliui/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
|
||||
"puppeteer/@puppeteer/browsers/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { checkForUpdate } from "./utils/updateCheck.js";
|
||||
import { refreshSkillsCache } from "./utils/skillsUpdateCheck.js";
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ for (const stream of [process.stdout, process.stderr]) {
|
||||
// probe lands in a directory that does not contain the bundled worker.
|
||||
// We emit the worker entry next to cli.js (see tsup.config.ts) and tell
|
||||
// the pool where to find it via the published env-var override.
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { existsSync } from "node:fs";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// fallow-ignore-file unused-file
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { injectDeterministicFontFaces } from "@hyperframes/producer";
|
||||
import { runFontLocalize, stampFontVersions } from "./fontLocalize.js";
|
||||
import { PRODUCER_VERSION, VERSION } from "./version.js";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { isAbsolute, join } from "node:path";
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { constants, setPriority } from "node:os";
|
||||
import { runBrowserInstall } from "./browser/installRun.js";
|
||||
import { ensureBrowser, releaseOwnedBrowserInstallLock } from "./browser/manager.js";
|
||||
|
||||
@@ -72,7 +72,7 @@ const openWithBundle = (bundleId: string, dir: string): boolean =>
|
||||
/** Single-instance app: a running copy is forwarded the folder. Only a refused spawn counts as failed. */
|
||||
function launchApp(executable: string, dir: string): boolean {
|
||||
try {
|
||||
const child = spawn(executable, [dir], { detached: true, stdio: "ignore" });
|
||||
const child = spawn(executable, [dir], { detached: true, stdio: "ignore", windowsHide: false });
|
||||
child.on("error", () => {});
|
||||
child.unref();
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { promisify } from "node:util";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { hideChildConsoles } from "./hideChildConsoles.js";
|
||||
|
||||
function fakeModule() {
|
||||
const calls: unknown[][] = [];
|
||||
const record = (name: string) => vi.fn((...args: unknown[]) => calls.push([name, ...args]));
|
||||
const module: Record<string, unknown> = {};
|
||||
for (const name of [
|
||||
"spawn",
|
||||
"spawnSync",
|
||||
"exec",
|
||||
"execSync",
|
||||
"execFile",
|
||||
"execFileSync",
|
||||
"fork",
|
||||
]) {
|
||||
module[name] = record(name);
|
||||
}
|
||||
const execFile = module.execFile as Record<symbol, unknown>;
|
||||
execFile[promisify.custom] = vi.fn((...args: unknown[]) =>
|
||||
calls.push(["execFile.promisified", ...args]),
|
||||
);
|
||||
return { module, calls };
|
||||
}
|
||||
|
||||
type Entry = "spawn" | "spawnSync" | "exec" | "execSync" | "execFile" | "execFileSync" | "fork";
|
||||
type Calls = Record<Entry, (...args: unknown[]) => unknown>;
|
||||
|
||||
const run = (platform: NodeJS.Platform, use: (m: Calls) => void) => {
|
||||
const { module, calls } = fakeModule();
|
||||
hideChildConsoles(module, platform);
|
||||
use(module as Calls);
|
||||
return calls;
|
||||
};
|
||||
|
||||
describe("hideChildConsoles", () => {
|
||||
it("hides a console on every way of passing the options", () => {
|
||||
const callback = () => {};
|
||||
const calls = run("win32", (m) => {
|
||||
m.spawn("a");
|
||||
m.spawn("a", ["x"]);
|
||||
m.spawnSync("a", ["x"], { cwd: "." });
|
||||
m.execFile("a", ["x"], callback);
|
||||
m.exec("a", callback);
|
||||
m.execSync("a", { timeout: 1 });
|
||||
m.execFileSync("a", { timeout: 1 });
|
||||
m.fork("a", ["x"], {});
|
||||
});
|
||||
expect(
|
||||
calls.map(([name, , ...rest]) => [
|
||||
name,
|
||||
rest.find((a) => a && typeof a === "object" && !Array.isArray(a)),
|
||||
]),
|
||||
).toEqual([
|
||||
["spawn", { windowsHide: true }],
|
||||
["spawn", { windowsHide: true }],
|
||||
["spawnSync", { windowsHide: true, cwd: "." }],
|
||||
["execFile", { windowsHide: true }],
|
||||
["exec", { windowsHide: true }],
|
||||
["execSync", { windowsHide: true, timeout: 1 }],
|
||||
["execFileSync", { windowsHide: true, timeout: 1 }],
|
||||
["fork", { windowsHide: true }],
|
||||
]);
|
||||
expect(calls[3]?.at(-1)).toBe(callback);
|
||||
});
|
||||
|
||||
it("leaves a caller that wants the window alone", () => {
|
||||
const calls = run("win32", (m) =>
|
||||
m.spawn("browser", [], { windowsHide: false, detached: true }),
|
||||
);
|
||||
expect(calls[0]?.[3]).toEqual({ windowsHide: false, detached: true });
|
||||
});
|
||||
|
||||
it("hides the promisified execFile too", async () => {
|
||||
const { module, calls } = fakeModule();
|
||||
hideChildConsoles(module, "win32");
|
||||
await promisify(module.execFile as never)("a", ["x"]);
|
||||
expect(calls[0]).toEqual(["execFile.promisified", "a", ["x"], { windowsHide: true }]);
|
||||
});
|
||||
|
||||
it("does nothing off Windows", () => {
|
||||
const { module } = fakeModule();
|
||||
const before = { ...module };
|
||||
hideChildConsoles(module, "linux");
|
||||
expect(module).toEqual(before);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import childProcess from "node:child_process";
|
||||
import { syncBuiltinESMExports } from "node:module";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
type Spawner = (command: string, ...rest: unknown[]) => unknown;
|
||||
type ChildProcessModule = Record<string, unknown>;
|
||||
|
||||
const ENTRY_POINTS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync", "fork"];
|
||||
|
||||
/** The call's arguments with `windowsHide: true` set unless the caller chose either way. */
|
||||
function hidden(command: string, rest: unknown[]): unknown[] {
|
||||
const args = [...rest];
|
||||
const at = args.findIndex((arg) => arg && typeof arg === "object" && !Array.isArray(arg));
|
||||
if (at >= 0) {
|
||||
args[at] = { windowsHide: true, ...(args[at] as object) };
|
||||
} else {
|
||||
const callback = args.findIndex((arg) => typeof arg === "function");
|
||||
args.splice(callback < 0 ? args.length : callback, 0, { windowsHide: true });
|
||||
}
|
||||
return [command, ...args];
|
||||
}
|
||||
|
||||
/** Windows opens a console window for every child that does not say `windowsHide`; `false` opts out. */
|
||||
export function hideChildConsoles(
|
||||
target: ChildProcessModule = childProcess as unknown as ChildProcessModule,
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
): void {
|
||||
if (platform !== "win32") return;
|
||||
for (const name of ENTRY_POINTS) {
|
||||
const original = target[name] as Spawner & { [promisify.custom]?: Spawner };
|
||||
const wrapped: Spawner & { [promisify.custom]?: Spawner } = (command, ...rest) =>
|
||||
original(...(hidden(command, rest) as [string, ...unknown[]]));
|
||||
const custom = original[promisify.custom];
|
||||
if (custom)
|
||||
wrapped[promisify.custom] = (command, ...rest) =>
|
||||
custom(...(hidden(command, rest) as [string]));
|
||||
target[name] = wrapped;
|
||||
}
|
||||
if (target === (childProcess as unknown as ChildProcessModule)) syncBuiltinESMExports();
|
||||
}
|
||||
|
||||
hideChildConsoles();
|
||||
@@ -29,7 +29,7 @@ describe("openBrowser", () => {
|
||||
"--remote-debugging-port=9222",
|
||||
"http://localhost:3002",
|
||||
],
|
||||
{ detached: true, stdio: "ignore" },
|
||||
{ detached: true, stdio: "ignore", windowsHide: false },
|
||||
);
|
||||
expect(unref).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
@@ -75,6 +75,7 @@ export function openBrowser(url: string, options: OpenBrowserOptions = {}): void
|
||||
const child = spawn(options.browserPath, args, {
|
||||
detached: true,
|
||||
stdio: "ignore",
|
||||
windowsHide: false,
|
||||
});
|
||||
child.on("error", () => {});
|
||||
child.unref();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { unlinkSync } from "node:fs";
|
||||
import { connect, createServer, type Socket } from "node:net";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "./utils/hideChildConsoles.js";
|
||||
import { spawn } from "node:child_process";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "../utils/hideChildConsoles.js";
|
||||
import { createRequire } from "node:module";
|
||||
import {
|
||||
droppedSpeechGaps,
|
||||
|
||||
@@ -99,8 +99,8 @@
|
||||
"@hyperframes/parsers": "workspace:^",
|
||||
"hono": "^4.6.0",
|
||||
"linkedom": "^0.18.12",
|
||||
"puppeteer": "^25.8.0",
|
||||
"puppeteer-core": "^25.8.0"
|
||||
"puppeteer": "~25.11.0",
|
||||
"puppeteer-core": "^25.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.10",
|
||||
|
||||
@@ -65,10 +65,15 @@ function supportsSettsDuration(binary = getFfmpegBinary()): Promise<boolean> {
|
||||
let supported = settsDurationSupport.get(binary);
|
||||
if (!supported) {
|
||||
supported = new Promise((resolve) => {
|
||||
execFile(binary, ["-hide_banner", "-h", "bsf=setts"], (error, stdout) => {
|
||||
// A binary that cannot even print help fails the encode with its own error, not this one.
|
||||
resolve(error !== null || stdout.includes("duration"));
|
||||
});
|
||||
execFile(
|
||||
binary,
|
||||
["-hide_banner", "-h", "bsf=setts"],
|
||||
{ windowsHide: true },
|
||||
(error, stdout) => {
|
||||
// A binary that cannot even print help fails the encode with its own error, not this one.
|
||||
resolve(error !== null || stdout.includes("duration"));
|
||||
},
|
||||
);
|
||||
});
|
||||
settsDurationSupport.set(binary, supported);
|
||||
}
|
||||
|
||||
@@ -69,8 +69,12 @@ export function shellStarts(binary: string, timeoutMs = 10_000): boolean {
|
||||
if (answer === undefined) {
|
||||
const probe = ["--no-sandbox", "--dump-dom", "about:blank"];
|
||||
answer =
|
||||
spawnSync(binary, probe, { stdio: "ignore", timeout: timeoutMs, killSignal: "SIGKILL" })
|
||||
?.status === 0;
|
||||
spawnSync(binary, probe, {
|
||||
stdio: "ignore",
|
||||
timeout: timeoutMs,
|
||||
killSignal: "SIGKILL",
|
||||
windowsHide: true,
|
||||
})?.status === 0;
|
||||
if (!answer) debug("skipping %s: it did not start", binary);
|
||||
starts.set(binary, answer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// @puppeteer/browsers before 3.2.2 started Chrome detached on every platform. On Windows a detached Chrome has no
|
||||
// console, so Windows Terminal opens a window for each Chrome process of a render.
|
||||
const FIRST_SAFE = [3, 2, 2];
|
||||
|
||||
function launcherVersion(): number[] {
|
||||
const fromEngine = createRequire(new URL("../../package.json", import.meta.url));
|
||||
const fromPuppeteer = createRequire(fromEngine.resolve("puppeteer/package.json"));
|
||||
const fromCore = createRequire(fromPuppeteer.resolve("puppeteer-core/package.json"));
|
||||
return (fromCore("@puppeteer/browsers/package.json") as { version: string }).version
|
||||
.split(".")
|
||||
.map(Number);
|
||||
}
|
||||
|
||||
describe("the Puppeteer the engine launches Chrome with", () => {
|
||||
it("does not start Chrome detached on Windows", () => {
|
||||
const version = launcherVersion();
|
||||
const behind = FIRST_SAFE.findIndex((part, i) => version[i] !== part);
|
||||
const atLeast = behind === -1 || (version[behind] ?? 0) > (FIRST_SAFE[behind] ?? 0);
|
||||
expect(atLeast, `@puppeteer/browsers ${version.join(".")}`).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -474,7 +474,10 @@ function ffprobeMajor(): Promise<number | undefined> {
|
||||
runtimeFfprobeMajor ??= new Promise((resolve) => {
|
||||
let out = "";
|
||||
const binary = getFfprobeBinary();
|
||||
const proc = spawn(binary, ["-version"], { stdio: ["ignore", "pipe", "ignore"] });
|
||||
const proc = spawn(binary, ["-version"], {
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
windowsHide: true,
|
||||
});
|
||||
proc.stdout.on("data", (chunk) => (out += String(chunk)));
|
||||
proc.on("error", () => resolve(undefined));
|
||||
proc.on("close", () => resolve(Number(/version n?(\d+)/.exec(out)?.[1]) || undefined));
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
"hono": "^4.6.0",
|
||||
"linkedom": "^0.18.12",
|
||||
"postcss": "^8.4.0",
|
||||
"puppeteer": "^25.2.1",
|
||||
"puppeteer-core": "^25.2.1"
|
||||
"puppeteer": "~25.11.0",
|
||||
"puppeteer-core": "^25.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hyperframes/aws-lambda": "workspace:*",
|
||||
|
||||
Reference in New Issue
Block a user