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 54edf7b9e2d2..555ba207cb79 100644 --- a/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs +++ b/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs @@ -67,6 +67,7 @@ const genericDirectCommitTerms = new Set([ "restore", "update", ]); +const ansiEscapePattern = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g"); let githubSnapshotState; function fail(message) { @@ -260,14 +261,11 @@ function gitCommit(ref, required = false) { function fetchGithubApi(args) { try { return JSON.parse( - run("ghx", ["api", ...args], { env: { GHX_NO_CACHE: "1" } }).replace( - /\u001B\[[0-?]*[ -/]*[@-~]/g, - "", - ), + run("ghx", ["api", ...args], { env: { GHX_NO_CACHE: "1" } }).replace(ansiEscapePattern, ""), ); } catch (error) { if (typeof error.stdout === "string" && error.stdout.trim() !== "") { - return JSON.parse(error.stdout.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "")); + return JSON.parse(error.stdout.replace(ansiEscapePattern, "")); } throw error; } @@ -1272,10 +1270,11 @@ function sourceCommits(base, target, mainRef) { for (const { candidates, commit, pullRequestOrigins } of pendingCanonicalMatches) { const matches = canonicalMainCommitMatches( commit, - [...candidates.values()].map((candidate) => ({ - ...withChangedPaths(candidate), - pullRequests: candidateMainPullRequests.get(candidate.hash) ?? [], - })), + [...candidates.values()].map((candidate) => + Object.assign({}, withChangedPaths(candidate), { + pullRequests: candidateMainPullRequests.get(candidate.hash) ?? [], + }), + ), ); canonicalMainCommitsByReleaseCommit.set(commit.hash, matches); if (pullRequestOrigins.length > 0 && matches.length === 1) { @@ -2091,14 +2090,12 @@ export function ledgerFor( for (const issue of linkedIssues) { addHandles(thanks, issue.thanks); } - return { - ...entry, - ...editorialClassification(entry.title), + return Object.assign({}, entry, editorialClassification(entry.title), { externalReferences: priorEntry?.externalReferences ?? [], linkedIssues, priorReferences, thanks, - }; + }); }); const shippedBaselineLine = formatShippedBaselineExclusions(shippedBaselines); const ledger = [ diff --git a/.agents/skills/openclaw-live-updater/scripts/update-main.mjs b/.agents/skills/openclaw-live-updater/scripts/update-main.mjs index 3ea069affb1e..41b6f1408941 100644 --- a/.agents/skills/openclaw-live-updater/scripts/update-main.mjs +++ b/.agents/skills/openclaw-live-updater/scripts/update-main.mjs @@ -74,6 +74,10 @@ function throwPreservingValue(value) { throw /** @type {Error} */ (value); } +function aggregateErrorWithCause(errors, message, cause) { + return new AggregateError(errors, message, { cause }); +} + function git(checkout, args, options = {}) { return execFileSync("git", ["-C", checkout, ...args], { encoding: options.encoding ?? "utf8", @@ -1087,9 +1091,10 @@ function prepareLaunchAgentEntrypointReplacement(deployment, entrypoint, options try { restore(); } catch (restoreError) { - throw new AggregateError( + throw aggregateErrorWithCause( [ownershipError, restoreError], "System LaunchDaemon ownership changed during plist publication and the previous LaunchAgent could not be restored", + restoreError, ); } throw ownershipError; @@ -1424,9 +1429,10 @@ function stopManagedGatewayAndProve( if (!stopError) { throw proofError; } - throw new AggregateError( + throw aggregateErrorWithCause( [stopError, proofError], "Gateway stop command failed and native stopped proof did not converge", + proofError, ); } @@ -1751,9 +1757,10 @@ function bootstrapManagedGateway(runCommand, checkout, deployment, options = {}) ); } catch (cleanupError) { if (restartError) { - throw new AggregateError( + throw aggregateErrorWithCause( [restartError, cleanupError], "Gateway restart failed and the one-shot startup trace environment could not be cleared", + cleanupError, ); } throw cleanupError; @@ -2572,7 +2579,7 @@ export function maintainMain(options, dependencies = {}) { } gatewaySuspension = prepareSuspension(update.checkout, gatewayControlDeployment); } catch (controlError) { - throw new AggregateError( + throw aggregateErrorWithCause( [ new UpdateInvariantError( "gateway_snapshot_control_unavailable", @@ -2582,6 +2589,7 @@ export function maintainMain(options, dependencies = {}) { controlError, ], "Gateway control is unavailable and the managed Gateway could not be proven stopped", + controlError, ); } } @@ -2595,9 +2603,10 @@ export function maintainMain(options, dependencies = {}) { proof: proveGatewayStopped(update.checkout), }; } catch (proofError) { - throw new AggregateError( + throw aggregateErrorWithCause( [prepareError, proofError], "Gateway suspension failed and the managed Gateway could not be proven stopped", + proofError, ); } } @@ -2651,9 +2660,10 @@ export function maintainMain(options, dependencies = {}) { gatewayControlDeployment, ); } catch (resumeError) { - throw new AggregateError( + throw aggregateErrorWithCause( [error, resumeError], "Gateway stop failed and the prepared maintenance suspension could not be resumed", + resumeError, ); } throw error; @@ -2757,9 +2767,10 @@ export function maintainMain(options, dependencies = {}) { }); waitForManagedGatewayReadiness(gatewayDeploymentBefore, probeMilestones, sleep); } catch (recoveryError) { - throw new AggregateError( + throw aggregateErrorWithCause( [error, recoveryError], "Gateway replacement failed and the previous managed service could not be restored", + recoveryError, ); } throw error; diff --git a/.oxlintrc.json b/.oxlintrc.json index b69b28663e19..fbef0a8cf208 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -8,7 +8,7 @@ }, "rules": { "curly": "error", - "eslint/no-underscore-dangle": "error", + "eslint/no-underscore-dangle": ["error", { "allow": ["__typename", "_meta"] }], "eslint-plugin-unicorn/prefer-array-find": "error", "eslint/no-array-constructor": "error", "eslint/no-await-in-loop": "off", @@ -210,6 +210,9 @@ "dist/", "dist-runtime/", "docs/_layouts/", + // Intentional negative-test corpora contain parser and lint violations by contract. + ".agents/skills/autoreview/tests/fixtures/**", + "test/fixtures/oxlint-boundary-guards/**", "**/a2ui.bundle.js", "extensions/browser/chrome-extension/modules/copilot-runtime.js", "extensions/diffs/assets/viewer-runtime.js", diff --git a/apps/android/app/src/main/assets/katex/renderer.js b/apps/android/app/src/main/assets/katex/renderer.js index 476942985838..3503e26d9f4f 100644 --- a/apps/android/app/src/main/assets/katex/renderer.js +++ b/apps/android/app/src/main/assets/katex/renderer.js @@ -28,10 +28,12 @@ window.renderMath = async (job) => { const finalBounds = container.getBoundingClientRect(); const height = Math.ceil(Math.max(finalBounds.height, container.scrollHeight)); window.ChatMathBridge.postMessage( + // oxlint-disable-next-line unicorn/require-post-message-target-origin -- AndroidX WebMessageListener bridge: single-argument postMessage; origin admitted at ChatMathRenderer.kt:526. JSON.stringify({ id: job.id, widthCssPx: width, heightCssPx: height, success: true }), ); } catch { window.ChatMathBridge.postMessage( + // oxlint-disable-next-line unicorn/require-post-message-target-origin -- AndroidX WebMessageListener bridge: single-argument postMessage; origin admitted at ChatMathRenderer.kt:526. JSON.stringify({ id: job.id, widthCssPx: 0, heightCssPx: 0, success: false }), ); } diff --git a/apps/linux/ui/quickchat.js b/apps/linux/ui/quickchat.js index f184b6588304..94b47b49e9fb 100644 --- a/apps/linux/ui/quickchat.js +++ b/apps/linux/ui/quickchat.js @@ -1159,6 +1159,7 @@ elements.input.addEventListener("input", () => { updateSendButton(); }); elements.input.addEventListener("keydown", (event) => { + // oxlint-disable-next-line unicorn/prefer-keyboard-event-key -- keyCode 229 covers WebView IME events when isComposing/key are unreliable. if (event.defaultPrevented || event.isComposing || event.keyCode === 229) { return; } diff --git a/test/scripts/bench-gateway-concurrency.test.ts b/test/scripts/bench-gateway-concurrency.test.ts index 66ec86667e4b..f48521d4a6cb 100644 --- a/test/scripts/bench-gateway-concurrency.test.ts +++ b/test/scripts/bench-gateway-concurrency.test.ts @@ -66,7 +66,7 @@ describe("gateway concurrency benchmark script", () => { const wait = calls.find((call) => call.method === "agent.wait"); expect(wait?.params).toMatchObject({ runId: "run-1" }); - const serverTimeoutMs = (wait?.params as { timeoutMs?: unknown }).timeoutMs; + const serverTimeoutMs = (wait?.params as { timeoutMs?: unknown } | undefined)?.timeoutMs; expect(serverTimeoutMs).toBe(0); expect(wait?.timeoutMs).toEqual(expect.any(Number)); expect(Number.isInteger(wait?.timeoutMs)).toBe(true); diff --git a/test/scripts/oxlint-config.test.ts b/test/scripts/oxlint-config.test.ts index ba7fed69e5e2..ef275334d871 100644 --- a/test/scripts/oxlint-config.test.ts +++ b/test/scripts/oxlint-config.test.ts @@ -145,6 +145,8 @@ describe("oxlint config", () => { "dist/", "dist-runtime/", "docs/_layouts/", + ".agents/skills/autoreview/tests/fixtures/**", + "test/fixtures/oxlint-boundary-guards/**", "**/a2ui.bundle.js", "extensions/browser/chrome-extension/modules/copilot-runtime.js", "extensions/diffs/assets/viewer-runtime.js", @@ -165,6 +167,15 @@ describe("oxlint config", () => { ]); }); + it("allows ecosystem contract fields with leading underscores", () => { + const config = readJson(".oxlintrc.json") as OxlintConfig; + + expect(config.rules?.["eslint/no-underscore-dangle"]).toEqual([ + "error", + { allow: ["__typename", "_meta"] }, + ]); + }); + it("preserves the indexed-access and test-file policies", () => { const config = readJson(".oxlintrc.json") as OxlintConfig; diff --git a/test/scripts/parallels-smoke-model.test.ts b/test/scripts/parallels-smoke-model.test.ts index 641eb447e700..bc927d97369d 100644 --- a/test/scripts/parallels-smoke-model.test.ts +++ b/test/scripts/parallels-smoke-model.test.ts @@ -1246,15 +1246,19 @@ kill -TERM "$$"`, withEnv(fakePrlctlEnv(tempDir), () => { const linuxPhases = new ExhaustedCleanupPhaseRunner(); - const linux = new LinuxGuest("Linux VM", linuxPhases as unknown as PhaseRunner); + const cleanupLinux = new LinuxGuest("Linux VM", linuxPhases as unknown as PhaseRunner); - expect(() => linux.bash("echo linux")).toThrow("Linux guest command failed with exit code 1"); + expect(() => cleanupLinux.bash("echo linux")).toThrow( + "Linux guest command failed with exit code 1", + ); expect(linuxPhases.remainingTimeoutCalls).toBe(2); const macosPhases = new ExhaustedCleanupPhaseRunner(); - const macos = createMacosGuest(macosPhases as unknown as PhaseRunner); + const cleanupMacos = createMacosGuest(macosPhases as unknown as PhaseRunner); - expect(() => macos.sh("echo macos")).toThrow("macOS guest command failed with exit code 1"); + expect(() => cleanupMacos.sh("echo macos")).toThrow( + "macOS guest command failed with exit code 1", + ); expect(macosPhases.remainingTimeoutCalls).toBe(2); }); @@ -1274,9 +1278,9 @@ kill -TERM "$$"`, append: () => undefined, remainingTimeoutMs: (fallbackMs?: number) => fallbackMs ?? 30_000, }; - const macos = createMacosGuest(phases as unknown as PhaseRunner); + const unavailableMacos = createMacosGuest(phases as unknown as PhaseRunner); - expect(() => macos.exec(["true"])).toThrow( + expect(() => unavailableMacos.exec(["true"])).toThrow( "macOS guest command failed: Parallels guest session unavailable", ); }); @@ -1284,7 +1288,7 @@ kill -TERM "$$"`, it("streams full phase logs to disk while bounding the failure tail", async () => { const runDir = makeTempDir(tempDirs, "openclaw-parallels-phase-"); - const phaseRunner = new PhaseRunner(runDir, 128); + const logPhaseRunner = new PhaseRunner(runDir, 128); const writes: string[] = []; const stderrWrite = vi.spyOn(process.stderr, "write").mockImplementation((chunk) => { writes.push(String(chunk)); @@ -1293,9 +1297,9 @@ kill -TERM "$$"`, try { await expect( - phaseRunner.phase("noisy", 30, () => { - phaseRunner.append(`old-${"x".repeat(256)}`); - phaseRunner.append("recent failure"); + logPhaseRunner.phase("noisy", 30, () => { + logPhaseRunner.append(`old-${"x".repeat(256)}`); + logPhaseRunner.append("recent failure"); throw new Error("phase failed"); }), ).rejects.toThrow("phase failed"); @@ -1314,12 +1318,12 @@ kill -TERM "$$"`, it("clamps oversized phase timers before scheduling", async () => { const runDir = makeTempDir(tempDirs, "openclaw-parallels-phase-timeout-"); - const phaseRunner = new PhaseRunner(runDir, 128); + const timerPhaseRunner = new PhaseRunner(runDir, 128); const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout"); try { await expect( - phaseRunner.phase("oversized", MAX_TIMER_TIMEOUT_SECONDS + 1, () => undefined), + timerPhaseRunner.phase("oversized", MAX_TIMER_TIMEOUT_SECONDS + 1, () => undefined), ).resolves.toBeUndefined(); expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS); expect(readFileSync(join(runDir, "phase-timings.json"), "utf8")).toContain( diff --git a/test/scripts/release-ci-summary.test.ts b/test/scripts/release-ci-summary.test.ts index cf728747f791..4fa7e33b9143 100644 --- a/test/scripts/release-ci-summary.test.ts +++ b/test/scripts/release-ci-summary.test.ts @@ -1321,7 +1321,7 @@ describe("release CI summary child correlation", () => { runId: "29090000000", }); const selected = requiredChildKeysForRerunGroup(manifest.rerunGroup, manifest.validationInputs); - expect([...selected].sort()).toEqual([ + expect([...selected].toSorted()).toEqual([ "normalCi", "npmTelegram", "pluginPrerelease",