mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor(auth): simplify codex login follow-up
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user