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/src/client.ts
CHANGED
|
@@ -26,6 +26,13 @@ export interface ClientOptions {
|
|
|
26
26
|
apiKey?: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export interface ApiKeyApiRequestOptions {
|
|
30
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
31
|
+
body?: unknown;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
retries?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
export class HttpError extends Error {
|
|
30
37
|
constructor(
|
|
31
38
|
public readonly status: number,
|
|
@@ -100,8 +107,28 @@ function parseRetryAfterMs(res: Response): number | undefined {
|
|
|
100
107
|
return Number.isFinite(at) ? Math.max(0, at - Date.now()) : undefined;
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
function
|
|
104
|
-
|
|
110
|
+
function abortError(signal?: AbortSignal | null): Error {
|
|
111
|
+
const reason = signal?.reason;
|
|
112
|
+
return reason instanceof Error
|
|
113
|
+
? reason
|
|
114
|
+
: new DOMException('The operation was aborted', 'AbortError');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function sleep(ms: number, signal?: AbortSignal | null): Promise<void> {
|
|
118
|
+
if (signal?.aborted) return Promise.reject(abortError(signal));
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
121
|
+
const onAbort = () => {
|
|
122
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
123
|
+
signal?.removeEventListener('abort', onAbort);
|
|
124
|
+
reject(abortError(signal));
|
|
125
|
+
};
|
|
126
|
+
timer = setTimeout(() => {
|
|
127
|
+
signal?.removeEventListener('abort', onAbort);
|
|
128
|
+
resolve();
|
|
129
|
+
}, ms);
|
|
130
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
131
|
+
});
|
|
105
132
|
}
|
|
106
133
|
|
|
107
134
|
export async function request<T>(
|
|
@@ -138,7 +165,7 @@ export async function request<T>(
|
|
|
138
165
|
if (isRetryableStatus(res.status) && attempt < retries) {
|
|
139
166
|
await res.text().catch(() => ''); // drain body so the socket can be reused
|
|
140
167
|
clearTimeout(timer);
|
|
141
|
-
await sleep(backoffDelayMs(attempt, retryAfterMs));
|
|
168
|
+
await sleep(backoffDelayMs(attempt, retryAfterMs), callerSignal);
|
|
142
169
|
attempt++;
|
|
143
170
|
continue;
|
|
144
171
|
}
|
|
@@ -174,7 +201,7 @@ export async function request<T>(
|
|
|
174
201
|
if (timedOut) {
|
|
175
202
|
const timeoutError = new RequestTimeoutError(timeoutMs);
|
|
176
203
|
if (attempt < retries) {
|
|
177
|
-
await sleep(backoffDelayMs(attempt));
|
|
204
|
+
await sleep(backoffDelayMs(attempt), callerSignal);
|
|
178
205
|
attempt++;
|
|
179
206
|
continue;
|
|
180
207
|
}
|
|
@@ -182,7 +209,7 @@ export async function request<T>(
|
|
|
182
209
|
}
|
|
183
210
|
if (isRetryableNetworkError(e) && attempt < retries) {
|
|
184
211
|
clearTimeout(timer);
|
|
185
|
-
await sleep(backoffDelayMs(attempt));
|
|
212
|
+
await sleep(backoffDelayMs(attempt), callerSignal);
|
|
186
213
|
attempt++;
|
|
187
214
|
continue;
|
|
188
215
|
}
|
|
@@ -200,6 +227,30 @@ function headers(apiKey?: string): Record<string, string> {
|
|
|
200
227
|
return h;
|
|
201
228
|
}
|
|
202
229
|
|
|
230
|
+
/** Call a scoped API-Key control-plane endpoint without exchanging for a JWT. */
|
|
231
|
+
export function apiKeyApiRequest<T>(
|
|
232
|
+
apiHost: string,
|
|
233
|
+
apiKey: string,
|
|
234
|
+
path: string,
|
|
235
|
+
options: ApiKeyApiRequestOptions = {},
|
|
236
|
+
): Promise<T> {
|
|
237
|
+
const method = options.method ?? 'GET';
|
|
238
|
+
const requestHeaders: Record<string, string> = { 'XAPI-KEY': apiKey };
|
|
239
|
+
if (options.body !== undefined) requestHeaders['Content-Type'] = 'application/json';
|
|
240
|
+
return request<T>(
|
|
241
|
+
`${scheme(apiHost)}://${apiHost}${path}`,
|
|
242
|
+
{
|
|
243
|
+
method,
|
|
244
|
+
headers: requestHeaders,
|
|
245
|
+
...(options.body !== undefined
|
|
246
|
+
? { body: JSON.stringify(options.body) }
|
|
247
|
+
: {}),
|
|
248
|
+
},
|
|
249
|
+
options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
250
|
+
options.retries ?? 0,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
203
254
|
function baseUrl(opts: ClientOptions): string {
|
|
204
255
|
return `${scheme(opts.actionHost)}://${opts.actionHost}`;
|
|
205
256
|
}
|
|
@@ -632,7 +683,11 @@ export async function initiateOAuth(
|
|
|
632
683
|
);
|
|
633
684
|
}
|
|
634
685
|
|
|
635
|
-
export async function listOAuthBindings(
|
|
686
|
+
export async function listOAuthBindings(
|
|
687
|
+
jwtToken: string,
|
|
688
|
+
apiHost: string,
|
|
689
|
+
signal?: AbortSignal,
|
|
690
|
+
) {
|
|
636
691
|
return request<Array<{
|
|
637
692
|
id: string;
|
|
638
693
|
apiKeyId: string;
|
|
@@ -645,7 +700,7 @@ export async function listOAuthBindings(jwtToken: string, apiHost: string) {
|
|
|
645
700
|
provider: { id: string; name: string; type: string };
|
|
646
701
|
}>>(
|
|
647
702
|
`${scheme(apiHost)}://${apiHost}/api/oauth/bindings`,
|
|
648
|
-
{ method: 'GET', headers: jwtHeaders(jwtToken) },
|
|
703
|
+
{ method: 'GET', headers: jwtHeaders(jwtToken), signal },
|
|
649
704
|
DEFAULT_TIMEOUT_MS,
|
|
650
705
|
IDEMPOTENT_RETRIES,
|
|
651
706
|
);
|
package/src/sandbox-client.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface SandboxRequirements {
|
|
|
26
26
|
gpu?: { count?: number; model?: string };
|
|
27
27
|
regions?: string[];
|
|
28
28
|
capabilities?: string[];
|
|
29
|
+
minContinuousRuntimeSeconds?: number;
|
|
29
30
|
[key: string]: unknown;
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -263,24 +264,43 @@ export async function sandboxWait(
|
|
|
263
264
|
signal?: AbortSignal,
|
|
264
265
|
): Promise<SandboxDetail> {
|
|
265
266
|
const deadline = Date.now() + timeoutMs;
|
|
267
|
+
const deadlineController = new AbortController();
|
|
268
|
+
const abortFromCaller = () => deadlineController.abort();
|
|
269
|
+
const deadlineTimer = setTimeout(() => deadlineController.abort(), Math.max(0, timeoutMs));
|
|
270
|
+
if (signal?.aborted) deadlineController.abort();
|
|
271
|
+
else signal?.addEventListener('abort', abortFromCaller, { once: true });
|
|
266
272
|
let last: SandboxDetail | undefined;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
try {
|
|
274
|
+
while (Date.now() < deadline) {
|
|
275
|
+
if (signal?.aborted) throw new Error(`sandbox wait interrupted while waiting for ${wanted.join(' or ')}`);
|
|
276
|
+
try {
|
|
277
|
+
last = await sandboxGet(opts, id, deadlineController.signal);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (signal?.aborted) {
|
|
280
|
+
throw new Error(`sandbox wait interrupted while waiting for ${wanted.join(' or ')}`);
|
|
281
|
+
}
|
|
282
|
+
if (Date.now() >= deadline) break;
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
if (Date.now() >= deadline) break;
|
|
286
|
+
const state = String(last.observedState || '');
|
|
287
|
+
if (wanted.includes(state)) return last;
|
|
288
|
+
if (['FAILED', 'TERMINATED'].includes(state) && !wanted.includes(state)) {
|
|
289
|
+
throw new Error(`sandbox ${id} entered ${state} while waiting for ${wanted.join(' or ')}`);
|
|
290
|
+
}
|
|
291
|
+
await new Promise<void>((resolve) => {
|
|
292
|
+
const done = () => {
|
|
293
|
+
clearTimeout(timer);
|
|
294
|
+
signal?.removeEventListener('abort', done);
|
|
295
|
+
resolve();
|
|
296
|
+
};
|
|
297
|
+
const timer = setTimeout(done, Math.min(intervalMs, Math.max(0, deadline - Date.now())));
|
|
298
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
299
|
+
});
|
|
274
300
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
signal?.removeEventListener('abort', done);
|
|
279
|
-
resolve();
|
|
280
|
-
};
|
|
281
|
-
const timer = setTimeout(done, Math.min(intervalMs, Math.max(0, deadline - Date.now())));
|
|
282
|
-
signal?.addEventListener('abort', done, { once: true });
|
|
283
|
-
});
|
|
301
|
+
} finally {
|
|
302
|
+
clearTimeout(deadlineTimer);
|
|
303
|
+
signal?.removeEventListener('abort', abortFromCaller);
|
|
284
304
|
}
|
|
285
305
|
throw new Error(
|
|
286
306
|
`sandbox ${id} did not enter ${wanted.join(' or ')} within ${timeoutMs}ms` +
|