improve setup flow: streamline steps, add pre-approval, new manage-mounts skill

- Disable sandbox by default in project settings
- Setup: remove Apple Container option (Docker only), single channel selection
  with plain text list, move fork to end, auto-set empty mounts, add command
  pre-approval step, add UTC timezone confirmation, add wait-on-user guidance,
  add 5m timeouts for long steps
- iMessage: improve Full Disk Access UX with Finder open + drag instructions
- Add /manage-mounts skill for post-setup mount configuration
- Enable iMessage channel import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-16 11:26:20 +03:00
parent 03684d33e2
commit 57c9bfc670
6 changed files with 159 additions and 129 deletions
+14 -4
View File
@@ -31,11 +31,21 @@ npm run build
### Local Mode (macOS)
Requirements: macOS with Full Disk Access granted to your terminal/Node.js process.
Requirements: macOS with Full Disk Access granted to the Node.js binary.
1. Go to **System Settings** > **Privacy & Security** > **Full Disk Access**
2. Add your terminal app (Terminal, iTerm2, etc.) or the Node.js binary
3. The adapter reads directly from the iMessage database on disk
The Node binary path is buried deep (e.g. `~/.nvm/versions/node/v22.x.x/bin/node`). To make it easy, open the folder in Finder so the user can drag the file into System Settings:
```bash
open "$(dirname "$(which node)")"
```
Then tell the user:
1. Open **System Settings** > **Privacy & Security** > **Full Disk Access**
2. Click **+**, then drag the `node` file from the Finder window that just opened
3. Toggle it on
Stop and wait for the user to confirm before continuing.
### Remote Mode (Photon API)
+47
View File
@@ -0,0 +1,47 @@
---
name: manage-mounts
description: Configure which host directories agent containers can access. View, add, or remove mount allowlist entries. Triggers on "mounts", "mount allowlist", "agent access to directories", "container mounts".
---
# Manage Mounts
Configure which host directories NanoClaw agent containers can access. The mount allowlist lives at `~/.config/nanoclaw/mount-allowlist.json`.
## Show Current Config
```bash
cat ~/.config/nanoclaw/mount-allowlist.json 2>/dev/null || echo "No mount allowlist configured"
```
Show the current config to the user in a readable format: which directories are allowed, whether non-main agents are read-only.
## Add Directories
Ask which directories the user wants agents to access. For each path:
- Validate the path exists
- Ask if it should be read-only for non-main agents (default: yes)
Build the JSON config and write it:
```bash
npx tsx setup/index.ts --step mounts --force -- --json '{"allowedRoots":[{"path":"/path/to/dir","readOnly":false}],"blockedPatterns":[],"nonMainReadOnly":true}'
```
Use `--force` to overwrite the existing config.
## Remove Directories
Read the current config, show it, ask which entry to remove, write the updated config.
## Reset to Empty
```bash
npx tsx setup/index.ts --step mounts --force -- --empty
```
## After Changes
Restart the service so containers pick up the new config:
- macOS: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`
- Linux: `systemctl --user restart nanoclaw`
+70 -118
View File
@@ -9,46 +9,30 @@ Run setup steps automatically. Only pause when user action is required (channel
**Principle:** When something is broken or missing, fix it. Don't tell the user to go fix it themselves unless it genuinely requires their manual action (e.g. authenticating a channel, pasting a secret token). If a dependency is missing, install it. If a service won't start, diagnose and repair. Ask the user for permission when needed, then do the work.
**UX Note:** Use `AskUserQuestion` for multiple-choice questions only (e.g. "Docker or Apple Container?", "which channels?"). Do NOT use it when free-text input is needed (e.g. phone numbers, tokens, paths) — just ask the question in plain text and wait for the user's reply.
**UX Note:** Use `AskUserQuestion` for multiple-choice questions only (e.g. "which credential method?"). Do NOT use it when free-text input is needed (e.g. phone numbers, tokens, paths) — just ask the question in plain text and wait for the user's reply.
## 0. Git & Fork Setup
**Timeouts:** Use 5m timeouts for install and build steps.
Check the git remote configuration to ensure the user has a fork and upstream is configured.
**Waiting on user:** When the user needs to do something (change a setting, get a token, open a browser, etc.), stop and wait. Give clear instructions, then say "Let me know when done or if you need help." Do NOT continue to the next step. If they ask for help, give more detail, ask where they got stuck, and try to assist (e.g. open a Finder window, construct a command for them).
Run:
- `git remote -v`
## Pre-step: Approve Setup Commands
**Case A — `origin` points to `qwibitai/nanoclaw` (user cloned directly):**
Tell the user:
The user cloned instead of forking. AskUserQuestion: "You cloned NanoClaw directly. We recommend forking so you can push your customizations. Would you like to set up a fork?"
- Fork now (recommended) — walk them through it
- Continue without fork — they'll only have local changes
> Setup runs many shell commands (installing packages, building containers, configuring services). Would you like to pre-approve these so you don't have to confirm each one?
If they agree, read `.claude/skills/setup/setup-permissions.json` and use the Skill tool to invoke `update-config` with: "Add these permissions to the project settings allow list: <paste the JSON array>".
If they decline, continue — they'll approve commands individually.
## 0. Git Upstream
Ensure `upstream` remote points to `qwibitai/nanoclaw`. If missing, add it silently:
If fork: instruct the user to fork `qwibitai/nanoclaw` on GitHub (they need to do this in their browser), then ask them for their GitHub username. Run:
```bash
git remote rename origin upstream
git remote add origin https://github.com/<their-username>/nanoclaw.git
git push --force origin main
git remote -v
git remote add upstream https://github.com/qwibitai/nanoclaw.git 2>/dev/null || true
```
Verify with `git remote -v`.
If continue without fork: add upstream so they can still pull updates:
```bash
git remote add upstream https://github.com/qwibitai/nanoclaw.git
```
**Case B — `origin` points to user's fork, no `upstream` remote:**
Add upstream:
```bash
git remote add upstream https://github.com/qwibitai/nanoclaw.git
```
**Case C — both `origin` (user's fork) and `upstream` (qwibitai) exist:**
Already configured. Continue.
**Verify:** `git remote -v` should show `origin` → user's repo, `upstream``qwibitai/nanoclaw.git`.
## 1. Bootstrap (Node.js + Dependencies)
@@ -68,7 +52,7 @@ Run `npx tsx setup/index.ts --step environment` and parse the status block.
- If HAS_AUTH=true → WhatsApp is already configured, note for step 5
- If HAS_REGISTERED_GROUPS=true → note existing config, offer to skip or reconfigure
- Record APPLE_CONTAINER and DOCKER values for step 3
- Record DOCKER value for step 3
### OpenClaw Migration Detection
@@ -91,58 +75,32 @@ If "Migrate now": invoke `/migrate-from-openclaw`, then return here and continue
Run `npx tsx setup/index.ts --step timezone` and parse the status block.
- If NEEDS_USER_INPUT=true → The system timezone could not be autodetected (e.g. POSIX-style TZ like `IST-2`). AskUserQuestion: "What is your timezone?" with common options (America/New_York, Europe/London, Asia/Jerusalem, Asia/Tokyo) and an "Other" escape. Then re-run: `npx tsx setup/index.ts --step timezone -- --tz <their-answer>`.
- If STATUS=success and RESOLVED_TZ is `UTC` or `Etc/UTC` → confirm with the user: "Your system timezone is UTC — is that correct, or are you on a remote server?" If wrong, ask for their actual timezone and re-run with `--tz`.
- If STATUS=success → Timezone is configured. Note RESOLVED_TZ for reference.
## 3. Container Runtime
## 3. Container Runtime (Docker)
### 3a. Choose runtime
### 3a. Install Docker
Check the preflight results for `APPLE_CONTAINER` and `DOCKER`, and the PLATFORM from step 1.
- PLATFORM=linux → Docker (only option)
- PLATFORM=macos + APPLE_CONTAINER=installed → AskUserQuestion with two options:
1. **Docker (recommended)** — description: "Cross-platform, better credential management, well-tested."
2. **Apple Container (experimental)** — description: "Native macOS runtime. Requires advanced setup."
If Apple Container, run `/convert-to-apple-container` now, then skip to 3c.
- PLATFORM=macos + APPLE_CONTAINER=not_found → Docker
### 3a-docker. Install Docker
- DOCKER=running → continue to 4b
- DOCKER=running → continue to step 4
- DOCKER=installed_not_running → start Docker: `open -a Docker` (macOS) or `sudo systemctl start docker` (Linux). Wait 15s, re-check with `docker info`.
- DOCKER=not_found → Use `AskUserQuestion: Docker is required for running agents. Would you like me to install it?` If confirmed:
- macOS: install via `brew install --cask docker`, then `open -a Docker` and wait for it to start. If brew not available, direct to Docker Desktop download at https://docker.com/products/docker-desktop
- Linux: install with `curl -fsSL https://get.docker.com | sh && sudo usermod -aG docker $USER`. Note: user may need to log out/in for group membership.
### 3b. Apple Container conversion gate (if needed)
### 3b. Build and test
**If the chosen runtime is Apple Container**, you MUST check whether the source code has already been converted from Docker to Apple Container. Do NOT skip this step. Run:
```bash
grep -q "CONTAINER_RUNTIME_BIN = 'container'" src/container-runtime.ts && echo "ALREADY_CONVERTED" || echo "NEEDS_CONVERSION"
```
**If NEEDS_CONVERSION**, the source code still uses Docker as the runtime. You MUST run the `/convert-to-apple-container` skill NOW, before proceeding to the build step.
**If ALREADY_CONVERTED**, the code already uses Apple Container. Continue to 3c.
**If the chosen runtime is Docker**, no conversion is needed. Continue to 3c.
### 3c. Build and test
Run `npx tsx setup/index.ts --step container -- --runtime <chosen>` and parse the status block.
Run `npx tsx setup/index.ts --step container -- --runtime docker` and parse the status block.
**If BUILD_OK=false:** Read `logs/setup.log` tail for the build error.
- Cache issue (stale layers): `docker builder prune -f` (Docker) or `container builder stop && container builder rm && container builder start` (Apple Container). Retry.
- Cache issue (stale layers): `docker builder prune -f`. Retry.
- Dockerfile syntax or missing files: diagnose from the log and fix, then retry.
**If TEST_OK=false but BUILD_OK=true:** The image built but won't run. Check logs — common cause is runtime not fully started. Wait a moment and retry the test.
## 4. Credential System
The credential system depends on the container runtime chosen in step 3.
### 4a. Docker → OneCLI
### 4a. OneCLI
Install OneCLI and its CLI tool:
@@ -214,51 +172,29 @@ Ask them to let you know when done.
**After user confirms:** verify with `onecli secrets list` that an Anthropic secret exists. If not, ask again.
### 4b. Apple Container → Native Credential Proxy
Apple Container is not compatible with OneCLI. The credential proxy code is already included in the apple-container branch — do NOT invoke `/use-native-credential-proxy` (it would conflict with already-applied code).
Instead, just configure the credentials in `.env`:
AskUserQuestion: Do you want to use your **Claude subscription** (Pro/Max) or an **Anthropic API key**?
1. **Claude subscription (Pro/Max)** — description: "Uses your existing Claude Pro or Max subscription. Run `claude setup-token` in another terminal to get your token."
2. **Anthropic API key** — description: "Pay-per-use API key from console.anthropic.com."
For subscription: tell the user to run `claude setup-token` in another terminal. Stop and wait for the user to confirm they have completed this step successfully before proceeding.
Once confirmed, add the token to `.env`:
```bash
echo 'CLAUDE_CODE_OAUTH_TOKEN=<their-token>' >> .env
```
For API key: add to `.env`:
```bash
echo 'ANTHROPIC_API_KEY=<their-key>' >> .env
```
Verify the proxy starts: `npm run dev` should show "Credential proxy listening" in the logs.
## 5. Set Up Channels
AskUserQuestion (multiSelect): Which messaging channels do you want to enable?
- Discord (bot token + public key)
- Slack (bot token + signing secret)
- Telegram (bot token from @BotFather)
- GitHub (PR/issue comment threads)
- Linear (issue comment threads)
- Microsoft Teams (Azure Bot)
- Google Chat (service account)
- WhatsApp Cloud API (Meta Business API)
- WhatsApp Baileys (QR code / pairing code)
- Resend (email)
- Matrix (any homeserver)
- Webex (bot token)
- iMessage (macOS local or Photon API)
Show the full list of available channels in plain text (do NOT use AskUserQuestion — it limits to 4 options). Ask which one they want to start with. They can add more later with `/customize`.
**Delegate to each selected channel's own skill.** Each channel skill handles its own package installation, authentication, registration, and configuration. This avoids duplicating channel-specific logic.
Channels where the agent gets its own identity (name and avatar) are marked as recommended.
For each selected channel, invoke its skill:
1. Discord *(recommended — agent gets own identity)*
2. Slack *(recommended — agent gets own identity)*
3. Telegram *(recommended — agent gets own identity)*
4. Microsoft Teams *(recommended — agent gets own identity)*
5. Webex *(recommended — agent gets own identity)*
6. WhatsApp
7. WhatsApp Cloud API
8. iMessage
9. GitHub
10. Linear
11. Google Chat
12. Resend (email)
13. Matrix
**Delegate to the selected channel's skill.** Each channel skill handles its own package installation, authentication, registration, and configuration.
Invoke the matching skill:
- **Discord:** Invoke `/add-discord-v2`
- **Slack:** Invoke `/add-slack-v2`
@@ -274,13 +210,13 @@ For each selected channel, invoke its skill:
- **Webex:** Invoke `/add-webex-v2`
- **iMessage:** Invoke `/add-imessage-v2`
Each skill will:
The skill will:
1. Install the Chat SDK adapter package
2. Uncomment the channel import in `src/channels/index.ts`
3. Collect credentials/tokens and write to `.env`
4. Build and verify
**After all channel skills complete**, install dependencies and rebuild — channel merges may introduce new packages:
**After the channel skill completes**, install dependencies and rebuild — channel merges may introduce new packages:
```bash
npm install && npm run build
@@ -290,10 +226,11 @@ If the build fails, read the error output and fix it (usually a missing dependen
## 6. Mount Allowlist
AskUserQuestion: Agent access to external directories?
Set empty mount allowlist (agents only access their own workspace). Users can configure mounts later with `/manage-mounts`.
**No:** `npx tsx setup/index.ts --step mounts -- --empty`
**Yes:** Collect paths/permissions. `npx tsx setup/index.ts --step mounts -- --json '{"allowedRoots":[...],"blockedPatterns":[],"nonMainReadOnly":true}'`
```bash
npx tsx setup/index.ts --step mounts -- --empty
```
## 7. Start Service
@@ -354,18 +291,16 @@ Run `npx tsx setup/index.ts --step verify` and parse the status block.
**If STATUS=failed, fix each:**
- SERVICE=stopped → `npm run build`, then restart: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux) or `bash start-nanoclaw.sh` (WSL nohup)
- SERVICE=not_found → re-run step 7
- CREDENTIALS=missing → re-run step 4 (Docker: check `onecli secrets list`; Apple Container: check `.env` for credentials)
- CREDENTIALS=missing → re-run step 4 (check `onecli secrets list`)
- CHANNEL_AUTH shows `not_found` for any channel → re-invoke that channel's skill (e.g. `/add-telegram`)
- REGISTERED_GROUPS=0 → re-invoke `/manage-channels` from step 7a
- MOUNT_ALLOWLIST=missing → `npx tsx setup/index.ts --step mounts -- --empty`
Tell user to test: send a message in their registered chat. Show: `tail -f logs/nanoclaw.log`
## Troubleshooting
**Service not starting:** Check `logs/nanoclaw.error.log`. Common: wrong Node path (re-run step 7), credential system not running (Docker: check `curl ${ONECLI_URL}/api/health`; Apple Container: check `.env` credentials), missing channel credentials (re-invoke channel skill).
**Service not starting:** Check `logs/nanoclaw.error.log`. Common: wrong Node path (re-run step 7), credential system not running (check `curl ${ONECLI_URL}/api/health`), missing channel credentials (re-invoke channel skill).
**Container agent fails ("Claude Code process exited with code 1"):** Ensure the container runtime is running — `open -a Docker` (macOS Docker), `container system start` (Apple Container), or `sudo systemctl start docker` (Linux). Check container logs in `groups/main/logs/container-*.log`.
**Container agent fails ("Claude Code process exited with code 1"):** Ensure Docker is running — `open -a Docker` (macOS) or `sudo systemctl start docker` (Linux). Check container logs in `groups/main/logs/container-*.log`.
**No response to messages:** Check trigger pattern. Main channel doesn't need prefix. Check DB: `npx tsx setup/index.ts --step verify`. Check `logs/nanoclaw.log`.
@@ -378,3 +313,20 @@ Tell user to test: send a message in their registered chat. Show: `tail -f logs/
1. Use the Read tool to read `.claude/skills/setup/diagnostics.md`.
2. Follow every step in that file before completing setup.
## 10. Fork Setup
Only run this after the user has confirmed 2-way messaging works.
Check `git remote -v`. If `origin` points to `qwibitai/nanoclaw` (not a fork), ask in plain text:
> We recommend forking NanoClaw so you can push your customizations and pull updates easily. Would you like to set up a fork now?
If yes: instruct the user to fork `qwibitai/nanoclaw` on GitHub (they need to do this in their browser), then ask for their GitHub username. Run:
```bash
git remote rename origin upstream
git remote add origin https://github.com/<their-username>/nanoclaw.git
git push --force origin main
```
If no: skip — upstream is already configured from step 0.
@@ -0,0 +1,26 @@
[
"Bash(bash setup.sh*)",
"Bash(git remote *)",
"Bash(npx tsx setup/index.ts*)",
"Bash(npx tsx scripts/init-first-agent.ts*)",
"Bash(npm install chat-adapter-*)",
"Bash(npm ci*)",
"Bash(npm run build*)",
"Bash(curl -fsSL onecli.sh*)",
"Bash(onecli *)",
"Bash(grep -q *)",
"Bash(echo *>> .env)",
"Bash(ls *)",
"Bash(cat ~/.config/nanoclaw/*)",
"Bash(tail *logs/*)",
"Bash(launchctl *nanoclaw*)",
"Bash(sqlite3 data/*)",
"Bash(docker info*)",
"Bash(docker logs *)",
"Bash(mkdir -p *)",
"Bash(cp .env *)",
"Bash(xattr *)",
"Bash(find ~/.npm *)",
"Bash(which onecli*)",
"Bash(./container/build.sh*)"
]