@sylphx/sdk 0.3.2 → 0.3.3
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 +15 -5
- package/dist/index.d.cts +16908 -5920
- package/dist/index.d.ts +16908 -5920
- package/dist/index.js +179 -145
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +179 -142
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.d.cts +5378 -1202
- package/dist/nextjs/index.d.ts +5378 -1202
- package/dist/nextjs/index.js +78 -37
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/index.mjs +76 -37
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/react/index.d.cts +5353 -1212
- package/dist/react/index.d.ts +5353 -1212
- package/dist/react/index.js +118 -96
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +118 -96
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.cts +14368 -3312
- package/dist/server/index.d.ts +14368 -3312
- package/dist/server/index.js +82 -47
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +80 -47
- package/dist/server/index.mjs.map +1 -1
- package/dist/web-analytics.js +1 -1
- package/dist/web-analytics.js.map +1 -1
- package/dist/web-analytics.mjs +1 -1
- package/dist/web-analytics.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -49,7 +49,6 @@ __export(index_exports, {
|
|
|
49
49
|
WorkersClient: () => WorkersClient,
|
|
50
50
|
acceptAllConsents: () => acceptAllConsents,
|
|
51
51
|
acceptOrganizationInvitation: () => acceptOrganizationInvitation,
|
|
52
|
-
addCustomDomain: () => addCustomDomain,
|
|
53
52
|
batchIndex: () => batchIndex,
|
|
54
53
|
canDeleteOrganization: () => canDeleteOrganization,
|
|
55
54
|
canManageMembers: () => canManageMembers,
|
|
@@ -184,7 +183,6 @@ __export(index_exports, {
|
|
|
184
183
|
kvZrange: () => kvZrange,
|
|
185
184
|
leaveOrganization: () => leaveOrganization,
|
|
186
185
|
linkAnonymousConsents: () => linkAnonymousConsents,
|
|
187
|
-
listCustomDomains: () => listCustomDomains,
|
|
188
186
|
listEnvVars: () => listEnvVars,
|
|
189
187
|
listScheduledEmails: () => listScheduledEmails,
|
|
190
188
|
listSecretKeys: () => listSecretKeys,
|
|
@@ -200,7 +198,6 @@ __export(index_exports, {
|
|
|
200
198
|
regenerateReferralCode: () => regenerateReferralCode,
|
|
201
199
|
registerPush: () => registerPush,
|
|
202
200
|
registerPushServiceWorker: () => registerPushServiceWorker,
|
|
203
|
-
removeCustomDomain: () => removeCustomDomain,
|
|
204
201
|
removeOrganizationMember: () => removeOrganizationMember,
|
|
205
202
|
replayWebhookDelivery: () => replayWebhookDelivery,
|
|
206
203
|
rescheduleEmail: () => rescheduleEmail,
|
|
@@ -251,9 +248,7 @@ __export(index_exports, {
|
|
|
251
248
|
module.exports = __toCommonJS(index_exports);
|
|
252
249
|
|
|
253
250
|
// src/constants.ts
|
|
254
|
-
var
|
|
255
|
-
var SDK_API_PATH = `/api/v1`;
|
|
256
|
-
var SDK_API_PATH_NEW = `/v1`;
|
|
251
|
+
var SDK_API_PATH = `/v1`;
|
|
257
252
|
var DEFAULT_SDK_API_HOST = "api.sylphx.com";
|
|
258
253
|
var SDK_VERSION = "0.1.0";
|
|
259
254
|
var SDK_PLATFORM = typeof window !== "undefined" ? "browser" : typeof process !== "undefined" && process.versions?.node ? "node" : "unknown";
|
|
@@ -761,32 +756,18 @@ function createConfig(input) {
|
|
|
761
756
|
}
|
|
762
757
|
secretKey = result.sanitizedKey;
|
|
763
758
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
let platformUrl;
|
|
774
|
-
let apiBasePath;
|
|
775
|
-
if (input.platformUrl) {
|
|
776
|
-
platformUrl = input.platformUrl.trim();
|
|
777
|
-
apiBasePath = SDK_API_PATH;
|
|
778
|
-
} else if (input.ref) {
|
|
779
|
-
platformUrl = `https://${input.ref.trim()}.${DEFAULT_SDK_API_HOST}`;
|
|
780
|
-
apiBasePath = SDK_API_PATH_NEW;
|
|
781
|
-
} else {
|
|
782
|
-
platformUrl = DEFAULT_PLATFORM_URL;
|
|
783
|
-
apiBasePath = SDK_API_PATH;
|
|
759
|
+
const trimmedRef = input.ref.trim();
|
|
760
|
+
if (!REF_PATTERN.test(trimmedRef)) {
|
|
761
|
+
throw new SylphxError(
|
|
762
|
+
`[Sylphx] Invalid project ref format: "${input.ref}". Expected a 16-character lowercase alphanumeric string (e.g. "abc123def456ghij"). Get your ref from Platform Console \u2192 Projects \u2192 Your Project \u2192 Overview.`,
|
|
763
|
+
{ code: "BAD_REQUEST" }
|
|
764
|
+
);
|
|
784
765
|
}
|
|
766
|
+
const baseUrl = `https://${trimmedRef}.${DEFAULT_SDK_API_HOST}${SDK_API_PATH}`;
|
|
785
767
|
return Object.freeze({
|
|
786
768
|
secretKey,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
apiBasePath,
|
|
769
|
+
ref: trimmedRef,
|
|
770
|
+
baseUrl,
|
|
790
771
|
accessToken: input.accessToken
|
|
791
772
|
});
|
|
792
773
|
}
|
|
@@ -794,7 +775,7 @@ function withToken(config, accessToken) {
|
|
|
794
775
|
return Object.freeze({
|
|
795
776
|
...config,
|
|
796
777
|
accessToken
|
|
797
|
-
// Preserve
|
|
778
|
+
// Preserve baseUrl and ref from original config
|
|
798
779
|
});
|
|
799
780
|
}
|
|
800
781
|
function buildHeaders(config) {
|
|
@@ -810,9 +791,9 @@ function buildHeaders(config) {
|
|
|
810
791
|
return headers;
|
|
811
792
|
}
|
|
812
793
|
function buildApiUrl(config, path) {
|
|
813
|
-
const base = config.
|
|
794
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
814
795
|
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
815
|
-
return `${base}${
|
|
796
|
+
return `${base}${cleanPath}`;
|
|
816
797
|
}
|
|
817
798
|
async function callApi(config, path, options = {}) {
|
|
818
799
|
const {
|
|
@@ -1366,7 +1347,7 @@ function createRetryMiddleware(retryConfig) {
|
|
|
1366
1347
|
function validateClientConfig(config) {
|
|
1367
1348
|
return {
|
|
1368
1349
|
secretKey: validateAndSanitizeSecretKey(config.secretKey),
|
|
1369
|
-
baseUrl: (config.platformUrl ||
|
|
1350
|
+
baseUrl: (config.platformUrl || `https://${DEFAULT_SDK_API_HOST}`).trim()
|
|
1370
1351
|
};
|
|
1371
1352
|
}
|
|
1372
1353
|
function createRestClient(config) {
|
|
@@ -1492,7 +1473,7 @@ async function verifyTwoFactor(config, userId, code) {
|
|
|
1492
1473
|
});
|
|
1493
1474
|
}
|
|
1494
1475
|
async function introspectToken(config, token, tokenTypeHint) {
|
|
1495
|
-
const response = await fetch(
|
|
1476
|
+
const response = await fetch(buildApiUrl(config, "/auth/introspect"), {
|
|
1496
1477
|
method: "POST",
|
|
1497
1478
|
headers: { "Content-Type": "application/json" },
|
|
1498
1479
|
body: JSON.stringify({
|
|
@@ -1507,7 +1488,7 @@ async function introspectToken(config, token, tokenTypeHint) {
|
|
|
1507
1488
|
return response.json();
|
|
1508
1489
|
}
|
|
1509
1490
|
async function revokeToken(config, token, options) {
|
|
1510
|
-
await fetch(
|
|
1491
|
+
await fetch(buildApiUrl(config, "/auth/revoke"), {
|
|
1511
1492
|
method: "POST",
|
|
1512
1493
|
headers: { "Content-Type": "application/json" },
|
|
1513
1494
|
body: JSON.stringify({
|
|
@@ -1658,7 +1639,7 @@ function createTracker(config, defaultAnonymousId) {
|
|
|
1658
1639
|
|
|
1659
1640
|
// src/ai.ts
|
|
1660
1641
|
async function chat(config, input) {
|
|
1661
|
-
const response = await fetch(
|
|
1642
|
+
const response = await fetch(buildApiUrl(config, "/chat/completions"), {
|
|
1662
1643
|
method: "POST",
|
|
1663
1644
|
headers: {
|
|
1664
1645
|
...buildHeaders(config),
|
|
@@ -1706,7 +1687,7 @@ async function chat(config, input) {
|
|
|
1706
1687
|
function chatStream(config, input) {
|
|
1707
1688
|
return {
|
|
1708
1689
|
[Symbol.asyncIterator]: async function* () {
|
|
1709
|
-
const response = await fetch(
|
|
1690
|
+
const response = await fetch(buildApiUrl(config, "/chat/completions"), {
|
|
1710
1691
|
method: "POST",
|
|
1711
1692
|
headers: {
|
|
1712
1693
|
...buildHeaders(config),
|
|
@@ -1778,7 +1759,7 @@ function chatStream(config, input) {
|
|
|
1778
1759
|
};
|
|
1779
1760
|
}
|
|
1780
1761
|
async function embed(config, input) {
|
|
1781
|
-
const response = await fetch(
|
|
1762
|
+
const response = await fetch(buildApiUrl(config, "/embeddings"), {
|
|
1782
1763
|
method: "POST",
|
|
1783
1764
|
headers: {
|
|
1784
1765
|
...buildHeaders(config),
|
|
@@ -1908,7 +1889,7 @@ async function uploadFile(config, file, options) {
|
|
|
1908
1889
|
let lastError = null;
|
|
1909
1890
|
for (let attempt = 0; attempt <= UPLOAD_RETRY_CONFIG.maxRetries; attempt++) {
|
|
1910
1891
|
try {
|
|
1911
|
-
tokenResponse = await fetch(
|
|
1892
|
+
tokenResponse = await fetch(buildApiUrl(config, "/storage/upload"), {
|
|
1912
1893
|
method: "POST",
|
|
1913
1894
|
headers: buildHeaders(config),
|
|
1914
1895
|
body: JSON.stringify({
|
|
@@ -2636,7 +2617,7 @@ async function getConsentHistory(config, input) {
|
|
|
2636
2617
|
userId: input.userId,
|
|
2637
2618
|
anonymousId: input.anonymousId,
|
|
2638
2619
|
limit: input.limit?.toString(),
|
|
2639
|
-
|
|
2620
|
+
cursor: input.cursor
|
|
2640
2621
|
}
|
|
2641
2622
|
});
|
|
2642
2623
|
}
|
|
@@ -3209,27 +3190,6 @@ async function deleteEnvVar(config, envId, key) {
|
|
|
3209
3190
|
{ method: "DELETE" }
|
|
3210
3191
|
);
|
|
3211
3192
|
}
|
|
3212
|
-
async function listCustomDomains(config, envId) {
|
|
3213
|
-
const result = await callApi(
|
|
3214
|
-
config,
|
|
3215
|
-
`/sdk/deploy/domains/${encodeURIComponent(envId)}`,
|
|
3216
|
-
{ method: "GET" }
|
|
3217
|
-
);
|
|
3218
|
-
return result.domains;
|
|
3219
|
-
}
|
|
3220
|
-
async function addCustomDomain(config, envId, request) {
|
|
3221
|
-
return callApi(config, `/sdk/deploy/domains/${encodeURIComponent(envId)}`, {
|
|
3222
|
-
method: "POST",
|
|
3223
|
-
body: request
|
|
3224
|
-
});
|
|
3225
|
-
}
|
|
3226
|
-
async function removeCustomDomain(config, envId, domain) {
|
|
3227
|
-
return callApi(
|
|
3228
|
-
config,
|
|
3229
|
-
`/sdk/deploy/domains/${encodeURIComponent(envId)}/${encodeURIComponent(domain)}`,
|
|
3230
|
-
{ method: "DELETE" }
|
|
3231
|
-
);
|
|
3232
|
-
}
|
|
3233
3193
|
|
|
3234
3194
|
// src/monitoring.ts
|
|
3235
3195
|
function errorToExceptionValue(error) {
|
|
@@ -3289,13 +3249,67 @@ async function captureMessage(config, message, options = {}) {
|
|
|
3289
3249
|
}
|
|
3290
3250
|
|
|
3291
3251
|
// src/sandbox.ts
|
|
3292
|
-
var
|
|
3252
|
+
var SandboxFiles = class {
|
|
3253
|
+
constructor(endpoint, token) {
|
|
3254
|
+
this.endpoint = endpoint;
|
|
3255
|
+
this.token = token;
|
|
3256
|
+
}
|
|
3257
|
+
authHeader() {
|
|
3258
|
+
return { Authorization: `Bearer ${this.token}` };
|
|
3259
|
+
}
|
|
3260
|
+
/** Write a file to the sandbox filesystem. */
|
|
3261
|
+
async write(path, content, encoding = "utf8") {
|
|
3262
|
+
const contentStr = Buffer.isBuffer(content) ? content.toString("base64") : content;
|
|
3263
|
+
const effectiveEncoding = Buffer.isBuffer(content) ? "base64" : encoding;
|
|
3264
|
+
const res = await fetch(`${this.endpoint}/files`, {
|
|
3265
|
+
method: "POST",
|
|
3266
|
+
headers: { ...this.authHeader(), "Content-Type": "application/json" },
|
|
3267
|
+
body: JSON.stringify({ path, content: contentStr, encoding: effectiveEncoding })
|
|
3268
|
+
});
|
|
3269
|
+
if (!res.ok) throw new Error(`files.write failed: ${await res.text()}`);
|
|
3270
|
+
}
|
|
3271
|
+
/** Read a file from the sandbox filesystem. Returns content as string. */
|
|
3272
|
+
async read(path) {
|
|
3273
|
+
const res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {
|
|
3274
|
+
headers: this.authHeader()
|
|
3275
|
+
});
|
|
3276
|
+
if (!res.ok) throw new Error(`files.read failed: ${await res.text()}`);
|
|
3277
|
+
const data = await res.json();
|
|
3278
|
+
return data.content;
|
|
3279
|
+
}
|
|
3280
|
+
/** Delete a file from the sandbox filesystem. */
|
|
3281
|
+
async delete(path) {
|
|
3282
|
+
const res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {
|
|
3283
|
+
method: "DELETE",
|
|
3284
|
+
headers: this.authHeader()
|
|
3285
|
+
});
|
|
3286
|
+
if (!res.ok) throw new Error(`files.delete failed: ${await res.text()}`);
|
|
3287
|
+
}
|
|
3288
|
+
/** List files in a directory. */
|
|
3289
|
+
async list(path = "/") {
|
|
3290
|
+
const res = await fetch(`${this.endpoint}/list?path=${encodeURIComponent(path)}`, {
|
|
3291
|
+
headers: this.authHeader()
|
|
3292
|
+
});
|
|
3293
|
+
if (!res.ok) throw new Error(`files.list failed: ${await res.text()}`);
|
|
3294
|
+
const data = await res.json();
|
|
3295
|
+
return data.files;
|
|
3296
|
+
}
|
|
3297
|
+
};
|
|
3293
3298
|
var SandboxClient = class _SandboxClient {
|
|
3294
3299
|
id;
|
|
3295
3300
|
config;
|
|
3296
|
-
|
|
3301
|
+
/** Public endpoint from Platform (may be null for sandboxes from pool pre-v2) */
|
|
3302
|
+
endpoint;
|
|
3303
|
+
/** Per-sandbox JWT for direct exec-server auth */
|
|
3304
|
+
token;
|
|
3305
|
+
/** File operations (direct to exec-server) */
|
|
3306
|
+
files;
|
|
3307
|
+
constructor(id, config, endpoint, token) {
|
|
3297
3308
|
this.id = id;
|
|
3298
3309
|
this.config = config;
|
|
3310
|
+
this.endpoint = endpoint;
|
|
3311
|
+
this.token = token;
|
|
3312
|
+
this.files = endpoint && token ? new SandboxFiles(endpoint, token) : null;
|
|
3299
3313
|
}
|
|
3300
3314
|
// ---------------------------------------------------------------------------
|
|
3301
3315
|
// Factory
|
|
@@ -3303,130 +3317,153 @@ var SandboxClient = class _SandboxClient {
|
|
|
3303
3317
|
/**
|
|
3304
3318
|
* Create a new sandbox.
|
|
3305
3319
|
*
|
|
3306
|
-
*
|
|
3307
|
-
*
|
|
3308
|
-
*
|
|
3309
|
-
* @param config - Sylphx config with a secret key (sk_*) and project ref
|
|
3310
|
-
* @param options - Sandbox creation options
|
|
3320
|
+
* Platform provisions the K8s pod, waits for readiness, and returns
|
|
3321
|
+
* { endpoint, token } once the sandbox is fully ready to accept traffic.
|
|
3322
|
+
* No client-side polling required.
|
|
3311
3323
|
*/
|
|
3312
3324
|
static async create(config, options) {
|
|
3313
|
-
const record = await callApi(config,
|
|
3325
|
+
const record = await callApi(config, "/sandboxes", {
|
|
3314
3326
|
method: "POST",
|
|
3315
3327
|
body: {
|
|
3316
|
-
image: options?.image
|
|
3328
|
+
image: options?.image,
|
|
3317
3329
|
idleTimeoutMs: options?.idleTimeoutMs ?? 3e5,
|
|
3318
3330
|
resources: options?.resources,
|
|
3319
3331
|
env: options?.env,
|
|
3320
3332
|
storage: options?.storageGi !== void 0 ? { enabled: true, sizeGi: options.storageGi } : void 0
|
|
3321
3333
|
}
|
|
3322
3334
|
});
|
|
3323
|
-
return new _SandboxClient(record.id, config);
|
|
3335
|
+
return new _SandboxClient(record.id, config, record.endpoint, record.token);
|
|
3324
3336
|
}
|
|
3325
3337
|
/**
|
|
3326
3338
|
* Reconnect to an existing sandbox by ID.
|
|
3327
|
-
*
|
|
3328
|
-
* Use this to resume operations on a sandbox created in a previous request.
|
|
3339
|
+
* Fetches the current status to get the endpoint + token.
|
|
3329
3340
|
*/
|
|
3330
|
-
static fromId(config, sandboxId) {
|
|
3331
|
-
|
|
3341
|
+
static async fromId(config, sandboxId) {
|
|
3342
|
+
const record = await callApi(config, `/sandboxes/${sandboxId}`, {
|
|
3343
|
+
method: "GET"
|
|
3344
|
+
});
|
|
3345
|
+
return new _SandboxClient(record.id, config, record.endpoint, record.token);
|
|
3332
3346
|
}
|
|
3333
3347
|
// ---------------------------------------------------------------------------
|
|
3334
3348
|
// Lifecycle
|
|
3335
3349
|
// ---------------------------------------------------------------------------
|
|
3336
|
-
/**
|
|
3337
|
-
* Get the current status of this sandbox.
|
|
3338
|
-
*/
|
|
3339
3350
|
async getStatus() {
|
|
3340
3351
|
return callApi(this.config, `/sandboxes/${this.id}`, { method: "GET" });
|
|
3341
3352
|
}
|
|
3342
|
-
/**
|
|
3343
|
-
* Terminate the sandbox immediately.
|
|
3344
|
-
*
|
|
3345
|
-
* Deletes the K8s Pod and Service. PVC (storage) is preserved for reuse.
|
|
3346
|
-
* This operation is idempotent — safe to call multiple times.
|
|
3347
|
-
*/
|
|
3348
3353
|
async terminate() {
|
|
3349
3354
|
await callApi(this.config, `/sandboxes/${this.id}`, { method: "DELETE" });
|
|
3350
3355
|
}
|
|
3351
3356
|
// ---------------------------------------------------------------------------
|
|
3352
|
-
//
|
|
3357
|
+
// Exec — SSE streaming (primary)
|
|
3353
3358
|
// ---------------------------------------------------------------------------
|
|
3354
3359
|
/**
|
|
3355
|
-
*
|
|
3360
|
+
* Execute a command and stream output as async iterable events.
|
|
3356
3361
|
*
|
|
3357
|
-
*
|
|
3358
|
-
*
|
|
3359
|
-
* @param encoding - 'utf8' (default) or 'base64' for binary files
|
|
3360
|
-
*/
|
|
3361
|
-
async writeFile(path, content, encoding = "utf8") {
|
|
3362
|
-
const contentStr = Buffer.isBuffer(content) ? content.toString("base64") : content;
|
|
3363
|
-
const effectiveEncoding = Buffer.isBuffer(content) ? "base64" : encoding;
|
|
3364
|
-
await callApi(this.config, `/sandboxes/${this.id}/files`, {
|
|
3365
|
-
method: "POST",
|
|
3366
|
-
body: { path, content: contentStr, encoding: effectiveEncoding }
|
|
3367
|
-
});
|
|
3368
|
-
}
|
|
3369
|
-
/**
|
|
3370
|
-
* Read a file from the sandbox filesystem.
|
|
3362
|
+
* Uses Server-Sent Events (SSE) for real-time stdout/stderr streaming.
|
|
3363
|
+
* Communicates DIRECTLY with exec-server (Platform not in data path).
|
|
3371
3364
|
*
|
|
3372
|
-
* @
|
|
3373
|
-
*
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
});
|
|
3380
|
-
return result.content;
|
|
3381
|
-
}
|
|
3382
|
-
/**
|
|
3383
|
-
* Delete a file from the sandbox filesystem.
|
|
3384
|
-
*
|
|
3385
|
-
* @param path - Absolute path inside the sandbox
|
|
3365
|
+
* @example
|
|
3366
|
+
* ```typescript
|
|
3367
|
+
* for await (const event of sandbox.exec(['npm', 'install'])) {
|
|
3368
|
+
* if (event.type === 'stdout') process.stdout.write(event.data)
|
|
3369
|
+
* if (event.type === 'exit') console.log('Done:', event.exitCode)
|
|
3370
|
+
* }
|
|
3371
|
+
* ```
|
|
3386
3372
|
*/
|
|
3387
|
-
async
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3373
|
+
async *exec(command, options) {
|
|
3374
|
+
this.assertDirect();
|
|
3375
|
+
const res = await fetch(`${this.endpoint}/exec/stream`, {
|
|
3376
|
+
method: "POST",
|
|
3377
|
+
headers: {
|
|
3378
|
+
Authorization: `Bearer ${this.token}`,
|
|
3379
|
+
"Content-Type": "application/json"
|
|
3380
|
+
},
|
|
3381
|
+
body: JSON.stringify({ command, ...options })
|
|
3391
3382
|
});
|
|
3383
|
+
if (!res.ok) {
|
|
3384
|
+
throw new Error(`exec failed (${res.status}): ${await res.text()}`);
|
|
3385
|
+
}
|
|
3386
|
+
if (!res.body) throw new Error("exec: no response body");
|
|
3387
|
+
const decoder = new TextDecoder();
|
|
3388
|
+
const reader = res.body.getReader();
|
|
3389
|
+
let buffer = "";
|
|
3390
|
+
try {
|
|
3391
|
+
while (true) {
|
|
3392
|
+
const { done, value } = await reader.read();
|
|
3393
|
+
if (done) break;
|
|
3394
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3395
|
+
const lines = buffer.split("\n");
|
|
3396
|
+
buffer = lines.pop() ?? "";
|
|
3397
|
+
for (const line of lines) {
|
|
3398
|
+
if (line.startsWith("data: ")) {
|
|
3399
|
+
try {
|
|
3400
|
+
const event = JSON.parse(line.slice(6));
|
|
3401
|
+
yield event;
|
|
3402
|
+
if (event.type === "exit" || event.type === "error") return;
|
|
3403
|
+
} catch {
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
} finally {
|
|
3409
|
+
reader.releaseLock();
|
|
3410
|
+
}
|
|
3392
3411
|
}
|
|
3393
3412
|
/**
|
|
3394
|
-
*
|
|
3413
|
+
* Execute a command and collect all output (non-streaming).
|
|
3414
|
+
* Convenience wrapper over exec() for simple use cases.
|
|
3395
3415
|
*
|
|
3396
|
-
*
|
|
3397
|
-
* @returns Array of file/directory paths
|
|
3416
|
+
* For long-running commands, prefer exec() to stream output incrementally.
|
|
3398
3417
|
*/
|
|
3399
|
-
async
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3418
|
+
async run(command, options) {
|
|
3419
|
+
let stdout = "";
|
|
3420
|
+
let stderr = "";
|
|
3421
|
+
let exitCode = 1;
|
|
3422
|
+
let durationMs = 0;
|
|
3423
|
+
for await (const event of this.exec(command, options)) {
|
|
3424
|
+
if (event.type === "stdout") stdout += event.data;
|
|
3425
|
+
else if (event.type === "stderr") stderr += event.data;
|
|
3426
|
+
else if (event.type === "exit") {
|
|
3427
|
+
exitCode = event.exitCode;
|
|
3428
|
+
durationMs = event.durationMs;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
return { stdout, stderr, exitCode, durationMs };
|
|
3405
3432
|
}
|
|
3406
3433
|
// ---------------------------------------------------------------------------
|
|
3407
|
-
//
|
|
3434
|
+
// PTY — Interactive terminal (WebSocket)
|
|
3408
3435
|
// ---------------------------------------------------------------------------
|
|
3409
3436
|
/**
|
|
3410
|
-
*
|
|
3437
|
+
* Open an interactive PTY session (WebSocket).
|
|
3411
3438
|
*
|
|
3412
|
-
*
|
|
3413
|
-
*
|
|
3414
|
-
* @param command - Full command + args as array (e.g. ['python3', 'script.py'])
|
|
3415
|
-
* @param options - Optional cwd, env, timeout, stdin
|
|
3416
|
-
* @returns { stdout, stderr, exitCode, durationMs }
|
|
3439
|
+
* Returns a WebSocket connected to a bash shell in the sandbox.
|
|
3440
|
+
* Token is passed as a query param (WebSocket doesn't support custom headers in browsers).
|
|
3417
3441
|
*
|
|
3418
3442
|
* @example
|
|
3419
3443
|
* ```typescript
|
|
3420
|
-
* const
|
|
3421
|
-
*
|
|
3422
|
-
*
|
|
3444
|
+
* const ws = await sandbox.pty()
|
|
3445
|
+
* ws.on('message', (data) => process.stdout.write(JSON.parse(data).data))
|
|
3446
|
+
* ws.send(JSON.stringify({ type: 'input', data: 'ls -la\n' }))
|
|
3447
|
+
* ws.send(JSON.stringify({ type: 'resize', cols: 120, rows: 40 }))
|
|
3423
3448
|
* ```
|
|
3424
3449
|
*/
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3450
|
+
pty() {
|
|
3451
|
+
this.assertDirect();
|
|
3452
|
+
const wsEndpoint = this.endpoint.replace(/^https:\/\//, "wss://").replace(
|
|
3453
|
+
/^http:\/\//,
|
|
3454
|
+
"ws://"
|
|
3455
|
+
);
|
|
3456
|
+
return new WebSocket(`${wsEndpoint}/pty?token=${encodeURIComponent(this.token)}`);
|
|
3457
|
+
}
|
|
3458
|
+
// ---------------------------------------------------------------------------
|
|
3459
|
+
// Private
|
|
3460
|
+
// ---------------------------------------------------------------------------
|
|
3461
|
+
assertDirect() {
|
|
3462
|
+
if (!this.endpoint || !this.token) {
|
|
3463
|
+
throw new Error(
|
|
3464
|
+
"Sandbox endpoint/token not available. This sandbox was created with an older SDK version or does not have a public endpoint."
|
|
3465
|
+
);
|
|
3466
|
+
}
|
|
3430
3467
|
}
|
|
3431
3468
|
};
|
|
3432
3469
|
|
|
@@ -3648,7 +3685,6 @@ function sleep2(ms) {
|
|
|
3648
3685
|
WorkersClient,
|
|
3649
3686
|
acceptAllConsents,
|
|
3650
3687
|
acceptOrganizationInvitation,
|
|
3651
|
-
addCustomDomain,
|
|
3652
3688
|
batchIndex,
|
|
3653
3689
|
canDeleteOrganization,
|
|
3654
3690
|
canManageMembers,
|
|
@@ -3783,7 +3819,6 @@ function sleep2(ms) {
|
|
|
3783
3819
|
kvZrange,
|
|
3784
3820
|
leaveOrganization,
|
|
3785
3821
|
linkAnonymousConsents,
|
|
3786
|
-
listCustomDomains,
|
|
3787
3822
|
listEnvVars,
|
|
3788
3823
|
listScheduledEmails,
|
|
3789
3824
|
listSecretKeys,
|
|
@@ -3799,7 +3834,6 @@ function sleep2(ms) {
|
|
|
3799
3834
|
regenerateReferralCode,
|
|
3800
3835
|
registerPush,
|
|
3801
3836
|
registerPushServiceWorker,
|
|
3802
|
-
removeCustomDomain,
|
|
3803
3837
|
removeOrganizationMember,
|
|
3804
3838
|
replayWebhookDelivery,
|
|
3805
3839
|
rescheduleEmail,
|