@toothfairyai/cli 1.0.16 → 1.1.1
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 +75 -0
- package/package.json +1 -1
- package/src/api.js +70 -41
package/README.md
CHANGED
|
@@ -205,6 +205,81 @@ toothfairy send "Hello" --agent-id "agent-123" --output json | jq '.agentRespons
|
|
|
205
205
|
toothfairy search "documentation" --output json | jq '.[].title'
|
|
206
206
|
```
|
|
207
207
|
|
|
208
|
+
## SDK Usage
|
|
209
|
+
|
|
210
|
+
This package also provides a JavaScript SDK for programmatic access to ToothFairyAI.
|
|
211
|
+
|
|
212
|
+
### Basic SDK Usage
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const ToothFairyAPI = require('@toothfairyai/cli/src/api');
|
|
216
|
+
|
|
217
|
+
const api = new ToothFairyAPI(
|
|
218
|
+
'https://api.toothfairyai.com', // baseUrl
|
|
219
|
+
'https://ai.toothfairyai.com', // aiUrl
|
|
220
|
+
'https://stream.toothfairyai.com', // aiStreamUrl
|
|
221
|
+
'your-api-key', // apiKey
|
|
222
|
+
'your-workspace-id', // workspaceId
|
|
223
|
+
false // verbose mode
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Send a message with streaming response
|
|
227
|
+
await api.sendMessageToAgentStream(
|
|
228
|
+
'Hello!',
|
|
229
|
+
'agent-id',
|
|
230
|
+
null, // phoneNumber
|
|
231
|
+
null, // customerId
|
|
232
|
+
null, // providerId
|
|
233
|
+
{}, // customerInfo
|
|
234
|
+
(eventType, data) => {
|
|
235
|
+
console.log(`Event: ${eventType}`, data);
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### New in v1.1.0: Show Progress Flag
|
|
241
|
+
|
|
242
|
+
The SDK now supports a `showProgress` flag that enables tracking of all events from the SSE endpoint, similar to the CLI's `--show-progress` flag:
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
// Default behavior (standard events only)
|
|
246
|
+
await api.sendMessageToAgentStream(
|
|
247
|
+
'Hello!',
|
|
248
|
+
'agent-id',
|
|
249
|
+
null, null, null, {},
|
|
250
|
+
(eventType, data) => {
|
|
251
|
+
// Receives: 'status', 'progress', 'data', 'complete', 'error', etc.
|
|
252
|
+
console.log(eventType, data);
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// With showProgress enabled (all SSE events)
|
|
257
|
+
await api.sendMessageToAgentStream(
|
|
258
|
+
'Hello!',
|
|
259
|
+
'agent-id',
|
|
260
|
+
null, null, null, {},
|
|
261
|
+
(eventType, data) => {
|
|
262
|
+
if (eventType === 'sse_event') {
|
|
263
|
+
// Raw SSE event with complete data from streaming endpoint
|
|
264
|
+
console.log('Raw SSE event:', data);
|
|
265
|
+
} else {
|
|
266
|
+
// Standard processed events
|
|
267
|
+
console.log('Standard event:', eventType, data);
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
{}, // attachments
|
|
271
|
+
true // showProgress = true
|
|
272
|
+
);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Event Types:**
|
|
276
|
+
- `'status'`: Connection status ('connected', 'complete')
|
|
277
|
+
- `'progress'`: Agent processing status updates
|
|
278
|
+
- `'data'`: Streaming response text chunks
|
|
279
|
+
- `'complete'`: Final response with metadata
|
|
280
|
+
- `'error'`: Error events
|
|
281
|
+
- `'sse_event'`: All raw SSE events (when showProgress=true)
|
|
282
|
+
|
|
208
283
|
For full documentation and cross-platform examples, see the main [README.md](../README.md) in the parent directory.
|
|
209
284
|
|
|
210
285
|
## Development
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -193,14 +193,14 @@ class ToothFairyAPI {
|
|
|
193
193
|
customerId ||
|
|
194
194
|
`cli-user-${
|
|
195
195
|
Math.abs(
|
|
196
|
-
message.split(
|
|
196
|
+
message.split("").reduce((a, b) => {
|
|
197
197
|
a = (a << 5) - a + b.charCodeAt(0);
|
|
198
198
|
return a & a;
|
|
199
199
|
}, 0)
|
|
200
200
|
) % 10000
|
|
201
201
|
}`;
|
|
202
|
-
phoneNumber = phoneNumber ||
|
|
203
|
-
providerId = providerId ||
|
|
202
|
+
phoneNumber = phoneNumber || "+1234567890";
|
|
203
|
+
providerId = providerId || "default-sms-provider";
|
|
204
204
|
|
|
205
205
|
// Process file attachments if provided
|
|
206
206
|
const processedAttachments = await this._processAttachments(attachments);
|
|
@@ -227,8 +227,8 @@ class ToothFairyAPI {
|
|
|
227
227
|
const messageData = {
|
|
228
228
|
chatID: createdChat.id,
|
|
229
229
|
text: message,
|
|
230
|
-
role:
|
|
231
|
-
userID:
|
|
230
|
+
role: "user",
|
|
231
|
+
userID: "CLI",
|
|
232
232
|
...processedAttachments,
|
|
233
233
|
};
|
|
234
234
|
const createdMessage = await this.createMessage(messageData);
|
|
@@ -310,6 +310,8 @@ class ToothFairyAPI {
|
|
|
310
310
|
* @param {string|null} providerId - SMS provider ID
|
|
311
311
|
* @param {Object} customerInfo - Additional customer information
|
|
312
312
|
* @param {Function} onEvent - Callback function called for each event
|
|
313
|
+
* @param {Object} attachments - File attachments (images, audios, videos, files)
|
|
314
|
+
* @param {boolean} showProgress - Show all progress events from SSE endpoint (default: false)
|
|
313
315
|
* @returns {Promise<void>} - Promise resolves when streaming is complete
|
|
314
316
|
*
|
|
315
317
|
* Event Types Explained:
|
|
@@ -324,8 +326,13 @@ class ToothFairyAPI {
|
|
|
324
326
|
* - 'data': Actual response text chunks (progressive text building)
|
|
325
327
|
* - 'complete': Final response with all metadata
|
|
326
328
|
* - 'error': Error occurred during streaming
|
|
329
|
+
* - 'sse_event': All raw SSE events (only when showProgress=true)
|
|
327
330
|
*
|
|
328
331
|
* The onEvent callback receives: (eventType, eventData) => {}
|
|
332
|
+
*
|
|
333
|
+
* When showProgress is true, all SSE events will be emitted to the onEvent callback
|
|
334
|
+
* with the 'sse_event' type, providing access to all raw events from the streaming
|
|
335
|
+
* endpoint, similar to the CLI's --show-progress flag behavior.
|
|
329
336
|
*/
|
|
330
337
|
async sendMessageToAgentStream(
|
|
331
338
|
message,
|
|
@@ -335,7 +342,8 @@ class ToothFairyAPI {
|
|
|
335
342
|
providerId = null,
|
|
336
343
|
customerInfo = {},
|
|
337
344
|
onEvent,
|
|
338
|
-
attachments = {}
|
|
345
|
+
attachments = {},
|
|
346
|
+
showProgress = false
|
|
339
347
|
) {
|
|
340
348
|
try {
|
|
341
349
|
// Use defaults for optional parameters
|
|
@@ -343,14 +351,14 @@ class ToothFairyAPI {
|
|
|
343
351
|
customerId ||
|
|
344
352
|
`cli-user-${
|
|
345
353
|
Math.abs(
|
|
346
|
-
message.split(
|
|
354
|
+
message.split("").reduce((a, b) => {
|
|
347
355
|
a = (a << 5) - a + b.charCodeAt(0);
|
|
348
356
|
return a & a;
|
|
349
357
|
}, 0)
|
|
350
358
|
) % 10000
|
|
351
359
|
}`;
|
|
352
|
-
phoneNumber = phoneNumber ||
|
|
353
|
-
providerId = providerId ||
|
|
360
|
+
phoneNumber = phoneNumber || "+1234567890";
|
|
361
|
+
providerId = providerId || "default-sms-provider";
|
|
354
362
|
|
|
355
363
|
// Process file attachments if provided
|
|
356
364
|
const processedAttachments = await this._processAttachments(attachments);
|
|
@@ -381,8 +389,8 @@ class ToothFairyAPI {
|
|
|
381
389
|
const messageData = {
|
|
382
390
|
chatID: createdChat.id,
|
|
383
391
|
text: message,
|
|
384
|
-
role:
|
|
385
|
-
userID:
|
|
392
|
+
role: "user",
|
|
393
|
+
userID: "CLI",
|
|
386
394
|
...processedAttachments,
|
|
387
395
|
};
|
|
388
396
|
const createdMessage = await this.createMessage(messageData);
|
|
@@ -442,13 +450,19 @@ class ToothFairyAPI {
|
|
|
442
450
|
try {
|
|
443
451
|
const eventData = JSON.parse(dataStr);
|
|
444
452
|
|
|
445
|
-
//
|
|
453
|
+
// If showProgress is enabled, emit all events
|
|
454
|
+
if (showProgress) {
|
|
455
|
+
// Emit all events with their raw structure
|
|
456
|
+
onEvent('sse_event', eventData);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Standard event processing (always executed for backward compatibility)
|
|
446
460
|
if (eventData.status) {
|
|
447
|
-
if (eventData.status ===
|
|
448
|
-
onEvent(
|
|
449
|
-
} else if (eventData.status ===
|
|
450
|
-
onEvent(
|
|
451
|
-
} else if (eventData.status ===
|
|
461
|
+
if (eventData.status === 'connected') {
|
|
462
|
+
onEvent('status', eventData);
|
|
463
|
+
} else if (eventData.status === 'complete') {
|
|
464
|
+
onEvent('status', eventData);
|
|
465
|
+
} else if (eventData.status === 'inProgress') {
|
|
452
466
|
// Parse metadata to understand what's happening
|
|
453
467
|
let metadata = {};
|
|
454
468
|
if (eventData.metadata) {
|
|
@@ -463,39 +477,42 @@ class ToothFairyAPI {
|
|
|
463
477
|
if (metadata.agent_processing_status) {
|
|
464
478
|
const processingStatus =
|
|
465
479
|
metadata.agent_processing_status;
|
|
466
|
-
onEvent(
|
|
480
|
+
onEvent('progress', {
|
|
467
481
|
...eventData,
|
|
468
482
|
processing_status: processingStatus,
|
|
469
483
|
metadata_parsed: metadata,
|
|
470
484
|
});
|
|
471
485
|
} else {
|
|
472
|
-
onEvent(
|
|
486
|
+
onEvent('progress', {
|
|
473
487
|
...eventData,
|
|
474
488
|
metadata_parsed: metadata,
|
|
475
489
|
});
|
|
476
490
|
}
|
|
477
|
-
} else if (eventData.status ===
|
|
491
|
+
} else if (eventData.status === 'fulfilled') {
|
|
478
492
|
// Final response with complete data
|
|
479
|
-
onEvent(
|
|
493
|
+
onEvent('complete', eventData);
|
|
480
494
|
}
|
|
481
|
-
} else if (eventData.text && eventData.type ===
|
|
495
|
+
} else if (eventData.text && eventData.type === 'message') {
|
|
482
496
|
// This is streaming text data
|
|
483
|
-
onEvent(
|
|
497
|
+
onEvent('data', eventData);
|
|
484
498
|
} else if (
|
|
485
|
-
eventData.type ===
|
|
499
|
+
eventData.type === 'message' &&
|
|
486
500
|
eventData.images !== undefined
|
|
487
501
|
) {
|
|
488
502
|
// Additional message metadata (images, files, etc.)
|
|
489
|
-
onEvent(
|
|
503
|
+
onEvent('metadata', eventData);
|
|
490
504
|
} else if (
|
|
491
|
-
eventData.type ===
|
|
505
|
+
eventData.type === 'message' &&
|
|
492
506
|
eventData.callbackMetadata
|
|
493
507
|
) {
|
|
494
508
|
// Callback metadata with function details and execution plan
|
|
495
|
-
onEvent(
|
|
509
|
+
onEvent('callback', eventData);
|
|
510
|
+
} else if (eventData.type === 'chat_created' || eventData.event === 'chat_created') {
|
|
511
|
+
// Chat creation event
|
|
512
|
+
onEvent('chat_created', eventData);
|
|
496
513
|
} else {
|
|
497
514
|
// Generic event data
|
|
498
|
-
onEvent(
|
|
515
|
+
onEvent('unknown', eventData);
|
|
499
516
|
}
|
|
500
517
|
} catch (e) {
|
|
501
518
|
console.error(
|
|
@@ -1689,7 +1706,7 @@ class ToothFairyAPI {
|
|
|
1689
1706
|
|
|
1690
1707
|
/**
|
|
1691
1708
|
* Process file attachments and upload them for message inclusion
|
|
1692
|
-
*
|
|
1709
|
+
*
|
|
1693
1710
|
* @param {Object} attachments - Attachment configuration
|
|
1694
1711
|
* @param {string[]} attachments.images - Array of image file paths (max 1)
|
|
1695
1712
|
* @param {string[]} attachments.audios - Array of audio file paths (max 1)
|
|
@@ -1699,7 +1716,7 @@ class ToothFairyAPI {
|
|
|
1699
1716
|
*/
|
|
1700
1717
|
async _processAttachments(attachments = {}) {
|
|
1701
1718
|
const result = {};
|
|
1702
|
-
|
|
1719
|
+
|
|
1703
1720
|
if (!attachments || Object.keys(attachments).length === 0) {
|
|
1704
1721
|
return result;
|
|
1705
1722
|
}
|
|
@@ -1707,23 +1724,26 @@ class ToothFairyAPI {
|
|
|
1707
1724
|
try {
|
|
1708
1725
|
// Validate attachment limits
|
|
1709
1726
|
if (attachments.images && attachments.images.length > 1) {
|
|
1710
|
-
throw new Error(
|
|
1727
|
+
throw new Error("Maximum 1 image attachment allowed");
|
|
1711
1728
|
}
|
|
1712
1729
|
if (attachments.audios && attachments.audios.length > 1) {
|
|
1713
|
-
throw new Error(
|
|
1730
|
+
throw new Error("Maximum 1 audio attachment allowed");
|
|
1714
1731
|
}
|
|
1715
1732
|
if (attachments.videos && attachments.videos.length > 1) {
|
|
1716
|
-
throw new Error(
|
|
1733
|
+
throw new Error("Maximum 1 video attachment allowed");
|
|
1717
1734
|
}
|
|
1718
1735
|
if (attachments.files && attachments.files.length > 5) {
|
|
1719
|
-
throw new Error(
|
|
1736
|
+
throw new Error("Maximum 5 file attachments allowed");
|
|
1720
1737
|
}
|
|
1721
1738
|
|
|
1722
1739
|
// Process images
|
|
1723
1740
|
if (attachments.images && attachments.images.length > 0) {
|
|
1724
1741
|
const imageResults = [];
|
|
1725
1742
|
for (const imagePath of attachments.images) {
|
|
1726
|
-
const uploadResult = await this.uploadFile(
|
|
1743
|
+
const uploadResult = await this.uploadFile(
|
|
1744
|
+
imagePath,
|
|
1745
|
+
this.workspaceId
|
|
1746
|
+
);
|
|
1727
1747
|
imageResults.push(uploadResult.filename);
|
|
1728
1748
|
}
|
|
1729
1749
|
result.images = imageResults;
|
|
@@ -1733,7 +1753,10 @@ class ToothFairyAPI {
|
|
|
1733
1753
|
if (attachments.audios && attachments.audios.length > 0) {
|
|
1734
1754
|
const audioResults = [];
|
|
1735
1755
|
for (const audioPath of attachments.audios) {
|
|
1736
|
-
const uploadResult = await this.uploadFile(
|
|
1756
|
+
const uploadResult = await this.uploadFile(
|
|
1757
|
+
audioPath,
|
|
1758
|
+
this.workspaceId
|
|
1759
|
+
);
|
|
1737
1760
|
audioResults.push(uploadResult.filename);
|
|
1738
1761
|
}
|
|
1739
1762
|
result.audios = audioResults;
|
|
@@ -1743,7 +1766,10 @@ class ToothFairyAPI {
|
|
|
1743
1766
|
if (attachments.videos && attachments.videos.length > 0) {
|
|
1744
1767
|
const videoResults = [];
|
|
1745
1768
|
for (const videoPath of attachments.videos) {
|
|
1746
|
-
const uploadResult = await this.uploadFile(
|
|
1769
|
+
const uploadResult = await this.uploadFile(
|
|
1770
|
+
videoPath,
|
|
1771
|
+
this.workspaceId
|
|
1772
|
+
);
|
|
1747
1773
|
videoResults.push(uploadResult.filename);
|
|
1748
1774
|
}
|
|
1749
1775
|
result.videos = videoResults;
|
|
@@ -1753,17 +1779,20 @@ class ToothFairyAPI {
|
|
|
1753
1779
|
if (attachments.files && attachments.files.length > 0) {
|
|
1754
1780
|
const fileResults = [];
|
|
1755
1781
|
for (const filePath of attachments.files) {
|
|
1756
|
-
const uploadResult = await this.uploadFile(
|
|
1782
|
+
const uploadResult = await this.uploadFile(
|
|
1783
|
+
filePath,
|
|
1784
|
+
this.workspaceId
|
|
1785
|
+
);
|
|
1757
1786
|
fileResults.push(uploadResult.filename);
|
|
1758
1787
|
}
|
|
1759
1788
|
result.files = fileResults;
|
|
1760
1789
|
}
|
|
1761
1790
|
|
|
1762
1791
|
if (this.verbose) {
|
|
1763
|
-
const chalk = require(
|
|
1764
|
-
console.error(chalk.dim(
|
|
1792
|
+
const chalk = require("chalk");
|
|
1793
|
+
console.error(chalk.dim("\n--- Processed Attachments ---"));
|
|
1765
1794
|
console.error(chalk.dim(`Result: ${JSON.stringify(result, null, 2)}`));
|
|
1766
|
-
console.error(chalk.dim(
|
|
1795
|
+
console.error(chalk.dim("-----------------------------\n"));
|
|
1767
1796
|
}
|
|
1768
1797
|
|
|
1769
1798
|
return result;
|