squad-openclaw 2026.2.2203 → 2026.2.2206

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 +7 -0
  2. package/dist/index.js +128 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -135,6 +135,13 @@ echo '{"claimToken":"<token>"}' > ~/.openclaw/squad-ceo-data/relay/squad-relay.j
135
135
 
136
136
  The claim token is a short-lived, single-use code generated by the relay server for the authenticated user. It links this gateway to the user's Squad account. Once consumed, the relay returns a `roomId` for future reconnections, and the claim token is no longer needed.
137
137
 
138
+ For local testing, the relay endpoint can be overridden with environment variables:
139
+
140
+ - `OPENCLAW_SQUAD_RELAY_URL` (preferred)
141
+ - `SQUAD_RELAY_URL` (fallback)
142
+
143
+ If neither is set, the plugin uses the production default `wss://relay.squad.ceo`.
144
+
138
145
  ### How the Relay Connection Works
139
146
 
140
147
  ```
package/dist/index.js CHANGED
@@ -2638,7 +2638,7 @@ function normalizeEnvelope(raw) {
2638
2638
  blocking: true
2639
2639
  };
2640
2640
  }
2641
- function validateHumanQuestionEnvelope(text) {
2641
+ function validateHumanQuestionEnvelope(text, options) {
2642
2642
  const markerIndex = text.indexOf(MARKER);
2643
2643
  if (markerIndex < 0) return { valid: false, markerFound: false };
2644
2644
  const tail = text.slice(markerIndex + MARKER.length).trimStart();
@@ -2656,23 +2656,146 @@ function validateHumanQuestionEnvelope(text) {
2656
2656
  if (!normalized) {
2657
2657
  return { valid: false, markerFound: true, errorCode: "schema_invalid" };
2658
2658
  }
2659
- return { valid: true, markerFound: true, normalizedEnvelope: normalized };
2659
+ const expectedSessionKey = options?.expectedSessionKey?.trim();
2660
+ const sessionKeyMismatch = !!(expectedSessionKey && normalized.sessionKey !== expectedSessionKey);
2661
+ return {
2662
+ valid: true,
2663
+ markerFound: true,
2664
+ normalizedEnvelope: normalized,
2665
+ sessionKeyMismatch
2666
+ };
2660
2667
  }
2661
2668
  function registerQuestionMethods(api) {
2662
2669
  api.registerGatewayMethod(
2663
2670
  "squad.questions.validate-envelope",
2664
2671
  async ({ params, respond }) => {
2665
2672
  const text = typeof params?.text === "string" ? params.text : "";
2673
+ const expectedSessionKey = typeof params?.expectedSessionKey === "string" ? params.expectedSessionKey : void 0;
2666
2674
  if (!text) {
2667
2675
  respond(false, { error: "Missing 'text' parameter" });
2668
2676
  return;
2669
2677
  }
2670
- const result = validateHumanQuestionEnvelope(text);
2678
+ const result = validateHumanQuestionEnvelope(text, { expectedSessionKey });
2671
2679
  respond(true, result);
2672
2680
  }
2673
2681
  );
2674
2682
  }
2675
2683
 
2684
+ // src/sessions.ts
2685
+ function asRecord(value) {
2686
+ return value && typeof value === "object" ? value : null;
2687
+ }
2688
+ function extractSessionKey(value) {
2689
+ const record = asRecord(value);
2690
+ if (!record) return null;
2691
+ if (typeof record.key === "string" && record.key.trim()) {
2692
+ return record.key.trim();
2693
+ }
2694
+ const nestedKey = asRecord(record.key);
2695
+ if (nestedKey && typeof nestedKey.key === "string" && nestedKey.key.trim()) {
2696
+ return nestedKey.key.trim();
2697
+ }
2698
+ if (typeof record.sessionKey === "string" && record.sessionKey.trim()) {
2699
+ return record.sessionKey.trim();
2700
+ }
2701
+ const nestedSession = asRecord(record.session);
2702
+ if (nestedSession && typeof nestedSession.key === "string" && nestedSession.key.trim()) {
2703
+ return nestedSession.key.trim();
2704
+ }
2705
+ return null;
2706
+ }
2707
+ function isInvoker(fn) {
2708
+ return typeof fn === "function";
2709
+ }
2710
+ async function callGatewayAny(ctx, api, method, params) {
2711
+ const ctxGateway = asRecord(ctx.gateway);
2712
+ const apiGateway = asRecord(api?.gateway);
2713
+ const candidates = [
2714
+ ctx.request,
2715
+ ctx.callGatewayMethod,
2716
+ ctx.gatewayRequest,
2717
+ ctx.invokeGatewayMethod,
2718
+ ctxGateway?.request,
2719
+ ctxGateway?.callGatewayMethod,
2720
+ api?.request,
2721
+ api?.callGatewayMethod,
2722
+ api?.gatewayRequest,
2723
+ api?.invokeGatewayMethod,
2724
+ apiGateway?.request,
2725
+ apiGateway?.callGatewayMethod
2726
+ ];
2727
+ let lastErr = null;
2728
+ for (const candidate of candidates) {
2729
+ if (!isInvoker(candidate)) continue;
2730
+ try {
2731
+ return await candidate(method, params);
2732
+ } catch (err2) {
2733
+ lastErr = err2;
2734
+ const msg = err2 instanceof Error ? err2.message : String(err2);
2735
+ if (/unknown method|method .* unavailable|not found|invalid[_ ]request|does not exist/i.test(
2736
+ msg
2737
+ )) {
2738
+ continue;
2739
+ }
2740
+ throw err2;
2741
+ }
2742
+ }
2743
+ if (lastErr) throw lastErr;
2744
+ throw new Error("Gateway method invocation API unavailable in plugin context");
2745
+ }
2746
+ function registerSessionMethods(api) {
2747
+ api.registerGatewayMethod(
2748
+ "squad.sessions.create",
2749
+ async (ctx) => {
2750
+ const { params, respond } = ctx;
2751
+ const sessionKey = params?.sessionKey;
2752
+ const agentId = params?.agentId;
2753
+ if (sessionKey !== void 0) {
2754
+ if (typeof sessionKey !== "string" || !sessionKey.trim()) {
2755
+ respond(false, { error: "Invalid 'sessionKey' parameter (must be a non-empty string when provided)" });
2756
+ return;
2757
+ }
2758
+ }
2759
+ if (agentId !== void 0) {
2760
+ if (typeof agentId !== "string" || !agentId.trim()) {
2761
+ respond(false, { error: "Invalid 'agentId' parameter (must be a non-empty string when provided)" });
2762
+ return;
2763
+ }
2764
+ }
2765
+ const createParams = {
2766
+ ...typeof sessionKey === "string" ? { sessionKey: sessionKey.trim() } : {},
2767
+ ...typeof agentId === "string" ? { agentId: agentId.trim() } : {}
2768
+ };
2769
+ try {
2770
+ const nativeResult = await callGatewayAny(
2771
+ ctx,
2772
+ api,
2773
+ "sessions.create",
2774
+ createParams
2775
+ );
2776
+ const normalizedKey = extractSessionKey(nativeResult) ?? (typeof sessionKey === "string" ? sessionKey.trim() : null);
2777
+ if (!normalizedKey) {
2778
+ respond(false, {
2779
+ error: "Session creation succeeded but no session key was returned",
2780
+ nativeResult
2781
+ });
2782
+ return;
2783
+ }
2784
+ const nativeRecord = asRecord(nativeResult);
2785
+ respond(true, {
2786
+ ...nativeRecord ?? {},
2787
+ key: normalizedKey
2788
+ });
2789
+ } catch (err2) {
2790
+ const msg = err2 instanceof Error ? err2.message : String(err2);
2791
+ respond(false, {
2792
+ error: `Failed to create session: ${msg}`.slice(0, 500)
2793
+ });
2794
+ }
2795
+ }
2796
+ );
2797
+ }
2798
+
2676
2799
  // src/shared-api.ts
2677
2800
  var CORE_TOOLS = [
2678
2801
  "exec",
@@ -2725,6 +2848,7 @@ function registerSquadSharedApi(api, onFsChange) {
2725
2848
  registerVersionMethods(api);
2726
2849
  registerAgentMethods(api);
2727
2850
  registerQuestionMethods(api);
2851
+ registerSessionMethods(api);
2728
2852
  const invokeTool = async (tool, args) => {
2729
2853
  const executeFn = toolExecutors.get(tool);
2730
2854
  if (!executeFn) {
@@ -2786,7 +2910,7 @@ function squadAppPlugin(api) {
2786
2910
  const relayState = readRelayState();
2787
2911
  const relayEnabled = !!(relayState.claimToken || relayState.roomId);
2788
2912
  if (relayEnabled) {
2789
- const relayUrl = "wss://relay.squad.ceo";
2913
+ const relayUrl = process.env.OPENCLAW_SQUAD_RELAY_URL || process.env.SQUAD_RELAY_URL || "wss://relay.squad.ceo";
2790
2914
  startRelayClient(api, relayUrl);
2791
2915
  }
2792
2916
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squad-openclaw",
3
- "version": "2026.2.2203",
3
+ "version": "2026.2.2206",
4
4
  "description": "Entity registry, filesystem tools, and version management plugin for OpenClaw gateway",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",