xapi-to 0.1.20 → 0.1.22
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/README.md +164 -1
- package/dist/{chunk-TYY6JR6O.js → chunk-2YRWNREY.js} +75 -23
- package/dist/index.js +1245 -55
- package/dist/openai-sandbox-client.js +1 -1
- package/examples/openai-gpt-live-text.mjs +128 -0
- package/examples/provider/openapi.json +34 -0
- package/package.json +1 -1
- package/skills/xapi/SKILL.md +43 -195
- package/skills/xapi/guides/binance_web3.md +210 -0
- package/skills/xapi/guides/blockpi.md +112 -0
- package/skills/xapi/guides/domains.md +189 -0
- package/skills/xapi/guides/provider.md +228 -0
- package/skills/xapi/guides/sandbox.md +100 -46
- package/skills/xapi/guides/ws_gateway.md +64 -4
- package/src/client.ts +62 -7
- package/src/sandbox-client.ts +36 -16
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Provider service management
|
|
2
|
+
|
|
3
|
+
Read this guide for provider-side service authoring and operations. These
|
|
4
|
+
commands use `XAPI-KEY` directly and never exchange it for a broad JWT session.
|
|
5
|
+
The key must belong to the service owner and carry the scope named by the
|
|
6
|
+
operation.
|
|
7
|
+
|
|
8
|
+
## Inspect capabilities and scopes
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx xapi-to provider --help
|
|
12
|
+
npx xapi-to skill --help
|
|
13
|
+
npx xapi-to skill spec
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Use narrowly scoped keys. Common scopes are:
|
|
17
|
+
|
|
18
|
+
- `service:create`, `service:read`, `service:update`
|
|
19
|
+
- `version:create`, `service:publish`, `service:rollback`
|
|
20
|
+
- `observability:read`
|
|
21
|
+
- `skill:read`, `skill:submit`
|
|
22
|
+
- `earnings:read`; `earnings:transfer` only when reinvestment is intended
|
|
23
|
+
- `service:delete` only for deliberate removal workflows
|
|
24
|
+
|
|
25
|
+
Scope permission and ownership are independent. A key with a scope still cannot
|
|
26
|
+
manage another provider's service or Skill.
|
|
27
|
+
|
|
28
|
+
## Create and describe a service
|
|
29
|
+
|
|
30
|
+
Create uses the backend service DTO as JSON so credentials and endpoint
|
|
31
|
+
contracts do not have to appear in shell history:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx xapi-to provider create --file ./service.json
|
|
35
|
+
npx xapi-to provider list
|
|
36
|
+
npx xapi-to provider get <service-id>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Keep the three service content layers distinct:
|
|
40
|
+
|
|
41
|
+
- `description` is the short marketplace-card summary.
|
|
42
|
+
- `aboutMarkdown` is the long About tab.
|
|
43
|
+
- `website` is a public HTTP(S) link.
|
|
44
|
+
|
|
45
|
+
Prefer files for long text:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx xapi-to provider update <service-id> \
|
|
49
|
+
--description "Short marketplace summary" \
|
|
50
|
+
--about-file ./ABOUT.md \
|
|
51
|
+
--website https://example.com
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `--clear-about` or `--clear-website` to clear a value. Provider metadata
|
|
55
|
+
updates cannot modify the version contract or upstream credentials; use the
|
|
56
|
+
version command for those fields.
|
|
57
|
+
|
|
58
|
+
## Configure a service request limit
|
|
59
|
+
|
|
60
|
+
Rate limits are optional service settings. Configure both values together when
|
|
61
|
+
creating a service or updating an existing one:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx xapi-to provider create --file ./service.json \
|
|
65
|
+
--rate-limit-requests 100 \
|
|
66
|
+
--rate-limit-period-seconds 60
|
|
67
|
+
|
|
68
|
+
npx xapi-to provider update <service-id> \
|
|
69
|
+
--rate-limit-requests 100 \
|
|
70
|
+
--rate-limit-period-seconds 60
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`requests` accepts 1 through 1,000,000 and `periodSeconds` accepts 1 through
|
|
74
|
+
86,400. The backend applies one quota to each User x Service pair, so all API keys
|
|
75
|
+
owned by the same user share that service quota. This setting is supported only
|
|
76
|
+
for `PROXY` services; the backend rejects a non-null limit for `DIRECT` services.
|
|
77
|
+
|
|
78
|
+
Disable the limit explicitly with:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx xapi-to provider update <service-id> --clear-rate-limit
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This sends `rateLimitConfig: null`. Omitting the rate-limit flags during an
|
|
85
|
+
update leaves the existing setting unchanged. A raw `rateLimitConfig` can also
|
|
86
|
+
be included in `service.json`; explicit CLI rate-limit flags override that field.
|
|
87
|
+
|
|
88
|
+
## Edit and publish a revision
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Inspect current majors and revisions
|
|
92
|
+
npx xapi-to provider versions <service-id>
|
|
93
|
+
|
|
94
|
+
# Either create a new major or pull a working revision from an existing major
|
|
95
|
+
npx xapi-to provider major create <service-id>
|
|
96
|
+
npx xapi-to provider revision start <service-id> <major>
|
|
97
|
+
|
|
98
|
+
# Merge a partial version-contract update; add --replace for full replacement
|
|
99
|
+
npx xapi-to provider version update \
|
|
100
|
+
<service-id> <version-id> --file ./contract.json
|
|
101
|
+
|
|
102
|
+
# Inspect before publishing
|
|
103
|
+
npx xapi-to provider diff <service-id> <major>
|
|
104
|
+
|
|
105
|
+
# Submit through the normal review gate with public release notes
|
|
106
|
+
npx xapi-to provider publish \
|
|
107
|
+
<service-id> <revision-id> --changelog-file ./CHANGELOG.md
|
|
108
|
+
|
|
109
|
+
# Inspect the review result
|
|
110
|
+
npx xapi-to provider review <service-id> <revision-id>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The changelog is provider-authored, public release information associated with
|
|
114
|
+
that revision. It is not a system deployment log. Build, review, and runtime
|
|
115
|
+
events are generated by the platform and should only be read, never uploaded as
|
|
116
|
+
if they were evidence.
|
|
117
|
+
|
|
118
|
+
`publish` can change live service behavior after review. The CLI does not
|
|
119
|
+
automatically retry this write after an ambiguous transport failure. Read the
|
|
120
|
+
version overview and review state before deciding whether to submit again.
|
|
121
|
+
|
|
122
|
+
## Create, upload, and link the usage Skill
|
|
123
|
+
|
|
124
|
+
Generate a service-specific starting point from the currently serving endpoints:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npx xapi-to provider skill scaffold \
|
|
128
|
+
<service-id> --output ./my-service/SKILL.md
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The scaffold command refuses to overwrite an existing file unless `--force` is
|
|
132
|
+
explicitly supplied. Complete the instructions and metadata, then submit either
|
|
133
|
+
a local directory or a public GitHub tree:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npx xapi-to skill submit --dir ./my-service
|
|
137
|
+
|
|
138
|
+
npx xapi-to skill submit \
|
|
139
|
+
--github https://github.com/org/repo/tree/main/skills/my-service \
|
|
140
|
+
--version 1.0.0
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Local submission skips symlinks, `.git`, and `node_modules`; requires a root
|
|
144
|
+
`SKILL.md`; permits at most 100 files; limits each file to 512 KiB and the encoded
|
|
145
|
+
package to 2 MiB. The server still performs manifest validation and secret
|
|
146
|
+
scanning. A successful upload creates or updates the owned Skill version and
|
|
147
|
+
submits it for review; it is not immediately public.
|
|
148
|
+
|
|
149
|
+
Use the returned submission ID:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx xapi-to skill status <submission-id>
|
|
153
|
+
npx xapi-to skill wait <submission-id> --timeout 10m
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
After the Skill is published, bind it as the service's primary tutorial and
|
|
157
|
+
record the serving-contract fingerprint:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx xapi-to provider skill link <service-id> <skill-id>
|
|
161
|
+
npx xapi-to provider skill fingerprint \
|
|
162
|
+
<service-id> --skill-version-id <skill-version-id>
|
|
163
|
+
npx xapi-to provider skill context <service-id>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Only a Skill owned by the same provider can be linked, and one Skill can be the
|
|
167
|
+
primary Skill of only one service. The backend permits linking a pending Skill,
|
|
168
|
+
but the public marketplace exposes only a published version; wait for publication
|
|
169
|
+
unless intentionally preparing the association early. Use `provider skill unlink`
|
|
170
|
+
to remove the primary association. The context response reports drift when host,
|
|
171
|
+
major version, or serving endpoints no longer match the stored fingerprint; update
|
|
172
|
+
and resubmit the Skill rather than merely overwriting the fingerprint.
|
|
173
|
+
|
|
174
|
+
## Observe and recover
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx xapi-to provider metrics --days 30
|
|
178
|
+
npx xapi-to provider metrics <service-id> --days 7
|
|
179
|
+
npx xapi-to provider events --limit 50
|
|
180
|
+
npx xapi-to provider events --after '<opaque-next-cursor>' --limit 50
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Pass event cursors back unchanged. Metrics and events are owner-scoped; usage
|
|
184
|
+
events are also restricted to the current key where applicable.
|
|
185
|
+
|
|
186
|
+
Verify the finalized cost of a canary or provider call with its receipt ID. Use
|
|
187
|
+
the `X-XAPI-Request-Id` response header or the final `xapi.usage` SSE event, and
|
|
188
|
+
wait when asynchronous billing has not finalized yet:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npx xapi-to usage <request-id>
|
|
192
|
+
npx xapi-to usage wait <request-id> --timeout 1m
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Receipt reads are idempotent. The wait command applies one total deadline,
|
|
196
|
+
retries not-found and transient transport failures within that deadline, and
|
|
197
|
+
fails immediately for permanent authorization or validation errors.
|
|
198
|
+
|
|
199
|
+
Rollback and default-major changes affect live routing:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npx xapi-to provider rollback \
|
|
203
|
+
<service-id> <major> --revision <published-revision-id> \
|
|
204
|
+
--reason "Restore the last known-good contract"
|
|
205
|
+
npx xapi-to provider default-major <service-id> <major>
|
|
206
|
+
npx xapi-to provider deprecate <service-id> <major>
|
|
207
|
+
npx xapi-to provider restore <service-id> <major>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Inspect the target revision before rollback. Do not automatically retry an
|
|
211
|
+
ambiguous rollback response. Deletion requires both `service:delete` and an
|
|
212
|
+
explicit service name or ID confirmation:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
npx xapi-to provider delete <service-id> --confirm <service-name-or-id>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Earnings reinvestment
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
npx xapi-to earnings
|
|
222
|
+
npx xapi-to earnings list --status SETTLED
|
|
223
|
+
npx xapi-to earnings transfer 1 --idempotency-key <stable-operation-key>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Transfer is one-way: it converts settled provider earnings into spendable xAPI
|
|
227
|
+
balance. Confirm the amount and available settled balance first. A transfer may
|
|
228
|
+
be retried only with the same idempotency key and the same amount.
|
|
@@ -7,32 +7,45 @@ not an ordinary per-call action, so cleanup and audit are part of task success.
|
|
|
7
7
|
|
|
8
8
|
## Contents
|
|
9
9
|
|
|
10
|
+
- [Product boundary](#product-boundary)
|
|
10
11
|
- [Choose the shortest safe lifecycle](#choose-the-shortest-safe-lifecycle)
|
|
11
12
|
- [Authentication and gateway selection](#authentication-and-gateway-selection)
|
|
12
13
|
- [Inspect offerings and quote first](#inspect-offerings-and-quote-first)
|
|
13
14
|
- [One-shot execution](#one-shot-execution)
|
|
14
|
-
- [Multi-step
|
|
15
|
+
- [Multi-step client lifecycle](#multi-step-client-lifecycle)
|
|
15
16
|
- [Files and artifacts](#files-and-artifacts)
|
|
16
17
|
- [Web preview and background processes](#web-preview-and-background-processes)
|
|
17
18
|
- [Suspend and resume](#suspend-and-resume)
|
|
18
19
|
- [GPU jobs](#gpu-jobs)
|
|
19
|
-
- [Parallel
|
|
20
|
-
- [OpenAI SandboxAgent
|
|
20
|
+
- [Parallel isolated instances](#parallel-isolated-instances)
|
|
21
|
+
- [OpenAI SandboxAgent integration example](#openai-sandboxagent-integration-example)
|
|
21
22
|
- [Audit, history, and billing](#audit-history-and-billing)
|
|
22
|
-
- [Run the real Playground acceptance suite](#run-the-real-playground-acceptance-suite)
|
|
23
|
+
- [Run the real Playground recipe acceptance suite](#run-the-real-playground-recipe-acceptance-suite)
|
|
23
24
|
- [Failure and interruption recovery](#failure-and-interruption-recovery)
|
|
24
25
|
- [AI operating rules](#ai-operating-rules)
|
|
25
26
|
|
|
27
|
+
## Product boundary
|
|
28
|
+
|
|
29
|
+
xAPI is the Sandbox resource and capability provider. The CLI is a thin client
|
|
30
|
+
for discovery, quote, lifecycle, exec, files, ports, provider extensions,
|
|
31
|
+
history, audit, and billing. It does not implement prompts, model loops, memory,
|
|
32
|
+
multi-agent orchestration, job DAGs, queues, or human approval workflows.
|
|
33
|
+
|
|
34
|
+
Agent, CI, browser, and data examples in this guide are client-side recipes that
|
|
35
|
+
compose Sandbox primitives. They are not additional Gateway workflow APIs.
|
|
36
|
+
Provider-native features remain valid Sandbox extensions when the live Offering
|
|
37
|
+
declares their schemas, limits, state effects, and billing behavior.
|
|
38
|
+
|
|
26
39
|
## Choose the shortest safe lifecycle
|
|
27
40
|
|
|
28
|
-
| Need
|
|
29
|
-
|
|
30
|
-
| Run one command and get stdout | `sandbox run`
|
|
31
|
-
| Several exec/file calls
|
|
32
|
-
| Inspect price/capabilities
|
|
33
|
-
| Publish a temporary port
|
|
34
|
-
| Pause a reusable workspace
|
|
35
|
-
| Inspect prior work/cost
|
|
41
|
+
| Need | Preferred command | Cleanup behavior |
|
|
42
|
+
| ------------------------------ | ------------------------------ | ------------------------ |
|
|
43
|
+
| Run one command and get stdout | `sandbox run` | Terminates automatically |
|
|
44
|
+
| Several exec/file calls | `create` + primitives | Agent must terminate |
|
|
45
|
+
| Inspect price/capabilities | `offerings`, `quote` | No instance created |
|
|
46
|
+
| Publish a temporary port | `port` after starting a server | Terminate afterward |
|
|
47
|
+
| Pause a reusable workspace | `suspend` | Storage may keep billing |
|
|
48
|
+
| Inspect prior work/cost | `history`, `get`, `audit` | Read-only |
|
|
36
49
|
|
|
37
50
|
Prefer `sandbox run` whenever the task fits one remote shell command. A shorter
|
|
38
51
|
lifecycle reduces orphan risk and returns one machine-readable JSON result.
|
|
@@ -79,6 +92,7 @@ npx xapi-to sandbox quote \
|
|
|
79
92
|
--capabilities exec,files \
|
|
80
93
|
--cpu 2 \
|
|
81
94
|
--memory 4 \
|
|
95
|
+
--min-runtime 24h \
|
|
82
96
|
--max-hourly-usd 0.20 \
|
|
83
97
|
--format pretty
|
|
84
98
|
```
|
|
@@ -121,9 +135,9 @@ shells and AI runners can detect failure without parsing stdout.
|
|
|
121
135
|
`--keep` suppresses automatic termination. Use it only after the user explicitly
|
|
122
136
|
asks to retain the instance and understands that billing continues.
|
|
123
137
|
|
|
124
|
-
## Multi-step
|
|
138
|
+
## Multi-step client lifecycle
|
|
125
139
|
|
|
126
|
-
Use granular commands when
|
|
140
|
+
Use granular commands when a client must alternate between files and commands.
|
|
127
141
|
Capture the instance ID without logging credentials:
|
|
128
142
|
|
|
129
143
|
```bash
|
|
@@ -205,15 +219,17 @@ port response contains `headers`, include them in external requests; they can
|
|
|
205
219
|
carry a provider preview token. Do not emulate this mode with `nohup ... &` on
|
|
206
220
|
an Offering that does not declare `backgroundExec`.
|
|
207
221
|
|
|
208
|
-
Cloudflare
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
222
|
+
Cloudflare declares the standard background command surface and maps it to its
|
|
223
|
+
native managed process API. Pin `cf-edge` only when the user explicitly wants
|
|
224
|
+
Cloudflare. For a one-day workspace, require `--min-runtime 24h` and explicitly
|
|
225
|
+
enable `cloudflare.set_keep_alive`; the runtime requirement filters offerings,
|
|
226
|
+
while keepAlive prevents the default ten-minute idle reset:
|
|
212
227
|
|
|
213
228
|
```bash
|
|
214
229
|
box_json="$(npx xapi-to sandbox create \
|
|
215
230
|
--provider cf-edge \
|
|
216
|
-
--capabilities exec,files,ports \
|
|
231
|
+
--capabilities exec,backgroundExec,files,ports,lifecycle.keep_alive \
|
|
232
|
+
--min-runtime 24h \
|
|
217
233
|
--wait)"
|
|
218
234
|
box_id="$(printf '%s' "$box_json" | jq -r '.id')"
|
|
219
235
|
port=8080
|
|
@@ -225,11 +241,11 @@ npx xapi-to sandbox file write "$box_id" index.html \
|
|
|
225
241
|
--provider cf-edge \
|
|
226
242
|
--content '<!doctype html><h1>xAPI preview</h1>'
|
|
227
243
|
|
|
228
|
-
npx xapi-to sandbox
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
npx xapi-to sandbox extension "$box_id" cloudflare.set_keep_alive \
|
|
245
|
+
--provider cf-edge --input '{"keepAlive":true}'
|
|
246
|
+
|
|
247
|
+
npx xapi-to sandbox exec "$box_id" --provider cf-edge --background --command \
|
|
248
|
+
"python3 -m http.server $port --directory /workspace"
|
|
233
249
|
|
|
234
250
|
npx xapi-to sandbox port "$box_id" "$port" --provider cf-edge
|
|
235
251
|
```
|
|
@@ -242,6 +258,37 @@ terminate instead of leaving the instance billing. The URL stops working after
|
|
|
242
258
|
termination. Quick Tunnels are for previews; use a stable, supported named
|
|
243
259
|
tunnel or application deployment for production traffic.
|
|
244
260
|
|
|
261
|
+
Cloudflare extensions also expose managed process logs/readiness, persistent
|
|
262
|
+
shell sessions, stateful Python/JavaScript/TypeScript code contexts, Git
|
|
263
|
+
checkout, file-change cursors, bucket mounts, and R2 backup/restore. Inspect
|
|
264
|
+
`capabilities.extensionIds` before calling them. A default idle reset starts a
|
|
265
|
+
fresh container and does not retain files, processes, sessions, or interpreter
|
|
266
|
+
state. `keepAlive` removes that idle cutoff but does not guarantee that platform
|
|
267
|
+
maintenance can never restart the host. Use R2 backup or external storage for
|
|
268
|
+
state that must survive restarts, disable keepAlive in `finally`, and terminate.
|
|
269
|
+
|
|
270
|
+
If the Offering declares `cloudflare.browser.*`, Cloudflare Browser Run can be
|
|
271
|
+
used through the same generic extension command. Prefer a Quick Action for
|
|
272
|
+
read-only page understanding before paying for a multi-step browser session:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx xapi-to sandbox extension "$box_id" cloudflare.browser.snapshot \
|
|
276
|
+
--provider cf-edge \
|
|
277
|
+
--input '{"url":"https://example.com/","formats":["screenshot","markdown","accessibilityTree"]}'
|
|
278
|
+
|
|
279
|
+
npx xapi-to sandbox extension "$box_id" cloudflare.browser.automate \
|
|
280
|
+
--provider cf-edge \
|
|
281
|
+
--input '{"url":"https://demo.playwright.dev/todomvc/","actions":[{"type":"fill","selector":".new-todo","value":"xAPI task"},{"type":"press","selector":".new-todo","key":"Enter"}],"extract":[{"name":"todos","selector":".todo-list li label","all":true}],"screenshot":{"type":"png","fullPage":true}}'
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Browser Run and the Sandbox container do not share a filesystem. Write the
|
|
285
|
+
returned page data into the sandbox through `sandbox file write` if a later
|
|
286
|
+
container command must analyze it. Browser operations have separate operation
|
|
287
|
+
prices; inspect the quote instead of assuming the container hourly cap includes
|
|
288
|
+
them. The current automation extension is one-shot and closes the browser.
|
|
289
|
+
Persistent CDP/Live View/HITL are not supported until xAPI owns and authorizes
|
|
290
|
+
the session and proxies its WebSocket without exposing Cloudflare credentials.
|
|
291
|
+
|
|
245
292
|
## Suspend and resume
|
|
246
293
|
|
|
247
294
|
Check offering lifecycle fields first because not every provider supports an
|
|
@@ -290,58 +337,65 @@ npx xapi-to sandbox terminate <id> --provider runpod
|
|
|
290
337
|
GPU work is usually more expensive. Quote first, set a deliberate ceiling, use
|
|
291
338
|
a command timeout, and terminate immediately after artifacts are retrieved.
|
|
292
339
|
|
|
293
|
-
## Parallel
|
|
340
|
+
## Parallel isolated instances
|
|
294
341
|
|
|
295
|
-
Give each agent a separate instance. Do not share a mutable
|
|
296
|
-
goal is isolation. Use unique idempotency keys and record every instance ID.
|
|
342
|
+
Give each concurrent worker or agent a separate instance. Do not share a mutable
|
|
343
|
+
workspace when the goal is isolation. Use unique idempotency keys and record every instance ID.
|
|
297
344
|
Run cleanup for all IDs even if one agent fails; then verify `sandbox list` has
|
|
298
345
|
no active instance from the job.
|
|
299
346
|
|
|
300
347
|
Limit concurrency based on budget. Parallel creation multiplies reservation and
|
|
301
348
|
running cost, even when the individual hourly quote is small.
|
|
302
349
|
|
|
303
|
-
## OpenAI SandboxAgent
|
|
350
|
+
## OpenAI SandboxAgent integration example
|
|
304
351
|
|
|
305
352
|
The OpenAI Agents SDK keeps the model provider and sandbox provider separate.
|
|
306
353
|
Use the SDK's OpenAI-compatible model provider for DeepSeek through
|
|
307
354
|
`https://ai.xapi.to/v1`, and the xAPI adapter for Sandbox compute:
|
|
308
355
|
|
|
356
|
+
The Agents SDK owns the model loop, tool choice, prompt, and conversation state.
|
|
357
|
+
xAPI owns only the Sandbox resource, execution, lifecycle, audit, and billing.
|
|
358
|
+
|
|
309
359
|
```ts
|
|
310
|
-
import { OpenAIProvider, Runner } from
|
|
311
|
-
import { Manifest, SandboxAgent, shell } from
|
|
312
|
-
import { XapiAgentsSandboxClient } from
|
|
360
|
+
import { OpenAIProvider, Runner } from "@openai/agents";
|
|
361
|
+
import { Manifest, SandboxAgent, shell } from "@openai/agents/sandbox";
|
|
362
|
+
import { XapiAgentsSandboxClient } from "xapi-to/openai-sandbox";
|
|
313
363
|
|
|
314
364
|
const sandboxApiKey = process.env.XAPI_SANDBOX_KEY;
|
|
315
365
|
const aiApiKey = process.env.XAPI_AI_KEY;
|
|
316
|
-
if (!sandboxApiKey) throw new Error(
|
|
317
|
-
if (!aiApiKey) throw new Error(
|
|
366
|
+
if (!sandboxApiKey) throw new Error("XAPI_SANDBOX_KEY is required");
|
|
367
|
+
if (!aiApiKey) throw new Error("XAPI_AI_KEY is required");
|
|
318
368
|
|
|
319
369
|
const sandbox = new XapiAgentsSandboxClient({
|
|
320
370
|
apiKey: sandboxApiKey,
|
|
321
|
-
sandboxHost:
|
|
322
|
-
provider:
|
|
323
|
-
model:
|
|
371
|
+
sandboxHost: "sandbox.test.xapi.to",
|
|
372
|
+
provider: "daytona",
|
|
373
|
+
model: "deepseek-v4-pro",
|
|
324
374
|
});
|
|
325
375
|
const modelProvider = new OpenAIProvider({
|
|
326
376
|
apiKey: aiApiKey,
|
|
327
|
-
baseURL:
|
|
377
|
+
baseURL: "https://ai.xapi.to/v1",
|
|
328
378
|
useResponses: false,
|
|
329
379
|
strictFeatureValidation: true,
|
|
330
380
|
});
|
|
331
381
|
const runner = new Runner({ modelProvider, tracingDisabled: true });
|
|
332
382
|
const agent = new SandboxAgent({
|
|
333
|
-
name:
|
|
334
|
-
model:
|
|
383
|
+
name: "xAPI DeepSeek sandbox agent",
|
|
384
|
+
model: "deepseek-v4-pro",
|
|
335
385
|
defaultManifest: new Manifest({ root: sandbox.workspaceRoot }),
|
|
336
386
|
capabilities: [shell()],
|
|
337
|
-
instructions:
|
|
387
|
+
instructions: "Use shell to complete and verify the task.",
|
|
338
388
|
});
|
|
339
389
|
|
|
340
390
|
try {
|
|
341
|
-
const result = await runner.run(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
391
|
+
const result = await runner.run(
|
|
392
|
+
agent,
|
|
393
|
+
"Write SDK_OK=42 to result.txt and read it.",
|
|
394
|
+
{
|
|
395
|
+
maxTurns: 8,
|
|
396
|
+
sandbox: { client: sandbox },
|
|
397
|
+
},
|
|
398
|
+
);
|
|
345
399
|
console.log(result.finalOutput);
|
|
346
400
|
} finally {
|
|
347
401
|
await sandbox.lastSession?.close();
|
|
@@ -406,9 +460,9 @@ For acceptance, verify:
|
|
|
406
460
|
|
|
407
461
|
Use the returned billing data rather than recomputing cost from wall-clock time.
|
|
408
462
|
|
|
409
|
-
## Run the real Playground acceptance suite
|
|
463
|
+
## Run the real Playground recipe acceptance suite
|
|
410
464
|
|
|
411
|
-
From an xapi-cli development checkout, run the same nine
|
|
465
|
+
From an xapi-cli development checkout, run the same nine client recipes shown
|
|
412
466
|
in the Web Playground. The suite uses normal CLI configuration, never accepts a
|
|
413
467
|
key on argv, records audit/billing evidence, terminates every tracked instance
|
|
414
468
|
in `finally`, and fails if any instance created after its baseline remains
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# WebSocket Gateway Guide
|
|
2
2
|
|
|
3
|
-
Use xAPI's WebSocket Gateway for full-duplex, low-latency sessions such as OpenAI Realtime, streaming speech recognition, bidirectional text-to-speech, simultaneous interpretation, and podcast generation.
|
|
3
|
+
Use xAPI's WebSocket Gateway for full-duplex, low-latency sessions such as GPT Live, OpenAI Realtime, streaming speech recognition, bidirectional text-to-speech, simultaneous interpretation, and podcast generation.
|
|
4
4
|
|
|
5
5
|
The WebSocket Gateway shares the public `ai.xapi.to` host with the HTTP AI Gateway, but it is a separate protocol surface. An HTTP request continues to use the AI Gateway; a valid WebSocket Upgrade request is routed to the WebSocket Gateway.
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ The WebSocket Gateway shares the public `ai.xapi.to` host with the HTTP AI Gatew
|
|
|
9
9
|
- [Choose the right interface](#choose-the-right-interface)
|
|
10
10
|
- [Public URLs and routing](#public-urls-and-routing)
|
|
11
11
|
- [Authentication](#authentication)
|
|
12
|
+
- [GPT Live example](#gpt-live-example)
|
|
12
13
|
- [OpenAI Realtime example](#openai-realtime-example)
|
|
13
14
|
- [Browser connections](#browser-connections)
|
|
14
15
|
- [Native protocol endpoints](#native-protocol-endpoints)
|
|
@@ -39,6 +40,7 @@ Current curated production paths include:
|
|
|
39
40
|
|
|
40
41
|
| Path | Protocol | Typical use |
|
|
41
42
|
|---|---|---|
|
|
43
|
+
| `/v1/live/sessions` on the GPT Live service host | OpenAI Live Sessions JSON events | GPT-Live 1 voice with Client or managed Responses delegation |
|
|
42
44
|
| `/v1/realtime` | OpenAI Realtime GA JSON events | Realtime text and voice |
|
|
43
45
|
| `/v1/asr` | Volcengine ASR binary frames | Streaming speech recognition |
|
|
44
46
|
| `/v1/tts` | Doubao bidirectional TTS binary frames | Streaming text-to-speech |
|
|
@@ -57,6 +59,15 @@ wss://<service-slug>.p.xapi.to/<endpoint-path>
|
|
|
57
59
|
|
|
58
60
|
This avoids shared-path ambiguity and is required when the desired service uses a provider-native protocol that is not selected by the unified path. Console Try-It and review workflows can also address an endpoint exactly with `?endpoint=<endpoint-id>`.
|
|
59
61
|
|
|
62
|
+
GPT Live currently uses the service-specific URL:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
wss://openai-live.p.xapi.to/v1/live/sessions
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Do not replace it with `/v1/realtime`. Live Sessions uses `session.start` and
|
|
69
|
+
`session.started`; Realtime uses a different session lifecycle and event model.
|
|
70
|
+
|
|
60
71
|
## Authentication
|
|
61
72
|
|
|
62
73
|
Use the same xAPI key as the CLI and HTTP Gateway. Server-side clients should send one of these handshake headers:
|
|
@@ -78,6 +89,43 @@ The Gateway also accepts `?token=<XAPI_KEY>` or `?xapi-key=<XAPI_KEY>` for clien
|
|
|
78
89
|
|
|
79
90
|
Authentication is checked before the WebSocket upgrade. Invalid handshakes therefore return an HTTP status instead of opening and immediately closing a socket.
|
|
80
91
|
|
|
92
|
+
## GPT Live example
|
|
93
|
+
|
|
94
|
+
GPT Live is a provider-native JSON event protocol, not an Action `call` and not
|
|
95
|
+
OpenAI Realtime. The first client frame must be `session.start`. The production
|
|
96
|
+
endpoint locks the Live model to `gpt-live-1`, disables storage, and lets the
|
|
97
|
+
caller choose `client` or `responses` delegation once per connection. In
|
|
98
|
+
`responses` mode, the managed Responses model and its limits remain
|
|
99
|
+
server-controlled.
|
|
100
|
+
|
|
101
|
+
The packaged `examples/openai-gpt-live-text.mjs` demonstrates the smallest
|
|
102
|
+
managed-Responses lifecycle: connect with a server-side xAPI key, send
|
|
103
|
+
`session.start`, wait for `session.started`, create a text item, request a
|
|
104
|
+
response, and finish with `session.close` after the nested response completes.
|
|
105
|
+
It intentionally omits microphone capture so the protocol boundary is clear.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Install the example's WebSocket transport in your application directory.
|
|
109
|
+
npm install ws
|
|
110
|
+
|
|
111
|
+
# Supply XAPI_KEY through the process environment; never put it in source code
|
|
112
|
+
# or pass it as a command-line argument.
|
|
113
|
+
node examples/openai-gpt-live-text.mjs "Answer in one short sentence."
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Voice clients use the same session lifecycle, then send base64 PCM chunks as
|
|
117
|
+
`session.input_audio.append` events and consume `session.output_audio.delta`.
|
|
118
|
+
Audio format, voice, interruption behavior, and the complete event schema must
|
|
119
|
+
follow the current Live Sessions contract. Do not copy Realtime
|
|
120
|
+
`conversation.item.create` or `input_audio_buffer.*` events into a Live session.
|
|
121
|
+
|
|
122
|
+
With `client` delegation, the application must handle
|
|
123
|
+
`session.delegation.created`, run its own text-model request, and return the
|
|
124
|
+
result with `session.commentary.append`, then wait for
|
|
125
|
+
`session.commentary.appended`. Selecting `client` does not make xAPI run a
|
|
126
|
+
model on the application's behalf. Use `responses` when the managed backend is
|
|
127
|
+
desired.
|
|
128
|
+
|
|
81
129
|
## OpenAI Realtime example
|
|
82
130
|
|
|
83
131
|
The unified `/v1/realtime` route speaks the OpenAI Realtime GA JSON event protocol. It is native passthrough: send the same events you would send to the upstream Realtime API, but authenticate with the xAPI key.
|
|
@@ -121,15 +169,26 @@ Do not send the retired `OpenAI-Beta: realtime=v1` header. Session settings, aud
|
|
|
121
169
|
The browser `WebSocket` API cannot set arbitrary handshake headers. The Gateway accepts an xAPI key or temporary token through a subprotocol entry:
|
|
122
170
|
|
|
123
171
|
```javascript
|
|
124
|
-
const
|
|
172
|
+
const credential = await getEndpointBoundCredentialFromYourBackend(endpointId);
|
|
173
|
+
if (Date.now() >= new Date(credential.latestStartAt).getTime()) {
|
|
174
|
+
throw new Error("refresh the credential before opening a full new session");
|
|
175
|
+
}
|
|
125
176
|
const ws = new WebSocket(
|
|
126
|
-
"wss://
|
|
127
|
-
[`xapi-key.${
|
|
177
|
+
"wss://openai-live.p.xapi.to/v1/live/sessions",
|
|
178
|
+
["xapi-ws-v1", `xapi-key.${credential.token}`],
|
|
128
179
|
);
|
|
129
180
|
```
|
|
130
181
|
|
|
131
182
|
Never embed a long-lived xAPI key in frontend JavaScript. Use the authenticated xAPI Console Try-It flow or your backend to obtain a short-lived token, then pass only that token to the browser. The Console's `POST /api/keys/ws-token` flow mints a temporary token for a WebSocket endpoint; it requires a logged-in entity account and an endpoint ID, and is not authenticated with a normal xAPI key.
|
|
132
183
|
|
|
184
|
+
The credential is endpoint-bound and returns `expiresAt`, `latestStartAt`, and
|
|
185
|
+
`maxDurationSec`. It may be reused for reconnects only while the new connection
|
|
186
|
+
starts before `latestStartAt`; after that boundary, mint a fresh credential so
|
|
187
|
+
the full advertised session and final `session.close` exchange fit inside its
|
|
188
|
+
lifetime. If an endpoint declares public subprotocols, retain them and append
|
|
189
|
+
the `xapi-key.*` entry; otherwise use the non-secret `xapi-ws-v1` marker shown
|
|
190
|
+
above. Never log the secret subprotocol value.
|
|
191
|
+
|
|
133
192
|
If a browser integration must use `?token=`, use only a short-lived token and avoid logging the complete URL.
|
|
134
193
|
|
|
135
194
|
## Native protocol endpoints
|
|
@@ -138,6 +197,7 @@ The Gateway forwards frames without translating the application protocol. The se
|
|
|
138
197
|
|
|
139
198
|
| Adapter | Client frames | Important client requirement |
|
|
140
199
|
|---|---|---|
|
|
200
|
+
| `openai-live` | UTF-8 JSON text | First frame is `session.start`; choose Client or managed Responses delegation once. |
|
|
141
201
|
| `openai-realtime` | UTF-8 JSON text | Use OpenAI Realtime GA events. |
|
|
142
202
|
| `volcengine-asr` | Binary | Send the Volcengine ASR header/config/audio frame sequence; PCM configuration must match the audio bytes. |
|
|
143
203
|
| `doubao-realtime` | Binary | Use the Doubao end-to-end realtime dialogue protocol through its service host or exact endpoint. |
|