freestyle-sandboxes 0.0.37 → 0.0.40

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  var index$1 = require('../index.cjs');
4
4
  var zod = require('zod');
5
- var index = require('../index-BBXyg0JQ.cjs');
5
+ var index = require('../index-H7UNEAjs.cjs');
6
6
  require('@hey-api/client-fetch');
7
7
 
8
8
  var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
@@ -1,6 +1,6 @@
1
1
  import { FreestyleSandboxes } from '../index.mjs';
2
2
  import { z } from 'zod';
3
- import { g as getDefaultExportFromCjs, z as zodToJsonSchema, a as executeCodeSchema, e as executeCodeDescription } from '../index-DCF70Xbq.mjs';
3
+ import { g as getDefaultExportFromCjs, z as zodToJsonSchema, a as executeCodeSchema, e as executeCodeDescription } from '../index-D1ulQeJR.mjs';
4
4
  import '@hey-api/client-fetch';
5
5
 
6
6
  var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var zod = require('zod');
4
- var index = require('../index-BBXyg0JQ.cjs');
4
+ var index = require('../index-H7UNEAjs.cjs');
5
5
  var index$1 = require('../index.cjs');
6
6
  require('@hey-api/client-fetch');
7
7
 
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { e as executeCodeDescription, a as executeCodeSchema } from '../index-DCF70Xbq.mjs';
2
+ import { e as executeCodeDescription, a as executeCodeSchema } from '../index-D1ulQeJR.mjs';
3
3
  import { FreestyleSandboxes } from '../index.mjs';
4
4
  import '@hey-api/client-fetch';
5
5
 
@@ -33,7 +33,8 @@ const prepareDirForDeployment = async (directory) => {
33
33
  nodir: true,
34
34
  ignore: ["**/node_modules/**"],
35
35
  absolute: false,
36
- dot: true
36
+ dot: true,
37
+ posix: true
37
38
  });
38
39
  for (const relativePath of patterns) {
39
40
  try {
@@ -59,7 +60,8 @@ const prepareDirForDeploymentSync = (directory) => {
59
60
  nodir: true,
60
61
  ignore: ["**/node_modules/**"],
61
62
  absolute: false,
62
- dot: true
63
+ dot: true,
64
+ posix: true
63
65
  });
64
66
  for (const relativePath of patterns) {
65
67
  try {
@@ -10,7 +10,8 @@ const prepareDirForDeployment = async (directory) => {
10
10
  nodir: true,
11
11
  ignore: ["**/node_modules/**"],
12
12
  absolute: false,
13
- dot: true
13
+ dot: true,
14
+ posix: true
14
15
  });
15
16
  for (const relativePath of patterns) {
16
17
  try {
@@ -36,7 +37,8 @@ const prepareDirForDeploymentSync = (directory) => {
36
37
  nodir: true,
37
38
  ignore: ["**/node_modules/**"],
38
39
  absolute: false,
39
- dot: true
40
+ dot: true,
41
+ posix: true
40
42
  });
41
43
  for (const relativePath of patterns) {
42
44
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freestyle-sandboxes",
3
- "version": "0.0.37",
3
+ "version": "0.0.40",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -46,6 +46,16 @@
46
46
  "default": "./dist/langgraph/index.mjs"
47
47
  }
48
48
  },
49
+ "./expo": {
50
+ "require": {
51
+ "types": "./dist/expo/index.d.cts",
52
+ "default": "./dist/expo/index.cjs"
53
+ },
54
+ "import": {
55
+ "types": "./dist/expo/index.d.mts",
56
+ "default": "./dist/expo/index.mjs"
57
+ }
58
+ },
49
59
  "./utils": {
50
60
  "require": {
51
61
  "types": "./dist/utils/index.d.cts",
@@ -67,16 +77,18 @@
67
77
  "description": "",
68
78
  "devDependencies": {
69
79
  "@hey-api/openapi-ts": "^0.60.1",
70
- "pkgroll": "^2.6.0",
71
80
  "@langchain/core": "^0.3.38",
72
81
  "@langchain/langgraph": "^0.2.44",
73
82
  "@mastra/core": "^0.6.0",
74
83
  "ai": "^4.0.25",
75
- "humanlayer": "^0.7.0"
84
+ "humanlayer": "^0.7.0",
85
+ "pkgroll": "^2.6.0"
76
86
  },
77
87
  "dependencies": {
78
88
  "@hey-api/client-fetch": "^0.5.7",
89
+ "expo-router": "^4.0.20",
79
90
  "glob": "^11.0.1",
91
+ "hono": "^4.7.5",
80
92
  "openai": "^4.77.3",
81
93
  "openapi": "^1.0.1",
82
94
  "zod": "^3.24.1"
package/src/ai/index.ts CHANGED
@@ -6,7 +6,6 @@ import {
6
6
  import { FreestyleSandboxes } from "..";
7
7
  import { tool } from "ai";
8
8
  import { z } from "zod";
9
- import { P } from "../../dist/types.gen-BuhQ5LpB";
10
9
 
11
10
  export const executeCodeSchema = z.object({
12
11
  script: z.string().describe(`
@@ -52,9 +51,11 @@ export const executeTool = (
52
51
  config: FreestyleExecuteScriptParamsConfiguration & {
53
52
  apiKey: string;
54
53
  onResult?: (_v: {
54
+ toolCallId: string;
55
55
  input: {
56
56
  script: string;
57
- } & Record<string, unknown>;
57
+ [key: string]: unknown;
58
+ };
58
59
  result: FreestyleExecuteScriptResultSuccess | HandleExecuteScriptError;
59
60
  }) => void | Promise<void>;
60
61
  truncateOutput?: boolean;
@@ -69,11 +70,12 @@ export const executeTool = (
69
70
  return tool({
70
71
  description: executeCodeDescription(envVars, nodeModules),
71
72
  parameters: executeCodeSchema,
72
- execute: async ({ script, ...otherParams }) => {
73
+ execute: async ({ script, ...otherParams }, { toolCallId }) => {
73
74
  try {
74
75
  const res = await api.executeScript(script, config);
75
76
  if (config.onResult) {
76
77
  await config.onResult({
78
+ toolCallId,
77
79
  result: res,
78
80
  input: {
79
81
  script,
@@ -0,0 +1,389 @@
1
+ import type {
2
+ ExpoRoutesManifestV1,
3
+ RouteInfo,
4
+ } from "expo-router/build/routes-manifest";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+
8
+ import { createRequire } from "module";
9
+ const require = createRequire(import.meta.url);
10
+ // import { ExpoRouterServerManifestV1FunctionRoute } from "./types";
11
+
12
+ const debug = console.log;
13
+
14
+ function getProcessedManifest(path: string): ExpoRoutesManifestV1<RegExp> {
15
+ // TODO: JSON Schema for validation
16
+ const routesManifest = JSON.parse(
17
+ fs.readFileSync(path, "utf-8")
18
+ ) as ExpoRoutesManifestV1;
19
+
20
+ const parsed: ExpoRoutesManifestV1<RegExp> = {
21
+ ...routesManifest,
22
+ notFoundRoutes: routesManifest.notFoundRoutes.map((value: any) => {
23
+ return {
24
+ ...value,
25
+ namedRegex: new RegExp(value.namedRegex),
26
+ };
27
+ }),
28
+ apiRoutes: routesManifest.apiRoutes.map((value: any) => {
29
+ return {
30
+ ...value,
31
+ namedRegex: new RegExp(value.namedRegex),
32
+ };
33
+ }),
34
+ htmlRoutes: routesManifest.htmlRoutes.map((value: any) => {
35
+ return {
36
+ ...value,
37
+ namedRegex: new RegExp(value.namedRegex),
38
+ };
39
+ }),
40
+ redirects: routesManifest.redirects?.map((value: any) => {
41
+ return {
42
+ ...value,
43
+ namedRegex: new RegExp(value.namedRegex),
44
+ };
45
+ }),
46
+ rewrites: routesManifest.rewrites?.map((value: any) => {
47
+ return {
48
+ ...value,
49
+ namedRegex: new RegExp(value.namedRegex),
50
+ };
51
+ }),
52
+ };
53
+
54
+ return parsed;
55
+ }
56
+
57
+ export function getRoutesManifest(distFolder: string) {
58
+ return getProcessedManifest(path.join(distFolder, "_expo/routes.json"));
59
+ }
60
+
61
+ // TODO: Reuse this for dev as well
62
+ export function createRequestHandler(
63
+ distFolder: string,
64
+ {
65
+ getRoutesManifest: getInternalRoutesManifest,
66
+ getHtml = async (_request, route) => {
67
+ // Serve a static file by exact route name
68
+ const filePath = path.join(distFolder, route.page + ".html");
69
+ if (fs.existsSync(filePath)) {
70
+ return fs.readFileSync(filePath, "utf-8");
71
+ }
72
+
73
+ // Serve a static file by route name with hoisted index
74
+ // See: https://github.com/expo/expo/pull/27935
75
+ const hoistedFilePath = route.page.match(/\/index$/)
76
+ ? path.join(distFolder, route.page.replace(/\/index$/, "") + ".html")
77
+ : null;
78
+ if (hoistedFilePath && fs.existsSync(hoistedFilePath)) {
79
+ return fs.readFileSync(hoistedFilePath, "utf-8");
80
+ }
81
+
82
+ return null;
83
+ },
84
+ getApiRoute = async (route) => {
85
+ const filePath = path.join(distFolder, route.file);
86
+
87
+ debug(`Handling API route: ${route.page}: ${filePath}`);
88
+
89
+ // TODO: What's the standard behavior for malformed projects?
90
+ if (!fs.existsSync(filePath)) {
91
+ return null;
92
+ }
93
+
94
+ if (/\.c?js$/.test(filePath)) {
95
+ return require(filePath);
96
+ }
97
+ return import(filePath);
98
+ },
99
+ logApiRouteExecutionError = (error: Error) => {
100
+ console.error(error);
101
+ },
102
+ handleApiRouteError = async (error: Error) => {
103
+ if ("statusCode" in error && typeof error.statusCode === "number") {
104
+ return new Response(error.message, {
105
+ status: error.statusCode,
106
+ headers: {
107
+ "Content-Type": "text/plain",
108
+ },
109
+ });
110
+ }
111
+ return new Response("Internal server error", {
112
+ status: 500,
113
+ headers: {
114
+ "Content-Type": "text/plain",
115
+ },
116
+ });
117
+ },
118
+ }: {
119
+ getHtml?: (
120
+ request: Request,
121
+ route: RouteInfo<RegExp>
122
+ ) => Promise<string | Response | null>;
123
+ getRoutesManifest?: (
124
+ distFolder: string
125
+ ) => Promise<ExpoRoutesManifestV1<RegExp> | null>;
126
+ getApiRoute?: (route: RouteInfo<RegExp>) => Promise<any>;
127
+ logApiRouteExecutionError?: (error: Error) => void;
128
+ handleApiRouteError?: (error: Error) => Promise<Response>;
129
+ } = {}
130
+ ) {
131
+ let routesManifest: ExpoRoutesManifestV1<RegExp> | undefined;
132
+
133
+ return async function handler(request: Request): Promise<Response> {
134
+ if (getInternalRoutesManifest) {
135
+ const manifest = await getInternalRoutesManifest(distFolder);
136
+ if (manifest) {
137
+ routesManifest = manifest;
138
+ } else {
139
+ // Development error when Expo Router is not setup.
140
+ return new Response("No routes manifest found", {
141
+ status: 404,
142
+ headers: {
143
+ "Content-Type": "text/plain",
144
+ },
145
+ });
146
+ }
147
+ } else if (!routesManifest) {
148
+ routesManifest = getRoutesManifest(distFolder);
149
+ }
150
+
151
+ const url = new URL(request.url, "http://expo.dev");
152
+
153
+ const sanitizedPathname = url.pathname;
154
+
155
+ debug("Request", sanitizedPathname);
156
+
157
+ if (routesManifest.rewrites) {
158
+ for (const route of routesManifest.rewrites) {
159
+ if (!route.namedRegex.test(sanitizedPathname)) {
160
+ continue;
161
+ }
162
+
163
+ const url = getRedirectRewriteLocation(request, route);
164
+
165
+ if (url) {
166
+ request = new Request(
167
+ new URL(url, new URL(request.url).origin),
168
+ request
169
+ );
170
+ }
171
+ }
172
+ }
173
+
174
+ if (routesManifest.redirects) {
175
+ for (const route of routesManifest.redirects) {
176
+ if (!route.namedRegex.test(sanitizedPathname)) {
177
+ continue;
178
+ }
179
+
180
+ const Location = getRedirectRewriteLocation(request, route);
181
+
182
+ if (Location) {
183
+ debug("Redirecting", Location);
184
+
185
+ // Get the params
186
+ return new Response(null, {
187
+ status: route.permanent ? 308 : 307,
188
+ headers: {
189
+ Location,
190
+ },
191
+ });
192
+ }
193
+ }
194
+ }
195
+
196
+ if (request.method === "GET" || request.method === "HEAD") {
197
+ // First test static routes
198
+ for (const route of routesManifest.htmlRoutes) {
199
+ if (!route.namedRegex.test(sanitizedPathname)) {
200
+ continue;
201
+ }
202
+
203
+ // // Mutate to add the expoUrl object.
204
+ updateRequestWithConfig(request, route);
205
+
206
+ // serve a static file
207
+ const contents = await getHtml(request, route);
208
+
209
+ // TODO: What's the standard behavior for malformed projects?
210
+ if (!contents) {
211
+ return new Response("Not found", {
212
+ status: 404,
213
+ headers: {
214
+ "Content-Type": "text/plain",
215
+ },
216
+ });
217
+ } else if (contents instanceof Response) {
218
+ return contents;
219
+ }
220
+
221
+ return new Response(contents, {
222
+ status: 200,
223
+ headers: {
224
+ "Content-Type": "text/html",
225
+ },
226
+ });
227
+ }
228
+ }
229
+
230
+ // Next, test API routes
231
+ for (const route of routesManifest.apiRoutes) {
232
+ if (!route.namedRegex.test(sanitizedPathname)) {
233
+ continue;
234
+ }
235
+
236
+ const func = await getApiRoute(route);
237
+
238
+ if (func instanceof Response) {
239
+ return func;
240
+ }
241
+
242
+ const routeHandler = func?.[request.method];
243
+ if (!routeHandler) {
244
+ return new Response("Method not allowed", {
245
+ status: 405,
246
+ headers: {
247
+ "Content-Type": "text/plain",
248
+ },
249
+ });
250
+ }
251
+
252
+ // Mutate to add the expoUrl object.
253
+ const params = updateRequestWithConfig(request, route);
254
+
255
+ try {
256
+ // TODO: Handle undefined
257
+ return (await routeHandler(request, params)) as Response;
258
+ } catch (error) {
259
+ if (error instanceof Error) {
260
+ logApiRouteExecutionError(error);
261
+ }
262
+
263
+ return handleApiRouteError(error as Error);
264
+ }
265
+ }
266
+
267
+ // Finally, test 404 routes
268
+ for (const route of routesManifest.notFoundRoutes) {
269
+ if (!route.namedRegex.test(sanitizedPathname)) {
270
+ continue;
271
+ }
272
+
273
+ // // Mutate to add the expoUrl object.
274
+ updateRequestWithConfig(request, route);
275
+
276
+ // serve a static file
277
+ const contents = await getHtml(request, route);
278
+
279
+ // TODO: What's the standard behavior for malformed projects?
280
+ if (!contents) {
281
+ return new Response("Not found", {
282
+ status: 404,
283
+ headers: {
284
+ "Content-Type": "text/plain",
285
+ },
286
+ });
287
+ } else if (contents instanceof Response) {
288
+ return contents;
289
+ }
290
+
291
+ return new Response(contents, {
292
+ status: 404,
293
+ headers: {
294
+ "Content-Type": "text/html",
295
+ },
296
+ });
297
+ }
298
+
299
+ // 404
300
+ const response = new Response("Not found", {
301
+ status: 404,
302
+ headers: {
303
+ "Content-Type": "text/plain",
304
+ },
305
+ });
306
+ return response;
307
+ };
308
+ }
309
+
310
+ /** Match `[page]` -> `page` */
311
+ // Ported from `expo-router/src/matchers.tsx`
312
+ function matchDynamicName(name: string): string | undefined {
313
+ // Don't match `...` or `[` or `]` inside the brackets
314
+ // eslint-disable-next-line no-useless-escape
315
+ return name.match(/^\[([^[\](?:\.\.\.)]+?)\]$/)?.[1];
316
+ }
317
+
318
+ /** Match `[...page]` -> `page` */
319
+ // Ported from `expo-router/src/matchers.tsx`
320
+ function matchDeepDynamicRouteName(name: string): string | undefined {
321
+ return name.match(/^\[\.\.\.([^/]+?)\]$/)?.[1];
322
+ }
323
+
324
+ function updateRequestWithConfig(
325
+ request: Request,
326
+ config: ExpoRouterServerManifestV1FunctionRoute
327
+ ) {
328
+ const params: Record<string, string> = {};
329
+ const url = new URL(request.url);
330
+ const match = config.namedRegex.exec(url.pathname);
331
+ if (match?.groups) {
332
+ for (const [key, value] of Object.entries(match.groups)) {
333
+ const namedKey = config.routeKeys[key];
334
+ params[namedKey] = value;
335
+ }
336
+ }
337
+
338
+ return params;
339
+ }
340
+
341
+ function getRedirectRewriteLocation(
342
+ request: Request,
343
+ route: RouteInfo<RegExp>
344
+ ) {
345
+ if (route.methods) {
346
+ if (!route.methods.includes(request.method)) {
347
+ return;
348
+ }
349
+ }
350
+
351
+ const params = updateRequestWithConfig(request, route);
352
+
353
+ const urlSearchParams = new URL(request.url).searchParams;
354
+
355
+ let location = route.page
356
+ .split("/")
357
+ .map((segment) => {
358
+ let match = matchDynamicName(segment);
359
+
360
+ if (match) {
361
+ const value = params[match];
362
+ delete params[match];
363
+ // If we are redirecting from a catch-all route, we need to remove the extra segments
364
+ return value?.split("/")[0];
365
+ }
366
+
367
+ match = matchDeepDynamicRouteName(segment);
368
+
369
+ if (match) {
370
+ const value = params[match];
371
+ delete params[match];
372
+ return value;
373
+ }
374
+
375
+ return segment;
376
+ })
377
+ .join("/");
378
+
379
+ if (Object.keys(params).length > 0 || urlSearchParams.size > 0) {
380
+ location +=
381
+ "?" +
382
+ new URLSearchParams({
383
+ ...params,
384
+ ...Object.fromEntries(urlSearchParams.entries()),
385
+ }).toString();
386
+ }
387
+
388
+ return location;
389
+ }
@@ -0,0 +1,26 @@
1
+ import { createRequestHandler } from "./_expo_internals";
2
+
3
+ import * as path from "node:path";
4
+ import * as process from "node:process";
5
+ import { Hono } from "hono";
6
+ import { serveStatic } from "hono/deno";
7
+
8
+ export const freestyleExpoServer = ({
9
+ CLIENT_BUILD_DIR = path.join(process.cwd(), "dist/client"),
10
+ SERVER_BUILD_DIR = path.join(process.cwd(), "dist/server"),
11
+ }) => {
12
+ // // Expo handler
13
+ const expoHandler = createRequestHandler(SERVER_BUILD_DIR);
14
+
15
+ const app = new Hono();
16
+ app.use("*", serveStatic({ root: CLIENT_BUILD_DIR }));
17
+
18
+ app.all("*", async (c, next) => {
19
+ console.log("Request received:", c.req.url);
20
+ const response = await expoHandler(c.req.raw);
21
+ return response;
22
+ });
23
+
24
+ // @ts-expect-error — Deno.serve is not in the types
25
+ Deno.serve(app.fetch);
26
+ };
@@ -15,6 +15,7 @@ export const prepareDirForDeployment = async (
15
15
  ignore: ["**/node_modules/**"],
16
16
  absolute: false,
17
17
  dot: true,
18
+ posix: true,
18
19
  });
19
20
 
20
21
  for (const relativePath of patterns) {
@@ -47,6 +48,7 @@ export const prepareDirForDeploymentSync = (
47
48
  ignore: ["**/node_modules/**"],
48
49
  absolute: false,
49
50
  dot: true,
51
+ posix: true,
50
52
  });
51
53
 
52
54
  for (const relativePath of patterns) {