xapi-to 0.1.18 → 0.1.20
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.
- package/LICENSE +21 -0
- package/README.md +258 -10
- package/dist/chunk-TYY6JR6O.js +870 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1256 -590
- package/dist/openai-sandbox-client.d.ts +85 -0
- package/dist/openai-sandbox-client.js +285 -0
- package/examples/openai-agents-sandbox-local.ts +131 -0
- package/examples/sandbox-api-cli-openai.mjs +450 -0
- package/package.json +33 -4
- package/scripts/openai-sandbox-agent-e2e.ts +219 -0
- package/scripts/sandbox-playground-e2e.mjs +463 -0
- package/skills/xapi/SKILL.md +498 -0
- package/skills/xapi/guides/ai.md +200 -0
- package/skills/xapi/guides/ai_gateway.md +263 -0
- package/skills/xapi/guides/crypto.md +197 -0
- package/skills/xapi/guides/douyin.md +297 -0
- package/skills/xapi/guides/google_search.md +194 -0
- package/skills/xapi/guides/linkedin.md +253 -0
- package/skills/xapi/guides/reddit.md +312 -0
- package/skills/xapi/guides/sandbox.md +466 -0
- package/skills/xapi/guides/serper.md +124 -0
- package/skills/xapi/guides/sms.md +186 -0
- package/skills/xapi/guides/tiktok.md +322 -0
- package/skills/xapi/guides/twitter.md +276 -0
- package/skills/xapi/guides/weibo.md +301 -0
- package/skills/xapi/guides/ws_gateway.md +206 -0
- package/skills/xapi/guides/xiaohongshu.md +315 -0
- package/skills/xapi/scripts/download_tweet_videos.sh +125 -0
- package/src/client.ts +664 -0
- package/src/config.ts +160 -0
- package/src/openai-sandbox-client.ts +349 -0
- package/src/sandbox-client.ts +289 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Managed Sandbox Compute Guide
|
|
2
|
+
|
|
3
|
+
Use xAPI Sandbox when a user or AI agent needs an isolated cloud computer for
|
|
4
|
+
code execution, file processing, CI reproduction, a temporary Web/API preview,
|
|
5
|
+
GPU work, or a resumable multi-step job. It is a billable lifecycle service,
|
|
6
|
+
not an ordinary per-call action, so cleanup and audit are part of task success.
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Choose the shortest safe lifecycle](#choose-the-shortest-safe-lifecycle)
|
|
11
|
+
- [Authentication and gateway selection](#authentication-and-gateway-selection)
|
|
12
|
+
- [Inspect offerings and quote first](#inspect-offerings-and-quote-first)
|
|
13
|
+
- [One-shot execution](#one-shot-execution)
|
|
14
|
+
- [Multi-step agent lifecycle](#multi-step-agent-lifecycle)
|
|
15
|
+
- [Files and artifacts](#files-and-artifacts)
|
|
16
|
+
- [Web preview and background processes](#web-preview-and-background-processes)
|
|
17
|
+
- [Suspend and resume](#suspend-and-resume)
|
|
18
|
+
- [GPU jobs](#gpu-jobs)
|
|
19
|
+
- [Parallel agents](#parallel-agents)
|
|
20
|
+
- [OpenAI SandboxAgent with xAPI DeepSeek](#openai-sandboxagent-with-xapi-deepseek)
|
|
21
|
+
- [Audit, history, and billing](#audit-history-and-billing)
|
|
22
|
+
- [Run the real Playground acceptance suite](#run-the-real-playground-acceptance-suite)
|
|
23
|
+
- [Failure and interruption recovery](#failure-and-interruption-recovery)
|
|
24
|
+
- [AI operating rules](#ai-operating-rules)
|
|
25
|
+
|
|
26
|
+
## Choose the shortest safe lifecycle
|
|
27
|
+
|
|
28
|
+
| Need | Preferred command | Cleanup behavior |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Run one command and get stdout | `sandbox run` | Terminates automatically |
|
|
31
|
+
| Several exec/file calls | `create` + primitives | Agent must terminate |
|
|
32
|
+
| Inspect price/capabilities | `offerings`, `quote` | No instance created |
|
|
33
|
+
| Publish a temporary port | `port` after starting a server | Terminate afterward |
|
|
34
|
+
| Pause a reusable workspace | `suspend` | Storage may keep billing |
|
|
35
|
+
| Inspect prior work/cost | `history`, `get`, `audit` | Read-only |
|
|
36
|
+
|
|
37
|
+
Prefer `sandbox run` whenever the task fits one remote shell command. A shorter
|
|
38
|
+
lifecycle reduces orphan risk and returns one machine-readable JSON result.
|
|
39
|
+
|
|
40
|
+
## Authentication and gateway selection
|
|
41
|
+
|
|
42
|
+
The CLI reads `XAPI_KEY`, then `XAPI_API_KEY`, then `~/.xapi/config.json`.
|
|
43
|
+
Do not print, interpolate into a URL, or pass the key inside the remote command.
|
|
44
|
+
The CLI sends Sandbox credentials only to `*.xapi.to` or localhost.
|
|
45
|
+
|
|
46
|
+
Production uses `sandbox.xapi.to`. The test service is selected explicitly:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export XAPI_SANDBOX_HOST=sandbox.test.xapi.to
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Omit `--provider` (or use `--provider auto`) for lowest-price compatible
|
|
53
|
+
selection. Pin only when the task or test requires a particular provider:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx xapi-to sandbox offerings --provider cf-edge --format table
|
|
57
|
+
npx xapi-to sandbox quote --provider daytona --capabilities exec,files
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Provider pinning derives a controlled hostname such as
|
|
61
|
+
`cf-edge.sandbox.test.xapi.to`; it does not accept arbitrary provider URLs.
|
|
62
|
+
For production, canonical `--provider daytona` and `--provider e2b` are mapped
|
|
63
|
+
to the deployed `daytona-sandbox.sandbox.xapi.to` and
|
|
64
|
+
`e2b-sandbox.sandbox.xapi.to` aliases; their test hosts remain
|
|
65
|
+
`daytona.sandbox.test.xapi.to` and `e2b.sandbox.test.xapi.to`.
|
|
66
|
+
Available providers and capabilities can change, so inspect `offerings` rather
|
|
67
|
+
than assuming a static capability matrix.
|
|
68
|
+
|
|
69
|
+
## Inspect offerings and quote first
|
|
70
|
+
|
|
71
|
+
`offerings` shows provider-declared resources, capabilities, lifecycle support,
|
|
72
|
+
and hourly estimates. `quote` applies requirements without creating or billing
|
|
73
|
+
an instance:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx xapi-to sandbox offerings --format table
|
|
77
|
+
|
|
78
|
+
npx xapi-to sandbox quote \
|
|
79
|
+
--capabilities exec,files \
|
|
80
|
+
--cpu 2 \
|
|
81
|
+
--memory 4 \
|
|
82
|
+
--max-hourly-usd 0.20 \
|
|
83
|
+
--format pretty
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use `--format table` for a compact comparison and JSON when a complete quote ID
|
|
87
|
+
or nested rate card must be copied. Table truncation is marked with `…`.
|
|
88
|
+
|
|
89
|
+
Use `--requirements '<json>'` for fields that do not have a shortcut. Treat
|
|
90
|
+
`--max-hourly-usd` as a hard guardrail chosen before creation. A quote is
|
|
91
|
+
short-lived; create promptly or quote again.
|
|
92
|
+
|
|
93
|
+
## One-shot execution
|
|
94
|
+
|
|
95
|
+
`sandbox run` performs quote → create → wait for `RUNNING` → exec → terminate →
|
|
96
|
+
read final cost. Its default price ceiling is `$0.20/hour`:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npx xapi-to sandbox run \
|
|
100
|
+
--capabilities exec \
|
|
101
|
+
--command 'python3 -c "print(sum(range(1000)))"' \
|
|
102
|
+
--format pretty
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Arguments after a bare `--` are joined into the remote command:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx xapi-to sandbox run -- node --version
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Read these output fields first:
|
|
112
|
+
|
|
113
|
+
- `result.exitCode`, `result.stdout`, `result.stderr`: remote result;
|
|
114
|
+
- `cleanup.operationStatus`, `cleanup.state`: teardown result;
|
|
115
|
+
- `finalState`: should be `TERMINATED` (or provider-terminal `FAILED`);
|
|
116
|
+
- `totalCost`: service-calculated cost, not a client estimate.
|
|
117
|
+
|
|
118
|
+
A remote non-zero exit code becomes the local CLI exit code after cleanup, so
|
|
119
|
+
shells and AI runners can detect failure without parsing stdout.
|
|
120
|
+
|
|
121
|
+
`--keep` suppresses automatic termination. Use it only after the user explicitly
|
|
122
|
+
asks to retain the instance and understands that billing continues.
|
|
123
|
+
|
|
124
|
+
## Multi-step agent lifecycle
|
|
125
|
+
|
|
126
|
+
Use granular commands when an agent must alternate between files and commands.
|
|
127
|
+
Capture the instance ID without logging credentials:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
box_json="$(npx xapi-to sandbox create \
|
|
131
|
+
--capabilities exec,files \
|
|
132
|
+
--idempotency-key "job-${JOB_ID}" \
|
|
133
|
+
--wait)"
|
|
134
|
+
box_id="$(printf '%s' "$box_json" | jq -r '.id')"
|
|
135
|
+
|
|
136
|
+
npx xapi-to sandbox wait "$box_id" --state RUNNING --wait-timeout 5m
|
|
137
|
+
|
|
138
|
+
cleanup() {
|
|
139
|
+
npx xapi-to sandbox terminate "$box_id" --wait-timeout 5m || true
|
|
140
|
+
}
|
|
141
|
+
trap cleanup EXIT INT TERM
|
|
142
|
+
|
|
143
|
+
npx xapi-to sandbox file write "$box_id" task.md --file ./task.md
|
|
144
|
+
npx xapi-to sandbox exec "$box_id" --command 'npm test' --timeout 120
|
|
145
|
+
npx xapi-to sandbox file read "$box_id" report.json --output ./report.json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Use a stable business `--idempotency-key` when the caller might repeat create
|
|
149
|
+
after a lost response. Do not blindly repeat mutations with a new key: the first
|
|
150
|
+
request may already have created a billable instance.
|
|
151
|
+
|
|
152
|
+
The CLI rejects unknown Sandbox flags before making a request. Exact
|
|
153
|
+
`--offering-id` selection cannot be combined with `--max-hourly-usd`; select by
|
|
154
|
+
requirements under a ceiling or create from a previously checked quote instead.
|
|
155
|
+
Successful create output returns `clientIdempotencyKey`. If `create --wait`
|
|
156
|
+
fails after acceptance, retain the structured `instanceId`, `observedState`,
|
|
157
|
+
`clientIdempotencyKey`, and recovery commands from stderr, then inspect and
|
|
158
|
+
terminate the instance as appropriate.
|
|
159
|
+
|
|
160
|
+
## Files and artifacts
|
|
161
|
+
|
|
162
|
+
Write inline text or a local file:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npx xapi-to sandbox file write <id> instructions.txt --content 'Run tests.'
|
|
166
|
+
npx xapi-to sandbox file write <id> input.csv --file ./input.csv
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Read and list artifacts:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npx xapi-to sandbox file list <id> --path . --depth 3
|
|
173
|
+
npx xapi-to sandbox file read <id> output.json
|
|
174
|
+
npx xapi-to sandbox file read <id> output.zip --output ./output.zip
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Local `--output` uses create-new semantics and refuses to overwrite an existing
|
|
178
|
+
file. The CLI base64-encodes local input bytes so binary files survive transfer.
|
|
179
|
+
|
|
180
|
+
## Web preview and background processes
|
|
181
|
+
|
|
182
|
+
For providers that declare both `backgroundExec` and `ports`, use the explicit
|
|
183
|
+
provider-managed background command. Daytona needs this mode because deleting a
|
|
184
|
+
foreground command session also kills shell-backgrounded child processes:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
box_json="$(npx xapi-to sandbox create \
|
|
188
|
+
--provider daytona \
|
|
189
|
+
--capabilities exec,backgroundExec,ports \
|
|
190
|
+
--wait)"
|
|
191
|
+
box_id="$(printf '%s' "$box_json" | jq -r '.id')"
|
|
192
|
+
port=25319
|
|
193
|
+
|
|
194
|
+
cleanup() { npx xapi-to sandbox terminate "$box_id" --provider daytona || true; }
|
|
195
|
+
trap cleanup EXIT INT TERM
|
|
196
|
+
|
|
197
|
+
npx xapi-to sandbox exec "$box_id" --provider daytona --background --command \
|
|
198
|
+
"python3 -m http.server $port --bind 0.0.0.0"
|
|
199
|
+
npx xapi-to sandbox port "$box_id" "$port" --provider daytona
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`--background` returning a session/command ID is only launch acknowledgement.
|
|
203
|
+
Poll the public URL with bounded retries and verify an expected marker. If the
|
|
204
|
+
port response contains `headers`, include them in external requests; they can
|
|
205
|
+
carry a provider preview token. Do not emulate this mode with `nohup ... &` on
|
|
206
|
+
an Offering that does not declare `backgroundExec`.
|
|
207
|
+
|
|
208
|
+
Cloudflare currently uses its provider-specific command/preview behavior rather
|
|
209
|
+
than the standard background session capability. Pin `cf-edge` only when the
|
|
210
|
+
user explicitly wants Cloudflare. Port `8080` is the currently verified preview
|
|
211
|
+
path for the deployed bridge:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
box_json="$(npx xapi-to sandbox create \
|
|
215
|
+
--provider cf-edge \
|
|
216
|
+
--capabilities exec,files,ports \
|
|
217
|
+
--wait)"
|
|
218
|
+
box_id="$(printf '%s' "$box_json" | jq -r '.id')"
|
|
219
|
+
port=8080
|
|
220
|
+
|
|
221
|
+
cleanup() { npx xapi-to sandbox terminate "$box_id" --provider cf-edge || true; }
|
|
222
|
+
trap cleanup EXIT INT TERM
|
|
223
|
+
|
|
224
|
+
npx xapi-to sandbox file write "$box_id" index.html \
|
|
225
|
+
--provider cf-edge \
|
|
226
|
+
--content '<!doctype html><h1>xAPI preview</h1>'
|
|
227
|
+
|
|
228
|
+
npx xapi-to sandbox exec "$box_id" --provider cf-edge --command \
|
|
229
|
+
"nohup python3 -m http.server $port >/tmp/server.log 2>&1 & \
|
|
230
|
+
for i in 1 2 3 4 5 6 7 8 9 10; do \
|
|
231
|
+
curl -sf http://127.0.0.1:$port/ && exit 0; sleep 1; done; \
|
|
232
|
+
cat /tmp/server.log >&2; exit 1"
|
|
233
|
+
|
|
234
|
+
npx xapi-to sandbox port "$box_id" "$port" --provider cf-edge
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Validate that the returned public URL serves the expected marker before calling
|
|
238
|
+
the workflow successful. Quick Tunnel DNS/TLS readiness can be intermittent, so
|
|
239
|
+
use bounded retries (for example, one request every two seconds for up to two
|
|
240
|
+
minutes). If it still fails, verify localhost again, record the URL/error, and
|
|
241
|
+
terminate instead of leaving the instance billing. The URL stops working after
|
|
242
|
+
termination. Quick Tunnels are for previews; use a stable, supported named
|
|
243
|
+
tunnel or application deployment for production traffic.
|
|
244
|
+
|
|
245
|
+
## Suspend and resume
|
|
246
|
+
|
|
247
|
+
Check offering lifecycle fields first because not every provider supports an
|
|
248
|
+
explicit suspend operation:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npx xapi-to sandbox offerings --format pretty
|
|
252
|
+
npx xapi-to sandbox suspend <id>
|
|
253
|
+
npx xapi-to sandbox get <id>
|
|
254
|
+
npx xapi-to sandbox resume <id>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The CLI waits for `SUSPENDED` and `RUNNING` by default. Files may persist while
|
|
258
|
+
memory/processes do not; rely on the selected offering's declared lifecycle
|
|
259
|
+
semantics. Suspension can reduce compute cost but storage may still accrue cost.
|
|
260
|
+
If `lifecycle.suspension.supported` is false (as with a current cf-edge
|
|
261
|
+
offering), do not call suspend/resume; terminate and create a new instance.
|
|
262
|
+
|
|
263
|
+
## GPU jobs
|
|
264
|
+
|
|
265
|
+
Request GPU resources instead of assuming a provider or model. The current
|
|
266
|
+
RunPod offering is a managed GPU resource without standard `exec`/`files`, so
|
|
267
|
+
inspect its declared extension and obtain connection details instead of sending
|
|
268
|
+
an impossible shell command:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npx xapi-to sandbox quote \
|
|
272
|
+
--gpu-count 1 \
|
|
273
|
+
--gpu-model L4 \
|
|
274
|
+
--capabilities exec \
|
|
275
|
+
--max-hourly-usd 2.00
|
|
276
|
+
|
|
277
|
+
npx xapi-to sandbox create \
|
|
278
|
+
--provider runpod \
|
|
279
|
+
--gpu-count 1 \
|
|
280
|
+
--max-hourly-usd 2.00 \
|
|
281
|
+
--wait
|
|
282
|
+
|
|
283
|
+
npx xapi-to sandbox extension <id> runpod.connection_info \
|
|
284
|
+
--provider runpod \
|
|
285
|
+
--input '{}'
|
|
286
|
+
|
|
287
|
+
npx xapi-to sandbox terminate <id> --provider runpod
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
GPU work is usually more expensive. Quote first, set a deliberate ceiling, use
|
|
291
|
+
a command timeout, and terminate immediately after artifacts are retrieved.
|
|
292
|
+
|
|
293
|
+
## Parallel agents
|
|
294
|
+
|
|
295
|
+
Give each agent a separate instance. Do not share a mutable workspace when the
|
|
296
|
+
goal is isolation. Use unique idempotency keys and record every instance ID.
|
|
297
|
+
Run cleanup for all IDs even if one agent fails; then verify `sandbox list` has
|
|
298
|
+
no active instance from the job.
|
|
299
|
+
|
|
300
|
+
Limit concurrency based on budget. Parallel creation multiplies reservation and
|
|
301
|
+
running cost, even when the individual hourly quote is small.
|
|
302
|
+
|
|
303
|
+
## OpenAI SandboxAgent with xAPI DeepSeek
|
|
304
|
+
|
|
305
|
+
The OpenAI Agents SDK keeps the model provider and sandbox provider separate.
|
|
306
|
+
Use the SDK's OpenAI-compatible model provider for DeepSeek through
|
|
307
|
+
`https://ai.xapi.to/v1`, and the xAPI adapter for Sandbox compute:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import { OpenAIProvider, Runner } from '@openai/agents';
|
|
311
|
+
import { Manifest, SandboxAgent, shell } from '@openai/agents/sandbox';
|
|
312
|
+
import { XapiAgentsSandboxClient } from 'xapi-to/openai-sandbox';
|
|
313
|
+
|
|
314
|
+
const sandboxApiKey = process.env.XAPI_SANDBOX_KEY;
|
|
315
|
+
const aiApiKey = process.env.XAPI_AI_KEY;
|
|
316
|
+
if (!sandboxApiKey) throw new Error('XAPI_SANDBOX_KEY is required');
|
|
317
|
+
if (!aiApiKey) throw new Error('XAPI_AI_KEY is required');
|
|
318
|
+
|
|
319
|
+
const sandbox = new XapiAgentsSandboxClient({
|
|
320
|
+
apiKey: sandboxApiKey,
|
|
321
|
+
sandboxHost: 'sandbox.test.xapi.to',
|
|
322
|
+
provider: 'daytona',
|
|
323
|
+
model: 'deepseek-v4-pro',
|
|
324
|
+
});
|
|
325
|
+
const modelProvider = new OpenAIProvider({
|
|
326
|
+
apiKey: aiApiKey,
|
|
327
|
+
baseURL: 'https://ai.xapi.to/v1',
|
|
328
|
+
useResponses: false,
|
|
329
|
+
strictFeatureValidation: true,
|
|
330
|
+
});
|
|
331
|
+
const runner = new Runner({ modelProvider, tracingDisabled: true });
|
|
332
|
+
const agent = new SandboxAgent({
|
|
333
|
+
name: 'xAPI DeepSeek sandbox agent',
|
|
334
|
+
model: 'deepseek-v4-pro',
|
|
335
|
+
defaultManifest: new Manifest({ root: sandbox.workspaceRoot }),
|
|
336
|
+
capabilities: [shell()],
|
|
337
|
+
instructions: 'Use shell to complete and verify the task.',
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const result = await runner.run(agent, 'Write SDK_OK=42 to result.txt and read it.', {
|
|
342
|
+
maxTurns: 8,
|
|
343
|
+
sandbox: { client: sandbox },
|
|
344
|
+
});
|
|
345
|
+
console.log(result.finalOutput);
|
|
346
|
+
} finally {
|
|
347
|
+
await sandbox.lastSession?.close();
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Use `useResponses: false` because `ai.xapi.to` currently implements the OpenAI
|
|
352
|
+
Chat Completions-compatible protocol. Disable tracing unless a separate OpenAI
|
|
353
|
+
telemetry credential is configured; do not send an xAPI key to OpenAI tracing.
|
|
354
|
+
Keep `XAPI_AI_KEY` and `XAPI_SANDBOX_KEY` separate for a mixed environment:
|
|
355
|
+
the former is sent only to production `ai.xapi.to`, while the latter is sent
|
|
356
|
+
only to `sandbox.test.xapi.to`. A production key with both permissions may be
|
|
357
|
+
injected into both variables, but a Sandbox test key must not be assumed to
|
|
358
|
+
have production AI Gateway access.
|
|
359
|
+
The current adapter honestly supports an empty Manifest and Shell capability.
|
|
360
|
+
It rejects Manifest file/mount/environment materialization until those mappings
|
|
361
|
+
are implemented and tested.
|
|
362
|
+
|
|
363
|
+
Run the real SDK + DeepSeek + Daytona acceptance test from the CLI repository:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
XAPI_SANDBOX_KEY='<sandbox-test-key>' \
|
|
367
|
+
XAPI_AI_KEY='<ai-production-key>' \
|
|
368
|
+
npm run test:sandbox:openai -- \
|
|
369
|
+
--host sandbox.test.xapi.to \
|
|
370
|
+
--provider daytona \
|
|
371
|
+
--model deepseek-v4-pro
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
The script writes a redacted report, audits operations/events/usage/billing,
|
|
375
|
+
terminates its instance, and fails if any active test instance remains.
|
|
376
|
+
|
|
377
|
+
## Audit, history, and billing
|
|
378
|
+
|
|
379
|
+
Inspect current state and service-calculated total:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
npx xapi-to sandbox get <id> --format pretty
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Read individual audit streams:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npx xapi-to sandbox audit <id> --kind operations
|
|
389
|
+
npx xapi-to sandbox audit <id> --kind events
|
|
390
|
+
npx xapi-to sandbox audit <id> --kind usageSegments
|
|
391
|
+
npx xapi-to sandbox audit <id> --kind billingPeriods
|
|
392
|
+
npx xapi-to sandbox history --state HISTORY --page-size 100
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
`history` is a separate paginated endpoint for prior instances; it is not an
|
|
396
|
+
`audit --kind`. Filter it with `--search`, `--from`, and `--to` when reconciling
|
|
397
|
+
a specific agent run.
|
|
398
|
+
|
|
399
|
+
For acceptance, verify:
|
|
400
|
+
|
|
401
|
+
1. create/exec/file/port/terminate operations have terminal success statuses;
|
|
402
|
+
2. state events reach `TERMINATED`;
|
|
403
|
+
3. no usage segment or billing period remains open;
|
|
404
|
+
4. `totalCost` agrees with settled billing periods;
|
|
405
|
+
5. `sandbox list` shows no active instance from the test.
|
|
406
|
+
|
|
407
|
+
Use the returned billing data rather than recomputing cost from wall-clock time.
|
|
408
|
+
|
|
409
|
+
## Run the real Playground acceptance suite
|
|
410
|
+
|
|
411
|
+
From an xapi-cli development checkout, run the same nine real workflows shown
|
|
412
|
+
in the Web Playground. The suite uses normal CLI configuration, never accepts a
|
|
413
|
+
key on argv, records audit/billing evidence, terminates every tracked instance
|
|
414
|
+
in `finally`, and fails if any instance created after its baseline remains
|
|
415
|
+
ACTIVE (unrelated pre-existing account instances are still reported):
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
npm run test:sandbox:playground -- --host sandbox.test.xapi.to
|
|
419
|
+
|
|
420
|
+
# Focus a rerun or avoid the higher-cost GPU reservation
|
|
421
|
+
npm run test:sandbox:playground -- --host sandbox.test.xapi.to --only 8,9
|
|
422
|
+
npm run test:sandbox:playground -- --host sandbox.test.xapi.to --skip-gpu
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
The JSON report path is printed at completion. A provider capacity or HTTP 402
|
|
426
|
+
balance error is an external test precondition failure, not proof that the
|
|
427
|
+
scenario works; retain the error and a previous successful provider-specific
|
|
428
|
+
report separately. For Cloudflare, success requires an external HTTP 200 with
|
|
429
|
+
the expected page marker, not merely a returned Quick Tunnel hostname.
|
|
430
|
+
|
|
431
|
+
## Failure and interruption recovery
|
|
432
|
+
|
|
433
|
+
`sandbox run` handles ordinary exceptions, remote non-zero exits, `SIGINT`, and
|
|
434
|
+
`SIGTERM` by attempting termination before it exits. `SIGKILL`, machine loss, or
|
|
435
|
+
a network partition cannot run local cleanup.
|
|
436
|
+
|
|
437
|
+
After an uncertain interruption:
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
npx xapi-to sandbox list --format table
|
|
441
|
+
npx xapi-to sandbox get <suspected-id>
|
|
442
|
+
npx xapi-to sandbox terminate <suspected-id> --wait-timeout 5m
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
If terminate returns a state-change conflict, inspect state and retry after the
|
|
446
|
+
in-flight transition finishes. Do not treat an accepted operation response as
|
|
447
|
+
completion; wait for the instance's observed terminal state.
|
|
448
|
+
|
|
449
|
+
## AI operating rules
|
|
450
|
+
|
|
451
|
+
When exposing Sandbox to an AI agent:
|
|
452
|
+
|
|
453
|
+
1. Inject the xAPI key in the tool execution layer; never place it in prompts,
|
|
454
|
+
files, environment dumps, remote commands, logs, or model-visible output.
|
|
455
|
+
2. Start with `offerings`/`quote` when selection or budget is uncertain.
|
|
456
|
+
3. Prefer `sandbox run` for one-shot work and granular primitives only when the
|
|
457
|
+
task needs persistent state across calls.
|
|
458
|
+
4. Set capabilities and a price ceiling narrowly enough for the task.
|
|
459
|
+
5. Use `--background` only when the selected Offering declares
|
|
460
|
+
`backgroundExec`; then verify the listening port independently.
|
|
461
|
+
6. Treat instance IDs as cleanup obligations and keep them in structured state.
|
|
462
|
+
7. Put termination in `finally`; on interruption, enumerate and reconcile any
|
|
463
|
+
uncertain instances.
|
|
464
|
+
8. Report stdout, exit code, final state, cost, and cleanup outcome separately.
|
|
465
|
+
9. Never claim success from page/API structure alone—execute the relevant path,
|
|
466
|
+
verify its artifact or public URL, then check audit and residual instances.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Serper Guide
|
|
2
|
+
|
|
3
|
+
Use the direct `serper.*` API actions when the task needs provider-native
|
|
4
|
+
Google results, several searches in one mini-batch, or Serper surfaces that the
|
|
5
|
+
built-in `web.search.*` capabilities do not expose. For a simple single search
|
|
6
|
+
with a normalized xAPI response, prefer `web.search.*` and read
|
|
7
|
+
`google_search.md` instead.
|
|
8
|
+
|
|
9
|
+
The current `serper` service exposes 12 v7 actions. They are third-party API
|
|
10
|
+
actions, so parameters go inside `body`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx xapi-to get serper.search
|
|
14
|
+
npx xapi-to call serper.search --input '{"body":{"q":"OpenAI","gl":"us","hl":"en"}}'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run `get` before relying on optional parameters or response fields. Serper
|
|
18
|
+
responses are passed through in provider-native form and can gain fields that
|
|
19
|
+
are not declared in the xAPI output schema.
|
|
20
|
+
|
|
21
|
+
## Mini-batch
|
|
22
|
+
|
|
23
|
+
Eleven actions accept either one request object or an array of request objects
|
|
24
|
+
in `body`. The response is respectively one result object or an array of result
|
|
25
|
+
objects in request order:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx xapi-to call serper.search --input \
|
|
29
|
+
'{"body":[{"q":"OpenAI","gl":"us","hl":"en"},{"q":"Cloudflare","gl":"us","hl":"en"}]}'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each array member has the same shape as a single request. Do not wrap the
|
|
33
|
+
members in `queries`, and do not confuse this with `xapi-to get-batch`, which
|
|
34
|
+
retrieves several Action schemas without executing them.
|
|
35
|
+
|
|
36
|
+
`serper.reviews` is the only current `serper.*` action that does not support
|
|
37
|
+
mini-batch. Send exactly one object in its `body`.
|
|
38
|
+
|
|
39
|
+
## Billing
|
|
40
|
+
|
|
41
|
+
All 12 actions use dynamic xAPI billing at **$0.002 per Serper credit**. For a
|
|
42
|
+
single request, the charge is `response.credits * $0.002`; for a mini-batch it
|
|
43
|
+
is `sum(response[*].credits) * $0.002`.
|
|
44
|
+
|
|
45
|
+
The `cost: 0` placeholder shown in discovery output does not mean the call is
|
|
46
|
+
free; dynamic prices are not comparable as a fixed per-call price. Inspect the
|
|
47
|
+
Action's `meta.description` and `meta.pricing`, and keep returned `credits` when
|
|
48
|
+
auditing usage.
|
|
49
|
+
|
|
50
|
+
## Current Actions
|
|
51
|
+
|
|
52
|
+
| Action | Use it for | Primary input |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `serper.search` | General Google web results | `q` |
|
|
55
|
+
| `serper.images` | Google Images; current schema accepts `num` 10 or 100 | `q` |
|
|
56
|
+
| `serper.news` | Google News results | `q` |
|
|
57
|
+
| `serper.videos` | Google video results | `q` |
|
|
58
|
+
| `serper.shopping` | Product and shopping results | `q` |
|
|
59
|
+
| `serper.scholar` | Academic publications and citations | `q` |
|
|
60
|
+
| `serper.patents` | Patent search | `q` |
|
|
61
|
+
| `serper.autocomplete` | Suggestions for a partial query | `q` |
|
|
62
|
+
| `serper.places` | Local businesses and place search | `q` |
|
|
63
|
+
| `serper.maps` | Map search or lookup by Google Place ID/CID | `q`, `placeId`, or `cid` |
|
|
64
|
+
| `serper.lens` | Reverse image search from a public image URL | `url` |
|
|
65
|
+
| `serper.reviews` | Place reviews and cursor pagination | `placeId`, `cid`, or `fid` |
|
|
66
|
+
|
|
67
|
+
The common search-family controls are `gl`, `hl`, `location`, `page`, `num`,
|
|
68
|
+
`tbs`, and `autocorrect`, but not every action exposes every control. Use the
|
|
69
|
+
current `get` schema instead of copying parameters between actions.
|
|
70
|
+
|
|
71
|
+
## Focused Examples
|
|
72
|
+
|
|
73
|
+
### News with a Google time filter
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx xapi-to call serper.news --input \
|
|
77
|
+
'{"body":{"q":"AI regulation","gl":"us","hl":"en","tbs":"qdr:d"}}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Maps by coordinates
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx xapi-to call serper.maps --input \
|
|
84
|
+
'{"body":{"q":"coffee","ll":"@40.7455096,-74.0083012,14z","hl":"en"}}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use `placeId` or `cid` instead of `q` when resolving a known Google place.
|
|
88
|
+
|
|
89
|
+
### Google Lens
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx xapi-to call serper.lens --input \
|
|
93
|
+
'{"body":{"url":"https://example.com/public-image.jpg","gl":"us","hl":"en"}}'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The image must be reachable through a public URL; a local filesystem path is
|
|
97
|
+
not a valid Lens input.
|
|
98
|
+
|
|
99
|
+
### Reviews and pagination
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# First page; body must be an object, not an array
|
|
103
|
+
npx xapi-to call serper.reviews --input \
|
|
104
|
+
'{"body":{"placeId":"ChIJ...","sortBy":"newest","gl":"us","hl":"en"}}'
|
|
105
|
+
|
|
106
|
+
# Continue with the provider's cursor
|
|
107
|
+
npx xapi-to call serper.reviews --input \
|
|
108
|
+
'{"body":{"placeId":"ChIJ...","nextPageToken":"<token>","sortBy":"newest","gl":"us","hl":"en"}}'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Current `sortBy` values are `mostRelevant`, `newest`, `highestRating`, and
|
|
112
|
+
`lowestRating`.
|
|
113
|
+
|
|
114
|
+
## Service Boundary
|
|
115
|
+
|
|
116
|
+
Serper's upstream product also advertises webpage extraction, but the current
|
|
117
|
+
xAPI service directory exposes only the 12 `serper.*` actions above. Do not
|
|
118
|
+
invent or call `serper.webpage`. Search the live registry first; if a Webpage
|
|
119
|
+
Action is added later, use its own discovered Action ID and schema because the
|
|
120
|
+
upstream scraper is a separate surface from Google search.
|
|
121
|
+
|
|
122
|
+
For provider details that are not exposed by `xapi-to get`, consult the current
|
|
123
|
+
official Serper documentation at <https://serper.dev/>. xAPI's `body` wrapper,
|
|
124
|
+
Action IDs, and billing metadata remain authoritative for calls through xAPI.
|