pytty 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/pytty/client/cli.rb +2 -0
- data/lib/pytty/client/cli/root_command.rb +1 -0
- data/lib/pytty/client/cli/run_command.rb +8 -3
- data/lib/pytty/client/cli/stream_command.rb +40 -0
- data/lib/pytty/daemon/api/chunk.rb +28 -0
- data/lib/pytty/daemon/api/router.rb +11 -2
- data/lib/pytty/daemon/api/server.rb +6 -1
- data/lib/pytty/daemon/api/web_sockets.rb +40 -12
- data/lib/pytty/daemon/components.rb +4 -1
- data/lib/pytty/daemon/components/run.rb +6 -1
- data/lib/pytty/daemon/components/stream.rb +37 -0
- data/lib/pytty/daemon/components/web_handler.rb +11 -2
- data/lib/pytty/daemon/components/web_socket_handler.rb +37 -0
- data/lib/pytty/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86943cf397ea5d69b0d735243100ad08ed00629b2b1ffc34157f44acb0ff2316
|
4
|
+
data.tar.gz: 5ee2c4f7c9bd1bbb9e550be3140d1c7c53f5b51f74ae3cad2f60994b1a026374
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04b69e3162611cda3e34a33dfae91a6cbad3226ae31e928d8a77dcf1e210c7b4fd766a173f182e8be7a089c2ca26cd3f6456fb13fab420695108c706649a56c3
|
7
|
+
data.tar.gz: 4a61b1ff318f39adb8918565e78a6ef7ed189c7063bcb97ce3ca9efc27593a2b73e8a9262e5542cd5c6e850eb77ddbe5c9555be8948f136f4422962d3d18c06b
|
data/Gemfile.lock
CHANGED
data/lib/pytty/client/cli.rb
CHANGED
@@ -2,18 +2,23 @@
|
|
2
2
|
require 'async'
|
3
3
|
require 'async/http'
|
4
4
|
require 'async/http/internet'
|
5
|
+
require 'json'
|
5
6
|
|
6
7
|
module Pytty
|
7
8
|
module Client
|
8
9
|
module Cli
|
9
10
|
class RunCommand < Clamp::Command
|
11
|
+
parameter "CMD ...", "command"
|
10
12
|
def execute
|
11
13
|
Async.run do
|
12
14
|
internet = Async::HTTP::Internet.new
|
13
15
|
headers = [['accept', 'application/json']]
|
14
|
-
body =
|
15
|
-
|
16
|
-
|
16
|
+
body = {
|
17
|
+
cmd: cmd_list
|
18
|
+
}.to_json
|
19
|
+
|
20
|
+
response = internet.post("http://localhost:1234/v1/run", headers, [body])
|
21
|
+
puts response.read
|
17
22
|
ensure
|
18
23
|
internet.close
|
19
24
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'async/reactor'
|
3
|
+
require 'async/io/stream'
|
4
|
+
require 'async/http/url_endpoint'
|
5
|
+
require 'async/websocket/client'
|
6
|
+
require 'io/console'
|
7
|
+
|
8
|
+
module Pytty
|
9
|
+
module Client
|
10
|
+
module Cli
|
11
|
+
class StreamCommand < Clamp::Command
|
12
|
+
parameter "CMD ...", "command"
|
13
|
+
def execute
|
14
|
+
env = {
|
15
|
+
"LINES" => IO.console.winsize.first.to_s,
|
16
|
+
"COLUMNS" => IO.console.winsize.last.to_s
|
17
|
+
}
|
18
|
+
|
19
|
+
url = "ws://localhost:1234/v1/stream"
|
20
|
+
endpoint = Async::HTTP::URLEndpoint.parse url
|
21
|
+
Async::Reactor.run do |task|
|
22
|
+
endpoint.connect do |socket|
|
23
|
+
connection = Async::WebSocket::Client.new socket, url
|
24
|
+
|
25
|
+
connection.send_message({
|
26
|
+
cmd: cmd_list,
|
27
|
+
env: env
|
28
|
+
})
|
29
|
+
|
30
|
+
while message = connection.next_message
|
31
|
+
print message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "falcon"
|
2
|
+
require_relative "router"
|
3
|
+
|
4
|
+
module Pytty
|
5
|
+
module Daemon
|
6
|
+
module Api
|
7
|
+
class Chunk
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
task = Async::Task.current
|
11
|
+
body = Async::HTTP::Body::Writable.new
|
12
|
+
task.async do |task|
|
13
|
+
10.times do
|
14
|
+
body.write "hello"
|
15
|
+
task.sleep 0.5
|
16
|
+
end
|
17
|
+
rescue Exception => ex
|
18
|
+
p ex
|
19
|
+
ensure
|
20
|
+
body.close
|
21
|
+
end
|
22
|
+
|
23
|
+
[200, {'content-type' => 'text/html; charset=utf-8'}, body]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require_relative "web_sockets"
|
2
|
+
require_relative "chunk"
|
3
|
+
|
2
4
|
require_relative "../components"
|
3
5
|
|
4
6
|
module Pytty
|
@@ -8,11 +10,18 @@ module Pytty
|
|
8
10
|
def call(env)
|
9
11
|
req = Rack::Request.new(env)
|
10
12
|
resp = case req.path_info
|
13
|
+
when "/stream"
|
14
|
+
if env["HTTP_UPGRADE"] == "websocket"
|
15
|
+
handler = Pytty::Daemon::Components::WebSocketHandler.new(env)
|
16
|
+
handler.handle
|
17
|
+
end
|
18
|
+
|
19
|
+
[404, {"Content-Type" => "text/html"}, ["websocket only"]]
|
11
20
|
when "/run"
|
12
21
|
handler = Pytty::Daemon::Components::WebHandler.new(env)
|
13
|
-
handler.handle
|
22
|
+
output = handler.handle
|
14
23
|
|
15
|
-
[200, {"Content-Type" => "text/html"}, [
|
24
|
+
[200, {"Content-Type" => "text/html"}, [output]]
|
16
25
|
when "/ws"
|
17
26
|
if env["HTTP_UPGRADE"] == "websocket"
|
18
27
|
ws = WebSockets.new env
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "falcon"
|
2
2
|
require_relative "router"
|
3
|
+
require_relative "chunk"
|
3
4
|
|
4
5
|
module Pytty
|
5
6
|
module Daemon
|
@@ -10,7 +11,11 @@ module Pytty
|
|
10
11
|
|
11
12
|
def run
|
12
13
|
rack_app = Rack::Builder.new do
|
13
|
-
use Rack::CommonLogger
|
14
|
+
#use Rack::CommonLogger
|
15
|
+
|
16
|
+
map "/chunk" do
|
17
|
+
run Chunk.new
|
18
|
+
end
|
14
19
|
map "/v1" do
|
15
20
|
run Router.new
|
16
21
|
end
|
@@ -10,23 +10,51 @@ module Pytty
|
|
10
10
|
|
11
11
|
@connection = Async::WebSocket::Server.open(env)
|
12
12
|
@@connections << @connection
|
13
|
+
|
14
|
+
@messages = Async::Queue.new
|
15
|
+
@writes = Async::Queue.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def close
|
19
|
+
@@connections.each do |connection|
|
20
|
+
connection.close
|
21
|
+
end
|
22
|
+
@@connections = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def read
|
26
|
+
@messages.dequeue
|
27
|
+
end
|
28
|
+
|
29
|
+
def write(message)
|
30
|
+
@writes.enqueue message
|
13
31
|
end
|
14
32
|
|
15
33
|
def handle
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
Async::Task.current.async do
|
35
|
+
while message = @connection.next_event
|
36
|
+
type = case message
|
37
|
+
when WebSocket::Driver::OpenEvent
|
38
|
+
puts "ws: open #{@env["REMOTE_ADDR"]}"
|
39
|
+
when WebSocket::Driver::CloseEvent
|
40
|
+
puts "ws: close #{@env["REMOTE_ADDR"]}"
|
41
|
+
@@connections.delete @connection
|
42
|
+
when WebSocket::Driver::MessageEvent
|
43
|
+
puts "ws: message #{@env["REMOTE_ADDR"]}"
|
44
|
+
@messages.enqueue message.data
|
45
|
+
else
|
46
|
+
raise "ws: unknown #{message.inspect}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Async::Task.current.async do |task|
|
52
|
+
while message = @writes.dequeue
|
25
53
|
@@connections.each do |connection|
|
26
|
-
connection.send_message(message
|
54
|
+
connection.send_message(message)
|
55
|
+
rescue Exception => ex
|
56
|
+
#TODO
|
27
57
|
end
|
28
|
-
else
|
29
|
-
raise "ws: unknown #{message.inspect}"
|
30
58
|
end
|
31
59
|
end
|
32
60
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'pty'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module Pytty
|
6
|
+
module Daemon
|
7
|
+
module Components
|
8
|
+
class Stream
|
9
|
+
def initialize(cmd:, env:)
|
10
|
+
@cmd = cmd
|
11
|
+
@env = env
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(stream:)
|
15
|
+
pipe = IO.pipe
|
16
|
+
stderr = Async::IO::Generic.new(pipe.first)
|
17
|
+
stderr_writer = Async::IO::Generic.new(pipe.last)
|
18
|
+
|
19
|
+
|
20
|
+
cmd, args = @cmd
|
21
|
+
process_stdout, process_stdin, pid = PTY.spawn(@env, cmd, *args, err: stderr_writer.fileno)
|
22
|
+
stderr_writer.close
|
23
|
+
|
24
|
+
stdout = Async::IO::Generic.new process_stdout
|
25
|
+
stdin = Async::IO::Generic.new process_stdin
|
26
|
+
Async::Task.current.async do |task|
|
27
|
+
while c = stdout.read(1)
|
28
|
+
stream.write c
|
29
|
+
end
|
30
|
+
stream.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -11,12 +11,21 @@ module Pytty
|
|
11
11
|
|
12
12
|
def handle
|
13
13
|
req = Rack::Request.new(@env)
|
14
|
-
|
14
|
+
body = begin
|
15
|
+
JSON.parse(req.body.read)
|
16
|
+
rescue
|
17
|
+
end
|
18
|
+
|
19
|
+
obj = case req.path_info
|
15
20
|
when "/run"
|
16
|
-
Run.new.
|
21
|
+
Run.new cmd: body.dig("cmd")
|
22
|
+
when "/stream"
|
23
|
+
Stream.new cmd: body.dig("cmd")
|
17
24
|
else
|
18
25
|
raise "Unknown: #{req.path_info}"
|
19
26
|
end
|
27
|
+
|
28
|
+
obj.run
|
20
29
|
end
|
21
30
|
end
|
22
31
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "rack/request"
|
3
|
+
|
4
|
+
module Pytty
|
5
|
+
module Daemon
|
6
|
+
module Components
|
7
|
+
class WebSocketHandler
|
8
|
+
def initialize(env)
|
9
|
+
@env = env
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle
|
13
|
+
req = Rack::Request.new(@env)
|
14
|
+
ws = Pytty::Daemon::Api::WebSockets.new(@env)
|
15
|
+
ws.handle
|
16
|
+
|
17
|
+
klass = case req.path_info
|
18
|
+
when "/stream"
|
19
|
+
Stream
|
20
|
+
else
|
21
|
+
raise "Unknown: #{req.path_info}"
|
22
|
+
end
|
23
|
+
params = ws.read
|
24
|
+
body = begin
|
25
|
+
JSON.parse(params)
|
26
|
+
rescue Exception => ex
|
27
|
+
p ex
|
28
|
+
end
|
29
|
+
|
30
|
+
obj = klass.new cmd: body.dig("cmd"), env: body.dig("env")
|
31
|
+
obj.run stream: ws
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
data/lib/pytty/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pytty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matti Paksula
|
@@ -178,7 +178,9 @@ files:
|
|
178
178
|
- lib/pytty/client/cli.rb
|
179
179
|
- lib/pytty/client/cli/root_command.rb
|
180
180
|
- lib/pytty/client/cli/run_command.rb
|
181
|
+
- lib/pytty/client/cli/stream_command.rb
|
181
182
|
- lib/pytty/common/cli/version_command.rb
|
183
|
+
- lib/pytty/daemon/api/chunk.rb
|
182
184
|
- lib/pytty/daemon/api/router.rb
|
183
185
|
- lib/pytty/daemon/api/server.rb
|
184
186
|
- lib/pytty/daemon/api/web_sockets.rb
|
@@ -187,7 +189,9 @@ files:
|
|
187
189
|
- lib/pytty/daemon/cli/serve_command.rb
|
188
190
|
- lib/pytty/daemon/components.rb
|
189
191
|
- lib/pytty/daemon/components/run.rb
|
192
|
+
- lib/pytty/daemon/components/stream.rb
|
190
193
|
- lib/pytty/daemon/components/web_handler.rb
|
194
|
+
- lib/pytty/daemon/components/web_socket_handler.rb
|
191
195
|
- lib/pytty/version.rb
|
192
196
|
- pytty.gemspec
|
193
197
|
homepage: https://github.com/pyttyhq/pytty-ruby
|