jeopi-catalog 16.5.6 → 16.6.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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.6.0] - 2026-10-01
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Refresh Antigravity's live model catalog with lazily resolved OAuth access, account-and-endpoint-scoped caching, and bounded cancellation. Successful discovery replaces stale bundled entries, including an empty catalog; failed refreshes retain the last authoritative snapshot without extending its lifetime.
|
|
10
|
+
- Preserve authoritative empty Antigravity results during provider-only catalog regeneration while retaining previous entries on discovery failure.
|
|
11
|
+
|
|
5
12
|
## [16.5.6] - 2026-10-01
|
|
6
13
|
|
|
7
14
|
### Fixed
|
|
@@ -28,6 +28,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
28
28
|
cacheTtlMs?: number;
|
|
29
29
|
/** When true, a successful dynamic fetch is the complete provider catalog and prunes static-only models. */
|
|
30
30
|
dynamicModelsAuthoritative?: boolean;
|
|
31
|
+
/** Keep a prior complete catalog (including empty) on failed/offline refresh, without extending its TTL. */
|
|
32
|
+
preserveAuthoritativeCacheOnFailure?: boolean;
|
|
31
33
|
/** Cached model ids to ignore when the cache was written against a different static catalog fingerprint. */
|
|
32
34
|
dropCachedModelIdsOnStaticMismatch?: readonly string[];
|
|
33
35
|
/** Trusted provider-wide headers restored locally, never persisted. */
|
|
@@ -59,6 +61,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
59
61
|
export interface ModelResolutionResult<TApi extends Api = Api> {
|
|
60
62
|
models: Model<TApi>[];
|
|
61
63
|
stale: boolean;
|
|
64
|
+
/** A stale result retained a complete provider snapshot; callers must not resurrect bundled-only ids. */
|
|
65
|
+
preservedAuthoritativeCache?: boolean;
|
|
62
66
|
}
|
|
63
67
|
/**
|
|
64
68
|
* Stateful facade over provider model resolution.
|
|
@@ -15,6 +15,10 @@ export interface GoogleAntigravityModelManagerConfig {
|
|
|
15
15
|
oauthToken?: string;
|
|
16
16
|
endpoint?: string;
|
|
17
17
|
fetch?: FetchImpl;
|
|
18
|
+
/** Stable credential identity; falls back to the token when unavailable. */
|
|
19
|
+
cacheScope?: string;
|
|
20
|
+
/** Resolve a fresh token only when discovery actually needs the network. */
|
|
21
|
+
resolveOAuthToken?: (signal: AbortSignal) => Promise<string | undefined>;
|
|
18
22
|
}
|
|
19
23
|
export interface GoogleGeminiCliModelManagerConfig {
|
|
20
24
|
oauthToken?: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-catalog",
|
|
4
|
-
"version": "16.
|
|
4
|
+
"version": "16.6.1",
|
|
5
5
|
"description": "Model catalog for jeopi: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
6
|
"homepage": "https://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@bufbuild/protobuf": "^2.12.0",
|
|
37
|
-
"jeopi-utils": "16.
|
|
37
|
+
"jeopi-utils": "16.6.1",
|
|
38
38
|
"arktype": "2.2.3",
|
|
39
39
|
"zod": "^4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"jeopi-ai": "16.
|
|
42
|
+
"jeopi-ai": "16.6.1",
|
|
43
43
|
"@types/bun": "^1.3.14"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
@@ -161,12 +161,16 @@ export interface FetchAntigravityDiscoveryModelsOptions {
|
|
|
161
161
|
export async function fetchAntigravityDiscoveryModels(
|
|
162
162
|
options: FetchAntigravityDiscoveryModelsOptions,
|
|
163
163
|
): Promise<ModelSpec<"google-gemini-cli">[] | null> {
|
|
164
|
+
if (!options.token.trim() || options.signal?.aborted) return null;
|
|
165
|
+
const timeout = AbortSignal.timeout(10_000);
|
|
166
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
164
167
|
const fetcher = discoveryFetch(options.fetcher);
|
|
165
168
|
const endpoints = options.endpoint
|
|
166
169
|
? [trimTrailingSlashes(options.endpoint)]
|
|
167
170
|
: DEFAULT_ANTIGRAVITY_DISCOVERY_ENDPOINTS.map(trimTrailingSlashes);
|
|
168
171
|
|
|
169
172
|
for (const endpoint of endpoints) {
|
|
173
|
+
if (signal.aborted) return null;
|
|
170
174
|
let response: Response;
|
|
171
175
|
try {
|
|
172
176
|
response = await fetcher(`${endpoint}${FETCH_AVAILABLE_MODELS_PATH}`, {
|
|
@@ -177,7 +181,7 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
177
181
|
"User-Agent": options.userAgent ?? getAntigravityUserAgent(),
|
|
178
182
|
},
|
|
179
183
|
body: JSON.stringify({}),
|
|
180
|
-
signal
|
|
184
|
+
signal,
|
|
181
185
|
});
|
|
182
186
|
} catch {
|
|
183
187
|
continue;
|
|
@@ -194,6 +198,7 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
194
198
|
continue;
|
|
195
199
|
}
|
|
196
200
|
|
|
201
|
+
if (signal.aborted) return null;
|
|
197
202
|
const parsed = parseAntigravityDiscoveryResponse(payload);
|
|
198
203
|
if (!parsed) {
|
|
199
204
|
continue;
|
|
@@ -241,6 +246,13 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
241
246
|
}
|
|
242
247
|
|
|
243
248
|
function parseAntigravityDiscoveryResponse(value: unknown): AntigravityDiscoveryApiResponse | null {
|
|
249
|
+
// An absent or corrupt map is a failed discovery, never an authoritative empty catalog.
|
|
250
|
+
if (typeof value !== "object" || value === null || !("models" in value)) return null;
|
|
251
|
+
const models = value.models;
|
|
252
|
+
if (typeof models !== "object" || models === null || Array.isArray(models)) return null;
|
|
253
|
+
for (const [id, model] of Object.entries(models)) {
|
|
254
|
+
if (!id.trim() || typeof model !== "object" || model === null || Array.isArray(model)) return null;
|
|
255
|
+
}
|
|
244
256
|
const parsed = AntigravityDiscoveryApiResponseSchema(value);
|
|
245
257
|
if (parsed instanceof type.errors) {
|
|
246
258
|
return null;
|
package/src/model-manager.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
39
39
|
cacheTtlMs?: number;
|
|
40
40
|
/** When true, a successful dynamic fetch is the complete provider catalog and prunes static-only models. */
|
|
41
41
|
dynamicModelsAuthoritative?: boolean;
|
|
42
|
+
/** Keep a prior complete catalog (including empty) on failed/offline refresh, without extending its TTL. */
|
|
43
|
+
preserveAuthoritativeCacheOnFailure?: boolean;
|
|
42
44
|
/** Cached model ids to ignore when the cache was written against a different static catalog fingerprint. */
|
|
43
45
|
dropCachedModelIdsOnStaticMismatch?: readonly string[];
|
|
44
46
|
/** Trusted provider-wide headers restored locally, never persisted. */
|
|
@@ -71,6 +73,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
71
73
|
export interface ModelResolutionResult<TApi extends Api = Api> {
|
|
72
74
|
models: Model<TApi>[];
|
|
73
75
|
stale: boolean;
|
|
76
|
+
/** A stale result retained a complete provider snapshot; callers must not resurrect bundled-only ids. */
|
|
77
|
+
preservedAuthoritativeCache?: boolean;
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
/**
|
|
@@ -209,6 +213,20 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
209
213
|
const shouldUseFreshCacheAsAuthoritative =
|
|
210
214
|
strategy === "online-if-uncached" && hasUsableFreshCache && hasAuthoritativeCache;
|
|
211
215
|
const dynamicFetchSucceeded = fetchedDynamicModels !== null;
|
|
216
|
+
if (
|
|
217
|
+
!dynamicFetchSucceeded &&
|
|
218
|
+
dynamicModelsAuthoritative &&
|
|
219
|
+
options.preserveAuthoritativeCacheOnFailure &&
|
|
220
|
+
cache?.authoritative &&
|
|
221
|
+
cacheFingerprintMatches &&
|
|
222
|
+
!cacheHasUnresolvedHeaders
|
|
223
|
+
) {
|
|
224
|
+
return {
|
|
225
|
+
models: collapseBuiltModelVariants(restoredCache.models),
|
|
226
|
+
stale: true,
|
|
227
|
+
preservedAuthoritativeCache: true,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
212
230
|
const cacheModels = dynamicFetchSucceeded
|
|
213
231
|
? []
|
|
214
232
|
: prepareCacheModelsForStaticMismatch(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchAntigravityDiscoveryModels } from "../discovery/antigravity";
|
|
1
|
+
import { ANTIGRAVITY_PRIMARY_ENDPOINT, fetchAntigravityDiscoveryModels } from "../discovery/antigravity";
|
|
2
2
|
import { fetchGeminiModels } from "../discovery/gemini";
|
|
3
3
|
import type { ModelManagerOptions } from "../model-manager";
|
|
4
4
|
import type { FetchImpl } from "../types";
|
|
@@ -21,6 +21,10 @@ export interface GoogleAntigravityModelManagerConfig {
|
|
|
21
21
|
oauthToken?: string;
|
|
22
22
|
endpoint?: string;
|
|
23
23
|
fetch?: FetchImpl;
|
|
24
|
+
/** Stable credential identity; falls back to the token when unavailable. */
|
|
25
|
+
cacheScope?: string;
|
|
26
|
+
/** Resolve a fresh token only when discovery actually needs the network. */
|
|
27
|
+
resolveOAuthToken?: (signal: AbortSignal) => Promise<string | undefined>;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export interface GoogleGeminiCliModelManagerConfig {
|
|
@@ -60,17 +64,26 @@ export function googleVertexModelManagerOptions(_config?: GoogleVertexModelManag
|
|
|
60
64
|
export function googleAntigravityModelManagerOptions(
|
|
61
65
|
config?: GoogleAntigravityModelManagerConfig,
|
|
62
66
|
): ModelManagerOptions<"google-gemini-cli"> {
|
|
63
|
-
const token = config?.oauthToken;
|
|
67
|
+
const token = config?.oauthToken?.trim();
|
|
68
|
+
const endpoint = (config?.endpoint ?? ANTIGRAVITY_PRIMARY_ENDPOINT).replace(/\/+$/, "");
|
|
64
69
|
return {
|
|
65
70
|
providerId: "google-antigravity",
|
|
66
71
|
...(token
|
|
67
72
|
? {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
cacheProviderId: `google-antigravity:discovery-v2:${Bun.hash(JSON.stringify([endpoint, config?.cacheScope ?? token])).toString(36)}`,
|
|
74
|
+
dynamicModelsAuthoritative: true,
|
|
75
|
+
preserveAuthoritativeCacheOnFailure: true,
|
|
76
|
+
fetchDynamicModels: async () => {
|
|
77
|
+
const signal = AbortSignal.timeout(10_000);
|
|
78
|
+
const resolvedToken = config?.resolveOAuthToken ? await config.resolveOAuthToken(signal) : token;
|
|
79
|
+
if (!resolvedToken?.trim() || signal.aborted) return null;
|
|
80
|
+
return fetchAntigravityDiscoveryModels({
|
|
81
|
+
token: resolvedToken,
|
|
71
82
|
endpoint: config?.endpoint,
|
|
72
83
|
fetcher: toDiscoveryFetch(config?.fetch),
|
|
73
|
-
|
|
84
|
+
signal,
|
|
85
|
+
});
|
|
86
|
+
},
|
|
74
87
|
}
|
|
75
88
|
: undefined),
|
|
76
89
|
};
|