squad-openclaw 2026.2.1807 → 2026.2.1810

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 (2) hide show
  1. package/dist/index.js +86 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1041,9 +1041,9 @@ function ensureDevicePaired(keys, operatorToken) {
1041
1041
  console.log(`[relay-client] Device pairing entry created in paired.json`);
1042
1042
  return true;
1043
1043
  }
1044
- function signDeviceIdentity(keys, clientId, clientMode, role, scopes, token) {
1044
+ function signDeviceIdentity(keys, clientId, clientMode, role, scopes, token, challengeNonce) {
1045
1045
  const signedAtMs = Date.now();
1046
- const nonce = crypto2.randomBytes(16).toString("hex");
1046
+ const nonce = challengeNonce || crypto2.randomBytes(16).toString("hex");
1047
1047
  const scopeStr = scopes.join(",");
1048
1048
  const payload = `v2|${keys.deviceId}|${clientId}|${clientMode}|${role}|${scopeStr}|${signedAtMs}|${token ?? ""}|${nonce}`;
1049
1049
  const privateKey = crypto2.createPrivateKey(keys.privateKeyPem);
@@ -1078,7 +1078,7 @@ var RelayClient = class {
1078
1078
  claimToken: config.claimToken ?? state.claimToken ?? null,
1079
1079
  roomId: config.roomId ?? state.roomId ?? null
1080
1080
  };
1081
- this.pendingClaimToken = this.config.claimToken;
1081
+ this.pendingClaimToken = this.config.roomId ? null : this.config.claimToken;
1082
1082
  this.deviceKeys = loadOrCreateRelayDeviceKeys();
1083
1083
  const newEntry = ensureDevicePaired(this.deviceKeys, this.config.operatorToken);
1084
1084
  if (newEntry) {
@@ -1265,6 +1265,12 @@ var RelayClient = class {
1265
1265
  }
1266
1266
  return;
1267
1267
  }
1268
+ if (typeof msg.type === "string" && msg.type.startsWith("relay.")) {
1269
+ if (msg.type === "relay.e2e.exchange" && msg.publicKey) {
1270
+ this.handleE2EExchange(userId, msg.publicKey);
1271
+ }
1272
+ return;
1273
+ }
1268
1274
  let conn = this.userConnections.get(userId);
1269
1275
  if (!conn || conn.localWs.readyState !== NodeWebSocket.OPEN) {
1270
1276
  this.createUserConnection(userId);
@@ -1272,24 +1278,12 @@ var RelayClient = class {
1272
1278
  if (!conn) return;
1273
1279
  }
1274
1280
  if (msg.type === "req" && msg.method === "connect") {
1275
- const params = msg.params ?? {};
1276
- if (this.config.operatorToken) {
1277
- params.auth = { token: this.config.operatorToken };
1281
+ if (!conn.challengeNonce) {
1282
+ console.log(`[relay-client] Connect request for ${userId} deferred \u2014 waiting for challenge nonce`);
1283
+ conn.pendingConnect = msg;
1284
+ return;
1278
1285
  }
1279
- const client = params.client ?? {};
1280
- const role = params.role ?? "operator";
1281
- const scopes = params.scopes ?? [];
1282
- params.device = signDeviceIdentity(
1283
- this.deviceKeys,
1284
- client.id ?? "cli",
1285
- client.mode ?? "ui",
1286
- role,
1287
- scopes,
1288
- this.config.operatorToken
1289
- );
1290
- msg.params = params;
1291
- conn.connectHandshakeComplete = false;
1292
- console.log(`[relay-client] Injected device identity for user ${userId}: ${this.deviceKeys.deviceId.substring(0, 12)}...`);
1286
+ this.injectDeviceIdentity(conn, msg);
1293
1287
  }
1294
1288
  if (conn.localWs.readyState === NodeWebSocket.CONNECTING) {
1295
1289
  conn.localWs.once("open", () => {
@@ -1299,6 +1293,28 @@ var RelayClient = class {
1299
1293
  }
1300
1294
  conn.localWs.send(JSON.stringify(msg));
1301
1295
  }
1296
+ /** Inject auth token and device identity into a connect request */
1297
+ injectDeviceIdentity(conn, msg) {
1298
+ const params = msg.params ?? {};
1299
+ if (this.config.operatorToken) {
1300
+ params.auth = { token: this.config.operatorToken };
1301
+ }
1302
+ const client = params.client ?? {};
1303
+ const role = params.role ?? "operator";
1304
+ const scopes = params.scopes ?? [];
1305
+ params.device = signDeviceIdentity(
1306
+ this.deviceKeys,
1307
+ client.id ?? "cli",
1308
+ client.mode ?? "ui",
1309
+ role,
1310
+ scopes,
1311
+ this.config.operatorToken,
1312
+ conn.challengeNonce
1313
+ );
1314
+ msg.params = params;
1315
+ conn.connectHandshakeComplete = false;
1316
+ console.log(`[relay-client] Injected device identity for ${conn.userId}: nonce=${conn.challengeNonce?.substring(0, 12)}...`);
1317
+ }
1302
1318
  /** Create a local WS connection to the gateway for a specific user */
1303
1319
  createUserConnection(userId) {
1304
1320
  const existing = this.userConnections.get(userId);
@@ -1315,7 +1331,9 @@ var RelayClient = class {
1315
1331
  localWs,
1316
1332
  userId,
1317
1333
  e2e: null,
1318
- connectHandshakeComplete: false
1334
+ connectHandshakeComplete: false,
1335
+ challengeNonce: null,
1336
+ pendingConnect: null
1319
1337
  };
1320
1338
  this.userConnections.set(userId, conn);
1321
1339
  localWs.on("open", () => {
@@ -1350,6 +1368,22 @@ var RelayClient = class {
1350
1368
  const conn = this.userConnections.get(userId);
1351
1369
  if (!conn) return;
1352
1370
  const parsed = msg;
1371
+ if (parsed.type === "event" && parsed.event === "connect.challenge") {
1372
+ const payload = parsed.payload;
1373
+ if (payload?.nonce) {
1374
+ conn.challengeNonce = payload.nonce;
1375
+ console.log(`[relay-client] Captured challenge nonce for ${userId}: ${conn.challengeNonce.substring(0, 12)}...`);
1376
+ if (conn.pendingConnect) {
1377
+ const pending = conn.pendingConnect;
1378
+ conn.pendingConnect = null;
1379
+ console.log(`[relay-client] Flushing deferred connect for ${userId}`);
1380
+ this.injectDeviceIdentity(conn, pending);
1381
+ if (conn.localWs.readyState === NodeWebSocket.OPEN) {
1382
+ conn.localWs.send(JSON.stringify(pending));
1383
+ }
1384
+ }
1385
+ }
1386
+ }
1353
1387
  if (parsed.type === "res" && parsed.id === "connect-1" && parsed.ok) {
1354
1388
  conn.connectHandshakeComplete = true;
1355
1389
  }
@@ -1439,9 +1473,40 @@ function startRelayClient(api, relayUrl) {
1439
1473
 
1440
1474
  // src/index.ts
1441
1475
  function squadAppPlugin(api) {
1476
+ const toolExecutors = /* @__PURE__ */ new Map();
1477
+ const origRegisterTool = api.registerTool.bind(api);
1478
+ api.registerTool = (toolDef) => {
1479
+ if (toolDef.name && typeof toolDef.execute === "function") {
1480
+ toolExecutors.set(toolDef.name, toolDef.execute);
1481
+ }
1482
+ return origRegisterTool(toolDef);
1483
+ };
1442
1484
  registerEntityTools(api);
1443
1485
  registerFilesystemTools(api);
1444
1486
  registerVersionMethods(api);
1487
+ api.registerGatewayMethod(
1488
+ "tools.invoke",
1489
+ async ({ params, respond }) => {
1490
+ const tool = params?.tool;
1491
+ const args = params?.args ?? {};
1492
+ if (!tool) {
1493
+ respond(false, { errorMessage: "Missing 'tool' parameter" });
1494
+ return;
1495
+ }
1496
+ const executeFn = toolExecutors.get(tool);
1497
+ if (!executeFn) {
1498
+ respond(false, { errorMessage: `Unknown tool: ${tool}` });
1499
+ return;
1500
+ }
1501
+ try {
1502
+ const result = await executeFn(`ws-${Date.now()}`, args);
1503
+ respond(true, result);
1504
+ } catch (err2) {
1505
+ const msg = err2 instanceof Error ? err2.message : String(err2);
1506
+ respond(false, { errorMessage: msg });
1507
+ }
1508
+ }
1509
+ );
1445
1510
  const relayEnabled = api.pluginConfig?.["relay.enabled"] ?? true;
1446
1511
  if (relayEnabled) {
1447
1512
  const relayUrl = api.pluginConfig?.["relay.url"] || "wss://relay.squad.ceo";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squad-openclaw",
3
- "version": "2026.2.1807",
3
+ "version": "2026.2.1810",
4
4
  "description": "Entity registry, filesystem tools, and version management plugin for OpenClaw gateway",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",