M0: toolchain, monorepo scaffold, gateway plugin skeleton, CMP app
- gateway-plugin/: android platform plugin (plugin.yaml + adapter.py register(ctx) + no-op AndroidAdapter) + stub modules for M1-M5 - app/: Compose Multiplatform project (shared KMP + androidApp + desktopApp) with Gradle wrapper; builds :androidApp:assembleDebug and :desktopApp:compileKotlin - scripts/guard_hermes_agent.sh + pre-commit hook: fail if hermes-agent/ is staged (read-only reference, never committed) - .gitignore excludes hermes-agent/; docs/ reference library
This commit is contained in:
commit
59acf66c89
49 files changed
+3950
No files matched your search
@@ -0,0 +1,138 @@
|
||||
# 12 — Toolchain Setup
|
||||
|
||||
First-time setup on a machine (verified baseline: CachyOS/Arch, `pacman`,
|
||||
`uv` present, ADB present, no JDK/SDK/Gradle).
|
||||
|
||||
## 12.1 JDK 17
|
||||
|
||||
```bash
|
||||
pacman -S jdk17-openjdk
|
||||
java -version # expect 17.x
|
||||
```
|
||||
(Compose Multiplatform + current AGP are happy on JDK 17. Use 17 to match the
|
||||
Android toolchain; 21 also works but 17 is the safe floor.)
|
||||
|
||||
## 12.2 Android SDK
|
||||
|
||||
```bash
|
||||
# cmdline-tools
|
||||
mkdir -p ~/android-sdk/cmdline-tools
|
||||
cd ~/android-sdk/cmdline-tools
|
||||
curl -O https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
|
||||
unzip commandlinetools-linux-*.zip && mv cmdline-tools latest
|
||||
rm commandlinetools-linux-*.zip
|
||||
|
||||
export ANDROID_HOME=$HOME/android-sdk
|
||||
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools
|
||||
|
||||
sdkmanager --licenses
|
||||
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
|
||||
```
|
||||
Persist `ANDROID_HOME`/`PATH` in `~/.bashrc`. ADB is already installed system-wide;
|
||||
`platform-tools` from the SDK is fine too (whichever is first on `PATH`).
|
||||
|
||||
Create `app/local.properties`:
|
||||
```
|
||||
sdk.dir=/home/<you>/android-sdk
|
||||
```
|
||||
|
||||
## 12.3 Gradle
|
||||
|
||||
No system install — use the project wrapper:
|
||||
```bash
|
||||
cd app
|
||||
./gradlew tasks # first run downloads the wrapper distribution
|
||||
```
|
||||
(The wrapper version is pinned in `app/gradle/wrapper/gradle-wrapper.properties`.)
|
||||
|
||||
## 12.4 hermes environment (for the plugin + running the gateway)
|
||||
|
||||
```bash
|
||||
cd hermes-agent
|
||||
uv sync # creates .venv with all core deps (websockets, httpx, …)
|
||||
source .venv/bin/activate
|
||||
hermes --version # sanity
|
||||
```
|
||||
- Run the gateway with the plugin:
|
||||
```bash
|
||||
# install the plugin (dev: symlink)
|
||||
mkdir -p ~/.hermes/plugins
|
||||
ln -s "$PWD/../gateway-plugin" ~/.hermes/plugins/android
|
||||
hermes gateway status # should list "android"
|
||||
hermes gateway # run
|
||||
```
|
||||
- Tests use hermes's hermetic runner (never bare `pytest`):
|
||||
```bash
|
||||
scripts/run_tests.sh tests/gateway/test_android.py
|
||||
```
|
||||
|
||||
## 12.5 Firebase (FCM) — primary push
|
||||
|
||||
1. Create a Firebase project (console.firebase.google.com).
|
||||
2. Add an **Android app** (package = `androidApp` applicationId, e.g.
|
||||
`dev.iris.app`). Download `google-services.json` → `app/androidApp/`.
|
||||
3. Create a **service account** (Project settings → Service accounts → Generate
|
||||
new private key) → download the JSON. Store its path in
|
||||
`ANDROID_FCM_SERVICE_ACCOUNT` (in `~/.hermes/.env`).
|
||||
4. The app's `FirebaseMessagingService` obtains the FCM token at runtime and
|
||||
registers it via `hello` / `fcm.register`.
|
||||
|
||||
> Skip Firebase → set `ANDROID_PUSH_BACKEND=ntfy` and configure `NTFY_TOPIC` /
|
||||
> `NTFY_SERVER_URL` (self-host ntfy or use ntfy.sh). See `08-push.md`.
|
||||
|
||||
## 12.6 Environment variables (summary)
|
||||
|
||||
**Secrets (`~/.hermes/.env`):**
|
||||
```
|
||||
ANDROID_TOKEN=<64-hex>
|
||||
ANDROID_PUSH_BACKEND=fcm # or ntfy
|
||||
ANDROID_FCM_SERVICE_ACCOUNT=/path/to/service-account.json
|
||||
# ANDROID_FCM_SERVER_KEY=<legacy key> # fallback if no service account
|
||||
# NTFY_TOPIC=iris-push # when ntfy
|
||||
# NTFY_SERVER_URL=https://ntfy.sh
|
||||
# ANDROID_WS_CERT=/path/cert.pem # WSS
|
||||
# ANDROID_WS_KEY=/path/key.pem
|
||||
```
|
||||
|
||||
**Behavioral (`~/.hermes/config.yaml`):**
|
||||
```yaml
|
||||
gateway:
|
||||
platforms:
|
||||
android:
|
||||
enabled: true
|
||||
extra:
|
||||
host: 127.0.0.1 # 0.0.0.0 for LAN
|
||||
port: 8790
|
||||
home_channel: android:default
|
||||
push_backend: fcm
|
||||
outbox_retention_hours: 72
|
||||
max_upload_bytes: 104857600 # 100 MB
|
||||
display:
|
||||
platforms:
|
||||
android:
|
||||
show_reasoning: true
|
||||
reasoning_style: code
|
||||
streaming: true
|
||||
tool_progress: all # gateway sends full data; app controls display
|
||||
```
|
||||
|
||||
## 12.7 Verify the stack (smoke test)
|
||||
|
||||
```bash
|
||||
# 1. gateway up with plugin
|
||||
hermes gateway status | grep -i android
|
||||
|
||||
# 2. a raw WS client can pair + echo
|
||||
python - <<'PY'
|
||||
import asyncio, json, websockets
|
||||
async def main():
|
||||
async with websockets.connect("ws://127.0.0.1:8790/ws") as ws:
|
||||
await ws.send(json.dumps({"v":1,"type":"hello","payload":{
|
||||
"token":"<ANDROID_TOKEN>","device_id":"test","device_name":"probe",
|
||||
"caps":{"min_protocol":1}}}))
|
||||
print("recv:", await ws.recv())
|
||||
asyncio.run(main())
|
||||
PY
|
||||
```
|
||||
Expect a `hello.ack`. If you get `error {code:"auth"}`, the token/host/port is
|
||||
wrong.
|
||||
Reference in new issue
Block a user