--- name: setup description: Run initial NanoClaw setup. Use when user wants to install dependencies, authenticate WhatsApp/Gmail, register their main channel, or start the background services. Triggers on "setup", "install", "configure nanoclaw", or first-time setup requests. --- # NanoClaw Setup Run all commands automatically. Only pause when user action is required (scanning QR codes). ## 1. Install Dependencies ```bash npm install ``` ## 2. Install Apple Container Check if Apple Container is installed: ```bash which container && container --version || echo "Not installed" ``` If not installed, tell the user: > Apple Container is required for running agents in isolated environments. > > 1. Download the latest `.pkg` from https://github.com/apple/container/releases > 2. Double-click to install > 3. Run `container system start` to start the service > > Let me know when you've completed these steps. Wait for user confirmation, then verify: ```bash container system start container --version ``` **Note:** NanoClaw automatically starts the Apple Container system when it launches, so you don't need to start it manually after reboots. ## 3. Configure Claude Authentication Ask the user: > Do you want to use your **Claude subscription** (Pro/Max) or an **Anthropic API key**? ### Option 1: Claude Subscription (Recommended) Ask the user: > Want me to grab the OAuth token from your current Claude session? If yes: ```bash TOKEN=$(cat ~/.claude/.credentials.json 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty') if [ -n "$TOKEN" ]; then echo "CLAUDE_CODE_OAUTH_TOKEN=$TOKEN" > .env echo "Token configured: ${TOKEN:0:20}...${TOKEN: -4}" else echo "No token found - are you logged in to Claude Code?" fi ``` If the token wasn't found, tell the user: > Run `claude` in another terminal and log in first, then come back here. ### Option 2: API Key Ask if they have an existing key to copy or need to create one. **Copy existing:** ```bash grep "^ANTHROPIC_API_KEY=" /path/to/source/.env > .env ``` **Create new:** ```bash echo 'ANTHROPIC_API_KEY=' > .env ``` Tell the user to add their key from https://console.anthropic.com/ **Verify:** ```bash KEY=$(grep "^ANTHROPIC_API_KEY=" .env | cut -d= -f2) [ -n "$KEY" ] && echo "API key configured: ${KEY:0:10}...${KEY: -4}" || echo "Missing" ``` ## 4. Build Container Image Build the NanoClaw agent container: ```bash ./container/build.sh ``` This creates the `nanoclaw-agent:latest` image with Node.js, Chromium, Claude Code CLI, and agent-browser. Verify the build succeeded (the `container images` command may not work due to a plugin issue, so we verify by running a simple test): ```bash echo '{}' | container run -i --entrypoint /bin/echo nanoclaw-agent:latest "Container OK" || echo "Container build failed" ``` ## 5. WhatsApp Authentication **USER ACTION REQUIRED** Run the authentication script: ```bash npm run auth ``` Tell the user: > A QR code will appear. On your phone: > 1. Open WhatsApp > 2. Tap **Settings → Linked Devices → Link a Device** > 3. Scan the QR code Wait for the script to output "Successfully authenticated" then continue. If it says "Already authenticated", skip to the next step. ## 6. Configure Assistant Name Ask the user: > What trigger word do you want to use? (default: `Andy`) > > Messages starting with `@TriggerWord` will be sent to Claude. If they choose something other than `Andy`, update it in these places: 1. `groups/CLAUDE.md` - Change "# Andy" and "You are Andy" to the new name 2. `groups/main/CLAUDE.md` - Same changes at the top 3. `data/registered_groups.json` - Use `@NewName` as the trigger when registering groups Store their choice - you'll use it when creating the registered_groups.json and when telling them how to test. ## 7. Register Main Channel Ask the user: > Do you want to use your **personal chat** (message yourself) or a **WhatsApp group** as your main control channel? For personal chat: > Send any message to yourself in WhatsApp (the "Message Yourself" chat). Tell me when done. For group: > Send any message in the WhatsApp group you want to use as your main channel. Tell me when done. After user confirms, start the app briefly to capture the message: ```bash timeout 10 npm run dev || true ``` Then find the JID from the database: ```bash # For personal chat (ends with @s.whatsapp.net) sqlite3 store/messages.db "SELECT DISTINCT chat_jid FROM messages WHERE chat_jid LIKE '%@s.whatsapp.net' ORDER BY timestamp DESC LIMIT 5" # For group (ends with @g.us) sqlite3 store/messages.db "SELECT DISTINCT chat_jid FROM messages WHERE chat_jid LIKE '%@g.us' ORDER BY timestamp DESC LIMIT 5" ``` Create/update `data/registered_groups.json` using the JID from above and the assistant name from step 5: ```json { "JID_HERE": { "name": "main", "folder": "main", "trigger": "@ASSISTANT_NAME", "added_at": "CURRENT_ISO_TIMESTAMP" } } ``` Ensure the groups folder exists: ```bash mkdir -p groups/main/logs ``` ## 8. Configure External Directory Access (Mount Allowlist) Ask the user: > Do you want the agent to be able to access any directories **outside** the NanoClaw project? > > Examples: Git repositories, project folders, documents you want Claude to work on. > > **Note:** This is optional. Without configuration, agents can only access their own group folders. If **no**, create an empty allowlist to make this explicit: ```bash mkdir -p ~/.config/nanoclaw cat > ~/.config/nanoclaw/mount-allowlist.json << 'EOF' { "allowedRoots": [], "blockedPatterns": [], "nonMainReadOnly": true } EOF echo "Mount allowlist created - no external directories allowed" ``` Skip to the next step. If **yes**, ask follow-up questions: ### 8a. Collect Directory Paths Ask the user: > Which directories do you want to allow access to? > > You can specify: > - A parent folder like `~/projects` (allows access to anything inside) > - Specific paths like `~/repos/my-app` > > List them one per line, or give me a comma-separated list. For each directory they provide, ask: > Should `[directory]` be **read-write** (agents can modify files) or **read-only**? > > Read-write is needed for: code changes, creating files, git commits > Read-only is safer for: reference docs, config examples, templates ### 8b. Configure Non-Main Group Access Ask the user: > Should **non-main groups** (other WhatsApp chats you add later) be restricted to **read-only** access even if read-write is allowed for the directory? > > Recommended: **Yes** - this prevents other groups from modifying files even if you grant them access to a directory. ### 8c. Create the Allowlist Create the allowlist file based on their answers: ```bash mkdir -p ~/.config/nanoclaw ``` Then write the JSON file. Example for a user who wants `~/projects` (read-write) and `~/docs` (read-only) with non-main read-only: ```bash cat > ~/.config/nanoclaw/mount-allowlist.json << 'EOF' { "allowedRoots": [ { "path": "~/projects", "allowReadWrite": true, "description": "Development projects" }, { "path": "~/docs", "allowReadWrite": false, "description": "Reference documents" } ], "blockedPatterns": [], "nonMainReadOnly": true } EOF ``` Verify the file: ```bash cat ~/.config/nanoclaw/mount-allowlist.json ``` Tell the user: > Mount allowlist configured. The following directories are now accessible: > - `~/projects` (read-write) > - `~/docs` (read-only) > > **Security notes:** > - Sensitive paths (`.ssh`, `.gnupg`, `.aws`, credentials) are always blocked > - This config file is stored outside the project, so agents cannot modify it > - Changes require restarting the NanoClaw service > > To grant a group access to a directory, add it to their config in `data/registered_groups.json`: > ```json > "containerConfig": { > "additionalMounts": [ > { "hostPath": "~/projects/my-app", "containerPath": "my-app", "readonly": false } > ] > } > ``` ## 9. Gmail Authentication (Optional) Ask the user: > Do you want to enable Gmail integration for reading/sending emails? > > **Note:** This requires setting up Google Cloud Platform OAuth credentials, which involves: > 1. Creating a GCP project > 2. Enabling the Gmail API > 3. Creating OAuth 2.0 credentials > 4. Downloading a credentials file > > This takes about 5-10 minutes. Skip if you don't need email integration. If yes, guide them through the prerequisites: 1. Go to https://console.cloud.google.com 2. Create a new project (or use an existing one) 3. Enable the Gmail API (APIs & Services → Enable APIs → search "Gmail API") 4. Create OAuth 2.0 credentials (APIs & Services → Credentials → Create Credentials → OAuth client ID → Desktop app) 5. Download the JSON file and save to `~/.gmail-mcp/gcp-oauth.keys.json` Then run: ```bash npx -y @gongrzhe/server-gmail-autoauth-mcp ``` This will open a browser for OAuth consent. After authorization, credentials are cached. ## 10. Configure launchd Service Get the actual paths: ```bash which node pwd ``` Create the plist file at `~/Library/LaunchAgents/com.nanoclaw.plist`: ```xml Label com.nanoclaw ProgramArguments NODE_PATH_HERE PROJECT_PATH_HERE/dist/index.js WorkingDirectory PROJECT_PATH_HERE RunAtLoad KeepAlive EnvironmentVariables PATH /usr/local/bin:/usr/bin:/bin:HOME_PATH_HERE/.local/bin HOME HOME_PATH_HERE StandardOutPath PROJECT_PATH_HERE/logs/nanoclaw.log StandardErrorPath PROJECT_PATH_HERE/logs/nanoclaw.error.log ``` Replace the placeholders with actual paths from the commands above. Build and start the service: ```bash npm run build mkdir -p logs launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist ``` Verify it's running: ```bash launchctl list | grep nanoclaw ``` ## 11. Test Tell the user (using the assistant name they configured): > Send `@ASSISTANT_NAME hello` in your registered chat. Check the logs: ```bash tail -f logs/nanoclaw.log ``` The user should receive a response in WhatsApp. ## Troubleshooting **Service not starting**: Check `logs/nanoclaw.error.log` **Container agent fails with "Claude Code process exited with code 1"**: - Ensure Apple Container is running: `container system start` - Check container logs: `cat groups/main/logs/container-*.log | tail -50` **No response to messages**: - Verify the trigger pattern matches (e.g., `@AssistantName` at start of message) - Check that the chat JID is in `data/registered_groups.json` - Check `logs/nanoclaw.log` for errors **WhatsApp disconnected**: - The service will show a macOS notification - Run `npm run auth` to re-authenticate - Restart the service: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` **Unload service**: ```bash launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist ```