@mcowger/opencode-plexus 1.1.2 → 1.2.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 +7 -3
- package/dist/index.js +52 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -116,7 +116,11 @@ Run inside OpenCode:
|
|
|
116
116
|
/connect
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
Select **Plexus** and enter your base URL and API key.
|
|
119
|
+
Select **Plexus** and enter your base URL and API key. OpenCode has no live model-discovery hook for custom providers, so the model list is seeded from the on-disk cache at startup. Run `/plexus-refresh` to force a live fetch from Plexus and rewrite the cache, then restart OpenCode to pick up the refreshed list:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
/plexus-refresh
|
|
123
|
+
```
|
|
120
124
|
|
|
121
125
|
You may enter either the Plexus root URL or the `/v1` API base URL:
|
|
122
126
|
|
|
@@ -197,7 +201,7 @@ packages/
|
|
|
197
201
|
plugin.ts # Plugin export: config hook, provider hook, auth handler
|
|
198
202
|
mapper.ts # PlexusApiModel → OpenCode ConfigModel
|
|
199
203
|
cache.ts # model cache I/O
|
|
200
|
-
config-store.ts # resolveConfig,
|
|
204
|
+
config-store.ts # resolveConfig, readStoredAuth
|
|
201
205
|
log.ts # logger via OpenCode SDK
|
|
202
206
|
constants.ts # provider ID, env var names, timeouts
|
|
203
207
|
url.ts # URL helpers (trimURL, apiBase, modelsUrl)
|
|
@@ -228,7 +232,7 @@ Models with a falsy `id` are skipped. Missing metadata falls back to safe defaul
|
|
|
228
232
|
## Adapter behavior
|
|
229
233
|
|
|
230
234
|
- **pi** refreshes on session start and through `/plexus refresh`. It accepts either root URLs or URLs ending in `/v1` and normalizes them before calling Plexus.
|
|
231
|
-
- **OpenCode** seeds the provider from cache or a placeholder model during config loading
|
|
235
|
+
- **OpenCode** seeds the provider from the on-disk cache (or a placeholder model) once, during config loading — OpenCode's `provider.models` hook never fires for custom providers, so there is no live discovery at startup. Run `/plexus-refresh` to force a live fetch and rewrite the cache; because OpenCode has no way to hot-reload a custom provider's model list mid-session, a restart is required afterward to see the refreshed models in the picker.
|
|
232
236
|
- OpenCode models retain their upstream model ID, SDK dialect, release date, and reasoning metadata so OpenCode can generate its native GPT, Claude, Gemini, and OpenAI-compatible variants and apply its current request transforms. DeepSeek models also preserve `reasoning_content` across tool-call turns.
|
|
233
237
|
- OpenCode uses a 250K-token context window when Plexus supplies no context metadata; its output fallback remains 20% of that window.
|
|
234
238
|
- Both adapters convert Plexus's per-token base and tier rates to the per-million-token units expected by their host.
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ var MODELS_FETCH_TIMEOUT_MS = 1e4;
|
|
|
13
13
|
var REFRESH_TTL_MS = 60000;
|
|
14
14
|
var CONFIG_HOOK_REFRESH_BUDGET_MS = 3000;
|
|
15
15
|
var PLACEHOLDER_MODEL_ID = "plexus-unconfigured";
|
|
16
|
+
var PLEXUS_REFRESH_COMMAND = "plexus-refresh";
|
|
16
17
|
|
|
17
18
|
// ../plexus-models/src/convert.ts
|
|
18
19
|
var REASONING_PARAMS = new Set(["reasoning", "include_reasoning", "reasoning_effort"]);
|
|
@@ -140,6 +141,11 @@ async function writeCache(_client, models, raw) {
|
|
|
140
141
|
} catch {}
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
// src/config-store.ts
|
|
145
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
146
|
+
import { homedir as homedir2 } from "os";
|
|
147
|
+
import { join as join2 } from "path";
|
|
148
|
+
|
|
143
149
|
// src/url.ts
|
|
144
150
|
function trimURL(s) {
|
|
145
151
|
return s.trim().replace(/\/+$/, "");
|
|
@@ -165,6 +171,21 @@ function modelsUrl(baseURL) {
|
|
|
165
171
|
var ENV_VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
166
172
|
var ENV_VAR_NAME_PREFIX_RE = /^[A-Za-z_][A-Za-z0-9_]*/;
|
|
167
173
|
var AUTH_METADATA_BASE_URL = "plexusBaseURL";
|
|
174
|
+
async function readStoredAuth() {
|
|
175
|
+
const dataHome = process.env.XDG_DATA_HOME || join2(homedir2(), ".local", "share");
|
|
176
|
+
const file = join2(dataHome, "opencode", "auth.json");
|
|
177
|
+
try {
|
|
178
|
+
const content = await readFile2(file, "utf8");
|
|
179
|
+
const parsed = JSON.parse(content);
|
|
180
|
+
const entry = parsed[PLEXUS_PROVIDER_ID];
|
|
181
|
+
if (typeof entry === "object" && entry !== null && entry.type === "api" && typeof entry.key === "string") {
|
|
182
|
+
return entry;
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
} catch {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
168
189
|
function resolveConfigTemplate(value) {
|
|
169
190
|
let result = "";
|
|
170
191
|
let index = 0;
|
|
@@ -476,8 +497,8 @@ function raceWithTimeout(promise, timeoutMs) {
|
|
|
476
497
|
});
|
|
477
498
|
});
|
|
478
499
|
}
|
|
479
|
-
function refreshModels(client, baseURL, log, apiKey) {
|
|
480
|
-
if (lastRefresh && Date.now() - lastRefresh.at < REFRESH_TTL_MS) {
|
|
500
|
+
function refreshModels(client, baseURL, log, apiKey, force = false) {
|
|
501
|
+
if (!force && lastRefresh && Date.now() - lastRefresh.at < REFRESH_TTL_MS) {
|
|
481
502
|
log.info(`Using in-memory plexus model cache (${Object.keys(lastRefresh.models).length} models)`);
|
|
482
503
|
return Promise.resolve(lastRefresh.models);
|
|
483
504
|
}
|
|
@@ -540,10 +561,15 @@ var PlexusProviderPlugin = async (ctx) => {
|
|
|
540
561
|
const mergedOptions = merged["options"];
|
|
541
562
|
delete mergedOptions["baseURL"];
|
|
542
563
|
if (baseURL) {
|
|
543
|
-
log.info("Plexus baseURL configured;
|
|
564
|
+
log.info("Plexus baseURL configured; run /plexus-refresh to force a live model refresh");
|
|
544
565
|
} else {
|
|
545
566
|
log.info("Plexus baseURL not configured; skipping live refresh");
|
|
546
567
|
}
|
|
568
|
+
cfg.command ??= {};
|
|
569
|
+
cfg.command[PLEXUS_REFRESH_COMMAND] ??= {
|
|
570
|
+
template: "Refreshing Plexus models from the live server...",
|
|
571
|
+
description: "Force a live refresh of Plexus models and rewrite the on-disk cache"
|
|
572
|
+
};
|
|
547
573
|
try {
|
|
548
574
|
const mergedModels = merged["models"];
|
|
549
575
|
for (const id of ["gemini-3.5-flash", "claude-haiku-4-5", "small-fast"]) {
|
|
@@ -584,6 +610,28 @@ var PlexusProviderPlugin = async (ctx) => {
|
|
|
584
610
|
return cached ? toRuntimeModels(cached, provider) : {};
|
|
585
611
|
}
|
|
586
612
|
},
|
|
613
|
+
"command.execute.before": async (input, output) => {
|
|
614
|
+
if (input.command !== PLEXUS_REFRESH_COMMAND)
|
|
615
|
+
return;
|
|
616
|
+
const configResponse = await client.config.get();
|
|
617
|
+
const provider = configResponse.data?.provider?.[PLEXUS_PROVIDER_ID];
|
|
618
|
+
const storedAuth = await readStoredAuth();
|
|
619
|
+
const { baseURL, apiKey } = resolveConfig(provider, storedAuth?.metadata);
|
|
620
|
+
const key = storedAuth?.key ?? apiKey;
|
|
621
|
+
const textPart = (text) => ({ type: "text", text, synthetic: true });
|
|
622
|
+
if (!baseURL) {
|
|
623
|
+
output.parts = [textPart("Plexus refresh failed: no baseURL configured. Run /connect first.")];
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
const models = await refreshModels(client, baseURL, log, key, true);
|
|
628
|
+
output.parts = [
|
|
629
|
+
textPart(`Plexus models refreshed: ${Object.keys(models).length} models fetched from ${baseURL} and cached. Restart OpenCode to pick up the new model list.`)
|
|
630
|
+
];
|
|
631
|
+
} catch (e) {
|
|
632
|
+
output.parts = [textPart(`Plexus refresh failed: ${String(e)}. Existing cache left untouched.`)];
|
|
633
|
+
}
|
|
634
|
+
},
|
|
587
635
|
auth: {
|
|
588
636
|
provider: PLEXUS_PROVIDER_ID,
|
|
589
637
|
async loader(getAuth, providerInfo) {
|
|
@@ -647,6 +695,7 @@ export {
|
|
|
647
695
|
buildModels,
|
|
648
696
|
REFRESH_TTL_MS,
|
|
649
697
|
PlexusProviderPlugin,
|
|
698
|
+
PLEXUS_REFRESH_COMMAND,
|
|
650
699
|
PLEXUS_PROVIDER_NAME,
|
|
651
700
|
PLEXUS_PROVIDER_ID,
|
|
652
701
|
PLEXUS_PLUGIN_ID,
|