openclaw-openviking-setup-helper 0.2.14 → 0.2.16-dev.0
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 +58 -844
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* OpenClaw
|
|
3
|
+
* OpenClaw OpenViking plugin installer (remote OpenViking server — does not install Python/OpenViking server).
|
|
4
4
|
*
|
|
5
5
|
* One-liner (after npm publish; use package name + bin name):
|
|
6
6
|
* npx -p openclaw-openviking-setup-helper ov-install [ -y ] [ --zh ] [ --workdir PATH ]
|
|
@@ -11,21 +11,17 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Direct run:
|
|
13
13
|
* node install.js [ -y | --yes ] [ --zh ] [ --workdir PATH ] [ --upgrade-plugin ]
|
|
14
|
-
* [ --plugin-version=TAG ]
|
|
14
|
+
* [ --plugin-version=TAG ]
|
|
15
15
|
*
|
|
16
16
|
* Environment variables:
|
|
17
|
-
* REPO, PLUGIN_VERSION (or BRANCH), OPENVIKING_INSTALL_YES, SKIP_OPENCLAW
|
|
18
|
-
*
|
|
19
|
-
* OPENVIKING_REPO Repo path: source install (pip -e) + local plugin (default: off)
|
|
20
|
-
* NPM_REGISTRY, PIP_INDEX_URL
|
|
21
|
-
* OPENVIKING_VLM_API_KEY, OPENVIKING_EMBEDDING_API_KEY, OPENVIKING_ARK_API_KEY
|
|
22
|
-
* OPENVIKING_ALLOW_BREAK_SYSTEM_PACKAGES (Linux)
|
|
17
|
+
* REPO, PLUGIN_VERSION (or BRANCH), OPENVIKING_INSTALL_YES, SKIP_OPENCLAW
|
|
18
|
+
* NPM_REGISTRY
|
|
23
19
|
*/
|
|
24
20
|
|
|
25
21
|
import { spawn } from "node:child_process";
|
|
26
22
|
import { cp, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
27
23
|
import { existsSync, readdirSync } from "node:fs";
|
|
28
|
-
import { basename, dirname, join
|
|
24
|
+
import { basename, dirname, join } from "node:path";
|
|
29
25
|
import { createInterface } from "node:readline";
|
|
30
26
|
import { fileURLToPath } from "node:url";
|
|
31
27
|
|
|
@@ -37,9 +33,6 @@ const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || ""
|
|
|
37
33
|
let PLUGIN_VERSION = pluginVersionEnv;
|
|
38
34
|
let pluginVersionExplicit = Boolean(pluginVersionEnv);
|
|
39
35
|
const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
40
|
-
const DEFAULT_PIP_INDEX_URL = "https://mirrors.volces.com/pypi/simple/";
|
|
41
|
-
const OFFICIAL_PIP_INDEX_URL = "https://pypi.org/simple/";
|
|
42
|
-
const PIP_INDEX_URL = process.env.PIP_INDEX_URL || DEFAULT_PIP_INDEX_URL;
|
|
43
36
|
|
|
44
37
|
const IS_WIN = process.platform === "win32";
|
|
45
38
|
const HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
@@ -48,13 +41,6 @@ const DEFAULT_OPENCLAW_DIR = join(HOME, ".openclaw");
|
|
|
48
41
|
let OPENCLAW_DIR = DEFAULT_OPENCLAW_DIR;
|
|
49
42
|
let PLUGIN_DEST = ""; // Will be set after resolving plugin config
|
|
50
43
|
|
|
51
|
-
const OPENVIKING_DIR = join(HOME, ".openviking");
|
|
52
|
-
|
|
53
|
-
const DEFAULT_SERVER_PORT = 1933;
|
|
54
|
-
const DEFAULT_AGFS_PORT = 1833;
|
|
55
|
-
const DEFAULT_VLM_MODEL = "doubao-seed-2-0-pro-260215";
|
|
56
|
-
const DEFAULT_EMBED_MODEL = "doubao-embedding-vision-251215";
|
|
57
|
-
|
|
58
44
|
// Fallback configs for old versions without manifest
|
|
59
45
|
const FALLBACK_LEGACY = {
|
|
60
46
|
dir: "openclaw-memory-plugin",
|
|
@@ -106,23 +92,15 @@ let resolvedPluginReleaseId = "";
|
|
|
106
92
|
|
|
107
93
|
let installYes = process.env.OPENVIKING_INSTALL_YES === "1";
|
|
108
94
|
let langZh = false;
|
|
109
|
-
let openvikingVersion = process.env.OPENVIKING_VERSION || "";
|
|
110
|
-
let openvikingRepo = process.env.OPENVIKING_REPO || "";
|
|
111
95
|
let workdirExplicit = false;
|
|
112
96
|
let upgradePluginOnly = false;
|
|
113
97
|
let rollbackLastUpgrade = false;
|
|
114
|
-
let combinedVersion = "";
|
|
115
|
-
let combinedVersionExplicit = false;
|
|
116
|
-
let pluginVersionArgExplicit = false;
|
|
117
|
-
let openvikingVersionArgExplicit = false;
|
|
118
98
|
let showCurrentVersion = false;
|
|
119
99
|
|
|
120
|
-
|
|
121
|
-
let selectedServerPort = DEFAULT_SERVER_PORT;
|
|
100
|
+
const selectedMode = "remote";
|
|
122
101
|
let remoteBaseUrl = "http://127.0.0.1:1933";
|
|
123
102
|
let remoteApiKey = "";
|
|
124
103
|
let remoteAgentId = "";
|
|
125
|
-
let openvikingPythonPath = "";
|
|
126
104
|
let upgradeRuntimeConfig = null;
|
|
127
105
|
let installedUpgradeState = null;
|
|
128
106
|
let upgradeAudit = null;
|
|
@@ -169,7 +147,6 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
169
147
|
}
|
|
170
148
|
PLUGIN_VERSION = version;
|
|
171
149
|
pluginVersionExplicit = true;
|
|
172
|
-
pluginVersionArgExplicit = true;
|
|
173
150
|
continue;
|
|
174
151
|
}
|
|
175
152
|
if (arg === "--plugin-version") {
|
|
@@ -180,51 +157,9 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
180
157
|
}
|
|
181
158
|
PLUGIN_VERSION = version;
|
|
182
159
|
pluginVersionExplicit = true;
|
|
183
|
-
pluginVersionArgExplicit = true;
|
|
184
|
-
i += 1;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (arg.startsWith("--openviking-version=")) {
|
|
188
|
-
openvikingVersion = arg.slice("--openviking-version=".length).trim();
|
|
189
|
-
openvikingVersionArgExplicit = true;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (arg === "--openviking-version") {
|
|
193
|
-
const version = argv[i + 1]?.trim();
|
|
194
|
-
if (!version) {
|
|
195
|
-
console.error("--openviking-version requires a value");
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
|
-
openvikingVersion = version;
|
|
199
|
-
openvikingVersionArgExplicit = true;
|
|
200
|
-
i += 1;
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (arg.startsWith("--version=")) {
|
|
204
|
-
const version = arg.slice("--version=".length).trim();
|
|
205
|
-
if (!version) {
|
|
206
|
-
console.error("--version requires a value");
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
209
|
-
combinedVersion = version;
|
|
210
|
-
combinedVersionExplicit = true;
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (arg === "--version") {
|
|
214
|
-
const version = argv[i + 1]?.trim();
|
|
215
|
-
if (!version) {
|
|
216
|
-
console.error("--version requires a value");
|
|
217
|
-
process.exit(1);
|
|
218
|
-
}
|
|
219
|
-
combinedVersion = version;
|
|
220
|
-
combinedVersionExplicit = true;
|
|
221
160
|
i += 1;
|
|
222
161
|
continue;
|
|
223
162
|
}
|
|
224
|
-
if (arg.startsWith("--repo=")) {
|
|
225
|
-
openvikingRepo = arg.slice("--repo=".length).trim();
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
163
|
if (arg.startsWith("--github-repo=")) {
|
|
229
164
|
REPO = arg.slice("--github-repo=".length).trim();
|
|
230
165
|
continue;
|
|
@@ -245,74 +180,20 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
245
180
|
}
|
|
246
181
|
}
|
|
247
182
|
|
|
248
|
-
if (combinedVersionExplicit) {
|
|
249
|
-
if (pluginVersionArgExplicit || openvikingVersionArgExplicit) {
|
|
250
|
-
console.error("--version cannot be used together with --plugin-version or --openviking-version");
|
|
251
|
-
process.exit(1);
|
|
252
|
-
}
|
|
253
|
-
const normalizedVersion = normalizeCombinedVersion(combinedVersion);
|
|
254
|
-
PLUGIN_VERSION = normalizedVersion.pluginVersion;
|
|
255
|
-
openvikingVersion = normalizedVersion.openvikingVersion;
|
|
256
|
-
pluginVersionExplicit = true;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
183
|
function setOpenClawDir(dir) {
|
|
260
184
|
OPENCLAW_DIR = dir;
|
|
261
185
|
}
|
|
262
186
|
|
|
263
|
-
function normalizeCombinedVersion(version) {
|
|
264
|
-
const value = (version || "").trim();
|
|
265
|
-
if (!/^(v)?\d+(\.\d+){1,2}$/.test(value)) {
|
|
266
|
-
console.error("--version requires a semantic version like 0.2.9 or v0.2.9");
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
const openvikingVersionValue = value.startsWith("v") ? value.slice(1) : value;
|
|
270
|
-
return {
|
|
271
|
-
pluginVersion: `v${openvikingVersionValue}`,
|
|
272
|
-
openvikingVersion: openvikingVersionValue,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function deriveOpenvikingVersionFromPluginVersion(version) {
|
|
277
|
-
const value = (version || "").trim();
|
|
278
|
-
if (!isSemverLike(value)) {
|
|
279
|
-
return "";
|
|
280
|
-
}
|
|
281
|
-
return value.startsWith("v") ? value.slice(1) : value;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function syncOpenvikingVersionWithPluginVersion(reason = "") {
|
|
285
|
-
if (openvikingVersion) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const derivedVersion = deriveOpenvikingVersionFromPluginVersion(PLUGIN_VERSION);
|
|
290
|
-
if (!derivedVersion) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
openvikingVersion = derivedVersion;
|
|
295
|
-
info(tr(
|
|
296
|
-
`No OpenViking version specified; syncing runtime version to plugin version ${PLUGIN_VERSION}${reason ? ` (${reason})` : ""}.`,
|
|
297
|
-
`未指定 OpenViking 版本;已将运行时版本同步为插件版本 ${PLUGIN_VERSION}${reason ? `(${reason})` : ""}。`,
|
|
298
|
-
));
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
syncOpenvikingVersionWithPluginVersion("requested plugin version");
|
|
302
|
-
|
|
303
187
|
function printHelp() {
|
|
304
188
|
console.log("Usage: node install.js [ OPTIONS ]");
|
|
305
189
|
console.log("");
|
|
306
190
|
console.log("Options:");
|
|
307
191
|
console.log(" --github-repo=OWNER/REPO GitHub repository (default: volcengine/OpenViking)");
|
|
308
|
-
console.log(" --version=V Shorthand for --plugin-version=vV and --openviking-version=V");
|
|
309
192
|
console.log(" --plugin-version=TAG Plugin version (Git tag, e.g. v0.2.9, default: latest tag)");
|
|
310
|
-
console.log(" --openviking-version=V OpenViking PyPI version (e.g. 0.2.9, default: match plugin release version)");
|
|
311
193
|
console.log(" --workdir PATH OpenClaw config directory (default: ~/.openclaw)");
|
|
312
|
-
console.log(" --current-version Print installed plugin
|
|
313
|
-
console.log(" --repo=PATH Use local OpenViking repo at PATH (pip -e + local plugin)");
|
|
194
|
+
console.log(" --current-version Print installed plugin version and exit");
|
|
314
195
|
console.log(" --update, --upgrade-plugin");
|
|
315
|
-
console.log(" Upgrade only the plugin to the requested --plugin-version;
|
|
196
|
+
console.log(" Upgrade only the plugin to the requested --plugin-version; keeps existing plugin runtime config");
|
|
316
197
|
console.log(" --rollback, --rollback-last-upgrade");
|
|
317
198
|
console.log(" Roll back the last plugin upgrade using the saved audit/backup files");
|
|
318
199
|
console.log(" -y, --yes Non-interactive (use defaults)");
|
|
@@ -327,7 +208,7 @@ function printHelp() {
|
|
|
327
208
|
console.log(" node install.js --current-version");
|
|
328
209
|
console.log("");
|
|
329
210
|
console.log(" # Install a specific release version");
|
|
330
|
-
console.log(" node install.js --version=
|
|
211
|
+
console.log(" node install.js --plugin-version=v0.2.9");
|
|
331
212
|
console.log("");
|
|
332
213
|
console.log(" # Install from a fork repository");
|
|
333
214
|
console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
|
|
@@ -341,7 +222,7 @@ function printHelp() {
|
|
|
341
222
|
console.log(" # Roll back the last plugin upgrade");
|
|
342
223
|
console.log(" node install.js --rollback");
|
|
343
224
|
console.log("");
|
|
344
|
-
console.log("Env: REPO, PLUGIN_VERSION,
|
|
225
|
+
console.log("Env: REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
|
|
345
226
|
}
|
|
346
227
|
|
|
347
228
|
function formatCliArg(value) {
|
|
@@ -435,62 +316,6 @@ function runCapture(cmd, args, opts = {}) {
|
|
|
435
316
|
});
|
|
436
317
|
}
|
|
437
318
|
|
|
438
|
-
function runLiveCapture(cmd, args, opts = {}) {
|
|
439
|
-
return new Promise((resolve) => {
|
|
440
|
-
const child = spawn(cmd, args, {
|
|
441
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
442
|
-
shell: opts.shell ?? false,
|
|
443
|
-
...opts,
|
|
444
|
-
});
|
|
445
|
-
let out = "";
|
|
446
|
-
let errOut = "";
|
|
447
|
-
child.stdout?.on("data", (chunk) => {
|
|
448
|
-
const text = String(chunk);
|
|
449
|
-
out += text;
|
|
450
|
-
process.stdout.write(text);
|
|
451
|
-
});
|
|
452
|
-
child.stderr?.on("data", (chunk) => {
|
|
453
|
-
const text = String(chunk);
|
|
454
|
-
errOut += text;
|
|
455
|
-
process.stderr.write(text);
|
|
456
|
-
});
|
|
457
|
-
child.on("error", (error) => {
|
|
458
|
-
resolve({ code: -1, out: "", err: String(error) });
|
|
459
|
-
});
|
|
460
|
-
child.on("close", (code) => {
|
|
461
|
-
resolve({ code, out: out.trim(), err: errOut.trim() });
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function shouldFallbackToOfficialPypi(output) {
|
|
467
|
-
if (process.env.PIP_INDEX_URL?.trim()) return false;
|
|
468
|
-
if (PIP_INDEX_URL !== DEFAULT_PIP_INDEX_URL) return false;
|
|
469
|
-
|
|
470
|
-
return /Could not find a version that satisfies the requirement|No matching distribution found|HTTP error 404|too many 502 error responses|Temporary failure in name resolution|Failed to establish a new connection|Connection (?:timed out|reset by peer)|Read timed out|ProxyError|SSLError|TLSV1_ALERT|Remote end closed connection/i.test(output);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
async function runPipInstallWithFallback(py, pipArgs, opts = {}) {
|
|
474
|
-
const shell = opts.shell ?? false;
|
|
475
|
-
const primaryResult = await runLiveCapture(py, [...pipArgs, "-i", PIP_INDEX_URL], { shell });
|
|
476
|
-
if (primaryResult.code === 0) {
|
|
477
|
-
return { result: primaryResult, usedFallback: false };
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const primaryOutput = `${primaryResult.out}\n${primaryResult.err}`;
|
|
481
|
-
if (!shouldFallbackToOfficialPypi(primaryOutput)) {
|
|
482
|
-
return { result: primaryResult, usedFallback: false };
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
warn(tr(
|
|
486
|
-
`Install from mirror failed. Retrying with official PyPI: ${OFFICIAL_PIP_INDEX_URL}`,
|
|
487
|
-
`镜像源安装失败,正在回退到官方 PyPI: ${OFFICIAL_PIP_INDEX_URL}`,
|
|
488
|
-
));
|
|
489
|
-
|
|
490
|
-
const fallbackResult = await runLiveCapture(py, [...pipArgs, "-i", OFFICIAL_PIP_INDEX_URL], { shell });
|
|
491
|
-
return { result: fallbackResult, usedFallback: true, primaryResult, fallbackResult };
|
|
492
|
-
}
|
|
493
|
-
|
|
494
319
|
function question(prompt, defaultValue = "") {
|
|
495
320
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
496
321
|
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
@@ -502,50 +327,6 @@ function question(prompt, defaultValue = "") {
|
|
|
502
327
|
});
|
|
503
328
|
}
|
|
504
329
|
|
|
505
|
-
async function resolveAbsoluteCommand(cmd) {
|
|
506
|
-
if (cmd.startsWith("/") || (IS_WIN && /^[A-Za-z]:[/\\]/.test(cmd))) return cmd;
|
|
507
|
-
if (IS_WIN) {
|
|
508
|
-
const r = await runCapture("where", [cmd], { shell: true });
|
|
509
|
-
return r.out.split(/\r?\n/)[0]?.trim() || cmd;
|
|
510
|
-
}
|
|
511
|
-
const r = await runCapture("which", [cmd], { shell: false });
|
|
512
|
-
return r.out.trim() || cmd;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
async function checkPython() {
|
|
516
|
-
const raw = process.env.OPENVIKING_PYTHON || (IS_WIN ? "python" : "python3");
|
|
517
|
-
const py = await resolveAbsoluteCommand(raw);
|
|
518
|
-
const result = await runCapture(py, ["-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"]);
|
|
519
|
-
if (result.code !== 0 || !result.out) {
|
|
520
|
-
return {
|
|
521
|
-
ok: false,
|
|
522
|
-
detail: tr("Python not found or failed. Install Python >= 3.10.", "Python 未找到或执行失败,请安装 Python >= 3.10"),
|
|
523
|
-
cmd: py,
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
const [major, minor] = result.out.split(".").map(Number);
|
|
527
|
-
if (major < 3 || (major === 3 && minor < 10)) {
|
|
528
|
-
return {
|
|
529
|
-
ok: false,
|
|
530
|
-
detail: tr(`Python ${result.out} is too old. Need >= 3.10.`, `Python ${result.out} 版本过低,需要 >= 3.10`),
|
|
531
|
-
cmd: py,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
return { ok: true, detail: result.out, cmd: py };
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
async function checkNode() {
|
|
538
|
-
const result = await runCapture("node", ["-v"], { shell: IS_WIN });
|
|
539
|
-
if (result.code !== 0 || !result.out) {
|
|
540
|
-
return { ok: false, detail: tr("Node.js not found. Install Node.js >= 22.", "Node.js 未找到,请安装 Node.js >= 22") };
|
|
541
|
-
}
|
|
542
|
-
const major = Number.parseInt(result.out.replace(/^v/, "").split(".")[0], 10);
|
|
543
|
-
if (!Number.isFinite(major) || major < 22) {
|
|
544
|
-
return { ok: false, detail: tr(`Node.js ${result.out} is too old. Need >= 22.`, `Node.js ${result.out} 版本过低,需要 >= 22`) };
|
|
545
|
-
}
|
|
546
|
-
return { ok: true, detail: result.out };
|
|
547
|
-
}
|
|
548
|
-
|
|
549
330
|
function detectOpenClawInstances() {
|
|
550
331
|
const instances = [];
|
|
551
332
|
try {
|
|
@@ -588,15 +369,6 @@ async function selectWorkdir() {
|
|
|
588
369
|
}
|
|
589
370
|
}
|
|
590
371
|
|
|
591
|
-
async function selectMode() {
|
|
592
|
-
if (installYes) {
|
|
593
|
-
selectedMode = "local";
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
const mode = (await question(tr("Plugin mode - local or remote", "插件模式 - local 或 remote"), "local")).toLowerCase();
|
|
597
|
-
selectedMode = mode === "remote" ? "remote" : "local";
|
|
598
|
-
}
|
|
599
|
-
|
|
600
372
|
async function collectRemoteConfig() {
|
|
601
373
|
if (installYes) return;
|
|
602
374
|
remoteBaseUrl = await question(tr("OpenViking server URL", "OpenViking 服务器地址"), remoteBaseUrl);
|
|
@@ -604,50 +376,6 @@ async function collectRemoteConfig() {
|
|
|
604
376
|
remoteAgentId = await question(tr("Agent ID (optional)", "Agent ID(可选)"), remoteAgentId);
|
|
605
377
|
}
|
|
606
378
|
|
|
607
|
-
async function validateEnvironment() {
|
|
608
|
-
info(tr("Checking OpenViking runtime environment...", "正在校验 OpenViking 运行环境..."));
|
|
609
|
-
console.log("");
|
|
610
|
-
|
|
611
|
-
const missing = [];
|
|
612
|
-
|
|
613
|
-
const python = await checkPython();
|
|
614
|
-
if (python.ok) {
|
|
615
|
-
info(` Python: ${python.detail} ✓`);
|
|
616
|
-
} else {
|
|
617
|
-
missing.push(`Python 3.10+ | ${python.detail}`);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
const node = await checkNode();
|
|
621
|
-
if (node.ok) {
|
|
622
|
-
info(` Node.js: ${node.detail} ✓`);
|
|
623
|
-
} else {
|
|
624
|
-
missing.push(`Node.js 22+ | ${node.detail}`);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (missing.length > 0) {
|
|
628
|
-
console.log("");
|
|
629
|
-
err(tr("Environment check failed. Install missing dependencies first.", "环境校验未通过,请先安装以下缺失组件。"));
|
|
630
|
-
console.log("");
|
|
631
|
-
if (missing.some((item) => item.startsWith("Python"))) {
|
|
632
|
-
console.log(tr("Python (example):", "Python(示例):"));
|
|
633
|
-
if (IS_WIN) console.log(" winget install --id Python.Python.3.11 -e");
|
|
634
|
-
else console.log(" pyenv install 3.11.12 && pyenv global 3.11.12");
|
|
635
|
-
console.log("");
|
|
636
|
-
}
|
|
637
|
-
if (missing.some((item) => item.startsWith("Node"))) {
|
|
638
|
-
console.log(tr("Node.js (example):", "Node.js(示例):"));
|
|
639
|
-
if (IS_WIN) console.log(" nvm install 22.22.0 && nvm use 22.22.0");
|
|
640
|
-
else console.log(" nvm install 22 && nvm use 22");
|
|
641
|
-
console.log("");
|
|
642
|
-
}
|
|
643
|
-
process.exit(1);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
console.log("");
|
|
647
|
-
info(tr("Environment check passed ✓", "环境校验通过 ✓"));
|
|
648
|
-
console.log("");
|
|
649
|
-
}
|
|
650
|
-
|
|
651
379
|
async function checkOpenClaw() {
|
|
652
380
|
if (process.env.SKIP_OPENCLAW === "1") {
|
|
653
381
|
info(tr("Skipping OpenClaw check (SKIP_OPENCLAW=1)", "跳过 OpenClaw 校验 (SKIP_OPENCLAW=1)"));
|
|
@@ -704,18 +432,6 @@ if (upgradePluginOnly && rollbackLastUpgrade) {
|
|
|
704
432
|
process.exit(1);
|
|
705
433
|
}
|
|
706
434
|
|
|
707
|
-
function ensurePluginOnlyOperationArgs() {
|
|
708
|
-
if ((upgradePluginOnly || rollbackLastUpgrade) && openvikingVersion) {
|
|
709
|
-
err(
|
|
710
|
-
tr(
|
|
711
|
-
"Plugin-only upgrade/rollback does not support --openviking-version or --version. Use --plugin-version to choose the plugin release, and run a full install if you need to change the OpenViking service version.",
|
|
712
|
-
"仅插件升级或回滚不支持 --openviking-version 或 --version。请使用 --plugin-version 指定插件版本;如果需要调整 OpenViking 服务版本,请执行完整安装流程。",
|
|
713
|
-
),
|
|
714
|
-
);
|
|
715
|
-
process.exit(1);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
435
|
// Detect OpenClaw version
|
|
720
436
|
async function detectOpenClawVersion() {
|
|
721
437
|
try {
|
|
@@ -819,7 +535,6 @@ async function resolveDefaultPluginVersion() {
|
|
|
819
535
|
const latestTag = pickLatestPluginTag(payload.map((item) => item?.name || ""));
|
|
820
536
|
if (latestTag) {
|
|
821
537
|
PLUGIN_VERSION = latestTag;
|
|
822
|
-
syncOpenvikingVersionWithPluginVersion("latest plugin tag");
|
|
823
538
|
info(tr(
|
|
824
539
|
`Resolved default plugin version to latest tag: ${PLUGIN_VERSION}`,
|
|
825
540
|
`已将默认插件版本解析为最新 tag: ${PLUGIN_VERSION}`,
|
|
@@ -844,7 +559,6 @@ async function resolveDefaultPluginVersion() {
|
|
|
844
559
|
const latestTag = pickLatestPluginTag(parseGitLsRemoteTags(gitResult.out));
|
|
845
560
|
if (latestTag) {
|
|
846
561
|
PLUGIN_VERSION = latestTag;
|
|
847
|
-
syncOpenvikingVersionWithPluginVersion("latest plugin tag");
|
|
848
562
|
info(tr(
|
|
849
563
|
`Resolved default plugin version via git tags: ${PLUGIN_VERSION}`,
|
|
850
564
|
`已通过 git tag 解析默认插件版本: ${PLUGIN_VERSION}`,
|
|
@@ -1016,243 +730,6 @@ async function checkOpenClawCompatibility() {
|
|
|
1016
730
|
}
|
|
1017
731
|
}
|
|
1018
732
|
|
|
1019
|
-
function checkRequestedOpenVikingCompatibility() {
|
|
1020
|
-
if (!resolvedMinOpenvikingVersion || !openvikingVersion) return;
|
|
1021
|
-
if (versionGte(openvikingVersion, resolvedMinOpenvikingVersion)) return;
|
|
1022
|
-
|
|
1023
|
-
err(tr(
|
|
1024
|
-
`OpenViking ${openvikingVersion} does not support this plugin (requires >= ${resolvedMinOpenvikingVersion})`,
|
|
1025
|
-
`OpenViking ${openvikingVersion} 不支持此插件(需要 >= ${resolvedMinOpenvikingVersion})`,
|
|
1026
|
-
));
|
|
1027
|
-
console.log("");
|
|
1028
|
-
console.log(tr(
|
|
1029
|
-
"Use a newer OpenViking version, or omit --openviking-version to install the latest release.",
|
|
1030
|
-
"请使用更新版本的 OpenViking,或省略 --openviking-version 以安装最新版本。",
|
|
1031
|
-
));
|
|
1032
|
-
process.exit(1);
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
async function installOpenViking() {
|
|
1036
|
-
if (process.env.SKIP_OPENVIKING === "1") {
|
|
1037
|
-
info(tr("Skipping OpenViking install (SKIP_OPENVIKING=1)", "跳过 OpenViking 安装 (SKIP_OPENVIKING=1)"));
|
|
1038
|
-
return;
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
const python = await checkPython();
|
|
1042
|
-
if (!python.cmd) {
|
|
1043
|
-
err(tr("Python check failed.", "Python 校验失败"));
|
|
1044
|
-
process.exit(1);
|
|
1045
|
-
}
|
|
1046
|
-
if (!python.ok) {
|
|
1047
|
-
warn(tr(
|
|
1048
|
-
`${python.detail}. Will attempt to find a suitable Python for pip install.`,
|
|
1049
|
-
`${python.detail}。将尝试查找合适的 Python 进行 pip 安装。`,
|
|
1050
|
-
));
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
const py = python.cmd;
|
|
1054
|
-
|
|
1055
|
-
if (openvikingRepo && existsSync(join(openvikingRepo, "pyproject.toml"))) {
|
|
1056
|
-
info(tr(`Installing OpenViking from source (editable): ${openvikingRepo}`, `正在从源码安装 OpenViking(可编辑): ${openvikingRepo}`));
|
|
1057
|
-
await run(py, ["-m", "pip", "install", "--upgrade", "pip", "-q", "-i", PIP_INDEX_URL], { silent: true });
|
|
1058
|
-
await run(py, ["-m", "pip", "install", "-e", openvikingRepo]);
|
|
1059
|
-
openvikingPythonPath = py;
|
|
1060
|
-
info(tr("OpenViking installed ✓ (source)", "OpenViking 安装完成 ✓(源码)"));
|
|
1061
|
-
return;
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
// Determine package spec
|
|
1065
|
-
const pkgSpec = openvikingVersion ? `openviking==${openvikingVersion}` : "openviking";
|
|
1066
|
-
if (openvikingVersion) {
|
|
1067
|
-
info(tr(`Installing or upgrading OpenViking ${openvikingVersion} from PyPI...`, `正在安装或升级 OpenViking ${openvikingVersion} (PyPI)...`));
|
|
1068
|
-
} else {
|
|
1069
|
-
info(tr("Installing or upgrading OpenViking (latest) from PyPI...", "正在安装或升级 OpenViking (最新版) (PyPI)..."));
|
|
1070
|
-
}
|
|
1071
|
-
info(tr(`Using pip index: ${PIP_INDEX_URL}`, `使用 pip 镜像源: ${PIP_INDEX_URL}`));
|
|
1072
|
-
|
|
1073
|
-
info(`Package: ${pkgSpec}`);
|
|
1074
|
-
await runCapture(py, ["-m", "pip", "install", "--upgrade", "pip", "-q", "-i", PIP_INDEX_URL], { shell: false });
|
|
1075
|
-
const { result: installResult } = await runPipInstallWithFallback(
|
|
1076
|
-
py,
|
|
1077
|
-
["-m", "pip", "install", "--upgrade", "--progress-bar", "on", pkgSpec],
|
|
1078
|
-
{ shell: false },
|
|
1079
|
-
);
|
|
1080
|
-
if (installResult.code === 0) {
|
|
1081
|
-
openvikingPythonPath = py;
|
|
1082
|
-
info(tr("OpenViking installed ✓", "OpenViking 安装完成 ✓"));
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
const installOutput = `${installResult.out}\n${installResult.err}`;
|
|
1087
|
-
const shouldTryVenv = !IS_WIN && /externally-managed-environment|externally managed|No module named pip/i.test(installOutput);
|
|
1088
|
-
if (shouldTryVenv) {
|
|
1089
|
-
const venvDir = join(OPENVIKING_DIR, "venv");
|
|
1090
|
-
const venvPy = IS_WIN ? join(venvDir, "Scripts", "python.exe") : join(venvDir, "bin", "python");
|
|
1091
|
-
|
|
1092
|
-
if (existsSync(venvPy)) {
|
|
1093
|
-
const reuseCheck = await runCapture(venvPy, ["-c", "import openviking"], { shell: false });
|
|
1094
|
-
if (reuseCheck.code === 0) {
|
|
1095
|
-
const { result: venvReuseInstall } = await runPipInstallWithFallback(
|
|
1096
|
-
venvPy,
|
|
1097
|
-
["-m", "pip", "install", "--progress-bar", "on", "-U", pkgSpec],
|
|
1098
|
-
{ shell: false },
|
|
1099
|
-
);
|
|
1100
|
-
if (venvReuseInstall.code !== 0) {
|
|
1101
|
-
err(tr("OpenViking install failed in venv.", "在虚拟环境中安装 OpenViking 失败。"));
|
|
1102
|
-
console.log(venvReuseInstall.err || venvReuseInstall.out);
|
|
1103
|
-
process.exit(1);
|
|
1104
|
-
}
|
|
1105
|
-
openvikingPythonPath = venvPy;
|
|
1106
|
-
info(tr("OpenViking installed ✓ (venv)", "OpenViking 安装完成 ✓(虚拟环境)"));
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
await mkdir(OPENVIKING_DIR, { recursive: true });
|
|
1112
|
-
const venvCreate = await runCapture(py, ["-m", "venv", venvDir], { shell: false });
|
|
1113
|
-
if (venvCreate.code !== 0) {
|
|
1114
|
-
console.log("");
|
|
1115
|
-
err(tr("Cannot create Python virtual environment.", "无法创建 Python 虚拟环境。"));
|
|
1116
|
-
console.log(tr(
|
|
1117
|
-
" python3-venv is not installed. Fix with:",
|
|
1118
|
-
" python3-venv 未安装,请执行以下命令修复:"
|
|
1119
|
-
));
|
|
1120
|
-
console.log(`
|
|
1121
|
-
apt update
|
|
1122
|
-
apt install -y software-properties-common
|
|
1123
|
-
add-apt-repository universe
|
|
1124
|
-
apt update
|
|
1125
|
-
apt install -y python3-venv
|
|
1126
|
-
`);
|
|
1127
|
-
console.log(tr(
|
|
1128
|
-
" Or force install into system Python (not recommended):",
|
|
1129
|
-
" 或强制安装到系统 Python(不推荐):"
|
|
1130
|
-
));
|
|
1131
|
-
console.log(` OPENVIKING_ALLOW_BREAK_SYSTEM_PACKAGES=1 ov-install\n`);
|
|
1132
|
-
process.exit(1);
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
await runCapture(venvPy, ["-m", "pip", "install", "--upgrade", "pip", "-q", "-i", PIP_INDEX_URL], { shell: false });
|
|
1136
|
-
const { result: venvInstall } = await runPipInstallWithFallback(
|
|
1137
|
-
venvPy,
|
|
1138
|
-
["-m", "pip", "install", "--upgrade", "--progress-bar", "on", pkgSpec],
|
|
1139
|
-
{ shell: false },
|
|
1140
|
-
);
|
|
1141
|
-
if (venvInstall.code === 0) {
|
|
1142
|
-
openvikingPythonPath = venvPy;
|
|
1143
|
-
info(tr("OpenViking installed ✓ (venv)", "OpenViking 安装完成 ✓(虚拟环境)"));
|
|
1144
|
-
return;
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
err(tr("OpenViking install failed in venv.", "在虚拟环境中安装 OpenViking 失败。"));
|
|
1148
|
-
console.log(venvInstall.err || venvInstall.out);
|
|
1149
|
-
process.exit(1);
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
if (process.env.OPENVIKING_ALLOW_BREAK_SYSTEM_PACKAGES === "1") {
|
|
1153
|
-
const { result: systemInstall } = await runPipInstallWithFallback(
|
|
1154
|
-
py,
|
|
1155
|
-
["-m", "pip", "install", "--upgrade", "--progress-bar", "on", "--break-system-packages", pkgSpec],
|
|
1156
|
-
{ shell: false },
|
|
1157
|
-
);
|
|
1158
|
-
if (systemInstall.code === 0) {
|
|
1159
|
-
openvikingPythonPath = py;
|
|
1160
|
-
info(tr("OpenViking installed ✓ (system)", "OpenViking 安装完成 ✓(系统)"));
|
|
1161
|
-
return;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
err(tr("OpenViking install failed. Check Python >= 3.10 and pip.", "OpenViking 安装失败,请检查 Python >= 3.10 及 pip"));
|
|
1166
|
-
console.log(installResult.err || installResult.out);
|
|
1167
|
-
process.exit(1);
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
async function configureOvConf() {
|
|
1171
|
-
await mkdir(OPENVIKING_DIR, { recursive: true });
|
|
1172
|
-
|
|
1173
|
-
const configPath = join(OPENVIKING_DIR, "ov.conf");
|
|
1174
|
-
if (installYes && existsSync(configPath)) {
|
|
1175
|
-
selectedServerPort = await readPortFromOvConf(configPath) || DEFAULT_SERVER_PORT;
|
|
1176
|
-
info(
|
|
1177
|
-
tr(
|
|
1178
|
-
`Preserved existing config: ${configPath}`,
|
|
1179
|
-
`已保留现有配置: ${configPath}`,
|
|
1180
|
-
),
|
|
1181
|
-
);
|
|
1182
|
-
return;
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
let workspace = join(OPENVIKING_DIR, "data");
|
|
1186
|
-
let serverPort = String(DEFAULT_SERVER_PORT);
|
|
1187
|
-
let agfsPort = String(DEFAULT_AGFS_PORT);
|
|
1188
|
-
let vlmModel = DEFAULT_VLM_MODEL;
|
|
1189
|
-
let embeddingModel = DEFAULT_EMBED_MODEL;
|
|
1190
|
-
let vlmApiKey = process.env.OPENVIKING_VLM_API_KEY || process.env.OPENVIKING_ARK_API_KEY || "";
|
|
1191
|
-
let embeddingApiKey = process.env.OPENVIKING_EMBEDDING_API_KEY || process.env.OPENVIKING_ARK_API_KEY || "";
|
|
1192
|
-
|
|
1193
|
-
if (!installYes) {
|
|
1194
|
-
console.log("");
|
|
1195
|
-
workspace = await question(tr("OpenViking workspace path", "OpenViking 数据目录"), workspace);
|
|
1196
|
-
serverPort = await question(tr("OpenViking HTTP port", "OpenViking HTTP 端口"), serverPort);
|
|
1197
|
-
agfsPort = await question(tr("AGFS port", "AGFS 端口"), agfsPort);
|
|
1198
|
-
vlmModel = await question(tr("VLM model", "VLM 模型"), vlmModel);
|
|
1199
|
-
embeddingModel = await question(tr("Embedding model", "Embedding 模型"), embeddingModel);
|
|
1200
|
-
console.log(tr("VLM and Embedding API keys can differ. Leave empty to edit ov.conf later.", "说明:VLM 与 Embedding 的 API Key 可分别填写,留空可稍后在 ov.conf 修改。"));
|
|
1201
|
-
const vlmInput = await question(tr("VLM API key (optional)", "VLM API Key(可留空)"), "");
|
|
1202
|
-
const embInput = await question(tr("Embedding API key (optional)", "Embedding API Key(可留空)"), "");
|
|
1203
|
-
if (vlmInput) vlmApiKey = vlmInput;
|
|
1204
|
-
if (embInput) embeddingApiKey = embInput;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
selectedServerPort = Number.parseInt(serverPort, 10) || DEFAULT_SERVER_PORT;
|
|
1208
|
-
const agfsPortNum = Number.parseInt(agfsPort, 10) || DEFAULT_AGFS_PORT;
|
|
1209
|
-
|
|
1210
|
-
await mkdir(workspace, { recursive: true });
|
|
1211
|
-
|
|
1212
|
-
const config = {
|
|
1213
|
-
server: {
|
|
1214
|
-
host: "127.0.0.1",
|
|
1215
|
-
port: selectedServerPort,
|
|
1216
|
-
root_api_key: null,
|
|
1217
|
-
cors_origins: ["*"],
|
|
1218
|
-
},
|
|
1219
|
-
storage: {
|
|
1220
|
-
workspace,
|
|
1221
|
-
vectordb: { name: "context", backend: "local", project: "default" },
|
|
1222
|
-
agfs: { port: agfsPortNum, log_level: "warn", backend: "local", timeout: 10, retry_times: 3 },
|
|
1223
|
-
},
|
|
1224
|
-
log: {
|
|
1225
|
-
level: "WARNING",
|
|
1226
|
-
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
1227
|
-
output: "file",
|
|
1228
|
-
rotation: true,
|
|
1229
|
-
rotation_days: 3,
|
|
1230
|
-
rotation_interval: "midnight",
|
|
1231
|
-
},
|
|
1232
|
-
embedding: {
|
|
1233
|
-
dense: {
|
|
1234
|
-
provider: "volcengine",
|
|
1235
|
-
api_key: embeddingApiKey || null,
|
|
1236
|
-
model: embeddingModel,
|
|
1237
|
-
api_base: "https://ark.cn-beijing.volces.com/api/v3",
|
|
1238
|
-
dimension: 1024,
|
|
1239
|
-
input: "multimodal",
|
|
1240
|
-
},
|
|
1241
|
-
},
|
|
1242
|
-
vlm: {
|
|
1243
|
-
provider: "volcengine",
|
|
1244
|
-
api_key: vlmApiKey || null,
|
|
1245
|
-
model: vlmModel,
|
|
1246
|
-
api_base: "https://ark.cn-beijing.volces.com/api/v3",
|
|
1247
|
-
temperature: 0.1,
|
|
1248
|
-
max_retries: 3,
|
|
1249
|
-
},
|
|
1250
|
-
};
|
|
1251
|
-
|
|
1252
|
-
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1253
|
-
info(tr(`Config generated: ${configPath}`, `已生成配置: ${configPath}`));
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
733
|
function getOpenClawConfigPath() {
|
|
1257
734
|
return join(OPENCLAW_DIR, "openclaw.json");
|
|
1258
735
|
}
|
|
@@ -1274,51 +751,11 @@ function getInstallStatePathForPlugin(pluginId) {
|
|
|
1274
751
|
return join(OPENCLAW_DIR, "extensions", pluginId, ".ov-install-state.json");
|
|
1275
752
|
}
|
|
1276
753
|
|
|
1277
|
-
async function detectInstalledOpenVikingVersion() {
|
|
1278
|
-
const pythonCandidates = [];
|
|
1279
|
-
const configuredPython = process.env.OPENVIKING_PYTHON?.trim();
|
|
1280
|
-
if (configuredPython) {
|
|
1281
|
-
pythonCandidates.push(configuredPython);
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
const envCandidates = IS_WIN
|
|
1285
|
-
? [join(OPENCLAW_DIR, "openviking.env.ps1"), join(OPENCLAW_DIR, "openviking.env.bat")]
|
|
1286
|
-
: [join(OPENCLAW_DIR, "openviking.env")];
|
|
1287
|
-
|
|
1288
|
-
for (const envPath of envCandidates) {
|
|
1289
|
-
if (!existsSync(envPath)) continue;
|
|
1290
|
-
const raw = await readFile(envPath, "utf8").catch(() => "");
|
|
1291
|
-
if (!raw) continue;
|
|
1292
|
-
const match = raw.match(/OPENVIKING_PYTHON(?:\s*=\s*|=)['"]?([^'"\r\n]+)['"]?/);
|
|
1293
|
-
const pythonPath = match?.[1]?.trim();
|
|
1294
|
-
if (pythonPath) {
|
|
1295
|
-
pythonCandidates.push(pythonPath);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
for (const candidate of IS_WIN ? ["py", "python", "python3"] : ["python3", "python"]) {
|
|
1300
|
-
pythonCandidates.push(candidate);
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
const seen = new Set();
|
|
1304
|
-
for (const candidate of pythonCandidates) {
|
|
1305
|
-
if (!candidate || seen.has(candidate)) continue;
|
|
1306
|
-
seen.add(candidate);
|
|
1307
|
-
const result = await runCapture(candidate, ["-c", "import openviking; print(openviking.__version__)"], { shell: IS_WIN });
|
|
1308
|
-
if (result.code === 0) {
|
|
1309
|
-
return result.out.trim();
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
return "";
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
754
|
async function printCurrentVersionInfo() {
|
|
1317
755
|
const state = await readJsonFileIfExists(getInstallStatePathForPlugin("openviking"));
|
|
1318
756
|
const pluginRequestedRef = state?.requestedRef || "";
|
|
1319
757
|
const pluginReleaseId = state?.releaseId || "";
|
|
1320
758
|
const pluginInstalledAt = state?.installedAt || "";
|
|
1321
|
-
const openvikingInstalledVersion = await detectInstalledOpenVikingVersion();
|
|
1322
759
|
|
|
1323
760
|
console.log("");
|
|
1324
761
|
bold(tr("Installed versions", "当前已安装版本"));
|
|
@@ -1328,7 +765,7 @@ async function printCurrentVersionInfo() {
|
|
|
1328
765
|
if (pluginRequestedRef && pluginReleaseId && pluginRequestedRef !== pluginReleaseId) {
|
|
1329
766
|
console.log(`Plugin requested ref: ${pluginRequestedRef}`);
|
|
1330
767
|
}
|
|
1331
|
-
console.log(
|
|
768
|
+
console.log(tr("OpenViking server: not installed by this tool (use a remote URL in plugin config)", "OpenViking 服务端:本工具不安装;请在插件配置中填写远程服务地址"));
|
|
1332
769
|
if (pluginInstalledAt) {
|
|
1333
770
|
console.log(`Installed at: ${pluginInstalledAt}`);
|
|
1334
771
|
}
|
|
@@ -1346,10 +783,6 @@ function getOpenClawConfigBackupPath() {
|
|
|
1346
783
|
return join(getUpgradeAuditDir(), "openclaw.json.bak");
|
|
1347
784
|
}
|
|
1348
785
|
|
|
1349
|
-
function normalizePluginMode(value) {
|
|
1350
|
-
return value === "remote" ? "remote" : "local";
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
786
|
function getPluginVariantById(pluginId) {
|
|
1354
787
|
return PLUGIN_VARIANTS.find((variant) => variant.id === pluginId) || null;
|
|
1355
788
|
}
|
|
@@ -1434,47 +867,21 @@ function formatTargetVersionLabel() {
|
|
|
1434
867
|
|
|
1435
868
|
function extractRuntimeConfigFromPluginEntry(entryConfig) {
|
|
1436
869
|
if (!entryConfig || typeof entryConfig !== "object") return null;
|
|
870
|
+
if (entryConfig.mode != null && entryConfig.mode !== "remote") return null;
|
|
1437
871
|
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
if (mode === "remote") {
|
|
1442
|
-
if (typeof entryConfig.baseUrl === "string" && entryConfig.baseUrl.trim()) {
|
|
1443
|
-
runtime.baseUrl = entryConfig.baseUrl.trim();
|
|
1444
|
-
}
|
|
1445
|
-
if (typeof entryConfig.apiKey === "string" && entryConfig.apiKey.trim()) {
|
|
1446
|
-
runtime.apiKey = entryConfig.apiKey;
|
|
1447
|
-
}
|
|
1448
|
-
if (typeof entryConfig.agentId === "string" && entryConfig.agentId.trim()) {
|
|
1449
|
-
runtime.agentId = entryConfig.agentId.trim();
|
|
1450
|
-
}
|
|
1451
|
-
return runtime;
|
|
872
|
+
const runtime = { mode: "remote" };
|
|
873
|
+
if (typeof entryConfig.baseUrl === "string" && entryConfig.baseUrl.trim()) {
|
|
874
|
+
runtime.baseUrl = entryConfig.baseUrl.trim();
|
|
1452
875
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
runtime.configPath = entryConfig.configPath.trim();
|
|
876
|
+
if (typeof entryConfig.apiKey === "string" && entryConfig.apiKey.trim()) {
|
|
877
|
+
runtime.apiKey = entryConfig.apiKey;
|
|
1456
878
|
}
|
|
1457
|
-
if (
|
|
1458
|
-
|
|
1459
|
-
if (Number.isFinite(parsedPort) && parsedPort > 0) {
|
|
1460
|
-
runtime.port = parsedPort;
|
|
1461
|
-
}
|
|
879
|
+
if (typeof entryConfig.agentId === "string" && entryConfig.agentId.trim()) {
|
|
880
|
+
runtime.agentId = entryConfig.agentId.trim();
|
|
1462
881
|
}
|
|
1463
882
|
return runtime;
|
|
1464
883
|
}
|
|
1465
884
|
|
|
1466
|
-
async function readPortFromOvConf(configPath) {
|
|
1467
|
-
const filePath = configPath || join(OPENVIKING_DIR, "ov.conf");
|
|
1468
|
-
if (!existsSync(filePath)) return null;
|
|
1469
|
-
try {
|
|
1470
|
-
const ovConf = await readJsonFileIfExists(filePath);
|
|
1471
|
-
const parsedPort = Number.parseInt(String(ovConf?.server?.port ?? ""), 10);
|
|
1472
|
-
return Number.isFinite(parsedPort) && parsedPort > 0 ? parsedPort : null;
|
|
1473
|
-
} catch {
|
|
1474
|
-
return null;
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
885
|
async function backupOpenClawConfig(configPath) {
|
|
1479
886
|
await mkdir(getUpgradeAuditDir(), { recursive: true });
|
|
1480
887
|
const backupPath = getOpenClawConfigBackupPath();
|
|
@@ -1607,7 +1014,7 @@ async function rollbackLastUpgradeOperation() {
|
|
|
1607
1014
|
info(tr("Run `openclaw gateway` and `openclaw status` to verify the restored plugin state.", "请运行 `openclaw gateway` 和 `openclaw status` 验证恢复后的插件状态。"));
|
|
1608
1015
|
}
|
|
1609
1016
|
|
|
1610
|
-
|
|
1017
|
+
function prepareUpgradeRuntimeConfig(installedState) {
|
|
1611
1018
|
const plugins = installedState.config?.plugins ?? {};
|
|
1612
1019
|
const candidateOrder = installedState.detections
|
|
1613
1020
|
.map((item) => item.variant)
|
|
@@ -1623,16 +1030,11 @@ async function prepareUpgradeRuntimeConfig(installedState) {
|
|
|
1623
1030
|
}
|
|
1624
1031
|
|
|
1625
1032
|
if (!runtime) {
|
|
1626
|
-
runtime = { mode: "
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
if (runtime.mode === "remote") {
|
|
1630
|
-
runtime.baseUrl = runtime.baseUrl || remoteBaseUrl;
|
|
1631
|
-
return runtime;
|
|
1033
|
+
runtime = { mode: "remote" };
|
|
1632
1034
|
}
|
|
1633
1035
|
|
|
1634
|
-
runtime.
|
|
1635
|
-
runtime.
|
|
1036
|
+
runtime.mode = "remote";
|
|
1037
|
+
runtime.baseUrl = runtime.baseUrl || remoteBaseUrl;
|
|
1636
1038
|
return runtime;
|
|
1637
1039
|
}
|
|
1638
1040
|
|
|
@@ -1759,24 +1161,19 @@ async function prepareStrongPluginUpgrade() {
|
|
|
1759
1161
|
}
|
|
1760
1162
|
|
|
1761
1163
|
installedUpgradeState = installedState;
|
|
1762
|
-
upgradeRuntimeConfig =
|
|
1164
|
+
upgradeRuntimeConfig = prepareUpgradeRuntimeConfig(installedState);
|
|
1763
1165
|
const fromVersion = formatInstalledStateLabel(installedState);
|
|
1764
1166
|
const toVersion = formatTargetVersionLabel();
|
|
1765
|
-
selectedMode = upgradeRuntimeConfig.mode;
|
|
1766
1167
|
info(
|
|
1767
1168
|
tr(
|
|
1768
1169
|
`Detected installed OpenViking plugin state: ${installedState.generation}`,
|
|
1769
1170
|
`检测到已安装 OpenViking 插件状态: ${installedState.generation}`,
|
|
1770
1171
|
),
|
|
1771
1172
|
);
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
} else {
|
|
1777
|
-
selectedServerPort = upgradeRuntimeConfig.port || DEFAULT_SERVER_PORT;
|
|
1778
|
-
}
|
|
1779
|
-
info(tr(`Upgrade runtime mode: ${selectedMode}`, `升级运行模式: ${selectedMode}`));
|
|
1173
|
+
remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
|
|
1174
|
+
remoteApiKey = upgradeRuntimeConfig.apiKey || "";
|
|
1175
|
+
remoteAgentId = upgradeRuntimeConfig.agentId || "";
|
|
1176
|
+
info(tr(`Upgrade runtime mode: ${selectedMode} (remote OpenViking server)`, `升级运行模式: ${selectedMode}(远程 OpenViking 服务)`));
|
|
1780
1177
|
|
|
1781
1178
|
info(tr(`Upgrade path: ${fromVersion} -> ${toVersion}`, `升级路径: ${fromVersion} -> ${toVersion}`));
|
|
1782
1179
|
|
|
@@ -1804,8 +1201,8 @@ async function prepareStrongPluginUpgrade() {
|
|
|
1804
1201
|
|
|
1805
1202
|
info(
|
|
1806
1203
|
tr(
|
|
1807
|
-
"Upgrade will
|
|
1808
|
-
"
|
|
1204
|
+
"Upgrade will preserve existing plugin server connection settings where possible and re-apply minimal remote plugin config.",
|
|
1205
|
+
"升级将尽可能保留已有的插件服务端连接信息,并只回填最少的远程插件配置。",
|
|
1809
1206
|
),
|
|
1810
1207
|
);
|
|
1811
1208
|
info(tr(`Upgrade audit file: ${getUpgradeAuditPath()}`, `升级审计文件: ${getUpgradeAuditPath()}`));
|
|
@@ -1909,30 +1306,6 @@ async function downloadPlugin(destDir) {
|
|
|
1909
1306
|
info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
|
|
1910
1307
|
}
|
|
1911
1308
|
|
|
1912
|
-
async function deployLocalPlugin(localPluginDir, destDir) {
|
|
1913
|
-
await rm(destDir, { recursive: true, force: true });
|
|
1914
|
-
await mkdir(destDir, { recursive: true });
|
|
1915
|
-
await cp(localPluginDir, destDir, {
|
|
1916
|
-
recursive: true,
|
|
1917
|
-
force: true,
|
|
1918
|
-
filter: (sourcePath) => {
|
|
1919
|
-
const rel = relative(localPluginDir, sourcePath);
|
|
1920
|
-
if (!rel) return true;
|
|
1921
|
-
const firstSegment = rel.split(/[\\/]/)[0];
|
|
1922
|
-
return firstSegment !== "node_modules" && firstSegment !== ".git";
|
|
1923
|
-
},
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
async function installPluginDependencies(destDir) {
|
|
1928
|
-
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
1929
|
-
const npmArgs = resolvedNpmOmitDev
|
|
1930
|
-
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
1931
|
-
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
1932
|
-
await run("npm", npmArgs, { cwd: destDir, silent: false });
|
|
1933
|
-
return info(tr(`Plugin prepared: ${destDir}`, `插件已准备: ${destDir}`));
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
1309
|
async function createPluginStagingDir() {
|
|
1937
1310
|
const pluginId = resolvedPluginId || "openviking";
|
|
1938
1311
|
const extensionsDir = join(OPENCLAW_DIR, "extensions");
|
|
@@ -2027,22 +1400,9 @@ async function scrubStaleOpenClawPluginRegistration() {
|
|
|
2027
1400
|
await rename(tmp, configPath);
|
|
2028
1401
|
}
|
|
2029
1402
|
|
|
2030
|
-
async function deployPluginFromLocal(localPluginDir) {
|
|
2031
|
-
const stagingDir = await createPluginStagingDir();
|
|
2032
|
-
try {
|
|
2033
|
-
await deployLocalPlugin(localPluginDir, stagingDir);
|
|
2034
|
-
await installPluginDependencies(stagingDir);
|
|
2035
|
-
await finalizePluginDeployment(stagingDir);
|
|
2036
|
-
} catch (error) {
|
|
2037
|
-
await rm(stagingDir, { recursive: true, force: true });
|
|
2038
|
-
throw error;
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
1403
|
async function configureOpenClawPlugin({
|
|
2043
1404
|
preserveExistingConfig = false,
|
|
2044
1405
|
runtimeConfig = null,
|
|
2045
|
-
skipGatewayMode = false,
|
|
2046
1406
|
claimSlot = true,
|
|
2047
1407
|
} = {}) {
|
|
2048
1408
|
info(tr("Configuring OpenClaw plugin...", "正在配置 OpenClaw 插件..."));
|
|
@@ -2088,31 +1448,20 @@ async function configureOpenClawPlugin({
|
|
|
2088
1448
|
return;
|
|
2089
1449
|
}
|
|
2090
1450
|
|
|
2091
|
-
const effectiveRuntimeConfig = runtimeConfig ||
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
1451
|
+
const effectiveRuntimeConfig = runtimeConfig || {
|
|
1452
|
+
mode: "remote",
|
|
1453
|
+
baseUrl: remoteBaseUrl,
|
|
1454
|
+
apiKey: remoteApiKey,
|
|
1455
|
+
agentId: remoteAgentId,
|
|
1456
|
+
};
|
|
2096
1457
|
|
|
2097
|
-
|
|
2098
|
-
|
|
1458
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.mode`, "remote"]);
|
|
1459
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.baseUrl`, effectiveRuntimeConfig.baseUrl || remoteBaseUrl]);
|
|
1460
|
+
if (effectiveRuntimeConfig.apiKey) {
|
|
1461
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.apiKey`, effectiveRuntimeConfig.apiKey]);
|
|
2099
1462
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
if (effectiveRuntimeConfig.mode === "local") {
|
|
2103
|
-
const ovConfPath = effectiveRuntimeConfig.configPath || join(OPENVIKING_DIR, "ov.conf");
|
|
2104
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.mode`, "local"]);
|
|
2105
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.configPath`, ovConfPath]);
|
|
2106
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.port`, String(effectiveRuntimeConfig.port || DEFAULT_SERVER_PORT)]);
|
|
2107
|
-
} else {
|
|
2108
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.mode`, "remote"]);
|
|
2109
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.baseUrl`, effectiveRuntimeConfig.baseUrl || remoteBaseUrl]);
|
|
2110
|
-
if (effectiveRuntimeConfig.apiKey) {
|
|
2111
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.apiKey`, effectiveRuntimeConfig.apiKey]);
|
|
2112
|
-
}
|
|
2113
|
-
if (effectiveRuntimeConfig.agentId) {
|
|
2114
|
-
await oc(["config", "set", `plugins.entries.${pluginId}.config.agentId`, effectiveRuntimeConfig.agentId]);
|
|
2115
|
-
}
|
|
1463
|
+
if (effectiveRuntimeConfig.agentId) {
|
|
1464
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.agentId`, effectiveRuntimeConfig.agentId]);
|
|
2116
1465
|
}
|
|
2117
1466
|
|
|
2118
1467
|
// Legacy (memory) plugins need explicit targetUri/autoRecall/autoCapture (new version has defaults in config.ts)
|
|
@@ -2125,75 +1474,9 @@ async function configureOpenClawPlugin({
|
|
|
2125
1474
|
info(tr("OpenClaw plugin configured", "OpenClaw 插件配置完成"));
|
|
2126
1475
|
}
|
|
2127
1476
|
|
|
2128
|
-
async function
|
|
2129
|
-
const candidates = IS_WIN
|
|
2130
|
-
? ["python3", "python", "py -3"]
|
|
2131
|
-
: ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python"];
|
|
2132
|
-
for (const candidate of candidates) {
|
|
2133
|
-
if (candidate === failedPy) continue;
|
|
2134
|
-
const resolved = await resolveAbsoluteCommand(candidate);
|
|
2135
|
-
if (!resolved || resolved === candidate || resolved === failedPy) continue;
|
|
2136
|
-
const check = await runCapture(resolved, ["-c", "import openviking"], { shell: false });
|
|
2137
|
-
if (check.code === 0) return resolved;
|
|
2138
|
-
}
|
|
2139
|
-
return "";
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
|
-
async function resolvePythonPath() {
|
|
2143
|
-
if (openvikingPythonPath) return openvikingPythonPath;
|
|
2144
|
-
const python = await checkPython();
|
|
2145
|
-
return python.cmd || "";
|
|
2146
|
-
}
|
|
2147
|
-
|
|
2148
|
-
async function writeOpenvikingEnv({ includePython }) {
|
|
1477
|
+
async function writeOpenvikingEnv() {
|
|
2149
1478
|
const needStateDir = OPENCLAW_DIR !== DEFAULT_OPENCLAW_DIR;
|
|
2150
|
-
|
|
2151
|
-
if (includePython) {
|
|
2152
|
-
pythonPath = await resolvePythonPath();
|
|
2153
|
-
if (!pythonPath) {
|
|
2154
|
-
pythonPath = (process.env.OPENVIKING_PYTHON || "").trim() || (IS_WIN ? "python" : "python3");
|
|
2155
|
-
warn(
|
|
2156
|
-
tr(
|
|
2157
|
-
"Could not resolve absolute Python path; wrote fallback OPENVIKING_PYTHON to openviking.env. Edit that file if OpenViking fails to start.",
|
|
2158
|
-
"未能解析 Python 绝对路径,已在 openviking.env 中写入后备值。若启动失败请手动修改为虚拟环境中的 python 可执行文件路径。",
|
|
2159
|
-
),
|
|
2160
|
-
);
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
// Verify the resolved Python can actually import openviking
|
|
2164
|
-
if (pythonPath) {
|
|
2165
|
-
const verify = await runCapture(pythonPath, ["-c", "import openviking"], { shell: false });
|
|
2166
|
-
if (verify.code !== 0) {
|
|
2167
|
-
warn(
|
|
2168
|
-
tr(
|
|
2169
|
-
`Resolved Python (${pythonPath}) cannot import openviking. The pip install target may differ from the runtime python3.`,
|
|
2170
|
-
`解析到的 Python(${pythonPath})无法 import openviking。pip 安装目标可能与运行时的 python3 不一致。`,
|
|
2171
|
-
),
|
|
2172
|
-
);
|
|
2173
|
-
// Try to discover the correct Python via pip show
|
|
2174
|
-
const corrected = await discoverOpenvikingPython(pythonPath);
|
|
2175
|
-
if (corrected) {
|
|
2176
|
-
info(
|
|
2177
|
-
tr(
|
|
2178
|
-
`Auto-corrected OPENVIKING_PYTHON to ${corrected}`,
|
|
2179
|
-
`已自动修正 OPENVIKING_PYTHON 为 ${corrected}`,
|
|
2180
|
-
),
|
|
2181
|
-
);
|
|
2182
|
-
pythonPath = corrected;
|
|
2183
|
-
} else {
|
|
2184
|
-
warn(
|
|
2185
|
-
tr(
|
|
2186
|
-
`Could not auto-detect the correct Python. Edit OPENVIKING_PYTHON in the env file manually.`,
|
|
2187
|
-
`无法自动检测正确的 Python。请手动修改 env 文件中的 OPENVIKING_PYTHON。`,
|
|
2188
|
-
),
|
|
2189
|
-
);
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
// Remote mode + default state dir + no python line → nothing to persist
|
|
2196
|
-
if (!needStateDir && !pythonPath) return null;
|
|
1479
|
+
if (!needStateDir) return null;
|
|
2197
1480
|
|
|
2198
1481
|
await mkdir(OPENCLAW_DIR, { recursive: true });
|
|
2199
1482
|
|
|
@@ -2201,14 +1484,8 @@ async function writeOpenvikingEnv({ includePython }) {
|
|
|
2201
1484
|
const batLines = ["@echo off"];
|
|
2202
1485
|
const psLines = [];
|
|
2203
1486
|
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
psLines.push(`$env:OPENCLAW_STATE_DIR = "${OPENCLAW_DIR.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
|
|
2207
|
-
}
|
|
2208
|
-
if (pythonPath) {
|
|
2209
|
-
batLines.push(`set "OPENVIKING_PYTHON=${pythonPath.replace(/"/g, '""')}"`);
|
|
2210
|
-
psLines.push(`$env:OPENVIKING_PYTHON = "${pythonPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
|
|
2211
|
-
}
|
|
1487
|
+
batLines.push(`set "OPENCLAW_STATE_DIR=${OPENCLAW_DIR.replace(/"/g, '""')}"`);
|
|
1488
|
+
psLines.push(`$env:OPENCLAW_STATE_DIR = "${OPENCLAW_DIR.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
|
|
2212
1489
|
|
|
2213
1490
|
const batPath = join(OPENCLAW_DIR, "openviking.env.bat");
|
|
2214
1491
|
const ps1Path = join(OPENCLAW_DIR, "openviking.env.ps1");
|
|
@@ -2219,16 +1496,12 @@ async function writeOpenvikingEnv({ includePython }) {
|
|
|
2219
1496
|
return { shellPath: batPath, powershellPath: ps1Path };
|
|
2220
1497
|
}
|
|
2221
1498
|
|
|
2222
|
-
const lines = [];
|
|
2223
|
-
if (needStateDir) {
|
|
2224
|
-
lines.push(`export OPENCLAW_STATE_DIR='${OPENCLAW_DIR.replace(/'/g, "'\"'\"'")}'`);
|
|
2225
|
-
}
|
|
2226
|
-
if (pythonPath) {
|
|
2227
|
-
lines.push(`export OPENVIKING_PYTHON='${pythonPath.replace(/'/g, "'\"'\"'")}'`);
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
1499
|
const envPath = join(OPENCLAW_DIR, "openviking.env");
|
|
2231
|
-
await writeFile(
|
|
1500
|
+
await writeFile(
|
|
1501
|
+
envPath,
|
|
1502
|
+
`export OPENCLAW_STATE_DIR='${OPENCLAW_DIR.replace(/'/g, "'\"'\"'")}'\n`,
|
|
1503
|
+
"utf8",
|
|
1504
|
+
);
|
|
2232
1505
|
info(tr(`Environment file generated: ${envPath}`, `已生成环境文件: ${envPath}`));
|
|
2233
1506
|
return { shellPath: envPath };
|
|
2234
1507
|
}
|
|
@@ -2256,24 +1529,11 @@ function getExistingEnvFiles() {
|
|
|
2256
1529
|
return existsSync(envPath) ? { shellPath: envPath } : null;
|
|
2257
1530
|
}
|
|
2258
1531
|
|
|
2259
|
-
function ensureExistingPluginForUpgrade() {
|
|
2260
|
-
if (!existsSync(PLUGIN_DEST)) {
|
|
2261
|
-
err(
|
|
2262
|
-
tr(
|
|
2263
|
-
`Plugin upgrade mode expects an existing plugin at ${PLUGIN_DEST}. Run the full installer first if this is a fresh install.`,
|
|
2264
|
-
`插件升级模式要求 ${PLUGIN_DEST} 处已存在插件安装。若是首次安装,请先运行完整安装流程。`,
|
|
2265
|
-
),
|
|
2266
|
-
);
|
|
2267
|
-
process.exit(1);
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
1532
|
async function main() {
|
|
2272
1533
|
console.log("");
|
|
2273
|
-
bold(tr("🦣 OpenClaw
|
|
1534
|
+
bold(tr("🦣 OpenClaw OpenViking plugin installer", "🦣 OpenClaw OpenViking 插件安装"));
|
|
2274
1535
|
console.log("");
|
|
2275
1536
|
|
|
2276
|
-
ensurePluginOnlyOperationArgs();
|
|
2277
1537
|
await selectWorkdir();
|
|
2278
1538
|
if (showCurrentVersion) {
|
|
2279
1539
|
await printCurrentVersionInfo();
|
|
@@ -2292,15 +1552,9 @@ async function main() {
|
|
|
2292
1552
|
info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
|
|
2293
1553
|
info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
|
|
2294
1554
|
info(tr(`Plugin version: ${PLUGIN_VERSION}`, `插件版本: ${PLUGIN_VERSION}`));
|
|
2295
|
-
if (openvikingVersion) {
|
|
2296
|
-
info(tr(`OpenViking version: ${openvikingVersion}`, `OpenViking 版本: ${openvikingVersion}`));
|
|
2297
|
-
}
|
|
2298
1555
|
|
|
2299
1556
|
if (upgradePluginOnly) {
|
|
2300
|
-
|
|
2301
|
-
info("Mode: plugin upgrade only (backup old plugin, clean only OpenViking plugin config, keep ov.conf)");
|
|
2302
|
-
} else {
|
|
2303
|
-
await selectMode();
|
|
1557
|
+
info(tr("Mode: plugin upgrade only", "模式: 仅升级插件"));
|
|
2304
1558
|
}
|
|
2305
1559
|
info(tr(`Mode: ${selectedMode}`, `模式: ${selectedMode}`));
|
|
2306
1560
|
|
|
@@ -2309,15 +1563,6 @@ async function main() {
|
|
|
2309
1563
|
await resolvePluginConfig();
|
|
2310
1564
|
await checkOpenClawCompatibility();
|
|
2311
1565
|
await prepareStrongPluginUpgrade();
|
|
2312
|
-
} else if (selectedMode === "local") {
|
|
2313
|
-
await validateEnvironment();
|
|
2314
|
-
await checkOpenClaw();
|
|
2315
|
-
// Resolve plugin config after OpenClaw is available (for version detection)
|
|
2316
|
-
await resolvePluginConfig();
|
|
2317
|
-
await checkOpenClawCompatibility();
|
|
2318
|
-
checkRequestedOpenVikingCompatibility();
|
|
2319
|
-
await installOpenViking();
|
|
2320
|
-
await configureOvConf();
|
|
2321
1566
|
} else {
|
|
2322
1567
|
await checkOpenClaw();
|
|
2323
1568
|
await resolvePluginConfig();
|
|
@@ -2325,25 +1570,12 @@ async function main() {
|
|
|
2325
1570
|
await collectRemoteConfig();
|
|
2326
1571
|
}
|
|
2327
1572
|
|
|
2328
|
-
|
|
2329
|
-
const localPluginDir = openvikingRepo ? join(openvikingRepo, "examples", resolvedPluginDir || "openclaw-plugin") : "";
|
|
2330
|
-
if (openvikingRepo && existsSync(join(localPluginDir, "index.ts"))) {
|
|
2331
|
-
pluginPath = localPluginDir;
|
|
2332
|
-
PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId || "openviking");
|
|
2333
|
-
info(tr(`Using local plugin from repo: ${pluginPath}`, `使用仓库内插件: ${pluginPath}`));
|
|
2334
|
-
await deployPluginFromLocal(pluginPath);
|
|
2335
|
-
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
2336
|
-
pluginPath = PLUGIN_DEST;
|
|
2337
|
-
} else {
|
|
2338
|
-
await deployPluginFromRemote();
|
|
2339
|
-
pluginPath = PLUGIN_DEST;
|
|
2340
|
-
}
|
|
1573
|
+
await deployPluginFromRemote();
|
|
2341
1574
|
|
|
2342
1575
|
await configureOpenClawPlugin(
|
|
2343
1576
|
upgradePluginOnly
|
|
2344
1577
|
? {
|
|
2345
1578
|
runtimeConfig: upgradeRuntimeConfig,
|
|
2346
|
-
skipGatewayMode: true,
|
|
2347
1579
|
claimSlot: installedUpgradeState ? shouldClaimTargetSlot(installedUpgradeState) : true,
|
|
2348
1580
|
}
|
|
2349
1581
|
: { preserveExistingConfig: false },
|
|
@@ -2360,11 +1592,9 @@ async function main() {
|
|
|
2360
1592
|
}
|
|
2361
1593
|
let envFiles = getExistingEnvFiles();
|
|
2362
1594
|
if (!upgradePluginOnly) {
|
|
2363
|
-
envFiles = await writeOpenvikingEnv(
|
|
2364
|
-
includePython: selectedMode === "local",
|
|
2365
|
-
});
|
|
1595
|
+
envFiles = await writeOpenvikingEnv();
|
|
2366
1596
|
} else if (!envFiles && OPENCLAW_DIR !== DEFAULT_OPENCLAW_DIR) {
|
|
2367
|
-
envFiles = await writeOpenvikingEnv(
|
|
1597
|
+
envFiles = await writeOpenvikingEnv();
|
|
2368
1598
|
}
|
|
2369
1599
|
|
|
2370
1600
|
console.log("");
|
|
@@ -2383,30 +1613,14 @@ async function main() {
|
|
|
2383
1613
|
console.log("");
|
|
2384
1614
|
}
|
|
2385
1615
|
|
|
2386
|
-
|
|
2387
|
-
info(tr("Run these commands to start OpenClaw + OpenViking:", "请按以下命令启动 OpenClaw + OpenViking:"));
|
|
2388
|
-
} else {
|
|
2389
|
-
info(tr("Run these commands to start OpenClaw:", "请按以下命令启动 OpenClaw:"));
|
|
2390
|
-
}
|
|
1616
|
+
info(tr("Run these commands to start OpenClaw:", "请按以下命令启动 OpenClaw:"));
|
|
2391
1617
|
console.log(` 1) ${wrapCommand("openclaw --version", envFiles)}`);
|
|
2392
1618
|
console.log(` 2) ${wrapCommand("openclaw onboard", envFiles)}`);
|
|
2393
1619
|
console.log(` 3) ${wrapCommand("openclaw gateway", envFiles)}`);
|
|
2394
1620
|
console.log(` 4) ${wrapCommand("openclaw status", envFiles)}`);
|
|
2395
1621
|
console.log("");
|
|
2396
1622
|
|
|
2397
|
-
|
|
2398
|
-
if (envFiles?.shellPath && !IS_WIN) {
|
|
2399
|
-
info(
|
|
2400
|
-
tr(
|
|
2401
|
-
'If source fails, set: export OPENVIKING_PYTHON="$(command -v python3)"',
|
|
2402
|
-
'若 source 失败,可执行: export OPENVIKING_PYTHON="$(command -v python3)"',
|
|
2403
|
-
),
|
|
2404
|
-
);
|
|
2405
|
-
}
|
|
2406
|
-
info(tr(`You can edit the config freely: ${OPENVIKING_DIR}/ov.conf`, `你可以按需自由修改配置文件: ${OPENVIKING_DIR}/ov.conf`));
|
|
2407
|
-
} else {
|
|
2408
|
-
info(tr(`Remote server: ${remoteBaseUrl}`, `远程服务器: ${remoteBaseUrl}`));
|
|
2409
|
-
}
|
|
1623
|
+
info(tr(`OpenViking server URL (plugin): ${remoteBaseUrl}`, `OpenViking 服务地址(插件): ${remoteBaseUrl}`));
|
|
2410
1624
|
console.log("");
|
|
2411
1625
|
}
|
|
2412
1626
|
|