pi-better-openai 0.1.7 → 0.1.8

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
@@ -7,7 +7,7 @@ A pi extension for OpenAI subscription workflows: fast mode, usage visibility, f
7
7
  Install from GitHub:
8
8
 
9
9
  ```bash
10
- pi install github:mattleong/pi-better-openai
10
+ pi install git:github.com/mattleong/pi-better-openai
11
11
  ```
12
12
 
13
13
  Or install from npm:
package/index.ts CHANGED
@@ -15,7 +15,6 @@ import {
15
15
  IMAGE_OUTPUT_FORMATS,
16
16
  IMAGE_SAVE_MODES,
17
17
  configPaths,
18
- type FooterMode,
19
18
  type ResolvedConfig,
20
19
  type SupportedModel,
21
20
  isRecord,
@@ -24,7 +23,7 @@ import {
24
23
  parseModels,
25
24
  readRawConfig,
26
25
  resolveConfig,
27
- writeConfig
26
+ writeConfig,
28
27
  } from "./src/config.ts";
29
28
  import {
30
29
  AUTH_FILE,
@@ -34,7 +33,7 @@ import {
34
33
  formatUsageSnapshot,
35
34
  parseUsageSnapshot,
36
35
  readCodexAuth,
37
- requestCodexUsage
36
+ requestCodexUsage,
38
37
  } from "./src/usage.ts";
39
38
  import { registerOpenAIImage, _imageTest } from "./src/image.ts";
40
39
 
@@ -49,7 +48,10 @@ type SettingsPickerItem = {
49
48
  description?: string;
50
49
  currentValue: string;
51
50
  values?: string[];
52
- submenu?: (currentValue: string, done: (selectedValue?: string) => void) => { render(width: number): string[]; invalidate(): void; handleInput?(data: string): void };
51
+ submenu?: (
52
+ currentValue: string,
53
+ done: (selectedValue?: string) => void,
54
+ ) => { render(width: number): string[]; invalidate(): void; handleInput?(data: string): void };
53
55
  };
54
56
 
55
57
  function currentModelKey(ctx: ExtensionContext): string {
@@ -59,7 +61,9 @@ function currentModelKey(ctx: ExtensionContext): string {
59
61
  function supportsFast(ctx: ExtensionContext, supportedModels: SupportedModel[]): boolean {
60
62
  const current = ctx.model;
61
63
  if (!current) return false;
62
- return supportedModels.some((model) => model.provider === current.provider && model.id === current.id);
64
+ return supportedModels.some(
65
+ (model) => model.provider === current.provider && model.id === current.id,
66
+ );
63
67
  }
64
68
 
65
69
  function modelList(supportedModels: SupportedModel[]): string {
@@ -68,7 +72,12 @@ function modelList(supportedModels: SupportedModel[]): string {
68
72
  : "none configured";
69
73
  }
70
74
 
71
- function stateText(ctx: ExtensionContext, desiredActive: boolean, active: boolean, supportedModels: SupportedModel[]): string {
75
+ function stateText(
76
+ ctx: ExtensionContext,
77
+ desiredActive: boolean,
78
+ active: boolean,
79
+ supportedModels: SupportedModel[],
80
+ ): string {
72
81
  const model = currentModelKey(ctx);
73
82
  if (active) return `Fast mode is on for ${model}.`;
74
83
  if (desiredActive) {
@@ -78,7 +87,8 @@ function stateText(ctx: ExtensionContext, desiredActive: boolean, active: boolea
78
87
  }
79
88
 
80
89
  function isOpenAISubscriptionModel(ctx: ExtensionContext, cfg: ResolvedConfig): boolean {
81
- if (!ctx.model || (ctx.model.provider !== "openai" && ctx.model.provider !== "openai-codex")) return false;
90
+ if (!ctx.model || (ctx.model.provider !== "openai" && ctx.model.provider !== "openai-codex"))
91
+ return false;
82
92
  return !cfg.usage.showOnlyOnSubscriptionModels || ctx.modelRegistry.isUsingOAuth(ctx.model);
83
93
  }
84
94
 
@@ -114,7 +124,11 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
114
124
  function persist(nextConfig: ResolvedConfig): void {
115
125
  cachedConfig = { ...nextConfig, active, desiredActive };
116
126
  if (!nextConfig.persistState) return;
117
- writeConfig(nextConfig.configPath, { ...readRawConfig(nextConfig.configPath), active, desiredActive });
127
+ writeConfig(nextConfig.configPath, {
128
+ ...readRawConfig(nextConfig.configPath),
129
+ active,
130
+ desiredActive,
131
+ });
118
132
  }
119
133
 
120
134
  function applyDesiredFastState(ctx: ExtensionContext, cfg = config(ctx)): void {
@@ -128,13 +142,20 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
128
142
  persist(nextConfig);
129
143
  updateFooter(ctx);
130
144
  if (next && !active) {
131
- ctx.ui.notify(`Fast mode requested, but ${currentModelKey(ctx)} is unsupported. It will activate automatically when you switch to a supported model: ${modelList(nextConfig.supportedModels)}.`, "warning");
145
+ ctx.ui.notify(
146
+ `Fast mode requested, but ${currentModelKey(ctx)} is unsupported. It will activate automatically when you switch to a supported model: ${modelList(nextConfig.supportedModels)}.`,
147
+ "warning",
148
+ );
132
149
  return;
133
150
  }
134
151
  ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
135
152
  }
136
153
 
137
- async function refreshUsage(ctx: ExtensionContext, modelId = ctx.model?.id, options?: { notify?: boolean }): Promise<void> {
154
+ async function refreshUsage(
155
+ ctx: ExtensionContext,
156
+ modelId = ctx.model?.id,
157
+ options?: { notify?: boolean },
158
+ ): Promise<void> {
138
159
  if (shuttingDown || !ctx.hasUI) return;
139
160
  if (usageRefreshInFlight) {
140
161
  queuedUsageRefresh = { ctx, modelId, notify: queuedUsageRefresh?.notify || options?.notify };
@@ -164,7 +185,8 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
164
185
  usageUpdatedAt = usageSnapshot ? Date.now() : undefined;
165
186
  usageError = data ? undefined : `Missing openai-codex OAuth credentials in ${AUTH_FILE}.`;
166
187
  if (!shuttingDown) updateFooter(ctx);
167
- if (!shuttingDown && options?.notify) ctx.ui.notify(formatUsageStatus(ctx), usageSnapshot ? "info" : "warning");
188
+ if (!shuttingDown && options?.notify)
189
+ ctx.ui.notify(formatUsageStatus(ctx), usageSnapshot ? "info" : "warning");
168
190
  } catch (error) {
169
191
  if (shuttingDown) return;
170
192
  usageError = error instanceof Error ? error.message : String(error);
@@ -216,25 +238,27 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
216
238
  `Last successful update: ${usageUpdatedAt ? new Date(usageUpdatedAt).toLocaleTimeString() : "never"}`,
217
239
  `Last error: ${usageError ?? "none"}`,
218
240
  `Refresh interval: ${cfg.usage.refreshIntervalMs}ms`,
219
- `Endpoint: https://chatgpt.com/backend-api/wham/usage`
241
+ `Endpoint: https://chatgpt.com/backend-api/wham/usage`,
220
242
  ].join("\n");
221
243
  }
222
244
 
223
245
  function formatUsageStatus(ctx: ExtensionContext): string {
224
246
  const cfg = config(ctx);
225
247
  if (!cfg.usage.enabled) return "Usage display is disabled.";
226
- if (!isOpenAISubscriptionModel(ctx, cfg)) return "Usage hidden: current model is not an OpenAI subscription model.";
248
+ if (!isOpenAISubscriptionModel(ctx, cfg))
249
+ return "Usage hidden: current model is not an OpenAI subscription model.";
227
250
  if (!usageSnapshot) return `Usage unavailable${usageError ? `: ${usageError}` : "."}`;
228
- const stale = usageUpdatedAt && Date.now() - usageUpdatedAt > cfg.usage.refreshIntervalMs * 2
229
- ? ` | stale ${formatResetCountdown((Date.now() - usageUpdatedAt) / 1000)}`
230
- : "";
251
+ const stale =
252
+ usageUpdatedAt && Date.now() - usageUpdatedAt > cfg.usage.refreshIntervalMs * 2
253
+ ? ` | stale ${formatResetCountdown((Date.now() - usageUpdatedAt) / 1000)}`
254
+ : "";
231
255
  return `${formatUsageSnapshot(usageSnapshot, cfg.usage)}${stale}`;
232
256
  }
233
257
 
234
258
  pi.registerFlag(FLAG, {
235
259
  description: "Start with OpenAI fast mode enabled (service_tier=priority)",
236
260
  type: "boolean",
237
- default: false
261
+ default: false,
238
262
  });
239
263
 
240
264
  function formatDebugStatus(ctx: ExtensionContext): string {
@@ -247,10 +271,12 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
247
271
  `Configured service_tier: ${SERVICE_TIER}`,
248
272
  `Last injected: ${lastInjectedAt ? `${new Date(lastInjectedAt).toLocaleTimeString()} (${lastInjectedModel}, ${lastInjectedTier})` : "never"}`,
249
273
  `Footer mode: ${cfg.footer.mode}`,
250
- `Usage enabled: ${cfg.usage.enabled}`,
274
+ "",
275
+ formatUsageDebug(ctx),
276
+ "",
251
277
  `Image enabled: ${cfg.image.enabled}`,
252
278
  `Image default save: ${cfg.image.defaultSave}`,
253
- `Config: ${cfg.configPath}`
279
+ `Config: ${cfg.configPath}`,
254
280
  ].join("\n");
255
281
  }
256
282
 
@@ -265,14 +291,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
265
291
  const arg = args.trim().toLowerCase();
266
292
  if (!arg) return setActive(ctx, !desiredActive);
267
293
  ctx.ui.notify("Usage: /fast", "error");
268
- }
294
+ },
269
295
  });
270
296
 
271
297
  pi.registerCommand(OPENAI_STATUS_COMMAND, {
272
298
  description: "Show OpenAI subscription usage status",
273
299
  handler: async (_args, ctx) => {
274
300
  ctx.ui.notify(formatOpenAIStatus(ctx), "info");
275
- }
301
+ },
276
302
  });
277
303
 
278
304
  function textPanel(title: string, lines: string[], done: () => void) {
@@ -284,27 +310,124 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
284
310
  invalidate() {},
285
311
  handleInput(data: string) {
286
312
  if (data.includes("\x1b") || data === "escape" || data === "q" || data === "\x03") done();
287
- }
313
+ },
288
314
  };
289
315
  }
290
316
 
291
317
  function buildSettingsItems(ctx: ExtensionContext, cfg: ResolvedConfig): SettingsPickerItem[] {
292
318
  return [
293
- { id: "fast.enabled", label: "Fast mode", currentValue: String(desiredActive), values: ["true", "false"], description: `Request OpenAI fast mode. Activates for supported models: ${modelList(cfg.supportedModels)}.` },
294
- { id: "persistState", label: "Persist fast state", currentValue: String(cfg.persistState), values: ["true", "false"], description: "Remember fast-mode state across sessions." },
295
- { id: "footer.mode", label: "Footer mode", currentValue: cfg.footer.mode, values: [...FOOTER_MODES], description: "replace = custom footer, status = pi footer plus status line, off = no Better OpenAI footer/status." },
296
- { id: "usage.enabled", label: "Usage display", currentValue: String(cfg.usage.enabled), values: ["true", "false"], description: "Fetch and display OpenAI subscription usage windows." },
297
- { id: "usage.refreshIntervalMs", label: "Usage refresh", currentValue: String(cfg.usage.refreshIntervalMs), values: ["15000", "30000", "60000", "120000", "300000", "600000"], description: "Usage refresh interval in milliseconds." },
298
- { id: "usage.showOnlyOnSubscriptionModels", label: "Usage only on OAuth", currentValue: String(cfg.usage.showOnlyOnSubscriptionModels), values: ["true", "false"], description: "Only show usage when the current OpenAI model uses subscription/OAuth auth." },
299
- { id: "usage.showResetTimes", label: "Usage reset times", currentValue: String(cfg.usage.showResetTimes), values: ["true", "false"], description: "Include compact reset countdowns and local reset times." },
300
- { id: "image.enabled", label: "Image tool", currentValue: String(cfg.image.enabled), values: ["true", "false"], description: "Allow the openai_image tool to make image requests." },
301
- { id: "image.defaultModel", label: "Image model", currentValue: cfg.image.defaultModel, values: ["gpt-5.5", "gpt-5.4", "gpt-5.2", "gpt-5"], description: "Mainline model used for image generation when current model is not openai-codex." },
302
- { id: "image.defaultSave", label: "Image save", currentValue: cfg.image.defaultSave, values: [...IMAGE_SAVE_MODES], description: "Where generated images are saved by default." },
303
- { id: "image.outputFormat", label: "Image format", currentValue: cfg.image.outputFormat, values: [...IMAGE_OUTPUT_FORMATS], description: "Generated image file format." },
304
- { id: "image.timeoutMs", label: "Image timeout", currentValue: String(cfg.image.timeoutMs), values: ["30000", "60000", "120000", "180000", "300000"], description: "Image request timeout in milliseconds." },
305
- { id: "debug", label: "Debug info", currentValue: "open", description: "Show Better OpenAI diagnostics.", submenu: (_value, done) => textPanel("Debug info", formatDebugStatus(ctx).split("\n"), () => done()) },
306
- { id: "config.path", label: "Config path", currentValue: cfg.configPath, description: `Project: ${cfg.projectConfigPath}\nGlobal: ${cfg.globalConfigPath}` },
307
- { id: "config.print", label: "Print config", currentValue: "open", description: "Show the selected raw config JSON.", submenu: (_value, done) => textPanel("Config", JSON.stringify(readRawConfig(cfg.configPath), null, 2).split("\n"), () => done()) }
319
+ {
320
+ id: "fast.enabled",
321
+ label: "Fast mode",
322
+ currentValue: String(desiredActive),
323
+ values: ["true", "false"],
324
+ description: `Request OpenAI fast mode. Activates for supported models: ${modelList(cfg.supportedModels)}.`,
325
+ },
326
+ {
327
+ id: "persistState",
328
+ label: "Persist fast state",
329
+ currentValue: String(cfg.persistState),
330
+ values: ["true", "false"],
331
+ description: "Remember fast-mode state across sessions.",
332
+ },
333
+ {
334
+ id: "footer.mode",
335
+ label: "Footer mode",
336
+ currentValue: cfg.footer.mode,
337
+ values: [...FOOTER_MODES],
338
+ description:
339
+ "replace = custom footer, status = pi footer plus status line, off = no Better OpenAI footer/status.",
340
+ },
341
+ {
342
+ id: "usage.enabled",
343
+ label: "Usage display",
344
+ currentValue: String(cfg.usage.enabled),
345
+ values: ["true", "false"],
346
+ description: "Fetch and display OpenAI subscription usage windows.",
347
+ },
348
+ {
349
+ id: "usage.refreshIntervalMs",
350
+ label: "Usage refresh",
351
+ currentValue: String(cfg.usage.refreshIntervalMs),
352
+ values: ["15000", "30000", "60000", "120000", "300000", "600000"],
353
+ description: "Usage refresh interval in milliseconds.",
354
+ },
355
+ {
356
+ id: "usage.showOnlyOnSubscriptionModels",
357
+ label: "Usage only on OAuth",
358
+ currentValue: String(cfg.usage.showOnlyOnSubscriptionModels),
359
+ values: ["true", "false"],
360
+ description: "Only show usage when the current OpenAI model uses subscription/OAuth auth.",
361
+ },
362
+ {
363
+ id: "usage.showResetTimes",
364
+ label: "Usage reset times",
365
+ currentValue: String(cfg.usage.showResetTimes),
366
+ values: ["true", "false"],
367
+ description: "Include compact reset countdowns and local reset times.",
368
+ },
369
+ {
370
+ id: "image.enabled",
371
+ label: "Image tool",
372
+ currentValue: String(cfg.image.enabled),
373
+ values: ["true", "false"],
374
+ description: "Allow the openai_image tool to make image requests.",
375
+ },
376
+ {
377
+ id: "image.defaultModel",
378
+ label: "Image model",
379
+ currentValue: cfg.image.defaultModel,
380
+ values: ["gpt-5.5", "gpt-5.4", "gpt-5.2", "gpt-5"],
381
+ description:
382
+ "Mainline model used for image generation when current model is not openai-codex.",
383
+ },
384
+ {
385
+ id: "image.defaultSave",
386
+ label: "Image save",
387
+ currentValue: cfg.image.defaultSave,
388
+ values: [...IMAGE_SAVE_MODES],
389
+ description: "Where generated images are saved by default.",
390
+ },
391
+ {
392
+ id: "image.outputFormat",
393
+ label: "Image format",
394
+ currentValue: cfg.image.outputFormat,
395
+ values: [...IMAGE_OUTPUT_FORMATS],
396
+ description: "Generated image file format.",
397
+ },
398
+ {
399
+ id: "image.timeoutMs",
400
+ label: "Image timeout",
401
+ currentValue: String(cfg.image.timeoutMs),
402
+ values: ["30000", "60000", "120000", "180000", "300000"],
403
+ description: "Image request timeout in milliseconds.",
404
+ },
405
+ {
406
+ id: "debug",
407
+ label: "Debug info",
408
+ currentValue: "open",
409
+ description: "Show Better OpenAI diagnostics.",
410
+ submenu: (_value, done) =>
411
+ textPanel("Debug info", formatDebugStatus(ctx).split("\n"), () => done()),
412
+ },
413
+ {
414
+ id: "config.path",
415
+ label: "Config path",
416
+ currentValue: cfg.configPath,
417
+ description: `Project: ${cfg.projectConfigPath}\nGlobal: ${cfg.globalConfigPath}`,
418
+ },
419
+ {
420
+ id: "config.print",
421
+ label: "Print config",
422
+ currentValue: "open",
423
+ description: "Show the selected raw config JSON.",
424
+ submenu: (_value, done) =>
425
+ textPanel(
426
+ "Config",
427
+ JSON.stringify(readRawConfig(cfg.configPath), null, 2).split("\n"),
428
+ () => done(),
429
+ ),
430
+ },
308
431
  ];
309
432
  }
310
433
 
@@ -333,7 +456,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
333
456
  } else if (id.startsWith("image.")) {
334
457
  const image = isRecord(current.image) ? current.image : {};
335
458
  const key = id.slice("image.".length);
336
- image[key] = key === "timeoutMs" ? num : rawValue === "true" ? true : rawValue === "false" ? false : rawValue;
459
+ image[key] =
460
+ key === "timeoutMs"
461
+ ? num
462
+ : rawValue === "true"
463
+ ? true
464
+ : rawValue === "false"
465
+ ? false
466
+ : rawValue;
337
467
  current.image = image;
338
468
  }
339
469
  writeConfig(cfg.configPath, current);
@@ -353,37 +483,51 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
353
483
  async function showSettingsPicker(ctx: ExtensionContext): Promise<void> {
354
484
  const [{ getSettingsListTheme }, { Container, SettingsList }] = await Promise.all([
355
485
  import("@mariozechner/pi-coding-agent"),
356
- import("@mariozechner/pi-tui")
486
+ import("@mariozechner/pi-tui"),
357
487
  ]);
358
488
  await ctx.ui.custom((tui, theme, _kb, done) => {
359
489
  const container = new Container();
360
- container.addChild(new (class {
361
- render(_width: number) {
362
- const cfg = config(ctx);
363
- return [theme.fg("accent", theme.bold("Better OpenAI Settings")), theme.fg("dim", cfg.configPath), ""];
364
- }
365
- invalidate() {}
366
- })());
490
+ container.addChild(
491
+ new (class {
492
+ render(_width: number) {
493
+ const cfg = config(ctx);
494
+ return [
495
+ theme.fg("accent", theme.bold("Better OpenAI Settings")),
496
+ theme.fg("dim", cfg.configPath),
497
+ "",
498
+ ];
499
+ }
500
+ invalidate() {}
501
+ })(),
502
+ );
367
503
  const settingsList = new SettingsList(
368
504
  buildSettingsItems(ctx, refresh(ctx)),
369
505
  13,
370
506
  getSettingsListTheme(),
371
507
  (id, newValue) => {
372
508
  writeSetting(ctx, id, newValue);
373
- settingsList.updateValue(id, buildSettingsItems(ctx, config(ctx)).find((item) => item.id === id)?.currentValue ?? newValue);
509
+ settingsList.updateValue(
510
+ id,
511
+ buildSettingsItems(ctx, config(ctx)).find((item) => item.id === id)?.currentValue ??
512
+ newValue,
513
+ );
374
514
  tui.requestRender();
375
515
  },
376
516
  () => done(undefined),
377
- { enableSearch: true }
517
+ { enableSearch: true },
378
518
  );
379
519
  container.addChild(settingsList);
380
520
  return {
381
- render(width: number) { return container.render(width); },
382
- invalidate() { container.invalidate(); },
521
+ render(width: number) {
522
+ return container.render(width);
523
+ },
524
+ invalidate() {
525
+ container.invalidate();
526
+ },
383
527
  handleInput(data: string) {
384
528
  settingsList.handleInput(data);
385
529
  tui.requestRender();
386
- }
530
+ },
387
531
  };
388
532
  });
389
533
  }
@@ -392,10 +536,9 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
392
536
  description: "Open Better OpenAI settings picker",
393
537
  handler: async (_args, ctx) => {
394
538
  await showSettingsPicker(ctx);
395
- }
539
+ },
396
540
  });
397
541
 
398
-
399
542
  registerOpenAIImage(pi, config);
400
543
 
401
544
  function installFooter(ctx: ExtensionContext): void {
@@ -438,18 +581,24 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
438
581
  if (totalCacheWrite) parts.push(`W${formatTokens(totalCacheWrite)}`);
439
582
 
440
583
  const usingSubscription = ctx.model ? ctx.modelRegistry.isUsingOAuth(ctx.model) : false;
441
- if (totalCost || usingSubscription) parts.push(`$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`);
584
+ if (totalCost || usingSubscription)
585
+ parts.push(`$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`);
442
586
 
443
587
  const contextUsage = ctx.getContextUsage();
444
588
  const contextWindow = contextUsage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
445
589
  const contextPercentValue = contextUsage?.percent ?? 0;
446
- const contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
447
- const contextDisplay = contextPercent === "?" ? `?/${formatTokens(contextWindow)} (auto)` : `${contextPercent}%/${formatTokens(contextWindow)} (auto)`;
448
- const contextText = contextPercentValue > 90
449
- ? theme.fg("error", contextDisplay)
450
- : contextPercentValue > 70
451
- ? theme.fg("warning", contextDisplay)
452
- : contextDisplay;
590
+ const contextPercent =
591
+ contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
592
+ const contextDisplay =
593
+ contextPercent === "?"
594
+ ? `?/${formatTokens(contextWindow)} (auto)`
595
+ : `${contextPercent}%/${formatTokens(contextWindow)} (auto)`;
596
+ const contextText =
597
+ contextPercentValue > 90
598
+ ? theme.fg("error", contextDisplay)
599
+ : contextPercentValue > 70
600
+ ? theme.fg("warning", contextDisplay)
601
+ : contextDisplay;
453
602
  parts.push(contextText);
454
603
 
455
604
  let usageLine: string | undefined;
@@ -467,12 +616,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
467
616
 
468
617
  const modelName = ctx.model?.id || "no-model";
469
618
  const thinkingLevel = pi.getThinkingLevel();
470
- const fastSuffix = active && supportsFast(ctx, config(ctx).supportedModels) ? " fast" : "";
619
+ const fastSuffix =
620
+ active && supportsFast(ctx, config(ctx).supportedModels) ? " fast" : "";
471
621
  let rightWithoutProvider = modelName;
472
622
  if (ctx.model?.reasoning) {
473
- rightWithoutProvider = thinkingLevel === "off"
474
- ? `${modelName}${fastSuffix} • thinking off`
475
- : `${modelName}${fastSuffix} • ${thinkingLevel}`;
623
+ rightWithoutProvider =
624
+ thinkingLevel === "off"
625
+ ? `${modelName}${fastSuffix} • thinking off`
626
+ : `${modelName}${fastSuffix} • ${thinkingLevel}`;
476
627
  } else if (fastSuffix) {
477
628
  rightWithoutProvider = `${modelName}${fastSuffix}`;
478
629
  }
@@ -492,7 +643,10 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
492
643
  const availableForRight = width - statsLeftWidth - 2;
493
644
  if (availableForRight > 0) {
494
645
  const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
495
- statsLine = statsLeft + " ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncatedRight))) + truncatedRight;
646
+ statsLine =
647
+ statsLeft +
648
+ " ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncatedRight))) +
649
+ truncatedRight;
496
650
  } else {
497
651
  statsLine = statsLeft;
498
652
  }
@@ -500,7 +654,7 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
500
654
 
501
655
  const lines = [
502
656
  truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "...")),
503
- theme.fg("dim", statsLeft) + theme.fg("dim", statsLine.slice(statsLeft.length))
657
+ theme.fg("dim", statsLeft) + theme.fg("dim", statsLine.slice(statsLeft.length)),
504
658
  ];
505
659
 
506
660
  if (usageLine) {
@@ -517,7 +671,7 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
517
671
  }
518
672
 
519
673
  return lines;
520
- }
674
+ },
521
675
  };
522
676
  });
523
677
  }
@@ -536,8 +690,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
536
690
  ctx.ui.setStatus(STATUS_KEY, undefined);
537
691
  return;
538
692
  }
539
- const fast = active && supportsFast(ctx, cfg.supportedModels) ? `${ctx.model?.id ?? "model"} fast` : undefined;
540
- const usage = usageSnapshot && cfg.usage.enabled && isOpenAISubscriptionModel(ctx, cfg) ? formatUsageSnapshot(usageSnapshot, cfg.usage) : undefined;
693
+ const fast =
694
+ active && supportsFast(ctx, cfg.supportedModels)
695
+ ? `${ctx.model?.id ?? "model"} fast`
696
+ : undefined;
697
+ const usage =
698
+ usageSnapshot && cfg.usage.enabled && isOpenAISubscriptionModel(ctx, cfg)
699
+ ? formatUsageSnapshot(usageSnapshot, cfg.usage)
700
+ : undefined;
541
701
  ctx.ui.setStatus(STATUS_KEY, [fast, usage].filter(Boolean).join(" | ") || undefined);
542
702
  }
543
703
 
@@ -546,14 +706,19 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
546
706
  desiredActive = nextConfig.persistState ? nextConfig.desiredActive : false;
547
707
  if (pi.getFlag(FLAG) === true) desiredActive = true;
548
708
  applyDesiredFastState(ctx, nextConfig);
549
- if (desiredActive !== nextConfig.desiredActive || active !== nextConfig.active) persist(nextConfig);
709
+ if (desiredActive !== nextConfig.desiredActive || active !== nextConfig.active)
710
+ persist(nextConfig);
550
711
  if (desiredActive && !active) {
551
- ctx.ui.notify(`Fast mode requested, but ${currentModelKey(ctx)} is unsupported. It will activate automatically when you switch to a supported model: ${modelList(nextConfig.supportedModels)}.`, "warning");
712
+ ctx.ui.notify(
713
+ `Fast mode requested, but ${currentModelKey(ctx)} is unsupported. It will activate automatically when you switch to a supported model: ${modelList(nextConfig.supportedModels)}.`,
714
+ "warning",
715
+ );
552
716
  }
553
717
  refreshFooterTotals(ctx);
554
718
  updateFooter(ctx);
555
719
  startUsageRefresh(ctx);
556
- if (active) ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
720
+ if (active)
721
+ ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
557
722
  });
558
723
 
559
724
  pi.on("turn_end", (_event, ctx) => {
@@ -578,7 +743,12 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
578
743
  applyDesiredFastState(ctx, cfg);
579
744
  if (active !== wasActive) {
580
745
  persist(cfg);
581
- ctx.ui.notify(active ? stateText(ctx, desiredActive, active, cfg.supportedModels) : `Fast mode inactive for unsupported model ${currentModelKey(ctx)}.`, active ? "info" : "warning");
746
+ ctx.ui.notify(
747
+ active
748
+ ? stateText(ctx, desiredActive, active, cfg.supportedModels)
749
+ : `Fast mode inactive for unsupported model ${currentModelKey(ctx)}.`,
750
+ active ? "info" : "warning",
751
+ );
582
752
  }
583
753
  updateFooter(ctx);
584
754
  void refreshUsage(ctx, event.model.id);
@@ -595,7 +765,8 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
595
765
 
596
766
  pi.on("before_provider_request", (event, ctx) => {
597
767
  const nextConfig = config(ctx);
598
- if (!active || !supportsFast(ctx, nextConfig.supportedModels) || !isRecord(event.payload)) return;
768
+ if (!active || !supportsFast(ctx, nextConfig.supportedModels) || !isRecord(event.payload))
769
+ return;
599
770
  lastInjectedAt = Date.now();
600
771
  lastInjectedModel = currentModelKey(ctx);
601
772
  lastInjectedTier = SERVICE_TIER;
@@ -620,5 +791,5 @@ export const _test = {
620
791
  formatPercent,
621
792
  formatUsageSnapshot,
622
793
  readCodexAuth,
623
- imageTest: _imageTest
794
+ imageTest: _imageTest,
624
795
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-openai",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Personal pi extension that improves OpenAI with fast mode, usage stats, and footer polish.",
5
5
  "keywords": [
6
6
  "fast",
@@ -29,9 +29,20 @@
29
29
  "access": "public"
30
30
  },
31
31
  "scripts": {
32
+ "check": "npm run typecheck && npm run lint && npm run format:check && npm test",
33
+ "format": "oxfmt .",
34
+ "format:check": "oxfmt --check .",
35
+ "lint": "oxlint --deny-warnings .",
36
+ "lint:fix": "oxlint --fix .",
32
37
  "test": "node tests/basic.mjs",
33
38
  "typecheck": "tsc --noEmit"
34
39
  },
40
+ "devDependencies": {
41
+ "@types/node": "^25.6.0",
42
+ "oxfmt": "^0.47.0",
43
+ "oxlint": "^1.62.0",
44
+ "typescript": "^6.0.3"
45
+ },
35
46
  "peerDependencies": {
36
47
  "@mariozechner/pi-coding-agent": ">=0.57.0",
37
48
  "@mariozechner/pi-tui": "*"
@@ -40,9 +51,5 @@
40
51
  "extensions": [
41
52
  "./index.ts"
42
53
  ]
43
- },
44
- "devDependencies": {
45
- "@types/node": "^25.6.0",
46
- "typescript": "^6.0.3"
47
54
  }
48
55
  }