notebooklm-sdk 0.1.2 → 0.1.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/dist/index.js CHANGED
@@ -10,6 +10,257 @@ var __export = (target, all) => {
10
10
  __defProp(target, name, { get: all[name], enumerable: true });
11
11
  };
12
12
 
13
+ // src/types/enums.ts
14
+ var enums_exports = {};
15
+ __export(enums_exports, {
16
+ ArtifactStatusCode: () => ArtifactStatusCode,
17
+ ArtifactTypeCode: () => ArtifactTypeCode,
18
+ AudioFormat: () => AudioFormat,
19
+ AudioLength: () => AudioLength,
20
+ ExportType: () => ExportType,
21
+ InfographicDetail: () => InfographicDetail,
22
+ InfographicOrientation: () => InfographicOrientation,
23
+ InfographicStyle: () => InfographicStyle,
24
+ QuizDifficulty: () => QuizDifficulty,
25
+ QuizQuantity: () => QuizQuantity,
26
+ RPCMethod: () => RPCMethod,
27
+ ShareAccess: () => ShareAccess,
28
+ SharePermission: () => SharePermission,
29
+ ShareViewLevel: () => ShareViewLevel,
30
+ SlideDeckFormat: () => SlideDeckFormat,
31
+ SlideDeckLength: () => SlideDeckLength,
32
+ SourceStatusCode: () => SourceStatusCode,
33
+ VideoFormat: () => VideoFormat,
34
+ VideoStyle: () => VideoStyle,
35
+ artifactStatusFromCode: () => artifactStatusFromCode,
36
+ artifactTypeFromCode: () => artifactTypeFromCode,
37
+ sourceStatusFromCode: () => sourceStatusFromCode,
38
+ sourceTypeFromCode: () => sourceTypeFromCode
39
+ });
40
+ function sourceTypeFromCode(code) {
41
+ if (code == null) return "unknown";
42
+ return SOURCE_TYPE_MAP[code] ?? "unknown";
43
+ }
44
+ function artifactTypeFromCode(typeCode, variant) {
45
+ if (typeCode === 4) {
46
+ if (variant === 1) return "flashcards";
47
+ if (variant === 2) return "quiz";
48
+ return "unknown";
49
+ }
50
+ return ARTIFACT_TYPE_MAP[typeCode] ?? "unknown";
51
+ }
52
+ function artifactStatusFromCode(code) {
53
+ return ARTIFACT_STATUS_MAP[code] ?? "unknown";
54
+ }
55
+ function sourceStatusFromCode(code) {
56
+ return SOURCE_STATUS_MAP[code] ?? "unknown";
57
+ }
58
+ var RPCMethod, ArtifactTypeCode, ArtifactStatusCode, SourceStatusCode, AudioFormat, AudioLength, VideoFormat, VideoStyle, QuizQuantity, QuizDifficulty, InfographicOrientation, InfographicDetail, InfographicStyle, SlideDeckFormat, SlideDeckLength, ExportType, SOURCE_TYPE_MAP, ARTIFACT_TYPE_MAP, ARTIFACT_STATUS_MAP, SOURCE_STATUS_MAP, ShareAccess, ShareViewLevel, SharePermission;
59
+ var init_enums = __esm({
60
+ "src/types/enums.ts"() {
61
+ RPCMethod = {
62
+ // Notebook
63
+ LIST_NOTEBOOKS: "wXbhsf",
64
+ CREATE_NOTEBOOK: "CCqFvf",
65
+ GET_NOTEBOOK: "rLM1Ne",
66
+ RENAME_NOTEBOOK: "s0tc2d",
67
+ DELETE_NOTEBOOK: "WWINqb",
68
+ // Sources
69
+ ADD_SOURCE: "izAoDd",
70
+ ADD_SOURCE_FILE: "o4cbdc",
71
+ DELETE_SOURCE: "tGMBJ",
72
+ GET_SOURCE: "hizoJc",
73
+ REFRESH_SOURCE: "FLmJqe",
74
+ CHECK_SOURCE_FRESHNESS: "yR9Yof",
75
+ UPDATE_SOURCE: "b7Wfje",
76
+ // Summary
77
+ SUMMARIZE: "VfAZjd",
78
+ GET_SOURCE_GUIDE: "tr032e",
79
+ GET_SUGGESTED_REPORTS: "ciyUvf",
80
+ // Artifacts
81
+ CREATE_ARTIFACT: "R7cb6c",
82
+ LIST_ARTIFACTS: "gArtLc",
83
+ DELETE_ARTIFACT: "V5N4be",
84
+ RENAME_ARTIFACT: "rc3d8d",
85
+ EXPORT_ARTIFACT: "Krh3pd",
86
+ SHARE_ARTIFACT: "RGP97b",
87
+ GET_INTERACTIVE_HTML: "v9rmvd",
88
+ REVISE_SLIDE: "KmcKPe",
89
+ // Research
90
+ START_FAST_RESEARCH: "Ljjv0c",
91
+ START_DEEP_RESEARCH: "QA9ei",
92
+ POLL_RESEARCH: "e3bVqc",
93
+ IMPORT_RESEARCH: "LBwxtb",
94
+ // Notes / Mind Maps
95
+ GENERATE_MIND_MAP: "yyryJe",
96
+ CREATE_NOTE: "CYK0Xb",
97
+ GET_NOTES_AND_MIND_MAPS: "cFji9",
98
+ UPDATE_NOTE: "cYAfTb",
99
+ DELETE_NOTE: "AH0mwd",
100
+ // Conversation
101
+ GET_LAST_CONVERSATION_ID: "hPTbtc",
102
+ GET_CONVERSATION_TURNS: "khqZz",
103
+ // Sharing
104
+ SHARE_NOTEBOOK: "QDyure",
105
+ GET_SHARE_STATUS: "JFMDGd",
106
+ // Misc
107
+ REMOVE_RECENTLY_VIEWED: "fejl7e",
108
+ GET_USER_SETTINGS: "ZwVcOc",
109
+ SET_USER_SETTINGS: "hT54vc"
110
+ };
111
+ ArtifactTypeCode = {
112
+ AUDIO: 1,
113
+ REPORT: 2,
114
+ VIDEO: 3,
115
+ QUIZ: 4,
116
+ MIND_MAP: 5,
117
+ INFOGRAPHIC: 7,
118
+ SLIDE_DECK: 8,
119
+ DATA_TABLE: 9
120
+ };
121
+ ArtifactStatusCode = {
122
+ PROCESSING: 1,
123
+ PENDING: 2,
124
+ COMPLETED: 3,
125
+ FAILED: 4
126
+ };
127
+ SourceStatusCode = {
128
+ PROCESSING: 1,
129
+ READY: 2,
130
+ ERROR: 3,
131
+ PREPARING: 5
132
+ };
133
+ AudioFormat = {
134
+ DEEP_DIVE: 1,
135
+ BRIEF: 2,
136
+ CRITIQUE: 3,
137
+ DEBATE: 4
138
+ };
139
+ AudioLength = {
140
+ SHORT: 1,
141
+ DEFAULT: 2,
142
+ LONG: 3
143
+ };
144
+ VideoFormat = {
145
+ EXPLAINER: 1,
146
+ BRIEF: 2,
147
+ CINEMATIC: 3
148
+ };
149
+ VideoStyle = {
150
+ AUTO_SELECT: 1,
151
+ CUSTOM: 2,
152
+ CLASSIC: 3,
153
+ WHITEBOARD: 4,
154
+ KAWAII: 5,
155
+ ANIME: 6,
156
+ WATERCOLOR: 7,
157
+ RETRO_PRINT: 8,
158
+ HERITAGE: 9,
159
+ PAPER_CRAFT: 10
160
+ };
161
+ QuizQuantity = {
162
+ FEWER: 1,
163
+ STANDARD: 2,
164
+ MORE: 2
165
+ // API limitation: same as STANDARD
166
+ };
167
+ QuizDifficulty = {
168
+ EASY: 1,
169
+ MEDIUM: 2,
170
+ HARD: 3
171
+ };
172
+ InfographicOrientation = {
173
+ LANDSCAPE: 1,
174
+ PORTRAIT: 2,
175
+ SQUARE: 3
176
+ };
177
+ InfographicDetail = {
178
+ CONCISE: 1,
179
+ STANDARD: 2,
180
+ DETAILED: 3
181
+ };
182
+ InfographicStyle = {
183
+ AUTO_SELECT: 1,
184
+ SKETCH_NOTE: 2,
185
+ PROFESSIONAL: 3,
186
+ BENTO_GRID: 4,
187
+ EDITORIAL: 5,
188
+ INSTRUCTIONAL: 6,
189
+ BRICKS: 7,
190
+ CLAY: 8,
191
+ ANIME: 9,
192
+ KAWAII: 10,
193
+ SCIENTIFIC: 11
194
+ };
195
+ SlideDeckFormat = {
196
+ DETAILED_DECK: 1,
197
+ PRESENTER_SLIDES: 2
198
+ };
199
+ SlideDeckLength = {
200
+ DEFAULT: 1,
201
+ SHORT: 2
202
+ };
203
+ ExportType = {
204
+ DOCS: 1,
205
+ SHEETS: 2
206
+ };
207
+ SOURCE_TYPE_MAP = {
208
+ 1: "google_docs",
209
+ 2: "google_slides",
210
+ 3: "pdf",
211
+ 4: "pasted_text",
212
+ 5: "web_page",
213
+ 8: "markdown",
214
+ 9: "youtube",
215
+ 10: "media",
216
+ 11: "docx",
217
+ 13: "image",
218
+ 14: "google_spreadsheet",
219
+ 16: "csv"
220
+ };
221
+ ARTIFACT_TYPE_MAP = {
222
+ 1: "audio",
223
+ 2: "report",
224
+ 3: "video",
225
+ 5: "mind_map",
226
+ 7: "infographic",
227
+ 8: "slide_deck",
228
+ 9: "data_table"
229
+ };
230
+ ARTIFACT_STATUS_MAP = {
231
+ 1: "in_progress",
232
+ 2: "pending",
233
+ 3: "completed",
234
+ 4: "failed"
235
+ };
236
+ SOURCE_STATUS_MAP = {
237
+ 1: "processing",
238
+ 2: "ready",
239
+ 3: "error",
240
+ 5: "preparing"
241
+ };
242
+ ShareAccess = {
243
+ /** Only explicitly shared users can access */
244
+ RESTRICTED: 0,
245
+ /** Anyone with the link can access */
246
+ ANYONE_WITH_LINK: 1
247
+ };
248
+ ShareViewLevel = {
249
+ /** Chat + sources + notes */
250
+ FULL_NOTEBOOK: 0,
251
+ /** Chat interface only */
252
+ CHAT_ONLY: 1
253
+ };
254
+ SharePermission = {
255
+ OWNER: 1,
256
+ EDITOR: 2,
257
+ VIEWER: 3,
258
+ /** Internal: remove user from share list */
259
+ _REMOVE: 4
260
+ };
261
+ }
262
+ });
263
+
13
264
  // src/types/errors.ts
14
265
  var NotebookLMError, NetworkError, RPCTimeoutError, RPCError, AuthError, RateLimitError, ServerError, ClientError, NotebookError, NotebookNotFoundError, SourceError, SourceNotFoundError, SourceAddError, SourceProcessingError, SourceTimeoutError, ArtifactError, ArtifactNotFoundError, ArtifactNotReadyError, ArtifactParseError, ArtifactDownloadError, ChatError;
15
266
  var init_errors = __esm({
@@ -347,543 +598,9 @@ var init_auth = __esm({
347
598
  }
348
599
  });
349
600
 
350
- // src/types/enums.ts
351
- var enums_exports = {};
352
- __export(enums_exports, {
353
- ArtifactStatusCode: () => ArtifactStatusCode,
354
- ArtifactTypeCode: () => ArtifactTypeCode,
355
- AudioFormat: () => AudioFormat,
356
- AudioLength: () => AudioLength,
357
- ExportType: () => ExportType,
358
- InfographicDetail: () => InfographicDetail,
359
- InfographicOrientation: () => InfographicOrientation,
360
- InfographicStyle: () => InfographicStyle,
361
- QuizDifficulty: () => QuizDifficulty,
362
- QuizQuantity: () => QuizQuantity,
363
- RPCMethod: () => RPCMethod,
364
- ShareAccess: () => ShareAccess,
365
- SharePermission: () => SharePermission,
366
- ShareViewLevel: () => ShareViewLevel,
367
- SlideDeckFormat: () => SlideDeckFormat,
368
- SlideDeckLength: () => SlideDeckLength,
369
- SourceStatusCode: () => SourceStatusCode,
370
- VideoFormat: () => VideoFormat,
371
- VideoStyle: () => VideoStyle,
372
- artifactStatusFromCode: () => artifactStatusFromCode,
373
- artifactTypeFromCode: () => artifactTypeFromCode,
374
- sourceStatusFromCode: () => sourceStatusFromCode,
375
- sourceTypeFromCode: () => sourceTypeFromCode
376
- });
377
- function sourceTypeFromCode(code) {
378
- if (code == null) return "unknown";
379
- return SOURCE_TYPE_MAP[code] ?? "unknown";
380
- }
381
- function artifactTypeFromCode(typeCode, variant) {
382
- if (typeCode === 4) {
383
- if (variant === 1) return "flashcards";
384
- if (variant === 2) return "quiz";
385
- return "unknown";
386
- }
387
- return ARTIFACT_TYPE_MAP[typeCode] ?? "unknown";
388
- }
389
- function artifactStatusFromCode(code) {
390
- return ARTIFACT_STATUS_MAP[code] ?? "unknown";
391
- }
392
- function sourceStatusFromCode(code) {
393
- return SOURCE_STATUS_MAP[code] ?? "unknown";
394
- }
395
- var RPCMethod, ArtifactTypeCode, ArtifactStatusCode, SourceStatusCode, AudioFormat, AudioLength, VideoFormat, VideoStyle, QuizQuantity, QuizDifficulty, InfographicOrientation, InfographicDetail, InfographicStyle, SlideDeckFormat, SlideDeckLength, ExportType, SOURCE_TYPE_MAP, ARTIFACT_TYPE_MAP, ARTIFACT_STATUS_MAP, SOURCE_STATUS_MAP, ShareAccess, ShareViewLevel, SharePermission;
396
- var init_enums = __esm({
397
- "src/types/enums.ts"() {
398
- RPCMethod = {
399
- // Notebook
400
- LIST_NOTEBOOKS: "wXbhsf",
401
- CREATE_NOTEBOOK: "CCqFvf",
402
- GET_NOTEBOOK: "rLM1Ne",
403
- RENAME_NOTEBOOK: "s0tc2d",
404
- DELETE_NOTEBOOK: "WWINqb",
405
- // Sources
406
- ADD_SOURCE: "izAoDd",
407
- ADD_SOURCE_FILE: "o4cbdc",
408
- DELETE_SOURCE: "tGMBJ",
409
- GET_SOURCE: "hizoJc",
410
- REFRESH_SOURCE: "FLmJqe",
411
- CHECK_SOURCE_FRESHNESS: "yR9Yof",
412
- UPDATE_SOURCE: "b7Wfje",
413
- // Summary
414
- SUMMARIZE: "VfAZjd",
415
- GET_SOURCE_GUIDE: "tr032e",
416
- GET_SUGGESTED_REPORTS: "ciyUvf",
417
- // Artifacts
418
- CREATE_ARTIFACT: "R7cb6c",
419
- LIST_ARTIFACTS: "gArtLc",
420
- DELETE_ARTIFACT: "V5N4be",
421
- RENAME_ARTIFACT: "rc3d8d",
422
- EXPORT_ARTIFACT: "Krh3pd",
423
- SHARE_ARTIFACT: "RGP97b",
424
- GET_INTERACTIVE_HTML: "v9rmvd",
425
- REVISE_SLIDE: "KmcKPe",
426
- // Research
427
- START_FAST_RESEARCH: "Ljjv0c",
428
- START_DEEP_RESEARCH: "QA9ei",
429
- POLL_RESEARCH: "e3bVqc",
430
- IMPORT_RESEARCH: "LBwxtb",
431
- // Notes / Mind Maps
432
- GENERATE_MIND_MAP: "yyryJe",
433
- CREATE_NOTE: "CYK0Xb",
434
- GET_NOTES_AND_MIND_MAPS: "cFji9",
435
- UPDATE_NOTE: "cYAfTb",
436
- DELETE_NOTE: "AH0mwd",
437
- // Conversation
438
- GET_LAST_CONVERSATION_ID: "hPTbtc",
439
- GET_CONVERSATION_TURNS: "khqZz",
440
- // Sharing
441
- SHARE_NOTEBOOK: "QDyure",
442
- GET_SHARE_STATUS: "JFMDGd",
443
- // Misc
444
- REMOVE_RECENTLY_VIEWED: "fejl7e",
445
- GET_USER_SETTINGS: "ZwVcOc",
446
- SET_USER_SETTINGS: "hT54vc"
447
- };
448
- ArtifactTypeCode = {
449
- AUDIO: 1,
450
- REPORT: 2,
451
- VIDEO: 3,
452
- QUIZ: 4,
453
- MIND_MAP: 5,
454
- INFOGRAPHIC: 7,
455
- SLIDE_DECK: 8,
456
- DATA_TABLE: 9
457
- };
458
- ArtifactStatusCode = {
459
- PROCESSING: 1,
460
- PENDING: 2,
461
- COMPLETED: 3,
462
- FAILED: 4
463
- };
464
- SourceStatusCode = {
465
- PROCESSING: 1,
466
- READY: 2,
467
- ERROR: 3,
468
- PREPARING: 5
469
- };
470
- AudioFormat = {
471
- DEEP_DIVE: 1,
472
- BRIEF: 2,
473
- CRITIQUE: 3,
474
- DEBATE: 4
475
- };
476
- AudioLength = {
477
- SHORT: 1,
478
- DEFAULT: 2,
479
- LONG: 3
480
- };
481
- VideoFormat = {
482
- EXPLAINER: 1,
483
- BRIEF: 2,
484
- CINEMATIC: 3
485
- };
486
- VideoStyle = {
487
- AUTO_SELECT: 1,
488
- CUSTOM: 2,
489
- CLASSIC: 3,
490
- WHITEBOARD: 4,
491
- KAWAII: 5,
492
- ANIME: 6,
493
- WATERCOLOR: 7,
494
- RETRO_PRINT: 8,
495
- HERITAGE: 9,
496
- PAPER_CRAFT: 10
497
- };
498
- QuizQuantity = {
499
- FEWER: 1,
500
- STANDARD: 2,
501
- MORE: 2
502
- // API limitation: same as STANDARD
503
- };
504
- QuizDifficulty = {
505
- EASY: 1,
506
- MEDIUM: 2,
507
- HARD: 3
508
- };
509
- InfographicOrientation = {
510
- LANDSCAPE: 1,
511
- PORTRAIT: 2,
512
- SQUARE: 3
513
- };
514
- InfographicDetail = {
515
- CONCISE: 1,
516
- STANDARD: 2,
517
- DETAILED: 3
518
- };
519
- InfographicStyle = {
520
- AUTO_SELECT: 1,
521
- SKETCH_NOTE: 2,
522
- PROFESSIONAL: 3,
523
- BENTO_GRID: 4,
524
- EDITORIAL: 5,
525
- INSTRUCTIONAL: 6,
526
- BRICKS: 7,
527
- CLAY: 8,
528
- ANIME: 9,
529
- KAWAII: 10,
530
- SCIENTIFIC: 11
531
- };
532
- SlideDeckFormat = {
533
- DETAILED_DECK: 1,
534
- PRESENTER_SLIDES: 2
535
- };
536
- SlideDeckLength = {
537
- DEFAULT: 1,
538
- SHORT: 2
539
- };
540
- ExportType = {
541
- DOCS: 1,
542
- SHEETS: 2
543
- };
544
- SOURCE_TYPE_MAP = {
545
- 1: "google_docs",
546
- 2: "google_slides",
547
- 3: "pdf",
548
- 4: "pasted_text",
549
- 5: "web_page",
550
- 8: "markdown",
551
- 9: "youtube",
552
- 10: "media",
553
- 11: "docx",
554
- 13: "image",
555
- 14: "google_spreadsheet",
556
- 16: "csv"
557
- };
558
- ARTIFACT_TYPE_MAP = {
559
- 1: "audio",
560
- 2: "report",
561
- 3: "video",
562
- 5: "mind_map",
563
- 7: "infographic",
564
- 8: "slide_deck",
565
- 9: "data_table"
566
- };
567
- ARTIFACT_STATUS_MAP = {
568
- 1: "in_progress",
569
- 2: "pending",
570
- 3: "completed",
571
- 4: "failed"
572
- };
573
- SOURCE_STATUS_MAP = {
574
- 1: "processing",
575
- 2: "ready",
576
- 3: "error",
577
- 5: "preparing"
578
- };
579
- ShareAccess = {
580
- /** Only explicitly shared users can access */
581
- RESTRICTED: 0,
582
- /** Anyone with the link can access */
583
- ANYONE_WITH_LINK: 1
584
- };
585
- ShareViewLevel = {
586
- /** Chat + sources + notes */
587
- FULL_NOTEBOOK: 0,
588
- /** Chat interface only */
589
- CHAT_ONLY: 1
590
- };
591
- SharePermission = {
592
- OWNER: 1,
593
- EDITOR: 2,
594
- VIEWER: 3,
595
- /** Internal: remove user from share list */
596
- _REMOVE: 4
597
- };
598
- }
599
- });
600
-
601
- // src/client.ts
602
- init_auth();
603
-
604
- // src/rpc/core.ts
605
- init_errors();
606
-
607
- // src/rpc/encoder.ts
608
- function encodeRPCRequest(methodId, params) {
609
- const paramsJson = JSON.stringify(params);
610
- return [[[methodId, paramsJson, null, "generic"]]];
611
- }
612
- function buildRequestBody(rpcRequest, csrfToken) {
613
- const fReq = encodeURIComponent(JSON.stringify(rpcRequest));
614
- const at = encodeURIComponent(csrfToken);
615
- return `f.req=${fReq}&at=${at}&`;
616
- }
617
- function buildUrlParams(methodId, sessionId, sourcePath = "/") {
618
- return new URLSearchParams({
619
- rpcids: methodId,
620
- "source-path": sourcePath,
621
- "f.sid": sessionId,
622
- hl: "en",
623
- rt: "c"
624
- });
625
- }
626
-
627
- // src/rpc/decoder.ts
628
- init_errors();
629
- function stripAntiXSSI(response) {
630
- if (response.startsWith(")]}'")) {
631
- const match = /\)\]\}'\r?\n/.exec(response);
632
- if (match) return response.slice(match[0].length);
633
- }
634
- return response;
635
- }
636
- function parseChunkedResponse(response) {
637
- if (!response || !response.trim()) return [];
638
- const chunks = [];
639
- let skippedCount = 0;
640
- const lines = response.trim().split("\n");
641
- let i = 0;
642
- while (i < lines.length) {
643
- const line = (lines[i] ?? "").trim();
644
- if (!line) {
645
- i++;
646
- continue;
647
- }
648
- if (/^\d+$/.test(line)) {
649
- i++;
650
- if (i < lines.length) {
651
- try {
652
- const chunk = JSON.parse(lines[i] ?? "");
653
- chunks.push(chunk);
654
- } catch {
655
- skippedCount++;
656
- }
657
- }
658
- i++;
659
- } else {
660
- try {
661
- const chunk = JSON.parse(line);
662
- chunks.push(chunk);
663
- } catch {
664
- skippedCount++;
665
- }
666
- i++;
667
- }
668
- }
669
- if (skippedCount > 0 && lines.length > 0) {
670
- const errorRate = skippedCount / lines.length;
671
- if (errorRate > 0.1) {
672
- throw new RPCError(
673
- `Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
674
- { rawResponse: response.slice(0, 500) }
675
- );
676
- }
677
- }
678
- return chunks;
679
- }
680
- function containsUserDisplayableError(obj) {
681
- if (typeof obj === "string") return obj.includes("UserDisplayableError");
682
- if (Array.isArray(obj)) return obj.some(containsUserDisplayableError);
683
- if (obj !== null && typeof obj === "object") {
684
- return Object.values(obj).some(containsUserDisplayableError);
685
- }
686
- return false;
687
- }
688
- function collectRPCIds(chunks) {
689
- const ids = [];
690
- for (const chunk of chunks) {
691
- if (!Array.isArray(chunk)) continue;
692
- const items = Array.isArray(chunk[0]) ? chunk : [chunk];
693
- for (const item of items) {
694
- if (!Array.isArray(item) || item.length < 2) continue;
695
- if ((item[0] === "wrb.fr" || item[0] === "er") && typeof item[1] === "string") {
696
- ids.push(item[1]);
697
- }
698
- }
699
- }
700
- return ids;
701
- }
702
- function extractRPCResult(chunks, rpcId) {
703
- for (const chunk of chunks) {
704
- if (!Array.isArray(chunk)) continue;
705
- const items = Array.isArray(chunk[0]) ? chunk : [chunk];
706
- for (const item of items) {
707
- if (!Array.isArray(item) || item.length < 3) continue;
708
- if (item[0] === "er" && item[1] === rpcId) {
709
- const code = item[2];
710
- let msg = "Unknown error";
711
- if (typeof code === "number") {
712
- if (code === 429) msg = "API rate limit exceeded. Please wait before retrying.";
713
- else if (code === 401 || code === 403) msg = "Authentication required or forbidden.";
714
- else if (code === 404) msg = "Resource not found.";
715
- else if (code >= 500) msg = `Server error ${code}. Try again later.`;
716
- else msg = `Error code: ${code}`;
717
- } else if (typeof code === "string") {
718
- msg = code;
719
- }
720
- throw new RPCError(msg, { methodId: rpcId, rpcCode: code });
721
- }
722
- if (item[0] === "wrb.fr" && item[1] === rpcId) {
723
- const resultData = item[2];
724
- if (resultData === null && item.length > 5 && item[5] != null) {
725
- if (containsUserDisplayableError(item[5])) {
726
- throw new RateLimitError(
727
- "API rate limit or quota exceeded. Please wait before retrying.",
728
- { methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
729
- );
730
- }
731
- }
732
- if (typeof resultData === "string") {
733
- try {
734
- return JSON.parse(resultData);
735
- } catch {
736
- return resultData;
737
- }
738
- }
739
- return resultData;
740
- }
741
- }
742
- }
743
- return void 0;
744
- }
745
- function decodeResponse(rawResponse, rpcId, allowNull = false) {
746
- const cleaned = stripAntiXSSI(rawResponse);
747
- const chunks = parseChunkedResponse(cleaned);
748
- const responsePreview = cleaned.slice(0, 500);
749
- const foundIds = collectRPCIds(chunks);
750
- let result;
751
- try {
752
- result = extractRPCResult(chunks, rpcId);
753
- } catch (e) {
754
- if (e instanceof RPCError && e.foundIds.length === 0) {
755
- throw new RPCError(e.message, {
756
- methodId: e.methodId,
757
- rpcCode: e.rpcCode,
758
- foundIds,
759
- rawResponse: responsePreview
760
- });
761
- }
762
- throw e;
763
- }
764
- if (result === void 0 && !allowNull) {
765
- if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
766
- throw new RPCError(
767
- `No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
768
- { methodId: rpcId, foundIds, rawResponse: responsePreview }
769
- );
770
- }
771
- throw new RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
772
- methodId: rpcId,
773
- foundIds,
774
- rawResponse: responsePreview
775
- });
776
- }
777
- return result ?? null;
778
- }
779
-
780
- // src/rpc/core.ts
781
- var BATCHEXECUTE_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/batchexecute";
782
- var DEFAULT_TIMEOUT_MS = 3e4;
783
- var RPCCore = class {
784
- auth;
785
- timeoutMs;
786
- constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
787
- this.auth = auth;
788
- this.timeoutMs = timeoutMs;
789
- }
790
- async call(methodId, params, opts = {}) {
791
- const sourcePath = opts.sourcePath ?? "/";
792
- const allowNull = opts.allowNull ?? false;
793
- const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
794
- const rpcRequest = encodeRPCRequest(methodId, params);
795
- const body = buildRequestBody(rpcRequest, this.auth.csrfToken);
796
- const urlParams = buildUrlParams(methodId, this.auth.sessionId, sourcePath);
797
- const url = `${BATCHEXECUTE_URL}?${urlParams.toString()}`;
798
- const controller = new AbortController();
799
- const timer = setTimeout(() => controller.abort(), timeoutMs);
800
- let response;
801
- try {
802
- response = await fetch(url, {
803
- method: "POST",
804
- headers: {
805
- "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
806
- Cookie: this.auth.cookieHeader
807
- },
808
- body,
809
- signal: controller.signal
810
- });
811
- } catch (e) {
812
- clearTimeout(timer);
813
- if (e instanceof Error && e.name === "AbortError") {
814
- throw new RPCTimeoutError(`Request timed out calling ${methodId}`, {
815
- methodId,
816
- originalError: e
817
- });
818
- }
819
- throw new NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
820
- methodId,
821
- originalError: e instanceof Error ? e : void 0
822
- });
823
- } finally {
824
- clearTimeout(timer);
825
- }
826
- if (!response.ok) {
827
- const status = response.status;
828
- if (status === 429) {
829
- const retryAfterHeader = response.headers.get("retry-after");
830
- const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
831
- throw new RateLimitError(`API rate limit exceeded calling ${methodId}`, {
832
- methodId,
833
- retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
834
- });
835
- }
836
- if (status === 401 || status === 403) {
837
- throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
838
- methodId
839
- });
840
- }
841
- if (status >= 500) {
842
- throw new ServerError(`Server error ${status} calling ${methodId}`, {
843
- methodId,
844
- statusCode: status
845
- });
846
- }
847
- if (status >= 400) {
848
- throw new ClientError(`Client error ${status} calling ${methodId}`, {
849
- methodId,
850
- statusCode: status
851
- });
852
- }
853
- throw new RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
854
- }
855
- const text = await response.text();
856
- return decodeResponse(text, methodId, allowNull);
857
- }
858
- /** Extract source IDs from a notebook (used by chat/artifact APIs). */
859
- async getSourceIds(notebookId) {
860
- const params = [notebookId, null, [2], null, 0];
861
- const { RPCMethod: RPCMethod2 } = await Promise.resolve().then(() => (init_enums(), enums_exports));
862
- const data = await this.call(RPCMethod2.GET_NOTEBOOK, params, {
863
- sourcePath: `/notebook/${notebookId}`
864
- });
865
- const sourceIds = [];
866
- if (!Array.isArray(data) || !data.length) return sourceIds;
867
- try {
868
- const nbInfo = data[0];
869
- if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return sourceIds;
870
- const sources = nbInfo[1];
871
- if (!Array.isArray(sources)) return sourceIds;
872
- for (const src of sources) {
873
- if (!Array.isArray(src) || !src.length) continue;
874
- const first = src[0];
875
- if (Array.isArray(first) && first.length > 0 && typeof first[0] === "string") {
876
- sourceIds.push(first[0]);
877
- }
878
- }
879
- } catch {
880
- }
881
- return sourceIds;
882
- }
883
- };
884
-
885
- // src/api/notebooks.ts
601
+ // src/api/artifacts.ts
886
602
  init_enums();
603
+ init_errors();
887
604
 
888
605
  // src/types/models.ts
889
606
  init_enums();
@@ -1007,287 +724,15 @@ function parseNote(data) {
1007
724
  }
1008
725
  }
1009
726
  if (Array.isArray(data[4]) && typeof data[4][0] === "number") {
1010
- try {
1011
- updatedAt = new Date(data[4][0] * 1e3);
1012
- } catch {
1013
- }
1014
- }
1015
- return { id, title, content, createdAt, updatedAt };
1016
- }
1017
-
1018
- // src/api/notebooks.ts
1019
- var NotebooksAPI = class {
1020
- constructor(rpc) {
1021
- this.rpc = rpc;
1022
- }
1023
- async list() {
1024
- const params = [null, 1, null, [2]];
1025
- const result = await this.rpc.call(RPCMethod.LIST_NOTEBOOKS, params);
1026
- if (!Array.isArray(result) || !result.length) return [];
1027
- const raw = Array.isArray(result[0]) ? result[0] : result;
1028
- return raw.map((nb) => parseNotebook(nb));
1029
- }
1030
- async create(title) {
1031
- const params = [title, null, null, [2], [1]];
1032
- const result = await this.rpc.call(RPCMethod.CREATE_NOTEBOOK, params);
1033
- return parseNotebook(result);
1034
- }
1035
- async get(notebookId) {
1036
- const params = [notebookId, null, [2], null, 0];
1037
- const result = await this.rpc.call(RPCMethod.GET_NOTEBOOK, params, {
1038
- sourcePath: `/notebook/${notebookId}`
1039
- });
1040
- const data = Array.isArray(result) && result.length ? result[0] : result;
1041
- return parseNotebook(data);
1042
- }
1043
- async delete(notebookId) {
1044
- const params = [[notebookId], [2]];
1045
- await this.rpc.call(RPCMethod.DELETE_NOTEBOOK, params);
1046
- return true;
1047
- }
1048
- async rename(notebookId, newTitle) {
1049
- const params = [notebookId, [[null, null, null, [null, newTitle]]]];
1050
- await this.rpc.call(RPCMethod.RENAME_NOTEBOOK, params, {
1051
- sourcePath: "/",
1052
- allowNull: true
1053
- });
1054
- return this.get(notebookId);
1055
- }
1056
- async getSummary(notebookId) {
1057
- const params = [notebookId, [2]];
1058
- const result = await this.rpc.call(RPCMethod.SUMMARIZE, params, {
1059
- sourcePath: `/notebook/${notebookId}`
1060
- });
1061
- try {
1062
- if (Array.isArray(result)) {
1063
- const val = result[0]?.[0]?.[0];
1064
- return typeof val === "string" ? val : "";
1065
- }
1066
- } catch {
1067
- }
1068
- return "";
1069
- }
1070
- async getDescription(notebookId) {
1071
- const params = [notebookId, [2]];
1072
- const result = await this.rpc.call(RPCMethod.SUMMARIZE, params, {
1073
- sourcePath: `/notebook/${notebookId}`
1074
- });
1075
- let summary = "";
1076
- const suggestedTopics = [];
1077
- try {
1078
- if (Array.isArray(result)) {
1079
- const outer = result[0];
1080
- if (Array.isArray(outer)) {
1081
- const summaryVal = outer[0]?.[0];
1082
- if (typeof summaryVal === "string") summary = summaryVal;
1083
- const topicsList = outer[1]?.[0];
1084
- if (Array.isArray(topicsList)) {
1085
- for (const t of topicsList) {
1086
- const question = typeof t[0] === "string" ? t[0] : "";
1087
- const prompt = typeof t[1] === "string" ? t[1] : "";
1088
- suggestedTopics.push({ question, prompt });
1089
- }
1090
- }
1091
- }
1092
- }
1093
- } catch {
1094
- }
1095
- return { summary, suggestedTopics };
1096
- }
1097
- };
1098
-
1099
- // src/api/sources.ts
1100
- init_enums();
1101
- init_errors();
1102
- var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
1103
- var SourcesAPI = class {
1104
- constructor(rpc, auth) {
1105
- this.rpc = rpc;
1106
- this.auth = auth;
1107
- }
1108
- async list(notebookId) {
1109
- const params = [notebookId, null, [2], null, 0];
1110
- const notebook = await this.rpc.call(RPCMethod.GET_NOTEBOOK, params, {
1111
- sourcePath: `/notebook/${notebookId}`
1112
- });
1113
- if (!Array.isArray(notebook) || !notebook.length) return [];
1114
- const nbInfo = notebook[0];
1115
- if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return [];
1116
- const sourcesList = nbInfo[1];
1117
- if (!Array.isArray(sourcesList)) return [];
1118
- return sourcesList.filter((s) => Array.isArray(s) && s.length > 0).map((s) => parseSource(s));
1119
- }
1120
- async get(notebookId, sourceId) {
1121
- const sources = await this.list(notebookId);
1122
- return sources.find((s) => s.id === sourceId) ?? null;
1123
- }
1124
- async addUrl(notebookId, url, opts = {}) {
1125
- const params = [notebookId, [[url]], null, null, [2]];
1126
- const result = await this.rpc.call(RPCMethod.ADD_SOURCE, params, {
1127
- sourcePath: `/notebook/${notebookId}`
1128
- });
1129
- const sourceId = extractSourceId(result);
1130
- if (opts.waitUntilReady) {
1131
- return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1132
- }
1133
- return {
1134
- id: sourceId,
1135
- title: url,
1136
- url,
1137
- kind: "web_page",
1138
- createdAt: null,
1139
- status: "processing",
1140
- _typeCode: null
1141
- };
1142
- }
1143
- async addText(notebookId, text, title, opts = {}) {
1144
- const params = [notebookId, null, [[null, null, null, text, title ?? null]], null, [2]];
1145
- const result = await this.rpc.call(RPCMethod.ADD_SOURCE, params, {
1146
- sourcePath: `/notebook/${notebookId}`
1147
- });
1148
- const sourceId = extractSourceId(result);
1149
- if (opts.waitUntilReady) {
1150
- return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1151
- }
1152
- return {
1153
- id: sourceId,
1154
- title: title ?? null,
1155
- url: null,
1156
- kind: "pasted_text",
1157
- createdAt: null,
1158
- status: "processing",
1159
- _typeCode: null
1160
- };
1161
- }
1162
- async addFile(notebookId, filePath, mimeType, opts = {}) {
1163
- const fileData = readFileSync(filePath);
1164
- const fileName = filePath.split("/").pop() ?? "file";
1165
- return this.addFileBuffer(notebookId, fileData, fileName, mimeType, opts);
1166
- }
1167
- async addFileBuffer(notebookId, data, fileName, mimeType, opts = {}) {
1168
- const uploadUrl = await this.uploadFile(notebookId, data, fileName, mimeType);
1169
- const params = [notebookId, null, null, [[uploadUrl, fileName, mimeType]], [2]];
1170
- const result = await this.rpc.call(RPCMethod.ADD_SOURCE_FILE, params, {
1171
- sourcePath: `/notebook/${notebookId}`
1172
- });
1173
- const sourceId = extractSourceId(result);
1174
- if (opts.waitUntilReady) {
1175
- return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1176
- }
1177
- return {
1178
- id: sourceId,
1179
- title: fileName,
1180
- url: null,
1181
- kind: "pdf",
1182
- createdAt: null,
1183
- status: "processing",
1184
- _typeCode: null
1185
- };
1186
- }
1187
- async uploadFile(notebookId, data, fileName, mimeType) {
1188
- const params = new URLSearchParams({
1189
- "source-path": `/notebook/${notebookId}`,
1190
- upload_id: `${Date.now()}`,
1191
- upload_protocol: "resumable"
1192
- });
1193
- const initResp = await fetch(`${UPLOAD_URL}?${params}`, {
1194
- method: "POST",
1195
- headers: {
1196
- Cookie: this.auth.cookieHeader,
1197
- "X-Goog-Upload-Command": "start",
1198
- "X-Goog-Upload-Header-Content-Length": String(data.length),
1199
- "X-Goog-Upload-Header-Content-Type": mimeType,
1200
- "X-Upload-Content-Type": mimeType,
1201
- "Content-Type": "application/json"
1202
- },
1203
- body: JSON.stringify({ title: fileName })
1204
- });
1205
- if (!initResp.ok) {
1206
- throw new Error(`Upload initiation failed: HTTP ${initResp.status}`);
1207
- }
1208
- const uploadSessionUrl = initResp.headers.get("x-goog-upload-url") ?? `${UPLOAD_URL}?${params}`;
1209
- const uploadResp = await fetch(uploadSessionUrl, {
1210
- method: "POST",
1211
- headers: {
1212
- Cookie: this.auth.cookieHeader,
1213
- "X-Goog-Upload-Command": "upload, finalize",
1214
- "X-Goog-Upload-Offset": "0",
1215
- "Content-Type": mimeType,
1216
- "Content-Length": String(data.length)
1217
- },
1218
- body: data instanceof Buffer ? data.buffer : data.buffer
1219
- });
1220
- if (!uploadResp.ok) {
1221
- throw new Error(`File upload failed: HTTP ${uploadResp.status}`);
1222
- }
1223
- const uploadResult = await uploadResp.text();
1224
- return uploadResult.trim();
1225
- }
1226
- async delete(notebookId, sourceId) {
1227
- const params = [notebookId, [sourceId], [2]];
1228
- await this.rpc.call(RPCMethod.DELETE_SOURCE, params, {
1229
- sourcePath: `/notebook/${notebookId}`,
1230
- allowNull: true
1231
- });
1232
- return true;
1233
- }
1234
- async refresh(notebookId, sourceId) {
1235
- const params = [notebookId, sourceId, [2]];
1236
- await this.rpc.call(RPCMethod.REFRESH_SOURCE, params, {
1237
- sourcePath: `/notebook/${notebookId}`,
1238
- allowNull: true
1239
- });
1240
- return true;
1241
- }
1242
- async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
1243
- const deadline = Date.now() + timeout * 1e3;
1244
- let interval = initialInterval;
1245
- let lastStatus;
1246
- while (Date.now() < deadline) {
1247
- const source = await this.get(notebookId, sourceId);
1248
- if (source) {
1249
- if (source.status === "ready") return source;
1250
- if (source.status === "error") {
1251
- throw new SourceProcessingError(sourceId, 3);
1252
- }
1253
- lastStatus = source._typeCode ?? void 0;
1254
- }
1255
- await sleep(interval * 1e3);
1256
- interval = Math.min(interval * backoffFactor, maxInterval);
1257
- }
1258
- throw new SourceTimeoutError(sourceId, timeout, lastStatus);
1259
- }
1260
- };
1261
- function extractSourceId(result) {
1262
- if (Array.isArray(result)) {
1263
- try {
1264
- const v0 = result[0];
1265
- if (Array.isArray(v0)) {
1266
- const v00 = v0[0];
1267
- if (Array.isArray(v00)) {
1268
- const v000 = v00[0];
1269
- if (Array.isArray(v000) && typeof v000[0] === "string") return v000[0];
1270
- if (typeof v000 === "string") return v000;
1271
- }
1272
- if (typeof v00 === "string") return v00;
1273
- }
1274
- if (typeof v0 === "string") return v0;
727
+ try {
728
+ updatedAt = new Date(data[4][0] * 1e3);
1275
729
  } catch {
1276
730
  }
1277
- for (const item of result) {
1278
- if (typeof item === "string" && item.length > 8) return item;
1279
- }
1280
731
  }
1281
- if (typeof result === "string") return result;
1282
- throw new Error("Could not extract source ID from API response");
1283
- }
1284
- function sleep(ms) {
1285
- return new Promise((resolve) => setTimeout(resolve, ms));
732
+ return { id, title, content, createdAt, updatedAt };
1286
733
  }
1287
734
 
1288
735
  // src/api/artifacts.ts
1289
- init_enums();
1290
- init_errors();
1291
736
  function tripleNest(ids) {
1292
737
  return ids.map((id) => [[id]]);
1293
738
  }
@@ -1576,7 +1021,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1576
1021
  if (artifact?.status === "failed") {
1577
1022
  throw new ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
1578
1023
  }
1579
- await sleep2(pollInterval * 1e3);
1024
+ await sleep(pollInterval * 1e3);
1580
1025
  }
1581
1026
  throw new ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
1582
1027
  }
@@ -1674,7 +1119,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1674
1119
  return { artifactId: null, status: "failed" };
1675
1120
  }
1676
1121
  };
1677
- function sleep2(ms) {
1122
+ function sleep(ms) {
1678
1123
  return new Promise((resolve) => setTimeout(resolve, ms));
1679
1124
  }
1680
1125
  var TRUSTED_MEDIA_DOMAINS = [
@@ -1744,589 +1189,1185 @@ var ChatAPI = class {
1744
1189
  } catch (e) {
1745
1190
  throw new ChatError(`Chat request failed: ${String(e)}`);
1746
1191
  }
1747
- if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
1748
- const text = await response.text();
1749
- const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1750
- const finalConvId = serverConvId ?? conversationId;
1751
- const cached = this.conversationCache.get(finalConvId) ?? [];
1752
- const turnNumber = cached.length + 1;
1753
- cached.push({ query, answer, turnNumber });
1754
- this.conversationCache.set(finalConvId, cached);
1755
- return { answer, conversationId: finalConvId, turnNumber, references };
1192
+ if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
1193
+ const text = await response.text();
1194
+ const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1195
+ const finalConvId = serverConvId ?? conversationId;
1196
+ const cached = this.conversationCache.get(finalConvId) ?? [];
1197
+ const turnNumber = cached.length + 1;
1198
+ cached.push({ query, answer, turnNumber });
1199
+ this.conversationCache.set(finalConvId, cached);
1200
+ return { answer, conversationId: finalConvId, turnNumber, references };
1201
+ }
1202
+ async getConversationTurns(notebookId, conversationId) {
1203
+ const params = [[], null, null, conversationId, 100];
1204
+ const result = await this.rpc.call(RPCMethod.GET_CONVERSATION_TURNS, params, {
1205
+ sourcePath: `/notebook/${notebookId}`,
1206
+ allowNull: true
1207
+ });
1208
+ if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1209
+ const rawTurns = [...result[0]].reverse();
1210
+ const turns = [];
1211
+ let i = 0;
1212
+ while (i < rawTurns.length) {
1213
+ const turn = rawTurns[i];
1214
+ if (!Array.isArray(turn) || turn.length < 3) {
1215
+ i++;
1216
+ continue;
1217
+ }
1218
+ if (turn[2] === 1 && turn.length > 3) {
1219
+ const q = typeof turn[3] === "string" ? turn[3] : "";
1220
+ let a = "";
1221
+ const next = rawTurns[i + 1];
1222
+ if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1223
+ try {
1224
+ a = String(next[4][0][0] ?? "");
1225
+ } catch {
1226
+ }
1227
+ i++;
1228
+ }
1229
+ turns.push({ query: q, answer: a, turnNumber: turns.length + 1 });
1230
+ }
1231
+ i++;
1232
+ }
1233
+ return turns;
1234
+ }
1235
+ async getLastConversationId(notebookId) {
1236
+ const params = [[], null, notebookId, 1];
1237
+ const result = await this.rpc.call(RPCMethod.GET_LAST_CONVERSATION_ID, params, {
1238
+ sourcePath: `/notebook/${notebookId}`,
1239
+ allowNull: true
1240
+ });
1241
+ if (!Array.isArray(result)) return null;
1242
+ for (const group of result) {
1243
+ if (!Array.isArray(group)) continue;
1244
+ for (const conv of group) {
1245
+ if (Array.isArray(conv) && typeof conv[0] === "string") {
1246
+ return conv[0];
1247
+ }
1248
+ }
1249
+ }
1250
+ return null;
1251
+ }
1252
+ clearCache(conversationId) {
1253
+ if (conversationId) {
1254
+ this.conversationCache.delete(conversationId);
1255
+ } else {
1256
+ this.conversationCache.clear();
1257
+ }
1258
+ }
1259
+ _buildHistory(conversationId) {
1260
+ const turns = this.conversationCache.get(conversationId) ?? [];
1261
+ if (!turns.length) return null;
1262
+ const history = [];
1263
+ for (const turn of turns) {
1264
+ history.push([turn.answer, null, 2]);
1265
+ history.push([turn.query, null, 1]);
1266
+ }
1267
+ return history;
1268
+ }
1269
+ };
1270
+ function parseStreamingResponse(rawText) {
1271
+ let text = rawText;
1272
+ if (text.startsWith(")]}'")) text = text.slice(4);
1273
+ const lines = text.trim().split("\n");
1274
+ let bestMarkedAnswer = "";
1275
+ let bestUnmarkedAnswer = "";
1276
+ let serverConvId = null;
1277
+ const references = [];
1278
+ function processChunk(jsonStr) {
1279
+ let data;
1280
+ try {
1281
+ data = JSON.parse(jsonStr);
1282
+ } catch {
1283
+ return;
1284
+ }
1285
+ if (!Array.isArray(data)) return;
1286
+ for (const item of data) {
1287
+ if (!Array.isArray(item) || item.length < 3 || item[0] !== "wrb.fr") continue;
1288
+ const innerJson = item[2];
1289
+ if (typeof innerJson !== "string") continue;
1290
+ let innerData;
1291
+ try {
1292
+ innerData = JSON.parse(innerJson);
1293
+ } catch {
1294
+ continue;
1295
+ }
1296
+ if (!Array.isArray(innerData) || !innerData.length) continue;
1297
+ const first = innerData[0];
1298
+ if (!Array.isArray(first) || !first.length) continue;
1299
+ const answerText = first[0];
1300
+ if (typeof answerText !== "string" || !answerText) continue;
1301
+ const typeInfo = first[4];
1302
+ const isAnswer = Array.isArray(typeInfo) && typeInfo.length > 0 && typeInfo[typeInfo.length - 1] === 1;
1303
+ const convData = first[2];
1304
+ if (!serverConvId && Array.isArray(convData) && convData.length > 0 && typeof convData[0] === "string") {
1305
+ serverConvId = convData[0];
1306
+ }
1307
+ if (Array.isArray(typeInfo) && typeInfo.length > 3) {
1308
+ const citations = typeInfo[3];
1309
+ if (Array.isArray(citations)) {
1310
+ for (const cite of citations) {
1311
+ const sourceId = extractUuid(cite);
1312
+ if (sourceId) {
1313
+ references.push({ sourceId, title: null, url: null });
1314
+ }
1315
+ }
1316
+ }
1317
+ }
1318
+ if (isAnswer && answerText.length > bestMarkedAnswer.length) {
1319
+ bestMarkedAnswer = answerText;
1320
+ } else if (!isAnswer && answerText.length > bestUnmarkedAnswer.length) {
1321
+ bestUnmarkedAnswer = answerText;
1322
+ }
1323
+ }
1324
+ }
1325
+ let i = 0;
1326
+ while (i < lines.length) {
1327
+ const line = (lines[i] ?? "").trim();
1328
+ if (!line) {
1329
+ i++;
1330
+ continue;
1331
+ }
1332
+ if (/^\d+$/.test(line)) {
1333
+ i++;
1334
+ const next = lines[i];
1335
+ if (next !== void 0) processChunk(next);
1336
+ i++;
1337
+ } else {
1338
+ processChunk(line);
1339
+ i++;
1340
+ }
1341
+ }
1342
+ return {
1343
+ answer: bestMarkedAnswer || bestUnmarkedAnswer,
1344
+ conversationId: serverConvId,
1345
+ references
1346
+ };
1347
+ }
1348
+ function extractUuid(data, depth = 8) {
1349
+ if (depth <= 0 || data == null) return null;
1350
+ if (typeof data === "string") return UUID_RE.test(data) ? data : null;
1351
+ if (Array.isArray(data)) {
1352
+ for (const item of data) {
1353
+ const found = extractUuid(item, depth - 1);
1354
+ if (found) return found;
1355
+ }
1356
+ }
1357
+ return null;
1358
+ }
1359
+ function randomUUID() {
1360
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1361
+ return crypto.randomUUID();
1362
+ }
1363
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1364
+ const r = Math.random() * 16 | 0;
1365
+ return (c === "x" ? r : r & 3 | 8).toString(16);
1366
+ });
1367
+ }
1368
+
1369
+ // src/api/notebooks.ts
1370
+ init_enums();
1371
+ var NotebooksAPI = class {
1372
+ constructor(rpc) {
1373
+ this.rpc = rpc;
1374
+ }
1375
+ async list() {
1376
+ const params = [null, 1, null, [2]];
1377
+ const result = await this.rpc.call(RPCMethod.LIST_NOTEBOOKS, params);
1378
+ if (!Array.isArray(result) || !result.length) return [];
1379
+ const raw = Array.isArray(result[0]) ? result[0] : result;
1380
+ return raw.map((nb) => parseNotebook(nb));
1381
+ }
1382
+ async create(title) {
1383
+ const params = [title, null, null, [2], [1]];
1384
+ const result = await this.rpc.call(RPCMethod.CREATE_NOTEBOOK, params);
1385
+ return parseNotebook(result);
1386
+ }
1387
+ async get(notebookId) {
1388
+ const params = [notebookId, null, [2], null, 0];
1389
+ const result = await this.rpc.call(RPCMethod.GET_NOTEBOOK, params, {
1390
+ sourcePath: `/notebook/${notebookId}`
1391
+ });
1392
+ const data = Array.isArray(result) && result.length ? result[0] : result;
1393
+ return parseNotebook(data);
1394
+ }
1395
+ async delete(notebookId) {
1396
+ const params = [[notebookId], [2]];
1397
+ await this.rpc.call(RPCMethod.DELETE_NOTEBOOK, params);
1398
+ return true;
1399
+ }
1400
+ async rename(notebookId, newTitle) {
1401
+ const params = [notebookId, [[null, null, null, [null, newTitle]]]];
1402
+ await this.rpc.call(RPCMethod.RENAME_NOTEBOOK, params, {
1403
+ sourcePath: "/",
1404
+ allowNull: true
1405
+ });
1406
+ return this.get(notebookId);
1407
+ }
1408
+ async getSummary(notebookId) {
1409
+ const params = [notebookId, [2]];
1410
+ const result = await this.rpc.call(RPCMethod.SUMMARIZE, params, {
1411
+ sourcePath: `/notebook/${notebookId}`
1412
+ });
1413
+ try {
1414
+ if (Array.isArray(result)) {
1415
+ const val = result[0]?.[0]?.[0];
1416
+ return typeof val === "string" ? val : "";
1417
+ }
1418
+ } catch {
1419
+ }
1420
+ return "";
1421
+ }
1422
+ async getDescription(notebookId) {
1423
+ const params = [notebookId, [2]];
1424
+ const result = await this.rpc.call(RPCMethod.SUMMARIZE, params, {
1425
+ sourcePath: `/notebook/${notebookId}`
1426
+ });
1427
+ let summary = "";
1428
+ const suggestedTopics = [];
1429
+ try {
1430
+ if (Array.isArray(result)) {
1431
+ const outer = result[0];
1432
+ if (Array.isArray(outer)) {
1433
+ const summaryVal = outer[0]?.[0];
1434
+ if (typeof summaryVal === "string") summary = summaryVal;
1435
+ const topicsList = outer[1]?.[0];
1436
+ if (Array.isArray(topicsList)) {
1437
+ for (const t of topicsList) {
1438
+ const question = typeof t[0] === "string" ? t[0] : "";
1439
+ const prompt = typeof t[1] === "string" ? t[1] : "";
1440
+ suggestedTopics.push({ question, prompt });
1441
+ }
1442
+ }
1443
+ }
1444
+ }
1445
+ } catch {
1446
+ }
1447
+ return { summary, suggestedTopics };
1756
1448
  }
1757
- async getConversationTurns(notebookId, conversationId) {
1758
- const params = [[], null, null, conversationId, 100];
1759
- const result = await this.rpc.call(RPCMethod.GET_CONVERSATION_TURNS, params, {
1760
- sourcePath: `/notebook/${notebookId}`,
1761
- allowNull: true
1449
+ };
1450
+
1451
+ // src/api/notes.ts
1452
+ init_enums();
1453
+ var NotesAPI = class {
1454
+ constructor(rpc) {
1455
+ this.rpc = rpc;
1456
+ }
1457
+ async list(notebookId) {
1458
+ const params = [notebookId, [2]];
1459
+ const result = await this.rpc.call(RPCMethod.GET_NOTES_AND_MIND_MAPS, params, {
1460
+ sourcePath: `/notebook/${notebookId}`
1762
1461
  });
1763
- if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1764
- const rawTurns = [...result[0]].reverse();
1765
- const turns = [];
1766
- let i = 0;
1767
- while (i < rawTurns.length) {
1768
- const turn = rawTurns[i];
1769
- if (!Array.isArray(turn) || turn.length < 3) {
1770
- i++;
1771
- continue;
1462
+ const notes = [];
1463
+ const mindMaps = [];
1464
+ if (!Array.isArray(result)) return { notes, mindMaps };
1465
+ try {
1466
+ const notesData = result[0];
1467
+ if (Array.isArray(notesData)) {
1468
+ for (const n of notesData) {
1469
+ if (Array.isArray(n)) notes.push(parseNote(n));
1470
+ }
1772
1471
  }
1773
- if (turn[2] === 1 && turn.length > 3) {
1774
- const q = typeof turn[3] === "string" ? turn[3] : "";
1775
- let a = "";
1776
- const next = rawTurns[i + 1];
1777
- if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1778
- try {
1779
- a = String(next[4][0][0] ?? "");
1780
- } catch {
1472
+ const mapsData = result[1];
1473
+ if (Array.isArray(mapsData)) {
1474
+ for (const m of mapsData) {
1475
+ if (Array.isArray(m)) {
1476
+ mindMaps.push({
1477
+ id: typeof m[0] === "string" ? m[0] : "",
1478
+ title: typeof m[2] === "string" ? m[2] : null,
1479
+ content: typeof m[1] === "string" ? m[1] : "",
1480
+ createdAt: Array.isArray(m[3]) && typeof m[3][0] === "number" ? new Date(m[3][0] * 1e3) : null
1481
+ });
1781
1482
  }
1782
- i++;
1783
1483
  }
1784
- turns.push({ query: q, answer: a, turnNumber: turns.length + 1 });
1785
1484
  }
1786
- i++;
1485
+ } catch {
1787
1486
  }
1788
- return turns;
1487
+ return { notes, mindMaps };
1789
1488
  }
1790
- async getLastConversationId(notebookId) {
1791
- const params = [[], null, notebookId, 1];
1792
- const result = await this.rpc.call(RPCMethod.GET_LAST_CONVERSATION_ID, params, {
1489
+ async create(notebookId, content, title) {
1490
+ const params = [notebookId, content, title ?? null, [2]];
1491
+ const result = await this.rpc.call(RPCMethod.CREATE_NOTE, params, {
1492
+ sourcePath: `/notebook/${notebookId}`
1493
+ });
1494
+ if (Array.isArray(result)) return parseNote(result);
1495
+ throw new Error("Could not parse note creation response");
1496
+ }
1497
+ async update(notebookId, noteId, content, title) {
1498
+ const params = [notebookId, noteId, content, title ?? null, [2]];
1499
+ const result = await this.rpc.call(RPCMethod.UPDATE_NOTE, params, {
1500
+ sourcePath: `/notebook/${notebookId}`
1501
+ });
1502
+ if (Array.isArray(result)) return parseNote(result);
1503
+ return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
1504
+ }
1505
+ async delete(notebookId, noteId) {
1506
+ const params = [notebookId, noteId, [2]];
1507
+ await this.rpc.call(RPCMethod.DELETE_NOTE, params, {
1793
1508
  sourcePath: `/notebook/${notebookId}`,
1794
1509
  allowNull: true
1795
1510
  });
1796
- if (!Array.isArray(result)) return null;
1797
- for (const group of result) {
1798
- if (!Array.isArray(group)) continue;
1799
- for (const conv of group) {
1800
- if (Array.isArray(conv) && typeof conv[0] === "string") {
1801
- return conv[0];
1802
- }
1803
- }
1804
- }
1805
- return null;
1511
+ return true;
1806
1512
  }
1807
- clearCache(conversationId) {
1808
- if (conversationId) {
1809
- this.conversationCache.delete(conversationId);
1513
+ };
1514
+
1515
+ // src/api/research.ts
1516
+ init_enums();
1517
+ var ResearchAPI = class {
1518
+ constructor(rpc) {
1519
+ this.rpc = rpc;
1520
+ }
1521
+ /**
1522
+ * Start a research session.
1523
+ * @param source "web" or "drive"
1524
+ * @param mode "fast" or "deep" (deep only available for web)
1525
+ */
1526
+ async start(notebookId, query, source = "web", mode = "fast") {
1527
+ if (mode === "deep" && source === "drive") {
1528
+ throw new Error("Deep research only supports web sources.");
1529
+ }
1530
+ const sourceType = source === "web" ? 1 : 2;
1531
+ let rpcId;
1532
+ let params;
1533
+ if (mode === "fast") {
1534
+ rpcId = RPCMethod.START_FAST_RESEARCH;
1535
+ params = [[query, sourceType], null, 1, notebookId];
1810
1536
  } else {
1811
- this.conversationCache.clear();
1537
+ rpcId = RPCMethod.START_DEEP_RESEARCH;
1538
+ params = [null, [1], [query, sourceType], 5, notebookId];
1812
1539
  }
1813
- }
1814
- _buildHistory(conversationId) {
1815
- const turns = this.conversationCache.get(conversationId) ?? [];
1816
- if (!turns.length) return null;
1817
- const history = [];
1818
- for (const turn of turns) {
1819
- history.push([turn.answer, null, 2]);
1820
- history.push([turn.query, null, 1]);
1540
+ const result = await this.rpc.call(rpcId, params, {
1541
+ sourcePath: `/notebook/${notebookId}`,
1542
+ allowNull: true
1543
+ });
1544
+ if (Array.isArray(result) && result.length > 0) {
1545
+ return {
1546
+ taskId: result[0],
1547
+ reportId: result.length > 1 ? result[1] : null,
1548
+ notebookId,
1549
+ query,
1550
+ mode
1551
+ };
1821
1552
  }
1822
- return history;
1553
+ return null;
1823
1554
  }
1824
- };
1825
- function parseStreamingResponse(rawText) {
1826
- let text = rawText;
1827
- if (text.startsWith(")]}'")) text = text.slice(4);
1828
- const lines = text.trim().split("\n");
1829
- let bestMarkedAnswer = "";
1830
- let bestUnmarkedAnswer = "";
1831
- let serverConvId = null;
1832
- const references = [];
1833
- function processChunk(jsonStr) {
1834
- let data;
1835
- try {
1836
- data = JSON.parse(jsonStr);
1837
- } catch {
1838
- return;
1555
+ /** Poll for current research results in a notebook. */
1556
+ async poll(notebookId) {
1557
+ const params = [null, null, notebookId];
1558
+ let result = await this.rpc.call(RPCMethod.POLL_RESEARCH, params, {
1559
+ sourcePath: `/notebook/${notebookId}`,
1560
+ allowNull: true
1561
+ });
1562
+ if (!Array.isArray(result) || !result.length) {
1563
+ return emptyResult();
1839
1564
  }
1840
- if (!Array.isArray(data)) return;
1841
- for (const item of data) {
1842
- if (!Array.isArray(item) || item.length < 3 || item[0] !== "wrb.fr") continue;
1843
- const innerJson = item[2];
1844
- if (typeof innerJson !== "string") continue;
1845
- let innerData;
1846
- try {
1847
- innerData = JSON.parse(innerJson);
1848
- } catch {
1849
- continue;
1850
- }
1851
- if (!Array.isArray(innerData) || !innerData.length) continue;
1852
- const first = innerData[0];
1853
- if (!Array.isArray(first) || !first.length) continue;
1854
- const answerText = first[0];
1855
- if (typeof answerText !== "string" || !answerText) continue;
1856
- const typeInfo = first[4];
1857
- const isAnswer = Array.isArray(typeInfo) && typeInfo.length > 0 && typeInfo[typeInfo.length - 1] === 1;
1858
- const convData = first[2];
1859
- if (!serverConvId && Array.isArray(convData) && convData.length > 0 && typeof convData[0] === "string") {
1860
- serverConvId = convData[0];
1861
- }
1862
- if (Array.isArray(typeInfo) && typeInfo.length > 3) {
1863
- const citations = typeInfo[3];
1864
- if (Array.isArray(citations)) {
1865
- for (const cite of citations) {
1866
- const sourceId = extractUuid(cite);
1867
- if (sourceId) {
1868
- references.push({ sourceId, title: null, url: null });
1565
+ if (Array.isArray(result[0]) && Array.isArray(result[0][0])) {
1566
+ result = result[0];
1567
+ }
1568
+ const parsedTasks = [];
1569
+ for (const taskData of result) {
1570
+ if (!Array.isArray(taskData) || taskData.length < 2) continue;
1571
+ const taskId = taskData[0];
1572
+ const taskInfo = taskData[1];
1573
+ if (typeof taskId !== "string" || !Array.isArray(taskInfo)) continue;
1574
+ const queryText = Array.isArray(taskInfo[1]) ? taskInfo[1][0] ?? "" : "";
1575
+ const sourcesAndSummary = Array.isArray(taskInfo[3]) ? taskInfo[3] : [];
1576
+ const statusCode = typeof taskInfo[4] === "number" ? taskInfo[4] : null;
1577
+ const sourcesData = Array.isArray(sourcesAndSummary[0]) ? sourcesAndSummary[0] : [];
1578
+ const summary = typeof sourcesAndSummary[1] === "string" ? sourcesAndSummary[1] : "";
1579
+ const parsedSources = [];
1580
+ let report = "";
1581
+ for (const src of sourcesData) {
1582
+ if (!Array.isArray(src) || src.length < 2) continue;
1583
+ let url = "";
1584
+ let title = "";
1585
+ let sourceReport = "";
1586
+ let resultType = parseResultType(src.length > 3 ? src[3] : 1);
1587
+ if (src[0] === null) {
1588
+ if (Array.isArray(src[1]) && src[1].length >= 2 && typeof src[1][0] === "string" && typeof src[1][1] === "string") {
1589
+ title = src[1][0];
1590
+ sourceReport = src[1][1];
1591
+ if (resultType === 1) resultType = 5;
1592
+ } else if (typeof src[1] === "string") {
1593
+ title = src[1];
1594
+ if (resultType === 1) resultType = 5;
1595
+ }
1596
+ } else {
1597
+ url = typeof src[0] === "string" ? src[0] : "";
1598
+ title = src.length > 1 && typeof src[1] === "string" ? src[1] : "";
1599
+ }
1600
+ if (title || url) {
1601
+ const parsed = { url, title, resultType, researchTaskId: taskId };
1602
+ if (sourceReport) parsed.reportMarkdown = sourceReport;
1603
+ parsedSources.push(parsed);
1604
+ if (!report && sourceReport) {
1605
+ report = sourceReport;
1606
+ } else if (!report) {
1607
+ const legacyReport = extractLegacyReport(src);
1608
+ if (legacyReport) {
1609
+ report = legacyReport;
1610
+ parsed.reportMarkdown = legacyReport;
1869
1611
  }
1870
1612
  }
1871
1613
  }
1872
1614
  }
1873
- if (isAnswer && answerText.length > bestMarkedAnswer.length) {
1874
- bestMarkedAnswer = answerText;
1875
- } else if (!isAnswer && answerText.length > bestUnmarkedAnswer.length) {
1876
- bestUnmarkedAnswer = answerText;
1877
- }
1615
+ const status = statusCode === 2 || statusCode === 6 ? "completed" : "in_progress";
1616
+ parsedTasks.push({
1617
+ taskId,
1618
+ status,
1619
+ query: queryText,
1620
+ sources: parsedSources,
1621
+ summary,
1622
+ report,
1623
+ tasks: []
1624
+ });
1625
+ }
1626
+ if (parsedTasks.length > 0) {
1627
+ return { ...parsedTasks[0], tasks: parsedTasks };
1878
1628
  }
1629
+ return emptyResult();
1879
1630
  }
1880
- let i = 0;
1881
- while (i < lines.length) {
1882
- const line = (lines[i] ?? "").trim();
1883
- if (!line) {
1884
- i++;
1885
- continue;
1631
+ /**
1632
+ * Import selected research sources into the notebook.
1633
+ * Pass sources from poll() web sources need `url`, deep research report entries
1634
+ * need `reportMarkdown` (resultType=5).
1635
+ *
1636
+ * Note: The API may return fewer items than imported. Use sources.list() to verify.
1637
+ */
1638
+ async importSources(notebookId, taskId, sources) {
1639
+ if (!sources.length) return [];
1640
+ const taskIds = new Set(sources.map((s) => s.researchTaskId).filter(Boolean));
1641
+ if (taskIds.size > 1)
1642
+ throw new Error("Cannot import sources from multiple research tasks in one batch.");
1643
+ const effectiveTaskId = taskIds.size === 1 ? [...taskIds][0] : taskId;
1644
+ const reportSources = sources.filter((s) => s.resultType === 5 && s.title && s.reportMarkdown);
1645
+ const reportSourceSet = new Set(reportSources);
1646
+ const webSources = sources.filter((s) => s.url && !reportSourceSet.has(s));
1647
+ if (!webSources.length && !reportSources.length) return [];
1648
+ const sourceArray = [
1649
+ ...reportSources.map((s) => buildReportEntry(s.title, s.reportMarkdown)),
1650
+ ...webSources.map((s) => buildWebEntry(s.url, s.title))
1651
+ ];
1652
+ const params = [null, [1], effectiveTaskId, notebookId, sourceArray];
1653
+ let result = await this.rpc.call(RPCMethod.IMPORT_RESEARCH, params, {
1654
+ sourcePath: `/notebook/${notebookId}`,
1655
+ allowNull: true
1656
+ });
1657
+ if (!Array.isArray(result)) return [];
1658
+ if (result.length > 0 && Array.isArray(result[0]) && Array.isArray(result[0][0])) {
1659
+ result = result[0];
1886
1660
  }
1887
- if (/^\d+$/.test(line)) {
1888
- i++;
1889
- const next = lines[i];
1890
- if (next !== void 0) processChunk(next);
1891
- i++;
1892
- } else {
1893
- processChunk(line);
1894
- i++;
1661
+ const imported = [];
1662
+ for (const srcData of result) {
1663
+ if (!Array.isArray(srcData) || srcData.length < 2) continue;
1664
+ const first = srcData[0];
1665
+ const srcId = Array.isArray(first) && first.length > 0 ? first[0] : null;
1666
+ if (srcId) imported.push({ id: srcId, title: srcData[1] });
1895
1667
  }
1668
+ return imported;
1669
+ }
1670
+ };
1671
+ function parseResultType(value) {
1672
+ if (typeof value === "number") return value;
1673
+ if (typeof value === "string") {
1674
+ const aliases = { web: 1, drive: 2, report: 5 };
1675
+ return aliases[value.toLowerCase()] ?? 1;
1896
1676
  }
1677
+ return 1;
1678
+ }
1679
+ function extractLegacyReport(src) {
1680
+ if (src.length <= 6 || !Array.isArray(src[6])) return "";
1681
+ return src[6].filter((c) => typeof c === "string" && !!c).join("\n\n");
1682
+ }
1683
+ function buildReportEntry(title, markdown) {
1684
+ return [null, [title, markdown], null, 3, null, null, null, null, null, null, 3];
1685
+ }
1686
+ function buildWebEntry(url, title) {
1687
+ return [null, null, [url, title], null, null, null, null, null, null, null, 2];
1688
+ }
1689
+ function emptyResult() {
1897
1690
  return {
1898
- answer: bestMarkedAnswer || bestUnmarkedAnswer,
1899
- conversationId: serverConvId,
1900
- references
1691
+ taskId: null,
1692
+ status: "no_research",
1693
+ query: "",
1694
+ sources: [],
1695
+ summary: "",
1696
+ report: "",
1697
+ tasks: []
1901
1698
  };
1902
1699
  }
1903
- function extractUuid(data, depth = 8) {
1904
- if (depth <= 0 || data == null) return null;
1905
- if (typeof data === "string") return UUID_RE.test(data) ? data : null;
1906
- if (Array.isArray(data)) {
1907
- for (const item of data) {
1908
- const found = extractUuid(item, depth - 1);
1909
- if (found) return found;
1910
- }
1700
+
1701
+ // src/api/settings.ts
1702
+ init_enums();
1703
+ var SettingsAPI = class {
1704
+ constructor(rpc) {
1705
+ this.rpc = rpc;
1911
1706
  }
1912
- return null;
1913
- }
1914
- function randomUUID() {
1915
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1916
- return crypto.randomUUID();
1707
+ /** Get the current output language setting (e.g. "en", "ja", "zh_Hans"). */
1708
+ async getOutputLanguage() {
1709
+ const params = [null, [1, null, null, null, null, null, null, null, null, null, [1]]];
1710
+ const result = await this.rpc.call(RPCMethod.GET_USER_SETTINGS, params, {
1711
+ sourcePath: "/",
1712
+ allowNull: true
1713
+ });
1714
+ return extractNested(result, [0, 2, 4, 0]);
1715
+ }
1716
+ /**
1717
+ * Set the output language for artifact generation.
1718
+ * Pass a BCP-47 language code, e.g. "en", "ja", "zh_Hans".
1719
+ * Returns the language that was set, or null if the response couldn't be parsed.
1720
+ */
1721
+ async setOutputLanguage(language) {
1722
+ if (!language) return null;
1723
+ const params = [[[null, [[null, null, null, null, [language]]]]]];
1724
+ const result = await this.rpc.call(RPCMethod.SET_USER_SETTINGS, params, {
1725
+ sourcePath: "/",
1726
+ allowNull: true
1727
+ });
1728
+ return extractNested(result, [2, 4, 0]);
1729
+ }
1730
+ };
1731
+ function extractNested(data, path) {
1732
+ try {
1733
+ let cur = data;
1734
+ for (const idx of path) {
1735
+ if (!Array.isArray(cur)) return null;
1736
+ cur = cur[idx];
1737
+ }
1738
+ return typeof cur === "string" && cur ? cur : null;
1739
+ } catch {
1740
+ return null;
1917
1741
  }
1918
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1919
- const r = Math.random() * 16 | 0;
1920
- return (c === "x" ? r : r & 3 | 8).toString(16);
1921
- });
1922
1742
  }
1923
1743
 
1924
- // src/api/notes.ts
1744
+ // src/api/sharing.ts
1925
1745
  init_enums();
1926
- var NotesAPI = class {
1746
+ var SharingAPI = class {
1927
1747
  constructor(rpc) {
1928
1748
  this.rpc = rpc;
1929
1749
  }
1930
- async list(notebookId) {
1750
+ /** Get current sharing configuration for a notebook. */
1751
+ async getStatus(notebookId) {
1931
1752
  const params = [notebookId, [2]];
1932
- const result = await this.rpc.call(RPCMethod.GET_NOTES_AND_MIND_MAPS, params, {
1933
- sourcePath: `/notebook/${notebookId}`
1753
+ const result = await this.rpc.call(RPCMethod.GET_SHARE_STATUS, params, {
1754
+ sourcePath: `/notebook/${notebookId}`,
1755
+ allowNull: true
1934
1756
  });
1935
- const notes = [];
1936
- const mindMaps = [];
1937
- if (!Array.isArray(result)) return { notes, mindMaps };
1938
- try {
1939
- const notesData = result[0];
1940
- if (Array.isArray(notesData)) {
1941
- for (const n of notesData) {
1942
- if (Array.isArray(n)) notes.push(parseNote(n));
1943
- }
1944
- }
1945
- const mapsData = result[1];
1946
- if (Array.isArray(mapsData)) {
1947
- for (const m of mapsData) {
1948
- if (Array.isArray(m)) {
1949
- mindMaps.push({
1950
- id: typeof m[0] === "string" ? m[0] : "",
1951
- title: typeof m[2] === "string" ? m[2] : null,
1952
- content: typeof m[1] === "string" ? m[1] : "",
1953
- createdAt: Array.isArray(m[3]) && typeof m[3][0] === "number" ? new Date(m[3][0] * 1e3) : null
1954
- });
1955
- }
1956
- }
1957
- }
1958
- } catch {
1959
- }
1960
- return { notes, mindMaps };
1757
+ return parseShareStatus(result, notebookId);
1961
1758
  }
1962
- async create(notebookId, content, title) {
1963
- const params = [notebookId, content, title ?? null, [2]];
1964
- const result = await this.rpc.call(RPCMethod.CREATE_NOTE, params, {
1965
- sourcePath: `/notebook/${notebookId}`
1759
+ /** Enable or disable public link sharing. Returns updated status. */
1760
+ async setPublic(notebookId, isPublic) {
1761
+ const access = isPublic ? ShareAccess.ANYONE_WITH_LINK : ShareAccess.RESTRICTED;
1762
+ const params = [[[notebookId, null, [access], [access, ""]]], 1, null, [2]];
1763
+ await this.rpc.call(RPCMethod.SHARE_NOTEBOOK, params, {
1764
+ sourcePath: `/notebook/${notebookId}`,
1765
+ allowNull: true
1966
1766
  });
1967
- if (Array.isArray(result)) return parseNote(result);
1968
- throw new Error("Could not parse note creation response");
1767
+ return this.getStatus(notebookId);
1969
1768
  }
1970
- async update(notebookId, noteId, content, title) {
1971
- const params = [notebookId, noteId, content, title ?? null, [2]];
1972
- const result = await this.rpc.call(RPCMethod.UPDATE_NOTE, params, {
1973
- sourcePath: `/notebook/${notebookId}`
1769
+ /**
1770
+ * Set what viewers can access: full notebook or chat only.
1771
+ * Note: GET_SHARE_STATUS doesn't return view_level, so it's inferred from what was set.
1772
+ */
1773
+ async setViewLevel(notebookId, level) {
1774
+ const params = [notebookId, [[null, null, null, null, null, null, null, null, [[level]]]]];
1775
+ await this.rpc.call(RPCMethod.RENAME_NOTEBOOK, params, {
1776
+ sourcePath: `/notebook/${notebookId}`,
1777
+ allowNull: true
1974
1778
  });
1975
- if (Array.isArray(result)) return parseNote(result);
1976
- return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
1779
+ const status = await this.getStatus(notebookId);
1780
+ return { ...status, viewLevel: level };
1781
+ }
1782
+ /** Share notebook with a user. Returns updated status. */
1783
+ async addUser(notebookId, email, permission = SharePermission.VIEWER, opts = {}) {
1784
+ if (permission === SharePermission.OWNER) throw new Error("Cannot assign OWNER permission");
1785
+ if (permission === SharePermission._REMOVE) throw new Error("Use removeUser() instead");
1786
+ const { notify = true, welcomeMessage = "" } = opts;
1787
+ const messageFlag = welcomeMessage ? 0 : 1;
1788
+ const notifyFlag = notify ? 1 : 0;
1789
+ const params = [
1790
+ [[notebookId, [[email, null, permission]], null, [messageFlag, welcomeMessage]]],
1791
+ notifyFlag,
1792
+ null,
1793
+ [2]
1794
+ ];
1795
+ await this.rpc.call(RPCMethod.SHARE_NOTEBOOK, params, {
1796
+ sourcePath: `/notebook/${notebookId}`,
1797
+ allowNull: true
1798
+ });
1799
+ return this.getStatus(notebookId);
1800
+ }
1801
+ /** Update an existing user's permission level. Returns updated status. */
1802
+ async updateUser(notebookId, email, permission) {
1803
+ return this.addUser(notebookId, email, permission, { notify: false });
1977
1804
  }
1978
- async delete(notebookId, noteId) {
1979
- const params = [notebookId, noteId, [2]];
1980
- await this.rpc.call(RPCMethod.DELETE_NOTE, params, {
1805
+ /** Remove a user's access to the notebook. Returns updated status. */
1806
+ async removeUser(notebookId, email) {
1807
+ const params = [
1808
+ [[notebookId, [[email, null, SharePermission._REMOVE]], null, [0, ""]]],
1809
+ 0,
1810
+ null,
1811
+ [2]
1812
+ ];
1813
+ await this.rpc.call(RPCMethod.SHARE_NOTEBOOK, params, {
1981
1814
  sourcePath: `/notebook/${notebookId}`,
1982
1815
  allowNull: true
1983
1816
  });
1984
- return true;
1817
+ return this.getStatus(notebookId);
1985
1818
  }
1986
1819
  };
1820
+ var PERM_MAP = {
1821
+ 1: "owner",
1822
+ 2: "editor",
1823
+ 3: "viewer"
1824
+ };
1825
+ function parseSharedUser(data) {
1826
+ const email = typeof data[0] === "string" ? data[0] : "";
1827
+ const permCode = typeof data[1] === "number" ? data[1] : 3;
1828
+ const permission = PERM_MAP[permCode] ?? "viewer";
1829
+ let displayName = null;
1830
+ let avatarUrl = null;
1831
+ if (Array.isArray(data[3])) {
1832
+ const info = data[3];
1833
+ displayName = typeof info[0] === "string" ? info[0] : null;
1834
+ avatarUrl = typeof info[1] === "string" ? info[1] : null;
1835
+ }
1836
+ return { email, permission, displayName, avatarUrl };
1837
+ }
1838
+ function parseShareStatus(data, notebookId) {
1839
+ const users = [];
1840
+ if (Array.isArray(data[0])) {
1841
+ for (const entry of data[0]) {
1842
+ if (Array.isArray(entry)) users.push(parseSharedUser(entry));
1843
+ }
1844
+ }
1845
+ const isPublic = Array.isArray(data[1]) && data[1][0] === true;
1846
+ const access = isPublic ? ShareAccess.ANYONE_WITH_LINK : ShareAccess.RESTRICTED;
1847
+ const shareUrl = isPublic ? `https://notebooklm.google.com/notebook/${notebookId}` : null;
1848
+ return {
1849
+ notebookId,
1850
+ isPublic,
1851
+ access,
1852
+ viewLevel: ShareViewLevel.FULL_NOTEBOOK,
1853
+ sharedUsers: users,
1854
+ shareUrl
1855
+ };
1856
+ }
1987
1857
 
1988
- // src/api/research.ts
1858
+ // src/api/sources.ts
1989
1859
  init_enums();
1990
- var ResearchAPI = class {
1991
- constructor(rpc) {
1860
+ init_errors();
1861
+ var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
1862
+ var SourcesAPI = class {
1863
+ constructor(rpc, auth) {
1992
1864
  this.rpc = rpc;
1865
+ this.auth = auth;
1993
1866
  }
1994
- /**
1995
- * Start a research session.
1996
- * @param source "web" or "drive"
1997
- * @param mode "fast" or "deep" (deep only available for web)
1998
- */
1999
- async start(notebookId, query, source = "web", mode = "fast") {
2000
- if (mode === "deep" && source === "drive") {
2001
- throw new Error("Deep research only supports web sources.");
2002
- }
2003
- const sourceType = source === "web" ? 1 : 2;
2004
- let rpcId;
1867
+ async list(notebookId) {
1868
+ const params = [notebookId, null, [2], null, 0];
1869
+ const notebook = await this.rpc.call(RPCMethod.GET_NOTEBOOK, params, {
1870
+ sourcePath: `/notebook/${notebookId}`
1871
+ });
1872
+ if (!Array.isArray(notebook) || !notebook.length) return [];
1873
+ const nbInfo = notebook[0];
1874
+ if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return [];
1875
+ const sourcesList = nbInfo[1];
1876
+ if (!Array.isArray(sourcesList)) return [];
1877
+ return sourcesList.filter((s) => Array.isArray(s) && s.length > 0).map((s) => parseSource(s));
1878
+ }
1879
+ async get(notebookId, sourceId) {
1880
+ const sources = await this.list(notebookId);
1881
+ return sources.find((s) => s.id === sourceId) ?? null;
1882
+ }
1883
+ async addUrl(notebookId, url, opts = {}) {
1884
+ const isYouTube = url.includes("youtube.com") || url.includes("youtu.be");
2005
1885
  let params;
2006
- if (mode === "fast") {
2007
- rpcId = RPCMethod.START_FAST_RESEARCH;
2008
- params = [[query, sourceType], null, 1, notebookId];
1886
+ if (isYouTube) {
1887
+ params = [
1888
+ [[null, null, null, null, null, null, null, [url], null, null, 1]],
1889
+ notebookId,
1890
+ [2],
1891
+ [1, null, null, null, null, null, null, null, null, null, [1]]
1892
+ ];
2009
1893
  } else {
2010
- rpcId = RPCMethod.START_DEEP_RESEARCH;
2011
- params = [null, [1], [query, sourceType], 5, notebookId];
1894
+ params = [[[null, null, [url], null, null, null, null, null]], notebookId, [2], null, null];
2012
1895
  }
2013
- const result = await this.rpc.call(rpcId, params, {
1896
+ const result = await this.rpc.call(RPCMethod.ADD_SOURCE, params, {
2014
1897
  sourcePath: `/notebook/${notebookId}`,
2015
- allowNull: true
1898
+ allowNull: isYouTube
2016
1899
  });
2017
- if (Array.isArray(result) && result.length > 0) {
2018
- return {
2019
- taskId: result[0],
2020
- reportId: result.length > 1 ? result[1] : null,
2021
- notebookId,
2022
- query,
2023
- mode
2024
- };
1900
+ const sourceId = extractSourceId(result);
1901
+ if (opts.waitUntilReady) {
1902
+ return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
2025
1903
  }
2026
- return null;
1904
+ return {
1905
+ id: sourceId,
1906
+ title: url,
1907
+ url,
1908
+ kind: "web_page",
1909
+ createdAt: null,
1910
+ status: "processing",
1911
+ _typeCode: null
1912
+ };
2027
1913
  }
2028
- /** Poll for current research results in a notebook. */
2029
- async poll(notebookId) {
2030
- const params = [null, null, notebookId];
2031
- let result = await this.rpc.call(RPCMethod.POLL_RESEARCH, params, {
1914
+ async addText(notebookId, text, title, opts = {}) {
1915
+ const params = [
1916
+ [[null, [title ?? "", text], null, null, null, null, null, null]],
1917
+ notebookId,
1918
+ [2],
1919
+ null,
1920
+ null
1921
+ ];
1922
+ const result = await this.rpc.call(RPCMethod.ADD_SOURCE, params, {
1923
+ sourcePath: `/notebook/${notebookId}`
1924
+ });
1925
+ const sourceId = extractSourceId(result);
1926
+ if (opts.waitUntilReady) {
1927
+ return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1928
+ }
1929
+ return {
1930
+ id: sourceId,
1931
+ title: title ?? null,
1932
+ url: null,
1933
+ kind: "pasted_text",
1934
+ createdAt: null,
1935
+ status: "processing",
1936
+ _typeCode: null
1937
+ };
1938
+ }
1939
+ async addFile(notebookId, filePath, mimeType, opts = {}) {
1940
+ const fileData = readFileSync(filePath);
1941
+ const fileName = filePath.split("/").pop() ?? "file";
1942
+ return this.addFileBuffer(notebookId, fileData, fileName, mimeType, opts);
1943
+ }
1944
+ async addFileBuffer(notebookId, data, fileName, mimeType, opts = {}) {
1945
+ const params = [
1946
+ [[fileName]],
1947
+ notebookId,
1948
+ [2],
1949
+ [1, null, null, null, null, null, null, null, null, null, [1]]
1950
+ ];
1951
+ const result = await this.rpc.call(RPCMethod.ADD_SOURCE_FILE, params, {
2032
1952
  sourcePath: `/notebook/${notebookId}`,
2033
1953
  allowNull: true
2034
1954
  });
2035
- if (!Array.isArray(result) || !result.length) {
2036
- return emptyResult();
1955
+ const sourceId = extractSourceId(result);
1956
+ const uploadUrl = await this.startResumableUpload(notebookId, fileName, data.length, sourceId);
1957
+ await this.uploadFile(uploadUrl, data);
1958
+ if (opts.waitUntilReady) {
1959
+ return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
2037
1960
  }
2038
- if (Array.isArray(result[0]) && Array.isArray(result[0][0])) {
2039
- result = result[0];
1961
+ return {
1962
+ id: sourceId,
1963
+ title: fileName,
1964
+ url: null,
1965
+ kind: "pdf",
1966
+ // Defaults to generic kind until ready
1967
+ createdAt: null,
1968
+ status: "processing",
1969
+ _typeCode: null
1970
+ };
1971
+ }
1972
+ async startResumableUpload(notebookId, fileName, fileSize, sourceId) {
1973
+ const startResp = await fetch(`${UPLOAD_URL}?authuser=0`, {
1974
+ method: "POST",
1975
+ headers: {
1976
+ Accept: "*/*",
1977
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1978
+ Cookie: this.auth.cookieHeader,
1979
+ Origin: "https://notebooklm.google.com",
1980
+ Referer: "https://notebooklm.google.com/",
1981
+ "x-goog-authuser": "0",
1982
+ "x-goog-upload-command": "start",
1983
+ "x-goog-upload-header-content-length": String(fileSize),
1984
+ "x-goog-upload-protocol": "resumable"
1985
+ },
1986
+ body: JSON.stringify({
1987
+ PROJECT_ID: notebookId,
1988
+ SOURCE_NAME: fileName,
1989
+ SOURCE_ID: sourceId
1990
+ })
1991
+ });
1992
+ if (!startResp.ok) {
1993
+ throw new Error(`Upload initiation failed: HTTP ${startResp.status}`);
2040
1994
  }
2041
- const parsedTasks = [];
2042
- for (const taskData of result) {
2043
- if (!Array.isArray(taskData) || taskData.length < 2) continue;
2044
- const taskId = taskData[0];
2045
- const taskInfo = taskData[1];
2046
- if (typeof taskId !== "string" || !Array.isArray(taskInfo)) continue;
2047
- const queryText = Array.isArray(taskInfo[1]) ? taskInfo[1][0] ?? "" : "";
2048
- const sourcesAndSummary = Array.isArray(taskInfo[3]) ? taskInfo[3] : [];
2049
- const statusCode = typeof taskInfo[4] === "number" ? taskInfo[4] : null;
2050
- const sourcesData = Array.isArray(sourcesAndSummary[0]) ? sourcesAndSummary[0] : [];
2051
- const summary = typeof sourcesAndSummary[1] === "string" ? sourcesAndSummary[1] : "";
2052
- const parsedSources = [];
2053
- let report = "";
2054
- for (const src of sourcesData) {
2055
- if (!Array.isArray(src) || src.length < 2) continue;
2056
- let url = "";
2057
- let title = "";
2058
- let sourceReport = "";
2059
- let resultType = parseResultType(src.length > 3 ? src[3] : 1);
2060
- if (src[0] === null) {
2061
- if (Array.isArray(src[1]) && src[1].length >= 2 && typeof src[1][0] === "string" && typeof src[1][1] === "string") {
2062
- title = src[1][0];
2063
- sourceReport = src[1][1];
2064
- if (resultType === 1) resultType = 5;
2065
- } else if (typeof src[1] === "string") {
2066
- title = src[1];
2067
- if (resultType === 1) resultType = 5;
2068
- }
2069
- } else {
2070
- url = typeof src[0] === "string" ? src[0] : "";
2071
- title = src.length > 1 && typeof src[1] === "string" ? src[1] : "";
2072
- }
2073
- if (title || url) {
2074
- const parsed = { url, title, resultType, researchTaskId: taskId };
2075
- if (sourceReport) parsed.reportMarkdown = sourceReport;
2076
- parsedSources.push(parsed);
2077
- if (!report && sourceReport) {
2078
- report = sourceReport;
2079
- } else if (!report) {
2080
- const legacyReport = extractLegacyReport(src);
2081
- if (legacyReport) {
2082
- report = legacyReport;
2083
- parsed.reportMarkdown = legacyReport;
2084
- }
2085
- }
2086
- }
2087
- }
2088
- const status = statusCode === 2 || statusCode === 6 ? "completed" : "in_progress";
2089
- parsedTasks.push({
2090
- taskId,
2091
- status,
2092
- query: queryText,
2093
- sources: parsedSources,
2094
- summary,
2095
- report,
2096
- tasks: []
2097
- });
1995
+ const uploadSessionUrl = startResp.headers.get("x-goog-upload-url");
1996
+ if (!uploadSessionUrl) {
1997
+ throw new Error("Failed to get upload URL from response headers");
2098
1998
  }
2099
- if (parsedTasks.length > 0) {
2100
- return { ...parsedTasks[0], tasks: parsedTasks };
1999
+ return uploadSessionUrl;
2000
+ }
2001
+ async uploadFile(uploadUrl, data) {
2002
+ const uploadResp = await fetch(uploadUrl, {
2003
+ method: "POST",
2004
+ headers: {
2005
+ Accept: "*/*",
2006
+ "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
2007
+ Cookie: this.auth.cookieHeader,
2008
+ Origin: "https://notebooklm.google.com",
2009
+ Referer: "https://notebooklm.google.com/",
2010
+ "X-Goog-Upload-Command": "upload, finalize",
2011
+ "X-Goog-Upload-Offset": "0"
2012
+ },
2013
+ body: new Uint8Array(data)
2014
+ });
2015
+ if (!uploadResp.ok) {
2016
+ throw new Error(`File upload failed: HTTP ${uploadResp.status}`);
2101
2017
  }
2102
- return emptyResult();
2018
+ const uploadResult = await uploadResp.text();
2019
+ return uploadResult.trim();
2020
+ }
2021
+ async delete(notebookId, sourceId) {
2022
+ const params = [notebookId, [sourceId], [2]];
2023
+ await this.rpc.call(RPCMethod.DELETE_SOURCE, params, {
2024
+ sourcePath: `/notebook/${notebookId}`,
2025
+ allowNull: true
2026
+ });
2027
+ return true;
2103
2028
  }
2104
- /**
2105
- * Import selected research sources into the notebook.
2106
- * Pass sources from poll() web sources need `url`, deep research report entries
2107
- * need `reportMarkdown` (resultType=5).
2108
- *
2109
- * Note: The API may return fewer items than imported. Use sources.list() to verify.
2110
- */
2111
- async importSources(notebookId, taskId, sources) {
2112
- if (!sources.length) return [];
2113
- const taskIds = new Set(sources.map((s) => s.researchTaskId).filter(Boolean));
2114
- if (taskIds.size > 1)
2115
- throw new Error("Cannot import sources from multiple research tasks in one batch.");
2116
- const effectiveTaskId = taskIds.size === 1 ? [...taskIds][0] : taskId;
2117
- const reportSources = sources.filter((s) => s.resultType === 5 && s.title && s.reportMarkdown);
2118
- const reportSourceSet = new Set(reportSources);
2119
- const webSources = sources.filter((s) => s.url && !reportSourceSet.has(s));
2120
- if (!webSources.length && !reportSources.length) return [];
2121
- const sourceArray = [
2122
- ...reportSources.map((s) => buildReportEntry(s.title, s.reportMarkdown)),
2123
- ...webSources.map((s) => buildWebEntry(s.url, s.title))
2124
- ];
2125
- const params = [null, [1], effectiveTaskId, notebookId, sourceArray];
2126
- let result = await this.rpc.call(RPCMethod.IMPORT_RESEARCH, params, {
2029
+ async refresh(notebookId, sourceId) {
2030
+ const params = [notebookId, sourceId, [2]];
2031
+ await this.rpc.call(RPCMethod.REFRESH_SOURCE, params, {
2127
2032
  sourcePath: `/notebook/${notebookId}`,
2128
2033
  allowNull: true
2129
2034
  });
2130
- if (!Array.isArray(result)) return [];
2131
- if (result.length > 0 && Array.isArray(result[0]) && Array.isArray(result[0][0])) {
2132
- result = result[0];
2133
- }
2134
- const imported = [];
2135
- for (const srcData of result) {
2136
- if (!Array.isArray(srcData) || srcData.length < 2) continue;
2137
- const first = srcData[0];
2138
- const srcId = Array.isArray(first) && first.length > 0 ? first[0] : null;
2139
- if (srcId) imported.push({ id: srcId, title: srcData[1] });
2035
+ return true;
2036
+ }
2037
+ async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2038
+ const deadline = Date.now() + timeout * 1e3;
2039
+ let interval = initialInterval;
2040
+ let lastStatus;
2041
+ while (Date.now() < deadline) {
2042
+ const source = await this.get(notebookId, sourceId);
2043
+ if (source) {
2044
+ if (source.status === "ready") return source;
2045
+ if (source.status === "error") {
2046
+ throw new SourceProcessingError(sourceId, 3);
2047
+ }
2048
+ lastStatus = source._typeCode ?? void 0;
2049
+ }
2050
+ await sleep2(interval * 1e3);
2051
+ interval = Math.min(interval * backoffFactor, maxInterval);
2140
2052
  }
2141
- return imported;
2053
+ throw new SourceTimeoutError(sourceId, timeout, lastStatus);
2142
2054
  }
2143
2055
  };
2144
- function parseResultType(value) {
2145
- if (typeof value === "number") return value;
2146
- if (typeof value === "string") {
2147
- const aliases = { web: 1, drive: 2, report: 5 };
2148
- return aliases[value.toLowerCase()] ?? 1;
2056
+ function extractSourceId(result) {
2057
+ if (Array.isArray(result)) {
2058
+ let current = result;
2059
+ while (Array.isArray(current) && current.length > 0) {
2060
+ if (typeof current[0] === "string") {
2061
+ if (current[0].length > 8) {
2062
+ return current[0];
2063
+ }
2064
+ }
2065
+ current = current[0];
2066
+ }
2067
+ for (const item of result) {
2068
+ if (typeof item === "string" && item.length > 8) return item;
2069
+ }
2149
2070
  }
2150
- return 1;
2071
+ if (typeof result === "string") return result;
2072
+ console.log("extractSourceId debug info: could not parse:", JSON.stringify(result, null, 2));
2073
+ throw new Error("Could not extract source ID from API response");
2151
2074
  }
2152
- function extractLegacyReport(src) {
2153
- if (src.length <= 6 || !Array.isArray(src[6])) return "";
2154
- return src[6].filter((c) => typeof c === "string" && !!c).join("\n\n");
2075
+ function sleep2(ms) {
2076
+ return new Promise((resolve) => setTimeout(resolve, ms));
2155
2077
  }
2156
- function buildReportEntry(title, markdown) {
2157
- return [null, [title, markdown], null, 3, null, null, null, null, null, null, 3];
2078
+
2079
+ // src/index.ts
2080
+ init_auth();
2081
+
2082
+ // src/client.ts
2083
+ init_auth();
2084
+
2085
+ // src/rpc/core.ts
2086
+ init_errors();
2087
+
2088
+ // src/rpc/decoder.ts
2089
+ init_errors();
2090
+ function stripAntiXSSI(response) {
2091
+ if (response.startsWith(")]}'")) {
2092
+ const match = /\)\]\}'\r?\n/.exec(response);
2093
+ if (match) return response.slice(match[0].length);
2094
+ }
2095
+ return response;
2158
2096
  }
2159
- function buildWebEntry(url, title) {
2160
- return [null, null, [url, title], null, null, null, null, null, null, null, 2];
2097
+ function parseChunkedResponse(response) {
2098
+ if (!response || !response.trim()) return [];
2099
+ const chunks = [];
2100
+ let skippedCount = 0;
2101
+ const lines = response.trim().split("\n");
2102
+ let i = 0;
2103
+ while (i < lines.length) {
2104
+ const line = (lines[i] ?? "").trim();
2105
+ if (!line) {
2106
+ i++;
2107
+ continue;
2108
+ }
2109
+ if (/^\d+$/.test(line)) {
2110
+ i++;
2111
+ if (i < lines.length) {
2112
+ try {
2113
+ const chunk = JSON.parse(lines[i] ?? "");
2114
+ chunks.push(chunk);
2115
+ } catch {
2116
+ skippedCount++;
2117
+ }
2118
+ }
2119
+ i++;
2120
+ } else {
2121
+ try {
2122
+ const chunk = JSON.parse(line);
2123
+ chunks.push(chunk);
2124
+ } catch {
2125
+ skippedCount++;
2126
+ }
2127
+ i++;
2128
+ }
2129
+ }
2130
+ if (skippedCount > 0 && lines.length > 0) {
2131
+ const errorRate = skippedCount / lines.length;
2132
+ if (errorRate > 0.1) {
2133
+ throw new RPCError(
2134
+ `Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
2135
+ { rawResponse: response.slice(0, 500) }
2136
+ );
2137
+ }
2138
+ }
2139
+ return chunks;
2161
2140
  }
2162
- function emptyResult() {
2163
- return {
2164
- taskId: null,
2165
- status: "no_research",
2166
- query: "",
2167
- sources: [],
2168
- summary: "",
2169
- report: "",
2170
- tasks: []
2171
- };
2141
+ function containsUserDisplayableError(obj) {
2142
+ if (typeof obj === "string") return obj.includes("UserDisplayableError");
2143
+ if (Array.isArray(obj)) return obj.some(containsUserDisplayableError);
2144
+ if (obj !== null && typeof obj === "object") {
2145
+ return Object.values(obj).some(containsUserDisplayableError);
2146
+ }
2147
+ return false;
2172
2148
  }
2173
-
2174
- // src/api/settings.ts
2175
- init_enums();
2176
- var SettingsAPI = class {
2177
- constructor(rpc) {
2178
- this.rpc = rpc;
2149
+ function collectRPCIds(chunks) {
2150
+ const ids = [];
2151
+ for (const chunk of chunks) {
2152
+ if (!Array.isArray(chunk)) continue;
2153
+ const items = Array.isArray(chunk[0]) ? chunk : [chunk];
2154
+ for (const item of items) {
2155
+ if (!Array.isArray(item) || item.length < 2) continue;
2156
+ if ((item[0] === "wrb.fr" || item[0] === "er") && typeof item[1] === "string") {
2157
+ ids.push(item[1]);
2158
+ }
2159
+ }
2179
2160
  }
2180
- /** Get the current output language setting (e.g. "en", "ja", "zh_Hans"). */
2181
- async getOutputLanguage() {
2182
- const params = [null, [1, null, null, null, null, null, null, null, null, null, [1]]];
2183
- const result = await this.rpc.call(RPCMethod.GET_USER_SETTINGS, params, {
2184
- sourcePath: "/",
2185
- allowNull: true
2186
- });
2187
- return extractNested(result, [0, 2, 4, 0]);
2161
+ return ids;
2162
+ }
2163
+ function extractRPCResult(chunks, rpcId) {
2164
+ for (const chunk of chunks) {
2165
+ if (!Array.isArray(chunk)) continue;
2166
+ const items = Array.isArray(chunk[0]) ? chunk : [chunk];
2167
+ for (const item of items) {
2168
+ if (!Array.isArray(item) || item.length < 3) continue;
2169
+ if (item[0] === "er" && item[1] === rpcId) {
2170
+ const code = item[2];
2171
+ let msg = "Unknown error";
2172
+ if (typeof code === "number") {
2173
+ if (code === 429) msg = "API rate limit exceeded. Please wait before retrying.";
2174
+ else if (code === 401 || code === 403) msg = "Authentication required or forbidden.";
2175
+ else if (code === 404) msg = "Resource not found.";
2176
+ else if (code >= 500) msg = `Server error ${code}. Try again later.`;
2177
+ else msg = `Error code: ${code}`;
2178
+ } else if (typeof code === "string") {
2179
+ msg = code;
2180
+ }
2181
+ throw new RPCError(msg, { methodId: rpcId, rpcCode: code });
2182
+ }
2183
+ if (item[0] === "wrb.fr" && item[1] === rpcId) {
2184
+ const resultData = item[2];
2185
+ if (resultData === null && item.length > 5 && item[5] != null) {
2186
+ if (containsUserDisplayableError(item[5])) {
2187
+ throw new RateLimitError(
2188
+ "API rate limit or quota exceeded. Please wait before retrying.",
2189
+ { methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
2190
+ );
2191
+ }
2192
+ }
2193
+ if (typeof resultData === "string") {
2194
+ try {
2195
+ return JSON.parse(resultData);
2196
+ } catch {
2197
+ return resultData;
2198
+ }
2199
+ }
2200
+ if (resultData === null) {
2201
+ console.log(
2202
+ "decodeResponse debug info: resultData is exactly null. Full item Array:",
2203
+ JSON.stringify(item)
2204
+ );
2205
+ }
2206
+ return resultData;
2207
+ }
2208
+ }
2188
2209
  }
2189
- /**
2190
- * Set the output language for artifact generation.
2191
- * Pass a BCP-47 language code, e.g. "en", "ja", "zh_Hans".
2192
- * Returns the language that was set, or null if the response couldn't be parsed.
2193
- */
2194
- async setOutputLanguage(language) {
2195
- if (!language) return null;
2196
- const params = [[[null, [[null, null, null, null, [language]]]]]];
2197
- const result = await this.rpc.call(RPCMethod.SET_USER_SETTINGS, params, {
2198
- sourcePath: "/",
2199
- allowNull: true
2200
- });
2201
- return extractNested(result, [2, 4, 0]);
2210
+ return void 0;
2211
+ }
2212
+ function decodeResponse(rawResponse, rpcId, allowNull = false) {
2213
+ const cleaned = stripAntiXSSI(rawResponse);
2214
+ const chunks = parseChunkedResponse(cleaned);
2215
+ const responsePreview = cleaned.slice(0, 500);
2216
+ const foundIds = collectRPCIds(chunks);
2217
+ let result;
2218
+ try {
2219
+ result = extractRPCResult(chunks, rpcId);
2220
+ } catch (e) {
2221
+ if (e instanceof RPCError && e.foundIds.length === 0) {
2222
+ throw new RPCError(e.message, {
2223
+ methodId: e.methodId,
2224
+ rpcCode: e.rpcCode,
2225
+ foundIds,
2226
+ rawResponse: responsePreview
2227
+ });
2228
+ }
2229
+ throw e;
2202
2230
  }
2203
- };
2204
- function extractNested(data, path) {
2205
- try {
2206
- let cur = data;
2207
- for (const idx of path) {
2208
- if (!Array.isArray(cur)) return null;
2209
- cur = cur[idx];
2231
+ if (result === void 0 && !allowNull) {
2232
+ if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
2233
+ throw new RPCError(
2234
+ `No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
2235
+ { methodId: rpcId, foundIds, rawResponse: responsePreview }
2236
+ );
2210
2237
  }
2211
- return typeof cur === "string" && cur ? cur : null;
2212
- } catch {
2213
- return null;
2238
+ throw new RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
2239
+ methodId: rpcId,
2240
+ foundIds,
2241
+ rawResponse: responsePreview
2242
+ });
2214
2243
  }
2244
+ return result ?? null;
2215
2245
  }
2216
2246
 
2217
- // src/api/sharing.ts
2218
- init_enums();
2219
- var SharingAPI = class {
2220
- constructor(rpc) {
2221
- this.rpc = rpc;
2222
- }
2223
- /** Get current sharing configuration for a notebook. */
2224
- async getStatus(notebookId) {
2225
- const params = [notebookId, [2]];
2226
- const result = await this.rpc.call(RPCMethod.GET_SHARE_STATUS, params, {
2227
- sourcePath: `/notebook/${notebookId}`,
2228
- allowNull: true
2229
- });
2230
- return parseShareStatus(result, notebookId);
2231
- }
2232
- /** Enable or disable public link sharing. Returns updated status. */
2233
- async setPublic(notebookId, isPublic) {
2234
- const access = isPublic ? ShareAccess.ANYONE_WITH_LINK : ShareAccess.RESTRICTED;
2235
- const params = [[[notebookId, null, [access], [access, ""]]], 1, null, [2]];
2236
- await this.rpc.call(RPCMethod.SHARE_NOTEBOOK, params, {
2237
- sourcePath: `/notebook/${notebookId}`,
2238
- allowNull: true
2239
- });
2240
- return this.getStatus(notebookId);
2241
- }
2242
- /**
2243
- * Set what viewers can access: full notebook or chat only.
2244
- * Note: GET_SHARE_STATUS doesn't return view_level, so it's inferred from what was set.
2245
- */
2246
- async setViewLevel(notebookId, level) {
2247
- const params = [notebookId, [[null, null, null, null, null, null, null, null, [[level]]]]];
2248
- await this.rpc.call(RPCMethod.RENAME_NOTEBOOK, params, {
2249
- sourcePath: `/notebook/${notebookId}`,
2250
- allowNull: true
2251
- });
2252
- const status = await this.getStatus(notebookId);
2253
- return { ...status, viewLevel: level };
2254
- }
2255
- /** Share notebook with a user. Returns updated status. */
2256
- async addUser(notebookId, email, permission = SharePermission.VIEWER, opts = {}) {
2257
- if (permission === SharePermission.OWNER) throw new Error("Cannot assign OWNER permission");
2258
- if (permission === SharePermission._REMOVE) throw new Error("Use removeUser() instead");
2259
- const { notify = true, welcomeMessage = "" } = opts;
2260
- const messageFlag = welcomeMessage ? 0 : 1;
2261
- const notifyFlag = notify ? 1 : 0;
2262
- const params = [
2263
- [[notebookId, [[email, null, permission]], null, [messageFlag, welcomeMessage]]],
2264
- notifyFlag,
2265
- null,
2266
- [2]
2267
- ];
2268
- await this.rpc.call(RPCMethod.SHARE_NOTEBOOK, params, {
2269
- sourcePath: `/notebook/${notebookId}`,
2270
- allowNull: true
2271
- });
2272
- return this.getStatus(notebookId);
2247
+ // src/rpc/encoder.ts
2248
+ function encodeRPCRequest(methodId, params) {
2249
+ const paramsJson = JSON.stringify(params);
2250
+ return [[[methodId, paramsJson, null, "generic"]]];
2251
+ }
2252
+ function buildRequestBody(rpcRequest, csrfToken) {
2253
+ const fReq = encodeURIComponent(JSON.stringify(rpcRequest));
2254
+ const at = encodeURIComponent(csrfToken);
2255
+ return `f.req=${fReq}&at=${at}&`;
2256
+ }
2257
+ function buildUrlParams(methodId, sessionId, sourcePath = "/") {
2258
+ return new URLSearchParams({
2259
+ rpcids: methodId,
2260
+ "source-path": sourcePath,
2261
+ "f.sid": sessionId,
2262
+ hl: "en",
2263
+ rt: "c"
2264
+ });
2265
+ }
2266
+
2267
+ // src/rpc/core.ts
2268
+ var BATCHEXECUTE_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/batchexecute";
2269
+ var DEFAULT_TIMEOUT_MS = 3e4;
2270
+ var RPCCore = class {
2271
+ auth;
2272
+ timeoutMs;
2273
+ constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
2274
+ this.auth = auth;
2275
+ this.timeoutMs = timeoutMs;
2273
2276
  }
2274
- /** Update an existing user's permission level. Returns updated status. */
2275
- async updateUser(notebookId, email, permission) {
2276
- return this.addUser(notebookId, email, permission, { notify: false });
2277
+ async call(methodId, params, opts = {}) {
2278
+ const sourcePath = opts.sourcePath ?? "/";
2279
+ const allowNull = opts.allowNull ?? false;
2280
+ const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
2281
+ const rpcRequest = encodeRPCRequest(methodId, params);
2282
+ const body = buildRequestBody(rpcRequest, this.auth.csrfToken);
2283
+ const urlParams = buildUrlParams(methodId, this.auth.sessionId, sourcePath);
2284
+ const url = `${BATCHEXECUTE_URL}?${urlParams.toString()}`;
2285
+ const controller = new AbortController();
2286
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2287
+ let response;
2288
+ try {
2289
+ response = await fetch(url, {
2290
+ method: "POST",
2291
+ headers: {
2292
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
2293
+ Cookie: this.auth.cookieHeader
2294
+ },
2295
+ body,
2296
+ signal: controller.signal
2297
+ });
2298
+ } catch (e) {
2299
+ clearTimeout(timer);
2300
+ if (e instanceof Error && e.name === "AbortError") {
2301
+ throw new RPCTimeoutError(`Request timed out calling ${methodId}`, {
2302
+ methodId,
2303
+ originalError: e
2304
+ });
2305
+ }
2306
+ throw new NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
2307
+ methodId,
2308
+ originalError: e instanceof Error ? e : void 0
2309
+ });
2310
+ } finally {
2311
+ clearTimeout(timer);
2312
+ }
2313
+ if (!response.ok) {
2314
+ const status = response.status;
2315
+ if (status === 429) {
2316
+ const retryAfterHeader = response.headers.get("retry-after");
2317
+ const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
2318
+ throw new RateLimitError(`API rate limit exceeded calling ${methodId}`, {
2319
+ methodId,
2320
+ retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
2321
+ });
2322
+ }
2323
+ if (status === 401 || status === 403) {
2324
+ throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2325
+ methodId
2326
+ });
2327
+ }
2328
+ if (status >= 500) {
2329
+ throw new ServerError(`Server error ${status} calling ${methodId}`, {
2330
+ methodId,
2331
+ statusCode: status
2332
+ });
2333
+ }
2334
+ if (status >= 400) {
2335
+ throw new ClientError(`Client error ${status} calling ${methodId}`, {
2336
+ methodId,
2337
+ statusCode: status
2338
+ });
2339
+ }
2340
+ throw new RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
2341
+ }
2342
+ const text = await response.text();
2343
+ return decodeResponse(text, methodId, allowNull);
2277
2344
  }
2278
- /** Remove a user's access to the notebook. Returns updated status. */
2279
- async removeUser(notebookId, email) {
2280
- const params = [
2281
- [[notebookId, [[email, null, SharePermission._REMOVE]], null, [0, ""]]],
2282
- 0,
2283
- null,
2284
- [2]
2285
- ];
2286
- await this.rpc.call(RPCMethod.SHARE_NOTEBOOK, params, {
2287
- sourcePath: `/notebook/${notebookId}`,
2288
- allowNull: true
2345
+ /** Extract source IDs from a notebook (used by chat/artifact APIs). */
2346
+ async getSourceIds(notebookId) {
2347
+ const params = [notebookId, null, [2], null, 0];
2348
+ const { RPCMethod: RPCMethod2 } = await Promise.resolve().then(() => (init_enums(), enums_exports));
2349
+ const data = await this.call(RPCMethod2.GET_NOTEBOOK, params, {
2350
+ sourcePath: `/notebook/${notebookId}`
2289
2351
  });
2290
- return this.getStatus(notebookId);
2291
- }
2292
- };
2293
- var PERM_MAP = {
2294
- 1: "owner",
2295
- 2: "editor",
2296
- 3: "viewer"
2297
- };
2298
- function parseSharedUser(data) {
2299
- const email = typeof data[0] === "string" ? data[0] : "";
2300
- const permCode = typeof data[1] === "number" ? data[1] : 3;
2301
- const permission = PERM_MAP[permCode] ?? "viewer";
2302
- let displayName = null;
2303
- let avatarUrl = null;
2304
- if (Array.isArray(data[3])) {
2305
- const info = data[3];
2306
- displayName = typeof info[0] === "string" ? info[0] : null;
2307
- avatarUrl = typeof info[1] === "string" ? info[1] : null;
2308
- }
2309
- return { email, permission, displayName, avatarUrl };
2310
- }
2311
- function parseShareStatus(data, notebookId) {
2312
- const users = [];
2313
- if (Array.isArray(data[0])) {
2314
- for (const entry of data[0]) {
2315
- if (Array.isArray(entry)) users.push(parseSharedUser(entry));
2352
+ const sourceIds = [];
2353
+ if (!Array.isArray(data) || !data.length) return sourceIds;
2354
+ try {
2355
+ const nbInfo = data[0];
2356
+ if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return sourceIds;
2357
+ const sources = nbInfo[1];
2358
+ if (!Array.isArray(sources)) return sourceIds;
2359
+ for (const src of sources) {
2360
+ if (!Array.isArray(src) || !src.length) continue;
2361
+ const first = src[0];
2362
+ if (Array.isArray(first) && first.length > 0 && typeof first[0] === "string") {
2363
+ sourceIds.push(first[0]);
2364
+ }
2365
+ }
2366
+ } catch {
2316
2367
  }
2368
+ return sourceIds;
2317
2369
  }
2318
- const isPublic = Array.isArray(data[1]) && data[1][0] === true;
2319
- const access = isPublic ? ShareAccess.ANYONE_WITH_LINK : ShareAccess.RESTRICTED;
2320
- const shareUrl = isPublic ? `https://notebooklm.google.com/notebook/${notebookId}` : null;
2321
- return {
2322
- notebookId,
2323
- isPublic,
2324
- access,
2325
- viewLevel: ShareViewLevel.FULL_NOTEBOOK,
2326
- sharedUsers: users,
2327
- shareUrl
2328
- };
2329
- }
2370
+ };
2330
2371
 
2331
2372
  // src/client.ts
2332
2373
  var NotebookLMClient = class _NotebookLMClient {
@@ -2370,7 +2411,6 @@ var NotebookLMClient = class _NotebookLMClient {
2370
2411
  };
2371
2412
 
2372
2413
  // src/index.ts
2373
- init_auth();
2374
2414
  init_enums();
2375
2415
  init_errors();
2376
2416