expo-openclaw-chat 0.2.0 → 0.2.3
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/package.json +5 -2
- package/src/core/client.ts +42 -2
- package/src/createChat.tsx +1 -1
- package/src/ui/ChatBubble.tsx +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-openclaw-chat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Minimal chat SDK for Expo apps to connect to OpenClaw gateway",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -32,11 +32,14 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@babel/core": "^7.24.0",
|
|
34
34
|
"@babel/preset-env": "^7.24.0",
|
|
35
|
+
"@expo/vector-icons": "^15.0.3",
|
|
35
36
|
"@types/jest": "^29.5.0",
|
|
36
37
|
"@types/react": "^19.2.10",
|
|
37
38
|
"@types/react-native": "^0.72.8",
|
|
38
39
|
"babel-jest": "^29.7.0",
|
|
39
40
|
"jest": "^29.7.0",
|
|
41
|
+
"react-native-keyboard-controller": "^1.20.7",
|
|
42
|
+
"react-native-safe-area-context": "^5.6.2",
|
|
40
43
|
"ts-jest": "^29.1.0",
|
|
41
44
|
"typescript": "^5.3.0"
|
|
42
45
|
},
|
|
@@ -70,6 +73,6 @@
|
|
|
70
73
|
"homepage": "https://github.com/brunobar79/expo-openclaw-chat",
|
|
71
74
|
"repository": {
|
|
72
75
|
"type": "git",
|
|
73
|
-
"url": "https://github.com/brunobar79/expo-openclaw-chat"
|
|
76
|
+
"url": "git+https://github.com/brunobar79/expo-openclaw-chat.git"
|
|
74
77
|
}
|
|
75
78
|
}
|
package/src/core/client.ts
CHANGED
|
@@ -268,6 +268,32 @@ export class GatewayClient {
|
|
|
268
268
|
): Promise<T> {
|
|
269
269
|
return new Promise<T>((resolve, reject) => {
|
|
270
270
|
if (!this.ws || this._connectionState !== "connected") {
|
|
271
|
+
// If autoReconnect is enabled, wait up to 5s for reconnection
|
|
272
|
+
// instead of immediately rejecting. This prevents transient
|
|
273
|
+
// "Not connected" errors during WS flaps (e.g. group chat fan-out).
|
|
274
|
+
if (this.options.autoReconnect !== false && this._connectionState !== "disconnected") {
|
|
275
|
+
const waitTimeout = 5000;
|
|
276
|
+
let settled = false;
|
|
277
|
+
const unsub = this.onConnectionStateChange((state) => {
|
|
278
|
+
if (settled) return;
|
|
279
|
+
if (state === "connected") {
|
|
280
|
+
settled = true;
|
|
281
|
+
unsub();
|
|
282
|
+
this.request<T>(method, params, timeoutMs).then(resolve, reject);
|
|
283
|
+
} else if (state === "disconnected") {
|
|
284
|
+
settled = true;
|
|
285
|
+
unsub();
|
|
286
|
+
reject(new Error("Not connected"));
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
if (settled) return;
|
|
291
|
+
settled = true;
|
|
292
|
+
unsub();
|
|
293
|
+
reject(new Error("Not connected"));
|
|
294
|
+
}, waitTimeout);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
271
297
|
return reject(new Error("Not connected"));
|
|
272
298
|
}
|
|
273
299
|
|
|
@@ -699,7 +725,6 @@ export class GatewayClient {
|
|
|
699
725
|
|
|
700
726
|
if (payload.decision === "approved") {
|
|
701
727
|
// Retry connect now that we're approved
|
|
702
|
-
console.log("[GatewayClient] Device approved, retrying connect...");
|
|
703
728
|
this.sendConnectFrame();
|
|
704
729
|
} else {
|
|
705
730
|
// Rejected - fail the connection
|
|
@@ -862,7 +887,6 @@ export class GatewayClient {
|
|
|
862
887
|
|
|
863
888
|
private handleClose(code: number, reason: string): void {
|
|
864
889
|
this.ws = null;
|
|
865
|
-
this.awaitingPairing = false;
|
|
866
890
|
this.clearTickTimer();
|
|
867
891
|
|
|
868
892
|
// Reject all pending requests
|
|
@@ -873,10 +897,26 @@ export class GatewayClient {
|
|
|
873
897
|
}
|
|
874
898
|
|
|
875
899
|
if (this.intentionalClose) {
|
|
900
|
+
this.awaitingPairing = false;
|
|
876
901
|
this.setConnectionState("disconnected");
|
|
877
902
|
return;
|
|
878
903
|
}
|
|
879
904
|
|
|
905
|
+
// Gateway closed with 1008 "pairing required" — treat like NOT_PAIRED
|
|
906
|
+
if (
|
|
907
|
+
code === 1008 &&
|
|
908
|
+
reason.toLowerCase().includes("pairing") &&
|
|
909
|
+
this._connectionState === "connecting"
|
|
910
|
+
) {
|
|
911
|
+
this.awaitingPairing = true;
|
|
912
|
+
this.emitEvent("pairing.required", {
|
|
913
|
+
deviceId: this.deviceIdentity?.deviceId,
|
|
914
|
+
});
|
|
915
|
+
// Don't reject connect promise — wait for user to get approved,
|
|
916
|
+
// then the UI will retry the connection
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
|
|
880
920
|
// If we were still in the initial connect(), reject it
|
|
881
921
|
if (this.connectPromiseReject) {
|
|
882
922
|
// Don't reject — we'll try reconnecting
|
package/src/createChat.tsx
CHANGED
|
@@ -69,7 +69,7 @@ export interface ChatInstance {
|
|
|
69
69
|
export function createChat(config: CreateChatConfig): ChatInstance {
|
|
70
70
|
const {
|
|
71
71
|
gatewayUrl,
|
|
72
|
-
sessionKey = `chat-${Date.now().toString(36)}`,
|
|
72
|
+
sessionKey = `agent:main:chat-${Date.now().toString(36)}`,
|
|
73
73
|
title,
|
|
74
74
|
placeholder,
|
|
75
75
|
showImagePicker,
|
package/src/ui/ChatBubble.tsx
CHANGED
|
@@ -142,6 +142,17 @@ export const ChatBubble = React.memo(function ChatBubble({
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// Strip OpenClaw conversation metadata prefix (injected for the agent, not for display)
|
|
146
|
+
text = text.replace(
|
|
147
|
+
/^Conversation info \(untrusted metadata\):\s*```json[\s\S]*?```\s*/,
|
|
148
|
+
"",
|
|
149
|
+
);
|
|
150
|
+
// Also strip the timestamp prefix e.g. "[Fri 2026-02-13 21:33 EST] "
|
|
151
|
+
text = text.replace(
|
|
152
|
+
/^\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+\w+\]\s*/,
|
|
153
|
+
"",
|
|
154
|
+
);
|
|
155
|
+
|
|
145
156
|
return { textContent: text, images: imgs };
|
|
146
157
|
}, [message.content]);
|
|
147
158
|
|