mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
improve: reduce repeated provider auth lookup work (#151570)
* improve: reduce repeated provider auth lookup work * test: complete provider auth metadata fixtures * fix: preserve released provider env snapshot inputs * fix: distinguish provider env core implementations * test: align image provider env mock with core export
This commit is contained in:
parent
f81c155d02
commit
fdbab4ce99
78 changed files with 493 additions and 312 deletions
|
|
@ -11,7 +11,7 @@ import {
|
|||
resolveWindowsSpawnProgram,
|
||||
} from "../plugin-sdk/windows-spawn.js";
|
||||
import {
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
listKnownProviderAuthEnvVarNamesCore,
|
||||
omitEnvKeysCaseInsensitive,
|
||||
} from "../secrets/provider-env-vars.js";
|
||||
import { classifyAcpToolApproval, type AcpApprovalClass } from "./approval-classifier.js";
|
||||
|
|
@ -198,7 +198,7 @@ export function buildAcpClientStripKeys(params: {
|
|||
}): Set<string> {
|
||||
const stripKeys = new Set<string>(params.activeSkillEnvKeys ?? []);
|
||||
if (params.stripProviderAuthEnvVars) {
|
||||
for (const key of listKnownProviderAuthEnvVarNames()) {
|
||||
for (const key of listKnownProviderAuthEnvVarNamesCore()) {
|
||||
stripKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
|||
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
|
||||
|
||||
vi.mock("../secrets/provider-env-vars.js", () => ({
|
||||
listKnownProviderAuthEnvVarNames: () => [
|
||||
listKnownProviderAuthEnvVarNamesCore: () => [
|
||||
"OPENAI_API_KEY",
|
||||
"OPENAI_ADMIN_KEY",
|
||||
"ANTHROPIC_ADMIN_KEY",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ function buildOwners(): PluginMetadataSnapshotOwnerMaps {
|
|||
setupProviders: empty,
|
||||
commandAliases: empty,
|
||||
contracts: empty,
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
providerEndpoints: [
|
||||
{ endpointClass: "openai-public", hosts: ["prepared.example"] },
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export const emptyPluginMetadataSnapshot: PluginMetadataSnapshot = {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
/**
|
||||
* Test harness mocks for embedded-run overflow compaction coverage.
|
||||
*/
|
||||
|
||||
import { matchesContextOverflowMessage } from "@openclaw/ai/internal/runtime";
|
||||
import { type Mock, vi } from "vitest";
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import type { ContextEngine, ContextEngineSessionTarget } from "../../context-engine/types.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
import { makeEmptyPluginMetadataOwners } from "../../plugins/current-plugin-metadata.test-support.js";
|
||||
import type {
|
||||
PluginHookBeforeAgentFinalizeEvent,
|
||||
PluginHookBeforeAgentFinalizeResult,
|
||||
|
|
@ -98,17 +100,7 @@ const emptyPluginMetadataSnapshot: PluginMetadataSnapshot = {
|
|||
byPluginId: new Map(),
|
||||
normalizePluginId: (pluginId: string) => pluginId,
|
||||
declaredProviderOwners: new Map(),
|
||||
owners: {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
providers: new Map(),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map(),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
owners: makeEmptyPluginMetadataOwners(),
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// Shared harness and mocks for embedded attempt spawn-workspace tests.
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
|
@ -20,6 +19,8 @@ import type {
|
|||
import { formatErrorMessage } from "../../../infra/errors.js";
|
||||
import { bindStreamLlmRuntime } from "../../../llm/model-runtime-binding.js";
|
||||
import type { Model } from "../../../llm/types.js";
|
||||
// Shared harness and mocks for embedded attempt spawn-workspace tests.
|
||||
import { makeEmptyPluginMetadataOwners } from "../../../plugins/current-plugin-metadata.test-support.js";
|
||||
import type { PluginMetadataSnapshot } from "../../../plugins/plugin-metadata-snapshot.js";
|
||||
import { createLazyPromise } from "../../../shared/lazy-runtime.js";
|
||||
import { prepareSystemAgentRunAdmission } from "../../admitted-run-context.js";
|
||||
|
|
@ -287,17 +288,7 @@ const emptyPluginMetadataSnapshot: PluginMetadataSnapshot = {
|
|||
byPluginId: new Map(),
|
||||
normalizePluginId: (pluginId: string) => pluginId,
|
||||
declaredProviderOwners: new Map(),
|
||||
owners: {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
providers: new Map(),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map(),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
owners: makeEmptyPluginMetadataOwners(),
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import { normalizeStringEntries } from "@openclaw/normalization-core/string-normalization";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import { classifyFailoverSignal } from "./failover/classify.js";
|
||||
|
||||
const KEY_SPLIT_RE = /[\s,;]+/g;
|
||||
|
|
@ -132,7 +132,7 @@ export function collectProviderApiKeys(
|
|||
const fallback = config.fallbackVars
|
||||
.map((envVar) => normalizeOptionalString(env[envVar]))
|
||||
.filter(Boolean) as string[];
|
||||
const manifestEnvVars = options.providerEnvVars ?? getProviderEnvVars(normalizedProvider);
|
||||
const manifestEnvVars = options.providerEnvVars ?? getProviderEnvVarsCore(normalizedProvider);
|
||||
const manifestFallback = manifestEnvVars
|
||||
.map((envVar) => normalizeOptionalString(env[envVar]))
|
||||
.filter(Boolean) as string[];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { SecretRef } from "../config/types.secrets.js";
|
||||
import type { ProviderModelRouteCandidate } from "../plugin-sdk/provider-model-types.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import { createPluginMetadataSnapshotFixture } from "../plugins/plugin-metadata.test-support.js";
|
||||
import { createApiKeyCredential } from "./auth-profiles/credential-fixtures.test-support.js";
|
||||
import { createModelAuthAvailabilityResolver } from "./model-auth-availability.js";
|
||||
import {
|
||||
|
|
@ -52,17 +52,7 @@ describe("createModelAuthAvailabilityResolver", () => {
|
|||
});
|
||||
|
||||
it("canonicalizes prepared runtime auth through provider aliases", () => {
|
||||
const metadataSnapshot = {
|
||||
index: {
|
||||
plugins: [
|
||||
{
|
||||
pluginId: "external-cloud",
|
||||
origin: "global",
|
||||
enabled: true,
|
||||
enabledByDefault: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
const metadataSnapshot = createPluginMetadataSnapshotFixture({
|
||||
plugins: [
|
||||
{
|
||||
id: "external-cloud",
|
||||
|
|
@ -70,7 +60,7 @@ describe("createModelAuthAvailabilityResolver", () => {
|
|||
providerAuthAliases: { "cloud-alias": "external-cloud" },
|
||||
},
|
||||
],
|
||||
} as unknown as PluginMetadataSnapshot;
|
||||
});
|
||||
const resolver = createModelAuthAvailabilityResolver({
|
||||
cfg: {},
|
||||
authStore: authStore(),
|
||||
|
|
@ -103,8 +93,7 @@ describe("createModelAuthAvailabilityResolver", () => {
|
|||
});
|
||||
|
||||
it("keeps prepared native-runtime authentication scoped to its exact owner", () => {
|
||||
const metadataSnapshot = {
|
||||
index: { plugins: [] },
|
||||
const metadataSnapshot = createPluginMetadataSnapshotFixture({
|
||||
plugins: [
|
||||
{
|
||||
id: "anthropic",
|
||||
|
|
@ -112,7 +101,7 @@ describe("createModelAuthAvailabilityResolver", () => {
|
|||
providerAuthAliases: { "claude-cli": "anthropic" },
|
||||
},
|
||||
],
|
||||
} as unknown as PluginMetadataSnapshot;
|
||||
});
|
||||
const resolver = createModelAuthAvailabilityResolver({
|
||||
cfg: {},
|
||||
authStore: authStore(),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* helper names to model/auth modules.
|
||||
*/
|
||||
import {
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
listKnownProviderAuthEnvVarNamesCore,
|
||||
resolveProviderAuthLookupMaps,
|
||||
} from "../secrets/provider-env-vars.js";
|
||||
import type {
|
||||
|
|
@ -33,5 +33,5 @@ export function listProviderEnvAuthLookupKeys(params: {
|
|||
|
||||
/** Lists known provider API-key env var names for redaction and marker matching. */
|
||||
export function listKnownProviderEnvApiKeyNames(): string[] {
|
||||
return listKnownProviderAuthEnvVarNames();
|
||||
return listKnownProviderAuthEnvVarNamesCore();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,28 @@
|
|||
// Verifies env API-key lookup through plugin provider-auth aliases.
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createPluginMetadataSnapshotFixture } from "../plugins/plugin-metadata.test-support.js";
|
||||
import {
|
||||
resolveEnvApiKey,
|
||||
resolveProviderDirectAuthPlanningEvidence,
|
||||
resolveProviderEnvAuthEvidence,
|
||||
} from "./model-auth-env.js";
|
||||
|
||||
const pluginMetadataMocks = vi.hoisted(() => {
|
||||
const snapshot = {
|
||||
index: {
|
||||
plugins: [
|
||||
{
|
||||
pluginId: "external-cloud",
|
||||
origin: "global",
|
||||
enabled: true,
|
||||
enabledByDefault: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
id: "external-cloud",
|
||||
origin: "global",
|
||||
providerAuthAliases: {
|
||||
"cloud-alias": "external-cloud",
|
||||
},
|
||||
setup: {
|
||||
providers: [{ id: "external-cloud", envVars: ["EXTERNAL_CLOUD_API_KEY"] }],
|
||||
},
|
||||
const pluginMetadataMocks = vi.hoisted(() => ({
|
||||
getCurrentPluginMetadataSnapshot: vi.fn(),
|
||||
loadPluginMetadataSnapshot: vi.fn(),
|
||||
}));
|
||||
|
||||
const snapshot = createPluginMetadataSnapshotFixture({
|
||||
plugins: [
|
||||
{
|
||||
id: "external-cloud",
|
||||
origin: "global",
|
||||
providerAuthAliases: { "cloud-alias": "external-cloud" },
|
||||
setup: {
|
||||
providers: [{ id: "external-cloud", envVars: ["EXTERNAL_CLOUD_API_KEY"] }],
|
||||
},
|
||||
],
|
||||
};
|
||||
return {
|
||||
snapshot,
|
||||
getCurrentPluginMetadataSnapshot: vi.fn(() => snapshot),
|
||||
loadPluginMetadataSnapshot: vi.fn(() => snapshot),
|
||||
};
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const setupRegistryMocks = vi.hoisted(() => ({
|
||||
|
|
@ -47,7 +34,8 @@ vi.mock("../plugins/current-plugin-metadata-snapshot.js", async (importOriginal)
|
|||
getCurrentPluginMetadataSnapshot: pluginMetadataMocks.getCurrentPluginMetadataSnapshot,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("../plugins/plugin-metadata-snapshot.js")>()),
|
||||
loadPluginMetadataSnapshot: pluginMetadataMocks.loadPluginMetadataSnapshot,
|
||||
}));
|
||||
|
||||
|
|
@ -58,11 +46,9 @@ vi.mock("../plugins/setup-registry.js", () => ({
|
|||
describe("resolveEnvApiKey provider auth aliases", () => {
|
||||
beforeEach(() => {
|
||||
pluginMetadataMocks.getCurrentPluginMetadataSnapshot.mockReset();
|
||||
pluginMetadataMocks.getCurrentPluginMetadataSnapshot.mockReturnValue(
|
||||
pluginMetadataMocks.snapshot,
|
||||
);
|
||||
pluginMetadataMocks.getCurrentPluginMetadataSnapshot.mockReturnValue(snapshot);
|
||||
pluginMetadataMocks.loadPluginMetadataSnapshot.mockReset();
|
||||
pluginMetadataMocks.loadPluginMetadataSnapshot.mockReturnValue(pluginMetadataMocks.snapshot);
|
||||
pluginMetadataMocks.loadPluginMetadataSnapshot.mockReturnValue(snapshot);
|
||||
setupRegistryMocks.resolvePluginSetupProviderCore.mockReset();
|
||||
setupRegistryMocks.resolvePluginSetupProviderCore.mockReturnValue(undefined);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ const emptyPluginMetadataSnapshot = vi.hoisted(() => ({
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ function metadataOwners(
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
...overrides,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const mocks = vi.hoisted(() => {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ const preparedModelRuntimeMocks = vi.hoisted(() => ({
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,20 +4,21 @@ import { describe, expect, it, vi } from "vitest";
|
|||
// Simulates a built dist tree: externalized provider metadata comes from the
|
||||
// catalog, while one installed manifest proves first-match precedence.
|
||||
vi.mock("../plugins/plugin-metadata-snapshot-required.js", async (importOriginal) => {
|
||||
const { buildPluginMetadataProviderFacts } =
|
||||
await import("../plugins/plugin-metadata-provider-facts.js");
|
||||
const { createPluginMetadataSnapshotFixture } =
|
||||
await import("../plugins/plugin-metadata.test-support.js");
|
||||
return {
|
||||
...(await importOriginal<typeof import("../plugins/plugin-metadata-snapshot-required.js")>()),
|
||||
getCurrentPluginMetadataSnapshotRequiredRuntime: () => ({
|
||||
owners: buildPluginMetadataProviderFacts([
|
||||
{
|
||||
id: "installed-conflict-fixture",
|
||||
providerEndpoints: [
|
||||
{ endpointClass: "openai-public", hosts: ["coding.dashscope.aliyuncs.com"] },
|
||||
],
|
||||
} as never,
|
||||
]),
|
||||
}),
|
||||
getCurrentPluginMetadataSnapshotRequiredRuntime: () =>
|
||||
createPluginMetadataSnapshotFixture({
|
||||
plugins: [
|
||||
{
|
||||
id: "installed-conflict-fixture",
|
||||
providerEndpoints: [
|
||||
{ endpointClass: "openai-public", hosts: ["coding.dashscope.aliyuncs.com"] },
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// Verifies provider attribution headers and endpoint classification policies.
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
// Verifies provider attribution headers and endpoint classification policies.
|
||||
import { makeEmptyPluginMetadataOwners } from "../plugins/current-plugin-metadata.test-support.js";
|
||||
|
||||
function expectRecordFields(record: unknown, expected: Record<string, unknown>) {
|
||||
// Policy helpers return broad records; assertions pin only the relevant fields.
|
||||
|
|
@ -326,15 +327,7 @@ describe("provider attribution", () => {
|
|||
providerMetadataState.pluginIdScoped = true;
|
||||
providerMetadataState.snapshot = undefined;
|
||||
const providerMetadataOwners = {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
providers: new Map(),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map(),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
...makeEmptyPluginMetadataOwners(),
|
||||
providerEndpoints: [
|
||||
{
|
||||
endpointClass: "anthropic-public" as const,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
* Verifies plugin metadata aliases, origin priority, trust, and cache behavior.
|
||||
*/
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildPluginMetadataProviderFacts } from "../plugins/plugin-metadata-provider-facts.js";
|
||||
import { buildDeclaredProviderOwnerIndex } from "../plugins/provider-owner-index.js";
|
||||
import { resolveProviderAuthLookupMaps } from "../secrets/provider-env-vars.js";
|
||||
|
||||
const pluginRegistryMocks = vi.hoisted(() => {
|
||||
const loadManifestRegistry = vi.fn();
|
||||
|
|
@ -14,17 +16,12 @@ const pluginRegistryMocks = vi.hoisted(() => {
|
|||
resolveInstalledManifestRegistryIndexFingerprint: vi.fn(() => "test-index"),
|
||||
loadPluginMetadataSnapshot: vi.fn((params: unknown) => {
|
||||
const registry = loadManifestRegistry(params) ?? { plugins: [], diagnostics: [] };
|
||||
return {
|
||||
index: {
|
||||
plugins: registry.plugins.map((plugin: { id: string; origin?: string }) => ({
|
||||
pluginId: plugin.id,
|
||||
origin: plugin.origin ?? "global",
|
||||
enabled: true,
|
||||
enabledByDefault: true,
|
||||
})),
|
||||
},
|
||||
plugins: registry.plugins,
|
||||
};
|
||||
return createPluginMetadataSnapshot({
|
||||
plugins: registry.plugins.map(
|
||||
(plugin: Partial<PluginManifestRecord> & Pick<PluginManifestRecord, "id">) =>
|
||||
createPluginManifestRecord({ ...plugin, origin: plugin.origin ?? "global" }),
|
||||
),
|
||||
});
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
|
@ -135,7 +132,10 @@ function createPluginMetadataSnapshot(params: {
|
|||
byPluginId: new Map(params.plugins.map((plugin) => [plugin.id, plugin])),
|
||||
normalizePluginId: (pluginId) => pluginId,
|
||||
declaredProviderOwners: buildDeclaredProviderOwnerIndex(params.plugins),
|
||||
owners: makeEmptyPluginMetadataOwners(),
|
||||
owners: {
|
||||
...makeEmptyPluginMetadataOwners(),
|
||||
...buildPluginMetadataProviderFacts(params.plugins),
|
||||
},
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
|
|
@ -457,12 +457,13 @@ describe("provider auth aliases", () => {
|
|||
expect(resolveProviderIdForAuth("added", { metadataSnapshot })).toBe("added-provider");
|
||||
});
|
||||
|
||||
it("retains alias ownership through worker cloning, projection and metadata replacement", async () => {
|
||||
it("retains auth contributions through worker cloning, projection and metadata replacement", async () => {
|
||||
const { metadata, snapshot } = await prepareAliasSnapshot([
|
||||
createPluginManifestRecord({
|
||||
id: "first",
|
||||
origin: "bundled",
|
||||
providerAuthAliases: { fixture: "first-provider" },
|
||||
setup: { providers: [{ id: "first-provider", envVars: ["FIRST_API_KEY"] }] },
|
||||
}),
|
||||
createPluginManifestRecord({
|
||||
id: "second",
|
||||
|
|
@ -476,7 +477,14 @@ describe("provider auth aliases", () => {
|
|||
fixture: "first-provider",
|
||||
second: "second-provider",
|
||||
});
|
||||
expect(
|
||||
resolveProviderAuthLookupMaps({ metadataSnapshot: restored }).envCandidateMap.fixture,
|
||||
).toEqual(["FIRST_API_KEY"]);
|
||||
expect(Object.isFrozen(restored.owners.providerAuthContributions)).toBe(true);
|
||||
const projected = metadata.projectPluginMetadataSnapshot(restored, ["second"]);
|
||||
expect(
|
||||
resolveProviderAuthLookupMaps({ metadataSnapshot: projected }).envCandidateMap.fixture,
|
||||
).toBeUndefined();
|
||||
expect(resolveProviderIdForAuth("fixture", { metadataSnapshot: projected })).toBe("fixture");
|
||||
expect(resolveProviderIdForAuth("second", { metadataSnapshot: projected })).toBe(
|
||||
"second-provider",
|
||||
|
|
@ -487,6 +495,7 @@ describe("provider auth aliases", () => {
|
|||
id: "first",
|
||||
origin: "bundled",
|
||||
providerAuthAliases: { fixture: "replacement-provider" },
|
||||
setup: { providers: [{ id: "replacement-provider", envVars: ["REPLACED_API_KEY"] }] },
|
||||
}),
|
||||
],
|
||||
diagnostics: [],
|
||||
|
|
@ -494,6 +503,12 @@ describe("provider auth aliases", () => {
|
|||
expect(resolveProviderIdForAuth("fixture", { metadataSnapshot: replacement })).toBe(
|
||||
"replacement-provider",
|
||||
);
|
||||
expect(
|
||||
resolveProviderAuthLookupMaps({ metadataSnapshot: replacement }).envCandidateMap.fixture,
|
||||
).toEqual(["REPLACED_API_KEY"]);
|
||||
expect(
|
||||
resolveProviderAuthLookupMaps({ metadataSnapshot: restored }).envCandidateMap.fixture,
|
||||
).toEqual(["FIRST_API_KEY"]);
|
||||
expect(resolveProviderIdForAuth("fixture", { metadataSnapshot: restored })).toBe(
|
||||
"first-provider",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ function buildProviderMetadataOwners(
|
|||
setupProviders: empty,
|
||||
commandAliases: empty,
|
||||
contracts: empty,
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
providerEndpoints: endpoints,
|
||||
providerRequests: requests,
|
||||
|
|
@ -50,6 +51,7 @@ describe("provider request config", () => {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
providerEndpoints: [],
|
||||
providerRequests: new Map([["prepared", { family: "prepared-family" }]]),
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ function createRegistry(
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ export function pluginOwnerSnapshotEntries(
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export function createEmptyPluginMetadataSnapshot(workspaceDir?: string): Plugin
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ describe("createImageGenerateTool", () => {
|
|||
);
|
||||
return {
|
||||
...actual,
|
||||
getProviderEnvVars: (providerId: string) => {
|
||||
getProviderEnvVarsCore: (providerId: string) => {
|
||||
if (providerId === "google") {
|
||||
return ["GEMINI_API_KEY", "GOOGLE_API_KEY"];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
type MediaGenerationCatalogKind,
|
||||
} from "../../../packages/media-generation-core/src/catalog.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { getProviderEnvVars } from "../../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../../secrets/provider-env-vars.js";
|
||||
import type { AuthProfileStore } from "../auth-profiles/types.js";
|
||||
import { isCapabilityProviderConfigured } from "./media-tool-shared.js";
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ export function createMediaGenerateProviderListActionResult<
|
|||
agentDir: params.agentDir,
|
||||
authStore: params.authStore,
|
||||
}),
|
||||
authEnvVars: getProviderEnvVars(provider.id),
|
||||
authEnvVars: getProviderEnvVarsCore(provider.id),
|
||||
capabilities: provider.capabilities,
|
||||
// Catalog entries are generated for model browser/search without invoking provider code.
|
||||
catalog: synthesizeMediaGenerationCatalogEntries({
|
||||
|
|
@ -116,7 +116,7 @@ export function createMediaGenerateProviderListActionResult<
|
|||
if (!provider) {
|
||||
return [];
|
||||
}
|
||||
const authHints = getProviderEnvVars(provider.id);
|
||||
const authHints = getProviderEnvVarsCore(provider.id);
|
||||
const capabilities = params.summarizeCapabilities(provider);
|
||||
const modelLine = details.models.length > 0 ? details.models.join(", ") : "unknown";
|
||||
const authHint =
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ const mocks = vi.hoisted(() => ({
|
|||
: {}),
|
||||
}),
|
||||
),
|
||||
getProviderEnvVars: vi.fn((providerId: string) => [
|
||||
getProviderEnvVarsCore: vi.fn((providerId: string) => [
|
||||
`${providerId.toUpperCase().replaceAll("-", "_")}_API_KEY`,
|
||||
]),
|
||||
embedBatch: vi.fn(async (inputs: unknown[], options?: { inputType?: string }) =>
|
||||
|
|
@ -292,7 +292,7 @@ vi.mock("../runtime.js", async (importOriginal) => ({
|
|||
}));
|
||||
|
||||
vi.mock("../secrets/provider-env-vars.js", () => ({
|
||||
getProviderEnvVars: mocks.getProviderEnvVars,
|
||||
getProviderEnvVarsCore: mocks.getProviderEnvVarsCore,
|
||||
resolveProviderAuthLookupMaps: () => ({
|
||||
aliasMap: {},
|
||||
envCandidateMap: {},
|
||||
|
|
@ -698,7 +698,7 @@ describe("capability cli", () => {
|
|||
mocks.getTtsProvider.mockReset().mockReturnValue("openai");
|
||||
mocks.listSpeechProviders.mockReset().mockReturnValue([]);
|
||||
mocks.resolveExplicitTtsOverrides.mockClear();
|
||||
mocks.getProviderEnvVars
|
||||
mocks.getProviderEnvVarsCore
|
||||
.mockReset()
|
||||
.mockImplementation((providerId: string) => [
|
||||
`${providerId.toUpperCase().replaceAll("-", "_")}_API_KEY`,
|
||||
|
|
@ -1032,7 +1032,7 @@ describe("capability cli", () => {
|
|||
expect(providers).toContainEqual(
|
||||
expect.objectContaining({ provider: "openai", configured: true }),
|
||||
);
|
||||
expect(mocks.getProviderEnvVars).toHaveBeenCalledWith("openai");
|
||||
expect(mocks.getProviderEnvVarsCore).toHaveBeenCalledWith("openai");
|
||||
});
|
||||
|
||||
it("scopes provider state and model selection to an explicit agent", async () => {
|
||||
|
|
@ -4351,7 +4351,7 @@ describe("capability cli", () => {
|
|||
|
||||
it("marks env-backed image providers as configured", async () => {
|
||||
vi.stubEnv("FAL_KEY", "fal-test-key");
|
||||
mocks.getProviderEnvVars.mockReturnValueOnce(["FAL_KEY"]);
|
||||
mocks.getProviderEnvVarsCore.mockReturnValueOnce(["FAL_KEY"]);
|
||||
mocks.listRuntimeImageGenerationProviders.mockReturnValueOnce([
|
||||
{ id: "fal", label: "fal", defaultModel: "fal-ai/flux", models: [] },
|
||||
] as never);
|
||||
|
|
@ -4366,7 +4366,7 @@ describe("capability cli", () => {
|
|||
it("marks env-backed video generation and description providers as configured", async () => {
|
||||
vi.stubEnv("RUNWAYML_API_SECRET", "runway-test-key");
|
||||
vi.stubEnv("GEMINI_API_KEY", "gemini-test-key");
|
||||
mocks.getProviderEnvVars.mockImplementation((providerId: string) =>
|
||||
mocks.getProviderEnvVarsCore.mockImplementation((providerId: string) =>
|
||||
providerId === "runway" ? ["RUNWAYML_API_SECRET"] : ["GEMINI_API_KEY"],
|
||||
);
|
||||
mocks.listRuntimeVideoGenerationProviders.mockReturnValueOnce([
|
||||
|
|
@ -4395,7 +4395,7 @@ describe("capability cli", () => {
|
|||
|
||||
it("marks env-backed TTS providers as configured", async () => {
|
||||
vi.stubEnv("XAI_API_KEY", "xai-test-key");
|
||||
mocks.getProviderEnvVars.mockReturnValueOnce(["XAI_API_KEY"]);
|
||||
mocks.getProviderEnvVarsCore.mockReturnValueOnce(["XAI_API_KEY"]);
|
||||
mocks.listSpeechProviders.mockReturnValueOnce([
|
||||
{ id: "xai", label: "xAI", models: [], voices: [] },
|
||||
] as never);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { inspectLocalAudioSelection } from "../../media-understanding/local-audi
|
|||
import { buildMediaUnderstandingRegistry } from "../../media-understanding/provider-registry.js";
|
||||
import { transcribeAudioFile } from "../../media-understanding/runtime.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { getProviderEnvVars } from "../../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../../secrets/provider-env-vars.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
import { getModelsCommandSecretTargetIds } from "../command-secret-targets.js";
|
||||
import { prepareLocalCapabilityAccountSecrets } from "./local-account-secrets.js";
|
||||
|
|
@ -102,7 +102,7 @@ export function registerAudioCapabilityCommands(capability: Command): void {
|
|||
cfg,
|
||||
providerId: provider.id,
|
||||
agentId,
|
||||
envVars: getProviderEnvVars(provider.id, {
|
||||
envVars: getProviderEnvVarsCore(provider.id, {
|
||||
config: cfg,
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import { callGateway, randomIdempotencyKey } from "../../gateway/call.js";
|
|||
import { ADMIN_SCOPE } from "../../gateway/operator-scopes.js";
|
||||
import { convertHeicToJpeg } from "../../media/media-services.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { getProviderEnvVars } from "../../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../../secrets/provider-env-vars.js";
|
||||
import { AsyncWorkScope, captureAsyncWorkTracker } from "../../shared/async-work-scope.js";
|
||||
import { createDeferredCore } from "../../shared/deferred.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
|
|
@ -393,7 +393,7 @@ async function buildModelProviders(rawAgentId?: string) {
|
|||
cfg,
|
||||
providerId: entry.provider,
|
||||
agentId,
|
||||
envVars: getProviderEnvVars(entry.provider),
|
||||
envVars: getProviderEnvVarsCore(entry.provider),
|
||||
}),
|
||||
selected: selectedProvider === entry.provider,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from "../../config/config.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { getProviderEnvVars } from "../../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../../secrets/provider-env-vars.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
import { resolveCommandConfigWithSecrets } from "../command-config-resolution.js";
|
||||
import { inheritOptionFromParent } from "../command-options.js";
|
||||
|
|
@ -133,7 +133,7 @@ export function providerHasGenericConfig(params: {
|
|||
const ttsProviders = (params.cfg.tts?.providers ?? {}) as Record<string, unknown>;
|
||||
const envVars =
|
||||
params.envVars ??
|
||||
getProviderEnvVars(params.providerId, {
|
||||
getProviderEnvVarsCore(params.providerId, {
|
||||
config: params.cfg,
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -398,7 +398,7 @@ export async function agentExecCommand(
|
|||
const [
|
||||
{ withAuthProfileStoreAgentDir, withEnvOnlyAuthProfileStore },
|
||||
{ withHostExecInheritedEnvOmitted },
|
||||
{ listKnownProviderAuthEnvVarNames },
|
||||
{ listKnownProviderAuthEnvVarNamesCore },
|
||||
runAgent,
|
||||
] = await Promise.all([
|
||||
import("../agents/auth-profiles.js"),
|
||||
|
|
@ -481,7 +481,7 @@ export async function agentExecCommand(
|
|||
}
|
||||
return await toolBudget.run(() =>
|
||||
withHostExecInheritedEnvOmitted(
|
||||
listKnownProviderAuthEnvVarNames({ env: process.env }),
|
||||
listKnownProviderAuthEnvVarNamesCore({ env: process.env }),
|
||||
runWithAuthScope,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { vi } from "vitest";
|
|||
import { withTempHome } from "../config/test-helpers.js";
|
||||
import { withStateDatabaseCoordinatorRuntimeDirectory } from "../infra/state-database-coordinator.js";
|
||||
import * as temporaryState from "../infra/tmp-openclaw-dir.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../secrets/provider-env-vars.js";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
|
||||
/** Keep real preflight fixtures from provisioning plugins for the developer's credentials. */
|
||||
|
|
@ -18,7 +18,7 @@ export async function withDoctorConfigPreflightHome<T>(
|
|||
.spyOn(temporaryState, "resolvePreferredOpenClawTmpDir")
|
||||
.mockReturnValue(control);
|
||||
const providerEnv = Object.fromEntries(
|
||||
listKnownProviderAuthEnvVarNames({ config: {}, env: process.env }).map((key) => [
|
||||
listKnownProviderAuthEnvVarNamesCore({ config: {}, env: process.env }).map((key) => [
|
||||
key,
|
||||
undefined,
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import {
|
|||
loadProviderPolicyArtifacts,
|
||||
} from "../plugins/provider-public-artifacts.js";
|
||||
import { defaultSlotIdForKey } from "../plugins/slots.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import {
|
||||
formatLocalRuntimeDoctorNote,
|
||||
|
|
@ -845,7 +845,7 @@ function resolvePrimaryMemoryProviderEnvVar(provider: string): string {
|
|||
return "OPENAI_API_KEY";
|
||||
}
|
||||
const authProviderId = MEMORY_EMBEDDING_PROVIDER_AUTH_IDS.get(provider);
|
||||
const envVar = authProviderId ? getProviderEnvVars(authProviderId)[0] : undefined;
|
||||
const envVar = authProviderId ? getProviderEnvVarsCore(authProviderId)[0] : undefined;
|
||||
return envVar ?? `${provider.toUpperCase()}_API_KEY`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ vi.mock("../agents/provider-auth-aliases.js", () => ({
|
|||
}));
|
||||
|
||||
vi.mock("../secrets/provider-env-vars.js", () => ({
|
||||
getProviderEnvVars: vi.fn((provider: string) => providerEnvVarsById[provider] ?? []),
|
||||
getProviderEnvVarsCore: vi.fn((provider: string) => providerEnvVarsById[provider] ?? []),
|
||||
resolveProviderAuthLookupMaps: () => ({
|
||||
aliasMap: {},
|
||||
envCandidateMap: {},
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const emptyPluginMetadataSnapshot = vi.hoisted(() => ({
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
import type { InstalledPluginIndex } from "../plugins/installed-plugin-index-types.js";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { clearPluginMetadataLifecycleCaches } from "../plugins/plugin-metadata-lifecycle.js";
|
||||
import { buildPluginMetadataProviderFacts } from "../plugins/plugin-metadata-provider-facts.js";
|
||||
import { restorePluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { buildDeclaredProviderOwnerIndex } from "../plugins/provider-owner-index.js";
|
||||
|
||||
|
|
@ -100,6 +101,8 @@ function workspaceSnapshot(
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions:
|
||||
buildPluginMetadataProviderFacts(plugins).providerAuthContributions,
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Provides fixtures for plugin auto-enable config tests.
|
||||
import path from "node:path";
|
||||
import { resolveInstalledPluginIndexPolicyHash } from "../plugins/installed-plugin-index-policy.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import { clearPluginMetadataLifecycleCaches } from "../plugins/plugin-metadata-lifecycle.js";
|
||||
// Provides fixtures for plugin auto-enable config tests.
|
||||
import { buildPluginMetadataProviderFacts } from "../plugins/plugin-metadata-provider-facts.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import type { PluginOrigin } from "../plugins/plugin-origin.types.js";
|
||||
import { buildDeclaredProviderOwnerIndex } from "../plugins/provider-owner-index.js";
|
||||
|
|
@ -117,6 +118,8 @@ export function createPluginMetadataSnapshot(params: {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: buildPluginMetadataProviderFacts(params.manifestRegistry.plugins)
|
||||
.providerAuthContributions,
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Lists expected shell environment keys for config validation.
|
||||
import { uniqueStrings } from "@openclaw/normalization-core/string-normalization";
|
||||
import { listKnownChannelEnvVarNames } from "../secrets/channel-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../secrets/provider-env-vars.js";
|
||||
import type { OpenClawConfig } from "./types.openclaw.js";
|
||||
|
||||
const CORE_SHELL_ENV_EXPECTED_KEYS = ["OPENCLAW_GATEWAY_TOKEN", "OPENCLAW_GATEWAY_PASSWORD"];
|
||||
|
|
@ -12,7 +12,7 @@ export function resolveShellEnvExpectedKeys(
|
|||
config?: OpenClawConfig,
|
||||
): string[] {
|
||||
return uniqueStrings([
|
||||
...listKnownProviderAuthEnvVarNames({ config, env }),
|
||||
...listKnownProviderAuthEnvVarNamesCore({ config, env }),
|
||||
...listKnownChannelEnvVarNames({ config, env }),
|
||||
...CORE_SHELL_ENV_EXPECTED_KEYS,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { runQaGatewayFixture } from "../../test/helpers/qa-gateway-cleanup.js";
|
|||
import { isLiveTestEnabled, logLiveProgress } from "../agents/live-test-helpers.js";
|
||||
import type { CronRunLogEntry } from "../cron/run-log-types.js";
|
||||
import type { CronJob } from "../cron/types.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../secrets/provider-env-vars.js";
|
||||
|
||||
const describeLive = isLiveTestEnabled() ? describe : describe.skip;
|
||||
|
||||
|
|
@ -52,7 +52,9 @@ describeLive("cron scheduling through an isolated Gateway", () => {
|
|||
const instance = await createOpenClawTestInstance({
|
||||
name: "cron-scheduler",
|
||||
env: {
|
||||
...Object.fromEntries(listKnownProviderAuthEnvVarNames().map((name) => [name, undefined])),
|
||||
...Object.fromEntries(
|
||||
listKnownProviderAuthEnvVarNamesCore().map((name) => [name, undefined]),
|
||||
),
|
||||
OPENCLAW_SKIP_CRON: "0",
|
||||
OPENCLAW_TEST_MINIMAL_GATEWAY: "0",
|
||||
OPENCLAW_AGENT_RUNTIME: undefined,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import type { OpenClawConfig } from "../config/config.js";
|
|||
import type { CronRunLogEntry } from "../cron/run-log-types.js";
|
||||
import type { CronJob } from "../cron/types.js";
|
||||
import type { Message } from "../llm/types.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../secrets/provider-env-vars.js";
|
||||
|
||||
const describeLive =
|
||||
isLiveTestEnabled() && process.env.OPENAI_API_KEY?.trim() ? describe : describe.skip;
|
||||
|
|
@ -41,7 +41,7 @@ describeLive("cron tool allowlists through live harnesses", () => {
|
|||
name: `cron-tools-${runtime}`,
|
||||
env: {
|
||||
...Object.fromEntries(
|
||||
listKnownProviderAuthEnvVarNames().map((name) => [name, undefined]),
|
||||
listKnownProviderAuthEnvVarNamesCore().map((name) => [name, undefined]),
|
||||
),
|
||||
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
OPENAI_BASE_URL: undefined,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type { createOpenAIModelRoutesResolver } from "../../agents/openai-model-
|
|||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { loadManifestMetadataSnapshot } from "../../plugins/manifest-contract-eligibility.js";
|
||||
import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js";
|
||||
import { createPluginMetadataSnapshotFixture } from "../../plugins/plugin-metadata.test-support.js";
|
||||
import type { PluginRegistry } from "../../plugins/registry-types.js";
|
||||
import {
|
||||
type PreparedGatewayModelCatalogSnapshot,
|
||||
|
|
@ -134,13 +135,11 @@ export async function listModels(params: ListModelsParams) {
|
|||
cfg: config,
|
||||
agentId,
|
||||
snapshot: { entries: params.catalog, routeVariants: params.catalog },
|
||||
metadataSnapshot: {
|
||||
index: { plugins: [] },
|
||||
manifestRegistry: { plugins: [] },
|
||||
metadataSnapshot: createPluginMetadataSnapshotFixture({
|
||||
plugins: [
|
||||
{ id: "test-provider", modelCatalog: { discovery: params.discoveryModes } },
|
||||
],
|
||||
} as never,
|
||||
}),
|
||||
preparedAuthStore: { version: 1, profiles: {} },
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,12 @@ const OPENCLAW_DEVICE_PLACEMENT: NonNullable<GatewayAgentRuntime["devicePlacemen
|
|||
};
|
||||
|
||||
const modelPluginMetadataSnapshot = await vi.hoisted(async () => {
|
||||
const { buildPluginMetadataProviderFacts } =
|
||||
await import("../../plugins/plugin-metadata-provider-facts.js");
|
||||
const { makeEmptyPluginMetadataOwners } =
|
||||
await import("../../plugins/current-plugin-metadata.test-support.js");
|
||||
const { buildDeclaredProviderOwnerIndex } = await import("../../plugins/provider-owner-index.js");
|
||||
const plugins = [
|
||||
const plugins: PluginMetadataSnapshot["manifestRegistry"]["plugins"] = [
|
||||
{
|
||||
id: "anthropic",
|
||||
channels: [],
|
||||
|
|
@ -130,20 +134,16 @@ const modelPluginMetadataSnapshot = await vi.hoisted(async () => {
|
|||
normalizePluginId: (pluginId: string) => pluginId,
|
||||
declaredProviderOwners: buildDeclaredProviderOwnerIndex(plugins),
|
||||
owners: {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
...makeEmptyPluginMetadataOwners(),
|
||||
providerAuthContributions:
|
||||
buildPluginMetadataProviderFacts(plugins).providerAuthContributions,
|
||||
providers: new Map([
|
||||
["anthropic", ["anthropic"]],
|
||||
["byteplus", ["byteplus"]],
|
||||
["byteplus-plan", ["byteplus"]],
|
||||
["github-copilot", ["github-copilot"]],
|
||||
]),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map([["claude-cli", ["anthropic"]]]),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Gateway plugin tests cover plugin loading, auto-enable, runtime registry setup,
|
||||
// request-scope injection, diagnostics, and handler dispatch integration.
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
|
@ -8,6 +6,9 @@ import { isRecord } from "@openclaw/normalization-core/record-coerce";
|
|||
import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { createTerminalTool } from "../agents/tools/terminal-tool.js";
|
||||
// Gateway plugin tests cover plugin loading, auto-enable, runtime registry setup,
|
||||
// request-scope injection, diagnostics, and handler dispatch integration.
|
||||
import { makeEmptyPluginMetadataOwners } from "../plugins/current-plugin-metadata.test-support.js";
|
||||
import {
|
||||
getGlobalPluginRegistry,
|
||||
initializeGlobalHookRunner,
|
||||
|
|
@ -219,17 +220,7 @@ function createLookUpTableForTest(params: {
|
|||
byPluginId: new Map(),
|
||||
normalizePluginId: (pluginId) => pluginId,
|
||||
declaredProviderOwners: buildDeclaredProviderOwnerIndex(params.manifestRegistry?.plugins ?? []),
|
||||
owners: {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
providers: new Map(),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map(),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
owners: makeEmptyPluginMetadataOwners(),
|
||||
startup: {
|
||||
channelPluginIds: [],
|
||||
pluginIds: params.pluginIds ?? [],
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const pluginMetadataSnapshot = vi.hoisted((): PluginMetadataSnapshot => {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
};
|
||||
const zeroMetrics = {
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ const pluginMetadataSnapshot = vi.hoisted((): PluginMetadataSnapshot => {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// Gateway chat integration tests cover dashboard chat requests, transcript
|
||||
// history limits, model overrides, inbound dispatch, and streaming event fanout.
|
||||
|
||||
import { randomUUID } from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
|
@ -53,6 +54,7 @@ import { readPersistedMediaFacts } from "../media/media-facts.js";
|
|||
import { resolveMediaReferenceLocalPath } from "../media/media-reference.js";
|
||||
import { getMediaDir } from "../media/store.js";
|
||||
import { withPluginMetadataSnapshotScope } from "../plugins/current-plugin-metadata-snapshot.js";
|
||||
import { makeEmptyPluginMetadataOwners } from "../plugins/current-plugin-metadata.test-support.js";
|
||||
import { resolveInstalledPluginIndexPolicyHash } from "../plugins/installed-plugin-index-policy.js";
|
||||
import { rebasePluginMetadataSnapshotManifestRegistry } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
|
|
@ -205,17 +207,7 @@ function createGatewayPluginMetadataSnapshot(config: OpenClawConfig): PluginMeta
|
|||
byPluginId: new Map(),
|
||||
normalizePluginId: (pluginId) => pluginId,
|
||||
declaredProviderOwners: new Map(),
|
||||
owners: {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
providers: new Map(),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map(),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
owners: makeEmptyPluginMetadataOwners(),
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
buildCapabilityProviderIndex,
|
||||
normalizeCapabilityProviderId,
|
||||
} from "../plugins/provider-registry-shared.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import { resolveImageGenerationMaxInputImages } from "./capabilities.js";
|
||||
import { resolveImageGenerationOverrides } from "./normalization.js";
|
||||
import type { GenerateImageParams, GenerateImageRuntimeResult } from "./runtime-types.js";
|
||||
|
|
@ -34,7 +34,7 @@ const log = createSubsystemLogger("image-generation");
|
|||
type ImageGenerationRuntimeDeps = {
|
||||
getProvider?: typeof getImageGenerationProvider;
|
||||
listProviders?: typeof listImageGenerationProviders;
|
||||
getProviderEnvVars?: typeof getProviderEnvVars;
|
||||
getProviderEnvVars?: typeof getProviderEnvVarsCore;
|
||||
log?: Pick<typeof log, "warn">;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import {
|
|||
import { resolveInstalledPluginIndexPolicyHash } from "../plugins/installed-plugin-index-policy.js";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { clearPluginMetadataLifecycleCaches } from "../plugins/plugin-metadata-lifecycle.js";
|
||||
import { buildPluginMetadataProviderFacts } from "../plugins/plugin-metadata-provider-facts.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import { buildDeclaredProviderOwnerIndex } from "../plugins/provider-owner-index.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../secrets/provider-env-vars.js";
|
||||
import { captureFullEnv, deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js";
|
||||
import { loadDotEnv, loadWorkspaceDotEnvFile } from "./dotenv.js";
|
||||
|
||||
|
|
@ -79,7 +80,10 @@ function createManifestBackedProviderSnapshot(
|
|||
byPluginId: new Map([[plugin.id, plugin]]),
|
||||
normalizePluginId: (pluginId: string) => pluginId,
|
||||
declaredProviderOwners: buildDeclaredProviderOwnerIndex([plugin]),
|
||||
owners: makeEmptyPluginMetadataOwners(),
|
||||
owners: {
|
||||
...makeEmptyPluginMetadataOwners(),
|
||||
...buildPluginMetadataProviderFacts([plugin]),
|
||||
},
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
|
|
@ -140,7 +144,7 @@ describe("workspace .env blocklist completeness", () => {
|
|||
it("keeps registered provider auth vars from trusted global dotenv", async () => {
|
||||
await withIsolatedEnvAndCwd(async () => {
|
||||
await withDotEnvFixture(async ({ cwdDir, stateDir }) => {
|
||||
const providerAuthKeys = listKnownProviderAuthEnvVarNames().toSorted();
|
||||
const providerAuthKeys = listKnownProviderAuthEnvVarNamesCore().toSorted();
|
||||
await writeEnvFile(
|
||||
path.join(cwdDir, ".env"),
|
||||
`${providerAuthKeys.map((key) => `${key}=workspace-${key}`).join("\n")}\n`,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Loads dotenv files while blocking unsafe workspace env keys.
|
||||
import path from "node:path";
|
||||
import {
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
listKnownProviderAuthEnvVarNamesCore,
|
||||
listKnownProviderAuthEnvVarNamesAsync,
|
||||
} from "../secrets/provider-env-vars.js";
|
||||
import {
|
||||
|
|
@ -293,7 +293,7 @@ export function loadWorkspaceDotEnvFile(
|
|||
let providerAuthBlockedKeys: ReadonlySet<string> | undefined;
|
||||
const getProviderAuthBlockedKeys = () => {
|
||||
providerAuthBlockedKeys ??= buildProviderAuthWorkspaceDotEnvBlocklist(
|
||||
listKnownProviderAuthEnvVarNames({ env, includeUntrustedWorkspacePlugins: false }),
|
||||
listKnownProviderAuthEnvVarNamesCore({ env, includeUntrustedWorkspacePlugins: false }),
|
||||
);
|
||||
return providerAuthBlockedKeys;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { readSessionMessagesAsync } from "../gateway/session-transcript-readers.js";
|
||||
import { loadGatewaySessionEntryReadOnly } from "../gateway/session-utils.js";
|
||||
import { extractPayloadText } from "../gateway/test-helpers.agent-results.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../secrets/provider-env-vars.js";
|
||||
|
||||
const enabled = isLiveTestEnabled() && process.env.OPENCLAW_LIVE_SESSION_EVENT_WAKE === "1";
|
||||
const describeLive = enabled ? describe : describe.skip;
|
||||
|
|
@ -46,7 +46,9 @@ describeLive("session event wake through a live Gateway", () => {
|
|||
const instance = await createOpenClawTestInstance({
|
||||
name: "live-session-event-wake",
|
||||
env: {
|
||||
...Object.fromEntries(listKnownProviderAuthEnvVarNames().map((name) => [name, undefined])),
|
||||
...Object.fromEntries(
|
||||
listKnownProviderAuthEnvVarNamesCore().map((name) => [name, undefined]),
|
||||
),
|
||||
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
OPENCLAW_AGENT_RUNTIME: "openclaw",
|
||||
OPENCLAW_ALLOW_SLOW_REPLY_TESTS: "1",
|
||||
|
|
|
|||
|
|
@ -62,12 +62,12 @@ vi.mock("../plugins/manifest-contract-eligibility.js", () => ({
|
|||
}));
|
||||
|
||||
vi.mock("../secrets/provider-env-vars.js", () => ({
|
||||
listKnownProviderAuthEnvVarNames: () => [
|
||||
listKnownProviderAuthEnvVarNamesCore: () => [
|
||||
"ANTHROPIC_API_KEY",
|
||||
"MINIMAX_CODE_PLAN_KEY",
|
||||
"OPENAI_API_KEY",
|
||||
],
|
||||
resolveProviderAuthEnvVarCandidates: () => ({
|
||||
resolveProviderAuthEnvVarCandidatesCore: () => ({
|
||||
anthropic: ["ANTHROPIC_API_KEY"],
|
||||
minimax: ["MINIMAX_CODE_PLAN_KEY"],
|
||||
openai: ["OPENAI_API_KEY"],
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from "../plugins/manifest-owner-policy.js";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { resolveProviderUsageAuthWithPlugin } from "../plugins/provider-runtime.js";
|
||||
import { resolveProviderAuthEnvVarCandidates } from "../secrets/provider-env-vars.js";
|
||||
import { resolveProviderAuthEnvVarCandidatesCore } from "../secrets/provider-env-vars.js";
|
||||
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { isOAuthOnlyUsageProvider } from "./provider-usage.shared.js";
|
||||
import type { UsageProviderId } from "./provider-usage.types.js";
|
||||
|
|
@ -90,7 +90,7 @@ function hasProviderAuthEnvCredentialSource(params: {
|
|||
state: UsageAuthState;
|
||||
providerIds: string[];
|
||||
}): boolean {
|
||||
const candidates = resolveProviderAuthEnvVarCandidates({
|
||||
const candidates = resolveProviderAuthEnvVarCandidatesCore({
|
||||
config: params.state.cfg,
|
||||
env: {
|
||||
...(process.env.VITEST ? process.env : {}),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import type { AgentModelConfig } from "../config/types.agents-shared.js";
|
|||
import type { OpenClawConfig } from "../config/types.js";
|
||||
import { formatErrorMessage, toErrorObject } from "../infra/errors.js";
|
||||
import { isProviderApiKeyConfigured } from "../plugin-sdk/provider-auth.js";
|
||||
import { getProviderEnvVars as getDefaultProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
|
||||
// Shared media-generation runtime helpers for provider fallback, request
|
||||
// timeout normalization, model selection, and capability value normalization.
|
||||
|
|
@ -693,9 +693,9 @@ export function buildNoCapabilityModelConfiguredMessage(params: {
|
|||
modelConfigKey: string;
|
||||
providers: Array<{ id: string; defaultModel?: string | null }>;
|
||||
fallbackSampleRef?: string;
|
||||
getProviderEnvVars?: typeof getDefaultProviderEnvVars;
|
||||
getProviderEnvVars?: typeof getProviderEnvVarsCore;
|
||||
}): string {
|
||||
const getProviderEnvVars = params.getProviderEnvVars ?? getDefaultProviderEnvVars;
|
||||
const getProviderEnvVars = params.getProviderEnvVars ?? getProviderEnvVarsCore;
|
||||
const sampleModel = params.providers.find(
|
||||
(provider) =>
|
||||
normalizeOptionalString(provider.id) && normalizeOptionalString(provider.defaultModel),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
buildCapabilityProviderIndex,
|
||||
normalizeCapabilityProviderId,
|
||||
} from "../plugins/provider-registry-shared.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import { resolveMusicGenerationOverrides } from "./normalization.js";
|
||||
import type { GenerateMusicParams, GenerateMusicRuntimeResult } from "./runtime-types.js";
|
||||
import type { MusicGenerationResult } from "./types.js";
|
||||
|
|
@ -37,7 +37,7 @@ const log = createSubsystemLogger("music-generation");
|
|||
type MusicGenerationRuntimeDeps = {
|
||||
getProvider?: typeof getMusicGenerationProvider;
|
||||
listProviders?: typeof listMusicGenerationProviders;
|
||||
getProviderEnvVars?: typeof getProviderEnvVars;
|
||||
getProviderEnvVars?: typeof getProviderEnvVarsCore;
|
||||
log?: Pick<typeof log, "debug">;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,10 @@ describe("agent-runtime model catalog compatibility", () => {
|
|||
PluginMetadataSnapshot,
|
||||
"owners" | "declaredProviderOwners"
|
||||
> & {
|
||||
owners: Omit<PluginMetadataSnapshot["owners"], "modelIdNormalizationPolicies">;
|
||||
owners: Omit<
|
||||
PluginMetadataSnapshot["owners"],
|
||||
"modelIdNormalizationPolicies" | "providerAuthContributions"
|
||||
>;
|
||||
};
|
||||
type AcceptedMetadataSnapshot = NonNullable<
|
||||
NonNullable<Parameters<typeof loadModelCatalog>[0]>["metadataSnapshot"]
|
||||
|
|
|
|||
|
|
@ -48,10 +48,18 @@ type LoadModelCatalogCompatibilityParams = LoadPreparedModelCatalogParams & {
|
|||
cacheOnly?: boolean;
|
||||
/** @deprecated Plugin metadata belongs to the published lifecycle generation. */
|
||||
metadataSnapshot?: Omit<PluginMetadataSnapshot, "owners" | "declaredProviderOwners"> & {
|
||||
// Shipped snapshots may predate prepared provider ownership and normalization policies.
|
||||
// Shipped snapshots may predate prepared provider ownership, auth contributions, and normalization policies.
|
||||
declaredProviderOwners?: PluginMetadataSnapshot["declaredProviderOwners"];
|
||||
owners: Omit<PluginMetadataSnapshot["owners"], "modelIdNormalizationPolicies"> &
|
||||
Partial<Pick<PluginMetadataSnapshot["owners"], "modelIdNormalizationPolicies">>;
|
||||
owners: Omit<
|
||||
PluginMetadataSnapshot["owners"],
|
||||
"modelIdNormalizationPolicies" | "providerAuthContributions"
|
||||
> &
|
||||
Partial<
|
||||
Pick<
|
||||
PluginMetadataSnapshot["owners"],
|
||||
"modelIdNormalizationPolicies" | "providerAuthContributions"
|
||||
>
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export {
|
|||
export { parseImageGenerationModelRef } from "../media-generation/model-ref.js";
|
||||
export { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
export { normalizeGooglePreviewModelId as normalizeGoogleModelId } from "./provider-model-shared.js";
|
||||
export { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
export { getProviderEnvVars } from "./provider-env-vars.js";
|
||||
/** Default OpenAI image model used when image-generation provider config omits one. */
|
||||
export const OPENAI_DEFAULT_IMAGE_MODEL = "gpt-image-2";
|
||||
|
||||
|
|
|
|||
|
|
@ -91,10 +91,8 @@ export {
|
|||
normalizeOptionalSecretInput,
|
||||
normalizeSecretInput,
|
||||
} from "../utils/normalize-secret-input.js";
|
||||
export {
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
omitEnvKeysCaseInsensitive,
|
||||
} from "../secrets/provider-env-vars.js";
|
||||
export { listKnownProviderAuthEnvVarNames } from "./provider-env-vars.js";
|
||||
export { omitEnvKeysCaseInsensitive } from "../secrets/provider-env-vars.js";
|
||||
export { buildOauthProviderAuthResult } from "./provider-auth-result.js";
|
||||
export {
|
||||
buildOpenAICodexCredentialExtra,
|
||||
|
|
|
|||
70
src/plugin-sdk/provider-env-vars.test.ts
Normal file
70
src/plugin-sdk/provider-env-vars.test.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { getProviderEnvVars as getImageProviderEnvVars } from "openclaw/plugin-sdk/image-generation-core";
|
||||
import { listKnownProviderAuthEnvVarNames as listAuthEnvVarNames } from "openclaw/plugin-sdk/provider-auth";
|
||||
import {
|
||||
getProviderEnvVars,
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
resolveProviderAuthEnvVarCandidates,
|
||||
} from "openclaw/plugin-sdk/provider-env-vars";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createPluginMetadataSnapshotFixture } from "../plugins/plugin-metadata.test-support.js";
|
||||
|
||||
type LookupParams = NonNullable<Parameters<typeof listKnownProviderAuthEnvVarNames>[0]>;
|
||||
const readers = [
|
||||
{
|
||||
name: "provider-env-vars/getProviderEnvVars",
|
||||
read: (params: LookupParams) => getProviderEnvVars("compat-provider", params),
|
||||
expected: ["COMPAT_PROVIDER_KEY"],
|
||||
},
|
||||
{
|
||||
name: "image-generation-core/getProviderEnvVars",
|
||||
read: (params: LookupParams) => getImageProviderEnvVars("compat-provider", params),
|
||||
expected: ["COMPAT_PROVIDER_KEY"],
|
||||
},
|
||||
{
|
||||
name: "provider-env-vars/resolveProviderAuthEnvVarCandidates",
|
||||
read: (params: LookupParams) =>
|
||||
resolveProviderAuthEnvVarCandidates(params)["compat-provider"] ?? [],
|
||||
expected: ["COMPAT_PROVIDER_KEY"],
|
||||
},
|
||||
{
|
||||
name: "provider-env-vars/listKnownProviderAuthEnvVarNames",
|
||||
read: listKnownProviderAuthEnvVarNames,
|
||||
expected: ["COMPAT_PROVIDER_KEY", "COMPAT_USAGE_KEY"],
|
||||
},
|
||||
{
|
||||
name: "provider-auth/listKnownProviderAuthEnvVarNames",
|
||||
read: listAuthEnvVarNames,
|
||||
expected: ["COMPAT_PROVIDER_KEY", "COMPAT_USAGE_KEY"],
|
||||
},
|
||||
];
|
||||
|
||||
describe.each(["current", "v2026.9.4"])("provider environment SDK with %s snapshots", (version) => {
|
||||
it.each(readers)(
|
||||
"preserves manifest names and trust filtering through $name",
|
||||
({ read, expected }) => {
|
||||
const snapshot = createPluginMetadataSnapshotFixture({
|
||||
plugins: [
|
||||
{
|
||||
id: "compat-provider",
|
||||
origin: "workspace",
|
||||
setup: { providers: [{ id: "compat-provider", envVars: ["COMPAT_PROVIDER_KEY"] }] },
|
||||
providerUsageAuthEnvVars: { "compat-provider": ["COMPAT_USAGE_KEY"] },
|
||||
},
|
||||
],
|
||||
});
|
||||
const { providerAuthContributions: _contributions, ...releasedOwners } = snapshot.owners;
|
||||
const metadataSnapshot =
|
||||
version === "current" ? snapshot : { ...snapshot, owners: Object.freeze(releasedOwners) };
|
||||
const params = { metadataSnapshot } satisfies LookupParams;
|
||||
|
||||
expect(read(params)).toEqual(expect.arrayContaining(expected));
|
||||
const untrustedNames = read({
|
||||
...params,
|
||||
config: {},
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
expect(untrustedNames).not.toContain("COMPAT_PROVIDER_KEY");
|
||||
expect(untrustedNames).not.toContain("COMPAT_USAGE_KEY");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -1,8 +1,65 @@
|
|||
// Public provider auth environment variable helpers for plugin runtimes.
|
||||
|
||||
export {
|
||||
getProviderEnvVars,
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
omitEnvKeysCaseInsensitive,
|
||||
resolveProviderAuthEnvVarCandidates,
|
||||
import { buildPluginMetadataProviderFacts } from "../plugins/plugin-metadata-provider-facts.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import {
|
||||
getProviderEnvVarsCore,
|
||||
listKnownProviderAuthEnvVarNamesCore,
|
||||
resolveProviderAuthEnvVarCandidatesCore,
|
||||
type ProviderEnvVarLookupParams as CoreLookupParams,
|
||||
} from "../secrets/provider-env-vars.js";
|
||||
|
||||
export { omitEnvKeysCaseInsensitive } from "../secrets/provider-env-vars.js";
|
||||
|
||||
type ProviderEnvVarLookupParams =
|
||||
| CoreLookupParams
|
||||
| (Omit<CoreLookupParams, "metadataSnapshot"> & {
|
||||
metadataSnapshot: Omit<PluginMetadataSnapshot, "owners"> & {
|
||||
owners: Omit<PluginMetadataSnapshot["owners"], "providerAuthContributions">;
|
||||
};
|
||||
});
|
||||
|
||||
function hasPreparedLookupParams(params: ProviderEnvVarLookupParams): params is CoreLookupParams {
|
||||
const snapshot = params.metadataSnapshot;
|
||||
return (
|
||||
snapshot === undefined ||
|
||||
("providerAuthContributions" in snapshot.owners &&
|
||||
snapshot.owners.providerAuthContributions !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
// v2026.9.4 accepted snapshots before these prepared facts existed. Keep this
|
||||
// adaptation at the SDK boundary until an approved SDK-breaking release.
|
||||
function prepareLookupParams(params?: ProviderEnvVarLookupParams): CoreLookupParams | undefined {
|
||||
if (!params || hasPreparedLookupParams(params)) {
|
||||
return params;
|
||||
}
|
||||
const snapshot = params.metadataSnapshot;
|
||||
return {
|
||||
...params,
|
||||
metadataSnapshot: {
|
||||
...snapshot,
|
||||
owners: {
|
||||
...snapshot.owners,
|
||||
providerAuthContributions: buildPluginMetadataProviderFacts(snapshot.plugins)
|
||||
.providerAuthContributions,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getProviderEnvVars(
|
||||
providerId: string,
|
||||
params?: ProviderEnvVarLookupParams,
|
||||
): string[] {
|
||||
return getProviderEnvVarsCore(providerId, prepareLookupParams(params));
|
||||
}
|
||||
|
||||
export function listKnownProviderAuthEnvVarNames(params?: ProviderEnvVarLookupParams): string[] {
|
||||
return listKnownProviderAuthEnvVarNamesCore(prepareLookupParams(params));
|
||||
}
|
||||
|
||||
export function resolveProviderAuthEnvVarCandidates(
|
||||
params?: ProviderEnvVarLookupParams,
|
||||
): Record<string, readonly string[]> {
|
||||
return resolveProviderAuthEnvVarCandidatesCore(prepareLookupParams(params));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// Covers current plugin metadata snapshot generation.
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
|
@ -34,6 +33,8 @@ import {
|
|||
clearPluginMetadataLifecycleCaches,
|
||||
retainGatewayPluginMetadata,
|
||||
} from "./plugin-metadata-lifecycle.js";
|
||||
// Covers current plugin metadata snapshot generation.
|
||||
import { buildPluginMetadataProviderFacts } from "./plugin-metadata-provider-facts.js";
|
||||
import {
|
||||
restorePluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
|
|
@ -113,6 +114,8 @@ function createSnapshot(
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions:
|
||||
buildPluginMetadataProviderFacts(plugins).providerAuthContributions,
|
||||
modelIdNormalizationPolicies: collectManifestModelIdNormalizationPolicies(plugins),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ export function makeEmptyPluginMetadataOwners(): PluginMetadataSnapshot["owners"
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ import type {
|
|||
PluginManifestProviderRequestProvider,
|
||||
} from "./manifest.js";
|
||||
import { listOfficialExternalProviderEndpointManifests } from "./official-external-provider-endpoints.js";
|
||||
import type { PluginProviderAuthAliasCandidate } from "./plugin-metadata-snapshot.types.js";
|
||||
import type {
|
||||
PluginProviderAuthAliasCandidate,
|
||||
PluginProviderAuthContribution,
|
||||
} from "./plugin-metadata-snapshot.types.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import { listSetupProviderIds } from "./setup-descriptors.js";
|
||||
|
||||
const PROVIDER_ENDPOINT_CLASSES = new Set(
|
||||
"anthropic-public cerebras-native chutes-native deepseek-native github-copilot-native groq-native meta-native mistral-public minimax-native moonshot-native modelstudio-native nvidia-native openai-public openai opencode-native opencode-go-native azure-openai openrouter xai-native xiaomi-native zai-native google-generative-ai google-vertex".split(
|
||||
|
|
@ -157,7 +161,27 @@ export function buildPluginMetadataProviderFacts(plugins: readonly PluginManifes
|
|||
prepareProviderEndpoints(plugin.providerEndpoints),
|
||||
);
|
||||
const providerRequests = new Map<string, PluginManifestProviderRequestProvider>();
|
||||
const providerAuthContributions: PluginProviderAuthContribution[] = [];
|
||||
for (const plugin of plugins) {
|
||||
// Package declarations are stable; readers still decide eligibility against current config.
|
||||
const envProviders = (plugin.setup?.providers ?? []).filter(
|
||||
(provider) => provider.envVars?.length,
|
||||
);
|
||||
const evidenceProviders = (plugin.setup?.providers ?? []).filter(
|
||||
(provider) => provider.authEvidence?.length,
|
||||
);
|
||||
const fallbackProviderRefs =
|
||||
plugin.setup?.requiresRuntime !== false
|
||||
? listSetupProviderIds(plugin).map(normalizeProviderId).filter(Boolean)
|
||||
: [];
|
||||
if (envProviders.length || evidenceProviders.length || fallbackProviderRefs.length) {
|
||||
providerAuthContributions.push({
|
||||
plugin,
|
||||
envProviders,
|
||||
evidenceProviders,
|
||||
fallbackProviderRefs,
|
||||
});
|
||||
}
|
||||
const requests = isRecord(plugin.providerRequest?.providers)
|
||||
? plugin.providerRequest.providers
|
||||
: {};
|
||||
|
|
@ -191,6 +215,7 @@ export function buildPluginMetadataProviderFacts(plugins: readonly PluginManifes
|
|||
return {
|
||||
providerEndpoints,
|
||||
providerRequests,
|
||||
providerAuthContributions,
|
||||
modelIdNormalizationPolicies: collectManifestModelIdNormalizationPolicies(plugins),
|
||||
providerAuthAliases: buildPluginMetadataProviderAuthAliases(plugins),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import type {
|
|||
PluginManifestModelIdNormalizationProvider,
|
||||
PluginManifestProviderEndpoint,
|
||||
PluginManifestProviderRequestProvider,
|
||||
PluginManifestSetupProvider,
|
||||
} from "./manifest-types.js";
|
||||
import type {
|
||||
PluginRegistrySnapshotDiagnostic,
|
||||
|
|
@ -28,6 +29,13 @@ export type PluginProviderAuthAliasCandidate = {
|
|||
order: number;
|
||||
};
|
||||
|
||||
export type PluginProviderAuthContribution = {
|
||||
plugin: PluginManifestRecord;
|
||||
envProviders: readonly PluginManifestSetupProvider[];
|
||||
evidenceProviders: readonly PluginManifestSetupProvider[];
|
||||
fallbackProviderRefs: readonly string[];
|
||||
};
|
||||
|
||||
export type PluginMetadataSnapshotOwnerMaps = {
|
||||
channels: ReadonlyMap<string, readonly string[]>;
|
||||
channelAccountKeyPolicies?: ReadonlyMap<string, ChannelAccountKeyPolicy>;
|
||||
|
|
@ -40,6 +48,7 @@ export type PluginMetadataSnapshotOwnerMaps = {
|
|||
contracts: ReadonlyMap<string, readonly string[]>;
|
||||
/** Empty views must not fall through to process-current model normalization policies. */
|
||||
modelIdNormalizationPolicies: ReadonlyMap<string, PluginManifestModelIdNormalizationProvider>;
|
||||
providerAuthContributions: readonly PluginProviderAuthContribution[];
|
||||
providerAuthAliases?: ReadonlyMap<string, readonly PluginProviderAuthAliasCandidate[]>;
|
||||
providerEndpoints?: readonly PluginManifestProviderEndpoint[];
|
||||
providerRequests?: ReadonlyMap<string, PluginManifestProviderRequestProvider>;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Verifies provider auth environment trust decisions.
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const getProviderEnvVars = vi.hoisted(() => vi.fn(() => ["WHISPERX_API_KEY"]));
|
||||
const getProviderEnvVarsCore = vi.hoisted(() => vi.fn(() => ["WHISPERX_API_KEY"]));
|
||||
|
||||
vi.mock("../secrets/provider-env-vars.js", () => ({
|
||||
getProviderEnvVars,
|
||||
getProviderEnvVarsCore,
|
||||
resolveProviderAuthLookupMaps: () => ({
|
||||
aliasMap: {},
|
||||
envCandidateMap: {},
|
||||
|
|
@ -22,7 +22,7 @@ describe("provider auth env trust", () => {
|
|||
config,
|
||||
});
|
||||
|
||||
expect(getProviderEnvVars).toHaveBeenCalledWith("whisperx", {
|
||||
expect(getProviderEnvVarsCore).toHaveBeenCalledWith("whisperx", {
|
||||
config,
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
|
|
@ -85,7 +85,7 @@ describe("provider auth env trust", () => {
|
|||
env: { WHISPERX_API_KEY: "test-secret" },
|
||||
});
|
||||
|
||||
expect(getProviderEnvVars).toHaveBeenCalledWith("whisperx", {
|
||||
expect(getProviderEnvVarsCore).toHaveBeenCalledWith("whisperx", {
|
||||
config,
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
|
|
@ -111,7 +111,7 @@ describe("provider auth env trust", () => {
|
|||
env: { WHISPERX_API_KEY: "test-secret" },
|
||||
});
|
||||
|
||||
expect(getProviderEnvVars).toHaveBeenCalledWith("whisperx", {
|
||||
expect(getProviderEnvVarsCore).toHaveBeenCalledWith("whisperx", {
|
||||
config,
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from "../config/types.secrets.js";
|
||||
import { safeRealpathSync } from "../infra/boundary-path.js";
|
||||
import type { OAuthCredentials } from "../llm/oauth.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import { isValidSecretRef } from "../secrets/ref-contract.js";
|
||||
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import type { SecretInputMode } from "./provider-auth-types.js";
|
||||
|
|
@ -45,7 +45,7 @@ function buildEnvSecretRef(id: string): SecretRef {
|
|||
}
|
||||
|
||||
function resolveProviderDefaultEnvSecretRef(provider: string, config?: OpenClawConfig): SecretRef {
|
||||
const envVars = getProviderEnvVars(provider, {
|
||||
const envVars = getProviderEnvVarsCore(provider, {
|
||||
...(config ? { config } : {}),
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type { OpenClawConfig } from "../config/types.js";
|
|||
import { isValidEnvSecretRefId, type SecretRef } from "../config/types.secrets.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { encodeJsonPointerToken } from "../secrets/json-pointer.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import {
|
||||
formatExecSecretRefIdValidationMessage,
|
||||
isValidExecSecretRefId,
|
||||
|
|
@ -53,7 +53,7 @@ function resolveDefaultProviderEnvVar(
|
|||
provider: string,
|
||||
config?: OpenClawConfig,
|
||||
): string | undefined {
|
||||
const envVars = getProviderEnvVars(provider, {
|
||||
const envVars = getProviderEnvVarsCore(provider, {
|
||||
...(config ? { config } : {}),
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
});
|
||||
|
|
@ -72,7 +72,7 @@ export function resolveRefFallbackInput(params: {
|
|||
}): { ref: SecretRef; resolvedValue: string } {
|
||||
const fallbackEnvVar =
|
||||
params.preferredEnvVar ??
|
||||
getProviderEnvVars(params.provider, {
|
||||
getProviderEnvVarsCore(params.provider, {
|
||||
config: params.config,
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
}).find((candidate) => normalizeOptionalString(candidate) !== undefined);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ function makeOwners(provider: string): PluginMetadataSnapshotOwnerMaps {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ import { sortUniqueStrings } from "@openclaw/normalization-core/string-normaliza
|
|||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginAutoEnableResult } from "../config/plugin-auto-enable.js";
|
||||
import { makeEmptyPluginMetadataOwners } from "./current-plugin-metadata.test-support.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import type { OpenClawPackageManifest } from "./manifest.js";
|
||||
import { buildPluginMetadataProviderFacts } from "./plugin-metadata-provider-facts.js";
|
||||
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js";
|
||||
import type { PluginRegistrySnapshot } from "./plugin-registry.js";
|
||||
import { createEmptyPluginRegistry } from "./registry-empty.js";
|
||||
|
|
@ -192,8 +194,9 @@ function createMetadataSnapshotFixture(
|
|||
},
|
||||
byPluginId: new Map(plugins.map((plugin) => [plugin.id, plugin])),
|
||||
owners: {
|
||||
channels: ownerMap([]),
|
||||
channelConfigs: ownerMap([]),
|
||||
...makeEmptyPluginMetadataOwners(),
|
||||
providerAuthContributions:
|
||||
buildPluginMetadataProviderFacts(plugins).providerAuthContributions,
|
||||
providers: ownerMap(
|
||||
plugins.flatMap((plugin) =>
|
||||
plugin.providers.map((providerId) => [providerId, [plugin.id]] as const),
|
||||
|
|
@ -213,10 +216,6 @@ function createMetadataSnapshotFixture(
|
|||
),
|
||||
),
|
||||
),
|
||||
setupProviders: ownerMap([]),
|
||||
commandAliases: ownerMap([]),
|
||||
contracts: ownerMap([]),
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ function createSnapshot(params: {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ const metadataSnapshot: PluginMetadataSnapshot = {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: [],
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import {
|
|||
} from "./configure-plan.js";
|
||||
import { getSkippedExecRefStaticError } from "./exec-resolution-policy.js";
|
||||
import type { SecretsApplyPlan } from "./plan.js";
|
||||
import { getProviderEnvVars } from "./provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "./provider-env-vars.js";
|
||||
import {
|
||||
listSecretProviderIntegrationPresets,
|
||||
type SecretProviderIntegrationPreset,
|
||||
|
|
@ -297,7 +297,7 @@ function resolveSuggestedEnvSecretId(candidate: ConfigureCandidate): string | un
|
|||
if (!hintedProvider) {
|
||||
return undefined;
|
||||
}
|
||||
const envCandidates = getProviderEnvVars(hintedProvider);
|
||||
const envCandidates = getProviderEnvVarsCore(hintedProvider);
|
||||
if (!Array.isArray(envCandidates) || envCandidates.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,20 @@ import fs from "node:fs";
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { sanitizeEnvVars } from "../agents/sandbox/sanitize-env-vars.js";
|
||||
import * as pluginConfigState from "../plugins/config-state.js";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { buildPluginMetadataProviderFacts } from "../plugins/plugin-metadata-provider-facts.js";
|
||||
import { resolveLocalProviderAuthEvidence } from "./provider-auth-evidence.js";
|
||||
import {
|
||||
getProviderEnvVars,
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
getProviderEnvVarsCore,
|
||||
listKnownProviderAuthEnvVarNamesCore,
|
||||
listKnownSecretEnvVarNames,
|
||||
resolveProviderAuthEnvVarCandidates,
|
||||
resolveProviderAuthEnvVarCandidatesCore,
|
||||
resolveProviderAuthLookupMaps,
|
||||
} from "./provider-env-vars.js";
|
||||
|
||||
type MockManifestPlugin = {
|
||||
id: string;
|
||||
origin: string;
|
||||
origin: PluginManifestRecord["origin"];
|
||||
enabled?: boolean;
|
||||
enabledByDefault?: boolean;
|
||||
kind?: "memory" | "context-engine" | Array<"memory" | "context-engine">;
|
||||
|
|
@ -78,7 +80,7 @@ function manifestRegistry(...plugins: MockManifestPlugin[]): MockManifestRegistr
|
|||
|
||||
function setupPlugin(
|
||||
id: string,
|
||||
origin: string,
|
||||
origin: PluginManifestRecord["origin"],
|
||||
provider: MockSetupProvider,
|
||||
extra: Omit<MockManifestPlugin, "id" | "origin" | "setup"> = {},
|
||||
): MockManifestPlugin {
|
||||
|
|
@ -86,7 +88,19 @@ function setupPlugin(
|
|||
}
|
||||
|
||||
function metadataSnapshot(...plugins: MockManifestPlugin[]) {
|
||||
const records: PluginManifestRecord[] = plugins.map((plugin) => ({
|
||||
channels: [],
|
||||
providers: [],
|
||||
cliBackends: [],
|
||||
skills: [],
|
||||
hooks: [],
|
||||
rootDir: `/plugins/${plugin.id}`,
|
||||
source: `/plugins/${plugin.id}/index.js`,
|
||||
manifestPath: `/plugins/${plugin.id}/openclaw.plugin.json`,
|
||||
...plugin,
|
||||
}));
|
||||
return {
|
||||
owners: buildPluginMetadataProviderFacts(records),
|
||||
index: {
|
||||
plugins: plugins.map((plugin) => ({
|
||||
pluginId: plugin.id,
|
||||
|
|
@ -112,7 +126,7 @@ function useInstalledPlugins(...plugins: MockManifestPlugin[]): void {
|
|||
|
||||
function useInstalledSetupPlugin(
|
||||
id: string,
|
||||
origin: string,
|
||||
origin: PluginManifestRecord["origin"],
|
||||
provider: MockSetupProvider,
|
||||
extra?: Omit<MockManifestPlugin, "id" | "origin" | "setup">,
|
||||
): void {
|
||||
|
|
@ -125,7 +139,11 @@ function useRegistryPlugins(...plugins: MockManifestPlugin[]): void {
|
|||
);
|
||||
}
|
||||
|
||||
function useRegistrySetupPlugin(id: string, origin: string, provider: MockSetupProvider): void {
|
||||
function useRegistrySetupPlugin(
|
||||
id: string,
|
||||
origin: PluginManifestRecord["origin"],
|
||||
provider: MockSetupProvider,
|
||||
): void {
|
||||
useRegistryPlugins(setupPlugin(id, origin, provider));
|
||||
}
|
||||
|
||||
|
|
@ -168,9 +186,11 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
{ providerAuthAliases: { "fireworks-plan": "fireworks" } },
|
||||
);
|
||||
|
||||
expect(getProviderEnvVars("fireworks", { config: {} })).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
expect(getProviderEnvVars("fireworks-plan", { config: {} })).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
expect(listKnownProviderAuthEnvVarNames()).toContain("FIREWORKS_ALT_API_KEY");
|
||||
expect(getProviderEnvVarsCore("fireworks", { config: {} })).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
expect(getProviderEnvVarsCore("fireworks-plan", { config: {} })).toEqual([
|
||||
"FIREWORKS_ALT_API_KEY",
|
||||
]);
|
||||
expect(listKnownProviderAuthEnvVarNamesCore()).toContain("FIREWORKS_ALT_API_KEY");
|
||||
expect(listKnownSecretEnvVarNames()).toContain("FIREWORKS_ALT_API_KEY");
|
||||
});
|
||||
|
||||
|
|
@ -184,10 +204,10 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(listKnownProviderAuthEnvVarNames()).toContain("PROVIDER_BILLING_CREDENTIAL");
|
||||
expect(listKnownProviderAuthEnvVarNamesCore()).toContain("PROVIDER_BILLING_CREDENTIAL");
|
||||
expect(listKnownSecretEnvVarNames()).toContain("PROVIDER_BILLING_CREDENTIAL");
|
||||
expect(resolveProviderAuthEnvVarCandidates()["provider-billing"]).toBeUndefined();
|
||||
expect(getProviderEnvVars("provider-billing")).toStrictEqual([]);
|
||||
expect(resolveProviderAuthEnvVarCandidatesCore()["provider-billing"]).toBeUndefined();
|
||||
expect(getProviderEnvVarsCore("provider-billing")).toStrictEqual([]);
|
||||
expect(
|
||||
sanitizeEnvVars({ PROVIDER_BILLING_CREDENTIAL: "billing-secret", SAFE_VALUE: "ok" }),
|
||||
).toMatchObject({
|
||||
|
|
@ -199,6 +219,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
it("scrubs usage credentials using host metadata rather than the candidate sandbox env", () => {
|
||||
const configuredSnapshot = {
|
||||
workspaceDir: "/workspace",
|
||||
owners: buildPluginMetadataProviderFacts([]),
|
||||
index: {
|
||||
plugins: [
|
||||
{
|
||||
|
|
@ -251,7 +272,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
});
|
||||
|
||||
it("lets openai bootstrap from Codex app-server API-key env", () => {
|
||||
expect(resolveProviderAuthEnvVarCandidates()["openai"]).toEqual([
|
||||
expect(resolveProviderAuthEnvVarCandidatesCore()["openai"]).toEqual([
|
||||
"CODEX_API_KEY",
|
||||
"OPENAI_API_KEY",
|
||||
]);
|
||||
|
|
@ -263,8 +284,10 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
envVars: ["MODEL_STUDIO_API_KEY", "MODEL_STUDIO_API_KEY"],
|
||||
});
|
||||
|
||||
expect(getProviderEnvVars("model-studio", { config: {} })).toEqual(["MODEL_STUDIO_API_KEY"]);
|
||||
expect(listKnownProviderAuthEnvVarNames()).toContain("MODEL_STUDIO_API_KEY");
|
||||
expect(getProviderEnvVarsCore("model-studio", { config: {} })).toEqual([
|
||||
"MODEL_STUDIO_API_KEY",
|
||||
]);
|
||||
expect(listKnownProviderAuthEnvVarNamesCore()).toContain("MODEL_STUDIO_API_KEY");
|
||||
expect(listKnownSecretEnvVarNames()).toContain("MODEL_STUDIO_API_KEY");
|
||||
});
|
||||
|
||||
|
|
@ -516,7 +539,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
);
|
||||
|
||||
expect(
|
||||
resolveProviderAuthEnvVarCandidates({ config: {} })["load-path-provider"],
|
||||
resolveProviderAuthEnvVarCandidatesCore({ config: {} })["load-path-provider"],
|
||||
).toBeUndefined();
|
||||
expect(pluginRegistryMocks.getCurrentPluginMetadataSnapshot).toHaveBeenCalledWith({
|
||||
env: process.env,
|
||||
|
|
@ -578,7 +601,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
envVars: ["FIREWORKS_API_KEY", "FIREWORKS_SETUP_KEY", "FIREWORKS_API_KEY"],
|
||||
});
|
||||
|
||||
expect(getProviderEnvVars("fireworks", { config: {} })).toEqual([
|
||||
expect(getProviderEnvVarsCore("fireworks", { config: {} })).toEqual([
|
||||
"FIREWORKS_API_KEY",
|
||||
"FIREWORKS_SETUP_KEY",
|
||||
]);
|
||||
|
|
@ -594,11 +617,11 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
const mod = await import("./provider-env-vars.js");
|
||||
|
||||
expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).not.toHaveBeenCalled();
|
||||
expect(mod.getProviderEnvVars("fireworks")).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
expect(mod.getProviderEnvVarsCore("fireworks")).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
const initialLoads =
|
||||
pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mock.calls.length;
|
||||
expect(initialLoads).toBeGreaterThan(0);
|
||||
expect(mod.getProviderEnvVars("fireworks")).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
expect(mod.getProviderEnvVarsCore("fireworks")).toEqual(["FIREWORKS_ALT_API_KEY"]);
|
||||
expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledTimes(
|
||||
initialLoads,
|
||||
);
|
||||
|
|
@ -613,8 +636,8 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
vi.resetModules();
|
||||
const mod = await import("./provider-env-vars.js");
|
||||
|
||||
expect(mod.getProviderEnvVars("whisperx")).toEqual(["WHISPERX_API_KEY"]);
|
||||
expect(mod.listKnownProviderAuthEnvVarNames()).toContain("WHISPERX_API_KEY");
|
||||
expect(mod.getProviderEnvVarsCore("whisperx")).toEqual(["WHISPERX_API_KEY"]);
|
||||
expect(mod.listKnownProviderAuthEnvVarNamesCore()).toContain("WHISPERX_API_KEY");
|
||||
});
|
||||
|
||||
it("excludes untrusted workspace plugin env vars when requested", async () => {
|
||||
|
|
@ -638,25 +661,25 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
const mod = await import("./provider-env-vars.js");
|
||||
|
||||
expect(
|
||||
mod.getProviderEnvVars("whisperx", {
|
||||
mod.getProviderEnvVarsCore("whisperx", {
|
||||
config: { plugins: {} },
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
}),
|
||||
).toStrictEqual([]);
|
||||
expect(
|
||||
mod.getProviderEnvVars("workspace-setup", {
|
||||
mod.getProviderEnvVarsCore("workspace-setup", {
|
||||
config: { plugins: {} },
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
}),
|
||||
).toStrictEqual([]);
|
||||
expect(
|
||||
mod.listKnownProviderAuthEnvVarNames({
|
||||
mod.listKnownProviderAuthEnvVarNamesCore({
|
||||
config: { plugins: {} },
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
}),
|
||||
).not.toContain("AWS_SECRET_ACCESS_KEY");
|
||||
expect(
|
||||
mod.listKnownProviderAuthEnvVarNames({
|
||||
mod.listKnownProviderAuthEnvVarNamesCore({
|
||||
config: { plugins: {} },
|
||||
includeUntrustedWorkspacePlugins: false,
|
||||
}),
|
||||
|
|
@ -672,7 +695,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
const mod = await import("./provider-env-vars.js");
|
||||
|
||||
expect(
|
||||
mod.getProviderEnvVars("whisperx", {
|
||||
mod.getProviderEnvVarsCore("whisperx", {
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["workspace-audio"],
|
||||
|
|
@ -692,7 +715,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
const mod = await import("./provider-env-vars.js");
|
||||
|
||||
expect(
|
||||
mod.getProviderEnvVars("whisperx", {
|
||||
mod.getProviderEnvVarsCore("whisperx", {
|
||||
config: {
|
||||
plugins: {
|
||||
slots: {
|
||||
|
|
@ -716,7 +739,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
const mod = await import("./provider-env-vars.js");
|
||||
|
||||
expect(
|
||||
mod.getProviderEnvVars("whisperx", {
|
||||
mod.getProviderEnvVarsCore("whisperx", {
|
||||
config: {
|
||||
plugins: {
|
||||
slots: {
|
||||
|
|
@ -732,13 +755,13 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
it.each([
|
||||
{
|
||||
name: "auth candidates",
|
||||
resolve: () => resolveProviderAuthEnvVarCandidates({ config: {} }).fireworks,
|
||||
resolve: () => resolveProviderAuthEnvVarCandidatesCore({ config: {} }).fireworks,
|
||||
metadataLoads: 1,
|
||||
},
|
||||
{
|
||||
name: "auth scrub keys",
|
||||
resolve: () =>
|
||||
listKnownProviderAuthEnvVarNames({ config: {} }).filter((key) =>
|
||||
listKnownProviderAuthEnvVarNamesCore({ config: {} }).filter((key) =>
|
||||
key.startsWith("FIREWORKS_"),
|
||||
),
|
||||
metadataLoads: 2,
|
||||
|
|
@ -918,7 +941,7 @@ describe("provider env vars dynamic manifest metadata", () => {
|
|||
},
|
||||
);
|
||||
|
||||
expect(resolveProviderAuthEnvVarCandidates()["load-path-provider"]).toBeUndefined();
|
||||
expect(resolveProviderAuthEnvVarCandidatesCore()["load-path-provider"]).toBeUndefined();
|
||||
expect(pluginRegistryMocks.getCurrentPluginMetadataSnapshot).toHaveBeenCalledWith({
|
||||
env: process.env,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/** Tests provider env-var candidate and auth evidence lookup. */
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
getProviderEnvVars,
|
||||
listKnownProviderAuthEnvVarNames,
|
||||
getProviderEnvVarsCore,
|
||||
listKnownProviderAuthEnvVarNamesCore,
|
||||
listKnownSecretEnvVarNames,
|
||||
omitEnvKeysCaseInsensitive,
|
||||
} from "./provider-env-vars.js";
|
||||
|
|
@ -19,7 +19,7 @@ describe("provider env vars", () => {
|
|||
"OPENROUTER_API_KEY",
|
||||
"TAVILY_API_KEY",
|
||||
];
|
||||
const providerAuthNames = listKnownProviderAuthEnvVarNames();
|
||||
const providerAuthNames = listKnownProviderAuthEnvVarNamesCore();
|
||||
const secretNames = listKnownSecretEnvVarNames();
|
||||
for (const name of sharedSecretNames) {
|
||||
expect(providerAuthNames).toContain(name);
|
||||
|
|
@ -38,8 +38,8 @@ describe("provider env vars", () => {
|
|||
|
||||
it.each(["GH_TOKEN", "GITHUB_TOKEN"])("audits %s without activating a provider", (name) => {
|
||||
expect(listKnownSecretEnvVarNames()).toContain(name);
|
||||
expect(listKnownProviderAuthEnvVarNames()).not.toContain(name);
|
||||
expect(getProviderEnvVars("github-copilot")).not.toContain(name);
|
||||
expect(listKnownProviderAuthEnvVarNamesCore()).not.toContain(name);
|
||||
expect(getProviderEnvVarsCore("github-copilot")).not.toContain(name);
|
||||
});
|
||||
|
||||
it("omits env keys case-insensitively", () => {
|
||||
|
|
@ -58,10 +58,13 @@ describe("provider env vars", () => {
|
|||
});
|
||||
|
||||
it("ignores prototype-chain keys when resolving provider env vars", () => {
|
||||
expect(getProviderEnvVars("__proto__")).toStrictEqual([]);
|
||||
expect(getProviderEnvVars("constructor")).toStrictEqual([]);
|
||||
expect(getProviderEnvVars("openai")).toEqual(["CODEX_API_KEY", "OPENAI_API_KEY"]);
|
||||
expect(getProviderEnvVars("anthropic")).toEqual(["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"]);
|
||||
expect(getProviderEnvVars("fal")).toEqual(["FAL_KEY", "FAL_API_KEY"]);
|
||||
expect(getProviderEnvVarsCore("__proto__")).toStrictEqual([]);
|
||||
expect(getProviderEnvVarsCore("constructor")).toStrictEqual([]);
|
||||
expect(getProviderEnvVarsCore("openai")).toEqual(["CODEX_API_KEY", "OPENAI_API_KEY"]);
|
||||
expect(getProviderEnvVarsCore("anthropic")).toEqual([
|
||||
"ANTHROPIC_OAUTH_TOKEN",
|
||||
"ANTHROPIC_API_KEY",
|
||||
]);
|
||||
expect(getProviderEnvVarsCore("fal")).toEqual(["FAL_KEY", "FAL_API_KEY"]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import {
|
|||
loadPluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
} from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { listSetupProviderIds } from "../plugins/setup-descriptors.js";
|
||||
import { hasKind } from "../plugins/slots.js";
|
||||
import { appendUniqueEnvVarCandidates } from "../shared/env-var-candidates.js";
|
||||
|
||||
|
|
@ -209,11 +208,11 @@ function resolveManifestProviderAuthEnvVarCandidates(
|
|||
sortedAliases: readonly (readonly [string, string])[],
|
||||
): Record<string, string[]> {
|
||||
const candidates: Record<string, string[]> = {};
|
||||
for (const plugin of snapshot.plugins) {
|
||||
if (!shouldUsePluginProviderEnvVars(plugin, params)) {
|
||||
for (const { plugin, envProviders } of snapshot.owners.providerAuthContributions) {
|
||||
if (envProviders.length === 0 || !shouldUsePluginProviderEnvVars(plugin, params)) {
|
||||
continue;
|
||||
}
|
||||
for (const provider of plugin.setup?.providers ?? []) {
|
||||
for (const provider of envProviders) {
|
||||
appendUniqueEnvVarCandidates(candidates, provider.id, provider.envVars ?? []);
|
||||
}
|
||||
}
|
||||
|
|
@ -235,15 +234,9 @@ function resolveManifestRuntimeAuthFacts(
|
|||
const evidenceByProvider: Record<string, ProviderAuthEvidence[]> = {};
|
||||
const refs = new Set<string>();
|
||||
const isEnabled = createInstalledPluginEnabledPredicate(snapshot.index.plugins, params?.config);
|
||||
for (const plugin of snapshot.plugins) {
|
||||
const evidenceProviders = (plugin.setup?.providers ?? []).filter(
|
||||
(provider) => provider.authEvidence?.length,
|
||||
);
|
||||
const fallbackProviders =
|
||||
plugin.setup?.requiresRuntime !== false && (plugin.setup?.providers || plugin.providers)
|
||||
? listSetupProviderIds(plugin)
|
||||
: [];
|
||||
if (evidenceProviders.length === 0 && fallbackProviders.length === 0) {
|
||||
for (const { plugin, evidenceProviders, fallbackProviderRefs } of snapshot.owners
|
||||
.providerAuthContributions) {
|
||||
if (evidenceProviders.length === 0 && fallbackProviderRefs.length === 0) {
|
||||
continue;
|
||||
}
|
||||
// Package contributions are fixed, but their eligibility follows current config.
|
||||
|
|
@ -256,8 +249,8 @@ function resolveManifestRuntimeAuthFacts(
|
|||
appendUniqueAuthEvidence(evidenceByProvider, provider.id, provider.authEvidence ?? []);
|
||||
}
|
||||
}
|
||||
for (const providerId of fallbackProviders) {
|
||||
appendUniqueProviderRef(refs, providerId);
|
||||
for (const providerId of fallbackProviderRefs) {
|
||||
refs.add(providerId);
|
||||
}
|
||||
}
|
||||
for (const [alias, target] of sortedAliases) {
|
||||
|
|
@ -279,7 +272,7 @@ function resolveManifestRuntimeAuthFacts(
|
|||
}
|
||||
|
||||
/** Resolves provider auth env-var candidates from core fallbacks and plugin metadata. */
|
||||
export function resolveProviderAuthEnvVarCandidates(
|
||||
export function resolveProviderAuthEnvVarCandidatesCore(
|
||||
params?: ProviderEnvVarLookupParams,
|
||||
): Record<string, readonly string[]> {
|
||||
const snapshot = resolveProviderMetadataSnapshot(params);
|
||||
|
|
@ -375,16 +368,16 @@ function createLazyReadonlyRecord(
|
|||
* overrides where generic onboarding wants a different preferred env var.
|
||||
*/
|
||||
const PROVIDER_ENV_VARS = createLazyReadonlyRecord(() =>
|
||||
withSetupEnvOverrides(resolveProviderAuthEnvVarCandidates()),
|
||||
withSetupEnvOverrides(resolveProviderAuthEnvVarCandidatesCore()),
|
||||
);
|
||||
|
||||
/** Returns known env var candidates for a provider id or alias. */
|
||||
export function getProviderEnvVars(
|
||||
export function getProviderEnvVarsCore(
|
||||
providerId: string,
|
||||
params?: ProviderEnvVarLookupParams,
|
||||
): string[] {
|
||||
const providerEnvVars = params
|
||||
? withSetupEnvOverrides(resolveProviderAuthEnvVarCandidates(params))
|
||||
? withSetupEnvOverrides(resolveProviderAuthEnvVarCandidatesCore(params))
|
||||
: PROVIDER_ENV_VARS;
|
||||
const envVars = Object.hasOwn(providerEnvVars, providerId)
|
||||
? providerEnvVars[providerId]
|
||||
|
|
@ -395,8 +388,10 @@ export function getProviderEnvVars(
|
|||
// OPENCLAW_API_KEY authenticates the local OpenClaw bridge itself and must
|
||||
// remain available to child bridge/runtime processes.
|
||||
/** Lists known provider auth env vars without bridge-only env vars. */
|
||||
export function listKnownProviderAuthEnvVarNames(params?: ProviderEnvVarLookupParams): string[] {
|
||||
const authCandidates = resolveProviderAuthEnvVarCandidates(params);
|
||||
export function listKnownProviderAuthEnvVarNamesCore(
|
||||
params?: ProviderEnvVarLookupParams,
|
||||
): string[] {
|
||||
const authCandidates = resolveProviderAuthEnvVarCandidatesCore(params);
|
||||
// Keep auth-only candidates before setup overrides, then append usage-only hints.
|
||||
return uniqueStrings([
|
||||
...Object.values(authCandidates).flat(),
|
||||
|
|
@ -410,7 +405,7 @@ export async function listKnownProviderAuthEnvVarNamesAsync(
|
|||
params?: ProviderEnvVarLookupParams,
|
||||
): Promise<string[]> {
|
||||
if (params?.metadataSnapshot) {
|
||||
return listKnownProviderAuthEnvVarNames(params);
|
||||
return listKnownProviderAuthEnvVarNamesCore(params);
|
||||
}
|
||||
const env = cloneEnvWithPlatformSemantics(params?.env ?? process.env);
|
||||
const lookup = { ...params, env };
|
||||
|
|
@ -426,7 +421,7 @@ export async function listKnownProviderAuthEnvVarNamesAsync(
|
|||
activate();
|
||||
metadataSnapshot = resolveProviderMetadataSnapshot(lookup);
|
||||
}
|
||||
return listKnownProviderAuthEnvVarNames({ ...lookup, metadataSnapshot });
|
||||
return listKnownProviderAuthEnvVarNamesCore({ ...lookup, metadataSnapshot });
|
||||
});
|
||||
} finally {
|
||||
release();
|
||||
|
|
@ -438,7 +433,7 @@ export function listKnownSecretEnvVarNames(params?: ProviderEnvVarLookupParams):
|
|||
return uniqueStrings([
|
||||
"GH_TOKEN",
|
||||
"GITHUB_TOKEN",
|
||||
...Object.values(withSetupEnvOverrides(resolveProviderAuthEnvVarCandidates(params))).flat(),
|
||||
...Object.values(withSetupEnvOverrides(resolveProviderAuthEnvVarCandidatesCore(params))).flat(),
|
||||
...resolveManifestProviderUsageAuthEnvVarNames(params),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// Workspace skill loader tests cover source merging, metadata, filtering, and precedence.
|
||||
import fsSync from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
|
|
@ -13,6 +12,8 @@ import type {
|
|||
PluginManifestRecord,
|
||||
PluginManifestRegistry,
|
||||
} from "../../plugins/manifest-registry.js";
|
||||
// Workspace skill loader tests cover source merging, metadata, filtering, and precedence.
|
||||
import { buildPluginMetadataProviderFacts } from "../../plugins/plugin-metadata-provider-facts.js";
|
||||
import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.js";
|
||||
import { buildDeclaredProviderOwnerIndex } from "../../plugins/provider-owner-index.js";
|
||||
import { setActiveDegradedSecretOwners } from "../../secrets/runtime-degraded-state.js";
|
||||
|
|
@ -356,6 +357,8 @@ function createWorkspacePluginMetadataSnapshot(params: {
|
|||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
providerAuthContributions: buildPluginMetadataProviderFacts(params.manifestRegistry.plugins)
|
||||
.providerAuthContributions,
|
||||
modelIdNormalizationPolicies: new Map(),
|
||||
};
|
||||
const index: PluginMetadataSnapshot["index"] = {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// Live-test helpers for generation provider credentials and config loading.
|
||||
import { loadShellEnvFallback } from "../infra/shell-env.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
|
||||
/** Loads shell env only when a live generation provider declares missing key names. */
|
||||
export function maybeLoadShellEnvForGenerationProviders(providerIds: string[]): void {
|
||||
const expectedKeys = [
|
||||
...new Set(providerIds.flatMap((providerId) => getProviderEnvVars(providerId))),
|
||||
...new Set(providerIds.flatMap((providerId) => getProviderEnvVarsCore(providerId))),
|
||||
];
|
||||
if (expectedKeys.length === 0) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
buildCapabilityProviderIndex,
|
||||
normalizeCapabilityProviderId,
|
||||
} from "../plugins/provider-registry-shared.js";
|
||||
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
||||
import { getProviderEnvVarsCore } from "../secrets/provider-env-vars.js";
|
||||
import { resolveVideoGenerationModeCapabilities } from "./capabilities.js";
|
||||
import {
|
||||
buildVideoGenerationCapabilityFailure,
|
||||
|
|
@ -38,7 +38,7 @@ const SUPPORTED_DURATIONS_HINT = Symbol.for("openclaw.videoGeneration.supportedD
|
|||
type VideoGenerationRuntimeDeps = {
|
||||
getProvider?: typeof getVideoGenerationProvider;
|
||||
listProviders?: typeof listVideoGenerationProviders;
|
||||
getProviderEnvVars?: typeof getProviderEnvVars;
|
||||
getProviderEnvVars?: typeof getProviderEnvVarsCore;
|
||||
log?: Pick<typeof log, "debug" | "warn">;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -185,8 +185,8 @@ async function collectProviderApiKeysForLiveMedia(provider: string): Promise<unk
|
|||
}
|
||||
|
||||
async function getProviderEnvVarsForLiveMedia(provider: string): Promise<string[]> {
|
||||
const { getProviderEnvVars } = await import("../../../../src/secrets/provider-env-vars.js");
|
||||
return getProviderEnvVars(provider);
|
||||
const { getProviderEnvVarsCore } = await import("../../../../src/secrets/provider-env-vars.js");
|
||||
return getProviderEnvVarsCore(provider);
|
||||
}
|
||||
|
||||
async function loadShellEnvFallbackForLiveMedia(params: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { AgentEventPayload } from "../../src/infra/agent-events.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../../src/secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../../src/secrets/provider-env-vars.js";
|
||||
// Native live fixture setup and capture shared with its offline boundary regressions.
|
||||
import { createOpenClawTestInstance } from "./openclaw-test-instance.js";
|
||||
|
||||
|
|
@ -13,7 +13,9 @@ export function createCodexHarnessLiveInstance(
|
|||
state: { layout: "state-only" },
|
||||
gatewayToken: token,
|
||||
env: {
|
||||
...Object.fromEntries(listKnownProviderAuthEnvVarNames().map((name) => [name, undefined])),
|
||||
...Object.fromEntries(
|
||||
listKnownProviderAuthEnvVarNamesCore().map((name) => [name, undefined]),
|
||||
),
|
||||
OPENCLAW_AGENT_RUNTIME: "codex",
|
||||
OPENCLAW_GATEWAY_TOKEN: token,
|
||||
OPENCLAW_ALLOW_SLOW_REPLY_TESTS: "1",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
connectGatewayClient,
|
||||
disconnectGatewayClient,
|
||||
} from "../../src/gateway/test-helpers.e2e.js";
|
||||
import { listKnownProviderAuthEnvVarNames } from "../../src/secrets/provider-env-vars.js";
|
||||
import { listKnownProviderAuthEnvVarNamesCore } from "../../src/secrets/provider-env-vars.js";
|
||||
import {
|
||||
closeOpenClawAgentDatabaseByPath,
|
||||
closeOpenClawAgentDatabasesForTest,
|
||||
|
|
@ -109,7 +109,9 @@ export async function runSqliteSessionsTranscriptsFlipProof(options: RunOptions
|
|||
const inst = await createOpenClawTestInstance({
|
||||
name: `sqlite-sessions-transcripts-flip-${randomUUID()}`,
|
||||
env: {
|
||||
...Object.fromEntries(listKnownProviderAuthEnvVarNames().map((name) => [name, undefined])),
|
||||
...Object.fromEntries(
|
||||
listKnownProviderAuthEnvVarNamesCore().map((name) => [name, undefined]),
|
||||
),
|
||||
ALL_PROXY: undefined,
|
||||
HTTP_PROXY: undefined,
|
||||
HTTPS_PROXY: undefined,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue