veryfront 0.1.14 → 0.1.16
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/esm/cli/commands/serve/command.d.ts.map +1 -1
- package/esm/cli/commands/serve/command.js +8 -0
- package/esm/deno.d.ts +3 -0
- package/esm/deno.js +4 -1
- package/esm/src/platform/adapters/runtime/deno/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/runtime/deno/adapter.js +19 -2
- package/esm/src/platform/compat/http/deno-server.d.ts.map +1 -1
- package/esm/src/platform/compat/http/deno-server.js +25 -1
- package/esm/src/sandbox/index.d.ts +31 -0
- package/esm/src/sandbox/index.d.ts.map +1 -0
- package/esm/src/sandbox/index.js +30 -0
- package/esm/src/sandbox/sandbox.d.ts +48 -0
- package/esm/src/sandbox/sandbox.d.ts.map +1 -0
- package/esm/src/sandbox/sandbox.js +178 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/import-finder.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/import-finder.js +8 -2
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/index.d.ts +1 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/index.js +1 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +15 -1
- package/package.json +4 -1
- package/src/cli/commands/serve/command.ts +10 -0
- package/src/deno.js +4 -1
- package/src/src/platform/adapters/runtime/deno/adapter.ts +20 -2
- package/src/src/platform/compat/http/deno-server.ts +31 -1
- package/src/src/sandbox/index.ts +32 -0
- package/src/src/sandbox/sandbox.ts +236 -0
- package/src/src/transforms/pipeline/stages/ssr-vf-modules/import-finder.ts +9 -2
- package/src/src/transforms/pipeline/stages/ssr-vf-modules/index.ts +1 -0
- package/src/src/transforms/pipeline/stages/ssr-vf-modules/transform.ts +17 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/serve/command.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,YAAY,CAAC;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/serve/command.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,YAAY,CAAC;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAgGD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAcvE"}
|
|
@@ -21,7 +21,15 @@ async function runProxy(options) {
|
|
|
21
21
|
const { setEnv } = await import("../../../src/platform/index.js");
|
|
22
22
|
setEnv("PORT", String(options.port));
|
|
23
23
|
setEnv("HOST", options.bindAddress);
|
|
24
|
+
registerTerminationSignals((signal) => {
|
|
25
|
+
cliLogger.info(`Received ${signal}, shutting down proxy server...`);
|
|
26
|
+
exitProcess(0);
|
|
27
|
+
});
|
|
28
|
+
// DenoHttpServer.serve() blocks until the server stops,
|
|
29
|
+
// so this import keeps the process alive.
|
|
24
30
|
await import("../../../src/proxy/main.js");
|
|
31
|
+
// Keep the process alive (Deno.serve returns immediately in compiled binaries)
|
|
32
|
+
await new Promise(() => { });
|
|
25
33
|
}
|
|
26
34
|
async function runProductionServer(options) {
|
|
27
35
|
showLogo();
|
package/esm/deno.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ declare namespace _default {
|
|
|
27
27
|
"./provider": string;
|
|
28
28
|
"./fs": string;
|
|
29
29
|
"./integrations": string;
|
|
30
|
+
"./sandbox": string;
|
|
30
31
|
"./cli": string;
|
|
31
32
|
};
|
|
32
33
|
let imports: {
|
|
@@ -45,6 +46,7 @@ declare namespace _default {
|
|
|
45
46
|
"veryfront/react/fonts": string;
|
|
46
47
|
"veryfront/react/components/ai": string;
|
|
47
48
|
"veryfront/components/ai": string;
|
|
49
|
+
"veryfront/sandbox": string;
|
|
48
50
|
"veryfront/agent/react": string;
|
|
49
51
|
"veryfront/agent/testing": string;
|
|
50
52
|
"veryfront/agent/middleware": string;
|
|
@@ -127,6 +129,7 @@ declare namespace _default {
|
|
|
127
129
|
"#veryfront/testing/bdd.ts": string;
|
|
128
130
|
"#veryfront/testing/deno-compat": string;
|
|
129
131
|
"#veryfront/testing/utils": string;
|
|
132
|
+
"#veryfront/sandbox": string;
|
|
130
133
|
"#veryfront/tool": string;
|
|
131
134
|
"#veryfront/tool/schema": string;
|
|
132
135
|
"#veryfront/transforms": string;
|
package/esm/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"nodeModulesDir": "auto",
|
|
5
5
|
"exclude": [
|
|
6
6
|
"npm/",
|
|
@@ -37,6 +37,7 @@ export default {
|
|
|
37
37
|
"./provider": "./src/provider/index.ts",
|
|
38
38
|
"./fs": "./src/fs/index.ts",
|
|
39
39
|
"./integrations": "./src/integrations/index.ts",
|
|
40
|
+
"./sandbox": "./src/sandbox/index.ts",
|
|
40
41
|
"./cli": "./cli/main.ts"
|
|
41
42
|
},
|
|
42
43
|
"imports": {
|
|
@@ -55,6 +56,7 @@ export default {
|
|
|
55
56
|
"veryfront/react/fonts": "./src/react/fonts/index.ts",
|
|
56
57
|
"veryfront/react/components/ai": "./src/react/components/ai/index.ts",
|
|
57
58
|
"veryfront/components/ai": "./src/react/components/ai/index.ts",
|
|
59
|
+
"veryfront/sandbox": "./src/sandbox/index.ts",
|
|
58
60
|
"veryfront/agent/react": "./src/agent/react/index.ts",
|
|
59
61
|
"veryfront/agent/testing": "./src/agent/testing/index.ts",
|
|
60
62
|
"veryfront/agent/middleware": "./src/agent/middleware/index.ts",
|
|
@@ -137,6 +139,7 @@ export default {
|
|
|
137
139
|
"#veryfront/testing/bdd.ts": "./src/testing/bdd.ts",
|
|
138
140
|
"#veryfront/testing/deno-compat": "./src/testing/deno-compat.ts",
|
|
139
141
|
"#veryfront/testing/utils": "./src/testing/utils.ts",
|
|
142
|
+
"#veryfront/sandbox": "./src/sandbox/index.ts",
|
|
140
143
|
"#veryfront/tool": "./src/tool/index.ts",
|
|
141
144
|
"#veryfront/tool/schema": "./src/tool/schema/index.ts",
|
|
142
145
|
"#veryfront/transforms": "./src/transforms/index.ts",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/runtime/deno/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,8BAA8B,CAAC;AAGxD,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAElB,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAiHvB,cAAM,qBAAsB,YAAW,iBAAiB;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAKhD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC;IAY/C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYrC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW;CAsErE;AAED,cAAM,sBAAuB,YAAW,kBAAkB;IACxD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAOrC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAInC;AAED,cAAM,iBAAkB,YAAW,aAAa;IAC9C,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,gBAAgB;CAO7D;AAED,cAAM,gBAAiB,YAAW,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE;IAejE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAanC;AAwBD,qBAAa,WAAY,YAAW,cAAc;IAChD,QAAQ,CAAC,EAAE,EAAG,MAAM,CAAU;IAC9B,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,EAAE,wBAA+B;IAC1C,QAAQ,CAAC,GAAG,yBAAgC;IAC5C,QAAQ,CAAC,MAAM,oBAA2B;IAC1C,QAAQ,CAAC,KAAK,mBAA0B;IAExC,QAAQ,CAAC,YAAY;;;;;;;;;;MAUnB;IAEF,OAAO,CAAC,YAAY,CAA2B;IAE/C,KAAK,CACH,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,EACnF,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/runtime/deno/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,8BAA8B,CAAC;AAGxD,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAElB,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAiHvB,cAAM,qBAAsB,YAAW,iBAAiB;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAKhD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC;IAY/C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYrC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW;CAsErE;AAED,cAAM,sBAAuB,YAAW,kBAAkB;IACxD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAOrC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAInC;AAED,cAAM,iBAAkB,YAAW,aAAa;IAC9C,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,gBAAgB;CAO7D;AAED,cAAM,gBAAiB,YAAW,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE;IAejE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAanC;AAwBD,qBAAa,WAAY,YAAW,cAAc;IAChD,QAAQ,CAAC,EAAE,EAAG,MAAM,CAAU;IAC9B,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,EAAE,wBAA+B;IAC1C,QAAQ,CAAC,GAAG,yBAAgC;IAC5C,QAAQ,CAAC,MAAM,oBAA2B;IAC1C,QAAQ,CAAC,KAAK,mBAA0B;IAExC,QAAQ,CAAC,YAAY;;;;;;;;;;MAUnB;IAEF,OAAO,CAAC,YAAY,CAA2B;IAE/C,KAAK,CACH,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,EACnF,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,MAAM,CAAC;IAqEZ,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAGhC;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|
|
@@ -312,17 +312,34 @@ export class DenoAdapter {
|
|
|
312
312
|
// dnt rewrites both `Deno.*` and `globalThis.*` to use @deno/shim-deno which lacks .serve.
|
|
313
313
|
// `self` is not shimmed by dnt and equals `globalThis` in Deno.
|
|
314
314
|
const nativeDeno = self["Deno"];
|
|
315
|
+
// Access native Response via `self` to bypass dnt shim transform.
|
|
316
|
+
// In npm packages, dnt replaces Response with undici's polyfill,
|
|
317
|
+
// but Deno.serve requires native Response instances.
|
|
318
|
+
const NativeResponse = self
|
|
319
|
+
.Response;
|
|
315
320
|
const server = nativeDeno.serve({
|
|
316
321
|
port,
|
|
317
322
|
hostname,
|
|
318
323
|
signal,
|
|
319
324
|
handler: async (request) => {
|
|
320
325
|
try {
|
|
321
|
-
|
|
326
|
+
const response = await wrappedHandler(request);
|
|
327
|
+
// If already native (compiled binary), return as-is
|
|
328
|
+
if (response instanceof NativeResponse)
|
|
329
|
+
return response;
|
|
330
|
+
// Re-wrap polyfilled Response as native Response.
|
|
331
|
+
// dnt replaces Response with undici's polyfill which fails
|
|
332
|
+
// Deno.serve's native instanceof check.
|
|
333
|
+
const r = response;
|
|
334
|
+
return new NativeResponse(r.body, {
|
|
335
|
+
status: r.status,
|
|
336
|
+
statusText: r.statusText,
|
|
337
|
+
headers: r.headers,
|
|
338
|
+
});
|
|
322
339
|
}
|
|
323
340
|
catch (error) {
|
|
324
341
|
serverLogger.error("Request handler error:", error);
|
|
325
|
-
return new
|
|
342
|
+
return new NativeResponse("Internal Server Error", { status: 500 });
|
|
326
343
|
}
|
|
327
344
|
},
|
|
328
345
|
onListen: (params) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno-server.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/deno-server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGpE,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,eAAe,CAAC,CAAkB;IAEpC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"deno-server.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/deno-server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGpE,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,eAAe,CAAC,CAAkB;IAEpC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2CxE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAIvB"}
|
|
@@ -8,7 +8,31 @@ export class DenoHttpServer {
|
|
|
8
8
|
onListen?.({ hostname, port });
|
|
9
9
|
// Access native Deno.serve via `self` to bypass dnt shim transform.
|
|
10
10
|
const nativeDeno = self["Deno"];
|
|
11
|
-
|
|
11
|
+
// Access native Response via `self` to bypass dnt shim transform.
|
|
12
|
+
// In npm packages, dnt replaces Response with undici's polyfill,
|
|
13
|
+
// but Deno.serve requires native Response instances.
|
|
14
|
+
const NativeResponse = self
|
|
15
|
+
.Response;
|
|
16
|
+
const wrappedHandler = async (req) => {
|
|
17
|
+
const response = await handler(req);
|
|
18
|
+
// If already native (compiled binary or WebSocket upgrade), return as-is
|
|
19
|
+
if (response instanceof NativeResponse)
|
|
20
|
+
return response;
|
|
21
|
+
// Re-wrap polyfilled Response as native Response.
|
|
22
|
+
// At runtime, `response` may be an undici Response (from dnt shim) that
|
|
23
|
+
// fails Deno's native instanceof check. Cast to access its properties.
|
|
24
|
+
const r = response;
|
|
25
|
+
return new NativeResponse(r.body, {
|
|
26
|
+
status: r.status,
|
|
27
|
+
statusText: r.statusText,
|
|
28
|
+
headers: r.headers,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const httpServer = nativeDeno.serve({ port, hostname, signal: serveSignal }, wrappedHandler);
|
|
32
|
+
// Block until the server stops (e.g. via signal abort).
|
|
33
|
+
// Deno.serve() returns synchronously; without awaiting .finished
|
|
34
|
+
// the event loop drains and the process exits in compiled binaries.
|
|
35
|
+
await httpServer.finished;
|
|
12
36
|
}
|
|
13
37
|
close() {
|
|
14
38
|
this.abortController?.abort();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox module for ephemeral compute environments.
|
|
3
|
+
*
|
|
4
|
+
* Provides the `Sandbox` class for creating and interacting with
|
|
5
|
+
* isolated execution environments, and re-exports `createBashTool`
|
|
6
|
+
* for AI agent integration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Sandbox } from "veryfront/sandbox";
|
|
11
|
+
*
|
|
12
|
+
* const sandbox = await Sandbox.create({ authToken: userJwt });
|
|
13
|
+
* const result = await sandbox.executeCommand("echo hello");
|
|
14
|
+
* console.log(result.stdout); // "hello\n"
|
|
15
|
+
* await sandbox.close();
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example With bash-tool for AI agents:
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Sandbox, createBashTool } from "veryfront/sandbox";
|
|
21
|
+
*
|
|
22
|
+
* const sandbox = await Sandbox.create({ authToken });
|
|
23
|
+
* const { tools } = await createBashTool({ sandbox });
|
|
24
|
+
* // Pass tools to agent...
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import "../../_dnt.polyfills.js";
|
|
30
|
+
export { type ExecResult, type ExecStreamEvent, Sandbox, type SandboxOptions } from "./sandbox.js";
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox module for ephemeral compute environments.
|
|
3
|
+
*
|
|
4
|
+
* Provides the `Sandbox` class for creating and interacting with
|
|
5
|
+
* isolated execution environments, and re-exports `createBashTool`
|
|
6
|
+
* for AI agent integration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Sandbox } from "veryfront/sandbox";
|
|
11
|
+
*
|
|
12
|
+
* const sandbox = await Sandbox.create({ authToken: userJwt });
|
|
13
|
+
* const result = await sandbox.executeCommand("echo hello");
|
|
14
|
+
* console.log(result.stdout); // "hello\n"
|
|
15
|
+
* await sandbox.close();
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example With bash-tool for AI agents:
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Sandbox, createBashTool } from "veryfront/sandbox";
|
|
21
|
+
*
|
|
22
|
+
* const sandbox = await Sandbox.create({ authToken });
|
|
23
|
+
* const { tools } = await createBashTool({ sandbox });
|
|
24
|
+
* // Pass tools to agent...
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import "../../_dnt.polyfills.js";
|
|
30
|
+
export { Sandbox } from "./sandbox.js";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface SandboxOptions {
|
|
2
|
+
/** Base URL of the Veryfront API. Defaults to VERYFRONT_API_URL env. */
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
/** User's JWT for authentication. */
|
|
5
|
+
authToken: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ExecResult {
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
exitCode: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ExecStreamEvent {
|
|
13
|
+
type: "stdout" | "stderr" | "exit" | "error";
|
|
14
|
+
data?: string;
|
|
15
|
+
exitCode?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class Sandbox {
|
|
18
|
+
private endpoint;
|
|
19
|
+
private sessionId;
|
|
20
|
+
private authToken;
|
|
21
|
+
private apiUrl;
|
|
22
|
+
private constructor();
|
|
23
|
+
/** Create a new sandbox session. Claims a warm pod or creates a new one. */
|
|
24
|
+
static create(options: SandboxOptions): Promise<Sandbox>;
|
|
25
|
+
/** Reconnect to an existing sandbox session. */
|
|
26
|
+
static get(id: string, options: SandboxOptions): Promise<Sandbox>;
|
|
27
|
+
private static waitForReady;
|
|
28
|
+
/** Execute a bash command in the sandbox and return buffered result. */
|
|
29
|
+
executeCommand(command: string): Promise<ExecResult>;
|
|
30
|
+
/** Execute a bash command with streaming output (NDJSON). */
|
|
31
|
+
executeStream(command: string): AsyncGenerator<ExecStreamEvent>;
|
|
32
|
+
/** Read a file from the sandbox workspace. */
|
|
33
|
+
readFile(path: string): Promise<string>;
|
|
34
|
+
/** Write files to the sandbox workspace. */
|
|
35
|
+
writeFiles(files: Array<{
|
|
36
|
+
path: string;
|
|
37
|
+
content: string;
|
|
38
|
+
}>): Promise<void>;
|
|
39
|
+
/** Send a heartbeat to prevent idle timeout. */
|
|
40
|
+
heartbeat(): Promise<void>;
|
|
41
|
+
/** Close the sandbox session and mark for deletion. */
|
|
42
|
+
close(): Promise<void>;
|
|
43
|
+
/** Get the session ID. */
|
|
44
|
+
get id(): string;
|
|
45
|
+
/** Get the sandbox endpoint URL. */
|
|
46
|
+
get url(): string;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/sandbox.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,OAAO;IAEhB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IAJhB,OAAO;IAOP,4EAA4E;WAC/D,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IA6B9D,gDAAgD;WACnC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;mBAmBlD,YAAY;IA0BjC,wEAAwE;IAClE,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAsB1D,6DAA6D;IACtD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,eAAe,CAAC;IAsCtE,8CAA8C;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAe7C,4CAA4C;IACtC,UAAU,CACd,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC;IAehB,gDAAgD;IAC1C,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAOhC,uDAAuD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B,0BAA0B;IAC1B,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,oCAAoC;IACpC,IAAI,GAAG,IAAI,MAAM,CAEhB;CACF"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox client SDK for ephemeral compute environments.
|
|
3
|
+
*
|
|
4
|
+
* Implements the bash-tool Sandbox interface for seamless integration
|
|
5
|
+
* with AI agent tool loops.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
10
|
+
export class Sandbox {
|
|
11
|
+
endpoint;
|
|
12
|
+
sessionId;
|
|
13
|
+
authToken;
|
|
14
|
+
apiUrl;
|
|
15
|
+
constructor(endpoint, sessionId, authToken, apiUrl) {
|
|
16
|
+
this.endpoint = endpoint;
|
|
17
|
+
this.sessionId = sessionId;
|
|
18
|
+
this.authToken = authToken;
|
|
19
|
+
this.apiUrl = apiUrl;
|
|
20
|
+
}
|
|
21
|
+
/** Create a new sandbox session. Claims a warm pod or creates a new one. */
|
|
22
|
+
static async create(options) {
|
|
23
|
+
const apiUrl = options.apiUrl ||
|
|
24
|
+
(typeof dntShim.Deno !== "undefined"
|
|
25
|
+
? dntShim.Deno.env.get("VERYFRONT_API_URL")
|
|
26
|
+
: process.env.VERYFRONT_API_URL) ||
|
|
27
|
+
"https://api.veryfront.com";
|
|
28
|
+
const res = await dntShim.fetch(`${apiUrl}/sandbox-sessions`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: `Bearer ${options.authToken}`,
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(`Failed to create sandbox: ${res.status} ${await res.text()}`);
|
|
37
|
+
}
|
|
38
|
+
const { id, endpoint, status } = await res.json();
|
|
39
|
+
// If not yet running, poll until ready
|
|
40
|
+
if (status !== "running") {
|
|
41
|
+
await Sandbox.waitForReady(apiUrl, id, options.authToken);
|
|
42
|
+
}
|
|
43
|
+
return new Sandbox(endpoint, id, options.authToken, apiUrl);
|
|
44
|
+
}
|
|
45
|
+
/** Reconnect to an existing sandbox session. */
|
|
46
|
+
static async get(id, options) {
|
|
47
|
+
const apiUrl = options.apiUrl ||
|
|
48
|
+
(typeof dntShim.Deno !== "undefined"
|
|
49
|
+
? dntShim.Deno.env.get("VERYFRONT_API_URL")
|
|
50
|
+
: process.env.VERYFRONT_API_URL) ||
|
|
51
|
+
"https://api.veryfront.com";
|
|
52
|
+
const res = await dntShim.fetch(`${apiUrl}/sandbox-sessions/${id}`, {
|
|
53
|
+
headers: { Authorization: `Bearer ${options.authToken}` },
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
throw new Error(`Failed to get sandbox: ${res.status} ${await res.text()}`);
|
|
57
|
+
}
|
|
58
|
+
const { endpoint } = await res.json();
|
|
59
|
+
return new Sandbox(endpoint, id, options.authToken, apiUrl);
|
|
60
|
+
}
|
|
61
|
+
static async waitForReady(apiUrl, id, authToken, maxWaitMs = 60_000, pollIntervalMs = 2_000) {
|
|
62
|
+
const start = Date.now();
|
|
63
|
+
while (Date.now() - start < maxWaitMs) {
|
|
64
|
+
await new Promise((r) => dntShim.setTimeout(r, pollIntervalMs));
|
|
65
|
+
const res = await dntShim.fetch(`${apiUrl}/sandbox-sessions/${id}`, {
|
|
66
|
+
headers: { Authorization: `Bearer ${authToken}` },
|
|
67
|
+
});
|
|
68
|
+
if (res.ok) {
|
|
69
|
+
const data = await res.json();
|
|
70
|
+
if (data.status === "running")
|
|
71
|
+
return;
|
|
72
|
+
if (data.status === "error" || data.status === "deleting") {
|
|
73
|
+
throw new Error(`Sandbox failed to start: status=${data.status}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
throw new Error("Sandbox did not become ready within timeout");
|
|
78
|
+
}
|
|
79
|
+
/** Execute a bash command in the sandbox and return buffered result. */
|
|
80
|
+
async executeCommand(command) {
|
|
81
|
+
let stdout = "";
|
|
82
|
+
let stderr = "";
|
|
83
|
+
let exitCode = 1;
|
|
84
|
+
for await (const event of this.executeStream(command)) {
|
|
85
|
+
switch (event.type) {
|
|
86
|
+
case "stdout":
|
|
87
|
+
stdout += event.data ?? "";
|
|
88
|
+
break;
|
|
89
|
+
case "stderr":
|
|
90
|
+
stderr += event.data ?? "";
|
|
91
|
+
break;
|
|
92
|
+
case "exit":
|
|
93
|
+
exitCode = event.exitCode ?? 1;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { stdout, stderr, exitCode };
|
|
98
|
+
}
|
|
99
|
+
/** Execute a bash command with streaming output (NDJSON). */
|
|
100
|
+
async *executeStream(command) {
|
|
101
|
+
const res = await dntShim.fetch(`${this.endpoint}/exec`, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: {
|
|
104
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
},
|
|
107
|
+
body: JSON.stringify({ command }),
|
|
108
|
+
});
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
throw new Error(`Exec failed: ${res.status} ${await res.text()}`);
|
|
111
|
+
}
|
|
112
|
+
const reader = res.body.getReader();
|
|
113
|
+
const decoder = new TextDecoder();
|
|
114
|
+
let buffer = "";
|
|
115
|
+
while (true) {
|
|
116
|
+
const { done, value } = await reader.read();
|
|
117
|
+
if (done)
|
|
118
|
+
break;
|
|
119
|
+
buffer += decoder.decode(value, { stream: true });
|
|
120
|
+
const lines = buffer.split("\n");
|
|
121
|
+
buffer = lines.pop();
|
|
122
|
+
for (const line of lines) {
|
|
123
|
+
if (line.trim()) {
|
|
124
|
+
yield JSON.parse(line);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (buffer.trim()) {
|
|
129
|
+
yield JSON.parse(buffer);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Read a file from the sandbox workspace. */
|
|
133
|
+
async readFile(path) {
|
|
134
|
+
const res = await dntShim.fetch(`${this.endpoint}/file?path=${encodeURIComponent(path)}`, {
|
|
135
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok) {
|
|
138
|
+
throw new Error(`Read file failed: ${res.status} ${await res.text()}`);
|
|
139
|
+
}
|
|
140
|
+
return res.text();
|
|
141
|
+
}
|
|
142
|
+
/** Write files to the sandbox workspace. */
|
|
143
|
+
async writeFiles(files) {
|
|
144
|
+
const res = await dntShim.fetch(`${this.endpoint}/files`, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: {
|
|
147
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
148
|
+
"Content-Type": "application/json",
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({ files }),
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok) {
|
|
153
|
+
throw new Error(`Write files failed: ${res.status} ${await res.text()}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Send a heartbeat to prevent idle timeout. */
|
|
157
|
+
async heartbeat() {
|
|
158
|
+
await dntShim.fetch(`${this.apiUrl}/sandbox-sessions/${this.sessionId}/heartbeat`, {
|
|
159
|
+
method: "POST",
|
|
160
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** Close the sandbox session and mark for deletion. */
|
|
164
|
+
async close() {
|
|
165
|
+
await dntShim.fetch(`${this.apiUrl}/sandbox-sessions/${this.sessionId}`, {
|
|
166
|
+
method: "DELETE",
|
|
167
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/** Get the session ID. */
|
|
171
|
+
get id() {
|
|
172
|
+
return this.sessionId;
|
|
173
|
+
}
|
|
174
|
+
/** Get the sandbox endpoint URL. */
|
|
175
|
+
get url() {
|
|
176
|
+
return this.endpoint;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-finder.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/pipeline/stages/ssr-vf-modules/import-finder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"import-finder.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/pipeline/stages/ssr-vf-modules/import-finder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB1D"}
|
|
@@ -22,9 +22,15 @@ export function findVfModuleImports(code) {
|
|
|
22
22
|
*/
|
|
23
23
|
export function findRelativeImports(code) {
|
|
24
24
|
const imports = [];
|
|
25
|
-
|
|
25
|
+
// Match: from "./foo" or from "../bar"
|
|
26
|
+
const fromPattern = /from\s*["'](\.\.?\/[^"']+)["']/g;
|
|
27
|
+
// Match side-effect imports: import "./foo" or import "../bar" (no `from`)
|
|
28
|
+
const sideEffectPattern = /import\s*["'](\.\.?\/[^"']+)["']/g;
|
|
26
29
|
let match;
|
|
27
|
-
while ((match =
|
|
30
|
+
while ((match = fromPattern.exec(code)) !== null) {
|
|
31
|
+
imports.push(match[1]);
|
|
32
|
+
}
|
|
33
|
+
while ((match = sideEffectPattern.exec(code)) !== null) {
|
|
28
34
|
imports.push(match[1]);
|
|
29
35
|
}
|
|
30
36
|
return [...new Set(imports)];
|
|
@@ -24,6 +24,7 @@ export declare const _testExports: {
|
|
|
24
24
|
resolveVeryfrontSourcePath: typeof resolveVeryfrontSourcePath;
|
|
25
25
|
resolveAndTransformVeryfrontImport: typeof resolveAndTransformVeryfrontImport;
|
|
26
26
|
FRAMEWORK_ROOT: string;
|
|
27
|
+
EMBEDDED_SRC_DIR: string;
|
|
27
28
|
EXTENSIONS: string[];
|
|
28
29
|
};
|
|
29
30
|
export declare const ssrVfModulesPlugin: TransformPlugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/pipeline/stages/ssr-vf-modules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAGL,kCAAkC,EAEnC,MAAM,gBAAgB,CAAC;AAUxB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,kCAAkC,EAClC,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,EACV,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAexB,eAAO,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/pipeline/stages/ssr-vf-modules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAGL,kCAAkC,EAEnC,MAAM,gBAAgB,CAAC;AAUxB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,kCAAkC,EAClC,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,EACV,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAexB,eAAO,MAAM,YAAY;;;;;;;;;;CAUxB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eA+EhC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/pipeline/stages/ssr-vf-modules/transform.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAYrE,OAAO,
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/pipeline/stages/ssr-vf-modules/transform.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAYrE,OAAO,EAOL,KAAK,gBAAgB,EAGtB,MAAM,gBAAgB,CAAC;AAExB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,gBAAgB,EACrB,oBAAoB,UAAQ,EAC5B,KAAK,SAAI,GACR,OAAO,CAAC,MAAM,CAAC,CAoPjB;AAED;;;GAGG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4CxB;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,CAEjB"}
|
|
@@ -15,7 +15,7 @@ import { VERSION } from "../../../../utils/version.js";
|
|
|
15
15
|
import { buildReactUrl, getReactImportMap } from "../../../import-rewriter/url-builder.js";
|
|
16
16
|
import { findRelativeImports } from "./import-finder.js";
|
|
17
17
|
import { resolveRelativeFrameworkImport, resolveVeryfrontSourcePath } from "./path-resolver.js";
|
|
18
|
-
import { FRAMEWORK_ROOT, frameworkFileCache, frameworkWriteFlight, LOG_PREFIX, MAX_RELATIVE_IMPORT_DEPTH, transformingFiles, veryfrontTransformCache, } from "./constants.js";
|
|
18
|
+
import { EMBEDDED_SRC_DIR, FRAMEWORK_ROOT, frameworkFileCache, frameworkWriteFlight, LOG_PREFIX, MAX_RELATIVE_IMPORT_DEPTH, transformingFiles, veryfrontTransformCache, } from "./constants.js";
|
|
19
19
|
/**
|
|
20
20
|
* Check if a transformed code string is a cycle placeholder.
|
|
21
21
|
* Cycle placeholders are returned when transformFrameworkCode detects a cycle
|
|
@@ -159,6 +159,10 @@ export async function transformFrameworkCode(content, sourcePath, ctx, throwOnMi
|
|
|
159
159
|
// Safety: MAX_RELATIVE_IMPORT_DEPTH limits recursion, transformingFiles detects
|
|
160
160
|
// cycles, and frameworkFileCache deduplicates already-transformed files.
|
|
161
161
|
const relativeReplacements = new Map();
|
|
162
|
+
// Prefixes for framework source directories - files outside these are
|
|
163
|
+
// already-compiled JS (e.g. dnt shims) that should not be recursively transformed.
|
|
164
|
+
const frameworkSrcDir = join(FRAMEWORK_ROOT, "src") + "/";
|
|
165
|
+
const embeddedSrcDirPrefix = EMBEDDED_SRC_DIR + "/";
|
|
162
166
|
{
|
|
163
167
|
const relativeImports = findRelativeImports(transformed);
|
|
164
168
|
for (const specifier of relativeImports) {
|
|
@@ -174,6 +178,16 @@ export async function transformFrameworkCode(content, sourcePath, ctx, throwOnMi
|
|
|
174
178
|
logger.warn(`${LOG_PREFIX} Could not resolve relative import "${specifier}" in ${sourcePath}`);
|
|
175
179
|
continue;
|
|
176
180
|
}
|
|
181
|
+
// Files outside framework source directories (e.g. _dnt.shims.js,
|
|
182
|
+
// _dnt.polyfills.js) are already-compiled JS with bare npm imports
|
|
183
|
+
// that Deno resolves natively. Skip recursive transformation and
|
|
184
|
+
// just point to the file directly.
|
|
185
|
+
const isFrameworkSource = resolvedPath.startsWith(frameworkSrcDir) ||
|
|
186
|
+
resolvedPath.startsWith(embeddedSrcDirPrefix);
|
|
187
|
+
if (!isFrameworkSource) {
|
|
188
|
+
relativeReplacements.set(specifier, `file://${resolvedPath}`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
177
191
|
// Check if this dependency was already transformed (by absolute path)
|
|
178
192
|
const existingFileUrl = frameworkFileCache.get(resolvedPath);
|
|
179
193
|
if (existingFileUrl) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -94,6 +94,9 @@
|
|
|
94
94
|
"./integrations": {
|
|
95
95
|
"import": "./esm/src/integrations/index.js"
|
|
96
96
|
},
|
|
97
|
+
"./sandbox": {
|
|
98
|
+
"import": "./esm/src/sandbox/index.js"
|
|
99
|
+
},
|
|
97
100
|
"./cli": {
|
|
98
101
|
"import": "./esm/cli/main.js"
|
|
99
102
|
},
|
|
@@ -37,7 +37,17 @@ async function runProxy(options: ServeOptions): Promise<void> {
|
|
|
37
37
|
setEnv("PORT", String(options.port));
|
|
38
38
|
setEnv("HOST", options.bindAddress);
|
|
39
39
|
|
|
40
|
+
registerTerminationSignals((signal) => {
|
|
41
|
+
cliLogger.info(`Received ${signal}, shutting down proxy server...`);
|
|
42
|
+
exitProcess(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// DenoHttpServer.serve() blocks until the server stops,
|
|
46
|
+
// so this import keeps the process alive.
|
|
40
47
|
await import("../../../src/proxy/main.js");
|
|
48
|
+
|
|
49
|
+
// Keep the process alive (Deno.serve returns immediately in compiled binaries)
|
|
50
|
+
await new Promise(() => {});
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
async function runProductionServer(options: ServeOptions): Promise<void> {
|
package/src/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"nodeModulesDir": "auto",
|
|
5
5
|
"exclude": [
|
|
6
6
|
"npm/",
|
|
@@ -37,6 +37,7 @@ export default {
|
|
|
37
37
|
"./provider": "./src/provider/index.ts",
|
|
38
38
|
"./fs": "./src/fs/index.ts",
|
|
39
39
|
"./integrations": "./src/integrations/index.ts",
|
|
40
|
+
"./sandbox": "./src/sandbox/index.ts",
|
|
40
41
|
"./cli": "./cli/main.ts"
|
|
41
42
|
},
|
|
42
43
|
"imports": {
|
|
@@ -55,6 +56,7 @@ export default {
|
|
|
55
56
|
"veryfront/react/fonts": "./src/react/fonts/index.ts",
|
|
56
57
|
"veryfront/react/components/ai": "./src/react/components/ai/index.ts",
|
|
57
58
|
"veryfront/components/ai": "./src/react/components/ai/index.ts",
|
|
59
|
+
"veryfront/sandbox": "./src/sandbox/index.ts",
|
|
58
60
|
"veryfront/agent/react": "./src/agent/react/index.ts",
|
|
59
61
|
"veryfront/agent/testing": "./src/agent/testing/index.ts",
|
|
60
62
|
"veryfront/agent/middleware": "./src/agent/middleware/index.ts",
|
|
@@ -137,6 +139,7 @@ export default {
|
|
|
137
139
|
"#veryfront/testing/bdd.ts": "./src/testing/bdd.ts",
|
|
138
140
|
"#veryfront/testing/deno-compat": "./src/testing/deno-compat.ts",
|
|
139
141
|
"#veryfront/testing/utils": "./src/testing/utils.ts",
|
|
142
|
+
"#veryfront/sandbox": "./src/sandbox/index.ts",
|
|
140
143
|
"#veryfront/tool": "./src/tool/index.ts",
|
|
141
144
|
"#veryfront/tool/schema": "./src/tool/schema/index.ts",
|
|
142
145
|
"#veryfront/transforms": "./src/transforms/index.ts",
|
|
@@ -397,16 +397,34 @@ export class DenoAdapter implements RuntimeAdapter {
|
|
|
397
397
|
// dnt rewrites both `Deno.*` and `globalThis.*` to use @deno/shim-deno which lacks .serve.
|
|
398
398
|
// `self` is not shimmed by dnt and equals `globalThis` in Deno.
|
|
399
399
|
const nativeDeno = (self as unknown as Record<string, typeof dntShim.Deno>)["Deno"]!;
|
|
400
|
+
|
|
401
|
+
// Access native Response via `self` to bypass dnt shim transform.
|
|
402
|
+
// In npm packages, dnt replaces Response with undici's polyfill,
|
|
403
|
+
// but Deno.serve requires native Response instances.
|
|
404
|
+
const NativeResponse = (self as unknown as { Response: typeof dntShim.Response })
|
|
405
|
+
.Response;
|
|
406
|
+
|
|
400
407
|
const server = nativeDeno.serve({
|
|
401
408
|
port,
|
|
402
409
|
hostname,
|
|
403
410
|
signal,
|
|
404
411
|
handler: async (request) => {
|
|
405
412
|
try {
|
|
406
|
-
|
|
413
|
+
const response: dntShim.Response = await wrappedHandler(request);
|
|
414
|
+
// If already native (compiled binary), return as-is
|
|
415
|
+
if (response instanceof NativeResponse) return response;
|
|
416
|
+
// Re-wrap polyfilled Response as native Response.
|
|
417
|
+
// dnt replaces Response with undici's polyfill which fails
|
|
418
|
+
// Deno.serve's native instanceof check.
|
|
419
|
+
const r = response as unknown as dntShim.Response;
|
|
420
|
+
return new NativeResponse(r.body, {
|
|
421
|
+
status: r.status,
|
|
422
|
+
statusText: r.statusText,
|
|
423
|
+
headers: r.headers,
|
|
424
|
+
});
|
|
407
425
|
} catch (error) {
|
|
408
426
|
serverLogger.error("Request handler error:", error);
|
|
409
|
-
return new
|
|
427
|
+
return new NativeResponse("Internal Server Error", { status: 500 });
|
|
410
428
|
}
|
|
411
429
|
},
|
|
412
430
|
onListen: (params) => {
|
|
@@ -15,7 +15,37 @@ export class DenoHttpServer implements HttpServer {
|
|
|
15
15
|
|
|
16
16
|
// Access native Deno.serve via `self` to bypass dnt shim transform.
|
|
17
17
|
const nativeDeno = (self as unknown as Record<string, typeof dntShim.Deno>)["Deno"]!;
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
// Access native Response via `self` to bypass dnt shim transform.
|
|
20
|
+
// In npm packages, dnt replaces Response with undici's polyfill,
|
|
21
|
+
// but Deno.serve requires native Response instances.
|
|
22
|
+
const NativeResponse = (self as unknown as { Response: typeof dntShim.Response })
|
|
23
|
+
.Response;
|
|
24
|
+
|
|
25
|
+
const wrappedHandler: Handler = async (req) => {
|
|
26
|
+
const response: dntShim.Response = await handler(req);
|
|
27
|
+
// If already native (compiled binary or WebSocket upgrade), return as-is
|
|
28
|
+
if (response instanceof NativeResponse) return response;
|
|
29
|
+
// Re-wrap polyfilled Response as native Response.
|
|
30
|
+
// At runtime, `response` may be an undici Response (from dnt shim) that
|
|
31
|
+
// fails Deno's native instanceof check. Cast to access its properties.
|
|
32
|
+
const r = response as unknown as dntShim.Response;
|
|
33
|
+
return new NativeResponse(r.body, {
|
|
34
|
+
status: r.status,
|
|
35
|
+
statusText: r.statusText,
|
|
36
|
+
headers: r.headers,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const httpServer = nativeDeno.serve(
|
|
41
|
+
{ port, hostname, signal: serveSignal },
|
|
42
|
+
wrappedHandler,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Block until the server stops (e.g. via signal abort).
|
|
46
|
+
// Deno.serve() returns synchronously; without awaiting .finished
|
|
47
|
+
// the event loop drains and the process exits in compiled binaries.
|
|
48
|
+
await httpServer.finished;
|
|
19
49
|
}
|
|
20
50
|
|
|
21
51
|
close(): Promise<void> {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox module for ephemeral compute environments.
|
|
3
|
+
*
|
|
4
|
+
* Provides the `Sandbox` class for creating and interacting with
|
|
5
|
+
* isolated execution environments, and re-exports `createBashTool`
|
|
6
|
+
* for AI agent integration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Sandbox } from "veryfront/sandbox";
|
|
11
|
+
*
|
|
12
|
+
* const sandbox = await Sandbox.create({ authToken: userJwt });
|
|
13
|
+
* const result = await sandbox.executeCommand("echo hello");
|
|
14
|
+
* console.log(result.stdout); // "hello\n"
|
|
15
|
+
* await sandbox.close();
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example With bash-tool for AI agents:
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Sandbox, createBashTool } from "veryfront/sandbox";
|
|
21
|
+
*
|
|
22
|
+
* const sandbox = await Sandbox.create({ authToken });
|
|
23
|
+
* const { tools } = await createBashTool({ sandbox });
|
|
24
|
+
* // Pass tools to agent...
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import "../../_dnt.polyfills.js";
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
export { type ExecResult, type ExecStreamEvent, Sandbox, type SandboxOptions } from "./sandbox.js";
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox client SDK for ephemeral compute environments.
|
|
3
|
+
*
|
|
4
|
+
* Implements the bash-tool Sandbox interface for seamless integration
|
|
5
|
+
* with AI agent tool loops.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export interface SandboxOptions {
|
|
13
|
+
/** Base URL of the Veryfront API. Defaults to VERYFRONT_API_URL env. */
|
|
14
|
+
apiUrl?: string;
|
|
15
|
+
/** User's JWT for authentication. */
|
|
16
|
+
authToken: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ExecResult {
|
|
20
|
+
stdout: string;
|
|
21
|
+
stderr: string;
|
|
22
|
+
exitCode: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ExecStreamEvent {
|
|
26
|
+
type: "stdout" | "stderr" | "exit" | "error";
|
|
27
|
+
data?: string;
|
|
28
|
+
exitCode?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class Sandbox {
|
|
32
|
+
private constructor(
|
|
33
|
+
private endpoint: string,
|
|
34
|
+
private sessionId: string,
|
|
35
|
+
private authToken: string,
|
|
36
|
+
private apiUrl: string,
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
/** Create a new sandbox session. Claims a warm pod or creates a new one. */
|
|
40
|
+
static async create(options: SandboxOptions): Promise<Sandbox> {
|
|
41
|
+
const apiUrl = options.apiUrl ||
|
|
42
|
+
(typeof dntShim.Deno !== "undefined"
|
|
43
|
+
? dntShim.Deno.env.get("VERYFRONT_API_URL")
|
|
44
|
+
: process.env.VERYFRONT_API_URL) ||
|
|
45
|
+
"https://api.veryfront.com";
|
|
46
|
+
|
|
47
|
+
const res = await dntShim.fetch(`${apiUrl}/sandbox-sessions`, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: `Bearer ${options.authToken}`,
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
throw new Error(`Failed to create sandbox: ${res.status} ${await res.text()}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { id, endpoint, status } = await res.json();
|
|
60
|
+
|
|
61
|
+
// If not yet running, poll until ready
|
|
62
|
+
if (status !== "running") {
|
|
63
|
+
await Sandbox.waitForReady(apiUrl, id, options.authToken);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return new Sandbox(endpoint, id, options.authToken, apiUrl);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Reconnect to an existing sandbox session. */
|
|
70
|
+
static async get(id: string, options: SandboxOptions): Promise<Sandbox> {
|
|
71
|
+
const apiUrl = options.apiUrl ||
|
|
72
|
+
(typeof dntShim.Deno !== "undefined"
|
|
73
|
+
? dntShim.Deno.env.get("VERYFRONT_API_URL")
|
|
74
|
+
: process.env.VERYFRONT_API_URL) ||
|
|
75
|
+
"https://api.veryfront.com";
|
|
76
|
+
|
|
77
|
+
const res = await dntShim.fetch(`${apiUrl}/sandbox-sessions/${id}`, {
|
|
78
|
+
headers: { Authorization: `Bearer ${options.authToken}` },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
throw new Error(`Failed to get sandbox: ${res.status} ${await res.text()}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const { endpoint } = await res.json();
|
|
86
|
+
return new Sandbox(endpoint, id, options.authToken, apiUrl);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private static async waitForReady(
|
|
90
|
+
apiUrl: string,
|
|
91
|
+
id: string,
|
|
92
|
+
authToken: string,
|
|
93
|
+
maxWaitMs = 60_000,
|
|
94
|
+
pollIntervalMs = 2_000,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const start = Date.now();
|
|
97
|
+
while (Date.now() - start < maxWaitMs) {
|
|
98
|
+
await new Promise((r) => dntShim.setTimeout(r, pollIntervalMs));
|
|
99
|
+
|
|
100
|
+
const res = await dntShim.fetch(`${apiUrl}/sandbox-sessions/${id}`, {
|
|
101
|
+
headers: { Authorization: `Bearer ${authToken}` },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (res.ok) {
|
|
105
|
+
const data = await res.json();
|
|
106
|
+
if (data.status === "running") return;
|
|
107
|
+
if (data.status === "error" || data.status === "deleting") {
|
|
108
|
+
throw new Error(`Sandbox failed to start: status=${data.status}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw new Error("Sandbox did not become ready within timeout");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Execute a bash command in the sandbox and return buffered result. */
|
|
116
|
+
async executeCommand(command: string): Promise<ExecResult> {
|
|
117
|
+
let stdout = "";
|
|
118
|
+
let stderr = "";
|
|
119
|
+
let exitCode = 1;
|
|
120
|
+
|
|
121
|
+
for await (const event of this.executeStream(command)) {
|
|
122
|
+
switch (event.type) {
|
|
123
|
+
case "stdout":
|
|
124
|
+
stdout += event.data ?? "";
|
|
125
|
+
break;
|
|
126
|
+
case "stderr":
|
|
127
|
+
stderr += event.data ?? "";
|
|
128
|
+
break;
|
|
129
|
+
case "exit":
|
|
130
|
+
exitCode = event.exitCode ?? 1;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { stdout, stderr, exitCode };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Execute a bash command with streaming output (NDJSON). */
|
|
139
|
+
async *executeStream(command: string): AsyncGenerator<ExecStreamEvent> {
|
|
140
|
+
const res = await dntShim.fetch(`${this.endpoint}/exec`, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: {
|
|
143
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
144
|
+
"Content-Type": "application/json",
|
|
145
|
+
},
|
|
146
|
+
body: JSON.stringify({ command }),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!res.ok) {
|
|
150
|
+
throw new Error(`Exec failed: ${res.status} ${await res.text()}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const reader = res.body!.getReader();
|
|
154
|
+
const decoder = new TextDecoder();
|
|
155
|
+
let buffer = "";
|
|
156
|
+
|
|
157
|
+
while (true) {
|
|
158
|
+
const { done, value } = await reader.read();
|
|
159
|
+
if (done) break;
|
|
160
|
+
|
|
161
|
+
buffer += decoder.decode(value, { stream: true });
|
|
162
|
+
const lines = buffer.split("\n");
|
|
163
|
+
buffer = lines.pop()!;
|
|
164
|
+
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
if (line.trim()) {
|
|
167
|
+
yield JSON.parse(line) as ExecStreamEvent;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (buffer.trim()) {
|
|
173
|
+
yield JSON.parse(buffer) as ExecStreamEvent;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Read a file from the sandbox workspace. */
|
|
178
|
+
async readFile(path: string): Promise<string> {
|
|
179
|
+
const res = await dntShim.fetch(
|
|
180
|
+
`${this.endpoint}/file?path=${encodeURIComponent(path)}`,
|
|
181
|
+
{
|
|
182
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (!res.ok) {
|
|
187
|
+
throw new Error(`Read file failed: ${res.status} ${await res.text()}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return res.text();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Write files to the sandbox workspace. */
|
|
194
|
+
async writeFiles(
|
|
195
|
+
files: Array<{ path: string; content: string }>,
|
|
196
|
+
): Promise<void> {
|
|
197
|
+
const res = await dntShim.fetch(`${this.endpoint}/files`, {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: {
|
|
200
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
201
|
+
"Content-Type": "application/json",
|
|
202
|
+
},
|
|
203
|
+
body: JSON.stringify({ files }),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (!res.ok) {
|
|
207
|
+
throw new Error(`Write files failed: ${res.status} ${await res.text()}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Send a heartbeat to prevent idle timeout. */
|
|
212
|
+
async heartbeat(): Promise<void> {
|
|
213
|
+
await dntShim.fetch(`${this.apiUrl}/sandbox-sessions/${this.sessionId}/heartbeat`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Close the sandbox session and mark for deletion. */
|
|
220
|
+
async close(): Promise<void> {
|
|
221
|
+
await dntShim.fetch(`${this.apiUrl}/sandbox-sessions/${this.sessionId}`, {
|
|
222
|
+
method: "DELETE",
|
|
223
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Get the session ID. */
|
|
228
|
+
get id(): string {
|
|
229
|
+
return this.sessionId;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Get the sandbox endpoint URL. */
|
|
233
|
+
get url(): string {
|
|
234
|
+
return this.endpoint;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -26,10 +26,17 @@ export function findVfModuleImports(code: string): string[] {
|
|
|
26
26
|
*/
|
|
27
27
|
export function findRelativeImports(code: string): string[] {
|
|
28
28
|
const imports: string[] = [];
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
// Match: from "./foo" or from "../bar"
|
|
31
|
+
const fromPattern = /from\s*["'](\.\.?\/[^"']+)["']/g;
|
|
32
|
+
// Match side-effect imports: import "./foo" or import "../bar" (no `from`)
|
|
33
|
+
const sideEffectPattern = /import\s*["'](\.\.?\/[^"']+)["']/g;
|
|
30
34
|
|
|
31
35
|
let match: RegExpExecArray | null;
|
|
32
|
-
while ((match =
|
|
36
|
+
while ((match = fromPattern.exec(code)) !== null) {
|
|
37
|
+
imports.push(match[1]!);
|
|
38
|
+
}
|
|
39
|
+
while ((match = sideEffectPattern.exec(code)) !== null) {
|
|
33
40
|
imports.push(match[1]!);
|
|
34
41
|
}
|
|
35
42
|
|
|
@@ -18,6 +18,7 @@ import { buildReactUrl, getReactImportMap } from "../../../import-rewriter/url-b
|
|
|
18
18
|
import { findRelativeImports } from "./import-finder.js";
|
|
19
19
|
import { resolveRelativeFrameworkImport, resolveVeryfrontSourcePath } from "./path-resolver.js";
|
|
20
20
|
import {
|
|
21
|
+
EMBEDDED_SRC_DIR,
|
|
21
22
|
FRAMEWORK_ROOT,
|
|
22
23
|
frameworkFileCache,
|
|
23
24
|
frameworkWriteFlight,
|
|
@@ -193,6 +194,11 @@ export async function transformFrameworkCode(
|
|
|
193
194
|
// cycles, and frameworkFileCache deduplicates already-transformed files.
|
|
194
195
|
const relativeReplacements = new Map<string, string>();
|
|
195
196
|
|
|
197
|
+
// Prefixes for framework source directories - files outside these are
|
|
198
|
+
// already-compiled JS (e.g. dnt shims) that should not be recursively transformed.
|
|
199
|
+
const frameworkSrcDir = join(FRAMEWORK_ROOT, "src") + "/";
|
|
200
|
+
const embeddedSrcDirPrefix = EMBEDDED_SRC_DIR + "/";
|
|
201
|
+
|
|
196
202
|
{
|
|
197
203
|
const relativeImports = findRelativeImports(transformed);
|
|
198
204
|
|
|
@@ -215,6 +221,17 @@ export async function transformFrameworkCode(
|
|
|
215
221
|
continue;
|
|
216
222
|
}
|
|
217
223
|
|
|
224
|
+
// Files outside framework source directories (e.g. _dnt.shims.js,
|
|
225
|
+
// _dnt.polyfills.js) are already-compiled JS with bare npm imports
|
|
226
|
+
// that Deno resolves natively. Skip recursive transformation and
|
|
227
|
+
// just point to the file directly.
|
|
228
|
+
const isFrameworkSource = resolvedPath.startsWith(frameworkSrcDir) ||
|
|
229
|
+
resolvedPath.startsWith(embeddedSrcDirPrefix);
|
|
230
|
+
if (!isFrameworkSource) {
|
|
231
|
+
relativeReplacements.set(specifier, `file://${resolvedPath}`);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
218
235
|
// Check if this dependency was already transformed (by absolute path)
|
|
219
236
|
const existingFileUrl = frameworkFileCache.get(resolvedPath);
|
|
220
237
|
if (existingFileUrl) {
|