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:
Peter Steinberger 2026-09-18 03:00:18 -07:00 • committed by GitHub
parent f81c155d02
commit fdbab4ce99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 493 additions and 312 deletions

View file

@ -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);
}
}

View file

@ -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",

View file

@ -21,6 +21,7 @@ function buildOwners(): PluginMetadataSnapshotOwnerMaps {
setupProviders: empty,
commandAliases: empty,
contracts: empty,
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
providerEndpoints: [
{ endpointClass: "openai-public", hosts: ["prepared.example"] },

View file

@ -36,6 +36,7 @@ export const emptyPluginMetadataSnapshot: PluginMetadataSnapshot = {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -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,

View file

@ -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,

View file

@ -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[];

View file

@ -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(),

View file

@ -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();
}

View file

@ -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);
});

View file

@ -100,6 +100,7 @@ const emptyPluginMetadataSnapshot = vi.hoisted(() => ({
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -89,6 +89,7 @@ function metadataOwners(
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
...overrides,
};

View file

@ -26,6 +26,7 @@ const mocks = vi.hoisted(() => {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
};

View file

@ -35,6 +35,7 @@ const preparedModelRuntimeMocks = vi.hoisted(() => ({
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
},

View file

@ -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"] },
],
},
],
}),
};
});

View file

@ -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,

View file

@ -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",
);

View file

@ -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" }]]),

View file

@ -72,6 +72,7 @@ function createRegistry(
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
},

View file

@ -88,6 +88,7 @@ export function pluginOwnerSnapshotEntries(
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
};

View file

@ -64,6 +64,7 @@ export function createEmptyPluginMetadataSnapshot(workspaceDir?: string): Plugin
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -346,7 +346,7 @@ describe("createImageGenerateTool", () => {
);
return {
...actual,
getProviderEnvVars: (providerId: string) => {
getProviderEnvVarsCore: (providerId: string) => {
if (providerId === "google") {
return ["GEMINI_API_KEY", "GOOGLE_API_KEY"];
}

View file

@ -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 =

View file

@ -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);

View file

@ -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,
}),

View file

@ -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,
};

View file

@ -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,
});

View file

@ -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,
),
);

View file

@ -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,
]),

View file

@ -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`;
}

View file

@ -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: {},

View file

@ -40,6 +40,7 @@ const emptyPluginMetadataSnapshot = vi.hoisted(() => ({
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -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: {

View file

@ -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: {

View file

@ -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,
]);

View file

@ -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,

View file

@ -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,

View file

@ -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: {} },
}),
}

View file

@ -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,

View file

@ -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 ?? [],

View file

@ -28,6 +28,7 @@ const pluginMetadataSnapshot = vi.hoisted((): PluginMetadataSnapshot => {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
};
const zeroMetrics = {

View file

@ -88,6 +88,7 @@ const pluginMetadataSnapshot = vi.hoisted((): PluginMetadataSnapshot => {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -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,

View file

@ -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">;
};

View file

@ -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`,

View file

@ -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;
};

View file

@ -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",

View file

@ -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"],

View file

@ -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 : {}),

View file

@ -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),

View file

@ -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">;
};

View file

@ -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"]

View file

@ -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"
>
>;
};
};

View file

@ -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";

View file

@ -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,

View 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");
},
);
});

View file

@ -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));
}

View file

@ -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: {

View file

@ -73,6 +73,7 @@ export function makeEmptyPluginMetadataOwners(): PluginMetadataSnapshot["owners"
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
};
}

View file

@ -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),
};

View file

@ -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>;

View file

@ -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,
});

View file

@ -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,
});

View file

@ -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);

View file

@ -27,6 +27,7 @@ function makeOwners(provider: string): PluginMetadataSnapshotOwnerMaps {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
};
}

View file

@ -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(),
},
};
}

View file

@ -54,6 +54,7 @@ function createSnapshot(params: {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -48,6 +48,7 @@ const metadataSnapshot: PluginMetadataSnapshot = {
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
providerAuthContributions: [],
modelIdNormalizationPolicies: new Map(),
},
metrics: {

View file

@ -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;
}

View file

@ -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,

View file

@ -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"]);
});
});

View file

@ -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),
]);
}

View file

@ -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"] = {

View file

@ -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;

View file

@ -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">;
};

View file

@ -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: {

View file

@ -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",

View file

@ -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,