withruntime 0.2.0 → 0.4.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 +4 -4
- data/lib/withruntime/client.rb +1 -1
- data/lib/withruntime/products.rb +1 -1
- data/lib/withruntime/sandbox.rb +45 -30
- data/lib/withruntime/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f471d00ffe082a8a507a514dda86ce4062342e2b5f0e27153309d094a27fb35b
|
|
4
|
+
data.tar.gz: 988ff09e0b418238acb50eba94f81bfa59c579e8103a81bd8b43e551c692f70e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4e8625a16d34f127de6bda5733b2645b733cb7a06ec3af65d87fc48a6b804d17c6d22f03cc657bd3cb64d8c29e3269cc42047aed28b0cf084f25a6cba9a048e
|
|
7
|
+
data.tar.gz: 33311be1f0d80a9ff3fd613acd21a02e0535945271e08af9cdf4b83d1021310479681bb7f07e8acb36663654d130fa29c635dcf796b1489cb9fbeec73d1f9a25
|
data/lib/withruntime/client.rb
CHANGED
|
@@ -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? })
|
data/lib/withruntime/products.rb
CHANGED
|
@@ -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
|
data/lib/withruntime/sandbox.rb
CHANGED
|
@@ -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
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
|
207
|
-
#
|
|
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
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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.
|
|
@@ -230,19 +241,20 @@ module WithRuntime
|
|
|
230
241
|
# Runs a command and returns its exit code and output. A String runs under
|
|
231
242
|
# +bash -c+; an Array runs the program directly, with no shell.
|
|
232
243
|
#
|
|
233
|
-
# +cwd+ (default /workspace), +
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
|
|
244
|
+
# +cwd+ (default /workspace), +user+ ("sandbox", the default, or "root",
|
|
245
|
+
# through the sandbox's passwordless sudo), +env+ (merged over the
|
|
246
|
+
# sandbox's; put secrets here), +stdin+, +timeout+ (seconds, default 60, up
|
|
247
|
+
# to a day; a timeout is a result, not an error), +on_stdout+ and
|
|
248
|
+
# +on_stderr+ (stream the command; the result keeps everything), and
|
|
249
|
+
# +check+ (raise CommandError on a non-zero exit or a timeout).
|
|
250
|
+
def exec(command, cwd: nil, user: nil, env: nil, stdin: nil, timeout: nil, on_stdout: nil, on_stderr: nil,
|
|
239
251
|
check: false, idempotency_key: nil)
|
|
240
252
|
streamed = on_stdout || on_stderr || (timeout && timeout > 60)
|
|
241
253
|
result = if streamed
|
|
242
|
-
collect(exec_stream(command, cwd: cwd, env: env, stdin: stdin, timeout: timeout,
|
|
254
|
+
collect(exec_stream(command, cwd: cwd, user: user, env: env, stdin: stdin, timeout: timeout,
|
|
243
255
|
idempotency_key: idempotency_key), on_stdout, on_stderr)
|
|
244
256
|
else
|
|
245
|
-
body = command_body(command, cwd, env, stdin, timeout)
|
|
257
|
+
body = command_body(command, cwd, env, stdin, timeout, user)
|
|
246
258
|
CommandResult.new(@t.json("POST", path(":exec"), body: body, key: idempotency_key,
|
|
247
259
|
timeout: timeout && (timeout + 60)))
|
|
248
260
|
end
|
|
@@ -254,14 +266,14 @@ module WithRuntime
|
|
|
254
266
|
# Runs a command and yields its events as they happen: start, stdout,
|
|
255
267
|
# stderr, exit (each a Hash). Resumes by itself when the server ends a long
|
|
256
268
|
# stream, so it never drops output. Without a block, an Enumerator.
|
|
257
|
-
def exec_stream(command, cwd: nil, env: nil, stdin: nil, timeout: nil, idempotency_key: nil, &block)
|
|
269
|
+
def exec_stream(command, cwd: nil, user: nil, env: nil, stdin: nil, timeout: nil, idempotency_key: nil, &block)
|
|
258
270
|
unless block
|
|
259
|
-
return enum_for(:exec_stream, command, cwd: cwd, env: env, stdin: stdin, timeout: timeout,
|
|
271
|
+
return enum_for(:exec_stream, command, cwd: cwd, user: user, env: env, stdin: stdin, timeout: timeout,
|
|
260
272
|
idempotency_key: idempotency_key)
|
|
261
273
|
end
|
|
262
274
|
|
|
263
275
|
longest = ApiDefaults::STREAMED_EXEC_TIMEOUT_SECONDS
|
|
264
|
-
body = command_body(command, cwd, env, stdin, timeout || longest).merge("stream" => true)
|
|
276
|
+
body = command_body(command, cwd, env, stdin, timeout || longest, user).merge("stream" => true)
|
|
265
277
|
handed_over = nil
|
|
266
278
|
process_id = nil
|
|
267
279
|
cursor = 0
|
|
@@ -350,8 +362,8 @@ module WithRuntime
|
|
|
350
362
|
# once. It outlives your connection; +process(id)+ gets it back.
|
|
351
363
|
# +pipe_stdin: true+ keeps input open for +write+; +pty: { cols:, rows: }+
|
|
352
364
|
# gives it a terminal.
|
|
353
|
-
def spawn(command, cwd: nil, env: nil, timeout: nil, stdin: nil, pipe_stdin: false, pty: nil, idempotency_key: nil)
|
|
354
|
-
body = command_body(command, cwd, env, pipe_stdin ? nil : stdin, timeout)
|
|
365
|
+
def spawn(command, cwd: nil, user: nil, env: nil, timeout: nil, stdin: nil, pipe_stdin: false, pty: nil, idempotency_key: nil)
|
|
366
|
+
body = command_body(command, cwd, env, pipe_stdin ? nil : stdin, timeout, user)
|
|
355
367
|
body["stdinMode"] = "pipe" if pipe_stdin
|
|
356
368
|
body["pty"] = Fields.body(pty) if pty
|
|
357
369
|
SandboxProcess.new(self, @t.json("POST", path("/processes"), body: body, key: idempotency_key))
|
|
@@ -367,14 +379,17 @@ module WithRuntime
|
|
|
367
379
|
|
|
368
380
|
private
|
|
369
381
|
|
|
370
|
-
def lifecycle(verb, body, wait, key)
|
|
371
|
-
@info = Record.new(@t.json("POST", path(":#{verb}"), body: body, wait: wait ? 60 : 0, key: key))
|
|
382
|
+
def lifecycle(verb, body, wait, key, room: 0)
|
|
383
|
+
@info = Record.new(@t.json("POST", path(":#{verb}"), body: body, wait: wait ? 60 : 0, key: key, room: room))
|
|
372
384
|
self
|
|
373
385
|
end
|
|
374
386
|
|
|
375
|
-
def
|
|
387
|
+
def room_for(wait_for_capacity) = wait_for_capacity.nil? ? @t.wait_for_capacity : [0, wait_for_capacity].max
|
|
388
|
+
|
|
389
|
+
def command_body(command, cwd, env, stdin, timeout, user)
|
|
376
390
|
body = command.is_a?(Array) ? { "argv" => command.map(&:to_s) } : { "command" => command.to_s }
|
|
377
391
|
body["cwd"] = cwd if cwd
|
|
392
|
+
body["user"] = user.to_s if user
|
|
378
393
|
body["env"] = env.to_h { |key, value| [key.to_s, value.to_s] } if env
|
|
379
394
|
body["stdinBase64"] = [stdin.to_s.b].pack("m0") unless stdin.nil?
|
|
380
395
|
body["timeoutMs"] = (timeout * 1000).round if timeout
|
data/lib/withruntime/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.4.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-
|
|
11
|
+
date: 2026-10-18 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,
|