@pooriaarab/foxden 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +295 -2
- package/dist/den/den.html +11 -0
- package/dist/den/den.js +139 -0
- package/dist/den.d.ts +77 -0
- package/dist/den.js +227 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.js +32 -0
- package/dist/idb-store.d.ts +3 -0
- package/dist/idb-store.js +35 -0
- package/dist/iframe-runtime.d.ts +20 -0
- package/dist/iframe-runtime.js +109 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/paths.d.ts +8 -0
- package/dist/paths.js +33 -0
- package/dist/protocol.d.ts +52 -0
- package/dist/protocol.js +47 -0
- package/dist/shell.d.ts +16 -0
- package/dist/shell.js +208 -0
- package/dist/snapshot.d.ts +7 -0
- package/dist/snapshot.js +99 -0
- package/package.json +47 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pooria Arab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,296 @@
|
|
|
1
|
-
#
|
|
1
|
+
# foxden
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A sandbox in a browser tab that runs Python and shell with no network.
|
|
4
|
+
|
|
5
|
+
foxden gives each "den" its own folder of files and its own Python. The Python
|
|
6
|
+
is [Pyodide](https://pyodide.org) (CPython compiled to WebAssembly). It runs in
|
|
7
|
+
a worker inside a sandboxed page. That page has an opaque origin and the CSP
|
|
8
|
+
`connect-src 'none'`, so code in it cannot reach any server. You put files in
|
|
9
|
+
`/drop`, run code, and take the results from `/out`.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i @pooriaarab/foxden
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The npm package is `@pooriaarab/foxden`: npm refuses the plain name `foxden` as too similar to an existing package (`boxen`).
|
|
18
|
+
|
|
19
|
+
foxden installs `pyodide` 314.0.7 next to it. Your page must serve two
|
|
20
|
+
folders: the sandbox page from foxden, and three files from Pyodide. Copy them
|
|
21
|
+
when you build:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
mkdir -p public/foxden/pyodide
|
|
25
|
+
cp -r node_modules/@pooriaarab/foxden/dist/den public/foxden/
|
|
26
|
+
cp node_modules/pyodide/pyodide.asm.wasm node_modules/pyodide/python_stdlib.zip node_modules/pyodide/pyodide-lock.json public/foxden/pyodide/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Use the Pyodide files of version 314.0.7. The loader inside `den.js` is that
|
|
30
|
+
version, and files from another version do not work with it. With pnpm,
|
|
31
|
+
`node_modules/pyodide` exists only when you also run `pnpm add pyodide@314.0.7`.
|
|
32
|
+
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import { idbStore, iframeRuntime, openDen } from "@pooriaarab/foxden";
|
|
37
|
+
|
|
38
|
+
const den = await openDen({
|
|
39
|
+
name: "notes",
|
|
40
|
+
store: idbStore(),
|
|
41
|
+
runtime: iframeRuntime({ denUrl: "/foxden/den/den.html", pyodideUrl: "/foxden/pyodide/" }),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await den.writeFile("/drop/sales.csv", "region,amount\nnorth,10\nsouth,20\n");
|
|
45
|
+
const run = await den.run(
|
|
46
|
+
`
|
|
47
|
+
import csv
|
|
48
|
+
rows = list(csv.DictReader(open("/drop/sales.csv")))
|
|
49
|
+
sum(float(row["amount"]) for row in rows)
|
|
50
|
+
`,
|
|
51
|
+
{ timeoutMs: 5000 },
|
|
52
|
+
);
|
|
53
|
+
console.log(run.result); // 30.0
|
|
54
|
+
|
|
55
|
+
const sh = await den.sh("grep north /drop/sales.csv");
|
|
56
|
+
console.log(sh.stdout); // north,10
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This runs in a browser page (through a bundler) and in a Firefox extension
|
|
60
|
+
page. It does not run in Node, because it needs an iframe.
|
|
61
|
+
|
|
62
|
+
## Use cases
|
|
63
|
+
|
|
64
|
+
| Who | What they build | How foxden helps |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| A person with a private spreadsheet | A tab that sums, filters, or cleans a CSV | The file stays in the browser. Code cannot send it anywhere, because the sandbox has no network. |
|
|
67
|
+
| A developer of a local AI agent | A "code interpreter" tool for the agent | The agent writes Python. foxden runs it with a time limit and returns stdout, stderr, the result, and the changed files. Model-written code never runs in the extension itself. |
|
|
68
|
+
| A teacher | A Python lesson page that needs no install and no server | Each student gets a den in their own tab. A loop that never ends stops at the time limit. |
|
|
69
|
+
| A documentation team | "Try it" boxes on a docs site | The reader's device runs the examples. The site needs only static files, no code-execution server. |
|
|
70
|
+
| A person who must convert a file | A page that turns JSON into CSV, or splits a large text file | The conversion runs locally. There is no need to upload the file to an unknown converter site. |
|
|
71
|
+
| A security analyst | A place to open a suspicious text, CSV, or log file offline | Code that parses the file has no network and no extension API. A snapshot keeps the file set for later. |
|
|
72
|
+
| A website owner | Heavy work on the visitor's device ("bring your own compute") | The site serves the sandbox page and Pyodide. The visitor's CPU does the work, and the data stays with the visitor. |
|
|
73
|
+
|
|
74
|
+
## How it works
|
|
75
|
+
|
|
76
|
+
The page that opens a den (the host) owns the files. For each run, it sends
|
|
77
|
+
the code and a copy of the files to the sandbox page. The sandbox page sends
|
|
78
|
+
back the output and the files that changed. The host checks every path and
|
|
79
|
+
saves the files in its store.
|
|
80
|
+
|
|
81
|
+
```mermaid
|
|
82
|
+
flowchart LR
|
|
83
|
+
subgraph host["Host page: extension page or website (has network)"]
|
|
84
|
+
api["openDen()<br/>den.run / den.sh / writeFile"]
|
|
85
|
+
store[("idbStore<br/>IndexedDB")]
|
|
86
|
+
files["Pyodide files<br/>wasm + stdlib zip"]
|
|
87
|
+
end
|
|
88
|
+
subgraph sandbox["den.html: opaque origin, CSP connect-src 'none'"]
|
|
89
|
+
page["den.js<br/>timeout watchdog"]
|
|
90
|
+
worker["module worker<br/>Pyodide (one thread)"]
|
|
91
|
+
end
|
|
92
|
+
api <-- "postMessage over a MessageChannel<br/>typed messages, each one checked" --> page
|
|
93
|
+
files -- "sent once, as bytes" --> page
|
|
94
|
+
page -- "start, run, terminate" --> worker
|
|
95
|
+
api <--> store
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A run moves files from `/drop` to `/out`:
|
|
99
|
+
|
|
100
|
+
```mermaid
|
|
101
|
+
sequenceDiagram
|
|
102
|
+
participant U as User
|
|
103
|
+
participant H as Host (foxden)
|
|
104
|
+
participant S as Sandbox page
|
|
105
|
+
participant W as Pyodide worker
|
|
106
|
+
U->>H: drop sales.csv
|
|
107
|
+
H->>H: writeFile("/drop/sales.csv"), save to IndexedDB
|
|
108
|
+
U->>H: den.run(code, { timeoutMs })
|
|
109
|
+
H->>S: run: code + every den file
|
|
110
|
+
S->>W: write files to /drop, /out, /work
|
|
111
|
+
W->>W: run Python (no network)
|
|
112
|
+
W-->>S: stdout, stderr, repr of result, changed files
|
|
113
|
+
S-->>H: reply (or "timeout" after it kills the worker)
|
|
114
|
+
H->>H: check paths, apply changes to /out, save
|
|
115
|
+
H-->>U: result, and /out/sums.csv to download
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The sandbox page is isolated in one of two ways:
|
|
119
|
+
|
|
120
|
+
- **`manifest-sandbox`** (Firefox 154 and later, in an extension). The
|
|
121
|
+
extension lists `den.html` in the manifest
|
|
122
|
+
[`sandbox`](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/154)
|
|
123
|
+
key, with its own CSP. The host loads it in a plain iframe. The E2E test
|
|
124
|
+
checks this mode on Firefox 157.
|
|
125
|
+
- **`iframe-sandbox`** (websites, and Firefox 153). The host loads `den.html`
|
|
126
|
+
in an iframe with `sandbox="allow-scripts"`. The `<meta>` CSP in `den.html`
|
|
127
|
+
blocks the network.
|
|
128
|
+
|
|
129
|
+
`iframeRuntime` picks the mode. Before it loads Pyodide, `den.html` checks that
|
|
130
|
+
its origin is opaque and that it has no extension API. If the check fails, it
|
|
131
|
+
refuses to start, and `iframeRuntime` tries `iframe-sandbox` instead.
|
|
132
|
+
|
|
133
|
+
What we found in Firefox 157 while we built this: the manifest sandbox CSP
|
|
134
|
+
applies only when the iframe has no `sandbox` attribute. With the attribute,
|
|
135
|
+
Firefox did not apply the manifest CSP, and `fetch`, `XMLHttpRequest` and
|
|
136
|
+
`WebSocket` calls from the page reached a local probe server. foxden
|
|
137
|
+
therefore keeps the `<meta>` CSP in `den.html` in every mode.
|
|
138
|
+
|
|
139
|
+
When a run passes `timeoutMs`, `den.html` terminates the worker and starts a
|
|
140
|
+
new one. Files survive, because the host owns them. Python variables do not.
|
|
141
|
+
If the sandbox page itself stops answering, the host removes the iframe and
|
|
142
|
+
starts a new runtime.
|
|
143
|
+
|
|
144
|
+
## What "shell" means here
|
|
145
|
+
|
|
146
|
+
`den.sh()` is not bash, and it does not run in the sandbox. It is a small
|
|
147
|
+
command language that reads the den files in the host page. It has `ls`,
|
|
148
|
+
`cat`, `head [-n N]`, `wc [-l|-w|-c]`, `grep [-i|-v|-c|-n]` and `echo`, joined
|
|
149
|
+
by `|`, with single and double quotes. `grep` matches plain text, not regular
|
|
150
|
+
expressions. Any other syntax (`;`, `&&`, `>`, `$`, `*`) gives exit code 2
|
|
151
|
+
and runs nothing. To write files, use Python.
|
|
152
|
+
|
|
153
|
+
## API
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { openDen, iframeRuntime, idbStore, memoryStore, runShell, normalizePath,
|
|
157
|
+
encodeSnapshot, decodeSnapshot } from "@pooriaarab/foxden";
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
| Name | What it does |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `openDen({ name, runtime, store?, snapshot?, maxDenBytes?, killGraceMs? })` | Opens a den and starts its runtime. `name` is 1-64 ASCII letters, digits, `-` or `_`. Rejects with `DenLoadError` when the runtime does not start. One name can be open once per page. |
|
|
163
|
+
| `den.run(code, { timeoutMs?, maxOutputBytes? })` | Runs Python. Returns `{ stdout, stderr, result, error, truncated, files, durationMs }`. `result` is the `repr()` of the last expression, or `null`. `error.kind` is `python`, `timeout`, `crashed`, or `storage`. Defaults: 30 s, 1 MiB. |
|
|
164
|
+
| `den.sh(command)` | Runs the shell above. Returns `{ stdout, stderr, code, truncated }`. |
|
|
165
|
+
| `den.writeFile(path, data)` / `den.readFile(path)` / `den.deleteFile(path)` / `den.list()` | Files under `/drop`, `/out`, and `/work`. A path with `..` throws `PathError`. |
|
|
166
|
+
| `den.snapshot()` / `den.restore(bytes)` | Packs all files into bytes with a SHA-256 for each file, and reads them back. Corrupt bytes throw `SnapshotError` and change nothing. Python variables are not in a snapshot. |
|
|
167
|
+
| `den.fork({ name })` | Opens a new den with a copy of the files. |
|
|
168
|
+
| `den.close()` | Removes the iframe and frees the name. |
|
|
169
|
+
| `den.info` | `{ kind, isolation, loadMs }` of the current runtime. |
|
|
170
|
+
| `iframeRuntime({ denUrl, pyodideUrl, isolation?, loadTimeoutMs?, container? })` | The default runtime. `isolation` is `"auto"`, `"manifest-sandbox"`, or `"iframe-sandbox"`. |
|
|
171
|
+
| `idbStore(dbName?)` / `memoryStore()` | Where the files live between page loads. The default is `memoryStore()`. |
|
|
172
|
+
|
|
173
|
+
`runtime` is an adapter: any object with `start()`, `run(request)`, and
|
|
174
|
+
`close()` can replace `iframeRuntime`. We checked
|
|
175
|
+
[BrowserPod](https://browserpod.io) as a second adapter. Its npm package is
|
|
176
|
+
proprietary ("All Rights Reserved"), so foxden does not include it.
|
|
177
|
+
|
|
178
|
+
Errors have a stable `name`: `PathError`, `SnapshotError`, `DenError`,
|
|
179
|
+
`DenLoadError`. foxden has no CLI and no MCP server.
|
|
180
|
+
|
|
181
|
+
### Use it in an extension
|
|
182
|
+
|
|
183
|
+
Copy `den/` and the Pyodide files into your extension, then add this to
|
|
184
|
+
`manifest.json`:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"sandbox": { "pages": ["den/den.html"] },
|
|
189
|
+
"content_security_policy": {
|
|
190
|
+
"extension_pages": "script-src 'self'; object-src 'none'",
|
|
191
|
+
"sandbox": "sandbox allow-scripts; default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src blob:; connect-src 'none'"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`worker-src blob:` is needed because a page with an opaque origin cannot
|
|
197
|
+
start a worker from a URL. `den.js` starts it from a string that is part of
|
|
198
|
+
the file. `web-ext lint` warns about this CSP, and about the `sandbox` key
|
|
199
|
+
when `strict_min_version` is below 154. See `scripts/lint-ext.mjs`.
|
|
200
|
+
|
|
201
|
+
### Use it on a website
|
|
202
|
+
|
|
203
|
+
Serve `den/` and the Pyodide files from your site, and call `iframeRuntime`
|
|
204
|
+
with their URLs. foxden uses `iframe-sandbox` there. Serve
|
|
205
|
+
`pyodide.asm.wasm` with any content type: the host reads it as bytes.
|
|
206
|
+
|
|
207
|
+
## Demo extension
|
|
208
|
+
|
|
209
|
+
`extension/` holds the demo. Its toolbar button opens the Space tab. Drop a
|
|
210
|
+
CSV, press Run, and the sample code counts the rows, sums each number column,
|
|
211
|
+
and writes `/out/sums.csv` for download. The badge says "Network: off".
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
pnpm install
|
|
215
|
+
pnpm build && pnpm build:ext # writes dist-ext/ (about 15 MB)
|
|
216
|
+
pnpm e2e # the extension in real Firefox
|
|
217
|
+
pnpm e2e:web # the website case in real Firefox
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Both E2E tests send all remote traffic to a dead proxy, so Pyodide must load
|
|
221
|
+
from local files. They try `fetch`, `XMLHttpRequest`, `WebSocket`,
|
|
222
|
+
`EventSource`, `import()`, and a nested worker against a local probe server,
|
|
223
|
+
and expect zero requests. They write `artifacts/e2e-<date>.json` and
|
|
224
|
+
`artifacts/e2e-web-<date>.json`. On an Apple silicon Mac with Firefox 157,
|
|
225
|
+
Pyodide loads in about 1.5 to 2.5 seconds.
|
|
226
|
+
|
|
227
|
+
## Firefox APIs used
|
|
228
|
+
|
|
229
|
+
| API | Why |
|
|
230
|
+
|---|---|
|
|
231
|
+
| [`sandbox` manifest key](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/154) (Firefox 154) | An extension page with an opaque origin and no extension API. |
|
|
232
|
+
| [`content_security_policy`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_security_policy) (`sandbox`) | `connect-src 'none'` and `worker-src blob:` for the sandbox page. |
|
|
233
|
+
| [`<iframe sandbox>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#sandbox) | The isolation on websites and on Firefox 153. |
|
|
234
|
+
| [CSP `connect-src`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/connect-src) in a `<meta>` tag | Blocks the network in every mode. A worker made from a blob URL gets the same CSP. |
|
|
235
|
+
| [`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) (`type: "module"`) and [`terminate()`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/terminate) | Runs Pyodide off the page thread, and kills code that runs too long. |
|
|
236
|
+
| [`URL.createObjectURL`](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static) | The worker script, and the download links for `/out` files. |
|
|
237
|
+
| [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) | The only link between the host and the sandbox page. |
|
|
238
|
+
| [WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly) and `'wasm-unsafe-eval'` | Runs Pyodide. One thread only. |
|
|
239
|
+
| [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) | `idbStore` keeps den files across reloads. |
|
|
240
|
+
| [`SubtleCrypto.digest`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) | The SHA-256 of each file in a snapshot. |
|
|
241
|
+
| [`runtime.getManifest`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/getManifest) | `iframeRuntime` checks if `den.html` is in the `sandbox` key. |
|
|
242
|
+
| [`action.onClicked`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/action/onClicked) and [`tabs.create`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/create) | The demo opens the Space tab. |
|
|
243
|
+
| [Drag and drop](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) and `<input type="file">` | The demo fills `/drop`. |
|
|
244
|
+
| [`unlimitedStorage`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) | Firefox does not evict the demo's files. |
|
|
245
|
+
|
|
246
|
+
foxden does not use `SharedArrayBuffer` or WebAssembly threads. Firefox does
|
|
247
|
+
not give them to extension pages. It does not use OPFS or JSPI yet.
|
|
248
|
+
|
|
249
|
+
## Limits
|
|
250
|
+
|
|
251
|
+
- Python runs on one thread, at WebAssembly speed. Heavy number work is
|
|
252
|
+
slower than native Python.
|
|
253
|
+
- The download is large: about 12 MB of Pyodide files, plus 1.6 MB for
|
|
254
|
+
`den.js`. Each den loads its own copy of Pyodide, in 1.5 to 2.5 seconds.
|
|
255
|
+
- Only the Python standard library is there. pandas and NumPy are not
|
|
256
|
+
bundled, and the sandbox cannot download packages.
|
|
257
|
+
- A timeout kills the worker, so Python variables are lost. Files stay.
|
|
258
|
+
- When a run ends, foxden cancels the asyncio tasks that the code started.
|
|
259
|
+
If a task or a JavaScript timer is still alive 200 ms later, the worker
|
|
260
|
+
restarts, and Python variables are lost. Files stay.
|
|
261
|
+
- Snapshots hold files only, not Python variables.
|
|
262
|
+
- Each run copies every den file to the sandbox and back. Very large dens
|
|
263
|
+
are slow. The default cap is 256 MiB per den.
|
|
264
|
+
- Two pages that open the same den name at once both write to IndexedDB. The
|
|
265
|
+
last save wins.
|
|
266
|
+
- The AMO policy says add-ons must not "load remote code for execution". It
|
|
267
|
+
does not say if Python that a user or a model writes, run in a bundled
|
|
268
|
+
interpreter, counts. This question is open. foxden bundles Pyodide and
|
|
269
|
+
runs all such code only in the sandbox page.
|
|
270
|
+
- The `iframe-sandbox` fallback for Firefox 153 is tested on Firefox 157 with
|
|
271
|
+
the attribute (the website E2E test), not on a real Firefox 153.
|
|
272
|
+
- The memory limit is the WebAssembly limit (4 GB). Code that asks for more
|
|
273
|
+
gets a `MemoryError`, but it can use that much RAM before it does.
|
|
274
|
+
- Only Firefox runs the E2E tests. The package is not on npm yet.
|
|
275
|
+
|
|
276
|
+
## Part of the fox primitives
|
|
277
|
+
|
|
278
|
+
```mermaid
|
|
279
|
+
graph LR
|
|
280
|
+
foxden --> foxmate
|
|
281
|
+
foxden -. "planned: a tool" .-> foxloop
|
|
282
|
+
foxden -. "planned: over MCP" .-> foxbridge
|
|
283
|
+
click foxden "https://github.com/pooriaarab/foxden"
|
|
284
|
+
click foxmate "https://github.com/pooriaarab/foxmate"
|
|
285
|
+
click foxloop "https://github.com/pooriaarab/foxloop"
|
|
286
|
+
click foxbridge "https://github.com/pooriaarab/foxbridge"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
foxden depends on no other fox repo.
|
|
290
|
+
[foxmate](https://github.com/pooriaarab/foxmate) will use it as its code
|
|
291
|
+
sandbox. [foxloop](https://github.com/pooriaarab/foxloop) and
|
|
292
|
+
[foxbridge](https://github.com/pooriaarab/foxbridge) can call it as a tool.
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
MIT. Pyodide is MPL-2.0. The demo extension ships it unmodified.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<!-- No network for this page or its worker, on every host. -->
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src blob:; connect-src 'none'" />
|
|
7
|
+
<title>foxden sandbox</title>
|
|
8
|
+
<script src="den.js"></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body></body>
|
|
11
|
+
</html>
|