devin_api 0.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 +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +21 -0
- data/README.md +136 -0
- data/lib/devin_api/client.rb +172 -0
- data/lib/devin_api/collection.rb +68 -0
- data/lib/devin_api/configuration.rb +22 -0
- data/lib/devin_api/core_ext/inflection.rb +16 -0
- data/lib/devin_api/endpoints/attachment.rb +23 -0
- data/lib/devin_api/endpoints/enterprise.rb +40 -0
- data/lib/devin_api/endpoints/knowledge.rb +55 -0
- data/lib/devin_api/endpoints/secrets.rb +28 -0
- data/lib/devin_api/endpoints/session.rb +39 -0
- data/lib/devin_api/endpoints/sessions.rb +38 -0
- data/lib/devin_api/endpoints.rb +23 -0
- data/lib/devin_api/error.rb +36 -0
- data/lib/devin_api/middleware/response/raise_error.rb +33 -0
- data/lib/devin_api/resources/attachment.rb +24 -0
- data/lib/devin_api/resources/base.rb +62 -0
- data/lib/devin_api/resources/enterprise.rb +22 -0
- data/lib/devin_api/resources/knowledge.rb +22 -0
- data/lib/devin_api/resources/secret.rb +18 -0
- data/lib/devin_api/resources/session.rb +22 -0
- data/lib/devin_api/version.rb +5 -0
- data/lib/devin_api.rb +14 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6c8ff85d6a59c3442dcf00776af4e5b8e80903f2ccdcad81f79db2a89d91cb9f
|
4
|
+
data.tar.gz: 3f14dc6752122758ae1a95915db964bc1812ce58d07afe26a02b45df1ef13457
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c99ddb448b733f01522c47664ecb8dc223792c7728a999cb87536aca2612893a7c989c21b8d89684f30e82df7210529940aed52cffb4b7de7b3a45b226b1dba9
|
7
|
+
data.tar.gz: 239eb0b9069025d187350c853d3c9ebd0bc65606dad01c32b1732fa312279d0e11a605066e823047d3f845accc2d5526ea604bb849f23d79ac4bde912f14ce89
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Masato Sugiyama
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# Devin API Client
|
2
|
+
[](https://github.com/smasato/devin_api_client_rb/actions/workflows/rspec.yml?query=branch%3Amain)
|
3
|
+
|
4
|
+
This Ruby gem is a client for the Devin API.
|
5
|
+
|
6
|
+
This gem is not officially supported by Cognition AI, Inc.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'devin_api'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
```bash
|
19
|
+
$ bundle install
|
20
|
+
```
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ gem install devin_api
|
26
|
+
```
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
### Endpoint-based API
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'devin_api'
|
34
|
+
|
35
|
+
client = DevinApi::Client.new do |config|
|
36
|
+
config.url = 'https://api.devin.ai'
|
37
|
+
config.access_token = 'your_access_token'
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sessions
|
41
|
+
sessions = client.list_sessions(limit: 10)
|
42
|
+
puts "Sessions: #{sessions}"
|
43
|
+
|
44
|
+
new_session = client.create_session(prompt: 'Build a simple web app')
|
45
|
+
session_id = new_session['id']
|
46
|
+
puts "Created session: #{session_id}"
|
47
|
+
|
48
|
+
session_details = client.get_session(session_id)
|
49
|
+
puts "Session details: #{session_details}"
|
50
|
+
|
51
|
+
message_response = client.send_message(session_id, message: 'How do I start?')
|
52
|
+
puts "Message response: #{message_response}"
|
53
|
+
|
54
|
+
# Upload files to a session
|
55
|
+
file = File.open('path/to/file.txt')
|
56
|
+
upload_response = client.upload_file(file)
|
57
|
+
puts "Upload response: #{upload_response}"
|
58
|
+
|
59
|
+
# Update session tags
|
60
|
+
tags_response = client.update_session_tags(session_id, tags: ['web', 'app'])
|
61
|
+
puts "Tags update response: #{tags_response}"
|
62
|
+
|
63
|
+
# Secrets
|
64
|
+
secrets = client.list_secrets
|
65
|
+
puts "Secrets: #{secrets}"
|
66
|
+
|
67
|
+
# Knowledge
|
68
|
+
knowledge_items = client.list_knowledge
|
69
|
+
puts "Knowledge items: #{knowledge_items}"
|
70
|
+
|
71
|
+
new_knowledge = client.create_knowledge(
|
72
|
+
name: 'API Documentation',
|
73
|
+
body: 'This is the API documentation for our service.',
|
74
|
+
trigger_description: 'When API documentation is needed'
|
75
|
+
)
|
76
|
+
knowledge_id = new_knowledge['id']
|
77
|
+
puts "Created knowledge: #{knowledge_id}"
|
78
|
+
|
79
|
+
# Enterprise (for enterprise customers)
|
80
|
+
audit_logs = client.list_audit_logs(start_time: '2023-01-01T00:00:00Z')
|
81
|
+
puts "Audit logs: #{audit_logs}"
|
82
|
+
|
83
|
+
consumption = client.get_enterprise_consumption(period: 'current_month')
|
84
|
+
puts "Consumption: #{consumption}"
|
85
|
+
```
|
86
|
+
|
87
|
+
### Resource-based API
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
require 'devin_api'
|
91
|
+
|
92
|
+
client = DevinApi::Client.new do |config|
|
93
|
+
config.url = 'https://api.devin.ai'
|
94
|
+
config.access_token = 'your_access_token'
|
95
|
+
end
|
96
|
+
|
97
|
+
# Resource-based usage
|
98
|
+
# Get a collection of sessions
|
99
|
+
sessions = client.sessions
|
100
|
+
sessions.each do |session|
|
101
|
+
puts "Session ID: #{session.id}, Prompt: #{session[:prompt]}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Create a new session
|
105
|
+
new_session = client.sessions.create(prompt: 'Build a simple web app')
|
106
|
+
puts "Created session: #{new_session.session_id}"
|
107
|
+
|
108
|
+
# Get a specific session
|
109
|
+
session = client.session('session_id')
|
110
|
+
|
111
|
+
# Send a message to the session
|
112
|
+
response = session.send_message('How do I start?')
|
113
|
+
|
114
|
+
# Upload files to the session
|
115
|
+
files = [File.open('path/to/file.txt')]
|
116
|
+
upload_response = session.upload_files(files)
|
117
|
+
|
118
|
+
# Update session tags
|
119
|
+
session.update_tags(['web', 'app'])
|
120
|
+
|
121
|
+
# Pagination
|
122
|
+
next_page = sessions.next_page
|
123
|
+
if next_page
|
124
|
+
next_page.each do |session|
|
125
|
+
puts "Next page session: #{session.id}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
## Contributing
|
131
|
+
|
132
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/smasato/devin_api_client_rb.
|
133
|
+
|
134
|
+
## License
|
135
|
+
|
136
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday/multipart'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
require 'devin_api/version'
|
8
|
+
require 'devin_api/configuration'
|
9
|
+
require 'devin_api/endpoints'
|
10
|
+
require 'devin_api/collection'
|
11
|
+
require 'devin_api/resources/session'
|
12
|
+
require 'devin_api/resources/secret'
|
13
|
+
require 'devin_api/resources/knowledge'
|
14
|
+
require 'devin_api/resources/enterprise'
|
15
|
+
require 'devin_api/resources/attachment'
|
16
|
+
|
17
|
+
module DevinApi
|
18
|
+
# The top-level class that handles configuration and connection to the Devin API.
|
19
|
+
class Client
|
20
|
+
include DevinApi::Endpoints
|
21
|
+
# @return [Configuration] Config instance
|
22
|
+
attr_reader :config
|
23
|
+
|
24
|
+
# Creates a new {Client} instance and yields {#config}.
|
25
|
+
#
|
26
|
+
# Requires a block to be given.
|
27
|
+
def initialize
|
28
|
+
raise ArgumentError, 'block not given' unless block_given?
|
29
|
+
|
30
|
+
@config = DevinApi::Configuration.new
|
31
|
+
yield config
|
32
|
+
|
33
|
+
check_url
|
34
|
+
set_access_token
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a connection if there is none, otherwise returns the existing connection.
|
38
|
+
#
|
39
|
+
# @return [Faraday::Connection] Faraday connection for the client
|
40
|
+
def connection
|
41
|
+
@connection ||= build_connection
|
42
|
+
end
|
43
|
+
|
44
|
+
# Executes a GET request
|
45
|
+
# @param [String] path The path to request
|
46
|
+
# @param [Hash] params Query parameters
|
47
|
+
# @return [Hash] Response body
|
48
|
+
def get(path, params = {})
|
49
|
+
response = connection.get(path, params)
|
50
|
+
parse_response(response)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Executes a POST request
|
54
|
+
# @param [String] path The path to request
|
55
|
+
# @param [Hash] params Body parameters
|
56
|
+
# @return [Hash] Response body
|
57
|
+
def post(path, params = {})
|
58
|
+
response = connection.post(path) do |req|
|
59
|
+
req.body = params.to_json
|
60
|
+
end
|
61
|
+
parse_response(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Executes a PUT request
|
65
|
+
# @param [String] path The path to request
|
66
|
+
# @param [Hash] params Body parameters
|
67
|
+
# @return [Hash] Response body
|
68
|
+
def put(path, params = {})
|
69
|
+
response = connection.put(path) do |req|
|
70
|
+
req.body = params.to_json
|
71
|
+
end
|
72
|
+
parse_response(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Executes a DELETE request
|
76
|
+
# @param [String] path The path to request
|
77
|
+
# @param [Hash] params Query parameters
|
78
|
+
# @return [Hash] Response body
|
79
|
+
def delete(path, params = {})
|
80
|
+
response = connection.delete(path, params)
|
81
|
+
parse_response(response)
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
# Called by {#connection} to build a connection.
|
87
|
+
#
|
88
|
+
# Uses middleware according to configuration options.
|
89
|
+
def build_connection
|
90
|
+
Faraday.new(url: config.url) do |conn|
|
91
|
+
# Response middlewares
|
92
|
+
conn.use DevinApi::Middleware::Response::RaiseError
|
93
|
+
conn.response :json, content_type: /\bjson$/
|
94
|
+
|
95
|
+
# Request middlewares
|
96
|
+
conn.request :json
|
97
|
+
conn.request :multipart
|
98
|
+
|
99
|
+
# Authentication
|
100
|
+
conn.headers['Authorization'] = "Bearer #{config.access_token}"
|
101
|
+
conn.headers['User-Agent'] = "DevinApi Ruby Client/#{DevinApi::VERSION}"
|
102
|
+
|
103
|
+
conn.adapter Faraday.default_adapter
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def parse_response(response)
|
110
|
+
return nil if response.status == 204
|
111
|
+
return {} if response.body.nil? || response.body.empty?
|
112
|
+
|
113
|
+
if response.body.is_a?(String) && response.body.start_with?('{', '[')
|
114
|
+
begin
|
115
|
+
JSON.parse(response.body)
|
116
|
+
rescue JSON::ParserError
|
117
|
+
response.body
|
118
|
+
end
|
119
|
+
else
|
120
|
+
response.body
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def check_url
|
125
|
+
raise ArgumentError, 'url must be provided' unless config.url
|
126
|
+
|
127
|
+
return if config.url.start_with?('https://')
|
128
|
+
|
129
|
+
raise ArgumentError, 'devin_api is ssl only; url must begin with https://'
|
130
|
+
end
|
131
|
+
|
132
|
+
def set_access_token
|
133
|
+
return if config.access_token
|
134
|
+
|
135
|
+
raise ArgumentError, 'access_token must be provided'
|
136
|
+
end
|
137
|
+
|
138
|
+
def method_missing(method, *args, &)
|
139
|
+
method_name = method.to_s
|
140
|
+
|
141
|
+
if resource_exists?(method_name.singularize)
|
142
|
+
resource_class = resource_class_for(method_name.singularize)
|
143
|
+
|
144
|
+
# Special case for 'knowledge' which is both singular and plural
|
145
|
+
return Collection.new(self, resource_class, args.first || {}) if method_name == 'knowledge'
|
146
|
+
|
147
|
+
if method_name.singularize == method_name
|
148
|
+
id = args.first
|
149
|
+
resource_class.new(self, get("#{resource_class.resource_path}/#{id}"))
|
150
|
+
else
|
151
|
+
Collection.new(self, resource_class, args.first || {})
|
152
|
+
end
|
153
|
+
else
|
154
|
+
super
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def respond_to_missing?(method, include_private = false)
|
159
|
+
resource_exists?(method.to_s.singularize) || super
|
160
|
+
end
|
161
|
+
|
162
|
+
def resource_class_for(resource_name)
|
163
|
+
class_name = resource_name.capitalize
|
164
|
+
DevinApi::Resources.const_get(class_name) if DevinApi::Resources.const_defined?(class_name)
|
165
|
+
end
|
166
|
+
|
167
|
+
def resource_exists?(resource_name)
|
168
|
+
class_name = resource_name.capitalize
|
169
|
+
DevinApi::Resources.const_defined?(class_name)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
# Collection class for handling resource lists and pagination
|
5
|
+
class Collection
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :resource_class, :client, :options, :resources
|
9
|
+
|
10
|
+
def initialize(client, resource_class, options = {})
|
11
|
+
@client = client
|
12
|
+
@resource_class = resource_class
|
13
|
+
@options = options
|
14
|
+
|
15
|
+
# Process array options
|
16
|
+
@options.each do |key, value|
|
17
|
+
@options[key] = value.join(',') if value.is_a?(Array)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def each(&)
|
22
|
+
fetch if @resources.nil?
|
23
|
+
@resources.each(&)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch
|
27
|
+
response = client.get(path, @options)
|
28
|
+
resource_key = "#{resource_class.resource_name.downcase}s"
|
29
|
+
|
30
|
+
@resources = if response[resource_key]
|
31
|
+
response[resource_key].map do |attrs|
|
32
|
+
resource_class.new(client, attrs)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
|
38
|
+
@next_cursor = response['pagination'] && response['pagination']['next_cursor']
|
39
|
+
@resources
|
40
|
+
end
|
41
|
+
|
42
|
+
def find(options = {})
|
43
|
+
if options[:id]
|
44
|
+
response = client.get("#{path}/#{options[:id]}")
|
45
|
+
resource_class.new(client, response)
|
46
|
+
else
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def next_page
|
52
|
+
return nil unless @next_cursor
|
53
|
+
|
54
|
+
new_options = @options.dup
|
55
|
+
new_options[:cursor] = @next_cursor
|
56
|
+
self.class.new(@client, @resource_class, new_options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def path
|
60
|
+
resource_class.resource_path
|
61
|
+
end
|
62
|
+
|
63
|
+
def create(attributes = {})
|
64
|
+
response = client.post(path, attributes)
|
65
|
+
resource_class.new(client, response)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
# Configuration class for the Devin API client
|
5
|
+
class Configuration
|
6
|
+
# @return [String] The base URL of the Devin API
|
7
|
+
attr_accessor :url
|
8
|
+
|
9
|
+
# @return [String] The access token for authentication
|
10
|
+
attr_accessor :access_token
|
11
|
+
|
12
|
+
# @return [Logger] The logger to use
|
13
|
+
attr_accessor :logger
|
14
|
+
|
15
|
+
# @return [Hash] Additional options to pass to Faraday
|
16
|
+
attr_accessor :options
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@options = {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'inflection'
|
4
|
+
|
5
|
+
module DevinApi
|
6
|
+
module CoreExt
|
7
|
+
# String extensions for inflection
|
8
|
+
module StringExtensions
|
9
|
+
def singularize
|
10
|
+
Inflection.singular(self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
String.include DevinApi::CoreExt::StringExtensions
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Endpoints
|
5
|
+
# Attachment endpoints for the Devin API
|
6
|
+
module Attachment
|
7
|
+
# Upload a file for Devin to work with during sessions
|
8
|
+
# @see https://docs.devin.ai/api-reference/attachments/upload-files-for-devin-to-work-with
|
9
|
+
#
|
10
|
+
# @param [File] file File object to upload
|
11
|
+
# @return [Hash] Response body
|
12
|
+
def upload_file(file)
|
13
|
+
payload = Faraday::UploadIO.new(
|
14
|
+
file.path,
|
15
|
+
file.content_type || 'application/octet-stream',
|
16
|
+
File.basename(file.path)
|
17
|
+
)
|
18
|
+
|
19
|
+
connection.post('/v1/attachments', payload).body
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Endpoints
|
5
|
+
# Enterprise endpoints for the Devin API
|
6
|
+
module Enterprise
|
7
|
+
# Get enterprise collection
|
8
|
+
#
|
9
|
+
# @param [Hash] options Query parameters
|
10
|
+
# @return [DevinApi::Collection] Collection of enterprise resources
|
11
|
+
def enterprise(options = {})
|
12
|
+
Collection.new(self, DevinApi::Resources::Enterprise, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
# List all audit logs
|
16
|
+
# @see https://docs.devin.ai/api-reference/audit-logs/list-audit-logs
|
17
|
+
#
|
18
|
+
# @param [Hash] params Query parameters
|
19
|
+
# @option params [Integer] :limit Maximum number of logs to return (default: 100, min: 1)
|
20
|
+
# @option params [String] :before Filter logs before a specific timestamp
|
21
|
+
# @option params [String] :after Filter logs after a specific timestamp
|
22
|
+
# @return [Hash] Response body with audit logs
|
23
|
+
def list_audit_logs(params = {})
|
24
|
+
get('/v1/enterprise/audit-logs', params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get enterprise consumption data
|
28
|
+
# @see https://docs.devin.ai/api-reference/enterprise/get-enterprise-consumption-data
|
29
|
+
#
|
30
|
+
# @param [Hash] params Query parameters
|
31
|
+
# @option params [String] :period Period for consumption data (e.g., 'current_month', 'previous_month')
|
32
|
+
# @option params [String] :start_date Start date for custom period (ISO 8601 format)
|
33
|
+
# @option params [String] :end_date End date for custom period (ISO 8601 format)
|
34
|
+
# @return [Hash] Response body with consumption data
|
35
|
+
def get_enterprise_consumption(params = {})
|
36
|
+
get('/v1/enterprise/consumption', params)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Endpoints
|
5
|
+
# Knowledge endpoints for the Devin API
|
6
|
+
module Knowledge
|
7
|
+
# List knowledge
|
8
|
+
# @see https://docs.devin.ai/api-reference/knowledge/list-knowledge
|
9
|
+
#
|
10
|
+
# @param [Hash] params Query parameters
|
11
|
+
# @option params [Integer] :limit Maximum number of knowledge items to return (default: 100, max: 1000)
|
12
|
+
# @option params [Integer] :offset Number of knowledge items to skip for pagination (default: 0)
|
13
|
+
# @return [Hash] Response body with knowledge and folders
|
14
|
+
def list_knowledge(params = {})
|
15
|
+
get('/v1/knowledge', params)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create knowledge
|
19
|
+
# @see https://docs.devin.ai/api-reference/knowledge/create-knowledge
|
20
|
+
#
|
21
|
+
# @param [Hash] params Body parameters
|
22
|
+
# @option params [String] :body Content of the knowledge (required)
|
23
|
+
# @option params [String] :name Name of the knowledge (required)
|
24
|
+
# @option params [String] :trigger_description Description of when this knowledge should be used (required)
|
25
|
+
# @option params [String] :parent_folder_id ID of the folder that this knowledge is located in
|
26
|
+
# @return [Hash] Response body with the created knowledge
|
27
|
+
def create_knowledge(params = {})
|
28
|
+
post('/v1/knowledge', params)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Update knowledge
|
32
|
+
# @see https://docs.devin.ai/api-reference/knowledge/update-knowledge
|
33
|
+
#
|
34
|
+
# @param [String] knowledge_id The ID of the knowledge to update
|
35
|
+
# @param [Hash] params Body parameters
|
36
|
+
# @option params [String] :body Content of the knowledge
|
37
|
+
# @option params [String] :name Name of the knowledge
|
38
|
+
# @option params [String] :trigger_description Description of when this knowledge should be used
|
39
|
+
# @option params [String] :parent_folder_id ID of the folder that this knowledge is located in
|
40
|
+
# @return [Hash] Response body with the updated knowledge
|
41
|
+
def update_knowledge(knowledge_id, params = {})
|
42
|
+
put("/v1/knowledge/#{knowledge_id}", params)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Delete knowledge
|
46
|
+
# @see https://docs.devin.ai/api-reference/knowledge/delete-knowledge
|
47
|
+
#
|
48
|
+
# @param [String] knowledge_id The ID of the knowledge to delete
|
49
|
+
# @return [nil] Returns nil on success (204 No Content)
|
50
|
+
def delete_knowledge(knowledge_id)
|
51
|
+
delete("/v1/knowledge/#{knowledge_id}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Endpoints
|
5
|
+
# Secrets endpoints for the Devin API
|
6
|
+
module Secrets
|
7
|
+
# List all secrets metadata
|
8
|
+
# @see https://docs.devin.ai/api-reference/sessions/list-secrets
|
9
|
+
#
|
10
|
+
# @param [Hash] params Query parameters
|
11
|
+
# @option params [Integer] :limit Maximum number of secrets to return (default: 100, max: 1000)
|
12
|
+
# @option params [Integer] :offset Number of secrets to skip for pagination (default: 0)
|
13
|
+
# @return [Hash] Response body with secrets metadata (does not return secret values)
|
14
|
+
def list_secrets(params = {})
|
15
|
+
get('/v1/secrets', params)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Delete a secret
|
19
|
+
# @see https://docs.devin.ai/api-reference/sessions/delete-secret
|
20
|
+
#
|
21
|
+
# @param [String] secret_id The ID of the secret to delete
|
22
|
+
# @return [nil] Returns nil on success (204 No Content)
|
23
|
+
def delete_secret(secret_id)
|
24
|
+
delete("/v1/secrets/#{secret_id}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Endpoints
|
5
|
+
# Session endpoint for the Devin API
|
6
|
+
module Session
|
7
|
+
# Retrieve details about an existing session
|
8
|
+
# @see https://docs.devin.ai/api-reference/sessions/retrieve-details-about-an-existing-session
|
9
|
+
#
|
10
|
+
# @param [String] session_id The ID of the session
|
11
|
+
# @return [Hash] Response body
|
12
|
+
def get_session(session_id)
|
13
|
+
get("/v1/session/#{session_id}")
|
14
|
+
end
|
15
|
+
|
16
|
+
# Send a message to an existing session
|
17
|
+
# @see https://docs.devin.ai/api-reference/sessions/send-a-message-to-an-existing-devin-session
|
18
|
+
#
|
19
|
+
# @param [String] session_id The ID of the session
|
20
|
+
# @param [Hash] params Body parameters
|
21
|
+
# @option params [String] :message The message to send (required)
|
22
|
+
# @return [Hash] Response body
|
23
|
+
def send_message(session_id, params = {})
|
24
|
+
post("/v1/session/#{session_id}/messages", params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Update session tags
|
28
|
+
# @see https://docs.devin.ai/api-reference/sessions/update-session-tags
|
29
|
+
#
|
30
|
+
# @param [String] session_id The ID of the session
|
31
|
+
# @param [Hash] params Body parameters
|
32
|
+
# @option params [Array<String>] :tags Tags to associate with the session (required)
|
33
|
+
# @return [Hash] Response body
|
34
|
+
def update_session_tags(session_id, params = {})
|
35
|
+
put("/v1/session/#{session_id}/tags", params)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Endpoints
|
5
|
+
# Sessions endpoints for the Devin API
|
6
|
+
module Sessions
|
7
|
+
# List all sessions
|
8
|
+
# @see https://docs.devin.ai/api-reference/sessions/list-sessions
|
9
|
+
#
|
10
|
+
# @param [Hash] params Query parameters
|
11
|
+
# @option params [Integer] :limit Maximum number of sessions to return (default: 100, max: 1000)
|
12
|
+
# @option params [Integer] :offset Number of sessions to skip for pagination (default: 0)
|
13
|
+
# @option params [Array<String>] :tags Filter sessions by tags
|
14
|
+
# @return [Hash] Response body
|
15
|
+
def list_sessions(params = {})
|
16
|
+
get('/v1/sessions', params)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new session
|
20
|
+
# @see https://docs.devin.ai/api-reference/sessions/create-a-new-devin-session
|
21
|
+
#
|
22
|
+
# @param [Hash] params Body parameters
|
23
|
+
# @option params [String] :prompt The task description for Devin (required)
|
24
|
+
# @option params [String, nil] :snapshot_id ID of a machine snapshot to use
|
25
|
+
# @option params [Boolean, nil] :unlisted Whether the session should be unlisted
|
26
|
+
# @option params [Boolean, nil] :idempotent Enable idempotent session creation
|
27
|
+
# @option params [Integer, nil] :max_acu_limit Maximum ACU limit for the session
|
28
|
+
# @option params [Array<String>, nil] :secret_ids Array of secret IDs to use. If nil, use all secrets. If empty array, use no secrets.
|
29
|
+
# @option params [Array<String>, nil] :knowledge_ids Array of knowledge IDs to use. If nil, use all knowledge. If empty array, use no knowledge.
|
30
|
+
# @option params [Array<String>, nil] :tags Array of tags to add to the session.
|
31
|
+
# @option params [String, nil] :title Custom title for the session. If nil, a title will be generated automatically.
|
32
|
+
# @return [Hash] Response body
|
33
|
+
def create_session(params = {})
|
34
|
+
post('/v1/sessions', params)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devin_api/endpoints/sessions'
|
4
|
+
require 'devin_api/endpoints/session'
|
5
|
+
require 'devin_api/endpoints/secrets'
|
6
|
+
require 'devin_api/endpoints/knowledge'
|
7
|
+
require 'devin_api/endpoints/enterprise'
|
8
|
+
require 'devin_api/endpoints/attachment'
|
9
|
+
|
10
|
+
module DevinApi
|
11
|
+
# Endpoints module for the Devin API
|
12
|
+
module Endpoints
|
13
|
+
# Include all endpoint modules
|
14
|
+
def self.included(base)
|
15
|
+
base.include(Sessions)
|
16
|
+
base.include(Session)
|
17
|
+
base.include(Secrets)
|
18
|
+
base.include(Knowledge)
|
19
|
+
base.include(Enterprise)
|
20
|
+
base.include(Attachment)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Error
|
5
|
+
# Base class for all errors
|
6
|
+
class Error < StandardError
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(response = nil)
|
10
|
+
@response = response
|
11
|
+
super(build_message)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_message
|
17
|
+
return response if response.is_a?(String)
|
18
|
+
return nil unless response
|
19
|
+
|
20
|
+
message = "Status: #{response[:status]}"
|
21
|
+
|
22
|
+
message += " - #{response[:body]['error']}" if response[:body].is_a?(Hash) && response[:body]['error']
|
23
|
+
|
24
|
+
message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ClientError < Error; end
|
29
|
+
class ServerError < Error; end
|
30
|
+
class BadRequest < ClientError; end
|
31
|
+
class Unauthorized < ClientError; end
|
32
|
+
class Forbidden < ClientError; end
|
33
|
+
class NotFound < ClientError; end
|
34
|
+
class RateLimited < ClientError; end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Middleware
|
5
|
+
module Response
|
6
|
+
# Middleware for raising errors based on HTTP status
|
7
|
+
class RaiseError < Faraday::Middleware
|
8
|
+
def call(env)
|
9
|
+
@app.call(env).on_complete do |response|
|
10
|
+
handle_error_response(response)
|
11
|
+
end
|
12
|
+
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
|
13
|
+
raise DevinApi::Error::ClientError, "Connection error: #{e.message}"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def handle_error_response(response)
|
19
|
+
status = response[:status]
|
20
|
+
case status
|
21
|
+
when 400 then raise DevinApi::Error::BadRequest, response
|
22
|
+
when 401 then raise DevinApi::Error::Unauthorized, response
|
23
|
+
when 403 then raise DevinApi::Error::Forbidden, response
|
24
|
+
when 404 then raise DevinApi::Error::NotFound, response
|
25
|
+
when 429 then raise DevinApi::Error::RateLimited, response
|
26
|
+
when 400..499 then raise DevinApi::Error::ClientError, response
|
27
|
+
when 500..599 then raise DevinApi::Error::ServerError, response
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devin_api/resources/base'
|
4
|
+
|
5
|
+
module DevinApi
|
6
|
+
module Resources
|
7
|
+
# Attachment resource for the Devin API
|
8
|
+
class Attachment < Base
|
9
|
+
def path
|
10
|
+
'/v1/attachments'
|
11
|
+
end
|
12
|
+
|
13
|
+
def upload_file(file)
|
14
|
+
payload = Faraday::UploadIO.new(
|
15
|
+
file.path,
|
16
|
+
file.content_type || 'application/octet-stream',
|
17
|
+
File.basename(file.path)
|
18
|
+
)
|
19
|
+
|
20
|
+
client.connection.post(path, payload).body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi
|
4
|
+
module Resources
|
5
|
+
# Base class for all API resources
|
6
|
+
class Base
|
7
|
+
attr_reader :client, :attributes
|
8
|
+
|
9
|
+
def initialize(client, attributes = {})
|
10
|
+
@client = client
|
11
|
+
@attributes = attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
@attributes[key.to_s] || @attributes[key.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
@attributes[key.to_sym] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
self[:id]
|
24
|
+
end
|
25
|
+
|
26
|
+
def path
|
27
|
+
"#{self.class.resource_path}/#{id}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_hash
|
31
|
+
@attributes.dup
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(method, *_args)
|
35
|
+
if @attributes.key?(method.to_s)
|
36
|
+
@attributes[method.to_s]
|
37
|
+
elsif @attributes.key?(method.to_sym)
|
38
|
+
@attributes[method.to_sym]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def respond_to_missing?(method, include_private = false)
|
43
|
+
@attributes.key?(method.to_s) || @attributes.key?(method.to_sym) || super
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def resource_name
|
48
|
+
@resource_name ||= name.split('::').last.downcase
|
49
|
+
end
|
50
|
+
|
51
|
+
def resource_path
|
52
|
+
"/v1/#{resource_name.gsub('_', '-')}s"
|
53
|
+
end
|
54
|
+
|
55
|
+
def create(client, attributes = {})
|
56
|
+
response = client.post(resource_path, attributes)
|
57
|
+
new(client, response)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devin_api/resources/base'
|
4
|
+
|
5
|
+
module DevinApi
|
6
|
+
module Resources
|
7
|
+
# Enterprise resource for the Devin API
|
8
|
+
class Enterprise < Base
|
9
|
+
def path
|
10
|
+
'/v1/enterprise'
|
11
|
+
end
|
12
|
+
|
13
|
+
def audit_logs(params = {})
|
14
|
+
client.get("#{path}/audit-logs", params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def consumption(params = {})
|
18
|
+
client.get("#{path}/consumption", params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devin_api/resources/base'
|
4
|
+
|
5
|
+
module DevinApi
|
6
|
+
module Resources
|
7
|
+
# Knowledge resource for the Devin API
|
8
|
+
class Knowledge < Base
|
9
|
+
def path
|
10
|
+
"/v1/knowledge/#{id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def update(attributes = {})
|
14
|
+
client.put(path, attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete
|
18
|
+
client.delete(path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devin_api/resources/base'
|
4
|
+
|
5
|
+
module DevinApi
|
6
|
+
module Resources
|
7
|
+
# Secret resource for the Devin API
|
8
|
+
class Secret < Base
|
9
|
+
def path
|
10
|
+
"/v1/secrets/#{id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def delete
|
14
|
+
client.delete(path)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devin_api/resources/base'
|
4
|
+
|
5
|
+
module DevinApi
|
6
|
+
module Resources
|
7
|
+
# Session resource for the Devin API
|
8
|
+
class Session < Base
|
9
|
+
def path
|
10
|
+
"/v1/session/#{session_id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_message(message)
|
14
|
+
client.post("#{path}/messages", { message: message })
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_tags(tags)
|
18
|
+
client.put("#{path}/tags", { tags: tags })
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/devin_api.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevinApi; end
|
4
|
+
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday/multipart'
|
7
|
+
require 'inflection'
|
8
|
+
|
9
|
+
require 'devin_api/version'
|
10
|
+
require 'devin_api/core_ext/inflection'
|
11
|
+
require 'devin_api/error'
|
12
|
+
require 'devin_api/middleware/response/raise_error'
|
13
|
+
require 'devin_api/client'
|
14
|
+
require 'devin_api/endpoints'
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devin_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masato Sugiyama
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-05-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday-multipart
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hashie
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: inflection
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
description: Unofficial Ruby client library for the Devin API
|
70
|
+
email:
|
71
|
+
- public@smasato.net
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- CHANGELOG.md
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- lib/devin_api.rb
|
80
|
+
- lib/devin_api/client.rb
|
81
|
+
- lib/devin_api/collection.rb
|
82
|
+
- lib/devin_api/configuration.rb
|
83
|
+
- lib/devin_api/core_ext/inflection.rb
|
84
|
+
- lib/devin_api/endpoints.rb
|
85
|
+
- lib/devin_api/endpoints/attachment.rb
|
86
|
+
- lib/devin_api/endpoints/enterprise.rb
|
87
|
+
- lib/devin_api/endpoints/knowledge.rb
|
88
|
+
- lib/devin_api/endpoints/secrets.rb
|
89
|
+
- lib/devin_api/endpoints/session.rb
|
90
|
+
- lib/devin_api/endpoints/sessions.rb
|
91
|
+
- lib/devin_api/error.rb
|
92
|
+
- lib/devin_api/middleware/response/raise_error.rb
|
93
|
+
- lib/devin_api/resources/attachment.rb
|
94
|
+
- lib/devin_api/resources/base.rb
|
95
|
+
- lib/devin_api/resources/enterprise.rb
|
96
|
+
- lib/devin_api/resources/knowledge.rb
|
97
|
+
- lib/devin_api/resources/secret.rb
|
98
|
+
- lib/devin_api/resources/session.rb
|
99
|
+
- lib/devin_api/version.rb
|
100
|
+
homepage: https://github.com/smasato/devin_api_client_rb
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata:
|
104
|
+
homepage_uri: https://github.com/smasato/devin_api_client_rb
|
105
|
+
source_code_uri: https://github.com/smasato/devin_api_client_rb
|
106
|
+
changelog_uri: https://github.com/smasato/devin_api_client_rb/blob/main/CHANGELOG.md
|
107
|
+
rubygems_mfa_required: 'true'
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 3.2.0
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubygems_version: 3.5.22
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Unofficial Ruby client for the Devin API
|
127
|
+
test_files: []
|