notebooklm-sdk 0.3.2 → 0.3.4
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 +23 -9
- package/dist/{auth-Dxsm8894.d.cts → auth-BlG6x47F.d.cts} +2 -1
- package/dist/{auth-Dxsm8894.d.ts → auth-BlG6x47F.d.ts} +2 -1
- package/dist/auth.cjs +7 -0
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +2 -2
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +7 -1
- package/dist/auth.js.map +1 -1
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +581 -402
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -7
- package/dist/index.d.ts +36 -7
- package/dist/index.js +535 -377
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -311,356 +311,176 @@ var init_enums = __esm({
|
|
|
311
311
|
}
|
|
312
312
|
});
|
|
313
313
|
|
|
314
|
+
// src/api/artifacts.ts
|
|
315
|
+
init_enums();
|
|
316
|
+
|
|
314
317
|
// src/types/errors.ts
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
constructor(message) {
|
|
320
|
-
super(message);
|
|
321
|
-
this.name = this.constructor.name;
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
exports.NetworkError = class extends exports.NotebookLMError {
|
|
325
|
-
methodId;
|
|
326
|
-
originalError;
|
|
327
|
-
constructor(message, opts = {}) {
|
|
328
|
-
super(message);
|
|
329
|
-
this.methodId = opts.methodId;
|
|
330
|
-
this.originalError = opts.originalError;
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
exports.RPCTimeoutError = class extends exports.NetworkError {
|
|
334
|
-
};
|
|
335
|
-
exports.RPCError = class extends exports.NotebookLMError {
|
|
336
|
-
methodId;
|
|
337
|
-
rawResponse;
|
|
338
|
-
rpcCode;
|
|
339
|
-
foundIds;
|
|
340
|
-
constructor(message, opts = {}) {
|
|
341
|
-
super(message);
|
|
342
|
-
this.methodId = opts.methodId;
|
|
343
|
-
this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
|
|
344
|
-
this.rpcCode = opts.rpcCode;
|
|
345
|
-
this.foundIds = opts.foundIds ?? [];
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
exports.AuthError = class extends exports.RPCError {
|
|
349
|
-
};
|
|
350
|
-
exports.RateLimitError = class extends exports.RPCError {
|
|
351
|
-
retryAfter;
|
|
352
|
-
constructor(message, opts = {}) {
|
|
353
|
-
super(message, opts);
|
|
354
|
-
this.retryAfter = opts.retryAfter;
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
exports.ServerError = class extends exports.RPCError {
|
|
358
|
-
statusCode;
|
|
359
|
-
constructor(message, opts = {}) {
|
|
360
|
-
super(message, opts);
|
|
361
|
-
this.statusCode = opts.statusCode;
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
exports.ClientError = class extends exports.RPCError {
|
|
365
|
-
statusCode;
|
|
366
|
-
constructor(message, opts = {}) {
|
|
367
|
-
super(message, opts);
|
|
368
|
-
this.statusCode = opts.statusCode;
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
exports.NotebookError = class extends exports.NotebookLMError {
|
|
372
|
-
};
|
|
373
|
-
exports.NotebookNotFoundError = class extends exports.NotebookError {
|
|
374
|
-
notebookId;
|
|
375
|
-
constructor(notebookId) {
|
|
376
|
-
super(`Notebook not found: ${notebookId}`);
|
|
377
|
-
this.notebookId = notebookId;
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
exports.SourceError = class extends exports.NotebookLMError {
|
|
381
|
-
};
|
|
382
|
-
exports.SourceNotFoundError = class extends exports.SourceError {
|
|
383
|
-
sourceId;
|
|
384
|
-
constructor(sourceId) {
|
|
385
|
-
super(`Source not found: ${sourceId}`);
|
|
386
|
-
this.sourceId = sourceId;
|
|
387
|
-
}
|
|
388
|
-
};
|
|
389
|
-
exports.SourceAddError = class extends exports.SourceError {
|
|
390
|
-
url;
|
|
391
|
-
cause;
|
|
392
|
-
constructor(url, opts = {}) {
|
|
393
|
-
super(
|
|
394
|
-
opts.message ?? `Failed to add source: ${url}
|
|
395
|
-
Possible causes:
|
|
396
|
-
- URL is invalid or inaccessible
|
|
397
|
-
- Content is behind a paywall or requires authentication
|
|
398
|
-
- Rate limiting or quota exceeded`
|
|
399
|
-
);
|
|
400
|
-
this.url = url;
|
|
401
|
-
this.cause = opts.cause;
|
|
402
|
-
}
|
|
403
|
-
};
|
|
404
|
-
exports.SourceProcessingError = class extends exports.SourceError {
|
|
405
|
-
sourceId;
|
|
406
|
-
status;
|
|
407
|
-
constructor(sourceId, status = 3, message) {
|
|
408
|
-
super(message ?? `Source ${sourceId} failed to process`);
|
|
409
|
-
this.sourceId = sourceId;
|
|
410
|
-
this.status = status;
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
|
-
exports.SourceTimeoutError = class extends exports.SourceError {
|
|
414
|
-
sourceId;
|
|
415
|
-
timeout;
|
|
416
|
-
lastStatus;
|
|
417
|
-
constructor(sourceId, timeout, lastStatus) {
|
|
418
|
-
const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
|
|
419
|
-
super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
|
|
420
|
-
this.sourceId = sourceId;
|
|
421
|
-
this.timeout = timeout;
|
|
422
|
-
this.lastStatus = lastStatus;
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
|
-
exports.ArtifactError = class extends exports.NotebookLMError {
|
|
426
|
-
};
|
|
427
|
-
exports.ArtifactNotFoundError = class extends exports.ArtifactError {
|
|
428
|
-
artifactId;
|
|
429
|
-
artifactType;
|
|
430
|
-
constructor(artifactId, artifactType) {
|
|
431
|
-
const typeInfo = artifactType ? ` ${artifactType}` : "";
|
|
432
|
-
super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
|
|
433
|
-
this.artifactId = artifactId;
|
|
434
|
-
this.artifactType = artifactType;
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
exports.ArtifactNotReadyError = class extends exports.ArtifactError {
|
|
438
|
-
artifactType;
|
|
439
|
-
artifactId;
|
|
440
|
-
status;
|
|
441
|
-
constructor(artifactType, opts = {}) {
|
|
442
|
-
const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
|
|
443
|
-
const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
|
|
444
|
-
super(`${base}${statusInfo}`);
|
|
445
|
-
this.artifactType = artifactType;
|
|
446
|
-
this.artifactId = opts.artifactId;
|
|
447
|
-
this.status = opts.status;
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
exports.ArtifactParseError = class extends exports.ArtifactError {
|
|
451
|
-
artifactType;
|
|
452
|
-
artifactId;
|
|
453
|
-
details;
|
|
454
|
-
cause;
|
|
455
|
-
constructor(artifactType, opts = {}) {
|
|
456
|
-
let msg = `Failed to parse ${artifactType} artifact`;
|
|
457
|
-
if (opts.artifactId) msg += ` ${opts.artifactId}`;
|
|
458
|
-
if (opts.details) msg += `: ${opts.details}`;
|
|
459
|
-
super(msg);
|
|
460
|
-
this.artifactType = artifactType;
|
|
461
|
-
this.artifactId = opts.artifactId;
|
|
462
|
-
this.details = opts.details;
|
|
463
|
-
this.cause = opts.cause;
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
exports.ArtifactDownloadError = class extends exports.ArtifactError {
|
|
467
|
-
artifactType;
|
|
468
|
-
artifactId;
|
|
469
|
-
details;
|
|
470
|
-
cause;
|
|
471
|
-
constructor(artifactType, opts = {}) {
|
|
472
|
-
let msg = `Failed to download ${artifactType} artifact`;
|
|
473
|
-
if (opts.artifactId) msg += ` ${opts.artifactId}`;
|
|
474
|
-
if (opts.details) msg += `: ${opts.details}`;
|
|
475
|
-
super(msg);
|
|
476
|
-
this.artifactType = artifactType;
|
|
477
|
-
this.artifactId = opts.artifactId;
|
|
478
|
-
this.details = opts.details;
|
|
479
|
-
this.cause = opts.cause;
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
exports.ChatError = class extends exports.NotebookLMError {
|
|
483
|
-
};
|
|
318
|
+
var NotebookLMError = class extends Error {
|
|
319
|
+
constructor(message) {
|
|
320
|
+
super(message);
|
|
321
|
+
this.name = this.constructor.name;
|
|
484
322
|
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
fetchTokens: () => fetchTokens,
|
|
494
|
-
loadCookiesFromFile: () => loadCookiesFromFile,
|
|
495
|
-
loadCookiesFromMap: () => loadCookiesFromMap,
|
|
496
|
-
loadCookiesFromObject: () => loadCookiesFromObject,
|
|
497
|
-
loadCookiesFromString: () => loadCookiesFromString
|
|
498
|
-
});
|
|
499
|
-
function loadCookiesFromFile(filePath) {
|
|
500
|
-
let raw;
|
|
501
|
-
try {
|
|
502
|
-
raw = fs.readFileSync(filePath, "utf-8");
|
|
503
|
-
} catch {
|
|
504
|
-
throw new exports.AuthError(`Session file not found: ${filePath}
|
|
505
|
-
Run: npx notebooklm-sdk login`);
|
|
323
|
+
};
|
|
324
|
+
var NetworkError = class extends NotebookLMError {
|
|
325
|
+
methodId;
|
|
326
|
+
originalError;
|
|
327
|
+
constructor(message, opts = {}) {
|
|
328
|
+
super(message);
|
|
329
|
+
this.methodId = opts.methodId;
|
|
330
|
+
this.originalError = opts.originalError;
|
|
506
331
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
332
|
+
};
|
|
333
|
+
var RPCTimeoutError = class extends NetworkError {
|
|
334
|
+
};
|
|
335
|
+
var RPCError = class extends NotebookLMError {
|
|
336
|
+
methodId;
|
|
337
|
+
rawResponse;
|
|
338
|
+
rpcCode;
|
|
339
|
+
foundIds;
|
|
340
|
+
constructor(message, opts = {}) {
|
|
341
|
+
super(message);
|
|
342
|
+
this.methodId = opts.methodId;
|
|
343
|
+
this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
|
|
344
|
+
this.rpcCode = opts.rpcCode;
|
|
345
|
+
this.foundIds = opts.foundIds ?? [];
|
|
518
346
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const idx = part.indexOf("=");
|
|
528
|
-
if (idx > 0) {
|
|
529
|
-
const name = part.slice(0, idx).trim();
|
|
530
|
-
const value = part.slice(idx + 1).trim();
|
|
531
|
-
if (name) map[name] = value;
|
|
532
|
-
}
|
|
347
|
+
};
|
|
348
|
+
var AuthError = class extends RPCError {
|
|
349
|
+
};
|
|
350
|
+
var RateLimitError = class extends RPCError {
|
|
351
|
+
retryAfter;
|
|
352
|
+
constructor(message, opts = {}) {
|
|
353
|
+
super(message, opts);
|
|
354
|
+
this.retryAfter = opts.retryAfter;
|
|
533
355
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
if (!isAllowedDomain(domain) || !name) continue;
|
|
541
|
-
const isBase = domain === ".google.com";
|
|
542
|
-
if (!(name in cookies) || isBase) {
|
|
543
|
-
cookies[name] = value;
|
|
544
|
-
}
|
|
356
|
+
};
|
|
357
|
+
var ServerError = class extends RPCError {
|
|
358
|
+
statusCode;
|
|
359
|
+
constructor(message, opts = {}) {
|
|
360
|
+
super(message, opts);
|
|
361
|
+
this.statusCode = opts.statusCode;
|
|
545
362
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
363
|
+
};
|
|
364
|
+
var ClientError = class extends RPCError {
|
|
365
|
+
statusCode;
|
|
366
|
+
constructor(message, opts = {}) {
|
|
367
|
+
super(message, opts);
|
|
368
|
+
this.statusCode = opts.statusCode;
|
|
550
369
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
370
|
+
};
|
|
371
|
+
var NotebookError = class extends NotebookLMError {
|
|
372
|
+
};
|
|
373
|
+
var NotebookNotFoundError = class extends NotebookError {
|
|
374
|
+
notebookId;
|
|
375
|
+
constructor(notebookId) {
|
|
376
|
+
super(`Notebook not found: ${notebookId}`);
|
|
377
|
+
this.notebookId = notebookId;
|
|
556
378
|
}
|
|
557
|
-
|
|
558
|
-
|
|
379
|
+
};
|
|
380
|
+
var SourceError = class extends NotebookLMError {
|
|
381
|
+
};
|
|
382
|
+
var SourceNotFoundError = class extends SourceError {
|
|
383
|
+
sourceId;
|
|
384
|
+
constructor(sourceId) {
|
|
385
|
+
super(`Source not found: ${sourceId}`);
|
|
386
|
+
this.sourceId = sourceId;
|
|
559
387
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
388
|
+
};
|
|
389
|
+
var SourceAddError = class extends SourceError {
|
|
390
|
+
url;
|
|
391
|
+
cause;
|
|
392
|
+
constructor(url, opts = {}) {
|
|
393
|
+
super(
|
|
394
|
+
opts.message ?? `Failed to add source: ${url}
|
|
395
|
+
Possible causes:
|
|
396
|
+
- URL is invalid or inaccessible
|
|
397
|
+
- Content is behind a paywall or requires authentication
|
|
398
|
+
- Rate limiting or quota exceeded`
|
|
399
|
+
);
|
|
400
|
+
this.url = url;
|
|
401
|
+
this.cause = opts.cause;
|
|
573
402
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
403
|
+
};
|
|
404
|
+
var SourceProcessingError = class extends SourceError {
|
|
405
|
+
sourceId;
|
|
406
|
+
status;
|
|
407
|
+
constructor(sourceId, status = 3, message) {
|
|
408
|
+
super(message ?? `Source ${sourceId} failed to process`);
|
|
409
|
+
this.sourceId = sourceId;
|
|
410
|
+
this.status = status;
|
|
577
411
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
throw new exports.AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
|
|
412
|
+
};
|
|
413
|
+
var SourceTimeoutError = class extends SourceError {
|
|
414
|
+
sourceId;
|
|
415
|
+
timeout;
|
|
416
|
+
lastStatus;
|
|
417
|
+
constructor(sourceId, timeout, lastStatus) {
|
|
418
|
+
const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
|
|
419
|
+
super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
|
|
420
|
+
this.sourceId = sourceId;
|
|
421
|
+
this.timeout = timeout;
|
|
422
|
+
this.lastStatus = lastStatus;
|
|
590
423
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
|
|
424
|
+
};
|
|
425
|
+
var ArtifactError = class extends NotebookLMError {
|
|
426
|
+
};
|
|
427
|
+
var ArtifactNotFoundError = class extends ArtifactError {
|
|
428
|
+
artifactId;
|
|
429
|
+
artifactType;
|
|
430
|
+
constructor(artifactId, artifactType) {
|
|
431
|
+
const typeInfo = artifactType ? ` ${artifactType}` : "";
|
|
432
|
+
super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
|
|
433
|
+
this.artifactId = artifactId;
|
|
434
|
+
this.artifactType = artifactType;
|
|
600
435
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
} else if (opts.cookiesObject) {
|
|
614
|
-
if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
|
|
615
|
-
const storageState = opts.cookiesObject;
|
|
616
|
-
cookieMap = loadCookiesFromObject(storageState);
|
|
617
|
-
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
618
|
-
} else {
|
|
619
|
-
cookieMap = loadCookiesFromMap(opts.cookiesObject);
|
|
620
|
-
}
|
|
621
|
-
} else {
|
|
622
|
-
const envCookies = process.env["NOTEBOOKLM_COOKIES"];
|
|
623
|
-
const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
|
|
624
|
-
if (envFile) {
|
|
625
|
-
cookieMap = loadCookiesFromFile(envFile);
|
|
626
|
-
} else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
|
|
627
|
-
const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
|
|
628
|
-
const storageState = JSON.parse(raw);
|
|
629
|
-
cookieMap = loadCookiesFromObject(storageState);
|
|
630
|
-
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
631
|
-
} else if (fs.existsSync("storage_state.json")) {
|
|
632
|
-
const raw = fs.readFileSync("storage_state.json", "utf-8");
|
|
633
|
-
const storageState = JSON.parse(raw);
|
|
634
|
-
cookieMap = loadCookiesFromObject(storageState);
|
|
635
|
-
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
636
|
-
} else if (envCookies) {
|
|
637
|
-
cookieMap = loadCookiesFromString(envCookies);
|
|
638
|
-
} else {
|
|
639
|
-
throw new exports.AuthError("No session found. Run: npx notebooklm-sdk login");
|
|
640
|
-
}
|
|
436
|
+
};
|
|
437
|
+
var ArtifactNotReadyError = class extends ArtifactError {
|
|
438
|
+
artifactType;
|
|
439
|
+
artifactId;
|
|
440
|
+
status;
|
|
441
|
+
constructor(artifactType, opts = {}) {
|
|
442
|
+
const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
|
|
443
|
+
const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
|
|
444
|
+
super(`${base}${statusInfo}`);
|
|
445
|
+
this.artifactType = artifactType;
|
|
446
|
+
this.artifactId = opts.artifactId;
|
|
447
|
+
this.status = opts.status;
|
|
641
448
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
NOTEBOOKLM_URL = "https://notebooklm.google.com/";
|
|
449
|
+
};
|
|
450
|
+
var ArtifactParseError = class extends ArtifactError {
|
|
451
|
+
artifactType;
|
|
452
|
+
artifactId;
|
|
453
|
+
details;
|
|
454
|
+
cause;
|
|
455
|
+
constructor(artifactType, opts = {}) {
|
|
456
|
+
let msg = `Failed to parse ${artifactType} artifact`;
|
|
457
|
+
if (opts.artifactId) msg += ` ${opts.artifactId}`;
|
|
458
|
+
if (opts.details) msg += `: ${opts.details}`;
|
|
459
|
+
super(msg);
|
|
460
|
+
this.artifactType = artifactType;
|
|
461
|
+
this.artifactId = opts.artifactId;
|
|
462
|
+
this.details = opts.details;
|
|
463
|
+
this.cause = opts.cause;
|
|
658
464
|
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
465
|
+
};
|
|
466
|
+
var ArtifactDownloadError = class extends ArtifactError {
|
|
467
|
+
artifactType;
|
|
468
|
+
artifactId;
|
|
469
|
+
details;
|
|
470
|
+
cause;
|
|
471
|
+
constructor(artifactType, opts = {}) {
|
|
472
|
+
let msg = `Failed to download ${artifactType} artifact`;
|
|
473
|
+
if (opts.artifactId) msg += ` ${opts.artifactId}`;
|
|
474
|
+
if (opts.details) msg += `: ${opts.details}`;
|
|
475
|
+
super(msg);
|
|
476
|
+
this.artifactType = artifactType;
|
|
477
|
+
this.artifactId = opts.artifactId;
|
|
478
|
+
this.details = opts.details;
|
|
479
|
+
this.cause = opts.cause;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
var ChatError = class extends NotebookLMError {
|
|
483
|
+
};
|
|
664
484
|
|
|
665
485
|
// src/types/models.ts
|
|
666
486
|
init_enums();
|
|
@@ -1120,34 +940,77 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1120
940
|
// Polling / download
|
|
1121
941
|
// ---------------------------------------------------------------------------
|
|
1122
942
|
/** Poll until artifact reaches completed/failed status. */
|
|
1123
|
-
async waitUntilReady(notebookId, artifactId, timeout =
|
|
943
|
+
async waitUntilReady(notebookId, artifactId, timeout = 1800, pollInterval = 3) {
|
|
944
|
+
return this.pollUntilReady(notebookId, artifactId, {
|
|
945
|
+
timeoutSecs: timeout,
|
|
946
|
+
intervalSecs: pollInterval
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
/** Poll until artifact is fully ready, with optional progress hooks and cancellation. */
|
|
950
|
+
async pollUntilReady(notebookId, artifactId, opts = {}) {
|
|
951
|
+
const timeout = opts.timeoutSecs ?? 1800;
|
|
952
|
+
const pollInterval = opts.intervalSecs ?? 3;
|
|
1124
953
|
const deadline = Date.now() + timeout * 1e3;
|
|
954
|
+
let lastStatus = null;
|
|
1125
955
|
while (Date.now() < deadline) {
|
|
956
|
+
this.throwIfAborted(opts.signal);
|
|
957
|
+
const status = await this.pollStatus(notebookId, artifactId);
|
|
958
|
+
lastStatus = status;
|
|
959
|
+
if (opts.onTick) await opts.onTick(status);
|
|
1126
960
|
const artifact = await this.get(notebookId, artifactId);
|
|
1127
|
-
if (artifact?.status === "completed")
|
|
1128
|
-
|
|
1129
|
-
|
|
961
|
+
if (artifact?.status === "completed") {
|
|
962
|
+
if (artifact.kind === "audio" && !artifact.audioUrl || artifact.kind === "video" && !artifact.videoUrl) {
|
|
963
|
+
await sleep(pollInterval * 1e3);
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
return artifact;
|
|
967
|
+
}
|
|
968
|
+
if (artifact?.status === "failed" || status.status === "failed") {
|
|
969
|
+
throw new ArtifactNotReadyError(artifact?.kind ?? "artifact", {
|
|
970
|
+
artifactId,
|
|
971
|
+
status: "failed"
|
|
972
|
+
});
|
|
1130
973
|
}
|
|
1131
974
|
await sleep(pollInterval * 1e3);
|
|
1132
975
|
}
|
|
1133
|
-
throw new
|
|
976
|
+
throw new ArtifactNotReadyError("artifact", {
|
|
977
|
+
artifactId,
|
|
978
|
+
status: lastStatus?.status ?? "timeout"
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
throwIfAborted(signal) {
|
|
982
|
+
if (!signal?.aborted) return;
|
|
983
|
+
throw new Error("Artifact polling aborted");
|
|
984
|
+
}
|
|
985
|
+
/** Get the current status of a generated artifact without waiting. */
|
|
986
|
+
async pollStatus(notebookId, artifactId) {
|
|
987
|
+
const rawList = await this._listRaw(notebookId);
|
|
988
|
+
for (const item of rawList) {
|
|
989
|
+
if (!Array.isArray(item) || item[0] !== artifactId) continue;
|
|
990
|
+
const statusCode = typeof item[4] === "number" ? item[4] : null;
|
|
991
|
+
return {
|
|
992
|
+
artifactId,
|
|
993
|
+
status: statusCode != null ? artifactStatusFromCode(statusCode) : "pending"
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
return { artifactId, status: "pending" };
|
|
1134
997
|
}
|
|
1135
998
|
/** Download audio content as a Buffer. */
|
|
1136
999
|
async downloadAudio(notebookId, artifactId) {
|
|
1137
1000
|
const artifact = await this.get(notebookId, artifactId);
|
|
1138
1001
|
if (!artifact || artifact.status !== "completed") {
|
|
1139
|
-
throw new
|
|
1002
|
+
throw new ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
|
|
1140
1003
|
}
|
|
1141
|
-
if (!artifact.audioUrl) throw new
|
|
1004
|
+
if (!artifact.audioUrl) throw new ArtifactNotReadyError("audio", { artifactId });
|
|
1142
1005
|
return this._fetchMediaWithCookies(artifact.audioUrl);
|
|
1143
1006
|
}
|
|
1144
1007
|
/** Download video content as a Buffer. */
|
|
1145
1008
|
async downloadVideo(notebookId, artifactId) {
|
|
1146
1009
|
const artifact = await this.get(notebookId, artifactId);
|
|
1147
1010
|
if (!artifact || artifact.status !== "completed") {
|
|
1148
|
-
throw new
|
|
1011
|
+
throw new ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
|
|
1149
1012
|
}
|
|
1150
|
-
if (!artifact.videoUrl) throw new
|
|
1013
|
+
if (!artifact.videoUrl) throw new ArtifactNotReadyError("video", { artifactId });
|
|
1151
1014
|
return this._fetchMediaWithCookies(artifact.videoUrl);
|
|
1152
1015
|
}
|
|
1153
1016
|
/** Get markdown content for a completed report artifact. */
|
|
@@ -1176,12 +1039,12 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1176
1039
|
const raw = rawList.find(
|
|
1177
1040
|
(a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.SLIDE_DECK
|
|
1178
1041
|
);
|
|
1179
|
-
if (!raw) throw new
|
|
1042
|
+
if (!raw) throw new ArtifactNotReadyError("slide_deck", { artifactId });
|
|
1180
1043
|
const metadata = raw[16];
|
|
1181
|
-
if (!Array.isArray(metadata)) throw new
|
|
1044
|
+
if (!Array.isArray(metadata)) throw new ArtifactNotReadyError("slide_deck", { artifactId });
|
|
1182
1045
|
const url = format === "pptx" ? metadata[4] : metadata[3];
|
|
1183
1046
|
if (typeof url !== "string" || !url.startsWith("http")) {
|
|
1184
|
-
throw new
|
|
1047
|
+
throw new ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
|
|
1185
1048
|
}
|
|
1186
1049
|
return this._fetchMediaWithCookies(url);
|
|
1187
1050
|
}
|
|
@@ -1191,7 +1054,7 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1191
1054
|
const raw = rawList.find(
|
|
1192
1055
|
(a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.INFOGRAPHIC
|
|
1193
1056
|
);
|
|
1194
|
-
if (!raw) throw new
|
|
1057
|
+
if (!raw) throw new ArtifactNotReadyError("infographic", { artifactId });
|
|
1195
1058
|
let url = null;
|
|
1196
1059
|
for (let i = raw.length - 1; i >= 0; i--) {
|
|
1197
1060
|
const item = raw[i];
|
|
@@ -1200,7 +1063,7 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1200
1063
|
break;
|
|
1201
1064
|
}
|
|
1202
1065
|
}
|
|
1203
|
-
if (!url) throw new
|
|
1066
|
+
if (!url) throw new ArtifactNotReadyError("infographic", { artifactId });
|
|
1204
1067
|
return this._fetchMediaWithCookies(url);
|
|
1205
1068
|
}
|
|
1206
1069
|
/** Get AI-suggested report formats based on notebook content. */
|
|
@@ -1382,14 +1245,14 @@ function isTrustedDomain(url) {
|
|
|
1382
1245
|
|
|
1383
1246
|
// src/api/chat.ts
|
|
1384
1247
|
init_enums();
|
|
1385
|
-
init_errors();
|
|
1386
1248
|
var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
|
|
1387
1249
|
var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
|
|
1388
1250
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1389
1251
|
var ChatAPI = class {
|
|
1390
|
-
constructor(rpc, auth) {
|
|
1252
|
+
constructor(rpc, auth, refreshAuth) {
|
|
1391
1253
|
this.rpc = rpc;
|
|
1392
1254
|
this.auth = auth;
|
|
1255
|
+
this.refreshAuth = refreshAuth;
|
|
1393
1256
|
}
|
|
1394
1257
|
conversationCache = /* @__PURE__ */ new Map();
|
|
1395
1258
|
reqid = Math.floor(Math.random() * 9e5) + 1e5;
|
|
@@ -1419,20 +1282,7 @@ var ChatAPI = class {
|
|
|
1419
1282
|
const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
|
|
1420
1283
|
if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
|
|
1421
1284
|
const body = bodyParts.join("&") + "&";
|
|
1422
|
-
|
|
1423
|
-
try {
|
|
1424
|
-
response = await fetch(`${QUERY_URL}?${urlParams.toString()}`, {
|
|
1425
|
-
method: "POST",
|
|
1426
|
-
headers: {
|
|
1427
|
-
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
1428
|
-
Cookie: this.auth.cookieHeader
|
|
1429
|
-
},
|
|
1430
|
-
body
|
|
1431
|
-
});
|
|
1432
|
-
} catch (e) {
|
|
1433
|
-
throw new exports.ChatError(`Chat request failed: ${String(e)}`);
|
|
1434
|
-
}
|
|
1435
|
-
if (!response.ok) throw new exports.ChatError(`Chat request failed: HTTP ${response.status}`);
|
|
1285
|
+
const response = await this._postChatRequest(`${QUERY_URL}?${urlParams.toString()}`, body);
|
|
1436
1286
|
const text = await response.text();
|
|
1437
1287
|
const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
|
|
1438
1288
|
const finalConvId = serverConvId ?? conversationId;
|
|
@@ -1492,12 +1342,47 @@ var ChatAPI = class {
|
|
|
1492
1342
|
}
|
|
1493
1343
|
return null;
|
|
1494
1344
|
}
|
|
1345
|
+
async getHistory(notebookId, limit = 100, conversationId) {
|
|
1346
|
+
const convId = conversationId ?? await this.getLastConversationId(notebookId);
|
|
1347
|
+
if (!convId) return [];
|
|
1348
|
+
const params = [[], null, null, convId, limit];
|
|
1349
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_CONVERSATION_TURNS, params, {
|
|
1350
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1351
|
+
allowNull: true
|
|
1352
|
+
});
|
|
1353
|
+
if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
|
|
1354
|
+
const rawTurns = [...result[0]].reverse();
|
|
1355
|
+
const history = [];
|
|
1356
|
+
let i = 0;
|
|
1357
|
+
while (i < rawTurns.length) {
|
|
1358
|
+
const turn = rawTurns[i];
|
|
1359
|
+
if (!Array.isArray(turn) || turn.length < 3) {
|
|
1360
|
+
i++;
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
if (turn[2] === 1 && turn.length > 3) {
|
|
1364
|
+
const query = typeof turn[3] === "string" ? turn[3] : "";
|
|
1365
|
+
let answer = "";
|
|
1366
|
+
const next = rawTurns[i + 1];
|
|
1367
|
+
if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
|
|
1368
|
+
try {
|
|
1369
|
+
answer = String(next[4][0][0] ?? "");
|
|
1370
|
+
} catch {
|
|
1371
|
+
}
|
|
1372
|
+
i++;
|
|
1373
|
+
}
|
|
1374
|
+
history.push([query, answer]);
|
|
1375
|
+
}
|
|
1376
|
+
i++;
|
|
1377
|
+
}
|
|
1378
|
+
return history;
|
|
1379
|
+
}
|
|
1495
1380
|
/**
|
|
1496
1381
|
* Low-level chat configuration. Set goal, response length, and optional
|
|
1497
1382
|
* custom instructions directly. Persists on the server per notebook.
|
|
1498
1383
|
* Use `setMode()` for preset combinations instead.
|
|
1499
1384
|
*/
|
|
1500
|
-
async configure(notebookId, goal, length, customPrompt) {
|
|
1385
|
+
async configure(notebookId, goal = exports.ChatGoal.DEFAULT, length = exports.ChatResponseLength.DEFAULT, customPrompt) {
|
|
1501
1386
|
if (goal === exports.ChatGoal.CUSTOM && !customPrompt) {
|
|
1502
1387
|
throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
|
|
1503
1388
|
}
|
|
@@ -1529,6 +1414,14 @@ var ChatAPI = class {
|
|
|
1529
1414
|
this.conversationCache.clear();
|
|
1530
1415
|
}
|
|
1531
1416
|
}
|
|
1417
|
+
getCachedTurns(conversationId) {
|
|
1418
|
+
const turns = this.conversationCache.get(conversationId) ?? [];
|
|
1419
|
+
return turns.map((turn) => ({
|
|
1420
|
+
query: turn.query,
|
|
1421
|
+
answer: turn.answer,
|
|
1422
|
+
turnNumber: turn.turnNumber
|
|
1423
|
+
}));
|
|
1424
|
+
}
|
|
1532
1425
|
_buildHistory(conversationId) {
|
|
1533
1426
|
const turns = this.conversationCache.get(conversationId) ?? [];
|
|
1534
1427
|
if (!turns.length) return null;
|
|
@@ -1539,6 +1432,27 @@ var ChatAPI = class {
|
|
|
1539
1432
|
}
|
|
1540
1433
|
return history;
|
|
1541
1434
|
}
|
|
1435
|
+
async _postChatRequest(url, body, retried = false) {
|
|
1436
|
+
let response;
|
|
1437
|
+
try {
|
|
1438
|
+
response = await fetch(url, {
|
|
1439
|
+
method: "POST",
|
|
1440
|
+
headers: {
|
|
1441
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
1442
|
+
Cookie: this.auth.cookieHeader
|
|
1443
|
+
},
|
|
1444
|
+
body
|
|
1445
|
+
});
|
|
1446
|
+
} catch (e) {
|
|
1447
|
+
throw new ChatError(`Chat request failed: ${String(e)}`);
|
|
1448
|
+
}
|
|
1449
|
+
if ((response.status === 401 || response.status === 403) && !retried && this.refreshAuth) {
|
|
1450
|
+
await this.refreshAuth();
|
|
1451
|
+
return this._postChatRequest(url, body, true);
|
|
1452
|
+
}
|
|
1453
|
+
if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
|
|
1454
|
+
return response;
|
|
1455
|
+
}
|
|
1542
1456
|
};
|
|
1543
1457
|
function parseStreamingResponse(rawText) {
|
|
1544
1458
|
let text = rawText;
|
|
@@ -1695,6 +1609,12 @@ var NotebooksAPI = class {
|
|
|
1695
1609
|
async removeFromRecent(notebookId) {
|
|
1696
1610
|
await this.rpc.call(exports.RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
|
|
1697
1611
|
}
|
|
1612
|
+
async getRaw(notebookId) {
|
|
1613
|
+
const params = [notebookId, null, [2], null, 0];
|
|
1614
|
+
return this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
|
|
1615
|
+
sourcePath: `/notebook/${notebookId}`
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1698
1618
|
async getDescription(notebookId) {
|
|
1699
1619
|
const params = [notebookId, [2]];
|
|
1700
1620
|
const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
|
|
@@ -1722,6 +1642,41 @@ var NotebooksAPI = class {
|
|
|
1722
1642
|
}
|
|
1723
1643
|
return { summary, suggestedTopics };
|
|
1724
1644
|
}
|
|
1645
|
+
async share(notebookId, publicAccess = true, artifactId) {
|
|
1646
|
+
const shareOptions = publicAccess ? [1] : [0];
|
|
1647
|
+
const params = artifactId ? [shareOptions, notebookId, artifactId] : [shareOptions, notebookId];
|
|
1648
|
+
await this.rpc.call(exports.RPCMethod.SHARE_ARTIFACT, params, {
|
|
1649
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1650
|
+
allowNull: true
|
|
1651
|
+
});
|
|
1652
|
+
return {
|
|
1653
|
+
public: publicAccess,
|
|
1654
|
+
url: publicAccess ? this.getShareUrl(notebookId, artifactId) : null,
|
|
1655
|
+
artifactId: artifactId ?? null
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
getShareUrl(notebookId, artifactId) {
|
|
1659
|
+
const baseUrl = `https://notebooklm.google.com/notebook/${notebookId}`;
|
|
1660
|
+
return artifactId ? `${baseUrl}?artifactId=${artifactId}` : baseUrl;
|
|
1661
|
+
}
|
|
1662
|
+
async getMetadata(notebookId) {
|
|
1663
|
+
const raw = await this.getRaw(notebookId);
|
|
1664
|
+
const notebookData = Array.isArray(raw) && raw.length > 0 && Array.isArray(raw[0]) ? raw[0] : [];
|
|
1665
|
+
const notebook = parseNotebook(notebookData);
|
|
1666
|
+
const sourcesRaw = Array.isArray(notebookData[1]) ? notebookData[1] : [];
|
|
1667
|
+
const sources = sourcesRaw.filter((source) => Array.isArray(source) && source.length > 0).map((source) => parseSource(source));
|
|
1668
|
+
return {
|
|
1669
|
+
id: notebook.id,
|
|
1670
|
+
title: notebook.title,
|
|
1671
|
+
createdAt: notebook.createdAt,
|
|
1672
|
+
isOwner: notebook.isOwner,
|
|
1673
|
+
sources: sources.map((source) => ({
|
|
1674
|
+
kind: source.kind,
|
|
1675
|
+
title: source.title,
|
|
1676
|
+
url: source.url
|
|
1677
|
+
}))
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1725
1680
|
};
|
|
1726
1681
|
|
|
1727
1682
|
// src/api/notes.ts
|
|
@@ -1734,6 +1689,10 @@ var NotesAPI = class {
|
|
|
1734
1689
|
const all = await this._fetchAll(notebookId);
|
|
1735
1690
|
return all.filter((n) => !this._isMindMap(n.content));
|
|
1736
1691
|
}
|
|
1692
|
+
async get(notebookId, noteId) {
|
|
1693
|
+
const all = await this._fetchAll(notebookId);
|
|
1694
|
+
return all.find((note) => note.id === noteId) ?? null;
|
|
1695
|
+
}
|
|
1737
1696
|
async listMindMaps(notebookId) {
|
|
1738
1697
|
const all = await this._fetchAll(notebookId);
|
|
1739
1698
|
return all.filter((n) => this._isMindMap(n.content));
|
|
@@ -1765,6 +1724,9 @@ var NotesAPI = class {
|
|
|
1765
1724
|
});
|
|
1766
1725
|
return true;
|
|
1767
1726
|
}
|
|
1727
|
+
async deleteMindMap(notebookId, mindMapId) {
|
|
1728
|
+
return this.delete(notebookId, mindMapId);
|
|
1729
|
+
}
|
|
1768
1730
|
async _fetchAll(notebookId) {
|
|
1769
1731
|
const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
|
|
1770
1732
|
sourcePath: `/notebook/${notebookId}`,
|
|
@@ -2141,7 +2103,6 @@ function parseShareStatus(data, notebookId) {
|
|
|
2141
2103
|
|
|
2142
2104
|
// src/api/sources.ts
|
|
2143
2105
|
init_enums();
|
|
2144
|
-
init_errors();
|
|
2145
2106
|
var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
|
|
2146
2107
|
var SourcesAPI = class {
|
|
2147
2108
|
constructor(rpc, auth) {
|
|
@@ -2424,6 +2385,29 @@ var SourcesAPI = class {
|
|
|
2424
2385
|
});
|
|
2425
2386
|
return true;
|
|
2426
2387
|
}
|
|
2388
|
+
async rename(notebookId, sourceId, newTitle) {
|
|
2389
|
+
const params = [null, [sourceId], [[[newTitle]]]];
|
|
2390
|
+
const result = await this.rpc.call(exports.RPCMethod.UPDATE_SOURCE, params, {
|
|
2391
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2392
|
+
allowNull: true
|
|
2393
|
+
});
|
|
2394
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
2395
|
+
try {
|
|
2396
|
+
const parsed = parseSource(result);
|
|
2397
|
+
return parsed.title ? parsed : { ...parsed, title: newTitle };
|
|
2398
|
+
} catch {
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
return {
|
|
2402
|
+
id: sourceId,
|
|
2403
|
+
title: newTitle,
|
|
2404
|
+
url: null,
|
|
2405
|
+
kind: "unknown",
|
|
2406
|
+
createdAt: null,
|
|
2407
|
+
status: "ready",
|
|
2408
|
+
_typeCode: null
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2427
2411
|
async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
|
|
2428
2412
|
const deadline = Date.now() + timeout * 1e3;
|
|
2429
2413
|
let interval = initialInterval;
|
|
@@ -2433,14 +2417,28 @@ var SourcesAPI = class {
|
|
|
2433
2417
|
if (source) {
|
|
2434
2418
|
if (source.status === "ready") return source;
|
|
2435
2419
|
if (source.status === "error") {
|
|
2436
|
-
throw new
|
|
2420
|
+
throw new SourceProcessingError(sourceId, 3);
|
|
2437
2421
|
}
|
|
2438
2422
|
lastStatus = source._typeCode ?? void 0;
|
|
2439
2423
|
}
|
|
2440
2424
|
await sleep2(interval * 1e3);
|
|
2441
2425
|
interval = Math.min(interval * backoffFactor, maxInterval);
|
|
2442
2426
|
}
|
|
2443
|
-
throw new
|
|
2427
|
+
throw new SourceTimeoutError(sourceId, timeout, lastStatus);
|
|
2428
|
+
}
|
|
2429
|
+
async waitForSources(notebookId, sourceIds, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
|
|
2430
|
+
return Promise.all(
|
|
2431
|
+
sourceIds.map(
|
|
2432
|
+
(sourceId) => this.waitUntilReady(
|
|
2433
|
+
notebookId,
|
|
2434
|
+
sourceId,
|
|
2435
|
+
timeout,
|
|
2436
|
+
initialInterval,
|
|
2437
|
+
maxInterval,
|
|
2438
|
+
backoffFactor
|
|
2439
|
+
)
|
|
2440
|
+
)
|
|
2441
|
+
);
|
|
2444
2442
|
}
|
|
2445
2443
|
};
|
|
2446
2444
|
function extractSourceId(result) {
|
|
@@ -2474,18 +2472,169 @@ function extractAllText(data, maxDepth = 100) {
|
|
|
2474
2472
|
function sleep2(ms) {
|
|
2475
2473
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2476
2474
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2475
|
+
var DEFAULT_SESSION_FILE = path.join(os.homedir(), ".notebooklm", "session.json");
|
|
2476
|
+
function loadCookiesFromFile(filePath) {
|
|
2477
|
+
let raw;
|
|
2478
|
+
try {
|
|
2479
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
2480
|
+
} catch {
|
|
2481
|
+
throw new AuthError(`Session file not found: ${filePath}
|
|
2482
|
+
Run: npx notebooklm-sdk login`);
|
|
2483
|
+
}
|
|
2484
|
+
return extractCookiesFromStorageState(JSON.parse(raw));
|
|
2485
|
+
}
|
|
2486
|
+
function loadCookiesFromObject(storageState) {
|
|
2487
|
+
return extractCookiesFromStorageState(storageState);
|
|
2488
|
+
}
|
|
2489
|
+
function buildGoogleCookieHeader(storageState) {
|
|
2490
|
+
const map = {};
|
|
2491
|
+
for (const c of storageState.cookies ?? []) {
|
|
2492
|
+
if (c.domain === ".google.com" && c.name && c.value) {
|
|
2493
|
+
map[c.name] = map[c.name] ?? c.value;
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return buildCookieHeader(map);
|
|
2497
|
+
}
|
|
2498
|
+
function loadCookiesFromMap(map) {
|
|
2499
|
+
return { ...map };
|
|
2500
|
+
}
|
|
2501
|
+
function loadCookiesFromString(cookieStr) {
|
|
2502
|
+
const map = {};
|
|
2503
|
+
for (const part of cookieStr.split(/;\s*/)) {
|
|
2504
|
+
const idx = part.indexOf("=");
|
|
2505
|
+
if (idx > 0) {
|
|
2506
|
+
const name = part.slice(0, idx).trim();
|
|
2507
|
+
const value = part.slice(idx + 1).trim();
|
|
2508
|
+
if (name) map[name] = value;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
return map;
|
|
2512
|
+
}
|
|
2513
|
+
function extractCookiesFromStorageState(storageState) {
|
|
2514
|
+
const cookies = {};
|
|
2515
|
+
for (const cookie of storageState.cookies ?? []) {
|
|
2516
|
+
const { domain, name, value } = cookie;
|
|
2517
|
+
if (!isAllowedDomain(domain) || !name) continue;
|
|
2518
|
+
const isBase = domain === ".google.com";
|
|
2519
|
+
if (!(name in cookies) || isBase) {
|
|
2520
|
+
cookies[name] = value;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (!cookies["SID"]) {
|
|
2524
|
+
throw new AuthError(
|
|
2525
|
+
"Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
|
|
2526
|
+
);
|
|
2527
|
+
}
|
|
2528
|
+
return cookies;
|
|
2529
|
+
}
|
|
2530
|
+
function isAllowedDomain(domain) {
|
|
2531
|
+
if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
|
|
2532
|
+
return true;
|
|
2533
|
+
}
|
|
2534
|
+
if (domain.startsWith(".google.")) {
|
|
2535
|
+
return true;
|
|
2536
|
+
}
|
|
2537
|
+
return false;
|
|
2538
|
+
}
|
|
2539
|
+
function buildCookieHeader(cookies) {
|
|
2540
|
+
return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
|
|
2541
|
+
}
|
|
2542
|
+
var NOTEBOOKLM_URL = "https://notebooklm.google.com/";
|
|
2543
|
+
async function fetchTokens(cookies) {
|
|
2544
|
+
const cookieHeader = buildCookieHeader(cookies);
|
|
2545
|
+
const response = await fetch(NOTEBOOKLM_URL, {
|
|
2546
|
+
headers: { Cookie: cookieHeader },
|
|
2547
|
+
redirect: "follow"
|
|
2548
|
+
});
|
|
2549
|
+
if (!response.ok) {
|
|
2550
|
+
throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
|
|
2551
|
+
}
|
|
2552
|
+
const finalUrl = response.url;
|
|
2553
|
+
if (isGoogleAuthRedirect(finalUrl)) {
|
|
2554
|
+
throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
|
|
2555
|
+
}
|
|
2556
|
+
const html = await response.text();
|
|
2557
|
+
const csrfToken = extractCsrfToken(html, finalUrl);
|
|
2558
|
+
const sessionId = extractSessionId(html, finalUrl);
|
|
2559
|
+
return { csrfToken, sessionId };
|
|
2560
|
+
}
|
|
2561
|
+
async function refreshAuthTokens(auth) {
|
|
2562
|
+
const { csrfToken, sessionId } = await fetchTokens(auth.cookies);
|
|
2563
|
+
auth.csrfToken = csrfToken;
|
|
2564
|
+
auth.sessionId = sessionId;
|
|
2565
|
+
return auth;
|
|
2566
|
+
}
|
|
2567
|
+
function extractCsrfToken(html, finalUrl) {
|
|
2568
|
+
const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
|
|
2569
|
+
if (!match?.[1]) {
|
|
2570
|
+
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
2571
|
+
throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
|
|
2572
|
+
}
|
|
2573
|
+
throw new AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
|
|
2574
|
+
}
|
|
2575
|
+
return match[1];
|
|
2576
|
+
}
|
|
2577
|
+
function extractSessionId(html, finalUrl) {
|
|
2578
|
+
const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
|
|
2579
|
+
if (!match?.[1]) {
|
|
2580
|
+
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
2581
|
+
throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
|
|
2582
|
+
}
|
|
2583
|
+
throw new AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
|
|
2584
|
+
}
|
|
2585
|
+
return match[1];
|
|
2586
|
+
}
|
|
2587
|
+
function isGoogleAuthRedirect(url) {
|
|
2588
|
+
return url.includes("accounts.google.com") || url.includes("signin");
|
|
2589
|
+
}
|
|
2590
|
+
async function connect(opts = {}) {
|
|
2591
|
+
let cookieMap;
|
|
2592
|
+
let googleCookieHeader = null;
|
|
2593
|
+
if (opts.cookies) {
|
|
2594
|
+
cookieMap = loadCookiesFromString(opts.cookies);
|
|
2595
|
+
} else if (opts.cookiesFile) {
|
|
2596
|
+
cookieMap = loadCookiesFromFile(opts.cookiesFile);
|
|
2597
|
+
} else if (opts.cookiesObject) {
|
|
2598
|
+
if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
|
|
2599
|
+
const storageState = opts.cookiesObject;
|
|
2600
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
2601
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
2602
|
+
} else {
|
|
2603
|
+
cookieMap = loadCookiesFromMap(opts.cookiesObject);
|
|
2604
|
+
}
|
|
2605
|
+
} else {
|
|
2606
|
+
const envCookies = process.env["NOTEBOOKLM_COOKIES"];
|
|
2607
|
+
const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
|
|
2608
|
+
if (envFile) {
|
|
2609
|
+
cookieMap = loadCookiesFromFile(envFile);
|
|
2610
|
+
} else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
|
|
2611
|
+
const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
|
|
2612
|
+
const storageState = JSON.parse(raw);
|
|
2613
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
2614
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
2615
|
+
} else if (fs.existsSync("storage_state.json")) {
|
|
2616
|
+
const raw = fs.readFileSync("storage_state.json", "utf-8");
|
|
2617
|
+
const storageState = JSON.parse(raw);
|
|
2618
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
2619
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
2620
|
+
} else if (envCookies) {
|
|
2621
|
+
cookieMap = loadCookiesFromString(envCookies);
|
|
2622
|
+
} else {
|
|
2623
|
+
throw new AuthError("No session found. Run: npx notebooklm-sdk login");
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
const { csrfToken, sessionId } = await fetchTokens(cookieMap);
|
|
2627
|
+
const cookieHeader = buildCookieHeader(cookieMap);
|
|
2628
|
+
return {
|
|
2629
|
+
cookies: cookieMap,
|
|
2630
|
+
csrfToken,
|
|
2631
|
+
sessionId,
|
|
2632
|
+
cookieHeader,
|
|
2633
|
+
googleCookieHeader: googleCookieHeader ?? cookieHeader
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2486
2636
|
|
|
2487
2637
|
// src/rpc/decoder.ts
|
|
2488
|
-
init_errors();
|
|
2489
2638
|
function stripAntiXSSI(response) {
|
|
2490
2639
|
if (response.startsWith(")]}'")) {
|
|
2491
2640
|
const match = /\)\]\}'\r?\n/.exec(response);
|
|
@@ -2529,7 +2678,7 @@ function parseChunkedResponse(response) {
|
|
|
2529
2678
|
if (skippedCount > 0 && lines.length > 0) {
|
|
2530
2679
|
const errorRate = skippedCount / lines.length;
|
|
2531
2680
|
if (errorRate > 0.1) {
|
|
2532
|
-
throw new
|
|
2681
|
+
throw new RPCError(
|
|
2533
2682
|
`Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
|
|
2534
2683
|
{ rawResponse: response.slice(0, 500) }
|
|
2535
2684
|
);
|
|
@@ -2577,13 +2726,13 @@ function extractRPCResult(chunks, rpcId) {
|
|
|
2577
2726
|
} else if (typeof code === "string") {
|
|
2578
2727
|
msg = code;
|
|
2579
2728
|
}
|
|
2580
|
-
throw new
|
|
2729
|
+
throw new RPCError(msg, { methodId: rpcId, rpcCode: code });
|
|
2581
2730
|
}
|
|
2582
2731
|
if (item[0] === "wrb.fr" && item[1] === rpcId) {
|
|
2583
2732
|
const resultData = item[2];
|
|
2584
2733
|
if (resultData === null && item.length > 5 && item[5] != null) {
|
|
2585
2734
|
if (containsUserDisplayableError(item[5])) {
|
|
2586
|
-
throw new
|
|
2735
|
+
throw new RateLimitError(
|
|
2587
2736
|
"API rate limit or quota exceeded. Please wait before retrying.",
|
|
2588
2737
|
{ methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
|
|
2589
2738
|
);
|
|
@@ -2617,8 +2766,8 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
|
|
|
2617
2766
|
try {
|
|
2618
2767
|
result = extractRPCResult(chunks, rpcId);
|
|
2619
2768
|
} catch (e) {
|
|
2620
|
-
if (e instanceof
|
|
2621
|
-
throw new
|
|
2769
|
+
if (e instanceof RPCError && e.foundIds.length === 0) {
|
|
2770
|
+
throw new RPCError(e.message, {
|
|
2622
2771
|
methodId: e.methodId,
|
|
2623
2772
|
rpcCode: e.rpcCode,
|
|
2624
2773
|
foundIds,
|
|
@@ -2629,12 +2778,12 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
|
|
|
2629
2778
|
}
|
|
2630
2779
|
if (result === void 0 && !allowNull) {
|
|
2631
2780
|
if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
|
|
2632
|
-
throw new
|
|
2781
|
+
throw new RPCError(
|
|
2633
2782
|
`No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
|
|
2634
2783
|
{ methodId: rpcId, foundIds, rawResponse: responsePreview }
|
|
2635
2784
|
);
|
|
2636
2785
|
}
|
|
2637
|
-
throw new
|
|
2786
|
+
throw new RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
|
|
2638
2787
|
methodId: rpcId,
|
|
2639
2788
|
foundIds,
|
|
2640
2789
|
rawResponse: responsePreview
|
|
@@ -2669,11 +2818,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
2669
2818
|
var RPCCore = class {
|
|
2670
2819
|
auth;
|
|
2671
2820
|
timeoutMs;
|
|
2672
|
-
|
|
2821
|
+
refreshAuth;
|
|
2822
|
+
constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS, refreshAuth) {
|
|
2673
2823
|
this.auth = auth;
|
|
2674
2824
|
this.timeoutMs = timeoutMs;
|
|
2825
|
+
this.refreshAuth = refreshAuth;
|
|
2675
2826
|
}
|
|
2676
|
-
async call(methodId, params, opts = {}) {
|
|
2827
|
+
async call(methodId, params, opts = {}, retried = false) {
|
|
2677
2828
|
const sourcePath = opts.sourcePath ?? "/";
|
|
2678
2829
|
const allowNull = opts.allowNull ?? false;
|
|
2679
2830
|
const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
|
|
@@ -2697,12 +2848,12 @@ var RPCCore = class {
|
|
|
2697
2848
|
} catch (e) {
|
|
2698
2849
|
clearTimeout(timer);
|
|
2699
2850
|
if (e instanceof Error && e.name === "AbortError") {
|
|
2700
|
-
throw new
|
|
2851
|
+
throw new RPCTimeoutError(`Request timed out calling ${methodId}`, {
|
|
2701
2852
|
methodId,
|
|
2702
2853
|
originalError: e
|
|
2703
2854
|
});
|
|
2704
2855
|
}
|
|
2705
|
-
throw new
|
|
2856
|
+
throw new NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
|
|
2706
2857
|
methodId,
|
|
2707
2858
|
originalError: e instanceof Error ? e : void 0
|
|
2708
2859
|
});
|
|
@@ -2714,29 +2865,33 @@ var RPCCore = class {
|
|
|
2714
2865
|
if (status === 429) {
|
|
2715
2866
|
const retryAfterHeader = response.headers.get("retry-after");
|
|
2716
2867
|
const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
|
|
2717
|
-
throw new
|
|
2868
|
+
throw new RateLimitError(`API rate limit exceeded calling ${methodId}`, {
|
|
2718
2869
|
methodId,
|
|
2719
2870
|
retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
|
|
2720
2871
|
});
|
|
2721
2872
|
}
|
|
2722
2873
|
if (status === 401 || status === 403) {
|
|
2723
|
-
|
|
2874
|
+
if (!retried && this.refreshAuth) {
|
|
2875
|
+
await this.refreshAuth();
|
|
2876
|
+
return this.call(methodId, params, opts, true);
|
|
2877
|
+
}
|
|
2878
|
+
throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
|
|
2724
2879
|
methodId
|
|
2725
2880
|
});
|
|
2726
2881
|
}
|
|
2727
2882
|
if (status >= 500) {
|
|
2728
|
-
throw new
|
|
2883
|
+
throw new ServerError(`Server error ${status} calling ${methodId}`, {
|
|
2729
2884
|
methodId,
|
|
2730
2885
|
statusCode: status
|
|
2731
2886
|
});
|
|
2732
2887
|
}
|
|
2733
2888
|
if (status >= 400) {
|
|
2734
|
-
throw new
|
|
2889
|
+
throw new ClientError(`Client error ${status} calling ${methodId}`, {
|
|
2735
2890
|
methodId,
|
|
2736
2891
|
statusCode: status
|
|
2737
2892
|
});
|
|
2738
2893
|
}
|
|
2739
|
-
throw new
|
|
2894
|
+
throw new RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
|
|
2740
2895
|
}
|
|
2741
2896
|
const text = await response.text();
|
|
2742
2897
|
return decodeResponse(text, methodId, allowNull);
|
|
@@ -2772,12 +2927,13 @@ var RPCCore = class {
|
|
|
2772
2927
|
var NotebookLMClient = class _NotebookLMClient {
|
|
2773
2928
|
constructor(auth, opts = {}) {
|
|
2774
2929
|
this.auth = auth;
|
|
2775
|
-
const
|
|
2930
|
+
const refreshAuth = this.refreshTokens.bind(this);
|
|
2931
|
+
const rpc = new RPCCore(auth, opts.timeoutMs, refreshAuth);
|
|
2776
2932
|
this.notebooks = new NotebooksAPI(rpc);
|
|
2777
2933
|
this.sources = new SourcesAPI(rpc, auth);
|
|
2778
2934
|
this.notes = new NotesAPI(rpc);
|
|
2779
2935
|
this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
|
|
2780
|
-
this.chat = new ChatAPI(rpc, auth);
|
|
2936
|
+
this.chat = new ChatAPI(rpc, auth, refreshAuth);
|
|
2781
2937
|
this.research = new ResearchAPI(rpc);
|
|
2782
2938
|
this.settings = new SettingsAPI(rpc);
|
|
2783
2939
|
this.sharing = new SharingAPI(rpc);
|
|
@@ -2790,6 +2946,7 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2790
2946
|
research;
|
|
2791
2947
|
settings;
|
|
2792
2948
|
sharing;
|
|
2949
|
+
refreshPromise = null;
|
|
2793
2950
|
/**
|
|
2794
2951
|
* Connect to NotebookLM using cookies.
|
|
2795
2952
|
* Fetches CSRF and session tokens from the NotebookLM homepage.
|
|
@@ -2802,25 +2959,47 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2802
2959
|
* Refresh CSRF and session tokens (e.g. if they expire mid-session).
|
|
2803
2960
|
*/
|
|
2804
2961
|
async refreshTokens() {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2962
|
+
if (!this.refreshPromise) {
|
|
2963
|
+
this.refreshPromise = refreshAuthTokens(this.auth).then(() => void 0).finally(() => {
|
|
2964
|
+
this.refreshPromise = null;
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
await this.refreshPromise;
|
|
2809
2968
|
}
|
|
2810
2969
|
};
|
|
2811
2970
|
|
|
2812
2971
|
// src/index.ts
|
|
2813
2972
|
init_enums();
|
|
2814
|
-
init_errors();
|
|
2815
2973
|
|
|
2974
|
+
exports.ArtifactDownloadError = ArtifactDownloadError;
|
|
2975
|
+
exports.ArtifactError = ArtifactError;
|
|
2976
|
+
exports.ArtifactNotFoundError = ArtifactNotFoundError;
|
|
2977
|
+
exports.ArtifactNotReadyError = ArtifactNotReadyError;
|
|
2978
|
+
exports.ArtifactParseError = ArtifactParseError;
|
|
2816
2979
|
exports.ArtifactsAPI = ArtifactsAPI;
|
|
2980
|
+
exports.AuthError = AuthError;
|
|
2817
2981
|
exports.ChatAPI = ChatAPI;
|
|
2982
|
+
exports.ChatError = ChatError;
|
|
2983
|
+
exports.ClientError = ClientError;
|
|
2984
|
+
exports.NetworkError = NetworkError;
|
|
2985
|
+
exports.NotebookError = NotebookError;
|
|
2818
2986
|
exports.NotebookLMClient = NotebookLMClient;
|
|
2987
|
+
exports.NotebookLMError = NotebookLMError;
|
|
2988
|
+
exports.NotebookNotFoundError = NotebookNotFoundError;
|
|
2819
2989
|
exports.NotebooksAPI = NotebooksAPI;
|
|
2820
2990
|
exports.NotesAPI = NotesAPI;
|
|
2991
|
+
exports.RPCError = RPCError;
|
|
2992
|
+
exports.RPCTimeoutError = RPCTimeoutError;
|
|
2993
|
+
exports.RateLimitError = RateLimitError;
|
|
2821
2994
|
exports.ResearchAPI = ResearchAPI;
|
|
2995
|
+
exports.ServerError = ServerError;
|
|
2822
2996
|
exports.SettingsAPI = SettingsAPI;
|
|
2823
2997
|
exports.SharingAPI = SharingAPI;
|
|
2998
|
+
exports.SourceAddError = SourceAddError;
|
|
2999
|
+
exports.SourceError = SourceError;
|
|
3000
|
+
exports.SourceNotFoundError = SourceNotFoundError;
|
|
3001
|
+
exports.SourceProcessingError = SourceProcessingError;
|
|
3002
|
+
exports.SourceTimeoutError = SourceTimeoutError;
|
|
2824
3003
|
exports.SourcesAPI = SourcesAPI;
|
|
2825
3004
|
exports.connect = connect;
|
|
2826
3005
|
//# sourceMappingURL=index.cjs.map
|