@oh-my-pi/pi-ai 6.7.67 → 6.8.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 +31 -31
- package/package.json +2 -1
- package/src/cli.ts +114 -52
- package/src/providers/anthropic.ts +215 -166
- package/src/providers/google-gemini-cli.ts +4 -20
- package/src/providers/openai-codex/response-handler.ts +4 -43
- package/src/providers/openai-codex-responses.ts +2 -2
- package/src/storage.ts +185 -0
- package/src/utils/event-stream.ts +3 -3
- package/src/utils/oauth/anthropic.ts +70 -88
- package/src/utils/oauth/callback-server.ts +245 -0
- package/src/utils/oauth/cursor.ts +1 -5
- package/src/utils/oauth/github-copilot.ts +1 -23
- package/src/utils/oauth/google-antigravity.ts +73 -263
- package/src/utils/oauth/google-gemini-cli.ts +73 -281
- package/src/utils/oauth/oauth.html +199 -0
- package/src/utils/oauth/openai-codex.ts +131 -318
- package/src/utils/oauth/pkce.ts +1 -1
- package/src/utils/oauth/types.ts +8 -0
package/README.md
CHANGED
|
@@ -10,38 +10,38 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
|
|
10
10
|
- [Installation](#installation)
|
|
11
11
|
- [Quick Start](#quick-start)
|
|
12
12
|
- [Tools](#tools)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
- [Defining Tools](#defining-tools)
|
|
14
|
+
- [Handling Tool Calls](#handling-tool-calls)
|
|
15
|
+
- [Streaming Tool Calls with Partial JSON](#streaming-tool-calls-with-partial-json)
|
|
16
|
+
- [Validating Tool Arguments](#validating-tool-arguments)
|
|
17
|
+
- [Complete Event Reference](#complete-event-reference)
|
|
18
18
|
- [Image Input](#image-input)
|
|
19
19
|
- [Thinking/Reasoning](#thinkingreasoning)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
- [Unified Interface](#unified-interface-streamsimplecompletesimple)
|
|
21
|
+
- [Provider-Specific Options](#provider-specific-options-streamcomplete)
|
|
22
|
+
- [Streaming Thinking Content](#streaming-thinking-content)
|
|
23
23
|
- [Stop Reasons](#stop-reasons)
|
|
24
24
|
- [Error Handling](#error-handling)
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
- [Aborting Requests](#aborting-requests)
|
|
26
|
+
- [Continuing After Abort](#continuing-after-abort)
|
|
27
27
|
- [APIs, Models, and Providers](#apis-models-and-providers)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
- [Providers and Models](#providers-and-models)
|
|
29
|
+
- [Querying Providers and Models](#querying-providers-and-models)
|
|
30
|
+
- [Custom Models](#custom-models)
|
|
31
|
+
- [OpenAI Compatibility Settings](#openai-compatibility-settings)
|
|
32
|
+
- [Type Safety](#type-safety)
|
|
33
33
|
- [Cross-Provider Handoffs](#cross-provider-handoffs)
|
|
34
34
|
- [Context Serialization](#context-serialization)
|
|
35
35
|
- [Browser Usage](#browser-usage)
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
- [Environment Variables](#environment-variables-nodejs-only)
|
|
37
|
+
- [Checking Environment Variables](#checking-environment-variables)
|
|
38
38
|
- [OAuth Providers](#oauth-providers)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
- [Vertex AI (ADC)](#vertex-ai-adc)
|
|
40
|
+
- [CLI Login](#cli-login)
|
|
41
|
+
- [Programmatic OAuth](#programmatic-oauth)
|
|
42
|
+
- [Login Flow Example](#login-flow-example)
|
|
43
|
+
- [Using OAuth Tokens](#using-oauth-tokens)
|
|
44
|
+
- [Provider Notes](#provider-notes)
|
|
45
45
|
- [License](#license)
|
|
46
46
|
|
|
47
47
|
## Supported Providers
|
|
@@ -156,7 +156,7 @@ for (const call of toolCalls) {
|
|
|
156
156
|
timeZone: call.arguments.timezone || "UTC",
|
|
157
157
|
dateStyle: "full",
|
|
158
158
|
timeStyle: "long",
|
|
159
|
-
|
|
159
|
+
})
|
|
160
160
|
: "Unknown tool";
|
|
161
161
|
|
|
162
162
|
// Add tool result to context (supports text and images)
|
|
@@ -443,7 +443,7 @@ const response = await completeSimple(
|
|
|
443
443
|
},
|
|
444
444
|
{
|
|
445
445
|
reasoning: "medium", // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' (xhigh maps to high on non-OpenAI providers)
|
|
446
|
-
}
|
|
446
|
+
},
|
|
447
447
|
);
|
|
448
448
|
|
|
449
449
|
// Access thinking and text blocks
|
|
@@ -563,7 +563,7 @@ const s = stream(
|
|
|
563
563
|
},
|
|
564
564
|
{
|
|
565
565
|
signal: controller.signal,
|
|
566
|
-
}
|
|
566
|
+
},
|
|
567
567
|
);
|
|
568
568
|
|
|
569
569
|
for await (const event of s) {
|
|
@@ -856,7 +856,7 @@ const response = await complete(
|
|
|
856
856
|
},
|
|
857
857
|
{
|
|
858
858
|
apiKey: "your-api-key",
|
|
859
|
-
}
|
|
859
|
+
},
|
|
860
860
|
);
|
|
861
861
|
```
|
|
862
862
|
|
|
@@ -957,9 +957,9 @@ Official docs: [Application Default Credentials](https://cloud.google.com/docs/a
|
|
|
957
957
|
The quickest way to authenticate:
|
|
958
958
|
|
|
959
959
|
```bash
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
960
|
+
bunx @oh-my-pi/pi-ai login # interactive provider selection
|
|
961
|
+
bunx @oh-my-pi/pi-ai login anthropic # login to specific provider
|
|
962
|
+
bunx @oh-my-pi/pi-ai list # list available providers
|
|
963
963
|
```
|
|
964
964
|
|
|
965
965
|
Credentials are saved to `auth.json` in the current directory.
|
|
@@ -1035,7 +1035,7 @@ const response = await complete(
|
|
|
1035
1035
|
{
|
|
1036
1036
|
messages: [{ role: "user", content: "Hello!" }],
|
|
1037
1037
|
},
|
|
1038
|
-
{ apiKey: result.apiKey }
|
|
1038
|
+
{ apiKey: result.apiKey },
|
|
1039
1039
|
);
|
|
1040
1040
|
```
|
|
1041
1041
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-ai",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.0",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"test": "bun test"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@oh-my-pi/pi-utils": "6.8.0",
|
|
20
21
|
"@anthropic-ai/sdk": "0.71.2",
|
|
21
22
|
"@aws-sdk/client-bedrock-runtime": "^3.968.0",
|
|
22
23
|
"@bufbuild/protobuf": "^2.10.2",
|
package/src/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import "./utils/migrate-env";
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
2
|
import { createInterface } from "readline";
|
|
3
|
+
import { CliAuthStorage } from "./storage";
|
|
4
|
+
import "./utils/migrate-env";
|
|
5
5
|
import { loginAnthropic } from "./utils/oauth/anthropic";
|
|
6
6
|
import { loginGitHubCopilot } from "./utils/oauth/github-copilot";
|
|
7
7
|
import { loginAntigravity } from "./utils/oauth/google-antigravity";
|
|
@@ -10,92 +10,81 @@ import { getOAuthProviders } from "./utils/oauth/index";
|
|
|
10
10
|
import { loginOpenAICodex } from "./utils/oauth/openai-codex";
|
|
11
11
|
import type { OAuthCredentials, OAuthProvider } from "./utils/oauth/types";
|
|
12
12
|
|
|
13
|
-
const AUTH_FILE = "auth.json";
|
|
14
13
|
const PROVIDERS = getOAuthProviders();
|
|
15
14
|
|
|
16
15
|
function prompt(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
function loadAuth(): Record<string, { type: "oauth" } & OAuthCredentials> {
|
|
21
|
-
if (!existsSync(AUTH_FILE)) return {};
|
|
22
|
-
try {
|
|
23
|
-
return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
24
|
-
} catch {
|
|
25
|
-
return {};
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function saveAuth(auth: Record<string, { type: "oauth" } & OAuthCredentials>): void {
|
|
30
|
-
writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), "utf-8");
|
|
16
|
+
const { promise, resolve } = Promise.withResolvers<string>();
|
|
17
|
+
rl.question(question, resolve);
|
|
18
|
+
return promise;
|
|
31
19
|
}
|
|
32
20
|
|
|
33
21
|
async function login(provider: OAuthProvider): Promise<void> {
|
|
34
22
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
35
23
|
|
|
36
24
|
const promptFn = (msg: string) => prompt(rl, `${msg} `);
|
|
25
|
+
const storage = new CliAuthStorage();
|
|
37
26
|
|
|
38
27
|
try {
|
|
39
28
|
let credentials: OAuthCredentials;
|
|
40
29
|
|
|
41
30
|
switch (provider) {
|
|
42
31
|
case "anthropic":
|
|
43
|
-
credentials = await loginAnthropic(
|
|
44
|
-
(
|
|
32
|
+
credentials = await loginAnthropic({
|
|
33
|
+
onAuth(info) {
|
|
34
|
+
const { url } = info;
|
|
45
35
|
console.log(`\nOpen this URL in your browser:\n${url}\n`);
|
|
46
36
|
},
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
onProgress(message) {
|
|
38
|
+
console.log(message);
|
|
49
39
|
},
|
|
50
|
-
);
|
|
40
|
+
});
|
|
51
41
|
break;
|
|
52
42
|
|
|
53
43
|
case "github-copilot":
|
|
54
44
|
credentials = await loginGitHubCopilot({
|
|
55
|
-
onAuth
|
|
45
|
+
onAuth(url, instructions) {
|
|
56
46
|
console.log(`\nOpen this URL in your browser:\n${url}`);
|
|
57
47
|
if (instructions) console.log(instructions);
|
|
58
48
|
console.log();
|
|
59
49
|
},
|
|
60
|
-
|
|
50
|
+
async onPrompt(p) {
|
|
61
51
|
return await promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
|
|
62
52
|
},
|
|
63
|
-
onProgress: (msg) => console.log(msg),
|
|
64
53
|
});
|
|
65
54
|
break;
|
|
66
55
|
|
|
67
56
|
case "google-gemini-cli":
|
|
68
|
-
credentials = await loginGeminiCli(
|
|
69
|
-
(info)
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
credentials = await loginGeminiCli({
|
|
58
|
+
onAuth(info) {
|
|
59
|
+
const { url, instructions } = info;
|
|
60
|
+
console.log(`\nOpen this URL in your browser:\n${url}`);
|
|
61
|
+
if (instructions) console.log(instructions);
|
|
72
62
|
console.log();
|
|
73
63
|
},
|
|
74
|
-
|
|
75
|
-
);
|
|
64
|
+
});
|
|
76
65
|
break;
|
|
77
66
|
|
|
78
67
|
case "google-antigravity":
|
|
79
|
-
credentials = await loginAntigravity(
|
|
80
|
-
(info)
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
credentials = await loginAntigravity({
|
|
69
|
+
onAuth(info) {
|
|
70
|
+
const { url, instructions } = info;
|
|
71
|
+
console.log(`\nOpen this URL in your browser:\n${url}`);
|
|
72
|
+
if (instructions) console.log(instructions);
|
|
83
73
|
console.log();
|
|
84
74
|
},
|
|
85
|
-
|
|
86
|
-
);
|
|
75
|
+
});
|
|
87
76
|
break;
|
|
88
77
|
case "openai-codex":
|
|
89
78
|
credentials = await loginOpenAICodex({
|
|
90
|
-
onAuth
|
|
91
|
-
|
|
92
|
-
|
|
79
|
+
onAuth(info) {
|
|
80
|
+
const { url, instructions } = info;
|
|
81
|
+
console.log(`\nOpen this URL in your browser:\n${url}`);
|
|
82
|
+
if (instructions) console.log(instructions);
|
|
93
83
|
console.log();
|
|
94
84
|
},
|
|
95
|
-
|
|
85
|
+
async onPrompt(p) {
|
|
96
86
|
return await promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
|
|
97
87
|
},
|
|
98
|
-
onProgress: (msg) => console.log(msg),
|
|
99
88
|
});
|
|
100
89
|
break;
|
|
101
90
|
|
|
@@ -103,12 +92,11 @@ async function login(provider: OAuthProvider): Promise<void> {
|
|
|
103
92
|
throw new Error(`Unknown provider: ${provider}`);
|
|
104
93
|
}
|
|
105
94
|
|
|
106
|
-
|
|
107
|
-
auth[provider] = { type: "oauth", ...credentials };
|
|
108
|
-
saveAuth(auth);
|
|
95
|
+
storage.saveOAuth(provider, credentials);
|
|
109
96
|
|
|
110
|
-
console.log(`\nCredentials saved to
|
|
97
|
+
console.log(`\nCredentials saved to ~/.omp/agent/agent.db`);
|
|
111
98
|
} finally {
|
|
99
|
+
storage.close();
|
|
112
100
|
rl.close();
|
|
113
101
|
}
|
|
114
102
|
}
|
|
@@ -118,10 +106,12 @@ async function main(): Promise<void> {
|
|
|
118
106
|
const command = args[0];
|
|
119
107
|
|
|
120
108
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
121
|
-
console.log(`Usage:
|
|
109
|
+
console.log(`Usage: bunx @oh-my-pi/pi-ai <command> [provider]
|
|
122
110
|
|
|
123
111
|
Commands:
|
|
124
112
|
login [provider] Login to an OAuth provider
|
|
113
|
+
logout [provider] Logout from an OAuth provider
|
|
114
|
+
status Show logged-in providers
|
|
125
115
|
list List available providers
|
|
126
116
|
|
|
127
117
|
Providers:
|
|
@@ -130,15 +120,43 @@ Providers:
|
|
|
130
120
|
google-gemini-cli Google Gemini CLI
|
|
131
121
|
google-antigravity Antigravity (Gemini 3, Claude, GPT-OSS)
|
|
132
122
|
openai-codex OpenAI Codex (ChatGPT Plus/Pro)
|
|
123
|
+
cursor Cursor (Claude, GPT, etc.)
|
|
133
124
|
|
|
134
125
|
Examples:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
bunx @oh-my-pi/pi-ai login # interactive provider selection
|
|
127
|
+
bunx @oh-my-pi/pi-ai login anthropic # login to specific provider
|
|
128
|
+
bunx @oh-my-pi/pi-ai logout anthropic # logout from specific provider
|
|
129
|
+
bunx @oh-my-pi/pi-ai status # show logged-in providers
|
|
130
|
+
bunx @oh-my-pi/pi-ai list # list providers
|
|
138
131
|
`);
|
|
139
132
|
return;
|
|
140
133
|
}
|
|
141
134
|
|
|
135
|
+
if (command === "status") {
|
|
136
|
+
const storage = new CliAuthStorage();
|
|
137
|
+
try {
|
|
138
|
+
const providers = storage.listProviders();
|
|
139
|
+
if (providers.length === 0) {
|
|
140
|
+
console.log("No OAuth credentials stored.");
|
|
141
|
+
console.log(`Use 'bunx @oh-my-pi/pi-ai login' to authenticate.`);
|
|
142
|
+
} else {
|
|
143
|
+
console.log("Logged-in providers:\n");
|
|
144
|
+
for (const provider of providers) {
|
|
145
|
+
const oauth = storage.getOAuth(provider);
|
|
146
|
+
if (oauth) {
|
|
147
|
+
const expires = new Date(oauth.expires);
|
|
148
|
+
const expired = Date.now() >= oauth.expires;
|
|
149
|
+
const status = expired ? "(expired)" : `(expires ${expires.toLocaleString()})`;
|
|
150
|
+
console.log(` ${provider.padEnd(20)} ${status}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} finally {
|
|
155
|
+
storage.close();
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
142
160
|
if (command === "list") {
|
|
143
161
|
console.log("Available OAuth providers:\n");
|
|
144
162
|
for (const p of PROVIDERS) {
|
|
@@ -147,6 +165,50 @@ Examples:
|
|
|
147
165
|
return;
|
|
148
166
|
}
|
|
149
167
|
|
|
168
|
+
if (command === "logout") {
|
|
169
|
+
let provider = args[1] as OAuthProvider | undefined;
|
|
170
|
+
const storage = new CliAuthStorage();
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
if (!provider) {
|
|
174
|
+
const providers = storage.listProviders();
|
|
175
|
+
if (providers.length === 0) {
|
|
176
|
+
console.log("No OAuth credentials stored.");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
181
|
+
console.log("Select a provider to logout:\n");
|
|
182
|
+
for (let i = 0; i < providers.length; i++) {
|
|
183
|
+
console.log(` ${i + 1}. ${providers[i]}`);
|
|
184
|
+
}
|
|
185
|
+
console.log();
|
|
186
|
+
|
|
187
|
+
const choice = await prompt(rl, `Enter number (1-${providers.length}): `);
|
|
188
|
+
rl.close();
|
|
189
|
+
|
|
190
|
+
const index = parseInt(choice, 10) - 1;
|
|
191
|
+
if (index < 0 || index >= providers.length) {
|
|
192
|
+
console.error("Invalid selection");
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
provider = providers[index] as OAuthProvider;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const oauth = storage.getOAuth(provider);
|
|
199
|
+
if (!oauth) {
|
|
200
|
+
console.error(`Not logged in to ${provider}`);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
storage.deleteProvider(provider);
|
|
205
|
+
console.log(`Logged out from ${provider}`);
|
|
206
|
+
} finally {
|
|
207
|
+
storage.close();
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
150
212
|
if (command === "login") {
|
|
151
213
|
let provider = args[1] as OAuthProvider | undefined;
|
|
152
214
|
|
|
@@ -171,7 +233,7 @@ Examples:
|
|
|
171
233
|
|
|
172
234
|
if (!PROVIDERS.some((p) => p.id === provider)) {
|
|
173
235
|
console.error(`Unknown provider: ${provider}`);
|
|
174
|
-
console.error(`Use '
|
|
236
|
+
console.error(`Use 'bunx @oh-my-pi/pi-ai list' to see available providers`);
|
|
175
237
|
process.exit(1);
|
|
176
238
|
}
|
|
177
239
|
|
|
@@ -181,7 +243,7 @@ Examples:
|
|
|
181
243
|
}
|
|
182
244
|
|
|
183
245
|
console.error(`Unknown command: ${command}`);
|
|
184
|
-
console.error(`Use '
|
|
246
|
+
console.error(`Use 'bunx @oh-my-pi/pi-ai --help' for usage`);
|
|
185
247
|
process.exit(1);
|
|
186
248
|
}
|
|
187
249
|
|