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.
- package/install.js +432 -100
- 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=
|
|
15
|
-
*
|
|
16
|
-
* Environment variables:
|
|
17
|
-
* REPO, PLUGIN_VERSION (or BRANCH),
|
|
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 {
|
|
26
|
-
import {
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
let
|
|
34
|
-
let
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
208
|
-
|
|
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(" --
|
|
284
|
-
console.log("
|
|
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=
|
|
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
|
-
|
|
632
|
-
|
|
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
|
-
|
|
716
|
-
|
|
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
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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
|
|
1607
|
-
const
|
|
1608
|
-
|
|
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(`
|
|
2381
|
-
|
|
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", "模式: 仅升级插件"));
|