@pure01fx/dsh-openai-codex-auth 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/catalog.js ADDED
@@ -0,0 +1,383 @@
1
+ /** Native Codex model discovery, validation, and account-partitioned caching. */
2
+ import { createHash } from 'node:crypto';
3
+ import { nativeCodexEndpoint } from './endpoint.js';
4
+ import { attributionHeaders, LlmError } from '@deepseek-ai/dsh-llm';
5
+ import { CODEX_CLIENT_VERSION } from './upstream.js';
6
+ export { CODEX_CLIENT_VERSION } from './upstream.js';
7
+ export const CODEX_MODELS_URL = 'https://chatgpt.com/backend-api/codex/models';
8
+ export const CODEX_CATALOG_CACHE_TTL_MS = 5 * 60_000;
9
+ const CODEX_CATALOG_MAX_STALE_MS = 7 * 24 * 60 * 60_000;
10
+ const CODEX_CATALOG_TIMEOUT_MS = 5_000;
11
+ const MAX_CATALOG_BODY_LENGTH = 2 * 1024 * 1024;
12
+ const ORIGINATOR = 'dsh';
13
+ class CatalogFetchError extends LlmError {
14
+ allowsStale;
15
+ constructor(message, code, allowsStale, options) {
16
+ super(message, code, options);
17
+ this.allowsStale = allowsStale;
18
+ }
19
+ }
20
+ function throwIfAborted(signal) {
21
+ if (signal?.aborted)
22
+ throw new LlmError('native Codex catalog request was aborted', 'ABORTED');
23
+ }
24
+ function awaitWithSignal(promise, signal) {
25
+ if (signal === undefined)
26
+ return promise;
27
+ throwIfAborted(signal);
28
+ return new Promise((resolve, reject) => {
29
+ const abort = () => {
30
+ reject(new LlmError('native Codex catalog request was aborted', 'ABORTED'));
31
+ };
32
+ signal.addEventListener('abort', abort, { once: true });
33
+ promise.then(resolve, reject).finally(() => {
34
+ signal.removeEventListener('abort', abort);
35
+ });
36
+ });
37
+ }
38
+ function record(value) {
39
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
40
+ ? value
41
+ : undefined;
42
+ }
43
+ function nonEmptyString(value) {
44
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
45
+ }
46
+ function positiveNumber(value) {
47
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : undefined;
48
+ }
49
+ function invalidCatalog(message) {
50
+ return new CatalogFetchError(message, 'CATALOG_INVALID_RESPONSE', true);
51
+ }
52
+ function strictStringList(value, fallback = []) {
53
+ if (value === undefined)
54
+ return fallback;
55
+ if (!Array.isArray(value)
56
+ || !value.every(item => typeof item === 'string' && item.length > 0))
57
+ return undefined;
58
+ return value;
59
+ }
60
+ function parseReasoningLevels(value) {
61
+ if (!Array.isArray(value))
62
+ return undefined;
63
+ const levels = [];
64
+ for (const item of value) {
65
+ const row = record(item);
66
+ if (row === undefined)
67
+ return undefined;
68
+ const effort = nonEmptyString(row?.effort);
69
+ const description = nonEmptyString(row?.description);
70
+ if (effort === undefined || description === undefined)
71
+ return undefined;
72
+ levels.push({ effort, description });
73
+ }
74
+ return levels;
75
+ }
76
+ function parseServiceTiers(value) {
77
+ if (value === undefined)
78
+ return [];
79
+ if (!Array.isArray(value))
80
+ return undefined;
81
+ const tiers = [];
82
+ for (const item of value) {
83
+ const row = record(item);
84
+ if (row === undefined)
85
+ return undefined;
86
+ const id = nonEmptyString(row?.id);
87
+ const name = nonEmptyString(row?.name);
88
+ const description = nonEmptyString(row?.description);
89
+ if (id === undefined || name === undefined || description === undefined)
90
+ return undefined;
91
+ tiers.push({ id, name, description });
92
+ }
93
+ return tiers;
94
+ }
95
+ async function boundedResponseText(response) {
96
+ if (response.body === null)
97
+ return '';
98
+ const reader = response.body.getReader();
99
+ const chunks = [];
100
+ let total = 0;
101
+ try {
102
+ while (true) {
103
+ const { done, value } = await reader.read();
104
+ if (done)
105
+ break;
106
+ total += value.byteLength;
107
+ if (total > MAX_CATALOG_BODY_LENGTH) {
108
+ await reader.cancel();
109
+ throw new CatalogFetchError('native Codex catalog response exceeded the size limit', 'CATALOG_INVALID_RESPONSE', true);
110
+ }
111
+ chunks.push(value);
112
+ }
113
+ }
114
+ finally {
115
+ reader.releaseLock();
116
+ }
117
+ return Buffer.concat(chunks.map(chunk => Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength)), total).toString('utf8');
118
+ }
119
+ function parseModel(value) {
120
+ const row = record(value);
121
+ if (row === undefined)
122
+ throw invalidCatalog('native Codex catalog contained a non-object model entry');
123
+ const slug = nonEmptyString(row?.slug);
124
+ const displayName = nonEmptyString(row?.display_name);
125
+ const reasoning = parseReasoningLevels(row?.supported_reasoning_levels);
126
+ const visibility = row?.visibility;
127
+ const priority = row?.priority;
128
+ const additionalSpeedTiers = strictStringList(row?.additional_speed_tiers);
129
+ const serviceTiers = parseServiceTiers(row?.service_tiers);
130
+ const inputModalities = strictStringList(row?.input_modalities, ['text', 'image']);
131
+ if (slug === undefined
132
+ || displayName === undefined
133
+ || reasoning === undefined
134
+ || !['list', 'hide', 'none'].includes(String(visibility))
135
+ || !['unified_exec', 'disabled', 'default', 'local', 'shell_command'].includes(String(row?.shell_type))
136
+ || typeof row.supported_in_api !== 'boolean'
137
+ || typeof priority !== 'number' || !Number.isSafeInteger(priority)
138
+ || priority < -2_147_483_648 || priority > 2_147_483_647
139
+ || additionalSpeedTiers === undefined
140
+ || serviceTiers === undefined
141
+ || inputModalities === undefined
142
+ || !inputModalities.every(value => ['text', 'image', 'audio'].includes(value))) {
143
+ throw invalidCatalog('native Codex catalog contained an invalid model entry');
144
+ }
145
+ if (row.description !== undefined && row.description !== null && typeof row.description !== 'string') {
146
+ throw invalidCatalog('native Codex catalog contained an invalid model description');
147
+ }
148
+ if (row.default_reasoning_level !== undefined
149
+ && row.default_reasoning_level !== null
150
+ && nonEmptyString(row.default_reasoning_level) === undefined) {
151
+ throw invalidCatalog('native Codex catalog contained an invalid default reasoning level');
152
+ }
153
+ if (row.default_service_tier !== undefined
154
+ && row.default_service_tier !== null
155
+ && nonEmptyString(row.default_service_tier) === undefined) {
156
+ throw invalidCatalog('native Codex catalog contained an invalid default service tier');
157
+ }
158
+ if (row.multi_agent_reasoning_effort !== undefined
159
+ && row.multi_agent_reasoning_effort !== null
160
+ && nonEmptyString(row.multi_agent_reasoning_effort) === undefined) {
161
+ throw invalidCatalog('native Codex catalog contained an invalid multi-agent reasoning effort');
162
+ }
163
+ for (const key of ['context_window', 'max_context_window']) {
164
+ if (row[key] !== undefined && row[key] !== null && positiveNumber(row[key]) === undefined) {
165
+ throw invalidCatalog('native Codex catalog contained an invalid context window');
166
+ }
167
+ }
168
+ const description = nonEmptyString(row.description);
169
+ const defaultReasoningLevel = nonEmptyString(row.default_reasoning_level);
170
+ const multiAgentReasoningEffort = nonEmptyString(row.multi_agent_reasoning_effort);
171
+ const defaultServiceTier = nonEmptyString(row.default_service_tier);
172
+ const contextWindow = positiveNumber(row.context_window) ?? positiveNumber(row.max_context_window);
173
+ return {
174
+ slug,
175
+ displayName,
176
+ ...description === undefined ? {} : { description },
177
+ ...defaultReasoningLevel === undefined ? {} : { defaultReasoningLevel },
178
+ supportedReasoningLevels: reasoning,
179
+ ...multiAgentReasoningEffort === undefined ? {} : { multiAgentReasoningEffort },
180
+ visibility: visibility,
181
+ supportedInApi: row.supported_in_api,
182
+ priority,
183
+ additionalSpeedTiers,
184
+ serviceTiers,
185
+ ...defaultServiceTier === undefined ? {} : { defaultServiceTier },
186
+ ...contextWindow === undefined ? {} : { contextWindow },
187
+ inputModalities,
188
+ };
189
+ }
190
+ function parseModelsPayload(value) {
191
+ const rows = record(value)?.models;
192
+ if (!Array.isArray(rows))
193
+ throw invalidCatalog('native Codex catalog response has no models array');
194
+ if (rows.length === 0) {
195
+ throw new CatalogFetchError('native Codex catalog response contained no usable models', 'CATALOG_EMPTY', true);
196
+ }
197
+ const models = rows.map(parseModel);
198
+ const slugs = new Set(models.map(model => model.slug));
199
+ if (slugs.size !== models.length)
200
+ throw invalidCatalog('native Codex catalog contained duplicate model ids');
201
+ if (!models.some(model => model.visibility === 'list')) {
202
+ throw new CatalogFetchError('native Codex catalog response contained no picker-visible models', 'CATALOG_EMPTY', true);
203
+ }
204
+ return models;
205
+ }
206
+ export function nativeCodexAuthorityHash(accountId) {
207
+ return createHash('sha256').update(accountId).digest('hex');
208
+ }
209
+ /** Codex-compatible live catalog with bounded stale fallback and no credential retention. */
210
+ export class NativeCodexCatalog {
211
+ options;
212
+ endpoint;
213
+ clientVersion;
214
+ cacheTtlMs;
215
+ maxStaleMs;
216
+ timeoutMs;
217
+ fetchImpl;
218
+ now;
219
+ snapshot;
220
+ refreshes = new Map();
221
+ currentEtag;
222
+ constructor(options) {
223
+ this.options = options;
224
+ this.endpoint = nativeCodexEndpoint(options.endpoint ?? CODEX_MODELS_URL).toString();
225
+ this.clientVersion = options.clientVersion ?? CODEX_CLIENT_VERSION;
226
+ this.cacheTtlMs = options.cacheTtlMs ?? CODEX_CATALOG_CACHE_TTL_MS;
227
+ this.maxStaleMs = options.maxStaleMs ?? CODEX_CATALOG_MAX_STALE_MS;
228
+ this.timeoutMs = options.timeoutMs ?? CODEX_CATALOG_TIMEOUT_MS;
229
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
230
+ this.now = options.now ?? Date.now;
231
+ }
232
+ etag() {
233
+ return this.currentEtag;
234
+ }
235
+ async list(signal) {
236
+ return (await this.listWithAuthority(signal)).models;
237
+ }
238
+ async listWithAuthority(signal) {
239
+ throwIfAborted(signal);
240
+ this.currentEtag = undefined;
241
+ let credential;
242
+ try {
243
+ credential = await this.options.resolveCredential(signal);
244
+ }
245
+ catch (error) {
246
+ if (error instanceof LlmError && error.code === 'ABORTED')
247
+ throw error;
248
+ if (!(error instanceof LlmError) || error.code !== 'MISSING_CREDENTIAL') {
249
+ this.options.warn?.('native Codex credential is unavailable; model metadata is advisory');
250
+ }
251
+ return { models: [] };
252
+ }
253
+ throwIfAborted(signal);
254
+ const hash = nativeCodexAuthorityHash(credential.accountId);
255
+ const cached = this.snapshot?.clientVersion === this.clientVersion
256
+ && this.snapshot.accountHash === hash
257
+ ? this.snapshot
258
+ : undefined;
259
+ throwIfAborted(signal);
260
+ const age = cached === undefined ? Number.POSITIVE_INFINITY : this.now() - cached.fetchedAt;
261
+ if (cached !== undefined && age >= 0 && age <= this.cacheTtlMs) {
262
+ this.currentEtag = cached.etag;
263
+ return { models: cached.models, authorityHash: hash };
264
+ }
265
+ try {
266
+ let refresh = this.refreshes.get(hash);
267
+ if (refresh === undefined) {
268
+ const started = this.fetchCatalog(credential, hash)
269
+ .then((fresh) => {
270
+ this.snapshot = fresh;
271
+ return fresh;
272
+ })
273
+ .finally(() => {
274
+ if (this.refreshes.get(hash) === started)
275
+ this.refreshes.delete(hash);
276
+ });
277
+ refresh = started;
278
+ this.refreshes.set(hash, refresh);
279
+ }
280
+ const fresh = await awaitWithSignal(refresh, signal);
281
+ this.currentEtag = fresh.etag;
282
+ return { models: fresh.models, authorityHash: hash };
283
+ }
284
+ catch (error) {
285
+ if (error instanceof LlmError && error.code === 'ABORTED')
286
+ throw error;
287
+ if (error instanceof CatalogFetchError
288
+ && error.allowsStale
289
+ && cached !== undefined
290
+ && age >= 0
291
+ && age <= this.maxStaleMs) {
292
+ this.currentEtag = cached.etag;
293
+ this.options.warn?.(`native Codex catalog refresh failed (${error.code}); using bounded stale cache`);
294
+ return { models: cached.models, authorityHash: hash };
295
+ }
296
+ const code = error instanceof LlmError ? error.code : 'UNKNOWN';
297
+ this.options.warn?.(`native Codex catalog refresh failed (${code}); model metadata is advisory`);
298
+ return { models: [], authorityHash: hash };
299
+ }
300
+ }
301
+ async fetchCatalog(credential, hash, signal) {
302
+ const controller = new AbortController();
303
+ const abortFromCaller = () => { controller.abort(signal?.reason); };
304
+ if (signal !== undefined)
305
+ signal.addEventListener('abort', abortFromCaller, { once: true });
306
+ const timeout = setTimeout(() => { controller.abort(new Error('catalog timeout')); }, this.timeoutMs);
307
+ try {
308
+ throwIfAborted(signal);
309
+ const url = new URL(this.endpoint);
310
+ url.searchParams.set('client_version', this.clientVersion);
311
+ let response;
312
+ try {
313
+ response = await this.fetchImpl(url, {
314
+ method: 'GET',
315
+ redirect: 'error',
316
+ headers: {
317
+ authorization: `Bearer ${credential.accessToken}`,
318
+ 'chatgpt-account-id': credential.accountId,
319
+ originator: ORIGINATOR,
320
+ ...attributionHeaders(),
321
+ },
322
+ signal: controller.signal,
323
+ });
324
+ }
325
+ catch (error) {
326
+ if (signal?.aborted)
327
+ throw new LlmError('native Codex catalog request was aborted', 'ABORTED');
328
+ if (controller.signal.aborted) {
329
+ throw new CatalogFetchError('native Codex catalog request timed out', 'CATALOG_TIMEOUT', true, { cause: error });
330
+ }
331
+ throw new CatalogFetchError('native Codex catalog request failed', 'CATALOG_UNAVAILABLE', true, { cause: error });
332
+ }
333
+ throwIfAborted(signal);
334
+ if (!response.ok) {
335
+ const allowsStale = response.status === 408 || response.status === 429 || response.status >= 500;
336
+ const code = response.status === 401
337
+ ? 'INVALID_CREDENTIAL'
338
+ : response.status === 403
339
+ ? 'CATALOG_FORBIDDEN'
340
+ : 'CATALOG_HTTP_ERROR';
341
+ throw new CatalogFetchError(`native Codex catalog request failed (HTTP ${response.status})`, code, allowsStale);
342
+ }
343
+ let body;
344
+ try {
345
+ body = await boundedResponseText(response);
346
+ }
347
+ catch (error) {
348
+ if (error instanceof CatalogFetchError)
349
+ throw error;
350
+ if (signal?.aborted)
351
+ throw new LlmError('native Codex catalog request was aborted', 'ABORTED');
352
+ if (controller.signal.aborted) {
353
+ throw new CatalogFetchError('native Codex catalog request timed out', 'CATALOG_TIMEOUT', true, { cause: error });
354
+ }
355
+ throw new CatalogFetchError('native Codex catalog response could not be read', 'CATALOG_UNAVAILABLE', true, { cause: error });
356
+ }
357
+ throwIfAborted(signal);
358
+ let payload;
359
+ try {
360
+ payload = JSON.parse(body);
361
+ }
362
+ catch (error) {
363
+ throw new CatalogFetchError('native Codex catalog response was not valid JSON', 'CATALOG_INVALID_RESPONSE', true, { cause: error });
364
+ }
365
+ const entry = {
366
+ fetchedAt: this.now(),
367
+ clientVersion: this.clientVersion,
368
+ accountHash: hash,
369
+ ...nonEmptyString(response.headers.get('etag')) === undefined
370
+ ? {}
371
+ : { etag: response.headers.get('etag') },
372
+ models: parseModelsPayload(payload),
373
+ };
374
+ throwIfAborted(signal);
375
+ return entry;
376
+ }
377
+ finally {
378
+ clearTimeout(timeout);
379
+ if (signal !== undefined)
380
+ signal.removeEventListener('abort', abortFromCaller);
381
+ }
382
+ }
383
+ }
@@ -0,0 +1,2 @@
1
+ /** Validate authority, identity components, and TLS before credentials are attached. */
2
+ export declare function nativeCodexEndpoint(endpoint: string, allowWebSocketProtocols?: boolean): URL;
@@ -0,0 +1,38 @@
1
+ /** Shared credential-bearing endpoint validation for catalog, HTTP, and WebSocket. */
2
+ import { isIP } from 'node:net';
3
+ import { LlmError } from '@deepseek-ai/dsh-llm';
4
+ function invalid(message, cause) {
5
+ return new LlmError(message, 'INVALID_ARGS', cause === undefined ? undefined : { cause });
6
+ }
7
+ function loopback(hostname) {
8
+ const unbracketed = hostname.startsWith('[') && hostname.endsWith(']')
9
+ ? hostname.slice(1, -1) : hostname;
10
+ const family = isIP(unbracketed);
11
+ if (family === 4)
12
+ return unbracketed.split('.')[0] === '127';
13
+ return family === 6 && unbracketed === '::1';
14
+ }
15
+ /** Validate authority, identity components, and TLS before credentials are attached. */
16
+ export function nativeCodexEndpoint(endpoint, allowWebSocketProtocols = false) {
17
+ let url;
18
+ try {
19
+ url = new URL(endpoint);
20
+ }
21
+ catch (error) {
22
+ throw invalid('native Codex endpoint is invalid', error);
23
+ }
24
+ if (url.username.length > 0 || url.password.length > 0 || url.hash.length > 0) {
25
+ throw invalid('native Codex endpoint contains forbidden identity');
26
+ }
27
+ const local = loopback(url.hostname);
28
+ if (url.hostname !== 'chatgpt.com' && !local) {
29
+ throw invalid('native Codex endpoint authority is unsupported');
30
+ }
31
+ const secure = url.protocol === 'https:' || (allowWebSocketProtocols && url.protocol === 'wss:');
32
+ const localPlaintext = local && (url.protocol === 'http:'
33
+ || (allowWebSocketProtocols && url.protocol === 'ws:'));
34
+ if (!secure && !localPlaintext) {
35
+ throw invalid('native Codex plaintext endpoint must be loopback');
36
+ }
37
+ return url;
38
+ }
package/lib/index.d.ts CHANGED
@@ -3,6 +3,10 @@ import { Context, Service } from '@deepseek-ai/cordis';
3
3
  import z from '@deepseek-ai/schemastery';
4
4
  import { type IncomingMessage } from 'node:http';
5
5
  import type { WebServer } from '@deepseek-ai/dsh-host-webserver';
6
+ import { CODEX_PROVIDER } from './native-adapter.js';
7
+ export { CODEX_PROVIDER, NATIVE_CODEX_PROVIDER } from './native-adapter.js';
8
+ export { normalizeUsage } from './usage.js';
9
+ export { CODEX_CLIENT_VERSION, TRACKED_CODEX_COMMIT, TRACKED_CODEX_RELEASE, TRACKED_CODEX_REPOSITORY, } from './upstream.js';
6
10
  /** Persisted OAuth credential. */
7
11
  export interface OpenAICodexCredential {
8
12
  access: string;
@@ -14,24 +18,39 @@ export interface OpenAICodexCredential {
14
18
  export interface Config {
15
19
  path?: string;
16
20
  dshHome?: string;
17
- }
18
- interface UsageWindow {
19
- usedPercent: number;
20
- windowSeconds?: number;
21
- resetAt?: number;
22
- }
23
- interface UsageSummary {
24
- planType?: string;
25
- primary?: UsageWindow;
26
- secondary?: UsageWindow;
27
- limitReached?: boolean;
28
- resetCredits?: number;
29
- fetchedAt: number;
21
+ nativeAdapter?: boolean;
22
+ nativeCompatibilityRoute?: boolean;
23
+ nativeWebSocket?: boolean;
30
24
  }
31
25
  interface WebRuntimeValues {
32
26
  lanAddresses: string[];
33
27
  trustedHosts: string[];
34
28
  }
29
+ export type CodexRouteOwner = 'native' | 'external' | 'unregistered';
30
+ export type NativeCodexRouteTransport = 'websocket-v2' | 'http-sse';
31
+ /** Host-observed ownership of the production Codex provider route. */
32
+ export interface CodexRouteStatus {
33
+ provider: typeof CODEX_PROVIDER;
34
+ owner: CodexRouteOwner;
35
+ active: boolean;
36
+ registeredName?: string;
37
+ transport?: NativeCodexRouteTransport;
38
+ compatibilityRoute: {
39
+ configured: boolean;
40
+ active: boolean;
41
+ };
42
+ }
43
+ interface NativeRouteConfig {
44
+ nativeAdapter: boolean;
45
+ nativeCompatibilityRoute: boolean;
46
+ nativeWebSocket: boolean;
47
+ }
48
+ interface RouteRuntime {
49
+ listProviders(): Array<{
50
+ id: string;
51
+ name: string;
52
+ }>;
53
+ }
35
54
  interface DeviceAuthorization {
36
55
  deviceAuthId: string;
37
56
  userCode: string;
@@ -47,8 +66,13 @@ interface ParsedDevicePoll {
47
66
  value?: DeviceTokenCode;
48
67
  message?: string;
49
68
  }
50
- /** Reduce the OpenAI response to the stable fields displayed by the Web card. */
51
- export declare function normalizeUsage(value: unknown): UsageSummary;
69
+ interface TokenResponse {
70
+ access_token?: unknown;
71
+ refresh_token?: unknown;
72
+ expires_in?: unknown;
73
+ id_token?: unknown;
74
+ }
75
+ declare function parseTokenResponse(value: TokenResponse | null, previous?: OpenAICodexCredential): OpenAICodexCredential;
52
76
  declare function parseAuthority(authority: string | undefined): URL | undefined;
53
77
  declare function isLoopbackHostname(hostname: string): boolean;
54
78
  declare function isTrustedHost(authority: string | undefined, trustedHosts: readonly string[]): boolean;
@@ -58,7 +82,9 @@ declare function callbackHostMatches(authority: string | undefined, redirectUri:
58
82
  declare function startDeviceAuthorization(signal?: AbortSignal): Promise<DeviceAuthorization>;
59
83
  declare function parseDevicePollResponse(response: Response): Promise<ParsedDevicePoll>;
60
84
  declare function pollDeviceAuthorization(device: DeviceAuthorization, signal?: AbortSignal): Promise<DeviceTokenCode>;
85
+ declare function resolveCodexRouteStatus(config: NativeRouteConfig, llm: RouteRuntime | undefined): CodexRouteStatus;
61
86
  export declare const internals: {
87
+ parseTokenResponse: typeof parseTokenResponse;
62
88
  parseAuthority: typeof parseAuthority;
63
89
  isLoopbackHostname: typeof isLoopbackHostname;
64
90
  isTrustedHost: typeof isTrustedHost;
@@ -68,6 +94,7 @@ export declare const internals: {
68
94
  startDeviceAuthorization: typeof startDeviceAuthorization;
69
95
  parseDevicePollResponse: typeof parseDevicePollResponse;
70
96
  pollDeviceAuthorization: typeof pollDeviceAuthorization;
97
+ resolveCodexRouteStatus: typeof resolveCodexRouteStatus;
71
98
  };
72
99
  declare module '@deepseek-ai/cordis' {
73
100
  interface Context {
@@ -81,25 +108,44 @@ export declare class OpenAICodexAuth extends Service {
81
108
  static Config: z<Config>;
82
109
  static inject: string[];
83
110
  private readonly filename;
111
+ private readonly routeConfig;
84
112
  private readonly csrf;
85
113
  private usageCache;
114
+ private usageAccountId;
115
+ private responseUsage;
116
+ private credentialAccountId;
86
117
  private usageError;
87
118
  private usageRefresh;
88
119
  private usageGeneration;
120
+ private directUsageSequence;
121
+ private directUsageAccountId;
122
+ private usageHasDirectDefault;
89
123
  private readonly codexTurns;
90
124
  private loginFlow;
91
125
  private startingDevice;
92
126
  private startingBrowser;
93
127
  private lastLoginError;
94
128
  constructor(ctx: Context, config: Config);
129
+ private setCredentialAccount;
130
+ private acceptResponseUsage;
131
+ private acceptRateLimits;
95
132
  private markCodexTurn;
96
133
  private consumeCodexTurn;
97
134
  private performUsageRefresh;
98
135
  private refreshUsage;
99
136
  private assertCredentialWritable;
100
- private storeCredentialToken;
101
- /** Return a valid bearer token, refreshing and persisting it when near expiry. */
137
+ private publishCredentialToken;
138
+ private restorePublishedCredential;
139
+ private publicationChangedError;
140
+ private failAfterPublicationRollback;
141
+ private commitCredential;
142
+ private resolveManagedCredentialLocked;
143
+ /** Return a valid managed bearer token, refreshing and persisting it when near expiry. */
102
144
  bearerToken(signal?: AbortSignal): Promise<string | undefined>;
145
+ private externalNativeCredential;
146
+ private resolveNativeCredential;
147
+ private nativeRecoveryError;
148
+ private recoverNativeCredential;
103
149
  private finishCredential;
104
150
  private finishAuthorizationCode;
105
151
  private settleFlow;