@f5-sales-demo/pi-ai 21.2.0 → 21.2.1

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
@@ -1047,7 +1047,7 @@ bunx @f5-sales-demo/pi-ai list # list available providers
1047
1047
  Credentials are saved to `agent.db` in the agent directory. `/login qianfan` opens the Qianfan console and stores the pasted API key.
1048
1048
 
1049
1049
  `login` supports OAuth providers (Anthropic, GitHub Copilot, Gemini CLI, Antigravity) and API-key onboarding flows.
1050
- OpenAI has two credential paths. Use `OPENAI_API_KEY` for usage-based Platform API access. Use `login openai-codex` for ChatGPT Plus/Pro subscription OAuth; SSH/headless terminals automatically receive a short device code, while local desktops use browser PKCE. Use `login openai-codex-browser` to force the callback flow. `login openai` explains the API-key path.
1050
+ OpenAI has two credential paths. Use `OPENAI_API_KEY` for usage-based Platform API access. Use `login openai-codex` for ChatGPT Plus/Pro subscription OAuth; SSH/headless terminals automatically receive a short device code, while local desktops use browser PKCE. `login openai` explains the API-key path.
1051
1051
 
1052
1052
  For the current OpenAI-compatible integrations, API-key onboarding covers Together, Moonshot, Qianfan, NVIDIA, NanoGPT, Hugging Face, Venice, Xiaomi, vLLM, LiteLLM, Cloudflare AI Gateway, and Qwen Portal. Ollama is typically local and unauthenticated; set `OLLAMA_API_KEY` only when your Ollama deployment enforces bearer auth.
1053
1053
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/pi-ai",
4
- "version": "21.2.0",
4
+ "version": "21.2.1",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -46,7 +46,7 @@
46
46
  "@anthropic-ai/sdk": "^0.115",
47
47
  "@aws-sdk/client-bedrock-runtime": "^3",
48
48
  "@bufbuild/protobuf": "^2.11",
49
- "@f5-sales-demo/pi-utils": "21.2.0",
49
+ "@f5-sales-demo/pi-utils": "21.2.1",
50
50
  "@google/genai": "^2.15",
51
51
  "@sinclair/typebox": "^0.34",
52
52
  "@smithy/node-http-handler": "^4.4",
@@ -821,13 +821,6 @@ export class AuthStorage {
821
821
  onManualCodeInput: ctrl.onManualCodeInput ?? manualCodeInput,
822
822
  });
823
823
  break;
824
- case "openai-codex-browser":
825
- credentials = await loginOpenAICodex({
826
- ...ctrl,
827
- method: "browser",
828
- onManualCodeInput: ctrl.onManualCodeInput ?? manualCodeInput,
829
- });
830
- break;
831
824
  case "openai":
832
825
  throw new Error("OpenAI uses usage-based API access. Set OPENAI_API_KEY instead of signing in.");
833
826
  case "gitlab-duo":
package/src/cli.ts CHANGED
@@ -140,21 +140,6 @@ async function login(provider: OAuthProvider): Promise<void> {
140
140
  },
141
141
  });
142
142
  break;
143
- case "openai-codex-browser":
144
- credentials = await loginOpenAICodex({
145
- method: "browser",
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
- async onPrompt(p) {
153
- return await promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
154
- },
155
- });
156
- break;
157
-
158
143
  case "kimi-code":
159
144
  credentials = await loginKimi({
160
145
  onAuth(info) {
@@ -351,7 +336,6 @@ Providers:
351
336
  google-gemini-cli Google Gemini CLI
352
337
  google-antigravity Antigravity (Gemini 3, Claude, GPT-OSS)
353
338
  openai-codex ChatGPT Plus/Pro (Codex subscription)
354
- openai-codex-browser ChatGPT Plus/Pro (explicit browser callback)
355
339
  openai OpenAI Responses API (usage-based; set OPENAI_API_KEY)
356
340
  kimi-code Kimi Code
357
341
  kilo Kilo Gateway
@@ -156,13 +156,6 @@ const builtInOAuthProviders: OAuthProviderInfo[] = [
156
156
  name: "ChatGPT Plus/Pro (Codex Subscription)",
157
157
  available: true,
158
158
  },
159
- {
160
- id: "openai-codex-browser",
161
- name: "ChatGPT Plus/Pro (Browser callback)",
162
- available: true,
163
- canonicalId: "openai-codex",
164
- loginOnly: true,
165
- },
166
159
  {
167
160
  id: "openai",
168
161
  name: "OpenAI Responses API (usage-based API access)",
@@ -254,16 +254,55 @@ export type OpenAICodexLoginOptions = OAuthController & {
254
254
  method?: OpenAICodexLoginMethod;
255
255
  };
256
256
 
257
- export async function loginOpenAICodex(options: OpenAICodexLoginOptions): Promise<OAuthCredentials> {
258
- if (resolveOpenAICodexLoginMethod(options.method) === "device") {
259
- return loginOpenAICodexDevice(options);
257
+ export type OpenAICodexLoginDependencies = {
258
+ deviceLogin?: (options: OAuthController) => Promise<OAuthCredentials>;
259
+ browserLogin?: (options: OAuthController & { originator?: string }) => Promise<OAuthCredentials>;
260
+ };
261
+
262
+ export class OpenAICodexDeviceUnavailableError extends Error {
263
+ constructor() {
264
+ super(
265
+ "Device-code login is not enabled for this ChatGPT account or workspace. Enable it in ChatGPT security or workspace settings, or continue with browser/manual redirect login.",
266
+ );
267
+ this.name = "OpenAICodexDeviceUnavailableError";
260
268
  }
269
+ }
270
+
271
+ function isDeviceAuthorizationUnavailable(error: unknown): boolean {
272
+ return error instanceof OpenAICodexDeviceUnavailableError;
273
+ }
274
+
275
+ async function loginOpenAICodexBrowser(options: OAuthController & { originator?: string }): Promise<OAuthCredentials> {
261
276
  const pkce = await generatePKCE();
262
277
  const originator = options.originator?.trim() || "pi";
263
278
  const flow = new OpenAICodexOAuthFlow(options, pkce, originator);
264
279
  return flow.login();
265
280
  }
266
281
 
282
+ export async function loginOpenAICodex(
283
+ options: OpenAICodexLoginOptions,
284
+ dependencies: OpenAICodexLoginDependencies = {},
285
+ ): Promise<OAuthCredentials> {
286
+ const browserLogin = dependencies.browserLogin ?? loginOpenAICodexBrowser;
287
+ if (resolveOpenAICodexLoginMethod(options.method) !== "device") {
288
+ return browserLogin(options);
289
+ }
290
+
291
+ try {
292
+ return await (dependencies.deviceLogin ?? loginOpenAICodexDevice)(options);
293
+ } catch (error) {
294
+ if (!isDeviceAuthorizationUnavailable(error) || !options.onPrompt) throw error;
295
+ const response = await options.onPrompt({
296
+ message: "ChatGPT device authorization is unavailable. Continue with browser/manual redirect login? [Y/n]",
297
+ placeholder: "Press Enter to continue, or type no to cancel",
298
+ allowEmpty: true,
299
+ });
300
+ if (/^(?:n|no)$/i.test(response.trim())) throw new Error("ChatGPT login cancelled");
301
+ options.onProgress?.("Continuing with browser/manual redirect authentication…");
302
+ return browserLogin(options);
303
+ }
304
+ }
305
+
267
306
  export type OpenAICodexDeviceFlowDependencies = {
268
307
  fetch?: typeof fetch;
269
308
  sleep?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
@@ -289,9 +328,7 @@ export async function loginOpenAICodexDevice(
289
328
  });
290
329
  if (!initResponse.ok) {
291
330
  if (initResponse.status === 404) {
292
- throw new Error(
293
- "Device-code login is not enabled for this ChatGPT account or workspace. Enable it in ChatGPT security or workspace settings, or choose the browser callback login.",
294
- );
331
+ throw new OpenAICodexDeviceUnavailableError();
295
332
  }
296
333
  const detail = formatOpenAICodexTokenEndpointError(initResponse.status, await initResponse.text());
297
334
  throw new Error(`Device authorization initiation failed: ${detail}`);
@@ -35,7 +35,6 @@ export type OAuthProvider =
35
35
  | "ollama"
36
36
  | "openai"
37
37
  | "openai-codex"
38
- | "openai-codex-browser"
39
38
  | "opencode-go"
40
39
  | "opencode-zen"
41
40
  | "parallel"
@@ -80,7 +79,6 @@ export interface OAuthProviderInfo {
80
79
  /** Resolve login-only aliases to the provider ID used by credentials and models. */
81
80
  export function canonicalizeOAuthProviderId(provider: string): string {
82
81
  if (provider === "google-antigravity-enterprise") return "google-antigravity";
83
- if (provider === "openai-codex-browser") return "openai-codex";
84
82
  return provider;
85
83
  }
86
84