pinesandbox 0.3.18 → 0.3.19
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/LICENSE +201 -0
- data/README.md +159 -157
- data/lib/pinesandbox/adapters/coordinator_adapter.rb +99 -74
- data/lib/pinesandbox/agent_events.rb +2 -2
- data/lib/pinesandbox/agent_turn.rb +24 -2
- data/lib/pinesandbox/attach_credentials_source.rb +13 -13
- data/lib/pinesandbox/bind_hpke.rb +4 -4
- data/lib/pinesandbox/client.rb +59 -36
- data/lib/pinesandbox/computer.rb +108 -108
- data/lib/pinesandbox/computer_binding.rb +14 -10
- data/lib/pinesandbox/control_plane.rb +10 -10
- data/lib/pinesandbox/error_taxonomy.rb +1 -0
- data/lib/pinesandbox/errors.rb +56 -76
- data/lib/pinesandbox/models/artifact.rb +1 -1
- data/lib/pinesandbox/operations.rb +0 -3
- data/lib/pinesandbox/{runtime_details.rb → sandbox_details.rb} +4 -4
- data/lib/pinesandbox/{runtime_handle.rb → sandbox_handle.rb} +14 -19
- data/lib/pinesandbox/session.rb +22 -2
- data/lib/pinesandbox/spec_version_middleware.rb +1 -1
- data/lib/pinesandbox/version.rb +1 -1
- data/lib/pinesandbox/zone.rb +8 -11
- data/lib/pinesandbox.rb +2 -2
- data/pinesandbox.gemspec +5 -3
- metadata +8 -6
data/README.md
CHANGED
|
@@ -1,36 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pine Computer Ruby SDK
|
|
2
2
|
|
|
3
3
|
`pinesandbox` is the Ruby server SDK for Pine Computers. Create and reconnect
|
|
4
4
|
Computers, run agent tasks, manage files, and authorize desktop access from
|
|
5
5
|
your backend. Stop/Start restores saved cookies, learned skills, and localStorage.
|
|
6
6
|
Pause/Resume retains live tabs, sessions, and memory when enabled for your project.
|
|
7
7
|
|
|
8
|
-
See the [integration guide](https://
|
|
8
|
+
See the [integration guide](https://console.pinecomputer.io/docs/integration) for setup,
|
|
9
9
|
authentication, and examples. This README covers the Ruby API.
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
the bundle from your `Gemfile`:
|
|
16
|
-
|
|
17
|
-
```ruby
|
|
18
|
-
gem "pinesandbox", path: "vendor/pinesandbox-ruby-sdk-<version>"
|
|
13
|
+
```sh
|
|
14
|
+
bundle add pinesandbox
|
|
19
15
|
```
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
[
|
|
26
|
-
for
|
|
17
|
+
Bundler pins it to the latest release's compatibility line (`"~> 0.3.<patch>"`), so
|
|
18
|
+
`bundle update` takes later patches but not a new minor version. Using it needs a project key (`pk_…`) from the
|
|
19
|
+
[Console](https://console.pinecomputer.io). Pine Computer is in beta:
|
|
20
|
+
[request access](https://pinecomputer.io/#access) if you don't have one yet. Each
|
|
21
|
+
release is also an archive on the [Ruby SDK page](https://console.pinecomputer.io/sdks/ruby),
|
|
22
|
+
for vendoring.
|
|
27
23
|
|
|
28
24
|
The current beta SDK is on the `0.3` compatibility line.
|
|
29
25
|
|
|
30
26
|
## Create a Computer
|
|
31
27
|
|
|
32
|
-
A **Computer** is the identity and saved browser state you keep. A **
|
|
33
|
-
|
|
28
|
+
A **Computer** is the identity and saved browser state you keep. A **session** is
|
|
29
|
+
work inside the running Computer.
|
|
30
|
+
[Terms](https://console.pinecomputer.io/docs/how-a-computer-works#terms) defines
|
|
31
|
+
the rest.
|
|
34
32
|
|
|
35
33
|
The examples use `store` for your application's encrypted database; it is not an
|
|
36
34
|
SDK object. Save credentials **before** Create, then save the returned binding.
|
|
@@ -49,7 +47,7 @@ store.save_credentials(credentials.id, credentials.to_json)
|
|
|
49
47
|
computer = client.create_computer(credentials)
|
|
50
48
|
store.save_binding(computer.id, computer.binding.to_json)
|
|
51
49
|
|
|
52
|
-
session = computer.create_session(
|
|
50
|
+
session = computer.create_session(browser: true) # Pine picks the name: save session.name
|
|
53
51
|
session.create_tab(url: "https://example.com")
|
|
54
52
|
```
|
|
55
53
|
|
|
@@ -58,11 +56,12 @@ The two records have different lifetimes:
|
|
|
58
56
|
| Save | When | Used for |
|
|
59
57
|
|---|---|---|
|
|
60
58
|
| `credentials.to_json` | Before Create; again if you change keys | Starting this Computer again and decrypting its saved state |
|
|
61
|
-
| `computer.binding.to_json` | After each successful Create or Start | Reconnecting to
|
|
59
|
+
| `computer.binding.to_json` | After each successful Create or Start | Reconnecting to the running Computer from any backend |
|
|
62
60
|
|
|
63
61
|
Both contain secrets. Keep them encrypted on your backend, never in a browser.
|
|
64
62
|
For a one-off script, `client.with_computer { |computer| … }` creates and stops the
|
|
65
|
-
Computer for you, raising if Stop cannot be confirmed.
|
|
63
|
+
Computer for you, raising if Stop cannot be confirmed. When the block raises, its
|
|
64
|
+
error propagates and an unconfirmed Stop is only warned about.
|
|
66
65
|
|
|
67
66
|
## Reuse it from another backend
|
|
68
67
|
|
|
@@ -75,12 +74,13 @@ state = computer.status # e.g. :running, :paused, :resuming
|
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
Reconnect reconstructs a handle locally. It makes no request, creates nothing,
|
|
78
|
-
and does not replay work. Its next call
|
|
79
|
-
backend has since
|
|
80
|
-
your existing Computer record. Save a replacement
|
|
81
|
-
(or it is the identical binding); a delayed reply
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
and does not replay work. Its next call goes to the Computer sandbox the binding
|
|
78
|
+
names, even if another backend has since started the Computer again. Store the binding
|
|
79
|
+
revision alongside its JSON in your existing Computer record. Save a replacement
|
|
80
|
+
only if its revision is newer (or it is the identical binding); a delayed reply
|
|
81
|
+
must not overwrite a newer one.
|
|
82
|
+
After confirmed Stop, clear the binding only if the stored revision and sandbox
|
|
83
|
+
id still match the one stopped. Keep that revision and the durable credentials.
|
|
84
84
|
Session records belong to a binding revision and cannot be reused after Start.
|
|
85
85
|
|
|
86
86
|
To continue a session, also save its name and token when you create it, then use
|
|
@@ -93,10 +93,10 @@ worker can wait for existing work rather than submit it again. See
|
|
|
93
93
|
|
|
94
94
|
| Intent | Call | Result |
|
|
95
95
|
|---|---|---|
|
|
96
|
-
| Finish using compute | `computer.stop` | Saves supported state,
|
|
97
|
-
| Start a stopped Computer | `client.start_computer(credentials)` |
|
|
98
|
-
| Keep live memory for later | `computer.pause` |
|
|
99
|
-
| Continue paused work | `computer.resume` |
|
|
96
|
+
| Finish using compute | `computer.stop` | Saves supported state, shuts the Computer down and ends its sessions |
|
|
97
|
+
| Start a stopped Computer | `client.start_computer(credentials)` | Runs the Computer from its saved state; create new sessions |
|
|
98
|
+
| Keep live memory for later | `computer.pause` | Keeps the Computer and its sessions in memory; requires project opt-in |
|
|
99
|
+
| Continue paused work | `computer.resume` | Brings back the paused Computer and its sessions |
|
|
100
100
|
| Observe after a connection failure | `computer.status` | Reads state without changing or renewing it |
|
|
101
101
|
|
|
102
102
|
```ruby
|
|
@@ -107,13 +107,14 @@ computer = client.start_computer(credentials)
|
|
|
107
107
|
store.save_binding(computer.id, computer.binding.to_json)
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Stop/Start is also how an application
|
|
110
|
+
Stop/Start is also how an application updates a Computer. An SDK upgrade or
|
|
111
111
|
Pine image rollout does not replace a running Computer. Start can receive a
|
|
112
112
|
compatible older warm revision during rollout; it does not guarantee a build.
|
|
113
113
|
|
|
114
|
-
`attach_computer` remains an alias for Start. **Use Reconnect for
|
|
115
|
-
|
|
116
|
-
browser state, not live tabs, session files or page
|
|
114
|
+
`attach_computer` remains an alias for Start. **Use Reconnect for a Computer
|
|
115
|
+
that's running or paused.** A failed Resume does not fall back to Start.
|
|
116
|
+
Stop/Start restores saved browser state, not live tabs, session files or page
|
|
117
|
+
memory. Export files first.
|
|
117
118
|
[Persistence and teardown](#persistence--teardown) covers completion, recovery and
|
|
118
119
|
permanent deletion.
|
|
119
120
|
|
|
@@ -121,19 +122,17 @@ permanent deletion.
|
|
|
121
122
|
|
|
122
123
|
`timeout:` is the inactivity window (default five minutes), not an HTTP request
|
|
123
124
|
timeout. Work renews it; reading status, watching events and other observation do
|
|
124
|
-
not. It starts
|
|
125
|
+
not. It starts when the Computer starts, so a one-minute window can expire during
|
|
126
|
+
slow setup.
|
|
125
127
|
|
|
126
|
-
Projects
|
|
127
|
-
24 hours of idle time and seven days of maximum
|
|
128
|
-
limits can be higher.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
timeout above the allowance, including the SDK default, is rejected. Before
|
|
132
|
-
enrolling a project, check its integrations' requested timeouts and handling of
|
|
133
|
-
maximum age. Deployment alone does not enroll projects or alter existing runtimes.
|
|
128
|
+
Projects have a lifetime policy unless Pine removed it for one. The standard
|
|
129
|
+
allowance is one minute to 24 hours of idle time and seven days of maximum age;
|
|
130
|
+
approved project limits can be higher. A project without one has no maximum age
|
|
131
|
+
and no idle maximum. A running Computer keeps the terms it started with.
|
|
132
|
+
A requested timeout above the allowance, including the SDK default, is rejected.
|
|
134
133
|
|
|
135
134
|
```ruby
|
|
136
|
-
details = computer.
|
|
135
|
+
details = computer.sandbox_details
|
|
137
136
|
if details.lifecycle
|
|
138
137
|
updated = computer.set_idle_timeout(seconds: 3600)
|
|
139
138
|
puts updated.lifecycle.idle_timeout_seconds
|
|
@@ -141,67 +140,71 @@ if details.lifecycle
|
|
|
141
140
|
end
|
|
142
141
|
```
|
|
143
142
|
|
|
144
|
-
For
|
|
145
|
-
or on the next Resume while paused. It cannot extend maximum age or
|
|
146
|
-
retention.
|
|
147
|
-
|
|
143
|
+
For a Computer started after enrollment, changing idle timeout takes effect now
|
|
144
|
+
while running, or on the next Resume while paused. It cannot extend maximum age or
|
|
145
|
+
snapshot retention. Computers started before enrollment keep their previous expiry
|
|
146
|
+
clock while paused; those started after use snapshot retention and maximum age. An
|
|
147
|
+
identical value does nothing.
|
|
148
148
|
After a conflict or lost response,
|
|
149
|
-
read `
|
|
149
|
+
read `sandbox_details` before deciding whether to edit again.
|
|
150
150
|
|
|
151
151
|
Maximum age includes paused time. Graceful Stop begins twelve minutes before the
|
|
152
152
|
hard deadline; expiry is not proof that a final save succeeded. Plan Stop/Start
|
|
153
153
|
at a safe point before that deadline when you need uninterrupted work.
|
|
154
154
|
|
|
155
|
-
`
|
|
156
|
-
terms and its opaque `runtime_revision`. Missing lifecycle means the
|
|
157
|
-
|
|
158
|
-
allowances for future
|
|
155
|
+
`sandbox_details` describes the current Computer sandbox, including optional
|
|
156
|
+
lifecycle terms and its opaque `runtime_revision`. Missing lifecycle means the Computer
|
|
157
|
+
was started without a project lifetime policy. `client.project_policy` optionally reads
|
|
158
|
+
allowances for future starts; ordinary operations do not require that call.
|
|
159
159
|
|
|
160
|
-
`ready_timeout:` bounds
|
|
161
|
-
following handshake. They do not change
|
|
162
|
-
`timeout: 28_800, ready_timeout: 120` requests
|
|
163
|
-
two-minute readiness wait. These waits are
|
|
164
|
-
cleanup; see the
|
|
160
|
+
`ready_timeout:` bounds waiting for the Computer to start and become ready, and
|
|
161
|
+
`bind_ready_timeout:` bounds the following handshake. They do not change the
|
|
162
|
+
Computer's expiry. For example, `timeout: 28_800, ready_timeout: 120` requests
|
|
163
|
+
eight hours of idle time with a two-minute readiness wait. These waits are
|
|
164
|
+
separate from transport retries and cleanup; see the
|
|
165
|
+
[integration guide](https://console.pinecomputer.io/docs/integration).
|
|
165
166
|
|
|
166
167
|
## Handle failures without replacing live work
|
|
167
168
|
|
|
168
|
-
- A network error or `alive? == false` does not prove the
|
|
169
|
-
Read `status`; its `NotFoundError` confirms
|
|
170
|
-
data-plane `
|
|
169
|
+
- A network error or `alive? == false` does not prove the Computer has stopped.
|
|
170
|
+
Read `status`; its `NotFoundError` confirms that it has. A
|
|
171
|
+
data-plane `SandboxGoneError` alone does not: paused/restoring Computers can
|
|
171
172
|
temporarily have no route.
|
|
172
|
-
- `
|
|
173
|
-
|
|
173
|
+
- `SandboxRetainedError` (a `BindError`) means Start found the Computer still
|
|
174
|
+
running or paused.
|
|
175
|
+
Match its `sandbox_id` to your saved binding, or reload the latest binding from
|
|
174
176
|
your database. Reconnect, Resume or finish Stop as appropriate; an ID alone
|
|
175
177
|
cannot reconstruct a missing token.
|
|
176
178
|
- A Stop timeout means completion is unconfirmed, not canceled. Keep the binding
|
|
177
179
|
and observe or retry Stop. Older servers report `StopSaveError` for a failed or
|
|
178
|
-
unconfirmed save; current servers still
|
|
180
|
+
unconfirmed save; current servers still shut the Computer down.
|
|
179
181
|
- Serialize Create/Start per Computer in your application. On
|
|
180
182
|
`BindingRevisionConflictError`, reload the winner's saved binding instead of
|
|
181
183
|
retrying with a higher revision.
|
|
182
184
|
- If a registered Computer's Start reply is lost before saving its binding,
|
|
183
185
|
serialize recovery, call `client.stop_computer(id)`, then Start separately with
|
|
184
|
-
the saved credentials. This
|
|
186
|
+
the saved credentials. This stops the Computer whose token was lost.
|
|
185
187
|
- An interrupted first Create can fail before registering the Computer. Stop-by-ID
|
|
186
188
|
then returns true, but Start raises `UnknownComputerError`. For that initial
|
|
187
189
|
creation, retry Create with your saved credentials after confirming the project
|
|
188
190
|
and ID are correct and the identity was not deleted. Do not treat every unknown
|
|
189
|
-
ID as permission to create.
|
|
190
|
-
|
|
191
|
+
ID as permission to create. A Computer sandbox started before registration may
|
|
192
|
+
run until it expires; Stop-by-ID cannot find it.
|
|
191
193
|
- On `AttachAuthorizationCommittedError`, registration has succeeded. Retain its
|
|
192
194
|
non-nil credentials and reconcile the registered Computer before Start. Private
|
|
193
195
|
keys cannot be recovered from an ID.
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
Retries within one call reuse its create ID. A fresh call is a new attempt. A
|
|
198
|
+
failed attach shuts down the Computer sandbox it started, on a best-effort
|
|
199
|
+
basis; after a process crash, that Computer sandbox runs until it expires.
|
|
200
|
+
Longer requested timeouts can leave abandoned compute running longer. See
|
|
201
|
+
[Errors](#errors) for the error types.
|
|
199
202
|
|
|
200
203
|
## Stored credentials and older integrations
|
|
201
204
|
|
|
202
205
|
The project API key stays on your backend. Computer credentials contain the state
|
|
203
206
|
key, current and retained capture keys, prior state keys and ephemeral mode.
|
|
204
|
-
Bindings contain the
|
|
207
|
+
Bindings contain the sandbox ID, Computer token and binding revision. The SDK
|
|
205
208
|
handles short-lived project and attach authorization internally.
|
|
206
209
|
|
|
207
210
|
`computer.credentials` returns an independent copy. Credentials and bindings use
|
|
@@ -219,8 +222,8 @@ For integrations using older storage or field-based methods:
|
|
|
219
222
|
- Starting from a credentials value requires custom credential providers to
|
|
220
223
|
implement `current_binding` and `credentials`. Bare callables still support
|
|
221
224
|
Create and the field-based Attach form with an explicit binding revision.
|
|
222
|
-
- `control_plane` and
|
|
223
|
-
Computer methods above.
|
|
225
|
+
- `control_plane` and the handles it returns are SDK internals. Integrations use
|
|
226
|
+
the Computer methods above.
|
|
224
227
|
|
|
225
228
|
## Sessions
|
|
226
229
|
|
|
@@ -312,7 +315,7 @@ same.
|
|
|
312
315
|
|
|
313
316
|
`run` raises `409` if a turn is already running, or a person or your own agent
|
|
314
317
|
holds the drive slot. Routes: `/v2/sessions/{name}/agent/*` in the
|
|
315
|
-
[Computer API reference](https://
|
|
318
|
+
[Computer API reference](https://console.pinecomputer.io/spec/computer).
|
|
316
319
|
|
|
317
320
|
### Activity
|
|
318
321
|
|
|
@@ -350,11 +353,11 @@ observation; do not guess from nearby events. A text-only update leaves the last
|
|
|
350
353
|
captured screen and its original attribution visible. Keep historical selection
|
|
351
354
|
separate from following new frames; the event ID identifies each image for replay.
|
|
352
355
|
|
|
353
|
-
This SDK requires a Computer
|
|
356
|
+
This SDK requires a Computer that serves the public V2 agent contract. There is
|
|
354
357
|
no fallback to native trace. Activity includes progress, questions and results;
|
|
355
358
|
native commands, reasoning and provider metadata are internal-only. Upgrade
|
|
356
|
-
agent consumers before
|
|
357
|
-
reject ordinary bindings.
|
|
359
|
+
agent consumers before you restart their Computers on the new release: its legacy
|
|
360
|
+
agent routes reject ordinary bindings.
|
|
358
361
|
|
|
359
362
|
Paged history uses the same typed events as streaming. Scope every saved cursor
|
|
360
363
|
to its Task; after a session replacement, discover the new Task before replaying
|
|
@@ -387,47 +390,42 @@ drive.upload_file("input[type=file]", "resume.pdf") # set a file input (deposi
|
|
|
387
390
|
|
|
388
391
|
`observe` is read-only (allowed even while Pine's agent runs — you can watch);
|
|
389
392
|
`computer_use` / `upload_file` take the drive slot (`409` if Pine's agent or a person
|
|
390
|
-
holds it).
|
|
391
|
-
|
|
392
|
-
works with session files. It cannot invoke Pine's private browser implementation.
|
|
393
|
-
Raw CDP is an image-owned runtime transport and is intentionally absent from
|
|
394
|
-
the SDK surface. Tab helpers
|
|
393
|
+
holds it). Shell commands run ordinary programs and work with the session's
|
|
394
|
+
files. Tab helpers
|
|
395
395
|
(`create_tab`/`tabs`/`patch_tab`/`close_tab`), `focus!`, and `epoch` round out the manual
|
|
396
396
|
surface. Public routes are defined by the
|
|
397
|
-
[Computer API reference](https://
|
|
397
|
+
[Computer API reference](https://console.pinecomputer.io/spec/computer).
|
|
398
398
|
|
|
399
|
-
## Files
|
|
399
|
+
## Files
|
|
400
400
|
|
|
401
|
-
A session has a **workdir** (where `exec` runs and the agent works)
|
|
402
|
-
**attachments** root (files you hand the agent),
|
|
403
|
-
|
|
401
|
+
A session has a **workdir** (where `exec` runs and the agent works) and an inbound
|
|
402
|
+
**attachments** root (files you hand the agent), which appears inside the workdir as
|
|
403
|
+
`attachments/`.
|
|
404
404
|
|
|
405
405
|
```ruby
|
|
406
406
|
# Read the workdir (what the agent produced / your shell wrote):
|
|
407
407
|
session.list_files(pattern: "*.csv") # => [FileEntry, ...]
|
|
408
408
|
session.read_file("out/report.csv") # => bytes (binary encoding)
|
|
409
|
+
session.open_file("out/export.zip") { |chunk| io.write(chunk) } # stream it without buffering
|
|
409
410
|
|
|
410
|
-
#
|
|
411
|
-
#
|
|
412
|
-
# read_file-able (read_file serves the workdir).
|
|
411
|
+
# Give the agent an input file. It lands in attachments/ (readable as
|
|
412
|
+
# read_file("attachments/brief.md")), and the agent sees it when its next turn starts.
|
|
413
413
|
session.write_file("brief.md", File.read("brief.md"))
|
|
414
414
|
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
session.
|
|
419
|
-
|
|
420
|
-
session.zip_artifacts # => a .zip of all artifacts (bytes)
|
|
421
|
-
session.open_artifact(art.id) { |chunk| io.write(chunk) } # stream it without buffering
|
|
415
|
+
# The files an ended turn produced, by workdir path:
|
|
416
|
+
turn.result.artifacts.each do |file|
|
|
417
|
+
path = file.fetch("relative_path")
|
|
418
|
+
save(path, session.read_file(path))
|
|
419
|
+
end
|
|
422
420
|
```
|
|
423
421
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
`/
|
|
430
|
-
[Computer API reference](https://
|
|
422
|
+
A turn's result lists its files by `relative_path` in the workdir, with size, SHA-256
|
|
423
|
+
and media type. They aren't copies: read them when the turn ends, because a later turn
|
|
424
|
+
or shell command can change them. A browser download isn't one of them; to get one
|
|
425
|
+
back, ask for it in the goal, and the agent copies it into the workdir and lists it.
|
|
426
|
+
Without a block, `open_file` returns an Enumerator of the chunks. Session files last
|
|
427
|
+
until the Computer stops; save what you need first. Routes: `/files` in the
|
|
428
|
+
[Computer API reference](https://console.pinecomputer.io/spec/computer).
|
|
431
429
|
|
|
432
430
|
## Skills — `learn` / `teach` (and when each blocks)
|
|
433
431
|
|
|
@@ -477,8 +475,8 @@ gate: nothing learned reaches another agent until you activate it.
|
|
|
477
475
|
|
|
478
476
|
```ruby
|
|
479
477
|
computer.checkpoint # synchronous state save (content-hash dedup) — before a risky step
|
|
480
|
-
computer.stop # save cookies, learned skills and localStorage, then
|
|
481
|
-
computer.kill #
|
|
478
|
+
computer.stop # save cookies, learned skills and localStorage, then shut the Computer down
|
|
479
|
+
computer.kill # shut the Computer down without waiting for a save
|
|
482
480
|
client.delete_computer(computer) # PERMANENT: kill if attached, then delete id + saved state
|
|
483
481
|
```
|
|
484
482
|
|
|
@@ -487,10 +485,10 @@ client.delete_computer(computer) # PERMANENT: kill if attached, then delete id
|
|
|
487
485
|
state is purged after 24 hours, and the id is never reusable. Deleting an unknown,
|
|
488
486
|
already-deleted, or other project's id also returns `true`. There is no undelete.
|
|
489
487
|
|
|
490
|
-
`stop` submits graceful Stop to
|
|
491
|
-
(`wait_timeout:`) for
|
|
488
|
+
`stop` submits graceful Stop to Pine's controller, then waits up to 390s
|
|
489
|
+
(`wait_timeout:`) for the Computer to shut down. The controller tries to save supported state
|
|
492
490
|
before deletion and continues if the SDK process disappears. Saving is best effort:
|
|
493
|
-
a save that fails or is not confirmed by the deadline still
|
|
491
|
+
a save that fails or is not confirmed by the deadline still shuts the Computer down,
|
|
494
492
|
and `stop` returns true; the next Start restores the last saved state. Reconstruct
|
|
495
493
|
the saved handle with `reconnect_computer` to observe or join the same operation from
|
|
496
494
|
another backend. Only an older server keeps the source and raises
|
|
@@ -498,49 +496,49 @@ another backend. Only an older server keeps the source and raises
|
|
|
498
496
|
failure from an unconfirmed outcome after the Computer's services restart.
|
|
499
497
|
A `false` return means completion is unconfirmed. Keep the saved binding for retry.
|
|
500
498
|
After Stop returns true, clear the stored binding only if its revision and
|
|
501
|
-
|
|
499
|
+
sandbox id still match the handle stopped; a concurrent Start may have replaced it.
|
|
502
500
|
Keep the revision fence, identity, state key and capture keypairs for future Start. Ephemeral
|
|
503
|
-
bindings skip persistence;
|
|
501
|
+
bindings skip persistence; Pine's binding record decides this.
|
|
504
502
|
|
|
505
|
-
If a Start response was lost before saving the
|
|
506
|
-
`client.stop_computer(id)` to stop the
|
|
503
|
+
If a Start response was lost before saving the Computer token, use
|
|
504
|
+
`client.stop_computer(id)` to stop the Computer with project
|
|
507
505
|
authorization alone, then call Start separately. This lookup selects one binding;
|
|
508
506
|
it never follows a concurrent newer binding. Private keys remain necessary for
|
|
509
507
|
Start and cannot be recovered from the ID.
|
|
510
508
|
|
|
511
|
-
A true result proves
|
|
512
|
-
not prove a final save
|
|
513
|
-
can still be running. Start restores only the latest committed
|
|
514
|
-
learned skills and compatible localStorage survive supported
|
|
515
|
-
tabs, session files, page memory and IndexedDB do not.
|
|
509
|
+
A true result proves the Computer has shut down or was already gone.
|
|
510
|
+
It does not prove a final save when the Computer had already gone, and provider
|
|
511
|
+
snapshot cleanup can still be running. Start restores only the latest committed
|
|
512
|
+
state. Cookies, learned skills and compatible localStorage survive supported
|
|
513
|
+
saved-state recovery; tabs, session files, page memory and IndexedDB do not.
|
|
516
514
|
|
|
517
|
-
`start_computer` reads the current binding revision once, checks that the
|
|
518
|
-
|
|
515
|
+
`start_computer` reads the current binding revision once, checks that the
|
|
516
|
+
Computer has stopped, waiting for an already accepted shutdown if needed,
|
|
519
517
|
and creates a new binding. A conflict stays a conflict;
|
|
520
518
|
it never adopts a newer revision and displaces another worker's winner. For a
|
|
521
|
-
recorded failed
|
|
522
|
-
`client.recover_computer(
|
|
523
|
-
Recovery accepts possible unsaved-state loss,
|
|
524
|
-
with a version precondition, waits up to 90s for
|
|
525
|
-
refuses healthy running
|
|
526
|
-
|
|
519
|
+
Computer you recorded as failed, explicitly call
|
|
520
|
+
`client.recover_computer(credentials)`.
|
|
521
|
+
Recovery accepts possible unsaved-state loss, shuts the failed Computer down
|
|
522
|
+
with a version precondition, waits up to 90s for it to shut down, then Starts. It
|
|
523
|
+
refuses healthy running or paused Computers and an unresolved capture timeout. If
|
|
524
|
+
shutting it down times out, retry later; nothing new has been started. `kill` remains
|
|
527
525
|
an unconditional destructive operation.
|
|
528
526
|
|
|
529
|
-
This SDK's retryable
|
|
530
|
-
and a compatible Computer
|
|
527
|
+
This SDK's retryable Start and server-owned Stop require the updated backend
|
|
528
|
+
and a compatible Computer; neither requires Pause opt-in. Existing
|
|
531
529
|
Computer create, capture/Finish and delete calls remain supported for applications
|
|
532
530
|
keeping their prior SDK. Complete the backend rollout before upgrading, including
|
|
533
531
|
the conditional-release support used by recovery. A pool image update does not
|
|
534
532
|
upgrade an already-running Computer: stop old-image bindings with the prior SDK
|
|
535
533
|
before upgrading their caller. Integrations using the removed generic
|
|
536
|
-
public
|
|
534
|
+
public lifecycle operations must migrate to the Computer contract; the private Ruby
|
|
537
535
|
`ControlPlane#create` adapter is removed as well.
|
|
538
536
|
|
|
539
|
-
For projects opted in to Pause/Resume on
|
|
537
|
+
For projects opted in to Pause/Resume, on Computers that support snapshots:
|
|
540
538
|
|
|
541
539
|
```ruby
|
|
542
540
|
computer.pause(timeout: 360) # waits for capture and confirmed source stop
|
|
543
|
-
computer.resume(timeout: 360) # waits
|
|
541
|
+
computer.resume(timeout: 360) # waits until the Computer is restored and reachable
|
|
544
542
|
```
|
|
545
543
|
|
|
546
544
|
Pause is disabled by default. Pine enables it for a project on request;
|
|
@@ -548,41 +546,45 @@ refresh the project token after it is enabled. Disabling the
|
|
|
548
546
|
feature prevents new pauses but preserves Resume and Stop for retained memory.
|
|
549
547
|
Ordinary Stop/Start remains available without the opt-in.
|
|
550
548
|
|
|
551
|
-
The same
|
|
552
|
-
A pool image update does not change
|
|
549
|
+
The same Computer handle, session capabilities and retained process memory remain.
|
|
550
|
+
A pool image update does not change a paused Computer. Calls include acceptance
|
|
553
551
|
and completion in one bounded timeout. A timeout leaves the outcome unconfirmed;
|
|
554
|
-
retry against the retained handle. The controller restores the
|
|
555
|
-
|
|
552
|
+
retry against the retained handle. The controller restores the paused Computer
|
|
553
|
+
before Stop can save it. Snapshot retention and admitted maximum age still apply;
|
|
554
|
+
legacy expiry is unchanged.
|
|
556
555
|
|
|
557
556
|
Do not rescue a failed `resume` by automatically calling `start_computer`.
|
|
558
557
|
Retrying Resume keeps the existing memory-recovery intent; Start explicitly
|
|
559
558
|
accepts recovery from the last saved state with new sessions and possible loss
|
|
560
559
|
of unsaved work. An unavailable status or timeout alone does not prove that
|
|
561
|
-
memory is lost. The [Computer API reference](https://
|
|
560
|
+
memory is lost. The [Computer API reference](https://console.pinecomputer.io/spec/computer)
|
|
562
561
|
defines the observed states and recovery choices: retry Resume, refresh the
|
|
563
562
|
status, or Start from saved state.
|
|
564
563
|
|
|
565
564
|
## Errors
|
|
566
565
|
|
|
566
|
+
[Troubleshooting](https://console.pinecomputer.io/docs/troubleshooting) says what
|
|
567
|
+
each common error means and how to fix it.
|
|
568
|
+
|
|
567
569
|
Three typed families, one per credential exchange (see `lib/pinesandbox/errors.rb`):
|
|
568
570
|
|
|
569
|
-
|
|
570
|
-
wire diagnostics and original `ApiError` cause. Errors about a
|
|
571
|
-
live Computer remain distinct, including
|
|
572
|
-
`PineSandbox::ControlNotHeld`.
|
|
571
|
+
A Computer that can no longer be reached raises `PineSandbox::SandboxGoneError`;
|
|
572
|
+
it preserves the wire diagnostics and original `ApiError` cause. Errors about a
|
|
573
|
+
resource on a live Computer remain distinct, including
|
|
574
|
+
`PineSandbox::SessionNotFound` and `PineSandbox::ControlNotHeld`.
|
|
573
575
|
|
|
574
576
|
- **`BindError`** — `Computer#attach` outcomes: `BindAuthError`,
|
|
575
|
-
`ComputerAlreadyAttachedError`, `
|
|
577
|
+
`ComputerAlreadyAttachedError`, `SandboxUnusableError`, `SavedStateUnavailableError`,
|
|
576
578
|
`BindTimeoutError`; plus `TokenRejectedError` on a post-bind 401 for the
|
|
577
|
-
Computer token (`ct_` not recognized — on a
|
|
578
|
-
`binding_auth_lost`; re-attach only when the
|
|
579
|
+
Computer token (`ct_` not recognized — on a running Computer it is
|
|
580
|
+
`binding_auth_lost`; re-attach only when the Computer is confirmed stopped). A 401
|
|
579
581
|
for a session token is an ordinary `UnauthorizedError`. If `create_computer` raises
|
|
580
|
-
`AttachAuthorizationCommittedError`, retain its non-nil `#credentials` and inspect `#
|
|
581
|
-
whether to stop or recover the failed
|
|
582
|
+
`AttachAuthorizationCommittedError`, retain its non-nil `#credentials` and inspect `#sandbox_id` before deciding
|
|
583
|
+
whether to stop or recover the failed Computer. Credential inspection redacts private material.
|
|
582
584
|
- **`AttachCredentialsError`** — the portal attach-credentials flow:
|
|
583
585
|
`UnknownComputerError` (404 — deleted or cross-project),
|
|
584
586
|
`ProjectAccessDenied` (403), and `BindingRevisionConflictError` (412 — another start changed the binding; `#current_binding_revision` /
|
|
585
|
-
`#
|
|
587
|
+
`#current_sandbox_id` identify the winner. Do not retry automatically). A bad `pk_` (401),
|
|
586
588
|
rate limit (429), and server errors are the base `AttachCredentialsError`.
|
|
587
589
|
Inspect the stable problem `#problem_type` and optional safe `#reason`; raw
|
|
588
590
|
internal exceptions are never part of the API contract.
|
|
@@ -591,9 +593,9 @@ live Computer remain distinct, including `PineSandbox::SessionNotFound` and
|
|
|
591
593
|
A `Retry-After` longer than the mint's 10-second backoff cap ends the mint at
|
|
592
594
|
once with that error instead of retrying early.
|
|
593
595
|
|
|
594
|
-
The old names `
|
|
595
|
-
`BrokerUnreachableError
|
|
596
|
-
|
|
596
|
+
The old names `PodPoisonedError` (now `SandboxUnusableError`) and
|
|
597
|
+
`BrokerUnreachableError` (now `SavedStateUnavailableError`) keep working as
|
|
598
|
+
deprecated aliases; they warn when Ruby's deprecation warnings are on.
|
|
597
599
|
|
|
598
600
|
## Troubleshooting — reporting an issue
|
|
599
601
|
|
|
@@ -601,7 +603,7 @@ The SDK supports saved Computer diagnostics. They require the diagnostics backen
|
|
|
601
603
|
|
|
602
604
|
```ruby
|
|
603
605
|
# Persist one key per logical request if you need to retry after a lost response.
|
|
604
|
-
report = client.create_diagnostic(computer_id: saved_id, session:
|
|
606
|
+
report = client.create_diagnostic(computer_id: saved_id, session: saved_session_name,
|
|
605
607
|
idempotency_key: diagnostic_request_key)
|
|
606
608
|
support_reference = report.fetch("id")
|
|
607
609
|
# Read later. Only poll queued/running reports, with a bounded deadline.
|
|
@@ -628,17 +630,17 @@ begin
|
|
|
628
630
|
session.agent.run("book the 9am slot")
|
|
629
631
|
rescue PineSandbox::ApiError => e
|
|
630
632
|
logger.error(e.message)
|
|
631
|
-
# => "… (
|
|
633
|
+
# => "… (sandbox=<sandbox-id>, op=POST /v2/sessions/main/agent/run, request_id=req_abc123)"
|
|
632
634
|
Report.open(computer_id: computer.id, session: session.name,
|
|
633
635
|
at: Time.now.utc, request_id: e.request_id, op: e.op)
|
|
634
636
|
end
|
|
635
637
|
```
|
|
636
638
|
|
|
637
639
|
Each error class exposes exactly its own typed context (the message always carries the same
|
|
638
|
-
data): Computer API **`ApiError`** → `#
|
|
640
|
+
data): Computer API **`ApiError`** → `#sandbox_id` / `#op` / `#request_id`; control-plane errors
|
|
639
641
|
→ `#op` / `#request_id`; portal **`ControlTokenError`** / **`AttachCredentialsError`** → `#op`
|
|
640
642
|
/ `#request_id`. The suffix grammar is uniform `key=value` across the Go and Ruby SDKs; Ruby
|
|
641
|
-
Computer API errors use **`
|
|
643
|
+
Computer API errors use **`sandbox=`** because the Ruby SDK holds the sandbox id, where the Go SDK
|
|
642
644
|
uses **`host=`** because its client holds the Computer host (same Computer, different handle
|
|
643
645
|
each SDK already has).
|
|
644
646
|
|
|
@@ -655,10 +657,10 @@ to find the failure:
|
|
|
655
657
|
|
|
656
658
|
## Known limitations
|
|
657
659
|
|
|
658
|
-
- Pause/Resume requires a
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
660
|
+
- Pause/Resume requires a Computer that supports snapshots; others reject it as
|
|
661
|
+
unsupported. Snapshot loss, expiry or failed recovery surfaces an error; it
|
|
662
|
+
never silently falls back to a fresh start. Computers started after enrollment
|
|
663
|
+
use snapshot retention and maximum age while paused; legacy expiry is unchanged.
|
|
662
664
|
|
|
663
665
|
## Develop
|
|
664
666
|
|