@xfxstudio/claworld 0.2.10-beta.1 → 0.2.10-beta.2
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/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
CLAWORLD_DOCTOR_COMMAND,
|
|
8
8
|
CLAWORLD_INSTALLER_BIN_NAME,
|
|
9
9
|
CLAWORLD_INSTALLER_COMMAND,
|
|
10
|
-
|
|
10
|
+
CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
11
11
|
CLAWORLD_UNINSTALL_COMMAND,
|
|
12
12
|
CLAWORLD_UPDATE_COMMAND,
|
|
13
13
|
} from './constants.js';
|
|
@@ -57,7 +57,7 @@ Install options:
|
|
|
57
57
|
--tool-profile <profile> minimal | default | full | world
|
|
58
58
|
--repo-root <path> Local repo root when using --plugin-install-mode link|copy
|
|
59
59
|
--plugin-install-mode <m> npm | link | copy | skip (default: npm)
|
|
60
|
-
--plugin-source <value> npm package or local path (default: ${
|
|
60
|
+
--plugin-source <value> npm package or local path (default: ${CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE})
|
|
61
61
|
Update options:
|
|
62
62
|
--config <path> OpenClaw config path (default: ${DEFAULT_OPENCLAW_CONFIG_PATH})
|
|
63
63
|
--state-dir <path> Optional OPENCLAW_STATE_DIR for OpenClaw commands
|
|
@@ -118,7 +118,7 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
118
118
|
displayName: null,
|
|
119
119
|
repoRoot: null,
|
|
120
120
|
pluginInstallMode: 'npm',
|
|
121
|
-
pluginInstallSource:
|
|
121
|
+
pluginInstallSource: CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
122
122
|
toolProfile: null,
|
|
123
123
|
},
|
|
124
124
|
update: {
|
|
@@ -266,7 +266,7 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
266
266
|
options.command === 'install'
|
|
267
267
|
&& options.install.pluginInstallMode === 'link'
|
|
268
268
|
&& !options.install.repoRoot
|
|
269
|
-
&& options.install.pluginInstallSource ===
|
|
269
|
+
&& options.install.pluginInstallSource === CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE
|
|
270
270
|
) {
|
|
271
271
|
throw new Error('Link install mode requires --repo-root or --plugin-source <local-path>.');
|
|
272
272
|
}
|
|
@@ -274,7 +274,7 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
274
274
|
options.command === 'install'
|
|
275
275
|
&& options.install.pluginInstallMode === 'copy'
|
|
276
276
|
&& !options.install.repoRoot
|
|
277
|
-
&& options.install.pluginInstallSource ===
|
|
277
|
+
&& options.install.pluginInstallSource === CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE
|
|
278
278
|
) {
|
|
279
279
|
throw new Error('Copy install mode requires --repo-root or --plugin-source <local-path>.');
|
|
280
280
|
}
|
|
@@ -283,7 +283,7 @@ export function parseInstallerCliArgs(argv = process.argv.slice(2), env = proces
|
|
|
283
283
|
options.command === 'install'
|
|
284
284
|
&& options.install.pluginInstallMode !== 'npm'
|
|
285
285
|
&& options.install.repoRoot
|
|
286
|
-
&& options.install.pluginInstallSource ===
|
|
286
|
+
&& options.install.pluginInstallSource === CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE
|
|
287
287
|
) {
|
|
288
288
|
options.install.pluginInstallSource = options.install.repoRoot;
|
|
289
289
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const installerPackageJson = require('../../../package.json');
|
|
5
|
+
|
|
1
6
|
export const CLAWORLD_INSTALLER_BIN_NAME = 'claworld';
|
|
2
7
|
export const CLAWORLD_INSTALLER_PACKAGE_NAME = '@xfxstudio/claworld';
|
|
8
|
+
export const CLAWORLD_INSTALLER_PACKAGE_VERSION = installerPackageJson.version;
|
|
9
|
+
export const CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE = `${CLAWORLD_INSTALLER_PACKAGE_NAME}@${CLAWORLD_INSTALLER_PACKAGE_VERSION}`;
|
|
3
10
|
export const CLAWORLD_INSTALLER_COMMAND = 'npx -y @xfxstudio/claworld install';
|
|
4
11
|
export const CLAWORLD_DOCTOR_COMMAND = 'npx -y @xfxstudio/claworld doctor';
|
|
5
12
|
export const CLAWORLD_UPDATE_COMMAND = 'npx -y @xfxstudio/claworld update';
|
|
@@ -5,10 +5,10 @@ import path from 'path';
|
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import vm from 'vm';
|
|
7
7
|
import {
|
|
8
|
+
applyClaworldBootstrapConfig,
|
|
8
9
|
DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
9
10
|
DEFAULT_CLAWORLD_AGENT_ID,
|
|
10
11
|
DEFAULT_CLAWORLD_SERVER_URL,
|
|
11
|
-
applyClaworldManagedRuntimeConfig,
|
|
12
12
|
ensureObject,
|
|
13
13
|
expandUserPath,
|
|
14
14
|
findClaworldManagedRuntimeBackup,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from '../plugin/config-schema.js';
|
|
25
25
|
import {
|
|
26
26
|
CLAWORLD_INSTALLER_COMMAND,
|
|
27
|
+
CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
27
28
|
CLAWORLD_INSTALLER_PACKAGE_NAME,
|
|
28
29
|
CLAWORLD_OPENCLAW_MIN_HOST_VERSION,
|
|
29
30
|
CLAWORLD_UNINSTALL_COMMAND,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
import { seedManagedWorkspaceContract } from './workspace-contract.js';
|
|
33
34
|
|
|
34
35
|
export const DEFAULT_OPENCLAW_BIN = 'openclaw';
|
|
36
|
+
const DEFAULT_NPM_BIN = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
35
37
|
export const DEFAULT_OPENCLAW_CONFIG_PATH = '~/.openclaw/openclaw.json';
|
|
36
38
|
export const DEFAULT_OPENCLAW_STATE_DIR = null;
|
|
37
39
|
export const DEFAULT_INSTALL_TIMEOUT_MS = 15_000;
|
|
@@ -54,9 +56,28 @@ function normalizeComparablePath(value) {
|
|
|
54
56
|
return path.resolve(String(value));
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
function splitPackageNameSegments(packageName = CLAWORLD_INSTALLER_PACKAGE_NAME) {
|
|
60
|
+
const normalized = String(packageName || '').trim();
|
|
61
|
+
if (!normalized) return ['claworld'];
|
|
62
|
+
return normalized.split('/').filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveInstallerManagedPluginInstallRoot(configPath = DEFAULT_OPENCLAW_CONFIG_PATH) {
|
|
66
|
+
const resolvedConfigPath = path.resolve(expandUserPath(configPath, os.homedir()));
|
|
67
|
+
return path.join(path.dirname(resolvedConfigPath), 'extensions', 'claworld');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveInstallerManagedPluginSourcePath(installRoot, packageName = CLAWORLD_INSTALLER_PACKAGE_NAME) {
|
|
71
|
+
return path.join(installRoot, 'node_modules', ...splitPackageNameSegments(packageName));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function readJsonFile(filePath) {
|
|
75
|
+
return JSON.parse(await fs.readFile(filePath, 'utf8'));
|
|
76
|
+
}
|
|
77
|
+
|
|
57
78
|
async function resolveLocalPluginInstallTarget({
|
|
58
79
|
installMode = 'npm',
|
|
59
|
-
installSource =
|
|
80
|
+
installSource = CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
60
81
|
repoRoot = null,
|
|
61
82
|
commandRunner = defaultCommandRunner,
|
|
62
83
|
cwd = process.cwd(),
|
|
@@ -85,6 +106,7 @@ async function resolveLocalPluginInstallTarget({
|
|
|
85
106
|
resolvedInstallSource === derivedRepoRoot
|
|
86
107
|
|| resolvedInstallSource === path.join(derivedRepoRoot, 'packages', 'openclaw-plugin')
|
|
87
108
|
|| resolvedInstallSource === CLAWORLD_INSTALLER_PACKAGE_NAME
|
|
109
|
+
|| resolvedInstallSource === CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE
|
|
88
110
|
),
|
|
89
111
|
);
|
|
90
112
|
|
|
@@ -444,6 +466,7 @@ function inspectTrackedClaworldPluginInstall(config = {}) {
|
|
|
444
466
|
resolvedSpec: normalizeText(installRecord.resolvedSpec, null),
|
|
445
467
|
resolvedVersion: normalizeText(installRecord.resolvedVersion, null),
|
|
446
468
|
installPath: normalizeText(installRecord.installPath, null),
|
|
469
|
+
sourcePath: normalizeText(installRecord.sourcePath, null),
|
|
447
470
|
record: tracked ? installRecord : null,
|
|
448
471
|
};
|
|
449
472
|
}
|
|
@@ -527,6 +550,67 @@ function mergePluginMetadata(config = {}, pluginConfig = {}) {
|
|
|
527
550
|
return next;
|
|
528
551
|
}
|
|
529
552
|
|
|
553
|
+
async function installPublishedClaworldPackageToLocalSource({
|
|
554
|
+
installSource = CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
555
|
+
configPath = DEFAULT_OPENCLAW_CONFIG_PATH,
|
|
556
|
+
commandRunner = defaultCommandRunner,
|
|
557
|
+
cwd = process.cwd(),
|
|
558
|
+
env = process.env,
|
|
559
|
+
dryRun = false,
|
|
560
|
+
refresh = false,
|
|
561
|
+
} = {}) {
|
|
562
|
+
const installRoot = resolveInstallerManagedPluginInstallRoot(configPath);
|
|
563
|
+
const sourcePath = resolveInstallerManagedPluginSourcePath(installRoot);
|
|
564
|
+
if (!dryRun && refresh) {
|
|
565
|
+
await fs.rm(installRoot, { recursive: true, force: true });
|
|
566
|
+
}
|
|
567
|
+
if (!dryRun) {
|
|
568
|
+
await fs.mkdir(installRoot, { recursive: true });
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
await executeCommand({
|
|
572
|
+
commandRunner,
|
|
573
|
+
bin: DEFAULT_NPM_BIN,
|
|
574
|
+
args: [
|
|
575
|
+
'install',
|
|
576
|
+
'--ignore-scripts',
|
|
577
|
+
'--no-package-lock',
|
|
578
|
+
'--omit=dev',
|
|
579
|
+
'--prefix',
|
|
580
|
+
installRoot,
|
|
581
|
+
installSource,
|
|
582
|
+
],
|
|
583
|
+
cwd,
|
|
584
|
+
env,
|
|
585
|
+
dryRun,
|
|
586
|
+
capture: false,
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
let resolvedVersion = null;
|
|
590
|
+
if (!dryRun) {
|
|
591
|
+
const sourcePackageJson = await readJsonFile(path.join(sourcePath, 'package.json'));
|
|
592
|
+
resolvedVersion = normalizeText(sourcePackageJson?.version, null);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return {
|
|
596
|
+
installRoot,
|
|
597
|
+
sourcePath,
|
|
598
|
+
resolvedVersion,
|
|
599
|
+
pluginConfig: {
|
|
600
|
+
installs: {
|
|
601
|
+
claworld: {
|
|
602
|
+
source: 'installer_npm',
|
|
603
|
+
spec: installSource,
|
|
604
|
+
resolvedSpec: installSource,
|
|
605
|
+
...(resolvedVersion ? { resolvedVersion } : {}),
|
|
606
|
+
installPath: installRoot,
|
|
607
|
+
sourcePath,
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
|
|
530
614
|
function defaultCommandRunner({
|
|
531
615
|
bin,
|
|
532
616
|
args,
|
|
@@ -863,9 +947,10 @@ export async function ensureClaworldPluginInstalled({
|
|
|
863
947
|
env = process.env,
|
|
864
948
|
dryRun = false,
|
|
865
949
|
installMode = 'npm',
|
|
866
|
-
installSource =
|
|
950
|
+
installSource = CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
867
951
|
refresh = false,
|
|
868
952
|
} = {}) {
|
|
953
|
+
const trackedInstall = inspectTrackedClaworldPluginInstall(config);
|
|
869
954
|
const bootstrap = await preparePluginBootstrapConfig({
|
|
870
955
|
configPath,
|
|
871
956
|
config,
|
|
@@ -883,7 +968,17 @@ export async function ensureClaworldPluginInstalled({
|
|
|
883
968
|
env,
|
|
884
969
|
dryRun,
|
|
885
970
|
});
|
|
886
|
-
|
|
971
|
+
const reusableInstallerManagedNpmInstall = (
|
|
972
|
+
installMode === 'npm'
|
|
973
|
+
&& trackedInstall.tracked
|
|
974
|
+
&& trackedInstall.source === 'installer_npm'
|
|
975
|
+
&& normalizeText(trackedInstall.sourcePath, null)
|
|
976
|
+
);
|
|
977
|
+
if (
|
|
978
|
+
before.installed
|
|
979
|
+
&& !refresh
|
|
980
|
+
&& (installMode !== 'npm' || reusableInstallerManagedNpmInstall)
|
|
981
|
+
) {
|
|
887
982
|
return {
|
|
888
983
|
changed: false,
|
|
889
984
|
action: 'reused_existing_plugin_install',
|
|
@@ -901,6 +996,37 @@ export async function ensureClaworldPluginInstalled({
|
|
|
901
996
|
};
|
|
902
997
|
}
|
|
903
998
|
|
|
999
|
+
if (installMode === 'npm') {
|
|
1000
|
+
const localInstall = await installPublishedClaworldPackageToLocalSource({
|
|
1001
|
+
installSource,
|
|
1002
|
+
configPath,
|
|
1003
|
+
commandRunner,
|
|
1004
|
+
cwd,
|
|
1005
|
+
env,
|
|
1006
|
+
dryRun,
|
|
1007
|
+
refresh,
|
|
1008
|
+
});
|
|
1009
|
+
return {
|
|
1010
|
+
changed: true,
|
|
1011
|
+
action: reusableInstallerManagedNpmInstall ? 'refreshed_local_plugin_source' : 'staged_local_plugin_source',
|
|
1012
|
+
plugin: before,
|
|
1013
|
+
pluginConfig: {
|
|
1014
|
+
...ensureObject(bootstrap?.pluginConfig),
|
|
1015
|
+
...ensureObject(localInstall.pluginConfig),
|
|
1016
|
+
entries: {
|
|
1017
|
+
...ensureObject(bootstrap?.pluginConfig?.entries),
|
|
1018
|
+
...ensureObject(localInstall.pluginConfig?.entries),
|
|
1019
|
+
},
|
|
1020
|
+
installs: {
|
|
1021
|
+
...ensureObject(bootstrap?.pluginConfig?.installs),
|
|
1022
|
+
...ensureObject(localInstall.pluginConfig?.installs),
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
installSource,
|
|
1026
|
+
managedRepoRoot: localInstall.sourcePath,
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
|
|
904
1030
|
if (before.installed && refresh && installMode === 'copy') {
|
|
905
1031
|
await executeCommand({
|
|
906
1032
|
commandRunner,
|
|
@@ -990,6 +1116,26 @@ export async function updateTrackedClaworldPluginInstall({
|
|
|
990
1116
|
);
|
|
991
1117
|
}
|
|
992
1118
|
|
|
1119
|
+
if (trackedInstall.source === 'installer_npm') {
|
|
1120
|
+
const localInstall = await installPublishedClaworldPackageToLocalSource({
|
|
1121
|
+
installSource: trackedInstall.spec || CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
1122
|
+
configPath,
|
|
1123
|
+
commandRunner,
|
|
1124
|
+
cwd,
|
|
1125
|
+
env,
|
|
1126
|
+
dryRun,
|
|
1127
|
+
refresh: true,
|
|
1128
|
+
});
|
|
1129
|
+
return {
|
|
1130
|
+
changed: true,
|
|
1131
|
+
action: dryRun ? 'dry_run_updated_local_plugin_source' : 'updated_local_plugin_source',
|
|
1132
|
+
trackedInstall,
|
|
1133
|
+
plugin: before,
|
|
1134
|
+
pluginConfig: localInstall.pluginConfig,
|
|
1135
|
+
managedRepoRoot: localInstall.sourcePath,
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
|
|
993
1139
|
if (!trackedInstall.tracked) {
|
|
994
1140
|
return {
|
|
995
1141
|
changed: false,
|
|
@@ -1467,7 +1613,7 @@ async function reconcileManagedClaworldRuntime({
|
|
|
1467
1613
|
},
|
|
1468
1614
|
});
|
|
1469
1615
|
|
|
1470
|
-
const transformed =
|
|
1616
|
+
const transformed = applyClaworldBootstrapConfig(currentConfig, managedOptions);
|
|
1471
1617
|
const configChanged = JSON.stringify(currentConfig) !== JSON.stringify(transformed.config);
|
|
1472
1618
|
const backupPath = configChanged
|
|
1473
1619
|
? await backupConfigIfPresent(configPath, currentConfigState.existed, dryRun)
|
|
@@ -1560,7 +1706,7 @@ export async function runClaworldInstallerInstall({
|
|
|
1560
1706
|
sessionDmScope = null,
|
|
1561
1707
|
repoRoot = null,
|
|
1562
1708
|
pluginInstallMode = 'npm',
|
|
1563
|
-
pluginInstallSource =
|
|
1709
|
+
pluginInstallSource = CLAWORLD_INSTALLER_DEFAULT_PLUGIN_SOURCE,
|
|
1564
1710
|
fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
1565
1711
|
commandRunner = defaultCommandRunner,
|
|
1566
1712
|
cwd = process.cwd(),
|
|
@@ -1639,7 +1785,7 @@ export async function runClaworldInstallerInstall({
|
|
|
1639
1785
|
toolProfile,
|
|
1640
1786
|
approvalMode,
|
|
1641
1787
|
sessionDmScope,
|
|
1642
|
-
repoRoot: localPluginInstall.managedRepoRoot,
|
|
1788
|
+
repoRoot: plugin.managedRepoRoot || localPluginInstall.managedRepoRoot,
|
|
1643
1789
|
fetchImpl,
|
|
1644
1790
|
commandRunner,
|
|
1645
1791
|
cwd,
|
|
@@ -1717,7 +1863,7 @@ export async function runClaworldInstallerUpdate({
|
|
|
1717
1863
|
});
|
|
1718
1864
|
|
|
1719
1865
|
const refreshedConfigState = await loadConfigFromDisk(resolvedConfigPath);
|
|
1720
|
-
const currentConfig = mergePluginMetadata(refreshedConfigState.config, null);
|
|
1866
|
+
const currentConfig = mergePluginMetadata(refreshedConfigState.config, plugin.pluginConfig || null);
|
|
1721
1867
|
const lifecycle = await reconcileManagedClaworldRuntime({
|
|
1722
1868
|
openclawBin,
|
|
1723
1869
|
configPath: resolvedConfigPath,
|
|
@@ -1736,6 +1882,7 @@ export async function runClaworldInstallerUpdate({
|
|
|
1736
1882
|
toolProfile,
|
|
1737
1883
|
approvalMode,
|
|
1738
1884
|
sessionDmScope,
|
|
1885
|
+
repoRoot: plugin.managedRepoRoot || null,
|
|
1739
1886
|
fetchImpl,
|
|
1740
1887
|
commandRunner,
|
|
1741
1888
|
cwd,
|
|
@@ -1778,6 +1925,7 @@ export async function runClaworldInstallerUninstall({
|
|
|
1778
1925
|
const currentConfigState = await loadConfigFromDisk(resolvedConfigPath);
|
|
1779
1926
|
const currentInstallerState = (await loadInstallerStateFromDisk(installerStatePath)).state;
|
|
1780
1927
|
const currentConfig = currentConfigState.config;
|
|
1928
|
+
const trackedInstall = inspectTrackedClaworldPluginInstall(currentConfig);
|
|
1781
1929
|
const host = await detectOpenclawHost({
|
|
1782
1930
|
openclawBin,
|
|
1783
1931
|
commandRunner,
|
|
@@ -1841,7 +1989,20 @@ export async function runClaworldInstallerUninstall({
|
|
|
1841
1989
|
|
|
1842
1990
|
let pluginAction = 'plugin_already_absent';
|
|
1843
1991
|
try {
|
|
1844
|
-
if (
|
|
1992
|
+
if (trackedInstall.source === 'installer_npm') {
|
|
1993
|
+
const localInstallPath = normalizeText(trackedInstall.installPath, null) || normalizeText(trackedInstall.sourcePath, null);
|
|
1994
|
+
if (!localInstallPath) {
|
|
1995
|
+
throw createInstallerError(
|
|
1996
|
+
'claworld_installer_managed_source_missing',
|
|
1997
|
+
'Installer-managed Claworld source is missing its local install path.',
|
|
1998
|
+
{ trackedInstall },
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
if (!dryRun) {
|
|
2002
|
+
await fs.rm(localInstallPath, { recursive: true, force: true });
|
|
2003
|
+
}
|
|
2004
|
+
pluginAction = 'removed_local_plugin_source';
|
|
2005
|
+
} else if (pluginBefore.installed) {
|
|
1845
2006
|
await executeCommand({
|
|
1846
2007
|
commandRunner,
|
|
1847
2008
|
bin: openclawBin,
|
|
@@ -1881,7 +2042,7 @@ export async function runClaworldInstallerUninstall({
|
|
|
1881
2042
|
env,
|
|
1882
2043
|
dryRun,
|
|
1883
2044
|
});
|
|
1884
|
-
if (pluginBefore.installed && pluginAfter.installed) {
|
|
2045
|
+
if ((pluginBefore.installed || trackedInstall.source === 'installer_npm') && pluginAfter.installed) {
|
|
1885
2046
|
throw createInstallerError(
|
|
1886
2047
|
'claworld_plugin_uninstall_failed',
|
|
1887
2048
|
'OpenClaw still reports the claworld plugin as installed after uninstall.',
|