refactor(auth): simplify codex login follow-up

This commit is contained in:
Ayaan Zaidi
2026-07-01 09:43:34 -07:00
parent 26a756ee84
commit 53fe2e4bc3
6 changed files with 14 additions and 41 deletions
@@ -1,2 +1,2 @@
5f576203a58926c97b80e2ecab7a44a2021ab487b9ecbb2365bcede01935869b plugin-sdk-api-baseline.json
674bb8dc19ddc39ed5571f38af4646ded5db3fba70eb57f33a8fd2cfe3f4d1bc plugin-sdk-api-baseline.jsonl
1a66a755b98a7d4ff29fe7ccd19cd525c41677eccf1d954b0dc7ed71902e66bf plugin-sdk-api-baseline.json
93efacdebaa86153a980ea0c1db713548aeed91ad6e76b914b899746896b1b32 plugin-sdk-api-baseline.jsonl
+2 -2
View File
@@ -202,8 +202,8 @@ let publicDeprecatedExportsByEntrypointBudget;
try {
budgets = {
publicEntrypoints: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_ENTRYPOINTS", 323),
publicExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_EXPORTS", 10405),
publicFunctionExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_FUNCTION_EXPORTS", 5223),
publicExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_EXPORTS", 10409),
publicFunctionExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_FUNCTION_EXPORTS", 5224),
publicDeprecatedExports: readBudgetEnv(
"OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_DEPRECATED_EXPORTS",
3261,
@@ -1,4 +1,3 @@
// Tests channel-native Codex login command routing and pairing-code delivery.
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ModelsAuthLoginFlowOptions } from "../../commands/models/auth.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
+10 -24
View File
@@ -1,4 +1,3 @@
/** Handles channel-native Codex/OpenAI login commands. */
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -29,10 +28,6 @@ function parseLoginCommand(commandBodyNormalized: string): { providerInput: stri
return { providerInput };
}
function hasConfiguredCommandOwners(params: HandleCommandsParams): boolean {
return codexChannelLoginRuntime.hasConfiguredCommandOwnerAllowlist(params.cfg);
}
function hasInternalAdminScope(params: HandleCommandsParams): boolean {
return (
Array.isArray(params.ctx.GatewayClientScopes) &&
@@ -44,7 +39,8 @@ function canStartCodexLogin(params: HandleCommandsParams): boolean {
return (
params.command.isAuthorizedSender &&
params.command.senderIsOwner &&
(hasConfiguredCommandOwners(params) || hasInternalAdminScope(params))
(codexChannelLoginRuntime.hasConfiguredCommandOwnerAllowlist(params.cfg) ||
hasInternalAdminScope(params))
);
}
@@ -132,10 +128,6 @@ function resolveLoginAgentId(params: HandleCommandsParams): string | undefined {
);
}
function buildFinalReply(status: string): ReplyPayload {
return { text: status };
}
async function emitLoginMessage(params: HandleCommandsParams, text: string): Promise<void> {
const trimmed = text.trim();
if (!trimmed) {
@@ -157,6 +149,12 @@ async function runChannelCodexLogin(params: {
runtime?: RuntimeEnv;
}): Promise<ReplyPayload> {
const flowKey = buildCodexLoginFlowKey(params.commandParams, params.provider);
if (!params.commandParams.opts?.onBlockReply) {
return {
text: "Codex login needs a live private response path so the code can be shown before it expires. Use the Web UI or a private chat and send `/login codex` again.",
};
}
const reservation = codexChannelLoginRuntime.reserveFlow({
flows: activeCodexLoginFlows,
flowKey,
@@ -166,16 +164,6 @@ async function runChannelCodexLogin(params: {
text: "A Codex login code is already active for this chat or channel. Complete it, or wait for it to expire before requesting a new one.",
};
}
if (!params.commandParams.opts?.onBlockReply) {
codexChannelLoginRuntime.releaseFlow({
flows: activeCodexLoginFlows,
flowKey,
record: reservation.record,
});
return {
text: "Codex login needs a live private response path so the code can be shown before it expires. Use the Web UI or a private chat and send `/login codex` again.",
};
}
try {
await codexChannelLoginRuntime.runDeviceLoginFlow({
@@ -188,11 +176,9 @@ async function runChannelCodexLogin(params: {
unsupportedPromptMessage: "Channel /login supports only fixed Codex device-code auth.",
runLoginFlow: params.runLoginFlow,
});
return buildFinalReply("Codex login complete. Try your request again now.");
return { text: "Codex login complete. Try your request again now." };
} catch {
return buildFinalReply(
"Codex login did not complete. Send `/login codex` to request a new code.",
);
return { text: "Codex login did not complete. Send `/login codex` to request a new code." };
} finally {
codexChannelLoginRuntime.releaseFlow({
flows: activeCodexLoginFlows,
-2
View File
@@ -991,7 +991,6 @@ function maybeLogOpenAICodexNativeSearchTip(runtime: RuntimeEnv, providerId: str
);
}
/** Runs interactive provider auth login and persists returned profiles. */
export async function runModelsAuthLoginFlow(
opts: ModelsAuthLoginFlowOptions,
): Promise<ModelsAuthLoginFlowResult> {
@@ -1099,7 +1098,6 @@ export async function runModelsAuthLoginFlow(
};
}
/** Runs interactive provider auth login and persists returned profiles. */
export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: RuntimeEnv) {
if (!process.stdin.isTTY) {
throw new Error(
@@ -1,4 +1,3 @@
// Lazy runtime facade for channel-triggered provider auth login flows.
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -37,11 +36,9 @@ const bindProviderAuthLoginFlowRuntime = createLazyRuntimeMethodBinder(
loadProviderAuthLoginFlowRuntime,
);
/** Runs provider login and persists returned auth profiles. Loaded lazily for channel /login. */
export const runModelsAuthLoginFlow: ProviderAuthLoginFlowRuntime["runModelsAuthLoginFlow"] =
bindProviderAuthLoginFlowRuntime((runtime) => runtime.runModelsAuthLoginFlow);
/** Normalizes channel login aliases to the provider that owns Codex ChatGPT auth. */
function resolveCodexLoginProvider(rawProvider: string | undefined): string | null {
const normalized = normalizeLowercaseStringOrEmpty(rawProvider ?? "codex").replace(/_/gu, "-");
if (!normalized) {
@@ -50,13 +47,11 @@ function resolveCodexLoginProvider(rawProvider: string | undefined): string | nu
return CODEX_LOGIN_PROVIDER_ALIASES.has(normalized) ? CODEX_LOGIN_PROVIDER : null;
}
/** Checks whether command-owner login is explicitly configured. */
function hasConfiguredCommandOwnerAllowlist(cfg: OpenClawConfig): boolean {
const owners = cfg.commands?.ownerAllowFrom;
return Array.isArray(owners) && owners.some((owner) => normalizeOptionalString(String(owner)));
}
/** Keeps a pinned auth profile only when it belongs to the requested provider. */
function resolveProviderScopedProfileId(
authProfileOverride: string | undefined,
provider: string,
@@ -71,7 +66,6 @@ function resolveProviderScopedProfileId(
: undefined;
}
/** Reserves a login flow key while preserving active non-expired flow dedupe. */
function reserveCodexLoginFlow(params: {
flows: Map<string, CodexLoginFlowRecord>;
flowKey: string;
@@ -90,7 +84,6 @@ function reserveCodexLoginFlow(params: {
return { status: "reserved", record };
}
/** Releases a reserved login flow only if this caller still owns the reservation. */
function releaseCodexLoginFlow(params: {
flows: Map<string, CodexLoginFlowRecord>;
flowKey: string;
@@ -101,7 +94,6 @@ function releaseCodexLoginFlow(params: {
}
}
/** Builds a fixed device-code prompter that can only emit text to the channel. */
function buildCodexDeviceLoginPrompter(params: {
sendMessage: (message: string) => Promise<void>;
unsupportedPromptMessage: string;
@@ -133,7 +125,6 @@ function buildCodexDeviceLoginPrompter(params: {
};
}
/** Runs fixed Codex device-code login for private channel and Web UI surfaces. */
async function runCodexDeviceLoginFlow(params: {
provider: string;
agentId: string;
@@ -160,7 +151,6 @@ async function runCodexDeviceLoginFlow(params: {
});
}
/** Shared private-channel Codex login helpers for text and native command surfaces. */
export const codexChannelLoginRuntime = {
resolveProvider: resolveCodexLoginProvider,
hasConfiguredCommandOwnerAllowlist,