openclaw-openviking-setup-helper 0.3.0-beta.21 → 0.3.0-beta.23

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 +295 -100
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -29,10 +29,12 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
29
29
 
30
30
  let REPO = process.env.REPO || "volcengine/OpenViking";
31
31
  // PLUGIN_VERSION takes precedence over BRANCH (legacy). If omitted, resolve the latest tag from GitHub.
32
- const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
33
- let PLUGIN_VERSION = pluginVersionEnv;
34
- let pluginVersionExplicit = Boolean(pluginVersionEnv);
35
- const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
32
+ const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
33
+ let PLUGIN_VERSION = pluginVersionEnv;
34
+ let pluginVersionExplicit = Boolean(pluginVersionEnv);
35
+ const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
36
+ const DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION = "2026.5.3";
37
+ const OPENCLAW_SHORT_VERSION_YEAR = 2026;
36
38
 
37
39
  const IS_WIN = process.platform === "win32";
38
40
  const HOME = process.env.HOME || process.env.USERPROFILE || "";
@@ -44,12 +46,13 @@ let PLUGIN_DEST = ""; // Will be set after resolving plugin config
44
46
  // Fallback configs for old versions without manifest
45
47
  const FALLBACK_LEGACY = {
46
48
  dir: "openclaw-memory-plugin",
47
- id: "memory-openviking",
48
- kind: "memory",
49
- slot: "memory",
50
- required: ["index.ts", "config.ts", "client.ts", "openclaw.plugin.json", "package.json"],
51
- optional: ["package-lock.json", ".gitignore", "memory-ranking.ts", "text-utils.ts", "process-manager.ts", "tsconfig.json"],
52
- };
49
+ id: "memory-openviking",
50
+ kind: "memory",
51
+ slot: "memory",
52
+ minOpenclawVersion: "2026.3.7",
53
+ required: ["index.ts", "config.ts", "client.ts", "openclaw.plugin.json", "package.json"],
54
+ optional: ["package-lock.json", ".gitignore", "memory-ranking.ts", "text-utils.ts", "process-manager.ts", "tsconfig.json"],
55
+ };
53
56
 
54
57
  // Must match examples/openclaw-plugin/install-manifest.json (npm only installs package deps, not these .ts files).
55
58
  const FALLBACK_CURRENT = {
@@ -57,15 +60,19 @@ const FALLBACK_CURRENT = {
57
60
  id: "openviking",
58
61
  kind: "context-engine",
59
62
  slot: "contextEngine",
63
+ minOpenclawVersion: "2026.4.24",
60
64
  required: ["index.ts", "config.ts", "package.json", "openclaw.plugin.json"],
61
65
  optional: [
62
66
  "context-engine.ts",
67
+ "auto-recall.ts",
63
68
  "client.ts",
64
69
  "process-manager.ts",
65
70
  "memory-ranking.ts",
66
71
  "text-utils.ts",
67
72
  "tool-call-id.ts",
68
73
  "session-transcript-repair.ts",
74
+ "runtime-utils.ts",
75
+ "commands/setup.ts",
69
76
  "tsconfig.json",
70
77
  "package-lock.json",
71
78
  ".gitignore",
@@ -82,12 +89,17 @@ let resolvedPluginDir = "";
82
89
  let resolvedPluginId = "";
83
90
  let resolvedPluginKind = "";
84
91
  let resolvedPluginSlot = "";
85
- let resolvedFilesRequired = [];
86
- let resolvedFilesOptional = [];
87
- let resolvedNpmOmitDev = true;
88
- let resolvedMinOpenclawVersion = "";
89
- let resolvedMinOpenvikingVersion = "";
90
- let resolvedPluginReleaseId = "";
92
+ let resolvedFilesRequired = [];
93
+ let resolvedFilesOptional = [];
94
+ let resolvedNpmOmitDev = true;
95
+ let resolvedNpmBuild = false;
96
+ let resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
97
+ let resolvedNpmBuildScript = "build";
98
+ let resolvedNpmPruneAfterBuild = true;
99
+ let resolvedMinOpenclawVersion = "";
100
+ let resolvedMinOpenvikingVersion = "";
101
+ let resolvedPluginReleaseId = "";
102
+ let detectedOpenClawVersion = "";
91
103
 
92
104
  let nonInteractive = false;
93
105
  let langZh = false;
@@ -402,15 +414,36 @@ function question(prompt, defaultValue = "") {
402
414
  });
403
415
  }
404
416
 
405
- function isValidAgentPrefixInput(value) {
406
- const trimmed = String(value || "").trim();
407
- return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
408
- }
409
-
410
- async function questionAgentPrefix(defaultValue = "") {
411
- while (true) {
412
- const answer = (await question(
413
- tr("Agent Prefix (optional)", "Agent Prefix(可选)"),
417
+ function isValidAgentPrefixInput(value) {
418
+ const trimmed = String(value || "").trim();
419
+ return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
420
+ }
421
+
422
+ function parseJsonObjectFromOutput(output) {
423
+ const text = String(output || "").trim();
424
+ if (!text) return null;
425
+ try {
426
+ return JSON.parse(text);
427
+ } catch {
428
+ // OpenClaw may print plugin registration logs before --json output.
429
+ }
430
+ for (let index = text.lastIndexOf("{"); index >= 0; index = text.lastIndexOf("{", index - 1)) {
431
+ try {
432
+ const parsed = JSON.parse(text.slice(index).trim());
433
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
434
+ return parsed;
435
+ }
436
+ } catch {
437
+ // Keep scanning earlier braces until the outer JSON object is found.
438
+ }
439
+ }
440
+ return null;
441
+ }
442
+
443
+ async function questionAgentPrefix(defaultValue = "") {
444
+ while (true) {
445
+ const answer = (await question(
446
+ tr("Agent Prefix (optional)", "Agent Prefix(可选)"),
414
447
  defaultValue,
415
448
  )).trim();
416
449
  if (isValidAgentPrefixInput(answer)) {
@@ -497,23 +530,68 @@ async function checkOpenClaw() {
497
530
  }
498
531
 
499
532
  // Compare versions: returns true if v1 >= v2
500
- function versionGte(v1, v2) {
501
- const parseVersion = (v) => {
502
- const cleaned = v.replace(/^v/, "").replace(/-.*$/, "");
503
- const parts = cleaned.split(".").map((p) => Number.parseInt(p, 10) || 0);
504
- while (parts.length < 3) parts.push(0);
533
+ function versionGte(v1, v2) {
534
+ const parseVersion = (v) => {
535
+ const cleaned = v.replace(/^v/, "").replace(/-.*$/, "");
536
+ const parts = cleaned.split(".").map((p) => Number.parseInt(p, 10) || 0);
537
+ while (parts.length < 3) parts.push(0);
505
538
  return parts;
506
539
  };
507
540
  const [a1, a2, a3] = parseVersion(v1);
508
541
  const [b1, b2, b3] = parseVersion(v2);
509
542
  if (a1 !== b1) return a1 > b1;
510
- if (a2 !== b2) return a2 > b2;
511
- return a3 >= b3;
512
- }
513
-
514
- function isSemverLike(value) {
515
- return /^v?\d+(\.\d+){1,2}$/.test(value);
516
- }
543
+ if (a2 !== b2) return a2 > b2;
544
+ return a3 >= b3;
545
+ }
546
+
547
+ function parseOpenClawPolicyVersion(value) {
548
+ const parts = String(value || "")
549
+ .match(/\d+/g)
550
+ ?.map((part) => Number.parseInt(part, 10) || 0) || [];
551
+ if (parts.length === 0) return [0, 0, 0];
552
+ if (parts[0] >= 2000) {
553
+ return [parts[0], parts[1] || 0, parts[2] || 0];
554
+ }
555
+ return [OPENCLAW_SHORT_VERSION_YEAR, parts[0] || 0, parts[1] || 0];
556
+ }
557
+
558
+ function openClawPolicyVersionGte(v1, v2) {
559
+ const a = parseOpenClawPolicyVersion(v1);
560
+ const b = parseOpenClawPolicyVersion(v2);
561
+ for (let i = 0; i < 3; i++) {
562
+ if (a[i] !== b[i]) return a[i] > b[i];
563
+ }
564
+ return true;
565
+ }
566
+
567
+ function applyOpenClawBuildPolicy(openClawVersion) {
568
+ if (!resolvedNpmBuild || !resolvedNpmBuildMinOpenclawVersion) {
569
+ return;
570
+ }
571
+ if (!openClawVersion || openClawVersion === "0.0.0") {
572
+ warn(tr(
573
+ "Could not determine OpenClaw version; keeping plugin source build enabled.",
574
+ "无法确定 OpenClaw 版本,保持插件源码构建开启。",
575
+ ));
576
+ return;
577
+ }
578
+ if (openClawPolicyVersionGte(openClawVersion, resolvedNpmBuildMinOpenclawVersion)) {
579
+ info(tr(
580
+ `OpenClaw ${openClawVersion} requires plugin source build (>= ${resolvedNpmBuildMinOpenclawVersion})`,
581
+ `OpenClaw ${openClawVersion} 需要插件源码构建(>= ${resolvedNpmBuildMinOpenclawVersion})`,
582
+ ));
583
+ return;
584
+ }
585
+ resolvedNpmBuild = false;
586
+ info(tr(
587
+ `OpenClaw ${openClawVersion} is below ${resolvedNpmBuildMinOpenclawVersion}; skipping plugin source build`,
588
+ `OpenClaw ${openClawVersion} 低于 ${resolvedNpmBuildMinOpenclawVersion},跳过插件源码构建`,
589
+ ));
590
+ }
591
+
592
+ function isSemverLike(value) {
593
+ return /^v?\d+(\.\d+){1,2}$/.test(value);
594
+ }
517
595
 
518
596
  function validateRequestedPluginVersion() {
519
597
  if (!isSemverLike(PLUGIN_VERSION)) return;
@@ -534,16 +612,24 @@ if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
534
612
  }
535
613
 
536
614
  // Detect OpenClaw version
537
- async function detectOpenClawVersion() {
538
- try {
539
- const result = await runCapture("openclaw", ["--version"], { shell: IS_WIN });
540
- if (result.code === 0 && result.out) {
541
- const match = result.out.match(/\d+\.\d+(\.\d+)?/);
542
- if (match) return match[0];
543
- }
544
- } catch {}
545
- return "0.0.0";
546
- }
615
+ async function detectOpenClawVersion() {
616
+ if (detectedOpenClawVersion) {
617
+ return detectedOpenClawVersion;
618
+ }
619
+ try {
620
+ const result = await runCapture("openclaw", ["--version"], { shell: IS_WIN });
621
+ const output = `${result.out || ""}\n${result.err || ""}`;
622
+ if (result.code === 0 && output) {
623
+ const match = output.match(/\d+\.\d+(\.\d+)?/);
624
+ if (match) {
625
+ detectedOpenClawVersion = match[0];
626
+ return detectedOpenClawVersion;
627
+ }
628
+ }
629
+ } catch {}
630
+ detectedOpenClawVersion = "0.0.0";
631
+ return detectedOpenClawVersion;
632
+ }
547
633
 
548
634
  // Try to fetch a URL, return response text or null
549
635
  async function tryFetch(url, timeout = 15000) {
@@ -686,13 +772,19 @@ async function resolveDefaultPluginVersion() {
686
772
  }
687
773
 
688
774
  // Resolve plugin configuration from manifest or fallback
689
- async function resolvePluginConfig() {
690
- const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
691
-
692
- info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
693
-
694
- let pluginDir = "";
695
- let manifestData = null;
775
+ async function resolvePluginConfig() {
776
+ const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
777
+
778
+ info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
779
+
780
+ resolvedNpmOmitDev = true;
781
+ resolvedNpmBuild = false;
782
+ resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
783
+ resolvedNpmBuildScript = "build";
784
+ resolvedNpmPruneAfterBuild = true;
785
+
786
+ let pluginDir = "";
787
+ let manifestData = null;
696
788
 
697
789
  // Try to detect plugin directory and download manifest
698
790
  const manifestCurrent = await tryFetch(`${ghRaw}/examples/openclaw-plugin/install-manifest.json`);
@@ -729,13 +821,25 @@ async function resolvePluginConfig() {
729
821
  resolvedPluginId = manifestData.plugin?.id || "";
730
822
  resolvedPluginKind = manifestData.plugin?.kind || "";
731
823
  resolvedPluginSlot = manifestData.plugin?.slot || "";
732
- resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
733
- resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
734
- resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
735
- resolvedNpmOmitDev = manifestData.npm?.omitDev !== false;
736
- resolvedFilesRequired = manifestData.files?.required || [];
737
- resolvedFilesOptional = manifestData.files?.optional || [];
738
- } else {
824
+ resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
825
+ resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
826
+ resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
827
+ const npmConfig = manifestData.npm && typeof manifestData.npm === "object"
828
+ ? manifestData.npm
829
+ : {};
830
+ resolvedNpmOmitDev = npmConfig.omitDev !== false;
831
+ resolvedNpmBuild = npmConfig.build === true || npmConfig.buildFromSource === true;
832
+ resolvedNpmBuildMinOpenclawVersion =
833
+ typeof npmConfig.buildMinOpenclawVersion === "string" && npmConfig.buildMinOpenclawVersion.trim()
834
+ ? npmConfig.buildMinOpenclawVersion.trim()
835
+ : DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
836
+ resolvedNpmBuildScript = typeof npmConfig.buildScript === "string" && npmConfig.buildScript.trim()
837
+ ? npmConfig.buildScript.trim()
838
+ : "build";
839
+ resolvedNpmPruneAfterBuild = npmConfig.pruneAfterBuild !== false;
840
+ resolvedFilesRequired = manifestData.files?.required || [];
841
+ resolvedFilesOptional = manifestData.files?.optional || [];
842
+ } else {
739
843
  // No manifest — determine plugin identity by package.json name
740
844
  let fallbackKey = pluginDir === "openclaw-memory-plugin" ? "legacy" : "current";
741
845
  let compatVer = "";
@@ -764,11 +868,15 @@ async function resolvePluginConfig() {
764
868
  resolvedPluginId = fallback.id;
765
869
  resolvedPluginKind = fallback.kind;
766
870
  resolvedPluginSlot = fallback.slot;
767
- resolvedFilesRequired = fallback.required;
768
- resolvedFilesOptional = fallback.optional;
769
- resolvedNpmOmitDev = true;
770
-
771
- // If no compatVer from package.json, try main branch manifest
871
+ resolvedFilesRequired = fallback.required;
872
+ resolvedFilesOptional = fallback.optional;
873
+ resolvedNpmOmitDev = true;
874
+ resolvedNpmBuild = false;
875
+ resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
876
+ resolvedNpmBuildScript = "build";
877
+ resolvedNpmPruneAfterBuild = true;
878
+
879
+ // If no compatVer from package.json, try main branch manifest
772
880
  if (!compatVer && PLUGIN_VERSION !== "main") {
773
881
  const mainRaw = `https://raw.githubusercontent.com/${REPO}/main`;
774
882
  const mainManifest = await tryFetch(`${mainRaw}/examples/openclaw-plugin/install-manifest.json`);
@@ -783,7 +891,7 @@ async function resolvePluginConfig() {
783
891
  }
784
892
  }
785
893
 
786
- resolvedMinOpenclawVersion = compatVer || "2026.3.7";
894
+ resolvedMinOpenclawVersion = compatVer || fallback.minOpenclawVersion || "2026.3.7";
787
895
  resolvedMinOpenvikingVersion = "";
788
896
  }
789
897
 
@@ -799,11 +907,12 @@ async function checkOpenClawCompatibility() {
799
907
  return;
800
908
  }
801
909
 
802
- const ocVersion = await detectOpenClawVersion();
803
- info(tr(`Detected OpenClaw version: ${ocVersion}`, `检测到 OpenClaw 版本: ${ocVersion}`));
804
-
805
- // If no minimum version required, pass
806
- if (!resolvedMinOpenclawVersion) {
910
+ const ocVersion = await detectOpenClawVersion();
911
+ info(tr(`Detected OpenClaw version: ${ocVersion}`, `检测到 OpenClaw 版本: ${ocVersion}`));
912
+ applyOpenClawBuildPolicy(ocVersion);
913
+
914
+ // If no minimum version required, pass
915
+ if (!resolvedMinOpenclawVersion) {
807
916
  return;
808
917
  }
809
918
 
@@ -1309,7 +1418,7 @@ async function prepareStrongPluginUpgrade() {
1309
1418
  info(tr(`Upgrade audit file: ${getUpgradeAuditPath()}`, `升级审计文件: ${getUpgradeAuditPath()}`));
1310
1419
  }
1311
1420
 
1312
- async function downloadPluginFile(destDir, fileName, url, required, index, total) {
1421
+ async function downloadPluginFile(destDir, fileName, url, required, index, total) {
1313
1422
  const maxRetries = 3;
1314
1423
  const destPath = join(destDir, fileName);
1315
1424
 
@@ -1369,12 +1478,109 @@ async function downloadPluginFile(destDir, fileName, url, required, index, total
1369
1478
 
1370
1479
  console.log("");
1371
1480
  err(tr(`Download failed after ${maxRetries} retries: ${url}`, `下载失败(已重试 ${maxRetries} 次): ${url}`));
1372
- process.exit(1);
1373
- }
1374
-
1375
- async function downloadPlugin(destDir) {
1376
- const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
1377
- const pluginDir = resolvedPluginDir;
1481
+ process.exit(1);
1482
+ }
1483
+
1484
+ function runtimeOutputCandidatesForEntry(entry) {
1485
+ const normalized = String(entry || "").replace(/\\/g, "/").replace(/^\.\//, "");
1486
+ if (!normalized.endsWith(".ts")) {
1487
+ return [];
1488
+ }
1489
+ const withoutExt = normalized.slice(0, -3);
1490
+ return [
1491
+ `dist/${withoutExt}.js`,
1492
+ `dist/${withoutExt}.mjs`,
1493
+ `dist/${withoutExt}.cjs`,
1494
+ `${withoutExt}.js`,
1495
+ `${withoutExt}.mjs`,
1496
+ `${withoutExt}.cjs`,
1497
+ ];
1498
+ }
1499
+
1500
+ async function assertBuiltRuntimeOutputs(destDir) {
1501
+ let pkg = null;
1502
+ try {
1503
+ pkg = JSON.parse(await readFile(join(destDir, "package.json"), "utf8"));
1504
+ } catch {
1505
+ return;
1506
+ }
1507
+
1508
+ const entries = [];
1509
+ const extensions = pkg?.openclaw?.extensions;
1510
+ if (Array.isArray(extensions)) {
1511
+ for (const entry of extensions) {
1512
+ if (typeof entry === "string") entries.push(entry);
1513
+ }
1514
+ }
1515
+ if (typeof pkg?.openclaw?.setupEntry === "string") {
1516
+ entries.push(pkg.openclaw.setupEntry);
1517
+ }
1518
+
1519
+ const missing = [];
1520
+ for (const entry of entries) {
1521
+ const candidates = runtimeOutputCandidatesForEntry(entry);
1522
+ if (candidates.length === 0) continue;
1523
+ const found = candidates.some((candidate) => existsSync(join(destDir, ...candidate.split("/"))));
1524
+ if (!found) {
1525
+ missing.push(`${entry} (expected one of: ${candidates.join(", ")})`);
1526
+ }
1527
+ }
1528
+
1529
+ if (missing.length === 0) {
1530
+ return;
1531
+ }
1532
+
1533
+ err(tr(
1534
+ `Plugin build did not create required runtime output:\n - ${missing.join("\n - ")}`,
1535
+ `插件构建未生成必需的运行时产物:\n - ${missing.join("\n - ")}`,
1536
+ ));
1537
+ process.exit(1);
1538
+ }
1539
+
1540
+ async function installPluginNpmDependencies(destDir) {
1541
+ if (!resolvedNpmBuild) {
1542
+ info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
1543
+ const npmArgs = resolvedNpmOmitDev
1544
+ ? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
1545
+ : ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
1546
+ await run("npm", npmArgs, { cwd: destDir, silent: false });
1547
+ return;
1548
+ }
1549
+
1550
+ info(tr(
1551
+ "Installing plugin npm dependencies for source build...",
1552
+ "正在安装插件源码构建所需的 npm 依赖...",
1553
+ ));
1554
+ await run("npm", [
1555
+ "install",
1556
+ "--include=dev",
1557
+ "--no-audit",
1558
+ "--no-fund",
1559
+ "--registry",
1560
+ NPM_REGISTRY,
1561
+ ], { cwd: destDir, silent: false });
1562
+
1563
+ info(tr(
1564
+ `Building plugin runtime output with npm run ${resolvedNpmBuildScript}...`,
1565
+ `正在执行 npm run ${resolvedNpmBuildScript} 构建插件运行时产物...`,
1566
+ ));
1567
+ await run("npm", ["run", resolvedNpmBuildScript], { cwd: destDir, silent: false });
1568
+ await assertBuiltRuntimeOutputs(destDir);
1569
+
1570
+ if (resolvedNpmOmitDev && resolvedNpmPruneAfterBuild) {
1571
+ info(tr("Pruning plugin dev dependencies...", "正在裁剪插件开发依赖..."));
1572
+ await run("npm", [
1573
+ "prune",
1574
+ "--omit=dev",
1575
+ "--no-audit",
1576
+ "--no-fund",
1577
+ ], { cwd: destDir, silent: false });
1578
+ }
1579
+ }
1580
+
1581
+ async function downloadPlugin(destDir) {
1582
+ const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
1583
+ const pluginDir = resolvedPluginDir;
1378
1584
  const total = resolvedFilesRequired.length + resolvedFilesOptional.length;
1379
1585
 
1380
1586
  await mkdir(destDir, { recursive: true });
@@ -1398,14 +1604,9 @@ async function downloadPlugin(destDir) {
1398
1604
  await downloadPluginFile(destDir, name, url, false, i, total);
1399
1605
  }
1400
1606
 
1401
- // npm install
1402
- info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
1403
- const npmArgs = resolvedNpmOmitDev
1404
- ? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
1405
- : ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
1406
- await run("npm", npmArgs, { cwd: destDir, silent: false });
1407
- info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
1408
- }
1607
+ await installPluginNpmDependencies(destDir);
1608
+ info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
1609
+ }
1409
1610
 
1410
1611
  async function createPluginStagingDir() {
1411
1612
  const pluginId = resolvedPluginId || "openviking";
@@ -1730,11 +1931,9 @@ async function configureOpenClawPlugin({
1730
1931
  }
1731
1932
  } catch { /* ignore read errors */ }
1732
1933
 
1733
- let setupResult = null;
1734
- if (setupJsonSupported) {
1735
- const setupArgs = needWorkdirFlag
1736
- ? ["--workdir", OPENCLAW_DIR, "openviking", "setup"]
1737
- : ["openviking", "setup"];
1934
+ let setupResult = null;
1935
+ if (setupJsonSupported) {
1936
+ const setupArgs = ["openviking", "setup"];
1738
1937
  setupArgs.push("--base-url", effectiveRuntimeConfig.baseUrl || remoteBaseUrl);
1739
1938
  setupArgs.push("--json");
1740
1939
  if (effectiveRuntimeConfig.apiKey) {
@@ -1769,14 +1968,10 @@ async function configureOpenClawPlugin({
1769
1968
  ));
1770
1969
  }
1771
1970
 
1772
- let parsed = null;
1773
- if (setupResult) {
1774
- try {
1775
- parsed = JSON.parse(setupResult.out.trim());
1776
- } catch {
1777
- // If JSON parse fails, fall back to checking exit code
1778
- }
1779
- }
1971
+ let parsed = null;
1972
+ if (setupResult) {
1973
+ parsed = parseJsonObjectFromOutput(`${setupResult.out || ""}\n${setupResult.err || ""}`);
1974
+ }
1780
1975
 
1781
1976
  if (parsed) {
1782
1977
  if (parsed.success) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-openviking-setup-helper",
3
- "version": "0.3.0-beta.21",
3
+ "version": "0.3.0-beta.23",
4
4
  "description": "Setup helper for installing OpenViking memory plugin into OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {