Overview
RPCortex packages are standard ZIP archives renamed to .pkg. Each package contains your Python source files and a package.cfg metadata file. The package manager handles installation, command registration, and removal — no reboot needed.
What you need:
- Python 3 on your PC (for
make_pkg.py) - A MicroPython file with at least one callable function
- A
package.cfgfile describing your package
.pkg archives using ZIP STORED (no compression). Required for Pico-compatible packages.
Package Structure
A package is a directory with at least two files:
MyPackage/
package.cfg # required: metadata and command registration
main.py # your code (can be named anything)
helpers.py # optional: additional modules
The directory name becomes the archive prefix. make_pkg.py walks the entire directory tree, so subdirectories work too.
zlib available. Packages must be built with no compression. make_pkg.py handles this automatically — do not use a standard ZIP tool or your package will fail to install on many devices.
package.cfg Reference
Plain text, one key per line, colon-separated. Lines starting with # are comments.
# RPCortex Package Config
pkg.name: MyPackage
pkg.dev: yourname
pkg.ver: 1.0.0
pkg.dir: /Packages/MyPackage
pkg.desc: One-line description shown in pkg list
pkg.cmd: mycommand:/Packages/MyPackage/main.py:run
| Key | Required | Description |
|---|---|---|
| pkg.name | Yes | Display name. Used by pkg info and pkg list. |
| pkg.dev | Yes | Author or maintainer. |
| pkg.ver | Yes | Version string (e.g. 1.0.0). Used by pkg upgrade. |
| pkg.dir | Yes | Absolute install path on device (e.g. /Packages/MyPackage). |
| pkg.desc | Yes | Short description shown in search and list output. |
| pkg.cmd | No | Shell command registration (see below). Omit for library-only packages. |
pkg.cmd format
One command per pkg.cmd line. Multiple commands are supported by repeating the key:
pkg.cmd: hello:/Packages/HelloWorld/main.py:hello
pkg.cmd: hello-verbose:/Packages/HelloWorld/main.py:hello_verbose
Format: <shell-command>:</absolute/path/to/file.py>:<function_name>
- shell-command — what the user types at the prompt
- path — absolute path to the Python file on the device
- function_name — the function called when the command runs
Writing Commands
Every command function receives a single args parameter containing everything the user typed after the command name, or None if nothing was given.
def mycommand(args=None):
if args:
# args is a plain string: "foo bar baz"
parts = args.split(None, 1)
else:
# no arguments given
pass
Output utilities
Import from RPCortex for colored, logged output:
import sys
if '/Core' not in sys.path:
sys.path.append('/Core')
from RPCortex import ok, info, warn, error, multi, inpt
| Function | Color | Use for |
|---|---|---|
| ok(msg) | Green [OK] | Success messages |
| info(msg) | Blue [ i] | Section headers, informational |
| warn(msg) | Yellow [!] | Non-fatal warnings |
| error(msg) | Red [E] | Recoverable errors |
| fatal(msg) | Red [X] | Critical failures |
| multi(msg) | White | Plain output, data lines |
| inpt(prompt) | Cyan | Interactive user input |
Rules for MicroPython compatibility
- Use
"{}".format(...)instead of f-strings if you want to support older firmware - Use
str.split(None, N)positional form, notsplit(maxsplit=N) - Keep imports inside functions where possible — the exec scope is loaded fresh each call and imports inside functions stay local, saving RAM
- Avoid large module imports at the top of the file; the exec scope must fit in available heap
Building a .pkg
Run make_pkg.py from your PC, pointing it at your package directory:
python make_pkg.py packages/MyPackage
# or specify an output name:
python make_pkg.py packages/MyPackage mypackage.pkg
If no output path is given, the file is named after pkg.name from your package.cfg (lowercased).
Example output:
+ MyPackage/package.cfg
+ MyPackage/main.py
Created 'mypackage.pkg' (1842 bytes)
Compression: ZIP_STORED (Pico-compatible, no zlib needed)
Install on device: pkg install /path/to/mypackage.pkg
make_pkg.py requires only the Python 3 standard library (zipfile, os, sys). No dependencies to install.
Testing Locally
Copy the .pkg file to your device (via Thonny, mpremote, or ampy), then install it from the shell:
pkg install /mypackage.pkg
The command is registered immediately. Run it:
mycommand
mycommand some arguments here
Check it appears in the package list:
pkg list
pkg info MyPackage
To remove it:
pkg remove MyPackage
The command disappears from the shell immediately — no reboot needed.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Install fails with zlib error | Package was built with ZIP deflate compression | Rebuild with make_pkg.py (uses ZIP_STORED) |
| Command not found after install | pkg.cmd path or function name is wrong | Check the path in package.cfg matches the actual file on device |
| MemoryError during install | Heap fragmented after other commands | Run freeup first, then retry |
| Function not callable | Wrong function name in pkg.cmd | Verify the function name matches exactly (case-sensitive) |
Hosting a Repo
A repo is a single JSON file accessible over HTTP or HTTPS. GitHub raw URLs, static hosting, and anything that returns Content-Type: application/json all work.
index.json format
{
"name": "My Repo",
"maintainer": "yourname",
"packages": [
{
"name": "MyPackage",
"ver": "1.0.0",
"desc": "One-line description",
"author": "yourname",
"url": "https://example.com/repo/packages/mypackage.pkg"
}
]
}
| Field | Description |
|---|---|
| name | Repo display name (shown during pkg update) |
| maintainer | Repo owner |
| packages[].name | Package name (must match pkg.name in package.cfg) |
| packages[].ver | Version string (used by pkg upgrade to detect updates) |
| packages[].desc | Shown in pkg available and pkg search |
| packages[].author | Package author |
| packages[].url | Direct download URL for the .pkg file |
Hosting on GitHub
The simplest option: put index.json and your .pkg files in a GitHub repo, then use raw URLs:
# index.json URL (add this on the device):
https://raw.githubusercontent.com/yourname/my-rpc-repo/main/index.json
# .pkg URL in index.json:
https://raw.githubusercontent.com/yourname/my-rpc-repo/main/packages/mypackage.pkg
freeup before pkg update on a Pico 1. Pico 2 and ESP32 are not affected.
Adding your repo to a device
pkg repo add https://raw.githubusercontent.com/yourname/my-rpc-repo/main/index.json
pkg update
pkg available
Getting Listed in the Official Repo
The official RPCortex repo is hosted at rpc.novalabs.app/repo/index.json. To get your package listed:
- Make sure your package installs and runs cleanly on a Pico W (the primary target)
- Host your
.pkgfile somewhere with a stable URL (GitHub releases or raw works well) - Open a pull request on GitHub adding an entry to
website/repo/index.json - Include your package name, version, description, author, and the URL of your hosted
.pkg
{
"name": "YourPackage",
"ver": "1.0.0",
"desc": "What your package does",
"author": "yourname",
"url": "https://your-host/yourpackage.pkg"
}
Full Example: HelloWorld
This is the reference sample package included in the repo.
File: HelloWorld/package.cfg
# RPCortex Package Config
pkg.name: HelloWorld
pkg.dev: dash1101
pkg.ver: 1.0.1
pkg.dir: /Packages/HelloWorld
pkg.desc: Sample demo package for RPCortex
pkg.cmd: hello:/Packages/HelloWorld/main.py:hello
File: HelloWorld/main.py
# Desc: HelloWorld -- sample RPCortex package
# File: /Packages/HelloWorld/main.py
# Version: 1.0.1
# Author: dash1101
import sys
if '/Core' not in sys.path:
sys.path.append('/Core')
from RPCortex import ok, multi
def hello(args=None):
multi("")
multi(" Hello from HelloWorld!")
multi(" RPCortex package system is working correctly.")
if args:
multi(" You said: " + args)
multi("")
ok("HelloWorld v1.0.1")
Build it
python make_pkg.py HelloWorld
# Output: helloworld.pkg
Install and test on device
# Copy helloworld.pkg to device root, then in the shell:
pkg install /helloworld.pkg
hello
hello testing 1 2 3
Expected output
Hello from HelloWorld!
RPCortex package system is working correctly.
[OK] HelloWorld v1.0.1
RPCortex Nebula v0.8.1-beta2 • by dash1101