pi-better-openai 0.1.6 → 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 +1 -1
- package/{extensions/pi-better-openai.ts → index.ts} +254 -82
- package/package.json +17 -4
- package/{extensions → src}/config.ts +62 -27
- package/{extensions → src}/format.ts +12 -4
- package/{extensions → src}/image.ts +246 -82
- package/{extensions → src}/usage.ts +54 -15
- /package/{extensions → src}/identity.ts +0 -0
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* enabled and the selected model is in the configured allow-list.
|
|
6
6
|
*/
|
|
7
7
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
8
|
-
import { CONFIG_BASENAME, STATUS_KEY } from "./identity.ts";
|
|
9
|
-
import { formatTokens, sanitizeStatusText, truncateToWidth, visibleWidth } from "./format.ts";
|
|
8
|
+
import { CONFIG_BASENAME, STATUS_KEY } from "./src/identity.ts";
|
|
9
|
+
import { formatTokens, sanitizeStatusText, truncateToWidth, visibleWidth } from "./src/format.ts";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_CONFIG,
|
|
12
12
|
DEFAULT_IMAGE_CONFIG,
|
|
@@ -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,8 +23,8 @@ import {
|
|
|
24
23
|
parseModels,
|
|
25
24
|
readRawConfig,
|
|
26
25
|
resolveConfig,
|
|
27
|
-
writeConfig
|
|
28
|
-
} from "./config.ts";
|
|
26
|
+
writeConfig,
|
|
27
|
+
} from "./src/config.ts";
|
|
29
28
|
import {
|
|
30
29
|
AUTH_FILE,
|
|
31
30
|
type UsageSnapshot,
|
|
@@ -34,9 +33,9 @@ import {
|
|
|
34
33
|
formatUsageSnapshot,
|
|
35
34
|
parseUsageSnapshot,
|
|
36
35
|
readCodexAuth,
|
|
37
|
-
requestCodexUsage
|
|
38
|
-
} from "./usage.ts";
|
|
39
|
-
import { registerOpenAIImage, _imageTest } from "./image.ts";
|
|
36
|
+
requestCodexUsage,
|
|
37
|
+
} from "./src/usage.ts";
|
|
38
|
+
import { registerOpenAIImage, _imageTest } from "./src/image.ts";
|
|
40
39
|
|
|
41
40
|
const COMMAND = "fast";
|
|
42
41
|
const OPENAI_STATUS_COMMAND = "openai-usage";
|
|
@@ -49,7 +48,10 @@ type SettingsPickerItem = {
|
|
|
49
48
|
description?: string;
|
|
50
49
|
currentValue: string;
|
|
51
50
|
values?: string[];
|
|
52
|
-
submenu?: (
|
|
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 {
|
|
@@ -57,8 +59,11 @@ function currentModelKey(ctx: ExtensionContext): string {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
function supportsFast(ctx: ExtensionContext, supportedModels: SupportedModel[]): boolean {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
const current = ctx.model;
|
|
63
|
+
if (!current) return false;
|
|
64
|
+
return supportedModels.some(
|
|
65
|
+
(model) => model.provider === current.provider && model.id === current.id,
|
|
66
|
+
);
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
function modelList(supportedModels: SupportedModel[]): string {
|
|
@@ -67,7 +72,12 @@ function modelList(supportedModels: SupportedModel[]): string {
|
|
|
67
72
|
: "none configured";
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
function stateText(
|
|
75
|
+
function stateText(
|
|
76
|
+
ctx: ExtensionContext,
|
|
77
|
+
desiredActive: boolean,
|
|
78
|
+
active: boolean,
|
|
79
|
+
supportedModels: SupportedModel[],
|
|
80
|
+
): string {
|
|
71
81
|
const model = currentModelKey(ctx);
|
|
72
82
|
if (active) return `Fast mode is on for ${model}.`;
|
|
73
83
|
if (desiredActive) {
|
|
@@ -77,7 +87,8 @@ function stateText(ctx: ExtensionContext, desiredActive: boolean, active: boolea
|
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
function isOpenAISubscriptionModel(ctx: ExtensionContext, cfg: ResolvedConfig): boolean {
|
|
80
|
-
if (!ctx.model || (ctx.model.provider !== "openai" && ctx.model.provider !== "openai-codex"))
|
|
90
|
+
if (!ctx.model || (ctx.model.provider !== "openai" && ctx.model.provider !== "openai-codex"))
|
|
91
|
+
return false;
|
|
81
92
|
return !cfg.usage.showOnlyOnSubscriptionModels || ctx.modelRegistry.isUsingOAuth(ctx.model);
|
|
82
93
|
}
|
|
83
94
|
|
|
@@ -113,7 +124,11 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
113
124
|
function persist(nextConfig: ResolvedConfig): void {
|
|
114
125
|
cachedConfig = { ...nextConfig, active, desiredActive };
|
|
115
126
|
if (!nextConfig.persistState) return;
|
|
116
|
-
writeConfig(nextConfig.configPath, {
|
|
127
|
+
writeConfig(nextConfig.configPath, {
|
|
128
|
+
...readRawConfig(nextConfig.configPath),
|
|
129
|
+
active,
|
|
130
|
+
desiredActive,
|
|
131
|
+
});
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
function applyDesiredFastState(ctx: ExtensionContext, cfg = config(ctx)): void {
|
|
@@ -127,13 +142,20 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
127
142
|
persist(nextConfig);
|
|
128
143
|
updateFooter(ctx);
|
|
129
144
|
if (next && !active) {
|
|
130
|
-
ctx.ui.notify(
|
|
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
|
+
);
|
|
131
149
|
return;
|
|
132
150
|
}
|
|
133
151
|
ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
|
|
134
152
|
}
|
|
135
153
|
|
|
136
|
-
async function refreshUsage(
|
|
154
|
+
async function refreshUsage(
|
|
155
|
+
ctx: ExtensionContext,
|
|
156
|
+
modelId = ctx.model?.id,
|
|
157
|
+
options?: { notify?: boolean },
|
|
158
|
+
): Promise<void> {
|
|
137
159
|
if (shuttingDown || !ctx.hasUI) return;
|
|
138
160
|
if (usageRefreshInFlight) {
|
|
139
161
|
queuedUsageRefresh = { ctx, modelId, notify: queuedUsageRefresh?.notify || options?.notify };
|
|
@@ -163,7 +185,8 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
163
185
|
usageUpdatedAt = usageSnapshot ? Date.now() : undefined;
|
|
164
186
|
usageError = data ? undefined : `Missing openai-codex OAuth credentials in ${AUTH_FILE}.`;
|
|
165
187
|
if (!shuttingDown) updateFooter(ctx);
|
|
166
|
-
if (!shuttingDown && options?.notify)
|
|
188
|
+
if (!shuttingDown && options?.notify)
|
|
189
|
+
ctx.ui.notify(formatUsageStatus(ctx), usageSnapshot ? "info" : "warning");
|
|
167
190
|
} catch (error) {
|
|
168
191
|
if (shuttingDown) return;
|
|
169
192
|
usageError = error instanceof Error ? error.message : String(error);
|
|
@@ -215,25 +238,27 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
215
238
|
`Last successful update: ${usageUpdatedAt ? new Date(usageUpdatedAt).toLocaleTimeString() : "never"}`,
|
|
216
239
|
`Last error: ${usageError ?? "none"}`,
|
|
217
240
|
`Refresh interval: ${cfg.usage.refreshIntervalMs}ms`,
|
|
218
|
-
`Endpoint: https://chatgpt.com/backend-api/wham/usage
|
|
241
|
+
`Endpoint: https://chatgpt.com/backend-api/wham/usage`,
|
|
219
242
|
].join("\n");
|
|
220
243
|
}
|
|
221
244
|
|
|
222
245
|
function formatUsageStatus(ctx: ExtensionContext): string {
|
|
223
246
|
const cfg = config(ctx);
|
|
224
247
|
if (!cfg.usage.enabled) return "Usage display is disabled.";
|
|
225
|
-
if (!isOpenAISubscriptionModel(ctx, cfg))
|
|
248
|
+
if (!isOpenAISubscriptionModel(ctx, cfg))
|
|
249
|
+
return "Usage hidden: current model is not an OpenAI subscription model.";
|
|
226
250
|
if (!usageSnapshot) return `Usage unavailable${usageError ? `: ${usageError}` : "."}`;
|
|
227
|
-
const stale =
|
|
228
|
-
|
|
229
|
-
|
|
251
|
+
const stale =
|
|
252
|
+
usageUpdatedAt && Date.now() - usageUpdatedAt > cfg.usage.refreshIntervalMs * 2
|
|
253
|
+
? ` | stale ${formatResetCountdown((Date.now() - usageUpdatedAt) / 1000)}`
|
|
254
|
+
: "";
|
|
230
255
|
return `${formatUsageSnapshot(usageSnapshot, cfg.usage)}${stale}`;
|
|
231
256
|
}
|
|
232
257
|
|
|
233
258
|
pi.registerFlag(FLAG, {
|
|
234
259
|
description: "Start with OpenAI fast mode enabled (service_tier=priority)",
|
|
235
260
|
type: "boolean",
|
|
236
|
-
default: false
|
|
261
|
+
default: false,
|
|
237
262
|
});
|
|
238
263
|
|
|
239
264
|
function formatDebugStatus(ctx: ExtensionContext): string {
|
|
@@ -246,10 +271,12 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
246
271
|
`Configured service_tier: ${SERVICE_TIER}`,
|
|
247
272
|
`Last injected: ${lastInjectedAt ? `${new Date(lastInjectedAt).toLocaleTimeString()} (${lastInjectedModel}, ${lastInjectedTier})` : "never"}`,
|
|
248
273
|
`Footer mode: ${cfg.footer.mode}`,
|
|
249
|
-
|
|
274
|
+
"",
|
|
275
|
+
formatUsageDebug(ctx),
|
|
276
|
+
"",
|
|
250
277
|
`Image enabled: ${cfg.image.enabled}`,
|
|
251
278
|
`Image default save: ${cfg.image.defaultSave}`,
|
|
252
|
-
`Config: ${cfg.configPath}
|
|
279
|
+
`Config: ${cfg.configPath}`,
|
|
253
280
|
].join("\n");
|
|
254
281
|
}
|
|
255
282
|
|
|
@@ -264,14 +291,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
264
291
|
const arg = args.trim().toLowerCase();
|
|
265
292
|
if (!arg) return setActive(ctx, !desiredActive);
|
|
266
293
|
ctx.ui.notify("Usage: /fast", "error");
|
|
267
|
-
}
|
|
294
|
+
},
|
|
268
295
|
});
|
|
269
296
|
|
|
270
297
|
pi.registerCommand(OPENAI_STATUS_COMMAND, {
|
|
271
298
|
description: "Show OpenAI subscription usage status",
|
|
272
299
|
handler: async (_args, ctx) => {
|
|
273
300
|
ctx.ui.notify(formatOpenAIStatus(ctx), "info");
|
|
274
|
-
}
|
|
301
|
+
},
|
|
275
302
|
});
|
|
276
303
|
|
|
277
304
|
function textPanel(title: string, lines: string[], done: () => void) {
|
|
@@ -283,27 +310,124 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
283
310
|
invalidate() {},
|
|
284
311
|
handleInput(data: string) {
|
|
285
312
|
if (data.includes("\x1b") || data === "escape" || data === "q" || data === "\x03") done();
|
|
286
|
-
}
|
|
313
|
+
},
|
|
287
314
|
};
|
|
288
315
|
}
|
|
289
316
|
|
|
290
317
|
function buildSettingsItems(ctx: ExtensionContext, cfg: ResolvedConfig): SettingsPickerItem[] {
|
|
291
318
|
return [
|
|
292
|
-
{
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
{
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
{
|
|
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
|
+
},
|
|
307
431
|
];
|
|
308
432
|
}
|
|
309
433
|
|
|
@@ -332,7 +456,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
332
456
|
} else if (id.startsWith("image.")) {
|
|
333
457
|
const image = isRecord(current.image) ? current.image : {};
|
|
334
458
|
const key = id.slice("image.".length);
|
|
335
|
-
image[key] =
|
|
459
|
+
image[key] =
|
|
460
|
+
key === "timeoutMs"
|
|
461
|
+
? num
|
|
462
|
+
: rawValue === "true"
|
|
463
|
+
? true
|
|
464
|
+
: rawValue === "false"
|
|
465
|
+
? false
|
|
466
|
+
: rawValue;
|
|
336
467
|
current.image = image;
|
|
337
468
|
}
|
|
338
469
|
writeConfig(cfg.configPath, current);
|
|
@@ -352,37 +483,51 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
352
483
|
async function showSettingsPicker(ctx: ExtensionContext): Promise<void> {
|
|
353
484
|
const [{ getSettingsListTheme }, { Container, SettingsList }] = await Promise.all([
|
|
354
485
|
import("@mariozechner/pi-coding-agent"),
|
|
355
|
-
import("@mariozechner/pi-tui")
|
|
486
|
+
import("@mariozechner/pi-tui"),
|
|
356
487
|
]);
|
|
357
488
|
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
358
489
|
const container = new Container();
|
|
359
|
-
container.addChild(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
+
);
|
|
366
503
|
const settingsList = new SettingsList(
|
|
367
504
|
buildSettingsItems(ctx, refresh(ctx)),
|
|
368
505
|
13,
|
|
369
506
|
getSettingsListTheme(),
|
|
370
507
|
(id, newValue) => {
|
|
371
508
|
writeSetting(ctx, id, newValue);
|
|
372
|
-
settingsList.updateValue(
|
|
509
|
+
settingsList.updateValue(
|
|
510
|
+
id,
|
|
511
|
+
buildSettingsItems(ctx, config(ctx)).find((item) => item.id === id)?.currentValue ??
|
|
512
|
+
newValue,
|
|
513
|
+
);
|
|
373
514
|
tui.requestRender();
|
|
374
515
|
},
|
|
375
516
|
() => done(undefined),
|
|
376
|
-
{ enableSearch: true }
|
|
517
|
+
{ enableSearch: true },
|
|
377
518
|
);
|
|
378
519
|
container.addChild(settingsList);
|
|
379
520
|
return {
|
|
380
|
-
render(width: number) {
|
|
381
|
-
|
|
521
|
+
render(width: number) {
|
|
522
|
+
return container.render(width);
|
|
523
|
+
},
|
|
524
|
+
invalidate() {
|
|
525
|
+
container.invalidate();
|
|
526
|
+
},
|
|
382
527
|
handleInput(data: string) {
|
|
383
528
|
settingsList.handleInput(data);
|
|
384
529
|
tui.requestRender();
|
|
385
|
-
}
|
|
530
|
+
},
|
|
386
531
|
};
|
|
387
532
|
});
|
|
388
533
|
}
|
|
@@ -391,10 +536,9 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
391
536
|
description: "Open Better OpenAI settings picker",
|
|
392
537
|
handler: async (_args, ctx) => {
|
|
393
538
|
await showSettingsPicker(ctx);
|
|
394
|
-
}
|
|
539
|
+
},
|
|
395
540
|
});
|
|
396
541
|
|
|
397
|
-
|
|
398
542
|
registerOpenAIImage(pi, config);
|
|
399
543
|
|
|
400
544
|
function installFooter(ctx: ExtensionContext): void {
|
|
@@ -437,18 +581,24 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
437
581
|
if (totalCacheWrite) parts.push(`W${formatTokens(totalCacheWrite)}`);
|
|
438
582
|
|
|
439
583
|
const usingSubscription = ctx.model ? ctx.modelRegistry.isUsingOAuth(ctx.model) : false;
|
|
440
|
-
if (totalCost || usingSubscription)
|
|
584
|
+
if (totalCost || usingSubscription)
|
|
585
|
+
parts.push(`$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`);
|
|
441
586
|
|
|
442
587
|
const contextUsage = ctx.getContextUsage();
|
|
443
588
|
const contextWindow = contextUsage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
|
|
444
589
|
const contextPercentValue = contextUsage?.percent ?? 0;
|
|
445
|
-
const contextPercent =
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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;
|
|
452
602
|
parts.push(contextText);
|
|
453
603
|
|
|
454
604
|
let usageLine: string | undefined;
|
|
@@ -466,12 +616,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
466
616
|
|
|
467
617
|
const modelName = ctx.model?.id || "no-model";
|
|
468
618
|
const thinkingLevel = pi.getThinkingLevel();
|
|
469
|
-
const fastSuffix =
|
|
619
|
+
const fastSuffix =
|
|
620
|
+
active && supportsFast(ctx, config(ctx).supportedModels) ? " fast" : "";
|
|
470
621
|
let rightWithoutProvider = modelName;
|
|
471
622
|
if (ctx.model?.reasoning) {
|
|
472
|
-
rightWithoutProvider =
|
|
473
|
-
|
|
474
|
-
|
|
623
|
+
rightWithoutProvider =
|
|
624
|
+
thinkingLevel === "off"
|
|
625
|
+
? `${modelName}${fastSuffix} • thinking off`
|
|
626
|
+
: `${modelName}${fastSuffix} • ${thinkingLevel}`;
|
|
475
627
|
} else if (fastSuffix) {
|
|
476
628
|
rightWithoutProvider = `${modelName}${fastSuffix}`;
|
|
477
629
|
}
|
|
@@ -491,7 +643,10 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
491
643
|
const availableForRight = width - statsLeftWidth - 2;
|
|
492
644
|
if (availableForRight > 0) {
|
|
493
645
|
const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
|
|
494
|
-
statsLine =
|
|
646
|
+
statsLine =
|
|
647
|
+
statsLeft +
|
|
648
|
+
" ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncatedRight))) +
|
|
649
|
+
truncatedRight;
|
|
495
650
|
} else {
|
|
496
651
|
statsLine = statsLeft;
|
|
497
652
|
}
|
|
@@ -499,7 +654,7 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
499
654
|
|
|
500
655
|
const lines = [
|
|
501
656
|
truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "...")),
|
|
502
|
-
theme.fg("dim", statsLeft) + theme.fg("dim", statsLine.slice(statsLeft.length))
|
|
657
|
+
theme.fg("dim", statsLeft) + theme.fg("dim", statsLine.slice(statsLeft.length)),
|
|
503
658
|
];
|
|
504
659
|
|
|
505
660
|
if (usageLine) {
|
|
@@ -516,7 +671,7 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
516
671
|
}
|
|
517
672
|
|
|
518
673
|
return lines;
|
|
519
|
-
}
|
|
674
|
+
},
|
|
520
675
|
};
|
|
521
676
|
});
|
|
522
677
|
}
|
|
@@ -535,8 +690,14 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
535
690
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
536
691
|
return;
|
|
537
692
|
}
|
|
538
|
-
const fast =
|
|
539
|
-
|
|
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;
|
|
540
701
|
ctx.ui.setStatus(STATUS_KEY, [fast, usage].filter(Boolean).join(" | ") || undefined);
|
|
541
702
|
}
|
|
542
703
|
|
|
@@ -545,14 +706,19 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
545
706
|
desiredActive = nextConfig.persistState ? nextConfig.desiredActive : false;
|
|
546
707
|
if (pi.getFlag(FLAG) === true) desiredActive = true;
|
|
547
708
|
applyDesiredFastState(ctx, nextConfig);
|
|
548
|
-
if (desiredActive !== nextConfig.desiredActive || active !== nextConfig.active)
|
|
709
|
+
if (desiredActive !== nextConfig.desiredActive || active !== nextConfig.active)
|
|
710
|
+
persist(nextConfig);
|
|
549
711
|
if (desiredActive && !active) {
|
|
550
|
-
ctx.ui.notify(
|
|
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
|
+
);
|
|
551
716
|
}
|
|
552
717
|
refreshFooterTotals(ctx);
|
|
553
718
|
updateFooter(ctx);
|
|
554
719
|
startUsageRefresh(ctx);
|
|
555
|
-
if (active)
|
|
720
|
+
if (active)
|
|
721
|
+
ctx.ui.notify(stateText(ctx, desiredActive, active, nextConfig.supportedModels), "info");
|
|
556
722
|
});
|
|
557
723
|
|
|
558
724
|
pi.on("turn_end", (_event, ctx) => {
|
|
@@ -577,7 +743,12 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
577
743
|
applyDesiredFastState(ctx, cfg);
|
|
578
744
|
if (active !== wasActive) {
|
|
579
745
|
persist(cfg);
|
|
580
|
-
ctx.ui.notify(
|
|
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
|
+
);
|
|
581
752
|
}
|
|
582
753
|
updateFooter(ctx);
|
|
583
754
|
void refreshUsage(ctx, event.model.id);
|
|
@@ -594,7 +765,8 @@ export default function betterOpenAI(pi: ExtensionAPI): void {
|
|
|
594
765
|
|
|
595
766
|
pi.on("before_provider_request", (event, ctx) => {
|
|
596
767
|
const nextConfig = config(ctx);
|
|
597
|
-
if (!active || !supportsFast(ctx, nextConfig.supportedModels) || !isRecord(event.payload))
|
|
768
|
+
if (!active || !supportsFast(ctx, nextConfig.supportedModels) || !isRecord(event.payload))
|
|
769
|
+
return;
|
|
598
770
|
lastInjectedAt = Date.now();
|
|
599
771
|
lastInjectedModel = currentModelKey(ctx);
|
|
600
772
|
lastInjectedTier = SERVICE_TIER;
|
|
@@ -619,5 +791,5 @@ export const _test = {
|
|
|
619
791
|
formatPercent,
|
|
620
792
|
formatUsageSnapshot,
|
|
621
793
|
readCodexAuth,
|
|
622
|
-
imageTest: _imageTest
|
|
794
|
+
imageTest: _imageTest,
|
|
623
795
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-better-openai",
|
|
3
|
-
"version": "0.1.
|
|
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",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"url": "git+https://github.com/mattleong/pi-better-openai.git"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"
|
|
22
|
+
"index.ts",
|
|
23
|
+
"src/",
|
|
23
24
|
"README.md",
|
|
24
25
|
"LICENSE"
|
|
25
26
|
],
|
|
@@ -28,7 +29,19 @@
|
|
|
28
29
|
"access": "public"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
|
-
"
|
|
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 .",
|
|
37
|
+
"test": "node tests/basic.mjs",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^25.6.0",
|
|
42
|
+
"oxfmt": "^0.47.0",
|
|
43
|
+
"oxlint": "^1.62.0",
|
|
44
|
+
"typescript": "^6.0.3"
|
|
32
45
|
},
|
|
33
46
|
"peerDependencies": {
|
|
34
47
|
"@mariozechner/pi-coding-agent": ">=0.57.0",
|
|
@@ -36,7 +49,7 @@
|
|
|
36
49
|
},
|
|
37
50
|
"pi": {
|
|
38
51
|
"extensions": [
|
|
39
|
-
"./
|
|
52
|
+
"./index.ts"
|
|
40
53
|
]
|
|
41
54
|
}
|
|
42
55
|
}
|