@sunshaobei/dsh-codex-auth 3.0.3 → 3.0.5

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.
Files changed (2) hide show
  1. package/lib/index.js +41 -0
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -384,6 +384,46 @@ function registerCodexAuthRpc(ctx, credentials, phase) {
384
384
  };
385
385
  ctx.effect(() => ctx.webServer.register(route), 'dsh-codex-auth: rpc channel');
386
386
  }
387
+ // ---------------------------------------------------------------------------
388
+ // silent token refresh (runs once per app start; no timers)
389
+ // ---------------------------------------------------------------------------
390
+
391
+ async function silentRefresh(credentials) {
392
+ try {
393
+ const record = await credentials.readRecord(CRED_KEY);
394
+ const payload = record && record.kind === 'grant' ? record.payload : undefined;
395
+ if (!payload || payload.type !== 'oauth' || !payload.refresh)
396
+ return;
397
+ const res = await fetch(TOKEN_URL, {
398
+ method: 'POST',
399
+ signal: AbortSignal.timeout(30000),
400
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
401
+ body: new URLSearchParams({
402
+ grant_type: 'refresh_token',
403
+ client_id: CLIENT_ID,
404
+ refresh_token: payload.refresh,
405
+ }),
406
+ });
407
+ if (!res.ok)
408
+ return; // keep the old grant; user can re-authorize if it expires
409
+ const json = await res.json().catch(() => undefined);
410
+ if (!json || typeof json.access_token !== 'string' || json.access_token.length === 0)
411
+ return;
412
+ const next = {
413
+ ...payload,
414
+ access: json.access_token,
415
+ refresh: typeof json.refresh_token === 'string' && json.refresh_token.length > 0 ? json.refresh_token : payload.refresh,
416
+ expires: Date.now() + Number(json.expires_in ?? 0) * 1000,
417
+ };
418
+ if (!(next.expires > Date.now()))
419
+ return;
420
+ await credentials.modifyRecord(CRED_KEY, async () => ({ kind: 'grant', payload: next }));
421
+ }
422
+ catch {
423
+ // silent: keep the old grant on any failure
424
+ }
425
+ }
426
+
387
427
  // ---------------------------------------------------------------------------
388
428
  // plugin entry
389
429
  // ---------------------------------------------------------------------------
@@ -391,4 +431,5 @@ export function apply(ctx, _config = {}) {
391
431
  const credentials = ctx.credentials;
392
432
  const phase = { phase: 'idle' };
393
433
  registerCodexAuthRpc(ctx, credentials, phase);
434
+ void silentRefresh(credentials);
394
435
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sunshaobei/dsh-codex-auth",
3
3
  "description": "Codex 订阅授权管理 for the dsh web surface: a dedicated settings page (authorization status, validity/expiry, one-click browser OAuth) plus an expired-credential modal when a GPT model is selected. Host half runs the full OpenAI OAuth flow (PKCE S256, localhost:1455 callback) and writes the llm-pi-ai/openai-codex grant record through ctx.credentials.",
4
- "version": "3.0.3",
4
+ "version": "3.0.5",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",