notebooklm 4.7.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.
- checksums.yaml +7 -0
- data/README.md +155 -0
- data/lib/notebooklm/client.rb +154 -0
- data/lib/notebooklm/ergonomics.rb +80 -0
- data/lib/notebooklm/errors.rb +80 -0
- data/lib/notebooklm/generated.rb +2068 -0
- data/lib/notebooklm/model.rb +109 -0
- data/lib/notebooklm/version.rb +8 -0
- data/lib/notebooklm.rb +34 -0
- data/notebooklm.gemspec +33 -0
- metadata +61 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotebookLM
|
|
4
|
+
# The base of every shape the server answers with.
|
|
5
|
+
#
|
|
6
|
+
# Ruby has no structs to generate, so a shape is a thin reader over the
|
|
7
|
+
# JSON the server actually sent. Three properties matter:
|
|
8
|
+
#
|
|
9
|
+
# - every field the specification documents has a reader, under its Ruby
|
|
10
|
+
# name, so the shape is discoverable without reading the wire;
|
|
11
|
+
# - the body is kept verbatim in {#to_h}, and a field the server added
|
|
12
|
+
# after this version was generated is still reachable with `[]` — an
|
|
13
|
+
# unknown shape is never a wall;
|
|
14
|
+
# - an absent scalar reads as `nil`, never as `0` or `""`. A default of
|
|
15
|
+
# zero for a missing offset is how a citation gets attributed to the
|
|
16
|
+
# first page of a long document.
|
|
17
|
+
#
|
|
18
|
+
# Nested objects read as their own shape even when absent, so a chain like
|
|
19
|
+
# `answer.sources.citations` is safe on an answer that carried no sources.
|
|
20
|
+
class Model
|
|
21
|
+
# @param raw [Hash] the JSON object the server sent
|
|
22
|
+
def initialize(raw = {})
|
|
23
|
+
@raw = raw.is_a?(Hash) ? raw : {}
|
|
24
|
+
@read = {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Hash] the body exactly as the server sent it
|
|
28
|
+
attr_reader :raw
|
|
29
|
+
alias to_h raw
|
|
30
|
+
|
|
31
|
+
# Any field, documented or not, under its wire name.
|
|
32
|
+
# @return [Object, nil]
|
|
33
|
+
def [](key)
|
|
34
|
+
@raw[key.to_s]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [Boolean] whether the server sent this field at all
|
|
38
|
+
def key?(key)
|
|
39
|
+
@raw.key?(key.to_s)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def ==(other)
|
|
43
|
+
other.instance_of?(self.class) && other.raw == @raw
|
|
44
|
+
end
|
|
45
|
+
alias eql? ==
|
|
46
|
+
|
|
47
|
+
def hash
|
|
48
|
+
[self.class, @raw].hash
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def inspect
|
|
52
|
+
"#<#{self.class.name} #{@raw.inspect}>"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_json(*args)
|
|
56
|
+
@raw.to_json(*args)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class << self
|
|
60
|
+
# Declare one documented field.
|
|
61
|
+
#
|
|
62
|
+
# `type` is a class *name*, not a class: the generated file defines
|
|
63
|
+
# shapes in the order the specification lists them, and a shape can
|
|
64
|
+
# name one that comes later or itself. Resolving the name on first read
|
|
65
|
+
# makes the order irrelevant.
|
|
66
|
+
#
|
|
67
|
+
# @param ruby [Symbol] the reader's name
|
|
68
|
+
# @param wire [String] the key the server uses
|
|
69
|
+
# @param type [String, nil] the nested shape's class name, if any
|
|
70
|
+
# @param array [Boolean] whether the field holds a list
|
|
71
|
+
def field(ruby, wire, type: nil, array: false)
|
|
72
|
+
fields[ruby] = { wire: wire, type: type, array: array }
|
|
73
|
+
define_method(ruby) { read_field(ruby) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Hash{Symbol => Hash}] every declared field of this shape
|
|
77
|
+
def fields
|
|
78
|
+
@fields ||= {}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def read_field(ruby)
|
|
85
|
+
return @read[ruby] if @read.key?(ruby)
|
|
86
|
+
|
|
87
|
+
spec = self.class.fields.fetch(ruby)
|
|
88
|
+
@read[ruby] = coerce(@raw[spec[:wire]], spec)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def coerce(value, spec)
|
|
92
|
+
type = spec[:type]
|
|
93
|
+
if spec[:array]
|
|
94
|
+
list = value.is_a?(Array) ? value : []
|
|
95
|
+
type ? list.map { |item| resolve(type).new(item) } : list
|
|
96
|
+
elsif type
|
|
97
|
+
# Absent reads as an empty shape rather than nil, so a reader can be
|
|
98
|
+
# chained without a guard at every hop.
|
|
99
|
+
resolve(type).new(value.is_a?(Hash) ? value : {})
|
|
100
|
+
else
|
|
101
|
+
value
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def resolve(name)
|
|
106
|
+
NotebookLM.const_get(name)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NotebookLM
|
|
4
|
+
# The gem tracks the server's version on purpose: it is a client for one
|
|
5
|
+
# API, and a reader comparing the two should not have to map between two
|
|
6
|
+
# numbering schemes. Kept in step by `npm run version:sync`.
|
|
7
|
+
VERSION = '4.7.0'
|
|
8
|
+
end
|
data/lib/notebooklm.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'notebooklm/version'
|
|
4
|
+
require_relative 'notebooklm/errors'
|
|
5
|
+
require_relative 'notebooklm/model'
|
|
6
|
+
require_relative 'notebooklm/client'
|
|
7
|
+
require_relative 'notebooklm/generated'
|
|
8
|
+
require_relative 'notebooklm/ergonomics'
|
|
9
|
+
|
|
10
|
+
# A Ruby client for a local Gemini Notebook (NotebookLM) server.
|
|
11
|
+
#
|
|
12
|
+
# Google ships no public API for the consumer product, and the part that
|
|
13
|
+
# cannot live in a gem is the Google session: the login is bound to the machine
|
|
14
|
+
# it was made on, the session expires server-side while local cookies still
|
|
15
|
+
# look valid, and the browser driving it has to stay patched against
|
|
16
|
+
# detection. So this gem does not own a browser. It talks to a process that
|
|
17
|
+
# does — `npx -y @roomi-fields/notebooklm-mcp`, listening on loopback — and
|
|
18
|
+
# gives you its API with documented shapes instead of raw hashes.
|
|
19
|
+
#
|
|
20
|
+
# @example
|
|
21
|
+
# client = NotebookLM.local
|
|
22
|
+
# library = client.list_notebooks
|
|
23
|
+
# answer = client.ask(library.notebooks.first.id, 'What is this notebook about?')
|
|
24
|
+
# puts answer.answer
|
|
25
|
+
# answer.citations.each { |c| puts " [#{c.number}] #{c.source_text}" }
|
|
26
|
+
module NotebookLM
|
|
27
|
+
# A client for the usual loopback address and port.
|
|
28
|
+
#
|
|
29
|
+
# @param options [Hash] passed through to {Client#initialize}
|
|
30
|
+
# @return [Client]
|
|
31
|
+
def self.local(**options)
|
|
32
|
+
Client.new(**options)
|
|
33
|
+
end
|
|
34
|
+
end
|
data/notebooklm.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/notebooklm/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'notebooklm'
|
|
7
|
+
spec.version = NotebookLM::VERSION
|
|
8
|
+
spec.authors = ['Roomi Fields']
|
|
9
|
+
spec.license = 'MIT'
|
|
10
|
+
|
|
11
|
+
spec.summary = 'Ruby client for a local Gemini Notebook (NotebookLM) server'
|
|
12
|
+
spec.description = <<~TEXT
|
|
13
|
+
Google ships no public API for the consumer NotebookLM. This gem does not
|
|
14
|
+
own a browser: it talks to the local notebooklm-mcp server that does, and
|
|
15
|
+
gives you its 56 operations as documented Ruby methods, generated from the
|
|
16
|
+
server's OpenAPI specification. No runtime dependencies.
|
|
17
|
+
TEXT
|
|
18
|
+
|
|
19
|
+
spec.homepage = 'https://github.com/roomi-fields/notebooklm-mcp'
|
|
20
|
+
spec.metadata = {
|
|
21
|
+
'homepage_uri' => spec.homepage,
|
|
22
|
+
'source_code_uri' => 'https://github.com/roomi-fields/notebooklm-mcp/tree/main/gems/notebooklm',
|
|
23
|
+
'changelog_uri' => 'https://github.com/roomi-fields/notebooklm-mcp/blob/main/CHANGELOG.md',
|
|
24
|
+
'documentation_uri' => 'https://notebooklm.roomi-fields.com/',
|
|
25
|
+
'bug_tracker_uri' => 'https://github.com/roomi-fields/notebooklm-mcp/issues',
|
|
26
|
+
'rubygems_mfa_required' => 'true'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
spec.required_ruby_version = '>= 3.1'
|
|
30
|
+
|
|
31
|
+
spec.files = Dir['lib/**/*.rb'] + ['README.md', 'notebooklm.gemspec']
|
|
32
|
+
spec.require_paths = ['lib']
|
|
33
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: notebooklm
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 4.7.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Roomi Fields
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-11-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |
|
|
14
|
+
Google ships no public API for the consumer NotebookLM. This gem does not
|
|
15
|
+
own a browser: it talks to the local notebooklm-mcp server that does, and
|
|
16
|
+
gives you its 56 operations as documented Ruby methods, generated from the
|
|
17
|
+
server's OpenAPI specification. No runtime dependencies.
|
|
18
|
+
email:
|
|
19
|
+
executables: []
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- README.md
|
|
24
|
+
- lib/notebooklm.rb
|
|
25
|
+
- lib/notebooklm/client.rb
|
|
26
|
+
- lib/notebooklm/ergonomics.rb
|
|
27
|
+
- lib/notebooklm/errors.rb
|
|
28
|
+
- lib/notebooklm/generated.rb
|
|
29
|
+
- lib/notebooklm/model.rb
|
|
30
|
+
- lib/notebooklm/version.rb
|
|
31
|
+
- notebooklm.gemspec
|
|
32
|
+
homepage: https://github.com/roomi-fields/notebooklm-mcp
|
|
33
|
+
licenses:
|
|
34
|
+
- MIT
|
|
35
|
+
metadata:
|
|
36
|
+
homepage_uri: https://github.com/roomi-fields/notebooklm-mcp
|
|
37
|
+
source_code_uri: https://github.com/roomi-fields/notebooklm-mcp/tree/main/gems/notebooklm
|
|
38
|
+
changelog_uri: https://github.com/roomi-fields/notebooklm-mcp/blob/main/CHANGELOG.md
|
|
39
|
+
documentation_uri: https://notebooklm.roomi-fields.com/
|
|
40
|
+
bug_tracker_uri: https://github.com/roomi-fields/notebooklm-mcp/issues
|
|
41
|
+
rubygems_mfa_required: 'true'
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options: []
|
|
44
|
+
require_paths:
|
|
45
|
+
- lib
|
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '3.1'
|
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '0'
|
|
56
|
+
requirements: []
|
|
57
|
+
rubygems_version: 3.5.22
|
|
58
|
+
signing_key:
|
|
59
|
+
specification_version: 4
|
|
60
|
+
summary: Ruby client for a local Gemini Notebook (NotebookLM) server
|
|
61
|
+
test_files: []
|