@byokit/openclaw 0.14.0 → 0.14.4
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 +38 -0
- package/README.md +12 -2
- package/dist/accounts.js +11 -5
- package/dist/approvals.js +1 -1
- package/dist/auth-status.js +1 -1
- package/dist/auth-store.js +2 -2
- package/dist/bridge.js +1 -1
- package/dist/browser/host.js +3 -3
- package/dist/browser/verify.js +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +7 -3
- package/dist/constants.d.ts +1 -1
- package/dist/day-usage.js +1 -1
- package/dist/device.js +7 -7
- package/dist/engine.d.ts +10 -0
- package/dist/engine.js +45 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +9 -8
- package/dist/keys.d.ts +14 -1
- package/dist/keys.js +56 -8
- package/dist/kit.d.ts +15 -1
- package/dist/kit.js +99 -31
- package/dist/link.js +5 -5
- package/dist/members.d.ts +3 -0
- package/dist/members.js +10 -0
- package/dist/migrate.js +3 -3
- package/dist/pick.js +1 -1
- package/dist/plugins.d.ts +17 -0
- package/dist/plugins.js +40 -0
- package/dist/routes.json +288 -288
- package/dist/runs.js +6 -6
- package/dist/signin.d.ts +5 -0
- package/dist/signin.js +55 -5
- package/dist/testing/browser.js +2 -2
- package/dist/testing/contract.js +3 -3
- package/dist/testing/fake-gateway.js +14 -8
- package/dist/testing/index.js +4 -4
- package/dist/testing/model-stub.d.ts +4 -0
- package/dist/testing/model-stub.js +8 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js +1 -1
- package/engine/plugins.json +150 -0
- package/package.json +6 -6
- package/policy/policy.mjs +43 -14
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.14.4 (2026-10-17)
|
|
6
|
+
|
|
7
|
+
- Dependency update: pins @byokit/relay 0.8.0.
|
|
8
|
+
- Dependency update: pins @byokit/pair 0.10.2.
|
|
9
|
+
- Dependency update: pins @byokit/accounts 0.24.3.
|
|
10
|
+
|
|
11
|
+
- FIX: `routes()` now describes the engine the kit actually pins. The generated table was still built from the 2026.8.1 pin while `ENGINE_VERSION` is 2026.8.35, so every row said `source: "... (2026.8.1)"` and `revision: "2026.8.1"`. `scripts/gen-routes.ts` pins 2026.8.35 (upstream commit from `engine/patches.json`), reads manifest directories only (the 2026.8.35 source tree keeps an `extensions/.npmignore` file), and a new public-API journey test fails whenever the table names anything but `ENGINE_VERSION`. All 96 rows re-verified against the 2026.8.35 manifests: the choice set is identical (nothing added or removed); every row now carries `(2026.8.35)`.
|
|
12
|
+
|
|
13
|
+
## 0.14.3 (2026-10-17)
|
|
14
|
+
|
|
15
|
+
- Dependency update: pins @byokit/ui 0.9.0.
|
|
16
|
+
- Dependency update: pins @byokit/discover 0.8.3.
|
|
17
|
+
- Dependency update: pins @byokit/accounts 0.24.2.
|
|
18
|
+
|
|
19
|
+
- FIX: `addKey` for an OpenCode Go plan key with no `orgId` no longer saves a key whose every call is refused with `403 Workspace selection is required`. The add lists the key's workspaces first (`openCodeWorkspaces` from `@byokit/accounts/opencode-go`): one is used as the `x-opencode-org-id` header; several resolve the new `AddKeyResult` `{ status: 'needs_workspace', workspaces: [{ id, name }] }` for the app's pick (add again with `orgId`); a key the console refuses adds with no header. A listing that could not be read resolves `{ status: 'retry', reason: 'network' }` and changes nothing. An explicit `orgId` is used as before.
|
|
20
|
+
|
|
21
|
+
## 0.14.2 (2026-10-16)
|
|
22
|
+
|
|
23
|
+
- Dependency update: pins @byokit/relay 0.7.1.
|
|
24
|
+
- Dependency update: pins @byokit/accounts 0.24.1.
|
|
25
|
+
|
|
26
|
+
- FIX: `addKey` takes `orgId` for the Go live-catalog route and writes it into the engine's provider config (`models.providers['opencode-go'].headers['x-opencode-org-id']`) before the activation, so a multi-workspace login token is accepted — the engine's own probe and every run carry the header, and the first chat runs. The slot is provider-wide (shared by every member's key on one engine): a replacement key with no `orgId` keeps the standing workspace, `removeKey` drops it, and naming an `orgId` on a route with no workspace is refused `not_included`/`model` instead of silently dropped. Keys and workspace ids never reach logs, errors or views.
|
|
27
|
+
|
|
28
|
+
## 0.14.1 (2026-10-16)
|
|
29
|
+
|
|
30
|
+
- Dependency update: pins @byokit/ui 0.8.0.
|
|
31
|
+
- Dependency update: pins @byokit/discover 0.8.2.
|
|
32
|
+
- Dependency update: pins @byokit/relay 0.7.0.
|
|
33
|
+
- Dependency update: pins @byokit/pair 0.10.1.
|
|
34
|
+
- Dependency update: pins @byokit/accounts 0.24.0.
|
|
35
|
+
|
|
36
|
+
- FIX: when the engine answers a sign-in's cancel `cancelled`, `done` now also waits (up to 10 s) for it to free its setup admission, so a sign-in started right after is admitted instead of ending `why: 'busy'`.
|
|
37
|
+
- FIX: The kit now resolves `stateDir` to one absolute path through no symlink before it uses it. A native Claude CLI sign-in (`claude-cli`, browser or phone) with a relative `stateDir`, or one under a symlinked folder such as macOS `/tmp` or `os.tmpdir()`, failed with "The app supplied invalid account options." Engine paths the kit reports (for example the bridge socket and `identityPath`) now use that resolved path.
|
|
38
|
+
- FIX: `removeKey` (and `signOut`, which calls it for that provider) also drops the lane's `<provider>.key` index entry, so `accounts()` no longer lists a removed key; a scoped removal keeps another provider's entry.
|
|
39
|
+
- FIX: `pick` builds its live room/models reads from the filtered pick rows, so an Auto pick for a member whose only other account is the unnamed key lane makes no `models.list`/`models.authStatus` calls for the lane it never consults.
|
|
40
|
+
- FIX: the key-lane docs now scope the "no `<member>--<hex6>` per later key" claim to the `addKey` path; a plan key added with `addAccount` when the member agent already holds that provider still mints one `<member>--<hex6>` agent per key. The Choosing spec names the kit-only lane filter.
|
|
41
|
+
- The README states platform support: Linux and macOS x64/arm64 supported, Windows unsupported. CI runs the offline suite on macOS.
|
|
42
|
+
|
|
5
43
|
## 0.14.0 (2026-10-15)
|
|
6
44
|
|
|
7
45
|
- Dependency update: pins @byokit/accounts 0.23.0.
|
package/README.md
CHANGED
|
@@ -157,6 +157,13 @@ register (`retry`). `network` is a timeout or transport failure; `inference` is
|
|
|
157
157
|
probe failure, or a typed request-validation refusal; `engine` is a control or preparation failure; `busy` is a rate limit or a
|
|
158
158
|
restarting engine. `not_included` covers an unsupported route, an unknown chosen model and a probe reporting the route unavailable;
|
|
159
159
|
every other failure is `retry`. Migrate from the old string result with `result.status === 'ok'`, not `result === 'ok'`.
|
|
160
|
+
An OpenCode Go plan login can act for several workspaces, and the Go gateway refuses its every call until one is named
|
|
161
|
+
(`403 Workspace selection is required`). Pass the workspace as `orgId` when you have it; it rides the probe and every run as
|
|
162
|
+
`x-opencode-org-id`. Without `orgId` the add lists the key's workspaces first (the console listing `@byokit/accounts` uses):
|
|
163
|
+
one is used unasked; several resolve `{ status: 'needs_workspace', workspaces: [{ id, name }] }` with nothing saved, so show
|
|
164
|
+
the pick (the names) and add again with `orgId: '<picked id>'`; a key the console refuses (a workspace-bound service key) adds
|
|
165
|
+
with no workspace. A listing that could not be read (offline, a server error) resolves `{ status: 'retry', reason: 'network' }`
|
|
166
|
+
and changes nothing. Workspace ids are row ids, never secrets. Another route refuses an `orgId` with `not_included`/`model`.
|
|
160
167
|
A member that is not your own throws. `retry` means the engine may have taken the key but it was not kept
|
|
161
168
|
sealed (for example a gateway restart ran late); no usable key was kept, so add it again. Failures never contain keys, key fragments, headers or raw engine diagnostics. Label the option **API key (billed per use)**. Select only `routes()` rows with
|
|
162
169
|
`keyEntry: true`; API rows remain `offer: false`. Each route also carries its engine `revision`, `checked` date,
|
|
@@ -381,7 +388,7 @@ streams, [`examples/openclaw-kit`](../../examples/openclaw-kit) for a whole app,
|
|
|
381
388
|
Subscription sign-ins are offered by default. API-billed routes remain in `kit.routes()` with `offer: false`;
|
|
382
389
|
apps can offer them when the app or person opts in, labelled API key (billed per use). Each `Route` carries `billing` (`subscription`, `api` or
|
|
383
390
|
`local`) so the app can label what is billed. Proxy routes, compatibility aliases, local runtimes stay off. Native Claude Code sign-in is offered by default. A plan key (OpenCode Go: `kit.addAccount(member, { authChoice: 'opencode-go' }, on)`) asks for `Plan key`; `paste` it and the engine live-tests it before `done`. A paired device starts the same sign-in with either `via` and pastes the key over the sealed link, which keeps it on the host. A run reaches the pinned engine's static Go models (default `opencode-go/deepseek-v4-pro`); the kit keeps OpenCode Go's `x-opencode-session` header on every request, the setup probe included. A live-only Go model such as Muse needs no app config: the kit registers the plan's live catalog with each model's API, so `run({ model: 'opencode-go/muse-spark-1.3-contributor' })` runs on the account (or with `auth: 'apiKey'` on a Go key); an app that sets `config.models.providers['opencode-go'].models` keeps its own list. A choice the pinned Gateway cannot set up has readiness `no_upstream_flow` and is never offered. Pass the accounts the app offers in `KitOptions.offered` (the provider, auth choice or alias each route carries, e.g. `'openrouter'`); the kit allow-lists the plugin of every route those name, so an opted-in sign-in is not blocked. An app can still name a plugin directly in `config.plugins.allow`, and pass the route's `choice` explicitly to `kit.signIn()`.
|
|
384
|
-
Offered provider plugins are allowed by default alongside the app's plugin ids.
|
|
391
|
+
Offered provider plugins are allowed by default alongside the app's plugin ids. A route whose provider plugin ships outside the engine (Z.AI's GLM Coding Plan, for one) has readiness `needs_plugin` until the app calls `await kit.installPlugin(route.needs.plugin)`: it installs that plugin into the kit's own engine at exactly the engine's version and pinned npm integrity, keeps it allowed across restarts, and rejects `PluginInstallError` (`code` `version` or `integrity`) with nothing installed on a mismatch. Discovery never installs.
|
|
385
392
|
|
|
386
393
|
## API at a glance
|
|
387
394
|
|
|
@@ -568,7 +575,8 @@ for await (const e of oc.run('Say hello.')) console.log(e);
|
|
|
568
575
|
|
|
569
576
|
## Isolation and billing
|
|
570
577
|
|
|
571
|
-
The kit writes only under the `stateDir` the app passes
|
|
578
|
+
The kit writes only under the `stateDir` the app passes, resolved to one absolute path through no symlink (so a
|
|
579
|
+
relative path or macOS `os.tmpdir()` works, and paths the kit reports use the resolved form). It spawns the engine with an explicit isolated environment
|
|
572
580
|
(never `process.env`), reads no environment variables except `PATH` to find `npm`, and never bills an API behind the
|
|
573
581
|
person's back (memory search is never a paid provider).
|
|
574
582
|
|
|
@@ -685,6 +693,8 @@ retained-login cleanup and sealed approval limits.
|
|
|
685
693
|
|
|
686
694
|
## Status
|
|
687
695
|
|
|
696
|
+
Linux and macOS x64/arm64 are supported; Windows is unsupported.
|
|
697
|
+
|
|
688
698
|
Pinned to OpenClaw `2026.8.35` (protocol 4); `ENGINE_VERSION` and `PROTOCOL_VERSION` carry the pin. Signatures are
|
|
689
699
|
frozen (docs/runtime-kits.md §5).
|
|
690
700
|
|
package/dist/accounts.js
CHANGED
|
@@ -10,11 +10,11 @@ import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync
|
|
|
10
10
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { DatabaseSync } from 'node:sqlite';
|
|
13
|
-
import { providerOf, resolveSelection } from
|
|
14
|
-
import { agentOf, memberOf as baseMemberOf } from
|
|
15
|
-
import { KEY_PREFIX, MEMBER_ID, NEW_MEMBER_ID, keyAgentId, untilListed } from
|
|
16
|
-
import { routes, routeFor } from
|
|
17
|
-
import { words } from
|
|
13
|
+
import { providerOf, resolveSelection } from './pick.js';
|
|
14
|
+
import { agentOf, memberOf as baseMemberOf } from './attribution.js';
|
|
15
|
+
import { KEY_PREFIX, MEMBER_ID, NEW_MEMBER_ID, keyAgentId, untilListed, untilUnlisted } from './members.js';
|
|
16
|
+
import { routes, routeFor } from './routes.js';
|
|
17
|
+
import { words } from './words.js';
|
|
18
18
|
const record = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
19
19
|
// `models.list { agentId, view: 'default' }` rows of the account's provider, mapped to `provider/model`. The pin
|
|
20
20
|
// reports no per-plan availability, so `why: 'plan'` only comes from a run ending `plan`, never from here.
|
|
@@ -1007,6 +1007,9 @@ export function createAccounts(ctx) {
|
|
|
1007
1007
|
if (listed.profiles.length)
|
|
1008
1008
|
throw new Error('removeAccount: the account is still signing in');
|
|
1009
1009
|
await ctx.request('agents.delete', { agentId: agent, deleteFiles: false });
|
|
1010
|
+
// The roster hot-reload applies a beat after the delete answer: wait until the agent is gone, so a
|
|
1011
|
+
// roster read right after the removal no longer sees it.
|
|
1012
|
+
await untilUnlisted(ctx.request, agent);
|
|
1010
1013
|
}
|
|
1011
1014
|
rmSync(agentDir(agent), { recursive: true, force: true });
|
|
1012
1015
|
// Already holding the index lock: write directly instead of `edit`, which would wait on itself.
|
|
@@ -1027,6 +1030,9 @@ export function createAccounts(ctx) {
|
|
|
1027
1030
|
if (created) {
|
|
1028
1031
|
await ctx.request('byokit.accounts', { action: 'clear', agentId: agent });
|
|
1029
1032
|
await ctx.request('agents.delete', { agentId: agent, deleteFiles: false });
|
|
1033
|
+
// The roster hot-reload applies a beat after the delete answer: `done` resolves only once the agent is
|
|
1034
|
+
// gone, so a roster read right after a cancelled sign-in no longer sees it.
|
|
1035
|
+
await untilUnlisted(ctx.request, agent);
|
|
1030
1036
|
}
|
|
1031
1037
|
rmSync(agentDir(agent), { recursive: true, force: true });
|
|
1032
1038
|
await edit(member, (file) => { delete file.accounts[id]; });
|
package/dist/approvals.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Native approvals: exec.approval.*, plugin.approval.* and question.* surfaced through the same Approval shape (5.9, O5).
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
|
-
import { MEMBER_ID } from
|
|
3
|
+
import { MEMBER_ID } from './members.js';
|
|
4
4
|
const strip = (a) => {
|
|
5
5
|
const { native, ...approval } = a;
|
|
6
6
|
void native;
|
package/dist/auth-status.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { signedInProviders, USABLE } from
|
|
3
|
+
import { signedInProviders, USABLE } from './runs.js';
|
|
4
4
|
/** Native Claude login stays in Claude Code. The pin's profile status does not include this synthetic auth;
|
|
5
5
|
* setup detection asks the CLI itself, without handing credentials to the kit. */
|
|
6
6
|
export async function authStatus(request, agentId, refresh = false, native = false) {
|
package/dist/auth-store.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, writeFileSync, closeSync, fsyncSync, openSync } from 'node:fs';
|
|
8
8
|
import { createHash } from 'node:crypto';
|
|
9
9
|
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
10
|
-
import { EngineAlreadyRunningError, pidAlive as live } from
|
|
11
|
-
import { damagedAccountMembers, heldBackAgent, removedAccountAgents, sweepOrphans } from
|
|
10
|
+
import { EngineAlreadyRunningError, pidAlive as live } from './engine-status.js';
|
|
11
|
+
import { damagedAccountMembers, heldBackAgent, removedAccountAgents, sweepOrphans } from './accounts.js';
|
|
12
12
|
const agentDirOf = (path) => path.startsWith('state/agents/') ? path.split('/')[2] : undefined;
|
|
13
13
|
const encoder = new TextEncoder();
|
|
14
14
|
const archive = (name) => !name.endsWith('.sealed') && (/^(auth-profiles|auth-state|auth|oauth)\.json\.(migrated-.+|sqlite-import\..+\.bak)$/.test(name)
|
package/dist/bridge.js
CHANGED
|
@@ -3,7 +3,7 @@ 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 { words } from
|
|
6
|
+
import { words } from './words.js';
|
|
7
7
|
/** Newline-framed JSON, one request per connection; anything larger is not a gate request. */
|
|
8
8
|
const FRAME_CAP = 1_000_000;
|
|
9
9
|
/**
|
package/dist/browser/host.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Internal host runtime (5.17). Production handoff is deliberately unprotected until W7/O17 qualification.
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
|
-
import { BrowserStoreError, checkUrl, validOrigin } from
|
|
4
|
-
import { validateVerifiers, verifySignIn } from
|
|
5
|
-
import { dispatchResume, newResume, recoverResume } from
|
|
3
|
+
import { BrowserStoreError, checkUrl, validOrigin } from './store.js';
|
|
4
|
+
import { validateVerifiers, verifySignIn } from './verify.js';
|
|
5
|
+
import { dispatchResume, newResume, recoverResume } from './resume.js';
|
|
6
6
|
export class SignInRefused extends Error {
|
|
7
7
|
why;
|
|
8
8
|
constructor(why) { super(`sign-in refused: ${why}`); this.why = why; }
|
package/dist/browser/verify.js
CHANGED
package/dist/config.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function reconcileConfig(saved: object | undefined, o: {
|
|
|
12
12
|
app?: object;
|
|
13
13
|
installPolicy?: KitOptions['installPolicy'];
|
|
14
14
|
offered?: readonly string[];
|
|
15
|
+
plugins?: readonly string[];
|
|
15
16
|
browser?: {
|
|
16
17
|
profiles: Record<string, {
|
|
17
18
|
cdpUrl: string;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dirname, join } from 'node:path';
|
|
2
|
-
import { routes } from
|
|
3
|
-
import {
|
|
2
|
+
import { routes } from './routes.js';
|
|
3
|
+
import { pluginPins } from './plugins.js';
|
|
4
|
+
import { MEMBER_ID, KEY_PREFIX, BASE_AGENT, keyAgentId } from './members.js';
|
|
4
5
|
const object = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
5
6
|
const merge = (a, b) => {
|
|
6
7
|
for (const [k, v] of Object.entries(b))
|
|
@@ -84,7 +85,9 @@ export function reconcileConfig(saved, o) {
|
|
|
84
85
|
...routes().filter(route => route.offerPolicy === 'default' && !route.needs?.plugin && route.plugin)
|
|
85
86
|
.map(route => route.plugin),
|
|
86
87
|
// Plus every explicitly offered route the app names, so an opted-in sign-in (OpenRouter) is not blocked.
|
|
87
|
-
...offeredPlugins(o.offered)
|
|
88
|
+
...offeredPlugins(o.offered),
|
|
89
|
+
// Plus every external plugin the kit installed (B7): an app's own allow list must not disable it at the next boot.
|
|
90
|
+
...o.plugins ?? []])];
|
|
88
91
|
c.plugins.entries ??= {};
|
|
89
92
|
c.plugins.entries[o.pluginId] = merge(c.plugins.entries[o.pluginId] ?? {}, { hooks: { timeouts: { before_tool_call: 200_000 } } });
|
|
90
93
|
if (o.browser) {
|
|
@@ -112,6 +115,7 @@ export function reconcileConfig(saved, o) {
|
|
|
112
115
|
passEnv: ['OPENCLAW_STATE_DIR'], env: {
|
|
113
116
|
BYOKIT_TRUSTED_SKILLS: o.installPolicy?.trustedSkills ?? '',
|
|
114
117
|
BYOKIT_OWN_ROOTS: JSON.stringify(o.installPolicy?.ownRoots ?? []),
|
|
118
|
+
BYOKIT_PLUGIN_PINS: JSON.stringify(Object.values(pluginPins())),
|
|
115
119
|
},
|
|
116
120
|
} };
|
|
117
121
|
return c;
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const ENGINE_VERSION = "2026.8.35";
|
|
2
2
|
export declare const PROTOCOL_VERSION = 4;
|
|
3
3
|
export declare const LIVE_CATALOGS: string[];
|
|
4
|
-
export declare const OPERATOR_SCOPES: readonly [
|
|
4
|
+
export declare const OPERATOR_SCOPES: readonly ['operator.read', 'operator.write', 'operator.admin', 'operator.approvals', 'operator.questions', 'operator.pairing', 'operator.talk'];
|
package/dist/day-usage.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Portable additive day reader. No host files, pricing guesses or side totals for persisted turns.
|
|
2
|
-
import { agentUsageOf } from
|
|
2
|
+
import { agentUsageOf } from './usage.js';
|
|
3
3
|
const object = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
4
4
|
const count = (v) => typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
5
5
|
const text = (v) => typeof v === 'string' && v.length > 0 && v.length <= 512;
|
package/dist/device.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { openBoxFromSeed, boxKeyPairFromSeed } from '@byokit/seal';
|
|
2
|
-
import { memberOf } from
|
|
3
|
-
import { toAccountView } from
|
|
4
|
-
import { b64urlDecode, b64urlEncode, openNotice } from
|
|
2
|
+
import { memberOf } from './attribution.js';
|
|
3
|
+
import { toAccountView } from './words.js';
|
|
4
|
+
import { b64urlDecode, b64urlEncode, openNotice } from './notices.js';
|
|
5
5
|
/**
|
|
6
6
|
* The host ended a stream with its own words (e.g. a refusal). Portable twin of link's PublicLinkError:
|
|
7
7
|
* same shape ({ code: 'failed', sealed: true }) so a screen can show .message the same way. A device-side
|
|
@@ -418,8 +418,8 @@ export function openclawDevice(link) {
|
|
|
418
418
|
};
|
|
419
419
|
}
|
|
420
420
|
// Portable, re-exported by ./device (7.3).
|
|
421
|
-
export { openNotice } from
|
|
422
|
-
export { readAgentUsage, agentUsageOf } from
|
|
423
|
-
export { readAgentDayUsage } from
|
|
421
|
+
export { openNotice } from './notices.js';
|
|
422
|
+
export { readAgentUsage, agentUsageOf } from './usage.js';
|
|
423
|
+
export { readAgentDayUsage } from './day-usage.js';
|
|
424
424
|
// The kit's sentences, so a phone or browser shows the same words the computer does (5.14).
|
|
425
|
-
export { stateWords, toAccountView, words } from
|
|
425
|
+
export { stateWords, toAccountView, words } from './words.js';
|
package/dist/engine.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { remoteCliHost } from '@byokit/accounts/cli';
|
|
|
2
2
|
import { AuthStore, AuthStoreSealSizeError } from './auth-store.ts';
|
|
3
3
|
import type { KitOptions } from './kit.ts';
|
|
4
4
|
import type { KitState, ToolSpec } from './types.ts';
|
|
5
|
+
/** The app's stateDir as an absolute path with no symlinked ancestor (macOS /tmp and /var are symlinks):
|
|
6
|
+
* the native Claude login's accounts store refuses any other, so a relative or symlinked stateDir failed it. */
|
|
7
|
+
export declare function canonicalStateDir(dir: string): string;
|
|
5
8
|
export type EngineOptions = Pick<KitOptions, 'stateDir' | 'engineDir' | 'npmPath' | 'enginePath' | 'config' | 'offered' | 'appOwnedSessions' | 'installPolicy' | 'log' | 'bridge' | 'authSeal'> & {
|
|
6
9
|
pluginId: string;
|
|
7
10
|
tools: ToolSpec[];
|
|
@@ -50,6 +53,13 @@ export declare class Engine {
|
|
|
50
53
|
private freePort;
|
|
51
54
|
prepare(): Promise<void>;
|
|
52
55
|
private prepareOnce;
|
|
56
|
+
/** B7: the engine's own installer for one pinned external plugin, in the isolated engine env with the kit's npm
|
|
57
|
+
* first on PATH (the installer runs `npm`). The explicit kit call is the consent; the install policy holds the pin and
|
|
58
|
+
* its integrity. Resolves the installer's exit code and output tail; the kit then restarts the gateway to load it. */
|
|
59
|
+
installPlugin(spec: string): Promise<{
|
|
60
|
+
code: number | null;
|
|
61
|
+
output: string;
|
|
62
|
+
}>;
|
|
53
63
|
doctorContext(): {
|
|
54
64
|
entry: string;
|
|
55
65
|
env: Record<string, string>;
|
package/dist/engine.js
CHANGED
|
@@ -2,18 +2,31 @@ import { spawn, spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { randomBytes, generateKeyPairSync, randomUUID } from 'node:crypto';
|
|
3
3
|
import { accessSync, constants, closeSync, existsSync, mkdirSync, openSync, readFileSync, writeFileSync, copyFileSync, rmSync, realpathSync, statSync } from 'node:fs';
|
|
4
4
|
import { createServer } from 'node:net';
|
|
5
|
-
import { basename, join, dirname } from 'node:path';
|
|
5
|
+
import { basename, join, dirname, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
8
8
|
import { cliAccounts, remoteCliHost } from '@byokit/accounts/cli';
|
|
9
|
-
import { AuthStore, AuthStoreUnreadableError, AuthStoreSealSizeError } from
|
|
10
|
-
import { EngineAlreadyRunningError, pidAlive, StartedProcesses } from
|
|
11
|
-
import { EnginePatchError, atomic, prepareEngineSet, processStartTime, readPatchSet, sha256, verifyEngineSet } from
|
|
12
|
-
import { ENGINE_VERSION } from
|
|
13
|
-
import { appendUsageBoot } from
|
|
14
|
-
import { reconcileConfig, appRecoveryPrefixes } from
|
|
15
|
-
import { writePlugin, resolveBridge } from
|
|
16
|
-
import { sweepOrphans } from
|
|
9
|
+
import { AuthStore, AuthStoreUnreadableError, AuthStoreSealSizeError } from './auth-store.js';
|
|
10
|
+
import { EngineAlreadyRunningError, pidAlive, StartedProcesses } from './engine-status.js';
|
|
11
|
+
import { EnginePatchError, atomic, prepareEngineSet, processStartTime, readPatchSet, sha256, verifyEngineSet } from './engine-patches.js';
|
|
12
|
+
import { ENGINE_VERSION } from './constants.js';
|
|
13
|
+
import { appendUsageBoot } from './usage-boots.js';
|
|
14
|
+
import { reconcileConfig, appRecoveryPrefixes } from './config.js';
|
|
15
|
+
import { writePlugin, resolveBridge } from './bridge.js';
|
|
16
|
+
import { sweepOrphans } from './accounts.js';
|
|
17
|
+
import { installedPlugins } from './plugins.js';
|
|
18
|
+
/** The app's stateDir as an absolute path with no symlinked ancestor (macOS /tmp and /var are symlinks):
|
|
19
|
+
* the native Claude login's accounts store refuses any other, so a relative or symlinked stateDir failed it. */
|
|
20
|
+
export function canonicalStateDir(dir) {
|
|
21
|
+
const path = resolve(dir);
|
|
22
|
+
try {
|
|
23
|
+
return realpathSync(path);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
const parent = dirname(path);
|
|
27
|
+
return parent === path ? path : join(canonicalStateDir(parent), basename(path));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
17
30
|
const kitDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
18
31
|
const putOnce = (path, data) => { if (!existsSync(path))
|
|
19
32
|
writeFileSync(path, data, { mode: 0o600, flag: 'wx' }); };
|
|
@@ -95,6 +108,7 @@ export class Engine {
|
|
|
95
108
|
appOwnedSessionPrefixes;
|
|
96
109
|
constructor(o) {
|
|
97
110
|
this.appOwnedSessionPrefixes = appRecoveryPrefixes(o.appOwnedSessions);
|
|
111
|
+
o = { ...o, stateDir: canonicalStateDir(o.stateDir) };
|
|
98
112
|
this.o = o;
|
|
99
113
|
const bridge = resolveBridge(o.bridge);
|
|
100
114
|
this.root = join(o.stateDir, 'openclaw');
|
|
@@ -277,9 +291,30 @@ export class Engine {
|
|
|
277
291
|
const saved = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : undefined;
|
|
278
292
|
const config = reconcileConfig(saved, { root: this.root, stateDir: this.o.stateDir, port: this.port, pluginId: this.o.pluginId,
|
|
279
293
|
pluginDir, policyPath: join(kitDir, 'policy', 'policy.mjs'), app: this.o.config, offered: this.o.offered,
|
|
280
|
-
installPolicy: this.o.installPolicy, browser: this.o.browserConfig?.() });
|
|
294
|
+
installPolicy: this.o.installPolicy, browser: this.o.browserConfig?.(), plugins: installedPlugins(this.root) });
|
|
281
295
|
putChanged(path, JSON.stringify(config, null, 2) + '\n');
|
|
282
296
|
}
|
|
297
|
+
/** B7: the engine's own installer for one pinned external plugin, in the isolated engine env with the kit's npm
|
|
298
|
+
* first on PATH (the installer runs `npm`). The explicit kit call is the consent; the install policy holds the pin and
|
|
299
|
+
* its integrity. Resolves the installer's exit code and output tail; the kit then restarts the gateway to load it. */
|
|
300
|
+
installPlugin(spec) {
|
|
301
|
+
const { entry, env } = this.doctorContext();
|
|
302
|
+
const npm = this.o.npmPath && this.o.npmPath.includes('/') ? [dirname(this.o.npmPath)] : (process.env.PATH ?? '').split(':');
|
|
303
|
+
return new Promise((resolve, reject) => {
|
|
304
|
+
const child = spawn(process.execPath, [entry, 'plugins', 'install', spec, '--force', '--accept-capabilities'], {
|
|
305
|
+
cwd: env.HOME, env: { ...env, PATH: [...npm, env.PATH].join(':'), npm_config_cache: join(this.root, 'npm-cache') },
|
|
306
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
307
|
+
});
|
|
308
|
+
let output = '';
|
|
309
|
+
this.started.add(child);
|
|
310
|
+
const keep = (chunk) => { output = (output + chunk).slice(-2000); };
|
|
311
|
+
child.stdout.on('data', keep);
|
|
312
|
+
child.stderr.on('data', keep);
|
|
313
|
+
const timer = setTimeout(() => child.kill('SIGKILL'), 300_000);
|
|
314
|
+
child.once('error', error => { clearTimeout(timer); this.started.forget(child); reject(error); });
|
|
315
|
+
child.once('close', code => { clearTimeout(timer); this.started.forget(child); resolve({ code, output }); });
|
|
316
|
+
});
|
|
317
|
+
}
|
|
283
318
|
doctorContext() {
|
|
284
319
|
const home = join(this.root, 'home');
|
|
285
320
|
return { entry: this.entry, env: {
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { stateWords, toAccountView, words, type AccountView, type WordKey } from
|
|
|
9
9
|
export { chooseAccount, consider, resolveSelection, type RoomOf } from './pick.ts';
|
|
10
10
|
export { AuthStoreUnreadableError, AuthStoreSealSizeError } from './auth-store.ts';
|
|
11
11
|
export { EngineAlreadyRunningError } from './engine-status.ts';
|
|
12
|
+
export { PluginInstallError, type PluginInstallFailure } from './plugins.ts';
|
|
12
13
|
export { readAgentDayUsage } from './day-usage.ts';
|
|
13
14
|
export type { AgentDayUsage, DayUsageClient, EngineStartedCharge, EngineStartedKind } from './day-usage.ts';
|
|
14
15
|
export { readAgentUsage, agentUsageOf } from './usage.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// @byokit/openclaw — the OpenClaw runtime kit, host side (docs/runtime-kits.md 5.3). Entries: `.` (this),
|
|
2
2
|
// `./device` (portable), `./link` (Node host-side adapter), `./testing` (fakes and contract suite).
|
|
3
|
-
export { ENGINE_VERSION, OPERATOR_SCOPES, PROTOCOL_VERSION } from
|
|
4
|
-
export { OpenClawKit } from
|
|
5
|
-
export { stateWords, toAccountView, words } from
|
|
6
|
-
export { chooseAccount, consider, resolveSelection } from
|
|
7
|
-
export { AuthStoreUnreadableError, AuthStoreSealSizeError } from
|
|
8
|
-
export { EngineAlreadyRunningError } from
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
3
|
+
export { ENGINE_VERSION, OPERATOR_SCOPES, PROTOCOL_VERSION } from './constants.js';
|
|
4
|
+
export { OpenClawKit } from './kit.js';
|
|
5
|
+
export { stateWords, toAccountView, words } from './words.js';
|
|
6
|
+
export { chooseAccount, consider, resolveSelection } from './pick.js';
|
|
7
|
+
export { AuthStoreUnreadableError, AuthStoreSealSizeError } from './auth-store.js';
|
|
8
|
+
export { EngineAlreadyRunningError } from './engine-status.js';
|
|
9
|
+
export { PluginInstallError } from './plugins.js';
|
|
10
|
+
export { readAgentDayUsage } from './day-usage.js';
|
|
11
|
+
export { readAgentUsage, agentUsageOf } from './usage.js';
|
package/dist/keys.d.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { type ActivationCtx } from './activation.ts';
|
|
2
|
-
import {
|
|
2
|
+
import type { RouteView } from './routes.ts';
|
|
3
3
|
import type { Member, ModelInfo } from './types.ts';
|
|
4
4
|
export type KeyFailureReason = 'auth' | 'billing' | 'empty' | 'model' | 'network' | 'engine' | 'inference' | 'busy';
|
|
5
|
+
/** `needs_workspace`: the Go plan login acts for several workspaces (console orgs); show the pick and add again with
|
|
6
|
+
* its `orgId`. Nothing was saved or sent to the engine. Ids are row ids, never secrets. */
|
|
5
7
|
export type AddKeyResult = {
|
|
6
8
|
status: 'ok';
|
|
7
9
|
} | {
|
|
8
10
|
status: 'invalid' | 'not_included' | 'retry';
|
|
9
11
|
reason: KeyFailureReason;
|
|
12
|
+
} | {
|
|
13
|
+
status: 'needs_workspace';
|
|
14
|
+
workspaces: {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
}[];
|
|
10
18
|
};
|
|
11
19
|
/** The add resolves like a Promise; `cancel()` stops a pending add and clears any partial key (retry/busy). */
|
|
12
20
|
export type AddKeyHandle = Promise<AddKeyResult> & {
|
|
@@ -14,6 +22,7 @@ export type AddKeyHandle = Promise<AddKeyResult> & {
|
|
|
14
22
|
};
|
|
15
23
|
export declare function createKeys(ctx: ActivationCtx & {
|
|
16
24
|
root: string;
|
|
25
|
+
routes(): RouteView[];
|
|
17
26
|
ensure(member: Member): Promise<{
|
|
18
27
|
agentId: string;
|
|
19
28
|
}>;
|
|
@@ -22,6 +31,9 @@ export declare function createKeys(ctx: ActivationCtx & {
|
|
|
22
31
|
indexLane(member: Member, route: RouteView): Promise<void>;
|
|
23
32
|
dropLane(member: Member, provider?: string): Promise<void>;
|
|
24
33
|
backfillLane(member: Member, model: string | undefined): Promise<void>;
|
|
34
|
+
/** The provider config headers a live-catalog key's calls and probes must carry (`null` clears them),
|
|
35
|
+
* written through the kit's own config patch. The Go workspace header is the one user. */
|
|
36
|
+
setProviderHeaders(provider: string, headers: Record<string, string> | null): Promise<void>;
|
|
25
37
|
}): {
|
|
26
38
|
models: (member: Member, o: {
|
|
27
39
|
authChoice: string;
|
|
@@ -31,6 +43,7 @@ export declare function createKeys(ctx: ActivationCtx & {
|
|
|
31
43
|
authChoice: string;
|
|
32
44
|
apiKey: string;
|
|
33
45
|
model?: string;
|
|
46
|
+
orgId?: string;
|
|
34
47
|
}): AddKeyHandle;
|
|
35
48
|
ready(member: Member, o?: {
|
|
36
49
|
once?: boolean;
|
package/dist/keys.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { modelsOf } from
|
|
7
|
-
import { LIVE_CATALOGS } from
|
|
2
|
+
import { openCodeWorkspaces } from '@byokit/accounts/opencode-go';
|
|
3
|
+
import { activate } from './activation.js';
|
|
4
|
+
import { keyAgentId, MEMBER_ID, KEY_PREFIX, untilListed } from './members.js';
|
|
5
|
+
import { providerOf } from './pick.js';
|
|
6
|
+
import { modelsOf } from './accounts.js';
|
|
7
|
+
import { LIVE_CATALOGS } from './constants.js';
|
|
8
8
|
export function createKeys(ctx) {
|
|
9
9
|
// Serialize key replacement and key runs, so an in-flight run never sees a half-installed replacement.
|
|
10
10
|
const tails = new Map();
|
|
@@ -60,7 +60,7 @@ export function createKeys(ctx) {
|
|
|
60
60
|
const models = async (member, o) => {
|
|
61
61
|
if (!check(member))
|
|
62
62
|
throw new Error('Choose your own member.');
|
|
63
|
-
const route = routes().find((r) => r.choice === o.authChoice && r.keyEntry && r.readiness === 'ready');
|
|
63
|
+
const route = ctx.routes().find((r) => r.choice === o.authChoice && r.keyEntry && r.readiness === 'ready');
|
|
64
64
|
if (!route || !LIVE_CATALOGS.includes(route.provider))
|
|
65
65
|
return [];
|
|
66
66
|
await ctx.ensure(member);
|
|
@@ -83,9 +83,36 @@ export function createKeys(ctx) {
|
|
|
83
83
|
throw new Error('Choose your own member.');
|
|
84
84
|
if (!o.apiKey.trim())
|
|
85
85
|
return failure('invalid', 'empty');
|
|
86
|
-
const route = routes().find((r) => r.choice === o.authChoice && r.keyEntry && r.readiness === 'ready');
|
|
86
|
+
const route = ctx.routes().find((r) => r.choice === o.authChoice && r.keyEntry && r.readiness === 'ready');
|
|
87
87
|
if (!route || (o.model !== undefined && !LIVE_CATALOGS.includes(route.provider)))
|
|
88
88
|
return failure('not_included', 'model');
|
|
89
|
+
// A workspace names a live-catalog login's own workspace only; another route takes no header, and a
|
|
90
|
+
// refusal here leaves the member's lanes untouched (it lands before any prepare or activation).
|
|
91
|
+
let orgId = typeof o.orgId === 'string' ? o.orgId.trim() : '';
|
|
92
|
+
if (orgId) {
|
|
93
|
+
if (orgId.length > 256)
|
|
94
|
+
return failure('invalid', 'auth');
|
|
95
|
+
if (!LIVE_CATALOGS.includes(route.provider))
|
|
96
|
+
return failure('not_included', 'model');
|
|
97
|
+
}
|
|
98
|
+
else if (route.provider === 'opencode-go') {
|
|
99
|
+
// The Go gateway refuses a login acting for several workspaces until one is named (403 Workspace selection
|
|
100
|
+
// is required), so an add without `orgId` lists the key's own (the console listing `@byokit/accounts` uses):
|
|
101
|
+
// one is used, several come back for the app's pick, and a key the console refuses (a workspace-bound
|
|
102
|
+
// service key) needs none. A listing that could not be read fails the add before anything changes.
|
|
103
|
+
let workspaces;
|
|
104
|
+
try {
|
|
105
|
+
workspaces = await openCodeWorkspaces(o.apiKey, (input, init) => fetch(input, { ...init, signal: init?.signal ? AbortSignal.any([init.signal, ac.signal]) : ac.signal }));
|
|
106
|
+
}
|
|
107
|
+
catch { // `workspaces_unread`: offline, timeout, a server error or a body that is no list
|
|
108
|
+
return ac.signal.aborted ? failure('retry', 'busy') : failure('retry', 'network');
|
|
109
|
+
}
|
|
110
|
+
if (ac.signal.aborted)
|
|
111
|
+
return failure('retry', 'busy');
|
|
112
|
+
if (workspaces.length > 1)
|
|
113
|
+
return { status: 'needs_workspace', workspaces };
|
|
114
|
+
orgId = workspaces[0]?.id ?? '';
|
|
115
|
+
}
|
|
89
116
|
if (ac.signal.aborted)
|
|
90
117
|
return failure('retry', 'busy');
|
|
91
118
|
return exclusive(member, async () => {
|
|
@@ -93,8 +120,13 @@ export function createKeys(ctx) {
|
|
|
93
120
|
// A key the engine may have taken but not sealed is cleared (prepare already emptied the agent); if the clear
|
|
94
121
|
// does not reach the engine, the key stays unsealed and unusable until the next key add or kit start, so runs
|
|
95
122
|
// fail closed.
|
|
123
|
+
let wroteHeader = false;
|
|
96
124
|
const abandon = async (result) => {
|
|
97
125
|
await control(member, 'clear').catch(() => undefined);
|
|
126
|
+
// A header this add wrote must not outlive its abandoned key; a clear that fails never throws past
|
|
127
|
+
// the abandon, like the key clear itself.
|
|
128
|
+
if (wroteHeader)
|
|
129
|
+
await ctx.setProviderHeaders(route.provider, null).catch(() => undefined);
|
|
98
130
|
return result;
|
|
99
131
|
};
|
|
100
132
|
const cancelled = () => failure('retry', 'busy');
|
|
@@ -112,6 +144,18 @@ export function createKeys(ctx) {
|
|
|
112
144
|
return cancelled();
|
|
113
145
|
if (!(await control(member, 'prepare', route.provider)).ok)
|
|
114
146
|
return failure('retry', 'engine');
|
|
147
|
+
// The workspace rides the provider's probe and every run from its config; it is written before the
|
|
148
|
+
// activation, so the probe itself carries it. The slot is provider-wide (shared by every member's
|
|
149
|
+
// key on this engine), so a workspace-scoped add that names none clears the standing one before the
|
|
150
|
+
// probe: a bound service key never carries another workspace, and `removeKey` drops it.
|
|
151
|
+
if (orgId) {
|
|
152
|
+
await ctx.setProviderHeaders(route.provider, { 'x-opencode-org-id': orgId });
|
|
153
|
+
wroteHeader = true;
|
|
154
|
+
}
|
|
155
|
+
else if (LIVE_CATALOGS.includes(route.provider)) {
|
|
156
|
+
await ctx.setProviderHeaders(route.provider, null);
|
|
157
|
+
wroteHeader = true;
|
|
158
|
+
}
|
|
115
159
|
// The public pin's typed API-key path, never a CLI-login import. Discard the engine's lines/errors.
|
|
116
160
|
sent = true;
|
|
117
161
|
if (ac.signal.aborted)
|
|
@@ -187,9 +231,13 @@ export function createKeys(ctx) {
|
|
|
187
231
|
else if (ready.ok !== true || providerOf(ready.model ?? '') !== o.provider)
|
|
188
232
|
return;
|
|
189
233
|
}
|
|
234
|
+
// The lane's own provider, when the removal can name it: the header clear below is scoped like the removal.
|
|
190
235
|
const cleared = await control(member, 'clear').catch(() => undefined);
|
|
191
236
|
if (cleared?.ok !== true && cleared?.absent !== true)
|
|
192
237
|
refused();
|
|
238
|
+
// A workspace header the removed key named must not outlive it; the next add names its own (or none).
|
|
239
|
+
if (o.provider !== undefined && LIVE_CATALOGS.includes(o.provider))
|
|
240
|
+
await ctx.setProviderHeaders(o.provider, null).catch(() => { throw new Error('The key could not be removed.'); });
|
|
193
241
|
await ctx.dropLane(member, o.provider);
|
|
194
242
|
});
|
|
195
243
|
},
|
package/dist/kit.d.ts
CHANGED
|
@@ -41,6 +41,10 @@ export type KitOptions = {
|
|
|
41
41
|
spawnEngine?: boolean;
|
|
42
42
|
onState?: (s: KitState) => void;
|
|
43
43
|
log?: (line: string) => void;
|
|
44
|
+
listWorkspaces?: (key: string, signal: AbortSignal) => Promise<{
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
}[]>;
|
|
44
48
|
};
|
|
45
49
|
export type RetainedLogin = {
|
|
46
50
|
path: string;
|
|
@@ -56,6 +60,7 @@ export declare class OpenClawKit {
|
|
|
56
60
|
private members?;
|
|
57
61
|
private readonly locks;
|
|
58
62
|
private readonly routeMap;
|
|
63
|
+
private installs;
|
|
59
64
|
private accountsCtl?;
|
|
60
65
|
private readonly bridge;
|
|
61
66
|
private readonly keys;
|
|
@@ -110,6 +115,11 @@ export declare class OpenClawKit {
|
|
|
110
115
|
}>;
|
|
111
116
|
private signInCtx;
|
|
112
117
|
routes(): RouteView[];
|
|
118
|
+
/** B7 (5.7): install one external provider plugin a `needs_plugin` route names into this kit's own engine, at its
|
|
119
|
+
* pin; resolves once the restarted gateway lists it enabled at that version. Discovery never installs. Serialized:
|
|
120
|
+
* concurrent installs would race the allow-list update and double-spawn the installer into one engine tree. */
|
|
121
|
+
installPlugin(pluginId: string): Promise<void>;
|
|
122
|
+
private runInstall;
|
|
113
123
|
providerStatus(member: Member): Promise<string[] | undefined>;
|
|
114
124
|
providers(member: Member): Promise<string[]>;
|
|
115
125
|
/** A member sign-in to `provider`, or a ready key (5.8) whose saved model is that provider's. */
|
|
@@ -153,11 +163,15 @@ export declare class OpenClawKit {
|
|
|
153
163
|
removeAccount(member: Member, id: AccountId): Promise<void>;
|
|
154
164
|
defaults(member: Member): Promise<Defaults>;
|
|
155
165
|
setDefaults(member: Member, d: Defaults): Promise<void>;
|
|
156
|
-
/** Save an explicitly supplied API key (billed per use) in this member's separate key agent.
|
|
166
|
+
/** Save an explicitly supplied API key (billed per use) in this member's separate key agent.
|
|
167
|
+
* `orgId` names the workspace a Go plan login acts for; it rides every probe and run as `x-opencode-org-id`,
|
|
168
|
+
* and replacing the key rewrites it. Without it a Go key's workspaces are listed: one is used, several resolve
|
|
169
|
+
* `needs_workspace` with the rows for the app's pick (add again with `orgId`). */
|
|
157
170
|
addKey(agentId: Member, o: {
|
|
158
171
|
authChoice: string;
|
|
159
172
|
apiKey: string;
|
|
160
173
|
model?: string;
|
|
174
|
+
orgId?: string;
|
|
161
175
|
}): AddKeyHandle;
|
|
162
176
|
private indexKeyLane;
|
|
163
177
|
/** Remove this member's saved key (the `byokit-key-<member>` lane: its credential store and ready marker), so
|