@craftedxp/sdk-node 0.20.1 → 0.22.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/DEVELOPING.md CHANGED
@@ -41,7 +41,7 @@ const client = new PlatformClient({
41
41
  baseUrl: 'http://localhost:8080',
42
42
  })
43
43
  console.log(await client.me.get())
44
- console.log((await client.agents.list()).map(a => ({ id: a.agentId, name: a.name })))
44
+ console.log((await client.agents.list()).data.map(a => ({ id: a.agentId, name: a.name })))
45
45
  EOF
46
46
  VOICE_AGENT_SK=sk_... node smoke.mjs
47
47
  ```
package/README.md CHANGED
@@ -59,12 +59,16 @@ client.callTokens.revoke(tokenId)
59
59
  client.me.get() // the org behind the sk_
60
60
 
61
61
  client.agents.create({ type, name, systemPrompt, ... }) // CRUD on agents (type required)
62
- client.agents.list({ limit, cursor })
62
+ client.agents.list({ limit, cursor, q }) // one page → { data, nextCursor }
63
+ client.agents.listAll({ q, pageSize }) // async iterator across pages
63
64
  client.agents.get(agentId)
64
65
  client.agents.update(agentId, patch)
65
66
  client.agents.delete(agentId)
66
67
  client.agents.webhooks(agentId).create({ url, secret, events })
67
68
  client.agents.webhooks(agentId).list()
69
+ client.agents.export(agentId) // portable single-agent envelope
70
+ client.agents.exportAll() // portable bulk envelope (all agents)
71
+ client.agents.import(envelope) // recreate agents from an envelope
68
72
  // ...
69
73
 
70
74
  client.calls.list({ agentId, status, limit, cursor }) // call records
@@ -108,6 +112,39 @@ await client.agents.create({ type: 'assistant', name: 'Concierge', systemPrompt:
108
112
  The legacy `canHostRooms` / `transcribeOnly` boolean flags are **removed**;
109
113
  sending them is a 400. Use `type: 'room'` / `type: 'transcribe'` instead.
110
114
 
115
+ ## Export / import agents (0.21.0+)
116
+
117
+ Move agent configuration between orgs — back it up, seed a staging org, or ship
118
+ a starter agent. Export produces a portable envelope with server-managed and
119
+ sensitive fields stripped (ids, timestamps, avatar, BYO LLM key); import feeds
120
+ an envelope back in and recreates the agents.
121
+
122
+ > ⚠️ **The exported JSON is sensitive** — it still contains inline tool config,
123
+ > including HTTP-tool `headers` that may hold `Authorization` / API keys (the
124
+ > recreated agent needs them). Treat an export like a `GET /v1/agents/:id` dump:
125
+ > don't commit it to source control.
126
+
127
+ ```ts
128
+ // Export one agent, or every agent in the org.
129
+ const one = await client.agents.export(agentId) // { formatVersion, exportedAt, agent }
130
+ const all = await client.agents.exportAll() // { formatVersion, exportedAt, agents: [...] }
131
+
132
+ // Import accepts either shape. Best-effort per agent.
133
+ const result = await client.agents.import(all)
134
+ for (const { agent, warnings } of result.imported) {
135
+ console.log(`imported ${agent.name} (${agent.agentId})`)
136
+ warnings.forEach((w) => console.warn(` ⚠ ${w}`))
137
+ }
138
+ for (const { name, error } of result.failed) {
139
+ console.error(`failed ${name}: ${error}`)
140
+ }
141
+ ```
142
+
143
+ A partial import still resolves: agents that couldn't be created land in
144
+ `failed` while the rest succeed. `warnings` flags non-fatal fixups — e.g. a
145
+ `knowledgeBaseId` cleared because it doesn't exist in the target org, or a BYO
146
+ LLM key that must be re-added before the agent can run.
147
+
111
148
  ## Multi-party video rooms
112
149
 
113
150
  Provision a hosted room from your backend; share the single returned link;
@@ -303,6 +340,8 @@ for await (const call of client.calls.listAll({ agentId })) {
303
340
 
304
341
  ## Changelog
305
342
 
343
+ - **0.22.0** — `agents.list()` now takes `{ limit, cursor, q }` and returns the `{ data, nextCursor }` page envelope (breaking: was `Agent[]`); `agents.listAll({ q, pageSize })` walks the cursor internally and yields every agent. New types `AgentListOptions`, `AgentListResponse`, `AgentListAllOptions`. Requires server with paginated `GET /v1/agents`. First npm release since 0.20.1 — 0.21.0 was never published, so the export/import changes below ship bundled in this 0.22.0 release.
344
+ - **0.21.0** — agent export / import: `agents.export(agentId)`, `agents.exportAll()`, and `agents.import(envelope)` wrap the new portability endpoints. New types `PortableAgent`, `SingleAgentEnvelope`, `BulkAgentEnvelope`, `AgentImportResult`.
306
345
  - **0.20.1** — docs: added the "Emotion" usage section + examples to the README (no API change; the `emotion` field ships from 0.20.0).
307
346
  - **0.20.0** — `speech.synthesize()` / `enqueue()` accept an `emotion` preset (`hype | shout | disappointed | warm | calm`) for emotional TTS voices.
308
347
  - **0.19.0** — type-only: `CallTokenMintResult.transport` widened to `'ws' | 'webrtc' | 'livekit'` (server returns `'livekit'` when the platform media edge is LiveKit Cloud; client SDKs that don't know it fall back to `'ws'`); `CallRecord.transport` added. No runtime change; drop-in for 0.18.0 consumers.
package/dist/index.d.mts CHANGED
@@ -214,6 +214,24 @@ interface AgentCreateInput {
214
214
  * here — to change an agent's kind, create a new agent.
215
215
  */
216
216
  type AgentUpdateInput = Partial<Omit<AgentCreateInput, 'type'>>;
217
+ interface AgentListOptions {
218
+ /** Server clamps to 100 max; default 20. */
219
+ limit?: number;
220
+ /** Opaque cursor returned as `nextCursor` on the previous page. */
221
+ cursor?: string;
222
+ /** Case-insensitive name prefix filter. */
223
+ q?: string;
224
+ }
225
+ interface AgentListResponse {
226
+ data: Agent[];
227
+ nextCursor: string | null;
228
+ }
229
+ interface AgentListAllOptions {
230
+ /** Case-insensitive name prefix filter, applied to every page. */
231
+ q?: string;
232
+ /** Per-page size mapped to the `limit` query param. Server clamps to 100. */
233
+ pageSize?: number;
234
+ }
217
235
  /**
218
236
  * Trimmed agent shape returned by the consumer catalog
219
237
  * (`GET /v1/orgs/:orgId/agents`). Operator-only fields like
@@ -248,6 +266,42 @@ interface CatalogListInput {
248
266
  */
249
267
  userTags?: string[];
250
268
  }
269
+ /**
270
+ * Portable agent config — an {@link Agent} with server-managed and sensitive
271
+ * fields stripped (identifiers, timestamps, the avatar asset, and the LLM
272
+ * credential reference). This is the shape carried inside export envelopes and
273
+ * accepted by import. Mirrors the server's `PortableAgent`.
274
+ */
275
+ type PortableAgent = Omit<Agent, 'agentId' | 'orgId' | 'createdAt' | 'updatedAt' | 'avatarUrl'>;
276
+ /** Envelope returned by `agents.export(agentId)`. */
277
+ interface SingleAgentEnvelope {
278
+ formatVersion: number;
279
+ exportedAt: string;
280
+ agent: PortableAgent;
281
+ }
282
+ /** Envelope returned by `agents.exportAll()`. */
283
+ interface BulkAgentEnvelope {
284
+ formatVersion: number;
285
+ exportedAt: string;
286
+ agents: PortableAgent[];
287
+ }
288
+ /**
289
+ * Result of `agents.import(envelope)`. Import is best-effort per agent:
290
+ * `imported` holds each created {@link Agent} plus any non-fatal `warnings`
291
+ * (e.g. a cleared knowledge-base reference, or a BYO key that must be re-added
292
+ * before use); `failed` holds the agents that couldn't be created, keyed by
293
+ * name. A partial import returns 200 with entries in both arrays.
294
+ */
295
+ interface AgentImportResult {
296
+ imported: {
297
+ agent: Agent;
298
+ warnings: string[];
299
+ }[];
300
+ failed: {
301
+ name: string;
302
+ error: string;
303
+ }[];
304
+ }
251
305
  type CallStatus = 'queued' | 'ringing' | 'in_progress' | 'completed' | 'failed' | 'no_answer';
252
306
  /**
253
307
  * Media path a call was carried over. Shared by {@link CallRecord.transport}
@@ -790,8 +844,8 @@ type AgentWebhooksResource = ReturnType<typeof createAgentWebhooksResource>;
790
844
 
791
845
  declare const createAgentsResource: (http: HttpClient) => {
792
846
  create: (input: AgentCreateInput) => Promise<Agent>;
793
- list: () => Promise<Agent[]>;
794
- listAll: () => AsyncIterable<Agent>;
847
+ list: (opts?: AgentListOptions) => Promise<AgentListResponse>;
848
+ listAll: (opts?: AgentListAllOptions) => AsyncIterable<Agent>;
795
849
  get: (agentId: string) => Promise<Agent>;
796
850
  update: (agentId: string, patch: AgentUpdateInput) => Promise<Agent>;
797
851
  delete: (agentId: string) => Promise<void>;
@@ -815,6 +869,29 @@ declare const createAgentsResource: (http: HttpClient) => {
815
869
  * returns the agent.
816
870
  */
817
871
  removeAvatar: (agentId: string) => Promise<Agent>;
872
+ /**
873
+ * Export a single agent as a portable envelope. Server-managed and
874
+ * sensitive fields (ids, timestamps, avatar, BYO LLM key) are stripped —
875
+ * the envelope carries only the config needed to recreate the agent via
876
+ * {@link import}. Feed it straight back into `agents.import(envelope)`,
877
+ * in this org or another.
878
+ */
879
+ export: (agentId: string) => Promise<SingleAgentEnvelope>;
880
+ /**
881
+ * Export every agent in the org as one bulk envelope — the same portable
882
+ * shape as {@link export}, with an `agents` array. Round-trips through
883
+ * {@link import} unchanged.
884
+ */
885
+ exportAll: () => Promise<BulkAgentEnvelope>;
886
+ /**
887
+ * Import agents from a single or bulk envelope produced by {@link export} /
888
+ * {@link exportAll}. Best-effort per agent: the result's `imported` array
889
+ * holds each created agent plus non-fatal `warnings` (e.g. a knowledge-base
890
+ * reference cleared because it doesn't exist in the target org, or a BYO
891
+ * LLM key that must be re-added), and `failed` holds the ones that couldn't
892
+ * be created. A partial import still resolves — inspect both arrays.
893
+ */
894
+ import: (envelope: SingleAgentEnvelope | BulkAgentEnvelope) => Promise<AgentImportResult>;
818
895
  webhooks: (agentId: string) => AgentWebhooksResource;
819
896
  };
820
897
  type AgentsResource = ReturnType<typeof createAgentsResource>;
@@ -1050,4 +1127,4 @@ declare class PlatformError extends Error {
1050
1127
 
1051
1128
  declare const verifyWebhookSignature: (rawBody: Buffer | string, signatureHeader: string, secret: string) => boolean;
1052
1129
 
1053
- export { type Agent, type AgentCreateInput, type AgentEndpointing, type AgentHttpTool, type AgentModel, type AgentRecording, type AgentTool, type AgentTranscriber, type AgentType, type AgentUpdateInput, type AgentVoice, type AgentWebhooksResource, type AgentsResource, type AnalysisWire, type ApiErrorCode, type BuildJoinUrlOptions, type CallListFilters, type CallRecord, type CallRecordingUrlResponse, type CallStatus, type CallSummary, type CallTokenMintInput, type CallTokenMintResult, type CallTokenSummary, type CallTokensResource, type CallTransport, type CallsResource, type CatalogAgent, type CatalogListInput, type Chat, type ChatEvent, type ChatsResource, type CostBreakdown, type CreateRoomInput, type CreateRoomResponse, type CreateSpaceInput, type CreditsResource, type EndReason, type EndpointingMode, type GuestRole, type JoinUrlStyle, type KnowledgeBase, type KnowledgeBaseFile, type KnowledgeBasesResource, type LedgerEntry, type ListAnalysisResponse, type ListRoomsResponse, type ListSpacesResponse, type ListUtterancesResponse, type LlmProvider, type MeResource, type MeResponse, type OrgsResource, PlatformClient, type PlatformClientOptions, PlatformError, type PlatformEventName, type RecordingConfig, type RecordingMeta, type RecordingMode, type RoomDoc, type RoomEndReason, type RoomEventAck, type RoomListFilters, type RoomMetricsWire, type RoomStatus, type RoomTranscriptOptions, type RoomsResource, type SpaceKind, type SpacePatch, type SpaceView, type SpacesResource, type SpeechAsset, type SpeechFailedEvent, type SpeechJob, type SpeechListInput, type SpeechReadyEvent, type SpeechResource, type SpeechSynthesizeInput, type StartChatInput, type SttProvider, type TranscriptTurn, type TtsProvider, type UtteranceWire, type WebhookConfig, type WebhookCreateInput, type WebhookDelivery, type WebhookUpdateInput, type WebhooksResource, type WorkerStatus, buildJoinUrl, verifyWebhookSignature };
1130
+ export { type Agent, type AgentCreateInput, type AgentEndpointing, type AgentHttpTool, type AgentImportResult, type AgentListAllOptions, type AgentListOptions, type AgentListResponse, type AgentModel, type AgentRecording, type AgentTool, type AgentTranscriber, type AgentType, type AgentUpdateInput, type AgentVoice, type AgentWebhooksResource, type AgentsResource, type AnalysisWire, type ApiErrorCode, type BuildJoinUrlOptions, type BulkAgentEnvelope, type CallListFilters, type CallRecord, type CallRecordingUrlResponse, type CallStatus, type CallSummary, type CallTokenMintInput, type CallTokenMintResult, type CallTokenSummary, type CallTokensResource, type CallTransport, type CallsResource, type CatalogAgent, type CatalogListInput, type Chat, type ChatEvent, type ChatsResource, type CostBreakdown, type CreateRoomInput, type CreateRoomResponse, type CreateSpaceInput, type CreditsResource, type EndReason, type EndpointingMode, type GuestRole, type JoinUrlStyle, type KnowledgeBase, type KnowledgeBaseFile, type KnowledgeBasesResource, type LedgerEntry, type ListAnalysisResponse, type ListRoomsResponse, type ListSpacesResponse, type ListUtterancesResponse, type LlmProvider, type MeResource, type MeResponse, type OrgsResource, PlatformClient, type PlatformClientOptions, PlatformError, type PlatformEventName, type PortableAgent, type RecordingConfig, type RecordingMeta, type RecordingMode, type RoomDoc, type RoomEndReason, type RoomEventAck, type RoomListFilters, type RoomMetricsWire, type RoomStatus, type RoomTranscriptOptions, type RoomsResource, type SingleAgentEnvelope, type SpaceKind, type SpacePatch, type SpaceView, type SpacesResource, type SpeechAsset, type SpeechFailedEvent, type SpeechJob, type SpeechListInput, type SpeechReadyEvent, type SpeechResource, type SpeechSynthesizeInput, type StartChatInput, type SttProvider, type TranscriptTurn, type TtsProvider, type UtteranceWire, type WebhookConfig, type WebhookCreateInput, type WebhookDelivery, type WebhookUpdateInput, type WebhooksResource, type WorkerStatus, buildJoinUrl, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -214,6 +214,24 @@ interface AgentCreateInput {
214
214
  * here — to change an agent's kind, create a new agent.
215
215
  */
216
216
  type AgentUpdateInput = Partial<Omit<AgentCreateInput, 'type'>>;
217
+ interface AgentListOptions {
218
+ /** Server clamps to 100 max; default 20. */
219
+ limit?: number;
220
+ /** Opaque cursor returned as `nextCursor` on the previous page. */
221
+ cursor?: string;
222
+ /** Case-insensitive name prefix filter. */
223
+ q?: string;
224
+ }
225
+ interface AgentListResponse {
226
+ data: Agent[];
227
+ nextCursor: string | null;
228
+ }
229
+ interface AgentListAllOptions {
230
+ /** Case-insensitive name prefix filter, applied to every page. */
231
+ q?: string;
232
+ /** Per-page size mapped to the `limit` query param. Server clamps to 100. */
233
+ pageSize?: number;
234
+ }
217
235
  /**
218
236
  * Trimmed agent shape returned by the consumer catalog
219
237
  * (`GET /v1/orgs/:orgId/agents`). Operator-only fields like
@@ -248,6 +266,42 @@ interface CatalogListInput {
248
266
  */
249
267
  userTags?: string[];
250
268
  }
269
+ /**
270
+ * Portable agent config — an {@link Agent} with server-managed and sensitive
271
+ * fields stripped (identifiers, timestamps, the avatar asset, and the LLM
272
+ * credential reference). This is the shape carried inside export envelopes and
273
+ * accepted by import. Mirrors the server's `PortableAgent`.
274
+ */
275
+ type PortableAgent = Omit<Agent, 'agentId' | 'orgId' | 'createdAt' | 'updatedAt' | 'avatarUrl'>;
276
+ /** Envelope returned by `agents.export(agentId)`. */
277
+ interface SingleAgentEnvelope {
278
+ formatVersion: number;
279
+ exportedAt: string;
280
+ agent: PortableAgent;
281
+ }
282
+ /** Envelope returned by `agents.exportAll()`. */
283
+ interface BulkAgentEnvelope {
284
+ formatVersion: number;
285
+ exportedAt: string;
286
+ agents: PortableAgent[];
287
+ }
288
+ /**
289
+ * Result of `agents.import(envelope)`. Import is best-effort per agent:
290
+ * `imported` holds each created {@link Agent} plus any non-fatal `warnings`
291
+ * (e.g. a cleared knowledge-base reference, or a BYO key that must be re-added
292
+ * before use); `failed` holds the agents that couldn't be created, keyed by
293
+ * name. A partial import returns 200 with entries in both arrays.
294
+ */
295
+ interface AgentImportResult {
296
+ imported: {
297
+ agent: Agent;
298
+ warnings: string[];
299
+ }[];
300
+ failed: {
301
+ name: string;
302
+ error: string;
303
+ }[];
304
+ }
251
305
  type CallStatus = 'queued' | 'ringing' | 'in_progress' | 'completed' | 'failed' | 'no_answer';
252
306
  /**
253
307
  * Media path a call was carried over. Shared by {@link CallRecord.transport}
@@ -790,8 +844,8 @@ type AgentWebhooksResource = ReturnType<typeof createAgentWebhooksResource>;
790
844
 
791
845
  declare const createAgentsResource: (http: HttpClient) => {
792
846
  create: (input: AgentCreateInput) => Promise<Agent>;
793
- list: () => Promise<Agent[]>;
794
- listAll: () => AsyncIterable<Agent>;
847
+ list: (opts?: AgentListOptions) => Promise<AgentListResponse>;
848
+ listAll: (opts?: AgentListAllOptions) => AsyncIterable<Agent>;
795
849
  get: (agentId: string) => Promise<Agent>;
796
850
  update: (agentId: string, patch: AgentUpdateInput) => Promise<Agent>;
797
851
  delete: (agentId: string) => Promise<void>;
@@ -815,6 +869,29 @@ declare const createAgentsResource: (http: HttpClient) => {
815
869
  * returns the agent.
816
870
  */
817
871
  removeAvatar: (agentId: string) => Promise<Agent>;
872
+ /**
873
+ * Export a single agent as a portable envelope. Server-managed and
874
+ * sensitive fields (ids, timestamps, avatar, BYO LLM key) are stripped —
875
+ * the envelope carries only the config needed to recreate the agent via
876
+ * {@link import}. Feed it straight back into `agents.import(envelope)`,
877
+ * in this org or another.
878
+ */
879
+ export: (agentId: string) => Promise<SingleAgentEnvelope>;
880
+ /**
881
+ * Export every agent in the org as one bulk envelope — the same portable
882
+ * shape as {@link export}, with an `agents` array. Round-trips through
883
+ * {@link import} unchanged.
884
+ */
885
+ exportAll: () => Promise<BulkAgentEnvelope>;
886
+ /**
887
+ * Import agents from a single or bulk envelope produced by {@link export} /
888
+ * {@link exportAll}. Best-effort per agent: the result's `imported` array
889
+ * holds each created agent plus non-fatal `warnings` (e.g. a knowledge-base
890
+ * reference cleared because it doesn't exist in the target org, or a BYO
891
+ * LLM key that must be re-added), and `failed` holds the ones that couldn't
892
+ * be created. A partial import still resolves — inspect both arrays.
893
+ */
894
+ import: (envelope: SingleAgentEnvelope | BulkAgentEnvelope) => Promise<AgentImportResult>;
818
895
  webhooks: (agentId: string) => AgentWebhooksResource;
819
896
  };
820
897
  type AgentsResource = ReturnType<typeof createAgentsResource>;
@@ -1050,4 +1127,4 @@ declare class PlatformError extends Error {
1050
1127
 
1051
1128
  declare const verifyWebhookSignature: (rawBody: Buffer | string, signatureHeader: string, secret: string) => boolean;
1052
1129
 
1053
- export { type Agent, type AgentCreateInput, type AgentEndpointing, type AgentHttpTool, type AgentModel, type AgentRecording, type AgentTool, type AgentTranscriber, type AgentType, type AgentUpdateInput, type AgentVoice, type AgentWebhooksResource, type AgentsResource, type AnalysisWire, type ApiErrorCode, type BuildJoinUrlOptions, type CallListFilters, type CallRecord, type CallRecordingUrlResponse, type CallStatus, type CallSummary, type CallTokenMintInput, type CallTokenMintResult, type CallTokenSummary, type CallTokensResource, type CallTransport, type CallsResource, type CatalogAgent, type CatalogListInput, type Chat, type ChatEvent, type ChatsResource, type CostBreakdown, type CreateRoomInput, type CreateRoomResponse, type CreateSpaceInput, type CreditsResource, type EndReason, type EndpointingMode, type GuestRole, type JoinUrlStyle, type KnowledgeBase, type KnowledgeBaseFile, type KnowledgeBasesResource, type LedgerEntry, type ListAnalysisResponse, type ListRoomsResponse, type ListSpacesResponse, type ListUtterancesResponse, type LlmProvider, type MeResource, type MeResponse, type OrgsResource, PlatformClient, type PlatformClientOptions, PlatformError, type PlatformEventName, type RecordingConfig, type RecordingMeta, type RecordingMode, type RoomDoc, type RoomEndReason, type RoomEventAck, type RoomListFilters, type RoomMetricsWire, type RoomStatus, type RoomTranscriptOptions, type RoomsResource, type SpaceKind, type SpacePatch, type SpaceView, type SpacesResource, type SpeechAsset, type SpeechFailedEvent, type SpeechJob, type SpeechListInput, type SpeechReadyEvent, type SpeechResource, type SpeechSynthesizeInput, type StartChatInput, type SttProvider, type TranscriptTurn, type TtsProvider, type UtteranceWire, type WebhookConfig, type WebhookCreateInput, type WebhookDelivery, type WebhookUpdateInput, type WebhooksResource, type WorkerStatus, buildJoinUrl, verifyWebhookSignature };
1130
+ export { type Agent, type AgentCreateInput, type AgentEndpointing, type AgentHttpTool, type AgentImportResult, type AgentListAllOptions, type AgentListOptions, type AgentListResponse, type AgentModel, type AgentRecording, type AgentTool, type AgentTranscriber, type AgentType, type AgentUpdateInput, type AgentVoice, type AgentWebhooksResource, type AgentsResource, type AnalysisWire, type ApiErrorCode, type BuildJoinUrlOptions, type BulkAgentEnvelope, type CallListFilters, type CallRecord, type CallRecordingUrlResponse, type CallStatus, type CallSummary, type CallTokenMintInput, type CallTokenMintResult, type CallTokenSummary, type CallTokensResource, type CallTransport, type CallsResource, type CatalogAgent, type CatalogListInput, type Chat, type ChatEvent, type ChatsResource, type CostBreakdown, type CreateRoomInput, type CreateRoomResponse, type CreateSpaceInput, type CreditsResource, type EndReason, type EndpointingMode, type GuestRole, type JoinUrlStyle, type KnowledgeBase, type KnowledgeBaseFile, type KnowledgeBasesResource, type LedgerEntry, type ListAnalysisResponse, type ListRoomsResponse, type ListSpacesResponse, type ListUtterancesResponse, type LlmProvider, type MeResource, type MeResponse, type OrgsResource, PlatformClient, type PlatformClientOptions, PlatformError, type PlatformEventName, type PortableAgent, type RecordingConfig, type RecordingMeta, type RecordingMode, type RoomDoc, type RoomEndReason, type RoomEventAck, type RoomListFilters, type RoomMetricsWire, type RoomStatus, type RoomTranscriptOptions, type RoomsResource, type SingleAgentEnvelope, type SpaceKind, type SpacePatch, type SpaceView, type SpacesResource, type SpeechAsset, type SpeechFailedEvent, type SpeechJob, type SpeechListInput, type SpeechReadyEvent, type SpeechResource, type SpeechSynthesizeInput, type StartChatInput, type SttProvider, type TranscriptTurn, type TtsProvider, type UtteranceWire, type WebhookConfig, type WebhookCreateInput, type WebhookDelivery, type WebhookUpdateInput, type WebhooksResource, type WorkerStatus, buildJoinUrl, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -308,17 +308,27 @@ var createAgentWebhooksResource = (http, agentId) => ({
308
308
  var createAgentsResource = (http) => {
309
309
  const agents = {
310
310
  create: async (input) => http.request({ method: "POST", path: "/v1/agents", body: input }),
311
- list: async () => {
312
- const res = await http.request({ method: "GET", path: "/v1/agents" });
313
- return res.data;
311
+ // One page of agents. Opaque cursor pagination (`nextCursor` is whatever
312
+ // the server needs to resume don't parse it client-side). Returns the
313
+ // full `{ data, nextCursor }` envelope; pass `nextCursor` back as `cursor`
314
+ // to walk pages, or use `listAll` to iterate every agent.
315
+ list: async (opts = {}) => {
316
+ const query = {};
317
+ if (opts.limit !== void 0) query.limit = opts.limit;
318
+ if (opts.cursor) query.cursor = opts.cursor;
319
+ if (opts.q) query.q = opts.q;
320
+ return http.request({ method: "GET", path: "/v1/agents", query });
314
321
  },
315
- // Async iterator for "give me every agent" — v1 server returns the full
316
- // list in one page, so this is just a thin convenience. When the server
317
- // picks up cursor pagination, this is the method that papers over that
318
- // migration without consumer changes.
319
- listAll: async function* () {
320
- const page = await agents.list();
321
- for (const a of page) yield a;
322
+ // Async iterator for "give me every agent" — walks the cursor internally,
323
+ // fetching one page at a time and yielding each agent, so consumers never
324
+ // have to thread `nextCursor` themselves.
325
+ listAll: async function* (opts = {}) {
326
+ let cursor;
327
+ do {
328
+ const page = await agents.list({ q: opts.q, limit: opts.pageSize, cursor });
329
+ for (const a of page.data) yield a;
330
+ cursor = page.nextCursor ?? void 0;
331
+ } while (cursor);
322
332
  },
323
333
  get: async (agentId) => http.request({ method: "GET", path: `/v1/agents/${agentId}` }),
324
334
  update: async (agentId, patch) => http.request({ method: "PATCH", path: `/v1/agents/${agentId}`, body: patch }),
@@ -352,6 +362,36 @@ var createAgentsResource = (http) => {
352
362
  * returns the agent.
353
363
  */
354
364
  removeAvatar: async (agentId) => http.request({ method: "DELETE", path: `/v1/agents/${agentId}/avatar` }),
365
+ /**
366
+ * Export a single agent as a portable envelope. Server-managed and
367
+ * sensitive fields (ids, timestamps, avatar, BYO LLM key) are stripped —
368
+ * the envelope carries only the config needed to recreate the agent via
369
+ * {@link import}. Feed it straight back into `agents.import(envelope)`,
370
+ * in this org or another.
371
+ */
372
+ export: async (agentId) => http.request({
373
+ method: "GET",
374
+ path: `/v1/agents/${agentId}/export`
375
+ }),
376
+ /**
377
+ * Export every agent in the org as one bulk envelope — the same portable
378
+ * shape as {@link export}, with an `agents` array. Round-trips through
379
+ * {@link import} unchanged.
380
+ */
381
+ exportAll: async () => http.request({ method: "GET", path: "/v1/agents/export" }),
382
+ /**
383
+ * Import agents from a single or bulk envelope produced by {@link export} /
384
+ * {@link exportAll}. Best-effort per agent: the result's `imported` array
385
+ * holds each created agent plus non-fatal `warnings` (e.g. a knowledge-base
386
+ * reference cleared because it doesn't exist in the target org, or a BYO
387
+ * LLM key that must be re-added), and `failed` holds the ones that couldn't
388
+ * be created. A partial import still resolves — inspect both arrays.
389
+ */
390
+ import: async (envelope) => http.request({
391
+ method: "POST",
392
+ path: "/v1/agents/import",
393
+ body: envelope
394
+ }),
355
395
  // Nested resource. Per-agent webhook CRUD lives at
356
396
  // /v1/agents/:agentId/webhooks/* — we expose it as a factory keyed on
357
397
  // agentId so consumers can bind once and reuse:
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/me.ts","../src/resources/agentWebhooks.ts","../src/resources/agents.ts","../src/resources/calls.ts","../src/resources/knowledgeBases.ts","../src/resources/credits.ts","../src/resources/callTokens.ts","../src/resources/webhooks.ts","../src/resources/orgs.ts","../src/resources/rooms.ts","../src/joinUrl.ts","../src/resources/spaces.ts","../src/resources/chats.ts","../src/resources/speech.ts","../src/PlatformClient.ts","../src/verify.ts"],"sourcesContent":["// Public API of @craftedxp/sdk-node.\n\nexport { PlatformClient } from './PlatformClient'\nexport type { PlatformClientOptions } from './PlatformClient'\n\n// Error class for `instanceof` checks in consumer code.\nexport { PlatformError } from './errors'\nexport type { ApiErrorCode } from './errors'\n\n// Webhook signature verification helper — standalone so frameworks\n// (Express, Koa, Next.js route handlers) can use it without instantiating\n// a PlatformClient.\nexport { verifyWebhookSignature } from './verify'\n\n// Standalone join-URL builder — pure, client-independent so it can run\n// anywhere the developer mints a participant link.\nexport { buildJoinUrl } from './joinUrl'\nexport type { JoinUrlStyle, BuildJoinUrlOptions } from './joinUrl'\n\n// Re-export DTO types. Consumers often type their own storage models\n// against these — re-exporting avoids `import type` gymnastics.\nexport type * from './types'\n\n// Advanced: expose the resource types for consumers subclassing / wrapping\n// the client. 99% of users don't need these.\nexport type { MeResource } from './resources/me'\nexport type { AgentsResource } from './resources/agents'\nexport type { AgentWebhooksResource } from './resources/agentWebhooks'\nexport type { CallsResource } from './resources/calls'\nexport type { KnowledgeBasesResource } from './resources/knowledgeBases'\nexport type { CreditsResource } from './resources/credits'\nexport type { CallTokensResource } from './resources/callTokens'\nexport type { WebhooksResource } from './resources/webhooks'\nexport type { OrgsResource } from './resources/orgs'\nexport type { RoomsResource } from './resources/rooms'\nexport type { SpacesResource } from './resources/spaces'\nexport type { ChatsResource } from './resources/chats'\nexport type { StartChatInput, Chat } from './resources/chats'\nexport type { SpeechResource } from './resources/speech'\n","// Typed error class mirroring the server's ErrorV1 shape:\n// { error: { code, message, field?, docs_url? } }\n//\n// Consumers do `err instanceof PlatformError` to branch on code without\n// parsing string messages. `status` carries the HTTP code for the 1% of\n// cases where the code field isn't enough (e.g. rate-limit → retry-after\n// header correlation).\n\nexport type ApiErrorCode =\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'bad_request'\n | 'conflict'\n | 'rate_limited'\n | 'payment_required'\n | 'internal_error'\n | 'accepted'\n | 'service_unavailable'\n | 'unknown'\n\nexport class PlatformError extends Error {\n readonly code: ApiErrorCode\n readonly status: number\n readonly field?: string\n readonly docsUrl?: string\n // The raw response body for debugging. Intentionally optional — we clear\n // it on `error.toJSON()` so logging libraries don't dump the whole\n // server response into production logs.\n readonly body?: unknown\n\n constructor(params: {\n code: ApiErrorCode\n message: string\n status: number\n field?: string\n docsUrl?: string\n body?: unknown\n }) {\n super(params.message)\n this.name = 'PlatformError'\n this.code = params.code\n this.status = params.status\n this.field = params.field\n this.docsUrl = params.docsUrl\n this.body = params.body\n // Preserve the stack trace — Node's Error doesn't capture it\n // automatically when subclassing in some older runtimes.\n if (\n typeof (Error as typeof Error & { captureStackTrace?: unknown }).captureStackTrace ===\n 'function'\n ) {\n ;(\n Error as typeof Error & { captureStackTrace: (target: unknown, ctor: unknown) => void }\n ).captureStackTrace(this, PlatformError)\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n field: this.field,\n docsUrl: this.docsUrl,\n }\n }\n}\n","import { PlatformError, type ApiErrorCode } from './errors'\n\n// Low-level HTTP wrapper around `fetch` (native in Node 18+). Every resource\n// method routes through here for consistent auth + error handling + retries.\n//\n// v1 scope: JSON-in / JSON-out + multipart for file uploads. No streaming —\n// call WebSockets go through @craftedxp/voice-rn (the React Native client)\n// or a web equivalent, not this server-side SDK.\n\nexport interface HttpClientOptions {\n apiKey: string\n baseUrl: string\n // Default 30s. File uploads can override per-request.\n timeoutMs?: number\n // 429 + 5xx retries. Defaults to 3 attempts (original + 2 retries) with\n // exponential backoff (250ms, 1s). Set to 0 to disable.\n maxRetries?: number\n // Optional — lets consumers swap in a test/mock fetch (or a custom one\n // with instrumentation). Defaults to the global.\n fetch?: typeof fetch\n // Optional — a callback that fires per-request with the final status and\n // duration. Handy for dropping traces into the consumer's observability\n // stack without wrapping each call.\n onRequest?: (info: {\n method: string\n url: string\n status: number\n durationMs: number\n attempt: number\n }) => void\n}\n\nexport interface HttpRequest {\n method: 'GET' | 'POST' | 'PATCH' | 'DELETE'\n path: string // starts with `/`, e.g. `/v1/agents/123`\n query?: Record<string, string | number | boolean | undefined>\n body?: unknown // JSON-serialised — for multipart use `formData`\n formData?: FormData\n timeoutMs?: number\n // Exposed for edge cases where a resource wants to tack on extra\n // headers (we don't have any today but leave the hook).\n headers?: Record<string, string>\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst DEFAULT_MAX_RETRIES = 2 // total attempts = 3\n\nconst buildUrl = (baseUrl: string, path: string, query?: HttpRequest['query']): string => {\n const u = new URL(path, baseUrl)\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue\n u.searchParams.set(k, String(v))\n }\n }\n return u.toString()\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\nconst isRetryable = (status: number): boolean => status === 429 || (status >= 500 && status < 600)\n\n// Parse whatever the server returned into a PlatformError. Falls back to\n// synthesising a sensible error for non-JSON responses.\nconst errorFromResponse = async (res: Response): Promise<PlatformError> => {\n const bodyText = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = bodyText ? JSON.parse(bodyText) : undefined\n } catch {\n // non-JSON response (e.g. an HTML error page from a proxy). Fall\n // through with parsed = undefined.\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n return new PlatformError({\n code: (errObj?.code as ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? bodyText,\n })\n}\n\nexport const createHttpClient = (opts: HttpClientOptions) => {\n const fetchImpl = opts.fetch ?? globalThis.fetch\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n\n if (!fetchImpl) {\n throw new Error('No global fetch available. @craftedxp/sdk-node requires Node >= 18.')\n }\n\n const request = async <T>(req: HttpRequest): Promise<T> => {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n Accept: 'application/json',\n ...(req.headers ?? {}),\n }\n\n // Body shaping: prefer formData when provided; otherwise JSON.\n // `FormData` sets its own Content-Type with boundary — don't preempt it.\n // Typed as `string | FormData | undefined` (a subset of the global\n // BodyInit) so we don't need DOM lib types in this server-side SDK.\n let body: string | FormData | undefined\n if (req.formData) {\n body = req.formData\n } else if (req.body !== undefined) {\n headers['Content-Type'] = 'application/json'\n body = JSON.stringify(req.body)\n }\n\n const reqTimeout = req.timeoutMs ?? timeoutMs\n let lastErr: unknown\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), reqTimeout)\n const started = Date.now()\n try {\n const res = await fetchImpl(url, {\n method: req.method,\n headers,\n body,\n signal: controller.signal,\n })\n const durationMs = Date.now() - started\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs,\n attempt: attempt + 1,\n })\n\n if (res.ok) {\n // 204 No Content — some endpoints (DELETE webhooks) return nothing.\n if (res.status === 204) return undefined as T\n const ct = res.headers.get('content-type') ?? ''\n if (ct.includes('application/json')) {\n return (await res.json()) as T\n }\n // Non-JSON 2xx — rare. Return raw text cast as T.\n return (await res.text()) as unknown as T\n }\n\n // Retryable error: back off + retry up to maxRetries.\n if (isRetryable(res.status) && attempt < maxRetries) {\n const retryAfter = res.headers.get('Retry-After')\n const backoff = retryAfter ? Number(retryAfter) * 1000 : 250 * Math.pow(2, attempt)\n await sleep(backoff)\n continue\n }\n\n throw await errorFromResponse(res)\n } catch (err) {\n if (err instanceof PlatformError) throw err\n // AbortError / network / DNS failures — retry up to maxRetries.\n if (attempt < maxRetries) {\n lastErr = err\n await sleep(250 * Math.pow(2, attempt))\n continue\n }\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n } finally {\n clearTimeout(timer)\n }\n }\n\n // Should be unreachable — the loop either returns, throws, or continues.\n throw lastErr instanceof Error ? lastErr : new Error('request exhausted retries')\n }\n\n // SSE streaming: returns an AsyncIterable<T> over parsed SSE events.\n // Each event is the JSON `data` payload merged with the `event` name as\n // `type`. No retry/backoff mid-stream (stream != request); the caller\n // should treat `error` events from the typed channel as the error path.\n async function* stream<T = unknown>(req: HttpRequest): AsyncIterable<T> {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...(req.headers ?? {}),\n }\n\n const started = Date.now()\n let res: Response\n try {\n res = await fetchImpl(url, {\n method: req.method,\n headers,\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n }\n\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs: Date.now() - started,\n attempt: 1,\n })\n\n if (!res.ok || !res.body) {\n const text = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = text ? JSON.parse(text) : undefined\n } catch {\n // non-JSON — fall through\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n throw new PlatformError({\n code: (errObj?.code as import('./errors').ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? text,\n })\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buf = ''\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) return\n buf += decoder.decode(value, { stream: true })\n let idx: number\n while ((idx = buf.indexOf('\\n\\n')) >= 0) {\n const block = buf.slice(0, idx)\n buf = buf.slice(idx + 2)\n let event = 'message'\n let data = ''\n for (const line of block.split('\\n')) {\n if (line.startsWith(':')) continue // SSE comment / keepalive\n if (line.startsWith('event:')) event = line.slice(6).trim()\n else if (line.startsWith('data:')) data += line.slice(5).trim()\n }\n if (!data) continue\n try {\n const parsed = JSON.parse(data)\n yield { type: event, ...parsed } as T\n } catch {\n // Drop malformed JSON; an error event will arrive via the typed channel.\n }\n }\n }\n }\n\n return { request, stream }\n}\n\nexport type HttpClient = ReturnType<typeof createHttpClient>\n","import type { HttpClient } from '../http'\nimport type { MeResponse } from '../types'\n\nexport const createMeResource = (http: HttpClient) => ({\n // Smoke test — confirms the API key is valid and returns the org + balance.\n // Most consumers use this as a ping on startup.\n get: async (): Promise<MeResponse> => http.request<MeResponse>({ method: 'GET', path: '/v1/me' }),\n})\n\nexport type MeResource = ReturnType<typeof createMeResource>\n","import type { HttpClient } from '../http'\nimport type {\n WebhookConfig,\n WebhookCreateInput,\n WebhookDelivery,\n WebhookUpdateInput,\n} from '../types'\n\n// Per-agent webhook resource, scoped at factory time to a specific agentId.\n// All endpoints mirror /v1/agents/:agentId/webhooks[/:id].\n\nexport const createAgentWebhooksResource = (http: HttpClient, agentId: string) => ({\n // Returns the webhook + its signing secret. The secret is only present in\n // this response — persist immediately, subsequent GETs omit it. Use the\n // secret to verify `X-Platform-Signature-256` (sha256=hex HMAC over body).\n create: async (input: WebhookCreateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks`,\n body: input,\n }),\n\n list: async (): Promise<WebhookConfig[]> => {\n const res = await http.request<{ data: WebhookConfig[] }>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks`,\n })\n return res.data\n },\n\n get: async (webhookId: string): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n update: async (webhookId: string, patch: WebhookUpdateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'PATCH',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n body: patch,\n }),\n\n delete: async (webhookId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n // Fires a synthetic call.started against this webhook and returns the\n // full delivery record (including attempt status codes) once the retry\n // sequence has finished. Useful during setup to verify receiver wiring.\n test: async (webhookId: string): Promise<WebhookDelivery> =>\n http.request<WebhookDelivery>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}/test`,\n }),\n})\n\nexport type AgentWebhooksResource = ReturnType<typeof createAgentWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type { Agent, AgentCreateInput, AgentUpdateInput } from '../types'\nimport { createAgentWebhooksResource, type AgentWebhooksResource } from './agentWebhooks'\n\n// The server returns Agent with `apiKey` stripped and `hasApiKey: boolean`\n// on the model. We type the happy path but keep our input type permissive\n// so consumers can POST a plaintext `apiKey` that the server encrypts +\n// swaps for `apiKeySecret` on persistence.\n\nexport const createAgentsResource = (http: HttpClient) => {\n const agents = {\n create: async (input: AgentCreateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'POST', path: '/v1/agents', body: input }),\n\n list: async (): Promise<Agent[]> => {\n const res = await http.request<{ data: Agent[] }>({ method: 'GET', path: '/v1/agents' })\n return res.data\n },\n\n // Async iterator for \"give me every agent\" — v1 server returns the full\n // list in one page, so this is just a thin convenience. When the server\n // picks up cursor pagination, this is the method that papers over that\n // migration without consumer changes.\n listAll: async function* (): AsyncIterable<Agent> {\n const page = await agents.list()\n for (const a of page) yield a\n },\n\n get: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'GET', path: `/v1/agents/${agentId}` }),\n\n update: async (agentId: string, patch: AgentUpdateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'PATCH', path: `/v1/agents/${agentId}`, body: patch }),\n\n delete: async (agentId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/agents/${agentId}` }),\n\n /**\n * Upload an avatar image for the agent. Server re-encodes to a 512×512\n * WebP and stores it in the public-read avatars bucket; the returned\n * `Agent` has `avatarUrl` set to the canonical public URL with a\n * `?v=` cache-buster.\n *\n * `file` accepts a Node Buffer / Uint8Array / Blob / typed-array.\n * `filename` and `contentType` are optional but help the server log\n * meaningful errors when something rejects.\n */\n uploadAvatar: async (\n agentId: string,\n file: Buffer | Uint8Array | Blob | ArrayBuffer,\n opts: { filename?: string; contentType?: string } = {},\n ): Promise<Agent> => {\n const fd = new FormData()\n const blob =\n file instanceof Blob\n ? file\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Blob([file as any], { type: opts.contentType ?? 'application/octet-stream' })\n fd.append('file', blob, opts.filename ?? 'avatar')\n return http.request<Agent>({\n method: 'POST',\n path: `/v1/agents/${agentId}/avatar`,\n formData: fd,\n })\n },\n\n /**\n * Remove the agent's avatar — both the GCS object and the `avatarUrl`\n * field. Idempotent: calling on an agent without an avatar still\n * returns the agent.\n */\n removeAvatar: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'DELETE', path: `/v1/agents/${agentId}/avatar` }),\n\n // Nested resource. Per-agent webhook CRUD lives at\n // /v1/agents/:agentId/webhooks/* — we expose it as a factory keyed on\n // agentId so consumers can bind once and reuse:\n //\n // const hooks = client.agents.webhooks(myAgentId)\n // await hooks.create({ url, events })\n // await hooks.list()\n webhooks: (agentId: string): AgentWebhooksResource =>\n createAgentWebhooksResource(http, agentId),\n }\n return agents\n}\n\nexport type AgentsResource = ReturnType<typeof createAgentsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CallListFilters,\n CallRecord,\n CallRecordingUrlResponse,\n CallSummary,\n TranscriptTurn,\n} from '../types'\n\nexport const createCallsResource = (http: HttpClient) => {\n const calls = {\n list: async (filters: CallListFilters = {}): Promise<CallSummary[]> => {\n const res = await http.request<{ data: CallSummary[] }>({\n method: 'GET',\n path: '/v1/calls',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination helper. v1 server caps at limit (max 200) in a single\n // page — consumers who ask for \"every call since X\" get that page, the\n // iterator ends. This is the shape we'd extend with cursor support\n // without breaking callers.\n listAll: async function* (filters: CallListFilters = {}): AsyncIterable<CallSummary> {\n const page = await calls.list(filters)\n for (const c of page) yield c\n },\n\n get: async (callId: string): Promise<CallRecord> =>\n http.request<CallRecord>({ method: 'GET', path: `/v1/calls/${callId}` }),\n\n transcript: async (callId: string): Promise<{ callId: string; transcript: TranscriptTurn[] }> =>\n http.request<{ callId: string; transcript: TranscriptTurn[] }>({\n method: 'GET',\n path: `/v1/calls/${callId}/transcript`,\n }),\n\n // Returns a V4 signed URL (1-hour TTL). `ready: false` + `artifact:\n // 'caller-raw'` means the async mix job hasn't finished — the URL still\n // points at a playable file (raw caller PCM).\n recording: async (callId: string): Promise<CallRecordingUrlResponse> =>\n http.request<CallRecordingUrlResponse>({\n method: 'GET',\n path: `/v1/calls/${callId}/recording`,\n }),\n\n // Outbound dialling (POST /v1/calls) + in-call control are Phase 1.4.4 /\n // 1.4.5 respectively — blocked on telephony. Surface clear \"not built\n // yet\" errors here if consumers guess those method names, so they don't\n // silently hit a non-existent endpoint and wonder why it 404s.\n }\n return calls\n}\n\nexport type CallsResource = ReturnType<typeof createCallsResource>\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { HttpClient } from '../http'\nimport type { KnowledgeBase, KnowledgeBaseFile } from '../types'\n\nexport const createKnowledgeBasesResource = (http: HttpClient) => ({\n create: async (name: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({\n method: 'POST',\n path: '/v1/knowledge-bases',\n body: { name },\n }),\n\n list: async (): Promise<KnowledgeBase[]> => {\n const res = await http.request<{ data: KnowledgeBase[] }>({\n method: 'GET',\n path: '/v1/knowledge-bases',\n })\n return res.data\n },\n\n get: async (kbId: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({ method: 'GET', path: `/v1/knowledge-bases/${kbId}` }),\n\n delete: async (kbId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/knowledge-bases/${kbId}` }),\n\n // File upload — accepts either a local file path OR raw bytes + filename.\n // The server ingests synchronously today (Phase 3.1.4 Cloud Tasks is a\n // followup), so the returned file has status='ready' in most cases.\n uploadFile: async (\n kbId: string,\n source:\n | { path: string; filename?: string; mimeType?: string }\n | { data: Buffer | Uint8Array; filename: string; mimeType?: string },\n ): Promise<KnowledgeBaseFile> => {\n const form = new FormData()\n let blob: Blob\n let filename: string\n let mime: string\n\n if ('path' in source) {\n const buf = await fs.promises.readFile(source.path)\n filename = source.filename ?? path.basename(source.path)\n mime = source.mimeType ?? 'application/octet-stream'\n blob = new Blob([new Uint8Array(buf)], { type: mime })\n } else {\n filename = source.filename\n mime = source.mimeType ?? 'application/octet-stream'\n const bytes = source.data instanceof Buffer ? new Uint8Array(source.data) : source.data\n blob = new Blob([bytes], { type: mime })\n }\n form.append('file', blob, filename)\n\n // File uploads can take a while (OCR, chunking, embedding) — give them\n // room before timing out. 5 min cap matches the server's own\n // processing budget.\n return http.request<KnowledgeBaseFile>({\n method: 'POST',\n path: `/v1/knowledge-bases/${kbId}/files`,\n formData: form,\n timeoutMs: 5 * 60 * 1000,\n })\n },\n\n listFiles: async (kbId: string): Promise<KnowledgeBaseFile[]> => {\n const res = await http.request<{ data: KnowledgeBaseFile[] }>({\n method: 'GET',\n path: `/v1/knowledge-bases/${kbId}/files`,\n })\n return res.data\n },\n\n deleteFile: async (kbId: string, fileId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/knowledge-bases/${kbId}/files/${fileId}`,\n }),\n})\n\nexport type KnowledgeBasesResource = ReturnType<typeof createKnowledgeBasesResource>\n","import type { HttpClient } from '../http'\nimport type { LedgerEntry } from '../types'\n\nexport const createCreditsResource = (http: HttpClient) => {\n const credits = {\n getBalance: async (): Promise<{ orgId: string; balanceCents: number }> =>\n http.request<{ orgId: string; balanceCents: number }>({\n method: 'GET',\n path: '/v1/credits/balance',\n }),\n\n // Returns ledger entries newest-first. `limit` is capped at 500 server-side.\n getLedger: async (opts: { limit?: number } = {}): Promise<LedgerEntry[]> => {\n const res = await http.request<{ data: LedgerEntry[] }>({\n method: 'GET',\n path: '/v1/credits/ledger',\n query: opts as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination scaffold — v1 server returns a single page up to\n // limit=500. When cursor pagination lands this is where it gets wired.\n getLedgerAll: async function* (opts: { limit?: number } = {}): AsyncIterable<LedgerEntry> {\n const page = await credits.getLedger(opts)\n for (const e of page) yield e\n },\n }\n return credits\n}\n\nexport type CreditsResource = ReturnType<typeof createCreditsResource>\n","import type { HttpClient } from '../http'\nimport type { CallTokenMintInput, CallTokenMintResult, CallTokenSummary } from '../types'\n\n// Short-lived, agent-scoped `ct_` tokens. Mint one per user session on your\n// backend, hand the raw value to the browser; let this SDK handle lifecycle\n// (revoke on sign-out, list active tokens for an admin panel).\n\nexport const createCallTokensResource = (http: HttpClient) => ({\n // Returns the RAW token value once. Don't log it; pass it straight to the\n // browser + discard server-side. `tokenId` is the stable public handle\n // used for revocation later.\n mint: async (input: CallTokenMintInput): Promise<CallTokenMintResult> =>\n http.request<CallTokenMintResult>({\n method: 'POST',\n path: '/v1/call-tokens',\n body: input,\n }),\n\n // Live tokens only by default. Pass includeRevoked/includeExpired when\n // debugging \"why did my token stop working\".\n list: async (\n opts: { includeRevoked?: boolean; includeExpired?: boolean; limit?: number } = {},\n ): Promise<CallTokenSummary[]> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.includeRevoked) query.includeRevoked = '1'\n if (opts.includeExpired) query.includeExpired = '1'\n if (opts.limit !== undefined) query.limit = opts.limit\n const res = await http.request<{ data: CallTokenSummary[] }>({\n method: 'GET',\n path: '/v1/call-tokens',\n query,\n })\n return res.data\n },\n\n // Idempotent — re-revoking a revoked token returns `alreadyRevoked: true`.\n revoke: async (\n tokenId: string,\n ): Promise<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }> =>\n http.request<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }>({\n method: 'DELETE',\n path: `/v1/call-tokens/${tokenId}`,\n }),\n})\n\nexport type CallTokensResource = ReturnType<typeof createCallTokensResource>\n","import type { HttpClient } from '../http'\nimport type { WebhookDelivery } from '../types'\n\n// Org-wide webhook delivery log. Per-agent CRUD lives on\n// `client.agents.webhooks(agentId)` — this namespace is just the\n// read-only deliveries surface that spans all agents.\n\nexport const createWebhooksResource = (http: HttpClient) => ({\n deliveries: async (\n filters: {\n agentId?: string\n webhookId?: string\n callId?: string\n limit?: number\n } = {},\n ): Promise<WebhookDelivery[]> => {\n const res = await http.request<{ data: WebhookDelivery[] }>({\n method: 'GET',\n path: '/v1/webhooks/deliveries',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n})\n\nexport type WebhooksResource = ReturnType<typeof createWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type { CatalogAgent, CatalogListInput } from '../types'\n\n// Org-scoped consumer endpoints. Today this is just the agent catalog —\n// the trimmed shape your mobile app picks an agent from. Lives at\n// `/v1/orgs/:orgId/agents` rather than reusing the admin `/v1/agents/*`\n// tree on purpose: the catalog response intentionally excludes the\n// operator-only fields (system prompt, tools, KB IDs) so a leaked\n// consumer-backend `sk_` can't lift the operator config out of it.\n//\n// Pattern from your backend:\n//\n// const client = new PlatformClient({ apiKey: process.env.SK })\n// const visible = await client.orgs.listAgents({\n// orgId: process.env.ORG_ID!,\n// userTags: ['tier1'], // omit to get the unfiltered admin view\n// })\n// res.json(visible)\n\nexport const createOrgsResource = (http: HttpClient) => ({\n /**\n * Fetch the agent catalog for an org. Returns the consumer-trimmed shape;\n * no system prompt, tools, or KB info. Use `client.agents.get(agentId)`\n * with the same `sk_` if you need the full admin shape.\n *\n * `userTags`: end-user entitlement tags. When supplied, hides agents\n * whose `allowedUserTags` is non-empty and doesn't intersect with the\n * supplied list. Omit to get the unfiltered admin view.\n *\n * Persona / category filtering is intentionally not a server param —\n * filter the returned list client-side over `name`s if you need it.\n *\n * Throws `403 forbidden` if `orgId` doesn't match the key's org.\n */\n listAgents: async (input: CatalogListInput): Promise<CatalogAgent[]> => {\n const query: Record<string, string | undefined> = {}\n if (input.userTags && input.userTags.length > 0) query.userTags = input.userTags.join(',')\n const res = await http.request<{ data: CatalogAgent[] }>({\n method: 'GET',\n path: `/v1/orgs/${input.orgId}/agents`,\n query,\n })\n return res.data\n },\n})\n\nexport type OrgsResource = ReturnType<typeof createOrgsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CreateRoomInput,\n CreateRoomResponse,\n ListAnalysisResponse,\n ListRoomsResponse,\n ListUtterancesResponse,\n RoomDoc,\n RoomEventAck,\n RoomListFilters,\n RoomTranscriptOptions,\n} from '../types'\n\n// REST wrappers for /v1/rooms — the multi-party video room surface (see\n// docs/superpowers/specs/2026-05-18-multiparty-rooms-design.md). Lets\n// server-side code mint rooms, fetch state, list transcript pages, and end\n// rooms programmatically.\n//\n// Auth model: same Bearer `sk_` flow as every other resource — the server\n// derives `orgId` from the API key, so the SDK never sets a tenant header.\n//\n// Host actions beyond `end` (promote/demote/kick) and observer-token mint\n// live in the server but aren't surfaced here yet — they're operator-side\n// flows that the dashboard hits directly. Re-add here if a programmatic\n// use-case shows up (CI tests, integration harnesses).\n\nexport const createRoomsResource = (http: HttpClient) => ({\n // Provision a new room. Server returns 201 with ONE shared room-level\n // `joinToken` + `joinUrl`. Share the single link with everyone you want in\n // the room — each visitor supplies their own display name at join time and\n // becomes a fresh, distinct participant. The server persists only the\n // token's sha256 hash; the raw token is returned here once and never stored.\n create: async (input: CreateRoomInput): Promise<CreateRoomResponse> =>\n http.request<CreateRoomResponse>({\n method: 'POST',\n path: '/v1/rooms',\n body: input,\n }),\n\n // Listing — opaque cursor pagination (`nextCursor` returned by the server\n // is whatever startAfter() needs, don't parse client-side).\n list: async (filters: RoomListFilters = {}): Promise<ListRoomsResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (filters.status) query.status = filters.status\n if (filters.limit !== undefined) query.limit = filters.limit\n if (filters.cursor) query.cursor = filters.cursor\n return http.request<ListRoomsResponse>({\n method: 'GET',\n path: '/v1/rooms',\n query,\n })\n },\n\n // Fetch a single room. 404s are surfaced as PlatformError('not_found').\n get: async (roomId: string): Promise<RoomDoc> =>\n http.request<RoomDoc>({\n method: 'GET',\n path: `/v1/rooms/${roomId}`,\n }),\n\n // Transcript pages — utterances are ordered by `startedAt asc`. Cursor is\n // an ISO timestamp (server enforces, don't construct yourself).\n transcript: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListUtterancesResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListUtterancesResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/transcript`,\n query,\n })\n },\n\n // Analysis pages — analyzer results ordered by createdAt asc. Auth-only\n // (org-scoped); there is no public token-gated variant. Cursor is an ISO\n // timestamp the server enforces — don't construct it yourself.\n analysis: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListAnalysisResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListAnalysisResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/analysis`,\n query,\n })\n },\n\n // End a room — async on the server: returns 202 + eventId once the\n // controlEvents entry is written. The room-worker observes the entry,\n // broadcasts the system message, and tears down LiveKit shortly after.\n end: async (roomId: string): Promise<RoomEventAck> =>\n http.request<RoomEventAck>({\n method: 'POST',\n path: `/v1/rooms/${roomId}/end`,\n }),\n})\n\nexport type RoomsResource = ReturnType<typeof createRoomsResource>\n","import type { SpaceView } from './types'\n\nexport type JoinUrlStyle = 'query' | 'path'\n\nexport interface BuildJoinUrlOptions {\n /** Required — the developer's own page that renders <VoiceRoom/>. Absolute URL. */\n baseUrl: string\n /** 'query' (default) → ?code=… · 'path' → /… */\n style?: JoinUrlStyle\n}\n\n/**\n * Build a participant join URL on the DEVELOPER's domain from a space (or a\n * raw code). White-label: the returned URL never points at a voissia origin —\n * `baseUrl` is required and supplied by the caller.\n *\n * The default `query` style appends `?code=<code>`, matching how\n * `<VoiceRoom/>` (and the example app) read the code from the URL.\n */\nexport const buildJoinUrl = (\n spaceOrCode: SpaceView | string,\n opts: BuildJoinUrlOptions,\n): string => {\n const code = typeof spaceOrCode === 'string' ? spaceOrCode : spaceOrCode.code\n if (!code) throw new Error('a space code is required')\n const base = opts.baseUrl?.trim()\n if (!base) throw new Error('baseUrl is required')\n\n let url: URL\n try {\n url = new URL(base)\n } catch {\n throw new Error('baseUrl must be an absolute URL (e.g. https://app.example.com/room)')\n }\n if ((opts.style ?? 'query') === 'path') {\n url.pathname = `${url.pathname.replace(/\\/+$/, '')}/${encodeURIComponent(code)}`\n } else {\n url.searchParams.set('code', code)\n }\n return url.toString()\n}\n","import type { HttpClient } from '../http'\nimport type { CreateSpaceInput, ListSpacesResponse, SpacePatch, SpaceView } from '../types'\nimport { buildJoinUrl, type BuildJoinUrlOptions } from '../joinUrl'\n\n// REST wrappers for /v1/spaces — durable multi-party meeting spaces (Phase 23,\n// see docs/superpowers/specs/2026-06-14-spaces-sdk-resource-design.md). Lets\n// server-side code create spaces, read/update/delete them, and build a\n// participant join URL on the developer's own domain.\n//\n// Auth: same Bearer `sk_` flow as every resource — orgId is derived server-side,\n// so the SDK never sets a tenant header.\n//\n// Out of scope here (client/host-side, in @craftedxp/voice-room-react): the\n// public join exchange, host-token mint, lobby admit/deny, moderation, and the\n// X-Host-Key-gated recording controls.\n\nexport const createSpacesResource = (http: HttpClient) => ({\n // Create a durable space. Server returns 201 with the SpaceView, including a\n // stable `code` participants join with. Pass it to `joinUrl` (or the\n // standalone `buildJoinUrl`) to make a link on YOUR domain.\n create: async (input: CreateSpaceInput): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'POST',\n path: '/v1/spaces',\n body: input,\n }),\n\n get: async (spaceId: string): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'GET',\n path: `/v1/spaces/${spaceId}`,\n }),\n\n // List every space for the org. Plain `{ data: [...] }` — no cursor.\n list: async (): Promise<ListSpacesResponse> =>\n http.request<ListSpacesResponse>({\n method: 'GET',\n path: '/v1/spaces',\n }),\n\n // Patch a space. The server rejects an empty patch — pass at least one field.\n update: async (spaceId: string, patch: SpacePatch): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'PATCH',\n path: `/v1/spaces/${spaceId}`,\n body: patch,\n }),\n\n // Delete a space. Resolves once the server returns 204.\n delete: async (spaceId: string): Promise<void> => {\n await http.request<void>({\n method: 'DELETE',\n path: `/v1/spaces/${spaceId}`,\n })\n },\n\n // Convenience: build a participant join URL on your domain from a space (or\n // raw code). Delegates to the standalone `buildJoinUrl` export.\n joinUrl: (spaceOrCode: SpaceView | string, opts: BuildJoinUrlOptions): string =>\n buildJoinUrl(spaceOrCode, opts),\n})\n\nexport type SpacesResource = ReturnType<typeof createSpacesResource>\n","import type { HttpClient } from '../http'\nimport type { ChatEvent } from '../types'\n\nexport interface StartChatInput {\n agentId: string\n text?: string\n}\n\nexport interface Chat {\n id: string\n callId: string\n greeting: AsyncIterable<ChatEvent>\n send(text: string): AsyncIterable<ChatEvent>\n end(): Promise<void>\n}\n\n/**\n * Per-token chat resource. Construct via `client.chatsFor(callToken)` —\n * `callToken` is a raw `ct_…` minted with `channel: 'text'`.\n *\n * The async iterables stream SSE events: `chat.started` (start only),\n * then a sequence of `token` / `tool.call` / `tool.result` / `error`,\n * then `turn.end` which closes the stream. The client must NOT keep\n * a connection open between turns — each `send` is a fresh POST.\n */\nexport const createChatsResource = (http: HttpClient, callToken: string) => ({\n async start(input: StartChatInput): Promise<Chat> {\n const tokenQs = `?token=${encodeURIComponent(callToken)}`\n const streamIterable = http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/agents/${input.agentId}/chat${tokenQs}`,\n body: input.text ? { text: input.text } : {},\n })\n\n // Pull events from the stream until we see chat.started — that's our\n // first event and carries the ids. Pass the rest through as `greeting`.\n let chatId = ''\n let callId = ''\n const buffered: ChatEvent[] = []\n const iter = streamIterable[Symbol.asyncIterator]()\n while (true) {\n const { value, done } = await iter.next()\n if (done) break\n if (value.type === 'chat.started') {\n chatId = value.chatId\n callId = value.callId\n break\n }\n buffered.push(value)\n }\n\n return {\n id: chatId,\n callId,\n greeting: replayThen(buffered, { [Symbol.asyncIterator]: () => iter }),\n send(text: string) {\n return http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/chats/${chatId}/messages${tokenQs}`,\n body: { text },\n })\n },\n async end() {\n await http.request({ method: 'DELETE', path: `/v1/calls/${callId}` })\n },\n }\n },\n})\n\nasync function* replayThen<T>(buffered: T[], rest: AsyncIterable<T>): AsyncIterable<T> {\n for (const x of buffered) yield x\n for await (const x of rest) yield x\n}\n\nexport type ChatsResource = ReturnType<typeof createChatsResource>\n","import type { HttpClient } from '../http'\nimport { PlatformError } from '../errors'\nimport type { SpeechAsset, SpeechJob, SpeechListInput, SpeechSynthesizeInput } from '../types'\n\n/** Comfortably past the server's 60s sync deadline (see `synthesize`). */\nexport const SYNTHESIZE_TIMEOUT_MS = 90_000\n\nexport const createSpeechResource = (http: HttpClient) => {\n const speech = {\n /**\n * Synchronous. Resolves with the ready asset. Throws PlatformError\n * code 'accepted' (status 202, `body` = SpeechJob) when the server hands\n * the job to the queue instead — poll `get(job.id)` or wait for the\n * `speech.ready` webhook.\n */\n synthesize: async (input: SpeechSynthesizeInput): Promise<SpeechAsset> => {\n const res = await http.request<SpeechAsset | SpeechJob>({\n method: 'POST',\n path: '/v1/speech',\n body: input,\n // The server's own sync budget is 60s, after which it hands the job\n // to the queue and answers 202. The default 30s client timeout would\n // abort first — turning a perfectly good handoff into a network\n // error — so give the server room to answer.\n timeoutMs: SYNTHESIZE_TIMEOUT_MS,\n })\n if (res.status === 'ready') return res\n if (res.status === 'failed') {\n throw new PlatformError({\n code: 'internal_error',\n message: res.error?.message ?? 'Speech synthesis failed',\n status: 200,\n body: res,\n })\n }\n throw new PlatformError({\n code: 'accepted',\n message: `Speech job ${res.id} accepted; poll speech.get() or await the speech.ready webhook`,\n status: 202,\n body: res,\n })\n },\n\n /**\n * Asynchronous. Normally returns a queued/processing SpeechJob —\n * completion arrives via the `speech.ready` / `speech.failed` webhook or\n * a later `get(job.id)`. Exception: when `idempotencyKey` matches an\n * existing *ready* asset, the server short-circuits the queue hop and\n * responds 200 with that SpeechAsset directly instead of 202.\n */\n enqueue: async (input: SpeechSynthesizeInput): Promise<SpeechJob | SpeechAsset> =>\n http.request<SpeechJob | SpeechAsset>({\n method: 'POST',\n path: '/v1/speech',\n query: { async: 1 },\n body: input,\n }),\n\n get: async (id: string): Promise<SpeechAsset | SpeechJob> =>\n http.request<SpeechAsset | SpeechJob>({ method: 'GET', path: `/v1/speech/${id}` }),\n\n list: async (\n input: SpeechListInput = {},\n ): Promise<{ items: Array<SpeechAsset | SpeechJob>; cursor?: string }> =>\n http.request({\n method: 'GET',\n path: '/v1/speech',\n query: input as Record<string, string | number | undefined>,\n }),\n\n /** Idempotent. Deleting a ready asset invalidates its URL immediately. */\n delete: async (id: string): Promise<void> => {\n await http.request<void>({ method: 'DELETE', path: `/v1/speech/${id}` })\n },\n }\n return speech\n}\n\nexport type SpeechResource = ReturnType<typeof createSpeechResource>\n","import { createHttpClient, type HttpClient, type HttpClientOptions } from './http'\nimport { createMeResource, type MeResource } from './resources/me'\nimport { createAgentsResource, type AgentsResource } from './resources/agents'\nimport { createCallsResource, type CallsResource } from './resources/calls'\nimport {\n createKnowledgeBasesResource,\n type KnowledgeBasesResource,\n} from './resources/knowledgeBases'\nimport { createCreditsResource, type CreditsResource } from './resources/credits'\nimport { createCallTokensResource, type CallTokensResource } from './resources/callTokens'\nimport { createWebhooksResource, type WebhooksResource } from './resources/webhooks'\nimport { createOrgsResource, type OrgsResource } from './resources/orgs'\nimport { createRoomsResource, type RoomsResource } from './resources/rooms'\nimport { createSpacesResource, type SpacesResource } from './resources/spaces'\nimport { createChatsResource, type ChatsResource } from './resources/chats'\nimport { createSpeechResource, type SpeechResource } from './resources/speech'\n\nexport interface PlatformClientOptions {\n // Full-org API key minted from the dashboard or bootstrap CLI. Start with\n // `sk_`. Never ship to a browser — use `client.callTokens.mint(...)` to\n // generate a narrow `ct_` token for client-side use instead.\n apiKey: string\n // Defaults to the hosted platform. Point at `http://localhost:8080` for\n // local dev or at a self-hosted deployment.\n baseUrl?: string\n // Passthrough tuning for the HTTP layer.\n timeoutMs?: number\n maxRetries?: number\n fetch?: HttpClientOptions['fetch']\n onRequest?: HttpClientOptions['onRequest']\n}\n\n// Single entry point. All resources are lazy-constructed in the constructor\n// so their refs don't incur any per-call allocation. Shape mirrors Stripe /\n// Twilio / Vapi's `resource.method()` convention for familiarity.\nexport class PlatformClient {\n readonly me: MeResource\n readonly agents: AgentsResource\n readonly calls: CallsResource\n readonly knowledgeBases: KnowledgeBasesResource\n readonly credits: CreditsResource\n readonly callTokens: CallTokensResource\n readonly webhooks: WebhooksResource\n readonly orgs: OrgsResource\n readonly rooms: RoomsResource\n readonly spaces: SpacesResource\n readonly speech: SpeechResource\n private readonly _http: HttpClient\n\n constructor(options: PlatformClientOptions) {\n if (!options.apiKey) {\n throw new Error('PlatformClient: `apiKey` is required')\n }\n const http = createHttpClient({\n apiKey: options.apiKey,\n baseUrl: options.baseUrl ?? 'https://api.example.com',\n timeoutMs: options.timeoutMs,\n maxRetries: options.maxRetries,\n fetch: options.fetch,\n onRequest: options.onRequest,\n })\n\n this.me = createMeResource(http)\n this.agents = createAgentsResource(http)\n this.calls = createCallsResource(http)\n this.knowledgeBases = createKnowledgeBasesResource(http)\n this.credits = createCreditsResource(http)\n this.callTokens = createCallTokensResource(http)\n this.webhooks = createWebhooksResource(http)\n this.orgs = createOrgsResource(http)\n this.rooms = createRoomsResource(http)\n this.spaces = createSpacesResource(http)\n this.speech = createSpeechResource(http)\n this._http = http\n }\n\n /**\n * Returns a per-token chat resource bound to the given `ct_…` call token.\n * The token must have been minted with `channel: 'text'`.\n *\n * Note: `chatsFor` is a factory method (not a fixed property) because the\n * underlying resource is scoped to a single call token, whereas this client\n * was constructed with an `sk_` admin key. Each end-user session needs its\n * own `chatsFor(token)` handle.\n */\n public chatsFor(callToken: string): ChatsResource {\n return createChatsResource(this._http, callToken)\n }\n}\n","import crypto from 'node:crypto'\n\n// Helper for consumers receiving webhooks: verify the `X-Platform-Signature-256`\n// header against the raw body + secret. Plain function (not tied to\n// PlatformClient) so Express/Koa/Next.js middleware can use it without\n// instantiating a client.\n//\n// import { verifyWebhookSignature } from '@craftedxp/sdk-node'\n// app.post('/webhooks/voice-agent', express.raw({ type: 'application/json' }), (req, res) => {\n// const sig = req.header('X-Platform-Signature-256') ?? ''\n// if (!verifyWebhookSignature(req.body, sig, process.env.VOICE_AGENT_WEBHOOK_SECRET!)) {\n// return res.status(401).send('invalid signature')\n// }\n// const event = JSON.parse(req.body.toString('utf8'))\n// // ...handle event\n// })\n//\n// The signature is `sha256=<hex HMAC-SHA256 of rawBody with secret>`.\n// Timing-safe compare to avoid microtiming side channels.\n\nexport const verifyWebhookSignature = (\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n): boolean => {\n if (!signatureHeader || !secret) return false\n const [algo, provided] = signatureHeader.split('=')\n if (algo !== 'sha256' || !provided) return false\n\n const expected = crypto\n .createHmac('sha256', secret)\n .update(typeof rawBody === 'string' ? rawBody : rawBody)\n .digest('hex')\n\n // Buffers must match in length for timingSafeEqual.\n const a = Buffer.from(expected, 'hex')\n const b = Buffer.from(provided, 'hex')\n if (a.length !== b.length) return false\n return crypto.timingSafeEqual(a, b)\n}\n"],"mappings":"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","names":["path","fs","path","crypto"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/me.ts","../src/resources/agentWebhooks.ts","../src/resources/agents.ts","../src/resources/calls.ts","../src/resources/knowledgeBases.ts","../src/resources/credits.ts","../src/resources/callTokens.ts","../src/resources/webhooks.ts","../src/resources/orgs.ts","../src/resources/rooms.ts","../src/joinUrl.ts","../src/resources/spaces.ts","../src/resources/chats.ts","../src/resources/speech.ts","../src/PlatformClient.ts","../src/verify.ts"],"sourcesContent":["// Public API of @craftedxp/sdk-node.\n\nexport { PlatformClient } from './PlatformClient'\nexport type { PlatformClientOptions } from './PlatformClient'\n\n// Error class for `instanceof` checks in consumer code.\nexport { PlatformError } from './errors'\nexport type { ApiErrorCode } from './errors'\n\n// Webhook signature verification helper — standalone so frameworks\n// (Express, Koa, Next.js route handlers) can use it without instantiating\n// a PlatformClient.\nexport { verifyWebhookSignature } from './verify'\n\n// Standalone join-URL builder — pure, client-independent so it can run\n// anywhere the developer mints a participant link.\nexport { buildJoinUrl } from './joinUrl'\nexport type { JoinUrlStyle, BuildJoinUrlOptions } from './joinUrl'\n\n// Re-export DTO types. Consumers often type their own storage models\n// against these — re-exporting avoids `import type` gymnastics.\nexport type * from './types'\n\n// Advanced: expose the resource types for consumers subclassing / wrapping\n// the client. 99% of users don't need these.\nexport type { MeResource } from './resources/me'\nexport type { AgentsResource } from './resources/agents'\nexport type { AgentWebhooksResource } from './resources/agentWebhooks'\nexport type { CallsResource } from './resources/calls'\nexport type { KnowledgeBasesResource } from './resources/knowledgeBases'\nexport type { CreditsResource } from './resources/credits'\nexport type { CallTokensResource } from './resources/callTokens'\nexport type { WebhooksResource } from './resources/webhooks'\nexport type { OrgsResource } from './resources/orgs'\nexport type { RoomsResource } from './resources/rooms'\nexport type { SpacesResource } from './resources/spaces'\nexport type { ChatsResource } from './resources/chats'\nexport type { StartChatInput, Chat } from './resources/chats'\nexport type { SpeechResource } from './resources/speech'\n","// Typed error class mirroring the server's ErrorV1 shape:\n// { error: { code, message, field?, docs_url? } }\n//\n// Consumers do `err instanceof PlatformError` to branch on code without\n// parsing string messages. `status` carries the HTTP code for the 1% of\n// cases where the code field isn't enough (e.g. rate-limit → retry-after\n// header correlation).\n\nexport type ApiErrorCode =\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'bad_request'\n | 'conflict'\n | 'rate_limited'\n | 'payment_required'\n | 'internal_error'\n | 'accepted'\n | 'service_unavailable'\n | 'unknown'\n\nexport class PlatformError extends Error {\n readonly code: ApiErrorCode\n readonly status: number\n readonly field?: string\n readonly docsUrl?: string\n // The raw response body for debugging. Intentionally optional — we clear\n // it on `error.toJSON()` so logging libraries don't dump the whole\n // server response into production logs.\n readonly body?: unknown\n\n constructor(params: {\n code: ApiErrorCode\n message: string\n status: number\n field?: string\n docsUrl?: string\n body?: unknown\n }) {\n super(params.message)\n this.name = 'PlatformError'\n this.code = params.code\n this.status = params.status\n this.field = params.field\n this.docsUrl = params.docsUrl\n this.body = params.body\n // Preserve the stack trace — Node's Error doesn't capture it\n // automatically when subclassing in some older runtimes.\n if (\n typeof (Error as typeof Error & { captureStackTrace?: unknown }).captureStackTrace ===\n 'function'\n ) {\n ;(\n Error as typeof Error & { captureStackTrace: (target: unknown, ctor: unknown) => void }\n ).captureStackTrace(this, PlatformError)\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n field: this.field,\n docsUrl: this.docsUrl,\n }\n }\n}\n","import { PlatformError, type ApiErrorCode } from './errors'\n\n// Low-level HTTP wrapper around `fetch` (native in Node 18+). Every resource\n// method routes through here for consistent auth + error handling + retries.\n//\n// v1 scope: JSON-in / JSON-out + multipart for file uploads. No streaming —\n// call WebSockets go through @craftedxp/voice-rn (the React Native client)\n// or a web equivalent, not this server-side SDK.\n\nexport interface HttpClientOptions {\n apiKey: string\n baseUrl: string\n // Default 30s. File uploads can override per-request.\n timeoutMs?: number\n // 429 + 5xx retries. Defaults to 3 attempts (original + 2 retries) with\n // exponential backoff (250ms, 1s). Set to 0 to disable.\n maxRetries?: number\n // Optional — lets consumers swap in a test/mock fetch (or a custom one\n // with instrumentation). Defaults to the global.\n fetch?: typeof fetch\n // Optional — a callback that fires per-request with the final status and\n // duration. Handy for dropping traces into the consumer's observability\n // stack without wrapping each call.\n onRequest?: (info: {\n method: string\n url: string\n status: number\n durationMs: number\n attempt: number\n }) => void\n}\n\nexport interface HttpRequest {\n method: 'GET' | 'POST' | 'PATCH' | 'DELETE'\n path: string // starts with `/`, e.g. `/v1/agents/123`\n query?: Record<string, string | number | boolean | undefined>\n body?: unknown // JSON-serialised — for multipart use `formData`\n formData?: FormData\n timeoutMs?: number\n // Exposed for edge cases where a resource wants to tack on extra\n // headers (we don't have any today but leave the hook).\n headers?: Record<string, string>\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst DEFAULT_MAX_RETRIES = 2 // total attempts = 3\n\nconst buildUrl = (baseUrl: string, path: string, query?: HttpRequest['query']): string => {\n const u = new URL(path, baseUrl)\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue\n u.searchParams.set(k, String(v))\n }\n }\n return u.toString()\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\nconst isRetryable = (status: number): boolean => status === 429 || (status >= 500 && status < 600)\n\n// Parse whatever the server returned into a PlatformError. Falls back to\n// synthesising a sensible error for non-JSON responses.\nconst errorFromResponse = async (res: Response): Promise<PlatformError> => {\n const bodyText = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = bodyText ? JSON.parse(bodyText) : undefined\n } catch {\n // non-JSON response (e.g. an HTML error page from a proxy). Fall\n // through with parsed = undefined.\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n return new PlatformError({\n code: (errObj?.code as ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? bodyText,\n })\n}\n\nexport const createHttpClient = (opts: HttpClientOptions) => {\n const fetchImpl = opts.fetch ?? globalThis.fetch\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n\n if (!fetchImpl) {\n throw new Error('No global fetch available. @craftedxp/sdk-node requires Node >= 18.')\n }\n\n const request = async <T>(req: HttpRequest): Promise<T> => {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n Accept: 'application/json',\n ...(req.headers ?? {}),\n }\n\n // Body shaping: prefer formData when provided; otherwise JSON.\n // `FormData` sets its own Content-Type with boundary — don't preempt it.\n // Typed as `string | FormData | undefined` (a subset of the global\n // BodyInit) so we don't need DOM lib types in this server-side SDK.\n let body: string | FormData | undefined\n if (req.formData) {\n body = req.formData\n } else if (req.body !== undefined) {\n headers['Content-Type'] = 'application/json'\n body = JSON.stringify(req.body)\n }\n\n const reqTimeout = req.timeoutMs ?? timeoutMs\n let lastErr: unknown\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), reqTimeout)\n const started = Date.now()\n try {\n const res = await fetchImpl(url, {\n method: req.method,\n headers,\n body,\n signal: controller.signal,\n })\n const durationMs = Date.now() - started\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs,\n attempt: attempt + 1,\n })\n\n if (res.ok) {\n // 204 No Content — some endpoints (DELETE webhooks) return nothing.\n if (res.status === 204) return undefined as T\n const ct = res.headers.get('content-type') ?? ''\n if (ct.includes('application/json')) {\n return (await res.json()) as T\n }\n // Non-JSON 2xx — rare. Return raw text cast as T.\n return (await res.text()) as unknown as T\n }\n\n // Retryable error: back off + retry up to maxRetries.\n if (isRetryable(res.status) && attempt < maxRetries) {\n const retryAfter = res.headers.get('Retry-After')\n const backoff = retryAfter ? Number(retryAfter) * 1000 : 250 * Math.pow(2, attempt)\n await sleep(backoff)\n continue\n }\n\n throw await errorFromResponse(res)\n } catch (err) {\n if (err instanceof PlatformError) throw err\n // AbortError / network / DNS failures — retry up to maxRetries.\n if (attempt < maxRetries) {\n lastErr = err\n await sleep(250 * Math.pow(2, attempt))\n continue\n }\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n } finally {\n clearTimeout(timer)\n }\n }\n\n // Should be unreachable — the loop either returns, throws, or continues.\n throw lastErr instanceof Error ? lastErr : new Error('request exhausted retries')\n }\n\n // SSE streaming: returns an AsyncIterable<T> over parsed SSE events.\n // Each event is the JSON `data` payload merged with the `event` name as\n // `type`. No retry/backoff mid-stream (stream != request); the caller\n // should treat `error` events from the typed channel as the error path.\n async function* stream<T = unknown>(req: HttpRequest): AsyncIterable<T> {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...(req.headers ?? {}),\n }\n\n const started = Date.now()\n let res: Response\n try {\n res = await fetchImpl(url, {\n method: req.method,\n headers,\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n }\n\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs: Date.now() - started,\n attempt: 1,\n })\n\n if (!res.ok || !res.body) {\n const text = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = text ? JSON.parse(text) : undefined\n } catch {\n // non-JSON — fall through\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n throw new PlatformError({\n code: (errObj?.code as import('./errors').ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? text,\n })\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buf = ''\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) return\n buf += decoder.decode(value, { stream: true })\n let idx: number\n while ((idx = buf.indexOf('\\n\\n')) >= 0) {\n const block = buf.slice(0, idx)\n buf = buf.slice(idx + 2)\n let event = 'message'\n let data = ''\n for (const line of block.split('\\n')) {\n if (line.startsWith(':')) continue // SSE comment / keepalive\n if (line.startsWith('event:')) event = line.slice(6).trim()\n else if (line.startsWith('data:')) data += line.slice(5).trim()\n }\n if (!data) continue\n try {\n const parsed = JSON.parse(data)\n yield { type: event, ...parsed } as T\n } catch {\n // Drop malformed JSON; an error event will arrive via the typed channel.\n }\n }\n }\n }\n\n return { request, stream }\n}\n\nexport type HttpClient = ReturnType<typeof createHttpClient>\n","import type { HttpClient } from '../http'\nimport type { MeResponse } from '../types'\n\nexport const createMeResource = (http: HttpClient) => ({\n // Smoke test — confirms the API key is valid and returns the org + balance.\n // Most consumers use this as a ping on startup.\n get: async (): Promise<MeResponse> => http.request<MeResponse>({ method: 'GET', path: '/v1/me' }),\n})\n\nexport type MeResource = ReturnType<typeof createMeResource>\n","import type { HttpClient } from '../http'\nimport type {\n WebhookConfig,\n WebhookCreateInput,\n WebhookDelivery,\n WebhookUpdateInput,\n} from '../types'\n\n// Per-agent webhook resource, scoped at factory time to a specific agentId.\n// All endpoints mirror /v1/agents/:agentId/webhooks[/:id].\n\nexport const createAgentWebhooksResource = (http: HttpClient, agentId: string) => ({\n // Returns the webhook + its signing secret. The secret is only present in\n // this response — persist immediately, subsequent GETs omit it. Use the\n // secret to verify `X-Platform-Signature-256` (sha256=hex HMAC over body).\n create: async (input: WebhookCreateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks`,\n body: input,\n }),\n\n list: async (): Promise<WebhookConfig[]> => {\n const res = await http.request<{ data: WebhookConfig[] }>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks`,\n })\n return res.data\n },\n\n get: async (webhookId: string): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n update: async (webhookId: string, patch: WebhookUpdateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'PATCH',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n body: patch,\n }),\n\n delete: async (webhookId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n // Fires a synthetic call.started against this webhook and returns the\n // full delivery record (including attempt status codes) once the retry\n // sequence has finished. Useful during setup to verify receiver wiring.\n test: async (webhookId: string): Promise<WebhookDelivery> =>\n http.request<WebhookDelivery>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}/test`,\n }),\n})\n\nexport type AgentWebhooksResource = ReturnType<typeof createAgentWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type {\n Agent,\n AgentCreateInput,\n AgentImportResult,\n AgentListAllOptions,\n AgentListOptions,\n AgentListResponse,\n AgentUpdateInput,\n BulkAgentEnvelope,\n SingleAgentEnvelope,\n} from '../types'\nimport { createAgentWebhooksResource, type AgentWebhooksResource } from './agentWebhooks'\n\n// The server returns Agent with `apiKey` stripped and `hasApiKey: boolean`\n// on the model. We type the happy path but keep our input type permissive\n// so consumers can POST a plaintext `apiKey` that the server encrypts +\n// swaps for `apiKeySecret` on persistence.\n\nexport const createAgentsResource = (http: HttpClient) => {\n const agents = {\n create: async (input: AgentCreateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'POST', path: '/v1/agents', body: input }),\n\n // One page of agents. Opaque cursor pagination (`nextCursor` is whatever\n // the server needs to resume — don't parse it client-side). Returns the\n // full `{ data, nextCursor }` envelope; pass `nextCursor` back as `cursor`\n // to walk pages, or use `listAll` to iterate every agent.\n list: async (opts: AgentListOptions = {}): Promise<AgentListResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.limit !== undefined) query.limit = opts.limit\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.q) query.q = opts.q\n return http.request<AgentListResponse>({ method: 'GET', path: '/v1/agents', query })\n },\n\n // Async iterator for \"give me every agent\" — walks the cursor internally,\n // fetching one page at a time and yielding each agent, so consumers never\n // have to thread `nextCursor` themselves.\n listAll: async function* (opts: AgentListAllOptions = {}): AsyncIterable<Agent> {\n let cursor: string | undefined\n do {\n const page = await agents.list({ q: opts.q, limit: opts.pageSize, cursor })\n for (const a of page.data) yield a\n cursor = page.nextCursor ?? undefined\n } while (cursor)\n },\n\n get: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'GET', path: `/v1/agents/${agentId}` }),\n\n update: async (agentId: string, patch: AgentUpdateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'PATCH', path: `/v1/agents/${agentId}`, body: patch }),\n\n delete: async (agentId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/agents/${agentId}` }),\n\n /**\n * Upload an avatar image for the agent. Server re-encodes to a 512×512\n * WebP and stores it in the public-read avatars bucket; the returned\n * `Agent` has `avatarUrl` set to the canonical public URL with a\n * `?v=` cache-buster.\n *\n * `file` accepts a Node Buffer / Uint8Array / Blob / typed-array.\n * `filename` and `contentType` are optional but help the server log\n * meaningful errors when something rejects.\n */\n uploadAvatar: async (\n agentId: string,\n file: Buffer | Uint8Array | Blob | ArrayBuffer,\n opts: { filename?: string; contentType?: string } = {},\n ): Promise<Agent> => {\n const fd = new FormData()\n const blob =\n file instanceof Blob\n ? file\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Blob([file as any], { type: opts.contentType ?? 'application/octet-stream' })\n fd.append('file', blob, opts.filename ?? 'avatar')\n return http.request<Agent>({\n method: 'POST',\n path: `/v1/agents/${agentId}/avatar`,\n formData: fd,\n })\n },\n\n /**\n * Remove the agent's avatar — both the GCS object and the `avatarUrl`\n * field. Idempotent: calling on an agent without an avatar still\n * returns the agent.\n */\n removeAvatar: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'DELETE', path: `/v1/agents/${agentId}/avatar` }),\n\n /**\n * Export a single agent as a portable envelope. Server-managed and\n * sensitive fields (ids, timestamps, avatar, BYO LLM key) are stripped —\n * the envelope carries only the config needed to recreate the agent via\n * {@link import}. Feed it straight back into `agents.import(envelope)`,\n * in this org or another.\n */\n export: async (agentId: string): Promise<SingleAgentEnvelope> =>\n http.request<SingleAgentEnvelope>({\n method: 'GET',\n path: `/v1/agents/${agentId}/export`,\n }),\n\n /**\n * Export every agent in the org as one bulk envelope — the same portable\n * shape as {@link export}, with an `agents` array. Round-trips through\n * {@link import} unchanged.\n */\n exportAll: async (): Promise<BulkAgentEnvelope> =>\n http.request<BulkAgentEnvelope>({ method: 'GET', path: '/v1/agents/export' }),\n\n /**\n * Import agents from a single or bulk envelope produced by {@link export} /\n * {@link exportAll}. Best-effort per agent: the result's `imported` array\n * holds each created agent plus non-fatal `warnings` (e.g. a knowledge-base\n * reference cleared because it doesn't exist in the target org, or a BYO\n * LLM key that must be re-added), and `failed` holds the ones that couldn't\n * be created. A partial import still resolves — inspect both arrays.\n */\n import: async (envelope: SingleAgentEnvelope | BulkAgentEnvelope): Promise<AgentImportResult> =>\n http.request<AgentImportResult>({\n method: 'POST',\n path: '/v1/agents/import',\n body: envelope,\n }),\n\n // Nested resource. Per-agent webhook CRUD lives at\n // /v1/agents/:agentId/webhooks/* — we expose it as a factory keyed on\n // agentId so consumers can bind once and reuse:\n //\n // const hooks = client.agents.webhooks(myAgentId)\n // await hooks.create({ url, events })\n // await hooks.list()\n webhooks: (agentId: string): AgentWebhooksResource =>\n createAgentWebhooksResource(http, agentId),\n }\n return agents\n}\n\nexport type AgentsResource = ReturnType<typeof createAgentsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CallListFilters,\n CallRecord,\n CallRecordingUrlResponse,\n CallSummary,\n TranscriptTurn,\n} from '../types'\n\nexport const createCallsResource = (http: HttpClient) => {\n const calls = {\n list: async (filters: CallListFilters = {}): Promise<CallSummary[]> => {\n const res = await http.request<{ data: CallSummary[] }>({\n method: 'GET',\n path: '/v1/calls',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination helper. v1 server caps at limit (max 200) in a single\n // page — consumers who ask for \"every call since X\" get that page, the\n // iterator ends. This is the shape we'd extend with cursor support\n // without breaking callers.\n listAll: async function* (filters: CallListFilters = {}): AsyncIterable<CallSummary> {\n const page = await calls.list(filters)\n for (const c of page) yield c\n },\n\n get: async (callId: string): Promise<CallRecord> =>\n http.request<CallRecord>({ method: 'GET', path: `/v1/calls/${callId}` }),\n\n transcript: async (callId: string): Promise<{ callId: string; transcript: TranscriptTurn[] }> =>\n http.request<{ callId: string; transcript: TranscriptTurn[] }>({\n method: 'GET',\n path: `/v1/calls/${callId}/transcript`,\n }),\n\n // Returns a V4 signed URL (1-hour TTL). `ready: false` + `artifact:\n // 'caller-raw'` means the async mix job hasn't finished — the URL still\n // points at a playable file (raw caller PCM).\n recording: async (callId: string): Promise<CallRecordingUrlResponse> =>\n http.request<CallRecordingUrlResponse>({\n method: 'GET',\n path: `/v1/calls/${callId}/recording`,\n }),\n\n // Outbound dialling (POST /v1/calls) + in-call control are Phase 1.4.4 /\n // 1.4.5 respectively — blocked on telephony. Surface clear \"not built\n // yet\" errors here if consumers guess those method names, so they don't\n // silently hit a non-existent endpoint and wonder why it 404s.\n }\n return calls\n}\n\nexport type CallsResource = ReturnType<typeof createCallsResource>\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { HttpClient } from '../http'\nimport type { KnowledgeBase, KnowledgeBaseFile } from '../types'\n\nexport const createKnowledgeBasesResource = (http: HttpClient) => ({\n create: async (name: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({\n method: 'POST',\n path: '/v1/knowledge-bases',\n body: { name },\n }),\n\n list: async (): Promise<KnowledgeBase[]> => {\n const res = await http.request<{ data: KnowledgeBase[] }>({\n method: 'GET',\n path: '/v1/knowledge-bases',\n })\n return res.data\n },\n\n get: async (kbId: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({ method: 'GET', path: `/v1/knowledge-bases/${kbId}` }),\n\n delete: async (kbId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/knowledge-bases/${kbId}` }),\n\n // File upload — accepts either a local file path OR raw bytes + filename.\n // The server ingests synchronously today (Phase 3.1.4 Cloud Tasks is a\n // followup), so the returned file has status='ready' in most cases.\n uploadFile: async (\n kbId: string,\n source:\n | { path: string; filename?: string; mimeType?: string }\n | { data: Buffer | Uint8Array; filename: string; mimeType?: string },\n ): Promise<KnowledgeBaseFile> => {\n const form = new FormData()\n let blob: Blob\n let filename: string\n let mime: string\n\n if ('path' in source) {\n const buf = await fs.promises.readFile(source.path)\n filename = source.filename ?? path.basename(source.path)\n mime = source.mimeType ?? 'application/octet-stream'\n blob = new Blob([new Uint8Array(buf)], { type: mime })\n } else {\n filename = source.filename\n mime = source.mimeType ?? 'application/octet-stream'\n const bytes = source.data instanceof Buffer ? new Uint8Array(source.data) : source.data\n blob = new Blob([bytes], { type: mime })\n }\n form.append('file', blob, filename)\n\n // File uploads can take a while (OCR, chunking, embedding) — give them\n // room before timing out. 5 min cap matches the server's own\n // processing budget.\n return http.request<KnowledgeBaseFile>({\n method: 'POST',\n path: `/v1/knowledge-bases/${kbId}/files`,\n formData: form,\n timeoutMs: 5 * 60 * 1000,\n })\n },\n\n listFiles: async (kbId: string): Promise<KnowledgeBaseFile[]> => {\n const res = await http.request<{ data: KnowledgeBaseFile[] }>({\n method: 'GET',\n path: `/v1/knowledge-bases/${kbId}/files`,\n })\n return res.data\n },\n\n deleteFile: async (kbId: string, fileId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/knowledge-bases/${kbId}/files/${fileId}`,\n }),\n})\n\nexport type KnowledgeBasesResource = ReturnType<typeof createKnowledgeBasesResource>\n","import type { HttpClient } from '../http'\nimport type { LedgerEntry } from '../types'\n\nexport const createCreditsResource = (http: HttpClient) => {\n const credits = {\n getBalance: async (): Promise<{ orgId: string; balanceCents: number }> =>\n http.request<{ orgId: string; balanceCents: number }>({\n method: 'GET',\n path: '/v1/credits/balance',\n }),\n\n // Returns ledger entries newest-first. `limit` is capped at 500 server-side.\n getLedger: async (opts: { limit?: number } = {}): Promise<LedgerEntry[]> => {\n const res = await http.request<{ data: LedgerEntry[] }>({\n method: 'GET',\n path: '/v1/credits/ledger',\n query: opts as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination scaffold — v1 server returns a single page up to\n // limit=500. When cursor pagination lands this is where it gets wired.\n getLedgerAll: async function* (opts: { limit?: number } = {}): AsyncIterable<LedgerEntry> {\n const page = await credits.getLedger(opts)\n for (const e of page) yield e\n },\n }\n return credits\n}\n\nexport type CreditsResource = ReturnType<typeof createCreditsResource>\n","import type { HttpClient } from '../http'\nimport type { CallTokenMintInput, CallTokenMintResult, CallTokenSummary } from '../types'\n\n// Short-lived, agent-scoped `ct_` tokens. Mint one per user session on your\n// backend, hand the raw value to the browser; let this SDK handle lifecycle\n// (revoke on sign-out, list active tokens for an admin panel).\n\nexport const createCallTokensResource = (http: HttpClient) => ({\n // Returns the RAW token value once. Don't log it; pass it straight to the\n // browser + discard server-side. `tokenId` is the stable public handle\n // used for revocation later.\n mint: async (input: CallTokenMintInput): Promise<CallTokenMintResult> =>\n http.request<CallTokenMintResult>({\n method: 'POST',\n path: '/v1/call-tokens',\n body: input,\n }),\n\n // Live tokens only by default. Pass includeRevoked/includeExpired when\n // debugging \"why did my token stop working\".\n list: async (\n opts: { includeRevoked?: boolean; includeExpired?: boolean; limit?: number } = {},\n ): Promise<CallTokenSummary[]> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.includeRevoked) query.includeRevoked = '1'\n if (opts.includeExpired) query.includeExpired = '1'\n if (opts.limit !== undefined) query.limit = opts.limit\n const res = await http.request<{ data: CallTokenSummary[] }>({\n method: 'GET',\n path: '/v1/call-tokens',\n query,\n })\n return res.data\n },\n\n // Idempotent — re-revoking a revoked token returns `alreadyRevoked: true`.\n revoke: async (\n tokenId: string,\n ): Promise<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }> =>\n http.request<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }>({\n method: 'DELETE',\n path: `/v1/call-tokens/${tokenId}`,\n }),\n})\n\nexport type CallTokensResource = ReturnType<typeof createCallTokensResource>\n","import type { HttpClient } from '../http'\nimport type { WebhookDelivery } from '../types'\n\n// Org-wide webhook delivery log. Per-agent CRUD lives on\n// `client.agents.webhooks(agentId)` — this namespace is just the\n// read-only deliveries surface that spans all agents.\n\nexport const createWebhooksResource = (http: HttpClient) => ({\n deliveries: async (\n filters: {\n agentId?: string\n webhookId?: string\n callId?: string\n limit?: number\n } = {},\n ): Promise<WebhookDelivery[]> => {\n const res = await http.request<{ data: WebhookDelivery[] }>({\n method: 'GET',\n path: '/v1/webhooks/deliveries',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n})\n\nexport type WebhooksResource = ReturnType<typeof createWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type { CatalogAgent, CatalogListInput } from '../types'\n\n// Org-scoped consumer endpoints. Today this is just the agent catalog —\n// the trimmed shape your mobile app picks an agent from. Lives at\n// `/v1/orgs/:orgId/agents` rather than reusing the admin `/v1/agents/*`\n// tree on purpose: the catalog response intentionally excludes the\n// operator-only fields (system prompt, tools, KB IDs) so a leaked\n// consumer-backend `sk_` can't lift the operator config out of it.\n//\n// Pattern from your backend:\n//\n// const client = new PlatformClient({ apiKey: process.env.SK })\n// const visible = await client.orgs.listAgents({\n// orgId: process.env.ORG_ID!,\n// userTags: ['tier1'], // omit to get the unfiltered admin view\n// })\n// res.json(visible)\n\nexport const createOrgsResource = (http: HttpClient) => ({\n /**\n * Fetch the agent catalog for an org. Returns the consumer-trimmed shape;\n * no system prompt, tools, or KB info. Use `client.agents.get(agentId)`\n * with the same `sk_` if you need the full admin shape.\n *\n * `userTags`: end-user entitlement tags. When supplied, hides agents\n * whose `allowedUserTags` is non-empty and doesn't intersect with the\n * supplied list. Omit to get the unfiltered admin view.\n *\n * Persona / category filtering is intentionally not a server param —\n * filter the returned list client-side over `name`s if you need it.\n *\n * Throws `403 forbidden` if `orgId` doesn't match the key's org.\n */\n listAgents: async (input: CatalogListInput): Promise<CatalogAgent[]> => {\n const query: Record<string, string | undefined> = {}\n if (input.userTags && input.userTags.length > 0) query.userTags = input.userTags.join(',')\n const res = await http.request<{ data: CatalogAgent[] }>({\n method: 'GET',\n path: `/v1/orgs/${input.orgId}/agents`,\n query,\n })\n return res.data\n },\n})\n\nexport type OrgsResource = ReturnType<typeof createOrgsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CreateRoomInput,\n CreateRoomResponse,\n ListAnalysisResponse,\n ListRoomsResponse,\n ListUtterancesResponse,\n RoomDoc,\n RoomEventAck,\n RoomListFilters,\n RoomTranscriptOptions,\n} from '../types'\n\n// REST wrappers for /v1/rooms — the multi-party video room surface (see\n// docs/superpowers/specs/2026-05-18-multiparty-rooms-design.md). Lets\n// server-side code mint rooms, fetch state, list transcript pages, and end\n// rooms programmatically.\n//\n// Auth model: same Bearer `sk_` flow as every other resource — the server\n// derives `orgId` from the API key, so the SDK never sets a tenant header.\n//\n// Host actions beyond `end` (promote/demote/kick) and observer-token mint\n// live in the server but aren't surfaced here yet — they're operator-side\n// flows that the dashboard hits directly. Re-add here if a programmatic\n// use-case shows up (CI tests, integration harnesses).\n\nexport const createRoomsResource = (http: HttpClient) => ({\n // Provision a new room. Server returns 201 with ONE shared room-level\n // `joinToken` + `joinUrl`. Share the single link with everyone you want in\n // the room — each visitor supplies their own display name at join time and\n // becomes a fresh, distinct participant. The server persists only the\n // token's sha256 hash; the raw token is returned here once and never stored.\n create: async (input: CreateRoomInput): Promise<CreateRoomResponse> =>\n http.request<CreateRoomResponse>({\n method: 'POST',\n path: '/v1/rooms',\n body: input,\n }),\n\n // Listing — opaque cursor pagination (`nextCursor` returned by the server\n // is whatever startAfter() needs, don't parse client-side).\n list: async (filters: RoomListFilters = {}): Promise<ListRoomsResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (filters.status) query.status = filters.status\n if (filters.limit !== undefined) query.limit = filters.limit\n if (filters.cursor) query.cursor = filters.cursor\n return http.request<ListRoomsResponse>({\n method: 'GET',\n path: '/v1/rooms',\n query,\n })\n },\n\n // Fetch a single room. 404s are surfaced as PlatformError('not_found').\n get: async (roomId: string): Promise<RoomDoc> =>\n http.request<RoomDoc>({\n method: 'GET',\n path: `/v1/rooms/${roomId}`,\n }),\n\n // Transcript pages — utterances are ordered by `startedAt asc`. Cursor is\n // an ISO timestamp (server enforces, don't construct yourself).\n transcript: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListUtterancesResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListUtterancesResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/transcript`,\n query,\n })\n },\n\n // Analysis pages — analyzer results ordered by createdAt asc. Auth-only\n // (org-scoped); there is no public token-gated variant. Cursor is an ISO\n // timestamp the server enforces — don't construct it yourself.\n analysis: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListAnalysisResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListAnalysisResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/analysis`,\n query,\n })\n },\n\n // End a room — async on the server: returns 202 + eventId once the\n // controlEvents entry is written. The room-worker observes the entry,\n // broadcasts the system message, and tears down LiveKit shortly after.\n end: async (roomId: string): Promise<RoomEventAck> =>\n http.request<RoomEventAck>({\n method: 'POST',\n path: `/v1/rooms/${roomId}/end`,\n }),\n})\n\nexport type RoomsResource = ReturnType<typeof createRoomsResource>\n","import type { SpaceView } from './types'\n\nexport type JoinUrlStyle = 'query' | 'path'\n\nexport interface BuildJoinUrlOptions {\n /** Required — the developer's own page that renders <VoiceRoom/>. Absolute URL. */\n baseUrl: string\n /** 'query' (default) → ?code=… · 'path' → /… */\n style?: JoinUrlStyle\n}\n\n/**\n * Build a participant join URL on the DEVELOPER's domain from a space (or a\n * raw code). White-label: the returned URL never points at a voissia origin —\n * `baseUrl` is required and supplied by the caller.\n *\n * The default `query` style appends `?code=<code>`, matching how\n * `<VoiceRoom/>` (and the example app) read the code from the URL.\n */\nexport const buildJoinUrl = (\n spaceOrCode: SpaceView | string,\n opts: BuildJoinUrlOptions,\n): string => {\n const code = typeof spaceOrCode === 'string' ? spaceOrCode : spaceOrCode.code\n if (!code) throw new Error('a space code is required')\n const base = opts.baseUrl?.trim()\n if (!base) throw new Error('baseUrl is required')\n\n let url: URL\n try {\n url = new URL(base)\n } catch {\n throw new Error('baseUrl must be an absolute URL (e.g. https://app.example.com/room)')\n }\n if ((opts.style ?? 'query') === 'path') {\n url.pathname = `${url.pathname.replace(/\\/+$/, '')}/${encodeURIComponent(code)}`\n } else {\n url.searchParams.set('code', code)\n }\n return url.toString()\n}\n","import type { HttpClient } from '../http'\nimport type { CreateSpaceInput, ListSpacesResponse, SpacePatch, SpaceView } from '../types'\nimport { buildJoinUrl, type BuildJoinUrlOptions } from '../joinUrl'\n\n// REST wrappers for /v1/spaces — durable multi-party meeting spaces (Phase 23,\n// see docs/superpowers/specs/2026-06-14-spaces-sdk-resource-design.md). Lets\n// server-side code create spaces, read/update/delete them, and build a\n// participant join URL on the developer's own domain.\n//\n// Auth: same Bearer `sk_` flow as every resource — orgId is derived server-side,\n// so the SDK never sets a tenant header.\n//\n// Out of scope here (client/host-side, in @craftedxp/voice-room-react): the\n// public join exchange, host-token mint, lobby admit/deny, moderation, and the\n// X-Host-Key-gated recording controls.\n\nexport const createSpacesResource = (http: HttpClient) => ({\n // Create a durable space. Server returns 201 with the SpaceView, including a\n // stable `code` participants join with. Pass it to `joinUrl` (or the\n // standalone `buildJoinUrl`) to make a link on YOUR domain.\n create: async (input: CreateSpaceInput): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'POST',\n path: '/v1/spaces',\n body: input,\n }),\n\n get: async (spaceId: string): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'GET',\n path: `/v1/spaces/${spaceId}`,\n }),\n\n // List every space for the org. Plain `{ data: [...] }` — no cursor.\n list: async (): Promise<ListSpacesResponse> =>\n http.request<ListSpacesResponse>({\n method: 'GET',\n path: '/v1/spaces',\n }),\n\n // Patch a space. The server rejects an empty patch — pass at least one field.\n update: async (spaceId: string, patch: SpacePatch): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'PATCH',\n path: `/v1/spaces/${spaceId}`,\n body: patch,\n }),\n\n // Delete a space. Resolves once the server returns 204.\n delete: async (spaceId: string): Promise<void> => {\n await http.request<void>({\n method: 'DELETE',\n path: `/v1/spaces/${spaceId}`,\n })\n },\n\n // Convenience: build a participant join URL on your domain from a space (or\n // raw code). Delegates to the standalone `buildJoinUrl` export.\n joinUrl: (spaceOrCode: SpaceView | string, opts: BuildJoinUrlOptions): string =>\n buildJoinUrl(spaceOrCode, opts),\n})\n\nexport type SpacesResource = ReturnType<typeof createSpacesResource>\n","import type { HttpClient } from '../http'\nimport type { ChatEvent } from '../types'\n\nexport interface StartChatInput {\n agentId: string\n text?: string\n}\n\nexport interface Chat {\n id: string\n callId: string\n greeting: AsyncIterable<ChatEvent>\n send(text: string): AsyncIterable<ChatEvent>\n end(): Promise<void>\n}\n\n/**\n * Per-token chat resource. Construct via `client.chatsFor(callToken)` —\n * `callToken` is a raw `ct_…` minted with `channel: 'text'`.\n *\n * The async iterables stream SSE events: `chat.started` (start only),\n * then a sequence of `token` / `tool.call` / `tool.result` / `error`,\n * then `turn.end` which closes the stream. The client must NOT keep\n * a connection open between turns — each `send` is a fresh POST.\n */\nexport const createChatsResource = (http: HttpClient, callToken: string) => ({\n async start(input: StartChatInput): Promise<Chat> {\n const tokenQs = `?token=${encodeURIComponent(callToken)}`\n const streamIterable = http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/agents/${input.agentId}/chat${tokenQs}`,\n body: input.text ? { text: input.text } : {},\n })\n\n // Pull events from the stream until we see chat.started — that's our\n // first event and carries the ids. Pass the rest through as `greeting`.\n let chatId = ''\n let callId = ''\n const buffered: ChatEvent[] = []\n const iter = streamIterable[Symbol.asyncIterator]()\n while (true) {\n const { value, done } = await iter.next()\n if (done) break\n if (value.type === 'chat.started') {\n chatId = value.chatId\n callId = value.callId\n break\n }\n buffered.push(value)\n }\n\n return {\n id: chatId,\n callId,\n greeting: replayThen(buffered, { [Symbol.asyncIterator]: () => iter }),\n send(text: string) {\n return http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/chats/${chatId}/messages${tokenQs}`,\n body: { text },\n })\n },\n async end() {\n await http.request({ method: 'DELETE', path: `/v1/calls/${callId}` })\n },\n }\n },\n})\n\nasync function* replayThen<T>(buffered: T[], rest: AsyncIterable<T>): AsyncIterable<T> {\n for (const x of buffered) yield x\n for await (const x of rest) yield x\n}\n\nexport type ChatsResource = ReturnType<typeof createChatsResource>\n","import type { HttpClient } from '../http'\nimport { PlatformError } from '../errors'\nimport type { SpeechAsset, SpeechJob, SpeechListInput, SpeechSynthesizeInput } from '../types'\n\n/** Comfortably past the server's 60s sync deadline (see `synthesize`). */\nexport const SYNTHESIZE_TIMEOUT_MS = 90_000\n\nexport const createSpeechResource = (http: HttpClient) => {\n const speech = {\n /**\n * Synchronous. Resolves with the ready asset. Throws PlatformError\n * code 'accepted' (status 202, `body` = SpeechJob) when the server hands\n * the job to the queue instead — poll `get(job.id)` or wait for the\n * `speech.ready` webhook.\n */\n synthesize: async (input: SpeechSynthesizeInput): Promise<SpeechAsset> => {\n const res = await http.request<SpeechAsset | SpeechJob>({\n method: 'POST',\n path: '/v1/speech',\n body: input,\n // The server's own sync budget is 60s, after which it hands the job\n // to the queue and answers 202. The default 30s client timeout would\n // abort first — turning a perfectly good handoff into a network\n // error — so give the server room to answer.\n timeoutMs: SYNTHESIZE_TIMEOUT_MS,\n })\n if (res.status === 'ready') return res\n if (res.status === 'failed') {\n throw new PlatformError({\n code: 'internal_error',\n message: res.error?.message ?? 'Speech synthesis failed',\n status: 200,\n body: res,\n })\n }\n throw new PlatformError({\n code: 'accepted',\n message: `Speech job ${res.id} accepted; poll speech.get() or await the speech.ready webhook`,\n status: 202,\n body: res,\n })\n },\n\n /**\n * Asynchronous. Normally returns a queued/processing SpeechJob —\n * completion arrives via the `speech.ready` / `speech.failed` webhook or\n * a later `get(job.id)`. Exception: when `idempotencyKey` matches an\n * existing *ready* asset, the server short-circuits the queue hop and\n * responds 200 with that SpeechAsset directly instead of 202.\n */\n enqueue: async (input: SpeechSynthesizeInput): Promise<SpeechJob | SpeechAsset> =>\n http.request<SpeechJob | SpeechAsset>({\n method: 'POST',\n path: '/v1/speech',\n query: { async: 1 },\n body: input,\n }),\n\n get: async (id: string): Promise<SpeechAsset | SpeechJob> =>\n http.request<SpeechAsset | SpeechJob>({ method: 'GET', path: `/v1/speech/${id}` }),\n\n list: async (\n input: SpeechListInput = {},\n ): Promise<{ items: Array<SpeechAsset | SpeechJob>; cursor?: string }> =>\n http.request({\n method: 'GET',\n path: '/v1/speech',\n query: input as Record<string, string | number | undefined>,\n }),\n\n /** Idempotent. Deleting a ready asset invalidates its URL immediately. */\n delete: async (id: string): Promise<void> => {\n await http.request<void>({ method: 'DELETE', path: `/v1/speech/${id}` })\n },\n }\n return speech\n}\n\nexport type SpeechResource = ReturnType<typeof createSpeechResource>\n","import { createHttpClient, type HttpClient, type HttpClientOptions } from './http'\nimport { createMeResource, type MeResource } from './resources/me'\nimport { createAgentsResource, type AgentsResource } from './resources/agents'\nimport { createCallsResource, type CallsResource } from './resources/calls'\nimport {\n createKnowledgeBasesResource,\n type KnowledgeBasesResource,\n} from './resources/knowledgeBases'\nimport { createCreditsResource, type CreditsResource } from './resources/credits'\nimport { createCallTokensResource, type CallTokensResource } from './resources/callTokens'\nimport { createWebhooksResource, type WebhooksResource } from './resources/webhooks'\nimport { createOrgsResource, type OrgsResource } from './resources/orgs'\nimport { createRoomsResource, type RoomsResource } from './resources/rooms'\nimport { createSpacesResource, type SpacesResource } from './resources/spaces'\nimport { createChatsResource, type ChatsResource } from './resources/chats'\nimport { createSpeechResource, type SpeechResource } from './resources/speech'\n\nexport interface PlatformClientOptions {\n // Full-org API key minted from the dashboard or bootstrap CLI. Start with\n // `sk_`. Never ship to a browser — use `client.callTokens.mint(...)` to\n // generate a narrow `ct_` token for client-side use instead.\n apiKey: string\n // Defaults to the hosted platform. Point at `http://localhost:8080` for\n // local dev or at a self-hosted deployment.\n baseUrl?: string\n // Passthrough tuning for the HTTP layer.\n timeoutMs?: number\n maxRetries?: number\n fetch?: HttpClientOptions['fetch']\n onRequest?: HttpClientOptions['onRequest']\n}\n\n// Single entry point. All resources are lazy-constructed in the constructor\n// so their refs don't incur any per-call allocation. Shape mirrors Stripe /\n// Twilio / Vapi's `resource.method()` convention for familiarity.\nexport class PlatformClient {\n readonly me: MeResource\n readonly agents: AgentsResource\n readonly calls: CallsResource\n readonly knowledgeBases: KnowledgeBasesResource\n readonly credits: CreditsResource\n readonly callTokens: CallTokensResource\n readonly webhooks: WebhooksResource\n readonly orgs: OrgsResource\n readonly rooms: RoomsResource\n readonly spaces: SpacesResource\n readonly speech: SpeechResource\n private readonly _http: HttpClient\n\n constructor(options: PlatformClientOptions) {\n if (!options.apiKey) {\n throw new Error('PlatformClient: `apiKey` is required')\n }\n const http = createHttpClient({\n apiKey: options.apiKey,\n baseUrl: options.baseUrl ?? 'https://api.example.com',\n timeoutMs: options.timeoutMs,\n maxRetries: options.maxRetries,\n fetch: options.fetch,\n onRequest: options.onRequest,\n })\n\n this.me = createMeResource(http)\n this.agents = createAgentsResource(http)\n this.calls = createCallsResource(http)\n this.knowledgeBases = createKnowledgeBasesResource(http)\n this.credits = createCreditsResource(http)\n this.callTokens = createCallTokensResource(http)\n this.webhooks = createWebhooksResource(http)\n this.orgs = createOrgsResource(http)\n this.rooms = createRoomsResource(http)\n this.spaces = createSpacesResource(http)\n this.speech = createSpeechResource(http)\n this._http = http\n }\n\n /**\n * Returns a per-token chat resource bound to the given `ct_…` call token.\n * The token must have been minted with `channel: 'text'`.\n *\n * Note: `chatsFor` is a factory method (not a fixed property) because the\n * underlying resource is scoped to a single call token, whereas this client\n * was constructed with an `sk_` admin key. Each end-user session needs its\n * own `chatsFor(token)` handle.\n */\n public chatsFor(callToken: string): ChatsResource {\n return createChatsResource(this._http, callToken)\n }\n}\n","import crypto from 'node:crypto'\n\n// Helper for consumers receiving webhooks: verify the `X-Platform-Signature-256`\n// header against the raw body + secret. Plain function (not tied to\n// PlatformClient) so Express/Koa/Next.js middleware can use it without\n// instantiating a client.\n//\n// import { verifyWebhookSignature } from '@craftedxp/sdk-node'\n// app.post('/webhooks/voice-agent', express.raw({ type: 'application/json' }), (req, res) => {\n// const sig = req.header('X-Platform-Signature-256') ?? ''\n// if (!verifyWebhookSignature(req.body, sig, process.env.VOICE_AGENT_WEBHOOK_SECRET!)) {\n// return res.status(401).send('invalid signature')\n// }\n// const event = JSON.parse(req.body.toString('utf8'))\n// // ...handle event\n// })\n//\n// The signature is `sha256=<hex HMAC-SHA256 of rawBody with secret>`.\n// Timing-safe compare to avoid microtiming side channels.\n\nexport const verifyWebhookSignature = (\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n): boolean => {\n if (!signatureHeader || !secret) return false\n const [algo, provided] = signatureHeader.split('=')\n if (algo !== 'sha256' || !provided) return false\n\n const expected = crypto\n .createHmac('sha256', secret)\n .update(typeof rawBody === 'string' ? rawBody : rawBody)\n .digest('hex')\n\n // Buffers must match in length for timingSafeEqual.\n const a = Buffer.from(expected, 'hex')\n const b = Buffer.from(provided, 'hex')\n if (a.length !== b.length) return false\n return crypto.timingSafeEqual(a, b)\n}\n"],"mappings":"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","names":["path","fs","path","crypto"]}
package/dist/index.mjs CHANGED
@@ -269,17 +269,27 @@ var createAgentWebhooksResource = (http, agentId) => ({
269
269
  var createAgentsResource = (http) => {
270
270
  const agents = {
271
271
  create: async (input) => http.request({ method: "POST", path: "/v1/agents", body: input }),
272
- list: async () => {
273
- const res = await http.request({ method: "GET", path: "/v1/agents" });
274
- return res.data;
272
+ // One page of agents. Opaque cursor pagination (`nextCursor` is whatever
273
+ // the server needs to resume don't parse it client-side). Returns the
274
+ // full `{ data, nextCursor }` envelope; pass `nextCursor` back as `cursor`
275
+ // to walk pages, or use `listAll` to iterate every agent.
276
+ list: async (opts = {}) => {
277
+ const query = {};
278
+ if (opts.limit !== void 0) query.limit = opts.limit;
279
+ if (opts.cursor) query.cursor = opts.cursor;
280
+ if (opts.q) query.q = opts.q;
281
+ return http.request({ method: "GET", path: "/v1/agents", query });
275
282
  },
276
- // Async iterator for "give me every agent" — v1 server returns the full
277
- // list in one page, so this is just a thin convenience. When the server
278
- // picks up cursor pagination, this is the method that papers over that
279
- // migration without consumer changes.
280
- listAll: async function* () {
281
- const page = await agents.list();
282
- for (const a of page) yield a;
283
+ // Async iterator for "give me every agent" — walks the cursor internally,
284
+ // fetching one page at a time and yielding each agent, so consumers never
285
+ // have to thread `nextCursor` themselves.
286
+ listAll: async function* (opts = {}) {
287
+ let cursor;
288
+ do {
289
+ const page = await agents.list({ q: opts.q, limit: opts.pageSize, cursor });
290
+ for (const a of page.data) yield a;
291
+ cursor = page.nextCursor ?? void 0;
292
+ } while (cursor);
283
293
  },
284
294
  get: async (agentId) => http.request({ method: "GET", path: `/v1/agents/${agentId}` }),
285
295
  update: async (agentId, patch) => http.request({ method: "PATCH", path: `/v1/agents/${agentId}`, body: patch }),
@@ -313,6 +323,36 @@ var createAgentsResource = (http) => {
313
323
  * returns the agent.
314
324
  */
315
325
  removeAvatar: async (agentId) => http.request({ method: "DELETE", path: `/v1/agents/${agentId}/avatar` }),
326
+ /**
327
+ * Export a single agent as a portable envelope. Server-managed and
328
+ * sensitive fields (ids, timestamps, avatar, BYO LLM key) are stripped —
329
+ * the envelope carries only the config needed to recreate the agent via
330
+ * {@link import}. Feed it straight back into `agents.import(envelope)`,
331
+ * in this org or another.
332
+ */
333
+ export: async (agentId) => http.request({
334
+ method: "GET",
335
+ path: `/v1/agents/${agentId}/export`
336
+ }),
337
+ /**
338
+ * Export every agent in the org as one bulk envelope — the same portable
339
+ * shape as {@link export}, with an `agents` array. Round-trips through
340
+ * {@link import} unchanged.
341
+ */
342
+ exportAll: async () => http.request({ method: "GET", path: "/v1/agents/export" }),
343
+ /**
344
+ * Import agents from a single or bulk envelope produced by {@link export} /
345
+ * {@link exportAll}. Best-effort per agent: the result's `imported` array
346
+ * holds each created agent plus non-fatal `warnings` (e.g. a knowledge-base
347
+ * reference cleared because it doesn't exist in the target org, or a BYO
348
+ * LLM key that must be re-added), and `failed` holds the ones that couldn't
349
+ * be created. A partial import still resolves — inspect both arrays.
350
+ */
351
+ import: async (envelope) => http.request({
352
+ method: "POST",
353
+ path: "/v1/agents/import",
354
+ body: envelope
355
+ }),
316
356
  // Nested resource. Per-agent webhook CRUD lives at
317
357
  // /v1/agents/:agentId/webhooks/* — we expose it as a factory keyed on
318
358
  // agentId so consumers can bind once and reuse:
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/me.ts","../src/resources/agentWebhooks.ts","../src/resources/agents.ts","../src/resources/calls.ts","../src/resources/knowledgeBases.ts","../src/resources/credits.ts","../src/resources/callTokens.ts","../src/resources/webhooks.ts","../src/resources/orgs.ts","../src/resources/rooms.ts","../src/joinUrl.ts","../src/resources/spaces.ts","../src/resources/chats.ts","../src/resources/speech.ts","../src/PlatformClient.ts","../src/verify.ts"],"sourcesContent":["// Typed error class mirroring the server's ErrorV1 shape:\n// { error: { code, message, field?, docs_url? } }\n//\n// Consumers do `err instanceof PlatformError` to branch on code without\n// parsing string messages. `status` carries the HTTP code for the 1% of\n// cases where the code field isn't enough (e.g. rate-limit → retry-after\n// header correlation).\n\nexport type ApiErrorCode =\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'bad_request'\n | 'conflict'\n | 'rate_limited'\n | 'payment_required'\n | 'internal_error'\n | 'accepted'\n | 'service_unavailable'\n | 'unknown'\n\nexport class PlatformError extends Error {\n readonly code: ApiErrorCode\n readonly status: number\n readonly field?: string\n readonly docsUrl?: string\n // The raw response body for debugging. Intentionally optional — we clear\n // it on `error.toJSON()` so logging libraries don't dump the whole\n // server response into production logs.\n readonly body?: unknown\n\n constructor(params: {\n code: ApiErrorCode\n message: string\n status: number\n field?: string\n docsUrl?: string\n body?: unknown\n }) {\n super(params.message)\n this.name = 'PlatformError'\n this.code = params.code\n this.status = params.status\n this.field = params.field\n this.docsUrl = params.docsUrl\n this.body = params.body\n // Preserve the stack trace — Node's Error doesn't capture it\n // automatically when subclassing in some older runtimes.\n if (\n typeof (Error as typeof Error & { captureStackTrace?: unknown }).captureStackTrace ===\n 'function'\n ) {\n ;(\n Error as typeof Error & { captureStackTrace: (target: unknown, ctor: unknown) => void }\n ).captureStackTrace(this, PlatformError)\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n field: this.field,\n docsUrl: this.docsUrl,\n }\n }\n}\n","import { PlatformError, type ApiErrorCode } from './errors'\n\n// Low-level HTTP wrapper around `fetch` (native in Node 18+). Every resource\n// method routes through here for consistent auth + error handling + retries.\n//\n// v1 scope: JSON-in / JSON-out + multipart for file uploads. No streaming —\n// call WebSockets go through @craftedxp/voice-rn (the React Native client)\n// or a web equivalent, not this server-side SDK.\n\nexport interface HttpClientOptions {\n apiKey: string\n baseUrl: string\n // Default 30s. File uploads can override per-request.\n timeoutMs?: number\n // 429 + 5xx retries. Defaults to 3 attempts (original + 2 retries) with\n // exponential backoff (250ms, 1s). Set to 0 to disable.\n maxRetries?: number\n // Optional — lets consumers swap in a test/mock fetch (or a custom one\n // with instrumentation). Defaults to the global.\n fetch?: typeof fetch\n // Optional — a callback that fires per-request with the final status and\n // duration. Handy for dropping traces into the consumer's observability\n // stack without wrapping each call.\n onRequest?: (info: {\n method: string\n url: string\n status: number\n durationMs: number\n attempt: number\n }) => void\n}\n\nexport interface HttpRequest {\n method: 'GET' | 'POST' | 'PATCH' | 'DELETE'\n path: string // starts with `/`, e.g. `/v1/agents/123`\n query?: Record<string, string | number | boolean | undefined>\n body?: unknown // JSON-serialised — for multipart use `formData`\n formData?: FormData\n timeoutMs?: number\n // Exposed for edge cases where a resource wants to tack on extra\n // headers (we don't have any today but leave the hook).\n headers?: Record<string, string>\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst DEFAULT_MAX_RETRIES = 2 // total attempts = 3\n\nconst buildUrl = (baseUrl: string, path: string, query?: HttpRequest['query']): string => {\n const u = new URL(path, baseUrl)\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue\n u.searchParams.set(k, String(v))\n }\n }\n return u.toString()\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\nconst isRetryable = (status: number): boolean => status === 429 || (status >= 500 && status < 600)\n\n// Parse whatever the server returned into a PlatformError. Falls back to\n// synthesising a sensible error for non-JSON responses.\nconst errorFromResponse = async (res: Response): Promise<PlatformError> => {\n const bodyText = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = bodyText ? JSON.parse(bodyText) : undefined\n } catch {\n // non-JSON response (e.g. an HTML error page from a proxy). Fall\n // through with parsed = undefined.\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n return new PlatformError({\n code: (errObj?.code as ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? bodyText,\n })\n}\n\nexport const createHttpClient = (opts: HttpClientOptions) => {\n const fetchImpl = opts.fetch ?? globalThis.fetch\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n\n if (!fetchImpl) {\n throw new Error('No global fetch available. @craftedxp/sdk-node requires Node >= 18.')\n }\n\n const request = async <T>(req: HttpRequest): Promise<T> => {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n Accept: 'application/json',\n ...(req.headers ?? {}),\n }\n\n // Body shaping: prefer formData when provided; otherwise JSON.\n // `FormData` sets its own Content-Type with boundary — don't preempt it.\n // Typed as `string | FormData | undefined` (a subset of the global\n // BodyInit) so we don't need DOM lib types in this server-side SDK.\n let body: string | FormData | undefined\n if (req.formData) {\n body = req.formData\n } else if (req.body !== undefined) {\n headers['Content-Type'] = 'application/json'\n body = JSON.stringify(req.body)\n }\n\n const reqTimeout = req.timeoutMs ?? timeoutMs\n let lastErr: unknown\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), reqTimeout)\n const started = Date.now()\n try {\n const res = await fetchImpl(url, {\n method: req.method,\n headers,\n body,\n signal: controller.signal,\n })\n const durationMs = Date.now() - started\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs,\n attempt: attempt + 1,\n })\n\n if (res.ok) {\n // 204 No Content — some endpoints (DELETE webhooks) return nothing.\n if (res.status === 204) return undefined as T\n const ct = res.headers.get('content-type') ?? ''\n if (ct.includes('application/json')) {\n return (await res.json()) as T\n }\n // Non-JSON 2xx — rare. Return raw text cast as T.\n return (await res.text()) as unknown as T\n }\n\n // Retryable error: back off + retry up to maxRetries.\n if (isRetryable(res.status) && attempt < maxRetries) {\n const retryAfter = res.headers.get('Retry-After')\n const backoff = retryAfter ? Number(retryAfter) * 1000 : 250 * Math.pow(2, attempt)\n await sleep(backoff)\n continue\n }\n\n throw await errorFromResponse(res)\n } catch (err) {\n if (err instanceof PlatformError) throw err\n // AbortError / network / DNS failures — retry up to maxRetries.\n if (attempt < maxRetries) {\n lastErr = err\n await sleep(250 * Math.pow(2, attempt))\n continue\n }\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n } finally {\n clearTimeout(timer)\n }\n }\n\n // Should be unreachable — the loop either returns, throws, or continues.\n throw lastErr instanceof Error ? lastErr : new Error('request exhausted retries')\n }\n\n // SSE streaming: returns an AsyncIterable<T> over parsed SSE events.\n // Each event is the JSON `data` payload merged with the `event` name as\n // `type`. No retry/backoff mid-stream (stream != request); the caller\n // should treat `error` events from the typed channel as the error path.\n async function* stream<T = unknown>(req: HttpRequest): AsyncIterable<T> {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...(req.headers ?? {}),\n }\n\n const started = Date.now()\n let res: Response\n try {\n res = await fetchImpl(url, {\n method: req.method,\n headers,\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n }\n\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs: Date.now() - started,\n attempt: 1,\n })\n\n if (!res.ok || !res.body) {\n const text = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = text ? JSON.parse(text) : undefined\n } catch {\n // non-JSON — fall through\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n throw new PlatformError({\n code: (errObj?.code as import('./errors').ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? text,\n })\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buf = ''\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) return\n buf += decoder.decode(value, { stream: true })\n let idx: number\n while ((idx = buf.indexOf('\\n\\n')) >= 0) {\n const block = buf.slice(0, idx)\n buf = buf.slice(idx + 2)\n let event = 'message'\n let data = ''\n for (const line of block.split('\\n')) {\n if (line.startsWith(':')) continue // SSE comment / keepalive\n if (line.startsWith('event:')) event = line.slice(6).trim()\n else if (line.startsWith('data:')) data += line.slice(5).trim()\n }\n if (!data) continue\n try {\n const parsed = JSON.parse(data)\n yield { type: event, ...parsed } as T\n } catch {\n // Drop malformed JSON; an error event will arrive via the typed channel.\n }\n }\n }\n }\n\n return { request, stream }\n}\n\nexport type HttpClient = ReturnType<typeof createHttpClient>\n","import type { HttpClient } from '../http'\nimport type { MeResponse } from '../types'\n\nexport const createMeResource = (http: HttpClient) => ({\n // Smoke test — confirms the API key is valid and returns the org + balance.\n // Most consumers use this as a ping on startup.\n get: async (): Promise<MeResponse> => http.request<MeResponse>({ method: 'GET', path: '/v1/me' }),\n})\n\nexport type MeResource = ReturnType<typeof createMeResource>\n","import type { HttpClient } from '../http'\nimport type {\n WebhookConfig,\n WebhookCreateInput,\n WebhookDelivery,\n WebhookUpdateInput,\n} from '../types'\n\n// Per-agent webhook resource, scoped at factory time to a specific agentId.\n// All endpoints mirror /v1/agents/:agentId/webhooks[/:id].\n\nexport const createAgentWebhooksResource = (http: HttpClient, agentId: string) => ({\n // Returns the webhook + its signing secret. The secret is only present in\n // this response — persist immediately, subsequent GETs omit it. Use the\n // secret to verify `X-Platform-Signature-256` (sha256=hex HMAC over body).\n create: async (input: WebhookCreateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks`,\n body: input,\n }),\n\n list: async (): Promise<WebhookConfig[]> => {\n const res = await http.request<{ data: WebhookConfig[] }>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks`,\n })\n return res.data\n },\n\n get: async (webhookId: string): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n update: async (webhookId: string, patch: WebhookUpdateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'PATCH',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n body: patch,\n }),\n\n delete: async (webhookId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n // Fires a synthetic call.started against this webhook and returns the\n // full delivery record (including attempt status codes) once the retry\n // sequence has finished. Useful during setup to verify receiver wiring.\n test: async (webhookId: string): Promise<WebhookDelivery> =>\n http.request<WebhookDelivery>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}/test`,\n }),\n})\n\nexport type AgentWebhooksResource = ReturnType<typeof createAgentWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type { Agent, AgentCreateInput, AgentUpdateInput } from '../types'\nimport { createAgentWebhooksResource, type AgentWebhooksResource } from './agentWebhooks'\n\n// The server returns Agent with `apiKey` stripped and `hasApiKey: boolean`\n// on the model. We type the happy path but keep our input type permissive\n// so consumers can POST a plaintext `apiKey` that the server encrypts +\n// swaps for `apiKeySecret` on persistence.\n\nexport const createAgentsResource = (http: HttpClient) => {\n const agents = {\n create: async (input: AgentCreateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'POST', path: '/v1/agents', body: input }),\n\n list: async (): Promise<Agent[]> => {\n const res = await http.request<{ data: Agent[] }>({ method: 'GET', path: '/v1/agents' })\n return res.data\n },\n\n // Async iterator for \"give me every agent\" — v1 server returns the full\n // list in one page, so this is just a thin convenience. When the server\n // picks up cursor pagination, this is the method that papers over that\n // migration without consumer changes.\n listAll: async function* (): AsyncIterable<Agent> {\n const page = await agents.list()\n for (const a of page) yield a\n },\n\n get: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'GET', path: `/v1/agents/${agentId}` }),\n\n update: async (agentId: string, patch: AgentUpdateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'PATCH', path: `/v1/agents/${agentId}`, body: patch }),\n\n delete: async (agentId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/agents/${agentId}` }),\n\n /**\n * Upload an avatar image for the agent. Server re-encodes to a 512×512\n * WebP and stores it in the public-read avatars bucket; the returned\n * `Agent` has `avatarUrl` set to the canonical public URL with a\n * `?v=` cache-buster.\n *\n * `file` accepts a Node Buffer / Uint8Array / Blob / typed-array.\n * `filename` and `contentType` are optional but help the server log\n * meaningful errors when something rejects.\n */\n uploadAvatar: async (\n agentId: string,\n file: Buffer | Uint8Array | Blob | ArrayBuffer,\n opts: { filename?: string; contentType?: string } = {},\n ): Promise<Agent> => {\n const fd = new FormData()\n const blob =\n file instanceof Blob\n ? file\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Blob([file as any], { type: opts.contentType ?? 'application/octet-stream' })\n fd.append('file', blob, opts.filename ?? 'avatar')\n return http.request<Agent>({\n method: 'POST',\n path: `/v1/agents/${agentId}/avatar`,\n formData: fd,\n })\n },\n\n /**\n * Remove the agent's avatar — both the GCS object and the `avatarUrl`\n * field. Idempotent: calling on an agent without an avatar still\n * returns the agent.\n */\n removeAvatar: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'DELETE', path: `/v1/agents/${agentId}/avatar` }),\n\n // Nested resource. Per-agent webhook CRUD lives at\n // /v1/agents/:agentId/webhooks/* — we expose it as a factory keyed on\n // agentId so consumers can bind once and reuse:\n //\n // const hooks = client.agents.webhooks(myAgentId)\n // await hooks.create({ url, events })\n // await hooks.list()\n webhooks: (agentId: string): AgentWebhooksResource =>\n createAgentWebhooksResource(http, agentId),\n }\n return agents\n}\n\nexport type AgentsResource = ReturnType<typeof createAgentsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CallListFilters,\n CallRecord,\n CallRecordingUrlResponse,\n CallSummary,\n TranscriptTurn,\n} from '../types'\n\nexport const createCallsResource = (http: HttpClient) => {\n const calls = {\n list: async (filters: CallListFilters = {}): Promise<CallSummary[]> => {\n const res = await http.request<{ data: CallSummary[] }>({\n method: 'GET',\n path: '/v1/calls',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination helper. v1 server caps at limit (max 200) in a single\n // page — consumers who ask for \"every call since X\" get that page, the\n // iterator ends. This is the shape we'd extend with cursor support\n // without breaking callers.\n listAll: async function* (filters: CallListFilters = {}): AsyncIterable<CallSummary> {\n const page = await calls.list(filters)\n for (const c of page) yield c\n },\n\n get: async (callId: string): Promise<CallRecord> =>\n http.request<CallRecord>({ method: 'GET', path: `/v1/calls/${callId}` }),\n\n transcript: async (callId: string): Promise<{ callId: string; transcript: TranscriptTurn[] }> =>\n http.request<{ callId: string; transcript: TranscriptTurn[] }>({\n method: 'GET',\n path: `/v1/calls/${callId}/transcript`,\n }),\n\n // Returns a V4 signed URL (1-hour TTL). `ready: false` + `artifact:\n // 'caller-raw'` means the async mix job hasn't finished — the URL still\n // points at a playable file (raw caller PCM).\n recording: async (callId: string): Promise<CallRecordingUrlResponse> =>\n http.request<CallRecordingUrlResponse>({\n method: 'GET',\n path: `/v1/calls/${callId}/recording`,\n }),\n\n // Outbound dialling (POST /v1/calls) + in-call control are Phase 1.4.4 /\n // 1.4.5 respectively — blocked on telephony. Surface clear \"not built\n // yet\" errors here if consumers guess those method names, so they don't\n // silently hit a non-existent endpoint and wonder why it 404s.\n }\n return calls\n}\n\nexport type CallsResource = ReturnType<typeof createCallsResource>\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { HttpClient } from '../http'\nimport type { KnowledgeBase, KnowledgeBaseFile } from '../types'\n\nexport const createKnowledgeBasesResource = (http: HttpClient) => ({\n create: async (name: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({\n method: 'POST',\n path: '/v1/knowledge-bases',\n body: { name },\n }),\n\n list: async (): Promise<KnowledgeBase[]> => {\n const res = await http.request<{ data: KnowledgeBase[] }>({\n method: 'GET',\n path: '/v1/knowledge-bases',\n })\n return res.data\n },\n\n get: async (kbId: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({ method: 'GET', path: `/v1/knowledge-bases/${kbId}` }),\n\n delete: async (kbId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/knowledge-bases/${kbId}` }),\n\n // File upload — accepts either a local file path OR raw bytes + filename.\n // The server ingests synchronously today (Phase 3.1.4 Cloud Tasks is a\n // followup), so the returned file has status='ready' in most cases.\n uploadFile: async (\n kbId: string,\n source:\n | { path: string; filename?: string; mimeType?: string }\n | { data: Buffer | Uint8Array; filename: string; mimeType?: string },\n ): Promise<KnowledgeBaseFile> => {\n const form = new FormData()\n let blob: Blob\n let filename: string\n let mime: string\n\n if ('path' in source) {\n const buf = await fs.promises.readFile(source.path)\n filename = source.filename ?? path.basename(source.path)\n mime = source.mimeType ?? 'application/octet-stream'\n blob = new Blob([new Uint8Array(buf)], { type: mime })\n } else {\n filename = source.filename\n mime = source.mimeType ?? 'application/octet-stream'\n const bytes = source.data instanceof Buffer ? new Uint8Array(source.data) : source.data\n blob = new Blob([bytes], { type: mime })\n }\n form.append('file', blob, filename)\n\n // File uploads can take a while (OCR, chunking, embedding) — give them\n // room before timing out. 5 min cap matches the server's own\n // processing budget.\n return http.request<KnowledgeBaseFile>({\n method: 'POST',\n path: `/v1/knowledge-bases/${kbId}/files`,\n formData: form,\n timeoutMs: 5 * 60 * 1000,\n })\n },\n\n listFiles: async (kbId: string): Promise<KnowledgeBaseFile[]> => {\n const res = await http.request<{ data: KnowledgeBaseFile[] }>({\n method: 'GET',\n path: `/v1/knowledge-bases/${kbId}/files`,\n })\n return res.data\n },\n\n deleteFile: async (kbId: string, fileId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/knowledge-bases/${kbId}/files/${fileId}`,\n }),\n})\n\nexport type KnowledgeBasesResource = ReturnType<typeof createKnowledgeBasesResource>\n","import type { HttpClient } from '../http'\nimport type { LedgerEntry } from '../types'\n\nexport const createCreditsResource = (http: HttpClient) => {\n const credits = {\n getBalance: async (): Promise<{ orgId: string; balanceCents: number }> =>\n http.request<{ orgId: string; balanceCents: number }>({\n method: 'GET',\n path: '/v1/credits/balance',\n }),\n\n // Returns ledger entries newest-first. `limit` is capped at 500 server-side.\n getLedger: async (opts: { limit?: number } = {}): Promise<LedgerEntry[]> => {\n const res = await http.request<{ data: LedgerEntry[] }>({\n method: 'GET',\n path: '/v1/credits/ledger',\n query: opts as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination scaffold — v1 server returns a single page up to\n // limit=500. When cursor pagination lands this is where it gets wired.\n getLedgerAll: async function* (opts: { limit?: number } = {}): AsyncIterable<LedgerEntry> {\n const page = await credits.getLedger(opts)\n for (const e of page) yield e\n },\n }\n return credits\n}\n\nexport type CreditsResource = ReturnType<typeof createCreditsResource>\n","import type { HttpClient } from '../http'\nimport type { CallTokenMintInput, CallTokenMintResult, CallTokenSummary } from '../types'\n\n// Short-lived, agent-scoped `ct_` tokens. Mint one per user session on your\n// backend, hand the raw value to the browser; let this SDK handle lifecycle\n// (revoke on sign-out, list active tokens for an admin panel).\n\nexport const createCallTokensResource = (http: HttpClient) => ({\n // Returns the RAW token value once. Don't log it; pass it straight to the\n // browser + discard server-side. `tokenId` is the stable public handle\n // used for revocation later.\n mint: async (input: CallTokenMintInput): Promise<CallTokenMintResult> =>\n http.request<CallTokenMintResult>({\n method: 'POST',\n path: '/v1/call-tokens',\n body: input,\n }),\n\n // Live tokens only by default. Pass includeRevoked/includeExpired when\n // debugging \"why did my token stop working\".\n list: async (\n opts: { includeRevoked?: boolean; includeExpired?: boolean; limit?: number } = {},\n ): Promise<CallTokenSummary[]> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.includeRevoked) query.includeRevoked = '1'\n if (opts.includeExpired) query.includeExpired = '1'\n if (opts.limit !== undefined) query.limit = opts.limit\n const res = await http.request<{ data: CallTokenSummary[] }>({\n method: 'GET',\n path: '/v1/call-tokens',\n query,\n })\n return res.data\n },\n\n // Idempotent — re-revoking a revoked token returns `alreadyRevoked: true`.\n revoke: async (\n tokenId: string,\n ): Promise<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }> =>\n http.request<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }>({\n method: 'DELETE',\n path: `/v1/call-tokens/${tokenId}`,\n }),\n})\n\nexport type CallTokensResource = ReturnType<typeof createCallTokensResource>\n","import type { HttpClient } from '../http'\nimport type { WebhookDelivery } from '../types'\n\n// Org-wide webhook delivery log. Per-agent CRUD lives on\n// `client.agents.webhooks(agentId)` — this namespace is just the\n// read-only deliveries surface that spans all agents.\n\nexport const createWebhooksResource = (http: HttpClient) => ({\n deliveries: async (\n filters: {\n agentId?: string\n webhookId?: string\n callId?: string\n limit?: number\n } = {},\n ): Promise<WebhookDelivery[]> => {\n const res = await http.request<{ data: WebhookDelivery[] }>({\n method: 'GET',\n path: '/v1/webhooks/deliveries',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n})\n\nexport type WebhooksResource = ReturnType<typeof createWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type { CatalogAgent, CatalogListInput } from '../types'\n\n// Org-scoped consumer endpoints. Today this is just the agent catalog —\n// the trimmed shape your mobile app picks an agent from. Lives at\n// `/v1/orgs/:orgId/agents` rather than reusing the admin `/v1/agents/*`\n// tree on purpose: the catalog response intentionally excludes the\n// operator-only fields (system prompt, tools, KB IDs) so a leaked\n// consumer-backend `sk_` can't lift the operator config out of it.\n//\n// Pattern from your backend:\n//\n// const client = new PlatformClient({ apiKey: process.env.SK })\n// const visible = await client.orgs.listAgents({\n// orgId: process.env.ORG_ID!,\n// userTags: ['tier1'], // omit to get the unfiltered admin view\n// })\n// res.json(visible)\n\nexport const createOrgsResource = (http: HttpClient) => ({\n /**\n * Fetch the agent catalog for an org. Returns the consumer-trimmed shape;\n * no system prompt, tools, or KB info. Use `client.agents.get(agentId)`\n * with the same `sk_` if you need the full admin shape.\n *\n * `userTags`: end-user entitlement tags. When supplied, hides agents\n * whose `allowedUserTags` is non-empty and doesn't intersect with the\n * supplied list. Omit to get the unfiltered admin view.\n *\n * Persona / category filtering is intentionally not a server param —\n * filter the returned list client-side over `name`s if you need it.\n *\n * Throws `403 forbidden` if `orgId` doesn't match the key's org.\n */\n listAgents: async (input: CatalogListInput): Promise<CatalogAgent[]> => {\n const query: Record<string, string | undefined> = {}\n if (input.userTags && input.userTags.length > 0) query.userTags = input.userTags.join(',')\n const res = await http.request<{ data: CatalogAgent[] }>({\n method: 'GET',\n path: `/v1/orgs/${input.orgId}/agents`,\n query,\n })\n return res.data\n },\n})\n\nexport type OrgsResource = ReturnType<typeof createOrgsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CreateRoomInput,\n CreateRoomResponse,\n ListAnalysisResponse,\n ListRoomsResponse,\n ListUtterancesResponse,\n RoomDoc,\n RoomEventAck,\n RoomListFilters,\n RoomTranscriptOptions,\n} from '../types'\n\n// REST wrappers for /v1/rooms — the multi-party video room surface (see\n// docs/superpowers/specs/2026-05-18-multiparty-rooms-design.md). Lets\n// server-side code mint rooms, fetch state, list transcript pages, and end\n// rooms programmatically.\n//\n// Auth model: same Bearer `sk_` flow as every other resource — the server\n// derives `orgId` from the API key, so the SDK never sets a tenant header.\n//\n// Host actions beyond `end` (promote/demote/kick) and observer-token mint\n// live in the server but aren't surfaced here yet — they're operator-side\n// flows that the dashboard hits directly. Re-add here if a programmatic\n// use-case shows up (CI tests, integration harnesses).\n\nexport const createRoomsResource = (http: HttpClient) => ({\n // Provision a new room. Server returns 201 with ONE shared room-level\n // `joinToken` + `joinUrl`. Share the single link with everyone you want in\n // the room — each visitor supplies their own display name at join time and\n // becomes a fresh, distinct participant. The server persists only the\n // token's sha256 hash; the raw token is returned here once and never stored.\n create: async (input: CreateRoomInput): Promise<CreateRoomResponse> =>\n http.request<CreateRoomResponse>({\n method: 'POST',\n path: '/v1/rooms',\n body: input,\n }),\n\n // Listing — opaque cursor pagination (`nextCursor` returned by the server\n // is whatever startAfter() needs, don't parse client-side).\n list: async (filters: RoomListFilters = {}): Promise<ListRoomsResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (filters.status) query.status = filters.status\n if (filters.limit !== undefined) query.limit = filters.limit\n if (filters.cursor) query.cursor = filters.cursor\n return http.request<ListRoomsResponse>({\n method: 'GET',\n path: '/v1/rooms',\n query,\n })\n },\n\n // Fetch a single room. 404s are surfaced as PlatformError('not_found').\n get: async (roomId: string): Promise<RoomDoc> =>\n http.request<RoomDoc>({\n method: 'GET',\n path: `/v1/rooms/${roomId}`,\n }),\n\n // Transcript pages — utterances are ordered by `startedAt asc`. Cursor is\n // an ISO timestamp (server enforces, don't construct yourself).\n transcript: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListUtterancesResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListUtterancesResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/transcript`,\n query,\n })\n },\n\n // Analysis pages — analyzer results ordered by createdAt asc. Auth-only\n // (org-scoped); there is no public token-gated variant. Cursor is an ISO\n // timestamp the server enforces — don't construct it yourself.\n analysis: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListAnalysisResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListAnalysisResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/analysis`,\n query,\n })\n },\n\n // End a room — async on the server: returns 202 + eventId once the\n // controlEvents entry is written. The room-worker observes the entry,\n // broadcasts the system message, and tears down LiveKit shortly after.\n end: async (roomId: string): Promise<RoomEventAck> =>\n http.request<RoomEventAck>({\n method: 'POST',\n path: `/v1/rooms/${roomId}/end`,\n }),\n})\n\nexport type RoomsResource = ReturnType<typeof createRoomsResource>\n","import type { SpaceView } from './types'\n\nexport type JoinUrlStyle = 'query' | 'path'\n\nexport interface BuildJoinUrlOptions {\n /** Required — the developer's own page that renders <VoiceRoom/>. Absolute URL. */\n baseUrl: string\n /** 'query' (default) → ?code=… · 'path' → /… */\n style?: JoinUrlStyle\n}\n\n/**\n * Build a participant join URL on the DEVELOPER's domain from a space (or a\n * raw code). White-label: the returned URL never points at a voissia origin —\n * `baseUrl` is required and supplied by the caller.\n *\n * The default `query` style appends `?code=<code>`, matching how\n * `<VoiceRoom/>` (and the example app) read the code from the URL.\n */\nexport const buildJoinUrl = (\n spaceOrCode: SpaceView | string,\n opts: BuildJoinUrlOptions,\n): string => {\n const code = typeof spaceOrCode === 'string' ? spaceOrCode : spaceOrCode.code\n if (!code) throw new Error('a space code is required')\n const base = opts.baseUrl?.trim()\n if (!base) throw new Error('baseUrl is required')\n\n let url: URL\n try {\n url = new URL(base)\n } catch {\n throw new Error('baseUrl must be an absolute URL (e.g. https://app.example.com/room)')\n }\n if ((opts.style ?? 'query') === 'path') {\n url.pathname = `${url.pathname.replace(/\\/+$/, '')}/${encodeURIComponent(code)}`\n } else {\n url.searchParams.set('code', code)\n }\n return url.toString()\n}\n","import type { HttpClient } from '../http'\nimport type { CreateSpaceInput, ListSpacesResponse, SpacePatch, SpaceView } from '../types'\nimport { buildJoinUrl, type BuildJoinUrlOptions } from '../joinUrl'\n\n// REST wrappers for /v1/spaces — durable multi-party meeting spaces (Phase 23,\n// see docs/superpowers/specs/2026-06-14-spaces-sdk-resource-design.md). Lets\n// server-side code create spaces, read/update/delete them, and build a\n// participant join URL on the developer's own domain.\n//\n// Auth: same Bearer `sk_` flow as every resource — orgId is derived server-side,\n// so the SDK never sets a tenant header.\n//\n// Out of scope here (client/host-side, in @craftedxp/voice-room-react): the\n// public join exchange, host-token mint, lobby admit/deny, moderation, and the\n// X-Host-Key-gated recording controls.\n\nexport const createSpacesResource = (http: HttpClient) => ({\n // Create a durable space. Server returns 201 with the SpaceView, including a\n // stable `code` participants join with. Pass it to `joinUrl` (or the\n // standalone `buildJoinUrl`) to make a link on YOUR domain.\n create: async (input: CreateSpaceInput): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'POST',\n path: '/v1/spaces',\n body: input,\n }),\n\n get: async (spaceId: string): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'GET',\n path: `/v1/spaces/${spaceId}`,\n }),\n\n // List every space for the org. Plain `{ data: [...] }` — no cursor.\n list: async (): Promise<ListSpacesResponse> =>\n http.request<ListSpacesResponse>({\n method: 'GET',\n path: '/v1/spaces',\n }),\n\n // Patch a space. The server rejects an empty patch — pass at least one field.\n update: async (spaceId: string, patch: SpacePatch): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'PATCH',\n path: `/v1/spaces/${spaceId}`,\n body: patch,\n }),\n\n // Delete a space. Resolves once the server returns 204.\n delete: async (spaceId: string): Promise<void> => {\n await http.request<void>({\n method: 'DELETE',\n path: `/v1/spaces/${spaceId}`,\n })\n },\n\n // Convenience: build a participant join URL on your domain from a space (or\n // raw code). Delegates to the standalone `buildJoinUrl` export.\n joinUrl: (spaceOrCode: SpaceView | string, opts: BuildJoinUrlOptions): string =>\n buildJoinUrl(spaceOrCode, opts),\n})\n\nexport type SpacesResource = ReturnType<typeof createSpacesResource>\n","import type { HttpClient } from '../http'\nimport type { ChatEvent } from '../types'\n\nexport interface StartChatInput {\n agentId: string\n text?: string\n}\n\nexport interface Chat {\n id: string\n callId: string\n greeting: AsyncIterable<ChatEvent>\n send(text: string): AsyncIterable<ChatEvent>\n end(): Promise<void>\n}\n\n/**\n * Per-token chat resource. Construct via `client.chatsFor(callToken)` —\n * `callToken` is a raw `ct_…` minted with `channel: 'text'`.\n *\n * The async iterables stream SSE events: `chat.started` (start only),\n * then a sequence of `token` / `tool.call` / `tool.result` / `error`,\n * then `turn.end` which closes the stream. The client must NOT keep\n * a connection open between turns — each `send` is a fresh POST.\n */\nexport const createChatsResource = (http: HttpClient, callToken: string) => ({\n async start(input: StartChatInput): Promise<Chat> {\n const tokenQs = `?token=${encodeURIComponent(callToken)}`\n const streamIterable = http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/agents/${input.agentId}/chat${tokenQs}`,\n body: input.text ? { text: input.text } : {},\n })\n\n // Pull events from the stream until we see chat.started — that's our\n // first event and carries the ids. Pass the rest through as `greeting`.\n let chatId = ''\n let callId = ''\n const buffered: ChatEvent[] = []\n const iter = streamIterable[Symbol.asyncIterator]()\n while (true) {\n const { value, done } = await iter.next()\n if (done) break\n if (value.type === 'chat.started') {\n chatId = value.chatId\n callId = value.callId\n break\n }\n buffered.push(value)\n }\n\n return {\n id: chatId,\n callId,\n greeting: replayThen(buffered, { [Symbol.asyncIterator]: () => iter }),\n send(text: string) {\n return http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/chats/${chatId}/messages${tokenQs}`,\n body: { text },\n })\n },\n async end() {\n await http.request({ method: 'DELETE', path: `/v1/calls/${callId}` })\n },\n }\n },\n})\n\nasync function* replayThen<T>(buffered: T[], rest: AsyncIterable<T>): AsyncIterable<T> {\n for (const x of buffered) yield x\n for await (const x of rest) yield x\n}\n\nexport type ChatsResource = ReturnType<typeof createChatsResource>\n","import type { HttpClient } from '../http'\nimport { PlatformError } from '../errors'\nimport type { SpeechAsset, SpeechJob, SpeechListInput, SpeechSynthesizeInput } from '../types'\n\n/** Comfortably past the server's 60s sync deadline (see `synthesize`). */\nexport const SYNTHESIZE_TIMEOUT_MS = 90_000\n\nexport const createSpeechResource = (http: HttpClient) => {\n const speech = {\n /**\n * Synchronous. Resolves with the ready asset. Throws PlatformError\n * code 'accepted' (status 202, `body` = SpeechJob) when the server hands\n * the job to the queue instead — poll `get(job.id)` or wait for the\n * `speech.ready` webhook.\n */\n synthesize: async (input: SpeechSynthesizeInput): Promise<SpeechAsset> => {\n const res = await http.request<SpeechAsset | SpeechJob>({\n method: 'POST',\n path: '/v1/speech',\n body: input,\n // The server's own sync budget is 60s, after which it hands the job\n // to the queue and answers 202. The default 30s client timeout would\n // abort first — turning a perfectly good handoff into a network\n // error — so give the server room to answer.\n timeoutMs: SYNTHESIZE_TIMEOUT_MS,\n })\n if (res.status === 'ready') return res\n if (res.status === 'failed') {\n throw new PlatformError({\n code: 'internal_error',\n message: res.error?.message ?? 'Speech synthesis failed',\n status: 200,\n body: res,\n })\n }\n throw new PlatformError({\n code: 'accepted',\n message: `Speech job ${res.id} accepted; poll speech.get() or await the speech.ready webhook`,\n status: 202,\n body: res,\n })\n },\n\n /**\n * Asynchronous. Normally returns a queued/processing SpeechJob —\n * completion arrives via the `speech.ready` / `speech.failed` webhook or\n * a later `get(job.id)`. Exception: when `idempotencyKey` matches an\n * existing *ready* asset, the server short-circuits the queue hop and\n * responds 200 with that SpeechAsset directly instead of 202.\n */\n enqueue: async (input: SpeechSynthesizeInput): Promise<SpeechJob | SpeechAsset> =>\n http.request<SpeechJob | SpeechAsset>({\n method: 'POST',\n path: '/v1/speech',\n query: { async: 1 },\n body: input,\n }),\n\n get: async (id: string): Promise<SpeechAsset | SpeechJob> =>\n http.request<SpeechAsset | SpeechJob>({ method: 'GET', path: `/v1/speech/${id}` }),\n\n list: async (\n input: SpeechListInput = {},\n ): Promise<{ items: Array<SpeechAsset | SpeechJob>; cursor?: string }> =>\n http.request({\n method: 'GET',\n path: '/v1/speech',\n query: input as Record<string, string | number | undefined>,\n }),\n\n /** Idempotent. Deleting a ready asset invalidates its URL immediately. */\n delete: async (id: string): Promise<void> => {\n await http.request<void>({ method: 'DELETE', path: `/v1/speech/${id}` })\n },\n }\n return speech\n}\n\nexport type SpeechResource = ReturnType<typeof createSpeechResource>\n","import { createHttpClient, type HttpClient, type HttpClientOptions } from './http'\nimport { createMeResource, type MeResource } from './resources/me'\nimport { createAgentsResource, type AgentsResource } from './resources/agents'\nimport { createCallsResource, type CallsResource } from './resources/calls'\nimport {\n createKnowledgeBasesResource,\n type KnowledgeBasesResource,\n} from './resources/knowledgeBases'\nimport { createCreditsResource, type CreditsResource } from './resources/credits'\nimport { createCallTokensResource, type CallTokensResource } from './resources/callTokens'\nimport { createWebhooksResource, type WebhooksResource } from './resources/webhooks'\nimport { createOrgsResource, type OrgsResource } from './resources/orgs'\nimport { createRoomsResource, type RoomsResource } from './resources/rooms'\nimport { createSpacesResource, type SpacesResource } from './resources/spaces'\nimport { createChatsResource, type ChatsResource } from './resources/chats'\nimport { createSpeechResource, type SpeechResource } from './resources/speech'\n\nexport interface PlatformClientOptions {\n // Full-org API key minted from the dashboard or bootstrap CLI. Start with\n // `sk_`. Never ship to a browser — use `client.callTokens.mint(...)` to\n // generate a narrow `ct_` token for client-side use instead.\n apiKey: string\n // Defaults to the hosted platform. Point at `http://localhost:8080` for\n // local dev or at a self-hosted deployment.\n baseUrl?: string\n // Passthrough tuning for the HTTP layer.\n timeoutMs?: number\n maxRetries?: number\n fetch?: HttpClientOptions['fetch']\n onRequest?: HttpClientOptions['onRequest']\n}\n\n// Single entry point. All resources are lazy-constructed in the constructor\n// so their refs don't incur any per-call allocation. Shape mirrors Stripe /\n// Twilio / Vapi's `resource.method()` convention for familiarity.\nexport class PlatformClient {\n readonly me: MeResource\n readonly agents: AgentsResource\n readonly calls: CallsResource\n readonly knowledgeBases: KnowledgeBasesResource\n readonly credits: CreditsResource\n readonly callTokens: CallTokensResource\n readonly webhooks: WebhooksResource\n readonly orgs: OrgsResource\n readonly rooms: RoomsResource\n readonly spaces: SpacesResource\n readonly speech: SpeechResource\n private readonly _http: HttpClient\n\n constructor(options: PlatformClientOptions) {\n if (!options.apiKey) {\n throw new Error('PlatformClient: `apiKey` is required')\n }\n const http = createHttpClient({\n apiKey: options.apiKey,\n baseUrl: options.baseUrl ?? 'https://api.example.com',\n timeoutMs: options.timeoutMs,\n maxRetries: options.maxRetries,\n fetch: options.fetch,\n onRequest: options.onRequest,\n })\n\n this.me = createMeResource(http)\n this.agents = createAgentsResource(http)\n this.calls = createCallsResource(http)\n this.knowledgeBases = createKnowledgeBasesResource(http)\n this.credits = createCreditsResource(http)\n this.callTokens = createCallTokensResource(http)\n this.webhooks = createWebhooksResource(http)\n this.orgs = createOrgsResource(http)\n this.rooms = createRoomsResource(http)\n this.spaces = createSpacesResource(http)\n this.speech = createSpeechResource(http)\n this._http = http\n }\n\n /**\n * Returns a per-token chat resource bound to the given `ct_…` call token.\n * The token must have been minted with `channel: 'text'`.\n *\n * Note: `chatsFor` is a factory method (not a fixed property) because the\n * underlying resource is scoped to a single call token, whereas this client\n * was constructed with an `sk_` admin key. Each end-user session needs its\n * own `chatsFor(token)` handle.\n */\n public chatsFor(callToken: string): ChatsResource {\n return createChatsResource(this._http, callToken)\n }\n}\n","import crypto from 'node:crypto'\n\n// Helper for consumers receiving webhooks: verify the `X-Platform-Signature-256`\n// header against the raw body + secret. Plain function (not tied to\n// PlatformClient) so Express/Koa/Next.js middleware can use it without\n// instantiating a client.\n//\n// import { verifyWebhookSignature } from '@craftedxp/sdk-node'\n// app.post('/webhooks/voice-agent', express.raw({ type: 'application/json' }), (req, res) => {\n// const sig = req.header('X-Platform-Signature-256') ?? ''\n// if (!verifyWebhookSignature(req.body, sig, process.env.VOICE_AGENT_WEBHOOK_SECRET!)) {\n// return res.status(401).send('invalid signature')\n// }\n// const event = JSON.parse(req.body.toString('utf8'))\n// // ...handle event\n// })\n//\n// The signature is `sha256=<hex HMAC-SHA256 of rawBody with secret>`.\n// Timing-safe compare to avoid microtiming side channels.\n\nexport const verifyWebhookSignature = (\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n): boolean => {\n if (!signatureHeader || !secret) return false\n const [algo, provided] = signatureHeader.split('=')\n if (algo !== 'sha256' || !provided) return false\n\n const expected = crypto\n .createHmac('sha256', secret)\n .update(typeof rawBody === 'string' ? rawBody : rawBody)\n .digest('hex')\n\n // Buffers must match in length for timingSafeEqual.\n const a = Buffer.from(expected, 'hex')\n const b = Buffer.from(provided, 'hex')\n if (a.length !== b.length) return false\n return crypto.timingSafeEqual(a, b)\n}\n"],"mappings":"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","names":["path"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/me.ts","../src/resources/agentWebhooks.ts","../src/resources/agents.ts","../src/resources/calls.ts","../src/resources/knowledgeBases.ts","../src/resources/credits.ts","../src/resources/callTokens.ts","../src/resources/webhooks.ts","../src/resources/orgs.ts","../src/resources/rooms.ts","../src/joinUrl.ts","../src/resources/spaces.ts","../src/resources/chats.ts","../src/resources/speech.ts","../src/PlatformClient.ts","../src/verify.ts"],"sourcesContent":["// Typed error class mirroring the server's ErrorV1 shape:\n// { error: { code, message, field?, docs_url? } }\n//\n// Consumers do `err instanceof PlatformError` to branch on code without\n// parsing string messages. `status` carries the HTTP code for the 1% of\n// cases where the code field isn't enough (e.g. rate-limit → retry-after\n// header correlation).\n\nexport type ApiErrorCode =\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'bad_request'\n | 'conflict'\n | 'rate_limited'\n | 'payment_required'\n | 'internal_error'\n | 'accepted'\n | 'service_unavailable'\n | 'unknown'\n\nexport class PlatformError extends Error {\n readonly code: ApiErrorCode\n readonly status: number\n readonly field?: string\n readonly docsUrl?: string\n // The raw response body for debugging. Intentionally optional — we clear\n // it on `error.toJSON()` so logging libraries don't dump the whole\n // server response into production logs.\n readonly body?: unknown\n\n constructor(params: {\n code: ApiErrorCode\n message: string\n status: number\n field?: string\n docsUrl?: string\n body?: unknown\n }) {\n super(params.message)\n this.name = 'PlatformError'\n this.code = params.code\n this.status = params.status\n this.field = params.field\n this.docsUrl = params.docsUrl\n this.body = params.body\n // Preserve the stack trace — Node's Error doesn't capture it\n // automatically when subclassing in some older runtimes.\n if (\n typeof (Error as typeof Error & { captureStackTrace?: unknown }).captureStackTrace ===\n 'function'\n ) {\n ;(\n Error as typeof Error & { captureStackTrace: (target: unknown, ctor: unknown) => void }\n ).captureStackTrace(this, PlatformError)\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n field: this.field,\n docsUrl: this.docsUrl,\n }\n }\n}\n","import { PlatformError, type ApiErrorCode } from './errors'\n\n// Low-level HTTP wrapper around `fetch` (native in Node 18+). Every resource\n// method routes through here for consistent auth + error handling + retries.\n//\n// v1 scope: JSON-in / JSON-out + multipart for file uploads. No streaming —\n// call WebSockets go through @craftedxp/voice-rn (the React Native client)\n// or a web equivalent, not this server-side SDK.\n\nexport interface HttpClientOptions {\n apiKey: string\n baseUrl: string\n // Default 30s. File uploads can override per-request.\n timeoutMs?: number\n // 429 + 5xx retries. Defaults to 3 attempts (original + 2 retries) with\n // exponential backoff (250ms, 1s). Set to 0 to disable.\n maxRetries?: number\n // Optional — lets consumers swap in a test/mock fetch (or a custom one\n // with instrumentation). Defaults to the global.\n fetch?: typeof fetch\n // Optional — a callback that fires per-request with the final status and\n // duration. Handy for dropping traces into the consumer's observability\n // stack without wrapping each call.\n onRequest?: (info: {\n method: string\n url: string\n status: number\n durationMs: number\n attempt: number\n }) => void\n}\n\nexport interface HttpRequest {\n method: 'GET' | 'POST' | 'PATCH' | 'DELETE'\n path: string // starts with `/`, e.g. `/v1/agents/123`\n query?: Record<string, string | number | boolean | undefined>\n body?: unknown // JSON-serialised — for multipart use `formData`\n formData?: FormData\n timeoutMs?: number\n // Exposed for edge cases where a resource wants to tack on extra\n // headers (we don't have any today but leave the hook).\n headers?: Record<string, string>\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst DEFAULT_MAX_RETRIES = 2 // total attempts = 3\n\nconst buildUrl = (baseUrl: string, path: string, query?: HttpRequest['query']): string => {\n const u = new URL(path, baseUrl)\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue\n u.searchParams.set(k, String(v))\n }\n }\n return u.toString()\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\nconst isRetryable = (status: number): boolean => status === 429 || (status >= 500 && status < 600)\n\n// Parse whatever the server returned into a PlatformError. Falls back to\n// synthesising a sensible error for non-JSON responses.\nconst errorFromResponse = async (res: Response): Promise<PlatformError> => {\n const bodyText = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = bodyText ? JSON.parse(bodyText) : undefined\n } catch {\n // non-JSON response (e.g. an HTML error page from a proxy). Fall\n // through with parsed = undefined.\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n return new PlatformError({\n code: (errObj?.code as ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? bodyText,\n })\n}\n\nexport const createHttpClient = (opts: HttpClientOptions) => {\n const fetchImpl = opts.fetch ?? globalThis.fetch\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n\n if (!fetchImpl) {\n throw new Error('No global fetch available. @craftedxp/sdk-node requires Node >= 18.')\n }\n\n const request = async <T>(req: HttpRequest): Promise<T> => {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n Accept: 'application/json',\n ...(req.headers ?? {}),\n }\n\n // Body shaping: prefer formData when provided; otherwise JSON.\n // `FormData` sets its own Content-Type with boundary — don't preempt it.\n // Typed as `string | FormData | undefined` (a subset of the global\n // BodyInit) so we don't need DOM lib types in this server-side SDK.\n let body: string | FormData | undefined\n if (req.formData) {\n body = req.formData\n } else if (req.body !== undefined) {\n headers['Content-Type'] = 'application/json'\n body = JSON.stringify(req.body)\n }\n\n const reqTimeout = req.timeoutMs ?? timeoutMs\n let lastErr: unknown\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), reqTimeout)\n const started = Date.now()\n try {\n const res = await fetchImpl(url, {\n method: req.method,\n headers,\n body,\n signal: controller.signal,\n })\n const durationMs = Date.now() - started\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs,\n attempt: attempt + 1,\n })\n\n if (res.ok) {\n // 204 No Content — some endpoints (DELETE webhooks) return nothing.\n if (res.status === 204) return undefined as T\n const ct = res.headers.get('content-type') ?? ''\n if (ct.includes('application/json')) {\n return (await res.json()) as T\n }\n // Non-JSON 2xx — rare. Return raw text cast as T.\n return (await res.text()) as unknown as T\n }\n\n // Retryable error: back off + retry up to maxRetries.\n if (isRetryable(res.status) && attempt < maxRetries) {\n const retryAfter = res.headers.get('Retry-After')\n const backoff = retryAfter ? Number(retryAfter) * 1000 : 250 * Math.pow(2, attempt)\n await sleep(backoff)\n continue\n }\n\n throw await errorFromResponse(res)\n } catch (err) {\n if (err instanceof PlatformError) throw err\n // AbortError / network / DNS failures — retry up to maxRetries.\n if (attempt < maxRetries) {\n lastErr = err\n await sleep(250 * Math.pow(2, attempt))\n continue\n }\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n } finally {\n clearTimeout(timer)\n }\n }\n\n // Should be unreachable — the loop either returns, throws, or continues.\n throw lastErr instanceof Error ? lastErr : new Error('request exhausted retries')\n }\n\n // SSE streaming: returns an AsyncIterable<T> over parsed SSE events.\n // Each event is the JSON `data` payload merged with the `event` name as\n // `type`. No retry/backoff mid-stream (stream != request); the caller\n // should treat `error` events from the typed channel as the error path.\n async function* stream<T = unknown>(req: HttpRequest): AsyncIterable<T> {\n const url = buildUrl(opts.baseUrl, req.path, req.query)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${opts.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...(req.headers ?? {}),\n }\n\n const started = Date.now()\n let res: Response\n try {\n res = await fetchImpl(url, {\n method: req.method,\n headers,\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'network error'\n throw new PlatformError({\n code: 'unknown',\n message: `Network error: ${msg}`,\n status: 0,\n })\n }\n\n opts.onRequest?.({\n method: req.method,\n url,\n status: res.status,\n durationMs: Date.now() - started,\n attempt: 1,\n })\n\n if (!res.ok || !res.body) {\n const text = await res.text().catch(() => '')\n let parsed: unknown = undefined\n try {\n parsed = text ? JSON.parse(text) : undefined\n } catch {\n // non-JSON — fall through\n }\n const errObj = (\n parsed as { error?: { code?: string; message?: string; field?: string; docs_url?: string } }\n )?.error\n throw new PlatformError({\n code: (errObj?.code as import('./errors').ApiErrorCode) ?? 'unknown',\n message: errObj?.message ?? res.statusText ?? `HTTP ${res.status}`,\n status: res.status,\n field: errObj?.field,\n docsUrl: errObj?.docs_url,\n body: parsed ?? text,\n })\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buf = ''\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) return\n buf += decoder.decode(value, { stream: true })\n let idx: number\n while ((idx = buf.indexOf('\\n\\n')) >= 0) {\n const block = buf.slice(0, idx)\n buf = buf.slice(idx + 2)\n let event = 'message'\n let data = ''\n for (const line of block.split('\\n')) {\n if (line.startsWith(':')) continue // SSE comment / keepalive\n if (line.startsWith('event:')) event = line.slice(6).trim()\n else if (line.startsWith('data:')) data += line.slice(5).trim()\n }\n if (!data) continue\n try {\n const parsed = JSON.parse(data)\n yield { type: event, ...parsed } as T\n } catch {\n // Drop malformed JSON; an error event will arrive via the typed channel.\n }\n }\n }\n }\n\n return { request, stream }\n}\n\nexport type HttpClient = ReturnType<typeof createHttpClient>\n","import type { HttpClient } from '../http'\nimport type { MeResponse } from '../types'\n\nexport const createMeResource = (http: HttpClient) => ({\n // Smoke test — confirms the API key is valid and returns the org + balance.\n // Most consumers use this as a ping on startup.\n get: async (): Promise<MeResponse> => http.request<MeResponse>({ method: 'GET', path: '/v1/me' }),\n})\n\nexport type MeResource = ReturnType<typeof createMeResource>\n","import type { HttpClient } from '../http'\nimport type {\n WebhookConfig,\n WebhookCreateInput,\n WebhookDelivery,\n WebhookUpdateInput,\n} from '../types'\n\n// Per-agent webhook resource, scoped at factory time to a specific agentId.\n// All endpoints mirror /v1/agents/:agentId/webhooks[/:id].\n\nexport const createAgentWebhooksResource = (http: HttpClient, agentId: string) => ({\n // Returns the webhook + its signing secret. The secret is only present in\n // this response — persist immediately, subsequent GETs omit it. Use the\n // secret to verify `X-Platform-Signature-256` (sha256=hex HMAC over body).\n create: async (input: WebhookCreateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks`,\n body: input,\n }),\n\n list: async (): Promise<WebhookConfig[]> => {\n const res = await http.request<{ data: WebhookConfig[] }>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks`,\n })\n return res.data\n },\n\n get: async (webhookId: string): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'GET',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n update: async (webhookId: string, patch: WebhookUpdateInput): Promise<WebhookConfig> =>\n http.request<WebhookConfig>({\n method: 'PATCH',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n body: patch,\n }),\n\n delete: async (webhookId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}`,\n }),\n\n // Fires a synthetic call.started against this webhook and returns the\n // full delivery record (including attempt status codes) once the retry\n // sequence has finished. Useful during setup to verify receiver wiring.\n test: async (webhookId: string): Promise<WebhookDelivery> =>\n http.request<WebhookDelivery>({\n method: 'POST',\n path: `/v1/agents/${agentId}/webhooks/${webhookId}/test`,\n }),\n})\n\nexport type AgentWebhooksResource = ReturnType<typeof createAgentWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type {\n Agent,\n AgentCreateInput,\n AgentImportResult,\n AgentListAllOptions,\n AgentListOptions,\n AgentListResponse,\n AgentUpdateInput,\n BulkAgentEnvelope,\n SingleAgentEnvelope,\n} from '../types'\nimport { createAgentWebhooksResource, type AgentWebhooksResource } from './agentWebhooks'\n\n// The server returns Agent with `apiKey` stripped and `hasApiKey: boolean`\n// on the model. We type the happy path but keep our input type permissive\n// so consumers can POST a plaintext `apiKey` that the server encrypts +\n// swaps for `apiKeySecret` on persistence.\n\nexport const createAgentsResource = (http: HttpClient) => {\n const agents = {\n create: async (input: AgentCreateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'POST', path: '/v1/agents', body: input }),\n\n // One page of agents. Opaque cursor pagination (`nextCursor` is whatever\n // the server needs to resume — don't parse it client-side). Returns the\n // full `{ data, nextCursor }` envelope; pass `nextCursor` back as `cursor`\n // to walk pages, or use `listAll` to iterate every agent.\n list: async (opts: AgentListOptions = {}): Promise<AgentListResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.limit !== undefined) query.limit = opts.limit\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.q) query.q = opts.q\n return http.request<AgentListResponse>({ method: 'GET', path: '/v1/agents', query })\n },\n\n // Async iterator for \"give me every agent\" — walks the cursor internally,\n // fetching one page at a time and yielding each agent, so consumers never\n // have to thread `nextCursor` themselves.\n listAll: async function* (opts: AgentListAllOptions = {}): AsyncIterable<Agent> {\n let cursor: string | undefined\n do {\n const page = await agents.list({ q: opts.q, limit: opts.pageSize, cursor })\n for (const a of page.data) yield a\n cursor = page.nextCursor ?? undefined\n } while (cursor)\n },\n\n get: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'GET', path: `/v1/agents/${agentId}` }),\n\n update: async (agentId: string, patch: AgentUpdateInput): Promise<Agent> =>\n http.request<Agent>({ method: 'PATCH', path: `/v1/agents/${agentId}`, body: patch }),\n\n delete: async (agentId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/agents/${agentId}` }),\n\n /**\n * Upload an avatar image for the agent. Server re-encodes to a 512×512\n * WebP and stores it in the public-read avatars bucket; the returned\n * `Agent` has `avatarUrl` set to the canonical public URL with a\n * `?v=` cache-buster.\n *\n * `file` accepts a Node Buffer / Uint8Array / Blob / typed-array.\n * `filename` and `contentType` are optional but help the server log\n * meaningful errors when something rejects.\n */\n uploadAvatar: async (\n agentId: string,\n file: Buffer | Uint8Array | Blob | ArrayBuffer,\n opts: { filename?: string; contentType?: string } = {},\n ): Promise<Agent> => {\n const fd = new FormData()\n const blob =\n file instanceof Blob\n ? file\n : // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new Blob([file as any], { type: opts.contentType ?? 'application/octet-stream' })\n fd.append('file', blob, opts.filename ?? 'avatar')\n return http.request<Agent>({\n method: 'POST',\n path: `/v1/agents/${agentId}/avatar`,\n formData: fd,\n })\n },\n\n /**\n * Remove the agent's avatar — both the GCS object and the `avatarUrl`\n * field. Idempotent: calling on an agent without an avatar still\n * returns the agent.\n */\n removeAvatar: async (agentId: string): Promise<Agent> =>\n http.request<Agent>({ method: 'DELETE', path: `/v1/agents/${agentId}/avatar` }),\n\n /**\n * Export a single agent as a portable envelope. Server-managed and\n * sensitive fields (ids, timestamps, avatar, BYO LLM key) are stripped —\n * the envelope carries only the config needed to recreate the agent via\n * {@link import}. Feed it straight back into `agents.import(envelope)`,\n * in this org or another.\n */\n export: async (agentId: string): Promise<SingleAgentEnvelope> =>\n http.request<SingleAgentEnvelope>({\n method: 'GET',\n path: `/v1/agents/${agentId}/export`,\n }),\n\n /**\n * Export every agent in the org as one bulk envelope — the same portable\n * shape as {@link export}, with an `agents` array. Round-trips through\n * {@link import} unchanged.\n */\n exportAll: async (): Promise<BulkAgentEnvelope> =>\n http.request<BulkAgentEnvelope>({ method: 'GET', path: '/v1/agents/export' }),\n\n /**\n * Import agents from a single or bulk envelope produced by {@link export} /\n * {@link exportAll}. Best-effort per agent: the result's `imported` array\n * holds each created agent plus non-fatal `warnings` (e.g. a knowledge-base\n * reference cleared because it doesn't exist in the target org, or a BYO\n * LLM key that must be re-added), and `failed` holds the ones that couldn't\n * be created. A partial import still resolves — inspect both arrays.\n */\n import: async (envelope: SingleAgentEnvelope | BulkAgentEnvelope): Promise<AgentImportResult> =>\n http.request<AgentImportResult>({\n method: 'POST',\n path: '/v1/agents/import',\n body: envelope,\n }),\n\n // Nested resource. Per-agent webhook CRUD lives at\n // /v1/agents/:agentId/webhooks/* — we expose it as a factory keyed on\n // agentId so consumers can bind once and reuse:\n //\n // const hooks = client.agents.webhooks(myAgentId)\n // await hooks.create({ url, events })\n // await hooks.list()\n webhooks: (agentId: string): AgentWebhooksResource =>\n createAgentWebhooksResource(http, agentId),\n }\n return agents\n}\n\nexport type AgentsResource = ReturnType<typeof createAgentsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CallListFilters,\n CallRecord,\n CallRecordingUrlResponse,\n CallSummary,\n TranscriptTurn,\n} from '../types'\n\nexport const createCallsResource = (http: HttpClient) => {\n const calls = {\n list: async (filters: CallListFilters = {}): Promise<CallSummary[]> => {\n const res = await http.request<{ data: CallSummary[] }>({\n method: 'GET',\n path: '/v1/calls',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination helper. v1 server caps at limit (max 200) in a single\n // page — consumers who ask for \"every call since X\" get that page, the\n // iterator ends. This is the shape we'd extend with cursor support\n // without breaking callers.\n listAll: async function* (filters: CallListFilters = {}): AsyncIterable<CallSummary> {\n const page = await calls.list(filters)\n for (const c of page) yield c\n },\n\n get: async (callId: string): Promise<CallRecord> =>\n http.request<CallRecord>({ method: 'GET', path: `/v1/calls/${callId}` }),\n\n transcript: async (callId: string): Promise<{ callId: string; transcript: TranscriptTurn[] }> =>\n http.request<{ callId: string; transcript: TranscriptTurn[] }>({\n method: 'GET',\n path: `/v1/calls/${callId}/transcript`,\n }),\n\n // Returns a V4 signed URL (1-hour TTL). `ready: false` + `artifact:\n // 'caller-raw'` means the async mix job hasn't finished — the URL still\n // points at a playable file (raw caller PCM).\n recording: async (callId: string): Promise<CallRecordingUrlResponse> =>\n http.request<CallRecordingUrlResponse>({\n method: 'GET',\n path: `/v1/calls/${callId}/recording`,\n }),\n\n // Outbound dialling (POST /v1/calls) + in-call control are Phase 1.4.4 /\n // 1.4.5 respectively — blocked on telephony. Surface clear \"not built\n // yet\" errors here if consumers guess those method names, so they don't\n // silently hit a non-existent endpoint and wonder why it 404s.\n }\n return calls\n}\n\nexport type CallsResource = ReturnType<typeof createCallsResource>\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { HttpClient } from '../http'\nimport type { KnowledgeBase, KnowledgeBaseFile } from '../types'\n\nexport const createKnowledgeBasesResource = (http: HttpClient) => ({\n create: async (name: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({\n method: 'POST',\n path: '/v1/knowledge-bases',\n body: { name },\n }),\n\n list: async (): Promise<KnowledgeBase[]> => {\n const res = await http.request<{ data: KnowledgeBase[] }>({\n method: 'GET',\n path: '/v1/knowledge-bases',\n })\n return res.data\n },\n\n get: async (kbId: string): Promise<KnowledgeBase> =>\n http.request<KnowledgeBase>({ method: 'GET', path: `/v1/knowledge-bases/${kbId}` }),\n\n delete: async (kbId: string): Promise<void> =>\n http.request<void>({ method: 'DELETE', path: `/v1/knowledge-bases/${kbId}` }),\n\n // File upload — accepts either a local file path OR raw bytes + filename.\n // The server ingests synchronously today (Phase 3.1.4 Cloud Tasks is a\n // followup), so the returned file has status='ready' in most cases.\n uploadFile: async (\n kbId: string,\n source:\n | { path: string; filename?: string; mimeType?: string }\n | { data: Buffer | Uint8Array; filename: string; mimeType?: string },\n ): Promise<KnowledgeBaseFile> => {\n const form = new FormData()\n let blob: Blob\n let filename: string\n let mime: string\n\n if ('path' in source) {\n const buf = await fs.promises.readFile(source.path)\n filename = source.filename ?? path.basename(source.path)\n mime = source.mimeType ?? 'application/octet-stream'\n blob = new Blob([new Uint8Array(buf)], { type: mime })\n } else {\n filename = source.filename\n mime = source.mimeType ?? 'application/octet-stream'\n const bytes = source.data instanceof Buffer ? new Uint8Array(source.data) : source.data\n blob = new Blob([bytes], { type: mime })\n }\n form.append('file', blob, filename)\n\n // File uploads can take a while (OCR, chunking, embedding) — give them\n // room before timing out. 5 min cap matches the server's own\n // processing budget.\n return http.request<KnowledgeBaseFile>({\n method: 'POST',\n path: `/v1/knowledge-bases/${kbId}/files`,\n formData: form,\n timeoutMs: 5 * 60 * 1000,\n })\n },\n\n listFiles: async (kbId: string): Promise<KnowledgeBaseFile[]> => {\n const res = await http.request<{ data: KnowledgeBaseFile[] }>({\n method: 'GET',\n path: `/v1/knowledge-bases/${kbId}/files`,\n })\n return res.data\n },\n\n deleteFile: async (kbId: string, fileId: string): Promise<void> =>\n http.request<void>({\n method: 'DELETE',\n path: `/v1/knowledge-bases/${kbId}/files/${fileId}`,\n }),\n})\n\nexport type KnowledgeBasesResource = ReturnType<typeof createKnowledgeBasesResource>\n","import type { HttpClient } from '../http'\nimport type { LedgerEntry } from '../types'\n\nexport const createCreditsResource = (http: HttpClient) => {\n const credits = {\n getBalance: async (): Promise<{ orgId: string; balanceCents: number }> =>\n http.request<{ orgId: string; balanceCents: number }>({\n method: 'GET',\n path: '/v1/credits/balance',\n }),\n\n // Returns ledger entries newest-first. `limit` is capped at 500 server-side.\n getLedger: async (opts: { limit?: number } = {}): Promise<LedgerEntry[]> => {\n const res = await http.request<{ data: LedgerEntry[] }>({\n method: 'GET',\n path: '/v1/credits/ledger',\n query: opts as Record<string, string | number | undefined>,\n })\n return res.data\n },\n\n // Auto-pagination scaffold — v1 server returns a single page up to\n // limit=500. When cursor pagination lands this is where it gets wired.\n getLedgerAll: async function* (opts: { limit?: number } = {}): AsyncIterable<LedgerEntry> {\n const page = await credits.getLedger(opts)\n for (const e of page) yield e\n },\n }\n return credits\n}\n\nexport type CreditsResource = ReturnType<typeof createCreditsResource>\n","import type { HttpClient } from '../http'\nimport type { CallTokenMintInput, CallTokenMintResult, CallTokenSummary } from '../types'\n\n// Short-lived, agent-scoped `ct_` tokens. Mint one per user session on your\n// backend, hand the raw value to the browser; let this SDK handle lifecycle\n// (revoke on sign-out, list active tokens for an admin panel).\n\nexport const createCallTokensResource = (http: HttpClient) => ({\n // Returns the RAW token value once. Don't log it; pass it straight to the\n // browser + discard server-side. `tokenId` is the stable public handle\n // used for revocation later.\n mint: async (input: CallTokenMintInput): Promise<CallTokenMintResult> =>\n http.request<CallTokenMintResult>({\n method: 'POST',\n path: '/v1/call-tokens',\n body: input,\n }),\n\n // Live tokens only by default. Pass includeRevoked/includeExpired when\n // debugging \"why did my token stop working\".\n list: async (\n opts: { includeRevoked?: boolean; includeExpired?: boolean; limit?: number } = {},\n ): Promise<CallTokenSummary[]> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.includeRevoked) query.includeRevoked = '1'\n if (opts.includeExpired) query.includeExpired = '1'\n if (opts.limit !== undefined) query.limit = opts.limit\n const res = await http.request<{ data: CallTokenSummary[] }>({\n method: 'GET',\n path: '/v1/call-tokens',\n query,\n })\n return res.data\n },\n\n // Idempotent — re-revoking a revoked token returns `alreadyRevoked: true`.\n revoke: async (\n tokenId: string,\n ): Promise<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }> =>\n http.request<{ tokenId: string; revoked: boolean; alreadyRevoked?: boolean }>({\n method: 'DELETE',\n path: `/v1/call-tokens/${tokenId}`,\n }),\n})\n\nexport type CallTokensResource = ReturnType<typeof createCallTokensResource>\n","import type { HttpClient } from '../http'\nimport type { WebhookDelivery } from '../types'\n\n// Org-wide webhook delivery log. Per-agent CRUD lives on\n// `client.agents.webhooks(agentId)` — this namespace is just the\n// read-only deliveries surface that spans all agents.\n\nexport const createWebhooksResource = (http: HttpClient) => ({\n deliveries: async (\n filters: {\n agentId?: string\n webhookId?: string\n callId?: string\n limit?: number\n } = {},\n ): Promise<WebhookDelivery[]> => {\n const res = await http.request<{ data: WebhookDelivery[] }>({\n method: 'GET',\n path: '/v1/webhooks/deliveries',\n query: filters as Record<string, string | number | undefined>,\n })\n return res.data\n },\n})\n\nexport type WebhooksResource = ReturnType<typeof createWebhooksResource>\n","import type { HttpClient } from '../http'\nimport type { CatalogAgent, CatalogListInput } from '../types'\n\n// Org-scoped consumer endpoints. Today this is just the agent catalog —\n// the trimmed shape your mobile app picks an agent from. Lives at\n// `/v1/orgs/:orgId/agents` rather than reusing the admin `/v1/agents/*`\n// tree on purpose: the catalog response intentionally excludes the\n// operator-only fields (system prompt, tools, KB IDs) so a leaked\n// consumer-backend `sk_` can't lift the operator config out of it.\n//\n// Pattern from your backend:\n//\n// const client = new PlatformClient({ apiKey: process.env.SK })\n// const visible = await client.orgs.listAgents({\n// orgId: process.env.ORG_ID!,\n// userTags: ['tier1'], // omit to get the unfiltered admin view\n// })\n// res.json(visible)\n\nexport const createOrgsResource = (http: HttpClient) => ({\n /**\n * Fetch the agent catalog for an org. Returns the consumer-trimmed shape;\n * no system prompt, tools, or KB info. Use `client.agents.get(agentId)`\n * with the same `sk_` if you need the full admin shape.\n *\n * `userTags`: end-user entitlement tags. When supplied, hides agents\n * whose `allowedUserTags` is non-empty and doesn't intersect with the\n * supplied list. Omit to get the unfiltered admin view.\n *\n * Persona / category filtering is intentionally not a server param —\n * filter the returned list client-side over `name`s if you need it.\n *\n * Throws `403 forbidden` if `orgId` doesn't match the key's org.\n */\n listAgents: async (input: CatalogListInput): Promise<CatalogAgent[]> => {\n const query: Record<string, string | undefined> = {}\n if (input.userTags && input.userTags.length > 0) query.userTags = input.userTags.join(',')\n const res = await http.request<{ data: CatalogAgent[] }>({\n method: 'GET',\n path: `/v1/orgs/${input.orgId}/agents`,\n query,\n })\n return res.data\n },\n})\n\nexport type OrgsResource = ReturnType<typeof createOrgsResource>\n","import type { HttpClient } from '../http'\nimport type {\n CreateRoomInput,\n CreateRoomResponse,\n ListAnalysisResponse,\n ListRoomsResponse,\n ListUtterancesResponse,\n RoomDoc,\n RoomEventAck,\n RoomListFilters,\n RoomTranscriptOptions,\n} from '../types'\n\n// REST wrappers for /v1/rooms — the multi-party video room surface (see\n// docs/superpowers/specs/2026-05-18-multiparty-rooms-design.md). Lets\n// server-side code mint rooms, fetch state, list transcript pages, and end\n// rooms programmatically.\n//\n// Auth model: same Bearer `sk_` flow as every other resource — the server\n// derives `orgId` from the API key, so the SDK never sets a tenant header.\n//\n// Host actions beyond `end` (promote/demote/kick) and observer-token mint\n// live in the server but aren't surfaced here yet — they're operator-side\n// flows that the dashboard hits directly. Re-add here if a programmatic\n// use-case shows up (CI tests, integration harnesses).\n\nexport const createRoomsResource = (http: HttpClient) => ({\n // Provision a new room. Server returns 201 with ONE shared room-level\n // `joinToken` + `joinUrl`. Share the single link with everyone you want in\n // the room — each visitor supplies their own display name at join time and\n // becomes a fresh, distinct participant. The server persists only the\n // token's sha256 hash; the raw token is returned here once and never stored.\n create: async (input: CreateRoomInput): Promise<CreateRoomResponse> =>\n http.request<CreateRoomResponse>({\n method: 'POST',\n path: '/v1/rooms',\n body: input,\n }),\n\n // Listing — opaque cursor pagination (`nextCursor` returned by the server\n // is whatever startAfter() needs, don't parse client-side).\n list: async (filters: RoomListFilters = {}): Promise<ListRoomsResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (filters.status) query.status = filters.status\n if (filters.limit !== undefined) query.limit = filters.limit\n if (filters.cursor) query.cursor = filters.cursor\n return http.request<ListRoomsResponse>({\n method: 'GET',\n path: '/v1/rooms',\n query,\n })\n },\n\n // Fetch a single room. 404s are surfaced as PlatformError('not_found').\n get: async (roomId: string): Promise<RoomDoc> =>\n http.request<RoomDoc>({\n method: 'GET',\n path: `/v1/rooms/${roomId}`,\n }),\n\n // Transcript pages — utterances are ordered by `startedAt asc`. Cursor is\n // an ISO timestamp (server enforces, don't construct yourself).\n transcript: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListUtterancesResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListUtterancesResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/transcript`,\n query,\n })\n },\n\n // Analysis pages — analyzer results ordered by createdAt asc. Auth-only\n // (org-scoped); there is no public token-gated variant. Cursor is an ISO\n // timestamp the server enforces — don't construct it yourself.\n analysis: async (\n roomId: string,\n opts: RoomTranscriptOptions = {},\n ): Promise<ListAnalysisResponse> => {\n const query: Record<string, string | number | undefined> = {}\n if (opts.cursor) query.cursor = opts.cursor\n if (opts.limit !== undefined) query.limit = opts.limit\n return http.request<ListAnalysisResponse>({\n method: 'GET',\n path: `/v1/rooms/${roomId}/analysis`,\n query,\n })\n },\n\n // End a room — async on the server: returns 202 + eventId once the\n // controlEvents entry is written. The room-worker observes the entry,\n // broadcasts the system message, and tears down LiveKit shortly after.\n end: async (roomId: string): Promise<RoomEventAck> =>\n http.request<RoomEventAck>({\n method: 'POST',\n path: `/v1/rooms/${roomId}/end`,\n }),\n})\n\nexport type RoomsResource = ReturnType<typeof createRoomsResource>\n","import type { SpaceView } from './types'\n\nexport type JoinUrlStyle = 'query' | 'path'\n\nexport interface BuildJoinUrlOptions {\n /** Required — the developer's own page that renders <VoiceRoom/>. Absolute URL. */\n baseUrl: string\n /** 'query' (default) → ?code=… · 'path' → /… */\n style?: JoinUrlStyle\n}\n\n/**\n * Build a participant join URL on the DEVELOPER's domain from a space (or a\n * raw code). White-label: the returned URL never points at a voissia origin —\n * `baseUrl` is required and supplied by the caller.\n *\n * The default `query` style appends `?code=<code>`, matching how\n * `<VoiceRoom/>` (and the example app) read the code from the URL.\n */\nexport const buildJoinUrl = (\n spaceOrCode: SpaceView | string,\n opts: BuildJoinUrlOptions,\n): string => {\n const code = typeof spaceOrCode === 'string' ? spaceOrCode : spaceOrCode.code\n if (!code) throw new Error('a space code is required')\n const base = opts.baseUrl?.trim()\n if (!base) throw new Error('baseUrl is required')\n\n let url: URL\n try {\n url = new URL(base)\n } catch {\n throw new Error('baseUrl must be an absolute URL (e.g. https://app.example.com/room)')\n }\n if ((opts.style ?? 'query') === 'path') {\n url.pathname = `${url.pathname.replace(/\\/+$/, '')}/${encodeURIComponent(code)}`\n } else {\n url.searchParams.set('code', code)\n }\n return url.toString()\n}\n","import type { HttpClient } from '../http'\nimport type { CreateSpaceInput, ListSpacesResponse, SpacePatch, SpaceView } from '../types'\nimport { buildJoinUrl, type BuildJoinUrlOptions } from '../joinUrl'\n\n// REST wrappers for /v1/spaces — durable multi-party meeting spaces (Phase 23,\n// see docs/superpowers/specs/2026-06-14-spaces-sdk-resource-design.md). Lets\n// server-side code create spaces, read/update/delete them, and build a\n// participant join URL on the developer's own domain.\n//\n// Auth: same Bearer `sk_` flow as every resource — orgId is derived server-side,\n// so the SDK never sets a tenant header.\n//\n// Out of scope here (client/host-side, in @craftedxp/voice-room-react): the\n// public join exchange, host-token mint, lobby admit/deny, moderation, and the\n// X-Host-Key-gated recording controls.\n\nexport const createSpacesResource = (http: HttpClient) => ({\n // Create a durable space. Server returns 201 with the SpaceView, including a\n // stable `code` participants join with. Pass it to `joinUrl` (or the\n // standalone `buildJoinUrl`) to make a link on YOUR domain.\n create: async (input: CreateSpaceInput): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'POST',\n path: '/v1/spaces',\n body: input,\n }),\n\n get: async (spaceId: string): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'GET',\n path: `/v1/spaces/${spaceId}`,\n }),\n\n // List every space for the org. Plain `{ data: [...] }` — no cursor.\n list: async (): Promise<ListSpacesResponse> =>\n http.request<ListSpacesResponse>({\n method: 'GET',\n path: '/v1/spaces',\n }),\n\n // Patch a space. The server rejects an empty patch — pass at least one field.\n update: async (spaceId: string, patch: SpacePatch): Promise<SpaceView> =>\n http.request<SpaceView>({\n method: 'PATCH',\n path: `/v1/spaces/${spaceId}`,\n body: patch,\n }),\n\n // Delete a space. Resolves once the server returns 204.\n delete: async (spaceId: string): Promise<void> => {\n await http.request<void>({\n method: 'DELETE',\n path: `/v1/spaces/${spaceId}`,\n })\n },\n\n // Convenience: build a participant join URL on your domain from a space (or\n // raw code). Delegates to the standalone `buildJoinUrl` export.\n joinUrl: (spaceOrCode: SpaceView | string, opts: BuildJoinUrlOptions): string =>\n buildJoinUrl(spaceOrCode, opts),\n})\n\nexport type SpacesResource = ReturnType<typeof createSpacesResource>\n","import type { HttpClient } from '../http'\nimport type { ChatEvent } from '../types'\n\nexport interface StartChatInput {\n agentId: string\n text?: string\n}\n\nexport interface Chat {\n id: string\n callId: string\n greeting: AsyncIterable<ChatEvent>\n send(text: string): AsyncIterable<ChatEvent>\n end(): Promise<void>\n}\n\n/**\n * Per-token chat resource. Construct via `client.chatsFor(callToken)` —\n * `callToken` is a raw `ct_…` minted with `channel: 'text'`.\n *\n * The async iterables stream SSE events: `chat.started` (start only),\n * then a sequence of `token` / `tool.call` / `tool.result` / `error`,\n * then `turn.end` which closes the stream. The client must NOT keep\n * a connection open between turns — each `send` is a fresh POST.\n */\nexport const createChatsResource = (http: HttpClient, callToken: string) => ({\n async start(input: StartChatInput): Promise<Chat> {\n const tokenQs = `?token=${encodeURIComponent(callToken)}`\n const streamIterable = http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/agents/${input.agentId}/chat${tokenQs}`,\n body: input.text ? { text: input.text } : {},\n })\n\n // Pull events from the stream until we see chat.started — that's our\n // first event and carries the ids. Pass the rest through as `greeting`.\n let chatId = ''\n let callId = ''\n const buffered: ChatEvent[] = []\n const iter = streamIterable[Symbol.asyncIterator]()\n while (true) {\n const { value, done } = await iter.next()\n if (done) break\n if (value.type === 'chat.started') {\n chatId = value.chatId\n callId = value.callId\n break\n }\n buffered.push(value)\n }\n\n return {\n id: chatId,\n callId,\n greeting: replayThen(buffered, { [Symbol.asyncIterator]: () => iter }),\n send(text: string) {\n return http.stream<ChatEvent>({\n method: 'POST',\n path: `/v1/chats/${chatId}/messages${tokenQs}`,\n body: { text },\n })\n },\n async end() {\n await http.request({ method: 'DELETE', path: `/v1/calls/${callId}` })\n },\n }\n },\n})\n\nasync function* replayThen<T>(buffered: T[], rest: AsyncIterable<T>): AsyncIterable<T> {\n for (const x of buffered) yield x\n for await (const x of rest) yield x\n}\n\nexport type ChatsResource = ReturnType<typeof createChatsResource>\n","import type { HttpClient } from '../http'\nimport { PlatformError } from '../errors'\nimport type { SpeechAsset, SpeechJob, SpeechListInput, SpeechSynthesizeInput } from '../types'\n\n/** Comfortably past the server's 60s sync deadline (see `synthesize`). */\nexport const SYNTHESIZE_TIMEOUT_MS = 90_000\n\nexport const createSpeechResource = (http: HttpClient) => {\n const speech = {\n /**\n * Synchronous. Resolves with the ready asset. Throws PlatformError\n * code 'accepted' (status 202, `body` = SpeechJob) when the server hands\n * the job to the queue instead — poll `get(job.id)` or wait for the\n * `speech.ready` webhook.\n */\n synthesize: async (input: SpeechSynthesizeInput): Promise<SpeechAsset> => {\n const res = await http.request<SpeechAsset | SpeechJob>({\n method: 'POST',\n path: '/v1/speech',\n body: input,\n // The server's own sync budget is 60s, after which it hands the job\n // to the queue and answers 202. The default 30s client timeout would\n // abort first — turning a perfectly good handoff into a network\n // error — so give the server room to answer.\n timeoutMs: SYNTHESIZE_TIMEOUT_MS,\n })\n if (res.status === 'ready') return res\n if (res.status === 'failed') {\n throw new PlatformError({\n code: 'internal_error',\n message: res.error?.message ?? 'Speech synthesis failed',\n status: 200,\n body: res,\n })\n }\n throw new PlatformError({\n code: 'accepted',\n message: `Speech job ${res.id} accepted; poll speech.get() or await the speech.ready webhook`,\n status: 202,\n body: res,\n })\n },\n\n /**\n * Asynchronous. Normally returns a queued/processing SpeechJob —\n * completion arrives via the `speech.ready` / `speech.failed` webhook or\n * a later `get(job.id)`. Exception: when `idempotencyKey` matches an\n * existing *ready* asset, the server short-circuits the queue hop and\n * responds 200 with that SpeechAsset directly instead of 202.\n */\n enqueue: async (input: SpeechSynthesizeInput): Promise<SpeechJob | SpeechAsset> =>\n http.request<SpeechJob | SpeechAsset>({\n method: 'POST',\n path: '/v1/speech',\n query: { async: 1 },\n body: input,\n }),\n\n get: async (id: string): Promise<SpeechAsset | SpeechJob> =>\n http.request<SpeechAsset | SpeechJob>({ method: 'GET', path: `/v1/speech/${id}` }),\n\n list: async (\n input: SpeechListInput = {},\n ): Promise<{ items: Array<SpeechAsset | SpeechJob>; cursor?: string }> =>\n http.request({\n method: 'GET',\n path: '/v1/speech',\n query: input as Record<string, string | number | undefined>,\n }),\n\n /** Idempotent. Deleting a ready asset invalidates its URL immediately. */\n delete: async (id: string): Promise<void> => {\n await http.request<void>({ method: 'DELETE', path: `/v1/speech/${id}` })\n },\n }\n return speech\n}\n\nexport type SpeechResource = ReturnType<typeof createSpeechResource>\n","import { createHttpClient, type HttpClient, type HttpClientOptions } from './http'\nimport { createMeResource, type MeResource } from './resources/me'\nimport { createAgentsResource, type AgentsResource } from './resources/agents'\nimport { createCallsResource, type CallsResource } from './resources/calls'\nimport {\n createKnowledgeBasesResource,\n type KnowledgeBasesResource,\n} from './resources/knowledgeBases'\nimport { createCreditsResource, type CreditsResource } from './resources/credits'\nimport { createCallTokensResource, type CallTokensResource } from './resources/callTokens'\nimport { createWebhooksResource, type WebhooksResource } from './resources/webhooks'\nimport { createOrgsResource, type OrgsResource } from './resources/orgs'\nimport { createRoomsResource, type RoomsResource } from './resources/rooms'\nimport { createSpacesResource, type SpacesResource } from './resources/spaces'\nimport { createChatsResource, type ChatsResource } from './resources/chats'\nimport { createSpeechResource, type SpeechResource } from './resources/speech'\n\nexport interface PlatformClientOptions {\n // Full-org API key minted from the dashboard or bootstrap CLI. Start with\n // `sk_`. Never ship to a browser — use `client.callTokens.mint(...)` to\n // generate a narrow `ct_` token for client-side use instead.\n apiKey: string\n // Defaults to the hosted platform. Point at `http://localhost:8080` for\n // local dev or at a self-hosted deployment.\n baseUrl?: string\n // Passthrough tuning for the HTTP layer.\n timeoutMs?: number\n maxRetries?: number\n fetch?: HttpClientOptions['fetch']\n onRequest?: HttpClientOptions['onRequest']\n}\n\n// Single entry point. All resources are lazy-constructed in the constructor\n// so their refs don't incur any per-call allocation. Shape mirrors Stripe /\n// Twilio / Vapi's `resource.method()` convention for familiarity.\nexport class PlatformClient {\n readonly me: MeResource\n readonly agents: AgentsResource\n readonly calls: CallsResource\n readonly knowledgeBases: KnowledgeBasesResource\n readonly credits: CreditsResource\n readonly callTokens: CallTokensResource\n readonly webhooks: WebhooksResource\n readonly orgs: OrgsResource\n readonly rooms: RoomsResource\n readonly spaces: SpacesResource\n readonly speech: SpeechResource\n private readonly _http: HttpClient\n\n constructor(options: PlatformClientOptions) {\n if (!options.apiKey) {\n throw new Error('PlatformClient: `apiKey` is required')\n }\n const http = createHttpClient({\n apiKey: options.apiKey,\n baseUrl: options.baseUrl ?? 'https://api.example.com',\n timeoutMs: options.timeoutMs,\n maxRetries: options.maxRetries,\n fetch: options.fetch,\n onRequest: options.onRequest,\n })\n\n this.me = createMeResource(http)\n this.agents = createAgentsResource(http)\n this.calls = createCallsResource(http)\n this.knowledgeBases = createKnowledgeBasesResource(http)\n this.credits = createCreditsResource(http)\n this.callTokens = createCallTokensResource(http)\n this.webhooks = createWebhooksResource(http)\n this.orgs = createOrgsResource(http)\n this.rooms = createRoomsResource(http)\n this.spaces = createSpacesResource(http)\n this.speech = createSpeechResource(http)\n this._http = http\n }\n\n /**\n * Returns a per-token chat resource bound to the given `ct_…` call token.\n * The token must have been minted with `channel: 'text'`.\n *\n * Note: `chatsFor` is a factory method (not a fixed property) because the\n * underlying resource is scoped to a single call token, whereas this client\n * was constructed with an `sk_` admin key. Each end-user session needs its\n * own `chatsFor(token)` handle.\n */\n public chatsFor(callToken: string): ChatsResource {\n return createChatsResource(this._http, callToken)\n }\n}\n","import crypto from 'node:crypto'\n\n// Helper for consumers receiving webhooks: verify the `X-Platform-Signature-256`\n// header against the raw body + secret. Plain function (not tied to\n// PlatformClient) so Express/Koa/Next.js middleware can use it without\n// instantiating a client.\n//\n// import { verifyWebhookSignature } from '@craftedxp/sdk-node'\n// app.post('/webhooks/voice-agent', express.raw({ type: 'application/json' }), (req, res) => {\n// const sig = req.header('X-Platform-Signature-256') ?? ''\n// if (!verifyWebhookSignature(req.body, sig, process.env.VOICE_AGENT_WEBHOOK_SECRET!)) {\n// return res.status(401).send('invalid signature')\n// }\n// const event = JSON.parse(req.body.toString('utf8'))\n// // ...handle event\n// })\n//\n// The signature is `sha256=<hex HMAC-SHA256 of rawBody with secret>`.\n// Timing-safe compare to avoid microtiming side channels.\n\nexport const verifyWebhookSignature = (\n rawBody: Buffer | string,\n signatureHeader: string,\n secret: string,\n): boolean => {\n if (!signatureHeader || !secret) return false\n const [algo, provided] = signatureHeader.split('=')\n if (algo !== 'sha256' || !provided) return false\n\n const expected = crypto\n .createHmac('sha256', secret)\n .update(typeof rawBody === 'string' ? rawBody : rawBody)\n .digest('hex')\n\n // Buffers must match in length for timingSafeEqual.\n const a = Buffer.from(expected, 'hex')\n const b = Buffer.from(provided, 'hex')\n if (a.length !== b.length) return false\n return crypto.timingSafeEqual(a, b)\n}\n"],"mappings":"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","names":["path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftedxp/sdk-node",
3
- "version": "0.20.1",
3
+ "version": "0.22.0",
4
4
  "description": "Node.js / TypeScript SDK for the voice agent platform. Server-side API client — mint call tokens, manage agents, query calls, upload knowledge-base docs.",
5
5
  "author": "Crafted XP",
6
6
  "license": "MIT",