@craftedxp/sdk-node 0.9.0 → 0.12.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 +62 -3
- package/dist/index.d.mts +237 -2
- package/dist/index.d.ts +237 -2
- package/dist/index.js +188 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +188 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,10 +23,11 @@ interface HttpRequest {
|
|
|
23
23
|
}
|
|
24
24
|
declare const createHttpClient: (opts: HttpClientOptions) => {
|
|
25
25
|
request: <T>(req: HttpRequest) => Promise<T>;
|
|
26
|
+
stream: <T = unknown>(req: HttpRequest) => AsyncIterable<T>;
|
|
26
27
|
};
|
|
27
28
|
type HttpClient = ReturnType<typeof createHttpClient>;
|
|
28
29
|
|
|
29
|
-
type TtsProvider = 'pockettts' | 'cartesia' | 'fish-audio' | 'gemini';
|
|
30
|
+
type TtsProvider = 'pockettts' | 'cartesia' | 'fish-audio' | 'gemini' | 'kokoro' | 'hume' | 'omnivoice' | 'chatterbox' | 'tada';
|
|
30
31
|
type SttProvider = 'deepgram';
|
|
31
32
|
type LlmProvider = 'gemini' | 'openai' | 'anthropic';
|
|
32
33
|
type EndpointingMode = 'smart' | 'simple';
|
|
@@ -34,6 +35,20 @@ interface AgentVoice {
|
|
|
34
35
|
provider: TtsProvider;
|
|
35
36
|
voiceId?: string;
|
|
36
37
|
language?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Secondary language for bilingual agents. When set, the agent supports
|
|
40
|
+
* mid-utterance code-switching via the `[lang=xx]...[/lang]` tag contract.
|
|
41
|
+
* Requires a multilingual-capable TTS provider (cartesia, kokoro,
|
|
42
|
+
* fish-audio, hume, omnivoice, gemini). See docs/sdks.md "Bilingual
|
|
43
|
+
* agents" section.
|
|
44
|
+
*/
|
|
45
|
+
secondaryLanguage?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Second-language voice ID for voice-pinned providers (kokoro). Stays
|
|
48
|
+
* unset for providers whose voices handle multiple languages natively
|
|
49
|
+
* (cartesia, hume, omnivoice).
|
|
50
|
+
*/
|
|
51
|
+
secondaryVoiceId?: string;
|
|
37
52
|
/**
|
|
38
53
|
* Per-utterance variation, 0.0–1.0. Honoured by Fish Audio and
|
|
39
54
|
* Chatterbox (passed through to each provider's synthesis API).
|
|
@@ -54,6 +69,13 @@ interface AgentTranscriber {
|
|
|
54
69
|
provider: SttProvider;
|
|
55
70
|
model: string;
|
|
56
71
|
language?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Secondary language for bilingual agents. When set, the STT provider
|
|
74
|
+
* auto-flips to multi-language mode (Deepgram `multi`, Whisper
|
|
75
|
+
* auto-detect). Other STT providers reject bilingual config at agent
|
|
76
|
+
* create time.
|
|
77
|
+
*/
|
|
78
|
+
secondaryLanguage?: string;
|
|
57
79
|
}
|
|
58
80
|
interface AgentModel {
|
|
59
81
|
provider: LlmProvider;
|
|
@@ -122,6 +144,19 @@ interface Agent {
|
|
|
122
144
|
* cache-buster so browser/CDN caches refresh on replace.
|
|
123
145
|
*/
|
|
124
146
|
avatarUrl?: string;
|
|
147
|
+
/**
|
|
148
|
+
* Transport the agent's live calls use. `'webrtc'` (the platform default
|
|
149
|
+
* for new agents) or `'ws'` (legacy). Forwarded on the mint result and
|
|
150
|
+
* needed by the agent-initiated push payload so the app dispatches to the
|
|
151
|
+
* right gateway.
|
|
152
|
+
*/
|
|
153
|
+
transport?: 'ws' | 'webrtc';
|
|
154
|
+
/** Whether this agent may host multi-party video rooms. */
|
|
155
|
+
canHostRooms?: boolean;
|
|
156
|
+
/** Room operating mode (`'notes-only'` today). */
|
|
157
|
+
roomMode?: 'notes-only';
|
|
158
|
+
/** Transcribe-only mode — the agent listens but never speaks. */
|
|
159
|
+
transcribeOnly?: boolean;
|
|
125
160
|
createdAt: number;
|
|
126
161
|
updatedAt: number;
|
|
127
162
|
}
|
|
@@ -140,6 +175,8 @@ interface AgentCreateInput {
|
|
|
140
175
|
recording?: AgentRecording;
|
|
141
176
|
structuredDataSchema?: Record<string, unknown>;
|
|
142
177
|
allowedUserTags?: string[];
|
|
178
|
+
/** See Agent.transport. Defaults to the platform default when omitted. */
|
|
179
|
+
transport?: 'ws' | 'webrtc';
|
|
143
180
|
/**
|
|
144
181
|
* Server-managed. Don't set this directly via create/update — use
|
|
145
182
|
* `agents.uploadAvatar(agentId, file)` instead. Sending it raw is
|
|
@@ -147,6 +184,25 @@ interface AgentCreateInput {
|
|
|
147
184
|
* actually changes.
|
|
148
185
|
*/
|
|
149
186
|
avatarUrl?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Let this agent host multi-party video rooms. Off by default. Required
|
|
189
|
+
* (`true`) before `client.rooms.create({ agentId })` will accept the agent.
|
|
190
|
+
*/
|
|
191
|
+
canHostRooms?: boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Room operating mode. Today only `'notes-only'` exists — the agent listens
|
|
194
|
+
* and takes structured notes without speaking. A literal union so future
|
|
195
|
+
* modes can be added without breaking the wire.
|
|
196
|
+
*/
|
|
197
|
+
roomMode?: 'notes-only';
|
|
198
|
+
/**
|
|
199
|
+
* Transcribe-only mode. When `true`, the agent listens but never speaks: no
|
|
200
|
+
* TTS/LLM, no greeting, idle nudges suppressed, user turns are not routed
|
|
201
|
+
* through the LLM — an STT-only loop that streams `transcript` data-messages
|
|
202
|
+
* back to the caller. `voice` stays required by the schema (defaults apply)
|
|
203
|
+
* but is never read at runtime.
|
|
204
|
+
*/
|
|
205
|
+
transcribeOnly?: boolean;
|
|
150
206
|
}
|
|
151
207
|
type AgentUpdateInput = Partial<AgentCreateInput>;
|
|
152
208
|
/**
|
|
@@ -228,6 +284,8 @@ interface CallRecord {
|
|
|
228
284
|
costBreakdown?: CostBreakdown;
|
|
229
285
|
errorMessage?: string;
|
|
230
286
|
metadata?: Record<string, string>;
|
|
287
|
+
/** Transport channel the call was carried over (`'voice'` = WebRTC/WS audio, `'text'` = text-only). */
|
|
288
|
+
channel?: 'voice' | 'text';
|
|
231
289
|
}
|
|
232
290
|
interface CallListFilters {
|
|
233
291
|
agentId?: string;
|
|
@@ -323,6 +381,25 @@ interface CallTokenMintInput {
|
|
|
323
381
|
* if you blindly accept client-asserted tier the gate is theatre.
|
|
324
382
|
*/
|
|
325
383
|
userTags?: string[];
|
|
384
|
+
/**
|
|
385
|
+
* Mark this token as agent-initiated. When 'agent', the tenant's server
|
|
386
|
+
* is ringing the end-user (push → app connects) and the resulting
|
|
387
|
+
* CallRecord is tagged `direction: 'outbound'`. Omit / 'user' for the
|
|
388
|
+
* default user-initiated connect. See docs/sdks.md "Agent-initiated calls".
|
|
389
|
+
*/
|
|
390
|
+
initiatedBy?: 'user' | 'agent';
|
|
391
|
+
/**
|
|
392
|
+
* Session channel selector. `voice` (default) routes to WS/WebRTC audio;
|
|
393
|
+
* `text` routes to HTTP+SSE chat via the `chats` resource. Rejected at
|
|
394
|
+
* mint when the agent has `transcribeOnly: true` or when `mode: 'learning'`
|
|
395
|
+
* is supplied.
|
|
396
|
+
*/
|
|
397
|
+
channel?: 'voice' | 'text';
|
|
398
|
+
/**
|
|
399
|
+
* Session purpose. `standard` (default) is a regular call. `learning`
|
|
400
|
+
* (admin-bypass only) flags a self-learning session — see Phase 19.
|
|
401
|
+
*/
|
|
402
|
+
mode?: 'standard' | 'learning';
|
|
326
403
|
}
|
|
327
404
|
interface CallTokenMintResult {
|
|
328
405
|
tokenId: string;
|
|
@@ -344,6 +421,12 @@ interface CallTokenMintResult {
|
|
|
344
421
|
* of falling back to the Phase-1 routes on the API base (local dev).
|
|
345
422
|
*/
|
|
346
423
|
webrtcGatewayBase?: string;
|
|
424
|
+
/**
|
|
425
|
+
* Session channel echoed back from the mint. Useful for the client SDK
|
|
426
|
+
* to dispatch correctly — text-mode tokens go to `chatsFor(token)`,
|
|
427
|
+
* voice-mode tokens go to `voice-js`/`voice-rn`.
|
|
428
|
+
*/
|
|
429
|
+
channel?: 'voice' | 'text';
|
|
347
430
|
}
|
|
348
431
|
interface CallTokenSummary {
|
|
349
432
|
tokenId: string;
|
|
@@ -400,6 +483,112 @@ interface MeResponse {
|
|
|
400
483
|
plan: 'free' | 'paid';
|
|
401
484
|
creditBalance: number;
|
|
402
485
|
}
|
|
486
|
+
type RoomStatus = 'provisioning' | 'active' | 'ended';
|
|
487
|
+
type RoomEndReason = 'duration_reached' | 'empty' | 'manual' | 'expired';
|
|
488
|
+
type GuestRole = 'host' | 'participant';
|
|
489
|
+
type WorkerStatus = 'pending' | 'up' | 'down' | 'exited';
|
|
490
|
+
interface CreateRoomInput {
|
|
491
|
+
agentId: string;
|
|
492
|
+
/** 1..240 — enforced by the server. */
|
|
493
|
+
durationMin: number;
|
|
494
|
+
}
|
|
495
|
+
interface CreateRoomResponse {
|
|
496
|
+
roomId: string;
|
|
497
|
+
status: 'provisioning';
|
|
498
|
+
expiresAt: string;
|
|
499
|
+
/** The shareable room-level secret — returned once, never persisted raw.
|
|
500
|
+
* Anyone with this token + a display name joins as a fresh participant. */
|
|
501
|
+
joinToken: string;
|
|
502
|
+
/** `${APP_ORIGIN}/rooms/${roomId}/join?t=${joinToken}` — the single link to
|
|
503
|
+
* share with everyone you want in the room. */
|
|
504
|
+
joinUrl: string;
|
|
505
|
+
}
|
|
506
|
+
interface RoomMetricsWire {
|
|
507
|
+
droppedUtterances: number;
|
|
508
|
+
deepgramReconnects: number;
|
|
509
|
+
}
|
|
510
|
+
interface RoomDoc {
|
|
511
|
+
roomId: string;
|
|
512
|
+
orgId: string;
|
|
513
|
+
agentId: string;
|
|
514
|
+
status: RoomStatus;
|
|
515
|
+
livekitSid: string | null;
|
|
516
|
+
durationMin: number;
|
|
517
|
+
/** sha256 of the shared room join token. The raw token is never persisted. */
|
|
518
|
+
joinTokenJti?: string;
|
|
519
|
+
createdByApiKeyId?: string;
|
|
520
|
+
createdAt?: string;
|
|
521
|
+
startedAt: string | null;
|
|
522
|
+
endedAt: string | null;
|
|
523
|
+
expiresAt: string;
|
|
524
|
+
endReason: RoomEndReason | null;
|
|
525
|
+
workerStatus?: WorkerStatus;
|
|
526
|
+
metrics?: RoomMetricsWire | null;
|
|
527
|
+
}
|
|
528
|
+
interface RoomListFilters {
|
|
529
|
+
status?: RoomStatus;
|
|
530
|
+
/** Server clamps to 100 max; default 20. */
|
|
531
|
+
limit?: number;
|
|
532
|
+
cursor?: string;
|
|
533
|
+
}
|
|
534
|
+
interface ListRoomsResponse {
|
|
535
|
+
rooms: RoomDoc[];
|
|
536
|
+
nextCursor: string | null;
|
|
537
|
+
}
|
|
538
|
+
interface RoomTranscriptOptions {
|
|
539
|
+
cursor?: string;
|
|
540
|
+
/** Server clamps to 1000 max; default 200. */
|
|
541
|
+
limit?: number;
|
|
542
|
+
}
|
|
543
|
+
interface UtteranceWire {
|
|
544
|
+
utteranceId: string;
|
|
545
|
+
participantId: string;
|
|
546
|
+
speakerName: string;
|
|
547
|
+
text: string;
|
|
548
|
+
startedAt: string;
|
|
549
|
+
endedAt: string;
|
|
550
|
+
sttConfidence: number;
|
|
551
|
+
}
|
|
552
|
+
interface ListUtterancesResponse {
|
|
553
|
+
utterances: UtteranceWire[];
|
|
554
|
+
nextCursor: string | null;
|
|
555
|
+
}
|
|
556
|
+
/** Returned by host-action endpoints (`/end` and friends) — 202 + eventId so
|
|
557
|
+
* callers can correlate the async system-message broadcast that follows. */
|
|
558
|
+
interface RoomEventAck {
|
|
559
|
+
eventId: string;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* SSE events emitted by the text-channel chat endpoints. Concatenate
|
|
563
|
+
* `token.text` chunks to build up the agent's reply; render `tool.call`
|
|
564
|
+
* and `tool.result` as "doing-something" UI hints; treat `turn.end` as
|
|
565
|
+
* the signal that the current POST has finished streaming.
|
|
566
|
+
*/
|
|
567
|
+
type ChatEvent = {
|
|
568
|
+
type: 'chat.started';
|
|
569
|
+
chatId: string;
|
|
570
|
+
callId: string;
|
|
571
|
+
} | {
|
|
572
|
+
type: 'token';
|
|
573
|
+
text: string;
|
|
574
|
+
} | {
|
|
575
|
+
type: 'tool.call';
|
|
576
|
+
name: string;
|
|
577
|
+
args: unknown;
|
|
578
|
+
} | {
|
|
579
|
+
type: 'tool.result';
|
|
580
|
+
name: string;
|
|
581
|
+
ok?: boolean;
|
|
582
|
+
[key: string]: unknown;
|
|
583
|
+
} | {
|
|
584
|
+
type: 'turn.end';
|
|
585
|
+
finishReason: 'stop' | 'aborted' | 'length' | 'tool_error';
|
|
586
|
+
committedText?: string;
|
|
587
|
+
} | {
|
|
588
|
+
type: 'error';
|
|
589
|
+
code: string;
|
|
590
|
+
message: string;
|
|
591
|
+
};
|
|
403
592
|
|
|
404
593
|
declare const createMeResource: (http: HttpClient) => {
|
|
405
594
|
get: () => Promise<MeResponse>;
|
|
@@ -536,6 +725,40 @@ declare const createOrgsResource: (http: HttpClient) => {
|
|
|
536
725
|
};
|
|
537
726
|
type OrgsResource = ReturnType<typeof createOrgsResource>;
|
|
538
727
|
|
|
728
|
+
declare const createRoomsResource: (http: HttpClient) => {
|
|
729
|
+
create: (input: CreateRoomInput) => Promise<CreateRoomResponse>;
|
|
730
|
+
list: (filters?: RoomListFilters) => Promise<ListRoomsResponse>;
|
|
731
|
+
get: (roomId: string) => Promise<RoomDoc>;
|
|
732
|
+
transcript: (roomId: string, opts?: RoomTranscriptOptions) => Promise<ListUtterancesResponse>;
|
|
733
|
+
end: (roomId: string) => Promise<RoomEventAck>;
|
|
734
|
+
};
|
|
735
|
+
type RoomsResource = ReturnType<typeof createRoomsResource>;
|
|
736
|
+
|
|
737
|
+
interface StartChatInput {
|
|
738
|
+
agentId: string;
|
|
739
|
+
text?: string;
|
|
740
|
+
}
|
|
741
|
+
interface Chat {
|
|
742
|
+
id: string;
|
|
743
|
+
callId: string;
|
|
744
|
+
greeting: AsyncIterable<ChatEvent>;
|
|
745
|
+
send(text: string): AsyncIterable<ChatEvent>;
|
|
746
|
+
end(): Promise<void>;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Per-token chat resource. Construct via `client.chatsFor(callToken)` —
|
|
750
|
+
* `callToken` is a raw `ct_…` minted with `channel: 'text'`.
|
|
751
|
+
*
|
|
752
|
+
* The async iterables stream SSE events: `chat.started` (start only),
|
|
753
|
+
* then a sequence of `token` / `tool.call` / `tool.result` / `error`,
|
|
754
|
+
* then `turn.end` which closes the stream. The client must NOT keep
|
|
755
|
+
* a connection open between turns — each `send` is a fresh POST.
|
|
756
|
+
*/
|
|
757
|
+
declare const createChatsResource: (http: HttpClient, callToken: string) => {
|
|
758
|
+
start(input: StartChatInput): Promise<Chat>;
|
|
759
|
+
};
|
|
760
|
+
type ChatsResource = ReturnType<typeof createChatsResource>;
|
|
761
|
+
|
|
539
762
|
interface PlatformClientOptions {
|
|
540
763
|
apiKey: string;
|
|
541
764
|
baseUrl?: string;
|
|
@@ -553,7 +776,19 @@ declare class PlatformClient {
|
|
|
553
776
|
readonly callTokens: CallTokensResource;
|
|
554
777
|
readonly webhooks: WebhooksResource;
|
|
555
778
|
readonly orgs: OrgsResource;
|
|
779
|
+
readonly rooms: RoomsResource;
|
|
780
|
+
private readonly _http;
|
|
556
781
|
constructor(options: PlatformClientOptions);
|
|
782
|
+
/**
|
|
783
|
+
* Returns a per-token chat resource bound to the given `ct_…` call token.
|
|
784
|
+
* The token must have been minted with `channel: 'text'`.
|
|
785
|
+
*
|
|
786
|
+
* Note: `chatsFor` is a factory method (not a fixed property) because the
|
|
787
|
+
* underlying resource is scoped to a single call token, whereas this client
|
|
788
|
+
* was constructed with an `sk_` admin key. Each end-user session needs its
|
|
789
|
+
* own `chatsFor(token)` handle.
|
|
790
|
+
*/
|
|
791
|
+
chatsFor(callToken: string): ChatsResource;
|
|
557
792
|
}
|
|
558
793
|
|
|
559
794
|
type ApiErrorCode = 'unauthorized' | 'forbidden' | 'not_found' | 'bad_request' | 'conflict' | 'rate_limited' | 'payment_required' | 'internal_error' | 'unknown';
|
|
@@ -576,4 +811,4 @@ declare class PlatformError extends Error {
|
|
|
576
811
|
|
|
577
812
|
declare const verifyWebhookSignature: (rawBody: Buffer | string, signatureHeader: string, secret: string) => boolean;
|
|
578
813
|
|
|
579
|
-
export { type Agent, type AgentCreateInput, type AgentEndpointing, type AgentHttpTool, type AgentModel, type AgentRecording, type AgentTool, type AgentTranscriber, type AgentUpdateInput, type AgentVoice, type AgentWebhooksResource, type AgentsResource, type ApiErrorCode, type CallListFilters, type CallRecord, type CallRecordingUrlResponse, type CallStatus, type CallSummary, type CallTokenMintInput, type CallTokenMintResult, type CallTokenSummary, type CallTokensResource, type CallsResource, type CatalogAgent, type CatalogListInput, type CostBreakdown, type CreditsResource, type EndReason, type EndpointingMode, type KnowledgeBase, type KnowledgeBaseFile, type KnowledgeBasesResource, type LedgerEntry, type LlmProvider, type MeResource, type MeResponse, type OrgsResource, PlatformClient, type PlatformClientOptions, PlatformError, type PlatformEventName, type RecordingMeta, type SttProvider, type TranscriptTurn, type TtsProvider, type WebhookConfig, type WebhookCreateInput, type WebhookDelivery, type WebhookUpdateInput, type WebhooksResource, verifyWebhookSignature };
|
|
814
|
+
export { type Agent, type AgentCreateInput, type AgentEndpointing, type AgentHttpTool, type AgentModel, type AgentRecording, type AgentTool, type AgentTranscriber, type AgentUpdateInput, type AgentVoice, type AgentWebhooksResource, type AgentsResource, type ApiErrorCode, type CallListFilters, type CallRecord, type CallRecordingUrlResponse, type CallStatus, type CallSummary, type CallTokenMintInput, type CallTokenMintResult, type CallTokenSummary, type CallTokensResource, type CallsResource, type CatalogAgent, type CatalogListInput, type Chat, type ChatEvent, type ChatsResource, type CostBreakdown, type CreateRoomInput, type CreateRoomResponse, type CreditsResource, type EndReason, type EndpointingMode, type GuestRole, type KnowledgeBase, type KnowledgeBaseFile, type KnowledgeBasesResource, type LedgerEntry, type ListRoomsResponse, type ListUtterancesResponse, type LlmProvider, type MeResource, type MeResponse, type OrgsResource, PlatformClient, type PlatformClientOptions, PlatformError, type PlatformEventName, type RecordingMeta, type RoomDoc, type RoomEndReason, type RoomEventAck, type RoomListFilters, type RoomMetricsWire, type RoomStatus, type RoomTranscriptOptions, type RoomsResource, type StartChatInput, type SttProvider, type TranscriptTurn, type TtsProvider, type UtteranceWire, type WebhookConfig, type WebhookCreateInput, type WebhookDelivery, type WebhookUpdateInput, type WebhooksResource, type WorkerStatus, verifyWebhookSignature };
|
package/dist/index.js
CHANGED
|
@@ -179,7 +179,82 @@ var createHttpClient = (opts) => {
|
|
|
179
179
|
}
|
|
180
180
|
throw lastErr instanceof Error ? lastErr : new Error("request exhausted retries");
|
|
181
181
|
};
|
|
182
|
-
|
|
182
|
+
async function* stream(req) {
|
|
183
|
+
const url = buildUrl(opts.baseUrl, req.path, req.query);
|
|
184
|
+
const headers = {
|
|
185
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
186
|
+
"Content-Type": "application/json",
|
|
187
|
+
Accept: "text/event-stream",
|
|
188
|
+
...req.headers ?? {}
|
|
189
|
+
};
|
|
190
|
+
const started = Date.now();
|
|
191
|
+
let res;
|
|
192
|
+
try {
|
|
193
|
+
res = await fetchImpl(url, {
|
|
194
|
+
method: req.method,
|
|
195
|
+
headers,
|
|
196
|
+
body: req.body !== void 0 ? JSON.stringify(req.body) : void 0
|
|
197
|
+
});
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const msg = err instanceof Error ? err.message : "network error";
|
|
200
|
+
throw new PlatformError({
|
|
201
|
+
code: "unknown",
|
|
202
|
+
message: `Network error: ${msg}`,
|
|
203
|
+
status: 0
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
opts.onRequest?.({
|
|
207
|
+
method: req.method,
|
|
208
|
+
url,
|
|
209
|
+
status: res.status,
|
|
210
|
+
durationMs: Date.now() - started,
|
|
211
|
+
attempt: 1
|
|
212
|
+
});
|
|
213
|
+
if (!res.ok || !res.body) {
|
|
214
|
+
const text = await res.text().catch(() => "");
|
|
215
|
+
let parsed = void 0;
|
|
216
|
+
try {
|
|
217
|
+
parsed = text ? JSON.parse(text) : void 0;
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
const errObj = parsed?.error;
|
|
221
|
+
throw new PlatformError({
|
|
222
|
+
code: errObj?.code ?? "unknown",
|
|
223
|
+
message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,
|
|
224
|
+
status: res.status,
|
|
225
|
+
field: errObj?.field,
|
|
226
|
+
docsUrl: errObj?.docs_url,
|
|
227
|
+
body: parsed ?? text
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const reader = res.body.getReader();
|
|
231
|
+
const decoder = new TextDecoder();
|
|
232
|
+
let buf = "";
|
|
233
|
+
while (true) {
|
|
234
|
+
const { value, done } = await reader.read();
|
|
235
|
+
if (done) return;
|
|
236
|
+
buf += decoder.decode(value, { stream: true });
|
|
237
|
+
let idx;
|
|
238
|
+
while ((idx = buf.indexOf("\n\n")) >= 0) {
|
|
239
|
+
const block = buf.slice(0, idx);
|
|
240
|
+
buf = buf.slice(idx + 2);
|
|
241
|
+
let event = "message";
|
|
242
|
+
let data = "";
|
|
243
|
+
for (const line of block.split("\n")) {
|
|
244
|
+
if (line.startsWith(":")) continue;
|
|
245
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
246
|
+
else if (line.startsWith("data:")) data += line.slice(5).trim();
|
|
247
|
+
}
|
|
248
|
+
if (!data) continue;
|
|
249
|
+
try {
|
|
250
|
+
const parsed = JSON.parse(data);
|
|
251
|
+
yield { type: event, ...parsed };
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return { request, stream };
|
|
183
258
|
};
|
|
184
259
|
|
|
185
260
|
// src/resources/me.ts
|
|
@@ -482,6 +557,102 @@ var createOrgsResource = (http) => ({
|
|
|
482
557
|
}
|
|
483
558
|
});
|
|
484
559
|
|
|
560
|
+
// src/resources/rooms.ts
|
|
561
|
+
var createRoomsResource = (http) => ({
|
|
562
|
+
// Provision a new room. Server returns 201 with ONE shared room-level
|
|
563
|
+
// `joinToken` + `joinUrl`. Share the single link with everyone you want in
|
|
564
|
+
// the room — each visitor supplies their own display name at join time and
|
|
565
|
+
// becomes a fresh, distinct participant. The server persists only the
|
|
566
|
+
// token's sha256 hash; the raw token is returned here once and never stored.
|
|
567
|
+
create: async (input) => http.request({
|
|
568
|
+
method: "POST",
|
|
569
|
+
path: "/v1/rooms",
|
|
570
|
+
body: input
|
|
571
|
+
}),
|
|
572
|
+
// Listing — opaque cursor pagination (`nextCursor` returned by the server
|
|
573
|
+
// is whatever startAfter() needs, don't parse client-side).
|
|
574
|
+
list: async (filters = {}) => {
|
|
575
|
+
const query = {};
|
|
576
|
+
if (filters.status) query.status = filters.status;
|
|
577
|
+
if (filters.limit !== void 0) query.limit = filters.limit;
|
|
578
|
+
if (filters.cursor) query.cursor = filters.cursor;
|
|
579
|
+
return http.request({
|
|
580
|
+
method: "GET",
|
|
581
|
+
path: "/v1/rooms",
|
|
582
|
+
query
|
|
583
|
+
});
|
|
584
|
+
},
|
|
585
|
+
// Fetch a single room. 404s are surfaced as PlatformError('not_found').
|
|
586
|
+
get: async (roomId) => http.request({
|
|
587
|
+
method: "GET",
|
|
588
|
+
path: `/v1/rooms/${roomId}`
|
|
589
|
+
}),
|
|
590
|
+
// Transcript pages — utterances are ordered by `startedAt asc`. Cursor is
|
|
591
|
+
// an ISO timestamp (server enforces, don't construct yourself).
|
|
592
|
+
transcript: async (roomId, opts = {}) => {
|
|
593
|
+
const query = {};
|
|
594
|
+
if (opts.cursor) query.cursor = opts.cursor;
|
|
595
|
+
if (opts.limit !== void 0) query.limit = opts.limit;
|
|
596
|
+
return http.request({
|
|
597
|
+
method: "GET",
|
|
598
|
+
path: `/v1/rooms/${roomId}/transcript`,
|
|
599
|
+
query
|
|
600
|
+
});
|
|
601
|
+
},
|
|
602
|
+
// End a room — async on the server: returns 202 + eventId once the
|
|
603
|
+
// controlEvents entry is written. The room-worker observes the entry,
|
|
604
|
+
// broadcasts the system message, and tears down LiveKit shortly after.
|
|
605
|
+
end: async (roomId) => http.request({
|
|
606
|
+
method: "POST",
|
|
607
|
+
path: `/v1/rooms/${roomId}/end`
|
|
608
|
+
})
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// src/resources/chats.ts
|
|
612
|
+
var createChatsResource = (http, callToken) => ({
|
|
613
|
+
async start(input) {
|
|
614
|
+
const tokenQs = `?token=${encodeURIComponent(callToken)}`;
|
|
615
|
+
const streamIterable = http.stream({
|
|
616
|
+
method: "POST",
|
|
617
|
+
path: `/v1/agents/${input.agentId}/chat${tokenQs}`,
|
|
618
|
+
body: input.text ? { text: input.text } : {}
|
|
619
|
+
});
|
|
620
|
+
let chatId = "";
|
|
621
|
+
let callId = "";
|
|
622
|
+
const buffered = [];
|
|
623
|
+
const iter = streamIterable[Symbol.asyncIterator]();
|
|
624
|
+
while (true) {
|
|
625
|
+
const { value, done } = await iter.next();
|
|
626
|
+
if (done) break;
|
|
627
|
+
if (value.type === "chat.started") {
|
|
628
|
+
chatId = value.chatId;
|
|
629
|
+
callId = value.callId;
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
buffered.push(value);
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
id: chatId,
|
|
636
|
+
callId,
|
|
637
|
+
greeting: replayThen(buffered, { [Symbol.asyncIterator]: () => iter }),
|
|
638
|
+
send(text) {
|
|
639
|
+
return http.stream({
|
|
640
|
+
method: "POST",
|
|
641
|
+
path: `/v1/chats/${chatId}/messages${tokenQs}`,
|
|
642
|
+
body: { text }
|
|
643
|
+
});
|
|
644
|
+
},
|
|
645
|
+
async end() {
|
|
646
|
+
await http.request({ method: "DELETE", path: `/v1/calls/${callId}` });
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
async function* replayThen(buffered, rest) {
|
|
652
|
+
for (const x of buffered) yield x;
|
|
653
|
+
for await (const x of rest) yield x;
|
|
654
|
+
}
|
|
655
|
+
|
|
485
656
|
// src/PlatformClient.ts
|
|
486
657
|
var PlatformClient = class {
|
|
487
658
|
me;
|
|
@@ -492,6 +663,8 @@ var PlatformClient = class {
|
|
|
492
663
|
callTokens;
|
|
493
664
|
webhooks;
|
|
494
665
|
orgs;
|
|
666
|
+
rooms;
|
|
667
|
+
_http;
|
|
495
668
|
constructor(options) {
|
|
496
669
|
if (!options.apiKey) {
|
|
497
670
|
throw new Error("PlatformClient: `apiKey` is required");
|
|
@@ -512,6 +685,20 @@ var PlatformClient = class {
|
|
|
512
685
|
this.callTokens = createCallTokensResource(http);
|
|
513
686
|
this.webhooks = createWebhooksResource(http);
|
|
514
687
|
this.orgs = createOrgsResource(http);
|
|
688
|
+
this.rooms = createRoomsResource(http);
|
|
689
|
+
this._http = http;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Returns a per-token chat resource bound to the given `ct_…` call token.
|
|
693
|
+
* The token must have been minted with `channel: 'text'`.
|
|
694
|
+
*
|
|
695
|
+
* Note: `chatsFor` is a factory method (not a fixed property) because the
|
|
696
|
+
* underlying resource is scoped to a single call token, whereas this client
|
|
697
|
+
* was constructed with an `sk_` admin key. Each end-user session needs its
|
|
698
|
+
* own `chatsFor(token)` handle.
|
|
699
|
+
*/
|
|
700
|
+
chatsFor(callToken) {
|
|
701
|
+
return createChatsResource(this._http, callToken);
|
|
515
702
|
}
|
|
516
703
|
};
|
|
517
704
|
|