spyglasses 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 500f84bd8f684ffc7b0bc15d28743dcf50e5c883191efb166de087ecc001496c
4
- data.tar.gz: 4dd3be43d678475d4f16d1988f51ebcdab33a941add56040aed2c714ab725121
3
+ metadata.gz: c0a3d6de667a48f878769b774eccd367018ed52f1c6628cd730e1ff8c9fdaabe
4
+ data.tar.gz: 737c8e8de705a5dc0eadf303afe1b1fa7a5d2292a700642aa5d82416565661ae
5
5
  SHA512:
6
- metadata.gz: c1e7d7d73fc2a1551987c96eafada4e9e893fa9735adeb0174b13bf7f8da99740313c8135ed90ca40ef41a4db694939f8d8359d97a9632744c79691ec0ad531e
7
- data.tar.gz: b383b31150503a03161f3fcf2915d5049177d947c44187f5180623cb7205c368be298d719aa6061dd1478617c6042aa2cc151e4b41bc94f60a96a5abb4e3cae6
6
+ metadata.gz: 4493abc121849ba91d73c18ceaf060f0008b409bbe8c3402f74fa224c8906e6b4ea07e43e3c0f477bd028b37899db73c144ec49daabff0cce54f4878475d54a5
7
+ data.tar.gz: 22e71db5b421dcb9a78ef599261036d5b32dde80b92abb995933d57261a6b68b9a5505482fe9392c3420e363f8ff42b798c2659d2e012930e4df0d4cd97c5a41
data/CHANGELOG.md CHANGED
@@ -5,37 +5,56 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [1.1.0] - 2024-12-29
9
9
 
10
- ## [1.0.0] - 2024-01-XX
10
+ ### Fixed
11
+ - **BREAKING**: Fixed API payload format to match TypeScript collector endpoint
12
+ - Changed `platform_type` to `platformType` in JSON payload (camelCase)
13
+ - Ensured `ip_address` and `request_query` are never nil (required strings in API schema)
14
+ - Enhanced IP extraction from forwarded headers with comma-separated values
15
+ - Fixed 400 Bad Request errors when logging to collector endpoint
16
+ - Improved error handling and data validation for API compatibility
17
+
18
+ ### Changed
19
+ - **BREAKING**: JSON payload field names now use camelCase to match API expectations
20
+ - Enhanced middleware IP extraction with better fallback handling
21
+ - Improved request query string handling to ensure non-nil values
22
+
23
+ ## [1.0.1] - 2024-12-28
24
+
25
+ ### Added
26
+ - Comprehensive Ruby documentation in main docs site
27
+ - Rails-specific configuration examples
28
+ - Deployment platform guidance (Heroku, Railway, etc.)
29
+ - Troubleshooting section for common issues
30
+
31
+ ### Fixed
32
+ - Documentation formatting and code examples
33
+ - Installation instructions for various Ruby frameworks
34
+
35
+ ## [1.0.0] - 2024-12-28
11
36
 
12
37
  ### Added
13
38
  - 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
39
+ - AI bot detection with pattern matching
40
+ - AI referrer detection for traffic from AI platforms
41
+ - Flexible blocking rules via Spyglasses platform
42
+ - Non-blocking request logging to collector API
43
+ - Rack middleware for universal Ruby framework support
44
+ - Thread-safe operations for concurrent applications
24
45
  - 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
46
+ - Configuration via environment variables
47
+ - Debug logging support
48
+ - Pattern synchronization with API
49
+ - Support for Rails, Sinatra, and other Rack-based frameworks
50
+
51
+ ### Features
52
+ - **Bot Detection**: GPTBot, ClaudeBot, ChatGPT-User, Claude-User, and more
53
+ - **AI Referrer Detection**: ChatGPT, Claude, Perplexity, Gemini, Copilot
54
+ - **Blocking Rules**: Configurable via platform dashboard
55
+ - **Performance**: Pattern caching, background logging, minimal overhead
56
+ - **Framework Support**: Universal Rack middleware design
57
+
58
+ [Unreleased]: https://github.com/spyglasses/spyglasses-ruby/compare/v1.0.1...HEAD
59
+ [1.0.1]: https://github.com/spyglasses/spyglasses-ruby/compare/v1.0.0...v1.0.1
41
60
  [1.0.0]: https://github.com/spyglasses/spyglasses-ruby/releases/tag/v1.0.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- spyglasses (1.0.0)
4
+ spyglasses (1.1.0)
5
5
  json (>= 2.0)
6
6
  rack (>= 2.0)
7
7
 
data/README.md CHANGED
@@ -331,5 +331,5 @@ The gem is available as open source under the terms of the [MIT License](https:/
331
331
  ## Support
332
332
 
333
333
  - 📧 Email: support@spyglasses.io
334
- - 📖 Documentation: https://docs.spyglasses.io
334
+ - 📖 Documentation: https://www.spyglasses.io/docs/platforms/ruby
335
335
  - 🐛 Issues: https://github.com/spyglasses/spyglasses-ruby/issues
@@ -137,13 +137,17 @@ module Spyglasses
137
137
  def log_request_async(detection_result, request, status, response_time)
138
138
  return unless @configuration.api_key_present?
139
139
 
140
+ # Ensure ip_address is never nil
141
+ client_ip = extract_client_ip(request) || '127.0.0.1'
142
+
140
143
  request_info = {
141
144
  url: request.url,
142
145
  user_agent: request.user_agent || '',
143
- ip_address: extract_client_ip(request),
146
+ ip_address: client_ip,
144
147
  request_method: request.request_method,
145
148
  request_path: request.path,
146
- request_query: request.query_string.empty? ? nil : request.query_string,
149
+ # Ensure request_query is never nil - use empty string if no query
150
+ request_query: request.query_string || '',
147
151
  referrer: request.referrer,
148
152
  response_status: status,
149
153
  response_time_ms: (response_time * 1000).round,
@@ -157,13 +161,21 @@ module Spyglasses
157
161
 
158
162
  def extract_client_ip(request)
159
163
  # Try various headers to get the real client IP
160
- [
164
+ ip = [
161
165
  request.env['HTTP_X_FORWARDED_FOR'],
162
166
  request.env['HTTP_X_REAL_IP'],
163
167
  request.env['HTTP_CF_CONNECTING_IP'], # Cloudflare
164
168
  request.env['HTTP_X_CLIENT_IP'],
165
169
  request.env['REMOTE_ADDR']
166
- ].find { |ip| ip && !ip.empty? && ip != '127.0.0.1' } || request.ip
170
+ ].find { |ip| ip && !ip.empty? && ip != '127.0.0.1' }
171
+
172
+ # If we found an IP in headers, handle comma-separated lists (X-Forwarded-For)
173
+ if ip && ip.include?(',')
174
+ ip = ip.split(',').first.strip
175
+ end
176
+
177
+ # Return the found IP or fallback to request.ip
178
+ ip || request.ip
167
179
  end
168
180
 
169
181
  def extract_headers(request)
@@ -187,17 +187,17 @@ module Spyglasses
187
187
  {
188
188
  url: @url,
189
189
  user_agent: @user_agent,
190
- ip_address: @ip_address,
190
+ ip_address: @ip_address || '',
191
191
  request_method: @request_method,
192
192
  request_path: @request_path,
193
- request_query: @request_query,
193
+ request_query: @request_query || '',
194
194
  request_body: @request_body,
195
195
  referrer: @referrer,
196
196
  response_status: @response_status,
197
197
  response_time_ms: @response_time_ms,
198
198
  headers: @headers,
199
199
  timestamp: @timestamp,
200
- platform_type: @platform_type,
200
+ platformType: @platform_type,
201
201
  metadata: @metadata
202
202
  }
203
203
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spyglasses
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -0,0 +1,43 @@
1
+ require_relative 'lib/spyglasses/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'spyglasses'
5
+ spec.version = Spyglasses::VERSION
6
+ spec.authors = ['Orchestra AI, Inc.']
7
+ spec.email = ['support@spyglasses.io']
8
+
9
+ spec.summary = 'AI Agent Detection and Management for Ruby web applications'
10
+ spec.description = 'Spyglasses provides comprehensive AI agent detection and management capabilities for Ruby web applications, including Rails, Sinatra, and other Rack-based frameworks.'
11
+ spec.homepage = 'https://www.spyglasses.io'
12
+ spec.license = 'MIT'
13
+ spec.required_ruby_version = '>= 2.7.0'
14
+
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = 'https://github.com/spyglasses/spyglasses-ruby'
17
+ spec.metadata['documentation_uri'] = 'https://www.spyglasses.io/docs/platforms/ruby'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/spyglasses/spyglasses-ruby/blob/main/CHANGELOG.md'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
24
+ end
25
+ end
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ # Runtime dependencies
31
+ spec.add_dependency 'rack', '>= 2.0'
32
+ spec.add_dependency 'json', '>= 2.0'
33
+
34
+ # Development dependencies
35
+ spec.add_development_dependency 'bundler', '>= 2.0'
36
+ spec.add_development_dependency 'rake', '>= 13.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+ spec.add_development_dependency 'webmock', '~> 3.0'
39
+ spec.add_development_dependency 'rack-test', '~> 2.0'
40
+ spec.add_development_dependency 'rubocop', '~> 1.0'
41
+ spec.add_development_dependency 'simplecov', '~> 0.21'
42
+ spec.add_development_dependency 'yard', '~> 0.9'
43
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spyglasses
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Orchestra AI, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-06-24 00:00:00.000000000 Z
11
+ date: 2025-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -173,13 +173,14 @@ files:
173
173
  - lib/spyglasses/middleware.rb
174
174
  - lib/spyglasses/types.rb
175
175
  - lib/spyglasses/version.rb
176
+ - spyglasses.gemspec
176
177
  homepage: https://www.spyglasses.io
177
178
  licenses:
178
179
  - MIT
179
180
  metadata:
180
181
  homepage_uri: https://www.spyglasses.io
181
182
  source_code_uri: https://github.com/spyglasses/spyglasses-ruby
182
- documentation_uri: https://docs.spyglasses.io/ruby
183
+ documentation_uri: https://www.spyglasses.io/docs/platforms/ruby
183
184
  changelog_uri: https://github.com/spyglasses/spyglasses-ruby/blob/main/CHANGELOG.md
184
185
  post_install_message:
185
186
  rdoc_options: []