mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix: preserve workspace auth scope in runtime paths
This commit is contained in:
@@ -390,6 +390,7 @@ export async function compactEmbeddedPiSessionDirect(
|
||||
cfg: params.config,
|
||||
profileId: authProfileId,
|
||||
agentDir,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
});
|
||||
|
||||
if (!apiKeyInfo.apiKey) {
|
||||
|
||||
@@ -176,6 +176,12 @@ describe("createEmbeddedRunAuthController", () => {
|
||||
|
||||
await controller.initializeAuthProfile();
|
||||
|
||||
expect(mocks.getApiKeyForModel).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
agentDir: "/tmp/agent",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
}),
|
||||
);
|
||||
expect(harness.runtimeModel.baseUrl).toBe("https://runtime.example.com/v1");
|
||||
expect(harness.runtimeModel.headers).toEqual({
|
||||
"api-key": "runtime-header-token",
|
||||
|
||||
@@ -353,6 +353,7 @@ export function createEmbeddedRunAuthController(params: {
|
||||
profileId: candidate,
|
||||
store: params.authStore,
|
||||
agentDir: params.agentDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
lockedProfile: candidate != null && candidate === params.lockedProfileId,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -10,6 +10,15 @@ import { requestHeartbeatNow } from "../../infra/heartbeat-wake.js";
|
||||
import * as execModule from "../../process/exec.js";
|
||||
import { onSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
|
||||
import { VERSION } from "../../version.js";
|
||||
|
||||
const runtimeModelAuthMocks = vi.hoisted(() => ({
|
||||
getApiKeyForModel: vi.fn(),
|
||||
getRuntimeAuthForModel: vi.fn(),
|
||||
resolveApiKeyForProvider: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./runtime-model-auth.runtime.js", () => runtimeModelAuthMocks);
|
||||
|
||||
import {
|
||||
clearGatewaySubagentRuntime,
|
||||
createPluginRuntime,
|
||||
@@ -107,6 +116,9 @@ function expectRunCommandOutcome(params: {
|
||||
describe("plugin runtime command execution", () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
runtimeModelAuthMocks.getApiKeyForModel.mockReset();
|
||||
runtimeModelAuthMocks.getRuntimeAuthForModel.mockReset();
|
||||
runtimeModelAuthMocks.resolveApiKeyForProvider.mockReset();
|
||||
resetConfigRuntimeState();
|
||||
clearGatewaySubagentRuntime();
|
||||
});
|
||||
@@ -291,6 +303,57 @@ describe("plugin runtime command execution", () => {
|
||||
expect(runtime.modelAuth.getApiKeyForModel).not.toBe(rawGetApiKey);
|
||||
});
|
||||
|
||||
it("modelAuth wrappers preserve workspace scope while stripping credential steering", async () => {
|
||||
const runtime = createPluginRuntime();
|
||||
const model = {
|
||||
id: "workspace-cloud/model",
|
||||
provider: "workspace-cloud",
|
||||
api: "openai-responses",
|
||||
baseUrl: "https://workspace-cloud.example/v1",
|
||||
};
|
||||
const cfg = { plugins: { allow: ["workspace-cloud"] } } as OpenClawConfig;
|
||||
runtimeModelAuthMocks.getApiKeyForModel.mockResolvedValue({
|
||||
apiKey: "model-key",
|
||||
source: "workspace cloud credentials",
|
||||
mode: "api-key",
|
||||
});
|
||||
runtimeModelAuthMocks.resolveApiKeyForProvider.mockResolvedValue({
|
||||
apiKey: "provider-key",
|
||||
source: "workspace cloud credentials",
|
||||
mode: "api-key",
|
||||
});
|
||||
|
||||
await expect(
|
||||
runtime.modelAuth.getApiKeyForModel({
|
||||
model: model as never,
|
||||
cfg,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
agentDir: "/tmp/agent",
|
||||
store: { version: 1, profiles: {} },
|
||||
} as never),
|
||||
).resolves.toMatchObject({ apiKey: "model-key" });
|
||||
await expect(
|
||||
runtime.modelAuth.resolveApiKeyForProvider({
|
||||
provider: "workspace-cloud",
|
||||
cfg,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
agentDir: "/tmp/agent",
|
||||
store: { version: 1, profiles: {} },
|
||||
} as never),
|
||||
).resolves.toMatchObject({ apiKey: "provider-key" });
|
||||
|
||||
expect(runtimeModelAuthMocks.getApiKeyForModel).toHaveBeenCalledWith({
|
||||
model,
|
||||
cfg,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
});
|
||||
expect(runtimeModelAuthMocks.resolveApiKeyForProvider).toHaveBeenCalledWith({
|
||||
provider: "workspace-cloud",
|
||||
cfg,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps subagent unavailable by default even after gateway initialization", async () => {
|
||||
const { runtime } = createGatewaySubagentRunFixture();
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ function createRuntimeModelAuth(): PluginRuntime["modelAuth"] {
|
||||
getApiKeyForModel({
|
||||
model: params.model,
|
||||
cfg: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
}),
|
||||
getRuntimeAuthForModel: (params) =>
|
||||
getRuntimeAuthForModel({
|
||||
@@ -126,6 +127,7 @@ function createRuntimeModelAuth(): PluginRuntime["modelAuth"] {
|
||||
resolveApiKeyForProvider({
|
||||
provider: params.provider,
|
||||
cfg: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -241,10 +241,11 @@ export type PluginRuntimeCore = {
|
||||
/** @deprecated Use runtime.tasks.flows for DTO-based TaskFlow access. */
|
||||
taskFlow: import("./runtime-taskflow.types.js").PluginRuntimeTaskFlow;
|
||||
modelAuth: {
|
||||
/** Resolve auth for a model. Only provider/model and optional cfg are used. */
|
||||
/** Resolve auth for a model. Only provider/model, optional cfg, and workspaceDir are used. */
|
||||
getApiKeyForModel: (params: {
|
||||
model: import("@mariozechner/pi-ai").Model<import("@mariozechner/pi-ai").Api>;
|
||||
cfg?: import("../../config/types.openclaw.js").OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
}) => Promise<import("../../agents/model-auth-runtime-shared.js").ResolvedProviderAuth>;
|
||||
/** Resolve request-ready auth for a model, including provider runtime exchanges. */
|
||||
getRuntimeAuthForModel: (params: {
|
||||
@@ -252,10 +253,11 @@ export type PluginRuntimeCore = {
|
||||
cfg?: import("../../config/types.openclaw.js").OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
}) => Promise<import("./model-auth-types.js").ResolvedProviderRuntimeAuth>;
|
||||
/** Resolve auth for a provider by name. Only provider and optional cfg are used. */
|
||||
/** Resolve auth for a provider by name. Only provider, optional cfg, and workspaceDir are used. */
|
||||
resolveApiKeyForProvider: (params: {
|
||||
provider: string;
|
||||
cfg?: import("../../config/types.openclaw.js").OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
}) => Promise<import("../../agents/model-auth-runtime-shared.js").ResolvedProviderAuth>;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user