@dialgem/sdk 0.1.0-beta.0 → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +96 -16
- package/dist/index.cjs +246 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +581 -77
- package/dist/index.d.ts +581 -77
- package/dist/index.js +244 -53
- package/dist/index.js.map +1 -1
- package/examples/export-calls.ts +11 -14
- package/examples/webhook-express.ts +13 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -106,7 +106,7 @@ function isErrorBody(body) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// src/version.ts
|
|
109
|
-
var VERSION = "0.1.0-beta.
|
|
109
|
+
var VERSION = "0.1.0-beta.1";
|
|
110
110
|
|
|
111
111
|
// src/core/http.ts
|
|
112
112
|
var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "PATCH", "DELETE"]);
|
|
@@ -132,6 +132,16 @@ var HttpClient = class {
|
|
|
132
132
|
post(path2, body, options) {
|
|
133
133
|
return this.request("POST", path2, { body, options });
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* POST a `FormData` body — file uploads.
|
|
137
|
+
*
|
|
138
|
+
* The body is sent as-is and `Content-Type` is left unset, because only
|
|
139
|
+
* `fetch` knows the multipart boundary it is about to generate. Setting the
|
|
140
|
+
* header ourselves produces a request no server can parse.
|
|
141
|
+
*/
|
|
142
|
+
postForm(path2, form, options) {
|
|
143
|
+
return this.request("POST", path2, { body: form, options });
|
|
144
|
+
}
|
|
135
145
|
patch(path2, body, options) {
|
|
136
146
|
return this.request("PATCH", path2, { body, options });
|
|
137
147
|
}
|
|
@@ -140,10 +150,11 @@ var HttpClient = class {
|
|
|
140
150
|
}
|
|
141
151
|
async request(method, path2, { query, body, options = {} }) {
|
|
142
152
|
const url = this.buildURL(path2, query);
|
|
143
|
-
const
|
|
144
|
-
const
|
|
153
|
+
const isForm = isFormData(body);
|
|
154
|
+
const headers = this.buildHeaders(body !== void 0 && !isForm, options.headers);
|
|
155
|
+
const payload = body === void 0 ? void 0 : isForm ? body : JSON.stringify(body);
|
|
145
156
|
const timeout = options.timeout ?? this.#timeout;
|
|
146
|
-
const maxRetries = options.maxRetries ?? (RETRYABLE_METHODS.has(method) ? this.#maxRetries : 0);
|
|
157
|
+
const maxRetries = isForm ? 0 : options.maxRetries ?? (RETRYABLE_METHODS.has(method) ? this.#maxRetries : 0);
|
|
147
158
|
assertTimeout(timeout);
|
|
148
159
|
assertMaxRetries(maxRetries);
|
|
149
160
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -197,7 +208,7 @@ var HttpClient = class {
|
|
|
197
208
|
const params = new URLSearchParams();
|
|
198
209
|
for (const [key, value] of Object.entries(query)) {
|
|
199
210
|
if (value === void 0 || value === null) continue;
|
|
200
|
-
params.append(key, String(
|
|
211
|
+
for (const item of Array.isArray(value) ? value : [value]) params.append(key, String(item));
|
|
201
212
|
}
|
|
202
213
|
const qs = params.toString();
|
|
203
214
|
return qs ? `${url}?${qs}` : url;
|
|
@@ -220,6 +231,9 @@ var HttpClient = class {
|
|
|
220
231
|
return Object.fromEntries(merged.values());
|
|
221
232
|
}
|
|
222
233
|
};
|
|
234
|
+
function isFormData(value) {
|
|
235
|
+
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
236
|
+
}
|
|
223
237
|
function assertTimeout(value) {
|
|
224
238
|
if (!Number.isInteger(value) || value <= 0) {
|
|
225
239
|
throw new DialgemError(`\`timeout\` must be a positive integer of milliseconds, got ${String(value)}.`);
|
|
@@ -316,11 +330,14 @@ var Agents = class extends APIResource {
|
|
|
316
330
|
knowledge;
|
|
317
331
|
/** Remote MCP servers whose tools are exposed to the agent. */
|
|
318
332
|
mcpServers;
|
|
333
|
+
/** Frozen snapshots of the agent that a number or a call can pin. */
|
|
334
|
+
versions;
|
|
319
335
|
constructor(client) {
|
|
320
336
|
super(client);
|
|
321
337
|
this.functions = new AgentFunctions(client);
|
|
322
338
|
this.knowledge = new AgentKnowledge(client);
|
|
323
339
|
this.mcpServers = new AgentMCPServers(client);
|
|
340
|
+
this.versions = new AgentVersions(client);
|
|
324
341
|
}
|
|
325
342
|
/** Create an agent. Only `name` is required; everything else has a server default. */
|
|
326
343
|
create(body, options) {
|
|
@@ -341,6 +358,48 @@ var Agents = class extends APIResource {
|
|
|
341
358
|
return this._client.delete(path`/v1/agents/${agentId}`, options);
|
|
342
359
|
}
|
|
343
360
|
};
|
|
361
|
+
var AgentVersions = class extends APIResource {
|
|
362
|
+
/** Freeze the agent as it is now. Returns the new version, snapshot included. */
|
|
363
|
+
publish(agentId, body = {}, options) {
|
|
364
|
+
return this._client.post(path`/v1/agents/${agentId}/versions`, body, options);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* One page of published versions, newest first, without their snapshots.
|
|
368
|
+
*
|
|
369
|
+
* Returns the array. When you need to page — an agent published on every
|
|
370
|
+
* deploy accumulates versions, and nothing prunes them — read `has_more` from
|
|
371
|
+
* {@link listPage} instead and pass the last `version` as `before_version`.
|
|
372
|
+
*/
|
|
373
|
+
async list(agentId, params, options) {
|
|
374
|
+
return (await this.listPage(agentId, params, options)).versions;
|
|
375
|
+
}
|
|
376
|
+
/** {@link list} with the `has_more` flag, for paging. */
|
|
377
|
+
listPage(agentId, params, options) {
|
|
378
|
+
return this._client.get(
|
|
379
|
+
path`/v1/agents/${agentId}/versions`,
|
|
380
|
+
// Spread into a plain query record: an `interface` has no index
|
|
381
|
+
// signature, so it does not structurally satisfy `Query`.
|
|
382
|
+
{ ...params },
|
|
383
|
+
options
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
/** One version with its full snapshot. Version 0 is the draft: use `agents.retrieve`. */
|
|
387
|
+
retrieve(agentId, version, options) {
|
|
388
|
+
return this._client.get(
|
|
389
|
+
path`/v1/agents/${agentId}/versions/${versionSegment(version)}`,
|
|
390
|
+
void 0,
|
|
391
|
+
options
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
function versionSegment(version) {
|
|
396
|
+
if (!Number.isInteger(version) || version < 1) {
|
|
397
|
+
throw new DialgemError(
|
|
398
|
+
`Expected a published version number (a positive integer), got ${JSON.stringify(version)}. Version 0 is the draft \u2014 read it with agents.retrieve().`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
return String(version);
|
|
402
|
+
}
|
|
344
403
|
var AgentFunctions = class extends APIResource {
|
|
345
404
|
list(agentId, options) {
|
|
346
405
|
return this._client.get(path`/v1/agents/${agentId}/functions`, void 0, options);
|
|
@@ -393,35 +452,33 @@ var AgentMCPServers = class extends APIResource {
|
|
|
393
452
|
// src/core/pagination.ts
|
|
394
453
|
var Page = class _Page {
|
|
395
454
|
data;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
page_size;
|
|
455
|
+
has_more;
|
|
456
|
+
pagination_key;
|
|
399
457
|
#fetchPage;
|
|
400
458
|
constructor(data, fetchPage) {
|
|
401
459
|
this.data = data.data;
|
|
402
|
-
this.
|
|
403
|
-
this.
|
|
404
|
-
this.page_size = data.page_size;
|
|
460
|
+
this.has_more = data.has_more;
|
|
461
|
+
this.pagination_key = data.pagination_key;
|
|
405
462
|
this.#fetchPage = fetchPage;
|
|
406
463
|
}
|
|
407
464
|
hasNextPage() {
|
|
408
|
-
return this.
|
|
465
|
+
return this.has_more && this.pagination_key !== null;
|
|
409
466
|
}
|
|
410
467
|
async getNextPage() {
|
|
411
468
|
if (!this.hasNextPage()) throw new DialgemError("No next page; check hasNextPage() first.");
|
|
412
|
-
return new _Page(await this.#fetchPage(this.
|
|
469
|
+
return new _Page(await this.#fetchPage(this.pagination_key), this.#fetchPage);
|
|
413
470
|
}
|
|
414
471
|
};
|
|
415
472
|
var PagePromise = class {
|
|
416
473
|
#fetchPage;
|
|
417
|
-
#
|
|
474
|
+
#startKey;
|
|
418
475
|
#first;
|
|
419
|
-
constructor(fetchPage,
|
|
476
|
+
constructor(fetchPage, startKey = null) {
|
|
420
477
|
this.#fetchPage = fetchPage;
|
|
421
|
-
this.#
|
|
478
|
+
this.#startKey = startKey;
|
|
422
479
|
}
|
|
423
480
|
#firstPage() {
|
|
424
|
-
this.#first ??= this.#fetchPage(this.#
|
|
481
|
+
this.#first ??= this.#fetchPage(this.#startKey).then((data) => new Page(data, this.#fetchPage));
|
|
425
482
|
return this.#first;
|
|
426
483
|
}
|
|
427
484
|
then(onfulfilled, onrejected) {
|
|
@@ -446,24 +503,50 @@ var PagePromise = class {
|
|
|
446
503
|
// src/resources/calls.ts
|
|
447
504
|
var Calls = class extends APIResource {
|
|
448
505
|
/**
|
|
449
|
-
* Calls, newest first. Await for one page, or iterate for every call:
|
|
506
|
+
* Calls, newest first by default. Await for one page, or iterate for every call:
|
|
450
507
|
*
|
|
451
|
-
* const page = await dialgem.calls.list({
|
|
452
|
-
* for await (const call of dialgem.calls.list({ status: "completed" })) {
|
|
453
|
-
* if (call.started_at < cutoff) break // newest first: everything after is older
|
|
454
|
-
* }
|
|
508
|
+
* const page = await dialgem.calls.list({ limit: 50 })
|
|
509
|
+
* for await (const call of dialgem.calls.list({ status: "completed", start_after: since })) { ... }
|
|
455
510
|
*/
|
|
456
511
|
list(params = {}, options) {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
512
|
+
const query = toQuery(params);
|
|
513
|
+
const fetchPage = async (paginationKey) => {
|
|
514
|
+
const res = await this._client.get(
|
|
515
|
+
"/v1/calls",
|
|
516
|
+
{ ...query, pagination_key: paginationKey ?? void 0 },
|
|
517
|
+
options
|
|
518
|
+
);
|
|
519
|
+
return { data: res.calls, has_more: res.has_more, pagination_key: res.pagination_key };
|
|
460
520
|
};
|
|
461
|
-
return new PagePromise(fetchPage
|
|
521
|
+
return new PagePromise(fetchPage);
|
|
462
522
|
}
|
|
463
|
-
/** One call, including its transcript, analysis and recording URL. */
|
|
523
|
+
/** One call, including its transcript, analysis, latency and recording URL. */
|
|
464
524
|
retrieve(callId, options) {
|
|
465
525
|
return this._client.get(path`/v1/calls/${callId}`, void 0, options);
|
|
466
526
|
}
|
|
527
|
+
/** Replace the call's `metadata`. */
|
|
528
|
+
update(callId, body, options) {
|
|
529
|
+
return this._client.patch(path`/v1/calls/${callId}`, body, options);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Permanently delete a finished call: its record, transcript, analysis and
|
|
533
|
+
* recording. A call still in progress is refused with `ConflictError`; stop it first.
|
|
534
|
+
*/
|
|
535
|
+
delete(callId, options) {
|
|
536
|
+
return this._client.delete(path`/v1/calls/${callId}`, options);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* End an ongoing call now, as if the agent had hung up. The call becomes
|
|
540
|
+
* `completed` with `disconnection_reason: "manual_stopped"` a moment later, and
|
|
541
|
+
* the usual `call_ended` webhook follows.
|
|
542
|
+
*
|
|
543
|
+
* `ConflictError` with `errCode` `call_not_ongoing` if it already ended, or
|
|
544
|
+
* `call_not_connected` if no agent is on the call yet (e.g. still ringing).
|
|
545
|
+
* Not retried by default.
|
|
546
|
+
*/
|
|
547
|
+
stop(callId, options) {
|
|
548
|
+
return this._client.post(path`/v1/calls/${callId}/stop`, void 0, options);
|
|
549
|
+
}
|
|
467
550
|
/**
|
|
468
551
|
* Dial `to` from one of your organization's numbers, with `agent_id` (or the
|
|
469
552
|
* number's outbound agent) on the line.
|
|
@@ -483,13 +566,70 @@ var Calls = class extends APIResource {
|
|
|
483
566
|
return this._client.post("/v1/calls/register", body, options);
|
|
484
567
|
}
|
|
485
568
|
};
|
|
569
|
+
function toQuery(params) {
|
|
570
|
+
const { metadata, ...rest } = params;
|
|
571
|
+
const query = { ...rest };
|
|
572
|
+
query["limit"] = params.limit ?? 10;
|
|
573
|
+
for (const [key, value] of Object.entries(metadata ?? {})) {
|
|
574
|
+
query[`metadata.${key}`] = String(value);
|
|
575
|
+
}
|
|
576
|
+
return query;
|
|
577
|
+
}
|
|
486
578
|
|
|
487
579
|
// src/resources/catalog.ts
|
|
488
580
|
var Voices = class extends APIResource {
|
|
489
|
-
/**
|
|
581
|
+
/**
|
|
582
|
+
* Voices available to agents: the shared catalog plus the ones your
|
|
583
|
+
* organization cloned, yours first. Put a voice's `provider_voice_id` in an
|
|
584
|
+
* agent's `voice_id`.
|
|
585
|
+
*/
|
|
490
586
|
list(params = {}, options) {
|
|
491
587
|
return this._client.get("/v1/voices", params, options);
|
|
492
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Clone a voice from audio you upload. The new voice is private to your
|
|
591
|
+
* organization and never appears in another one's catalog.
|
|
592
|
+
*
|
|
593
|
+
* Consent is required: you are asserting that you hold the rights to the
|
|
594
|
+
* audio and have the speaker's consent, and that assertion is recorded
|
|
595
|
+
* against your API identity.
|
|
596
|
+
*
|
|
597
|
+
* The upload is never retried, whatever `maxRetries` says — a retried
|
|
598
|
+
* multipart body can send truncated audio, and a clone built from half the
|
|
599
|
+
* recording succeeds and sounds wrong.
|
|
600
|
+
*/
|
|
601
|
+
clone(params, options) {
|
|
602
|
+
const { name, files, consent_confirmed, engine, description, language, filenames } = params;
|
|
603
|
+
if (!consent_confirmed) {
|
|
604
|
+
throw new DialgemError(
|
|
605
|
+
"consent_confirmed must be true: you are asserting that you hold the rights to this audio and have the speaker's consent to clone their voice."
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
if (!files?.length) {
|
|
609
|
+
throw new DialgemError("At least one audio file is required to clone a voice.");
|
|
610
|
+
}
|
|
611
|
+
const form = new FormData();
|
|
612
|
+
form.append("name", name);
|
|
613
|
+
form.append("consent_confirmed", "true");
|
|
614
|
+
if (engine) form.append("engine", engine);
|
|
615
|
+
if (description) form.append("description", description);
|
|
616
|
+
if (language) form.append("language", language);
|
|
617
|
+
files.forEach((file, i) => {
|
|
618
|
+
const fallback = file.name || `clip-${i + 1}.wav`;
|
|
619
|
+
form.append("files", file, filenames?.[i] ?? fallback);
|
|
620
|
+
});
|
|
621
|
+
return this._client.postForm("/v1/voices/clone", form, options);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Delete a voice your organization cloned, at the provider and here.
|
|
625
|
+
*
|
|
626
|
+
* Only your own cloned voices: the shared catalog is not deletable, and a
|
|
627
|
+
* voice an agent still uses is refused with a 409 rather than leaving that
|
|
628
|
+
* agent without one.
|
|
629
|
+
*/
|
|
630
|
+
delete(voiceId, options) {
|
|
631
|
+
return this._client.delete(path`/v1/voices/${voiceId}`, options);
|
|
632
|
+
}
|
|
493
633
|
};
|
|
494
634
|
var FlowTemplates = class extends APIResource {
|
|
495
635
|
/** Built-in conversation-flow templates (summaries, without `flow_config`). */
|
|
@@ -520,6 +660,20 @@ var PhoneNumbers = class extends APIResource {
|
|
|
520
660
|
list(options) {
|
|
521
661
|
return this._client.get("/v1/phone-numbers", void 0, options);
|
|
522
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* One number by its E.164 value, e.g. `+14155550123`.
|
|
665
|
+
*
|
|
666
|
+
* Keyed on the number rather than on `id`, because the number is what you
|
|
667
|
+
* have when you are asking whether it is registered at all. Throws
|
|
668
|
+
* `NotFoundError` when it is not.
|
|
669
|
+
*/
|
|
670
|
+
retrieve(phoneNumber, options) {
|
|
671
|
+
return this._client.get(
|
|
672
|
+
path`/v1/phone-numbers/by-number/${phoneNumber}`,
|
|
673
|
+
void 0,
|
|
674
|
+
options
|
|
675
|
+
);
|
|
676
|
+
}
|
|
523
677
|
/**
|
|
524
678
|
* Partial update.
|
|
525
679
|
*
|
|
@@ -544,52 +698,84 @@ var PhoneNumbers = class extends APIResource {
|
|
|
544
698
|
// src/webhooks.ts
|
|
545
699
|
var WEBHOOK_SIGNATURE_HEADER = "x-dialgem-signature";
|
|
546
700
|
var WEBHOOK_EVENT_HEADER = "x-dialgem-event";
|
|
701
|
+
var WEBHOOK_DELIVERY_HEADER = "x-dialgem-delivery";
|
|
702
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
547
703
|
var encoder = new TextEncoder();
|
|
548
704
|
var decoder = new TextDecoder();
|
|
705
|
+
var SIGNATURE_RE = /^v=(\d{10,16}),d=([0-9a-f]{64})$/;
|
|
549
706
|
var Webhooks = class {
|
|
550
707
|
/**
|
|
551
|
-
* Returns whether `signature` is
|
|
552
|
-
* Constant-time. Returns `false` for a missing
|
|
553
|
-
* if `secret` is empty, which is a configuration
|
|
708
|
+
* Returns whether `signature` is a current, valid signature of `payload` under
|
|
709
|
+
* `secret`. Constant-time. Returns `false` for a missing, malformed, tampered or
|
|
710
|
+
* stale signature; throws only if `secret` is empty, which is a configuration
|
|
711
|
+
* error rather than a bad request.
|
|
554
712
|
*/
|
|
555
|
-
async verify(payload, signature, secret) {
|
|
713
|
+
async verify(payload, signature, secret, options = {}) {
|
|
556
714
|
if (!secret) {
|
|
557
715
|
throw new DialgemError(
|
|
558
716
|
"Webhook secret is empty. Set the agent's `webhook_signing_secret` and pass the same value here."
|
|
559
717
|
);
|
|
560
718
|
}
|
|
561
|
-
const
|
|
562
|
-
if (!
|
|
563
|
-
const
|
|
719
|
+
const match = SIGNATURE_RE.exec(firstHeader(signature)?.trim().toLowerCase() ?? "");
|
|
720
|
+
if (!match) return false;
|
|
721
|
+
const [, timestamp, received] = match;
|
|
722
|
+
const now = options.now ?? Date.now();
|
|
723
|
+
if (Math.abs(now - Number(timestamp)) > (options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS)) {
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
const body = toBytes(payload);
|
|
727
|
+
const message = new Uint8Array(body.length + timestamp.length);
|
|
728
|
+
message.set(body, 0);
|
|
729
|
+
message.set(encoder.encode(timestamp), body.length);
|
|
730
|
+
const expected = await hmacSha256Hex(secret, message);
|
|
564
731
|
return timingSafeEqual(expected, received);
|
|
565
732
|
}
|
|
566
733
|
/**
|
|
567
|
-
* Verifies
|
|
734
|
+
* Verifies a webhook's signature, then parses its body.
|
|
568
735
|
*
|
|
569
|
-
* @throws WebhookVerificationError if the signature is missing or
|
|
736
|
+
* @throws WebhookVerificationError if the signature is missing, invalid or stale.
|
|
570
737
|
*/
|
|
571
|
-
async constructEvent(payload, signature, secret) {
|
|
738
|
+
async constructEvent(payload, signature, secret, options = {}) {
|
|
739
|
+
const parsed = await this.#verifiedJson(payload, signature, secret, options);
|
|
740
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.event !== "string") {
|
|
741
|
+
throw new WebhookVerificationError("Webhook body has no `event` field.");
|
|
742
|
+
}
|
|
743
|
+
return parsed;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Verifies a request Dialgem made to one of your custom functions, then parses
|
|
747
|
+
* its body. For functions with `args_at_root`, the body is the arguments object
|
|
748
|
+
* alone; use `verify` and parse it yourself.
|
|
749
|
+
*
|
|
750
|
+
* @throws WebhookVerificationError if the signature is missing, invalid or stale.
|
|
751
|
+
*/
|
|
752
|
+
async constructFunctionCall(payload, signature, secret, options = {}) {
|
|
753
|
+
const parsed = await this.#verifiedJson(payload, signature, secret, options);
|
|
754
|
+
const body = parsed;
|
|
755
|
+
if (!body || typeof body.name !== "string" || typeof body.call !== "object" || body.call === null) {
|
|
756
|
+
throw new WebhookVerificationError(
|
|
757
|
+
"Function request body has no `name` and `call`. Does this function have args_at_root set?"
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
return body;
|
|
761
|
+
}
|
|
762
|
+
async #verifiedJson(payload, signature, secret, options) {
|
|
572
763
|
if (!firstHeader(signature)) {
|
|
573
764
|
throw new WebhookVerificationError(
|
|
574
765
|
`Missing ${WEBHOOK_SIGNATURE_HEADER} header. Is a webhook_signing_secret set on the agent?`
|
|
575
766
|
);
|
|
576
767
|
}
|
|
577
|
-
if (!await this.verify(payload, signature, secret)) {
|
|
768
|
+
if (!await this.verify(payload, signature, secret, options)) {
|
|
578
769
|
throw new WebhookVerificationError(
|
|
579
|
-
"
|
|
770
|
+
"Signature is invalid or older than the allowed tolerance. Check the secret, the server clock, and that the body was not re-serialized before verification."
|
|
580
771
|
);
|
|
581
772
|
}
|
|
582
773
|
const text = typeof payload === "string" ? payload : decoder.decode(toBytes(payload));
|
|
583
|
-
let parsed;
|
|
584
774
|
try {
|
|
585
|
-
|
|
775
|
+
return JSON.parse(text);
|
|
586
776
|
} catch {
|
|
587
|
-
throw new WebhookVerificationError("
|
|
777
|
+
throw new WebhookVerificationError("Body is signed but is not valid JSON.");
|
|
588
778
|
}
|
|
589
|
-
if (typeof parsed !== "object" || parsed === null || typeof parsed.event !== "string") {
|
|
590
|
-
throw new WebhookVerificationError("Webhook body has no `event` field.");
|
|
591
|
-
}
|
|
592
|
-
return parsed;
|
|
593
779
|
}
|
|
594
780
|
};
|
|
595
781
|
var webhooks = new Webhooks();
|
|
@@ -619,11 +805,11 @@ function timingSafeEqual(a, b) {
|
|
|
619
805
|
}
|
|
620
806
|
|
|
621
807
|
// src/client.ts
|
|
622
|
-
var
|
|
808
|
+
var BASE_URL = "https://api.dialgem.com";
|
|
623
809
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
624
810
|
var DEFAULT_MAX_RETRIES = 2;
|
|
625
811
|
var Dialgem = class {
|
|
626
|
-
static
|
|
812
|
+
static BASE_URL = BASE_URL;
|
|
627
813
|
/** Webhook verification without a client instance: `Dialgem.webhooks.constructEvent(...)`. */
|
|
628
814
|
static webhooks = webhooks;
|
|
629
815
|
agents;
|
|
@@ -633,8 +819,14 @@ var Dialgem = class {
|
|
|
633
819
|
flowTemplates;
|
|
634
820
|
promptTemplates;
|
|
635
821
|
webhooks = webhooks;
|
|
636
|
-
|
|
822
|
+
/** Always `https://api.dialgem.com`. */
|
|
823
|
+
baseURL = BASE_URL;
|
|
637
824
|
constructor(options = {}) {
|
|
825
|
+
if ("baseURL" in options) {
|
|
826
|
+
throw new DialgemError(
|
|
827
|
+
"`baseURL` is not an option: the SDK always talks to https://api.dialgem.com. Remove it."
|
|
828
|
+
);
|
|
829
|
+
}
|
|
638
830
|
if (isBrowser() && !options.dangerouslyAllowBrowser) {
|
|
639
831
|
throw new DialgemError(
|
|
640
832
|
"The Dialgem SDK is for server-side use: running it in a browser exposes your API key to every visitor. Call Dialgem from your backend instead, or pass `dangerouslyAllowBrowser: true` if you understand the risk."
|
|
@@ -651,10 +843,9 @@ var Dialgem = class {
|
|
|
651
843
|
assertTimeout(timeout);
|
|
652
844
|
assertMaxRetries(maxRetries);
|
|
653
845
|
const fetchImpl = options.fetch ?? defaultFetch();
|
|
654
|
-
this.baseURL = (options.baseURL ?? readEnv("DIALGEM_BASE_URL") ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
655
846
|
const http = new HttpClient({
|
|
656
847
|
apiKey,
|
|
657
|
-
baseURL:
|
|
848
|
+
baseURL: BASE_URL,
|
|
658
849
|
timeout,
|
|
659
850
|
maxRetries,
|
|
660
851
|
fetch: fetchImpl,
|
|
@@ -690,9 +881,10 @@ exports.APIConnectionTimeoutError = APIConnectionTimeoutError;
|
|
|
690
881
|
exports.APIError = APIError;
|
|
691
882
|
exports.APIUserAbortError = APIUserAbortError;
|
|
692
883
|
exports.AuthenticationError = AuthenticationError;
|
|
884
|
+
exports.BASE_URL = BASE_URL;
|
|
693
885
|
exports.BadRequestError = BadRequestError;
|
|
694
886
|
exports.ConflictError = ConflictError;
|
|
695
|
-
exports.
|
|
887
|
+
exports.DEFAULT_SIGNATURE_TOLERANCE_MS = DEFAULT_SIGNATURE_TOLERANCE_MS;
|
|
696
888
|
exports.Dialgem = Dialgem;
|
|
697
889
|
exports.DialgemError = DialgemError;
|
|
698
890
|
exports.InternalServerError = InternalServerError;
|
|
@@ -703,6 +895,7 @@ exports.PermissionDeniedError = PermissionDeniedError;
|
|
|
703
895
|
exports.RateLimitError = RateLimitError;
|
|
704
896
|
exports.UnprocessableEntityError = UnprocessableEntityError;
|
|
705
897
|
exports.VERSION = VERSION;
|
|
898
|
+
exports.WEBHOOK_DELIVERY_HEADER = WEBHOOK_DELIVERY_HEADER;
|
|
706
899
|
exports.WEBHOOK_EVENT_HEADER = WEBHOOK_EVENT_HEADER;
|
|
707
900
|
exports.WEBHOOK_SIGNATURE_HEADER = WEBHOOK_SIGNATURE_HEADER;
|
|
708
901
|
exports.WebhookVerificationError = WebhookVerificationError;
|