openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
112 lines (111 loc) • 6 kB
JavaScript
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js";
import "./utils-P__uGsPB.js";
import { w as resolveStateDir } from "./paths-D2sRr1a_.js";
import { r as LEGACY_IMPLICIT_AGENT_ID } from "./session-key-BnWWjqNc.js";
import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db-schema-version-c1ZL6JGz.js";
import { r as readConfigMachineState } from "./config-machine-state-BCereLZr.js";
import path from "node:path";
//#region src/agents/auth-profiles/shared-main-dir.ts
/** Resolve the shipped shared-main auth store, including its supported relocation. */
function resolveSharedMainAuthAgentDir(env = process.env) {
const configured = env.OPENCLAW_AGENT_DIR?.trim();
return configured ? resolveUserPath(configured, env) : path.join(resolveStateDir(env), "agents", LEGACY_IMPLICIT_AGENT_ID, "agent");
}
//#endregion
//#region src/agents/auth-profiles/path-resolve.ts
/**
* Auth profile path resolution.
* Centralizes canonical shared SQLite and cross-agent OAuth refresh lock paths.
*/
const SHARED_AUTH_STORE_STATE_KEY = "auth.sharedStore";
const SHARED_AUTH_STORE_OWNERSHIP_CACHE_LIMIT = 256;
function captureAuthProfileOwnerScope(env = process.env) {
return {
stateDir: path.resolve(resolveStateDir(env)),
sharedMainDir: path.resolve(resolveSharedMainAuthAgentDir(env))
};
}
const sharedAuthStoreOwnershipByDatabasePath = /* @__PURE__ */ new Map();
var InvalidSharedAuthStoreOwnershipError = class extends Error {
constructor(value) {
super(`Config machine state ${SHARED_AUTH_STORE_STATE_KEY} has an invalid shared auth store location (${JSON.stringify(value)}); run openclaw doctor --fix.`);
this.code = "INVALID_SHARED_AUTH_STORE_OWNERSHIP";
this.action = "openclaw doctor --fix";
this.stateKey = SHARED_AUTH_STORE_STATE_KEY;
this.name = "InvalidSharedAuthStoreOwnershipError";
}
};
function parseSharedAuthStoreOwnership(value) {
if (value === void 0) return { location: "legacy-main" };
if (isRecord(value) && Object.keys(value).length === 1 && (value.location === "legacy-main" || value.location === "state-db")) return { location: value.location };
throw new InvalidSharedAuthStoreOwnershipError(value);
}
/** Resolve the process-stable owner of the shared auth store. */
function resolveSharedAuthStoreOwnership(env = process.env) {
const databasePath = path.resolve(resolveOpenClawStateSqlitePath(env));
const cached = sharedAuthStoreOwnershipByDatabasePath.get(databasePath);
if (cached) return cached;
if (sharedAuthStoreOwnershipByDatabasePath.size >= SHARED_AUTH_STORE_OWNERSHIP_CACHE_LIMIT) throw new Error("Shared auth store ownership cache exceeded its process root limit; restart OpenClaw.");
const ownership = parseSharedAuthStoreOwnership(readConfigMachineState(SHARED_AUTH_STORE_STATE_KEY, {
env,
path: databasePath
}));
sharedAuthStoreOwnershipByDatabasePath.set(databasePath, ownership);
return ownership;
}
/** Update the process-stable cache after this process commits the ownership row. */
function noteCommittedSharedAuthStoreOwnership(ownership, env = process.env) {
const databasePath = path.resolve(resolveOpenClawStateSqlitePath(env));
sharedAuthStoreOwnershipByDatabasePath.set(databasePath, ownership);
}
/** Reload shared auth ownership after an explicit out-of-process auth mutation. */
function reloadSharedAuthStoreOwnership(env = process.env) {
const databasePath = path.resolve(resolveOpenClawStateSqlitePath(env));
const ownership = parseSharedAuthStoreOwnership(readConfigMachineState(SHARED_AUTH_STORE_STATE_KEY, {
env,
path: databasePath
}));
sharedAuthStoreOwnershipByDatabasePath.set(databasePath, ownership);
return ownership;
}
/** Resolve the canonical shared auth database path. */
function resolveSharedAuthStorePath(env = process.env) {
if (resolveSharedAuthStoreOwnership(env).location === "state-db") return resolveOpenClawStateSqlitePath(env);
return path.join(resolveSharedMainAuthAgentDir(env), "openclaw-agent.sqlite");
}
/**
* Resolve the path of the cross-agent, per-profile OAuth refresh coordination
* lock. The filename digests a JSON tuple of `[provider, profileId]` so it is
* filesystem-safe for arbitrary unicode/control-character inputs and always
* bounded in length. Tuple encoding makes it impossible to collide two distinct
* `(provider, profileId)` pairs by separator-sensitive string concatenation.
*
* This lock is the serialization point that prevents the `refresh_token_reused`
* storm when N agents share one OAuth profile (see issue #26322): every agent
* that attempts a refresh acquires this same file lock, so only one HTTP
* refresh is in-flight at a time and peers can adopt the resulting fresh
* credentials instead of racing against a single-use refresh token.
*
* The key intentionally includes `provider` so that two profiles that
* happen to share a `profileId` across providers (operator-renamed profile,
* test fixture, etc.) do not needlessly serialize against each other.
*/
function resolveOAuthRefreshLockPath(provider, profileId) {
const safeId = `lock-${oauthLockPathDigest(JSON.stringify([provider, profileId]))}`;
return path.join(resolveStateDir(), "locks", "oauth-refresh", safeId);
}
function oauthLockPathDigest(value) {
let left = 14695981039346656037n;
let right = 11160318154034397263n;
const prime = 1099511628211n;
const mask = 18446744073709551615n;
for (const byte of Buffer.from(value, "utf8")) {
const octet = BigInt(byte);
left = (left ^ octet) * prime & mask;
right = (right ^ octet + 11400714819323198485n) * prime & mask;
}
return `${left.toString(16).padStart(16, "0")}${right.toString(16).padStart(16, "0")}`;
}
//#endregion
export { resolveOAuthRefreshLockPath as a, resolveSharedMainAuthAgentDir as c, reloadSharedAuthStoreOwnership as i, captureAuthProfileOwnerScope as n, resolveSharedAuthStoreOwnership as o, noteCommittedSharedAuthStoreOwnership as r, resolveSharedAuthStorePath as s, SHARED_AUTH_STORE_STATE_KEY as t };