@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.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "0.2.10-beta.1",
11
+ "version": "0.2.10-beta.2",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "0.2.10-beta.1",
3
+ "version": "0.2.10-beta.2",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -7,7 +7,7 @@ import {
7
7
  CLAWORLD_DOCTOR_COMMAND,
8
8
  CLAWORLD_INSTALLER_BIN_NAME,
9
9
  CLAWORLD_INSTALLER_COMMAND,
10
- CLAWORLD_INSTALLER_PACKAGE_NAME,
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: ${CLAWORLD_INSTALLER_PACKAGE_NAME})
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: CLAWORLD_INSTALLER_PACKAGE_NAME,
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 === CLAWORLD_INSTALLER_PACKAGE_NAME
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 === CLAWORLD_INSTALLER_PACKAGE_NAME
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 === CLAWORLD_INSTALLER_PACKAGE_NAME
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 = CLAWORLD_INSTALLER_PACKAGE_NAME,
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 = CLAWORLD_INSTALLER_PACKAGE_NAME,
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
- if (before.installed && !refresh) {
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 = applyClaworldManagedRuntimeConfig(currentConfig, managedOptions);
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 = CLAWORLD_INSTALLER_PACKAGE_NAME,
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 (pluginBefore.installed) {
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.',