terminalwire 0.2.5 → 0.3.0.alpha2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +67 -5
- data/exe/terminalwire +1 -1
- data/exe/terminalwire-exec +1 -1
- metadata +9 -171
- data/.rspec +0 -3
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -132
- data/LICENSE.txt +0 -9
- data/README.md +0 -45
- data/examples/exec/localrails +0 -2
- data/lib/generators/terminalwire/install/USAGE +0 -9
- data/lib/generators/terminalwire/install/install_generator.rb +0 -38
- data/lib/generators/terminalwire/install/templates/application_terminal.rb.tt +0 -21
- data/lib/generators/terminalwire/install/templates/bin/terminalwire +0 -2
- data/lib/generators/terminalwire/install/templates/main_terminal.rb +0 -40
- data/lib/terminalwire/adapter.rb +0 -53
- data/lib/terminalwire/cache.rb +0 -114
- data/lib/terminalwire/client/entitlement/environment_variables.rb +0 -26
- data/lib/terminalwire/client/entitlement/paths.rb +0 -97
- data/lib/terminalwire/client/entitlement/policy.rb +0 -117
- data/lib/terminalwire/client/entitlement/schemes.rb +0 -26
- data/lib/terminalwire/client/entitlement.rb +0 -9
- data/lib/terminalwire/client/exec.rb +0 -44
- data/lib/terminalwire/client/handler.rb +0 -67
- data/lib/terminalwire/client/resource.rb +0 -204
- data/lib/terminalwire/client/server_license_verification.rb +0 -74
- data/lib/terminalwire/client.rb +0 -30
- data/lib/terminalwire/logging.rb +0 -8
- data/lib/terminalwire/rails.rb +0 -69
- data/lib/terminalwire/server/context.rb +0 -80
- data/lib/terminalwire/server/resource.rb +0 -120
- data/lib/terminalwire/server.rb +0 -63
- data/lib/terminalwire/thor.rb +0 -67
- data/lib/terminalwire/transport.rb +0 -58
- data/lib/terminalwire/version.rb +0 -5
- data/lib/terminalwire.rb +0 -56
- data/sig/terminalwire.rbs +0 -4
@@ -1,204 +0,0 @@
|
|
1
|
-
require "fileutils"
|
2
|
-
require "io/console"
|
3
|
-
|
4
|
-
module Terminalwire::Client::Resource
|
5
|
-
# Dispatches messages from the Client::Handler to the appropriate resource.
|
6
|
-
class Handler
|
7
|
-
include Enumerable
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@resources = {}
|
11
|
-
yield self if block_given?
|
12
|
-
end
|
13
|
-
|
14
|
-
def each(&block)
|
15
|
-
@resources.values.each(&block)
|
16
|
-
end
|
17
|
-
|
18
|
-
def add(resource)
|
19
|
-
# Detect if the resource is already registered and throw an error
|
20
|
-
if @resources.key?(resource.name)
|
21
|
-
raise "Resource #{resource.name} already registered"
|
22
|
-
else
|
23
|
-
@resources[resource.name] = resource
|
24
|
-
end
|
25
|
-
end
|
26
|
-
alias :<< :add
|
27
|
-
|
28
|
-
def dispatch(**message)
|
29
|
-
case message
|
30
|
-
in { event:, action:, name:, command:, parameters: }
|
31
|
-
resource = @resources.fetch(name)
|
32
|
-
resource.command(command, **parameters)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Dispatcher, security, and response macros for resources.
|
38
|
-
class Base < Terminalwire::Resource::Base
|
39
|
-
def initialize(*, entitlement:, **)
|
40
|
-
super(*, **)
|
41
|
-
@entitlement = entitlement
|
42
|
-
connect
|
43
|
-
end
|
44
|
-
|
45
|
-
def command(command, **parameters)
|
46
|
-
begin
|
47
|
-
if permit(command, **parameters)
|
48
|
-
succeed self.public_send(command, **parameters)
|
49
|
-
else
|
50
|
-
fail "Client denied #{command}", command:, parameters:
|
51
|
-
end
|
52
|
-
rescue => e
|
53
|
-
fail e.message, command:, parameters:
|
54
|
-
raise
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
protected
|
59
|
-
|
60
|
-
def permit(...)
|
61
|
-
false
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class EnvironmentVariable < Base
|
66
|
-
# Accepts a list of environment variables to permit.
|
67
|
-
def read(name:)
|
68
|
-
ENV[name]
|
69
|
-
end
|
70
|
-
|
71
|
-
# def write(name:, value:)
|
72
|
-
# ENV[name] = value
|
73
|
-
# end
|
74
|
-
|
75
|
-
protected
|
76
|
-
|
77
|
-
def permit(command, name:, **)
|
78
|
-
@entitlement.environment_variables.permitted? name
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
class STDOUT < Base
|
83
|
-
def connect
|
84
|
-
@io = $stdout
|
85
|
-
end
|
86
|
-
|
87
|
-
def print(data:)
|
88
|
-
@io.print(data)
|
89
|
-
end
|
90
|
-
|
91
|
-
def print_line(data:)
|
92
|
-
@io.puts(data)
|
93
|
-
end
|
94
|
-
|
95
|
-
protected
|
96
|
-
|
97
|
-
def permit(...)
|
98
|
-
true
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
class STDERR < STDOUT
|
103
|
-
def connect
|
104
|
-
@io = $stderr
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
class STDIN < Base
|
109
|
-
def connect
|
110
|
-
@io = $stdin
|
111
|
-
end
|
112
|
-
|
113
|
-
def read_line
|
114
|
-
@io.gets
|
115
|
-
end
|
116
|
-
|
117
|
-
def read_password
|
118
|
-
@io.getpass
|
119
|
-
end
|
120
|
-
|
121
|
-
protected
|
122
|
-
|
123
|
-
def permit(...)
|
124
|
-
true
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class File < Base
|
129
|
-
File = ::File
|
130
|
-
|
131
|
-
def read(path:)
|
132
|
-
File.read File.expand_path(path)
|
133
|
-
end
|
134
|
-
|
135
|
-
def write(path:, content:, mode: nil)
|
136
|
-
File.open(File.expand_path(path), "w", mode) { |f| f.write(content) }
|
137
|
-
end
|
138
|
-
|
139
|
-
def append(path:, content:, mode: nil)
|
140
|
-
File.open(File.expand_path(path), "a", mode) { |f| f.write(content) }
|
141
|
-
end
|
142
|
-
|
143
|
-
def delete(path:)
|
144
|
-
File.delete File.expand_path(path)
|
145
|
-
end
|
146
|
-
|
147
|
-
def exist(path:)
|
148
|
-
File.exist? File.expand_path(path)
|
149
|
-
end
|
150
|
-
|
151
|
-
def change_mode(path:, mode:)
|
152
|
-
File.chmod mode, File.expand_path(path)
|
153
|
-
end
|
154
|
-
|
155
|
-
protected
|
156
|
-
|
157
|
-
def permit(command, path:, mode: nil, **)
|
158
|
-
@entitlement.paths.permitted? path, mode:
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
class Directory < Base
|
163
|
-
File = ::File
|
164
|
-
|
165
|
-
def list(path:)
|
166
|
-
Dir.glob path
|
167
|
-
end
|
168
|
-
|
169
|
-
def create(path:)
|
170
|
-
FileUtils.mkdir_p File.expand_path(path)
|
171
|
-
rescue Errno::EEXIST
|
172
|
-
# Do nothing
|
173
|
-
end
|
174
|
-
|
175
|
-
def exist(path:)
|
176
|
-
Dir.exist? path
|
177
|
-
end
|
178
|
-
|
179
|
-
def delete(path:)
|
180
|
-
Dir.delete path
|
181
|
-
end
|
182
|
-
|
183
|
-
protected
|
184
|
-
|
185
|
-
def permit(command, path:, **)
|
186
|
-
@entitlement.paths.permitted? path
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
class Browser < Base
|
191
|
-
def launch(url:)
|
192
|
-
Launchy.open(URI(url))
|
193
|
-
# TODO: This is a hack to get the `respond` method to work.
|
194
|
-
# Maybe explicitly call a `suceed` and `fail` method?
|
195
|
-
nil
|
196
|
-
end
|
197
|
-
|
198
|
-
protected
|
199
|
-
|
200
|
-
def permit(command, url:, **)
|
201
|
-
@entitlement.schemes.permitted? url
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
require "async/http/internet"
|
2
|
-
require "base64"
|
3
|
-
require "uri"
|
4
|
-
require "fileutils"
|
5
|
-
|
6
|
-
module Terminalwire::Client
|
7
|
-
# Checkes the server for a license verification at `https://terminalwire.com/licenses/verifications/`
|
8
|
-
# and displays the message to the user, if necessary.
|
9
|
-
class ServerLicenseVerification
|
10
|
-
include Terminalwire::Logging
|
11
|
-
|
12
|
-
def initialize(url:)
|
13
|
-
@url = URI(url)
|
14
|
-
@internet = Async::HTTP::Internet.new
|
15
|
-
@cache_store = Terminalwire::Cache::File::Store.new(path: Terminalwire::Client.root_path.join("cache/licenses/verifications"))
|
16
|
-
end
|
17
|
-
|
18
|
-
def key
|
19
|
-
Base64.urlsafe_encode64 @url
|
20
|
-
end
|
21
|
-
|
22
|
-
def cache = @cache_store.entry key
|
23
|
-
|
24
|
-
def payload
|
25
|
-
if cache.miss?
|
26
|
-
logger.debug "Stale verification. Requesting new verification."
|
27
|
-
request do |response|
|
28
|
-
# Set the expiry on the file cache for the header.
|
29
|
-
if max_age = response.headers["cache-control"].max_age
|
30
|
-
logger.debug "Caching for #{max_age}"
|
31
|
-
cache.expires = Time.now + max_age
|
32
|
-
end
|
33
|
-
|
34
|
-
# Process based on the response code.
|
35
|
-
case response.status
|
36
|
-
in 200
|
37
|
-
logger.debug "License for #{@url} found."
|
38
|
-
data = self.class.unpack response.read
|
39
|
-
cache.value = data
|
40
|
-
return data
|
41
|
-
in 404
|
42
|
-
logger.debug "License for #{@url} not found."
|
43
|
-
return self.class.unpack response.read
|
44
|
-
end
|
45
|
-
end
|
46
|
-
else
|
47
|
-
return cache.value
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def message
|
52
|
-
payload.dig(:shell, :output)
|
53
|
-
end
|
54
|
-
|
55
|
-
protected
|
56
|
-
|
57
|
-
def verification_url
|
58
|
-
Terminalwire.url
|
59
|
-
.path("/licenses/verifications", key)
|
60
|
-
end
|
61
|
-
|
62
|
-
def request(&)
|
63
|
-
logger.debug "Requesting license verification from #{verification_url}."
|
64
|
-
response = @internet.get verification_url, {
|
65
|
-
"Accept" => "application/x-msgpack",
|
66
|
-
"User-Agent" => "Terminalwire/#{Terminalwire::VERSION} Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM})",
|
67
|
-
}, &
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.unpack(pack)
|
71
|
-
MessagePack.unpack(pack, symbolize_keys: true)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/terminalwire/client.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'launchy'
|
3
|
-
require 'io/console'
|
4
|
-
require 'pathname'
|
5
|
-
|
6
|
-
module Terminalwire
|
7
|
-
module Client
|
8
|
-
ROOT_PATH = "~/.terminalwire".freeze
|
9
|
-
def self.root_path = Pathname.new(ENV.fetch("TERMINALWIRE_HOME", ROOT_PATH))
|
10
|
-
|
11
|
-
def self.websocket(url:, arguments: ARGV, &configuration)
|
12
|
-
ENV["TERMINALWIRE_HOME"] ||= root_path.to_s
|
13
|
-
|
14
|
-
url = URI(url)
|
15
|
-
|
16
|
-
Async do |task|
|
17
|
-
endpoint = Async::HTTP::Endpoint.parse(
|
18
|
-
url,
|
19
|
-
alpn_protocols: Async::HTTP::Protocol::HTTP11.names
|
20
|
-
)
|
21
|
-
|
22
|
-
Async::WebSocket::Client.connect(endpoint) do |adapter|
|
23
|
-
transport = Terminalwire::Transport::WebSocket.new(adapter)
|
24
|
-
adapter = Terminalwire::Adapter::Socket.new(transport)
|
25
|
-
Terminalwire::Client::Handler.new(adapter, arguments:, endpoint:, &configuration).connect
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/terminalwire/logging.rb
DELETED
data/lib/terminalwire/rails.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
require 'jwt'
|
2
|
-
require 'pathname'
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
module Terminalwire::Rails
|
6
|
-
class Session
|
7
|
-
# JWT file name for the session file.
|
8
|
-
FILENAME = "session.jwt"
|
9
|
-
|
10
|
-
# Empty dictionary the user can stash all their session data into.
|
11
|
-
EMPTY_SESSION = {}.freeze
|
12
|
-
|
13
|
-
extend Forwardable
|
14
|
-
|
15
|
-
# Delegate `dig` and `fetch` to the `read` method
|
16
|
-
def_delegators :read,
|
17
|
-
:dig, :fetch, :[]
|
18
|
-
|
19
|
-
def initialize(context:, path: nil, secret_key: self.class.secret_key)
|
20
|
-
@context = context
|
21
|
-
@path = Pathname.new(path || context.storage_path)
|
22
|
-
@config_file_path = @path.join(FILENAME)
|
23
|
-
@secret_key = secret_key
|
24
|
-
|
25
|
-
ensure_file
|
26
|
-
end
|
27
|
-
|
28
|
-
def read
|
29
|
-
jwt_token = @context.file.read(@config_file_path)
|
30
|
-
decoded_data = JWT.decode(jwt_token, @secret_key, true, algorithm: 'HS256')
|
31
|
-
decoded_data[0] # JWT payload is the first element in the array
|
32
|
-
rescue JWT::DecodeError => e
|
33
|
-
raise "Invalid or tampered file: #{e.message}"
|
34
|
-
end
|
35
|
-
|
36
|
-
def reset
|
37
|
-
@context.file.delete @config_file_path
|
38
|
-
end
|
39
|
-
|
40
|
-
def edit
|
41
|
-
config = read
|
42
|
-
yield config
|
43
|
-
write(config)
|
44
|
-
end
|
45
|
-
|
46
|
-
def []=(key, value)
|
47
|
-
edit { |config| config[key] = value }
|
48
|
-
end
|
49
|
-
|
50
|
-
def write(config)
|
51
|
-
token = JWT.encode(config, @secret_key, 'HS256')
|
52
|
-
@context.file.write(@config_file_path, token)
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def ensure_file
|
58
|
-
return true if @context.file.exist? @config_file_path
|
59
|
-
# Create the path if it doesn't exist on the client.
|
60
|
-
@context.directory.create @path
|
61
|
-
# Write an empty configuration on initialization
|
62
|
-
write(EMPTY_SESSION)
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.secret_key
|
66
|
-
Rails.application.secret_key_base
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require "fileutils"
|
2
|
-
|
3
|
-
module Terminalwire::Server
|
4
|
-
# Contains all of the resources that are accessible to the server on the client-side.
|
5
|
-
# It's the primary interface for the server to interact with the client and is integrated
|
6
|
-
# into other libraries like Thor, etc.
|
7
|
-
class Context
|
8
|
-
extend Forwardable
|
9
|
-
|
10
|
-
attr_reader \
|
11
|
-
:stdout, :stdin, :stderr,
|
12
|
-
:browser,
|
13
|
-
:file, :directory,
|
14
|
-
:environment_variable,
|
15
|
-
:authority,
|
16
|
-
:root_path,
|
17
|
-
:authority_path,
|
18
|
-
:storage_path
|
19
|
-
|
20
|
-
def_delegators :@stdout, :puts, :print
|
21
|
-
def_delegators :@stdin, :gets, :getpass
|
22
|
-
|
23
|
-
def initialize(adapter:, entitlement:)
|
24
|
-
@adapter = adapter
|
25
|
-
@entitlement = entitlement
|
26
|
-
|
27
|
-
# Initialize resources
|
28
|
-
@stdout = Resource::STDOUT.new("stdout", @adapter)
|
29
|
-
@stdin = Resource::STDIN.new("stdin", @adapter)
|
30
|
-
@stderr = Resource::STDERR.new("stderr", @adapter)
|
31
|
-
@browser = Resource::Browser.new("browser", @adapter)
|
32
|
-
@file = Resource::File.new("file", @adapter)
|
33
|
-
@directory = Resource::Directory.new("directory", @adapter)
|
34
|
-
@environment_variable = Resource::EnvironmentVariable.new("environment_variable", @adapter)
|
35
|
-
|
36
|
-
# Authority is provided by the client.
|
37
|
-
@authority = @entitlement.fetch(:authority)
|
38
|
-
# The Terminalwire home path is provided by the client and set
|
39
|
-
# as an environment variable.
|
40
|
-
@root_path = Pathname.new(
|
41
|
-
@environment_variable.read("TERMINALWIRE_HOME")
|
42
|
-
)
|
43
|
-
# Now derive the rest of the paths from the Terminalwire home path.
|
44
|
-
@authority_path = @root_path.join("authorities", @authority)
|
45
|
-
@storage_path = @authority_path.join("storage")
|
46
|
-
|
47
|
-
if block_given?
|
48
|
-
begin
|
49
|
-
yield self
|
50
|
-
ensure
|
51
|
-
exit
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Wraps the environment variables in a hash-like object that can be accessed
|
57
|
-
# from client#ENV. This makes it look and feel just like the ENV object in Ruby.
|
58
|
-
class Env
|
59
|
-
def initialize(context:)
|
60
|
-
@context = context
|
61
|
-
end
|
62
|
-
|
63
|
-
def [](name)
|
64
|
-
@context.environment_variable.read(name)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def ENV
|
69
|
-
@ENV ||= Env.new(context: self)
|
70
|
-
end
|
71
|
-
|
72
|
-
def exit(status = 0)
|
73
|
-
@adapter.write(event: "exit", status: status)
|
74
|
-
end
|
75
|
-
|
76
|
-
def close
|
77
|
-
@adapter.close
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,120 +0,0 @@
|
|
1
|
-
module Terminalwire::Server
|
2
|
-
# Representation of the resources avilable to the server on the client-side. These
|
3
|
-
# classes encapsulate the API alls to the client and provide a more Ruby-like interface.
|
4
|
-
module Resource
|
5
|
-
class Base < Terminalwire::Resource::Base
|
6
|
-
private
|
7
|
-
|
8
|
-
def command(command, **parameters)
|
9
|
-
@adapter.write(
|
10
|
-
event: "resource",
|
11
|
-
name: @name,
|
12
|
-
action: "command",
|
13
|
-
command: command,
|
14
|
-
parameters: parameters
|
15
|
-
)
|
16
|
-
|
17
|
-
response = @adapter.read
|
18
|
-
case response.fetch(:status)
|
19
|
-
when "success"
|
20
|
-
response.fetch(:response)
|
21
|
-
when "failure"
|
22
|
-
raise Terminalwire::Error, response.inspect
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class EnvironmentVariable < Base
|
28
|
-
# Accepts a list of environment variables to permit.
|
29
|
-
def read(name)
|
30
|
-
command("read", name:)
|
31
|
-
end
|
32
|
-
|
33
|
-
# def write(name:, value:)
|
34
|
-
# command("write", name:, value:)
|
35
|
-
# end
|
36
|
-
end
|
37
|
-
|
38
|
-
class STDOUT < Base
|
39
|
-
def puts(data)
|
40
|
-
command("print_line", data: data)
|
41
|
-
end
|
42
|
-
|
43
|
-
def print(data)
|
44
|
-
command("print", data: data)
|
45
|
-
end
|
46
|
-
|
47
|
-
def flush
|
48
|
-
# Do nothing
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class STDERR < STDOUT
|
53
|
-
end
|
54
|
-
|
55
|
-
class STDIN < Base
|
56
|
-
def getpass
|
57
|
-
command("read_password")
|
58
|
-
end
|
59
|
-
|
60
|
-
def gets
|
61
|
-
command("read_line")
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class File < Base
|
66
|
-
def read(path)
|
67
|
-
command("read", path: path.to_s)
|
68
|
-
end
|
69
|
-
|
70
|
-
def write(path, content)
|
71
|
-
command("write", path: path.to_s, content:)
|
72
|
-
end
|
73
|
-
|
74
|
-
def append(path, content)
|
75
|
-
command("append", path: path.to_s, content:)
|
76
|
-
end
|
77
|
-
|
78
|
-
def delete(path)
|
79
|
-
command("delete", path: path.to_s)
|
80
|
-
end
|
81
|
-
alias :rm :delete
|
82
|
-
|
83
|
-
def exist?(path)
|
84
|
-
command("exist", path: path.to_s)
|
85
|
-
end
|
86
|
-
|
87
|
-
def change_mode(path, mode)
|
88
|
-
command("change_mode", path: path.to_s, mode:)
|
89
|
-
end
|
90
|
-
alias :chmod :change_mode
|
91
|
-
end
|
92
|
-
|
93
|
-
class Directory < Base
|
94
|
-
def list(path)
|
95
|
-
command("list", path: path.to_s)
|
96
|
-
end
|
97
|
-
alias :ls :list
|
98
|
-
|
99
|
-
def create(path)
|
100
|
-
command("create", path: path.to_s)
|
101
|
-
end
|
102
|
-
alias :mkdir :create
|
103
|
-
|
104
|
-
def exist?(path)
|
105
|
-
command("exist", path: path.to_s)
|
106
|
-
end
|
107
|
-
|
108
|
-
def delete(path)
|
109
|
-
command("delete", path: path.to_s)
|
110
|
-
end
|
111
|
-
alias :rm :delete
|
112
|
-
end
|
113
|
-
|
114
|
-
class Browser < Base
|
115
|
-
def launch(url)
|
116
|
-
command("launch", url: url)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
data/lib/terminalwire/server.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require "thor"
|
2
|
-
|
3
|
-
module Terminalwire
|
4
|
-
module Server
|
5
|
-
class WebSocket
|
6
|
-
include Logging
|
7
|
-
|
8
|
-
def call(env)
|
9
|
-
Async::WebSocket::Adapters::Rack.open(env, protocols: ['ws']) do |connection|
|
10
|
-
handle(Adapter::Socket.new(Terminalwire::Transport::WebSocket.new(connection)))
|
11
|
-
end or [200, { "Content-Type" => "text/plain" }, ["Connect via WebSockets"]]
|
12
|
-
end
|
13
|
-
|
14
|
-
def handle(adapter)
|
15
|
-
while message = adapter.read
|
16
|
-
puts message
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class Thor < WebSocket
|
22
|
-
Rails = ::Rails
|
23
|
-
|
24
|
-
include Logging
|
25
|
-
|
26
|
-
def initialize(cli_class)
|
27
|
-
@cli_class = cli_class
|
28
|
-
|
29
|
-
unless @cli_class.included_modules.include?(Terminalwire::Thor)
|
30
|
-
raise 'Add `include Terminalwire::Thor` to the #{@cli_class.inspect} class.'
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def error_message
|
35
|
-
"An error occurred. Please try again."
|
36
|
-
end
|
37
|
-
|
38
|
-
def handle(adapter)
|
39
|
-
logger.info "ThorServer: Running #{@cli_class.inspect}"
|
40
|
-
while message = adapter.read
|
41
|
-
case message
|
42
|
-
in { event: "initialization", protocol:, program: { arguments: }, entitlement: }
|
43
|
-
context = Terminalwire::Server::Context.new(adapter:, entitlement:)
|
44
|
-
|
45
|
-
begin
|
46
|
-
@cli_class.start(arguments, context:)
|
47
|
-
context.exit
|
48
|
-
rescue StandardError => e
|
49
|
-
if Rails.application.config.consider_all_requests_local
|
50
|
-
# Show the full error message with stack trace in development
|
51
|
-
context.stderr.puts "#{e.inspect}\n#{e.backtrace.join("\n")}"
|
52
|
-
else
|
53
|
-
# Show a generic message in production
|
54
|
-
context.stderr.puts error_message
|
55
|
-
end
|
56
|
-
context.exit 1
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|