Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
305
extensions/openai/openai-chatgpt-device-code.ts
Normal file
305
extensions/openai/openai-chatgpt-device-code.ts
Normal file
@@ -0,0 +1,305 @@
|
||||
// Openai plugin module implements openai chatgpt device code behavior.
|
||||
import {
|
||||
positiveSecondsToSafeMilliseconds,
|
||||
resolveExpiresAtMsFromDurationSeconds,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
||||
import { resolveCodexAccessTokenExpiry } from "./openai-chatgpt-auth-identity.js";
|
||||
import { trimNonEmptyString } from "./openai-chatgpt-shared.js";
|
||||
|
||||
const OPENAI_AUTH_BASE_URL = "https://auth.openai.com";
|
||||
const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
||||
const OPENAI_CODEX_DEVICE_CODE_TIMEOUT_MS = 15 * 60_000;
|
||||
const OPENAI_CODEX_DEVICE_CODE_DEFAULT_INTERVAL_MS = 5_000;
|
||||
const OPENAI_CODEX_DEVICE_CODE_MIN_INTERVAL_MS = 1_000;
|
||||
const OPENAI_CODEX_DEVICE_CALLBACK_URL = `${OPENAI_AUTH_BASE_URL}/deviceauth/callback`;
|
||||
const OPENAI_CODEX_DEVICE_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
||||
const OPENAI_CODEX_DEVICE_JSON_BODY_LIMIT_BYTES = 256 * 1024;
|
||||
|
||||
function resolveOpenAICodexDeviceCodeHeaders(contentType: string): Record<string, string> {
|
||||
const version = process.env.OPENCLAW_VERSION?.trim();
|
||||
return {
|
||||
"Content-Type": contentType,
|
||||
originator: "openclaw",
|
||||
...(version ? { version } : {}),
|
||||
"User-Agent": version ? `openclaw/${version}` : "openclaw",
|
||||
};
|
||||
}
|
||||
|
||||
type OpenAICodexDeviceCodePrompt = {
|
||||
verificationUrl: string;
|
||||
userCode: string;
|
||||
expiresInMs: number;
|
||||
};
|
||||
|
||||
type OpenAICodexDeviceCodeCredentials = {
|
||||
access: string;
|
||||
refresh: string;
|
||||
expires: number;
|
||||
};
|
||||
|
||||
type DeviceCodeUserCodePayload = {
|
||||
device_auth_id?: unknown;
|
||||
user_code?: unknown;
|
||||
usercode?: unknown;
|
||||
interval?: unknown;
|
||||
};
|
||||
|
||||
type DeviceCodeTokenPayload = {
|
||||
authorization_code?: unknown;
|
||||
code_challenge?: unknown;
|
||||
code_verifier?: unknown;
|
||||
};
|
||||
|
||||
type OAuthTokenPayload = {
|
||||
access_token?: unknown;
|
||||
refresh_token?: unknown;
|
||||
expires_in?: unknown;
|
||||
};
|
||||
|
||||
type RequestedDeviceCode = {
|
||||
deviceAuthId: string;
|
||||
userCode: string;
|
||||
verificationUrl: string;
|
||||
intervalMs: number;
|
||||
};
|
||||
|
||||
type DeviceCodeAuthorizationCode = {
|
||||
authorizationCode: string;
|
||||
codeVerifier: string;
|
||||
};
|
||||
|
||||
function parseJsonObject(text: string): Record<string, unknown> | null {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
return parsed && typeof parsed === "object" ? (parsed as Record<string, unknown>) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeDeviceCodeErrorText(value: string): string {
|
||||
const esc = String.fromCharCode(0x1b);
|
||||
const ansiCsiRegex = new RegExp(`${esc}\\[[\\u0020-\\u003f]*[\\u0040-\\u007e]`, "g");
|
||||
const osc8Regex = new RegExp(`${esc}\\]8;;.*?${esc}\\\\|${esc}\\]8;;${esc}\\\\`, "g");
|
||||
const c0Start = String.fromCharCode(0x00);
|
||||
const c0End = String.fromCharCode(0x1f);
|
||||
const del = String.fromCharCode(0x7f);
|
||||
const c1Start = String.fromCharCode(0x80);
|
||||
const c1End = String.fromCharCode(0x9f);
|
||||
const controlCharsRegex = new RegExp(`[${c0Start}-${c0End}${del}${c1Start}-${c1End}]`, "g");
|
||||
return value
|
||||
.replace(osc8Regex, "")
|
||||
.replace(ansiCsiRegex, "")
|
||||
.replace(controlCharsRegex, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function resolveNextDeviceCodePollDelayMs(intervalMs: number, deadlineMs: number): number {
|
||||
const remainingMs = Math.max(0, deadlineMs - Date.now());
|
||||
return Math.min(Math.max(intervalMs, OPENAI_CODEX_DEVICE_CODE_MIN_INTERVAL_MS), remainingMs);
|
||||
}
|
||||
|
||||
function formatDeviceCodeError(params: {
|
||||
prefix: string;
|
||||
status: number;
|
||||
bodyText: string;
|
||||
}): string {
|
||||
const body = parseJsonObject(params.bodyText);
|
||||
const error = trimNonEmptyString(body?.error);
|
||||
const description = trimNonEmptyString(body?.error_description);
|
||||
const safeError = error ? sanitizeDeviceCodeErrorText(error) : undefined;
|
||||
const safeDescription = description ? sanitizeDeviceCodeErrorText(description) : undefined;
|
||||
if (safeError && safeDescription) {
|
||||
return `${params.prefix}: ${safeError} (${safeDescription})`;
|
||||
}
|
||||
if (safeError) {
|
||||
return `${params.prefix}: ${safeError}`;
|
||||
}
|
||||
const bodyText = sanitizeDeviceCodeErrorText(params.bodyText);
|
||||
return bodyText
|
||||
? `${params.prefix}: HTTP ${params.status} ${bodyText}`
|
||||
: `${params.prefix}: HTTP ${params.status}`;
|
||||
}
|
||||
|
||||
async function readOpenAICodexDeviceBody(response: Response): Promise<string> {
|
||||
return await readResponseTextLimited(
|
||||
response,
|
||||
response.ok
|
||||
? OPENAI_CODEX_DEVICE_JSON_BODY_LIMIT_BYTES
|
||||
: OPENAI_CODEX_DEVICE_ERROR_BODY_LIMIT_BYTES,
|
||||
);
|
||||
}
|
||||
|
||||
async function requestOpenAICodexDeviceCode(fetchFn: typeof fetch): Promise<RequestedDeviceCode> {
|
||||
const response = await fetchFn(`${OPENAI_AUTH_BASE_URL}/api/accounts/deviceauth/usercode`, {
|
||||
method: "POST",
|
||||
headers: resolveOpenAICodexDeviceCodeHeaders("application/json"),
|
||||
body: JSON.stringify({
|
||||
client_id: OPENAI_CODEX_CLIENT_ID,
|
||||
}),
|
||||
});
|
||||
|
||||
const bodyText = await readOpenAICodexDeviceBody(response);
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
throw new Error(
|
||||
"OpenAI Codex device code login is not enabled for this server. Use ChatGPT OAuth instead.",
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
formatDeviceCodeError({
|
||||
prefix: "OpenAI device code request failed",
|
||||
status: response.status,
|
||||
bodyText,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const body = parseJsonObject(bodyText) as DeviceCodeUserCodePayload | null;
|
||||
const deviceAuthId = trimNonEmptyString(body?.device_auth_id);
|
||||
const userCode = trimNonEmptyString(body?.user_code) ?? trimNonEmptyString(body?.usercode);
|
||||
if (!deviceAuthId || !userCode) {
|
||||
throw new Error("OpenAI device code response was missing the device code or user code.");
|
||||
}
|
||||
|
||||
return {
|
||||
deviceAuthId,
|
||||
userCode,
|
||||
verificationUrl: `${OPENAI_AUTH_BASE_URL}/codex/device`,
|
||||
intervalMs:
|
||||
positiveSecondsToSafeMilliseconds(body?.interval) ??
|
||||
OPENAI_CODEX_DEVICE_CODE_DEFAULT_INTERVAL_MS,
|
||||
};
|
||||
}
|
||||
|
||||
async function pollOpenAICodexDeviceCode(params: {
|
||||
fetchFn: typeof fetch;
|
||||
deviceAuthId: string;
|
||||
userCode: string;
|
||||
intervalMs: number;
|
||||
}): Promise<DeviceCodeAuthorizationCode> {
|
||||
const deadline = Date.now() + OPENAI_CODEX_DEVICE_CODE_TIMEOUT_MS;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const response = await params.fetchFn(`${OPENAI_AUTH_BASE_URL}/api/accounts/deviceauth/token`, {
|
||||
method: "POST",
|
||||
headers: resolveOpenAICodexDeviceCodeHeaders("application/json"),
|
||||
body: JSON.stringify({
|
||||
device_auth_id: params.deviceAuthId,
|
||||
user_code: params.userCode,
|
||||
}),
|
||||
});
|
||||
|
||||
const bodyText = await readOpenAICodexDeviceBody(response);
|
||||
if (response.ok) {
|
||||
const body = parseJsonObject(bodyText) as DeviceCodeTokenPayload | null;
|
||||
const authorizationCode = trimNonEmptyString(body?.authorization_code);
|
||||
const codeVerifier = trimNonEmptyString(body?.code_verifier);
|
||||
if (!authorizationCode || !codeVerifier) {
|
||||
throw new Error("OpenAI device authorization response was missing the exchange code.");
|
||||
}
|
||||
return {
|
||||
authorizationCode,
|
||||
codeVerifier,
|
||||
};
|
||||
}
|
||||
|
||||
if (response.status === 403 || response.status === 404) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, resolveNextDeviceCodePollDelayMs(params.intervalMs, deadline));
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
formatDeviceCodeError({
|
||||
prefix: "OpenAI device authorization failed",
|
||||
status: response.status,
|
||||
bodyText,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("OpenAI device authorization timed out after 15 minutes.");
|
||||
}
|
||||
|
||||
async function exchangeOpenAICodexDeviceCode(params: {
|
||||
fetchFn: typeof fetch;
|
||||
authorizationCode: string;
|
||||
codeVerifier: string;
|
||||
}): Promise<OpenAICodexDeviceCodeCredentials> {
|
||||
const response = await params.fetchFn(`${OPENAI_AUTH_BASE_URL}/oauth/token`, {
|
||||
method: "POST",
|
||||
headers: resolveOpenAICodexDeviceCodeHeaders("application/x-www-form-urlencoded"),
|
||||
body: new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
code: params.authorizationCode,
|
||||
redirect_uri: OPENAI_CODEX_DEVICE_CALLBACK_URL,
|
||||
client_id: OPENAI_CODEX_CLIENT_ID,
|
||||
code_verifier: params.codeVerifier,
|
||||
}),
|
||||
});
|
||||
|
||||
const bodyText = await readOpenAICodexDeviceBody(response);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
formatDeviceCodeError({
|
||||
prefix: "OpenAI device token exchange failed",
|
||||
status: response.status,
|
||||
bodyText,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const body = parseJsonObject(bodyText) as OAuthTokenPayload | null;
|
||||
const access = trimNonEmptyString(body?.access_token);
|
||||
const refresh = trimNonEmptyString(body?.refresh_token);
|
||||
if (!access || !refresh) {
|
||||
throw new Error("OpenAI token exchange succeeded but did not return OAuth tokens.");
|
||||
}
|
||||
|
||||
const expires =
|
||||
resolveExpiresAtMsFromDurationSeconds(body?.expires_in) ??
|
||||
resolveCodexAccessTokenExpiry(access) ??
|
||||
Date.now();
|
||||
|
||||
return {
|
||||
access,
|
||||
refresh,
|
||||
expires,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loginOpenAICodexDeviceCode(params: {
|
||||
fetchFn?: typeof fetch;
|
||||
onVerification: (prompt: OpenAICodexDeviceCodePrompt) => Promise<void> | void;
|
||||
onProgress?: (message: string) => void;
|
||||
}): Promise<OpenAICodexDeviceCodeCredentials> {
|
||||
const fetchFn = params.fetchFn ?? fetch;
|
||||
|
||||
params.onProgress?.("Requesting device code…");
|
||||
const deviceCode = await requestOpenAICodexDeviceCode(fetchFn);
|
||||
|
||||
await params.onVerification({
|
||||
verificationUrl: deviceCode.verificationUrl,
|
||||
userCode: deviceCode.userCode,
|
||||
expiresInMs: OPENAI_CODEX_DEVICE_CODE_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
params.onProgress?.("Waiting for device authorization…");
|
||||
const authorization = await pollOpenAICodexDeviceCode({
|
||||
fetchFn,
|
||||
deviceAuthId: deviceCode.deviceAuthId,
|
||||
userCode: deviceCode.userCode,
|
||||
intervalMs: deviceCode.intervalMs,
|
||||
});
|
||||
|
||||
params.onProgress?.("Exchanging device code…");
|
||||
return await exchangeOpenAICodexDeviceCode({
|
||||
fetchFn,
|
||||
authorizationCode: authorization.authorizationCode,
|
||||
codeVerifier: authorization.codeVerifier,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user