diff --git a/extensions/phone-control/index.test.ts b/extensions/phone-control/index.test.ts index cecc6b5f06f..ae7a84acc03 100644 --- a/extensions/phone-control/index.test.ts +++ b/extensions/phone-control/index.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import type { OpenKeyedStoreOptions } from "openclaw/plugin-sdk/plugin-state-runtime"; +import type { PluginStateKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime"; import { createPluginStateKeyedStoreForTests, resetPluginStateStoreForTests, @@ -101,6 +102,25 @@ function createPhoneControlConfig(): Record { }; } +function createMockOpenKeyedStore(params: { + lookup: ReturnType; + delete?: ReturnType; +}): OpenClawPluginApi["runtime"]["state"]["openKeyedStore"] { + return () => { + const store: PluginStateKeyedStore = { + register: vi.fn(async () => {}), + registerIfAbsent: vi.fn(async () => true), + update: vi.fn(async () => true), + lookup: params.lookup as (key: string) => Promise, + consume: vi.fn(async () => undefined), + delete: (params.delete ?? vi.fn(async () => true)) as (key: string) => Promise, + entries: vi.fn(async () => []), + clear: vi.fn(async () => {}), + }; + return store; + }; +} + async function withRegisteredPhoneControl( run: (params: { command: OpenClawPluginCommandDefinition; @@ -343,7 +363,7 @@ describe("phone-control plugin", () => { it("does not block service startup on the initial expiry check", async () => { const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), PHONE_CONTROL_STATE_PREFIX)); try { - const lookup = vi.fn(async () => null); + const lookup = vi.fn(async () => undefined); let service: OpenClawPluginService | undefined; registerPhoneControl.register( @@ -355,12 +375,7 @@ describe("phone-control plugin", () => { registerService: (registeredService) => { service = registeredService; }, - openKeyedStore: () => - ({ - lookup, - delete: vi.fn(), - register: vi.fn(), - }) as ReturnType, + openKeyedStore: createMockOpenKeyedStore({ lookup }), }), ); @@ -376,7 +391,9 @@ describe("phone-control plugin", () => { expect(lookup).not.toHaveBeenCalled(); - await new Promise((resolve) => setImmediate(resolve)); + await new Promise((resolve) => { + setImmediate(resolve); + }); expect(lookup).toHaveBeenCalledWith("current"); @@ -425,12 +442,7 @@ describe("phone-control plugin", () => { registerService: (registeredService) => { service = registeredService; }, - openKeyedStore: () => - ({ - lookup, - delete: removeState, - register: vi.fn(), - }) as ReturnType, + openKeyedStore: createMockOpenKeyedStore({ lookup, delete: removeState }), }), ); diff --git a/extensions/phone-control/index.ts b/extensions/phone-control/index.ts index b7fd143e267..6ad5270bb32 100644 --- a/extensions/phone-control/index.ts +++ b/extensions/phone-control/index.ts @@ -35,6 +35,14 @@ type ArmStateFileV2 = { }; type ArmStateFile = ArmStateFileV1 | ArmStateFileV2; +type PhoneControlConfigView = { + readonly gateway?: { + readonly nodes?: { + readonly allowCommands?: readonly string[]; + readonly denyCommands?: readonly string[]; + }; + }; +}; const STATE_VERSION = 2; const ARM_STATE_NAMESPACE = "armed"; @@ -119,15 +127,15 @@ async function writeArmState(api: OpenClawPluginApi, state: ArmStateFile | null) await store.register(ARM_STATE_KEY, state); } -function normalizeDenyList(cfg: OpenClawPluginApi["config"]): string[] { +function normalizeDenyList(cfg: PhoneControlConfigView): string[] { return uniqSorted([...(cfg.gateway?.nodes?.denyCommands ?? [])]); } -function normalizeAllowList(cfg: OpenClawPluginApi["config"]): string[] { +function normalizeAllowList(cfg: PhoneControlConfigView): string[] { return uniqSorted([...(cfg.gateway?.nodes?.allowCommands ?? [])]); } -function hasPhoneControlAllowOverride(cfg: OpenClawPluginApi["config"]): boolean { +function hasPhoneControlAllowOverride(cfg: PhoneControlConfigView): boolean { const allow = new Set(normalizeAllowList(cfg)); return PHONE_CONTROL_COMMANDS.some((cmd) => allow.has(cmd)); } diff --git a/src/agents/tools/cron-tool.ts b/src/agents/tools/cron-tool.ts index aa67c2eeb05..9416fb57ecd 100644 --- a/src/agents/tools/cron-tool.ts +++ b/src/agents/tools/cron-tool.ts @@ -64,11 +64,17 @@ function isMissingOrEmptyObject(value: unknown): boolean { } function nullableStringSchema(description: string) { - return Type.Optional(Type.Union([Type.String(), Type.Null()], { description })); + return Type.Optional(Type.Unsafe({ type: "string", description })); } function nullableStringArraySchema(description: string) { - return Type.Optional(Type.Union([Type.Array(Type.String()), Type.Null()], { description })); + return Type.Optional( + Type.Unsafe({ + type: "array", + items: { type: "string" }, + description, + }), + ); } function deliveryStringSchema(params: { description: string; nullableClears: boolean }) { diff --git a/src/gateway/gateway-acp-bind.live.test.ts b/src/gateway/gateway-acp-bind.live.test.ts index 45ca7a1f5d9..4d79b6a07d4 100644 --- a/src/gateway/gateway-acp-bind.live.test.ts +++ b/src/gateway/gateway-acp-bind.live.test.ts @@ -212,7 +212,7 @@ async function prepareCodexHomeForLiveBindTest(tempRoot: string): Promise () => { hasAuthFile = true; }, - (error) => { + (error: unknown) => { if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") { throw error; } diff --git a/src/plugin-sdk/channel-entry-contract.ts b/src/plugin-sdk/channel-entry-contract.ts index 84e62f0326b..2df1ba56782 100644 --- a/src/plugin-sdk/channel-entry-contract.ts +++ b/src/plugin-sdk/channel-entry-contract.ts @@ -465,8 +465,8 @@ function loadBundledEntryModuleSync( return loaded; } -// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic entry export loaders use caller-supplied export types. /** Loads one export from a bundled channel sidecar module through the guarded entry boundary. */ +// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic entry export loaders use caller-supplied export types. export function loadBundledEntryExportSync( importMetaUrl: string, reference: BundledEntryModuleRef,