openclaw-openviking-setup-helper 0.3.0-beta.26 → 0.3.0-beta.27

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 +432 -100
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -9,29 +9,34 @@
9
9
  * ov-install
10
10
  * openclaw-openviking-install
11
11
  *
12
- * Direct run:
13
- * node install.js [ --base-url URL ] [ --api-key KEY ] [ --zh ] [ --workdir PATH ] [ --upgrade-plugin ]
14
- * [ --plugin-version=TAG ]
15
- *
16
- * Environment variables:
17
- * REPO, PLUGIN_VERSION (or BRANCH), OPENVIKING_BASE_URL, OPENVIKING_API_KEY, SKIP_OPENCLAW
18
- * NPM_REGISTRY
19
- */
12
+ * Direct run:
13
+ * node install.js [ --base-url URL ] [ --api-key KEY ] [ --zh ] [ --workdir PATH ] [ --upgrade-plugin ]
14
+ * [ --plugin-version=VERSION ] [ --plugin-source=npm|github ]
15
+ *
16
+ * Environment variables:
17
+ * PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION (or BRANCH),
18
+ * OPENVIKING_BASE_URL, OPENVIKING_API_KEY, SKIP_OPENCLAW, NPM_REGISTRY
19
+ */
20
20
 
21
21
  import { spawn } from "node:child_process";
22
- import { cp, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
23
- import { existsSync, readdirSync } from "node:fs";
24
- import { basename, dirname, join } from "node:path";
25
- import { createInterface } from "node:readline";
26
- import { fileURLToPath } from "node:url";
22
+ import { cp, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
23
+ import { existsSync, readdirSync } from "node:fs";
24
+ import { basename, dirname, join } from "node:path";
25
+ import { tmpdir } from "node:os";
26
+ import { createInterface } from "node:readline";
27
+ import { fileURLToPath } from "node:url";
27
28
 
28
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
30
 
30
- let REPO = process.env.REPO || "volcengine/OpenViking";
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);
31
+ let REPO = process.env.REPO || "volcengine/OpenViking";
32
+ const DEFAULT_PLUGIN_NPM_PACKAGE = "@openviking/openclaw-plugin";
33
+ let pluginNpmPackage = (process.env.PLUGIN_NPM_PACKAGE || DEFAULT_PLUGIN_NPM_PACKAGE).trim();
34
+ let pluginSource = (process.env.PLUGIN_SOURCE || "npm").trim().toLowerCase();
35
+ let pluginSourceExplicit = Boolean(process.env.PLUGIN_SOURCE);
36
+ // PLUGIN_VERSION takes precedence over BRANCH (legacy). If omitted, resolve the latest npm dist-tag or GitHub tag.
37
+ const pluginVersionEnv = (process.env.PLUGIN_VERSION || process.env.BRANCH || "").trim();
38
+ let PLUGIN_VERSION = pluginVersionEnv;
39
+ let pluginVersionExplicit = Boolean(pluginVersionEnv);
35
40
  const NPM_REGISTRY = process.env.NPM_REGISTRY || "https://registry.npmmirror.com";
36
41
  const DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION = "2026.5.3";
37
42
  const OPENCLAW_SHORT_VERSION_YEAR = 2026;
@@ -97,9 +102,11 @@ let resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
97
102
  let resolvedNpmBuildScript = "build";
98
103
  let resolvedNpmPruneAfterBuild = true;
99
104
  let resolvedMinOpenclawVersion = "";
100
- let resolvedMinOpenvikingVersion = "";
101
- let resolvedPluginReleaseId = "";
102
- let detectedOpenClawVersion = "";
105
+ let resolvedMinOpenvikingVersion = "";
106
+ let resolvedPluginReleaseId = "";
107
+ let detectedOpenClawVersion = "";
108
+ let npmPackageTempDir = "";
109
+ let npmPackageExtractDir = "";
103
110
 
104
111
  let nonInteractive = false;
105
112
  let langZh = false;
@@ -178,35 +185,81 @@ for (let i = 0; i < argv.length; i++) {
178
185
  console.error("--plugin-version requires a value");
179
186
  process.exit(1);
180
187
  }
181
- PLUGIN_VERSION = version;
182
- pluginVersionExplicit = true;
183
- continue;
184
- }
188
+ PLUGIN_VERSION = version;
189
+ pluginVersionExplicit = true;
190
+ continue;
191
+ }
185
192
  if (arg === "--plugin-version") {
186
193
  const version = argv[i + 1]?.trim();
187
194
  if (!version) {
188
195
  console.error("--plugin-version requires a value");
189
196
  process.exit(1);
190
197
  }
191
- PLUGIN_VERSION = version;
192
- pluginVersionExplicit = true;
193
- i += 1;
194
- continue;
195
- }
196
- if (arg.startsWith("--github-repo=")) {
197
- REPO = arg.slice("--github-repo=".length).trim();
198
- continue;
199
- }
200
- if (arg === "--github-repo") {
198
+ PLUGIN_VERSION = version;
199
+ pluginVersionExplicit = true;
200
+ i += 1;
201
+ continue;
202
+ }
203
+ if (arg.startsWith("--github-repo=")) {
204
+ REPO = arg.slice("--github-repo=".length).trim();
205
+ if (!pluginSourceExplicit) {
206
+ pluginSource = "github";
207
+ }
208
+ continue;
209
+ }
210
+ if (arg === "--github-repo") {
201
211
  const repo = argv[i + 1]?.trim();
202
212
  if (!repo) {
203
213
  console.error("--github-repo requires a value (e.g. owner/repo)");
204
214
  process.exit(1);
205
- }
206
- REPO = repo;
207
- i += 1;
208
- continue;
209
- }
215
+ }
216
+ REPO = repo;
217
+ if (!pluginSourceExplicit) {
218
+ pluginSource = "github";
219
+ }
220
+ i += 1;
221
+ continue;
222
+ }
223
+ if (arg.startsWith("--plugin-source=") || arg.startsWith("--source=")) {
224
+ const value = arg.includes("--plugin-source=")
225
+ ? arg.slice("--plugin-source=".length).trim()
226
+ : arg.slice("--source=".length).trim();
227
+ pluginSource = value.toLowerCase();
228
+ pluginSourceExplicit = true;
229
+ continue;
230
+ }
231
+ if (arg === "--plugin-source" || arg === "--source") {
232
+ const value = argv[i + 1]?.trim();
233
+ if (!value) {
234
+ console.error(`${arg} requires a value (npm or github)`);
235
+ process.exit(1);
236
+ }
237
+ pluginSource = value.toLowerCase();
238
+ pluginSourceExplicit = true;
239
+ i += 1;
240
+ continue;
241
+ }
242
+ if (arg.startsWith("--plugin-package=") || arg.startsWith("--npm-package=")) {
243
+ const value = arg.includes("--plugin-package=")
244
+ ? arg.slice("--plugin-package=".length).trim()
245
+ : arg.slice("--npm-package=".length).trim();
246
+ if (!value) {
247
+ console.error("--plugin-package requires a package name");
248
+ process.exit(1);
249
+ }
250
+ pluginNpmPackage = value;
251
+ continue;
252
+ }
253
+ if (arg === "--plugin-package" || arg === "--npm-package") {
254
+ const value = argv[i + 1]?.trim();
255
+ if (!value) {
256
+ console.error(`${arg} requires a package name`);
257
+ process.exit(1);
258
+ }
259
+ pluginNpmPackage = value;
260
+ i += 1;
261
+ continue;
262
+ }
210
263
  if (arg === "--base-url") {
211
264
  const val = argv[i + 1]?.trim();
212
265
  if (!val) { console.error("--base-url requires a URL"); process.exit(1); }
@@ -278,10 +331,13 @@ function setOpenClawDir(dir) {
278
331
 
279
332
  function printHelp() {
280
333
  console.log("Usage: node install.js [ OPTIONS ]");
281
- console.log("");
282
- console.log("Options:");
283
- console.log(" --github-repo=OWNER/REPO GitHub repository (default: volcengine/OpenViking)");
284
- console.log(" --plugin-version=TAG Plugin version (Git tag, e.g. v0.2.9, default: latest tag)");
334
+ console.log("");
335
+ console.log("Options:");
336
+ console.log(" --plugin-source=npm|github");
337
+ console.log(" Plugin download source (default: npm)");
338
+ console.log(" --plugin-package=NAME npm plugin package (default: @openviking/openclaw-plugin)");
339
+ console.log(" --github-repo=OWNER/REPO GitHub repository (implies --plugin-source=github unless source is set)");
340
+ console.log(" --plugin-version=VERSION Plugin version (npm version/tag or Git tag; default: npm latest)");
285
341
  console.log(" --workdir PATH OpenClaw config directory (default: ~/.openclaw)");
286
342
  console.log(" --current-version Print installed plugin version and exit");
287
343
  console.log(" --update, --upgrade-plugin");
@@ -306,11 +362,11 @@ function printHelp() {
306
362
  console.log(" # Show installed versions");
307
363
  console.log(" node install.js --current-version");
308
364
  console.log("");
309
- console.log(" # Install a specific release version");
310
- console.log(" node install.js --plugin-version=v0.2.9");
311
- console.log("");
312
- console.log(" # Install from a fork repository");
313
- console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
365
+ console.log(" # Install a specific release version");
366
+ console.log(" node install.js --plugin-version=2026.5.8");
367
+ console.log("");
368
+ console.log(" # Install from a fork repository");
369
+ console.log(" node install.js --github-repo=yourname/OpenViking --plugin-version=dev-branch");
314
370
  console.log("");
315
371
  console.log(" # Install specific plugin version");
316
372
  console.log(" node install.js --plugin-version=v0.2.8");
@@ -321,8 +377,8 @@ function printHelp() {
321
377
  console.log(" # Roll back the last plugin upgrade");
322
378
  console.log(" node install.js --rollback");
323
379
  console.log("");
324
- console.log("Env: REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
325
- }
380
+ console.log("Env: PLUGIN_SOURCE, PLUGIN_NPM_PACKAGE, REPO, PLUGIN_VERSION, SKIP_OPENCLAW, NPM_REGISTRY");
381
+ }
326
382
 
327
383
  function formatCliArg(value) {
328
384
  if (!value) {
@@ -623,13 +679,30 @@ if (upgradePluginOnly && rollbackLastUpgrade) {
623
679
  process.exit(1);
624
680
  }
625
681
 
626
- if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
627
- console.error("--uninstall cannot be used with --upgrade-plugin or --rollback");
628
- process.exit(1);
629
- }
630
-
631
- // Detect OpenClaw version
632
- async function detectOpenClawVersion() {
682
+ if (uninstallPlugin && (upgradePluginOnly || rollbackLastUpgrade)) {
683
+ console.error("--uninstall cannot be used with --upgrade-plugin or --rollback");
684
+ process.exit(1);
685
+ }
686
+
687
+ if (!["npm", "github"].includes(pluginSource)) {
688
+ console.error("--plugin-source must be either npm or github");
689
+ process.exit(1);
690
+ }
691
+
692
+ function looksLikeLegacyGitHubRef(value) {
693
+ const ref = String(value || "").trim();
694
+ if (!ref) return false;
695
+ if (/^v\d+(\.\d+){1,2}([-.].*)?$/i.test(ref)) return true;
696
+ if (["main", "master"].includes(ref.toLowerCase())) return true;
697
+ return false;
698
+ }
699
+
700
+ if (!pluginSourceExplicit && pluginVersionExplicit && looksLikeLegacyGitHubRef(PLUGIN_VERSION)) {
701
+ pluginSource = "github";
702
+ }
703
+
704
+ // Detect OpenClaw version
705
+ async function detectOpenClawVersion() {
633
706
  if (detectedOpenClawVersion) {
634
707
  return detectedOpenClawVersion;
635
708
  }
@@ -681,10 +754,10 @@ function compareSemverDesc(a, b) {
681
754
  return versionGte(a, b) ? -1 : 1;
682
755
  }
683
756
 
684
- function pickLatestPluginTag(tagNames) {
685
- const normalized = tagNames
686
- .map((tag) => String(tag ?? "").trim())
687
- .filter(Boolean);
757
+ function pickLatestPluginTag(tagNames) {
758
+ const normalized = tagNames
759
+ .map((tag) => String(tag ?? "").trim())
760
+ .filter(Boolean);
688
761
 
689
762
  const semverTags = normalized
690
763
  .filter((tag) => isSemverLike(tag))
@@ -693,9 +766,118 @@ function pickLatestPluginTag(tagNames) {
693
766
  if (semverTags.length > 0) {
694
767
  return semverTags[0];
695
768
  }
696
-
697
- return normalized[0] || "";
698
- }
769
+
770
+ return normalized[0] || "";
771
+ }
772
+
773
+ function npmPackageSpec(version = PLUGIN_VERSION) {
774
+ return version ? `${pluginNpmPackage}@${version}` : pluginNpmPackage;
775
+ }
776
+
777
+ function parseNpmJsonOutput(output) {
778
+ const text = String(output || "").trim();
779
+ if (!text) return null;
780
+ try {
781
+ return JSON.parse(text);
782
+ } catch {
783
+ const firstArray = text.indexOf("[");
784
+ const lastArray = text.lastIndexOf("]");
785
+ if (firstArray >= 0 && lastArray > firstArray) {
786
+ try {
787
+ return JSON.parse(text.slice(firstArray, lastArray + 1));
788
+ } catch {}
789
+ }
790
+ const firstObject = text.indexOf("{");
791
+ const lastObject = text.lastIndexOf("}");
792
+ if (firstObject >= 0 && lastObject > firstObject) {
793
+ try {
794
+ return JSON.parse(text.slice(firstObject, lastObject + 1));
795
+ } catch {}
796
+ }
797
+ }
798
+ return null;
799
+ }
800
+
801
+ async function resolveDefaultPluginVersionFromNpm() {
802
+ info(tr(
803
+ `No plugin version specified; resolving latest npm version from ${pluginNpmPackage}...`,
804
+ `No plugin version specified; resolving latest npm version from ${pluginNpmPackage}...`,
805
+ ));
806
+
807
+ const result = await runCapture("npm", [
808
+ "view",
809
+ `${pluginNpmPackage}@latest`,
810
+ "version",
811
+ "--json",
812
+ "--registry",
813
+ NPM_REGISTRY,
814
+ ], { shell: IS_WIN });
815
+
816
+ if (result.code === 0) {
817
+ const parsed = parseNpmJsonOutput(result.out);
818
+ const version = typeof parsed === "string" ? parsed : String(result.out || "").trim().replace(/^"|"$/g, "");
819
+ if (version) {
820
+ PLUGIN_VERSION = version;
821
+ info(tr(
822
+ `Resolved default plugin version to npm latest: ${PLUGIN_VERSION}`,
823
+ `Resolved default plugin version to npm latest: ${PLUGIN_VERSION}`,
824
+ ));
825
+ return true;
826
+ }
827
+ }
828
+
829
+ warn(tr(
830
+ `Could not resolve npm latest for ${pluginNpmPackage}${result.err ? `: ${result.err}` : ""}`,
831
+ `Could not resolve npm latest for ${pluginNpmPackage}${result.err ? `: ${result.err}` : ""}`,
832
+ ));
833
+ return false;
834
+ }
835
+
836
+ async function ensureNpmPackageExtracted() {
837
+ if (npmPackageExtractDir && existsSync(npmPackageExtractDir)) {
838
+ return npmPackageExtractDir;
839
+ }
840
+
841
+ npmPackageTempDir = await mkdtemp(join(tmpdir(), "ov-plugin-npm-"));
842
+ info(tr(
843
+ `Downloading plugin package from npm: ${npmPackageSpec()}`,
844
+ `Downloading plugin package from npm: ${npmPackageSpec()}`,
845
+ ));
846
+
847
+ const packResult = await runCapture("npm", [
848
+ "pack",
849
+ npmPackageSpec(),
850
+ "--pack-destination",
851
+ npmPackageTempDir,
852
+ "--json",
853
+ "--registry",
854
+ NPM_REGISTRY,
855
+ ], { shell: IS_WIN });
856
+
857
+ if (packResult.code !== 0) {
858
+ throw new Error(`npm pack failed for ${npmPackageSpec()}${packResult.err ? `: ${packResult.err}` : ""}`);
859
+ }
860
+
861
+ const parsed = parseNpmJsonOutput(packResult.out);
862
+ const first = Array.isArray(parsed) ? parsed[0] : parsed;
863
+ const filename = first?.filename || readdirSync(npmPackageTempDir).find((name) => name.endsWith(".tgz"));
864
+ if (!filename) {
865
+ throw new Error(`npm pack did not produce a tarball for ${npmPackageSpec()}`);
866
+ }
867
+
868
+ const tarballPath = join(npmPackageTempDir, filename);
869
+ const extractRoot = join(npmPackageTempDir, "extract");
870
+ await mkdir(extractRoot, { recursive: true });
871
+ await run("tar", ["-xzf", tarballPath, "-C", extractRoot], { silent: true, shell: IS_WIN });
872
+
873
+ const packageDir = join(extractRoot, "package");
874
+ if (!existsSync(packageDir)) {
875
+ throw new Error(`npm package ${npmPackageSpec()} did not contain the expected package directory`);
876
+ }
877
+
878
+ npmPackageExtractDir = packageDir;
879
+ return npmPackageExtractDir;
880
+ }
699
881
 
700
882
  function parseGitLsRemoteTags(output) {
701
883
  return String(output ?? "")
@@ -707,13 +889,23 @@ function parseGitLsRemoteTags(output) {
707
889
  .filter(Boolean);
708
890
  }
709
891
 
710
- async function resolveDefaultPluginVersion() {
711
- if (PLUGIN_VERSION) {
712
- return;
713
- }
714
-
715
- info(tr(
716
- `No plugin version specified; resolving latest tag from ${REPO}...`,
892
+ async function resolveDefaultPluginVersion() {
893
+ if (PLUGIN_VERSION) {
894
+ return;
895
+ }
896
+
897
+ if (pluginSource === "npm") {
898
+ if (await resolveDefaultPluginVersionFromNpm()) {
899
+ return;
900
+ }
901
+ warn(tr(
902
+ "Falling back to GitHub tag resolution.",
903
+ "Falling back to GitHub tag resolution.",
904
+ ));
905
+ }
906
+
907
+ info(tr(
908
+ `No plugin version specified; resolving latest tag from ${REPO}...`,
717
909
  `未指定插件版本,正在解析 ${REPO} 的最新 tag...`,
718
910
  ));
719
911
 
@@ -785,12 +977,117 @@ async function resolveDefaultPluginVersion() {
785
977
  if (failures.length > 0) {
786
978
  warn(failures.join(" | "));
787
979
  }
788
- process.exit(1);
789
- }
790
-
791
- // Resolve plugin configuration from manifest or fallback
792
- async function resolvePluginConfig() {
793
- const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
980
+ process.exit(1);
981
+ }
982
+
983
+ function applyManifestConfig(manifestData) {
984
+ resolvedPluginId = manifestData.plugin?.id || "";
985
+ resolvedPluginKind = manifestData.plugin?.kind || "";
986
+ resolvedPluginSlot = manifestData.plugin?.slot || "";
987
+ resolvedMinOpenclawVersion = manifestData.compatibility?.minOpenclawVersion || "";
988
+ resolvedMinOpenvikingVersion = manifestData.compatibility?.minOpenvikingVersion || "";
989
+ resolvedPluginReleaseId = manifestData.pluginVersion || manifestData.release?.id || "";
990
+ const npmConfig = manifestData.npm && typeof manifestData.npm === "object"
991
+ ? manifestData.npm
992
+ : {};
993
+ resolvedNpmOmitDev = npmConfig.omitDev !== false;
994
+ resolvedNpmBuild = npmConfig.build === true || npmConfig.buildFromSource === true;
995
+ resolvedNpmBuildMinOpenclawVersion =
996
+ typeof npmConfig.buildMinOpenclawVersion === "string" && npmConfig.buildMinOpenclawVersion.trim()
997
+ ? npmConfig.buildMinOpenclawVersion.trim()
998
+ : DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
999
+ resolvedNpmBuildScript = typeof npmConfig.buildScript === "string" && npmConfig.buildScript.trim()
1000
+ ? npmConfig.buildScript.trim()
1001
+ : "build";
1002
+ resolvedNpmPruneAfterBuild = npmConfig.pruneAfterBuild !== false;
1003
+ resolvedFilesRequired = manifestData.files?.required || [];
1004
+ resolvedFilesOptional = manifestData.files?.optional || [];
1005
+ }
1006
+
1007
+ function hasPrebuiltRuntimeOutputs(packageDir) {
1008
+ return existsSync(join(packageDir, "dist", "index.js"));
1009
+ }
1010
+
1011
+ async function resolvePluginConfigFromNpm() {
1012
+ info(tr(
1013
+ `Resolving plugin configuration from npm package: ${npmPackageSpec()}`,
1014
+ `Resolving plugin configuration from npm package: ${npmPackageSpec()}`,
1015
+ ));
1016
+
1017
+ const packageDir = await ensureNpmPackageExtracted();
1018
+ const manifestPath = join(packageDir, "install-manifest.json");
1019
+ const packageJsonPath = join(packageDir, "package.json");
1020
+ let manifestData = null;
1021
+ let packageJson = null;
1022
+
1023
+ if (existsSync(packageJsonPath)) {
1024
+ try {
1025
+ packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
1026
+ resolvedPluginReleaseId = packageJson.version || "";
1027
+ } catch {}
1028
+ }
1029
+
1030
+ if (existsSync(manifestPath)) {
1031
+ try {
1032
+ manifestData = JSON.parse(await readFile(manifestPath, "utf8"));
1033
+ info(tr("Found manifest in npm package", "Found manifest in npm package"));
1034
+ } catch {}
1035
+ }
1036
+
1037
+ resolvedPluginDir = ".";
1038
+ if (manifestData) {
1039
+ applyManifestConfig(manifestData);
1040
+ } else {
1041
+ const pkgName = packageJson?.name || "";
1042
+ const fallback = pkgName && pkgName !== DEFAULT_PLUGIN_NPM_PACKAGE ? FALLBACK_LEGACY : FALLBACK_CURRENT;
1043
+ resolvedPluginId = fallback.id;
1044
+ resolvedPluginKind = fallback.kind;
1045
+ resolvedPluginSlot = fallback.slot;
1046
+ resolvedFilesRequired = fallback.required;
1047
+ resolvedFilesOptional = fallback.optional;
1048
+ resolvedNpmOmitDev = true;
1049
+ resolvedNpmBuild = false;
1050
+ resolvedNpmBuildMinOpenclawVersion = DEFAULT_NPM_BUILD_MIN_OPENCLAW_VERSION;
1051
+ resolvedNpmBuildScript = "build";
1052
+ resolvedNpmPruneAfterBuild = true;
1053
+ resolvedMinOpenclawVersion = (packageJson?.engines?.openclaw || "").replace(/^>=?\s*/, "").trim()
1054
+ || fallback.minOpenclawVersion
1055
+ || "2026.3.7";
1056
+ resolvedMinOpenvikingVersion = "";
1057
+ }
1058
+
1059
+ if (hasPrebuiltRuntimeOutputs(packageDir)) {
1060
+ resolvedNpmBuild = false;
1061
+ info(tr(
1062
+ "npm package contains prebuilt runtime output; skipping source build.",
1063
+ "npm package contains prebuilt runtime output; skipping source build.",
1064
+ ));
1065
+ }
1066
+
1067
+ PLUGIN_DEST = join(OPENCLAW_DIR, "extensions", resolvedPluginId || "openviking");
1068
+ info(tr(`Plugin: ${resolvedPluginId} (${resolvedPluginKind})`, `Plugin: ${resolvedPluginId} (${resolvedPluginKind})`));
1069
+ }
1070
+
1071
+ // Resolve plugin configuration from manifest or fallback
1072
+ async function resolvePluginConfig() {
1073
+ if (pluginSource === "npm") {
1074
+ try {
1075
+ await resolvePluginConfigFromNpm();
1076
+ return;
1077
+ } catch (error) {
1078
+ warn(tr(
1079
+ `npm plugin resolution failed: ${error?.message || error}`,
1080
+ `npm plugin resolution failed: ${error?.message || error}`,
1081
+ ));
1082
+ warn(tr(
1083
+ "Falling back to GitHub plugin download.",
1084
+ "Falling back to GitHub plugin download.",
1085
+ ));
1086
+ pluginSource = "github";
1087
+ }
1088
+ }
1089
+
1090
+ const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
794
1091
 
795
1092
  info(tr(`Resolving plugin configuration for version: ${PLUGIN_VERSION}`, `正在解析插件配置,版本: ${PLUGIN_VERSION}`));
796
1093
 
@@ -1562,7 +1859,7 @@ async function assertBuiltRuntimeOutputs(destDir) {
1562
1859
  process.exit(1);
1563
1860
  }
1564
1861
 
1565
- async function installPluginNpmDependencies(destDir) {
1862
+ async function installPluginNpmDependencies(destDir) {
1566
1863
  if (!resolvedNpmBuild) {
1567
1864
  info(tr("Installing plugin npm dependencies...", "正在安装插件 npm 依赖..."));
1568
1865
  const npmArgs = resolvedNpmOmitDev
@@ -1600,12 +1897,40 @@ async function installPluginNpmDependencies(destDir) {
1600
1897
  "--no-audit",
1601
1898
  "--no-fund",
1602
1899
  ], { cwd: destDir, silent: false });
1603
- }
1604
- }
1605
-
1606
- async function downloadPlugin(destDir) {
1607
- const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
1608
- const pluginDir = resolvedPluginDir;
1900
+ }
1901
+ }
1902
+
1903
+ async function copyNpmPackageToDest(destDir) {
1904
+ const packageDir = await ensureNpmPackageExtracted();
1905
+ await mkdir(destDir, { recursive: true });
1906
+ const entries = readdirSync(packageDir, { withFileTypes: true });
1907
+ for (const entry of entries) {
1908
+ await cp(join(packageDir, entry.name), join(destDir, entry.name), { recursive: true, force: true });
1909
+ }
1910
+ }
1911
+
1912
+ async function cleanupNpmPackageTemp() {
1913
+ if (!npmPackageTempDir) return;
1914
+ await rm(npmPackageTempDir, { recursive: true, force: true });
1915
+ npmPackageTempDir = "";
1916
+ npmPackageExtractDir = "";
1917
+ }
1918
+
1919
+ async function downloadPlugin(destDir) {
1920
+ if (pluginSource === "npm") {
1921
+ await mkdir(destDir, { recursive: true });
1922
+ info(tr(
1923
+ `Installing plugin from npm package ${npmPackageSpec()}...`,
1924
+ `Installing plugin from npm package ${npmPackageSpec()}...`,
1925
+ ));
1926
+ await copyNpmPackageToDest(destDir);
1927
+ await installPluginNpmDependencies(destDir);
1928
+ info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `Plugin deployed: ${PLUGIN_DEST}`));
1929
+ return;
1930
+ }
1931
+
1932
+ const ghRaw = `https://raw.githubusercontent.com/${REPO}/${PLUGIN_VERSION}`;
1933
+ const pluginDir = resolvedPluginDir;
1609
1934
  const total = resolvedFilesRequired.length + resolvedFilesOptional.length;
1610
1935
 
1611
1936
  await mkdir(destDir, { recursive: true });
@@ -1662,16 +1987,18 @@ async function finalizePluginDeployment(stagingDir) {
1662
1987
  return info(tr(`Plugin deployed: ${PLUGIN_DEST}`, `插件部署完成: ${PLUGIN_DEST}`));
1663
1988
  }
1664
1989
 
1665
- async function deployPluginFromRemote() {
1666
- const stagingDir = await createPluginStagingDir();
1667
- try {
1668
- await downloadPlugin(stagingDir);
1669
- await finalizePluginDeployment(stagingDir);
1670
- } catch (error) {
1671
- await rm(stagingDir, { recursive: true, force: true });
1672
- throw error;
1673
- }
1674
- }
1990
+ async function deployPluginFromRemote() {
1991
+ const stagingDir = await createPluginStagingDir();
1992
+ try {
1993
+ await downloadPlugin(stagingDir);
1994
+ await finalizePluginDeployment(stagingDir);
1995
+ } catch (error) {
1996
+ await rm(stagingDir, { recursive: true, force: true });
1997
+ throw error;
1998
+ } finally {
1999
+ await cleanupNpmPackageTemp();
2000
+ }
2001
+ }
1675
2002
 
1676
2003
  /** Same as INSTALL*.md manual cleanup: stale entries block `plugins.slots.*` validation after reinstall. */
1677
2004
  function resolvedPluginSlotFallback() {
@@ -2374,11 +2701,16 @@ async function main() {
2374
2701
  await rollbackLastUpgradeOperation();
2375
2702
  return;
2376
2703
  }
2377
- await resolveDefaultPluginVersion();
2378
- validateRequestedPluginVersion();
2379
- info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
2380
- info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
2381
- info(tr(`Plugin version: ${PLUGIN_VERSION}`, `插件版本: ${PLUGIN_VERSION}`));
2704
+ await resolveDefaultPluginVersion();
2705
+ validateRequestedPluginVersion();
2706
+ info(tr(`Target: ${OPENCLAW_DIR}`, `目标实例: ${OPENCLAW_DIR}`));
2707
+ info(tr(`Plugin source: ${pluginSource}`, `Plugin source: ${pluginSource}`));
2708
+ if (pluginSource === "npm") {
2709
+ info(tr(`Plugin package: ${pluginNpmPackage}`, `Plugin package: ${pluginNpmPackage}`));
2710
+ } else {
2711
+ info(tr(`Repository: ${REPO}`, `仓库: ${REPO}`));
2712
+ }
2713
+ info(tr(`Plugin version: ${PLUGIN_VERSION}`, `插件版本: ${PLUGIN_VERSION}`));
2382
2714
 
2383
2715
  if (upgradePluginOnly) {
2384
2716
  info(tr("Mode: plugin upgrade only", "模式: 仅升级插件"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-openviking-setup-helper",
3
- "version": "0.3.0-beta.26",
3
+ "version": "0.3.0-beta.27",
4
4
  "description": "Setup helper for installing OpenViking memory plugin into OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {