@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.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// src/constants.ts
|
|
2
|
-
var
|
|
3
|
-
var SDK_API_PATH = `/api/v1`;
|
|
4
|
-
var SDK_API_PATH_NEW = `/v1`;
|
|
2
|
+
var SDK_API_PATH = `/v1`;
|
|
5
3
|
var DEFAULT_SDK_API_HOST = "api.sylphx.com";
|
|
6
4
|
var SDK_VERSION = "0.1.0";
|
|
7
5
|
var SDK_PLATFORM = typeof window !== "undefined" ? "browser" : typeof process !== "undefined" && process.versions?.node ? "node" : "unknown";
|
|
@@ -509,32 +507,18 @@ function createConfig(input) {
|
|
|
509
507
|
}
|
|
510
508
|
secretKey = result.sanitizedKey;
|
|
511
509
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
let platformUrl;
|
|
522
|
-
let apiBasePath;
|
|
523
|
-
if (input.platformUrl) {
|
|
524
|
-
platformUrl = input.platformUrl.trim();
|
|
525
|
-
apiBasePath = SDK_API_PATH;
|
|
526
|
-
} else if (input.ref) {
|
|
527
|
-
platformUrl = `https://${input.ref.trim()}.${DEFAULT_SDK_API_HOST}`;
|
|
528
|
-
apiBasePath = SDK_API_PATH_NEW;
|
|
529
|
-
} else {
|
|
530
|
-
platformUrl = DEFAULT_PLATFORM_URL;
|
|
531
|
-
apiBasePath = SDK_API_PATH;
|
|
510
|
+
const trimmedRef = input.ref.trim();
|
|
511
|
+
if (!REF_PATTERN.test(trimmedRef)) {
|
|
512
|
+
throw new SylphxError(
|
|
513
|
+
`[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.`,
|
|
514
|
+
{ code: "BAD_REQUEST" }
|
|
515
|
+
);
|
|
532
516
|
}
|
|
517
|
+
const baseUrl = `https://${trimmedRef}.${DEFAULT_SDK_API_HOST}${SDK_API_PATH}`;
|
|
533
518
|
return Object.freeze({
|
|
534
519
|
secretKey,
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
apiBasePath,
|
|
520
|
+
ref: trimmedRef,
|
|
521
|
+
baseUrl,
|
|
538
522
|
accessToken: input.accessToken
|
|
539
523
|
});
|
|
540
524
|
}
|
|
@@ -542,7 +526,7 @@ function withToken(config, accessToken) {
|
|
|
542
526
|
return Object.freeze({
|
|
543
527
|
...config,
|
|
544
528
|
accessToken
|
|
545
|
-
// Preserve
|
|
529
|
+
// Preserve baseUrl and ref from original config
|
|
546
530
|
});
|
|
547
531
|
}
|
|
548
532
|
function buildHeaders(config) {
|
|
@@ -558,9 +542,9 @@ function buildHeaders(config) {
|
|
|
558
542
|
return headers;
|
|
559
543
|
}
|
|
560
544
|
function buildApiUrl(config, path) {
|
|
561
|
-
const base = config.
|
|
545
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
562
546
|
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
563
|
-
return `${base}${
|
|
547
|
+
return `${base}${cleanPath}`;
|
|
564
548
|
}
|
|
565
549
|
async function callApi(config, path, options = {}) {
|
|
566
550
|
const {
|
|
@@ -1114,7 +1098,7 @@ function createRetryMiddleware(retryConfig) {
|
|
|
1114
1098
|
function validateClientConfig(config) {
|
|
1115
1099
|
return {
|
|
1116
1100
|
secretKey: validateAndSanitizeSecretKey(config.secretKey),
|
|
1117
|
-
baseUrl: (config.platformUrl ||
|
|
1101
|
+
baseUrl: (config.platformUrl || `https://${DEFAULT_SDK_API_HOST}`).trim()
|
|
1118
1102
|
};
|
|
1119
1103
|
}
|
|
1120
1104
|
function createRestClient(config) {
|
|
@@ -1240,7 +1224,7 @@ async function verifyTwoFactor(config, userId, code) {
|
|
|
1240
1224
|
});
|
|
1241
1225
|
}
|
|
1242
1226
|
async function introspectToken(config, token, tokenTypeHint) {
|
|
1243
|
-
const response = await fetch(
|
|
1227
|
+
const response = await fetch(buildApiUrl(config, "/auth/introspect"), {
|
|
1244
1228
|
method: "POST",
|
|
1245
1229
|
headers: { "Content-Type": "application/json" },
|
|
1246
1230
|
body: JSON.stringify({
|
|
@@ -1255,7 +1239,7 @@ async function introspectToken(config, token, tokenTypeHint) {
|
|
|
1255
1239
|
return response.json();
|
|
1256
1240
|
}
|
|
1257
1241
|
async function revokeToken(config, token, options) {
|
|
1258
|
-
await fetch(
|
|
1242
|
+
await fetch(buildApiUrl(config, "/auth/revoke"), {
|
|
1259
1243
|
method: "POST",
|
|
1260
1244
|
headers: { "Content-Type": "application/json" },
|
|
1261
1245
|
body: JSON.stringify({
|
|
@@ -1406,7 +1390,7 @@ function createTracker(config, defaultAnonymousId) {
|
|
|
1406
1390
|
|
|
1407
1391
|
// src/ai.ts
|
|
1408
1392
|
async function chat(config, input) {
|
|
1409
|
-
const response = await fetch(
|
|
1393
|
+
const response = await fetch(buildApiUrl(config, "/chat/completions"), {
|
|
1410
1394
|
method: "POST",
|
|
1411
1395
|
headers: {
|
|
1412
1396
|
...buildHeaders(config),
|
|
@@ -1454,7 +1438,7 @@ async function chat(config, input) {
|
|
|
1454
1438
|
function chatStream(config, input) {
|
|
1455
1439
|
return {
|
|
1456
1440
|
[Symbol.asyncIterator]: async function* () {
|
|
1457
|
-
const response = await fetch(
|
|
1441
|
+
const response = await fetch(buildApiUrl(config, "/chat/completions"), {
|
|
1458
1442
|
method: "POST",
|
|
1459
1443
|
headers: {
|
|
1460
1444
|
...buildHeaders(config),
|
|
@@ -1526,7 +1510,7 @@ function chatStream(config, input) {
|
|
|
1526
1510
|
};
|
|
1527
1511
|
}
|
|
1528
1512
|
async function embed(config, input) {
|
|
1529
|
-
const response = await fetch(
|
|
1513
|
+
const response = await fetch(buildApiUrl(config, "/embeddings"), {
|
|
1530
1514
|
method: "POST",
|
|
1531
1515
|
headers: {
|
|
1532
1516
|
...buildHeaders(config),
|
|
@@ -1656,7 +1640,7 @@ async function uploadFile(config, file, options) {
|
|
|
1656
1640
|
let lastError = null;
|
|
1657
1641
|
for (let attempt = 0; attempt <= UPLOAD_RETRY_CONFIG.maxRetries; attempt++) {
|
|
1658
1642
|
try {
|
|
1659
|
-
tokenResponse = await fetch(
|
|
1643
|
+
tokenResponse = await fetch(buildApiUrl(config, "/storage/upload"), {
|
|
1660
1644
|
method: "POST",
|
|
1661
1645
|
headers: buildHeaders(config),
|
|
1662
1646
|
body: JSON.stringify({
|
|
@@ -2384,7 +2368,7 @@ async function getConsentHistory(config, input) {
|
|
|
2384
2368
|
userId: input.userId,
|
|
2385
2369
|
anonymousId: input.anonymousId,
|
|
2386
2370
|
limit: input.limit?.toString(),
|
|
2387
|
-
|
|
2371
|
+
cursor: input.cursor
|
|
2388
2372
|
}
|
|
2389
2373
|
});
|
|
2390
2374
|
}
|
|
@@ -2957,27 +2941,6 @@ async function deleteEnvVar(config, envId, key) {
|
|
|
2957
2941
|
{ method: "DELETE" }
|
|
2958
2942
|
);
|
|
2959
2943
|
}
|
|
2960
|
-
async function listCustomDomains(config, envId) {
|
|
2961
|
-
const result = await callApi(
|
|
2962
|
-
config,
|
|
2963
|
-
`/sdk/deploy/domains/${encodeURIComponent(envId)}`,
|
|
2964
|
-
{ method: "GET" }
|
|
2965
|
-
);
|
|
2966
|
-
return result.domains;
|
|
2967
|
-
}
|
|
2968
|
-
async function addCustomDomain(config, envId, request) {
|
|
2969
|
-
return callApi(config, `/sdk/deploy/domains/${encodeURIComponent(envId)}`, {
|
|
2970
|
-
method: "POST",
|
|
2971
|
-
body: request
|
|
2972
|
-
});
|
|
2973
|
-
}
|
|
2974
|
-
async function removeCustomDomain(config, envId, domain) {
|
|
2975
|
-
return callApi(
|
|
2976
|
-
config,
|
|
2977
|
-
`/sdk/deploy/domains/${encodeURIComponent(envId)}/${encodeURIComponent(domain)}`,
|
|
2978
|
-
{ method: "DELETE" }
|
|
2979
|
-
);
|
|
2980
|
-
}
|
|
2981
2944
|
|
|
2982
2945
|
// src/monitoring.ts
|
|
2983
2946
|
function errorToExceptionValue(error) {
|
|
@@ -3037,13 +3000,67 @@ async function captureMessage(config, message, options = {}) {
|
|
|
3037
3000
|
}
|
|
3038
3001
|
|
|
3039
3002
|
// src/sandbox.ts
|
|
3040
|
-
var
|
|
3003
|
+
var SandboxFiles = class {
|
|
3004
|
+
constructor(endpoint, token) {
|
|
3005
|
+
this.endpoint = endpoint;
|
|
3006
|
+
this.token = token;
|
|
3007
|
+
}
|
|
3008
|
+
authHeader() {
|
|
3009
|
+
return { Authorization: `Bearer ${this.token}` };
|
|
3010
|
+
}
|
|
3011
|
+
/** Write a file to the sandbox filesystem. */
|
|
3012
|
+
async write(path, content, encoding = "utf8") {
|
|
3013
|
+
const contentStr = Buffer.isBuffer(content) ? content.toString("base64") : content;
|
|
3014
|
+
const effectiveEncoding = Buffer.isBuffer(content) ? "base64" : encoding;
|
|
3015
|
+
const res = await fetch(`${this.endpoint}/files`, {
|
|
3016
|
+
method: "POST",
|
|
3017
|
+
headers: { ...this.authHeader(), "Content-Type": "application/json" },
|
|
3018
|
+
body: JSON.stringify({ path, content: contentStr, encoding: effectiveEncoding })
|
|
3019
|
+
});
|
|
3020
|
+
if (!res.ok) throw new Error(`files.write failed: ${await res.text()}`);
|
|
3021
|
+
}
|
|
3022
|
+
/** Read a file from the sandbox filesystem. Returns content as string. */
|
|
3023
|
+
async read(path) {
|
|
3024
|
+
const res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {
|
|
3025
|
+
headers: this.authHeader()
|
|
3026
|
+
});
|
|
3027
|
+
if (!res.ok) throw new Error(`files.read failed: ${await res.text()}`);
|
|
3028
|
+
const data = await res.json();
|
|
3029
|
+
return data.content;
|
|
3030
|
+
}
|
|
3031
|
+
/** Delete a file from the sandbox filesystem. */
|
|
3032
|
+
async delete(path) {
|
|
3033
|
+
const res = await fetch(`${this.endpoint}/files?path=${encodeURIComponent(path)}`, {
|
|
3034
|
+
method: "DELETE",
|
|
3035
|
+
headers: this.authHeader()
|
|
3036
|
+
});
|
|
3037
|
+
if (!res.ok) throw new Error(`files.delete failed: ${await res.text()}`);
|
|
3038
|
+
}
|
|
3039
|
+
/** List files in a directory. */
|
|
3040
|
+
async list(path = "/") {
|
|
3041
|
+
const res = await fetch(`${this.endpoint}/list?path=${encodeURIComponent(path)}`, {
|
|
3042
|
+
headers: this.authHeader()
|
|
3043
|
+
});
|
|
3044
|
+
if (!res.ok) throw new Error(`files.list failed: ${await res.text()}`);
|
|
3045
|
+
const data = await res.json();
|
|
3046
|
+
return data.files;
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3041
3049
|
var SandboxClient = class _SandboxClient {
|
|
3042
3050
|
id;
|
|
3043
3051
|
config;
|
|
3044
|
-
|
|
3052
|
+
/** Public endpoint from Platform (may be null for sandboxes from pool pre-v2) */
|
|
3053
|
+
endpoint;
|
|
3054
|
+
/** Per-sandbox JWT for direct exec-server auth */
|
|
3055
|
+
token;
|
|
3056
|
+
/** File operations (direct to exec-server) */
|
|
3057
|
+
files;
|
|
3058
|
+
constructor(id, config, endpoint, token) {
|
|
3045
3059
|
this.id = id;
|
|
3046
3060
|
this.config = config;
|
|
3061
|
+
this.endpoint = endpoint;
|
|
3062
|
+
this.token = token;
|
|
3063
|
+
this.files = endpoint && token ? new SandboxFiles(endpoint, token) : null;
|
|
3047
3064
|
}
|
|
3048
3065
|
// ---------------------------------------------------------------------------
|
|
3049
3066
|
// Factory
|
|
@@ -3051,130 +3068,153 @@ var SandboxClient = class _SandboxClient {
|
|
|
3051
3068
|
/**
|
|
3052
3069
|
* Create a new sandbox.
|
|
3053
3070
|
*
|
|
3054
|
-
*
|
|
3055
|
-
*
|
|
3056
|
-
*
|
|
3057
|
-
* @param config - Sylphx config with a secret key (sk_*) and project ref
|
|
3058
|
-
* @param options - Sandbox creation options
|
|
3071
|
+
* Platform provisions the K8s pod, waits for readiness, and returns
|
|
3072
|
+
* { endpoint, token } once the sandbox is fully ready to accept traffic.
|
|
3073
|
+
* No client-side polling required.
|
|
3059
3074
|
*/
|
|
3060
3075
|
static async create(config, options) {
|
|
3061
|
-
const record = await callApi(config,
|
|
3076
|
+
const record = await callApi(config, "/sandboxes", {
|
|
3062
3077
|
method: "POST",
|
|
3063
3078
|
body: {
|
|
3064
|
-
image: options?.image
|
|
3079
|
+
image: options?.image,
|
|
3065
3080
|
idleTimeoutMs: options?.idleTimeoutMs ?? 3e5,
|
|
3066
3081
|
resources: options?.resources,
|
|
3067
3082
|
env: options?.env,
|
|
3068
3083
|
storage: options?.storageGi !== void 0 ? { enabled: true, sizeGi: options.storageGi } : void 0
|
|
3069
3084
|
}
|
|
3070
3085
|
});
|
|
3071
|
-
return new _SandboxClient(record.id, config);
|
|
3086
|
+
return new _SandboxClient(record.id, config, record.endpoint, record.token);
|
|
3072
3087
|
}
|
|
3073
3088
|
/**
|
|
3074
3089
|
* Reconnect to an existing sandbox by ID.
|
|
3075
|
-
*
|
|
3076
|
-
* Use this to resume operations on a sandbox created in a previous request.
|
|
3090
|
+
* Fetches the current status to get the endpoint + token.
|
|
3077
3091
|
*/
|
|
3078
|
-
static fromId(config, sandboxId) {
|
|
3079
|
-
|
|
3092
|
+
static async fromId(config, sandboxId) {
|
|
3093
|
+
const record = await callApi(config, `/sandboxes/${sandboxId}`, {
|
|
3094
|
+
method: "GET"
|
|
3095
|
+
});
|
|
3096
|
+
return new _SandboxClient(record.id, config, record.endpoint, record.token);
|
|
3080
3097
|
}
|
|
3081
3098
|
// ---------------------------------------------------------------------------
|
|
3082
3099
|
// Lifecycle
|
|
3083
3100
|
// ---------------------------------------------------------------------------
|
|
3084
|
-
/**
|
|
3085
|
-
* Get the current status of this sandbox.
|
|
3086
|
-
*/
|
|
3087
3101
|
async getStatus() {
|
|
3088
3102
|
return callApi(this.config, `/sandboxes/${this.id}`, { method: "GET" });
|
|
3089
3103
|
}
|
|
3090
|
-
/**
|
|
3091
|
-
* Terminate the sandbox immediately.
|
|
3092
|
-
*
|
|
3093
|
-
* Deletes the K8s Pod and Service. PVC (storage) is preserved for reuse.
|
|
3094
|
-
* This operation is idempotent — safe to call multiple times.
|
|
3095
|
-
*/
|
|
3096
3104
|
async terminate() {
|
|
3097
3105
|
await callApi(this.config, `/sandboxes/${this.id}`, { method: "DELETE" });
|
|
3098
3106
|
}
|
|
3099
3107
|
// ---------------------------------------------------------------------------
|
|
3100
|
-
//
|
|
3108
|
+
// Exec — SSE streaming (primary)
|
|
3101
3109
|
// ---------------------------------------------------------------------------
|
|
3102
3110
|
/**
|
|
3103
|
-
*
|
|
3111
|
+
* Execute a command and stream output as async iterable events.
|
|
3104
3112
|
*
|
|
3105
|
-
*
|
|
3106
|
-
*
|
|
3107
|
-
* @param encoding - 'utf8' (default) or 'base64' for binary files
|
|
3108
|
-
*/
|
|
3109
|
-
async writeFile(path, content, encoding = "utf8") {
|
|
3110
|
-
const contentStr = Buffer.isBuffer(content) ? content.toString("base64") : content;
|
|
3111
|
-
const effectiveEncoding = Buffer.isBuffer(content) ? "base64" : encoding;
|
|
3112
|
-
await callApi(this.config, `/sandboxes/${this.id}/files`, {
|
|
3113
|
-
method: "POST",
|
|
3114
|
-
body: { path, content: contentStr, encoding: effectiveEncoding }
|
|
3115
|
-
});
|
|
3116
|
-
}
|
|
3117
|
-
/**
|
|
3118
|
-
* Read a file from the sandbox filesystem.
|
|
3113
|
+
* Uses Server-Sent Events (SSE) for real-time stdout/stderr streaming.
|
|
3114
|
+
* Communicates DIRECTLY with exec-server (Platform not in data path).
|
|
3119
3115
|
*
|
|
3120
|
-
* @
|
|
3121
|
-
*
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
});
|
|
3128
|
-
return result.content;
|
|
3129
|
-
}
|
|
3130
|
-
/**
|
|
3131
|
-
* Delete a file from the sandbox filesystem.
|
|
3132
|
-
*
|
|
3133
|
-
* @param path - Absolute path inside the sandbox
|
|
3116
|
+
* @example
|
|
3117
|
+
* ```typescript
|
|
3118
|
+
* for await (const event of sandbox.exec(['npm', 'install'])) {
|
|
3119
|
+
* if (event.type === 'stdout') process.stdout.write(event.data)
|
|
3120
|
+
* if (event.type === 'exit') console.log('Done:', event.exitCode)
|
|
3121
|
+
* }
|
|
3122
|
+
* ```
|
|
3134
3123
|
*/
|
|
3135
|
-
async
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3124
|
+
async *exec(command, options) {
|
|
3125
|
+
this.assertDirect();
|
|
3126
|
+
const res = await fetch(`${this.endpoint}/exec/stream`, {
|
|
3127
|
+
method: "POST",
|
|
3128
|
+
headers: {
|
|
3129
|
+
Authorization: `Bearer ${this.token}`,
|
|
3130
|
+
"Content-Type": "application/json"
|
|
3131
|
+
},
|
|
3132
|
+
body: JSON.stringify({ command, ...options })
|
|
3139
3133
|
});
|
|
3134
|
+
if (!res.ok) {
|
|
3135
|
+
throw new Error(`exec failed (${res.status}): ${await res.text()}`);
|
|
3136
|
+
}
|
|
3137
|
+
if (!res.body) throw new Error("exec: no response body");
|
|
3138
|
+
const decoder = new TextDecoder();
|
|
3139
|
+
const reader = res.body.getReader();
|
|
3140
|
+
let buffer = "";
|
|
3141
|
+
try {
|
|
3142
|
+
while (true) {
|
|
3143
|
+
const { done, value } = await reader.read();
|
|
3144
|
+
if (done) break;
|
|
3145
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3146
|
+
const lines = buffer.split("\n");
|
|
3147
|
+
buffer = lines.pop() ?? "";
|
|
3148
|
+
for (const line of lines) {
|
|
3149
|
+
if (line.startsWith("data: ")) {
|
|
3150
|
+
try {
|
|
3151
|
+
const event = JSON.parse(line.slice(6));
|
|
3152
|
+
yield event;
|
|
3153
|
+
if (event.type === "exit" || event.type === "error") return;
|
|
3154
|
+
} catch {
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
} finally {
|
|
3160
|
+
reader.releaseLock();
|
|
3161
|
+
}
|
|
3140
3162
|
}
|
|
3141
3163
|
/**
|
|
3142
|
-
*
|
|
3164
|
+
* Execute a command and collect all output (non-streaming).
|
|
3165
|
+
* Convenience wrapper over exec() for simple use cases.
|
|
3143
3166
|
*
|
|
3144
|
-
*
|
|
3145
|
-
* @returns Array of file/directory paths
|
|
3167
|
+
* For long-running commands, prefer exec() to stream output incrementally.
|
|
3146
3168
|
*/
|
|
3147
|
-
async
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3169
|
+
async run(command, options) {
|
|
3170
|
+
let stdout = "";
|
|
3171
|
+
let stderr = "";
|
|
3172
|
+
let exitCode = 1;
|
|
3173
|
+
let durationMs = 0;
|
|
3174
|
+
for await (const event of this.exec(command, options)) {
|
|
3175
|
+
if (event.type === "stdout") stdout += event.data;
|
|
3176
|
+
else if (event.type === "stderr") stderr += event.data;
|
|
3177
|
+
else if (event.type === "exit") {
|
|
3178
|
+
exitCode = event.exitCode;
|
|
3179
|
+
durationMs = event.durationMs;
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
return { stdout, stderr, exitCode, durationMs };
|
|
3153
3183
|
}
|
|
3154
3184
|
// ---------------------------------------------------------------------------
|
|
3155
|
-
//
|
|
3185
|
+
// PTY — Interactive terminal (WebSocket)
|
|
3156
3186
|
// ---------------------------------------------------------------------------
|
|
3157
3187
|
/**
|
|
3158
|
-
*
|
|
3188
|
+
* Open an interactive PTY session (WebSocket).
|
|
3159
3189
|
*
|
|
3160
|
-
*
|
|
3161
|
-
*
|
|
3162
|
-
* @param command - Full command + args as array (e.g. ['python3', 'script.py'])
|
|
3163
|
-
* @param options - Optional cwd, env, timeout, stdin
|
|
3164
|
-
* @returns { stdout, stderr, exitCode, durationMs }
|
|
3190
|
+
* Returns a WebSocket connected to a bash shell in the sandbox.
|
|
3191
|
+
* Token is passed as a query param (WebSocket doesn't support custom headers in browsers).
|
|
3165
3192
|
*
|
|
3166
3193
|
* @example
|
|
3167
3194
|
* ```typescript
|
|
3168
|
-
* const
|
|
3169
|
-
*
|
|
3170
|
-
*
|
|
3195
|
+
* const ws = await sandbox.pty()
|
|
3196
|
+
* ws.on('message', (data) => process.stdout.write(JSON.parse(data).data))
|
|
3197
|
+
* ws.send(JSON.stringify({ type: 'input', data: 'ls -la\n' }))
|
|
3198
|
+
* ws.send(JSON.stringify({ type: 'resize', cols: 120, rows: 40 }))
|
|
3171
3199
|
* ```
|
|
3172
3200
|
*/
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3201
|
+
pty() {
|
|
3202
|
+
this.assertDirect();
|
|
3203
|
+
const wsEndpoint = this.endpoint.replace(/^https:\/\//, "wss://").replace(
|
|
3204
|
+
/^http:\/\//,
|
|
3205
|
+
"ws://"
|
|
3206
|
+
);
|
|
3207
|
+
return new WebSocket(`${wsEndpoint}/pty?token=${encodeURIComponent(this.token)}`);
|
|
3208
|
+
}
|
|
3209
|
+
// ---------------------------------------------------------------------------
|
|
3210
|
+
// Private
|
|
3211
|
+
// ---------------------------------------------------------------------------
|
|
3212
|
+
assertDirect() {
|
|
3213
|
+
if (!this.endpoint || !this.token) {
|
|
3214
|
+
throw new Error(
|
|
3215
|
+
"Sandbox endpoint/token not available. This sandbox was created with an older SDK version or does not have a public endpoint."
|
|
3216
|
+
);
|
|
3217
|
+
}
|
|
3178
3218
|
}
|
|
3179
3219
|
};
|
|
3180
3220
|
|
|
@@ -3395,7 +3435,6 @@ export {
|
|
|
3395
3435
|
WorkersClient,
|
|
3396
3436
|
acceptAllConsents,
|
|
3397
3437
|
acceptOrganizationInvitation,
|
|
3398
|
-
addCustomDomain,
|
|
3399
3438
|
batchIndex,
|
|
3400
3439
|
canDeleteOrganization,
|
|
3401
3440
|
canManageMembers,
|
|
@@ -3530,7 +3569,6 @@ export {
|
|
|
3530
3569
|
kvZrange,
|
|
3531
3570
|
leaveOrganization,
|
|
3532
3571
|
linkAnonymousConsents,
|
|
3533
|
-
listCustomDomains,
|
|
3534
3572
|
listEnvVars,
|
|
3535
3573
|
listScheduledEmails,
|
|
3536
3574
|
listSecretKeys,
|
|
@@ -3546,7 +3584,6 @@ export {
|
|
|
3546
3584
|
regenerateReferralCode,
|
|
3547
3585
|
registerPush,
|
|
3548
3586
|
registerPushServiceWorker,
|
|
3549
|
-
removeCustomDomain,
|
|
3550
3587
|
removeOrganizationMember,
|
|
3551
3588
|
replayWebhookDelivery,
|
|
3552
3589
|
rescheduleEmail,
|