mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix: align model auth display with workspace evidence
This commit is contained in:
@@ -16,9 +16,16 @@ const modelCatalogMocks = vi.hoisted(() => ({
|
||||
const modelAuthLabelMocks = vi.hoisted(() => ({
|
||||
resolveModelAuthLabel: vi.fn<(params: unknown) => string | undefined>(() => undefined),
|
||||
}));
|
||||
const modelProviderAuthMocks = vi.hoisted(() => ({
|
||||
authenticatedProviders: new Set(["anthropic", "google", "openai"]),
|
||||
}));
|
||||
const modelProviderAuthMocks = vi.hoisted(() => {
|
||||
const state = {
|
||||
authenticatedProviders: new Set(["anthropic", "google", "openai"]),
|
||||
createProviderAuthChecker: vi.fn(),
|
||||
};
|
||||
state.createProviderAuthChecker.mockImplementation(
|
||||
() => (provider: string) => state.authenticatedProviders.has(provider),
|
||||
);
|
||||
return state;
|
||||
});
|
||||
|
||||
const MODELS_ADD_DEPRECATED_TEXT =
|
||||
"⚠️ /models add is deprecated. Use /models to browse providers and /model to switch models.";
|
||||
@@ -32,8 +39,7 @@ vi.mock("../../agents/model-auth-label.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/model-provider-auth.js", () => ({
|
||||
createProviderAuthChecker: () => (provider: string) =>
|
||||
modelProviderAuthMocks.authenticatedProviders.has(provider),
|
||||
createProviderAuthChecker: modelProviderAuthMocks.createProviderAuthChecker,
|
||||
hasAuthForModelProvider: ({ provider }: { provider: string }) =>
|
||||
modelProviderAuthMocks.authenticatedProviders.has(provider),
|
||||
}));
|
||||
@@ -104,6 +110,7 @@ beforeEach(() => {
|
||||
modelAuthLabelMocks.resolveModelAuthLabel.mockReset();
|
||||
modelAuthLabelMocks.resolveModelAuthLabel.mockReturnValue(undefined);
|
||||
modelProviderAuthMocks.authenticatedProviders = new Set(["anthropic", "google", "openai"]);
|
||||
modelProviderAuthMocks.createProviderAuthChecker.mockClear();
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
...textSurfaceModelsTestPlugins,
|
||||
@@ -179,6 +186,9 @@ describe("handleModelsCommand", () => {
|
||||
expect(result?.reply?.text).toContain("Use: /models <provider>");
|
||||
expect(result?.reply?.text).toContain("Switch: /model <provider/model>");
|
||||
expect(result?.reply?.text).not.toContain("Add: /models add");
|
||||
expect(modelProviderAuthMocks.createProviderAuthChecker).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ workspaceDir: "/tmp" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("hides unauthenticated providers by default and keeps all as explicit browse", async () => {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { resolveAgentDir, resolveSessionAgentId } from "../../agents/agent-scope.js";
|
||||
import {
|
||||
resolveAgentDir,
|
||||
resolveAgentWorkspaceDir,
|
||||
resolveSessionAgentId,
|
||||
} from "../../agents/agent-scope.js";
|
||||
import { resolveModelAuthLabel } from "../../agents/model-auth-label.js";
|
||||
import { resolveVisibleModelCatalog } from "../../agents/model-catalog-visibility.js";
|
||||
import { loadModelCatalog } from "../../agents/model-catalog.js";
|
||||
@@ -11,6 +15,7 @@ import {
|
||||
resolveDefaultModelForAgent,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
||||
import { getChannelPlugin } from "../../channels/plugins/index.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
@@ -63,7 +68,7 @@ type ParsedModelsCommand =
|
||||
export async function buildModelsProviderData(
|
||||
cfg: OpenClawConfig,
|
||||
agentId?: string,
|
||||
options: { view?: "default" | "all" } = {},
|
||||
options: { view?: "default" | "all"; workspaceDir?: string } = {},
|
||||
): Promise<ModelsProviderData> {
|
||||
const resolvedDefault = resolveDefaultModelForAgent({
|
||||
cfg,
|
||||
@@ -77,6 +82,10 @@ export async function buildModelsProviderData(
|
||||
defaultProvider: resolvedDefault.provider,
|
||||
defaultModel: resolvedDefault.model,
|
||||
agentId,
|
||||
workspaceDir:
|
||||
options.workspaceDir ??
|
||||
(agentId ? resolveAgentWorkspaceDir(cfg, agentId) : undefined) ??
|
||||
resolveDefaultAgentWorkspaceDir(),
|
||||
view: options.view,
|
||||
});
|
||||
|
||||
@@ -329,6 +338,7 @@ export async function resolveModelsCommandReply(params: {
|
||||
currentModel?: string;
|
||||
agentId?: string;
|
||||
agentDir?: string;
|
||||
workspaceDir?: string;
|
||||
sessionEntry?: ModelsCommandSessionEntry;
|
||||
}): Promise<ReplyPayload | null> {
|
||||
const body = params.commandBodyNormalized.trim();
|
||||
@@ -342,7 +352,10 @@ export async function resolveModelsCommandReply(params: {
|
||||
const { byProvider, providers, modelNames } = await buildModelsProviderData(
|
||||
params.cfg,
|
||||
params.agentId,
|
||||
parsed.action === "list" && parsed.all ? { view: "all" } : undefined,
|
||||
{
|
||||
...(parsed.action === "list" && parsed.all ? { view: "all" as const } : {}),
|
||||
workspaceDir: params.workspaceDir,
|
||||
},
|
||||
);
|
||||
const commandPlugin = params.surface ? getChannelPlugin(params.surface) : null;
|
||||
const providerInfos = buildProviderInfos({ providers, byProvider });
|
||||
@@ -523,6 +536,7 @@ export const handleModelsCommand: CommandHandler = async (params, allowTextComma
|
||||
currentModel: params.model ? `${params.provider}/${params.model}` : undefined,
|
||||
agentId: modelsAgentId,
|
||||
agentDir: modelsAgentDir,
|
||||
workspaceDir: modelsAgentId === currentAgentId ? params.workspaceDir : undefined,
|
||||
sessionEntry: targetSessionEntry,
|
||||
});
|
||||
if (!reply) {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
|
||||
let modelsListCommand: typeof import("./models/list.list-command.js").modelsListCommand;
|
||||
let loadModelRegistry: typeof import("./models/list.registry.js").loadModelRegistry;
|
||||
@@ -333,6 +337,35 @@ describe("models list/status", () => {
|
||||
modelRegistryState.available = available ? [ZAI_MODEL, OPENAI_MODEL] : [];
|
||||
}
|
||||
|
||||
async function writeWorkspaceAuthEvidencePlugin(workspaceDir: string) {
|
||||
const pluginDir = path.join(workspaceDir, ".openclaw", "extensions", "workspace-cloud");
|
||||
await fs.mkdir(pluginDir, { recursive: true });
|
||||
await fs.writeFile(path.join(pluginDir, "index.ts"), "export default {}\n", "utf8");
|
||||
await fs.writeFile(
|
||||
path.join(pluginDir, "openclaw.plugin.json"),
|
||||
JSON.stringify({
|
||||
id: "workspace-cloud",
|
||||
configSchema: { type: "object" },
|
||||
setup: {
|
||||
providers: [
|
||||
{
|
||||
id: "workspace-cloud",
|
||||
authEvidence: [
|
||||
{
|
||||
type: "local-file-with-env",
|
||||
fileEnvVar: "WORKSPACE_CLOUD_CREDENTIALS",
|
||||
credentialMarker: "workspace-cloud-local-credentials",
|
||||
source: "workspace cloud credentials",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
({ modelsListCommand } = await import("./models/list.list-command.js"));
|
||||
({ loadModelRegistry, toModelRow } = await import("./models/list.registry.js"));
|
||||
@@ -410,6 +443,67 @@ describe("models list/status", () => {
|
||||
expect(payload.models[0]?.available).toBe(false);
|
||||
});
|
||||
|
||||
it("models list uses trusted workspace plugin auth evidence for configured rows", async () => {
|
||||
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-models-list-auth-"));
|
||||
const workspaceDir = path.join(tempRoot, "workspace");
|
||||
const bundledDir = path.join(tempRoot, "bundled");
|
||||
const stateDir = path.join(tempRoot, "state");
|
||||
const credentialsPath = path.join(tempRoot, "credentials.json");
|
||||
await fs.mkdir(bundledDir, { recursive: true });
|
||||
await fs.mkdir(stateDir, { recursive: true });
|
||||
await fs.writeFile(credentialsPath, "{}", "utf8");
|
||||
await writeWorkspaceAuthEvidencePlugin(workspaceDir);
|
||||
getRuntimeConfig.mockReturnValue({
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: workspaceDir,
|
||||
model: "workspace-cloud/model-a",
|
||||
},
|
||||
},
|
||||
plugins: { allow: ["workspace-cloud"] },
|
||||
models: {
|
||||
providers: {
|
||||
"workspace-cloud": {
|
||||
baseUrl: "https://workspace-cloud.example/v1",
|
||||
api: "openai-responses",
|
||||
models: [
|
||||
{
|
||||
id: "model-a",
|
||||
name: "Workspace Cloud Model A",
|
||||
input: ["text"],
|
||||
contextWindow: 8192,
|
||||
maxTokens: 4096,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const runtime = makeRuntime();
|
||||
|
||||
try {
|
||||
await withEnvAsync(
|
||||
{
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: bundledDir,
|
||||
OPENCLAW_STATE_DIR: stateDir,
|
||||
WORKSPACE_CLOUD_CREDENTIALS: credentialsPath,
|
||||
},
|
||||
() => modelsListCommand({ all: true, provider: "workspace-cloud", json: true }, runtime),
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const payload = parseJsonLog(runtime);
|
||||
expect(payload.models).toEqual([
|
||||
expect.objectContaining({
|
||||
key: "workspace-cloud/model-a",
|
||||
available: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("models list all includes unauthenticated provider catalog rows", async () => {
|
||||
setDefaultZaiRegistry({ available: false });
|
||||
hasProviderStaticCatalogForFilter.mockResolvedValueOnce(true);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AuthProfileStore } from "../../agents/auth-profiles/types.js";
|
||||
import { withEnvAsync } from "../../test-utils/env.js";
|
||||
import { createModelListAuthIndex } from "./list.auth-index.js";
|
||||
|
||||
type PluginSnapshotResult = {
|
||||
@@ -61,6 +65,35 @@ function modelConfig(id: string) {
|
||||
};
|
||||
}
|
||||
|
||||
async function writeWorkspaceAuthEvidencePlugin(workspaceDir: string) {
|
||||
const pluginDir = path.join(workspaceDir, ".openclaw", "extensions", "workspace-cloud");
|
||||
await fs.mkdir(pluginDir, { recursive: true });
|
||||
await fs.writeFile(path.join(pluginDir, "index.ts"), "export default {}\n", "utf8");
|
||||
await fs.writeFile(
|
||||
path.join(pluginDir, "openclaw.plugin.json"),
|
||||
JSON.stringify({
|
||||
id: "workspace-cloud",
|
||||
configSchema: { type: "object" },
|
||||
setup: {
|
||||
providers: [
|
||||
{
|
||||
id: "workspace-cloud",
|
||||
authEvidence: [
|
||||
{
|
||||
type: "local-file-with-env",
|
||||
fileEnvVar: "WORKSPACE_CLOUD_CREDENTIALS",
|
||||
credentialMarker: "workspace-cloud-local-credentials",
|
||||
source: "workspace cloud credentials",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
describe("createModelListAuthIndex", () => {
|
||||
beforeEach(() => {
|
||||
envCandidateMocks.resolveProviderEnvApiKeyCandidates.mockClear();
|
||||
@@ -113,6 +146,47 @@ describe("createModelListAuthIndex", () => {
|
||||
expect(index.hasProviderAuth("google-vertex")).toBe(true);
|
||||
});
|
||||
|
||||
it("uses trusted workspace plugin auth evidence when workspace scope is supplied", async () => {
|
||||
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-list-auth-index-"));
|
||||
const workspaceDir = path.join(tempRoot, "workspace");
|
||||
const bundledDir = path.join(tempRoot, "bundled");
|
||||
const stateDir = path.join(tempRoot, "state");
|
||||
const credentialsPath = path.join(tempRoot, "credentials.json");
|
||||
await fs.mkdir(bundledDir, { recursive: true });
|
||||
await fs.mkdir(stateDir, { recursive: true });
|
||||
await fs.writeFile(credentialsPath, "{}", "utf8");
|
||||
await writeWorkspaceAuthEvidencePlugin(workspaceDir);
|
||||
|
||||
try {
|
||||
await withEnvAsync(
|
||||
{
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: bundledDir,
|
||||
OPENCLAW_STATE_DIR: stateDir,
|
||||
WORKSPACE_CLOUD_CREDENTIALS: credentialsPath,
|
||||
},
|
||||
async () => {
|
||||
const cfg = { plugins: { allow: ["workspace-cloud"] } };
|
||||
const withoutWorkspace = createModelListAuthIndex({
|
||||
cfg,
|
||||
authStore: emptyStore,
|
||||
env: process.env,
|
||||
});
|
||||
const withWorkspace = createModelListAuthIndex({
|
||||
cfg,
|
||||
authStore: emptyStore,
|
||||
workspaceDir,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
expect(withoutWorkspace.hasProviderAuth("workspace-cloud")).toBe(false);
|
||||
expect(withWorkspace.hasProviderAuth("workspace-cloud")).toBe(true);
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("records configured provider API keys", () => {
|
||||
const index = createModelListAuthIndex({
|
||||
cfg: {
|
||||
|
||||
@@ -21,6 +21,7 @@ export type ModelListAuthIndex = {
|
||||
export type CreateModelListAuthIndexParams = {
|
||||
cfg: OpenClawConfig;
|
||||
authStore: AuthProfileStore;
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
syntheticAuthProviderRefs?: readonly string[];
|
||||
};
|
||||
@@ -39,10 +40,12 @@ function normalizeAuthProvider(
|
||||
|
||||
function listValidatedSyntheticAuthProviderRefs(params: {
|
||||
cfg: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): readonly string[] {
|
||||
const result = loadPluginRegistrySnapshotWithMetadata({
|
||||
config: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
});
|
||||
if (result.source !== "persisted" && result.source !== "provided") {
|
||||
@@ -57,9 +60,14 @@ export function createModelListAuthIndex(
|
||||
params: CreateModelListAuthIndexParams,
|
||||
): ModelListAuthIndex {
|
||||
const env = params.env ?? process.env;
|
||||
const aliasMap = resolveProviderAuthAliasMap({ config: params.cfg, env });
|
||||
const envCandidateMap = resolveProviderEnvApiKeyCandidates({ config: params.cfg, env });
|
||||
const authEvidenceMap = resolveProviderEnvAuthEvidence({ config: params.cfg, env });
|
||||
const lookupParams = {
|
||||
config: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env,
|
||||
};
|
||||
const aliasMap = resolveProviderAuthAliasMap(lookupParams);
|
||||
const envCandidateMap = resolveProviderEnvApiKeyCandidates(lookupParams);
|
||||
const authEvidenceMap = resolveProviderEnvAuthEvidence(lookupParams);
|
||||
const authenticatedProviders = new Set<string>();
|
||||
const syntheticAuthProviders = new Set<string>();
|
||||
const envProviderAuthCache = new Map<string, boolean>();
|
||||
@@ -90,6 +98,7 @@ export function createModelListAuthIndex(
|
||||
aliasMap,
|
||||
candidateMap: envCandidateMap,
|
||||
authEvidenceMap,
|
||||
workspaceDir: params.workspaceDir,
|
||||
})
|
||||
) {
|
||||
addProvider(provider);
|
||||
@@ -110,7 +119,11 @@ export function createModelListAuthIndex(
|
||||
}
|
||||
|
||||
for (const provider of params.syntheticAuthProviderRefs ??
|
||||
listValidatedSyntheticAuthProviderRefs({ cfg: params.cfg, env })) {
|
||||
listValidatedSyntheticAuthProviderRefs({
|
||||
cfg: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env,
|
||||
})) {
|
||||
addSyntheticProvider(provider);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ export function resolveProviderAuthOverview(params: {
|
||||
store: AuthProfileStore;
|
||||
modelsPath: string;
|
||||
agentDir?: string;
|
||||
workspaceDir?: string;
|
||||
syntheticAuth?: { value: string; source: string };
|
||||
}): ProviderAuthOverview {
|
||||
const { provider, cfg, store } = params;
|
||||
@@ -123,7 +124,10 @@ export function resolveProviderAuthOverview(params: {
|
||||
const tokenCount = profiles.filter((id) => store.profiles[id]?.type === "token").length;
|
||||
const apiKeyCount = profiles.filter((id) => store.profiles[id]?.type === "api_key").length;
|
||||
|
||||
const envKey = resolveEnvApiKey(provider);
|
||||
const envKey = resolveEnvApiKey(provider, process.env, {
|
||||
config: cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
const customKey = getCustomProviderApiKey(cfg, provider);
|
||||
const usableCustomKey = resolveUsableCustomProviderApiKey({ cfg, provider });
|
||||
|
||||
|
||||
@@ -65,18 +65,26 @@ export async function modelsListCommand(
|
||||
if (providerFilter === null) {
|
||||
return;
|
||||
}
|
||||
const [{ loadAuthProfileStoreWithoutExternalProfiles }, { resolveOpenClawAgentDir }] =
|
||||
await Promise.all([
|
||||
import("../../agents/auth-profiles/store.js"),
|
||||
import("../../agents/agent-paths.js"),
|
||||
]);
|
||||
const [
|
||||
{ loadAuthProfileStoreWithoutExternalProfiles },
|
||||
{ resolveOpenClawAgentDir },
|
||||
{ resolveAgentWorkspaceDir, resolveDefaultAgentId },
|
||||
{ resolveDefaultAgentWorkspaceDir },
|
||||
] = await Promise.all([
|
||||
import("../../agents/auth-profiles/store.js"),
|
||||
import("../../agents/agent-paths.js"),
|
||||
import("../../agents/agent-scope.js"),
|
||||
import("../../agents/workspace.js"),
|
||||
]);
|
||||
const { resolvedConfig: cfg } = await loadModelsConfigWithSource({
|
||||
commandName: "models list",
|
||||
runtime,
|
||||
});
|
||||
const authStore = loadAuthProfileStoreWithoutExternalProfiles();
|
||||
const agentDir = resolveOpenClawAgentDir();
|
||||
const authIndex = createModelListAuthIndex({ cfg, authStore });
|
||||
const workspaceDir =
|
||||
resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)) ?? resolveDefaultAgentWorkspaceDir();
|
||||
const authIndex = createModelListAuthIndex({ cfg, authStore, workspaceDir });
|
||||
|
||||
let modelRegistry: ModelRegistry | undefined;
|
||||
let registryModels: Model<Api>[] = [];
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
resolveAgentDir,
|
||||
resolveAgentExplicitModelPrimary,
|
||||
resolveAgentModelFallbacksOverride,
|
||||
resolveAgentWorkspaceDir,
|
||||
resolveDefaultAgentId,
|
||||
} from "../../agents/agent-scope.js";
|
||||
import {
|
||||
buildAuthHealthSummary,
|
||||
@@ -24,6 +26,7 @@ import {
|
||||
resolveConfiguredModelRef,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
||||
import { createConfigIO } from "../../config/config.js";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
@@ -161,6 +164,9 @@ export async function modelsStatusCommand(
|
||||
const cfg = await loadModelsConfig({ commandName: "models status", runtime });
|
||||
const agentId = resolveKnownAgentId({ cfg, rawAgentId: opts.agent });
|
||||
const agentDir = agentId ? resolveAgentDir(cfg, agentId) : resolveOpenClawAgentDir();
|
||||
const workspaceAgentId = agentId ?? resolveDefaultAgentId(cfg);
|
||||
const workspaceDir =
|
||||
resolveAgentWorkspaceDir(cfg, workspaceAgentId) ?? resolveDefaultAgentWorkspaceDir();
|
||||
const agentModelPrimary = agentId ? resolveAgentExplicitModelPrimary(cfg, agentId) : undefined;
|
||||
const agentFallbacksOverride = agentId
|
||||
? resolveAgentModelFallbacksOverride(cfg, agentId)
|
||||
@@ -241,8 +247,12 @@ export async function modelsStatusCommand(
|
||||
const providersFromEnv = new Set<string>();
|
||||
// Use the shared provider-env registry so `models status` stays aligned with
|
||||
// env-backed providers beyond the text-model defaults (for example image-gen).
|
||||
for (const provider of Object.keys(resolveProviderEnvApiKeyCandidates()).toSorted()) {
|
||||
if (resolveEnvApiKey(provider)) {
|
||||
const envCandidateMap = resolveProviderEnvApiKeyCandidates({
|
||||
config: cfg,
|
||||
workspaceDir,
|
||||
});
|
||||
for (const provider of Object.keys(envCandidateMap).toSorted()) {
|
||||
if (resolveEnvApiKey(provider, process.env, { config: cfg, workspaceDir })) {
|
||||
providersFromEnv.add(provider);
|
||||
}
|
||||
}
|
||||
@@ -296,6 +306,7 @@ export async function modelsStatusCommand(
|
||||
store,
|
||||
modelsPath,
|
||||
agentDir,
|
||||
workspaceDir,
|
||||
syntheticAuth: syntheticAuthByProvider.get(provider),
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -38,6 +38,7 @@ const mocks = vi.hoisted(() => {
|
||||
resolveOpenClawAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"),
|
||||
resolveAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"),
|
||||
resolveAgentWorkspaceDir: vi.fn().mockReturnValue("/tmp/openclaw-agent/workspace"),
|
||||
resolveDefaultAgentId: vi.fn().mockReturnValue("main"),
|
||||
resolveAgentExplicitModelPrimary: vi.fn().mockReturnValue(undefined),
|
||||
resolveAgentEffectiveModelPrimary: vi.fn().mockReturnValue(undefined),
|
||||
resolveAgentModelFallbacksOverride: vi.fn().mockReturnValue(undefined),
|
||||
@@ -132,11 +133,15 @@ vi.mock("../../agents/agent-paths.js", () => ({
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveAgentDir: mocks.resolveAgentDir,
|
||||
resolveAgentWorkspaceDir: mocks.resolveAgentWorkspaceDir,
|
||||
resolveDefaultAgentId: mocks.resolveDefaultAgentId,
|
||||
resolveAgentExplicitModelPrimary: mocks.resolveAgentExplicitModelPrimary,
|
||||
resolveAgentEffectiveModelPrimary: mocks.resolveAgentEffectiveModelPrimary,
|
||||
resolveAgentModelFallbacksOverride: mocks.resolveAgentModelFallbacksOverride,
|
||||
listAgentIds: mocks.listAgentIds,
|
||||
}));
|
||||
vi.mock("../../agents/workspace.js", () => ({
|
||||
resolveDefaultAgentWorkspaceDir: vi.fn().mockReturnValue("/tmp/openclaw-agent/workspace"),
|
||||
}));
|
||||
vi.mock("../../agents/auth-profiles/display.js", () => ({
|
||||
resolveAuthProfileDisplayLabel: mocks.resolveAuthProfileDisplayLabel,
|
||||
}));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { DEFAULT_PROVIDER } from "../../agents/defaults.js";
|
||||
import { resolveVisibleModelCatalog } from "../../agents/model-catalog-visibility.js";
|
||||
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
||||
import {
|
||||
ErrorCodes,
|
||||
errorShape,
|
||||
@@ -30,6 +32,9 @@ export const modelsHandlers: GatewayRequestHandlers = {
|
||||
try {
|
||||
const catalog = await context.loadGatewayModelCatalog();
|
||||
const cfg = context.getRuntimeConfig();
|
||||
const workspaceDir =
|
||||
resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)) ??
|
||||
resolveDefaultAgentWorkspaceDir();
|
||||
const view = resolveModelsListView(params);
|
||||
if (view === "all") {
|
||||
respond(true, { models: catalog }, undefined);
|
||||
@@ -39,6 +44,7 @@ export const modelsHandlers: GatewayRequestHandlers = {
|
||||
cfg,
|
||||
catalog,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
workspaceDir,
|
||||
view,
|
||||
});
|
||||
respond(true, { models }, undefined);
|
||||
|
||||
Reference in New Issue
Block a user