mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-10 20:45:15 +00:00
fix: keep oauth sibling sync sqlite-local
This commit is contained in:
@@ -14,6 +14,8 @@ import {
|
||||
setupAuthTestEnv,
|
||||
} from "./test-wizard-helpers.js";
|
||||
|
||||
const legacyAuthProfilePathFor = (dir: string) => path.join(dir, "auth-profiles.json");
|
||||
|
||||
const providerEnvVarsById = vi.hoisted(
|
||||
(): Record<string, readonly string[]> => ({
|
||||
"cloudflare-ai-gateway": ["CLOUDFLARE_AI_GATEWAY_API_KEY"],
|
||||
@@ -30,43 +32,6 @@ vi.mock("../config/paths.js", () => ({
|
||||
resolveStateDir: () => process.env.OPENCLAW_STATE_DIR ?? "/tmp/openclaw-state",
|
||||
}));
|
||||
|
||||
vi.mock("../agents/auth-profiles/profiles.js", async () => {
|
||||
const fs = await import("node:fs");
|
||||
const path = await import("node:path");
|
||||
return {
|
||||
upsertAuthProfile: (params: { profileId: string; credential: unknown; agentDir?: string }) => {
|
||||
const stateDir = process.env.OPENCLAW_STATE_DIR ?? "/tmp/openclaw-state";
|
||||
const agentDir = params.agentDir ?? path.join(stateDir, "agents", "main", "agent");
|
||||
const file = path.join(agentDir, "auth-profiles.json");
|
||||
fs.mkdirSync(agentDir, { recursive: true });
|
||||
const existing = (() => {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(file, "utf8")) as {
|
||||
version?: number;
|
||||
profiles?: Record<string, unknown>;
|
||||
};
|
||||
} catch {
|
||||
return { version: 1, profiles: {} };
|
||||
}
|
||||
})();
|
||||
fs.writeFileSync(
|
||||
file,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
version: existing.version ?? 1,
|
||||
profiles: {
|
||||
...existing.profiles,
|
||||
[params.profileId]: params.credential,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/provider-auth-aliases.js", () => ({
|
||||
resolveProviderIdForAuth: (provider: string) => {
|
||||
const normalized = provider.trim().toLowerCase();
|
||||
@@ -90,13 +55,12 @@ describe("writeOAuthCredentials", () => {
|
||||
]);
|
||||
|
||||
let tempStateDir: string;
|
||||
const authProfilePathFor = (dir: string) => path.join(dir, "auth-profiles.json");
|
||||
|
||||
afterEach(async () => {
|
||||
await lifecycle.cleanup();
|
||||
});
|
||||
|
||||
it("writes auth-profiles.json under the default agent dir", async () => {
|
||||
it("writes OAuth credentials under the default agent dir SQLite store", async () => {
|
||||
const env = await setupAuthTestEnv("openclaw-oauth-");
|
||||
lifecycle.setStateDir(env.stateDir);
|
||||
const defaultAgentDir = path.join(env.stateDir, "agents", "main", "agent");
|
||||
@@ -118,8 +82,11 @@ describe("writeOAuthCredentials", () => {
|
||||
type: "oauth",
|
||||
});
|
||||
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(env.agentDir), "utf8")).rejects.toMatchObject(
|
||||
{ code: "ENOENT" },
|
||||
);
|
||||
await expect(
|
||||
fs.readFile(path.join(env.agentDir, "auth-profiles.json"), "utf8"),
|
||||
fs.readFile(legacyAuthProfilePathFor(defaultAgentDir), "utf8"),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
|
||||
@@ -148,15 +115,17 @@ describe("writeOAuthCredentials", () => {
|
||||
});
|
||||
|
||||
for (const dir of [mainAgentDir, kidAgentDir, workerAgentDir]) {
|
||||
const raw = await fs.readFile(authProfilePathFor(dir), "utf8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
const parsed = await readAuthProfilesForAgent<{
|
||||
profiles?: Record<string, OAuthCredentials & { type?: string }>;
|
||||
};
|
||||
}>(dir);
|
||||
expect(parsed.profiles?.["openai-codex:default"]).toMatchObject({
|
||||
refresh: "refresh-sync",
|
||||
access: "access-sync",
|
||||
type: "oauth",
|
||||
});
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(dir), "utf8")).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -180,18 +149,25 @@ describe("writeOAuthCredentials", () => {
|
||||
|
||||
await writeOAuthCredentials("openai-codex", creds, kidAgentDir);
|
||||
|
||||
const kidRaw = await fs.readFile(authProfilePathFor(kidAgentDir), "utf8");
|
||||
const kidParsed = JSON.parse(kidRaw) as {
|
||||
const kidParsed = await readAuthProfilesForAgent<{
|
||||
profiles?: Record<string, OAuthCredentials & { type?: string }>;
|
||||
};
|
||||
}>(kidAgentDir);
|
||||
expect(kidParsed.profiles?.["openai-codex:default"]).toMatchObject({
|
||||
access: "access-kid",
|
||||
type: "oauth",
|
||||
});
|
||||
|
||||
await expect(fs.readFile(authProfilePathFor(mainAgentDir), "utf8")).rejects.toMatchObject({
|
||||
await expect(readAuthProfilesForAgent(mainAgentDir)).rejects.toThrow(
|
||||
"Expected SQLite auth profile store",
|
||||
);
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(kidAgentDir), "utf8")).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(mainAgentDir), "utf8")).rejects.toMatchObject(
|
||||
{
|
||||
code: "ENOENT",
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("syncs siblings from explicit agentDir outside OPENCLAW_STATE_DIR", async () => {
|
||||
@@ -219,20 +195,25 @@ describe("writeOAuthCredentials", () => {
|
||||
|
||||
// All siblings under the external root should have credentials
|
||||
for (const dir of [extMain, extKid, extWorker]) {
|
||||
const raw = await fs.readFile(authProfilePathFor(dir), "utf8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
const parsed = await readAuthProfilesForAgent<{
|
||||
profiles?: Record<string, OAuthCredentials & { type?: string }>;
|
||||
};
|
||||
}>(dir);
|
||||
expect(parsed.profiles?.["openai-codex:default"]).toMatchObject({
|
||||
refresh: "refresh-ext",
|
||||
access: "access-ext",
|
||||
type: "oauth",
|
||||
});
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(dir), "utf8")).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
}
|
||||
|
||||
// Global state dir should NOT have credentials written
|
||||
const globalMain = path.join(tempStateDir, "agents", "main", "agent");
|
||||
await expect(fs.readFile(authProfilePathFor(globalMain), "utf8")).rejects.toMatchObject({
|
||||
await expect(readAuthProfilesForAgent(globalMain)).rejects.toThrow(
|
||||
"Expected SQLite auth profile store",
|
||||
);
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(globalMain), "utf8")).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
});
|
||||
@@ -411,8 +392,11 @@ describe("upsertApiKeyProfile", () => {
|
||||
key: "sk-minimax-test",
|
||||
});
|
||||
|
||||
await expect(fs.readFile(legacyAuthProfilePathFor(env.agentDir), "utf8")).rejects.toMatchObject(
|
||||
{ code: "ENOENT" },
|
||||
);
|
||||
await expect(
|
||||
fs.readFile(path.join(env.agentDir, "auth-profiles.json"), "utf8"),
|
||||
fs.readFile(legacyAuthProfilePathFor(defaultAgentDir), "utf8"),
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { resolveDefaultAgentDir } from "../agents/agent-scope-config.js";
|
||||
import { buildAuthProfileId } from "../agents/auth-profiles/identity.js";
|
||||
import { upsertAuthProfile } from "../agents/auth-profiles/profiles.js";
|
||||
import { upsertAuthProfile, upsertAuthProfileWithLock } from "../agents/auth-profiles/profiles.js";
|
||||
import type { OAuthCredentials } from "../agents/pi-ai-contract.js";
|
||||
import { resolveProviderIdForAuth } from "../agents/provider-auth-aliases.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
@@ -261,7 +261,7 @@ function resolveSiblingAgentDirs(primaryAgentDir: string): string[] {
|
||||
const real = safeRealpathSync(dir);
|
||||
if (real && !seen.has(real)) {
|
||||
seen.add(real);
|
||||
result.push(real);
|
||||
result.push(dir);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -291,7 +291,7 @@ export async function writeOAuthCredentials(
|
||||
...(options?.displayName ? { displayName: options.displayName } : {}),
|
||||
};
|
||||
|
||||
upsertAuthProfile({
|
||||
await upsertAuthProfileWithLock({
|
||||
profileId,
|
||||
credential,
|
||||
agentDir: resolvedAgentDir,
|
||||
@@ -305,7 +305,7 @@ export async function writeOAuthCredentials(
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
upsertAuthProfile({
|
||||
await upsertAuthProfileWithLock({
|
||||
profileId,
|
||||
credential,
|
||||
agentDir: targetAgentDir,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { vi } from "vitest";
|
||||
import { loadPersistedAuthProfileStore } from "../../src/agents/auth-profiles/persisted.js";
|
||||
import type { RuntimeEnv } from "../../src/runtime.js";
|
||||
import { makeTempWorkspace } from "../../src/test-helpers/workspace.js";
|
||||
import { captureEnv } from "../../src/test-utils/env.js";
|
||||
@@ -82,11 +83,10 @@ export function requireOpenClawAgentDir(): string {
|
||||
return agentDir;
|
||||
}
|
||||
|
||||
function authProfilePathForAgent(agentDir: string): string {
|
||||
return path.join(agentDir, "auth-profiles.json");
|
||||
}
|
||||
|
||||
export async function readAuthProfilesForAgent<T>(agentDir: string): Promise<T> {
|
||||
const raw = await fs.readFile(authProfilePathForAgent(agentDir), "utf8");
|
||||
return JSON.parse(raw) as T;
|
||||
const store = loadPersistedAuthProfileStore(agentDir);
|
||||
if (!store) {
|
||||
throw new Error(`Expected SQLite auth profile store for ${agentDir}`);
|
||||
}
|
||||
return store as T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user