openclaw-openviking-setup-helper 0.2.9-dev.0 → 0.2.9-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.
- package/install.js +219 -127
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { spawn } from "node:child_process";
|
|
26
|
-
import { cp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
27
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
28
|
-
import { dirname, join, relative } from "node:path";
|
|
26
|
+
import { cp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
27
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
28
|
+
import { dirname, join, relative } from "node:path";
|
|
29
29
|
import { createInterface } from "node:readline";
|
|
30
30
|
import { fileURLToPath } from "node:url";
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ let REPO = process.env.REPO || "volcengine/OpenViking";
|
|
|
35
35
|
// PLUGIN_VERSION takes precedence over BRANCH (legacy)
|
|
36
36
|
let PLUGIN_VERSION = process.env.PLUGIN_VERSION || process.env.BRANCH || "main";
|
|
37
37
|
const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
38
|
-
const PIP_INDEX_URL = process.env.PIP_INDEX_URL || "https://mirrors.volces.com/pypi/simple/";
|
|
38
|
+
const PIP_INDEX_URL = process.env.PIP_INDEX_URL || "https://mirrors.volces.com/pypi/simple/";
|
|
39
39
|
|
|
40
40
|
const IS_WIN = process.platform === "win32";
|
|
41
41
|
const HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
@@ -66,8 +66,8 @@ const FALLBACK_CURRENT = {
|
|
|
66
66
|
id: "openviking",
|
|
67
67
|
kind: "context-engine",
|
|
68
68
|
slot: "contextEngine",
|
|
69
|
-
required: ["index.ts", "
|
|
70
|
-
optional: ["package-lock.json", ".gitignore"],
|
|
69
|
+
required: ["index.ts", "config.ts", "openclaw.plugin.json", "package.json"],
|
|
70
|
+
optional: ["context-engine.ts", "client.ts", "process-manager.ts", "memory-ranking.ts", "text-utils.ts", "session-transcript-repair.ts", "tool-call-id.ts", "tsconfig.json", "package-lock.json", ".gitignore"],
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
// Resolved plugin config (set by resolvePluginConfig)
|
|
@@ -75,11 +75,11 @@ let resolvedPluginDir = "";
|
|
|
75
75
|
let resolvedPluginId = "";
|
|
76
76
|
let resolvedPluginKind = "";
|
|
77
77
|
let resolvedPluginSlot = "";
|
|
78
|
-
let resolvedFilesRequired = [];
|
|
79
|
-
let resolvedFilesOptional = [];
|
|
80
|
-
let resolvedNpmOmitDev = true;
|
|
81
|
-
let resolvedMinOpenclawVersion = "";
|
|
82
|
-
let resolvedMinOpenvikingVersion = "";
|
|
78
|
+
let resolvedFilesRequired = [];
|
|
79
|
+
let resolvedFilesOptional = [];
|
|
80
|
+
let resolvedNpmOmitDev = true;
|
|
81
|
+
let resolvedMinOpenclawVersion = "";
|
|
82
|
+
let resolvedMinOpenvikingVersion = "";
|
|
83
83
|
|
|
84
84
|
let installYes = process.env.OPENVIKING_INSTALL_YES === "1";
|
|
85
85
|
let langZh = false;
|
|
@@ -296,8 +296,19 @@ function question(prompt, defaultValue = "") {
|
|
|
296
296
|
});
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
async function resolveAbsoluteCommand(cmd) {
|
|
300
|
+
if (cmd.startsWith("/") || (IS_WIN && /^[A-Za-z]:[/\\]/.test(cmd))) return cmd;
|
|
301
|
+
if (IS_WIN) {
|
|
302
|
+
const r = await runCapture("where", [cmd], { shell: true });
|
|
303
|
+
return r.out.split(/\r?\n/)[0]?.trim() || cmd;
|
|
304
|
+
}
|
|
305
|
+
const r = await runCapture("which", [cmd], { shell: false });
|
|
306
|
+
return r.out.trim() || cmd;
|
|
307
|
+
}
|
|
308
|
+
|
|
299
309
|
async function checkPython() {
|
|
300
|
-
const
|
|
310
|
+
const raw = process.env.OPENVIKING_PYTHON || (IS_WIN ? "python" : "python3");
|
|
311
|
+
const py = await resolveAbsoluteCommand(raw);
|
|
301
312
|
const result = await runCapture(py, ["-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"]);
|
|
302
313
|
if (result.code !== 0 || !result.out) {
|
|
303
314
|
return {
|
|
@@ -452,7 +463,7 @@ async function checkOpenClaw() {
|
|
|
452
463
|
}
|
|
453
464
|
|
|
454
465
|
// Compare versions: returns true if v1 >= v2
|
|
455
|
-
function versionGte(v1, v2) {
|
|
466
|
+
function versionGte(v1, v2) {
|
|
456
467
|
const parseVersion = (v) => {
|
|
457
468
|
const cleaned = v.replace(/^v/, "").replace(/-.*$/, "");
|
|
458
469
|
const parts = cleaned.split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
@@ -463,12 +474,12 @@ function versionGte(v1, v2) {
|
|
|
463
474
|
const [b1, b2, b3] = parseVersion(v2);
|
|
464
475
|
if (a1 !== b1) return a1 > b1;
|
|
465
476
|
if (a2 !== b2) return a2 > b2;
|
|
466
|
-
return a3 >= b3;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function isSemverLike(value) {
|
|
470
|
-
return /^v?\d+(\.\d+){1,2}$/.test(value);
|
|
471
|
-
}
|
|
477
|
+
return a3 >= b3;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function isSemverLike(value) {
|
|
481
|
+
return /^v?\d+(\.\d+){1,2}$/.test(value);
|
|
482
|
+
}
|
|
472
483
|
|
|
473
484
|
// Detect OpenClaw version
|
|
474
485
|
async function detectOpenClawVersion() {
|
|
@@ -547,19 +558,40 @@ async function resolvePluginConfig() {
|
|
|
547
558
|
|
|
548
559
|
resolvedPluginDir = pluginDir;
|
|
549
560
|
|
|
550
|
-
if (manifestData) {
|
|
551
|
-
resolvedPluginId = manifestData.plugin?.id || "";
|
|
552
|
-
resolvedPluginKind = manifestData.plugin?.kind || "";
|
|
553
|
-
resolvedPluginSlot = manifestData.plugin?.slot || "";
|
|
554
|
-
resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
|
|
555
|
-
resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
|
|
556
|
-
resolvedNpmOmitDev = manifestData.npm?.omitDev !== false;
|
|
557
|
-
resolvedFilesRequired = manifestData.files?.required || [];
|
|
558
|
-
resolvedFilesOptional = manifestData.files?.optional || [];
|
|
559
|
-
} else {
|
|
560
|
-
//
|
|
561
|
-
|
|
562
|
-
|
|
561
|
+
if (manifestData) {
|
|
562
|
+
resolvedPluginId = manifestData.plugin?.id || "";
|
|
563
|
+
resolvedPluginKind = manifestData.plugin?.kind || "";
|
|
564
|
+
resolvedPluginSlot = manifestData.plugin?.slot || "";
|
|
565
|
+
resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
|
|
566
|
+
resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
|
|
567
|
+
resolvedNpmOmitDev = manifestData.npm?.omitDev !== false;
|
|
568
|
+
resolvedFilesRequired = manifestData.files?.required || [];
|
|
569
|
+
resolvedFilesOptional = manifestData.files?.optional || [];
|
|
570
|
+
} else {
|
|
571
|
+
// No manifest — determine plugin identity by package.json name
|
|
572
|
+
let fallbackKey = pluginDir === "openclaw-memory-plugin" ? "legacy" : "current";
|
|
573
|
+
let compatVer = "";
|
|
574
|
+
|
|
575
|
+
const pkgJson = await tryFetch(`${ghRaw}/examples/${pluginDir}/package.json`);
|
|
576
|
+
if (pkgJson) {
|
|
577
|
+
try {
|
|
578
|
+
const pkg = JSON.parse(pkgJson);
|
|
579
|
+
const pkgName = pkg.name || "";
|
|
580
|
+
if (pkgName && pkgName !== "@openclaw/openviking") {
|
|
581
|
+
fallbackKey = "legacy";
|
|
582
|
+
info(tr(`Detected legacy plugin by package name: ${pkgName}`, `通过 package.json 名称检测到旧版插件: ${pkgName}`));
|
|
583
|
+
} else if (pkgName) {
|
|
584
|
+
fallbackKey = "current";
|
|
585
|
+
}
|
|
586
|
+
compatVer = (pkg.engines?.openclaw || "").replace(/^>=?\s*/, "").trim();
|
|
587
|
+
if (compatVer) {
|
|
588
|
+
info(tr(`Read minOpenclawVersion from package.json engines.openclaw: >=${compatVer}`, `从 package.json engines.openclaw 读取到最低版本: >=${compatVer}`));
|
|
589
|
+
}
|
|
590
|
+
} catch {}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const fallback = fallbackKey === "legacy" ? FALLBACK_LEGACY : FALLBACK_CURRENT;
|
|
594
|
+
resolvedPluginDir = pluginDir;
|
|
563
595
|
resolvedPluginId = fallback.id;
|
|
564
596
|
resolvedPluginKind = fallback.kind;
|
|
565
597
|
resolvedPluginSlot = fallback.slot;
|
|
@@ -567,13 +599,24 @@ async function resolvePluginConfig() {
|
|
|
567
599
|
resolvedFilesOptional = fallback.optional;
|
|
568
600
|
resolvedNpmOmitDev = true;
|
|
569
601
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
602
|
+
// If no compatVer from package.json, try main branch manifest
|
|
603
|
+
if (!compatVer && PLUGIN_VERSION !== "main") {
|
|
604
|
+
const mainRaw = `https://raw.githubusercontent.com/${REPO}/main`;
|
|
605
|
+
const mainManifest = await tryFetch(`${mainRaw}/examples/openclaw-plugin/install-manifest.json`);
|
|
606
|
+
if (mainManifest) {
|
|
607
|
+
try {
|
|
608
|
+
const m = JSON.parse(mainManifest);
|
|
609
|
+
compatVer = m.compatibility?.minOpenclawVersion || "";
|
|
610
|
+
if (compatVer) {
|
|
611
|
+
info(tr(`Read minOpenclawVersion from main branch manifest: >=${compatVer}`, `从 main 分支 manifest 读取到最低版本: >=${compatVer}`));
|
|
612
|
+
}
|
|
613
|
+
} catch {}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
resolvedMinOpenclawVersion = compatVer || "2026.3.7";
|
|
618
|
+
resolvedMinOpenvikingVersion = "";
|
|
619
|
+
}
|
|
577
620
|
|
|
578
621
|
// Set plugin destination
|
|
579
622
|
PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId);
|
|
@@ -596,9 +639,9 @@ async function checkOpenClawCompatibility() {
|
|
|
596
639
|
}
|
|
597
640
|
|
|
598
641
|
// If user explicitly requested an old version, pass
|
|
599
|
-
if (PLUGIN_VERSION !== "main" && isSemverLike(PLUGIN_VERSION) && !versionGte(PLUGIN_VERSION, "v0.2.9")) {
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
642
|
+
if (PLUGIN_VERSION !== "main" && isSemverLike(PLUGIN_VERSION) && !versionGte(PLUGIN_VERSION, "v0.2.9")) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
602
645
|
|
|
603
646
|
// Check compatibility
|
|
604
647
|
if (!versionGte(ocVersion, resolvedMinOpenclawVersion)) {
|
|
@@ -610,32 +653,32 @@ async function checkOpenClawCompatibility() {
|
|
|
610
653
|
bold(tr("Please choose one of the following options:", "请选择以下方案之一:"));
|
|
611
654
|
console.log("");
|
|
612
655
|
console.log(` ${tr("Option 1: Upgrade OpenClaw", "方案 1:升级 OpenClaw")}`);
|
|
613
|
-
console.log(` npm update -g openclaw --registry ${NPM_REGISTRY}`);
|
|
656
|
+
console.log(` npm update -g openclaw --registry ${NPM_REGISTRY}`);
|
|
614
657
|
console.log("");
|
|
615
658
|
console.log(` ${tr("Option 2: Install legacy plugin (v0.2.8)", "方案 2:安装旧版插件 (v0.2.8)")}`);
|
|
616
659
|
console.log(` node install.js --plugin-version=v0.2.8${langZh ? " --zh" : ""}`);
|
|
617
660
|
console.log("");
|
|
618
661
|
process.exit(1);
|
|
619
662
|
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function checkRequestedOpenVikingCompatibility() {
|
|
623
|
-
if (!resolvedMinOpenvikingVersion || !openvikingVersion) return;
|
|
624
|
-
if (versionGte(openvikingVersion, resolvedMinOpenvikingVersion)) return;
|
|
625
|
-
|
|
626
|
-
err(tr(
|
|
627
|
-
`OpenViking ${openvikingVersion} does not support this plugin (requires >= ${resolvedMinOpenvikingVersion})`,
|
|
628
|
-
`OpenViking ${openvikingVersion} 不支持此插件(需要 >= ${resolvedMinOpenvikingVersion})`,
|
|
629
|
-
));
|
|
630
|
-
console.log("");
|
|
631
|
-
console.log(tr(
|
|
632
|
-
"Use a newer OpenViking version, or omit --openviking-version to install the latest release.",
|
|
633
|
-
"请使用更新版本的 OpenViking,或省略 --openviking-version 以安装最新版本。",
|
|
634
|
-
));
|
|
635
|
-
process.exit(1);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
async function installOpenViking() {
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function checkRequestedOpenVikingCompatibility() {
|
|
666
|
+
if (!resolvedMinOpenvikingVersion || !openvikingVersion) return;
|
|
667
|
+
if (versionGte(openvikingVersion, resolvedMinOpenvikingVersion)) return;
|
|
668
|
+
|
|
669
|
+
err(tr(
|
|
670
|
+
`OpenViking ${openvikingVersion} does not support this plugin (requires >= ${resolvedMinOpenvikingVersion})`,
|
|
671
|
+
`OpenViking ${openvikingVersion} 不支持此插件(需要 >= ${resolvedMinOpenvikingVersion})`,
|
|
672
|
+
));
|
|
673
|
+
console.log("");
|
|
674
|
+
console.log(tr(
|
|
675
|
+
"Use a newer OpenViking version, or omit --openviking-version to install the latest release.",
|
|
676
|
+
"请使用更新版本的 OpenViking,或省略 --openviking-version 以安装最新版本。",
|
|
677
|
+
));
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async function installOpenViking() {
|
|
639
682
|
if (process.env.SKIP_OPENVIKING === "1") {
|
|
640
683
|
info(tr("Skipping OpenViking install (SKIP_OPENVIKING=1)", "跳过 OpenViking 安装 (SKIP_OPENVIKING=1)"));
|
|
641
684
|
return;
|
|
@@ -646,6 +689,12 @@ async function installOpenViking() {
|
|
|
646
689
|
err(tr("Python check failed.", "Python 校验失败"));
|
|
647
690
|
process.exit(1);
|
|
648
691
|
}
|
|
692
|
+
if (!python.ok) {
|
|
693
|
+
warn(tr(
|
|
694
|
+
`${python.detail}. Will attempt to find a suitable Python for pip install.`,
|
|
695
|
+
`${python.detail}。将尝试查找合适的 Python 进行 pip 安装。`,
|
|
696
|
+
));
|
|
697
|
+
}
|
|
649
698
|
|
|
650
699
|
const py = python.cmd;
|
|
651
700
|
|
|
@@ -809,20 +858,20 @@ async function configureOvConf() {
|
|
|
809
858
|
rotation_days: 3,
|
|
810
859
|
rotation_interval: "midnight",
|
|
811
860
|
},
|
|
812
|
-
embedding: {
|
|
813
|
-
dense: {
|
|
814
|
-
provider: "volcengine",
|
|
815
|
-
api_key: embeddingApiKey || null,
|
|
816
|
-
model: embeddingModel,
|
|
817
|
-
api_base: "https://ark.cn-beijing.volces.com/api/v3",
|
|
861
|
+
embedding: {
|
|
862
|
+
dense: {
|
|
863
|
+
provider: "volcengine",
|
|
864
|
+
api_key: embeddingApiKey || null,
|
|
865
|
+
model: embeddingModel,
|
|
866
|
+
api_base: "https://ark.cn-beijing.volces.com/api/v3",
|
|
818
867
|
dimension: 1024,
|
|
819
868
|
input: "multimodal",
|
|
820
869
|
},
|
|
821
|
-
},
|
|
822
|
-
vlm: {
|
|
823
|
-
provider: "volcengine",
|
|
824
|
-
api_key: vlmApiKey || null,
|
|
825
|
-
model: vlmModel,
|
|
870
|
+
},
|
|
871
|
+
vlm: {
|
|
872
|
+
provider: "volcengine",
|
|
873
|
+
api_key: vlmApiKey || null,
|
|
874
|
+
model: vlmModel,
|
|
826
875
|
api_base: "https://ark.cn-beijing.volces.com/api/v3",
|
|
827
876
|
temperature: 0.1,
|
|
828
877
|
max_retries: 3,
|
|
@@ -903,29 +952,29 @@ async function downloadPlugin() {
|
|
|
903
952
|
|
|
904
953
|
// npm install
|
|
905
954
|
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
906
|
-
const npmArgs = resolvedNpmOmitDev
|
|
907
|
-
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
908
|
-
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
909
|
-
await run("npm", npmArgs, { cwd: PLUGIN_DEST, silent: false });
|
|
955
|
+
const npmArgs = resolvedNpmOmitDev
|
|
956
|
+
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
957
|
+
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
958
|
+
await run("npm", npmArgs, { cwd: PLUGIN_DEST, silent: false });
|
|
910
959
|
info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
|
|
911
960
|
}
|
|
912
961
|
|
|
913
|
-
async function deployLocalPlugin(localPluginDir) {
|
|
914
|
-
await rm(PLUGIN_DEST, { recursive: true, force: true });
|
|
915
|
-
await mkdir(PLUGIN_DEST, { recursive: true });
|
|
916
|
-
await cp(localPluginDir, PLUGIN_DEST, {
|
|
917
|
-
recursive: true,
|
|
918
|
-
force: true,
|
|
919
|
-
filter: (sourcePath) => {
|
|
920
|
-
const rel = relative(localPluginDir, sourcePath);
|
|
921
|
-
if (!rel) return true;
|
|
922
|
-
const firstSegment = rel.split(/[\\/]/)[0];
|
|
923
|
-
return firstSegment !== "node_modules" && firstSegment !== ".git";
|
|
924
|
-
},
|
|
925
|
-
});
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
async function configureOpenClawPlugin() {
|
|
962
|
+
async function deployLocalPlugin(localPluginDir) {
|
|
963
|
+
await rm(PLUGIN_DEST, { recursive: true, force: true });
|
|
964
|
+
await mkdir(PLUGIN_DEST, { recursive: true });
|
|
965
|
+
await cp(localPluginDir, PLUGIN_DEST, {
|
|
966
|
+
recursive: true,
|
|
967
|
+
force: true,
|
|
968
|
+
filter: (sourcePath) => {
|
|
969
|
+
const rel = relative(localPluginDir, sourcePath);
|
|
970
|
+
if (!rel) return true;
|
|
971
|
+
const firstSegment = rel.split(/[\\/]/)[0];
|
|
972
|
+
return firstSegment !== "node_modules" && firstSegment !== ".git";
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
async function configureOpenClawPlugin() {
|
|
929
978
|
info(tr("Configuring OpenClaw plugin...", "正在配置 OpenClaw 插件..."));
|
|
930
979
|
|
|
931
980
|
const pluginId = resolvedPluginId;
|
|
@@ -936,18 +985,18 @@ async function configureOpenClawPlugin() {
|
|
|
936
985
|
ocEnv.OPENCLAW_STATE_DIR = OPENCLAW_DIR;
|
|
937
986
|
}
|
|
938
987
|
|
|
939
|
-
const oc = async (args) => {
|
|
940
|
-
const result = await runCapture("openclaw", args, { env: ocEnv, shell: IS_WIN });
|
|
941
|
-
if (result.code !== 0) {
|
|
942
|
-
const detail = result.err || result.out;
|
|
943
|
-
throw new Error(`openclaw ${args.join(" ")} failed (exit code ${result.code})${detail ? `: ${detail}` : ""}`);
|
|
944
|
-
}
|
|
945
|
-
return result;
|
|
946
|
-
};
|
|
947
|
-
|
|
948
|
-
// Enable plugin (files already deployed to extensions dir by deployPlugin)
|
|
949
|
-
await oc(["plugins", "enable", pluginId]);
|
|
950
|
-
await oc(["config", "set", `plugins.slots.${pluginSlot}`, pluginId]);
|
|
988
|
+
const oc = async (args) => {
|
|
989
|
+
const result = await runCapture("openclaw", args, { env: ocEnv, shell: IS_WIN });
|
|
990
|
+
if (result.code !== 0) {
|
|
991
|
+
const detail = result.err || result.out;
|
|
992
|
+
throw new Error(`openclaw ${args.join(" ")} failed (exit code ${result.code})${detail ? `: ${detail}` : ""}`);
|
|
993
|
+
}
|
|
994
|
+
return result;
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
// Enable plugin (files already deployed to extensions dir by deployPlugin)
|
|
998
|
+
await oc(["plugins", "enable", pluginId]);
|
|
999
|
+
await oc(["config", "set", `plugins.slots.${pluginSlot}`, pluginId]);
|
|
951
1000
|
|
|
952
1001
|
// Set gateway mode
|
|
953
1002
|
await oc(["config", "set", "gateway.mode", "local"]);
|
|
@@ -969,22 +1018,34 @@ async function configureOpenClawPlugin() {
|
|
|
969
1018
|
}
|
|
970
1019
|
}
|
|
971
1020
|
|
|
1021
|
+
// Legacy (memory) plugins need explicit targetUri/autoRecall/autoCapture (new version has defaults in config.ts)
|
|
1022
|
+
if (resolvedPluginKind === "memory") {
|
|
1023
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.targetUri`, "viking://user/memories"]);
|
|
1024
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.autoRecall`, "true", "--json"]);
|
|
1025
|
+
await oc(["config", "set", `plugins.entries.${pluginId}.config.autoCapture`, "true", "--json"]);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
972
1028
|
info(tr("OpenClaw plugin configured", "OpenClaw 插件配置完成"));
|
|
973
1029
|
}
|
|
974
1030
|
|
|
1031
|
+
async function discoverOpenvikingPython(failedPy) {
|
|
1032
|
+
const candidates = IS_WIN
|
|
1033
|
+
? ["python3", "python", "py -3"]
|
|
1034
|
+
: ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python"];
|
|
1035
|
+
for (const candidate of candidates) {
|
|
1036
|
+
if (candidate === failedPy) continue;
|
|
1037
|
+
const resolved = await resolveAbsoluteCommand(candidate);
|
|
1038
|
+
if (!resolved || resolved === candidate || resolved === failedPy) continue;
|
|
1039
|
+
const check = await runCapture(resolved, ["-c", "import openviking"], { shell: false });
|
|
1040
|
+
if (check.code === 0) return resolved;
|
|
1041
|
+
}
|
|
1042
|
+
return "";
|
|
1043
|
+
}
|
|
1044
|
+
|
|
975
1045
|
async function resolvePythonPath() {
|
|
976
1046
|
if (openvikingPythonPath) return openvikingPythonPath;
|
|
977
1047
|
const python = await checkPython();
|
|
978
|
-
|
|
979
|
-
if (!py) return "";
|
|
980
|
-
|
|
981
|
-
if (IS_WIN) {
|
|
982
|
-
const result = await runCapture("where", [py], { shell: true });
|
|
983
|
-
return result.out.split(/\r?\n/)[0]?.trim() || py;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
const result = await runCapture("which", [py], { shell: false });
|
|
987
|
-
return result.out.trim() || py;
|
|
1048
|
+
return python.cmd || "";
|
|
988
1049
|
}
|
|
989
1050
|
|
|
990
1051
|
async function writeOpenvikingEnv({ includePython }) {
|
|
@@ -1001,6 +1062,37 @@ async function writeOpenvikingEnv({ includePython }) {
|
|
|
1001
1062
|
),
|
|
1002
1063
|
);
|
|
1003
1064
|
}
|
|
1065
|
+
|
|
1066
|
+
// Verify the resolved Python can actually import openviking
|
|
1067
|
+
if (pythonPath) {
|
|
1068
|
+
const verify = await runCapture(pythonPath, ["-c", "import openviking"], { shell: false });
|
|
1069
|
+
if (verify.code !== 0) {
|
|
1070
|
+
warn(
|
|
1071
|
+
tr(
|
|
1072
|
+
`Resolved Python (${pythonPath}) cannot import openviking. The pip install target may differ from the runtime python3.`,
|
|
1073
|
+
`解析到的 Python(${pythonPath})无法 import openviking。pip 安装目标可能与运行时的 python3 不一致。`,
|
|
1074
|
+
),
|
|
1075
|
+
);
|
|
1076
|
+
// Try to discover the correct Python via pip show
|
|
1077
|
+
const corrected = await discoverOpenvikingPython(pythonPath);
|
|
1078
|
+
if (corrected) {
|
|
1079
|
+
info(
|
|
1080
|
+
tr(
|
|
1081
|
+
`Auto-corrected OPENVIKING_PYTHON to ${corrected}`,
|
|
1082
|
+
`已自动修正 OPENVIKING_PYTHON 为 ${corrected}`,
|
|
1083
|
+
),
|
|
1084
|
+
);
|
|
1085
|
+
pythonPath = corrected;
|
|
1086
|
+
} else {
|
|
1087
|
+
warn(
|
|
1088
|
+
tr(
|
|
1089
|
+
`Could not auto-detect the correct Python. Edit OPENVIKING_PYTHON in the env file manually.`,
|
|
1090
|
+
`无法自动检测正确的 Python。请手动修改 env 文件中的 OPENVIKING_PYTHON。`,
|
|
1091
|
+
),
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1004
1096
|
}
|
|
1005
1097
|
|
|
1006
1098
|
// Remote mode + default state dir + no python line → nothing to persist
|
|
@@ -1069,11 +1161,11 @@ async function main() {
|
|
|
1069
1161
|
if (selectedMode === "local") {
|
|
1070
1162
|
await validateEnvironment();
|
|
1071
1163
|
await checkOpenClaw();
|
|
1072
|
-
// Resolve plugin config after OpenClaw is available (for version detection)
|
|
1073
|
-
await resolvePluginConfig();
|
|
1074
|
-
await checkOpenClawCompatibility();
|
|
1075
|
-
checkRequestedOpenVikingCompatibility();
|
|
1076
|
-
await installOpenViking();
|
|
1164
|
+
// Resolve plugin config after OpenClaw is available (for version detection)
|
|
1165
|
+
await resolvePluginConfig();
|
|
1166
|
+
await checkOpenClawCompatibility();
|
|
1167
|
+
checkRequestedOpenVikingCompatibility();
|
|
1168
|
+
await installOpenViking();
|
|
1077
1169
|
await configureOvConf();
|
|
1078
1170
|
} else {
|
|
1079
1171
|
await checkOpenClaw();
|
|
@@ -1088,19 +1180,19 @@ async function main() {
|
|
|
1088
1180
|
pluginPath = localPluginDir;
|
|
1089
1181
|
PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId || "openviking");
|
|
1090
1182
|
info(tr(`Using local plugin from repo: ${pluginPath}`, `使用仓库内插件: ${pluginPath}`));
|
|
1091
|
-
await deployLocalPlugin(pluginPath);
|
|
1183
|
+
await deployLocalPlugin(pluginPath);
|
|
1092
1184
|
info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
|
|
1093
|
-
const npmArgs = resolvedNpmOmitDev
|
|
1094
|
-
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
1095
|
-
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
1096
|
-
await run("npm", npmArgs, { cwd: PLUGIN_DEST, silent: false });
|
|
1097
|
-
pluginPath = PLUGIN_DEST;
|
|
1185
|
+
const npmArgs = resolvedNpmOmitDev
|
|
1186
|
+
? ["install", "--omit=dev", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY]
|
|
1187
|
+
: ["install", "--no-audit", "--no-fund", "--registry", NPM_REGISTRY];
|
|
1188
|
+
await run("npm", npmArgs, { cwd: PLUGIN_DEST, silent: false });
|
|
1189
|
+
pluginPath = PLUGIN_DEST;
|
|
1098
1190
|
} else {
|
|
1099
1191
|
await downloadPlugin();
|
|
1100
1192
|
pluginPath = PLUGIN_DEST;
|
|
1101
1193
|
}
|
|
1102
1194
|
|
|
1103
|
-
await configureOpenClawPlugin();
|
|
1195
|
+
await configureOpenClawPlugin();
|
|
1104
1196
|
const envFiles = await writeOpenvikingEnv({
|
|
1105
1197
|
includePython: selectedMode === "local",
|
|
1106
1198
|
});
|