notebooklm-sdk 0.1.7 → 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-Ba2hsZW_.d.cts → auth-Dxsm8894.d.cts} +1 -1
- package/dist/{auth-Ba2hsZW_.d.ts → auth-Dxsm8894.d.ts} +1 -1
- package/dist/auth.cjs +45 -25
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +8 -4
- package/dist/auth.d.ts +8 -4
- package/dist/auth.js +45 -27
- package/dist/auth.js.map +1 -1
- package/dist/bin.cjs +170 -31
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +172 -33
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +199 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -26
- package/dist/index.d.ts +42 -26
- package/dist/index.js +200 -87
- package/dist/index.js.map +1 -1
- package/package.json +11 -3
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types/errors.ts","../src/auth.ts","../src/auth/browser.ts","../src/bin.ts"],"names":[],"mappings":"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","file":"bin.js","sourcesContent":["/**\n * All exceptions for notebooklm-sdk.\n *\n * All errors extend NotebookLMError so you can catch everything with:\n * try { ... } catch (e) { if (e instanceof NotebookLMError) ... }\n */\n\nexport class NotebookLMError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Network (transport-level, before RPC processing)\n// ---------------------------------------------------------------------------\n\nexport class NetworkError extends NotebookLMError {\n readonly methodId?: string;\n readonly originalError?: Error;\n\n constructor(message: string, opts: { methodId?: string; originalError?: Error } = {}) {\n super(message);\n this.methodId = opts.methodId;\n this.originalError = opts.originalError;\n }\n}\n\nexport class RPCTimeoutError extends NetworkError {}\n\n// ---------------------------------------------------------------------------\n// RPC Protocol (after connection established)\n// ---------------------------------------------------------------------------\n\nexport class RPCError extends NotebookLMError {\n readonly methodId?: string;\n readonly rawResponse?: string;\n readonly rpcCode?: string | number;\n readonly foundIds: string[];\n\n constructor(\n message: string,\n opts: {\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message);\n this.methodId = opts.methodId;\n this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : undefined;\n this.rpcCode = opts.rpcCode;\n this.foundIds = opts.foundIds ?? [];\n }\n}\n\nexport class AuthError extends RPCError {}\n\nexport class RateLimitError extends RPCError {\n readonly retryAfter?: number;\n\n constructor(\n message: string,\n opts: {\n retryAfter?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message, opts);\n this.retryAfter = opts.retryAfter;\n }\n}\n\nexport class ServerError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\nexport class ClientError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Notebooks\n// ---------------------------------------------------------------------------\n\nexport class NotebookError extends NotebookLMError {}\n\nexport class NotebookNotFoundError extends NotebookError {\n readonly notebookId: string;\n\n constructor(notebookId: string) {\n super(`Notebook not found: ${notebookId}`);\n this.notebookId = notebookId;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Sources\n// ---------------------------------------------------------------------------\n\nexport class SourceError extends NotebookLMError {}\n\nexport class SourceNotFoundError extends SourceError {\n readonly sourceId: string;\n\n constructor(sourceId: string) {\n super(`Source not found: ${sourceId}`);\n this.sourceId = sourceId;\n }\n}\n\nexport class SourceAddError extends SourceError {\n readonly url: string;\n readonly cause?: Error;\n\n constructor(url: string, opts: { cause?: Error; message?: string } = {}) {\n super(\n opts.message ??\n `Failed to add source: ${url}\\n` +\n \"Possible causes:\\n\" +\n \" - URL is invalid or inaccessible\\n\" +\n \" - Content is behind a paywall or requires authentication\\n\" +\n \" - Rate limiting or quota exceeded\",\n );\n this.url = url;\n this.cause = opts.cause;\n }\n}\n\nexport class SourceProcessingError extends SourceError {\n readonly sourceId: string;\n readonly status: number;\n\n constructor(sourceId: string, status = 3, message?: string) {\n super(message ?? `Source ${sourceId} failed to process`);\n this.sourceId = sourceId;\n this.status = status;\n }\n}\n\nexport class SourceTimeoutError extends SourceError {\n readonly sourceId: string;\n readonly timeout: number;\n readonly lastStatus?: number;\n\n constructor(sourceId: string, timeout: number, lastStatus?: number) {\n const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : \"\";\n super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);\n this.sourceId = sourceId;\n this.timeout = timeout;\n this.lastStatus = lastStatus;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Artifacts\n// ---------------------------------------------------------------------------\n\nexport class ArtifactError extends NotebookLMError {}\n\nexport class ArtifactNotFoundError extends ArtifactError {\n readonly artifactId: string;\n readonly artifactType?: string;\n\n constructor(artifactId: string, artifactType?: string) {\n const typeInfo = artifactType ? ` ${artifactType}` : \"\";\n super(`${typeInfo.trim() || \"Artifact\"} ${artifactId} not found`);\n this.artifactId = artifactId;\n this.artifactType = artifactType;\n }\n}\n\nexport class ArtifactNotReadyError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly status?: string;\n\n constructor(artifactType: string, opts: { artifactId?: string; status?: string } = {}) {\n const base = opts.artifactId\n ? `${artifactType} artifact ${opts.artifactId} is not ready`\n : `No completed ${artifactType} found`;\n const statusInfo = opts.status ? ` (status: ${opts.status})` : \"\";\n super(`${base}${statusInfo}`);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.status = opts.status;\n }\n}\n\nexport class ArtifactParseError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to parse ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\nexport class ArtifactDownloadError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to download ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Chat\n// ---------------------------------------------------------------------------\n\nexport class ChatError extends NotebookLMError {}\n","import { readFileSync } from \"node:fs\";\nimport { AuthError } from \"./types/errors.js\";\n\nexport interface CookieMap {\n [key: string]: string;\n}\n\nexport interface AuthTokens {\n cookies: CookieMap;\n csrfToken: string;\n sessionId: string;\n cookieHeader: string;\n /** Cookie header containing only .google.com domain cookies — for media downloads */\n googleCookieHeader: string;\n}\n\n// ---------------------------------------------------------------------------\n// Cookie loading\n// ---------------------------------------------------------------------------\n\n/** Load cookies from a Playwright storage_state.json file. */\nexport function loadCookiesFromFile(filePath: string): CookieMap {\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch {\n throw new AuthError(\n `Cookie file not found: ${filePath}\\n` + \"Provide valid Playwright storage state JSON.\",\n );\n }\n return extractCookiesFromStorageState(JSON.parse(raw));\n}\n\n/** Load cookies from a raw Playwright storage state object. */\nexport function loadCookiesFromObject(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n return extractCookiesFromStorageState(storageState);\n}\n\n/** Build a cookie header containing only .google.com domain cookies (for media downloads). */\nexport function buildGoogleCookieHeader(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): string {\n const map: CookieMap = {};\n for (const c of storageState.cookies ?? []) {\n if (c.domain === \".google.com\" && c.name && c.value) {\n map[c.name] = map[c.name] ?? c.value;\n }\n }\n return buildCookieHeader(map);\n}\n\n/** Load cookies from a flat cookie map (already parsed). */\nexport function loadCookiesFromMap(map: CookieMap): CookieMap {\n return { ...map };\n}\n\n/** Load cookies from a \"; \"-separated cookie string (e.g. process.env.NOTEBOOKLM_COOKIES). */\nexport function loadCookiesFromString(cookieStr: string): CookieMap {\n const map: CookieMap = {};\n for (const part of cookieStr.split(/;\\s*/)) {\n const idx = part.indexOf(\"=\");\n if (idx > 0) {\n const name = part.slice(0, idx).trim();\n const value = part.slice(idx + 1).trim();\n if (name) map[name] = value;\n }\n }\n return map;\n}\n\nfunction extractCookiesFromStorageState(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n const cookies: CookieMap = {};\n const domainTrack: Record<string, string> = {};\n\n for (const cookie of storageState.cookies ?? []) {\n const { domain, name, value } = cookie;\n if (!isAllowedDomain(domain) || !name) continue;\n\n const isBase = domain === \".google.com\";\n if (!(name in cookies) || isBase) {\n cookies[name] = value;\n domainTrack[name] = domain;\n }\n }\n\n if (!cookies[\"SID\"]) {\n throw new AuthError(\n \"Missing required cookie: SID. \" +\n \"Provide valid Playwright storage state with Google cookies.\",\n );\n }\n return cookies;\n}\n\nfunction isAllowedDomain(domain: string): boolean {\n if (\n domain === \".google.com\" ||\n domain === \"notebooklm.google.com\" ||\n domain === \".googleusercontent.com\"\n ) {\n return true;\n }\n if (domain.startsWith(\".google.\")) {\n return true; // Allow all regional Google domains\n }\n return false;\n}\n\nexport function buildCookieHeader(cookies: CookieMap): string {\n return Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \");\n}\n\n// ---------------------------------------------------------------------------\n// Token fetching\n// ---------------------------------------------------------------------------\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\n\nexport async function fetchTokens(\n cookies: CookieMap,\n): Promise<{ csrfToken: string; sessionId: string }> {\n const cookieHeader = buildCookieHeader(cookies);\n\n const response = await fetch(NOTEBOOKLM_URL, {\n headers: { Cookie: cookieHeader },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);\n }\n\n const finalUrl = response.url;\n if (isGoogleAuthRedirect(finalUrl)) {\n throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);\n }\n\n const html = await response.text();\n const csrfToken = extractCsrfToken(html, finalUrl);\n const sessionId = extractSessionId(html, finalUrl);\n\n return { csrfToken, sessionId };\n}\n\nfunction extractCsrfToken(html: string, finalUrl: string): string {\n const match = /\"SNlM0e\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Authentication expired or invalid. Cookies may need to be refreshed.\");\n }\n throw new AuthError(\"CSRF token (SNlM0e) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction extractSessionId(html: string, finalUrl: string): string {\n const match = /\"FdrFJe\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Authentication expired or invalid. Cookies may need to be refreshed.\");\n }\n throw new AuthError(\"Session ID (FdrFJe) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction isGoogleAuthRedirect(url: string): boolean {\n return url.includes(\"accounts.google.com\") || url.includes(\"signin\");\n}\n\n// ---------------------------------------------------------------------------\n// Connect options\n// ---------------------------------------------------------------------------\n\nexport interface ConnectOptions {\n /** \"; \"-separated cookie string (e.g. \"SID=abc; HSID=xyz\") */\n cookies?: string;\n /** Path to Playwright storage_state.json */\n cookiesFile?: string;\n /** Pre-parsed cookie map */\n cookiesObject?: CookieMap | { cookies?: Array<{ name: string; value: string; domain: string }> };\n}\n\nexport async function connect(opts: ConnectOptions): Promise<AuthTokens> {\n let cookieMap: CookieMap;\n let googleCookieHeader: string | null = null;\n\n if (opts.cookies) {\n cookieMap = loadCookiesFromString(opts.cookies);\n } else if (opts.cookiesFile) {\n cookieMap = loadCookiesFromFile(opts.cookiesFile);\n } else if (opts.cookiesObject) {\n if (\"cookies\" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {\n const storageState = opts.cookiesObject as {\n cookies: Array<{ name: string; value: string; domain: string }>;\n };\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else {\n cookieMap = loadCookiesFromMap(opts.cookiesObject as CookieMap);\n }\n } else {\n // Fallback: check environment variable\n const envCookies = process.env[\"NOTEBOOKLM_COOKIES\"];\n if (envCookies) {\n cookieMap = loadCookiesFromString(envCookies);\n } else {\n throw new AuthError(\n \"No cookies provided. Pass cookies, cookiesFile, or cookiesObject to connect().\",\n );\n }\n }\n\n const { csrfToken, sessionId } = await fetchTokens(cookieMap);\n const cookieHeader = buildCookieHeader(cookieMap);\n\n return {\n cookies: cookieMap,\n csrfToken,\n sessionId,\n cookieHeader,\n googleCookieHeader: googleCookieHeader ?? cookieHeader,\n };\n}\n","import { type BrowserContext, chromium, type Page } from \"playwright\";\nimport { type CookieMap, loadCookiesFromObject } from \"../auth.js\";\n\nexport interface LoginOptions {\n /**\n * Path to a directory for a persistent browser profile.\n * If provided, session remains logged in for future calls.\n */\n persistFolder?: string;\n /**\n * Browser type to use. Default is \"chromium\".\n */\n browserType?: \"chromium\" | \"msedge\";\n /**\n * Whether to run the browser in headless mode. Default is false.\n * Headless login is usually blocked by Google, so this is mostly for testing or refreshes.\n */\n headless?: boolean;\n}\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\nconst GOOGLE_ACCOUNTS_URL = \"https://accounts.google.com/\";\n\n/**\n * Log in to NotebookLM via a headful browser window.\n *\n * Flow:\n * 1. Opens browser to NotebookLM.\n * 2. If already logged in (via persistFolder), it proceeds.\n * 3. If not, it waits for the user to reach the home page.\n * 4. Captures cookies and returns them.\n */\nexport async function login(opts: LoginOptions = {}): Promise<{\n cookies: CookieMap;\n storageState: any;\n cookieHeader: string;\n}> {\n const { persistFolder, headless = false, browserType = \"chromium\" } = opts;\n\n let context: BrowserContext;\n\n const launchOptions = {\n headless,\n args: [\"--disable-blink-features=AutomationControlled\"],\n };\n\n if (persistFolder) {\n context = await chromium.launchPersistentContext(persistFolder, {\n ...launchOptions,\n channel: browserType === \"msedge\" ? \"msedge\" : undefined,\n });\n } else {\n const browser = await chromium.launch({\n ...launchOptions,\n channel: browserType === \"msedge\" ? \"msedge\" : undefined,\n });\n context = await browser.newContext();\n }\n\n const page = context.pages()[0] || (await context.newPage());\n await page.goto(NOTEBOOKLM_URL);\n\n // Check if we are on the login page\n if (page.url().includes(\"accounts.google.com\")) {\n console.log(\"Please log in to Google in the browser window...\");\n\n // Wait for navigation back to NotebookLM or successful login indicator\n // We poll until the URL includes notebooklm.google.com and it's not a generic landing page\n await page.waitForURL(\n (url) => {\n return url.hostname === \"notebooklm.google.com\" && !url.pathname.includes(\"/login\");\n },\n { timeout: 0 },\n ); // No timeout, wait for user\n }\n\n // Ensure we are fully loaded on the accounts domain too to capture those cookies\n await page.goto(GOOGLE_ACCOUNTS_URL, { waitUntil: \"load\" });\n await page.goto(NOTEBOOKLM_URL, { waitUntil: \"load\" });\n\n const storageState = await context.storageState();\n const cookies = loadCookiesFromObject(storageState as any);\n\n await context.close();\n\n return {\n cookies,\n storageState,\n cookieHeader: Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \"),\n };\n}\n","#!/usr/bin/env node\nimport { writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { login } from \"./auth/browser.js\";\n\nasync function run() {\n const args = process.argv.slice(2);\n const command = args[0];\n\n if (command === \"login\") {\n console.log(\"🚀 Starting browser login flow...\");\n console.log(\"A browser window will open. Please log in to your Google account.\");\n\n try {\n const authResult = await login({\n persistFolder: \"./.auth_profile\",\n headless: false,\n });\n\n const outPath = resolve(process.cwd(), \"storage_state.json\");\n writeFileSync(outPath, JSON.stringify(authResult.storageState, null, 2));\n\n console.log(\"\\n✅ Login successful!\");\n console.log(`Saved session to: ${outPath}`);\n console.log(\"\\nNext steps:\");\n console.log(\"In your code, connect using:\");\n console.log(\"const client = await NotebookLMClient.connect({ cookiesFile: 'storage_state.json' });\");\n } catch (error) {\n console.error(\"❌ Login failed:\", error);\n process.exit(1);\n }\n } else {\n console.log(\"NotebookLM SDK CLI\");\n console.log(\"\\nUsage:\");\n console.log(\" npx notebooklm-sdk login Start browser login flow\");\n process.exit(command ? 1 : 0);\n }\n}\n\nrun();\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types/errors.ts","../src/auth.ts","../src/auth/browser.ts","../src/bin.ts"],"names":["join","homedir","DEFAULT_SESSION_FILE","NOTEBOOKLM_URL","existsSync"],"mappings":"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","file":"bin.js","sourcesContent":["/**\n * All exceptions for notebooklm-sdk.\n *\n * All errors extend NotebookLMError so you can catch everything with:\n * try { ... } catch (e) { if (e instanceof NotebookLMError) ... }\n */\n\nexport class NotebookLMError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Network (transport-level, before RPC processing)\n// ---------------------------------------------------------------------------\n\nexport class NetworkError extends NotebookLMError {\n readonly methodId?: string;\n readonly originalError?: Error;\n\n constructor(message: string, opts: { methodId?: string; originalError?: Error } = {}) {\n super(message);\n this.methodId = opts.methodId;\n this.originalError = opts.originalError;\n }\n}\n\nexport class RPCTimeoutError extends NetworkError {}\n\n// ---------------------------------------------------------------------------\n// RPC Protocol (after connection established)\n// ---------------------------------------------------------------------------\n\nexport class RPCError extends NotebookLMError {\n readonly methodId?: string;\n readonly rawResponse?: string;\n readonly rpcCode?: string | number;\n readonly foundIds: string[];\n\n constructor(\n message: string,\n opts: {\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message);\n this.methodId = opts.methodId;\n this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : undefined;\n this.rpcCode = opts.rpcCode;\n this.foundIds = opts.foundIds ?? [];\n }\n}\n\nexport class AuthError extends RPCError {}\n\nexport class RateLimitError extends RPCError {\n readonly retryAfter?: number;\n\n constructor(\n message: string,\n opts: {\n retryAfter?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message, opts);\n this.retryAfter = opts.retryAfter;\n }\n}\n\nexport class ServerError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\nexport class ClientError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Notebooks\n// ---------------------------------------------------------------------------\n\nexport class NotebookError extends NotebookLMError {}\n\nexport class NotebookNotFoundError extends NotebookError {\n readonly notebookId: string;\n\n constructor(notebookId: string) {\n super(`Notebook not found: ${notebookId}`);\n this.notebookId = notebookId;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Sources\n// ---------------------------------------------------------------------------\n\nexport class SourceError extends NotebookLMError {}\n\nexport class SourceNotFoundError extends SourceError {\n readonly sourceId: string;\n\n constructor(sourceId: string) {\n super(`Source not found: ${sourceId}`);\n this.sourceId = sourceId;\n }\n}\n\nexport class SourceAddError extends SourceError {\n readonly url: string;\n readonly cause?: Error;\n\n constructor(url: string, opts: { cause?: Error; message?: string } = {}) {\n super(\n opts.message ??\n `Failed to add source: ${url}\\n` +\n \"Possible causes:\\n\" +\n \" - URL is invalid or inaccessible\\n\" +\n \" - Content is behind a paywall or requires authentication\\n\" +\n \" - Rate limiting or quota exceeded\",\n );\n this.url = url;\n this.cause = opts.cause;\n }\n}\n\nexport class SourceProcessingError extends SourceError {\n readonly sourceId: string;\n readonly status: number;\n\n constructor(sourceId: string, status = 3, message?: string) {\n super(message ?? `Source ${sourceId} failed to process`);\n this.sourceId = sourceId;\n this.status = status;\n }\n}\n\nexport class SourceTimeoutError extends SourceError {\n readonly sourceId: string;\n readonly timeout: number;\n readonly lastStatus?: number;\n\n constructor(sourceId: string, timeout: number, lastStatus?: number) {\n const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : \"\";\n super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);\n this.sourceId = sourceId;\n this.timeout = timeout;\n this.lastStatus = lastStatus;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Artifacts\n// ---------------------------------------------------------------------------\n\nexport class ArtifactError extends NotebookLMError {}\n\nexport class ArtifactNotFoundError extends ArtifactError {\n readonly artifactId: string;\n readonly artifactType?: string;\n\n constructor(artifactId: string, artifactType?: string) {\n const typeInfo = artifactType ? ` ${artifactType}` : \"\";\n super(`${typeInfo.trim() || \"Artifact\"} ${artifactId} not found`);\n this.artifactId = artifactId;\n this.artifactType = artifactType;\n }\n}\n\nexport class ArtifactNotReadyError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly status?: string;\n\n constructor(artifactType: string, opts: { artifactId?: string; status?: string } = {}) {\n const base = opts.artifactId\n ? `${artifactType} artifact ${opts.artifactId} is not ready`\n : `No completed ${artifactType} found`;\n const statusInfo = opts.status ? ` (status: ${opts.status})` : \"\";\n super(`${base}${statusInfo}`);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.status = opts.status;\n }\n}\n\nexport class ArtifactParseError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to parse ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\nexport class ArtifactDownloadError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to download ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Chat\n// ---------------------------------------------------------------------------\n\nexport class ChatError extends NotebookLMError {}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { AuthError } from \"./types/errors.js\";\n\n/** Default session file written by `npx notebooklm-sdk login`. */\nconst DEFAULT_SESSION_FILE = join(homedir(), \".notebooklm\", \"session.json\");\n\nexport interface CookieMap {\n [key: string]: string;\n}\n\nexport interface AuthTokens {\n cookies: CookieMap;\n csrfToken: string;\n sessionId: string;\n cookieHeader: string;\n /** Cookie header containing only .google.com domain cookies — for media downloads */\n googleCookieHeader: string;\n}\n\n// ---------------------------------------------------------------------------\n// Cookie loading\n// ---------------------------------------------------------------------------\n\n/** Load cookies from a Playwright storage_state.json file. */\nexport function loadCookiesFromFile(filePath: string): CookieMap {\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch {\n throw new AuthError(`Session file not found: ${filePath}\\nRun: npx notebooklm-sdk login`);\n }\n return extractCookiesFromStorageState(JSON.parse(raw));\n}\n\n/** Load cookies from a raw Playwright storage state object. */\nexport function loadCookiesFromObject(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n return extractCookiesFromStorageState(storageState);\n}\n\n/** Build a cookie header containing only .google.com domain cookies (for media downloads). */\nexport function buildGoogleCookieHeader(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): string {\n const map: CookieMap = {};\n for (const c of storageState.cookies ?? []) {\n if (c.domain === \".google.com\" && c.name && c.value) {\n map[c.name] = map[c.name] ?? c.value;\n }\n }\n return buildCookieHeader(map);\n}\n\n/** Load cookies from a flat cookie map (already parsed). */\nexport function loadCookiesFromMap(map: CookieMap): CookieMap {\n return { ...map };\n}\n\n/** Load cookies from a \"; \"-separated cookie string (e.g. process.env.NOTEBOOKLM_COOKIES). */\nexport function loadCookiesFromString(cookieStr: string): CookieMap {\n const map: CookieMap = {};\n for (const part of cookieStr.split(/;\\s*/)) {\n const idx = part.indexOf(\"=\");\n if (idx > 0) {\n const name = part.slice(0, idx).trim();\n const value = part.slice(idx + 1).trim();\n if (name) map[name] = value;\n }\n }\n return map;\n}\n\nfunction extractCookiesFromStorageState(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n const cookies: CookieMap = {};\n const domainTrack: Record<string, string> = {};\n\n for (const cookie of storageState.cookies ?? []) {\n const { domain, name, value } = cookie;\n if (!isAllowedDomain(domain) || !name) continue;\n\n const isBase = domain === \".google.com\";\n if (!(name in cookies) || isBase) {\n cookies[name] = value;\n domainTrack[name] = domain;\n }\n }\n\n if (!cookies[\"SID\"]) {\n throw new AuthError(\n \"Missing required cookie: SID. Session may be invalid or expired.\\nRun: npx notebooklm-sdk login\",\n );\n }\n return cookies;\n}\n\nfunction isAllowedDomain(domain: string): boolean {\n if (\n domain === \".google.com\" ||\n domain === \"notebooklm.google.com\" ||\n domain === \".googleusercontent.com\"\n ) {\n return true;\n }\n if (domain.startsWith(\".google.\")) {\n return true; // Allow all regional Google domains\n }\n return false;\n}\n\nexport function buildCookieHeader(cookies: CookieMap): string {\n return Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \");\n}\n\n// ---------------------------------------------------------------------------\n// Token fetching\n// ---------------------------------------------------------------------------\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\n\nexport async function fetchTokens(\n cookies: CookieMap,\n): Promise<{ csrfToken: string; sessionId: string }> {\n const cookieHeader = buildCookieHeader(cookies);\n\n const response = await fetch(NOTEBOOKLM_URL, {\n headers: { Cookie: cookieHeader },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);\n }\n\n const finalUrl = response.url;\n if (isGoogleAuthRedirect(finalUrl)) {\n throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);\n }\n\n const html = await response.text();\n const csrfToken = extractCsrfToken(html, finalUrl);\n const sessionId = extractSessionId(html, finalUrl);\n\n return { csrfToken, sessionId };\n}\n\nfunction extractCsrfToken(html: string, finalUrl: string): string {\n const match = /\"SNlM0e\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Session expired or invalid.\\nRun: npx notebooklm-sdk login\");\n }\n throw new AuthError(\"CSRF token (SNlM0e) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction extractSessionId(html: string, finalUrl: string): string {\n const match = /\"FdrFJe\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Session expired or invalid.\\nRun: npx notebooklm-sdk login\");\n }\n throw new AuthError(\"Session ID (FdrFJe) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction isGoogleAuthRedirect(url: string): boolean {\n return url.includes(\"accounts.google.com\") || url.includes(\"signin\");\n}\n\n// ---------------------------------------------------------------------------\n// Connect options\n// ---------------------------------------------------------------------------\n\nexport interface ConnectOptions {\n /** \"; \"-separated cookie string (e.g. \"SID=abc; HSID=xyz\") */\n cookies?: string;\n /** Path to Playwright storage_state.json */\n cookiesFile?: string;\n /** Pre-parsed cookie map */\n cookiesObject?: CookieMap | { cookies?: Array<{ name: string; value: string; domain: string }> };\n}\n\nexport async function connect(opts: ConnectOptions = {}): Promise<AuthTokens> {\n let cookieMap: CookieMap;\n let googleCookieHeader: string | null = null;\n\n if (opts.cookies) {\n cookieMap = loadCookiesFromString(opts.cookies);\n } else if (opts.cookiesFile) {\n cookieMap = loadCookiesFromFile(opts.cookiesFile);\n } else if (opts.cookiesObject) {\n if (\"cookies\" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {\n const storageState = opts.cookiesObject as {\n cookies: Array<{ name: string; value: string; domain: string }>;\n };\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else {\n cookieMap = loadCookiesFromMap(opts.cookiesObject as CookieMap);\n }\n } else {\n // Auto-discovery: ~/.notebooklm/session.json → ./storage_state.json → env var\n const envCookies = process.env[\"NOTEBOOKLM_COOKIES\"];\n const envFile = process.env[\"NOTEBOOKLM_COOKIES_FILE\"];\n if (envFile) {\n cookieMap = loadCookiesFromFile(envFile);\n } else if (existsSync(DEFAULT_SESSION_FILE)) {\n const raw = readFileSync(DEFAULT_SESSION_FILE, \"utf-8\");\n const storageState = JSON.parse(raw);\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else if (existsSync(\"storage_state.json\")) {\n const raw = readFileSync(\"storage_state.json\", \"utf-8\");\n const storageState = JSON.parse(raw);\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else if (envCookies) {\n cookieMap = loadCookiesFromString(envCookies);\n } else {\n throw new AuthError(\"No session found. Run: npx notebooklm-sdk login\");\n }\n }\n\n const { csrfToken, sessionId } = await fetchTokens(cookieMap);\n const cookieHeader = buildCookieHeader(cookieMap);\n\n return {\n cookies: cookieMap,\n csrfToken,\n sessionId,\n cookieHeader,\n googleCookieHeader: googleCookieHeader ?? cookieHeader,\n };\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type BrowserContext, chromium, type Page } from \"playwright\";\nimport { type CookieMap, loadCookiesFromObject } from \"../auth.js\";\n\n/** Default directory for storing NotebookLM session files (~/.notebooklm). */\nexport const DEFAULT_SESSION_DIR = join(homedir(), \".notebooklm\");\n/** Default session file path (~/.notebooklm/session.json). */\nexport const DEFAULT_SESSION_FILE = join(DEFAULT_SESSION_DIR, \"session.json\");\n\nexport interface LoginOptions {\n /**\n * Path to a directory for a persistent browser profile.\n * Defaults to ~/.notebooklm/.auth_profile.\n */\n persistFolder?: string;\n /**\n * Browser type to use. Default is \"chromium\".\n */\n browserType?: \"chromium\" | \"msedge\";\n /**\n * Whether to run the browser in headless mode. Default is false.\n * Headless login is usually blocked by Google, so this is mostly for testing or refreshes.\n */\n headless?: boolean;\n}\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\nconst GOOGLE_ACCOUNTS_URL = \"https://accounts.google.com/\";\n\n/**\n * Log in to NotebookLM via a headful browser window.\n *\n * Flow:\n * 1. Opens browser to NotebookLM.\n * 2. If already logged in (via persistFolder), it proceeds.\n * 3. If not, it waits for the user to reach the home page.\n * 4. Captures cookies and returns them.\n */\nexport async function login(opts: LoginOptions = {}): Promise<{\n cookies: CookieMap;\n storageState: any;\n cookieHeader: string;\n}> {\n const {\n persistFolder = join(DEFAULT_SESSION_DIR, \".auth_profile\"),\n headless = false,\n browserType = \"chromium\",\n } = opts;\n\n // Pre-flight: verify Playwright browser is installed\n try {\n chromium.executablePath();\n } catch {\n throw new Error(\"Playwright browser not found. Run: npx playwright install chromium\");\n }\n\n // Ensure the session directory exists\n if (!existsSync(DEFAULT_SESSION_DIR)) {\n mkdirSync(DEFAULT_SESSION_DIR, { recursive: true });\n }\n\n let context: BrowserContext;\n\n const launchOptions = {\n headless,\n args: [\"--disable-blink-features=AutomationControlled\"],\n };\n\n context = await chromium.launchPersistentContext(persistFolder, {\n ...launchOptions,\n channel: browserType === \"msedge\" ? \"msedge\" : undefined,\n });\n\n const page = context.pages()[0] || (await context.newPage());\n await page.goto(NOTEBOOKLM_URL);\n\n // Check if we are on the login page\n if (page.url().includes(\"accounts.google.com\")) {\n console.log(\"Please log in to Google in the browser window...\");\n\n // Wait for navigation back to NotebookLM or successful login indicator\n // We poll until the URL includes notebooklm.google.com and it's not a generic landing page\n await page.waitForURL(\n (url) => {\n return url.hostname === \"notebooklm.google.com\" && !url.pathname.includes(\"/login\");\n },\n { timeout: 0 },\n ); // No timeout, wait for user\n }\n\n // Ensure we are fully loaded on the accounts domain too to capture those cookies\n await page.goto(GOOGLE_ACCOUNTS_URL, { waitUntil: \"load\" });\n await page.goto(NOTEBOOKLM_URL, { waitUntil: \"load\" });\n\n const storageState = await context.storageState();\n const cookies = loadCookiesFromObject(storageState as any);\n\n await context.close();\n\n return {\n cookies,\n storageState,\n cookieHeader: Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \"),\n };\n}\n","#!/usr/bin/env node\nimport { writeFileSync } from \"node:fs\";\nimport { DEFAULT_SESSION_DIR, DEFAULT_SESSION_FILE, login } from \"./auth/browser.js\";\nimport { connect } from \"./auth.js\";\n\nasync function run() {\n const args = process.argv.slice(2);\n const command = args[0];\n\n if (command === \"login\") {\n console.log(\"Starting browser login flow...\");\n console.log(\"A browser window will open. Please log in to your Google account.\");\n\n try {\n const authResult = await login();\n\n writeFileSync(DEFAULT_SESSION_FILE, JSON.stringify(authResult.storageState, null, 2));\n\n console.log(\"\\nLogin successful!\");\n console.log(`Session saved to: ${DEFAULT_SESSION_FILE}`);\n console.log(\"\\nIn your code:\");\n console.log(\" const client = await NotebookLMClient.connect();\");\n } catch (error) {\n console.error(\"Login failed:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n } else if (command === \"whoami\") {\n try {\n const auth = await connect();\n console.log(\"Session valid\");\n console.log(`Session file: ${DEFAULT_SESSION_FILE}`);\n console.log(`CSRF token: ${auth.csrfToken.slice(0, 8)}...`);\n } catch (error) {\n console.error(\"Not authenticated:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n } else {\n console.log(\"NotebookLM SDK CLI\");\n console.log(\"\\nUsage:\");\n console.log(\" npx notebooklm-sdk login Authenticate via browser\");\n console.log(\" npx notebooklm-sdk whoami Check current session\");\n process.exit(command ? 1 : 0);\n }\n}\n\nrun();\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var fs = require('fs');
|
|
4
|
+
var os = require('os');
|
|
5
|
+
var path = require('path');
|
|
4
6
|
|
|
5
7
|
var __defProp = Object.defineProperty;
|
|
6
8
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -453,10 +455,8 @@ function loadCookiesFromFile(filePath) {
|
|
|
453
455
|
try {
|
|
454
456
|
raw = fs.readFileSync(filePath, "utf-8");
|
|
455
457
|
} catch {
|
|
456
|
-
throw new exports.AuthError(
|
|
457
|
-
|
|
458
|
-
Provide valid Playwright storage state JSON.`
|
|
459
|
-
);
|
|
458
|
+
throw new exports.AuthError(`Session file not found: ${filePath}
|
|
459
|
+
Run: npx notebooklm-sdk login`);
|
|
460
460
|
}
|
|
461
461
|
return extractCookiesFromStorageState(JSON.parse(raw));
|
|
462
462
|
}
|
|
@@ -499,7 +499,7 @@ function extractCookiesFromStorageState(storageState) {
|
|
|
499
499
|
}
|
|
500
500
|
if (!cookies["SID"]) {
|
|
501
501
|
throw new exports.AuthError(
|
|
502
|
-
"Missing required cookie: SID.
|
|
502
|
+
"Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
|
|
503
503
|
);
|
|
504
504
|
}
|
|
505
505
|
return cookies;
|
|
@@ -538,7 +538,7 @@ function extractCsrfToken(html, finalUrl) {
|
|
|
538
538
|
const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
|
|
539
539
|
if (!match?.[1]) {
|
|
540
540
|
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
541
|
-
throw new exports.AuthError("
|
|
541
|
+
throw new exports.AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
|
|
542
542
|
}
|
|
543
543
|
throw new exports.AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
|
|
544
544
|
}
|
|
@@ -548,7 +548,7 @@ function extractSessionId(html, finalUrl) {
|
|
|
548
548
|
const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
|
|
549
549
|
if (!match?.[1]) {
|
|
550
550
|
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
551
|
-
throw new exports.AuthError("
|
|
551
|
+
throw new exports.AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
|
|
552
552
|
}
|
|
553
553
|
throw new exports.AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
|
|
554
554
|
}
|
|
@@ -557,7 +557,7 @@ function extractSessionId(html, finalUrl) {
|
|
|
557
557
|
function isGoogleAuthRedirect(url) {
|
|
558
558
|
return url.includes("accounts.google.com") || url.includes("signin");
|
|
559
559
|
}
|
|
560
|
-
async function connect(opts) {
|
|
560
|
+
async function connect(opts = {}) {
|
|
561
561
|
let cookieMap;
|
|
562
562
|
let googleCookieHeader = null;
|
|
563
563
|
if (opts.cookies) {
|
|
@@ -574,12 +574,23 @@ async function connect(opts) {
|
|
|
574
574
|
}
|
|
575
575
|
} else {
|
|
576
576
|
const envCookies = process.env["NOTEBOOKLM_COOKIES"];
|
|
577
|
-
|
|
577
|
+
const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
|
|
578
|
+
if (envFile) {
|
|
579
|
+
cookieMap = loadCookiesFromFile(envFile);
|
|
580
|
+
} else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
|
|
581
|
+
const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
|
|
582
|
+
const storageState = JSON.parse(raw);
|
|
583
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
584
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
585
|
+
} else if (fs.existsSync("storage_state.json")) {
|
|
586
|
+
const raw = fs.readFileSync("storage_state.json", "utf-8");
|
|
587
|
+
const storageState = JSON.parse(raw);
|
|
588
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
589
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
590
|
+
} else if (envCookies) {
|
|
578
591
|
cookieMap = loadCookiesFromString(envCookies);
|
|
579
592
|
} else {
|
|
580
|
-
throw new exports.AuthError(
|
|
581
|
-
"No cookies provided. Pass cookies, cookiesFile, or cookiesObject to connect()."
|
|
582
|
-
);
|
|
593
|
+
throw new exports.AuthError("No session found. Run: npx notebooklm-sdk login");
|
|
583
594
|
}
|
|
584
595
|
}
|
|
585
596
|
const { csrfToken, sessionId } = await fetchTokens(cookieMap);
|
|
@@ -592,10 +603,11 @@ async function connect(opts) {
|
|
|
592
603
|
googleCookieHeader: googleCookieHeader ?? cookieHeader
|
|
593
604
|
};
|
|
594
605
|
}
|
|
595
|
-
var NOTEBOOKLM_URL;
|
|
606
|
+
var DEFAULT_SESSION_FILE, NOTEBOOKLM_URL;
|
|
596
607
|
var init_auth = __esm({
|
|
597
608
|
"src/auth.ts"() {
|
|
598
609
|
init_errors();
|
|
610
|
+
DEFAULT_SESSION_FILE = path.join(os.homedir(), ".notebooklm", "session.json");
|
|
599
611
|
NOTEBOOKLM_URL = "https://notebooklm.google.com/";
|
|
600
612
|
}
|
|
601
613
|
});
|
|
@@ -713,26 +725,6 @@ function parseArtifact(data, notebookId) {
|
|
|
713
725
|
_raw: Array.isArray(data) ? data : []
|
|
714
726
|
};
|
|
715
727
|
}
|
|
716
|
-
function parseNote(data) {
|
|
717
|
-
const id = typeof data[0] === "string" ? data[0] : "";
|
|
718
|
-
const content = typeof data[1] === "string" ? data[1] : "";
|
|
719
|
-
const title = typeof data[2] === "string" ? data[2] : null;
|
|
720
|
-
let createdAt = null;
|
|
721
|
-
let updatedAt = null;
|
|
722
|
-
if (Array.isArray(data[3]) && typeof data[3][0] === "number") {
|
|
723
|
-
try {
|
|
724
|
-
createdAt = new Date(data[3][0] * 1e3);
|
|
725
|
-
} catch {
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
if (Array.isArray(data[4]) && typeof data[4][0] === "number") {
|
|
729
|
-
try {
|
|
730
|
-
updatedAt = new Date(data[4][0] * 1e3);
|
|
731
|
-
} catch {
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
return { id, title, content, createdAt, updatedAt };
|
|
735
|
-
}
|
|
736
728
|
|
|
737
729
|
// src/api/artifacts.ts
|
|
738
730
|
function tripleNest(ids) {
|
|
@@ -742,18 +734,13 @@ function doubleNest(ids) {
|
|
|
742
734
|
return ids.map((id) => [id]);
|
|
743
735
|
}
|
|
744
736
|
var ArtifactsAPI = class {
|
|
745
|
-
constructor(rpc, auth) {
|
|
737
|
+
constructor(rpc, auth, notes) {
|
|
746
738
|
this.rpc = rpc;
|
|
747
739
|
this.auth = auth;
|
|
740
|
+
this.notes = notes;
|
|
748
741
|
}
|
|
749
742
|
async list(notebookId) {
|
|
750
|
-
const
|
|
751
|
-
const result = await this.rpc.call(exports.RPCMethod.LIST_ARTIFACTS, params, {
|
|
752
|
-
sourcePath: `/notebook/${notebookId}`,
|
|
753
|
-
allowNull: true
|
|
754
|
-
});
|
|
755
|
-
if (!Array.isArray(result) || !result.length) return [];
|
|
756
|
-
const rawList = Array.isArray(result[0]) ? result[0] : result;
|
|
743
|
+
const rawList = await this._listRaw(notebookId);
|
|
757
744
|
const artifacts = [];
|
|
758
745
|
for (const item of rawList) {
|
|
759
746
|
if (Array.isArray(item)) {
|
|
@@ -765,6 +752,15 @@ var ArtifactsAPI = class {
|
|
|
765
752
|
}
|
|
766
753
|
return artifacts;
|
|
767
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
|
+
}
|
|
768
764
|
async get(notebookId, artifactId) {
|
|
769
765
|
const artifacts = await this.list(notebookId);
|
|
770
766
|
return artifacts.find((a) => a.id === artifactId) ?? null;
|
|
@@ -944,6 +940,37 @@ var ArtifactsAPI = class {
|
|
|
944
940
|
];
|
|
945
941
|
return this._callGenerate(notebookId, params);
|
|
946
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
|
+
}
|
|
947
974
|
async createReport(notebookId, opts = {}) {
|
|
948
975
|
const format = opts.format ?? "briefing_doc";
|
|
949
976
|
const language = opts.language ?? "en";
|
|
@@ -1009,7 +1036,15 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1009
1036
|
sourcePath: `/notebook/${notebookId}`,
|
|
1010
1037
|
allowNull: true
|
|
1011
1038
|
});
|
|
1012
|
-
|
|
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);
|
|
1013
1048
|
}
|
|
1014
1049
|
// ---------------------------------------------------------------------------
|
|
1015
1050
|
// Polling / download
|
|
@@ -1065,6 +1100,48 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1065
1100
|
}
|
|
1066
1101
|
return null;
|
|
1067
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
|
+
}
|
|
1068
1145
|
// ---------------------------------------------------------------------------
|
|
1069
1146
|
// Internal
|
|
1070
1147
|
// ---------------------------------------------------------------------------
|
|
@@ -1121,6 +1198,34 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1121
1198
|
return { artifactId: null, status: "failed" };
|
|
1122
1199
|
}
|
|
1123
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
|
+
}
|
|
1124
1229
|
function sleep(ms) {
|
|
1125
1230
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1126
1231
|
}
|
|
@@ -1457,61 +1562,69 @@ var NotesAPI = class {
|
|
|
1457
1562
|
this.rpc = rpc;
|
|
1458
1563
|
}
|
|
1459
1564
|
async list(notebookId) {
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1466
|
-
if (!Array.isArray(result)) return { notes, mindMaps };
|
|
1467
|
-
try {
|
|
1468
|
-
const notesData = result[0];
|
|
1469
|
-
if (Array.isArray(notesData)) {
|
|
1470
|
-
for (const n of notesData) {
|
|
1471
|
-
if (Array.isArray(n)) notes.push(parseNote(n));
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
const mapsData = result[1];
|
|
1475
|
-
if (Array.isArray(mapsData)) {
|
|
1476
|
-
for (const m of mapsData) {
|
|
1477
|
-
if (Array.isArray(m)) {
|
|
1478
|
-
mindMaps.push({
|
|
1479
|
-
id: typeof m[0] === "string" ? m[0] : "",
|
|
1480
|
-
title: typeof m[2] === "string" ? m[2] : null,
|
|
1481
|
-
content: typeof m[1] === "string" ? m[1] : "",
|
|
1482
|
-
createdAt: Array.isArray(m[3]) && typeof m[3][0] === "number" ? new Date(m[3][0] * 1e3) : null
|
|
1483
|
-
});
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
} catch {
|
|
1488
|
-
}
|
|
1489
|
-
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));
|
|
1490
1571
|
}
|
|
1491
1572
|
async create(notebookId, content, title) {
|
|
1492
|
-
const
|
|
1493
|
-
const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTE,
|
|
1494
|
-
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
|
|
1495
1577
|
});
|
|
1496
|
-
|
|
1497
|
-
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() };
|
|
1498
1582
|
}
|
|
1499
1583
|
async update(notebookId, noteId, content, title) {
|
|
1500
|
-
const params = [notebookId, noteId, content, title ??
|
|
1501
|
-
|
|
1502
|
-
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
|
|
1503
1588
|
});
|
|
1504
|
-
if (Array.isArray(result)) return parseNote(result);
|
|
1505
1589
|
return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
|
|
1506
1590
|
}
|
|
1507
1591
|
async delete(notebookId, noteId) {
|
|
1508
|
-
const params = [notebookId,
|
|
1592
|
+
const params = [notebookId, null, [noteId]];
|
|
1509
1593
|
await this.rpc.call(exports.RPCMethod.DELETE_NOTE, params, {
|
|
1510
1594
|
sourcePath: `/notebook/${notebookId}`,
|
|
1511
1595
|
allowNull: true
|
|
1512
1596
|
});
|
|
1513
1597
|
return true;
|
|
1514
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
|
+
}
|
|
1515
1628
|
};
|
|
1516
1629
|
|
|
1517
1630
|
// src/api/research.ts
|
|
@@ -1648,7 +1761,7 @@ var ResearchAPI = class {
|
|
|
1648
1761
|
const webSources = sources.filter((s) => s.url && !reportSourceSet.has(s));
|
|
1649
1762
|
if (!webSources.length && !reportSources.length) return [];
|
|
1650
1763
|
const sourceArray = [
|
|
1651
|
-
...reportSources.map((s) => buildReportEntry(s.title, s.reportMarkdown)),
|
|
1764
|
+
...reportSources.filter((s) => s.reportMarkdown).map((s) => buildReportEntry(s.title, s.reportMarkdown)),
|
|
1652
1765
|
...webSources.map((s) => buildWebEntry(s.url, s.title))
|
|
1653
1766
|
];
|
|
1654
1767
|
const params = [null, [1], effectiveTaskId, notebookId, sourceArray];
|
|
@@ -2378,9 +2491,9 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2378
2491
|
const rpc = new RPCCore(auth, opts.timeoutMs);
|
|
2379
2492
|
this.notebooks = new NotebooksAPI(rpc);
|
|
2380
2493
|
this.sources = new SourcesAPI(rpc, auth);
|
|
2381
|
-
this.artifacts = new ArtifactsAPI(rpc, auth);
|
|
2382
|
-
this.chat = new ChatAPI(rpc, auth);
|
|
2383
2494
|
this.notes = new NotesAPI(rpc);
|
|
2495
|
+
this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
|
|
2496
|
+
this.chat = new ChatAPI(rpc, auth);
|
|
2384
2497
|
this.research = new ResearchAPI(rpc);
|
|
2385
2498
|
this.settings = new SettingsAPI(rpc);
|
|
2386
2499
|
this.sharing = new SharingAPI(rpc);
|
|
@@ -2397,7 +2510,7 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2397
2510
|
* Connect to NotebookLM using cookies.
|
|
2398
2511
|
* Fetches CSRF and session tokens from the NotebookLM homepage.
|
|
2399
2512
|
*/
|
|
2400
|
-
static async connect(opts, clientOpts = {}) {
|
|
2513
|
+
static async connect(opts = {}, clientOpts = {}) {
|
|
2401
2514
|
const auth = await connect(opts);
|
|
2402
2515
|
return new _NotebookLMClient(auth, clientOpts);
|
|
2403
2516
|
}
|