@xfxstudio/claworld 0.2.9 → 0.2.10-beta.1
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/README.md +1 -1
- package/openclaw.plugin.json +7 -63
- package/package.json +6 -2
- package/skills/claworld-help/SKILL.md +5 -1
- package/skills/claworld-join-and-chat/SKILL.md +21 -1
- package/skills/claworld-manage-worlds/SKILL.md +81 -10
- package/src/lib/agent-profile.js +8 -3
- package/src/lib/chat-request.js +0 -1
- package/src/lib/policy.js +2 -6
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/kickoff-text.js +1 -0
- package/src/openclaw/installer/cli.js +48 -4
- package/src/openclaw/installer/constants.js +1 -0
- package/src/openclaw/installer/core.js +247 -71
- package/src/openclaw/installer/doctor.js +31 -17
- package/src/openclaw/plugin/account-identity.js +1 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +453 -263
- package/src/openclaw/plugin/config-schema.js +9 -23
- package/src/openclaw/plugin/managed-config.js +294 -84
- package/src/openclaw/plugin/onboarding.js +37 -45
- package/src/openclaw/plugin/register.js +124 -13
- package/src/openclaw/plugin/relay-client.js +233 -17
- package/src/openclaw/runtime/backend-error-context.js +91 -0
- package/src/openclaw/runtime/feedback-helper.js +1 -2
- package/src/openclaw/runtime/product-shell-helper.js +43 -9
- package/src/openclaw/runtime/tool-contracts.js +26 -3
- package/src/openclaw/runtime/tool-inventory.js +7 -0
- package/src/openclaw/runtime/world-moderation-helper.js +3 -19
- package/src/product-shell/contracts/candidate-feed.js +7 -0
- package/src/product-shell/contracts/world-manifest.js +0 -1
- package/src/product-shell/contracts/world-orchestration.js +10 -1
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
- package/src/product-shell/feedback/feedback-routes.js +0 -1
- package/src/product-shell/feedback/feedback-service.js +4 -9
- package/src/product-shell/index.js +40 -7
- package/src/product-shell/matching/matchmaking-service.js +22 -1
- package/src/product-shell/membership/membership-service.js +5 -1
- package/src/product-shell/onboarding/onboarding-service.js +16 -26
- package/src/product-shell/profile/public-identity-routes.js +60 -0
- package/src/product-shell/profile/public-identity-service.js +190 -0
- package/src/product-shell/search/search-service.js +9 -2
- package/src/product-shell/social/chat-request-service.js +22 -7
- package/src/product-shell/social/friend-routes.js +1 -1
- package/src/product-shell/social/friend-service.js +16 -19
- package/src/product-shell/social/social-routes.js +2 -2
- package/src/product-shell/social/social-service.js +31 -35
- package/src/product-shell/worlds/world-admin-service.js +31 -10
- package/src/product-shell/worlds/world-broadcast-service.js +2 -2
- package/src/lib/agent-address.js +0 -46
|
@@ -11,8 +11,11 @@ import {
|
|
|
11
11
|
applyClaworldManagedRuntimeConfig,
|
|
12
12
|
ensureObject,
|
|
13
13
|
expandUserPath,
|
|
14
|
+
findClaworldManagedRuntimeBackup,
|
|
14
15
|
normalizeText,
|
|
15
16
|
resolveClaworldManagedRuntimeOptions,
|
|
17
|
+
setClaworldManagedRuntimeBackupState,
|
|
18
|
+
stripClaworldManagedRuntimeConfig,
|
|
16
19
|
} from '../plugin/managed-config.js';
|
|
17
20
|
import {
|
|
18
21
|
defaultClaworldAccountId,
|
|
@@ -23,6 +26,7 @@ import {
|
|
|
23
26
|
CLAWORLD_INSTALLER_COMMAND,
|
|
24
27
|
CLAWORLD_INSTALLER_PACKAGE_NAME,
|
|
25
28
|
CLAWORLD_OPENCLAW_MIN_HOST_VERSION,
|
|
29
|
+
CLAWORLD_UNINSTALL_COMMAND,
|
|
26
30
|
CLAWORLD_UPDATE_COMMAND,
|
|
27
31
|
} from './constants.js';
|
|
28
32
|
import { seedManagedWorkspaceContract } from './workspace-contract.js';
|
|
@@ -139,13 +143,7 @@ export function isManagedToolAllowlistReady(config = {}, options = {}) {
|
|
|
139
143
|
export function isRelayBootstrapReady(account = {}) {
|
|
140
144
|
return Boolean(
|
|
141
145
|
account?.configured
|
|
142
|
-
&& (
|
|
143
|
-
normalizeText(account?.appToken, null)
|
|
144
|
-
|| (
|
|
145
|
-
account?.registration?.enabled === true
|
|
146
|
-
&& normalizeText(account?.registration?.agentCode, null)
|
|
147
|
-
)
|
|
148
|
-
),
|
|
146
|
+
&& normalizeText(account?.appToken, null),
|
|
149
147
|
);
|
|
150
148
|
}
|
|
151
149
|
|
|
@@ -658,6 +656,23 @@ export async function loadConfigFromDisk(configPath) {
|
|
|
658
656
|
}
|
|
659
657
|
}
|
|
660
658
|
|
|
659
|
+
export function resolveClaworldInstallerStatePath(configPath) {
|
|
660
|
+
const resolvedConfigPath = path.resolve(String(configPath || DEFAULT_OPENCLAW_CONFIG_PATH));
|
|
661
|
+
return path.join(path.dirname(resolvedConfigPath), '.claworld-installer-state.json');
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export async function loadInstallerStateFromDisk(installerStatePath) {
|
|
665
|
+
try {
|
|
666
|
+
const raw = await fs.readFile(installerStatePath, 'utf8');
|
|
667
|
+
return { existed: true, state: parseConfigObject(raw, installerStatePath) };
|
|
668
|
+
} catch (error) {
|
|
669
|
+
if (error && error.code === 'ENOENT') {
|
|
670
|
+
return { existed: false, state: {} };
|
|
671
|
+
}
|
|
672
|
+
throw error;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
661
676
|
export async function backupConfigIfPresent(configPath, existed, dryRun = false) {
|
|
662
677
|
if (!existed) return null;
|
|
663
678
|
const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+$/, '').replace('T', '-');
|
|
@@ -675,6 +690,20 @@ export async function writeConfig(configPath, config, dryRun = false) {
|
|
|
675
690
|
return rendered;
|
|
676
691
|
}
|
|
677
692
|
|
|
693
|
+
export async function writeInstallerState(installerStatePath, installerState, dryRun = false) {
|
|
694
|
+
const renderedState = ensureObject(installerState);
|
|
695
|
+
if (Object.keys(renderedState).length === 0) {
|
|
696
|
+
if (dryRun) return '';
|
|
697
|
+
await fs.rm(installerStatePath, { force: true });
|
|
698
|
+
return '';
|
|
699
|
+
}
|
|
700
|
+
const rendered = `${JSON.stringify(renderedState, null, 2)}\n`;
|
|
701
|
+
if (dryRun) return rendered;
|
|
702
|
+
await fs.mkdir(path.dirname(installerStatePath), { recursive: true });
|
|
703
|
+
await fs.writeFile(installerStatePath, rendered, 'utf8');
|
|
704
|
+
return rendered;
|
|
705
|
+
}
|
|
706
|
+
|
|
678
707
|
export function buildOpenclawCommandEnv({ configPath, stateDir = null, env = process.env } = {}) {
|
|
679
708
|
return {
|
|
680
709
|
...env,
|
|
@@ -688,6 +717,7 @@ export function inspectManagedClaworldInstall({
|
|
|
688
717
|
accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
689
718
|
input = {},
|
|
690
719
|
overrides = {},
|
|
720
|
+
installerState = {},
|
|
691
721
|
} = {}) {
|
|
692
722
|
const configuredAccountIds = listClaworldAccountIds(cfg);
|
|
693
723
|
const hasAnyConfig = configuredAccountIds.length > 0 || cfg?.channels?.claworld != null;
|
|
@@ -696,6 +726,7 @@ export function inspectManagedClaworldInstall({
|
|
|
696
726
|
accountId,
|
|
697
727
|
input,
|
|
698
728
|
overrides,
|
|
729
|
+
installerState,
|
|
699
730
|
});
|
|
700
731
|
const managedAgentPresent = Boolean(findAgentEntry(cfg, managedOptions.agentId));
|
|
701
732
|
const managedBindingPresent = hasManagedBinding(cfg, managedOptions);
|
|
@@ -719,8 +750,8 @@ export function inspectManagedClaworldInstall({
|
|
|
719
750
|
statusLabel = managedReady ? 'configured' : 'configured (managed refresh recommended)';
|
|
720
751
|
selectionHint = managedReady ? 'configured · managed runtime' : 'configured · managed refresh';
|
|
721
752
|
} else if (hasAnyConfig) {
|
|
722
|
-
statusLabel = '
|
|
723
|
-
selectionHint = '
|
|
753
|
+
statusLabel = 'configured (activation pending)';
|
|
754
|
+
selectionHint = 'configured · activation pending';
|
|
724
755
|
}
|
|
725
756
|
|
|
726
757
|
return {
|
|
@@ -735,7 +766,10 @@ export function inspectManagedClaworldInstall({
|
|
|
735
766
|
accountStatus,
|
|
736
767
|
managedRuntimeReady,
|
|
737
768
|
managedReady,
|
|
738
|
-
reusableAppToken: normalizeText(
|
|
769
|
+
reusableAppToken: normalizeText(
|
|
770
|
+
managedOptions.appToken,
|
|
771
|
+
normalizeText(accountStatus?.appToken, null),
|
|
772
|
+
),
|
|
739
773
|
statusLabel,
|
|
740
774
|
selectionHint,
|
|
741
775
|
quickstartScore: managedRuntimeReady ? 2 : 5,
|
|
@@ -879,7 +913,7 @@ export async function ensureClaworldPluginInstalled({
|
|
|
879
913
|
});
|
|
880
914
|
}
|
|
881
915
|
|
|
882
|
-
const args = ['plugins', 'install'];
|
|
916
|
+
const args = ['plugins', 'install', '--dangerously-force-unsafe-install'];
|
|
883
917
|
if (installMode === 'link') args.push('--link');
|
|
884
918
|
args.push(installSource);
|
|
885
919
|
await executeCommand({
|
|
@@ -1248,6 +1282,7 @@ export async function verifyClaworldInstall({
|
|
|
1248
1282
|
attempts = DEFAULT_VERIFICATION_ATTEMPTS,
|
|
1249
1283
|
delayMs = DEFAULT_VERIFICATION_DELAY_MS,
|
|
1250
1284
|
requireGatewayRunning = resolveRequireGatewayRunning(env),
|
|
1285
|
+
requireChannelToken = true,
|
|
1251
1286
|
} = {}) {
|
|
1252
1287
|
let lastResult = null;
|
|
1253
1288
|
|
|
@@ -1301,7 +1336,10 @@ export async function verifyClaworldInstall({
|
|
|
1301
1336
|
channelAccount
|
|
1302
1337
|
&& channelAccount.configured === true
|
|
1303
1338
|
&& channelAccount.enabled !== false
|
|
1304
|
-
&&
|
|
1339
|
+
&& (
|
|
1340
|
+
requireChannelToken !== true
|
|
1341
|
+
|| channelAccount.tokenStatus === 'available'
|
|
1342
|
+
)
|
|
1305
1343
|
);
|
|
1306
1344
|
const pluginReady = Boolean(plugin.installed);
|
|
1307
1345
|
const bindingReady = Boolean(bindingLine);
|
|
@@ -1318,6 +1356,7 @@ export async function verifyClaworldInstall({
|
|
|
1318
1356
|
channelReady,
|
|
1319
1357
|
bindingReady,
|
|
1320
1358
|
requireGatewayRunning,
|
|
1359
|
+
requireChannelToken,
|
|
1321
1360
|
};
|
|
1322
1361
|
|
|
1323
1362
|
if (lastResult.ok) {
|
|
@@ -1337,8 +1376,10 @@ export async function verifyClaworldInstall({
|
|
|
1337
1376
|
async function reconcileManagedClaworldRuntime({
|
|
1338
1377
|
openclawBin = DEFAULT_OPENCLAW_BIN,
|
|
1339
1378
|
configPath = DEFAULT_OPENCLAW_CONFIG_PATH,
|
|
1379
|
+
installerStatePath = null,
|
|
1340
1380
|
stateDir = DEFAULT_OPENCLAW_STATE_DIR,
|
|
1341
1381
|
currentConfigState = { existed: false, config: {} },
|
|
1382
|
+
currentInstallerState = {},
|
|
1342
1383
|
currentConfig = {},
|
|
1343
1384
|
host = null,
|
|
1344
1385
|
serverUrl = null,
|
|
@@ -1361,6 +1402,7 @@ async function reconcileManagedClaworldRuntime({
|
|
|
1361
1402
|
const existingInstall = inspectManagedClaworldInstall({
|
|
1362
1403
|
cfg: currentConfig,
|
|
1363
1404
|
accountId,
|
|
1405
|
+
installerState: currentInstallerState,
|
|
1364
1406
|
overrides: {
|
|
1365
1407
|
agentId,
|
|
1366
1408
|
workspace,
|
|
@@ -1376,35 +1418,19 @@ async function reconcileManagedClaworldRuntime({
|
|
|
1376
1418
|
normalizeText(existingInstall.accountStatus?.serverUrl, DEFAULT_CLAWORLD_SERVER_URL),
|
|
1377
1419
|
);
|
|
1378
1420
|
|
|
1379
|
-
const manifest = await fetchInstallManifest({
|
|
1380
|
-
serverUrl: effectiveServerUrl,
|
|
1381
|
-
apiKey,
|
|
1382
|
-
fetchImpl,
|
|
1383
|
-
});
|
|
1384
|
-
const manifestMinHostVersion = normalizeText(
|
|
1385
|
-
manifest?.installer?.minHostVersion,
|
|
1386
|
-
normalizeText(manifest?.plugin?.minHostVersion, CLAWORLD_OPENCLAW_MIN_HOST_VERSION),
|
|
1387
|
-
);
|
|
1388
|
-
if (host && compareVersionParts(host.version, manifestMinHostVersion) < 0) {
|
|
1389
|
-
throw createInstallerError(
|
|
1390
|
-
'openclaw_version_too_old',
|
|
1391
|
-
`OpenClaw ${host.version} is below the required minimum ${manifestMinHostVersion}.`,
|
|
1392
|
-
{ hostVersion: host.version, minHostVersion: manifestMinHostVersion },
|
|
1393
|
-
);
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1421
|
const installAccountId = normalizeText(
|
|
1397
1422
|
accountId,
|
|
1398
|
-
|
|
1423
|
+
DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
1399
1424
|
);
|
|
1400
1425
|
const installAgentId = normalizeText(
|
|
1401
1426
|
agentId,
|
|
1402
|
-
|
|
1427
|
+
installAccountId,
|
|
1403
1428
|
);
|
|
1404
1429
|
|
|
1405
1430
|
const preflight = inspectManagedClaworldInstall({
|
|
1406
1431
|
cfg: currentConfig,
|
|
1407
1432
|
accountId: installAccountId,
|
|
1433
|
+
installerState: currentInstallerState,
|
|
1408
1434
|
overrides: {
|
|
1409
1435
|
agentId: installAgentId,
|
|
1410
1436
|
workspace,
|
|
@@ -1421,50 +1447,15 @@ async function reconcileManagedClaworldRuntime({
|
|
|
1421
1447
|
normalizeText(preflight.managedOptions.displayName, null),
|
|
1422
1448
|
);
|
|
1423
1449
|
|
|
1424
|
-
let activation = null;
|
|
1425
|
-
let activationMode = 'new_activation';
|
|
1426
|
-
const existingAppToken = normalizeText(preflight.reusableAppToken, null);
|
|
1427
|
-
if (existingAppToken) {
|
|
1428
|
-
try {
|
|
1429
|
-
activation = await activateInstall({
|
|
1430
|
-
serverUrl: effectiveServerUrl,
|
|
1431
|
-
apiKey,
|
|
1432
|
-
appToken: existingAppToken,
|
|
1433
|
-
displayName: desiredDisplayName,
|
|
1434
|
-
fetchImpl,
|
|
1435
|
-
});
|
|
1436
|
-
activationMode = 'reused_existing_token';
|
|
1437
|
-
} catch (error) {
|
|
1438
|
-
const status = error?.context?.response?.status;
|
|
1439
|
-
if (status !== 401 && status !== 403) {
|
|
1440
|
-
throw error;
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
if (!activation) {
|
|
1445
|
-
activation = await activateInstall({
|
|
1446
|
-
serverUrl: effectiveServerUrl,
|
|
1447
|
-
apiKey,
|
|
1448
|
-
displayName: desiredDisplayName,
|
|
1449
|
-
fetchImpl,
|
|
1450
|
-
});
|
|
1451
|
-
activationMode = 'created_new_activation';
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
1450
|
const managedOptions = resolveClaworldManagedRuntimeOptions({
|
|
1455
1451
|
cfg: currentConfig,
|
|
1452
|
+
installerState: currentInstallerState,
|
|
1456
1453
|
accountId: installAccountId,
|
|
1457
|
-
input: {
|
|
1458
|
-
name: desiredDisplayName,
|
|
1459
|
-
appToken: activation.appToken,
|
|
1460
|
-
toolProfile,
|
|
1461
|
-
},
|
|
1462
1454
|
overrides: {
|
|
1463
1455
|
agentId: installAgentId,
|
|
1464
1456
|
workspace,
|
|
1465
1457
|
serverUrl: effectiveServerUrl,
|
|
1466
1458
|
apiKey,
|
|
1467
|
-
appToken: activation.appToken,
|
|
1468
1459
|
displayName: desiredDisplayName,
|
|
1469
1460
|
toolProfile,
|
|
1470
1461
|
approvalMode,
|
|
@@ -1517,29 +1508,40 @@ async function reconcileManagedClaworldRuntime({
|
|
|
1517
1508
|
env,
|
|
1518
1509
|
dryRun,
|
|
1519
1510
|
delayMs: Math.min(timeoutMs, DEFAULT_VERIFICATION_DELAY_MS),
|
|
1511
|
+
requireChannelToken: Boolean(managedOptions.appToken),
|
|
1520
1512
|
});
|
|
1521
1513
|
if (!verification.ok) {
|
|
1522
1514
|
throw createInstallerError(
|
|
1523
1515
|
'claworld_install_verification_failed',
|
|
1524
|
-
'Claworld install verification did not confirm the managed
|
|
1516
|
+
'Claworld install verification did not confirm the managed channel runtime shape.',
|
|
1525
1517
|
{ verification },
|
|
1526
1518
|
);
|
|
1527
1519
|
}
|
|
1528
1520
|
|
|
1521
|
+
let installerStateChanged = false;
|
|
1522
|
+
if (installerStatePath && installAccountId) {
|
|
1523
|
+
const nextInstallerState = JSON.parse(JSON.stringify(ensureObject(currentInstallerState)));
|
|
1524
|
+
setClaworldManagedRuntimeBackupState(nextInstallerState, installAccountId, null);
|
|
1525
|
+
installerStateChanged = JSON.stringify(currentInstallerState) !== JSON.stringify(nextInstallerState);
|
|
1526
|
+
if (installerStateChanged) {
|
|
1527
|
+
await writeInstallerState(installerStatePath, nextInstallerState, dryRun);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1529
1531
|
return {
|
|
1530
1532
|
backupPath,
|
|
1531
1533
|
existingInstall,
|
|
1532
1534
|
effectiveServerUrl,
|
|
1533
|
-
manifest,
|
|
1534
1535
|
preflight,
|
|
1535
|
-
|
|
1536
|
-
activation,
|
|
1536
|
+
activationStatus: managedOptions.appToken ? 'ready' : 'pending',
|
|
1537
1537
|
managedOptions,
|
|
1538
1538
|
transformed,
|
|
1539
1539
|
configChanged,
|
|
1540
1540
|
workspaceActions,
|
|
1541
1541
|
runtimeRefresh,
|
|
1542
1542
|
verification,
|
|
1543
|
+
installerStatePath,
|
|
1544
|
+
installerStateChanged,
|
|
1543
1545
|
};
|
|
1544
1546
|
}
|
|
1545
1547
|
|
|
@@ -1567,6 +1569,7 @@ export async function runClaworldInstallerInstall({
|
|
|
1567
1569
|
timeoutMs = DEFAULT_INSTALL_TIMEOUT_MS,
|
|
1568
1570
|
} = {}) {
|
|
1569
1571
|
const resolvedConfigPath = path.resolve(expandUserPath(configPath, os.homedir()));
|
|
1572
|
+
const installerStatePath = resolveClaworldInstallerStatePath(resolvedConfigPath);
|
|
1570
1573
|
const resolvedStateDir = stateDir ? path.resolve(expandUserPath(stateDir, os.homedir())) : null;
|
|
1571
1574
|
const commandEnv = buildOpenclawCommandEnv({
|
|
1572
1575
|
configPath: resolvedConfigPath,
|
|
@@ -1574,6 +1577,7 @@ export async function runClaworldInstallerInstall({
|
|
|
1574
1577
|
env,
|
|
1575
1578
|
});
|
|
1576
1579
|
const currentConfigState = await loadConfigFromDisk(resolvedConfigPath);
|
|
1580
|
+
const currentInstallerState = (await loadInstallerStateFromDisk(installerStatePath)).state;
|
|
1577
1581
|
const currentConfigBeforePluginInstall = currentConfigState.config;
|
|
1578
1582
|
const host = await detectOpenclawHost({
|
|
1579
1583
|
openclawBin,
|
|
@@ -1620,8 +1624,10 @@ export async function runClaworldInstallerInstall({
|
|
|
1620
1624
|
const lifecycle = await reconcileManagedClaworldRuntime({
|
|
1621
1625
|
openclawBin,
|
|
1622
1626
|
configPath: resolvedConfigPath,
|
|
1627
|
+
installerStatePath,
|
|
1623
1628
|
stateDir: resolvedStateDir,
|
|
1624
1629
|
currentConfigState,
|
|
1630
|
+
currentInstallerState,
|
|
1625
1631
|
currentConfig,
|
|
1626
1632
|
host,
|
|
1627
1633
|
serverUrl,
|
|
@@ -1646,6 +1652,7 @@ export async function runClaworldInstallerInstall({
|
|
|
1646
1652
|
ok: true,
|
|
1647
1653
|
command: CLAWORLD_INSTALLER_COMMAND,
|
|
1648
1654
|
configPath: resolvedConfigPath,
|
|
1655
|
+
installerStatePath,
|
|
1649
1656
|
stateDir: resolvedStateDir,
|
|
1650
1657
|
host,
|
|
1651
1658
|
plugin,
|
|
@@ -1674,6 +1681,7 @@ export async function runClaworldInstallerUpdate({
|
|
|
1674
1681
|
timeoutMs = DEFAULT_INSTALL_TIMEOUT_MS,
|
|
1675
1682
|
} = {}) {
|
|
1676
1683
|
const resolvedConfigPath = path.resolve(expandUserPath(configPath, os.homedir()));
|
|
1684
|
+
const installerStatePath = resolveClaworldInstallerStatePath(resolvedConfigPath);
|
|
1677
1685
|
const resolvedStateDir = stateDir ? path.resolve(expandUserPath(stateDir, os.homedir())) : null;
|
|
1678
1686
|
const commandEnv = buildOpenclawCommandEnv({
|
|
1679
1687
|
configPath: resolvedConfigPath,
|
|
@@ -1681,6 +1689,7 @@ export async function runClaworldInstallerUpdate({
|
|
|
1681
1689
|
env,
|
|
1682
1690
|
});
|
|
1683
1691
|
const currentConfigState = await loadConfigFromDisk(resolvedConfigPath);
|
|
1692
|
+
const currentInstallerState = (await loadInstallerStateFromDisk(installerStatePath)).state;
|
|
1684
1693
|
const host = await detectOpenclawHost({
|
|
1685
1694
|
openclawBin,
|
|
1686
1695
|
commandRunner,
|
|
@@ -1712,8 +1721,10 @@ export async function runClaworldInstallerUpdate({
|
|
|
1712
1721
|
const lifecycle = await reconcileManagedClaworldRuntime({
|
|
1713
1722
|
openclawBin,
|
|
1714
1723
|
configPath: resolvedConfigPath,
|
|
1724
|
+
installerStatePath,
|
|
1715
1725
|
stateDir: resolvedStateDir,
|
|
1716
1726
|
currentConfigState: refreshedConfigState,
|
|
1727
|
+
currentInstallerState,
|
|
1717
1728
|
currentConfig,
|
|
1718
1729
|
host,
|
|
1719
1730
|
serverUrl,
|
|
@@ -1737,6 +1748,7 @@ export async function runClaworldInstallerUpdate({
|
|
|
1737
1748
|
ok: true,
|
|
1738
1749
|
command: CLAWORLD_UPDATE_COMMAND,
|
|
1739
1750
|
configPath: resolvedConfigPath,
|
|
1751
|
+
installerStatePath,
|
|
1740
1752
|
stateDir: resolvedStateDir,
|
|
1741
1753
|
host,
|
|
1742
1754
|
plugin,
|
|
@@ -1744,6 +1756,170 @@ export async function runClaworldInstallerUpdate({
|
|
|
1744
1756
|
};
|
|
1745
1757
|
}
|
|
1746
1758
|
|
|
1759
|
+
export async function runClaworldInstallerUninstall({
|
|
1760
|
+
openclawBin = DEFAULT_OPENCLAW_BIN,
|
|
1761
|
+
configPath = DEFAULT_OPENCLAW_CONFIG_PATH,
|
|
1762
|
+
stateDir = DEFAULT_OPENCLAW_STATE_DIR,
|
|
1763
|
+
accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
1764
|
+
agentId = DEFAULT_CLAWORLD_AGENT_ID,
|
|
1765
|
+
commandRunner = defaultCommandRunner,
|
|
1766
|
+
cwd = process.cwd(),
|
|
1767
|
+
env = process.env,
|
|
1768
|
+
dryRun = false,
|
|
1769
|
+
} = {}) {
|
|
1770
|
+
const resolvedConfigPath = path.resolve(expandUserPath(configPath, os.homedir()));
|
|
1771
|
+
const installerStatePath = resolveClaworldInstallerStatePath(resolvedConfigPath);
|
|
1772
|
+
const resolvedStateDir = stateDir ? path.resolve(expandUserPath(stateDir, os.homedir())) : null;
|
|
1773
|
+
const commandEnv = buildOpenclawCommandEnv({
|
|
1774
|
+
configPath: resolvedConfigPath,
|
|
1775
|
+
stateDir: resolvedStateDir,
|
|
1776
|
+
env,
|
|
1777
|
+
});
|
|
1778
|
+
const currentConfigState = await loadConfigFromDisk(resolvedConfigPath);
|
|
1779
|
+
const currentInstallerState = (await loadInstallerStateFromDisk(installerStatePath)).state;
|
|
1780
|
+
const currentConfig = currentConfigState.config;
|
|
1781
|
+
const host = await detectOpenclawHost({
|
|
1782
|
+
openclawBin,
|
|
1783
|
+
commandRunner,
|
|
1784
|
+
cwd,
|
|
1785
|
+
env: commandEnv,
|
|
1786
|
+
dryRun,
|
|
1787
|
+
});
|
|
1788
|
+
if (compareVersionParts(host.version, CLAWORLD_OPENCLAW_MIN_HOST_VERSION) < 0) {
|
|
1789
|
+
throw createInstallerError(
|
|
1790
|
+
'openclaw_version_too_old',
|
|
1791
|
+
`OpenClaw ${host.version} is below the required minimum ${CLAWORLD_OPENCLAW_MIN_HOST_VERSION}.`,
|
|
1792
|
+
{ hostVersion: host.version, minHostVersion: CLAWORLD_OPENCLAW_MIN_HOST_VERSION },
|
|
1793
|
+
);
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
const pluginBefore = await inspectClaworldPluginInstall({
|
|
1797
|
+
openclawBin,
|
|
1798
|
+
configPath: resolvedConfigPath,
|
|
1799
|
+
stateDir: resolvedStateDir,
|
|
1800
|
+
commandRunner,
|
|
1801
|
+
cwd,
|
|
1802
|
+
env,
|
|
1803
|
+
dryRun,
|
|
1804
|
+
});
|
|
1805
|
+
const transformed = stripClaworldManagedRuntimeConfig(currentConfig, {
|
|
1806
|
+
accountId,
|
|
1807
|
+
agentId,
|
|
1808
|
+
preserveBackup: true,
|
|
1809
|
+
});
|
|
1810
|
+
const configChanged = JSON.stringify(currentConfig) !== JSON.stringify(transformed.config);
|
|
1811
|
+
const backupPath = configChanged
|
|
1812
|
+
? await backupConfigIfPresent(resolvedConfigPath, currentConfigState.existed, dryRun)
|
|
1813
|
+
: null;
|
|
1814
|
+
if (configChanged) {
|
|
1815
|
+
await writeConfig(resolvedConfigPath, transformed.config, dryRun);
|
|
1816
|
+
}
|
|
1817
|
+
const nextInstallerState = JSON.parse(JSON.stringify(ensureObject(currentInstallerState)));
|
|
1818
|
+
setClaworldManagedRuntimeBackupState(nextInstallerState, accountId, transformed.backup);
|
|
1819
|
+
const installerStateChanged = JSON.stringify(currentInstallerState) !== JSON.stringify(nextInstallerState);
|
|
1820
|
+
if (installerStateChanged) {
|
|
1821
|
+
await writeInstallerState(installerStatePath, nextInstallerState, dryRun);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
await validateOpenclawConfig({
|
|
1825
|
+
openclawBin,
|
|
1826
|
+
configPath: resolvedConfigPath,
|
|
1827
|
+
stateDir: resolvedStateDir,
|
|
1828
|
+
commandRunner,
|
|
1829
|
+
cwd,
|
|
1830
|
+
env,
|
|
1831
|
+
dryRun,
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
const uninstallBootstrap = pluginBefore.installed
|
|
1835
|
+
? await preparePluginBootstrapConfig({
|
|
1836
|
+
configPath: resolvedConfigPath,
|
|
1837
|
+
config: currentConfig,
|
|
1838
|
+
dryRun,
|
|
1839
|
+
})
|
|
1840
|
+
: null;
|
|
1841
|
+
|
|
1842
|
+
let pluginAction = 'plugin_already_absent';
|
|
1843
|
+
try {
|
|
1844
|
+
if (pluginBefore.installed) {
|
|
1845
|
+
await executeCommand({
|
|
1846
|
+
commandRunner,
|
|
1847
|
+
bin: openclawBin,
|
|
1848
|
+
args: ['plugins', 'uninstall', 'claworld', '--force'],
|
|
1849
|
+
cwd,
|
|
1850
|
+
env: buildOpenclawCommandEnv({
|
|
1851
|
+
configPath: uninstallBootstrap?.configPath || resolvedConfigPath,
|
|
1852
|
+
stateDir: resolvedStateDir,
|
|
1853
|
+
env,
|
|
1854
|
+
}),
|
|
1855
|
+
dryRun,
|
|
1856
|
+
capture: false,
|
|
1857
|
+
});
|
|
1858
|
+
pluginAction = 'uninstalled_plugin';
|
|
1859
|
+
}
|
|
1860
|
+
} finally {
|
|
1861
|
+
if (uninstallBootstrap) {
|
|
1862
|
+
await uninstallBootstrap.cleanup();
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
const runtimeRefresh = await refreshOpenclawRuntime({
|
|
1867
|
+
openclawBin,
|
|
1868
|
+
configPath: resolvedConfigPath,
|
|
1869
|
+
stateDir: resolvedStateDir,
|
|
1870
|
+
commandRunner,
|
|
1871
|
+
cwd,
|
|
1872
|
+
env,
|
|
1873
|
+
dryRun,
|
|
1874
|
+
});
|
|
1875
|
+
const pluginAfter = await inspectClaworldPluginInstall({
|
|
1876
|
+
openclawBin,
|
|
1877
|
+
configPath: resolvedConfigPath,
|
|
1878
|
+
stateDir: resolvedStateDir,
|
|
1879
|
+
commandRunner,
|
|
1880
|
+
cwd,
|
|
1881
|
+
env,
|
|
1882
|
+
dryRun,
|
|
1883
|
+
});
|
|
1884
|
+
if (pluginBefore.installed && pluginAfter.installed) {
|
|
1885
|
+
throw createInstallerError(
|
|
1886
|
+
'claworld_plugin_uninstall_failed',
|
|
1887
|
+
'OpenClaw still reports the claworld plugin as installed after uninstall.',
|
|
1888
|
+
{ before: pluginBefore, after: pluginAfter },
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
const gatewayStatus = await readGatewayStatus({
|
|
1893
|
+
openclawBin,
|
|
1894
|
+
configPath: resolvedConfigPath,
|
|
1895
|
+
stateDir: resolvedStateDir,
|
|
1896
|
+
commandRunner,
|
|
1897
|
+
cwd,
|
|
1898
|
+
env,
|
|
1899
|
+
dryRun,
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
return {
|
|
1903
|
+
ok: true,
|
|
1904
|
+
command: CLAWORLD_UNINSTALL_COMMAND,
|
|
1905
|
+
configPath: resolvedConfigPath,
|
|
1906
|
+
installerStatePath,
|
|
1907
|
+
stateDir: resolvedStateDir,
|
|
1908
|
+
host,
|
|
1909
|
+
backupPath,
|
|
1910
|
+
transformed,
|
|
1911
|
+
configChanged,
|
|
1912
|
+
installerStateChanged,
|
|
1913
|
+
plugin: {
|
|
1914
|
+
action: pluginAction,
|
|
1915
|
+
before: pluginBefore,
|
|
1916
|
+
after: pluginAfter,
|
|
1917
|
+
},
|
|
1918
|
+
runtimeRefresh,
|
|
1919
|
+
gatewayStatus,
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1747
1923
|
export {
|
|
1748
1924
|
compareVersionParts,
|
|
1749
1925
|
defaultCommandRunner,
|
|
@@ -249,8 +249,8 @@ export async function runClaworldDoctor({
|
|
|
249
249
|
workspace: resolvedWorkspace,
|
|
250
250
|
serverUrl: effectiveServerUrl,
|
|
251
251
|
appToken: configuredAccount.appToken,
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
registrationDisplayName: configuredAccount.registration?.displayName || null,
|
|
253
|
+
defaultTargetAgentId: configuredAccount.relay?.defaultTargetAgentId || null,
|
|
254
254
|
})
|
|
255
255
|
: {
|
|
256
256
|
ok: true,
|
|
@@ -522,11 +522,13 @@ export async function runClaworldDoctor({
|
|
|
522
522
|
id: 'app-token',
|
|
523
523
|
category: 'Credentials and runtime',
|
|
524
524
|
label: 'Managed appToken',
|
|
525
|
-
status: configuredAccount.tokenStatus === 'available' ? 'pass' : '
|
|
525
|
+
status: configuredAccount.tokenStatus === 'available' ? 'pass' : 'warn',
|
|
526
526
|
summary: configuredAccount.tokenStatus === 'available'
|
|
527
527
|
? 'Managed account has an available appToken.'
|
|
528
|
-
: `Managed account token status is ${configuredAccount.tokenStatus}.`,
|
|
529
|
-
action:
|
|
528
|
+
: `Managed account token status is ${configuredAccount.tokenStatus}; activation is still pending.`,
|
|
529
|
+
action: configuredAccount.tokenStatus === 'available'
|
|
530
|
+
? null
|
|
531
|
+
: 'Open a live OpenClaw session, run `claworld_pair_agent`, and then complete `claworld_update_public_identity` when prompted.',
|
|
530
532
|
details: { tokenSource: configuredAccount.tokenSource, tokenStatus: configuredAccount.tokenStatus },
|
|
531
533
|
}));
|
|
532
534
|
|
|
@@ -549,9 +551,9 @@ export async function runClaworldDoctor({
|
|
|
549
551
|
id: 'backend-reachable',
|
|
550
552
|
category: 'Credentials and runtime',
|
|
551
553
|
label: 'Claworld backend reachability',
|
|
552
|
-
status: '
|
|
553
|
-
summary: `Unable to
|
|
554
|
-
action: '
|
|
554
|
+
status: 'warn',
|
|
555
|
+
summary: `Unable to reach the Claworld backend at ${effectiveServerUrl}.`,
|
|
556
|
+
action: 'Restore backend reachability before pairing or live Claworld use, then rerun doctor.',
|
|
555
557
|
details: { serverUrl: effectiveServerUrl, message: error?.message || String(error) },
|
|
556
558
|
}));
|
|
557
559
|
}
|
|
@@ -580,7 +582,7 @@ export async function runClaworldDoctor({
|
|
|
580
582
|
label: 'Stable relay binding',
|
|
581
583
|
status: 'fail',
|
|
582
584
|
summary: 'Managed appToken could not be resolved into a healthy backend binding.',
|
|
583
|
-
action: `
|
|
585
|
+
action: 'Run `claworld_pair_agent` in a live OpenClaw session to refresh the managed relay binding.',
|
|
584
586
|
details: { message: error?.message || String(error) },
|
|
585
587
|
}));
|
|
586
588
|
}
|
|
@@ -589,17 +591,17 @@ export async function runClaworldDoctor({
|
|
|
589
591
|
id: 'stable-agent-binding',
|
|
590
592
|
category: 'Credentials and runtime',
|
|
591
593
|
label: 'Stable relay binding',
|
|
592
|
-
status: '
|
|
593
|
-
summary: 'Doctor could not verify the managed relay binding because
|
|
594
|
-
action:
|
|
594
|
+
status: 'warn',
|
|
595
|
+
summary: 'Doctor could not verify the managed relay binding because activation is still pending.',
|
|
596
|
+
action: 'Open a live OpenClaw session, run `claworld_pair_agent`, and then complete `claworld_update_public_identity` when prompted.',
|
|
595
597
|
}));
|
|
596
598
|
} else {
|
|
597
599
|
checks.push(createCheck({
|
|
598
600
|
id: 'stable-agent-binding',
|
|
599
601
|
category: 'Credentials and runtime',
|
|
600
602
|
label: 'Stable relay binding',
|
|
601
|
-
status: '
|
|
602
|
-
summary: 'Doctor could not verify the managed relay binding because the backend
|
|
603
|
+
status: 'warn',
|
|
604
|
+
summary: 'Doctor could not verify the managed relay binding because the backend was not reachable.',
|
|
603
605
|
action: 'Restore backend reachability, then rerun doctor to verify the managed relay binding.',
|
|
604
606
|
}));
|
|
605
607
|
}
|
|
@@ -655,15 +657,27 @@ export async function runClaworldDoctor({
|
|
|
655
657
|
&& channelAccount.enabled !== false
|
|
656
658
|
&& channelAccount.tokenStatus === 'available'
|
|
657
659
|
);
|
|
660
|
+
const channelPendingActivation = Boolean(
|
|
661
|
+
channelAccount
|
|
662
|
+
&& channelAccount.configured === true
|
|
663
|
+
&& channelAccount.enabled !== false
|
|
664
|
+
&& channelAccount.tokenStatus !== 'available'
|
|
665
|
+
);
|
|
658
666
|
checks.push(createCheck({
|
|
659
667
|
id: 'channel-runtime',
|
|
660
668
|
category: 'Credentials and runtime',
|
|
661
669
|
label: 'Claworld channel runtime account',
|
|
662
|
-
status: channelHealthy ? 'pass' : 'fail',
|
|
670
|
+
status: channelHealthy ? 'pass' : channelPendingActivation ? 'warn' : 'fail',
|
|
663
671
|
summary: channelHealthy
|
|
664
672
|
? `channels status reports managed account \`${configuredAccount.accountId}\` as configured with an available token.`
|
|
665
|
-
:
|
|
666
|
-
|
|
673
|
+
: channelPendingActivation
|
|
674
|
+
? `channels status reports managed account \`${configuredAccount.accountId}\` as configured, but activation is still pending (${channelAccount.tokenStatus}).`
|
|
675
|
+
: `channels status does not report a healthy managed account for \`${configuredAccount.accountId}\`.`,
|
|
676
|
+
action: channelHealthy
|
|
677
|
+
? null
|
|
678
|
+
: channelPendingActivation
|
|
679
|
+
? 'Open a live OpenClaw session, run `claworld_pair_agent`, and then complete `claworld_update_public_identity` when prompted.'
|
|
680
|
+
: `Rerun \`${CLAWORLD_INSTALLER_COMMAND}\` or restart the gateway to recover the managed account runtime.`,
|
|
667
681
|
details: { channelAccount },
|
|
668
682
|
}));
|
|
669
683
|
} catch (error) {
|
|
@@ -17,7 +17,6 @@ export function normalizeRuntimeRegistration(candidate = {}) {
|
|
|
17
17
|
|
|
18
18
|
return {
|
|
19
19
|
enabled: true,
|
|
20
|
-
agentCode: normalizeText(registration.agentCode, normalizeText(legacyLocalAgent.agentCode, null)),
|
|
21
20
|
displayName: normalizeText(registration.displayName, normalizeText(legacyLocalAgent.displayName, null)),
|
|
22
21
|
};
|
|
23
22
|
}
|
|
@@ -49,7 +48,7 @@ export function applyRuntimeIdentity(runtimeConfig = {}, { agentId = null, appTo
|
|
|
49
48
|
agentId: normalizeText(agentId, normalizeText(relay.agentId, null)),
|
|
50
49
|
appToken: resolvedAppToken,
|
|
51
50
|
credentialToken: resolvedAppToken,
|
|
52
|
-
|
|
51
|
+
defaultTargetAgentId: normalizeText(relay.defaultTargetAgentId, null),
|
|
53
52
|
},
|
|
54
53
|
};
|
|
55
54
|
}
|