opencode-openai-multi-auth 5.0.0

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 (89) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +145 -0
  3. package/assets/opencode-logo-ornate-dark.svg +18 -0
  4. package/assets/readme-hero.svg +31 -0
  5. package/config/README.md +103 -0
  6. package/config/minimal-opencode.json +12 -0
  7. package/config/opencode-legacy.json +571 -0
  8. package/config/opencode-modern.json +239 -0
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +321 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/accounts/index.d.ts +4 -0
  14. package/dist/lib/accounts/index.d.ts.map +1 -0
  15. package/dist/lib/accounts/index.js +3 -0
  16. package/dist/lib/accounts/index.js.map +1 -0
  17. package/dist/lib/accounts/manager.d.ts +23 -0
  18. package/dist/lib/accounts/manager.d.ts.map +1 -0
  19. package/dist/lib/accounts/manager.js +270 -0
  20. package/dist/lib/accounts/manager.js.map +1 -0
  21. package/dist/lib/accounts/types.d.ts +35 -0
  22. package/dist/lib/accounts/types.d.ts.map +1 -0
  23. package/dist/lib/accounts/types.js +10 -0
  24. package/dist/lib/accounts/types.js.map +1 -0
  25. package/dist/lib/auth/auth.d.ts +43 -0
  26. package/dist/lib/auth/auth.d.ts.map +1 -0
  27. package/dist/lib/auth/auth.js +163 -0
  28. package/dist/lib/auth/auth.js.map +1 -0
  29. package/dist/lib/auth/browser.d.ts +17 -0
  30. package/dist/lib/auth/browser.d.ts.map +1 -0
  31. package/dist/lib/auth/browser.js +76 -0
  32. package/dist/lib/auth/browser.js.map +1 -0
  33. package/dist/lib/auth/server.d.ts +10 -0
  34. package/dist/lib/auth/server.d.ts.map +1 -0
  35. package/dist/lib/auth/server.js +78 -0
  36. package/dist/lib/auth/server.js.map +1 -0
  37. package/dist/lib/config.d.ts +17 -0
  38. package/dist/lib/config.d.ts.map +1 -0
  39. package/dist/lib/config.js +51 -0
  40. package/dist/lib/config.js.map +1 -0
  41. package/dist/lib/constants.d.ts +67 -0
  42. package/dist/lib/constants.d.ts.map +1 -0
  43. package/dist/lib/constants.js +67 -0
  44. package/dist/lib/constants.js.map +1 -0
  45. package/dist/lib/logger.d.ts +21 -0
  46. package/dist/lib/logger.d.ts.map +1 -0
  47. package/dist/lib/logger.js +77 -0
  48. package/dist/lib/logger.js.map +1 -0
  49. package/dist/lib/oauth-success.html +712 -0
  50. package/dist/lib/prompts/codex-opencode-bridge.d.ts +19 -0
  51. package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
  52. package/dist/lib/prompts/codex-opencode-bridge.js +152 -0
  53. package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
  54. package/dist/lib/prompts/codex.d.ts +27 -0
  55. package/dist/lib/prompts/codex.d.ts.map +1 -0
  56. package/dist/lib/prompts/codex.js +241 -0
  57. package/dist/lib/prompts/codex.js.map +1 -0
  58. package/dist/lib/prompts/opencode-codex.d.ts +21 -0
  59. package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
  60. package/dist/lib/prompts/opencode-codex.js +91 -0
  61. package/dist/lib/prompts/opencode-codex.js.map +1 -0
  62. package/dist/lib/request/fetch-helpers.d.ts +73 -0
  63. package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
  64. package/dist/lib/request/fetch-helpers.js +221 -0
  65. package/dist/lib/request/fetch-helpers.js.map +1 -0
  66. package/dist/lib/request/helpers/input-utils.d.ts +6 -0
  67. package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
  68. package/dist/lib/request/helpers/input-utils.js +174 -0
  69. package/dist/lib/request/helpers/input-utils.js.map +1 -0
  70. package/dist/lib/request/helpers/model-map.d.ts +28 -0
  71. package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
  72. package/dist/lib/request/helpers/model-map.js +109 -0
  73. package/dist/lib/request/helpers/model-map.js.map +1 -0
  74. package/dist/lib/request/request-transformer.d.ts +93 -0
  75. package/dist/lib/request/request-transformer.d.ts.map +1 -0
  76. package/dist/lib/request/request-transformer.js +403 -0
  77. package/dist/lib/request/request-transformer.js.map +1 -0
  78. package/dist/lib/request/response-handler.d.ts +14 -0
  79. package/dist/lib/request/response-handler.d.ts.map +1 -0
  80. package/dist/lib/request/response-handler.js +88 -0
  81. package/dist/lib/request/response-handler.js.map +1 -0
  82. package/dist/lib/types.d.ts +167 -0
  83. package/dist/lib/types.d.ts.map +1 -0
  84. package/dist/lib/types.js +2 -0
  85. package/dist/lib/types.js.map +1 -0
  86. package/package.json +73 -0
  87. package/scripts/install-opencode-codex-auth.js +430 -0
  88. package/scripts/test-all-models.sh +259 -0
  89. package/scripts/validate-model-map.sh +97 -0
@@ -0,0 +1,270 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join, dirname } from "node:path";
4
+ import { decodeJWT, refreshAccessToken } from "../auth/auth.js";
5
+ import { DEFAULT_MULTI_ACCOUNT_CONFIG } from "./types.js";
6
+ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
7
+ const ACCOUNTS_FILE = join(homedir(), ".config", "opencode", "openai-accounts.json");
8
+ const OPENCODE_AUTH_FILE = join(homedir(), ".local", "share", "opencode", "auth.json");
9
+ export class AccountManager {
10
+ accounts = [];
11
+ activeIndex = 0;
12
+ config;
13
+ constructor(config = {}) {
14
+ this.config = { ...DEFAULT_MULTI_ACCOUNT_CONFIG, ...config };
15
+ }
16
+ async loadFromDisk() {
17
+ if (!existsSync(ACCOUNTS_FILE))
18
+ return;
19
+ try {
20
+ const data = JSON.parse(readFileSync(ACCOUNTS_FILE, "utf-8"));
21
+ if (data.version === 1 && Array.isArray(data.accounts)) {
22
+ this.accounts = data.accounts;
23
+ this.activeIndex = data.activeAccountIndex || 0;
24
+ }
25
+ }
26
+ catch {
27
+ this.accounts = [];
28
+ }
29
+ }
30
+ async saveToDisk() {
31
+ const dir = dirname(ACCOUNTS_FILE);
32
+ if (!existsSync(dir)) {
33
+ mkdirSync(dir, { recursive: true });
34
+ }
35
+ const data = {
36
+ version: 1,
37
+ accounts: this.accounts,
38
+ activeAccountIndex: this.activeIndex,
39
+ };
40
+ writeFileSync(ACCOUNTS_FILE, JSON.stringify(data, null, 2), "utf-8");
41
+ }
42
+ async importFromOpenCodeAuth() {
43
+ if (!existsSync(OPENCODE_AUTH_FILE))
44
+ return;
45
+ try {
46
+ const authData = JSON.parse(readFileSync(OPENCODE_AUTH_FILE, "utf-8"));
47
+ const openaiAuth = authData?.openai;
48
+ if (openaiAuth?.type === "oauth" && openaiAuth?.refresh) {
49
+ const existingAccount = this.accounts.find((a) => a.parts.refreshToken === openaiAuth.refresh);
50
+ if (!existingAccount) {
51
+ await this.addAccount(undefined, openaiAuth.refresh, openaiAuth.access, openaiAuth.expires);
52
+ }
53
+ }
54
+ }
55
+ catch { }
56
+ }
57
+ async addAccount(email, refreshToken, accessToken, expires) {
58
+ let accountId;
59
+ let userId;
60
+ let extractedEmail = email;
61
+ let planType;
62
+ if (accessToken) {
63
+ const decoded = decodeJWT(accessToken);
64
+ if (decoded) {
65
+ const authClaims = decoded[JWT_CLAIM_PATH];
66
+ accountId = authClaims?.chatgpt_account_id;
67
+ userId = authClaims?.chatgpt_user_id;
68
+ planType = authClaims?.chatgpt_plan_type;
69
+ const profile = decoded["https://api.openai.com/profile"];
70
+ if (!extractedEmail && profile?.email) {
71
+ extractedEmail = profile.email;
72
+ }
73
+ }
74
+ }
75
+ // Deduplicate by userId + accountId (unique per user per workspace) or fallback to refreshToken
76
+ const existingIndex = this.accounts.findIndex((a) => {
77
+ if (userId && a.userId) {
78
+ return a.userId === userId && a.accountId === accountId;
79
+ }
80
+ return a.parts.refreshToken === refreshToken;
81
+ });
82
+ if (existingIndex >= 0) {
83
+ const existing = this.accounts[existingIndex];
84
+ if (accessToken)
85
+ existing.access = accessToken;
86
+ if (refreshToken)
87
+ existing.parts.refreshToken = refreshToken;
88
+ if (expires)
89
+ existing.expires = expires;
90
+ if (userId)
91
+ existing.userId = userId;
92
+ if (accountId)
93
+ existing.accountId = accountId;
94
+ if (planType)
95
+ existing.planType = planType;
96
+ if (extractedEmail)
97
+ existing.email = extractedEmail;
98
+ existing.consecutiveFailures = 0;
99
+ await this.saveToDisk();
100
+ if (!this.config.quietMode) {
101
+ console.log(`[openai-multi-auth] Updated account ${extractedEmail || existing.index}`);
102
+ }
103
+ return existing;
104
+ }
105
+ const account = {
106
+ index: this.accounts.length,
107
+ email: extractedEmail,
108
+ userId,
109
+ planType,
110
+ accountId,
111
+ addedAt: Date.now(),
112
+ parts: { refreshToken },
113
+ access: accessToken,
114
+ expires,
115
+ rateLimitResets: {},
116
+ consecutiveFailures: 0,
117
+ };
118
+ this.accounts.push(account);
119
+ await this.saveToDisk();
120
+ if (!this.config.quietMode) {
121
+ console.log(`[openai-multi-auth] Added account ${extractedEmail || account.index}`);
122
+ }
123
+ return account;
124
+ }
125
+ getAllAccounts() {
126
+ return this.accounts;
127
+ }
128
+ getAccountCount() {
129
+ return this.accounts.length;
130
+ }
131
+ async getNextAvailableAccount(model) {
132
+ if (this.accounts.length === 0)
133
+ return null;
134
+ const now = Date.now();
135
+ const startIndex = this.activeIndex;
136
+ let attempts = 0;
137
+ while (attempts < this.accounts.length) {
138
+ const index = (startIndex + attempts) % this.accounts.length;
139
+ const account = this.accounts[index];
140
+ if (this.isAccountAvailable(account, model, now)) {
141
+ this.activeIndex = index;
142
+ account.lastUsed = now;
143
+ return account;
144
+ }
145
+ attempts++;
146
+ }
147
+ return this.getLeastRateLimitedAccount(model);
148
+ }
149
+ isAccountAvailable(account, model, now) {
150
+ if (account.consecutiveFailures >= 3)
151
+ return false;
152
+ if (account.globalRateLimitReset && account.globalRateLimitReset > now) {
153
+ return false;
154
+ }
155
+ if (model && this.config.perModelRateLimits) {
156
+ const modelReset = account.rateLimitResets[model];
157
+ if (modelReset && modelReset > now) {
158
+ return false;
159
+ }
160
+ }
161
+ return true;
162
+ }
163
+ getLeastRateLimitedAccount(model) {
164
+ if (this.accounts.length === 0)
165
+ return null;
166
+ const now = Date.now();
167
+ let bestAccount = null;
168
+ let earliestReset = Infinity;
169
+ for (const account of this.accounts) {
170
+ if (account.consecutiveFailures >= 3)
171
+ continue;
172
+ let resetTime = account.globalRateLimitReset || 0;
173
+ if (model && this.config.perModelRateLimits) {
174
+ const modelReset = account.rateLimitResets[model] || 0;
175
+ resetTime = Math.max(resetTime, modelReset);
176
+ }
177
+ if (resetTime < earliestReset) {
178
+ earliestReset = resetTime;
179
+ bestAccount = account;
180
+ }
181
+ }
182
+ return bestAccount;
183
+ }
184
+ markRateLimited(account, retryAfterMs, model) {
185
+ const resetTime = Date.now() + retryAfterMs;
186
+ if (model && this.config.perModelRateLimits) {
187
+ account.rateLimitResets[model] = resetTime;
188
+ }
189
+ else {
190
+ account.globalRateLimitReset = resetTime;
191
+ }
192
+ if (this.config.debug) {
193
+ const identifier = account.email || `account-${account.index}`;
194
+ console.log(`[openai-multi-auth] ${identifier} rate limited until ${new Date(resetTime).toISOString()}`);
195
+ }
196
+ }
197
+ markRefreshFailed(account, error) {
198
+ account.consecutiveFailures++;
199
+ account.lastRefreshError = error;
200
+ account.isRefreshing = false;
201
+ if (this.config.removeOnInvalidGrant && error.includes("invalid_grant")) {
202
+ this.removeAccount(account);
203
+ }
204
+ }
205
+ removeAccount(account) {
206
+ const index = this.accounts.findIndex((a) => a.index === account.index);
207
+ if (index >= 0) {
208
+ this.accounts.splice(index, 1);
209
+ this.accounts.forEach((a, i) => (a.index = i));
210
+ if (this.activeIndex >= this.accounts.length) {
211
+ this.activeIndex = Math.max(0, this.accounts.length - 1);
212
+ }
213
+ this.saveToDisk();
214
+ if (!this.config.quietMode) {
215
+ console.log(`[openai-multi-auth] Removed account ${account.email || account.index}`);
216
+ }
217
+ }
218
+ }
219
+ async updateAccountTokens(account, accessToken, refreshToken, expires) {
220
+ account.access = accessToken;
221
+ account.parts.refreshToken = refreshToken;
222
+ account.expires = expires;
223
+ account.consecutiveFailures = 0;
224
+ account.isRefreshing = false;
225
+ account.lastRefreshError = undefined;
226
+ const decoded = decodeJWT(accessToken);
227
+ if (decoded) {
228
+ const authClaims = decoded[JWT_CLAIM_PATH];
229
+ if (authClaims?.chatgpt_account_id) {
230
+ account.accountId = authClaims.chatgpt_account_id;
231
+ }
232
+ if (authClaims?.chatgpt_user_id) {
233
+ account.userId = authClaims.chatgpt_user_id;
234
+ }
235
+ if (authClaims?.chatgpt_plan_type) {
236
+ account.planType = authClaims.chatgpt_plan_type;
237
+ }
238
+ }
239
+ await this.saveToDisk();
240
+ }
241
+ async ensureValidToken(account) {
242
+ if (!account.expires ||
243
+ account.expires > Date.now() + this.config.proactiveRefreshThresholdMs) {
244
+ return true;
245
+ }
246
+ if (account.isRefreshing) {
247
+ return !!account.access;
248
+ }
249
+ account.isRefreshing = true;
250
+ try {
251
+ const result = await refreshAccessToken(account.parts.refreshToken);
252
+ if (result.type === "success") {
253
+ await this.updateAccountTokens(account, result.access, result.refresh, result.expires);
254
+ return true;
255
+ }
256
+ this.markRefreshFailed(account, "Token refresh failed");
257
+ return false;
258
+ }
259
+ catch (err) {
260
+ this.markRefreshFailed(account, String(err));
261
+ return false;
262
+ }
263
+ }
264
+ getActiveAccount() {
265
+ if (this.accounts.length === 0)
266
+ return null;
267
+ return this.accounts[this.activeIndex] || this.accounts[0];
268
+ }
269
+ }
270
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../lib/accounts/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface ManagedAccount {
2
+ index: number;
3
+ email?: string;
4
+ userId?: string;
5
+ accountId?: string;
6
+ planType?: string;
7
+ addedAt: number;
8
+ lastUsed?: number;
9
+ parts: {
10
+ refreshToken: string;
11
+ };
12
+ access?: string;
13
+ expires?: number;
14
+ rateLimitResets: Record<string, number>;
15
+ globalRateLimitReset?: number;
16
+ consecutiveFailures: number;
17
+ isRefreshing?: boolean;
18
+ lastRefreshError?: string;
19
+ }
20
+ export interface AccountsStorage {
21
+ version: 1;
22
+ accounts: ManagedAccount[];
23
+ activeAccountIndex: number;
24
+ }
25
+ export interface MultiAccountConfig {
26
+ accountSelectionStrategy: "sticky" | "round-robin" | "hybrid";
27
+ debug: boolean;
28
+ quietMode: boolean;
29
+ pidOffsetEnabled: boolean;
30
+ proactiveRefreshThresholdMs: number;
31
+ removeOnInvalidGrant: boolean;
32
+ perModelRateLimits: boolean;
33
+ }
34
+ export declare const DEFAULT_MULTI_ACCOUNT_CONFIG: MultiAccountConfig;
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../lib/accounts/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,wBAAwB,EAAE,QAAQ,GAAG,aAAa,GAAG,QAAQ,CAAC;IAC9D,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,2BAA2B,EAAE,MAAM,CAAC;IACpC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,4BAA4B,EAAE,kBAQ1C,CAAC"}
@@ -0,0 +1,10 @@
1
+ export const DEFAULT_MULTI_ACCOUNT_CONFIG = {
2
+ accountSelectionStrategy: "sticky",
3
+ debug: false,
4
+ quietMode: false,
5
+ pidOffsetEnabled: false,
6
+ proactiveRefreshThresholdMs: 5 * 60 * 1000,
7
+ removeOnInvalidGrant: true,
8
+ perModelRateLimits: true,
9
+ };
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../lib/accounts/types.ts"],"names":[],"mappings":"AAoCA,MAAM,CAAC,MAAM,4BAA4B,GAAuB;IAC9D,wBAAwB,EAAE,QAAQ;IAClC,KAAK,EAAE,KAAK;IACZ,SAAS,EAAE,KAAK;IAChB,gBAAgB,EAAE,KAAK;IACvB,2BAA2B,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;IAC1C,oBAAoB,EAAE,IAAI;IAC1B,kBAAkB,EAAE,IAAI;CACzB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { AuthorizationFlow, TokenResult, ParsedAuthInput, JWTPayload } from "../types.js";
2
+ export declare const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
3
+ export declare const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
4
+ export declare const TOKEN_URL = "https://auth.openai.com/oauth/token";
5
+ export declare const REDIRECT_URI = "http://localhost:1455/auth/callback";
6
+ export declare const SCOPE = "openid profile email offline_access";
7
+ /**
8
+ * Generate a random state value for OAuth flow
9
+ * @returns Random hex string
10
+ */
11
+ export declare function createState(): string;
12
+ /**
13
+ * Parse authorization code and state from user input
14
+ * @param input - User input (URL, code#state, or just code)
15
+ * @returns Parsed authorization data
16
+ */
17
+ export declare function parseAuthorizationInput(input: string): ParsedAuthInput;
18
+ /**
19
+ * Exchange authorization code for access and refresh tokens
20
+ * @param code - Authorization code from OAuth flow
21
+ * @param verifier - PKCE verifier
22
+ * @param redirectUri - OAuth redirect URI
23
+ * @returns Token result
24
+ */
25
+ export declare function exchangeAuthorizationCode(code: string, verifier: string, redirectUri?: string): Promise<TokenResult>;
26
+ /**
27
+ * Decode a JWT token to extract payload
28
+ * @param token - JWT token to decode
29
+ * @returns Decoded payload or null if invalid
30
+ */
31
+ export declare function decodeJWT(token: string): JWTPayload | null;
32
+ /**
33
+ * Refresh access token using refresh token
34
+ * @param refreshToken - Refresh token
35
+ * @returns Token result
36
+ */
37
+ export declare function refreshAccessToken(refreshToken: string): Promise<TokenResult>;
38
+ /**
39
+ * Create OAuth authorization flow
40
+ * @returns Authorization flow details
41
+ */
42
+ export declare function createAuthorizationFlow(): Promise<AuthorizationFlow>;
43
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../lib/auth/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAY,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzG,eAAO,MAAM,SAAS,iCAAiC,CAAC;AACxD,eAAO,MAAM,aAAa,4CAA4C,CAAC;AACvE,eAAO,MAAM,SAAS,wCAAwC,CAAC;AAC/D,eAAO,MAAM,YAAY,wCAAwC,CAAC;AAClE,eAAO,MAAM,KAAK,wCAAwC,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAwBtE;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC9C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,GAAE,MAAqB,GAChC,OAAO,CAAC,WAAW,CAAC,CAoCtB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAU1D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAkDnF;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAiB1E"}
@@ -0,0 +1,163 @@
1
+ import { generatePKCE } from "@openauthjs/openauth/pkce";
2
+ import { randomBytes } from "node:crypto";
3
+ // OAuth constants (from openai/codex)
4
+ export const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
5
+ export const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
6
+ export const TOKEN_URL = "https://auth.openai.com/oauth/token";
7
+ export const REDIRECT_URI = "http://localhost:1455/auth/callback";
8
+ export const SCOPE = "openid profile email offline_access";
9
+ /**
10
+ * Generate a random state value for OAuth flow
11
+ * @returns Random hex string
12
+ */
13
+ export function createState() {
14
+ return randomBytes(16).toString("hex");
15
+ }
16
+ /**
17
+ * Parse authorization code and state from user input
18
+ * @param input - User input (URL, code#state, or just code)
19
+ * @returns Parsed authorization data
20
+ */
21
+ export function parseAuthorizationInput(input) {
22
+ const value = (input || "").trim();
23
+ if (!value)
24
+ return {};
25
+ try {
26
+ const url = new URL(value);
27
+ return {
28
+ code: url.searchParams.get("code") ?? undefined,
29
+ state: url.searchParams.get("state") ?? undefined,
30
+ };
31
+ }
32
+ catch { }
33
+ if (value.includes("#")) {
34
+ const [code, state] = value.split("#", 2);
35
+ return { code, state };
36
+ }
37
+ if (value.includes("code=")) {
38
+ const params = new URLSearchParams(value);
39
+ return {
40
+ code: params.get("code") ?? undefined,
41
+ state: params.get("state") ?? undefined,
42
+ };
43
+ }
44
+ return { code: value };
45
+ }
46
+ /**
47
+ * Exchange authorization code for access and refresh tokens
48
+ * @param code - Authorization code from OAuth flow
49
+ * @param verifier - PKCE verifier
50
+ * @param redirectUri - OAuth redirect URI
51
+ * @returns Token result
52
+ */
53
+ export async function exchangeAuthorizationCode(code, verifier, redirectUri = REDIRECT_URI) {
54
+ const res = await fetch(TOKEN_URL, {
55
+ method: "POST",
56
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
57
+ body: new URLSearchParams({
58
+ grant_type: "authorization_code",
59
+ client_id: CLIENT_ID,
60
+ code,
61
+ code_verifier: verifier,
62
+ redirect_uri: redirectUri,
63
+ }),
64
+ });
65
+ if (!res.ok) {
66
+ const text = await res.text().catch(() => "");
67
+ console.error("[openai-codex-plugin] code->token failed:", res.status, text);
68
+ return { type: "failed" };
69
+ }
70
+ const json = (await res.json());
71
+ if (!json?.access_token ||
72
+ !json?.refresh_token ||
73
+ typeof json?.expires_in !== "number") {
74
+ console.error("[openai-codex-plugin] token response missing fields:", json);
75
+ return { type: "failed" };
76
+ }
77
+ return {
78
+ type: "success",
79
+ access: json.access_token,
80
+ refresh: json.refresh_token,
81
+ expires: Date.now() + json.expires_in * 1000,
82
+ };
83
+ }
84
+ /**
85
+ * Decode a JWT token to extract payload
86
+ * @param token - JWT token to decode
87
+ * @returns Decoded payload or null if invalid
88
+ */
89
+ export function decodeJWT(token) {
90
+ try {
91
+ const parts = token.split(".");
92
+ if (parts.length !== 3)
93
+ return null;
94
+ const payload = parts[1];
95
+ const decoded = Buffer.from(payload, "base64").toString("utf-8");
96
+ return JSON.parse(decoded);
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ /**
103
+ * Refresh access token using refresh token
104
+ * @param refreshToken - Refresh token
105
+ * @returns Token result
106
+ */
107
+ export async function refreshAccessToken(refreshToken) {
108
+ try {
109
+ const response = await fetch(TOKEN_URL, {
110
+ method: "POST",
111
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
112
+ body: new URLSearchParams({
113
+ grant_type: "refresh_token",
114
+ refresh_token: refreshToken,
115
+ client_id: CLIENT_ID,
116
+ }),
117
+ });
118
+ if (!response.ok) {
119
+ const text = await response.text().catch(() => "");
120
+ console.error("[openai-codex-plugin] Token refresh failed:", response.status, text);
121
+ return { type: "failed" };
122
+ }
123
+ const json = (await response.json());
124
+ if (!json?.access_token ||
125
+ !json?.refresh_token ||
126
+ typeof json?.expires_in !== "number") {
127
+ console.error("[openai-codex-plugin] Token refresh response missing fields:", json);
128
+ return { type: "failed" };
129
+ }
130
+ return {
131
+ type: "success",
132
+ access: json.access_token,
133
+ refresh: json.refresh_token,
134
+ expires: Date.now() + json.expires_in * 1000,
135
+ };
136
+ }
137
+ catch (error) {
138
+ const err = error;
139
+ console.error("[openai-codex-plugin] Token refresh error:", err);
140
+ return { type: "failed" };
141
+ }
142
+ }
143
+ /**
144
+ * Create OAuth authorization flow
145
+ * @returns Authorization flow details
146
+ */
147
+ export async function createAuthorizationFlow() {
148
+ const pkce = (await generatePKCE());
149
+ const state = createState();
150
+ const url = new URL(AUTHORIZE_URL);
151
+ url.searchParams.set("response_type", "code");
152
+ url.searchParams.set("client_id", CLIENT_ID);
153
+ url.searchParams.set("redirect_uri", REDIRECT_URI);
154
+ url.searchParams.set("scope", SCOPE);
155
+ url.searchParams.set("code_challenge", pkce.challenge);
156
+ url.searchParams.set("code_challenge_method", "S256");
157
+ url.searchParams.set("state", state);
158
+ url.searchParams.set("id_token_add_organizations", "true");
159
+ url.searchParams.set("codex_cli_simplified_flow", "true");
160
+ url.searchParams.set("originator", "codex_cli_rs");
161
+ return { pkce, state, url: url.toString() };
162
+ }
163
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../lib/auth/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Browser utilities for OAuth flow
3
+ * Handles platform-specific browser opening
4
+ */
5
+ /**
6
+ * Gets the platform-specific command to open a URL in the default browser
7
+ * @returns Browser opener command for the current platform
8
+ */
9
+ export declare function getBrowserOpener(): string;
10
+ /**
11
+ * Opens a URL in the default browser
12
+ * Silently fails if browser cannot be opened (user can copy URL manually)
13
+ * @param url - URL to open
14
+ * @returns True if a browser launch was attempted
15
+ */
16
+ export declare function openBrowserUrl(url: string): boolean;
17
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../lib/auth/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAkCD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAgBnD"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Browser utilities for OAuth flow
3
+ * Handles platform-specific browser opening
4
+ */
5
+ import { spawn } from "node:child_process";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { PLATFORM_OPENERS } from "../constants.js";
9
+ /**
10
+ * Gets the platform-specific command to open a URL in the default browser
11
+ * @returns Browser opener command for the current platform
12
+ */
13
+ export function getBrowserOpener() {
14
+ const platform = process.platform;
15
+ if (platform === "darwin")
16
+ return PLATFORM_OPENERS.darwin;
17
+ if (platform === "win32")
18
+ return PLATFORM_OPENERS.win32;
19
+ return PLATFORM_OPENERS.linux;
20
+ }
21
+ function commandExists(command) {
22
+ if (!command)
23
+ return false;
24
+ // "start" is a shell builtin on Windows; rely on shell execution
25
+ if (process.platform === "win32" && command.toLowerCase() === "start") {
26
+ return true;
27
+ }
28
+ const pathValue = process.env.PATH || "";
29
+ const entries = pathValue.split(path.delimiter).filter(Boolean);
30
+ if (entries.length === 0)
31
+ return false;
32
+ if (process.platform === "win32") {
33
+ const pathext = (process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM")
34
+ .split(";")
35
+ .filter(Boolean);
36
+ for (const entry of entries) {
37
+ for (const ext of pathext) {
38
+ const candidate = path.join(entry, `${command}${ext}`);
39
+ if (fs.existsSync(candidate))
40
+ return true;
41
+ }
42
+ }
43
+ return false;
44
+ }
45
+ for (const entry of entries) {
46
+ const candidate = path.join(entry, command);
47
+ if (fs.existsSync(candidate))
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+ /**
53
+ * Opens a URL in the default browser
54
+ * Silently fails if browser cannot be opened (user can copy URL manually)
55
+ * @param url - URL to open
56
+ * @returns True if a browser launch was attempted
57
+ */
58
+ export function openBrowserUrl(url) {
59
+ try {
60
+ const opener = getBrowserOpener();
61
+ if (!commandExists(opener)) {
62
+ return false;
63
+ }
64
+ const child = spawn(opener, [url], {
65
+ stdio: "ignore",
66
+ shell: process.platform === "win32",
67
+ });
68
+ child.on("error", () => { });
69
+ return true;
70
+ }
71
+ catch (error) {
72
+ // Silently fail - user can manually open the URL from instructions
73
+ return false;
74
+ }
75
+ }
76
+ //# sourceMappingURL=browser.js.map