openclaw-openviking-setup-helper 0.2.15 → 0.2.16-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/install.js +56 -841
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * OpenClaw + OpenViking cross-platform installer
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 ] [ --openviking-version=V ] [ --version=V ] [ --repo=PATH ]
14
+ * [ --plugin-version=TAG ]
15
15
  *
16
16
  * Environment variables:
17
- * REPO, PLUGIN_VERSION (or BRANCH), OPENVIKING_INSTALL_YES, SKIP_OPENCLAW, SKIP_OPENVIKING
18
- * OPENVIKING_VERSION Pip install openviking==VERSION (omit to follow release plugin version when possible)
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, relative } from "node:path";
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
- let selectedMode = "local";
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/OpenViking versions and exit");
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; keep ov.conf and do not change the OpenViking service");
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=0.2.9");
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, OPENVIKING_VERSION, SKIP_OPENCLAW, SKIP_OPENVIKING, NPM_REGISTRY, PIP_INDEX_URL");
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(`OpenViking: ${openvikingInstalledVersion || "unknown"}`);
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
  }
@@ -1435,46 +868,19 @@ function formatTargetVersionLabel() {
1435
868
  function extractRuntimeConfigFromPluginEntry(entryConfig) {
1436
869
  if (!entryConfig || typeof entryConfig !== "object") return null;
1437
870
 
1438
- const mode = normalizePluginMode(entryConfig.mode);
1439
- const runtime = { mode };
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;
871
+ const runtime = {};
872
+ if (typeof entryConfig.baseUrl === "string" && entryConfig.baseUrl.trim()) {
873
+ runtime.baseUrl = entryConfig.baseUrl.trim();
1452
874
  }
1453
-
1454
- if (typeof entryConfig.configPath === "string" && entryConfig.configPath.trim()) {
1455
- runtime.configPath = entryConfig.configPath.trim();
875
+ if (typeof entryConfig.apiKey === "string" && entryConfig.apiKey.trim()) {
876
+ runtime.apiKey = entryConfig.apiKey;
1456
877
  }
1457
- if (entryConfig.port !== undefined && entryConfig.port !== null && `${entryConfig.port}`.trim()) {
1458
- const parsedPort = Number.parseInt(String(entryConfig.port), 10);
1459
- if (Number.isFinite(parsedPort) && parsedPort > 0) {
1460
- runtime.port = parsedPort;
1461
- }
878
+ if (typeof entryConfig.agentId === "string" && entryConfig.agentId.trim()) {
879
+ runtime.agentId = entryConfig.agentId.trim();
1462
880
  }
1463
881
  return runtime;
1464
882
  }
1465
883
 
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
884
  async function backupOpenClawConfig(configPath) {
1479
885
  await mkdir(getUpgradeAuditDir(), { recursive: true });
1480
886
  const backupPath = getOpenClawConfigBackupPath();
@@ -1607,7 +1013,7 @@ async function rollbackLastUpgradeOperation() {
1607
1013
  info(tr("Run `openclaw gateway` and `openclaw status` to verify the restored plugin state.", "请运行 `openclaw gateway` 和 `openclaw status` 验证恢复后的插件状态。"));
1608
1014
  }
1609
1015
 
1610
- async function prepareUpgradeRuntimeConfig(installedState) {
1016
+ function prepareUpgradeRuntimeConfig(installedState) {
1611
1017
  const plugins = installedState.config?.plugins ?? {};
1612
1018
  const candidateOrder = installedState.detections
1613
1019
  .map((item) => item.variant)
@@ -1623,16 +1029,11 @@ async function prepareUpgradeRuntimeConfig(installedState) {
1623
1029
  }
1624
1030
 
1625
1031
  if (!runtime) {
1626
- runtime = { mode: "local" };
1627
- }
1628
-
1629
- if (runtime.mode === "remote") {
1630
- runtime.baseUrl = runtime.baseUrl || remoteBaseUrl;
1631
- return runtime;
1032
+ runtime = {};
1632
1033
  }
1633
1034
 
1634
- runtime.configPath = runtime.configPath || join(OPENVIKING_DIR, "ov.conf");
1635
- runtime.port = runtime.port || await readPortFromOvConf(runtime.configPath) || DEFAULT_SERVER_PORT;
1035
+ delete runtime.mode;
1036
+ runtime.baseUrl = runtime.baseUrl || remoteBaseUrl;
1636
1037
  return runtime;
1637
1038
  }
1638
1039
 
@@ -1759,24 +1160,19 @@ async function prepareStrongPluginUpgrade() {
1759
1160
  }
1760
1161
 
1761
1162
  installedUpgradeState = installedState;
1762
- upgradeRuntimeConfig = await prepareUpgradeRuntimeConfig(installedState);
1163
+ upgradeRuntimeConfig = prepareUpgradeRuntimeConfig(installedState);
1763
1164
  const fromVersion = formatInstalledStateLabel(installedState);
1764
1165
  const toVersion = formatTargetVersionLabel();
1765
- selectedMode = upgradeRuntimeConfig.mode;
1766
1166
  info(
1767
1167
  tr(
1768
1168
  `Detected installed OpenViking plugin state: ${installedState.generation}`,
1769
1169
  `检测到已安装 OpenViking 插件状态: ${installedState.generation}`,
1770
1170
  ),
1771
1171
  );
1772
- if (upgradeRuntimeConfig.mode === "remote") {
1773
- remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
1774
- remoteApiKey = upgradeRuntimeConfig.apiKey || "";
1775
- remoteAgentId = upgradeRuntimeConfig.agentId || "";
1776
- } else {
1777
- selectedServerPort = upgradeRuntimeConfig.port || DEFAULT_SERVER_PORT;
1778
- }
1779
- info(tr(`Upgrade runtime mode: ${selectedMode}`, `升级运行模式: ${selectedMode}`));
1172
+ remoteBaseUrl = upgradeRuntimeConfig.baseUrl || remoteBaseUrl;
1173
+ remoteApiKey = upgradeRuntimeConfig.apiKey || "";
1174
+ remoteAgentId = upgradeRuntimeConfig.agentId || "";
1175
+ info(tr(`Upgrade runtime mode: ${selectedMode} (remote OpenViking server)`, `升级运行模式: ${selectedMode}(远程 OpenViking 服务)`));
1780
1176
 
1781
1177
  info(tr(`Upgrade path: ${fromVersion} -> ${toVersion}`, `升级路径: ${fromVersion} -> ${toVersion}`));
1782
1178
 
@@ -1804,8 +1200,8 @@ async function prepareStrongPluginUpgrade() {
1804
1200
 
1805
1201
  info(
1806
1202
  tr(
1807
- "Upgrade will keep the existing OpenViking runtime file and re-apply only the minimum plugin runtime settings.",
1808
- "升级将保留现有 OpenViking 运行时文件,并只回填最小插件运行配置。",
1203
+ "Upgrade will preserve existing plugin server connection settings where possible and re-apply minimal remote plugin config.",
1204
+ "升级将尽可能保留已有的插件服务端连接信息,并只回填最少的远程插件配置。",
1809
1205
  ),
1810
1206
  );
1811
1207
  info(tr(`Upgrade audit file: ${getUpgradeAuditPath()}`, `升级审计文件: ${getUpgradeAuditPath()}`));
@@ -1909,30 +1305,6 @@ async function downloadPlugin(destDir) {
1909
1305
  info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
1910
1306
  }
1911
1307
 
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
1308
  async function createPluginStagingDir() {
1937
1309
  const pluginId = resolvedPluginId || "openviking";
1938
1310
  const extensionsDir = join(OPENCLAW_DIR, "extensions");
@@ -2027,22 +1399,9 @@ async function scrubStaleOpenClawPluginRegistration() {
2027
1399
  await rename(tmp, configPath);
2028
1400
  }
2029
1401
 
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
1402
  async function configureOpenClawPlugin({
2043
1403
  preserveExistingConfig = false,
2044
1404
  runtimeConfig = null,
2045
- skipGatewayMode = false,
2046
1405
  claimSlot = true,
2047
1406
  } = {}) {
2048
1407
  info(tr("Configuring OpenClaw plugin...", "正在配置 OpenClaw 插件..."));
@@ -2088,27 +1447,18 @@ async function configureOpenClawPlugin({
2088
1447
  return;
2089
1448
  }
2090
1449
 
2091
- const effectiveRuntimeConfig = runtimeConfig || (
2092
- selectedMode === "remote"
2093
- ? { mode: "remote", baseUrl: remoteBaseUrl, apiKey: remoteApiKey, agentId: remoteAgentId }
2094
- : { mode: "local", configPath: join(OPENVIKING_DIR, "ov.conf"), port: selectedServerPort }
2095
- );
1450
+ const effectiveRuntimeConfig = runtimeConfig || {
1451
+ baseUrl: remoteBaseUrl,
1452
+ apiKey: remoteApiKey,
1453
+ agentId: remoteAgentId,
1454
+ };
2096
1455
 
2097
- // Set plugin config for the selected mode
2098
- if (effectiveRuntimeConfig.mode === "local") {
2099
- const ovConfPath = effectiveRuntimeConfig.configPath || join(OPENVIKING_DIR, "ov.conf");
2100
- await oc(["config", "set", `plugins.entries.${pluginId}.config.mode`, "local"]);
2101
- await oc(["config", "set", `plugins.entries.${pluginId}.config.configPath`, ovConfPath]);
2102
- await oc(["config", "set", `plugins.entries.${pluginId}.config.port`, String(effectiveRuntimeConfig.port || DEFAULT_SERVER_PORT)]);
2103
- } else {
2104
- await oc(["config", "set", `plugins.entries.${pluginId}.config.mode`, "remote"]);
2105
- await oc(["config", "set", `plugins.entries.${pluginId}.config.baseUrl`, effectiveRuntimeConfig.baseUrl || remoteBaseUrl]);
2106
- if (effectiveRuntimeConfig.apiKey) {
2107
- await oc(["config", "set", `plugins.entries.${pluginId}.config.apiKey`, effectiveRuntimeConfig.apiKey]);
2108
- }
2109
- if (effectiveRuntimeConfig.agentId) {
2110
- await oc(["config", "set", `plugins.entries.${pluginId}.config.agentId`, effectiveRuntimeConfig.agentId]);
2111
- }
1456
+ await oc(["config", "set", `plugins.entries.${pluginId}.config.baseUrl`, effectiveRuntimeConfig.baseUrl || remoteBaseUrl]);
1457
+ if (effectiveRuntimeConfig.apiKey) {
1458
+ await oc(["config", "set", `plugins.entries.${pluginId}.config.apiKey`, effectiveRuntimeConfig.apiKey]);
1459
+ }
1460
+ if (effectiveRuntimeConfig.agentId) {
1461
+ await oc(["config", "set", `plugins.entries.${pluginId}.config.agentId`, effectiveRuntimeConfig.agentId]);
2112
1462
  }
2113
1463
 
2114
1464
  // Legacy (memory) plugins need explicit targetUri/autoRecall/autoCapture (new version has defaults in config.ts)
@@ -2121,75 +1471,9 @@ async function configureOpenClawPlugin({
2121
1471
  info(tr("OpenClaw plugin configured", "OpenClaw 插件配置完成"));
2122
1472
  }
2123
1473
 
2124
- async function discoverOpenvikingPython(failedPy) {
2125
- const candidates = IS_WIN
2126
- ? ["python3", "python", "py -3"]
2127
- : ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python"];
2128
- for (const candidate of candidates) {
2129
- if (candidate === failedPy) continue;
2130
- const resolved = await resolveAbsoluteCommand(candidate);
2131
- if (!resolved || resolved === candidate || resolved === failedPy) continue;
2132
- const check = await runCapture(resolved, ["-c", "import openviking"], { shell: false });
2133
- if (check.code === 0) return resolved;
2134
- }
2135
- return "";
2136
- }
2137
-
2138
- async function resolvePythonPath() {
2139
- if (openvikingPythonPath) return openvikingPythonPath;
2140
- const python = await checkPython();
2141
- return python.cmd || "";
2142
- }
2143
-
2144
- async function writeOpenvikingEnv({ includePython }) {
1474
+ async function writeOpenvikingEnv() {
2145
1475
  const needStateDir = OPENCLAW_DIR !== DEFAULT_OPENCLAW_DIR;
2146
- let pythonPath = "";
2147
- if (includePython) {
2148
- pythonPath = await resolvePythonPath();
2149
- if (!pythonPath) {
2150
- pythonPath = (process.env.OPENVIKING_PYTHON || "").trim() || (IS_WIN ? "python" : "python3");
2151
- warn(
2152
- tr(
2153
- "Could not resolve absolute Python path; wrote fallback OPENVIKING_PYTHON to openviking.env. Edit that file if OpenViking fails to start.",
2154
- "未能解析 Python 绝对路径,已在 openviking.env 中写入后备值。若启动失败请手动修改为虚拟环境中的 python 可执行文件路径。",
2155
- ),
2156
- );
2157
- }
2158
-
2159
- // Verify the resolved Python can actually import openviking
2160
- if (pythonPath) {
2161
- const verify = await runCapture(pythonPath, ["-c", "import openviking"], { shell: false });
2162
- if (verify.code !== 0) {
2163
- warn(
2164
- tr(
2165
- `Resolved Python (${pythonPath}) cannot import openviking. The pip install target may differ from the runtime python3.`,
2166
- `解析到的 Python(${pythonPath})无法 import openviking。pip 安装目标可能与运行时的 python3 不一致。`,
2167
- ),
2168
- );
2169
- // Try to discover the correct Python via pip show
2170
- const corrected = await discoverOpenvikingPython(pythonPath);
2171
- if (corrected) {
2172
- info(
2173
- tr(
2174
- `Auto-corrected OPENVIKING_PYTHON to ${corrected}`,
2175
- `已自动修正 OPENVIKING_PYTHON 为 ${corrected}`,
2176
- ),
2177
- );
2178
- pythonPath = corrected;
2179
- } else {
2180
- warn(
2181
- tr(
2182
- `Could not auto-detect the correct Python. Edit OPENVIKING_PYTHON in the env file manually.`,
2183
- `无法自动检测正确的 Python。请手动修改 env 文件中的 OPENVIKING_PYTHON。`,
2184
- ),
2185
- );
2186
- }
2187
- }
2188
- }
2189
- }
2190
-
2191
- // Remote mode + default state dir + no python line → nothing to persist
2192
- if (!needStateDir && !pythonPath) return null;
1476
+ if (!needStateDir) return null;
2193
1477
 
2194
1478
  await mkdir(OPENCLAW_DIR, { recursive: true });
2195
1479
 
@@ -2197,14 +1481,8 @@ async function writeOpenvikingEnv({ includePython }) {
2197
1481
  const batLines = ["@echo off"];
2198
1482
  const psLines = [];
2199
1483
 
2200
- if (needStateDir) {
2201
- batLines.push(`set "OPENCLAW_STATE_DIR=${OPENCLAW_DIR.replace(/"/g, '""')}"`);
2202
- psLines.push(`$env:OPENCLAW_STATE_DIR = "${OPENCLAW_DIR.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
2203
- }
2204
- if (pythonPath) {
2205
- batLines.push(`set "OPENVIKING_PYTHON=${pythonPath.replace(/"/g, '""')}"`);
2206
- psLines.push(`$env:OPENVIKING_PYTHON = "${pythonPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
2207
- }
1484
+ batLines.push(`set "OPENCLAW_STATE_DIR=${OPENCLAW_DIR.replace(/"/g, '""')}"`);
1485
+ psLines.push(`$env:OPENCLAW_STATE_DIR = "${OPENCLAW_DIR.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`);
2208
1486
 
2209
1487
  const batPath = join(OPENCLAW_DIR, "openviking.env.bat");
2210
1488
  const ps1Path = join(OPENCLAW_DIR, "openviking.env.ps1");
@@ -2215,16 +1493,12 @@ async function writeOpenvikingEnv({ includePython }) {
2215
1493
  return { shellPath: batPath, powershellPath: ps1Path };
2216
1494
  }
2217
1495
 
2218
- const lines = [];
2219
- if (needStateDir) {
2220
- lines.push(`export OPENCLAW_STATE_DIR='${OPENCLAW_DIR.replace(/'/g, "'\"'\"'")}'`);
2221
- }
2222
- if (pythonPath) {
2223
- lines.push(`export OPENVIKING_PYTHON='${pythonPath.replace(/'/g, "'\"'\"'")}'`);
2224
- }
2225
-
2226
1496
  const envPath = join(OPENCLAW_DIR, "openviking.env");
2227
- await writeFile(envPath, `${lines.join("\n")}\n`, "utf8");
1497
+ await writeFile(
1498
+ envPath,
1499
+ `export OPENCLAW_STATE_DIR='${OPENCLAW_DIR.replace(/'/g, "'\"'\"'")}'\n`,
1500
+ "utf8",
1501
+ );
2228
1502
  info(tr(`Environment file generated: ${envPath}`, `已生成环境文件: ${envPath}`));
2229
1503
  return { shellPath: envPath };
2230
1504
  }
@@ -2252,24 +1526,11 @@ function getExistingEnvFiles() {
2252
1526
  return existsSync(envPath) ? { shellPath: envPath } : null;
2253
1527
  }
2254
1528
 
2255
- function ensureExistingPluginForUpgrade() {
2256
- if (!existsSync(PLUGIN_DEST)) {
2257
- err(
2258
- tr(
2259
- `Plugin upgrade mode expects an existing plugin at ${PLUGIN_DEST}. Run the full installer first if this is a fresh install.`,
2260
- `插件升级模式要求 ${PLUGIN_DEST} 处已存在插件安装。若是首次安装,请先运行完整安装流程。`,
2261
- ),
2262
- );
2263
- process.exit(1);
2264
- }
2265
- }
2266
-
2267
1529
  async function main() {
2268
1530
  console.log("");
2269
- bold(tr("🦣 OpenClaw + OpenViking Installer", "🦣 OpenClaw + OpenViking 一键安装"));
1531
+ bold(tr("🦣 OpenClaw OpenViking plugin installer", "🦣 OpenClaw OpenViking 插件安装"));
2270
1532
  console.log("");
2271
1533
 
2272
- ensurePluginOnlyOperationArgs();
2273
1534
  await selectWorkdir();
2274
1535
  if (showCurrentVersion) {
2275
1536
  await printCurrentVersionInfo();
@@ -2288,15 +1549,9 @@ async function main() {
2288
1549
  info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
2289
1550
  info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
2290
1551
  info(tr(`Plugin version: ${PLUGIN_VERSION}`, `插件版本: ${PLUGIN_VERSION}`));
2291
- if (openvikingVersion) {
2292
- info(tr(`OpenViking version: ${openvikingVersion}`, `OpenViking 版本: ${openvikingVersion}`));
2293
- }
2294
1552
 
2295
1553
  if (upgradePluginOnly) {
2296
- selectedMode = "local";
2297
- info("Mode: plugin upgrade only (backup old plugin, clean only OpenViking plugin config, keep ov.conf)");
2298
- } else {
2299
- await selectMode();
1554
+ info(tr("Mode: plugin upgrade only", "模式: 仅升级插件"));
2300
1555
  }
2301
1556
  info(tr(`Mode: ${selectedMode}`, `模式: ${selectedMode}`));
2302
1557
 
@@ -2305,15 +1560,6 @@ async function main() {
2305
1560
  await resolvePluginConfig();
2306
1561
  await checkOpenClawCompatibility();
2307
1562
  await prepareStrongPluginUpgrade();
2308
- } else if (selectedMode === "local") {
2309
- await validateEnvironment();
2310
- await checkOpenClaw();
2311
- // Resolve plugin config after OpenClaw is available (for version detection)
2312
- await resolvePluginConfig();
2313
- await checkOpenClawCompatibility();
2314
- checkRequestedOpenVikingCompatibility();
2315
- await installOpenViking();
2316
- await configureOvConf();
2317
1563
  } else {
2318
1564
  await checkOpenClaw();
2319
1565
  await resolvePluginConfig();
@@ -2321,25 +1567,12 @@ async function main() {
2321
1567
  await collectRemoteConfig();
2322
1568
  }
2323
1569
 
2324
- let pluginPath;
2325
- const localPluginDir = openvikingRepo ? join(openvikingRepo, "examples", resolvedPluginDir || "openclaw-plugin") : "";
2326
- if (openvikingRepo && existsSync(join(localPluginDir, "index.ts"))) {
2327
- pluginPath = localPluginDir;
2328
- PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId || "openviking");
2329
- info(tr(`Using local plugin from repo: ${pluginPath}`, `使用仓库内插件: ${pluginPath}`));
2330
- await deployPluginFromLocal(pluginPath);
2331
- info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
2332
- pluginPath = PLUGIN_DEST;
2333
- } else {
2334
- await deployPluginFromRemote();
2335
- pluginPath = PLUGIN_DEST;
2336
- }
1570
+ await deployPluginFromRemote();
2337
1571
 
2338
1572
  await configureOpenClawPlugin(
2339
1573
  upgradePluginOnly
2340
1574
  ? {
2341
1575
  runtimeConfig: upgradeRuntimeConfig,
2342
- skipGatewayMode: true,
2343
1576
  claimSlot: installedUpgradeState ? shouldClaimTargetSlot(installedUpgradeState) : true,
2344
1577
  }
2345
1578
  : { preserveExistingConfig: false },
@@ -2356,11 +1589,9 @@ async function main() {
2356
1589
  }
2357
1590
  let envFiles = getExistingEnvFiles();
2358
1591
  if (!upgradePluginOnly) {
2359
- envFiles = await writeOpenvikingEnv({
2360
- includePython: selectedMode === "local",
2361
- });
1592
+ envFiles = await writeOpenvikingEnv();
2362
1593
  } else if (!envFiles && OPENCLAW_DIR !== DEFAULT_OPENCLAW_DIR) {
2363
- envFiles = await writeOpenvikingEnv({ includePython: false });
1594
+ envFiles = await writeOpenvikingEnv();
2364
1595
  }
2365
1596
 
2366
1597
  console.log("");
@@ -2379,30 +1610,14 @@ async function main() {
2379
1610
  console.log("");
2380
1611
  }
2381
1612
 
2382
- if (selectedMode === "local") {
2383
- info(tr("Run these commands to start OpenClaw + OpenViking:", "请按以下命令启动 OpenClaw + OpenViking:"));
2384
- } else {
2385
- info(tr("Run these commands to start OpenClaw:", "请按以下命令启动 OpenClaw:"));
2386
- }
1613
+ info(tr("Run these commands to start OpenClaw:", "请按以下命令启动 OpenClaw:"));
2387
1614
  console.log(` 1) ${wrapCommand("openclaw --version", envFiles)}`);
2388
1615
  console.log(` 2) ${wrapCommand("openclaw onboard", envFiles)}`);
2389
1616
  console.log(` 3) ${wrapCommand("openclaw gateway", envFiles)}`);
2390
1617
  console.log(` 4) ${wrapCommand("openclaw status", envFiles)}`);
2391
1618
  console.log("");
2392
1619
 
2393
- if (selectedMode === "local") {
2394
- if (envFiles?.shellPath && !IS_WIN) {
2395
- info(
2396
- tr(
2397
- 'If source fails, set: export OPENVIKING_PYTHON="$(command -v python3)"',
2398
- '若 source 失败,可执行: export OPENVIKING_PYTHON="$(command -v python3)"',
2399
- ),
2400
- );
2401
- }
2402
- info(tr(`You can edit the config freely: ${OPENVIKING_DIR}/ov.conf`, `你可以按需自由修改配置文件: ${OPENVIKING_DIR}/ov.conf`));
2403
- } else {
2404
- info(tr(`Remote server: ${remoteBaseUrl}`, `远程服务器: ${remoteBaseUrl}`));
2405
- }
1620
+ info(tr(`OpenViking server URL (plugin): ${remoteBaseUrl}`, `OpenViking 服务地址(插件): ${remoteBaseUrl}`));
2406
1621
  console.log("");
2407
1622
  }
2408
1623
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-openviking-setup-helper",
3
- "version": "0.2.15",
3
+ "version": "0.2.16-dev.1",
4
4
  "description": "Setup helper for installing OpenViking memory plugin into OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {