dashclaw 1.8.1 → 1.8.2
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 +33 -0
- package/dashclaw.js +95 -21
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1026,6 +1026,7 @@ Send a message to another agent or broadcast.
|
|
|
1026
1026
|
| threadId | string | No | Thread ID to attach to |
|
|
1027
1027
|
| urgent | boolean | No | Mark as urgent |
|
|
1028
1028
|
| docRef | string | No | Reference to a shared doc ID |
|
|
1029
|
+
| attachments | Array<{filename, mime_type, data}> | No | File attachments (base64, max 3, max 5MB each) |
|
|
1029
1030
|
|
|
1030
1031
|
**Returns:** `Promise<{message: Object, message_id: string}>`
|
|
1031
1032
|
|
|
@@ -1119,6 +1120,38 @@ Create or update a shared workspace document. Upserts by name.
|
|
|
1119
1120
|
|
|
1120
1121
|
**Returns:** `Promise<{doc: Object, doc_id: string}>`
|
|
1121
1122
|
|
|
1123
|
+
### claw.getAttachmentUrl(attachmentId)
|
|
1124
|
+
|
|
1125
|
+
Get a URL to download an attachment.
|
|
1126
|
+
|
|
1127
|
+
| Parameter | Type | Description |
|
|
1128
|
+
|---|---|---|
|
|
1129
|
+
| `attachmentId` | `string` | Attachment ID (`att_*`) |
|
|
1130
|
+
|
|
1131
|
+
**Returns:** `string` — URL to fetch the attachment binary
|
|
1132
|
+
|
|
1133
|
+
---
|
|
1134
|
+
|
|
1135
|
+
### claw.getAttachment(attachmentId)
|
|
1136
|
+
|
|
1137
|
+
Download an attachment as a Buffer.
|
|
1138
|
+
|
|
1139
|
+
| Parameter | Type | Description |
|
|
1140
|
+
|---|---|---|
|
|
1141
|
+
| `attachmentId` | `string` | Attachment ID (`att_*`) |
|
|
1142
|
+
|
|
1143
|
+
**Returns:** `Promise<{ data: Buffer, filename: string, mimeType: string }>`
|
|
1144
|
+
|
|
1145
|
+
```js
|
|
1146
|
+
const inbox = await claw.getInbox();
|
|
1147
|
+
for (const msg of inbox.messages) {
|
|
1148
|
+
for (const att of msg.attachments || []) {
|
|
1149
|
+
const { data, filename } = await claw.getAttachment(att.id);
|
|
1150
|
+
fs.writeFileSync(filename, data);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1122
1155
|
---
|
|
1123
1156
|
|
|
1124
1157
|
## Agent Pairing
|
package/dashclaw.js
CHANGED
|
@@ -542,6 +542,10 @@ class DashClaw {
|
|
|
542
542
|
* Subscribe to real-time SSE events from the DashClaw server.
|
|
543
543
|
* Uses fetch-based SSE parsing for Node 18+ compatibility (no native EventSource required).
|
|
544
544
|
*
|
|
545
|
+
* @param {Object} [options]
|
|
546
|
+
* @param {boolean} [options.reconnect=true] - Auto-reconnect on disconnect (resumes from last event ID)
|
|
547
|
+
* @param {number} [options.maxRetries=Infinity] - Max reconnection attempts before giving up
|
|
548
|
+
* @param {number} [options.retryInterval=3000] - Milliseconds between reconnection attempts
|
|
545
549
|
* @returns {{ on(eventType: string, callback: Function): this, close(): void, _promise: Promise<void> }}
|
|
546
550
|
*
|
|
547
551
|
* @example
|
|
@@ -552,23 +556,36 @@ class DashClaw {
|
|
|
552
556
|
* .on('policy.updated', (data) => console.log('Policy changed:', data))
|
|
553
557
|
* .on('task.assigned', (data) => console.log('Task assigned:', data))
|
|
554
558
|
* .on('task.completed', (data) => console.log('Task done:', data))
|
|
559
|
+
* .on('reconnecting', ({ attempt }) => console.log(`Reconnecting #${attempt}...`))
|
|
555
560
|
* .on('error', (err) => console.error('Stream error:', err));
|
|
556
561
|
*
|
|
557
562
|
* // Later:
|
|
558
563
|
* stream.close();
|
|
559
564
|
*/
|
|
560
|
-
events() {
|
|
565
|
+
events({ reconnect = true, maxRetries = Infinity, retryInterval = 3000 } = {}) {
|
|
561
566
|
const url = `${this.baseUrl}/api/stream`;
|
|
562
567
|
const apiKey = this.apiKey;
|
|
563
568
|
|
|
564
569
|
const handlers = new Map();
|
|
565
570
|
let closed = false;
|
|
566
571
|
let controller = null;
|
|
572
|
+
let lastEventId = null;
|
|
573
|
+
let retryCount = 0;
|
|
574
|
+
|
|
575
|
+
const emit = (eventType, data) => {
|
|
576
|
+
const cbs = handlers.get(eventType) || [];
|
|
577
|
+
for (const cb of cbs) {
|
|
578
|
+
try { cb(data); } catch { /* ignore handler errors */ }
|
|
579
|
+
}
|
|
580
|
+
};
|
|
567
581
|
|
|
568
582
|
const connect = async () => {
|
|
569
583
|
controller = new AbortController();
|
|
584
|
+
const headers = { 'x-api-key': apiKey };
|
|
585
|
+
if (lastEventId) headers['last-event-id'] = lastEventId;
|
|
586
|
+
|
|
570
587
|
const res = await fetch(url, {
|
|
571
|
-
headers
|
|
588
|
+
headers,
|
|
572
589
|
signal: controller.signal,
|
|
573
590
|
});
|
|
574
591
|
|
|
@@ -576,9 +593,14 @@ class DashClaw {
|
|
|
576
593
|
throw new Error(`SSE connection failed: ${res.status} ${res.statusText}`);
|
|
577
594
|
}
|
|
578
595
|
|
|
596
|
+
retryCount = 0; // Reset on successful connection
|
|
597
|
+
|
|
579
598
|
const reader = res.body.getReader();
|
|
580
599
|
const decoder = new TextDecoder();
|
|
581
600
|
let buffer = '';
|
|
601
|
+
// Persist across reads so frames split across chunks are handled correctly
|
|
602
|
+
let currentEvent = null;
|
|
603
|
+
let currentData = '';
|
|
582
604
|
|
|
583
605
|
while (!closed) {
|
|
584
606
|
const { done, value } = await reader.read();
|
|
@@ -589,39 +611,55 @@ class DashClaw {
|
|
|
589
611
|
const lines = buffer.split('\n');
|
|
590
612
|
buffer = lines.pop(); // Keep incomplete line in buffer
|
|
591
613
|
|
|
592
|
-
let currentEvent = null;
|
|
593
|
-
let currentData = '';
|
|
594
|
-
|
|
595
614
|
for (const line of lines) {
|
|
596
|
-
if (line.startsWith('
|
|
615
|
+
if (line.startsWith('id: ')) {
|
|
616
|
+
lastEventId = line.slice(4).trim();
|
|
617
|
+
} else if (line.startsWith('event: ')) {
|
|
597
618
|
currentEvent = line.slice(7).trim();
|
|
598
619
|
} else if (line.startsWith('data: ')) {
|
|
599
620
|
currentData += line.slice(6);
|
|
621
|
+
} else if (line.startsWith(':')) {
|
|
622
|
+
// SSE comment (keepalive heartbeat) — ignore
|
|
600
623
|
} else if (line === '' && currentEvent) {
|
|
601
624
|
// End of SSE frame — dispatch
|
|
602
|
-
|
|
603
|
-
if (eventHandlers.length > 0 && currentData) {
|
|
625
|
+
if (currentData) {
|
|
604
626
|
try {
|
|
605
627
|
const parsed = JSON.parse(currentData);
|
|
606
|
-
|
|
607
|
-
try { cb(parsed); } catch { /* ignore handler errors */ }
|
|
608
|
-
}
|
|
628
|
+
emit(currentEvent, parsed);
|
|
609
629
|
} catch { /* ignore parse errors */ }
|
|
610
630
|
}
|
|
611
631
|
currentEvent = null;
|
|
612
632
|
currentData = '';
|
|
633
|
+
} else if (line === '') {
|
|
634
|
+
// Blank line without a pending event — reset partial state
|
|
635
|
+
currentEvent = null;
|
|
636
|
+
currentData = '';
|
|
613
637
|
}
|
|
614
638
|
}
|
|
615
639
|
}
|
|
616
640
|
};
|
|
617
641
|
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
642
|
+
const connectLoop = async () => {
|
|
643
|
+
while (!closed) {
|
|
644
|
+
try {
|
|
645
|
+
await connect();
|
|
646
|
+
} catch (err) {
|
|
647
|
+
if (closed) return;
|
|
648
|
+
emit('error', err);
|
|
649
|
+
}
|
|
650
|
+
// Stream ended (server closed, network drop, etc.)
|
|
651
|
+
if (closed) return;
|
|
652
|
+
if (!reconnect || retryCount >= maxRetries) {
|
|
653
|
+
emit('error', new Error('SSE stream ended'));
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
retryCount++;
|
|
657
|
+
emit('reconnecting', { attempt: retryCount, maxRetries });
|
|
658
|
+
await new Promise((r) => setTimeout(r, retryInterval));
|
|
623
659
|
}
|
|
624
|
-
}
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
const connectionPromise = connectLoop();
|
|
625
663
|
|
|
626
664
|
const handle = {
|
|
627
665
|
on(eventType, callback) {
|
|
@@ -1495,7 +1533,7 @@ class DashClaw {
|
|
|
1495
1533
|
}
|
|
1496
1534
|
|
|
1497
1535
|
// ══════════════════════════════════════════════
|
|
1498
|
-
// Category 11: Agent Messaging (
|
|
1536
|
+
// Category 11: Agent Messaging (11 methods)
|
|
1499
1537
|
// ══════════════════════════════════════════════
|
|
1500
1538
|
|
|
1501
1539
|
/**
|
|
@@ -1508,10 +1546,11 @@ class DashClaw {
|
|
|
1508
1546
|
* @param {string} [params.threadId] - Thread ID to attach message to
|
|
1509
1547
|
* @param {boolean} [params.urgent=false] - Mark as urgent
|
|
1510
1548
|
* @param {string} [params.docRef] - Reference to a shared doc ID
|
|
1549
|
+
* @param {Array<{filename: string, mime_type: string, data: string}>} [params.attachments] - File attachments (base64 data, max 3, max 5MB each)
|
|
1511
1550
|
* @returns {Promise<{message: Object, message_id: string}>}
|
|
1512
1551
|
*/
|
|
1513
|
-
async sendMessage({ to, type, subject, body, threadId, urgent, docRef }) {
|
|
1514
|
-
|
|
1552
|
+
async sendMessage({ to, type, subject, body, threadId, urgent, docRef, attachments }) {
|
|
1553
|
+
const payload = {
|
|
1515
1554
|
from_agent_id: this.agentId,
|
|
1516
1555
|
to_agent_id: to || null,
|
|
1517
1556
|
message_type: type || 'info',
|
|
@@ -1520,7 +1559,9 @@ class DashClaw {
|
|
|
1520
1559
|
thread_id: threadId,
|
|
1521
1560
|
urgent,
|
|
1522
1561
|
doc_ref: docRef,
|
|
1523
|
-
}
|
|
1562
|
+
};
|
|
1563
|
+
if (attachments?.length) payload.attachments = attachments;
|
|
1564
|
+
return this._request('/api/messages', 'POST', payload);
|
|
1524
1565
|
}
|
|
1525
1566
|
|
|
1526
1567
|
/**
|
|
@@ -1642,6 +1683,39 @@ class DashClaw {
|
|
|
1642
1683
|
});
|
|
1643
1684
|
}
|
|
1644
1685
|
|
|
1686
|
+
/**
|
|
1687
|
+
* Get an attachment's download URL or fetch its binary data.
|
|
1688
|
+
* @param {string} attachmentId - Attachment ID (att_*)
|
|
1689
|
+
* @returns {string} URL to fetch the attachment
|
|
1690
|
+
*/
|
|
1691
|
+
getAttachmentUrl(attachmentId) {
|
|
1692
|
+
return `${this.baseUrl}/api/messages/attachments?id=${encodeURIComponent(attachmentId)}`;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* Download an attachment as a Buffer.
|
|
1697
|
+
* @param {string} attachmentId - Attachment ID (att_*)
|
|
1698
|
+
* @returns {Promise<{data: Buffer, filename: string, mimeType: string}>}
|
|
1699
|
+
*/
|
|
1700
|
+
async getAttachment(attachmentId) {
|
|
1701
|
+
const url = this.getAttachmentUrl(attachmentId);
|
|
1702
|
+
const res = await fetch(url, {
|
|
1703
|
+
headers: { 'x-api-key': this.apiKey },
|
|
1704
|
+
});
|
|
1705
|
+
if (!res.ok) {
|
|
1706
|
+
const err = await res.json().catch(() => ({}));
|
|
1707
|
+
throw new Error(err.error || `Attachment fetch failed: ${res.status}`);
|
|
1708
|
+
}
|
|
1709
|
+
const data = Buffer.from(await res.arrayBuffer());
|
|
1710
|
+
const cd = res.headers.get('content-disposition') || '';
|
|
1711
|
+
const match = cd.match(/filename="(.+?)"/);
|
|
1712
|
+
return {
|
|
1713
|
+
data,
|
|
1714
|
+
filename: match ? match[1] : attachmentId,
|
|
1715
|
+
mimeType: res.headers.get('content-type') || 'application/octet-stream',
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1645
1719
|
// ══════════════════════════════════════════════
|
|
1646
1720
|
// Category 13: Behavior Guard (2 methods)
|
|
1647
1721
|
// ══════════════════════════════════════════════
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dashclaw",
|
|
3
|
-
"version": "1.8.
|
|
4
|
-
"description": "Full-featured agent toolkit for the DashClaw platform.
|
|
3
|
+
"version": "1.8.2",
|
|
4
|
+
"description": "Full-featured agent toolkit for the DashClaw platform. 96+ methods across 22+ categories for action recording, context management, session handoffs, security scanning, behavior guard, compliance, task routing, identity binding, organization management, webhooks, bulk sync, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|