@openai-oauth/web 2.0.0-beta.1 → 2.0.0-beta.3

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/README.md CHANGED
@@ -24,6 +24,29 @@ await fetch("/api/chat", {
24
24
 
25
25
  `@openai-oauth/web` is the lower-level web package. Use it when you need browser primitives without React.
26
26
 
27
+ ### Hosted Sign-in
28
+
29
+ Hosted browser sign-in uses the open-source Sign in with ChatGPT extension for [Chrome](https://chromewebstore.google.com/detail/sign-in-with-chatgpt/odbgboachaefbbbdiffcefhpkekhfcna) or [Firefox](https://addons.mozilla.org/firefox/addon/sign-in-with-chatgpt/) to complete OpenAI's local OAuth callback. The extension shows the destination app for confirmation and then returns the callback directly to it.
30
+
31
+ `startLogin()` uses this extension flow by default. When installation is needed, it returns the correct Chrome Web Store or Firefox Add-ons URL for your interface to display:
32
+
33
+ ```ts
34
+ import { startLogin } from "@openai-oauth/web";
35
+
36
+ const result = await startLogin();
37
+
38
+ if (result.status === "needs-extension") {
39
+ installLink.href = result.installUrl;
40
+ installScreen.hidden = false;
41
+ }
42
+ ```
43
+
44
+ After installation, call `startLogin()` again when the user returns to your app or presses Sign in again. It will start OAuth once the extension is available.
45
+
46
+ The extension redirects only `http://localhost:1455/auth/callback`. After the user confirms the destination, call `completeLogin()` on the returned app URL to exchange the code and save the browser session.
47
+
48
+ `startLogin()` returns `{ status: "started" }` when OAuth begins or `{ status: "needs-extension", installUrl }` when your interface should show installation UI. Provide an explicit `redirectUri` only when your environment handles its own registered callback without the extension.
49
+
27
50
  ### Browser Session
28
51
 
29
52
  ```ts
@@ -114,6 +137,8 @@ await completeLogin();
114
137
  await logout();
115
138
  ```
116
139
 
140
+ `completeLogin()` returns the signed-in session when the current URL contains an OAuth callback, and `null` when there is no callback to complete.
141
+
117
142
  Useful browser options:
118
143
 
119
144
  ```ts
@@ -137,7 +162,6 @@ type WebServerOpenAIOAuthOptions = {
137
162
  headers?: Record<string, string>;
138
163
  instructions?: string;
139
164
  openAIBaseURL?: string;
140
- storeResponses?: boolean;
141
165
  };
142
166
  ```
143
167
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type FetchFunction, type OpenAIOAuthRequest, type OpenAIOAuthRequestOptions, type OpenAIOAuthSession, type SessionStore } from "@openai-oauth/core";
1
+ import { type FetchFunction, type OpenAIOAuthRequestOptions, type OpenAIOAuthSession, type SessionStore } from "@openai-oauth/core";
2
2
  export type { OpenAIOAuthSession, SessionStore };
3
3
  export type BrowserSessionStoreOptions = {
4
4
  dbName?: string;
@@ -62,7 +62,12 @@ export declare const exchangeCode: (input: ExchangeCodeInput, options?: Exchange
62
62
  export declare const refreshSession: (input: RefreshSessionInput, options?: RefreshSessionOptions) => Promise<OpenAIOAuthSession>;
63
63
  export declare const getSession: (options?: BrowserSessionOptions) => Promise<OpenAIOAuthSession | null>;
64
64
  export declare const openaiAuthHeaders: (options?: OpenAIAuthHeadersOptions) => Promise<OpenAIAuthHeaders>;
65
- export declare const startLogin: (options?: StartLoginOptions) => Promise<OpenAIOAuthRequest>;
65
+ export declare const startLogin: (options?: StartLoginOptions) => Promise<{
66
+ status: "needs-extension";
67
+ installUrl: string;
68
+ } | {
69
+ status: "started";
70
+ }>;
66
71
  export declare const completeLogin: (options?: CompleteLoginOptions) => Promise<OpenAIOAuthSession | null>;
67
72
  export declare const logout: (options?: LogoutOptions) => Promise<void>;
68
73
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EAIvB,KAAK,YAAY,EACjB,MAAM,oBAAoB,CAAA;AAE3B,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,CAAA;AAEhD,MAAM,MAAM,0BAA0B,GAAG;IACxC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,CAAA;AAEzD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,WAAW,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE3D,MAAM,MAAM,mBAAmB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,WAAW,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,GAAG;IAC9D,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEtD,MAAM,MAAM,iBAAiB,GAAG,IAAI,CACnC,yBAAyB,EACzB,aAAa,CACb,GAAG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IAClC,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAA;CAC3B,CAAA;AAsOD,eAAO,MAAM,kBAAkB,GAC9B,UAAS,0BAA+B,KACtC,YAiCF,CAAA;AA2ED,eAAO,MAAM,YAAY,GACxB,OAAO,iBAAiB,EACxB,UAAS,mBAAwB,KAC/B,OAAO,CAAC,kBAAkB,CAc5B,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,OAAO,mBAAmB,EAC1B,UAAS,qBAA0B,KACjC,OAAO,CAAC,kBAAkB,CAa5B,CAAA;AAED,eAAO,MAAM,UAAU,GACtB,UAAS,qBAA0B,KACjC,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAuBnC,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC7B,UAAS,wBAA6B,KACpC,OAAO,CAAC,iBAAiB,CAa3B,CAAA;AAuCD,eAAO,MAAM,UAAU,GACtB,UAAS,iBAAsB,KAC7B,OAAO,CAAC,kBAAkB,CAsC5B,CAAA;AAED,eAAO,MAAM,aAAa,GACzB,UAAS,oBAAyB,KAChC,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAgDnC,CAAA;AAED,eAAO,MAAM,MAAM,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,IAAI,CAKtE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -6,6 +6,15 @@ const defaultStoreSettings = {
6
6
  cryptoKey: "openai-oauth:crypto-key",
7
7
  };
8
8
  const pendingLoginKey = "openai-oauth:pending-login";
9
+ const browserExtensionStatePrefix = "oo2_";
10
+ const browserExtensionRedirectUri = "http://localhost:1455/auth/callback";
11
+ const browserExtensionId = "odbgboachaefbbbdiffcefhpkekhfcna";
12
+ const browserExtensionInstalledPath = "src/installed.json";
13
+ const chromeExtensionInstallUrl = "https://chromewebstore.google.com/detail/sign-in-with-chatgpt/odbgboachaefbbbdiffcefhpkekhfcna";
14
+ const firefoxExtensionInstallUrl = "https://addons.mozilla.org/firefox/addon/sign-in-with-chatgpt/";
15
+ const firefoxExtensionDetectionUrl = "http://localhost:1455/openai-oauth/installed";
16
+ const browserExtensionMarkerType = "openai-oauth:browser-extension-installed";
17
+ const browserExtensionDetectionTimeoutMs = 750;
9
18
  const refreshExpiryMarginMs = 5 * 60 * 1000;
10
19
  const refreshIntervalMs = 55 * 60 * 1000;
11
20
  const textEncoder = new TextEncoder();
@@ -22,6 +31,83 @@ const assertBrowserWindow = () => {
22
31
  }
23
32
  return window;
24
33
  };
34
+ const isFirefox = () => typeof navigator !== "undefined" &&
35
+ navigator.userAgent.toLowerCase().includes("firefox/");
36
+ const isChromeExtensionInstalled = async () => {
37
+ const fetchImpl = globalThis.fetch;
38
+ if (!fetchImpl) {
39
+ return false;
40
+ }
41
+ const controller = typeof AbortController === "undefined" ? null : new AbortController();
42
+ const timeout = controller
43
+ ? globalThis.setTimeout(() => controller.abort(), browserExtensionDetectionTimeoutMs)
44
+ : null;
45
+ try {
46
+ const response = await fetchImpl(`chrome-extension://${browserExtensionId}/${browserExtensionInstalledPath}`, {
47
+ cache: "no-store",
48
+ signal: controller?.signal,
49
+ });
50
+ if (!response.ok) {
51
+ return false;
52
+ }
53
+ const marker = (await response.json().catch(() => null));
54
+ return marker?.installed === true;
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ finally {
60
+ if (timeout !== null) {
61
+ globalThis.clearTimeout(timeout);
62
+ }
63
+ }
64
+ };
65
+ const isFirefoxExtensionInstalled = (browserWindow) => new Promise((resolve) => {
66
+ const document = browserWindow.document;
67
+ const parent = document?.body ?? document?.documentElement;
68
+ if (!document?.createElement || !parent) {
69
+ resolve(false);
70
+ return;
71
+ }
72
+ const iframe = document.createElement("iframe");
73
+ iframe.hidden = true;
74
+ iframe.setAttribute("aria-hidden", "true");
75
+ iframe.src = firefoxExtensionDetectionUrl;
76
+ let settled = false;
77
+ const finish = (installed) => {
78
+ if (settled) {
79
+ return;
80
+ }
81
+ settled = true;
82
+ globalThis.clearTimeout(timeout);
83
+ browserWindow.removeEventListener("message", onMessage);
84
+ iframe.remove();
85
+ resolve(installed);
86
+ };
87
+ const onMessage = (event) => {
88
+ const data = event.data;
89
+ if (event.source === iframe.contentWindow &&
90
+ event.origin.startsWith("moz-extension://") &&
91
+ data?.type === browserExtensionMarkerType &&
92
+ data.name === "sign-in-with-chatgpt" &&
93
+ data.protocol === "openai-oauth-browser-extension" &&
94
+ data.protocolVersion === 1) {
95
+ finish(true);
96
+ }
97
+ };
98
+ const timeout = globalThis.setTimeout(() => finish(false), browserExtensionDetectionTimeoutMs);
99
+ browserWindow.addEventListener("message", onMessage);
100
+ parent.appendChild(iframe);
101
+ });
102
+ const getBrowserExtension = (browserWindow) => isFirefox()
103
+ ? {
104
+ installUrl: firefoxExtensionInstallUrl,
105
+ isInstalled: () => isFirefoxExtensionInstalled(browserWindow),
106
+ }
107
+ : {
108
+ installUrl: chromeExtensionInstallUrl,
109
+ isInstalled: isChromeExtensionInstalled,
110
+ };
25
111
  const requestToPromise = (request) => new Promise((resolve, reject) => {
26
112
  request.onsuccess = () => resolve(request.result);
27
113
  request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed."));
@@ -207,6 +293,7 @@ const toSession = (token, options) => {
207
293
  return {
208
294
  accessToken: token.accessToken,
209
295
  accountId,
296
+ isFedRamp: token.isFedRamp,
210
297
  idToken: token.idToken,
211
298
  refreshToken: token.refreshToken ?? options.previousRefreshToken,
212
299
  expiresAt: typeof token.expiresIn === "number"
@@ -258,8 +345,11 @@ export const getSession = async (options = {}) => {
258
345
  const refreshed = await refreshSession({
259
346
  refreshToken: session.refreshToken,
260
347
  }, options);
261
- await sessionStore.set(refreshed);
262
- return refreshed;
348
+ const nextSession = session.isFedRamp && !refreshed.isFedRamp
349
+ ? { ...refreshed, isFedRamp: true }
350
+ : refreshed;
351
+ await sessionStore.set(nextSession);
352
+ return nextSession;
263
353
  };
264
354
  export const openaiAuthHeaders = async (options = {}) => {
265
355
  const session = await getSession(options);
@@ -272,6 +362,9 @@ export const openaiAuthHeaders = async (options = {}) => {
272
362
  const headers = new Headers(options.headers);
273
363
  headers.set("Authorization", `Bearer ${session.accessToken}`);
274
364
  headers.set("chatgpt-account-id", session.accountId);
365
+ if (session.isFedRamp) {
366
+ headers.set("X-OpenAI-Fedramp", "true");
367
+ }
275
368
  return toPlainHeaders(headers);
276
369
  };
277
370
  const toPlainHeaders = (headers) => {
@@ -302,15 +395,53 @@ const getCurrentRelativeUrl = () => {
302
395
  return `${browserWindow.location.pathname}${browserWindow.location.search}${browserWindow.location.hash}`;
303
396
  };
304
397
  const getDefaultRedirectUri = (callbackPath) => new URL(callbackPath, assertBrowserWindow().location.origin).toString();
398
+ const getCurrentUrl = () => new URL(getCurrentRelativeUrl(), assertBrowserWindow().location.origin).toString();
399
+ const bytesToBase64Url = (bytes) => bytesToBase64(bytes)
400
+ .replaceAll("+", "-")
401
+ .replaceAll("/", "_")
402
+ .replace(/=+$/, "");
403
+ const randomURLSafeString = (byteLength) => {
404
+ const bytes = new Uint8Array(byteLength);
405
+ globalThis.crypto.getRandomValues(bytes);
406
+ return bytesToBase64Url(bytes);
407
+ };
408
+ const encodeBase64Url = (value) => bytesToBase64Url(textEncoder.encode(value));
409
+ const createBrowserExtensionState = (callbackUrl, appState) => {
410
+ const payload = {
411
+ type: "openai-oauth-callback",
412
+ version: 1,
413
+ nonce: randomURLSafeString(24),
414
+ callbackUrl,
415
+ };
416
+ if (appState) {
417
+ payload.appState = appState;
418
+ }
419
+ return `${browserExtensionStatePrefix}${encodeBase64Url(JSON.stringify(payload))}`;
420
+ };
305
421
  export const startLogin = async (options = {}) => {
306
422
  const browserWindow = assertBrowserWindow();
307
- const redirectUri = options.redirectUri ??
308
- getDefaultRedirectUri(options.callbackPath ?? "/auth/callback");
423
+ const usesBrowserExtension = options.redirectUri === undefined;
424
+ const browserExtension = getBrowserExtension(browserWindow);
425
+ if (usesBrowserExtension && !(await browserExtension.isInstalled())) {
426
+ return {
427
+ status: "needs-extension",
428
+ installUrl: browserExtension.installUrl,
429
+ };
430
+ }
431
+ const returnTo = options.returnTo ?? getCurrentRelativeUrl();
432
+ const callbackUrl = options.callbackPath
433
+ ? getDefaultRedirectUri(options.callbackPath)
434
+ : usesBrowserExtension
435
+ ? getCurrentUrl()
436
+ : getDefaultRedirectUri("/auth/callback");
437
+ const redirectUri = options.redirectUri ?? browserExtensionRedirectUri;
309
438
  const request = await createOpenAIOAuthRequest({
310
439
  clientId: options.clientId,
311
440
  issuer: options.issuer,
312
441
  scope: options.scope,
313
- state: options.state,
442
+ state: usesBrowserExtension
443
+ ? createBrowserExtensionState(callbackUrl, options.state)
444
+ : options.state,
314
445
  codeVerifier: options.codeVerifier,
315
446
  simplifiedFlow: options.simplifiedFlow,
316
447
  idTokenAddOrganizations: options.idTokenAddOrganizations,
@@ -321,17 +452,17 @@ export const startLogin = async (options = {}) => {
321
452
  state: request.state,
322
453
  codeVerifier: request.codeVerifier,
323
454
  redirectUri: request.redirectUri,
324
- returnTo: options.returnTo ?? getCurrentRelativeUrl(),
455
+ returnTo,
325
456
  });
326
457
  if (options.openMode === "popup") {
327
458
  const popup = browserWindow.open(request.authorizationUrl, "_blank", "popup,width=520,height=720");
328
459
  if (!popup) {
329
460
  throw new Error("The ChatGPT login popup was blocked.");
330
461
  }
331
- return request;
462
+ return { status: "started" };
332
463
  }
333
464
  browserWindow.location.assign(request.authorizationUrl);
334
- return request;
465
+ return { status: "started" };
335
466
  };
336
467
  export const completeLogin = async (options = {}) => {
337
468
  const browserWindow = assertBrowserWindow();
@@ -352,6 +483,13 @@ export const completeLogin = async (options = {}) => {
352
483
  }
353
484
  }
354
485
  if (oauthError) {
486
+ if (oauthError === "access_denied" &&
487
+ pending &&
488
+ callbackState === pending.state) {
489
+ clearPendingLogin();
490
+ browserWindow.history.replaceState(null, "", pending.returnTo || "/");
491
+ return null;
492
+ }
355
493
  throw new Error(url.searchParams.get("error_description") ??
356
494
  `OpenAI OAuth returned ${oauthError}.`);
357
495
  }
package/dist/server.d.ts CHANGED
@@ -5,7 +5,6 @@ export type WebServerOpenAIOAuthOptions = {
5
5
  headers?: Record<string, string>;
6
6
  instructions?: string;
7
7
  openAIBaseURL?: string;
8
- storeResponses?: boolean;
9
8
  };
10
9
  export declare const openaiCredentials: (input: Request | Headers, options?: WebServerOpenAIOAuthOptions) => OpenAIOAuth;
11
10
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,aAAa,EACb,WAAW,EAEX,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,2BAA2B,GAAG;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AA4BD,eAAO,MAAM,iBAAiB,GAC7B,OAAO,OAAO,GAAG,OAAO,EACxB,UAAS,2BAAgC,KACvC,WAcF,CAAA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,aAAa,EACb,WAAW,EAEX,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,2BAA2B,GAAG;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AA6BD,eAAO,MAAM,iBAAiB,GAC7B,OAAO,OAAO,GAAG,OAAO,EACxB,UAAS,2BAAgC,KACvC,WAaF,CAAA"}
package/dist/server.js CHANGED
@@ -14,6 +14,7 @@ const getRequestSession = (input) => {
14
14
  return {
15
15
  accessToken,
16
16
  accountId,
17
+ isFedRamp: headers.get("x-openai-fedramp") === "true",
17
18
  };
18
19
  };
19
20
  export const openaiCredentials = (input, options = {}) => {
@@ -25,7 +26,6 @@ export const openaiCredentials = (input, options = {}) => {
25
26
  headers: options.headers,
26
27
  instructions: options.instructions,
27
28
  openAIBaseURL: options.openAIBaseURL,
28
- storeResponses: options.storeResponses,
29
29
  getSession: async () => session,
30
30
  refreshSession: async () => session,
31
31
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openai-oauth/web",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.3",
4
4
  "description": "Browser credential storage and web utilities for openai-oauth.",
5
5
  "keywords": [
6
6
  "openai",
@@ -41,7 +41,7 @@
41
41
  "access": "public"
42
42
  },
43
43
  "dependencies": {
44
- "@openai-oauth/core": "2.0.0-beta.1"
44
+ "@openai-oauth/core": "2.0.0-beta.3"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/node": "20.17.24",