notebooklm-sdk 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -242
- package/dist/auth.cjs +4 -10
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js +4 -10
- package/dist/auth.js.map +1 -1
- package/dist/bin.cjs +4 -10
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +4 -10
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +176 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -20
- package/dist/index.d.ts +34 -20
- package/dist/index.js +176 -79
- package/dist/index.js.map +1 -1
- package/package.json +11 -3
package/dist/index.cjs
CHANGED
|
@@ -455,10 +455,8 @@ function loadCookiesFromFile(filePath) {
|
|
|
455
455
|
try {
|
|
456
456
|
raw = fs.readFileSync(filePath, "utf-8");
|
|
457
457
|
} catch {
|
|
458
|
-
throw new exports.AuthError(
|
|
459
|
-
|
|
460
|
-
Run: npx notebooklm-sdk login`
|
|
461
|
-
);
|
|
458
|
+
throw new exports.AuthError(`Session file not found: ${filePath}
|
|
459
|
+
Run: npx notebooklm-sdk login`);
|
|
462
460
|
}
|
|
463
461
|
return extractCookiesFromStorageState(JSON.parse(raw));
|
|
464
462
|
}
|
|
@@ -592,9 +590,7 @@ async function connect(opts = {}) {
|
|
|
592
590
|
} else if (envCookies) {
|
|
593
591
|
cookieMap = loadCookiesFromString(envCookies);
|
|
594
592
|
} else {
|
|
595
|
-
throw new exports.AuthError(
|
|
596
|
-
"No session found. Run: npx notebooklm-sdk login"
|
|
597
|
-
);
|
|
593
|
+
throw new exports.AuthError("No session found. Run: npx notebooklm-sdk login");
|
|
598
594
|
}
|
|
599
595
|
}
|
|
600
596
|
const { csrfToken, sessionId } = await fetchTokens(cookieMap);
|
|
@@ -729,26 +725,6 @@ function parseArtifact(data, notebookId) {
|
|
|
729
725
|
_raw: Array.isArray(data) ? data : []
|
|
730
726
|
};
|
|
731
727
|
}
|
|
732
|
-
function parseNote(data) {
|
|
733
|
-
const id = typeof data[0] === "string" ? data[0] : "";
|
|
734
|
-
const content = typeof data[1] === "string" ? data[1] : "";
|
|
735
|
-
const title = typeof data[2] === "string" ? data[2] : null;
|
|
736
|
-
let createdAt = null;
|
|
737
|
-
let updatedAt = null;
|
|
738
|
-
if (Array.isArray(data[3]) && typeof data[3][0] === "number") {
|
|
739
|
-
try {
|
|
740
|
-
createdAt = new Date(data[3][0] * 1e3);
|
|
741
|
-
} catch {
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
if (Array.isArray(data[4]) && typeof data[4][0] === "number") {
|
|
745
|
-
try {
|
|
746
|
-
updatedAt = new Date(data[4][0] * 1e3);
|
|
747
|
-
} catch {
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return { id, title, content, createdAt, updatedAt };
|
|
751
|
-
}
|
|
752
728
|
|
|
753
729
|
// src/api/artifacts.ts
|
|
754
730
|
function tripleNest(ids) {
|
|
@@ -758,18 +734,13 @@ function doubleNest(ids) {
|
|
|
758
734
|
return ids.map((id) => [id]);
|
|
759
735
|
}
|
|
760
736
|
var ArtifactsAPI = class {
|
|
761
|
-
constructor(rpc, auth) {
|
|
737
|
+
constructor(rpc, auth, notes) {
|
|
762
738
|
this.rpc = rpc;
|
|
763
739
|
this.auth = auth;
|
|
740
|
+
this.notes = notes;
|
|
764
741
|
}
|
|
765
742
|
async list(notebookId) {
|
|
766
|
-
const
|
|
767
|
-
const result = await this.rpc.call(exports.RPCMethod.LIST_ARTIFACTS, params, {
|
|
768
|
-
sourcePath: `/notebook/${notebookId}`,
|
|
769
|
-
allowNull: true
|
|
770
|
-
});
|
|
771
|
-
if (!Array.isArray(result) || !result.length) return [];
|
|
772
|
-
const rawList = Array.isArray(result[0]) ? result[0] : result;
|
|
743
|
+
const rawList = await this._listRaw(notebookId);
|
|
773
744
|
const artifacts = [];
|
|
774
745
|
for (const item of rawList) {
|
|
775
746
|
if (Array.isArray(item)) {
|
|
@@ -781,6 +752,15 @@ var ArtifactsAPI = class {
|
|
|
781
752
|
}
|
|
782
753
|
return artifacts;
|
|
783
754
|
}
|
|
755
|
+
async _listRaw(notebookId) {
|
|
756
|
+
const params = [[2], notebookId, 'NOT artifact.status = "ARTIFACT_STATUS_SUGGESTED"'];
|
|
757
|
+
const result = await this.rpc.call(exports.RPCMethod.LIST_ARTIFACTS, params, {
|
|
758
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
759
|
+
allowNull: true
|
|
760
|
+
});
|
|
761
|
+
if (!Array.isArray(result) || !result.length) return [];
|
|
762
|
+
return Array.isArray(result[0]) ? result[0] : result;
|
|
763
|
+
}
|
|
784
764
|
async get(notebookId, artifactId) {
|
|
785
765
|
const artifacts = await this.list(notebookId);
|
|
786
766
|
return artifacts.find((a) => a.id === artifactId) ?? null;
|
|
@@ -960,6 +940,37 @@ var ArtifactsAPI = class {
|
|
|
960
940
|
];
|
|
961
941
|
return this._callGenerate(notebookId, params);
|
|
962
942
|
}
|
|
943
|
+
async createDataTable(notebookId, opts = {}) {
|
|
944
|
+
const language = opts.language ?? "en";
|
|
945
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
946
|
+
const triple = tripleNest(sourceIds);
|
|
947
|
+
const params = [
|
|
948
|
+
[2],
|
|
949
|
+
notebookId,
|
|
950
|
+
[
|
|
951
|
+
null,
|
|
952
|
+
null,
|
|
953
|
+
exports.ArtifactTypeCode.DATA_TABLE,
|
|
954
|
+
triple,
|
|
955
|
+
null,
|
|
956
|
+
null,
|
|
957
|
+
null,
|
|
958
|
+
null,
|
|
959
|
+
null,
|
|
960
|
+
null,
|
|
961
|
+
null,
|
|
962
|
+
null,
|
|
963
|
+
null,
|
|
964
|
+
null,
|
|
965
|
+
null,
|
|
966
|
+
null,
|
|
967
|
+
null,
|
|
968
|
+
null,
|
|
969
|
+
[null, [opts.instructions ?? null, language]]
|
|
970
|
+
]
|
|
971
|
+
];
|
|
972
|
+
return this._callGenerate(notebookId, params);
|
|
973
|
+
}
|
|
963
974
|
async createReport(notebookId, opts = {}) {
|
|
964
975
|
const format = opts.format ?? "briefing_doc";
|
|
965
976
|
const language = opts.language ?? "en";
|
|
@@ -1025,7 +1036,15 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1025
1036
|
sourcePath: `/notebook/${notebookId}`,
|
|
1026
1037
|
allowNull: true
|
|
1027
1038
|
});
|
|
1028
|
-
|
|
1039
|
+
const mindMapJson = Array.isArray(result) && Array.isArray(result[0]) && typeof result[0][0] === "string" ? result[0][0] : null;
|
|
1040
|
+
if (!mindMapJson) throw new Error("Mind map generation returned no content");
|
|
1041
|
+
let title = "Mind Map";
|
|
1042
|
+
try {
|
|
1043
|
+
const parsed = JSON.parse(mindMapJson);
|
|
1044
|
+
if (typeof parsed["name"] === "string") title = parsed["name"];
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
return this.notes.create(notebookId, mindMapJson, title);
|
|
1029
1048
|
}
|
|
1030
1049
|
// ---------------------------------------------------------------------------
|
|
1031
1050
|
// Polling / download
|
|
@@ -1081,6 +1100,48 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1081
1100
|
}
|
|
1082
1101
|
return null;
|
|
1083
1102
|
}
|
|
1103
|
+
/** Download a completed slide deck as PDF or PPTX. Returns a Buffer. */
|
|
1104
|
+
async downloadSlideDeck(notebookId, artifactId, format = "pdf") {
|
|
1105
|
+
const rawList = await this._listRaw(notebookId);
|
|
1106
|
+
const raw = rawList.find(
|
|
1107
|
+
(a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.SLIDE_DECK
|
|
1108
|
+
);
|
|
1109
|
+
if (!raw) throw new exports.ArtifactNotReadyError("slide_deck", { artifactId });
|
|
1110
|
+
const metadata = raw[16];
|
|
1111
|
+
if (!Array.isArray(metadata)) throw new exports.ArtifactNotReadyError("slide_deck", { artifactId });
|
|
1112
|
+
const url = format === "pptx" ? metadata[4] : metadata[3];
|
|
1113
|
+
if (typeof url !== "string" || !url.startsWith("http")) {
|
|
1114
|
+
throw new exports.ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
|
|
1115
|
+
}
|
|
1116
|
+
return this._fetchMediaWithCookies(url);
|
|
1117
|
+
}
|
|
1118
|
+
/** Download a completed infographic as PNG. Returns a Buffer. */
|
|
1119
|
+
async downloadInfographic(notebookId, artifactId) {
|
|
1120
|
+
const rawList = await this._listRaw(notebookId);
|
|
1121
|
+
const raw = rawList.find(
|
|
1122
|
+
(a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.INFOGRAPHIC
|
|
1123
|
+
);
|
|
1124
|
+
if (!raw) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
|
|
1125
|
+
let url = null;
|
|
1126
|
+
for (let i = raw.length - 1; i >= 0; i--) {
|
|
1127
|
+
const item = raw[i];
|
|
1128
|
+
if (Array.isArray(item) && Array.isArray(item[2]) && Array.isArray(item[2][0]) && Array.isArray(item[2][0][1]) && typeof item[2][0][1][0] === "string" && item[2][0][1][0].startsWith("http")) {
|
|
1129
|
+
url = item[2][0][1][0];
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (!url) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
|
|
1134
|
+
return this._fetchMediaWithCookies(url);
|
|
1135
|
+
}
|
|
1136
|
+
/** Get parsed headers and rows from a completed data table artifact. */
|
|
1137
|
+
async getDataTableContent(notebookId, artifactId) {
|
|
1138
|
+
const artifacts = await this._listRaw(notebookId);
|
|
1139
|
+
const raw = artifacts.find(
|
|
1140
|
+
(a) => Array.isArray(a) && a[0] === artifactId && a[2] === exports.ArtifactTypeCode.DATA_TABLE
|
|
1141
|
+
);
|
|
1142
|
+
if (!raw || !Array.isArray(raw) || !Array.isArray(raw[18])) return null;
|
|
1143
|
+
return parseDataTable(raw[18]);
|
|
1144
|
+
}
|
|
1084
1145
|
// ---------------------------------------------------------------------------
|
|
1085
1146
|
// Internal
|
|
1086
1147
|
// ---------------------------------------------------------------------------
|
|
@@ -1137,6 +1198,34 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1137
1198
|
return { artifactId: null, status: "failed" };
|
|
1138
1199
|
}
|
|
1139
1200
|
};
|
|
1201
|
+
function extractCellText(cell) {
|
|
1202
|
+
if (typeof cell === "string") return cell;
|
|
1203
|
+
if (typeof cell === "number") return "";
|
|
1204
|
+
if (Array.isArray(cell)) return cell.map(extractCellText).join("");
|
|
1205
|
+
return "";
|
|
1206
|
+
}
|
|
1207
|
+
function parseDataTable(rawData) {
|
|
1208
|
+
try {
|
|
1209
|
+
const nav = rawData;
|
|
1210
|
+
const rowsArray = nav[0][0][0][0][4][2];
|
|
1211
|
+
if (!rowsArray?.length) throw new Error("Empty data table");
|
|
1212
|
+
const headers = [];
|
|
1213
|
+
const rows = [];
|
|
1214
|
+
for (let i = 0; i < rowsArray.length; i++) {
|
|
1215
|
+
const rowSection = rowsArray[i];
|
|
1216
|
+
if (!Array.isArray(rowSection) || rowSection.length < 3) continue;
|
|
1217
|
+
const cellArray = rowSection[2];
|
|
1218
|
+
if (!Array.isArray(cellArray)) continue;
|
|
1219
|
+
const values = cellArray.map(extractCellText);
|
|
1220
|
+
if (i === 0) headers.push(...values);
|
|
1221
|
+
else rows.push(values);
|
|
1222
|
+
}
|
|
1223
|
+
if (!headers.length) throw new Error("No headers found");
|
|
1224
|
+
return { headers, rows };
|
|
1225
|
+
} catch (e) {
|
|
1226
|
+
throw new Error(`Failed to parse data table: ${e}`);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1140
1229
|
function sleep(ms) {
|
|
1141
1230
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1142
1231
|
}
|
|
@@ -1473,61 +1562,69 @@ var NotesAPI = class {
|
|
|
1473
1562
|
this.rpc = rpc;
|
|
1474
1563
|
}
|
|
1475
1564
|
async list(notebookId) {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
if (!Array.isArray(result)) return { notes, mindMaps };
|
|
1483
|
-
try {
|
|
1484
|
-
const notesData = result[0];
|
|
1485
|
-
if (Array.isArray(notesData)) {
|
|
1486
|
-
for (const n of notesData) {
|
|
1487
|
-
if (Array.isArray(n)) notes.push(parseNote(n));
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
const mapsData = result[1];
|
|
1491
|
-
if (Array.isArray(mapsData)) {
|
|
1492
|
-
for (const m of mapsData) {
|
|
1493
|
-
if (Array.isArray(m)) {
|
|
1494
|
-
mindMaps.push({
|
|
1495
|
-
id: typeof m[0] === "string" ? m[0] : "",
|
|
1496
|
-
title: typeof m[2] === "string" ? m[2] : null,
|
|
1497
|
-
content: typeof m[1] === "string" ? m[1] : "",
|
|
1498
|
-
createdAt: Array.isArray(m[3]) && typeof m[3][0] === "number" ? new Date(m[3][0] * 1e3) : null
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
} catch {
|
|
1504
|
-
}
|
|
1505
|
-
return { notes, mindMaps };
|
|
1565
|
+
const all = await this._fetchAll(notebookId);
|
|
1566
|
+
return all.filter((n) => !this._isMindMap(n.content));
|
|
1567
|
+
}
|
|
1568
|
+
async listMindMaps(notebookId) {
|
|
1569
|
+
const all = await this._fetchAll(notebookId);
|
|
1570
|
+
return all.filter((n) => this._isMindMap(n.content));
|
|
1506
1571
|
}
|
|
1507
1572
|
async create(notebookId, content, title) {
|
|
1508
|
-
const
|
|
1509
|
-
const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTE,
|
|
1510
|
-
sourcePath: `/notebook/${notebookId}
|
|
1573
|
+
const createParams = [notebookId, "", [1], null, "New Note"];
|
|
1574
|
+
const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTE, createParams, {
|
|
1575
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1576
|
+
allowNull: true
|
|
1511
1577
|
});
|
|
1512
|
-
|
|
1513
|
-
throw new Error("
|
|
1578
|
+
const noteId = Array.isArray(result) && Array.isArray(result[0]) && typeof result[0][0] === "string" ? result[0][0] : Array.isArray(result) && typeof result[0] === "string" ? result[0] : null;
|
|
1579
|
+
if (!noteId) throw new Error("CREATE_NOTE did not return a note ID");
|
|
1580
|
+
await this.update(notebookId, noteId, content, title ?? "New Note");
|
|
1581
|
+
return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
|
|
1514
1582
|
}
|
|
1515
1583
|
async update(notebookId, noteId, content, title) {
|
|
1516
|
-
const params = [notebookId, noteId, content, title ??
|
|
1517
|
-
|
|
1518
|
-
sourcePath: `/notebook/${notebookId}
|
|
1584
|
+
const params = [notebookId, noteId, [[[content, title ?? "New Note", [], 0]]]];
|
|
1585
|
+
await this.rpc.call(exports.RPCMethod.UPDATE_NOTE, params, {
|
|
1586
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1587
|
+
allowNull: true
|
|
1519
1588
|
});
|
|
1520
|
-
if (Array.isArray(result)) return parseNote(result);
|
|
1521
1589
|
return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
|
|
1522
1590
|
}
|
|
1523
1591
|
async delete(notebookId, noteId) {
|
|
1524
|
-
const params = [notebookId,
|
|
1592
|
+
const params = [notebookId, null, [noteId]];
|
|
1525
1593
|
await this.rpc.call(exports.RPCMethod.DELETE_NOTE, params, {
|
|
1526
1594
|
sourcePath: `/notebook/${notebookId}`,
|
|
1527
1595
|
allowNull: true
|
|
1528
1596
|
});
|
|
1529
1597
|
return true;
|
|
1530
1598
|
}
|
|
1599
|
+
async _fetchAll(notebookId) {
|
|
1600
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
|
|
1601
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1602
|
+
allowNull: true
|
|
1603
|
+
});
|
|
1604
|
+
if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
|
|
1605
|
+
const notes = [];
|
|
1606
|
+
for (const item of result[0]) {
|
|
1607
|
+
if (!Array.isArray(item) || typeof item[0] !== "string") continue;
|
|
1608
|
+
if (item[1] === null && item[2] === 2) continue;
|
|
1609
|
+
const content = this._extractContent(item);
|
|
1610
|
+
notes.push(this._parseItem(item, notebookId, content));
|
|
1611
|
+
}
|
|
1612
|
+
return notes;
|
|
1613
|
+
}
|
|
1614
|
+
_isMindMap(content) {
|
|
1615
|
+
return content.includes('"children":') || content.includes('"nodes":');
|
|
1616
|
+
}
|
|
1617
|
+
_extractContent(item) {
|
|
1618
|
+
if (typeof item[1] === "string") return item[1];
|
|
1619
|
+
if (Array.isArray(item[1]) && typeof item[1][1] === "string") return item[1][1];
|
|
1620
|
+
return "";
|
|
1621
|
+
}
|
|
1622
|
+
_parseItem(item, _notebookId, content) {
|
|
1623
|
+
const inner = Array.isArray(item[1]) ? item[1] : null;
|
|
1624
|
+
const title = inner && typeof inner[4] === "string" && inner[4] ? inner[4] : null;
|
|
1625
|
+
const createdAt = Array.isArray(item[3]) && typeof item[3][0] === "number" ? new Date(item[3][0] * 1e3) : null;
|
|
1626
|
+
return { id: item[0], title, content, createdAt, updatedAt: null };
|
|
1627
|
+
}
|
|
1531
1628
|
};
|
|
1532
1629
|
|
|
1533
1630
|
// src/api/research.ts
|
|
@@ -1664,7 +1761,7 @@ var ResearchAPI = class {
|
|
|
1664
1761
|
const webSources = sources.filter((s) => s.url && !reportSourceSet.has(s));
|
|
1665
1762
|
if (!webSources.length && !reportSources.length) return [];
|
|
1666
1763
|
const sourceArray = [
|
|
1667
|
-
...reportSources.map((s) => buildReportEntry(s.title, s.reportMarkdown)),
|
|
1764
|
+
...reportSources.filter((s) => s.reportMarkdown).map((s) => buildReportEntry(s.title, s.reportMarkdown)),
|
|
1668
1765
|
...webSources.map((s) => buildWebEntry(s.url, s.title))
|
|
1669
1766
|
];
|
|
1670
1767
|
const params = [null, [1], effectiveTaskId, notebookId, sourceArray];
|
|
@@ -2394,9 +2491,9 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2394
2491
|
const rpc = new RPCCore(auth, opts.timeoutMs);
|
|
2395
2492
|
this.notebooks = new NotebooksAPI(rpc);
|
|
2396
2493
|
this.sources = new SourcesAPI(rpc, auth);
|
|
2397
|
-
this.artifacts = new ArtifactsAPI(rpc, auth);
|
|
2398
|
-
this.chat = new ChatAPI(rpc, auth);
|
|
2399
2494
|
this.notes = new NotesAPI(rpc);
|
|
2495
|
+
this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
|
|
2496
|
+
this.chat = new ChatAPI(rpc, auth);
|
|
2400
2497
|
this.research = new ResearchAPI(rpc);
|
|
2401
2498
|
this.settings = new SettingsAPI(rpc);
|
|
2402
2499
|
this.sharing = new SharingAPI(rpc);
|