openclaw-openviking-setup-helper 0.3.0 → 0.3.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.js +635 -191
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -9,29 +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=
|
|
15
|
-
*
|
|
16
|
-
* Environment variables:
|
|
17
|
-
* REPO, PLUGIN_VERSION (or BRANCH),
|
|
18
|
-
* 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, 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 {
|
|
26
|
-
import {
|
|
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";
|
|
27
28
|
|
|
28
29
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
30
|
|
|
30
|
-
let REPO = process.env.REPO || "volcengine/OpenViking";
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
let
|
|
34
|
-
let
|
|
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);
|
|
35
40
|
const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
36
41
|
const DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION = "2026.5.3";
|
|
37
42
|
const OPENCLAW_SHORT_VERSION_YEAR = 2026;
|
|
@@ -97,17 +102,21 @@ let resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
|
97
102
|
let resolvedNpmBuildScript = "build";
|
|
98
103
|
let resolvedNpmPruneAfterBuild = true;
|
|
99
104
|
let resolvedMinOpenclawVersion = "";
|
|
100
|
-
let resolvedMinOpenvikingVersion = "";
|
|
101
|
-
let resolvedPluginReleaseId = "";
|
|
102
|
-
let detectedOpenClawVersion = "";
|
|
105
|
+
let resolvedMinOpenvikingVersion = "";
|
|
106
|
+
let resolvedPluginReleaseId = "";
|
|
107
|
+
let detectedOpenClawVersion = "";
|
|
108
|
+
let npmPackageTempDir = "";
|
|
109
|
+
let npmPackageExtractDir = "";
|
|
103
110
|
|
|
104
111
|
let nonInteractive = false;
|
|
105
112
|
let langZh = false;
|
|
106
113
|
let workdirExplicit = false;
|
|
107
114
|
let upgradePluginOnly = false;
|
|
108
115
|
let rollbackLastUpgrade = false;
|
|
109
|
-
let showCurrentVersion = false;
|
|
110
|
-
let uninstallPlugin = false;
|
|
116
|
+
let showCurrentVersion = false;
|
|
117
|
+
let uninstallPlugin = false;
|
|
118
|
+
let forceSlotExplicit = false;
|
|
119
|
+
let allowOfflineExplicit = false;
|
|
111
120
|
|
|
112
121
|
const selectedMode = "remote";
|
|
113
122
|
const baseUrlFromEnv = !!process.env.OPENVIKING_BASE_URL;
|
|
@@ -147,11 +156,19 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
147
156
|
rollbackLastUpgrade = true;
|
|
148
157
|
continue;
|
|
149
158
|
}
|
|
150
|
-
if (arg === "--uninstall" || arg === "--remove") {
|
|
151
|
-
uninstallPlugin = true;
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (arg === "--
|
|
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") {
|
|
155
172
|
const workdir = argv[i + 1]?.trim();
|
|
156
173
|
if (!workdir) {
|
|
157
174
|
console.error("--workdir requires a path");
|
|
@@ -168,35 +185,81 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
168
185
|
console.error("--plugin-version requires a value");
|
|
169
186
|
process.exit(1);
|
|
170
187
|
}
|
|
171
|
-
PLUGIN_VERSION = version;
|
|
172
|
-
pluginVersionExplicit = true;
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
188
|
+
PLUGIN_VERSION = version;
|
|
189
|
+
pluginVersionExplicit = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
175
192
|
if (arg === "--plugin-version") {
|
|
176
193
|
const version = argv[i + 1]?.trim();
|
|
177
194
|
if (!version) {
|
|
178
195
|
console.error("--plugin-version requires a value");
|
|
179
196
|
process.exit(1);
|
|
180
197
|
}
|
|
181
|
-
PLUGIN_VERSION = version;
|
|
182
|
-
pluginVersionExplicit = true;
|
|
183
|
-
i += 1;
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
if (arg.startsWith("--github-repo=")) {
|
|
187
|
-
REPO = arg.slice("--github-repo=".length).trim();
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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") {
|
|
191
211
|
const repo = argv[i + 1]?.trim();
|
|
192
212
|
if (!repo) {
|
|
193
213
|
console.error("--github-repo requires a value (e.g. owner/repo)");
|
|
194
214
|
process.exit(1);
|
|
195
|
-
}
|
|
196
|
-
REPO = repo;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
+
}
|
|
200
263
|
if (arg === "--base-url") {
|
|
201
264
|
const val = argv[i + 1]?.trim();
|
|
202
265
|
if (!val) { console.error("--base-url requires a URL"); process.exit(1); }
|
|
@@ -268,10 +331,13 @@ function setOpenClawDir(dir) {
|
|
|
268
331
|
|
|
269
332
|
function printHelp() {
|
|
270
333
|
console.log("Usage: node install.js [ OPTIONS ]");
|
|
271
|
-
console.log("");
|
|
272
|
-
console.log("Options:");
|
|
273
|
-
console.log(" --
|
|
274
|
-
console.log("
|
|
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)");
|
|
275
341
|
console.log(" --workdir PATH OpenClaw config directory (default: ~/.openclaw)");
|
|
276
342
|
console.log(" --current-version Print installed plugin version and exit");
|
|
277
343
|
console.log(" --update, --upgrade-plugin");
|
|
@@ -282,9 +348,11 @@ function printHelp() {
|
|
|
282
348
|
console.log(" --base-url=URL OpenViking server URL (default: $OPENVIKING_BASE_URL or http://127.0.0.1:1933)");
|
|
283
349
|
console.log(" --api-key=KEY OpenViking API key (default: $OPENVIKING_API_KEY)");
|
|
284
350
|
console.log(" --agent-prefix=PREFIX Agent routing prefix (default: $OPENVIKING_AGENT_PREFIX)");
|
|
285
|
-
console.log(" --account-id=ID Account ID for root API key (default: $OPENVIKING_ACCOUNT_ID)");
|
|
286
|
-
console.log(" --user-id=ID User ID for root API key (default: $OPENVIKING_USER_ID)");
|
|
287
|
-
console.log(" --
|
|
351
|
+
console.log(" --account-id=ID Account ID for root API key (default: $OPENVIKING_ACCOUNT_ID)");
|
|
352
|
+
console.log(" --user-id=ID User ID for root API key (default: $OPENVIKING_USER_ID)");
|
|
353
|
+
console.log(" --force-slot Explicitly replace an existing contextEngine slot owner");
|
|
354
|
+
console.log(" --allow-offline Explicitly save config when the OpenViking server is unreachable");
|
|
355
|
+
console.log(" --zh Chinese prompts");
|
|
288
356
|
console.log(" -h, --help This help");
|
|
289
357
|
console.log("");
|
|
290
358
|
console.log("Examples:");
|
|
@@ -294,11 +362,11 @@ function printHelp() {
|
|
|
294
362
|
console.log(" # Show installed versions");
|
|
295
363
|
console.log(" node install.js --current-version");
|
|
296
364
|
console.log("");
|
|
297
|
-
console.log(" # Install a specific release version");
|
|
298
|
-
console.log(" node install.js --plugin-version=
|
|
299
|
-
console.log("");
|
|
300
|
-
console.log(" # Install from a fork repository");
|
|
301
|
-
console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
|
|
365
|
+
console.log(" # Install a specific release version");
|
|
366
|
+
console.log(" node install.js --plugin-version=2026.5.8");
|
|
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");
|
|
302
370
|
console.log("");
|
|
303
371
|
console.log(" # Install specific plugin version");
|
|
304
372
|
console.log(" node install.js --plugin-version=v0.2.8");
|
|
@@ -309,8 +377,8 @@ function printHelp() {
|
|
|
309
377
|
console.log(" # Roll back the last plugin upgrade");
|
|
310
378
|
console.log(" node install.js --rollback");
|
|
311
379
|
console.log("");
|
|
312
|
-
console.log("Env: REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
|
|
313
|
-
}
|
|
380
|
+
console.log("Env: PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
|
|
381
|
+
}
|
|
314
382
|
|
|
315
383
|
function formatCliArg(value) {
|
|
316
384
|
if (!value) {
|
|
@@ -403,18 +471,23 @@ function runCapture(cmd, args, opts = {}) {
|
|
|
403
471
|
});
|
|
404
472
|
}
|
|
405
473
|
|
|
406
|
-
function question(prompt, defaultValue = "") {
|
|
407
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
408
|
-
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
409
|
-
return new Promise((resolve) => {
|
|
410
|
-
rl.question(`${prompt}${suffix}: `, (answer) => {
|
|
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) => {
|
|
411
479
|
rl.close();
|
|
412
480
|
resolve((answer ?? defaultValue).trim() || defaultValue);
|
|
413
481
|
});
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function isYes(answer) {
|
|
486
|
+
const normalized = String(answer || "").trim().toLowerCase();
|
|
487
|
+
return normalized === "y" || normalized === "yes";
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function isValidAgentPrefixInput(value) {
|
|
418
491
|
const trimmed = String(value || "").trim();
|
|
419
492
|
return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
|
|
420
493
|
}
|
|
@@ -606,13 +679,30 @@ if (upgradePluginOnly && rollbackLastUpgrade) {
|
|
|
606
679
|
process.exit(1);
|
|
607
680
|
}
|
|
608
681
|
|
|
609
|
-
if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
|
|
610
|
-
console.error("--uninstall cannot be used with --upgrade-plugin or --rollback");
|
|
611
|
-
process.exit(1);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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() {
|
|
616
706
|
if (detectedOpenClawVersion) {
|
|
617
707
|
return detectedOpenClawVersion;
|
|
618
708
|
}
|
|
@@ -664,10 +754,10 @@ function compareSemverDesc(a, b) {
|
|
|
664
754
|
return versionGte(a, b) ? -1 : 1;
|
|
665
755
|
}
|
|
666
756
|
|
|
667
|
-
function pickLatestPluginTag(tagNames) {
|
|
668
|
-
const normalized = tagNames
|
|
669
|
-
.map((tag) => String(tag ?? "").trim())
|
|
670
|
-
.filter(Boolean);
|
|
757
|
+
function pickLatestPluginTag(tagNames) {
|
|
758
|
+
const normalized = tagNames
|
|
759
|
+
.map((tag) => String(tag ?? "").trim())
|
|
760
|
+
.filter(Boolean);
|
|
671
761
|
|
|
672
762
|
const semverTags = normalized
|
|
673
763
|
.filter((tag) => isSemverLike(tag))
|
|
@@ -676,9 +766,118 @@ function pickLatestPluginTag(tagNames) {
|
|
|
676
766
|
if (semverTags.length > 0) {
|
|
677
767
|
return semverTags[0];
|
|
678
768
|
}
|
|
679
|
-
|
|
680
|
-
return normalized[0] || "";
|
|
681
|
-
}
|
|
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
|
+
}
|
|
682
881
|
|
|
683
882
|
function parseGitLsRemoteTags(output) {
|
|
684
883
|
return String(output ?? "")
|
|
@@ -690,13 +889,23 @@ function parseGitLsRemoteTags(output) {
|
|
|
690
889
|
.filter(Boolean);
|
|
691
890
|
}
|
|
692
891
|
|
|
693
|
-
async function resolveDefaultPluginVersion() {
|
|
694
|
-
if (PLUGIN_VERSION) {
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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}...`,
|
|
700
909
|
`未指定插件版本,正在解析 ${REPO} 的最新 tag...`,
|
|
701
910
|
));
|
|
702
911
|
|
|
@@ -768,12 +977,117 @@ async function resolveDefaultPluginVersion() {
|
|
|
768
977
|
if (failures.length > 0) {
|
|
769
978
|
warn(failures.join(" | "));
|
|
770
979
|
}
|
|
771
|
-
process.exit(1);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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}`;
|
|
777
1091
|
|
|
778
1092
|
info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
|
|
779
1093
|
|
|
@@ -1086,11 +1400,17 @@ function extractRuntimeConfigFromPluginEntry(entryConfig) {
|
|
|
1086
1400
|
runtime.apiKey = entryConfig.apiKey;
|
|
1087
1401
|
}
|
|
1088
1402
|
const prefix = entryConfig.agent_prefix || entryConfig.agentId;
|
|
1089
|
-
if (typeof prefix === "string" && prefix.trim()) {
|
|
1090
|
-
runtime.agent_prefix = prefix.trim();
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1403
|
+
if (typeof prefix === "string" && prefix.trim()) {
|
|
1404
|
+
runtime.agent_prefix = prefix.trim();
|
|
1405
|
+
}
|
|
1406
|
+
if (typeof entryConfig.accountId === "string" && entryConfig.accountId.trim()) {
|
|
1407
|
+
runtime.accountId = entryConfig.accountId.trim();
|
|
1408
|
+
}
|
|
1409
|
+
if (typeof entryConfig.userId === "string" && entryConfig.userId.trim()) {
|
|
1410
|
+
runtime.userId = entryConfig.userId.trim();
|
|
1411
|
+
}
|
|
1412
|
+
return runtime;
|
|
1413
|
+
}
|
|
1094
1414
|
|
|
1095
1415
|
async function backupOpenClawConfig(configPath) {
|
|
1096
1416
|
await mkdir(getUpgradeAuditDir(), { recursive: true });
|
|
@@ -1380,10 +1700,12 @@ async function prepareStrongPluginUpgrade() {
|
|
|
1380
1700
|
`检测到已安装 OpenViking 插件状态: ${installedState.generation}`,
|
|
1381
1701
|
),
|
|
1382
1702
|
);
|
|
1383
|
-
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1384
|
-
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1385
|
-
remoteAgentPrefix = upgradeRuntimeConfig.agent_prefix || "";
|
|
1386
|
-
|
|
1703
|
+
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1704
|
+
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1705
|
+
remoteAgentPrefix = upgradeRuntimeConfig.agent_prefix || "";
|
|
1706
|
+
remoteAccountId = upgradeRuntimeConfig.accountId || "";
|
|
1707
|
+
remoteUserId = upgradeRuntimeConfig.userId || "";
|
|
1708
|
+
info(tr(`Upgrade runtime mode: ${selectedMode} (remote OpenViking server)`, `升级运行模式: ${selectedMode}(远程 OpenViking 服务)`));
|
|
1387
1709
|
|
|
1388
1710
|
info(tr(`Upgrade path: ${fromVersion} -> ${toVersion}`, `升级路径: ${fromVersion} -> ${toVersion}`));
|
|
1389
1711
|
|
|
@@ -1537,7 +1859,7 @@ async function assertBuiltRuntimeOutputs(destDir) {
|
|
|
1537
1859
|
process.exit(1);
|
|
1538
1860
|
}
|
|
1539
1861
|
|
|
1540
|
-
async function installPluginNpmDependencies(destDir) {
|
|
1862
|
+
async function installPluginNpmDependencies(destDir) {
|
|
1541
1863
|
if (!resolvedNpmBuild) {
|
|
1542
1864
|
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
1543
1865
|
const npmArgs = resolvedNpmOmitDev
|
|
@@ -1575,12 +1897,40 @@ async function installPluginNpmDependencies(destDir) {
|
|
|
1575
1897
|
"--no-audit",
|
|
1576
1898
|
"--no-fund",
|
|
1577
1899
|
], { cwd: destDir, silent: false });
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
async function
|
|
1582
|
-
const
|
|
1583
|
-
|
|
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;
|
|
1584
1934
|
const total = resolvedFilesRequired.length + resolvedFilesOptional.length;
|
|
1585
1935
|
|
|
1586
1936
|
await mkdir(destDir, { recursive: true });
|
|
@@ -1637,16 +1987,18 @@ async function finalizePluginDeployment(stagingDir) {
|
|
|
1637
1987
|
return info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
|
|
1638
1988
|
}
|
|
1639
1989
|
|
|
1640
|
-
async function deployPluginFromRemote() {
|
|
1641
|
-
const stagingDir = await createPluginStagingDir();
|
|
1642
|
-
try {
|
|
1643
|
-
await downloadPlugin(stagingDir);
|
|
1644
|
-
await finalizePluginDeployment(stagingDir);
|
|
1645
|
-
} catch (error) {
|
|
1646
|
-
await rm(stagingDir, { recursive: true, force: true });
|
|
1647
|
-
throw error;
|
|
1648
|
-
}
|
|
1649
|
-
|
|
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
|
+
}
|
|
1650
2002
|
|
|
1651
2003
|
/** Same as INSTALL*.md manual cleanup: stale entries block `plugins.slots.*` validation after reinstall. */
|
|
1652
2004
|
function resolvedPluginSlotFallback() {
|
|
@@ -1768,6 +2120,53 @@ async function cleanupConflictingPluginVariants() {
|
|
|
1768
2120
|
info(tr("Conflicting plugin variants cleaned up", "冲突的插件变体已清理"));
|
|
1769
2121
|
}
|
|
1770
2122
|
|
|
2123
|
+
function normalizeOpenClawLoadPath(filePath) {
|
|
2124
|
+
return String(filePath || "")
|
|
2125
|
+
.replace(/\\/g, "/")
|
|
2126
|
+
.replace(/\/+$/g, "");
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
async function ensureOpenClawPluginLoadPath() {
|
|
2130
|
+
const configPath = getOpenClawConfigPath();
|
|
2131
|
+
let cfg = {};
|
|
2132
|
+
if (existsSync(configPath)) {
|
|
2133
|
+
try {
|
|
2134
|
+
cfg = JSON.parse(await readFile(configPath, "utf8"));
|
|
2135
|
+
} catch {
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
const pluginPath = PLUGIN_DEST;
|
|
2141
|
+
const normalizedPluginPath = normalizeOpenClawLoadPath(pluginPath);
|
|
2142
|
+
const plugins = cfg.plugins && typeof cfg.plugins === "object" && !Array.isArray(cfg.plugins)
|
|
2143
|
+
? cfg.plugins
|
|
2144
|
+
: {};
|
|
2145
|
+
const load = plugins.load && typeof plugins.load === "object" && !Array.isArray(plugins.load)
|
|
2146
|
+
? plugins.load
|
|
2147
|
+
: {};
|
|
2148
|
+
const paths = Array.isArray(load.paths) ? load.paths : [];
|
|
2149
|
+
if (paths.some((item) => normalizeOpenClawLoadPath(item) === normalizedPluginPath)) {
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
const next = {
|
|
2154
|
+
...cfg,
|
|
2155
|
+
plugins: {
|
|
2156
|
+
...plugins,
|
|
2157
|
+
load: {
|
|
2158
|
+
...load,
|
|
2159
|
+
paths: [...paths, pluginPath],
|
|
2160
|
+
},
|
|
2161
|
+
},
|
|
2162
|
+
};
|
|
2163
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
2164
|
+
const tmp = `${configPath}.ov-install-tmp.${process.pid}`;
|
|
2165
|
+
await writeFile(tmp, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
2166
|
+
await rename(tmp, configPath);
|
|
2167
|
+
info(tr(`Added OpenClaw plugin load path: ${pluginPath}`, `已添加 OpenClaw 插件加载路径: ${pluginPath}`));
|
|
2168
|
+
}
|
|
2169
|
+
|
|
1771
2170
|
async function configureOpenClawPlugin({
|
|
1772
2171
|
preserveExistingConfig = false,
|
|
1773
2172
|
runtimeConfig = null,
|
|
@@ -1821,6 +2220,8 @@ async function configureOpenClawPlugin({
|
|
|
1821
2220
|
await scrubStaleOpenClawPluginRegistration();
|
|
1822
2221
|
}
|
|
1823
2222
|
|
|
2223
|
+
await ensureOpenClawPluginLoadPath();
|
|
2224
|
+
|
|
1824
2225
|
// Enable plugin: try CLI first (default path), fall back to direct file for --workdir
|
|
1825
2226
|
if (!needWorkdirFlag) {
|
|
1826
2227
|
try {
|
|
@@ -1931,13 +2332,14 @@ async function configureOpenClawPlugin({
|
|
|
1931
2332
|
}
|
|
1932
2333
|
} catch { /* ignore read errors */ }
|
|
1933
2334
|
|
|
1934
|
-
let setupResult = null;
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
setupArgs
|
|
1938
|
-
setupArgs.push("--
|
|
1939
|
-
|
|
1940
|
-
|
|
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);
|
|
1941
2343
|
}
|
|
1942
2344
|
if (effectiveRuntimeConfig.agent_prefix) {
|
|
1943
2345
|
setupArgs.push("--agent-prefix", effectiveRuntimeConfig.agent_prefix);
|
|
@@ -1945,35 +2347,67 @@ async function configureOpenClawPlugin({
|
|
|
1945
2347
|
if (effectiveRuntimeConfig.accountId) {
|
|
1946
2348
|
setupArgs.push("--account-id", effectiveRuntimeConfig.accountId);
|
|
1947
2349
|
}
|
|
1948
|
-
if (effectiveRuntimeConfig.userId) {
|
|
1949
|
-
setupArgs.push("--user-id", effectiveRuntimeConfig.userId);
|
|
1950
|
-
}
|
|
1951
|
-
if (
|
|
1952
|
-
setupArgs.push("--force-slot");
|
|
1953
|
-
}
|
|
1954
|
-
if (
|
|
1955
|
-
setupArgs.push("--allow-offline");
|
|
1956
|
-
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
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) {
|
|
1977
2411
|
if (parsed.success) {
|
|
1978
2412
|
info(tr("OpenClaw plugin configured via setup", "OpenClaw 插件通过 setup 配置完成"));
|
|
1979
2413
|
if (parsed.health?.ok) {
|
|
@@ -1991,32 +2425,37 @@ async function configureOpenClawPlugin({
|
|
|
1991
2425
|
if (parsed.slot?.activated) {
|
|
1992
2426
|
info(tr(`contextEngine slot activated`, `contextEngine slot 已激活`));
|
|
1993
2427
|
}
|
|
1994
|
-
} else {
|
|
1995
|
-
// Setup returned success: false
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
`openclaw openviking setup
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
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) {
|
|
2020
2459
|
// Direct write: only used when the installed plugin doesn't support `setup --json` (old version).
|
|
2021
2460
|
// Read the deployed configSchema to determine which fields are allowed, avoiding
|
|
2022
2461
|
// "additionalProperties" validation failures when writing new fields to old schemas.
|
|
@@ -2153,7 +2592,7 @@ async function performUninstall() {
|
|
|
2153
2592
|
tr("Confirm uninstall? (y/N)", "确认卸载?(y/N)"),
|
|
2154
2593
|
"N",
|
|
2155
2594
|
);
|
|
2156
|
-
if (
|
|
2595
|
+
if (!isYes(answer)) {
|
|
2157
2596
|
info(tr("Cancelled.", "已取消。"));
|
|
2158
2597
|
return;
|
|
2159
2598
|
}
|
|
@@ -2262,11 +2701,16 @@ async function main() {
|
|
|
2262
2701
|
await rollbackLastUpgradeOperation();
|
|
2263
2702
|
return;
|
|
2264
2703
|
}
|
|
2265
|
-
await resolveDefaultPluginVersion();
|
|
2266
|
-
validateRequestedPluginVersion();
|
|
2267
|
-
info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
|
|
2268
|
-
info(tr(`
|
|
2269
|
-
|
|
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}`));
|
|
2270
2714
|
|
|
2271
2715
|
if (upgradePluginOnly) {
|
|
2272
2716
|
info(tr("Mode: plugin upgrade only", "模式: 仅升级插件"));
|