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
package/README.md
CHANGED
|
@@ -26,7 +26,8 @@ npx skills add xapi-labs/xapi-cli
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
This installs the bundled [`xapi` skill](skills/xapi), which teaches the agent
|
|
29
|
-
how to call social, search, crypto,
|
|
29
|
+
how to call social, search, domains/DNS, crypto, BlockPI RPC, Binance Web3, and
|
|
30
|
+
AI services through this CLI. Then just ask
|
|
30
31
|
— "what's the price of BTC" — and it takes it from there. Set up a key first;
|
|
31
32
|
see [Quick Start](#quick-start).
|
|
32
33
|
|
|
@@ -111,6 +112,13 @@ WebSocket client. Active SSE and raw downloads may run longer than 60 seconds,
|
|
|
111
112
|
but abort after 60 seconds without data by default. Set
|
|
112
113
|
`XAPI_TRANSFER_IDLE_TIMEOUT_MS` to change that idle timeout.
|
|
113
114
|
|
|
115
|
+
GPT Live is a WebSocket protocol and cannot be invoked with `xapi-to call`.
|
|
116
|
+
Read [the WebSocket Gateway guide](skills/xapi/guides/ws_gateway.md) and use a
|
|
117
|
+
real WebSocket client. The packaged
|
|
118
|
+
[`examples/openai-gpt-live-text.mjs`](examples/openai-gpt-live-text.mjs)
|
|
119
|
+
demonstrates `session.start`, managed Responses delegation, text events, and a
|
|
120
|
+
graceful `session.close` without placing an xAPI key in source or CLI arguments.
|
|
121
|
+
|
|
114
122
|
### Async Task Commands
|
|
115
123
|
|
|
116
124
|
Task helpers built on top of the `task.poll` capability.
|
|
@@ -121,6 +129,114 @@ xapi-to task wait 550e8400-e29b-41d4-a716-446655440000 # wait un
|
|
|
121
129
|
xapi-to task wait 550e8400-e29b-41d4-a716-446655440000 --interval 1s --timeout 10m
|
|
122
130
|
```
|
|
123
131
|
|
|
132
|
+
### Provider: Import → Configure → Submit → Wait
|
|
133
|
+
|
|
134
|
+
`provider` manages APIs owned by your account. It uses `XAPI_API_HOST`
|
|
135
|
+
(default `api.xapi.to`) and the same saved or environment API key as other
|
|
136
|
+
commands. In the xAPI Console API Keys settings, grant `service:create`,
|
|
137
|
+
`service:read`, `service:update`, and `service:publish`. Legacy `allowRegister`
|
|
138
|
+
only grants creation, not the remaining lifecycle permissions. Missing
|
|
139
|
+
permissions return a nonzero exit with the required scope.
|
|
140
|
+
|
|
141
|
+
Start from [examples/provider/openapi.json](examples/provider/openapi.json),
|
|
142
|
+
replace its upstream URL, service details, and endpoint contract, then run:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Inspect current rules; no API key required
|
|
146
|
+
xapi-to provider spec-rules --format pretty
|
|
147
|
+
|
|
148
|
+
# Import a raw OpenAPI 3.0.3 JSON object (not a {openApiSpec: ...} envelope)
|
|
149
|
+
xapi-to provider import --file openapi.json > imported.json
|
|
150
|
+
|
|
151
|
+
# Use the serviceId and revisionId from imported.json (jq is optional)
|
|
152
|
+
PROVIDER_SERVICE_ID=$(jq -er '.serviceId' imported.json)
|
|
153
|
+
PROVIDER_REVISION_ID=$(jq -er '.revisionId' imported.json)
|
|
154
|
+
|
|
155
|
+
# Save version configuration to move the draft revision to SANDBOX.
|
|
156
|
+
# config.json can be {"description":"Initial release"} when the imported
|
|
157
|
+
# endpoints, authentication, and pricing are already complete.
|
|
158
|
+
xapi-to provider update "$PROVIDER_SERVICE_ID" \
|
|
159
|
+
--revision "$PROVIDER_REVISION_ID" --file config.json
|
|
160
|
+
|
|
161
|
+
# Submit the specified revision, then wait for the actual publication result
|
|
162
|
+
xapi-to provider submit "$PROVIDER_SERVICE_ID" \
|
|
163
|
+
--revision "$PROVIDER_REVISION_ID" --changelog "Initial release"
|
|
164
|
+
xapi-to provider wait "$PROVIDER_SERVICE_ID" \
|
|
165
|
+
--revision "$PROVIDER_REVISION_ID" --interval 2s --timeout 10m
|
|
166
|
+
|
|
167
|
+
# Inspect owned services, configuration, version overview, or review reports
|
|
168
|
+
xapi-to provider list --format table
|
|
169
|
+
xapi-to provider get "$PROVIDER_SERVICE_ID" --format pretty
|
|
170
|
+
xapi-to provider versions "$PROVIDER_SERVICE_ID" --format pretty
|
|
171
|
+
xapi-to provider review "$PROVIDER_SERVICE_ID" --revision "$PROVIDER_REVISION_ID"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
When scripting these steps, stop on nonzero exit (for example, use `set -e`).
|
|
175
|
+
Import returns the backend validation/preview plus `serviceId`, `revisionId`,
|
|
176
|
+
and `state`. An HTTP 201 with `success: false` is a validation failure and exits
|
|
177
|
+
nonzero; its structured validation errors are preserved. Registration creates
|
|
178
|
+
a new service each time. If a response is lost, inspect `provider list` before
|
|
179
|
+
retrying to avoid duplicate services.
|
|
180
|
+
|
|
181
|
+
For an authenticated upstream, store credentials in a local JSON object such
|
|
182
|
+
as `{"Authorization":"Bearer YOUR_UPSTREAM_KEY"}` and pass
|
|
183
|
+
`--private-headers-file private-headers.json` to `provider import`. Keep this
|
|
184
|
+
file out of version control. `--file -` and `--private-headers-file -` accept
|
|
185
|
+
stdin, but only one input can consume stdin per command. Files must be JSON;
|
|
186
|
+
YAML and URL imports are not supported in this command group.
|
|
187
|
+
|
|
188
|
+
`provider update --revision <id>` reads a version configuration object, using the backend
|
|
189
|
+
fields `description`, `baseUrl`, `baseUrls`, `authType`, `privateHeaders`,
|
|
190
|
+
`authConfig`, `openApiSpec`, `endpoints`, and `status`. Prefer structured
|
|
191
|
+
`privateHeaders` for upstream credentials. Endpoint fields include billing
|
|
192
|
+
configuration such as `billingType` and `costPerCall`. Update does not accept
|
|
193
|
+
a raw OpenAPI document; the nested backend field is `openApiSpec: {spec: ...}`.
|
|
194
|
+
Saving that field alone does not re-import endpoint definitions; configure
|
|
195
|
+
`endpoints` explicitly when changing the contract.
|
|
196
|
+
|
|
197
|
+
- `--mode merge` (default) sends PATCH and preserves omitted fields/endpoints.
|
|
198
|
+
Existing endpoint edits require `id`, e.g.
|
|
199
|
+
`{"endpoints":[{"id":"ENDPOINT_ID","costPerCall":"0.002"}]}`.
|
|
200
|
+
- `--allow-new-endpoints` explicitly permits ID-less merge entries to create
|
|
201
|
+
endpoints. Repeating such a merge can create duplicates.
|
|
202
|
+
- `--mode replace` sends PUT. If `endpoints` is provided, it replaces the
|
|
203
|
+
endpoint list; include every endpoint you intend to keep. Omitted fields
|
|
204
|
+
otherwise follow backend PUT semantics. Use full configuration for replacement.
|
|
205
|
+
|
|
206
|
+
`get` retains its existing service response; `--version v1.0` selects the
|
|
207
|
+
configuration returned by the backend. Find endpoint IDs in
|
|
208
|
+
`currentVersion.endpoints`; `provider versions` returns working revision IDs
|
|
209
|
+
in `majors[].working.id`. For an already-published API, use the existing
|
|
210
|
+
`provider revision start <service-id> <major>` command to create a working revision.
|
|
211
|
+
`provider update` without `--revision` continues to update service metadata
|
|
212
|
+
and rate limits. Existing `version update`, `publish`, and positional `review`
|
|
213
|
+
commands remain available. The `submit` and `review --revision` forms are
|
|
214
|
+
additional onboarding commands.
|
|
215
|
+
|
|
216
|
+
Updates to `IN_REVIEW`, `PUBLISHED`, or `SUSPENDED` revisions return a conflict;
|
|
217
|
+
the backend enforces this check under a transaction lock. When updating
|
|
218
|
+
`privateHeaders`, send the complete desired map: it replaces the old map and
|
|
219
|
+
rebuilds the derived authentication configuration. An empty map without an
|
|
220
|
+
explicit `authConfig` clears those credentials. Omitting both fields preserves them.
|
|
221
|
+
|
|
222
|
+
`submit` returns `{serviceId, revisionId, submission}`. A successful submission
|
|
223
|
+
does not guarantee publication. `wait` checks the requested revision, succeeds
|
|
224
|
+
only for `PUBLISHED`, and outputs the review report with `success` and `reason`.
|
|
225
|
+
`--changelog` is limited to 2,000 characters by both the CLI and backend.
|
|
226
|
+
Rejection, a draft/sandbox/suspended revision, or a legacy manual-review hold
|
|
227
|
+
exit nonzero. Pending review continues until publication, the timeout (default
|
|
228
|
+
10 minutes), or optional `--max-attempts`. Timeout and attempt-limit results
|
|
229
|
+
include the last received report; polling can be resumed with the same IDs.
|
|
230
|
+
The deadline also bounds in-flight HTTP requests and retry delays.
|
|
231
|
+
|
|
232
|
+
Reads retry transient errors; writes are never automatically retried. The CLI
|
|
233
|
+
redacts credential fields and known credential values from provider output.
|
|
234
|
+
Redacted reads are for inspection and must not be submitted unchanged as
|
|
235
|
+
configuration. After an ambiguous write failure, use `list`, `get`, or `review`
|
|
236
|
+
to inspect the result before repeating the operation. No npm release is implied
|
|
237
|
+
by a local source checkout; use `bun run src/index.ts provider ...` or build and
|
|
238
|
+
run `node dist/index.js provider ...` while testing unreleased changes.
|
|
239
|
+
|
|
124
240
|
### Sandbox Commands
|
|
125
241
|
|
|
126
242
|
Sandbox commands provide an AI-friendly cloud computer lifecycle. The fastest
|
|
@@ -327,11 +443,58 @@ xapi-to register --referral-code xapito # register with an invit
|
|
|
327
443
|
xapi-to register xapito # positional shorthand for --referral-code
|
|
328
444
|
xapi-to register --force # replace an existing file-based key
|
|
329
445
|
xapi-to balance # show USD balance
|
|
446
|
+
xapi-to usage <request-id> # finalized cost + balance-after receipt
|
|
447
|
+
xapi-to usage wait <request-id> --timeout 1m # poll until a streaming receipt is finalized
|
|
448
|
+
xapi-to earnings # spendable balance + provider earnings
|
|
449
|
+
xapi-to earnings list --status SETTLED --limit 20 # provider earning records
|
|
450
|
+
xapi-to earnings transfer 1 --idempotency-key reinvest-001 # reinvest settled earnings
|
|
330
451
|
xapi-to topup # generate payment URL
|
|
331
452
|
xapi-to topup --method stripe --amount 10 # stripe, $10
|
|
332
453
|
xapi-to topup --method x402 # x402 (USDC on Base)
|
|
333
454
|
```
|
|
334
455
|
|
|
456
|
+
`earnings` summary/list require the `earnings:read` scope on the current key;
|
|
457
|
+
`earnings transfer` requires `earnings:transfer`. Transfers are one-way and
|
|
458
|
+
idempotent: reuse a key only when retrying the same amount.
|
|
459
|
+
|
|
460
|
+
### Provider Management
|
|
461
|
+
|
|
462
|
+
Provider commands use scoped `XAPI-KEY` routes for service and release
|
|
463
|
+
management without a JWT exchange:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
xapi-to provider list
|
|
467
|
+
xapi-to provider create --file ./service.json
|
|
468
|
+
xapi-to provider update <service-id> --about-file ./ABOUT.md --website https://example.com
|
|
469
|
+
xapi-to provider update <service-id> --rate-limit-requests 100 --rate-limit-period-seconds 60
|
|
470
|
+
xapi-to provider update <service-id> --clear-rate-limit
|
|
471
|
+
xapi-to provider versions <service-id>
|
|
472
|
+
xapi-to provider revision start <service-id> 1
|
|
473
|
+
xapi-to provider version update <service-id> <version-id> --file ./contract.json
|
|
474
|
+
xapi-to provider diff <service-id> 1
|
|
475
|
+
xapi-to provider publish <service-id> <revision-id> --changelog-file ./CHANGELOG.md
|
|
476
|
+
xapi-to provider metrics <service-id> --days 7
|
|
477
|
+
xapi-to provider events --after '<opaque-next-cursor>'
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Service rate limits are optional and supported only for proxied services. Both
|
|
481
|
+
numeric flags are required when setting a limit; the quota is shared by all API
|
|
482
|
+
keys belonging to the same user for that service.
|
|
483
|
+
|
|
484
|
+
Service usage tutorials are Skill packages. Scaffold one from the serving
|
|
485
|
+
contract, submit it for review, wait for publication, then link it:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
xapi-to provider skill scaffold <service-id> --output ./my-skill/SKILL.md
|
|
489
|
+
xapi-to skill submit --dir ./my-skill
|
|
490
|
+
xapi-to skill wait <submission-id> --timeout 10m
|
|
491
|
+
xapi-to provider skill link <service-id> <skill-id>
|
|
492
|
+
xapi-to provider skill fingerprint <service-id> --skill-version-id <version-id>
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Run `xapi-to provider --help` and `xapi-to skill --help` for the complete
|
|
496
|
+
lifecycle, rollback, deletion, GitHub import, scope, and safety options.
|
|
497
|
+
|
|
335
498
|
### Config
|
|
336
499
|
|
|
337
500
|
```bash
|
|
@@ -265,8 +265,25 @@ function parseRetryAfterMs(res) {
|
|
|
265
265
|
const at = Date.parse(header);
|
|
266
266
|
return Number.isFinite(at) ? Math.max(0, at - Date.now()) : void 0;
|
|
267
267
|
}
|
|
268
|
-
function
|
|
269
|
-
|
|
268
|
+
function abortError(signal) {
|
|
269
|
+
const reason = signal?.reason;
|
|
270
|
+
return reason instanceof Error ? reason : new DOMException("The operation was aborted", "AbortError");
|
|
271
|
+
}
|
|
272
|
+
function sleep(ms, signal) {
|
|
273
|
+
if (signal?.aborted) return Promise.reject(abortError(signal));
|
|
274
|
+
return new Promise((resolve2, reject) => {
|
|
275
|
+
let timer;
|
|
276
|
+
const onAbort = () => {
|
|
277
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
278
|
+
signal?.removeEventListener("abort", onAbort);
|
|
279
|
+
reject(abortError(signal));
|
|
280
|
+
};
|
|
281
|
+
timer = setTimeout(() => {
|
|
282
|
+
signal?.removeEventListener("abort", onAbort);
|
|
283
|
+
resolve2();
|
|
284
|
+
}, ms);
|
|
285
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
286
|
+
});
|
|
270
287
|
}
|
|
271
288
|
async function request(url, options, timeoutMs = DEFAULT_TIMEOUT_MS, retries = 0) {
|
|
272
289
|
assertAllowedHost(url);
|
|
@@ -294,7 +311,7 @@ async function request(url, options, timeoutMs = DEFAULT_TIMEOUT_MS, retries = 0
|
|
|
294
311
|
if (isRetryableStatus(res.status) && attempt < retries) {
|
|
295
312
|
await res.text().catch(() => "");
|
|
296
313
|
clearTimeout(timer);
|
|
297
|
-
await sleep(backoffDelayMs(attempt, retryAfterMs));
|
|
314
|
+
await sleep(backoffDelayMs(attempt, retryAfterMs), callerSignal);
|
|
298
315
|
attempt++;
|
|
299
316
|
continue;
|
|
300
317
|
}
|
|
@@ -327,7 +344,7 @@ async function request(url, options, timeoutMs = DEFAULT_TIMEOUT_MS, retries = 0
|
|
|
327
344
|
if (timedOut) {
|
|
328
345
|
const timeoutError = new RequestTimeoutError(timeoutMs);
|
|
329
346
|
if (attempt < retries) {
|
|
330
|
-
await sleep(backoffDelayMs(attempt));
|
|
347
|
+
await sleep(backoffDelayMs(attempt), callerSignal);
|
|
331
348
|
attempt++;
|
|
332
349
|
continue;
|
|
333
350
|
}
|
|
@@ -335,7 +352,7 @@ async function request(url, options, timeoutMs = DEFAULT_TIMEOUT_MS, retries = 0
|
|
|
335
352
|
}
|
|
336
353
|
if (isRetryableNetworkError(e) && attempt < retries) {
|
|
337
354
|
clearTimeout(timer);
|
|
338
|
-
await sleep(backoffDelayMs(attempt));
|
|
355
|
+
await sleep(backoffDelayMs(attempt), callerSignal);
|
|
339
356
|
attempt++;
|
|
340
357
|
continue;
|
|
341
358
|
}
|
|
@@ -351,6 +368,21 @@ function headers(apiKey) {
|
|
|
351
368
|
if (apiKey) h["XAPI-Key"] = apiKey;
|
|
352
369
|
return h;
|
|
353
370
|
}
|
|
371
|
+
function apiKeyApiRequest(apiHost, apiKey, path, options = {}) {
|
|
372
|
+
const method = options.method ?? "GET";
|
|
373
|
+
const requestHeaders = { "XAPI-KEY": apiKey };
|
|
374
|
+
if (options.body !== void 0) requestHeaders["Content-Type"] = "application/json";
|
|
375
|
+
return request(
|
|
376
|
+
`${scheme(apiHost)}://${apiHost}${path}`,
|
|
377
|
+
{
|
|
378
|
+
method,
|
|
379
|
+
headers: requestHeaders,
|
|
380
|
+
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {}
|
|
381
|
+
},
|
|
382
|
+
options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
383
|
+
options.retries ?? 0
|
|
384
|
+
);
|
|
385
|
+
}
|
|
354
386
|
function baseUrl(opts) {
|
|
355
387
|
return `${scheme(opts.actionHost)}://${opts.actionHost}`;
|
|
356
388
|
}
|
|
@@ -657,10 +689,10 @@ async function initiateOAuth(apiKeyId, providerId, jwtToken, apiHost, scopes) {
|
|
|
657
689
|
}
|
|
658
690
|
);
|
|
659
691
|
}
|
|
660
|
-
async function listOAuthBindings(jwtToken, apiHost) {
|
|
692
|
+
async function listOAuthBindings(jwtToken, apiHost, signal) {
|
|
661
693
|
return request(
|
|
662
694
|
`${scheme(apiHost)}://${apiHost}/api/oauth/bindings`,
|
|
663
|
-
{ method: "GET", headers: jwtHeaders(jwtToken) },
|
|
695
|
+
{ method: "GET", headers: jwtHeaders(jwtToken), signal },
|
|
664
696
|
DEFAULT_TIMEOUT_MS,
|
|
665
697
|
IDEMPOTENT_RETRIES
|
|
666
698
|
);
|
|
@@ -794,24 +826,43 @@ var sandboxAudit = (opts, id, kind, page = 1, pageSize = 100) => sandboxRequest(
|
|
|
794
826
|
);
|
|
795
827
|
async function sandboxWait(opts, id, wanted, timeoutMs = 3e5, intervalMs = 2e3, signal) {
|
|
796
828
|
const deadline = Date.now() + timeoutMs;
|
|
829
|
+
const deadlineController = new AbortController();
|
|
830
|
+
const abortFromCaller = () => deadlineController.abort();
|
|
831
|
+
const deadlineTimer = setTimeout(() => deadlineController.abort(), Math.max(0, timeoutMs));
|
|
832
|
+
if (signal?.aborted) deadlineController.abort();
|
|
833
|
+
else signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
797
834
|
let last;
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
835
|
+
try {
|
|
836
|
+
while (Date.now() < deadline) {
|
|
837
|
+
if (signal?.aborted) throw new Error(`sandbox wait interrupted while waiting for ${wanted.join(" or ")}`);
|
|
838
|
+
try {
|
|
839
|
+
last = await sandboxGet(opts, id, deadlineController.signal);
|
|
840
|
+
} catch (error) {
|
|
841
|
+
if (signal?.aborted) {
|
|
842
|
+
throw new Error(`sandbox wait interrupted while waiting for ${wanted.join(" or ")}`);
|
|
843
|
+
}
|
|
844
|
+
if (Date.now() >= deadline) break;
|
|
845
|
+
throw error;
|
|
846
|
+
}
|
|
847
|
+
if (Date.now() >= deadline) break;
|
|
848
|
+
const state = String(last.observedState || "");
|
|
849
|
+
if (wanted.includes(state)) return last;
|
|
850
|
+
if (["FAILED", "TERMINATED"].includes(state) && !wanted.includes(state)) {
|
|
851
|
+
throw new Error(`sandbox ${id} entered ${state} while waiting for ${wanted.join(" or ")}`);
|
|
852
|
+
}
|
|
853
|
+
await new Promise((resolve2) => {
|
|
854
|
+
const done = () => {
|
|
855
|
+
clearTimeout(timer);
|
|
856
|
+
signal?.removeEventListener("abort", done);
|
|
857
|
+
resolve2();
|
|
858
|
+
};
|
|
859
|
+
const timer = setTimeout(done, Math.min(intervalMs, Math.max(0, deadline - Date.now())));
|
|
860
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
861
|
+
});
|
|
805
862
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
signal?.removeEventListener("abort", done);
|
|
810
|
-
resolve2();
|
|
811
|
-
};
|
|
812
|
-
const timer = setTimeout(done, Math.min(intervalMs, Math.max(0, deadline - Date.now())));
|
|
813
|
-
signal?.addEventListener("abort", done, { once: true });
|
|
814
|
-
});
|
|
863
|
+
} finally {
|
|
864
|
+
clearTimeout(deadlineTimer);
|
|
865
|
+
signal?.removeEventListener("abort", abortFromCaller);
|
|
815
866
|
}
|
|
816
867
|
throw new Error(
|
|
817
868
|
`sandbox ${id} did not enter ${wanted.join(" or ")} within ${timeoutMs}ms (last state: ${last?.observedState || "unknown"})`
|
|
@@ -835,6 +886,7 @@ export {
|
|
|
835
886
|
HttpError,
|
|
836
887
|
isRetryableRequestError,
|
|
837
888
|
request,
|
|
889
|
+
apiKeyApiRequest,
|
|
838
890
|
actionList,
|
|
839
891
|
actionSearch,
|
|
840
892
|
actionCategories,
|