@mcowger/opencode-plexus 1.3.9 → 1.4.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.
Files changed (2) hide show
  1. package/dist/index.js +30 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -120,23 +120,32 @@ function isChatModel(model) {
120
120
  return !NON_CHAT_PATTERN.test(`${model.id} ${model.name ?? ""} ${apiHints}`);
121
121
  }
122
122
  var DEFAULT_MODELS_FETCH_TIMEOUT_MS = 1e4;
123
- async function fetchPlexusModels(apiKey, modelsUrl, timeoutMs = DEFAULT_MODELS_FETCH_TIMEOUT_MS) {
123
+ async function fetchPlexusModels(apiKey, modelsUrl, timeoutMs = DEFAULT_MODELS_FETCH_TIMEOUT_MS, etag, signal) {
124
124
  const controller = new AbortController;
125
125
  const timer = setTimeout(() => controller.abort(), timeoutMs);
126
+ const requestSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
126
127
  try {
127
128
  const headers = { Accept: "application/json" };
128
129
  if (apiKey)
129
130
  headers.Authorization = `Bearer ${apiKey}`;
131
+ if (etag)
132
+ headers["If-None-Match"] = etag;
130
133
  const res = await fetch(modelsUrl, {
131
134
  headers,
132
- signal: controller.signal
135
+ signal: requestSignal
133
136
  });
137
+ if (res.status === 304) {
138
+ return { models: [], notModified: true };
139
+ }
134
140
  if (!res.ok) {
135
141
  throw new Error(`Plexus models fetch failed: ${res.status} ${res.statusText}`);
136
142
  }
137
143
  const raw = await res.json();
138
- return { models: raw.data ?? [], raw };
144
+ const responseEtag = res.headers.get("etag") ?? undefined;
145
+ return { models: raw.data ?? [], raw, etag: responseEtag };
139
146
  } catch (err) {
147
+ if (signal?.aborted)
148
+ throw err;
140
149
  if (err instanceof Error && err.name === "AbortError") {
141
150
  throw new Error(`Plexus models fetch timed out after ${timeoutMs}ms`);
142
151
  }
@@ -178,18 +187,21 @@ async function readCachedModels(_client, suppress) {
178
187
  const content = await readFile(join(dir, CACHE_FILE), "utf8");
179
188
  const parsed = JSON.parse(content);
180
189
  if (parsed && typeof parsed.models === "object" && !Array.isArray(parsed.models)) {
181
- return filterCachedModels(parsed.models, suppress);
190
+ return {
191
+ models: filterCachedModels(parsed.models, suppress),
192
+ etag: typeof parsed.etag === "string" ? parsed.etag : undefined
193
+ };
182
194
  }
183
195
  return null;
184
196
  } catch {
185
197
  return null;
186
198
  }
187
199
  }
188
- async function writeCache(_client, models, raw) {
200
+ async function writeCache(_client, models, raw, etag) {
189
201
  try {
190
202
  const dir = getDir();
191
203
  await mkdir(dir, { recursive: true });
192
- const cache = { models, timestamp: Date.now() };
204
+ const cache = { models, timestamp: Date.now(), etag };
193
205
  await writeFile(join(dir, CACHE_FILE), JSON.stringify(cache, null, 2) + `
194
206
  `, "utf8");
195
207
  if (raw !== undefined) {
@@ -581,7 +593,13 @@ function refreshModels(client, baseURL, log, apiKey, force = false, suppress) {
581
593
  return inFlightRefresh;
582
594
  const run = async () => {
583
595
  const url = modelsUrl(baseURL);
584
- const { models: apiModels, raw } = await fetchPlexusModels(apiKey ?? "", url);
596
+ const cached = await readCachedModels(client, suppress);
597
+ const { models: apiModels, raw, etag, notModified } = await fetchPlexusModels(apiKey ?? "", url, undefined, cached?.etag);
598
+ if (notModified && cached?.models) {
599
+ log.info(`Plexus models not modified (etag: ${cached.etag})`);
600
+ lastRefresh = { at: Date.now(), models: cached.models };
601
+ return cached.models;
602
+ }
585
603
  const built = buildModels(apiModels, apiBase(baseURL), suppress);
586
604
  for (const [id, model] of Object.entries(built)) {
587
605
  const providerNpm = model.provider?.npm ?? OPENAI_COMPATIBLE_NPM;
@@ -589,7 +607,7 @@ function refreshModels(client, baseURL, log, apiKey, force = false, suppress) {
589
607
  log.info(`Model mapping ${id}: npm=${providerNpm} api=${providerApi}`);
590
608
  }
591
609
  lastRefresh = { at: Date.now(), models: built };
592
- writeCache(client, built, raw).catch(() => {});
610
+ writeCache(client, built, raw, etag).catch(() => {});
593
611
  return built;
594
612
  };
595
613
  inFlightRefresh = run().finally(() => {
@@ -614,7 +632,7 @@ var PlexusProviderPlugin = async (ctx) => {
614
632
  }
615
633
  const cachedAsync = await readCachedModels(client, suppress);
616
634
  if (cachedAsync) {
617
- log.info(`Loaded plexus cache with ${Object.keys(cachedAsync).length} models`);
635
+ log.info(`Loaded plexus cache with ${Object.keys(cachedAsync.models).length} models`);
618
636
  }
619
637
  const effectiveExistingModels = existingModels ? filterCachedModels(existingModels, suppress) : null;
620
638
  const merged = {
@@ -626,7 +644,7 @@ var PlexusProviderPlugin = async (ctx) => {
626
644
  ...baseURL ? { [PLEXUS_BASE_URL_OPTION]: baseURL } : {},
627
645
  ...apiKey ? { apiKey } : {}
628
646
  },
629
- models: (effectiveExistingModels && Object.keys(effectiveExistingModels).length > 0 ? effectiveExistingModels : null) ?? (cachedAsync ? toConfigModels(cachedAsync) : null) ?? {
647
+ models: (effectiveExistingModels && Object.keys(effectiveExistingModels).length > 0 ? effectiveExistingModels : null) ?? (cachedAsync ? toConfigModels(cachedAsync.models) : null) ?? {
630
648
  [PLACEHOLDER_MODEL_ID]: {
631
649
  id: PLACEHOLDER_MODEL_ID,
632
650
  name: "Plexus (run /connect to configure)",
@@ -668,7 +686,7 @@ var PlexusProviderPlugin = async (ctx) => {
668
686
  if (!baseURL) {
669
687
  log.info("Provider hook skipped live refresh; baseURL missing");
670
688
  const cached2 = await readCachedModels(client, suppress);
671
- return cached2 ? toRuntimeModels(cached2, provider) : {};
689
+ return cached2 ? toRuntimeModels(cached2.models, provider) : {};
672
690
  }
673
691
  const refreshPromise = refreshModels(client, baseURL, log, key, false, suppress);
674
692
  const race = await raceWithTimeout(refreshPromise, CONFIG_HOOK_REFRESH_BUDGET_MS);
@@ -685,7 +703,7 @@ var PlexusProviderPlugin = async (ctx) => {
685
703
  log.warn(`Background plexus model refresh failed: ${String(e)}`);
686
704
  });
687
705
  }
688
- return cached ? toRuntimeModels(cached, provider) : {};
706
+ return cached ? toRuntimeModels(cached.models, provider) : {};
689
707
  }
690
708
  },
691
709
  "command.execute.before": async (input) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcowger/opencode-plexus",
3
- "version": "1.3.9",
3
+ "version": "1.4.0",
4
4
  "description": "OpenCode plugin: Plexus provider with dynamic model discovery",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",