omni-pi 0.8.2 → 0.8.3

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.3 - 2026-04-17
4
+
5
+ ### Model refresh flow
6
+
7
+ - added a custom model refresh flow so newly released provider models can be picked up without repeating setup from scratch
8
+ - stored refresh state separately so daily checks can detect when a refresh is needed and avoid redundant prompts
9
+ - added coverage for the refresh command, state handling, and daily refresh behavior
10
+
3
11
  ## 0.8.2 - 2026-04-07
4
12
 
5
13
  ### Single-brain runtime
@@ -23,6 +31,10 @@
23
31
  - removed the unused `pi-subagents` dependency
24
32
  - added npm overrides for `@mozilla/readability`, `brace-expansion`, `picomatch`, and `vite`
25
33
 
34
+ ## 0.8.1 - 2026-04-07
35
+
36
+ - blocked Anthropic oAuth login to avoid bans from recent policy changes on the Claude Code subscription ToS
37
+
26
38
  ## 0.8.0 - 2026-04-06
27
39
 
28
40
  ### Runtime and UX
package/PROVIDERS.md CHANGED
@@ -62,7 +62,9 @@ The bundled-provider list below is expected to stay in sync with the exported pr
62
62
 
63
63
  For custom providers that expose a compatible model listing endpoint, Omni-Pi can fetch models for you after you add the provider details and credentials.
64
64
 
65
- On launch, Omni-Pi also refreshes authenticated, discoverable custom providers that are already configured in `models.json`.
65
+ On launch, Omni-Pi refreshes authenticated, discoverable custom providers that are already configured in `models.json` at most once per day.
66
+
67
+ You can also run `/model-setup refresh` to re-discover those custom provider models on demand.
66
68
 
67
69
  ## When To Use Which Path
68
70
 
package/README.md CHANGED
@@ -29,7 +29,7 @@ cd your-project
29
29
  omni
30
30
  ```
31
31
 
32
- Custom provider setup and bundled provider behavior are documented in [PROVIDERS.md](PROVIDERS.md).
32
+ Custom provider setup, refresh behavior, and bundled provider behavior are documented in [PROVIDERS.md](PROVIDERS.md).
33
33
 
34
34
  ## Features
35
35
 
@@ -66,7 +66,7 @@ Omni-Pi now bundles [Glimpse](https://github.com/HazAT/glimpse) for native micro
66
66
 
67
67
  | Command | Description |
68
68
  |---------|-------------|
69
- | `/model-setup` | Add custom providers/models or remove custom model entries |
69
+ | `/model-setup` | Add, refresh, or remove custom provider/model entries |
70
70
  | `/manage-providers` | Remove stored auth for bundled providers |
71
71
  | `/omni-mode` | Toggle persistent Omni mode on or off for this project |
72
72
  | `/companion` | Toggle the Glimpse floating companion widget |
@@ -87,12 +87,13 @@ Omni-Pi checks for new versions on startup (cached, re-checks every 4 hours). Wh
87
87
 
88
88
  `/model-setup` is for custom providers and custom model entries only.
89
89
 
90
- Use it when you want to configure:
90
+ Use `/model-setup` when you want to configure:
91
91
 
92
92
  - a custom provider id
93
93
  - an API type and base URL
94
94
  - an API key for that custom provider
95
95
  - discovered models or manual model entries
96
+ - a manual refresh of already configured custom providers
96
97
 
97
98
  Use `/manage-providers` to remove stored auth for bundled Pi providers.
98
99
 
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
 
3
3
  import { disableAnthropicOAuth } from "../../src/anthropic-auth-guard.js";
4
- import { refreshAuthenticatedProviderModels } from "../../src/model-setup.js";
4
+ import { refreshAuthenticatedProviderModelsWithDailyGuard } from "../../src/model-setup.js";
5
5
  import { registerOmniProviders } from "../../src/providers.js";
6
6
 
7
7
  export default async function omniProvidersExtension(
@@ -11,6 +11,6 @@ export default async function omniProvidersExtension(
11
11
 
12
12
  api.on("session_start", async (_event, ctx) => {
13
13
  disableAnthropicOAuth(ctx.modelRegistry);
14
- await refreshAuthenticatedProviderModels(ctx.modelRegistry);
14
+ await refreshAuthenticatedProviderModelsWithDailyGuard(ctx.modelRegistry);
15
15
  });
16
16
  }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "omni-pi",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Single-agent Pi package that interviews the user, documents the spec, and implements work in bounded slices.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Eduard-David Gyarmati",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/EdGy2k/Omni-Pi"
10
+ "url": "https://github.com/edgyarmati/Omni-Pi"
11
11
  },
12
- "homepage": "https://github.com/EdGy2k/Omni-Pi#readme",
12
+ "homepage": "https://github.com/edgyarmati/Omni-Pi#readme",
13
13
  "bugs": {
14
- "url": "https://github.com/EdGy2k/Omni-Pi/issues"
14
+ "url": "https://github.com/edgyarmati/Omni-Pi/issues"
15
15
  },
16
16
  "engines": {
17
17
  "node": ">=22"
@@ -7,7 +7,10 @@ import type {
7
7
  } from "@mariozechner/pi-coding-agent";
8
8
  import { getAgentDir } from "@mariozechner/pi-coding-agent";
9
9
 
10
- import { runModelSetupWizard } from "./model-setup.js";
10
+ import {
11
+ refreshAuthenticatedProviderModelsWithDailyGuard,
12
+ runModelSetupWizard,
13
+ } from "./model-setup.js";
11
14
  import { searchableSelect } from "./searchable-select.js";
12
15
 
13
16
  interface ModelsJsonModel {
@@ -101,6 +104,23 @@ async function handleAdd(ctx: ExtensionCommandContext): Promise<void> {
101
104
  ctx.ui.notify(result.summary, "info");
102
105
  }
103
106
 
107
+ async function handleRefresh(ctx: ExtensionCommandContext): Promise<void> {
108
+ const result = await refreshAuthenticatedProviderModelsWithDailyGuard(
109
+ ctx.modelRegistry,
110
+ { force: true },
111
+ );
112
+
113
+ if (result.refreshedProviders.length === 0) {
114
+ ctx.ui.notify("No eligible custom providers were refreshed.", "info");
115
+ return;
116
+ }
117
+
118
+ ctx.ui.notify(
119
+ `Refreshed custom providers: ${result.refreshedProviders.join(", ")}.`,
120
+ "info",
121
+ );
122
+ }
123
+
104
124
  async function handleList(ctx: ExtensionCommandContext): Promise<void> {
105
125
  const config = await readModelsJson();
106
126
  const custom = getCustomModels(config);
@@ -149,21 +169,28 @@ async function handleList(ctx: ExtensionCommandContext): Promise<void> {
149
169
 
150
170
  export function registerModelCommand(api: ExtensionAPI): void {
151
171
  api.registerCommand("model-setup", {
152
- description: "Add custom providers/models or remove custom model entries",
172
+ description:
173
+ "Add custom providers/models, refresh discovered models, or remove custom model entries",
153
174
  async handler(args: string, ctx: ExtensionCommandContext) {
154
175
  const sub = args.trim().toLowerCase();
155
176
 
156
177
  if (sub === "add") return handleAdd(ctx);
178
+ if (sub === "refresh") return handleRefresh(ctx);
157
179
  if (sub === "list") return handleList(ctx);
158
180
 
159
181
  const choice = await searchableSelect(ctx.ui, "Model setup:", [
160
182
  {
161
- label: "add — Add a custom provider or model",
183
+ label: "add — Add a custom provider or model",
162
184
  value: "add",
163
185
  searchText: "add custom provider model",
164
186
  },
165
187
  {
166
- label: "list Show custom models / remove model entries",
188
+ label: "refresh Re-discover models for configured custom providers",
189
+ value: "refresh",
190
+ searchText: "refresh rediscover custom provider models",
191
+ },
192
+ {
193
+ label: "list — Show custom models / remove model entries",
167
194
  value: "list",
168
195
  searchText: "list remove custom models",
169
196
  },
@@ -171,6 +198,7 @@ export function registerModelCommand(api: ExtensionAPI): void {
171
198
  if (!choice) return;
172
199
 
173
200
  if (choice === "add") return handleAdd(ctx);
201
+ if (choice === "refresh") return handleRefresh(ctx);
174
202
  if (choice === "list") return handleList(ctx);
175
203
  },
176
204
  });
@@ -0,0 +1,39 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { getAgentDir } from "@mariozechner/pi-coding-agent";
5
+
6
+ export interface ModelRefreshState {
7
+ lastSuccessfulRefreshDate?: string;
8
+ }
9
+
10
+ export function getModelRefreshStatePath(agentDir = getAgentDir()): string {
11
+ return path.join(agentDir, "model-refresh-state.json");
12
+ }
13
+
14
+ export function getLocalDateStamp(date = new Date()): string {
15
+ const year = date.getFullYear();
16
+ const month = String(date.getMonth() + 1).padStart(2, "0");
17
+ const day = String(date.getDate()).padStart(2, "0");
18
+ return `${year}-${month}-${day}`;
19
+ }
20
+
21
+ export async function readModelRefreshState(
22
+ statePath = getModelRefreshStatePath(),
23
+ ): Promise<ModelRefreshState> {
24
+ try {
25
+ const content = await readFile(statePath, "utf8");
26
+ const parsed = JSON.parse(content) as ModelRefreshState;
27
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
28
+ } catch {
29
+ return {};
30
+ }
31
+ }
32
+
33
+ export async function writeModelRefreshState(
34
+ state: ModelRefreshState,
35
+ statePath = getModelRefreshStatePath(),
36
+ ): Promise<void> {
37
+ await mkdir(path.dirname(statePath), { recursive: true });
38
+ await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
39
+ }
@@ -7,6 +7,18 @@ import path from "node:path";
7
7
  import type { ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
8
8
  import { getAgentDir } from "@mariozechner/pi-coding-agent";
9
9
 
10
+ import {
11
+ getLocalDateStamp,
12
+ readModelRefreshState,
13
+ writeModelRefreshState,
14
+ } from "./model-refresh-state.js";
15
+
16
+ export {
17
+ getLocalDateStamp,
18
+ readModelRefreshState,
19
+ writeModelRefreshState,
20
+ } from "./model-refresh-state.js";
21
+
10
22
  import type { OmniConfig } from "./contracts.js";
11
23
  import { discoverProviderModels, type OmniProviderModel } from "./providers.js";
12
24
  import { searchableSelect } from "./searchable-select.js";
@@ -410,6 +422,39 @@ export async function refreshAuthenticatedProviderModels(
410
422
  return refreshed.refreshedProviders;
411
423
  }
412
424
 
425
+ export async function refreshAuthenticatedProviderModelsWithDailyGuard(
426
+ modelRegistry: ModelRegistryLike,
427
+ options?: {
428
+ force?: boolean;
429
+ now?: Date;
430
+ statePath?: string;
431
+ },
432
+ ): Promise<{ refreshedProviders: string[]; skipped: boolean }> {
433
+ const today = getLocalDateStamp(options?.now);
434
+ if (!options?.force) {
435
+ const state = await readModelRefreshState(options?.statePath);
436
+ if (state.lastSuccessfulRefreshDate === today) {
437
+ return { refreshedProviders: [], skipped: true };
438
+ }
439
+ }
440
+
441
+ const refreshedProviders =
442
+ await refreshAuthenticatedProviderModels(modelRegistry);
443
+
444
+ if (refreshedProviders.length > 0) {
445
+ try {
446
+ await writeModelRefreshState(
447
+ { lastSuccessfulRefreshDate: today },
448
+ options?.statePath,
449
+ );
450
+ } catch {
451
+ // Keep the refreshed models even if the durable refresh stamp cannot be written.
452
+ }
453
+ }
454
+
455
+ return { refreshedProviders, skipped: false };
456
+ }
457
+
413
458
  function sanitizeProviderId(input: string): string {
414
459
  return input.trim().toLowerCase().replace(/\s+/gu, "-");
415
460
  }