@robhowley/pi-openrouter 0.11.1 → 0.12.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 +27 -1
- package/extensions/openrouter/__tests__/commands.test.ts +142 -44
- package/extensions/openrouter/__tests__/fixtures.ts +1 -0
- package/extensions/openrouter/__tests__/hooks.test.ts +82 -3
- package/extensions/openrouter/commands.ts +95 -17
- package/extensions/openrouter/hooks.ts +24 -4
- package/extensions/openrouter/models/__tests__/cache.test.ts +20 -1
- package/extensions/openrouter/models/__tests__/sync.test.ts +398 -222
- package/extensions/openrouter/models/cache.ts +27 -5
- package/extensions/openrouter/models/sync.ts +230 -77
- package/extensions/openrouter/models/types.ts +36 -3
- package/package.json +2 -2
|
@@ -9,7 +9,12 @@ import { createSessionState, type SessionState } from './session-state.js';
|
|
|
9
9
|
import { writeLocalUsage, type LocalUsageEvent } from './local-usage.js';
|
|
10
10
|
import { loadCache, getCacheAgeMs, formatDuration } from './models/cache.js';
|
|
11
11
|
import { mapOpenRouterModels } from './models/mapper.js';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
filterModelsForCatalogMode,
|
|
14
|
+
includeBuiltinRouterModels,
|
|
15
|
+
isSyncEnabled,
|
|
16
|
+
setActiveCatalogState,
|
|
17
|
+
} from './models/sync.js';
|
|
13
18
|
import { loadOpenRouterStatusBar } from './status-bar.js';
|
|
14
19
|
|
|
15
20
|
let sessionState: SessionState | null = null;
|
|
@@ -90,8 +95,13 @@ export async function loadStartupCacheState(
|
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
try {
|
|
93
|
-
const
|
|
94
|
-
const
|
|
98
|
+
const filteredCacheModels = filterModelsForCatalogMode(cache.models, cache.catalogMode);
|
|
99
|
+
const { configs, skipped, skippedDetails } = await mapOpenRouterModels(filteredCacheModels);
|
|
100
|
+
const configsWithRouters = includeBuiltinRouterModels(configs, cache.catalogMode);
|
|
101
|
+
const effectiveSkippedDetails = cache.skippedDetails ?? skippedDetails;
|
|
102
|
+
const skippedCount =
|
|
103
|
+
effectiveSkippedDetails.length > 0 ? effectiveSkippedDetails.length : skipped;
|
|
104
|
+
const cacheAgeMs = getCacheAgeMs(cache);
|
|
95
105
|
|
|
96
106
|
pi.registerProvider('openrouter', {
|
|
97
107
|
baseUrl: 'https://openrouter.ai/api/v1',
|
|
@@ -101,9 +111,19 @@ export async function loadStartupCacheState(
|
|
|
101
111
|
authHeader: true,
|
|
102
112
|
});
|
|
103
113
|
|
|
114
|
+
setActiveCatalogState({
|
|
115
|
+
mode: cache.catalogMode,
|
|
116
|
+
registeredModelIds: configsWithRouters.map((config) => config.id),
|
|
117
|
+
registeredCount: configsWithRouters.length,
|
|
118
|
+
skippedCount,
|
|
119
|
+
skippedDetails: effectiveSkippedDetails,
|
|
120
|
+
source: 'cache',
|
|
121
|
+
cacheAgeMs,
|
|
122
|
+
});
|
|
123
|
+
|
|
104
124
|
startupState.info = {
|
|
105
125
|
count: configsWithRouters.length,
|
|
106
|
-
age: formatDuration(
|
|
126
|
+
age: formatDuration(cacheAgeMs),
|
|
107
127
|
};
|
|
108
128
|
} catch (error) {
|
|
109
129
|
startupState.warning = `OpenRouter: cached models found but failed to register: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -45,11 +45,12 @@ describe('loadCache', () => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('should return parsed cache when file exists and is valid', async () => {
|
|
48
|
-
const mockCache = createMockCache({ timestamp: 1234567890 });
|
|
48
|
+
const mockCache = createMockCache({ catalogMode: 'free-only', timestamp: 1234567890 });
|
|
49
49
|
await saveCache(mockCache);
|
|
50
50
|
|
|
51
51
|
const result = await loadCache();
|
|
52
52
|
expect(result).not.toBeNull();
|
|
53
|
+
expect(result!.catalogMode).toBe('free-only');
|
|
53
54
|
expect(result!.timestamp).toBe(1234567890);
|
|
54
55
|
expect(result!.models).toHaveLength(1);
|
|
55
56
|
expect(result!.models[0]!.id).toBe('test/model');
|
|
@@ -73,6 +74,23 @@ describe('loadCache', () => {
|
|
|
73
74
|
expect(result).toBeNull();
|
|
74
75
|
});
|
|
75
76
|
|
|
77
|
+
it('should normalize old cache files without catalogMode to full', async () => {
|
|
78
|
+
await mkdir(testCacheDir, { recursive: true });
|
|
79
|
+
const cacheFile = join(testCacheDir, 'models-cache.json');
|
|
80
|
+
const oldCache = createMockCache({ timestamp: 1234567890 });
|
|
81
|
+
const legacyCache = {
|
|
82
|
+
models: oldCache.models,
|
|
83
|
+
skippedDetails: oldCache.skippedDetails,
|
|
84
|
+
timestamp: oldCache.timestamp,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
await writeFile(cacheFile, JSON.stringify(legacyCache));
|
|
88
|
+
|
|
89
|
+
const result = await loadCache();
|
|
90
|
+
expect(result).not.toBeNull();
|
|
91
|
+
expect(result!.catalogMode).toBe('full');
|
|
92
|
+
});
|
|
93
|
+
|
|
76
94
|
it('should return null when cache timestamp is too far in the future', async () => {
|
|
77
95
|
await mkdir(testCacheDir, { recursive: true });
|
|
78
96
|
const cacheFile = join(testCacheDir, 'models-cache.json');
|
|
@@ -98,6 +116,7 @@ describe('saveCache', () => {
|
|
|
98
116
|
// Verify it can be loaded back
|
|
99
117
|
const loaded = await loadCache();
|
|
100
118
|
expect(loaded).not.toBeNull();
|
|
119
|
+
expect(loaded!.catalogMode).toBe('full');
|
|
101
120
|
expect(loaded!.timestamp).toBe(mockCache.timestamp);
|
|
102
121
|
expect(loaded!.models).toEqual(mockCache.models);
|
|
103
122
|
});
|