@fro.bot/systematic 2.14.3 → 2.14.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/dist/index.js CHANGED
@@ -634,6 +634,7 @@ function emptyAvailability() {
634
634
  var MAX_CACHE_FILE_BYTES = 16 * 1024 * 1024;
635
635
  var DEFAULT_API_TIMEOUT_MS = 1500;
636
636
  var MODELS_JSON_FILENAME = "models.json";
637
+ var availabilityCache = new WeakMap;
637
638
  function resolveCacheDir() {
638
639
  const xdgCacheHome = process.env.XDG_CACHE_HOME?.trim();
639
640
  const cacheBase = xdgCacheHome && path5.isAbsolute(xdgCacheHome) ? xdgCacheHome : path5.join(os2.homedir(), ".cache");
@@ -717,14 +718,14 @@ function readFallbackCache() {
717
718
  if (openCodeModelsUrl) {
718
719
  const urlDerivedPath = path5.join(cacheDir, `models-${fastHash(openCodeModelsUrl)}.json`);
719
720
  const urlResult = readModelsFromCache(urlDerivedPath);
720
- if (urlResult !== null) {
721
+ if (urlResult !== null && urlResult.size > 0) {
721
722
  return { status: "cache", models: urlResult };
722
723
  }
723
724
  return emptyAvailability();
724
725
  }
725
726
  const defaultPath = path5.join(cacheDir, MODELS_JSON_FILENAME);
726
727
  const defaultResult = readModelsFromCache(defaultPath);
727
- if (defaultResult !== null) {
728
+ if (defaultResult !== null && defaultResult.size > 0) {
728
729
  return { status: "cache", models: defaultResult };
729
730
  }
730
731
  return emptyAvailability();
@@ -739,9 +740,19 @@ function buildSetFromProviders(providers) {
739
740
  return result;
740
741
  }
741
742
  async function getAvailableModels(client, options = {}) {
743
+ const cached = availabilityCache.get(client);
744
+ if (cached !== undefined) {
745
+ return cached;
746
+ }
747
+ const cacheAndReturn = (envelope) => {
748
+ if (envelope.status !== "unknown") {
749
+ availabilityCache.set(client, envelope);
750
+ }
751
+ return envelope;
752
+ };
742
753
  const timeoutMs = options.apiTimeoutMs === undefined ? DEFAULT_API_TIMEOUT_MS : options.apiTimeoutMs;
743
754
  if (typeof client.config?.providers !== "function") {
744
- return readFallbackCache();
755
+ return cacheAndReturn(readFallbackCache());
745
756
  }
746
757
  const apiCall = client.config.providers();
747
758
  let response;
@@ -757,24 +768,24 @@ async function getAvailableModels(client, options = {}) {
757
768
  const raced = await Promise.race([apiCall, timeoutPromise]);
758
769
  if (raced === TIMEOUT_SENTINEL) {
759
770
  console.warn(`[systematic] client.config.providers() exceeded ${timeoutMs}ms; falling back to models.json cache.`);
760
- return readFallbackCache();
771
+ return cacheAndReturn(readFallbackCache());
761
772
  }
762
773
  response = raced;
763
774
  }
764
775
  } catch {
765
- return readFallbackCache();
776
+ return cacheAndReturn(readFallbackCache());
766
777
  }
767
778
  if (response.error !== undefined || response.data === undefined) {
768
- return readFallbackCache();
779
+ return cacheAndReturn(readFallbackCache());
769
780
  }
770
781
  const models = buildSetFromProviders(response.data.providers);
771
782
  if (models.size === 0) {
772
- return emptyAvailability();
783
+ return cacheAndReturn(emptyAvailability());
773
784
  }
774
- return {
785
+ return cacheAndReturn({
775
786
  status: "api",
776
787
  models
777
- };
788
+ });
778
789
  }
779
790
 
780
791
  // src/lib/config-handler.ts
@@ -28,10 +28,10 @@ export interface OpencodeClientLike {
28
28
  * operational consequence is identical and downstream consumers should treat
29
29
  * both cases the same way.
30
30
  * - `cache`: The API call failed (error envelope, thrown, or timed out) and
31
- * the local `models.json` cache was readable. `models` reflects whatever
32
- * OpenCode last wrote to disk. The cache may itself be empty; callers that
33
- * need to distinguish "cached empty" from "cached with content" should
34
- * inspect `models.size`.
31
+ * the local `models.json` cache was readable AND non-empty. `models`
32
+ * reflects whatever OpenCode last wrote to disk. A cache that loads
33
+ * successfully but produces a zero-size set collapses to `'unknown'` for
34
+ * the same operational reason an empty API response does.
35
35
  * - `unknown`: Either both the API call and the cache fallback failed (cache
36
36
  * missing, unreadable, corrupt, or schema-mismatched), OR the API call
37
37
  * succeeded with zero usable models. Resolution should degrade gracefully —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "2.14.3",
3
+ "version": "2.14.5",
4
4
  "description": "Structured engineering workflows for OpenCode",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",