@xfxstudio/claworld 2026.6.10-testing.5 → 2026.6.26-testing.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.
@@ -336,25 +336,6 @@ function inferExistingAgentId(config = {}, accountId = DEFAULT_CLAWORLD_ACCOUNT_
336
336
  return DEFAULT_CLAWORLD_AGENT_ID;
337
337
  }
338
338
 
339
- const MANAGED_LEGACY_BUNDLED_SKILL_NAMES = Object.freeze([
340
- 'claworld-main-session',
341
- 'claworld-manage-worlds',
342
- 'claworld-help',
343
- ]);
344
-
345
- function hasOnlyManagedBundledSkills(value) {
346
- if (!Array.isArray(value) || value.length === 0) return false;
347
- return value.every((skillName) => MANAGED_LEGACY_BUNDLED_SKILL_NAMES.includes(skillName));
348
- }
349
-
350
- function buildManagedAgentEntry(options = {}) {
351
- return {
352
- id: options.agentId,
353
- workspace: options.workspace,
354
- ...(options.agentDirExplicit && options.agentDir ? { agentDir: options.agentDir } : {}),
355
- };
356
- }
357
-
358
339
  function buildManagedRoutingEntry(options = {}, existingRouting = {}) {
359
340
  return {
360
341
  ...ensureObject(existingRouting),
@@ -383,21 +364,33 @@ function buildManagedAccountEntry(options = {}) {
383
364
  };
384
365
  }
385
366
 
386
- if (!options.defaultTargetAgentId) {
367
+ const relay = {
368
+ ...(normalizeText(options.relayAgentId, null) ? { agentId: normalizeText(options.relayAgentId, null) } : {}),
369
+ ...(normalizeText(options.defaultTargetAgentId, null)
370
+ ? { defaultTargetAgentId: normalizeText(options.defaultTargetAgentId, null) }
371
+ : {}),
372
+ };
373
+ if (Object.keys(relay).length === 0) {
387
374
  return base;
388
375
  }
389
376
 
390
377
  return {
391
378
  ...base,
392
- relay: {
393
- defaultTargetAgentId: options.defaultTargetAgentId,
394
- },
379
+ relay,
395
380
  };
396
381
  }
397
382
 
398
383
  function buildMergedAccountEntry(existingAccount = {}, options = {}) {
399
384
  const existingRelay = ensureObject(existingAccount.relay);
400
385
  const existingRegistration = ensureObject(existingAccount.registration);
386
+ const relayAgentId = normalizeText(options.relayAgentId, null);
387
+ const defaultTargetAgentId = normalizeText(options.defaultTargetAgentId, null);
388
+ const mergedRelay = {
389
+ ...existingRelay,
390
+ ...(relayAgentId ? { agentId: relayAgentId } : {}),
391
+ ...(defaultTargetAgentId ? { defaultTargetAgentId } : {}),
392
+ };
393
+ const shouldPersistRelay = Boolean(existingAccount.relay || relayAgentId || defaultTargetAgentId);
401
394
  const merged = {
402
395
  ...existingAccount,
403
396
  enabled: true,
@@ -406,16 +399,7 @@ function buildMergedAccountEntry(existingAccount = {}, options = {}) {
406
399
  accountId: options.accountId,
407
400
  name: normalizeText(options.name, normalizeText(existingAccount.name, normalizeText(options.displayName, null))),
408
401
  routing: buildManagedRoutingEntry(options, existingAccount.routing),
409
- ...(options.defaultTargetAgentId
410
- ? {
411
- relay: {
412
- ...existingRelay,
413
- defaultTargetAgentId: options.defaultTargetAgentId,
414
- },
415
- }
416
- : existingAccount.relay
417
- ? { relay: existingRelay }
418
- : {}),
402
+ ...(shouldPersistRelay ? { relay: mergedRelay } : {}),
419
403
  };
420
404
  delete merged.toolProfile;
421
405
 
@@ -507,60 +491,6 @@ export function resolveToolNames({ toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE }
507
491
  return [...baseProfile];
508
492
  }
509
493
 
510
- const MANAGED_BUNDLED_SKILL_NAMES = Object.freeze([
511
- 'claworld-main-session',
512
- 'claworld-manage-worlds',
513
- 'claworld-help',
514
- ]);
515
-
516
- export function resolveManagedAgentSkills({ toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE } = {}) {
517
- const normalizedProfile = normalizeClaworldToolProfile(toolProfile);
518
- return normalizedProfile === 'full' ? undefined : [...MANAGED_BUNDLED_SKILL_NAMES];
519
- }
520
-
521
- function describeToolAllowEntries(toolNames = []) {
522
- if (toolNames.length === 1) {
523
- return toolNames[0] === '*' ? '1 entry (includes *)' : '1 entry';
524
- }
525
- return toolNames.includes('*')
526
- ? `${toolNames.length} entries (includes *)`
527
- : `${toolNames.length} entries`;
528
- }
529
-
530
- export function buildWorkspaceAgentsContent({
531
- agentId,
532
- accountId,
533
- registrationDisplayName,
534
- appToken = null,
535
- defaultTargetAgentId = null,
536
- } = {}) {
537
- const identityLine = appToken
538
- ? '- relay binding is resolved from the configured appToken at runtime'
539
- : registrationDisplayName
540
- ? '- relay binding is created during runtime bootstrap and persists as backend-issued credentials'
541
- : '- activation is pending until the user completes Claworld public identity setup';
542
-
543
- return `# Claworld Channel Agent
544
-
545
- This workspace is dedicated to the OpenClaw \`claworld\` channel.
546
-
547
- Routing contract:
548
-
549
- - local OpenClaw agent id: \`${agentId}\`
550
- - claworld account id: \`${accountId}\`
551
- ${registrationDisplayName ? `- bootstrap display name: \`${registrationDisplayName}\`` : (appToken ? '- credential mode: appToken/manual binding' : '- credential mode: activation pending')}
552
- ${identityLine}
553
- ${defaultTargetAgentId ? `- default outbound target agentId: \`${defaultTargetAgentId}\`` : '- outbound sends require explicit target agentId inputs'}
554
-
555
- Operating rules:
556
-
557
- - keep this workspace focused on Claworld relay and world interactions
558
- - use explicit \`claworld_*\` tools when they are available
559
- - do not treat this workspace as the user's general-purpose main agent
560
- - do not treat this as a separately bootstrapped OpenClaw persona
561
- `;
562
- }
563
-
564
494
  function buildBoundAgentEntry(existingAgent = {}, agentId) {
565
495
  const nextAgent = {
566
496
  ...ensureObject(existingAgent),
@@ -570,41 +500,8 @@ function buildBoundAgentEntry(existingAgent = {}, agentId) {
570
500
  return nextAgent;
571
501
  }
572
502
 
573
- export function buildWorkspaceMemoryContent({
574
- agentId,
575
- accountId,
576
- registrationDisplayName,
577
- appToken = null,
578
- defaultTargetAgentId = null,
579
- } = {}) {
580
- const identityLine = appToken
581
- ? '- relay binding: resolved from appToken at runtime'
582
- : registrationDisplayName
583
- ? '- relay binding: assigned during runtime bootstrap'
584
- : '- relay binding: pending until public identity setup completes';
585
-
586
- return `# Claworld Memory
587
-
588
- - workspace owner: \`${agentId}\`
589
- - claworld account: \`${accountId}\`
590
- ${registrationDisplayName ? `- bootstrap display name: \`${registrationDisplayName}\`` : ''}
591
- ${identityLine}
592
- ${defaultTargetAgentId ? `- default outbound target agentId: \`${defaultTargetAgentId}\`` : ''}
593
-
594
- Use this file for durable Claworld-specific notes only.
595
-
596
- - keep world rules, pairing context, and recurring counterpart preferences here when they help future Claworld conversations
597
- - do not duplicate a full standalone OpenClaw bootstrap/persona here
598
- - prefer channel/world-specific memory over general-purpose assistant memory
599
- `;
600
- }
601
-
602
- export function resolveDefaultManagedWorkspace(agentId = DEFAULT_CLAWORLD_AGENT_ID) {
603
- return `~/.openclaw/workspace-${agentId}`;
604
- }
605
-
606
503
  export function resolveDefaultManagedDisplayName(accountId = DEFAULT_CLAWORLD_ACCOUNT_ID) {
607
- return `${titleCase(accountId)} Channel Agent`;
504
+ return `${titleCase(accountId)} Channel`;
608
505
  }
609
506
 
610
507
  export function resolveClaworldManagedRuntimeOptions({
@@ -615,7 +512,6 @@ export function resolveClaworldManagedRuntimeOptions({
615
512
  installerState = null,
616
513
  } = {}) {
617
514
  const resolvedAccountId = normalizeText(accountId, DEFAULT_CLAWORLD_ACCOUNT_ID);
618
- const attachToExistingAgent = overrides.attachToExistingAgent !== false;
619
515
  const existingBackup = findClaworldManagedRuntimeBackup(installerState, resolvedAccountId);
620
516
  const inferredAgentId = inferExistingAgentId(cfg, resolvedAccountId)
621
517
  || normalizeText(existingBackup.agentId, null);
@@ -623,20 +519,7 @@ export function resolveClaworldManagedRuntimeOptions({
623
519
  const existingAgent = findAgentEntry(cfg, agentId);
624
520
  const existingAccount = findManagedAccountEntry(cfg, resolvedAccountId);
625
521
  const replaceManagedRuntime = overrides.replaceManagedRuntime !== false;
626
- const explicitWorkspace = normalizeText(overrides.workspace, null);
627
- const manageAgentEntry = overrides.manageAgentEntry === true
628
- || overrides.agentDirExplicit === true
629
- || attachToExistingAgent !== true;
630
- const manageWorkspace = overrides.manageWorkspace === true
631
- || Boolean(explicitWorkspace)
632
- || attachToExistingAgent !== true;
633
- const defaultWorkspace = manageWorkspace ? resolveDefaultManagedWorkspace(agentId) : null;
634
- const workspace = normalizeText(
635
- explicitWorkspace,
636
- replaceManagedRuntime
637
- ? normalizeText(existingAgent?.workspace, normalizeText(existingBackup.workspace, defaultWorkspace))
638
- : normalizeText(existingAgent?.workspace, normalizeText(existingBackup.workspace, defaultWorkspace)),
639
- );
522
+ const workspace = normalizeText(existingAgent?.workspace, normalizeText(existingBackup.workspace, null));
640
523
  const serverUrl = normalizeText(
641
524
  overrides.serverUrl,
642
525
  normalizeText(input.httpUrl, normalizeText(input.url, normalizeText(existingBackup.serverUrl, DEFAULT_CLAWORLD_SERVER_URL))),
@@ -680,20 +563,16 @@ export function resolveClaworldManagedRuntimeOptions({
680
563
 
681
564
  return {
682
565
  repoRoot: normalizeText(overrides.repoRoot, null),
683
- attachToExistingAgent,
684
566
  agentId,
685
567
  accountId: resolvedAccountId,
686
568
  workspace,
687
- manageAgentEntry,
688
- manageWorkspace,
689
- agentDir: normalizeText(overrides.agentDir, null),
690
- agentDirExplicit: overrides.agentDirExplicit === true,
691
569
  serverUrl,
692
570
  apiKey,
693
571
  appToken,
694
572
  registrationDisplayName,
695
573
  displayName,
696
574
  name,
575
+ relayAgentId: normalizeText(overrides.relayAgentId, normalizeText(existingAccount?.relay?.agentId, null)),
697
576
  defaultTargetAgentId: normalizeText(overrides.defaultTargetAgentId, null),
698
577
  sessionDmScope: normalizeText(
699
578
  overrides.sessionDmScope,
@@ -715,7 +594,6 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
715
594
  const replaceManagedRuntime = options.replaceManagedRuntime !== false;
716
595
  const preserveDefaultAccount = options.preserveDefaultAccount === true;
717
596
  const sessionDmScope = normalizeText(options.sessionDmScope, DEFAULT_CLAWORLD_DM_SCOPE);
718
- const manageAgentEntry = options.manageAgentEntry === true;
719
597
 
720
598
  const removedManagedToolNames = new Set(CLAWORLD_PUBLIC_TOOL_NAMES);
721
599
  if (inputConfig?.tools && typeof inputConfig.tools === 'object') {
@@ -754,72 +632,18 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
754
632
 
755
633
  config.agents = ensureObject(config.agents);
756
634
  const existingAgentList = Array.isArray(config.agents.list) ? [...config.agents.list] : [];
757
- if (manageAgentEntry) {
758
- const managedAgentEntry = buildManagedAgentEntry(options);
759
- if (replaceManagedRuntime) {
760
- const removedAgentEntries = existingAgentList.filter((item) => ensureObject(item).id === options.agentId).length;
761
- config.agents.list = [
762
- ...existingAgentList.filter((item) => ensureObject(item).id !== options.agentId),
763
- {
764
- ...managedAgentEntry,
765
- tools: mergeManagedPluginToolExposure(managedAgentEntry.tools),
766
- },
767
- ];
768
- summary.push(
769
- removedAgentEntries > 0
770
- ? `replaced managed agent entry ${options.agentId}`
771
- : `added workspace-scoped agent entry ${options.agentId}`,
772
- );
773
- } else {
774
- const agentIndex = findAgentIndex(existingAgentList, options.agentId);
775
- if (agentIndex >= 0) {
776
- const existingAgent = ensureObject(existingAgentList[agentIndex]);
777
- const existingAgentDir = normalizeText(existingAgent.agentDir, null);
778
- const keepExistingAgentDir = Boolean(
779
- existingAgentDir
780
- && (
781
- options.agentDirExplicit
782
- || existingAgentDir !== normalizeText(options.agentDir, null)
783
- ),
784
- );
785
- const nextAgentEntry = {
786
- ...existingAgent,
787
- id: options.agentId,
788
- workspace: normalizeText(existingAgent.workspace, options.workspace),
789
- ...(keepExistingAgentDir ? { agentDir: existingAgentDir } : {}),
790
- };
791
- if (!keepExistingAgentDir) {
792
- delete nextAgentEntry.agentDir;
793
- }
794
- if (hasOnlyManagedBundledSkills(existingAgent.skills)) {
795
- delete nextAgentEntry.skills;
796
- }
797
- nextAgentEntry.tools = mergeManagedPluginToolExposure(existingAgent.tools);
798
- existingAgentList[agentIndex] = nextAgentEntry;
799
- summary.push(`updated existing agent entry ${options.agentId}`);
800
- } else {
801
- existingAgentList.push({
802
- ...managedAgentEntry,
803
- tools: mergeManagedPluginToolExposure(managedAgentEntry.tools),
804
- });
805
- summary.push(`added workspace-scoped agent entry ${options.agentId}`);
806
- }
807
- config.agents.list = existingAgentList;
808
- }
635
+ const agentIndex = findAgentIndex(existingAgentList, options.agentId);
636
+ if (agentIndex >= 0) {
637
+ const existingAgent = ensureObject(existingAgentList[agentIndex]);
638
+ existingAgentList[agentIndex] = buildBoundAgentEntry(existingAgent, options.agentId);
639
+ config.agents.list = existingAgentList;
640
+ summary.push(`updated bound agent tool exposure ${options.agentId}`);
809
641
  } else {
810
- const agentIndex = findAgentIndex(existingAgentList, options.agentId);
811
- if (agentIndex >= 0) {
812
- const existingAgent = ensureObject(existingAgentList[agentIndex]);
813
- existingAgentList[agentIndex] = buildBoundAgentEntry(existingAgent, options.agentId);
814
- config.agents.list = existingAgentList;
815
- summary.push(`updated bound agent tool exposure ${options.agentId}`);
816
- } else {
817
- existingAgentList.push(buildBoundAgentEntry({}, options.agentId));
818
- config.agents.list = existingAgentList;
819
- summary.push(`added bound agent entry ${options.agentId}`);
820
- }
821
- summary.push(`attached claworld account ${options.accountId} to existing local agent ${options.agentId}`);
642
+ existingAgentList.push(buildBoundAgentEntry({}, options.agentId));
643
+ config.agents.list = existingAgentList;
644
+ summary.push(`added bound agent entry ${options.agentId}`);
822
645
  }
646
+ summary.push(`attached claworld account ${options.accountId} to local agent ${options.agentId}`);
823
647
 
824
648
  config.channels = ensureObject(config.channels);
825
649
  const existingClaworldRoot = ensureObject(config.channels.claworld);
@@ -10,6 +10,10 @@ import {
10
10
  inspectClaworldChannelAccount,
11
11
  listClaworldAccountIds,
12
12
  } from './config-schema.js';
13
+ import {
14
+ fetchJson,
15
+ normalizeRelayHttpBaseUrl,
16
+ } from '../runtime/http-boundary.js';
13
17
 
14
18
  function collectUnsupportedSetupFlags(input = {}) {
15
19
  const unsupported = [];
@@ -102,7 +106,7 @@ function hasClaworldBinding(config = {}, { agentId, accountId } = {}) {
102
106
  });
103
107
  }
104
108
 
105
- function isRelayBootstrapReady(account = {}) {
109
+ function hasRuntimeCredential(account = {}) {
106
110
  return Boolean(
107
111
  account?.configured
108
112
  && normalizeText(account?.appToken, null),
@@ -129,24 +133,23 @@ export function inspectManagedClaworldInstall({
129
133
  const accountStatus = managedAccountPresent
130
134
  ? inspectClaworldChannelAccount(cfg, managedOptions.accountId)
131
135
  : inspectClaworldChannelAccount({}, managedOptions.accountId);
132
- const activationReady = isRelayBootstrapReady(accountStatus);
136
+ const runtimeCredentialReady = hasRuntimeCredential(accountStatus);
133
137
  const setupReady = Boolean(
134
138
  managedAccountPresent
135
139
  && managedBindingPresent
136
- && (managedOptions.manageAgentEntry !== true || managedAgentPresent)
137
140
  );
138
141
 
139
142
  let statusLabel = 'needs setup';
140
143
  let selectionHint = 'remote relay world channel';
141
144
  let quickstartScore = 5;
142
145
 
143
- if (setupReady && activationReady) {
146
+ if (setupReady && runtimeCredentialReady) {
144
147
  statusLabel = 'configured';
145
148
  selectionHint = 'configured · ready';
146
149
  quickstartScore = 2;
147
150
  } else if (setupReady) {
148
- statusLabel = 'configured (activation pending)';
149
- selectionHint = 'configured · activation pending';
151
+ statusLabel = 'configured (email verification pending)';
152
+ selectionHint = 'configured · email verification pending';
150
153
  quickstartScore = 3;
151
154
  } else if (managedAccountPresent && !managedBindingPresent) {
152
155
  statusLabel = 'configured (binding pending)';
@@ -167,7 +170,7 @@ export function inspectManagedClaworldInstall({
167
170
  managedAgentPresent,
168
171
  managedBindingPresent,
169
172
  accountStatus,
170
- activationReady,
173
+ runtimeCredentialReady,
171
174
  setupReady,
172
175
  statusLabel,
173
176
  selectionHint,
@@ -185,7 +188,7 @@ export function buildClaworldOnboardingStatus({
185
188
  statusLines: [`Claworld: ${inspection.statusLabel}`],
186
189
  selectionHint: inspection.selectionHint,
187
190
  quickstartScore: inspection.quickstartScore,
188
- activationReady: inspection.activationReady,
191
+ runtimeCredentialReady: inspection.runtimeCredentialReady,
189
192
  };
190
193
  }
191
194
 
@@ -232,23 +235,172 @@ function resolveManagedOptionsFromContext({ cfg = {}, accountId = null, input =
232
235
  });
233
236
  }
234
237
 
238
+ function resolveSetupFetchImpl(runtime = {}) {
239
+ const candidate = runtime?.fetchImpl
240
+ || runtime?.fetch
241
+ || (typeof globalThis.fetch === 'function' ? globalThis.fetch : null);
242
+ if (typeof candidate !== 'function') {
243
+ throw new Error('Claworld setup requires fetch support to complete email verification.');
244
+ }
245
+ return (url, init) => candidate(url, init);
246
+ }
247
+
248
+ function validateSetupEmail(value) {
249
+ const normalized = normalizeText(value, null);
250
+ if (!normalized) return 'Email is required.';
251
+ if (!normalized.includes('@')) return 'Enter a valid email address.';
252
+ return null;
253
+ }
254
+
255
+ function validateSetupVerificationCode(value) {
256
+ return normalizeText(value, null) ? null : 'Verification code is required.';
257
+ }
258
+
259
+ function formatSetupApiError(result, fallbackMessage) {
260
+ const body = ensureObject(result?.body);
261
+ const detail = normalizeText(
262
+ body.publicMessage,
263
+ normalizeText(body.error, normalizeText(body.message, null)),
264
+ );
265
+ return detail ? `${fallbackMessage}: ${detail}` : fallbackMessage;
266
+ }
267
+
268
+ async function startSetupEmailVerification({
269
+ runtimeConfig,
270
+ email,
271
+ displayName = null,
272
+ fetchImpl,
273
+ } = {}) {
274
+ const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
275
+ const normalizedEmail = normalizeText(email, null);
276
+ const normalizedDisplayName = normalizeText(displayName, null);
277
+ const result = await fetchJson(fetchImpl, `${baseUrl}/v1/identity/email/start`, {
278
+ method: 'POST',
279
+ headers: {
280
+ 'content-type': 'application/json',
281
+ ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
282
+ },
283
+ body: JSON.stringify({
284
+ email: normalizedEmail,
285
+ ...(normalizedDisplayName ? { displayName: normalizedDisplayName } : {}),
286
+ }),
287
+ });
288
+ if (!result.ok) {
289
+ throw new Error(formatSetupApiError(result, 'Failed to start Claworld email verification'));
290
+ }
291
+ return ensureObject(result.body);
292
+ }
293
+
294
+ async function completeSetupEmailVerification({
295
+ runtimeConfig,
296
+ email,
297
+ code,
298
+ fetchImpl,
299
+ } = {}) {
300
+ const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
301
+ const result = await fetchJson(fetchImpl, `${baseUrl}/v1/identity/email/verify`, {
302
+ method: 'POST',
303
+ headers: {
304
+ 'content-type': 'application/json',
305
+ ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
306
+ },
307
+ body: JSON.stringify({
308
+ email: normalizeText(email, null),
309
+ code: normalizeText(code, null),
310
+ }),
311
+ });
312
+ if (!result.ok) {
313
+ throw new Error(formatSetupApiError(result, 'Failed to complete Claworld email verification'));
314
+ }
315
+ return ensureObject(result.body);
316
+ }
317
+
318
+ async function resolveSetupCredential({
319
+ managedOptions,
320
+ prompter,
321
+ runtime = {},
322
+ input = {},
323
+ } = {}) {
324
+ if (managedOptions.appToken) return null;
325
+
326
+ const fetchImpl = resolveSetupFetchImpl(runtime);
327
+ const email = normalizeText(await prompter.text({
328
+ message: 'Claworld account email',
329
+ placeholder: 'you@example.com',
330
+ validate: validateSetupEmail,
331
+ }), null);
332
+
333
+ await startSetupEmailVerification({
334
+ runtimeConfig: managedOptions,
335
+ email,
336
+ displayName: normalizeText(input?.name, null),
337
+ fetchImpl,
338
+ });
339
+
340
+ const code = normalizeText(await prompter.text({
341
+ message: `Verification code sent to ${email}`,
342
+ placeholder: '123456',
343
+ validate: validateSetupVerificationCode,
344
+ }), null);
345
+
346
+ const verification = await completeSetupEmailVerification({
347
+ runtimeConfig: managedOptions,
348
+ email,
349
+ code,
350
+ fetchImpl,
351
+ });
352
+ const appToken = normalizeText(verification.appToken, null);
353
+ const relayAgentId = normalizeText(verification.agentId, null);
354
+ if (!appToken || !relayAgentId) {
355
+ throw new Error('Claworld email verification did not return appToken and agentId.');
356
+ }
357
+ return {
358
+ email,
359
+ appToken,
360
+ relayAgentId,
361
+ created: verification.created === true,
362
+ recovered: verification.recovered === true,
363
+ };
364
+ }
365
+
235
366
  async function applyManagedOnboardingConfig({
236
367
  cfg = {},
368
+ runtime = {},
237
369
  prompter,
238
370
  accountId = null,
239
371
  phase = 'setup',
240
372
  input = {},
241
373
  } = {}) {
242
- const managedOptions = resolveManagedOptionsFromContext({ cfg, accountId, input });
374
+ const initialManagedOptions = resolveManagedOptionsFromContext({ cfg, accountId, input });
375
+ const setupCredential = await resolveSetupCredential({
376
+ managedOptions: initialManagedOptions,
377
+ prompter,
378
+ runtime,
379
+ input,
380
+ });
381
+ const managedOptions = setupCredential
382
+ ? {
383
+ ...initialManagedOptions,
384
+ appToken: setupCredential.appToken,
385
+ relayAgentId: setupCredential.relayAgentId,
386
+ }
387
+ : initialManagedOptions;
243
388
  const next = applyClaworldManagedRuntimeConfig(cfg, managedOptions);
244
389
 
245
390
  const noteLines = [
246
391
  `Bound local agent/account: ${managedOptions.agentId}`,
247
392
  `Remote backend: ${managedOptions.serverUrl}`,
248
- managedOptions.appToken
249
- ? 'Activation state: ready via configured appToken'
250
- : 'Activation state: pending until claworld_manage_account(action=activate_account) runs',
251
- 'This flow refreshes plugin-side config and binds claworld onto the selected local agent. It does not run installer commands or start a backend service.',
393
+ setupCredential
394
+ ? `Email verification: completed for ${setupCredential.email}; runtime credential saved to OpenClaw config`
395
+ : managedOptions.appToken
396
+ ? 'Runtime credential: configured appToken is present'
397
+ : 'Email verification: pending until claworld_manage_account(action=start_email_verification|complete_email_verification) runs',
398
+ managedOptions.relayAgentId
399
+ ? `Remote agent identity: ${managedOptions.relayAgentId}`
400
+ : 'Remote agent identity: pending',
401
+ 'Workspace memory: runtime prompt bootstrap maintains .claworld/ in the active host workspace',
402
+ 'This flow refreshes plugin-side config and binds claworld onto the selected local agent.',
403
+ 'Setup lifecycle: OpenClaw host-native setup; channel reload is handled by config reload.',
252
404
  ];
253
405
  await prompter.note(
254
406
  noteLines.join('\n'),
@@ -293,17 +445,19 @@ export const claworldOnboardingAdapter = {
293
445
  }),
294
446
  };
295
447
  },
296
- configure: async ({ cfg, prompter, accountOverrides }) =>
448
+ configure: async ({ cfg, runtime, prompter, accountOverrides }) =>
297
449
  applyManagedOnboardingConfig({
298
450
  cfg,
451
+ runtime,
299
452
  prompter,
300
453
  accountId: accountOverrides?.claworld,
301
454
  phase: 'setup',
302
455
  input: {},
303
456
  }),
304
- configureWhenConfigured: async ({ cfg, prompter, accountOverrides }) =>
457
+ configureWhenConfigured: async ({ cfg, runtime, prompter, accountOverrides }) =>
305
458
  applyManagedOnboardingConfig({
306
459
  cfg,
460
+ runtime,
307
461
  prompter,
308
462
  accountId: accountOverrides?.claworld,
309
463
  phase: 'refresh',