@narumitw/pi-usage 0.59.0 → 0.60.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 +30 -25
- package/dist/index.ts +889 -338
- package/dist/index.ts.map +4 -4
- package/package.json +1 -1
- package/src/core.ts +28 -2
- package/src/format.ts +18 -8
- package/src/index.ts +14 -1
- package/src/providers/fireworks.ts +112 -0
- package/src/providers/zai.ts +109 -5
- package/src/query.ts +171 -175
- package/src/settings.ts +155 -8
- package/src/types.ts +50 -2
- package/src/usage-settings-ui.ts +88 -183
- package/src/usage-targets.ts +143 -0
- package/src/usage.ts +413 -99
package/src/settings.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { chmod, mkdir, open, rename, rm, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { basename, dirname, join } from "node:path";
|
|
5
5
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { isFireworksAccountId } from "./providers/fireworks.js";
|
|
7
|
+
import { isBoundedTargetId } from "./usage-targets.js";
|
|
7
8
|
|
|
8
9
|
export const USAGE_SETTINGS_FILE = "pi-usage.json";
|
|
9
10
|
export const MAX_USAGE_SETTINGS_BYTES = 64 * 1024;
|
|
@@ -11,12 +12,13 @@ export const MAX_USAGE_SETTINGS_BYTES = 64 * 1024;
|
|
|
11
12
|
export interface UsageSettings {
|
|
12
13
|
codexFastMode: boolean;
|
|
13
14
|
codexStatusResetCountdown: boolean;
|
|
14
|
-
|
|
15
|
+
selectedTargets: Record<string, string>;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const DEFAULT_USAGE_SETTINGS: Readonly<UsageSettings> = Object.freeze({
|
|
18
19
|
codexFastMode: false,
|
|
19
20
|
codexStatusResetCountdown: true,
|
|
21
|
+
selectedTargets: Object.freeze({}),
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
export interface UsageSettingsState {
|
|
@@ -27,6 +29,8 @@ export interface UsageSettingsState {
|
|
|
27
29
|
issue?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
export type UsageTargetPublicationCheck = () => Promise<void>;
|
|
33
|
+
|
|
30
34
|
export interface UsageSettingsRuntime {
|
|
31
35
|
get(): Readonly<UsageSettingsState>;
|
|
32
36
|
reload(signal?: AbortSignal): Promise<Readonly<UsageSettingsState>>;
|
|
@@ -34,6 +38,12 @@ export interface UsageSettingsRuntime {
|
|
|
34
38
|
patch: Partial<UsageSettings>,
|
|
35
39
|
signal?: AbortSignal,
|
|
36
40
|
): Promise<Readonly<UsageSettingsState>>;
|
|
41
|
+
updateSelectedTarget(
|
|
42
|
+
providerId: string,
|
|
43
|
+
targetId: string,
|
|
44
|
+
signal?: AbortSignal,
|
|
45
|
+
checkPublishedSelection?: UsageTargetPublicationCheck,
|
|
46
|
+
): Promise<Readonly<UsageSettingsState>>;
|
|
37
47
|
flush(): Promise<void>;
|
|
38
48
|
}
|
|
39
49
|
|
|
@@ -68,6 +78,12 @@ export function normalizeUsageSettings(value: unknown): UsageSettings | undefine
|
|
|
68
78
|
) {
|
|
69
79
|
return undefined;
|
|
70
80
|
}
|
|
81
|
+
const selectedTargets = normalizeSelectedTargets(value.selectedTargets);
|
|
82
|
+
if (Object.hasOwn(value, "selectedTargets") && !selectedTargets) return undefined;
|
|
83
|
+
const effectiveTargets = { ...(selectedTargets ?? {}) };
|
|
84
|
+
if (!effectiveTargets.fireworks && isFireworksAccountId(value.fireworksAccountId)) {
|
|
85
|
+
effectiveTargets.fireworks = value.fireworksAccountId;
|
|
86
|
+
}
|
|
71
87
|
return {
|
|
72
88
|
codexFastMode:
|
|
73
89
|
typeof value.codexFastMode === "boolean"
|
|
@@ -77,9 +93,7 @@ export function normalizeUsageSettings(value: unknown): UsageSettings | undefine
|
|
|
77
93
|
typeof value.codexStatusResetCountdown === "boolean"
|
|
78
94
|
? value.codexStatusResetCountdown
|
|
79
95
|
: DEFAULT_USAGE_SETTINGS.codexStatusResetCountdown,
|
|
80
|
-
|
|
81
|
-
? { fireworksAccountId: value.fireworksAccountId }
|
|
82
|
-
: {}),
|
|
96
|
+
selectedTargets: effectiveTargets,
|
|
83
97
|
};
|
|
84
98
|
}
|
|
85
99
|
|
|
@@ -169,6 +183,39 @@ export function createUsageSettingsRuntime(
|
|
|
169
183
|
state = saved;
|
|
170
184
|
return structuredClone(state);
|
|
171
185
|
}),
|
|
186
|
+
updateSelectedTarget: (providerId, targetId, signal, checkPublishedSelection) =>
|
|
187
|
+
enqueue(async () => {
|
|
188
|
+
const transaction = await saveUsageTargetSelection(
|
|
189
|
+
path,
|
|
190
|
+
providerId,
|
|
191
|
+
targetId,
|
|
192
|
+
operations,
|
|
193
|
+
signal,
|
|
194
|
+
);
|
|
195
|
+
try {
|
|
196
|
+
await checkPublishedSelection?.();
|
|
197
|
+
throwIfAborted(signal);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
try {
|
|
200
|
+
await restoreUsageSettingsState(
|
|
201
|
+
path,
|
|
202
|
+
transaction.saved,
|
|
203
|
+
transaction.previous,
|
|
204
|
+
operations,
|
|
205
|
+
);
|
|
206
|
+
state = transaction.previous;
|
|
207
|
+
} catch (rollbackError) {
|
|
208
|
+
state = await loadUsageSettings(path);
|
|
209
|
+
throw new AggregateError(
|
|
210
|
+
[error, rollbackError],
|
|
211
|
+
"Target selection changed after publication and pi-usage.json rollback failed",
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
state = transaction.saved;
|
|
217
|
+
return structuredClone(state);
|
|
218
|
+
}),
|
|
172
219
|
flush: () => queue,
|
|
173
220
|
};
|
|
174
221
|
}
|
|
@@ -178,16 +225,63 @@ async function saveUsageSettingsPatch(
|
|
|
178
225
|
patch: Partial<UsageSettings>,
|
|
179
226
|
operations: UsageSettingsFileOperations,
|
|
180
227
|
signal?: AbortSignal,
|
|
228
|
+
): Promise<UsageSettingsState> {
|
|
229
|
+
return saveUsageSettingsDocument(
|
|
230
|
+
path,
|
|
231
|
+
(document) => {
|
|
232
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
233
|
+
if (value === undefined) delete document[key];
|
|
234
|
+
else document[key] = value;
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
operations,
|
|
238
|
+
signal,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function saveUsageTargetSelection(
|
|
243
|
+
path: string,
|
|
244
|
+
providerId: string,
|
|
245
|
+
targetId: string,
|
|
246
|
+
operations: UsageSettingsFileOperations,
|
|
247
|
+
signal?: AbortSignal,
|
|
248
|
+
): Promise<{ saved: UsageSettingsState; previous: UsageSettingsState }> {
|
|
249
|
+
if (!isProviderId(providerId) || !isBoundedTargetId(targetId)) {
|
|
250
|
+
throw new Error("Refusing to save an invalid usage target selection");
|
|
251
|
+
}
|
|
252
|
+
const previous = await loadUsageSettings(path, signal);
|
|
253
|
+
const saved = await saveUsageSettingsDocument(
|
|
254
|
+
path,
|
|
255
|
+
(document) => {
|
|
256
|
+
document.selectedTargets = {
|
|
257
|
+
...(normalizeSelectedTargets(document.selectedTargets) ?? {}),
|
|
258
|
+
[providerId]: targetId,
|
|
259
|
+
};
|
|
260
|
+
if (providerId === "fireworks") delete document.fireworksAccountId;
|
|
261
|
+
},
|
|
262
|
+
operations,
|
|
263
|
+
signal,
|
|
264
|
+
previous,
|
|
265
|
+
);
|
|
266
|
+
return { saved, previous };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function saveUsageSettingsDocument(
|
|
270
|
+
path: string,
|
|
271
|
+
mutate: (document: Record<string, unknown>) => void,
|
|
272
|
+
operations: UsageSettingsFileOperations,
|
|
273
|
+
signal?: AbortSignal,
|
|
274
|
+
expected?: UsageSettingsState,
|
|
181
275
|
): Promise<UsageSettingsState> {
|
|
182
276
|
const latest = await loadUsageSettings(path, signal);
|
|
183
277
|
if (latest.kind === "invalid") {
|
|
184
278
|
throw new Error("Cannot overwrite an invalid pi-usage.json; repair it and reload first");
|
|
185
279
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (value === undefined) delete document[key];
|
|
189
|
-
else document[key] = value;
|
|
280
|
+
if (expected && !sameUsageSettingsDocument(latest, expected)) {
|
|
281
|
+
throw new Error("pi-usage.json changed while saving; retry the action");
|
|
190
282
|
}
|
|
283
|
+
const document = { ...latest.document };
|
|
284
|
+
mutate(document);
|
|
191
285
|
const settings = normalizeUsageSettings(document);
|
|
192
286
|
if (!settings) throw new Error("Refusing to save invalid pi-usage settings");
|
|
193
287
|
const directory = dirname(path);
|
|
@@ -218,6 +312,44 @@ async function saveUsageSettingsPatch(
|
|
|
218
312
|
return { kind: "loaded", path, settings, document };
|
|
219
313
|
}
|
|
220
314
|
|
|
315
|
+
async function restoreUsageSettingsState(
|
|
316
|
+
path: string,
|
|
317
|
+
published: UsageSettingsState,
|
|
318
|
+
previous: UsageSettingsState,
|
|
319
|
+
operations: UsageSettingsFileOperations,
|
|
320
|
+
): Promise<void> {
|
|
321
|
+
if (previous.kind === "missing") {
|
|
322
|
+
const current = await loadUsageSettings(path);
|
|
323
|
+
if (!sameUsageSettingsDocument(current, published)) {
|
|
324
|
+
throw new Error("pi-usage.json changed before target selection rollback");
|
|
325
|
+
}
|
|
326
|
+
await rm(path);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (previous.kind !== "loaded" || !previous.document) {
|
|
330
|
+
throw new Error("Cannot restore invalid prior pi-usage.json settings");
|
|
331
|
+
}
|
|
332
|
+
await saveUsageSettingsDocument(
|
|
333
|
+
path,
|
|
334
|
+
(document) => {
|
|
335
|
+
for (const key of Object.keys(document)) delete document[key];
|
|
336
|
+
Object.assign(document, previous.document);
|
|
337
|
+
},
|
|
338
|
+
operations,
|
|
339
|
+
undefined,
|
|
340
|
+
published,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function sameUsageSettingsDocument(
|
|
345
|
+
left: Pick<UsageSettingsState, "kind" | "document">,
|
|
346
|
+
right: Pick<UsageSettingsState, "kind" | "document">,
|
|
347
|
+
): boolean {
|
|
348
|
+
return (
|
|
349
|
+
left.kind === right.kind && JSON.stringify(left.document) === JSON.stringify(right.document)
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
221
353
|
async function chmodPrivate(path: string): Promise<void> {
|
|
222
354
|
await chmod(path, 0o600);
|
|
223
355
|
}
|
|
@@ -233,3 +365,18 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
233
365
|
function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
|
234
366
|
return error instanceof Error && "code" in error;
|
|
235
367
|
}
|
|
368
|
+
|
|
369
|
+
function normalizeSelectedTargets(value: unknown): Record<string, string> | undefined {
|
|
370
|
+
if (value === undefined) return {};
|
|
371
|
+
if (!isRecord(value)) return undefined;
|
|
372
|
+
const targets: Record<string, string> = {};
|
|
373
|
+
for (const [providerId, targetId] of Object.entries(value)) {
|
|
374
|
+
if (!isProviderId(providerId) || !isBoundedTargetId(targetId)) return undefined;
|
|
375
|
+
targets[providerId] = targetId;
|
|
376
|
+
}
|
|
377
|
+
return targets;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function isProviderId(value: string): boolean {
|
|
381
|
+
return /^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$/u.test(value);
|
|
382
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -53,23 +53,51 @@ export interface ResolvedUsageAuth {
|
|
|
53
53
|
fingerprint: string;
|
|
54
54
|
secrets: string[];
|
|
55
55
|
model: PiModel;
|
|
56
|
+
auth?: {
|
|
57
|
+
apiKey?: string;
|
|
58
|
+
headers?: Record<string, string | null>;
|
|
59
|
+
baseUrl?: string;
|
|
60
|
+
};
|
|
61
|
+
env?: Record<string, string>;
|
|
62
|
+
source?: string;
|
|
63
|
+
effectiveBaseUrl?: string;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
export interface UsageQuerySettings {
|
|
59
67
|
fireworksAccountId?: string;
|
|
60
68
|
}
|
|
61
69
|
|
|
70
|
+
export type UsageRequestGuard = () => Promise<void>;
|
|
71
|
+
|
|
72
|
+
export interface UsageProviderTarget {
|
|
73
|
+
id: string;
|
|
74
|
+
label: string;
|
|
75
|
+
description?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface UsageTargetResolver {
|
|
79
|
+
singularLabel: string;
|
|
80
|
+
pluralLabel: string;
|
|
81
|
+
list(
|
|
82
|
+
auth: ResolvedUsageAuth,
|
|
83
|
+
signal: AbortSignal,
|
|
84
|
+
timeoutMs: number,
|
|
85
|
+
guard: UsageRequestGuard,
|
|
86
|
+
): Promise<readonly UsageProviderTarget[]>;
|
|
87
|
+
}
|
|
88
|
+
|
|
62
89
|
export interface UsageProviderAdapter {
|
|
63
90
|
id: string;
|
|
64
91
|
displayName: string;
|
|
65
92
|
semantics: UsageSemantics;
|
|
66
93
|
publishesStatusline?: boolean;
|
|
94
|
+
targets?: UsageTargetResolver;
|
|
67
95
|
query(
|
|
68
96
|
auth: ResolvedUsageAuth,
|
|
69
97
|
signal: AbortSignal,
|
|
70
98
|
timeoutMs: number,
|
|
71
|
-
guard?:
|
|
72
|
-
|
|
99
|
+
guard?: UsageRequestGuard,
|
|
100
|
+
targetId?: string,
|
|
73
101
|
): Promise<UsageReport>;
|
|
74
102
|
}
|
|
75
103
|
|
|
@@ -81,6 +109,15 @@ export type ProviderUsageState =
|
|
|
81
109
|
status: "ready";
|
|
82
110
|
report: UsageReport;
|
|
83
111
|
}
|
|
112
|
+
| {
|
|
113
|
+
providerId: string;
|
|
114
|
+
providerName: string;
|
|
115
|
+
displayState: UsageDisplayState;
|
|
116
|
+
status: "selection-required";
|
|
117
|
+
singularLabel: string;
|
|
118
|
+
pluralLabel: string;
|
|
119
|
+
choices: readonly UsageProviderTarget[];
|
|
120
|
+
}
|
|
84
121
|
| {
|
|
85
122
|
providerId: string;
|
|
86
123
|
providerName: string;
|
|
@@ -157,6 +194,17 @@ export type ZaiQuotaPayload = {
|
|
|
157
194
|
data?: unknown;
|
|
158
195
|
};
|
|
159
196
|
|
|
197
|
+
export type ZaiSubscriptionPayload = {
|
|
198
|
+
code?: unknown;
|
|
199
|
+
success?: unknown;
|
|
200
|
+
data?: unknown;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export interface ZaiPlanInfo {
|
|
204
|
+
name: string;
|
|
205
|
+
renewsAt?: string;
|
|
206
|
+
}
|
|
207
|
+
|
|
160
208
|
export type KimiCodingUsagePayload = {
|
|
161
209
|
usage?: unknown;
|
|
162
210
|
limits?: unknown;
|
package/src/usage-settings-ui.ts
CHANGED
|
@@ -11,16 +11,12 @@ import {
|
|
|
11
11
|
Text,
|
|
12
12
|
} from "@earendil-works/pi-tui";
|
|
13
13
|
import { errorMessage } from "./core.js";
|
|
14
|
-
import {
|
|
15
|
-
import type { UsageSettings, UsageSettingsRuntime } from "./settings.js";
|
|
14
|
+
import type { UsageSettingsRuntime } from "./settings.js";
|
|
16
15
|
|
|
17
|
-
const AUTO = "Auto";
|
|
18
|
-
const EDIT = "Edit…";
|
|
19
16
|
const OFF = "Off";
|
|
20
17
|
const ON = "On";
|
|
21
18
|
|
|
22
|
-
type UsageSettingId =
|
|
23
|
-
type SettingsScreenResult = { changed: boolean; editFireworksAccount: boolean };
|
|
19
|
+
type UsageSettingId = "codexFastMode" | "codexStatusResetCountdown";
|
|
24
20
|
|
|
25
21
|
export async function showUsageSettings(
|
|
26
22
|
ctx: ExtensionCommandContext,
|
|
@@ -33,187 +29,96 @@ export async function showUsageSettings(
|
|
|
33
29
|
if (ctx.hasUI) ctx.ui.notify(`Edit settings manually: ${settingsRuntime.get().path}`, "info");
|
|
34
30
|
return false;
|
|
35
31
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
32
|
+
return (
|
|
33
|
+
(await ctx.ui.custom<boolean>((tui, theme, _keybindings, done) => {
|
|
34
|
+
const localController = new AbortController();
|
|
35
|
+
const signal = AbortSignal.any([parentSignal, localController.signal]);
|
|
36
|
+
let changed = false;
|
|
37
|
+
let closing = false;
|
|
38
|
+
let saveQueue = Promise.resolve();
|
|
39
|
+
const state = settingsRuntime.get();
|
|
40
|
+
const items: SettingItem[] = [
|
|
41
|
+
{
|
|
42
|
+
id: "codexFastMode",
|
|
43
|
+
label: "Codex Fast mode",
|
|
44
|
+
description: "Use faster Codex routing at increased plan allowance consumption.",
|
|
45
|
+
currentValue: state.settings.codexFastMode ? ON : OFF,
|
|
46
|
+
values: [OFF, ON],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "codexStatusResetCountdown",
|
|
50
|
+
label: "Codex reset countdown",
|
|
51
|
+
description: "Show time remaining until each Codex usage limit resets.",
|
|
52
|
+
currentValue: state.settings.codexStatusResetCountdown ? ON : OFF,
|
|
53
|
+
values: [OFF, ON],
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
const container = new Container();
|
|
57
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("pi-usage Settings")), 1, 1));
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
id: "codexStatusResetCountdown",
|
|
78
|
-
label: "Codex reset countdown",
|
|
79
|
-
description: "Show time remaining until each Codex usage limit resets.",
|
|
80
|
-
currentValue: state.settings.codexStatusResetCountdown ? ON : OFF,
|
|
81
|
-
values: [OFF, ON],
|
|
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
|
-
},
|
|
92
|
-
];
|
|
93
|
-
const container = new Container();
|
|
94
|
-
container.addChild(new Text(theme.fg("accent", theme.bold("pi-usage Settings")), 1, 1));
|
|
95
|
-
|
|
96
|
-
let settingsList: SettingsList;
|
|
97
|
-
const cancel = () => {
|
|
98
|
-
if (closing) return;
|
|
99
|
-
closing = true;
|
|
100
|
-
localController.abort();
|
|
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");
|
|
59
|
+
let settingsList: SettingsList;
|
|
60
|
+
const cancel = () => {
|
|
61
|
+
if (closing) return;
|
|
62
|
+
closing = true;
|
|
63
|
+
localController.abort();
|
|
64
|
+
done(changed);
|
|
65
|
+
};
|
|
66
|
+
const queueUpdate = (id: UsageSettingId, requested: boolean, display: string) => {
|
|
67
|
+
saveQueue = saveQueue.then(async () => {
|
|
68
|
+
const previous = settingsRuntime.get().settings[id];
|
|
69
|
+
if (settingsRuntime.get().kind === "invalid") {
|
|
70
|
+
settingsList.updateValue(id, previous ? ON : OFF);
|
|
71
|
+
if (!signal.aborted && isCurrent()) {
|
|
72
|
+
ctx.ui.notify("Repair pi-usage.json and reload before changing settings.", "error");
|
|
73
|
+
tui.requestRender();
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await settingsRuntime.update({ [id]: requested }, signal);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (signal.aborted || !isCurrent()) return;
|
|
81
|
+
settingsList.updateValue(id, previous ? ON : OFF);
|
|
82
|
+
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
114
83
|
tui.requestRender();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (previous !== requested) {
|
|
87
|
+
changed = true;
|
|
88
|
+
onApplied(id);
|
|
115
89
|
}
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
try {
|
|
119
|
-
await settingsRuntime.update({ [id]: requested }, signal);
|
|
120
|
-
} catch (error) {
|
|
121
90
|
if (signal.aborted || !isCurrent()) return;
|
|
122
|
-
settingsList.updateValue(id,
|
|
123
|
-
ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error");
|
|
91
|
+
settingsList.updateValue(id, display);
|
|
124
92
|
tui.requestRender();
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
items.length + 2,
|
|
139
|
-
getSettingsListTheme(),
|
|
140
|
-
(id, value) => {
|
|
141
|
-
if (closing || signal.aborted || !isCurrent()) 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
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const settingId = id as "codexFastMode" | "codexStatusResetCountdown";
|
|
153
|
-
queueUpdate(settingId, value !== OFF, value);
|
|
154
|
-
},
|
|
155
|
-
cancel,
|
|
156
|
-
);
|
|
157
|
-
container.addChild(settingsList);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
settingsList = new SettingsList(
|
|
96
|
+
items,
|
|
97
|
+
items.length + 2,
|
|
98
|
+
getSettingsListTheme(),
|
|
99
|
+
(id, value) => {
|
|
100
|
+
if (closing || signal.aborted || !isCurrent()) return;
|
|
101
|
+
queueUpdate(id as UsageSettingId, value !== OFF, value);
|
|
102
|
+
},
|
|
103
|
+
cancel,
|
|
104
|
+
);
|
|
105
|
+
container.addChild(settingsList);
|
|
158
106
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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;
|
|
107
|
+
parentSignal.addEventListener("abort", cancel, { once: true });
|
|
108
|
+
return {
|
|
109
|
+
render: (width: number) => container.render(width),
|
|
110
|
+
invalidate: () => container.invalidate(),
|
|
111
|
+
handleInput(data: string) {
|
|
112
|
+
if (closing) return;
|
|
113
|
+
if (matchesKey(data, Key.ctrl("c"))) cancel();
|
|
114
|
+
else settingsList.handleInput(data);
|
|
115
|
+
tui.requestRender();
|
|
116
|
+
},
|
|
117
|
+
dispose() {
|
|
118
|
+
localController.abort();
|
|
119
|
+
parentSignal.removeEventListener("abort", cancel);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
})) ?? false
|
|
123
|
+
);
|
|
219
124
|
}
|