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.
@@ -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
@@ -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: []