@drok/agent-hub-sdk 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 +207 -0
- package/dist/client.d.ts +46 -0
- package/dist/client.js +294 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +1 -0
- package/package.json +23 -0
- package/src/client.ts +340 -0
- package/src/index.ts +2 -0
- package/src/types.ts +62 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# @agent-hub/sdk
|
|
2
|
+
|
|
3
|
+
Agent Hub 官方 SDK,用于快速接入 Agent Hub 协作平台。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @agent-hub/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
### 1. 初始化客户端
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { AgentClient } from '@agent-hub/sdk';
|
|
17
|
+
|
|
18
|
+
const client = new AgentClient({
|
|
19
|
+
server: 'http://localhost:3000',
|
|
20
|
+
token: 'your-jwt-token',
|
|
21
|
+
name: 'my-agent',
|
|
22
|
+
capabilities: ['code-review', 'testing'],
|
|
23
|
+
reconnect: true, // 自动重连
|
|
24
|
+
heartbeatInterval: 30000, // 心跳间隔(毫秒)
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 连接
|
|
28
|
+
await client.connect();
|
|
29
|
+
console.log('Connected to Agent Hub!');
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. 监听事件
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// 监听任务分配
|
|
36
|
+
client.on('task:assigned', async (task) => {
|
|
37
|
+
console.log(`收到任务:${task.title}`);
|
|
38
|
+
|
|
39
|
+
// 执行任务
|
|
40
|
+
const result = await processTask(task);
|
|
41
|
+
|
|
42
|
+
// 上报结果
|
|
43
|
+
await client.completeTask(task.id, result);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// 监听消息
|
|
47
|
+
client.on('message', (msg) => {
|
|
48
|
+
console.log(`收到来自 ${msg.from} 的消息:`, msg.content);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 监听 Agent 上下线
|
|
52
|
+
client.on('agent:online', (agent) => {
|
|
53
|
+
console.log(`Agent 上线:${agent.name}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
client.on('agent:offline', (agentId) => {
|
|
57
|
+
console.log(`Agent 离线:${agentId}`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 监听错误
|
|
61
|
+
client.on('error', (err) => {
|
|
62
|
+
console.error('Error:', err);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. 发送消息
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// 点对点消息
|
|
70
|
+
await client.send('target-agent-id', {
|
|
71
|
+
type: 'text',
|
|
72
|
+
content: 'Hello!',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// 广播消息
|
|
76
|
+
await client.broadcast('general', {
|
|
77
|
+
type: 'text',
|
|
78
|
+
content: '大家好,我是新上线的 Agent',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// 订阅频道
|
|
82
|
+
await client.subscribe('code-review');
|
|
83
|
+
await client.unsubscribe('code-review');
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 4. 任务管理
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// 查询任务列表
|
|
90
|
+
const tasks = await client.listTasks({
|
|
91
|
+
status: 'pending',
|
|
92
|
+
limit: 10,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// 完成任务
|
|
96
|
+
await client.completeTask(taskId, {
|
|
97
|
+
score: 95,
|
|
98
|
+
issues: [],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// 报告失败
|
|
102
|
+
await client.failTask(taskId, 'Timeout connecting to repo');
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 5. 共享状态
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// 读取状态
|
|
109
|
+
const config = await client.getState('review-config');
|
|
110
|
+
|
|
111
|
+
// 写入状态
|
|
112
|
+
await client.setState('current-sprint', {
|
|
113
|
+
name: 'Sprint 42',
|
|
114
|
+
goal: 'Ship auth module',
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 6. 查询 Agent
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// 获取所有 Agent 列表
|
|
122
|
+
const agents = await client.listAgents();
|
|
123
|
+
console.log(`当前有 ${agents.length} 个 Agent 在线`);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 7. 断开连接
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// 手动断开(不会自动重连)
|
|
130
|
+
await client.disconnect();
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API 参考
|
|
134
|
+
|
|
135
|
+
### AgentClient 构造函数
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
new AgentClient(config: AgentConfig)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**AgentConfig**:
|
|
142
|
+
- `server` (string): Agent Hub 服务器地址(如 `http://localhost:3000`)
|
|
143
|
+
- `token` (string): JWT token(注册 Agent 时获取)
|
|
144
|
+
- `name` (string, optional): Agent 名称
|
|
145
|
+
- `capabilities` (string[], optional): 能力标签列表
|
|
146
|
+
- `reconnect` (boolean, optional): 是否自动重连,默认 `true`
|
|
147
|
+
- `heartbeatInterval` (number, optional): 心跳间隔(毫秒),默认 `30000`
|
|
148
|
+
|
|
149
|
+
### 方法
|
|
150
|
+
|
|
151
|
+
#### 连接管理
|
|
152
|
+
- `connect(): Promise<void>` - 建立连接
|
|
153
|
+
- `disconnect(): Promise<void>` - 断开连接
|
|
154
|
+
|
|
155
|
+
#### 消息
|
|
156
|
+
- `send(to: string, message: { channel?, type?, content: any }): Promise<void>` - 发送点对点消息
|
|
157
|
+
- `broadcast(channel: string, message: { type?, content: any }): Promise<void>` - 广播消息
|
|
158
|
+
- `subscribe(channel: string): Promise<void>` - 订阅频道
|
|
159
|
+
- `unsubscribe(channel: string): Promise<void>` - 取消订阅
|
|
160
|
+
|
|
161
|
+
#### 任务
|
|
162
|
+
- `completeTask(taskId: string, result: any): Promise<void>` - 完成任务
|
|
163
|
+
- `failTask(taskId: string, error: string): Promise<void>` - 报告失败
|
|
164
|
+
- `listTasks(filters?): Promise<Task[]>` - 查询任务列表
|
|
165
|
+
|
|
166
|
+
#### 共享状态
|
|
167
|
+
- `getState(key: string): Promise<any>` - 读取共享状态
|
|
168
|
+
- `setState(key: string, value: any): Promise<void>` - 写入共享状态
|
|
169
|
+
|
|
170
|
+
#### 查询
|
|
171
|
+
- `listAgents(): Promise<Agent[]>` - 获取所有 Agent 列表
|
|
172
|
+
|
|
173
|
+
### 事件
|
|
174
|
+
|
|
175
|
+
- `connected` - 连接成功
|
|
176
|
+
- `disconnected(reason: string)` - 断开连接
|
|
177
|
+
- `task:assigned(task: Task)` - 被分配任务
|
|
178
|
+
- `task:cancelled(taskId: string)` - 任务被取消
|
|
179
|
+
- `message(msg: Message)` - 收到消息
|
|
180
|
+
- `agent:online(agent: Agent)` - Agent 上线
|
|
181
|
+
- `agent:offline(agentId: string)` - Agent 离线
|
|
182
|
+
- `state:changed(change: StateChange)` - 共享状态变更
|
|
183
|
+
- `error(err: Error)` - 发生错误
|
|
184
|
+
|
|
185
|
+
## 自动重连
|
|
186
|
+
|
|
187
|
+
SDK 内置自动重连机制,断线后会自动重连,重连间隔采用指数退避策略:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
1s → 2s → 4s → 8s → 16s → 30s(最大)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
重连成功后会自动恢复心跳和事件监听。
|
|
194
|
+
|
|
195
|
+
## 错误处理
|
|
196
|
+
|
|
197
|
+
建议始终监听 `error` 事件:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
client.on('error', (err) => {
|
|
201
|
+
console.error('SDK error:', err);
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## 许可证
|
|
206
|
+
|
|
207
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { AgentConfig, Agent, Task, Events } from './types';
|
|
3
|
+
export declare class AgentClient extends EventEmitter {
|
|
4
|
+
private config;
|
|
5
|
+
private ws;
|
|
6
|
+
private connected;
|
|
7
|
+
private reconnectTimer;
|
|
8
|
+
private heartbeatTimer;
|
|
9
|
+
private reconnectAttempts;
|
|
10
|
+
private manualDisconnect;
|
|
11
|
+
constructor(config: AgentConfig);
|
|
12
|
+
connect(): Promise<void>;
|
|
13
|
+
disconnect(): Promise<void>;
|
|
14
|
+
send(to: string, message: {
|
|
15
|
+
channel?: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
content: any;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
broadcast(channel: string, message: {
|
|
20
|
+
type?: string;
|
|
21
|
+
content: any;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
subscribe(channel: string): Promise<void>;
|
|
24
|
+
unsubscribe(channel: string): Promise<void>;
|
|
25
|
+
completeTask(taskId: string, result: any): Promise<void>;
|
|
26
|
+
failTask(taskId: string, error: string): Promise<void>;
|
|
27
|
+
private updateTask;
|
|
28
|
+
getState(key: string): Promise<any>;
|
|
29
|
+
setState(key: string, value: any): Promise<void>;
|
|
30
|
+
listAgents(): Promise<Agent[]>;
|
|
31
|
+
listTasks(filters?: {
|
|
32
|
+
status?: string;
|
|
33
|
+
assignedTo?: string;
|
|
34
|
+
limit?: number;
|
|
35
|
+
offset?: number;
|
|
36
|
+
}): Promise<Task[]>;
|
|
37
|
+
private handleMessage;
|
|
38
|
+
private startHeartbeat;
|
|
39
|
+
private stopHeartbeat;
|
|
40
|
+
private scheduleReconnect;
|
|
41
|
+
private stopReconnect;
|
|
42
|
+
private generateId;
|
|
43
|
+
private httpGet;
|
|
44
|
+
private httpPut;
|
|
45
|
+
on<Event extends keyof Events>(event: Event, handler: Events[Event]): this;
|
|
46
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
export class AgentClient extends EventEmitter {
|
|
4
|
+
config;
|
|
5
|
+
ws = null;
|
|
6
|
+
connected = false;
|
|
7
|
+
reconnectTimer = null;
|
|
8
|
+
heartbeatTimer = null;
|
|
9
|
+
reconnectAttempts = 0;
|
|
10
|
+
manualDisconnect = false;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super();
|
|
13
|
+
this.config = {
|
|
14
|
+
server: config.server,
|
|
15
|
+
token: config.token,
|
|
16
|
+
name: config.name || '',
|
|
17
|
+
capabilities: config.capabilities || [],
|
|
18
|
+
reconnect: config.reconnect !== false,
|
|
19
|
+
heartbeatInterval: config.heartbeatInterval || 30000,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async connect() {
|
|
23
|
+
if (this.connected) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
try {
|
|
28
|
+
const url = `${this.config.server.replace('http', 'ws')}/ws?token=${this.config.token}`;
|
|
29
|
+
this.ws = new WebSocket(url);
|
|
30
|
+
this.ws.on('open', () => {
|
|
31
|
+
this.connected = true;
|
|
32
|
+
this.reconnectAttempts = 0;
|
|
33
|
+
this.manualDisconnect = false;
|
|
34
|
+
this.startHeartbeat();
|
|
35
|
+
this.emit('connected');
|
|
36
|
+
resolve();
|
|
37
|
+
});
|
|
38
|
+
this.ws.on('message', (data) => {
|
|
39
|
+
try {
|
|
40
|
+
const msg = JSON.parse(data.toString());
|
|
41
|
+
this.handleMessage(msg);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
this.emit('error', new Error(`Failed to parse message: ${err}`));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
this.ws.on('close', (code, reason) => {
|
|
48
|
+
this.connected = false;
|
|
49
|
+
this.stopHeartbeat();
|
|
50
|
+
if (!this.manualDisconnect && this.config.reconnect) {
|
|
51
|
+
this.scheduleReconnect();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.emit('disconnected', reason.toString());
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.ws.on('error', (err) => {
|
|
58
|
+
this.emit('error', err);
|
|
59
|
+
if (!this.connected) {
|
|
60
|
+
reject(err);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
reject(err);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async disconnect() {
|
|
70
|
+
this.manualDisconnect = true;
|
|
71
|
+
this.stopReconnect();
|
|
72
|
+
this.stopHeartbeat();
|
|
73
|
+
if (this.ws) {
|
|
74
|
+
this.ws.close();
|
|
75
|
+
this.ws = null;
|
|
76
|
+
}
|
|
77
|
+
this.connected = false;
|
|
78
|
+
}
|
|
79
|
+
async send(to, message) {
|
|
80
|
+
if (!this.ws || !this.connected) {
|
|
81
|
+
throw new Error('Not connected');
|
|
82
|
+
}
|
|
83
|
+
this.ws.send(JSON.stringify({
|
|
84
|
+
type: 'send',
|
|
85
|
+
id: this.generateId(),
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
payload: {
|
|
88
|
+
to,
|
|
89
|
+
channel: message.channel || 'direct',
|
|
90
|
+
type: message.type || 'text',
|
|
91
|
+
content: message.content,
|
|
92
|
+
},
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
async broadcast(channel, message) {
|
|
96
|
+
if (!this.ws || !this.connected) {
|
|
97
|
+
throw new Error('Not connected');
|
|
98
|
+
}
|
|
99
|
+
this.ws.send(JSON.stringify({
|
|
100
|
+
type: 'broadcast',
|
|
101
|
+
id: this.generateId(),
|
|
102
|
+
timestamp: Date.now(),
|
|
103
|
+
payload: {
|
|
104
|
+
channel,
|
|
105
|
+
type: message.type || 'text',
|
|
106
|
+
content: message.content,
|
|
107
|
+
},
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
async subscribe(channel) {
|
|
111
|
+
if (!this.ws || !this.connected) {
|
|
112
|
+
throw new Error('Not connected');
|
|
113
|
+
}
|
|
114
|
+
this.ws.send(JSON.stringify({
|
|
115
|
+
type: 'subscribe',
|
|
116
|
+
id: this.generateId(),
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
payload: { channel },
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
async unsubscribe(channel) {
|
|
122
|
+
if (!this.ws || !this.connected) {
|
|
123
|
+
throw new Error('Not connected');
|
|
124
|
+
}
|
|
125
|
+
this.ws.send(JSON.stringify({
|
|
126
|
+
type: 'unsubscribe',
|
|
127
|
+
id: this.generateId(),
|
|
128
|
+
timestamp: Date.now(),
|
|
129
|
+
payload: { channel },
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
async completeTask(taskId, result) {
|
|
133
|
+
await this.updateTask(taskId, 'completed', result);
|
|
134
|
+
}
|
|
135
|
+
async failTask(taskId, error) {
|
|
136
|
+
await this.updateTask(taskId, 'failed', undefined, error);
|
|
137
|
+
}
|
|
138
|
+
async updateTask(taskId, status, result, error) {
|
|
139
|
+
if (!this.ws || !this.connected) {
|
|
140
|
+
throw new Error('Not connected');
|
|
141
|
+
}
|
|
142
|
+
this.ws.send(JSON.stringify({
|
|
143
|
+
type: 'task_update',
|
|
144
|
+
id: this.generateId(),
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
payload: {
|
|
147
|
+
taskId,
|
|
148
|
+
status,
|
|
149
|
+
result,
|
|
150
|
+
error,
|
|
151
|
+
},
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
async getState(key) {
|
|
155
|
+
const response = await this.httpGet(`/api/v1/state/${key}`);
|
|
156
|
+
return response.data;
|
|
157
|
+
}
|
|
158
|
+
async setState(key, value) {
|
|
159
|
+
await this.httpPut(`/api/v1/state/${key}`, { value });
|
|
160
|
+
}
|
|
161
|
+
async listAgents() {
|
|
162
|
+
const response = await this.httpGet('/api/v1/agents');
|
|
163
|
+
return response.data;
|
|
164
|
+
}
|
|
165
|
+
async listTasks(filters) {
|
|
166
|
+
const params = new URLSearchParams();
|
|
167
|
+
if (filters?.status)
|
|
168
|
+
params.append('status', filters.status);
|
|
169
|
+
if (filters?.assignedTo)
|
|
170
|
+
params.append('assignedTo', filters.assignedTo);
|
|
171
|
+
if (filters?.limit)
|
|
172
|
+
params.append('limit', filters.limit.toString());
|
|
173
|
+
if (filters?.offset)
|
|
174
|
+
params.append('offset', filters.offset.toString());
|
|
175
|
+
const query = params.toString();
|
|
176
|
+
const response = await this.httpGet(`/api/v1/tasks${query ? `?${query}` : ''}`);
|
|
177
|
+
return response.data;
|
|
178
|
+
}
|
|
179
|
+
handleMessage(msg) {
|
|
180
|
+
const { type, payload } = msg;
|
|
181
|
+
switch (type) {
|
|
182
|
+
case 'heartbeat_ack':
|
|
183
|
+
// Heartbeat acknowledged
|
|
184
|
+
break;
|
|
185
|
+
case 'task_assigned':
|
|
186
|
+
this.emit('task:assigned', payload.task);
|
|
187
|
+
break;
|
|
188
|
+
case 'task_cancelled':
|
|
189
|
+
this.emit('task:cancelled', payload.taskId);
|
|
190
|
+
break;
|
|
191
|
+
case 'message':
|
|
192
|
+
this.emit('message', {
|
|
193
|
+
id: payload.id,
|
|
194
|
+
from: payload.from,
|
|
195
|
+
channel: payload.channel,
|
|
196
|
+
type: payload.type,
|
|
197
|
+
content: payload.content,
|
|
198
|
+
timestamp: payload.timestamp,
|
|
199
|
+
});
|
|
200
|
+
break;
|
|
201
|
+
case 'agent_online':
|
|
202
|
+
this.emit('agent:online', {
|
|
203
|
+
id: payload.agentId,
|
|
204
|
+
name: payload.name,
|
|
205
|
+
status: 'online',
|
|
206
|
+
capabilities: [],
|
|
207
|
+
});
|
|
208
|
+
break;
|
|
209
|
+
case 'agent_offline':
|
|
210
|
+
this.emit('agent:offline', payload.agentId);
|
|
211
|
+
break;
|
|
212
|
+
case 'state_changed':
|
|
213
|
+
this.emit('state:changed', {
|
|
214
|
+
key: payload.key,
|
|
215
|
+
value: payload.value,
|
|
216
|
+
updatedBy: payload.updatedBy,
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
case 'error':
|
|
220
|
+
this.emit('error', new Error(payload.message));
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
startHeartbeat() {
|
|
225
|
+
this.stopHeartbeat();
|
|
226
|
+
this.heartbeatTimer = setInterval(() => {
|
|
227
|
+
if (this.ws && this.connected) {
|
|
228
|
+
this.ws.send(JSON.stringify({
|
|
229
|
+
type: 'heartbeat',
|
|
230
|
+
id: this.generateId(),
|
|
231
|
+
timestamp: Date.now(),
|
|
232
|
+
payload: {},
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
}, this.config.heartbeatInterval);
|
|
236
|
+
}
|
|
237
|
+
stopHeartbeat() {
|
|
238
|
+
if (this.heartbeatTimer) {
|
|
239
|
+
clearInterval(this.heartbeatTimer);
|
|
240
|
+
this.heartbeatTimer = null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
scheduleReconnect() {
|
|
244
|
+
this.stopReconnect();
|
|
245
|
+
const delays = [1000, 2000, 4000, 8000, 16000, 30000];
|
|
246
|
+
const delay = delays[Math.min(this.reconnectAttempts, delays.length - 1)];
|
|
247
|
+
this.reconnectAttempts++;
|
|
248
|
+
this.reconnectTimer = setTimeout(() => {
|
|
249
|
+
this.connect().catch(() => {
|
|
250
|
+
// Reconnect will be rescheduled
|
|
251
|
+
});
|
|
252
|
+
}, delay);
|
|
253
|
+
}
|
|
254
|
+
stopReconnect() {
|
|
255
|
+
if (this.reconnectTimer) {
|
|
256
|
+
clearTimeout(this.reconnectTimer);
|
|
257
|
+
this.reconnectTimer = null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
generateId() {
|
|
261
|
+
return `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
262
|
+
}
|
|
263
|
+
async httpGet(path) {
|
|
264
|
+
const url = `${this.config.server}${path}`;
|
|
265
|
+
const response = await fetch(url, {
|
|
266
|
+
headers: {
|
|
267
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
268
|
+
'Content-Type': 'application/json',
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
if (!response.ok) {
|
|
272
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
273
|
+
}
|
|
274
|
+
return response.json();
|
|
275
|
+
}
|
|
276
|
+
async httpPut(path, body) {
|
|
277
|
+
const url = `${this.config.server}${path}`;
|
|
278
|
+
const response = await fetch(url, {
|
|
279
|
+
method: 'PUT',
|
|
280
|
+
headers: {
|
|
281
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
},
|
|
284
|
+
body: JSON.stringify(body),
|
|
285
|
+
});
|
|
286
|
+
if (!response.ok) {
|
|
287
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
on(event, handler) {
|
|
291
|
+
super.on(event, handler);
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface AgentConfig {
|
|
2
|
+
server: string;
|
|
3
|
+
token: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
capabilities?: string[];
|
|
6
|
+
reconnect?: boolean;
|
|
7
|
+
heartbeatInterval?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Agent {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
status: 'online' | 'offline' | 'busy';
|
|
13
|
+
capabilities: string[];
|
|
14
|
+
lastSeen?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Task {
|
|
17
|
+
id: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
status: 'pending' | 'assigned' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
21
|
+
priority: number;
|
|
22
|
+
createdBy: string;
|
|
23
|
+
assignedTo?: string;
|
|
24
|
+
payload?: any;
|
|
25
|
+
result?: any;
|
|
26
|
+
error?: string;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
createdAt: number;
|
|
29
|
+
startedAt?: number;
|
|
30
|
+
completedAt?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface Message {
|
|
33
|
+
id: string;
|
|
34
|
+
from: string;
|
|
35
|
+
channel: string;
|
|
36
|
+
type: string;
|
|
37
|
+
content: any;
|
|
38
|
+
timestamp: number;
|
|
39
|
+
}
|
|
40
|
+
export interface StateChange {
|
|
41
|
+
key: string;
|
|
42
|
+
value: any;
|
|
43
|
+
updatedBy: string;
|
|
44
|
+
}
|
|
45
|
+
export type EventHandler<T = any> = (data: T) => void;
|
|
46
|
+
export interface Events {
|
|
47
|
+
connected: () => void;
|
|
48
|
+
disconnected: (reason: string) => void;
|
|
49
|
+
'task:assigned': (task: Task) => void;
|
|
50
|
+
'task:cancelled': (taskId: string) => void;
|
|
51
|
+
message: (msg: Message) => void;
|
|
52
|
+
'agent:online': (agent: Agent) => void;
|
|
53
|
+
'agent:offline': (agentId: string) => void;
|
|
54
|
+
'state:changed': (change: StateChange) => void;
|
|
55
|
+
error: (err: Error) => void;
|
|
56
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drok/agent-hub-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Agent SDK for Agent Hub",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"dev": "tsc --watch"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["agent", "hub", "sdk"],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"ws": "^8.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^20.0.0",
|
|
20
|
+
"@types/ws": "^8.0.0",
|
|
21
|
+
"typescript": "^5.0.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { AgentConfig, Agent, Task, Message, StateChange, Events } from './types';
|
|
4
|
+
|
|
5
|
+
export class AgentClient extends EventEmitter {
|
|
6
|
+
private config: Required<AgentConfig>;
|
|
7
|
+
private ws: WebSocket | null = null;
|
|
8
|
+
private connected = false;
|
|
9
|
+
private reconnectTimer: NodeJS.Timeout | null = null;
|
|
10
|
+
private heartbeatTimer: NodeJS.Timeout | null = null;
|
|
11
|
+
private reconnectAttempts = 0;
|
|
12
|
+
private manualDisconnect = false;
|
|
13
|
+
|
|
14
|
+
constructor(config: AgentConfig) {
|
|
15
|
+
super();
|
|
16
|
+
this.config = {
|
|
17
|
+
server: config.server,
|
|
18
|
+
token: config.token,
|
|
19
|
+
name: config.name || '',
|
|
20
|
+
capabilities: config.capabilities || [],
|
|
21
|
+
reconnect: config.reconnect !== false,
|
|
22
|
+
heartbeatInterval: config.heartbeatInterval || 30000,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async connect(): Promise<void> {
|
|
27
|
+
if (this.connected) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
try {
|
|
33
|
+
const url = `${this.config.server.replace('http', 'ws')}/ws?token=${this.config.token}`;
|
|
34
|
+
this.ws = new WebSocket(url);
|
|
35
|
+
|
|
36
|
+
this.ws.on('open', () => {
|
|
37
|
+
this.connected = true;
|
|
38
|
+
this.reconnectAttempts = 0;
|
|
39
|
+
this.manualDisconnect = false;
|
|
40
|
+
this.startHeartbeat();
|
|
41
|
+
this.emit('connected');
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.ws.on('message', (data) => {
|
|
46
|
+
try {
|
|
47
|
+
const msg = JSON.parse(data.toString());
|
|
48
|
+
this.handleMessage(msg);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
this.emit('error', new Error(`Failed to parse message: ${err}`));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.ws.on('close', (code, reason) => {
|
|
55
|
+
this.connected = false;
|
|
56
|
+
this.stopHeartbeat();
|
|
57
|
+
|
|
58
|
+
if (!this.manualDisconnect && this.config.reconnect) {
|
|
59
|
+
this.scheduleReconnect();
|
|
60
|
+
} else {
|
|
61
|
+
this.emit('disconnected', reason.toString());
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
this.ws.on('error', (err) => {
|
|
66
|
+
this.emit('error', err);
|
|
67
|
+
if (!this.connected) {
|
|
68
|
+
reject(err);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
} catch (err) {
|
|
73
|
+
reject(err);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async disconnect(): Promise<void> {
|
|
79
|
+
this.manualDisconnect = true;
|
|
80
|
+
this.stopReconnect();
|
|
81
|
+
this.stopHeartbeat();
|
|
82
|
+
|
|
83
|
+
if (this.ws) {
|
|
84
|
+
this.ws.close();
|
|
85
|
+
this.ws = null;
|
|
86
|
+
}
|
|
87
|
+
this.connected = false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async send(to: string, message: { channel?: string; type?: string; content: any }): Promise<void> {
|
|
91
|
+
if (!this.ws || !this.connected) {
|
|
92
|
+
throw new Error('Not connected');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.ws.send(JSON.stringify({
|
|
96
|
+
type: 'send',
|
|
97
|
+
id: this.generateId(),
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
payload: {
|
|
100
|
+
to,
|
|
101
|
+
channel: message.channel || 'direct',
|
|
102
|
+
type: message.type || 'text',
|
|
103
|
+
content: message.content,
|
|
104
|
+
},
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async broadcast(channel: string, message: { type?: string; content: any }): Promise<void> {
|
|
109
|
+
if (!this.ws || !this.connected) {
|
|
110
|
+
throw new Error('Not connected');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.ws.send(JSON.stringify({
|
|
114
|
+
type: 'broadcast',
|
|
115
|
+
id: this.generateId(),
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
payload: {
|
|
118
|
+
channel,
|
|
119
|
+
type: message.type || 'text',
|
|
120
|
+
content: message.content,
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async subscribe(channel: string): Promise<void> {
|
|
126
|
+
if (!this.ws || !this.connected) {
|
|
127
|
+
throw new Error('Not connected');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.ws.send(JSON.stringify({
|
|
131
|
+
type: 'subscribe',
|
|
132
|
+
id: this.generateId(),
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
payload: { channel },
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async unsubscribe(channel: string): Promise<void> {
|
|
139
|
+
if (!this.ws || !this.connected) {
|
|
140
|
+
throw new Error('Not connected');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.ws.send(JSON.stringify({
|
|
144
|
+
type: 'unsubscribe',
|
|
145
|
+
id: this.generateId(),
|
|
146
|
+
timestamp: Date.now(),
|
|
147
|
+
payload: { channel },
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async completeTask(taskId: string, result: any): Promise<void> {
|
|
152
|
+
await this.updateTask(taskId, 'completed', result);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async failTask(taskId: string, error: string): Promise<void> {
|
|
156
|
+
await this.updateTask(taskId, 'failed', undefined, error);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async updateTask(taskId: string, status: string, result?: any, error?: string): Promise<void> {
|
|
160
|
+
if (!this.ws || !this.connected) {
|
|
161
|
+
throw new Error('Not connected');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.ws.send(JSON.stringify({
|
|
165
|
+
type: 'task_update',
|
|
166
|
+
id: this.generateId(),
|
|
167
|
+
timestamp: Date.now(),
|
|
168
|
+
payload: {
|
|
169
|
+
taskId,
|
|
170
|
+
status,
|
|
171
|
+
result,
|
|
172
|
+
error,
|
|
173
|
+
},
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async getState(key: string): Promise<any> {
|
|
178
|
+
const response = await this.httpGet(`/api/v1/state/${key}`);
|
|
179
|
+
return response.data;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async setState(key: string, value: any): Promise<void> {
|
|
183
|
+
await this.httpPut(`/api/v1/state/${key}`, { value });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async listAgents(): Promise<Agent[]> {
|
|
187
|
+
const response = await this.httpGet('/api/v1/agents');
|
|
188
|
+
return response.data;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async listTasks(filters?: { status?: string; assignedTo?: string; limit?: number; offset?: number }): Promise<Task[]> {
|
|
192
|
+
const params = new URLSearchParams();
|
|
193
|
+
if (filters?.status) params.append('status', filters.status);
|
|
194
|
+
if (filters?.assignedTo) params.append('assignedTo', filters.assignedTo);
|
|
195
|
+
if (filters?.limit) params.append('limit', filters.limit.toString());
|
|
196
|
+
if (filters?.offset) params.append('offset', filters.offset.toString());
|
|
197
|
+
|
|
198
|
+
const query = params.toString();
|
|
199
|
+
const response = await this.httpGet(`/api/v1/tasks${query ? `?${query}` : ''}`);
|
|
200
|
+
return response.data;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private handleMessage(msg: any) {
|
|
204
|
+
const { type, payload } = msg;
|
|
205
|
+
|
|
206
|
+
switch (type) {
|
|
207
|
+
case 'heartbeat_ack':
|
|
208
|
+
// Heartbeat acknowledged
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case 'task_assigned':
|
|
212
|
+
this.emit('task:assigned', payload.task);
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case 'task_cancelled':
|
|
216
|
+
this.emit('task:cancelled', payload.taskId);
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case 'message':
|
|
220
|
+
this.emit('message', {
|
|
221
|
+
id: payload.id,
|
|
222
|
+
from: payload.from,
|
|
223
|
+
channel: payload.channel,
|
|
224
|
+
type: payload.type,
|
|
225
|
+
content: payload.content,
|
|
226
|
+
timestamp: payload.timestamp,
|
|
227
|
+
});
|
|
228
|
+
break;
|
|
229
|
+
|
|
230
|
+
case 'agent_online':
|
|
231
|
+
this.emit('agent:online', {
|
|
232
|
+
id: payload.agentId,
|
|
233
|
+
name: payload.name,
|
|
234
|
+
status: 'online',
|
|
235
|
+
capabilities: [],
|
|
236
|
+
});
|
|
237
|
+
break;
|
|
238
|
+
|
|
239
|
+
case 'agent_offline':
|
|
240
|
+
this.emit('agent:offline', payload.agentId);
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case 'state_changed':
|
|
244
|
+
this.emit('state:changed', {
|
|
245
|
+
key: payload.key,
|
|
246
|
+
value: payload.value,
|
|
247
|
+
updatedBy: payload.updatedBy,
|
|
248
|
+
});
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case 'error':
|
|
252
|
+
this.emit('error', new Error(payload.message));
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private startHeartbeat() {
|
|
258
|
+
this.stopHeartbeat();
|
|
259
|
+
|
|
260
|
+
this.heartbeatTimer = setInterval(() => {
|
|
261
|
+
if (this.ws && this.connected) {
|
|
262
|
+
this.ws.send(JSON.stringify({
|
|
263
|
+
type: 'heartbeat',
|
|
264
|
+
id: this.generateId(),
|
|
265
|
+
timestamp: Date.now(),
|
|
266
|
+
payload: {},
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
}, this.config.heartbeatInterval);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private stopHeartbeat() {
|
|
273
|
+
if (this.heartbeatTimer) {
|
|
274
|
+
clearInterval(this.heartbeatTimer);
|
|
275
|
+
this.heartbeatTimer = null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private scheduleReconnect() {
|
|
280
|
+
this.stopReconnect();
|
|
281
|
+
|
|
282
|
+
const delays = [1000, 2000, 4000, 8000, 16000, 30000];
|
|
283
|
+
const delay = delays[Math.min(this.reconnectAttempts, delays.length - 1)];
|
|
284
|
+
this.reconnectAttempts++;
|
|
285
|
+
|
|
286
|
+
this.reconnectTimer = setTimeout(() => {
|
|
287
|
+
this.connect().catch(() => {
|
|
288
|
+
// Reconnect will be rescheduled
|
|
289
|
+
});
|
|
290
|
+
}, delay);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private stopReconnect() {
|
|
294
|
+
if (this.reconnectTimer) {
|
|
295
|
+
clearTimeout(this.reconnectTimer);
|
|
296
|
+
this.reconnectTimer = null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private generateId(): string {
|
|
301
|
+
return `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private async httpGet(path: string): Promise<any> {
|
|
305
|
+
const url = `${this.config.server}${path}`;
|
|
306
|
+
const response = await fetch(url, {
|
|
307
|
+
headers: {
|
|
308
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
309
|
+
'Content-Type': 'application/json',
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return response.json();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private async httpPut(path: string, body: any): Promise<void> {
|
|
321
|
+
const url = `${this.config.server}${path}`;
|
|
322
|
+
const response = await fetch(url, {
|
|
323
|
+
method: 'PUT',
|
|
324
|
+
headers: {
|
|
325
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
326
|
+
'Content-Type': 'application/json',
|
|
327
|
+
},
|
|
328
|
+
body: JSON.stringify(body),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
if (!response.ok) {
|
|
332
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
on<Event extends keyof Events>(event: Event, handler: Events[Event]): this {
|
|
337
|
+
super.on(event, handler as any);
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface AgentConfig {
|
|
2
|
+
server: string;
|
|
3
|
+
token: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
capabilities?: string[];
|
|
6
|
+
reconnect?: boolean;
|
|
7
|
+
heartbeatInterval?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Agent {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
status: 'online' | 'offline' | 'busy';
|
|
14
|
+
capabilities: string[];
|
|
15
|
+
lastSeen?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Task {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
status: 'pending' | 'assigned' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
23
|
+
priority: number;
|
|
24
|
+
createdBy: string;
|
|
25
|
+
assignedTo?: string;
|
|
26
|
+
payload?: any;
|
|
27
|
+
result?: any;
|
|
28
|
+
error?: string;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
createdAt: number;
|
|
31
|
+
startedAt?: number;
|
|
32
|
+
completedAt?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Message {
|
|
36
|
+
id: string;
|
|
37
|
+
from: string;
|
|
38
|
+
channel: string;
|
|
39
|
+
type: string;
|
|
40
|
+
content: any;
|
|
41
|
+
timestamp: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface StateChange {
|
|
45
|
+
key: string;
|
|
46
|
+
value: any;
|
|
47
|
+
updatedBy: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type EventHandler<T = any> = (data: T) => void;
|
|
51
|
+
|
|
52
|
+
export interface Events {
|
|
53
|
+
connected: () => void;
|
|
54
|
+
disconnected: (reason: string) => void;
|
|
55
|
+
'task:assigned': (task: Task) => void;
|
|
56
|
+
'task:cancelled': (taskId: string) => void;
|
|
57
|
+
message: (msg: Message) => void;
|
|
58
|
+
'agent:online': (agent: Agent) => void;
|
|
59
|
+
'agent:offline': (agentId: string) => void;
|
|
60
|
+
'state:changed': (change: StateChange) => void;
|
|
61
|
+
error: (err: Error) => void;
|
|
62
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"strict": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|