@chronary/sdk 0.1.3

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/dist/index.cjs ADDED
@@ -0,0 +1,1213 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ APIPromise: () => APIPromise,
24
+ AuthenticationError: () => AuthenticationError,
25
+ Chronary: () => Chronary,
26
+ ChronaryError: () => ChronaryError,
27
+ ConnectionError: () => ConnectionError,
28
+ CoreClient: () => CoreClient,
29
+ NotFoundError: () => NotFoundError,
30
+ PageIterator: () => PageIterator,
31
+ QuotaExceededError: () => QuotaExceededError,
32
+ RateLimitError: () => RateLimitError,
33
+ TimeoutError: () => TimeoutError,
34
+ VERSION: () => VERSION,
35
+ ValidationError: () => ValidationError,
36
+ constructEvent: () => constructEvent,
37
+ default: () => index_default,
38
+ isAgentSignUpNewOrg: () => isAgentSignUpNewOrg,
39
+ verifySignature: () => verifySignature
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/version.ts
44
+ var VERSION = "0.1.3";
45
+
46
+ // src/error.ts
47
+ var ChronaryError = class extends Error {
48
+ status;
49
+ requestId;
50
+ headers;
51
+ errorType;
52
+ constructor(message, status, requestId, headers, errorType) {
53
+ super(message);
54
+ this.name = "ChronaryError";
55
+ this.status = status;
56
+ this.requestId = requestId;
57
+ this.headers = headers;
58
+ this.errorType = errorType;
59
+ }
60
+ };
61
+ var AuthenticationError = class extends ChronaryError {
62
+ constructor(message, requestId, headers) {
63
+ super(message, 401, requestId, headers, "authentication_error");
64
+ this.name = "AuthenticationError";
65
+ }
66
+ };
67
+ var RateLimitError = class extends ChronaryError {
68
+ retryAfter;
69
+ constructor(message, requestId, headers) {
70
+ super(message, 429, requestId, headers, "rate_limit_error");
71
+ this.name = "RateLimitError";
72
+ if (headers) {
73
+ const ra = headers.get("retry-after");
74
+ if (ra) this.retryAfter = parseInt(ra, 10);
75
+ }
76
+ }
77
+ };
78
+ var NotFoundError = class extends ChronaryError {
79
+ constructor(message, requestId, headers) {
80
+ super(message, 404, requestId, headers, "not_found");
81
+ this.name = "NotFoundError";
82
+ }
83
+ };
84
+ var ValidationError = class extends ChronaryError {
85
+ constructor(message, status, requestId, headers) {
86
+ super(message, status, requestId, headers, "validation_error");
87
+ this.name = "ValidationError";
88
+ }
89
+ };
90
+ var QuotaExceededError = class extends ChronaryError {
91
+ constructor(message, requestId, headers) {
92
+ super(message, 402, requestId, headers, "quota_exceeded");
93
+ this.name = "QuotaExceededError";
94
+ }
95
+ };
96
+ var TimeoutError = class extends ChronaryError {
97
+ constructor(message) {
98
+ super(message, 0);
99
+ this.name = "TimeoutError";
100
+ }
101
+ };
102
+ var ConnectionError = class extends ChronaryError {
103
+ constructor(message) {
104
+ super(message, 0);
105
+ this.name = "ConnectionError";
106
+ }
107
+ };
108
+
109
+ // src/api-promise.ts
110
+ var APIPromise = class {
111
+ innerPromise;
112
+ constructor(responsePromise) {
113
+ this.innerPromise = responsePromise;
114
+ }
115
+ /**
116
+ * Returns both the parsed data and the raw HTTP response.
117
+ */
118
+ async withResponse() {
119
+ const { data, rawResponse } = await this.innerPromise;
120
+ return { data, response: rawResponse };
121
+ }
122
+ /**
123
+ * Implements the thenable interface. When awaited, resolves to just the parsed data `T`.
124
+ */
125
+ then(onfulfilled, onrejected) {
126
+ return this.innerPromise.then(
127
+ (result) => onfulfilled ? onfulfilled(result.data) : result.data,
128
+ onrejected
129
+ );
130
+ }
131
+ /**
132
+ * Attaches a rejection handler.
133
+ */
134
+ catch(onrejected) {
135
+ return this.then(void 0, onrejected);
136
+ }
137
+ /**
138
+ * Attaches a handler that runs on both fulfillment and rejection.
139
+ */
140
+ finally(onfinally) {
141
+ return this.then(
142
+ (value) => {
143
+ onfinally?.();
144
+ return value;
145
+ },
146
+ (reason) => {
147
+ onfinally?.();
148
+ throw reason;
149
+ }
150
+ );
151
+ }
152
+ };
153
+
154
+ // src/client.ts
155
+ var DEFAULT_BASE_URL = "https://api.chronary.ai";
156
+ var DEFAULT_TIMEOUT = 3e4;
157
+ var DEFAULT_MAX_RETRIES = 2;
158
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
159
+ var MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
160
+ var MAX_RETRY_DELAY = 6e4;
161
+ function getEnvVar(name) {
162
+ try {
163
+ if (typeof process !== "undefined" && process.env) {
164
+ return process.env[name];
165
+ }
166
+ } catch {
167
+ }
168
+ return void 0;
169
+ }
170
+ function resolveLogLevel(config) {
171
+ if (config.logLevel) return config.logLevel;
172
+ const envLog = getEnvVar("CHRONARY_LOG");
173
+ if (envLog && ["debug", "info", "warn", "error", "off"].includes(envLog)) {
174
+ return envLog;
175
+ }
176
+ return "off";
177
+ }
178
+ var LOG_PRIORITY = {
179
+ debug: 0,
180
+ info: 1,
181
+ warn: 2,
182
+ error: 3,
183
+ off: 4
184
+ };
185
+ var CoreClient = class {
186
+ baseUrl;
187
+ apiKey;
188
+ timeout;
189
+ maxRetries;
190
+ logLevel;
191
+ fetchFn;
192
+ userAgent;
193
+ extraHeaders;
194
+ constructor(config = {}) {
195
+ this.apiKey = config.apiKey ?? getEnvVar("CHRONARY_API_KEY") ?? "";
196
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
197
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
198
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
199
+ this.logLevel = resolveLogLevel(config);
200
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
201
+ this.extraHeaders = config.extraHeaders ?? {};
202
+ let ua = `chronary-ts/${VERSION}`;
203
+ if (config.appInfo) {
204
+ ua += ` ${config.appInfo.name}`;
205
+ if (config.appInfo.version) ua += `/${config.appInfo.version}`;
206
+ }
207
+ try {
208
+ if (typeof process !== "undefined" && process.version) {
209
+ ua += ` node/${process.version.replace("v", "")}`;
210
+ }
211
+ } catch {
212
+ }
213
+ this.userAgent = ua;
214
+ }
215
+ log(level, message) {
216
+ if (level === "off" || LOG_PRIORITY[level] < LOG_PRIORITY[this.logLevel]) return;
217
+ const prefix = `[chronary:${level}]`;
218
+ switch (level) {
219
+ case "debug":
220
+ console.debug(prefix, message);
221
+ break;
222
+ case "info":
223
+ console.info(prefix, message);
224
+ break;
225
+ case "warn":
226
+ console.warn(prefix, message);
227
+ break;
228
+ case "error":
229
+ console.error(prefix, message);
230
+ break;
231
+ }
232
+ }
233
+ request(method, path, body, query, options) {
234
+ return new APIPromise(this._request(method, path, body, query, options));
235
+ }
236
+ async _request(method, path, body, query, options) {
237
+ const url = this.buildUrl(path, query);
238
+ const headers = {
239
+ "User-Agent": this.userAgent,
240
+ "X-Chronary-SDK-Version": VERSION,
241
+ ...this.extraHeaders
242
+ };
243
+ if (this.apiKey) {
244
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
245
+ }
246
+ if (body !== void 0) {
247
+ headers["Content-Type"] = "application/json";
248
+ }
249
+ if (MUTATING_METHODS.has(method)) {
250
+ headers["Idempotency-Key"] = options?.idempotencyKey ?? crypto.randomUUID();
251
+ }
252
+ const requestTimeout = options?.timeout ?? this.timeout;
253
+ let lastError;
254
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
255
+ if (attempt > 0) {
256
+ const delay = this.retryDelay(attempt, lastError);
257
+ this.log("info", `Retry #${attempt} after ${delay}ms`);
258
+ await sleep(delay);
259
+ }
260
+ if (options?.signal?.aborted) throw new ChronaryError("Request aborted", 0);
261
+ const controller = new AbortController();
262
+ const abortFromCaller = () => controller.abort(options?.signal?.reason);
263
+ options?.signal?.addEventListener("abort", abortFromCaller, { once: true });
264
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
265
+ try {
266
+ this.log("debug", `${method} ${path}`);
267
+ const startTime = Date.now();
268
+ const response = await this.fetchFn(url, {
269
+ method,
270
+ headers,
271
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
272
+ signal: controller.signal
273
+ });
274
+ clearTimeout(timeoutId);
275
+ options?.signal?.removeEventListener("abort", abortFromCaller);
276
+ const elapsed = Date.now() - startTime;
277
+ const requestId = response.headers.get("x-request-id") ?? void 0;
278
+ this.log("debug", `${method} ${path} \u2192 ${response.status} in ${elapsed}ms${requestId ? ` (${requestId})` : ""}`);
279
+ if (response.ok) {
280
+ const rawResponse = {
281
+ status: response.status,
282
+ headers: response.headers,
283
+ url: response.url,
284
+ quota: parseQuotaSnapshot(response.headers)
285
+ };
286
+ if (response.status === 204) {
287
+ return { data: void 0, rawResponse };
288
+ }
289
+ const data = await response.json();
290
+ return { data, rawResponse };
291
+ }
292
+ if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
293
+ lastError = await this.buildError(response, requestId);
294
+ continue;
295
+ }
296
+ throw await this.buildError(response, requestId);
297
+ } catch (err) {
298
+ clearTimeout(timeoutId);
299
+ options?.signal?.removeEventListener("abort", abortFromCaller);
300
+ if (err instanceof ChronaryError) throw err;
301
+ if (err instanceof DOMException && err.name === "AbortError") {
302
+ if (options?.signal?.aborted) throw new ChronaryError("Request aborted", 0);
303
+ throw new TimeoutError(`Request timed out after ${requestTimeout}ms`);
304
+ }
305
+ if (attempt < this.maxRetries) {
306
+ lastError = err;
307
+ continue;
308
+ }
309
+ throw new ConnectionError(`Connection failed: ${err.message}`);
310
+ }
311
+ }
312
+ throw lastError ?? new ConnectionError("Request failed after retries");
313
+ }
314
+ buildUrl(path, query) {
315
+ const base = `${this.baseUrl}${path}`;
316
+ if (!query) return base;
317
+ const params = new URLSearchParams();
318
+ for (const [key, value] of Object.entries(query)) {
319
+ if (value !== void 0) params.set(key, String(value));
320
+ }
321
+ const qs = params.toString();
322
+ return qs ? `${base}?${qs}` : base;
323
+ }
324
+ async buildError(response, requestId) {
325
+ let errorBody;
326
+ try {
327
+ errorBody = await response.json();
328
+ } catch {
329
+ }
330
+ const message = errorBody?.error?.message ?? `HTTP ${response.status}`;
331
+ const rid = requestId ?? errorBody?.error?.request_id;
332
+ const errorType = errorBody?.error?.type;
333
+ switch (response.status) {
334
+ case 400:
335
+ if (errorType === "validation_error") {
336
+ return new ValidationError(message, response.status, rid, response.headers);
337
+ }
338
+ return new ChronaryError(message, response.status, rid, response.headers, errorType);
339
+ case 401:
340
+ return new AuthenticationError(message, rid, response.headers);
341
+ case 402:
342
+ return new QuotaExceededError(message, rid, response.headers);
343
+ case 404:
344
+ return new NotFoundError(message, rid, response.headers);
345
+ case 422:
346
+ return new ValidationError(message, response.status, rid, response.headers);
347
+ case 429: {
348
+ if (errorType === "quota_exceeded") {
349
+ return new QuotaExceededError(message, rid, response.headers);
350
+ }
351
+ return new RateLimitError(message, rid, response.headers);
352
+ }
353
+ default:
354
+ return new ChronaryError(message, response.status, rid, response.headers, errorType);
355
+ }
356
+ }
357
+ retryDelay(attempt, lastError) {
358
+ if (lastError instanceof RateLimitError && lastError.retryAfter) {
359
+ return Math.min(lastError.retryAfter * 1e3, MAX_RETRY_DELAY);
360
+ }
361
+ const base = 500;
362
+ const exponential = base * Math.pow(2, attempt - 1);
363
+ const jitter = Math.random() * base;
364
+ return Math.min(exponential + jitter, MAX_RETRY_DELAY);
365
+ }
366
+ };
367
+ function sleep(ms) {
368
+ return new Promise((resolve) => setTimeout(resolve, ms));
369
+ }
370
+ function parseQuotaSnapshot(headers) {
371
+ const rateLimit = headers.get("ratelimit");
372
+ const policy = headers.get("ratelimit-policy");
373
+ if (!rateLimit || !policy) return void 0;
374
+ const remaining = parseStructuredParam(rateLimit, "r");
375
+ const resetSeconds = parseStructuredParam(rateLimit, "t");
376
+ const limit = parseStructuredParam(policy, "q");
377
+ if (remaining === void 0 || resetSeconds === void 0 || limit === void 0) {
378
+ return void 0;
379
+ }
380
+ return {
381
+ limit,
382
+ remaining,
383
+ resetAt: new Date(Date.now() + resetSeconds * 1e3)
384
+ };
385
+ }
386
+ function parseStructuredParam(header, key) {
387
+ const re = new RegExp(`(?:^|;)\\s*${key}=(-?\\d+)(?:;|$)`);
388
+ const match = re.exec(header);
389
+ if (!match) return void 0;
390
+ const n = Number(match[1]);
391
+ return Number.isFinite(n) ? n : void 0;
392
+ }
393
+
394
+ // src/pagination.ts
395
+ var PageIterator = class {
396
+ fetchPage;
397
+ defaultLimit;
398
+ initialOffset;
399
+ constructor(fetchPage, defaultLimit = 50, initialOffset = 0) {
400
+ this.fetchPage = fetchPage;
401
+ this.defaultLimit = defaultLimit;
402
+ this.initialOffset = initialOffset;
403
+ }
404
+ async getPage(offset = this.initialOffset, limit) {
405
+ const l = limit ?? this.defaultLimit;
406
+ const response = await this.fetchPage(offset, l);
407
+ return {
408
+ ...response,
409
+ hasMore: offset + response.data.length < response.total
410
+ };
411
+ }
412
+ async *[Symbol.asyncIterator]() {
413
+ let offset = this.initialOffset;
414
+ const limit = this.defaultLimit;
415
+ while (true) {
416
+ const page = await this.fetchPage(offset, limit);
417
+ for (const item of page.data) {
418
+ yield item;
419
+ }
420
+ offset += page.data.length;
421
+ if (offset >= page.total || page.data.length === 0) break;
422
+ }
423
+ }
424
+ };
425
+
426
+ // src/resources/agents.ts
427
+ var AgentsClient = class {
428
+ constructor(client) {
429
+ this.client = client;
430
+ }
431
+ client;
432
+ /**
433
+ * Register your agent with Chronary. Creates a Chronary identity for an agent
434
+ * that already exists in your system, so it can own calendars, events, and webhooks.
435
+ */
436
+ async create(params, options) {
437
+ return this.client.request("POST", "/v1/agents", params, void 0, options);
438
+ }
439
+ async get(id, options) {
440
+ return this.client.request("GET", `/v1/agents/${id}`, void 0, void 0, options);
441
+ }
442
+ list(params = {}) {
443
+ const { limit = 50, offset = 0, ...filters } = params;
444
+ return new PageIterator(
445
+ (offset2, l) => this.client.request("GET", "/v1/agents", void 0, {
446
+ ...filters,
447
+ limit: l,
448
+ offset: offset2
449
+ }),
450
+ limit,
451
+ offset
452
+ );
453
+ }
454
+ async update(id, params, options) {
455
+ return this.client.request("PATCH", `/v1/agents/${id}`, params, void 0, options);
456
+ }
457
+ async delete(id, options) {
458
+ await this.client.request("DELETE", `/v1/agents/${id}`, void 0, void 0, options);
459
+ }
460
+ };
461
+
462
+ // src/resources/calendars.ts
463
+ var CalendarsClient = class {
464
+ constructor(client) {
465
+ this.client = client;
466
+ }
467
+ client;
468
+ async create(params, options) {
469
+ const { agentId, ...body } = params;
470
+ const path = agentId ? `/v1/agents/${agentId}/calendars` : "/v1/calendars";
471
+ return this.client.request("POST", path, body, void 0, options);
472
+ }
473
+ async get(id, options) {
474
+ return this.client.request("GET", `/v1/calendars/${id}`, void 0, void 0, options);
475
+ }
476
+ list(params = {}) {
477
+ const { agentId, limit = 50, offset = 0, ...filters } = params;
478
+ const path = agentId ? `/v1/agents/${agentId}/calendars` : "/v1/calendars";
479
+ return new PageIterator(
480
+ (offset2, l) => this.client.request("GET", path, void 0, {
481
+ ...filters,
482
+ limit: l,
483
+ offset: offset2
484
+ }),
485
+ limit,
486
+ offset
487
+ );
488
+ }
489
+ async update(id, params, options) {
490
+ return this.client.request("PATCH", `/v1/calendars/${id}`, params, void 0, options);
491
+ }
492
+ async delete(id, options) {
493
+ await this.client.request("DELETE", `/v1/calendars/${id}`, void 0, void 0, options);
494
+ }
495
+ async getContext(id, options) {
496
+ return this.client.request(
497
+ "GET",
498
+ `/v1/calendars/${id}/context`,
499
+ void 0,
500
+ void 0,
501
+ options
502
+ );
503
+ }
504
+ async setAvailabilityRules(id, params, options) {
505
+ return this.client.request(
506
+ "PUT",
507
+ `/v1/calendars/${id}/availability-rules`,
508
+ params,
509
+ void 0,
510
+ options
511
+ );
512
+ }
513
+ async getAvailabilityRules(id, options) {
514
+ return this.client.request(
515
+ "GET",
516
+ `/v1/calendars/${id}/availability-rules`,
517
+ void 0,
518
+ void 0,
519
+ options
520
+ );
521
+ }
522
+ async deleteAvailabilityRules(id, options) {
523
+ await this.client.request(
524
+ "DELETE",
525
+ `/v1/calendars/${id}/availability-rules`,
526
+ void 0,
527
+ void 0,
528
+ options
529
+ );
530
+ }
531
+ };
532
+
533
+ // src/resources/events.ts
534
+ var EventsClient = class {
535
+ constructor(client) {
536
+ this.client = client;
537
+ }
538
+ client;
539
+ async create(calendarId, params, options) {
540
+ return this.client.request(
541
+ "POST",
542
+ `/v1/calendars/${calendarId}/events`,
543
+ params,
544
+ void 0,
545
+ options
546
+ );
547
+ }
548
+ async get(calendarId, eventId, options) {
549
+ return this.client.request(
550
+ "GET",
551
+ `/v1/calendars/${calendarId}/events/${eventId}`,
552
+ void 0,
553
+ void 0,
554
+ options
555
+ );
556
+ }
557
+ list(params = {}) {
558
+ const { calendarId, agentId, limit = 50, offset = 0, ...filters } = params;
559
+ let path;
560
+ if (agentId) {
561
+ path = `/v1/agents/${agentId}/events`;
562
+ } else if (calendarId) {
563
+ path = `/v1/calendars/${calendarId}/events`;
564
+ } else {
565
+ throw new Error("Either calendarId or agentId is required for events.list()");
566
+ }
567
+ return new PageIterator(
568
+ (offset2, l) => this.client.request("GET", path, void 0, {
569
+ ...filters,
570
+ limit: l,
571
+ offset: offset2
572
+ }),
573
+ limit,
574
+ offset
575
+ );
576
+ }
577
+ async update(calendarId, eventId, params, options) {
578
+ return this.client.request(
579
+ "PATCH",
580
+ `/v1/calendars/${calendarId}/events/${eventId}`,
581
+ params,
582
+ void 0,
583
+ options
584
+ );
585
+ }
586
+ async delete(calendarId, eventId, options) {
587
+ await this.client.request(
588
+ "DELETE",
589
+ `/v1/calendars/${calendarId}/events/${eventId}`,
590
+ void 0,
591
+ void 0,
592
+ options
593
+ );
594
+ }
595
+ /**
596
+ * Promote a held event (status='hold') to status='confirmed'. Fails with 409
597
+ * if the event is not a hold, or if the hold has already expired.
598
+ */
599
+ async confirm(eventId, options) {
600
+ return this.client.request(
601
+ "PUT",
602
+ `/v1/events/${eventId}/confirm`,
603
+ void 0,
604
+ void 0,
605
+ options
606
+ );
607
+ }
608
+ /**
609
+ * Manually release a held event before its TTL, freeing the slot. Fails with
610
+ * 409 if the event is not a hold.
611
+ */
612
+ async release(eventId, options) {
613
+ return this.client.request(
614
+ "PUT",
615
+ `/v1/events/${eventId}/release`,
616
+ void 0,
617
+ void 0,
618
+ options
619
+ );
620
+ }
621
+ };
622
+
623
+ // src/resources/availability.ts
624
+ var AvailabilityClient = class {
625
+ constructor(client) {
626
+ this.client = client;
627
+ }
628
+ client;
629
+ async forAgent(agentId, params, options) {
630
+ return this.client.request(
631
+ "GET",
632
+ `/v1/agents/${agentId}/availability`,
633
+ void 0,
634
+ {
635
+ start: params.start,
636
+ end: params.end,
637
+ slot_duration: params.slot_duration,
638
+ include_busy: params.include_busy
639
+ },
640
+ options
641
+ );
642
+ }
643
+ async forCalendar(calendarId, params, options) {
644
+ return this.client.request(
645
+ "GET",
646
+ `/v1/calendars/${calendarId}/availability`,
647
+ void 0,
648
+ {
649
+ start: params.start,
650
+ end: params.end,
651
+ slot_duration: params.slot_duration,
652
+ include_busy: params.include_busy
653
+ },
654
+ options
655
+ );
656
+ }
657
+ async check(params, options) {
658
+ return this.client.request(
659
+ "GET",
660
+ "/v1/availability",
661
+ void 0,
662
+ {
663
+ agents: params.agents.join(","),
664
+ start: params.start,
665
+ end: params.end,
666
+ slot_duration: params.slot_duration,
667
+ calendars: params.calendars?.join(","),
668
+ include_busy: params.include_busy
669
+ },
670
+ options
671
+ );
672
+ }
673
+ };
674
+
675
+ // src/resources/webhooks.ts
676
+ var WebhooksClient = class {
677
+ constructor(client) {
678
+ this.client = client;
679
+ }
680
+ client;
681
+ async create(params, options) {
682
+ return this.client.request("POST", "/v1/webhooks", params, void 0, options);
683
+ }
684
+ async get(id, options) {
685
+ return this.client.request("GET", `/v1/webhooks/${id}`, void 0, void 0, options);
686
+ }
687
+ list(params = {}) {
688
+ const { limit = 20, offset = 0, ...filters } = params;
689
+ return new PageIterator(
690
+ (offset2, l) => this.client.request("GET", "/v1/webhooks", void 0, {
691
+ ...filters,
692
+ limit: l,
693
+ offset: offset2
694
+ }),
695
+ limit,
696
+ offset
697
+ );
698
+ }
699
+ async update(id, params, options) {
700
+ return this.client.request("PATCH", `/v1/webhooks/${id}`, params, void 0, options);
701
+ }
702
+ async delete(id, options) {
703
+ await this.client.request("DELETE", `/v1/webhooks/${id}`, void 0, void 0, options);
704
+ }
705
+ async listDeliveries(webhookId, params = {}, options) {
706
+ const { limit = 20, offset = 0, status, include_payload } = params;
707
+ return this.client.request(
708
+ "GET",
709
+ `/v1/webhooks/${webhookId}/deliveries`,
710
+ void 0,
711
+ { limit, offset, ...status !== void 0 && { status }, ...include_payload !== void 0 && { include_payload } },
712
+ options
713
+ );
714
+ }
715
+ };
716
+
717
+ // src/resources/ical-subscriptions.ts
718
+ var ICalSubscriptionsClient = class {
719
+ constructor(client) {
720
+ this.client = client;
721
+ }
722
+ client;
723
+ async create(agentId, params, options) {
724
+ return this.client.request(
725
+ "POST",
726
+ `/v1/agents/${agentId}/ical-subscriptions`,
727
+ params,
728
+ void 0,
729
+ options
730
+ );
731
+ }
732
+ async get(id, options) {
733
+ return this.client.request(
734
+ "GET",
735
+ `/v1/ical-subscriptions/${id}`,
736
+ void 0,
737
+ void 0,
738
+ options
739
+ );
740
+ }
741
+ list(params) {
742
+ const { agentId, limit = 50, offset = 0, ...filters } = params;
743
+ return new PageIterator(
744
+ (offset2, l) => this.client.request(
745
+ "GET",
746
+ `/v1/agents/${agentId}/ical-subscriptions`,
747
+ void 0,
748
+ { ...filters, limit: l, offset: offset2 }
749
+ ),
750
+ limit,
751
+ offset
752
+ );
753
+ }
754
+ async update(id, params, options) {
755
+ return this.client.request(
756
+ "PATCH",
757
+ `/v1/ical-subscriptions/${id}`,
758
+ params,
759
+ void 0,
760
+ options
761
+ );
762
+ }
763
+ async delete(id, options) {
764
+ await this.client.request(
765
+ "DELETE",
766
+ `/v1/ical-subscriptions/${id}`,
767
+ void 0,
768
+ void 0,
769
+ options
770
+ );
771
+ }
772
+ async sync(id, options) {
773
+ return this.client.request(
774
+ "POST",
775
+ `/v1/ical-subscriptions/${id}/sync`,
776
+ void 0,
777
+ void 0,
778
+ options
779
+ );
780
+ }
781
+ };
782
+
783
+ // src/resources/scheduling.ts
784
+ var SchedulingClient = class {
785
+ constructor(client) {
786
+ this.client = client;
787
+ }
788
+ client;
789
+ async create(params, options) {
790
+ return this.client.request(
791
+ "POST",
792
+ "/v1/scheduling/proposals",
793
+ params,
794
+ void 0,
795
+ options
796
+ );
797
+ }
798
+ list(params = {}) {
799
+ const { limit = 50, offset = 0, ...filters } = params;
800
+ return new PageIterator(
801
+ (offset2, l) => this.client.request(
802
+ "GET",
803
+ "/v1/scheduling/proposals",
804
+ void 0,
805
+ { ...filters, limit: l, offset: offset2 }
806
+ ),
807
+ limit,
808
+ offset
809
+ );
810
+ }
811
+ async get(id, options) {
812
+ return this.client.request(
813
+ "GET",
814
+ `/v1/scheduling/proposals/${id}`,
815
+ void 0,
816
+ void 0,
817
+ options
818
+ );
819
+ }
820
+ async respond(id, params, options) {
821
+ return this.client.request(
822
+ "POST",
823
+ `/v1/scheduling/proposals/${id}/respond`,
824
+ params,
825
+ void 0,
826
+ options
827
+ );
828
+ }
829
+ async resolve(id, options) {
830
+ return this.client.request(
831
+ "POST",
832
+ `/v1/scheduling/proposals/${id}/resolve`,
833
+ void 0,
834
+ void 0,
835
+ options
836
+ );
837
+ }
838
+ async cancel(id, options) {
839
+ return this.client.request(
840
+ "POST",
841
+ `/v1/scheduling/proposals/${id}/cancel`,
842
+ void 0,
843
+ void 0,
844
+ options
845
+ );
846
+ }
847
+ };
848
+
849
+ // src/resources/usage.ts
850
+ var UsageClient = class {
851
+ constructor(client) {
852
+ this.client = client;
853
+ }
854
+ client;
855
+ async get(options) {
856
+ return this.client.request("GET", "/v1/usage", void 0, void 0, options);
857
+ }
858
+ };
859
+
860
+ // src/resources/audit-log.ts
861
+ var AuditLogClient = class {
862
+ constructor(client) {
863
+ this.client = client;
864
+ }
865
+ client;
866
+ async list(params, options) {
867
+ const query = {
868
+ from: params?.from,
869
+ to: params?.to,
870
+ action: params?.action,
871
+ actor_key_prefix: params?.actor_key_prefix,
872
+ cursor: params?.cursor,
873
+ limit: params?.limit
874
+ };
875
+ return this.client.request("GET", "/v1/audit-log", void 0, query, options);
876
+ }
877
+ };
878
+
879
+ // src/resources/keys.ts
880
+ var KeysClient = class {
881
+ constructor(client) {
882
+ this.client = client;
883
+ }
884
+ client;
885
+ async create(params, options) {
886
+ return this.client.request("POST", "/v1/keys", params, void 0, options);
887
+ }
888
+ async list(options) {
889
+ const response = await this.client.request(
890
+ "GET",
891
+ "/v1/keys",
892
+ void 0,
893
+ void 0,
894
+ options
895
+ );
896
+ return response.keys;
897
+ }
898
+ async delete(id, options) {
899
+ await this.client.request("DELETE", `/v1/keys/${id}`, void 0, void 0, options);
900
+ }
901
+ };
902
+
903
+ // src/resources/agent-auth.ts
904
+ var AgentAuthClient = class {
905
+ constructor(client) {
906
+ this.client = client;
907
+ }
908
+ client;
909
+ /**
910
+ * Register a new agent + org. Sends an OTP to the provided email.
911
+ *
912
+ * No authentication required — callers may construct a `Chronary` client
913
+ * with no `apiKey` to invoke this method.
914
+ *
915
+ * The response is a discriminated union:
916
+ * - **New org path** — returns `org_id`, `agent_id`, and `api_key`.
917
+ * The `api_key` is restricted to the verify endpoint until the OTP
918
+ * is submitted successfully; after verification it unlocks the full API.
919
+ * - **Existing-org dedup** — returns only `message`. No credentials are
920
+ * leaked when the email matches an existing org (enumeration defense).
921
+ *
922
+ * Use `isAgentSignUpNewOrg()` to narrow the union before accessing keys.
923
+ *
924
+ * @throws ChronaryError (409 `tos_version_stale`) if `tos_version` is not
925
+ * the currently-published version — retry with the value from the
926
+ * `current_version` field on the error body.
927
+ */
928
+ async signUp(params, options) {
929
+ return this.client.request(
930
+ "POST",
931
+ "/v1/agent/sign-up",
932
+ params,
933
+ void 0,
934
+ options
935
+ );
936
+ }
937
+ /**
938
+ * Submit the OTP sent during `signUp()` to unlock the API key.
939
+ *
940
+ * Must be invoked on a `Chronary` client constructed with the restricted
941
+ * `api_key` returned by `signUp()` — NOT on the client that issued
942
+ * `signUp()` itself (which had no key).
943
+ *
944
+ * @throws ChronaryError (status 400) when the OTP is wrong or expired.
945
+ * The SDK's `ValidationError` class is reserved for HTTP 422; the
946
+ * API returns 400 for invalid/expired OTPs, which surfaces as a
947
+ * generic `ChronaryError` with the original message.
948
+ */
949
+ async verify(params, options) {
950
+ return this.client.request(
951
+ "POST",
952
+ "/v1/agent/verify",
953
+ params,
954
+ void 0,
955
+ options
956
+ );
957
+ }
958
+ };
959
+ function isAgentSignUpNewOrg(response) {
960
+ return "api_key" in response;
961
+ }
962
+
963
+ // src/resources/waitlist.ts
964
+ var WaitlistClient = class {
965
+ constructor(client) {
966
+ this.client = client;
967
+ }
968
+ client;
969
+ /**
970
+ * Join the Chronary waitlist (#442). Used during private preview when open
971
+ * signup is gated. Creates a real organization row flagged as
972
+ * `is_waitlisted: true`; an admin flips the flag to activate the account.
973
+ *
974
+ * No authentication required — invoke on a `Chronary` client constructed
975
+ * with no `apiKey`.
976
+ *
977
+ * Idempotent on repeat hits: a second call with the same email returns
978
+ * the existing waitlisted org instead of erroring. An active (non-waitlisted)
979
+ * account at the same email returns 409 `email_taken`.
980
+ *
981
+ * @throws ChronaryError (409 `email_taken`) when an active account already
982
+ * exists for this email — direct the user to sign-in instead.
983
+ * @throws ChronaryError (409 `tos_version_stale`) when the supplied
984
+ * `tos_version` doesn't match the currently-published version.
985
+ * @throws ChronaryError (429 `rate_limit_error`) on the per-IP / per-email
986
+ * waitlist limiter.
987
+ */
988
+ async join(params, options) {
989
+ return this.client.request(
990
+ "POST",
991
+ "/v1/waitlist",
992
+ params,
993
+ void 0,
994
+ options
995
+ );
996
+ }
997
+ };
998
+
999
+ // src/resources/feedback.ts
1000
+ var FeedbackClient = class {
1001
+ constructor(client) {
1002
+ this.client = client;
1003
+ }
1004
+ client;
1005
+ /**
1006
+ * Submit structured feedback (bug, feature, or friction) to Chronary.
1007
+ *
1008
+ * Rate-limited to 25 submissions per day per organization (UTC day).
1009
+ * Available on all plans, including free. The 26th submission returns
1010
+ * HTTP 429 with a `Retry-After` header set to the seconds until the
1011
+ * next UTC midnight.
1012
+ */
1013
+ async submit(params, options) {
1014
+ return this.client.request(
1015
+ "POST",
1016
+ "/v1/feedback",
1017
+ params,
1018
+ void 0,
1019
+ options
1020
+ );
1021
+ }
1022
+ };
1023
+
1024
+ // src/resources/plans.ts
1025
+ var PlansClient = class {
1026
+ constructor(client) {
1027
+ this.client = client;
1028
+ }
1029
+ client;
1030
+ /**
1031
+ * Fetch the public plan catalog (`GET /v1/plans`).
1032
+ *
1033
+ * Returns all sellable tiers (free, pro, scale) plus the enterprise
1034
+ * tier with `custom_pricing: true`. No authentication required.
1035
+ * Responses are cacheable for 5 minutes at the edge.
1036
+ */
1037
+ async list(options) {
1038
+ return this.client.request("GET", "/v1/plans", void 0, void 0, options);
1039
+ }
1040
+ };
1041
+
1042
+ // src/resources/account.ts
1043
+ var AccountClient = class {
1044
+ constructor(client) {
1045
+ this.client = client;
1046
+ }
1047
+ client;
1048
+ /**
1049
+ * Export every row this org owns as a single JSON payload (GDPR Art. 15 + 20
1050
+ * portability / CCPA right-to-know / EU Data Act interoperability).
1051
+ *
1052
+ * **Authentication:** This endpoint is JWT-only — it returns decrypted
1053
+ * webhook secrets and iCal subscription URLs that aren't normally accessible
1054
+ * via API-key endpoints. Configure the SDK with a console JWT (e.g. cookie
1055
+ * value or Bearer token from the console session) as the `apiKey` config to
1056
+ * call this. API keys (`chr_sk_*` / `chr_ak_*`) will return 401.
1057
+ *
1058
+ * In most cases, end users should download via the console UI at
1059
+ * `console.chronary.ai/settings`. The SDK method exists for programmatic
1060
+ * use cases (e.g. server-side compliance tooling holding a delegated JWT).
1061
+ *
1062
+ * **Rate limit:** 10 exports/hour/org.
1063
+ */
1064
+ async export(options) {
1065
+ return this.client.request(
1066
+ "GET",
1067
+ "/v1/auth/export",
1068
+ void 0,
1069
+ void 0,
1070
+ options
1071
+ );
1072
+ }
1073
+ };
1074
+
1075
+ // src/webhook-verify.ts
1076
+ var DEFAULT_TOLERANCE = 5 * 60 * 1e3;
1077
+ function bufToHex(buf) {
1078
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
1079
+ }
1080
+ async function computeSignature(secret, timestamp, payload) {
1081
+ const encoder = new TextEncoder();
1082
+ const key = await crypto.subtle.importKey(
1083
+ "raw",
1084
+ encoder.encode(secret),
1085
+ { name: "HMAC", hash: "SHA-256" },
1086
+ false,
1087
+ ["sign"]
1088
+ );
1089
+ const message = encoder.encode(`${timestamp}.${payload}`);
1090
+ const sig = await crypto.subtle.sign("HMAC", key, message);
1091
+ return `sha256=${bufToHex(sig)}`;
1092
+ }
1093
+ function constantTimeEqual(a, b) {
1094
+ const encoder = new TextEncoder();
1095
+ const aBuf = encoder.encode(a);
1096
+ const bBuf = encoder.encode(b);
1097
+ if (aBuf.length !== bBuf.length) return false;
1098
+ let diff = 0;
1099
+ for (let i = 0; i < aBuf.length; i++) diff |= aBuf[i] ^ bBuf[i];
1100
+ return diff === 0;
1101
+ }
1102
+ function getHeader(headers, name) {
1103
+ if (headers instanceof Headers) return headers.get(name);
1104
+ const record = headers;
1105
+ const direct = record[name] ?? record[name.toLowerCase()];
1106
+ if (direct !== void 0) return direct;
1107
+ const lowerName = name.toLowerCase();
1108
+ for (const [key, value] of Object.entries(record)) {
1109
+ if (key.toLowerCase() === lowerName) return value;
1110
+ }
1111
+ return null;
1112
+ }
1113
+ function extractHeaders(headers) {
1114
+ const get = (name) => {
1115
+ return getHeader(headers, name);
1116
+ };
1117
+ const signature = get("X-Signature") ?? get("x-signature");
1118
+ const timestamp = get("X-Timestamp") ?? get("x-timestamp");
1119
+ if (!signature) throw new ChronaryError("Missing X-Signature header", 0);
1120
+ if (!timestamp) throw new ChronaryError("Missing X-Timestamp header", 0);
1121
+ return { signature, timestamp };
1122
+ }
1123
+ async function verifySignature(rawBody, headers, secret, options) {
1124
+ if (typeof rawBody !== "string") {
1125
+ throw new ChronaryError(
1126
+ "rawBody must be a string. If you parsed the JSON body, pass the raw string instead.",
1127
+ 0
1128
+ );
1129
+ }
1130
+ const { signature, timestamp } = extractHeaders(headers);
1131
+ const tolerance = options?.tolerance ?? DEFAULT_TOLERANCE;
1132
+ const tsMs = parseInt(timestamp, 10) * 1e3;
1133
+ const now = Date.now();
1134
+ if (Math.abs(now - tsMs) > tolerance) {
1135
+ throw new ChronaryError(
1136
+ `Webhook timestamp too old. Received ${timestamp}, current time ${Math.floor(now / 1e3)}. Tolerance: ${tolerance / 1e3}s`,
1137
+ 0
1138
+ );
1139
+ }
1140
+ const expected = await computeSignature(secret, timestamp, rawBody);
1141
+ if (!constantTimeEqual(expected, signature)) {
1142
+ throw new ChronaryError("Webhook signature verification failed", 0);
1143
+ }
1144
+ }
1145
+ async function constructEvent(rawBody, headers, secret, options) {
1146
+ await verifySignature(rawBody, headers, secret, options);
1147
+ const type = getHeader(headers, "X-Chronary-Event-Type");
1148
+ if (!type) throw new ChronaryError("Missing X-Chronary-Event-Type header", 0);
1149
+ const data = JSON.parse(rawBody);
1150
+ return { type, data };
1151
+ }
1152
+
1153
+ // src/index.ts
1154
+ var Chronary = class {
1155
+ agents;
1156
+ calendars;
1157
+ events;
1158
+ availability;
1159
+ webhooks;
1160
+ icalSubscriptions;
1161
+ scheduling;
1162
+ usage;
1163
+ auditLog;
1164
+ keys;
1165
+ agentAuth;
1166
+ waitlist;
1167
+ feedback;
1168
+ plans;
1169
+ account;
1170
+ constructor(config) {
1171
+ const client = new CoreClient(config);
1172
+ this.agents = new AgentsClient(client);
1173
+ this.calendars = new CalendarsClient(client);
1174
+ this.events = new EventsClient(client);
1175
+ this.availability = new AvailabilityClient(client);
1176
+ this.webhooks = new WebhooksClient(client);
1177
+ this.icalSubscriptions = new ICalSubscriptionsClient(client);
1178
+ this.scheduling = new SchedulingClient(client);
1179
+ this.usage = new UsageClient(client);
1180
+ this.auditLog = new AuditLogClient(client);
1181
+ this.keys = new KeysClient(client);
1182
+ this.agentAuth = new AgentAuthClient(client);
1183
+ this.waitlist = new WaitlistClient(client);
1184
+ this.feedback = new FeedbackClient(client);
1185
+ this.plans = new PlansClient(client);
1186
+ this.account = new AccountClient(client);
1187
+ }
1188
+ static webhooks = {
1189
+ verifySignature,
1190
+ constructEvent
1191
+ };
1192
+ };
1193
+ var index_default = Chronary;
1194
+ // Annotate the CommonJS export names for ESM import in node:
1195
+ 0 && (module.exports = {
1196
+ APIPromise,
1197
+ AuthenticationError,
1198
+ Chronary,
1199
+ ChronaryError,
1200
+ ConnectionError,
1201
+ CoreClient,
1202
+ NotFoundError,
1203
+ PageIterator,
1204
+ QuotaExceededError,
1205
+ RateLimitError,
1206
+ TimeoutError,
1207
+ VERSION,
1208
+ ValidationError,
1209
+ constructEvent,
1210
+ isAgentSignUpNewOrg,
1211
+ verifySignature
1212
+ });
1213
+ //# sourceMappingURL=index.cjs.map