openclaw-openviking-setup-helper 0.3.0-beta.24 → 0.3.0-beta.26
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 +479 -367
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -29,12 +29,12 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
29
29
|
|
|
30
30
|
let REPO = process.env.REPO || "volcengine/OpenViking";
|
|
31
31
|
// PLUGIN_VERSION takes precedence over BRANCH (legacy). If omitted, resolve the latest tag from GitHub.
|
|
32
|
-
const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
|
|
33
|
-
let PLUGIN_VERSION = pluginVersionEnv;
|
|
34
|
-
let pluginVersionExplicit = Boolean(pluginVersionEnv);
|
|
35
|
-
const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
36
|
-
const DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION = "2026.5.3";
|
|
37
|
-
const OPENCLAW_SHORT_VERSION_YEAR = 2026;
|
|
32
|
+
const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
|
|
33
|
+
let PLUGIN_VERSION = pluginVersionEnv;
|
|
34
|
+
let pluginVersionExplicit = Boolean(pluginVersionEnv);
|
|
35
|
+
const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
36
|
+
const DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION = "2026.5.3";
|
|
37
|
+
const OPENCLAW_SHORT_VERSION_YEAR = 2026;
|
|
38
38
|
|
|
39
39
|
const IS_WIN = process.platform === "win32";
|
|
40
40
|
const HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
@@ -46,13 +46,13 @@ let PLUGIN_DEST = ""; // Will be set after resolving plugin config
|
|
|
46
46
|
// Fallback configs for old versions without manifest
|
|
47
47
|
const FALLBACK_LEGACY = {
|
|
48
48
|
dir: "openclaw-memory-plugin",
|
|
49
|
-
id: "memory-openviking",
|
|
50
|
-
kind: "memory",
|
|
51
|
-
slot: "memory",
|
|
52
|
-
minOpenclawVersion: "2026.3.7",
|
|
53
|
-
required: ["index.ts", "config.ts", "client.ts", "openclaw.plugin.json", "package.json"],
|
|
54
|
-
optional: ["package-lock.json", ".gitignore", "memory-ranking.ts", "text-utils.ts", "process-manager.ts", "tsconfig.json"],
|
|
55
|
-
};
|
|
49
|
+
id: "memory-openviking",
|
|
50
|
+
kind: "memory",
|
|
51
|
+
slot: "memory",
|
|
52
|
+
minOpenclawVersion: "2026.3.7",
|
|
53
|
+
required: ["index.ts", "config.ts", "client.ts", "openclaw.plugin.json", "package.json"],
|
|
54
|
+
optional: ["package-lock.json", ".gitignore", "memory-ranking.ts", "text-utils.ts", "process-manager.ts", "tsconfig.json"],
|
|
55
|
+
};
|
|
56
56
|
|
|
57
57
|
// Must match examples/openclaw-plugin/install-manifest.json (npm only installs package deps, not these .ts files).
|
|
58
58
|
const FALLBACK_CURRENT = {
|
|
@@ -60,7 +60,7 @@ const FALLBACK_CURRENT = {
|
|
|
60
60
|
id: "openviking",
|
|
61
61
|
kind: "context-engine",
|
|
62
62
|
slot: "contextEngine",
|
|
63
|
-
minOpenclawVersion: "2026.4.8",
|
|
63
|
+
minOpenclawVersion: "2026.4.8",
|
|
64
64
|
required: ["index.ts", "config.ts", "package.json", "openclaw.plugin.json"],
|
|
65
65
|
optional: [
|
|
66
66
|
"context-engine.ts",
|
|
@@ -89,25 +89,27 @@ let resolvedPluginDir = "";
|
|
|
89
89
|
let resolvedPluginId = "";
|
|
90
90
|
let resolvedPluginKind = "";
|
|
91
91
|
let resolvedPluginSlot = "";
|
|
92
|
-
let resolvedFilesRequired = [];
|
|
93
|
-
let resolvedFilesOptional = [];
|
|
94
|
-
let resolvedNpmOmitDev = true;
|
|
95
|
-
let resolvedNpmBuild = false;
|
|
96
|
-
let resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
97
|
-
let resolvedNpmBuildScript = "build";
|
|
98
|
-
let resolvedNpmPruneAfterBuild = true;
|
|
99
|
-
let resolvedMinOpenclawVersion = "";
|
|
100
|
-
let resolvedMinOpenvikingVersion = "";
|
|
101
|
-
let resolvedPluginReleaseId = "";
|
|
102
|
-
let detectedOpenClawVersion = "";
|
|
92
|
+
let resolvedFilesRequired = [];
|
|
93
|
+
let resolvedFilesOptional = [];
|
|
94
|
+
let resolvedNpmOmitDev = true;
|
|
95
|
+
let resolvedNpmBuild = false;
|
|
96
|
+
let resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
97
|
+
let resolvedNpmBuildScript = "build";
|
|
98
|
+
let resolvedNpmPruneAfterBuild = true;
|
|
99
|
+
let resolvedMinOpenclawVersion = "";
|
|
100
|
+
let resolvedMinOpenvikingVersion = "";
|
|
101
|
+
let resolvedPluginReleaseId = "";
|
|
102
|
+
let detectedOpenClawVersion = "";
|
|
103
103
|
|
|
104
104
|
let nonInteractive = false;
|
|
105
105
|
let langZh = false;
|
|
106
106
|
let workdirExplicit = false;
|
|
107
107
|
let upgradePluginOnly = false;
|
|
108
108
|
let rollbackLastUpgrade = false;
|
|
109
|
-
let showCurrentVersion = false;
|
|
110
|
-
let uninstallPlugin = false;
|
|
109
|
+
let showCurrentVersion = false;
|
|
110
|
+
let uninstallPlugin = false;
|
|
111
|
+
let forceSlotExplicit = false;
|
|
112
|
+
let allowOfflineExplicit = false;
|
|
111
113
|
|
|
112
114
|
const selectedMode = "remote";
|
|
113
115
|
const baseUrlFromEnv = !!process.env.OPENVIKING_BASE_URL;
|
|
@@ -147,11 +149,19 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
147
149
|
rollbackLastUpgrade = true;
|
|
148
150
|
continue;
|
|
149
151
|
}
|
|
150
|
-
if (arg === "--uninstall" || arg === "--remove") {
|
|
151
|
-
uninstallPlugin = true;
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (arg === "--
|
|
152
|
+
if (arg === "--uninstall" || arg === "--remove") {
|
|
153
|
+
uninstallPlugin = true;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (arg === "--force-slot") {
|
|
157
|
+
forceSlotExplicit = true;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (arg === "--allow-offline") {
|
|
161
|
+
allowOfflineExplicit = true;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (arg === "--workdir") {
|
|
155
165
|
const workdir = argv[i + 1]?.trim();
|
|
156
166
|
if (!workdir) {
|
|
157
167
|
console.error("--workdir requires a path");
|
|
@@ -282,9 +292,11 @@ function printHelp() {
|
|
|
282
292
|
console.log(" --base-url=URL OpenViking server URL (default: $OPENVIKING_BASE_URL or http://127.0.0.1:1933)");
|
|
283
293
|
console.log(" --api-key=KEY OpenViking API key (default: $OPENVIKING_API_KEY)");
|
|
284
294
|
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(" --
|
|
295
|
+
console.log(" --account-id=ID Account ID for root API key (default: $OPENVIKING_ACCOUNT_ID)");
|
|
296
|
+
console.log(" --user-id=ID User ID for root API key (default: $OPENVIKING_USER_ID)");
|
|
297
|
+
console.log(" --force-slot Explicitly replace an existing contextEngine slot owner");
|
|
298
|
+
console.log(" --allow-offline Explicitly save config when the OpenViking server is unreachable");
|
|
299
|
+
console.log(" --zh Chinese prompts");
|
|
288
300
|
console.log(" -h, --help This help");
|
|
289
301
|
console.log("");
|
|
290
302
|
console.log("Examples:");
|
|
@@ -403,47 +415,52 @@ function runCapture(cmd, args, opts = {}) {
|
|
|
403
415
|
});
|
|
404
416
|
}
|
|
405
417
|
|
|
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) => {
|
|
418
|
+
function question(prompt, defaultValue = "") {
|
|
419
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
420
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
421
|
+
return new Promise((resolve) => {
|
|
422
|
+
rl.question(`${prompt}${suffix}: `, (answer) => {
|
|
411
423
|
rl.close();
|
|
412
424
|
resolve((answer ?? defaultValue).trim() || defaultValue);
|
|
413
425
|
});
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function isValidAgentPrefixInput(value) {
|
|
418
|
-
const trimmed = String(value || "").trim();
|
|
419
|
-
return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
|
|
426
|
+
});
|
|
420
427
|
}
|
|
421
428
|
|
|
422
|
-
function
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
try {
|
|
426
|
-
return JSON.parse(text);
|
|
427
|
-
} catch {
|
|
428
|
-
// OpenClaw may print plugin registration logs before --json output.
|
|
429
|
-
}
|
|
430
|
-
for (let index = text.lastIndexOf("{"); index >= 0; index = text.lastIndexOf("{", index - 1)) {
|
|
431
|
-
try {
|
|
432
|
-
const parsed = JSON.parse(text.slice(index).trim());
|
|
433
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
434
|
-
return parsed;
|
|
435
|
-
}
|
|
436
|
-
} catch {
|
|
437
|
-
// Keep scanning earlier braces until the outer JSON object is found.
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return null;
|
|
429
|
+
function isYes(answer) {
|
|
430
|
+
const normalized = String(answer || "").trim().toLowerCase();
|
|
431
|
+
return normalized === "y" || normalized === "yes";
|
|
441
432
|
}
|
|
442
433
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
434
|
+
function isValidAgentPrefixInput(value) {
|
|
435
|
+
const trimmed = String(value || "").trim();
|
|
436
|
+
return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function parseJsonObjectFromOutput(output) {
|
|
440
|
+
const text = String(output || "").trim();
|
|
441
|
+
if (!text) return null;
|
|
442
|
+
try {
|
|
443
|
+
return JSON.parse(text);
|
|
444
|
+
} catch {
|
|
445
|
+
// OpenClaw may print plugin registration logs before --json output.
|
|
446
|
+
}
|
|
447
|
+
for (let index = text.lastIndexOf("{"); index >= 0; index = text.lastIndexOf("{", index - 1)) {
|
|
448
|
+
try {
|
|
449
|
+
const parsed = JSON.parse(text.slice(index).trim());
|
|
450
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
451
|
+
return parsed;
|
|
452
|
+
}
|
|
453
|
+
} catch {
|
|
454
|
+
// Keep scanning earlier braces until the outer JSON object is found.
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async function questionAgentPrefix(defaultValue = "") {
|
|
461
|
+
while (true) {
|
|
462
|
+
const answer = (await question(
|
|
463
|
+
tr("Agent Prefix (optional)", "Agent Prefix(可选)"),
|
|
447
464
|
defaultValue,
|
|
448
465
|
)).trim();
|
|
449
466
|
if (isValidAgentPrefixInput(answer)) {
|
|
@@ -530,68 +547,68 @@ async function checkOpenClaw() {
|
|
|
530
547
|
}
|
|
531
548
|
|
|
532
549
|
// Compare versions: returns true if v1 >= v2
|
|
533
|
-
function versionGte(v1, v2) {
|
|
534
|
-
const parseVersion = (v) => {
|
|
535
|
-
const cleaned = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
536
|
-
const parts = cleaned.split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
537
|
-
while (parts.length < 3) parts.push(0);
|
|
550
|
+
function versionGte(v1, v2) {
|
|
551
|
+
const parseVersion = (v) => {
|
|
552
|
+
const cleaned = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
553
|
+
const parts = cleaned.split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
554
|
+
while (parts.length < 3) parts.push(0);
|
|
538
555
|
return parts;
|
|
539
556
|
};
|
|
540
557
|
const [a1, a2, a3] = parseVersion(v1);
|
|
541
558
|
const [b1, b2, b3] = parseVersion(v2);
|
|
542
559
|
if (a1 !== b1) return a1 > b1;
|
|
543
|
-
if (a2 !== b2) return a2 > b2;
|
|
544
|
-
return a3 >= b3;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function parseOpenClawPolicyVersion(value) {
|
|
548
|
-
const parts = String(value || "")
|
|
549
|
-
.match(/\d+/g)
|
|
550
|
-
?.map((part) => Number.parseInt(part, 10) || 0) || [];
|
|
551
|
-
if (parts.length === 0) return [0, 0, 0];
|
|
552
|
-
if (parts[0] >= 2000) {
|
|
553
|
-
return [parts[0], parts[1] || 0, parts[2] || 0];
|
|
554
|
-
}
|
|
555
|
-
return [OPENCLAW_SHORT_VERSION_YEAR, parts[0] || 0, parts[1] || 0];
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function openClawPolicyVersionGte(v1, v2) {
|
|
559
|
-
const a = parseOpenClawPolicyVersion(v1);
|
|
560
|
-
const b = parseOpenClawPolicyVersion(v2);
|
|
561
|
-
for (let i = 0; i < 3; i++) {
|
|
562
|
-
if (a[i] !== b[i]) return a[i] > b[i];
|
|
563
|
-
}
|
|
564
|
-
return true;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
function applyOpenClawBuildPolicy(openClawVersion) {
|
|
568
|
-
if (!resolvedNpmBuild || !resolvedNpmBuildMinOpenclawVersion) {
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
if (!openClawVersion || openClawVersion === "0.0.0") {
|
|
572
|
-
warn(tr(
|
|
573
|
-
"Could not determine OpenClaw version; keeping plugin source build enabled.",
|
|
574
|
-
"无法确定 OpenClaw 版本,保持插件源码构建开启。",
|
|
575
|
-
));
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (openClawPolicyVersionGte(openClawVersion, resolvedNpmBuildMinOpenclawVersion)) {
|
|
579
|
-
info(tr(
|
|
580
|
-
`OpenClaw ${openClawVersion} requires plugin source build (>= ${resolvedNpmBuildMinOpenclawVersion})`,
|
|
581
|
-
`OpenClaw ${openClawVersion} 需要插件源码构建(>= ${resolvedNpmBuildMinOpenclawVersion})`,
|
|
582
|
-
));
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
resolvedNpmBuild = false;
|
|
586
|
-
info(tr(
|
|
587
|
-
`OpenClaw ${openClawVersion} is below ${resolvedNpmBuildMinOpenclawVersion}; skipping plugin source build`,
|
|
588
|
-
`OpenClaw ${openClawVersion} 低于 ${resolvedNpmBuildMinOpenclawVersion},跳过插件源码构建`,
|
|
589
|
-
));
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function isSemverLike(value) {
|
|
593
|
-
return /^v?\d+(\.\d+){1,2}$/.test(value);
|
|
594
|
-
}
|
|
560
|
+
if (a2 !== b2) return a2 > b2;
|
|
561
|
+
return a3 >= b3;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function parseOpenClawPolicyVersion(value) {
|
|
565
|
+
const parts = String(value || "")
|
|
566
|
+
.match(/\d+/g)
|
|
567
|
+
?.map((part) => Number.parseInt(part, 10) || 0) || [];
|
|
568
|
+
if (parts.length === 0) return [0, 0, 0];
|
|
569
|
+
if (parts[0] >= 2000) {
|
|
570
|
+
return [parts[0], parts[1] || 0, parts[2] || 0];
|
|
571
|
+
}
|
|
572
|
+
return [OPENCLAW_SHORT_VERSION_YEAR, parts[0] || 0, parts[1] || 0];
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function openClawPolicyVersionGte(v1, v2) {
|
|
576
|
+
const a = parseOpenClawPolicyVersion(v1);
|
|
577
|
+
const b = parseOpenClawPolicyVersion(v2);
|
|
578
|
+
for (let i = 0; i < 3; i++) {
|
|
579
|
+
if (a[i] !== b[i]) return a[i] > b[i];
|
|
580
|
+
}
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function applyOpenClawBuildPolicy(openClawVersion) {
|
|
585
|
+
if (!resolvedNpmBuild || !resolvedNpmBuildMinOpenclawVersion) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (!openClawVersion || openClawVersion === "0.0.0") {
|
|
589
|
+
warn(tr(
|
|
590
|
+
"Could not determine OpenClaw version; keeping plugin source build enabled.",
|
|
591
|
+
"无法确定 OpenClaw 版本,保持插件源码构建开启。",
|
|
592
|
+
));
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (openClawPolicyVersionGte(openClawVersion, resolvedNpmBuildMinOpenclawVersion)) {
|
|
596
|
+
info(tr(
|
|
597
|
+
`OpenClaw ${openClawVersion} requires plugin source build (>= ${resolvedNpmBuildMinOpenclawVersion})`,
|
|
598
|
+
`OpenClaw ${openClawVersion} 需要插件源码构建(>= ${resolvedNpmBuildMinOpenclawVersion})`,
|
|
599
|
+
));
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
resolvedNpmBuild = false;
|
|
603
|
+
info(tr(
|
|
604
|
+
`OpenClaw ${openClawVersion} is below ${resolvedNpmBuildMinOpenclawVersion}; skipping plugin source build`,
|
|
605
|
+
`OpenClaw ${openClawVersion} 低于 ${resolvedNpmBuildMinOpenclawVersion},跳过插件源码构建`,
|
|
606
|
+
));
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function isSemverLike(value) {
|
|
610
|
+
return /^v?\d+(\.\d+){1,2}$/.test(value);
|
|
611
|
+
}
|
|
595
612
|
|
|
596
613
|
function validateRequestedPluginVersion() {
|
|
597
614
|
if (!isSemverLike(PLUGIN_VERSION)) return;
|
|
@@ -612,24 +629,24 @@ if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
|
|
|
612
629
|
}
|
|
613
630
|
|
|
614
631
|
// Detect OpenClaw version
|
|
615
|
-
async function detectOpenClawVersion() {
|
|
616
|
-
if (detectedOpenClawVersion) {
|
|
617
|
-
return detectedOpenClawVersion;
|
|
618
|
-
}
|
|
619
|
-
try {
|
|
620
|
-
const result = await runCapture("openclaw", ["--version"], { shell: IS_WIN });
|
|
621
|
-
const output = `${result.out || ""}\n${result.err || ""}`;
|
|
622
|
-
if (result.code === 0 && output) {
|
|
623
|
-
const match = output.match(/\d+\.\d+(\.\d+)?/);
|
|
624
|
-
if (match) {
|
|
625
|
-
detectedOpenClawVersion = match[0];
|
|
626
|
-
return detectedOpenClawVersion;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
} catch {}
|
|
630
|
-
detectedOpenClawVersion = "0.0.0";
|
|
631
|
-
return detectedOpenClawVersion;
|
|
632
|
-
}
|
|
632
|
+
async function detectOpenClawVersion() {
|
|
633
|
+
if (detectedOpenClawVersion) {
|
|
634
|
+
return detectedOpenClawVersion;
|
|
635
|
+
}
|
|
636
|
+
try {
|
|
637
|
+
const result = await runCapture("openclaw", ["--version"], { shell: IS_WIN });
|
|
638
|
+
const output = `${result.out || ""}\n${result.err || ""}`;
|
|
639
|
+
if (result.code === 0 && output) {
|
|
640
|
+
const match = output.match(/\d+\.\d+(\.\d+)?/);
|
|
641
|
+
if (match) {
|
|
642
|
+
detectedOpenClawVersion = match[0];
|
|
643
|
+
return detectedOpenClawVersion;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
} catch {}
|
|
647
|
+
detectedOpenClawVersion = "0.0.0";
|
|
648
|
+
return detectedOpenClawVersion;
|
|
649
|
+
}
|
|
633
650
|
|
|
634
651
|
// Try to fetch a URL, return response text or null
|
|
635
652
|
async function tryFetch(url, timeout = 15000) {
|
|
@@ -772,19 +789,19 @@ async function resolveDefaultPluginVersion() {
|
|
|
772
789
|
}
|
|
773
790
|
|
|
774
791
|
// Resolve plugin configuration from manifest or fallback
|
|
775
|
-
async function resolvePluginConfig() {
|
|
776
|
-
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
777
|
-
|
|
778
|
-
info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
|
|
779
|
-
|
|
780
|
-
resolvedNpmOmitDev = true;
|
|
781
|
-
resolvedNpmBuild = false;
|
|
782
|
-
resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
783
|
-
resolvedNpmBuildScript = "build";
|
|
784
|
-
resolvedNpmPruneAfterBuild = true;
|
|
785
|
-
|
|
786
|
-
let pluginDir = "";
|
|
787
|
-
let manifestData = null;
|
|
792
|
+
async function resolvePluginConfig() {
|
|
793
|
+
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
794
|
+
|
|
795
|
+
info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
|
|
796
|
+
|
|
797
|
+
resolvedNpmOmitDev = true;
|
|
798
|
+
resolvedNpmBuild = false;
|
|
799
|
+
resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
800
|
+
resolvedNpmBuildScript = "build";
|
|
801
|
+
resolvedNpmPruneAfterBuild = true;
|
|
802
|
+
|
|
803
|
+
let pluginDir = "";
|
|
804
|
+
let manifestData = null;
|
|
788
805
|
|
|
789
806
|
// Try to detect plugin directory and download manifest
|
|
790
807
|
const manifestCurrent = await tryFetch(`${ghRaw}/examples/openclaw-plugin/install-manifest.json`);
|
|
@@ -821,25 +838,25 @@ async function resolvePluginConfig() {
|
|
|
821
838
|
resolvedPluginId = manifestData.plugin?.id || "";
|
|
822
839
|
resolvedPluginKind = manifestData.plugin?.kind || "";
|
|
823
840
|
resolvedPluginSlot = manifestData.plugin?.slot || "";
|
|
824
|
-
resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
|
|
825
|
-
resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
|
|
826
|
-
resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
|
|
827
|
-
const npmConfig = manifestData.npm && typeof manifestData.npm === "object"
|
|
828
|
-
? manifestData.npm
|
|
829
|
-
: {};
|
|
830
|
-
resolvedNpmOmitDev = npmConfig.omitDev !== false;
|
|
831
|
-
resolvedNpmBuild = npmConfig.build === true || npmConfig.buildFromSource === true;
|
|
832
|
-
resolvedNpmBuildMinOpenclawVersion =
|
|
833
|
-
typeof npmConfig.buildMinOpenclawVersion === "string" && npmConfig.buildMinOpenclawVersion.trim()
|
|
834
|
-
? npmConfig.buildMinOpenclawVersion.trim()
|
|
835
|
-
: DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
836
|
-
resolvedNpmBuildScript = typeof npmConfig.buildScript === "string" && npmConfig.buildScript.trim()
|
|
837
|
-
? npmConfig.buildScript.trim()
|
|
838
|
-
: "build";
|
|
839
|
-
resolvedNpmPruneAfterBuild = npmConfig.pruneAfterBuild !== false;
|
|
840
|
-
resolvedFilesRequired = manifestData.files?.required || [];
|
|
841
|
-
resolvedFilesOptional = manifestData.files?.optional || [];
|
|
842
|
-
} else {
|
|
841
|
+
resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
|
|
842
|
+
resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
|
|
843
|
+
resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
|
|
844
|
+
const npmConfig = manifestData.npm && typeof manifestData.npm === "object"
|
|
845
|
+
? manifestData.npm
|
|
846
|
+
: {};
|
|
847
|
+
resolvedNpmOmitDev = npmConfig.omitDev !== false;
|
|
848
|
+
resolvedNpmBuild = npmConfig.build === true || npmConfig.buildFromSource === true;
|
|
849
|
+
resolvedNpmBuildMinOpenclawVersion =
|
|
850
|
+
typeof npmConfig.buildMinOpenclawVersion === "string" && npmConfig.buildMinOpenclawVersion.trim()
|
|
851
|
+
? npmConfig.buildMinOpenclawVersion.trim()
|
|
852
|
+
: DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
853
|
+
resolvedNpmBuildScript = typeof npmConfig.buildScript === "string" && npmConfig.buildScript.trim()
|
|
854
|
+
? npmConfig.buildScript.trim()
|
|
855
|
+
: "build";
|
|
856
|
+
resolvedNpmPruneAfterBuild = npmConfig.pruneAfterBuild !== false;
|
|
857
|
+
resolvedFilesRequired = manifestData.files?.required || [];
|
|
858
|
+
resolvedFilesOptional = manifestData.files?.optional || [];
|
|
859
|
+
} else {
|
|
843
860
|
// No manifest — determine plugin identity by package.json name
|
|
844
861
|
let fallbackKey = pluginDir === "openclaw-memory-plugin" ? "legacy" : "current";
|
|
845
862
|
let compatVer = "";
|
|
@@ -868,15 +885,15 @@ async function resolvePluginConfig() {
|
|
|
868
885
|
resolvedPluginId = fallback.id;
|
|
869
886
|
resolvedPluginKind = fallback.kind;
|
|
870
887
|
resolvedPluginSlot = fallback.slot;
|
|
871
|
-
resolvedFilesRequired = fallback.required;
|
|
872
|
-
resolvedFilesOptional = fallback.optional;
|
|
873
|
-
resolvedNpmOmitDev = true;
|
|
874
|
-
resolvedNpmBuild = false;
|
|
875
|
-
resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
876
|
-
resolvedNpmBuildScript = "build";
|
|
877
|
-
resolvedNpmPruneAfterBuild = true;
|
|
878
|
-
|
|
879
|
-
// If no compatVer from package.json, try main branch manifest
|
|
888
|
+
resolvedFilesRequired = fallback.required;
|
|
889
|
+
resolvedFilesOptional = fallback.optional;
|
|
890
|
+
resolvedNpmOmitDev = true;
|
|
891
|
+
resolvedNpmBuild = false;
|
|
892
|
+
resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
|
|
893
|
+
resolvedNpmBuildScript = "build";
|
|
894
|
+
resolvedNpmPruneAfterBuild = true;
|
|
895
|
+
|
|
896
|
+
// If no compatVer from package.json, try main branch manifest
|
|
880
897
|
if (!compatVer && PLUGIN_VERSION !== "main") {
|
|
881
898
|
const mainRaw = `https://raw.githubusercontent.com/${REPO}/main`;
|
|
882
899
|
const mainManifest = await tryFetch(`${mainRaw}/examples/openclaw-plugin/install-manifest.json`);
|
|
@@ -907,12 +924,12 @@ async function checkOpenClawCompatibility() {
|
|
|
907
924
|
return;
|
|
908
925
|
}
|
|
909
926
|
|
|
910
|
-
const ocVersion = await detectOpenClawVersion();
|
|
911
|
-
info(tr(`Detected OpenClaw version: ${ocVersion}`, `检测到 OpenClaw 版本: ${ocVersion}`));
|
|
912
|
-
applyOpenClawBuildPolicy(ocVersion);
|
|
913
|
-
|
|
914
|
-
// If no minimum version required, pass
|
|
915
|
-
if (!resolvedMinOpenclawVersion) {
|
|
927
|
+
const ocVersion = await detectOpenClawVersion();
|
|
928
|
+
info(tr(`Detected OpenClaw version: ${ocVersion}`, `检测到 OpenClaw 版本: ${ocVersion}`));
|
|
929
|
+
applyOpenClawBuildPolicy(ocVersion);
|
|
930
|
+
|
|
931
|
+
// If no minimum version required, pass
|
|
932
|
+
if (!resolvedMinOpenclawVersion) {
|
|
916
933
|
return;
|
|
917
934
|
}
|
|
918
935
|
|
|
@@ -922,7 +939,7 @@ async function checkOpenClawCompatibility() {
|
|
|
922
939
|
}
|
|
923
940
|
|
|
924
941
|
// Check compatibility
|
|
925
|
-
if (!
|
|
942
|
+
if (!openClawPolicyVersionGte(ocVersion, resolvedMinOpenclawVersion)) {
|
|
926
943
|
err(tr(
|
|
927
944
|
`OpenClaw ${ocVersion} does not support this plugin (requires >= ${resolvedMinOpenclawVersion})`,
|
|
928
945
|
`OpenClaw ${ocVersion} 不支持此插件(需要 >= ${resolvedMinOpenclawVersion})`
|
|
@@ -1086,11 +1103,17 @@ function extractRuntimeConfigFromPluginEntry(entryConfig) {
|
|
|
1086
1103
|
runtime.apiKey = entryConfig.apiKey;
|
|
1087
1104
|
}
|
|
1088
1105
|
const prefix = entryConfig.agent_prefix || entryConfig.agentId;
|
|
1089
|
-
if (typeof prefix === "string" && prefix.trim()) {
|
|
1090
|
-
runtime.agent_prefix = prefix.trim();
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1106
|
+
if (typeof prefix === "string" && prefix.trim()) {
|
|
1107
|
+
runtime.agent_prefix = prefix.trim();
|
|
1108
|
+
}
|
|
1109
|
+
if (typeof entryConfig.accountId === "string" && entryConfig.accountId.trim()) {
|
|
1110
|
+
runtime.accountId = entryConfig.accountId.trim();
|
|
1111
|
+
}
|
|
1112
|
+
if (typeof entryConfig.userId === "string" && entryConfig.userId.trim()) {
|
|
1113
|
+
runtime.userId = entryConfig.userId.trim();
|
|
1114
|
+
}
|
|
1115
|
+
return runtime;
|
|
1116
|
+
}
|
|
1094
1117
|
|
|
1095
1118
|
async function backupOpenClawConfig(configPath) {
|
|
1096
1119
|
await mkdir(getUpgradeAuditDir(), { recursive: true });
|
|
@@ -1380,10 +1403,12 @@ async function prepareStrongPluginUpgrade() {
|
|
|
1380
1403
|
`检测到已安装 OpenViking 插件状态: ${installedState.generation}`,
|
|
1381
1404
|
),
|
|
1382
1405
|
);
|
|
1383
|
-
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1384
|
-
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1385
|
-
remoteAgentPrefix = upgradeRuntimeConfig.agent_prefix || "";
|
|
1386
|
-
|
|
1406
|
+
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1407
|
+
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1408
|
+
remoteAgentPrefix = upgradeRuntimeConfig.agent_prefix || "";
|
|
1409
|
+
remoteAccountId = upgradeRuntimeConfig.accountId || "";
|
|
1410
|
+
remoteUserId = upgradeRuntimeConfig.userId || "";
|
|
1411
|
+
info(tr(`Upgrade runtime mode: ${selectedMode} (remote OpenViking server)`, `升级运行模式: ${selectedMode}(远程 OpenViking 服务)`));
|
|
1387
1412
|
|
|
1388
1413
|
info(tr(`Upgrade path: ${fromVersion} -> ${toVersion}`, `升级路径: ${fromVersion} -> ${toVersion}`));
|
|
1389
1414
|
|
|
@@ -1418,7 +1443,7 @@ async function prepareStrongPluginUpgrade() {
|
|
|
1418
1443
|
info(tr(`Upgrade audit file: ${getUpgradeAuditPath()}`, `升级审计文件: ${getUpgradeAuditPath()}`));
|
|
1419
1444
|
}
|
|
1420
1445
|
|
|
1421
|
-
async function downloadPluginFile(destDir, fileName, url, required, index, total) {
|
|
1446
|
+
async function downloadPluginFile(destDir, fileName, url, required, index, total) {
|
|
1422
1447
|
const maxRetries = 3;
|
|
1423
1448
|
const destPath = join(destDir, fileName);
|
|
1424
1449
|
|
|
@@ -1478,109 +1503,109 @@ async function downloadPluginFile(destDir, fileName, url, required, index, total
|
|
|
1478
1503
|
|
|
1479
1504
|
console.log("");
|
|
1480
1505
|
err(tr(`Download failed after ${maxRetries} retries: ${url}`, `下载失败(已重试 ${maxRetries} 次): ${url}`));
|
|
1481
|
-
process.exit(1);
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
function runtimeOutputCandidatesForEntry(entry) {
|
|
1485
|
-
const normalized = String(entry || "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1486
|
-
if (!normalized.endsWith(".ts")) {
|
|
1487
|
-
return [];
|
|
1488
|
-
}
|
|
1489
|
-
const withoutExt = normalized.slice(0, -3);
|
|
1490
|
-
return [
|
|
1491
|
-
`dist/${withoutExt}.js`,
|
|
1492
|
-
`dist/${withoutExt}.mjs`,
|
|
1493
|
-
`dist/${withoutExt}.cjs`,
|
|
1494
|
-
`${withoutExt}.js`,
|
|
1495
|
-
`${withoutExt}.mjs`,
|
|
1496
|
-
`${withoutExt}.cjs`,
|
|
1497
|
-
];
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
async function assertBuiltRuntimeOutputs(destDir) {
|
|
1501
|
-
let pkg = null;
|
|
1502
|
-
try {
|
|
1503
|
-
pkg = JSON.parse(await readFile(join(destDir, "package.json"), "utf8"));
|
|
1504
|
-
} catch {
|
|
1505
|
-
return;
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
const entries = [];
|
|
1509
|
-
const extensions = pkg?.openclaw?.extensions;
|
|
1510
|
-
if (Array.isArray(extensions)) {
|
|
1511
|
-
for (const entry of extensions) {
|
|
1512
|
-
if (typeof entry === "string") entries.push(entry);
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
if (typeof pkg?.openclaw?.setupEntry === "string") {
|
|
1516
|
-
entries.push(pkg.openclaw.setupEntry);
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
const missing = [];
|
|
1520
|
-
for (const entry of entries) {
|
|
1521
|
-
const candidates = runtimeOutputCandidatesForEntry(entry);
|
|
1522
|
-
if (candidates.length === 0) continue;
|
|
1523
|
-
const found = candidates.some((candidate) => existsSync(join(destDir, ...candidate.split("/"))));
|
|
1524
|
-
if (!found) {
|
|
1525
|
-
missing.push(`${entry} (expected one of: ${candidates.join(", ")})`);
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
if (missing.length === 0) {
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
err(tr(
|
|
1534
|
-
`Plugin build did not create required runtime output:\n - ${missing.join("\n - ")}`,
|
|
1535
|
-
`插件构建未生成必需的运行时产物:\n - ${missing.join("\n - ")}`,
|
|
1536
|
-
));
|
|
1537
|
-
process.exit(1);
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
async function installPluginNpmDependencies(destDir) {
|
|
1541
|
-
if (!resolvedNpmBuild) {
|
|
1542
|
-
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
1543
|
-
const npmArgs = resolvedNpmOmitDev
|
|
1544
|
-
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
1545
|
-
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
1546
|
-
await run("npm", npmArgs, { cwd: destDir, silent: false });
|
|
1547
|
-
return;
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
info(tr(
|
|
1551
|
-
"Installing plugin npm dependencies for source build...",
|
|
1552
|
-
"正在安装插件源码构建所需的 npm 依赖...",
|
|
1553
|
-
));
|
|
1554
|
-
await run("npm", [
|
|
1555
|
-
"install",
|
|
1556
|
-
"--include=dev",
|
|
1557
|
-
"--no-audit",
|
|
1558
|
-
"--no-fund",
|
|
1559
|
-
"--registry",
|
|
1560
|
-
NPM_REGISTRY,
|
|
1561
|
-
], { cwd: destDir, silent: false });
|
|
1562
|
-
|
|
1563
|
-
info(tr(
|
|
1564
|
-
`Building plugin runtime output with npm run ${resolvedNpmBuildScript}...`,
|
|
1565
|
-
`正在执行 npm run ${resolvedNpmBuildScript} 构建插件运行时产物...`,
|
|
1566
|
-
));
|
|
1567
|
-
await run("npm", ["run", resolvedNpmBuildScript], { cwd: destDir, silent: false });
|
|
1568
|
-
await assertBuiltRuntimeOutputs(destDir);
|
|
1569
|
-
|
|
1570
|
-
if (resolvedNpmOmitDev && resolvedNpmPruneAfterBuild) {
|
|
1571
|
-
info(tr("Pruning plugin dev dependencies...", "正在裁剪插件开发依赖..."));
|
|
1572
|
-
await run("npm", [
|
|
1573
|
-
"prune",
|
|
1574
|
-
"--omit=dev",
|
|
1575
|
-
"--no-audit",
|
|
1576
|
-
"--no-fund",
|
|
1577
|
-
], { cwd: destDir, silent: false });
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
async function downloadPlugin(destDir) {
|
|
1582
|
-
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
1583
|
-
const pluginDir = resolvedPluginDir;
|
|
1506
|
+
process.exit(1);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
function runtimeOutputCandidatesForEntry(entry) {
|
|
1510
|
+
const normalized = String(entry || "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1511
|
+
if (!normalized.endsWith(".ts")) {
|
|
1512
|
+
return [];
|
|
1513
|
+
}
|
|
1514
|
+
const withoutExt = normalized.slice(0, -3);
|
|
1515
|
+
return [
|
|
1516
|
+
`dist/${withoutExt}.js`,
|
|
1517
|
+
`dist/${withoutExt}.mjs`,
|
|
1518
|
+
`dist/${withoutExt}.cjs`,
|
|
1519
|
+
`${withoutExt}.js`,
|
|
1520
|
+
`${withoutExt}.mjs`,
|
|
1521
|
+
`${withoutExt}.cjs`,
|
|
1522
|
+
];
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
async function assertBuiltRuntimeOutputs(destDir) {
|
|
1526
|
+
let pkg = null;
|
|
1527
|
+
try {
|
|
1528
|
+
pkg = JSON.parse(await readFile(join(destDir, "package.json"), "utf8"));
|
|
1529
|
+
} catch {
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
const entries = [];
|
|
1534
|
+
const extensions = pkg?.openclaw?.extensions;
|
|
1535
|
+
if (Array.isArray(extensions)) {
|
|
1536
|
+
for (const entry of extensions) {
|
|
1537
|
+
if (typeof entry === "string") entries.push(entry);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (typeof pkg?.openclaw?.setupEntry === "string") {
|
|
1541
|
+
entries.push(pkg.openclaw.setupEntry);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
const missing = [];
|
|
1545
|
+
for (const entry of entries) {
|
|
1546
|
+
const candidates = runtimeOutputCandidatesForEntry(entry);
|
|
1547
|
+
if (candidates.length === 0) continue;
|
|
1548
|
+
const found = candidates.some((candidate) => existsSync(join(destDir, ...candidate.split("/"))));
|
|
1549
|
+
if (!found) {
|
|
1550
|
+
missing.push(`${entry} (expected one of: ${candidates.join(", ")})`);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
if (missing.length === 0) {
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
err(tr(
|
|
1559
|
+
`Plugin build did not create required runtime output:\n - ${missing.join("\n - ")}`,
|
|
1560
|
+
`插件构建未生成必需的运行时产物:\n - ${missing.join("\n - ")}`,
|
|
1561
|
+
));
|
|
1562
|
+
process.exit(1);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
async function installPluginNpmDependencies(destDir) {
|
|
1566
|
+
if (!resolvedNpmBuild) {
|
|
1567
|
+
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
1568
|
+
const npmArgs = resolvedNpmOmitDev
|
|
1569
|
+
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
1570
|
+
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
1571
|
+
await run("npm", npmArgs, { cwd: destDir, silent: false });
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
info(tr(
|
|
1576
|
+
"Installing plugin npm dependencies for source build...",
|
|
1577
|
+
"正在安装插件源码构建所需的 npm 依赖...",
|
|
1578
|
+
));
|
|
1579
|
+
await run("npm", [
|
|
1580
|
+
"install",
|
|
1581
|
+
"--include=dev",
|
|
1582
|
+
"--no-audit",
|
|
1583
|
+
"--no-fund",
|
|
1584
|
+
"--registry",
|
|
1585
|
+
NPM_REGISTRY,
|
|
1586
|
+
], { cwd: destDir, silent: false });
|
|
1587
|
+
|
|
1588
|
+
info(tr(
|
|
1589
|
+
`Building plugin runtime output with npm run ${resolvedNpmBuildScript}...`,
|
|
1590
|
+
`正在执行 npm run ${resolvedNpmBuildScript} 构建插件运行时产物...`,
|
|
1591
|
+
));
|
|
1592
|
+
await run("npm", ["run", resolvedNpmBuildScript], { cwd: destDir, silent: false });
|
|
1593
|
+
await assertBuiltRuntimeOutputs(destDir);
|
|
1594
|
+
|
|
1595
|
+
if (resolvedNpmOmitDev && resolvedNpmPruneAfterBuild) {
|
|
1596
|
+
info(tr("Pruning plugin dev dependencies...", "正在裁剪插件开发依赖..."));
|
|
1597
|
+
await run("npm", [
|
|
1598
|
+
"prune",
|
|
1599
|
+
"--omit=dev",
|
|
1600
|
+
"--no-audit",
|
|
1601
|
+
"--no-fund",
|
|
1602
|
+
], { cwd: destDir, silent: false });
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
async function downloadPlugin(destDir) {
|
|
1607
|
+
const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
|
|
1608
|
+
const pluginDir = resolvedPluginDir;
|
|
1584
1609
|
const total = resolvedFilesRequired.length + resolvedFilesOptional.length;
|
|
1585
1610
|
|
|
1586
1611
|
await mkdir(destDir, { recursive: true });
|
|
@@ -1604,9 +1629,9 @@ async function downloadPlugin(destDir) {
|
|
|
1604
1629
|
await downloadPluginFile(destDir, name, url, false, i, total);
|
|
1605
1630
|
}
|
|
1606
1631
|
|
|
1607
|
-
await installPluginNpmDependencies(destDir);
|
|
1608
|
-
info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
|
|
1609
|
-
}
|
|
1632
|
+
await installPluginNpmDependencies(destDir);
|
|
1633
|
+
info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
|
|
1634
|
+
}
|
|
1610
1635
|
|
|
1611
1636
|
async function createPluginStagingDir() {
|
|
1612
1637
|
const pluginId = resolvedPluginId || "openviking";
|
|
@@ -1768,6 +1793,53 @@ async function cleanupConflictingPluginVariants() {
|
|
|
1768
1793
|
info(tr("Conflicting plugin variants cleaned up", "冲突的插件变体已清理"));
|
|
1769
1794
|
}
|
|
1770
1795
|
|
|
1796
|
+
function normalizeOpenClawLoadPath(filePath) {
|
|
1797
|
+
return String(filePath || "")
|
|
1798
|
+
.replace(/\\/g, "/")
|
|
1799
|
+
.replace(/\/+$/g, "");
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
async function ensureOpenClawPluginLoadPath() {
|
|
1803
|
+
const configPath = getOpenClawConfigPath();
|
|
1804
|
+
let cfg = {};
|
|
1805
|
+
if (existsSync(configPath)) {
|
|
1806
|
+
try {
|
|
1807
|
+
cfg = JSON.parse(await readFile(configPath, "utf8"));
|
|
1808
|
+
} catch {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
const pluginPath = PLUGIN_DEST;
|
|
1814
|
+
const normalizedPluginPath = normalizeOpenClawLoadPath(pluginPath);
|
|
1815
|
+
const plugins = cfg.plugins && typeof cfg.plugins === "object" && !Array.isArray(cfg.plugins)
|
|
1816
|
+
? cfg.plugins
|
|
1817
|
+
: {};
|
|
1818
|
+
const load = plugins.load && typeof plugins.load === "object" && !Array.isArray(plugins.load)
|
|
1819
|
+
? plugins.load
|
|
1820
|
+
: {};
|
|
1821
|
+
const paths = Array.isArray(load.paths) ? load.paths : [];
|
|
1822
|
+
if (paths.some((item) => normalizeOpenClawLoadPath(item) === normalizedPluginPath)) {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
const next = {
|
|
1827
|
+
...cfg,
|
|
1828
|
+
plugins: {
|
|
1829
|
+
...plugins,
|
|
1830
|
+
load: {
|
|
1831
|
+
...load,
|
|
1832
|
+
paths: [...paths, pluginPath],
|
|
1833
|
+
},
|
|
1834
|
+
},
|
|
1835
|
+
};
|
|
1836
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
1837
|
+
const tmp = `${configPath}.ov-install-tmp.${process.pid}`;
|
|
1838
|
+
await writeFile(tmp, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
1839
|
+
await rename(tmp, configPath);
|
|
1840
|
+
info(tr(`Added OpenClaw plugin load path: ${pluginPath}`, `已添加 OpenClaw 插件加载路径: ${pluginPath}`));
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1771
1843
|
async function configureOpenClawPlugin({
|
|
1772
1844
|
preserveExistingConfig = false,
|
|
1773
1845
|
runtimeConfig = null,
|
|
@@ -1821,6 +1893,8 @@ async function configureOpenClawPlugin({
|
|
|
1821
1893
|
await scrubStaleOpenClawPluginRegistration();
|
|
1822
1894
|
}
|
|
1823
1895
|
|
|
1896
|
+
await ensureOpenClawPluginLoadPath();
|
|
1897
|
+
|
|
1824
1898
|
// Enable plugin: try CLI first (default path), fall back to direct file for --workdir
|
|
1825
1899
|
if (!needWorkdirFlag) {
|
|
1826
1900
|
try {
|
|
@@ -1932,12 +2006,13 @@ async function configureOpenClawPlugin({
|
|
|
1932
2006
|
} catch { /* ignore read errors */ }
|
|
1933
2007
|
|
|
1934
2008
|
let setupResult = null;
|
|
1935
|
-
|
|
2009
|
+
let parsed = null;
|
|
2010
|
+
const runSetupJson = async (extraArgs = []) => {
|
|
1936
2011
|
const setupArgs = ["openviking", "setup"];
|
|
1937
|
-
setupArgs.push("--base-url", effectiveRuntimeConfig.baseUrl || remoteBaseUrl);
|
|
1938
|
-
setupArgs.push("--json");
|
|
1939
|
-
if (effectiveRuntimeConfig.apiKey) {
|
|
1940
|
-
setupArgs.push("--api-key", effectiveRuntimeConfig.apiKey);
|
|
2012
|
+
setupArgs.push("--base-url", effectiveRuntimeConfig.baseUrl || remoteBaseUrl);
|
|
2013
|
+
setupArgs.push("--json");
|
|
2014
|
+
if (effectiveRuntimeConfig.apiKey) {
|
|
2015
|
+
setupArgs.push("--api-key", effectiveRuntimeConfig.apiKey);
|
|
1941
2016
|
}
|
|
1942
2017
|
if (effectiveRuntimeConfig.agent_prefix) {
|
|
1943
2018
|
setupArgs.push("--agent-prefix", effectiveRuntimeConfig.agent_prefix);
|
|
@@ -1945,35 +2020,67 @@ async function configureOpenClawPlugin({
|
|
|
1945
2020
|
if (effectiveRuntimeConfig.accountId) {
|
|
1946
2021
|
setupArgs.push("--account-id", effectiveRuntimeConfig.accountId);
|
|
1947
2022
|
}
|
|
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
|
-
|
|
2023
|
+
if (effectiveRuntimeConfig.userId) {
|
|
2024
|
+
setupArgs.push("--user-id", effectiveRuntimeConfig.userId);
|
|
2025
|
+
}
|
|
2026
|
+
if (forceSlotExplicit) {
|
|
2027
|
+
setupArgs.push("--force-slot");
|
|
2028
|
+
}
|
|
2029
|
+
if (allowOfflineExplicit) {
|
|
2030
|
+
setupArgs.push("--allow-offline");
|
|
2031
|
+
}
|
|
2032
|
+
setupArgs.push(...extraArgs);
|
|
2033
|
+
|
|
2034
|
+
const result = await runCapture("openclaw", setupArgs, { env: ocEnv, shell: IS_WIN });
|
|
2035
|
+
return {
|
|
2036
|
+
result,
|
|
2037
|
+
parsed: parseJsonObjectFromOutput(`${result.out || ""}\n${result.err || ""}`),
|
|
2038
|
+
};
|
|
2039
|
+
};
|
|
2040
|
+
|
|
2041
|
+
if (setupJsonSupported) {
|
|
2042
|
+
info(tr(
|
|
2043
|
+
"Delegating configuration to: openclaw openviking setup --json",
|
|
2044
|
+
"委托配置给: openclaw openviking setup --json",
|
|
2045
|
+
));
|
|
2046
|
+
|
|
2047
|
+
({ result: setupResult, parsed } = await runSetupJson());
|
|
2048
|
+
} else {
|
|
2049
|
+
info(tr(
|
|
2050
|
+
"Installed plugin does not support setup --json, using direct config write",
|
|
2051
|
+
"已安装的插件不支持 setup --json,使用直接配置写入",
|
|
2052
|
+
));
|
|
1974
2053
|
}
|
|
1975
2054
|
|
|
1976
|
-
if (parsed) {
|
|
2055
|
+
if (parsed && !parsed.success && !nonInteractive) {
|
|
2056
|
+
if (parsed.action === "slot_blocked" && !forceSlotExplicit) {
|
|
2057
|
+
const answer = await question(
|
|
2058
|
+
tr(
|
|
2059
|
+
`contextEngine slot is owned by "${parsed.slot?.previousOwner}". Replace it with OpenViking? (y/N)`,
|
|
2060
|
+
`contextEngine slot is owned by "${parsed.slot?.previousOwner}". Replace it with OpenViking? (y/N)`,
|
|
2061
|
+
),
|
|
2062
|
+
);
|
|
2063
|
+
if (isYes(answer)) {
|
|
2064
|
+
({ result: setupResult, parsed } = await runSetupJson(["--force-slot"]));
|
|
2065
|
+
}
|
|
2066
|
+
} else if (
|
|
2067
|
+
typeof parsed.error === "string" &&
|
|
2068
|
+
parsed.error.includes("Server unreachable") &&
|
|
2069
|
+
!allowOfflineExplicit
|
|
2070
|
+
) {
|
|
2071
|
+
const answer = await question(
|
|
2072
|
+
tr(
|
|
2073
|
+
"OpenViking server is unreachable. Save config offline anyway? (y/N)",
|
|
2074
|
+
"OpenViking server is unreachable. Save config offline anyway? (y/N)",
|
|
2075
|
+
),
|
|
2076
|
+
);
|
|
2077
|
+
if (isYes(answer)) {
|
|
2078
|
+
({ result: setupResult, parsed } = await runSetupJson(["--allow-offline"]));
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
if (parsed) {
|
|
1977
2084
|
if (parsed.success) {
|
|
1978
2085
|
info(tr("OpenClaw plugin configured via setup", "OpenClaw 插件通过 setup 配置完成"));
|
|
1979
2086
|
if (parsed.health?.ok) {
|
|
@@ -1991,32 +2098,37 @@ async function configureOpenClawPlugin({
|
|
|
1991
2098
|
if (parsed.slot?.activated) {
|
|
1992
2099
|
info(tr(`contextEngine slot activated`, `contextEngine slot 已激活`));
|
|
1993
2100
|
}
|
|
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
|
-
|
|
2101
|
+
} else {
|
|
2102
|
+
// Setup returned success: false
|
|
2103
|
+
const setupError = parsed.error || parsed.action || "unknown error";
|
|
2104
|
+
if (parsed.action === "slot_blocked") {
|
|
2105
|
+
warn(tr(
|
|
2106
|
+
`Config saved but contextEngine slot is owned by "${parsed.slot?.previousOwner}". Use --force-slot to override.`,
|
|
2107
|
+
`配置已保存,但 contextEngine slot 被 "${parsed.slot?.previousOwner}" 占用。使用 --force-slot 覆盖。`,
|
|
2108
|
+
));
|
|
2109
|
+
} else {
|
|
2110
|
+
err(tr(
|
|
2111
|
+
`Setup failed: ${setupError}`,
|
|
2112
|
+
`配置失败: ${setupError}`,
|
|
2113
|
+
));
|
|
2114
|
+
}
|
|
2115
|
+
return {
|
|
2116
|
+
runtimeConfigOk: false,
|
|
2117
|
+
error: setupError,
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
} else if (setupJsonSupported) {
|
|
2121
|
+
const setupError = setupResult
|
|
2122
|
+
? `openclaw openviking setup did not return JSON (exit code ${setupResult.code})`
|
|
2123
|
+
: "openclaw openviking setup did not run";
|
|
2124
|
+
err(tr(`Setup failed: ${setupError}`, `配置失败: ${setupError}`));
|
|
2125
|
+
return {
|
|
2126
|
+
runtimeConfigOk: false,
|
|
2127
|
+
error: setupError,
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
if (!setupJsonSupported) {
|
|
2020
2132
|
// Direct write: only used when the installed plugin doesn't support `setup --json` (old version).
|
|
2021
2133
|
// Read the deployed configSchema to determine which fields are allowed, avoiding
|
|
2022
2134
|
// "additionalProperties" validation failures when writing new fields to old schemas.
|
|
@@ -2153,7 +2265,7 @@ async function performUninstall() {
|
|
|
2153
2265
|
tr("Confirm uninstall? (y/N)", "确认卸载?(y/N)"),
|
|
2154
2266
|
"N",
|
|
2155
2267
|
);
|
|
2156
|
-
if (
|
|
2268
|
+
if (!isYes(answer)) {
|
|
2157
2269
|
info(tr("Cancelled.", "已取消。"));
|
|
2158
2270
|
return;
|
|
2159
2271
|
}
|