@byokit/openclaw 0.25.0 → 0.26.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/CHANGELOG.md +7 -0
- package/README.md +10 -7
- package/dist/bridge.js +3 -3
- package/dist/constants.d.ts +0 -2
- package/dist/constants.js +1 -7
- package/dist/keys.js +7 -7
- package/package.json +2 -2
- package/plugin/catalog.js +13 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.26.0 (2026-10-20)
|
|
6
|
+
|
|
7
|
+
- Dependency update: pins @byokit/accounts 0.30.0.
|
|
8
|
+
|
|
9
|
+
- FEAT: every key route now lists its own models: `kit.models(member, { authChoice })` answers the provider plugin's catalog joined by the vendor's own `/models` (not only OpenCode Go, OpenRouter and MiniMax), and `addKey({ ..., model })` takes any full `provider/model` id on any key route. An id the catalog lags is taken with the route's own capabilities instead of being refused; only a route with no catalog at all refuses a model.
|
|
10
|
+
- The bridge's Codex CLI route now names its two model rows itself instead of reading them from `@byokit/accounts`' catalogue, which no longer keeps model tiers; the rows the engine lists are unchanged.
|
|
11
|
+
|
|
5
12
|
## 0.25.0 (2026-10-20)
|
|
6
13
|
|
|
7
14
|
- Dependency update: pins @byokit/accounts 0.29.0.
|
package/README.md
CHANGED
|
@@ -159,10 +159,10 @@ other choice that takes no key (a subscription such as `openai`, a provider with
|
|
|
159
159
|
throws its cause before anything is sent.
|
|
160
160
|
`reason` is `auth` (provider refused the key) or `billing` (the provider's billing refused it; the kit words it and never passes
|
|
161
161
|
provider text): both are `invalid`, and an unchanged key must not be retried. `empty` is a blank key, rejected locally before any
|
|
162
|
-
provider call, also `invalid`. `model` is an unsupported route or
|
|
162
|
+
provider call, also `invalid`. `model` is an unsupported route or a model on a route with no catalog (`not_included`), or a model the engine did not
|
|
163
163
|
register (`retry`). `network` is a timeout or transport failure; `inference` is a provider reply in an unexpected format, an unknown
|
|
164
164
|
probe failure, or a typed request-validation refusal; `engine` is a control or preparation failure; `busy` is a rate limit or a
|
|
165
|
-
restarting engine. `not_included` covers an unsupported route,
|
|
165
|
+
restarting engine. `not_included` covers an unsupported route, a chosen model on a route with no catalog and a probe reporting the route unavailable;
|
|
166
166
|
every other failure is `retry`. Migrate from the old string result with `result.status === 'ok'`, not `result === 'ok'`.
|
|
167
167
|
An OpenCode Go plan login can act for several workspaces, and the Go gateway refuses its every call until one is named
|
|
168
168
|
(`403 Workspace selection is required`). Pass the workspace as `orgId` when you have it; it rides the probe and every run as
|
|
@@ -185,16 +185,19 @@ live catalog. Before a key exists it returns `[]` (a failed catalog probe with n
|
|
|
185
185
|
empty; with one it is an error) and runs no engine discovery. Only available key-lane
|
|
186
186
|
models can be saved; the ordinary member's sign-ins never supply the list. Pass a full catalog id, e.g.
|
|
187
187
|
`model: 'opencode-go/muse-spark-1.3-contributor'`, to `addKey` to save it as the lane's default after activation.
|
|
188
|
-
|
|
188
|
+
Every key route lists and takes models: its catalog is the provider plugin's own, joined by the vendor's own `/models`
|
|
189
|
+
list, and the kit keeps no provider or model list. An OpenRouter key is
|
|
189
190
|
pay-as-you-go with no plan, so it takes any model its own live catalog lists, named `openrouter/<id>`, e.g.
|
|
190
191
|
`model: 'openrouter/deepseek/deepseek-v4.1-flash-1017'`; an id already beginning with its own provider keeps one
|
|
191
192
|
prefix (`openrouter/auto`). A Google preview id is named and saved as the canonical id the engine keeps
|
|
192
193
|
(`openrouter/google/gemini-3.1-flash-lite-preview` becomes `openrouter/google/gemini-3.1-flash-lite`); the add
|
|
193
|
-
registers that one model with the engine
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
registers that one model with the engine (every provider but OpenCode Go and MiniMax, whose whole lists are registered).
|
|
195
|
+
A vendor's list can lag a model it already runs, so an id the catalog lacks is taken, not refused: it rides the route's
|
|
196
|
+
own models (the richest row's api, base URL and context), unpriced, and the app sees it missing from `models()` to warn.
|
|
197
|
+
A route with no catalog rows (its plugin has none) lists nothing, and a `model` there returns
|
|
198
|
+
`{ status: 'not_included', reason: 'model' }` and clears the replacement.
|
|
196
199
|
Before success, `addKey` waits for catalog registration and prepares the new key agent's full-catalog owner once.
|
|
197
|
-
|
|
200
|
+
A chosen model must also be in the engine's registered `models.providers[provider].models`
|
|
198
201
|
list before anything is sealed; otherwise the add clears the attempt and returns `{ status: 'retry', reason: 'model' }`.
|
|
199
202
|
Preparation failure clears the attempted key and returns `{ status: 'retry', reason: 'engine' }`; ordinary runs stay unchanged.
|
|
200
203
|
With no choice, today's starter stays.
|
package/dist/bridge.js
CHANGED
|
@@ -3,7 +3,6 @@ import { randomBytes } from 'node:crypto';
|
|
|
3
3
|
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { createServer } from 'node:net';
|
|
5
5
|
import { rm } from 'node:fs/promises';
|
|
6
|
-
import { provider } from '@byokit/accounts';
|
|
7
6
|
import { words } from './words.js';
|
|
8
7
|
import { HelperError } from './helpers.js';
|
|
9
8
|
/** Newline-framed JSON, one request per connection; anything larger is not a gate request. */
|
|
@@ -40,8 +39,9 @@ export function writePlugin(dir, o) {
|
|
|
40
39
|
name: 'BYOKit bridge',
|
|
41
40
|
activation: { onStartup: true },
|
|
42
41
|
cliBackends: ['openai-codex'],
|
|
43
|
-
// The
|
|
44
|
-
|
|
42
|
+
// The engine lists a plugin route's rows only from here. Named exception: the Codex CLI's own list (its app-server
|
|
43
|
+
// `model/list`) is not read yet, so the route names these two ids; a recorded real `model/list` reply lifts it.
|
|
44
|
+
modelCatalog: { providers: { 'openai-codex': { models: ['gpt-6-sol', 'gpt-6-luna'].map((id) => ({ id, name: id })) } }, discovery: { 'openai-codex': 'static' } },
|
|
45
45
|
contracts: { tools: o.tools.map((t) => t.name), ...(o.browser ? { agentToolResultMiddleware: ['openclaw', 'codex'] } : {}) },
|
|
46
46
|
configSchema: { type: 'object', additionalProperties: false },
|
|
47
47
|
};
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export declare const ENGINE_VERSION = "2026.8.35";
|
|
2
2
|
export declare const PROTOCOL_VERSION = 4;
|
|
3
|
-
export declare const LIVE_CATALOGS: string[];
|
|
4
3
|
export declare const REGISTERED_CATALOGS: string[];
|
|
5
|
-
export declare const OPEN_CATALOGS: string[];
|
|
6
4
|
export declare const OPERATOR_SCOPES: readonly ['operator.read', 'operator.write', 'operator.admin', 'operator.approvals', 'operator.questions', 'operator.pairing', 'operator.talk'];
|
package/dist/constants.js
CHANGED
|
@@ -2,15 +2,9 @@
|
|
|
2
2
|
// `needs-update`, never silently run.
|
|
3
3
|
export const ENGINE_VERSION = '2026.8.35';
|
|
4
4
|
export const PROTOCOL_VERSION = 4;
|
|
5
|
-
//
|
|
6
|
-
// pay-as-you-go: its key takes any model its own catalog lists, with no plan to include it.
|
|
7
|
-
export const LIVE_CATALOGS = ['opencode-go', 'openrouter', 'minimax'];
|
|
8
|
-
// Live catalogs the kit registers whole. The engine canonicalizes Google preview ids on config write, so OpenRouter's
|
|
5
|
+
// Live catalogs (5.6) the kit registers whole; every other key route registers its chosen model alone. The engine canonicalizes Google preview ids on config write, so OpenRouter's
|
|
9
6
|
// list collides with itself (`google/x-preview` and `google/x`); an OpenRouter add registers only the chosen model.
|
|
10
7
|
export const REGISTERED_CATALOGS = ['opencode-go', 'minimax'];
|
|
11
|
-
// Pay-as-you-go catalogs whose vendor list can lag a model that already runs: an id the list lacks is warned about, never
|
|
12
|
-
// refused, and takes the capabilities of the route's own models.
|
|
13
|
-
export const OPEN_CATALOGS = ['minimax'];
|
|
14
8
|
export const OPERATOR_SCOPES = [
|
|
15
9
|
'operator.read',
|
|
16
10
|
'operator.write',
|
package/dist/keys.js
CHANGED
|
@@ -5,7 +5,7 @@ import { activate } from './activation.js';
|
|
|
5
5
|
import { keyAgentId, MEMBER_ID, KEY_PREFIX, untilListed } from './members.js';
|
|
6
6
|
import { providerOf } from './pick.js';
|
|
7
7
|
import { kitId, modelsOf } from './accounts.js';
|
|
8
|
-
import {
|
|
8
|
+
import { REGISTERED_CATALOGS } from './constants.js';
|
|
9
9
|
export function createKeys(ctx) {
|
|
10
10
|
// Serialize key replacement and key runs, so an in-flight run never sees a half-installed replacement.
|
|
11
11
|
const tails = new Map();
|
|
@@ -96,7 +96,7 @@ export function createKeys(ctx) {
|
|
|
96
96
|
// The vendor's list can lag a model that runs: an id it lacks rides the route's own models (the richest row's api,
|
|
97
97
|
// base URL and context, every input modality any row takes), unpriced. Nothing is added to an empty list.
|
|
98
98
|
const unlisted = (provider, rows, model) => {
|
|
99
|
-
if (!rows.length || rows.some((m) => kitId(provider, String(m.id)) === model))
|
|
99
|
+
if (!rows.length || !model.startsWith(`${provider}/`) || rows.some((m) => kitId(provider, String(m.id)) === model))
|
|
100
100
|
return [];
|
|
101
101
|
const base = rows.reduce((a, b) => (Number(b.contextWindow) || 0) > (Number(a.contextWindow) || 0) ? b : a);
|
|
102
102
|
const input = ['text', 'image'].filter((i) => rows.some((m) => Array.isArray(m.input) && m.input.includes(i)));
|
|
@@ -108,7 +108,7 @@ export function createKeys(ctx) {
|
|
|
108
108
|
if (!check(member))
|
|
109
109
|
throw new Error('Choose your own member.');
|
|
110
110
|
const route = keyRoute(o.authChoice);
|
|
111
|
-
if (route.readiness !== 'ready'
|
|
111
|
+
if (route.readiness !== 'ready')
|
|
112
112
|
return [];
|
|
113
113
|
try {
|
|
114
114
|
return listed(route.provider, await catalog(member, route.provider));
|
|
@@ -138,7 +138,7 @@ export function createKeys(ctx) {
|
|
|
138
138
|
if (!o.apiKey.trim())
|
|
139
139
|
return failure('invalid', 'empty');
|
|
140
140
|
const route = keyRoute(o.authChoice);
|
|
141
|
-
if (route.readiness !== 'ready'
|
|
141
|
+
if (route.readiness !== 'ready')
|
|
142
142
|
return failure('not_included', 'model');
|
|
143
143
|
const model = o.model === undefined ? undefined : canonicalModel(o.model);
|
|
144
144
|
// A workspace names a Go login's own workspace only; another route takes no header, and a
|
|
@@ -227,7 +227,7 @@ export function createKeys(ctx) {
|
|
|
227
227
|
if (result.late)
|
|
228
228
|
return abandon(failure('retry', 'busy'));
|
|
229
229
|
const rows = model === undefined ? [] : await catalog(member, route.provider);
|
|
230
|
-
const lagging = model !== undefined
|
|
230
|
+
const lagging = model !== undefined ? unlisted(route.provider, rows, model) : [];
|
|
231
231
|
rows.push(...lagging);
|
|
232
232
|
if (model !== undefined && !listed(route.provider, rows).some((m) => m.id === model && m.available))
|
|
233
233
|
return abandon(failure('not_included', 'model'));
|
|
@@ -240,10 +240,10 @@ export function createKeys(ctx) {
|
|
|
240
240
|
}
|
|
241
241
|
if (ac.signal.aborted)
|
|
242
242
|
return abandon(cancelled());
|
|
243
|
-
// A
|
|
243
|
+
// A catalog the kit does not register whole (OpenRouter's, and any other provider's) registers the chosen model alone.
|
|
244
244
|
if (model !== undefined && (!REGISTERED_CATALOGS.includes(route.provider) || lagging.length))
|
|
245
245
|
await ctx.registerModels(route.provider, rows.filter((m) => kitId(route.provider, String(m.id)) === model)).catch(() => { });
|
|
246
|
-
if (model !== undefined &&
|
|
246
|
+
if (model !== undefined && !(await registered(route.provider, model)))
|
|
247
247
|
return abandon(failure('retry', 'model'));
|
|
248
248
|
if (!(await control(member, 'seal', undefined, model)).ok)
|
|
249
249
|
return abandon(failure('retry', 'engine'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byokit/openclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "The OpenClaw runtime kit: the pinned engine's full operator surface with plain-words helpers for members, sign-in, runs and approvals.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"prepack": "tsc -b && node ../../scripts/fix-words-dts.cjs"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@byokit/accounts": "0.
|
|
63
|
+
"@byokit/accounts": "0.30.0",
|
|
64
64
|
"@byokit/pair": "0.10.4",
|
|
65
65
|
"@byokit/relay": "0.8.2",
|
|
66
66
|
"@byokit/discover": "0.8.4",
|
package/plugin/catalog.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// byokit.catalog (5.6 live provider catalogs): the provider plugin's own live model list, with each model's api and
|
|
2
|
-
// baseUrl from its catalog data, so the kit can register models embedded runs
|
|
3
|
-
// catalog loader stops at a registered list; the provider's catalog hook does not.
|
|
2
|
+
// baseUrl from its catalog data, joined by the vendor's own model list, so the kit can register models embedded runs
|
|
3
|
+
// cannot resolve otherwise. The engine's catalog loader stops at a registered list; the provider's catalog hook does not.
|
|
4
4
|
export function registerCatalog(api) {
|
|
5
5
|
api.registerGatewayMethod('byokit.catalog', async ({ params, respond }) => {
|
|
6
6
|
try {
|
|
@@ -11,7 +11,8 @@ export function registerCatalog(api) {
|
|
|
11
11
|
const { resolveApiKeyForProvider } = await import('openclaw/plugin-sdk/provider-auth-runtime');
|
|
12
12
|
const config = getRuntimeConfig();
|
|
13
13
|
const hook = resolvePluginProviders({ config, env: process.env }).find((p) => p.id === provider)?.catalog;
|
|
14
|
-
if (!provider
|
|
14
|
+
if (!provider) return respond(true, { ok: false, reason: 'failed' });
|
|
15
|
+
if (typeof hook?.run !== 'function') return respond(true, { ok: true, models: [] }); // a provider with no catalog lists nothing
|
|
15
16
|
// The first agent holding the provider's key; every account of one provider sees the same catalog.
|
|
16
17
|
let unreadable = false;
|
|
17
18
|
for (const agentId of new Set([...Object.keys(config.agents?.entries ?? {}), 'main'])) {
|
|
@@ -38,13 +39,16 @@ export function registerCatalog(api) {
|
|
|
38
39
|
const models = [...live.models, ...(pinned?.models ?? []).filter((m) => !liveIds.has(m.id))]
|
|
39
40
|
.map(({ provider: _p, apiKey: _k, headers: _h, ...model }) =>
|
|
40
41
|
(endpoint.has(model.api) ? { ...model, baseUrl: endpoint.get(model.api) } : model));
|
|
41
|
-
//
|
|
42
|
-
// row's api, base URL and capabilities (
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
// A plugin catalog can be a fixed list (MiniMax's): the vendor's own `/models` lists newer ids, which join it with
|
|
43
|
+
// the richest row's api, base URL and capabilities (a vendor list carries ids only). Same rule as
|
|
44
|
+
// @byokit/accounts' live lists; another api, or a failed fetch, keeps the plugin's rows.
|
|
45
|
+
const base = models.reduce((a, b) => (b.contextWindow ?? 0) > (a.contextWindow ?? 0) ? b : a);
|
|
46
|
+
const openai = String(base.api).startsWith('openai-');
|
|
47
|
+
if (base.baseUrl && (openai || base.api === 'anthropic-messages')) {
|
|
45
48
|
const input = ['text', 'image'].filter((i) => models.some((m) => m.input?.includes(i)));
|
|
46
|
-
const
|
|
47
|
-
|
|
49
|
+
const bearer = openai || /^minimax/.test(provider);
|
|
50
|
+
const listed = await fetch(`${String(base.baseUrl).replace(/\/$/, '')}${openai ? '/models' : '/v1/models'}`, { signal: AbortSignal.timeout(10_000),
|
|
51
|
+
headers: bearer ? { authorization: `Bearer ${apiKey}`, 'anthropic-version': '2023-06-01' } : { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' } })
|
|
48
52
|
.then((r) => r.ok ? r.json() : null, () => null);
|
|
49
53
|
const have = new Set(models.map((m) => m.id));
|
|
50
54
|
for (const m of Array.isArray(listed?.data) ? listed.data : [])
|