notebooklm-sdk 0.3.2 → 0.3.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/README.md +22 -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 +555 -400
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -7
- package/dist/index.d.ts +27 -7
- package/dist/index.js +507 -373
- 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();
|
|
@@ -1124,30 +944,49 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1124
944
|
const deadline = Date.now() + timeout * 1e3;
|
|
1125
945
|
while (Date.now() < deadline) {
|
|
1126
946
|
const artifact = await this.get(notebookId, artifactId);
|
|
1127
|
-
if (artifact?.status === "completed")
|
|
947
|
+
if (artifact?.status === "completed") {
|
|
948
|
+
if (artifact.kind === "audio" && !artifact.audioUrl || artifact.kind === "video" && !artifact.videoUrl) {
|
|
949
|
+
await sleep(pollInterval * 1e3);
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
return artifact;
|
|
953
|
+
}
|
|
1128
954
|
if (artifact?.status === "failed") {
|
|
1129
|
-
throw new
|
|
955
|
+
throw new ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
|
|
1130
956
|
}
|
|
1131
957
|
await sleep(pollInterval * 1e3);
|
|
1132
958
|
}
|
|
1133
|
-
throw new
|
|
959
|
+
throw new ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
|
|
960
|
+
}
|
|
961
|
+
/** Get the current status of a generated artifact without waiting. */
|
|
962
|
+
async pollStatus(notebookId, artifactId) {
|
|
963
|
+
const rawList = await this._listRaw(notebookId);
|
|
964
|
+
for (const item of rawList) {
|
|
965
|
+
if (!Array.isArray(item) || item[0] !== artifactId) continue;
|
|
966
|
+
const statusCode = typeof item[4] === "number" ? item[4] : null;
|
|
967
|
+
return {
|
|
968
|
+
artifactId,
|
|
969
|
+
status: statusCode != null ? artifactStatusFromCode(statusCode) : "pending"
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
return { artifactId, status: "pending" };
|
|
1134
973
|
}
|
|
1135
974
|
/** Download audio content as a Buffer. */
|
|
1136
975
|
async downloadAudio(notebookId, artifactId) {
|
|
1137
976
|
const artifact = await this.get(notebookId, artifactId);
|
|
1138
977
|
if (!artifact || artifact.status !== "completed") {
|
|
1139
|
-
throw new
|
|
978
|
+
throw new ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
|
|
1140
979
|
}
|
|
1141
|
-
if (!artifact.audioUrl) throw new
|
|
980
|
+
if (!artifact.audioUrl) throw new ArtifactNotReadyError("audio", { artifactId });
|
|
1142
981
|
return this._fetchMediaWithCookies(artifact.audioUrl);
|
|
1143
982
|
}
|
|
1144
983
|
/** Download video content as a Buffer. */
|
|
1145
984
|
async downloadVideo(notebookId, artifactId) {
|
|
1146
985
|
const artifact = await this.get(notebookId, artifactId);
|
|
1147
986
|
if (!artifact || artifact.status !== "completed") {
|
|
1148
|
-
throw new
|
|
987
|
+
throw new ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
|
|
1149
988
|
}
|
|
1150
|
-
if (!artifact.videoUrl) throw new
|
|
989
|
+
if (!artifact.videoUrl) throw new ArtifactNotReadyError("video", { artifactId });
|
|
1151
990
|
return this._fetchMediaWithCookies(artifact.videoUrl);
|
|
1152
991
|
}
|
|
1153
992
|
/** Get markdown content for a completed report artifact. */
|
|
@@ -1176,12 +1015,12 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1176
1015
|
const raw = rawList.find(
|
|
1177
1016
|
(a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.SLIDE_DECK
|
|
1178
1017
|
);
|
|
1179
|
-
if (!raw) throw new
|
|
1018
|
+
if (!raw) throw new ArtifactNotReadyError("slide_deck", { artifactId });
|
|
1180
1019
|
const metadata = raw[16];
|
|
1181
|
-
if (!Array.isArray(metadata)) throw new
|
|
1020
|
+
if (!Array.isArray(metadata)) throw new ArtifactNotReadyError("slide_deck", { artifactId });
|
|
1182
1021
|
const url = format === "pptx" ? metadata[4] : metadata[3];
|
|
1183
1022
|
if (typeof url !== "string" || !url.startsWith("http")) {
|
|
1184
|
-
throw new
|
|
1023
|
+
throw new ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
|
|
1185
1024
|
}
|
|
1186
1025
|
return this._fetchMediaWithCookies(url);
|
|
1187
1026
|
}
|
|
@@ -1191,7 +1030,7 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1191
1030
|
const raw = rawList.find(
|
|
1192
1031
|
(a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.INFOGRAPHIC
|
|
1193
1032
|
);
|
|
1194
|
-
if (!raw) throw new
|
|
1033
|
+
if (!raw) throw new ArtifactNotReadyError("infographic", { artifactId });
|
|
1195
1034
|
let url = null;
|
|
1196
1035
|
for (let i = raw.length - 1; i >= 0; i--) {
|
|
1197
1036
|
const item = raw[i];
|
|
@@ -1200,7 +1039,7 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1200
1039
|
break;
|
|
1201
1040
|
}
|
|
1202
1041
|
}
|
|
1203
|
-
if (!url) throw new
|
|
1042
|
+
if (!url) throw new ArtifactNotReadyError("infographic", { artifactId });
|
|
1204
1043
|
return this._fetchMediaWithCookies(url);
|
|
1205
1044
|
}
|
|
1206
1045
|
/** Get AI-suggested report formats based on notebook content. */
|
|
@@ -1382,14 +1221,14 @@ function isTrustedDomain(url) {
|
|
|
1382
1221
|
|
|
1383
1222
|
// src/api/chat.ts
|
|
1384
1223
|
init_enums();
|
|
1385
|
-
init_errors();
|
|
1386
1224
|
var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
|
|
1387
1225
|
var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
|
|
1388
1226
|
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
1227
|
var ChatAPI = class {
|
|
1390
|
-
constructor(rpc, auth) {
|
|
1228
|
+
constructor(rpc, auth, refreshAuth) {
|
|
1391
1229
|
this.rpc = rpc;
|
|
1392
1230
|
this.auth = auth;
|
|
1231
|
+
this.refreshAuth = refreshAuth;
|
|
1393
1232
|
}
|
|
1394
1233
|
conversationCache = /* @__PURE__ */ new Map();
|
|
1395
1234
|
reqid = Math.floor(Math.random() * 9e5) + 1e5;
|
|
@@ -1419,20 +1258,7 @@ var ChatAPI = class {
|
|
|
1419
1258
|
const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
|
|
1420
1259
|
if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
|
|
1421
1260
|
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}`);
|
|
1261
|
+
const response = await this._postChatRequest(`${QUERY_URL}?${urlParams.toString()}`, body);
|
|
1436
1262
|
const text = await response.text();
|
|
1437
1263
|
const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
|
|
1438
1264
|
const finalConvId = serverConvId ?? conversationId;
|
|
@@ -1492,12 +1318,47 @@ var ChatAPI = class {
|
|
|
1492
1318
|
}
|
|
1493
1319
|
return null;
|
|
1494
1320
|
}
|
|
1321
|
+
async getHistory(notebookId, limit = 100, conversationId) {
|
|
1322
|
+
const convId = conversationId ?? await this.getLastConversationId(notebookId);
|
|
1323
|
+
if (!convId) return [];
|
|
1324
|
+
const params = [[], null, null, convId, limit];
|
|
1325
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_CONVERSATION_TURNS, params, {
|
|
1326
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1327
|
+
allowNull: true
|
|
1328
|
+
});
|
|
1329
|
+
if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
|
|
1330
|
+
const rawTurns = [...result[0]].reverse();
|
|
1331
|
+
const history = [];
|
|
1332
|
+
let i = 0;
|
|
1333
|
+
while (i < rawTurns.length) {
|
|
1334
|
+
const turn = rawTurns[i];
|
|
1335
|
+
if (!Array.isArray(turn) || turn.length < 3) {
|
|
1336
|
+
i++;
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
if (turn[2] === 1 && turn.length > 3) {
|
|
1340
|
+
const query = typeof turn[3] === "string" ? turn[3] : "";
|
|
1341
|
+
let answer = "";
|
|
1342
|
+
const next = rawTurns[i + 1];
|
|
1343
|
+
if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
|
|
1344
|
+
try {
|
|
1345
|
+
answer = String(next[4][0][0] ?? "");
|
|
1346
|
+
} catch {
|
|
1347
|
+
}
|
|
1348
|
+
i++;
|
|
1349
|
+
}
|
|
1350
|
+
history.push([query, answer]);
|
|
1351
|
+
}
|
|
1352
|
+
i++;
|
|
1353
|
+
}
|
|
1354
|
+
return history;
|
|
1355
|
+
}
|
|
1495
1356
|
/**
|
|
1496
1357
|
* Low-level chat configuration. Set goal, response length, and optional
|
|
1497
1358
|
* custom instructions directly. Persists on the server per notebook.
|
|
1498
1359
|
* Use `setMode()` for preset combinations instead.
|
|
1499
1360
|
*/
|
|
1500
|
-
async configure(notebookId, goal, length, customPrompt) {
|
|
1361
|
+
async configure(notebookId, goal = exports.ChatGoal.DEFAULT, length = exports.ChatResponseLength.DEFAULT, customPrompt) {
|
|
1501
1362
|
if (goal === exports.ChatGoal.CUSTOM && !customPrompt) {
|
|
1502
1363
|
throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
|
|
1503
1364
|
}
|
|
@@ -1529,6 +1390,14 @@ var ChatAPI = class {
|
|
|
1529
1390
|
this.conversationCache.clear();
|
|
1530
1391
|
}
|
|
1531
1392
|
}
|
|
1393
|
+
getCachedTurns(conversationId) {
|
|
1394
|
+
const turns = this.conversationCache.get(conversationId) ?? [];
|
|
1395
|
+
return turns.map((turn) => ({
|
|
1396
|
+
query: turn.query,
|
|
1397
|
+
answer: turn.answer,
|
|
1398
|
+
turnNumber: turn.turnNumber
|
|
1399
|
+
}));
|
|
1400
|
+
}
|
|
1532
1401
|
_buildHistory(conversationId) {
|
|
1533
1402
|
const turns = this.conversationCache.get(conversationId) ?? [];
|
|
1534
1403
|
if (!turns.length) return null;
|
|
@@ -1539,6 +1408,27 @@ var ChatAPI = class {
|
|
|
1539
1408
|
}
|
|
1540
1409
|
return history;
|
|
1541
1410
|
}
|
|
1411
|
+
async _postChatRequest(url, body, retried = false) {
|
|
1412
|
+
let response;
|
|
1413
|
+
try {
|
|
1414
|
+
response = await fetch(url, {
|
|
1415
|
+
method: "POST",
|
|
1416
|
+
headers: {
|
|
1417
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
1418
|
+
Cookie: this.auth.cookieHeader
|
|
1419
|
+
},
|
|
1420
|
+
body
|
|
1421
|
+
});
|
|
1422
|
+
} catch (e) {
|
|
1423
|
+
throw new ChatError(`Chat request failed: ${String(e)}`);
|
|
1424
|
+
}
|
|
1425
|
+
if ((response.status === 401 || response.status === 403) && !retried && this.refreshAuth) {
|
|
1426
|
+
await this.refreshAuth();
|
|
1427
|
+
return this._postChatRequest(url, body, true);
|
|
1428
|
+
}
|
|
1429
|
+
if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
|
|
1430
|
+
return response;
|
|
1431
|
+
}
|
|
1542
1432
|
};
|
|
1543
1433
|
function parseStreamingResponse(rawText) {
|
|
1544
1434
|
let text = rawText;
|
|
@@ -1695,6 +1585,12 @@ var NotebooksAPI = class {
|
|
|
1695
1585
|
async removeFromRecent(notebookId) {
|
|
1696
1586
|
await this.rpc.call(exports.RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
|
|
1697
1587
|
}
|
|
1588
|
+
async getRaw(notebookId) {
|
|
1589
|
+
const params = [notebookId, null, [2], null, 0];
|
|
1590
|
+
return this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
|
|
1591
|
+
sourcePath: `/notebook/${notebookId}`
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1698
1594
|
async getDescription(notebookId) {
|
|
1699
1595
|
const params = [notebookId, [2]];
|
|
1700
1596
|
const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
|
|
@@ -1722,6 +1618,41 @@ var NotebooksAPI = class {
|
|
|
1722
1618
|
}
|
|
1723
1619
|
return { summary, suggestedTopics };
|
|
1724
1620
|
}
|
|
1621
|
+
async share(notebookId, publicAccess = true, artifactId) {
|
|
1622
|
+
const shareOptions = publicAccess ? [1] : [0];
|
|
1623
|
+
const params = artifactId ? [shareOptions, notebookId, artifactId] : [shareOptions, notebookId];
|
|
1624
|
+
await this.rpc.call(exports.RPCMethod.SHARE_ARTIFACT, params, {
|
|
1625
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1626
|
+
allowNull: true
|
|
1627
|
+
});
|
|
1628
|
+
return {
|
|
1629
|
+
public: publicAccess,
|
|
1630
|
+
url: publicAccess ? this.getShareUrl(notebookId, artifactId) : null,
|
|
1631
|
+
artifactId: artifactId ?? null
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
getShareUrl(notebookId, artifactId) {
|
|
1635
|
+
const baseUrl = `https://notebooklm.google.com/notebook/${notebookId}`;
|
|
1636
|
+
return artifactId ? `${baseUrl}?artifactId=${artifactId}` : baseUrl;
|
|
1637
|
+
}
|
|
1638
|
+
async getMetadata(notebookId) {
|
|
1639
|
+
const raw = await this.getRaw(notebookId);
|
|
1640
|
+
const notebookData = Array.isArray(raw) && raw.length > 0 && Array.isArray(raw[0]) ? raw[0] : [];
|
|
1641
|
+
const notebook = parseNotebook(notebookData);
|
|
1642
|
+
const sourcesRaw = Array.isArray(notebookData[1]) ? notebookData[1] : [];
|
|
1643
|
+
const sources = sourcesRaw.filter((source) => Array.isArray(source) && source.length > 0).map((source) => parseSource(source));
|
|
1644
|
+
return {
|
|
1645
|
+
id: notebook.id,
|
|
1646
|
+
title: notebook.title,
|
|
1647
|
+
createdAt: notebook.createdAt,
|
|
1648
|
+
isOwner: notebook.isOwner,
|
|
1649
|
+
sources: sources.map((source) => ({
|
|
1650
|
+
kind: source.kind,
|
|
1651
|
+
title: source.title,
|
|
1652
|
+
url: source.url
|
|
1653
|
+
}))
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1725
1656
|
};
|
|
1726
1657
|
|
|
1727
1658
|
// src/api/notes.ts
|
|
@@ -1734,6 +1665,10 @@ var NotesAPI = class {
|
|
|
1734
1665
|
const all = await this._fetchAll(notebookId);
|
|
1735
1666
|
return all.filter((n) => !this._isMindMap(n.content));
|
|
1736
1667
|
}
|
|
1668
|
+
async get(notebookId, noteId) {
|
|
1669
|
+
const all = await this._fetchAll(notebookId);
|
|
1670
|
+
return all.find((note) => note.id === noteId) ?? null;
|
|
1671
|
+
}
|
|
1737
1672
|
async listMindMaps(notebookId) {
|
|
1738
1673
|
const all = await this._fetchAll(notebookId);
|
|
1739
1674
|
return all.filter((n) => this._isMindMap(n.content));
|
|
@@ -1765,6 +1700,9 @@ var NotesAPI = class {
|
|
|
1765
1700
|
});
|
|
1766
1701
|
return true;
|
|
1767
1702
|
}
|
|
1703
|
+
async deleteMindMap(notebookId, mindMapId) {
|
|
1704
|
+
return this.delete(notebookId, mindMapId);
|
|
1705
|
+
}
|
|
1768
1706
|
async _fetchAll(notebookId) {
|
|
1769
1707
|
const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
|
|
1770
1708
|
sourcePath: `/notebook/${notebookId}`,
|
|
@@ -2141,7 +2079,6 @@ function parseShareStatus(data, notebookId) {
|
|
|
2141
2079
|
|
|
2142
2080
|
// src/api/sources.ts
|
|
2143
2081
|
init_enums();
|
|
2144
|
-
init_errors();
|
|
2145
2082
|
var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
|
|
2146
2083
|
var SourcesAPI = class {
|
|
2147
2084
|
constructor(rpc, auth) {
|
|
@@ -2424,6 +2361,29 @@ var SourcesAPI = class {
|
|
|
2424
2361
|
});
|
|
2425
2362
|
return true;
|
|
2426
2363
|
}
|
|
2364
|
+
async rename(notebookId, sourceId, newTitle) {
|
|
2365
|
+
const params = [null, [sourceId], [[[newTitle]]]];
|
|
2366
|
+
const result = await this.rpc.call(exports.RPCMethod.UPDATE_SOURCE, params, {
|
|
2367
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2368
|
+
allowNull: true
|
|
2369
|
+
});
|
|
2370
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
2371
|
+
try {
|
|
2372
|
+
const parsed = parseSource(result);
|
|
2373
|
+
return parsed.title ? parsed : { ...parsed, title: newTitle };
|
|
2374
|
+
} catch {
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
return {
|
|
2378
|
+
id: sourceId,
|
|
2379
|
+
title: newTitle,
|
|
2380
|
+
url: null,
|
|
2381
|
+
kind: "unknown",
|
|
2382
|
+
createdAt: null,
|
|
2383
|
+
status: "ready",
|
|
2384
|
+
_typeCode: null
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2427
2387
|
async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
|
|
2428
2388
|
const deadline = Date.now() + timeout * 1e3;
|
|
2429
2389
|
let interval = initialInterval;
|
|
@@ -2433,14 +2393,28 @@ var SourcesAPI = class {
|
|
|
2433
2393
|
if (source) {
|
|
2434
2394
|
if (source.status === "ready") return source;
|
|
2435
2395
|
if (source.status === "error") {
|
|
2436
|
-
throw new
|
|
2396
|
+
throw new SourceProcessingError(sourceId, 3);
|
|
2437
2397
|
}
|
|
2438
2398
|
lastStatus = source._typeCode ?? void 0;
|
|
2439
2399
|
}
|
|
2440
2400
|
await sleep2(interval * 1e3);
|
|
2441
2401
|
interval = Math.min(interval * backoffFactor, maxInterval);
|
|
2442
2402
|
}
|
|
2443
|
-
throw new
|
|
2403
|
+
throw new SourceTimeoutError(sourceId, timeout, lastStatus);
|
|
2404
|
+
}
|
|
2405
|
+
async waitForSources(notebookId, sourceIds, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
|
|
2406
|
+
return Promise.all(
|
|
2407
|
+
sourceIds.map(
|
|
2408
|
+
(sourceId) => this.waitUntilReady(
|
|
2409
|
+
notebookId,
|
|
2410
|
+
sourceId,
|
|
2411
|
+
timeout,
|
|
2412
|
+
initialInterval,
|
|
2413
|
+
maxInterval,
|
|
2414
|
+
backoffFactor
|
|
2415
|
+
)
|
|
2416
|
+
)
|
|
2417
|
+
);
|
|
2444
2418
|
}
|
|
2445
2419
|
};
|
|
2446
2420
|
function extractSourceId(result) {
|
|
@@ -2474,18 +2448,169 @@ function extractAllText(data, maxDepth = 100) {
|
|
|
2474
2448
|
function sleep2(ms) {
|
|
2475
2449
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2476
2450
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2451
|
+
var DEFAULT_SESSION_FILE = path.join(os.homedir(), ".notebooklm", "session.json");
|
|
2452
|
+
function loadCookiesFromFile(filePath) {
|
|
2453
|
+
let raw;
|
|
2454
|
+
try {
|
|
2455
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
2456
|
+
} catch {
|
|
2457
|
+
throw new AuthError(`Session file not found: ${filePath}
|
|
2458
|
+
Run: npx notebooklm-sdk login`);
|
|
2459
|
+
}
|
|
2460
|
+
return extractCookiesFromStorageState(JSON.parse(raw));
|
|
2461
|
+
}
|
|
2462
|
+
function loadCookiesFromObject(storageState) {
|
|
2463
|
+
return extractCookiesFromStorageState(storageState);
|
|
2464
|
+
}
|
|
2465
|
+
function buildGoogleCookieHeader(storageState) {
|
|
2466
|
+
const map = {};
|
|
2467
|
+
for (const c of storageState.cookies ?? []) {
|
|
2468
|
+
if (c.domain === ".google.com" && c.name && c.value) {
|
|
2469
|
+
map[c.name] = map[c.name] ?? c.value;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
return buildCookieHeader(map);
|
|
2473
|
+
}
|
|
2474
|
+
function loadCookiesFromMap(map) {
|
|
2475
|
+
return { ...map };
|
|
2476
|
+
}
|
|
2477
|
+
function loadCookiesFromString(cookieStr) {
|
|
2478
|
+
const map = {};
|
|
2479
|
+
for (const part of cookieStr.split(/;\s*/)) {
|
|
2480
|
+
const idx = part.indexOf("=");
|
|
2481
|
+
if (idx > 0) {
|
|
2482
|
+
const name = part.slice(0, idx).trim();
|
|
2483
|
+
const value = part.slice(idx + 1).trim();
|
|
2484
|
+
if (name) map[name] = value;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
return map;
|
|
2488
|
+
}
|
|
2489
|
+
function extractCookiesFromStorageState(storageState) {
|
|
2490
|
+
const cookies = {};
|
|
2491
|
+
for (const cookie of storageState.cookies ?? []) {
|
|
2492
|
+
const { domain, name, value } = cookie;
|
|
2493
|
+
if (!isAllowedDomain(domain) || !name) continue;
|
|
2494
|
+
const isBase = domain === ".google.com";
|
|
2495
|
+
if (!(name in cookies) || isBase) {
|
|
2496
|
+
cookies[name] = value;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
if (!cookies["SID"]) {
|
|
2500
|
+
throw new AuthError(
|
|
2501
|
+
"Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2504
|
+
return cookies;
|
|
2505
|
+
}
|
|
2506
|
+
function isAllowedDomain(domain) {
|
|
2507
|
+
if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
|
|
2508
|
+
return true;
|
|
2509
|
+
}
|
|
2510
|
+
if (domain.startsWith(".google.")) {
|
|
2511
|
+
return true;
|
|
2512
|
+
}
|
|
2513
|
+
return false;
|
|
2514
|
+
}
|
|
2515
|
+
function buildCookieHeader(cookies) {
|
|
2516
|
+
return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
|
|
2517
|
+
}
|
|
2518
|
+
var NOTEBOOKLM_URL = "https://notebooklm.google.com/";
|
|
2519
|
+
async function fetchTokens(cookies) {
|
|
2520
|
+
const cookieHeader = buildCookieHeader(cookies);
|
|
2521
|
+
const response = await fetch(NOTEBOOKLM_URL, {
|
|
2522
|
+
headers: { Cookie: cookieHeader },
|
|
2523
|
+
redirect: "follow"
|
|
2524
|
+
});
|
|
2525
|
+
if (!response.ok) {
|
|
2526
|
+
throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
|
|
2527
|
+
}
|
|
2528
|
+
const finalUrl = response.url;
|
|
2529
|
+
if (isGoogleAuthRedirect(finalUrl)) {
|
|
2530
|
+
throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
|
|
2531
|
+
}
|
|
2532
|
+
const html = await response.text();
|
|
2533
|
+
const csrfToken = extractCsrfToken(html, finalUrl);
|
|
2534
|
+
const sessionId = extractSessionId(html, finalUrl);
|
|
2535
|
+
return { csrfToken, sessionId };
|
|
2536
|
+
}
|
|
2537
|
+
async function refreshAuthTokens(auth) {
|
|
2538
|
+
const { csrfToken, sessionId } = await fetchTokens(auth.cookies);
|
|
2539
|
+
auth.csrfToken = csrfToken;
|
|
2540
|
+
auth.sessionId = sessionId;
|
|
2541
|
+
return auth;
|
|
2542
|
+
}
|
|
2543
|
+
function extractCsrfToken(html, finalUrl) {
|
|
2544
|
+
const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
|
|
2545
|
+
if (!match?.[1]) {
|
|
2546
|
+
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
2547
|
+
throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
|
|
2548
|
+
}
|
|
2549
|
+
throw new AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
|
|
2550
|
+
}
|
|
2551
|
+
return match[1];
|
|
2552
|
+
}
|
|
2553
|
+
function extractSessionId(html, finalUrl) {
|
|
2554
|
+
const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
|
|
2555
|
+
if (!match?.[1]) {
|
|
2556
|
+
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
2557
|
+
throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
|
|
2558
|
+
}
|
|
2559
|
+
throw new AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
|
|
2560
|
+
}
|
|
2561
|
+
return match[1];
|
|
2562
|
+
}
|
|
2563
|
+
function isGoogleAuthRedirect(url) {
|
|
2564
|
+
return url.includes("accounts.google.com") || url.includes("signin");
|
|
2565
|
+
}
|
|
2566
|
+
async function connect(opts = {}) {
|
|
2567
|
+
let cookieMap;
|
|
2568
|
+
let googleCookieHeader = null;
|
|
2569
|
+
if (opts.cookies) {
|
|
2570
|
+
cookieMap = loadCookiesFromString(opts.cookies);
|
|
2571
|
+
} else if (opts.cookiesFile) {
|
|
2572
|
+
cookieMap = loadCookiesFromFile(opts.cookiesFile);
|
|
2573
|
+
} else if (opts.cookiesObject) {
|
|
2574
|
+
if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
|
|
2575
|
+
const storageState = opts.cookiesObject;
|
|
2576
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
2577
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
2578
|
+
} else {
|
|
2579
|
+
cookieMap = loadCookiesFromMap(opts.cookiesObject);
|
|
2580
|
+
}
|
|
2581
|
+
} else {
|
|
2582
|
+
const envCookies = process.env["NOTEBOOKLM_COOKIES"];
|
|
2583
|
+
const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
|
|
2584
|
+
if (envFile) {
|
|
2585
|
+
cookieMap = loadCookiesFromFile(envFile);
|
|
2586
|
+
} else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
|
|
2587
|
+
const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
|
|
2588
|
+
const storageState = JSON.parse(raw);
|
|
2589
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
2590
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
2591
|
+
} else if (fs.existsSync("storage_state.json")) {
|
|
2592
|
+
const raw = fs.readFileSync("storage_state.json", "utf-8");
|
|
2593
|
+
const storageState = JSON.parse(raw);
|
|
2594
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
2595
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
2596
|
+
} else if (envCookies) {
|
|
2597
|
+
cookieMap = loadCookiesFromString(envCookies);
|
|
2598
|
+
} else {
|
|
2599
|
+
throw new AuthError("No session found. Run: npx notebooklm-sdk login");
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
const { csrfToken, sessionId } = await fetchTokens(cookieMap);
|
|
2603
|
+
const cookieHeader = buildCookieHeader(cookieMap);
|
|
2604
|
+
return {
|
|
2605
|
+
cookies: cookieMap,
|
|
2606
|
+
csrfToken,
|
|
2607
|
+
sessionId,
|
|
2608
|
+
cookieHeader,
|
|
2609
|
+
googleCookieHeader: googleCookieHeader ?? cookieHeader
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2486
2612
|
|
|
2487
2613
|
// src/rpc/decoder.ts
|
|
2488
|
-
init_errors();
|
|
2489
2614
|
function stripAntiXSSI(response) {
|
|
2490
2615
|
if (response.startsWith(")]}'")) {
|
|
2491
2616
|
const match = /\)\]\}'\r?\n/.exec(response);
|
|
@@ -2529,7 +2654,7 @@ function parseChunkedResponse(response) {
|
|
|
2529
2654
|
if (skippedCount > 0 && lines.length > 0) {
|
|
2530
2655
|
const errorRate = skippedCount / lines.length;
|
|
2531
2656
|
if (errorRate > 0.1) {
|
|
2532
|
-
throw new
|
|
2657
|
+
throw new RPCError(
|
|
2533
2658
|
`Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
|
|
2534
2659
|
{ rawResponse: response.slice(0, 500) }
|
|
2535
2660
|
);
|
|
@@ -2577,13 +2702,13 @@ function extractRPCResult(chunks, rpcId) {
|
|
|
2577
2702
|
} else if (typeof code === "string") {
|
|
2578
2703
|
msg = code;
|
|
2579
2704
|
}
|
|
2580
|
-
throw new
|
|
2705
|
+
throw new RPCError(msg, { methodId: rpcId, rpcCode: code });
|
|
2581
2706
|
}
|
|
2582
2707
|
if (item[0] === "wrb.fr" && item[1] === rpcId) {
|
|
2583
2708
|
const resultData = item[2];
|
|
2584
2709
|
if (resultData === null && item.length > 5 && item[5] != null) {
|
|
2585
2710
|
if (containsUserDisplayableError(item[5])) {
|
|
2586
|
-
throw new
|
|
2711
|
+
throw new RateLimitError(
|
|
2587
2712
|
"API rate limit or quota exceeded. Please wait before retrying.",
|
|
2588
2713
|
{ methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
|
|
2589
2714
|
);
|
|
@@ -2617,8 +2742,8 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
|
|
|
2617
2742
|
try {
|
|
2618
2743
|
result = extractRPCResult(chunks, rpcId);
|
|
2619
2744
|
} catch (e) {
|
|
2620
|
-
if (e instanceof
|
|
2621
|
-
throw new
|
|
2745
|
+
if (e instanceof RPCError && e.foundIds.length === 0) {
|
|
2746
|
+
throw new RPCError(e.message, {
|
|
2622
2747
|
methodId: e.methodId,
|
|
2623
2748
|
rpcCode: e.rpcCode,
|
|
2624
2749
|
foundIds,
|
|
@@ -2629,12 +2754,12 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
|
|
|
2629
2754
|
}
|
|
2630
2755
|
if (result === void 0 && !allowNull) {
|
|
2631
2756
|
if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
|
|
2632
|
-
throw new
|
|
2757
|
+
throw new RPCError(
|
|
2633
2758
|
`No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
|
|
2634
2759
|
{ methodId: rpcId, foundIds, rawResponse: responsePreview }
|
|
2635
2760
|
);
|
|
2636
2761
|
}
|
|
2637
|
-
throw new
|
|
2762
|
+
throw new RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
|
|
2638
2763
|
methodId: rpcId,
|
|
2639
2764
|
foundIds,
|
|
2640
2765
|
rawResponse: responsePreview
|
|
@@ -2669,11 +2794,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
2669
2794
|
var RPCCore = class {
|
|
2670
2795
|
auth;
|
|
2671
2796
|
timeoutMs;
|
|
2672
|
-
|
|
2797
|
+
refreshAuth;
|
|
2798
|
+
constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS, refreshAuth) {
|
|
2673
2799
|
this.auth = auth;
|
|
2674
2800
|
this.timeoutMs = timeoutMs;
|
|
2801
|
+
this.refreshAuth = refreshAuth;
|
|
2675
2802
|
}
|
|
2676
|
-
async call(methodId, params, opts = {}) {
|
|
2803
|
+
async call(methodId, params, opts = {}, retried = false) {
|
|
2677
2804
|
const sourcePath = opts.sourcePath ?? "/";
|
|
2678
2805
|
const allowNull = opts.allowNull ?? false;
|
|
2679
2806
|
const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
|
|
@@ -2697,12 +2824,12 @@ var RPCCore = class {
|
|
|
2697
2824
|
} catch (e) {
|
|
2698
2825
|
clearTimeout(timer);
|
|
2699
2826
|
if (e instanceof Error && e.name === "AbortError") {
|
|
2700
|
-
throw new
|
|
2827
|
+
throw new RPCTimeoutError(`Request timed out calling ${methodId}`, {
|
|
2701
2828
|
methodId,
|
|
2702
2829
|
originalError: e
|
|
2703
2830
|
});
|
|
2704
2831
|
}
|
|
2705
|
-
throw new
|
|
2832
|
+
throw new NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
|
|
2706
2833
|
methodId,
|
|
2707
2834
|
originalError: e instanceof Error ? e : void 0
|
|
2708
2835
|
});
|
|
@@ -2714,29 +2841,33 @@ var RPCCore = class {
|
|
|
2714
2841
|
if (status === 429) {
|
|
2715
2842
|
const retryAfterHeader = response.headers.get("retry-after");
|
|
2716
2843
|
const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
|
|
2717
|
-
throw new
|
|
2844
|
+
throw new RateLimitError(`API rate limit exceeded calling ${methodId}`, {
|
|
2718
2845
|
methodId,
|
|
2719
2846
|
retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
|
|
2720
2847
|
});
|
|
2721
2848
|
}
|
|
2722
2849
|
if (status === 401 || status === 403) {
|
|
2723
|
-
|
|
2850
|
+
if (!retried && this.refreshAuth) {
|
|
2851
|
+
await this.refreshAuth();
|
|
2852
|
+
return this.call(methodId, params, opts, true);
|
|
2853
|
+
}
|
|
2854
|
+
throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
|
|
2724
2855
|
methodId
|
|
2725
2856
|
});
|
|
2726
2857
|
}
|
|
2727
2858
|
if (status >= 500) {
|
|
2728
|
-
throw new
|
|
2859
|
+
throw new ServerError(`Server error ${status} calling ${methodId}`, {
|
|
2729
2860
|
methodId,
|
|
2730
2861
|
statusCode: status
|
|
2731
2862
|
});
|
|
2732
2863
|
}
|
|
2733
2864
|
if (status >= 400) {
|
|
2734
|
-
throw new
|
|
2865
|
+
throw new ClientError(`Client error ${status} calling ${methodId}`, {
|
|
2735
2866
|
methodId,
|
|
2736
2867
|
statusCode: status
|
|
2737
2868
|
});
|
|
2738
2869
|
}
|
|
2739
|
-
throw new
|
|
2870
|
+
throw new RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
|
|
2740
2871
|
}
|
|
2741
2872
|
const text = await response.text();
|
|
2742
2873
|
return decodeResponse(text, methodId, allowNull);
|
|
@@ -2772,12 +2903,13 @@ var RPCCore = class {
|
|
|
2772
2903
|
var NotebookLMClient = class _NotebookLMClient {
|
|
2773
2904
|
constructor(auth, opts = {}) {
|
|
2774
2905
|
this.auth = auth;
|
|
2775
|
-
const
|
|
2906
|
+
const refreshAuth = this.refreshTokens.bind(this);
|
|
2907
|
+
const rpc = new RPCCore(auth, opts.timeoutMs, refreshAuth);
|
|
2776
2908
|
this.notebooks = new NotebooksAPI(rpc);
|
|
2777
2909
|
this.sources = new SourcesAPI(rpc, auth);
|
|
2778
2910
|
this.notes = new NotesAPI(rpc);
|
|
2779
2911
|
this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
|
|
2780
|
-
this.chat = new ChatAPI(rpc, auth);
|
|
2912
|
+
this.chat = new ChatAPI(rpc, auth, refreshAuth);
|
|
2781
2913
|
this.research = new ResearchAPI(rpc);
|
|
2782
2914
|
this.settings = new SettingsAPI(rpc);
|
|
2783
2915
|
this.sharing = new SharingAPI(rpc);
|
|
@@ -2790,6 +2922,7 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2790
2922
|
research;
|
|
2791
2923
|
settings;
|
|
2792
2924
|
sharing;
|
|
2925
|
+
refreshPromise = null;
|
|
2793
2926
|
/**
|
|
2794
2927
|
* Connect to NotebookLM using cookies.
|
|
2795
2928
|
* Fetches CSRF and session tokens from the NotebookLM homepage.
|
|
@@ -2802,25 +2935,47 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2802
2935
|
* Refresh CSRF and session tokens (e.g. if they expire mid-session).
|
|
2803
2936
|
*/
|
|
2804
2937
|
async refreshTokens() {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2938
|
+
if (!this.refreshPromise) {
|
|
2939
|
+
this.refreshPromise = refreshAuthTokens(this.auth).then(() => void 0).finally(() => {
|
|
2940
|
+
this.refreshPromise = null;
|
|
2941
|
+
});
|
|
2942
|
+
}
|
|
2943
|
+
await this.refreshPromise;
|
|
2809
2944
|
}
|
|
2810
2945
|
};
|
|
2811
2946
|
|
|
2812
2947
|
// src/index.ts
|
|
2813
2948
|
init_enums();
|
|
2814
|
-
init_errors();
|
|
2815
2949
|
|
|
2950
|
+
exports.ArtifactDownloadError = ArtifactDownloadError;
|
|
2951
|
+
exports.ArtifactError = ArtifactError;
|
|
2952
|
+
exports.ArtifactNotFoundError = ArtifactNotFoundError;
|
|
2953
|
+
exports.ArtifactNotReadyError = ArtifactNotReadyError;
|
|
2954
|
+
exports.ArtifactParseError = ArtifactParseError;
|
|
2816
2955
|
exports.ArtifactsAPI = ArtifactsAPI;
|
|
2956
|
+
exports.AuthError = AuthError;
|
|
2817
2957
|
exports.ChatAPI = ChatAPI;
|
|
2958
|
+
exports.ChatError = ChatError;
|
|
2959
|
+
exports.ClientError = ClientError;
|
|
2960
|
+
exports.NetworkError = NetworkError;
|
|
2961
|
+
exports.NotebookError = NotebookError;
|
|
2818
2962
|
exports.NotebookLMClient = NotebookLMClient;
|
|
2963
|
+
exports.NotebookLMError = NotebookLMError;
|
|
2964
|
+
exports.NotebookNotFoundError = NotebookNotFoundError;
|
|
2819
2965
|
exports.NotebooksAPI = NotebooksAPI;
|
|
2820
2966
|
exports.NotesAPI = NotesAPI;
|
|
2967
|
+
exports.RPCError = RPCError;
|
|
2968
|
+
exports.RPCTimeoutError = RPCTimeoutError;
|
|
2969
|
+
exports.RateLimitError = RateLimitError;
|
|
2821
2970
|
exports.ResearchAPI = ResearchAPI;
|
|
2971
|
+
exports.ServerError = ServerError;
|
|
2822
2972
|
exports.SettingsAPI = SettingsAPI;
|
|
2823
2973
|
exports.SharingAPI = SharingAPI;
|
|
2974
|
+
exports.SourceAddError = SourceAddError;
|
|
2975
|
+
exports.SourceError = SourceError;
|
|
2976
|
+
exports.SourceNotFoundError = SourceNotFoundError;
|
|
2977
|
+
exports.SourceProcessingError = SourceProcessingError;
|
|
2978
|
+
exports.SourceTimeoutError = SourceTimeoutError;
|
|
2824
2979
|
exports.SourcesAPI = SourcesAPI;
|
|
2825
2980
|
exports.connect = connect;
|
|
2826
2981
|
//# sourceMappingURL=index.cjs.map
|