@dhfpub/clawpool 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/index.js +171 -86
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -49,6 +49,27 @@ openclaw plugins list
|
|
|
49
49
|
openclaw plugins doctor
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
For source-level local development from this repository, use the repo debug scripts or merge this fragment into `~/.openclaw/openclaw.json` so source edits take effect after a gateway restart:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"plugins": {
|
|
57
|
+
"load": {
|
|
58
|
+
"paths": ["/path/to/repo/openclaw_plugins/clawpool/index.ts"]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If you prefer a packaged install instead of source debugging, build first and then install the plugin directory:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install
|
|
68
|
+
npm run build
|
|
69
|
+
openclaw plugins install -l /path/to/repo/openclaw_plugins/clawpool
|
|
70
|
+
openclaw plugins enable clawpool
|
|
71
|
+
```
|
|
72
|
+
|
|
52
73
|
## Configuration
|
|
53
74
|
|
|
54
75
|
### Option 1: Use the onboarding flow
|
package/dist/index.js
CHANGED
|
@@ -1171,6 +1171,93 @@ function requireActiveAibotClient(accountId) {
|
|
|
1171
1171
|
return client;
|
|
1172
1172
|
}
|
|
1173
1173
|
|
|
1174
|
+
// src/target-resolver.ts
|
|
1175
|
+
var aibotSessionIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1176
|
+
function isAibotSessionID(value) {
|
|
1177
|
+
const normalized = String(value ?? "").trim();
|
|
1178
|
+
if (!normalized) {
|
|
1179
|
+
return false;
|
|
1180
|
+
}
|
|
1181
|
+
return aibotSessionIDPattern.test(normalized);
|
|
1182
|
+
}
|
|
1183
|
+
function normalizeAibotSessionTarget2(raw) {
|
|
1184
|
+
const trimmed = String(raw ?? "").trim();
|
|
1185
|
+
if (!trimmed) {
|
|
1186
|
+
return "";
|
|
1187
|
+
}
|
|
1188
|
+
return trimmed.replace(/^clawpool:/i, "").replace(/^session:/i, "").trim();
|
|
1189
|
+
}
|
|
1190
|
+
function buildRouteSessionKeyCandidates(rawTarget, normalizedTarget) {
|
|
1191
|
+
if (rawTarget === normalizedTarget) {
|
|
1192
|
+
return [rawTarget];
|
|
1193
|
+
}
|
|
1194
|
+
return [rawTarget, normalizedTarget].filter((candidate) => candidate.length > 0);
|
|
1195
|
+
}
|
|
1196
|
+
async function resolveAibotOutboundTarget(params) {
|
|
1197
|
+
const rawTarget = String(params.to ?? "").trim();
|
|
1198
|
+
if (!rawTarget) {
|
|
1199
|
+
throw new Error("clawpool outbound target must be non-empty");
|
|
1200
|
+
}
|
|
1201
|
+
const normalizedTarget = normalizeAibotSessionTarget2(rawTarget);
|
|
1202
|
+
if (!normalizedTarget) {
|
|
1203
|
+
throw new Error("clawpool outbound target must contain session_id or route_session_key");
|
|
1204
|
+
}
|
|
1205
|
+
if (isAibotSessionID(normalizedTarget)) {
|
|
1206
|
+
return {
|
|
1207
|
+
sessionId: normalizedTarget,
|
|
1208
|
+
rawTarget,
|
|
1209
|
+
normalizedTarget,
|
|
1210
|
+
resolveSource: "direct"
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
if (/^\d+$/.test(normalizedTarget)) {
|
|
1214
|
+
throw new Error(
|
|
1215
|
+
`clawpool outbound target "${rawTarget}" is numeric; expected session_id(UUID) or route.sessionKey`
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
const routeSessionKeyCandidates = buildRouteSessionKeyCandidates(rawTarget, normalizedTarget);
|
|
1219
|
+
let lastResolveError = null;
|
|
1220
|
+
for (const routeSessionKey of routeSessionKeyCandidates) {
|
|
1221
|
+
try {
|
|
1222
|
+
const ack = await params.client.resolveSessionRoute("clawpool", params.accountId, routeSessionKey);
|
|
1223
|
+
const sessionId = String(ack.session_id ?? "").trim();
|
|
1224
|
+
if (!isAibotSessionID(sessionId)) {
|
|
1225
|
+
throw new Error(
|
|
1226
|
+
`session_route_resolve returned invalid session_id for route_session_key="${routeSessionKey}"`
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
return {
|
|
1230
|
+
sessionId,
|
|
1231
|
+
rawTarget,
|
|
1232
|
+
normalizedTarget,
|
|
1233
|
+
resolveSource: "sessionRouteMap"
|
|
1234
|
+
};
|
|
1235
|
+
} catch (err) {
|
|
1236
|
+
lastResolveError = err instanceof Error ? err : new Error(String(err));
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (lastResolveError) {
|
|
1240
|
+
throw new Error(
|
|
1241
|
+
`clawpool outbound target resolve failed target="${rawTarget}" accountId=${params.accountId}: ${lastResolveError.message}`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
throw new Error(`clawpool outbound target resolve failed target="${rawTarget}" accountId=${params.accountId}`);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// src/delete-target-resolver.ts
|
|
1248
|
+
async function resolveAibotDeleteTarget(params) {
|
|
1249
|
+
const rawTarget = String(params.sessionId ?? "").trim() || String(params.to ?? "").trim() || String(params.topic ?? "").trim() || String(params.currentChannelId ?? "").trim();
|
|
1250
|
+
if (!rawTarget) {
|
|
1251
|
+
return "";
|
|
1252
|
+
}
|
|
1253
|
+
const resolved = await resolveAibotOutboundTarget({
|
|
1254
|
+
client: params.client,
|
|
1255
|
+
accountId: params.accountId,
|
|
1256
|
+
to: rawTarget
|
|
1257
|
+
});
|
|
1258
|
+
return resolved.sessionId;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1174
1261
|
// src/actions.ts
|
|
1175
1262
|
var SUPPORTED_AIBOT_MESSAGE_ACTIONS = /* @__PURE__ */ new Set(["unsend", "delete"]);
|
|
1176
1263
|
function toSnakeCaseKey(key) {
|
|
@@ -1188,10 +1275,6 @@ function readStringishParam(params, key) {
|
|
|
1188
1275
|
}
|
|
1189
1276
|
return void 0;
|
|
1190
1277
|
}
|
|
1191
|
-
function resolveDeleteSessionId(params) {
|
|
1192
|
-
const direct = readStringParam(params.params, "sessionId") ?? readStringParam(params.params, "to") ?? params.currentChannelId;
|
|
1193
|
-
return normalizeAibotSessionTarget(direct ?? "");
|
|
1194
|
-
}
|
|
1195
1278
|
var aibotMessageActions = {
|
|
1196
1279
|
listActions: ({ cfg }) => {
|
|
1197
1280
|
const hasConfiguredAccount = listAibotAccountIds(cfg).map((accountId) => resolveAibotAccount({ cfg, accountId })).some((account) => account.enabled && account.configured);
|
|
@@ -1211,8 +1294,12 @@ var aibotMessageActions = {
|
|
|
1211
1294
|
if (!messageId) {
|
|
1212
1295
|
throw new Error("Clawpool unsend requires messageId.");
|
|
1213
1296
|
}
|
|
1214
|
-
const sessionId =
|
|
1215
|
-
|
|
1297
|
+
const sessionId = await resolveAibotDeleteTarget({
|
|
1298
|
+
client,
|
|
1299
|
+
accountId: account.accountId,
|
|
1300
|
+
sessionId: readStringishParam(params, "sessionId"),
|
|
1301
|
+
to: readStringishParam(params, "to"),
|
|
1302
|
+
topic: readStringishParam(params, "topic"),
|
|
1216
1303
|
currentChannelId: toolContext?.currentChannelId
|
|
1217
1304
|
});
|
|
1218
1305
|
if (!sessionId) {
|
|
@@ -1351,6 +1438,56 @@ function buildBodyWithQuotedReplyId(rawBody, quotedMessageId) {
|
|
|
1351
1438
|
${rawBody}`;
|
|
1352
1439
|
}
|
|
1353
1440
|
|
|
1441
|
+
// src/revoke-event.ts
|
|
1442
|
+
function toStringId(value) {
|
|
1443
|
+
return String(value ?? "").trim();
|
|
1444
|
+
}
|
|
1445
|
+
function resolveChatType(sessionType) {
|
|
1446
|
+
if (sessionType === 1) {
|
|
1447
|
+
return "direct";
|
|
1448
|
+
}
|
|
1449
|
+
if (sessionType === 2) {
|
|
1450
|
+
return "group";
|
|
1451
|
+
}
|
|
1452
|
+
throw new Error(`clawpool revoke event has unsupported session_type=${sessionType}`);
|
|
1453
|
+
}
|
|
1454
|
+
function enqueueRevokeSystemEvent(params) {
|
|
1455
|
+
const sessionId = toStringId(params.event.session_id);
|
|
1456
|
+
const messageId = toStringId(params.event.msg_id);
|
|
1457
|
+
const senderId = toStringId(params.event.sender_id);
|
|
1458
|
+
const sessionType = Number(params.event.session_type);
|
|
1459
|
+
if (!sessionId || !messageId) {
|
|
1460
|
+
throw new Error(
|
|
1461
|
+
`invalid event_revoke payload: session_id=${sessionId || "<empty>"} msg_id=${messageId || "<empty>"}`
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
const chatType = resolveChatType(sessionType);
|
|
1465
|
+
const route = params.core.channel.routing.resolveAgentRoute({
|
|
1466
|
+
cfg: params.config,
|
|
1467
|
+
channel: "clawpool",
|
|
1468
|
+
accountId: params.account.accountId,
|
|
1469
|
+
peer: {
|
|
1470
|
+
kind: chatType,
|
|
1471
|
+
id: sessionId
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
const metadataParts = [`session_id=${sessionId}`, `msg_id=${messageId}`];
|
|
1475
|
+
if (senderId) {
|
|
1476
|
+
metadataParts.push(`sender_id=${senderId}`);
|
|
1477
|
+
}
|
|
1478
|
+
const text = `Clawpool ${chatType} message deleted [${metadataParts.join(" ")}]`;
|
|
1479
|
+
params.core.system.enqueueSystemEvent(text, {
|
|
1480
|
+
sessionKey: route.sessionKey,
|
|
1481
|
+
contextKey: `clawpool:revoke:${sessionId}:${messageId}`
|
|
1482
|
+
});
|
|
1483
|
+
return {
|
|
1484
|
+
messageId,
|
|
1485
|
+
sessionId,
|
|
1486
|
+
sessionKey: route.sessionKey,
|
|
1487
|
+
text
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1354
1491
|
// src/monitor.ts
|
|
1355
1492
|
var activeMonitorClients = /* @__PURE__ */ new Map();
|
|
1356
1493
|
function registerActiveMonitor(accountId, client) {
|
|
@@ -1376,7 +1513,7 @@ function clearActiveMonitor(accountId, client) {
|
|
|
1376
1513
|
}
|
|
1377
1514
|
activeMonitorClients.delete(accountId);
|
|
1378
1515
|
}
|
|
1379
|
-
function
|
|
1516
|
+
function toStringId2(value) {
|
|
1380
1517
|
const text = String(value ?? "").trim();
|
|
1381
1518
|
return text;
|
|
1382
1519
|
}
|
|
@@ -1391,7 +1528,7 @@ function toTimestampMs(value) {
|
|
|
1391
1528
|
return Math.floor(n);
|
|
1392
1529
|
}
|
|
1393
1530
|
function normalizeNumericMessageId(value) {
|
|
1394
|
-
const raw =
|
|
1531
|
+
const raw = toStringId2(value);
|
|
1395
1532
|
if (!raw) {
|
|
1396
1533
|
return void 0;
|
|
1397
1534
|
}
|
|
@@ -1572,8 +1709,8 @@ async function bindSessionRouteMapping(params) {
|
|
|
1572
1709
|
async function processEvent(params) {
|
|
1573
1710
|
const { event, account, config, runtime: runtime2, client, statusSink } = params;
|
|
1574
1711
|
const core = getAibotRuntime();
|
|
1575
|
-
const sessionId =
|
|
1576
|
-
const messageSid =
|
|
1712
|
+
const sessionId = toStringId2(event.session_id);
|
|
1713
|
+
const messageSid = toStringId2(event.msg_id);
|
|
1577
1714
|
const rawBody = String(event.content ?? "").trim();
|
|
1578
1715
|
if (!sessionId || !messageSid || !rawBody) {
|
|
1579
1716
|
const reason = `invalid event_msg payload: session_id=${sessionId || "<empty>"} msg_id=${messageSid || "<empty>"}`;
|
|
@@ -1581,10 +1718,10 @@ async function processEvent(params) {
|
|
|
1581
1718
|
statusSink?.({ lastError: reason });
|
|
1582
1719
|
return;
|
|
1583
1720
|
}
|
|
1584
|
-
const eventId =
|
|
1721
|
+
const eventId = toStringId2(event.event_id);
|
|
1585
1722
|
const quotedMessageId = normalizeNumericMessageId(event.quoted_message_id);
|
|
1586
1723
|
const bodyForAgent = buildBodyWithQuotedReplyId(rawBody, quotedMessageId);
|
|
1587
|
-
const senderId =
|
|
1724
|
+
const senderId = toStringId2(event.sender_id);
|
|
1588
1725
|
const isGroup = Number(event.session_type ?? 0) === 2 || String(event.event_type ?? "").startsWith("group_");
|
|
1589
1726
|
const chatType = isGroup ? "group" : "direct";
|
|
1590
1727
|
const createdAt = toTimestampMs(event.created_at);
|
|
@@ -1933,6 +2070,27 @@ async function monitorAibotProvider(options) {
|
|
|
1933
2070
|
runtime2.error(`[clawpool:${account.accountId}] process event failed: ${msg}`);
|
|
1934
2071
|
guardedStatusSink({ lastError: msg });
|
|
1935
2072
|
});
|
|
2073
|
+
},
|
|
2074
|
+
onEventRevoke: (event) => {
|
|
2075
|
+
if (!isActiveMonitor(account.accountId, client)) {
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
guardedStatusSink({ lastInboundAt: Date.now() });
|
|
2079
|
+
try {
|
|
2080
|
+
const revokeEvent = enqueueRevokeSystemEvent({
|
|
2081
|
+
core: getAibotRuntime(),
|
|
2082
|
+
event,
|
|
2083
|
+
account,
|
|
2084
|
+
config
|
|
2085
|
+
});
|
|
2086
|
+
runtime2.log(
|
|
2087
|
+
`[clawpool:${account.accountId}] inbound revoke sessionId=${revokeEvent.sessionId} messageSid=${revokeEvent.messageId} routeSessionKey=${revokeEvent.sessionKey}`
|
|
2088
|
+
);
|
|
2089
|
+
} catch (err) {
|
|
2090
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2091
|
+
runtime2.error(`[clawpool:${account.accountId}] process revoke event failed: ${msg}`);
|
|
2092
|
+
guardedStatusSink({ lastError: msg });
|
|
2093
|
+
}
|
|
1936
2094
|
}
|
|
1937
2095
|
});
|
|
1938
2096
|
const previousClient = registerActiveMonitor(account.accountId, client);
|
|
@@ -2181,85 +2339,12 @@ var clawpoolOnboardingAdapter = {
|
|
|
2181
2339
|
})
|
|
2182
2340
|
};
|
|
2183
2341
|
|
|
2184
|
-
// src/target-resolver.ts
|
|
2185
|
-
var aibotSessionIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2186
|
-
function isAibotSessionID(value) {
|
|
2187
|
-
const normalized = String(value ?? "").trim();
|
|
2188
|
-
if (!normalized) {
|
|
2189
|
-
return false;
|
|
2190
|
-
}
|
|
2191
|
-
return aibotSessionIDPattern.test(normalized);
|
|
2192
|
-
}
|
|
2193
|
-
function normalizeAibotSessionTarget2(raw) {
|
|
2194
|
-
const trimmed = String(raw ?? "").trim();
|
|
2195
|
-
if (!trimmed) {
|
|
2196
|
-
return "";
|
|
2197
|
-
}
|
|
2198
|
-
return trimmed.replace(/^clawpool:/i, "").replace(/^session:/i, "").trim();
|
|
2199
|
-
}
|
|
2200
|
-
function buildRouteSessionKeyCandidates(rawTarget, normalizedTarget) {
|
|
2201
|
-
if (rawTarget === normalizedTarget) {
|
|
2202
|
-
return [rawTarget];
|
|
2203
|
-
}
|
|
2204
|
-
return [rawTarget, normalizedTarget].filter((candidate) => candidate.length > 0);
|
|
2205
|
-
}
|
|
2206
|
-
async function resolveAibotOutboundTarget(params) {
|
|
2207
|
-
const rawTarget = String(params.to ?? "").trim();
|
|
2208
|
-
if (!rawTarget) {
|
|
2209
|
-
throw new Error("clawpool outbound target must be non-empty");
|
|
2210
|
-
}
|
|
2211
|
-
const normalizedTarget = normalizeAibotSessionTarget2(rawTarget);
|
|
2212
|
-
if (!normalizedTarget) {
|
|
2213
|
-
throw new Error("clawpool outbound target must contain session_id or route_session_key");
|
|
2214
|
-
}
|
|
2215
|
-
if (isAibotSessionID(normalizedTarget)) {
|
|
2216
|
-
return {
|
|
2217
|
-
sessionId: normalizedTarget,
|
|
2218
|
-
rawTarget,
|
|
2219
|
-
normalizedTarget,
|
|
2220
|
-
resolveSource: "direct"
|
|
2221
|
-
};
|
|
2222
|
-
}
|
|
2223
|
-
if (/^\d+$/.test(normalizedTarget)) {
|
|
2224
|
-
throw new Error(
|
|
2225
|
-
`clawpool outbound target "${rawTarget}" is numeric; expected session_id(UUID) or route.sessionKey`
|
|
2226
|
-
);
|
|
2227
|
-
}
|
|
2228
|
-
const routeSessionKeyCandidates = buildRouteSessionKeyCandidates(rawTarget, normalizedTarget);
|
|
2229
|
-
let lastResolveError = null;
|
|
2230
|
-
for (const routeSessionKey of routeSessionKeyCandidates) {
|
|
2231
|
-
try {
|
|
2232
|
-
const ack = await params.client.resolveSessionRoute("clawpool", params.accountId, routeSessionKey);
|
|
2233
|
-
const sessionId = String(ack.session_id ?? "").trim();
|
|
2234
|
-
if (!isAibotSessionID(sessionId)) {
|
|
2235
|
-
throw new Error(
|
|
2236
|
-
`session_route_resolve returned invalid session_id for route_session_key="${routeSessionKey}"`
|
|
2237
|
-
);
|
|
2238
|
-
}
|
|
2239
|
-
return {
|
|
2240
|
-
sessionId,
|
|
2241
|
-
rawTarget,
|
|
2242
|
-
normalizedTarget,
|
|
2243
|
-
resolveSource: "sessionRouteMap"
|
|
2244
|
-
};
|
|
2245
|
-
} catch (err) {
|
|
2246
|
-
lastResolveError = err instanceof Error ? err : new Error(String(err));
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
if (lastResolveError) {
|
|
2250
|
-
throw new Error(
|
|
2251
|
-
`clawpool outbound target resolve failed target="${rawTarget}" accountId=${params.accountId}: ${lastResolveError.message}`
|
|
2252
|
-
);
|
|
2253
|
-
}
|
|
2254
|
-
throw new Error(`clawpool outbound target resolve failed target="${rawTarget}" accountId=${params.accountId}`);
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
2342
|
// src/channel.ts
|
|
2258
2343
|
var meta = {
|
|
2259
2344
|
id: "clawpool",
|
|
2260
2345
|
label: "Clawpool",
|
|
2261
2346
|
selectionLabel: "Clawpool",
|
|
2262
|
-
blurb: "Bridge OpenClaw to Clawpool over the
|
|
2347
|
+
blurb: "Bridge OpenClaw to Clawpool over the ClawPool Agent API WebSocket.",
|
|
2263
2348
|
aliases: ["cp", "clowpool"],
|
|
2264
2349
|
order: 90
|
|
2265
2350
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhfpub/clawpool",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "OpenClaw channel plugin for ClawPool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"id": "clawpool",
|
|
53
53
|
"label": "Clawpool",
|
|
54
54
|
"selectionLabel": "Clawpool",
|
|
55
|
-
"blurb": "Bridge OpenClaw to Clawpool over the
|
|
55
|
+
"blurb": "Bridge OpenClaw to Clawpool over the ClawPool Agent API WebSocket.",
|
|
56
56
|
"aliases": [
|
|
57
57
|
"cp",
|
|
58
58
|
"clowpool"
|