@moesi/cli
v0.4.0
Published
Moesi CLI — `moesi plan` (state diff) and `moesi apply` (browser-bridged signing). Entry point for the @moesi/* state-diff multi-chain deploy stack.
Maintainers
Readme
@moesi/cli
Command-line entry for Moesi. Core commands: plan, drift, apply, runs, run, validate, generate, schema, doctor, verify, and chains.
Install
npm install -g @moesi/cliOr call directly via npx:
npx @moesi/cli plan ./manifest.yaml --kernel 0xYourKernelCommands
Manifest arguments accept YAML or a TypeScript/JavaScript module whose default export is defineManifest(...).
moesi plan <manifest> [--kernel <addr>]
Reads on-chain state across every chain in the manifest and prints the state-diff matrix. External-only infrastructure manifests run keylessly; deployable or identity-referencing manifests require --kernel.
moesi drift <manifest> [--kernel <addr>]
Same as plan, but the output filters to drift cells only and the exit code is non-zero when drift exists. Drop into CI:
- run: moesi drift ./manifest.yaml --kernel ${{ secrets.KERNEL }}One-shot mode exits 2 when any cell is drift, infra-missing, or
unknown; missing deployable/permissioned cells remain visible in plan but
do not fail this attestation gate. --watch persists the last block-pinned snapshot under
~/.moesi/observations, so transitions survive process restarts. --json
with --watch emits baseline, transition, and error events as NDJSON.
moesi apply <manifest> --kernel <addr>
Opens the token-gated localhost browser bridge for signing. Connect
@moesi/web to drive the signing flow. Receipts persist below
~/.moesi/receipts/<first-12-manifest-hash-characters>/<chain-key>/. Apply
also persists an authority-free Run v1 artifact under ~/.moesi/runs before
execution begins. The bridge rejects receipts until Kernel account, version,
EntryPoint, permission ID, starting nonce, exact action scopes, and compiled
enforcement evidence are durably recorded. Apply exits 3 when any planned
action lacks a verified receipt; only independently finalized evidence counts
as durable run completion.
Open the web app with the bridge URL printed by apply:
http://localhost:5173/?bridge=http%3A%2F%2F127.0.0.1%3A3210%2F%3Ftoken%3D...Resume is explicit and fail-closed:
moesi runs
moesi run <run-id> --json
moesi apply ./manifest.yaml --kernel 0xKernel --resume <run-id>Before replay, Moesi re-verifies saved receipt transaction/UserOperation, canonical block and finality, runtime code, attribution, enforcement, and postcondition evidence. If finalized coverage now completes the run, nothing is replayed. Otherwise the currently observed action set must equal the old run's uncovered set exactly; a new nonce-bound run supersedes the interrupted one after fresh browser authorization. Legacy receipt files alone never skip an action.
moesi validate <manifest> [--json]
Loads YAML or a manifest module, validates the schema and refs, and prints the canonical manifest hash. It performs no RPC reads and never signs.
moesi generate <manifest.ts> -o <manifest.yaml>
Exports a TypeScript/JavaScript manifest as deterministic YAML, preserving authored contract order, and verifies that reloading the generated file produces the same deployment order and canonical manifest hash.
// manifest.ts
import { defineManifest } from "@moesi/model";
const chains = ["sepolia", "base-sepolia", "op-sepolia"];
const tokens = ["0xTokenA", "0xTokenB", "0xTokenC"];
export default defineManifest({
apiVersion: "moesi.dev/v1.0",
chains,
contracts: {
Router: {
deployer: "createX-CREATE2",
salt: "router-v1",
artifact: "./Router.json",
post_deploy: [{
method: "setRoutes",
batch: true,
for_each: chains.flatMap((chain, chainId) =>
tokens.map((token) => ({ chain, chainId, token }))),
args: ["${item.chainId}", "${item.token}"],
}],
},
},
registry: {},
});moesi validate ./manifest.ts
moesi generate ./manifest.ts -o ./manifest.yamlmoesi schema [--json]
Prints the Moesi manifest JSON Schema for editor integration and CI.
moesi doctor [manifest] [--artifacts <dir>] [--json]
Checks local runtime state: Node version, package-manager context, ZeroDev env, optional Etherscan env, manifest readability, artifact coverage, ${env.X} refs, and chain catalog loading. Exits non-zero when required local inputs are missing.
moesi verify <verify-config.json>
Submits source code to Etherscan V2 and Sourcify. Reads a JSON config:
{
"contracts": [
{
"address": "0xabc...",
"chainId": 11155111,
"contractName": "src/Counter.sol:Counter",
"compilerVersion": "v0.8.20+commit.a1b79de6",
"optimization": { "enabled": true, "runs": 200 },
"license": "apache-2.0",
"standardJson": "./out/standard-input.json",
"metadata": "./out/Counter.sol/Counter.metadata.json",
"sources": {
"src/Counter.sol": "./src/Counter.sol"
}
}
]
}Etherscan is skipped if ETHERSCAN_API_KEY is not set. Sourcify runs unconditionally.
Env
| Var | When | Purpose |
|---|---|---|
| ZERODEV_PROJECT_ID | apply | Required for AA bundler |
| ETHERSCAN_API_KEY | verify | Optional — enables Etherscan |
| SEPOLIA_RPC, BASE_SEPOLIA_RPC, etc. | all | Optional per-chain RPC overrides |
License
Apache-2.0
