mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(models): keep prepared auth contracts acyclic
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/** Secret-free credential modes captured by a prepared agent runtime. */
|
||||
export type PreparedAgentCredentialModes = Readonly<Record<string, "api_key" | "oauth" | "token">>;
|
||||
@@ -4,6 +4,7 @@ import { asDateTimestampMs } from "@openclaw/normalization-core/number-coercion"
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { coerceSecretRef } from "../config/types.secrets.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credential-modes.js";
|
||||
import { resolveAuthProfileOrder } from "./auth-profiles/order.js";
|
||||
import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles/types.js";
|
||||
import type { AuthStorageData } from "./sessions/auth-storage.js";
|
||||
@@ -22,7 +23,6 @@ type AgentOAuthCredential = {
|
||||
/** Credential value shape consumed by agent runtimes after auth-profile normalization. */
|
||||
type AgentCredential = AgentApiKeyCredential | AgentOAuthCredential;
|
||||
export type AgentCredentialMap = Record<string, AgentCredential>;
|
||||
export type PreparedAgentCredentialModes = Readonly<Record<string, "api_key" | "oauth" | "token">>;
|
||||
|
||||
type ResolveAgentCredentialMapOptions = {
|
||||
includeSecretRefPlaceholders?: boolean;
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
ProviderModelRouteResolution,
|
||||
} from "../plugin-sdk/provider-model-types.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credentials.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credential-modes.js";
|
||||
import type { RuntimeAuthMaterialization } from "./auth-profiles/runtime-materializations.js";
|
||||
import type { AuthProfileStore } from "./auth-profiles/types.js";
|
||||
import {
|
||||
|
||||
@@ -17,7 +17,7 @@ import type {
|
||||
} from "../plugin-sdk/provider-model-types.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import { isValidSecretRef } from "../secrets/ref-contract.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credentials.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credential-modes.js";
|
||||
import { hasUsableOAuthCredential } from "./auth-profiles/credential-state.js";
|
||||
import { resolveExternalCliAuthProfiles } from "./auth-profiles/external-cli-sync.js";
|
||||
import {
|
||||
|
||||
@@ -202,6 +202,7 @@ describe("createOpenClawTools browser plugin integration", () => {
|
||||
workspaceDir: "/tmp",
|
||||
activeProjectKeys: [],
|
||||
config,
|
||||
authStore: { version: 1, profiles: {} },
|
||||
authModes: {},
|
||||
metadataSnapshot,
|
||||
pluginRegistry,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credentials.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credential-modes.js";
|
||||
import type { AuthProfileStore } from "./auth-profiles/types.js";
|
||||
import type { ModelCatalogSnapshot } from "./model-catalog.types.js";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot
|
||||
import type { PreparedProviderStaticCatalog } from "../plugins/provider-discovery.js";
|
||||
import type { ProviderRuntimeModel } from "../plugins/provider-runtime-model.types.js";
|
||||
import type { PluginRegistry } from "../plugins/registry-types.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credentials.js";
|
||||
import type { PreparedAgentCredentialModes } from "./agent-auth-credential-modes.js";
|
||||
import type { AuthProfileStore } from "./auth-profiles/types.js";
|
||||
import type { InlineModelEntry } from "./embedded-agent-runner/model.inline-provider.js";
|
||||
import type { AgentHarnessPluginSelection } from "./harness/runtime-plugin-load-plan.js";
|
||||
|
||||
@@ -60,6 +60,9 @@ describe("local gateway request context", () => {
|
||||
agentDir: "/tmp/local-model-catalog-agent",
|
||||
workspaceDir: "/tmp/local-model-catalog-workspace",
|
||||
config: cfg,
|
||||
authModes: {},
|
||||
authStore: { version: 1, profiles: {} },
|
||||
metadataSnapshot: { index: { plugins: [] }, plugins: [] } as never,
|
||||
modelCatalog: { entries: [], routeVariants: [] },
|
||||
});
|
||||
|
||||
|
||||
@@ -21,12 +21,22 @@ function createOwner(
|
||||
api?: ModelCatalogEntry["api"],
|
||||
): PreparedModelRuntimeSnapshot {
|
||||
const model = { id, name: id, provider, ...(api ? { api } : {}) };
|
||||
const authStore: AuthProfileStore = {
|
||||
version: 1,
|
||||
profiles: Object.fromEntries(
|
||||
Object.entries(credentials).map(([credentialProvider, credential]) => [
|
||||
`${credentialProvider}:prepared`,
|
||||
{ ...credential, provider: credentialProvider },
|
||||
]),
|
||||
),
|
||||
};
|
||||
return {
|
||||
agentId: "main",
|
||||
agentDir: `/tmp/${id}/agent`,
|
||||
workspaceDir: `/tmp/${id}/workspace`,
|
||||
activeProjectKeys: [],
|
||||
config,
|
||||
authStore,
|
||||
authModes: resolveUsableAgentCredentialModes(credentials),
|
||||
metadataSnapshot: { index: { plugins: [] }, plugins: [] } as never,
|
||||
allowGatewaySubagentBinding: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PreparedAgentCredentialModes } from "../../agents/agent-auth-credentials.js";
|
||||
import type { PreparedAgentCredentialModes } from "../../agents/agent-auth-credential-modes.js";
|
||||
import { resolveAgentDir } from "../../agents/agent-scope.js";
|
||||
import type { RuntimeAuthMaterialization } from "../../agents/auth-profiles/runtime-materializations.js";
|
||||
import type { AuthProfileStore } from "../../agents/auth-profiles/types.js";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { asPositiveSafeInteger as resolvePositiveSafeInteger } from "@openclaw/normalization-core/number-coercion";
|
||||
import type { ModelChoice } from "../../../packages/gateway-protocol/src/schema/agents-models-skills.js";
|
||||
import type { PreparedAgentCredentialModes } from "../../agents/agent-auth-credentials.js";
|
||||
import type { PreparedAgentCredentialModes } from "../../agents/agent-auth-credential-modes.js";
|
||||
import {
|
||||
resolveAgentEffectiveModelPrimary,
|
||||
resolveAgentWorkspaceDir,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { ModelCatalogSnapshot } from "../agents/model-catalog.types.js";
|
||||
import type { PublishedModelCatalogOwnerCandidate } from "../agents/prepared-model-catalog.types.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import {
|
||||
loadGatewayModelCatalog,
|
||||
@@ -33,11 +34,14 @@ function ownerSnapshot(
|
||||
config: OpenClawConfig,
|
||||
modelCatalog: ModelCatalogSnapshot = snapshot,
|
||||
agentId?: string,
|
||||
) {
|
||||
): PublishedModelCatalogOwnerCandidate {
|
||||
return {
|
||||
...(agentId ? { agentId } : {}),
|
||||
agentDir: "/tmp/gateway-agent",
|
||||
config,
|
||||
authModes: {},
|
||||
authStore: { version: 1, profiles: {} },
|
||||
metadataSnapshot: { index: { plugins: [] }, plugins: [] } as never,
|
||||
modelCatalog,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1296,6 +1296,7 @@ describe("gateway server chat", () => {
|
||||
compat: { supportedReasoningEfforts: ["low"] },
|
||||
params: { apiKey: "private-route-token" },
|
||||
};
|
||||
const pluginMetadata = resolvePluginMetadataSnapshot({ config, env: process.env });
|
||||
const catalogSnapshot = {
|
||||
entries: [subscriptionRoute],
|
||||
routeVariants: [subscriptionRoute, platformRoute],
|
||||
@@ -1317,6 +1318,13 @@ describe("gateway server chat", () => {
|
||||
}),
|
||||
],
|
||||
]);
|
||||
const requirePreparedAuthStore = (agentId: string) => {
|
||||
const authStore = preparedAuthStoreByAgentId.get(agentId);
|
||||
if (!authStore) {
|
||||
throw new Error(`expected prepared auth store for agent "${agentId}"`);
|
||||
}
|
||||
return authStore;
|
||||
};
|
||||
const responses: Array<{ ok: boolean; payload?: unknown; error?: unknown }> = [];
|
||||
const { buildModelsListResult, createGatewayAgentModelCatalogProjector } =
|
||||
await import("./server-methods/models-list-result.js");
|
||||
@@ -1351,7 +1359,8 @@ describe("gateway server chat", () => {
|
||||
cfg: config,
|
||||
agentId,
|
||||
snapshot: catalogSnapshot,
|
||||
preparedAuthStore: preparedAuthStoreByAgentId.get(agentId),
|
||||
metadataSnapshot: pluginMetadata,
|
||||
preparedAuthStore: requirePreparedAuthStore(agentId),
|
||||
...(profileId ? { preferredProfileId: profileId } : {}),
|
||||
...(profileId && (profileSource === "user" || legacyUserProfile)
|
||||
? { lockedProfileId: profileId }
|
||||
@@ -1412,7 +1421,6 @@ describe("gateway server chat", () => {
|
||||
const persistedConfig = getRuntimeConfig();
|
||||
// Direct handlers bypass Gateway startup, so publish its process-lifecycle handoff once.
|
||||
// Otherwise every route projector rediscovers the full plugin metadata graph.
|
||||
const pluginMetadata = resolvePluginMetadataSnapshot({ config, env: process.env });
|
||||
releasePluginMetadata = installTemporaryCurrentPluginMetadataSnapshot(pluginMetadata, {
|
||||
config,
|
||||
compatibleConfigs: [persistedConfig],
|
||||
@@ -1427,6 +1435,8 @@ describe("gateway server chat", () => {
|
||||
cfg: persistedConfig,
|
||||
agentId: "work",
|
||||
snapshot: catalogSnapshot,
|
||||
metadataSnapshot: pluginMetadata,
|
||||
preparedAuthStore: requirePreparedAuthStore("work"),
|
||||
preferredProfileId: "openai:expired",
|
||||
}).evaluateEntry(subscriptionRoute, catalogSnapshot.routeVariants);
|
||||
expect(expiredPreferenceEvaluation).toMatchObject({
|
||||
|
||||
@@ -186,6 +186,7 @@ function setup(entry: SessionEntry = sessionEntry) {
|
||||
allowGatewaySubagentBinding: true,
|
||||
workspaceDir: WORKSPACE,
|
||||
config,
|
||||
authStore: { version: 1, profiles: {} },
|
||||
authModes: {},
|
||||
metadataSnapshot: { plugins: [] } as never,
|
||||
modelCatalog: {
|
||||
|
||||
Reference in New Issue
Block a user