diff --git a/scripts/check-web-fetch-provider-boundaries.mjs b/scripts/check-web-fetch-provider-boundaries.mjs index febb8c14e161..e1f5e6eaa01f 100644 --- a/scripts/check-web-fetch-provider-boundaries.mjs +++ b/scripts/check-web-fetch-provider-boundaries.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Checks core web-fetch surfaces for provider-owned Firecrawl coupling. import path from "node:path"; import { fileURLToPath } from "node:url"; import { collectSourceFileContents } from "./lib/source-file-scan-cache.mjs"; @@ -34,6 +35,9 @@ const suspiciousPatterns = [ let webFetchProviderViolationsPromise; +/** + * Collects web-fetch provider boundary violations in core source files. + */ export async function collectWebFetchProviderBoundaryViolations() { if (!webFetchProviderViolationsPromise) { webFetchProviderViolationsPromise = (async () => { @@ -81,6 +85,9 @@ export async function collectWebFetchProviderBoundaryViolations() { return await webFetchProviderViolationsPromise; } +/** + * Runs the web-fetch provider boundary check. + */ export async function main(argv, io) { const args = argv ?? process.argv.slice(2); const json = args.includes("--json"); diff --git a/scripts/check-web-search-provider-boundaries.mjs b/scripts/check-web-search-provider-boundaries.mjs index b89cb2a0c9b5..4dca8b1fb663 100644 --- a/scripts/check-web-search-provider-boundaries.mjs +++ b/scripts/check-web-search-provider-boundaries.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Inventories core web-search surfaces that still mention bundled providers. import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -153,6 +154,9 @@ function scanGenericCoreImports(lines, relativeFile, inventory) { } } +/** + * Collects web-search provider boundary inventory from core source files. + */ export async function collectWebSearchProviderBoundaryInventory() { if (!webSearchProviderInventoryPromise) { webSearchProviderInventoryPromise = (async () => { @@ -184,6 +188,9 @@ export async function collectWebSearchProviderBoundaryInventory() { return await webSearchProviderInventoryPromise; } +/** + * Reads the expected web-search provider boundary inventory baseline. + */ export async function readExpectedInventory() { try { return JSON.parse(await fs.readFile(baselinePath, "utf8")); @@ -195,6 +202,9 @@ export async function readExpectedInventory() { } } +/** + * Diffs expected and actual web-search provider boundary inventory entries. + */ export function diffInventory(expected, actual) { return diffInventoryEntries(expected, actual, compareInventoryEntries); } @@ -219,6 +229,9 @@ function formatEntry(entry) { return `${entry.provider} ${entry.file}:${entry.line} ${entry.reason}`; } +/** + * Runs the web-search provider boundary baseline check. + */ export async function runWebSearchProviderBoundaryCheck(argv, io) { return await runBaselineInventoryCheck({ argv: argv ?? process.argv.slice(2), @@ -231,6 +244,9 @@ export async function runWebSearchProviderBoundaryCheck(argv, io) { }); } +/** + * Entrypoint wrapper for the web-search provider boundary check. + */ export async function main(argv, io) { const exitCode = await runWebSearchProviderBoundaryCheck(argv, io); if (!io && exitCode !== 0) { diff --git a/scripts/check-webhook-auth-body-order.mjs b/scripts/check-webhook-auth-body-order.mjs index cbf9eacdbdad..a4aab88b8147 100644 --- a/scripts/check-webhook-auth-body-order.mjs +++ b/scripts/check-webhook-auth-body-order.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Ensures webhook handlers authenticate before reading request bodies. import path from "node:path"; import ts from "typescript"; import { bundledPluginCallsite, bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; @@ -29,6 +30,9 @@ function getCalleeName(expression) { return null; } +/** + * Finds request body reads that occur before webhook auth validation. + */ export function findBlockedWebhookBodyReadLines(content, fileName = "source.ts") { const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true); const lines = []; @@ -45,6 +49,9 @@ export function findBlockedWebhookBodyReadLines(content, fileName = "source.ts") return lines; } +/** + * Runs the webhook auth/body-order guard. + */ export async function main() { await runCallsiteGuard({ importMetaUrl: import.meta.url, diff --git a/scripts/check.mjs b/scripts/check.mjs index 6b88c07ea30b..96ca9170abcc 100644 --- a/scripts/check.mjs +++ b/scripts/check.mjs @@ -1,7 +1,11 @@ +// Runs the repository check lanes selected by CLI arguments. import { performance } from "node:perf_hooks"; import { printTimingSummary } from "./lib/check-timing-summary.mjs"; import { runManagedCommand } from "./lib/managed-child-process.mjs"; +/** + * Returns command usage text for the aggregate check runner. + */ export function usage() { return [ "Usage: node scripts/check.mjs [--timed] [--include-architecture] [--include-test-types]", @@ -16,6 +20,9 @@ export function usage() { ].join("\n"); } +/** + * Parses aggregate check runner arguments. + */ export function parseCheckArgs(argv) { const args = { help: false, @@ -39,6 +46,9 @@ export function parseCheckArgs(argv) { return args; } +/** + * Runs selected repository check lanes. + */ export async function main(argv = process.argv.slice(2)) { let args; try { @@ -158,6 +168,9 @@ async function runSerial(commands) { return results; } +/** + * Runs one managed check command and returns timing/status details. + */ export async function runCommand(command, runManagedCommandImpl = runManagedCommand) { const startedAt = performance.now(); let status = 1; diff --git a/scripts/ci-changed-scope.mjs b/scripts/ci-changed-scope.mjs index 614dc281e76b..14ea864adffe 100644 --- a/scripts/ci-changed-scope.mjs +++ b/scripts/ci-changed-scope.mjs @@ -1,3 +1,4 @@ +// Determines CI scope from changed paths. import { execFileSync } from "node:child_process"; import { appendFileSync } from "node:fs"; import { isDirectRunUrl } from "./lib/direct-run.mjs"; @@ -64,6 +65,9 @@ const NODE_FAST_SCOPE_RE = new RegExp( * @param {string[]} changedPaths * @returns {ChangedScope} */ +/** + * Detects high-level CI scope from changed file paths. + */ export function detectChangedScope(changedPaths) { if (!Array.isArray(changedPaths) || changedPaths.length === 0) { return { @@ -158,6 +162,9 @@ export function detectChangedScope(changedPaths) { * @param {string[]} changedPaths * @returns {NodeFastScope} */ +/** + * Detects whether node-fast CI can cover the changed paths. + */ export function detectNodeFastScope(changedPaths) { if (!Array.isArray(changedPaths) || changedPaths.length === 0) { return { runFastOnly: false, runPluginContracts: false, runCiRouting: false }; @@ -207,6 +214,9 @@ function detectInstallSmokeScopeForPath(path) { * @param {string[]} changedPaths * @returns {InstallSmokeScope} */ +/** + * Detects whether install-smoke CI should run for changed paths. + */ export function detectInstallSmokeScope(changedPaths) { if (!Array.isArray(changedPaths) || changedPaths.length === 0) { return { runFastInstallSmoke: true, runFullInstallSmoke: true }; @@ -232,6 +242,9 @@ export function detectInstallSmokeScope(changedPaths) { * @param {string} [cwd] * @returns {string[]} */ +/** + * Lists changed paths for CI base/head inputs. + */ export function listChangedPaths( base, head = "HEAD", @@ -263,6 +276,9 @@ export function listChangedPaths( * @param {string} [outputPath] * @param {InstallSmokeScope} [installSmokeScope] */ +/** + * Writes CI scope decisions to GitHub Actions output. + */ export function writeGitHubOutput( scope, outputPath = process.env.GITHUB_OUTPUT, diff --git a/scripts/ci-run-timings.mjs b/scripts/ci-run-timings.mjs index 6b3c4c3554fe..10b9af304551 100644 --- a/scripts/ci-run-timings.mjs +++ b/scripts/ci-run-timings.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Summarizes GitHub Actions run/job timings for CI analysis. import { execFileSync } from "node:child_process"; import { parsePositiveInt } from "./lib/numeric-options.mjs"; @@ -46,6 +47,9 @@ function normalizeRunJob(job) { }; } +/** + * Flattens paginated GitHub run job responses. + */ export function collectRunJobsFromPages(pages) { return pages.flatMap((page) => (Array.isArray(page.jobs) ? page.jobs.map(normalizeRunJob) : [])); } @@ -118,6 +122,9 @@ function collectRunTimingContext(run) { return { created, jobs, updated }; } +/** + * Summarizes longest jobs and total timing for a workflow run. + */ export function summarizeRunTimings(run, limit = 15) { const { created, jobs, updated } = collectRunTimingContext(run); const byDuration = [...jobs] @@ -142,6 +149,9 @@ export function summarizeRunTimings(run, limit = 15) { }; } +/** + * Summarizes pnpm store warmup overlap near run start. + */ export function summarizePnpmStoreWarmupBarrier(run, windowSeconds = 5) { const { jobs } = collectRunTimingContext(run); const preflight = jobs.find((job) => job.name === "preflight"); @@ -182,6 +192,9 @@ export function summarizePnpmStoreWarmupBarrier(run, windowSeconds = 5) { }; } +/** + * Selects the latest main push CI run, optionally matching a head SHA. + */ export function selectLatestMainPushCiRun(runs, headSha = null) { const pushRuns = runs.filter((run) => run.event === "push"); if (headSha) { @@ -337,6 +350,9 @@ function printSection(title, jobs, metric) { } } +/** + * Parses CI run timing CLI arguments. + */ export function parseRunTimingArgs(args) { let explicitRunId; let limit = 15; diff --git a/scripts/close-duplicate-prs-after-merge.mjs b/scripts/close-duplicate-prs-after-merge.mjs index bab96a39605c..d4a8ac34aa51 100644 --- a/scripts/close-duplicate-prs-after-merge.mjs +++ b/scripts/close-duplicate-prs-after-merge.mjs @@ -1,3 +1,4 @@ +// Finds duplicate PRs after merge and closes overlapping candidates. import { execFileSync } from "node:child_process"; import { pathToFileURL } from "node:url"; @@ -10,6 +11,9 @@ Closes explicit duplicate PRs after a landed PR, after verifying the landed PR i each duplicate has either a shared referenced issue or overlapping changed hunks. Defaults to dry-run.`; } +/** + * Parses comma-separated PR numbers from CLI/env input. + */ export function parsePrNumberList(value) { return [ ...new Set( @@ -27,6 +31,9 @@ export function parsePrNumberList(value) { ]; } +/** + * Parses duplicate PR close workflow arguments. + */ export function parseArgs(argv, env = process.env) { const args = { apply: false, @@ -109,6 +116,9 @@ function intersectSets(left, right) { return [...left].filter((value) => right.has(value)); } +/** + * Parses changed hunk ranges from unified diff text. + */ export function parseUnifiedDiffRanges(diffText) { const ranges = new Map(); let currentPath = null; @@ -136,6 +146,9 @@ export function parseUnifiedDiffRanges(diffText) { return ranges; } +/** + * Reports whether two PR diffs touch overlapping hunks. + */ export function hasOverlappingHunks(leftRanges, rightRanges) { for (const [path, left] of leftRanges) { const right = rightRanges.get(path) ?? []; @@ -182,6 +195,9 @@ Evidence: ${formatEvidence(evidence)}. Closing #${candidate.number} as a duplicate.`; } +/** + * Builds the close/skip plan for duplicate PR candidates. + */ export function buildDuplicateClosePlan({ candidates, diffs, landed, repo }) { if (landed.state !== "MERGED" || !landed.mergedAt) { throw new Error(`#${landed.number} is not merged`); @@ -246,6 +262,9 @@ function loadDiff(repo, number, runGh) { return runGh(["pr", "diff", String(number), "--repo", repo, "--color=never"]); } +/** + * Applies labels/comments/closes for planned duplicate PR actions. + */ export function applyClosePlan({ labels = DEFAULT_LABELS, plan, repo, runGh }) { for (const item of plan) { if (item.action !== "close") { @@ -261,6 +280,9 @@ export function applyClosePlan({ labels = DEFAULT_LABELS, plan, repo, runGh }) { } } +/** + * Runs the duplicate PR close workflow. + */ export function runDuplicateCloseWorkflow(args, runGh = defaultRunGh) { const landed = loadPr(args.repo, args.landedPr, runGh); const candidates = args.duplicates.map((number) => loadPr(args.repo, number, runGh)); diff --git a/scripts/copy-bundled-plugin-metadata.mjs b/scripts/copy-bundled-plugin-metadata.mjs index 639822d359c1..c832079d07e6 100644 --- a/scripts/copy-bundled-plugin-metadata.mjs +++ b/scripts/copy-bundled-plugin-metadata.mjs @@ -1,3 +1,4 @@ +// Copies bundled plugin metadata into generated runtime locations. import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; @@ -30,6 +31,9 @@ function shouldCopyBundledPluginMetadata(id, env, buildablePluginDirs) { return env.OPENCLAW_BUILD_PRIVATE_QA === "1"; } +/** + * Rewrites package extension entries for bundled metadata output. + */ export function rewritePackageExtensions(entries) { if (!Array.isArray(entries)) { return undefined; @@ -234,6 +238,9 @@ function copyDeclaredPluginSkillPaths(params) { * env?: NodeJS.ProcessEnv; * }} [params] */ +/** + * Copies bundled plugin metadata and package extension files. + */ export function copyBundledPluginMetadata(params = {}) { const repoRoot = params.cwd ?? params.repoRoot ?? process.cwd(); const env = params.env ?? process.env; diff --git a/scripts/copy-plugin-sdk-root-alias.mjs b/scripts/copy-plugin-sdk-root-alias.mjs index 982a5fa9eeb9..3312dd26380a 100644 --- a/scripts/copy-plugin-sdk-root-alias.mjs +++ b/scripts/copy-plugin-sdk-root-alias.mjs @@ -1,8 +1,12 @@ +// Copies the CommonJS plugin SDK root alias into dist output. import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { writeTextFileIfChanged } from "./runtime-postbuild-shared.mjs"; +/** + * Copies the plugin SDK root alias source into the configured output path. + */ export function copyPluginSdkRootAlias(params = {}) { const cwd = params.cwd ?? process.cwd(); const source = resolve(cwd, "src/plugin-sdk/root-alias.cjs"); diff --git a/scripts/crabbox-wrapper.mjs b/scripts/crabbox-wrapper.mjs index f5908a881727..beb30a0ea58a 100755 --- a/scripts/crabbox-wrapper.mjs +++ b/scripts/crabbox-wrapper.mjs @@ -1,4 +1,5 @@ #!/usr/bin/env node +// Resolves and delegates to the repo-local or PATH crabbox binary. import { spawn, spawnSync } from "node:child_process"; import { accessSync, diff --git a/scripts/dependency-changes-report.mjs b/scripts/dependency-changes-report.mjs index d27f856dbd14..b814b1d2201c 100644 --- a/scripts/dependency-changes-report.mjs +++ b/scripts/dependency-changes-report.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Builds dependency change reports from lockfile and manifest diffs. import { execFileSync } from "node:child_process"; import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; @@ -41,6 +42,9 @@ function versionsFor(payload, packageName) { return new Set(payload[packageName] ?? []); } +/** + * Creates a structured dependency diff report from base/head payloads. + */ export function createDependencyChangesReport({ basePayload, headPayload, @@ -184,10 +188,16 @@ function readGitFile(ref, filePath, cwd) { }); } +/** + * Reports whether a path is a dependency-related file. + */ export function isDependencyFile(filePath) { return DEPENDENCY_FILE_PATTERNS.some((pattern) => pattern.test(filePath)); } +/** + * Returns git pathspecs used for dependency diff collection. + */ export function dependencyDiffPathspecs() { return [...DEPENDENCY_DIFF_PATHS]; } @@ -276,6 +286,9 @@ async function writeArtifact(filePath, content) { await writeFile(filePath, content, "utf8"); } +/** + * Generates and writes dependency change report artifacts. + */ export async function runDependencyChangesReport(options) { const headLockfileText = await readFile(path.join(options.rootDir, options.headLockfile), "utf8"); const baseLockfileText = options.baseRef @@ -293,6 +306,9 @@ export async function runDependencyChangesReport(options) { }); } +/** + * Runs the dependency changes report CLI. + */ export async function main(argv = process.argv.slice(2)) { const options = parseArgs(argv); const report = await runDependencyChangesReport(options); diff --git a/scripts/dependency-ownership-surface-report.mjs b/scripts/dependency-ownership-surface-report.mjs index 70aa7f09130f..15146995eb63 100644 --- a/scripts/dependency-ownership-surface-report.mjs +++ b/scripts/dependency-ownership-surface-report.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Reports dependency ownership, closure, and risk surface from lockfile data. import { execFileSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -36,6 +37,9 @@ function normalizeDependencies(record = {}) { return entries.toSorted((left, right) => left.name.localeCompare(right.name)); } +/** + * Extracts the package name from a pnpm lockfile package key. + */ export function packageNameFromLockKey(lockKey) { const peerSuffixIndex = lockKey.indexOf("("); const baseKey = peerSuffixIndex >= 0 ? lockKey.slice(0, peerSuffixIndex) : lockKey; @@ -143,6 +147,9 @@ function collectReportTarget({ repoRoot, packageJson, ownershipPath }) { }; } +/** + * Collects dependency ownership and transitive surface metadata. + */ export function collectDependencyOwnershipSurfaceReport(params = {}) { const repoRoot = path.resolve(params.repoRoot ?? process.cwd()); const packageJson = readJson(path.join(repoRoot, "package.json")); @@ -262,6 +269,9 @@ export function collectDependencyOwnershipSurfaceReport(params = {}) { }; } +/** + * Collects policy errors from a dependency ownership surface report. + */ export function collectDependencyOwnershipSurfaceCheckErrors(report) { return report.ownershipGaps.map( (name) => `root dependency '${name}' is missing from ${DEFAULT_OWNERSHIP_PATH}`, @@ -289,6 +299,9 @@ function pluralize(count, singular, plural = `${singular}s`) { return `${count} ${count === 1 ? singular : plural}`; } +/** + * Renders a dependency ownership surface report as Markdown. + */ export function renderDependencyOwnershipSurfaceMarkdownReport(report) { const lines = [ "# Dependency Ownership and Install Surface Report", diff --git a/scripts/dependency-vulnerability-gate.mjs b/scripts/dependency-vulnerability-gate.mjs index f3eac8f1db7c..633eb7ba6d49 100644 --- a/scripts/dependency-vulnerability-gate.mjs +++ b/scripts/dependency-vulnerability-gate.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Checks resolved dependencies against npm advisory data and gate policy. import { readFile } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; @@ -124,6 +125,9 @@ function sortFindings(findings) { }); } +/** + * Classifies npm advisory findings into report-only findings and hard blockers. + */ export function classifyVulnerabilityFindings({ allAdvisories, productionAdvisories }) { const allFindings = flattenAdvisories(allAdvisories, "all"); const productionFindings = flattenAdvisories(productionAdvisories, "production"); @@ -150,6 +154,9 @@ function countPayloadVersions(payload) { return Object.values(payload).reduce((sum, versions) => sum + versions.length, 0); } +/** + * Runs the dependency vulnerability gate against pnpm-lock.yaml. + */ export async function runDependencyVulnerabilityGate({ rootDir = process.cwd(), fetchImpl = fetch, @@ -195,6 +202,9 @@ export async function runDependencyVulnerabilityGate({ }; } +/** + * Renders the dependency vulnerability gate report as Markdown. + */ export function renderDependencyVulnerabilityGateMarkdownReport(report) { const lines = [ "# npm Advisory Vulnerability Gate: Resolved Dependency Graph", diff --git a/scripts/docs-link-audit.mjs b/scripts/docs-link-audit.mjs index 03bd82879637..e57f46523d07 100644 --- a/scripts/docs-link-audit.mjs +++ b/scripts/docs-link-audit.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Audits docs links, routes, redirects, and Mintlify anchors. import { spawnSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; @@ -48,7 +49,11 @@ function normalizeSlashes(p) { return p.replace(/\\/g, "/"); } -/** @param {string} p */ +/** + * Normalizes a docs route by stripping query, hash, and edge slashes. + * + * @param {string} p + */ export function normalizeRoute(p) { const [withoutFragment] = p.split("#"); const [withoutQuery] = withoutFragment.split("?"); @@ -276,6 +281,8 @@ export function sanitizeDocsConfigForEnglishOnly(value) { } /** + * Prepares a docs directory, mirroring ClawHub docs when available. + * * @param {string} [sourceDir] * @param {{ * resolveClawHubRepoPathImpl?: typeof resolveClawHubRepoPath; @@ -310,6 +317,9 @@ export function prepareMirroredDocsDir(sourceDir = DOCS_DIR, options = {}) { } } +/** + * Creates an English-only temporary docs tree for Mintlify anchor checks. + */ export function prepareAnchorAuditDocsDir(sourceDir = DOCS_DIR) { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-docs-anchor-audit-")); try { @@ -406,6 +416,9 @@ export function resolveMintlifyAnchorAuditInvocation(params) { return createMintlifyPnpmRunnerSpawnSpec(params); } +/** + * Audits local docs links against route, file, and redirect indexes. + */ export function auditDocsLinks(options = {}) { const docsDir = options.docsDir ?? DOCS_DIR; const index = buildAuditIndex(docsDir, { @@ -537,6 +550,8 @@ export function auditDocsLinks(options = {}) { } /** + * Runs the docs link audit CLI. + * * @param {{ * args?: string[]; * comSpec?: string; diff --git a/scripts/docs-list.js b/scripts/docs-list.js index f6f58cad593f..c9e87cc1b7d3 100755 --- a/scripts/docs-list.js +++ b/scripts/docs-list.js @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Lists source docs pages and routes for docs-aware tooling. import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { join, relative } from "node:path"; diff --git a/scripts/docs-sync-publish.mjs b/scripts/docs-sync-publish.mjs index e1bf3db1193c..e605c34c96c4 100644 --- a/scripts/docs-sync-publish.mjs +++ b/scripts/docs-sync-publish.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Syncs source docs into the generated publish tree. import { execFileSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -298,6 +299,9 @@ function getGitHeadSha(repoPath) { } } +/** + * Resolves the local ClawHub repository path used for docs mirroring. + */ export function resolveClawHubRepoPath(value = "", options = {}) { const required = options.required !== false; const candidates = [ @@ -579,6 +583,9 @@ function rewriteClawHubMarkdownLinks(raw, relativeSourcePath, source) { }); } +/** + * Mirrors ClawHub docs into the target docs tree. + */ export function syncClawHubDocsTree(targetDocsDir, options = {}) { const repoPath = resolveClawHubRepoPath(options.repoPath || "", { required: options.required !== false, diff --git a/scripts/ensure-cli-startup-build.mjs b/scripts/ensure-cli-startup-build.mjs index c976cfc1dc98..b8d32943faed 100644 --- a/scripts/ensure-cli-startup-build.mjs +++ b/scripts/ensure-cli-startup-build.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Ensures CLI startup benchmark assets are built before checks. import { spawnSync } from "node:child_process"; import { existsSync } from "node:fs"; import path from "node:path"; @@ -25,10 +26,16 @@ function positiveEnvInt(name, env, fallback) { return value; } +/** + * Resolves the CLI startup build timeout from environment. + */ export function resolveCliStartupBuildTimeoutMs(env = process.env) { return positiveEnvInt("OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS", env, DEFAULT_BUILD_TIMEOUT_MS); } +/** + * Reports whether required CLI startup build outputs exist. + */ export function hasCliStartupBuild(params = {}) { const rootDir = params.rootDir ?? repoRoot; const exists = params.existsSync ?? existsSync; @@ -36,6 +43,9 @@ export function hasCliStartupBuild(params = {}) { return hasEntry && exists(path.join(rootDir, startupMetadataPath)); } +/** + * Builds CLI startup assets when required outputs are missing. + */ export function ensureCliStartupBuild(params = {}) { const rootDir = params.rootDir ?? repoRoot; if (hasCliStartupBuild({ rootDir, existsSync: params.existsSync })) { diff --git a/scripts/ensure-extension-memory-build.mjs b/scripts/ensure-extension-memory-build.mjs index c985bbeb5781..da0e3dafbef6 100644 --- a/scripts/ensure-extension-memory-build.mjs +++ b/scripts/ensure-extension-memory-build.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Ensures memory extension runtime entries are built before checks. import { spawnSync } from "node:child_process"; import { existsSync, readdirSync } from "node:fs"; import path from "node:path"; @@ -27,6 +28,9 @@ function positiveEnvInt(name, env, fallback) { return value; } +/** + * Resolves the extension memory build timeout from environment. + */ export function resolveExtensionMemoryBuildTimeoutMs(env = process.env) { return positiveEnvInt( "OPENCLAW_EXTENSION_MEMORY_BUILD_TIMEOUT_MS", @@ -52,6 +56,9 @@ function collectExpectedExtensionMemoryEntryIds(rootDir, env) { } } +/** + * Reports whether built memory extension entries exist. + */ export function hasBuiltExtensionMemoryEntries(params = {}) { const rootDir = params.rootDir ?? repoRoot; const exists = params.existsSync ?? existsSync; @@ -80,6 +87,9 @@ export function hasBuiltExtensionMemoryEntries(params = {}) { ); } +/** + * Builds memory extension entries when required outputs are missing. + */ export function ensureExtensionMemoryBuild(params = {}) { const rootDir = params.rootDir ?? repoRoot; if ( diff --git a/scripts/ensure-playwright-chromium.mjs b/scripts/ensure-playwright-chromium.mjs index 0490698adcd0..5e158d3394bf 100644 --- a/scripts/ensure-playwright-chromium.mjs +++ b/scripts/ensure-playwright-chromium.mjs @@ -1,4 +1,5 @@ #!/usr/bin/env node +// Ensures Playwright Chromium is installed or a usable system browser is available. import { spawnSync as spawnSyncImpl } from "node:child_process"; import { existsSync as existsSyncImpl, realpathSync } from "node:fs"; import { dirname, resolve } from "node:path"; @@ -18,6 +19,9 @@ const playwrightInstallWithDepsArgs = [ "chromium", ]; const executableOverrideEnvKey = "PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH"; +/** + * System Chromium executable paths used before downloading Playwright browsers. + */ export const systemChromiumExecutableCandidates = [ "/snap/bin/chromium", "/usr/bin/chromium-browser", @@ -26,6 +30,9 @@ export const systemChromiumExecutableCandidates = [ "/usr/bin/google-chrome-stable", ]; +/** + * Checks whether a Chromium executable can start enough to print its version. + */ export function canRunChromiumExecutable(executablePath, spawnSync = spawnSyncImpl) { const result = spawnSync(executablePath, ["--version"], { stdio: "ignore", @@ -33,6 +40,9 @@ export function canRunChromiumExecutable(executablePath, spawnSync = spawnSyncIm return result.status === 0; } +/** + * Resolves the first runnable system Chromium executable path. + */ export function resolveSystemChromiumExecutablePath( existsSync = existsSyncImpl, spawnSync = spawnSyncImpl, @@ -44,6 +54,9 @@ export function resolveSystemChromiumExecutablePath( ); } +/** + * Builds the pnpm runner invocation for Playwright browser install. + */ export function resolvePlaywrightInstallRunner(options = {}) { const env = options.env ?? process.env; return resolvePnpmRunner({ @@ -59,6 +72,9 @@ function isTruthyEnvFlag(value) { return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on"; } +/** + * Reports whether Linux system dependencies should be installed with Chromium. + */ export function shouldInstallPlaywrightSystemDependencies(options = {}) { const env = options.env ?? process.env; const platform = options.platform ?? process.platform; @@ -76,6 +92,9 @@ export function shouldInstallPlaywrightSystemDependencies(options = {}) { ); } +/** + * Checks whether this module is the direct script entrypoint. + */ export function isDirectScriptExecution( argvEntry = process.argv[1], modulePath = fileURLToPath(import.meta.url), @@ -91,6 +110,9 @@ export function isDirectScriptExecution( } } +/** + * Ensures a runnable Chromium exists for Playwright-based UI tests. + */ export function ensurePlaywrightChromium(options = {}) { const env = options.env ?? process.env; const executableOverride = diff --git a/scripts/format-docs.mjs b/scripts/format-docs.mjs index 05fdbe9c5899..7b4ed5c0c7ce 100644 --- a/scripts/format-docs.mjs +++ b/scripts/format-docs.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +// Formats docs Markdown/MDX and repairs Mintlify accordion indentation. import { execFileSync, spawnSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os";