openclaw-client 1.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/README.md +127 -0
- package/dist/client.d.ts +122 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +294 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/server-client.d.ts +29 -0
- package/dist/server-client.d.ts.map +1 -0
- package/dist/server-client.js +62 -0
- package/dist/types.d.ts +1222 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/package.json +47 -0
- package/src/client.ts +379 -0
- package/src/generate-openclaw-types.ts +70 -0
- package/src/index.ts +3 -0
- package/src/protocol.schema.json +5457 -0
- package/src/server-client.ts +69 -0
- package/src/types.ts +1375 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# openclaw-client
|
|
2
|
+
|
|
3
|
+
Lightweight TypeScript client for OpenClaw Gateway WebSocket RPC.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install openclaw-client
|
|
9
|
+
# or
|
|
10
|
+
pnpm add openclaw-client
|
|
11
|
+
# or
|
|
12
|
+
yarn add openclaw-client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { OpenClawClient } from 'openclaw-client';
|
|
19
|
+
|
|
20
|
+
const client = new OpenClawClient({
|
|
21
|
+
gatewayUrl: 'ws://localhost:18789',
|
|
22
|
+
token: 'your-token',
|
|
23
|
+
clientId: 'gateway-client',
|
|
24
|
+
mode: 'ui',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await client.connect();
|
|
28
|
+
const sessions = await client.listSessions();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- ✅ **Type-safe** - Auto-generated TypeScript types from OpenClaw protocol schema
|
|
34
|
+
- ✅ **Lightweight** - Minimal dependencies, works in Node.js 18+ and browsers
|
|
35
|
+
- ✅ **Event handling** - Listen to real-time events from the Gateway
|
|
36
|
+
- ✅ **Server-friendly** - Includes utilities for server-side usage (Next.js, etc.)
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
### `OpenClawClient`
|
|
41
|
+
|
|
42
|
+
Main WebSocket client for OpenClaw Gateway.
|
|
43
|
+
|
|
44
|
+
#### Configuration
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
interface OpenClawClientConfig {
|
|
48
|
+
gatewayUrl: string; // WebSocket URL (ws:// or wss://)
|
|
49
|
+
token: string; // Authentication token
|
|
50
|
+
clientId?: string; // Client identifier (default: 'webchat-ui')
|
|
51
|
+
clientVersion?: string; // Client version (default: '1.0.0')
|
|
52
|
+
platform?: string; // Platform name (default: 'web')
|
|
53
|
+
mode?: string; // Client mode (default: 'ui')
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Methods
|
|
58
|
+
|
|
59
|
+
- `connect(): Promise<HelloOk>` - Connect and authenticate
|
|
60
|
+
- `disconnect(): void` - Disconnect from Gateway
|
|
61
|
+
- `isConnected(): boolean` - Check connection status
|
|
62
|
+
- `addEventListener(listener): () => void` - Add event listener
|
|
63
|
+
- `getConfig(params?): Promise<any>` - Get configuration
|
|
64
|
+
- `setConfig(params): Promise<any>` - Update configuration
|
|
65
|
+
- `listSessions(params?): Promise<any>` - List sessions
|
|
66
|
+
- `deleteSession(params): Promise<any>` - Delete a session
|
|
67
|
+
- `listAgents(params?): Promise<AgentsListResult>` - List agents
|
|
68
|
+
- `getAgentFile(params): Promise<AgentsFilesGetResult>` - Get agent file
|
|
69
|
+
- `listAgentFiles(params): Promise<AgentsFilesListResult>` - List agent files
|
|
70
|
+
- `setAgentFile(params): Promise<AgentsFilesSetResult>` - Update agent file
|
|
71
|
+
- `listModels(params?): Promise<ModelsListResult>` - List available models
|
|
72
|
+
- `getLogTail(params?): Promise<LogsTailResult>` - Get log tail
|
|
73
|
+
- `call<T>(method, params?): Promise<T>` - Generic RPC method call
|
|
74
|
+
|
|
75
|
+
### `ServerOpenClawClient`
|
|
76
|
+
|
|
77
|
+
Server-side client manager for connection lifecycle management.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { ServerOpenClawClient, createServerClient } from 'openclaw-client';
|
|
81
|
+
|
|
82
|
+
// Create from environment variables
|
|
83
|
+
const serverClient = createServerClient();
|
|
84
|
+
|
|
85
|
+
// Use with automatic connection management
|
|
86
|
+
export async function myAction() {
|
|
87
|
+
return serverClient.withClient(async (client) => {
|
|
88
|
+
return await client.listSessions();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Environment variables:
|
|
94
|
+
- `OPENCLAW_GATEWAY_URL` - Gateway URL (default: `http://localhost:18789`)
|
|
95
|
+
- `OPENCLAW_TOKEN` - Authentication token
|
|
96
|
+
|
|
97
|
+
## Type Generation
|
|
98
|
+
|
|
99
|
+
This package includes auto-generated types from the OpenClaw protocol schema.
|
|
100
|
+
|
|
101
|
+
To regenerate types (for development):
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm run generate:types
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The schema file is located at `src/protocol.schema.json` and is manually updated when the OpenClaw protocol changes.
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Install dependencies
|
|
113
|
+
npm install
|
|
114
|
+
|
|
115
|
+
# Generate types from schema
|
|
116
|
+
npm run generate:types
|
|
117
|
+
|
|
118
|
+
# Build the package
|
|
119
|
+
npm run build
|
|
120
|
+
|
|
121
|
+
# Publish to npm
|
|
122
|
+
npm publish
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { AgentIdentityParams, AgentIdentityResult, AgentsFilesGetParams, AgentsFilesGetResult, AgentsFilesListParams, AgentsFilesListResult, AgentsFilesSetParams, AgentsFilesSetResult, AgentsListParams, AgentsListResult, ConfigGetParams, ConfigSchemaParams, ConfigSchemaResponse, ConfigSetParams, ConnectParams, EventFrame, HelloOk, LogsTailParams, LogsTailResult, ModelsListParams, ModelsListResult, SessionsDeleteParams, SessionsListParams } from './types';
|
|
2
|
+
export interface OpenClawClientConfig {
|
|
3
|
+
gatewayUrl: string;
|
|
4
|
+
token: string;
|
|
5
|
+
clientId?: ConnectParams['client']['id'];
|
|
6
|
+
clientVersion?: string;
|
|
7
|
+
platform?: string;
|
|
8
|
+
mode?: ConnectParams['client']['mode'];
|
|
9
|
+
}
|
|
10
|
+
export type EventListener = (event: EventFrame) => void;
|
|
11
|
+
export declare class OpenClawClient {
|
|
12
|
+
private ws;
|
|
13
|
+
private config;
|
|
14
|
+
private requestId;
|
|
15
|
+
private pending;
|
|
16
|
+
private eventListeners;
|
|
17
|
+
private connected;
|
|
18
|
+
private connectionId;
|
|
19
|
+
constructor(config: OpenClawClientConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Connect to the OpenClaw Gateway and perform handshake
|
|
22
|
+
*/
|
|
23
|
+
connect(): Promise<HelloOk>;
|
|
24
|
+
/**
|
|
25
|
+
* Disconnect from the Gateway
|
|
26
|
+
*/
|
|
27
|
+
disconnect(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if connected
|
|
30
|
+
*/
|
|
31
|
+
isConnected(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Get the current connection ID
|
|
34
|
+
*/
|
|
35
|
+
getConnectionId(): string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Add an event listener
|
|
38
|
+
*/
|
|
39
|
+
addEventListener(listener: EventListener): () => void;
|
|
40
|
+
/**
|
|
41
|
+
* Perform the connection handshake
|
|
42
|
+
*/
|
|
43
|
+
private handshake;
|
|
44
|
+
/**
|
|
45
|
+
* Send a request and wait for response
|
|
46
|
+
*/
|
|
47
|
+
private request;
|
|
48
|
+
/**
|
|
49
|
+
* Handle incoming messages
|
|
50
|
+
*/
|
|
51
|
+
private handleMessage;
|
|
52
|
+
/**
|
|
53
|
+
* Handle response frame
|
|
54
|
+
*/
|
|
55
|
+
private handleResponse;
|
|
56
|
+
/**
|
|
57
|
+
* Handle event frame
|
|
58
|
+
*/
|
|
59
|
+
private handleEvent;
|
|
60
|
+
/**
|
|
61
|
+
* Handle connection close
|
|
62
|
+
*/
|
|
63
|
+
private handleClose;
|
|
64
|
+
/**
|
|
65
|
+
* Handle connection error
|
|
66
|
+
*/
|
|
67
|
+
private handleError;
|
|
68
|
+
/**
|
|
69
|
+
* Get the current configuration
|
|
70
|
+
*/
|
|
71
|
+
getConfig(params?: ConfigGetParams): Promise<any>;
|
|
72
|
+
/**
|
|
73
|
+
* Set configuration
|
|
74
|
+
*/
|
|
75
|
+
setConfig(params: ConfigSetParams): Promise<any>;
|
|
76
|
+
/**
|
|
77
|
+
* Get configuration schema
|
|
78
|
+
*/
|
|
79
|
+
getConfigSchema(params?: ConfigSchemaParams): Promise<ConfigSchemaResponse>;
|
|
80
|
+
/**
|
|
81
|
+
* List sessions
|
|
82
|
+
*/
|
|
83
|
+
listSessions(params?: SessionsListParams): Promise<any>;
|
|
84
|
+
/**
|
|
85
|
+
* Delete a session
|
|
86
|
+
*/
|
|
87
|
+
deleteSession(params: SessionsDeleteParams): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* Get agent file
|
|
90
|
+
*/
|
|
91
|
+
getAgentFile(params: AgentsFilesGetParams): Promise<AgentsFilesGetResult>;
|
|
92
|
+
/**
|
|
93
|
+
* List agent files
|
|
94
|
+
*/
|
|
95
|
+
listAgentFiles(params: AgentsFilesListParams): Promise<AgentsFilesListResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Set agent file content
|
|
98
|
+
*/
|
|
99
|
+
setAgentFile(params: AgentsFilesSetParams): Promise<AgentsFilesSetResult>;
|
|
100
|
+
/**
|
|
101
|
+
* List available agents
|
|
102
|
+
*/
|
|
103
|
+
listAgents(params?: AgentsListParams): Promise<AgentsListResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Get agent identity
|
|
106
|
+
*/
|
|
107
|
+
getAgentIdentity(params?: AgentIdentityParams): Promise<AgentIdentityResult>;
|
|
108
|
+
/**
|
|
109
|
+
* List available models
|
|
110
|
+
*/
|
|
111
|
+
listModels(params?: ModelsListParams): Promise<ModelsListResult>;
|
|
112
|
+
/**
|
|
113
|
+
* Get log tail
|
|
114
|
+
*/
|
|
115
|
+
getLogTail(params?: LogsTailParams): Promise<LogsTailResult>;
|
|
116
|
+
/**
|
|
117
|
+
* Generic call method for any RPC method
|
|
118
|
+
* Use this for methods that don't have a dedicated wrapper
|
|
119
|
+
*/
|
|
120
|
+
call<T = any>(method: string, params?: any): Promise<T>;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// Get WebSocket constructor (works in both browser and Node.js 21+)
|
|
2
|
+
const getWebSocketConstructor = () => {
|
|
3
|
+
if (typeof WebSocket !== 'undefined') {
|
|
4
|
+
return WebSocket;
|
|
5
|
+
}
|
|
6
|
+
throw new Error('WebSocket is not available in this environment');
|
|
7
|
+
};
|
|
8
|
+
export class OpenClawClient {
|
|
9
|
+
ws = null;
|
|
10
|
+
config;
|
|
11
|
+
requestId = 0;
|
|
12
|
+
pending = new Map();
|
|
13
|
+
eventListeners = [];
|
|
14
|
+
connected = false;
|
|
15
|
+
connectionId = null;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Connect to the OpenClaw Gateway and perform handshake
|
|
21
|
+
*/
|
|
22
|
+
async connect() {
|
|
23
|
+
if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
|
|
24
|
+
throw new Error('Already connected');
|
|
25
|
+
}
|
|
26
|
+
// Create WebSocket connection
|
|
27
|
+
const WS = getWebSocketConstructor();
|
|
28
|
+
this.ws = new WS(this.config.gatewayUrl);
|
|
29
|
+
// Wait for connection to open
|
|
30
|
+
await new Promise((resolve, reject) => {
|
|
31
|
+
if (!this.ws) {
|
|
32
|
+
reject(new Error('WebSocket not initialized'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.ws.onopen = () => resolve();
|
|
36
|
+
this.ws.onerror = (error) => reject(error);
|
|
37
|
+
});
|
|
38
|
+
// Set up message handler
|
|
39
|
+
if (this.ws) {
|
|
40
|
+
this.ws.onmessage = (event) => this.handleMessage(event);
|
|
41
|
+
this.ws.onclose = () => this.handleClose();
|
|
42
|
+
this.ws.onerror = (error) => this.handleError(error);
|
|
43
|
+
}
|
|
44
|
+
// Perform handshake
|
|
45
|
+
const result = await this.handshake();
|
|
46
|
+
this.connected = true;
|
|
47
|
+
this.connectionId = result.server.connId;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Disconnect from the Gateway
|
|
52
|
+
*/
|
|
53
|
+
disconnect() {
|
|
54
|
+
if (this.ws) {
|
|
55
|
+
this.ws.close();
|
|
56
|
+
this.ws = null;
|
|
57
|
+
}
|
|
58
|
+
this.connected = false;
|
|
59
|
+
this.connectionId = null;
|
|
60
|
+
// Reject all pending requests
|
|
61
|
+
for (const [id, { reject }] of this.pending.entries()) {
|
|
62
|
+
reject(new Error('Connection closed'));
|
|
63
|
+
this.pending.delete(id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if connected
|
|
68
|
+
*/
|
|
69
|
+
isConnected() {
|
|
70
|
+
return this.connected && this.ws?.readyState === WebSocket.OPEN;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the current connection ID
|
|
74
|
+
*/
|
|
75
|
+
getConnectionId() {
|
|
76
|
+
return this.connectionId;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Add an event listener
|
|
80
|
+
*/
|
|
81
|
+
addEventListener(listener) {
|
|
82
|
+
this.eventListeners.push(listener);
|
|
83
|
+
return () => {
|
|
84
|
+
const index = this.eventListeners.indexOf(listener);
|
|
85
|
+
if (index > -1) {
|
|
86
|
+
this.eventListeners.splice(index, 1);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Perform the connection handshake
|
|
92
|
+
*/
|
|
93
|
+
async handshake() {
|
|
94
|
+
const params = {
|
|
95
|
+
minProtocol: 3,
|
|
96
|
+
maxProtocol: 3,
|
|
97
|
+
client: {
|
|
98
|
+
id: this.config.clientId || 'webchat-ui',
|
|
99
|
+
version: this.config.clientVersion || '1.0.0',
|
|
100
|
+
platform: this.config.platform || 'web',
|
|
101
|
+
mode: this.config.mode || 'ui',
|
|
102
|
+
},
|
|
103
|
+
role: 'operator',
|
|
104
|
+
scopes: ['operator.read', 'operator.write', 'operator.admin'],
|
|
105
|
+
auth: {
|
|
106
|
+
token: this.config.token,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
return this.request('connect', params);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Send a request and wait for response
|
|
113
|
+
*/
|
|
114
|
+
async request(method, params) {
|
|
115
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
116
|
+
throw new Error('Not connected');
|
|
117
|
+
}
|
|
118
|
+
const id = `req-${++this.requestId}`;
|
|
119
|
+
const req = {
|
|
120
|
+
type: 'req',
|
|
121
|
+
id,
|
|
122
|
+
method,
|
|
123
|
+
params,
|
|
124
|
+
};
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
this.pending.set(id, { resolve, reject });
|
|
127
|
+
// Set timeout for request (30 seconds)
|
|
128
|
+
const timeout = setTimeout(() => {
|
|
129
|
+
this.pending.delete(id);
|
|
130
|
+
reject(new Error(`Request timeout: ${method}`));
|
|
131
|
+
}, 30000);
|
|
132
|
+
// Clear timeout when promise settles
|
|
133
|
+
const originalResolve = resolve;
|
|
134
|
+
const originalReject = reject;
|
|
135
|
+
this.pending.set(id, {
|
|
136
|
+
resolve: (payload) => {
|
|
137
|
+
clearTimeout(timeout);
|
|
138
|
+
originalResolve(payload);
|
|
139
|
+
},
|
|
140
|
+
reject: (error) => {
|
|
141
|
+
clearTimeout(timeout);
|
|
142
|
+
originalReject(error);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
this.ws.send(JSON.stringify(req));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Handle incoming messages
|
|
150
|
+
*/
|
|
151
|
+
handleMessage(event) {
|
|
152
|
+
try {
|
|
153
|
+
const frame = JSON.parse(event.data);
|
|
154
|
+
if (frame.type === 'res') {
|
|
155
|
+
this.handleResponse(frame);
|
|
156
|
+
}
|
|
157
|
+
else if (frame.type === 'event') {
|
|
158
|
+
this.handleEvent(frame);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error('Failed to parse message:', error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Handle response frame
|
|
167
|
+
*/
|
|
168
|
+
handleResponse(frame) {
|
|
169
|
+
const pending = this.pending.get(frame.id);
|
|
170
|
+
if (!pending) {
|
|
171
|
+
console.warn('Received response for unknown request:', frame.id);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.pending.delete(frame.id);
|
|
175
|
+
if (frame.ok) {
|
|
176
|
+
pending.resolve(frame.payload);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const error = new Error(frame.error?.message || 'Request failed');
|
|
180
|
+
error.code = frame.error?.code;
|
|
181
|
+
error.details = frame.error?.details;
|
|
182
|
+
error.retryable = frame.error?.retryable;
|
|
183
|
+
pending.reject(error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Handle event frame
|
|
188
|
+
*/
|
|
189
|
+
handleEvent(frame) {
|
|
190
|
+
for (const listener of this.eventListeners) {
|
|
191
|
+
try {
|
|
192
|
+
listener(frame);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.error('Event listener error:', error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Handle connection close
|
|
201
|
+
*/
|
|
202
|
+
handleClose() {
|
|
203
|
+
this.connected = false;
|
|
204
|
+
console.log('WebSocket connection closed');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Handle connection error
|
|
208
|
+
*/
|
|
209
|
+
handleError(error) {
|
|
210
|
+
console.error('WebSocket error:', error);
|
|
211
|
+
}
|
|
212
|
+
// ============================================================================
|
|
213
|
+
// Type-safe method wrappers based on schema
|
|
214
|
+
// ============================================================================
|
|
215
|
+
/**
|
|
216
|
+
* Get the current configuration
|
|
217
|
+
*/
|
|
218
|
+
async getConfig(params = {}) {
|
|
219
|
+
return this.request('config.get', params);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Set configuration
|
|
223
|
+
*/
|
|
224
|
+
async setConfig(params) {
|
|
225
|
+
return this.request('config.set', params);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get configuration schema
|
|
229
|
+
*/
|
|
230
|
+
async getConfigSchema(params = {}) {
|
|
231
|
+
return this.request('config.schema', params);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* List sessions
|
|
235
|
+
*/
|
|
236
|
+
async listSessions(params = {}) {
|
|
237
|
+
return this.request('sessions.list', params);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Delete a session
|
|
241
|
+
*/
|
|
242
|
+
async deleteSession(params) {
|
|
243
|
+
return this.request('sessions.delete', params);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get agent file
|
|
247
|
+
*/
|
|
248
|
+
async getAgentFile(params) {
|
|
249
|
+
return this.request('agents.files.get', params);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* List agent files
|
|
253
|
+
*/
|
|
254
|
+
async listAgentFiles(params) {
|
|
255
|
+
return this.request('agents.files.list', params);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Set agent file content
|
|
259
|
+
*/
|
|
260
|
+
async setAgentFile(params) {
|
|
261
|
+
return this.request('agents.files.set', params);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* List available agents
|
|
265
|
+
*/
|
|
266
|
+
async listAgents(params = {}) {
|
|
267
|
+
return this.request('agents.list', params);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get agent identity
|
|
271
|
+
*/
|
|
272
|
+
async getAgentIdentity(params = {}) {
|
|
273
|
+
return this.request('agent.identity', params);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* List available models
|
|
277
|
+
*/
|
|
278
|
+
async listModels(params = {}) {
|
|
279
|
+
return this.request('models.list', params);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Get log tail
|
|
283
|
+
*/
|
|
284
|
+
async getLogTail(params = {}) {
|
|
285
|
+
return this.request('logs.tail', params);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Generic call method for any RPC method
|
|
289
|
+
* Use this for methods that don't have a dedicated wrapper
|
|
290
|
+
*/
|
|
291
|
+
async call(method, params) {
|
|
292
|
+
return this.request(method, params);
|
|
293
|
+
}
|
|
294
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { OpenClawClient, type OpenClawClientConfig } from './client';
|
|
2
|
+
/**
|
|
3
|
+
* Server-side OpenClaw client manager
|
|
4
|
+
* Handles connection lifecycle for server actions
|
|
5
|
+
*/
|
|
6
|
+
export declare class ServerOpenClawClient {
|
|
7
|
+
private config;
|
|
8
|
+
private client;
|
|
9
|
+
constructor(config: OpenClawClientConfig);
|
|
10
|
+
/**
|
|
11
|
+
* Execute a function with a connected client
|
|
12
|
+
* Automatically handles connection and disconnection
|
|
13
|
+
*/
|
|
14
|
+
withClient<T>(fn: (client: OpenClawClient) => Promise<T>): Promise<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Get or create a persistent client connection
|
|
17
|
+
* Note: Use with caution in serverless environments
|
|
18
|
+
*/
|
|
19
|
+
getClient(): Promise<OpenClawClient>;
|
|
20
|
+
/**
|
|
21
|
+
* Disconnect the persistent client
|
|
22
|
+
*/
|
|
23
|
+
disconnect(): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a server OpenClaw client from environment variables
|
|
27
|
+
*/
|
|
28
|
+
export declare function createServerClient(): ServerOpenClawClient;
|
|
29
|
+
//# sourceMappingURL=server-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-client.d.ts","sourceRoot":"","sources":["../src/server-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAErE;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAA+B;gBAEjC,MAAM,EAAE,oBAAoB;IAIxC;;;OAGG;IACG,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW3E;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC;IAQ1C;;OAEG;IACH,UAAU,IAAI,IAAI;CAMnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,oBAAoB,CAazD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { OpenClawClient } from './client';
|
|
2
|
+
/**
|
|
3
|
+
* Server-side OpenClaw client manager
|
|
4
|
+
* Handles connection lifecycle for server actions
|
|
5
|
+
*/
|
|
6
|
+
export class ServerOpenClawClient {
|
|
7
|
+
config;
|
|
8
|
+
client = null;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Execute a function with a connected client
|
|
14
|
+
* Automatically handles connection and disconnection
|
|
15
|
+
*/
|
|
16
|
+
async withClient(fn) {
|
|
17
|
+
const client = new OpenClawClient(this.config);
|
|
18
|
+
try {
|
|
19
|
+
await client.connect();
|
|
20
|
+
return await fn(client);
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
client.disconnect();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get or create a persistent client connection
|
|
28
|
+
* Note: Use with caution in serverless environments
|
|
29
|
+
*/
|
|
30
|
+
async getClient() {
|
|
31
|
+
if (!this.client || !this.client.isConnected()) {
|
|
32
|
+
this.client = new OpenClawClient(this.config);
|
|
33
|
+
await this.client.connect();
|
|
34
|
+
}
|
|
35
|
+
return this.client;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Disconnect the persistent client
|
|
39
|
+
*/
|
|
40
|
+
disconnect() {
|
|
41
|
+
if (this.client) {
|
|
42
|
+
this.client.disconnect();
|
|
43
|
+
this.client = null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a server OpenClaw client from environment variables
|
|
49
|
+
*/
|
|
50
|
+
export function createServerClient() {
|
|
51
|
+
// Convert HTTP URL to WebSocket URL
|
|
52
|
+
const gatewayUrl = process.env.OPENCLAW_GATEWAY_URL || 'http://localhost:18789';
|
|
53
|
+
const wsUrl = gatewayUrl.replace(/^http/, 'ws');
|
|
54
|
+
return new ServerOpenClawClient({
|
|
55
|
+
gatewayUrl: wsUrl,
|
|
56
|
+
token: process.env.OPENCLAW_TOKEN || '',
|
|
57
|
+
clientId: 'gateway-client',
|
|
58
|
+
clientVersion: '1.0.0',
|
|
59
|
+
platform: 'web',
|
|
60
|
+
mode: 'ui',
|
|
61
|
+
});
|
|
62
|
+
}
|