withruntime 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e403c64b0fd311fd7ba3fb2a9fab6304afda36100ebc1fe0bd30f6b8f82bcece
4
- data.tar.gz: a6eb390df90ab8f676704e8181c535d2dd23a41eebec44efdbf0219f46f675c6
3
+ metadata.gz: 53b4e63218b0587cacbd8e0ad8ec583ba4ce9b32ec0b792208b8f70750b5acd5
4
+ data.tar.gz: 3461a8000fc4817de3f06c6f6f40b6e5d29b49312a4e1f9c9dc94a649e91b8ae
5
5
  SHA512:
6
- metadata.gz: a9994df04fad6ef2af83e9c1bb92e037b42e048bdf2806465d5969e43f7bc4ba9862fcbc77036d61b7d826579f9e0c316e302b05822cf6c326e6739a657ef677
7
- data.tar.gz: de3343c87c44ea8dbed899137c0034de4683f64cb2ff44029ffcfbd6da98652f68d4fae8b53fbc90d3dc00a4ba5305d0239190eb756d8dff95ee54f0c0584043
6
+ metadata.gz: b1bb2aea12c2959b3afcd694ad90124d1c04fcf60201fdece3f5f1264b40bfa3b3f8deec865c5265d806b6e8c389ebacfd4da833648133fdba93b9ece3d7352f
7
+ data.tar.gz: d0238775905bea2408a2d930a402f49a4001bf900c3a8c13acbd511aaf5c486b7b01da8b5f93dfc188e50b696e135b8b92dfd75a7f9c74f5d4dcbdfcce63b668
@@ -33,7 +33,7 @@ module WithRuntime
33
33
  # +timeout+: seconds for each call, retries included (300).
34
34
  # +max_retries+: of transport failures, 429, 502, 503 and 504 (4).
35
35
  # +max_connections+: calls in flight at once; streams do not count (32).
36
- # +wait_for_capacity+: seconds a sandbox create keeps retrying, with the
36
+ # +wait_for_capacity+: seconds a sandbox create, wake or restart keeps retrying, with the
37
37
  # same key and input, when the slots without credit, quota or region are full (120; 0 fails at once).
38
38
  def initialize(api_key: nil, base_url: nil, timeout: 300, max_retries: 4, max_connections: 32,
39
39
  wait_for_capacity: 120, env: ENV, in_sandbox: -> { Credentials.in_runtime_sandbox? })
@@ -104,7 +104,7 @@ module WithRuntime
104
104
  end
105
105
  end
106
106
 
107
- # +runtime.limits+: whether this key is read-only, and what its agent may still spend today.
107
+ # +runtime.limits+: whether this key is read-only, and what its agent may still spend today, plus +account+: the account's limits (limit, label, unit, max, used), or nil.
108
108
  class Limits < Product
109
109
  def get = Record.new(@t.json("GET", "/v1/limits"))
110
110
  end
@@ -19,6 +19,12 @@ module WithRuntime
19
19
  # free, up to +wait_for_capacity+ seconds (the client's, two minutes by default).
20
20
  #
21
21
  # runtime.sandboxes.create(labels: { team: "search" }, timeout_seconds: 900)
22
+ #
23
+ # +spread_from: [id, ...]+ (one to 16 ids of your own sandboxes or snapshots)
24
+ # starts it on a server none of them is on. With no other server that has
25
+ # room, the create raises +no_capacity+ and nothing is made or charged.
26
+ # +sbx.info.placement+ is an opaque tag for its server: the same tag means
27
+ # the same server.
22
28
  def create(wait: true, idempotency_key: nil, wait_for_capacity: nil, **fields)
23
29
  room = wait_for_capacity.nil? ? @t.wait_for_capacity : [0, wait_for_capacity].max
24
30
  info = @t.json("POST", "/v1/sandboxes", body: Fields.body(fields), wait: wait ? 60 : 0,
@@ -118,8 +124,12 @@ module WithRuntime
118
124
 
119
125
  # Carries on a paused sandbox, with its memory and processes;
120
126
  # +timeout_seconds+ is its new time limit, for a sandbox that has one.
121
- def wake(wait: true, timeout_seconds: nil, idempotency_key: nil)
122
- lifecycle("wake", timeout_seconds ? { "timeoutSeconds" => timeout_seconds } : {}, wait, idempotency_key)
127
+ # When its server has no room (+no_capacity+) it waits for room, as +create+
128
+ # does, up to +wait_for_capacity+ seconds (the client's, two minutes by
129
+ # default; 0 fails at once), sending the same idempotency key each time.
130
+ def wake(wait: true, timeout_seconds: nil, idempotency_key: nil, wait_for_capacity: nil)
131
+ lifecycle("wake", timeout_seconds ? { "timeoutSeconds" => timeout_seconds } : {}, wait, idempotency_key,
132
+ room: room_for(wait_for_capacity))
123
133
  end
124
134
 
125
135
  # Moves a sandbox's time limit on, at most an hour ahead of now. A sandbox
@@ -130,7 +140,10 @@ module WithRuntime
130
140
  def set_retention(days, idempotency_key: nil) = lifecycle("retention", { "days" => days }, false, idempotency_key)
131
141
 
132
142
  # Starts a stopped persistent sandbox again from its disk. Memory is not kept.
133
- def restart(wait: true, idempotency_key: nil) = lifecycle("restart", {}, wait, idempotency_key)
143
+ # Waits for room like +wake+ when its server is full.
144
+ def restart(wait: true, idempotency_key: nil, wait_for_capacity: nil)
145
+ lifecycle("restart", {}, wait, idempotency_key, room: room_for(wait_for_capacity))
146
+ end
134
147
 
135
148
  # Changes name, labels, auto_wake, idle_pause_seconds, persistent or
136
149
  # max_total_cost_micros; fields left out stay as they are, and
@@ -203,8 +216,10 @@ module WithRuntime
203
216
  end
204
217
 
205
218
  # Keeps this sandbox's whole machine as a snapshot to start new sandboxes
206
- # from. A running sandbox is paused for the moment it takes, then woken; a
207
- # paused one stays paused. A sandbox with volumes cannot be snapshotted.
219
+ # from. A running sandbox is paused by the server for the moment it takes
220
+ # and woken once the capture is over (one still capturing when the call
221
+ # answers stays paused until it is ready); a paused one stays paused. A
222
+ # sandbox with volumes cannot be snapshotted.
208
223
  def snapshot(name: nil, labels: nil, retention_days: nil, idempotency_key: nil)
209
224
  refresh
210
225
  # Straight after a fork or a wake the sandbox is still resuming, and
@@ -213,15 +228,11 @@ module WithRuntime
213
228
  if %w[resuming starting].include?(state) then wait_for("running")
214
229
  elsif state == "pausing" then wait_for("paused")
215
230
  end
216
- running = state == "running"
217
- pause if running
218
- begin
219
- Record.new(@t.json("POST", path(":snapshot"),
220
- body: Fields.body(name: name, labels: labels, retention_days: retention_days),
221
- wait: 10, key: idempotency_key))
222
- ensure
223
- wake if running
224
- end
231
+ # The server pauses a running sandbox itself: a client pause and wake
232
+ # would be two more round trips.
233
+ Record.new(@t.json("POST", path(":snapshot"),
234
+ body: Fields.body(name: name, labels: labels, retention_days: retention_days),
235
+ wait: 10, key: idempotency_key))
225
236
  end
226
237
 
227
238
  # CPU and memory over a range: 15m, 1h, 6h, 24h, 7d or 30d.
@@ -367,11 +378,13 @@ module WithRuntime
367
378
 
368
379
  private
369
380
 
370
- def lifecycle(verb, body, wait, key)
371
- @info = Record.new(@t.json("POST", path(":#{verb}"), body: body, wait: wait ? 60 : 0, key: key))
381
+ def lifecycle(verb, body, wait, key, room: 0)
382
+ @info = Record.new(@t.json("POST", path(":#{verb}"), body: body, wait: wait ? 60 : 0, key: key, room: room))
372
383
  self
373
384
  end
374
385
 
386
+ def room_for(wait_for_capacity) = wait_for_capacity.nil? ? @t.wait_for_capacity : [0, wait_for_capacity].max
387
+
375
388
  def command_body(command, cwd, env, stdin, timeout)
376
389
  body = command.is_a?(Array) ? { "argv" => command.map(&:to_s) } : { "command" => command.to_s }
377
390
  body["cwd"] = cwd if cwd
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WithRuntime
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: withruntime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Runtime
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-16 00:00:00.000000000 Z
11
+ date: 2026-10-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'Linux sandboxes for agents, and every other Runtime Cloud product: streaming
14
14
  commands and terminals, files and watches, images, durable volumes and backups,