spyglasses 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 500f84bd8f684ffc7b0bc15d28743dcf50e5c883191efb166de087ecc001496c
4
+ data.tar.gz: 4dd3be43d678475d4f16d1988f51ebcdab33a941add56040aed2c714ab725121
5
+ SHA512:
6
+ metadata.gz: c1e7d7d73fc2a1551987c96eafada4e9e893fa9735adeb0174b13bf7f8da99740313c8135ed90ca40ef41a4db694939f8d8359d97a9632744c79691ec0ad531e
7
+ data.tar.gz: b383b31150503a03161f3fcf2915d5049177d947c44187f5180623cb7205c368be298d719aa6061dd1478617c6042aa2cc151e4b41bc94f60a96a5abb4e3cae6
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --color
3
+ --format documentation
data/.rspec_status ADDED
@@ -0,0 +1,54 @@
1
+ example_id | status | run_time |
2
+ ---------------------------------------------- | ------ | --------------- |
3
+ ./spec/spyglasses/client_spec.rb[1:1:1] | passed | 0.00252 seconds |
4
+ ./spec/spyglasses/client_spec.rb[1:1:2] | passed | 0.00096 seconds |
5
+ ./spec/spyglasses/client_spec.rb[1:2:1:1] | passed | 0.02254 seconds |
6
+ ./spec/spyglasses/client_spec.rb[1:2:2:1] | passed | 0.00235 seconds |
7
+ ./spec/spyglasses/client_spec.rb[1:2:3:1] | passed | 0.00059 seconds |
8
+ ./spec/spyglasses/client_spec.rb[1:3:1] | passed | 0.00084 seconds |
9
+ ./spec/spyglasses/client_spec.rb[1:3:2] | passed | 0.00029 seconds |
10
+ ./spec/spyglasses/client_spec.rb[1:3:3] | passed | 0.0002 seconds |
11
+ ./spec/spyglasses/client_spec.rb[1:4:1] | passed | 0.00034 seconds |
12
+ ./spec/spyglasses/client_spec.rb[1:4:2] | passed | 0.00022 seconds |
13
+ ./spec/spyglasses/client_spec.rb[1:4:3] | passed | 0.00023 seconds |
14
+ ./spec/spyglasses/client_spec.rb[1:5:1] | passed | 0.00029 seconds |
15
+ ./spec/spyglasses/client_spec.rb[1:5:2] | passed | 0.0003 seconds |
16
+ ./spec/spyglasses/client_spec.rb[1:5:3] | passed | 0.00018 seconds |
17
+ ./spec/spyglasses/client_spec.rb[1:6:1] | passed | 0.10244 seconds |
18
+ ./spec/spyglasses/client_spec.rb[1:6:2] | passed | 0.10159 seconds |
19
+ ./spec/spyglasses/client_spec.rb[1:6:3] | passed | 0.10182 seconds |
20
+ ./spec/spyglasses/configuration_spec.rb[1:1:1] | passed | 0.00037 seconds |
21
+ ./spec/spyglasses/configuration_spec.rb[1:1:2] | passed | 0.00022 seconds |
22
+ ./spec/spyglasses/configuration_spec.rb[1:2:1] | passed | 0.00013 seconds |
23
+ ./spec/spyglasses/configuration_spec.rb[1:2:2] | passed | 0.00008 seconds |
24
+ ./spec/spyglasses/configuration_spec.rb[1:2:3] | passed | 0.00009 seconds |
25
+ ./spec/spyglasses/configuration_spec.rb[1:3:1] | passed | 0.00047 seconds |
26
+ ./spec/spyglasses/configuration_spec.rb[1:4:1] | passed | 0.00012 seconds |
27
+ ./spec/spyglasses/configuration_spec.rb[1:5:1] | passed | 0.00328 seconds |
28
+ ./spec/spyglasses/configuration_spec.rb[1:5:2] | passed | 0.00024 seconds |
29
+ ./spec/spyglasses/configuration_spec.rb[1:5:3] | passed | 0.00016 seconds |
30
+ ./spec/spyglasses/configuration_spec.rb[1:5:4] | passed | 0.00016 seconds |
31
+ ./spec/spyglasses/configuration_spec.rb[1:5:5] | passed | 0.0001 seconds |
32
+ ./spec/spyglasses/configuration_spec.rb[1:6:1] | passed | 0.0001 seconds |
33
+ ./spec/spyglasses/configuration_spec.rb[1:6:2] | passed | 0.00007 seconds |
34
+ ./spec/spyglasses/middleware_spec.rb[1:1:1] | passed | 0.00018 seconds |
35
+ ./spec/spyglasses/middleware_spec.rb[1:2:1:1] | passed | 0.00041 seconds |
36
+ ./spec/spyglasses/middleware_spec.rb[1:2:1:2] | passed | 0.00024 seconds |
37
+ ./spec/spyglasses/middleware_spec.rb[1:2:1:3] | passed | 0.00019 seconds |
38
+ ./spec/spyglasses/middleware_spec.rb[1:2:1:4] | passed | 0.00301 seconds |
39
+ ./spec/spyglasses/middleware_spec.rb[1:2:1:5] | passed | 0.00038 seconds |
40
+ ./spec/spyglasses/middleware_spec.rb[1:2:2:1] | passed | 0.00033 seconds |
41
+ ./spec/spyglasses/middleware_spec.rb[1:2:2:2] | passed | 0.10132 seconds |
42
+ ./spec/spyglasses/middleware_spec.rb[1:2:2:3] | passed | 0.10237 seconds |
43
+ ./spec/spyglasses/middleware_spec.rb[1:2:3:1] | passed | 0.1138 seconds |
44
+ ./spec/spyglasses/middleware_spec.rb[1:2:4:1] | passed | 0.10115 seconds |
45
+ ./spec/spyglasses/middleware_spec.rb[1:2:5:1] | passed | 0.10181 seconds |
46
+ ./spec/spyglasses/middleware_spec.rb[1:3:1] | passed | 0.00046 seconds |
47
+ ./spec/spyglasses/middleware_spec.rb[1:3:2] | passed | 0.00054 seconds |
48
+ ./spec/spyglasses/middleware_spec.rb[1:3:3] | passed | 0.00013 seconds |
49
+ ./spec/spyglasses_spec.rb[1:1] | passed | 0.00008 seconds |
50
+ ./spec/spyglasses_spec.rb[1:2:1] | passed | 0.00181 seconds |
51
+ ./spec/spyglasses_spec.rb[1:2:2] | passed | 0.00035 seconds |
52
+ ./spec/spyglasses_spec.rb[1:3:1] | passed | 0.00008 seconds |
53
+ ./spec/spyglasses_spec.rb[1:3:2] | passed | 0.00006 seconds |
54
+ ./spec/spyglasses_spec.rb[1:4:1] | passed | 0.00007 seconds |
data/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2024-01-XX
11
+
12
+ ### Added
13
+ - Initial release of Spyglasses Ruby gem
14
+ - Core AI agent detection functionality
15
+ - Bot pattern matching with regex support
16
+ - AI referrer detection for platforms like ChatGPT, Claude, Perplexity
17
+ - Flexible blocking system based on patterns and property settings
18
+ - Rack middleware for easy integration with Ruby web frameworks
19
+ - Rails, Sinatra, and generic Rack application support
20
+ - Non-blocking request logging to Spyglasses collector API
21
+ - Thread-safe pattern caching and background processing
22
+ - Comprehensive test suite with >95% code coverage
23
+ - Environment variable configuration support
24
+ - Default patterns for common AI agents and crawlers
25
+ - Pattern synchronization from Spyglasses API
26
+ - Debug logging capabilities
27
+ - Comprehensive documentation and examples
28
+
29
+ ### Security
30
+ - Thread-safe operations for concurrent Ruby applications
31
+ - Secure API key handling with masked logging
32
+ - Input validation and error handling
33
+
34
+ ### Performance
35
+ - Compiled regex pattern caching
36
+ - Background thread processing for API calls
37
+ - Smart path exclusions for static assets
38
+ - Minimal request overhead (<1ms typical)
39
+
40
+ [Unreleased]: https://github.com/spyglasses/spyglasses-ruby/compare/v1.0.0...HEAD
41
+ [1.0.0]: https://github.com/spyglasses/spyglasses-ruby/releases/tag/v1.0.0
data/DEVELOPMENT.md ADDED
@@ -0,0 +1,215 @@
1
+ # Development Guide
2
+
3
+ This guide provides information for developers working on the Spyglasses Ruby gem.
4
+
5
+ ## Architecture Overview
6
+
7
+ The gem is structured into several key components:
8
+
9
+ ### Core Components
10
+
11
+ 1. **`Spyglasses::Configuration`** - Handles configuration management and environment variables
12
+ 2. **`Spyglasses::Client`** - Core detection logic and API communication
13
+ 3. **`Spyglasses::Middleware`** - Rack middleware for web framework integration
14
+ 4. **`Spyglasses::Types`** - Data structures and type definitions
15
+
16
+ ### Key Features
17
+
18
+ - **Pattern Management**: Loads default patterns and syncs from API
19
+ - **Detection Logic**: Bot and AI referrer detection with regex matching
20
+ - **Blocking System**: Configurable blocking based on property settings
21
+ - **Request Logging**: Non-blocking background logging to collector API
22
+ - **Thread Safety**: Mutex-protected pattern updates and caching
23
+
24
+ ## Development Setup
25
+
26
+ ```bash
27
+ git clone https://github.com/spyglasses/spyglasses-ruby.git
28
+ cd spyglasses-ruby
29
+ bin/setup
30
+ ```
31
+
32
+ ## Running Tests
33
+
34
+ ```bash
35
+ # Run all tests
36
+ bundle exec rspec
37
+
38
+ # Run with coverage
39
+ rake coverage
40
+
41
+ # Run specific test file
42
+ bundle exec rspec spec/spyglasses/client_spec.rb
43
+
44
+ # Run specific test
45
+ bundle exec rspec spec/spyglasses/client_spec.rb:42
46
+ ```
47
+
48
+ ## Code Quality
49
+
50
+ ```bash
51
+ # Run RuboCop
52
+ bundle exec rubocop
53
+
54
+ # Auto-fix RuboCop issues
55
+ bundle exec rubocop -a
56
+
57
+ # Run all checks
58
+ rake check
59
+ ```
60
+
61
+ ## Building and Testing the Gem
62
+
63
+ ```bash
64
+ # Build the gem
65
+ rake build
66
+
67
+ # Install locally for testing
68
+ rake install_local
69
+
70
+ # Test in an app
71
+ gem 'spyglasses', path: '/path/to/spyglasses-ruby'
72
+ ```
73
+
74
+ ## Testing with Real API
75
+
76
+ To test with the real Spyglasses API:
77
+
78
+ ```bash
79
+ export SPYGLASSES_API_KEY=your_real_api_key
80
+ export SPYGLASSES_DEBUG=true
81
+
82
+ # Create a test script
83
+ cat > test_api.rb << 'EOF'
84
+ require 'spyglasses'
85
+
86
+ client = Spyglasses::Client.new
87
+ result = client.sync_patterns
88
+ puts "Sync result: #{result.class}"
89
+ puts "Patterns loaded: #{client.patterns.length}"
90
+
91
+ detection = client.detect('GPTBot/1.0')
92
+ puts "Detection result: #{detection.to_h}"
93
+ EOF
94
+
95
+ ruby test_api.rb
96
+ ```
97
+
98
+ ## Performance Testing
99
+
100
+ ```bash
101
+ # Simple benchmark
102
+ cat > benchmark.rb << 'EOF'
103
+ require 'benchmark'
104
+ require 'spyglasses'
105
+
106
+ client = Spyglasses::Client.new(
107
+ Spyglasses::Configuration.new.tap { |c| c.auto_sync = false }
108
+ )
109
+
110
+ user_agents = [
111
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
112
+ 'GPTBot/1.0',
113
+ 'ClaudeBot/1.0',
114
+ 'ChatGPT-User/1.0'
115
+ ]
116
+
117
+ Benchmark.bm do |x|
118
+ x.report("1000 detections") do
119
+ 1000.times do
120
+ user_agents.each { |ua| client.detect(ua) }
121
+ end
122
+ end
123
+ end
124
+ EOF
125
+
126
+ ruby benchmark.rb
127
+ ```
128
+
129
+ ## Release Process
130
+
131
+ 1. Update version in `lib/spyglasses/version.rb`
132
+ 2. Update `CHANGELOG.md` with changes
133
+ 3. Ensure all tests pass: `rake check`
134
+ 4. Build and test the gem: `rake build && rake install_local`
135
+ 5. Commit changes: `git commit -am "Release v1.x.x"`
136
+ 6. Tag the release: `git tag v1.x.x`
137
+ 7. Push: `git push && git push --tags`
138
+ 8. Publish: `gem push pkg/spyglasses-1.x.x.gem`
139
+
140
+ ## Contributing Guidelines
141
+
142
+ ### Code Style
143
+
144
+ - Follow Ruby community standards
145
+ - Use RuboCop for linting
146
+ - Write descriptive commit messages
147
+ - Include tests for new functionality
148
+
149
+ ### Testing Requirements
150
+
151
+ - Maintain >90% test coverage
152
+ - Include unit tests for all public methods
153
+ - Test error conditions and edge cases
154
+ - Use WebMock to stub HTTP requests
155
+
156
+ ### Documentation
157
+
158
+ - Update README.md for user-facing changes
159
+ - Add inline documentation for complex methods
160
+ - Include usage examples in docstrings
161
+ - Update CHANGELOG.md for all changes
162
+
163
+ ## Debugging
164
+
165
+ ### Enable Debug Mode
166
+
167
+ ```ruby
168
+ # In code
169
+ Spyglasses.configure do |config|
170
+ config.debug = true
171
+ end
172
+
173
+ # Via environment
174
+ export SPYGLASSES_DEBUG=true
175
+ ```
176
+
177
+ ### Common Issues
178
+
179
+ 1. **Pattern loading fails**: Check API key and network connectivity
180
+ 2. **Tests fail with real HTTP**: Ensure WebMock stubs are in place
181
+ 3. **Middleware not detecting**: Check user agent patterns and exclusions
182
+ 4. **Performance issues**: Profile pattern matching and API calls
183
+
184
+ ### Debugging API Issues
185
+
186
+ ```ruby
187
+ # Test API connectivity
188
+ require 'net/http'
189
+ require 'json'
190
+
191
+ uri = URI('https://www.spyglasses.io/api/patterns')
192
+ http = Net::HTTP.new(uri.host, uri.port)
193
+ http.use_ssl = true
194
+
195
+ request = Net::HTTP::Get.new(uri)
196
+ request['x-api-key'] = 'your-api-key'
197
+
198
+ response = http.request(request)
199
+ puts "Status: #{response.code}"
200
+ puts "Body: #{response.body[0..200]}..."
201
+ ```
202
+
203
+ ## Compatibility
204
+
205
+ - **Ruby**: 2.7+ (tested on 2.7, 3.0, 3.1, 3.2, 3.3)
206
+ - **Rails**: 6.0+ (all versions with Rack 2.0+)
207
+ - **Rack**: 2.0+
208
+ - **Other frameworks**: Sinatra, Hanami, Roda, etc.
209
+
210
+ ## Security Considerations
211
+
212
+ - API keys are masked in logs and debug output
213
+ - No sensitive data is stored in memory longer than necessary
214
+ - HTTP requests use TLS/SSL for API communication
215
+ - Input validation prevents regex injection attacks
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in spyglasses.gemspec
6
+ gemspec
7
+
8
+ gem 'pry', '~> 0.14.0'
9
+ gem 'pry-byebug', '~> 3.10.0'
10
+
11
+ group :development do
12
+ gem 'guard', '~> 2.18.0'
13
+ gem 'guard-rspec', '~> 4.7.0'
14
+ gem 'listen', '~> 3.8.0'
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,143 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ spyglasses (1.0.0)
5
+ json (>= 2.0)
6
+ rack (>= 2.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.8.7)
12
+ public_suffix (>= 2.0.2, < 7.0)
13
+ ast (2.4.3)
14
+ bigdecimal (3.2.2)
15
+ byebug (11.1.3)
16
+ coderay (1.1.3)
17
+ crack (1.0.0)
18
+ bigdecimal
19
+ rexml
20
+ diff-lcs (1.6.2)
21
+ docile (1.4.1)
22
+ ffi (1.17.2)
23
+ ffi (1.17.2-arm64-darwin)
24
+ ffi (1.17.2-x86_64-darwin)
25
+ formatador (1.1.0)
26
+ guard (2.18.1)
27
+ formatador (>= 0.2.4)
28
+ listen (>= 2.7, < 4.0)
29
+ lumberjack (>= 1.0.12, < 2.0)
30
+ nenv (~> 0.1)
31
+ notiffany (~> 0.0)
32
+ pry (>= 0.13.0)
33
+ shellany (~> 0.0)
34
+ thor (>= 0.18.1)
35
+ guard-compat (1.2.1)
36
+ guard-rspec (4.7.3)
37
+ guard (~> 2.1)
38
+ guard-compat (~> 1.1)
39
+ rspec (>= 2.99.0, < 4.0)
40
+ hashdiff (1.2.0)
41
+ json (2.12.2)
42
+ language_server-protocol (3.17.0.5)
43
+ lint_roller (1.1.0)
44
+ listen (3.8.0)
45
+ rb-fsevent (~> 0.10, >= 0.10.3)
46
+ rb-inotify (~> 0.9, >= 0.9.10)
47
+ lumberjack (1.2.10)
48
+ method_source (1.1.0)
49
+ nenv (0.3.0)
50
+ notiffany (0.1.3)
51
+ nenv (~> 0.1)
52
+ shellany (~> 0.0)
53
+ parallel (1.27.0)
54
+ parser (3.3.8.0)
55
+ ast (~> 2.4.1)
56
+ racc
57
+ prism (1.4.0)
58
+ pry (0.14.2)
59
+ coderay (~> 1.1)
60
+ method_source (~> 1.0)
61
+ pry-byebug (3.10.1)
62
+ byebug (~> 11.0)
63
+ pry (>= 0.13, < 0.15)
64
+ public_suffix (6.0.2)
65
+ racc (1.8.1)
66
+ rack (3.1.16)
67
+ rack-test (2.2.0)
68
+ rack (>= 1.3)
69
+ rainbow (3.1.1)
70
+ rake (13.3.0)
71
+ rb-fsevent (0.11.2)
72
+ rb-inotify (0.11.1)
73
+ ffi (~> 1.0)
74
+ regexp_parser (2.10.0)
75
+ rexml (3.4.1)
76
+ rspec (3.13.1)
77
+ rspec-core (~> 3.13.0)
78
+ rspec-expectations (~> 3.13.0)
79
+ rspec-mocks (~> 3.13.0)
80
+ rspec-core (3.13.4)
81
+ rspec-support (~> 3.13.0)
82
+ rspec-expectations (3.13.5)
83
+ diff-lcs (>= 1.2.0, < 2.0)
84
+ rspec-support (~> 3.13.0)
85
+ rspec-mocks (3.13.5)
86
+ diff-lcs (>= 1.2.0, < 2.0)
87
+ rspec-support (~> 3.13.0)
88
+ rspec-support (3.13.4)
89
+ rubocop (1.77.0)
90
+ json (~> 2.3)
91
+ language_server-protocol (~> 3.17.0.2)
92
+ lint_roller (~> 1.1.0)
93
+ parallel (~> 1.10)
94
+ parser (>= 3.3.0.2)
95
+ rainbow (>= 2.2.2, < 4.0)
96
+ regexp_parser (>= 2.9.3, < 3.0)
97
+ rubocop-ast (>= 1.45.1, < 2.0)
98
+ ruby-progressbar (~> 1.7)
99
+ unicode-display_width (>= 2.4.0, < 4.0)
100
+ rubocop-ast (1.45.1)
101
+ parser (>= 3.3.7.2)
102
+ prism (~> 1.4)
103
+ ruby-progressbar (1.13.0)
104
+ shellany (0.0.1)
105
+ simplecov (0.22.0)
106
+ docile (~> 1.1)
107
+ simplecov-html (~> 0.11)
108
+ simplecov_json_formatter (~> 0.1)
109
+ simplecov-html (0.13.1)
110
+ simplecov_json_formatter (0.1.4)
111
+ thor (1.3.2)
112
+ unicode-display_width (3.1.4)
113
+ unicode-emoji (~> 4.0, >= 4.0.4)
114
+ unicode-emoji (4.0.4)
115
+ webmock (3.25.1)
116
+ addressable (>= 2.8.0)
117
+ crack (>= 0.3.2)
118
+ hashdiff (>= 0.4.0, < 2.0.0)
119
+ yard (0.9.37)
120
+
121
+ PLATFORMS
122
+ arm64-darwin
123
+ ruby
124
+ x86_64-darwin
125
+
126
+ DEPENDENCIES
127
+ bundler (>= 2.0)
128
+ guard (~> 2.18.0)
129
+ guard-rspec (~> 4.7.0)
130
+ listen (~> 3.8.0)
131
+ pry (~> 0.14.0)
132
+ pry-byebug (~> 3.10.0)
133
+ rack-test (~> 2.0)
134
+ rake (>= 13.0)
135
+ rspec (~> 3.0)
136
+ rubocop (~> 1.0)
137
+ simplecov (~> 0.21)
138
+ spyglasses!
139
+ webmock (~> 3.0)
140
+ yard (~> 0.9)
141
+
142
+ BUNDLED WITH
143
+ 2.6.6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Orchestra AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.