@sandboxxjs/core 0.1.0 → 0.3.0
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/dist/index.d.ts +181 -75
- package/dist/index.js +1303 -178
- package/dist/index.js.map +13 -9
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -474,6 +474,36 @@ var require_cross_spawn = __commonJS((exports, module) => {
|
|
|
474
474
|
module.exports._enoent = enoent;
|
|
475
475
|
});
|
|
476
476
|
|
|
477
|
+
// ../../node_modules/.bun/nanoid@5.1.6/node_modules/nanoid/index.js
|
|
478
|
+
import { webcrypto as crypto } from "node:crypto";
|
|
479
|
+
|
|
480
|
+
// ../../node_modules/.bun/nanoid@5.1.6/node_modules/nanoid/url-alphabet/index.js
|
|
481
|
+
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
482
|
+
|
|
483
|
+
// ../../node_modules/.bun/nanoid@5.1.6/node_modules/nanoid/index.js
|
|
484
|
+
var POOL_SIZE_MULTIPLIER = 128;
|
|
485
|
+
var pool;
|
|
486
|
+
var poolOffset;
|
|
487
|
+
function fillPool(bytes) {
|
|
488
|
+
if (!pool || pool.length < bytes) {
|
|
489
|
+
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
|
|
490
|
+
crypto.getRandomValues(pool);
|
|
491
|
+
poolOffset = 0;
|
|
492
|
+
} else if (poolOffset + bytes > pool.length) {
|
|
493
|
+
crypto.getRandomValues(pool);
|
|
494
|
+
poolOffset = 0;
|
|
495
|
+
}
|
|
496
|
+
poolOffset += bytes;
|
|
497
|
+
}
|
|
498
|
+
function nanoid(size = 21) {
|
|
499
|
+
fillPool(size |= 0);
|
|
500
|
+
let id = "";
|
|
501
|
+
for (let i = poolOffset - size;i < poolOffset; i++) {
|
|
502
|
+
id += urlAlphabet[pool[i] & 63];
|
|
503
|
+
}
|
|
504
|
+
return id;
|
|
505
|
+
}
|
|
506
|
+
|
|
477
507
|
// ../../node_modules/.bun/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
478
508
|
function isPlainObject(value) {
|
|
479
509
|
if (typeof value !== "object" || value === null) {
|
|
@@ -7234,119 +7264,58 @@ class FileSystemError extends SandboxError {
|
|
|
7234
7264
|
// src/isolators/LocalIsolator.ts
|
|
7235
7265
|
class LocalIsolator extends Isolator {
|
|
7236
7266
|
workDir;
|
|
7237
|
-
|
|
7238
|
-
constructor(runtime = "node") {
|
|
7267
|
+
constructor() {
|
|
7239
7268
|
super();
|
|
7240
|
-
this.runtime = runtime;
|
|
7241
7269
|
this.workDir = path6.join(process.cwd(), ".sandbox", `session-${Date.now()}`);
|
|
7242
7270
|
}
|
|
7243
|
-
async
|
|
7244
|
-
const {
|
|
7271
|
+
async shell(command, options = {}) {
|
|
7272
|
+
const { timeout = 30000, env = {} } = options;
|
|
7245
7273
|
const startTime = Date.now();
|
|
7246
7274
|
await fs.mkdir(this.workDir, { recursive: true });
|
|
7247
7275
|
try {
|
|
7248
|
-
const
|
|
7249
|
-
const result = await execa(command[0], command.slice(1), {
|
|
7276
|
+
const result = await execa("sh", ["-c", command], {
|
|
7250
7277
|
cwd: this.workDir,
|
|
7251
7278
|
env: { ...process.env, ...env },
|
|
7252
7279
|
timeout,
|
|
7253
7280
|
maxBuffer: 10 * 1024 * 1024,
|
|
7254
|
-
reject:
|
|
7281
|
+
reject: false,
|
|
7255
7282
|
all: true
|
|
7256
7283
|
});
|
|
7257
7284
|
return {
|
|
7258
7285
|
success: result.exitCode === 0,
|
|
7259
|
-
stdout: result.stdout,
|
|
7260
|
-
stderr: result.stderr,
|
|
7261
|
-
exitCode: result.exitCode
|
|
7262
|
-
|
|
7263
|
-
executionTime: Date.now() - startTime,
|
|
7264
|
-
timestamp: new Date().toISOString()
|
|
7265
|
-
}
|
|
7286
|
+
stdout: result.stdout || "",
|
|
7287
|
+
stderr: result.stderr || "",
|
|
7288
|
+
exitCode: result.exitCode ?? 0,
|
|
7289
|
+
executionTime: Date.now() - startTime
|
|
7266
7290
|
};
|
|
7267
7291
|
} catch (error) {
|
|
7268
|
-
if (error.timedOut) {
|
|
7292
|
+
if (error && typeof error === "object" && "timedOut" in error && error.timedOut) {
|
|
7269
7293
|
throw new TimeoutError(`Execution timed out after ${timeout}ms`);
|
|
7270
7294
|
}
|
|
7271
|
-
|
|
7272
|
-
return {
|
|
7273
|
-
success: false,
|
|
7274
|
-
stdout: error.stdout || "",
|
|
7275
|
-
stderr: error.stderr || "",
|
|
7276
|
-
exitCode: error.exitCode,
|
|
7277
|
-
metadata: {
|
|
7278
|
-
executionTime: Date.now() - startTime,
|
|
7279
|
-
timestamp: new Date().toISOString()
|
|
7280
|
-
}
|
|
7281
|
-
};
|
|
7282
|
-
}
|
|
7283
|
-
throw new ExecutionError(`Execution failed: ${error.message}`);
|
|
7295
|
+
throw new ExecutionError(`Shell execution failed: ${error.message}`);
|
|
7284
7296
|
}
|
|
7285
7297
|
}
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
return ["bash", "-c", code];
|
|
7294
|
-
default:
|
|
7295
|
-
throw new ExecutionError(`Unsupported runtime: ${this.runtime}`);
|
|
7298
|
+
async upload(data, remotePath) {
|
|
7299
|
+
try {
|
|
7300
|
+
const fullPath = path6.join(this.workDir, remotePath);
|
|
7301
|
+
await fs.mkdir(path6.dirname(fullPath), { recursive: true });
|
|
7302
|
+
await fs.writeFile(fullPath, data);
|
|
7303
|
+
} catch (error) {
|
|
7304
|
+
throw new FileSystemError(`Failed to upload file: ${error.message}`);
|
|
7296
7305
|
}
|
|
7297
7306
|
}
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
} catch (error) {
|
|
7306
|
-
throw new FileSystemError(`Failed to write file: ${error.message}`);
|
|
7307
|
-
}
|
|
7308
|
-
},
|
|
7309
|
-
read: async (filePath) => {
|
|
7310
|
-
try {
|
|
7311
|
-
const fullPath = path6.join(this.workDir, filePath);
|
|
7312
|
-
return await fs.readFile(fullPath, "utf-8");
|
|
7313
|
-
} catch (error) {
|
|
7314
|
-
throw new FileSystemError(`Failed to read file: ${error.message}`);
|
|
7315
|
-
}
|
|
7316
|
-
},
|
|
7317
|
-
list: async (dirPath) => {
|
|
7318
|
-
try {
|
|
7319
|
-
const fullPath = path6.join(this.workDir, dirPath);
|
|
7320
|
-
return await fs.readdir(fullPath);
|
|
7321
|
-
} catch (error) {
|
|
7322
|
-
throw new FileSystemError(`Failed to list directory: ${error.message}`);
|
|
7323
|
-
}
|
|
7324
|
-
},
|
|
7325
|
-
delete: async (filePath) => {
|
|
7326
|
-
try {
|
|
7327
|
-
const fullPath = path6.join(this.workDir, filePath);
|
|
7328
|
-
await fs.rm(fullPath, { recursive: true, force: true });
|
|
7329
|
-
} catch (error) {
|
|
7330
|
-
throw new FileSystemError(`Failed to delete: ${error.message}`);
|
|
7331
|
-
}
|
|
7332
|
-
},
|
|
7333
|
-
exists: async (filePath) => {
|
|
7334
|
-
try {
|
|
7335
|
-
const fullPath = path6.join(this.workDir, filePath);
|
|
7336
|
-
await fs.access(fullPath);
|
|
7337
|
-
return true;
|
|
7338
|
-
} catch {
|
|
7339
|
-
return false;
|
|
7340
|
-
}
|
|
7341
|
-
}
|
|
7342
|
-
};
|
|
7307
|
+
async download(remotePath) {
|
|
7308
|
+
try {
|
|
7309
|
+
const fullPath = path6.join(this.workDir, remotePath);
|
|
7310
|
+
return await fs.readFile(fullPath);
|
|
7311
|
+
} catch (error) {
|
|
7312
|
+
throw new FileSystemError(`Failed to download file: ${error.message}`);
|
|
7313
|
+
}
|
|
7343
7314
|
}
|
|
7344
7315
|
async destroy() {
|
|
7345
7316
|
try {
|
|
7346
7317
|
await fs.rm(this.workDir, { recursive: true, force: true });
|
|
7347
|
-
} catch
|
|
7348
|
-
console.warn(`Failed to clean up work directory: ${error.message}`);
|
|
7349
|
-
}
|
|
7318
|
+
} catch {}
|
|
7350
7319
|
}
|
|
7351
7320
|
}
|
|
7352
7321
|
|
|
@@ -7360,10 +7329,8 @@ class CloudflareContainerIsolator extends Isolator {
|
|
|
7360
7329
|
serverProcess;
|
|
7361
7330
|
serverUrl;
|
|
7362
7331
|
isReady = false;
|
|
7363
|
-
|
|
7364
|
-
constructor(runtime = "node") {
|
|
7332
|
+
constructor() {
|
|
7365
7333
|
super();
|
|
7366
|
-
this.runtime = runtime;
|
|
7367
7334
|
}
|
|
7368
7335
|
async findFreePort() {
|
|
7369
7336
|
return new Promise((resolve, reject) => {
|
|
@@ -7411,17 +7378,19 @@ class CloudflareContainerIsolator extends Isolator {
|
|
|
7411
7378
|
await this.waitForReady(this.serverUrl);
|
|
7412
7379
|
this.isReady = true;
|
|
7413
7380
|
}
|
|
7414
|
-
async
|
|
7381
|
+
async shell(command, options = {}) {
|
|
7382
|
+
const { timeout = 30000, env = {} } = options;
|
|
7383
|
+
const startTime = Date.now();
|
|
7415
7384
|
await this.ensureServerRunning();
|
|
7416
7385
|
try {
|
|
7417
7386
|
const response = await fetch(`${this.serverUrl}/execute`, {
|
|
7418
7387
|
method: "POST",
|
|
7419
7388
|
headers: { "Content-Type": "application/json" },
|
|
7420
7389
|
body: JSON.stringify({
|
|
7421
|
-
code:
|
|
7422
|
-
runtime:
|
|
7423
|
-
env
|
|
7424
|
-
timeout
|
|
7390
|
+
code: command,
|
|
7391
|
+
runtime: "bash",
|
|
7392
|
+
env,
|
|
7393
|
+
timeout
|
|
7425
7394
|
})
|
|
7426
7395
|
});
|
|
7427
7396
|
if (!response.ok) {
|
|
@@ -7429,29 +7398,22 @@ class CloudflareContainerIsolator extends Isolator {
|
|
|
7429
7398
|
throw new ExecutionError(`Server execution failed: ${error}`);
|
|
7430
7399
|
}
|
|
7431
7400
|
const result = await response.json();
|
|
7432
|
-
return
|
|
7401
|
+
return {
|
|
7402
|
+
success: result.success,
|
|
7403
|
+
stdout: result.stdout || "",
|
|
7404
|
+
stderr: result.stderr || "",
|
|
7405
|
+
exitCode: result.exitCode ?? (result.success ? 0 : 1),
|
|
7406
|
+
executionTime: Date.now() - startTime
|
|
7407
|
+
};
|
|
7433
7408
|
} catch (error) {
|
|
7434
|
-
throw new ExecutionError(`
|
|
7409
|
+
throw new ExecutionError(`Shell execution failed: ${error.message}`);
|
|
7435
7410
|
}
|
|
7436
7411
|
}
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
read: async () => {
|
|
7443
|
-
throw new FileSystemError("Filesystem not yet implemented for CloudflareContainerIsolator");
|
|
7444
|
-
},
|
|
7445
|
-
list: async () => {
|
|
7446
|
-
throw new FileSystemError("Filesystem not yet implemented for CloudflareContainerIsolator");
|
|
7447
|
-
},
|
|
7448
|
-
delete: async () => {
|
|
7449
|
-
throw new FileSystemError("Filesystem not yet implemented for CloudflareContainerIsolator");
|
|
7450
|
-
},
|
|
7451
|
-
exists: async () => {
|
|
7452
|
-
throw new FileSystemError("Filesystem not yet implemented for CloudflareContainerIsolator");
|
|
7453
|
-
}
|
|
7454
|
-
};
|
|
7412
|
+
async upload(_data, _remotePath) {
|
|
7413
|
+
throw new FileSystemError("Upload not yet implemented for CloudflareContainerIsolator");
|
|
7414
|
+
}
|
|
7415
|
+
async download(_remotePath) {
|
|
7416
|
+
throw new FileSystemError("Download not yet implemented for CloudflareContainerIsolator");
|
|
7455
7417
|
}
|
|
7456
7418
|
async destroy() {
|
|
7457
7419
|
if (this.serverProcess) {
|
|
@@ -7463,103 +7425,1266 @@ class CloudflareContainerIsolator extends Isolator {
|
|
|
7463
7425
|
}
|
|
7464
7426
|
}
|
|
7465
7427
|
|
|
7466
|
-
// src/runtimes/Runtime.ts
|
|
7467
|
-
class Runtime {
|
|
7468
|
-
}
|
|
7469
|
-
|
|
7470
|
-
// src/runtimes/GenericRuntime.ts
|
|
7471
|
-
class GenericRuntime extends Runtime {
|
|
7472
|
-
isolator;
|
|
7473
|
-
constructor(isolator) {
|
|
7474
|
-
super();
|
|
7475
|
-
this.isolator = isolator;
|
|
7476
|
-
}
|
|
7477
|
-
async execute(options) {
|
|
7478
|
-
return this.isolator.execute(options);
|
|
7479
|
-
}
|
|
7480
|
-
async prepare() {}
|
|
7481
|
-
async cleanup() {}
|
|
7482
|
-
}
|
|
7483
|
-
|
|
7484
7428
|
// src/Sandbox.ts
|
|
7485
|
-
class
|
|
7429
|
+
class BaseSandbox {
|
|
7430
|
+
id;
|
|
7486
7431
|
isolator;
|
|
7487
|
-
|
|
7488
|
-
eventHandlers = new Map;
|
|
7489
|
-
fs;
|
|
7432
|
+
config;
|
|
7490
7433
|
constructor(config) {
|
|
7491
|
-
this.
|
|
7492
|
-
this.
|
|
7493
|
-
this.
|
|
7434
|
+
this.id = `sandbox-${nanoid()}`;
|
|
7435
|
+
this.config = config;
|
|
7436
|
+
this.isolator = this.createIsolator(config.isolator);
|
|
7494
7437
|
}
|
|
7495
|
-
createIsolator(isolatorType
|
|
7438
|
+
createIsolator(isolatorType) {
|
|
7496
7439
|
switch (isolatorType) {
|
|
7497
7440
|
case "local":
|
|
7498
|
-
return new LocalIsolator
|
|
7441
|
+
return new LocalIsolator;
|
|
7499
7442
|
case "cloudflare":
|
|
7500
|
-
return new CloudflareContainerIsolator
|
|
7443
|
+
return new CloudflareContainerIsolator;
|
|
7501
7444
|
case "e2b":
|
|
7502
|
-
case "firecracker":
|
|
7503
7445
|
case "docker":
|
|
7504
7446
|
throw new SandboxError(`Isolator "${isolatorType}" not yet implemented`);
|
|
7505
7447
|
default:
|
|
7506
7448
|
throw new SandboxError(`Unknown isolator type: ${isolatorType}`);
|
|
7507
7449
|
}
|
|
7508
7450
|
}
|
|
7509
|
-
|
|
7510
|
-
return
|
|
7451
|
+
async shell(command) {
|
|
7452
|
+
return this.isolator.shell(command, {
|
|
7453
|
+
timeout: this.config.limits?.timeout
|
|
7454
|
+
});
|
|
7455
|
+
}
|
|
7456
|
+
async upload(data, remotePath) {
|
|
7457
|
+
return this.isolator.upload(data, remotePath);
|
|
7458
|
+
}
|
|
7459
|
+
async download(remotePath) {
|
|
7460
|
+
return this.isolator.download(remotePath);
|
|
7461
|
+
}
|
|
7462
|
+
async destroy() {
|
|
7463
|
+
return this.isolator.destroy();
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
// ../state/dist/index.js
|
|
7467
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
7468
|
+
import { readFile as readFile2, writeFile as writeFile2, readdir, mkdir as mkdir2, rm as rm2, access, stat as fsStat } from "node:fs/promises";
|
|
7469
|
+
import { resolve, dirname as dirname2 } from "node:path";
|
|
7470
|
+
import { homedir } from "node:os";
|
|
7471
|
+
import { join as join2 } from "node:path";
|
|
7472
|
+
import { readFile as readFile22, writeFile as writeFile22, readdir as readdir2, access as access2, unlink, mkdir as mkdir22, stat, rm as rm22 } from "node:fs/promises";
|
|
7473
|
+
import { join as join22 } from "node:path";
|
|
7474
|
+
import { createHash } from "crypto";
|
|
7475
|
+
var __require2 = /* @__PURE__ */ createRequire3(import.meta.url);
|
|
7476
|
+
|
|
7477
|
+
class StateError extends Error {
|
|
7478
|
+
constructor(message) {
|
|
7479
|
+
super(message);
|
|
7480
|
+
this.name = "StateError";
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
7483
|
+
|
|
7484
|
+
class FileSystemError2 extends StateError {
|
|
7485
|
+
constructor(message) {
|
|
7486
|
+
super(message);
|
|
7487
|
+
this.name = "FileSystemError";
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
|
|
7491
|
+
class StateFS {
|
|
7492
|
+
sandbox;
|
|
7493
|
+
constructor(sandbox) {
|
|
7494
|
+
this.sandbox = sandbox;
|
|
7495
|
+
}
|
|
7496
|
+
async read(path7) {
|
|
7497
|
+
const result = await this.sandbox.shell(`cat "${path7}"`);
|
|
7498
|
+
if (!result.success) {
|
|
7499
|
+
throw new FileSystemError2(`Failed to read file: ${path7}`);
|
|
7500
|
+
}
|
|
7501
|
+
return result.stdout;
|
|
7502
|
+
}
|
|
7503
|
+
async write(path7, data) {
|
|
7504
|
+
const dir = path7.substring(0, path7.lastIndexOf("/"));
|
|
7505
|
+
if (dir) {
|
|
7506
|
+
await this.sandbox.shell(`mkdir -p "${dir}"`);
|
|
7507
|
+
}
|
|
7508
|
+
const result = await this.sandbox.shell(`cat > "${path7}" << 'SANDBOX_EOF'
|
|
7509
|
+
${data}
|
|
7510
|
+
SANDBOX_EOF`);
|
|
7511
|
+
if (!result.success) {
|
|
7512
|
+
throw new FileSystemError2(`Failed to write file: ${path7}`);
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
async exists(path7) {
|
|
7516
|
+
const result = await this.sandbox.shell(`test -e "${path7}" && echo "yes" || echo "no"`);
|
|
7517
|
+
return result.stdout.trim() === "yes";
|
|
7518
|
+
}
|
|
7519
|
+
async delete(path7) {
|
|
7520
|
+
await this.sandbox.shell(`rm -rf "${path7}"`);
|
|
7521
|
+
}
|
|
7522
|
+
async list(path7) {
|
|
7523
|
+
const result = await this.sandbox.shell(`ls -1 "${path7}" 2>/dev/null`);
|
|
7524
|
+
if (!result.success || !result.stdout.trim()) {
|
|
7525
|
+
return [];
|
|
7526
|
+
}
|
|
7527
|
+
return result.stdout.trim().split(`
|
|
7528
|
+
`).filter(Boolean);
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
|
|
7532
|
+
class StateEnv {
|
|
7533
|
+
vars;
|
|
7534
|
+
constructor(initial) {
|
|
7535
|
+
this.vars = new Map(Object.entries(initial ?? {}));
|
|
7536
|
+
}
|
|
7537
|
+
get(key) {
|
|
7538
|
+
return this.vars.get(key);
|
|
7539
|
+
}
|
|
7540
|
+
set(key, value) {
|
|
7541
|
+
this.vars.set(key, value);
|
|
7542
|
+
}
|
|
7543
|
+
has(key) {
|
|
7544
|
+
return this.vars.has(key);
|
|
7545
|
+
}
|
|
7546
|
+
delete(key) {
|
|
7547
|
+
this.vars.delete(key);
|
|
7548
|
+
}
|
|
7549
|
+
keys() {
|
|
7550
|
+
return [...this.vars.keys()];
|
|
7551
|
+
}
|
|
7552
|
+
all() {
|
|
7553
|
+
return Object.fromEntries(this.vars);
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7556
|
+
|
|
7557
|
+
class StateStorage {
|
|
7558
|
+
data;
|
|
7559
|
+
constructor(initial) {
|
|
7560
|
+
this.data = new Map(Object.entries(initial ?? {}));
|
|
7561
|
+
}
|
|
7562
|
+
getItem(key) {
|
|
7563
|
+
return this.data.get(key) ?? null;
|
|
7564
|
+
}
|
|
7565
|
+
setItem(key, value) {
|
|
7566
|
+
this.data.set(key, value);
|
|
7567
|
+
}
|
|
7568
|
+
removeItem(key) {
|
|
7569
|
+
this.data.delete(key);
|
|
7570
|
+
}
|
|
7571
|
+
clear() {
|
|
7572
|
+
this.data.clear();
|
|
7573
|
+
}
|
|
7574
|
+
keys() {
|
|
7575
|
+
return [...this.data.keys()];
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
|
|
7579
|
+
class StateLog {
|
|
7580
|
+
entries = [];
|
|
7581
|
+
fs = {
|
|
7582
|
+
write: (path7, data) => {
|
|
7583
|
+
this.entries.push({ op: "fs.write", args: { path: path7, data } });
|
|
7584
|
+
return this;
|
|
7585
|
+
},
|
|
7586
|
+
delete: (path7) => {
|
|
7587
|
+
this.entries.push({ op: "fs.delete", args: { path: path7 } });
|
|
7588
|
+
return this;
|
|
7589
|
+
}
|
|
7590
|
+
};
|
|
7591
|
+
env = {
|
|
7592
|
+
set: (key, value) => {
|
|
7593
|
+
this.entries.push({ op: "env.set", args: { key, value } });
|
|
7594
|
+
return this;
|
|
7595
|
+
},
|
|
7596
|
+
delete: (key) => {
|
|
7597
|
+
this.entries.push({ op: "env.delete", args: { key } });
|
|
7598
|
+
return this;
|
|
7599
|
+
}
|
|
7600
|
+
};
|
|
7601
|
+
storage = {
|
|
7602
|
+
set: (key, value) => {
|
|
7603
|
+
this.entries.push({ op: "storage.set", args: { key, value } });
|
|
7604
|
+
return this;
|
|
7605
|
+
},
|
|
7606
|
+
delete: (key) => {
|
|
7607
|
+
this.entries.push({ op: "storage.delete", args: { key } });
|
|
7608
|
+
return this;
|
|
7609
|
+
},
|
|
7610
|
+
clear: () => {
|
|
7611
|
+
this.entries.push({ op: "storage.clear", args: {} });
|
|
7612
|
+
return this;
|
|
7613
|
+
}
|
|
7614
|
+
};
|
|
7615
|
+
recordEntry(op, args) {
|
|
7616
|
+
this.entries.push({ op, args });
|
|
7617
|
+
return this;
|
|
7618
|
+
}
|
|
7619
|
+
getEntries() {
|
|
7620
|
+
return [...this.entries];
|
|
7621
|
+
}
|
|
7622
|
+
toJSON() {
|
|
7623
|
+
return JSON.stringify(this.entries);
|
|
7624
|
+
}
|
|
7625
|
+
static fromJSON(json) {
|
|
7626
|
+
const log = new StateLog;
|
|
7627
|
+
log.entries = JSON.parse(json);
|
|
7628
|
+
return log;
|
|
7629
|
+
}
|
|
7630
|
+
static fromEntries(entries) {
|
|
7631
|
+
const log = new StateLog;
|
|
7632
|
+
log.entries = [...entries];
|
|
7633
|
+
return log;
|
|
7634
|
+
}
|
|
7635
|
+
compact() {
|
|
7636
|
+
const fsState = new Map;
|
|
7637
|
+
const envState = new Map;
|
|
7638
|
+
const storageState = new Map;
|
|
7639
|
+
let storageClear = null;
|
|
7640
|
+
for (const entry of this.entries) {
|
|
7641
|
+
const { op, args } = entry;
|
|
7642
|
+
if (op === "fs.write") {
|
|
7643
|
+
fsState.set(args.path, entry);
|
|
7644
|
+
} else if (op === "fs.delete") {
|
|
7645
|
+
fsState.set(args.path, entry);
|
|
7646
|
+
} else if (op === "env.set") {
|
|
7647
|
+
envState.set(args.key, entry);
|
|
7648
|
+
} else if (op === "env.delete") {
|
|
7649
|
+
envState.set(args.key, entry);
|
|
7650
|
+
} else if (op === "storage.set") {
|
|
7651
|
+
storageState.set(args.key, entry);
|
|
7652
|
+
} else if (op === "storage.delete") {
|
|
7653
|
+
storageState.set(args.key, entry);
|
|
7654
|
+
} else if (op === "storage.clear") {
|
|
7655
|
+
storageClear = entry;
|
|
7656
|
+
storageState.clear();
|
|
7657
|
+
}
|
|
7658
|
+
}
|
|
7659
|
+
const compactedEntries = [];
|
|
7660
|
+
for (const entry of fsState.values()) {
|
|
7661
|
+
compactedEntries.push(entry);
|
|
7662
|
+
}
|
|
7663
|
+
for (const entry of envState.values()) {
|
|
7664
|
+
compactedEntries.push(entry);
|
|
7665
|
+
}
|
|
7666
|
+
if (storageClear) {
|
|
7667
|
+
compactedEntries.push(storageClear);
|
|
7668
|
+
}
|
|
7669
|
+
for (const entry of storageState.values()) {
|
|
7670
|
+
compactedEntries.push(entry);
|
|
7671
|
+
}
|
|
7672
|
+
return StateLog.fromEntries(compactedEntries);
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
function buildStateLog() {
|
|
7676
|
+
return new StateLog;
|
|
7677
|
+
}
|
|
7678
|
+
function loadStateLog(json) {
|
|
7679
|
+
return StateLog.fromJSON(json);
|
|
7680
|
+
}
|
|
7681
|
+
var opRegistry = {
|
|
7682
|
+
"fs.write": {
|
|
7683
|
+
namespace: "fs",
|
|
7684
|
+
method: "write",
|
|
7685
|
+
args: ["path", "data"],
|
|
7686
|
+
replay: async (target, args) => {
|
|
7687
|
+
await target.fs.write(args.path, args.data);
|
|
7688
|
+
}
|
|
7689
|
+
},
|
|
7690
|
+
"fs.delete": {
|
|
7691
|
+
namespace: "fs",
|
|
7692
|
+
method: "delete",
|
|
7693
|
+
args: ["path"],
|
|
7694
|
+
replay: async (target, args) => {
|
|
7695
|
+
await target.fs.delete(args.path);
|
|
7696
|
+
}
|
|
7697
|
+
},
|
|
7698
|
+
"env.set": {
|
|
7699
|
+
namespace: "env",
|
|
7700
|
+
method: "set",
|
|
7701
|
+
args: ["key", "value"],
|
|
7702
|
+
replay: (target, args) => {
|
|
7703
|
+
target.env.set(args.key, args.value);
|
|
7704
|
+
}
|
|
7705
|
+
},
|
|
7706
|
+
"env.delete": {
|
|
7707
|
+
namespace: "env",
|
|
7708
|
+
method: "delete",
|
|
7709
|
+
args: ["key"],
|
|
7710
|
+
replay: (target, args) => {
|
|
7711
|
+
target.env.delete(args.key);
|
|
7712
|
+
}
|
|
7713
|
+
},
|
|
7714
|
+
"storage.set": {
|
|
7715
|
+
namespace: "storage",
|
|
7716
|
+
method: "setItem",
|
|
7717
|
+
args: ["key", "value"],
|
|
7718
|
+
replay: (target, args) => {
|
|
7719
|
+
target.storage.setItem(args.key, args.value);
|
|
7720
|
+
}
|
|
7721
|
+
},
|
|
7722
|
+
"storage.delete": {
|
|
7723
|
+
namespace: "storage",
|
|
7724
|
+
method: "removeItem",
|
|
7725
|
+
args: ["key"],
|
|
7726
|
+
replay: (target, args) => {
|
|
7727
|
+
target.storage.removeItem(args.key);
|
|
7728
|
+
}
|
|
7729
|
+
},
|
|
7730
|
+
"storage.clear": {
|
|
7731
|
+
namespace: "storage",
|
|
7732
|
+
method: "clear",
|
|
7733
|
+
args: [],
|
|
7734
|
+
replay: (target) => {
|
|
7735
|
+
target.storage.clear();
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
};
|
|
7739
|
+
function findOp(namespace, method) {
|
|
7740
|
+
for (const [op, config] of Object.entries(opRegistry)) {
|
|
7741
|
+
if (config.namespace === namespace && config.method === method) {
|
|
7742
|
+
return op;
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
return;
|
|
7746
|
+
}
|
|
7747
|
+
function argsToEntry(op, methodArgs) {
|
|
7748
|
+
const config = opRegistry[op];
|
|
7749
|
+
if (!config)
|
|
7750
|
+
return {};
|
|
7751
|
+
const entry = {};
|
|
7752
|
+
config.args.forEach((name, index) => {
|
|
7753
|
+
entry[name] = methodArgs[index];
|
|
7754
|
+
});
|
|
7755
|
+
return entry;
|
|
7756
|
+
}
|
|
7757
|
+
async function replayStateLog(log, target) {
|
|
7758
|
+
for (const entry of log.getEntries()) {
|
|
7759
|
+
const config = opRegistry[entry.op];
|
|
7760
|
+
if (config) {
|
|
7761
|
+
await config.replay(target, entry.args);
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
|
|
7766
|
+
class ResourceXError extends Error {
|
|
7767
|
+
constructor(message, options) {
|
|
7768
|
+
super(message, options);
|
|
7769
|
+
this.name = "ResourceXError";
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
|
|
7773
|
+
class ParseError extends ResourceXError {
|
|
7774
|
+
url;
|
|
7775
|
+
constructor(message, url) {
|
|
7776
|
+
super(message);
|
|
7777
|
+
this.url = url;
|
|
7778
|
+
this.name = "ParseError";
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
|
|
7782
|
+
class TransportError extends ResourceXError {
|
|
7783
|
+
transport;
|
|
7784
|
+
constructor(message, transport, options) {
|
|
7785
|
+
super(message, options);
|
|
7786
|
+
this.transport = transport;
|
|
7787
|
+
this.name = "TransportError";
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
|
|
7791
|
+
class SemanticError extends ResourceXError {
|
|
7792
|
+
semantic;
|
|
7793
|
+
constructor(message, semantic, options) {
|
|
7794
|
+
super(message, options);
|
|
7795
|
+
this.semantic = semantic;
|
|
7796
|
+
this.name = "SemanticError";
|
|
7797
|
+
}
|
|
7798
|
+
}
|
|
7799
|
+
function parseARP(url) {
|
|
7800
|
+
if (!url.startsWith("arp:")) {
|
|
7801
|
+
throw new ParseError(`Invalid ARP URL: must start with "arp:"`, url);
|
|
7802
|
+
}
|
|
7803
|
+
const content = url.substring(4);
|
|
7804
|
+
const separatorIndex = content.indexOf("://");
|
|
7805
|
+
if (separatorIndex === -1) {
|
|
7806
|
+
throw new ParseError(`Invalid ARP URL: missing "://"`, url);
|
|
7807
|
+
}
|
|
7808
|
+
const typePart = content.substring(0, separatorIndex);
|
|
7809
|
+
const location = content.substring(separatorIndex + 3);
|
|
7810
|
+
const colonIndex = typePart.indexOf(":");
|
|
7811
|
+
if (colonIndex === -1) {
|
|
7812
|
+
throw new ParseError(`Invalid ARP URL: must have exactly 2 types (semantic:transport)`, url);
|
|
7813
|
+
}
|
|
7814
|
+
const semantic = typePart.substring(0, colonIndex);
|
|
7815
|
+
const transport = typePart.substring(colonIndex + 1);
|
|
7816
|
+
if (!semantic) {
|
|
7817
|
+
throw new ParseError(`Invalid ARP URL: semantic type cannot be empty`, url);
|
|
7818
|
+
}
|
|
7819
|
+
if (!transport) {
|
|
7820
|
+
throw new ParseError(`Invalid ARP URL: transport type cannot be empty`, url);
|
|
7821
|
+
}
|
|
7822
|
+
if (!location) {
|
|
7823
|
+
throw new ParseError(`Invalid ARP URL: location cannot be empty`, url);
|
|
7511
7824
|
}
|
|
7512
|
-
|
|
7513
|
-
|
|
7825
|
+
return { semantic, transport, location };
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7828
|
+
class HttpTransportHandler {
|
|
7829
|
+
name;
|
|
7830
|
+
protocol;
|
|
7831
|
+
capabilities = {
|
|
7832
|
+
canRead: true,
|
|
7833
|
+
canWrite: false,
|
|
7834
|
+
canList: false,
|
|
7835
|
+
canDelete: false,
|
|
7836
|
+
canStat: false
|
|
7837
|
+
};
|
|
7838
|
+
constructor(protocol = "https") {
|
|
7839
|
+
this.protocol = protocol;
|
|
7840
|
+
this.name = protocol;
|
|
7841
|
+
}
|
|
7842
|
+
async read(location) {
|
|
7843
|
+
const url = `${this.protocol}://${location}`;
|
|
7514
7844
|
try {
|
|
7515
|
-
const
|
|
7516
|
-
|
|
7517
|
-
|
|
7845
|
+
const response = await fetch(url);
|
|
7846
|
+
if (!response.ok) {
|
|
7847
|
+
throw new TransportError(`HTTP ${response.status}: ${response.statusText} - ${url}`, this.name);
|
|
7848
|
+
}
|
|
7849
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
7850
|
+
return Buffer.from(arrayBuffer);
|
|
7518
7851
|
} catch (error) {
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
this.
|
|
7852
|
+
if (error instanceof TransportError) {
|
|
7853
|
+
throw error;
|
|
7854
|
+
}
|
|
7855
|
+
throw new TransportError(`Network error: ${url}`, this.name, {
|
|
7856
|
+
cause: error
|
|
7857
|
+
});
|
|
7523
7858
|
}
|
|
7524
7859
|
}
|
|
7525
|
-
|
|
7526
|
-
|
|
7860
|
+
}
|
|
7861
|
+
var httpsHandler = new HttpTransportHandler("https");
|
|
7862
|
+
var httpHandler = new HttpTransportHandler("http");
|
|
7863
|
+
|
|
7864
|
+
class FileTransportHandler {
|
|
7865
|
+
name = "file";
|
|
7866
|
+
capabilities = {
|
|
7867
|
+
canRead: true,
|
|
7868
|
+
canWrite: true,
|
|
7869
|
+
canList: true,
|
|
7870
|
+
canDelete: true,
|
|
7871
|
+
canStat: true
|
|
7872
|
+
};
|
|
7873
|
+
resolvePath(location) {
|
|
7874
|
+
return resolve(process.cwd(), location);
|
|
7527
7875
|
}
|
|
7528
|
-
async
|
|
7529
|
-
|
|
7876
|
+
async read(location) {
|
|
7877
|
+
const filePath = this.resolvePath(location);
|
|
7878
|
+
try {
|
|
7879
|
+
return await readFile2(filePath);
|
|
7880
|
+
} catch (error) {
|
|
7881
|
+
const err = error;
|
|
7882
|
+
throw new TransportError(`File read error: ${err.code} - ${filePath}`, this.name, {
|
|
7883
|
+
cause: err
|
|
7884
|
+
});
|
|
7885
|
+
}
|
|
7530
7886
|
}
|
|
7531
|
-
async
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7887
|
+
async write(location, content) {
|
|
7888
|
+
const filePath = this.resolvePath(location);
|
|
7889
|
+
try {
|
|
7890
|
+
await mkdir2(dirname2(filePath), { recursive: true });
|
|
7891
|
+
await writeFile2(filePath, content);
|
|
7892
|
+
} catch (error) {
|
|
7893
|
+
const err = error;
|
|
7894
|
+
throw new TransportError(`File write error: ${err.code} - ${filePath}`, this.name, {
|
|
7895
|
+
cause: err
|
|
7896
|
+
});
|
|
7897
|
+
}
|
|
7535
7898
|
}
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7899
|
+
async list(location) {
|
|
7900
|
+
const dirPath = this.resolvePath(location);
|
|
7901
|
+
try {
|
|
7902
|
+
return await readdir(dirPath);
|
|
7903
|
+
} catch (error) {
|
|
7904
|
+
const err = error;
|
|
7905
|
+
throw new TransportError(`Directory list error: ${err.code} - ${dirPath}`, this.name, {
|
|
7906
|
+
cause: err
|
|
7907
|
+
});
|
|
7539
7908
|
}
|
|
7540
|
-
this.eventHandlers.get(event).push(handler);
|
|
7541
7909
|
}
|
|
7542
|
-
|
|
7543
|
-
const
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7910
|
+
async mkdir(location) {
|
|
7911
|
+
const dirPath = this.resolvePath(location);
|
|
7912
|
+
try {
|
|
7913
|
+
await mkdir2(dirPath, { recursive: true });
|
|
7914
|
+
} catch (error) {
|
|
7915
|
+
const err = error;
|
|
7916
|
+
throw new TransportError(`Directory create error: ${err.code} - ${dirPath}`, this.name, {
|
|
7917
|
+
cause: err
|
|
7918
|
+
});
|
|
7919
|
+
}
|
|
7920
|
+
}
|
|
7921
|
+
async exists(location) {
|
|
7922
|
+
const filePath = this.resolvePath(location);
|
|
7923
|
+
try {
|
|
7924
|
+
await access(filePath);
|
|
7925
|
+
return true;
|
|
7926
|
+
} catch {
|
|
7927
|
+
return false;
|
|
7928
|
+
}
|
|
7929
|
+
}
|
|
7930
|
+
async stat(location) {
|
|
7931
|
+
const filePath = this.resolvePath(location);
|
|
7932
|
+
try {
|
|
7933
|
+
const stats = await fsStat(filePath);
|
|
7934
|
+
return {
|
|
7935
|
+
size: stats.size,
|
|
7936
|
+
modifiedAt: stats.mtime,
|
|
7937
|
+
isDirectory: stats.isDirectory()
|
|
7938
|
+
};
|
|
7939
|
+
} catch (error) {
|
|
7940
|
+
const err = error;
|
|
7941
|
+
throw new TransportError(`File stat error: ${err.code} - ${filePath}`, this.name, {
|
|
7942
|
+
cause: err
|
|
7943
|
+
});
|
|
7944
|
+
}
|
|
7945
|
+
}
|
|
7946
|
+
async delete(location) {
|
|
7947
|
+
const filePath = this.resolvePath(location);
|
|
7948
|
+
try {
|
|
7949
|
+
await rm2(filePath, { recursive: true });
|
|
7950
|
+
} catch (error) {
|
|
7951
|
+
const err = error;
|
|
7952
|
+
throw new TransportError(`File delete error: ${err.code} - ${filePath}`, this.name, {
|
|
7953
|
+
cause: err
|
|
7954
|
+
});
|
|
7955
|
+
}
|
|
7956
|
+
}
|
|
7957
|
+
}
|
|
7958
|
+
var fileHandler = new FileTransportHandler;
|
|
7959
|
+
function deepracticeHandler(config = {}) {
|
|
7960
|
+
const parentDir = config.parentDir || homedir();
|
|
7961
|
+
const baseDir = join2(parentDir, ".deepractice");
|
|
7962
|
+
function resolvePath(location) {
|
|
7963
|
+
return join2(baseDir, location);
|
|
7964
|
+
}
|
|
7965
|
+
return {
|
|
7966
|
+
name: "deepractice",
|
|
7967
|
+
capabilities: {
|
|
7968
|
+
canRead: true,
|
|
7969
|
+
canWrite: true,
|
|
7970
|
+
canList: true,
|
|
7971
|
+
canDelete: true,
|
|
7972
|
+
canStat: true
|
|
7973
|
+
},
|
|
7974
|
+
async read(location) {
|
|
7975
|
+
const fullPath = resolvePath(location);
|
|
7976
|
+
try {
|
|
7977
|
+
return await readFile22(fullPath);
|
|
7978
|
+
} catch (error) {
|
|
7979
|
+
throw new TransportError(`Failed to read from deepractice: ${error.message}`, "deepractice", { cause: error });
|
|
7547
7980
|
}
|
|
7981
|
+
},
|
|
7982
|
+
async write(location, content) {
|
|
7983
|
+
const fullPath = resolvePath(location);
|
|
7984
|
+
try {
|
|
7985
|
+
await mkdir22(join2(fullPath, ".."), { recursive: true });
|
|
7986
|
+
await writeFile22(fullPath, content);
|
|
7987
|
+
} catch (error) {
|
|
7988
|
+
throw new TransportError(`Failed to write to deepractice: ${error.message}`, "deepractice", { cause: error });
|
|
7989
|
+
}
|
|
7990
|
+
},
|
|
7991
|
+
async list(location) {
|
|
7992
|
+
const fullPath = resolvePath(location);
|
|
7993
|
+
try {
|
|
7994
|
+
return await readdir2(fullPath);
|
|
7995
|
+
} catch (error) {
|
|
7996
|
+
throw new TransportError(`Failed to list deepractice directory: ${error.message}`, "deepractice", { cause: error });
|
|
7997
|
+
}
|
|
7998
|
+
},
|
|
7999
|
+
async exists(location) {
|
|
8000
|
+
const fullPath = resolvePath(location);
|
|
8001
|
+
try {
|
|
8002
|
+
await access2(fullPath);
|
|
8003
|
+
return true;
|
|
8004
|
+
} catch {
|
|
8005
|
+
return false;
|
|
8006
|
+
}
|
|
8007
|
+
},
|
|
8008
|
+
async stat(location) {
|
|
8009
|
+
const fullPath = resolvePath(location);
|
|
8010
|
+
try {
|
|
8011
|
+
const stats = await stat(fullPath);
|
|
8012
|
+
return {
|
|
8013
|
+
size: stats.size,
|
|
8014
|
+
isDirectory: stats.isDirectory(),
|
|
8015
|
+
modifiedAt: stats.mtime
|
|
8016
|
+
};
|
|
8017
|
+
} catch (error) {
|
|
8018
|
+
throw new TransportError(`Failed to stat deepractice resource: ${error.message}`, "deepractice", { cause: error });
|
|
8019
|
+
}
|
|
8020
|
+
},
|
|
8021
|
+
async delete(location) {
|
|
8022
|
+
const fullPath = resolvePath(location);
|
|
8023
|
+
try {
|
|
8024
|
+
const stats = await stat(fullPath);
|
|
8025
|
+
if (stats.isDirectory()) {
|
|
8026
|
+
await rm22(fullPath, { recursive: true, force: true });
|
|
8027
|
+
} else {
|
|
8028
|
+
await unlink(fullPath);
|
|
8029
|
+
}
|
|
8030
|
+
} catch (error) {
|
|
8031
|
+
throw new TransportError(`Failed to delete from deepractice: ${error.message}`, "deepractice", { cause: error });
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
8036
|
+
var handlers = new Map([
|
|
8037
|
+
["https", httpsHandler],
|
|
8038
|
+
["http", httpHandler],
|
|
8039
|
+
["file", fileHandler]
|
|
8040
|
+
]);
|
|
8041
|
+
function getTransportHandler(name) {
|
|
8042
|
+
const handler = handlers.get(name);
|
|
8043
|
+
if (!handler) {
|
|
8044
|
+
throw new TransportError(`Unsupported transport type: ${name}`, name);
|
|
8045
|
+
}
|
|
8046
|
+
return handler;
|
|
8047
|
+
}
|
|
8048
|
+
function registerTransportHandler(handler) {
|
|
8049
|
+
handlers.set(handler.name, handler);
|
|
8050
|
+
}
|
|
8051
|
+
|
|
8052
|
+
class TextSemanticHandler {
|
|
8053
|
+
name = "text";
|
|
8054
|
+
async resolve(transport, location, context) {
|
|
8055
|
+
const buffer = await transport.read(location);
|
|
8056
|
+
const text = buffer.toString("utf-8");
|
|
8057
|
+
const meta = {
|
|
8058
|
+
url: context.url,
|
|
8059
|
+
semantic: context.semantic,
|
|
8060
|
+
transport: context.transport,
|
|
8061
|
+
location: context.location,
|
|
8062
|
+
size: buffer.length,
|
|
8063
|
+
encoding: "utf-8",
|
|
8064
|
+
mimeType: "text/plain",
|
|
8065
|
+
resolvedAt: context.timestamp.toISOString()
|
|
8066
|
+
};
|
|
8067
|
+
return {
|
|
8068
|
+
type: "text",
|
|
8069
|
+
content: text,
|
|
8070
|
+
meta
|
|
8071
|
+
};
|
|
8072
|
+
}
|
|
8073
|
+
async deposit(transport, location, data, _context) {
|
|
8074
|
+
if (!transport.write) {
|
|
8075
|
+
throw new SemanticError(`Transport "${transport.name}" does not support write operation`, this.name);
|
|
8076
|
+
}
|
|
8077
|
+
const buffer = Buffer.from(data, "utf-8");
|
|
8078
|
+
await transport.write(location, buffer);
|
|
8079
|
+
}
|
|
8080
|
+
async exists(transport, location, _context) {
|
|
8081
|
+
if (transport.exists) {
|
|
8082
|
+
return transport.exists(location);
|
|
8083
|
+
}
|
|
8084
|
+
try {
|
|
8085
|
+
await transport.read(location);
|
|
8086
|
+
return true;
|
|
8087
|
+
} catch {
|
|
8088
|
+
return false;
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
8091
|
+
async delete(transport, location, _context) {
|
|
8092
|
+
if (!transport.delete) {
|
|
8093
|
+
throw new SemanticError(`Transport "${transport.name}" does not support delete operation`, this.name);
|
|
8094
|
+
}
|
|
8095
|
+
await transport.delete(location);
|
|
8096
|
+
}
|
|
8097
|
+
}
|
|
8098
|
+
var textHandler = new TextSemanticHandler;
|
|
8099
|
+
function toBuffer(data) {
|
|
8100
|
+
if (Buffer.isBuffer(data)) {
|
|
8101
|
+
return data;
|
|
8102
|
+
}
|
|
8103
|
+
if (data instanceof Uint8Array) {
|
|
8104
|
+
return Buffer.from(data);
|
|
8105
|
+
}
|
|
8106
|
+
if (data instanceof ArrayBuffer) {
|
|
8107
|
+
return Buffer.from(data);
|
|
8108
|
+
}
|
|
8109
|
+
if (Array.isArray(data)) {
|
|
8110
|
+
return Buffer.from(data);
|
|
8111
|
+
}
|
|
8112
|
+
throw new SemanticError(`Unsupported binary input type`, "binary");
|
|
8113
|
+
}
|
|
8114
|
+
|
|
8115
|
+
class BinarySemanticHandler {
|
|
8116
|
+
name = "binary";
|
|
8117
|
+
async resolve(transport, location, context) {
|
|
8118
|
+
const buffer = await transport.read(location);
|
|
8119
|
+
const meta = {
|
|
8120
|
+
url: context.url,
|
|
8121
|
+
semantic: context.semantic,
|
|
8122
|
+
transport: context.transport,
|
|
8123
|
+
location: context.location,
|
|
8124
|
+
size: buffer.length,
|
|
8125
|
+
resolvedAt: context.timestamp.toISOString()
|
|
8126
|
+
};
|
|
8127
|
+
return {
|
|
8128
|
+
type: "binary",
|
|
8129
|
+
content: buffer,
|
|
8130
|
+
meta
|
|
8131
|
+
};
|
|
8132
|
+
}
|
|
8133
|
+
async deposit(transport, location, data, _context) {
|
|
8134
|
+
if (!transport.write) {
|
|
8135
|
+
throw new SemanticError(`Transport "${transport.name}" does not support write operation`, this.name);
|
|
8136
|
+
}
|
|
8137
|
+
const buffer = toBuffer(data);
|
|
8138
|
+
await transport.write(location, buffer);
|
|
8139
|
+
}
|
|
8140
|
+
async exists(transport, location, _context) {
|
|
8141
|
+
if (transport.exists) {
|
|
8142
|
+
return transport.exists(location);
|
|
8143
|
+
}
|
|
8144
|
+
try {
|
|
8145
|
+
await transport.read(location);
|
|
8146
|
+
return true;
|
|
8147
|
+
} catch {
|
|
8148
|
+
return false;
|
|
8149
|
+
}
|
|
8150
|
+
}
|
|
8151
|
+
async delete(transport, location, _context) {
|
|
8152
|
+
if (!transport.delete) {
|
|
8153
|
+
throw new SemanticError(`Transport "${transport.name}" does not support delete operation`, this.name);
|
|
7548
8154
|
}
|
|
8155
|
+
await transport.delete(location);
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8158
|
+
var binaryHandler = new BinarySemanticHandler;
|
|
8159
|
+
var handlers2 = new Map([
|
|
8160
|
+
["text", textHandler],
|
|
8161
|
+
["binary", binaryHandler]
|
|
8162
|
+
]);
|
|
8163
|
+
function getSemanticHandler(name) {
|
|
8164
|
+
const handler = handlers2.get(name);
|
|
8165
|
+
if (!handler) {
|
|
8166
|
+
throw new SemanticError(`Unsupported semantic type: ${name}`, name);
|
|
7549
8167
|
}
|
|
8168
|
+
return handler;
|
|
8169
|
+
}
|
|
8170
|
+
function registerSemanticHandler(handler) {
|
|
8171
|
+
handlers2.set(handler.name, handler);
|
|
8172
|
+
}
|
|
8173
|
+
function createContext(url, semantic, transport, location) {
|
|
8174
|
+
return {
|
|
8175
|
+
url,
|
|
8176
|
+
semantic,
|
|
8177
|
+
transport,
|
|
8178
|
+
location,
|
|
8179
|
+
timestamp: new Date
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
8182
|
+
async function resolve2(url) {
|
|
8183
|
+
const parsed = parseARP(url);
|
|
8184
|
+
const transport = getTransportHandler(parsed.transport);
|
|
8185
|
+
const semantic = getSemanticHandler(parsed.semantic);
|
|
8186
|
+
const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
|
|
8187
|
+
return semantic.resolve(transport, parsed.location, context);
|
|
8188
|
+
}
|
|
8189
|
+
async function deposit(url, data) {
|
|
8190
|
+
const parsed = parseARP(url);
|
|
8191
|
+
const transport = getTransportHandler(parsed.transport);
|
|
8192
|
+
const semantic = getSemanticHandler(parsed.semantic);
|
|
8193
|
+
if (!semantic.deposit) {
|
|
8194
|
+
throw new SemanticError(`Semantic "${semantic.name}" does not support deposit operation`, parsed.semantic);
|
|
8195
|
+
}
|
|
8196
|
+
const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
|
|
8197
|
+
await semantic.deposit(transport, parsed.location, data, context);
|
|
8198
|
+
}
|
|
8199
|
+
async function resourceExists(url) {
|
|
8200
|
+
const parsed = parseARP(url);
|
|
8201
|
+
const transport = getTransportHandler(parsed.transport);
|
|
8202
|
+
const semantic = getSemanticHandler(parsed.semantic);
|
|
8203
|
+
const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
|
|
8204
|
+
if (semantic.exists) {
|
|
8205
|
+
return semantic.exists(transport, parsed.location, context);
|
|
8206
|
+
}
|
|
8207
|
+
if (transport.exists) {
|
|
8208
|
+
return transport.exists(parsed.location);
|
|
8209
|
+
}
|
|
8210
|
+
try {
|
|
8211
|
+
await transport.read(parsed.location);
|
|
8212
|
+
return true;
|
|
8213
|
+
} catch {
|
|
8214
|
+
return false;
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
async function resourceDelete(url) {
|
|
8218
|
+
const parsed = parseARP(url);
|
|
8219
|
+
const transport = getTransportHandler(parsed.transport);
|
|
8220
|
+
const semantic = getSemanticHandler(parsed.semantic);
|
|
8221
|
+
const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
|
|
8222
|
+
if (semantic.delete) {
|
|
8223
|
+
return semantic.delete(transport, parsed.location, context);
|
|
8224
|
+
}
|
|
8225
|
+
if (!transport.delete) {
|
|
8226
|
+
throw new SemanticError(`Neither semantic "${semantic.name}" nor transport "${transport.name}" supports delete operation`, parsed.semantic);
|
|
8227
|
+
}
|
|
8228
|
+
await transport.delete(parsed.location);
|
|
8229
|
+
}
|
|
8230
|
+
function createResourceRegistry() {
|
|
8231
|
+
const registry = new Map;
|
|
8232
|
+
return {
|
|
8233
|
+
register(definition) {
|
|
8234
|
+
if (!/^[a-z][a-z0-9-]*$/.test(definition.name)) {
|
|
8235
|
+
throw new ParseError(`Invalid resource name: "${definition.name}". Must start with lowercase letter and contain only lowercase letters, numbers, and hyphens.`, definition.name);
|
|
8236
|
+
}
|
|
8237
|
+
getSemanticHandler(definition.semantic);
|
|
8238
|
+
getTransportHandler(definition.transport);
|
|
8239
|
+
registry.set(definition.name, definition);
|
|
8240
|
+
},
|
|
8241
|
+
get(name) {
|
|
8242
|
+
return registry.get(name);
|
|
8243
|
+
},
|
|
8244
|
+
has(name) {
|
|
8245
|
+
return registry.has(name);
|
|
8246
|
+
},
|
|
8247
|
+
clear() {
|
|
8248
|
+
registry.clear();
|
|
8249
|
+
}
|
|
8250
|
+
};
|
|
8251
|
+
}
|
|
8252
|
+
|
|
8253
|
+
class ResourceX {
|
|
8254
|
+
timeout;
|
|
8255
|
+
alias;
|
|
8256
|
+
resourceRegistry;
|
|
8257
|
+
constructor(config = {}) {
|
|
8258
|
+
this.timeout = config.timeout;
|
|
8259
|
+
this.alias = config.alias || "@";
|
|
8260
|
+
this.resourceRegistry = createResourceRegistry();
|
|
8261
|
+
if (config.transports) {
|
|
8262
|
+
for (const handler of config.transports) {
|
|
8263
|
+
registerTransportHandler(handler);
|
|
8264
|
+
}
|
|
8265
|
+
}
|
|
8266
|
+
if (config.semantics) {
|
|
8267
|
+
for (const handler of config.semantics) {
|
|
8268
|
+
registerSemanticHandler(handler);
|
|
8269
|
+
}
|
|
8270
|
+
}
|
|
8271
|
+
if (config.resources) {
|
|
8272
|
+
for (const resource of config.resources) {
|
|
8273
|
+
this.resourceRegistry.register(resource);
|
|
8274
|
+
}
|
|
8275
|
+
}
|
|
8276
|
+
}
|
|
8277
|
+
parseURL(url) {
|
|
8278
|
+
let content;
|
|
8279
|
+
if (url.startsWith("arp:")) {
|
|
8280
|
+
content = url.substring(4);
|
|
8281
|
+
} else if (url.startsWith(this.alias)) {
|
|
8282
|
+
content = url.substring(this.alias.length);
|
|
8283
|
+
} else {
|
|
8284
|
+
throw new ParseError(`Invalid URL prefix: must start with "arp:" or "${this.alias}"`, url);
|
|
8285
|
+
}
|
|
8286
|
+
const separatorIndex = content.indexOf("://");
|
|
8287
|
+
if (separatorIndex === -1) {
|
|
8288
|
+
throw new ParseError(`Invalid URL format: missing "://"`, url);
|
|
8289
|
+
}
|
|
8290
|
+
const beforeSeparator = content.substring(0, separatorIndex);
|
|
8291
|
+
const location = content.substring(separatorIndex + 3);
|
|
8292
|
+
const colonCount = (beforeSeparator.match(/:/g) || []).length;
|
|
8293
|
+
if (colonCount === 1) {
|
|
8294
|
+
const parts = beforeSeparator.split(":");
|
|
8295
|
+
if (parts.length !== 2) {
|
|
8296
|
+
throw new ParseError(`Invalid ARP URL format`, url);
|
|
8297
|
+
}
|
|
8298
|
+
const [semantic, transport] = parts;
|
|
8299
|
+
if (!semantic || !transport || !location) {
|
|
8300
|
+
throw new ParseError(`Invalid ARP URL: semantic, transport, and location are required`, url);
|
|
8301
|
+
}
|
|
8302
|
+
const arpUrl = `arp:${semantic}:${transport}://${location}`;
|
|
8303
|
+
return {
|
|
8304
|
+
arpUrl,
|
|
8305
|
+
parsed: { semantic, transport, location }
|
|
8306
|
+
};
|
|
8307
|
+
}
|
|
8308
|
+
if (colonCount === 0) {
|
|
8309
|
+
const name = beforeSeparator;
|
|
8310
|
+
if (!name || !location) {
|
|
8311
|
+
throw new ParseError(`Invalid Resource URL: name and location are required`, url);
|
|
8312
|
+
}
|
|
8313
|
+
const definition = this.resourceRegistry.get(name);
|
|
8314
|
+
if (!definition) {
|
|
8315
|
+
throw new ParseError(`Unknown resource: "${name}"`, url);
|
|
8316
|
+
}
|
|
8317
|
+
const fullLocation = definition.basePath ? join22(definition.basePath, location) : location;
|
|
8318
|
+
const arpUrl = `arp:${definition.semantic}:${definition.transport}://${fullLocation}`;
|
|
8319
|
+
return {
|
|
8320
|
+
arpUrl,
|
|
8321
|
+
parsed: {
|
|
8322
|
+
semantic: definition.semantic,
|
|
8323
|
+
transport: definition.transport,
|
|
8324
|
+
location: fullLocation
|
|
8325
|
+
}
|
|
8326
|
+
};
|
|
8327
|
+
}
|
|
8328
|
+
throw new ParseError(`Invalid URL format: unexpected colon count in "${beforeSeparator}"`, url);
|
|
8329
|
+
}
|
|
8330
|
+
parse(url) {
|
|
8331
|
+
return this.parseURL(url).parsed;
|
|
8332
|
+
}
|
|
8333
|
+
async resolve(url) {
|
|
8334
|
+
const { arpUrl } = this.parseURL(url);
|
|
8335
|
+
return resolve2(arpUrl);
|
|
8336
|
+
}
|
|
8337
|
+
async deposit(url, data) {
|
|
8338
|
+
const { arpUrl } = this.parseURL(url);
|
|
8339
|
+
return deposit(arpUrl, data);
|
|
8340
|
+
}
|
|
8341
|
+
async exists(url) {
|
|
8342
|
+
const { arpUrl } = this.parseURL(url);
|
|
8343
|
+
return resourceExists(arpUrl);
|
|
8344
|
+
}
|
|
8345
|
+
async delete(url) {
|
|
8346
|
+
const { arpUrl } = this.parseURL(url);
|
|
8347
|
+
return resourceDelete(arpUrl);
|
|
8348
|
+
}
|
|
8349
|
+
}
|
|
8350
|
+
function createResourceX(config) {
|
|
8351
|
+
return new ResourceX(config);
|
|
8352
|
+
}
|
|
8353
|
+
|
|
8354
|
+
class MemoryStateStore {
|
|
8355
|
+
logs = new Map;
|
|
8356
|
+
blobs = new Map;
|
|
8357
|
+
entries = new Map;
|
|
8358
|
+
async saveLog(key, data) {
|
|
8359
|
+
this.logs.set(key, data);
|
|
8360
|
+
}
|
|
8361
|
+
async loadLog(key) {
|
|
8362
|
+
const entries = this.entries.get(key);
|
|
8363
|
+
if (entries && entries.length > 0) {
|
|
8364
|
+
return JSON.stringify(entries);
|
|
8365
|
+
}
|
|
8366
|
+
return this.logs.get(key) ?? null;
|
|
8367
|
+
}
|
|
8368
|
+
async deleteLog(key) {
|
|
8369
|
+
this.logs.delete(key);
|
|
8370
|
+
this.entries.delete(key);
|
|
8371
|
+
}
|
|
8372
|
+
async listLogs() {
|
|
8373
|
+
const keys = new Set([...this.logs.keys(), ...this.entries.keys()]);
|
|
8374
|
+
return [...keys];
|
|
8375
|
+
}
|
|
8376
|
+
async saveBlob(ref, data) {
|
|
8377
|
+
this.blobs.set(ref, data);
|
|
8378
|
+
}
|
|
8379
|
+
async loadBlob(ref) {
|
|
8380
|
+
return this.blobs.get(ref) ?? null;
|
|
8381
|
+
}
|
|
8382
|
+
async deleteBlob(ref) {
|
|
8383
|
+
this.blobs.delete(ref);
|
|
8384
|
+
}
|
|
8385
|
+
async appendEntry(sandboxId, entry) {
|
|
8386
|
+
const entries = this.entries.get(sandboxId) ?? [];
|
|
8387
|
+
entries.push(entry);
|
|
8388
|
+
this.entries.set(sandboxId, entries);
|
|
8389
|
+
}
|
|
8390
|
+
}
|
|
8391
|
+
|
|
8392
|
+
class ResourceXStateStore {
|
|
8393
|
+
rx;
|
|
8394
|
+
basePath;
|
|
8395
|
+
constructor() {
|
|
8396
|
+
this.rx = createResourceX({
|
|
8397
|
+
transports: [deepracticeHandler()]
|
|
8398
|
+
});
|
|
8399
|
+
const os = __require2("os");
|
|
8400
|
+
const path7 = __require2("path");
|
|
8401
|
+
this.basePath = path7.join(os.homedir(), ".deepractice/sandbox");
|
|
8402
|
+
}
|
|
8403
|
+
logUrl(key) {
|
|
8404
|
+
return `@text:deepractice://sandbox/state-logs/${key}.json`;
|
|
8405
|
+
}
|
|
8406
|
+
logPath(sandboxId) {
|
|
8407
|
+
const path7 = __require2("path");
|
|
8408
|
+
return path7.join(this.basePath, "state-logs", `${sandboxId}.jsonl`);
|
|
8409
|
+
}
|
|
8410
|
+
blobUrl(ref) {
|
|
8411
|
+
return `@binary:deepractice://sandbox/blobs/${ref}`;
|
|
8412
|
+
}
|
|
8413
|
+
async saveLog(key, data) {
|
|
8414
|
+
await this.rx.deposit(this.logUrl(key), data);
|
|
8415
|
+
}
|
|
8416
|
+
async loadLog(key) {
|
|
8417
|
+
const fs2 = __require2("fs/promises");
|
|
8418
|
+
const jsonlPath = this.logPath(key);
|
|
8419
|
+
try {
|
|
8420
|
+
const content = await fs2.readFile(jsonlPath, "utf-8");
|
|
8421
|
+
const entries = content.trim().split(`
|
|
8422
|
+
`).filter((line) => line).map((line) => JSON.parse(line));
|
|
8423
|
+
return JSON.stringify(entries);
|
|
8424
|
+
} catch {
|
|
8425
|
+
try {
|
|
8426
|
+
const exists = await this.rx.exists(this.logUrl(key));
|
|
8427
|
+
if (!exists)
|
|
8428
|
+
return null;
|
|
8429
|
+
const resource = await this.rx.resolve(this.logUrl(key));
|
|
8430
|
+
return resource.content;
|
|
8431
|
+
} catch {
|
|
8432
|
+
return null;
|
|
8433
|
+
}
|
|
8434
|
+
}
|
|
8435
|
+
}
|
|
8436
|
+
async deleteLog(key) {
|
|
8437
|
+
const fs2 = __require2("fs/promises");
|
|
8438
|
+
try {
|
|
8439
|
+
await fs2.unlink(this.logPath(key));
|
|
8440
|
+
} catch {}
|
|
8441
|
+
try {
|
|
8442
|
+
const exists = await this.rx.exists(this.logUrl(key));
|
|
8443
|
+
if (exists) {
|
|
8444
|
+
await this.rx.delete(this.logUrl(key));
|
|
8445
|
+
}
|
|
8446
|
+
} catch {}
|
|
8447
|
+
}
|
|
8448
|
+
async listLogs() {
|
|
8449
|
+
return [];
|
|
8450
|
+
}
|
|
8451
|
+
async appendEntry(sandboxId, entry) {
|
|
8452
|
+
const fs2 = __require2("fs/promises");
|
|
8453
|
+
const path7 = __require2("path");
|
|
8454
|
+
const filePath = this.logPath(sandboxId);
|
|
8455
|
+
await fs2.mkdir(path7.dirname(filePath), { recursive: true });
|
|
8456
|
+
const line = JSON.stringify(entry) + `
|
|
8457
|
+
`;
|
|
8458
|
+
await fs2.appendFile(filePath, line, "utf-8");
|
|
8459
|
+
}
|
|
8460
|
+
async saveBlob(ref, data) {
|
|
8461
|
+
await this.rx.deposit(this.blobUrl(ref), data);
|
|
8462
|
+
}
|
|
8463
|
+
async loadBlob(ref) {
|
|
8464
|
+
try {
|
|
8465
|
+
const exists = await this.rx.exists(this.blobUrl(ref));
|
|
8466
|
+
if (!exists)
|
|
8467
|
+
return null;
|
|
8468
|
+
const resource = await this.rx.resolve(this.blobUrl(ref));
|
|
8469
|
+
return resource.content;
|
|
8470
|
+
} catch {
|
|
8471
|
+
return null;
|
|
8472
|
+
}
|
|
8473
|
+
}
|
|
8474
|
+
async deleteBlob(ref) {
|
|
8475
|
+
try {
|
|
8476
|
+
const exists = await this.rx.exists(this.blobUrl(ref));
|
|
8477
|
+
if (exists) {
|
|
8478
|
+
await this.rx.delete(this.blobUrl(ref));
|
|
8479
|
+
}
|
|
8480
|
+
} catch {}
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
function createStateStore(options) {
|
|
8484
|
+
if (options.type === "memory") {
|
|
8485
|
+
return new MemoryStateStore;
|
|
8486
|
+
}
|
|
8487
|
+
if (options.type === "resourcex") {
|
|
8488
|
+
return new ResourceXStateStore;
|
|
8489
|
+
}
|
|
8490
|
+
throw new Error(`StateStore type "${options.type}" not implemented`);
|
|
8491
|
+
}
|
|
8492
|
+
function createRecordingProxy(target, namespace, log, onRecord) {
|
|
8493
|
+
return new Proxy(target, {
|
|
8494
|
+
get(obj, prop) {
|
|
8495
|
+
const value = obj[prop];
|
|
8496
|
+
if (typeof value !== "function") {
|
|
8497
|
+
return value;
|
|
8498
|
+
}
|
|
8499
|
+
const method = prop;
|
|
8500
|
+
const op = findOp(namespace, method);
|
|
8501
|
+
if (!op) {
|
|
8502
|
+
return value.bind(obj);
|
|
8503
|
+
}
|
|
8504
|
+
return (...args) => {
|
|
8505
|
+
const result = value.apply(obj, args);
|
|
8506
|
+
const record = () => {
|
|
8507
|
+
const entryArgs = argsToEntry(op, args);
|
|
8508
|
+
log.recordEntry(op, entryArgs);
|
|
8509
|
+
if (onRecord) {
|
|
8510
|
+
onRecord({ op, args: entryArgs });
|
|
8511
|
+
}
|
|
8512
|
+
};
|
|
8513
|
+
if (result instanceof Promise) {
|
|
8514
|
+
return result.then((res) => {
|
|
8515
|
+
record();
|
|
8516
|
+
return res;
|
|
8517
|
+
});
|
|
8518
|
+
} else {
|
|
8519
|
+
record();
|
|
8520
|
+
return result;
|
|
8521
|
+
}
|
|
8522
|
+
};
|
|
8523
|
+
}
|
|
8524
|
+
});
|
|
8525
|
+
}
|
|
8526
|
+
function createState(options) {
|
|
8527
|
+
const { sandbox, env, enableRecord, store = "resourcex", sandboxId } = options;
|
|
8528
|
+
const baseFS = new StateFS(sandbox);
|
|
8529
|
+
const baseEnv = new StateEnv(env);
|
|
8530
|
+
const baseStorage = new StateStorage;
|
|
8531
|
+
if (!enableRecord) {
|
|
8532
|
+
return {
|
|
8533
|
+
fs: baseFS,
|
|
8534
|
+
env: baseEnv,
|
|
8535
|
+
storage: baseStorage
|
|
8536
|
+
};
|
|
8537
|
+
}
|
|
8538
|
+
const stateLog = buildStateLog();
|
|
8539
|
+
const stateStore = createStateStore({ type: store });
|
|
8540
|
+
const onRecord = (entry) => {
|
|
8541
|
+
stateStore.appendEntry(sandboxId, entry).catch((err) => {
|
|
8542
|
+
console.error(`[SandboX] Failed to persist entry:`, err);
|
|
8543
|
+
});
|
|
8544
|
+
};
|
|
8545
|
+
return {
|
|
8546
|
+
fs: createRecordingProxy(baseFS, "fs", stateLog, onRecord),
|
|
8547
|
+
env: createRecordingProxy(baseEnv, "env", stateLog, onRecord),
|
|
8548
|
+
storage: createRecordingProxy(baseStorage, "storage", stateLog, onRecord),
|
|
8549
|
+
stateLog
|
|
8550
|
+
};
|
|
8551
|
+
}
|
|
8552
|
+
function generateRef(data) {
|
|
8553
|
+
const hash = createHash("sha256").update(data).digest("hex");
|
|
8554
|
+
return `sha256-${hash}`;
|
|
8555
|
+
}
|
|
8556
|
+
|
|
8557
|
+
class StateAssetsImpl {
|
|
8558
|
+
sandbox;
|
|
8559
|
+
store;
|
|
8560
|
+
uploadedPaths = new Set;
|
|
8561
|
+
constructor(options) {
|
|
8562
|
+
this.sandbox = options.sandbox;
|
|
8563
|
+
this.store = options.store;
|
|
8564
|
+
}
|
|
8565
|
+
async uploadBuffer(data, remotePath) {
|
|
8566
|
+
const ref = generateRef(data);
|
|
8567
|
+
await this.sandbox.upload(data, remotePath);
|
|
8568
|
+
await this.store.saveBlob(ref, data);
|
|
8569
|
+
this.uploadedPaths.add(remotePath);
|
|
8570
|
+
return ref;
|
|
8571
|
+
}
|
|
8572
|
+
async downloadBuffer(remotePath, options) {
|
|
8573
|
+
const data = await this.sandbox.download(remotePath);
|
|
8574
|
+
if (options?.persist) {
|
|
8575
|
+
const ref = generateRef(data);
|
|
8576
|
+
await this.store.saveBlob(ref, data);
|
|
8577
|
+
return ref;
|
|
8578
|
+
}
|
|
8579
|
+
return data;
|
|
8580
|
+
}
|
|
8581
|
+
list() {
|
|
8582
|
+
return [...this.uploadedPaths];
|
|
8583
|
+
}
|
|
8584
|
+
}
|
|
8585
|
+
// src/mixins/withState.ts
|
|
8586
|
+
function withState(Base) {
|
|
8587
|
+
return class extends Base {
|
|
8588
|
+
fs;
|
|
8589
|
+
env;
|
|
8590
|
+
storage;
|
|
8591
|
+
stateLog;
|
|
8592
|
+
constructor(config) {
|
|
8593
|
+
super(config);
|
|
8594
|
+
const stateConfig = config.state;
|
|
8595
|
+
const state = createState({
|
|
8596
|
+
sandbox: this,
|
|
8597
|
+
env: stateConfig?.env,
|
|
8598
|
+
enableRecord: stateConfig?.enableRecord,
|
|
8599
|
+
store: stateConfig?.store,
|
|
8600
|
+
sandboxId: this.id
|
|
8601
|
+
});
|
|
8602
|
+
this.fs = state.fs;
|
|
8603
|
+
this.env = state.env;
|
|
8604
|
+
this.storage = state.storage;
|
|
8605
|
+
this.stateLog = state.stateLog;
|
|
8606
|
+
if (stateConfig?.initializeLog) {
|
|
8607
|
+
replayStateLog(stateConfig.initializeLog, this);
|
|
8608
|
+
}
|
|
8609
|
+
}
|
|
8610
|
+
getStateLog() {
|
|
8611
|
+
return this.stateLog;
|
|
8612
|
+
}
|
|
8613
|
+
};
|
|
8614
|
+
}
|
|
8615
|
+
// src/mixins/withNodeExecute.ts
|
|
8616
|
+
function withNodeExecute(Base) {
|
|
8617
|
+
return class extends Base {
|
|
8618
|
+
nodeChecked = false;
|
|
8619
|
+
constructor(config) {
|
|
8620
|
+
super(config);
|
|
8621
|
+
}
|
|
8622
|
+
async execute(code) {
|
|
8623
|
+
if (!this.nodeChecked) {
|
|
8624
|
+
const check = await this.shell("which node");
|
|
8625
|
+
if (!check.success) {
|
|
8626
|
+
throw new ExecutionError("Node.js is not installed. Please install Node.js or use a different isolator (cloudflare/e2b).");
|
|
8627
|
+
}
|
|
8628
|
+
this.nodeChecked = true;
|
|
8629
|
+
}
|
|
8630
|
+
const escapedCode = code.replace(/'/g, "'\\''");
|
|
8631
|
+
const result = await this.shell(`node -e '${escapedCode}'`);
|
|
8632
|
+
return {
|
|
8633
|
+
success: result.success,
|
|
8634
|
+
stdout: result.stdout,
|
|
8635
|
+
stderr: result.stderr,
|
|
8636
|
+
exitCode: result.exitCode,
|
|
8637
|
+
executionTime: result.executionTime
|
|
8638
|
+
};
|
|
8639
|
+
}
|
|
8640
|
+
};
|
|
8641
|
+
}
|
|
8642
|
+
// src/mixins/withPythonExecute.ts
|
|
8643
|
+
function withPythonExecute(Base) {
|
|
8644
|
+
return class extends Base {
|
|
8645
|
+
pythonChecked = false;
|
|
8646
|
+
constructor(config) {
|
|
8647
|
+
super(config);
|
|
8648
|
+
}
|
|
8649
|
+
async execute(code) {
|
|
8650
|
+
if (!this.pythonChecked) {
|
|
8651
|
+
const check = await this.shell("which python3");
|
|
8652
|
+
if (!check.success) {
|
|
8653
|
+
throw new ExecutionError("Python 3 is not installed. Please install Python 3 or use a different isolator (cloudflare/e2b).");
|
|
8654
|
+
}
|
|
8655
|
+
this.pythonChecked = true;
|
|
8656
|
+
}
|
|
8657
|
+
const escapedCode = code.replace(/'/g, "'\\''");
|
|
8658
|
+
const result = await this.shell(`python3 -c '${escapedCode}'`);
|
|
8659
|
+
return {
|
|
8660
|
+
success: result.success,
|
|
8661
|
+
stdout: result.stdout,
|
|
8662
|
+
stderr: result.stderr,
|
|
8663
|
+
exitCode: result.exitCode,
|
|
8664
|
+
executionTime: result.executionTime
|
|
8665
|
+
};
|
|
8666
|
+
}
|
|
8667
|
+
};
|
|
7550
8668
|
}
|
|
7551
8669
|
export {
|
|
8670
|
+
withState,
|
|
8671
|
+
withPythonExecute,
|
|
8672
|
+
withNodeExecute,
|
|
8673
|
+
loadStateLog,
|
|
8674
|
+
buildStateLog,
|
|
7552
8675
|
TimeoutError,
|
|
8676
|
+
StateStorage,
|
|
8677
|
+
StateFS,
|
|
8678
|
+
StateError,
|
|
8679
|
+
StateEnv,
|
|
7553
8680
|
SandboxError,
|
|
7554
|
-
Sandbox,
|
|
7555
|
-
Runtime,
|
|
7556
8681
|
LocalIsolator,
|
|
7557
8682
|
Isolator,
|
|
7558
8683
|
IsolationError,
|
|
7559
|
-
|
|
7560
|
-
FileSystemError,
|
|
8684
|
+
FileSystemError2 as FileSystemError,
|
|
7561
8685
|
ExecutionError,
|
|
7562
|
-
CloudflareContainerIsolator
|
|
8686
|
+
CloudflareContainerIsolator,
|
|
8687
|
+
BaseSandbox
|
|
7563
8688
|
};
|
|
7564
8689
|
|
|
7565
|
-
//# debugId=
|
|
8690
|
+
//# debugId=09AEF7B255D0477C64756E2164756E21
|