openclaw-client 1.1.0 → 2.0.0

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/src/client.ts CHANGED
@@ -2,6 +2,10 @@ import type {
2
2
  AgentIdentityParams,
3
3
  AgentIdentityResult,
4
4
  AgentParams,
5
+ AgentsCreateParams,
6
+ AgentsCreateResult,
7
+ AgentsDeleteParams,
8
+ AgentsDeleteResult,
5
9
  AgentsFilesGetParams,
6
10
  AgentsFilesGetResult,
7
11
  AgentsFilesListParams,
@@ -10,6 +14,8 @@ import type {
10
14
  AgentsFilesSetResult,
11
15
  AgentsListParams,
12
16
  AgentsListResult,
17
+ AgentsUpdateParams,
18
+ AgentsUpdateResult,
13
19
  AgentWaitParams,
14
20
  ChannelsLogoutParams,
15
21
  ChannelsStatusParams,
@@ -37,6 +43,7 @@ import type {
37
43
  DevicePairApproveParams,
38
44
  DevicePairListParams,
39
45
  DevicePairRejectParams,
46
+ DevicePairRemoveParams,
40
47
  DeviceTokenRevokeParams,
41
48
  DeviceTokenRotateParams,
42
49
  EventFrame,
@@ -62,6 +69,8 @@ import type {
62
69
  NodePairVerifyParams,
63
70
  NodeRenameParams,
64
71
  PollParams,
72
+ PushTestParams,
73
+ PushTestResult,
65
74
  RequestFrame,
66
75
  ResponseFrame,
67
76
  SendParams,
@@ -72,11 +81,14 @@ import type {
72
81
  SessionsPreviewParams,
73
82
  SessionsResetParams,
74
83
  SessionsResolveParams,
84
+ SessionsUsageParams,
75
85
  SkillsBinsParams,
76
86
  SkillsBinsResult,
77
87
  SkillsInstallParams,
78
88
  SkillsStatusParams,
79
89
  SkillsUpdateParams,
90
+ TalkConfigParams,
91
+ TalkConfigResult,
80
92
  TalkModeParams,
81
93
  UpdateRunParams,
82
94
  WakeParams,
@@ -98,6 +110,14 @@ export interface OpenClawClientConfig {
98
110
  clientVersion?: string;
99
111
  platform?: string;
100
112
  mode?: ConnectParams['client']['mode'];
113
+ /** Timeout in ms for the connect handshake (default: 120000).
114
+ * Set this higher if device approval may take a while. */
115
+ connectTimeoutMs?: number;
116
+ /** Timeout in ms for regular RPC requests (default: 30000). */
117
+ requestTimeoutMs?: number;
118
+ /** Additional ConnectParams fields to merge into the handshake request
119
+ * (e.g. `device` for device identity, `caps`, `commands`). */
120
+ connectParams?: Partial<ConnectParams>;
101
121
  }
102
122
 
103
123
  export type EventListener = (event: EventFrame) => void;
@@ -130,7 +150,13 @@ export class OpenClawClient {
130
150
  }
131
151
 
132
152
  /**
133
- * Connect to the OpenClaw Gateway and perform handshake
153
+ * Connect to the OpenClaw Gateway and perform handshake.
154
+ *
155
+ * The protocol flow is:
156
+ * 1. Open WebSocket connection
157
+ * 2. Gateway sends a `connect.challenge` event with a nonce
158
+ * 3. Client responds with a `connect` request (including auth token)
159
+ * 4. Gateway replies with `hello-ok` containing server info and snapshot
134
160
  */
135
161
  async connect(): Promise<HelloOk> {
136
162
  if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
@@ -141,26 +167,49 @@ export class OpenClawClient {
141
167
  const WS = getWebSocketConstructor();
142
168
  this.ws = new WS(this.config.gatewayUrl);
143
169
 
144
- // Wait for connection to open
145
- await new Promise<void>((resolve, reject) => {
170
+ // Wait for connection to open and receive the challenge event
171
+ const challenge = await new Promise<{ nonce: string; ts: number }>((resolve, reject) => {
146
172
  if (!this.ws) {
147
173
  reject(new Error('WebSocket not initialized'));
148
174
  return;
149
175
  }
150
176
 
151
- this.ws.onopen = () => resolve();
152
- this.ws.onerror = (error) => reject(error);
177
+ const timeout = setTimeout(() => {
178
+ reject(new Error('Connection timeout: no challenge received'));
179
+ }, 15000);
180
+
181
+ this.ws.onerror = (error) => {
182
+ clearTimeout(timeout);
183
+ reject(error);
184
+ };
185
+
186
+ this.ws.onmessage = (event) => {
187
+ try {
188
+ const frame = JSON.parse(event.data);
189
+ if (frame.type === 'event' && frame.event === 'connect.challenge') {
190
+ clearTimeout(timeout);
191
+ resolve(frame.payload);
192
+ }
193
+ } catch (error) {
194
+ clearTimeout(timeout);
195
+ reject(new Error('Failed to parse challenge frame'));
196
+ }
197
+ };
198
+
199
+ this.ws.onopen = () => {
200
+ // Wait for the challenge event after connection opens
201
+ };
153
202
  });
154
203
 
155
- // Set up message handler
204
+ // Set up message handler for subsequent messages
156
205
  if (this.ws) {
157
206
  this.ws.onmessage = (event) => this.handleMessage(event);
158
207
  this.ws.onclose = () => this.handleClose();
159
208
  this.ws.onerror = (error) => this.handleError(error);
160
209
  }
161
210
 
162
- // Perform handshake
163
- const result = await this.handshake();
211
+ // Perform handshake (send connect request after receiving challenge)
212
+ const result = await this.handshake(challenge);
164
213
  this.connected = true;
165
214
  this.connectionId = result.server.connId;
166
215
  return result;
@@ -212,9 +261,10 @@ export class OpenClawClient {
212
261
  }
213
262
 
214
263
  /**
215
- * Perform the connection handshake
264
+ * Perform the connection handshake after receiving the challenge.
265
+ * Uses connectTimeoutMs (default 120s) since device approval may take a while.
216
266
  */
217
- private async handshake(): Promise<HelloOk> {
267
+ private async handshake(_challenge: { nonce: string; ts: number }): Promise<HelloOk> {
218
268
  const params: ConnectParams = {
219
269
  minProtocol: 3,
220
270
  maxProtocol: 3,
@@ -229,15 +279,17 @@ export class OpenClawClient {
229
279
  auth: {
230
280
  token: this.config.token,
231
281
  },
282
+ ...this.config.connectParams,
232
283
  };
233
284
 
234
- return this.request<HelloOk>('connect', params);
285
+ const connectTimeout = this.config.connectTimeoutMs ?? 120000;
286
+ return this.request<HelloOk>('connect', params, connectTimeout);
235
287
  }
236
288
 
237
289
  /**
238
290
  * Send a request and wait for response
239
291
  */
240
- private async request<T = any>(method: string, params?: any): Promise<T> {
292
+ private async request<T = any>(method: string, params?: any, timeoutMs?: number): Promise<T> {
241
293
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
242
294
  throw new Error('Not connected');
243
295
  }
@@ -253,11 +305,11 @@ export class OpenClawClient {
253
305
  return new Promise((resolve, reject) => {
254
306
  this.pending.set(id, { resolve, reject });
255
307
 
256
- // Set timeout for request (30 seconds)
308
+ const effectiveTimeout = timeoutMs ?? this.config.requestTimeoutMs ?? 30000;
257
309
  const timeout = setTimeout(() => {
258
310
  this.pending.delete(id);
259
311
  reject(new Error(`Request timeout: ${method}`));
260
- }, 30000);
312
+ }, effectiveTimeout);
261
313
 
262
314
  // Clear timeout when promise settles
263
315
  const originalResolve = resolve;
@@ -413,6 +465,27 @@ export class OpenClawClient {
413
465
  return this.request<AgentsListResult>('agents.list', params);
414
466
  }
415
467
 
468
+ /**
469
+ * Create agent
470
+ */
471
+ async createAgent(params: AgentsCreateParams): Promise<AgentsCreateResult> {
472
+ return this.request<AgentsCreateResult>('agents.create', params);
473
+ }
474
+
475
+ /**
476
+ * Update agent
477
+ */
478
+ async updateAgent(params: AgentsUpdateParams): Promise<AgentsUpdateResult> {
479
+ return this.request<AgentsUpdateResult>('agents.update', params);
480
+ }
481
+
482
+ /**
483
+ * Delete agent
484
+ */
485
+ async deleteAgent(params: AgentsDeleteParams): Promise<AgentsDeleteResult> {
486
+ return this.request<AgentsDeleteResult>('agents.delete', params);
487
+ }
488
+
416
489
  /**
417
490
  * Get agent identity
418
491
  */
@@ -483,6 +556,13 @@ export class OpenClawClient {
483
556
  return this.request('sessions.compact', params);
484
557
  }
485
558
 
559
+ /**
560
+ * Get session usage
561
+ */
562
+ async getSessionsUsage(params: SessionsUsageParams = {}): Promise<any> {
563
+ return this.request('sessions.usage', params);
564
+ }
565
+
486
566
  /**
487
567
  * Send a message to agent
488
568
  */
@@ -553,6 +633,13 @@ export class OpenClawClient {
553
633
  return this.request('talk.mode', params);
554
634
  }
555
635
 
636
+ /**
637
+ * Get talk config
638
+ */
639
+ async getTalkConfig(params: TalkConfigParams = {}): Promise<TalkConfigResult> {
640
+ return this.request<TalkConfigResult>('talk.config', params);
641
+ }
642
+
556
643
  /**
557
644
  * Get channels status
558
645
  */
@@ -721,6 +808,13 @@ export class OpenClawClient {
721
808
  return this.request('device.pair.reject', params);
722
809
  }
723
810
 
811
+ /**
812
+ * Remove paired device
813
+ */
814
+ async removeDevicePairing(params: DevicePairRemoveParams): Promise<any> {
815
+ return this.request('device.pair.remove', params);
816
+ }
817
+
724
818
  /**
725
819
  * Rotate device token
726
820
  */
@@ -826,6 +920,13 @@ export class OpenClawClient {
826
920
  return this.request('node.invoke', params);
827
921
  }
828
922
 
923
+ /**
924
+ * Test push notification to node
925
+ */
926
+ async testPush(params: PushTestParams): Promise<PushTestResult> {
927
+ return this.request<PushTestResult>('push.test', params);
928
+ }
929
+
829
930
  /**
830
931
  * Update and run
831
932
  */