terminalwire-rails 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6ba7bd27bf86aee365f4f45139d8a125e08fa01295d872a059570de818b834f9
4
+ data.tar.gz: 9f71189cbd898c0c4b5033e1dcaadec72126e96f35e5860fa58e6711189a7982
5
+ SHA512:
6
+ metadata.gz: 49fe21592da92e7587a2c2bd93f920d1bde2b2e4d40af8076a4d43a9405497c58e7fe343d50f4a6e831a7ea1c6f0106466904127ad2a575492c13cf7d4edcedc
7
+ data.tar.gz: 0fe43f8b5f6658131efe122161d5700bccdf8e4d958da4ba7c9cd6d72c2e9dc62cc490b2141b2cd8d26ad3068bd4683bf14274e493fb2230d2d034e0222de96d
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Installs Terminalwire
3
+
4
+ Example:
5
+ bin/rails generate terminalwire:install
6
+
7
+ This will create:
8
+ app/terminal/application_terminal.rb
9
+ bin/<your-app>
@@ -0,0 +1,43 @@
1
+ require "bundler"
2
+
3
+ class Terminalwire::InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ argument :binary_name, type: :string, required: true, banner: "binary_name"
7
+
8
+ def create_terminal_files
9
+ template "application_terminal.rb.tt", Rails.root.join("app/terminal/application_terminal.rb")
10
+ template "main_terminal.rb", Rails.root.join("app/terminal/main_terminal.rb")
11
+ end
12
+
13
+ def create_binary_files
14
+ copy_file "bin/terminalwire", binary_path
15
+ chmod binary_path, 0755, verbose: false
16
+ end
17
+
18
+ def add_route
19
+ route <<~ROUTE
20
+ match "/terminal",
21
+ to: Terminalwire::Rails::Thor.new(MainTerminal),
22
+ via: [:get, :connect]
23
+ ROUTE
24
+ end
25
+
26
+ def bundle_development_dependencies
27
+ # Add the terminalwire gem to the development group in the Gemfile.
28
+ gem "terminalwire", group: :development
29
+ end
30
+
31
+ def print_post_install_message
32
+ say ""
33
+ say "Terminalwire has been successfully installed!", :green
34
+ say "Run `#{binary_path.relative_path_from(Rails.root)}` to verify everything is in working order. For support visit https://terminalwire.com."
35
+ say ""
36
+ end
37
+
38
+ private
39
+
40
+ def binary_path
41
+ Rails.root.join("bin", binary_name)
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ # Learn how to use Thor at http://whatisthor.com.
2
+ class ApplicationTerminal < Thor
3
+ # Enables IO Streaming.
4
+ include Terminalwire::Thor
5
+
6
+ # The name of your binary. Thor uses this for its help output.
7
+ def self.basename = "<%= binary_name %>"
8
+
9
+ private
10
+
11
+ def current_user=(user)
12
+ # The Session object is a hash-like object that encrypts and signs a hash that's
13
+ # stored on the client's file sytem. Conceptually, it's similar to Rails signed
14
+ # and encrypted client-side cookies.
15
+ session["user_id"] = user.id
16
+ end
17
+
18
+ def current_user
19
+ @current_user ||= User.find(session["user_id"])
20
+ end
21
+ end
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env terminalwire-exec
2
+ url: "ws://localhost:3000/terminal"
@@ -0,0 +1,40 @@
1
+ class MainTerminal < ApplicationTerminal
2
+ desc "hello NAME", "say hello to NAME"
3
+ def hello(name)
4
+ puts "Hello #{name}"
5
+ end
6
+
7
+ desc "login", "Login to your account"
8
+ def login
9
+ print "Email: "
10
+ email = gets.chomp
11
+
12
+ print "Password: "
13
+ password = getpass
14
+
15
+ # Replace this with your own authentication logic; this is an example
16
+ # of how you might do this with Devise.
17
+ user = User.find_for_authentication(email: email)
18
+ if user && user.valid_password?(password)
19
+ self.current_user = user
20
+ puts "Successfully logged in as #{current_user.email}."
21
+ else
22
+ fail "Could not find a user with that email and password."
23
+ end
24
+ end
25
+
26
+ desc "whoami", "Displays current user information."
27
+ def whoami
28
+ if self.current_user
29
+ puts "Logged in as #{current_user.email}."
30
+ else
31
+ fail "Not logged in. Run `#{self.class.basename} login` to login."
32
+ end
33
+ end
34
+
35
+ desc "logout", "Logout of your account"
36
+ def logout
37
+ session.reset
38
+ puts "Successfully logged out."
39
+ end
40
+ end
@@ -0,0 +1,116 @@
1
+ require "terminalwire-server"
2
+ require "thor"
3
+ require "rails"
4
+ require "jwt"
5
+
6
+ module Terminalwire
7
+ module Rails
8
+ class Session
9
+ # JWT file name for the session file.
10
+ FILENAME = "session.jwt"
11
+
12
+ # Empty dictionary the user can stash all their session data into.
13
+ EMPTY_SESSION = {}.freeze
14
+
15
+ extend Forwardable
16
+
17
+ # Delegate `dig` and `fetch` to the `read` method
18
+ def_delegators :read,
19
+ :dig, :fetch, :[]
20
+
21
+ def initialize(context:, path: nil, secret_key: self.class.secret_key)
22
+ @context = context
23
+ @path = Pathname.new(path || context.storage_path)
24
+ @config_file_path = @path.join(FILENAME)
25
+ @secret_key = secret_key
26
+
27
+ ensure_file
28
+ end
29
+
30
+ def read
31
+ jwt_token = @context.file.read(@config_file_path)
32
+ decoded_data = JWT.decode(jwt_token, @secret_key, true, algorithm: 'HS256')
33
+ decoded_data[0] # JWT payload is the first element in the array
34
+ rescue JWT::DecodeError => e
35
+ raise "Invalid or tampered file: #{e.message}"
36
+ end
37
+
38
+ def reset
39
+ @context.file.delete @config_file_path
40
+ end
41
+
42
+ def edit
43
+ config = read
44
+ yield config
45
+ write(config)
46
+ end
47
+
48
+ def []=(key, value)
49
+ edit { |config| config[key] = value }
50
+ end
51
+
52
+ def write(config)
53
+ token = JWT.encode(config, @secret_key, 'HS256')
54
+ @context.file.write(@config_file_path, token)
55
+ end
56
+
57
+ private
58
+
59
+ def ensure_file
60
+ return true if @context.file.exist? @config_file_path
61
+ # Create the path if it doesn't exist on the client.
62
+ @context.directory.create @path
63
+ # Write an empty configuration on initialization
64
+ write(EMPTY_SESSION)
65
+ end
66
+
67
+ def self.secret_key
68
+ ::Rails.application.secret_key_base
69
+ end
70
+ end
71
+
72
+ class Thor < Server::WebSocket
73
+ include Logging
74
+
75
+ def initialize(cli_class)
76
+ @cli_class = cli_class
77
+
78
+ unless @cli_class.included_modules.include?(Terminalwire::Server::Thor)
79
+ raise 'Add `include Terminalwire::Server::Thor` to the #{@cli_class.inspect} class.'
80
+ end
81
+ end
82
+
83
+ def error_message
84
+ "An error occurred. Please try again."
85
+ end
86
+
87
+ def handle(adapter)
88
+ logger.info "ThorServer: Running #{@cli_class.inspect}"
89
+ while message = adapter.read
90
+ case message
91
+ in { event: "initialization", protocol:, program: { arguments: }, entitlement: }
92
+ context = Terminalwire::Server::Context.new(adapter:, entitlement:)
93
+
94
+ begin
95
+ @cli_class.start(arguments, context:)
96
+ context.exit
97
+ rescue StandardError => e
98
+ if ::Rails.application.config.consider_all_requests_local
99
+ # Show the full error message with stack trace in development
100
+ context.stderr.puts "#{e.inspect}\n#{e.backtrace.join("\n")}"
101
+ else
102
+ # Show a generic message in production
103
+ context.stderr.puts error_message
104
+ end
105
+ context.exit 1
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ # Alias so we can put off breaking `include Terminalwire::Thor`
114
+ # in the ApplicationTerminal class.
115
+ Thor = Terminalwire::Server::Thor
116
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'terminalwire/rails'
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: terminalwire-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0.alpha1
5
+ platform: ruby
6
+ authors:
7
+ - Brad Gessler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: terminalwire-server
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0.alpha1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.0.alpha1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '7.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '7.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jwt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ description: Stream command-line apps from your server without a web API
56
+ email:
57
+ - brad@terminalwire.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/generators/terminalwire/install/USAGE
63
+ - lib/generators/terminalwire/install/install_generator.rb
64
+ - lib/generators/terminalwire/install/templates/application_terminal.rb.tt
65
+ - lib/generators/terminalwire/install/templates/bin/terminalwire
66
+ - lib/generators/terminalwire/install/templates/main_terminal.rb
67
+ - lib/terminalwire-rails.rb
68
+ - lib/terminalwire/rails.rb
69
+ homepage: https://terminalwire.com/ruby
70
+ licenses:
71
+ - Proprietary (https://terminalwire.com/license)
72
+ metadata:
73
+ allowed_push_host: https://rubygems.org/
74
+ homepage_uri: https://terminalwire.com/ruby
75
+ source_code_uri: https://github.com/terminalwire/ruby/tree/main/terminalwire-rails
76
+ changelog_uri: https://github.com/terminalwire/ruby/tags
77
+ funding_uri: https://terminalwire.com/funding
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 3.0.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.5.9
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Ship a CLI for your web app. No API required.
97
+ test_files: []