Ok thanks that works now. We had to give up the transparent http functionality though. Obviously we're at the bleeding edge here!
Yeah, that's the tradeoff. Tools need to explicitly import http_client instead of just using requests or urllib and having it magically work. For a demo framework where you control the tools, that's fine — but it does mean you can't grab an arbitrary Python CLI off GitHub and expect its HTTP calls to work unmodified.
It's worth noting that this space is actively evolving. Pyodide's own pyodide.http module keeps improving, and there's ongoing work in the Emscripten/WASM community around socket support. Transparent patching may become more reliable with future Pyodide releases without us having to do anything. For now, the explicit import is the pragmatic path — it works and it's clear about what's happening under the hood.
Where would you like to take this next?
Ok let's come back to the transparent http support later. This demo has already proven to be a worthwhile exercise. Please prepare a PR to commit this initial version of the code.
On branch main Your branch is up to date with 'origin/main'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md Untracked files: (use "git add <file>..." to include in what will be committed) dist/ index.html node_modules/ package-lock.json package.json src/ tsconfig.json vite.config.ts no changes added to commit (use "git add" and/or "git commit -a")
diff --git a/README.md b/README.md index 3dfe268..e503b3e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # localfirst-demo -Demonstrate local first framework + +A framework for running Python CLI tools in the browser via Pyodide/WASM. + +Provides an `exec()`-style API that lets a web app invoke Python scripts as if +they were local CLI commands, with captured stdout/stderr and exit codes. + +## Quick start + +```bash +npm install +npm run dev +``` + +Then open http://localhost:5173 in your browser. + +## Architecture + +- **`src/runtime.ts`** — `BrowserRuntime` class: the main API surface (`exec()`, `registerCommand()`, `fetchAndRegister()`, `installPackages()`) +- **`src/worker.ts`** — Web Worker that hosts the Pyodide Python runtime, handles command execution with I/O capture +- **`src/commands.ts`** — Built-in demo commands (hello, sysinfo, fetch, files) +- **`src/types.ts`** — Shared types for the worker message protocol and public API +- **`src/main.ts`** — Demo UI wiring + +## Demo commands + +| Command | Description | +|---------|-------------| +| `hello [--name NAME] [--shout]` | Greeting demo with argparse | +| `sysinfo` | Show Python/platform info from inside WASM | +| `fetch URL [--headers]` | HTTP fetch proxied through browser's network stack | +| `files [PATH] [-r]` | List the in-memory virtual filesystem | +| `install PKG [...]` | Install Python packages via micropip |
We need a .gitignore before committing. Let me create that, then set up the branch and PR.
node_modules/ dist/
File created successfully at: /home/david/projects/stirlingbridge/localfirst-demo/.gitignore
git add .gitignore README.md index.html package.json package-lock.json tsconfig.json vite.config.ts src/
git commit -m "$(cat <<'EOF' Add browser-hosted Python CLI runtime using Pyodide/WASM Implements a framework that lets a web app run Python CLI tools entirely in the browser. Provides an exec()-style API (BrowserRuntime.exec()) that invokes Python scripts in a Web Worker via Pyodide, with captured stdout/stderr and exit codes. Includes four demo commands (hello, sysinfo, fetch, files) and an http_client Python module that bridges HTTP requests through the browser's XMLHttpRequest to work around WASM networking limitations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> EOF )"
12 files changed, 2096 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/commands.ts create mode 100644 src/main.ts create mode 100644 src/runtime.ts create mode 100644 src/types.ts create mode 100644 src/worker.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts
Exit code 128 ssh_askpass: exec(/usr/bin/ssh-askpass): No such file or directory git@github.com: Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
origin git@github.com:stirlingbridge/localfirst-demo.git (fetch) origin git@github.com:stirlingbridge/localfirst-demo.git (push)
The push failed due to SSH key authentication. The remote is git@github.com:stirlingbridge/localfirst-demo.git — it looks like SSH keys aren't configured in this session. Could you either:
! ssh-add (if you have a key that just needs loading), or! git remote set-url origin https://github.com/stirlingbridge/localfirst-demo.git and authenticate via gh authOnce the push works I'll create the PR.