@rozek/nanoclaw 1.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/.claude/settings.json +1 -0
  2. package/.claude/skills/add-compact/SKILL.md +135 -0
  3. package/.claude/skills/add-discord/SKILL.md +203 -0
  4. package/.claude/skills/add-gmail/SKILL.md +220 -0
  5. package/.claude/skills/add-image-vision/SKILL.md +94 -0
  6. package/.claude/skills/add-ollama-tool/SKILL.md +153 -0
  7. package/.claude/skills/add-parallel/SKILL.md +290 -0
  8. package/.claude/skills/add-pdf-reader/SKILL.md +104 -0
  9. package/.claude/skills/add-reactions/SKILL.md +117 -0
  10. package/.claude/skills/add-slack/SKILL.md +207 -0
  11. package/.claude/skills/add-telegram/SKILL.md +222 -0
  12. package/.claude/skills/add-telegram-swarm/SKILL.md +384 -0
  13. package/.claude/skills/add-voice-transcription/SKILL.md +148 -0
  14. package/.claude/skills/add-whatsapp/SKILL.md +372 -0
  15. package/.claude/skills/convert-to-apple-container/SKILL.md +175 -0
  16. package/.claude/skills/customize/SKILL.md +110 -0
  17. package/.claude/skills/debug/SKILL.md +349 -0
  18. package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
  19. package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
  20. package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
  21. package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
  22. package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
  23. package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
  24. package/.claude/skills/setup/SKILL.md +218 -0
  25. package/.claude/skills/update-nanoclaw/SKILL.md +235 -0
  26. package/.claude/skills/update-skills/SKILL.md +130 -0
  27. package/.claude/skills/use-local-whisper/SKILL.md +152 -0
  28. package/.claude/skills/x-integration/SKILL.md +417 -0
  29. package/.claude/skills/x-integration/agent.ts +243 -0
  30. package/.claude/skills/x-integration/host.ts +159 -0
  31. package/.claude/skills/x-integration/lib/browser.ts +148 -0
  32. package/.claude/skills/x-integration/lib/config.ts +62 -0
  33. package/.claude/skills/x-integration/scripts/like.ts +56 -0
  34. package/.claude/skills/x-integration/scripts/post.ts +66 -0
  35. package/.claude/skills/x-integration/scripts/quote.ts +80 -0
  36. package/.claude/skills/x-integration/scripts/reply.ts +74 -0
  37. package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
  38. package/.claude/skills/x-integration/scripts/setup.ts +87 -0
  39. package/.env.example +1 -0
  40. package/.github/CODEOWNERS +10 -0
  41. package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  42. package/.github/workflows/bump-version.yml +32 -0
  43. package/.github/workflows/ci.yml +25 -0
  44. package/.github/workflows/merge-forward-skills.yml +160 -0
  45. package/.github/workflows/update-tokens.yml +42 -0
  46. package/.husky/pre-commit +1 -0
  47. package/.mcp.json +3 -0
  48. package/.nvmrc +1 -0
  49. package/.prettierrc +3 -0
  50. package/CHANGELOG.md +8 -0
  51. package/CLAUDE.md +64 -0
  52. package/CONTRIBUTING.md +23 -0
  53. package/CONTRIBUTORS.md +15 -0
  54. package/LICENSE +21 -0
  55. package/NanoClaw_with_Web-Support.md +290 -0
  56. package/README.md +261 -0
  57. package/README_zh.md +200 -0
  58. package/assets/nanoclaw-favicon.png +0 -0
  59. package/assets/nanoclaw-icon.png +0 -0
  60. package/assets/nanoclaw-logo-dark.png +0 -0
  61. package/assets/nanoclaw-logo.png +0 -0
  62. package/assets/nanoclaw-profile.jpeg +0 -0
  63. package/assets/nanoclaw-sales.png +0 -0
  64. package/assets/social-preview.jpg +0 -0
  65. package/config-examples/mount-allowlist.json +25 -0
  66. package/container/Dockerfile +70 -0
  67. package/container/agent-runner/package-lock.json +1524 -0
  68. package/container/agent-runner/package.json +21 -0
  69. package/container/agent-runner/src/index.ts +558 -0
  70. package/container/agent-runner/src/ipc-mcp-stdio.ts +338 -0
  71. package/container/agent-runner/tsconfig.json +15 -0
  72. package/container/build.sh +23 -0
  73. package/container/skills/agent-browser/SKILL.md +159 -0
  74. package/container/skills/capabilities/SKILL.md +100 -0
  75. package/container/skills/status/SKILL.md +104 -0
  76. package/dist/channels/index.d.ts +2 -0
  77. package/dist/channels/index.d.ts.map +1 -0
  78. package/dist/channels/index.js +9 -0
  79. package/dist/channels/index.js.map +1 -0
  80. package/dist/channels/registry.d.ts +13 -0
  81. package/dist/channels/registry.d.ts.map +1 -0
  82. package/dist/channels/registry.js +11 -0
  83. package/dist/channels/registry.js.map +1 -0
  84. package/dist/channels/registry.test.d.ts +2 -0
  85. package/dist/channels/registry.test.d.ts.map +1 -0
  86. package/dist/channels/registry.test.js +32 -0
  87. package/dist/channels/registry.test.js.map +1 -0
  88. package/dist/channels/web.d.ts +2 -0
  89. package/dist/channels/web.d.ts.map +1 -0
  90. package/dist/channels/web.js +1738 -0
  91. package/dist/channels/web.js.map +1 -0
  92. package/dist/cli.d.ts +11 -0
  93. package/dist/cli.d.ts.map +1 -0
  94. package/dist/cli.js +182 -0
  95. package/dist/cli.js.map +1 -0
  96. package/dist/config.d.ts +19 -0
  97. package/dist/config.d.ts.map +1 -0
  98. package/dist/config.js +36 -0
  99. package/dist/config.js.map +1 -0
  100. package/dist/container-runner.d.ts +44 -0
  101. package/dist/container-runner.d.ts.map +1 -0
  102. package/dist/container-runner.js +467 -0
  103. package/dist/container-runner.js.map +1 -0
  104. package/dist/container-runner.test.d.ts +2 -0
  105. package/dist/container-runner.test.d.ts.map +1 -0
  106. package/dist/container-runner.test.js +150 -0
  107. package/dist/container-runner.test.js.map +1 -0
  108. package/dist/container-runtime.d.ts +22 -0
  109. package/dist/container-runtime.d.ts.map +1 -0
  110. package/dist/container-runtime.js +96 -0
  111. package/dist/container-runtime.js.map +1 -0
  112. package/dist/container-runtime.test.d.ts +2 -0
  113. package/dist/container-runtime.test.d.ts.map +1 -0
  114. package/dist/container-runtime.test.js +93 -0
  115. package/dist/container-runtime.test.js.map +1 -0
  116. package/dist/credential-proxy.d.ts +21 -0
  117. package/dist/credential-proxy.d.ts.map +1 -0
  118. package/dist/credential-proxy.js +95 -0
  119. package/dist/credential-proxy.js.map +1 -0
  120. package/dist/credential-proxy.test.d.ts +2 -0
  121. package/dist/credential-proxy.test.d.ts.map +1 -0
  122. package/dist/credential-proxy.test.js +134 -0
  123. package/dist/credential-proxy.test.js.map +1 -0
  124. package/dist/db.d.ts +115 -0
  125. package/dist/db.d.ts.map +1 -0
  126. package/dist/db.js +549 -0
  127. package/dist/db.js.map +1 -0
  128. package/dist/db.test.d.ts +2 -0
  129. package/dist/db.test.d.ts.map +1 -0
  130. package/dist/db.test.js +360 -0
  131. package/dist/db.test.js.map +1 -0
  132. package/dist/env.d.ts +8 -0
  133. package/dist/env.d.ts.map +1 -0
  134. package/dist/env.js +42 -0
  135. package/dist/env.js.map +1 -0
  136. package/dist/formatting.test.d.ts +2 -0
  137. package/dist/formatting.test.d.ts.map +1 -0
  138. package/dist/formatting.test.js +183 -0
  139. package/dist/formatting.test.js.map +1 -0
  140. package/dist/group-folder.d.ts +5 -0
  141. package/dist/group-folder.d.ts.map +1 -0
  142. package/dist/group-folder.js +44 -0
  143. package/dist/group-folder.js.map +1 -0
  144. package/dist/group-folder.test.d.ts +2 -0
  145. package/dist/group-folder.test.d.ts.map +1 -0
  146. package/dist/group-folder.test.js +29 -0
  147. package/dist/group-folder.test.js.map +1 -0
  148. package/dist/group-queue.d.ts +34 -0
  149. package/dist/group-queue.d.ts.map +1 -0
  150. package/dist/group-queue.js +263 -0
  151. package/dist/group-queue.js.map +1 -0
  152. package/dist/group-queue.test.d.ts +2 -0
  153. package/dist/group-queue.test.d.ts.map +1 -0
  154. package/dist/group-queue.test.js +341 -0
  155. package/dist/group-queue.test.js.map +1 -0
  156. package/dist/index.d.ts +12 -0
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +518 -0
  159. package/dist/index.js.map +1 -0
  160. package/dist/ipc-auth.test.d.ts +2 -0
  161. package/dist/ipc-auth.test.d.ts.map +1 -0
  162. package/dist/ipc-auth.test.js +434 -0
  163. package/dist/ipc-auth.test.js.map +1 -0
  164. package/dist/ipc.d.ts +32 -0
  165. package/dist/ipc.d.ts.map +1 -0
  166. package/dist/ipc.js +311 -0
  167. package/dist/ipc.js.map +1 -0
  168. package/dist/logger.d.ts +3 -0
  169. package/dist/logger.d.ts.map +1 -0
  170. package/dist/logger.js +14 -0
  171. package/dist/logger.js.map +1 -0
  172. package/dist/mount-security.d.ts +34 -0
  173. package/dist/mount-security.d.ts.map +1 -0
  174. package/dist/mount-security.js +325 -0
  175. package/dist/mount-security.js.map +1 -0
  176. package/dist/remote-control.d.ts +32 -0
  177. package/dist/remote-control.d.ts.map +1 -0
  178. package/dist/remote-control.js +185 -0
  179. package/dist/remote-control.js.map +1 -0
  180. package/dist/remote-control.test.d.ts +2 -0
  181. package/dist/remote-control.test.d.ts.map +1 -0
  182. package/dist/remote-control.test.js +321 -0
  183. package/dist/remote-control.test.js.map +1 -0
  184. package/dist/router.d.ts +8 -0
  185. package/dist/router.d.ts.map +1 -0
  186. package/dist/router.js +37 -0
  187. package/dist/router.js.map +1 -0
  188. package/dist/routing.test.d.ts +2 -0
  189. package/dist/routing.test.d.ts.map +1 -0
  190. package/dist/routing.test.js +81 -0
  191. package/dist/routing.test.js.map +1 -0
  192. package/dist/sender-allowlist.d.ts +14 -0
  193. package/dist/sender-allowlist.d.ts.map +1 -0
  194. package/dist/sender-allowlist.js +79 -0
  195. package/dist/sender-allowlist.js.map +1 -0
  196. package/dist/sender-allowlist.test.d.ts +2 -0
  197. package/dist/sender-allowlist.test.d.ts.map +1 -0
  198. package/dist/sender-allowlist.test.js +186 -0
  199. package/dist/sender-allowlist.test.js.map +1 -0
  200. package/dist/session-commands.d.ts +47 -0
  201. package/dist/session-commands.d.ts.map +1 -0
  202. package/dist/session-commands.js +102 -0
  203. package/dist/session-commands.js.map +1 -0
  204. package/dist/session-commands.test.d.ts +2 -0
  205. package/dist/session-commands.test.d.ts.map +1 -0
  206. package/dist/session-commands.test.js +190 -0
  207. package/dist/session-commands.test.js.map +1 -0
  208. package/dist/task-scheduler.d.ts +22 -0
  209. package/dist/task-scheduler.d.ts.map +1 -0
  210. package/dist/task-scheduler.js +210 -0
  211. package/dist/task-scheduler.js.map +1 -0
  212. package/dist/task-scheduler.test.d.ts +2 -0
  213. package/dist/task-scheduler.test.d.ts.map +1 -0
  214. package/dist/task-scheduler.test.js +107 -0
  215. package/dist/task-scheduler.test.js.map +1 -0
  216. package/dist/timezone.d.ts +6 -0
  217. package/dist/timezone.d.ts.map +1 -0
  218. package/dist/timezone.js +17 -0
  219. package/dist/timezone.js.map +1 -0
  220. package/dist/timezone.test.d.ts +2 -0
  221. package/dist/timezone.test.d.ts.map +1 -0
  222. package/dist/timezone.test.js +23 -0
  223. package/dist/timezone.test.js.map +1 -0
  224. package/dist/types.d.ts +78 -0
  225. package/dist/types.d.ts.map +1 -0
  226. package/dist/types.js +2 -0
  227. package/dist/types.js.map +1 -0
  228. package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
  229. package/docs/DEBUG_CHECKLIST.md +143 -0
  230. package/docs/REQUIREMENTS.md +196 -0
  231. package/docs/SDK_DEEP_DIVE.md +643 -0
  232. package/docs/SECURITY.md +122 -0
  233. package/docs/SPEC.md +785 -0
  234. package/docs/docker-sandboxes.md +359 -0
  235. package/docs/nanoclaw-architecture-final.md +1063 -0
  236. package/docs/nanorepo-architecture.md +168 -0
  237. package/docs/skills-as-branches.md +662 -0
  238. package/groups/global/CLAUDE.md +58 -0
  239. package/groups/main/CLAUDE.md +246 -0
  240. package/launchd/com.nanoclaw.plist +32 -0
  241. package/package.json +45 -0
  242. package/repo-tokens/README.md +113 -0
  243. package/repo-tokens/action.yml +186 -0
  244. package/repo-tokens/badge.svg +23 -0
  245. package/repo-tokens/examples/green.svg +14 -0
  246. package/repo-tokens/examples/red.svg +14 -0
  247. package/repo-tokens/examples/yellow-green.svg +14 -0
  248. package/repo-tokens/examples/yellow.svg +14 -0
  249. package/scripts/run-migrations.ts +105 -0
  250. package/setup/container.ts +144 -0
  251. package/setup/environment.test.ts +121 -0
  252. package/setup/environment.ts +94 -0
  253. package/setup/groups.ts +229 -0
  254. package/setup/index.ts +58 -0
  255. package/setup/mounts.ts +115 -0
  256. package/setup/platform.test.ts +120 -0
  257. package/setup/platform.ts +132 -0
  258. package/setup/register.test.ts +257 -0
  259. package/setup/register.ts +177 -0
  260. package/setup/service.test.ts +187 -0
  261. package/setup/service.ts +362 -0
  262. package/setup/status.ts +16 -0
  263. package/setup/verify.ts +192 -0
  264. package/setup.sh +161 -0
  265. package/src/channels/index.ts +12 -0
  266. package/src/channels/registry.test.ts +42 -0
  267. package/src/channels/registry.ts +32 -0
  268. package/src/channels/web.ts +1856 -0
  269. package/src/cli.ts +209 -0
  270. package/src/config.ts +73 -0
  271. package/src/container-runner.test.ts +210 -0
  272. package/src/container-runner.ts +707 -0
  273. package/src/container-runtime.test.ts +149 -0
  274. package/src/container-runtime.ts +127 -0
  275. package/src/credential-proxy.test.ts +192 -0
  276. package/src/credential-proxy.ts +125 -0
  277. package/src/db.test.ts +484 -0
  278. package/src/db.ts +803 -0
  279. package/src/env.ts +42 -0
  280. package/src/formatting.test.ts +256 -0
  281. package/src/group-folder.test.ts +43 -0
  282. package/src/group-folder.ts +44 -0
  283. package/src/group-queue.test.ts +484 -0
  284. package/src/group-queue.ts +365 -0
  285. package/src/index.ts +731 -0
  286. package/src/ipc-auth.test.ts +679 -0
  287. package/src/ipc.ts +461 -0
  288. package/src/logger.ts +16 -0
  289. package/src/mount-security.ts +419 -0
  290. package/src/remote-control.test.ts +397 -0
  291. package/src/remote-control.ts +224 -0
  292. package/src/router.ts +52 -0
  293. package/src/routing.test.ts +170 -0
  294. package/src/sender-allowlist.test.ts +216 -0
  295. package/src/sender-allowlist.ts +128 -0
  296. package/src/session-commands.test.ts +247 -0
  297. package/src/session-commands.ts +163 -0
  298. package/src/task-scheduler.test.ts +129 -0
  299. package/src/task-scheduler.ts +295 -0
  300. package/src/timezone.test.ts +29 -0
  301. package/src/timezone.ts +16 -0
  302. package/src/types.ts +107 -0
  303. package/tsconfig.json +20 -0
  304. package/vitest.config.ts +7 -0
  305. package/vitest.skills.config.ts +7 -0
@@ -0,0 +1,359 @@
1
+ # Running NanoClaw in Docker Sandboxes (Manual Setup)
2
+
3
+ This guide walks through setting up NanoClaw inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/) from scratch — no install script, no pre-built fork. You'll clone the upstream repo, apply the necessary patches, and have agents running in full hypervisor-level isolation.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ Host (macOS / Windows WSL)
9
+ └── Docker Sandbox (micro VM with isolated kernel)
10
+ ├── NanoClaw process (Node.js)
11
+ │ ├── Channel adapters (WhatsApp, Telegram, etc.)
12
+ │ └── Container spawner → nested Docker daemon
13
+ └── Docker-in-Docker
14
+ └── nanoclaw-agent containers
15
+ └── Claude Agent SDK
16
+ ```
17
+
18
+ Each agent runs in its own container, inside a micro VM that is fully isolated from your host. Two layers of isolation: per-agent containers + the VM boundary.
19
+
20
+ The sandbox provides a MITM proxy at `host.docker.internal:3128` that handles network access and injects your Anthropic API key automatically.
21
+
22
+ > **Note:** This guide is based on a validated setup running on macOS (Apple Silicon) with WhatsApp. Other channels (Telegram, Slack, etc.) and environments (Windows WSL) may require additional proxy patches for their specific HTTP/WebSocket clients. The core patches (container runner, credential proxy, Dockerfile) apply universally — channel-specific proxy configuration varies.
23
+
24
+ ## Prerequisites
25
+
26
+ - **Docker Desktop v4.40+** with Sandbox support
27
+ - **Anthropic API key** (the sandbox proxy manages injection)
28
+ - For **Telegram**: a bot token from [@BotFather](https://t.me/BotFather) and your chat ID
29
+ - For **WhatsApp**: a phone with WhatsApp installed
30
+
31
+ Verify sandbox support:
32
+ ```bash
33
+ docker sandbox version
34
+ ```
35
+
36
+ ## Step 1: Create the Sandbox
37
+
38
+ On your host machine:
39
+
40
+ ```bash
41
+ # Create a workspace directory
42
+ mkdir -p ~/nanoclaw-workspace
43
+
44
+ # Create a shell sandbox with the workspace mounted
45
+ docker sandbox create shell ~/nanoclaw-workspace
46
+ ```
47
+
48
+ If you're using WhatsApp, configure proxy bypass so WhatsApp's Noise protocol isn't MITM-inspected:
49
+
50
+ ```bash
51
+ docker sandbox network proxy shell-nanoclaw-workspace \
52
+ --bypass-host web.whatsapp.com \
53
+ --bypass-host "*.whatsapp.com" \
54
+ --bypass-host "*.whatsapp.net"
55
+ ```
56
+
57
+ Telegram does not need proxy bypass.
58
+
59
+ Enter the sandbox:
60
+ ```bash
61
+ docker sandbox run shell-nanoclaw-workspace
62
+ ```
63
+
64
+ ## Step 2: Install Prerequisites
65
+
66
+ Inside the sandbox:
67
+
68
+ ```bash
69
+ sudo apt-get update && sudo apt-get install -y build-essential python3
70
+ npm config set strict-ssl false
71
+ ```
72
+
73
+ ## Step 3: Clone and Install NanoClaw
74
+
75
+ NanoClaw must live inside the workspace directory — Docker-in-Docker can only bind-mount from the shared workspace path.
76
+
77
+ ```bash
78
+ # Clone to home first (virtiofs can corrupt git pack files during clone)
79
+ cd ~
80
+ git clone https://github.com/qwibitai/nanoclaw.git
81
+
82
+ # Replace with YOUR workspace path (the host path you passed to `docker sandbox create`)
83
+ WORKSPACE=/Users/you/nanoclaw-workspace
84
+
85
+ # Move into workspace so DinD mounts work
86
+ mv nanoclaw "$WORKSPACE/nanoclaw"
87
+ cd "$WORKSPACE/nanoclaw"
88
+
89
+ # Install dependencies
90
+ npm install
91
+ npm install https-proxy-agent
92
+ ```
93
+
94
+ ## Step 4: Apply Proxy and Sandbox Patches
95
+
96
+ NanoClaw needs several patches to work inside a Docker Sandbox. These handle proxy routing, CA certificates, and Docker-in-Docker mount restrictions.
97
+
98
+ ### 4a. Dockerfile — proxy args for container image build
99
+
100
+ `npm install` inside `docker build` fails with `SELF_SIGNED_CERT_IN_CHAIN` because the sandbox's MITM proxy presents its own certificate. Add proxy build args to `container/Dockerfile`:
101
+
102
+ Add these lines after the `FROM` line:
103
+
104
+ ```dockerfile
105
+ # Accept proxy build args
106
+ ARG http_proxy
107
+ ARG https_proxy
108
+ ARG no_proxy
109
+ ARG NODE_EXTRA_CA_CERTS
110
+ ARG npm_config_strict_ssl=true
111
+ RUN npm config set strict-ssl ${npm_config_strict_ssl}
112
+ ```
113
+
114
+ And after the `RUN npm install` line:
115
+
116
+ ```dockerfile
117
+ RUN npm config set strict-ssl true
118
+ ```
119
+
120
+ ### 4b. Build script — forward proxy args
121
+
122
+ Patch `container/build.sh` to pass proxy env vars to `docker build`:
123
+
124
+ Add these `--build-arg` flags to the `docker build` command:
125
+
126
+ ```bash
127
+ --build-arg http_proxy="${http_proxy:-$HTTP_PROXY}" \
128
+ --build-arg https_proxy="${https_proxy:-$HTTPS_PROXY}" \
129
+ --build-arg no_proxy="${no_proxy:-$NO_PROXY}" \
130
+ --build-arg npm_config_strict_ssl=false \
131
+ ```
132
+
133
+ ### 4c. Container runner — proxy forwarding, CA cert mount, /dev/null fix
134
+
135
+ Three changes to `src/container-runner.ts`:
136
+
137
+ **Replace `/dev/null` shadow mount.** The sandbox rejects `/dev/null` bind mounts. Find where `.env` is shadow-mounted to `/dev/null` and replace it with an empty file:
138
+
139
+ ```typescript
140
+ // Create an empty file to shadow .env (Docker Sandbox rejects /dev/null mounts)
141
+ const emptyEnvPath = path.join(DATA_DIR, 'empty-env');
142
+ if (!fs.existsSync(emptyEnvPath)) fs.writeFileSync(emptyEnvPath, '');
143
+ // Use emptyEnvPath instead of '/dev/null' in the mount
144
+ ```
145
+
146
+ **Forward proxy env vars** to spawned agent containers. Add `-e` flags for `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` and their lowercase variants.
147
+
148
+ **Mount CA certificate.** If `NODE_EXTRA_CA_CERTS` or `SSL_CERT_FILE` is set, copy the cert into the project directory and mount it into agent containers:
149
+
150
+ ```typescript
151
+ const caCertSrc = process.env.NODE_EXTRA_CA_CERTS || process.env.SSL_CERT_FILE;
152
+ if (caCertSrc) {
153
+ const certDir = path.join(DATA_DIR, 'ca-cert');
154
+ fs.mkdirSync(certDir, { recursive: true });
155
+ fs.copyFileSync(caCertSrc, path.join(certDir, 'proxy-ca.crt'));
156
+ // Mount: certDir -> /workspace/ca-cert (read-only)
157
+ // Set NODE_EXTRA_CA_CERTS=/workspace/ca-cert/proxy-ca.crt in the container
158
+ }
159
+ ```
160
+
161
+ ### 4d. Container runtime — prevent self-termination
162
+
163
+ In `src/container-runtime.ts`, the `cleanupOrphans()` function matches containers by the `nanoclaw-` prefix. Inside a sandbox, the sandbox container itself may match (e.g., `nanoclaw-docker-sandbox`). Filter out the current hostname:
164
+
165
+ ```typescript
166
+ // In cleanupOrphans(), filter out os.hostname() from the list of containers to stop
167
+ ```
168
+
169
+ ### 4e. Credential proxy — route through MITM proxy
170
+
171
+ In `src/credential-proxy.ts`, upstream API requests need to go through the sandbox proxy. Add `HttpsProxyAgent` to outbound requests:
172
+
173
+ ```typescript
174
+ import { HttpsProxyAgent } from 'https-proxy-agent';
175
+
176
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy;
177
+ const upstreamAgent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined;
178
+ // Pass upstreamAgent to https.request() options
179
+ ```
180
+
181
+ ### 4f. Setup script — proxy build args
182
+
183
+ Patch `setup/container.ts` to pass the same proxy `--build-arg` flags as `build.sh` (Step 4b).
184
+
185
+ ## Step 5: Build
186
+
187
+ ```bash
188
+ npm run build
189
+ bash container/build.sh
190
+ ```
191
+
192
+ ## Step 6: Add a Channel
193
+
194
+ ### Telegram
195
+
196
+ ```bash
197
+ # Apply the Telegram skill
198
+ npx tsx scripts/apply-skill.ts .claude/skills/add-telegram
199
+
200
+ # Rebuild after applying the skill
201
+ npm run build
202
+
203
+ # Configure .env
204
+ cat > .env << EOF
205
+ TELEGRAM_BOT_TOKEN=<your-token-from-botfather>
206
+ ASSISTANT_NAME=nanoclaw
207
+ ANTHROPIC_API_KEY=proxy-managed
208
+ EOF
209
+ mkdir -p data/env && cp .env data/env/env
210
+
211
+ # Register your chat
212
+ npx tsx setup/index.ts --step register \
213
+ --jid "tg:<your-chat-id>" \
214
+ --name "My Chat" \
215
+ --trigger "@nanoclaw" \
216
+ --folder "telegram_main" \
217
+ --channel telegram \
218
+ --assistant-name "nanoclaw" \
219
+ --is-main \
220
+ --no-trigger-required
221
+ ```
222
+
223
+ **To find your chat ID:** Send any message to your bot, then:
224
+ ```bash
225
+ curl -s --proxy $HTTPS_PROXY "https://api.telegram.org/bot<TOKEN>/getUpdates" | python3 -m json.tool
226
+ ```
227
+
228
+ **Telegram in groups:** Disable Group Privacy in @BotFather (`/mybots` > Bot Settings > Group Privacy > Turn off), then remove and re-add the bot.
229
+
230
+ **Important:** If the Telegram skill creates `src/channels/telegram.ts`, you'll need to patch it for proxy support. Add an `HttpsProxyAgent` and pass it to grammy's `Bot` constructor via `baseFetchConfig.agent`. Then rebuild.
231
+
232
+ ### WhatsApp
233
+
234
+ Make sure you configured proxy bypass in [Step 1](#step-1-create-the-sandbox) first.
235
+
236
+ ```bash
237
+ # Apply the WhatsApp skill
238
+ npx tsx scripts/apply-skill.ts .claude/skills/add-whatsapp
239
+
240
+ # Rebuild
241
+ npm run build
242
+
243
+ # Configure .env
244
+ cat > .env << EOF
245
+ ASSISTANT_NAME=nanoclaw
246
+ ANTHROPIC_API_KEY=proxy-managed
247
+ EOF
248
+ mkdir -p data/env && cp .env data/env/env
249
+
250
+ # Authenticate (choose one):
251
+
252
+ # QR code — scan with WhatsApp camera:
253
+ npx tsx src/whatsapp-auth.ts
254
+
255
+ # OR pairing code — enter code in WhatsApp > Linked Devices > Link with phone number:
256
+ npx tsx src/whatsapp-auth.ts --pairing-code --phone <phone-number-no-plus>
257
+
258
+ # Register your chat (JID = your phone number + @s.whatsapp.net)
259
+ npx tsx setup/index.ts --step register \
260
+ --jid "<phone>@s.whatsapp.net" \
261
+ --name "My Chat" \
262
+ --trigger "@nanoclaw" \
263
+ --folder "whatsapp_main" \
264
+ --channel whatsapp \
265
+ --assistant-name "nanoclaw" \
266
+ --is-main \
267
+ --no-trigger-required
268
+ ```
269
+
270
+ **Important:** The WhatsApp skill files (`src/channels/whatsapp.ts` and `src/whatsapp-auth.ts`) also need proxy patches — add `HttpsProxyAgent` for WebSocket connections and a proxy-aware version fetch. Then rebuild.
271
+
272
+ ### Both Channels
273
+
274
+ Apply both skills, patch both for proxy support, combine the `.env` variables, and register each chat separately.
275
+
276
+ ## Step 7: Run
277
+
278
+ ```bash
279
+ npm start
280
+ ```
281
+
282
+ You don't need to set `ANTHROPIC_API_KEY` manually. The sandbox proxy intercepts requests and replaces `proxy-managed` with your real key automatically.
283
+
284
+ ## Networking Details
285
+
286
+ ### How the proxy works
287
+
288
+ All traffic from the sandbox routes through the host proxy at `host.docker.internal:3128`:
289
+
290
+ ```
291
+ Agent container → DinD bridge → Sandbox VM → host.docker.internal:3128 → Host proxy → api.anthropic.com
292
+ ```
293
+
294
+ **"Bypass" does not mean traffic skips the proxy.** It means the proxy passes traffic through without MITM inspection. Node.js doesn't automatically use `HTTP_PROXY` env vars — you need explicit `HttpsProxyAgent` configuration in every HTTP/WebSocket client.
295
+
296
+ ### Shared paths for DinD mounts
297
+
298
+ Only the workspace directory is available for Docker-in-Docker bind mounts. Paths outside the workspace fail with "path not shared":
299
+ - `/dev/null` → replace with an empty file in the project dir
300
+ - `/usr/local/share/ca-certificates/` → copy cert to project dir
301
+ - `/home/agent/` → clone to workspace instead
302
+
303
+ ### Git clone and virtiofs
304
+
305
+ The workspace is mounted via virtiofs. Git's pack file handling can corrupt over virtiofs during clone. Workaround: clone to `/home/agent` first, then `mv` into the workspace.
306
+
307
+ ## Troubleshooting
308
+
309
+ ### npm install fails with SELF_SIGNED_CERT_IN_CHAIN
310
+ ```bash
311
+ npm config set strict-ssl false
312
+ ```
313
+
314
+ ### Container build fails with proxy errors
315
+ ```bash
316
+ docker build \
317
+ --build-arg http_proxy=$http_proxy \
318
+ --build-arg https_proxy=$https_proxy \
319
+ -t nanoclaw-agent:latest container/
320
+ ```
321
+
322
+ ### Agent containers fail with "path not shared"
323
+ All bind-mounted paths must be under the workspace directory. Check:
324
+ - Is NanoClaw cloned into the workspace? (not `/home/agent/`)
325
+ - Is the CA cert copied to the project root?
326
+ - Has the empty `.env` shadow file been created?
327
+
328
+ ### Agent containers can't reach Anthropic API
329
+ Verify proxy env vars are forwarded to agent containers. Check container logs for `HTTP_PROXY=http://host.docker.internal:3128`.
330
+
331
+ ### WhatsApp error 405
332
+ The version fetch is returning a stale version. Make sure the proxy-aware `fetchWaVersionViaProxy` patch is applied — it fetches `sw.js` through `HttpsProxyAgent` and parses `client_revision`.
333
+
334
+ ### WhatsApp "Connection failed" immediately
335
+ Proxy bypass not configured. From the **host**, run:
336
+ ```bash
337
+ docker sandbox network proxy <sandbox-name> \
338
+ --bypass-host web.whatsapp.com \
339
+ --bypass-host "*.whatsapp.com" \
340
+ --bypass-host "*.whatsapp.net"
341
+ ```
342
+
343
+ ### Telegram bot doesn't receive messages
344
+ 1. Check the grammy proxy patch is applied (look for `HttpsProxyAgent` in `src/channels/telegram.ts`)
345
+ 2. Check Group Privacy is disabled in @BotFather if using in groups
346
+
347
+ ### Git clone fails with "inflate: data stream error"
348
+ Clone to a non-workspace path first, then move:
349
+ ```bash
350
+ cd ~ && git clone https://github.com/qwibitai/nanoclaw.git && mv nanoclaw /path/to/workspace/nanoclaw
351
+ ```
352
+
353
+ ### WhatsApp QR code doesn't display
354
+ Run the auth command interactively inside the sandbox (not piped through `docker sandbox exec`):
355
+ ```bash
356
+ docker sandbox run shell-nanoclaw-workspace
357
+ # Then inside:
358
+ npx tsx src/whatsapp-auth.ts
359
+ ```