@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.
Files changed (3) hide show
  1. package/README.md +21 -0
  2. package/dist/index.js +171 -86
  3. 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 = resolveDeleteSessionId({
1215
- params,
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 toStringId(value) {
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 = toStringId(value);
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 = toStringId(event.session_id);
1576
- const messageSid = toStringId(event.msg_id);
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 = toStringId(event.event_id);
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 = toStringId(event.sender_id);
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 Aibot Agent API WebSocket.",
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.0",
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 Aibot Agent API WebSocket.",
55
+ "blurb": "Bridge OpenClaw to Clawpool over the ClawPool Agent API WebSocket.",
56
56
  "aliases": [
57
57
  "cp",
58
58
  "clowpool"