mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-15 10:00:43 +00:00
* refactor: extract filesystem safety primitives * refactor: use fs-safe for file access helpers * refactor: reuse fs-safe for media reads * refactor: use fs-safe for image reads * refactor: reuse fs-safe in qqbot media opener * refactor: reuse fs-safe for local media checks * refactor: consume cleaner fs-safe api * refactor: align fs-safe json option names * fix: preserve fs-safe migration contracts * refactor: use fs-safe primitive subpaths * refactor: use grouped fs-safe subpaths * refactor: align fs-safe api usage * refactor: adapt private state store api * chore: refresh proof gate * refactor: follow fs-safe json api split * refactor: follow reduced fs-safe surface * build: default fs-safe python helper off * fix: preserve fs-safe plugin sdk aliases * refactor: consolidate fs-safe usage * refactor: unify fs-safe store usage * refactor: trim fs-safe temp workspace usage * refactor: hide low-level fs-safe primitives * build: use published fs-safe package * fix: preserve outbound recovery durability after rebase * chore: refresh pr checks
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import path from "node:path";
|
|
import { z } from "zod";
|
|
import { privateFileStore } from "../infra/private-file-store.js";
|
|
import { safeParseWithSchema } from "../utils/zod-parse.js";
|
|
import { ensureAuthProfileStore } from "./auth-profiles/store.js";
|
|
import {
|
|
piCredentialsEqual,
|
|
resolvePiCredentialMapFromStore,
|
|
type PiCredential,
|
|
} from "./pi-auth-credentials.js";
|
|
|
|
type AuthJsonShape = Record<string, unknown>;
|
|
|
|
const PiCredentialSchema: z.ZodType<PiCredential> = z.discriminatedUnion("type", [
|
|
z.object({
|
|
type: z.literal("api_key"),
|
|
key: z.string(),
|
|
}),
|
|
z.object({
|
|
type: z.literal("oauth"),
|
|
access: z.string(),
|
|
refresh: z.string(),
|
|
expires: z.number(),
|
|
}),
|
|
]);
|
|
|
|
const AuthJsonShapeSchema = z.record(z.string(), z.unknown());
|
|
|
|
async function readAuthJson(rootDir: string, filePath: string): Promise<AuthJsonShape> {
|
|
try {
|
|
const parsed = await privateFileStore(rootDir).readJsonIfExists(
|
|
path.relative(rootDir, filePath),
|
|
);
|
|
return safeParseWithSchema(AuthJsonShapeSchema, parsed) ?? {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pi-coding-agent's ModelRegistry/AuthStorage expects credentials in auth.json.
|
|
*
|
|
* OpenClaw stores credentials in auth-profiles.json instead. This helper
|
|
* bridges all credentials into agentDir/auth.json so pi-coding-agent can
|
|
* (a) consider providers authenticated and (b) include built-in models in its
|
|
* registry/catalog output.
|
|
*
|
|
* Syncs all credential types: api_key, token (as api_key), and oauth.
|
|
*
|
|
* @deprecated Runtime auth now comes from OpenClaw auth-profiles snapshots.
|
|
*/
|
|
export async function ensurePiAuthJsonFromAuthProfiles(agentDir: string): Promise<{
|
|
wrote: boolean;
|
|
authPath: string;
|
|
}> {
|
|
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
|
const authPath = path.join(agentDir, "auth.json");
|
|
const providerCredentials = resolvePiCredentialMapFromStore(store);
|
|
if (Object.keys(providerCredentials).length === 0) {
|
|
return { wrote: false, authPath };
|
|
}
|
|
|
|
const existing = await readAuthJson(agentDir, authPath);
|
|
let changed = false;
|
|
|
|
for (const [provider, cred] of Object.entries(providerCredentials)) {
|
|
const current = safeParseWithSchema(PiCredentialSchema, existing[provider]) ?? undefined;
|
|
if (!piCredentialsEqual(current, cred)) {
|
|
existing[provider] = cred;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (!changed) {
|
|
return { wrote: false, authPath };
|
|
}
|
|
|
|
await privateFileStore(agentDir).writeJson(path.basename(authPath), existing, {
|
|
trailingNewline: true,
|
|
});
|
|
|
|
return { wrote: true, authPath };
|
|
}
|