From a65f9971b727a94acdb0efa1fc26a3b89ad4612e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 6 Apr 2026 17:00:33 +0100 Subject: [PATCH] refactor(deadcode): remove duplicate barrels and helper shims --- src/channels/plugins/contracts/registry.ts | 111 --- src/channels/plugins/contracts/suites.ts | 805 ------------------ .../onboard-auth.config-shared.test.ts | 2 +- src/commands/status-all/agents.ts | 72 -- src/plugins/contracts/suites.ts | 195 ----- .../provider-onboarding-config.test.ts | 174 ---- src/plugins/provider-onboarding-config.ts | 413 --------- src/qa-e2e/report.ts | 1 - src/qa-e2e/scenario.ts | 1 - src/security/audit-extra.ts | 40 - src/tts/runtime.ts | 4 - 11 files changed, 1 insertion(+), 1817 deletions(-) delete mode 100644 src/channels/plugins/contracts/registry.ts delete mode 100644 src/channels/plugins/contracts/suites.ts delete mode 100644 src/commands/status-all/agents.ts delete mode 100644 src/plugins/contracts/suites.ts delete mode 100644 src/plugins/provider-onboarding-config.test.ts delete mode 100644 src/plugins/provider-onboarding-config.ts delete mode 100644 src/qa-e2e/report.ts delete mode 100644 src/qa-e2e/scenario.ts delete mode 100644 src/security/audit-extra.ts delete mode 100644 src/tts/runtime.ts diff --git a/src/channels/plugins/contracts/registry.ts b/src/channels/plugins/contracts/registry.ts deleted file mode 100644 index 9d591505245..00000000000 --- a/src/channels/plugins/contracts/registry.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { vi } from "vitest"; -import type { OpenClawConfig } from "../../../config/config.js"; -import { listBundledChannelPlugins, setBundledChannelRuntime } from "../bundled.js"; -import type { ChannelPlugin } from "../types.js"; -import { channelPluginSurfaceKeys, type ChannelPluginSurface } from "./manifest.js"; -import { - importBundledChannelContractArtifact, - resolveBundledChannelContractArtifactUrl, -} from "./runtime-artifacts.js"; - -type SurfaceContractEntry = { - id: string; - plugin: Pick< - ChannelPlugin, - | "id" - | "actions" - | "setup" - | "status" - | "outbound" - | "messaging" - | "threading" - | "directory" - | "gateway" - >; - surfaces: readonly ChannelPluginSurface[]; -}; - -type ThreadingContractEntry = { - id: string; - plugin: Pick; -}; - -type DirectoryContractEntry = { - id: string; - plugin: Pick; - coverage: "lookups" | "presence"; - cfg?: OpenClawConfig; - accountId?: string; -}; - -const sendMessageMatrixMock = vi.hoisted(() => - vi.fn(async (to: string, _message: string, opts?: { threadId?: string }) => ({ - messageId: opts?.threadId ? "$matrix-thread" : "$matrix-root", - roomId: to.replace(/^room:/, ""), - })), -); -const matrixRuntimeApiModuleId = resolveBundledChannelContractArtifactUrl( - "matrix", - "runtime-api.js", -); - -const lineContractApi = await importBundledChannelContractArtifact<{ - listLineAccountIds: () => string[]; - resolveDefaultLineAccountId: (cfg: OpenClawConfig) => string | undefined; - resolveLineAccount: (params: { cfg: OpenClawConfig; accountId?: string }) => unknown; -}>("line", "contract-api"); - -setBundledChannelRuntime("line", { - channel: { - line: { - listLineAccountIds: lineContractApi.listLineAccountIds, - resolveDefaultLineAccountId: lineContractApi.resolveDefaultLineAccountId, - resolveLineAccount: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) => - lineContractApi.resolveLineAccount({ cfg, accountId }), - }, - }, -} as never); - -vi.doMock(matrixRuntimeApiModuleId, async () => { - const actual = await vi.importActual(matrixRuntimeApiModuleId); - return { - ...actual, - sendMessageMatrix: sendMessageMatrixMock, - }; -}); - -let surfaceContractRegistryCache: SurfaceContractEntry[] | undefined; -let threadingContractRegistryCache: ThreadingContractEntry[] | undefined; -let directoryContractRegistryCache: DirectoryContractEntry[] | undefined; - -export function getSurfaceContractRegistry(): SurfaceContractEntry[] { - surfaceContractRegistryCache ??= listBundledChannelPlugins().map((plugin) => ({ - id: plugin.id, - plugin, - surfaces: channelPluginSurfaceKeys.filter((surface) => Boolean(plugin[surface])), - })); - return surfaceContractRegistryCache; -} - -export function getThreadingContractRegistry(): ThreadingContractEntry[] { - threadingContractRegistryCache ??= getSurfaceContractRegistry() - .filter((entry) => entry.surfaces.includes("threading")) - .map((entry) => ({ - id: entry.id, - plugin: entry.plugin, - })); - return threadingContractRegistryCache; -} - -const directoryPresenceOnlyIds = new Set(["whatsapp", "zalouser"]); - -export function getDirectoryContractRegistry(): DirectoryContractEntry[] { - directoryContractRegistryCache ??= getSurfaceContractRegistry() - .filter((entry) => entry.surfaces.includes("directory")) - .map((entry) => ({ - id: entry.id, - plugin: entry.plugin, - coverage: directoryPresenceOnlyIds.has(entry.id) ? "presence" : "lookups", - })); - return directoryContractRegistryCache; -} diff --git a/src/channels/plugins/contracts/suites.ts b/src/channels/plugins/contracts/suites.ts deleted file mode 100644 index 934d84e90a7..00000000000 --- a/src/channels/plugins/contracts/suites.ts +++ /dev/null @@ -1,805 +0,0 @@ -import { expect, it, type Mock } from "vitest"; -import type { OpenClawConfig } from "../../../config/config.js"; -import type { - ResolveProviderRuntimeGroupPolicyParams, - RuntimeGroupPolicyResolution, -} from "../../../config/runtime-group-policy.js"; -import type { - SessionBindingCapabilities, - SessionBindingRecord, -} from "../../../infra/outbound/session-binding-service.js"; -import { createNonExitingRuntime } from "../../../runtime.js"; -import type { - ChannelAccountSnapshot, - ChannelAccountState, - ChannelDirectoryEntry, - ChannelFocusedBindingContext, - ChannelReplyTransport, - ChannelSetupInput, - ChannelThreadingToolContext, -} from "../types.core.js"; -import type { - ChannelMessageActionName, - ChannelMessageCapability, - ChannelPlugin, -} from "../types.js"; -import { primeChannelOutboundSendMock } from "./test-helpers.js"; -export { - expectChannelInboundContextContract, - primeChannelOutboundSendMock, -} from "./test-helpers.js"; - -function sortStrings(values: readonly string[]) { - return [...values].toSorted((left, right) => left.localeCompare(right)); -} - -function resolveContractMessageDiscovery(params: { - plugin: Pick; - cfg: OpenClawConfig; -}) { - const actions = params.plugin.actions; - if (!actions) { - return { - actions: [] as ChannelMessageActionName[], - capabilities: [] as readonly ChannelMessageCapability[], - }; - } - const discovery = actions.describeMessageTool({ cfg: params.cfg }) ?? null; - return { - actions: Array.isArray(discovery?.actions) ? [...discovery.actions] : [], - capabilities: Array.isArray(discovery?.capabilities) ? discovery.capabilities : [], - }; -} - -const contractRuntime = createNonExitingRuntime(); - -function expectDirectoryEntryShape(entry: ChannelDirectoryEntry) { - expect(["user", "group", "channel"]).toContain(entry.kind); - expect(typeof entry.id).toBe("string"); - expect(entry.id.trim()).not.toBe(""); - if (entry.name !== undefined) { - expect(typeof entry.name).toBe("string"); - } - if (entry.handle !== undefined) { - expect(typeof entry.handle).toBe("string"); - } - if (entry.avatarUrl !== undefined) { - expect(typeof entry.avatarUrl).toBe("string"); - } - if (entry.rank !== undefined) { - expect(typeof entry.rank).toBe("number"); - } -} - -function expectThreadingToolContextShape(context: ChannelThreadingToolContext) { - if (context.currentChannelId !== undefined) { - expect(typeof context.currentChannelId).toBe("string"); - } - if (context.currentChannelProvider !== undefined) { - expect(typeof context.currentChannelProvider).toBe("string"); - } - if (context.currentThreadTs !== undefined) { - expect(typeof context.currentThreadTs).toBe("string"); - } - if (context.currentMessageId !== undefined) { - expect(["string", "number"]).toContain(typeof context.currentMessageId); - } - if (context.replyToMode !== undefined) { - expect(["off", "first", "all", "batched"]).toContain(context.replyToMode); - } - if (context.hasRepliedRef !== undefined) { - expect(typeof context.hasRepliedRef).toBe("object"); - } - if (context.skipCrossContextDecoration !== undefined) { - expect(typeof context.skipCrossContextDecoration).toBe("boolean"); - } -} - -function expectReplyTransportShape(transport: ChannelReplyTransport) { - if (transport.replyToId !== undefined && transport.replyToId !== null) { - expect(typeof transport.replyToId).toBe("string"); - } - if (transport.threadId !== undefined && transport.threadId !== null) { - expect(["string", "number"]).toContain(typeof transport.threadId); - } -} - -function expectFocusedBindingShape(binding: ChannelFocusedBindingContext) { - expect(typeof binding.conversationId).toBe("string"); - expect(binding.conversationId.trim()).not.toBe(""); - if (binding.parentConversationId !== undefined) { - expect(typeof binding.parentConversationId).toBe("string"); - } - expect(["current", "child"]).toContain(binding.placement); - expect(typeof binding.labelNoun).toBe("string"); - expect(binding.labelNoun.trim()).not.toBe(""); -} - -export function installChannelPluginContractSuite(params: { - plugin: Pick; -}) { - it("satisfies the base channel plugin contract", () => { - const { plugin } = params; - - expect(typeof plugin.id).toBe("string"); - expect(plugin.id.trim()).not.toBe(""); - - expect(plugin.meta.id).toBe(plugin.id); - expect(plugin.meta.label.trim()).not.toBe(""); - expect(plugin.meta.selectionLabel.trim()).not.toBe(""); - expect(plugin.meta.docsPath).toMatch(/^\/channels\//); - expect(plugin.meta.blurb.trim()).not.toBe(""); - - expect(plugin.capabilities.chatTypes.length).toBeGreaterThan(0); - - expect(typeof plugin.config.listAccountIds).toBe("function"); - expect(typeof plugin.config.resolveAccount).toBe("function"); - }); -} - -type ChannelActionsContractCase = { - name: string; - cfg: OpenClawConfig; - expectedActions: readonly ChannelMessageActionName[]; - expectedCapabilities?: readonly ChannelMessageCapability[]; - beforeTest?: () => void; -}; - -export function installChannelActionsContractSuite(params: { - plugin: Pick; - cases: readonly ChannelActionsContractCase[]; - unsupportedAction?: ChannelMessageActionName; -}) { - it("exposes the base message actions contract", () => { - expect(params.plugin.actions).toBeDefined(); - expect(typeof params.plugin.actions?.describeMessageTool).toBe("function"); - }); - - for (const testCase of params.cases) { - it(`actions contract: ${testCase.name}`, () => { - testCase.beforeTest?.(); - - const discovery = resolveContractMessageDiscovery({ - plugin: params.plugin, - cfg: testCase.cfg, - }); - const actions = discovery.actions; - const capabilities = discovery.capabilities; - - expect(actions).toEqual([...new Set(actions)]); - expect(capabilities).toEqual([...new Set(capabilities)]); - expect(sortStrings(actions)).toEqual(sortStrings(testCase.expectedActions)); - expect(sortStrings(capabilities)).toEqual(sortStrings(testCase.expectedCapabilities ?? [])); - - if (params.plugin.actions?.supportsAction) { - for (const action of testCase.expectedActions) { - expect(params.plugin.actions.supportsAction({ action })).toBe(true); - } - if ( - params.unsupportedAction && - !testCase.expectedActions.includes(params.unsupportedAction) - ) { - expect(params.plugin.actions.supportsAction({ action: params.unsupportedAction })).toBe( - false, - ); - } - } - }); - } -} - -export function installChannelSurfaceContractSuite(params: { - plugin: Pick< - ChannelPlugin, - | "id" - | "actions" - | "setup" - | "status" - | "outbound" - | "messaging" - | "threading" - | "directory" - | "gateway" - >; - surface: - | "actions" - | "setup" - | "status" - | "outbound" - | "messaging" - | "threading" - | "directory" - | "gateway"; -}) { - const { plugin, surface } = params; - - it(`exposes the ${surface} surface contract`, () => { - if (surface === "actions") { - expect(plugin.actions).toBeDefined(); - expect(typeof plugin.actions?.describeMessageTool).toBe("function"); - return; - } - - if (surface === "setup") { - expect(plugin.setup).toBeDefined(); - expect(typeof plugin.setup?.applyAccountConfig).toBe("function"); - return; - } - - if (surface === "status") { - expect(plugin.status).toBeDefined(); - expect(typeof plugin.status?.buildAccountSnapshot).toBe("function"); - return; - } - - if (surface === "outbound") { - const outbound = plugin.outbound; - expect(outbound).toBeDefined(); - expect(["direct", "gateway", "hybrid"]).toContain(outbound?.deliveryMode); - expect( - [ - outbound?.sendPayload, - outbound?.sendFormattedText, - outbound?.sendFormattedMedia, - outbound?.sendText, - outbound?.sendMedia, - outbound?.sendPoll, - ].some((value) => typeof value === "function"), - ).toBe(true); - return; - } - - if (surface === "messaging") { - const messaging = plugin.messaging; - expect(messaging).toBeDefined(); - expect( - [ - messaging?.normalizeTarget, - messaging?.parseExplicitTarget, - messaging?.inferTargetChatType, - messaging?.buildCrossContextComponents, - messaging?.enableInteractiveReplies, - messaging?.hasStructuredReplyPayload, - messaging?.formatTargetDisplay, - messaging?.resolveOutboundSessionRoute, - ].some((value) => typeof value === "function"), - ).toBe(true); - if (messaging?.targetResolver) { - if (messaging.targetResolver.looksLikeId) { - expect(typeof messaging.targetResolver.looksLikeId).toBe("function"); - } - if (messaging.targetResolver.hint !== undefined) { - expect(typeof messaging.targetResolver.hint).toBe("string"); - expect(messaging.targetResolver.hint.trim()).not.toBe(""); - } - if (messaging.targetResolver.resolveTarget) { - expect(typeof messaging.targetResolver.resolveTarget).toBe("function"); - } - } - return; - } - - if (surface === "threading") { - const threading = plugin.threading; - expect(threading).toBeDefined(); - expect( - [ - threading?.resolveReplyToMode, - threading?.buildToolContext, - threading?.resolveAutoThreadId, - threading?.resolveReplyTransport, - threading?.resolveFocusedBinding, - ].some((value) => typeof value === "function"), - ).toBe(true); - return; - } - - if (surface === "directory") { - const directory = plugin.directory; - expect(directory).toBeDefined(); - expect( - [ - directory?.self, - directory?.listPeers, - directory?.listPeersLive, - directory?.listGroups, - directory?.listGroupsLive, - directory?.listGroupMembers, - ].some((value) => typeof value === "function"), - ).toBe(true); - return; - } - - const gateway = plugin.gateway; - expect(gateway).toBeDefined(); - expect( - [ - gateway?.startAccount, - gateway?.stopAccount, - gateway?.loginWithQrStart, - gateway?.loginWithQrWait, - gateway?.logoutAccount, - ].some((value) => typeof value === "function"), - ).toBe(true); - }); -} - -export function installChannelThreadingContractSuite(params: { - plugin: Pick; -}) { - it("exposes the base threading contract", () => { - expect(params.plugin.threading).toBeDefined(); - }); - - it("keeps threading return values normalized", () => { - const threading = params.plugin.threading; - expect(threading).toBeDefined(); - - if (threading?.resolveReplyToMode) { - expect( - ["off", "first", "all", "batched"].includes( - threading.resolveReplyToMode({ - cfg: {} as OpenClawConfig, - accountId: "default", - chatType: "group", - }), - ), - ).toBe(true); - } - - const repliedRef = { value: false }; - const toolContext = threading?.buildToolContext?.({ - cfg: {} as OpenClawConfig, - accountId: "default", - context: { - Channel: "group:test", - From: "user:test", - To: "group:test", - ChatType: "group", - CurrentMessageId: "msg-1", - ReplyToId: "msg-0", - ReplyToIdFull: "thread-0", - MessageThreadId: "thread-0", - NativeChannelId: "native:test", - }, - hasRepliedRef: repliedRef, - }); - - if (toolContext) { - expectThreadingToolContextShape(toolContext); - if (toolContext.hasRepliedRef) { - expect(toolContext.hasRepliedRef).toBe(repliedRef); - } - } - - const autoThreadId = threading?.resolveAutoThreadId?.({ - cfg: {} as OpenClawConfig, - accountId: "default", - to: "group:test", - toolContext, - replyToId: null, - }); - if (autoThreadId !== undefined) { - expect(typeof autoThreadId).toBe("string"); - expect(autoThreadId.trim()).not.toBe(""); - } - - const replyTransport = threading?.resolveReplyTransport?.({ - cfg: {} as OpenClawConfig, - accountId: "default", - threadId: "thread-0", - replyToId: "msg-0", - }); - if (replyTransport) { - expectReplyTransportShape(replyTransport); - } - - const focusedBinding = threading?.resolveFocusedBinding?.({ - cfg: {} as OpenClawConfig, - accountId: "default", - context: { - Channel: "group:test", - From: "user:test", - To: "group:test", - ChatType: "group", - CurrentMessageId: "msg-1", - ReplyToId: "msg-0", - ReplyToIdFull: "thread-0", - MessageThreadId: "thread-0", - NativeChannelId: "native:test", - }, - }); - if (focusedBinding) { - expectFocusedBindingShape(focusedBinding); - } - }); -} - -export function installChannelDirectoryContractSuite(params: { - plugin: Pick; - coverage?: "lookups" | "presence"; - cfg?: OpenClawConfig; - accountId?: string; -}) { - it("exposes the base directory contract", async () => { - const directory = params.plugin.directory; - expect(directory).toBeDefined(); - - if (params.coverage === "presence") { - return; - } - const self = await directory?.self?.({ - cfg: params.cfg ?? ({} as OpenClawConfig), - accountId: params.accountId ?? "default", - runtime: contractRuntime, - }); - if (self) { - expectDirectoryEntryShape(self); - } - - const peers = - (await directory?.listPeers?.({ - cfg: params.cfg ?? ({} as OpenClawConfig), - accountId: params.accountId ?? "default", - query: "", - limit: 5, - runtime: contractRuntime, - })) ?? []; - expect(Array.isArray(peers)).toBe(true); - for (const peer of peers) { - expectDirectoryEntryShape(peer); - } - - const groups = - (await directory?.listGroups?.({ - cfg: params.cfg ?? ({} as OpenClawConfig), - accountId: params.accountId ?? "default", - query: "", - limit: 5, - runtime: contractRuntime, - })) ?? []; - expect(Array.isArray(groups)).toBe(true); - for (const group of groups) { - expectDirectoryEntryShape(group); - } - - if (directory?.listGroupMembers && groups[0]?.id) { - const members = await directory.listGroupMembers({ - cfg: params.cfg ?? ({} as OpenClawConfig), - accountId: params.accountId ?? "default", - groupId: groups[0].id, - limit: 5, - runtime: contractRuntime, - }); - expect(Array.isArray(members)).toBe(true); - for (const member of members) { - expectDirectoryEntryShape(member); - } - } - }); -} - -export function installSessionBindingContractSuite(params: { - getCapabilities: () => SessionBindingCapabilities | Promise; - bindAndResolve: () => Promise; - unbindAndVerify: (binding: SessionBindingRecord) => Promise; - cleanup: () => Promise | void; - expectedCapabilities: SessionBindingCapabilities; -}) { - it("registers the expected session binding capabilities", async () => { - expect(await Promise.resolve(params.getCapabilities())).toEqual(params.expectedCapabilities); - }); - - it("binds and resolves a session binding through the shared service", async () => { - const binding = await params.bindAndResolve(); - expect(typeof binding.bindingId).toBe("string"); - expect(binding.bindingId.trim()).not.toBe(""); - expect(typeof binding.targetSessionKey).toBe("string"); - expect(binding.targetSessionKey.trim()).not.toBe(""); - expect(["session", "subagent"]).toContain(binding.targetKind); - expect(typeof binding.conversation.channel).toBe("string"); - expect(typeof binding.conversation.accountId).toBe("string"); - expect(typeof binding.conversation.conversationId).toBe("string"); - expect(["active", "ending", "ended"]).toContain(binding.status); - expect(typeof binding.boundAt).toBe("number"); - }); - - it("unbinds a registered binding through the shared service", async () => { - const binding = await params.bindAndResolve(); - await params.unbindAndVerify(binding); - }); - - it("cleans up registered bindings", async () => { - await params.cleanup(); - }); -} - -type ChannelSetupContractCase = { - name: string; - cfg: OpenClawConfig; - accountId?: string; - input: ChannelSetupInput; - expectedAccountId?: string; - expectedValidation?: string | null; - beforeTest?: () => void; - assertPatchedConfig?: (cfg: OpenClawConfig) => void; - assertResolvedAccount?: (account: ResolvedAccount, cfg: OpenClawConfig) => void; -}; - -export function installChannelSetupContractSuite(params: { - plugin: Pick, "id" | "config" | "setup">; - cases: readonly ChannelSetupContractCase[]; -}) { - it("exposes the base setup contract", () => { - expect(params.plugin.setup).toBeDefined(); - expect(typeof params.plugin.setup?.applyAccountConfig).toBe("function"); - }); - - for (const testCase of params.cases) { - it(`setup contract: ${testCase.name}`, () => { - testCase.beforeTest?.(); - - const resolvedAccountId = - params.plugin.setup?.resolveAccountId?.({ - cfg: testCase.cfg, - accountId: testCase.accountId, - input: testCase.input, - }) ?? - testCase.accountId ?? - "default"; - - expect(resolvedAccountId).toBe(testCase.expectedAccountId ?? resolvedAccountId); - - const validation = - params.plugin.setup?.validateInput?.({ - cfg: testCase.cfg, - accountId: resolvedAccountId, - input: testCase.input, - }) ?? null; - expect(validation).toBe(testCase.expectedValidation ?? null); - - const nextCfg = params.plugin.setup?.applyAccountConfig({ - cfg: testCase.cfg, - accountId: resolvedAccountId, - input: testCase.input, - }); - expect(nextCfg).toBeDefined(); - - const account = params.plugin.config.resolveAccount(nextCfg!, resolvedAccountId); - testCase.assertPatchedConfig?.(nextCfg!); - testCase.assertResolvedAccount?.(account, nextCfg!); - }); - } -} - -type ChannelStatusContractCase = { - name: string; - cfg: OpenClawConfig; - accountId?: string; - runtime?: ChannelAccountSnapshot; - probe?: Probe; - beforeTest?: () => void; - expectedState?: ChannelAccountState; - resolveStateInput?: { - configured: boolean; - enabled: boolean; - }; - assertSnapshot?: (snapshot: ChannelAccountSnapshot) => void; - assertSummary?: (summary: Record) => void; -}; - -export function installChannelStatusContractSuite(params: { - plugin: Pick, "id" | "config" | "status">; - cases: readonly ChannelStatusContractCase[]; -}) { - it("exposes the base status contract", () => { - expect(params.plugin.status).toBeDefined(); - expect(typeof params.plugin.status?.buildAccountSnapshot).toBe("function"); - }); - - if (params.plugin.status?.defaultRuntime) { - it("status contract: default runtime is shaped like an account snapshot", () => { - expect(typeof params.plugin.status?.defaultRuntime?.accountId).toBe("string"); - }); - } - - for (const testCase of params.cases) { - it(`status contract: ${testCase.name}`, async () => { - testCase.beforeTest?.(); - - const account = params.plugin.config.resolveAccount(testCase.cfg, testCase.accountId); - const snapshot = await params.plugin.status!.buildAccountSnapshot!({ - account, - cfg: testCase.cfg, - runtime: testCase.runtime, - probe: testCase.probe, - }); - - expect(typeof snapshot.accountId).toBe("string"); - expect(snapshot.accountId.trim()).not.toBe(""); - testCase.assertSnapshot?.(snapshot); - - if (params.plugin.status?.buildChannelSummary) { - const defaultAccountId = - params.plugin.config.defaultAccountId?.(testCase.cfg) ?? testCase.accountId ?? "default"; - const summary = await params.plugin.status.buildChannelSummary({ - account, - cfg: testCase.cfg, - defaultAccountId, - snapshot, - }); - expect(summary).toEqual(expect.any(Object)); - testCase.assertSummary?.(summary); - } - - if (testCase.expectedState && params.plugin.status?.resolveAccountState) { - const state = params.plugin.status.resolveAccountState({ - account, - cfg: testCase.cfg, - configured: testCase.resolveStateInput?.configured ?? true, - enabled: testCase.resolveStateInput?.enabled ?? true, - }); - expect(state).toBe(testCase.expectedState); - } - }); - } -} - -type PayloadLike = { - mediaUrl?: string; - mediaUrls?: string[]; - text?: string; -}; - -type SendResultLike = { - messageId: string; - [key: string]: unknown; -}; - -type ChunkingMode = - | { - longTextLength: number; - maxChunkLength: number; - mode: "split"; - } - | { - longTextLength: number; - mode: "passthrough"; - }; - -export function installChannelOutboundPayloadContractSuite(params: { - channel: string; - chunking: ChunkingMode; - createHarness: (params: { payload: PayloadLike; sendResults?: SendResultLike[] }) => { - run: () => Promise>; - sendMock: Mock; - to: string; - }; -}) { - it("text-only delegates to sendText", async () => { - const { run, sendMock, to } = params.createHarness({ - payload: { text: "hello" }, - }); - const result = await run(); - - expect(sendMock).toHaveBeenCalledTimes(1); - expect(sendMock).toHaveBeenCalledWith(to, "hello", expect.any(Object)); - expect(result).toMatchObject({ channel: params.channel }); - }); - - it("single media delegates to sendMedia", async () => { - const { run, sendMock, to } = params.createHarness({ - payload: { text: "cap", mediaUrl: "https://example.com/a.jpg" }, - }); - const result = await run(); - - expect(sendMock).toHaveBeenCalledTimes(1); - expect(sendMock).toHaveBeenCalledWith( - to, - "cap", - expect.objectContaining({ mediaUrl: "https://example.com/a.jpg" }), - ); - expect(result).toMatchObject({ channel: params.channel }); - }); - - it("multi-media iterates URLs with caption on first", async () => { - const { run, sendMock, to } = params.createHarness({ - payload: { - text: "caption", - mediaUrls: ["https://example.com/1.jpg", "https://example.com/2.jpg"], - }, - sendResults: [{ messageId: "m-1" }, { messageId: "m-2" }], - }); - const result = await run(); - - expect(sendMock).toHaveBeenCalledTimes(2); - expect(sendMock).toHaveBeenNthCalledWith( - 1, - to, - "caption", - expect.objectContaining({ mediaUrl: "https://example.com/1.jpg" }), - ); - expect(sendMock).toHaveBeenNthCalledWith( - 2, - to, - "", - expect.objectContaining({ mediaUrl: "https://example.com/2.jpg" }), - ); - expect(result).toMatchObject({ channel: params.channel, messageId: "m-2" }); - }); - - it("empty payload returns no-op", async () => { - const { run, sendMock } = params.createHarness({ payload: {} }); - const result = await run(); - - expect(sendMock).not.toHaveBeenCalled(); - expect(result).toEqual({ channel: params.channel, messageId: "" }); - }); - - if (params.chunking.mode === "passthrough") { - it("text exceeding chunk limit is sent as-is when chunker is null", async () => { - const text = "a".repeat(params.chunking.longTextLength); - const { run, sendMock, to } = params.createHarness({ payload: { text } }); - const result = await run(); - - expect(sendMock).toHaveBeenCalledTimes(1); - expect(sendMock).toHaveBeenCalledWith(to, text, expect.any(Object)); - expect(result).toMatchObject({ channel: params.channel }); - }); - return; - } - - const chunking = params.chunking; - - it("chunking splits long text", async () => { - const text = "a".repeat(chunking.longTextLength); - const { run, sendMock } = params.createHarness({ - payload: { text }, - sendResults: [{ messageId: "c-1" }, { messageId: "c-2" }], - }); - const result = await run(); - - expect(sendMock.mock.calls.length).toBeGreaterThanOrEqual(2); - for (const call of sendMock.mock.calls) { - expect((call[1] as string).length).toBeLessThanOrEqual(chunking.maxChunkLength); - } - expect(result).toMatchObject({ channel: params.channel }); - }); -} - -type RuntimeGroupPolicyResolver = ( - params: ResolveProviderRuntimeGroupPolicyParams, -) => RuntimeGroupPolicyResolution; - -export function installChannelRuntimeGroupPolicyFallbackSuite(params: { - configuredLabel: string; - defaultGroupPolicyUnderTest: "allowlist" | "disabled" | "open"; - missingConfigLabel: string; - missingDefaultLabel: string; - resolve: RuntimeGroupPolicyResolver; -}) { - it(params.missingConfigLabel, () => { - const resolved = params.resolve({ - providerConfigPresent: false, - }); - expect(resolved.groupPolicy).toBe("allowlist"); - expect(resolved.providerMissingFallbackApplied).toBe(true); - }); - - it(params.configuredLabel, () => { - const resolved = params.resolve({ - providerConfigPresent: true, - }); - expect(resolved.groupPolicy).toBe("open"); - expect(resolved.providerMissingFallbackApplied).toBe(false); - }); - - it(params.missingDefaultLabel, () => { - const resolved = params.resolve({ - providerConfigPresent: false, - defaultGroupPolicy: params.defaultGroupPolicyUnderTest, - }); - expect(resolved.groupPolicy).toBe("allowlist"); - expect(resolved.providerMissingFallbackApplied).toBe(true); - }); -} diff --git a/src/commands/onboard-auth.config-shared.test.ts b/src/commands/onboard-auth.config-shared.test.ts index ecdfd227094..044787457b4 100644 --- a/src/commands/onboard-auth.config-shared.test.ts +++ b/src/commands/onboard-auth.config-shared.test.ts @@ -9,7 +9,7 @@ import { applyProviderConfigWithDefaultModels, applyProviderConfigWithModelCatalog, withAgentModelAliases, -} from "../plugins/provider-onboarding-config.js"; +} from "../plugin-sdk/provider-onboard.js"; function makeModel(id: string): ModelDefinitionConfig { return { diff --git a/src/commands/status-all/agents.ts b/src/commands/status-all/agents.ts deleted file mode 100644 index e8d7c485fe5..00000000000 --- a/src/commands/status-all/agents.ts +++ /dev/null @@ -1,72 +0,0 @@ -import fs from "node:fs/promises"; -import path from "node:path"; -import { resolveAgentWorkspaceDir } from "../../agents/agent-scope.js"; -import type { OpenClawConfig } from "../../config/config.js"; -import { loadSessionStore, resolveStorePath } from "../../config/sessions.js"; -import { listGatewayAgentsBasic } from "../../gateway/agent-list.js"; - -async function fileExists(p: string): Promise { - try { - await fs.access(p); - return true; - } catch { - return false; - } -} - -export async function getAgentLocalStatuses(cfg: OpenClawConfig) { - const agentList = listGatewayAgentsBasic(cfg); - const now = Date.now(); - - const agents = await Promise.all( - agentList.agents.map(async (agent) => { - const workspaceDir = (() => { - try { - return resolveAgentWorkspaceDir(cfg, agent.id); - } catch { - return null; - } - })(); - const bootstrapPending = - workspaceDir != null ? await fileExists(path.join(workspaceDir, "BOOTSTRAP.md")) : null; - const sessionsPath = resolveStorePath(cfg.session?.store, { - agentId: agent.id, - }); - const store = (() => { - try { - return loadSessionStore(sessionsPath); - } catch { - return {}; - } - })(); - const updatedAt = Object.values(store).reduce( - (max, entry) => Math.max(max, entry?.updatedAt ?? 0), - 0, - ); - const lastUpdatedAt = updatedAt > 0 ? updatedAt : null; - const lastActiveAgeMs = lastUpdatedAt ? now - lastUpdatedAt : null; - const sessionsCount = Object.keys(store).filter( - (k) => k !== "global" && k !== "unknown", - ).length; - return { - id: agent.id, - name: agent.name, - workspaceDir, - bootstrapPending, - sessionsPath, - sessionsCount, - lastUpdatedAt, - lastActiveAgeMs, - }; - }), - ); - - const totalSessions = agents.reduce((sum, a) => sum + a.sessionsCount, 0); - const bootstrapPendingCount = agents.reduce((sum, a) => sum + (a.bootstrapPending ? 1 : 0), 0); - return { - defaultId: agentList.defaultId, - agents, - totalSessions, - bootstrapPendingCount, - }; -} diff --git a/src/plugins/contracts/suites.ts b/src/plugins/contracts/suites.ts deleted file mode 100644 index 5d46d9caab5..00000000000 --- a/src/plugins/contracts/suites.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { expect, it } from "vitest"; -import type { OpenClawConfig } from "../../config/config.js"; -import type { ProviderPlugin, WebFetchProviderPlugin, WebSearchProviderPlugin } from "../types.js"; - -type Lazy = T | (() => T); - -function resolveLazy(value: Lazy): T { - return typeof value === "function" ? (value as () => T)() : value; -} - -export function installProviderPluginContractSuite(params: { provider: Lazy }) { - it("satisfies the base provider plugin contract", () => { - const provider = resolveLazy(params.provider); - const authIds = provider.auth.map((method) => method.id); - const wizardChoiceIds = new Set(); - - expect(provider.id).toMatch(/^[a-z0-9][a-z0-9-]*$/); - expect(provider.label.trim()).not.toBe(""); - - if (provider.docsPath) { - expect(provider.docsPath.startsWith("/")).toBe(true); - } - if (provider.aliases) { - expect(provider.aliases).toEqual([...new Set(provider.aliases)]); - } - if (provider.envVars) { - expect(provider.envVars).toEqual([...new Set(provider.envVars)]); - expect(provider.envVars.every((entry) => entry.trim().length > 0)).toBe(true); - } - - expect(Array.isArray(provider.auth)).toBe(true); - expect(authIds).toEqual([...new Set(authIds)]); - for (const method of provider.auth) { - expect(method.id.trim()).not.toBe(""); - expect(method.label.trim()).not.toBe(""); - if (method.hint !== undefined) { - expect(method.hint.trim()).not.toBe(""); - } - if (method.wizard) { - if (method.wizard.choiceId) { - expect(method.wizard.choiceId.trim()).not.toBe(""); - expect(wizardChoiceIds.has(method.wizard.choiceId)).toBe(false); - wizardChoiceIds.add(method.wizard.choiceId); - } - if (method.wizard.methodId) { - expect(authIds).toContain(method.wizard.methodId); - } - if (method.wizard.modelAllowlist?.allowedKeys) { - expect(method.wizard.modelAllowlist.allowedKeys).toEqual([ - ...new Set(method.wizard.modelAllowlist.allowedKeys), - ]); - } - if (method.wizard.modelAllowlist?.initialSelections) { - expect(method.wizard.modelAllowlist.initialSelections).toEqual([ - ...new Set(method.wizard.modelAllowlist.initialSelections), - ]); - } - } - expect(typeof method.run).toBe("function"); - } - - if (provider.wizard?.setup || provider.wizard?.modelPicker) { - expect(provider.auth.length).toBeGreaterThan(0); - } - if (provider.wizard?.setup) { - if (provider.wizard.setup.choiceId) { - expect(provider.wizard.setup.choiceId.trim()).not.toBe(""); - expect(wizardChoiceIds.has(provider.wizard.setup.choiceId)).toBe(false); - } - if (provider.wizard.setup.methodId) { - expect(authIds).toContain(provider.wizard.setup.methodId); - } - if (provider.wizard.setup.modelAllowlist?.allowedKeys) { - expect(provider.wizard.setup.modelAllowlist.allowedKeys).toEqual([ - ...new Set(provider.wizard.setup.modelAllowlist.allowedKeys), - ]); - } - if (provider.wizard.setup.modelAllowlist?.initialSelections) { - expect(provider.wizard.setup.modelAllowlist.initialSelections).toEqual([ - ...new Set(provider.wizard.setup.modelAllowlist.initialSelections), - ]); - } - } - if (provider.wizard?.modelPicker?.methodId) { - expect(authIds).toContain(provider.wizard.modelPicker.methodId); - } - }); -} - -export function installWebSearchProviderContractSuite(params: { - provider: Lazy; - credentialValue: Lazy; -}) { - it("satisfies the base web search provider contract", () => { - const provider = resolveLazy(params.provider); - const credentialValue = resolveLazy(params.credentialValue); - - expect(provider.id).toMatch(/^[a-z0-9][a-z0-9-]*$/); - expect(provider.label.trim()).not.toBe(""); - expect(provider.hint.trim()).not.toBe(""); - expect(provider.placeholder.trim()).not.toBe(""); - expect(provider.signupUrl.startsWith("https://")).toBe(true); - if (provider.docsUrl) { - expect(provider.docsUrl.startsWith("http")).toBe(true); - } - - expect(provider.envVars).toEqual([...new Set(provider.envVars)]); - expect(provider.envVars.every((entry) => entry.trim().length > 0)).toBe(true); - - const searchConfigTarget: Record = {}; - provider.setCredentialValue(searchConfigTarget, credentialValue); - expect(provider.getCredentialValue(searchConfigTarget)).toEqual(credentialValue); - - const config = { - tools: { - web: { - search: { - provider: provider.id, - ...searchConfigTarget, - }, - }, - }, - } as OpenClawConfig; - const tool = provider.createTool({ config, searchConfig: searchConfigTarget }); - - expect(tool).not.toBeNull(); - expect(tool?.description.trim()).not.toBe(""); - expect(tool?.parameters).toEqual(expect.any(Object)); - expect(typeof tool?.execute).toBe("function"); - if (provider.runSetup) { - expect(typeof provider.runSetup).toBe("function"); - } - }); -} - -export function installWebFetchProviderContractSuite(params: { - provider: Lazy; - credentialValue: Lazy; - pluginId?: string; -}) { - it("satisfies the base web fetch provider contract", () => { - const provider = resolveLazy(params.provider); - const credentialValue = resolveLazy(params.credentialValue); - - expect(provider.id).toMatch(/^[a-z0-9][a-z0-9-]*$/); - expect(provider.label.trim()).not.toBe(""); - expect(provider.hint.trim()).not.toBe(""); - expect(provider.placeholder.trim()).not.toBe(""); - expect(provider.signupUrl.startsWith("https://")).toBe(true); - if (provider.docsUrl) { - expect(provider.docsUrl.startsWith("http")).toBe(true); - } - - expect(provider.envVars).toEqual([...new Set(provider.envVars)]); - expect(provider.envVars.every((entry) => entry.trim().length > 0)).toBe(true); - expect(provider.credentialPath.trim()).not.toBe(""); - if (provider.inactiveSecretPaths) { - expect(provider.inactiveSecretPaths).toEqual([...new Set(provider.inactiveSecretPaths)]); - // Runtime inactive-path classification uses inactiveSecretPaths as the complete list. - expect(provider.inactiveSecretPaths).toContain(provider.credentialPath); - } - - const fetchConfigTarget: Record = {}; - provider.setCredentialValue(fetchConfigTarget, credentialValue); - expect(provider.getCredentialValue(fetchConfigTarget)).toEqual(credentialValue); - - if (provider.setConfiguredCredentialValue && provider.getConfiguredCredentialValue) { - const configTarget = {} as OpenClawConfig; - provider.setConfiguredCredentialValue(configTarget, credentialValue); - expect(provider.getConfiguredCredentialValue(configTarget)).toEqual(credentialValue); - } - - if (provider.applySelectionConfig && params.pluginId) { - const applied = provider.applySelectionConfig({} as OpenClawConfig); - expect(applied.plugins?.entries?.[params.pluginId]?.enabled).toBe(true); - } - - const config = { - tools: { - web: { - fetch: { - provider: provider.id, - ...fetchConfigTarget, - }, - }, - }, - } as OpenClawConfig; - const tool = provider.createTool({ config, fetchConfig: fetchConfigTarget }); - - expect(tool).not.toBeNull(); - expect(tool?.description.trim()).not.toBe(""); - expect(tool?.parameters).toEqual(expect.any(Object)); - expect(typeof tool?.execute).toBe("function"); - }); -} diff --git a/src/plugins/provider-onboarding-config.test.ts b/src/plugins/provider-onboarding-config.test.ts deleted file mode 100644 index 7676d3523b6..00000000000 --- a/src/plugins/provider-onboarding-config.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { OpenClawConfig } from "../config/config.js"; -import type { ModelDefinitionConfig } from "../config/types.models.js"; -import { - createDefaultModelPresetAppliers, - createDefaultModelsPresetAppliers, - createModelCatalogPresetAppliers, -} from "./provider-onboarding-config.js"; - -function createModel(id: string, name: string): ModelDefinitionConfig { - return { - id, - name, - reasoning: false, - input: ["text"], - cost: { - input: 0, - output: 0, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 128_000, - maxTokens: 8_192, - }; -} - -function expectPrimaryModel(cfg: OpenClawConfig, primary: string) { - expect(cfg.agents?.defaults?.model).toEqual({ - primary, - }); -} - -function expectPrimaryModelAlias(cfg: OpenClawConfig, modelRef: string, alias: string) { - expect(cfg.agents?.defaults?.models).toMatchObject({ - [modelRef]: { - alias, - }, - }); -} - -function expectProviderModels( - cfg: OpenClawConfig, - providerId: string, - expected: Record, -) { - const providers = cfg.models?.providers as Record | undefined; - expect(providers?.[providerId]).toMatchObject(expected); -} - -function resolveAliasObjects(aliases: Array) { - return aliases.filter( - (alias): alias is { modelRef: string; alias: string } => typeof alias !== "string", - ); -} - -function createDemoProviderParams(params?: { - providerId?: string; - baseUrl?: string; - aliases?: Array; - models?: ModelDefinitionConfig[]; -}) { - const providerId = params?.providerId ?? "demo"; - const baseUrl = params?.baseUrl ?? "https://demo.test/v1"; - const models = params?.models ?? [createModel("demo-default", "Demo Default")]; - return { - providerId, - api: "openai-completions" as const, - baseUrl, - aliases: params?.aliases ?? [ - { modelRef: `${providerId}/${models[0]?.id ?? "demo-default"}`, alias: "Demo" }, - ], - models, - }; -} - -describe("provider onboarding preset appliers", () => { - it.each([ - { - name: "creates provider and primary-model appliers for a default model preset", - kind: "default-model", - }, - { - name: "passes variant args through default-models resolvers", - kind: "default-models", - }, - { - name: "creates model-catalog appliers that preserve existing aliases", - kind: "catalog-models", - }, - ] as const)("$name", ({ kind }) => { - if (kind === "default-model") { - const params = createDemoProviderParams(); - const appliers = createDefaultModelPresetAppliers({ - primaryModelRef: "demo/demo-default", - resolveParams: () => ({ - providerId: params.providerId, - api: params.api, - baseUrl: params.baseUrl, - defaultModel: params.models[0], - defaultModelId: params.models[0]?.id ?? "demo-default", - aliases: resolveAliasObjects(params.aliases), - }), - }); - - const providerOnly = appliers.applyProviderConfig({}); - expectPrimaryModelAlias(providerOnly, "demo/demo-default", "Demo"); - expect(providerOnly.agents?.defaults?.model).toBeUndefined(); - - const withPrimary = appliers.applyConfig({}); - expectPrimaryModel(withPrimary, "demo/demo-default"); - return; - } - - if (kind === "default-models") { - const params = createDemoProviderParams({ - models: [createModel("a", "Model A"), createModel("b", "Model B")], - aliases: [{ modelRef: "demo/a", alias: "Demo A" }], - }); - const appliers = createDefaultModelsPresetAppliers<[string]>({ - primaryModelRef: "demo/a", - resolveParams: (_cfg, baseUrl) => ({ - providerId: params.providerId, - api: params.api, - baseUrl, - defaultModels: params.models, - aliases: resolveAliasObjects(params.aliases), - }), - }); - - const cfg = appliers.applyConfig({}, "https://alt.test/v1"); - expectProviderModels(cfg, "demo", { - baseUrl: "https://alt.test/v1", - models: [ - { id: "a", name: "Model A" }, - { id: "b", name: "Model B" }, - ], - }); - expectPrimaryModel(cfg, "demo/a"); - return; - } - - const params = createDemoProviderParams({ - providerId: "catalog", - baseUrl: "https://catalog.test/v1", - models: [createModel("default", "Catalog Default"), createModel("backup", "Catalog Backup")], - aliases: ["catalog/default", { modelRef: "catalog/default", alias: "Catalog Default" }], - }); - const appliers = createModelCatalogPresetAppliers({ - primaryModelRef: "catalog/default", - resolveParams: () => ({ - providerId: params.providerId, - api: params.api, - baseUrl: params.baseUrl, - catalogModels: params.models, - aliases: params.aliases, - }), - }); - - const cfg = appliers.applyConfig({ - agents: { - defaults: { - models: { - "catalog/default": { - alias: "Existing Alias", - }, - }, - }, - }, - }); - - expectPrimaryModelAlias(cfg, "catalog/default", "Existing Alias"); - expectPrimaryModel(cfg, "catalog/default"); - }); -}); diff --git a/src/plugins/provider-onboarding-config.ts b/src/plugins/provider-onboarding-config.ts deleted file mode 100644 index e8aec68a479..00000000000 --- a/src/plugins/provider-onboarding-config.ts +++ /dev/null @@ -1,413 +0,0 @@ -import { findNormalizedProviderKey } from "../agents/provider-id.js"; -import type { OpenClawConfig } from "../config/config.js"; -import type { AgentModelEntryConfig } from "../config/types.agent-defaults.js"; -import type { - ModelApi, - ModelDefinitionConfig, - ModelProviderConfig, -} from "../config/types.models.js"; - -function extractAgentDefaultModelFallbacks(model: unknown): string[] | undefined { - if (!model || typeof model !== "object") { - return undefined; - } - if (!("fallbacks" in model)) { - return undefined; - } - const fallbacks = (model as { fallbacks?: unknown }).fallbacks; - return Array.isArray(fallbacks) ? fallbacks.map((v) => String(v)) : undefined; -} - -export type AgentModelAliasEntry = - | string - | { - modelRef: string; - alias?: string; - }; - -function normalizeAgentModelAliasEntry(entry: AgentModelAliasEntry): { - modelRef: string; - alias?: string; -} { - if (typeof entry === "string") { - return { modelRef: entry }; - } - return entry; -} - -export function withAgentModelAliases( - existing: Record | undefined, - aliases: readonly AgentModelAliasEntry[], -): Record { - const next = { ...existing }; - for (const entry of aliases) { - const normalized = normalizeAgentModelAliasEntry(entry); - next[normalized.modelRef] = { - ...next[normalized.modelRef], - ...(normalized.alias ? { alias: next[normalized.modelRef]?.alias ?? normalized.alias } : {}), - }; - } - return next; -} - -export function applyOnboardAuthAgentModelsAndProviders( - cfg: OpenClawConfig, - params: { - agentModels: Record; - providers: Record; - }, -): OpenClawConfig { - return { - ...cfg, - agents: { - ...cfg.agents, - defaults: { - ...cfg.agents?.defaults, - models: params.agentModels, - }, - }, - models: { - mode: cfg.models?.mode ?? "merge", - providers: params.providers, - }, - }; -} - -export function applyAgentDefaultModelPrimary( - cfg: OpenClawConfig, - primary: string, -): OpenClawConfig { - const existingFallbacks = extractAgentDefaultModelFallbacks(cfg.agents?.defaults?.model); - return { - ...cfg, - agents: { - ...cfg.agents, - defaults: { - ...cfg.agents?.defaults, - model: { - ...(existingFallbacks ? { fallbacks: existingFallbacks } : undefined), - primary, - }, - }, - }, - }; -} - -export function applyProviderConfigWithDefaultModels( - cfg: OpenClawConfig, - params: { - agentModels: Record; - providerId: string; - api: ModelApi; - baseUrl: string; - defaultModels: ModelDefinitionConfig[]; - defaultModelId?: string; - }, -): OpenClawConfig { - const providerState = resolveProviderModelMergeState(cfg, params.providerId); - - const defaultModels = params.defaultModels; - const defaultModelId = params.defaultModelId ?? defaultModels[0]?.id; - const hasDefaultModel = defaultModelId - ? providerState.existingModels.some((model) => model.id === defaultModelId) - : true; - const mergedModels = - providerState.existingModels.length > 0 - ? hasDefaultModel || defaultModels.length === 0 - ? providerState.existingModels - : [...providerState.existingModels, ...defaultModels] - : defaultModels; - return applyProviderConfigWithMergedModels(cfg, { - agentModels: params.agentModels, - providerId: params.providerId, - providerState, - api: params.api, - baseUrl: params.baseUrl, - mergedModels, - fallbackModels: defaultModels, - }); -} - -export function applyProviderConfigWithDefaultModel( - cfg: OpenClawConfig, - params: { - agentModels: Record; - providerId: string; - api: ModelApi; - baseUrl: string; - defaultModel: ModelDefinitionConfig; - defaultModelId?: string; - }, -): OpenClawConfig { - return applyProviderConfigWithDefaultModels(cfg, { - agentModels: params.agentModels, - providerId: params.providerId, - api: params.api, - baseUrl: params.baseUrl, - defaultModels: [params.defaultModel], - defaultModelId: params.defaultModelId ?? params.defaultModel.id, - }); -} - -export function applyProviderConfigWithDefaultModelPreset( - cfg: OpenClawConfig, - params: { - providerId: string; - api: ModelApi; - baseUrl: string; - defaultModel: ModelDefinitionConfig; - defaultModelId?: string; - aliases?: readonly AgentModelAliasEntry[]; - primaryModelRef?: string; - }, -): OpenClawConfig { - const next = applyProviderConfigWithDefaultModel(cfg, { - agentModels: withAgentModelAliases(cfg.agents?.defaults?.models, params.aliases ?? []), - providerId: params.providerId, - api: params.api, - baseUrl: params.baseUrl, - defaultModel: params.defaultModel, - defaultModelId: params.defaultModelId, - }); - return params.primaryModelRef - ? applyAgentDefaultModelPrimary(next, params.primaryModelRef) - : next; -} - -export type ProviderOnboardPresetAppliers = { - applyProviderConfig: (cfg: OpenClawConfig, ...args: TArgs) => OpenClawConfig; - applyConfig: (cfg: OpenClawConfig, ...args: TArgs) => OpenClawConfig; -}; - -function createProviderPresetAppliers< - TArgs extends unknown[], - TParams extends { - primaryModelRef?: string; - }, ->(params: { - resolveParams: ( - cfg: OpenClawConfig, - ...args: TArgs - ) => Omit | null | undefined; - applyPreset: (cfg: OpenClawConfig, preset: TParams) => OpenClawConfig; - primaryModelRef: string; -}): ProviderOnboardPresetAppliers { - return { - applyProviderConfig(cfg, ...args) { - const resolved = params.resolveParams(cfg, ...args); - return resolved ? params.applyPreset(cfg, resolved as TParams) : cfg; - }, - applyConfig(cfg, ...args) { - const resolved = params.resolveParams(cfg, ...args); - if (!resolved) { - return cfg; - } - return params.applyPreset(cfg, { - ...(resolved as TParams), - primaryModelRef: params.primaryModelRef, - }); - }, - }; -} - -export function createDefaultModelPresetAppliers(params: { - resolveParams: ( - cfg: OpenClawConfig, - ...args: TArgs - ) => - | Omit[1], "primaryModelRef"> - | null - | undefined; - primaryModelRef: string; -}): ProviderOnboardPresetAppliers { - return createProviderPresetAppliers({ - resolveParams: params.resolveParams, - applyPreset: applyProviderConfigWithDefaultModelPreset, - primaryModelRef: params.primaryModelRef, - }); -} - -export function applyProviderConfigWithDefaultModelsPreset( - cfg: OpenClawConfig, - params: { - providerId: string; - api: ModelApi; - baseUrl: string; - defaultModels: ModelDefinitionConfig[]; - defaultModelId?: string; - aliases?: readonly AgentModelAliasEntry[]; - primaryModelRef?: string; - }, -): OpenClawConfig { - const next = applyProviderConfigWithDefaultModels(cfg, { - agentModels: withAgentModelAliases(cfg.agents?.defaults?.models, params.aliases ?? []), - providerId: params.providerId, - api: params.api, - baseUrl: params.baseUrl, - defaultModels: params.defaultModels, - defaultModelId: params.defaultModelId, - }); - return params.primaryModelRef - ? applyAgentDefaultModelPrimary(next, params.primaryModelRef) - : next; -} - -export function createDefaultModelsPresetAppliers(params: { - resolveParams: ( - cfg: OpenClawConfig, - ...args: TArgs - ) => - | Omit[1], "primaryModelRef"> - | null - | undefined; - primaryModelRef: string; -}): ProviderOnboardPresetAppliers { - return createProviderPresetAppliers({ - resolveParams: params.resolveParams, - applyPreset: applyProviderConfigWithDefaultModelsPreset, - primaryModelRef: params.primaryModelRef, - }); -} - -export function applyProviderConfigWithModelCatalog( - cfg: OpenClawConfig, - params: { - agentModels: Record; - providerId: string; - api: ModelApi; - baseUrl: string; - catalogModels: ModelDefinitionConfig[]; - }, -): OpenClawConfig { - const providerState = resolveProviderModelMergeState(cfg, params.providerId); - const catalogModels = params.catalogModels; - const mergedModels = - providerState.existingModels.length > 0 - ? [ - ...providerState.existingModels, - ...catalogModels.filter( - (model) => !providerState.existingModels.some((existing) => existing.id === model.id), - ), - ] - : catalogModels; - return applyProviderConfigWithMergedModels(cfg, { - agentModels: params.agentModels, - providerId: params.providerId, - providerState, - api: params.api, - baseUrl: params.baseUrl, - mergedModels, - fallbackModels: catalogModels, - }); -} - -export function applyProviderConfigWithModelCatalogPreset( - cfg: OpenClawConfig, - params: { - providerId: string; - api: ModelApi; - baseUrl: string; - catalogModels: ModelDefinitionConfig[]; - aliases?: readonly AgentModelAliasEntry[]; - primaryModelRef?: string; - }, -): OpenClawConfig { - const next = applyProviderConfigWithModelCatalog(cfg, { - agentModels: withAgentModelAliases(cfg.agents?.defaults?.models, params.aliases ?? []), - providerId: params.providerId, - api: params.api, - baseUrl: params.baseUrl, - catalogModels: params.catalogModels, - }); - return params.primaryModelRef - ? applyAgentDefaultModelPrimary(next, params.primaryModelRef) - : next; -} - -export function createModelCatalogPresetAppliers(params: { - resolveParams: ( - cfg: OpenClawConfig, - ...args: TArgs - ) => - | Omit[1], "primaryModelRef"> - | null - | undefined; - primaryModelRef: string; -}): ProviderOnboardPresetAppliers { - return createProviderPresetAppliers({ - resolveParams: params.resolveParams, - applyPreset: applyProviderConfigWithModelCatalogPreset, - primaryModelRef: params.primaryModelRef, - }); -} - -type ProviderModelMergeState = { - providers: Record; - existingProvider?: ModelProviderConfig; - existingModels: ModelDefinitionConfig[]; -}; - -function resolveProviderModelMergeState( - cfg: OpenClawConfig, - providerId: string, -): ProviderModelMergeState { - const providers = { ...cfg.models?.providers } as Record; - const existingProviderKey = findNormalizedProviderKey(providers, providerId); - const existingProvider = - existingProviderKey !== undefined - ? (providers[existingProviderKey] as ModelProviderConfig | undefined) - : undefined; - const existingModels: ModelDefinitionConfig[] = Array.isArray(existingProvider?.models) - ? existingProvider.models - : []; - if (existingProviderKey && existingProviderKey !== providerId) { - delete providers[existingProviderKey]; - } - return { providers, existingProvider, existingModels }; -} - -function applyProviderConfigWithMergedModels( - cfg: OpenClawConfig, - params: { - agentModels: Record; - providerId: string; - providerState: ProviderModelMergeState; - api: ModelApi; - baseUrl: string; - mergedModels: ModelDefinitionConfig[]; - fallbackModels: ModelDefinitionConfig[]; - }, -): OpenClawConfig { - params.providerState.providers[params.providerId] = buildProviderConfig({ - existingProvider: params.providerState.existingProvider, - api: params.api, - baseUrl: params.baseUrl, - mergedModels: params.mergedModels, - fallbackModels: params.fallbackModels, - }); - return applyOnboardAuthAgentModelsAndProviders(cfg, { - agentModels: params.agentModels, - providers: params.providerState.providers, - }); -} - -function buildProviderConfig(params: { - existingProvider: ModelProviderConfig | undefined; - api: ModelApi; - baseUrl: string; - mergedModels: ModelDefinitionConfig[]; - fallbackModels: ModelDefinitionConfig[]; -}): ModelProviderConfig { - const { apiKey: existingApiKey, ...existingProviderRest } = (params.existingProvider ?? {}) as { - apiKey?: string; - }; - const normalizedApiKey = typeof existingApiKey === "string" ? existingApiKey.trim() : undefined; - - return { - ...existingProviderRest, - baseUrl: params.baseUrl, - api: params.api, - ...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}), - models: params.mergedModels.length > 0 ? params.mergedModels : params.fallbackModels, - }; -} diff --git a/src/qa-e2e/report.ts b/src/qa-e2e/report.ts deleted file mode 100644 index 57d9d5c96c8..00000000000 --- a/src/qa-e2e/report.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "../../extensions/qa-lab/api.js"; diff --git a/src/qa-e2e/scenario.ts b/src/qa-e2e/scenario.ts deleted file mode 100644 index 57d9d5c96c8..00000000000 --- a/src/qa-e2e/scenario.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "../../extensions/qa-lab/api.js"; diff --git a/src/security/audit-extra.ts b/src/security/audit-extra.ts deleted file mode 100644 index 90fcc0c6bf3..00000000000 --- a/src/security/audit-extra.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Re-export barrel for security audit collector functions. - * - * Maintains backward compatibility with existing imports from audit-extra. - * Implementation split into: - * - audit-extra.sync.ts: Config-based checks (no I/O) - * - audit-extra.async.ts: Filesystem/plugin checks (async I/O) - */ - -// Sync collectors -export { - collectAttackSurfaceSummaryFindings, - collectExposureMatrixFindings, - collectGatewayHttpNoAuthFindings, - collectGatewayHttpSessionKeyOverrideFindings, - collectHooksHardeningFindings, - collectLikelyMultiUserSetupFindings, - collectMinimalProfileOverrideFindings, - collectModelHygieneFindings, - collectNodeDangerousAllowCommandFindings, - collectNodeDenyCommandPatternFindings, - collectSandboxDangerousConfigFindings, - collectSandboxDockerNoopFindings, - collectSecretsInConfigFindings, - collectSmallModelRiskFindings, - collectSyncedFolderFindings, - type SecurityAuditFinding, -} from "./audit-extra.sync.js"; - -// Async collectors -export { - collectSandboxBrowserHashLabelFindings, - collectIncludeFilePermFindings, - collectInstalledSkillsCodeSafetyFindings, - collectPluginsCodeSafetyFindings, - collectPluginsTrustFindings, - collectStateDeepFilesystemFindings, - collectWorkspaceSkillSymlinkEscapeFindings, - readConfigSnapshotForAudit, -} from "./audit-extra.async.js"; diff --git a/src/tts/runtime.ts b/src/tts/runtime.ts deleted file mode 100644 index 2235a1124e0..00000000000 --- a/src/tts/runtime.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Shared runtime-facing speech helpers. Keep channel/feature plugins on this -// boundary instead of importing the full TTS orchestrator module directly. - -export { listSpeechVoices, textToSpeech, textToSpeechTelephony } from "./tts.js";