relay.app 0.5.0 → 0.6.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: 3caa789124f0cee625277b4f1e3472e04c12d1aeeaa193ed6146ba593061d57d
4
- data.tar.gz: 83e22033af1263c50c8746958d56e07836f5570a02399e894be48468de750c74
3
+ metadata.gz: 51b2db665dd9baa7f6003b0cdd360d07ab3e1cba08f451400d7063f4bce234d5
4
+ data.tar.gz: e058de42d7416b06df5e35c605c734d8b7af9f93d7fda9015b81f576cca7d286
5
5
  SHA512:
6
- metadata.gz: 848201fc63074254365539657cd709eb60dd3fbe729d7b1421d4076e58bc6dcb82e437058c1fa8f6cb7ce1a887a6ba3583724abc044ce9e10179d76884f46f91
7
- data.tar.gz: 347db524516e78944bce97e8ee6f96944190206fb057f85be65eada7a1fcd3449bb3001be17016ad2125a190600deb6e08f7fcbfff69d4fba77285203860dbb9
6
+ metadata.gz: 2efe61c19fb25fcbe28dfc94ad68bcae7fe6927950edf0ab617fe473166590491d465913d185ffa94f35d6e4458b2ed0534afddb71a160faba72445bcb36b674
7
+ data.tar.gz: 1185e1e81c2c8180ee17165579400a688fc146af4e40f2cb9d03ed2e149274ff2bbfd7fdd7125c638c995beab6e955b2c06ac63995bfb01d9d093d4bb9158e9d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,45 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Change
6
+
7
+ * **Move the default Relay home to `~/.config/relay`** <br>
8
+ Change Relay's default writable home directory from `~/.relay` to
9
+ `~/.config/relay`, and update the README examples for user-installed
10
+ tools to match the new location.
11
+
12
+ * **Add `relay console`** <br>
13
+ Add a console command that boots Relay and starts IRB in the loaded
14
+ application process for interactive debugging.
15
+
16
+ * **Add AWS Bedrock provider support** <br>
17
+ Add Bedrock to Relay's provider registry and persisted context
18
+ initialization, and extend `relay configure` to prompt for AWS access
19
+ key credentials.
20
+
21
+ * **Prepopulate provider API key prompts from environment** <br>
22
+ Let `relay configure` reuse existing provider secrets from process
23
+ environment variables such as `OPENAI_API_KEY` and
24
+ `DEEPSEEK_API_KEY`, while still writing Relay's canonical `*_SECRET`
25
+ keys to `~/.relay/env`.
26
+
27
+ ### Fix
28
+
29
+ * **Launch Falcon with the current Ruby interpreter** <br>
30
+ Change `relay start` to exec Falcon through `RbConfig.ruby -S` so the
31
+ server process uses the same Ruby and gem environment as Relay instead
32
+ of whichever `falcon` binary appears first on `PATH`.
33
+
34
+ * **Load user-installed tools through Zeitwerk** <br>
35
+ Replace manual loading of `~/.config/relay/tools/*.rb` with a dedicated
36
+ Zeitwerk loader so development reloads unload and recreate user tools
37
+ instead of reopening existing classes.
38
+
39
+ * **Activate the `llm.rb` gem before `require "llm"`** <br>
40
+ Avoid RubyGems loading the unrelated `llm` gem when both gems are
41
+ installed by explicitly activating `llm.rb` before requiring its
42
+ `llm` entrypoint.
43
+
5
44
  ## v0.5.0
6
45
 
7
46
  Model catalog workflow release.
data/README.md CHANGED
@@ -7,9 +7,9 @@ set of dependencies - built on Roda, Sequel, Falcon, [llm.rb](https://github.com
7
7
  HTMX and web sockets.
8
8
 
9
9
  There is support for connecting to MCP servers too - both HTTP and stdio. You can
10
- add your own tools to `~/.relay/tools` which is a neat way to extend the environment
11
- with your own functionality. The database uses SQLite3 to keep things simple - the
12
- goal is to have something you can setup in under two minutes.
10
+ add your own tools to `~/.config/relay/tools` which is a neat way to extend the
11
+ environment with your own functionality. The database uses SQLite3 to keep things
12
+ simple - the goal is to have something you can setup in under two minutes.
13
13
 
14
14
  ## Getting started
15
15
 
@@ -62,7 +62,7 @@ Very easy.
62
62
 
63
63
  ## How do I add my own tool?
64
64
 
65
- Before running `relay start` you should add a `~/.relay/tools/<yourtool>.rb`.
65
+ Before running `relay start` you should add `~/.config/relay/tools/<yourtool>.rb`.
66
66
  The tool will be automatically made available to the LLM. This is how a tool
67
67
  might look - it is not very useful because it does not emit command output
68
68
  but it serves as a simple example that you can modify and change to meet
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Relay::Concerns
2
4
  module Attachment
3
5
  def attachment
@@ -55,9 +55,9 @@ module Relay::Concerns
55
55
  # @return [Array<Relay::Models::MCP>]
56
56
  # Saved MCP servers for the current user, newest first.
57
57
  def mcps
58
- @mcps ||= user ? Relay::Models::MCP.summary_dataset(user.mcps_dataset).
59
- reverse_order(:created_at).
60
- all : []
58
+ @mcps ||= user ? Relay::Models::MCP.summary_dataset(user.mcps_dataset)
59
+ .reverse_order(:created_at)
60
+ .all : []
61
61
  end
62
62
 
63
63
  ##
@@ -94,13 +94,7 @@ module Relay::Concerns
94
94
  # @return [Hash<String, LLM::Provider>]
95
95
  # A map of initialized LLM providers.
96
96
  def llms
97
- @llms ||= {
98
- "openai" => LLM.openai(key: ENV["OPENAI_SECRET"]),
99
- "google" => LLM.google(key: ENV["GOOGLE_SECRET"]),
100
- "anthropic" => LLM.anthropic(key: ENV["ANTHROPIC_SECRET"]),
101
- "deepseek" => LLM.deepseek(key: ENV["DEEPSEEK_SECRET"]),
102
- "xai" => LLM.xai(key: ENV["XAI_SECRET"])
103
- }.transform_values(&:persist!)
97
+ Relay.providers
104
98
  end
105
99
 
106
100
  ##
@@ -114,7 +108,7 @@ module Relay::Concerns
114
108
  # @return [String]
115
109
  # Returns the default chat model for the current provider.
116
110
  def default_model
117
- case (provider = llms.fetch(self.provider)).name
111
+ case (provider = llms[self.provider]).name
118
112
  when :deepseek then "deepseek-v4-flash"
119
113
  when :openai then "gpt-5.4"
120
114
  when :xai then "grok-3"
@@ -142,6 +136,5 @@ module Relay::Concerns
142
136
  def user
143
137
  @user ||= Relay::Models::User[session["user_id"]] if session["user_id"]
144
138
  end
145
-
146
139
  end
147
140
  end
@@ -4,7 +4,7 @@ module Relay::Hooks
4
4
  module RequireUser
5
5
  def call(*args, **kwargs)
6
6
  @user = Relay::Models::User[session["user_id"]]
7
- @user.nil? ? r.redirect("/sign-in") : super(*args, **kwargs)
7
+ @user.nil? ? r.redirect("/sign-in") : super
8
8
  end
9
9
  end
10
10
  end
data/app/init/database.rb CHANGED
@@ -8,7 +8,7 @@ module Relay::Database
8
8
  # @param [String] env
9
9
  # @return [Hash]
10
10
  def load(env:)
11
- erb = ERB.new(File.read(File.join(__dir__, "..", "..", "db", "config.yml")))
11
+ erb = ERB.new(File.read(File.join(Relay.home, "db", "config.yml")))
12
12
  config = YAML.safe_load(erb.result, aliases: true)
13
13
  config.fetch(env)
14
14
  end
data/app/init/router.rb CHANGED
@@ -157,8 +157,6 @@ module Relay
157
157
  Routes::ClearAttachment.new(self).call
158
158
  end
159
159
  end
160
-
161
160
  end
162
-
163
161
  end
164
162
  end
data/app/init.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Relay
4
+ require_relative "../lib/relay"
5
+
4
6
  require "async"
5
7
  require "async/websocket"
6
8
  require "async/websocket/adapters/rack"
@@ -12,7 +14,6 @@ module Relay
12
14
  require "zeitwerk"
13
15
  require "sequel"
14
16
  require "yaml"
15
- require "relay"
16
17
 
17
18
  loader = Zeitwerk::Loader.new
18
19
  loader.inflector.inflect(
@@ -25,7 +26,6 @@ module Relay
25
26
  File.join(__dir__, "init")
26
27
  )
27
28
  loader.push_dir(__dir__, namespace: self)
28
-
29
29
  loader.enable_reloading if development?
30
30
  loader.setup
31
31
 
@@ -37,14 +37,24 @@ module Relay
37
37
  end
38
38
  @loader = loader
39
39
 
40
- FileUtils.mkdir_p Relay.home
41
- FileUtils.mkdir_p File.join(Relay.home, "db")
42
- FileUtils.mkdir_p Relay.images_dir
43
- FileUtils.mkdir_p Relay.logs_dir
40
+ Relay.bootstrap!
41
+ user_tools_dir = File.join(home, "tools")
42
+ user_loader = Zeitwerk::Loader.new
43
+ user_loader.tag = "relay-user-tools"
44
+ user_loader.push_dir(user_tools_dir)
45
+ user_loader.enable_reloading if development?
46
+ user_loader.setup
47
+
48
+ ##
49
+ # Returns the Zeitwerk loader used for user-installed tools
50
+ # @return [Zeitwerk::Loader]
51
+ def self.user_loader
52
+ @user_loader
53
+ end
54
+ @user_loader = user_loader
44
55
 
45
56
  require_relative "init/env"
46
57
  require_relative "init/database"
47
58
  require_relative "init/router"
48
-
49
59
  Relay.reload
50
60
  end
@@ -44,13 +44,13 @@ module Relay::Models
44
44
 
45
45
  ##
46
46
  # @note
47
- # This method excludes tool calls and system messages.
47
+ # This method excludes tool calls, tool returns, and system messages.
48
48
  # It is safe to render in the UI.
49
49
  # @return [Array<Hash>]
50
50
  # Returns persisted user and assistant messages
51
51
  def messages
52
52
  ctx.messages.filter_map do |message|
53
- next if message.tool_call? || message.compaction?
53
+ next if message.tool_call? || message.tool_return? || message.compaction?
54
54
  next unless message.user? || message.assistant?
55
55
  {role: message.role.to_sym, content: message.content.to_s}
56
56
  end
@@ -67,11 +67,20 @@ module Relay::Models
67
67
  private
68
68
 
69
69
  def set_provider
70
- LLM.method(provider).call(key: ENV["#{provider.upcase}_SECRET"], persistent: true)
70
+ case provider
71
+ when "bedrock"
72
+ LLM.bedrock(
73
+ access_key_id: ENV["AWS_ACCESS_KEY_ID"],
74
+ secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
75
+ persistent: true
76
+ )
77
+ else
78
+ LLM.method(provider).call(key: ENV["#{provider.upcase}_SECRET"], persistent: true)
79
+ end
71
80
  end
72
81
 
73
82
  def set_context
74
- { model: self[:model], compactor: { retention_window: 8, token_threshold: "95%" } }
83
+ {model: self[:model], compactor: {retention_window: 8, token_threshold: "95%"}}
75
84
  end
76
85
 
77
86
  def set_tracer
data/app/pages/base.rb CHANGED
@@ -20,6 +20,5 @@ module Relay::Pages
20
20
  def page(name, **locals)
21
21
  view(File.join("pages", name), locals:, layout_opts: {locals:})
22
22
  end
23
-
24
23
  end
25
24
  end
data/app/routes/base.rb CHANGED
@@ -31,6 +31,5 @@ module Relay::Routes
31
31
  def htmx?
32
32
  request.env["HTTP_HX_REQUEST"] == "true"
33
33
  end
34
-
35
34
  end
36
35
  end
@@ -8,7 +8,7 @@ module Relay::Routes
8
8
  Relay::Models::MCP.where(id:, user_id: user.id).first || raise(Sequel::NoMatchingRow)
9
9
  end
10
10
 
11
- def workspace(selected_id: nil, form:)
11
+ def workspace(form:, selected_id: nil)
12
12
  partial("fragments/mcp/workspace", locals: {mcps:, selected_id:, form:}) +
13
13
  partial("fragments/mcp_settings", locals: {servers: mcps, show_label: false, swap_oob: true})
14
14
  end
@@ -37,9 +37,9 @@ class Relay::Routes::Websocket
37
37
  # The mutable request params for the current turn
38
38
  # @return [void]
39
39
  def dispatch(conn, ctx, payload, params)
40
- case
41
- when interrupt?(payload) then interrupt!(conn, ctx)
42
- when request_in_flight?
40
+ if interrupt?(payload)
41
+ interrupt!(conn, ctx)
42
+ elsif request_in_flight?
43
43
  write(conn, fragment(:status, status_bar(status: "Busy", ctx:)))
44
44
  else
45
45
  @task = Async { on_message(conn, ctx, payload, params) }
@@ -19,7 +19,7 @@ module Relay::Tools
19
19
  title: entry["title"],
20
20
  track: entry["track"],
21
21
  html: Relay.erb("fragments/_iframe.erb", {entry:}),
22
- directions:,
22
+ directions:
23
23
  }
24
24
  end
25
25
  end
@@ -22,6 +22,9 @@
22
22
  <form
23
23
  id="chat-composer"
24
24
  class="flex min-w-0 flex-1 items-end gap-3"
25
+ data-context-id="<%= ctx.id %>"
26
+ data-provider="<%= provider %>"
27
+ data-model="<%= model %>"
25
28
  hx-on::after-settle="this.querySelector('textarea')?.focus()"
26
29
  ws-send
27
30
  >
@@ -12,8 +12,7 @@
12
12
  hx-swap="outerHTML"
13
13
  hx-trigger="change"
14
14
  >
15
- <% Relay.providers.each do |_, builder| %>
16
- <% llm = builder.call %>
15
+ <% Relay.providers.each do |_, llm| %>
17
16
  <option
18
17
  <%= "selected" if llm.name.to_s == provider.to_s %>
19
18
  value="<%= llm.name %>">
data/bin/relay CHANGED
@@ -13,27 +13,19 @@ end
13
13
 
14
14
  def main(argv)
15
15
  case argv[0]
16
- when "setup"
17
- Process.spawn File.join(libexec, "setup"), *argv[1..]
18
- Process.wait
19
- when "download-models"
20
- Process.spawn File.join(libexec, "download-models"), *argv[1..]
21
- Process.wait
22
- when "migrate"
23
- Process.spawn File.join(libexec, "migrate"), *argv[1..]
24
- Process.wait
25
- when "start"
26
- Process.spawn File.join(libexec, "start"), *argv[1..]
16
+ when "start", "setup", "download-models", "migrate", "bootstrap", "console"
17
+ Process.spawn File.join(libexec, argv[0]), *argv[1..]
27
18
  Process.wait
28
19
  else
29
- require "relay"
20
+ require_relative "../lib/relay"
30
21
  warn Relay.banner
31
22
  warn "Usage: relay [COMMAND] [OPTIONS]\n\n" \
32
- "Commands:\n" \
33
- " setup Setup Relay for the first time\n" \
34
- " download-models Download provider model catalogs\n" \
35
- " start Start Relay\n" \
36
- " migrate Run database migrations\n"
23
+ "Commands:\n" \
24
+ " setup Setup Relay for the first time\n" \
25
+ " download-models Download provider model catalogs\n" \
26
+ " start Start Relay\n" \
27
+ " migrate Run database migrations\n" \
28
+ " console Start an IRB console with Relay booted\n"
37
29
  end
38
30
  rescue Interrupt
39
31
  wait
data/config.ru CHANGED
@@ -4,7 +4,7 @@ ENV["RACK_MULTIPART_BUFFERED_UPLOAD_BYTESIZE_LIMIT"] ||= (64 * 1024 * 1024).to_s
4
4
 
5
5
  require_relative "app/init"
6
6
 
7
- use Rack::Static, urls: ["/g"], root: Relay.home
7
+ use Rack::Static, urls: ["/g"], root: Relay.public_dir
8
8
  use Rack::Static, urls: ["/images", "/stylesheets", "/js"], root: Relay.public_dir
9
9
  case Relay.environment
10
10
  when "development"
data/lib/relay/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Relay
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/relay.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Relay
4
+ require "fileutils"
5
+ gem "llm.rb", "= 8.1.0"
6
+
4
7
  require_relative "relay/version"
5
8
  require_relative "relay/cache"
6
9
  require_relative "relay/attachment"
@@ -18,7 +21,8 @@ module Relay
18
21
  "deepseek" => -> { LLM.deepseek(key: ENV["DEEPSEEK_SECRET"]) },
19
22
  "google" => -> { LLM.google(key: ENV["GOOGLE_SECRET"]) },
20
23
  "openai" => -> { LLM.openai(key: ENV["OPENAI_SECRET"]) },
21
- "xai" => -> { LLM.xai(key: ENV["XAI_SECRET"]) }
24
+ "xai" => -> { LLM.xai(key: ENV["XAI_SECRET"]) },
25
+ "bedrock" => -> { LLM.bedrock(access_key_id: ENV["AWS_ACCESS_KEY_ID"], secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"]) }
22
26
  }.freeze
23
27
  private_constant :PROVIDERS
24
28
 
@@ -37,7 +41,7 @@ module Relay
37
41
  # Returns all known providers
38
42
  # @return [LLM::Object]
39
43
  def self.providers
40
- @providers ||= LLM::Object.from(PROVIDERS)
44
+ @providers ||= LLM::Object.from(PROVIDERS).transform_values!(&:call)
41
45
  end
42
46
 
43
47
  ##
@@ -81,7 +85,7 @@ module Relay
81
85
  # Returns the writable Relay home directory
82
86
  # @return [String]
83
87
  def self.home
84
- @home ||= ENV["RELAY_HOME"] || File.join(Dir.home, ".relay")
88
+ @home ||= ENV["RELAY_HOME"] || File.join(Dir.home, ".config", "relay")
85
89
  end
86
90
 
87
91
  ##
@@ -91,6 +95,21 @@ module Relay
91
95
  @env_path ||= File.join(home, "env")
92
96
  end
93
97
 
98
+ ##
99
+ # Creates the Relay home layout and copies bundled defaults into it.
100
+ # @return [String]
101
+ def self.bootstrap!
102
+ FileUtils.mkdir_p home
103
+ FileUtils.mkdir_p File.join(home, "db")
104
+ FileUtils.mkdir_p File.join(home, "tools")
105
+ FileUtils.mkdir_p images_dir
106
+ FileUtils.mkdir_p logs_dir
107
+ source = File.join(root, "db", "config.yml")
108
+ destination = File.join(home, "db", "config.yml")
109
+ FileUtils.cp(source, destination) if File.exist?(source) && !File.exist?(destination)
110
+ home
111
+ end
112
+
94
113
  ##
95
114
  # @return [Array<String>]
96
115
  # Returns the tools directory
@@ -109,7 +128,7 @@ module Relay
109
128
  # Returns the path to generated images
110
129
  # @return [String]
111
130
  def self.images_dir
112
- @images_dir ||= File.join(home, "g")
131
+ @images_dir ||= File.join(public_dir, "g")
113
132
  end
114
133
 
115
134
  ##
@@ -170,14 +189,8 @@ module Relay
170
189
  def self.reload
171
190
  LLM::Tool.clear_registry!
172
191
  Relay.loader.reload if development?
173
- paths = Dir[File.join(tools_dir, "*.rb")]
174
- paths.concat Dir[File.join(home, "tools", "*.rb")]
175
- paths.sort.each do
176
- load(_1)
177
- rescue => ex
178
- warn "tool error\n"
179
- "#{ex.class}: #{ex.message}\n"
180
- "#{ex.backtrace.join("\n")}"
181
- end
192
+ Relay.user_loader.reload if development?
193
+ Relay.user_loader.eager_load
194
+ Relay.loader.eager_load_dir(tools_dir)
182
195
  end
183
196
  end
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "relay"
4
+ require_relative "../../lib/relay"
5
+ Relay.bootstrap!
5
6
  require File.join(Relay.root, "app", "init")
6
7
  warn "relay: bootstrap the database"
7
8
  Sequel.extension :migration
@@ -1,17 +1,44 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "relay"
4
+ require_relative "../../lib/relay"
5
+ require "llm"
5
6
  require "fileutils"
6
7
  require "io/console"
7
8
 
8
- PROVIDER_KEYS = {
9
- "OPENAI_SECRET" => "OpenAI API key",
10
- "GOOGLE_SECRET" => "Google API key",
11
- "ANTHROPIC_SECRET" => "Anthropic API key",
12
- "DEEPSEEK_SECRET" => "DeepSeek API key",
13
- "XAI_SECRET" => "xAI API key"
14
- }.freeze
9
+ PROVIDERS = [
10
+ LLM::Object.from(
11
+ fields: [
12
+ LLM::Object.from(label: "OpenAI API key", key: "OPENAI_SECRET", aliases: ["OPENAI_API_KEY"], secret: true)
13
+ ]
14
+ ),
15
+ LLM::Object.from(
16
+ fields: [
17
+ LLM::Object.from(label: "Google API key", key: "GOOGLE_SECRET", aliases: ["GOOGLE_API_KEY"], secret: true)
18
+ ]
19
+ ),
20
+ LLM::Object.from(
21
+ fields: [
22
+ LLM::Object.from(label: "Anthropic API key", key: "ANTHROPIC_SECRET", aliases: ["ANTHROPIC_API_KEY"], secret: true)
23
+ ]
24
+ ),
25
+ LLM::Object.from(
26
+ fields: [
27
+ LLM::Object.from(label: "DeepSeek API key", key: "DEEPSEEK_SECRET", aliases: ["DEEPSEEK_API_KEY"], secret: true)
28
+ ]
29
+ ),
30
+ LLM::Object.from(
31
+ fields: [
32
+ LLM::Object.from(label: "xAI API key", key: "XAI_SECRET", aliases: ["XAI_API_KEY"], secret: true)
33
+ ]
34
+ ),
35
+ LLM::Object.from(
36
+ fields: [
37
+ LLM::Object.from(label: "AWS access key ID", key: "AWS_ACCESS_KEY_ID", aliases: [], secret: false),
38
+ LLM::Object.from(label: "AWS secret access key", key: "AWS_SECRET_ACCESS_KEY", aliases: [], secret: true)
39
+ ]
40
+ )
41
+ ].freeze
15
42
 
16
43
  ##
17
44
  # utils
@@ -36,15 +63,24 @@ end
36
63
  def prompt(label, current: nil, secret: false)
37
64
  suffix = current.to_s.empty? ? "" : " [configured]"
38
65
  print "#{label}#{suffix}: "
39
- value = secret ? STDIN.noecho(&:gets).to_s.chomp : STDIN.gets.to_s.chomp
66
+ value = secret ? $stdin.noecho(&:gets).to_s.chomp : $stdin.gets.to_s.chomp
40
67
  puts if secret
41
68
  value.empty? ? current.to_s : value
42
69
  end
43
70
 
71
+ def prompt_provider(env, provider)
72
+ provider.fields.each do |field|
73
+ key = field.key
74
+ aliases = Array(field.aliases)
75
+ current = env[key] || aliases.filter_map { ENV[_1] }.first || ENV[key]
76
+ env[key] = prompt(field.label, current:, secret: field.secret)
77
+ end
78
+ end
79
+
44
80
  def prompt_required(label)
45
81
  loop do
46
82
  print "#{label}: "
47
- value = STDIN.gets.to_s.strip
83
+ value = $stdin.gets.to_s.strip
48
84
  return value unless value.empty?
49
85
  warn "#{label} is required."
50
86
  end
@@ -53,10 +89,10 @@ end
53
89
  def prompt_password
54
90
  loop do
55
91
  print "Password: "
56
- password = STDIN.noecho(&:gets).to_s.chomp
92
+ password = $stdin.noecho(&:gets).to_s.chomp
57
93
  puts
58
94
  print "Confirm password: "
59
- confirm = STDIN.noecho(&:gets).to_s.chomp
95
+ confirm = $stdin.noecho(&:gets).to_s.chomp
60
96
  puts
61
97
  return password unless password.empty? || password != confirm
62
98
  warn "Passwords must match and cannot be empty."
@@ -66,15 +102,15 @@ end
66
102
  ##
67
103
  # main
68
104
  def main
69
- unless STDIN.tty? && $stdout.tty?
105
+ unless $stdin.tty? && $stdout.tty?
70
106
  warn "relay configure requires an interactive terminal"
71
107
  throw(:exit, 1)
72
108
  end
73
109
  env = load_env
74
110
  puts Relay.banner
75
111
  puts "Relay configuration\n"
76
- PROVIDER_KEYS.each do |key, label|
77
- env[key] = prompt(label, current: env[key], secret: true)
112
+ PROVIDERS.each do |provider|
113
+ prompt_provider(env, provider)
78
114
  end
79
115
  name = prompt_required("Name")
80
116
  email = prompt_required("Email")
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../../lib/relay"
5
+ require File.join(Relay.root, "app", "init")
6
+ require "irb"
7
+
8
+ IRB.start(__FILE__)
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "relay"
4
+ require_relative "../../lib/relay"
5
5
  require File.join(Relay.root, "app", "init")
6
6
 
7
7
  warn "relay: download models"
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "relay"
4
+ require_relative "../../lib/relay"
5
+ Relay.bootstrap!
5
6
  require File.join(Relay.root, "app", "init")
6
7
  Sequel.extension :migration
7
8
  Sequel::Migrator.run(Relay::DB, Relay.migrations_dir)
data/libexec/relay/setup CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "relay"
4
+ require_relative "../../lib/relay"
5
5
 
6
6
  bootstrap = File.join(Relay.root, "libexec", "relay", "bootstrap")
7
7
  configure = File.join(Relay.root, "libexec", "relay", "configure")
data/libexec/relay/start CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "relay"
4
+ require_relative "../../lib/relay"
5
5
  require "optparse"
6
+ require "rbconfig"
6
7
 
7
8
  DEFAULT_BIND = "http://0.0.0.0:9292"
8
9
 
@@ -22,7 +23,7 @@ def main(argv)
22
23
  option_parser(bind).parse!(argv)
23
24
  bind = argv.shift || bind
24
25
  Dir.chdir(Relay.root) do
25
- exec "falcon", "serve", "--bind", bind
26
+ exec RbConfig.ruby, "-S", "falcon", "serve", "--bind", bind
26
27
  end
27
28
  end
28
29