fix: preserve sqlite refactor runtime fallbacks

This commit is contained in:
Peter Steinberger
2026-05-18 02:46:32 +01:00
parent 438241f410
commit 58d8ae96bc
6 changed files with 122 additions and 17 deletions

View File

@@ -411,6 +411,43 @@ describe("resolveAuthProfileOrder", () => {
expect(order).toEqual(["openai-codex:legacy"]);
});
it("keeps configured Codex auth order ahead of stored OpenAI alias order", async () => {
const store: AuthProfileStore = {
version: 1,
profiles: {
"openai:platform": {
type: "api_key",
provider: "openai",
key: "sk-platform",
},
"openai-codex:work": {
type: "oauth",
provider: "openai-codex",
access: "work-access",
refresh: "work-refresh",
expires: Date.now() + 60_000,
},
},
order: {
openai: ["openai:platform"],
},
};
const order = resolveAuthProfileOrder({
cfg: {
auth: {
order: {
"openai-codex": ["openai-codex:work"],
},
},
},
store,
provider: "openai-codex",
});
expect(order).toEqual(["openai-codex:work"]);
});
it("marks profile success with one canonical last-good and usage update", async () => {
const agentDir = await mkdtemp(path.join(os.tmpdir(), "openclaw-auth-profile-success-"));
try {

View File

@@ -233,19 +233,19 @@ export function resolveAuthProfileOrder(params: {
providerAuthKey === OPENAI_CODEX_PROVIDER_ID || providerKey === OPENAI_CODEX_PROVIDER_ID
? OPENAI_PROVIDER_ID
: undefined;
const storedOrder =
resolveAuthOrder(store.order, providerAuthKey) ??
resolveAuthOrder(store.order, providerKey) ??
(openAIOrderAliasProvider
? resolveAuthOrder(store.order, openAIOrderAliasProvider)
: undefined);
const configuredOrder =
const directStoredOrder =
resolveAuthOrder(store.order, providerAuthKey) ?? resolveAuthOrder(store.order, providerKey);
const directConfiguredOrder =
resolveAuthOrder(cfg?.auth?.order, providerAuthKey) ??
resolveAuthOrder(cfg?.auth?.order, providerKey) ??
(openAIOrderAliasProvider
? resolveAuthOrder(cfg?.auth?.order, openAIOrderAliasProvider)
: undefined);
const explicitOrder = storedOrder ?? configuredOrder;
resolveAuthOrder(cfg?.auth?.order, providerKey);
const aliasStoredOrder = openAIOrderAliasProvider
? resolveAuthOrder(store.order, openAIOrderAliasProvider)
: undefined;
const aliasConfiguredOrder = openAIOrderAliasProvider
? resolveAuthOrder(cfg?.auth?.order, openAIOrderAliasProvider)
: undefined;
const explicitOrder =
directStoredOrder ?? directConfiguredOrder ?? aliasStoredOrder ?? aliasConfiguredOrder;
const explicitProfiles = cfg?.auth?.profiles
? Object.entries(cfg.auth.profiles)
.filter(([profileId, profile]) =>

View File

@@ -1046,6 +1046,8 @@ export function hasPersistedAuthProfileSecretsStore(
agentDir?: string,
options: OpenClawStateDatabaseOptions = {},
): boolean {
return readAuthProfileStorePayloadResult(authProfileStoreKey(agentDir, options.env), options)
.exists;
return readAuthProfileStorePayloadResultReadOnly(
authProfileStoreKey(agentDir, options.env),
options,
).exists;
}

View File

@@ -69,4 +69,24 @@ describe("hasAnyAuthProfileStoreSource", () => {
});
});
});
it("does not create SQLite state while probing an empty auth source", () => {
withTempAgentDir("openclaw-auth-source-empty-", (agentDir) => {
const previousStateDir = process.env.OPENCLAW_STATE_DIR;
try {
const stateDir = path.join(agentDir, "state");
process.env.OPENCLAW_STATE_DIR = stateDir;
const sqlitePath = path.join(stateDir, "state", "openclaw.sqlite");
expect(hasAnyAuthProfileStoreSource(agentDir)).toBe(false);
expect(fs.existsSync(sqlitePath)).toBe(false);
} finally {
if (previousStateDir === undefined) {
delete process.env.OPENCLAW_STATE_DIR;
} else {
process.env.OPENCLAW_STATE_DIR = previousStateDir;
}
}
});
});
});

View File

@@ -1,5 +1,5 @@
import { randomUUID } from "node:crypto";
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { executeSqliteQuerySync, getNodeSqliteKysely } from "../../infra/kysely-sync.js";
import type { DB as OpenClawAgentKyselyDatabase } from "../../state/openclaw-agent-db.generated.js";
import {
@@ -15,6 +15,11 @@ import type { SessionEntry } from "./types.js";
type DeliveryInfoTestDatabase = Pick<OpenClawAgentKyselyDatabase, "session_entries">;
const ORIGINAL_STATE_DIR = process.env.OPENCLAW_STATE_DIR;
const runtimeConfigMock = vi.hoisted(() => vi.fn(() => ({}) as OpenClawConfig));
vi.mock("../io.js", () => ({
getRuntimeConfig: runtimeConfigMock,
}));
function setStateDir(): NodeJS.ProcessEnv {
const stateDir = `${process.env.TMPDIR ?? "/tmp"}/openclaw-delivery-info-${randomUUID()}`;
@@ -57,6 +62,8 @@ function corruptStoredEntryJson(params: {
afterEach(() => {
closeOpenClawAgentDatabasesForTest();
closeOpenClawStateDatabaseForTest();
runtimeConfigMock.mockReset();
runtimeConfigMock.mockReturnValue({} as OpenClawConfig);
if (ORIGINAL_STATE_DIR === undefined) {
delete process.env.OPENCLAW_STATE_DIR;
} else {
@@ -171,6 +178,40 @@ describe("extractDeliveryInfo", () => {
});
});
it("uses runtime config to search registered agent databases when cfg is omitted", () => {
const env = setStateDir();
const cfg = {
agents: {
list: [{ id: "main", default: true }, { id: "ops" }],
},
} as OpenClawConfig;
runtimeConfigMock.mockReturnValue(cfg);
const sessionKey = "agent:ops:matrix:channel:!ops:example.org";
const registeredPath = `${env.OPENCLAW_STATE_DIR}/registered/ops.sqlite`;
upsertSessionEntry({
agentId: "ops",
env,
path: registeredPath,
sessionKey,
entry: buildEntry({
channel: "matrix",
to: "!ops:example.org",
accountId: "work",
threadId: "$thread",
}),
});
expect(extractDeliveryInfo(sessionKey)).toEqual({
deliveryContext: {
channel: "matrix",
to: "!ops:example.org",
accountId: "work",
threadId: "$thread",
},
threadId: "$thread",
});
});
it("returns empty delivery info when the session row is missing", () => {
setStateDir();

View File

@@ -5,6 +5,7 @@ import {
} from "../../routing/session-key.js";
import { deliveryContextFromSession } from "../../utils/delivery-context.shared.js";
import type { DeliveryContext } from "../../utils/delivery-context.types.js";
import { getRuntimeConfig } from "../io.js";
import type { OpenClawConfig } from "../types.openclaw.js";
import { normalizeSessionRowKey } from "./store-entry.js";
import { getSessionEntry } from "./store.js";
@@ -99,9 +100,13 @@ export function extractDeliveryInfo(
const { baseSessionKey, threadId } = parseSessionThreadInfo(sessionKey);
const lookupKey = baseSessionKey ?? sessionKey;
try {
const lookupOptions = {
...options,
cfg: options.cfg ?? getRuntimeConfig(),
};
const entry =
readDeliverySessionEntry(lookupKey, options) ??
(lookupKey === sessionKey ? undefined : readDeliverySessionEntry(sessionKey, options));
readDeliverySessionEntry(lookupKey, lookupOptions) ??
(lookupKey === sessionKey ? undefined : readDeliverySessionEntry(sessionKey, lookupOptions));
const deliveryContext = toExtractedDeliveryContext(entry);
return {
deliveryContext,