@marckrenn/pi-sub-bar 1.0.4 → 1.0.5
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/CHANGELOG.md +10 -0
- package/index.ts +78 -35
- package/node_modules/@marckrenn/pi-sub-core/CHANGELOG.md +9 -0
- package/node_modules/@marckrenn/pi-sub-core/index.ts +72 -21
- package/node_modules/@marckrenn/pi-sub-core/package.json +2 -2
- package/node_modules/@marckrenn/pi-sub-core/src/cache.ts +2 -0
- package/node_modules/@marckrenn/pi-sub-core/src/usage/controller.ts +22 -7
- package/node_modules/@marckrenn/pi-sub-shared/package.json +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @marckrenn/pi-sub-bar
|
|
2
2
|
|
|
3
|
+
## 1.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#35](https://github.com/marckrenn/pi-sub/pull/35) [`59e2b45`](https://github.com/marckrenn/pi-sub/commit/59e2b456e0e5c41479dccedcef93f9175cc4aa55) Thanks [@marckrenn](https://github.com/marckrenn)! - Improve startup responsiveness by deferring refreshes and watchers, skipping headless UI work, and unref-ing long-lived timers so pi CLI commands exit cleanly.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`59e2b45`](https://github.com/marckrenn/pi-sub/commit/59e2b456e0e5c41479dccedcef93f9175cc4aa55)]:
|
|
10
|
+
- @marckrenn/pi-sub-core@1.0.5
|
|
11
|
+
- @marckrenn/pi-sub-shared@1.0.5
|
|
12
|
+
|
|
3
13
|
## 1.0.4
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import * as fs from "node:fs";
|
|
|
10
10
|
import { homedir, tmpdir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import type { ProviderName, ProviderUsageEntry, SubCoreAllState, SubCoreState, UsageSnapshot } from "./src/types.js";
|
|
13
|
-
import type
|
|
13
|
+
import { getDefaultSettings, type Settings, type BaseTextColor } from "./src/settings-types.js";
|
|
14
14
|
import { isBackgroundColor, resolveBaseTextColor, resolveDividerColor } from "./src/settings-types.js";
|
|
15
15
|
import { buildDividerLine } from "./src/dividers.js";
|
|
16
16
|
import type { CoreSettings } from "@marckrenn/pi-sub-shared";
|
|
@@ -69,6 +69,8 @@ const DEFAULT_AGENT_DIR = join(homedir(), ".pi", "agent");
|
|
|
69
69
|
const PROJECT_SETTINGS_DIR = ".pi";
|
|
70
70
|
const SETTINGS_FILE_NAME = "settings.json";
|
|
71
71
|
|
|
72
|
+
let scopedModelPatternsCache: { cwd: string; patterns: string[] } | undefined;
|
|
73
|
+
|
|
72
74
|
function expandTilde(value: string): string {
|
|
73
75
|
if (value === "~") return homedir();
|
|
74
76
|
if (value.startsWith("~/")) return join(homedir(), value.slice(2));
|
|
@@ -92,6 +94,10 @@ function readPiSettings(path: string): PiSettings | null {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
function loadScopedModelPatterns(cwd: string): string[] {
|
|
97
|
+
if (scopedModelPatternsCache?.cwd === cwd) {
|
|
98
|
+
return scopedModelPatternsCache.patterns;
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
const globalSettings = readPiSettings(resolveAgentSettingsPath());
|
|
96
102
|
const projectSettingsPath = join(cwd, PROJECT_SETTINGS_DIR, SETTINGS_FILE_NAME);
|
|
97
103
|
const projectSettings = readPiSettings(projectSettingsPath);
|
|
@@ -106,8 +112,11 @@ function loadScopedModelPatterns(cwd: string): string[] {
|
|
|
106
112
|
: [];
|
|
107
113
|
}
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
const patterns = !enabledModels || enabledModels.length === 0
|
|
116
|
+
? []
|
|
117
|
+
: enabledModels.filter((value) => typeof value === "string");
|
|
118
|
+
scopedModelPatternsCache = { cwd, patterns };
|
|
119
|
+
return patterns;
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
/**
|
|
@@ -115,7 +124,8 @@ function loadScopedModelPatterns(cwd: string): string[] {
|
|
|
115
124
|
*/
|
|
116
125
|
export default function createExtension(pi: ExtensionAPI) {
|
|
117
126
|
let lastContext: ExtensionContext | undefined;
|
|
118
|
-
let settings: Settings =
|
|
127
|
+
let settings: Settings = getDefaultSettings();
|
|
128
|
+
let uiEnabled = true;
|
|
119
129
|
let currentUsage: UsageSnapshot | undefined;
|
|
120
130
|
let usageEntries: Partial<Record<ProviderName, UsageSnapshot>> = {};
|
|
121
131
|
let coreAvailable = false;
|
|
@@ -180,7 +190,6 @@ export default function createExtension(pi: ExtensionAPI) {
|
|
|
180
190
|
}
|
|
181
191
|
}
|
|
182
192
|
|
|
183
|
-
void ensureSubCoreLoaded();
|
|
184
193
|
|
|
185
194
|
async function promptImportAction(ctx: ExtensionContext): Promise<"save-apply" | "save" | "cancel"> {
|
|
186
195
|
return new Promise((resolve) => {
|
|
@@ -431,25 +440,32 @@ export default function createExtension(pi: ExtensionAPI) {
|
|
|
431
440
|
function startSettingsWatch(): void {
|
|
432
441
|
if (settingsWatchStarted) return;
|
|
433
442
|
settingsWatchStarted = true;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
443
|
+
if (!settingsSnapshot) {
|
|
444
|
+
const content = readSettingsFile();
|
|
445
|
+
if (content) {
|
|
446
|
+
settingsSnapshot = content;
|
|
447
|
+
try {
|
|
448
|
+
const stat = fs.statSync(SETTINGS_PATH, { throwIfNoEntry: false });
|
|
449
|
+
if (stat?.mtimeMs) settingsMtimeMs = stat.mtimeMs;
|
|
450
|
+
} catch {
|
|
451
|
+
// Ignore
|
|
452
|
+
}
|
|
442
453
|
}
|
|
443
454
|
}
|
|
444
455
|
try {
|
|
445
456
|
settingsWatcher = fs.watch(SETTINGS_PATH, scheduleSettingsRefresh);
|
|
457
|
+
settingsWatcher.unref?.();
|
|
446
458
|
} catch {
|
|
447
459
|
settingsWatcher = undefined;
|
|
448
460
|
}
|
|
449
461
|
settingsPoll = setInterval(() => checkSettingsFile(), 2000);
|
|
462
|
+
settingsPoll.unref?.();
|
|
450
463
|
}
|
|
451
464
|
|
|
452
465
|
function renderUsageWidget(ctx: ExtensionContext, usage: UsageSnapshot | undefined, message?: string): void {
|
|
466
|
+
if (!ctx.hasUI || !uiEnabled) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
453
469
|
if (!usage && !message) {
|
|
454
470
|
ctx.ui.setWidget("usage", undefined);
|
|
455
471
|
return;
|
|
@@ -595,6 +611,13 @@ export default function createExtension(pi: ExtensionAPI) {
|
|
|
595
611
|
}
|
|
596
612
|
|
|
597
613
|
function updateFetchFailureTicker(): void {
|
|
614
|
+
if (!uiEnabled) {
|
|
615
|
+
if (fetchFailureTimer) {
|
|
616
|
+
clearInterval(fetchFailureTimer);
|
|
617
|
+
fetchFailureTimer = undefined;
|
|
618
|
+
}
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
598
621
|
const usage = resolveDisplayedUsage();
|
|
599
622
|
const shouldTick = Boolean(usage?.error && usage.lastSuccessAt);
|
|
600
623
|
if (shouldTick && !fetchFailureTimer) {
|
|
@@ -602,6 +625,7 @@ export default function createExtension(pi: ExtensionAPI) {
|
|
|
602
625
|
if (!lastContext) return;
|
|
603
626
|
renderCurrent(lastContext);
|
|
604
627
|
}, 60000);
|
|
628
|
+
fetchFailureTimer.unref?.();
|
|
605
629
|
}
|
|
606
630
|
if (!shouldTick && fetchFailureTimer) {
|
|
607
631
|
clearInterval(fetchFailureTimer);
|
|
@@ -902,37 +926,57 @@ export default function createExtension(pi: ExtensionAPI) {
|
|
|
902
926
|
|
|
903
927
|
pi.on("session_start", async (_event, ctx) => {
|
|
904
928
|
lastContext = ctx;
|
|
929
|
+
uiEnabled = ctx.hasUI;
|
|
930
|
+
if (!uiEnabled) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
905
933
|
settings = loadSettings();
|
|
906
934
|
coreSettings = getFallbackCoreSettings(settings);
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
935
|
+
if (!settingsSnapshot) {
|
|
936
|
+
const content = readSettingsFile();
|
|
937
|
+
if (content) {
|
|
938
|
+
settingsSnapshot = content;
|
|
939
|
+
try {
|
|
940
|
+
const stat = fs.statSync(SETTINGS_PATH, { throwIfNoEntry: false });
|
|
941
|
+
if (stat?.mtimeMs) settingsMtimeMs = stat.mtimeMs;
|
|
942
|
+
} catch {
|
|
943
|
+
// Ignore
|
|
944
|
+
}
|
|
915
945
|
}
|
|
916
946
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
947
|
+
|
|
948
|
+
const watchTimer = setTimeout(() => startSettingsWatch(), 0);
|
|
949
|
+
watchTimer.unref?.();
|
|
950
|
+
|
|
951
|
+
const sessionContext = ctx;
|
|
952
|
+
void (async () => {
|
|
953
|
+
await ensureSubCoreLoaded();
|
|
954
|
+
if (!lastContext || lastContext !== sessionContext || !uiEnabled) return;
|
|
955
|
+
const state = await requestCoreState();
|
|
956
|
+
if (!lastContext || lastContext !== sessionContext || !uiEnabled) return;
|
|
957
|
+
if (state) {
|
|
958
|
+
coreAvailable = true;
|
|
959
|
+
updateUsage(state.usage);
|
|
960
|
+
if (settings.pinnedProvider) {
|
|
961
|
+
const entries = await requestCoreEntries();
|
|
962
|
+
if (!lastContext || lastContext !== sessionContext || !uiEnabled) return;
|
|
963
|
+
updateEntries(entries);
|
|
964
|
+
if (lastContext) {
|
|
965
|
+
renderCurrent(lastContext);
|
|
966
|
+
}
|
|
926
967
|
}
|
|
968
|
+
} else if (lastContext && !coreAvailable) {
|
|
969
|
+
coreAvailable = false;
|
|
970
|
+
renderCurrent(lastContext);
|
|
927
971
|
}
|
|
928
|
-
}
|
|
929
|
-
coreAvailable = false;
|
|
930
|
-
renderCurrent(lastContext);
|
|
931
|
-
}
|
|
972
|
+
})();
|
|
932
973
|
});
|
|
933
974
|
|
|
934
975
|
pi.on("model_select" as unknown as "session_start", async (_event: unknown, ctx: ExtensionContext) => {
|
|
935
976
|
lastContext = ctx;
|
|
977
|
+
if (!uiEnabled || !ctx.hasUI) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
936
980
|
if (currentUsage) {
|
|
937
981
|
renderUsageWidget(ctx, currentUsage);
|
|
938
982
|
}
|
|
@@ -946,5 +990,4 @@ export default function createExtension(pi: ExtensionAPI) {
|
|
|
946
990
|
}
|
|
947
991
|
});
|
|
948
992
|
|
|
949
|
-
startSettingsWatch();
|
|
950
993
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @marckrenn/pi-sub-core
|
|
2
2
|
|
|
3
|
+
## 1.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#35](https://github.com/marckrenn/pi-sub/pull/35) [`59e2b45`](https://github.com/marckrenn/pi-sub/commit/59e2b456e0e5c41479dccedcef93f9175cc4aa55) Thanks [@marckrenn](https://github.com/marckrenn)! - Improve startup responsiveness by deferring refreshes and watchers, skipping headless UI work, and unref-ing long-lived timers so pi CLI commands exit cleanly.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`59e2b45`](https://github.com/marckrenn/pi-sub/commit/59e2b456e0e5c41479dccedcef93f9175cc4aa55)]:
|
|
10
|
+
- @marckrenn/pi-sub-shared@1.0.5
|
|
11
|
+
|
|
3
12
|
## 1.0.4
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import type { Dependencies, ProviderName, SubCoreState, UsageSnapshot } from "./src/types.js";
|
|
8
|
-
import type
|
|
8
|
+
import { getDefaultSettings, type Settings } from "./src/settings-types.js";
|
|
9
9
|
import type { ProviderUsageEntry } from "./src/usage/types.js";
|
|
10
10
|
import { createDefaultDependencies } from "./src/dependencies.js";
|
|
11
11
|
import { createUsageController, type UsageUpdate } from "./src/usage/controller.js";
|
|
@@ -85,8 +85,11 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
85
85
|
let lastContext: ExtensionContext | undefined;
|
|
86
86
|
let lastUsageRefreshAt = 0;
|
|
87
87
|
let lastStatusRefreshAt = 0;
|
|
88
|
-
let settings: Settings =
|
|
88
|
+
let settings: Settings = getDefaultSettings();
|
|
89
|
+
let settingsLoaded = false;
|
|
90
|
+
let toolsRegistered = false;
|
|
89
91
|
let lastState: SubCoreState = {};
|
|
92
|
+
let shouldSkipNextModelSelectFetch = true;
|
|
90
93
|
|
|
91
94
|
const controller = createUsageController(deps);
|
|
92
95
|
const controllerState = {
|
|
@@ -133,31 +136,51 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
133
136
|
emitCurrentUpdate(controllerState.currentProvider, usage);
|
|
134
137
|
});
|
|
135
138
|
|
|
136
|
-
|
|
139
|
+
let stopCacheWatch: (() => void) | undefined;
|
|
140
|
+
let cacheWatchStarted = false;
|
|
141
|
+
|
|
142
|
+
const startCacheWatch = (): void => {
|
|
143
|
+
if (cacheWatchStarted) return;
|
|
144
|
+
cacheWatchStarted = true;
|
|
145
|
+
stopCacheWatch = watchCacheUpdates();
|
|
146
|
+
};
|
|
137
147
|
|
|
138
148
|
function emitUpdate(update: UsageUpdate): void {
|
|
139
149
|
emitCurrentUpdate(update.provider, update.usage);
|
|
140
150
|
}
|
|
141
151
|
|
|
142
|
-
async function refresh(
|
|
152
|
+
async function refresh(
|
|
153
|
+
ctx: ExtensionContext,
|
|
154
|
+
options?: { force?: boolean; allowStaleCache?: boolean; skipFetch?: boolean }
|
|
155
|
+
) {
|
|
143
156
|
lastContext = ctx;
|
|
157
|
+
ensureSettingsLoaded();
|
|
144
158
|
try {
|
|
145
159
|
await controller.refresh(ctx, settings, controllerState, emitUpdate, options);
|
|
146
160
|
} finally {
|
|
147
|
-
|
|
161
|
+
if (!options?.skipFetch) {
|
|
162
|
+
lastUsageRefreshAt = Date.now();
|
|
163
|
+
}
|
|
148
164
|
}
|
|
149
165
|
}
|
|
150
166
|
|
|
151
|
-
async function refreshStatus(
|
|
167
|
+
async function refreshStatus(
|
|
168
|
+
ctx: ExtensionContext,
|
|
169
|
+
options?: { force?: boolean; allowStaleCache?: boolean; skipFetch?: boolean }
|
|
170
|
+
) {
|
|
152
171
|
lastContext = ctx;
|
|
172
|
+
ensureSettingsLoaded();
|
|
153
173
|
try {
|
|
154
174
|
await controller.refreshStatus(ctx, settings, controllerState, emitUpdate, options);
|
|
155
175
|
} finally {
|
|
156
|
-
|
|
176
|
+
if (!options?.skipFetch) {
|
|
177
|
+
lastStatusRefreshAt = Date.now();
|
|
178
|
+
}
|
|
157
179
|
}
|
|
158
180
|
}
|
|
159
181
|
|
|
160
182
|
async function cycleProvider(ctx: ExtensionContext): Promise<void> {
|
|
183
|
+
ensureSettingsLoaded();
|
|
161
184
|
await controller.cycleProvider(ctx, settings, controllerState, emitUpdate);
|
|
162
185
|
}
|
|
163
186
|
|
|
@@ -181,6 +204,7 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
181
204
|
void refresh(lastContext);
|
|
182
205
|
}
|
|
183
206
|
}, usageTickMs);
|
|
207
|
+
usageRefreshInterval.unref?.();
|
|
184
208
|
}
|
|
185
209
|
|
|
186
210
|
const statusIntervalMs = settings.statusRefresh.refreshInterval * 1000;
|
|
@@ -193,10 +217,12 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
193
217
|
void refreshStatus(lastContext);
|
|
194
218
|
}
|
|
195
219
|
}, statusTickMs);
|
|
220
|
+
statusRefreshInterval.unref?.();
|
|
196
221
|
}
|
|
197
222
|
}
|
|
198
223
|
|
|
199
224
|
function applySettingsPatch(patch: Partial<Settings>): void {
|
|
225
|
+
ensureSettingsLoaded();
|
|
200
226
|
settings = deepMerge(settings, patch);
|
|
201
227
|
saveSettings(settings);
|
|
202
228
|
setupRefreshInterval();
|
|
@@ -204,6 +230,7 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
204
230
|
}
|
|
205
231
|
|
|
206
232
|
async function getEntries(force?: boolean): Promise<ProviderUsageEntry[]> {
|
|
233
|
+
ensureSettingsLoaded();
|
|
207
234
|
const enabledProviders = controller.getEnabledProviders(settings);
|
|
208
235
|
if (enabledProviders.length === 0) return [];
|
|
209
236
|
if (force) {
|
|
@@ -255,22 +282,37 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
255
282
|
});
|
|
256
283
|
};
|
|
257
284
|
|
|
258
|
-
|
|
259
|
-
|
|
285
|
+
function registerToolsFromSettings(nextSettings: Settings): void {
|
|
286
|
+
if (toolsRegistered) return;
|
|
287
|
+
const usageToolEnabled = nextSettings.tools?.usageTool ?? false;
|
|
288
|
+
const allUsageToolEnabled = nextSettings.tools?.allUsageTool ?? false;
|
|
260
289
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
290
|
+
if (usageToolEnabled) {
|
|
291
|
+
for (const name of TOOL_NAMES.usage) {
|
|
292
|
+
registerUsageTool(name);
|
|
293
|
+
}
|
|
264
294
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
295
|
+
if (allUsageToolEnabled) {
|
|
296
|
+
for (const name of TOOL_NAMES.allUsage) {
|
|
297
|
+
registerAllUsageTool(name);
|
|
298
|
+
}
|
|
269
299
|
}
|
|
300
|
+
toolsRegistered = true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function ensureSettingsLoaded(): void {
|
|
304
|
+
if (settingsLoaded) return;
|
|
305
|
+
settings = loadSettings();
|
|
306
|
+
settingsLoaded = true;
|
|
307
|
+
registerToolsFromSettings(settings);
|
|
308
|
+
setupRefreshInterval();
|
|
309
|
+
const watchTimer = setTimeout(() => startCacheWatch(), 0);
|
|
310
|
+
watchTimer.unref?.();
|
|
270
311
|
}
|
|
271
312
|
pi.registerCommand("sub-core:settings", {
|
|
272
313
|
description: "Open sub-core settings",
|
|
273
314
|
handler: async (_args, ctx) => {
|
|
315
|
+
ensureSettingsLoaded();
|
|
274
316
|
const handleSettingsChange = async (updatedSettings: Settings) => {
|
|
275
317
|
applySettingsPatch(updatedSettings);
|
|
276
318
|
if (lastContext) {
|
|
@@ -288,6 +330,7 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
288
330
|
});
|
|
289
331
|
|
|
290
332
|
pi.events.on("sub-core:request", async (payload) => {
|
|
333
|
+
ensureSettingsLoaded();
|
|
291
334
|
const request = payload as SubCoreRequest;
|
|
292
335
|
if (request.type === "entries") {
|
|
293
336
|
const entries = await getEntries(request.force);
|
|
@@ -327,10 +370,10 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
327
370
|
|
|
328
371
|
pi.on("session_start", async (_event, ctx) => {
|
|
329
372
|
lastContext = ctx;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
void refresh(ctx, {
|
|
333
|
-
void refreshStatus(ctx, {
|
|
373
|
+
shouldSkipNextModelSelectFetch = true;
|
|
374
|
+
ensureSettingsLoaded();
|
|
375
|
+
void refresh(ctx, { allowStaleCache: true, skipFetch: true });
|
|
376
|
+
void refreshStatus(ctx, { allowStaleCache: true, skipFetch: true });
|
|
334
377
|
pi.events.emit("sub-core:ready", { state: lastState, settings });
|
|
335
378
|
});
|
|
336
379
|
|
|
@@ -373,6 +416,12 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
373
416
|
pi.on("model_select" as unknown as "session_start", async (_event: unknown, ctx: ExtensionContext) => {
|
|
374
417
|
controllerState.currentProvider = undefined;
|
|
375
418
|
controllerState.cachedUsage = undefined;
|
|
419
|
+
if (shouldSkipNextModelSelectFetch) {
|
|
420
|
+
shouldSkipNextModelSelectFetch = false;
|
|
421
|
+
void refresh(ctx, { allowStaleCache: true, skipFetch: true });
|
|
422
|
+
void refreshStatus(ctx, { allowStaleCache: true, skipFetch: true });
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
376
425
|
void refresh(ctx, { force: true, allowStaleCache: true });
|
|
377
426
|
void refreshStatus(ctx, { force: true, allowStaleCache: true });
|
|
378
427
|
});
|
|
@@ -388,7 +437,9 @@ export default function createExtension(pi: ExtensionAPI, deps: Dependencies = c
|
|
|
388
437
|
}
|
|
389
438
|
unsubscribeCache();
|
|
390
439
|
unsubscribeCacheSnapshot();
|
|
391
|
-
stopCacheWatch();
|
|
440
|
+
stopCacheWatch?.();
|
|
441
|
+
stopCacheWatch = undefined;
|
|
442
|
+
cacheWatchStarted = false;
|
|
392
443
|
lastContext = undefined;
|
|
393
444
|
subCoreGlobal.__piSubCore = undefined;
|
|
394
445
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marckrenn/pi-sub-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Shared usage data core for pi extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"typescript": "^5.8.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@marckrenn/pi-sub-shared": "^1.0.
|
|
30
|
+
"@marckrenn/pi-sub-shared": "^1.0.5"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@mariozechner/pi-coding-agent": "*"
|
|
@@ -295,11 +295,13 @@ export function watchCacheUpdates(options?: CacheWatchOptions): () => void {
|
|
|
295
295
|
let watcher: fs.FSWatcher | undefined;
|
|
296
296
|
try {
|
|
297
297
|
watcher = fs.watch(CACHE_PATH, scheduleEmit);
|
|
298
|
+
watcher.unref?.();
|
|
298
299
|
} catch {
|
|
299
300
|
watcher = undefined;
|
|
300
301
|
}
|
|
301
302
|
|
|
302
303
|
pollTimer = setInterval(() => emitFromCache(), pollIntervalMs);
|
|
304
|
+
pollTimer.unref?.();
|
|
303
305
|
|
|
304
306
|
return () => {
|
|
305
307
|
stopped = true;
|
|
@@ -28,10 +28,15 @@ export interface UsageUpdate {
|
|
|
28
28
|
export type UsageUpdateHandler = (update: UsageUpdate) => void;
|
|
29
29
|
|
|
30
30
|
export function createUsageController(deps: Dependencies) {
|
|
31
|
-
function isProviderAvailable(
|
|
31
|
+
function isProviderAvailable(
|
|
32
|
+
settings: Settings,
|
|
33
|
+
provider: ProviderName,
|
|
34
|
+
options?: { skipCredentials?: boolean }
|
|
35
|
+
): boolean {
|
|
32
36
|
const setting = settings.providers[provider];
|
|
33
37
|
if (setting.enabled === "off" || setting.enabled === false) return false;
|
|
34
38
|
if (setting.enabled === "on" || setting.enabled === true) return true;
|
|
39
|
+
if (options?.skipCredentials) return true;
|
|
35
40
|
return hasProviderCredentials(provider, deps);
|
|
36
41
|
}
|
|
37
42
|
|
|
@@ -42,10 +47,11 @@ export function createUsageController(deps: Dependencies) {
|
|
|
42
47
|
function resolveProvider(
|
|
43
48
|
ctx: ExtensionContext,
|
|
44
49
|
settings: Settings,
|
|
45
|
-
state: UsageControllerState
|
|
50
|
+
state: UsageControllerState,
|
|
51
|
+
options?: { skipCredentials?: boolean }
|
|
46
52
|
): ProviderName | undefined {
|
|
47
53
|
const detected = detectProviderFromModel(ctx.model);
|
|
48
|
-
if (detected && isProviderAvailable(settings, detected)) {
|
|
54
|
+
if (detected && isProviderAvailable(settings, detected, options)) {
|
|
49
55
|
return detected;
|
|
50
56
|
}
|
|
51
57
|
return undefined;
|
|
@@ -63,9 +69,9 @@ export function createUsageController(deps: Dependencies) {
|
|
|
63
69
|
settings: Settings,
|
|
64
70
|
state: UsageControllerState,
|
|
65
71
|
onUpdate: UsageUpdateHandler,
|
|
66
|
-
options?: { force?: boolean; allowStaleCache?: boolean; forceStatus?: boolean }
|
|
72
|
+
options?: { force?: boolean; allowStaleCache?: boolean; forceStatus?: boolean; skipFetch?: boolean }
|
|
67
73
|
): Promise<void> {
|
|
68
|
-
const provider = resolveProvider(ctx, settings, state);
|
|
74
|
+
const provider = resolveProvider(ctx, settings, state, { skipCredentials: options?.skipFetch });
|
|
69
75
|
if (!provider) {
|
|
70
76
|
state.currentProvider = undefined;
|
|
71
77
|
state.cachedUsage = undefined;
|
|
@@ -96,6 +102,10 @@ export function createUsageController(deps: Dependencies) {
|
|
|
96
102
|
}
|
|
97
103
|
emitUpdate(state, onUpdate);
|
|
98
104
|
|
|
105
|
+
if (options?.skipFetch) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
99
109
|
const result = await fetchUsageForProvider(deps, settings, provider, options);
|
|
100
110
|
const error = result.usage?.error;
|
|
101
111
|
const fetchError = Boolean(error && !isExpectedMissingData(error));
|
|
@@ -138,9 +148,9 @@ export function createUsageController(deps: Dependencies) {
|
|
|
138
148
|
settings: Settings,
|
|
139
149
|
state: UsageControllerState,
|
|
140
150
|
onUpdate: UsageUpdateHandler,
|
|
141
|
-
options?: { force?: boolean; allowStaleCache?: boolean }
|
|
151
|
+
options?: { force?: boolean; allowStaleCache?: boolean; skipFetch?: boolean }
|
|
142
152
|
): Promise<void> {
|
|
143
|
-
const provider = resolveProvider(ctx, settings, state);
|
|
153
|
+
const provider = resolveProvider(ctx, settings, state, { skipCredentials: options?.skipFetch });
|
|
144
154
|
if (!provider) {
|
|
145
155
|
state.currentProvider = undefined;
|
|
146
156
|
state.cachedUsage = undefined;
|
|
@@ -170,6 +180,11 @@ export function createUsageController(deps: Dependencies) {
|
|
|
170
180
|
}
|
|
171
181
|
}
|
|
172
182
|
|
|
183
|
+
if (options?.skipFetch) {
|
|
184
|
+
emitUpdate(state, onUpdate);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
173
188
|
const status = await refreshStatusForProvider(deps, settings, provider, { force: options?.force });
|
|
174
189
|
if (status && state.cachedUsage) {
|
|
175
190
|
state.cachedUsage = { ...state.cachedUsage, status };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marckrenn/pi-sub-bar",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Usage widget extension for pi-coding-agent - shows current provider usage above the editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"typescript": "^5.8.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@marckrenn/pi-sub-core": "^1.0.
|
|
34
|
-
"@marckrenn/pi-sub-shared": "^1.0.
|
|
33
|
+
"@marckrenn/pi-sub-core": "^1.0.5",
|
|
34
|
+
"@marckrenn/pi-sub-shared": "^1.0.5"
|
|
35
35
|
},
|
|
36
36
|
"bundledDependencies": [
|
|
37
37
|
"@marckrenn/pi-sub-core"
|