@dhfpub/clawpool-openclaw-admin 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1113 @@
1
+ // index.ts
2
+ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
3
+
4
+ // src/agent-api-actions.ts
5
+ var AGENT_NAME_RE = /^[a-z][a-z0-9-]{2,31}$/;
6
+ function readRawParam(params, key) {
7
+ if (Object.hasOwn(params, key)) {
8
+ return params[key];
9
+ }
10
+ return void 0;
11
+ }
12
+ function readStringParam(params, key) {
13
+ const raw = readRawParam(params, key);
14
+ if (typeof raw === "string") {
15
+ return raw.trim();
16
+ }
17
+ if (typeof raw === "number" && Number.isFinite(raw)) {
18
+ return String(raw);
19
+ }
20
+ return "";
21
+ }
22
+ function readRequiredStringParam(params, key) {
23
+ const value = readStringParam(params, key);
24
+ if (!value) {
25
+ throw new Error(`Clawpool action requires ${key}.`);
26
+ }
27
+ return value;
28
+ }
29
+ function readArrayParam(params, key) {
30
+ const raw = readRawParam(params, key);
31
+ if (raw == null) {
32
+ return void 0;
33
+ }
34
+ if (!Array.isArray(raw)) {
35
+ throw new Error(`Clawpool action requires ${key} as array.`);
36
+ }
37
+ return raw;
38
+ }
39
+ function readNumericIDArray(params, key, required) {
40
+ const values = readArrayParam(params, key);
41
+ if (!values || values.length == 0) {
42
+ if (required) {
43
+ throw new Error(`Clawpool action requires non-empty ${key}.`);
44
+ }
45
+ return [];
46
+ }
47
+ const normalized = [];
48
+ for (const item of values) {
49
+ const value = typeof item === "string" ? item.trim() : typeof item === "number" && Number.isFinite(item) ? String(Math.trunc(item)) : "";
50
+ if (!/^\d+$/.test(value)) {
51
+ throw new Error(`Clawpool action ${key} must contain numeric IDs.`);
52
+ }
53
+ normalized.push(value);
54
+ }
55
+ return normalized;
56
+ }
57
+ function readIntArray(params, key) {
58
+ const values = readArrayParam(params, key);
59
+ if (!values || values.length == 0) {
60
+ return [];
61
+ }
62
+ const normalized = [];
63
+ for (const item of values) {
64
+ const num = typeof item === "number" ? item : Number(String(item ?? "").trim());
65
+ if (!Number.isInteger(num)) {
66
+ throw new Error(`Clawpool action ${key} must contain integers.`);
67
+ }
68
+ normalized.push(num);
69
+ }
70
+ return normalized;
71
+ }
72
+ function readOptionalInt(params, key) {
73
+ const raw = readRawParam(params, key);
74
+ if (raw == null) {
75
+ return void 0;
76
+ }
77
+ const num = typeof raw === "number" ? raw : Number(String(raw).trim());
78
+ if (!Number.isInteger(num)) {
79
+ throw new Error(`Clawpool action ${key} must be an integer.`);
80
+ }
81
+ return num;
82
+ }
83
+ function readRequiredInt(params, key) {
84
+ const value = readOptionalInt(params, key);
85
+ if (value == null) {
86
+ throw new Error(`Clawpool action requires ${key}.`);
87
+ }
88
+ return value;
89
+ }
90
+ function readOptionalBool(params, key) {
91
+ const raw = readRawParam(params, key);
92
+ if (raw == null) {
93
+ return void 0;
94
+ }
95
+ if (typeof raw === "boolean") {
96
+ return raw;
97
+ }
98
+ if (typeof raw === "number") {
99
+ if (raw === 1) return true;
100
+ if (raw === 0) return false;
101
+ }
102
+ if (typeof raw === "string") {
103
+ const normalized = raw.trim().toLowerCase();
104
+ if (normalized === "true" || normalized === "1") return true;
105
+ if (normalized === "false" || normalized === "0") return false;
106
+ }
107
+ throw new Error(`Clawpool action ${key} must be a boolean.`);
108
+ }
109
+ function readRequiredBool(params, key) {
110
+ const value = readOptionalBool(params, key);
111
+ if (value == null) {
112
+ throw new Error(`Clawpool action requires ${key}.`);
113
+ }
114
+ return value;
115
+ }
116
+ function ensureMemberTypes(types) {
117
+ for (const memberType of types) {
118
+ if (memberType !== 1 && memberType !== 2) {
119
+ throw new Error("Clawpool action member_types only supports 1 (human) or 2 (agent).");
120
+ }
121
+ }
122
+ }
123
+ function ensureMemberType(memberType) {
124
+ if (memberType !== 1) {
125
+ throw new Error("Clawpool action member_type only supports 1 for role update.");
126
+ }
127
+ }
128
+ function ensureSpeakingMemberType(memberType) {
129
+ if (memberType !== 1 && memberType !== 2) {
130
+ throw new Error("Clawpool action member_type only supports 1 (human) or 2 (agent).");
131
+ }
132
+ }
133
+ function buildGroupCreateRequest(params) {
134
+ const name = readRequiredStringParam(params, "name");
135
+ const memberIDs = readNumericIDArray(params, "memberIds", false);
136
+ const memberTypes = readIntArray(params, "memberTypes");
137
+ if (memberTypes.length > 0) {
138
+ ensureMemberTypes(memberTypes);
139
+ if (memberIDs.length == 0 || memberTypes.length !== memberIDs.length) {
140
+ throw new Error("Clawpool action memberTypes length must match memberIds.");
141
+ }
142
+ }
143
+ const body = { name };
144
+ if (memberIDs.length > 0) {
145
+ body.member_ids = memberIDs;
146
+ }
147
+ if (memberTypes.length > 0) {
148
+ body.member_types = memberTypes;
149
+ }
150
+ return {
151
+ actionName: "group_create",
152
+ method: "POST",
153
+ path: "/sessions/create_group",
154
+ body
155
+ };
156
+ }
157
+ function buildGroupMemberAddRequest(params) {
158
+ const sessionID = readRequiredStringParam(params, "sessionId");
159
+ const memberIDs = readNumericIDArray(params, "memberIds", true);
160
+ const memberTypes = readIntArray(params, "memberTypes");
161
+ if (memberTypes.length > 0) {
162
+ ensureMemberTypes(memberTypes);
163
+ if (memberTypes.length !== memberIDs.length) {
164
+ throw new Error("Clawpool action memberTypes length must match memberIds.");
165
+ }
166
+ }
167
+ const body = {
168
+ session_id: sessionID,
169
+ member_ids: memberIDs
170
+ };
171
+ if (memberTypes.length > 0) {
172
+ body.member_types = memberTypes;
173
+ }
174
+ return {
175
+ actionName: "group_member_add",
176
+ method: "POST",
177
+ path: "/sessions/members/add",
178
+ body
179
+ };
180
+ }
181
+ function buildGroupMemberRemoveRequest(params) {
182
+ const sessionID = readRequiredStringParam(params, "sessionId");
183
+ const memberIDs = readNumericIDArray(params, "memberIds", true);
184
+ const memberTypes = readIntArray(params, "memberTypes");
185
+ if (memberTypes.length > 0) {
186
+ ensureMemberTypes(memberTypes);
187
+ if (memberTypes.length !== memberIDs.length) {
188
+ throw new Error("Clawpool action memberTypes length must match memberIds.");
189
+ }
190
+ }
191
+ const body = {
192
+ session_id: sessionID,
193
+ member_ids: memberIDs
194
+ };
195
+ if (memberTypes.length > 0) {
196
+ body.member_types = memberTypes;
197
+ }
198
+ return {
199
+ actionName: "group_member_remove",
200
+ method: "POST",
201
+ path: "/sessions/members/remove",
202
+ body
203
+ };
204
+ }
205
+ function buildGroupMemberRoleUpdateRequest(params) {
206
+ const sessionID = readRequiredStringParam(params, "sessionId");
207
+ const memberID = readRequiredStringParam(params, "memberId");
208
+ if (!/^\d+$/.test(memberID)) {
209
+ throw new Error("Clawpool action memberId must be numeric.");
210
+ }
211
+ const role = readRequiredInt(params, "role");
212
+ if (role !== 1 && role !== 2) {
213
+ throw new Error("Clawpool action role only supports 1 or 2.");
214
+ }
215
+ const memberType = readOptionalInt(params, "memberType") ?? 1;
216
+ ensureMemberType(memberType);
217
+ return {
218
+ actionName: "group_member_role_update",
219
+ method: "POST",
220
+ path: "/sessions/members/role",
221
+ body: {
222
+ session_id: sessionID,
223
+ member_id: memberID,
224
+ member_type: memberType,
225
+ role
226
+ }
227
+ };
228
+ }
229
+ function buildGroupDissolveRequest(params) {
230
+ const sessionID = readRequiredStringParam(params, "sessionId");
231
+ return {
232
+ actionName: "group_dissolve",
233
+ method: "POST",
234
+ path: "/sessions/dissolve",
235
+ body: {
236
+ session_id: sessionID
237
+ }
238
+ };
239
+ }
240
+ function buildGroupAllMembersMutedUpdateRequest(params) {
241
+ const sessionID = readRequiredStringParam(params, "sessionId");
242
+ const allMembersMuted = readRequiredBool(params, "allMembersMuted");
243
+ return {
244
+ actionName: "group_all_members_muted_update",
245
+ method: "POST",
246
+ path: "/sessions/speaking/all_muted",
247
+ body: {
248
+ session_id: sessionID,
249
+ all_members_muted: allMembersMuted
250
+ }
251
+ };
252
+ }
253
+ function buildGroupMemberSpeakingUpdateRequest(params) {
254
+ const sessionID = readRequiredStringParam(params, "sessionId");
255
+ const memberID = readRequiredStringParam(params, "memberId");
256
+ if (!/^\d+$/.test(memberID)) {
257
+ throw new Error("Clawpool action memberId must be numeric.");
258
+ }
259
+ const memberType = readOptionalInt(params, "memberType") ?? 1;
260
+ ensureSpeakingMemberType(memberType);
261
+ const isSpeakMuted = readOptionalBool(params, "isSpeakMuted");
262
+ const canSpeakWhenAllMuted = readOptionalBool(params, "canSpeakWhenAllMuted");
263
+ if (isSpeakMuted == null && canSpeakWhenAllMuted == null) {
264
+ throw new Error(
265
+ "Clawpool action update_member_speaking requires isSpeakMuted or canSpeakWhenAllMuted."
266
+ );
267
+ }
268
+ const body = {
269
+ session_id: sessionID,
270
+ member_id: memberID,
271
+ member_type: memberType
272
+ };
273
+ if (isSpeakMuted != null) {
274
+ body.is_speak_muted = isSpeakMuted;
275
+ }
276
+ if (canSpeakWhenAllMuted != null) {
277
+ body.can_speak_when_all_muted = canSpeakWhenAllMuted;
278
+ }
279
+ return {
280
+ actionName: "group_member_speaking_update",
281
+ method: "POST",
282
+ path: "/sessions/members/speaking",
283
+ body
284
+ };
285
+ }
286
+ function buildGroupDetailReadRequest(params) {
287
+ const sessionID = readRequiredStringParam(params, "sessionId");
288
+ return {
289
+ actionName: "group_detail_read",
290
+ method: "GET",
291
+ path: "/sessions/group/detail",
292
+ query: {
293
+ session_id: sessionID
294
+ }
295
+ };
296
+ }
297
+ function buildContactSearchRequest(params) {
298
+ const id = readRequiredStringParam(params, "id");
299
+ const limit = readOptionalInt(params, "limit");
300
+ const offset = readOptionalInt(params, "offset");
301
+ const query = {
302
+ id
303
+ };
304
+ if (limit != null) {
305
+ query.limit = String(limit);
306
+ }
307
+ if (offset != null) {
308
+ query.offset = String(offset);
309
+ }
310
+ return {
311
+ actionName: "contact_search",
312
+ method: "GET",
313
+ path: "/contacts/search",
314
+ query
315
+ };
316
+ }
317
+ function buildSessionSearchRequest(params) {
318
+ const id = readRequiredStringParam(params, "id");
319
+ const limit = readOptionalInt(params, "limit");
320
+ const offset = readOptionalInt(params, "offset");
321
+ const query = {
322
+ id
323
+ };
324
+ if (limit != null) {
325
+ query.limit = String(limit);
326
+ }
327
+ if (offset != null) {
328
+ query.offset = String(offset);
329
+ }
330
+ return {
331
+ actionName: "session_search",
332
+ method: "GET",
333
+ path: "/sessions/search",
334
+ query
335
+ };
336
+ }
337
+ function buildMessageHistoryRequest(params) {
338
+ const sessionID = readRequiredStringParam(params, "sessionId");
339
+ const beforeID = readStringParam(params, "beforeId");
340
+ const limit = readOptionalInt(params, "limit");
341
+ const query = {
342
+ session_id: sessionID
343
+ };
344
+ if (beforeID) {
345
+ query.before_id = beforeID;
346
+ }
347
+ if (limit != null) {
348
+ query.limit = String(limit);
349
+ }
350
+ return {
351
+ actionName: "message_history",
352
+ method: "GET",
353
+ path: "/messages/history",
354
+ query
355
+ };
356
+ }
357
+ function buildAgentAPICreateRequest(params) {
358
+ const agentName = readRequiredStringParam(params, "agentName");
359
+ if (!AGENT_NAME_RE.test(agentName)) {
360
+ throw new Error("Clawpool action agentName must match ^[a-z][a-z0-9-]{2,31}$.");
361
+ }
362
+ const avatarURL = readStringParam(params, "avatarUrl");
363
+ const body = {
364
+ agent_name: agentName
365
+ };
366
+ if (avatarURL) {
367
+ body.avatar_url = avatarURL;
368
+ }
369
+ return {
370
+ actionName: "agent_api_create",
371
+ method: "POST",
372
+ path: "/agents/create",
373
+ body
374
+ };
375
+ }
376
+ function buildAgentHTTPRequest(action, params) {
377
+ switch (action) {
378
+ case "contact_search":
379
+ return buildContactSearchRequest(params);
380
+ case "session_search":
381
+ return buildSessionSearchRequest(params);
382
+ case "message_history":
383
+ return buildMessageHistoryRequest(params);
384
+ case "group_create":
385
+ return buildGroupCreateRequest(params);
386
+ case "group_member_add":
387
+ return buildGroupMemberAddRequest(params);
388
+ case "group_member_remove":
389
+ return buildGroupMemberRemoveRequest(params);
390
+ case "group_member_role_update":
391
+ return buildGroupMemberRoleUpdateRequest(params);
392
+ case "group_all_members_muted_update":
393
+ return buildGroupAllMembersMutedUpdateRequest(params);
394
+ case "group_member_speaking_update":
395
+ return buildGroupMemberSpeakingUpdateRequest(params);
396
+ case "group_dissolve":
397
+ return buildGroupDissolveRequest(params);
398
+ case "group_detail_read":
399
+ return buildGroupDetailReadRequest(params);
400
+ case "agent_api_create":
401
+ return buildAgentAPICreateRequest(params);
402
+ default:
403
+ throw new Error(`Clawpool action ${action} is not supported.`);
404
+ }
405
+ }
406
+
407
+ // src/agent-api-http.ts
408
+ var DEFAULT_HTTP_TIMEOUT_MS = 15e3;
409
+ function trimTrailingSlash(value) {
410
+ return value.replace(/\/+$/, "");
411
+ }
412
+ function resolveExplicitAgentAPIBase() {
413
+ const base = String(
414
+ process.env.CLAWPOOL_AGENT_API_BASE ?? process.env.AIBOT_AGENT_API_BASE ?? ""
415
+ ).trim();
416
+ if (!base) {
417
+ return "";
418
+ }
419
+ return trimTrailingSlash(base);
420
+ }
421
+ function deriveAgentAPIBaseFromWsUrl(wsUrl) {
422
+ const normalizedWsUrl = String(wsUrl ?? "").trim();
423
+ if (!normalizedWsUrl) {
424
+ throw new Error("Clawpool account wsUrl is missing");
425
+ }
426
+ let parsed;
427
+ try {
428
+ parsed = new URL(normalizedWsUrl);
429
+ } catch {
430
+ throw new Error(`Clawpool wsUrl is invalid: ${normalizedWsUrl}`);
431
+ }
432
+ const protocol = parsed.protocol === "wss:" ? "https:" : parsed.protocol === "ws:" ? "http:" : "";
433
+ if (!protocol) {
434
+ throw new Error(`Clawpool wsUrl must start with ws:// or wss://: ${normalizedWsUrl}`);
435
+ }
436
+ const marker = "/v1/agent-api/ws";
437
+ const markerIndex = parsed.pathname.indexOf(marker);
438
+ const basePath = markerIndex >= 0 ? parsed.pathname.slice(0, markerIndex) : parsed.pathname;
439
+ return trimTrailingSlash(`${protocol}//${parsed.host}${basePath}`) + "/v1/agent-api";
440
+ }
441
+ function deriveLocalAgentAPIBaseFromWsUrl(wsUrl) {
442
+ const normalizedWsUrl = String(wsUrl ?? "").trim();
443
+ if (!normalizedWsUrl) {
444
+ return "";
445
+ }
446
+ let parsed;
447
+ try {
448
+ parsed = new URL(normalizedWsUrl);
449
+ } catch {
450
+ return "";
451
+ }
452
+ const host = String(parsed.hostname ?? "").trim().toLowerCase();
453
+ const localHosts = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
454
+ if (!localHosts.has(host)) {
455
+ return "";
456
+ }
457
+ const wsPort = Number(parsed.port || (parsed.protocol === "wss:" ? 443 : 80));
458
+ if (!Number.isFinite(wsPort) || wsPort <= 0) {
459
+ return "";
460
+ }
461
+ const apiPort = wsPort % 10 === 9 ? wsPort - 9 : 27180;
462
+ const protocol = parsed.protocol === "wss:" ? "https:" : "http:";
463
+ return trimTrailingSlash(`${protocol}//${parsed.hostname}:${apiPort}`) + "/v1/agent-api";
464
+ }
465
+ function resolveAgentAPIBase(account) {
466
+ const explicit = resolveExplicitAgentAPIBase();
467
+ if (explicit) {
468
+ return explicit;
469
+ }
470
+ const local = deriveLocalAgentAPIBaseFromWsUrl(account.wsUrl);
471
+ if (local) {
472
+ return local;
473
+ }
474
+ return deriveAgentAPIBaseFromWsUrl(account.wsUrl);
475
+ }
476
+ function buildRequestURL(base, path, query) {
477
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
478
+ const url = new URL(`${trimTrailingSlash(base)}${normalizedPath}`);
479
+ if (query) {
480
+ for (const [key, value] of Object.entries(query)) {
481
+ const normalizedValue = String(value ?? "").trim();
482
+ if (!normalizedValue) {
483
+ continue;
484
+ }
485
+ url.searchParams.set(key, normalizedValue);
486
+ }
487
+ }
488
+ return url.toString();
489
+ }
490
+ function normalizeStatusCode(raw) {
491
+ const n = Number(raw);
492
+ if (Number.isFinite(n)) {
493
+ return Math.floor(n);
494
+ }
495
+ return 0;
496
+ }
497
+ function normalizeBizCode(raw) {
498
+ const n = Number(raw);
499
+ if (Number.isFinite(n)) {
500
+ return Math.floor(n);
501
+ }
502
+ return -1;
503
+ }
504
+ function normalizeMessage(raw) {
505
+ const message = String(raw ?? "").trim();
506
+ if (!message) {
507
+ return "unknown error";
508
+ }
509
+ return message;
510
+ }
511
+ function extractNetworkErrorMessage(error) {
512
+ if (error instanceof Error) {
513
+ return error.message || String(error);
514
+ }
515
+ return String(error);
516
+ }
517
+ async function callAgentAPI(params) {
518
+ const base = resolveAgentAPIBase(params.account);
519
+ const url = buildRequestURL(base, params.path, params.query);
520
+ const timeoutMs = Number.isFinite(params.timeoutMs) ? Math.max(1e3, Math.floor(params.timeoutMs)) : DEFAULT_HTTP_TIMEOUT_MS;
521
+ const controller = new AbortController();
522
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
523
+ let resp;
524
+ try {
525
+ resp = await fetch(url, {
526
+ method: params.method,
527
+ headers: {
528
+ Authorization: `Bearer ${params.account.apiKey}`,
529
+ ...params.method === "POST" ? { "Content-Type": "application/json" } : {}
530
+ },
531
+ body: params.method === "POST" ? JSON.stringify(params.body ?? {}) : void 0,
532
+ signal: controller.signal
533
+ });
534
+ } catch (error) {
535
+ clearTimeout(timer);
536
+ throw new Error(
537
+ `Clawpool ${params.actionName} network error: ${extractNetworkErrorMessage(error)}`
538
+ );
539
+ }
540
+ clearTimeout(timer);
541
+ const status = normalizeStatusCode(resp.status);
542
+ const rawBody = await resp.text();
543
+ let envelope;
544
+ try {
545
+ envelope = JSON.parse(rawBody);
546
+ } catch {
547
+ throw new Error(
548
+ `Clawpool ${params.actionName} invalid response: status=${status} body=${rawBody.slice(0, 256)}`
549
+ );
550
+ }
551
+ const bizCode = normalizeBizCode(envelope.code);
552
+ if (!resp.ok || bizCode !== 0) {
553
+ const message = normalizeMessage(envelope.msg);
554
+ throw new Error(
555
+ `Clawpool ${params.actionName} failed: status=${status} code=${bizCode} msg=${message}`
556
+ );
557
+ }
558
+ return envelope.data;
559
+ }
560
+
561
+ // src/accounts.ts
562
+ var DEFAULT_ACCOUNT_ID = "default";
563
+ function normalizeAccountId(value) {
564
+ const normalized = String(value ?? "").trim();
565
+ return normalized || DEFAULT_ACCOUNT_ID;
566
+ }
567
+ function normalizeOptionalAccountId(value) {
568
+ const normalized = String(value ?? "").trim();
569
+ return normalized || void 0;
570
+ }
571
+ function rawClawpoolConfig(cfg) {
572
+ return cfg.channels?.clawpool ?? {};
573
+ }
574
+ function listConfiguredAccountIds(cfg) {
575
+ const accounts = rawClawpoolConfig(cfg).accounts;
576
+ if (!accounts || typeof accounts !== "object") {
577
+ return [];
578
+ }
579
+ return Object.keys(accounts).filter(Boolean);
580
+ }
581
+ function normalizeNonEmpty(value) {
582
+ return String(value ?? "").trim();
583
+ }
584
+ function appendAgentIdToWsUrl(rawWsUrl, agentId) {
585
+ if (!rawWsUrl) {
586
+ return "";
587
+ }
588
+ const direct = rawWsUrl.replaceAll("{agent_id}", encodeURIComponent(agentId));
589
+ if (!agentId) {
590
+ return direct;
591
+ }
592
+ try {
593
+ const parsed = new URL(direct);
594
+ if (!parsed.searchParams.get("agent_id")) {
595
+ parsed.searchParams.set("agent_id", agentId);
596
+ }
597
+ return parsed.toString();
598
+ } catch {
599
+ if (direct.includes("agent_id=")) {
600
+ return direct;
601
+ }
602
+ return direct.includes("?") ? `${direct}&agent_id=${encodeURIComponent(agentId)}` : `${direct}?agent_id=${encodeURIComponent(agentId)}`;
603
+ }
604
+ }
605
+ function resolveWsUrl(merged, agentId) {
606
+ const envWs = normalizeNonEmpty(process.env.CLAWPOOL_WS_URL);
607
+ const cfgWs = normalizeNonEmpty(merged.wsUrl);
608
+ const ws = cfgWs || envWs;
609
+ if (ws) {
610
+ return appendAgentIdToWsUrl(ws, agentId);
611
+ }
612
+ if (!agentId) {
613
+ return "";
614
+ }
615
+ return `ws://127.0.0.1:27189/v1/agent-api/ws?agent_id=${encodeURIComponent(agentId)}`;
616
+ }
617
+ function resolveMergedAccountConfig(cfg, accountId) {
618
+ const clawpoolCfg = rawClawpoolConfig(cfg);
619
+ const { accounts: _ignoredAccounts, defaultAccount: _ignoredDefault, ...base } = clawpoolCfg;
620
+ const account = clawpoolCfg.accounts?.[accountId] ?? {};
621
+ return {
622
+ ...base,
623
+ ...account
624
+ };
625
+ }
626
+ function listClawpoolAccountIds(cfg) {
627
+ const ids = listConfiguredAccountIds(cfg);
628
+ if (ids.length === 0) {
629
+ return [DEFAULT_ACCOUNT_ID];
630
+ }
631
+ return ids.toSorted((a, b) => a.localeCompare(b));
632
+ }
633
+ function resolveDefaultClawpoolAccountId(cfg) {
634
+ const clawpoolCfg = rawClawpoolConfig(cfg);
635
+ const preferred = normalizeOptionalAccountId(clawpoolCfg.defaultAccount);
636
+ if (preferred && listClawpoolAccountIds(cfg).some((accountId) => normalizeAccountId(accountId) === preferred)) {
637
+ return preferred;
638
+ }
639
+ const ids = listClawpoolAccountIds(cfg);
640
+ if (ids.includes(DEFAULT_ACCOUNT_ID)) {
641
+ return DEFAULT_ACCOUNT_ID;
642
+ }
643
+ return ids[0] ?? DEFAULT_ACCOUNT_ID;
644
+ }
645
+ function resolveClawpoolAccount(params) {
646
+ const accountId = params.accountId == null || String(params.accountId).trim() === "" ? resolveDefaultClawpoolAccountId(params.cfg) : normalizeAccountId(params.accountId);
647
+ const merged = resolveMergedAccountConfig(params.cfg, accountId);
648
+ const baseEnabled = rawClawpoolConfig(params.cfg).enabled !== false;
649
+ const accountEnabled = merged.enabled !== false;
650
+ const enabled = baseEnabled && accountEnabled;
651
+ const agentId = normalizeNonEmpty(merged.agentId || process.env.CLAWPOOL_AGENT_ID);
652
+ const apiKey = normalizeNonEmpty(merged.apiKey || process.env.CLAWPOOL_API_KEY);
653
+ const wsUrl = resolveWsUrl(merged, agentId);
654
+ const configured = Boolean(wsUrl && agentId && apiKey);
655
+ return {
656
+ accountId,
657
+ name: normalizeNonEmpty(merged.name) || void 0,
658
+ enabled,
659
+ configured,
660
+ wsUrl,
661
+ agentId,
662
+ apiKey,
663
+ config: merged
664
+ };
665
+ }
666
+ function summarizeClawpoolAccounts(cfg) {
667
+ return listClawpoolAccountIds(cfg).map((accountId) => {
668
+ const account = resolveClawpoolAccount({ cfg, accountId });
669
+ return {
670
+ accountId: account.accountId,
671
+ name: account.name ?? null,
672
+ enabled: account.enabled,
673
+ configured: account.configured,
674
+ wsUrl: account.wsUrl || null,
675
+ agentId: account.agentId || null
676
+ };
677
+ });
678
+ }
679
+
680
+ // src/agent-admin-service.ts
681
+ function buildChannelBootstrapCommand(params) {
682
+ return [
683
+ "openclaw channels add",
684
+ "--channel clawpool",
685
+ `--name ${JSON.stringify(params.channelName)}`,
686
+ `--http-url ${JSON.stringify(params.apiEndpoint)}`,
687
+ `--user-id ${JSON.stringify(params.agentId)}`,
688
+ `--token ${JSON.stringify(params.apiKey)}`
689
+ ].join(" ");
690
+ }
691
+ async function createClawpoolApiAgent(params) {
692
+ const account = resolveClawpoolAccount({
693
+ cfg: params.cfg,
694
+ accountId: params.toolParams.accountId
695
+ });
696
+ if (!account.enabled) {
697
+ throw new Error(`Clawpool account "${account.accountId}" is disabled.`);
698
+ }
699
+ if (!account.configured) {
700
+ throw new Error(`Clawpool account "${account.accountId}" is not configured.`);
701
+ }
702
+ const request = buildAgentHTTPRequest("agent_api_create", params.toolParams);
703
+ const data = await callAgentAPI({
704
+ account,
705
+ actionName: request.actionName,
706
+ method: request.method,
707
+ path: request.path,
708
+ query: request.query,
709
+ body: request.body
710
+ });
711
+ const agentName = String(data.agent_name ?? params.toolParams.agentName ?? "").trim();
712
+ const apiEndpoint = String(data.api_endpoint ?? "").trim();
713
+ const agentId = String(data.id ?? "").trim();
714
+ const apiKey = String(data.api_key ?? "").trim();
715
+ return {
716
+ ok: true,
717
+ accountId: account.accountId,
718
+ action: "create_api_agent",
719
+ data,
720
+ nextSteps: agentName && apiEndpoint && agentId && apiKey ? [
721
+ "Install and enable the channel plugin if it is not installed yet: `openclaw plugins install @dhfpub/clawpool-openclaw && openclaw plugins enable clawpool`.",
722
+ `Bind the new API agent to OpenClaw with: \`${buildChannelBootstrapCommand({
723
+ channelName: `clawpool-${agentName}`,
724
+ apiEndpoint,
725
+ agentId,
726
+ apiKey
727
+ })}\``,
728
+ "Restart the gateway after adding the channel: `openclaw gateway restart`."
729
+ ] : []
730
+ };
731
+ }
732
+ function inspectClawpoolAdminConfig(cfg) {
733
+ return {
734
+ accounts: summarizeClawpoolAccounts(cfg),
735
+ defaultAccountId: resolveClawpoolAccount({ cfg }).accountId
736
+ };
737
+ }
738
+
739
+ // src/cli.ts
740
+ function registerClawpoolAdminCli(params) {
741
+ const root = params.program.command("clawpool-admin").description("Clawpool admin utilities").addHelpText(
742
+ "after",
743
+ "\nThis CLI is for operator workflows. Agent tools stay scoped to typed remote admin actions only.\n"
744
+ );
745
+ root.command("doctor").description("Show the Clawpool accounts visible from the current OpenClaw config").action(() => {
746
+ console.log(JSON.stringify(inspectClawpoolAdminConfig(params.api.config), null, 2));
747
+ });
748
+ root.command("create-agent").description("Create a Clawpool API agent and print the exact next steps for channel binding").requiredOption("--agent-name <name>", "New API agent name").option("--account-id <id>", "Configured Clawpool account id").option("--avatar-url <url>", "Optional avatar URL").action(
749
+ async (options) => {
750
+ const result = await createClawpoolApiAgent({
751
+ cfg: params.api.config,
752
+ toolParams: options
753
+ });
754
+ console.log(JSON.stringify(result, null, 2));
755
+ }
756
+ );
757
+ }
758
+
759
+ // src/json-result.ts
760
+ function jsonToolResult(payload) {
761
+ return {
762
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
763
+ details: payload
764
+ };
765
+ }
766
+
767
+ // src/agent-admin-tool.ts
768
+ var ClawpoolAgentAdminToolSchema = {
769
+ type: "object",
770
+ additionalProperties: false,
771
+ properties: {
772
+ accountId: { type: "string", minLength: 1 },
773
+ agentName: {
774
+ type: "string",
775
+ pattern: "^[a-z][a-z0-9-]{2,31}$",
776
+ description: "Lowercase API agent name."
777
+ },
778
+ avatarUrl: { type: "string", minLength: 1 }
779
+ },
780
+ required: ["agentName"]
781
+ };
782
+ function createClawpoolAgentAdminTool(api) {
783
+ return {
784
+ name: "clawpool_agent_admin",
785
+ label: "Clawpool Agent Admin",
786
+ description: "Create Clawpool API agents with typed parameters. This tool does not modify local OpenClaw channel config.",
787
+ parameters: ClawpoolAgentAdminToolSchema,
788
+ async execute(_toolCallId, params) {
789
+ try {
790
+ return jsonToolResult(
791
+ await createClawpoolApiAgent({
792
+ cfg: api.config,
793
+ toolParams: params
794
+ })
795
+ );
796
+ } catch (err) {
797
+ return jsonToolResult({
798
+ error: err instanceof Error ? err.message : String(err)
799
+ });
800
+ }
801
+ }
802
+ };
803
+ }
804
+
805
+ // src/group-service.ts
806
+ function mapGroupActionToRequestAction(action) {
807
+ switch (action) {
808
+ case "create":
809
+ return "group_create";
810
+ case "detail":
811
+ return "group_detail_read";
812
+ case "add_members":
813
+ return "group_member_add";
814
+ case "remove_members":
815
+ return "group_member_remove";
816
+ case "update_member_role":
817
+ return "group_member_role_update";
818
+ case "update_all_members_muted":
819
+ return "group_all_members_muted_update";
820
+ case "update_member_speaking":
821
+ return "group_member_speaking_update";
822
+ case "dissolve":
823
+ return "group_dissolve";
824
+ default:
825
+ action;
826
+ throw new Error(`Unsupported Clawpool group action: ${String(action)}`);
827
+ }
828
+ }
829
+ async function runClawpoolGroupAction(params) {
830
+ const account = resolveClawpoolAccount({
831
+ cfg: params.cfg,
832
+ accountId: params.toolParams.accountId
833
+ });
834
+ if (!account.enabled) {
835
+ throw new Error(`Clawpool account "${account.accountId}" is disabled.`);
836
+ }
837
+ if (!account.configured) {
838
+ throw new Error(`Clawpool account "${account.accountId}" is not configured.`);
839
+ }
840
+ const requestAction = mapGroupActionToRequestAction(params.toolParams.action);
841
+ const request = buildAgentHTTPRequest(requestAction, params.toolParams);
842
+ const data = await callAgentAPI({
843
+ account,
844
+ actionName: request.actionName,
845
+ method: request.method,
846
+ path: request.path,
847
+ query: request.query,
848
+ body: request.body
849
+ });
850
+ return {
851
+ ok: true,
852
+ accountId: account.accountId,
853
+ action: params.toolParams.action,
854
+ data
855
+ };
856
+ }
857
+
858
+ // src/group-tool.ts
859
+ var numericIdSchema = {
860
+ type: "string",
861
+ pattern: "^[0-9]+$"
862
+ };
863
+ var ClawpoolGroupToolSchema = {
864
+ oneOf: [
865
+ {
866
+ type: "object",
867
+ additionalProperties: false,
868
+ properties: {
869
+ action: { const: "create" },
870
+ accountId: { type: "string", minLength: 1 },
871
+ name: { type: "string", minLength: 1 },
872
+ memberIds: { type: "array", items: numericIdSchema },
873
+ memberTypes: { type: "array", items: { type: "integer", enum: [1, 2] } }
874
+ },
875
+ required: ["action", "name"]
876
+ },
877
+ {
878
+ type: "object",
879
+ additionalProperties: false,
880
+ properties: {
881
+ action: { const: "detail" },
882
+ accountId: { type: "string", minLength: 1 },
883
+ sessionId: { type: "string", minLength: 1 }
884
+ },
885
+ required: ["action", "sessionId"]
886
+ },
887
+ {
888
+ type: "object",
889
+ additionalProperties: false,
890
+ properties: {
891
+ action: { const: "add_members" },
892
+ accountId: { type: "string", minLength: 1 },
893
+ sessionId: { type: "string", minLength: 1 },
894
+ memberIds: { type: "array", items: numericIdSchema, minItems: 1 },
895
+ memberTypes: { type: "array", items: { type: "integer", enum: [1, 2] } }
896
+ },
897
+ required: ["action", "sessionId", "memberIds"]
898
+ },
899
+ {
900
+ type: "object",
901
+ additionalProperties: false,
902
+ properties: {
903
+ action: { const: "remove_members" },
904
+ accountId: { type: "string", minLength: 1 },
905
+ sessionId: { type: "string", minLength: 1 },
906
+ memberIds: { type: "array", items: numericIdSchema, minItems: 1 },
907
+ memberTypes: { type: "array", items: { type: "integer", enum: [1, 2] } }
908
+ },
909
+ required: ["action", "sessionId", "memberIds"]
910
+ },
911
+ {
912
+ type: "object",
913
+ additionalProperties: false,
914
+ properties: {
915
+ action: { const: "update_member_role" },
916
+ accountId: { type: "string", minLength: 1 },
917
+ sessionId: { type: "string", minLength: 1 },
918
+ memberId: numericIdSchema,
919
+ memberType: { type: "integer", enum: [1] },
920
+ role: { type: "integer", enum: [1, 2] }
921
+ },
922
+ required: ["action", "sessionId", "memberId", "role"]
923
+ },
924
+ {
925
+ type: "object",
926
+ additionalProperties: false,
927
+ properties: {
928
+ action: { const: "update_all_members_muted" },
929
+ accountId: { type: "string", minLength: 1 },
930
+ sessionId: { type: "string", minLength: 1 },
931
+ allMembersMuted: { type: "boolean" }
932
+ },
933
+ required: ["action", "sessionId", "allMembersMuted"]
934
+ },
935
+ {
936
+ type: "object",
937
+ additionalProperties: false,
938
+ properties: {
939
+ action: { const: "update_member_speaking" },
940
+ accountId: { type: "string", minLength: 1 },
941
+ sessionId: { type: "string", minLength: 1 },
942
+ memberId: numericIdSchema,
943
+ memberType: { type: "integer", enum: [1, 2] },
944
+ isSpeakMuted: { type: "boolean" },
945
+ canSpeakWhenAllMuted: { type: "boolean" }
946
+ },
947
+ required: ["action", "sessionId", "memberId"],
948
+ anyOf: [
949
+ { required: ["isSpeakMuted"] },
950
+ { required: ["canSpeakWhenAllMuted"] }
951
+ ]
952
+ },
953
+ {
954
+ type: "object",
955
+ additionalProperties: false,
956
+ properties: {
957
+ action: { const: "dissolve" },
958
+ accountId: { type: "string", minLength: 1 },
959
+ sessionId: { type: "string", minLength: 1 }
960
+ },
961
+ required: ["action", "sessionId"]
962
+ }
963
+ ]
964
+ };
965
+ function createClawpoolGroupTool(api) {
966
+ return {
967
+ name: "clawpool_group",
968
+ label: "Clawpool Group",
969
+ description: "Manage Clawpool groups through typed admin operations. This tool only handles group lifecycle and membership changes.",
970
+ parameters: ClawpoolGroupToolSchema,
971
+ async execute(_toolCallId, params) {
972
+ try {
973
+ return jsonToolResult(
974
+ await runClawpoolGroupAction({
975
+ cfg: api.config,
976
+ toolParams: params
977
+ })
978
+ );
979
+ } catch (err) {
980
+ return jsonToolResult({
981
+ error: err instanceof Error ? err.message : String(err)
982
+ });
983
+ }
984
+ }
985
+ };
986
+ }
987
+
988
+ // src/query-service.ts
989
+ function mapQueryActionToRequestAction(action) {
990
+ switch (action) {
991
+ case "contact_search":
992
+ return "contact_search";
993
+ case "session_search":
994
+ return "session_search";
995
+ case "message_history":
996
+ return "message_history";
997
+ default:
998
+ action;
999
+ throw new Error(`Unsupported Clawpool query action: ${String(action)}`);
1000
+ }
1001
+ }
1002
+ async function runClawpoolQueryAction(params) {
1003
+ const account = resolveClawpoolAccount({
1004
+ cfg: params.cfg,
1005
+ accountId: params.toolParams.accountId
1006
+ });
1007
+ if (!account.enabled) {
1008
+ throw new Error(`Clawpool account "${account.accountId}" is disabled.`);
1009
+ }
1010
+ if (!account.configured) {
1011
+ throw new Error(`Clawpool account "${account.accountId}" is not configured.`);
1012
+ }
1013
+ const requestAction = mapQueryActionToRequestAction(params.toolParams.action);
1014
+ const request = buildAgentHTTPRequest(requestAction, params.toolParams);
1015
+ const data = await callAgentAPI({
1016
+ account,
1017
+ actionName: request.actionName,
1018
+ method: request.method,
1019
+ path: request.path,
1020
+ query: request.query,
1021
+ body: request.body
1022
+ });
1023
+ return {
1024
+ ok: true,
1025
+ accountId: account.accountId,
1026
+ action: params.toolParams.action,
1027
+ data
1028
+ };
1029
+ }
1030
+
1031
+ // src/query-tool.ts
1032
+ var ClawpoolQueryToolSchema = {
1033
+ oneOf: [
1034
+ {
1035
+ type: "object",
1036
+ additionalProperties: false,
1037
+ properties: {
1038
+ action: { const: "contact_search" },
1039
+ accountId: { type: "string", minLength: 1 },
1040
+ id: { type: "string", pattern: "^[0-9]+$" },
1041
+ limit: { type: "integer", minimum: 1 },
1042
+ offset: { type: "integer", minimum: 0 }
1043
+ },
1044
+ required: ["action", "id"]
1045
+ },
1046
+ {
1047
+ type: "object",
1048
+ additionalProperties: false,
1049
+ properties: {
1050
+ action: { const: "session_search" },
1051
+ accountId: { type: "string", minLength: 1 },
1052
+ id: { type: "string", minLength: 1 },
1053
+ limit: { type: "integer", minimum: 1 },
1054
+ offset: { type: "integer", minimum: 0 }
1055
+ },
1056
+ required: ["action", "id"]
1057
+ },
1058
+ {
1059
+ type: "object",
1060
+ additionalProperties: false,
1061
+ properties: {
1062
+ action: { const: "message_history" },
1063
+ accountId: { type: "string", minLength: 1 },
1064
+ sessionId: { type: "string", minLength: 1 },
1065
+ beforeId: { type: "string", pattern: "^[0-9]+$" },
1066
+ limit: { type: "integer", minimum: 1 }
1067
+ },
1068
+ required: ["action", "sessionId"]
1069
+ }
1070
+ ]
1071
+ };
1072
+ function createClawpoolQueryTool(api) {
1073
+ return {
1074
+ name: "clawpool_query",
1075
+ label: "Clawpool Query",
1076
+ description: "Search Clawpool contacts and sessions, or read session message history through typed query operations.",
1077
+ parameters: ClawpoolQueryToolSchema,
1078
+ async execute(_toolCallId, params) {
1079
+ try {
1080
+ return jsonToolResult(
1081
+ await runClawpoolQueryAction({
1082
+ cfg: api.config,
1083
+ toolParams: params
1084
+ })
1085
+ );
1086
+ } catch (err) {
1087
+ return jsonToolResult({
1088
+ error: err instanceof Error ? err.message : String(err)
1089
+ });
1090
+ }
1091
+ }
1092
+ };
1093
+ }
1094
+
1095
+ // index.ts
1096
+ var plugin = {
1097
+ id: "clawpool-admin",
1098
+ name: "Clawpool Admin",
1099
+ description: "Typed optional admin tools and operator CLI for Clawpool",
1100
+ configSchema: emptyPluginConfigSchema(),
1101
+ register(api) {
1102
+ api.registerTool(createClawpoolQueryTool(api), { optional: true });
1103
+ api.registerTool(createClawpoolGroupTool(api), { optional: true });
1104
+ api.registerTool(createClawpoolAgentAdminTool(api), { optional: true });
1105
+ api.registerCli(({ program }) => registerClawpoolAdminCli({ api, program }), {
1106
+ commands: ["clawpool-admin"]
1107
+ });
1108
+ }
1109
+ };
1110
+ var index_default = plugin;
1111
+ export {
1112
+ index_default as default
1113
+ };