@clawroom/sdk 0.3.0 → 0.5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawroom/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "ClawRoom SDK — polling client and protocol types for connecting any agent to ClawRoom",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/client.ts CHANGED
@@ -21,6 +21,8 @@ export type ClawroomClientOptions = {
21
21
  deviceId: string;
22
22
  /** Agent skills */
23
23
  skills: string[];
24
+ /** Agent kind (e.g. "openclaw", "claude-code", "codex", "custom"). Defaults to "openclaw" */
25
+ kind?: string;
24
26
  /** Optional logger */
25
27
  log?: {
26
28
  info?: (message: string, ...args: unknown[]) => void;
@@ -117,6 +119,7 @@ export class ClawroomClient {
117
119
  await this.httpRequest("POST", "/heartbeat", {
118
120
  deviceId: this.options.deviceId,
119
121
  skills: this.options.skills,
122
+ kind: this.options.kind ?? "openclaw",
120
123
  });
121
124
  this.onPollSuccess(undefined);
122
125
  } catch (err) {
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { ClawroomClient } from "./client.js";
2
2
  export type { ClawroomClientOptions } from "./client.js";
3
+ export { ClawroomMachineClient } from "./machine-client.js";
4
+ export type { ClawroomMachineClientOptions } from "./machine-client.js";
3
5
 
4
6
  export type {
5
7
  AgentMessage,
@@ -0,0 +1,127 @@
1
+ /**
2
+ * ClawRoom Machine Client — machine-level auth with multi-agent support.
3
+ */
4
+
5
+ import * as os from "node:os";
6
+
7
+ export type ClawroomMachineClientOptions = {
8
+ endpoint?: string;
9
+ apiKey: string;
10
+ hostname?: string;
11
+ capabilities?: string[];
12
+ log?: {
13
+ info?: (message: string, ...args: unknown[]) => void;
14
+ warn?: (message: string, ...args: unknown[]) => void;
15
+ error?: (message: string, ...args: unknown[]) => void;
16
+ };
17
+ };
18
+
19
+ type AgentWork = {
20
+ agentId: string;
21
+ agentName: string;
22
+ task: { type: "server.task"; taskId: string; title: string; description: string; input: string; skillTags: string[] } | null;
23
+ chat: Array<{ messageId: string; channelId: string; content: string; isMention: boolean; context: unknown[] }> | null;
24
+ };
25
+
26
+ export class ClawroomMachineClient {
27
+ private options: ClawroomMachineClientOptions;
28
+ private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
29
+ private pollTimer: ReturnType<typeof setInterval> | null = null;
30
+ private taskHandler: ((agentId: string, task: AgentWork["task"]) => void) | null = null;
31
+ private chatHandler: ((agentId: string, messages: NonNullable<AgentWork["chat"]>) => void) | null = null;
32
+ private _connected = false;
33
+ private _stopped = false;
34
+
35
+ constructor(options: ClawroomMachineClientOptions) {
36
+ this.options = options;
37
+ }
38
+
39
+ private get baseUrl(): string {
40
+ return this.options.endpoint ?? "http://localhost:3000/api/machines";
41
+ }
42
+
43
+ private async httpRequest(method: string, path: string, body?: unknown): Promise<unknown> {
44
+ const url = `${this.baseUrl}${path}`;
45
+ const res = await fetch(url, {
46
+ method,
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ Authorization: `Bearer ${this.options.apiKey}`,
50
+ },
51
+ body: body ? JSON.stringify(body) : undefined,
52
+ });
53
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
54
+ return res.json();
55
+ }
56
+
57
+ onAgentTask(handler: (agentId: string, task: AgentWork["task"]) => void) {
58
+ this.taskHandler = handler;
59
+ }
60
+
61
+ onAgentChat(handler: (agentId: string, messages: NonNullable<AgentWork["chat"]>) => void) {
62
+ this.chatHandler = handler;
63
+ }
64
+
65
+ async sendAgentComplete(agentId: string, taskId: string, output: string) {
66
+ // Use agent-level API via machine auth proxy (or direct)
67
+ await this.httpRequest("POST", "/complete", { agentId, taskId, output });
68
+ }
69
+
70
+ async sendAgentFail(agentId: string, taskId: string, reason: string) {
71
+ await this.httpRequest("POST", "/fail", { agentId, taskId, reason });
72
+ }
73
+
74
+ async sendAgentChatReply(agentId: string, channelId: string, content: string) {
75
+ await this.httpRequest("POST", "/chat-reply", { agentId, channelId, content });
76
+ }
77
+
78
+ async sendAgentTyping(agentId: string, channelId: string) {
79
+ await this.httpRequest("POST", "/typing", { agentId, channelId });
80
+ }
81
+
82
+ get connected() { return this._connected; }
83
+ get stopped() { return this._stopped; }
84
+
85
+ connect() {
86
+ this._stopped = false;
87
+ const hostname = this.options.hostname ?? os.hostname();
88
+ const hbBody = { hostname, capabilities: this.options.capabilities };
89
+
90
+ // Heartbeat every 30s
91
+ this.heartbeatTimer = setInterval(async () => {
92
+ try {
93
+ await this.httpRequest("POST", "/heartbeat", hbBody);
94
+ if (!this._connected) { this._connected = true; this.options.log?.info?.("[machine] connected"); }
95
+ } catch (err) {
96
+ if (this._connected) { this._connected = false; this.options.log?.warn?.("[machine] disconnected"); }
97
+ this.options.log?.warn?.(`[machine] heartbeat error: ${err}`);
98
+ }
99
+ }, 30_000);
100
+
101
+ // Initial heartbeat
102
+ this.httpRequest("POST", "/heartbeat", hbBody)
103
+ .then(() => { this._connected = true; this.options.log?.info?.("[machine] connected"); })
104
+ .catch((err) => this.options.log?.warn?.(`[machine] initial heartbeat failed: ${err}`));
105
+
106
+ // Poll every 10s
107
+ this.pollTimer = setInterval(async () => {
108
+ if (!this._connected) return;
109
+ try {
110
+ const result = (await this.httpRequest("POST", "/poll", {})) as { machineId: string; agents: AgentWork[] };
111
+ for (const agent of result.agents) {
112
+ if (agent.task && this.taskHandler) this.taskHandler(agent.agentId, agent.task);
113
+ if (agent.chat && agent.chat.length > 0 && this.chatHandler) this.chatHandler(agent.agentId, agent.chat);
114
+ }
115
+ } catch (err) {
116
+ this.options.log?.warn?.(`[machine] poll error: ${err}`);
117
+ }
118
+ }, 10_000);
119
+ }
120
+
121
+ disconnect() {
122
+ this._stopped = true;
123
+ this._connected = false;
124
+ if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
125
+ if (this.pollTimer) clearInterval(this.pollTimer);
126
+ }
127
+ }
package/src/protocol.ts CHANGED
@@ -68,6 +68,7 @@ export interface ServerChatMessage {
68
68
  messageId: string;
69
69
  channelId: string;
70
70
  content: string;
71
+ isMention: boolean;
71
72
  context: Array<{
72
73
  id: string;
73
74
  senderType: string;