diff --git a/scripts/check-extension-wildcard-reexports.mjs b/scripts/check-extension-wildcard-reexports.mjs index 0eaf265c1bde..c0965f95eb31 100644 --- a/scripts/check-extension-wildcard-reexports.mjs +++ b/scripts/check-extension-wildcard-reexports.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Rejects local wildcard re-exports in guarded extension API barrels. import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -38,6 +39,9 @@ async function listGuardedFiles(rootDir = repoRoot) { ); } +/** + * Finds local wildcard re-export lines in a barrel source string. + */ export function findLocalWildcardReexports(source) { return source .split(/\r?\n/u) @@ -45,6 +49,9 @@ export function findLocalWildcardReexports(source) { .filter(({ text }) => LOCAL_WILDCARD_REEXPORT_PATTERN.test(text)); } +/** + * Collects guarded extension API/runtime barrels that use wildcard re-exports. + */ export async function collectExtensionWildcardReexports(rootDir = repoRoot) { const files = await listGuardedFiles(rootDir); const violations = []; @@ -61,6 +68,9 @@ export async function collectExtensionWildcardReexports(rootDir = repoRoot) { return violations; } +/** + * Runs the extension wildcard re-export guard. + */ export async function main(argv = process.argv.slice(2), io = process) { const json = argv.includes("--json"); const violations = await collectExtensionWildcardReexports(); diff --git a/scripts/check-gateway-cpu-scenarios.mjs b/scripts/check-gateway-cpu-scenarios.mjs index 862b761b319e..c7973d5dc364 100644 --- a/scripts/check-gateway-cpu-scenarios.mjs +++ b/scripts/check-gateway-cpu-scenarios.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Runs gateway startup and QA scenarios while checking hot CPU observations. import { spawnSync as defaultSpawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -350,6 +351,9 @@ async function main(params = {}) { } } +/** + * Test-only access to the gateway CPU scenario parser and runner helpers. + */ export const testing = { hasPrivateQaDist, parseArgs, diff --git a/scripts/check-gateway-watch-regression.mjs b/scripts/check-gateway-watch-regression.mjs index 9902b6724498..4e5b866df28b 100644 --- a/scripts/check-gateway-watch-regression.mjs +++ b/scripts/check-gateway-watch-regression.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Measures gateway watch idle CPU and dist/runtime artifact churn. import { spawn, spawnSync } from "node:child_process"; import fs from "node:fs"; import net from "node:net"; @@ -44,10 +45,16 @@ const WATCH_GATEWAY_SKIP_ENV = { NODE_ENV: "test", }; +/** + * Maximum retained stdout/stderr text for gateway watch diagnostics. + */ export const WATCH_LOG_CAPTURE_MAX_CHARS = 2 * 1024 * 1024; const WATCH_BUILD_DETECTION_MAX_CHARS = 4096; const NON_NEGATIVE_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u; +/** + * Appends watch output while preserving only the diagnostic tail. + */ export function appendBoundedWatchLog(current, chunk, maxChars = WATCH_LOG_CAPTURE_MAX_CHARS) { const next = `${current}${String(chunk)}`; if (next.length <= maxChars) { @@ -62,6 +69,9 @@ function formatCapturedWatchLog(text, truncated) { : text; } +/** + * Updates bounded watch-build detection state from new output. + */ export function updateWatchBuildDetection(state, chunk) { const combined = `${state.buffer ?? ""}${String(chunk)}`; const next = appendBoundedWatchLog("", combined, WATCH_BUILD_DETECTION_MAX_CHARS); @@ -74,6 +84,9 @@ export function updateWatchBuildDetection(state, chunk) { }; } +/** + * Parses a safe non-negative integer CLI value. + */ export function readNonNegativeInteger(value, label) { const raw = String(value).trim(); if (!NON_NEGATIVE_INTEGER_PATTERN.test(raw)) { @@ -86,6 +99,9 @@ export function readNonNegativeInteger(value, label) { return parsed; } +/** + * Parses gateway watch regression CLI arguments. + */ export function parseArgs(argv) { const args = stripLeadingPackageManagerSeparator(argv); const options = { ...DEFAULTS }; @@ -396,6 +412,9 @@ function readProcessTreeCpuMs(rootPid) { return totalCpuMs; } +/** + * Reports whether gateway watch output contains a ready marker. + */ export function hasGatewayReadyLog(text) { return /\[gateway\] (?:http server listening|ready \()/.test(text); } @@ -504,6 +523,9 @@ function parseTimingFile(timeFilePath) { }; } +/** + * Runs a bounded gateway watch process and captures timing/log artifacts. + */ export async function runTimedWatch(options, outputDir, deps = {}) { const allocatePort = deps.allocateLoopbackPort ?? allocateLoopbackPort; const parseTiming = deps.parseTimingFile ?? parseTimingFile; @@ -642,6 +664,9 @@ export async function runTimedWatch(options, outputDir, deps = {}) { } } +/** + * Stops the timed watch child process with TERM/KILL fallback. + */ export async function stopTimedWatchChild(child, watchPid, options, deps = {}) { const killProcess = deps.killProcess ?? ((pid, signal) => process.kill(pid, signal)); const currentExit = () => @@ -749,6 +774,9 @@ function buildRunNodeDeps(env) { }; } +/** + * Reports whether restored CI artifacts need fresh build stamps. + */ export function shouldRefreshBuildStampForRestoredArtifacts(params) { return ( params.skipBuild === true && @@ -757,6 +785,9 @@ export function shouldRefreshBuildStampForRestoredArtifacts(params) { ); } +/** + * Writes build and runtime-postbuild stamps for the current artifact set. + */ export function writeBuildAndRuntimePostBuildStamps(params = {}) { const cwd = params.cwd ?? process.cwd(); writeBuildStamp({ cwd }); diff --git a/scripts/check-ingress-agent-owner-context.mjs b/scripts/check-ingress-agent-owner-context.mjs index c7b923f62b56..bd23b8451844 100644 --- a/scripts/check-ingress-agent-owner-context.mjs +++ b/scripts/check-ingress-agent-owner-context.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Ensures ingress agent command callsites pass explicit owner context. import path from "node:path"; import ts from "typescript"; import { bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; @@ -19,6 +20,9 @@ const enforcedFiles = new Set([ "src/gateway/server-node-events.ts", ]); +/** + * Finds legacy `agentCommand(...)` call lines in ingress-owned source. + */ export function findLegacyAgentCommandCallLines(content, fileName = "source.ts") { const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); return collectCallExpressionLines(ts, sourceFile, (node) => { @@ -27,6 +31,9 @@ export function findLegacyAgentCommandCallLines(content, fileName = "source.ts") }); } +/** + * Runs the ingress owner-context guard. + */ export async function main() { await runCallsiteGuard({ importMetaUrl: import.meta.url, diff --git a/scripts/check-kysely-guardrails.mjs b/scripts/check-kysely-guardrails.mjs index b0d405866e50..62aced59216e 100644 --- a/scripts/check-kysely-guardrails.mjs +++ b/scripts/check-kysely-guardrails.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Enforces Kysely and SQLite guardrails in infrastructure code. import { promises as fs } from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; @@ -217,6 +218,9 @@ function isPersistedStringCastType(typeText) { ].some((pattern) => pattern.test(typeText)); } +/** + * Collects Kysely/raw SQLite violations from one source file. + */ export function collectKyselyGuardrailViolations(content, relativePath) { const sourceFile = ts.createSourceFile(relativePath, content, ts.ScriptTarget.Latest, true); const imports = collectImports(sourceFile); @@ -338,6 +342,9 @@ export function collectKyselyGuardrailViolations(content, relativePath) { return violations; } +/** + * Collects Kysely guardrail violations across configured source roots. + */ export async function collectKyselyGuardrails() { const files = await collectTypeScriptFilesFromRoots(sourceRoots, { includeTests: true }); const violations = []; @@ -351,6 +358,9 @@ export async function collectKyselyGuardrails() { return violations; } +/** + * Runs the Kysely guardrail check. + */ export async function main() { const violations = await collectKyselyGuardrails(); if (violations.length === 0) { diff --git a/scripts/check-media-download-helper-roundtrip.mjs b/scripts/check-media-download-helper-roundtrip.mjs index 18e6030a24b4..045c93fe02fe 100644 --- a/scripts/check-media-download-helper-roundtrip.mjs +++ b/scripts/check-media-download-helper-roundtrip.mjs @@ -1,4 +1,5 @@ #!/usr/bin/env node +// Checks extension media downloads keep helper-read/save calls close together. import { execFileSync } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; diff --git a/scripts/check-memory-fd-repro.mjs b/scripts/check-memory-fd-repro.mjs index 8e5709b10c23..518aa44016de 100644 --- a/scripts/check-memory-fd-repro.mjs +++ b/scripts/check-memory-fd-repro.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Reproduces memory-search file descriptor retention with a synthetic workspace. import { spawn, spawnSync } from "node:child_process"; import fs from "node:fs"; import net from "node:net"; @@ -26,8 +27,17 @@ const ISSUE_FILE_COUNTS = [ const ISSUE_MEMORY_FILE_COUNT = ISSUE_FILE_COUNTS.reduce((sum, [, count]) => sum + count, 0); const DEFAULT_FILE_COUNT = 512; const DEFAULT_MAX_WORKSPACE_REG_FDS = process.platform === "darwin" ? 8 : 64; +/** + * Maximum gateway-ready output tail retained while waiting for startup. + */ export const GATEWAY_READY_OUTPUT_MAX_CHARS = 128 * 1024; +/** + * Maximum bytes read from the memory_search HTTP response. + */ export const MEMORY_SEARCH_RESPONSE_MAX_BYTES = 256 * 1024; +/** + * Probe query expected to hit the synthetic top-level memory file. + */ export const MEMORY_SEARCH_PROBE_QUERY = "Top-level memory file"; const SKIP_GATEWAY_ENV = { @@ -88,6 +98,9 @@ function stripPackageManagerSeparatorForKnownFlags(argv) { : argv; } +/** + * Parses a safe non-negative integer option. + */ export function readNumber(value, label) { const raw = String(value).trim(); if (!NON_NEGATIVE_INTEGER_PATTERN.test(raw)) { @@ -100,6 +113,9 @@ export function readNumber(value, label) { return parsed; } +/** + * Parses a safe positive integer option. + */ export function readPositiveNumber(value, label) { const parsed = readNumber(value, label); if (parsed <= 0) { @@ -118,6 +134,9 @@ function readPositiveNumberEnv(name, fallback) { return raw == null || raw.trim() === "" ? fallback : readPositiveNumber(raw, name); } +/** + * Parses memory FD repro CLI arguments and environment fallbacks. + */ export function parseArgs(argv) { const args = stripPackageManagerSeparatorForKnownFlags(argv); const stamp = new Date().toISOString().replace(/[:.]/g, "-"); @@ -278,6 +297,9 @@ function writeSyntheticWorkspace(workspaceDir, fileCount) { } } +/** + * Writes isolated OpenClaw config for the synthetic memory workspace. + */ export function writeConfig({ homeDir, workspaceDir, port, token }) { const configDir = path.join(homeDir, ".openclaw"); fs.mkdirSync(configDir, { recursive: true }); @@ -352,6 +374,9 @@ function preindexSyntheticMemory(env) { logStep("preindex complete"); } +/** + * Updates bounded gateway-ready output state from a stdout/stderr chunk. + */ export function updateGatewayReadyOutputState( state, chunk, @@ -431,10 +456,16 @@ function sampleFds({ label, pid, workspaceRealPath }) { return sample; } +/** + * Reports whether a spawned child has already exited. + */ export function hasChildExited(child) { return child.exitCode !== null || child.signalCode !== null; } +/** + * Waits until gateway output and listener state both indicate readiness. + */ export async function waitForGatewayReady({ child, port, logPath, timeoutMs }) { const startedAt = Date.now(); let outputState = { tail: "", readySeen: false }; @@ -458,6 +489,9 @@ export async function waitForGatewayReady({ child, port, logPath, timeoutMs }) { throw new Error(`gateway did not become ready within ${timeoutMs}ms; see ${logPath}`); } +/** + * Stops the gateway child using the default process/runtime hooks. + */ export async function stopGateway({ child, port }) { return stopGatewayWithRuntime({ child, @@ -467,6 +501,9 @@ export async function stopGateway({ child, port }) { }); } +/** + * Stops the gateway child and any remaining listener process. + */ export async function stopGatewayWithRuntime({ child, childExitPollIntervalMs = 100, @@ -499,6 +536,9 @@ export async function stopGatewayWithRuntime({ } } +/** + * Reads an HTTP response body up to a configured byte limit. + */ export { readBoundedResponseText }; function signalChild(child, signal) { @@ -549,6 +589,9 @@ function parseToolTextContent(result) { return null; } +/** + * Classifies the memory_search HTTP response into success/error details. + */ export function classifyMemorySearchInvokeResponse({ httpOk, status, bodyText }) { const parsedBody = parseJsonValue(bodyText); const body = asRecord(parsedBody); diff --git a/scripts/check-no-conflict-markers.mjs b/scripts/check-no-conflict-markers.mjs index ef9c8a00ebc6..e82cbd33faff 100644 --- a/scripts/check-no-conflict-markers.mjs +++ b/scripts/check-no-conflict-markers.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Rejects unresolved merge conflict markers in tracked files. import { execFileSync, spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -11,6 +12,9 @@ function isBinaryBuffer(buffer) { return buffer.includes(0); } +/** + * Returns one-based line numbers containing merge conflict markers. + */ export function findConflictMarkerLines(content) { const lines = content.split(/\r?\n/u); const matches = []; @@ -27,6 +31,9 @@ export function findConflictMarkerLines(content) { return matches; } +/** + * Lists tracked files in the repository. + */ export function listTrackedFiles(cwd = process.cwd()) { const output = execFileSync("git", ["ls-files", "-z"], { cwd, @@ -38,6 +45,9 @@ export function listTrackedFiles(cwd = process.cwd()) { .map((relativePath) => path.join(cwd, relativePath)); } +/** + * Scans files for merge conflict markers, skipping binary content. + */ export function findConflictMarkersInFiles(filePaths, readFile = fs.readFileSync) { const violations = []; for (const filePath of filePaths) { @@ -64,6 +74,9 @@ export function findConflictMarkersInFiles(filePaths, readFile = fs.readFileSync return violations; } +/** + * Uses git grep to list tracked files that may contain conflict markers. + */ export function listTrackedFilesWithConflictMarkerCandidates(cwd = process.cwd(), run = spawnSync) { const result = run( "git", @@ -87,10 +100,16 @@ export function listTrackedFilesWithConflictMarkerCandidates(cwd = process.cwd() .map((relativePath) => path.join(cwd, relativePath)); } +/** + * Finds merge conflict markers in tracked repository files. + */ export function findConflictMarkersInTrackedFiles(cwd = process.cwd()) { return findConflictMarkersInFiles(listTrackedFilesWithConflictMarkerCandidates(cwd)); } +/** + * Runs the merge conflict marker check. + */ export async function main() { const cwd = process.cwd(); const violations = findConflictMarkersInTrackedFiles(cwd); diff --git a/scripts/check-no-pairing-store-group-auth.mjs b/scripts/check-no-pairing-store-group-auth.mjs index 3a4f925d64b1..fc5e2d4932c7 100644 --- a/scripts/check-no-pairing-store-group-auth.mjs +++ b/scripts/check-no-pairing-store-group-auth.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Prevents direct pairing-store group auth reads outside resolver helpers. import ts from "typescript"; import { createPairingGuardContext } from "./lib/pairing-guard-context.mjs"; import { diff --git a/scripts/check-no-random-messaging-tmp.mjs b/scripts/check-no-random-messaging-tmp.mjs index acabf81ec448..040be5e2c5aa 100644 --- a/scripts/check-no-random-messaging-tmp.mjs +++ b/scripts/check-no-random-messaging-tmp.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Blocks host-random tmpdir usage in messaging/channel runtime sources. import ts from "typescript"; import { bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; @@ -9,6 +10,9 @@ import { unwrapExpression, } from "./lib/ts-guard-utils.mjs"; +/** + * Source roots scanned for unsafe messaging tmpdir usage. + */ export const messagingTmpdirGuardSourceRoots = [ "src/channels", "src/infra/outbound", @@ -53,6 +57,9 @@ function collectOsTmpdirImports(sourceFile) { return { osNamespaceOrDefault, namedTmpdir }; } +/** + * Finds `os.tmpdir()` or imported `tmpdir()` call lines in source. + */ export function findMessagingTmpdirCallLines(content, fileName = "source.ts") { const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); const { osNamespaceOrDefault, namedTmpdir } = collectOsTmpdirImports(sourceFile); @@ -70,6 +77,9 @@ export function findMessagingTmpdirCallLines(content, fileName = "source.ts") { }); } +/** + * Runs the messaging tmpdir guard. + */ export async function main() { await runCallsiteGuard({ importMetaUrl: import.meta.url, diff --git a/scripts/check-no-raw-channel-fetch.mjs b/scripts/check-no-raw-channel-fetch.mjs index 1127174b597c..d88913c0a41c 100644 --- a/scripts/check-no-raw-channel-fetch.mjs +++ b/scripts/check-no-raw-channel-fetch.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Blocks new raw fetch callsites in channel and plugin runtime sources. import ts from "typescript"; import { bundledPluginCallsite } from "./lib/bundled-plugin-paths.mjs"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; @@ -79,6 +80,9 @@ function isRawFetchCall(expression) { return false; } +/** + * Finds raw `fetch(...)` and `globalThis.fetch(...)` call lines. + */ export function findRawFetchCallLines(content, fileName = "source.ts") { const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); return collectCallExpressionLines(ts, sourceFile, (node) => @@ -86,6 +90,9 @@ export function findRawFetchCallLines(content, fileName = "source.ts") { ); } +/** + * Runs the raw channel/plugin fetch guard. + */ export async function main() { await runCallsiteGuard({ importMetaUrl: import.meta.url, diff --git a/scripts/check-no-raw-http2-imports.mjs b/scripts/check-no-raw-http2-imports.mjs index 794406118346..347a74e0b46e 100644 --- a/scripts/check-no-raw-http2-imports.mjs +++ b/scripts/check-no-raw-http2-imports.mjs @@ -1,3 +1,4 @@ +// Rejects raw Node http2 imports in source and extension code. import fs from "node:fs"; import path from "node:path"; const SOURCE_ROOTS = ["src", "extensions"]; diff --git a/scripts/check-no-raw-window-open.mjs b/scripts/check-no-raw-window-open.mjs index 5ac43cf24abc..a78b686138d0 100644 --- a/scripts/check-no-raw-window-open.mjs +++ b/scripts/check-no-raw-window-open.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Ensures UI code opens external URLs through the safe helper. import { promises as fs } from "node:fs"; import path from "node:path"; import ts from "typescript"; @@ -37,6 +38,9 @@ function isRawWindowOpenCall(expression) { ); } +/** + * Finds raw `window.open(...)` or `globalThis.open(...)` call lines. + */ export function findRawWindowOpenLines(content, fileName = "source.ts") { const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); const lines = []; @@ -52,6 +56,9 @@ export function findRawWindowOpenLines(content, fileName = "source.ts") { return lines; } +/** + * Runs the raw window.open guard. + */ export async function main() { const files = await collectTypeScriptFiles(uiSourceDir, { extraTestSuffixes: [".browser.test.ts", ".node.test.ts"], diff --git a/scripts/check-no-register-http-handler.mjs b/scripts/check-no-register-http-handler.mjs index ed949b7328ed..9b602eb5e935 100644 --- a/scripts/check-no-register-http-handler.mjs +++ b/scripts/check-no-register-http-handler.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Blocks deprecated plugin `registerHttpHandler` callsites. import ts from "typescript"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; import { @@ -15,6 +16,9 @@ function isDeprecatedRegisterHttpHandlerCall(expression) { return ts.isPropertyAccessExpression(callee) && callee.name.text === "registerHttpHandler"; } +/** + * Finds deprecated `registerHttpHandler(...)` call lines. + */ export function findDeprecatedRegisterHttpHandlerLines(content, fileName = "source.ts") { const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); return collectCallExpressionLines(ts, sourceFile, (node) => @@ -22,6 +26,9 @@ export function findDeprecatedRegisterHttpHandlerLines(content, fileName = "sour ); } +/** + * Runs the deprecated HTTP handler API guard. + */ export async function main() { await runCallsiteGuard({ importMetaUrl: import.meta.url,