@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 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
- - [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)
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
- - [Unified Interface](#unified-interface-streamsimplecompletesimple)
21
- - [Provider-Specific Options](#provider-specific-options-streamcomplete)
22
- - [Streaming Thinking Content](#streaming-thinking-content)
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
- - [Aborting Requests](#aborting-requests)
26
- - [Continuing After Abort](#continuing-after-abort)
25
+ - [Aborting Requests](#aborting-requests)
26
+ - [Continuing After Abort](#continuing-after-abort)
27
27
  - [APIs, Models, and Providers](#apis-models-and-providers)
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)
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
- - [Environment Variables](#environment-variables-nodejs-only)
37
- - [Checking Environment Variables](#checking-environment-variables)
36
+ - [Environment Variables](#environment-variables-nodejs-only)
37
+ - [Checking Environment Variables](#checking-environment-variables)
38
38
  - [OAuth Providers](#oauth-providers)
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)
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
- npx @oh-my-pi/pi-ai login # interactive provider selection
961
- npx @oh-my-pi/pi-ai login anthropic # login to specific provider
962
- npx @oh-my-pi/pi-ai list # list available providers
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.7.67",
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
- return new Promise((resolve) => rl.question(question, resolve));
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
- (url) => {
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
- async () => {
48
- return await promptFn("Paste the authorization code:");
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: (url, instructions) => {
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
- onPrompt: async (p) => {
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
- console.log(`\nOpen this URL in your browser:\n${info.url}`);
71
- if (info.instructions) console.log(info.instructions);
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
- (msg) => console.log(msg),
75
- );
64
+ });
76
65
  break;
77
66
 
78
67
  case "google-antigravity":
79
- credentials = await loginAntigravity(
80
- (info) => {
81
- console.log(`\nOpen this URL in your browser:\n${info.url}`);
82
- if (info.instructions) console.log(info.instructions);
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
- (msg) => console.log(msg),
86
- );
75
+ });
87
76
  break;
88
77
  case "openai-codex":
89
78
  credentials = await loginOpenAICodex({
90
- onAuth: (info) => {
91
- console.log(`\nOpen this URL in your browser:\n${info.url}`);
92
- if (info.instructions) console.log(info.instructions);
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
- onPrompt: async (p) => {
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
- const auth = loadAuth();
107
- auth[provider] = { type: "oauth", ...credentials };
108
- saveAuth(auth);
95
+ storage.saveOAuth(provider, credentials);
109
96
 
110
- console.log(`\nCredentials saved to ${AUTH_FILE}`);
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: npx @oh-my-pi/pi-ai <command> [provider]
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
- npx @oh-my-pi/pi-ai login # interactive provider selection
136
- npx @oh-my-pi/pi-ai login anthropic # login to specific provider
137
- npx @oh-my-pi/pi-ai list # list providers
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 'npx @oh-my-pi/pi-ai list' to see available providers`);
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 'npx @oh-my-pi/pi-ai --help' for usage`);
246
+ console.error(`Use 'bunx @oh-my-pi/pi-ai --help' for usage`);
185
247
  process.exit(1);
186
248
  }
187
249