mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-26 19:43:32 +00:00
Gateway: harden custom session-store discovery (#44176)
Merged via squash.
Prepared head SHA: 52ebbf5188
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
committed by
GitHub
parent
dc3bb1890b
commit
46f0bfc55b
@@ -1,17 +1,10 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveSessionStoreTargets } from "./session-store-targets.js";
|
||||
|
||||
const resolveStorePathMock = vi.hoisted(() => vi.fn());
|
||||
const resolveDefaultAgentIdMock = vi.hoisted(() => vi.fn());
|
||||
const listAgentIdsMock = vi.hoisted(() => vi.fn());
|
||||
const resolveSessionStoreTargetsMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../config/sessions.js", () => ({
|
||||
resolveStorePath: resolveStorePathMock,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: resolveDefaultAgentIdMock,
|
||||
listAgentIds: listAgentIdsMock,
|
||||
resolveSessionStoreTargets: resolveSessionStoreTargetsMock,
|
||||
}));
|
||||
|
||||
describe("resolveSessionStoreTargets", () => {
|
||||
@@ -19,61 +12,14 @@ describe("resolveSessionStoreTargets", () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("resolves the default agent store when no selector is provided", () => {
|
||||
resolveDefaultAgentIdMock.mockReturnValue("main");
|
||||
resolveStorePathMock.mockReturnValue("/tmp/main-sessions.json");
|
||||
it("delegates session store target resolution to the shared config helper", () => {
|
||||
resolveSessionStoreTargetsMock.mockReturnValue([
|
||||
{ agentId: "main", storePath: "/tmp/main-sessions.json" },
|
||||
]);
|
||||
|
||||
const targets = resolveSessionStoreTargets({}, {});
|
||||
|
||||
expect(targets).toEqual([{ agentId: "main", storePath: "/tmp/main-sessions.json" }]);
|
||||
expect(resolveStorePathMock).toHaveBeenCalledWith(undefined, { agentId: "main" });
|
||||
});
|
||||
|
||||
it("resolves all configured agent stores", () => {
|
||||
listAgentIdsMock.mockReturnValue(["main", "work"]);
|
||||
resolveStorePathMock
|
||||
.mockReturnValueOnce("/tmp/main-sessions.json")
|
||||
.mockReturnValueOnce("/tmp/work-sessions.json");
|
||||
|
||||
const targets = resolveSessionStoreTargets(
|
||||
{
|
||||
session: { store: "~/.openclaw/agents/{agentId}/sessions/sessions.json" },
|
||||
},
|
||||
{ allAgents: true },
|
||||
);
|
||||
|
||||
expect(targets).toEqual([
|
||||
{ agentId: "main", storePath: "/tmp/main-sessions.json" },
|
||||
{ agentId: "work", storePath: "/tmp/work-sessions.json" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("dedupes shared store paths for --all-agents", () => {
|
||||
listAgentIdsMock.mockReturnValue(["main", "work"]);
|
||||
resolveStorePathMock.mockReturnValue("/tmp/shared-sessions.json");
|
||||
|
||||
const targets = resolveSessionStoreTargets(
|
||||
{
|
||||
session: { store: "/tmp/shared-sessions.json" },
|
||||
},
|
||||
{ allAgents: true },
|
||||
);
|
||||
|
||||
expect(targets).toEqual([{ agentId: "main", storePath: "/tmp/shared-sessions.json" }]);
|
||||
expect(resolveStorePathMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("rejects unknown agent ids", () => {
|
||||
listAgentIdsMock.mockReturnValue(["main", "work"]);
|
||||
expect(() => resolveSessionStoreTargets({}, { agent: "ghost" })).toThrow(/Unknown agent id/);
|
||||
});
|
||||
|
||||
it("rejects conflicting selectors", () => {
|
||||
expect(() => resolveSessionStoreTargets({}, { agent: "main", allAgents: true })).toThrow(
|
||||
/cannot be used together/i,
|
||||
);
|
||||
expect(() =>
|
||||
resolveSessionStoreTargets({}, { store: "/tmp/sessions.json", allAgents: true }),
|
||||
).toThrow(/cannot be combined/i);
|
||||
expect(resolveSessionStoreTargetsMock).toHaveBeenCalledWith({}, {});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,84 +1,11 @@
|
||||
import { listAgentIds, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { resolveStorePath } from "../config/sessions.js";
|
||||
import {
|
||||
resolveSessionStoreTargets,
|
||||
type SessionStoreSelectionOptions,
|
||||
type SessionStoreTarget,
|
||||
} from "../config/sessions.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
||||
export type SessionStoreSelectionOptions = {
|
||||
store?: string;
|
||||
agent?: string;
|
||||
allAgents?: boolean;
|
||||
};
|
||||
|
||||
export type SessionStoreTarget = {
|
||||
agentId: string;
|
||||
storePath: string;
|
||||
};
|
||||
|
||||
function dedupeTargetsByStorePath(targets: SessionStoreTarget[]): SessionStoreTarget[] {
|
||||
const deduped = new Map<string, SessionStoreTarget>();
|
||||
for (const target of targets) {
|
||||
if (!deduped.has(target.storePath)) {
|
||||
deduped.set(target.storePath, target);
|
||||
}
|
||||
}
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
export function resolveSessionStoreTargets(
|
||||
cfg: OpenClawConfig,
|
||||
opts: SessionStoreSelectionOptions,
|
||||
): SessionStoreTarget[] {
|
||||
const defaultAgentId = resolveDefaultAgentId(cfg);
|
||||
const hasAgent = Boolean(opts.agent?.trim());
|
||||
const allAgents = opts.allAgents === true;
|
||||
if (hasAgent && allAgents) {
|
||||
throw new Error("--agent and --all-agents cannot be used together");
|
||||
}
|
||||
if (opts.store && (hasAgent || allAgents)) {
|
||||
throw new Error("--store cannot be combined with --agent or --all-agents");
|
||||
}
|
||||
|
||||
if (opts.store) {
|
||||
return [
|
||||
{
|
||||
agentId: defaultAgentId,
|
||||
storePath: resolveStorePath(opts.store, { agentId: defaultAgentId }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (allAgents) {
|
||||
const targets = listAgentIds(cfg).map((agentId) => ({
|
||||
agentId,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId }),
|
||||
}));
|
||||
return dedupeTargetsByStorePath(targets);
|
||||
}
|
||||
|
||||
if (hasAgent) {
|
||||
const knownAgents = listAgentIds(cfg);
|
||||
const requested = normalizeAgentId(opts.agent ?? "");
|
||||
if (!knownAgents.includes(requested)) {
|
||||
throw new Error(
|
||||
`Unknown agent id "${opts.agent}". Use "openclaw agents list" to see configured agents.`,
|
||||
);
|
||||
}
|
||||
return [
|
||||
{
|
||||
agentId: requested,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId: requested }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
agentId: defaultAgentId,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }),
|
||||
},
|
||||
];
|
||||
}
|
||||
export { resolveSessionStoreTargets, type SessionStoreSelectionOptions, type SessionStoreTarget };
|
||||
|
||||
export function resolveSessionStoreTargetsOrExit(params: {
|
||||
cfg: OpenClawConfig;
|
||||
|
||||
Reference in New Issue
Block a user