gretl 1.0.3 → 1.0.5

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 (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gretl.rb +196 -1
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ec89db993e1173b658f0212d7400f2ff72ec28df4e916d06496633b983d8e83
4
- data.tar.gz: 0b9b805cb901980105a889b53c08c01d07ce15941e2ab6e3d8fa590fce724598
3
+ metadata.gz: 96aab4c6f7f37f115ae1b8db4f531374f4aa37e4cd7da305b87f24c97a8e6bb0
4
+ data.tar.gz: 5607a48037a46aeb238cc01c982a6c9819fae444b03bd0d66ee33a8e77e12230
5
5
  SHA512:
6
- metadata.gz: 18c3a255ebe2071a6d26f37f5e902a981916caf3ae56a810637205f331904adbfc3e85275b6490c17b848a1686d37ce35268c31a638efc6f50501363f957089d
7
- data.tar.gz: b61235716204afefda5eb1a4cb7f9461f0c760aab819c5b5aa7225b9dd62d2c09a1aa9efd5fa61e05455ee7e039afa2859906dc28ce99ff7915f5d3a86e73132
6
+ metadata.gz: 5f338845601daefa91c8bd5a2a31facaed2afbc7b0b648896d1913dab2c90c175b55d3d09da0d0a1705e28f535c54d3ac24e7fdced128196e3f994ca216ee0f7
7
+ data.tar.gz: 632f13e0748e22208bb452479d6fe7a877eb16f374570589286bc7b3e1aabb96b684962319be967c3579ca1c2a579a0e16ac44f2db8839d95850071d7b573dd4
data/lib/gretl.rb CHANGED
@@ -129,6 +129,201 @@ module Gretl
129
129
  raise "Port matching #{query.inspect} did not become active within #{timeout}s"
130
130
  end
131
131
 
132
+ def add(port, name: nil, cmd: nil, group: nil, cwd: nil)
133
+ Gretl._ensure_daemon
134
+ uri = URI("#{Gretl::BASE}/ports")
135
+ body = { port: port }
136
+ body[:name] = name if name
137
+ body[:cmd] = cmd if cmd
138
+ body[:group] = group if group
139
+ body[:cwd] = cwd if cwd
140
+ http = Net::HTTP.new(uri.host, uri.port)
141
+ req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
142
+ req.body = body.to_json
143
+ res = http.request(req)
144
+ data = JSON.parse(res.body, symbolize_names: true)
145
+ Gretl::PortInfo.new(**data.slice(*Gretl::PortInfo.members))
146
+ end
147
+
148
+ def wait_for_inactive(query, timeout: 30, interval: 0.5)
149
+ deadline = Time.now + timeout
150
+ while Time.now < deadline
151
+ Gretl._resolve(query).each { |p| return p if p.stopped? }
152
+ sleep interval
153
+ end
154
+ raise "Port matching #{query.inspect} did not become inactive within #{timeout}s"
155
+ end
156
+ end
157
+
158
+ CLOUD_API = "https://api.gretl.dev"
159
+
160
+ class K8sClient
161
+ def initialize(token)
162
+ @token = token
163
+ end
164
+
165
+ def clusters
166
+ _cloud_get("/k8s/clusters")
167
+ end
168
+
169
+ def workloads(cluster_id: nil)
170
+ path = cluster_id ? "/k8s/clusters/#{cluster_id}/workloads" : "/k8s/workloads"
171
+ _cloud_get(path)
172
+ end
173
+
174
+ def sync(cluster_id)
175
+ _cloud_post("/k8s/clusters/#{cluster_id}/sync")
176
+ end
177
+
178
+ def sleep(workload_id)
179
+ _cloud_post("/k8s/workloads/#{workload_id}/sleep")
180
+ end
181
+
182
+ def wake(workload_id)
183
+ _cloud_post("/k8s/workloads/#{workload_id}/wake")
184
+ end
185
+
186
+ def tether(workload_id, container_port:, label: nil)
187
+ body = { container_port: container_port }
188
+ body[:label] = label if label
189
+ _cloud_post("/k8s/workloads/#{workload_id}/tether", body)
190
+ end
191
+
192
+ def detach(tether_id)
193
+ _cloud_post("/k8s/tethers/#{tether_id}/deactivate")
194
+ end
195
+
196
+ def tethers
197
+ _cloud_get("/k8s/tethers")
198
+ end
199
+
200
+ def auto_sleep(workload_id)
201
+ _cloud_get("/k8s/workloads/#{workload_id}/auto-sleep")
202
+ end
203
+
204
+ def set_auto_sleep(workload_id, enabled:, idle_timeout_minutes:)
205
+ _cloud_put("/k8s/workloads/#{workload_id}/auto-sleep",
206
+ { auto_sleep_enabled: enabled, idle_timeout_minutes: idle_timeout_minutes })
207
+ end
208
+
209
+ private
210
+
211
+ def _cloud_get(path)
212
+ uri = URI("#{CLOUD_API}#{path}")
213
+ req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer #{@token}")
214
+ _cloud_call(uri, req)
215
+ end
216
+
217
+ def _cloud_post(path, body = nil)
218
+ uri = URI("#{CLOUD_API}#{path}")
219
+ req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{@token}", "Content-Type" => "application/json")
220
+ req.body = body.to_json if body
221
+ _cloud_call(uri, req)
222
+ end
223
+
224
+ def _cloud_put(path, body)
225
+ uri = URI("#{CLOUD_API}#{path}")
226
+ req = Net::HTTP::Put.new(uri, "Authorization" => "Bearer #{@token}", "Content-Type" => "application/json")
227
+ req.body = body.to_json
228
+ _cloud_call(uri, req)
229
+ end
230
+
231
+ def _cloud_call(uri, req)
232
+ http = Net::HTTP.new(uri.host, uri.port)
233
+ http.use_ssl = uri.scheme == "https"
234
+ http.open_timeout = 10
235
+ http.read_timeout = 10
236
+ res = http.request(req)
237
+ raise "Gretl API error #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)
238
+ JSON.parse(res.body, symbolize_names: true)
239
+ rescue Errno::ECONNREFUSED => e
240
+ raise "Cannot reach Gretl API: #{e}"
241
+ end
242
+ end
243
+
244
+ class Client
245
+ def initialize(token: nil)
246
+ _token = token || ENV.fetch("GR_TOKEN", "")
247
+ @k8s = K8sClient.new(_token)
248
+ end
249
+
250
+ attr_reader :k8s
251
+
252
+ def ports = Gretl._fetch_ports
253
+ def port(query) = Gretl._resolve(query)
254
+ def active = Gretl._fetch_ports.select(&:active)
255
+ def inactive = Gretl._fetch_ports.reject(&:active)
256
+
257
+ def start(query)
258
+ matches = Gretl._resolve(query)
259
+ raise ArgumentError, "No port found matching #{query.inspect}" if matches.empty?
260
+ matches.each do |p|
261
+ Gretl._request(:post, "/ports/#{p.port}/start") if !p.active && p.has_command
262
+ end
263
+ matches
264
+ end
265
+
266
+ def stop(query)
267
+ matches = Gretl._resolve(query)
268
+ raise ArgumentError, "No port found matching #{query.inspect}" if matches.empty?
269
+ matches.each do |p|
270
+ Gretl._request(:post, "/ports/#{p.port}/stop") if p.active
271
+ end
272
+ matches
273
+ end
274
+
275
+ def start_group(group)
276
+ matches = Gretl._fetch_ports.select { |p| p.group&.downcase == group.downcase }
277
+ raise ArgumentError, "No ports found in group #{group.inspect}" if matches.empty?
278
+ matches.each { |p| Gretl._request(:post, "/ports/#{p.port}/start") if !p.active && p.has_command }
279
+ matches
280
+ end
281
+
282
+ def stop_group(group)
283
+ matches = Gretl._fetch_ports.select { |p| p.group&.downcase == group.downcase }
284
+ raise ArgumentError, "No ports found in group #{group.inspect}" if matches.empty?
285
+ matches.each { |p| Gretl._request(:post, "/ports/#{p.port}/stop") if p.active }
286
+ matches
287
+ end
288
+
289
+ def status_group(group)
290
+ matches = Gretl._fetch_ports.select { |p| p.group&.downcase == group.downcase }
291
+ raise ArgumentError, "No ports found in group #{group.inspect}" if matches.empty?
292
+ matches
293
+ end
294
+
295
+ def remove(query)
296
+ matches = Gretl._resolve(query)
297
+ raise ArgumentError, "No port found matching #{query.inspect}" if matches.empty?
298
+ matches.each { |p| Gretl._request(:delete, "/ports/#{p.port}") }
299
+ matches
300
+ end
301
+
302
+ def wait_for_active(query, timeout: 30, interval: 0.5)
303
+ deadline = Time.now + timeout
304
+ while Time.now < deadline
305
+ Gretl._resolve(query).each { |p| return p if p.active }
306
+ sleep interval
307
+ end
308
+ raise "Port matching #{query.inspect} did not become active within #{timeout}s"
309
+ end
310
+
311
+ def add(port, name: nil, cmd: nil, group: nil, cwd: nil)
312
+ Gretl._ensure_daemon
313
+ uri = URI("#{Gretl::BASE}/ports")
314
+ body = { port: port }
315
+ body[:name] = name if name
316
+ body[:cmd] = cmd if cmd
317
+ body[:group] = group if group
318
+ body[:cwd] = cwd if cwd
319
+ http = Net::HTTP.new(uri.host, uri.port)
320
+ req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
321
+ req.body = body.to_json
322
+ res = http.request(req)
323
+ data = JSON.parse(res.body, symbolize_names: true)
324
+ Gretl::PortInfo.new(**data.slice(*Gretl::PortInfo.members))
325
+ end
326
+
132
327
  def wait_for_inactive(query, timeout: 30, interval: 0.5)
133
328
  deadline = Time.now + timeout
134
329
  while Time.now < deadline
@@ -139,6 +334,6 @@ module Gretl
139
334
  end
140
335
  end
141
336
 
142
- # Default client
337
+ # Default client — token read from GR_TOKEN env var
143
338
  PA = Client.new
144
339
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gretl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gretl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-13 00:00:00.000000000 Z
11
+ date: 2026-05-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: