diff --git a/scripts/generate-npm-shrinkwrap.mjs b/scripts/generate-npm-shrinkwrap.mjs index e3728a7b8c8d..a611672aaf6e 100644 --- a/scripts/generate-npm-shrinkwrap.mjs +++ b/scripts/generate-npm-shrinkwrap.mjs @@ -1,4 +1,6 @@ #!/usr/bin/env node +// Generates npm-shrinkwrap.json files that mirror pnpm lock policy for +// published packages while stripping dev-only dependency state. import { execFileSync } from "node:child_process"; import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -384,6 +386,9 @@ function packageJsonForShrinkwrap(packageJson, shrinkwrapOverrides) { return normalized; } +/** + * Resolves the npm command invocation used by shrinkwrap generation. + */ export function createNpmShrinkwrapCommand(args, options = {}) { return resolveNpmRunner({ comSpec: options.comSpec, @@ -395,6 +400,9 @@ export function createNpmShrinkwrapCommand(args, options = {}) { }); } +/** + * Reads a positive integer env override for shrinkwrap subprocess limits. + */ export function readPositiveIntEnv(name, fallback, env = process.env) { const text = String(env[name] ?? fallback).trim(); if (!/^\d+$/u.test(text)) { @@ -407,6 +415,9 @@ export function readPositiveIntEnv(name, fallback, env = process.env) { return value; } +/** + * Builds execFileSync options with bounded timeout and output buffer limits. + */ export function createNpmShrinkwrapExecOptions(invocation, cwd, env = process.env) { return { cwd, @@ -1299,6 +1310,8 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me } export { + // Test-facing helpers cover lockfile normalization, override merging, and + // changed-package detection without invoking npm. collectCurrentShrinkwrapOverrides, collectOverrideViolations, collectPnpmLockViolations, diff --git a/scripts/measure-rpc-rtt.mjs b/scripts/measure-rpc-rtt.mjs index f806668a1c57..0911eaa94291 100644 --- a/scripts/measure-rpc-rtt.mjs +++ b/scripts/measure-rpc-rtt.mjs @@ -1,3 +1,5 @@ +// Measures gateway RPC round-trip time by launching an isolated local gateway +// and writing qa-lab-compatible summary artifacts. import { spawn } from "node:child_process"; import { randomUUID } from "node:crypto"; import { existsSync } from "node:fs"; @@ -10,7 +12,9 @@ import { fileURLToPath, pathToFileURL } from "node:url"; const DEFAULT_METHODS = ["health", "config.get"]; const DEFAULT_ITERATIONS = 10; +/** Maximum time to wait for a spawned gateway to become reachable. */ export const READY_TIMEOUT_MS = 120_000; +/** Per-probe timeout used while polling gateway readiness endpoints. */ export const READY_PROBE_TIMEOUT_MS = 1_000; const PARENT_TERMINATION_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM"]; const IS_DIRECT_RUN = @@ -100,6 +104,9 @@ function formatErrorMessage(error) { return String(error); } +/** + * Polls readiness endpoints while also failing fast if the child exits. + */ export async function waitForGatewayReady({ child, fetchImpl = fetch, @@ -165,6 +172,9 @@ function resolveOpenClawLaunchArgs(repoRoot, sourceEntryExists = existsSync) { return [path.join(repoRoot, "openclaw.mjs")]; } +/** + * Signals the gateway process group on POSIX so spawned children are cleaned up. + */ export function signalGatewayProcess(child, signal, killProcess = defaultKillProcess) { if (process.platform !== "win32" && typeof child.pid === "number") { try { @@ -187,6 +197,9 @@ export function signalGatewayProcess(child, signal, killProcess = defaultKillPro } } +/** + * Checks process-group liveness without treating an already-exited child as an error. + */ export function isGatewayProcessAlive(child, killProcess = defaultKillProcess) { if (process.platform !== "win32" && typeof child.pid === "number") { try { @@ -210,6 +223,9 @@ function signalGatewayProcessForParentExit(child, signal, killProcess) { } } +/** + * Installs parent-process cleanup handlers for a spawned gateway. + */ export function installGatewayParentCleanup( child, { killProcess = defaultKillProcess, processLike = process } = {}, @@ -255,6 +271,9 @@ async function waitForGatewayExit(child, timeoutMs, killProcess = defaultKillPro return !isGatewayProcessAlive(child, killProcess); } +/** + * Stops the gateway with SIGTERM first and SIGKILL after the grace window. + */ export async function stopGateway(child, options = {}) { if (!isGatewayProcessAlive(child, options.killProcess)) { return; @@ -275,6 +294,9 @@ async function closeFileHandles(handles) { } } +/** + * Starts an isolated loopback gateway with temp HOME/state directories. + */ export async function startGateway({ configPath, env = process.env, @@ -354,6 +376,9 @@ export async function startGateway({ return child; } +/** + * Removes the temporary root used by the RPC RTT probe. + */ export async function cleanupTempRoot(tempRoot, { rmImpl = fs.rm } = {}) { try { await rmImpl(tempRoot, { force: true, recursive: true }); diff --git a/scripts/prepare-extension-package-boundary-artifacts.mjs b/scripts/prepare-extension-package-boundary-artifacts.mjs index e3dec0d8aa04..61f6d19284cb 100644 --- a/scripts/prepare-extension-package-boundary-artifacts.mjs +++ b/scripts/prepare-extension-package-boundary-artifacts.mjs @@ -1,3 +1,5 @@ +// Prepares declaration and entry-shim artifacts that prove plugin package +// boundary imports resolve through public package surfaces. import { spawn } from "node:child_process"; import fs from "node:fs"; import path, { resolve } from "node:path"; @@ -248,6 +250,9 @@ function isRelevantTypeInput(filePath) { return TYPE_INPUT_EXTENSIONS.has(path.extname(filePath)); } +/** + * Parses the artifact preparation mode from CLI arguments. + */ export function parseMode(argv = process.argv.slice(2)) { const modeArg = argv.find((arg) => arg.startsWith("--mode=")); const mode = modeArg?.slice("--mode=".length) ?? "all"; @@ -257,6 +262,9 @@ export function parseMode(argv = process.argv.slice(2)) { return mode; } +/** + * Reads the root shim timeout override for long package-boundary builds. + */ export function resolveBoundaryRootShimsTimeoutMs(env = process.env) { const raw = env.OPENCLAW_PLUGIN_SDK_BOUNDARY_ROOT_SHIMS_TIMEOUT_MS?.trim(); if (!raw) { @@ -309,6 +317,9 @@ function collectOldestMtime(paths, params = {}) { return Number.isFinite(oldestMtimeMs) ? oldestMtimeMs : null; } +/** + * Compares input and output mtimes to skip fresh generated artifacts. + */ export function isArtifactSetFresh(params) { const newestInputMtimeMs = collectNewestMtime(params.inputPaths, { rootDir: params.rootDir, @@ -335,6 +346,9 @@ function writeStampFile(relativePath) { fs.writeFileSync(filePath, `${new Date().toISOString()}\n`, "utf8"); } +/** + * Prefixes streamed child output line-by-line without breaking partial chunks. + */ export function createPrefixedOutputWriter(label, target) { let buffered = ""; const prefix = `[${label}] `; @@ -412,6 +426,9 @@ function installNodeStepParentSignalForwarders() { }); } +/** + * Runs one artifact step with timeout, abort propagation, and prefixed output. + */ export function runNodeStep(label, args, timeoutMs, params = {}) { const abortController = params.abortController; const spawnImpl = params.spawnImpl ?? spawn; @@ -516,6 +533,9 @@ export function runNodeStep(label, args, timeoutMs, params = {}) { }); } +/** + * Runs independent artifact steps together and aborts siblings on first failure. + */ export async function runNodeStepsInParallel(steps) { const abortController = new AbortController(); const results = await Promise.allSettled( @@ -529,6 +549,9 @@ export async function runNodeStepsInParallel(steps) { } } +/** + * Chooses serial or parallel artifact execution based on local heavy-check policy. + */ export async function runNodeSteps(steps, env = process.env) { if (!isLocalCheckEnabled(env)) { await runNodeStepsInParallel(steps); diff --git a/scripts/prepare-git-hooks.mjs b/scripts/prepare-git-hooks.mjs index a4d50b824339..f780db8d0261 100644 --- a/scripts/prepare-git-hooks.mjs +++ b/scripts/prepare-git-hooks.mjs @@ -1,3 +1,5 @@ +// Configures this checkout's Git hooks path during package prepare when git +// and the hooks directory are available. import { spawnSync } from "node:child_process"; import { existsSync } from "node:fs"; import { dirname, join } from "node:path"; @@ -21,6 +23,9 @@ function runGit(spawn, gitBin, args, cwd, stdio) { }); } +/** + * Installs the repo-local hooks path and returns a structured reason if skipped. + */ export function configurePrepareGitHooks(params = {}) { const cwd = params.cwd ?? DEFAULT_PACKAGE_ROOT; const exists = params.existsSync ?? existsSync; @@ -32,13 +37,11 @@ export function configurePrepareGitHooks(params = {}) { return { configured: false, reason: "missing-hooks-dir" }; } - const worktree = runGit( - spawn, - gitBin, - ["rev-parse", "--is-inside-work-tree"], - cwd, - ["ignore", "pipe", "ignore"], - ); + const worktree = runGit(spawn, gitBin, ["rev-parse", "--is-inside-work-tree"], cwd, [ + "ignore", + "pipe", + "ignore", + ]); const missingGitReason = getMissingGitReason(worktree.error); if (missingGitReason) { return { configured: false, reason: missingGitReason }; @@ -47,13 +50,11 @@ export function configurePrepareGitHooks(params = {}) { return { configured: false, reason: "not-worktree" }; } - const configured = runGit( - spawn, - gitBin, - ["config", "core.hooksPath", "git-hooks"], - cwd, - ["ignore", "ignore", "pipe"], - ); + const configured = runGit(spawn, gitBin, ["config", "core.hooksPath", "git-hooks"], cwd, [ + "ignore", + "ignore", + "pipe", + ]); const configMissingGitReason = getMissingGitReason(configured.error); if (configMissingGitReason) { return { configured: false, reason: configMissingGitReason }; diff --git a/scripts/process-warning-filter.mjs b/scripts/process-warning-filter.mjs index 04bd6ed99e61..0e53cc48b251 100644 --- a/scripts/process-warning-filter.mjs +++ b/scripts/process-warning-filter.mjs @@ -1,5 +1,10 @@ +// Installs a process-wide warning filter for dependency warnings that are known +// noise in current toolchains. const warningFilterKey = Symbol.for("openclaw.warning-filter"); +/** + * Suppresses punycode deprecation warnings while preserving all other warnings. + */ export function installProcessWarningFilter() { if (globalThis[warningFilterKey]?.installed) { return; diff --git a/scripts/profile-extension-memory.mjs b/scripts/profile-extension-memory.mjs index 5151e2acb0e0..b40a187cba52 100644 --- a/scripts/profile-extension-memory.mjs +++ b/scripts/profile-extension-memory.mjs @@ -1,5 +1,7 @@ #!/usr/bin/env node +// Profiles peak RSS for built bundled plugin entrypoints and emits a JSON +// report suitable for extension memory budget review. import { spawn } from "node:child_process"; import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; @@ -53,6 +55,9 @@ function parsePositiveInt(raw, flagName) { return parsed; } +/** + * Parses extension memory profiler options after pnpm's optional separator. + */ export function parseArgs(argv) { const args = stripLeadingPackageManagerSeparator(argv); const options = { @@ -171,6 +176,9 @@ function summarizeStderr(stderr, lines = 8, maxChars = STDERR_PREVIEW_MAX_CHARS) )}`; } +/** + * Runs one import scenario in a child process and captures bounded output plus RSS. + */ export async function runCase({ repoRoot, env, diff --git a/scripts/profile-tsgo.mjs b/scripts/profile-tsgo.mjs index 2e7151f4dd95..1dab0771812b 100644 --- a/scripts/profile-tsgo.mjs +++ b/scripts/profile-tsgo.mjs @@ -1,5 +1,7 @@ #!/usr/bin/env node +// Profiles selected tsgo graphs and writes diagnostics/trace artifacts for +// TypeScript graph size and performance investigations. import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path";