@drej/flue 1.0.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.mts +30 -0
- package/dist/index.mjs +113 -0
- package/package.json +34 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SandboxFactory } from "@flue/runtime";
|
|
2
|
+
import { Sandbox } from "drej";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Flue `SandboxFactory` backed by a drej `Sandbox`.
|
|
7
|
+
*
|
|
8
|
+
* Pass an already-created `Sandbox` (lifecycle is the caller's responsibility —
|
|
9
|
+
* the adapter never calls `sb.close()`). Returns a factory suitable for
|
|
10
|
+
* Flue's `sandbox` agent option.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // src/sandboxes/drej.ts (Flue adapter file)
|
|
15
|
+
* import { drej } from "@drej/flue";
|
|
16
|
+
* import { Drej } from "drej";
|
|
17
|
+
* import { SQLiteAdapter } from "@drej/sqlite";
|
|
18
|
+
*
|
|
19
|
+
* const client = new Drej({ baseUrl: "http://localhost:8080", adapter: new SQLiteAdapter("./drej.db") });
|
|
20
|
+
*
|
|
21
|
+
* export default drej(
|
|
22
|
+
* await client.sandbox({ image: "node:22", resources: { cpu: "500m", memory: "256Mi" } }),
|
|
23
|
+
* );
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare function drej(sandbox: Sandbox, opts?: {
|
|
27
|
+
cwd?: string;
|
|
28
|
+
}): SandboxFactory;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { drej };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createSandboxSessionEnv } from "@flue/runtime";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
function esc(p) {
|
|
4
|
+
return `'${p.replace(/'/g, "'\\''")}'`;
|
|
5
|
+
}
|
|
6
|
+
function rejectAfter(ms) {
|
|
7
|
+
return new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`exec timed out after ${ms}ms`)), ms));
|
|
8
|
+
}
|
|
9
|
+
function uint8ToBase64(buf) {
|
|
10
|
+
let binary = "";
|
|
11
|
+
for (let i = 0; i < buf.length; i++) binary += String.fromCharCode(buf[i]);
|
|
12
|
+
return btoa(binary);
|
|
13
|
+
}
|
|
14
|
+
function base64ToUint8(b64) {
|
|
15
|
+
const binary = atob(b64);
|
|
16
|
+
const buf = new Uint8Array(binary.length);
|
|
17
|
+
for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
|
|
18
|
+
return buf;
|
|
19
|
+
}
|
|
20
|
+
var DrejSandboxApi = class {
|
|
21
|
+
sb;
|
|
22
|
+
constructor(sb) {
|
|
23
|
+
this.sb = sb;
|
|
24
|
+
}
|
|
25
|
+
async exec(command, opts) {
|
|
26
|
+
const run = Promise.resolve(this.sb.exec(command, {
|
|
27
|
+
cwd: opts?.cwd,
|
|
28
|
+
env: opts?.env,
|
|
29
|
+
strict: false
|
|
30
|
+
}));
|
|
31
|
+
if (opts?.timeoutMs == null) return run;
|
|
32
|
+
return Promise.race([run, rejectAfter(Math.ceil(opts.timeoutMs))]);
|
|
33
|
+
}
|
|
34
|
+
readFile(path) {
|
|
35
|
+
return this.sb.readFile(path);
|
|
36
|
+
}
|
|
37
|
+
async readFileBuffer(path) {
|
|
38
|
+
const { stdout } = await this.sb.exec(`base64 -w0 ${esc(path)}`);
|
|
39
|
+
return base64ToUint8(stdout.trim());
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Write a file into the sandbox.
|
|
43
|
+
*
|
|
44
|
+
* When `content` is a `Uint8Array`, the bytes are base64-encoded and piped
|
|
45
|
+
* through `base64 -d` in the container. This relies on the shell `ARG_MAX`
|
|
46
|
+
* limit (~2 MB on Linux), so binary writes are capped at roughly **1.5 MB**.
|
|
47
|
+
* For larger binary files, write via string (pre-encoded) or split writes.
|
|
48
|
+
*/
|
|
49
|
+
async writeFile(path, content) {
|
|
50
|
+
if (typeof content === "string") {
|
|
51
|
+
await this.sb.writeFile(path, content);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const b64 = uint8ToBase64(content);
|
|
55
|
+
await this.sb.exec(`echo '${b64}' | base64 -d > ${esc(path)}`);
|
|
56
|
+
}
|
|
57
|
+
async stat(path) {
|
|
58
|
+
const { stdout, exitCode } = await this.sb.exec(`stat -c '%F|%s|%Y' ${esc(path)}`, { strict: false });
|
|
59
|
+
if (exitCode !== 0) throw new Error(`stat: cannot stat '${path}': No such file or directory`);
|
|
60
|
+
const [typeStr, sizeStr, mtimeStr] = stdout.trim().split("|");
|
|
61
|
+
return {
|
|
62
|
+
isFile: typeStr === "regular file",
|
|
63
|
+
isDirectory: typeStr === "directory",
|
|
64
|
+
isSymbolicLink: typeStr === "symbolic link",
|
|
65
|
+
size: parseInt(sizeStr, 10),
|
|
66
|
+
mtime: /* @__PURE__ */ new Date(parseInt(mtimeStr, 10) * 1e3)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async readdir(path) {
|
|
70
|
+
const entries = await this.sb.listDirectory(path, { depth: 1 });
|
|
71
|
+
const prefix = (path.endsWith("/") ? path.slice(0, -1) : path) + "/";
|
|
72
|
+
return entries.filter((e) => e.path.startsWith(prefix) && !e.path.slice(prefix.length).includes("/")).map((e) => e.path.slice(prefix.length)).filter(Boolean);
|
|
73
|
+
}
|
|
74
|
+
async exists(path) {
|
|
75
|
+
const { exitCode } = await this.sb.exec(`test -e ${esc(path)}`, { strict: false });
|
|
76
|
+
return exitCode === 0;
|
|
77
|
+
}
|
|
78
|
+
async mkdir(path, opts) {
|
|
79
|
+
await this.sb.exec(`mkdir ${opts?.recursive ? "-p " : ""}${esc(path)}`);
|
|
80
|
+
}
|
|
81
|
+
async rm(path, opts) {
|
|
82
|
+
const flags = [opts?.recursive && "-r", opts?.force && "-f"].filter(Boolean).join(" ");
|
|
83
|
+
await this.sb.exec(`rm ${flags ? flags + " " : ""}${esc(path)}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Flue `SandboxFactory` backed by a drej `Sandbox`.
|
|
88
|
+
*
|
|
89
|
+
* Pass an already-created `Sandbox` (lifecycle is the caller's responsibility —
|
|
90
|
+
* the adapter never calls `sb.close()`). Returns a factory suitable for
|
|
91
|
+
* Flue's `sandbox` agent option.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* // src/sandboxes/drej.ts (Flue adapter file)
|
|
96
|
+
* import { drej } from "@drej/flue";
|
|
97
|
+
* import { Drej } from "drej";
|
|
98
|
+
* import { SQLiteAdapter } from "@drej/sqlite";
|
|
99
|
+
*
|
|
100
|
+
* const client = new Drej({ baseUrl: "http://localhost:8080", adapter: new SQLiteAdapter("./drej.db") });
|
|
101
|
+
*
|
|
102
|
+
* export default drej(
|
|
103
|
+
* await client.sandbox({ image: "node:22", resources: { cpu: "500m", memory: "256Mi" } }),
|
|
104
|
+
* );
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function drej(sandbox, opts) {
|
|
108
|
+
return { async createSessionEnv(_) {
|
|
109
|
+
return createSandboxSessionEnv(new DrejSandboxApi(sandbox), opts?.cwd ?? "/");
|
|
110
|
+
} };
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { drej };
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drej/flue",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.mts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"types": "./dist/index.d.mts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsdown",
|
|
21
|
+
"test": "bun test"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@flue/runtime": "1.0.0-beta.3",
|
|
25
|
+
"bun-types": "1.3.14",
|
|
26
|
+
"drej": "workspace:*",
|
|
27
|
+
"tsdown": "0.22.3",
|
|
28
|
+
"typescript": "6.0.3"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@flue/runtime": ">=1.0.0-beta",
|
|
32
|
+
"drej": ">=0.8.0"
|
|
33
|
+
}
|
|
34
|
+
}
|