openclaw-openviking-setup-helper 0.2.10 → 0.2.12

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 +273 -15
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -11,7 +11,7 @@
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 ] [ --repo=PATH ]
14
+ * [ --plugin-version=TAG ] [ --openviking-version=V ] [ --version=V ] [ --repo=PATH ]
15
15
  *
16
16
  * Environment variables:
17
17
  * REPO, PLUGIN_VERSION (or BRANCH), OPENVIKING_INSTALL_YES, SKIP_OPENCLAW, SKIP_OPENVIKING
@@ -32,8 +32,10 @@ import { fileURLToPath } from "node:url";
32
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
33
33
 
34
34
  let REPO = process.env.REPO || "volcengine/OpenViking";
35
- // PLUGIN_VERSION takes precedence over BRANCH (legacy)
36
- let PLUGIN_VERSION = process.env.PLUGIN_VERSION || process.env.BRANCH || "main";
35
+ // PLUGIN_VERSION takes precedence over BRANCH (legacy). If omitted, resolve the latest tag from GitHub.
36
+ const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
37
+ let PLUGIN_VERSION = pluginVersionEnv;
38
+ let pluginVersionExplicit = Boolean(pluginVersionEnv);
37
39
  const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
38
40
  const PIP_INDEX_URL = process.env.PIP_INDEX_URL || "https://mirrors.volces.com/pypi/simple/";
39
41
 
@@ -107,6 +109,11 @@ let openvikingRepo = process.env.OPENVIKING_REPO || "";
107
109
  let workdirExplicit = false;
108
110
  let upgradePluginOnly = false;
109
111
  let rollbackLastUpgrade = false;
112
+ let combinedVersion = "";
113
+ let combinedVersionExplicit = false;
114
+ let pluginVersionArgExplicit = false;
115
+ let openvikingVersionArgExplicit = false;
116
+ let showCurrentVersion = false;
110
117
 
111
118
  let selectedMode = "local";
112
119
  let selectedServerPort = DEFAULT_SERVER_PORT;
@@ -129,6 +136,10 @@ for (let i = 0; i < argv.length; i++) {
129
136
  langZh = true;
130
137
  continue;
131
138
  }
139
+ if (arg === "--current-version") {
140
+ showCurrentVersion = true;
141
+ continue;
142
+ }
132
143
  if (arg === "--upgrade-plugin" || arg === "--update" || arg === "--upgrade") {
133
144
  upgradePluginOnly = true;
134
145
  continue;
@@ -149,7 +160,14 @@ for (let i = 0; i < argv.length; i++) {
149
160
  continue;
150
161
  }
151
162
  if (arg.startsWith("--plugin-version=")) {
152
- PLUGIN_VERSION = arg.slice("--plugin-version=".length).trim();
163
+ const version = arg.slice("--plugin-version=".length).trim();
164
+ if (!version) {
165
+ console.error("--plugin-version requires a value");
166
+ process.exit(1);
167
+ }
168
+ PLUGIN_VERSION = version;
169
+ pluginVersionExplicit = true;
170
+ pluginVersionArgExplicit = true;
153
171
  continue;
154
172
  }
155
173
  if (arg === "--plugin-version") {
@@ -159,11 +177,14 @@ for (let i = 0; i < argv.length; i++) {
159
177
  process.exit(1);
160
178
  }
161
179
  PLUGIN_VERSION = version;
180
+ pluginVersionExplicit = true;
181
+ pluginVersionArgExplicit = true;
162
182
  i += 1;
163
183
  continue;
164
184
  }
165
185
  if (arg.startsWith("--openviking-version=")) {
166
186
  openvikingVersion = arg.slice("--openviking-version=".length).trim();
187
+ openvikingVersionArgExplicit = true;
167
188
  continue;
168
189
  }
169
190
  if (arg === "--openviking-version") {
@@ -173,6 +194,28 @@ for (let i = 0; i < argv.length; i++) {
173
194
  process.exit(1);
174
195
  }
175
196
  openvikingVersion = version;
197
+ openvikingVersionArgExplicit = true;
198
+ i += 1;
199
+ continue;
200
+ }
201
+ if (arg.startsWith("--version=")) {
202
+ const version = arg.slice("--version=".length).trim();
203
+ if (!version) {
204
+ console.error("--version requires a value");
205
+ process.exit(1);
206
+ }
207
+ combinedVersion = version;
208
+ combinedVersionExplicit = true;
209
+ continue;
210
+ }
211
+ if (arg === "--version") {
212
+ const version = argv[i + 1]?.trim();
213
+ if (!version) {
214
+ console.error("--version requires a value");
215
+ process.exit(1);
216
+ }
217
+ combinedVersion = version;
218
+ combinedVersionExplicit = true;
176
219
  i += 1;
177
220
  continue;
178
221
  }
@@ -200,18 +243,44 @@ for (let i = 0; i < argv.length; i++) {
200
243
  }
201
244
  }
202
245
 
246
+ if (combinedVersionExplicit) {
247
+ if (pluginVersionArgExplicit || openvikingVersionArgExplicit) {
248
+ console.error("--version cannot be used together with --plugin-version or --openviking-version");
249
+ process.exit(1);
250
+ }
251
+ const normalizedVersion = normalizeCombinedVersion(combinedVersion);
252
+ PLUGIN_VERSION = normalizedVersion.pluginVersion;
253
+ openvikingVersion = normalizedVersion.openvikingVersion;
254
+ pluginVersionExplicit = true;
255
+ }
256
+
203
257
  function setOpenClawDir(dir) {
204
258
  OPENCLAW_DIR = dir;
205
259
  }
206
260
 
261
+ function normalizeCombinedVersion(version) {
262
+ const value = (version || "").trim();
263
+ if (!/^(v)?\d+(\.\d+){1,2}$/.test(value)) {
264
+ console.error("--version requires a semantic version like 0.2.9 or v0.2.9");
265
+ process.exit(1);
266
+ }
267
+ const openvikingVersionValue = value.startsWith("v") ? value.slice(1) : value;
268
+ return {
269
+ pluginVersion: `v${openvikingVersionValue}`,
270
+ openvikingVersion: openvikingVersionValue,
271
+ };
272
+ }
273
+
207
274
  function printHelp() {
208
275
  console.log("Usage: node install.js [ OPTIONS ]");
209
276
  console.log("");
210
277
  console.log("Options:");
211
278
  console.log(" --github-repo=OWNER/REPO GitHub repository (default: volcengine/OpenViking)");
212
- console.log(" --plugin-version=TAG Plugin version (Git tag, e.g. v0.2.9, default: main)");
279
+ console.log(" --version=V Shorthand for --plugin-version=vV and --openviking-version=V");
280
+ console.log(" --plugin-version=TAG Plugin version (Git tag, e.g. v0.2.9, default: latest tag)");
213
281
  console.log(" --openviking-version=V OpenViking PyPI version (e.g. 0.2.9, default: latest)");
214
282
  console.log(" --workdir PATH OpenClaw config directory (default: ~/.openclaw)");
283
+ console.log(" --current-version Print installed plugin/OpenViking versions and exit");
215
284
  console.log(" --repo=PATH Use local OpenViking repo at PATH (pip -e + local plugin)");
216
285
  console.log(" --update, --upgrade-plugin");
217
286
  console.log(" Upgrade only the plugin to the requested --plugin-version; keep ov.conf and do not change the OpenViking service");
@@ -225,13 +294,19 @@ function printHelp() {
225
294
  console.log(" # Install latest version");
226
295
  console.log(" node install.js");
227
296
  console.log("");
297
+ console.log(" # Show installed versions");
298
+ console.log(" node install.js --current-version");
299
+ console.log("");
300
+ console.log(" # Install a specific release version");
301
+ console.log(" node install.js --version=0.2.9");
302
+ console.log("");
228
303
  console.log(" # Install from a fork repository");
229
304
  console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
230
305
  console.log("");
231
306
  console.log(" # Install specific plugin version");
232
307
  console.log(" node install.js --plugin-version=v0.2.8");
233
308
  console.log("");
234
- console.log(" # Upgrade only the plugin files");
309
+ console.log(" # Upgrade only the plugin files from main branch");
235
310
  console.log(" node install.js --update --plugin-version=main");
236
311
  console.log("");
237
312
  console.log(" # Roll back the last plugin upgrade");
@@ -433,6 +508,10 @@ async function selectWorkdir() {
433
508
 
434
509
  const instances = detectOpenClawInstances();
435
510
  if (instances.length <= 1) return;
511
+ if (showCurrentVersion) {
512
+ setOpenClawDir(instances[0]);
513
+ return;
514
+ }
436
515
  if (installYes) return;
437
516
 
438
517
  console.log("");
@@ -572,8 +651,8 @@ function ensurePluginOnlyOperationArgs() {
572
651
  if ((upgradePluginOnly || rollbackLastUpgrade) && openvikingVersion) {
573
652
  err(
574
653
  tr(
575
- "Plugin-only upgrade/rollback does not support --openviking-version. Use --plugin-version to choose the plugin release, and run a full install if you need to change the OpenViking service version.",
576
- "仅插件升级或回滚不支持 --openviking-version。请使用 --plugin-version 指定插件版本;如果需要调整 OpenViking 服务版本,请执行完整安装流程。",
654
+ "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.",
655
+ "仅插件升级或回滚不支持 --openviking-version 或 --version。请使用 --plugin-version 指定插件版本;如果需要调整 OpenViking 服务版本,请执行完整安装流程。",
577
656
  ),
578
657
  );
579
658
  process.exit(1);
@@ -618,6 +697,120 @@ async function testRemoteFile(url) {
618
697
  return false;
619
698
  }
620
699
 
700
+ function compareSemverDesc(a, b) {
701
+ if (versionGte(a, b) && versionGte(b, a)) {
702
+ return 0;
703
+ }
704
+ return versionGte(a, b) ? -1 : 1;
705
+ }
706
+
707
+ function pickLatestPluginTag(tagNames) {
708
+ const normalized = tagNames
709
+ .map((tag) => String(tag ?? "").trim())
710
+ .filter(Boolean);
711
+
712
+ const semverTags = normalized
713
+ .filter((tag) => isSemverLike(tag))
714
+ .sort(compareSemverDesc);
715
+
716
+ if (semverTags.length > 0) {
717
+ return semverTags[0];
718
+ }
719
+
720
+ return normalized[0] || "";
721
+ }
722
+
723
+ function parseGitLsRemoteTags(output) {
724
+ return String(output ?? "")
725
+ .split(/\r?\n/)
726
+ .map((line) => {
727
+ const match = line.match(/refs\/tags\/(.+)$/);
728
+ return match?.[1]?.trim() || "";
729
+ })
730
+ .filter(Boolean);
731
+ }
732
+
733
+ async function resolveDefaultPluginVersion() {
734
+ if (PLUGIN_VERSION) {
735
+ return;
736
+ }
737
+
738
+ info(tr(
739
+ `No plugin version specified; resolving latest tag from ${REPO}...`,
740
+ `未指定插件版本,正在解析 ${REPO} 的最新 tag...`,
741
+ ));
742
+
743
+ const failures = [];
744
+ const apiUrl = `https://api.github.com/repos/${REPO}/tags?per_page=100`;
745
+
746
+ try {
747
+ const controller = new AbortController();
748
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
749
+ const response = await fetch(apiUrl, {
750
+ headers: {
751
+ Accept: "application/vnd.github+json",
752
+ "User-Agent": "openviking-setup-helper",
753
+ "X-GitHub-Api-Version": "2022-11-28",
754
+ },
755
+ signal: controller.signal,
756
+ });
757
+ clearTimeout(timeoutId);
758
+
759
+ if (response.ok) {
760
+ const payload = await response.json().catch(() => null);
761
+ if (Array.isArray(payload)) {
762
+ const latestTag = pickLatestPluginTag(payload.map((item) => item?.name || ""));
763
+ if (latestTag) {
764
+ PLUGIN_VERSION = latestTag;
765
+ info(tr(
766
+ `Resolved default plugin version to latest tag: ${PLUGIN_VERSION}`,
767
+ `已将默认插件版本解析为最新 tag: ${PLUGIN_VERSION}`,
768
+ ));
769
+ return;
770
+ }
771
+ } else {
772
+ failures.push("GitHub tags API returned an unexpected payload");
773
+ }
774
+ } else {
775
+ failures.push(`GitHub tags API returned HTTP ${response.status}`);
776
+ }
777
+ } catch (error) {
778
+ failures.push(`GitHub tags API failed: ${String(error)}`);
779
+ }
780
+
781
+ const gitRef = `https://github.com/${REPO}.git`;
782
+ const gitResult = await runCapture("git", ["ls-remote", "--tags", "--refs", gitRef], {
783
+ shell: IS_WIN,
784
+ });
785
+ if (gitResult.code === 0 && gitResult.out) {
786
+ const latestTag = pickLatestPluginTag(parseGitLsRemoteTags(gitResult.out));
787
+ if (latestTag) {
788
+ PLUGIN_VERSION = latestTag;
789
+ info(tr(
790
+ `Resolved default plugin version via git tags: ${PLUGIN_VERSION}`,
791
+ `已通过 git tag 解析默认插件版本: ${PLUGIN_VERSION}`,
792
+ ));
793
+ return;
794
+ }
795
+ failures.push("git ls-remote returned no usable tags");
796
+ } else {
797
+ failures.push(`git ls-remote failed${gitResult.err ? `: ${gitResult.err}` : ""}`);
798
+ }
799
+
800
+ err(tr(
801
+ `Could not resolve the latest tag for ${REPO}.`,
802
+ `无法解析 ${REPO} 的最新 tag。`,
803
+ ));
804
+ console.log(tr(
805
+ "Please rerun with --plugin-version <tag>, or use --plugin-version main to track the branch head explicitly.",
806
+ "请使用 --plugin-version <tag> 重新执行;如果需要显式跟踪分支头,请使用 --plugin-version main。",
807
+ ));
808
+ if (failures.length > 0) {
809
+ warn(failures.join(" | "));
810
+ }
811
+ process.exit(1);
812
+ }
813
+
621
814
  // Resolve plugin configuration from manifest or fallback
622
815
  async function resolvePluginConfig() {
623
816
  const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
@@ -741,7 +934,7 @@ async function checkOpenClawCompatibility() {
741
934
  }
742
935
 
743
936
  // If user explicitly requested an old version, pass
744
- if (PLUGIN_VERSION !== "main" && isSemverLike(PLUGIN_VERSION) && !versionGte(PLUGIN_VERSION, "v0.2.8")) {
937
+ if (isSemverLike(PLUGIN_VERSION) && !versionGte(PLUGIN_VERSION, "v0.2.8")) {
745
938
  return;
746
939
  }
747
940
 
@@ -812,9 +1005,9 @@ async function installOpenViking() {
812
1005
  // Determine package spec
813
1006
  const pkgSpec = openvikingVersion ? `openviking==${openvikingVersion}` : "openviking";
814
1007
  if (openvikingVersion) {
815
- info(tr(`Installing OpenViking ${openvikingVersion} from PyPI...`, `正在安装 OpenViking ${openvikingVersion} (PyPI)...`));
1008
+ info(tr(`Installing or upgrading OpenViking ${openvikingVersion} from PyPI...`, `正在安装或升级 OpenViking ${openvikingVersion} (PyPI)...`));
816
1009
  } else {
817
- info(tr("Installing OpenViking (latest) from PyPI...", "正在安装 OpenViking (最新版) (PyPI)..."));
1010
+ info(tr("Installing or upgrading OpenViking (latest) from PyPI...", "正在安装或升级 OpenViking (最新版) (PyPI)..."));
818
1011
  }
819
1012
  info(tr(`Using pip index: ${PIP_INDEX_URL}`, `使用 pip 镜像源: ${PIP_INDEX_URL}`));
820
1013
 
@@ -822,7 +1015,7 @@ async function installOpenViking() {
822
1015
  await runCapture(py, ["-m", "pip", "install", "--upgrade", "pip", "-q", "-i", PIP_INDEX_URL], { shell: false });
823
1016
  const installResult = await runLiveCapture(
824
1017
  py,
825
- ["-m", "pip", "install", "--progress-bar", "on", pkgSpec, "-i", PIP_INDEX_URL],
1018
+ ["-m", "pip", "install", "--upgrade", "--progress-bar", "on", pkgSpec, "-i", PIP_INDEX_URL],
826
1019
  { shell: false },
827
1020
  );
828
1021
  if (installResult.code === 0) {
@@ -878,7 +1071,7 @@ async function installOpenViking() {
878
1071
  await runCapture(venvPy, ["-m", "pip", "install", "--upgrade", "pip", "-q", "-i", PIP_INDEX_URL], { shell: false });
879
1072
  const venvInstall = await runLiveCapture(
880
1073
  venvPy,
881
- ["-m", "pip", "install", "--progress-bar", "on", pkgSpec, "-i", PIP_INDEX_URL],
1074
+ ["-m", "pip", "install", "--upgrade", "--progress-bar", "on", pkgSpec, "-i", PIP_INDEX_URL],
882
1075
  { shell: false },
883
1076
  );
884
1077
  if (venvInstall.code === 0) {
@@ -895,7 +1088,7 @@ async function installOpenViking() {
895
1088
  if (process.env.OPENVIKING_ALLOW_BREAK_SYSTEM_PACKAGES === "1") {
896
1089
  const systemInstall = await runLiveCapture(
897
1090
  py,
898
- ["-m", "pip", "install", "--progress-bar", "on", "--break-system-packages", pkgSpec, "-i", PIP_INDEX_URL],
1091
+ ["-m", "pip", "install", "--upgrade", "--progress-bar", "on", "--break-system-packages", pkgSpec, "-i", PIP_INDEX_URL],
899
1092
  { shell: false },
900
1093
  );
901
1094
  if (systemInstall.code === 0) {
@@ -1006,6 +1199,66 @@ function getInstallStatePathForPlugin(pluginId) {
1006
1199
  return join(OPENCLAW_DIR, "extensions", pluginId, ".ov-install-state.json");
1007
1200
  }
1008
1201
 
1202
+ async function detectInstalledOpenVikingVersion() {
1203
+ const pythonCandidates = [];
1204
+ const configuredPython = process.env.OPENVIKING_PYTHON?.trim();
1205
+ if (configuredPython) {
1206
+ pythonCandidates.push(configuredPython);
1207
+ }
1208
+
1209
+ const envCandidates = IS_WIN
1210
+ ? [join(OPENCLAW_DIR, "openviking.env.ps1"), join(OPENCLAW_DIR, "openviking.env.bat")]
1211
+ : [join(OPENCLAW_DIR, "openviking.env")];
1212
+
1213
+ for (const envPath of envCandidates) {
1214
+ if (!existsSync(envPath)) continue;
1215
+ const raw = await readFile(envPath, "utf8").catch(() => "");
1216
+ if (!raw) continue;
1217
+ const match = raw.match(/OPENVIKING_PYTHON(?:\s*=\s*|=)['"]?([^'"\r\n]+)['"]?/);
1218
+ const pythonPath = match?.[1]?.trim();
1219
+ if (pythonPath) {
1220
+ pythonCandidates.push(pythonPath);
1221
+ }
1222
+ }
1223
+
1224
+ for (const candidate of IS_WIN ? ["py", "python", "python3"] : ["python3", "python"]) {
1225
+ pythonCandidates.push(candidate);
1226
+ }
1227
+
1228
+ const seen = new Set();
1229
+ for (const candidate of pythonCandidates) {
1230
+ if (!candidate || seen.has(candidate)) continue;
1231
+ seen.add(candidate);
1232
+ const result = await runCapture(candidate, ["-c", "import openviking; print(openviking.__version__)"], { shell: IS_WIN });
1233
+ if (result.code === 0) {
1234
+ return result.out.trim();
1235
+ }
1236
+ }
1237
+
1238
+ return "";
1239
+ }
1240
+
1241
+ async function printCurrentVersionInfo() {
1242
+ const state = await readJsonFileIfExists(getInstallStatePathForPlugin("openviking"));
1243
+ const pluginRequestedRef = state?.requestedRef || "";
1244
+ const pluginReleaseId = state?.releaseId || "";
1245
+ const pluginInstalledAt = state?.installedAt || "";
1246
+ const openvikingInstalledVersion = await detectInstalledOpenVikingVersion();
1247
+
1248
+ console.log("");
1249
+ bold(tr("Installed versions", "当前已安装版本"));
1250
+ console.log("");
1251
+ console.log(`Target: ${OPENCLAW_DIR}`);
1252
+ console.log(`Plugin: ${pluginReleaseId || pluginRequestedRef || "not installed"}`);
1253
+ if (pluginRequestedRef && pluginReleaseId && pluginRequestedRef !== pluginReleaseId) {
1254
+ console.log(`Plugin requested ref: ${pluginRequestedRef}`);
1255
+ }
1256
+ console.log(`OpenViking: ${openvikingInstalledVersion || "unknown"}`);
1257
+ if (pluginInstalledAt) {
1258
+ console.log(`Installed at: ${pluginInstalledAt}`);
1259
+ }
1260
+ }
1261
+
1009
1262
  function getUpgradeAuditDir() {
1010
1263
  return join(OPENCLAW_DIR, ".openviking-upgrade-backup");
1011
1264
  }
@@ -1947,14 +2200,19 @@ async function main() {
1947
2200
 
1948
2201
  ensurePluginOnlyOperationArgs();
1949
2202
  await selectWorkdir();
2203
+ if (showCurrentVersion) {
2204
+ await printCurrentVersionInfo();
2205
+ return;
2206
+ }
1950
2207
  if (rollbackLastUpgrade) {
1951
2208
  info(tr("Mode: rollback last plugin upgrade", "模式: 回滚最近一次插件升级"));
1952
- if (PLUGIN_VERSION !== "main") {
2209
+ if (pluginVersionExplicit) {
1953
2210
  warn("--plugin-version is ignored in --rollback mode.");
1954
2211
  }
1955
2212
  await rollbackLastUpgradeOperation();
1956
2213
  return;
1957
2214
  }
2215
+ await resolveDefaultPluginVersion();
1958
2216
  validateRequestedPluginVersion();
1959
2217
  info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
1960
2218
  info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-openviking-setup-helper",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Setup helper for installing OpenViking memory plugin into OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {