squad-openclaw 2026.2.2203 → 2026.2.2205
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 +7 -0
- package/dist/index.js +93 -4
- 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,111 @@ function validateHumanQuestionEnvelope(text) {
|
|
|
2656
2656
|
if (!normalized) {
|
|
2657
2657
|
return { valid: false, markerFound: true, errorCode: "schema_invalid" };
|
|
2658
2658
|
}
|
|
2659
|
-
|
|
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 registerSessionMethods(api) {
|
|
2708
|
+
const callGateway = async (ctx, method, params = {}) => {
|
|
2709
|
+
const ctxRequest = ctx.request;
|
|
2710
|
+
if (typeof ctxRequest === "function") return ctxRequest(method, params);
|
|
2711
|
+
const apiRequest = api?.request;
|
|
2712
|
+
if (typeof apiRequest === "function") return apiRequest(method, params);
|
|
2713
|
+
const apiCallGatewayMethod = api?.callGatewayMethod;
|
|
2714
|
+
if (typeof apiCallGatewayMethod === "function") return apiCallGatewayMethod(method, params);
|
|
2715
|
+
throw new Error("Gateway method invocation API unavailable in plugin context");
|
|
2716
|
+
};
|
|
2717
|
+
api.registerGatewayMethod(
|
|
2718
|
+
"squad.sessions.create",
|
|
2719
|
+
async (ctx) => {
|
|
2720
|
+
const { params, respond } = ctx;
|
|
2721
|
+
const sessionKey = params?.sessionKey;
|
|
2722
|
+
const agentId = params?.agentId;
|
|
2723
|
+
if (sessionKey !== void 0) {
|
|
2724
|
+
if (typeof sessionKey !== "string" || !sessionKey.trim()) {
|
|
2725
|
+
respond(false, { error: "Invalid 'sessionKey' parameter (must be a non-empty string when provided)" });
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
if (agentId !== void 0) {
|
|
2730
|
+
if (typeof agentId !== "string" || !agentId.trim()) {
|
|
2731
|
+
respond(false, { error: "Invalid 'agentId' parameter (must be a non-empty string when provided)" });
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
const createParams = {
|
|
2736
|
+
...typeof sessionKey === "string" ? { sessionKey: sessionKey.trim() } : {},
|
|
2737
|
+
...typeof agentId === "string" ? { agentId: agentId.trim() } : {}
|
|
2738
|
+
};
|
|
2739
|
+
try {
|
|
2740
|
+
const nativeResult = await callGateway(ctx, "sessions.create", createParams);
|
|
2741
|
+
const normalizedKey = extractSessionKey(nativeResult) ?? (typeof sessionKey === "string" ? sessionKey.trim() : null);
|
|
2742
|
+
if (!normalizedKey) {
|
|
2743
|
+
respond(false, {
|
|
2744
|
+
error: "Session creation succeeded but no session key was returned",
|
|
2745
|
+
nativeResult
|
|
2746
|
+
});
|
|
2747
|
+
return;
|
|
2748
|
+
}
|
|
2749
|
+
const nativeRecord = asRecord(nativeResult);
|
|
2750
|
+
respond(true, {
|
|
2751
|
+
...nativeRecord ?? {},
|
|
2752
|
+
key: normalizedKey
|
|
2753
|
+
});
|
|
2754
|
+
} catch (err2) {
|
|
2755
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2756
|
+
respond(false, {
|
|
2757
|
+
error: `Failed to create session: ${msg}`.slice(0, 500)
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2676
2764
|
// src/shared-api.ts
|
|
2677
2765
|
var CORE_TOOLS = [
|
|
2678
2766
|
"exec",
|
|
@@ -2725,6 +2813,7 @@ function registerSquadSharedApi(api, onFsChange) {
|
|
|
2725
2813
|
registerVersionMethods(api);
|
|
2726
2814
|
registerAgentMethods(api);
|
|
2727
2815
|
registerQuestionMethods(api);
|
|
2816
|
+
registerSessionMethods(api);
|
|
2728
2817
|
const invokeTool = async (tool, args) => {
|
|
2729
2818
|
const executeFn = toolExecutors.get(tool);
|
|
2730
2819
|
if (!executeFn) {
|
|
@@ -2786,7 +2875,7 @@ function squadAppPlugin(api) {
|
|
|
2786
2875
|
const relayState = readRelayState();
|
|
2787
2876
|
const relayEnabled = !!(relayState.claimToken || relayState.roomId);
|
|
2788
2877
|
if (relayEnabled) {
|
|
2789
|
-
const relayUrl = "wss://relay.squad.ceo";
|
|
2878
|
+
const relayUrl = process.env.OPENCLAW_SQUAD_RELAY_URL || process.env.SQUAD_RELAY_URL || "wss://relay.squad.ceo";
|
|
2790
2879
|
startRelayClient(api, relayUrl);
|
|
2791
2880
|
}
|
|
2792
2881
|
}
|
package/package.json
CHANGED