naisys
Version:
NAISYS - Autonomous AI agent runner with built-in context management and cost tracking
65 lines • 3.12 kB
JavaScript
import { CODEX_REFRESH_TOKEN_VAR } from "@naisys/common";
import { createCodexAccessTokenProvider, } from "@naisys/common-node";
import { HubEvents } from "@naisys/hub-protocol";
import { sendWithOpenAiStandard } from "./openai-standard.js";
// Standalone runs as a single-writer process, but multiple LLM services
// (parent + subagents) share .env and must single-flight rotation through
// one provider — otherwise concurrent refreshes burn the token lineage at
// OpenAI. Module-scoped so all callers in the process share state. Hub
// mode is stateless on the agent side (hub owns rotation).
let standaloneProvider;
/**
* Builds the function vendors call to obtain a Codex OAuth access token.
*
* - Hub mode: each call round-trips the hub; the hub is the single-flight
* authority and already caches minted tokens. No agent-side cache — the
* socket hop is negligible vs LLM latency.
* - Standalone: rotate locally and persist the refresh token to .env. The
* process is single-writer (no concurrent supervisor) so the save path
* has no CAS to perform and always reports success.
*/
export function createCodexAccessTokenGetter(globalConfigService, hubClient) {
if (hubClient) {
return async (forceRefresh) => {
const response = await hubClient.sendRequest(HubEvents.CODEX_ACCESS_TOKEN_GET, forceRefresh ? { forceRefresh: true } : {});
if (!response.success || !response.accessToken) {
throw new Error(response.error ?? "Hub did not return an OpenAI Codex access token.");
}
return response.accessToken;
};
}
if (!standaloneProvider) {
standaloneProvider = createCodexAccessTokenProvider({
getRefreshToken: () => globalConfigService.globalConfig().variableMap[CODEX_REFRESH_TOKEN_VAR],
saveRefreshToken: (refreshToken) => {
globalConfigService.setVariableValue(CODEX_REFRESH_TOKEN_VAR, refreshToken, { exportToShell: false });
return Promise.resolve(true);
},
});
}
const provider = standaloneProvider;
return async (forceRefresh) => {
if (forceRefresh)
provider.invalidate();
return (await provider.getAccessToken())?.accessToken;
};
}
function isUnauthorizedError(err) {
const status = err?.status;
return status === 401;
}
export async function sendWithOpenAiOauth(deps, modelKey, systemMessage, context, source, abortSignal) {
const accessToken = await deps.getCodexAccessToken();
try {
return await sendWithOpenAiStandard(deps, modelKey, systemMessage, context, source, accessToken, abortSignal);
}
catch (err) {
if (!isUnauthorizedError(err))
throw err;
// Token rejected mid-life (revocation, account switch). Force-refresh
// and retry once; a second 401 is fatal.
const refreshedToken = await deps.getCodexAccessToken(true);
return sendWithOpenAiStandard(deps, modelKey, systemMessage, context, source, refreshedToken, abortSignal);
}
}
//# sourceMappingURL=openai-oauth.js.map