@xfxstudio/claworld 0.1.0
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 +60 -0
- package/bin/claworld.mjs +9 -0
- package/index.js +51 -0
- package/openclaw.plugin.json +470 -0
- package/package.json +76 -0
- package/setup-entry.js +6 -0
- package/src/lib/accepted-chat-kickoff.js +192 -0
- package/src/lib/agent-address.js +46 -0
- package/src/lib/agent-profile.js +69 -0
- package/src/lib/http-auth.js +151 -0
- package/src/lib/policy.js +118 -0
- package/src/lib/runtime-errors.js +149 -0
- package/src/lib/runtime-guidance.js +458 -0
- package/src/openclaw/index.js +53 -0
- package/src/openclaw/installer/cli.js +349 -0
- package/src/openclaw/installer/constants.js +6 -0
- package/src/openclaw/installer/core.js +1548 -0
- package/src/openclaw/installer/doctor.js +690 -0
- package/src/openclaw/installer/workspace-contract.js +403 -0
- package/src/openclaw/plugin/account-identity.js +66 -0
- package/src/openclaw/plugin/claworld-channel-plugin.js +3118 -0
- package/src/openclaw/plugin/config-schema.js +464 -0
- package/src/openclaw/plugin/lifecycle.js +114 -0
- package/src/openclaw/plugin/managed-config.js +648 -0
- package/src/openclaw/plugin/onboarding.js +291 -0
- package/src/openclaw/plugin/register.js +961 -0
- package/src/openclaw/plugin/relay-client.js +783 -0
- package/src/openclaw/plugin/runtime.js +12 -0
- package/src/openclaw/protocol/relay-event-protocol.js +31 -0
- package/src/openclaw/runtime/canonical-result-builder.js +116 -0
- package/src/openclaw/runtime/demo-session-bootstrap.js +37 -0
- package/src/openclaw/runtime/feedback-helper.js +145 -0
- package/src/openclaw/runtime/inbound-session-router.js +36 -0
- package/src/openclaw/runtime/outbound-session-bridge.js +17 -0
- package/src/openclaw/runtime/product-shell-helper.js +1712 -0
- package/src/openclaw/runtime/runtime-path.js +19 -0
- package/src/openclaw/runtime/system-message-orchestrator.js +1 -0
- package/src/openclaw/runtime/tool-contracts.js +714 -0
- package/src/openclaw/runtime/tool-inventory.js +92 -0
- package/src/openclaw/runtime/world-moderation-helper.js +415 -0
- package/src/openclaw/runtime/world-session-startup.js +1 -0
- package/src/product-shell/catalog/default-world-catalog.js +296 -0
- package/src/product-shell/contracts/candidate-feed.js +330 -0
- package/src/product-shell/contracts/chat-request-approval-policy.js +98 -0
- package/src/product-shell/contracts/world-manifest.js +435 -0
- package/src/product-shell/contracts/world-orchestration.js +1024 -0
- package/src/product-shell/feedback/feedback-contract.js +13 -0
- package/src/product-shell/feedback/feedback-routes.js +98 -0
- package/src/product-shell/feedback/feedback-service.js +254 -0
- package/src/product-shell/index.js +163 -0
- package/src/product-shell/matching/matchmaking-service.js +340 -0
- package/src/product-shell/membership/membership-service.js +277 -0
- package/src/product-shell/onboarding/onboarding-routes.js +37 -0
- package/src/product-shell/onboarding/onboarding-service.js +230 -0
- package/src/product-shell/orchestration/session-orchestrator.js +38 -0
- package/src/product-shell/results/result-service.js +15 -0
- package/src/product-shell/search/search-service.js +359 -0
- package/src/product-shell/social/chat-request-approval-policy.js +332 -0
- package/src/product-shell/social/chat-request-routes.js +108 -0
- package/src/product-shell/social/chat-request-service.js +632 -0
- package/src/product-shell/social/friend-routes.js +82 -0
- package/src/product-shell/social/friend-service.js +560 -0
- package/src/product-shell/social/social-routes.js +21 -0
- package/src/product-shell/social/social-service.js +140 -0
- package/src/product-shell/worlds/world-admin-service.js +705 -0
- package/src/product-shell/worlds/world-authorization.js +135 -0
- package/src/product-shell/worlds/world-broadcast-service.js +299 -0
- package/src/product-shell/worlds/world-routes.js +410 -0
- package/src/product-shell/worlds/world-service.js +89 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
CLAWORLD_MINIMAL_OPENCLAW_TOOL_NAMES,
|
|
5
|
+
CLAWORLD_OPTIONAL_WORLD_HELPER_TOOL_NAMES,
|
|
6
|
+
CLAWORLD_PUBLIC_TOOL_NAMES,
|
|
7
|
+
CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES,
|
|
8
|
+
CLAWORLD_TOOL_PROFILES,
|
|
9
|
+
} from '../runtime/tool-inventory.js';
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_CHAT_REQUEST_APPROVAL_POLICY_MODE,
|
|
12
|
+
normalizeChatRequestApprovalMode,
|
|
13
|
+
} from '../../product-shell/contracts/chat-request-approval-policy.js';
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_CLAWORLD_SERVER_URL = 'https://claworld.zy-sj.com';
|
|
16
|
+
export const DEFAULT_CLAWORLD_API_KEY = 'local-test';
|
|
17
|
+
export const DEFAULT_CLAWORLD_AGENT_ID = 'claworld';
|
|
18
|
+
export const DEFAULT_CLAWORLD_ACCOUNT_ID = 'claworld';
|
|
19
|
+
export const DEFAULT_CLAWORLD_TOOL_PROFILE = 'default';
|
|
20
|
+
export const DEFAULT_CLAWORLD_DM_SCOPE = 'per-channel-peer';
|
|
21
|
+
export const DEFAULT_CLAWORLD_APPROVAL_MODE = DEFAULT_CHAT_REQUEST_APPROVAL_POLICY_MODE;
|
|
22
|
+
|
|
23
|
+
export const TOOL_PROFILES = CLAWORLD_TOOL_PROFILES;
|
|
24
|
+
|
|
25
|
+
export function normalizeText(value, fallback = null) {
|
|
26
|
+
if (value == null) return fallback;
|
|
27
|
+
const normalized = String(value).trim();
|
|
28
|
+
return normalized || fallback;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function titleCase(value) {
|
|
32
|
+
const normalized = normalizeText(value, '') || '';
|
|
33
|
+
if (!normalized) return 'Claworld';
|
|
34
|
+
return normalized
|
|
35
|
+
.split(/[^A-Za-z0-9]+/)
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
38
|
+
.join(' ');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function ensureObject(value) {
|
|
42
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeRegistrationAgentCode(value, fallback = null) {
|
|
47
|
+
const normalized = normalizeText(value, fallback);
|
|
48
|
+
return normalized ? normalized.toLowerCase() : fallback;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function expandUserPath(input, homeDir = os.homedir()) {
|
|
52
|
+
const normalized = normalizeText(input, null);
|
|
53
|
+
if (!normalized) return normalized;
|
|
54
|
+
if (normalized === '~') return homeDir;
|
|
55
|
+
if (normalized.startsWith('~/')) return path.join(homeDir, normalized.slice(2));
|
|
56
|
+
return normalized;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function asStringArray(value) {
|
|
60
|
+
if (value == null) return [];
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
return value
|
|
63
|
+
.map((item) => normalizeText(item, null))
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
const normalized = normalizeText(value, null);
|
|
67
|
+
return normalized ? [normalized] : [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function uniqueStrings(values = []) {
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
const result = [];
|
|
73
|
+
for (const value of values) {
|
|
74
|
+
const normalized = normalizeText(value, null);
|
|
75
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
76
|
+
seen.add(normalized);
|
|
77
|
+
result.push(normalized);
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function inferRelayDomain(defaultToAddress = null) {
|
|
83
|
+
const normalized = normalizeText(defaultToAddress, null);
|
|
84
|
+
if (!normalized) return 'relay.local';
|
|
85
|
+
const atIndex = normalized.indexOf('@');
|
|
86
|
+
if (atIndex <= 0 || atIndex >= normalized.length - 1) return 'relay.local';
|
|
87
|
+
return normalized.slice(atIndex + 1).trim().toLowerCase() || 'relay.local';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function findAgentIndex(agentList = [], agentId) {
|
|
91
|
+
return agentList.findIndex((item) => ensureObject(item).id === agentId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function findAgentEntry(config = {}, agentId) {
|
|
95
|
+
const list = Array.isArray(config?.agents?.list) ? config.agents.list : [];
|
|
96
|
+
return list
|
|
97
|
+
.map((item) => ensureObject(item))
|
|
98
|
+
.find((item) => item.id === agentId) || null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function findManagedAccountEntry(config = {}, accountId) {
|
|
102
|
+
const claworldRoot = ensureObject(config?.channels?.claworld);
|
|
103
|
+
const accounts = ensureObject(claworldRoot.accounts);
|
|
104
|
+
if (accounts[accountId]) return ensureObject(accounts[accountId]);
|
|
105
|
+
if (normalizeText(claworldRoot.accountId, null) === accountId) return claworldRoot;
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function buildManagedAgentEntry(options = {}) {
|
|
110
|
+
const managedSkills = resolveManagedAgentSkills({ toolProfile: options.toolProfile });
|
|
111
|
+
return {
|
|
112
|
+
id: options.agentId,
|
|
113
|
+
workspace: options.workspace,
|
|
114
|
+
...(managedSkills === undefined ? {} : { skills: managedSkills }),
|
|
115
|
+
...(options.agentDirExplicit && options.agentDir ? { agentDir: options.agentDir } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildManagedAccountEntry(options = {}) {
|
|
120
|
+
const base = {
|
|
121
|
+
enabled: true,
|
|
122
|
+
serverUrl: options.serverUrl,
|
|
123
|
+
apiKey: options.apiKey,
|
|
124
|
+
accountId: options.accountId,
|
|
125
|
+
toolProfile: options.toolProfile,
|
|
126
|
+
name: normalizeText(options.name, normalizeText(options.displayName, null)),
|
|
127
|
+
approval: {
|
|
128
|
+
mode: normalizeChatRequestApprovalMode(options.approvalMode, DEFAULT_CLAWORLD_APPROVAL_MODE),
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
if (options.appToken) {
|
|
133
|
+
base.appToken = options.appToken;
|
|
134
|
+
} else {
|
|
135
|
+
base.registration = {
|
|
136
|
+
enabled: true,
|
|
137
|
+
agentCode: options.registrationAgentCode,
|
|
138
|
+
displayName: options.displayName,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!options.defaultToAddress) {
|
|
143
|
+
return base;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
...base,
|
|
148
|
+
relay: {
|
|
149
|
+
defaultToAddress: options.defaultToAddress,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildMergedAccountEntry(existingAccount = {}, options = {}) {
|
|
155
|
+
const existingApproval = ensureObject(existingAccount.approval);
|
|
156
|
+
const existingRelay = ensureObject(existingAccount.relay);
|
|
157
|
+
const existingRegistration = ensureObject(existingAccount.registration);
|
|
158
|
+
const merged = {
|
|
159
|
+
...existingAccount,
|
|
160
|
+
enabled: true,
|
|
161
|
+
serverUrl: options.serverUrl,
|
|
162
|
+
apiKey: options.apiKey,
|
|
163
|
+
accountId: options.accountId,
|
|
164
|
+
toolProfile: options.toolProfile,
|
|
165
|
+
name: normalizeText(options.name, normalizeText(existingAccount.name, normalizeText(options.displayName, null))),
|
|
166
|
+
approval: {
|
|
167
|
+
...existingApproval,
|
|
168
|
+
mode: normalizeChatRequestApprovalMode(options.approvalMode, DEFAULT_CLAWORLD_APPROVAL_MODE),
|
|
169
|
+
},
|
|
170
|
+
...(options.defaultToAddress
|
|
171
|
+
? {
|
|
172
|
+
relay: {
|
|
173
|
+
...existingRelay,
|
|
174
|
+
defaultToAddress: options.defaultToAddress,
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
: existingAccount.relay
|
|
178
|
+
? { relay: existingRelay }
|
|
179
|
+
: {}),
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (options.appToken) {
|
|
183
|
+
return {
|
|
184
|
+
...merged,
|
|
185
|
+
appToken: options.appToken,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
...merged,
|
|
191
|
+
registration: {
|
|
192
|
+
...existingRegistration,
|
|
193
|
+
enabled: true,
|
|
194
|
+
agentCode: options.registrationAgentCode,
|
|
195
|
+
displayName: options.displayName,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function canonicalRelayAgentCode(registrationAgentCode, defaultToAddress = null) {
|
|
201
|
+
const normalizedAgentCode = normalizeRegistrationAgentCode(registrationAgentCode, null);
|
|
202
|
+
if (!normalizedAgentCode) return null;
|
|
203
|
+
if (normalizedAgentCode.includes('@')) return normalizedAgentCode;
|
|
204
|
+
return `${normalizedAgentCode}@${inferRelayDomain(defaultToAddress)}`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function normalizeClaworldToolProfile(toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE) {
|
|
208
|
+
const normalized = normalizeText(toolProfile, DEFAULT_CLAWORLD_TOOL_PROFILE);
|
|
209
|
+
if (normalized === 'world') return 'default';
|
|
210
|
+
return normalized;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function inferClaworldToolProfile(config = {}) {
|
|
214
|
+
const allow = new Set(asStringArray(config?.tools?.allow));
|
|
215
|
+
if (allow.has('*')) return 'full';
|
|
216
|
+
if (CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES.some((toolName) => allow.has(toolName))) {
|
|
217
|
+
return 'default';
|
|
218
|
+
}
|
|
219
|
+
if (CLAWORLD_PUBLIC_TOOL_NAMES.some((toolName) => allow.has(toolName))) {
|
|
220
|
+
return 'minimal';
|
|
221
|
+
}
|
|
222
|
+
return DEFAULT_CLAWORLD_TOOL_PROFILE;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function resolveStoredClaworldToolProfile(account = {}) {
|
|
226
|
+
const persistedToolProfile = normalizeText(account?.toolProfile, null);
|
|
227
|
+
return persistedToolProfile
|
|
228
|
+
? normalizeClaworldToolProfile(persistedToolProfile)
|
|
229
|
+
: null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function resolveManagedToolProfile({
|
|
233
|
+
cfg = {},
|
|
234
|
+
existingAccount = {},
|
|
235
|
+
explicitToolProfile = null,
|
|
236
|
+
} = {}) {
|
|
237
|
+
const normalizedExplicitToolProfile = normalizeText(explicitToolProfile, null);
|
|
238
|
+
if (normalizedExplicitToolProfile) {
|
|
239
|
+
return normalizeClaworldToolProfile(normalizedExplicitToolProfile);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const persistedToolProfile = resolveStoredClaworldToolProfile(existingAccount);
|
|
243
|
+
if (persistedToolProfile) {
|
|
244
|
+
return persistedToolProfile;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const inferredToolProfile = inferClaworldToolProfile(cfg);
|
|
248
|
+
if (inferredToolProfile === 'minimal') {
|
|
249
|
+
const allow = new Set(asStringArray(cfg?.tools?.allow));
|
|
250
|
+
const hasMinimalCoreAnchor = CLAWORLD_MINIMAL_OPENCLAW_TOOL_NAMES
|
|
251
|
+
.some((toolName) => allow.has(toolName));
|
|
252
|
+
if (!hasMinimalCoreAnchor) {
|
|
253
|
+
// Legacy managed installs stored only the old public claworld allowlist.
|
|
254
|
+
// Treat that shape as the historical default/world profile during refresh.
|
|
255
|
+
return DEFAULT_CLAWORLD_TOOL_PROFILE;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return normalizeClaworldToolProfile(inferredToolProfile);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function resolveToolNames({ toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE } = {}) {
|
|
262
|
+
const normalizedProfile = normalizeClaworldToolProfile(toolProfile);
|
|
263
|
+
const baseProfile = TOOL_PROFILES[normalizedProfile];
|
|
264
|
+
if (!baseProfile) {
|
|
265
|
+
throw new Error(`Unsupported tool profile: ${toolProfile}`);
|
|
266
|
+
}
|
|
267
|
+
return [...baseProfile];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function resolveManagedAgentSkills({ toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE } = {}) {
|
|
271
|
+
const normalizedProfile = normalizeClaworldToolProfile(toolProfile);
|
|
272
|
+
return normalizedProfile === 'full' ? undefined : [];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function describeToolAllowEntries(toolNames = []) {
|
|
276
|
+
if (toolNames.length === 1) {
|
|
277
|
+
return toolNames[0] === '*' ? '1 entry (includes *)' : '1 entry';
|
|
278
|
+
}
|
|
279
|
+
return toolNames.includes('*')
|
|
280
|
+
? `${toolNames.length} entries (includes *)`
|
|
281
|
+
: `${toolNames.length} entries`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function buildWorkspaceAgentsContent({
|
|
285
|
+
agentId,
|
|
286
|
+
accountId,
|
|
287
|
+
registrationAgentCode,
|
|
288
|
+
appToken = null,
|
|
289
|
+
defaultToAddress = null,
|
|
290
|
+
} = {}) {
|
|
291
|
+
const canonicalCode = canonicalRelayAgentCode(registrationAgentCode, defaultToAddress);
|
|
292
|
+
const identityLine = canonicalCode
|
|
293
|
+
? `- canonical relay agentCode after bootstrap: \`${canonicalCode}\``
|
|
294
|
+
: appToken
|
|
295
|
+
? '- relay identity is resolved from the configured appToken at runtime'
|
|
296
|
+
: '- relay identity is resolved during runtime bootstrap';
|
|
297
|
+
|
|
298
|
+
return `# Claworld Channel Agent
|
|
299
|
+
|
|
300
|
+
This workspace is dedicated to the OpenClaw \`claworld\` channel.
|
|
301
|
+
|
|
302
|
+
Routing contract:
|
|
303
|
+
|
|
304
|
+
- local OpenClaw agent id: \`${agentId}\`
|
|
305
|
+
- claworld account id: \`${accountId}\`
|
|
306
|
+
${registrationAgentCode ? `- registration.agentCode: \`${registrationAgentCode}\`` : '- credential mode: appToken/manual binding'}
|
|
307
|
+
${identityLine}
|
|
308
|
+
|
|
309
|
+
Operating rules:
|
|
310
|
+
|
|
311
|
+
- keep this workspace focused on Claworld relay and world interactions
|
|
312
|
+
- use explicit \`claworld_*\` tools when they are available
|
|
313
|
+
- do not treat this workspace as the user's general-purpose main agent
|
|
314
|
+
- do not treat this as a separately bootstrapped OpenClaw persona
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function buildWorkspaceMemoryContent({
|
|
319
|
+
agentId,
|
|
320
|
+
accountId,
|
|
321
|
+
registrationAgentCode,
|
|
322
|
+
appToken = null,
|
|
323
|
+
defaultToAddress = null,
|
|
324
|
+
} = {}) {
|
|
325
|
+
const canonicalCode = canonicalRelayAgentCode(registrationAgentCode, defaultToAddress);
|
|
326
|
+
const identityLine = canonicalCode
|
|
327
|
+
? `- relay identity: \`${canonicalCode}\``
|
|
328
|
+
: appToken
|
|
329
|
+
? '- relay identity: resolved from appToken at runtime'
|
|
330
|
+
: '- relay identity: assigned during runtime bootstrap';
|
|
331
|
+
|
|
332
|
+
return `# Claworld Memory
|
|
333
|
+
|
|
334
|
+
- workspace owner: \`${agentId}\`
|
|
335
|
+
- claworld account: \`${accountId}\`
|
|
336
|
+
${identityLine}
|
|
337
|
+
|
|
338
|
+
Use this file for durable Claworld-specific notes only.
|
|
339
|
+
|
|
340
|
+
- keep world rules, pairing context, and recurring counterpart preferences here when they help future Claworld sessions
|
|
341
|
+
- do not duplicate a full standalone OpenClaw bootstrap/persona here
|
|
342
|
+
- prefer channel/world-specific memory over general-purpose assistant memory
|
|
343
|
+
`;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function resolveDefaultManagedWorkspace(agentId = DEFAULT_CLAWORLD_AGENT_ID) {
|
|
347
|
+
return `~/.openclaw/workspace-${agentId}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function resolveDefaultManagedRegistrationAgentCode(accountId = DEFAULT_CLAWORLD_ACCOUNT_ID) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function resolveDefaultManagedDisplayName(accountId = DEFAULT_CLAWORLD_ACCOUNT_ID) {
|
|
355
|
+
return `${titleCase(accountId)} Channel Agent`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function resolveStoredApprovalMode(existingAccount = {}) {
|
|
359
|
+
const existingApproval = ensureObject(existingAccount.approval);
|
|
360
|
+
if (normalizeText(existingApproval.mode, null)) {
|
|
361
|
+
return normalizeChatRequestApprovalMode(existingApproval.mode, DEFAULT_CLAWORLD_APPROVAL_MODE);
|
|
362
|
+
}
|
|
363
|
+
if (typeof existingApproval.autoAccept === 'boolean') {
|
|
364
|
+
return existingApproval.autoAccept ? 'open' : DEFAULT_CLAWORLD_APPROVAL_MODE;
|
|
365
|
+
}
|
|
366
|
+
return DEFAULT_CLAWORLD_APPROVAL_MODE;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function resolveClaworldManagedRuntimeOptions({
|
|
370
|
+
cfg = {},
|
|
371
|
+
accountId = null,
|
|
372
|
+
input = {},
|
|
373
|
+
overrides = {},
|
|
374
|
+
} = {}) {
|
|
375
|
+
const resolvedAccountId = normalizeText(accountId, DEFAULT_CLAWORLD_ACCOUNT_ID);
|
|
376
|
+
const agentId = normalizeText(overrides.agentId, resolvedAccountId);
|
|
377
|
+
const existingAgent = findAgentEntry(cfg, agentId);
|
|
378
|
+
const existingAccount = findManagedAccountEntry(cfg, resolvedAccountId);
|
|
379
|
+
const replaceManagedRuntime = overrides.replaceManagedRuntime !== false;
|
|
380
|
+
const defaultWorkspace = resolveDefaultManagedWorkspace(agentId);
|
|
381
|
+
const workspace = normalizeText(
|
|
382
|
+
overrides.workspace,
|
|
383
|
+
replaceManagedRuntime
|
|
384
|
+
? defaultWorkspace
|
|
385
|
+
: normalizeText(existingAgent?.workspace, defaultWorkspace),
|
|
386
|
+
);
|
|
387
|
+
const serverUrl = normalizeText(
|
|
388
|
+
overrides.serverUrl,
|
|
389
|
+
normalizeText(input.httpUrl, normalizeText(input.url, DEFAULT_CLAWORLD_SERVER_URL)),
|
|
390
|
+
);
|
|
391
|
+
const apiKey = normalizeText(overrides.apiKey, DEFAULT_CLAWORLD_API_KEY);
|
|
392
|
+
const explicitAppToken = normalizeText(
|
|
393
|
+
overrides.appToken,
|
|
394
|
+
normalizeText(input.appToken, null),
|
|
395
|
+
);
|
|
396
|
+
const explicitRegistrationAgentCode = normalizeRegistrationAgentCode(
|
|
397
|
+
overrides.registrationAgentCode,
|
|
398
|
+
normalizeRegistrationAgentCode(input.code, null),
|
|
399
|
+
);
|
|
400
|
+
const appToken = explicitRegistrationAgentCode && !explicitAppToken
|
|
401
|
+
? null
|
|
402
|
+
: normalizeText(
|
|
403
|
+
explicitAppToken,
|
|
404
|
+
normalizeText(
|
|
405
|
+
existingAccount.appToken,
|
|
406
|
+
normalizeText(
|
|
407
|
+
existingAccount?.relay?.appToken,
|
|
408
|
+
normalizeText(existingAccount?.relay?.credentialToken, null),
|
|
409
|
+
),
|
|
410
|
+
),
|
|
411
|
+
);
|
|
412
|
+
const displayName = normalizeText(
|
|
413
|
+
overrides.displayName,
|
|
414
|
+
normalizeText(input.name, resolveDefaultManagedDisplayName(resolvedAccountId)),
|
|
415
|
+
);
|
|
416
|
+
const name = normalizeText(overrides.name, displayName);
|
|
417
|
+
const explicitToolProfile = normalizeText(
|
|
418
|
+
overrides.toolProfile,
|
|
419
|
+
normalizeText(input.toolProfile, null),
|
|
420
|
+
);
|
|
421
|
+
const existingRegistrationAgentCode = normalizeRegistrationAgentCode(
|
|
422
|
+
existingAccount?.registration?.agentCode,
|
|
423
|
+
normalizeRegistrationAgentCode(existingAccount?.localAgent?.agentCode, null),
|
|
424
|
+
);
|
|
425
|
+
const registrationAgentCode = appToken && !explicitRegistrationAgentCode
|
|
426
|
+
? null
|
|
427
|
+
: normalizeRegistrationAgentCode(
|
|
428
|
+
explicitRegistrationAgentCode,
|
|
429
|
+
normalizeRegistrationAgentCode(
|
|
430
|
+
normalizeRegistrationAgentCode(
|
|
431
|
+
resolveDefaultManagedRegistrationAgentCode(resolvedAccountId),
|
|
432
|
+
existingRegistrationAgentCode,
|
|
433
|
+
),
|
|
434
|
+
),
|
|
435
|
+
);
|
|
436
|
+
const approvalMode = normalizeChatRequestApprovalMode(
|
|
437
|
+
normalizeText(overrides.approvalMode, null),
|
|
438
|
+
typeof overrides.autoAccept === 'boolean'
|
|
439
|
+
? (overrides.autoAccept ? 'open' : DEFAULT_CLAWORLD_APPROVAL_MODE)
|
|
440
|
+
: resolveStoredApprovalMode(existingAccount),
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
repoRoot: normalizeText(overrides.repoRoot, null),
|
|
445
|
+
agentId,
|
|
446
|
+
accountId: resolvedAccountId,
|
|
447
|
+
workspace,
|
|
448
|
+
agentDir: normalizeText(overrides.agentDir, null),
|
|
449
|
+
agentDirExplicit: overrides.agentDirExplicit === true,
|
|
450
|
+
serverUrl,
|
|
451
|
+
apiKey,
|
|
452
|
+
appToken,
|
|
453
|
+
registrationAgentCode,
|
|
454
|
+
displayName,
|
|
455
|
+
name,
|
|
456
|
+
defaultToAddress: normalizeText(overrides.defaultToAddress, null),
|
|
457
|
+
toolProfile: resolveManagedToolProfile({
|
|
458
|
+
cfg,
|
|
459
|
+
existingAccount,
|
|
460
|
+
explicitToolProfile,
|
|
461
|
+
}),
|
|
462
|
+
approvalMode,
|
|
463
|
+
sessionDmScope: normalizeText(
|
|
464
|
+
overrides.sessionDmScope,
|
|
465
|
+
DEFAULT_CLAWORLD_DM_SCOPE,
|
|
466
|
+
),
|
|
467
|
+
replaceManagedRuntime,
|
|
468
|
+
preserveDefaultAccount: overrides.preserveDefaultAccount === true,
|
|
469
|
+
forceDefaultAccount: overrides.forceDefaultAccount === true,
|
|
470
|
+
pluginInstallMode: normalizeText(overrides.pluginInstallMode, 'skip'),
|
|
471
|
+
installPlugin: overrides.installPlugin !== false,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}) {
|
|
476
|
+
const config = JSON.parse(JSON.stringify(ensureObject(inputConfig)));
|
|
477
|
+
const toolProfile = normalizeClaworldToolProfile(options.toolProfile);
|
|
478
|
+
const toolNames = resolveToolNames({ toolProfile });
|
|
479
|
+
const summary = [];
|
|
480
|
+
const replaceManagedRuntime = options.replaceManagedRuntime !== false;
|
|
481
|
+
const preserveDefaultAccount = options.preserveDefaultAccount === true;
|
|
482
|
+
const sessionDmScope = normalizeText(options.sessionDmScope, DEFAULT_CLAWORLD_DM_SCOPE);
|
|
483
|
+
|
|
484
|
+
if (!options.appToken && !normalizeText(options.registrationAgentCode, null)) {
|
|
485
|
+
throw new Error('claworld registration agentCode is required when appToken is absent');
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
config.tools = ensureObject(config.tools);
|
|
489
|
+
const managedOptionalHelperTools = new Set(CLAWORLD_OPTIONAL_WORLD_HELPER_TOOL_NAMES);
|
|
490
|
+
const existingAllow = asStringArray(config.tools.allow);
|
|
491
|
+
const removedHelperTools = existingAllow.filter((toolName) => managedOptionalHelperTools.has(toolName));
|
|
492
|
+
config.tools.allow = uniqueStrings([
|
|
493
|
+
...existingAllow.filter((toolName) => !managedOptionalHelperTools.has(toolName)),
|
|
494
|
+
...toolNames,
|
|
495
|
+
]);
|
|
496
|
+
if (removedHelperTools.length > 0) {
|
|
497
|
+
summary.push(`tools.allow removed optional helper entries (${removedHelperTools.join(',')})`);
|
|
498
|
+
}
|
|
499
|
+
summary.push(`tools.allow updated for ${toolProfile} profile (${describeToolAllowEntries(toolNames)})`);
|
|
500
|
+
|
|
501
|
+
config.session = ensureObject(config.session);
|
|
502
|
+
if (!Object.prototype.hasOwnProperty.call(config.session, 'dmScope')) {
|
|
503
|
+
config.session.dmScope = sessionDmScope;
|
|
504
|
+
summary.push(`session.dmScope set to ${sessionDmScope}`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
config.agents = ensureObject(config.agents);
|
|
508
|
+
const existingAgentList = Array.isArray(config.agents.list) ? [...config.agents.list] : [];
|
|
509
|
+
const managedAgentEntry = buildManagedAgentEntry(options);
|
|
510
|
+
if (replaceManagedRuntime) {
|
|
511
|
+
const removedAgentEntries = existingAgentList.filter((item) => ensureObject(item).id === options.agentId).length;
|
|
512
|
+
config.agents.list = [
|
|
513
|
+
...existingAgentList.filter((item) => ensureObject(item).id !== options.agentId),
|
|
514
|
+
managedAgentEntry,
|
|
515
|
+
];
|
|
516
|
+
summary.push(
|
|
517
|
+
removedAgentEntries > 0
|
|
518
|
+
? `replaced managed agent entry ${options.agentId}`
|
|
519
|
+
: `added workspace-scoped agent entry ${options.agentId}`,
|
|
520
|
+
);
|
|
521
|
+
} else {
|
|
522
|
+
const agentIndex = findAgentIndex(existingAgentList, options.agentId);
|
|
523
|
+
if (agentIndex >= 0) {
|
|
524
|
+
const existingAgent = ensureObject(existingAgentList[agentIndex]);
|
|
525
|
+
const existingAgentDir = normalizeText(existingAgent.agentDir, null);
|
|
526
|
+
const managedSkills = resolveManagedAgentSkills({ toolProfile: options.toolProfile });
|
|
527
|
+
const keepExistingAgentDir = Boolean(
|
|
528
|
+
existingAgentDir
|
|
529
|
+
&& (
|
|
530
|
+
options.agentDirExplicit
|
|
531
|
+
|| existingAgentDir !== normalizeText(options.agentDir, null)
|
|
532
|
+
),
|
|
533
|
+
);
|
|
534
|
+
const nextAgentEntry = {
|
|
535
|
+
...existingAgent,
|
|
536
|
+
id: options.agentId,
|
|
537
|
+
workspace: normalizeText(existingAgent.workspace, options.workspace),
|
|
538
|
+
...(keepExistingAgentDir ? { agentDir: existingAgentDir } : {}),
|
|
539
|
+
...(managedSkills === undefined ? {} : { skills: managedSkills }),
|
|
540
|
+
};
|
|
541
|
+
if (!keepExistingAgentDir) {
|
|
542
|
+
delete nextAgentEntry.agentDir;
|
|
543
|
+
}
|
|
544
|
+
if (managedSkills === undefined) {
|
|
545
|
+
delete nextAgentEntry.skills;
|
|
546
|
+
}
|
|
547
|
+
existingAgentList[agentIndex] = nextAgentEntry;
|
|
548
|
+
summary.push(`updated existing agent entry ${options.agentId}`);
|
|
549
|
+
} else {
|
|
550
|
+
existingAgentList.push(managedAgentEntry);
|
|
551
|
+
summary.push(`added workspace-scoped agent entry ${options.agentId}`);
|
|
552
|
+
}
|
|
553
|
+
config.agents.list = existingAgentList;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
config.channels = ensureObject(config.channels);
|
|
557
|
+
const existingClaworldRoot = ensureObject(config.channels.claworld);
|
|
558
|
+
const claworldRoot = replaceManagedRuntime ? {} : { ...existingClaworldRoot };
|
|
559
|
+
const existingDefaultAccount = normalizeText(existingClaworldRoot.defaultAccount, null);
|
|
560
|
+
const existingAccounts = ensureObject(existingClaworldRoot.accounts);
|
|
561
|
+
const targetExistingAccount = ensureObject(existingAccounts[options.accountId]);
|
|
562
|
+
const nextAccounts = { ...existingAccounts };
|
|
563
|
+
nextAccounts[options.accountId] = replaceManagedRuntime
|
|
564
|
+
? buildManagedAccountEntry(options)
|
|
565
|
+
: buildMergedAccountEntry(targetExistingAccount, options);
|
|
566
|
+
claworldRoot.accounts = nextAccounts;
|
|
567
|
+
const shouldKeepDefaultAccount = preserveDefaultAccount
|
|
568
|
+
&& existingDefaultAccount
|
|
569
|
+
&& Object.prototype.hasOwnProperty.call(nextAccounts, existingDefaultAccount);
|
|
570
|
+
if (!shouldKeepDefaultAccount || options.forceDefaultAccount) {
|
|
571
|
+
claworldRoot.defaultAccount = options.accountId;
|
|
572
|
+
summary.push(`channels.claworld.defaultAccount set to ${options.accountId}`);
|
|
573
|
+
} else {
|
|
574
|
+
claworldRoot.defaultAccount = existingDefaultAccount;
|
|
575
|
+
summary.push(`channels.claworld.defaultAccount preserved as ${existingDefaultAccount}`);
|
|
576
|
+
}
|
|
577
|
+
config.channels.claworld = claworldRoot;
|
|
578
|
+
summary.push(
|
|
579
|
+
replaceManagedRuntime
|
|
580
|
+
? `replaced managed channels.claworld.accounts.${options.accountId}`
|
|
581
|
+
: `configured channels.claworld.accounts.${options.accountId}`,
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
const existingBindings = Array.isArray(config.bindings) ? [...config.bindings] : [];
|
|
585
|
+
const remainingBindings = existingBindings.filter((binding) => {
|
|
586
|
+
const candidate = ensureObject(binding);
|
|
587
|
+
const match = ensureObject(candidate.match);
|
|
588
|
+
const bindingChannel = normalizeText(match.channel, null);
|
|
589
|
+
const bindingAccountId = normalizeText(match.accountId, null);
|
|
590
|
+
const bindingAgentId = normalizeText(candidate.agentId, null);
|
|
591
|
+
|
|
592
|
+
if (bindingChannel === 'claworld' && bindingAccountId === options.accountId) return false;
|
|
593
|
+
if (bindingChannel === 'claworld' && bindingAgentId === options.agentId) return false;
|
|
594
|
+
return true;
|
|
595
|
+
});
|
|
596
|
+
remainingBindings.push({
|
|
597
|
+
agentId: options.agentId,
|
|
598
|
+
match: {
|
|
599
|
+
channel: 'claworld',
|
|
600
|
+
accountId: options.accountId,
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
config.bindings = remainingBindings;
|
|
604
|
+
summary.push(
|
|
605
|
+
replaceManagedRuntime
|
|
606
|
+
? `replaced claworld binding for ${options.accountId}`
|
|
607
|
+
: `reconciled claworld binding for ${options.accountId}`,
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
return {
|
|
611
|
+
config,
|
|
612
|
+
toolNames,
|
|
613
|
+
summary,
|
|
614
|
+
canonicalAgentCode: canonicalRelayAgentCode(
|
|
615
|
+
options.registrationAgentCode,
|
|
616
|
+
options.defaultToAddress,
|
|
617
|
+
),
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function applyClaworldBootstrapConfig(inputConfig = {}, options = {}) {
|
|
622
|
+
const config = JSON.parse(JSON.stringify(ensureObject(inputConfig)));
|
|
623
|
+
const summary = [];
|
|
624
|
+
|
|
625
|
+
config.plugins = ensureObject(config.plugins);
|
|
626
|
+
config.plugins.allow = uniqueStrings([...asStringArray(config.plugins.allow), 'claworld']);
|
|
627
|
+
config.plugins.entries = ensureObject(config.plugins.entries);
|
|
628
|
+
config.plugins.entries.claworld = {
|
|
629
|
+
...ensureObject(config.plugins.entries.claworld),
|
|
630
|
+
enabled: true,
|
|
631
|
+
};
|
|
632
|
+
config.plugins.load = ensureObject(config.plugins.load);
|
|
633
|
+
const shouldEnsureLoadPath = options.pluginInstallMode === 'link' || options.installPlugin === false;
|
|
634
|
+
if (shouldEnsureLoadPath) {
|
|
635
|
+
config.plugins.load.paths = uniqueStrings([
|
|
636
|
+
...asStringArray(config.plugins.load.paths),
|
|
637
|
+
options.repoRoot,
|
|
638
|
+
]);
|
|
639
|
+
summary.push(`plugins.load.paths includes ${options.repoRoot}`);
|
|
640
|
+
}
|
|
641
|
+
summary.push('plugins.allow includes claworld');
|
|
642
|
+
|
|
643
|
+
const runtimeResult = applyClaworldManagedRuntimeConfig(config, options);
|
|
644
|
+
return {
|
|
645
|
+
...runtimeResult,
|
|
646
|
+
summary: [...summary, ...runtimeResult.summary],
|
|
647
|
+
};
|
|
648
|
+
}
|