@xfxstudio/claworld 2026.6.3-testing.2 → 2026.6.3-testing.3

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.
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "name": "Claworld Persona Relay",
19
19
  "description": "Claworld relay world channel plugin for OpenClaw.",
20
- "version": "2026.6.3-testing.2",
20
+ "version": "2026.6.3-testing.3",
21
21
  "configSchema": {
22
22
  "type": "object",
23
23
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.6.3-testing.2",
3
+ "version": "2026.6.3-testing.3",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -267,6 +267,10 @@ function normalizeClaworldText(value, fallback = null) {
267
267
  return normalized || fallback;
268
268
  }
269
269
 
270
+ function isClaworldPlainObject(value) {
271
+ return value && typeof value === 'object' && !Array.isArray(value);
272
+ }
273
+
270
274
  function resolveNormalizedText(value, fallback = null) {
271
275
  return normalizeClaworldText(value, fallback);
272
276
  }
@@ -3309,48 +3313,196 @@ export function createClaworldChannelPlugin({
3309
3313
  return promise;
3310
3314
  }
3311
3315
 
3312
- async function persistRuntimeAppToken({ runtime, accountId, appToken, relayAgentId = null }) {
3316
+ function buildRuntimeAppTokenAccountConfig(existingAccount = {}, {
3317
+ accountId,
3318
+ appToken,
3319
+ relayAgentId = null,
3320
+ runtimeConfig = null,
3321
+ } = {}) {
3322
+ const account = isClaworldPlainObject(existingAccount) ? existingAccount : {};
3323
+ const sourceRuntimeConfig = isClaworldPlainObject(runtimeConfig) ? runtimeConfig : {};
3324
+ const existingRelay = isClaworldPlainObject(account.relay) ? account.relay : {};
3325
+ const runtimeRelay = isClaworldPlainObject(sourceRuntimeConfig.relay) ? sourceRuntimeConfig.relay : {};
3326
+ const normalizedAccountId = normalizeClaworldText(accountId, normalizeClaworldText(sourceRuntimeConfig.accountId, null));
3327
+ const normalizedRelayAgentId = normalizeClaworldText(
3328
+ relayAgentId,
3329
+ normalizeClaworldText(existingRelay.agentId, normalizeClaworldText(runtimeRelay.agentId, null)),
3330
+ );
3331
+ const normalizedAppToken = normalizeClaworldText(appToken, resolveRuntimeAppToken(sourceRuntimeConfig));
3332
+ const serverUrl = normalizeClaworldText(account.serverUrl, normalizeClaworldText(sourceRuntimeConfig.serverUrl, null));
3333
+ const apiKey = normalizeClaworldText(account.apiKey, normalizeClaworldText(sourceRuntimeConfig.apiKey, null));
3334
+ const name = normalizeClaworldText(account.name, normalizeClaworldText(sourceRuntimeConfig.name, null));
3335
+ const toolProfile = normalizeClaworldText(account.toolProfile, normalizeClaworldText(sourceRuntimeConfig.toolProfile, null));
3336
+ const heartbeatSeconds = Number.isInteger(account.heartbeatSeconds)
3337
+ ? account.heartbeatSeconds
3338
+ : (Number.isInteger(sourceRuntimeConfig.heartbeatSeconds) ? sourceRuntimeConfig.heartbeatSeconds : null);
3339
+ const reconnect = typeof account.reconnect === 'boolean'
3340
+ ? account.reconnect
3341
+ : (typeof sourceRuntimeConfig.reconnect === 'boolean' ? sourceRuntimeConfig.reconnect : null);
3342
+ const routing = isClaworldPlainObject(account.routing)
3343
+ ? account.routing
3344
+ : (isClaworldPlainObject(sourceRuntimeConfig.routing) ? sourceRuntimeConfig.routing : null);
3345
+ const testing = isClaworldPlainObject(account.testing)
3346
+ ? account.testing
3347
+ : (isClaworldPlainObject(sourceRuntimeConfig.testing) ? sourceRuntimeConfig.testing : null);
3348
+
3349
+ const nextAccount = {
3350
+ ...account,
3351
+ enabled: typeof account.enabled === 'boolean'
3352
+ ? account.enabled
3353
+ : (typeof sourceRuntimeConfig.enabled === 'boolean' ? sourceRuntimeConfig.enabled : true),
3354
+ ...(serverUrl ? { serverUrl } : {}),
3355
+ ...(apiKey ? { apiKey } : {}),
3356
+ ...(normalizedAccountId ? { accountId: normalizedAccountId } : {}),
3357
+ ...(normalizedAppToken ? { appToken: normalizedAppToken } : {}),
3358
+ ...(name ? { name } : {}),
3359
+ ...(toolProfile ? { toolProfile } : {}),
3360
+ ...(heartbeatSeconds ? { heartbeatSeconds } : {}),
3361
+ ...(typeof reconnect === 'boolean' ? { reconnect } : {}),
3362
+ ...(routing ? { routing } : {}),
3363
+ ...(testing ? { testing } : {}),
3364
+ };
3365
+
3366
+ const relayAppToken = normalizeClaworldText(existingRelay.appToken, normalizeClaworldText(runtimeRelay.appToken, null));
3367
+ const relayCredentialToken = normalizeClaworldText(
3368
+ existingRelay.credentialToken,
3369
+ normalizeClaworldText(runtimeRelay.credentialToken, null),
3370
+ );
3371
+ const relayDefaultTargetAgentId = normalizeClaworldText(
3372
+ existingRelay.defaultTargetAgentId,
3373
+ normalizeClaworldText(runtimeRelay.defaultTargetAgentId, null),
3374
+ );
3375
+ const relay = {
3376
+ ...(relayAppToken ? { appToken: relayAppToken } : {}),
3377
+ ...(relayCredentialToken ? { credentialToken: relayCredentialToken } : {}),
3378
+ ...(relayDefaultTargetAgentId ? { defaultTargetAgentId: relayDefaultTargetAgentId } : {}),
3379
+ ...(normalizedRelayAgentId ? { agentId: normalizedRelayAgentId } : {}),
3380
+ };
3381
+ if (Object.keys(relay).length > 0) nextAccount.relay = relay;
3382
+ else delete nextAccount.relay;
3383
+ delete nextAccount.registration;
3384
+
3385
+ const missingRequired = [];
3386
+ if (typeof nextAccount.enabled !== 'boolean') missingRequired.push('enabled');
3387
+ if (!normalizeClaworldText(nextAccount.serverUrl, null)) missingRequired.push('serverUrl');
3388
+ if (!normalizeClaworldText(nextAccount.apiKey, null)) missingRequired.push('apiKey');
3389
+ if (!normalizeClaworldText(nextAccount.accountId, null)) missingRequired.push('accountId');
3390
+
3391
+ return { account: nextAccount, missingRequired };
3392
+ }
3393
+
3394
+ function applyRuntimeAppTokenConfigMutation(configDraft, {
3395
+ accountId,
3396
+ appToken,
3397
+ relayAgentId = null,
3398
+ runtimeConfig = null,
3399
+ } = {}) {
3400
+ if (!isClaworldPlainObject(configDraft)) {
3401
+ return { skipped: true, reason: 'invalid_config_draft' };
3402
+ }
3403
+ const normalizedAccountId = normalizeClaworldText(accountId, normalizeClaworldText(runtimeConfig?.accountId, null));
3404
+ if (!normalizedAccountId || !normalizeClaworldText(appToken, resolveRuntimeAppToken(runtimeConfig))) {
3405
+ return { skipped: true, reason: 'missing_account_or_token' };
3406
+ }
3407
+
3408
+ configDraft.channels = isClaworldPlainObject(configDraft.channels) ? configDraft.channels : {};
3409
+ const claworldRoot = isClaworldPlainObject(configDraft.channels.claworld)
3410
+ ? configDraft.channels.claworld
3411
+ : {};
3412
+ const accounts = isClaworldPlainObject(claworldRoot.accounts) ? claworldRoot.accounts : {};
3413
+ const currentAccount = isClaworldPlainObject(accounts[normalizedAccountId])
3414
+ ? accounts[normalizedAccountId]
3415
+ : {};
3416
+ const built = buildRuntimeAppTokenAccountConfig(currentAccount, {
3417
+ accountId: normalizedAccountId,
3418
+ appToken,
3419
+ relayAgentId,
3420
+ runtimeConfig,
3421
+ });
3422
+ if (built.missingRequired.length > 0) {
3423
+ return {
3424
+ skipped: true,
3425
+ reason: 'missing_required_config_fields',
3426
+ accountId: normalizedAccountId,
3427
+ missingRequired: built.missingRequired,
3428
+ };
3429
+ }
3430
+
3431
+ if (JSON.stringify(currentAccount) === JSON.stringify(built.account)) {
3432
+ return {
3433
+ skipped: true,
3434
+ reason: 'already_persisted',
3435
+ accountId: normalizedAccountId,
3436
+ };
3437
+ }
3438
+
3439
+ accounts[normalizedAccountId] = built.account;
3440
+ claworldRoot.accounts = accounts;
3441
+ if (!normalizeClaworldText(claworldRoot.defaultAccount, null)) {
3442
+ claworldRoot.defaultAccount = normalizedAccountId;
3443
+ }
3444
+ configDraft.channels.claworld = claworldRoot;
3445
+ return {
3446
+ skipped: false,
3447
+ ok: true,
3448
+ accountId: normalizedAccountId,
3449
+ };
3450
+ }
3451
+
3452
+ async function persistRuntimeAppToken({ runtime, accountId, appToken, relayAgentId = null, runtimeConfig = null }) {
3313
3453
  if (!accountId || !appToken) {
3314
3454
  return { skipped: true, reason: 'missing_account_or_token' };
3315
3455
  }
3316
3456
 
3317
3457
  let configPersistResult = { skipped: true, reason: 'missing_runtime_config_io' };
3318
- if (runtime?.config?.loadConfig && runtime?.config?.writeConfigFile) {
3319
- const currentCfg = await runtime.config.loadConfig();
3320
- const nextCfg = JSON.parse(JSON.stringify(currentCfg || {}));
3321
- nextCfg.channels = nextCfg.channels && typeof nextCfg.channels === 'object' && !Array.isArray(nextCfg.channels)
3322
- ? nextCfg.channels
3323
- : {};
3324
- const claworldRoot = nextCfg.channels.claworld && typeof nextCfg.channels.claworld === 'object' && !Array.isArray(nextCfg.channels.claworld)
3325
- ? nextCfg.channels.claworld
3326
- : {};
3327
- const accounts = claworldRoot.accounts && typeof claworldRoot.accounts === 'object' && !Array.isArray(claworldRoot.accounts)
3328
- ? claworldRoot.accounts
3329
- : {};
3330
- const account = accounts[accountId] && typeof accounts[accountId] === 'object' && !Array.isArray(accounts[accountId])
3331
- ? accounts[accountId]
3332
- : {};
3333
-
3334
- const normalizedRelayAgentId = normalizeClaworldText(relayAgentId, normalizeClaworldText(account?.relay?.agentId, null));
3335
- const currentAppToken = normalizeClaworldText(account.appToken, null);
3336
- const currentRelayAgentId = normalizeClaworldText(account?.relay?.agentId, null);
3337
- if (currentAppToken === appToken && currentRelayAgentId === normalizedRelayAgentId) {
3338
- configPersistResult = { skipped: true, reason: 'already_persisted' };
3339
- } else {
3340
- accounts[accountId] = {
3341
- ...account,
3342
- appToken,
3343
- relay: {
3344
- ...(account?.relay && typeof account.relay === 'object' && !Array.isArray(account.relay) ? account.relay : {}),
3345
- ...(normalizedRelayAgentId ? { agentId: normalizedRelayAgentId } : {}),
3458
+ try {
3459
+ if (typeof runtime?.config?.mutateConfigFile === 'function') {
3460
+ let mutationOutcome = null;
3461
+ const mutationParams = {
3462
+ base: 'source',
3463
+ afterWrite: { mode: 'auto' },
3464
+ mutate: (draft) => {
3465
+ mutationOutcome = applyRuntimeAppTokenConfigMutation(draft, {
3466
+ accountId,
3467
+ appToken,
3468
+ relayAgentId,
3469
+ runtimeConfig,
3470
+ });
3471
+ return mutationOutcome;
3346
3472
  },
3347
3473
  };
3348
- delete accounts[accountId].registration;
3349
- claworldRoot.accounts = accounts;
3350
- nextCfg.channels.claworld = claworldRoot;
3351
- await runtime.config.writeConfigFile(nextCfg);
3352
- configPersistResult = { skipped: false, ok: true };
3474
+ const mutationResult = await runtime.config.mutateConfigFile(mutationParams);
3475
+ configPersistResult = {
3476
+ ...(mutationResult?.result || mutationOutcome || { skipped: false, ok: true }),
3477
+ method: 'mutateConfigFile',
3478
+ };
3479
+ } else if (runtime?.config?.loadConfig && runtime?.config?.writeConfigFile) {
3480
+ const currentCfg = await runtime.config.loadConfig();
3481
+ const nextCfg = JSON.parse(JSON.stringify(currentCfg || {}));
3482
+ const mutationOutcome = applyRuntimeAppTokenConfigMutation(nextCfg, {
3483
+ accountId,
3484
+ appToken,
3485
+ relayAgentId,
3486
+ runtimeConfig,
3487
+ });
3488
+ configPersistResult = {
3489
+ ...mutationOutcome,
3490
+ method: 'loadConfig_writeConfigFile',
3491
+ };
3492
+ if (!mutationOutcome.skipped) {
3493
+ await runtime.config.writeConfigFile(nextCfg);
3494
+ }
3353
3495
  }
3496
+ } catch (error) {
3497
+ configPersistResult = {
3498
+ skipped: true,
3499
+ reason: 'config_persist_failed',
3500
+ error: error?.message || String(error),
3501
+ };
3502
+ logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist runtime appToken to OpenClaw config`, {
3503
+ accountId,
3504
+ error: configPersistResult.error,
3505
+ });
3354
3506
  }
3355
3507
 
3356
3508
  let backupPersistResult = { skipped: true, reason: 'missing_runtime_config_loader' };
@@ -3358,6 +3510,9 @@ export function createClaworldChannelPlugin({
3358
3510
  backupPersistResult = await persistClaworldRuntimeBackup({
3359
3511
  runtime,
3360
3512
  accountId,
3513
+ runtimeConfig,
3514
+ appToken,
3515
+ relayAgentId,
3361
3516
  });
3362
3517
  } catch (error) {
3363
3518
  backupPersistResult = {
@@ -3399,8 +3554,10 @@ export function createClaworldChannelPlugin({
3399
3554
  };
3400
3555
  }
3401
3556
 
3557
+ const backupAgentId = normalizeClaworldText(backup?.agentId, null);
3402
3558
  const restoredRuntimeConfig = applyRuntimeIdentity(runtimeConfig, {
3403
3559
  appToken: backupToken,
3560
+ agentId: backupAgentId,
3404
3561
  });
3405
3562
 
3406
3563
  try {
@@ -3408,6 +3565,8 @@ export function createClaworldChannelPlugin({
3408
3565
  runtime,
3409
3566
  accountId,
3410
3567
  appToken: backupToken,
3568
+ relayAgentId: backupAgentId,
3569
+ runtimeConfig: restoredRuntimeConfig,
3411
3570
  });
3412
3571
  } catch (error) {
3413
3572
  logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist restored runtime appToken`, {
@@ -3621,6 +3780,7 @@ export function createClaworldChannelPlugin({
3621
3780
  accountId: runtimeConfig.accountId,
3622
3781
  appToken: resolveRuntimeAppToken(runtimeConfig),
3623
3782
  relayAgentId: runtimeConfig.relay?.agentId || null,
3783
+ runtimeConfig,
3624
3784
  });
3625
3785
  if (!persisted.skipped || persisted.backup?.skipped === false) {
3626
3786
  logger.info?.(`[claworld:${runtimeAccountId}] persisted runtime binding state`, {
@@ -3874,6 +4034,7 @@ async function updateRuntimePublicIdentity(context = {}) {
3874
4034
  accountId: resolvedContext.accountId || boundRuntimeConfig.accountId || null,
3875
4035
  appToken: nextAppToken,
3876
4036
  relayAgentId: nextAgentId,
4037
+ runtimeConfig: boundRuntimeConfig,
3877
4038
  });
3878
4039
  } catch (error) {
3879
4040
  logger.warn?.('[claworld:profile] failed to persist activated runtime binding', {
@@ -2061,14 +2061,16 @@ function buildRegisteredTools(api, plugin) {
2061
2061
  }));
2062
2062
  }
2063
2063
 
2064
- const cfg = await loadCurrentConfig(api);
2065
- const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
2066
- const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
2064
+ const context = await resolveToolContext(api, plugin, params);
2065
+ const cfg = context.cfg || await loadCurrentConfig(api);
2066
+ const accountId = context.accountId;
2067
+ const runtimeConfig = context.runtimeConfig || plugin.config.resolveRuntimeConfig(cfg, accountId);
2067
2068
  const identityPayload = await plugin.runtime.productShell.profile.getPublicIdentity({
2069
+ ...context,
2068
2070
  cfg,
2069
2071
  accountId,
2070
2072
  runtimeConfig,
2071
- agentId: runtimeConfig.relay?.agentId || null,
2073
+ agentId: context.agentId || runtimeConfig.relay?.agentId || null,
2072
2074
  generateShareCard,
2073
2075
  expiresInSeconds: params.expiresInSeconds ?? null,
2074
2076
  });
@@ -8,6 +8,7 @@ import {
8
8
  setClaworldManagedRuntimeBackupState,
9
9
  stripClaworldManagedRuntimeConfig,
10
10
  } from './managed-config.js';
11
+ import { resolveRuntimeAppToken } from './account-identity.js';
11
12
 
12
13
  function normalizeText(value, fallback = null) {
13
14
  if (value == null) return fallback;
@@ -67,18 +68,53 @@ export async function persistClaworldRuntimeBackup({
67
68
  runtime = null,
68
69
  accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
69
70
  configPath = resolveDefaultOpenClawConfigPath(),
71
+ runtimeConfig = null,
72
+ appToken = null,
73
+ relayAgentId = null,
70
74
  } = {}) {
71
- if (!runtime?.config?.loadConfig) {
75
+ if (!runtime?.config?.loadConfig && !runtimeConfig && !appToken) {
72
76
  return { skipped: true, reason: 'missing_runtime_config_loader' };
73
77
  }
74
78
 
75
- const currentConfig = await runtime.config.loadConfig();
76
- const { backup } = stripClaworldManagedRuntimeConfig(currentConfig, {
79
+ let currentConfig = {};
80
+ if (runtime?.config?.loadConfig) {
81
+ try {
82
+ currentConfig = await runtime.config.loadConfig();
83
+ } catch (error) {
84
+ if (!runtimeConfig && !appToken) throw error;
85
+ currentConfig = {};
86
+ }
87
+ }
88
+ const { backup: configBackup } = stripClaworldManagedRuntimeConfig(currentConfig, {
77
89
  accountId,
78
90
  preserveBackup: true,
79
91
  });
92
+ const explicitRuntimeConfig = runtimeConfig && typeof runtimeConfig === 'object' && !Array.isArray(runtimeConfig)
93
+ ? runtimeConfig
94
+ : {};
95
+ const backup = {
96
+ ...configBackup,
97
+ version: configBackup?.version || 1,
98
+ accountId: normalizeText(configBackup?.accountId, normalizeText(explicitRuntimeConfig.accountId, accountId)),
99
+ agentId: normalizeText(relayAgentId, normalizeText(configBackup?.agentId, normalizeText(explicitRuntimeConfig?.relay?.agentId, null))),
100
+ serverUrl: normalizeText(configBackup?.serverUrl, normalizeText(explicitRuntimeConfig.serverUrl, null)),
101
+ apiKey: normalizeText(configBackup?.apiKey, normalizeText(explicitRuntimeConfig.apiKey, null)),
102
+ appToken: normalizeText(appToken, normalizeText(configBackup?.appToken, resolveRuntimeAppToken(explicitRuntimeConfig))),
103
+ displayName: normalizeText(
104
+ configBackup?.displayName,
105
+ normalizeText(explicitRuntimeConfig.name, normalizeText(explicitRuntimeConfig.registration?.displayName, null)),
106
+ ),
107
+ name: normalizeText(configBackup?.name, normalizeText(explicitRuntimeConfig.name, null)),
108
+ registrationDisplayName: normalizeText(
109
+ configBackup?.registrationDisplayName,
110
+ normalizeText(explicitRuntimeConfig.registration?.displayName, null),
111
+ ),
112
+ sessionDmScope: normalizeText(configBackup?.sessionDmScope, null),
113
+ toolProfile: normalizeText(configBackup?.toolProfile, null),
114
+ preservedAt: new Date().toISOString(),
115
+ };
80
116
 
81
- if (!backup?.appToken) {
117
+ if (!backup.appToken) {
82
118
  return { skipped: true, reason: 'missing_app_token', backup: null };
83
119
  }
84
120