panteao-client 1.1.18
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 +7 -0
- data/lib/panteao_client.rb +234 -0
- metadata +42 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 05cd40c5543a8619277c2b9d3ead17650da0c6ce1a08809dd20fd50fc53a6468
|
|
4
|
+
data.tar.gz: 832292ba43dd44c4190282edbe57046dd64933512b6fcdc24fdca0238afa3304
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3d84d924695d2374fa319f28e351b27aa9dc53f711494036012ae45d65160ac734aa7fda2f2c61eaaa7bc5c8e74d16387569975744eddad814e849d0526b57a1
|
|
7
|
+
data.tar.gz: 76b9c30c91c66500d8e85ee6ca234db5cd4a2645b56a18713df507bfba795a6fe51043ad5b2d19e19587b901e5c1834efd632c543ec499702c4fbae1042d6fe3
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'rubygems/package'
|
|
6
|
+
require 'zlib'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'open3'
|
|
9
|
+
require 'stringio'
|
|
10
|
+
|
|
11
|
+
module Panteao
|
|
12
|
+
VERSION = '1.1.18'
|
|
13
|
+
|
|
14
|
+
class BdiClient
|
|
15
|
+
|
|
16
|
+
def self.download_engine(bin_path)
|
|
17
|
+
is_win = Gem.win_platform?
|
|
18
|
+
is_mac = /darwin/ =~ RUBY_PLATFORM
|
|
19
|
+
os_name = is_win ? 'win32' : (is_mac ? 'darwin' : 'linux')
|
|
20
|
+
|
|
21
|
+
arch = (/arm/ =~ RUBY_PLATFORM || /aarch64/ =~ RUBY_PLATFORM) ? 'arm64' : 'x64'
|
|
22
|
+
|
|
23
|
+
pkg_name = "panteao-engine-#{os_name}-#{arch}"
|
|
24
|
+
url = "https://registry.npmjs.org/#{pkg_name}/-/#{pkg_name}-#{VERSION}.tgz"
|
|
25
|
+
|
|
26
|
+
puts "\e[36m[Panteao]\e[0m Downloading native engine for #{os_name}-#{arch} (v#{VERSION})..."
|
|
27
|
+
|
|
28
|
+
uri = URI(url)
|
|
29
|
+
response = Net::HTTP.get_response(uri)
|
|
30
|
+
|
|
31
|
+
raise "Failed to download engine: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
|
32
|
+
|
|
33
|
+
sio = StringIO.new(response.body)
|
|
34
|
+
gz = Zlib::GzipReader.new(sio)
|
|
35
|
+
tar = Gem::Package::TarReader.new(gz)
|
|
36
|
+
|
|
37
|
+
tar.each do |entry|
|
|
38
|
+
if entry.full_name.end_with?('panteao-engine') || entry.full_name.end_with?('panteao-engine.exe')
|
|
39
|
+
FileUtils.mkdir_p(File.dirname(bin_path))
|
|
40
|
+
File.open(bin_path, 'wb') { |f| f.write(entry.read) }
|
|
41
|
+
FileUtils.chmod(0755, bin_path)
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
raise "Binary not found in tarball"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.read_logs(io)
|
|
49
|
+
Thread.new do
|
|
50
|
+
begin
|
|
51
|
+
io.each_line do |line|
|
|
52
|
+
line = line.strip
|
|
53
|
+
next if line.empty?
|
|
54
|
+
if line.start_with?('[') && (idx2 = line.index(']'))
|
|
55
|
+
name = line[1...idx2].split('.').last
|
|
56
|
+
puts "\e[36m[#{name}]\e[0m #{line[(idx2 + 2)..-1]}"
|
|
57
|
+
else
|
|
58
|
+
puts "\e[36m[MAS]\e[0m #{line}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
rescue
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.get_free_port
|
|
67
|
+
server = TCPServer.new('127.0.0.1', 0)
|
|
68
|
+
port = server.addr[1]
|
|
69
|
+
server.close
|
|
70
|
+
port
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.find_binary
|
|
74
|
+
is_win = Gem.win_platform?
|
|
75
|
+
bin_name = is_win ? 'panteao-engine.exe' : 'panteao-engine'
|
|
76
|
+
|
|
77
|
+
current_dir = File.expand_path(File.dirname(__FILE__))
|
|
78
|
+
cand1 = File.join(current_dir, bin_name)
|
|
79
|
+
return cand1 if File.exist?(cand1)
|
|
80
|
+
cand2 = File.join(current_dir, 'bin', bin_name)
|
|
81
|
+
return cand2 if File.exist?(cand2)
|
|
82
|
+
|
|
83
|
+
cwd = Dir.pwd
|
|
84
|
+
cand3 = File.join(cwd, bin_name)
|
|
85
|
+
return cand3 if File.exist?(cand3)
|
|
86
|
+
cand4 = File.join(cwd, 'bin', bin_name)
|
|
87
|
+
return cand4 if File.exist?(cand4)
|
|
88
|
+
|
|
89
|
+
bin_name
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def initialize(host = '127.0.0.1', port = 0, project: nil)
|
|
93
|
+
if project
|
|
94
|
+
if port == 0
|
|
95
|
+
port = self.class.get_free_port
|
|
96
|
+
end
|
|
97
|
+
bin = self.class.find_binary
|
|
98
|
+
unless File.exist?(bin)
|
|
99
|
+
current_dir = File.expand_path(File.dirname(__FILE__))
|
|
100
|
+
bin = File.join(current_dir, Gem.win_platform? ? 'panteao-engine.exe' : 'panteao-engine')
|
|
101
|
+
self.class.download_engine(bin)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
@stdin, @stdout, @stderr, @wait_thr = Open3.popen3(bin, project, '--port', port.to_s)
|
|
105
|
+
@pid = @wait_thr.pid
|
|
106
|
+
self.class.read_logs(@stdout)
|
|
107
|
+
self.class.read_logs(@stderr)
|
|
108
|
+
sleep 0.8
|
|
109
|
+
elsif port == 0
|
|
110
|
+
port = 44444
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
@socket = TCPSocket.new(host, port)
|
|
114
|
+
while (line = @socket.gets)
|
|
115
|
+
break if line.include?('"type":"mas_ready"')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
@handlers = {}
|
|
119
|
+
@running = true
|
|
120
|
+
@thread = Thread.new { listen }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def send_msg(performative, sender, receiver, content)
|
|
124
|
+
msg = { type: 'message', performative: performative, sender: sender, receiver: receiver, content: content }
|
|
125
|
+
@socket.puts(msg.to_json)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def send_perception(action, perception)
|
|
129
|
+
payload = { type: 'perception', action: action, perception: perception }.to_json + "\n"
|
|
130
|
+
@socket.write(payload)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def register_action(action_name, &block)
|
|
134
|
+
@handlers[action_name] = block
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def send_action_result(id, success)
|
|
140
|
+
payload = { type: 'action_result', id: id, success: success }.to_json + "\n"
|
|
141
|
+
@socket.write(payload)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def listen
|
|
145
|
+
while @running && (line = @socket.gets)
|
|
146
|
+
handle_line(line.strip)
|
|
147
|
+
end
|
|
148
|
+
rescue => e
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def handle_line(line)
|
|
152
|
+
return if line.empty?
|
|
153
|
+
begin
|
|
154
|
+
msg = JSON.parse(line)
|
|
155
|
+
if msg['type'] == 'action'
|
|
156
|
+
raw_action = msg['action']
|
|
157
|
+
action_id = msg['id']
|
|
158
|
+
name, args = parse_action(raw_action)
|
|
159
|
+
|
|
160
|
+
handler = @handlers[name]
|
|
161
|
+
if handler
|
|
162
|
+
respond = proc { |success| send_action_result(action_id, success) }
|
|
163
|
+
handler.call(args, respond)
|
|
164
|
+
else
|
|
165
|
+
send_action_result(action_id, true)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
rescue => e
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def parse_action(action_str)
|
|
173
|
+
paren_idx = action_str.index('(')
|
|
174
|
+
return action_str.strip, [] unless paren_idx
|
|
175
|
+
|
|
176
|
+
name = action_str[0...paren_idx].strip
|
|
177
|
+
args_str = action_str[paren_idx + 1...action_str.rindex(')')]
|
|
178
|
+
|
|
179
|
+
args = []
|
|
180
|
+
current = ""
|
|
181
|
+
inside_quotes = false
|
|
182
|
+
depth_brackets = 0
|
|
183
|
+
depth_parens = 0
|
|
184
|
+
|
|
185
|
+
args_str.each_char do |c|
|
|
186
|
+
if c == '"'
|
|
187
|
+
inside_quotes = !inside_quotes
|
|
188
|
+
current << c
|
|
189
|
+
elsif !inside_quotes && c == '['
|
|
190
|
+
depth_brackets += 1
|
|
191
|
+
current << c
|
|
192
|
+
elsif !inside_quotes && c == ']'
|
|
193
|
+
depth_brackets -= 1
|
|
194
|
+
current << c
|
|
195
|
+
elsif !inside_quotes && c == '('
|
|
196
|
+
depth_parens += 1
|
|
197
|
+
current << c
|
|
198
|
+
elsif !inside_quotes && c == ')'
|
|
199
|
+
depth_parens -= 1
|
|
200
|
+
current << c
|
|
201
|
+
elsif c == ',' && !inside_quotes && depth_brackets == 0 && depth_parens == 0
|
|
202
|
+
args << clean_arg(current)
|
|
203
|
+
current = ""
|
|
204
|
+
else
|
|
205
|
+
current << c
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
args << clean_arg(current) unless current.empty?
|
|
209
|
+
|
|
210
|
+
return name, args
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def clean_arg(arg)
|
|
214
|
+
s = arg.strip
|
|
215
|
+
if s.start_with?('"') && s.end_with?('"') && s.length >= 2
|
|
216
|
+
return s[1..-2]
|
|
217
|
+
end
|
|
218
|
+
s
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
public
|
|
222
|
+
|
|
223
|
+
def close
|
|
224
|
+
@running = false
|
|
225
|
+
@socket.close rescue nil
|
|
226
|
+
@thread.join rescue nil
|
|
227
|
+
if @pid
|
|
228
|
+
Process.kill('KILL', @pid) rescue nil
|
|
229
|
+
Process.wait(@pid) rescue nil
|
|
230
|
+
@pid = nil
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: panteao-client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.1.18
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Panteao Authors
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-07-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/panteao_client.rb
|
|
20
|
+
homepage:
|
|
21
|
+
licenses: []
|
|
22
|
+
metadata: {}
|
|
23
|
+
post_install_message:
|
|
24
|
+
rdoc_options: []
|
|
25
|
+
require_paths:
|
|
26
|
+
- lib
|
|
27
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
28
|
+
requirements:
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 2.7.0
|
|
32
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
33
|
+
requirements:
|
|
34
|
+
- - ">="
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '0'
|
|
37
|
+
requirements: []
|
|
38
|
+
rubygems_version: 3.4.20
|
|
39
|
+
signing_key:
|
|
40
|
+
specification_version: 4
|
|
41
|
+
summary: Ruby client for Panteao BDI Coprocessor
|
|
42
|
+
test_files: []
|