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