terminalwire 0.2.5 → 0.3.0.alpha2
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 +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
|