refactor: remove legacy container and non-discord remnants
This commit is contained in:
@@ -1,349 +1,91 @@
|
||||
---
|
||||
name: debug
|
||||
description: Debug container agent issues. Use when things aren't working, container fails, authentication problems, or to understand how the container system works. Covers logs, environment variables, mounts, and common issues.
|
||||
description: Debug NanoClaw runtime issues on the current Discord-only, host-process architecture.
|
||||
---
|
||||
|
||||
# NanoClaw Container Debugging
|
||||
# NanoClaw Debugging
|
||||
|
||||
This guide covers debugging the containerized agent execution system.
|
||||
현재 기준 NanoClaw는 디스코드 전용이고, 에이전트는 컨테이너가 아니라 호스트 프로세스로 실행됩니다. 디버깅은 `채널`, `서비스`, `러너`, `DB 등록`, `자격 증명` 순서로 좁혀갑니다.
|
||||
|
||||
## Architecture Overview
|
||||
## 빠른 점검 순서
|
||||
|
||||
```
|
||||
Host (macOS) Container (Linux VM)
|
||||
─────────────────────────────────────────────────────────────
|
||||
src/container-runner.ts container/agent-runner/
|
||||
│ │
|
||||
│ spawns container │ runs Claude Agent SDK
|
||||
│ with volume mounts │ with MCP servers
|
||||
│ │
|
||||
├── data/env/env ──────────────> /workspace/env-dir/env
|
||||
├── groups/{folder} ───────────> /workspace/group
|
||||
├── data/ipc/{folder} ────────> /workspace/ipc
|
||||
├── data/sessions/{folder}/.claude/ ──> /home/node/.claude/ (isolated per-group)
|
||||
└── (main only) project root ──> /workspace/project
|
||||
```
|
||||
1. 타입과 테스트부터 확인
|
||||
```bash
|
||||
npm run typecheck
|
||||
npm test
|
||||
```
|
||||
2. 서비스 상태 확인
|
||||
```bash
|
||||
npm run setup -- --step verify
|
||||
```
|
||||
3. 런타임 로그 확인
|
||||
```bash
|
||||
tail -f logs/nanoclaw.log
|
||||
tail -f logs/nanoclaw.error.log
|
||||
ls -t groups/*/logs/agent-*.log | head
|
||||
```
|
||||
4. 러너 빌드 확인
|
||||
```bash
|
||||
npm run build:runners
|
||||
```
|
||||
|
||||
**Important:** The container runs as user `node` with `HOME=/home/node`. Session files must be mounted to `/home/node/.claude/` (not `/root/.claude/`) for session resumption to work.
|
||||
## 핵심 파일
|
||||
|
||||
## Log Locations
|
||||
- `src/index.ts` - 메시지 루프, 세션 명령, 라우팅
|
||||
- `src/channels/discord.ts` - 디스코드 수신/송신, 멘션, 첨부파일, 음성 전사
|
||||
- `src/agent-runner.ts` - 러너 실행, 환경 변수 전달, 워크디렉터리 처리
|
||||
- `runners/agent-runner/src/index.ts` - Claude Code 러너
|
||||
- `runners/codex-runner/src/index.ts` - Codex 러너
|
||||
- `src/db.ts` - 등록 그룹, 세션, 스케줄 저장
|
||||
- `setup/register.ts` - 디스코드 채널 등록
|
||||
- `setup/verify.ts` - 설치 상태 검증
|
||||
|
||||
| Log | Location | Content |
|
||||
|-----|----------|---------|
|
||||
| **Main app logs** | `logs/nanoclaw.log` | Host-side WhatsApp, routing, container spawning |
|
||||
| **Main app errors** | `logs/nanoclaw.error.log` | Host-side errors |
|
||||
| **Container run logs** | `groups/{folder}/logs/container-*.log` | Per-run: input, mounts, stderr, stdout |
|
||||
| **Claude sessions** | `~/.claude/projects/` | Claude Code session history |
|
||||
## 자주 보는 문제
|
||||
|
||||
## Enabling Debug Logging
|
||||
|
||||
Set `LOG_LEVEL=debug` for verbose output:
|
||||
### 봇이 아예 말이 없음
|
||||
|
||||
```bash
|
||||
# For development
|
||||
LOG_LEVEL=debug npm run dev
|
||||
|
||||
# For launchd service (macOS), add to plist EnvironmentVariables:
|
||||
<key>LOG_LEVEL</key>
|
||||
<string>debug</string>
|
||||
# For systemd service (Linux), add to unit [Service] section:
|
||||
# Environment=LOG_LEVEL=debug
|
||||
grep -n '^DISCORD_BOT_TOKEN=' .env
|
||||
npm run setup -- --step verify
|
||||
```
|
||||
|
||||
Debug level shows:
|
||||
- Full mount configurations
|
||||
- Container command arguments
|
||||
- Real-time container stderr
|
||||
- `DISCORD_BOT_TOKEN`이 없으면 채널이 안 붙습니다.
|
||||
- `REGISTERED_GROUPS=0`이면 채널 등록이 안 된 상태입니다.
|
||||
- 메인 채널이 아니면 디스코드 멘션이나 트리거 조건을 먼저 확인합니다.
|
||||
|
||||
## Common Issues
|
||||
|
||||
### 1. "Claude Code process exited with code 1"
|
||||
|
||||
**Check the container log file** in `groups/{folder}/logs/container-*.log`
|
||||
|
||||
Common causes:
|
||||
|
||||
#### Missing Authentication
|
||||
```
|
||||
Invalid API key · Please run /login
|
||||
```
|
||||
**Fix:** Ensure `.env` file exists with either OAuth token or API key:
|
||||
```bash
|
||||
cat .env # Should show one of:
|
||||
# CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... (subscription)
|
||||
# ANTHROPIC_API_KEY=sk-ant-api03-... (pay-per-use)
|
||||
```
|
||||
|
||||
#### Root User Restriction
|
||||
```
|
||||
--dangerously-skip-permissions cannot be used with root/sudo privileges
|
||||
```
|
||||
**Fix:** Container must run as non-root user. Check Dockerfile has `USER node`.
|
||||
|
||||
### 2. Environment Variables Not Passing
|
||||
|
||||
**Runtime note:** Environment variables passed via `-e` may be lost when using `-i` (interactive/piped stdin).
|
||||
|
||||
**Workaround:** The system extracts only authentication variables (`CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`) from `.env` and mounts them for sourcing inside the container. Other env vars are not exposed.
|
||||
|
||||
To verify env vars are reaching the container:
|
||||
```bash
|
||||
echo '{}' | docker run -i \
|
||||
-v $(pwd)/data/env:/workspace/env-dir:ro \
|
||||
--entrypoint /bin/bash nanoclaw-agent:latest \
|
||||
-c 'export $(cat /workspace/env-dir/env | xargs); echo "OAuth: ${#CLAUDE_CODE_OAUTH_TOKEN} chars, API: ${#ANTHROPIC_API_KEY} chars"'
|
||||
```
|
||||
|
||||
### 3. Mount Issues
|
||||
|
||||
**Container mount notes:**
|
||||
- Docker supports both `-v` and `--mount` syntax
|
||||
- Use `:ro` suffix for readonly mounts:
|
||||
```bash
|
||||
# Readonly
|
||||
-v /path:/container/path:ro
|
||||
|
||||
# Read-write
|
||||
-v /path:/container/path
|
||||
```
|
||||
|
||||
To check what's mounted inside a container:
|
||||
```bash
|
||||
docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c 'ls -la /workspace/'
|
||||
```
|
||||
|
||||
Expected structure:
|
||||
```
|
||||
/workspace/
|
||||
├── env-dir/env # Environment file (CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY)
|
||||
├── group/ # Current group folder (cwd)
|
||||
├── project/ # Project root (main channel only)
|
||||
├── global/ # Global CLAUDE.md (non-main only)
|
||||
├── ipc/ # Inter-process communication
|
||||
│ ├── messages/ # Outgoing WhatsApp messages
|
||||
│ ├── tasks/ # Scheduled task commands
|
||||
│ ├── current_tasks.json # Read-only: scheduled tasks visible to this group
|
||||
│ └── available_groups.json # Read-only: WhatsApp groups for activation (main only)
|
||||
└── extra/ # Additional custom mounts
|
||||
```
|
||||
|
||||
### 4. Permission Issues
|
||||
|
||||
The container runs as user `node` (uid 1000). Check ownership:
|
||||
```bash
|
||||
docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c '
|
||||
whoami
|
||||
ls -la /workspace/
|
||||
ls -la /app/
|
||||
'
|
||||
```
|
||||
|
||||
All of `/workspace/` and `/app/` should be owned by `node`.
|
||||
|
||||
### 5. Session Not Resuming / "Claude Code process exited with code 1"
|
||||
|
||||
If sessions aren't being resumed (new session ID every time), or Claude Code exits with code 1 when resuming:
|
||||
|
||||
**Root cause:** The SDK looks for sessions at `$HOME/.claude/projects/`. Inside the container, `HOME=/home/node`, so it looks at `/home/node/.claude/projects/`.
|
||||
|
||||
**Check the mount path:**
|
||||
```bash
|
||||
# In container-runner.ts, verify mount is to /home/node/.claude/, NOT /root/.claude/
|
||||
grep -A3 "Claude sessions" src/container-runner.ts
|
||||
```
|
||||
|
||||
**Verify sessions are accessible:**
|
||||
```bash
|
||||
docker run --rm --entrypoint /bin/bash \
|
||||
-v ~/.claude:/home/node/.claude \
|
||||
nanoclaw-agent:latest -c '
|
||||
echo "HOME=$HOME"
|
||||
ls -la $HOME/.claude/projects/ 2>&1 | head -5
|
||||
'
|
||||
```
|
||||
|
||||
**Fix:** Ensure `container-runner.ts` mounts to `/home/node/.claude/`:
|
||||
```typescript
|
||||
mounts.push({
|
||||
hostPath: claudeDir,
|
||||
containerPath: '/home/node/.claude', // NOT /root/.claude
|
||||
readonly: false
|
||||
});
|
||||
```
|
||||
|
||||
### 6. MCP Server Failures
|
||||
|
||||
If an MCP server fails to start, the agent may exit. Check the container logs for MCP initialization errors.
|
||||
|
||||
## Manual Container Testing
|
||||
|
||||
### Test the full agent flow:
|
||||
```bash
|
||||
# Set up env file
|
||||
mkdir -p data/env groups/test
|
||||
cp .env data/env/env
|
||||
|
||||
# Run test query
|
||||
echo '{"prompt":"What is 2+2?","groupFolder":"test","chatJid":"test@g.us","isMain":false}' | \
|
||||
docker run -i \
|
||||
-v $(pwd)/data/env:/workspace/env-dir:ro \
|
||||
-v $(pwd)/groups/test:/workspace/group \
|
||||
-v $(pwd)/data/ipc:/workspace/ipc \
|
||||
nanoclaw-agent:latest
|
||||
```
|
||||
|
||||
### Test Claude Code directly:
|
||||
```bash
|
||||
docker run --rm --entrypoint /bin/bash \
|
||||
-v $(pwd)/data/env:/workspace/env-dir:ro \
|
||||
nanoclaw-agent:latest -c '
|
||||
export $(cat /workspace/env-dir/env | xargs)
|
||||
claude -p "Say hello" --dangerously-skip-permissions --allowedTools ""
|
||||
'
|
||||
```
|
||||
|
||||
### Interactive shell in container:
|
||||
```bash
|
||||
docker run --rm -it --entrypoint /bin/bash nanoclaw-agent:latest
|
||||
```
|
||||
|
||||
## SDK Options Reference
|
||||
|
||||
The agent-runner uses these Claude Agent SDK options:
|
||||
|
||||
```typescript
|
||||
query({
|
||||
prompt: input.prompt,
|
||||
options: {
|
||||
cwd: '/workspace/group',
|
||||
allowedTools: ['Bash', 'Read', 'Write', ...],
|
||||
permissionMode: 'bypassPermissions',
|
||||
allowDangerouslySkipPermissions: true, // Required with bypassPermissions
|
||||
settingSources: ['project'],
|
||||
mcpServers: { ... }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Important:** `allowDangerouslySkipPermissions: true` is required when using `permissionMode: 'bypassPermissions'`. Without it, Claude Code exits with code 1.
|
||||
|
||||
## Rebuilding After Changes
|
||||
### 에이전트가 실행 직후 죽음
|
||||
|
||||
```bash
|
||||
# Rebuild main app
|
||||
npm run build
|
||||
|
||||
# Rebuild container (use --no-cache for clean rebuild)
|
||||
./container/build.sh
|
||||
|
||||
# Or force full rebuild
|
||||
docker builder prune -af
|
||||
./container/build.sh
|
||||
grep -nE '^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY|OPENAI_API_KEY|CODEX_OPENAI_API_KEY)=' .env
|
||||
ls -t groups/*/logs/agent-*.log | head -3
|
||||
```
|
||||
|
||||
## Checking Container Image
|
||||
- Claude 계열은 `CLAUDE_CODE_OAUTH_TOKEN` 또는 `ANTHROPIC_API_KEY`가 필요합니다.
|
||||
- Codex 계열은 `OPENAI_API_KEY` 또는 `CODEX_OPENAI_API_KEY`가 필요합니다.
|
||||
- 러너 로그에 인증 실패나 CLI 실행 오류가 바로 찍힙니다.
|
||||
|
||||
### 음성 전사가 안 됨
|
||||
|
||||
```bash
|
||||
# List images
|
||||
docker images
|
||||
|
||||
# Check what's in the image
|
||||
docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c '
|
||||
echo "=== Node version ==="
|
||||
node --version
|
||||
|
||||
echo "=== Claude Code version ==="
|
||||
claude --version
|
||||
|
||||
echo "=== Installed packages ==="
|
||||
ls /app/node_modules/
|
||||
'
|
||||
grep -nE '^(GROQ_API_KEY|OPENAI_API_KEY)=' .env
|
||||
tail -f logs/nanoclaw.log | grep -iE 'transcri|audio|whisper|groq'
|
||||
```
|
||||
|
||||
## Session Persistence
|
||||
- 기본 우선순위는 Groq Whisper, 없으면 OpenAI Whisper fallback입니다.
|
||||
- 키가 없으면 디스코드 채널은 음성 첨부를 텍스트로 확장하지 못합니다.
|
||||
|
||||
Claude sessions are stored per-group in `data/sessions/{group}/.claude/` for security isolation. Each group has its own session directory, preventing cross-group access to conversation history.
|
||||
|
||||
**Critical:** The mount path must match the container user's HOME directory:
|
||||
- Container user: `node`
|
||||
- Container HOME: `/home/node`
|
||||
- Mount target: `/home/node/.claude/` (NOT `/root/.claude/`)
|
||||
|
||||
To clear sessions:
|
||||
### 등록은 되어 있는데 응답이 이상함
|
||||
|
||||
```bash
|
||||
# Clear all sessions for all groups
|
||||
rm -rf data/sessions/
|
||||
|
||||
# Clear sessions for a specific group
|
||||
rm -rf data/sessions/{groupFolder}/.claude/
|
||||
|
||||
# Also clear the session ID from NanoClaw's tracking (stored in SQLite)
|
||||
sqlite3 store/messages.db "DELETE FROM sessions WHERE group_folder = '{groupFolder}'"
|
||||
sqlite3 store/messages.db "select jid, folder, requires_trigger, is_main, agent_type from registered_groups;"
|
||||
```
|
||||
|
||||
To verify session resumption is working, check the logs for the same session ID across messages:
|
||||
```bash
|
||||
grep "Session initialized" logs/nanoclaw.log | tail -5
|
||||
# Should show the SAME session ID for consecutive messages in the same group
|
||||
```
|
||||
- `jid`는 `dc:<channel_id>` 형식이어야 합니다.
|
||||
- `folder`는 `discord_main` 또는 `discord_<name>` 형태를 유지합니다.
|
||||
- 세션 명령 문제면 `src/session-commands.ts`와 `src/index.ts` 호출부를 같이 봅니다.
|
||||
|
||||
## IPC Debugging
|
||||
## 원칙
|
||||
|
||||
The container communicates back to the host via files in `/workspace/ipc/`:
|
||||
|
||||
```bash
|
||||
# Check pending messages
|
||||
ls -la data/ipc/messages/
|
||||
|
||||
# Check pending task operations
|
||||
ls -la data/ipc/tasks/
|
||||
|
||||
# Read a specific IPC file
|
||||
cat data/ipc/messages/*.json
|
||||
|
||||
# Check available groups (main channel only)
|
||||
cat data/ipc/main/available_groups.json
|
||||
|
||||
# Check current tasks snapshot
|
||||
cat data/ipc/{groupFolder}/current_tasks.json
|
||||
```
|
||||
|
||||
**IPC file types:**
|
||||
- `messages/*.json` - Agent writes: outgoing WhatsApp messages
|
||||
- `tasks/*.json` - Agent writes: task operations (schedule, pause, resume, cancel, refresh_groups)
|
||||
- `current_tasks.json` - Host writes: read-only snapshot of scheduled tasks
|
||||
- `available_groups.json` - Host writes: read-only list of WhatsApp groups (main only)
|
||||
|
||||
## Quick Diagnostic Script
|
||||
|
||||
Run this to check common issues:
|
||||
|
||||
```bash
|
||||
echo "=== Checking NanoClaw Container Setup ==="
|
||||
|
||||
echo -e "\n1. Authentication configured?"
|
||||
[ -f .env ] && (grep -q "CLAUDE_CODE_OAUTH_TOKEN=sk-" .env || grep -q "ANTHROPIC_API_KEY=sk-" .env) && echo "OK" || echo "MISSING - add CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY to .env"
|
||||
|
||||
echo -e "\n2. Env file copied for container?"
|
||||
[ -f data/env/env ] && echo "OK" || echo "MISSING - will be created on first run"
|
||||
|
||||
echo -e "\n3. Container runtime running?"
|
||||
docker info &>/dev/null && echo "OK" || echo "NOT RUNNING - start Docker Desktop (macOS) or sudo systemctl start docker (Linux)"
|
||||
|
||||
echo -e "\n4. Container image exists?"
|
||||
echo '{}' | docker run -i --entrypoint /bin/echo nanoclaw-agent:latest "OK" 2>/dev/null || echo "MISSING - run ./container/build.sh"
|
||||
|
||||
echo -e "\n5. Session mount path correct?"
|
||||
grep -q "/home/node/.claude" src/container-runner.ts 2>/dev/null && echo "OK" || echo "WRONG - should mount to /home/node/.claude/, not /root/.claude/"
|
||||
|
||||
echo -e "\n6. Groups directory?"
|
||||
ls -la groups/ 2>/dev/null || echo "MISSING - run setup"
|
||||
|
||||
echo -e "\n7. Recent container logs?"
|
||||
ls -t groups/*/logs/container-*.log 2>/dev/null | head -3 || echo "No container logs yet"
|
||||
|
||||
echo -e "\n8. Session continuity working?"
|
||||
SESSIONS=$(grep "Session initialized" logs/nanoclaw.log 2>/dev/null | tail -5 | awk '{print $NF}' | sort -u | wc -l)
|
||||
[ "$SESSIONS" -le 2 ] && echo "OK (recent sessions reusing IDs)" || echo "CHECK - multiple different session IDs, may indicate resumption issues"
|
||||
```
|
||||
- 채널 문제와 에이전트 문제를 섞지 말고 분리해서 봅니다.
|
||||
- `.env`, DB 등록, 서비스, 러너 빌드 중 하나라도 틀리면 상위 증상이 비슷하게 보입니다.
|
||||
- 컨테이너 전제 문서는 무시하고 `runners/*`와 `setup/*` 기준으로 확인합니다.
|
||||
|
||||
Reference in New Issue
Block a user