Command line interface
The edge CLI runs on macOS, Linux, and WSL. It embeds the engine the JS host loads, a compiler.wasm built from the same source for speed and precompiled for the host machine, executed under wasmtime, so run, repl, test, and actor start in milliseconds with no browser and no server behind them. JavaScript modules run in StarlingMonkey, a runtime the CLI downloads once on the first JavaScript import, and scripts that need a browser (dom, storage, frame()) belong to the JS host, see JavaScript in the CLI for what runs where.
edge run app.py # run a script, a .edge, or stdin
edge build # pack a portable .edge (--app, --web)
edge actor actor.yml # run a pool of cooperative actors
edge serve # dev server with live reload
edge repl # interactive shell
edge test # run *_test.py files
edge init my-app # scaffold a project
edge add network # add a package to edge.json
edge remove network # remove a package from edge.json
edge uninstall # remove the binary and the PATH entryInstall
# Prebuilt binary (recommended)
curl -fsSL https://cdn.edgepython.com/cli/install.sh | sh
# Or from source (any platform with Rust and Cargo)
cargo install --path cliinstall.sh drops the binary at ~/.local/bin/edge and appends that directory to your ~/.bashrc or ~/.zshrc unless already present. Open a new shell and edge --version should work. Re-run the same line to upgrade. The Linux binaries are static, so any distribution works.
Building from source embeds compiler.wasm and the std packages, so run cargo wasm and build each std/* package for wasm32-unknown-unknown first, or point EDGE_COMPILER_WASM and EDGE_STD_DIR at copies. The build fetches nothing.
edge run: run a script
Runs a script and streams its output to the terminal. Bare imports resolve through edge.json, the official packages included, so declare each one with edge add first. Relative imports resolve against the importing file. Uncaught errors print a traceback to stderr and exit 1.
$ edge run broken.py
before
error: ZeroDivisionError: division by zero
--> broken.py:2:1
|
2 | x = 1 / 0
| ^raise SystemExit(code) with no argument or an integer exits cleanly with that code and no traceback. A string argument surfaces as a regular error and exits 1.
With no path, edge run reads the script from piped stdin (cat app.py | edge run) and errors when stdin is a terminal. -c <code> runs inline code instead (edge run -c 'print(1)'). With a path or -c, piped stdin instead feeds input() one line per call. A packed .edge or an app binary also runs here, edge run app.edge unpacks and runs it exactly as ./app would.
Flags: --events, --save-state, --restore-state, and --preempt, see run flags.
edge serve: local dev server
Serves the current directory for browser apps and reloads the page on any file change, via a small polling client injected into served HTML.
$ edge serve
http://localhost:5173
watching .Flags: --port <n> (default 5173), --host <addr> (default 127.0.0.1), --open (open a browser). With --host 0.0.0.0 the banner adds your LAN URL, so you can open the app from a phone on the same network.
edge repl: interactive shell
$ edge add math
+ math https://cdn.edgepython.com/std/math.wasm
updated edge.json
$ edge repl
Edge Python 0.5.0 · .reset to start fresh · .exit, Ctrl+C or Ctrl+D to quit
>>> from math import sqrt
>>> print(sqrt(2))
1.4142135623730951One interpreter stays alive across prompts: imports, definitions, and mutations persist, and an input that raises keeps the effects it made before the error. Each line is one input, so compound statements go on a single line (def double(n): return n * 2). Expression results are not auto-printed, use print(). Arrow-key history works within the session and is not saved to disk. .reset wipes the session state, .exit quits, and Ctrl+C or Ctrl+D also quits the shell.
edge test: test runner
Discovers *_test.py files recursively (skipping dist/ and hidden directories), runs each in a fresh interpreter, and prints a verdict per file. A directory argument narrows discovery to that subtree, a file argument runs just that file.
$ edge test
PASS - adds
1 passed, 0 failed
(successful) main_test.py
PASS - parses
1 passed, 0 failed
(successful) lib/parse_test.py
2/2 files passed · 0.0sTest files declare tests with the test package and do not need to call run(). The runner drives it after the file loads and reads the verdict from the file’s SystemExit code, never from parsed output. A file that registers no tests fails. State never leaks between files. The project must declare test in edge.json, otherwise the runner stops with declare test in edge.json (edge add test).
Exit codes: 0 when every file passed, 1 when a file failed or no *_test.py was found, 2 when the engine session could not start.
edge init: scaffold a project
Creates a ready-to-serve project. With no argument it scaffolds the current directory.
$ edge init my-app
created my-app/
├─ index.html
├─ main.py
└─ edge.json
cd my-app && edge serve--bare skips index.html for script-only projects. The manifest starts empty, nothing resolves until edge add fills it.
edge add / edge remove: package management
Edits edge.json by name. The CLI knows the official packages (json, re, math, struct, test, dom, network, storage, time), so you never paste URLs. Every entry goes to imports and points at the package on the CDN, a .wasm for the standard packages (test is test.py), an index.js for the JavaScript libraries, and the entry.py facade for dom. Each line prints the name and the URL it wrote. The full catalog is in Modules.
$ edge add math network
+ math https://cdn.edgepython.com/std/math.wasm
+ network https://cdn.edgepython.com/js/builtins/network/index.js
updated edge.jsonPoint a name at a custom URL with edge add foo=https://example.com/foo.wasm. It goes to imports too, whatever the URL, because each host tells a code module, a native module, and a JS module apart by the artifact. edge add keeps extends and any other key already in the manifest. edge remove deletes entries the same way.
The catalog is an index the CLI build generates from the repository’s std/* and js/builtins/* folders, so adding an official package is adding its folder and nobody files it under a category.
edge build: pack the app
Packs the project and its imports into one artifact, in one of three modes for three targets.
| Command | Output | Runs on |
|---|---|---|
edge build | a portable .edge | a host that already has edge, or a pool |
edge build --app | a standalone binary | any host of the same OS and CPU, offline, nothing installed |
edge build --web | a self-contained dist/ | any browser |
The default .edge carries only the code and its custom imports, since edge is already present where it lands. --app appends the project to this edge binary, so ./app runs it directly and it honors the run flags --save-state, --restore-state, --preempt, and --events. --web vendors the JS host under dist/js/ with compiler.wasm beside it, then every declared module together with what it needs from the same origin (its relative JavaScript and Python imports and a sibling edge.json), and rewrites edge.json to the vendored paths, so the dist/ works offline, dom and network included. The CLI resolves the official std entries to its embedded copies, so neither artifact needs the network for them. Both carry every module the manifest declares by URL, with the files it imports beside it, so time and a third-party package travel with the project. An app binary for a project that declares a JavaScript module also carries the precompiled JavaScript runtime, around 26 MB. A run prefers that copy, then the cache, then the download, so a packed app never needs the network. An .edge leaves the runtime out, since the edge that runs it brings its own.
$ edge build
packed app.edge (2 files)
0.15 KB
run edge run app.edge or send it to an actor eval groupFlags: --out <path> (mode-specific default), --app, --web.
edge actor: actor pool
Runs many edge-python programs as cooperative actors over a few threads, described by an actor.yml. See Actors for the manifest, groups, server, and untrusted code.
edge actor actor.ymlGroups resolve their imports through the edge.json beside actor.yml, or the one --manifest names. Sending needs no import, send() is a builtin.
edge uninstall
Removes the binary and its PATH entry. The non-interactive equivalent is curl -fsSL https://cdn.edgepython.com/cli/uninstall.sh | sh.
Global flags
| Flag | Effect |
|---|---|
--manifest <file> | Use a specific manifest instead of ./edge.json |
--version, -v | Print the version |
--help, -h | Print the command list |
Ctrl+C cancels a running command with exit code 130.