granola-toolkit 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +71 -17
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -540,13 +540,23 @@ function parseDocument(value) {
|
|
|
540
540
|
}
|
|
541
541
|
//#endregion
|
|
542
542
|
//#region src/client/granola.ts
|
|
543
|
-
const
|
|
544
|
-
const CLIENT_VERSION = "5.354.0";
|
|
543
|
+
const DEFAULT_CLIENT_VERSION = "5.354.0";
|
|
545
544
|
const DOCUMENTS_URL = "https://api.granola.ai/v2/get-documents";
|
|
545
|
+
function resolveClientVersion(value) {
|
|
546
|
+
return value?.trim() || process.env.GRANOLA_CLIENT_VERSION?.trim() || DEFAULT_CLIENT_VERSION;
|
|
547
|
+
}
|
|
546
548
|
var GranolaApiClient = class {
|
|
547
|
-
|
|
549
|
+
clientVersion;
|
|
550
|
+
documentsUrl;
|
|
551
|
+
constructor(httpClient, options = DOCUMENTS_URL) {
|
|
548
552
|
this.httpClient = httpClient;
|
|
549
|
-
|
|
553
|
+
if (typeof options === "string") {
|
|
554
|
+
this.documentsUrl = options;
|
|
555
|
+
this.clientVersion = resolveClientVersion();
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
this.documentsUrl = options.documentsUrl ?? DOCUMENTS_URL;
|
|
559
|
+
this.clientVersion = resolveClientVersion(options.clientVersion);
|
|
550
560
|
}
|
|
551
561
|
async listDocuments(options) {
|
|
552
562
|
const documents = [];
|
|
@@ -559,8 +569,8 @@ var GranolaApiClient = class {
|
|
|
559
569
|
offset
|
|
560
570
|
}, {
|
|
561
571
|
headers: {
|
|
562
|
-
"User-Agent":
|
|
563
|
-
"X-Client-Version":
|
|
572
|
+
"User-Agent": `Granola/${this.clientVersion}`,
|
|
573
|
+
"X-Client-Version": this.clientVersion
|
|
564
574
|
},
|
|
565
575
|
timeoutMs: options.timeoutMs
|
|
566
576
|
});
|
|
@@ -580,35 +590,79 @@ var GranolaApiClient = class {
|
|
|
580
590
|
};
|
|
581
591
|
//#endregion
|
|
582
592
|
//#region src/client/http.ts
|
|
593
|
+
const RETRYABLE_STATUS_CODES = new Set([
|
|
594
|
+
429,
|
|
595
|
+
500,
|
|
596
|
+
502,
|
|
597
|
+
503,
|
|
598
|
+
504
|
|
599
|
+
]);
|
|
600
|
+
function sleep(delayMs) {
|
|
601
|
+
return new Promise((resolve) => {
|
|
602
|
+
setTimeout(resolve, delayMs);
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function parseRetryAfter(headerValue) {
|
|
606
|
+
if (!headerValue?.trim()) return;
|
|
607
|
+
if (/^\d+$/.test(headerValue.trim())) return Number(headerValue.trim()) * 1e3;
|
|
608
|
+
const retryAt = Date.parse(headerValue);
|
|
609
|
+
if (Number.isNaN(retryAt)) return;
|
|
610
|
+
return Math.max(0, retryAt - Date.now());
|
|
611
|
+
}
|
|
583
612
|
var AuthenticatedHttpClient = class {
|
|
584
613
|
fetchImpl;
|
|
585
614
|
constructor(options) {
|
|
586
615
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
587
616
|
this.logger = options.logger;
|
|
617
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
618
|
+
this.retryBaseDelayMs = options.retryBaseDelayMs ?? 500;
|
|
619
|
+
this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
|
|
620
|
+
this.sleepImpl = options.sleepImpl ?? sleep;
|
|
588
621
|
this.tokenProvider = options.tokenProvider;
|
|
589
622
|
}
|
|
590
623
|
logger;
|
|
624
|
+
maxRetries;
|
|
625
|
+
retryBaseDelayMs;
|
|
626
|
+
retryMaxDelayMs;
|
|
627
|
+
sleepImpl;
|
|
591
628
|
tokenProvider;
|
|
592
|
-
async
|
|
629
|
+
async retry(options, attempt, reason, response) {
|
|
630
|
+
const retryAfterMs = parseRetryAfter(response?.headers.get("retry-after") ?? null);
|
|
631
|
+
const delayMs = Math.min(retryAfterMs ?? this.retryBaseDelayMs * 2 ** attempt, this.retryMaxDelayMs);
|
|
632
|
+
this.logger?.warn?.(`${reason}; retrying in ${delayMs}ms (${attempt + 1}/${this.maxRetries})`);
|
|
633
|
+
await this.sleepImpl(delayMs);
|
|
634
|
+
return this.request(options, attempt + 1);
|
|
635
|
+
}
|
|
636
|
+
async request(options, attempt = 0) {
|
|
593
637
|
const { retryOnUnauthorized = true, timeoutMs, url } = options;
|
|
594
638
|
const accessToken = await this.tokenProvider.getAccessToken();
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
639
|
+
let response;
|
|
640
|
+
try {
|
|
641
|
+
response = await this.fetchImpl(url, {
|
|
642
|
+
body: options.body,
|
|
643
|
+
headers: {
|
|
644
|
+
...options.headers,
|
|
645
|
+
Authorization: `Bearer ${accessToken}`
|
|
646
|
+
},
|
|
647
|
+
method: options.method ?? "GET",
|
|
648
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
649
|
+
});
|
|
650
|
+
} catch (error) {
|
|
651
|
+
if (attempt < this.maxRetries) {
|
|
652
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
653
|
+
return this.retry(options, attempt, `request failed: ${message}`);
|
|
654
|
+
}
|
|
655
|
+
throw error;
|
|
656
|
+
}
|
|
604
657
|
if (response.status === 401 && retryOnUnauthorized) {
|
|
605
658
|
this.logger?.warn?.("request returned 401; invalidating token provider and retrying once");
|
|
606
659
|
await this.tokenProvider.invalidate();
|
|
607
660
|
return this.request({
|
|
608
661
|
...options,
|
|
609
662
|
retryOnUnauthorized: false
|
|
610
|
-
});
|
|
663
|
+
}, attempt);
|
|
611
664
|
}
|
|
665
|
+
if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) return this.retry(options, attempt, `request returned ${response.status} ${response.statusText || ""}`.trim(), response);
|
|
612
666
|
return response;
|
|
613
667
|
}
|
|
614
668
|
async postJson(url, body, options = { timeoutMs: 3e4 }) {
|