terminalwire 0.2.4 → 0.3.0.alpha1

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/exe/terminalwire +1 -1
  3. data/exe/terminalwire-exec +1 -1
  4. metadata +9 -169
  5. data/.rspec +0 -3
  6. data/CHANGELOG.md +0 -5
  7. data/CODE_OF_CONDUCT.md +0 -132
  8. data/LICENSE.txt +0 -9
  9. data/README.md +0 -45
  10. data/Rakefile +0 -8
  11. data/examples/exec/localrails +0 -2
  12. data/lib/generators/terminalwire/install/USAGE +0 -9
  13. data/lib/generators/terminalwire/install/install_generator.rb +0 -38
  14. data/lib/generators/terminalwire/install/templates/application_terminal.rb.tt +0 -21
  15. data/lib/generators/terminalwire/install/templates/bin/terminalwire +0 -2
  16. data/lib/generators/terminalwire/install/templates/main_terminal.rb +0 -40
  17. data/lib/terminalwire/adapter.rb +0 -53
  18. data/lib/terminalwire/cache.rb +0 -114
  19. data/lib/terminalwire/client/entitlement/environment_variables.rb +0 -26
  20. data/lib/terminalwire/client/entitlement/paths.rb +0 -97
  21. data/lib/terminalwire/client/entitlement/policy.rb +0 -117
  22. data/lib/terminalwire/client/entitlement/schemes.rb +0 -26
  23. data/lib/terminalwire/client/entitlement.rb +0 -9
  24. data/lib/terminalwire/client/exec.rb +0 -44
  25. data/lib/terminalwire/client/handler.rb +0 -67
  26. data/lib/terminalwire/client/resource.rb +0 -204
  27. data/lib/terminalwire/client/server_license_verification.rb +0 -74
  28. data/lib/terminalwire/client.rb +0 -30
  29. data/lib/terminalwire/logging.rb +0 -8
  30. data/lib/terminalwire/rails.rb +0 -69
  31. data/lib/terminalwire/server/context.rb +0 -80
  32. data/lib/terminalwire/server/resource.rb +0 -120
  33. data/lib/terminalwire/server.rb +0 -63
  34. data/lib/terminalwire/thor.rb +0 -55
  35. data/lib/terminalwire/transport.rb +0 -58
  36. data/lib/terminalwire/version.rb +0 -5
  37. data/lib/terminalwire.rb +0 -56
  38. data/sig/terminalwire.rbs +0 -4
@@ -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
@@ -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
@@ -1,8 +0,0 @@
1
- require 'logger'
2
-
3
- module Terminalwire
4
- module Logging
5
- DEVICE = Logger.new($stdout, level: ENV.fetch("TERMINALWIRE_LOG_LEVEL", "info"))
6
- def logger = DEVICE
7
- end
8
- end
@@ -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
@@ -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
@@ -1,55 +0,0 @@
1
- require 'thor'
2
-
3
- module Terminalwire
4
- module Thor
5
- class Shell < ::Thor::Shell::Basic
6
- extend Forwardable
7
-
8
- # Encapsulates all of the IO resources for a Terminalwire adapter.
9
- attr_reader :context, :session
10
-
11
- def_delegators :context,
12
- :stdin, :stdout, :stderr
13
-
14
- def initialize(context, *, **, &)
15
- @context = context
16
- @session = Terminalwire::Rails::Session.new(context:)
17
- super(*,**,&)
18
- end
19
- end
20
-
21
- def self.included(base)
22
- base.extend ClassMethods
23
-
24
- # I have to do this in a block to deal with some of Thor's DSL
25
- base.class_eval do
26
- extend Forwardable
27
-
28
- protected
29
-
30
- no_commands do
31
- def_delegators :shell,
32
- :context, :session
33
- def_delegators :context,
34
- :stdout, :stdin, :stderr, :browser
35
- def_delegators :stdout,
36
- :puts, :print
37
- def_delegators :stdin,
38
- :gets, :getpass
39
-
40
- # Feels more naturual to call `client.files` etc. from
41
- # the serve since it's more apparent that it's a client.
42
- alias :client :context
43
- end
44
- end
45
- end
46
-
47
- module ClassMethods
48
- def start(given_args = ARGV, config = {})
49
- context = config.delete(:context)
50
- config[:shell] = Shell.new(context) if context
51
- super(given_args, config)
52
- end
53
- end
54
- end
55
- end
@@ -1,58 +0,0 @@
1
- require 'uri'
2
- require 'async/websocket/client'
3
-
4
- module Terminalwire
5
- module Transport
6
- class Base
7
- def self.connect(url)
8
- raise NotImplementedError, "Subclass must implement .connect"
9
- end
10
-
11
- def self.listen(url)
12
- raise NotImplementedError, "Subclass must implement .listen"
13
- end
14
-
15
- def read
16
- raise NotImplementedError, "Subclass must implement #read"
17
- end
18
-
19
- def write(data)
20
- raise NotImplementedError, "Subclass must implement #write"
21
- end
22
-
23
- def close
24
- raise NotImplementedError, "Subclass must implement #close"
25
- end
26
- end
27
-
28
- class WebSocket < Base
29
- def self.connect(url)
30
- uri = URI(url)
31
- endpoint = Async::HTTP::Endpoint.parse(uri)
32
- adapter = Async::WebSocket::Client.connect(endpoint)
33
- new(adapter)
34
- end
35
-
36
- def self.listen(url)
37
- # This would need to be implemented with a WebSocket server library
38
- raise NotImplementedError, "WebSocket server not implemented"
39
- end
40
-
41
- def initialize(websocket)
42
- @websocket = websocket
43
- end
44
-
45
- def read
46
- @websocket.read&.buffer
47
- end
48
-
49
- def write(data)
50
- @websocket.write(data)
51
- end
52
-
53
- def close
54
- @websocket.close
55
- end
56
- end
57
- end
58
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Terminalwire
4
- VERSION = "0.2.4"
5
- end
data/lib/terminalwire.rb DELETED
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "terminalwire/version"
4
-
5
- require 'forwardable'
6
- require 'uri'
7
- require 'zeitwerk'
8
-
9
- require 'async'
10
- require 'async/http/endpoint'
11
- require 'async/websocket/client'
12
- require 'async/websocket/adapters/rack'
13
- require 'uri-builder'
14
-
15
- module Terminalwire
16
- class Error < StandardError; end
17
-
18
- # Zeitwerk loader for the Terminalwire gem.
19
- Loader = Zeitwerk::Loader.for_gem.tap do |loader|
20
- loader.ignore("#{__dir__}/generators")
21
- loader.setup
22
- end
23
-
24
- # Used by Terminalwire client to connect to Terminalire.com for license
25
- # validations, etc.
26
- TERMINALWIRE_URL = "https://terminalwire.com".freeze
27
- def self.url = URI.build(TERMINALWIRE_URL)
28
-
29
- module Resource
30
- class Base
31
- attr_reader :name, :adapter
32
-
33
- def initialize(name, adapter)
34
- @name = name.to_s
35
- @adapter = adapter
36
- end
37
-
38
- def connect; end
39
- def disconnect; end
40
-
41
- def fail(response, **data)
42
- respond(status: "failure", response:, **data)
43
- end
44
-
45
- def succeed(response, **data)
46
- respond(status: "success", response:, **data)
47
- end
48
-
49
- private
50
-
51
- def respond(**response)
52
- adapter.write(event: "resource", name: @name, **response)
53
- end
54
- end
55
- end
56
- end
data/sig/terminalwire.rbs DELETED
@@ -1,4 +0,0 @@
1
- module Terminalwire
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end