From 2ac40dedbedd480367a65cd1d69cc3a0ef09602a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 29 Aug 2026 10:07:08 -0700 Subject: [PATCH] chore(lint): upgrade oxlint to 1.79.0 (#132682) * chore(lint): upgrade oxlint to 1.79.0 * test(ui): make incremental markdown scan proof deterministic --- .oxlintrc.json | 14 ++ docs/ci.md | 12 ++ .../src/test-support/provider.test-support.ts | 2 +- extensions/google-meet/index.create.test.ts | 9 +- extensions/matrix/src/matrix/send.test.ts | 4 +- .../provider-catalog.contract-test-support.ts | 2 +- .../qa-lab/src/gateway-child-lifecycle.ts | 2 - extensions/slack/src/monitor/auth.test.ts | 5 +- .../synology-chat/src/test-http-utils.ts | 2 +- .../src/bot-message-dispatch-delivery.ts | 2 +- .../telegram/src/bot-native-commands.test.ts | 8 +- .../telegram/src/bot.command-menu.test.ts | 16 +- .../bot.create-telegram-bot.test-harness.ts | 2 +- package.json | 2 +- pnpm-lock.yaml | 162 +++++++++--------- ...adapter.after-tool-call.fires-once.test.ts | 2 +- ...definition-adapter.after-tool-call.test.ts | 4 +- src/agents/auth-health.ts | 2 +- .../openclaw-tools.session-status.test.ts | 2 +- .../agent-runner-execution.test-support.ts | 10 +- ...t-sandbox-docker-browser-prune.e2e.test.ts | 2 +- src/commands/test-runtime-config-helpers.ts | 2 +- ...r-worker-placement-reconcile-guard.test.ts | 21 ++- src/plugins/management-service.ts | 1 - src/plugins/provider-runtime.test.ts | 18 +- src/plugins/providers.test.ts | 2 +- src/tasks/task-registry-query.ts | 6 +- test/scripts/lint-suppressions.test.ts | 3 - test/scripts/oxlint-config.test.ts | 122 ++++++++++++- .../browser/browser-annotation.test.ts | 43 ++--- ui/src/components/markdown.test.ts | 41 ++--- 31 files changed, 333 insertions(+), 192 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index cfa18de8e14..d7802faab42 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -394,6 +394,20 @@ "rules": { "max-lines": ["error", { "max": 1200, "skipBlankLines": true, "skipComments": true }] } + }, + { + "files": ["**/*.{ts,tsx,mts,cts}"], + "rules": { + // tsgo owns TypeScript type/value namespace validity; no-var still rejects var declarations. + "eslint/no-redeclare": "off" + } + }, + { + "files": ["extensions/qa-lab/src/web-runtime.ts"], + "rules": { + // Scenario scripts need page-global declarations, unlike Playwright's expression evaluation. + "eslint/no-eval": ["error", { "allowIndirect": true }] + } } ] } diff --git a/docs/ci.md b/docs/ci.md index fdd382686ec..05a4f2a0c49 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -442,6 +442,18 @@ ratchet-down when cleanup lowers the real count. ## Local equivalents +Oxlint keeps `eslint/no-redeclare` enabled for JavaScript. For `.ts`, `.tsx`, +`.mts`, and `.cts`, `tsgo` owns declaration validity, including intentional +type/value pairs with the same public name. `eslint/no-var` remains enabled +for all source formats; the compiler does not reject every `var` redeclaration. + +`eslint/no-eval` rejects direct and indirect evaluation by default. Only +`extensions/qa-lab/src/web-runtime.ts` allows indirect evaluation, because QA +scenario scripts need page-global declaration semantics that Playwright's +expression evaluation does not preserve. Direct evaluation remains an error +there. Tests that execute emitted browser scripts use isolated `node:vm` +contexts instead of process-global evaluation. + ```bash pnpm changed:lanes # inspect the local changed-lane classifier for origin/main...HEAD pnpm check:changed # smart local check gate: changed formatting/typecheck/lint/guards by boundary lane diff --git a/extensions/discord/src/test-support/provider.test-support.ts b/extensions/discord/src/test-support/provider.test-support.ts index 8493d5e105b..763c9f17ea2 100644 --- a/extensions/discord/src/test-support/provider.test-support.ts +++ b/extensions/discord/src/test-support/provider.test-support.ts @@ -135,7 +135,7 @@ const providerMonitorTestMocks: ProviderMonitorTestMocks = vi.hoisted(() => { monitorLifecycleMock: vi.fn(async (params: { threadBindings: { stop: () => void } }) => { params.threadBindings.stop(); }), - resolveDiscordAccountMock: vi.fn((_) => ({ + resolveDiscordAccountMock: vi.fn((_params) => ({ accountId: "default", token: "cfg-token", config: baseDiscordAccountConfig(), diff --git a/extensions/google-meet/index.create.test.ts b/extensions/google-meet/index.create.test.ts index 821ef095d70..725eaf4006e 100644 --- a/extensions/google-meet/index.create.test.ts +++ b/extensions/google-meet/index.create.test.ts @@ -1,3 +1,4 @@ +import { runInNewContext } from "node:vm"; import { Command } from "commander"; // Google Meet tests cover index.create plugin behavior. import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; @@ -94,8 +95,6 @@ async function runCreateMeetBrowserScript(params: { buttonText: string }) { }, querySelectorAll: (selector: string) => (selector === "button" ? [button] : []), }; - vi.stubGlobal("document", document); - vi.stubGlobal("location", location); type BrowserScriptResult = { meetingUri?: string; manualAction?: { reason: string; message: string }; @@ -115,8 +114,10 @@ async function runCreateMeetBrowserScript(params: { buttonText: string }) { if (typeof body.fn !== "string") { throw new Error("expected browser create script"); } - const fn = (0, eval)(`(${body.fn})`) as () => Promise; - scriptResult = await fn(); + scriptResult = await (runInNewContext(`(${body.fn})()`, { + document, + location, + }) as Promise); return { manualAction: { reason: "meet-permission-required", diff --git a/extensions/matrix/src/matrix/send.test.ts b/extensions/matrix/src/matrix/send.test.ts index 15de9056c51..3459e998780 100644 --- a/extensions/matrix/src/matrix/send.test.ts +++ b/extensions/matrix/src/matrix/send.test.ts @@ -35,9 +35,9 @@ const loadConfigMock = vi.fn(() => ({})); const withResolvedRuntimeMatrixClientMock = vi.hoisted(() => vi.fn()); const getImageMetadataMock = vi.fn().mockResolvedValue(null); const resizeToJpegMock = vi.fn(); -const mediaKindFromMimeMock = vi.fn((_: string | null | undefined) => "image"); +const mediaKindFromMimeMock = vi.fn((_mime: string | null | undefined) => "image"); const isVoiceCompatibleAudioMock = vi.fn( - (_: { contentType?: string | null; fileName?: string | null }) => false, + (_options: { contentType?: string | null; fileName?: string | null }) => false, ); const resolveTextChunkLimitMock = vi.fn< (cfg: unknown, channel: unknown, accountId?: unknown) => number diff --git a/extensions/openai/test-support/provider-catalog.contract-test-support.ts b/extensions/openai/test-support/provider-catalog.contract-test-support.ts index 398d4ed4533..018c851b99b 100644 --- a/extensions/openai/test-support/provider-catalog.contract-test-support.ts +++ b/extensions/openai/test-support/provider-catalog.contract-test-support.ts @@ -24,7 +24,7 @@ const resolveOwningPluginIdsForProviderMock = vi.hoisted(() => vi.fn(() => undefined), ); const resolveCatalogHookProviderPluginIdsMock = vi.hoisted(() => - vi.fn((_) => [] as string[]), + vi.fn((_params) => [] as string[]), ); vi.mock("openclaw/plugin-sdk/provider-catalog-runtime", async () => { diff --git a/extensions/qa-lab/src/gateway-child-lifecycle.ts b/extensions/qa-lab/src/gateway-child-lifecycle.ts index 195e0c46396..5db8577394d 100644 --- a/extensions/qa-lab/src/gateway-child-lifecycle.ts +++ b/extensions/qa-lab/src/gateway-child-lifecycle.ts @@ -125,8 +125,6 @@ export class QaGatewayChildLifecycle { ? new QaSuiteInfraError(error.code, message, { cause: error }) : new Error(message, { cause: error }); if (result.errors.length) { - // Oxlint 1.78 checks cause at argument 2; AggregateError takes it at 3. - // oxlint-disable-next-line preserve-caught-error throw new AggregateError( [primary, ...result.errors], "qa gateway startup and cleanup failed", diff --git a/extensions/slack/src/monitor/auth.test.ts b/extensions/slack/src/monitor/auth.test.ts index a84d90c6dcd..0a07ec692de 100644 --- a/extensions/slack/src/monitor/auth.test.ts +++ b/extensions/slack/src/monitor/auth.test.ts @@ -88,10 +88,11 @@ function makeAuthorizeCtx(params?: { }, isChannelAllowed: vi.fn(params?.isChannelAllowed ?? (() => true)), resolveUserName: vi.fn( - params?.resolveUserName ?? ((_) => Promise.resolve({ name: undefined })), + params?.resolveUserName ?? ((_userId) => Promise.resolve({ name: undefined })), ), resolveChannelName: vi.fn( - params?.resolveChannelName ?? ((_) => Promise.resolve({ name: "general", type: "channel" })), + params?.resolveChannelName ?? + ((_channelId) => Promise.resolve({ name: "general", type: "channel" })), ), } as unknown as SlackMonitorContext; } diff --git a/extensions/synology-chat/src/test-http-utils.ts b/extensions/synology-chat/src/test-http-utils.ts index 44652db89ec..fe4c3652dd0 100644 --- a/extensions/synology-chat/src/test-http-utils.ts +++ b/extensions/synology-chat/src/test-http-utils.ts @@ -12,7 +12,7 @@ function makeBaseReq( req.url = opts.url ?? "/webhook/synology"; req.socket = { remoteAddress: "127.0.0.1" } as unknown as IncomingMessage["socket"]; req.destroyed = false; - req.destroy = ((_: Error | undefined) => { + req.destroy = ((_error: Error | undefined) => { if (req.destroyed) { return req; } diff --git a/extensions/telegram/src/bot-message-dispatch-delivery.ts b/extensions/telegram/src/bot-message-dispatch-delivery.ts index 7788b5444ce..79a01e70750 100644 --- a/extensions/telegram/src/bot-message-dispatch-delivery.ts +++ b/extensions/telegram/src/bot-message-dispatch-delivery.ts @@ -231,7 +231,7 @@ export async function sendPayload( isSingleUseReplyToMode(turn.replyToMode) && !targetsDifferentMessage; const deliverablePayload = consumedSingleUseReply - ? (({ replyToId: _, replyToTag: _tag, replyToCurrent: _current, ...rest }) => rest)( + ? (({ replyToId: _replyToId, replyToTag: _tag, replyToCurrent: _current, ...rest }) => rest)( targetedPayload, ) : targetedPayload; diff --git a/extensions/telegram/src/bot-native-commands.test.ts b/extensions/telegram/src/bot-native-commands.test.ts index 01b6258dd4f..b36e6aa972f 100644 --- a/extensions/telegram/src/bot-native-commands.test.ts +++ b/extensions/telegram/src/bot-native-commands.test.ts @@ -191,10 +191,14 @@ describe("registerTelegramNativeCommands", () => { expect(registered).toEqual([ { command: "custom_two", description: "Custom two unchanged" }, { command: "custom_one", description: "Custom one unchanged" }, - ...native.filter((command) => !command.isAlias).map(({ isAlias: _, ...command }) => command), + ...native + .filter((command) => !command.isAlias) + .map(({ isAlias: _isAlias, ...command }) => command), { command: "alpha", description: "Alpha unchanged" }, { command: "zeta", description: "Zeta unchanged" }, - ...native.filter((command) => command.isAlias).map(({ isAlias: _, ...command }) => command), + ...native + .filter((command) => command.isAlias) + .map(({ isAlias: _isAlias, ...command }) => command), ]); }); diff --git a/extensions/telegram/src/bot.command-menu.test.ts b/extensions/telegram/src/bot.command-menu.test.ts index 976ea4832b4..b1ae43d0124 100644 --- a/extensions/telegram/src/bot.command-menu.test.ts +++ b/extensions/telegram/src/bot.command-menu.test.ts @@ -134,8 +134,12 @@ describe("createTelegramBot command menu", () => { expect(registered).toStrictEqual([ { command: "custom_backup", description: "Git backup" }, { command: "custom_generate", description: "Create an image" }, - ...native.filter((command) => !command.isAlias).map(({ isAlias: _, ...command }) => command), - ...native.filter((command) => command.isAlias).map(({ isAlias: _, ...command }) => command), + ...native + .filter((command) => !command.isAlias) + .map(({ isAlias: _isAlias, ...command }) => command), + ...native + .filter((command) => command.isAlias) + .map(({ isAlias: _isAlias, ...command }) => command), ]); }); @@ -193,8 +197,12 @@ describe("createTelegramBot command menu", () => { } expect(registered).toStrictEqual([ { command: "custom_backup", description: "Git backup" }, - ...native.filter((command) => !command.isAlias).map(({ isAlias: _, ...command }) => command), - ...native.filter((command) => command.isAlias).map(({ isAlias: _, ...command }) => command), + ...native + .filter((command) => !command.isAlias) + .map(({ isAlias: _isAlias, ...command }) => command), + ...native + .filter((command) => command.isAlias) + .map(({ isAlias: _isAlias, ...command }) => command), ]); expect(registered.find((command) => command.command === "status")).toEqual({ command: nativeStatus.command, diff --git a/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts b/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts index d515af08622..ea6e86e459a 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts @@ -339,7 +339,7 @@ const grammySpies = vi.hoisted(() => ({ stopSpy: vi.fn(), commandSpy: vi.fn(), botCtorSpy: vi.fn( - (_: string, __?: { client?: { fetch?: typeof fetch }; botInfo?: unknown }) => undefined, + (_token: string, __?: { client?: { fetch?: typeof fetch }; botInfo?: unknown }) => undefined, ), answerCallbackQuerySpy: vi.fn(async () => undefined) as AnyAsyncMock, sendChatActionSpy: vi.fn(), diff --git a/package.json b/package.json index 5d9f4c7768f..3e474f4b11f 100644 --- a/package.json +++ b/package.json @@ -2145,7 +2145,7 @@ "markdown-it": "15.0.0", "marked": "18.0.10", "oxfmt": "0.60.0", - "oxlint": "1.78.0", + "oxlint": "1.79.0", "oxlint-tsgolint": "7.0.2001", "playwright": "1.62.1", "postcss": "8.5.26", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57983864753..868f49dfdfa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -451,8 +451,8 @@ importers: specifier: 0.60.0 version: 0.60.0 oxlint: - specifier: 1.78.0 - version: 1.78.0(oxlint-tsgolint@7.0.2001) + specifier: 1.79.0 + version: 1.79.0(oxlint-tsgolint@7.0.2001) oxlint-tsgolint: specifier: 7.0.2001 version: 7.0.2001 @@ -4621,124 +4621,124 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.78.0': - resolution: {integrity: sha512-Bu819lmAfZMUHErrpe0cEWj3iaefuUODHSU8+UbXy67V/r7/7f4K3FL0NmbD85E+wiFLDYuhP8Zlv0XnVeXshw==} + '@oxlint/binding-android-arm-eabi@1.79.0': + resolution: {integrity: sha512-TebFaaMklO/RXzTv7PucaCq9l3X6D1gA+C8H6K4njtjFOV+zWE9MKLpulcJZN9bzytbUbQIY0mZuz12nQ5Kv4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.78.0': - resolution: {integrity: sha512-CDfxZgB61B7buRdY2FJoAYYPPXCZ1EoC1LKscnC5dg3kjobdxiconvAvvN1BmHyW4PyFT3jRLDag/BY/roSNBQ==} + '@oxlint/binding-android-arm64@1.79.0': + resolution: {integrity: sha512-KqqnOtAVgNsPPF0YSodkFZA1O80jcKoCZCTu3bgsszxA+MrMP9TLzfXitKjEj1FmrPprKDMdRDMmY3weESO9sg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.78.0': - resolution: {integrity: sha512-2Y2U9Ahrz+OO0Ej88f9SJYq51/jUBp1Mc7iZu0ukrbeeZ3gpRGfzIFnoqfHDY96xr0GEfNrPUBFEy0nN5aD7HA==} + '@oxlint/binding-darwin-arm64@1.79.0': + resolution: {integrity: sha512-BVC2nsMzqQzRDPc5RhixkZ+m1p7iH4bxRRvqkbwDXX0PlQKm1BPy8J8cRjnAFafOq2QzI+BfO3vE8w2GZ3CBag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.78.0': - resolution: {integrity: sha512-rpych6eJq6m9jDRypTEaPD1xysaEW5h9+xuxhGK/QhOg+/xaqPZrCrTNoIl/f3nEjuJeCEmstNDlrE9rJi/3/g==} + '@oxlint/binding-darwin-x64@1.79.0': + resolution: {integrity: sha512-p6Lm+snmhGuLKL1+CpCV8L6ijkE/qJzK2H2jG9+eKJT0n31RbY4FLsdhexekgP3bLpw4Kgde+9DZuDZQ4yIInA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.78.0': - resolution: {integrity: sha512-IcMGrQT3QizkOESUJd5et+rOhVqSkNDfNik1cvrKDqIbzqx9KMtRswpFgkCuNTSwylCFLKhGUu8KmqY1ZnC0Dg==} + '@oxlint/binding-freebsd-x64@1.79.0': + resolution: {integrity: sha512-qDMm0dXZnoHyRqSL4N4xUq82T4sqK5cbKSjvd/dF/YbMUXc2R1wEPf+vmA5S0qUmi0nwXfNbjXBtZaIqzQLIMg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.78.0': - resolution: {integrity: sha512-/uLdoJ0IXE6vo/0f0LKjinQAp+re+VMaCWaNT8ENIv2EOCkSsc8SGaflXAuW0Jua2dq5+GLVWm1NQK7P3UFSNQ==} + '@oxlint/binding-linux-arm-gnueabihf@1.79.0': + resolution: {integrity: sha512-2od7s0nuKPzqyUZAWk9KkCyGg7eI9dwFPZg+20lB15fKFkVZ0c9ZFxqPfiBAyDTlTkh9stPI0t+JlPCqMbItVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.78.0': - resolution: {integrity: sha512-7xi4Wb/O8NRJhLoUXmDJMUVpNYvB5kefdhFU1Jb8rtae4QoXlTiLwI14X4YvAXVZLNZChP8m5qO9SQAlWQTbkQ==} + '@oxlint/binding-linux-arm-musleabihf@1.79.0': + resolution: {integrity: sha512-ZOQUjkzDnvlhSE3+tWC3YXx94MMl+sYMlwH+u1+YGApGHOJP/YAc8ZBRFOXZ6eOBmxtXAWuS/fBcdZr8qqNO1A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.78.0': - resolution: {integrity: sha512-4hFW0+fVXa3OIh1Y4A5SPkmvI4wuuBSrCVKzOyE7PTjhc7yEqZ1pmvEEeS5Lj/MaqvegFxXyF33N+6jkehxdyg==} + '@oxlint/binding-linux-arm64-gnu@1.79.0': + resolution: {integrity: sha512-lu158FR4nGqGeRS3BQvtG85wRgU/Fy4MD5Cxp1hzJXizGiLo6u2742wJSCDKh8cFcZntvX7fcxlq4mMmfryH1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.78.0': - resolution: {integrity: sha512-oC0mvsgBJjlMijSDEhx9KuvR9zYeHXceA9MjbuXB1F8NSR78Yj2unOBrstEvTVaq+pko+kuue6DajC00eqvTdg==} + '@oxlint/binding-linux-arm64-musl@1.79.0': + resolution: {integrity: sha512-mbpKQeE2aflTjddaHK7MP8KP/OFbUM++lt5M635ENM8IyIdK0jm2t9pb+2v9mVVIvhF6TqA4l7F79Pll1mi+uw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.78.0': - resolution: {integrity: sha512-XAllT5SUZS+ohjuZ3/5S0cwe0r7eboiuigeStCZ5DXRYx/2KVM2UvQXvAfyzXEimtQjAB7cDQ2YxDe2Zl2WNQQ==} + '@oxlint/binding-linux-ppc64-gnu@1.79.0': + resolution: {integrity: sha512-WpGNua7gaxaHnpSDeog2ji8IDHn/QLPl9LPzwkR/FvVv58vT5BcXjRXnU+wbu3N75cpeha8CdC7ho/U2OIsB4g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.78.0': - resolution: {integrity: sha512-trucMER/0QtecoXvc1y/UVqE3kwJipDwrx4oHfj+nNm3dq2zjP44WT0CfHNDPM3G1DXIkx/gY6lAD21NSCZVhA==} + '@oxlint/binding-linux-riscv64-gnu@1.79.0': + resolution: {integrity: sha512-tK1E93A5LVzISg4ngpKJnfTs7EqtIUceGI7MQ4GyDjJiLi8wPCkEyKlj2xkyKWZ1yzkDJyLHTBJ5/iFWRdnJvg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.78.0': - resolution: {integrity: sha512-cm3O4F/HQbdzOUX5mKHqG5KDL6E5w0pnlZ+fbBy2rmLryPOowkuLagFHTopQsEIpjcaZoPOrL+BmmAytAG9HFg==} + '@oxlint/binding-linux-riscv64-musl@1.79.0': + resolution: {integrity: sha512-qhQvUIrngXivA2A9pQ+xPCychztn/5qUv7yS3gDwXv3w7Rag+eTeeXWmRyx+t7XsW5x6LuY/8AsTq36UgFIblg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.78.0': - resolution: {integrity: sha512-33wRf6HqGNsybJ3qX4cGaQN2ODPxNmc1rMa0mrTmx3eFq1VzOnvQooi9bIGVYakW8a/wmqVx1mgsUm8R2xfTiw==} + '@oxlint/binding-linux-s390x-gnu@1.79.0': + resolution: {integrity: sha512-sv6AaVgU/eE6u+6WFiQVDcPPwTxP6IJMSB9k701W2r/r6Tx465e8vPvVyRxquNH4Vy6KwRNu90mVbxXJN8+5gg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.78.0': - resolution: {integrity: sha512-rRdISSYegj6VganMZ9tjRjijowfHJ09IZU01i0toBAqr6n5LEtwHq2IeS4FjW2RoskOHlb6efB26H5izYb3GEQ==} + '@oxlint/binding-linux-x64-gnu@1.79.0': + resolution: {integrity: sha512-iFZL02deziHslb3jEX9KdqlAkYoo4fGyotchKDzdfK1f5mxlIBeiQeHhvK3iFpuEJSB4ma/qeFn9oxPiwnhUPQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.78.0': - resolution: {integrity: sha512-GmsP4rW0xTL6u5CVdcDsaN5Fbc7hBc382Wmar1kttbnwSEviM+rSINKOMQ+UQ6iH+AGwC+8gaAiwu134Tgh6Lg==} + '@oxlint/binding-linux-x64-musl@1.79.0': + resolution: {integrity: sha512-3DtZR2raqObnh7wXZoFYFd0Fw7skBvcb3f7A+/lkEiDuh8hrE6vv9b/62Qxao1a9/OeHLw/FcXlXzgsW9wTRFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.78.0': - resolution: {integrity: sha512-sy9yeYuADc8a+n4TLBayzMCZiHPW78DcIFVpOXTmdKHWQeM9xe5uzkqIIZmi326D5hY9XVwacipEB1p7tQjPAg==} + '@oxlint/binding-openharmony-arm64@1.79.0': + resolution: {integrity: sha512-Oatt4GuA1WJkqzk2ozx4HrWROOi7opV3AKDw/U8qDIqeTqzsjn5K2x3REJMNjU3/KU/Bkq96Zi3CknaiDTaC/Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.78.0': - resolution: {integrity: sha512-rjc2hF1KfMi8fZj1X/m3AmnHbdsF3rL0v6KQg0Uc880Yb2khjz+3U14sfdZ7jWTpRnN1m1NQa/TT7uU9lJWPrA==} + '@oxlint/binding-win32-arm64-msvc@1.79.0': + resolution: {integrity: sha512-NAgZr9Qp8nIA9rpo0JEvwiabTF/2UVqBNnupBG9X4kxXcQoScJUTi+qHhvabb9s/thgj5wQ4XcIaJvb+ZMgoKw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.78.0': - resolution: {integrity: sha512-zcuXFVrEFHIafRfkCQT8w/Xe41o07ozl/vwHq7p94vB29xVzsB0sZGYORU1jhcYKv3Lr0J3HbJ2T4fHH5rWmvA==} + '@oxlint/binding-win32-ia32-msvc@1.79.0': + resolution: {integrity: sha512-+KyXjIvcpaXmWW/j9NNY5yWjrIVxaX18VyIheQy3jwc2GSYgpCr7MGI/HxIGQ/shAL5IWEKbhsqoMpAO5Stiog==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.78.0': - resolution: {integrity: sha512-Sb5ocmLSuYeOuXd+CFOToGKp/gjXUEWDnvIGwhnh8aq8wY4TMmEnKnvbogSW7RdMZv77JSARduS7/gv+khYEjA==} + '@oxlint/binding-win32-x64-msvc@1.79.0': + resolution: {integrity: sha512-mEelcCMMBS57sIXh2veGMNy+pQwuGtcMxHxGIZWQ5Ba9pJ5jCCUFOZB9E2JhBaxGsURe+WGe0zJp4RVre52gpQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -8095,8 +8095,8 @@ packages: resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.78.0: - resolution: {integrity: sha512-QgQePuxIqKOzo1KSjG2EnITEeWvWnKAm77eq8nrMtf6AGoA+zyGc4PFYtDNJSD25g/ibOwfQ851hZ4/SPkMVoA==} + oxlint@1.79.0: + resolution: {integrity: sha512-hVJ9hq9m2unPS+Of4eJJgCPdIeCC+3DHEUX3tkmrPJr3OK2hz7PhXwgC+ZP71ZcYu8cCDEtQrqLxWNvxBppBVg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -11841,61 +11841,61 @@ snapshots: '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@oxlint/binding-android-arm-eabi@1.78.0': + '@oxlint/binding-android-arm-eabi@1.79.0': optional: true - '@oxlint/binding-android-arm64@1.78.0': + '@oxlint/binding-android-arm64@1.79.0': optional: true - '@oxlint/binding-darwin-arm64@1.78.0': + '@oxlint/binding-darwin-arm64@1.79.0': optional: true - '@oxlint/binding-darwin-x64@1.78.0': + '@oxlint/binding-darwin-x64@1.79.0': optional: true - '@oxlint/binding-freebsd-x64@1.78.0': + '@oxlint/binding-freebsd-x64@1.79.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.78.0': + '@oxlint/binding-linux-arm-gnueabihf@1.79.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.78.0': + '@oxlint/binding-linux-arm-musleabihf@1.79.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.78.0': + '@oxlint/binding-linux-arm64-gnu@1.79.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.78.0': + '@oxlint/binding-linux-arm64-musl@1.79.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.78.0': + '@oxlint/binding-linux-ppc64-gnu@1.79.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.78.0': + '@oxlint/binding-linux-riscv64-gnu@1.79.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.78.0': + '@oxlint/binding-linux-riscv64-musl@1.79.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.78.0': + '@oxlint/binding-linux-s390x-gnu@1.79.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.78.0': + '@oxlint/binding-linux-x64-gnu@1.79.0': optional: true - '@oxlint/binding-linux-x64-musl@1.78.0': + '@oxlint/binding-linux-x64-musl@1.79.0': optional: true - '@oxlint/binding-openharmony-arm64@1.78.0': + '@oxlint/binding-openharmony-arm64@1.79.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.78.0': + '@oxlint/binding-win32-arm64-msvc@1.79.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.78.0': + '@oxlint/binding-win32-ia32-msvc@1.79.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.78.0': + '@oxlint/binding-win32-x64-msvc@1.79.0': optional: true '@panzoom/panzoom@4.6.2': {} @@ -15568,27 +15568,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.78.0(oxlint-tsgolint@7.0.2001): + oxlint@1.79.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.78.0 - '@oxlint/binding-android-arm64': 1.78.0 - '@oxlint/binding-darwin-arm64': 1.78.0 - '@oxlint/binding-darwin-x64': 1.78.0 - '@oxlint/binding-freebsd-x64': 1.78.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.78.0 - '@oxlint/binding-linux-arm-musleabihf': 1.78.0 - '@oxlint/binding-linux-arm64-gnu': 1.78.0 - '@oxlint/binding-linux-arm64-musl': 1.78.0 - '@oxlint/binding-linux-ppc64-gnu': 1.78.0 - '@oxlint/binding-linux-riscv64-gnu': 1.78.0 - '@oxlint/binding-linux-riscv64-musl': 1.78.0 - '@oxlint/binding-linux-s390x-gnu': 1.78.0 - '@oxlint/binding-linux-x64-gnu': 1.78.0 - '@oxlint/binding-linux-x64-musl': 1.78.0 - '@oxlint/binding-openharmony-arm64': 1.78.0 - '@oxlint/binding-win32-arm64-msvc': 1.78.0 - '@oxlint/binding-win32-ia32-msvc': 1.78.0 - '@oxlint/binding-win32-x64-msvc': 1.78.0 + '@oxlint/binding-android-arm-eabi': 1.79.0 + '@oxlint/binding-android-arm64': 1.79.0 + '@oxlint/binding-darwin-arm64': 1.79.0 + '@oxlint/binding-darwin-x64': 1.79.0 + '@oxlint/binding-freebsd-x64': 1.79.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.79.0 + '@oxlint/binding-linux-arm-musleabihf': 1.79.0 + '@oxlint/binding-linux-arm64-gnu': 1.79.0 + '@oxlint/binding-linux-arm64-musl': 1.79.0 + '@oxlint/binding-linux-ppc64-gnu': 1.79.0 + '@oxlint/binding-linux-riscv64-gnu': 1.79.0 + '@oxlint/binding-linux-riscv64-musl': 1.79.0 + '@oxlint/binding-linux-s390x-gnu': 1.79.0 + '@oxlint/binding-linux-x64-gnu': 1.79.0 + '@oxlint/binding-linux-x64-musl': 1.79.0 + '@oxlint/binding-openharmony-arm64': 1.79.0 + '@oxlint/binding-win32-arm64-msvc': 1.79.0 + '@oxlint/binding-win32-ia32-msvc': 1.79.0 + '@oxlint/binding-win32-x64-msvc': 1.79.0 oxlint-tsgolint: 7.0.2001 p-finally@1.0.0: {} diff --git a/src/agents/agent-tool-definition-adapter.after-tool-call.fires-once.test.ts b/src/agents/agent-tool-definition-adapter.after-tool-call.fires-once.test.ts index 705f2f4b79e..53366a05083 100644 --- a/src/agents/agent-tool-definition-adapter.after-tool-call.fires-once.test.ts +++ b/src/agents/agent-tool-definition-adapter.after-tool-call.fires-once.test.ts @@ -29,7 +29,7 @@ const beforeToolCallMocks = vi.hoisted(() => ({ this.reason = reason; } }, - consumeAdjustedParamsForToolCall: vi.fn((_: string): unknown => undefined), + consumeAdjustedParamsForToolCall: vi.fn((_toolCallId: string): unknown => undefined), recordAdjustedParamsForToolCall: vi.fn(), recordStructuredReplayTrustForToolCall: vi.fn(), isToolWrappedWithBeforeToolCallHook: vi.fn(() => false), diff --git a/src/agents/agent-tool-definition-adapter.after-tool-call.test.ts b/src/agents/agent-tool-definition-adapter.after-tool-call.test.ts index 5f40a2c57f9..bbb39492d31 100644 --- a/src/agents/agent-tool-definition-adapter.after-tool-call.test.ts +++ b/src/agents/agent-tool-definition-adapter.after-tool-call.test.ts @@ -10,7 +10,7 @@ import { toToolDefinitions } from "./agent-tool-definition-adapter.js"; const hookMocks = vi.hoisted(() => ({ runner: { - hasHooks: vi.fn((_: string) => true), + hasHooks: vi.fn((_hookName: string) => true), runAfterToolCall: vi.fn(async () => {}), }, BeforeToolCallBlockedError: class BeforeToolCallBlockedError extends Error { @@ -23,7 +23,7 @@ const hookMocks = vi.hoisted(() => ({ } }, isToolWrappedWithBeforeToolCallHook: vi.fn(() => false), - consumeAdjustedParamsForToolCall: vi.fn((_: string) => undefined as unknown), + consumeAdjustedParamsForToolCall: vi.fn((_toolCallId: string) => undefined as unknown), recordAdjustedParamsForToolCall: vi.fn(), recordStructuredReplayTrustForToolCall: vi.fn(), runBeforeToolCallHook: vi.fn(async ({ params }: { params: unknown }) => ({ diff --git a/src/agents/auth-health.ts b/src/agents/auth-health.ts index 68df61a299d..676bc46c611 100644 --- a/src/agents/auth-health.ts +++ b/src/agents/auth-health.ts @@ -292,7 +292,7 @@ export function buildAuthHealthSummary(params: { : null; const profiles = Object.entries(params.store.profiles) - .filter(([_, cred]) => + .filter(([, cred]) => providerFilter ? providerFilter.has(normalizeProviderId(cred.provider)) : true, ) .map(([profileId, credential]) => diff --git a/src/agents/openclaw-tools.session-status.test.ts b/src/agents/openclaw-tools.session-status.test.ts index 4a6eafb3e0b..e065ab64fa8 100644 --- a/src/agents/openclaw-tools.session-status.test.ts +++ b/src/agents/openclaw-tools.session-status.test.ts @@ -30,7 +30,7 @@ const resolveQueueSettingsMock = vi.hoisted(() => ); const listTasksForRelatedSessionKeyForOwnerMock = vi.hoisted(() => vi.fn( - (_: { relatedSessionKey: string; callerOwnerKey: string }) => + (_params: { relatedSessionKey: string; callerOwnerKey: string }) => [] as Array>, ), ); diff --git a/src/auto-reply/reply/agent-runner-execution.test-support.ts b/src/auto-reply/reply/agent-runner-execution.test-support.ts index 7d754256498..cf2631010b0 100644 --- a/src/auto-reply/reply/agent-runner-execution.test-support.ts +++ b/src/auto-reply/reply/agent-runner-execution.test-support.ts @@ -59,12 +59,12 @@ const state = vi.hoisted(() => ({ runEmbeddedAgentEntryMock: vi.fn(), runCliAgentMock: vi.fn(), runWithModelFallbackMock: vi.fn(), - isCliProviderMock: vi.fn((_: unknown) => false), - isInternalMessageChannelMock: vi.fn((_: unknown) => false), + isCliProviderMock: vi.fn((_provider: unknown) => false), + isInternalMessageChannelMock: vi.fn((_channel: unknown) => false), createBlockReplyDeliveryHandlerMock: vi.fn(), - isCompactionFailureErrorMock: vi.fn((_: string | undefined) => false), - isContextOverflowErrorMock: vi.fn((_: string | undefined) => false), - isLikelyContextOverflowErrorMock: vi.fn((_: string | undefined) => false), + isCompactionFailureErrorMock: vi.fn((_message: string | undefined) => false), + isContextOverflowErrorMock: vi.fn((_message: string | undefined) => false), + isLikelyContextOverflowErrorMock: vi.fn((_message: string | undefined) => false), updateSessionStoreMock: vi.fn(), resolveCurrentTurnImagesMock: vi.fn(), peekSessionMcpRuntimeMock: vi.fn(), diff --git a/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.e2e.test.ts b/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.e2e.test.ts index 01dd3c677d2..2e82c898f7e 100644 --- a/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.e2e.test.ts +++ b/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.e2e.test.ts @@ -82,7 +82,7 @@ describe("doctor command", () => { await doctorCommand(createDoctorRuntime(), { nonInteractive: true }); - const noteTitles = terminalNoteMock.mock.calls.map(([_, title]) => title); + const noteTitles = terminalNoteMock.mock.calls.map(([, title]) => title); expect(noteTitles).not.toContain("Extra workspace"); homedirSpy.mockRestore(); diff --git a/src/commands/test-runtime-config-helpers.ts b/src/commands/test-runtime-config-helpers.ts index a5750c64d3a..51e78b7d379 100644 --- a/src/commands/test-runtime-config-helpers.ts +++ b/src/commands/test-runtime-config-helpers.ts @@ -58,7 +58,7 @@ type CapturingTestRuntime = { export function createTestRuntime(): TestRuntime { const log = vi.fn() as MockFn; const error = vi.fn() as MockFn; - const exit = vi.fn((_: number) => undefined) as MockFn; + const exit = vi.fn((_code: number) => undefined) as MockFn; return { log, error, diff --git a/src/gateway/server-worker-placement-reconcile-guard.test.ts b/src/gateway/server-worker-placement-reconcile-guard.test.ts index d028ba86716..602026a8533 100644 --- a/src/gateway/server-worker-placement-reconcile-guard.test.ts +++ b/src/gateway/server-worker-placement-reconcile-guard.test.ts @@ -84,15 +84,18 @@ describe("worker placement reconciliation teardown authority", () => { activeOwnerEpoch: null, destroyRequestedAtMs: null, }, - ])("keeps failed-placement cleanup fenced with $reason", async ({ reason: _, ...params }) => { - const { guard, resumeProvisioning } = createFailedPlacementGuard(params); - const reconcileCore = vi.fn(async () => {}); + ])( + "keeps failed-placement cleanup fenced with $reason", + async ({ reason: _reason, ...params }) => { + const { guard, resumeProvisioning } = createFailedPlacementGuard(params); + const reconcileCore = vi.fn(async () => {}); - await expect(guard("worker-cleanup", reconcileCore)).rejects.toThrow( - "provisioning owner is failed", - ); + await expect(guard("worker-cleanup", reconcileCore)).rejects.toThrow( + "provisioning owner is failed", + ); - expect(reconcileCore).not.toHaveBeenCalled(); - expect(resumeProvisioning).not.toHaveBeenCalled(); - }); + expect(reconcileCore).not.toHaveBeenCalled(); + expect(resumeProvisioning).not.toHaveBeenCalled(); + }, + ); }); diff --git a/src/plugins/management-service.ts b/src/plugins/management-service.ts index 4a4f0d8898d..58fb96834cb 100644 --- a/src/plugins/management-service.ts +++ b/src/plugins/management-service.ts @@ -1389,7 +1389,6 @@ async function persistManagedSourceInstall(params: { try { await params.transaction?.rollback(); } catch (rollbackError) { - // oxlint-disable-next-line preserve-caught-error -- Oxlint 1.78 ignores AggregateError's third-argument cause. throw new AggregateError( [error, rollbackError], "Plugin install failed and payload rollback failed", diff --git a/src/plugins/provider-runtime.test.ts b/src/plugins/provider-runtime.test.ts index 716c519bceb..9377a5b34a6 100644 --- a/src/plugins/provider-runtime.test.ts +++ b/src/plugins/provider-runtime.test.ts @@ -36,23 +36,25 @@ type ResolveBundledProviderPolicySurface = type ResolveProviderPolicySurface = typeof import("./provider-public-artifacts.js").resolveProviderPolicySurface; -const resolvePluginProvidersMock = vi.fn((_) => [] as ProviderPlugin[]); -const isPluginProvidersLoadInFlightMock = vi.fn((_) => false); +const resolvePluginProvidersMock = vi.fn( + (_params) => [] as ProviderPlugin[], +); +const isPluginProvidersLoadInFlightMock = vi.fn((_params) => false); const resolveCatalogHookProviderPluginIdsMock = vi.fn( - (_) => [] as string[], + (_params) => [] as string[], ); const resolveUsageHookProviderPluginContractsMock = vi.fn( - (_) => [], + (_params) => [], ); const resolveExternalAuthProfileProviderPluginIdsMock = - vi.fn((_) => [] as string[]); + vi.fn((_params) => [] as string[]); const resolveOwningPluginIdsForProviderMock = vi.fn( - (_) => undefined, + (_params) => undefined, ); const resolveBundledProviderPolicySurfaceMock = vi.fn( - (_) => null, + (_providerId) => null, ); -const resolveProviderPolicySurfaceMock = vi.fn((_) => null); +const resolveProviderPolicySurfaceMock = vi.fn((_providerId) => null); const providerRuntimeWarnMock = vi.fn(); let getAiTransportHost: typeof import("@openclaw/ai").getAiTransportHost; diff --git a/src/plugins/providers.test.ts b/src/plugins/providers.test.ts index 8816104e1e9..88bcd3dae57 100644 --- a/src/plugins/providers.test.ts +++ b/src/plugins/providers.test.ts @@ -29,7 +29,7 @@ const resolveRuntimePluginRegistryMock = vi.fn(); const getRuntimePluginRegistryForLoadOptionsMock = vi.fn(); const resolveCompatibleRuntimePluginRegistryMock = vi.fn(); const loadOpenClawPluginsMock = vi.fn(); -const isPluginRegistryLoadInFlightMock = vi.fn((_) => false); +const isPluginRegistryLoadInFlightMock = vi.fn((_options) => false); const loadPluginManifestRegistryMock = vi.fn(); const loadPluginMetadataSnapshotMock = vi.fn(); const loadPluginRegistrySnapshotMock = vi.fn(); diff --git a/src/tasks/task-registry-query.ts b/src/tasks/task-registry-query.ts index 61083930e4d..629061821b5 100644 --- a/src/tasks/task-registry-query.ts +++ b/src/tasks/task-registry-query.ts @@ -158,7 +158,7 @@ export function listTaskRecords(filter?: (task: Readonly) => boolean return (filter ? records.filter(filter) : records) .map((task, insertionIndex) => Object.assign({}, cloneTaskRecord(task), { insertionIndex })) .toSorted(compareTasksNewestFirst) - .map(({ insertionIndex: _, ...task }) => task); + .map(({ insertionIndex: _insertionIndex, ...task }) => task); } export function hasActiveTaskForChildSessionKey(params: { @@ -220,7 +220,7 @@ function listTasksFromIndex(index: Map>, key: string): TaskR } => Boolean(task), ) .toSorted(compareTasksNewestFirst) - .map(({ insertionIndex: _, ...task }) => task); + .map(({ insertionIndex: _insertionIndex, ...task }) => task); } export function listTasksForSessionKey(sessionKey: string): TaskRecord[] { @@ -273,7 +273,7 @@ export function listFreshTasksForOwnerKey(ownerKey: string): TaskRecord[] { return [...merged.values()] .map((task, insertionIndex) => Object.assign({}, task, { insertionIndex })) .toSorted(compareTasksNewestFirst) - .map(({ insertionIndex: _, ...task }) => task); + .map(({ insertionIndex: _insertionIndex, ...task }) => task); } catch (error) { taskRegistryLog.warn("Failed to read fresh owner task registry records", { ownerKey: key, diff --git a/test/scripts/lint-suppressions.test.ts b/test/scripts/lint-suppressions.test.ts index de433859299..f12bbaa575e 100644 --- a/test/scripts/lint-suppressions.test.ts +++ b/test/scripts/lint-suppressions.test.ts @@ -197,8 +197,6 @@ describe("production lint suppressions", () => { "extensions/discord/src/test-support/provider.test-support.ts|typescript/no-unnecessary-type-parameters|1", "extensions/feishu/src/bitable.ts|typescript/no-unnecessary-type-parameters|1", "extensions/matrix/src/onboarding.test-harness.ts|typescript/no-unnecessary-type-parameters|1", - // Oxlint 1.78 checks AggregateError cause options at the wrong argument position. - "extensions/qa-lab/src/gateway-child-lifecycle.ts|preserve-caught-error|1", "extensions/qa-lab/src/gateway-child-setup.ts|preserve-caught-error|1", "extensions/slack/src/monitor/provider-support.ts|typescript/no-unnecessary-type-parameters|1", "src/agents/agent-bundle-mcp-runtime.ts|unicorn/prefer-add-event-listener|1", @@ -235,7 +233,6 @@ describe("production lint suppressions", () => { "src/plugins/hooks.ts|typescript/no-unnecessary-type-parameters|1", "src/plugins/host-hooks.ts|typescript/no-unnecessary-type-parameters|1", "src/plugins/lazy-service-module.ts|typescript/no-unnecessary-type-parameters|1", - "src/plugins/management-service.ts|preserve-caught-error|1", "src/plugins/public-surface-loader.ts|typescript/no-unnecessary-type-parameters|3", "src/plugins/runtime/runtime-plugin-boundary.ts|typescript/no-unnecessary-type-parameters|1", "src/plugins/runtime/types-channel.ts|typescript/no-unnecessary-type-parameters|1", diff --git a/test/scripts/oxlint-config.test.ts b/test/scripts/oxlint-config.test.ts index 0e6b9c2ccbc..5e0e99b3c16 100644 --- a/test/scripts/oxlint-config.test.ts +++ b/test/scripts/oxlint-config.test.ts @@ -1,7 +1,12 @@ // Oxlint Config tests cover oxlint config script behavior. +import { spawnSync } from "node:child_process"; import fs from "node:fs"; +import path from "node:path"; import JSON5 from "json5"; import { describe, expect, it } from "vitest"; +import { createScriptTestHarness } from "./test-helpers.js"; + +const { createTempDir } = createScriptTestHarness(); type OxlintConfig = { ignorePatterns?: string[]; @@ -123,11 +128,124 @@ const DEFERRED_IMPORT_RULES = [ "import/no-unassigned-import", ]; -function readJson(path: string): unknown { - return JSON5.parse(fs.readFileSync(path, "utf8")); +function readJson(filePath: string): unknown { + return JSON5.parse(fs.readFileSync(filePath, "utf8")); } describe("oxlint config", () => { + it("enforces namespace, evaluation, and unused-binding policies with the installed binary", () => { + const tempRoot = fs.realpathSync(createTempDir("openclaw-oxlint-policy-")); + const typescriptExtensions = ["ts", "tsx", "mts", "cts"]; + const javascriptExtensions = ["js", "jsx", "cjs", "mjs"]; + const evaluation = 'eval("1 + 1");\nglobalThis.eval("1 + 1");\n'; + const fixtures = [ + ...typescriptExtensions.map((extension) => ({ + file: `src/namespaces.${extension}`, + source: [ + "export type Profile = { ready: boolean };", + "export const Profile = { ready: true };", + "export interface Adapter { ready: boolean; }", + "export const Adapter: Adapter = { ready: true };", + ].join("\n"), + rules: [], + })), + ...javascriptExtensions.map((extension) => ({ + file: `src/redeclaration.${extension}`, + source: + "var duplicateBinding = 1;\nvar duplicateBinding = 2;\nconsole.log(duplicateBinding);\n", + rules: ["eslint(no-redeclare)", "eslint(no-var)", "eslint(no-var)"], + })), + ...typescriptExtensions.map((extension) => ({ + file: `src/no-var.${extension}`, + source: "export var legacyBinding = 1;\n", + rules: ["eslint(no-var)"], + })), + ...[...typescriptExtensions, ...javascriptExtensions].flatMap((extension) => [ + { + file: `src/evaluation.${extension}`, + source: evaluation, + rules: ["eslint(no-eval)", "eslint(no-eval)"], + }, + { + file: `src/unused.${extension}`, + source: + "function meaningful(_event) { return true; }\nfunction bare(_) { return true; }\nmeaningful(1);\nbare(1);\n", + rules: ["eslint(no-unused-vars)"], + }, + ]), + ...[ + "extensions/qa-lab/src/web-runtime.ts", + "extensions/qa-lab/src/web-runtime.test.ts", + "extensions/qa-lab/src/other-runtime.ts", + "extensions/other/src/web-runtime.ts", + ].map((file) => ({ + file, + source: evaluation, + rules: + file === "extensions/qa-lab/src/web-runtime.ts" + ? ["eslint(no-eval)"] + : ["eslint(no-eval)", "eslint(no-eval)"], + })), + ]; + fs.copyFileSync(".oxlintrc.json", path.join(tempRoot, ".oxlintrc.json")); + for (const fixture of fixtures) { + const target = path.join(tempRoot, fixture.file); + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.writeFileSync(target, fixture.source); + } + // These syntax-rule fixtures need no type program; one batch uses the real config and paths. + const result = spawnSync( + process.execPath, + [ + path.resolve("node_modules/oxlint/bin/oxlint"), + "--config", + ".oxlintrc.json", + "--format", + "json", + "--threads=1", + "--report-unused-disable-directives-severity", + "error", + ...fixtures.map((fixture) => fixture.file), + ], + { cwd: tempRoot, encoding: "utf8", timeout: 10_000 }, + ); + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(1); + const report = JSON.parse(result.stdout) as { + number_of_files: number; + diagnostics: Array<{ + filename: string; + code: string; + severity: string; + labels: Array<{ span: { line: number } }>; + }>; + }; + expect(report.number_of_files).toBe(fixtures.length); + for (const fixture of fixtures) { + const diagnostics = report.diagnostics.filter( + (diagnostic) => diagnostic.filename.replaceAll("\\", "/") === fixture.file, + ); + expect(diagnostics.map((diagnostic) => diagnostic.code).toSorted(), fixture.file).toEqual( + fixture.rules.toSorted(), + ); + expect( + diagnostics.every((diagnostic) => diagnostic.severity === "error"), + fixture.file, + ).toBe(true); + } + const ownerDiagnostics = report.diagnostics.filter( + (diagnostic) => + diagnostic.filename.replaceAll("\\", "/") === "extensions/qa-lab/src/web-runtime.ts", + ); + expect(ownerDiagnostics.map((diagnostic) => diagnostic.labels[0]?.span.line)).toEqual([1]); + const unusedDiagnostics = report.diagnostics.filter( + (diagnostic) => diagnostic.code === "eslint(no-unused-vars)", + ); + expect(unusedDiagnostics.map((diagnostic) => diagnostic.labels[0]?.span.line)).toEqual( + [...typescriptExtensions, ...javascriptExtensions].map(() => 2), + ); + }); + it("includes bundled extensions in type-aware lint coverage", () => { const tsconfig = readJson("config/tsconfig/oxlint.json") as OxlintTsconfig; diff --git a/ui/src/components/browser/browser-annotation.test.ts b/ui/src/components/browser/browser-annotation.test.ts index 035b7ef52e9..319989c8dbb 100644 --- a/ui/src/components/browser/browser-annotation.test.ts +++ b/ui/src/components/browser/browser-annotation.test.ts @@ -1,3 +1,4 @@ +import { runInNewContext } from "node:vm"; import { expectDefined } from "@openclaw/normalization-core"; import { describe, expect, it, vi } from "vitest"; import type { GatewayBrowserClient } from "../../api/gateway.ts"; @@ -133,41 +134,29 @@ describe("buildBrowserAnnotationContent", () => { it("preserves valid UTF-16 from inspected accessible names", async () => { const element = document.createElement("button"); element.setAttribute("aria-label", `${"a".repeat(78)}${" ".repeat(41)}😀tail`); - const originalElementFromPoint = Object.getOwnPropertyDescriptor(document, "elementFromPoint"); - Object.defineProperty(document, "elementFromPoint", { - configurable: true, - value: vi.fn(() => element), - }); + const stubDocument = { elementFromPoint: () => element }; const client = { request: vi.fn(async (_method: string, envelope: { body?: { fn?: string } }) => { const fn = envelope.body?.fn; if (!fn) { throw new Error("missing browser evaluation function"); } - return { result: (0, eval)(`(${fn})`)() }; + return { result: runInNewContext(`(${fn})()`, { document: stubDocument }) }; }), }; - try { - const inspected = await inspectBrowserElementAt(client as unknown as GatewayBrowserClient, { - targetId: "proof-tab", - x: 10, - y: 20, - }); - const { modelContext } = buildBrowserAnnotationContent({ - url: "https://example.com", - title: "Boundary proof", - strokes: [], - element: inspected, - }); - expect(inspected?.name.charCodeAt((inspected?.name.length ?? 0) - 1)).not.toBe(0xd83d); - expect(modelContext).toContain(`button "${"a".repeat(78)}"`); - } finally { - if (originalElementFromPoint) { - Object.defineProperty(document, "elementFromPoint", originalElementFromPoint); - } else { - Reflect.deleteProperty(document, "elementFromPoint"); - } - } + const inspected = await inspectBrowserElementAt(client as unknown as GatewayBrowserClient, { + targetId: "proof-tab", + x: 10, + y: 20, + }); + const { modelContext } = buildBrowserAnnotationContent({ + url: "https://example.com", + title: "Boundary proof", + strokes: [], + element: inspected, + }); + expect(inspected?.name.charCodeAt((inspected?.name.length ?? 0) - 1)).not.toBe(0xd83d); + expect(modelContext).toContain(`button "${"a".repeat(78)}"`); }); }); diff --git a/ui/src/components/markdown.test.ts b/ui/src/components/markdown.test.ts index a1de97442ac..f797f74739f 100644 --- a/ui/src/components/markdown.test.ts +++ b/ui/src/components/markdown.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from "vitest"; import { i18n } from "../i18n/index.ts"; import { handleMarkdownCodeBlockClick } from "./markdown-code-blocks.ts"; +import * as markdownDetails from "./markdown-details.ts"; import { splitStableStreamingMarkdown } from "./markdown-streaming.ts"; import { toSanitizedMarkdownHtml, toStreamingMarkdownHtml } from "./markdown.ts"; @@ -865,36 +866,30 @@ PY }); describe("toStreamingMarkdownHtml", () => { - it("keeps appended-prefix splitting below repeated full-rescan cost", () => { - const splitIncrementally = splitStableStreamingMarkdown as ( - markdown: string, - streamKey: string, - ) => ReturnType; + it("does not rescan completed disclosures in appended prefixes", () => { const prefixes: string[] = []; let prefix = "
Done
\n\n"; - // The ratio catches a reverted full-rescan path without making runner load - // part of the assertion by spending most of the test timeout on the baseline. for (let index = 0; index < 48; index += 1) { prefix += `${String(index).padStart(3, "0")} ${"streaming markdown ".repeat(30)}\n`; prefixes.push(prefix); } - const measure = (streamKey?: string) => { - const startedAt = performance.now(); - for (const value of prefixes) { - if (streamKey) { - splitIncrementally(value, streamKey); - } else { - splitStableStreamingMarkdown(value); - } - } - return performance.now() - startedAt; - }; - measure("line-scan-warmup"); - const fullRescanMs = measure(); - const incrementalMs = measure("line-scan-regression"); + // A full rescan revisits the completed disclosure on every chunk. Observe + // the real scanner instead of comparing sub-millisecond wall-clock times. + const scanDisclosure = vi.spyOn(markdownDetails, "scanMarkdownDisclosureLine"); + try { + const fullSplits = prefixes.map((value) => splitStableStreamingMarkdown(value)); + expect(scanDisclosure).toHaveBeenCalledTimes(prefixes.length); + scanDisclosure.mockClear(); - expect(incrementalMs).toBeLessThan(fullRescanMs / 5); - }, 5_000); + const incrementalSplits = prefixes.map((value) => + splitStableStreamingMarkdown(value, "line-scan-regression"), + ); + expect(incrementalSplits).toEqual(fullSplits); + expect(scanDisclosure).toHaveBeenCalledTimes(1); + } finally { + scanDisclosure.mockRestore(); + } + }); it("keeps chunked-prefix splits identical to full splits", () => { const splitIncrementally = splitStableStreamingMarkdown as (