sandboxedjs 0.2.11 → 0.2.13
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/README.md +21 -9
- package/assets/logo.png +0 -0
- package/bin/sandboxedjs-egress.mjs +25 -10
- package/dist/index.cjs +87 -3
- package/dist/index.js +87 -3
- package/dist/python/python.data +140 -141
- package/dist/python/python.js +1 -1
- package/dist/python/python.wasm +0 -0
- package/dist/python/runtime.json +3 -3
- package/dist/python-worker.js +9 -0
- package/dist/service-worker.js +3 -2
- package/docs/agent/COMMANDS.md +85 -0
- package/docs/agent/DECISION-TREE.md +84 -0
- package/docs/agent/INVARIANTS.md +40 -0
- package/docs/agent/LAUNCH-PROMPT.md +37 -0
- package/docs/agent/LOOP.md +84 -0
- package/docs/agent/README.md +77 -0
- package/docs/agent/ROADMAP.md +37 -0
- package/docs/agent/STATE.md +93 -0
- package/docs/agent/tasks/00-verify-inherited-work.md +36 -0
- package/docs/agent/tasks/01-authoritative-metadata.md +34 -0
- package/docs/agent/tasks/02-native-dependencies.md +35 -0
- package/docs/agent/tasks/03-reproducible-inputs.md +27 -0
- package/docs/agent/tasks/04-build-frontends.md +27 -0
- package/docs/agent/tasks/05-registry-integration.md +27 -0
- package/docs/agent/tasks/06-package-cohorts.md +42 -0
- package/docs/agent/tasks/07-build-on-miss-boundary.md +31 -0
- package/docs/browser-runtime-architecture.md +132 -0
- package/docs/compatibility-implementation-plan.md +98 -0
- package/docs/developer-tool-packs.md +134 -0
- package/docs/frontend-automation.md +49 -0
- package/docs/fullstack-deployment.md +163 -0
- package/docs/handoff.md +275 -0
- package/docs/original-x64.md +39 -0
- package/docs/platform-hardening.md +49 -0
- package/docs/python/abi.md +97 -0
- package/docs/python/architecture.md +94 -0
- package/docs/python/baseline-inventory.md +54 -0
- package/docs/python/build-on-miss.md +198 -0
- package/docs/python/compatibility.md +206 -0
- package/docs/python/cross-build.md +354 -0
- package/docs/python/extensions.md +282 -0
- package/docs/python/release-gates.md +46 -0
- package/docs/python/virtual-sockets-plan.md +331 -0
- package/docs/runtime-lifecycle-fixes.md +39 -0
- package/docs/server-previews.md +268 -0
- package/docs/virtual-browser.md +120 -0
- package/package.json +6 -3
package/dist/python/python.wasm
CHANGED
|
Binary file
|
package/dist/python/runtime.json
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"artifacts": {
|
|
19
19
|
"moduleUrl": "./python.js",
|
|
20
20
|
"hashes": {
|
|
21
|
-
"python.js": "sha256-
|
|
22
|
-
"python.wasm": "sha256-
|
|
23
|
-
"python.data": "sha256-
|
|
21
|
+
"python.js": "sha256-2077b7c944097fa32fd80ffdd2575d5877a75bea77a0083f3dce93e7d2ab2976",
|
|
22
|
+
"python.wasm": "sha256-03f5cb9889a26d5f0b5a3e9d7310e2c5d9f2e8d1c4f9673b061ee19fcbe759c1",
|
|
23
|
+
"python.data": "sha256-c6e6bcf4afd43d12b50c6a0d013d400868434bbfd37341d4642ea40ad69cacd3",
|
|
24
24
|
"python.worker.js": "sha256-63bb1df52c4e3a95eae7b2d77c98554188fcc5925e72628ea0c34d5c573977a5"
|
|
25
25
|
}
|
|
26
26
|
},
|
package/dist/python-worker.js
CHANGED
|
@@ -802,6 +802,7 @@ async function runPythonProcess(start, port) {
|
|
|
802
802
|
)).default;
|
|
803
803
|
let exitCode = 0;
|
|
804
804
|
let guestMemory;
|
|
805
|
+
let threadBridgeName;
|
|
805
806
|
const guestHeap = () => {
|
|
806
807
|
const exported = config.HEAPU8;
|
|
807
808
|
if (exported && exported.byteLength > 0) return exported;
|
|
@@ -886,6 +887,7 @@ async function runPythonProcess(start, port) {
|
|
|
886
887
|
if (!guestMemory && result.instance.exports.memory instanceof WebAssembly.Memory) {
|
|
887
888
|
guestMemory = result.instance.exports.memory;
|
|
888
889
|
}
|
|
890
|
+
threadBridgeName = result.instance.exports.sbx_thread_bridge_name;
|
|
889
891
|
receiveInstance(result.instance, result.module);
|
|
890
892
|
} catch (error) {
|
|
891
893
|
config.onAbort?.(error);
|
|
@@ -903,6 +905,13 @@ async function runPythonProcess(start, port) {
|
|
|
903
905
|
mountContainer(config.FS, client, start.mounts, config.ERRNO_CODES ?? {});
|
|
904
906
|
bindStdio(config.FS, client, start.isTty);
|
|
905
907
|
for (const [key, value] of Object.entries(start.env)) config.ENV[key] = value;
|
|
908
|
+
if (start.threadChannel && typeof threadBridgeName === "function") {
|
|
909
|
+
const bytes = new TextEncoder().encode(start.threadChannel + "\0");
|
|
910
|
+
if (bytes.length > 128) throw new Error("Python thread channel name is too long");
|
|
911
|
+
guestHeap().set(bytes, threadBridgeName());
|
|
912
|
+
port.postMessage({ type: "thread-memory", memory: guestMemory });
|
|
913
|
+
config.ENV.SBX_THREAD_HOST_CALLS = "1";
|
|
914
|
+
}
|
|
906
915
|
try {
|
|
907
916
|
config.FS.chdir(start.cwd);
|
|
908
917
|
} catch {
|
package/dist/service-worker.js
CHANGED
|
@@ -530,7 +530,8 @@ var PreviewRouter = class {
|
|
|
530
530
|
this.options.onStale?.(clientId);
|
|
531
531
|
}
|
|
532
532
|
const headers = new Headers(result.headers);
|
|
533
|
-
const
|
|
533
|
+
const bodyless = request.method === "HEAD" || [204, 205, 304].includes(result.status);
|
|
534
|
+
const body = bodyless ? null : this.options.injectSockets === false ? result.body : withSockets(result.body, headers);
|
|
534
535
|
headers.set("Cross-Origin-Resource-Policy", "cross-origin");
|
|
535
536
|
headers.set("Cross-Origin-Embedder-Policy", "require-corp");
|
|
536
537
|
return new Response(body, { status: result.status, statusText: result.statusText, headers });
|
|
@@ -656,7 +657,7 @@ worker.addEventListener("fetch", (event) => {
|
|
|
656
657
|
const claimed = router.claimedPort(url.pathname);
|
|
657
658
|
if (claimed !== null && event.request.mode === "navigate") {
|
|
658
659
|
router.bind(event.resultingClientId || event.clientId, claimed);
|
|
659
|
-
event.respondWith(router.fetch(claimed,
|
|
660
|
+
event.respondWith(router.fetch(claimed, router.containerPath(url.pathname, url.search), event.request, event.resultingClientId || event.clientId));
|
|
660
661
|
return;
|
|
661
662
|
}
|
|
662
663
|
const port = router.portForRequest(url.pathname, event.clientId);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Command catalog
|
|
2
|
+
|
|
3
|
+
Run commands from `/Users/shazi/Projects/SandboxedJs` unless stated otherwise.
|
|
4
|
+
Use focused commands first. Keep long logs out of model context:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
some-command > /tmp/sbx-native-build.log 2>&1
|
|
8
|
+
tail -n 120 /tmp/sbx-native-build.log
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Do not use the redirection form when editing source files; it is for disposable
|
|
12
|
+
logs only.
|
|
13
|
+
|
|
14
|
+
## Cheap inspection
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
git status --short
|
|
18
|
+
git diff --cached --check
|
|
19
|
+
git diff --cached --stat
|
|
20
|
+
python3 -m json.tool python-runtime/abi/extension-abi.json >/dev/null
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Build prerequisites and runtime
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
make -C python-runtime doctor
|
|
27
|
+
make -C python-runtime python PROFILE=dynamic
|
|
28
|
+
make -C python-runtime package PROFILE=dynamic
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Do not rebuild CPython when the runtime and ABI inputs have not changed and the
|
|
33
|
+
required output already exists.
|
|
34
|
+
|
|
35
|
+
## Extension fixtures and index
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
python3 python-runtime/scripts/build_extension.py \
|
|
39
|
+
python-runtime/fixtures/sbx_setuptools_probe/recipe.json
|
|
40
|
+
python3 python-runtime/scripts/build_extension.py \
|
|
41
|
+
python-runtime/fixtures/sbx_cython_probe/recipe.json
|
|
42
|
+
python3 python-runtime/scripts/build_extension.py \
|
|
43
|
+
python-runtime/fixtures/sbx_rust_probe/recipe.json
|
|
44
|
+
python3 python-runtime/scripts/build_index.py
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If a wheel changes, rebuild `index.json` before runtime tests or its digest will
|
|
48
|
+
correctly be stale.
|
|
49
|
+
|
|
50
|
+
## Focused tests
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npx vitest run test/python-runtime/extensions.test.ts
|
|
54
|
+
npx vitest run test/python-runtime/resolver.test.ts
|
|
55
|
+
npx vitest run test/python-runtime/extension-abi.test.ts
|
|
56
|
+
npm run typecheck
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Run `npm run check` only at milestone gates. Network-enabled suites are not a
|
|
60
|
+
default native-package check.
|
|
61
|
+
|
|
62
|
+
## Wheel inspection
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
python3 -m zipfile -l path/to/package.whl
|
|
66
|
+
python3 python-runtime/scripts/build_index.py --wheels path/to/wheel-directory
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Prefer adding a small reusable inspection script over repeatedly embedding
|
|
70
|
+
complex Python in shell commands.
|
|
71
|
+
|
|
72
|
+
## Logs
|
|
73
|
+
|
|
74
|
+
Use one log per active failure under `/tmp`, named with the task and package.
|
|
75
|
+
Keep only:
|
|
76
|
+
|
|
77
|
+
- the exact command;
|
|
78
|
+
- toolchain versions when relevant;
|
|
79
|
+
- first causal error;
|
|
80
|
+
- final 80-150 lines;
|
|
81
|
+
- exit code.
|
|
82
|
+
|
|
83
|
+
Warnings are not tasks unless they identify incorrect output or a future
|
|
84
|
+
failure covered by the current milestone.
|
|
85
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Failure classification and decisions
|
|
2
|
+
|
|
3
|
+
Classify an observed failure by its earliest decisive symptom.
|
|
4
|
+
|
|
5
|
+
## Resolver says no usable distribution
|
|
6
|
+
|
|
7
|
+
1. Inspect candidate filenames and tags.
|
|
8
|
+
2. If a pure wheel exists, fix resolver/tag/metadata behavior; do not compile.
|
|
9
|
+
3. If a matching SandboxedJS wheel exists, inspect index `abiId`, digest, and
|
|
10
|
+
dependency metadata.
|
|
11
|
+
4. If only an sdist or host-native wheels exist, route to the external
|
|
12
|
+
cross-builder. Do not let the runtime installer build source yet.
|
|
13
|
+
|
|
14
|
+
## Build frontend cannot start
|
|
15
|
+
|
|
16
|
+
- Missing host Python module: classify as a build requirement.
|
|
17
|
+
- Unpinned requirement: add an exact version and hash acquisition path.
|
|
18
|
+
- Unsupported backend: add a backend adapter, not a package conditional.
|
|
19
|
+
- Network failure: preserve the command and request network approval; do not
|
|
20
|
+
substitute an unverified archive.
|
|
21
|
+
|
|
22
|
+
## Compiler uses host headers or compiler
|
|
23
|
+
|
|
24
|
+
- Inspect generated cross-sysconfig and command line.
|
|
25
|
+
- Fix host/target environment separation.
|
|
26
|
+
- Never solve this by renaming a produced artifact.
|
|
27
|
+
|
|
28
|
+
## Header or library not found
|
|
29
|
+
|
|
30
|
+
1. Determine whether it is a Python dependency or a native target library.
|
|
31
|
+
2. For a target library, require a declared, pinned native dependency.
|
|
32
|
+
3. Build it into the ABI/profile-specific sysroot.
|
|
33
|
+
4. Expose sysroot include/library paths through the cross environment.
|
|
34
|
+
5. Add a reduced fixture proving the dependency mechanism before retrying a
|
|
35
|
+
large real package.
|
|
36
|
+
|
|
37
|
+
## Configure test compiles, then tries to execute
|
|
38
|
+
|
|
39
|
+
This is a cross-compilation probe problem. Prefer, in order:
|
|
40
|
+
|
|
41
|
+
1. upstream-supported cross-file/cache variables;
|
|
42
|
+
2. a generic build-backend cross configuration;
|
|
43
|
+
3. a visible package patch providing the target fact.
|
|
44
|
+
|
|
45
|
+
Never execute target WebAssembly as if it were a host binary. If executing a
|
|
46
|
+
probe under a controlled runner is proposed, stop for architectural review.
|
|
47
|
+
|
|
48
|
+
## Linker failure
|
|
49
|
+
|
|
50
|
+
- Missing `Py*` symbols can remain unresolved in a side module if supplied by
|
|
51
|
+
the main module; confirm rather than blindly adding `-lpython`.
|
|
52
|
+
- Missing native-library symbols require target dependency/link-order work.
|
|
53
|
+
- Shared-memory, PIC, exception, relocation, or atomics errors are ABI issues;
|
|
54
|
+
inspect `INVARIANTS.md` and escalate before changing flags.
|
|
55
|
+
- Duplicate symbols often indicate a static library linked twice; inspect the
|
|
56
|
+
complete link line.
|
|
57
|
+
|
|
58
|
+
## Wheel builds but validation fails
|
|
59
|
+
|
|
60
|
+
- Not `\0asm`: host compiler leakage.
|
|
61
|
+
- No `dylink`: side-module link mode was lost.
|
|
62
|
+
- Missing `PyInit_*`: module naming/export problem.
|
|
63
|
+
- Wrong tag or ABI: wheel construction problem. Rebuild; never retag.
|
|
64
|
+
|
|
65
|
+
## Install succeeds but import fails
|
|
66
|
+
|
|
67
|
+
- Missing Python wrapper files: wheel assembly/layout defect.
|
|
68
|
+
- Missing dependent shared/static symbols: target dependency closure defect.
|
|
69
|
+
- Init symbol mismatch: derive the module name from installed path.
|
|
70
|
+
- Trap, memory corruption, or exception failure: ABI mismatch until disproven.
|
|
71
|
+
|
|
72
|
+
## Import succeeds but behavior fails
|
|
73
|
+
|
|
74
|
+
Classify whether the package needs an unavailable OS capability, contains a
|
|
75
|
+
32-bit assumption, exceeds memory, or has a package defect. Record an honest
|
|
76
|
+
compatibility limitation when the platform capability is genuinely absent.
|
|
77
|
+
|
|
78
|
+
## Test hangs
|
|
79
|
+
|
|
80
|
+
Do not immediately increase timeouts. Determine whether the process is waiting
|
|
81
|
+
on input, network, a child process, a pthread, or a host ABI response. Capture a
|
|
82
|
+
bounded stack/process snapshot where possible. Kill only the exact process you
|
|
83
|
+
started.
|
|
84
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Non-negotiable invariants
|
|
2
|
+
|
|
3
|
+
Read this before changing ABI, wheel, resolver, or compiler behavior.
|
|
4
|
+
|
|
5
|
+
1. `python-runtime/abi/extension-abi.json` is the source of truth for target
|
|
6
|
+
identity and compile/link compatibility. Recipes cannot override ABI flags.
|
|
7
|
+
2. A host-native ELF, Mach-O, `.node`, or other binary is never accepted or
|
|
8
|
+
relabeled as WebAssembly.
|
|
9
|
+
3. A native wheel must match both its Python wheel tag and SandboxedJS
|
|
10
|
+
`abiId`.
|
|
11
|
+
4. Build tools run on the build machine. Target code and target libraries are
|
|
12
|
+
compiled for wasm32. Never put target artifacts on the host interpreter's
|
|
13
|
+
import path.
|
|
14
|
+
5. Build, host, Python-runtime, and native-target dependencies are separate
|
|
15
|
+
concepts and must be represented separately.
|
|
16
|
+
6. Sources and build-tool artifacts are version-pinned and hash-verifiable.
|
|
17
|
+
7. Generic behavior lives in generic machinery. Package-specific behavior is
|
|
18
|
+
visible in a recipe and/or patch, never hidden behind `if package == ...`.
|
|
19
|
+
8. The package's own metadata is authoritative. Recipes may constrain or add
|
|
20
|
+
target facts, but must not silently duplicate drifting `Requires-Dist` data.
|
|
21
|
+
9. Installer operations remain transactional. Failed resolution, download, or
|
|
22
|
+
installation must not leave a partial environment.
|
|
23
|
+
10. A build is supported only after the wheel installs and its compiled module
|
|
24
|
+
executes under the owned SandboxedJS CPython runtime.
|
|
25
|
+
11. Browser compatibility remains mandatory. Runtime TypeScript must not gain
|
|
26
|
+
unconditional Node builtin imports.
|
|
27
|
+
12. Unsupported operating-system capabilities fail honestly. Package success
|
|
28
|
+
must not be manufactured by fake sockets, fake processes, or silent skips.
|
|
29
|
+
|
|
30
|
+
## Forbidden shortcuts
|
|
31
|
+
|
|
32
|
+
- Weakening `verify_side_module` or ABI checks to make a fixture pass.
|
|
33
|
+
- Adding arbitrary recipe compiler/link flags.
|
|
34
|
+
- Copying metadata solely from a handwritten recipe when upstream metadata can
|
|
35
|
+
be generated.
|
|
36
|
+
- Installing unpinned build requirements from the network in a release build.
|
|
37
|
+
- Treating "wheel file exists" as an acceptance result.
|
|
38
|
+
- Starting with NumPy-specific branches in the generic pipeline.
|
|
39
|
+
- Running full tests repeatedly when a focused test identifies the defect.
|
|
40
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Prompt to launch or resume Claude Opus
|
|
2
|
+
|
|
3
|
+
Copy the text below into the coding task. Do not paste the rest of this folder
|
|
4
|
+
into the prompt; the agent should read files on demand.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
You are the sole implementation agent for the SandboxedJS native Python
|
|
8
|
+
package platform. The project controller is:
|
|
9
|
+
|
|
10
|
+
agent-projects/python-package-platform/README.md
|
|
11
|
+
|
|
12
|
+
Begin by reading only README.md, STATE.md, and the current task named in
|
|
13
|
+
STATE.md. Follow LOOP.md. Execute commands and make decisions from their actual
|
|
14
|
+
outputs. Continue through tasks while gates pass and context remains healthy.
|
|
15
|
+
Keep STATE.md current so another task can resume without chat history.
|
|
16
|
+
|
|
17
|
+
Preserve all pre-existing and staged work. Do not commit, publish, deploy,
|
|
18
|
+
discard changes, weaken ABI validation, or change the ABI contract unless I
|
|
19
|
+
explicitly authorize it. Use focused tests before broad suites. Keep long build
|
|
20
|
+
logs in /tmp and bring only decisive excerpts into context.
|
|
21
|
+
|
|
22
|
+
If a stop condition in LOOP.md occurs, stop with a compact escalation packet.
|
|
23
|
+
The goal is a general package platform; NumPy is a later acceptance case, not a
|
|
24
|
+
package-specific architecture target.
|
|
25
|
+
|
|
26
|
+
Do not merely review or propose a plan. Work autonomously: inspect, edit,
|
|
27
|
+
execute focused commands, diagnose their outputs, verify exit gates, update
|
|
28
|
+
STATE.md, and advance to the next task. Do not ask for confirmation between
|
|
29
|
+
ordinary in-scope steps. Continue until a documented stop condition requires my
|
|
30
|
+
decision or all roadmap gates are complete.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Execution policy
|
|
34
|
+
|
|
35
|
+
This workflow assumes one Claude Opus agent. Do not delegate work to other
|
|
36
|
+
models or create subagents. Use the stop conditions and ask the user for one
|
|
37
|
+
concrete decision only when necessary.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Agent execution loop
|
|
2
|
+
|
|
3
|
+
Use this loop exactly. It is designed for a smaller model that can act well
|
|
4
|
+
when each decision is grounded in the previous command's output.
|
|
5
|
+
|
|
6
|
+
## 1. Orient
|
|
7
|
+
|
|
8
|
+
Read `README.md`, `STATE.md`, and the current task only. Inspect `git status
|
|
9
|
+
--short` before editing. Treat all pre-existing changes as user-owned.
|
|
10
|
+
|
|
11
|
+
State the current hypothesis in one sentence. A hypothesis must predict an
|
|
12
|
+
observable result, for example: "the linker cannot find `libxml2` because the
|
|
13
|
+
dynamic sysroot is absent from target link flags."
|
|
14
|
+
|
|
15
|
+
## 2. Choose the cheapest discriminating command
|
|
16
|
+
|
|
17
|
+
Run the smallest command that can disprove the hypothesis. Prefer, in order:
|
|
18
|
+
|
|
19
|
+
1. static inspection of one relevant file;
|
|
20
|
+
2. one unit test or one fixture build;
|
|
21
|
+
3. one runtime import test;
|
|
22
|
+
4. the focused suite;
|
|
23
|
+
5. the full suite only at a milestone gate.
|
|
24
|
+
|
|
25
|
+
Never rerun a long build unchanged. If a command failed, inspect its decisive
|
|
26
|
+
error first and change either the hypothesis or the implementation.
|
|
27
|
+
|
|
28
|
+
## 3. Classify the result
|
|
29
|
+
|
|
30
|
+
Use `DECISION-TREE.md`. Every failure must be classified before code changes.
|
|
31
|
+
If there is not enough evidence, gather more evidence rather than guessing.
|
|
32
|
+
|
|
33
|
+
## 4. Make one coherent change
|
|
34
|
+
|
|
35
|
+
Change the smallest architectural seam that removes the demonstrated failure
|
|
36
|
+
class. Do not add a package-name conditional to the generic builder. A package
|
|
37
|
+
patch belongs in an explicit recipe patch directory, with a comment explaining
|
|
38
|
+
the upstream assumption it replaces.
|
|
39
|
+
|
|
40
|
+
Use `apply_patch` for hand edits. Do not overwrite files with shell redirects.
|
|
41
|
+
Do not alter generated ABI files directly.
|
|
42
|
+
|
|
43
|
+
## 5. Verify in layers
|
|
44
|
+
|
|
45
|
+
Run the focused check that failed. If it passes, run the task's exit gate.
|
|
46
|
+
Compilation is never the final gate for an extension: install and import it in
|
|
47
|
+
the owned CPython runtime and exercise at least one native behavior.
|
|
48
|
+
|
|
49
|
+
## 6. Record and advance
|
|
50
|
+
|
|
51
|
+
Update `STATE.md` with compact evidence. If the task exit gate passes, set its
|
|
52
|
+
checkboxes, point `Current task` to the next task in `ROADMAP.md`, and continue
|
|
53
|
+
if budget remains.
|
|
54
|
+
|
|
55
|
+
## Stop conditions
|
|
56
|
+
|
|
57
|
+
Stop and request a user decision when:
|
|
58
|
+
|
|
59
|
+
- the next action requires credentials, deployment, paid infrastructure, or
|
|
60
|
+
publishing outside the repository;
|
|
61
|
+
- fulfilling the task requires changing the ABI contract rather than fixing a
|
|
62
|
+
consumer of it;
|
|
63
|
+
- an upstream license is unclear or incompatible;
|
|
64
|
+
- a destructive action, history rewrite, or discard of existing work appears
|
|
65
|
+
necessary;
|
|
66
|
+
- the same failure class survives three evidence-driven attempts;
|
|
67
|
+
- a design choice changes public API or materially increases shipped runtime
|
|
68
|
+
size without an already-stated budget.
|
|
69
|
+
|
|
70
|
+
When stopping, preserve logs and state the smallest exact decision needed.
|
|
71
|
+
|
|
72
|
+
## Escalation packet for the user
|
|
73
|
+
|
|
74
|
+
When a stop condition is reached, prepare a compact packet containing:
|
|
75
|
+
|
|
76
|
+
- intended invariant;
|
|
77
|
+
- exact command;
|
|
78
|
+
- final 80-150 relevant log lines;
|
|
79
|
+
- files and functions involved;
|
|
80
|
+
- three attempted hypotheses and what disproved each;
|
|
81
|
+
- the decision required.
|
|
82
|
+
|
|
83
|
+
Do not send entire build logs or the full repository history. Wait for the
|
|
84
|
+
user's decision, record it in `STATE.md`, and then resume this same loop.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# SandboxedJS native Python package platform
|
|
2
|
+
|
|
3
|
+
This folder is an executable work specification for a coding agent. Its goal is
|
|
4
|
+
not to port NumPy. Its goal is to make native Python packages a platform
|
|
5
|
+
capability of SandboxedJS, with NumPy eventually serving as one demanding
|
|
6
|
+
acceptance case.
|
|
7
|
+
|
|
8
|
+
## Start here
|
|
9
|
+
|
|
10
|
+
At the beginning of every turn, read only these files:
|
|
11
|
+
|
|
12
|
+
1. `README.md` (this file).
|
|
13
|
+
2. `STATE.md`.
|
|
14
|
+
3. The one task file named by `STATE.md`.
|
|
15
|
+
|
|
16
|
+
Do not reread every document on every turn. Open `INVARIANTS.md`,
|
|
17
|
+
`DECISION-TREE.md`, or `COMMANDS.md` only when the current task points to them
|
|
18
|
+
or an observed failure requires them. This is intentional: the folder is also
|
|
19
|
+
a context and token budget.
|
|
20
|
+
|
|
21
|
+
Then execute the loop in `LOOP.md`. Continue until the current task's exit gate
|
|
22
|
+
passes or a stop condition requires the user or a stronger model.
|
|
23
|
+
|
|
24
|
+
## Product objective
|
|
25
|
+
|
|
26
|
+
The eventual user experience is:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
pip install some-package
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The implementation may obtain a pure wheel, install an already-built
|
|
33
|
+
SandboxedJS wheel, or ask an external build system to create and cache a wheel.
|
|
34
|
+
It must never install a host-native artifact by relabeling it, silently omit a
|
|
35
|
+
dependency, or claim support because compilation alone succeeded.
|
|
36
|
+
|
|
37
|
+
## Current baseline
|
|
38
|
+
|
|
39
|
+
The working tree already contains staged, uncommitted work produced in an
|
|
40
|
+
earlier session:
|
|
41
|
+
|
|
42
|
+
- a generic setuptools cross environment;
|
|
43
|
+
- package-owned `setup.py` and `pyproject.toml` builds;
|
|
44
|
+
- C and Cython fixtures;
|
|
45
|
+
- the existing PyO3 path;
|
|
46
|
+
- side-module validation and runtime import tests;
|
|
47
|
+
- `docs/python/cross-build.md`, which records ten known assumptions.
|
|
48
|
+
|
|
49
|
+
Preserve those staged changes. Do not unstage, discard, rewrite wholesale, or
|
|
50
|
+
commit them unless the user explicitly requests it. First verify them and
|
|
51
|
+
record the result in `STATE.md`.
|
|
52
|
+
|
|
53
|
+
## Scope boundary
|
|
54
|
+
|
|
55
|
+
This project owns:
|
|
56
|
+
|
|
57
|
+
- reproducible cross-build inputs;
|
|
58
|
+
- build/host/target dependency separation;
|
|
59
|
+
- package build-backend execution;
|
|
60
|
+
- ABI-valid wheel construction and metadata;
|
|
61
|
+
- wheel indexing, resolution, installation, and runtime verification;
|
|
62
|
+
- explicit recipes and patches for irreducibly package-specific behavior;
|
|
63
|
+
- compatibility evidence.
|
|
64
|
+
|
|
65
|
+
This project does not own, unless the user separately authorizes it:
|
|
66
|
+
|
|
67
|
+
- a hosted public build service;
|
|
68
|
+
- cloud accounts, deployment, billing, or package signing infrastructure;
|
|
69
|
+
- pretending raw sockets, subprocesses, GPU APIs, or unavailable operating
|
|
70
|
+
system capabilities exist;
|
|
71
|
+
- broad unrelated cleanup of SandboxedJS.
|
|
72
|
+
|
|
73
|
+
## Definition of success
|
|
74
|
+
|
|
75
|
+
Success is a general pipeline demonstrated by several independent package
|
|
76
|
+
shapes, not one famous package. The final gate is described in `ROADMAP.md`.
|
|
77
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Capability roadmap
|
|
2
|
+
|
|
3
|
+
Advance only when the current task's exit gate passes. Each task must leave a
|
|
4
|
+
focused regression test.
|
|
5
|
+
|
|
6
|
+
| Order | Task | Capability produced |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| 00 | `tasks/00-verify-inherited-work.md` | trusted baseline for the staged generic builder |
|
|
9
|
+
| 01 | `tasks/01-authoritative-metadata.md` | wheel metadata derived from the package |
|
|
10
|
+
| 02 | `tasks/02-native-dependencies.md` | declared target libraries and profile sysroot |
|
|
11
|
+
| 03 | `tasks/03-reproducible-inputs.md` | hashed sources, build tools, patches, provenance |
|
|
12
|
+
| 04 | `tasks/04-build-frontends.md` | explicit backend interface beyond setuptools/PyO3 |
|
|
13
|
+
| 05 | `tasks/05-registry-integration.md` | reproducible build output consumable by `pip` |
|
|
14
|
+
| 06 | `tasks/06-package-cohorts.md` | evidence across independent package shapes |
|
|
15
|
+
| 07 | `tasks/07-build-on-miss-boundary.md` | local protocol/spec for an eventual build service |
|
|
16
|
+
|
|
17
|
+
## Platform completion gate
|
|
18
|
+
|
|
19
|
+
The platform milestone is complete when all of the following are true:
|
|
20
|
+
|
|
21
|
+
- Pure wheels continue to install directly and transactionally.
|
|
22
|
+
- Three native language/build shapes work without package-name branches.
|
|
23
|
+
- At least one extension links a separately built native target library.
|
|
24
|
+
- At least one package needs and cleanly carries a visible target patch.
|
|
25
|
+
- Wheel metadata and dependency resolution come from authoritative package
|
|
26
|
+
metadata plus explicit target constraints.
|
|
27
|
+
- Every indexed artifact is source-pinned, hash-verifiable, ABI-identified,
|
|
28
|
+
installable, importable, and behavior-tested in the owned runtime.
|
|
29
|
+
- A compatibility report distinguishes supported, recipe-required,
|
|
30
|
+
platform-blocked, and not-yet-attempted packages.
|
|
31
|
+
- The external build-on-miss interface is specified without embedding a
|
|
32
|
+
compiler toolchain in the browser runtime.
|
|
33
|
+
|
|
34
|
+
NumPy may be attempted after tasks 00-05. It is evidence for native dependency,
|
|
35
|
+
build-probe, memory, and scientific-stack behavior; it is not itself the
|
|
36
|
+
definition of platform completion.
|
|
37
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Execution state
|
|
2
|
+
|
|
3
|
+
Keep under roughly 120 lines. Replace stale detail instead of appending.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
- Phase: `complete`
|
|
8
|
+
- Blocker: none
|
|
9
|
+
- User decision required: none outstanding
|
|
10
|
+
|
|
11
|
+
## Repository layout (restructured)
|
|
12
|
+
|
|
13
|
+
src/
|
|
14
|
+
kernel/ fs/ net/ shell/ the linux-mimicking core
|
|
15
|
+
node/ module shims, commonjs engine, esm transform
|
|
16
|
+
python/ cpython, syscalls, pip, resolver, worker, abi, local-builder
|
|
17
|
+
worker/ pods, sync channel, worker host
|
|
18
|
+
tools/ esbuild, rolldown, ffmpeg hosts
|
|
19
|
+
container/ pkg/ agent/ preview/ hosting/
|
|
20
|
+
docs/ every markdown file: agent/, python/, handoff.md
|
|
21
|
+
python-runtime/ the Python build pipeline (Makefile, scripts, recipes)
|
|
22
|
+
|
|
23
|
+
`src/runtime/` is gone. Moves were done with `git mv` and every relative import
|
|
24
|
+
recomputed programmatically. Four references were strings rather than import
|
|
25
|
+
specifiers -- two `dist/` paths whose depth changed, a `/src/runtime/python/`
|
|
26
|
+
check in config.ts, and a test allowlist -- and those caused 79 failures until
|
|
27
|
+
found. There was no dead code to remove: 2 of 130 source files are unreferenced
|
|
28
|
+
and both are ambient `.d.ts` files tsconfig loads implicitly.
|
|
29
|
+
|
|
30
|
+
## Packages: nothing to pre-build any more
|
|
31
|
+
|
|
32
|
+
`pip install <anything>` now builds a wheel when the index has none.
|
|
33
|
+
|
|
34
|
+
- `scripts/auto_recipe.py` writes a recipe from what PyPI and the source
|
|
35
|
+
already state: identity, verified sdist, and the declared PEP 517 backend.
|
|
36
|
+
Backends with no adapter are refused by name. Build requirements the lock has
|
|
37
|
+
never seen are pinned at that moment rather than dropped.
|
|
38
|
+
- `src/python/local-builder.ts` runs the pipeline; `bin/sandboxedjs-build-wheels.mjs`
|
|
39
|
+
serves the index and builds on request, for browsers that have no compiler.
|
|
40
|
+
- Opt-in, defaulting on only in Node from a checkout. Submit-and-poll, because
|
|
41
|
+
a connection held open for a minutes-long build reads as an unreachable
|
|
42
|
+
service.
|
|
43
|
+
- Verified: `pip install ujson` with no recipe -> generated, built, indexed,
|
|
44
|
+
installed, imported, both locally and through the service.
|
|
45
|
+
|
|
46
|
+
## Two real defects found and fixed
|
|
47
|
+
|
|
48
|
+
- The browser's default wheel index was filtered to the wheels whose bytes are
|
|
49
|
+
inlined in the bundle, so every other built wheel was invisible -- `pip
|
|
50
|
+
install numpy` reported no usable distribution while numpy sat unreferenced
|
|
51
|
+
beside the interpreter. The index now lists all built wheels, served from
|
|
52
|
+
next to the interpreter, with the inlined bytes as an overlay keyed by
|
|
53
|
+
filename.
|
|
54
|
+
- The post-build index read went through pip's memoizing cache and returned the
|
|
55
|
+
pre-build copy, so a package was built twice.
|
|
56
|
+
|
|
57
|
+
## Pre-existing failures, verified not caused by this work
|
|
58
|
+
|
|
59
|
+
11 tests, reproduced identically in a clean worktree at HEAD: `python-syscalls`
|
|
60
|
+
(5), `cpython` (2), `python-abi/baseline` (3), `python-runtime/asyncio` (1).
|
|
61
|
+
Also: pytest's default output capture fails in the container
|
|
62
|
+
(`OSError: [Errno 8] Bad file descriptor`), so upstream suites need
|
|
63
|
+
`--capture=no`.
|
|
64
|
+
|
|
65
|
+
## Unverified
|
|
66
|
+
|
|
67
|
+
`import numpy` in a browser. The embedded browser used for testing blocks
|
|
68
|
+
nested module workers loaded over HTTP, which the runtime needs to start Python
|
|
69
|
+
at all -- `python3 --version` times out there too, and so does a trivial nested
|
|
70
|
+
worker. `pip install numpy` was verified in that browser and returns 0.
|
|
71
|
+
|
|
72
|
+
## Last evidence
|
|
73
|
+
|
|
74
|
+
`npx vitest run`: 610 passed, 24 skipped, 12 failed (the 11 above plus one
|
|
75
|
+
PyPI-network flake that passes in isolation). `npm run typecheck`: pass.
|
|
76
|
+
`npm run build`: pass. All 16 wheels byte-identical across rebuilds.
|
|
77
|
+
|
|
78
|
+
## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template## Compact checkpoint template
|
|
79
|
+
|
|
80
|
+
Replace the sections above after each meaningful loop:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
Phase: <name>
|
|
84
|
+
Current task: <path>
|
|
85
|
+
Last completed task: <path or none>
|
|
86
|
+
Blocker: <one sentence or none>
|
|
87
|
+
User decision required: <yes/no and exact decision>
|
|
88
|
+
Last evidence:
|
|
89
|
+
- <command>: <pass/fail and decisive result>
|
|
90
|
+
Next action: <one concrete action>
|
|
91
|
+
Files changed this loop: <paths>
|
|
92
|
+
```
|
|
93
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Task 00: verify inherited generic cross-build work
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Establish that the staged setuptools/Cython work is real and reproducible
|
|
6
|
+
before building new layers on it. Do not expand scope in this task.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
|
|
10
|
+
1. Inspect `git status --short`, `git diff --cached --check`, and the staged
|
|
11
|
+
diff limited to the files listed in `STATE.md`.
|
|
12
|
+
2. Confirm recipes cannot provide ABI compiler or linker flags.
|
|
13
|
+
3. Confirm generated artifacts are checked for WebAssembly, `dylink`, and the
|
|
14
|
+
derived `PyInit_*` symbol.
|
|
15
|
+
4. Run the setuptools and Cython fixture builds. Rebuild the wheel index after
|
|
16
|
+
any wheel output changes.
|
|
17
|
+
5. Run `npm run build` only if runtime bundles required by the acceptance test
|
|
18
|
+
are absent or older than relevant inputs.
|
|
19
|
+
6. Run `npx vitest run test/python-runtime/extensions.test.ts`.
|
|
20
|
+
7. Run `npm run typecheck`.
|
|
21
|
+
|
|
22
|
+
## Exit gate
|
|
23
|
+
|
|
24
|
+
- Staged diff check passes.
|
|
25
|
+
- Setuptools C, Cython, and PyO3 wheels install and execute native behavior in
|
|
26
|
+
the owned runtime.
|
|
27
|
+
- Pydantic acceptance remains green.
|
|
28
|
+
- No test passed by weakening or skipping an ABI assertion.
|
|
29
|
+
- `STATE.md` contains exact passing commands and points to task 01.
|
|
30
|
+
|
|
31
|
+
## If it fails
|
|
32
|
+
|
|
33
|
+
Use `DECISION-TREE.md`. Fix only regressions within the inherited work. After
|
|
34
|
+
three failed evidence-driven attempts on the same class, prepare an escalation
|
|
35
|
+
packet instead of redesigning the ABI.
|
|
36
|
+
|