package.json that declares which entry files power which tiers. Aether reads this manifest at load time to discover entry points for the Pi agent layer, the Script Mod runtime, and the native DEX loader. Only the fields relevant to the tiers you use are required — unused tier fields are simply absent.
Full package.json example
The example below activates all three tiers from a single package:package.json
Fields reference
An npm-style package name that identifies your extension. Use lowercase letters, numbers, and hyphens (for example,
my-aether-mod or @myorg/chat-enhancer). Aether uses this name in the Extensions list and when generating stable extension IDs.A semver version string (for example,
1.0.0). Increment this value when you publish updates so Aether and package managers can track changes.Array of paths to Pi extension entry files, relative to the package root. Each file is loaded by the Pi Coding Agent runtime. Export a factory function as
default to register Pi commands, tools, and hooks. Omit this field if your extension does not target the Pi agent layer.Array of paths to Aether Script Mod entry files, relative to the package root. Each file is loaded by the Aether Script Mod runtime (API v2). Export your factory as
activateAether or aether to register surfaces, components, pages, actions, and event handlers. The runtime also accepts a default export for entries explicitly listed here (useful for packages that target only this tier). Changes to these files hot-reload without restarting the app. Omit this field if your extension does not use the Script Mod API.Array of paths to
.dex, .apk, or jar/zip files that contain a compiled classes.dex, relative to the package root. Aether passes these paths to DexClassLoader at process startup. A plain JVM .class jar is not sufficient — run Android D8/R8 on your compiled output first. Omit the entire aether.native object if your extension does not include a Native Mod.Array of paths to directories containing
.so native libraries, relative to the package root. Aether adds these directories to the native library search path when loading the DEX classpath. Only required if your Native Mod uses JNI libraries.Array of fully-qualified Kotlin class names that implement
AetherNativeMod. Aether instantiates each class via the DEX classloader and calls onLoad(context) during process startup. Requires aether.native.classpath to contain the compiled classes.Script-only vs native-only packages
Script-only packages omit theaether.native object entirely. This is the most common extension format and supports full hot-reload:
package.json
pi field and the extensions field under aether. Include only aether.native:
package.json
Installation location
Extension packages live under~/.aether/extensions inside Aether’s managed Alpine filesystem. When you import a zip via Settings → Extensions → Import, Aether extracts the package into a subdirectory there. npm-based packages installed through Aether’s package management are also discovered from this root at load time.
Shared Pi + Aether entry point
When you share a single entry file betweenpi.extensions and aether.extensions, keep the Pi factory as the default export and export the Aether factory as activateAether or aether. The Script Mod runtime looks for activateAether or aether first; it falls back to default only for entries that are explicitly listed in aether.extensions (as opposed to implicitly discovered files).
index.ts
The
defineAetherExtension helper provides TypeScript types for the aether argument and is a no-op at runtime — it simply returns the factory function you pass in. Import it from @aether/extension-api.