@clawroom/sdk 0.2.3 → 0.3.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/package.json +3 -3
- package/src/client.ts +55 -29
- package/src/index.ts +3 -2
- package/src/protocol.ts +27 -13
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawroom/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "ClawRoom SDK — polling client and protocol types for connecting any agent to
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "ClawRoom SDK — polling client and protocol types for connecting any agent to ClawRoom",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./src/index.ts",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"clawroom",
|
|
15
15
|
"sdk",
|
|
16
16
|
"agent",
|
|
17
|
-
"
|
|
17
|
+
"workspace"
|
|
18
18
|
],
|
|
19
19
|
"files": [
|
|
20
20
|
"src"
|
package/src/client.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AgentMessage,
|
|
3
|
-
ServerClaimAck,
|
|
4
3
|
ServerTask,
|
|
4
|
+
ServerChatMessage,
|
|
5
5
|
} from "./protocol.js";
|
|
6
6
|
|
|
7
7
|
const DEFAULT_ENDPOINT = "https://clawroom.site9.ai/api/agents";
|
|
@@ -9,8 +9,8 @@ const DEFAULT_ENDPOINT = "https://clawroom.site9.ai/api/agents";
|
|
|
9
9
|
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
10
10
|
const POLL_INTERVAL_MS = 10_000;
|
|
11
11
|
|
|
12
|
-
type TaskCallback = (task: ServerTask) => void;
|
|
13
|
-
type
|
|
12
|
+
export type TaskCallback = (task: ServerTask) => void;
|
|
13
|
+
export type ChatCallback = (messages: ServerChatMessage[]) => void;
|
|
14
14
|
|
|
15
15
|
export type ClawroomClientOptions = {
|
|
16
16
|
/** HTTP base URL. Defaults to https://clawroom.site9.ai/api/agents */
|
|
@@ -32,8 +32,8 @@ export type ClawroomClientOptions = {
|
|
|
32
32
|
/**
|
|
33
33
|
* ClawRoom SDK client using HTTP polling.
|
|
34
34
|
*
|
|
35
|
-
* Agents register with /heartbeat and
|
|
36
|
-
* All agent actions (complete, fail, progress) use HTTP POST.
|
|
35
|
+
* Agents register with /heartbeat and receive assigned tasks + chat via /poll.
|
|
36
|
+
* All agent actions (complete, fail, progress, chat reply) use HTTP POST.
|
|
37
37
|
*
|
|
38
38
|
* Usage:
|
|
39
39
|
* ```ts
|
|
@@ -46,12 +46,13 @@ export type ClawroomClientOptions = {
|
|
|
46
46
|
* });
|
|
47
47
|
*
|
|
48
48
|
* client.onTask((task) => {
|
|
49
|
-
* //
|
|
49
|
+
* // task was assigned to this agent — execute it
|
|
50
|
+
* client.send({ type: "agent.complete", taskId: task.taskId, output: "Done!" });
|
|
50
51
|
* });
|
|
51
52
|
*
|
|
52
|
-
* client.
|
|
53
|
-
*
|
|
54
|
-
* client.send({ type: "agent.
|
|
53
|
+
* client.onChatMessage((messages) => {
|
|
54
|
+
* for (const msg of messages) {
|
|
55
|
+
* client.send({ type: "agent.chat.reply", channelId: msg.channelId, content: "Hello!" });
|
|
55
56
|
* }
|
|
56
57
|
* });
|
|
57
58
|
*
|
|
@@ -61,13 +62,15 @@ export type ClawroomClientOptions = {
|
|
|
61
62
|
export class ClawroomClient {
|
|
62
63
|
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
63
64
|
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
protected stopped = false;
|
|
66
|
+
protected readonly httpBase: string;
|
|
67
|
+
protected readonly options: ClawroomClientOptions;
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
protected taskCallbacks: TaskCallback[] = [];
|
|
70
|
+
protected chatCallbacks: ChatCallback[] = [];
|
|
69
71
|
|
|
70
|
-
constructor(
|
|
72
|
+
constructor(options: ClawroomClientOptions) {
|
|
73
|
+
this.options = options;
|
|
71
74
|
this.httpBase = (options.endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -90,14 +93,14 @@ export class ClawroomClient {
|
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
onTask(cb: TaskCallback): void { this.taskCallbacks.push(cb); }
|
|
93
|
-
|
|
96
|
+
onChatMessage(cb: ChatCallback): void { this.chatCallbacks.push(cb); }
|
|
94
97
|
|
|
95
98
|
// ── Heartbeat ───────────────────────────────────────────────────
|
|
96
99
|
|
|
97
100
|
private startHeartbeat(): void {
|
|
98
101
|
this.stopHeartbeat();
|
|
99
102
|
this.heartbeatTimer = setInterval(() => {
|
|
100
|
-
if (!this.stopped) this.
|
|
103
|
+
if (!this.stopped) void this.register();
|
|
101
104
|
}, HEARTBEAT_INTERVAL_MS);
|
|
102
105
|
}
|
|
103
106
|
|
|
@@ -109,49 +112,72 @@ export class ClawroomClient {
|
|
|
109
112
|
if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; }
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
protected async register(): Promise<void> {
|
|
113
116
|
try {
|
|
114
|
-
await this.
|
|
117
|
+
await this.httpRequest("POST", "/heartbeat", {
|
|
115
118
|
deviceId: this.options.deviceId,
|
|
116
119
|
skills: this.options.skills,
|
|
117
120
|
});
|
|
121
|
+
this.onPollSuccess(undefined);
|
|
118
122
|
} catch (err) {
|
|
119
123
|
this.options.log?.warn?.(`[clawroom] heartbeat error: ${err}`);
|
|
124
|
+
this.onPollError(err);
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
protected async pollTick(): Promise<void> {
|
|
124
129
|
if (this.stopped) return;
|
|
125
130
|
try {
|
|
126
|
-
const res = await this.
|
|
131
|
+
const res = await this.httpRequest("POST", "/poll", {});
|
|
132
|
+
this.onPollSuccess(res?.agentId);
|
|
133
|
+
|
|
127
134
|
if (res.task) {
|
|
135
|
+
this.options.log?.info?.(`[clawroom] received task ${res.task.taskId}: ${res.task.title}`);
|
|
128
136
|
for (const cb of this.taskCallbacks) cb(res.task);
|
|
129
|
-
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (res.chat && Array.isArray(res.chat) && res.chat.length > 0) {
|
|
140
|
+
this.options.log?.info?.(`[clawroom] received ${res.chat.length} chat mention(s)`);
|
|
141
|
+
for (const cb of this.chatCallbacks) cb(res.chat);
|
|
130
142
|
}
|
|
131
143
|
} catch (err) {
|
|
132
144
|
this.options.log?.warn?.(`[clawroom] poll error: ${err}`);
|
|
145
|
+
this.onPollError(err);
|
|
133
146
|
}
|
|
134
147
|
}
|
|
135
148
|
|
|
149
|
+
/** Override in subclass for lifecycle tracking */
|
|
150
|
+
protected onPollSuccess(_agentId: string | undefined): void {}
|
|
151
|
+
/** Override in subclass for lifecycle tracking */
|
|
152
|
+
protected onPollError(_err: unknown): void {}
|
|
153
|
+
|
|
136
154
|
// ── HTTP ────────────────────────────────────────────────────────
|
|
137
155
|
|
|
138
156
|
private async sendViaHttp(message: AgentMessage): Promise<void> {
|
|
139
157
|
switch (message.type) {
|
|
140
|
-
case "agent.complete": await this.
|
|
141
|
-
case "agent.fail": await this.
|
|
142
|
-
case "agent.progress": await this.
|
|
143
|
-
case "agent.heartbeat": await this.
|
|
144
|
-
case "agent.
|
|
158
|
+
case "agent.complete": await this.httpRequest("POST", "/complete", { taskId: message.taskId, output: message.output, attachments: message.attachments }); break;
|
|
159
|
+
case "agent.fail": await this.httpRequest("POST", "/fail", { taskId: message.taskId, reason: message.reason }); break;
|
|
160
|
+
case "agent.progress": await this.httpRequest("POST", "/progress", { taskId: message.taskId, message: message.message, percent: message.percent }); break;
|
|
161
|
+
case "agent.heartbeat": await this.httpRequest("POST", "/heartbeat", {}); break;
|
|
162
|
+
case "agent.chat.reply": await this.httpRequest("POST", "/chat/reply", { channelId: message.channelId, content: message.content, replyTo: message.replyTo }); break;
|
|
163
|
+
case "agent.typing": await this.httpRequest("POST", "/typing", { channelId: message.channelId }); break;
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
|
|
148
|
-
|
|
167
|
+
protected async httpRequest(method: string, path: string, body: unknown): Promise<any> {
|
|
149
168
|
const res = await fetch(`${this.httpBase}${path}`, {
|
|
150
|
-
method
|
|
169
|
+
method,
|
|
151
170
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${this.options.token}` },
|
|
152
171
|
body: JSON.stringify(body),
|
|
153
172
|
});
|
|
154
|
-
if (!res.ok)
|
|
173
|
+
if (!res.ok) {
|
|
174
|
+
const text = await res.text().catch(() => "");
|
|
175
|
+
this.onHttpError(res.status, text);
|
|
176
|
+
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
177
|
+
}
|
|
155
178
|
return res.json();
|
|
156
179
|
}
|
|
180
|
+
|
|
181
|
+
/** Override in subclass for error handling (e.g. 401 auto-stop) */
|
|
182
|
+
protected onHttpError(_status: number, _text: string): void {}
|
|
157
183
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,12 +4,13 @@ export type { ClawroomClientOptions } from "./client.js";
|
|
|
4
4
|
export type {
|
|
5
5
|
AgentMessage,
|
|
6
6
|
AgentHeartbeat,
|
|
7
|
-
AgentClaim,
|
|
8
7
|
AgentComplete,
|
|
9
8
|
AgentProgress,
|
|
10
9
|
AgentResultFile,
|
|
11
10
|
AgentFail,
|
|
11
|
+
AgentChatReply,
|
|
12
|
+
AgentTyping,
|
|
12
13
|
ServerMessage,
|
|
13
14
|
ServerTask,
|
|
14
|
-
|
|
15
|
+
ServerChatMessage,
|
|
15
16
|
} from "./protocol.js";
|
package/src/protocol.ts
CHANGED
|
@@ -7,11 +7,6 @@ export interface AgentHeartbeat {
|
|
|
7
7
|
type: "agent.heartbeat";
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface AgentClaim {
|
|
11
|
-
type: "agent.claim";
|
|
12
|
-
taskId: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
10
|
export interface AgentResultFile {
|
|
16
11
|
filename: string;
|
|
17
12
|
mimeType: string;
|
|
@@ -38,12 +33,25 @@ export interface AgentFail {
|
|
|
38
33
|
reason: string;
|
|
39
34
|
}
|
|
40
35
|
|
|
36
|
+
export interface AgentChatReply {
|
|
37
|
+
type: "agent.chat.reply";
|
|
38
|
+
channelId: string;
|
|
39
|
+
content: string;
|
|
40
|
+
replyTo?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AgentTyping {
|
|
44
|
+
type: "agent.typing";
|
|
45
|
+
channelId: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
export type AgentMessage =
|
|
42
49
|
| AgentHeartbeat
|
|
43
|
-
| AgentClaim
|
|
44
50
|
| AgentComplete
|
|
45
51
|
| AgentProgress
|
|
46
|
-
| AgentFail
|
|
52
|
+
| AgentFail
|
|
53
|
+
| AgentChatReply
|
|
54
|
+
| AgentTyping;
|
|
47
55
|
|
|
48
56
|
// ---- Server -> Agent ----
|
|
49
57
|
|
|
@@ -56,13 +64,19 @@ export interface ServerTask {
|
|
|
56
64
|
skillTags: string[];
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
export interface
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
export interface ServerChatMessage {
|
|
68
|
+
messageId: string;
|
|
69
|
+
channelId: string;
|
|
70
|
+
content: string;
|
|
71
|
+
context: Array<{
|
|
72
|
+
id: string;
|
|
73
|
+
senderType: string;
|
|
74
|
+
senderName: string;
|
|
75
|
+
content: string;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
}>;
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
export type ServerMessage =
|
|
67
81
|
| ServerTask
|
|
68
|
-
|
|
|
82
|
+
| ServerChatMessage;
|