@narumitw/pi-usage 0.57.0 → 0.58.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 +36 -2
- package/dist/index.ts +492 -101
- package/dist/index.ts.map +4 -4
- package/package.json +2 -1
- package/src/codex-fast-runtime.ts +34 -26
- package/src/format.ts +33 -1
- package/src/index.ts +7 -0
- package/src/providers/fireworks.ts +198 -0
- package/src/query.ts +141 -3
- package/src/settings.ts +16 -1
- package/src/types.ts +14 -0
- package/src/usage-settings-ui.ts +125 -33
- package/src/usage.ts +37 -11
package/src/usage-settings-ui.ts
CHANGED
|
@@ -11,12 +11,16 @@ import {
|
|
|
11
11
|
Text,
|
|
12
12
|
} from "@earendil-works/pi-tui";
|
|
13
13
|
import { errorMessage } from "./core.js";
|
|
14
|
+
import { isFireworksAccountId } from "./providers/fireworks.js";
|
|
14
15
|
import type { UsageSettings, UsageSettingsRuntime } from "./settings.js";
|
|
15
16
|
|
|
17
|
+
const AUTO = "Auto";
|
|
18
|
+
const EDIT = "Edit…";
|
|
16
19
|
const OFF = "Off";
|
|
17
20
|
const ON = "On";
|
|
18
21
|
|
|
19
22
|
type UsageSettingId = keyof UsageSettings;
|
|
23
|
+
type SettingsScreenResult = { changed: boolean; editFireworksAccount: boolean };
|
|
20
24
|
|
|
21
25
|
export async function showUsageSettings(
|
|
22
26
|
ctx: ExtensionCommandContext,
|
|
@@ -29,15 +33,38 @@ export async function showUsageSettings(
|
|
|
29
33
|
if (ctx.hasUI) ctx.ui.notify(`Edit settings manually: ${settingsRuntime.get().path}`, "info");
|
|
30
34
|
return false;
|
|
31
35
|
}
|
|
32
|
-
|
|
36
|
+
let changed = false;
|
|
37
|
+
while (!parentSignal.aborted && isCurrent()) {
|
|
38
|
+
const result = await showSettingsList(ctx, settingsRuntime, parentSignal, isCurrent, onApplied);
|
|
39
|
+
if (!result) return changed;
|
|
40
|
+
changed ||= result.changed;
|
|
41
|
+
if (!result.editFireworksAccount) return changed;
|
|
42
|
+
changed ||= await editFireworksAccount(
|
|
43
|
+
ctx,
|
|
44
|
+
settingsRuntime,
|
|
45
|
+
parentSignal,
|
|
46
|
+
isCurrent,
|
|
47
|
+
onApplied,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return changed;
|
|
51
|
+
}
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
async function showSettingsList(
|
|
54
|
+
ctx: ExtensionCommandContext,
|
|
55
|
+
settingsRuntime: UsageSettingsRuntime,
|
|
56
|
+
parentSignal: AbortSignal,
|
|
57
|
+
isCurrent: () => boolean,
|
|
58
|
+
onApplied: (id: UsageSettingId) => void,
|
|
59
|
+
): Promise<SettingsScreenResult | undefined> {
|
|
60
|
+
return ctx.ui.custom<SettingsScreenResult>((tui, theme, _keybindings, done) => {
|
|
35
61
|
const localController = new AbortController();
|
|
36
62
|
const signal = AbortSignal.any([parentSignal, localController.signal]);
|
|
37
63
|
let changed = false;
|
|
38
64
|
let closing = false;
|
|
39
65
|
let saveQueue = Promise.resolve();
|
|
40
66
|
const state = settingsRuntime.get();
|
|
67
|
+
const fireworksValue = state.settings.fireworksAccountId ?? AUTO;
|
|
41
68
|
const items: SettingItem[] = [
|
|
42
69
|
{
|
|
43
70
|
id: "codexFastMode",
|
|
@@ -53,6 +80,15 @@ export async function showUsageSettings(
|
|
|
53
80
|
currentValue: state.settings.codexStatusResetCountdown ? ON : OFF,
|
|
54
81
|
values: [OFF, ON],
|
|
55
82
|
},
|
|
83
|
+
{
|
|
84
|
+
id: "fireworksAccountId",
|
|
85
|
+
label: "Fireworks account",
|
|
86
|
+
description: "Select Edit to enter a visible account slug, or submit blank to clear it.",
|
|
87
|
+
currentValue: fireworksValue,
|
|
88
|
+
values: state.settings.fireworksAccountId
|
|
89
|
+
? [state.settings.fireworksAccountId, EDIT]
|
|
90
|
+
: [AUTO, EDIT],
|
|
91
|
+
},
|
|
56
92
|
];
|
|
57
93
|
const container = new Container();
|
|
58
94
|
container.addChild(new Text(theme.fg("accent", theme.bold("pi-usage Settings")), 1, 1));
|
|
@@ -62,7 +98,40 @@ export async function showUsageSettings(
|
|
|
62
98
|
if (closing) return;
|
|
63
99
|
closing = true;
|
|
64
100
|
localController.abort();
|
|
65
|
-
done(changed);
|
|
101
|
+
done({ changed, editFireworksAccount: false });
|
|
102
|
+
};
|
|
103
|
+
const queueUpdate = (
|
|
104
|
+
id: UsageSettingId,
|
|
105
|
+
requested: UsageSettings[UsageSettingId],
|
|
106
|
+
display: string,
|
|
107
|
+
) => {
|
|
108
|
+
saveQueue = saveQueue.then(async () => {
|
|
109
|
+
const previous = settingsRuntime.get().settings[id];
|
|
110
|
+
if (settingsRuntime.get().kind === "invalid") {
|
|
111
|
+
settingsList.updateValue(id, displaySetting(id, previous));
|
|
112
|
+
if (!signal.aborted && isCurrent()) {
|
|
113
|
+
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
114
|
+
tui.requestRender();
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
await settingsRuntime.update({ [id]: requested }, signal);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (signal.aborted || !isCurrent()) return;
|
|
122
|
+
settingsList.updateValue(id, displaySetting(id, previous));
|
|
123
|
+
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
124
|
+
tui.requestRender();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (previous !== requested) {
|
|
128
|
+
changed = true;
|
|
129
|
+
onApplied(id);
|
|
130
|
+
}
|
|
131
|
+
if (signal.aborted || !isCurrent()) return;
|
|
132
|
+
settingsList.updateValue(id, display);
|
|
133
|
+
tui.requestRender();
|
|
134
|
+
});
|
|
66
135
|
};
|
|
67
136
|
settingsList = new SettingsList(
|
|
68
137
|
items,
|
|
@@ -70,35 +139,18 @@ export async function showUsageSettings(
|
|
|
70
139
|
getSettingsListTheme(),
|
|
71
140
|
(id, value) => {
|
|
72
141
|
if (closing || signal.aborted || !isCurrent()) return;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
81
|
-
tui.requestRender();
|
|
82
|
-
}
|
|
83
|
-
return;
|
|
142
|
+
if (id === "fireworksAccountId") {
|
|
143
|
+
if (value === EDIT) {
|
|
144
|
+
saveQueue = saveQueue.then(() => {
|
|
145
|
+
if (closing || signal.aborted || !isCurrent()) return;
|
|
146
|
+
closing = true;
|
|
147
|
+
done({ changed, editFireworksAccount: true });
|
|
148
|
+
});
|
|
84
149
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
settingsList.updateValue(id, displayValue(previous));
|
|
90
|
-
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
91
|
-
tui.requestRender();
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (previous !== requested) {
|
|
95
|
-
changed = true;
|
|
96
|
-
onApplied(settingId);
|
|
97
|
-
}
|
|
98
|
-
if (signal.aborted || !isCurrent()) return;
|
|
99
|
-
settingsList.updateValue(id, displayValue(requested));
|
|
100
|
-
tui.requestRender();
|
|
101
|
-
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const settingId = id as "codexFastMode" | "codexStatusResetCountdown";
|
|
153
|
+
queueUpdate(settingId, value !== OFF, value);
|
|
102
154
|
},
|
|
103
155
|
cancel,
|
|
104
156
|
);
|
|
@@ -122,6 +174,46 @@ export async function showUsageSettings(
|
|
|
122
174
|
});
|
|
123
175
|
}
|
|
124
176
|
|
|
125
|
-
function
|
|
126
|
-
|
|
177
|
+
async function editFireworksAccount(
|
|
178
|
+
ctx: ExtensionCommandContext,
|
|
179
|
+
settingsRuntime: UsageSettingsRuntime,
|
|
180
|
+
signal: AbortSignal,
|
|
181
|
+
isCurrent: () => boolean,
|
|
182
|
+
onApplied: (id: UsageSettingId) => void,
|
|
183
|
+
): Promise<boolean> {
|
|
184
|
+
while (!signal.aborted && isCurrent()) {
|
|
185
|
+
const state = settingsRuntime.get();
|
|
186
|
+
if (state.kind === "invalid") {
|
|
187
|
+
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
const entered = await ctx.ui.input(
|
|
191
|
+
"Fireworks account slug · submit blank for Auto",
|
|
192
|
+
state.settings.fireworksAccountId ?? "Example: acme",
|
|
193
|
+
{ signal },
|
|
194
|
+
);
|
|
195
|
+
if (signal.aborted || !isCurrent() || entered === undefined) return false;
|
|
196
|
+
const normalized = entered.trim();
|
|
197
|
+
const requested = normalized || undefined;
|
|
198
|
+
if (requested !== undefined && !isFireworksAccountId(requested)) {
|
|
199
|
+
ctx.ui.notify("Enter a URL-safe Fireworks account slug.", "warning");
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (requested === state.settings.fireworksAccountId) return false;
|
|
203
|
+
try {
|
|
204
|
+
await settingsRuntime.update({ fireworksAccountId: requested }, signal);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (signal.aborted || !isCurrent()) return false;
|
|
207
|
+
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
onApplied("fireworksAccountId");
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function displaySetting(id: UsageSettingId, value: UsageSettings[UsageSettingId]): string {
|
|
217
|
+
if (id === "fireworksAccountId") return typeof value === "string" ? value : AUTO;
|
|
218
|
+
return value ? ON : OFF;
|
|
127
219
|
}
|
package/src/usage.ts
CHANGED
|
@@ -254,6 +254,10 @@ export default function usageExtension(
|
|
|
254
254
|
const expectedSessionGeneration = sessionGeneration;
|
|
255
255
|
const expectedSessionId = ctx.sessionManager.getSessionId();
|
|
256
256
|
const expectedModelIdentity = modelIdentity(ctx.model);
|
|
257
|
+
const expectedFireworksAccountId =
|
|
258
|
+
adapter.id === "fireworks" ? settingsRuntime.get().settings.fireworksAccountId : undefined;
|
|
259
|
+
const querySettings =
|
|
260
|
+
adapter.id === "fireworks" ? { fireworksAccountId: expectedFireworksAccountId } : undefined;
|
|
257
261
|
let auth: ResolvedUsageAuth | undefined;
|
|
258
262
|
try {
|
|
259
263
|
auth = await awaitWithDeadline(
|
|
@@ -277,11 +281,13 @@ export default function usageExtension(
|
|
|
277
281
|
},
|
|
278
282
|
};
|
|
279
283
|
}
|
|
280
|
-
const requiresRequestBoundaryGuard =
|
|
284
|
+
const requiresRequestBoundaryGuard = ["deepseek", "fireworks", "xai"].includes(adapter.id);
|
|
281
285
|
const requestContextChanged = () =>
|
|
282
286
|
expectedSessionGeneration !== sessionGeneration ||
|
|
283
287
|
ctx.sessionManager.getSessionId() !== expectedSessionId ||
|
|
284
|
-
modelIdentity(ctx.model) !== expectedModelIdentity
|
|
288
|
+
modelIdentity(ctx.model) !== expectedModelIdentity ||
|
|
289
|
+
(adapter.id === "fireworks" &&
|
|
290
|
+
settingsRuntime.get().settings.fireworksAccountId !== expectedFireworksAccountId);
|
|
285
291
|
if (requiresRequestBoundaryGuard && requestContextChanged()) throw abortError();
|
|
286
292
|
if (!auth) {
|
|
287
293
|
if (displayState === "current") {
|
|
@@ -298,11 +304,15 @@ export default function usageExtension(
|
|
|
298
304
|
authState: "unavailable",
|
|
299
305
|
};
|
|
300
306
|
}
|
|
307
|
+
const queryFingerprint =
|
|
308
|
+
adapter.id === "fireworks"
|
|
309
|
+
? `${auth.fingerprint}:account:${expectedFireworksAccountId ?? "auto"}`
|
|
310
|
+
: auth.fingerprint;
|
|
301
311
|
if (displayState === "current") {
|
|
302
|
-
transitionCurrentIdentity(`${adapter.id}:${
|
|
312
|
+
transitionCurrentIdentity(`${adapter.id}:${queryFingerprint}`, adapter.id);
|
|
303
313
|
}
|
|
304
314
|
|
|
305
|
-
const cached = !force ? cache.get(adapter.id,
|
|
315
|
+
const cached = !force ? cache.get(adapter.id, queryFingerprint) : undefined;
|
|
306
316
|
if (cached) {
|
|
307
317
|
return {
|
|
308
318
|
state: {
|
|
@@ -316,7 +326,7 @@ export default function usageExtension(
|
|
|
316
326
|
};
|
|
317
327
|
}
|
|
318
328
|
|
|
319
|
-
const failureKey = `${adapter.id}:${
|
|
329
|
+
const failureKey = `${adapter.id}:${queryFingerprint}`;
|
|
320
330
|
const previousFailure = failureBackoff.get(failureKey);
|
|
321
331
|
if (!force && previousFailure && previousFailure.until > Date.now()) {
|
|
322
332
|
return {
|
|
@@ -357,10 +367,17 @@ export default function usageExtension(
|
|
|
357
367
|
}
|
|
358
368
|
}
|
|
359
369
|
: undefined;
|
|
360
|
-
const report = await queryProviderUsage(
|
|
370
|
+
const report = await queryProviderUsage(
|
|
371
|
+
adapter,
|
|
372
|
+
auth,
|
|
373
|
+
signal,
|
|
374
|
+
remainingMs,
|
|
375
|
+
guard,
|
|
376
|
+
querySettings,
|
|
377
|
+
);
|
|
361
378
|
if (guard) await guard();
|
|
362
379
|
if (latestQueries.get(failureKey) === queryId) {
|
|
363
|
-
cache.set(adapter.id,
|
|
380
|
+
cache.set(adapter.id, queryFingerprint, report);
|
|
364
381
|
failureBackoff.delete(failureKey);
|
|
365
382
|
}
|
|
366
383
|
return {
|
|
@@ -1103,7 +1120,7 @@ export default function usageExtension(
|
|
|
1103
1120
|
}
|
|
1104
1121
|
},
|
|
1105
1122
|
});
|
|
1106
|
-
pi.on("session_start", (_event, ctx) => {
|
|
1123
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
1107
1124
|
sessionGeneration += 1;
|
|
1108
1125
|
statusGeneration += 1;
|
|
1109
1126
|
clearStatusTimers();
|
|
@@ -1111,7 +1128,13 @@ export default function usageExtension(
|
|
|
1111
1128
|
activeControllers.clear();
|
|
1112
1129
|
statusController = undefined;
|
|
1113
1130
|
sessionActive = true;
|
|
1114
|
-
|
|
1131
|
+
const ownerGeneration = sessionGeneration;
|
|
1132
|
+
try {
|
|
1133
|
+
await fastRuntime.prepareSession(ctx);
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
if (isStaleExtensionContextError(error) || ownerGeneration !== sessionGeneration) return;
|
|
1136
|
+
throw error;
|
|
1137
|
+
}
|
|
1115
1138
|
});
|
|
1116
1139
|
pi.on("session_tree", (_event, ctx) => {
|
|
1117
1140
|
startStatusRefresh(ctx, ctx.model, false);
|
|
@@ -1137,7 +1160,10 @@ export default function usageExtension(
|
|
|
1137
1160
|
safeSetStatus(ctx, undefined);
|
|
1138
1161
|
});
|
|
1139
1162
|
|
|
1140
|
-
fastRuntime = registerCodexFastMode(
|
|
1141
|
-
|
|
1163
|
+
fastRuntime = registerCodexFastMode(
|
|
1164
|
+
pi,
|
|
1165
|
+
settingsRuntime,
|
|
1166
|
+
(ctx) => startStatusRefresh(ctx, ctx.model, false),
|
|
1167
|
+
{ registerSessionStart: false },
|
|
1142
1168
|
);
|
|
1143
1169
|
}
|