From 7c7057168389a656446c63174898550dda76cca1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 2 Aug 2026 14:08:09 -0700 Subject: [PATCH] chore(lint): clear 172 lint:all baseline violations in five batches (#118098) * chore(lint): clean Android-Linux app assets batch * chore(lint): clean setup-launcher-plugin batch * chore(lint): clean changelog-updater batch * chore(lint): clean QA-runtime-helper batch * chore(lint): clean script-tests batch * fix(mxc): await sandbox spawn before bridge selection * fix(test): make fake plutil metacharacter escaping survive the template hop --- .../scripts/verify-release-notes.mjs | 25 +-- .../scripts/update-main.mjs | 143 ++++++++++-------- .../setup-node-env/dependency-fingerprint.mjs | 6 +- .../setup-node-env/verify-importers.mjs | 20 ++- .../app/src/main/assets/katex/renderer.js | 10 +- .../scripts/build-release-artifacts.ts | 5 +- apps/linux/ui/quickchat.js | 84 +++++----- extensions/mxc/src/mxc-spawn-launcher.mjs | 47 +++--- .../mxc/test/mxc-spawn-launcher.test.ts | 48 ++++++ .../onepassword-secret-ref-resolver.js | 2 +- openclaw.mjs | 5 +- .../convex/payload_validation.ts | 2 +- test/clawrouter-managed-gateway.e2e.test.ts | 34 +++-- test/e2e/qa-lab/config/cli-channel-picker.ts | 10 +- .../media/hosted-media-provider-live.ts | 2 +- .../plugin-lifecycle-probe.e2e.test.ts | 2 +- .../runtime/docker-e2e-lane.fixture.test.ts | 2 +- .../qa-lab/runtime/gateway-smoke.e2e.test.ts | 11 +- test/e2e/qa-lab/runtime/media-talk-gateway.ts | 20 ++- .../runtime/openwebui-probe.e2e.test.ts | 58 +++---- .../qa-lab/runtime/qa-otel-smoke.e2e.test.ts | 3 +- test/e2e/qa-lab/runtime/voice-call-gateway.ts | 6 +- test/embedded-transcript-cursor.e2e.test.ts | 2 +- test/external-script-modules.d.ts | 8 - ...ateway-queued-session-rotation.e2e.test.ts | 2 +- test/helpers/process-wait.ts | 4 +- ...huggingface-local-app-openclaw.e2e.test.ts | 9 +- test/linux-quickchat-stream.test.ts | 2 +- test/mcp-show-redact.e2e.test.ts | 4 +- test/openai-onboarding.live.test.ts | 2 +- test/plugin-clawhub-release.test.ts | 4 +- test/plugin-cron-registry-owner.e2e.test.ts | 4 +- ...ndled-provider-auth-literal-parity.test.ts | 2 +- test/scripts/android-app-i18n.test.ts | 6 +- .../android-release-wrapper-args.test.ts | 8 +- test/scripts/bench-sqlite-reliability.test.ts | 4 +- .../check-changelog-attributions.test.ts | 2 +- .../check-protocol-event-coverage.test.ts | 11 +- test/scripts/ci-run-node-test-shard.test.ts | 4 +- test/scripts/ci-workflow-guards.test.ts | 12 +- test/scripts/codesign-mac-app.test.ts | 4 +- .../dependency-vulnerability-gate.test.ts | 5 +- test/scripts/docker-e2e-observability.test.ts | 2 +- test/scripts/e2e-helper-env-limits.test.ts | 6 +- .../find-reusable-release-validation.test.ts | 57 ++++--- test/scripts/format-docs.test.ts | 6 +- test/scripts/gateway-network-client.test.ts | 11 +- test/scripts/ios-node-e2e.test.ts | 15 +- .../ios-release-fastlane-gates.test.ts | 2 +- test/scripts/ios-release-wrapper-args.test.ts | 8 +- .../ios-validate-app-store-ipa.test.ts | 48 +++++- test/scripts/k8s-manifests.test.ts | 2 +- .../scripts/kova-report-publish-files.test.ts | 2 +- .../mantis-publish-pr-evidence.test.ts | 8 +- .../mantis-web-ui-chat-proof-workflow.test.ts | 7 +- test/scripts/mock-openai-config.test.ts | 4 +- test/scripts/openclaw-live-updater.test.ts | 2 +- ...nclaw-release-telegram-qa-workflow.test.ts | 2 +- .../scripts/periphery-scope-workflows.test.ts | 68 +++++---- test/scripts/pr-prepare-gates.test.ts | 4 +- .../pr-review-artifact-validation.test.ts | 2 +- test/scripts/pr-wrappers.test.ts | 4 +- ...tension-package-boundary-artifacts.test.ts | 3 +- .../release-candidate-checklist.test.ts | 14 +- test/scripts/release-no-push-workflow.test.ts | 4 +- ...release-telegram-candidate-archive.test.ts | 6 +- .../release-user-journey-assertions.test.ts | 4 +- test/scripts/sync-labels.test.ts | 3 +- test/scripts/test-group-report.test.ts | 4 +- test/scripts/test-install-sh-docker.test.ts | 6 +- .../transitive-manifest-risk-report.test.ts | 3 +- test/scripts/verify-pr-hosted-gates.test.ts | 4 +- .../write-cli-startup-metadata.test.ts | 4 +- 73 files changed, 597 insertions(+), 362 deletions(-) diff --git a/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs b/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs index 412b1e282b13..54edf7b9e2d2 100644 --- a/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs +++ b/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs @@ -44,7 +44,7 @@ const nonEditorialTypes = new Set([ "test", ]); const nonEditorialTitlePattern = - /(?:^|[\s:([{\-])(docs?|documentation|tests?|testing|qa|quality assurance|refactor(?:ing)?|ci|continuous integration|build|chore|style|lint|format)(?:$|[\s:)\]}\-])/i; + /(?:^|[\s:([{-])(docs?|documentation|tests?|testing|qa|quality assurance|refactor(?:ing)?|ci|continuous integration|build|chore|style|lint|format)(?:$|[\s:)\]}-])/i; const editorialTitlePattern = /^\s*(?:\[[^\]]+\]\s*)?(?:#\d+:\s*)?(?:add|allow|block|enable|expose|fail|fix|harden|honor|improve|keep|migrate|move|persist|polish|preserve|prevent|propagate|rate[- ]?limit|restore|revert|ship|support|treat|validate)\b|^\s*#\d+:/i; const genericDirectCommitTerms = new Set([ @@ -111,7 +111,7 @@ function parseArgs(argv) { mainRef: undefined, noGithubSnapshot: false, refreshGithubSnapshot: false, - seedRef: undefined, + seedRef: /** @type {string | undefined} */ (undefined), shippedRefs: [], writeLedger: false, }; @@ -233,7 +233,7 @@ function gitIsAncestor(base, target) { if (result.status === 1) { return false; } - fail( + return fail( `could not validate release range ancestry for ${base}..${target}: ${ result.stderr?.trim() || result.signal || result.status }`, @@ -252,7 +252,9 @@ function gitCommit(ref, required = false) { if (!required) { return undefined; } - fail(`could not resolve canonical main ref ${ref}: ${result.stderr?.trim() || result.status}`); + return fail( + `could not resolve canonical main ref ${ref}: ${result.stderr?.trim() || result.status}`, + ); } function fetchGithubApi(args) { @@ -942,8 +944,8 @@ function authorsMatch(left, right) { } function pathsOverlap(left, right) { - for (const path of left) { - if (right.has(path)) { + for (const filePath of left) { + if (right.has(filePath)) { return true; } } @@ -998,7 +1000,7 @@ function canonicalMainCommits(base, mainRef) { if (!mainRef) { return []; } - const mainCommit = gitCommit(mainRef, true); + const mainCommit = /** @type {string} */ (gitCommit(mainRef, true)); const mainBase = git(["merge-base", base, mainCommit]); const output = git([ "log", @@ -1103,8 +1105,8 @@ function sourceCommits(base, target, mainRef) { fail(`cyclic revert history at ${hash}`); } seen.add(hash); - const output = git(["show", "-s", "--format=%s%x1f%B", hash]); - const [subject, ...bodyParts] = output.split("\x1f"); + const commitOutput = git(["show", "-s", "--format=%s%x1f%B", hash]); + const [subject, ...bodyParts] = commitOutput.split("\x1f"); const body = bodyParts.join("\x1f"); const message = `${subject}\n${body}`; const revertedHash = standardRevertedHash(body); @@ -1345,11 +1347,11 @@ function sourceCommits(base, target, mainRef) { } } const revertedPullRequests = new Set(); - for (const pullRequests of resolveAssociatedPullRequests( + for (const revertedPullRequestNumbers of resolveAssociatedPullRequests( [...revertedCommitHashes], targetTimestamp, ).values()) { - for (const number of pullRequests) { + for (const number of revertedPullRequestNumbers) { revertedPullRequests.add(number); } } @@ -2544,7 +2546,6 @@ function main() { ]; const resolvedHandles = resolveGitHubHandles(contributorHandles); const relationships = contributionRelationships(source, nodes, resolvedHandles); - const unlinkedCommits = source.activeCommits.filter((commit) => commit.references.length === 0); const resolvedCommitAuthors = resolveDirectCommitAuthors(relationships.directCommits); relationships.directCommits = withDirectCommitAuthors( relationships.directCommits, diff --git a/.agents/skills/openclaw-live-updater/scripts/update-main.mjs b/.agents/skills/openclaw-live-updater/scripts/update-main.mjs index 12cb55a21d9a..3ea069affb1e 100644 --- a/.agents/skills/openclaw-live-updater/scripts/update-main.mjs +++ b/.agents/skills/openclaw-live-updater/scripts/update-main.mjs @@ -61,7 +61,7 @@ const DEPENDENCY_INPUT_RE = /^(?:\.npmrc$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|patches\/)|(?:^|\/)package\.json$/u; class UpdateInvariantError extends Error { - constructor(code, message, details = undefined) { + constructor(code, message, details) { super(message); this.name = "UpdateInvariantError"; this.code = code; @@ -69,6 +69,11 @@ class UpdateInvariantError extends Error { } } +/** Re-throw the original runtime value while exposing the Error contract to type-aware lint. */ +function throwPreservingValue(value) { + throw /** @type {Error} */ (value); +} + function git(checkout, args, options = {}) { return execFileSync("git", ["-C", checkout, ...args], { encoding: options.encoding ?? "utf8", @@ -149,7 +154,7 @@ export function classifyActions( ) { // CI skips generated protocol-only macOS jobs, but the live app embeds these Swift sources. const generatedMacProtocolChanged = changedPaths.some((changedPath) => - /^apps\/shared\/OpenClawKit\/Sources\/OpenClawProtocol\//u.test(changedPath), + changedPath.startsWith("apps/shared/OpenClawKit/Sources/OpenClawProtocol/"), ); const runMacos = changedPaths.length > 0 && @@ -246,7 +251,9 @@ function missingControlUiAssets(checkout) { if (!hasAssetPayload) { missing.push("assets/*"); } - return [...new Set(missing)].toSorted(); + return [...new Set(missing)].toSorted((left, right) => + left < right ? -1 : left > right ? 1 : 0, + ); } export function inspectBuildState(checkout, expectedSha) { @@ -867,7 +874,7 @@ function readManagedGatewayLaunchAgent(checkout) { if (plistResult.status !== 0) { throw new UpdateInvariantError( "gateway_launchagent_failed", - `could not read the managed Gateway LaunchAgent: ${String(plistResult.stderr).trim()}`, + `could not read the managed Gateway LaunchAgent: ${plistResult.stderr.trim()}`, ); } const plist = JSON.parse(plistResult.stdout); @@ -999,7 +1006,7 @@ function prepareLaunchAgentEntrypointReplacement(deployment, entrypoint, options if (plistResult.status !== 0) { throw new UpdateInvariantError( "gateway_repoint_failed", - `could not read the managed Gateway LaunchAgent: ${String(plistResult.stderr).trim()}`, + `could not read the managed Gateway LaunchAgent: ${plistResult.stderr.trim()}`, ); } const programArguments = replaceLaunchAgentProgramArgument( @@ -1131,10 +1138,9 @@ function verifyManagedGatewayRuntime(checkout, expectedSha) { ["print", `gui/${process.getuid()}/${deployment.label}`], { encoding: "utf8" }, ); - const pidMatch = - launchctl.status === 0 ? String(launchctl.stdout).match(/\bpid = (\d+)\b/u) : null; + const pidMatch = launchctl.status === 0 ? launchctl.stdout.match(/\bpid = (\d+)\b/u) : null; const pid = Number(pidMatch?.[1] ?? Number.NaN); - const loadedArguments = parseLaunchctlArguments(String(launchctl.stdout)); + const loadedArguments = parseLaunchctlArguments(launchctl.stdout); const loadedCommand = resolveManagedGatewayCommand( loadedArguments, process.env.HOME, @@ -1167,7 +1173,7 @@ function verifyManagedGatewayRuntime(checkout, expectedSha) { ["-nP", `-iTCP:${deployment.port}`, "-sTCP:LISTEN", "-t"], { encoding: "utf8" }, ); - const listenerPids = String(listeners.stdout).trim().split(/\s+/u).filter(Boolean).map(Number); + const listenerPids = listeners.stdout.trim().split(/\s+/u).filter(Boolean).map(Number); // The Gateway overwrites process.title, so ps cannot prove argv. The owned // LaunchAgent arguments plus its exact listener PID remain stable evidence. if (listeners.status !== 0 || !listenerPids.includes(pid)) { @@ -1481,7 +1487,7 @@ function proveMacLaunchdGatewayStopped(checkout) { encoding: "utf8", }); const listenerClosed = - listeners.status === 1 && !String(listeners.stdout).trim() && !String(listeners.stderr).trim(); + listeners.status === 1 && !listeners.stdout.trim() && !listeners.stderr.trim(); const details = { listenerClosed, processExited, serviceBootedOut }; if (!serviceBootedOut) { throw new UpdateInvariantError( @@ -1597,62 +1603,68 @@ function runBuildWithPreservedMacApp(runCommand, checkout, sleep = defaultSleep) `.openclaw-live-mac-${process.pid}-${randomUUID()}.app`, ); renameSync(appBundle, preservedBundle); + let buildFailed = false; + let buildError; try { runCommand("pnpm", ["build"], checkout); - } finally { - // A running app or external file coordinator can temporarily relocate and - // restore the exact bundle while the JS build runs. Allow that move to settle, but - // require the original inode so an unrelated replacement still fails closed. - for (let attempt = 0; attempt < 20; attempt += 1) { - if (existsSync(preservedBundle) || existsSync(appBundle)) { - break; - } - sleep(100); + } catch (error) { + buildFailed = true; + buildError = error; + } + // Restore outside `finally` so restoration failures retain precedence over build failures. + // Accept an external restore only when the original inode returns; replacements still fail closed. + for (let attempt = 0; attempt < 20; attempt += 1) { + if (existsSync(preservedBundle) || existsSync(appBundle)) { + break; } - const alreadyRestored = isOriginalMacBundle(appBundle, appStat); - if (!alreadyRestored && existsSync(appBundle)) { - throw new UpdateInvariantError( - "mac_bundle_restore_conflict", - `build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`, - ); - } - if (!alreadyRestored) { - mkdirSync(path.dirname(appBundle), { recursive: true }); - try { - renameSync(preservedBundle, appBundle); - } catch (error) { - if (!isOriginalMacBundle(appBundle, appStat)) { - if (existsSync(appBundle)) { - throw new UpdateInvariantError( - "mac_bundle_restore_conflict", - `build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`, - ); - } - if (existsSync(preservedBundle)) { - throw new UpdateInvariantError( - "mac_bundle_restore_failed", - `failed to restore Mac app bundle: ${String(error)}`, - ); - } + sleep(100); + } + const alreadyRestored = isOriginalMacBundle(appBundle, appStat); + if (!alreadyRestored && existsSync(appBundle)) { + throw new UpdateInvariantError( + "mac_bundle_restore_conflict", + `build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`, + ); + } + if (!alreadyRestored) { + mkdirSync(path.dirname(appBundle), { recursive: true }); + try { + renameSync(preservedBundle, appBundle); + } catch (error) { + if (!isOriginalMacBundle(appBundle, appStat)) { + if (existsSync(appBundle)) { throw new UpdateInvariantError( - "missing_preserved_mac_bundle", - `preserved Mac app bundle disappeared: ${preservedBundle}`, + "mac_bundle_restore_conflict", + `build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`, ); } + if (existsSync(preservedBundle)) { + throw new UpdateInvariantError( + "mac_bundle_restore_failed", + `failed to restore Mac app bundle: ${String(error)}`, + ); + } + throw new UpdateInvariantError( + "missing_preserved_mac_bundle", + `preserved Mac app bundle disappeared: ${preservedBundle}`, + ); } } - if (!isOriginalMacBundle(appBundle, appStat)) { - throw new UpdateInvariantError( - "missing_preserved_mac_bundle", - `original Mac app bundle was not restored to ${appBundle}`, - ); - } - if (existsSync(preservedBundle)) { - throw new UpdateInvariantError( - "mac_bundle_restore_conflict", - `original Mac app bundle exists at both ${appBundle} and ${preservedBundle}`, - ); - } + } + if (!isOriginalMacBundle(appBundle, appStat)) { + throw new UpdateInvariantError( + "missing_preserved_mac_bundle", + `original Mac app bundle was not restored to ${appBundle}`, + ); + } + if (existsSync(preservedBundle)) { + throw new UpdateInvariantError( + "mac_bundle_restore_conflict", + `original Mac app bundle exists at both ${appBundle} and ${preservedBundle}`, + ); + } + if (buildFailed) { + throwPreservingValue(buildError); } } @@ -1666,7 +1678,6 @@ function restartGateway( options = {}, ) { assertExactBuild(checkout, expectedSha); - const now = options.now ?? Date.now; if (!deployment) { runCommand("pnpm", ["openclaw", "gateway", "restart"], checkout); return { processStartedAt: null, restartStartedAtMs: startedAtMs }; @@ -1749,7 +1760,7 @@ function bootstrapManagedGateway(runCommand, checkout, deployment, options = {}) } environmentRestore.disarm(); if (restartError) { - throw restartError; + throwPreservingValue(restartError); } return { processStartedAt }; } @@ -1811,7 +1822,7 @@ function readLaunchdEnvironmentVariable(name) { } // launchd normalizes `setenv NAME ""` to the same absent manager state as // `unsetenv NAME`; both `getenv` and `print gui/$UID` omit the value. - const value = String(result.stdout).replace(/\r?\n$/u, ""); + const value = result.stdout.replace(/\r?\n$/u, ""); return value || null; } @@ -1821,7 +1832,7 @@ function waitForManagedGatewayProcess(deployment, sleep = defaultSleep) { Math.ceil(GATEWAY_PROCESS_START_TIMEOUT_MS / GATEWAY_PROCESS_START_RETRY_DELAY_MS) + 1; for (let attempt = 0; attempt < attempts; attempt += 1) { const result = spawnSync("/bin/launchctl", ["print", target], { encoding: "utf8" }); - if (result.status === 0 && /\bpid\s*=\s*\d+\b/iu.test(String(result.stdout))) { + if (result.status === 0 && /\bpid\s*=\s*\d+\b/iu.test(result.stdout)) { return; } if (attempt + 1 < attempts) { @@ -1849,7 +1860,7 @@ function waitForManagedGatewayReadiness( sleep = defaultSleep, ) { for (let attempt = 1; attempt <= GATEWAY_READINESS_ATTEMPTS; attempt += 1) { - if (probeMilestones(deployment)?.readyzReady === true) { + if (probeMilestones(deployment)?.readyzReady) { return; } if (attempt < GATEWAY_READINESS_ATTEMPTS) { @@ -1904,7 +1915,7 @@ function probeGatewayMilestones(deployment) { ["-nP", `-iTCP:${deployment.port}`, "-sTCP:LISTEN", "-t"], { encoding: "utf8" }, ); - const listenerReady = listeners.status === 0 && Boolean(String(listeners.stdout).trim()); + const listenerReady = listeners.status === 0 && Boolean(listeners.stdout.trim()); if (!listenerReady) { return { listenerReady: false, healthzReady: false, readyzReady: false }; } @@ -2342,7 +2353,7 @@ function verifyAndAuditGateway({ } const audit = auditGatewayLogs(checkout, sinceMs, deployment); if (verificationError) { - throw verificationError; + throwPreservingValue(verificationError); } return { audit, timing: gatewayTiming }; } @@ -2815,7 +2826,7 @@ export function maintainMain(options, dependencies = {}) { if (actions.macAppRebuild) { const pendingState = { ...queuedMacState, - attempts: Number(queuedMacState?.attempts ?? 0) + 1, + attempts: (queuedMacState?.attempts ?? 0) + 1, lastAttemptAt: new Date().toISOString(), }; writeMaintenanceState(statePath, pendingState); diff --git a/.github/actions/setup-node-env/dependency-fingerprint.mjs b/.github/actions/setup-node-env/dependency-fingerprint.mjs index a86b63e9cb25..b0ddd468beda 100644 --- a/.github/actions/setup-node-env/dependency-fingerprint.mjs +++ b/.github/actions/setup-node-env/dependency-fingerprint.mjs @@ -55,7 +55,7 @@ function canonicalize(value) { } return Object.fromEntries( Object.entries(value) - .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0)) + .toSorted(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0)) .map(([key, child]) => [key, canonicalize(child)]), ); } @@ -128,7 +128,7 @@ function trackedPackageManifests(workspace) { return result.stdout .split("\0") .filter((entry) => entry === "package.json" || entry.endsWith("/package.json")) - .sort(); + .toSorted(); } function computeDependencyFingerprint({ workspace, frozenLockfile }) { @@ -145,7 +145,7 @@ function computeDependencyFingerprint({ workspace, frozenLockfile }) { try { manifest = JSON.parse(source); } catch (error) { - throw new Error(`invalid JSON in ${relativePath}: ${error.message}`); + throw new Error(`invalid JSON in ${relativePath}: ${error.message}`, { cause: error }); } return { manifest, relativePath }; }); diff --git a/.github/actions/setup-node-env/verify-importers.mjs b/.github/actions/setup-node-env/verify-importers.mjs index 34903f8ab029..a74a4f77b641 100644 --- a/.github/actions/setup-node-env/verify-importers.mjs +++ b/.github/actions/setup-node-env/verify-importers.mjs @@ -91,7 +91,7 @@ function parseManifest(manifestPath) { try { return JSON.parse(readFileSync(manifestPath, "utf8")); } catch (error) { - throw new Error(`could not read ${manifestPath}: ${error.message}`); + throw new Error(`could not read ${manifestPath}: ${error.message}`, { cause: error }); } } @@ -201,9 +201,13 @@ function verifyImporters(workspace, manifestPath) { if (!expectedResolution) { continue; } - const manifestPath = findInstalledManifest({ dependencyName, projectPath, workspace }); + const resolvedManifestPath = findInstalledManifest({ + dependencyName, + projectPath, + workspace, + }); const importerDisplay = relativeDisplayPath(workspace, projectPath); - if (!manifestPath) { + if (!resolvedManifestPath) { if (field.optional) { continue; } @@ -214,7 +218,7 @@ function verifyImporters(workspace, manifestPath) { } checked += 1; const installedLocation = normalizeLocation( - relativeDisplayPath(workspace, path.dirname(manifestPath)), + relativeDisplayPath(workspace, path.dirname(resolvedManifestPath)), ); const expectedLocation = normalizeLocation( path.join( @@ -232,20 +236,22 @@ function verifyImporters(workspace, manifestPath) { // says this importer owns the lockfile snapshot in its local slot. if (exactImporterSlot && !installedSnapshotKeys?.has(expectedResolution.snapshotKey)) { const actualKeys = installedSnapshotKeys - ? [...installedSnapshotKeys].toSorted().join(", ") + ? [...installedSnapshotKeys] + .toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0)) + .join(", ") : ""; mismatches.push( `${importerDisplay}: ${dependencyName} expected pnpm snapshot ${expectedResolution.snapshotKey}, resolved ${actualKeys} from ${installedLocation}`, ); } - const actual = parseManifest(manifestPath); + const actual = parseManifest(resolvedManifestPath); if ( actual.name !== expectedResolution.packageName || actual.version !== expectedResolution.version ) { const resolvedFrom = relativeDisplayPath( workspace, - realpathSync(path.dirname(manifestPath)), + realpathSync(path.dirname(resolvedManifestPath)), ); mismatches.push( `${importerDisplay}: ${dependencyName} expected ${expectedResolution.packageName}@${expectedResolution.version}, resolved ${actual.name ?? ""}@${actual.version ?? ""} from ${resolvedFrom}`, diff --git a/apps/android/app/src/main/assets/katex/renderer.js b/apps/android/app/src/main/assets/katex/renderer.js index 46835d67a309..476942985838 100644 --- a/apps/android/app/src/main/assets/katex/renderer.js +++ b/apps/android/app/src/main/assets/katex/renderer.js @@ -16,17 +16,21 @@ window.renderMath = async (job) => { trust: false, }); await document.fonts.ready; - await new Promise((resolve) => setTimeout(resolve, 0)); + await new Promise((resolve) => { + setTimeout(resolve, 0); + }); const initialBounds = container.getBoundingClientRect(); const width = Math.ceil(Math.max(initialBounds.width, container.scrollWidth)); document.body.style.width = `${width}px`; - await new Promise((resolve) => setTimeout(resolve, 0)); + await new Promise((resolve) => { + setTimeout(resolve, 0); + }); const finalBounds = container.getBoundingClientRect(); const height = Math.ceil(Math.max(finalBounds.height, container.scrollHeight)); window.ChatMathBridge.postMessage( JSON.stringify({ id: job.id, widthCssPx: width, heightCssPx: height, success: true }), ); - } catch (_) { + } catch { window.ChatMathBridge.postMessage( JSON.stringify({ id: job.id, widthCssPx: 0, heightCssPx: 0, success: false }), ); diff --git a/apps/android/scripts/build-release-artifacts.ts b/apps/android/scripts/build-release-artifacts.ts index 3e8b6724f988..3ac57647f4f1 100644 --- a/apps/android/scripts/build-release-artifacts.ts +++ b/apps/android/scripts/build-release-artifacts.ts @@ -303,10 +303,9 @@ function resolveApkSignerFromSdk(sdkRoot: string | undefined): string | null { const candidates = readdirSync(buildToolsDir) .toSorted((left, right) => right.localeCompare(left)) - .map((version) => join(buildToolsDir, version, "apksigner")) - .filter((candidate) => existsSync(candidate)); + .map((version) => join(buildToolsDir, version, "apksigner")); - return candidates[0] ?? null; + return candidates.find((candidate) => existsSync(candidate)) ?? null; } function resolveApkSigner(): string { diff --git a/apps/linux/ui/quickchat.js b/apps/linux/ui/quickchat.js index 20f8d9790978..f184b6588304 100644 --- a/apps/linux/ui/quickchat.js +++ b/apps/linux/ui/quickchat.js @@ -169,7 +169,7 @@ function chatMessageWidgets(message) { suffix += 1; } emitted.add(key); - return key === widget.key ? widget : { ...widget, key }; + return key === widget.key ? widget : Object.assign({}, widget, { key }); }); } @@ -573,7 +573,7 @@ function scheduleWidgetSync() { generation, }), ) - .catch((error) => { + .catch(/** @param {unknown} error */ (error) => { sendError = friendlyError(error, "Could not render the widget."); renderStatus(); }); @@ -903,7 +903,7 @@ async function openNamedPopover(kind) { setPopoverVisibility(kind); if (kind === "agents") { const selectedIndex = agents.findIndex((agent) => agent.id === activeIdentity.id); - menuIndex = selectedIndex >= 0 ? selectedIndex : 0; + menuIndex = Math.max(selectedIndex, 0); renderAgentList(); elements.agentList.querySelectorAll(".agent-option")[menuIndex]?.focus(); } else { @@ -967,10 +967,18 @@ function acceleratorFromEvent(event) { return null; } const parts = []; - if (event.ctrlKey) parts.push("Ctrl"); - if (event.altKey) parts.push("Alt"); - if (event.shiftKey) parts.push("Shift"); - if (event.metaKey) parts.push("Super"); + if (event.ctrlKey) { + parts.push("Ctrl"); + } + if (event.altKey) { + parts.push("Alt"); + } + if (event.shiftKey) { + parts.push("Shift"); + } + if (event.metaKey) { + parts.push("Super"); + } if (parts.length === 0) { return null; } @@ -1005,34 +1013,36 @@ async function requestHide() { document.body.classList.remove("shown"); window.clearTimeout(hideTimer); hideTimer = window.setTimeout( - async () => { - try { - const hidden = await invoke("quickchat_hide", { - sessionId: rendererSessionId, - rendererEpoch, - generation: operationGeneration, - }); - if (visibilitySequence !== operationGeneration) { - return; + () => { + void (async () => { + try { + const hidden = await invoke("quickchat_hide", { + sessionId: rendererSessionId, + rendererEpoch, + generation: operationGeneration, + }); + if (visibilitySequence !== operationGeneration) { + return; + } + if (hidden !== true) { + document.body.classList.add("shown"); + return; + } + resetAccepted(); + clearReply(); + } catch (error) { + if (visibilitySequence === operationGeneration) { + sendError = friendlyError(error); + renderStatus(); + document.body.classList.add("shown"); + elements.input.focus(); + } + } finally { + if (visibilitySequence === operationGeneration) { + hiding = false; + } } - if (hidden !== true) { - document.body.classList.add("shown"); - return; - } - resetAccepted(); - clearReply(); - } catch (error) { - if (visibilitySequence === operationGeneration) { - sendError = friendlyError(error); - renderStatus(); - document.body.classList.add("shown"); - elements.input.focus(); - } - } finally { - if (visibilitySequence === operationGeneration) { - hiding = false; - } - } + })(); }, reducedMotion.matches ? 45 : 120, ); @@ -1045,13 +1055,13 @@ function reveal() { rendererEpoch, generation: operationGeneration, }) - .then((accepted) => { - if (accepted !== true && visibilitySequence === operationGeneration) { + .then((activated) => { + if (activated !== true && visibilitySequence === operationGeneration) { document.body.classList.remove("shown"); return; } if ( - accepted === true && + activated === true && visibilitySequence === operationGeneration && activeReply?.widgets.length ) { diff --git a/extensions/mxc/src/mxc-spawn-launcher.mjs b/extensions/mxc/src/mxc-spawn-launcher.mjs index 5cecd69752e7..b4b3650abd77 100644 --- a/extensions/mxc/src/mxc-spawn-launcher.mjs +++ b/extensions/mxc/src/mxc-spawn-launcher.mjs @@ -35,9 +35,9 @@ export function forwardSignals(spawned, options = {}) { if (exitTimer) { return; } - const setTimeoutFn = options.setTimeout ?? setTimeout; + const setTimeoutFn = options.setTimeout?.bind(undefined) ?? setTimeout; exitTimer = setTimeoutFn(() => { - const exit = options.exit ?? process.exit; + const exit = options.exit?.bind(undefined) ?? ((code) => process.exit(code)); exit(signalExitCode(signal)); }, exitGraceMs); exitTimer?.unref?.(); @@ -86,11 +86,37 @@ function bridgeChildProcess(child) { export function exitOnChildProcessClose(child, options = {}) { child.on("close", (exitCode, signal) => { - const exit = options.exit ?? process.exit; + const exit = options.exit?.bind(undefined) ?? ((code) => process.exit(code)); exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal)); }); } +function attachPtyProcess(spawned) { + bridgeStdio(spawned); + forwardSignals(spawned); + spawned.onExit(({ exitCode, signal }) => { + process.exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal)); + }); +} + +function attachChildProcess(spawned) { + bridgeChildProcess(spawned); + forwardSignals(spawned); + exitOnChildProcessClose(spawned); +} + +export async function launchSandbox(spawnSandboxFromConfig, config, options, bridges = {}) { + // Normalize sync and Promise-returning SDK implementations before selecting an I/O bridge. + const spawned = await spawnSandboxFromConfig(config, options ?? {}); + + if (typeof spawned.onData === "function") { + (bridges.pty ?? attachPtyProcess)(spawned); + return; + } + + (bridges.child ?? attachChildProcess)(spawned); +} + const SIGNAL_NUMBERS = new Map([ ["SIGHUP", 1], ["SIGINT", 2], @@ -123,20 +149,7 @@ export async function main() { try { const { config, options } = decodePayload(process.argv.slice(2)); const { spawnSandboxFromConfig } = await import("@microsoft/mxc-sdk"); - const spawned = await spawnSandboxFromConfig(config, options ?? {}); - - if (typeof spawned.onData === "function") { - bridgeStdio(spawned); - forwardSignals(spawned); - spawned.onExit(({ exitCode, signal }) => { - process.exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal)); - }); - return; - } - - bridgeChildProcess(spawned); - forwardSignals(spawned); - exitOnChildProcessClose(spawned); + await launchSandbox(spawnSandboxFromConfig, config, options); } catch (error) { process.stderr.write(`${formatErrorStack(error)}\n`); process.exit(127); diff --git a/extensions/mxc/test/mxc-spawn-launcher.test.ts b/extensions/mxc/test/mxc-spawn-launcher.test.ts index e6dbbd03b738..40a81b8ee8cb 100644 --- a/extensions/mxc/test/mxc-spawn-launcher.test.ts +++ b/extensions/mxc/test/mxc-spawn-launcher.test.ts @@ -28,6 +28,15 @@ const loadLauncher = () => setTimeout?: (callback: () => void, ms: number) => { unref?: () => void }; }, ) => void; + launchSandbox: ( + spawnSandboxFromConfig: (config: unknown, options: unknown) => unknown, + config: unknown, + options: unknown, + bridges?: { + child?: (spawned: unknown) => void; + pty?: (spawned: unknown) => void; + }, + ) => Promise; signalExitCode: (signal: number | string | undefined) => number; }; @@ -64,6 +73,45 @@ describe("mxc-spawn-launcher", () => { expect(signalExitCode(undefined)).toBe(1); }); + it("resolves a promised PTY before selecting its bridge", async () => { + const { launchSandbox } = loadLauncher(); + const pty = { onData: vi.fn() }; + const spawnResult = Promise.resolve(pty); + const ptyBridge = vi.fn(); + const childBridge = vi.fn(); + + await launchSandbox(() => spawnResult, { process: {} }, undefined, { + pty: ptyBridge, + child: childBridge, + }); + + expect(ptyBridge).toHaveBeenCalledWith(pty); + expect(ptyBridge).not.toHaveBeenCalledWith(spawnResult); + expect(childBridge).not.toHaveBeenCalled(); + }); + + it("resolves a promised child process before selecting its bridge", async () => { + const { launchSandbox } = loadLauncher(); + const child = { on: vi.fn(), stdout: undefined, stderr: undefined }; + const spawnResult = Promise.resolve(child); + const ptyBridge = vi.fn(); + const childBridge = vi.fn(); + + await launchSandbox( + () => spawnResult, + { process: {} }, + { usePty: false }, + { + pty: ptyBridge, + child: childBridge, + }, + ); + + expect(childBridge).toHaveBeenCalledWith(child); + expect(childBridge).not.toHaveBeenCalledWith(spawnResult); + expect(ptyBridge).not.toHaveBeenCalled(); + }); + it("forwards process termination signals to spawned sandbox children", () => { const { forwardSignals } = loadLauncher(); const listeners = new Map void>(); diff --git a/extensions/onepassword/onepassword-secret-ref-resolver.js b/extensions/onepassword/onepassword-secret-ref-resolver.js index 87acb30bde8d..96e26f947b94 100644 --- a/extensions/onepassword/onepassword-secret-ref-resolver.js +++ b/extensions/onepassword/onepassword-secret-ref-resolver.js @@ -18,7 +18,7 @@ function readStdin() { let input = ""; process.stdin.setEncoding("utf8"); process.stdin.on("data", (chunk) => { - input += chunk; + input += String(chunk); }); process.stdin.on("error", reject); process.stdin.on("end", () => resolve(input)); diff --git a/openclaw.mjs b/openclaw.mjs index 43a15d120d4b..017fee6c5167 100755 --- a/openclaw.mjs +++ b/openclaw.mjs @@ -51,7 +51,7 @@ const ensureSupportedRuntimeVersion = () => { if (process.versions.bun) { // Bun >=1.4 (Rust rewrite) ships node:sqlite; feature-probe instead of // rejecting Bun outright so capable Bun builds can run OpenClaw. - let hasNodeSqlite = false; + let hasNodeSqlite; try { hasNodeSqlite = Boolean(process.getBuiltinModule?.("node:sqlite")); } catch { @@ -463,8 +463,7 @@ const hasLauncherContainerTarget = (argv) => { return true; } const args = argv.slice(2); - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; + for (const arg of args) { if (!arg || arg === "--") { return false; } diff --git a/qa/convex-credential-broker/convex/payload_validation.ts b/qa/convex-credential-broker/convex/payload_validation.ts index be4e31c3bd17..346c98bc51ea 100644 --- a/qa/convex-credential-broker/convex/payload_validation.ts +++ b/qa/convex-credential-broker/convex/payload_validation.ts @@ -93,7 +93,7 @@ function requireBuzzPrivateKey( try { return { value, publicKey: getPublicKey(decodeBuzzPrivateKey(value)) }; } catch { - throwPayloadError( + return throwPayloadError( createFailure, `Credential payload for kind "buzz" must include "${key}" as an nsec or 64-character hex private key.`, ); diff --git a/test/clawrouter-managed-gateway.e2e.test.ts b/test/clawrouter-managed-gateway.e2e.test.ts index d3ecadb3d8a0..a7d24e741180 100644 --- a/test/clawrouter-managed-gateway.e2e.test.ts +++ b/test/clawrouter-managed-gateway.e2e.test.ts @@ -245,16 +245,20 @@ async function waitForGatewayReadiness( async function startFakeClawRouter(): Promise { const requests: CapturedRequest[] = []; const server = createServer((req, res) => { - void handleClawRouterRequest(req, res, requests).catch((error) => { + void handleClawRouterRequest(req, res, requests).catch((error: unknown) => { res.writeHead(500, { "content-type": "application/json" }); res.end(JSON.stringify({ error: { message: String(error) } })); }); }); - await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + await new Promise((resolve) => { + server.listen(0, "127.0.0.1", resolve); + }); const address = server.address(); if (!address || typeof address === "string") { server.closeAllConnections(); - await new Promise((resolve) => server.close(() => resolve())); + await new Promise((resolve) => { + server.close(() => resolve()); + }); throw new Error("fake ClawRouter did not bind a TCP port"); } return { @@ -262,7 +266,9 @@ async function startFakeClawRouter(): Promise { requests, close: async () => { server.closeAllConnections(); - await new Promise((resolve) => server.close(() => resolve())); + await new Promise((resolve) => { + server.close(() => resolve()); + }); }, }; } @@ -273,13 +279,19 @@ async function handleClawRouterRequest( requests: CapturedRequest[], ): Promise { const method = req.method ?? "GET"; - const path = new URL(req.url ?? "/", "http://127.0.0.1").pathname; + const requestPath = new URL(req.url ?? "/", "http://127.0.0.1").pathname; const bodyText = await readRequestBody(req); const body = bodyText ? (JSON.parse(bodyText) as Record) : undefined; const authorization = req.headers.authorization; - requests.push({ method, path, authorization, headers: { ...req.headers }, body }); + requests.push({ + method, + path: requestPath, + authorization, + headers: { ...req.headers }, + body, + }); - if (method === "GET" && path === "/v1/health") { + if (method === "GET" && requestPath === "/v1/health") { writeJson(res, 200, { ok: true, environment: "fakeco", @@ -296,7 +308,7 @@ async function handleClawRouterRequest( return; } - if (method === "GET" && path === "/v1/catalog") { + if (method === "GET" && requestPath === "/v1/catalog") { writeJson(res, 200, { providers: [ { @@ -318,7 +330,7 @@ async function handleClawRouterRequest( return; } - if (method === "GET" && path === "/v1/usage") { + if (method === "GET" && requestPath === "/v1/usage") { writeJson(res, 200, { budget: { configured: false, ledger: "unmetered" }, usage: { summary: { requestCount: 0, totalTokens: 0, actualCostMicros: 0 } }, @@ -326,12 +338,12 @@ async function handleClawRouterRequest( return; } - if (method === "POST" && path === "/v1/responses") { + if (method === "POST" && requestPath === "/v1/responses") { writeResponsesStream(res, resolveResponseText(body)); return; } - writeJson(res, 404, { error: { message: `unexpected ${method} ${path}` } }); + writeJson(res, 404, { error: { message: `unexpected ${method} ${requestPath}` } }); } function resolveResponseText(body: Record | undefined): string { diff --git a/test/e2e/qa-lab/config/cli-channel-picker.ts b/test/e2e/qa-lab/config/cli-channel-picker.ts index 8c40750203c5..e48f592cd8bf 100644 --- a/test/e2e/qa-lab/config/cli-channel-picker.ts +++ b/test/e2e/qa-lab/config/cli-channel-picker.ts @@ -166,7 +166,10 @@ async function runRealPicker(options: ProducerOptions, openclawHome: string) { await sendAndWait("\r", /Configure DM access policies now\?/u); await sendAndWait("\r", /Configuration updated\./u); - while (!exit) { + for (;;) { + if (exit) { + break; + } if (remainingMs() === 0) { throw new Error(`picker timed out after ${options.timeoutMs}ms`); } @@ -182,7 +185,10 @@ async function runRealPicker(options: ProducerOptions, openclawHome: string) { if (!exit) { child.kill("SIGTERM"); const cleanupDeadline = Date.now() + 5_000; - while (!exit && Date.now() < cleanupDeadline) { + while (Date.now() < cleanupDeadline) { + if (exit) { + break; + } await delay(25); } } diff --git a/test/e2e/qa-lab/media/hosted-media-provider-live.ts b/test/e2e/qa-lab/media/hosted-media-provider-live.ts index 6c3ad0bc25f2..2b4aaadfe328 100644 --- a/test/e2e/qa-lab/media/hosted-media-provider-live.ts +++ b/test/e2e/qa-lab/media/hosted-media-provider-live.ts @@ -406,7 +406,7 @@ async function selectProviders(params: { const candidates = explicit ? params.suite.providers : (params.suite.defaultProviders ?? params.suite.providers); - let providers = candidates.filter((provider) => (explicit ? explicit.has(provider) : true)); + const providers = candidates.filter((provider) => (explicit ? explicit.has(provider) : true)); if (!params.requireAuth) { return providers; } diff --git a/test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts b/test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts index d7f7e62c7d85..fd05cba70ba5 100644 --- a/test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts +++ b/test/e2e/qa-lab/plugins/plugin-lifecycle-probe.e2e.test.ts @@ -60,7 +60,7 @@ class FakeCommandChild extends EventEmitter { } } -afterEach(tempDirs.cleanup); +afterEach(() => tempDirs.cleanup()); describe("plugin lifecycle matrix probe", () => { it("accepts inspect JSON for an enabled loaded plugin", async () => { diff --git a/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts b/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts index 063b77c7a1ec..15722b2a9b74 100644 --- a/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts +++ b/test/e2e/qa-lab/runtime/docker-e2e-lane.fixture.test.ts @@ -21,7 +21,7 @@ describe("QA Docker E2E lane fixture", () => { "update-restart-auth", ]), ); - expect(listQaDockerE2eLaneNames()).toEqual([...listQaDockerE2eLaneNames()].sort()); + expect(listQaDockerE2eLaneNames()).toEqual([...listQaDockerE2eLaneNames()].toSorted()); }); it("parses help, list, and lane arguments", () => { diff --git a/test/e2e/qa-lab/runtime/gateway-smoke.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-smoke.e2e.test.ts index 3e33bcde34d1..d8bf21376f37 100644 --- a/test/e2e/qa-lab/runtime/gateway-smoke.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-smoke.e2e.test.ts @@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process"; import { createServer, type Server } from "node:http"; import { afterEach, describe, expect, it } from "vitest"; -import { WebSocket, WebSocketServer } from "ws"; +import { WebSocket, WebSocketServer, type RawData } from "ws"; import { runGatewaySmoke } from "../../../../scripts/dev/gateway-smoke.js"; let server: Server | undefined; @@ -73,8 +73,13 @@ describe("gateway-smoke", () => { server = createServer(); wss = new WebSocketServer({ server }); wss.on("connection", (ws: WebSocket) => { - ws.on("message", (data) => { - const frame = JSON.parse(data.toString()) as { + ws.on("message", (data: RawData) => { + const text = Array.isArray(data) + ? Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString("utf8") + : Buffer.isBuffer(data) + ? data.toString("utf8") + : Buffer.from(data).toString("utf8"); + const frame = JSON.parse(text) as { id: string; method: string; params?: unknown; diff --git a/test/e2e/qa-lab/runtime/media-talk-gateway.ts b/test/e2e/qa-lab/runtime/media-talk-gateway.ts index 329cb7649396..3085256c3636 100644 --- a/test/e2e/qa-lab/runtime/media-talk-gateway.ts +++ b/test/e2e/qa-lab/runtime/media-talk-gateway.ts @@ -280,7 +280,9 @@ async function waitForChatFinal( if (finalEvent) { return finalEvent.payload; } - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); } throw new Error(`timed out waiting for WebChat final event for run ${runId}`); } @@ -301,7 +303,9 @@ async function waitForWebchatAudio(params: { if (attachment) { return { attachment, history }; } - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); } return { attachment: undefined, history }; } @@ -434,7 +438,9 @@ async function waitForActiveTalkStatus(client: GatewayClient, sessionKey: string return status; } catch (error) { lastError = error; - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); } } throw lastError instanceof Error ? lastError : new Error("timed out waiting for active Talk run"); @@ -462,7 +468,9 @@ async function waitForQueuedTalkSteer(client: GatewayClient, sessionKey: string) } catch (error) { lastError = error; } - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); } if (lastError instanceof Error) { throw lastError; @@ -513,7 +521,7 @@ async function runActiveTalkAgentRunProof(options: ProducerOptions): Promise>("talk.client.create", { + const created = await client.request("talk.client.create", { sessionKey, provider: FIXTURE_REALTIME_PROVIDER_ID, }); @@ -643,7 +651,7 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { .then((exitCode) => { process.exitCode = exitCode; }) - .catch((error) => { + .catch((error: unknown) => { console.error(formatErrorMessage(error)); process.exitCode = 1; }); diff --git a/test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts b/test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts index ab36ddaf5714..16cb8787b58a 100644 --- a/test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/openwebui-probe.e2e.test.ts @@ -221,7 +221,7 @@ describe("scripts/e2e/openwebui-probe.mjs", () => { }); it("redacts admin credentials from sign-in error bodies", async () => { - const adminEmail = "openwebui-e2e" + "@example.com"; + const adminEmail = "openwebui-e2e@example.com"; const server = createServer((request, response) => { if (request.url === "/api/v1/auths/signin") { response.writeHead(401, { "content-type": "application/json" }); @@ -400,33 +400,35 @@ describe("scripts/e2e/openwebui-probe.mjs", () => { it("runs chat mode through Open WebUI chat completions and validates the nonce", async () => { const chatRequests: unknown[] = []; - const server = createServer(async (request, response) => { - if (request.url === "/api/v1/auths/signin") { - response.writeHead(200, { - "content-type": "application/json", - "set-cookie": "openwebui-session=test; Path=/", - }); - response.end(JSON.stringify({ token: "test-token" })); - return; - } - if (request.url === "/api/models") { - response.writeHead(200, { "content-type": "application/json" }); - response.end(JSON.stringify({ data: [{ id: "openclaw/default" }] })); - return; - } - if (request.url === "/api/chat/completions") { - expect(request.headers.authorization).toBe("Bearer test-token"); - expect(request.headers.cookie).toContain("openwebui-session=test"); - chatRequests.push(JSON.parse(await readRequestBody(request))); - response.writeHead(200, { "content-type": "application/json" }); - response.end( - JSON.stringify({ - choices: [{ message: { content: "OpenClaw replied with nonce-123" } }], - }), - ); - return; - } - response.writeHead(404).end(); + const server = createServer((request, response) => { + void (async () => { + if (request.url === "/api/v1/auths/signin") { + response.writeHead(200, { + "content-type": "application/json", + "set-cookie": "openwebui-session=test; Path=/", + }); + response.end(JSON.stringify({ token: "test-token" })); + return; + } + if (request.url === "/api/models") { + response.writeHead(200, { "content-type": "application/json" }); + response.end(JSON.stringify({ data: [{ id: "openclaw/default" }] })); + return; + } + if (request.url === "/api/chat/completions") { + expect(request.headers.authorization).toBe("Bearer test-token"); + expect(request.headers.cookie).toContain("openwebui-session=test"); + chatRequests.push(JSON.parse(await readRequestBody(request))); + response.writeHead(200, { "content-type": "application/json" }); + response.end( + JSON.stringify({ + choices: [{ message: { content: "OpenClaw replied with nonce-123" } }], + }), + ); + return; + } + response.writeHead(404).end(); + })(); }); const baseUrl = await listen(server); try { diff --git a/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts b/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts index 7504d752c55a..b1542d73e221 100644 --- a/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts @@ -569,7 +569,8 @@ describe("qa-otel-smoke receiver bounds", () => { stopDockerContainer, waitForLocalPort: async () => {}, writeFile: async (_path, config) => { - writtenConfig = String(config); + writtenConfig = + typeof config === "string" ? config : Buffer.from(config as Uint8Array).toString("utf8"); }, }); diff --git a/test/e2e/qa-lab/runtime/voice-call-gateway.ts b/test/e2e/qa-lab/runtime/voice-call-gateway.ts index 23be3ad8e8b9..fe5d86bdec5b 100644 --- a/test/e2e/qa-lab/runtime/voice-call-gateway.ts +++ b/test/e2e/qa-lab/runtime/voice-call-gateway.ts @@ -141,7 +141,9 @@ async function waitForFinalToolResult(filePath: string) { if (final) { return { entries, final }; } - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); } throw new Error("timed out waiting for final Voice Call consult tool result"); } @@ -359,7 +361,7 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { .then((exitCode) => { process.exitCode = exitCode; }) - .catch((error) => { + .catch((error: unknown) => { console.error(formatErrorMessage(error)); process.exitCode = 1; }); diff --git a/test/embedded-transcript-cursor.e2e.test.ts b/test/embedded-transcript-cursor.e2e.test.ts index f75e96a075b6..7baa66f91949 100644 --- a/test/embedded-transcript-cursor.e2e.test.ts +++ b/test/embedded-transcript-cursor.e2e.test.ts @@ -230,7 +230,7 @@ async function startMockModelServer(): Promise { await drainRequest(request); responseCount += 1; writeModelResponse(response, responseCount); - })().catch((error) => { + })().catch((error: unknown) => { response.writeHead(500, { "content-type": "application/json" }); response.end(JSON.stringify({ error: { message: String(error) } })); }); diff --git a/test/external-script-modules.d.ts b/test/external-script-modules.d.ts index a23451765d80..fe71800daa1c 100644 --- a/test/external-script-modules.d.ts +++ b/test/external-script-modules.d.ts @@ -85,11 +85,6 @@ declare module "*openclaw-changelog-update/scripts/verify-release-notes.mjs" { legacyIssues: Map; pullRequests: Map; }; - export function contributionRecordTarget(section: { source: string }): string | undefined; - export function pullRequestTitleFromCommitSubject( - subject: string, - number: number, - ): string | undefined; export function recoverUnavailablePullRequests(params: { numbers: Iterable; nodes: Map; @@ -161,9 +156,6 @@ declare module "*openclaw-changelog-update/scripts/verify-release-notes.mjs" { associatedPullRequests: number[], hasProvenanceOverride: boolean, ): number[]; - export function recoverUnavailablePullRequests( - params: Record, - ): Map>; export function validateReleaseProvenanceOverrides( provenanceOverrides: Map, nodes: Map, diff --git a/test/gateway-queued-session-rotation.e2e.test.ts b/test/gateway-queued-session-rotation.e2e.test.ts index ad7741d54617..6602e92bec7d 100644 --- a/test/gateway-queued-session-rotation.e2e.test.ts +++ b/test/gateway-queued-session-rotation.e2e.test.ts @@ -295,7 +295,7 @@ describe("Gateway queued session rotation", () => { expect(JSON.stringify(modelServer.requests[1]?.body)).toContain("OPENCLAW_E2E_AFTER_RESET"); } finally { await client.abortChat({ sessionKey }).catch(() => undefined); - client.stop(); + void client.stop(); modelServer.releaseHeldResponse(); } }, diff --git a/test/helpers/process-wait.ts b/test/helpers/process-wait.ts index 07c6ae1e9829..b74a74777057 100644 --- a/test/helpers/process-wait.ts +++ b/test/helpers/process-wait.ts @@ -11,7 +11,9 @@ export function isProcessAlive(pid: number): boolean { } async function sleep(ms: number): Promise { - await new Promise((resolve) => setTimeout(resolve, ms)); + await new Promise((resolve) => { + setTimeout(resolve, ms); + }); } export async function waitForFile(filePath: string, timeoutMs: number): Promise { diff --git a/test/huggingface-local-app-openclaw.e2e.test.ts b/test/huggingface-local-app-openclaw.e2e.test.ts index 12c50970c757..dfacb5df637d 100644 --- a/test/huggingface-local-app-openclaw.e2e.test.ts +++ b/test/huggingface-local-app-openclaw.e2e.test.ts @@ -165,13 +165,18 @@ async function startFakeOpenAiServer(params: { modelId: string }): Promise((resolve) => server.close(() => resolve())); + await new Promise((resolve) => { + server.close(() => resolve()); + }); throw new Error("fake OpenAI server did not bind a TCP port"); } return { baseUrl: `http://127.0.0.1:${(address as AddressInfo).port}/v1`, requests, - close: () => new Promise((resolve) => server.close(() => resolve())), + close: () => + new Promise((resolve) => { + server.close(() => resolve()); + }), }; } diff --git a/test/linux-quickchat-stream.test.ts b/test/linux-quickchat-stream.test.ts index 765b4e9441da..77be03482b30 100644 --- a/test/linux-quickchat-stream.test.ts +++ b/test/linux-quickchat-stream.test.ts @@ -900,7 +900,7 @@ test("adding a widget preserves the existing native webview identity", async () message: { role: "assistant", content: [widgetBlock("first"), widgetBlock("second")] }, }); await harness.flushWidgets(); - const layouts = harness.syncedWidgets().map((layout: object) => ({ ...layout })); + const layouts = harness.syncedWidgets().map((layout: object) => Object.assign({}, layout)); assert.deepEqual(layouts[0], firstLayout); assert.equal(layouts[0].visible, true); diff --git a/test/mcp-show-redact.e2e.test.ts b/test/mcp-show-redact.e2e.test.ts index fbe5445dbc95..760e99ca8d13 100644 --- a/test/mcp-show-redact.e2e.test.ts +++ b/test/mcp-show-redact.e2e.test.ts @@ -58,7 +58,9 @@ async function waitForChatFinal( return payload; } } - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); } throw new Error( `timed out waiting for chat final runId=${runId}; events=${JSON.stringify( diff --git a/test/openai-onboarding.live.test.ts b/test/openai-onboarding.live.test.ts index f5c23a95f2b0..904501787142 100644 --- a/test/openai-onboarding.live.test.ts +++ b/test/openai-onboarding.live.test.ts @@ -35,7 +35,7 @@ async function runOpenClaw(args: string[], env: NodeJS.ProcessEnv): Promise { activeRequests += 1; maxActiveRequests = Math.max(maxActiveRequests, activeRequests); try { - await new Promise((resolve) => setTimeout(resolve, 5)); + await new Promise((resolve) => { + setTimeout(resolve, 5); + }); return await baseFetch(...args); } finally { activeRequests -= 1; diff --git a/test/plugin-cron-registry-owner.e2e.test.ts b/test/plugin-cron-registry-owner.e2e.test.ts index 8621303d4335..ecd7f88b4e51 100644 --- a/test/plugin-cron-registry-owner.e2e.test.ts +++ b/test/plugin-cron-registry-owner.e2e.test.ts @@ -321,7 +321,7 @@ describe("plugin cron registry ownership e2e", () => { providers: { "cron-owner": { baseUrl: `${modelServer.baseUrl}/v1`, - ["api" + "Key"]: TEST_API_KEY, + apiKey: TEST_API_KEY, api: "openai-responses", request: { allowPrivateNetwork: true }, models: [ @@ -360,7 +360,7 @@ describe("plugin cron registry ownership e2e", () => { const client = await connectGatewayClient({ url: instance.url, - ["to" + "ken"]: instance.gatewayToken, + token: instance.gatewayToken, role: "operator", scopes: ["operator.admin", "operator.read", "operator.write"], requestTimeoutMs: 30_000, diff --git a/test/plugins/bundled-provider-auth-literal-parity.test.ts b/test/plugins/bundled-provider-auth-literal-parity.test.ts index 022f6af93c4f..4bc68041edbc 100644 --- a/test/plugins/bundled-provider-auth-literal-parity.test.ts +++ b/test/plugins/bundled-provider-auth-literal-parity.test.ts @@ -190,7 +190,7 @@ beforeAll(() => { status: "fulfilled", value, }), - (reason): PromiseRejectedResult => ({ status: "rejected", reason }), + (reason: unknown): PromiseRejectedResult => ({ status: "rejected", reason }), ), ); } diff --git a/test/scripts/android-app-i18n.test.ts b/test/scripts/android-app-i18n.test.ts index 4394e1ce7c06..3e21c3ccbe22 100644 --- a/test/scripts/android-app-i18n.test.ts +++ b/test/scripts/android-app-i18n.test.ts @@ -55,11 +55,11 @@ describe("Android app i18n resources", () => { ); const base = await readFile("apps/android/wear/src/main/res/values/strings.xml", "utf8"); const baseKeys = [...base.matchAll(/ match[1]) - .toSorted(); + .map((match) => match[1] as string) + .toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0)); const basePlaceholders = [...base.matchAll(/%\d+\$[a-z]/giu)] .map((match) => match[0]) - .toSorted(); + .toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0)); expect(wearResources).toHaveLength(NATIVE_I18N_LOCALES.length); for (const [, content] of wearResources) { diff --git a/test/scripts/android-release-wrapper-args.test.ts b/test/scripts/android-release-wrapper-args.test.ts index 71b111b69dec..53940ba5dda7 100644 --- a/test/scripts/android-release-wrapper-args.test.ts +++ b/test/scripts/android-release-wrapper-args.test.ts @@ -20,8 +20,12 @@ function runScript( return { ok: true, stdout, stderr: "" }; } catch (error) { const e = error as { stdout?: unknown; stderr?: unknown }; - const stdout = Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf8") : String(e.stdout ?? ""); - const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf8") : String(e.stderr ?? ""); + const stdout = Buffer.isBuffer(e.stdout) + ? e.stdout.toString("utf8") + : ((e.stdout ?? "") as string); + const stderr = Buffer.isBuffer(e.stderr) + ? e.stderr.toString("utf8") + : ((e.stderr ?? "") as string); return { ok: false, stdout, stderr }; } } diff --git a/test/scripts/bench-sqlite-reliability.test.ts b/test/scripts/bench-sqlite-reliability.test.ts index 6faa208b3a7d..c31553d83621 100644 --- a/test/scripts/bench-sqlite-reliability.test.ts +++ b/test/scripts/bench-sqlite-reliability.test.ts @@ -126,7 +126,9 @@ describe("scripts/bench-sqlite-reliability", () => { }, operation: async () => { fs.writeFileSync(walPath, Buffer.alloc(2048)); - await new Promise((resolve) => setTimeout(resolve, 25)); + await new Promise((resolve) => { + setTimeout(resolve, 25); + }); fs.truncateSync(walPath, 0); return "complete"; }, diff --git a/test/scripts/check-changelog-attributions.test.ts b/test/scripts/check-changelog-attributions.test.ts index 7b795c871b40..f74416e011db 100644 --- a/test/scripts/check-changelog-attributions.test.ts +++ b/test/scripts/check-changelog-attributions.test.ts @@ -25,7 +25,7 @@ function run(cwd: string, command: string, args: string[], env?: NodeJS.ProcessE function commandOutput(error: unknown): string { const result = error as { stderr?: unknown; stdout?: unknown }; - return `${String(result.stdout ?? "")}${String(result.stderr ?? "")}`; + return `${(result.stdout ?? "") as string}${(result.stderr ?? "") as string}`; } function createRepoWithPrChangelogDiff(entry: string): string { diff --git a/test/scripts/check-protocol-event-coverage.test.ts b/test/scripts/check-protocol-event-coverage.test.ts index b577fb376fc7..99c65d929030 100644 --- a/test/scripts/check-protocol-event-coverage.test.ts +++ b/test/scripts/check-protocol-event-coverage.test.ts @@ -88,13 +88,10 @@ describe("extractSwiftHandledEvents", () => { if evt.event == "connect.challenge" { return } `; const handled = extractSwiftHandledEvents(source, constants); - expect([...handled].toSorted()).toEqual([ - "chat", - "connect.challenge", - "exec.approval.requested", - "session.message", - "tick", - ]); + const handledEvents = [...handled] as string[]; + expect( + handledEvents.toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0)), + ).toEqual(["chat", "connect.challenge", "exec.approval.requested", "session.message", "tick"]); }); it("extracts only type-scoped static string constants", () => { diff --git a/test/scripts/ci-run-node-test-shard.test.ts b/test/scripts/ci-run-node-test-shard.test.ts index e039c15900d2..696a472e053c 100644 --- a/test/scripts/ci-run-node-test-shard.test.ts +++ b/test/scripts/ci-run-node-test-shard.test.ts @@ -203,7 +203,9 @@ describe("scripts/ci-run-node-test-shard.mjs", () => { } activeCaches.add(cache); seenCaches.add(cache); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); activeCaches.delete(cache); return 0; }, diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 6f29f5242f3f..de2f21702119 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -449,8 +449,8 @@ function readCriticalQualityWorkflow() { return readFileSync(".github/workflows/codeql-critical-quality.yml", "utf8"); } -function readWorkflow(path: string) { - return parse(readFileSync(path, "utf8")); +function readWorkflow(filePath: string) { + return parse(readFileSync(filePath, "utf8")); } const PULL_REQUEST_EDIT_FIELDS = ["title", "body", "base"] as const; @@ -3176,7 +3176,11 @@ describe("ci workflow guards", () => { ), ).toBe(true); expect( - new Set(retiredDisks.map((disk) => `${disk.key}:${disk.architecture}:${disk.region}`)).size, + new Set( + retiredDisks.map( + (disk) => `${disk.key as string}:${disk.architecture as string}:${disk.region as string}`, + ), + ).size, ).toBe(retiredDisks.length); expect(cleanup.on).toHaveProperty("workflow_dispatch"); expect(cleanup.permissions).toEqual({ contents: "read" }); @@ -3263,7 +3267,7 @@ describe("ci workflow guards", () => { for (const retiredDisk of retiredDisks) { expect( activeKeyPatterns.some((pattern) => pattern.test(retiredDisk.key as string)), - `${retiredDisk.key} is still an active sticky-disk key`, + `${retiredDisk.key as string} is still an active sticky-disk key`, ).toBe(false); } }); diff --git a/test/scripts/codesign-mac-app.test.ts b/test/scripts/codesign-mac-app.test.ts index f52763de33da..7c7c35e7f075 100644 --- a/test/scripts/codesign-mac-app.test.ts +++ b/test/scripts/codesign-mac-app.test.ts @@ -194,7 +194,9 @@ describe("codesign-mac-app temp file hygiene", () => { expect(signLines[1]).toContain(`${path.join(app, "Contents", "MacOS", "OpenClaw")}\t`); expect(signLines[2]).toContain(`${app}\t`); for (const line of signLines) { - const [, , entitlementPath, copiedEntitlementsPath] = line.split("\t"); + const columns = line.split("\t"); + const entitlementPath = columns[2]; + const copiedEntitlementsPath = columns[3]; const entitlementSource = expectDefined(entitlementPath, "codesign entitlement source path"); const copiedEntitlementSource = expectDefined( copiedEntitlementsPath, diff --git a/test/scripts/dependency-vulnerability-gate.test.ts b/test/scripts/dependency-vulnerability-gate.test.ts index 1c75f72d2213..fbd54e2eaee2 100644 --- a/test/scripts/dependency-vulnerability-gate.test.ts +++ b/test/scripts/dependency-vulnerability-gate.test.ts @@ -126,7 +126,10 @@ describe("dependency-vulnerability-gate", () => { const report = await runDependencyVulnerabilityGate({ rootDir, fetchImpl: async (_url, init) => { - const payload = JSON.parse(String(init?.body)); + if (typeof init?.body !== "string") { + throw new Error("expected a JSON request body"); + } + const payload = JSON.parse(init.body); payloads.push(payload); const packages = Object.keys(payload); const body: Record = {}; diff --git a/test/scripts/docker-e2e-observability.test.ts b/test/scripts/docker-e2e-observability.test.ts index 2018e328adaa..e092c1c5350e 100644 --- a/test/scripts/docker-e2e-observability.test.ts +++ b/test/scripts/docker-e2e-observability.test.ts @@ -50,7 +50,7 @@ describe("Docker E2E observability", () => { const script = readFileSync("scripts/e2e/cron-cli-docker.sh", "utf8"); expect(script).toMatch( - /docker_e2e_run_with_harness[\s\S]*\n -i \\\n "\$IMAGE_NAME" \\\n bash -s >"\$CLIENT_LOG" 2>&1 <<'INNER'/u, + /docker_e2e_run_with_harness[\s\S]*\n {2}-i \\\n {2}"\$IMAGE_NAME" \\\n {2}bash -s >"\$CLIENT_LOG" 2>&1 <<'INNER'/u, ); }); diff --git a/test/scripts/e2e-helper-env-limits.test.ts b/test/scripts/e2e-helper-env-limits.test.ts index 851e9ef6a530..4183f5e0a6de 100644 --- a/test/scripts/e2e-helper-env-limits.test.ts +++ b/test/scripts/e2e-helper-env-limits.test.ts @@ -291,7 +291,7 @@ describe("e2e helper numeric env limits", () => { headersSentResolve?.(); }); const baseUrl = await listen(server); - const realTimeout = AbortSignal.timeout; + const realTimeout = AbortSignal.timeout.bind(AbortSignal); const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockImplementation(() => realTimeout(200)); try { const result = runScript(clickclackPluginWritePath, [tempDir]); @@ -328,7 +328,9 @@ describe("e2e helper numeric env limits", () => { } finally { timeoutSpy.mockRestore(); server.closeAllConnections(); - await new Promise((resolve) => server.close(() => resolve())); + await new Promise((resolve) => { + server.close(() => resolve()); + }); fs.rmSync(tempDir, { force: true, recursive: true }); } }); diff --git a/test/scripts/find-reusable-release-validation.test.ts b/test/scripts/find-reusable-release-validation.test.ts index 576545d5e474..1fa34300b748 100644 --- a/test/scripts/find-reusable-release-validation.test.ts +++ b/test/scripts/find-reusable-release-validation.test.ts @@ -316,24 +316,27 @@ function normalizedEvidence(options: { ["productPerformance", "204", 3, 2, "OpenClaw Performance", "openclaw-performance.yml", ""], ] as const; const children = roles.map( - ([role, childRunId, runAttempt, sourceParentAttempt, name, workflow, suffix]) => ({ - conclusion: "success", - dispatchNonce: `full-release-validation-${runId}-${sourceParentAttempt}${suffix}`, - displayTitle: `${name} full-release-validation-${runId}-${sourceParentAttempt}${suffix}`, - event: "workflow_dispatch", - headBranch: workflowRef, - parentJobId: `job-${role}`, - path: `.github/workflows/${workflow}`, - role, - runAttempt, - runId: childRunId, - sourceParentAttempt, - sourceParentRunId: runId, - status: "completed", - url: `https://example.test/runs/${childRunId}`, - workflowSha: producerSha, - ...(role === "productPerformance" ? { reportPublication: "artifact-only" } : {}), - }), + ([role, childRunId, runAttempt, sourceParentAttempt, name, workflow, suffix]) => + Object.assign( + { + conclusion: "success", + dispatchNonce: `full-release-validation-${runId}-${sourceParentAttempt}${suffix}`, + displayTitle: `${name} full-release-validation-${runId}-${sourceParentAttempt}${suffix}`, + event: "workflow_dispatch", + headBranch: workflowRef, + parentJobId: `job-${role}`, + path: `.github/workflows/${workflow}`, + role, + runAttempt, + runId: childRunId, + sourceParentAttempt, + sourceParentRunId: runId, + status: "completed", + url: `https://example.test/runs/${childRunId}`, + workflowSha: producerSha, + }, + role === "productPerformance" ? { reportPublication: "artifact-only" } : {}, + ), ); return { children, @@ -485,11 +488,15 @@ function runResolver(args: { `repos/${REPOSITORY}/compare/${args.compareBaseSha}...${args.targetSha}`, ), JSON.stringify({ - files: (args.compareFiles ?? ["CHANGELOG.md"]).map((filename, index) => ({ - filename, - status: args.compareRenamed && index === 0 ? "renamed" : "modified", - ...(args.compareRenamed && index === 0 ? { previous_filename: "src/index.ts" } : {}), - })), + files: (args.compareFiles ?? ["CHANGELOG.md"]).map((filename, index) => + Object.assign( + { + filename, + status: args.compareRenamed && index === 0 ? "renamed" : "modified", + }, + args.compareRenamed && index === 0 ? { previous_filename: "src/index.ts" } : {}, + ), + ), merge_base_commit: { sha: args.compareBaseSha }, status: args.compareStatus ?? "ahead", }), @@ -819,10 +826,10 @@ describe("scripts/github/find-reusable-release-validation.sh", () => { record.current.artifact.digest = "sha256:not-a-digest"; }, }, - ])("rejects normalized evidence that is not reusable: $label", ({ mutate }) => { + ])("rejects normalized evidence that is not reusable: $label", (testCase) => { const { clone, priorSha } = getSharedRepo(); const record = normalizedEvidence({ targetSha: priorSha }); - mutate(record); + testCase.mutate(record); const { binDir, fixtures, validatorPath } = setUpFixtures([{ record, runId: "111" }]); const result = runResolver({ diff --git a/test/scripts/format-docs.test.ts b/test/scripts/format-docs.test.ts index 5f8a26833921..45546eb54b4a 100644 --- a/test/scripts/format-docs.test.ts +++ b/test/scripts/format-docs.test.ts @@ -129,7 +129,7 @@ describe("format-docs", () => { writeDocsFixture(root); const oxfmtFileArgs: string[][] = []; - const spawnSync = (command: string, args: string[]) => { + const runCommandSync = (command: string, args: string[]) => { if (command === "git") { return { status: 0, @@ -150,7 +150,7 @@ describe("format-docs", () => { }, { existsSync: fs.existsSync, - spawnSync, + spawnSync: runCommandSync, }, ), ).toEqual({ changed: [], fileCount: 2 }); @@ -164,7 +164,7 @@ describe("format-docs", () => { }, { existsSync: fs.existsSync, - spawnSync, + spawnSync: runCommandSync, }, ), ).toEqual({ changed: [], fileCount: 2 }); diff --git a/test/scripts/gateway-network-client.test.ts b/test/scripts/gateway-network-client.test.ts index df04876915cb..3605a97e0442 100644 --- a/test/scripts/gateway-network-client.test.ts +++ b/test/scripts/gateway-network-client.test.ts @@ -24,7 +24,7 @@ describe("gateway network client", () => { expect(signal).toBeInstanceOf(AbortSignal); const requestSignal = signal as AbortSignal; return new Promise((_, reject) => { - const rejectWithReason = () => reject(requestSignal.reason); + const rejectWithReason = () => reject(requestSignal.reason as Error); if (requestSignal.aborted) { rejectWithReason(); return; @@ -207,7 +207,14 @@ describe("gateway network client", () => { expect(Date.now() - startedAt).toBeLessThan(500); expect(bodySignal?.aborted).toBe(true); expect(fetchImpl).toHaveBeenCalledTimes(2); - expect(String(fetchImpl.mock.calls[1]?.[0])).toContain("/healthz"); + const request = fetchImpl.mock.calls[1]?.[0]; + const requestUrl = + request instanceof Request + ? request.url + : request instanceof URL + ? request.href + : (request ?? ""); + expect(requestUrl).toContain("/healthz"); }); it("bounds a stalled post-restart admin request by the client deadline", async () => { diff --git a/test/scripts/ios-node-e2e.test.ts b/test/scripts/ios-node-e2e.test.ts index dcae56d963fd..d7de3469cfa7 100644 --- a/test/scripts/ios-node-e2e.test.ts +++ b/test/scripts/ios-node-e2e.test.ts @@ -2,7 +2,7 @@ import { spawn } from "node:child_process"; import { createServer, type Server } from "node:http"; import { afterEach, describe, expect, it } from "vitest"; -import { WebSocket, WebSocketServer } from "ws"; +import { WebSocket, WebSocketServer, type RawData } from "ws"; import { createBoundedChildOutput } from "../helpers/bounded-child-output.js"; type ScriptResult = { @@ -92,8 +92,13 @@ async function listenGateway(params: { server = createServer(); wss = new WebSocketServer({ server }); wss.on("connection", (ws: WebSocket) => { - ws.on("message", (data) => { - const frame = JSON.parse(String(data)) as GatewayFrame; + ws.on("message", (data: RawData) => { + const text = Array.isArray(data) + ? Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString("utf8") + : Buffer.isBuffer(data) + ? data.toString("utf8") + : Buffer.from(data).toString("utf8"); + const frame = JSON.parse(text) as GatewayFrame; if (frame.type !== "req") { return; } @@ -138,7 +143,7 @@ async function listenGateway(params: { ok: true, nodeId: "ios-node", command: frame.params?.command, - payload: invokePayload(String(frame.params?.command ?? ""), params.mode), + payload: invokePayload(frame.params?.command ?? "", params.mode), }, }), ); @@ -290,7 +295,7 @@ describe("ios-node-e2e", () => { payload: {}, }, }); - expect(report.results.every((entry) => entry.ok === false)).toBe(true); + expect(report.results.every((entry) => !entry.ok)).toBe(true); expect(invokeParams.length).toBeGreaterThan(0); }); diff --git a/test/scripts/ios-release-fastlane-gates.test.ts b/test/scripts/ios-release-fastlane-gates.test.ts index a565906e51aa..428a7b8d835e 100644 --- a/test/scripts/ios-release-fastlane-gates.test.ts +++ b/test/scripts/ios-release-fastlane-gates.test.ts @@ -53,7 +53,7 @@ function swiftFunctionBody(source: string, name: string): string { } const rest = source.slice(start + startMarker.length); - const nextFunction = rest.search(/\n (?:private )?func /); + const nextFunction = rest.search(/\n {4}(?:private )?func /); return nextFunction < 0 ? rest : rest.slice(0, nextFunction); } diff --git a/test/scripts/ios-release-wrapper-args.test.ts b/test/scripts/ios-release-wrapper-args.test.ts index 1b578fc2146a..bc58504abd49 100644 --- a/test/scripts/ios-release-wrapper-args.test.ts +++ b/test/scripts/ios-release-wrapper-args.test.ts @@ -27,8 +27,12 @@ function runScript( return { ok: true, stdout, stderr: "" }; } catch (error) { const e = error as { stdout?: unknown; stderr?: unknown }; - const stdout = Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf8") : String(e.stdout ?? ""); - const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf8") : String(e.stderr ?? ""); + const stdout = Buffer.isBuffer(e.stdout) + ? e.stdout.toString("utf8") + : ((e.stdout ?? "") as string); + const stderr = Buffer.isBuffer(e.stderr) + ? e.stderr.toString("utf8") + : ((e.stderr ?? "") as string); return { ok: false, stdout, stderr }; } } diff --git a/test/scripts/ios-validate-app-store-ipa.test.ts b/test/scripts/ios-validate-app-store-ipa.test.ts index a1909d4f5c40..0e9ffde7ec31 100644 --- a/test/scripts/ios-validate-app-store-ipa.test.ts +++ b/test/scripts/ios-validate-app-store-ipa.test.ts @@ -1,5 +1,5 @@ // iOS IPA validation tests cover the App Store upload gate without real signing assets. -import { execFileSync } from "node:child_process"; +import { execFileSync, spawnSync } from "node:child_process"; import { chmodSync, mkdirSync, @@ -138,8 +138,11 @@ if (extractIndex < 0 || expectIndex < 0 || process.argv[expectIndex + 1] !== "st const key = process.argv[extractIndex + 1]; const file = process.argv[process.argv.length - 1]; const xml = readFileSync(file, "utf8"); -const escapedKey = key.replace(/[.*+?^\${}()|[\]\\]/g, "\\$&"); -const match = xml.match(new RegExp("" + escapedKey + "<\\/key>\\s*([^<]*)<\\/string>")); +// Escapes are doubled for the template-literal -> emitted-file hop: the emitted script +// must contain \\] in the class and \\$& in the replacement, or keys with regex +// metacharacters interpolate unescaped into the RegExp below and stop emulating plutil. +const escapedKey = key.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&"); +const match = xml.match(new RegExp("" + escapedKey + "\\\\s*([^<]*)")); if (!match) process.exit(1); process.stdout.write(match[1]); `, @@ -378,8 +381,12 @@ function runValidator( return { ok: true, stdout, stderr: "" }; } catch (error) { const e = error as { stdout?: unknown; stderr?: unknown }; - const stdout = Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf8") : String(e.stdout ?? ""); - const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf8") : String(e.stderr ?? ""); + const stdout = Buffer.isBuffer(e.stdout) + ? e.stdout.toString("utf8") + : ((e.stdout ?? "") as string); + const stderr = Buffer.isBuffer(e.stderr) + ? e.stderr.toString("utf8") + : ((e.stderr ?? "") as string); return { ok: false, stdout, stderr }; } } @@ -391,6 +398,37 @@ describe("scripts/ios-validate-app-store-ipa.sh", () => { } }); + it("fake plutil escapes regex-metacharacter keys before matching", () => { + const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-")); + tempDirs.push(root); + const plutil = path.join(root, "plutil"); + writeFakePlutil(plutil); + const plistPath = path.join(root, "meta.plist"); + writeFileSync( + plistPath, + "\nWeird[Key]*\nmetavalue\n", + "utf8", + ); + const escaped = spawnSync( + process.execPath, + [plutil, "-extract", "Weird[Key]*", "-expect", "string", plistPath], + { + encoding: "utf8", + }, + ); + expect(escaped.status).toBe(0); + expect(escaped.stdout).toBe("metavalue"); + // An unescaped interpolation would let this key match as a regex; it must miss instead. + const missing = spawnSync( + process.execPath, + [plutil, "-extract", "Weird.Key.*", "-expect", "string", plistPath], + { + encoding: "utf8", + }, + ); + expect(missing.status).toBe(1); + }); + it("accepts an App Store IPA with appStore mode and production entitlements", async () => { const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-")); tempDirs.push(root); diff --git a/test/scripts/k8s-manifests.test.ts b/test/scripts/k8s-manifests.test.ts index a720e09ff071..388165b3f1f6 100644 --- a/test/scripts/k8s-manifests.test.ts +++ b/test/scripts/k8s-manifests.test.ts @@ -47,7 +47,7 @@ describe("k8s manifests", () => { apiVersion: "kustomize.config.k8s.io/v1beta1", kind: "Kustomization", }); - expect(asStrings(kustomization.resources, "kustomization resources").sort()).toEqual([ + expect(asStrings(kustomization.resources, "kustomization resources").toSorted()).toEqual([ "configmap.yaml", "deployment.yaml", "pvc.yaml", diff --git a/test/scripts/kova-report-publish-files.test.ts b/test/scripts/kova-report-publish-files.test.ts index d1a067e49fdc..55973c3f21a1 100644 --- a/test/scripts/kova-report-publish-files.test.ts +++ b/test/scripts/kova-report-publish-files.test.ts @@ -23,7 +23,7 @@ describe("Kova report publish files", () => { "bundle.json", "bundle.tar.gz.sha256", ]); - expect(readdirSync(destinationDir).sort()).toEqual(["bundle.json", "bundle.tar.gz.sha256"]); + expect(readdirSync(destinationDir).toSorted()).toEqual(["bundle.json", "bundle.tar.gz.sha256"]); expect(readFileSync(join(destinationDir, "bundle.tar.gz.sha256"), "utf8")).toBe( "abc bundle.tar.gz\n", ); diff --git a/test/scripts/mantis-publish-pr-evidence.test.ts b/test/scripts/mantis-publish-pr-evidence.test.ts index 4c210b709a69..1cf1041dd824 100644 --- a/test/scripts/mantis-publish-pr-evidence.test.ts +++ b/test/scripts/mantis-publish-pr-evidence.test.ts @@ -178,7 +178,9 @@ describe("scripts/mantis/publish-pr-evidence", () => { fetchImpl: (_url, init) => { observedSignal = init.signal; return new Promise((_resolve, reject) => { - init.signal.addEventListener("abort", () => reject(init.signal.reason), { once: true }); + init.signal.addEventListener("abort", () => reject(init.signal.reason as Error), { + once: true, + }); }); }, manifest, @@ -276,7 +278,9 @@ describe("scripts/mantis/publish-pr-evidence", () => { cause: { name: "TimeoutError" }, message: "Timed out uploading Mantis artifact baseline.png after 50ms.", }); - await new Promise((resolve) => setTimeout(resolve, 0)); + await new Promise((resolve) => { + setTimeout(resolve, 0); + }); expect(cancelled).toBe(true); }); diff --git a/test/scripts/mantis-web-ui-chat-proof-workflow.test.ts b/test/scripts/mantis-web-ui-chat-proof-workflow.test.ts index 8f4f65a0c8be..9d4c86050ac2 100644 --- a/test/scripts/mantis-web-ui-chat-proof-workflow.test.ts +++ b/test/scripts/mantis-web-ui-chat-proof-workflow.test.ts @@ -45,7 +45,12 @@ function candidateOverridePattern(): RegExp { if (!match) { throw new Error("Missing candidate override regex"); } - return Function(`"use strict"; return ${match[1]};`)() as RegExp; + const literal = match[1]; + if (!literal) { + throw new Error("Missing candidate override regex literal"); + } + const flagsStart = literal.lastIndexOf("/"); + return new RegExp(literal.slice(1, flagsStart), literal.slice(flagsStart + 1)); } describe("Mantis Web UI chat proof workflow", () => { diff --git a/test/scripts/mock-openai-config.test.ts b/test/scripts/mock-openai-config.test.ts index a9b22956ab1a..89cb62545b14 100644 --- a/test/scripts/mock-openai-config.test.ts +++ b/test/scripts/mock-openai-config.test.ts @@ -135,9 +135,7 @@ describe("scripts/e2e/lib/fixtures/mock-openai-config.mjs", () => { agents: { defaults: { models: {} }, entries: { - main: { - ...(model === undefined ? {} : { model }), - }, + main: model === undefined ? {} : { model }, }, }, models: { providers: {} }, diff --git a/test/scripts/openclaw-live-updater.test.ts b/test/scripts/openclaw-live-updater.test.ts index 75bec517c789..2c2bcce14e26 100644 --- a/test/scripts/openclaw-live-updater.test.ts +++ b/test/scripts/openclaw-live-updater.test.ts @@ -194,7 +194,7 @@ function fakeCommands(mirror: string) { const calls: string[] = []; return { calls, - runCommand(command: string, args: string[]) { + runCommand: (command: string, args: string[]) => { calls.push([command, ...args].join(" ")); if (command === "pnpm" && args[0] === "install") { mkdirSync(path.join(mirror, "node_modules"), { recursive: true }); diff --git a/test/scripts/openclaw-release-telegram-qa-workflow.test.ts b/test/scripts/openclaw-release-telegram-qa-workflow.test.ts index 1fd33744923b..3922b912d6fc 100644 --- a/test/scripts/openclaw-release-telegram-qa-workflow.test.ts +++ b/test/scripts/openclaw-release-telegram-qa-workflow.test.ts @@ -433,7 +433,7 @@ describe("release Telegram QA workflow", () => { }, ); expect(result.status, result.stderr).toBe(0); - expect(result.stdout.toString().split("\0").filter(Boolean)).toEqual(paths.slice(4).reverse()); + expect(result.stdout.split("\0").filter(Boolean)).toEqual(paths.slice(4).toReversed()); }); it("keeps generated SUT programs syntactically valid", () => { diff --git a/test/scripts/periphery-scope-workflows.test.ts b/test/scripts/periphery-scope-workflows.test.ts index f59677714157..0556aed9b7a4 100644 --- a/test/scripts/periphery-scope-workflows.test.ts +++ b/test/scripts/periphery-scope-workflows.test.ts @@ -131,37 +131,47 @@ function writeFixture(repoRoot: string, relativePath: string, contents: string): } describe("Periphery scope workflows", () => { - it.each(WORKFLOW_CASES)("uses the synthetic merge parent for $name scope", ({ path }) => { - const workflow = readWorkflow(path); - const steps = workflow.jobs?.scope?.steps ?? []; - const checkout = steps.find((step) => step.name === "Checkout"); - const script = scopeScript(path); + it.each(WORKFLOW_CASES)( + "uses the synthetic merge parent for $name scope", + ({ path: workflowPath }) => { + const workflow = readWorkflow(workflowPath); + const steps = workflow.jobs?.scope?.steps ?? []; + const checkout = steps.find((step) => step.name === "Checkout"); + const script = scopeScript(workflowPath); - expect(workflow.on?.pull_request?.types).toContain("converted_to_draft"); - expect(workflow.on?.pull_request?.paths).toBeUndefined(); - expect(checkout?.with?.["fetch-depth"]).toBe(2); - expect(steps.some((step) => step.name === "Ensure base commit")).toBe(false); - expect(script).toContain('"HEAD^1"'); - expect(script).not.toContain("pulls.listFiles"); - expect(() => - compileFunction(`return (async () => {\n${script}\n})();`, ["context", "core", "exec"]), - ).not.toThrow(); - }); + expect(workflow.on?.pull_request?.types).toContain("converted_to_draft"); + expect(workflow.on?.pull_request?.paths).toBeUndefined(); + expect(checkout?.with?.["fetch-depth"]).toBe(2); + expect(steps.some((step) => step.name === "Ensure base commit")).toBe(false); + expect(script).toContain('"HEAD^1"'); + expect(script).not.toContain("pulls.listFiles"); + expect(() => + compileFunction(`return (async () => {\n${script}\n})();`, ["context", "core", "exec"]), + ).not.toThrow(); + }, + ); - it.each(WORKFLOW_CASES)("selects only $name scope changes", async ({ path, scopedPath }) => { - await expect(runScope(path, { files: [scopedPath] })).resolves.toBe("true"); - await expect(runScope(path, { files: ["docs/index.md"] })).resolves.toBe("false"); - await expect(runScope(path, { draft: true, files: [scopedPath] })).resolves.toBe("false"); - await expect(runScope(path, { eventName: "workflow_dispatch" })).resolves.toBe("true"); - await expect( - runScope(path, { - files: [{ filename: "docs/Moved.swift", previous_filename: scopedPath }], - }), - ).resolves.toBe("true"); - await expect(runScope(path, { diffExitCode: 128 })).rejects.toThrow( - "git diff failed with exit code 128", - ); - }); + it.each(WORKFLOW_CASES)( + "selects only $name scope changes", + async ({ path: workflowPath, scopedPath }) => { + await expect(runScope(workflowPath, { files: [scopedPath] })).resolves.toBe("true"); + await expect(runScope(workflowPath, { files: ["docs/index.md"] })).resolves.toBe("false"); + await expect(runScope(workflowPath, { draft: true, files: [scopedPath] })).resolves.toBe( + "false", + ); + await expect(runScope(workflowPath, { eventName: "workflow_dispatch" })).resolves.toBe( + "true", + ); + await expect( + runScope(workflowPath, { + files: [{ filename: "docs/Moved.swift", previous_filename: scopedPath }], + }), + ).resolves.toBe("true"); + await expect(runScope(workflowPath, { diffExitCode: 128 })).rejects.toThrow( + "git diff failed with exit code 128", + ); + }, + ); it("ignores scoped files added only by base-branch drift", async () => { const repoRoot = makeTempRepoRoot(tempDirs, "openclaw-periphery-scope-"); diff --git a/test/scripts/pr-prepare-gates.test.ts b/test/scripts/pr-prepare-gates.test.ts index 1cfb1eaabf36..7ee008861c9b 100644 --- a/test/scripts/pr-prepare-gates.test.ts +++ b/test/scripts/pr-prepare-gates.test.ts @@ -257,7 +257,9 @@ async function waitFor(predicate: () => boolean, timeoutMs: number): Promise setTimeout(resolve, 10)); + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); } return predicate(); } diff --git a/test/scripts/pr-review-artifact-validation.test.ts b/test/scripts/pr-review-artifact-validation.test.ts index e90ca6601418..7a42b669706e 100644 --- a/test/scripts/pr-review-artifact-validation.test.ts +++ b/test/scripts/pr-review-artifact-validation.test.ts @@ -315,7 +315,7 @@ describePosix("scripts/pr review artifact validation", () => { const archives = readdirSync(join(localDir, "superseded")); expect(archives).toHaveLength(1); const archive = join(localDir, "superseded", archives[0]!); - expect(readdirSync(archive).sort()).toEqual(["review.json", "review.md"]); + expect(readdirSync(archive).toSorted()).toEqual(["review.json", "review.md"]); expect(JSON.parse(readFileSync(join(archive, "review.json"), "utf8")).pr.number).toBe(113928); expect(readFileSync(join(archive, "review.md"), "utf8")).toBe("A) Ship another PR\n"); diff --git a/test/scripts/pr-wrappers.test.ts b/test/scripts/pr-wrappers.test.ts index a0357fda6fbf..92e34a0505bb 100644 --- a/test/scripts/pr-wrappers.test.ts +++ b/test/scripts/pr-wrappers.test.ts @@ -216,7 +216,9 @@ describe("scripts/pr wrappers", () => { const classifications = parseSubcommandClassifications(script); const dispatched = parseDispatchedSubcommands(script); - expect([...classifications.keys()].sort()).toEqual([...dispatched, "lock-recover"].sort()); + expect([...classifications.keys()].toSorted()).toEqual( + [...dispatched, "lock-recover"].toSorted(), + ); expect(classifications.get("ls")).toBe("advisory"); expect(classifications.get("ci-dispatch")).toBe("advisory"); for (const command of dispatched.filter((value) => !["ls", "ci-dispatch"].includes(value))) { diff --git a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts index b7d78415e61a..34e75c572bf0 100644 --- a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts +++ b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts @@ -438,7 +438,6 @@ describe("prepare-extension-package-boundary-artifacts", () => { tempRoots.add(rootDir); const descendantPidPath = path.join(rootDir, "descendant.pid"); let descendantPid = 0; - let runnerPid = 0; const moduleHref = pathToFileURL( path.resolve("scripts/prepare-extension-package-boundary-artifacts.mjs"), ).href; @@ -461,7 +460,7 @@ describe("prepare-extension-package-boundary-artifacts", () => { const runner = spawn(process.execPath, ["--input-type=module", "--eval", runnerScript], { stdio: "ignore", }); - runnerPid = runner.pid ?? 0; + const runnerPid = runner.pid ?? 0; try { descendantPid = Number.parseInt(await waitForFile(descendantPidPath, 10_000), 10); diff --git a/test/scripts/release-candidate-checklist.test.ts b/test/scripts/release-candidate-checklist.test.ts index f7a976b69d38..6429fbcb1182 100644 --- a/test/scripts/release-candidate-checklist.test.ts +++ b/test/scripts/release-candidate-checklist.test.ts @@ -79,14 +79,12 @@ describe("release candidate checklist", () => { toolingSha: "b".repeat(40), }); const resumed = reconcileReleaseCandidateState( - JSON.parse( - JSON.stringify({ - ...expected, - phase: "waiting", - fullReleaseRunId: "111", - npmPreflightRunId: "222", - }), - ), + structuredClone({ + ...expected, + phase: "waiting", + fullReleaseRunId: "111", + npmPreflightRunId: "222", + }), expected, ); diff --git a/test/scripts/release-no-push-workflow.test.ts b/test/scripts/release-no-push-workflow.test.ts index e626f82727f9..ae65ce3b850f 100644 --- a/test/scripts/release-no-push-workflow.test.ts +++ b/test/scripts/release-no-push-workflow.test.ts @@ -199,8 +199,8 @@ describe("release validation no-push transport", () => { "validate_docker_lanes", "validate_docker_openwebui", ]) { - const job = workflow.jobs?.[jobName]; - const runStep = job?.steps?.find((candidate) => + const workflowJob = workflow.jobs?.[jobName]; + const runStep = workflowJob?.steps?.find((candidate) => candidate.run?.includes("test-live-build-docker.sh"), ); diff --git a/test/scripts/release-telegram-candidate-archive.test.ts b/test/scripts/release-telegram-candidate-archive.test.ts index bfcaacabe780..4f523c82daee 100644 --- a/test/scripts/release-telegram-candidate-archive.test.ts +++ b/test/scripts/release-telegram-candidate-archive.test.ts @@ -20,7 +20,7 @@ import { afterEach, describe, expect, it } from "vitest"; const SCRIPT = path.resolve("scripts/release-telegram-candidate-archive.py"); const tempDirs: string[] = []; const tarVersion = spawnSync("tar", ["--version"], { encoding: "utf8" }); -const hasGnuTar = tarVersion.status === 0 && tarVersion.stdout?.includes("GNU tar") === true; +const hasGnuTar = tarVersion.status === 0 && tarVersion.stdout?.includes("GNU tar"); afterEach(() => { for (const directory of tempDirs.splice(0)) { @@ -472,7 +472,9 @@ describe("release Telegram candidate archive guard", () => { try { expectFailure(["validate-tree", root], "unsupported special entry"); } finally { - await new Promise((resolve) => server.close(() => resolve())); + await new Promise((resolve) => { + server.close(() => resolve()); + }); } }); diff --git a/test/scripts/release-user-journey-assertions.test.ts b/test/scripts/release-user-journey-assertions.test.ts index 94047368fb51..997b5abc6771 100644 --- a/test/scripts/release-user-journey-assertions.test.ts +++ b/test/scripts/release-user-journey-assertions.test.ts @@ -50,7 +50,9 @@ async function waitUntil(matches: () => boolean, label: string): Promise { if (matches()) { return; } - await new Promise((resolve) => setTimeout(resolve, 20)); + await new Promise((resolve) => { + setTimeout(resolve, 20); + }); } throw new Error(`timed out waiting for ${label}`); } diff --git a/test/scripts/sync-labels.test.ts b/test/scripts/sync-labels.test.ts index 56568df99063..3978e7944880 100644 --- a/test/scripts/sync-labels.test.ts +++ b/test/scripts/sync-labels.test.ts @@ -37,7 +37,8 @@ describe("sync-labels", () => { const ghCalls = execFileSyncMock.mock.calls.filter(([command]) => command === "gh"); expect(ghCalls.length).toBeGreaterThan(1); - for (const [, , options] of ghCalls) { + for (const call of ghCalls) { + const options = call[2]; expect(options).toMatchObject({ timeout: 120_000, killSignal: "SIGKILL", diff --git a/test/scripts/test-group-report.test.ts b/test/scripts/test-group-report.test.ts index 1cc8ba9ff7ed..b8ab4ba75fc6 100644 --- a/test/scripts/test-group-report.test.ts +++ b/test/scripts/test-group-report.test.ts @@ -882,9 +882,7 @@ describe("scripts/test-group-report arg parsing", () => { flag, expectDefined(values[1], `second ${flag} value`), ]; - expect(() => parseTestGroupReportArgs(args)).toThrow( - `${String(flag)} was provided more than once`, - ); + expect(() => parseTestGroupReportArgs(args)).toThrow(`${flag} was provided more than once`); } expect(parseTestGroupReportArgs(["--config", "a.ts", "--config", "b.ts"]).configs).toEqual([ "a.ts", diff --git a/test/scripts/test-install-sh-docker.test.ts b/test/scripts/test-install-sh-docker.test.ts index c14d315c4fd0..53da50ad5fb3 100644 --- a/test/scripts/test-install-sh-docker.test.ts +++ b/test/scripts/test-install-sh-docker.test.ts @@ -296,7 +296,7 @@ function normalizeInstallE2eAgentOutput(output: string) { function extractInstallSmokeUpdateJsonParser(): string { const script = readFileSync(SMOKE_RUNNER_PATH, "utf8"); const match = script.match( - /UPDATE_JSON="\$UPDATE_JSON" \\\n[\s\S]*?node - <<'NODE'\n([\s\S]*?)\nNODE\n\n echo "==> Verify updated version"/u, + /UPDATE_JSON="\$UPDATE_JSON" \\\n[\s\S]*?node - <<'NODE'\n([\s\S]*?)\nNODE\n\n {2}echo "==> Verify updated version"/u, ); if (!match) { throw new Error("install smoke update JSON parser was not found"); @@ -463,7 +463,9 @@ async function waitForCondition( if (predicate()) { return; } - await new Promise((resolve) => setTimeout(resolve, 5)); + await new Promise((resolve) => { + setTimeout(resolve, 5); + }); } throw new Error(`timed out waiting for ${label}`); } diff --git a/test/scripts/transitive-manifest-risk-report.test.ts b/test/scripts/transitive-manifest-risk-report.test.ts index 3c59b0a70876..9939d98d5dca 100644 --- a/test/scripts/transitive-manifest-risk-report.test.ts +++ b/test/scripts/transitive-manifest-risk-report.test.ts @@ -208,8 +208,9 @@ describe("transitive-manifest-risk-report", () => { version: "1.0.0", registryBaseUrl: "https://registry.example.test", fetchImpl: async (url, init) => { + const requestUrl = url instanceof Request ? url.url : url instanceof URL ? url.href : url; fetchCalls.push({ - url: String(url), + url: requestUrl, accept: new Headers(init?.headers).get("accept"), signal: init?.signal instanceof AbortSignal ? init.signal : null, }); diff --git a/test/scripts/verify-pr-hosted-gates.test.ts b/test/scripts/verify-pr-hosted-gates.test.ts index 343f218b1c5f..12509f8c533c 100644 --- a/test/scripts/verify-pr-hosted-gates.test.ts +++ b/test/scripts/verify-pr-hosted-gates.test.ts @@ -1077,7 +1077,9 @@ describe("verify-pr-hosted-gates", () => { ["queued artifact run", 5], ])("does not cover queued artifacts with a stale %s", (_kind, staleRunIndex) => { const workflowRuns = queuedBuildArtifactFallbackRuns().map((run, index) => - index === staleRunIndex ? { ...run, updated_at: "2026-06-16T10:54:59Z" } : run, + index === staleRunIndex + ? Object.assign({}, run, { updated_at: "2026-06-16T10:54:59Z" }) + : run, ); expect(() => collectHostedGateEvidence({ sha, workflowRuns })).toThrow( "Missing successful recent Blacksmith Build Artifacts Testbox workflow", diff --git a/test/scripts/write-cli-startup-metadata.test.ts b/test/scripts/write-cli-startup-metadata.test.ts index 5b83f70a5e7a..49582769a527 100644 --- a/test/scripts/write-cli-startup-metadata.test.ts +++ b/test/scripts/write-cli-startup-metadata.test.ts @@ -590,7 +590,9 @@ describe("write-cli-startup-metadata", () => { return "Usage: openclaw browser\n"; }, renderSourceSecretsHelpText: async () => { - await new Promise((resolve) => setImmediate(resolve)); + await new Promise((resolve) => { + setImmediate(resolve); + }); statePresentDuringSiblingRender = existsSync(stateDir); return "Usage: openclaw secrets\n"; },