Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
264
extensions/clawrouter/index.test.ts
Normal file
264
extensions/clawrouter/index.test.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
||||
import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-runtime";
|
||||
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const providerAuthRuntimeMocks = vi.hoisted(() => ({
|
||||
resolveApiKeyForProvider: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/provider-auth-runtime", () => providerAuthRuntimeMocks);
|
||||
|
||||
import plugin from "./index.js";
|
||||
|
||||
const LIVE_CATALOG = {
|
||||
providers: [
|
||||
{
|
||||
id: "openai",
|
||||
displayName: "OpenAI",
|
||||
openaiCompatible: true,
|
||||
nativeBaseUrl: "/v1/native/openai",
|
||||
routes: [],
|
||||
models: [
|
||||
{
|
||||
id: "openai/gpt-5.5",
|
||||
upstream: "gpt-5.5",
|
||||
capabilities: ["llm.responses"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe("ClawRouter plugin", () => {
|
||||
beforeEach(() => {
|
||||
clearLiveCatalogCacheForTests();
|
||||
providerAuthRuntimeMocks.resolveApiKeyForProvider.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("registers catalog, transport compatibility, and quota hooks", async () => {
|
||||
const provider = await registerSingleProviderPlugin(plugin);
|
||||
|
||||
expect(provider).toMatchObject({
|
||||
id: "clawrouter",
|
||||
label: "ClawRouter",
|
||||
docsPath: "/providers/clawrouter",
|
||||
envVars: ["CLAWROUTER_API_KEY"],
|
||||
buildReplayPolicy: expect.any(Function),
|
||||
fetchUsageSnapshot: expect.any(Function),
|
||||
inspectToolSchemas: expect.any(Function),
|
||||
normalizeResolvedModel: expect.any(Function),
|
||||
normalizeToolSchemas: expect.any(Function),
|
||||
prepareDynamicModel: expect.any(Function),
|
||||
resolveDynamicModel: expect.any(Function),
|
||||
resolveUsageAuth: expect.any(Function),
|
||||
sanitizeReplayHistory: expect.any(Function),
|
||||
wrapSimpleCompletionStreamFn: expect.any(Function),
|
||||
wrapStreamFn: expect.any(Function),
|
||||
});
|
||||
expect(provider?.auth[0]).toMatchObject({
|
||||
id: "api-key",
|
||||
label: "ClawRouter proxy key",
|
||||
kind: "api_key",
|
||||
});
|
||||
expect(provider?.wrapSimpleCompletionStreamFn).toBe(provider?.wrapStreamFn);
|
||||
});
|
||||
|
||||
it("attaches the proxy key and native upstream id only at request dispatch", async () => {
|
||||
const provider = await registerSingleProviderPlugin(plugin);
|
||||
const calls: Array<Parameters<StreamFn>[0]> = [];
|
||||
const baseStreamFn: StreamFn = (model) => {
|
||||
calls.push(model);
|
||||
return {} as ReturnType<StreamFn>;
|
||||
};
|
||||
const wrapped = provider?.wrapStreamFn?.({
|
||||
provider: "clawrouter",
|
||||
modelId: "anthropic/claude-sonnet-4-6",
|
||||
streamFn: baseStreamFn,
|
||||
} as never);
|
||||
|
||||
void wrapped?.(
|
||||
{
|
||||
provider: "clawrouter",
|
||||
api: "anthropic-messages",
|
||||
id: "anthropic/claude-sonnet-4-6",
|
||||
headers: { "X-Request-ID": "request-1" },
|
||||
params: {
|
||||
clawrouterRoute: {
|
||||
api: "anthropic-messages",
|
||||
baseUrl: "https://clawrouter.example/v1/native/anthropic",
|
||||
upstreamModel: "claude-sonnet-4-6",
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
{} as never,
|
||||
{ apiKey: "runtime-proxy-key" } as never,
|
||||
);
|
||||
|
||||
expect(calls[0]?.headers).toEqual({
|
||||
"X-Request-ID": "request-1",
|
||||
Authorization: "Bearer runtime-proxy-key",
|
||||
});
|
||||
expect(calls[0]?.id).toBe("claude-sonnet-4-6");
|
||||
expect(calls[0]?.params).toBeUndefined();
|
||||
});
|
||||
|
||||
it("resolves managed secret refs before scoped discovery", async () => {
|
||||
providerAuthRuntimeMocks.resolveApiKeyForProvider.mockResolvedValue({
|
||||
apiKey: "resolved-proxy-key",
|
||||
mode: "api-key",
|
||||
source: "models.json secretref",
|
||||
});
|
||||
const fetchMock = vi.fn(async () => Response.json(LIVE_CATALOG));
|
||||
vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch);
|
||||
const provider = await registerSingleProviderPlugin(plugin);
|
||||
|
||||
const result = await provider?.catalog?.run({
|
||||
config: { models: {} },
|
||||
agentDir: "/agent",
|
||||
workspaceDir: "/workspace",
|
||||
env: {},
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "secretref-managed",
|
||||
discoveryApiKey: undefined,
|
||||
mode: "api_key",
|
||||
source: "profile",
|
||||
profileId: "clawrouter-profile",
|
||||
}),
|
||||
resolveProviderApiKey: () => ({
|
||||
apiKey: "secretref-managed",
|
||||
discoveryApiKey: undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!result || !("provider" in result)) {
|
||||
throw new Error("expected ClawRouter catalog provider result");
|
||||
}
|
||||
expect(result.provider.apiKey).toBe("secretref-managed");
|
||||
expect(result.provider.models.map((model) => model.id)).toEqual(["openai/gpt-5.5"]);
|
||||
expect(providerAuthRuntimeMocks.resolveApiKeyForProvider).toHaveBeenCalledWith({
|
||||
provider: "clawrouter",
|
||||
cfg: { models: {} },
|
||||
agentDir: "/agent",
|
||||
workspaceDir: "/workspace",
|
||||
profileId: "clawrouter-profile",
|
||||
lockedProfile: true,
|
||||
});
|
||||
const fetchCall = fetchMock.mock.calls[0] as unknown as [string, RequestInit] | undefined;
|
||||
expect(new Headers(fetchCall?.[1]?.headers).get("Authorization")).toBe(
|
||||
"Bearer resolved-proxy-key",
|
||||
);
|
||||
});
|
||||
|
||||
it("surfaces catalog authentication failures", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => new Response("Unauthorized", { status: 401 })),
|
||||
);
|
||||
const provider = await registerSingleProviderPlugin(plugin);
|
||||
|
||||
await expect(
|
||||
provider?.catalog?.run({
|
||||
config: { models: {} },
|
||||
env: { CLAWROUTER_API_KEY: "invalid-proxy-key" },
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "invalid-proxy-key",
|
||||
discoveryApiKey: "invalid-proxy-key",
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
resolveProviderApiKey: () => ({
|
||||
apiKey: "invalid-proxy-key",
|
||||
discoveryApiKey: "invalid-proxy-key",
|
||||
}),
|
||||
}),
|
||||
).rejects.toThrow(/401/u);
|
||||
});
|
||||
|
||||
it("resolves configured catalog models through a stored auth profile", async () => {
|
||||
providerAuthRuntimeMocks.resolveApiKeyForProvider.mockResolvedValue({
|
||||
apiKey: "resolved-proxy-key",
|
||||
mode: "api-key",
|
||||
source: "auth profile",
|
||||
});
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => Response.json(LIVE_CATALOG)),
|
||||
);
|
||||
const provider = await registerSingleProviderPlugin(plugin);
|
||||
const context = {
|
||||
config: { models: {} },
|
||||
agentDir: "/agent",
|
||||
workspaceDir: "/workspace",
|
||||
provider: "clawrouter",
|
||||
modelId: "openai/gpt-5.5",
|
||||
modelRegistry: { find: vi.fn(() => null) },
|
||||
authProfileId: "clawrouter-profile",
|
||||
authProfileMode: "api_key",
|
||||
};
|
||||
|
||||
expect(provider?.resolveDynamicModel?.(context as never)).toBeUndefined();
|
||||
await provider?.prepareDynamicModel?.(context as never);
|
||||
|
||||
expect(provider?.resolveDynamicModel?.(context as never)).toMatchObject({
|
||||
id: "openai/gpt-5.5",
|
||||
provider: "clawrouter",
|
||||
api: "openai-responses",
|
||||
baseUrl: "https://clawrouter.openclaw.ai/v1",
|
||||
params: {
|
||||
clawrouterRoute: {
|
||||
api: "openai-responses",
|
||||
baseUrl: "https://clawrouter.openclaw.ai/v1",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(providerAuthRuntimeMocks.resolveApiKeyForProvider).toHaveBeenCalledWith({
|
||||
provider: "clawrouter",
|
||||
cfg: { models: {} },
|
||||
agentDir: "/agent",
|
||||
workspaceDir: "/workspace",
|
||||
profileId: "clawrouter-profile",
|
||||
lockedProfile: true,
|
||||
});
|
||||
expect(
|
||||
provider?.resolveDynamicModel?.({
|
||||
...context,
|
||||
authProfileId: "another-profile",
|
||||
} as never),
|
||||
).toBeUndefined();
|
||||
|
||||
providerAuthRuntimeMocks.resolveApiKeyForProvider.mockResolvedValue(undefined);
|
||||
await provider?.prepareDynamicModel?.(context as never);
|
||||
expect(provider?.resolveDynamicModel?.(context as never)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("dispatches replay and tool policies by upstream protocol family", async () => {
|
||||
const provider = await registerSingleProviderPlugin(plugin);
|
||||
|
||||
expect(
|
||||
provider?.buildReplayPolicy?.({
|
||||
provider: "clawrouter",
|
||||
modelApi: "anthropic-messages",
|
||||
modelId: "anthropic/claude-sonnet-4-6",
|
||||
} as never),
|
||||
).toMatchObject({ preserveNativeAnthropicToolUseIds: true, validateAnthropicTurns: true });
|
||||
expect(
|
||||
provider?.buildReplayPolicy?.({
|
||||
provider: "clawrouter",
|
||||
modelApi: "google-generative-ai",
|
||||
modelId: "google/gemini-3.5-flash",
|
||||
} as never),
|
||||
).toMatchObject({ validateGeminiTurns: true });
|
||||
expect(
|
||||
provider?.buildReplayPolicy?.({
|
||||
provider: "clawrouter",
|
||||
modelApi: "openai-completions",
|
||||
modelId: "deepseek/deepseek-v4-flash",
|
||||
} as never),
|
||||
).toMatchObject({ sanitizeToolCallIds: true });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user