@oh-my-pi/pi-ai 3.20.0 → 3.34.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.
- package/README.md +69 -12
- package/package.json +3 -10
- package/src/cli.ts +89 -89
- package/src/index.ts +2 -2
- package/src/models.generated.ts +871 -151
- package/src/models.ts +11 -17
- package/src/providers/anthropic.ts +92 -28
- package/src/providers/google-gemini-cli.ts +268 -133
- package/src/providers/google-shared.ts +48 -5
- package/src/providers/google-vertex.ts +13 -3
- package/src/providers/google.ts +13 -3
- package/src/providers/openai-codex/index.ts +7 -0
- package/src/providers/openai-codex/prompts/codex.ts +26 -59
- package/src/providers/openai-codex/prompts/pi-codex-bridge.ts +38 -31
- package/src/providers/openai-codex/prompts/system-prompt.ts +26 -0
- package/src/providers/openai-codex/request-transformer.ts +38 -203
- package/src/providers/openai-codex-responses.ts +91 -24
- package/src/providers/openai-completions.ts +33 -26
- package/src/providers/openai-responses.ts +1 -1
- package/src/providers/transorm-messages.ts +4 -3
- package/src/stream.ts +34 -25
- package/src/types.ts +21 -4
- package/src/utils/oauth/github-copilot.ts +38 -3
- package/src/utils/oauth/google-antigravity.ts +146 -55
- package/src/utils/oauth/google-gemini-cli.ts +146 -55
- package/src/utils/oauth/index.ts +5 -5
- package/src/utils/oauth/openai-codex.ts +129 -54
- package/src/utils/overflow.ts +1 -1
- package/src/bun-imports.d.ts +0 -14
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* OpenAI Codex (ChatGPT OAuth) flow
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { randomBytes } from "node:crypto";
|
|
6
|
+
import http from "node:http";
|
|
5
7
|
import { generatePKCE } from "./pkce";
|
|
6
8
|
import type { OAuthCredentials, OAuthPrompt } from "./types";
|
|
7
9
|
|
|
@@ -36,9 +38,7 @@ type JwtPayload = {
|
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
function createState(): string {
|
|
39
|
-
|
|
40
|
-
crypto.getRandomValues(bytes);
|
|
41
|
-
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
41
|
+
return randomBytes(16).toString("hex");
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function parseAuthorizationInput(input: string): { code?: string; state?: string } {
|
|
@@ -187,64 +187,79 @@ async function createAuthorizationFlow(): Promise<{ verifier: string; state: str
|
|
|
187
187
|
|
|
188
188
|
type OAuthServerInfo = {
|
|
189
189
|
close: () => void;
|
|
190
|
+
cancelWait: () => void;
|
|
190
191
|
waitForCode: () => Promise<{ code: string } | null>;
|
|
191
192
|
};
|
|
192
193
|
|
|
193
194
|
function startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {
|
|
194
195
|
let lastCode: string | null = null;
|
|
196
|
+
let cancelled = false;
|
|
197
|
+
const server = http.createServer((req, res) => {
|
|
198
|
+
try {
|
|
199
|
+
const url = new URL(req.url || "", "http://localhost");
|
|
200
|
+
if (url.pathname !== "/auth/callback") {
|
|
201
|
+
res.statusCode = 404;
|
|
202
|
+
res.end("Not found");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (url.searchParams.get("state") !== state) {
|
|
206
|
+
res.statusCode = 400;
|
|
207
|
+
res.end("State mismatch");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const code = url.searchParams.get("code");
|
|
211
|
+
if (!code) {
|
|
212
|
+
res.statusCode = 400;
|
|
213
|
+
res.end("Missing authorization code");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
res.statusCode = 200;
|
|
217
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
218
|
+
res.end(SUCCESS_HTML);
|
|
219
|
+
lastCode = code;
|
|
220
|
+
} catch {
|
|
221
|
+
res.statusCode = 500;
|
|
222
|
+
res.end("Internal error");
|
|
223
|
+
}
|
|
224
|
+
});
|
|
195
225
|
|
|
196
226
|
return new Promise((resolve) => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
227
|
+
server
|
|
228
|
+
.listen(1455, "127.0.0.1", () => {
|
|
229
|
+
resolve({
|
|
230
|
+
close: () => server.close(),
|
|
231
|
+
cancelWait: () => {
|
|
232
|
+
cancelled = true;
|
|
233
|
+
},
|
|
234
|
+
waitForCode: async () => {
|
|
235
|
+
const sleep = () => new Promise((r) => setTimeout(r, 100));
|
|
236
|
+
for (let i = 0; i < 600; i += 1) {
|
|
237
|
+
if (lastCode) return { code: lastCode };
|
|
238
|
+
if (cancelled) return null;
|
|
239
|
+
await sleep();
|
|
209
240
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
241
|
+
return null;
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
})
|
|
245
|
+
.on("error", (err: NodeJS.ErrnoException) => {
|
|
246
|
+
console.error(
|
|
247
|
+
"[openai-codex] Failed to bind http://127.0.0.1:1455 (",
|
|
248
|
+
err.code,
|
|
249
|
+
") Falling back to manual paste.",
|
|
250
|
+
);
|
|
251
|
+
resolve({
|
|
252
|
+
close: () => {
|
|
253
|
+
try {
|
|
254
|
+
server.close();
|
|
255
|
+
} catch {
|
|
256
|
+
// ignore
|
|
213
257
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
219
|
-
} catch {
|
|
220
|
-
return new Response("Internal error", { status: 500 });
|
|
221
|
-
}
|
|
222
|
-
},
|
|
258
|
+
},
|
|
259
|
+
cancelWait: () => {},
|
|
260
|
+
waitForCode: async () => null,
|
|
261
|
+
});
|
|
223
262
|
});
|
|
224
|
-
|
|
225
|
-
resolve({
|
|
226
|
-
close: () => server.stop(),
|
|
227
|
-
waitForCode: async () => {
|
|
228
|
-
const sleep = () => new Promise((r) => setTimeout(r, 100));
|
|
229
|
-
for (let i = 0; i < 600; i += 1) {
|
|
230
|
-
if (lastCode) return { code: lastCode };
|
|
231
|
-
await sleep();
|
|
232
|
-
}
|
|
233
|
-
return null;
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
} catch (err) {
|
|
237
|
-
const code = (err as { code?: string }).code;
|
|
238
|
-
console.error(
|
|
239
|
-
"[openai-codex] Failed to bind http://127.0.0.1:1455 (",
|
|
240
|
-
code,
|
|
241
|
-
") Falling back to manual paste.",
|
|
242
|
-
);
|
|
243
|
-
resolve({
|
|
244
|
-
close: () => {},
|
|
245
|
-
waitForCode: async () => null,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
263
|
});
|
|
249
264
|
}
|
|
250
265
|
|
|
@@ -257,11 +272,19 @@ function getAccountId(accessToken: string): string | null {
|
|
|
257
272
|
|
|
258
273
|
/**
|
|
259
274
|
* Login with OpenAI Codex OAuth
|
|
275
|
+
*
|
|
276
|
+
* @param options.onAuth - Called with URL and instructions when auth starts
|
|
277
|
+
* @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)
|
|
278
|
+
* @param options.onProgress - Optional progress messages
|
|
279
|
+
* @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.
|
|
280
|
+
* Races with browser callback - whichever completes first wins.
|
|
281
|
+
* Useful for showing paste input immediately alongside browser flow.
|
|
260
282
|
*/
|
|
261
283
|
export async function loginOpenAICodex(options: {
|
|
262
284
|
onAuth: (info: { url: string; instructions?: string }) => void;
|
|
263
285
|
onPrompt: (prompt: OAuthPrompt) => Promise<string>;
|
|
264
286
|
onProgress?: (message: string) => void;
|
|
287
|
+
onManualCodeInput?: () => Promise<string>;
|
|
265
288
|
}): Promise<OAuthCredentials> {
|
|
266
289
|
const { verifier, state, url } = await createAuthorizationFlow();
|
|
267
290
|
const server = await startLocalOAuthServer(state);
|
|
@@ -270,11 +293,63 @@ export async function loginOpenAICodex(options: {
|
|
|
270
293
|
|
|
271
294
|
let code: string | undefined;
|
|
272
295
|
try {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
296
|
+
if (options.onManualCodeInput) {
|
|
297
|
+
// Race between browser callback and manual input
|
|
298
|
+
let manualCode: string | undefined;
|
|
299
|
+
let manualError: Error | undefined;
|
|
300
|
+
const manualPromise = options
|
|
301
|
+
.onManualCodeInput()
|
|
302
|
+
.then((input) => {
|
|
303
|
+
manualCode = input;
|
|
304
|
+
server.cancelWait();
|
|
305
|
+
})
|
|
306
|
+
.catch((err) => {
|
|
307
|
+
manualError = err instanceof Error ? err : new Error(String(err));
|
|
308
|
+
server.cancelWait();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const result = await server.waitForCode();
|
|
312
|
+
|
|
313
|
+
// If manual input was cancelled, throw that error
|
|
314
|
+
if (manualError) {
|
|
315
|
+
throw manualError;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (result?.code) {
|
|
319
|
+
// Browser callback won
|
|
320
|
+
code = result.code;
|
|
321
|
+
} else if (manualCode) {
|
|
322
|
+
// Manual input won (or callback timed out and user had entered code)
|
|
323
|
+
const parsed = parseAuthorizationInput(manualCode);
|
|
324
|
+
if (parsed.state && parsed.state !== state) {
|
|
325
|
+
throw new Error("State mismatch");
|
|
326
|
+
}
|
|
327
|
+
code = parsed.code;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// If still no code, wait for manual promise to complete and try that
|
|
331
|
+
if (!code) {
|
|
332
|
+
await manualPromise;
|
|
333
|
+
if (manualError) {
|
|
334
|
+
throw manualError;
|
|
335
|
+
}
|
|
336
|
+
if (manualCode) {
|
|
337
|
+
const parsed = parseAuthorizationInput(manualCode);
|
|
338
|
+
if (parsed.state && parsed.state !== state) {
|
|
339
|
+
throw new Error("State mismatch");
|
|
340
|
+
}
|
|
341
|
+
code = parsed.code;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
// Original flow: wait for callback, then prompt if needed
|
|
346
|
+
const result = await server.waitForCode();
|
|
347
|
+
if (result?.code) {
|
|
348
|
+
code = result.code;
|
|
349
|
+
}
|
|
276
350
|
}
|
|
277
351
|
|
|
352
|
+
// Fallback to onPrompt if still no code
|
|
278
353
|
if (!code) {
|
|
279
354
|
const input = await options.onPrompt({
|
|
280
355
|
message: "Paste the authorization code (or full redirect URL):",
|
package/src/utils/overflow.ts
CHANGED
|
@@ -32,7 +32,7 @@ const OVERFLOW_PATTERNS = [
|
|
|
32
32
|
/exceeds the limit of \d+/i, // GitHub Copilot
|
|
33
33
|
/exceeds the available context size/i, // llama.cpp server
|
|
34
34
|
/greater than the context length/i, // LM Studio
|
|
35
|
-
/context length exceeded/i, // Generic fallback
|
|
35
|
+
/context[_ ]length[_ ]exceeded/i, // Generic fallback
|
|
36
36
|
/too many tokens/i, // Generic fallback
|
|
37
37
|
/token limit exceeded/i, // Generic fallback
|
|
38
38
|
];
|
package/src/bun-imports.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type declarations for Bun's import attributes.
|
|
3
|
-
* These allow importing non-JS files as text at build time.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
declare module "*.md" {
|
|
7
|
-
const content: string;
|
|
8
|
-
export default content;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
declare module "*.txt" {
|
|
12
|
-
const content: string;
|
|
13
|
-
export default content;
|
|
14
|
-
}
|