openclaw-openviking-setup-helper 0.3.1-beta.0 → 2026.6.18
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.js +772 -613
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -9,34 +9,34 @@
|
|
|
9
9
|
* ov-install
|
|
10
10
|
* openclaw-openviking-install
|
|
11
11
|
*
|
|
12
|
-
* Direct run:
|
|
13
|
-
* node install.js [ --base-url URL ] [ --api-key KEY ] [ --zh ] [ --workdir PATH ] [ --upgrade-plugin ]
|
|
14
|
-
* [ --plugin-version=VERSION ] [ --plugin-source=npm|github ]
|
|
15
|
-
*
|
|
16
|
-
* Environment variables:
|
|
17
|
-
* PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION (or BRANCH),
|
|
18
|
-
* OPENVIKING_BASE_URL, OPENVIKING_API_KEY, SKIP_OPENCLAW, NPM_REGISTRY
|
|
19
|
-
*/
|
|
12
|
+
* Direct run:
|
|
13
|
+
* node install.js [ --base-url URL ] [ --api-key KEY ] [ --zh ] [ --workdir PATH ] [ --upgrade-plugin ]
|
|
14
|
+
* [ --plugin-version=VERSION ] [ --plugin-source=npm|github ]
|
|
15
|
+
*
|
|
16
|
+
* Environment variables:
|
|
17
|
+
* PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION (or BRANCH),
|
|
18
|
+
* OPENVIKING_BASE_URL, OPENVIKING_API_KEY, SKIP_OPENCLAW, NPM_REGISTRY
|
|
19
|
+
*/
|
|
20
20
|
|
|
21
21
|
import { spawn } from "node:child_process";
|
|
22
|
-
import { cp, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
23
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
24
|
-
import { basename, dirname, join } from "node:path";
|
|
25
|
-
import { tmpdir } from "node:os";
|
|
26
|
-
import { createInterface } from "node:readline";
|
|
27
|
-
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { cp, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
23
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
24
|
+
import { basename, dirname, join } from "node:path";
|
|
25
|
+
import { tmpdir } from "node:os";
|
|
26
|
+
import { createInterface } from "node:readline";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
28
|
|
|
29
29
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
30
|
|
|
31
|
-
let REPO = process.env.REPO || "volcengine/OpenViking";
|
|
32
|
-
const DEFAULT_PLUGIN_NPM_PACKAGE = "@openviking/openclaw-plugin";
|
|
33
|
-
let pluginNpmPackage = (process.env.PLUGIN_NPM_PACKAGE || DEFAULT_PLUGIN_NPM_PACKAGE).trim();
|
|
34
|
-
let pluginSource = (process.env.PLUGIN_SOURCE || "npm").trim().toLowerCase();
|
|
35
|
-
let pluginSourceExplicit = Boolean(process.env.PLUGIN_SOURCE);
|
|
36
|
-
// PLUGIN_VERSION takes precedence over BRANCH (legacy). If omitted, resolve the latest npm dist-tag or GitHub tag.
|
|
37
|
-
const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
|
|
38
|
-
let PLUGIN_VERSION = pluginVersionEnv;
|
|
39
|
-
let pluginVersionExplicit = Boolean(pluginVersionEnv);
|
|
31
|
+
let REPO = process.env.REPO || "volcengine/OpenViking";
|
|
32
|
+
const DEFAULT_PLUGIN_NPM_PACKAGE = "@openviking/openclaw-plugin";
|
|
33
|
+
let pluginNpmPackage = (process.env.PLUGIN_NPM_PACKAGE || DEFAULT_PLUGIN_NPM_PACKAGE).trim();
|
|
34
|
+
let pluginSource = (process.env.PLUGIN_SOURCE || "npm").trim().toLowerCase();
|
|
35
|
+
let pluginSourceExplicit = Boolean(process.env.PLUGIN_SOURCE);
|
|
36
|
+
// PLUGIN_VERSION takes precedence over BRANCH (legacy). If omitted, resolve the latest npm dist-tag or GitHub tag.
|
|
37
|
+
const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
|
|
38
|
+
let PLUGIN_VERSION = pluginVersionEnv;
|
|
39
|
+
let pluginVersionExplicit = Boolean(pluginVersionEnv);
|
|
40
40
|
const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
41
41
|
const DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION = "2026.5.3";
|
|
42
42
|
const OPENCLAW_SHORT_VERSION_YEAR = 2026;
|
|
@@ -102,27 +102,30 @@ let resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
|
102
102
|
let resolvedNpmBuildScript = "build";
|
|
103
103
|
let resolvedNpmPruneAfterBuild = true;
|
|
104
104
|
let resolvedMinOpenclawVersion = "";
|
|
105
|
-
let resolvedMinOpenvikingVersion = "";
|
|
106
|
-
let resolvedPluginReleaseId = "";
|
|
107
|
-
let detectedOpenClawVersion = "";
|
|
108
|
-
let npmPackageTempDir = "";
|
|
109
|
-
let npmPackageExtractDir = "";
|
|
105
|
+
let resolvedMinOpenvikingVersion = "";
|
|
106
|
+
let resolvedPluginReleaseId = "";
|
|
107
|
+
let detectedOpenClawVersion = "";
|
|
108
|
+
let npmPackageTempDir = "";
|
|
109
|
+
let npmPackageExtractDir = "";
|
|
110
110
|
|
|
111
111
|
let nonInteractive = false;
|
|
112
112
|
let langZh = false;
|
|
113
113
|
let workdirExplicit = false;
|
|
114
114
|
let upgradePluginOnly = false;
|
|
115
115
|
let rollbackLastUpgrade = false;
|
|
116
|
-
let showCurrentVersion = false;
|
|
117
|
-
let uninstallPlugin = false;
|
|
118
|
-
let forceSlotExplicit = false;
|
|
119
|
-
let allowOfflineExplicit = false;
|
|
116
|
+
let showCurrentVersion = false;
|
|
117
|
+
let uninstallPlugin = false;
|
|
118
|
+
let forceSlotExplicit = false;
|
|
119
|
+
let allowOfflineExplicit = false;
|
|
120
120
|
|
|
121
121
|
const selectedMode = "remote";
|
|
122
122
|
const baseUrlFromEnv = !!process.env.OPENVIKING_BASE_URL;
|
|
123
123
|
let remoteBaseUrl = (process.env.OPENVIKING_BASE_URL || "http://127.0.0.1:1933").trim();
|
|
124
124
|
let remoteApiKey = (process.env.OPENVIKING_API_KEY || "").trim();
|
|
125
|
-
let
|
|
125
|
+
let remotePeerRole = (process.env.OPENVIKING_PEER_ROLE || "").trim().toLowerCase();
|
|
126
|
+
let remotePeerPrefix = (process.env.OPENVIKING_PEER_PREFIX || "").trim();
|
|
127
|
+
let peerRoleExplicit = !!process.env.OPENVIKING_PEER_ROLE;
|
|
128
|
+
if (!remotePeerRole) remotePeerRole = "assistant";
|
|
126
129
|
let remoteAccountId = (process.env.OPENVIKING_ACCOUNT_ID || "").trim();
|
|
127
130
|
let remoteUserId = (process.env.OPENVIKING_USER_ID || "").trim();
|
|
128
131
|
let baseUrlExplicit = baseUrlFromEnv;
|
|
@@ -156,19 +159,19 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
156
159
|
rollbackLastUpgrade = true;
|
|
157
160
|
continue;
|
|
158
161
|
}
|
|
159
|
-
if (arg === "--uninstall" || arg === "--remove") {
|
|
160
|
-
uninstallPlugin = true;
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
if (arg === "--force-slot") {
|
|
164
|
-
forceSlotExplicit = true;
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (arg === "--allow-offline") {
|
|
168
|
-
allowOfflineExplicit = true;
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
if (arg === "--workdir") {
|
|
162
|
+
if (arg === "--uninstall" || arg === "--remove") {
|
|
163
|
+
uninstallPlugin = true;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (arg === "--force-slot") {
|
|
167
|
+
forceSlotExplicit = true;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (arg === "--allow-offline") {
|
|
171
|
+
allowOfflineExplicit = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (arg === "--workdir") {
|
|
172
175
|
const workdir = argv[i + 1]?.trim();
|
|
173
176
|
if (!workdir) {
|
|
174
177
|
console.error("--workdir requires a path");
|
|
@@ -185,81 +188,81 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
185
188
|
console.error("--plugin-version requires a value");
|
|
186
189
|
process.exit(1);
|
|
187
190
|
}
|
|
188
|
-
PLUGIN_VERSION = version;
|
|
189
|
-
pluginVersionExplicit = true;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
191
|
+
PLUGIN_VERSION = version;
|
|
192
|
+
pluginVersionExplicit = true;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
192
195
|
if (arg === "--plugin-version") {
|
|
193
196
|
const version = argv[i + 1]?.trim();
|
|
194
197
|
if (!version) {
|
|
195
198
|
console.error("--plugin-version requires a value");
|
|
196
199
|
process.exit(1);
|
|
197
200
|
}
|
|
198
|
-
PLUGIN_VERSION = version;
|
|
199
|
-
pluginVersionExplicit = true;
|
|
200
|
-
i += 1;
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (arg.startsWith("--github-repo=")) {
|
|
204
|
-
REPO = arg.slice("--github-repo=".length).trim();
|
|
205
|
-
if (!pluginSourceExplicit) {
|
|
206
|
-
pluginSource = "github";
|
|
207
|
-
}
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (arg === "--github-repo") {
|
|
201
|
+
PLUGIN_VERSION = version;
|
|
202
|
+
pluginVersionExplicit = true;
|
|
203
|
+
i += 1;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (arg.startsWith("--github-repo=")) {
|
|
207
|
+
REPO = arg.slice("--github-repo=".length).trim();
|
|
208
|
+
if (!pluginSourceExplicit) {
|
|
209
|
+
pluginSource = "github";
|
|
210
|
+
}
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (arg === "--github-repo") {
|
|
211
214
|
const repo = argv[i + 1]?.trim();
|
|
212
215
|
if (!repo) {
|
|
213
216
|
console.error("--github-repo requires a value (e.g. owner/repo)");
|
|
214
217
|
process.exit(1);
|
|
215
|
-
}
|
|
216
|
-
REPO = repo;
|
|
217
|
-
if (!pluginSourceExplicit) {
|
|
218
|
-
pluginSource = "github";
|
|
219
|
-
}
|
|
220
|
-
i += 1;
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (arg.startsWith("--plugin-source=") || arg.startsWith("--source=")) {
|
|
224
|
-
const value = arg.includes("--plugin-source=")
|
|
225
|
-
? arg.slice("--plugin-source=".length).trim()
|
|
226
|
-
: arg.slice("--source=".length).trim();
|
|
227
|
-
pluginSource = value.toLowerCase();
|
|
228
|
-
pluginSourceExplicit = true;
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
if (arg === "--plugin-source" || arg === "--source") {
|
|
232
|
-
const value = argv[i + 1]?.trim();
|
|
233
|
-
if (!value) {
|
|
234
|
-
console.error(`${arg} requires a value (npm or github)`);
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
237
|
-
pluginSource = value.toLowerCase();
|
|
238
|
-
pluginSourceExplicit = true;
|
|
239
|
-
i += 1;
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (arg.startsWith("--plugin-package=") || arg.startsWith("--npm-package=")) {
|
|
243
|
-
const value = arg.includes("--plugin-package=")
|
|
244
|
-
? arg.slice("--plugin-package=".length).trim()
|
|
245
|
-
: arg.slice("--npm-package=".length).trim();
|
|
246
|
-
if (!value) {
|
|
247
|
-
console.error("--plugin-package requires a package name");
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
pluginNpmPackage = value;
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
if (arg === "--plugin-package" || arg === "--npm-package") {
|
|
254
|
-
const value = argv[i + 1]?.trim();
|
|
255
|
-
if (!value) {
|
|
256
|
-
console.error(`${arg} requires a package name`);
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
259
|
-
pluginNpmPackage = value;
|
|
260
|
-
i += 1;
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
218
|
+
}
|
|
219
|
+
REPO = repo;
|
|
220
|
+
if (!pluginSourceExplicit) {
|
|
221
|
+
pluginSource = "github";
|
|
222
|
+
}
|
|
223
|
+
i += 1;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (arg.startsWith("--plugin-source=") || arg.startsWith("--source=")) {
|
|
227
|
+
const value = arg.includes("--plugin-source=")
|
|
228
|
+
? arg.slice("--plugin-source=".length).trim()
|
|
229
|
+
: arg.slice("--source=".length).trim();
|
|
230
|
+
pluginSource = value.toLowerCase();
|
|
231
|
+
pluginSourceExplicit = true;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (arg === "--plugin-source" || arg === "--source") {
|
|
235
|
+
const value = argv[i + 1]?.trim();
|
|
236
|
+
if (!value) {
|
|
237
|
+
console.error(`${arg} requires a value (npm or github)`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
pluginSource = value.toLowerCase();
|
|
241
|
+
pluginSourceExplicit = true;
|
|
242
|
+
i += 1;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (arg.startsWith("--plugin-package=") || arg.startsWith("--npm-package=")) {
|
|
246
|
+
const value = arg.includes("--plugin-package=")
|
|
247
|
+
? arg.slice("--plugin-package=".length).trim()
|
|
248
|
+
: arg.slice("--npm-package=".length).trim();
|
|
249
|
+
if (!value) {
|
|
250
|
+
console.error("--plugin-package requires a package name");
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
pluginNpmPackage = value;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (arg === "--plugin-package" || arg === "--npm-package") {
|
|
257
|
+
const value = argv[i + 1]?.trim();
|
|
258
|
+
if (!value) {
|
|
259
|
+
console.error(`${arg} requires a package name`);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
pluginNpmPackage = value;
|
|
263
|
+
i += 1;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
263
266
|
if (arg === "--base-url") {
|
|
264
267
|
const val = argv[i + 1]?.trim();
|
|
265
268
|
if (!val) { console.error("--base-url requires a URL"); process.exit(1); }
|
|
@@ -284,15 +287,32 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
284
287
|
remoteApiKey = arg.slice("--api-key=".length).trim();
|
|
285
288
|
continue;
|
|
286
289
|
}
|
|
287
|
-
if (arg === "--
|
|
290
|
+
if (arg === "--peer-role") {
|
|
291
|
+
const val = argv[i + 1]?.trim();
|
|
292
|
+
if (!val) { console.error("--peer-role requires a value"); process.exit(1); }
|
|
293
|
+
const role = normalizePeerRole(val);
|
|
294
|
+
if (!role) { console.error('--peer-role must be "none", "assistant", or "person"'); process.exit(1); }
|
|
295
|
+
remotePeerRole = role;
|
|
296
|
+
peerRoleExplicit = true;
|
|
297
|
+
i += 1;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (arg.startsWith("--peer-role=")) {
|
|
301
|
+
const role = normalizePeerRole(arg.slice("--peer-role=".length));
|
|
302
|
+
if (!role) { console.error('--peer-role must be "none", "assistant", or "person"'); process.exit(1); }
|
|
303
|
+
remotePeerRole = role;
|
|
304
|
+
peerRoleExplicit = true;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (arg === "--peer-prefix") {
|
|
288
308
|
const val = argv[i + 1]?.trim();
|
|
289
|
-
if (!val) { console.error("--
|
|
290
|
-
|
|
309
|
+
if (!val) { console.error("--peer-prefix requires a value"); process.exit(1); }
|
|
310
|
+
remotePeerPrefix = val;
|
|
291
311
|
i += 1;
|
|
292
312
|
continue;
|
|
293
313
|
}
|
|
294
|
-
if (arg.startsWith("--
|
|
295
|
-
|
|
314
|
+
if (arg.startsWith("--peer-prefix=")) {
|
|
315
|
+
remotePeerPrefix = arg.slice("--peer-prefix=".length).trim();
|
|
296
316
|
continue;
|
|
297
317
|
}
|
|
298
318
|
if (arg === "--account-id") {
|
|
@@ -323,6 +343,12 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
323
343
|
}
|
|
324
344
|
}
|
|
325
345
|
|
|
346
|
+
remotePeerRole = normalizePeerRole(remotePeerRole) || "assistant";
|
|
347
|
+
if (!isValidPeerPrefixInput(remotePeerPrefix)) {
|
|
348
|
+
console.error("--peer-prefix may only contain letters, digits, underscores, and hyphens");
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
|
|
326
352
|
nonInteractive = baseUrlExplicit;
|
|
327
353
|
|
|
328
354
|
function setOpenClawDir(dir) {
|
|
@@ -331,13 +357,13 @@ function setOpenClawDir(dir) {
|
|
|
331
357
|
|
|
332
358
|
function printHelp() {
|
|
333
359
|
console.log("Usage: node install.js [ OPTIONS ]");
|
|
334
|
-
console.log("");
|
|
335
|
-
console.log("Options:");
|
|
336
|
-
console.log(" --plugin-source=npm|github");
|
|
337
|
-
console.log(" Plugin download source (default: npm)");
|
|
338
|
-
console.log(" --plugin-package=NAME npm plugin package (default: @openviking/openclaw-plugin)");
|
|
339
|
-
console.log(" --github-repo=OWNER/REPO GitHub repository (implies --plugin-source=github unless source is set)");
|
|
340
|
-
console.log(" --plugin-version=VERSION Plugin version (npm version/tag or Git tag; default: npm latest)");
|
|
360
|
+
console.log("");
|
|
361
|
+
console.log("Options:");
|
|
362
|
+
console.log(" --plugin-source=npm|github");
|
|
363
|
+
console.log(" Plugin download source (default: npm)");
|
|
364
|
+
console.log(" --plugin-package=NAME npm plugin package (default: @openviking/openclaw-plugin)");
|
|
365
|
+
console.log(" --github-repo=OWNER/REPO GitHub repository (implies --plugin-source=github unless source is set)");
|
|
366
|
+
console.log(" --plugin-version=VERSION Plugin version (npm version/tag or Git tag; default: npm latest)");
|
|
341
367
|
console.log(" --workdir PATH OpenClaw config directory (default: ~/.openclaw)");
|
|
342
368
|
console.log(" --current-version Print installed plugin version and exit");
|
|
343
369
|
console.log(" --update, --upgrade-plugin");
|
|
@@ -347,12 +373,13 @@ function printHelp() {
|
|
|
347
373
|
console.log(" --uninstall, --remove Uninstall OpenViking plugin from OpenClaw (backup config, remove plugin entries)");
|
|
348
374
|
console.log(" --base-url=URL OpenViking server URL (default: $OPENVIKING_BASE_URL or http://127.0.0.1:1933)");
|
|
349
375
|
console.log(" --api-key=KEY OpenViking API key (default: $OPENVIKING_API_KEY)");
|
|
350
|
-
console.log(" --
|
|
351
|
-
console.log(" --
|
|
352
|
-
console.log(" --
|
|
353
|
-
console.log(" --
|
|
354
|
-
console.log(" --
|
|
355
|
-
console.log(" --
|
|
376
|
+
console.log(" --peer-role=ROLE Peer role: none, assistant, or person (default: $OPENVIKING_PEER_ROLE or assistant)");
|
|
377
|
+
console.log(" --peer-prefix=PREFIX Prefix for assistant peer_id values (default: $OPENVIKING_PEER_PREFIX)");
|
|
378
|
+
console.log(" --account-id=ID Account ID for root API key (default: $OPENVIKING_ACCOUNT_ID)");
|
|
379
|
+
console.log(" --user-id=ID User ID for root API key (default: $OPENVIKING_USER_ID)");
|
|
380
|
+
console.log(" --force-slot Explicitly replace an existing contextEngine slot owner");
|
|
381
|
+
console.log(" --allow-offline Explicitly save config when the OpenViking server is unreachable");
|
|
382
|
+
console.log(" --zh Chinese prompts");
|
|
356
383
|
console.log(" -h, --help This help");
|
|
357
384
|
console.log("");
|
|
358
385
|
console.log("Examples:");
|
|
@@ -362,11 +389,11 @@ function printHelp() {
|
|
|
362
389
|
console.log(" # Show installed versions");
|
|
363
390
|
console.log(" node install.js --current-version");
|
|
364
391
|
console.log("");
|
|
365
|
-
console.log(" # Install a specific release version");
|
|
366
|
-
console.log(" node install.js --plugin-version=2026.
|
|
367
|
-
console.log("");
|
|
368
|
-
console.log(" # Install from a fork repository");
|
|
369
|
-
console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
|
|
392
|
+
console.log(" # Install a specific release version");
|
|
393
|
+
console.log(" node install.js --plugin-version=2026.6.18");
|
|
394
|
+
console.log("");
|
|
395
|
+
console.log(" # Install from a fork repository");
|
|
396
|
+
console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
|
|
370
397
|
console.log("");
|
|
371
398
|
console.log(" # Install specific plugin version");
|
|
372
399
|
console.log(" node install.js --plugin-version=v0.2.8");
|
|
@@ -377,8 +404,8 @@ function printHelp() {
|
|
|
377
404
|
console.log(" # Roll back the last plugin upgrade");
|
|
378
405
|
console.log(" node install.js --rollback");
|
|
379
406
|
console.log("");
|
|
380
|
-
console.log("Env: PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
|
|
381
|
-
}
|
|
407
|
+
console.log("Env: PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
|
|
408
|
+
}
|
|
382
409
|
|
|
383
410
|
function formatCliArg(value) {
|
|
384
411
|
if (!value) {
|
|
@@ -471,27 +498,32 @@ function runCapture(cmd, args, opts = {}) {
|
|
|
471
498
|
});
|
|
472
499
|
}
|
|
473
500
|
|
|
474
|
-
function question(prompt, defaultValue = "") {
|
|
475
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
476
|
-
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
477
|
-
return new Promise((resolve) => {
|
|
478
|
-
rl.question(`${prompt}${suffix}: `, (answer) => {
|
|
501
|
+
function question(prompt, defaultValue = "") {
|
|
502
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
503
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
504
|
+
return new Promise((resolve) => {
|
|
505
|
+
rl.question(`${prompt}${suffix}: `, (answer) => {
|
|
479
506
|
rl.close();
|
|
480
507
|
resolve((answer ?? defaultValue).trim() || defaultValue);
|
|
481
508
|
});
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
function isYes(answer) {
|
|
486
|
-
const normalized = String(answer || "").trim().toLowerCase();
|
|
487
|
-
return normalized === "y" || normalized === "yes";
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function isYes(answer) {
|
|
513
|
+
const normalized = String(answer || "").trim().toLowerCase();
|
|
514
|
+
return normalized === "y" || normalized === "yes";
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function isValidPeerPrefixInput(value) {
|
|
491
518
|
const trimmed = String(value || "").trim();
|
|
492
519
|
return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
|
|
493
520
|
}
|
|
494
521
|
|
|
522
|
+
function normalizePeerRole(value) {
|
|
523
|
+
const role = String(value || "").trim().toLowerCase();
|
|
524
|
+
return role === "none" || role === "assistant" || role === "person" ? role : null;
|
|
525
|
+
}
|
|
526
|
+
|
|
495
527
|
function parseJsonObjectFromOutput(output) {
|
|
496
528
|
const text = String(output || "").trim();
|
|
497
529
|
if (!text) return null;
|
|
@@ -513,18 +545,33 @@ function parseJsonObjectFromOutput(output) {
|
|
|
513
545
|
return null;
|
|
514
546
|
}
|
|
515
547
|
|
|
516
|
-
async function
|
|
548
|
+
async function questionPeerRole(defaultValue = "assistant") {
|
|
549
|
+
while (true) {
|
|
550
|
+
const answer = await question(
|
|
551
|
+
tr("Peer Role (none/assistant/person)", "Peer Role(none/assistant/person)"),
|
|
552
|
+
defaultValue,
|
|
553
|
+
);
|
|
554
|
+
const role = normalizePeerRole(answer);
|
|
555
|
+
if (role) return role;
|
|
556
|
+
warn(tr(
|
|
557
|
+
'Peer Role must be "none", "assistant", or "person".',
|
|
558
|
+
'Peer Role 必须是 "none"、"assistant" 或 "person"。',
|
|
559
|
+
));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async function questionPeerPrefix(defaultValue = "") {
|
|
517
564
|
while (true) {
|
|
518
565
|
const answer = (await question(
|
|
519
|
-
tr("
|
|
566
|
+
tr("Peer Prefix (optional)", "Peer Prefix(可选)"),
|
|
520
567
|
defaultValue,
|
|
521
568
|
)).trim();
|
|
522
|
-
if (
|
|
569
|
+
if (isValidPeerPrefixInput(answer)) {
|
|
523
570
|
return answer;
|
|
524
571
|
}
|
|
525
572
|
warn(tr(
|
|
526
|
-
"
|
|
527
|
-
"
|
|
573
|
+
"Peer Prefix may only contain letters, digits, underscores, and hyphens, or be empty.",
|
|
574
|
+
"Peer Prefix 只能包含字母、数字、下划线和连字符,或留空。",
|
|
528
575
|
));
|
|
529
576
|
}
|
|
530
577
|
}
|
|
@@ -575,7 +622,10 @@ async function collectRemoteConfig() {
|
|
|
575
622
|
if (nonInteractive) return;
|
|
576
623
|
remoteBaseUrl = await question(tr("OpenViking server URL", "OpenViking 服务器地址"), remoteBaseUrl);
|
|
577
624
|
remoteApiKey = await question(tr("API Key (optional)", "API Key(可选)"), remoteApiKey);
|
|
578
|
-
|
|
625
|
+
remotePeerRole = await questionPeerRole(remotePeerRole);
|
|
626
|
+
remotePeerPrefix = remotePeerRole === "assistant"
|
|
627
|
+
? await questionPeerPrefix(remotePeerPrefix)
|
|
628
|
+
: "";
|
|
579
629
|
}
|
|
580
630
|
|
|
581
631
|
async function checkOpenClaw() {
|
|
@@ -679,30 +729,30 @@ if (upgradePluginOnly && rollbackLastUpgrade) {
|
|
|
679
729
|
process.exit(1);
|
|
680
730
|
}
|
|
681
731
|
|
|
682
|
-
if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
|
|
683
|
-
console.error("--uninstall cannot be used with --upgrade-plugin or --rollback");
|
|
684
|
-
process.exit(1);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
if (!["npm", "github"].includes(pluginSource)) {
|
|
688
|
-
console.error("--plugin-source must be either npm or github");
|
|
689
|
-
process.exit(1);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function looksLikeLegacyGitHubRef(value) {
|
|
693
|
-
const ref = String(value || "").trim();
|
|
694
|
-
if (!ref) return false;
|
|
695
|
-
if (/^v\d+(\.\d+){1,2}([-.].*)?$/i.test(ref)) return true;
|
|
696
|
-
if (["main", "master"].includes(ref.toLowerCase())) return true;
|
|
697
|
-
return false;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (!pluginSourceExplicit && pluginVersionExplicit && looksLikeLegacyGitHubRef(PLUGIN_VERSION)) {
|
|
701
|
-
pluginSource = "github";
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// Detect OpenClaw version
|
|
705
|
-
async function detectOpenClawVersion() {
|
|
732
|
+
if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
|
|
733
|
+
console.error("--uninstall cannot be used with --upgrade-plugin or --rollback");
|
|
734
|
+
process.exit(1);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (!["npm", "github"].includes(pluginSource)) {
|
|
738
|
+
console.error("--plugin-source must be either npm or github");
|
|
739
|
+
process.exit(1);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function looksLikeLegacyGitHubRef(value) {
|
|
743
|
+
const ref = String(value || "").trim();
|
|
744
|
+
if (!ref) return false;
|
|
745
|
+
if (/^v\d+(\.\d+){1,2}([-.].*)?$/i.test(ref)) return true;
|
|
746
|
+
if (["main", "master"].includes(ref.toLowerCase())) return true;
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (!pluginSourceExplicit && pluginVersionExplicit && looksLikeLegacyGitHubRef(PLUGIN_VERSION)) {
|
|
751
|
+
pluginSource = "github";
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Detect OpenClaw version
|
|
755
|
+
async function detectOpenClawVersion() {
|
|
706
756
|
if (detectedOpenClawVersion) {
|
|
707
757
|
return detectedOpenClawVersion;
|
|
708
758
|
}
|
|
@@ -754,10 +804,10 @@ function compareSemverDesc(a, b) {
|
|
|
754
804
|
return versionGte(a, b) ? -1 : 1;
|
|
755
805
|
}
|
|
756
806
|
|
|
757
|
-
function pickLatestPluginTag(tagNames) {
|
|
758
|
-
const normalized = tagNames
|
|
759
|
-
.map((tag) => String(tag ?? "").trim())
|
|
760
|
-
.filter(Boolean);
|
|
807
|
+
function pickLatestPluginTag(tagNames) {
|
|
808
|
+
const normalized = tagNames
|
|
809
|
+
.map((tag) => String(tag ?? "").trim())
|
|
810
|
+
.filter(Boolean);
|
|
761
811
|
|
|
762
812
|
const semverTags = normalized
|
|
763
813
|
.filter((tag) => isSemverLike(tag))
|
|
@@ -766,118 +816,118 @@ function pickLatestPluginTag(tagNames) {
|
|
|
766
816
|
if (semverTags.length > 0) {
|
|
767
817
|
return semverTags[0];
|
|
768
818
|
}
|
|
769
|
-
|
|
770
|
-
return normalized[0] || "";
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
function npmPackageSpec(version = PLUGIN_VERSION) {
|
|
774
|
-
return version ? `${pluginNpmPackage}@${version}` : pluginNpmPackage;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
function parseNpmJsonOutput(output) {
|
|
778
|
-
const text = String(output || "").trim();
|
|
779
|
-
if (!text) return null;
|
|
780
|
-
try {
|
|
781
|
-
return JSON.parse(text);
|
|
782
|
-
} catch {
|
|
783
|
-
const firstArray = text.indexOf("[");
|
|
784
|
-
const lastArray = text.lastIndexOf("]");
|
|
785
|
-
if (firstArray >= 0 && lastArray > firstArray) {
|
|
786
|
-
try {
|
|
787
|
-
return JSON.parse(text.slice(firstArray, lastArray + 1));
|
|
788
|
-
} catch {}
|
|
789
|
-
}
|
|
790
|
-
const firstObject = text.indexOf("{");
|
|
791
|
-
const lastObject = text.lastIndexOf("}");
|
|
792
|
-
if (firstObject >= 0 && lastObject > firstObject) {
|
|
793
|
-
try {
|
|
794
|
-
return JSON.parse(text.slice(firstObject, lastObject + 1));
|
|
795
|
-
} catch {}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
return null;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
async function resolveDefaultPluginVersionFromNpm() {
|
|
802
|
-
info(tr(
|
|
803
|
-
`No plugin version specified; resolving latest npm version from ${pluginNpmPackage}...`,
|
|
804
|
-
`No plugin version specified; resolving latest npm version from ${pluginNpmPackage}...`,
|
|
805
|
-
));
|
|
806
|
-
|
|
807
|
-
const result = await runCapture("npm", [
|
|
808
|
-
"view",
|
|
809
|
-
`${pluginNpmPackage}@latest`,
|
|
810
|
-
"version",
|
|
811
|
-
"--json",
|
|
812
|
-
"--registry",
|
|
813
|
-
NPM_REGISTRY,
|
|
814
|
-
], { shell: IS_WIN });
|
|
815
|
-
|
|
816
|
-
if (result.code === 0) {
|
|
817
|
-
const parsed = parseNpmJsonOutput(result.out);
|
|
818
|
-
const version = typeof parsed === "string" ? parsed : String(result.out || "").trim().replace(/^"|"$/g, "");
|
|
819
|
-
if (version) {
|
|
820
|
-
PLUGIN_VERSION = version;
|
|
821
|
-
info(tr(
|
|
822
|
-
`Resolved default plugin version to npm latest: ${PLUGIN_VERSION}`,
|
|
823
|
-
`Resolved default plugin version to npm latest: ${PLUGIN_VERSION}`,
|
|
824
|
-
));
|
|
825
|
-
return true;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
warn(tr(
|
|
830
|
-
`Could not resolve npm latest for ${pluginNpmPackage}${result.err ? `: ${result.err}` : ""}`,
|
|
831
|
-
`Could not resolve npm latest for ${pluginNpmPackage}${result.err ? `: ${result.err}` : ""}`,
|
|
832
|
-
));
|
|
833
|
-
return false;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
async function ensureNpmPackageExtracted() {
|
|
837
|
-
if (npmPackageExtractDir && existsSync(npmPackageExtractDir)) {
|
|
838
|
-
return npmPackageExtractDir;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
npmPackageTempDir = await mkdtemp(join(tmpdir(), "ov-plugin-npm-"));
|
|
842
|
-
info(tr(
|
|
843
|
-
`Downloading plugin package from npm: ${npmPackageSpec()}`,
|
|
844
|
-
`Downloading plugin package from npm: ${npmPackageSpec()}`,
|
|
845
|
-
));
|
|
846
|
-
|
|
847
|
-
const packResult = await runCapture("npm", [
|
|
848
|
-
"pack",
|
|
849
|
-
npmPackageSpec(),
|
|
850
|
-
"--pack-destination",
|
|
851
|
-
npmPackageTempDir,
|
|
852
|
-
"--json",
|
|
853
|
-
"--registry",
|
|
854
|
-
NPM_REGISTRY,
|
|
855
|
-
], { shell: IS_WIN });
|
|
856
|
-
|
|
857
|
-
if (packResult.code !== 0) {
|
|
858
|
-
throw new Error(`npm pack failed for ${npmPackageSpec()}${packResult.err ? `: ${packResult.err}` : ""}`);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
const parsed = parseNpmJsonOutput(packResult.out);
|
|
862
|
-
const first = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
863
|
-
const filename = first?.filename || readdirSync(npmPackageTempDir).find((name) => name.endsWith(".tgz"));
|
|
864
|
-
if (!filename) {
|
|
865
|
-
throw new Error(`npm pack did not produce a tarball for ${npmPackageSpec()}`);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
const tarballPath = join(npmPackageTempDir, filename);
|
|
869
|
-
const extractRoot = join(npmPackageTempDir, "extract");
|
|
870
|
-
await mkdir(extractRoot, { recursive: true });
|
|
871
|
-
await run("tar", ["-xzf", tarballPath, "-C", extractRoot], { silent: true, shell: IS_WIN });
|
|
872
|
-
|
|
873
|
-
const packageDir = join(extractRoot, "package");
|
|
874
|
-
if (!existsSync(packageDir)) {
|
|
875
|
-
throw new Error(`npm package ${npmPackageSpec()} did not contain the expected package directory`);
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
npmPackageExtractDir = packageDir;
|
|
879
|
-
return npmPackageExtractDir;
|
|
880
|
-
}
|
|
819
|
+
|
|
820
|
+
return normalized[0] || "";
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function npmPackageSpec(version = PLUGIN_VERSION) {
|
|
824
|
+
return version ? `${pluginNpmPackage}@${version}` : pluginNpmPackage;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function parseNpmJsonOutput(output) {
|
|
828
|
+
const text = String(output || "").trim();
|
|
829
|
+
if (!text) return null;
|
|
830
|
+
try {
|
|
831
|
+
return JSON.parse(text);
|
|
832
|
+
} catch {
|
|
833
|
+
const firstArray = text.indexOf("[");
|
|
834
|
+
const lastArray = text.lastIndexOf("]");
|
|
835
|
+
if (firstArray >= 0 && lastArray > firstArray) {
|
|
836
|
+
try {
|
|
837
|
+
return JSON.parse(text.slice(firstArray, lastArray + 1));
|
|
838
|
+
} catch {}
|
|
839
|
+
}
|
|
840
|
+
const firstObject = text.indexOf("{");
|
|
841
|
+
const lastObject = text.lastIndexOf("}");
|
|
842
|
+
if (firstObject >= 0 && lastObject > firstObject) {
|
|
843
|
+
try {
|
|
844
|
+
return JSON.parse(text.slice(firstObject, lastObject + 1));
|
|
845
|
+
} catch {}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
async function resolveDefaultPluginVersionFromNpm() {
|
|
852
|
+
info(tr(
|
|
853
|
+
`No plugin version specified; resolving latest npm version from ${pluginNpmPackage}...`,
|
|
854
|
+
`No plugin version specified; resolving latest npm version from ${pluginNpmPackage}...`,
|
|
855
|
+
));
|
|
856
|
+
|
|
857
|
+
const result = await runCapture("npm", [
|
|
858
|
+
"view",
|
|
859
|
+
`${pluginNpmPackage}@latest`,
|
|
860
|
+
"version",
|
|
861
|
+
"--json",
|
|
862
|
+
"--registry",
|
|
863
|
+
NPM_REGISTRY,
|
|
864
|
+
], { shell: IS_WIN });
|
|
865
|
+
|
|
866
|
+
if (result.code === 0) {
|
|
867
|
+
const parsed = parseNpmJsonOutput(result.out);
|
|
868
|
+
const version = typeof parsed === "string" ? parsed : String(result.out || "").trim().replace(/^"|"$/g, "");
|
|
869
|
+
if (version) {
|
|
870
|
+
PLUGIN_VERSION = version;
|
|
871
|
+
info(tr(
|
|
872
|
+
`Resolved default plugin version to npm latest: ${PLUGIN_VERSION}`,
|
|
873
|
+
`Resolved default plugin version to npm latest: ${PLUGIN_VERSION}`,
|
|
874
|
+
));
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
warn(tr(
|
|
880
|
+
`Could not resolve npm latest for ${pluginNpmPackage}${result.err ? `: ${result.err}` : ""}`,
|
|
881
|
+
`Could not resolve npm latest for ${pluginNpmPackage}${result.err ? `: ${result.err}` : ""}`,
|
|
882
|
+
));
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
async function ensureNpmPackageExtracted() {
|
|
887
|
+
if (npmPackageExtractDir && existsSync(npmPackageExtractDir)) {
|
|
888
|
+
return npmPackageExtractDir;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
npmPackageTempDir = await mkdtemp(join(tmpdir(), "ov-plugin-npm-"));
|
|
892
|
+
info(tr(
|
|
893
|
+
`Downloading plugin package from npm: ${npmPackageSpec()}`,
|
|
894
|
+
`Downloading plugin package from npm: ${npmPackageSpec()}`,
|
|
895
|
+
));
|
|
896
|
+
|
|
897
|
+
const packResult = await runCapture("npm", [
|
|
898
|
+
"pack",
|
|
899
|
+
npmPackageSpec(),
|
|
900
|
+
"--pack-destination",
|
|
901
|
+
npmPackageTempDir,
|
|
902
|
+
"--json",
|
|
903
|
+
"--registry",
|
|
904
|
+
NPM_REGISTRY,
|
|
905
|
+
], { shell: IS_WIN });
|
|
906
|
+
|
|
907
|
+
if (packResult.code !== 0) {
|
|
908
|
+
throw new Error(`npm pack failed for ${npmPackageSpec()}${packResult.err ? `: ${packResult.err}` : ""}`);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const parsed = parseNpmJsonOutput(packResult.out);
|
|
912
|
+
const first = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
913
|
+
const filename = first?.filename || readdirSync(npmPackageTempDir).find((name) => name.endsWith(".tgz"));
|
|
914
|
+
if (!filename) {
|
|
915
|
+
throw new Error(`npm pack did not produce a tarball for ${npmPackageSpec()}`);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const tarballPath = join(npmPackageTempDir, filename);
|
|
919
|
+
const extractRoot = join(npmPackageTempDir, "extract");
|
|
920
|
+
await mkdir(extractRoot, { recursive: true });
|
|
921
|
+
await run("tar", ["-xzf", tarballPath, "-C", extractRoot], { silent: true, shell: IS_WIN });
|
|
922
|
+
|
|
923
|
+
const packageDir = join(extractRoot, "package");
|
|
924
|
+
if (!existsSync(packageDir)) {
|
|
925
|
+
throw new Error(`npm package ${npmPackageSpec()} did not contain the expected package directory`);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
npmPackageExtractDir = packageDir;
|
|
929
|
+
return npmPackageExtractDir;
|
|
930
|
+
}
|
|
881
931
|
|
|
882
932
|
function parseGitLsRemoteTags(output) {
|
|
883
933
|
return String(output ?? "")
|
|
@@ -889,23 +939,23 @@ function parseGitLsRemoteTags(output) {
|
|
|
889
939
|
.filter(Boolean);
|
|
890
940
|
}
|
|
891
941
|
|
|
892
|
-
async function resolveDefaultPluginVersion() {
|
|
893
|
-
if (PLUGIN_VERSION) {
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
if (pluginSource === "npm") {
|
|
898
|
-
if (await resolveDefaultPluginVersionFromNpm()) {
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
warn(tr(
|
|
902
|
-
"Falling back to GitHub tag resolution.",
|
|
903
|
-
"Falling back to GitHub tag resolution.",
|
|
904
|
-
));
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
info(tr(
|
|
908
|
-
`No plugin version specified; resolving latest tag from ${REPO}...`,
|
|
942
|
+
async function resolveDefaultPluginVersion() {
|
|
943
|
+
if (PLUGIN_VERSION) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
if (pluginSource === "npm") {
|
|
948
|
+
if (await resolveDefaultPluginVersionFromNpm()) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
warn(tr(
|
|
952
|
+
"Falling back to GitHub tag resolution.",
|
|
953
|
+
"Falling back to GitHub tag resolution.",
|
|
954
|
+
));
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
info(tr(
|
|
958
|
+
`No plugin version specified; resolving latest tag from ${REPO}...`,
|
|
909
959
|
`未指定插件版本,正在解析 ${REPO} 的最新 tag...`,
|
|
910
960
|
));
|
|
911
961
|
|
|
@@ -977,117 +1027,117 @@ async function resolveDefaultPluginVersion() {
|
|
|
977
1027
|
if (failures.length > 0) {
|
|
978
1028
|
warn(failures.join(" | "));
|
|
979
1029
|
}
|
|
980
|
-
process.exit(1);
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
function applyManifestConfig(manifestData) {
|
|
984
|
-
resolvedPluginId = manifestData.plugin?.id || "";
|
|
985
|
-
resolvedPluginKind = manifestData.plugin?.kind || "";
|
|
986
|
-
resolvedPluginSlot = manifestData.plugin?.slot || "";
|
|
987
|
-
resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
|
|
988
|
-
resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
|
|
989
|
-
resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
|
|
990
|
-
const npmConfig = manifestData.npm && typeof manifestData.npm === "object"
|
|
991
|
-
? manifestData.npm
|
|
992
|
-
: {};
|
|
993
|
-
resolvedNpmOmitDev = npmConfig.omitDev !== false;
|
|
994
|
-
resolvedNpmBuild = npmConfig.build === true || npmConfig.buildFromSource === true;
|
|
995
|
-
resolvedNpmBuildMinOpenclawVersion =
|
|
996
|
-
typeof npmConfig.buildMinOpenclawVersion === "string" && npmConfig.buildMinOpenclawVersion.trim()
|
|
997
|
-
? npmConfig.buildMinOpenclawVersion.trim()
|
|
998
|
-
: DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
999
|
-
resolvedNpmBuildScript = typeof npmConfig.buildScript === "string" && npmConfig.buildScript.trim()
|
|
1000
|
-
? npmConfig.buildScript.trim()
|
|
1001
|
-
: "build";
|
|
1002
|
-
resolvedNpmPruneAfterBuild = npmConfig.pruneAfterBuild !== false;
|
|
1003
|
-
resolvedFilesRequired = manifestData.files?.required || [];
|
|
1004
|
-
resolvedFilesOptional = manifestData.files?.optional || [];
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
function hasPrebuiltRuntimeOutputs(packageDir) {
|
|
1008
|
-
return existsSync(join(packageDir, "dist", "index.js"));
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
async function resolvePluginConfigFromNpm() {
|
|
1012
|
-
info(tr(
|
|
1013
|
-
`Resolving plugin configuration from npm package: ${npmPackageSpec()}`,
|
|
1014
|
-
`Resolving plugin configuration from npm package: ${npmPackageSpec()}`,
|
|
1015
|
-
));
|
|
1016
|
-
|
|
1017
|
-
const packageDir = await ensureNpmPackageExtracted();
|
|
1018
|
-
const manifestPath = join(packageDir, "install-manifest.json");
|
|
1019
|
-
const packageJsonPath = join(packageDir, "package.json");
|
|
1020
|
-
let manifestData = null;
|
|
1021
|
-
let packageJson = null;
|
|
1022
|
-
|
|
1023
|
-
if (existsSync(packageJsonPath)) {
|
|
1024
|
-
try {
|
|
1025
|
-
packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
1026
|
-
resolvedPluginReleaseId = packageJson.version || "";
|
|
1027
|
-
} catch {}
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
if (existsSync(manifestPath)) {
|
|
1031
|
-
try {
|
|
1032
|
-
manifestData = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
1033
|
-
info(tr("Found manifest in npm package", "Found manifest in npm package"));
|
|
1034
|
-
} catch {}
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
resolvedPluginDir = ".";
|
|
1038
|
-
if (manifestData) {
|
|
1039
|
-
applyManifestConfig(manifestData);
|
|
1040
|
-
} else {
|
|
1041
|
-
const pkgName = packageJson?.name || "";
|
|
1042
|
-
const fallback = pkgName && pkgName !== DEFAULT_PLUGIN_NPM_PACKAGE ? FALLBACK_LEGACY : FALLBACK_CURRENT;
|
|
1043
|
-
resolvedPluginId = fallback.id;
|
|
1044
|
-
resolvedPluginKind = fallback.kind;
|
|
1045
|
-
resolvedPluginSlot = fallback.slot;
|
|
1046
|
-
resolvedFilesRequired = fallback.required;
|
|
1047
|
-
resolvedFilesOptional = fallback.optional;
|
|
1048
|
-
resolvedNpmOmitDev = true;
|
|
1049
|
-
resolvedNpmBuild = false;
|
|
1050
|
-
resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
1051
|
-
resolvedNpmBuildScript = "build";
|
|
1052
|
-
resolvedNpmPruneAfterBuild = true;
|
|
1053
|
-
resolvedMinOpenclawVersion = (packageJson?.engines?.openclaw || "").replace(/^>=?\s*/, "").trim()
|
|
1054
|
-
|| fallback.minOpenclawVersion
|
|
1055
|
-
|| "2026.3.7";
|
|
1056
|
-
resolvedMinOpenvikingVersion = "";
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
if (hasPrebuiltRuntimeOutputs(packageDir)) {
|
|
1060
|
-
resolvedNpmBuild = false;
|
|
1061
|
-
info(tr(
|
|
1062
|
-
"npm package contains prebuilt runtime output; skipping source build.",
|
|
1063
|
-
"npm package contains prebuilt runtime output; skipping source build.",
|
|
1064
|
-
));
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId || "openviking");
|
|
1068
|
-
info(tr(`Plugin: ${resolvedPluginId} (${resolvedPluginKind})`, `Plugin: ${resolvedPluginId} (${resolvedPluginKind})`));
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
// Resolve plugin configuration from manifest or fallback
|
|
1072
|
-
async function resolvePluginConfig() {
|
|
1073
|
-
if (pluginSource === "npm") {
|
|
1074
|
-
try {
|
|
1075
|
-
await resolvePluginConfigFromNpm();
|
|
1076
|
-
return;
|
|
1077
|
-
} catch (error) {
|
|
1078
|
-
warn(tr(
|
|
1079
|
-
`npm plugin resolution failed: ${error?.message || error}`,
|
|
1080
|
-
`npm plugin resolution failed: ${error?.message || error}`,
|
|
1081
|
-
));
|
|
1082
|
-
warn(tr(
|
|
1083
|
-
"Falling back to GitHub plugin download.",
|
|
1084
|
-
"Falling back to GitHub plugin download.",
|
|
1085
|
-
));
|
|
1086
|
-
pluginSource = "github";
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
1030
|
+
process.exit(1);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function applyManifestConfig(manifestData) {
|
|
1034
|
+
resolvedPluginId = manifestData.plugin?.id || "";
|
|
1035
|
+
resolvedPluginKind = manifestData.plugin?.kind || "";
|
|
1036
|
+
resolvedPluginSlot = manifestData.plugin?.slot || "";
|
|
1037
|
+
resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
|
|
1038
|
+
resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
|
|
1039
|
+
resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
|
|
1040
|
+
const npmConfig = manifestData.npm && typeof manifestData.npm === "object"
|
|
1041
|
+
? manifestData.npm
|
|
1042
|
+
: {};
|
|
1043
|
+
resolvedNpmOmitDev = npmConfig.omitDev !== false;
|
|
1044
|
+
resolvedNpmBuild = npmConfig.build === true || npmConfig.buildFromSource === true;
|
|
1045
|
+
resolvedNpmBuildMinOpenclawVersion =
|
|
1046
|
+
typeof npmConfig.buildMinOpenclawVersion === "string" && npmConfig.buildMinOpenclawVersion.trim()
|
|
1047
|
+
? npmConfig.buildMinOpenclawVersion.trim()
|
|
1048
|
+
: DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
1049
|
+
resolvedNpmBuildScript = typeof npmConfig.buildScript === "string" && npmConfig.buildScript.trim()
|
|
1050
|
+
? npmConfig.buildScript.trim()
|
|
1051
|
+
: "build";
|
|
1052
|
+
resolvedNpmPruneAfterBuild = npmConfig.pruneAfterBuild !== false;
|
|
1053
|
+
resolvedFilesRequired = manifestData.files?.required || [];
|
|
1054
|
+
resolvedFilesOptional = manifestData.files?.optional || [];
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
function hasPrebuiltRuntimeOutputs(packageDir) {
|
|
1058
|
+
return existsSync(join(packageDir, "dist", "index.js"));
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
async function resolvePluginConfigFromNpm() {
|
|
1062
|
+
info(tr(
|
|
1063
|
+
`Resolving plugin configuration from npm package: ${npmPackageSpec()}`,
|
|
1064
|
+
`Resolving plugin configuration from npm package: ${npmPackageSpec()}`,
|
|
1065
|
+
));
|
|
1066
|
+
|
|
1067
|
+
const packageDir = await ensureNpmPackageExtracted();
|
|
1068
|
+
const manifestPath = join(packageDir, "install-manifest.json");
|
|
1069
|
+
const packageJsonPath = join(packageDir, "package.json");
|
|
1070
|
+
let manifestData = null;
|
|
1071
|
+
let packageJson = null;
|
|
1072
|
+
|
|
1073
|
+
if (existsSync(packageJsonPath)) {
|
|
1074
|
+
try {
|
|
1075
|
+
packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
1076
|
+
resolvedPluginReleaseId = packageJson.version || "";
|
|
1077
|
+
} catch {}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (existsSync(manifestPath)) {
|
|
1081
|
+
try {
|
|
1082
|
+
manifestData = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
1083
|
+
info(tr("Found manifest in npm package", "Found manifest in npm package"));
|
|
1084
|
+
} catch {}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
resolvedPluginDir = ".";
|
|
1088
|
+
if (manifestData) {
|
|
1089
|
+
applyManifestConfig(manifestData);
|
|
1090
|
+
} else {
|
|
1091
|
+
const pkgName = packageJson?.name || "";
|
|
1092
|
+
const fallback = pkgName && pkgName !== DEFAULT_PLUGIN_NPM_PACKAGE ? FALLBACK_LEGACY : FALLBACK_CURRENT;
|
|
1093
|
+
resolvedPluginId = fallback.id;
|
|
1094
|
+
resolvedPluginKind = fallback.kind;
|
|
1095
|
+
resolvedPluginSlot = fallback.slot;
|
|
1096
|
+
resolvedFilesRequired = fallback.required;
|
|
1097
|
+
resolvedFilesOptional = fallback.optional;
|
|
1098
|
+
resolvedNpmOmitDev = true;
|
|
1099
|
+
resolvedNpmBuild = false;
|
|
1100
|
+
resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
1101
|
+
resolvedNpmBuildScript = "build";
|
|
1102
|
+
resolvedNpmPruneAfterBuild = true;
|
|
1103
|
+
resolvedMinOpenclawVersion = (packageJson?.engines?.openclaw || "").replace(/^>=?\s*/, "").trim()
|
|
1104
|
+
|| fallback.minOpenclawVersion
|
|
1105
|
+
|| "2026.3.7";
|
|
1106
|
+
resolvedMinOpenvikingVersion = "";
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (hasPrebuiltRuntimeOutputs(packageDir)) {
|
|
1110
|
+
resolvedNpmBuild = false;
|
|
1111
|
+
info(tr(
|
|
1112
|
+
"npm package contains prebuilt runtime output; skipping source build.",
|
|
1113
|
+
"npm package contains prebuilt runtime output; skipping source build.",
|
|
1114
|
+
));
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId || "openviking");
|
|
1118
|
+
info(tr(`Plugin: ${resolvedPluginId} (${resolvedPluginKind})`, `Plugin: ${resolvedPluginId} (${resolvedPluginKind})`));
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Resolve plugin configuration from manifest or fallback
|
|
1122
|
+
async function resolvePluginConfig() {
|
|
1123
|
+
if (pluginSource === "npm") {
|
|
1124
|
+
try {
|
|
1125
|
+
await resolvePluginConfigFromNpm();
|
|
1126
|
+
return;
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
warn(tr(
|
|
1129
|
+
`npm plugin resolution failed: ${error?.message || error}`,
|
|
1130
|
+
`npm plugin resolution failed: ${error?.message || error}`,
|
|
1131
|
+
));
|
|
1132
|
+
warn(tr(
|
|
1133
|
+
"Falling back to GitHub plugin download.",
|
|
1134
|
+
"Falling back to GitHub plugin download.",
|
|
1135
|
+
));
|
|
1136
|
+
pluginSource = "github";
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
1091
1141
|
|
|
1092
1142
|
info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
|
|
1093
1143
|
|
|
@@ -1164,7 +1214,8 @@ async function resolvePluginConfig() {
|
|
|
1164
1214
|
const pkg = JSON.parse(pkgJson);
|
|
1165
1215
|
const pkgName = pkg.name || "";
|
|
1166
1216
|
resolvedPluginReleaseId = pkg.version || "";
|
|
1167
|
-
|
|
1217
|
+
const currentPackageNames = new Set(["@openviking/openclaw-plugin", "@openclaw/openviking"]);
|
|
1218
|
+
if (pkgName && !currentPackageNames.has(pkgName)) {
|
|
1168
1219
|
fallbackKey = "legacy";
|
|
1169
1220
|
info(tr(`Detected legacy plugin by package name: ${pkgName}`, `通过 package.json 名称检测到旧版插件: ${pkgName}`));
|
|
1170
1221
|
} else if (pkgName) {
|
|
@@ -1399,18 +1450,22 @@ function extractRuntimeConfigFromPluginEntry(entryConfig) {
|
|
|
1399
1450
|
if (typeof entryConfig.apiKey === "string" && entryConfig.apiKey.trim()) {
|
|
1400
1451
|
runtime.apiKey = entryConfig.apiKey;
|
|
1401
1452
|
}
|
|
1402
|
-
const
|
|
1403
|
-
if (
|
|
1404
|
-
runtime.
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1453
|
+
const role = normalizePeerRole(entryConfig.peer_role);
|
|
1454
|
+
if (role) {
|
|
1455
|
+
runtime.peer_role = role;
|
|
1456
|
+
}
|
|
1457
|
+
const prefix = entryConfig.peer_prefix;
|
|
1458
|
+
if (typeof prefix === "string" && prefix.trim()) {
|
|
1459
|
+
runtime.peer_prefix = prefix.trim();
|
|
1460
|
+
}
|
|
1461
|
+
if (typeof entryConfig.accountId === "string" && entryConfig.accountId.trim()) {
|
|
1462
|
+
runtime.accountId = entryConfig.accountId.trim();
|
|
1463
|
+
}
|
|
1464
|
+
if (typeof entryConfig.userId === "string" && entryConfig.userId.trim()) {
|
|
1465
|
+
runtime.userId = entryConfig.userId.trim();
|
|
1466
|
+
}
|
|
1467
|
+
return runtime;
|
|
1468
|
+
}
|
|
1414
1469
|
|
|
1415
1470
|
async function backupOpenClawConfig(configPath) {
|
|
1416
1471
|
await mkdir(getUpgradeAuditDir(), { recursive: true });
|
|
@@ -1700,12 +1755,13 @@ async function prepareStrongPluginUpgrade() {
|
|
|
1700
1755
|
`检测到已安装 OpenViking 插件状态: ${installedState.generation}`,
|
|
1701
1756
|
),
|
|
1702
1757
|
);
|
|
1703
|
-
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1704
|
-
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1758
|
+
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1759
|
+
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1760
|
+
remotePeerRole = upgradeRuntimeConfig.peer_role || remotePeerRole || "assistant";
|
|
1761
|
+
remotePeerPrefix = upgradeRuntimeConfig.peer_prefix || "";
|
|
1762
|
+
remoteAccountId = upgradeRuntimeConfig.accountId || "";
|
|
1763
|
+
remoteUserId = upgradeRuntimeConfig.userId || "";
|
|
1764
|
+
info(tr(`Upgrade runtime mode: ${selectedMode} (remote OpenViking server)`, `升级运行模式: ${selectedMode}(远程 OpenViking 服务)`));
|
|
1709
1765
|
|
|
1710
1766
|
info(tr(`Upgrade path: ${fromVersion} -> ${toVersion}`, `升级路径: ${fromVersion} -> ${toVersion}`));
|
|
1711
1767
|
|
|
@@ -1803,6 +1859,96 @@ async function downloadPluginFile(destDir, fileName, url, required, index, total
|
|
|
1803
1859
|
process.exit(1);
|
|
1804
1860
|
}
|
|
1805
1861
|
|
|
1862
|
+
function githubContentsUrl(pluginDir, fileName) {
|
|
1863
|
+
const path = `examples/${pluginDir}/${fileName}`.replace(/\/+$/u, "");
|
|
1864
|
+
const encodedPath = path.split("/").map((part) => encodeURIComponent(part)).join("/");
|
|
1865
|
+
return `https://api.github.com/repos/${REPO}/contents/${encodedPath}?ref=${encodeURIComponent(PLUGIN_VERSION)}`;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
async function fetchGitHubDirectoryEntries(pluginDir, dirName, required) {
|
|
1869
|
+
const maxRetries = 3;
|
|
1870
|
+
const url = githubContentsUrl(pluginDir, dirName);
|
|
1871
|
+
let lastStatus = 0;
|
|
1872
|
+
|
|
1873
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1874
|
+
try {
|
|
1875
|
+
const response = await fetch(url, { headers: { "User-Agent": "openviking-setup-helper" } });
|
|
1876
|
+
lastStatus = response.status;
|
|
1877
|
+
if (response.ok) {
|
|
1878
|
+
const json = await response.json();
|
|
1879
|
+
if (Array.isArray(json)) return json;
|
|
1880
|
+
lastStatus = 0;
|
|
1881
|
+
} else if (!required && response.status === 404) {
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
} catch {
|
|
1885
|
+
lastStatus = 0;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
if (attempt < maxRetries) {
|
|
1889
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
if (!required) {
|
|
1894
|
+
err(tr(
|
|
1895
|
+
`Optional directory failed after ${maxRetries} retries (HTTP ${lastStatus || "network"}): ${url}`,
|
|
1896
|
+
`可选目录已重试 ${maxRetries} 次仍失败(HTTP ${lastStatus || "网络错误"}): ${url}`,
|
|
1897
|
+
));
|
|
1898
|
+
process.exit(1);
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
err(tr(
|
|
1902
|
+
`Directory download failed after ${maxRetries} retries (HTTP ${lastStatus || "network"}): ${url}`,
|
|
1903
|
+
`目录下载失败(已重试 ${maxRetries} 次,HTTP ${lastStatus || "网络错误"}): ${url}`,
|
|
1904
|
+
));
|
|
1905
|
+
process.exit(1);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
async function collectGitHubDirectoryFiles(pluginDir, dirName, required) {
|
|
1909
|
+
const entries = await fetchGitHubDirectoryEntries(pluginDir, dirName, required);
|
|
1910
|
+
if (!entries) return [];
|
|
1911
|
+
|
|
1912
|
+
const prefix = `examples/${pluginDir}/`;
|
|
1913
|
+
const files = [];
|
|
1914
|
+
for (const entry of entries) {
|
|
1915
|
+
if (entry?.type === "file" && entry.download_url) {
|
|
1916
|
+
const relativePath = String(entry.path || "").startsWith(prefix)
|
|
1917
|
+
? String(entry.path).slice(prefix.length)
|
|
1918
|
+
: `${dirName}${entry.name}`;
|
|
1919
|
+
files.push({ fileName: relativePath, url: entry.download_url });
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
if (entry?.type === "dir" && entry.path) {
|
|
1924
|
+
const relativeDir = String(entry.path).startsWith(prefix)
|
|
1925
|
+
? String(entry.path).slice(prefix.length)
|
|
1926
|
+
: `${dirName}${entry.name}/`;
|
|
1927
|
+
files.push(...await collectGitHubDirectoryFiles(pluginDir, `${relativeDir}/`, required));
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
return files;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
async function downloadPluginDirectory(destDir, pluginDir, dirName, required, index, total) {
|
|
1935
|
+
process.stdout.write(` [${index}/${total}] ${dirName} `);
|
|
1936
|
+
const files = await collectGitHubDirectoryFiles(pluginDir, dirName, required);
|
|
1937
|
+
if (files.length === 0) {
|
|
1938
|
+
console.log(required ? " empty" : tr(" skip", " 跳过"));
|
|
1939
|
+
if (!required) return;
|
|
1940
|
+
err(tr(`Required directory is empty or unavailable: ${dirName}`, `必需目录为空或不可用: ${dirName}`));
|
|
1941
|
+
process.exit(1);
|
|
1942
|
+
}
|
|
1943
|
+
console.log(` OK (${files.length} files)`);
|
|
1944
|
+
|
|
1945
|
+
let fileIndex = 0;
|
|
1946
|
+
for (const file of files) {
|
|
1947
|
+
fileIndex++;
|
|
1948
|
+
await downloadPluginFile(destDir, file.fileName, file.url, required, `${index}.${fileIndex}`, total);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1806
1952
|
function runtimeOutputCandidatesForEntry(entry) {
|
|
1807
1953
|
const normalized = String(entry || "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1808
1954
|
if (!normalized.endsWith(".ts")) {
|
|
@@ -1859,7 +2005,7 @@ async function assertBuiltRuntimeOutputs(destDir) {
|
|
|
1859
2005
|
process.exit(1);
|
|
1860
2006
|
}
|
|
1861
2007
|
|
|
1862
|
-
async function installPluginNpmDependencies(destDir) {
|
|
2008
|
+
async function installPluginNpmDependencies(destDir) {
|
|
1863
2009
|
if (!resolvedNpmBuild) {
|
|
1864
2010
|
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
1865
2011
|
const npmArgs = resolvedNpmOmitDev
|
|
@@ -1897,40 +2043,40 @@ async function installPluginNpmDependencies(destDir) {
|
|
|
1897
2043
|
"--no-audit",
|
|
1898
2044
|
"--no-fund",
|
|
1899
2045
|
], { cwd: destDir, silent: false });
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
async function copyNpmPackageToDest(destDir) {
|
|
1904
|
-
const packageDir = await ensureNpmPackageExtracted();
|
|
1905
|
-
await mkdir(destDir, { recursive: true });
|
|
1906
|
-
const entries = readdirSync(packageDir, { withFileTypes: true });
|
|
1907
|
-
for (const entry of entries) {
|
|
1908
|
-
await cp(join(packageDir, entry.name), join(destDir, entry.name), { recursive: true, force: true });
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
async function cleanupNpmPackageTemp() {
|
|
1913
|
-
if (!npmPackageTempDir) return;
|
|
1914
|
-
await rm(npmPackageTempDir, { recursive: true, force: true });
|
|
1915
|
-
npmPackageTempDir = "";
|
|
1916
|
-
npmPackageExtractDir = "";
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
|
-
async function downloadPlugin(destDir) {
|
|
1920
|
-
if (pluginSource === "npm") {
|
|
1921
|
-
await mkdir(destDir, { recursive: true });
|
|
1922
|
-
info(tr(
|
|
1923
|
-
`Installing plugin from npm package ${npmPackageSpec()}...`,
|
|
1924
|
-
`Installing plugin from npm package ${npmPackageSpec()}...`,
|
|
1925
|
-
));
|
|
1926
|
-
await copyNpmPackageToDest(destDir);
|
|
1927
|
-
await installPluginNpmDependencies(destDir);
|
|
1928
|
-
info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `Plugin deployed: ${PLUGIN_DEST}`));
|
|
1929
|
-
return;
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
|
-
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
1933
|
-
const pluginDir = resolvedPluginDir;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
async function copyNpmPackageToDest(destDir) {
|
|
2050
|
+
const packageDir = await ensureNpmPackageExtracted();
|
|
2051
|
+
await mkdir(destDir, { recursive: true });
|
|
2052
|
+
const entries = readdirSync(packageDir, { withFileTypes: true });
|
|
2053
|
+
for (const entry of entries) {
|
|
2054
|
+
await cp(join(packageDir, entry.name), join(destDir, entry.name), { recursive: true, force: true });
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
async function cleanupNpmPackageTemp() {
|
|
2059
|
+
if (!npmPackageTempDir) return;
|
|
2060
|
+
await rm(npmPackageTempDir, { recursive: true, force: true });
|
|
2061
|
+
npmPackageTempDir = "";
|
|
2062
|
+
npmPackageExtractDir = "";
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
async function downloadPlugin(destDir) {
|
|
2066
|
+
if (pluginSource === "npm") {
|
|
2067
|
+
await mkdir(destDir, { recursive: true });
|
|
2068
|
+
info(tr(
|
|
2069
|
+
`Installing plugin from npm package ${npmPackageSpec()}...`,
|
|
2070
|
+
`Installing plugin from npm package ${npmPackageSpec()}...`,
|
|
2071
|
+
));
|
|
2072
|
+
await copyNpmPackageToDest(destDir);
|
|
2073
|
+
await installPluginNpmDependencies(destDir);
|
|
2074
|
+
info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `Plugin deployed: ${PLUGIN_DEST}`));
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
2079
|
+
const pluginDir = resolvedPluginDir;
|
|
1934
2080
|
const total = resolvedFilesRequired.length + resolvedFilesOptional.length;
|
|
1935
2081
|
|
|
1936
2082
|
await mkdir(destDir, { recursive: true });
|
|
@@ -1942,6 +2088,10 @@ async function downloadPlugin(destDir) {
|
|
|
1942
2088
|
for (const name of resolvedFilesRequired) {
|
|
1943
2089
|
if (!name) continue;
|
|
1944
2090
|
i++;
|
|
2091
|
+
if (name.endsWith("/")) {
|
|
2092
|
+
await downloadPluginDirectory(destDir, pluginDir, name, true, i, total);
|
|
2093
|
+
continue;
|
|
2094
|
+
}
|
|
1945
2095
|
const url = `${ghRaw}/examples/${pluginDir}/${name}`;
|
|
1946
2096
|
await downloadPluginFile(destDir, name, url, true, i, total);
|
|
1947
2097
|
}
|
|
@@ -1950,6 +2100,10 @@ async function downloadPlugin(destDir) {
|
|
|
1950
2100
|
for (const name of resolvedFilesOptional) {
|
|
1951
2101
|
if (!name) continue;
|
|
1952
2102
|
i++;
|
|
2103
|
+
if (name.endsWith("/")) {
|
|
2104
|
+
await downloadPluginDirectory(destDir, pluginDir, name, false, i, total);
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
1953
2107
|
const url = `${ghRaw}/examples/${pluginDir}/${name}`;
|
|
1954
2108
|
await downloadPluginFile(destDir, name, url, false, i, total);
|
|
1955
2109
|
}
|
|
@@ -1987,18 +2141,18 @@ async function finalizePluginDeployment(stagingDir) {
|
|
|
1987
2141
|
return info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
|
|
1988
2142
|
}
|
|
1989
2143
|
|
|
1990
|
-
async function deployPluginFromRemote() {
|
|
1991
|
-
const stagingDir = await createPluginStagingDir();
|
|
1992
|
-
try {
|
|
1993
|
-
await downloadPlugin(stagingDir);
|
|
1994
|
-
await finalizePluginDeployment(stagingDir);
|
|
1995
|
-
} catch (error) {
|
|
1996
|
-
await rm(stagingDir, { recursive: true, force: true });
|
|
1997
|
-
throw error;
|
|
1998
|
-
} finally {
|
|
1999
|
-
await cleanupNpmPackageTemp();
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2144
|
+
async function deployPluginFromRemote() {
|
|
2145
|
+
const stagingDir = await createPluginStagingDir();
|
|
2146
|
+
try {
|
|
2147
|
+
await downloadPlugin(stagingDir);
|
|
2148
|
+
await finalizePluginDeployment(stagingDir);
|
|
2149
|
+
} catch (error) {
|
|
2150
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
2151
|
+
throw error;
|
|
2152
|
+
} finally {
|
|
2153
|
+
await cleanupNpmPackageTemp();
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2002
2156
|
|
|
2003
2157
|
/** Same as INSTALL*.md manual cleanup: stale entries block `plugins.slots.*` validation after reinstall. */
|
|
2004
2158
|
function resolvedPluginSlotFallback() {
|
|
@@ -2272,7 +2426,8 @@ async function configureOpenClawPlugin({
|
|
|
2272
2426
|
const effectiveRuntimeConfig = runtimeConfig || {
|
|
2273
2427
|
baseUrl: remoteBaseUrl,
|
|
2274
2428
|
apiKey: remoteApiKey,
|
|
2275
|
-
|
|
2429
|
+
peer_role: remotePeerRole,
|
|
2430
|
+
peer_prefix: remotePeerPrefix,
|
|
2276
2431
|
};
|
|
2277
2432
|
|
|
2278
2433
|
let allowedPropsLegacy = null;
|
|
@@ -2287,7 +2442,7 @@ async function configureOpenClawPlugin({
|
|
|
2287
2442
|
}
|
|
2288
2443
|
} catch { /* ignore parse errors */ }
|
|
2289
2444
|
|
|
2290
|
-
const
|
|
2445
|
+
const peerVal = effectiveRuntimeConfig.peer_prefix || "";
|
|
2291
2446
|
const candidates = {
|
|
2292
2447
|
mode: "remote",
|
|
2293
2448
|
baseUrl: effectiveRuntimeConfig.baseUrl || remoteBaseUrl,
|
|
@@ -2295,7 +2450,7 @@ async function configureOpenClawPlugin({
|
|
|
2295
2450
|
autoRecall: true,
|
|
2296
2451
|
autoCapture: true,
|
|
2297
2452
|
apiKey: effectiveRuntimeConfig.apiKey || undefined,
|
|
2298
|
-
|
|
2453
|
+
peer_prefix: peerVal || undefined,
|
|
2299
2454
|
};
|
|
2300
2455
|
|
|
2301
2456
|
const pluginConfig = {};
|
|
@@ -2317,7 +2472,8 @@ async function configureOpenClawPlugin({
|
|
|
2317
2472
|
const effectiveRuntimeConfig = runtimeConfig || {
|
|
2318
2473
|
baseUrl: remoteBaseUrl,
|
|
2319
2474
|
apiKey: remoteApiKey,
|
|
2320
|
-
|
|
2475
|
+
peer_role: remotePeerRole,
|
|
2476
|
+
peer_prefix: remotePeerPrefix,
|
|
2321
2477
|
accountId: remoteAccountId,
|
|
2322
2478
|
userId: remoteUserId,
|
|
2323
2479
|
};
|
|
@@ -2332,82 +2488,85 @@ async function configureOpenClawPlugin({
|
|
|
2332
2488
|
}
|
|
2333
2489
|
} catch { /* ignore read errors */ }
|
|
2334
2490
|
|
|
2335
|
-
let setupResult = null;
|
|
2336
|
-
let parsed = null;
|
|
2337
|
-
const runSetupJson = async (extraArgs = []) => {
|
|
2338
|
-
const setupArgs = ["openviking", "setup"];
|
|
2339
|
-
setupArgs.push("--base-url", effectiveRuntimeConfig.baseUrl || remoteBaseUrl);
|
|
2340
|
-
setupArgs.push("--json");
|
|
2341
|
-
if (effectiveRuntimeConfig.apiKey) {
|
|
2342
|
-
setupArgs.push("--api-key", effectiveRuntimeConfig.apiKey);
|
|
2491
|
+
let setupResult = null;
|
|
2492
|
+
let parsed = null;
|
|
2493
|
+
const runSetupJson = async (extraArgs = []) => {
|
|
2494
|
+
const setupArgs = ["openviking", "setup"];
|
|
2495
|
+
setupArgs.push("--base-url", effectiveRuntimeConfig.baseUrl || remoteBaseUrl);
|
|
2496
|
+
setupArgs.push("--json");
|
|
2497
|
+
if (effectiveRuntimeConfig.apiKey) {
|
|
2498
|
+
setupArgs.push("--api-key", effectiveRuntimeConfig.apiKey);
|
|
2499
|
+
}
|
|
2500
|
+
if (effectiveRuntimeConfig.peer_role) {
|
|
2501
|
+
setupArgs.push("--peer-role", effectiveRuntimeConfig.peer_role);
|
|
2343
2502
|
}
|
|
2344
|
-
if (effectiveRuntimeConfig.
|
|
2345
|
-
setupArgs.push("--
|
|
2503
|
+
if (effectiveRuntimeConfig.peer_prefix) {
|
|
2504
|
+
setupArgs.push("--peer-prefix", effectiveRuntimeConfig.peer_prefix);
|
|
2346
2505
|
}
|
|
2347
2506
|
if (effectiveRuntimeConfig.accountId) {
|
|
2348
2507
|
setupArgs.push("--account-id", effectiveRuntimeConfig.accountId);
|
|
2349
2508
|
}
|
|
2350
|
-
if (effectiveRuntimeConfig.userId) {
|
|
2351
|
-
setupArgs.push("--user-id", effectiveRuntimeConfig.userId);
|
|
2352
|
-
}
|
|
2353
|
-
if (forceSlotExplicit) {
|
|
2354
|
-
setupArgs.push("--force-slot");
|
|
2355
|
-
}
|
|
2356
|
-
if (allowOfflineExplicit) {
|
|
2357
|
-
setupArgs.push("--allow-offline");
|
|
2358
|
-
}
|
|
2359
|
-
setupArgs.push(...extraArgs);
|
|
2360
|
-
|
|
2361
|
-
const result = await runCapture("openclaw", setupArgs, { env: ocEnv, shell: IS_WIN });
|
|
2362
|
-
return {
|
|
2363
|
-
result,
|
|
2364
|
-
parsed: parseJsonObjectFromOutput(`${result.out || ""}\n${result.err || ""}`),
|
|
2365
|
-
};
|
|
2366
|
-
};
|
|
2367
|
-
|
|
2368
|
-
if (setupJsonSupported) {
|
|
2369
|
-
info(tr(
|
|
2370
|
-
"Delegating configuration to: openclaw openviking setup --json",
|
|
2371
|
-
"委托配置给: openclaw openviking setup --json",
|
|
2372
|
-
));
|
|
2373
|
-
|
|
2374
|
-
({ result: setupResult, parsed } = await runSetupJson());
|
|
2375
|
-
} else {
|
|
2376
|
-
info(tr(
|
|
2377
|
-
"Installed plugin does not support setup --json, using direct config write",
|
|
2378
|
-
"已安装的插件不支持 setup --json,使用直接配置写入",
|
|
2379
|
-
));
|
|
2380
|
-
}
|
|
2381
|
-
|
|
2382
|
-
if (parsed && !parsed.success && !nonInteractive) {
|
|
2383
|
-
if (parsed.action === "slot_blocked" && !forceSlotExplicit) {
|
|
2384
|
-
const answer = await question(
|
|
2385
|
-
tr(
|
|
2386
|
-
`contextEngine slot is owned by "${parsed.slot?.previousOwner}". Replace it with OpenViking? (y/N)`,
|
|
2387
|
-
`contextEngine slot is owned by "${parsed.slot?.previousOwner}". Replace it with OpenViking? (y/N)`,
|
|
2388
|
-
),
|
|
2389
|
-
);
|
|
2390
|
-
if (isYes(answer)) {
|
|
2391
|
-
({ result: setupResult, parsed } = await runSetupJson(["--force-slot"]));
|
|
2392
|
-
}
|
|
2393
|
-
} else if (
|
|
2394
|
-
typeof parsed.error === "string" &&
|
|
2395
|
-
parsed.error.includes("Server unreachable") &&
|
|
2396
|
-
!allowOfflineExplicit
|
|
2397
|
-
) {
|
|
2398
|
-
const answer = await question(
|
|
2399
|
-
tr(
|
|
2400
|
-
"OpenViking server is unreachable. Save config offline anyway? (y/N)",
|
|
2401
|
-
"OpenViking server is unreachable. Save config offline anyway? (y/N)",
|
|
2402
|
-
),
|
|
2403
|
-
);
|
|
2404
|
-
if (isYes(answer)) {
|
|
2405
|
-
({ result: setupResult, parsed } = await runSetupJson(["--allow-offline"]));
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
|
-
if (parsed) {
|
|
2509
|
+
if (effectiveRuntimeConfig.userId) {
|
|
2510
|
+
setupArgs.push("--user-id", effectiveRuntimeConfig.userId);
|
|
2511
|
+
}
|
|
2512
|
+
if (forceSlotExplicit) {
|
|
2513
|
+
setupArgs.push("--force-slot");
|
|
2514
|
+
}
|
|
2515
|
+
if (allowOfflineExplicit) {
|
|
2516
|
+
setupArgs.push("--allow-offline");
|
|
2517
|
+
}
|
|
2518
|
+
setupArgs.push(...extraArgs);
|
|
2519
|
+
|
|
2520
|
+
const result = await runCapture("openclaw", setupArgs, { env: ocEnv, shell: IS_WIN });
|
|
2521
|
+
return {
|
|
2522
|
+
result,
|
|
2523
|
+
parsed: parseJsonObjectFromOutput(`${result.out || ""}\n${result.err || ""}`),
|
|
2524
|
+
};
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
if (setupJsonSupported) {
|
|
2528
|
+
info(tr(
|
|
2529
|
+
"Delegating configuration to: openclaw openviking setup --json",
|
|
2530
|
+
"委托配置给: openclaw openviking setup --json",
|
|
2531
|
+
));
|
|
2532
|
+
|
|
2533
|
+
({ result: setupResult, parsed } = await runSetupJson());
|
|
2534
|
+
} else {
|
|
2535
|
+
info(tr(
|
|
2536
|
+
"Installed plugin does not support setup --json, using direct config write",
|
|
2537
|
+
"已安装的插件不支持 setup --json,使用直接配置写入",
|
|
2538
|
+
));
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
if (parsed && !parsed.success && !nonInteractive) {
|
|
2542
|
+
if (parsed.action === "slot_blocked" && !forceSlotExplicit) {
|
|
2543
|
+
const answer = await question(
|
|
2544
|
+
tr(
|
|
2545
|
+
`contextEngine slot is owned by "${parsed.slot?.previousOwner}". Replace it with OpenViking? (y/N)`,
|
|
2546
|
+
`contextEngine slot is owned by "${parsed.slot?.previousOwner}". Replace it with OpenViking? (y/N)`,
|
|
2547
|
+
),
|
|
2548
|
+
);
|
|
2549
|
+
if (isYes(answer)) {
|
|
2550
|
+
({ result: setupResult, parsed } = await runSetupJson(["--force-slot"]));
|
|
2551
|
+
}
|
|
2552
|
+
} else if (
|
|
2553
|
+
typeof parsed.error === "string" &&
|
|
2554
|
+
parsed.error.includes("Server unreachable") &&
|
|
2555
|
+
!allowOfflineExplicit
|
|
2556
|
+
) {
|
|
2557
|
+
const answer = await question(
|
|
2558
|
+
tr(
|
|
2559
|
+
"OpenViking server is unreachable. Save config offline anyway? (y/N)",
|
|
2560
|
+
"OpenViking server is unreachable. Save config offline anyway? (y/N)",
|
|
2561
|
+
),
|
|
2562
|
+
);
|
|
2563
|
+
if (isYes(answer)) {
|
|
2564
|
+
({ result: setupResult, parsed } = await runSetupJson(["--allow-offline"]));
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
if (parsed) {
|
|
2411
2570
|
if (parsed.success) {
|
|
2412
2571
|
info(tr("OpenClaw plugin configured via setup", "OpenClaw 插件通过 setup 配置完成"));
|
|
2413
2572
|
if (parsed.health?.ok) {
|
|
@@ -2425,37 +2584,37 @@ async function configureOpenClawPlugin({
|
|
|
2425
2584
|
if (parsed.slot?.activated) {
|
|
2426
2585
|
info(tr(`contextEngine slot activated`, `contextEngine slot 已激活`));
|
|
2427
2586
|
}
|
|
2428
|
-
} else {
|
|
2429
|
-
// Setup returned success: false
|
|
2430
|
-
const setupError = parsed.error || parsed.action || "unknown error";
|
|
2431
|
-
if (parsed.action === "slot_blocked") {
|
|
2432
|
-
warn(tr(
|
|
2433
|
-
`Config saved but contextEngine slot is owned by "${parsed.slot?.previousOwner}". Use --force-slot to override.`,
|
|
2434
|
-
`配置已保存,但 contextEngine slot 被 "${parsed.slot?.previousOwner}" 占用。使用 --force-slot 覆盖。`,
|
|
2435
|
-
));
|
|
2436
|
-
} else {
|
|
2437
|
-
err(tr(
|
|
2438
|
-
`Setup failed: ${setupError}`,
|
|
2439
|
-
`配置失败: ${setupError}`,
|
|
2440
|
-
));
|
|
2441
|
-
}
|
|
2442
|
-
return {
|
|
2443
|
-
runtimeConfigOk: false,
|
|
2444
|
-
error: setupError,
|
|
2445
|
-
};
|
|
2446
|
-
}
|
|
2447
|
-
} else if (setupJsonSupported) {
|
|
2448
|
-
const setupError = setupResult
|
|
2449
|
-
? `openclaw openviking setup did not return JSON (exit code ${setupResult.code})`
|
|
2450
|
-
: "openclaw openviking setup did not run";
|
|
2451
|
-
err(tr(`Setup failed: ${setupError}`, `配置失败: ${setupError}`));
|
|
2452
|
-
return {
|
|
2453
|
-
runtimeConfigOk: false,
|
|
2454
|
-
error: setupError,
|
|
2455
|
-
};
|
|
2456
|
-
}
|
|
2457
|
-
|
|
2458
|
-
if (!setupJsonSupported) {
|
|
2587
|
+
} else {
|
|
2588
|
+
// Setup returned success: false
|
|
2589
|
+
const setupError = parsed.error || parsed.action || "unknown error";
|
|
2590
|
+
if (parsed.action === "slot_blocked") {
|
|
2591
|
+
warn(tr(
|
|
2592
|
+
`Config saved but contextEngine slot is owned by "${parsed.slot?.previousOwner}". Use --force-slot to override.`,
|
|
2593
|
+
`配置已保存,但 contextEngine slot 被 "${parsed.slot?.previousOwner}" 占用。使用 --force-slot 覆盖。`,
|
|
2594
|
+
));
|
|
2595
|
+
} else {
|
|
2596
|
+
err(tr(
|
|
2597
|
+
`Setup failed: ${setupError}`,
|
|
2598
|
+
`配置失败: ${setupError}`,
|
|
2599
|
+
));
|
|
2600
|
+
}
|
|
2601
|
+
return {
|
|
2602
|
+
runtimeConfigOk: false,
|
|
2603
|
+
error: setupError,
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
} else if (setupJsonSupported) {
|
|
2607
|
+
const setupError = setupResult
|
|
2608
|
+
? `openclaw openviking setup did not return JSON (exit code ${setupResult.code})`
|
|
2609
|
+
: "openclaw openviking setup did not run";
|
|
2610
|
+
err(tr(`Setup failed: ${setupError}`, `配置失败: ${setupError}`));
|
|
2611
|
+
return {
|
|
2612
|
+
runtimeConfigOk: false,
|
|
2613
|
+
error: setupError,
|
|
2614
|
+
};
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
if (!setupJsonSupported) {
|
|
2459
2618
|
// Direct write: only used when the installed plugin doesn't support `setup --json` (old version).
|
|
2460
2619
|
// Read the deployed configSchema to determine which fields are allowed, avoiding
|
|
2461
2620
|
// "additionalProperties" validation failures when writing new fields to old schemas.
|
|
@@ -2471,8 +2630,9 @@ async function configureOpenClawPlugin({
|
|
|
2471
2630
|
}
|
|
2472
2631
|
} catch { /* ignore parse errors, write all fields */ }
|
|
2473
2632
|
|
|
2474
|
-
const
|
|
2475
|
-
const
|
|
2633
|
+
const peerRole = normalizePeerRole(effectiveRuntimeConfig.peer_role) || "assistant";
|
|
2634
|
+
const peerVal = effectiveRuntimeConfig.peer_prefix || "";
|
|
2635
|
+
const usePeerFields = !allowedProps || allowedProps.has("peer_role") || allowedProps.has("peer_prefix");
|
|
2476
2636
|
const candidates = {
|
|
2477
2637
|
mode: "remote",
|
|
2478
2638
|
baseUrl: effectiveRuntimeConfig.baseUrl || remoteBaseUrl,
|
|
@@ -2480,10 +2640,9 @@ async function configureOpenClawPlugin({
|
|
|
2480
2640
|
accountId: effectiveRuntimeConfig.accountId || undefined,
|
|
2481
2641
|
userId: effectiveRuntimeConfig.userId || undefined,
|
|
2482
2642
|
};
|
|
2483
|
-
if (
|
|
2484
|
-
candidates.
|
|
2485
|
-
|
|
2486
|
-
candidates.agentId = agentVal;
|
|
2643
|
+
if (usePeerFields) {
|
|
2644
|
+
candidates.peer_role = peerRole;
|
|
2645
|
+
if (peerVal) candidates.peer_prefix = peerVal;
|
|
2487
2646
|
}
|
|
2488
2647
|
|
|
2489
2648
|
const pluginConfig = {};
|
|
@@ -2592,7 +2751,7 @@ async function performUninstall() {
|
|
|
2592
2751
|
tr("Confirm uninstall? (y/N)", "确认卸载?(y/N)"),
|
|
2593
2752
|
"N",
|
|
2594
2753
|
);
|
|
2595
|
-
if (!isYes(answer)) {
|
|
2754
|
+
if (!isYes(answer)) {
|
|
2596
2755
|
info(tr("Cancelled.", "已取消。"));
|
|
2597
2756
|
return;
|
|
2598
2757
|
}
|
|
@@ -2701,16 +2860,16 @@ async function main() {
|
|
|
2701
2860
|
await rollbackLastUpgradeOperation();
|
|
2702
2861
|
return;
|
|
2703
2862
|
}
|
|
2704
|
-
await resolveDefaultPluginVersion();
|
|
2705
|
-
validateRequestedPluginVersion();
|
|
2706
|
-
info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
|
|
2707
|
-
info(tr(`Plugin source: ${pluginSource}`, `Plugin source: ${pluginSource}`));
|
|
2708
|
-
if (pluginSource === "npm") {
|
|
2709
|
-
info(tr(`Plugin package: ${pluginNpmPackage}`, `Plugin package: ${pluginNpmPackage}`));
|
|
2710
|
-
} else {
|
|
2711
|
-
info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
|
|
2712
|
-
}
|
|
2713
|
-
info(tr(`Plugin version: ${PLUGIN_VERSION}`, `插件版本: ${PLUGIN_VERSION}`));
|
|
2863
|
+
await resolveDefaultPluginVersion();
|
|
2864
|
+
validateRequestedPluginVersion();
|
|
2865
|
+
info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
|
|
2866
|
+
info(tr(`Plugin source: ${pluginSource}`, `Plugin source: ${pluginSource}`));
|
|
2867
|
+
if (pluginSource === "npm") {
|
|
2868
|
+
info(tr(`Plugin package: ${pluginNpmPackage}`, `Plugin package: ${pluginNpmPackage}`));
|
|
2869
|
+
} else {
|
|
2870
|
+
info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
|
|
2871
|
+
}
|
|
2872
|
+
info(tr(`Plugin version: ${PLUGIN_VERSION}`, `插件版本: ${PLUGIN_VERSION}`));
|
|
2714
2873
|
|
|
2715
2874
|
if (upgradePluginOnly) {
|
|
2716
2875
|
info(tr("Mode: plugin upgrade only", "模式: 仅升级插件"));
|