popeye-cli 1.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 (209) hide show
  1. package/.env.example +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/dist/adapters/claude.d.ts +82 -0
  5. package/dist/adapters/claude.d.ts.map +1 -0
  6. package/dist/adapters/claude.js +230 -0
  7. package/dist/adapters/claude.js.map +1 -0
  8. package/dist/adapters/openai.d.ts +48 -0
  9. package/dist/adapters/openai.d.ts.map +1 -0
  10. package/dist/adapters/openai.js +257 -0
  11. package/dist/adapters/openai.js.map +1 -0
  12. package/dist/auth/claude.d.ts +44 -0
  13. package/dist/auth/claude.d.ts.map +1 -0
  14. package/dist/auth/claude.js +139 -0
  15. package/dist/auth/claude.js.map +1 -0
  16. package/dist/auth/index.d.ts +61 -0
  17. package/dist/auth/index.d.ts.map +1 -0
  18. package/dist/auth/index.js +141 -0
  19. package/dist/auth/index.js.map +1 -0
  20. package/dist/auth/keychain.d.ts +66 -0
  21. package/dist/auth/keychain.d.ts.map +1 -0
  22. package/dist/auth/keychain.js +125 -0
  23. package/dist/auth/keychain.js.map +1 -0
  24. package/dist/auth/openai-entry.d.ts +9 -0
  25. package/dist/auth/openai-entry.d.ts.map +1 -0
  26. package/dist/auth/openai-entry.js +410 -0
  27. package/dist/auth/openai-entry.js.map +1 -0
  28. package/dist/auth/openai.d.ts +71 -0
  29. package/dist/auth/openai.d.ts.map +1 -0
  30. package/dist/auth/openai.js +212 -0
  31. package/dist/auth/openai.js.map +1 -0
  32. package/dist/auth/server.d.ts +32 -0
  33. package/dist/auth/server.d.ts.map +1 -0
  34. package/dist/auth/server.js +213 -0
  35. package/dist/auth/server.js.map +1 -0
  36. package/dist/cli/commands/auth.d.ts +10 -0
  37. package/dist/cli/commands/auth.d.ts.map +1 -0
  38. package/dist/cli/commands/auth.js +162 -0
  39. package/dist/cli/commands/auth.js.map +1 -0
  40. package/dist/cli/commands/config.d.ts +10 -0
  41. package/dist/cli/commands/config.d.ts.map +1 -0
  42. package/dist/cli/commands/config.js +215 -0
  43. package/dist/cli/commands/config.js.map +1 -0
  44. package/dist/cli/commands/create.d.ts +10 -0
  45. package/dist/cli/commands/create.d.ts.map +1 -0
  46. package/dist/cli/commands/create.js +240 -0
  47. package/dist/cli/commands/create.js.map +1 -0
  48. package/dist/cli/commands/index.d.ts +10 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -0
  50. package/dist/cli/commands/index.js +10 -0
  51. package/dist/cli/commands/index.js.map +1 -0
  52. package/dist/cli/commands/resume.d.ts +18 -0
  53. package/dist/cli/commands/resume.d.ts.map +1 -0
  54. package/dist/cli/commands/resume.js +241 -0
  55. package/dist/cli/commands/resume.js.map +1 -0
  56. package/dist/cli/commands/status.d.ts +18 -0
  57. package/dist/cli/commands/status.d.ts.map +1 -0
  58. package/dist/cli/commands/status.js +154 -0
  59. package/dist/cli/commands/status.js.map +1 -0
  60. package/dist/cli/index.d.ts +17 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +71 -0
  63. package/dist/cli/index.js.map +1 -0
  64. package/dist/cli/interactive.d.ts +9 -0
  65. package/dist/cli/interactive.d.ts.map +1 -0
  66. package/dist/cli/interactive.js +330 -0
  67. package/dist/cli/interactive.js.map +1 -0
  68. package/dist/cli/output.d.ts +182 -0
  69. package/dist/cli/output.d.ts.map +1 -0
  70. package/dist/cli/output.js +355 -0
  71. package/dist/cli/output.js.map +1 -0
  72. package/dist/config/defaults.d.ts +57 -0
  73. package/dist/config/defaults.d.ts.map +1 -0
  74. package/dist/config/defaults.js +103 -0
  75. package/dist/config/defaults.js.map +1 -0
  76. package/dist/config/index.d.ts +138 -0
  77. package/dist/config/index.d.ts.map +1 -0
  78. package/dist/config/index.js +244 -0
  79. package/dist/config/index.js.map +1 -0
  80. package/dist/config/schema.d.ts +220 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +141 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/generators/index.d.ts +101 -0
  85. package/dist/generators/index.d.ts.map +1 -0
  86. package/dist/generators/index.js +200 -0
  87. package/dist/generators/index.js.map +1 -0
  88. package/dist/generators/python.d.ts +48 -0
  89. package/dist/generators/python.d.ts.map +1 -0
  90. package/dist/generators/python.js +262 -0
  91. package/dist/generators/python.js.map +1 -0
  92. package/dist/generators/templates/index.d.ts +6 -0
  93. package/dist/generators/templates/index.d.ts.map +1 -0
  94. package/dist/generators/templates/index.js +6 -0
  95. package/dist/generators/templates/index.js.map +1 -0
  96. package/dist/generators/templates/python.d.ts +53 -0
  97. package/dist/generators/templates/python.d.ts.map +1 -0
  98. package/dist/generators/templates/python.js +454 -0
  99. package/dist/generators/templates/python.js.map +1 -0
  100. package/dist/generators/templates/typescript.d.ts +53 -0
  101. package/dist/generators/templates/typescript.d.ts.map +1 -0
  102. package/dist/generators/templates/typescript.js +394 -0
  103. package/dist/generators/templates/typescript.js.map +1 -0
  104. package/dist/generators/typescript.d.ts +64 -0
  105. package/dist/generators/typescript.d.ts.map +1 -0
  106. package/dist/generators/typescript.js +271 -0
  107. package/dist/generators/typescript.js.map +1 -0
  108. package/dist/index.d.ts +7 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +12 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/state/index.d.ts +168 -0
  113. package/dist/state/index.d.ts.map +1 -0
  114. package/dist/state/index.js +338 -0
  115. package/dist/state/index.js.map +1 -0
  116. package/dist/state/persistence.d.ts +91 -0
  117. package/dist/state/persistence.d.ts.map +1 -0
  118. package/dist/state/persistence.js +201 -0
  119. package/dist/state/persistence.js.map +1 -0
  120. package/dist/types/cli.d.ts +132 -0
  121. package/dist/types/cli.d.ts.map +1 -0
  122. package/dist/types/cli.js +17 -0
  123. package/dist/types/cli.js.map +1 -0
  124. package/dist/types/consensus.d.ts +111 -0
  125. package/dist/types/consensus.d.ts.map +1 -0
  126. package/dist/types/consensus.js +29 -0
  127. package/dist/types/consensus.js.map +1 -0
  128. package/dist/types/index.d.ts +9 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +13 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/project.d.ts +73 -0
  133. package/dist/types/project.d.ts.map +1 -0
  134. package/dist/types/project.js +55 -0
  135. package/dist/types/project.js.map +1 -0
  136. package/dist/types/workflow.d.ts +236 -0
  137. package/dist/types/workflow.d.ts.map +1 -0
  138. package/dist/types/workflow.js +74 -0
  139. package/dist/types/workflow.js.map +1 -0
  140. package/dist/workflow/consensus.d.ts +89 -0
  141. package/dist/workflow/consensus.d.ts.map +1 -0
  142. package/dist/workflow/consensus.js +220 -0
  143. package/dist/workflow/consensus.js.map +1 -0
  144. package/dist/workflow/execution-mode.d.ts +82 -0
  145. package/dist/workflow/execution-mode.d.ts.map +1 -0
  146. package/dist/workflow/execution-mode.js +346 -0
  147. package/dist/workflow/execution-mode.js.map +1 -0
  148. package/dist/workflow/index.d.ts +110 -0
  149. package/dist/workflow/index.d.ts.map +1 -0
  150. package/dist/workflow/index.js +283 -0
  151. package/dist/workflow/index.js.map +1 -0
  152. package/dist/workflow/plan-mode.d.ts +83 -0
  153. package/dist/workflow/plan-mode.d.ts.map +1 -0
  154. package/dist/workflow/plan-mode.js +241 -0
  155. package/dist/workflow/plan-mode.js.map +1 -0
  156. package/dist/workflow/test-runner.d.ts +87 -0
  157. package/dist/workflow/test-runner.d.ts.map +1 -0
  158. package/dist/workflow/test-runner.js +273 -0
  159. package/dist/workflow/test-runner.js.map +1 -0
  160. package/eslint.config.js +25 -0
  161. package/package.json +66 -0
  162. package/src/adapters/claude.ts +298 -0
  163. package/src/adapters/openai.ts +300 -0
  164. package/src/auth/claude.ts +166 -0
  165. package/src/auth/index.ts +171 -0
  166. package/src/auth/keychain.ts +138 -0
  167. package/src/auth/openai-entry.ts +410 -0
  168. package/src/auth/openai.ts +260 -0
  169. package/src/auth/server.ts +252 -0
  170. package/src/cli/commands/auth.ts +194 -0
  171. package/src/cli/commands/config.ts +241 -0
  172. package/src/cli/commands/create.ts +308 -0
  173. package/src/cli/commands/index.ts +10 -0
  174. package/src/cli/commands/resume.ts +304 -0
  175. package/src/cli/commands/status.ts +189 -0
  176. package/src/cli/index.ts +90 -0
  177. package/src/cli/interactive.ts +418 -0
  178. package/src/cli/output.ts +410 -0
  179. package/src/config/defaults.ts +114 -0
  180. package/src/config/index.ts +315 -0
  181. package/src/config/schema.ts +164 -0
  182. package/src/generators/index.ts +251 -0
  183. package/src/generators/python.ts +318 -0
  184. package/src/generators/templates/index.ts +6 -0
  185. package/src/generators/templates/python.ts +465 -0
  186. package/src/generators/templates/typescript.ts +417 -0
  187. package/src/generators/typescript.ts +340 -0
  188. package/src/index.ts +13 -0
  189. package/src/state/index.ts +454 -0
  190. package/src/state/persistence.ts +230 -0
  191. package/src/types/cli.ts +146 -0
  192. package/src/types/consensus.ts +116 -0
  193. package/src/types/index.ts +64 -0
  194. package/src/types/project.ts +85 -0
  195. package/src/types/workflow.ts +149 -0
  196. package/src/workflow/consensus.ts +299 -0
  197. package/src/workflow/execution-mode.ts +517 -0
  198. package/src/workflow/index.ts +396 -0
  199. package/src/workflow/plan-mode.ts +356 -0
  200. package/src/workflow/test-runner.ts +345 -0
  201. package/tests/adapters/openai.test.ts +145 -0
  202. package/tests/config/config.test.ts +208 -0
  203. package/tests/generators/generators.test.ts +185 -0
  204. package/tests/types/consensus.test.ts +152 -0
  205. package/tests/types/project.test.ts +134 -0
  206. package/tests/workflow/consensus.test.ts +221 -0
  207. package/tests/workflow/test-runner.test.ts +214 -0
  208. package/tsconfig.json +25 -0
  209. package/vitest.config.ts +22 -0
@@ -0,0 +1,410 @@
1
+ /**
2
+ * OpenAI API key entry HTML page
3
+ * Served by the local auth server for browser-based token entry
4
+ */
5
+ /**
6
+ * Generate the HTML for the OpenAI token entry page
7
+ */
8
+ export function getOpenAIEntryHTML(_port) {
9
+ return `
10
+ <!DOCTYPE html>
11
+ <html lang="en">
12
+ <head>
13
+ <meta charset="UTF-8">
14
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
15
+ <title>OpenAI API Key - Popeye CLI</title>
16
+ <style>
17
+ * {
18
+ box-sizing: border-box;
19
+ margin: 0;
20
+ padding: 0;
21
+ }
22
+
23
+ body {
24
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
25
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
26
+ min-height: 100vh;
27
+ display: flex;
28
+ justify-content: center;
29
+ align-items: center;
30
+ padding: 20px;
31
+ }
32
+
33
+ .container {
34
+ background: rgba(255, 255, 255, 0.05);
35
+ backdrop-filter: blur(20px);
36
+ border-radius: 20px;
37
+ padding: 40px;
38
+ max-width: 500px;
39
+ width: 100%;
40
+ border: 1px solid rgba(255, 255, 255, 0.1);
41
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
42
+ }
43
+
44
+ .logo {
45
+ text-align: center;
46
+ margin-bottom: 30px;
47
+ }
48
+
49
+ .logo-icon {
50
+ font-size: 48px;
51
+ margin-bottom: 10px;
52
+ }
53
+
54
+ h1 {
55
+ color: #ffffff;
56
+ font-size: 24px;
57
+ font-weight: 600;
58
+ text-align: center;
59
+ margin-bottom: 10px;
60
+ }
61
+
62
+ .subtitle {
63
+ color: rgba(255, 255, 255, 0.7);
64
+ text-align: center;
65
+ margin-bottom: 30px;
66
+ font-size: 14px;
67
+ line-height: 1.5;
68
+ }
69
+
70
+ .instructions {
71
+ background: rgba(255, 255, 255, 0.05);
72
+ border-radius: 12px;
73
+ padding: 20px;
74
+ margin-bottom: 25px;
75
+ }
76
+
77
+ .instructions h3 {
78
+ color: #10b981;
79
+ font-size: 14px;
80
+ font-weight: 600;
81
+ margin-bottom: 15px;
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 8px;
85
+ }
86
+
87
+ .instructions ol {
88
+ color: rgba(255, 255, 255, 0.8);
89
+ font-size: 14px;
90
+ padding-left: 20px;
91
+ line-height: 1.8;
92
+ }
93
+
94
+ .instructions a {
95
+ color: #60a5fa;
96
+ text-decoration: none;
97
+ }
98
+
99
+ .instructions a:hover {
100
+ text-decoration: underline;
101
+ }
102
+
103
+ .input-group {
104
+ margin-bottom: 20px;
105
+ }
106
+
107
+ .input-group label {
108
+ display: block;
109
+ color: rgba(255, 255, 255, 0.9);
110
+ font-size: 14px;
111
+ font-weight: 500;
112
+ margin-bottom: 8px;
113
+ }
114
+
115
+ .input-wrapper {
116
+ position: relative;
117
+ }
118
+
119
+ input[type="password"],
120
+ input[type="text"] {
121
+ width: 100%;
122
+ padding: 14px 16px;
123
+ padding-right: 45px;
124
+ background: rgba(255, 255, 255, 0.08);
125
+ border: 1px solid rgba(255, 255, 255, 0.15);
126
+ border-radius: 10px;
127
+ color: #ffffff;
128
+ font-size: 14px;
129
+ font-family: 'SF Mono', Monaco, 'Courier New', monospace;
130
+ transition: all 0.2s ease;
131
+ }
132
+
133
+ input:focus {
134
+ outline: none;
135
+ border-color: #10b981;
136
+ background: rgba(255, 255, 255, 0.1);
137
+ }
138
+
139
+ input::placeholder {
140
+ color: rgba(255, 255, 255, 0.4);
141
+ }
142
+
143
+ .toggle-visibility {
144
+ position: absolute;
145
+ right: 12px;
146
+ top: 50%;
147
+ transform: translateY(-50%);
148
+ background: none;
149
+ border: none;
150
+ color: rgba(255, 255, 255, 0.5);
151
+ cursor: pointer;
152
+ padding: 5px;
153
+ font-size: 18px;
154
+ }
155
+
156
+ .toggle-visibility:hover {
157
+ color: rgba(255, 255, 255, 0.8);
158
+ }
159
+
160
+ .checkbox-group {
161
+ display: flex;
162
+ align-items: center;
163
+ gap: 10px;
164
+ margin-bottom: 25px;
165
+ }
166
+
167
+ .checkbox-group input[type="checkbox"] {
168
+ width: 18px;
169
+ height: 18px;
170
+ accent-color: #10b981;
171
+ }
172
+
173
+ .checkbox-group label {
174
+ color: rgba(255, 255, 255, 0.8);
175
+ font-size: 14px;
176
+ }
177
+
178
+ .button-group {
179
+ display: flex;
180
+ gap: 12px;
181
+ }
182
+
183
+ button {
184
+ flex: 1;
185
+ padding: 14px 24px;
186
+ border-radius: 10px;
187
+ font-size: 14px;
188
+ font-weight: 600;
189
+ cursor: pointer;
190
+ transition: all 0.2s ease;
191
+ border: none;
192
+ }
193
+
194
+ .btn-primary {
195
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
196
+ color: white;
197
+ }
198
+
199
+ .btn-primary:hover {
200
+ transform: translateY(-2px);
201
+ box-shadow: 0 10px 20px rgba(16, 185, 129, 0.3);
202
+ }
203
+
204
+ .btn-primary:disabled {
205
+ opacity: 0.5;
206
+ cursor: not-allowed;
207
+ transform: none;
208
+ box-shadow: none;
209
+ }
210
+
211
+ .btn-secondary {
212
+ background: rgba(255, 255, 255, 0.1);
213
+ color: rgba(255, 255, 255, 0.8);
214
+ border: 1px solid rgba(255, 255, 255, 0.15);
215
+ }
216
+
217
+ .btn-secondary:hover {
218
+ background: rgba(255, 255, 255, 0.15);
219
+ }
220
+
221
+ .btn-link {
222
+ background: none;
223
+ color: #60a5fa;
224
+ text-decoration: none;
225
+ padding: 0;
226
+ flex: none;
227
+ width: auto;
228
+ }
229
+
230
+ .btn-link:hover {
231
+ text-decoration: underline;
232
+ }
233
+
234
+ .error-message {
235
+ background: rgba(239, 68, 68, 0.1);
236
+ border: 1px solid rgba(239, 68, 68, 0.3);
237
+ border-radius: 10px;
238
+ padding: 12px 16px;
239
+ color: #fca5a5;
240
+ font-size: 14px;
241
+ margin-bottom: 20px;
242
+ display: none;
243
+ }
244
+
245
+ .success-message {
246
+ background: rgba(16, 185, 129, 0.1);
247
+ border: 1px solid rgba(16, 185, 129, 0.3);
248
+ border-radius: 10px;
249
+ padding: 12px 16px;
250
+ color: #6ee7b7;
251
+ font-size: 14px;
252
+ margin-bottom: 20px;
253
+ display: none;
254
+ }
255
+
256
+ .footer {
257
+ text-align: center;
258
+ margin-top: 25px;
259
+ padding-top: 20px;
260
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
261
+ }
262
+
263
+ .footer a {
264
+ color: #60a5fa;
265
+ text-decoration: none;
266
+ font-size: 13px;
267
+ }
268
+
269
+ .footer a:hover {
270
+ text-decoration: underline;
271
+ }
272
+ </style>
273
+ </head>
274
+ <body>
275
+ <div class="container">
276
+ <div class="logo">
277
+ <div class="logo-icon">&#127871;</div>
278
+ <h1>OpenAI API Key Required</h1>
279
+ </div>
280
+
281
+ <p class="subtitle">
282
+ Popeye CLI needs an OpenAI API key for consensus reviews.<br>
283
+ Your key will be stored securely in your system keychain.
284
+ </p>
285
+
286
+ <div class="instructions">
287
+ <h3>&#9989; How to get your API key:</h3>
288
+ <ol>
289
+ <li>Visit <a href="https://platform.openai.com/api-keys" target="_blank">platform.openai.com/api-keys</a></li>
290
+ <li>Click "Create new secret key"</li>
291
+ <li>Copy the key and paste it below</li>
292
+ </ol>
293
+ </div>
294
+
295
+ <div class="error-message" id="error"></div>
296
+ <div class="success-message" id="success"></div>
297
+
298
+ <form id="tokenForm" onsubmit="submitToken(event)">
299
+ <div class="input-group">
300
+ <label for="token">API Key</label>
301
+ <div class="input-wrapper">
302
+ <input
303
+ type="password"
304
+ id="token"
305
+ name="token"
306
+ placeholder="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
307
+ required
308
+ autocomplete="off"
309
+ />
310
+ <button type="button" class="toggle-visibility" onclick="toggleVisibility()">
311
+ &#128065;
312
+ </button>
313
+ </div>
314
+ </div>
315
+
316
+ <div class="checkbox-group">
317
+ <input type="checkbox" id="saveToKeychain" checked />
318
+ <label for="saveToKeychain">Save to system keychain (recommended)</label>
319
+ </div>
320
+
321
+ <div class="button-group">
322
+ <button type="button" class="btn-secondary" onclick="openOpenAI()">
323
+ Open OpenAI
324
+ </button>
325
+ <button type="button" class="btn-secondary" onclick="cancel()">
326
+ Cancel
327
+ </button>
328
+ <button type="submit" class="btn-primary" id="submitBtn">
329
+ Authenticate
330
+ </button>
331
+ </div>
332
+ </form>
333
+
334
+ <div class="footer">
335
+ <a href="https://platform.openai.com/docs/api-reference" target="_blank">
336
+ OpenAI API Documentation
337
+ </a>
338
+ </div>
339
+ </div>
340
+
341
+ <script>
342
+ const tokenInput = document.getElementById('token');
343
+ const errorDiv = document.getElementById('error');
344
+ const successDiv = document.getElementById('success');
345
+ const submitBtn = document.getElementById('submitBtn');
346
+
347
+ function toggleVisibility() {
348
+ const input = document.getElementById('token');
349
+ input.type = input.type === 'password' ? 'text' : 'password';
350
+ }
351
+
352
+ function openOpenAI() {
353
+ window.open('https://platform.openai.com/api-keys', '_blank');
354
+ }
355
+
356
+ function cancel() {
357
+ window.location.href = '/cancel';
358
+ }
359
+
360
+ function showError(message) {
361
+ errorDiv.textContent = message;
362
+ errorDiv.style.display = 'block';
363
+ successDiv.style.display = 'none';
364
+ }
365
+
366
+ function showSuccess(message) {
367
+ successDiv.textContent = message;
368
+ successDiv.style.display = 'block';
369
+ errorDiv.style.display = 'none';
370
+ }
371
+
372
+ function validateToken(token) {
373
+ if (!token) {
374
+ return 'Please enter your API key';
375
+ }
376
+ if (!token.startsWith('sk-')) {
377
+ return 'API key should start with "sk-"';
378
+ }
379
+ if (token.length < 20) {
380
+ return 'API key seems too short';
381
+ }
382
+ return null;
383
+ }
384
+
385
+ async function submitToken(event) {
386
+ event.preventDefault();
387
+
388
+ const token = tokenInput.value.trim();
389
+ const validationError = validateToken(token);
390
+
391
+ if (validationError) {
392
+ showError(validationError);
393
+ return;
394
+ }
395
+
396
+ submitBtn.disabled = true;
397
+ submitBtn.textContent = 'Validating...';
398
+
399
+ // Submit the token
400
+ window.location.href = '/submit?token=' + encodeURIComponent(token);
401
+ }
402
+
403
+ // Focus the input on load
404
+ tokenInput.focus();
405
+ </script>
406
+ </body>
407
+ </html>
408
+ `;
409
+ }
410
+ //# sourceMappingURL=openai-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-entry.js","sourceRoot":"","sources":["../../src/auth/openai-entry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+YR,CAAC;AACF,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * OpenAI API authentication module
3
+ * Handles API key validation and storage
4
+ */
5
+ import OpenAI from 'openai';
6
+ /**
7
+ * OpenAI authentication status
8
+ */
9
+ export interface OpenAIAuthStatus {
10
+ authenticated: boolean;
11
+ keyLastFour?: string;
12
+ modelAccess?: string[];
13
+ error?: string;
14
+ }
15
+ /**
16
+ * Validate an OpenAI API key by making a test API call
17
+ *
18
+ * @param apiKey - The API key to validate
19
+ * @returns True if the key is valid
20
+ */
21
+ export declare function validateOpenAIToken(apiKey: string): Promise<boolean>;
22
+ /**
23
+ * Get available models for an API key
24
+ *
25
+ * @param apiKey - The API key
26
+ * @returns List of available model IDs
27
+ */
28
+ export declare function getAvailableModels(apiKey: string): Promise<string[]>;
29
+ /**
30
+ * Check if OpenAI is already authenticated
31
+ * Checks keychain first, then environment variable
32
+ */
33
+ export declare function checkOpenAIAuth(): Promise<OpenAIAuthStatus>;
34
+ /**
35
+ * Launch the browser-based token entry popup
36
+ *
37
+ * @returns The entered API key or null if cancelled
38
+ */
39
+ export declare function launchTokenEntryPopup(): Promise<string | null>;
40
+ /**
41
+ * Authenticate with OpenAI API
42
+ *
43
+ * @returns True if authentication was successful
44
+ */
45
+ export declare function authenticateOpenAI(): Promise<boolean>;
46
+ /**
47
+ * Authenticate with a provided API key (for CLI --api-key option)
48
+ *
49
+ * @param apiKey - The API key to use
50
+ * @returns True if authentication was successful
51
+ */
52
+ export declare function authenticateOpenAIWithKey(apiKey: string): Promise<boolean>;
53
+ /**
54
+ * Logout from OpenAI API
55
+ * Removes stored credentials
56
+ */
57
+ export declare function logoutOpenAI(): Promise<void>;
58
+ /**
59
+ * Get the OpenAI API key for API calls
60
+ */
61
+ export declare function getOpenAIToken(): Promise<string | null>;
62
+ /**
63
+ * Ensure OpenAI is authenticated
64
+ * Prompts for authentication if not already authenticated
65
+ */
66
+ export declare function ensureOpenAIAuth(): Promise<boolean>;
67
+ /**
68
+ * Create an OpenAI client with the stored credentials
69
+ */
70
+ export declare function createOpenAIClient(): Promise<OpenAI | null>;
71
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/auth/openai.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAU5B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB1E;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB1E;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAgCjE;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BpE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqC3D;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAchF;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAOlD;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE7D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAQzD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQjE"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * OpenAI API authentication module
3
+ * Handles API key validation and storage
4
+ */
5
+ import OpenAI from 'openai';
6
+ import open from 'open';
7
+ import { getOpenAICredential, setOpenAICredential, deleteOpenAICredential, maskCredential, } from './keychain.js';
8
+ import { startAuthCallbackServer, findAvailablePort } from './server.js';
9
+ /**
10
+ * Validate an OpenAI API key by making a test API call
11
+ *
12
+ * @param apiKey - The API key to validate
13
+ * @returns True if the key is valid
14
+ */
15
+ export async function validateOpenAIToken(apiKey) {
16
+ try {
17
+ const client = new OpenAI({ apiKey });
18
+ // Test the key by listing models
19
+ await client.models.list();
20
+ return true;
21
+ }
22
+ catch (error) {
23
+ // 401 means invalid key, other errors might be rate limits etc
24
+ if (error instanceof OpenAI.AuthenticationError) {
25
+ return false;
26
+ }
27
+ // For other errors, assume the key might be valid
28
+ // (could be rate limiting, network issues, etc)
29
+ console.warn('Could not fully validate OpenAI key:', error);
30
+ return true;
31
+ }
32
+ }
33
+ /**
34
+ * Get available models for an API key
35
+ *
36
+ * @param apiKey - The API key
37
+ * @returns List of available model IDs
38
+ */
39
+ export async function getAvailableModels(apiKey) {
40
+ try {
41
+ const client = new OpenAI({ apiKey });
42
+ const models = await client.models.list();
43
+ // Filter for GPT and O1 models
44
+ return models.data
45
+ .filter((m) => m.id.includes('gpt-4') ||
46
+ m.id.includes('gpt-3.5') ||
47
+ m.id.startsWith('o1'))
48
+ .map((m) => m.id)
49
+ .sort();
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ }
55
+ /**
56
+ * Check if OpenAI is already authenticated
57
+ * Checks keychain first, then environment variable
58
+ */
59
+ export async function checkOpenAIAuth() {
60
+ try {
61
+ const apiKey = await getOpenAICredential();
62
+ if (!apiKey) {
63
+ return { authenticated: false };
64
+ }
65
+ // Validate the key
66
+ const isValid = await validateOpenAIToken(apiKey);
67
+ if (!isValid) {
68
+ return {
69
+ authenticated: false,
70
+ error: 'Stored API key is invalid',
71
+ };
72
+ }
73
+ // Get available models
74
+ const models = await getAvailableModels(apiKey);
75
+ return {
76
+ authenticated: true,
77
+ keyLastFour: maskCredential(apiKey),
78
+ modelAccess: models,
79
+ };
80
+ }
81
+ catch (error) {
82
+ return {
83
+ authenticated: false,
84
+ error: error instanceof Error ? error.message : 'Unknown error',
85
+ };
86
+ }
87
+ }
88
+ /**
89
+ * Launch the browser-based token entry popup
90
+ *
91
+ * @returns The entered API key or null if cancelled
92
+ */
93
+ export async function launchTokenEntryPopup() {
94
+ try {
95
+ const port = await findAvailablePort(3000, 3100);
96
+ console.log('Opening browser for API key entry...\n');
97
+ // Start the token entry server
98
+ const authPromise = startAuthCallbackServer({
99
+ port,
100
+ type: 'openai',
101
+ timeout: 300000, // 5 minutes
102
+ });
103
+ // Open the browser
104
+ await open(`http://127.0.0.1:${port}`);
105
+ // Wait for the token
106
+ const result = await authPromise;
107
+ if (result.success && result.token) {
108
+ return result.token;
109
+ }
110
+ return null;
111
+ }
112
+ catch (error) {
113
+ console.error(`Failed to launch token entry: ${error}`);
114
+ return null;
115
+ }
116
+ }
117
+ /**
118
+ * Authenticate with OpenAI API
119
+ *
120
+ * @returns True if authentication was successful
121
+ */
122
+ export async function authenticateOpenAI() {
123
+ // Check if already authenticated
124
+ const existingAuth = await checkOpenAIAuth();
125
+ if (existingAuth.authenticated) {
126
+ console.log('Already authenticated with OpenAI API');
127
+ return true;
128
+ }
129
+ console.log('OpenAI API key required.');
130
+ try {
131
+ // Launch the token entry popup
132
+ const token = await launchTokenEntryPopup();
133
+ if (!token) {
134
+ console.error('No API key provided');
135
+ return false;
136
+ }
137
+ // Validate the token
138
+ console.log('Validating API key...');
139
+ const isValid = await validateOpenAIToken(token);
140
+ if (!isValid) {
141
+ console.error('Invalid OpenAI API key');
142
+ return false;
143
+ }
144
+ // Store the token
145
+ await setOpenAICredential(token);
146
+ console.log('OpenAI API authenticated successfully!\n');
147
+ return true;
148
+ }
149
+ catch (error) {
150
+ console.error(`Authentication error: ${error instanceof Error ? error.message : error}`);
151
+ return false;
152
+ }
153
+ }
154
+ /**
155
+ * Authenticate with a provided API key (for CLI --api-key option)
156
+ *
157
+ * @param apiKey - The API key to use
158
+ * @returns True if authentication was successful
159
+ */
160
+ export async function authenticateOpenAIWithKey(apiKey) {
161
+ // Validate the token
162
+ const isValid = await validateOpenAIToken(apiKey);
163
+ if (!isValid) {
164
+ console.error('Invalid OpenAI API key');
165
+ return false;
166
+ }
167
+ // Store the token
168
+ await setOpenAICredential(apiKey);
169
+ console.log('OpenAI API authenticated successfully!\n');
170
+ return true;
171
+ }
172
+ /**
173
+ * Logout from OpenAI API
174
+ * Removes stored credentials
175
+ */
176
+ export async function logoutOpenAI() {
177
+ const deleted = await deleteOpenAICredential();
178
+ if (deleted) {
179
+ console.log('OpenAI API credentials removed.');
180
+ }
181
+ else {
182
+ console.log('No OpenAI API credentials found.');
183
+ }
184
+ }
185
+ /**
186
+ * Get the OpenAI API key for API calls
187
+ */
188
+ export async function getOpenAIToken() {
189
+ return getOpenAICredential();
190
+ }
191
+ /**
192
+ * Ensure OpenAI is authenticated
193
+ * Prompts for authentication if not already authenticated
194
+ */
195
+ export async function ensureOpenAIAuth() {
196
+ const status = await checkOpenAIAuth();
197
+ if (status.authenticated) {
198
+ return true;
199
+ }
200
+ return authenticateOpenAI();
201
+ }
202
+ /**
203
+ * Create an OpenAI client with the stored credentials
204
+ */
205
+ export async function createOpenAIClient() {
206
+ const apiKey = await getOpenAIToken();
207
+ if (!apiKey) {
208
+ return null;
209
+ }
210
+ return new OpenAI({ apiKey });
211
+ }
212
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/auth/openai.ts"],"names":[],"mappings":"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"}