Native Shell
Native Shell: AI-generated UI, native in your app, on every platform. Apps on iOS, Android, macOS, Windows and Linux, GPUI apps and web pages load signed, sandboxed TSX modules at run time and draw th
Cells, genes, organelles, and particles — a framework for building apps in the Code language. Scaffold, run, and build with one CLI.
Developer toolsRust · HTMLGitHub @codelovesmeOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
euglena — CLI to scaffold, run, and build Euglena applications (uses the code language toolchain)
Taken from the repository README
Loading the full README.
Read the full README ↗The euglena CLI — scaffold, run, and build Euglena
applications. Euglena apps are written in the Code
language (requires code >= 1.8.0); this CLI is a thin cell layer on top
of the code toolchain: a manifest.json cell definition, *.gene.code
genes that auto-link, organelle config blocks, and mock mode. Everything else — the
project marker, the module installer and its lockfile, format, test — is
code's own, and euglena wraps it rather than duplicating it.
Working on this repo (human or AI)? Read
AGENTS.mdfirst — the layer boundary, the generated entry and its stamp, version floors, and the testing gotchas that have actually bitten.
code'scode | euglena | |
|---|---|---|
| the language, the interpreter, the LLVM backend | ✔ | delegates |
installing modules, .code/lock.json | ✔ | wraps as install/uninstall |
format | ✔ | delegates |
test — the tests/ walk, the fail_ convention | ✔ | delegates |
the cell model: manifest.json, *.gene.code, organelle aliases, mock mode | — | ✔ |
generating main.code from those | — | ✔ |
Your Euglena app is a code project, and nothing here fences it off:
code list, code format, code build main.code --target wasm, reading the
generated entry to see what your manifest and genes actually compile to —
all of it works, and reading main.code is the fastest way to learn the
language underneath.
Only run, build and test should go through euglena, because only
euglena knows to regenerate the entry first. In a directory with no
manifest.json they don't even try — see
run and build need a manifest.
The recommended way is via cdlvsm,
the package manager for codelovesme CLI tools:
cdlvsm install euglena
cdlvsm install code # euglena runs apps through the code interpreter
This gives you cdlvsm euglena <command> (dispatched through cdlvsm) — by
design there's no bare euglena command unless you pass --link:
cdlvsm install euglena --link # also get a bare `euglena` on PATH
Or install directly from a release, which does put a bare euglena on PATH:
curl -sSf https://raw.githubusercontent.com/codelovesme/euglena/main/install.sh | sh
Linux x86_64 only, for now. Examples below use cdlvsm euglena — drop the
cdlvsm prefix if you installed with --link or install.sh.
cdlvsm euglena init <name> [--web|--service] scaffold a new app in ./<name>
cdlvsm euglena run [app] run the app (default: this directory)
cdlvsm euglena build [app] [options] compile to a native binary (or a module)
cdlvsm euglena test run this project's fixtures (wraps `code test`)
cdlvsm euglena format [--check] [path...] the canonical layout (wraps `code format`)
cdlvsm euglena install install every organelle the manifest declares
cdlvsm euglena install <name> [--as <alias>] install one, and declare it in the manifest
cdlvsm euglena install <name> --gene install a shared gene, and declare it
cdlvsm euglena uninstall <alias> drop the alias, and the module if unreferenced
(or a gene, by its name)
cdlvsm euglena list declared organelles, and whether each is installed
cdlvsm euglena doctor check the interpreter, project, and organelles
cdlvsm euglena code set <path> point euglena at a specific `code` binary
cdlvsm euglena code show
cdlvsm euglena code clear
init --web scaffolds an app that runs in a browser: a manifest that says so
(which is what makes install fetch the archive rather than the library), a
page to load the module into, and a gene that draws instead of one that
answers. It does not write the page's half of the browser organelles —
code build --target wasm emits that beside the module it built, so the two
halves are always the same age.
init --service scaffolds one that listens on its own port, for a machine
or a container rather than a host: net_server configured from ${HOST}
and ${PORT}, a gene that answers Ping, a server gene that starts
listening, a .env for running it here (127.0.0.1:8090, gitignored), and
a Dockerfile:
cdlvsm euglena init api --service && cd api
cdlvsm euglena install
cdlvsm euglena build --release # build/api: modules linked in, nothing to copy beside it
docker build -t api .
docker run -p 8080:8080 -e MONGO_URI=… api
curl -d '{"_class":"Ping","value":1}' localhost:8080/api
The release binary links every module in, so it runs from wherever it is
copied: the image is debian:trixie-slim, certificates, a user that is not
root, and the binary. Nothing is built inside it, and nothing secret goes
into either, since every ${VAR} is read when the binary starts; the image
sets HOST=0.0.0.0 and PORT=8080, and a deployment passes the rest. An
application that needs more beside it adds a COPY of its own. The base
needs a glibc at least as new as the machine that built the binary.
install with no name is what a fresh checkout runs: the manifest is the
list, and everything it names is fetched — once per module, so two aliases
on one module are one download, and mock-organelles counts too. A literal
path is skipped, having nothing to fetch. One failure does not stop the rest.
With a name, it also keeps the manifest in step, which is the whole reason it
wraps code install. A module already declared is not declared twice — that
would be a second organelle, and a module has state, so two names are two of
them; ask for that with --as. An alias already naming something else is
refused rather than repointed, since every emit ... to <alias> means it.
Either way the manifest says which bytes to fetch: an app whose "runtime"
is "web" gets the browser's archive rather than the machine's library,
since a page has no way to open one and links the other in. Nothing to pass —
the app already said what it is.
run, build and test all take -v / --verbose, which prints the
generated entry and the exact code command euglena hands it to.
build options:
-r, --release -O2; the default is unoptimized
-t, --target exe|shared|static|wasm default exe — shared/static make this cell
linkable as another cell's organelle
-o, --output <path> where to write it (default: build/<name>)
cdlvsm euglena init app
cd app
cdlvsm euglena run # just works — no code-path setup needed, see below
coderun/build/test/format/install/uninstall shell out to the code
interpreter. euglena finds it automatically: if you haven't pinned a path,
it uses cdlvsm's cdlvsm-code shim from your PATH — no extra setup needed
after cdlvsm install code. Either way it's version-checked: euglena
generates syntax that only parses on code >= 1.8.0, so a stale binary is
refused by name up front rather than failing as a parse error partway
through a generated file. euglena doctor reports what was found and why.
It deliberately looks only for cdlvsm-code, never a bare code — on Linux
code is VS Code's own CLI, and euglena won't risk invoking that.
If your code came from somewhere other than cdlvsm (a direct install.sh,
a dev build, cdlvsm install code --link), point euglena at it explicitly
(stored in ~/.config/euglena-cli/code_binary_path):
cdlvsm euglena code set /path/to/your/code
cdlvsm euglena code show # what's configured
cdlvsm euglena code clear # go back to cdlvsm-code discovery
euglena init myapp creates:
myapp/
manifest.json cell name + organelles + genes
src/nucleus.gene.code boot gene (any src/*.gene.code is auto-linked)
tests/nucleus.code a starter fixture for `euglena test`
.code/lock.json marks the project root — where `install` installs into
.gitignore .code/modules/, .code/genes/, main.code, build/
main.code GENERATED on first run/build/test — not written by init
main.code is regenerated from manifest.json + src/*.gene.code on every
run/build/test and gitignored; euglena refuses to touch one that
doesn't carry its | GENERATED header, so a hand-written entry is never at
risk.
Its second line is a stamp — the euglena that wrote the file, and the SHA-256 of everything below:
| GENERATED — do not edit. Modify manifest.json or src/*.gene.code instead.
| euglena 0.2.0 · body sha256:1f0c…
The header says euglena owns the file; the stamp is what lets euglena doctor tell apart the two states it can't:
manifest.json,
a gene, or .code/lock.json has moved on since. Harmless: the next
run/build/test regenerates it. Worth reporting because a plain
code run . in that directory would quietly execute the old entry.run/build/test will throw
that work away. This one fails doctor; move the change into
manifest.json or a gene.run and build are for cells. In a directory with no manifest.json
they stop and point at code rather than passing the path through — a
plain code project or a lone .code file is code's job, and
euglena run being a second spelling of code run on some inputs but not
others helped nobody:
$ cdlvsm euglena run
euglena: '.' is not a Euglena app — no manifest.json in /home/you/scratch.
euglena runs and builds cells: manifest.json + src/*.gene.code.
For a plain code project or a lone .code file, use code directly:
cdlvsm code run .
To make this directory a Euglena app: cdlvsm euglena init <name>
The other direction stays open, and is worth using: a Euglena app is a
code project. code list, code format, reading main.code to see what
your manifest and genes actually compile to — all of it works. Only
run/build should go through euglena, so the entry is regenerated first.
An organelle is a code native module, declared in manifest.json by
module name — not a path:
{
"name": "myapp",
"organelles": {
"con": "console",
"srv": { "module": "http_server", "config": { "port": "${PORT}" } }
}
}
cdlvsm euglena install console # code install console, then declares "console": "console"
cdlvsm euglena install http_server --as srv
cdlvsm euglena list # each alias, and whether it's installed
cdlvsm euglena uninstall srv # the ALIAS — `code uninstall` is the one taking a module name
install fetches the module via code install (bytes land in this project's
.code/modules/, pinned by sha256 in .code/lock.json) and records the
alias. At generate time euglena checks the module name against that lockfile
and writes link "<name>.<ext>" as <alias> — link "console.so" as con,
not the platform-suffixed asset code install laid down
(console-linux-x86_64.so). code's loader maps the tidy spelling back to
the pinned asset through the same lockfile. A declared organelle with no lock
entry fails generation with the fix: euglena install <name>.
install / uninstall / list are cdlvsm's and code's three words, on
purpose — one vocabulary across the family. What differs is the argument, and
it has to: code uninstall takes a module name, euglena uninstall takes the
alias the manifest is keyed by and euglena list prints. uninstall needs
code >= 1.3.0, the release those names landed in.
A reference containing / or ending .so/.code is treated as a literal
path instead — an escape hatch for a vendored or locally-built organelle.
A config block, if given, becomes emit <Setup> { … } to <alias> get _cfg_<alias> right after the links — where <Setup> is the particle the
module names for its configuration (Config for most, Listen for
http_server), read from its module.json via .code/lock.json. A
config block on a stateless module (crypto, strings, …) is a
generation error — those take their parameters per call. A literal-path
organelle names its own setup particle in the entry ("setup": "Config"),
since there is no lockfile row to read.
An organelle is code's: a native artifact with an ABI, reached through an
alias. A gene is euglena's: .code source whose handlers join the one
program-wide table, exactly as src/*.gene.code do. So a shared palette, a
shared clock, a shared anything is written once and installed, rather than
copied into every application that wants it.
{
"name": "myapp",
"organelles": { "dom": "dom" },
"genes": ["palette"]
}
cdlvsm euglena install palette --gene # fetch it, and declare it
cdlvsm euglena install # a fresh checkout: every gene the manifest names
cdlvsm euglena uninstall palette # undeclare, unpin, remove
--gene is only needed the first time. Once manifest.json declares it,
euglena install palette means the gene.
Source, not a compiled artifact, and that is not a compromise. A
.so/.a/.wasm is a module: its handlers are reachable only through an
alias, which is the one thing a gene must not have. Source also has no
per-platform matrix — one file serves a native build, a browser build and a
held one alike.
The genes that ship today:
| gene | what it is | asks of the application |
|---|---|---|
palette | the workspace's colours, both themes, read from ui:theme | a store alias |
diagram | a flow of steps drawn as a dom value — boxes left to right joined by arrows, widths by duration, colours by outcome; Mermaid's flowchart look with nothing to parse | the palette gene (Palette); DiagramFlow { steps } → Node { value, styles } |
outbox | changes not yet at the service: a line sent one at a time, kept through no network and a reload, with one word — saving, saved, failed — for the toolbar | store, json, clock, arrays aliases (arrays supplies Length); OutboxDeliver { item } and OutboxChanged handlers; OutboxDelivered / OutboxRejected / OutboxUndelivered { id } called from wherever answers land |
versions | a stored program (a rule, a saved plasmid) keeps every version: saving adds one and deactivates the last, deleting deactivates, restoring makes an old source the new active version; each records created_at | no alias; VersionsRead { name } → VersionsHeld { versions } and VersionsWrite { name, versions } → VersionsWritten handlers over the application's own store |
A gene's handlers share the program's one table, so a gene owns a prefix
(Outbox…) rather than a plain word an application is likely to have.
Genes live in this repository (genes/*.gene.code), are published as release
artifacts beside the CLI, and carry euglena's own version: an application
holding euglena 0.3.0 and palette 0.3.0 never has to ask whether the two
agree. install fetches the metadata, checks the bytes against the sha256 it
publishes, writes them to .code/genes/<name>/<version>/, and pins them in
.code/lock.json — in a genes section of its own, beside but not among the
modules, because the two are different kinds of thing.
At generate time euglena verifies the installed bytes against that pin again
— a gene edited in place is refused, not quietly compiled in — and links the
installed ones before src/*.gene.code, so an application reads its own
last. There is no namespace, so a name defined by both a gene and the
application is refused before the program runs. The pin is written into the
entry as a comment:
| gene [email protected] sha256:81373a59b1c2
link ".code/genes/palette/0.3.0/palette.gene.code"
link "src/nucleus.gene.code"
That line is what makes a republished gene visible. Without it the entry
would name only a path, so a gene rebuilt at the same version would leave
the generated text identical and every application would go on reporting "up
to date" while compiling in bytes that had changed. The digest is part of the
body, so the stamp covers it and doctor says STALE the moment a pin moves.
EUGLENA_GENE_RELEASE points the registry somewhere else — a directory on
disk (file://…) is how a gene is tried before it is published.
An application that may be run on its own or held inside a host has one thing it cannot settle at build time: its door. Alone it opens a port; held it must not, because a thread that outlives the application cannot be unloaded, and an application that cannot be unloaded never gives its memory back.
"hosted" names the stand-in to link instead when it turns out to be held:
"organelles": {
"net": { "module": "net_server", "hosted": "membrane",
"config": { "port": "${PORT}" } }
}
One manifest, one build, both lives. The alias is the same, the particles are
the same, the config block is the same — no gene changes anything. Only
the organelle behind the name differs, and euglena picks it while the
application starts, by asking code whether a host is holding it.
Both sides have to answer the same setup particle, since one config block
is emitted for whichever gets linked; a mismatch is a generation error rather
than a surprise in whichever of the two lives you did not test.
Needs code >= 1.8.0 — see Requiring a code version.
${VAR} in a config value interpolates from the environment (loaded from
a .env file next to manifest.json, if present) — so secrets stay out of
version control. EUGLENA_MOCK_MODE=true (optionally scoped with
EUGLENA_MOCK_TYPES=type1,type2) overlays a mock-organelles block onto
organelles in memory, for local/test runs — nothing here has an
equivalent in code itself.
code versioneuglena refuses a code older than 1.8.0, because that is what the
syntax it generates needs. What your app needs is a different question —
a module handler, a field, a particle that only exists from some version on —
and it is the app's to state:
{
"name": "myapp",
"code": ">=1.5.0",
"organelles": { "srv": { "module": "http_server", "config": { "port": "${PORT}" } } }
}
run, build and test then refuse anything below the higher of
euglena's own baseline and this field, and the error says which of the two
asked, since the fix differs:
euglena: found Code v1.8.0 at '/home/you/.local/bin/cdlvsm-code',
but this needs >= v1.9.0, which is what this app's manifest.json asks for.
(There is a third input — a floor for a subcommand newer than the baseline — but nothing needs one today: the 1.8.0 baseline is above every version that introduced a command euglena delegates to.)
Only a minimum is expressible — "1.5.0" and ">=1.5.0" mean the same
thing, and carets, tildes and ranges are refused by name. A code
requirement only ever moves forward, so the other spellings would describe a
situation that does not arise.
cargo build --release # ./target/release/euglena
cargo test
Depends only on clap and serde_json — no LLVM, no build-time dependency
on the code language (it invokes the code binary at runtime). The
real-run integration tests are gated behind EUGLENA_TEST_CODE_BIN=/path/to/code
(CI has no code); point it at a code >= 1.8.0 binary to run them.
GPL-3.0-or-later — see LICENSE.
A browser shell declares its hosted projects in manifest.json:
"guests": ["../auth-web", "../todo-web"]
Paths are relative to the shell project. euglena doctor compares every guest's
real organelle module references with the shell's references, regardless of alias
names, and reports each missing guest/alias/module. doctor --guest ../other-web
adds a project for this check without changing the manifest. Guest manifests are
read without loading their .env files or mock overlays. Missing or malformed
guest declarations fail the check. Wasm builds enforce declared guests before
entry generation; deployment should also run the shell's doctor when a guest
changes. Keep the list current when registering a hosted application.
Doctor also audits existing module/gene pins (unreleased, ticket 106). A pin no longer declared by real/mock organelles, hosted stand-ins or genes produces an advisory warning; no bytes or lock entries are deleted. Declared hosted guests with a newer stable numeric module version than the shell also warn. Unknown version forms are not ordered, and ordering does not establish compatibility. Malformed lock JSON/sections fail the audit. Warnings retain a successful exit when the other checks pass, with a summary that acknowledges the warnings.