@terminai/core 0.23.0 → 0.25.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 (95) hide show
  1. package/dist/src/audit/redaction.js +24 -0
  2. package/dist/src/audit/redaction.js.map +1 -1
  3. package/dist/src/audit/redaction.test.d.ts +7 -0
  4. package/dist/src/audit/redaction.test.js +45 -0
  5. package/dist/src/audit/redaction.test.js.map +1 -0
  6. package/dist/src/auth/providerRegistry.js +20 -0
  7. package/dist/src/auth/providerRegistry.js.map +1 -1
  8. package/dist/src/auth/wizardSettings.d.ts +5 -0
  9. package/dist/src/auth/wizardSettings.js +43 -0
  10. package/dist/src/auth/wizardSettings.js.map +1 -1
  11. package/dist/src/auth/wizardSettings.test.js +20 -0
  12. package/dist/src/auth/wizardSettings.test.js.map +1 -1
  13. package/dist/src/auth/wizardState.d.ts +1 -1
  14. package/dist/src/auth/wizardState.js +2 -0
  15. package/dist/src/auth/wizardState.js.map +1 -1
  16. package/dist/src/auth/wizardState.test.js +23 -0
  17. package/dist/src/auth/wizardState.test.js.map +1 -1
  18. package/dist/src/config/builder.js +23 -0
  19. package/dist/src/config/builder.js.map +1 -1
  20. package/dist/src/config/config.js +40 -1
  21. package/dist/src/config/config.js.map +1 -1
  22. package/dist/src/config/settings/schema.d.ts +50 -0
  23. package/dist/src/config/settings/schema.js +48 -0
  24. package/dist/src/config/settings/schema.js.map +1 -1
  25. package/dist/src/core/chatGptCodexContentGenerator.d.ts +35 -0
  26. package/dist/src/core/chatGptCodexContentGenerator.js +605 -0
  27. package/dist/src/core/chatGptCodexContentGenerator.js.map +1 -0
  28. package/dist/src/core/chatGptCodexContentGenerator.test.d.ts +7 -0
  29. package/dist/src/core/chatGptCodexContentGenerator.test.js +250 -0
  30. package/dist/src/core/chatGptCodexContentGenerator.test.js.map +1 -0
  31. package/dist/src/core/contentGenerator.d.ts +2 -1
  32. package/dist/src/core/contentGenerator.js +10 -0
  33. package/dist/src/core/contentGenerator.js.map +1 -1
  34. package/dist/src/core/contentGenerator.test.js +36 -0
  35. package/dist/src/core/contentGenerator.test.js.map +1 -1
  36. package/dist/src/core/loggingContentGenerator.d.ts +1 -0
  37. package/dist/src/core/loggingContentGenerator.js +30 -0
  38. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  39. package/dist/src/core/loggingContentGenerator.test.js +62 -0
  40. package/dist/src/core/loggingContentGenerator.test.js.map +1 -1
  41. package/dist/src/core/openaiContentGenerator.d.ts +16 -0
  42. package/dist/src/core/openaiContentGenerator.js +244 -52
  43. package/dist/src/core/openaiContentGenerator.js.map +1 -1
  44. package/dist/src/core/openaiContentGenerator.test.js +176 -7
  45. package/dist/src/core/openaiContentGenerator.test.js.map +1 -1
  46. package/dist/src/core/providerTypes.d.ts +17 -1
  47. package/dist/src/core/providerTypes.js +10 -0
  48. package/dist/src/core/providerTypes.js.map +1 -1
  49. package/dist/src/core/providerTypes.test.js +11 -0
  50. package/dist/src/core/providerTypes.test.js.map +1 -1
  51. package/dist/src/core/turn.js +1 -1
  52. package/dist/src/core/turn.js.map +1 -1
  53. package/dist/src/core/turn.test.js +1 -1
  54. package/dist/src/core/turn.test.js.map +1 -1
  55. package/dist/src/generated/git-commit.d.ts +2 -2
  56. package/dist/src/generated/git-commit.js +2 -2
  57. package/dist/src/index.d.ts +6 -0
  58. package/dist/src/index.js +7 -0
  59. package/dist/src/index.js.map +1 -1
  60. package/dist/src/openai_chatgpt/constants.d.ts +23 -0
  61. package/dist/src/openai_chatgpt/constants.js +35 -0
  62. package/dist/src/openai_chatgpt/constants.js.map +1 -0
  63. package/dist/src/openai_chatgpt/credentialStorage.d.ts +18 -0
  64. package/dist/src/openai_chatgpt/credentialStorage.js +116 -0
  65. package/dist/src/openai_chatgpt/credentialStorage.js.map +1 -0
  66. package/dist/src/openai_chatgpt/credentialStorage.test.d.ts +7 -0
  67. package/dist/src/openai_chatgpt/credentialStorage.test.js +73 -0
  68. package/dist/src/openai_chatgpt/credentialStorage.test.js.map +1 -0
  69. package/dist/src/openai_chatgpt/imports.d.ts +10 -0
  70. package/dist/src/openai_chatgpt/imports.js +164 -0
  71. package/dist/src/openai_chatgpt/imports.js.map +1 -0
  72. package/dist/src/openai_chatgpt/imports.test.d.ts +7 -0
  73. package/dist/src/openai_chatgpt/imports.test.js +82 -0
  74. package/dist/src/openai_chatgpt/imports.test.js.map +1 -0
  75. package/dist/src/openai_chatgpt/jwt.d.ts +7 -0
  76. package/dist/src/openai_chatgpt/jwt.js +31 -0
  77. package/dist/src/openai_chatgpt/jwt.js.map +1 -0
  78. package/dist/src/openai_chatgpt/jwt.test.d.ts +7 -0
  79. package/dist/src/openai_chatgpt/jwt.test.js +23 -0
  80. package/dist/src/openai_chatgpt/jwt.test.js.map +1 -0
  81. package/dist/src/openai_chatgpt/oauthClient.d.ts +52 -0
  82. package/dist/src/openai_chatgpt/oauthClient.js +196 -0
  83. package/dist/src/openai_chatgpt/oauthClient.js.map +1 -0
  84. package/dist/src/openai_chatgpt/oauthClient.test.d.ts +7 -0
  85. package/dist/src/openai_chatgpt/oauthClient.test.js +138 -0
  86. package/dist/src/openai_chatgpt/oauthClient.test.js.map +1 -0
  87. package/dist/src/openai_chatgpt/types.d.ts +34 -0
  88. package/dist/src/openai_chatgpt/types.js +8 -0
  89. package/dist/src/openai_chatgpt/types.js.map +1 -0
  90. package/dist/src/utils/errorReporting.js +1 -1
  91. package/dist/src/utils/errorReporting.js.map +1 -1
  92. package/dist/src/utils/errorReporting.test.js +1 -1
  93. package/dist/src/utils/errorReporting.test.js.map +1 -1
  94. package/dist/tsconfig.tsbuildinfo +1 -1
  95. package/package.json +1 -1
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import * as os from 'node:os';
8
+ import * as path from 'node:path';
9
+ import { promises as fs } from 'node:fs';
10
+ import { ChatGptOAuthClient } from './oauthClient.js';
11
+ const MAX_FILE_BYTES = 1024 * 1024;
12
+ const MAX_TOKEN_LENGTH = 10_000;
13
+ const MAX_ACCOUNT_ID_LENGTH = 200;
14
+ export async function tryImportFromCodexCli(client = new ChatGptOAuthClient()) {
15
+ const codexHome = resolveCodexHome();
16
+ const authPath = path.join(codexHome, 'auth.json');
17
+ const raw = await readJsonFile(authPath).catch((e) => {
18
+ if (isEnoent(e))
19
+ return null;
20
+ throw e;
21
+ });
22
+ if (!raw)
23
+ return null;
24
+ const parsed = parseCodexAuthJson(raw);
25
+ if (!parsed)
26
+ return null;
27
+ const accountId = client.deriveAccountId({
28
+ accountId: parsed.accountId,
29
+ idToken: parsed.idToken,
30
+ accessToken: parsed.accessToken,
31
+ });
32
+ return {
33
+ token: {
34
+ accessToken: parsed.accessToken,
35
+ refreshToken: parsed.refreshToken,
36
+ tokenType: 'Bearer',
37
+ idToken: parsed.idToken,
38
+ expiresAt: undefined,
39
+ scope: undefined,
40
+ },
41
+ accountId,
42
+ lastRefresh: parsed.lastRefresh,
43
+ };
44
+ }
45
+ export async function tryImportFromOpenCode(client = new ChatGptOAuthClient()) {
46
+ const authPath = path.join(os.homedir(), '.opencode', 'auth', 'openai.json');
47
+ const raw = await readJsonFile(authPath).catch((e) => {
48
+ if (isEnoent(e))
49
+ return null;
50
+ throw e;
51
+ });
52
+ if (!raw)
53
+ return null;
54
+ const parsed = parseOpenCodeAuthJson(raw);
55
+ if (!parsed)
56
+ return null;
57
+ const accountId = client.deriveAccountId({
58
+ idToken: parsed.idToken,
59
+ accessToken: parsed.accessToken,
60
+ });
61
+ return {
62
+ token: {
63
+ accessToken: parsed.accessToken,
64
+ refreshToken: parsed.refreshToken,
65
+ tokenType: 'Bearer',
66
+ idToken: parsed.idToken,
67
+ expiresAt: parsed.expiresAt,
68
+ scope: undefined,
69
+ },
70
+ accountId,
71
+ lastRefresh: Date.now(),
72
+ };
73
+ }
74
+ function resolveCodexHome() {
75
+ const fromEnv = process.env['CODEX_HOME'];
76
+ if (typeof fromEnv === 'string' && fromEnv.trim().length > 0) {
77
+ return fromEnv.trim();
78
+ }
79
+ return path.join(os.homedir(), '.codex');
80
+ }
81
+ async function readJsonFile(filePath) {
82
+ const stat = await fs.stat(filePath);
83
+ if (stat.size > MAX_FILE_BYTES) {
84
+ throw new Error(`Refusing to read oversized auth file: ${filePath}`);
85
+ }
86
+ const text = await fs.readFile(filePath, 'utf8');
87
+ return JSON.parse(text);
88
+ }
89
+ function parseCodexAuthJson(value) {
90
+ if (!isPlainObject(value))
91
+ return null;
92
+ const tokens = value['tokens'];
93
+ if (!isPlainObject(tokens))
94
+ return null;
95
+ const access_token = tokens['access_token'];
96
+ const refresh_token = tokens['refresh_token'];
97
+ const id_token = tokens['id_token'];
98
+ const account_id = tokens['account_id'];
99
+ const last_refresh = value['last_refresh'];
100
+ if (!isStringWithin(access_token, MAX_TOKEN_LENGTH))
101
+ return null;
102
+ if (!isStringWithin(refresh_token, MAX_TOKEN_LENGTH))
103
+ return null;
104
+ const result = {
105
+ accessToken: access_token.trim(),
106
+ refreshToken: refresh_token.trim(),
107
+ };
108
+ if (isStringWithin(id_token, MAX_TOKEN_LENGTH)) {
109
+ result.idToken = id_token.trim();
110
+ }
111
+ if (isStringWithin(account_id, MAX_ACCOUNT_ID_LENGTH)) {
112
+ result.accountId = account_id.trim();
113
+ }
114
+ if (typeof last_refresh === 'number' && Number.isFinite(last_refresh)) {
115
+ result.lastRefresh = last_refresh;
116
+ }
117
+ return result;
118
+ }
119
+ function parseOpenCodeAuthJson(value) {
120
+ if (!isPlainObject(value))
121
+ return null;
122
+ // Supported shapes:
123
+ // 1) { type: "oauth", access: "...", refresh: "...", expires?: number }
124
+ // 2) { access_token: "...", refresh_token: "...", id_token?: "...", expires_at?: number }
125
+ const type = value['type'];
126
+ if (type !== undefined && type !== 'oauth') {
127
+ return null;
128
+ }
129
+ const access = value['access'] ?? value['access_token'];
130
+ const refresh = value['refresh'] ?? value['refresh_token'];
131
+ const idToken = value['id_token'];
132
+ const expires = value['expires'] ?? value['expires_at'] ?? value['expiresAt'];
133
+ if (!isStringWithin(access, MAX_TOKEN_LENGTH))
134
+ return null;
135
+ if (!isStringWithin(refresh, MAX_TOKEN_LENGTH))
136
+ return null;
137
+ const parsed = {
138
+ accessToken: access.trim(),
139
+ refreshToken: refresh.trim(),
140
+ };
141
+ if (isStringWithin(idToken, MAX_TOKEN_LENGTH)) {
142
+ parsed.idToken = idToken.trim();
143
+ }
144
+ if (typeof expires === 'number' && Number.isFinite(expires)) {
145
+ // OpenCode may store seconds, ms, or epoch; we accept epoch ms only if it looks plausible.
146
+ parsed.expiresAt = expires > 10_000_000_000 ? expires : expires * 1000;
147
+ }
148
+ return parsed;
149
+ }
150
+ function isPlainObject(value) {
151
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
152
+ }
153
+ function isStringWithin(value, maxLen) {
154
+ return (typeof value === 'string' &&
155
+ value.trim().length > 0 &&
156
+ value.length <= maxLen);
157
+ }
158
+ function isEnoent(error) {
159
+ return (typeof error === 'object' &&
160
+ error !== null &&
161
+ 'code' in error &&
162
+ error.code === 'ENOENT');
163
+ }
164
+ //# sourceMappingURL=imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/imports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
8
+ import * as os from 'node:os';
9
+ import * as path from 'node:path';
10
+ import { promises as fs } from 'node:fs';
11
+ import { tryImportFromCodexCli, tryImportFromOpenCode } from './imports.js';
12
+ import { ChatGptOAuthClient } from './oauthClient.js';
13
+ import { CHATGPT_ACCOUNT_ID_CLAIM, OPENAI_AUTH_CLAIM } from './constants.js';
14
+ function jwt(payload) {
15
+ const header = Buffer.from(JSON.stringify({ alg: 'none' })).toString('base64url');
16
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
17
+ return `${header}.${body}.`;
18
+ }
19
+ describe('imports', () => {
20
+ const tmpRoot = path.join(os.tmpdir(), `terminai-chatgpt-oauth-${Date.now()}`);
21
+ beforeEach(async () => {
22
+ await fs.mkdir(tmpRoot, { recursive: true });
23
+ vi.unstubAllEnvs();
24
+ });
25
+ afterEach(async () => {
26
+ await fs.rm(tmpRoot, { recursive: true, force: true });
27
+ });
28
+ it('imports from CODEX_HOME/auth.json', async () => {
29
+ const codexHome = path.join(tmpRoot, 'codex');
30
+ await fs.mkdir(codexHome, { recursive: true });
31
+ await fs.writeFile(path.join(codexHome, 'auth.json'), JSON.stringify({
32
+ tokens: {
33
+ access_token: jwt({
34
+ [OPENAI_AUTH_CLAIM]: { chatgpt_account_id: 'acct_from_access' },
35
+ }),
36
+ refresh_token: 'refresh123',
37
+ id_token: jwt({
38
+ [OPENAI_AUTH_CLAIM]: { chatgpt_account_id: 'acct_from_id' },
39
+ }),
40
+ account_id: 'acct_stored',
41
+ },
42
+ last_refresh: 123456,
43
+ }), 'utf8');
44
+ vi.stubEnv('CODEX_HOME', codexHome);
45
+ const payload = await tryImportFromCodexCli(new ChatGptOAuthClient());
46
+ expect(payload?.token.accessToken).toBeTruthy();
47
+ expect(payload?.token.refreshToken).toBe('refresh123');
48
+ // Stored account_id wins
49
+ expect(payload?.accountId).toBe('acct_stored');
50
+ expect(payload?.lastRefresh).toBe(123456);
51
+ });
52
+ it('imports from ~/.opencode/auth/openai.json under HOME', async () => {
53
+ const home = path.join(tmpRoot, 'home');
54
+ await fs.mkdir(path.join(home, '.opencode', 'auth'), { recursive: true });
55
+ await fs.writeFile(path.join(home, '.opencode', 'auth', 'openai.json'), JSON.stringify({
56
+ type: 'oauth',
57
+ access: jwt({
58
+ [OPENAI_AUTH_CLAIM]: { chatgpt_account_id: 'acct_1' },
59
+ }),
60
+ refresh: 'refresh_1',
61
+ expires: 123,
62
+ }), 'utf8');
63
+ vi.stubEnv('HOME', home);
64
+ const payload = await tryImportFromOpenCode(new ChatGptOAuthClient());
65
+ expect(payload?.token.refreshToken).toBe('refresh_1');
66
+ expect(payload?.accountId).toBe('acct_1');
67
+ });
68
+ it('imports legacy flat claim for backwards compatibility', async () => {
69
+ const home = path.join(tmpRoot, 'home-legacy');
70
+ await fs.mkdir(path.join(home, '.opencode', 'auth'), { recursive: true });
71
+ await fs.writeFile(path.join(home, '.opencode', 'auth', 'openai.json'), JSON.stringify({
72
+ type: 'oauth',
73
+ access: jwt({ [CHATGPT_ACCOUNT_ID_CLAIM]: 'acct_legacy' }),
74
+ refresh: 'refresh_legacy',
75
+ }), 'utf8');
76
+ vi.stubEnv('HOME', home);
77
+ const payload = await tryImportFromOpenCode(new ChatGptOAuthClient());
78
+ expect(payload?.token.refreshToken).toBe('refresh_legacy');
79
+ expect(payload?.accountId).toBe('acct_legacy');
80
+ });
81
+ });
82
+ //# sourceMappingURL=imports.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.test.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/imports.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export declare function decodeJwtPayload(token: string): Record<string, unknown>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export function decodeJwtPayload(token) {
8
+ const parts = token.split('.');
9
+ if (parts.length < 2) {
10
+ throw new Error('Invalid JWT: missing payload');
11
+ }
12
+ const payloadB64Url = parts[1];
13
+ const json = Buffer.from(base64UrlToBase64(payloadB64Url), 'base64').toString('utf8');
14
+ const parsed = JSON.parse(json);
15
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
16
+ throw new Error('Invalid JWT: payload is not an object');
17
+ }
18
+ return parsed;
19
+ }
20
+ function base64UrlToBase64(value) {
21
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
22
+ const padding = normalized.length % 4;
23
+ if (padding === 0)
24
+ return normalized;
25
+ if (padding === 2)
26
+ return normalized + '==';
27
+ if (padding === 3)
28
+ return normalized + '=';
29
+ throw new Error('Invalid base64url string');
30
+ }
31
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/jwt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAC3E,MAAM,CACP,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IAC3C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IACtC,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC;IACrC,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,UAAU,GAAG,IAAI,CAAC;IAC5C,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,UAAU,GAAG,GAAG,CAAC;IAC3C,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ import { decodeJwtPayload } from './jwt.js';
9
+ function jwt(payload) {
10
+ const header = Buffer.from(JSON.stringify({ alg: 'none' })).toString('base64url');
11
+ const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
12
+ return `${header}.${body}.`;
13
+ }
14
+ describe('decodeJwtPayload', () => {
15
+ it('decodes base64url JSON payload', () => {
16
+ const token = jwt({ sub: 'user', foo: 'bar' });
17
+ expect(decodeJwtPayload(token)).toEqual({ sub: 'user', foo: 'bar' });
18
+ });
19
+ it('throws for invalid token', () => {
20
+ expect(() => decodeJwtPayload('not-a-jwt')).toThrow('Invalid JWT');
21
+ });
22
+ });
23
+ //# sourceMappingURL=jwt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.test.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/jwt.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,SAAS,GAAG,CAAC,OAAgC;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,CAClE,WAAW,CACZ,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxE,OAAO,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC;AAC9B,CAAC;AAED,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import type { ChatGptOAuthCredentialPayload } from './types.js';
8
+ export interface ChatGptOAuthClientOptions {
9
+ readonly clientId?: string;
10
+ readonly authorizeUrl?: string;
11
+ readonly tokenUrl?: string;
12
+ readonly refreshStaleMs?: number;
13
+ }
14
+ export interface ChatGptOAuthStart {
15
+ readonly authUrl: string;
16
+ readonly state: string;
17
+ readonly codeVerifier: string;
18
+ }
19
+ /**
20
+ * Error thrown when OpenAI returns refresh_token_reused.
21
+ * This indicates the stored credentials are permanently invalid.
22
+ */
23
+ export declare class RefreshTokenReusedError extends Error {
24
+ constructor(message: string);
25
+ }
26
+ export declare class ChatGptOAuthClient {
27
+ private readonly clientId;
28
+ private readonly authorizeUrl;
29
+ private readonly tokenUrl;
30
+ private readonly refreshStaleMs;
31
+ constructor(options?: ChatGptOAuthClientOptions);
32
+ startAuthorization(input: {
33
+ redirectUri: string;
34
+ scope?: string;
35
+ }): ChatGptOAuthStart;
36
+ exchangeAuthorizationCode(input: {
37
+ code: string;
38
+ redirectUri: string;
39
+ codeVerifier: string;
40
+ }): Promise<ChatGptOAuthCredentialPayload>;
41
+ refresh(input: {
42
+ refreshToken: string;
43
+ existingRefreshToken?: string;
44
+ }): Promise<ChatGptOAuthCredentialPayload>;
45
+ shouldRefreshByStaleness(lastRefresh: number | undefined): boolean;
46
+ deriveAccountId(input: {
47
+ accountId?: string;
48
+ idToken?: string;
49
+ accessToken?: string;
50
+ }): string | undefined;
51
+ private toCredentialPayloadFromTokenResponse;
52
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import * as crypto from 'node:crypto';
8
+ import { URL } from 'node:url';
9
+ import { CHATGPT_ACCOUNT_ID_CLAIM, OPENAI_AUTH_CLAIM, OPENAI_AUTH_CHATGPT_ACCOUNT_ID_FIELD, CODEX_ORIGINATOR, DEFAULT_OPENAI_OAUTH_AUTHORIZE_URL, DEFAULT_OPENAI_OAUTH_CLIENT_ID, DEFAULT_OPENAI_OAUTH_TOKEN_URL, DEFAULT_REFRESH_STALE_MS, } from './constants.js';
10
+ import { decodeJwtPayload } from './jwt.js';
11
+ /**
12
+ * Error thrown when OpenAI returns refresh_token_reused.
13
+ * This indicates the stored credentials are permanently invalid.
14
+ */
15
+ export class RefreshTokenReusedError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = 'RefreshTokenReusedError';
19
+ }
20
+ }
21
+ export class ChatGptOAuthClient {
22
+ clientId;
23
+ authorizeUrl;
24
+ tokenUrl;
25
+ refreshStaleMs;
26
+ constructor(options = {}) {
27
+ this.clientId =
28
+ options.clientId ??
29
+ process.env['TERMINAI_OPENAI_OAUTH_CLIENT_ID'] ??
30
+ DEFAULT_OPENAI_OAUTH_CLIENT_ID;
31
+ this.authorizeUrl =
32
+ options.authorizeUrl ?? DEFAULT_OPENAI_OAUTH_AUTHORIZE_URL;
33
+ this.tokenUrl = options.tokenUrl ?? DEFAULT_OPENAI_OAUTH_TOKEN_URL;
34
+ this.refreshStaleMs = options.refreshStaleMs ?? DEFAULT_REFRESH_STALE_MS;
35
+ }
36
+ startAuthorization(input) {
37
+ const { codeVerifier, codeChallenge } = generatePkceS256();
38
+ const state = crypto.randomBytes(16).toString('base64url');
39
+ const scope = input.scope ?? 'openid profile email offline_access';
40
+ const url = new URL(this.authorizeUrl);
41
+ url.searchParams.set('client_id', this.clientId);
42
+ url.searchParams.set('redirect_uri', input.redirectUri);
43
+ url.searchParams.set('response_type', 'code');
44
+ url.searchParams.set('scope', scope);
45
+ url.searchParams.set('code_challenge', codeChallenge);
46
+ url.searchParams.set('code_challenge_method', 'S256');
47
+ url.searchParams.set('state', state);
48
+ // Codex CLI parity flags (stability-first)
49
+ url.searchParams.set('id_token_add_organizations', 'true');
50
+ url.searchParams.set('codex_cli_simplified_flow', 'true');
51
+ url.searchParams.set('originator', CODEX_ORIGINATOR);
52
+ return { authUrl: url.toString(), state, codeVerifier };
53
+ }
54
+ async exchangeAuthorizationCode(input) {
55
+ const body = new URLSearchParams();
56
+ body.set('grant_type', 'authorization_code');
57
+ body.set('code', input.code);
58
+ body.set('redirect_uri', input.redirectUri);
59
+ body.set('client_id', this.clientId);
60
+ body.set('code_verifier', input.codeVerifier);
61
+ const response = await fetch(this.tokenUrl, {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
64
+ body: body.toString(),
65
+ });
66
+ if (!response.ok) {
67
+ const text = await response.text().catch(() => '');
68
+ throw new Error(`OpenAI OAuth token exchange failed (${response.status}): ${text}`);
69
+ }
70
+ const json = (await response.json());
71
+ return this.toCredentialPayloadFromTokenResponse(json);
72
+ }
73
+ async refresh(input) {
74
+ const response = await fetch(this.tokenUrl, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ body: JSON.stringify({
78
+ client_id: this.clientId,
79
+ grant_type: 'refresh_token',
80
+ refresh_token: input.refreshToken,
81
+ scope: 'openid profile email',
82
+ }),
83
+ });
84
+ if (!response.ok) {
85
+ const text = await response.text().catch(() => '');
86
+ // Detect refresh_token_reused error - this means stored creds are permanently invalid
87
+ if (response.status === 401 && text.includes('refresh_token_reused')) {
88
+ throw new RefreshTokenReusedError('ChatGPT OAuth session expired. Your refresh token was already used. Run /auth reset then /auth wizard to re-authenticate.');
89
+ }
90
+ throw new Error(`OpenAI OAuth refresh failed (${response.status}): ${text}`);
91
+ }
92
+ const json = (await response.json());
93
+ return this.toCredentialPayloadFromTokenResponse(json, {
94
+ existingRefreshToken: input.existingRefreshToken,
95
+ });
96
+ }
97
+ shouldRefreshByStaleness(lastRefresh) {
98
+ if (typeof lastRefresh !== 'number' || !Number.isFinite(lastRefresh)) {
99
+ return true;
100
+ }
101
+ return Date.now() - lastRefresh >= this.refreshStaleMs;
102
+ }
103
+ deriveAccountId(input) {
104
+ if (input.accountId && input.accountId.trim().length > 0) {
105
+ return input.accountId.trim();
106
+ }
107
+ const idToken = input.idToken?.trim();
108
+ if (idToken) {
109
+ const claim = tryGetChatGptAccountIdClaim(idToken);
110
+ if (claim)
111
+ return claim;
112
+ }
113
+ const accessToken = input.accessToken?.trim();
114
+ if (accessToken) {
115
+ const claim = tryGetChatGptAccountIdClaim(accessToken);
116
+ if (claim)
117
+ return claim;
118
+ }
119
+ return undefined;
120
+ }
121
+ toCredentialPayloadFromTokenResponse(tokenResponse, options) {
122
+ if (!tokenResponse.access_token ||
123
+ tokenResponse.access_token.length === 0) {
124
+ throw new Error('OpenAI OAuth token response missing access_token');
125
+ }
126
+ const refreshToken = tokenResponse.refresh_token && tokenResponse.refresh_token.length > 0
127
+ ? tokenResponse.refresh_token
128
+ : options?.existingRefreshToken;
129
+ if (!refreshToken || refreshToken.length === 0) {
130
+ throw new Error('OpenAI OAuth token response missing refresh_token');
131
+ }
132
+ const idToken = typeof tokenResponse.id_token === 'string' &&
133
+ tokenResponse.id_token.trim().length > 0
134
+ ? tokenResponse.id_token
135
+ : undefined;
136
+ const expiresAt = typeof tokenResponse.expires_in === 'number' &&
137
+ Number.isFinite(tokenResponse.expires_in) &&
138
+ tokenResponse.expires_in > 0
139
+ ? Date.now() + tokenResponse.expires_in * 1000
140
+ : undefined;
141
+ const token = {
142
+ accessToken: tokenResponse.access_token,
143
+ refreshToken,
144
+ tokenType: 'Bearer',
145
+ idToken,
146
+ expiresAt,
147
+ scope: tokenResponse.scope,
148
+ };
149
+ const accountId = this.deriveAccountId({
150
+ idToken,
151
+ accessToken: token.accessToken,
152
+ });
153
+ if (!accountId) {
154
+ throw new Error('ChatGPT OAuth account id is missing. Re-authenticate to obtain an id_token with chatgpt_account_id.');
155
+ }
156
+ return {
157
+ token,
158
+ accountId,
159
+ lastRefresh: Date.now(),
160
+ };
161
+ }
162
+ }
163
+ function generatePkceS256() {
164
+ const codeVerifier = crypto.randomBytes(32).toString('base64url');
165
+ const codeChallenge = crypto
166
+ .createHash('sha256')
167
+ .update(codeVerifier)
168
+ .digest('base64url');
169
+ return { codeVerifier, codeChallenge };
170
+ }
171
+ function tryGetChatGptAccountIdClaim(token) {
172
+ try {
173
+ const payload = decodeJwtPayload(token);
174
+ // Preferred: nested object at https://api.openai.com/auth
175
+ const authObj = payload[OPENAI_AUTH_CLAIM];
176
+ if (isPlainObject(authObj)) {
177
+ const accountId = authObj[OPENAI_AUTH_CHATGPT_ACCOUNT_ID_FIELD];
178
+ if (typeof accountId === 'string' && accountId.trim().length > 0) {
179
+ return accountId.trim();
180
+ }
181
+ }
182
+ // Fallback: legacy flat claim
183
+ const legacy = payload[CHATGPT_ACCOUNT_ID_CLAIM];
184
+ if (typeof legacy === 'string' && legacy.trim().length > 0) {
185
+ return legacy.trim();
186
+ }
187
+ return undefined;
188
+ }
189
+ catch {
190
+ return undefined;
191
+ }
192
+ }
193
+ function isPlainObject(value) {
194
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
195
+ }
196
+ //# sourceMappingURL=oauthClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthClient.js","sourceRoot":"","sources":["../../../src/openai_chatgpt/oauthClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * Portions Copyright 2025 TerminaI Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ export {};