@sandboxxjs/core 0.2.0 → 0.4.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 +6 -1
- package/dist/index.js +534 -54
- package/dist/index.js.map +8 -6
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ interface StateConfig {
|
|
|
8
8
|
initializeLog?: StateLog;
|
|
9
9
|
/** Enable state recording */
|
|
10
10
|
enableRecord?: boolean;
|
|
11
|
+
/** Store type (default: resourcex, test: memory) */
|
|
12
|
+
store?: "resourcex" | "memory";
|
|
11
13
|
}
|
|
12
14
|
interface SandboxConfig {
|
|
13
15
|
/** Isolator type */
|
|
@@ -44,9 +46,11 @@ interface PythonConfig {
|
|
|
44
46
|
useVenv?: boolean;
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
|
-
* Base Sandbox interface - 4 core APIs
|
|
49
|
+
* Base Sandbox interface - 4 core APIs + ID
|
|
48
50
|
*/
|
|
49
51
|
interface Sandbox {
|
|
52
|
+
/** Unique sandbox ID */
|
|
53
|
+
readonly id: string;
|
|
50
54
|
/** Execute shell command */
|
|
51
55
|
shell(command: string): Promise<ShellResult>;
|
|
52
56
|
/** Upload file to sandbox */
|
|
@@ -125,6 +129,7 @@ declare abstract class Isolator {
|
|
|
125
129
|
abstract destroy(): Promise<void>;
|
|
126
130
|
}
|
|
127
131
|
declare class BaseSandbox implements Sandbox {
|
|
132
|
+
readonly id: string;
|
|
128
133
|
protected isolator: Isolator;
|
|
129
134
|
protected config: SandboxConfig;
|
|
130
135
|
constructor(config: SandboxConfig);
|
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) {
|
|
@@ -7397,9 +7427,11 @@ class CloudflareContainerIsolator extends Isolator {
|
|
|
7397
7427
|
|
|
7398
7428
|
// src/Sandbox.ts
|
|
7399
7429
|
class BaseSandbox {
|
|
7430
|
+
id;
|
|
7400
7431
|
isolator;
|
|
7401
7432
|
config;
|
|
7402
7433
|
constructor(config) {
|
|
7434
|
+
this.id = `sandbox-${nanoid()}`;
|
|
7403
7435
|
this.config = config;
|
|
7404
7436
|
this.isolator = this.createIsolator(config.isolator);
|
|
7405
7437
|
}
|
|
@@ -7432,9 +7464,15 @@ class BaseSandbox {
|
|
|
7432
7464
|
}
|
|
7433
7465
|
}
|
|
7434
7466
|
// ../state/dist/index.js
|
|
7467
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
7435
7468
|
import { readFile as readFile2, writeFile as writeFile2, readdir, mkdir as mkdir2, rm as rm2, access, stat as fsStat } from "node:fs/promises";
|
|
7436
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";
|
|
7437
7474
|
import { createHash } from "crypto";
|
|
7475
|
+
var __require2 = /* @__PURE__ */ createRequire3(import.meta.url);
|
|
7438
7476
|
|
|
7439
7477
|
class StateError extends Error {
|
|
7440
7478
|
constructor(message) {
|
|
@@ -7724,57 +7762,6 @@ async function replayStateLog(log, target) {
|
|
|
7724
7762
|
}
|
|
7725
7763
|
}
|
|
7726
7764
|
}
|
|
7727
|
-
function createRecordingProxy(target, namespace, log) {
|
|
7728
|
-
return new Proxy(target, {
|
|
7729
|
-
get(obj, prop) {
|
|
7730
|
-
const value = obj[prop];
|
|
7731
|
-
if (typeof value !== "function") {
|
|
7732
|
-
return value;
|
|
7733
|
-
}
|
|
7734
|
-
const method = prop;
|
|
7735
|
-
const op = findOp(namespace, method);
|
|
7736
|
-
if (!op) {
|
|
7737
|
-
return value.bind(obj);
|
|
7738
|
-
}
|
|
7739
|
-
return (...args) => {
|
|
7740
|
-
const result = value.apply(obj, args);
|
|
7741
|
-
const record = () => {
|
|
7742
|
-
const entryArgs = argsToEntry(op, args);
|
|
7743
|
-
log.recordEntry(op, entryArgs);
|
|
7744
|
-
};
|
|
7745
|
-
if (result instanceof Promise) {
|
|
7746
|
-
return result.then((res) => {
|
|
7747
|
-
record();
|
|
7748
|
-
return res;
|
|
7749
|
-
});
|
|
7750
|
-
} else {
|
|
7751
|
-
record();
|
|
7752
|
-
return result;
|
|
7753
|
-
}
|
|
7754
|
-
};
|
|
7755
|
-
}
|
|
7756
|
-
});
|
|
7757
|
-
}
|
|
7758
|
-
function createState(options) {
|
|
7759
|
-
const { sandbox, env, enableRecord } = options;
|
|
7760
|
-
const baseFS = new StateFS(sandbox);
|
|
7761
|
-
const baseEnv = new StateEnv(env);
|
|
7762
|
-
const baseStorage = new StateStorage;
|
|
7763
|
-
if (!enableRecord) {
|
|
7764
|
-
return {
|
|
7765
|
-
fs: baseFS,
|
|
7766
|
-
env: baseEnv,
|
|
7767
|
-
storage: baseStorage
|
|
7768
|
-
};
|
|
7769
|
-
}
|
|
7770
|
-
const stateLog = buildStateLog();
|
|
7771
|
-
return {
|
|
7772
|
-
fs: createRecordingProxy(baseFS, "fs", stateLog),
|
|
7773
|
-
env: createRecordingProxy(baseEnv, "env", stateLog),
|
|
7774
|
-
storage: createRecordingProxy(baseStorage, "storage", stateLog),
|
|
7775
|
-
stateLog
|
|
7776
|
-
};
|
|
7777
|
-
}
|
|
7778
7765
|
|
|
7779
7766
|
class ResourceXError extends Error {
|
|
7780
7767
|
constructor(message, options) {
|
|
@@ -7782,6 +7769,16 @@ class ResourceXError extends Error {
|
|
|
7782
7769
|
this.name = "ResourceXError";
|
|
7783
7770
|
}
|
|
7784
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
|
+
|
|
7785
7782
|
class TransportError extends ResourceXError {
|
|
7786
7783
|
transport;
|
|
7787
7784
|
constructor(message, transport, options) {
|
|
@@ -7799,6 +7796,35 @@ class SemanticError extends ResourceXError {
|
|
|
7799
7796
|
this.name = "SemanticError";
|
|
7800
7797
|
}
|
|
7801
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);
|
|
7824
|
+
}
|
|
7825
|
+
return { semantic, transport, location };
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7802
7828
|
class HttpTransportHandler {
|
|
7803
7829
|
name;
|
|
7804
7830
|
protocol;
|
|
@@ -7930,11 +7956,99 @@ class FileTransportHandler {
|
|
|
7930
7956
|
}
|
|
7931
7957
|
}
|
|
7932
7958
|
var fileHandler = new FileTransportHandler;
|
|
7959
|
+
function agentvmHandler(config = {}) {
|
|
7960
|
+
const parentDir = config.parentDir || homedir();
|
|
7961
|
+
const baseDir = join2(parentDir, ".agentvm");
|
|
7962
|
+
function resolvePath(location) {
|
|
7963
|
+
return join2(baseDir, location);
|
|
7964
|
+
}
|
|
7965
|
+
return {
|
|
7966
|
+
name: "agentvm",
|
|
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 agentvm: ${error.message}`, "agentvm", { cause: error });
|
|
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 agentvm: ${error.message}`, "agentvm", { 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 agentvm directory: ${error.message}`, "agentvm", { 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 agentvm resource: ${error.message}`, "agentvm", { 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 agentvm: ${error.message}`, "agentvm", { cause: error });
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
7933
8036
|
var handlers = new Map([
|
|
7934
8037
|
["https", httpsHandler],
|
|
7935
8038
|
["http", httpHandler],
|
|
7936
8039
|
["file", fileHandler]
|
|
7937
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
|
+
|
|
7938
8052
|
class TextSemanticHandler {
|
|
7939
8053
|
name = "text";
|
|
7940
8054
|
async resolve(transport, location, context) {
|
|
@@ -8046,20 +8160,218 @@ var handlers2 = new Map([
|
|
|
8046
8160
|
["text", textHandler],
|
|
8047
8161
|
["binary", binaryHandler]
|
|
8048
8162
|
]);
|
|
8163
|
+
function getSemanticHandler(name) {
|
|
8164
|
+
const handler = handlers2.get(name);
|
|
8165
|
+
if (!handler) {
|
|
8166
|
+
throw new SemanticError(`Unsupported semantic type: ${name}`, name);
|
|
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
|
+
|
|
8049
8354
|
class MemoryStateStore {
|
|
8050
8355
|
logs = new Map;
|
|
8051
8356
|
blobs = new Map;
|
|
8357
|
+
entries = new Map;
|
|
8052
8358
|
async saveLog(key, data) {
|
|
8053
8359
|
this.logs.set(key, data);
|
|
8054
8360
|
}
|
|
8055
8361
|
async loadLog(key) {
|
|
8362
|
+
const entries = this.entries.get(key);
|
|
8363
|
+
if (entries && entries.length > 0) {
|
|
8364
|
+
return JSON.stringify(entries);
|
|
8365
|
+
}
|
|
8056
8366
|
return this.logs.get(key) ?? null;
|
|
8057
8367
|
}
|
|
8058
8368
|
async deleteLog(key) {
|
|
8059
8369
|
this.logs.delete(key);
|
|
8370
|
+
this.entries.delete(key);
|
|
8060
8371
|
}
|
|
8061
8372
|
async listLogs() {
|
|
8062
|
-
|
|
8373
|
+
const keys = new Set([...this.logs.keys(), ...this.entries.keys()]);
|
|
8374
|
+
return [...keys];
|
|
8063
8375
|
}
|
|
8064
8376
|
async saveBlob(ref, data) {
|
|
8065
8377
|
this.blobs.set(ref, data);
|
|
@@ -8070,6 +8382,172 @@ class MemoryStateStore {
|
|
|
8070
8382
|
async deleteBlob(ref) {
|
|
8071
8383
|
this.blobs.delete(ref);
|
|
8072
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: [agentvmHandler()]
|
|
8398
|
+
});
|
|
8399
|
+
const os = __require2("os");
|
|
8400
|
+
const path7 = __require2("path");
|
|
8401
|
+
this.basePath = path7.join(os.homedir(), ".agentvm/sandbox");
|
|
8402
|
+
}
|
|
8403
|
+
logUrl(key) {
|
|
8404
|
+
return `@text:agentvm://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:agentvm://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
|
+
};
|
|
8073
8551
|
}
|
|
8074
8552
|
function generateRef(data) {
|
|
8075
8553
|
const hash = createHash("sha256").update(data).digest("hex");
|
|
@@ -8117,7 +8595,9 @@ function withState(Base) {
|
|
|
8117
8595
|
const state = createState({
|
|
8118
8596
|
sandbox: this,
|
|
8119
8597
|
env: stateConfig?.env,
|
|
8120
|
-
enableRecord: stateConfig?.enableRecord
|
|
8598
|
+
enableRecord: stateConfig?.enableRecord,
|
|
8599
|
+
store: stateConfig?.store,
|
|
8600
|
+
sandboxId: this.id
|
|
8121
8601
|
});
|
|
8122
8602
|
this.fs = state.fs;
|
|
8123
8603
|
this.env = state.env;
|
|
@@ -8207,4 +8687,4 @@ export {
|
|
|
8207
8687
|
BaseSandbox
|
|
8208
8688
|
};
|
|
8209
8689
|
|
|
8210
|
-
//# debugId=
|
|
8690
|
+
//# debugId=4CF3CA86A60974A964756E2164756E21
|