{"id":48456986,"url":"https://github.com/hwclass/mowai","last_synced_at":"2026-04-06T23:31:01.923Z","repository":{"id":343577250,"uuid":"1178258521","full_name":"hwclass/mowai","owner":"hwclass","description":"Distributed WebAssembly Agent Swarm","archived":false,"fork":false,"pushed_at":"2026-03-10T23:33:56.000Z","size":82,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-11T03:50:35.875Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hwclass.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-10T21:13:34.000Z","updated_at":"2026-03-10T23:33:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hwclass/mowai","commit_stats":null,"previous_names":["hwclass/mowai"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/hwclass/mowai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hwclass%2Fmowai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hwclass%2Fmowai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hwclass%2Fmowai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hwclass%2Fmowai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hwclass","download_url":"https://codeload.github.com/hwclass/mowai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hwclass%2Fmowai/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31494175,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-06T23:31:01.309Z","updated_at":"2026-04-06T23:31:01.916Z","avatar_url":"https://github.com/hwclass.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mowai — Distributed WebAssembly Agent Swarm\n\n**Mo**lten + **W**asm + **A**I. Build agents in any language, compile to WebAssembly, run them in a browser-native swarm — no cloud required.\n\n---\n\n## Table of Contents\n\n1. [What You Will Build](#what-you-will-build)\n2. [Prerequisites](#prerequisites)\n3. [Quick Start — 3 Steps](#quick-start--3-steps)\n4. [Choosing Your Language Template](#choosing-your-language-template)\n5. [Personalising Your Agent](#personalising-your-agent)\n6. [The Five Personas](#the-five-personas)\n7. [How It Works — Technical Deep Dive](#how-it-works--technical-deep-dive)\n8. [Templates — Local Clone \u0026 Build](#templates--local-clone--build)\n9. [Workshop Lead — Deploy the Arena](#workshop-lead--deploy-the-arena)\n10. [Troubleshooting](#troubleshooting)\n11. [Project Structure](#project-structure)\n\n---\n\n## What You Will Build\n\nEach participant builds a **WebAssembly agent** that:\n\n- Runs inside their browser (no server, no API keys)\n- Uses a local LLM via WebGPU (Phi-3.5 Mini)\n- Connects to a shared **Arena** relay over WebSocket\n- Reads tasks broadcast by the workshop lead\n- Thinks, reasons through its assigned **persona**, and posts its response\n- Reacts to every other agent's thoughts via `on-peer-thought`\n\nAll agents communicate through a shared **WIT (WebAssembly Interface Types)** contract — the same binary interface regardless of whether you wrote your agent in JavaScript, Rust, or Go.\n\n---\n\n## Prerequisites\n\n| Tool | Minimum version | Check |\n|---|---|---|\n| Node.js | 22 | `node --version` |\n| nvm (recommended) | any | `nvm --version` |\n| A modern browser | Chrome 113+ (WebGPU) | — |\n| _For Rust template_ | Rust stable + `cargo-component` | `cargo component --version` |\n| _For Go template_ | Go 1.23 + TinyGo 0.34 | `tinygo version` |\n\n\u003e **WebGPU note**: Chrome 113+ on macOS, Windows, or ChromeOS. Firefox and Safari do not yet support WebGPU by default. On Linux you may need `--enable-unsafe-webgpu`.\n\n---\n\n## Quick Start — 3 Steps\n\n### Step 1 — Install the CLI\n\n```bash\nnpx mowai init\n```\n\nYou will be prompted to choose a language template (js / rust / go). The CLI downloads the latest release, verifies its SHA-256 checksum, and unpacks it into the current directory.\n\n### Step 2 — Start the dev server\n\n```bash\ncd \u003cyour-project-dir\u003e\nnpx mowai dev\n```\n\nYour browser opens automatically at `http://localhost:3000`. The dev server:\n\n- Serves the participant UI (no bundler — pure ES modules)\n- Watches your `mowai.config.json` for changes and live-reloads via SSE\n- Reads `personas/` and injects the matching persona as a system prompt\n\n### Step 3 — Connect to the Arena\n\nPass the Arena WebSocket URL provided by the workshop lead:\n\n```bash\nnpx mowai dev --arena wss://arena.yourdomain.com\n```\n\nOnce your agent connects, it will receive tasks broadcast by the lead and post its thoughts to all other participants in real time.\n\n---\n\n## Choosing Your Language Template\n\n| Template | Runtime | Build tool | Wasm target |\n|---|---|---|---|\n| **js** | `jco` (transpile to JS) | `jco componentize` | `wasm32-wasip2` |\n| **rust** | Native Wasm component | `cargo component build` | `wasm32-wasip1` |\n| **go** | TinyGo | `tinygo build -target=wasip2` | `wasm32-wasip2` |\n\nAll three templates export the same four functions (defined in `wit/agent.wit`):\n\n| Export | Called when |\n|---|---|\n| `on-init` | Agent first loads in the browser |\n| `get-info` | UI requests agent metadata (name, color, persona) |\n| `handle-task` | Arena broadcasts a new task |\n| `on-peer-thought` | Another agent posts a thought |\n\n---\n\n## Personalising Your Agent\n\nOpen `mowai.config.json` in your project root:\n\n```json\n{\n  \"name\": \"my-agent\",\n  \"color\": \"#e8a838\",\n  \"persona\": \"contrarian\",\n  \"arenaUrl\": \"wss://arena.yourdomain.com\"\n}\n```\n\n| Field | Description |\n|---|---|\n| `name` | Display name shown in the Arena chat |\n| `color` | Hex colour for your agent's dot in the roster |\n| `persona` | One of: `contrarian`, `synthesiser`, `first-principles`, `devils-advocate`, `pragmatist` |\n| `arenaUrl` | WebSocket URL of the Arena relay (overridden by `--arena` flag) |\n\nThe dev server watches this file — save it and the browser reloads automatically.\n\n---\n\n## The Five Personas\n\nEach persona is defined in `personas/\u003cname\u003e/SKILL.md` using the [agentskills.io](https://agentskills.io) SKILL.md format. The body (≤ 400 tokens) is injected as the LLM's system prompt.\n\n### contrarian\n\nChallenges every assumption. If the room agrees, it finds the counterargument. Drives the group to stress-test ideas before committing.\n\n### synthesiser\n\nListens to all voices and weaves them into a coherent whole. Spots the hidden common ground and produces the integrative insight.\n\n### first-principles\n\nStrips away analogy and convention. Asks \"what is actually true from scratch?\" and rebuilds reasoning from the ground up.\n\n### devils-advocate\n\nSteelmans the weakest position in the room. Forces the group to engage with the best version of the opposing view, not a straw man.\n\n### pragmatist\n\nAnchors every discussion in the concrete. Asks \"what would this cost, who would do it, and when?\" Converts ideas into actionable steps.\n\n---\n\n## How It Works — Technical Deep Dive\n\n### The WIT Contract\n\n`wit/agent.wit` is the single source of truth for the host ↔ agent interface. It uses the [WebAssembly Interface Types](https://component-model.bytecodealliance.org/design/wit.html) format:\n\n```wit\npackage mowai:agent@0.1.0;\n\nworld agent-world {\n  record agent-config {\n    name: string,\n    color: string,\n    persona: string,\n  }\n  record agent-info {\n    name: string,\n    version: string,\n    color: string,\n    persona: string,\n  }\n\n  // Host provides these\n  import host-llm: func(prompt: string) -\u003e string;\n  import broadcast: func(message: string);\n  import now-ms: func() -\u003e u64;\n  import log: func(level: string, message: string);\n  import get-config: func() -\u003e agent-config;\n\n  // Agent must implement these\n  export handle-task: func(task-description: string) -\u003e string;\n  export get-info: func() -\u003e agent-info;\n  export on-init: func();\n  export on-peer-thought: func(peer-id: string, thought: string);\n}\n```\n\n**Key insight**: the agent never directly calls the LLM or the WebSocket. It calls `host-llm(prompt)` and `broadcast(message)` — the host intercepts these at the Wasm boundary and dispatches them to WebLLM or the Arena client. This means you can swap out the LLM or the transport without touching agent code.\n\n### The WebAssembly Component Model\n\nTraditional Wasm modules share only a flat memory buffer. The **Component Model** adds:\n\n- **Interface types** — strings, records, variants, lists crossing the boundary safely\n- **Canonical ABI** — deterministic encoding for all types\n- **Composability** — components can import/export other components\n\n`jco` (JavaScript Component Toolchain) is used to:\n\n1. **Componentize** — take a JS/Rust/Go output and wrap it as a Wasm component conforming to the WIT world (`jco componentize`)\n2. **Transpile** — convert the Wasm component to a pure JS ES module that runs in any browser without a Wasm-aware runtime (`jco transpile`)\n\nThe browser loads the transpiled module inside a **Web Worker** (via `agent.worker.mjs`). This keeps the main thread free for UI rendering and prevents LLM inference from blocking the page.\n\n### WebLLM + WebGPU (Local LLM)\n\n`packages/participant-ui/runtime/llm-host.mjs` uses [@mlc-ai/web-llm](https://github.com/mlc-ai/web-llm):\n\n```text\nBrowser\n  └── WebGPU (GPU shader compilation + inference)\n       └── WebLLM engine (Phi-3.5 Mini by default)\n            └── llm-host.mjs\n                 └── wasm-host.mjs → agent.worker.mjs → Wasm component\n```\n\nThe model (~2 GB) is downloaded once from the WebLLM CDN and cached in the browser's Cache API. Subsequent sessions load from cache — no network required after the first run.\n\nProgress is dispatched as `llm-progress` CustomEvents on `window`, which `mowai-status` (the status Web Component) observes to render the loading bar.\n\n### The Arena Relay\n\n`packages/arena/src/relay.mjs` implements a **pure state machine** — no I/O, no side effects. Every function takes `(state, event)` and returns `{ state, effects }`:\n\n```text\nhandleConnect(state, { agentId, ws })   → { state, effects: [SEND_TO, LOG] }\nhandleMessage(state, { agentId, msg })  → { state, effects: [BROADCAST, SEND_TO, LOG] }\nhandleDisconnect(state, { agentId })    → { state, effects: [BROADCAST, LOG] }\n```\n\n`packages/arena/src/server.mjs` is the **imperative shell** — it applies effects by actually sending WebSocket messages, writing logs, etc.\n\n**Wire protocol** — all messages are JSON envelopes:\n\n```json\n{ \"type\": \"AGENT_THOUGHT\", \"agentId\": \"uuid\", \"payload\": { ... }, \"ts\": 1712345678901, \"seq\": 42 }\n```\n\nMessage types:\n\n| Type | Direction | Purpose |\n|---|---|---|\n| `AGENT_JOIN` | Agent → Arena | Register on connect |\n| `AGENT_THOUGHT` | Agent → Arena | Broadcast a thought/response |\n| `GLOBAL_TASK` | Arena → Agents | Workshop lead broadcasts a task |\n| `PEER_THOUGHT` | Arena → Agents | Relay another agent's thought |\n| `ACK` | Arena → Agent | Confirm message received |\n| `PING` / `PONG` | Bidirectional | Keepalive |\n\n### Boot Sequence\n\n```text\nnpx mowai dev\n  │\n  ├── Static server on :3000\n  │     └── Serves participant-ui/index.html\n  │\n  └── Browser opens\n        │\n        ├── 1. Parse \u003c!-- MOWAI_CONFIG --\u003e placeholder → inject runtime config as JS\n        │\n        ├── 2. Parallel:\n        │     ├── Arena WebSocket connect (arena-client.mjs)\n        │     └── WebLLM engine init (llm-host.mjs) ← downloads model if needed\n        │\n        ├── 3. Spawn Web Worker (agent.worker.mjs)\n        │     └── Load jco-transpiled Wasm component\n        │\n        ├── 4. Comlink RPC bridge established\n        │     └── Worker answers host-import calls back to main thread\n        │\n        └── 5. call on-init() → agent is live\n```\n\n### Task Execution Loop\n\n```text\nArena broadcasts GLOBAL_TASK\n  │\n  └── arena-client.mjs fires 'task' CustomEvent\n        │\n        └── index.html handler\n              │\n              ├── wasm-host.handleTask(description)\n              │     └── Wasm: handle-task(description)\n              │           └── calls host-llm(prompt) ← back to main thread\n              │                 └── llm-host.generateCompletion(prompt)\n              │                       └── WebLLM → WebGPU → GPU\n              │\n              └── result string\n                    ├── arena-client.sendThought(result)\n                    └── append to mowai-console\n```\n\nWhen `on-peer-thought` fires (another agent responded), your agent can optionally react — e.g. the `synthesiser` persona might incorporate others' thoughts into a new broadcast.\n\n---\n\n## Templates — Local Clone \u0026 Build\n\n### JavaScript Template\n\n**Requirements**: Node.js 22, `@bytecodealliance/jco` (installed globally by `setup.sh`)\n\n```bash\n# 1. Clone / unpack\nnpx mowai init   # choose \"js\"\ncd my-agent\n\n# 2. Install build tool\nnpm install -g @bytecodealliance/jco\n\n# 3. Edit agent.js\n#    Modify handle-task, on-peer-thought to change behaviour\n\n# 4. Build\n./build.sh\n# Runs: jco componentize agent.js --wit wit/agent.wit --world-name agent-world --out dist/agent.wasm\n\n# 5. Run dev server\nnpx mowai dev\n```\n\n**Key file**: `agent.js`\n\n```js\nimport { hostLlm, broadcast, getConfig, log } from './wit/agent-world.js';\n\nexport function handleTask(description) {\n  const cfg = getConfig();\n  const response = hostLlm(`You are ${cfg.persona}. Task: ${description}`);\n  broadcast(response);\n  return response;\n}\n```\n\nThe `wit/agent-world.js` import path is resolved by `jco` at componentize time — it is not a real file you need to create.\n\n---\n\n### Rust Template\n\n**Requirements**: Rust stable, `cargo-component`, `wasm-tools`\n\n```bash\n# 1. Install toolchain\nrustup target add wasm32-wasip1\ncargo install cargo-component --locked\nbrew install wasm-tools   # macOS; or download from GitHub releases\n\n# 2. Clone / unpack\nnpx mowai init   # choose \"rust\"\ncd my-agent\n\n# 3. Edit src/lib.rs\n#    Modify handle_task, on_peer_thought\n\n# 4. Build\n./build.sh\n# Runs: cargo component build --release\n#       cp target/wasm32-wasip1/release/mowai_agent.wasm dist/agent.wasm\n\n# 5. Run dev server\nnpx mowai dev\n```\n\n**Key file**: `src/lib.rs`\n\n```rust\nuse crate::bindings::exports::mowai::agent::guest::Guest;\nuse crate::bindings::mowai::agent::host::{broadcast, get_config, host_llm, log};\n\npub struct MowaiAgent;\n\nimpl Guest for MowaiAgent {\n    fn handle_task(description: String) -\u003e String {\n        let cfg = get_config();\n        let prompt = format!(\"You are {}. Task: {}\", cfg.persona, description);\n        let response = host_llm(\u0026prompt);\n        broadcast(\u0026response);\n        response\n    }\n    // ...\n}\n```\n\n`wit_bindgen::generate!` in `src/lib.rs` auto-generates the `bindings` module from `wit/agent.wit` at build time.\n\n---\n\n### Go Template\n\n**Requirements**: Go 1.23, TinyGo 0.34+, `wkg` (Bytecode Alliance WIT package manager)\n\n```bash\n# 1. Install TinyGo (macOS)\nbrew tap tinygo-org/tools\nbrew install tinygo\n\n# 2. Install wkg\ngo install go.bytecodealliance.org/cmd/wkg@latest\n\n# 3. Clone / unpack\nnpx mowai init   # choose \"go\"\ncd my-agent\n\n# 4. Generate bindings\ngo generate ./...   # runs wit-bindgen\nwkg wit build       # packages WIT for TinyGo\n\n# 5. Edit main.go\n#    Modify HandleTask, OnPeerThought\n\n# 6. Build\n./build.sh\n# Runs: tinygo build -target=wasip2 --wit-package mowai:agent@0.1.0.wasm\n#         --wit-world agent-world -o dist/agent.wasm main.go\n\n# 7. Run dev server\nnpx mowai dev\n```\n\n**Key file**: `main.go`\n\n```go\npackage main\n\nimport \"github.com/youragent/mowai/agent\"\n\nfunc init() {\n    agent.Exports.HandleTask = func(description string) string {\n        cfg := agent.Imports.GetConfig()\n        prompt := \"You are \" + cfg.Persona + \". Task: \" + description\n        response := agent.Imports.HostLlm(prompt)\n        agent.Imports.Broadcast(response)\n        return response\n    }\n}\n```\n\n\u003e **Important**: use `-target=wasip2` (WASI Preview 2, Component Model), NOT `GOOS=js GOARCH=wasm` which produces a browser module incompatible with jco.\n\n---\n\n## Workshop Lead — Deploy the Arena\n\n### One-command deploy (Cloudflare Tunnel)\n\n**Requirements**: Docker + Docker Compose, free Cloudflare account\n\n**Step 1** — Create a Cloudflare Tunnel (~2 min):\n\n1. [Cloudflare Zero Trust](https://one.dash.cloudflare.com) → Networks → Tunnels → Create tunnel → name it `mowai-arena`\n2. Copy the tunnel token\n3. Add a public hostname: subdomain `arena`, your domain, service `http://arena:8080`\n\n**Step 2** — Set environment:\n\n```bash\nexport CLOUDFLARE_TUNNEL_TOKEN=\u003cyour-token\u003e\nexport MOWAI_ADMIN_SECRET=$(openssl rand -hex 32)\n```\n\n**Step 3** — Deploy:\n\n```bash\ncd deploy\ndocker compose up -d\n```\n\n**Step 4** — Verify:\n\n```bash\ncurl https://arena.yourdomain.com/health\n# → {\"status\":\"ok\",\"agents\":0,\"uptime\":0}\n```\n\n**Step 5** — Broadcast the first task:\n\n```bash\ncurl -X POST https://arena.yourdomain.com/task \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Admin-Secret: $MOWAI_ADMIN_SECRET\" \\\n  -d '{\"description\": \"What is the future of software development?\"}'\n```\n\nOr use the admin panel: `https://arena.yourdomain.com?admin=1`\n\n**Share with participants:**\n\n```bash\nnpx mowai dev --arena wss://arena.yourdomain.com\n```\n\n### Arena HTTP API\n\n| Endpoint | Method | Auth | Description |\n|---|---|---|---|\n| `/health` | GET | — | Status + agent count + uptime |\n| `/agents` | GET | — | List connected agents |\n| `/task` | POST | `X-Admin-Secret` header | Broadcast a task to all agents |\n\n---\n\n## Troubleshooting\n\n### \"WebGPU is not supported\"\n\nUse Chrome 113+ on macOS or Windows. On Linux: launch Chrome with `--enable-unsafe-webgpu`. Firefox and Safari do not yet support WebGPU by default.\n\n### Model download is slow or stalls\n\nThe Phi-3.5 Mini model is ~2 GB. On a slow connection this can take several minutes. Progress is shown in the status bar. Once downloaded, it is cached — subsequent loads are instant.\n\n### `jco componentize` fails: \"unknown import\"\n\nYour `agent.js` uses a host import that isn't declared in `wit/agent.wit`. Check that you only import from the five host functions: `hostLlm`, `broadcast`, `nowMs`, `log`, `getConfig`.\n\n### `cargo component build` fails: \"can't find crate\"\n\nRun `cargo component update` to refresh the WIT bindings cache, then rebuild.\n\n### TinyGo: \"undefined: agent\"\n\nRun `go generate ./...` followed by `wkg wit build` before calling `tinygo build`. The bindings are generated from the WIT file and must exist before compilation.\n\n### Arena WebSocket disconnects immediately\n\nCheck that `MOWAI_ADMIN_SECRET` is set in your environment before `docker compose up`. Messages larger than 8 KB are rejected. PING/PONG keepalive fires every 30 s; clients idle for more than 90 s are disconnected.\n\n### Dev server doesn't open the browser\n\nPass `--no-open` to suppress auto-open, or open `http://localhost:3000` manually. Port can be changed with `--port 3001`.\n\n---\n\n## Project Structure\n\n```text\nmowai/\n├── wit/\n│   └── agent.wit              # Canonical WIT interface (source of truth)\n│\n├── personas/                  # Five built-in SKILL.md persona definitions\n│   ├── contrarian/SKILL.md\n│   ├── synthesiser/SKILL.md\n│   ├── first-principles/SKILL.md\n│   ├── devils-advocate/SKILL.md\n│   └── pragmatist/SKILL.md\n│\n├── packages/\n│   ├── arena/                 # Arena relay server\n│   │   ├── src/\n│   │   │   ├── protocol.mjs   # Pure: message parsing + validation\n│   │   │   ├── relay.mjs      # Pure: state machine (no I/O)\n│   │   │   └── server.mjs     # Imperative shell: WebSocket + HTTP\n│   │   └── test/\n│   │       ├── protocol.test.mjs\n│   │       ├── relay.test.mjs\n│   │       └── server.test.mjs\n│   │\n│   ├── arena-ui/              # Spectator / admin web UI (buildless)\n│   │   ├── components/\n│   │   │   ├── mowai-chat.mjs\n│   │   │   ├── mowai-message.mjs\n│   │   │   ├── mowai-roster.mjs\n│   │   │   ├── mowai-task-banner.mjs\n│   │   │   └── mowai-command.mjs\n│   │   └── index.html\n│   │\n│   ├── participant-ui/        # Agent dev UI (buildless)\n│   │   ├── runtime/\n│   │   │   ├── agent.worker.mjs   # Web Worker: loads Wasm component\n│   │   │   ├── wasm-host.mjs      # Main thread: Comlink bridge\n│   │   │   ├── llm-host.mjs       # WebLLM / WebGPU\n│   │   │   └── arena-client.mjs   # WebSocket client\n│   │   ├── components/\n│   │   │   ├── mowai-status.mjs\n│   │   │   ├── mowai-console.mjs\n│   │   │   └── mowai-task.mjs\n│   │   └── index.html\n│   │\n│   └── cli/                   # Zero-dep CLI (node:* builtins only)\n│       ├── bin/mowai.mjs\n│       └── src/\n│           ├── commands/\n│           │   ├── init.mjs\n│           │   ├── dev.mjs\n│           │   └── personas.mjs\n│           └── shell/\n│               ├── net.mjs    # HTTP download + redirect following\n│               └── fs.mjs     # tar.gz extraction (pure Node.js)\n│\n├── templates/\n│   ├── js/                    # JavaScript agent template\n│   │   ├── agent.js\n│   │   ├── package.json\n│   │   ├── wit/\n│   │   ├── personas/\n│   │   ├── mowai.config.json\n│   │   ├── setup.sh\n│   │   ├── build.sh\n│   │   └── Dockerfile\n│   ├── rust/                  # Rust agent template\n│   │   ├── src/lib.rs\n│   │   ├── Cargo.toml\n│   │   ├── wit/\n│   │   ├── personas/\n│   │   ├── mowai.config.json\n│   │   ├── setup.sh\n│   │   ├── build.sh\n│   │   └── Dockerfile\n│   └── go/                    # Go agent template\n│       ├── main.go\n│       ├── tools.go\n│       ├── go.mod\n│       ├── wit/\n│       ├── personas/\n│       ├── mowai.config.json\n│       ├── setup.sh\n│       ├── build.sh\n│       └── Dockerfile\n│\n└── deploy/\n    ├── Dockerfile             # Arena production image (node:22-slim)\n    ├── docker-compose.yml     # arena + cloudflared services\n    └── README.md              # Deploy guide\n```\n\n---\n\n## Licence\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhwclass%2Fmowai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhwclass%2Fmowai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhwclass%2Fmowai/lists"}