@f5-sales-demo/pi-ai 19.51.2

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 (128) hide show
  1. package/CHANGELOG.md +1997 -0
  2. package/README.md +1160 -0
  3. package/package.json +135 -0
  4. package/src/api-registry.ts +95 -0
  5. package/src/auth-storage.ts +2694 -0
  6. package/src/cli.ts +493 -0
  7. package/src/index.ts +42 -0
  8. package/src/model-cache.ts +97 -0
  9. package/src/model-manager.ts +349 -0
  10. package/src/model-thinking.ts +561 -0
  11. package/src/models.json +49439 -0
  12. package/src/models.json.d.ts +9 -0
  13. package/src/models.ts +56 -0
  14. package/src/prompts/turn-aborted-guidance.md +4 -0
  15. package/src/provider-details.ts +81 -0
  16. package/src/provider-models/descriptors.ts +285 -0
  17. package/src/provider-models/google.ts +90 -0
  18. package/src/provider-models/index.ts +4 -0
  19. package/src/provider-models/openai-compat.ts +2074 -0
  20. package/src/provider-models/special.ts +106 -0
  21. package/src/providers/amazon-bedrock.ts +706 -0
  22. package/src/providers/anthropic.ts +1682 -0
  23. package/src/providers/azure-openai-responses.ts +391 -0
  24. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  25. package/src/providers/cursor/proto/agent.proto +3526 -0
  26. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  27. package/src/providers/cursor/proto/buf.yaml +17 -0
  28. package/src/providers/cursor.ts +2218 -0
  29. package/src/providers/github-copilot-headers.ts +140 -0
  30. package/src/providers/gitlab-duo.ts +381 -0
  31. package/src/providers/google-gemini-cli.ts +1133 -0
  32. package/src/providers/google-shared.ts +354 -0
  33. package/src/providers/google-vertex.ts +436 -0
  34. package/src/providers/google.ts +381 -0
  35. package/src/providers/kimi.ts +151 -0
  36. package/src/providers/openai-codex/constants.ts +43 -0
  37. package/src/providers/openai-codex/request-transformer.ts +158 -0
  38. package/src/providers/openai-codex/response-handler.ts +81 -0
  39. package/src/providers/openai-codex-responses.ts +2345 -0
  40. package/src/providers/openai-completions-compat.ts +159 -0
  41. package/src/providers/openai-completions.ts +1290 -0
  42. package/src/providers/openai-responses-shared.ts +452 -0
  43. package/src/providers/openai-responses.ts +519 -0
  44. package/src/providers/register-builtins.ts +329 -0
  45. package/src/providers/synthetic.ts +154 -0
  46. package/src/providers/transform-messages.ts +234 -0
  47. package/src/rate-limit-utils.ts +84 -0
  48. package/src/stream.ts +728 -0
  49. package/src/types.ts +546 -0
  50. package/src/usage/claude.ts +337 -0
  51. package/src/usage/gemini.ts +248 -0
  52. package/src/usage/github-copilot.ts +421 -0
  53. package/src/usage/google-antigravity.ts +200 -0
  54. package/src/usage/kimi.ts +286 -0
  55. package/src/usage/minimax-code.ts +31 -0
  56. package/src/usage/openai-codex.ts +387 -0
  57. package/src/usage/zai.ts +247 -0
  58. package/src/usage.ts +130 -0
  59. package/src/utils/abort.ts +36 -0
  60. package/src/utils/anthropic-auth.ts +293 -0
  61. package/src/utils/discovery/antigravity.ts +261 -0
  62. package/src/utils/discovery/codex.ts +371 -0
  63. package/src/utils/discovery/cursor.ts +306 -0
  64. package/src/utils/discovery/gemini.ts +248 -0
  65. package/src/utils/discovery/index.ts +5 -0
  66. package/src/utils/discovery/openai-compatible.ts +224 -0
  67. package/src/utils/event-stream.ts +209 -0
  68. package/src/utils/http-inspector.ts +165 -0
  69. package/src/utils/idle-iterator.ts +176 -0
  70. package/src/utils/json-parse.ts +28 -0
  71. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  72. package/src/utils/oauth/anthropic.ts +134 -0
  73. package/src/utils/oauth/api-key-validation.ts +92 -0
  74. package/src/utils/oauth/callback-server.ts +276 -0
  75. package/src/utils/oauth/cerebras.ts +59 -0
  76. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  77. package/src/utils/oauth/cursor.ts +157 -0
  78. package/src/utils/oauth/github-copilot.ts +358 -0
  79. package/src/utils/oauth/gitlab-duo.ts +123 -0
  80. package/src/utils/oauth/google-antigravity.ts +275 -0
  81. package/src/utils/oauth/google-gemini-cli.ts +334 -0
  82. package/src/utils/oauth/huggingface.ts +62 -0
  83. package/src/utils/oauth/index.ts +512 -0
  84. package/src/utils/oauth/kagi.ts +47 -0
  85. package/src/utils/oauth/kilo.ts +87 -0
  86. package/src/utils/oauth/kimi.ts +251 -0
  87. package/src/utils/oauth/litellm.ts +81 -0
  88. package/src/utils/oauth/lm-studio.ts +40 -0
  89. package/src/utils/oauth/minimax-code.ts +78 -0
  90. package/src/utils/oauth/moonshot.ts +59 -0
  91. package/src/utils/oauth/nanogpt.ts +51 -0
  92. package/src/utils/oauth/nvidia.ts +70 -0
  93. package/src/utils/oauth/oauth.html +199 -0
  94. package/src/utils/oauth/ollama.ts +47 -0
  95. package/src/utils/oauth/openai-codex.ts +190 -0
  96. package/src/utils/oauth/opencode.ts +49 -0
  97. package/src/utils/oauth/parallel.ts +46 -0
  98. package/src/utils/oauth/perplexity.ts +200 -0
  99. package/src/utils/oauth/pkce.ts +18 -0
  100. package/src/utils/oauth/qianfan.ts +58 -0
  101. package/src/utils/oauth/qwen-portal.ts +60 -0
  102. package/src/utils/oauth/synthetic.ts +60 -0
  103. package/src/utils/oauth/tavily.ts +46 -0
  104. package/src/utils/oauth/together.ts +59 -0
  105. package/src/utils/oauth/types.ts +89 -0
  106. package/src/utils/oauth/venice.ts +59 -0
  107. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  108. package/src/utils/oauth/vllm.ts +40 -0
  109. package/src/utils/oauth/xiaomi.ts +88 -0
  110. package/src/utils/oauth/zai.ts +60 -0
  111. package/src/utils/oauth/zenmux.ts +51 -0
  112. package/src/utils/overflow.ts +134 -0
  113. package/src/utils/retry-after.ts +110 -0
  114. package/src/utils/retry.ts +93 -0
  115. package/src/utils/schema/CONSTRAINTS.md +160 -0
  116. package/src/utils/schema/adapt.ts +20 -0
  117. package/src/utils/schema/compatibility.ts +397 -0
  118. package/src/utils/schema/dereference.ts +93 -0
  119. package/src/utils/schema/equality.ts +93 -0
  120. package/src/utils/schema/fields.ts +147 -0
  121. package/src/utils/schema/index.ts +9 -0
  122. package/src/utils/schema/normalize-cca.ts +479 -0
  123. package/src/utils/schema/sanitize-google.ts +212 -0
  124. package/src/utils/schema/strict-mode.ts +385 -0
  125. package/src/utils/schema/types.ts +5 -0
  126. package/src/utils/tool-choice.ts +81 -0
  127. package/src/utils/validation.ts +664 -0
  128. package/src/utils.ts +147 -0
package/src/cli.ts ADDED
@@ -0,0 +1,493 @@
1
+ #!/usr/bin/env bun
2
+ import * as readline from "node:readline";
3
+ import { AuthCredentialStore } from "./auth-storage";
4
+ import { getOAuthProviders } from "./utils/oauth";
5
+ import { loginAnthropic } from "./utils/oauth/anthropic";
6
+ import { loginCursor } from "./utils/oauth/cursor";
7
+ import { loginGitHubCopilot } from "./utils/oauth/github-copilot";
8
+ import { loginAntigravity } from "./utils/oauth/google-antigravity";
9
+ import { loginGeminiCli } from "./utils/oauth/google-gemini-cli";
10
+ import { loginKagi } from "./utils/oauth/kagi";
11
+ import { loginKilo } from "./utils/oauth/kilo";
12
+ import { loginKimi } from "./utils/oauth/kimi";
13
+ import { loginMiniMaxCode, loginMiniMaxCodeCn } from "./utils/oauth/minimax-code";
14
+ import { loginNanoGPT } from "./utils/oauth/nanogpt";
15
+ import { loginOpenAICodex } from "./utils/oauth/openai-codex";
16
+ import { loginParallel } from "./utils/oauth/parallel";
17
+ import { loginTavily } from "./utils/oauth/tavily";
18
+ import type { OAuthCredentials, OAuthProvider } from "./utils/oauth/types";
19
+ import { loginZai } from "./utils/oauth/zai";
20
+ import { loginZenMux } from "./utils/oauth/zenmux";
21
+
22
+ const PROVIDERS = getOAuthProviders();
23
+
24
+ function prompt(rl: readline.Interface, question: string): Promise<string> {
25
+ const { promise, resolve, reject } = Promise.withResolvers<string>();
26
+ const input = process.stdin as NodeJS.ReadStream;
27
+ const supportsRawMode = input.isTTY && typeof input.setRawMode === "function";
28
+ const wasRaw = supportsRawMode ? input.isRaw : false;
29
+ let settled = false;
30
+
31
+ const cleanup = () => {
32
+ rl.off("SIGINT", onSigint);
33
+ if (supportsRawMode) {
34
+ input.off("keypress", onKeypress);
35
+ input.setRawMode?.(wasRaw);
36
+ }
37
+ };
38
+
39
+ const finish = (result: () => void) => {
40
+ if (settled) return;
41
+ settled = true;
42
+ cleanup();
43
+ result();
44
+ };
45
+
46
+ const cancel = () => {
47
+ finish(() => reject(new Error("Login cancelled")));
48
+ };
49
+
50
+ const onSigint = () => {
51
+ cancel();
52
+ };
53
+
54
+ const onKeypress = (_str: string, key: readline.Key) => {
55
+ if (key.name === "escape" || (key.ctrl && key.name === "c")) {
56
+ cancel();
57
+ rl.close();
58
+ }
59
+ };
60
+
61
+ if (supportsRawMode) {
62
+ readline.emitKeypressEvents(input, rl);
63
+ input.setRawMode(true);
64
+ input.on("keypress", onKeypress);
65
+ }
66
+
67
+ rl.once("SIGINT", onSigint);
68
+ rl.question(question, answer => {
69
+ finish(() => resolve(answer));
70
+ });
71
+ return promise;
72
+ }
73
+
74
+ async function login(provider: OAuthProvider): Promise<void> {
75
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
76
+
77
+ const promptFn = (msg: string) => prompt(rl, `${msg} `);
78
+ const storage = await AuthCredentialStore.open();
79
+
80
+ try {
81
+ let credentials: OAuthCredentials;
82
+
83
+ switch (provider) {
84
+ case "anthropic":
85
+ credentials = await loginAnthropic({
86
+ onAuth(info) {
87
+ const { url } = info;
88
+ console.log(`\nOpen this URL in your browser:\n${url}\n`);
89
+ },
90
+ onProgress(message) {
91
+ console.log(message);
92
+ },
93
+ });
94
+ break;
95
+
96
+ case "github-copilot":
97
+ credentials = await loginGitHubCopilot({
98
+ onAuth(url, instructions) {
99
+ console.log(`\nOpen this URL in your browser:\n${url}`);
100
+ if (instructions) console.log(instructions);
101
+ console.log();
102
+ },
103
+ async onPrompt(p) {
104
+ return await promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
105
+ },
106
+ });
107
+ break;
108
+
109
+ case "google-gemini-cli":
110
+ credentials = await loginGeminiCli({
111
+ onAuth(info) {
112
+ const { url, instructions } = info;
113
+ console.log(`\nOpen this URL in your browser:\n${url}`);
114
+ if (instructions) console.log(instructions);
115
+ console.log();
116
+ },
117
+ });
118
+ break;
119
+
120
+ case "google-antigravity":
121
+ credentials = await loginAntigravity({
122
+ onAuth(info) {
123
+ const { url, instructions } = info;
124
+ console.log(`\nOpen this URL in your browser:\n${url}`);
125
+ if (instructions) console.log(instructions);
126
+ console.log();
127
+ },
128
+ });
129
+ break;
130
+ case "openai-codex":
131
+ credentials = await loginOpenAICodex({
132
+ onAuth(info) {
133
+ const { url, instructions } = info;
134
+ console.log(`\nOpen this URL in your browser:\n${url}`);
135
+ if (instructions) console.log(instructions);
136
+ console.log();
137
+ },
138
+ async onPrompt(p) {
139
+ return await promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
140
+ },
141
+ });
142
+ break;
143
+
144
+ case "kimi-code":
145
+ credentials = await loginKimi({
146
+ onAuth(info) {
147
+ const { url, instructions } = info;
148
+ console.log(`\nOpen this URL in your browser:\n${url}`);
149
+ if (instructions) console.log(instructions);
150
+ console.log();
151
+ },
152
+ });
153
+ break;
154
+ case "kilo":
155
+ credentials = await loginKilo({
156
+ onAuth(info) {
157
+ const { url, instructions } = info;
158
+ console.log(`\nOpen this URL in your browser:\n${url}`);
159
+ if (instructions) console.log(instructions);
160
+ console.log();
161
+ },
162
+ });
163
+ break;
164
+ case "kagi": {
165
+ const apiKey = await loginKagi({
166
+ onAuth(info) {
167
+ const { url, instructions } = info;
168
+ console.log(`\nOpen this URL in your browser:\n${url}`);
169
+ if (instructions) console.log(instructions);
170
+ console.log();
171
+ },
172
+ onPrompt(p) {
173
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
174
+ },
175
+ });
176
+ storage.saveApiKey(provider, apiKey);
177
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
178
+ return;
179
+ }
180
+ case "tavily": {
181
+ const apiKey = await loginTavily({
182
+ onAuth(info) {
183
+ const { url, instructions } = info;
184
+ console.log(`\nOpen this URL in your browser:\n${url}`);
185
+ if (instructions) console.log(instructions);
186
+ console.log();
187
+ },
188
+ onPrompt(p) {
189
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
190
+ },
191
+ });
192
+ storage.saveApiKey(provider, apiKey);
193
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
194
+ return;
195
+ }
196
+ case "parallel": {
197
+ const apiKey = await loginParallel({
198
+ onAuth(info) {
199
+ const { url, instructions } = info;
200
+ console.log(`\nOpen this URL in your browser:\n${url}`);
201
+ if (instructions) console.log(instructions);
202
+ console.log();
203
+ },
204
+ onPrompt(p) {
205
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
206
+ },
207
+ });
208
+ storage.saveApiKey(provider, apiKey);
209
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
210
+ return;
211
+ }
212
+
213
+ case "cursor":
214
+ credentials = await loginCursor(
215
+ url => {
216
+ console.log(`\nOpen this URL in your browser:\n${url}\n`);
217
+ },
218
+ () => {
219
+ console.log("Waiting for browser authentication...");
220
+ },
221
+ );
222
+ break;
223
+
224
+ case "zai": {
225
+ const apiKey = await loginZai({
226
+ onAuth(info) {
227
+ const { url, instructions } = info;
228
+ console.log(`\nOpen this URL in your browser:\n${url}`);
229
+ if (instructions) console.log(instructions);
230
+ console.log();
231
+ },
232
+ onPrompt(p) {
233
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
234
+ },
235
+ });
236
+ storage.saveApiKey(provider, apiKey);
237
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
238
+ return;
239
+ }
240
+
241
+ case "nanogpt": {
242
+ const apiKey = await loginNanoGPT({
243
+ onAuth(info) {
244
+ const { url, instructions } = info;
245
+ console.log(`\nOpen this URL in your browser:\n${url}`);
246
+ if (instructions) console.log(instructions);
247
+ console.log();
248
+ },
249
+ onPrompt(p) {
250
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
251
+ },
252
+ });
253
+ storage.saveApiKey(provider, apiKey);
254
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
255
+ return;
256
+ }
257
+
258
+ case "zenmux": {
259
+ const apiKey = await loginZenMux({
260
+ onAuth(info) {
261
+ const { url, instructions } = info;
262
+ console.log(`\nOpen this URL in your browser:\n${url}`);
263
+ if (instructions) console.log(instructions);
264
+ console.log();
265
+ },
266
+ onPrompt(p) {
267
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
268
+ },
269
+ });
270
+ storage.saveApiKey(provider, apiKey);
271
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
272
+ return;
273
+ }
274
+ case "minimax-code": {
275
+ const apiKey = await loginMiniMaxCode({
276
+ onAuth(info) {
277
+ const { url, instructions } = info;
278
+ console.log(`\nOpen this URL in your browser:\n${url}`);
279
+ if (instructions) console.log(instructions);
280
+ console.log();
281
+ },
282
+ onPrompt(p) {
283
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
284
+ },
285
+ });
286
+ storage.saveApiKey(provider, apiKey);
287
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
288
+ return;
289
+ }
290
+
291
+ case "minimax-code-cn": {
292
+ const apiKey = await loginMiniMaxCodeCn({
293
+ onAuth(info) {
294
+ const { url, instructions } = info;
295
+ console.log(`\nOpen this URL in your browser:\n${url}`);
296
+ if (instructions) console.log(instructions);
297
+ console.log();
298
+ },
299
+ onPrompt(p) {
300
+ return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
301
+ },
302
+ });
303
+ storage.saveApiKey(provider, apiKey);
304
+ console.log(`\nAPI key saved to ~/.xcsh/agent/agent.db`);
305
+ return;
306
+ }
307
+
308
+ default:
309
+ throw new Error(`Unknown provider: ${provider}`);
310
+ }
311
+
312
+ storage.saveOAuth(provider, credentials);
313
+
314
+ console.log(`\nCredentials saved to ~/.xcsh/agent/agent.db`);
315
+ } finally {
316
+ storage.close();
317
+ rl.close();
318
+ }
319
+ }
320
+
321
+ async function main(): Promise<void> {
322
+ const args = process.argv.slice(2);
323
+ const command = args[0];
324
+
325
+ if (!command || command === "help" || command === "--help" || command === "-h") {
326
+ console.log(`Usage: bunx @f5xc-salesdemos/pi-ai <command> [provider]
327
+
328
+ Commands:
329
+ login [provider] Login to a provider
330
+ logout [provider] Logout from a provider
331
+ status Show logged-in providers
332
+ list List available providers
333
+
334
+ Providers:
335
+ anthropic Anthropic (Claude Pro/Max)
336
+ github-copilot GitHub Copilot
337
+ google-gemini-cli Google Gemini CLI
338
+ google-antigravity Antigravity (Gemini 3, Claude, GPT-OSS)
339
+ openai-codex OpenAI Codex (ChatGPT Plus/Pro)
340
+ kimi-code Kimi Code
341
+ kilo Kilo Gateway
342
+ kagi Kagi
343
+ tavily Tavily
344
+ zai Z.AI (GLM Coding Plan)
345
+ nanogpt NanoGPT
346
+ minimax-code MiniMax Coding Plan (International)
347
+ minimax-code-cn MiniMax Coding Plan (China)
348
+ cursor Cursor (Claude, GPT, etc.)
349
+ zenmux ZenMux
350
+
351
+ Examples:
352
+ bunx @f5xc-salesdemos/pi-ai login # interactive provider selection
353
+ bunx @f5xc-salesdemos/pi-ai login anthropic # login to specific provider
354
+ bunx @f5xc-salesdemos/pi-ai logout anthropic # logout from specific provider
355
+ bunx @f5xc-salesdemos/pi-ai status # show logged-in providers
356
+ bunx @f5xc-salesdemos/pi-ai list # list providers
357
+ `);
358
+ return;
359
+ }
360
+
361
+ if (command === "status") {
362
+ const storage = await AuthCredentialStore.open();
363
+ try {
364
+ const providers = storage.listProviders();
365
+ if (providers.length === 0) {
366
+ console.log("No credentials stored.");
367
+ console.log(`Use 'bunx @f5xc-salesdemos/pi-ai login' to authenticate.`);
368
+ } else {
369
+ console.log("Logged-in providers:\n");
370
+ for (const provider of providers) {
371
+ const oauth = storage.getOAuth(provider);
372
+ if (oauth) {
373
+ const expires = new Date(oauth.expires);
374
+ const expired = Date.now() >= oauth.expires;
375
+ const status = expired ? "(expired)" : `(expires ${expires.toLocaleString()})`;
376
+ console.log(` ${provider.padEnd(20)} ${status}`);
377
+ continue;
378
+ }
379
+ const apiKey = storage.getApiKey(provider);
380
+ if (apiKey) {
381
+ console.log(` ${provider.padEnd(20)} (api key)`);
382
+ }
383
+ }
384
+ }
385
+ } finally {
386
+ storage.close();
387
+ }
388
+ return;
389
+ }
390
+
391
+ if (command === "list") {
392
+ console.log("Available providers:\n");
393
+ for (const p of PROVIDERS) {
394
+ console.log(` ${p.id.padEnd(20)} ${p.name}`);
395
+ }
396
+ return;
397
+ }
398
+
399
+ if (command === "logout") {
400
+ let provider = args[1] as OAuthProvider | undefined;
401
+ const storage = await AuthCredentialStore.open();
402
+
403
+ try {
404
+ if (!provider) {
405
+ const providers = storage.listProviders();
406
+ if (providers.length === 0) {
407
+ console.log("No credentials stored.");
408
+ return;
409
+ }
410
+
411
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
412
+ console.log("Select a provider to logout:\n");
413
+ for (let i = 0; i < providers.length; i++) {
414
+ console.log(` ${i + 1}. ${providers[i]}`);
415
+ }
416
+ console.log();
417
+
418
+ const choice = await prompt(rl, `Enter number (1-${providers.length}): `);
419
+ rl.close();
420
+
421
+ const index = parseInt(choice, 10) - 1;
422
+ if (index < 0 || index >= providers.length) {
423
+ console.error("Invalid selection");
424
+ process.exit(1);
425
+ }
426
+ provider = providers[index] as OAuthProvider;
427
+ }
428
+ if (!provider) {
429
+ console.error("No provider selected");
430
+ process.exit(1);
431
+ }
432
+
433
+ const oauth = storage.getOAuth(provider);
434
+ const apiKey = storage.getApiKey(provider);
435
+ if (!oauth && !apiKey) {
436
+ console.error(`Not logged in to ${provider}`);
437
+ process.exit(1);
438
+ }
439
+
440
+ storage.deleteProvider(provider);
441
+ console.log(`Logged out from ${provider}`);
442
+ } finally {
443
+ storage.close();
444
+ }
445
+ return;
446
+ }
447
+
448
+ if (command === "login") {
449
+ let provider = args[1] as OAuthProvider | undefined;
450
+
451
+ if (!provider) {
452
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
453
+ console.log("Select a provider:\n");
454
+ for (let i = 0; i < PROVIDERS.length; i++) {
455
+ console.log(` ${i + 1}. ${PROVIDERS[i].name}`);
456
+ }
457
+ console.log();
458
+
459
+ const choice = await prompt(rl, `Enter number (1-${PROVIDERS.length}): `);
460
+ rl.close();
461
+
462
+ const index = parseInt(choice, 10) - 1;
463
+ if (index < 0 || index >= PROVIDERS.length) {
464
+ console.error("Invalid selection");
465
+ process.exit(1);
466
+ }
467
+ provider = PROVIDERS[index].id as OAuthProvider;
468
+ }
469
+ if (!provider) {
470
+ console.error("No provider selected");
471
+ process.exit(1);
472
+ }
473
+
474
+ if (!PROVIDERS.some(p => p.id === provider)) {
475
+ console.error(`Unknown provider: ${provider}`);
476
+ console.error(`Use 'bunx @f5xc-salesdemos/pi-ai list' to see available providers`);
477
+ process.exit(1);
478
+ }
479
+
480
+ console.log(`Logging in to ${provider}…`);
481
+ await login(provider);
482
+ return;
483
+ }
484
+
485
+ console.error(`Unknown command: ${command}`);
486
+ console.error(`Use 'bunx @f5xc-salesdemos/pi-ai --help' for usage`);
487
+ process.exit(1);
488
+ }
489
+
490
+ main().catch(err => {
491
+ console.error("Error:", err.message);
492
+ process.exit(1);
493
+ });
package/src/index.ts ADDED
@@ -0,0 +1,42 @@
1
+ export type { Static, TSchema } from "@sinclair/typebox";
2
+ export { Type } from "@sinclair/typebox";
3
+ export * from "./api-registry";
4
+ export * from "./auth-storage";
5
+ export * from "./model-cache";
6
+ export * from "./model-manager";
7
+ export * from "./model-thinking";
8
+ export * from "./models";
9
+ export * from "./provider-details";
10
+ export * from "./provider-models";
11
+ export * from "./providers/anthropic";
12
+ export * from "./providers/azure-openai-responses";
13
+ export * from "./providers/cursor";
14
+ export * from "./providers/gitlab-duo";
15
+ export * from "./providers/google";
16
+ export * from "./providers/google-gemini-cli";
17
+ export * from "./providers/google-vertex";
18
+ export * from "./providers/kimi";
19
+ export type { OpenAICodexResponsesOptions } from "./providers/openai-codex-responses";
20
+ export * from "./providers/openai-completions";
21
+ export * from "./providers/openai-responses";
22
+ export * from "./providers/synthetic";
23
+ export * from "./rate-limit-utils";
24
+ export * from "./stream";
25
+ export * from "./types";
26
+ export * from "./usage";
27
+ export * from "./usage/claude";
28
+ export * from "./usage/gemini";
29
+ export * from "./usage/github-copilot";
30
+ export * from "./usage/google-antigravity";
31
+ export * from "./usage/kimi";
32
+ export * from "./usage/minimax-code";
33
+ export * from "./usage/openai-codex";
34
+ export * from "./usage/zai";
35
+ export * from "./utils/anthropic-auth";
36
+ export * from "./utils/discovery";
37
+ export * from "./utils/event-stream";
38
+ export * from "./utils/oauth";
39
+ export * from "./utils/overflow";
40
+ export * from "./utils/retry";
41
+ export * from "./utils/schema";
42
+ export * from "./utils/validation";
@@ -0,0 +1,97 @@
1
+ /**
2
+ * SQLite-backed model cache for atomic cross-process access.
3
+ * Replaces per-provider JSON files with a single cache.db.
4
+ */
5
+ import { Database } from "bun:sqlite";
6
+ import { getModelDbPath } from "@f5xc-salesdemos/pi-utils";
7
+ import type { Api, Model } from "./types";
8
+
9
+ const CACHE_SCHEMA_VERSION = 2;
10
+
11
+ interface CacheRow {
12
+ provider_id: string;
13
+ version: number;
14
+ updated_at: number;
15
+ authoritative: number;
16
+ models: string;
17
+ }
18
+
19
+ interface CacheEntry<TApi extends Api = Api> {
20
+ models: Model<TApi>[];
21
+ fresh: boolean;
22
+ authoritative: boolean;
23
+ updatedAt: number;
24
+ }
25
+
26
+ let sharedDb: Database | null = null;
27
+ let sharedDbPath: string | null = null;
28
+
29
+ function getDb(dbPath?: string): Database {
30
+ const resolvedPath = dbPath ?? getModelDbPath();
31
+ if (sharedDb && sharedDbPath === resolvedPath) {
32
+ return sharedDb;
33
+ }
34
+ if (sharedDb) {
35
+ sharedDb.close();
36
+ }
37
+ const db = new Database(resolvedPath, { create: true });
38
+ db.run("PRAGMA journal_mode = WAL");
39
+ db.run("PRAGMA busy_timeout = 3000");
40
+ db.run(`
41
+ CREATE TABLE IF NOT EXISTS model_cache (
42
+ provider_id TEXT PRIMARY KEY,
43
+ version INTEGER NOT NULL,
44
+ updated_at INTEGER NOT NULL,
45
+ authoritative INTEGER NOT NULL DEFAULT 0,
46
+ models TEXT NOT NULL
47
+ )
48
+ `);
49
+ sharedDb = db;
50
+ sharedDbPath = resolvedPath;
51
+ return db;
52
+ }
53
+
54
+ export function readModelCache<TApi extends Api>(
55
+ providerId: string,
56
+ ttlMs: number,
57
+ now: () => number,
58
+ dbPath?: string,
59
+ ): CacheEntry<TApi> | null {
60
+ try {
61
+ const db = getDb(dbPath);
62
+ const row = db.query<CacheRow, [string]>("SELECT * FROM model_cache WHERE provider_id = ?").get(providerId);
63
+ if (!row || row.version !== CACHE_SCHEMA_VERSION) {
64
+ return null;
65
+ }
66
+ const models = JSON.parse(row.models) as Model<TApi>[];
67
+ const ageMs = now() - row.updated_at;
68
+ const fresh = Number.isFinite(ageMs) && ageMs >= 0 && ageMs <= ttlMs;
69
+ return {
70
+ models,
71
+ fresh,
72
+ authoritative: row.authoritative === 1,
73
+ updatedAt: row.updated_at,
74
+ };
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ export function writeModelCache<TApi extends Api>(
81
+ providerId: string,
82
+ updatedAt: number,
83
+ models: Model<TApi>[],
84
+ authoritative: boolean,
85
+ dbPath?: string,
86
+ ): void {
87
+ try {
88
+ const db = getDb(dbPath);
89
+ db.run(
90
+ `INSERT OR REPLACE INTO model_cache (provider_id, version, updated_at, authoritative, models)
91
+ VALUES (?, ?, ?, ?, ?)`,
92
+ [providerId, CACHE_SCHEMA_VERSION, updatedAt, authoritative ? 1 : 0, JSON.stringify(models)],
93
+ );
94
+ } catch {
95
+ // Cache writes are best-effort; failures should not break model resolution.
96
+ }
97
+ }