@openviking/openclaw-plugin 2026.9.4 → 2026.9.8-1
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/INSTALL-AGENT.md +25 -4
- package/INSTALL-ZH.md +22 -4
- package/INSTALL.md +22 -4
- package/README.md +16 -5
- package/client.ts +0 -91
- package/commands/setup.ts +41 -150
- package/config.ts +24 -11
- package/context-engine.ts +7 -2
- package/dist/client.js +0 -82
- package/dist/commands/setup.js +29 -127
- package/dist/config.js +13 -8
- package/dist/context-engine.js +5 -0
- package/dist/plugin/openclaw-runtime-compaction.js +20 -0
- package/dist/plugin/openviking-memory-tools.js +1 -1
- package/dist/plugin/openviking-session-routing-runtime.js +2 -2
- package/dist/routing/identity-routing.js +5 -5
- package/dist/services/context-lifecycle-service.js +17 -5
- package/dist/services/setup/probe-service.js +112 -0
- package/openclaw.plugin.json +8 -7
- package/package.json +1 -7
- package/plugin/openclaw-runtime-compaction.ts +41 -0
- package/plugin/openviking-client-runtime.ts +1 -1
- package/plugin/openviking-memory-tools.ts +1 -1
- package/plugin/openviking-session-routing-runtime.ts +2 -2
- package/routing/identity-routing.ts +8 -8
- package/services/context-lifecycle-service.ts +19 -4
- package/services/setup/setup-flow.ts +8 -6
- package/skills/install-openviking-memory/SKILL.md +33 -10
- package/skills/openviking-context-database/SKILL.md +2 -2
package/INSTALL-AGENT.md
CHANGED
|
@@ -42,6 +42,7 @@ Before setup, determine:
|
|
|
42
42
|
| --- | --- | --- |
|
|
43
43
|
| OpenViking base URL | Yes | Ask user or read `OPENVIKING_BASE_URL` |
|
|
44
44
|
| API key | Usually | Ask user or read `OPENVIKING_API_KEY` |
|
|
45
|
+
| Memory scope | Optional; default `none` | Ask whether memory is shared, separated by assistant, or separated by sender. Use `sender`; accept legacy `person` as an alias. |
|
|
45
46
|
| Account ID | Only for root API keys | Ask user if setup reports root-key tenant context is needed |
|
|
46
47
|
| User ID | Only for root API keys | Ask user if setup reports root-key tenant context is needed |
|
|
47
48
|
| Slot replacement approval | Only if another context engine owns the slot | Ask user before using `--force-slot` |
|
|
@@ -73,6 +74,23 @@ Run setup with `--json` whenever possible:
|
|
|
73
74
|
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --json
|
|
74
75
|
```
|
|
75
76
|
|
|
77
|
+
Choose memory scope before building that command:
|
|
78
|
+
|
|
79
|
+
| Value | Concrete layout | Use when |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `none` (default; omit the flag) | Shared `viking://user/<user_id>/memories/...`; no concrete peer memory subtree | General case |
|
|
82
|
+
| `assistant` | Assistant-attributed peer memory under `.../peers/<assistant_id>/memories/...` | A human is the OpenViking user and uses multiple OpenClaw assistants |
|
|
83
|
+
| `sender` | Sender-attributed peer memory under `.../peers/<sender_id>/memories/...` | An agent is the OpenViking user and receives messages from multiple humans |
|
|
84
|
+
|
|
85
|
+
`person` is a legacy alias for `sender`. Accept it from existing config or user input, but write `sender` in new setup commands. OpenViking initializes the managed `peers/` container for every user; `none` only means that no concrete `peers/<peer_id>/memories` subtree is used. Actor-peer recall includes shared user memory plus the current peer memory, and changing the scope does not move existing memories.
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role assistant --json
|
|
91
|
+
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role sender --json
|
|
92
|
+
```
|
|
93
|
+
|
|
76
94
|
Branch on the result:
|
|
77
95
|
|
|
78
96
|
| JSON result | Meaning | Agent action |
|
|
@@ -96,10 +114,10 @@ openclaw openviking setup \
|
|
|
96
114
|
--json
|
|
97
115
|
```
|
|
98
116
|
|
|
99
|
-
Custom
|
|
117
|
+
Custom assistant routing prefix (optional; only with `peer_role=assistant` and when the user explicitly requests a prefix):
|
|
100
118
|
|
|
101
119
|
```bash
|
|
102
|
-
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-prefix <PREFIX> --json
|
|
120
|
+
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role assistant --peer-prefix <PREFIX> --json
|
|
103
121
|
```
|
|
104
122
|
|
|
105
123
|
Slot replacement retry, only after user approval:
|
|
@@ -139,6 +157,7 @@ Also inspect:
|
|
|
139
157
|
| `health.version` | Records server version |
|
|
140
158
|
| `health.compatibility` | Determines whether to warn |
|
|
141
159
|
| `config.hasApiKey` | Confirms whether an API key was saved |
|
|
160
|
+
| `config.peer_role` | Confirms `none`, `assistant`, or canonical `sender` scope |
|
|
142
161
|
| `config.peer_prefix` | Confirms configured peer prefix when present |
|
|
143
162
|
|
|
144
163
|
## Environment Detection
|
|
@@ -267,7 +286,8 @@ Core fields:
|
|
|
267
286
|
| `mode` | Legacy compatibility field. Expected value: `remote`. |
|
|
268
287
|
| `baseUrl` | OpenViking HTTP endpoint |
|
|
269
288
|
| `apiKey` | OpenViking API key |
|
|
270
|
-
| `
|
|
289
|
+
| `peer_role` | `none` (shared user memory), `assistant` (`peers/<assistant_id>`), or `sender` (`peers/<sender_id>`). Legacy `person` is accepted as `sender`. |
|
|
290
|
+
| `peer_prefix` | Optional only with `peer_role=assistant`; prefix for OpenClaw agent IDs. Interactive setup accepts only letters, digits, `_`, and `-`. If unset, the plugin follows session agent IDs. |
|
|
271
291
|
| `accountId` | Required for root API keys |
|
|
272
292
|
| `userId` | Required for root API keys |
|
|
273
293
|
|
|
@@ -329,7 +349,8 @@ Existing config fields are preserved during migration. The new plugin reads old
|
|
|
329
349
|
|
|
330
350
|
- `baseUrl`
|
|
331
351
|
- `apiKey`
|
|
332
|
-
- `
|
|
352
|
+
- `peer_role`: optional; `none`, `assistant`, or `sender`; legacy `person` is accepted as `sender`
|
|
353
|
+
- `peer_prefix`: optional only with `peer_role=assistant`; interactive setup accepts only letters, digits, `_`, and `-`
|
|
333
354
|
|
|
334
355
|
## Backup Path: ov-install
|
|
335
356
|
|
package/INSTALL-ZH.md
CHANGED
|
@@ -197,12 +197,30 @@ openclaw openviking setup \
|
|
|
197
197
|
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --force-slot --json
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
根据 OpenViking user 代表谁来选择 `--peer-role`:
|
|
201
|
+
|
|
202
|
+
| 值 | 存储示例 | 适用场景 |
|
|
203
|
+
| --- | --- | --- |
|
|
204
|
+
| `none`(默认) | `viking://user/alice/memories/...` | 该 OpenViking 用户下的所有对话共享 user-level 记忆,不使用具体 peer 的记忆子树。 |
|
|
205
|
+
| `assistant` | `viking://user/alice/peers/main/memories/...` | OpenViking user 代表人,并希望把 assistant 归因的 peer 记忆按不同 OpenClaw 助手分开。 |
|
|
206
|
+
| `sender` | `viking://user/support-agent/peers/customer-42/memories/...` | OpenViking user 代表 agent,并希望把 sender 归因的 peer 记忆按不同发送者分开。 |
|
|
207
|
+
|
|
208
|
+
`person` 仍作为 `sender` 的旧配置别名被兼容;新配置请使用 `sender`。
|
|
209
|
+
|
|
210
|
+
例如,让每个助手使用独立的 peer 记忆,并可选给 assistant id 加前缀:
|
|
201
211
|
|
|
202
212
|
```bash
|
|
203
213
|
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role assistant --peer-prefix <PREFIX> --json
|
|
204
214
|
```
|
|
205
215
|
|
|
216
|
+
如果 OpenViking user 是 agent,按给它发消息的 sender 分开 peer 记忆:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role sender --json
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
OpenViking 会为每个用户初始化受管的 `peers/` 容器。`none` 表示插件不创建、也不路由到具体的 `peers/<peer_id>/memories` 子树。Actor-peer 召回同时包含用户共享记忆和当前 peer 记忆;切换配置不会搬迁已有记忆。
|
|
223
|
+
|
|
206
224
|
#### 无法执行 CLI 时直接配置文件
|
|
207
225
|
|
|
208
226
|
如果容器内无法执行 `openclaw` CLI,可以把以下字段合并到 OpenClaw 实际读取的配置文件。设置了 `OPENCLAW_CONFIG_PATH` 时使用该路径;否则通常是 `$OPENCLAW_STATE_DIR/openclaw.json`(默认 `~/.openclaw/openclaw.json`)。
|
|
@@ -300,7 +318,7 @@ plugins.entries.openviking.config
|
|
|
300
318
|
| `mode` | `remote` | 兼容旧配置的字段。当前只支持 remote。 |
|
|
301
319
|
| `baseUrl` | `http://127.0.0.1:1933` | OpenViking HTTP 地址 |
|
|
302
320
|
| `apiKey` | 空 | OpenViking API key |
|
|
303
|
-
| `peer_role` | `
|
|
321
|
+
| `peer_role` | `none` | 记忆归属:`none`(共享 `viking://user/<user_id>/memories`)、`assistant`(`.../peers/<assistant_id>/memories`)或 `sender`(`.../peers/<sender_id>/memories`)。旧值 `person` 作为 `sender` 的别名兼容。Session message 使用 body `peer_id`;数据面 recall/search 使用 `X-OpenViking-Actor-Peer`。 |
|
|
304
322
|
| `peer_prefix` | 空 | `peer_role=assistant` 时 assistant `peer_id` / actor peer 值的可选前缀。 |
|
|
305
323
|
| `accountId` | 空 | 使用 root API key 时需要 |
|
|
306
324
|
| `userId` | 空 | 使用 root API key 时需要 |
|
|
@@ -325,7 +343,7 @@ openclaw config get plugins.entries.openviking.config
|
|
|
325
343
|
| --- | --- | --- |
|
|
326
344
|
| `baseUrl` | `http://127.0.0.1:1933` | 远端 OpenViking 服务地址 |
|
|
327
345
|
| `apiKey` | 空 | 远端 OpenViking API Key;服务端未开启认证时可不填 |
|
|
328
|
-
| `peer_role` | `
|
|
346
|
+
| `peer_role` | `none` | 记忆归属:`none`、`assistant` 或 `sender`;旧值 `person` 作为 `sender` 的别名兼容。Session message 使用 body `peer_id`,数据面 recall/search 使用 `X-OpenViking-Actor-Peer` |
|
|
329
347
|
| `peer_prefix` | 空 | `peer_role=assistant` 时 assistant `peer_id` / actor peer 值的可选前缀 |
|
|
330
348
|
|
|
331
349
|
常见设置:
|
|
@@ -387,7 +405,7 @@ ov-install
|
|
|
387
405
|
| `--current-version` | 查看 helper 记录的当前版本 |
|
|
388
406
|
| `--base-url URL` | OpenViking 服务器地址(启用非交互模式) |
|
|
389
407
|
| `--api-key KEY` | OpenViking API key |
|
|
390
|
-
| `--peer-role ROLE` |
|
|
408
|
+
| `--peer-role ROLE` | 记忆归属:`none`、`assistant` 或 `sender`;旧值 `person` 作为 `sender` 的别名兼容 |
|
|
391
409
|
| `--peer-prefix PREFIX` | assistant `peer_id` / actor peer 值的前缀 |
|
|
392
410
|
| `--update` | 更新 helper 管理的安装 |
|
|
393
411
|
|
package/INSTALL.md
CHANGED
|
@@ -141,12 +141,30 @@ If another context engine already owns the slot, setup will not replace it by de
|
|
|
141
141
|
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --force-slot --json
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
Choose `--peer-role` from what the OpenViking user represents:
|
|
145
|
+
|
|
146
|
+
| Value | Storage example | Use when |
|
|
147
|
+
| --- | --- | --- |
|
|
148
|
+
| `none` (default) | `viking://user/alice/memories/...` | All conversations for this OpenViking user share user-level memory. No peer-specific memory subtree is used. |
|
|
149
|
+
| `assistant` | `viking://user/alice/peers/main/memories/...` | The OpenViking user is a human and assistant-attributed peer memory should be separated by OpenClaw assistant. |
|
|
150
|
+
| `sender` | `viking://user/support-agent/peers/customer-42/memories/...` | The OpenViking user is an agent and sender-attributed peer memory should be separated by human sender. |
|
|
151
|
+
|
|
152
|
+
`person` is still accepted as a legacy alias for `sender`. New configuration should use `sender`.
|
|
153
|
+
|
|
154
|
+
For example, give each assistant its own peer memory and optionally namespace the assistant id:
|
|
145
155
|
|
|
146
156
|
```bash
|
|
147
157
|
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role assistant --peer-prefix <PREFIX> --json
|
|
148
158
|
```
|
|
149
159
|
|
|
160
|
+
Or scope peer memory by the sender talking to an agent:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
openclaw openviking setup --base-url <OPENVIKING_URL> --api-key <API_KEY> --peer-role sender --json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
OpenViking initializes the managed `peers/` container for every user. `none` means the plugin does not create or route into a concrete `peers/<peer_id>/memories` subtree. Actor-peer recall includes shared user memory plus the current peer memory, and changing the setting does not move existing memories.
|
|
167
|
+
|
|
150
168
|
#### Configure The File Directly When The CLI Is Unavailable
|
|
151
169
|
|
|
152
170
|
If the `openclaw` CLI cannot run inside the container, merge the following fields into the config file that OpenClaw actually reads. Use `OPENCLAW_CONFIG_PATH` when it is set; otherwise the file is usually `$OPENCLAW_STATE_DIR/openclaw.json` (default: `~/.openclaw/openclaw.json`).
|
|
@@ -244,7 +262,7 @@ Core fields:
|
|
|
244
262
|
| `mode` | `remote` | Legacy compatibility field. Only remote mode is supported. |
|
|
245
263
|
| `baseUrl` | `http://127.0.0.1:1933` | OpenViking HTTP endpoint |
|
|
246
264
|
| `apiKey` | empty | OpenViking API key |
|
|
247
|
-
| `peer_role` | `
|
|
265
|
+
| `peer_role` | `none` | Memory scope: `none` (shared `viking://user/<user_id>/memories`), `assistant` (`.../peers/<assistant_id>/memories`), or `sender` (`.../peers/<sender_id>/memories`). Legacy `person` is accepted as `sender`. Session messages use body `peer_id`; data-plane recall/search uses `X-OpenViking-Actor-Peer`. |
|
|
248
266
|
| `peer_prefix` | empty | Optional prefix for assistant `peer_id` / actor peer values when `peer_role=assistant`. |
|
|
249
267
|
| `accountId` | empty | Required when using a root API key |
|
|
250
268
|
| `userId` | empty | Required when using a root API key |
|
|
@@ -311,7 +329,7 @@ Useful backup/source flags:
|
|
|
311
329
|
| `--current-version` | Print the version tracked by the helper |
|
|
312
330
|
| `--base-url URL` | OpenViking server URL (enables non-interactive mode) |
|
|
313
331
|
| `--api-key KEY` | OpenViking API key |
|
|
314
|
-
| `--peer-role ROLE` |
|
|
332
|
+
| `--peer-role ROLE` | Memory scope: `none`, `assistant`, or `sender`; legacy `person` is accepted as `sender` |
|
|
315
333
|
| `--peer-prefix PREFIX` | Prefix for assistant `peer_id` / actor peer values |
|
|
316
334
|
| `--update` | Update an existing helper-managed install |
|
|
317
335
|
|
|
@@ -356,7 +374,7 @@ The plugin connects to an existing remote OpenViking server.
|
|
|
356
374
|
| --- | --- | --- |
|
|
357
375
|
| `baseUrl` | `http://127.0.0.1:1933` | Remote OpenViking HTTP endpoint |
|
|
358
376
|
| `apiKey` | empty | Optional OpenViking API key |
|
|
359
|
-
| `peer_role` | `
|
|
377
|
+
| `peer_role` | `none` | Memory scope: `none`, `assistant`, or `sender`; legacy `person` is accepted as `sender`. Session messages use body `peer_id`, while data-plane recall/search uses `X-OpenViking-Actor-Peer` |
|
|
360
378
|
| `peer_prefix` | empty | Optional prefix for assistant `peer_id` / actor peer values when `peer_role=assistant` |
|
|
361
379
|
|
|
362
380
|
Common settings:
|
package/README.md
CHANGED
|
@@ -124,15 +124,26 @@ The main rules are:
|
|
|
124
124
|
- reuse `sessionId` directly when it is already a UUID
|
|
125
125
|
- prefer `sessionKey` when deriving a stable `ovSessionId`
|
|
126
126
|
- normalize unsafe path characters, or fall back to a stable SHA-256 when needed
|
|
127
|
-
- `peer_role=
|
|
128
|
-
- `peer_role=
|
|
129
|
-
- `peer_role=
|
|
130
|
-
-
|
|
127
|
+
- `peer_role=none` is the default: messages have no peer attribution and memory stays in the shared user scope, for example `viking://user/alice/memories/...`; no peer-specific memory subtree is used
|
|
128
|
+
- `peer_role=assistant` writes assistant messages with `peer_id=<sessionAgent>` and uses peer-scoped memory such as `viking://user/alice/peers/main/memories/...`; if `peer_prefix` is set, the peer id becomes `<peer_prefix>_<sessionAgent>`
|
|
129
|
+
- `peer_role=sender` writes user messages with the OpenClaw sender identity as `peer_id` and uses peer-scoped memory such as `viking://user/support-agent/peers/customer-42/memories/...`; assistant messages do not get `peer_id`
|
|
130
|
+
- `person` remains accepted as a legacy config alias for `sender`, but new configuration and documentation use `sender`
|
|
131
|
+
- data-plane recall/search/read/import/delete sends the same resolved peer identity as `X-OpenViking-Actor-Peer` when `peer_role` is `assistant` or `sender`
|
|
131
132
|
- when OpenClaw does not provide a session agent, use its default agent `main` for local session and assistant peer metadata
|
|
132
133
|
- only add `X-OpenViking-Account` / `X-OpenViking-User` when `accountId` / `userId` are explicitly configured
|
|
133
134
|
|
|
134
135
|
This matters because OpenViking tenant identity is account/user-scoped, while OpenClaw agent identity is runtime metadata.
|
|
135
136
|
|
|
137
|
+
Choose the scope from what `viking://user/<user_id>` represents:
|
|
138
|
+
|
|
139
|
+
| Model | Example | Result |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| General/shared (`none`) | `user_id=alice` uses any OpenClaw assistant | Shared user memory under `viking://user/alice/memories/...` |
|
|
142
|
+
| Human is the OpenViking user (`assistant`) | Alice uses OpenClaw assistants `main` and `research` | Assistant-scoped memories are separated under `.../peers/main/memories/...` and `.../peers/research/memories/...` |
|
|
143
|
+
| Agent is the OpenViking user (`sender`) | `user_id=support-agent` receives messages from `customer-42` and `customer-99` | Sender-scoped memories are separated under `.../peers/customer-42/memories/...` and `.../peers/customer-99/memories/...` |
|
|
144
|
+
|
|
145
|
+
OpenViking creates the managed `peers/` container as part of the user namespace. `none` means that the plugin does not create or route into a specific `peers/<peer_id>/memories` subtree. With `assistant` or `sender`, actor-peer recall includes the shared user memory plus the current peer's memory; changing the setting does not move existing memories.
|
|
146
|
+
|
|
136
147
|
The recommended remote-mode configuration only needs:
|
|
137
148
|
|
|
138
149
|
- `baseUrl`
|
|
@@ -143,7 +154,7 @@ The recommended remote-mode configuration only needs:
|
|
|
143
154
|
In this setup:
|
|
144
155
|
|
|
145
156
|
- `apiKey` should usually be a user key
|
|
146
|
-
- new installs default to `peer_role=
|
|
157
|
+
- new installs default to `peer_role=none`
|
|
147
158
|
- `accountId` / `userId` are advanced options only when the deployment needs explicit identity headers, such as root-key or trusted-server flows
|
|
148
159
|
|
|
149
160
|
### User namespace
|
package/client.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { once } from "node:events";
|
|
3
|
-
import { createWriteStream } from "node:fs";
|
|
4
|
-
import { mkdtemp, readdir, readFile, rm, stat } from "node:fs/promises";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { basename, dirname, join, relative } from "node:path";
|
|
7
|
-
|
|
8
|
-
import { Zip, ZipDeflate } from "fflate";
|
|
9
|
-
|
|
10
1
|
import { defaultHttpTransport, type HttpTransport } from "./adapters/http-transport.js";
|
|
11
2
|
import {
|
|
12
3
|
defaultResourcePackager,
|
|
@@ -255,23 +246,6 @@ function sleep(ms: number): Promise<void> {
|
|
|
255
246
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
256
247
|
}
|
|
257
248
|
|
|
258
|
-
const MEMORY_URI_PATTERNS = [
|
|
259
|
-
/^viking:\/\/user\/(?:[^/]+\/)?memories(?:\/|$)/,
|
|
260
|
-
];
|
|
261
|
-
const REMOTE_RESOURCE_PREFIXES = ["http://", "https://", "git@", "ssh://", "git://"];
|
|
262
|
-
|
|
263
|
-
export function isMemoryUri(uri: string): boolean {
|
|
264
|
-
return MEMORY_URI_PATTERNS.some((pattern) => pattern.test(uri));
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function isRemoteResourceSource(source: string): boolean {
|
|
268
|
-
return REMOTE_RESOURCE_PREFIXES.some((prefix) => source.startsWith(prefix));
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function toBlobPart(value: Buffer): ArrayBuffer {
|
|
272
|
-
return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength) as ArrayBuffer;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
249
|
function resolveWaitRequestTimeoutMs(defaultTimeoutMs: number, waitTimeoutSeconds?: number): number {
|
|
276
250
|
const requestedMs =
|
|
277
251
|
typeof waitTimeoutSeconds === "number" && Number.isFinite(waitTimeoutSeconds) && waitTimeoutSeconds > 0
|
|
@@ -280,14 +254,6 @@ function resolveWaitRequestTimeoutMs(defaultTimeoutMs: number, waitTimeoutSecond
|
|
|
280
254
|
return Math.max(defaultTimeoutMs, requestedMs);
|
|
281
255
|
}
|
|
282
256
|
|
|
283
|
-
async function cleanupUploadTempPath(path?: string): Promise<void> {
|
|
284
|
-
if (!path) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
await rm(path, { force: true }).catch(() => undefined);
|
|
288
|
-
await rm(dirname(path), { recursive: true, force: true }).catch(() => undefined);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
257
|
export class OpenVikingClient {
|
|
292
258
|
private readonly transport: HttpTransport;
|
|
293
259
|
private readonly configuredHeaders: Record<string, string>;
|
|
@@ -685,63 +651,6 @@ export class OpenVikingClient {
|
|
|
685
651
|
return result.temp_file_id;
|
|
686
652
|
}
|
|
687
653
|
|
|
688
|
-
async zipDirectoryForUpload(dirPath: string): Promise<string> {
|
|
689
|
-
const rootStats = await stat(dirPath);
|
|
690
|
-
if (!rootStats.isDirectory()) {
|
|
691
|
-
throw new Error(`Not a directory: ${dirPath}`);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
const zipDir = await mkdtemp(join(tmpdir(), "openviking-openclaw-upload-"));
|
|
695
|
-
const zipPath = join(zipDir, `${basename(dirPath).replace(/[^a-zA-Z0-9._-]/g, "_")}-${randomUUID()}.zip`);
|
|
696
|
-
const output = createWriteStream(zipPath);
|
|
697
|
-
const outputClosed = once(output, "close");
|
|
698
|
-
const outputErrored = once(output, "error").then(([err]) => Promise.reject(err));
|
|
699
|
-
const zip = new Zip((err, chunk, final) => {
|
|
700
|
-
if (err) {
|
|
701
|
-
output.destroy(err);
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
if (chunk?.length) {
|
|
705
|
-
output.write(Buffer.from(chunk));
|
|
706
|
-
}
|
|
707
|
-
if (final) {
|
|
708
|
-
output.end();
|
|
709
|
-
}
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
const walk = async (currentDir: string) => {
|
|
713
|
-
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
714
|
-
for (const entry of entries) {
|
|
715
|
-
const fullPath = join(currentDir, entry.name);
|
|
716
|
-
if (entry.isDirectory()) {
|
|
717
|
-
await walk(fullPath);
|
|
718
|
-
continue;
|
|
719
|
-
}
|
|
720
|
-
if (!entry.isFile()) {
|
|
721
|
-
continue;
|
|
722
|
-
}
|
|
723
|
-
const relPath = relative(dirPath, fullPath).replace(/\\/g, "/");
|
|
724
|
-
if (!relPath || relPath.startsWith("../") || relPath.includes("/../")) {
|
|
725
|
-
throw new Error(`Unsafe relative path while zipping: ${relPath}`);
|
|
726
|
-
}
|
|
727
|
-
const file = new ZipDeflate(relPath);
|
|
728
|
-
zip.add(file);
|
|
729
|
-
file.push(new Uint8Array(await readFile(fullPath)), true);
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
try {
|
|
733
|
-
await walk(dirPath);
|
|
734
|
-
zip.end();
|
|
735
|
-
await Promise.race([outputClosed, outputErrored]);
|
|
736
|
-
} catch (err) {
|
|
737
|
-
zip.terminate();
|
|
738
|
-
output.destroy(err as Error);
|
|
739
|
-
await cleanupUploadTempPath(zipPath);
|
|
740
|
-
throw err;
|
|
741
|
-
}
|
|
742
|
-
return zipPath;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
654
|
async addResource(input: AddResourceInput, actorPeerId?: string): Promise<AddResourceResult> {
|
|
746
655
|
const pathOrUrl = input.pathOrUrl.trim();
|
|
747
656
|
if (!pathOrUrl) {
|
package/commands/setup.ts
CHANGED
|
@@ -6,9 +6,13 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { getEnv } from "../runtime-utils.js";
|
|
7
7
|
import {
|
|
8
8
|
cleanOpenVikingRequestHeaders,
|
|
9
|
-
resolveOpenVikingRequestHeaders,
|
|
10
9
|
type OpenVikingRequestHeaders,
|
|
11
10
|
} from "../request-headers.js";
|
|
11
|
+
import {
|
|
12
|
+
createSetupNetworkProbes,
|
|
13
|
+
type SetupApiKeyProbeResult,
|
|
14
|
+
type SetupHealthResult,
|
|
15
|
+
} from "../services/setup/probe-service.js";
|
|
12
16
|
|
|
13
17
|
const HOME = os.homedir();
|
|
14
18
|
const OPENCLAW_DIR = getEnv("OPENCLAW_STATE_DIR") || path.join(HOME, ".openclaw");
|
|
@@ -84,8 +88,8 @@ const CONFIG_KEYS_TO_PRESERVE = [
|
|
|
84
88
|
"logFindRequests",
|
|
85
89
|
] as const;
|
|
86
90
|
|
|
87
|
-
type PeerRole = "none" | "assistant" | "
|
|
88
|
-
const DEFAULT_SETUP_PEER_ROLE: PeerRole = "
|
|
91
|
+
type PeerRole = "none" | "assistant" | "sender";
|
|
92
|
+
const DEFAULT_SETUP_PEER_ROLE: PeerRole = "none";
|
|
89
93
|
type RecallTargetType = "resource" | "user" | "agent";
|
|
90
94
|
const ALLOWED_RECALL_TARGET_TYPES = ["resource", "user", "agent"] as const;
|
|
91
95
|
|
|
@@ -126,7 +130,8 @@ function isValidPeerPrefixInput(value: string): boolean {
|
|
|
126
130
|
function normalizePeerRole(value: unknown): PeerRole | undefined {
|
|
127
131
|
if (typeof value !== "string") return undefined;
|
|
128
132
|
const role = value.trim().toLowerCase();
|
|
129
|
-
if (role === "
|
|
133
|
+
if (role === "person") return "sender";
|
|
134
|
+
if (role === "none" || role === "assistant" || role === "sender") return role;
|
|
130
135
|
return undefined;
|
|
131
136
|
}
|
|
132
137
|
|
|
@@ -147,7 +152,7 @@ function resolveSetupPeerRole(value: unknown): PeerRole {
|
|
|
147
152
|
if (value === undefined) return DEFAULT_SETUP_PEER_ROLE;
|
|
148
153
|
const role = normalizePeerRole(value);
|
|
149
154
|
if (role) return role;
|
|
150
|
-
throw new Error('peer_role must be "none", "assistant", or "person"');
|
|
155
|
+
throw new Error('peer_role must be "none", "assistant", or "sender" (legacy alias: "person")');
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
function normalizeSetupRecallTargetTypes(value: unknown): RecallTargetType[] | undefined {
|
|
@@ -206,9 +211,20 @@ async function askPeerRole(
|
|
|
206
211
|
q: (prompt: string, def?: string) => Promise<string>,
|
|
207
212
|
defaultValue: PeerRole,
|
|
208
213
|
): Promise<PeerRole> {
|
|
214
|
+
console.log(
|
|
215
|
+
` ${tr(
|
|
216
|
+
zh,
|
|
217
|
+
"Memory scope — none (default): viking://user/<user_id>/memories, shared across all conversations; " +
|
|
218
|
+
"assistant: viking://user/<user_id>/peers/<assistant_id>/memories; " +
|
|
219
|
+
"sender: viking://user/<user_id>/peers/<sender_id>/memories (legacy alias: person).",
|
|
220
|
+
"记忆归属 —— none(默认):viking://user/<user_id>/memories,所有对话共享;" +
|
|
221
|
+
"assistant:viking://user/<user_id>/peers/<assistant_id>/memories;" +
|
|
222
|
+
"sender:viking://user/<user_id>/peers/<sender_id>/memories(兼容旧值 person)。",
|
|
223
|
+
)}`,
|
|
224
|
+
);
|
|
209
225
|
while (true) {
|
|
210
226
|
const value = await q(
|
|
211
|
-
tr(zh, "
|
|
227
|
+
tr(zh, "Memory scope (none/assistant/sender)", "记忆归属(none/assistant/sender)"),
|
|
212
228
|
defaultValue,
|
|
213
229
|
);
|
|
214
230
|
const role = normalizePeerRole(value);
|
|
@@ -216,8 +232,8 @@ async function askPeerRole(
|
|
|
216
232
|
console.log(
|
|
217
233
|
` ✗ ${tr(
|
|
218
234
|
zh,
|
|
219
|
-
'
|
|
220
|
-
'
|
|
235
|
+
'Memory scope must be "none", "assistant", or "sender" (legacy "person" is also accepted).',
|
|
236
|
+
'记忆归属必须是 "none"、"assistant" 或 "sender"(也兼容旧值 "person")。',
|
|
221
237
|
)}`,
|
|
222
238
|
);
|
|
223
239
|
}
|
|
@@ -257,14 +273,7 @@ function ask(rl: readline.Interface, prompt: string, defaultValue = ""): Promise
|
|
|
257
273
|
|
|
258
274
|
type VersionCompatibility = "compatible" | "server_too_old" | "server_too_new" | "unknown";
|
|
259
275
|
|
|
260
|
-
type HealthResult =
|
|
261
|
-
ok: boolean;
|
|
262
|
-
version: string;
|
|
263
|
-
error: string;
|
|
264
|
-
compatibility: VersionCompatibility;
|
|
265
|
-
pluginVersion: string;
|
|
266
|
-
compatRange: string;
|
|
267
|
-
};
|
|
276
|
+
type HealthResult = SetupHealthResult;
|
|
268
277
|
|
|
269
278
|
function parseVersionTuple(v: string): number[] | null {
|
|
270
279
|
const cleaned = v.replace(/^v/i, "").split("-")[0];
|
|
@@ -305,132 +314,13 @@ function formatCompatRange(): string {
|
|
|
305
314
|
return "any";
|
|
306
315
|
}
|
|
307
316
|
|
|
308
|
-
type ApiKeyProbeResult =
|
|
309
|
-
keyType: "user_key" | "root_key" | "no_key" | "unknown";
|
|
310
|
-
needsAccountId: boolean;
|
|
311
|
-
needsUserId: boolean;
|
|
312
|
-
detail: string;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
async function probeApiKeyType(
|
|
316
|
-
baseUrl: string,
|
|
317
|
-
apiKey?: string,
|
|
318
|
-
configuredHeaders?: OpenVikingRequestHeaders,
|
|
319
|
-
): Promise<ApiKeyProbeResult> {
|
|
320
|
-
if (!apiKey) return { keyType: "no_key", needsAccountId: false, needsUserId: false, detail: "No API key configured" };
|
|
321
|
-
|
|
322
|
-
const controller = new AbortController();
|
|
323
|
-
const timeoutId = setTimeout(() => controller.abort(), 10_000);
|
|
324
|
-
const sessionsUrl = `${baseUrl.replace(/\/+$/, "")}/api/v1/sessions?limit=1`;
|
|
325
|
-
try {
|
|
326
|
-
const configuredRequestHeaders = resolveOpenVikingRequestHeaders({ headers: configuredHeaders });
|
|
327
|
-
const headers: Record<string, string> = {};
|
|
328
|
-
headers["X-API-Key"] = apiKey;
|
|
329
|
-
Object.assign(headers, configuredRequestHeaders);
|
|
330
|
-
const response = await fetch(sessionsUrl, {
|
|
331
|
-
headers,
|
|
332
|
-
signal: controller.signal,
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
if (response.ok) {
|
|
336
|
-
return { keyType: "user_key", needsAccountId: false, needsUserId: false, detail: "API key has full user context" };
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Server raises InvalidArgumentError (-> HTTP 400) when a ROOT key calls
|
|
340
|
-
// tenant-scoped endpoints without X-OpenViking-Account / X-OpenViking-User.
|
|
341
|
-
// 401/403 may also be returned by older versions, FastAPI validation can
|
|
342
|
-
// surface as 422. Treat all four as candidates for tenant-context errors.
|
|
343
|
-
if ([400, 401, 403, 422].includes(response.status)) {
|
|
344
|
-
let body = "";
|
|
345
|
-
try {
|
|
346
|
-
body = await response.text();
|
|
347
|
-
} catch { /* ignore parse errors */ }
|
|
348
|
-
const lower = body.toLowerCase();
|
|
349
|
-
const needsAccount = /x-openviking-account|account[_ ]?id|account context|tenant/.test(lower);
|
|
350
|
-
const needsUser = /x-openviking-user|user[_ ]?id|user context|user key/.test(lower);
|
|
351
|
-
if (needsAccount || needsUser) {
|
|
352
|
-
return {
|
|
353
|
-
keyType: "root_key",
|
|
354
|
-
needsAccountId: needsAccount,
|
|
355
|
-
needsUserId: needsUser,
|
|
356
|
-
detail: body.slice(0, 200),
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Body did not name account/user/tenant explicitly (custom auth middleware,
|
|
361
|
-
// localized message, etc.). Re-probe with placeholder tenant headers; if
|
|
362
|
-
// the failure was due to missing tenant headers the response will change.
|
|
363
|
-
try {
|
|
364
|
-
const probeHeaders: Record<string, string> = {
|
|
365
|
-
"X-API-Key": apiKey,
|
|
366
|
-
"X-OpenViking-Account": "__probe__",
|
|
367
|
-
"X-OpenViking-User": "__probe__",
|
|
368
|
-
...configuredRequestHeaders,
|
|
369
|
-
};
|
|
370
|
-
const probe2 = await fetch(sessionsUrl, {
|
|
371
|
-
headers: probeHeaders,
|
|
372
|
-
signal: controller.signal,
|
|
373
|
-
});
|
|
374
|
-
if (probe2.status !== response.status) {
|
|
375
|
-
return {
|
|
376
|
-
keyType: "root_key",
|
|
377
|
-
needsAccountId: true,
|
|
378
|
-
needsUserId: true,
|
|
379
|
-
detail: body.slice(0, 200) || `HTTP ${response.status} -> ${probe2.status} after adding tenant headers`,
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
} catch { /* ignore probe errors, fall through to unknown */ }
|
|
317
|
+
type ApiKeyProbeResult = SetupApiKeyProbeResult;
|
|
383
318
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
return { keyType: "unknown", needsAccountId: false, needsUserId: false, detail: `HTTP ${response.status}` };
|
|
391
|
-
} catch (err) {
|
|
392
|
-
return { keyType: "unknown", needsAccountId: false, needsUserId: false, detail: String(err instanceof Error ? err.message : err) };
|
|
393
|
-
} finally {
|
|
394
|
-
clearTimeout(timeoutId);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async function checkServiceHealth(
|
|
399
|
-
baseUrl: string,
|
|
400
|
-
apiKey?: string,
|
|
401
|
-
configuredHeaders?: OpenVikingRequestHeaders,
|
|
402
|
-
): Promise<HealthResult> {
|
|
403
|
-
const controller = new AbortController();
|
|
404
|
-
const timeoutId = setTimeout(() => controller.abort(), 10_000);
|
|
405
|
-
try {
|
|
406
|
-
const configuredRequestHeaders = resolveOpenVikingRequestHeaders({ headers: configuredHeaders });
|
|
407
|
-
const headers: Record<string, string> = {};
|
|
408
|
-
if (apiKey) {
|
|
409
|
-
headers["X-API-Key"] = apiKey;
|
|
410
|
-
}
|
|
411
|
-
Object.assign(headers, configuredRequestHeaders);
|
|
412
|
-
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/health`, {
|
|
413
|
-
headers,
|
|
414
|
-
signal: controller.signal,
|
|
415
|
-
});
|
|
416
|
-
if (response.ok) {
|
|
417
|
-
try {
|
|
418
|
-
const data = await response.json() as Record<string, unknown>;
|
|
419
|
-
const result = (data.result ?? data) as Record<string, unknown>;
|
|
420
|
-
const version = String(result.version ?? data.version ?? "");
|
|
421
|
-
const compatibility = checkVersionCompatibility(version);
|
|
422
|
-
return { ok: true, version, error: "", compatibility, pluginVersion: PLUGIN_VERSION, compatRange: formatCompatRange() };
|
|
423
|
-
} catch {
|
|
424
|
-
return { ok: true, version: "", error: "", compatibility: "unknown", pluginVersion: PLUGIN_VERSION, compatRange: formatCompatRange() };
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return { ok: false, version: "", error: `HTTP ${response.status}`, compatibility: "unknown", pluginVersion: PLUGIN_VERSION, compatRange: formatCompatRange() };
|
|
428
|
-
} catch (err) {
|
|
429
|
-
return { ok: false, version: "", error: String(err instanceof Error ? err.message : err), compatibility: "unknown", pluginVersion: PLUGIN_VERSION, compatRange: formatCompatRange() };
|
|
430
|
-
} finally {
|
|
431
|
-
clearTimeout(timeoutId);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
319
|
+
const setupProbes = createSetupNetworkProbes({
|
|
320
|
+
pluginVersion: PLUGIN_VERSION,
|
|
321
|
+
compatRange: formatCompatRange(),
|
|
322
|
+
checkVersionCompatibility,
|
|
323
|
+
});
|
|
434
324
|
|
|
435
325
|
function readOpenClawConfig(configPath: string): Record<string, unknown> {
|
|
436
326
|
if (!fs.existsSync(configPath)) return {};
|
|
@@ -605,7 +495,7 @@ export function registerSetupCli(api: any): void {
|
|
|
605
495
|
.option("--zh", "Chinese prompts")
|
|
606
496
|
.option("--base-url <url>", "OpenViking server URL (enables non-interactive mode)")
|
|
607
497
|
.option("--api-key <key>", "API key for authentication")
|
|
608
|
-
.option("--peer-role <role>", "
|
|
498
|
+
.option("--peer-role <role>", "Memory scope: none (shared), assistant (per assistant), or sender (per sender); person is a legacy alias")
|
|
609
499
|
.option("--peer-prefix <prefix>", "Prefix for assistant peer_id values")
|
|
610
500
|
.option("--account-id <id>", "Account ID (required for root API keys)")
|
|
611
501
|
.option("--user-id <id>", "User ID (required for root API keys)")
|
|
@@ -801,7 +691,7 @@ async function runRemoteCheck(
|
|
|
801
691
|
const apiKey = existing.apiKey ? String(existing.apiKey) : undefined;
|
|
802
692
|
const headers = nonEmptyOpenVikingRequestHeaders(existing.headers);
|
|
803
693
|
console.log(tr(zh, `Testing connectivity to ${baseUrl}...`, `正在测试连接 ${baseUrl}...`));
|
|
804
|
-
const health = await checkServiceHealth(baseUrl, apiKey, headers);
|
|
694
|
+
const health = await setupProbes.checkServiceHealth(baseUrl, apiKey, headers);
|
|
805
695
|
if (health.ok) {
|
|
806
696
|
const ver = health.version ? ` (version: ${health.version})` : "";
|
|
807
697
|
console.log(` ✓ ${tr(zh, `Connected successfully${ver}`, `连接成功${ver}`)}`);
|
|
@@ -835,7 +725,7 @@ async function setupNonInteractive(
|
|
|
835
725
|
}
|
|
836
726
|
|
|
837
727
|
// Phase 1: validate connectivity and key type BEFORE writing config
|
|
838
|
-
const health = await checkServiceHealth(baseUrl, apiKey);
|
|
728
|
+
const health = await setupProbes.checkServiceHealth(baseUrl, apiKey);
|
|
839
729
|
|
|
840
730
|
if (!health.ok && !allowOffline) {
|
|
841
731
|
return {
|
|
@@ -848,7 +738,7 @@ async function setupNonInteractive(
|
|
|
848
738
|
};
|
|
849
739
|
}
|
|
850
740
|
|
|
851
|
-
const keyProbe = health.ok ? await probeApiKeyType(baseUrl, apiKey) : undefined;
|
|
741
|
+
const keyProbe = health.ok ? await setupProbes.probeApiKeyType(baseUrl, apiKey) : undefined;
|
|
852
742
|
|
|
853
743
|
if (keyProbe?.keyType === "root_key" && (!accountId || !userId)) {
|
|
854
744
|
const missing: string[] = [];
|
|
@@ -984,8 +874,8 @@ async function getStatus(configPath: string): Promise<StatusResult> {
|
|
|
984
874
|
const baseUrl = String(existing.baseUrl ?? DEFAULT_REMOTE_URL);
|
|
985
875
|
const apiKey = existing.apiKey ? String(existing.apiKey) : undefined;
|
|
986
876
|
const headers = nonEmptyOpenVikingRequestHeaders(existing.headers);
|
|
987
|
-
const health = await checkServiceHealth(baseUrl, apiKey, headers);
|
|
988
|
-
const keyProbe = health.ok ? await probeApiKeyType(baseUrl, apiKey, headers) : undefined;
|
|
877
|
+
const health = await setupProbes.checkServiceHealth(baseUrl, apiKey, headers);
|
|
878
|
+
const keyProbe = health.ok ? await setupProbes.probeApiKeyType(baseUrl, apiKey, headers) : undefined;
|
|
989
879
|
|
|
990
880
|
const peerPrefix = resolveExistingPeerPrefix(existing);
|
|
991
881
|
return {
|
|
@@ -1107,7 +997,7 @@ async function setupRemote(
|
|
|
1107
997
|
|
|
1108
998
|
if (apiKey) {
|
|
1109
999
|
console.log(tr(zh, " Detecting API key type...", " 正在检测 API Key 类型..."));
|
|
1110
|
-
const probe = await probeApiKeyType(baseUrl, apiKey, headers);
|
|
1000
|
+
const probe = await setupProbes.probeApiKeyType(baseUrl, apiKey, headers);
|
|
1111
1001
|
if (probe.keyType === "root_key") {
|
|
1112
1002
|
console.log(tr(zh,
|
|
1113
1003
|
" ⚠ Root API key detected. accountId and userId are required.",
|
|
@@ -1128,7 +1018,7 @@ async function setupRemote(
|
|
|
1128
1018
|
console.log("");
|
|
1129
1019
|
|
|
1130
1020
|
console.log(tr(zh, `Testing connectivity to ${baseUrl}...`, `正在测试连接 ${baseUrl}...`));
|
|
1131
|
-
const health = await checkServiceHealth(baseUrl, apiKey || undefined, headers);
|
|
1021
|
+
const health = await setupProbes.checkServiceHealth(baseUrl, apiKey || undefined, headers);
|
|
1132
1022
|
if (health.ok) {
|
|
1133
1023
|
const ver = health.version ? ` (version: ${health.version})` : "";
|
|
1134
1024
|
console.log(` ✓ ${tr(zh, `Connected successfully${ver}`, `连接成功${ver}`)}`);
|
|
@@ -1182,6 +1072,8 @@ async function setupRemote(
|
|
|
1182
1072
|
export const __test__ = {
|
|
1183
1073
|
isLegacyLocalMode,
|
|
1184
1074
|
isValidPeerPrefixInput,
|
|
1075
|
+
normalizePeerRole,
|
|
1076
|
+
resolveSetupPeerRole,
|
|
1185
1077
|
normalizeSetupRecallTargetTypes,
|
|
1186
1078
|
activateContextEngineSlot,
|
|
1187
1079
|
isContextEngineSlotActive,
|
|
@@ -1190,7 +1082,6 @@ export const __test__ = {
|
|
|
1190
1082
|
checkVersionCompatibility,
|
|
1191
1083
|
parseVersionTuple,
|
|
1192
1084
|
compareVersions,
|
|
1193
|
-
probeApiKeyType,
|
|
1194
1085
|
ensureInstallRecord,
|
|
1195
1086
|
findPluginPackageRoot,
|
|
1196
1087
|
readCompatRangeFromManifest,
|