@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 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 { Settings, BaseTextColor } from "./src/settings-types.js";
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
- if (!enabledModels || enabledModels.length === 0) return [];
110
- return enabledModels.filter((value) => typeof value === "string");
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 = loadSettings();
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
- const content = readSettingsFile();
435
- if (content) {
436
- settingsSnapshot = content;
437
- try {
438
- const stat = fs.statSync(SETTINGS_PATH, { throwIfNoEntry: false });
439
- if (stat?.mtimeMs) settingsMtimeMs = stat.mtimeMs;
440
- } catch {
441
- // Ignore
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
- const content = readSettingsFile();
908
- if (content) {
909
- settingsSnapshot = content;
910
- try {
911
- const stat = fs.statSync(SETTINGS_PATH, { throwIfNoEntry: false });
912
- if (stat?.mtimeMs) settingsMtimeMs = stat.mtimeMs;
913
- } catch {
914
- // Ignore
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
- const state = await requestCoreState();
918
- if (state) {
919
- coreAvailable = true;
920
- updateUsage(state.usage);
921
- if (settings.pinnedProvider) {
922
- const entries = await requestCoreEntries();
923
- updateEntries(entries);
924
- if (lastContext) {
925
- renderCurrent(lastContext);
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
- } else if (lastContext) {
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 { Settings } from "./src/settings-types.js";
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 = loadSettings();
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
- const stopCacheWatch = watchCacheUpdates();
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(ctx: ExtensionContext, options?: { force?: boolean; allowStaleCache?: boolean }) {
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
- lastUsageRefreshAt = Date.now();
161
+ if (!options?.skipFetch) {
162
+ lastUsageRefreshAt = Date.now();
163
+ }
148
164
  }
149
165
  }
150
166
 
151
- async function refreshStatus(ctx: ExtensionContext, options?: { force?: boolean; allowStaleCache?: boolean }) {
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
- lastStatusRefreshAt = Date.now();
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
- const usageToolEnabled = settings.tools?.usageTool ?? false;
259
- const allUsageToolEnabled = settings.tools?.allUsageTool ?? false;
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
- if (usageToolEnabled) {
262
- for (const name of TOOL_NAMES.usage) {
263
- registerUsageTool(name);
290
+ if (usageToolEnabled) {
291
+ for (const name of TOOL_NAMES.usage) {
292
+ registerUsageTool(name);
293
+ }
264
294
  }
265
- }
266
- if (allUsageToolEnabled) {
267
- for (const name of TOOL_NAMES.allUsage) {
268
- registerAllUsageTool(name);
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
- settings = loadSettings();
331
- setupRefreshInterval();
332
- void refresh(ctx, { force: true, allowStaleCache: true });
333
- void refreshStatus(ctx, { force: true, allowStaleCache: true });
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.4",
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.4"
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(settings: Settings, provider: ProviderName): boolean {
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 };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marckrenn/pi-sub-shared",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Shared types and event contract for the sub-* ecosystem",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marckrenn/pi-sub-bar",
3
- "version": "1.0.4",
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.4",
34
- "@marckrenn/pi-sub-shared": "^1.0.4"
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"