mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-06-04 10:14:47 +08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6418dda3da | |||
| d2a015074d | |||
| 8ea451aced | |||
| 5b14ae249a | |||
| 06711b5e47 | |||
| d0139a7c0f | |||
| 2abb34bc78 | |||
| b8d7777740 | |||
| 43ff3a4644 | |||
| 34b9b259ea | |||
| f3d5b82899 | |||
| e603236223 | |||
| 5fff2d2728 | |||
| 529d2db8e2 | |||
| 26eb89c771 |
@@ -182,9 +182,12 @@ ATOMIC_CHAT_API_KEY=sk-...
|
||||
|
||||
### Restart the service
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# Linux: systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
## Phase 4: Verify
|
||||
|
||||
@@ -93,10 +93,13 @@ Generate the secret: `node -e "console.log('nc-' + require('crypto').randomBytes
|
||||
|
||||
### 6. Build and restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
systemctl --user restart nanoclaw # Linux
|
||||
# or: launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
source setup/lib/install-slug.sh
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
# or: launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
```
|
||||
|
||||
### 7. Verify
|
||||
|
||||
@@ -23,14 +23,17 @@ DC_SMTP_PORT
|
||||
|
||||
## 3. Rebuild and restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
source setup/lib/install-slug.sh
|
||||
|
||||
# Linux
|
||||
systemctl --user restart nanoclaw
|
||||
systemctl --user restart $(systemd_unit)
|
||||
|
||||
# macOS
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label)
|
||||
```
|
||||
|
||||
## 4. Remove account data (optional)
|
||||
|
||||
@@ -98,12 +98,16 @@ The `/set-avatar` command (send an image with that caption) is the easiest way t
|
||||
|
||||
### Restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
|
||||
# Linux
|
||||
systemctl --user restart nanoclaw
|
||||
systemctl --user restart $(systemd_unit)
|
||||
|
||||
# macOS
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label)
|
||||
```
|
||||
|
||||
On first start the adapter configures the email account (IMAP/SMTP credentials, calls `configure()`). Subsequent starts skip straight to `startIo()`. Account data is stored in `dc-account/` in the project root (or your `DC_ACCOUNT_DIR`).
|
||||
@@ -232,7 +236,7 @@ Set `DC_SMTP_SECURITY=1` and `DC_SMTP_PORT=465` in `.env`, then restart.
|
||||
|
||||
```bash
|
||||
rm -f dc-account/accounts.lock
|
||||
systemctl --user restart nanoclaw
|
||||
systemctl --user restart "$(. setup/lib/install-slug.sh && systemd_unit)"
|
||||
```
|
||||
|
||||
### Bot not responding after restart
|
||||
|
||||
@@ -162,10 +162,13 @@ If you changed `EMACS_CHANNEL_PORT` from the default:
|
||||
|
||||
## Restart NanoClaw
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# systemctl --user restart nanoclaw # Linux
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
## Verify
|
||||
@@ -240,7 +243,7 @@ grep -q "import './emacs.js'" src/channels/index.ts && echo "imported" || echo "
|
||||
|
||||
### No response from agent
|
||||
|
||||
1. NanoClaw running: `launchctl list | grep nanoclaw` (macOS) / `systemctl --user status nanoclaw` (Linux)
|
||||
1. NanoClaw running: `launchctl list | grep "$(. setup/lib/install-slug.sh && launchd_label)"` (macOS) / `systemctl --user status "$(. setup/lib/install-slug.sh && systemd_unit)"` (Linux)
|
||||
2. Messaging group wired: `pnpm exec tsx scripts/q.ts data/v2.db "SELECT mg.platform_id, ag.folder FROM messaging_groups mg JOIN messaging_group_agents mga ON mg.id = mga.messaging_group_id JOIN agent_groups ag ON ag.id = mga.agent_group_id WHERE mg.channel_type = 'emacs'"`
|
||||
3. Logs show inbound: `grep 'channel_type=emacs\|Emacs' logs/nanoclaw.log | tail -20`
|
||||
|
||||
@@ -282,13 +285,16 @@ If an agent outputs org-mode directly, markers get double-converted and render i
|
||||
|
||||
## Removal
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
rm src/channels/emacs.ts src/channels/emacs.test.ts emacs/nanoclaw.el
|
||||
# Remove the `import './emacs.js';` line from src/channels/index.ts
|
||||
# Remove EMACS_* lines from .env
|
||||
pnpm run build
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# systemctl --user restart nanoclaw # Linux
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# systemctl --user restart $(systemd_unit) # Linux
|
||||
|
||||
# Remove the NanoClaw block from your Emacs config
|
||||
# Optionally clean up the messaging group:
|
||||
|
||||
@@ -92,7 +92,6 @@ onecli agents list
|
||||
|
||||
```bash
|
||||
grep -q 'CALENDAR_MCP_VERSION' container/Dockerfile && \
|
||||
grep -q "mcp__calendar__\*" container/agent-runner/src/providers/claude.ts && \
|
||||
echo "ALREADY APPLIED — skip to Phase 3"
|
||||
```
|
||||
|
||||
@@ -121,9 +120,7 @@ RUN --mount=type=cache,target=/root/.cache/pnpm \
|
||||
pnpm install -g "@cocal/google-calendar-mcp@${CALENDAR_MCP_VERSION}"
|
||||
```
|
||||
|
||||
### Add tools to allowlist
|
||||
|
||||
Edit `container/agent-runner/src/providers/claude.ts`. Add `'mcp__calendar__*'` to `TOOL_ALLOWLIST` after `'mcp__nanoclaw__*'` (or after `'mcp__gmail__*'` if present).
|
||||
**No `TOOL_ALLOWLIST` edit needed.** `container/agent-runner/src/providers/claude.ts` derives the allow-pattern dynamically from each group's `mcpServers` map (`Object.keys(this.mcpServers).map(mcpAllowPattern)`), so registering `calendar` in Phase 3 automatically allows `mcp__calendar__*`. Earlier versions of this skill instructed a static `TOOL_ALLOWLIST` edit — that's now redundant.
|
||||
|
||||
### Rebuild the container image
|
||||
|
||||
@@ -133,40 +130,59 @@ Edit `container/agent-runner/src/providers/claude.ts`. Add `'mcp__calendar__*'`
|
||||
|
||||
## Phase 3: Wire Per-Agent-Group
|
||||
|
||||
For each agent group, merge into `groups/<folder>/container.json`:
|
||||
For each agent group, persist two changes to the **central DB** (`data/v2.db`): the `mcpServers.calendar` entry and an `additionalMounts` entry for `.calendar-mcp`. Both flow through `materializeContainerJson` on every spawn, so editing `groups/<folder>/container.json` by hand does **not** stick — that file is regenerated from the DB.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"mcpServers": {
|
||||
"calendar": {
|
||||
"command": "google-calendar-mcp",
|
||||
"args": [],
|
||||
"env": {
|
||||
"GOOGLE_OAUTH_CREDENTIALS": "/workspace/extra/.calendar-mcp/gcp-oauth.keys.json",
|
||||
"GOOGLE_CALENDAR_MCP_TOKEN_PATH": "/workspace/extra/.calendar-mcp/credentials.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalMounts": [
|
||||
{
|
||||
"hostPath": "/home/<user>/.calendar-mcp",
|
||||
"containerPath": ".calendar-mcp",
|
||||
"readonly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
### Register the MCP server
|
||||
|
||||
For each chosen `<group-id>` (use `ncl groups list` to enumerate):
|
||||
|
||||
```bash
|
||||
ncl groups config add-mcp-server \
|
||||
--id <group-id> \
|
||||
--name calendar \
|
||||
--command google-calendar-mcp \
|
||||
--args '[]' \
|
||||
--env '{"GOOGLE_OAUTH_CREDENTIALS":"/workspace/extra/.calendar-mcp/gcp-oauth.keys.json","GOOGLE_CALENDAR_MCP_TOKEN_PATH":"/workspace/extra/.calendar-mcp/credentials.json"}'
|
||||
```
|
||||
|
||||
Substitute `<user>` with `echo $HOME`. `containerPath` is relative (mount-security rejects absolute paths — additional mounts land at `/workspace/extra/<relative>`).
|
||||
Approval behaviour depends on where you run it: from inside an agent's container `ncl` write verbs are approval-gated (admin approves before it lands); from a host operator shell with full scope, it executes immediately. Either way, the response tells you which path it took.
|
||||
|
||||
**Same-group-as-gmail tip:** if this group already has the gmail MCP + `.gmail-mcp` mount, **merge, don't replace** — both entries coexist in `mcpServers` and `additionalMounts`.
|
||||
### Add the `.calendar-mcp` mount
|
||||
|
||||
There is no `ncl groups config add-mount` verb yet (tracked in [#2395](https://github.com/nanocoai/nanoclaw/issues/2395)). Until that ships, edit the DB directly via the in-tree wrapper (`scripts/q.ts` — `setup/verify.ts:5` codifies that NanoClaw avoids depending on the `sqlite3` CLI binary, so don't shell out to it):
|
||||
|
||||
```bash
|
||||
GROUP_ID='<group-id>'
|
||||
HOST_PATH="$HOME/.calendar-mcp"
|
||||
MOUNT=$(jq -cn --arg h "$HOST_PATH" '{hostPath:$h, containerPath:".calendar-mcp", readonly:false}')
|
||||
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
|
||||
SET additional_mounts = json_insert(additional_mounts, '\$[#]', json('$MOUNT')), \
|
||||
updated_at = datetime('now') \
|
||||
WHERE agent_group_id = '$GROUP_ID';"
|
||||
```
|
||||
|
||||
Run from your NanoClaw project root (where `data/v2.db` lives). The `$[#]` placeholder is SQLite JSON1's append-to-end notation; it's `\$`-escaped so bash doesn't arithmetic-expand it before sqlite sees it. `updated_at` is ISO-string everywhere else in the schema, so use `datetime('now')` — not `strftime('%s','now')`, which would silently mix epoch ints into a column of YYYY-MM-DD HH:MM:SS strings.
|
||||
|
||||
**Switch to `ncl groups config add-mount` once #2395 lands.** Update this skill at that time.
|
||||
|
||||
`containerPath` is relative (mount-security rejects absolute paths — additional mounts land at `/workspace/extra/<relative>`).
|
||||
|
||||
**Why this can't be `groups/<folder>/container.json`:** post-migration `014-container-configs`, `materializeContainerJson` in `src/container-config.ts` rewrites that file from the DB on every spawn. Anything hand-edited there is silently overwritten on next restart.
|
||||
|
||||
**Same-group-as-gmail tip:** if this group already has the gmail MCP + `.gmail-mcp` mount, both coexist — `ncl groups config add-mcp-server` only updates the named entry, and `json_insert` appends to `additional_mounts` without disturbing existing entries.
|
||||
|
||||
## Phase 4: Build and Restart
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
systemctl --user restart nanoclaw # Linux
|
||||
# launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
```
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
Kill any existing agent containers so they respawn with the new mcpServers config:
|
||||
@@ -193,16 +209,28 @@ Common signals:
|
||||
- `command not found: google-calendar-mcp` → image not rebuilt.
|
||||
- `ENOENT ...credentials.json` → mount missing. Check the mount allowlist.
|
||||
- `401 Unauthorized` from `*.googleapis.com` → OneCLI isn't injecting; verify agent's secret mode and that Google Calendar is connected.
|
||||
- Agent says "I don't have calendar tools" → `mcp__calendar__*` missing from `TOOL_ALLOWLIST`, or image cache stale (`./container/build.sh` again).
|
||||
- Agent says "I don't have calendar tools" → the `calendar` MCP server isn't registered in this group's `mcpServers` (re-run the `ncl groups config add-mcp-server` step in Phase 3 for that group and restart it), or the agent-runner image is stale (`./container/build.sh`, `--no-cache` if suspicious).
|
||||
|
||||
## Removal
|
||||
|
||||
1. Delete `"calendar"` from `mcpServers` and the `.calendar-mcp` mount from `additionalMounts` in each group's `container.json`.
|
||||
2. Remove `'mcp__calendar__*'` from `TOOL_ALLOWLIST`.
|
||||
1. For each group that had Calendar wired, remove the MCP server from the DB:
|
||||
```bash
|
||||
ncl groups config remove-mcp-server --id <group-id> --name calendar
|
||||
```
|
||||
2. Remove the `.calendar-mcp` mount from the DB (no `remove-mount` verb yet — same #2395 dependency):
|
||||
```bash
|
||||
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
|
||||
SET additional_mounts = (SELECT json_group_array(value) FROM json_each(additional_mounts) \
|
||||
WHERE json_extract(value, '\$.containerPath') != '.calendar-mcp'), \
|
||||
updated_at = datetime('now') \
|
||||
WHERE agent_group_id = '<group-id>';"
|
||||
```
|
||||
3. Remove `CALENDAR_MCP_VERSION` ARG and the calendar package from the Dockerfile install block.
|
||||
4. `pnpm run build && ./container/build.sh && systemctl --user restart nanoclaw`.
|
||||
4. `pnpm run build && ./container/build.sh && systemctl --user restart "$(. setup/lib/install-slug.sh && systemd_unit)"`.
|
||||
5. Optional: `rm -rf ~/.calendar-mcp/` and `onecli apps disconnect --provider google-calendar`.
|
||||
|
||||
No `TOOL_ALLOWLIST` removal step — Phase 2 no longer edits it.
|
||||
|
||||
## Credits & references
|
||||
|
||||
- **MCP server:** [`@cocal/google-calendar-mcp`](https://github.com/cocal-com/google-calendar-mcp) — MIT-licensed, actively maintained, multi-account and multi-calendar.
|
||||
|
||||
@@ -136,7 +136,15 @@ Use `per-thread` session mode so each PR/issue gets its own agent session.
|
||||
|
||||
If you're in the middle of `/setup`, return to the setup flow now.
|
||||
|
||||
Otherwise, restart the service (`systemctl --user restart nanoclaw` or `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`) to pick up the new channel.
|
||||
Otherwise, restart the service to pick up the new channel.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
## Channel Info
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ onecli agents secrets --id <agent-id>
|
||||
|
||||
```bash
|
||||
grep -q 'GMAIL_MCP_VERSION' container/Dockerfile && \
|
||||
grep -q "mcp__gmail__\*" container/agent-runner/src/providers/claude.ts && \
|
||||
echo "ALREADY APPLIED — skip to Phase 3"
|
||||
```
|
||||
|
||||
@@ -132,9 +131,7 @@ Pinned version matters — `minimumReleaseAge` in `pnpm-workspace.yaml` gates tr
|
||||
|
||||
**Why the `zod-to-json-schema` pin:** `@gongrzhe/server-gmail-autoauth-mcp@1.1.11` has loose deps (`zod-to-json-schema: ^3.22.1`, `zod: ^3.22.4`). pnpm resolves `zod-to-json-schema` to the latest 3.25.x, which imports `zod/v3` — a subpath that only exists in `zod>=3.25`. But `zod` resolves to `3.24.x` (highest satisfying `^3.22.4` without breaking peer ranges). Result: `ERR_PACKAGE_PATH_NOT_EXPORTED` at import time. Pinning `zod-to-json-schema` to a pre-v3-subpath version avoids it. Re-check if you bump `GMAIL_MCP_VERSION`.
|
||||
|
||||
### Add tools to allowlist
|
||||
|
||||
Edit `container/agent-runner/src/providers/claude.ts`. Find `'mcp__nanoclaw__*',` in `TOOL_ALLOWLIST` and add `'mcp__gmail__*',` after it.
|
||||
**No `TOOL_ALLOWLIST` edit needed.** `container/agent-runner/src/providers/claude.ts` derives the allow-pattern dynamically from each group's `mcpServers` map (`Object.keys(this.mcpServers).map(mcpAllowPattern)`), so registering `gmail` in Phase 3 automatically allows `mcp__gmail__*`. Earlier versions of this skill instructed a static `TOOL_ALLOWLIST` edit — that's now redundant.
|
||||
|
||||
### Rebuild the container image
|
||||
|
||||
@@ -146,42 +143,63 @@ Must complete cleanly. The new `pnpm install -g` layer is ~60s first time (cache
|
||||
|
||||
## Phase 3: Wire Per-Agent-Group
|
||||
|
||||
For each agent group that should have Gmail (ask the user — typically their personal DM and CLI agents, sometimes shared household agents), edit `groups/<folder>/container.json` to add the mount and MCP server.
|
||||
For each agent group that should have Gmail (ask the user — typically their personal DM and CLI agents, sometimes shared household agents), persist two changes to the **central DB** (`data/v2.db`): the `mcpServers.gmail` entry and an `additionalMounts` entry for `.gmail-mcp`. Both flow through `materializeContainerJson` on every spawn, so editing `groups/<folder>/container.json` by hand does **not** stick — that file is regenerated from the DB.
|
||||
|
||||
Merge these into the group's `container.json`:
|
||||
### List groups, pick which ones get Gmail
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"mcpServers": {
|
||||
"gmail": {
|
||||
"command": "gmail-mcp",
|
||||
"args": [],
|
||||
"env": {
|
||||
"GMAIL_OAUTH_PATH": "/workspace/extra/.gmail-mcp/gcp-oauth.keys.json",
|
||||
"GMAIL_CREDENTIALS_PATH": "/workspace/extra/.gmail-mcp/credentials.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalMounts": [
|
||||
{
|
||||
"hostPath": "/home/<user>/.gmail-mcp",
|
||||
"containerPath": ".gmail-mcp",
|
||||
"readonly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```bash
|
||||
ncl groups list
|
||||
```
|
||||
|
||||
Substitute `<user>` with the host user's home (use `echo $HOME`, don't assume `~` will expand — `container-runner.ts` does expand `~` via `expandPath`, but an explicit absolute path is clearer and matches what `/manage-mounts` writes).
|
||||
### Register the MCP server
|
||||
|
||||
For each chosen `<group-id>`:
|
||||
|
||||
```bash
|
||||
ncl groups config add-mcp-server \
|
||||
--id <group-id> \
|
||||
--name gmail \
|
||||
--command gmail-mcp \
|
||||
--args '[]' \
|
||||
--env '{"GMAIL_OAUTH_PATH":"/workspace/extra/.gmail-mcp/gcp-oauth.keys.json","GMAIL_CREDENTIALS_PATH":"/workspace/extra/.gmail-mcp/credentials.json"}'
|
||||
```
|
||||
|
||||
Approval behaviour depends on where you run it: from inside an agent's container `ncl` write verbs are approval-gated (admin approves before it lands); from a host operator shell with full scope, it executes immediately. Either way, the response tells you which path it took.
|
||||
|
||||
### Add the `.gmail-mcp` mount
|
||||
|
||||
There is no `ncl groups config add-mount` verb yet (tracked in [#2395](https://github.com/nanocoai/nanoclaw/issues/2395)). Until that ships, edit the DB directly via the in-tree wrapper (`scripts/q.ts` — `setup/verify.ts:5` codifies that NanoClaw avoids depending on the `sqlite3` CLI binary, so don't shell out to it):
|
||||
|
||||
```bash
|
||||
GROUP_ID='<group-id>'
|
||||
HOST_PATH="$HOME/.gmail-mcp"
|
||||
MOUNT=$(jq -cn --arg h "$HOST_PATH" '{hostPath:$h, containerPath:".gmail-mcp", readonly:false}')
|
||||
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
|
||||
SET additional_mounts = json_insert(additional_mounts, '\$[#]', json('$MOUNT')), \
|
||||
updated_at = datetime('now') \
|
||||
WHERE agent_group_id = '$GROUP_ID';"
|
||||
```
|
||||
|
||||
Run from your NanoClaw project root (where `data/v2.db` lives). The `$[#]` placeholder is SQLite JSON1's append-to-end notation; it's `\$`-escaped so bash doesn't arithmetic-expand it before sqlite sees it. `updated_at` is ISO-string everywhere else in the schema, so use `datetime('now')` — not `strftime('%s','now')`, which would silently mix epoch ints into a column of YYYY-MM-DD HH:MM:SS strings.
|
||||
|
||||
**Switch to `ncl groups config add-mount` once #2395 lands.** Update this skill at that time.
|
||||
|
||||
**Why the container path is relative:** `mount-security` rejects absolute `containerPath` values. Additional mounts are prefixed with `/workspace/extra/`, so `containerPath: ".gmail-mcp"` lands at `/workspace/extra/.gmail-mcp`. The MCP server's `GMAIL_OAUTH_PATH` / `GMAIL_CREDENTIALS_PATH` env vars point at that absolute location inside the container.
|
||||
|
||||
**Why this can't be `groups/<folder>/container.json`:** post-migration `014-container-configs`, `materializeContainerJson` in `src/container-config.ts` rewrites that file from the DB on every spawn. Anything hand-edited there is silently overwritten on next restart.
|
||||
|
||||
## Phase 4: Build and Restart
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
systemctl --user restart nanoclaw # Linux
|
||||
# launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
```
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
## Phase 5: Verify
|
||||
@@ -206,17 +224,29 @@ Common signals:
|
||||
- `command not found: gmail-mcp` → image wasn't rebuilt or PATH doesn't include `/pnpm` (should — `ENV PATH="$PNPM_HOME:$PATH"` in Dockerfile).
|
||||
- `ENOENT: no such file or directory, open '/workspace/extra/.gmail-mcp/credentials.json'` → mount is missing. Check `~/.config/nanoclaw/mount-allowlist.json` includes a parent of `~/.gmail-mcp`.
|
||||
- `401 Unauthorized` from `gmail.googleapis.com` → OneCLI isn't injecting. Check the agent's secret mode (`onecli agents secrets --id <agent-id>`) and that the Gmail app is connected (`onecli apps get --provider gmail`).
|
||||
- Agent says "I don't have Gmail tools" → `mcp__gmail__*` wasn't added to `TOOL_ALLOWLIST`, or the agent-runner wasn't rebuilt (image cache — run `./container/build.sh` again with `--no-cache` if suspicious).
|
||||
- Agent says "I don't have Gmail tools" → the `gmail` MCP server isn't registered in this group's `mcpServers` (re-run the `ncl groups config add-mcp-server` step in Phase 3 for that group and restart it), or the agent-runner image is stale (rebuild with `./container/build.sh`, with `--no-cache` if suspicious).
|
||||
|
||||
## Removal
|
||||
|
||||
1. Delete the `"gmail"` entry from `mcpServers` and the `.gmail-mcp` entry from `additionalMounts` in each group's `container.json`.
|
||||
2. Remove `'mcp__gmail__*'` from `TOOL_ALLOWLIST` in `container/agent-runner/src/providers/claude.ts`.
|
||||
1. For each group that had Gmail wired, remove the MCP server from the DB:
|
||||
```bash
|
||||
ncl groups config remove-mcp-server --id <group-id> --name gmail
|
||||
```
|
||||
2. Remove the `.gmail-mcp` mount from the DB (no `remove-mount` verb yet — same #2395 dependency):
|
||||
```bash
|
||||
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
|
||||
SET additional_mounts = (SELECT json_group_array(value) FROM json_each(additional_mounts) \
|
||||
WHERE json_extract(value, '\$.containerPath') != '.gmail-mcp'), \
|
||||
updated_at = datetime('now') \
|
||||
WHERE agent_group_id = '<group-id>';"
|
||||
```
|
||||
3. Remove the `GMAIL_MCP_VERSION` ARG and the `pnpm install -g @gongrzhe/server-gmail-autoauth-mcp` block from `container/Dockerfile`.
|
||||
4. `pnpm run build && ./container/build.sh && systemctl --user restart nanoclaw`.
|
||||
4. `pnpm run build && ./container/build.sh && systemctl --user restart "$(. setup/lib/install-slug.sh && systemd_unit)"`.
|
||||
5. (Optional) `rm -rf ~/.gmail-mcp/` if no other host-side tool needs the stubs.
|
||||
6. (Optional) Disconnect Gmail in OneCLI: `onecli apps disconnect --provider gmail`.
|
||||
|
||||
No `TOOL_ALLOWLIST` removal step — Phase 2 no longer edits it.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Stub format is OneCLI-prescribed.** The `access_token: "onecli-managed"` pattern with `expiry_date: 99999999999999` tells the Google auth client the token is valid; OneCLI intercepts the outgoing Gmail API call and rewrites `Authorization: Bearer onecli-managed` to the real token. `expiry_date: 0` (refresh-interception) is an alternative the OneCLI docs describe — both work but OneCLI's own `migrate` command writes the far-future variant, which is what this skill assumes.
|
||||
|
||||
@@ -75,9 +75,12 @@ If yes, ask the agent to schedule the lint task using the `schedule_task` MCP to
|
||||
|
||||
## Step 6: Restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
Tell the user to test by sending a source to the wiki group.
|
||||
|
||||
@@ -156,7 +156,15 @@ The `platform_id` must be `linear:<TEAM_KEY>` matching the `LINEAR_TEAM_KEY` env
|
||||
|
||||
If you're in the middle of `/setup`, return to the setup flow now.
|
||||
|
||||
Otherwise, restart the service (`systemctl --user restart nanoclaw` or `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`) to pick up the new channel.
|
||||
Otherwise, restart the service to pick up the new channel.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
## Channel Info
|
||||
|
||||
|
||||
@@ -89,9 +89,12 @@ docker run --rm --entrypoint mnemon nanoclaw-agent:latest --version
|
||||
|
||||
### Restart the service
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
systemctl --user restart nanoclaw # Linux
|
||||
# launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
source setup/lib/install-slug.sh
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
# launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
```
|
||||
|
||||
### Confirm mnemon hooks are registered
|
||||
|
||||
@@ -130,12 +130,15 @@ file, not from env vars. This file is bind-mounted into the container as `~/.cla
|
||||
|
||||
## 5. Build and restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
pnpm run build
|
||||
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl unload ~/Library/LaunchAgents/$(launchd_label).plist
|
||||
launchctl load ~/Library/LaunchAgents/$(launchd_label).plist
|
||||
# Linux: systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
## 6. Verify
|
||||
|
||||
@@ -122,9 +122,12 @@ OLLAMA_HOST=http://your-ollama-host:11434
|
||||
|
||||
### Restart the service
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
## Phase 4: Verify
|
||||
|
||||
@@ -229,19 +229,22 @@ echo '{}' | docker run -i --entrypoint /bin/echo nanoclaw-agent:latest "Containe
|
||||
|
||||
### 7. Restart Service
|
||||
|
||||
Rebuild the main app and restart:
|
||||
Rebuild the main app and restart.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# Linux: systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
Wait 3 seconds for service to start, then verify:
|
||||
```bash
|
||||
sleep 3
|
||||
launchctl list | grep nanoclaw # macOS
|
||||
# Linux: systemctl --user status nanoclaw
|
||||
launchctl list | grep "$(. setup/lib/install-slug.sh && launchd_label)" # macOS
|
||||
# Linux: systemctl --user status "$(. setup/lib/install-slug.sh && systemd_unit)"
|
||||
```
|
||||
|
||||
### 8. Test Integration
|
||||
@@ -287,4 +290,4 @@ To remove Parallel AI integration:
|
||||
2. Revert changes to container-runner.ts and agent-runner/src/index.ts
|
||||
3. Remove Web Research Tools section from groups/main/CLAUDE.md
|
||||
4. Rebuild: `./container/build.sh && pnpm run build`
|
||||
5. Restart: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
|
||||
5. Restart: `source setup/lib/install-slug.sh && launchctl kickstart -k gui/$(id -u)/$(launchd_label)` (macOS) or `source setup/lib/install-slug.sh && systemctl --user restart $(systemd_unit)` (Linux)
|
||||
|
||||
@@ -90,17 +90,21 @@ No output = success.
|
||||
|
||||
> ⚠ Stop NanoClaw before running signal-cli commands — the daemon holds an exclusive lock on its data directory while running.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
|
||||
# macOS
|
||||
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
launchctl unload ~/Library/LaunchAgents/$(launchd_label).plist
|
||||
signal-cli -a +1YOURNUMBER updateProfile --name "YourBotName"
|
||||
# optionally: --avatar /path/to/avatar.jpg
|
||||
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
launchctl load ~/Library/LaunchAgents/$(launchd_label).plist
|
||||
|
||||
# Linux
|
||||
systemctl --user stop nanoclaw
|
||||
systemctl --user stop $(systemd_unit)
|
||||
signal-cli -a +1YOURNUMBER updateProfile --name "YourBotName"
|
||||
systemctl --user start nanoclaw
|
||||
systemctl --user start $(systemd_unit)
|
||||
```
|
||||
|
||||
### Path B: Link as secondary device
|
||||
@@ -185,12 +189,16 @@ Sync to container: `mkdir -p data/env && cp .env data/env/env`
|
||||
|
||||
### Restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
|
||||
# macOS
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label)
|
||||
|
||||
# Linux
|
||||
systemctl --user restart nanoclaw
|
||||
systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
## Wiring
|
||||
@@ -283,7 +291,7 @@ If you see `Signal daemon not reachable at 127.0.0.1:7583` and `SIGNAL_MANAGE_DA
|
||||
|
||||
1. Channel initialized: `grep "Signal channel connected" logs/nanoclaw.log | tail -1`
|
||||
2. Channel wired: `pnpm exec tsx scripts/q.ts data/v2.db "SELECT mg.platform_id, mg.name FROM messaging_groups mg JOIN messaging_group_agents mga ON mg.id = mga.messaging_group_id WHERE mg.channel_type='signal'"`
|
||||
3. Service running: `launchctl print gui/$(id -u)/com.nanoclaw` (macOS) / `systemctl --user status nanoclaw` (Linux)
|
||||
3. Service running: `launchctl print gui/$(id -u)/"$(. setup/lib/install-slug.sh && launchd_label)"` (macOS) / `systemctl --user status "$(. setup/lib/install-slug.sh && systemd_unit)"` (Linux)
|
||||
4. **Check for duplicate service instances** — if `logs/nanoclaw.error.log` shows `No adapter for channel type channelType="signal"` despite the adapter starting, two NanoClaw processes are racing. See the `/debug` skill section "No adapter for channel type / Messages silently lost" for the full fix.
|
||||
|
||||
### Messages delivered but never arrive (null platformMsgId)
|
||||
|
||||
@@ -41,9 +41,12 @@ DELETE FROM messaging_groups WHERE channel_type = 'wechat';
|
||||
|
||||
### 6. Rebuild and restart
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
systemctl --user restart nanoclaw # Linux
|
||||
source setup/lib/install-slug.sh
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
# or
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
```
|
||||
|
||||
@@ -82,12 +82,15 @@ Sync to container: `mkdir -p data/env && cp .env data/env/env`
|
||||
|
||||
### 2. Start the service and scan the QR
|
||||
|
||||
Restart NanoClaw:
|
||||
Restart NanoClaw.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
systemctl --user restart nanoclaw # Linux
|
||||
source setup/lib/install-slug.sh
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
# or
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
```
|
||||
|
||||
The adapter will print a **QR URL** to the logs and save it to `data/wechat/qr.txt`:
|
||||
|
||||
@@ -244,12 +244,15 @@ rm -rf store/auth/ && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --met
|
||||
|
||||
### "waiting for this message" on reactions
|
||||
|
||||
Signal sessions corrupted from rapid restarts. Clear sessions:
|
||||
Signal sessions corrupted from rapid restarts. Clear sessions.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
systemctl --user stop nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
systemctl --user stop $(systemd_unit)
|
||||
rm store/auth/session-*.json
|
||||
systemctl --user start nanoclaw
|
||||
systemctl --user start $(systemd_unit)
|
||||
```
|
||||
|
||||
### Bot not responding
|
||||
@@ -257,7 +260,7 @@ systemctl --user start nanoclaw
|
||||
1. Auth exists: `test -f store/auth/creds.json`
|
||||
2. Connected: `grep "Connected to WhatsApp" logs/nanoclaw.log | tail -1`
|
||||
3. Channel wired: `pnpm exec tsx scripts/q.ts data/v2.db "SELECT mg.platform_id, mg.name FROM messaging_groups mg JOIN messaging_group_agents mga ON mg.id=mga.messaging_group_id WHERE mg.channel_type='whatsapp'"`
|
||||
4. Service running: `systemctl --user status nanoclaw`
|
||||
4. Service running: `systemctl --user status "$(. setup/lib/install-slug.sh && systemd_unit)"`
|
||||
|
||||
### "conflict" disconnection
|
||||
|
||||
|
||||
@@ -171,9 +171,12 @@ Expected: Both operations succeed.
|
||||
|
||||
### Full integration test
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label)
|
||||
```
|
||||
|
||||
Send a message via WhatsApp and verify the agent responds.
|
||||
|
||||
@@ -88,15 +88,19 @@ Implementation:
|
||||
|
||||
## After Changes
|
||||
|
||||
Always tell the user:
|
||||
Always tell the user.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
# Rebuild and restart
|
||||
pnpm run build
|
||||
source setup/lib/install-slug.sh
|
||||
# macOS:
|
||||
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||
launchctl unload ~/Library/LaunchAgents/$(launchd_label).plist
|
||||
launchctl load ~/Library/LaunchAgents/$(launchd_label).plist
|
||||
# Linux:
|
||||
# systemctl --user restart nanoclaw
|
||||
# systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
## Example Interaction
|
||||
|
||||
@@ -9,7 +9,7 @@ Stand up the first NanoClaw agent for a channel and verify end-to-end delivery b
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Service running.** Check: `launchctl list | grep nanoclaw` (macOS) or `systemctl --user status nanoclaw` (Linux). If stopped, tell the user to run `/setup` first.
|
||||
- **Service running.** Check: `launchctl list | grep "$(. setup/lib/install-slug.sh && launchd_label)"` (macOS) or `systemctl --user status "$(. setup/lib/install-slug.sh && systemd_unit)"` (Linux). If stopped, tell the user to run `/setup` first.
|
||||
- **Target channel installed.** At least one `/add-<channel>` skill has run, credentials are in `.env`, and the adapter is uncommented in `src/channels/index.ts`.
|
||||
- **Adapter connected.** Tail `logs/nanoclaw.log` — look for a recent `channel setup` / `adapter connected` line for the target channel.
|
||||
|
||||
|
||||
@@ -236,9 +236,12 @@ pnpm run build
|
||||
|
||||
If build fails, diagnose and fix. Common issue: `@onecli-sh/sdk` not installed — run `pnpm install` first.
|
||||
|
||||
Restart the service:
|
||||
- macOS (launchd): `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`
|
||||
- Linux (systemd): `systemctl --user restart nanoclaw`
|
||||
Restart the service.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
- macOS (launchd): `launchctl kickstart -k gui/$(id -u)/"$(. setup/lib/install-slug.sh && launchd_label)"`
|
||||
- Linux (systemd): `systemctl --user restart "$(. setup/lib/install-slug.sh && systemd_unit)"`
|
||||
- WSL/manual: stop and re-run `bash start-nanoclaw.sh`
|
||||
|
||||
## Phase 5: Verify
|
||||
|
||||
@@ -41,7 +41,12 @@ npx tsx setup/index.ts --step mounts --force -- --empty
|
||||
|
||||
## After Changes
|
||||
|
||||
Restart the service so containers pick up the new config:
|
||||
Restart the service so containers pick up the new config (the unit/label names are per-install — see `setup/lib/install-slug.sh`).
|
||||
|
||||
- macOS: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`
|
||||
- Linux: `systemctl --user restart nanoclaw`
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
systemctl --user restart $(systemd_unit) # Linux
|
||||
```
|
||||
|
||||
@@ -270,9 +270,9 @@ Show:
|
||||
Tell the user:
|
||||
- To rollback: `git reset --hard <backup-tag-from-step-1>`
|
||||
- Backup branch also exists: `backup/pre-update-<HASH>-<TIMESTAMP>`
|
||||
- Restart the service to apply changes. Detect platform with `uname -s`:
|
||||
- **macOS (Darwin)**: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`
|
||||
- **Linux**: detect the service name with `systemctl --user list-units --type=service | grep nanoclaw | awk '{print $1}'`, then `systemctl --user restart <detected-name>`
|
||||
- Restart the service to apply changes. The unit/label names are per-install — derive them with `setup/lib/install-slug.sh`. Run from your NanoClaw project root:
|
||||
- **macOS (Darwin)**: `source setup/lib/install-slug.sh && launchctl kickstart -k gui/$(id -u)/$(launchd_label)`
|
||||
- **Linux**: `source setup/lib/install-slug.sh && systemctl --user restart $(systemd_unit)` (or, if you want to confirm the unit name first: `systemctl --user list-units --type=service | grep "$(. setup/lib/install-slug.sh && systemd_unit)"`)
|
||||
- **Manual** (no service found): restart `pnpm run dev`
|
||||
|
||||
|
||||
|
||||
@@ -128,9 +128,12 @@ echo 'ANTHROPIC_API_KEY=<key>' >> .env
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
Then restart the service:
|
||||
- macOS: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw`
|
||||
- Linux: `systemctl --user restart nanoclaw`
|
||||
Then restart the service.
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
- macOS: `launchctl kickstart -k gui/$(id -u)/"$(. setup/lib/install-slug.sh && launchd_label)"`
|
||||
- Linux: `systemctl --user restart "$(. setup/lib/install-slug.sh && systemd_unit)"`
|
||||
- WSL/manual: stop and re-run `bash start-nanoclaw.sh`
|
||||
|
||||
2. Check logs for successful proxy startup:
|
||||
|
||||
@@ -38,6 +38,8 @@ Before using this skill, ensure:
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
# 1. Setup authentication (interactive)
|
||||
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
|
||||
@@ -49,9 +51,10 @@ pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/s
|
||||
|
||||
# 3. Rebuild host and restart service
|
||||
pnpm run build
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
# Verify: launchctl list | grep nanoclaw (macOS) or systemctl --user status nanoclaw (Linux)
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# Linux: systemctl --user restart $(systemd_unit)
|
||||
# Verify: launchctl list | grep "$(launchd_label)" (macOS) or systemctl --user status $(systemd_unit) (Linux)
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -270,16 +273,23 @@ cat data/x-auth.json # Should show {"authenticated": true, ...}
|
||||
|
||||
### 4. Restart Service
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# Linux: systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
**Verify success:**
|
||||
**Verify success.**
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
launchctl list | grep nanoclaw # macOS — should show PID and exit code 0 or -
|
||||
# Linux: systemctl --user status nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl list | grep "$(launchd_label)" # macOS — should show PID and exit code 0 or -
|
||||
# Linux: systemctl --user status $(systemd_unit)
|
||||
```
|
||||
|
||||
## Usage via WhatsApp
|
||||
@@ -343,10 +353,13 @@ echo '{"content":"Test"}' | pnpm exec tsx .claude/skills/x-integration/scripts/p
|
||||
|
||||
### Authentication Expired
|
||||
|
||||
Run from your NanoClaw project root:
|
||||
|
||||
```bash
|
||||
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
||||
# Linux: systemctl --user restart nanoclaw
|
||||
source setup/lib/install-slug.sh
|
||||
launchctl kickstart -k gui/$(id -u)/$(launchd_label) # macOS
|
||||
# Linux: systemctl --user restart $(systemd_unit)
|
||||
```
|
||||
|
||||
### Browser Lock Files
|
||||
|
||||
+14
-1
@@ -2,7 +2,20 @@
|
||||
|
||||
All notable changes to NanoClaw will be documented in this file.
|
||||
|
||||
For detailed release notes, see the [full changelog on the documentation site](https://docs.nanoclaw.dev/changelog).
|
||||
## [2.0.63] - 2026-05-15
|
||||
|
||||
Rollup release covering v2.0.55 through v2.0.63 — everything merged since the v2.0.54 tag. Starting with this release, the goal is to publish a GitHub Release for every `package.json` version bump that lands on `main`; see [RELEASING.md](RELEASING.md).
|
||||
|
||||
- [BREAKING] **Service names are now per-install.** On v2 installs the launchd label and systemd unit are slugged to your project root: `com.nanoclaw.<sha1(projectRoot)[:8]>` and `nanoclaw-<slug>.service`. The old `com.nanoclaw` / `nanoclaw.service` names no longer match a real service — update any copy-pasted restart or status commands. Find your install's names with `source setup/lib/install-slug.sh && launchd_label` (macOS) or `systemd_unit` (Linux). The `ncl` transport-error help text and 26 skill files now use the canonical helper-driven pattern; see [setup/lib/install-slug.sh](setup/lib/install-slug.sh).
|
||||
- **Compaction destination reminder placement fixed.** The reminder injected after SDK auto-compaction now appears at the end of the compaction summary so it isn't stripped during truncation. Replaces the placement shipped in v2.0.54.
|
||||
- **Stronger message-wrapping enforcement.** The poll loop nudges the agent when its output lacks `<message>` wrapping, and `CLAUDE.md` core instructions now require wrapping even for single-destination agents. The welcome flow no longer double-greets.
|
||||
- **OneCLI credentials after MCP install.** MCP servers added through `add_mcp_server` now inherit OneCLI gateway routing — fixes the case where the agent kept asking for API keys after installing a new server.
|
||||
- **CLI scope hardening.** `scopeField` now fails closed when scope is missing, and `sessions get` is guarded against cross-group oracle access from group-scoped agents.
|
||||
- **gmail/gcal skills aligned with v2.** `/add-gmail-tool` and `/add-gcal-tool` now reflect the v2 container-config model — DB-backed mounts, no dead `TOOL_ALLOWLIST` edits, no `container.json` writes that get clobbered on next spawn. Manual sqlite3/JSON1 invocations corrected.
|
||||
- **Repo-rename cleanup.** Remaining `qwibitai/nanoclaw` references swept to `nanocoai/nanoclaw` across code and docs; CI workflow guards updated so they no longer no-op after the rename.
|
||||
- Slack scope checklist now includes `files:read` and `files:write` for skills that read or post attachments.
|
||||
- The internal-tag description in destination instructions no longer mentions scratchpads (which confused agents into routing them incorrectly).
|
||||
- Container startup is now graceful when the `on_wake` column is missing on older sessions DBs.
|
||||
|
||||
## [2.0.54] - 2026-05-10
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Releasing NanoClaw
|
||||
|
||||
Starting with v2.0.63, the goal is to publish a GitHub Release for every `package.json` version bump that lands on `main`. Releases are cut manually by a maintainer, so there can be lag between a bump merging and its release being published. The intent is *timeliness*, not strict 1:1 correlation with every bump.
|
||||
|
||||
Each release ships:
|
||||
|
||||
- A tagged commit on `main` (`vX.Y.Z`).
|
||||
- A `CHANGELOG.md` entry under `## [<version>] - <YYYY-MM-DD>`.
|
||||
- A GitHub Release whose body mirrors the CHANGELOG entry plus a contributors section.
|
||||
|
||||
## When to cut a release
|
||||
|
||||
A release is cut by a maintainer publishing it. The trigger is a `package.json` bump on `main`, but the publish step is manual — there is no fixed schedule, and bumps that land back-to-back may be rolled into a single release (as v2.0.55 through v2.0.63 were). Cutting more frequently is preferable to batching: smaller releases are easier to read, pin, and revert.
|
||||
|
||||
## What goes in a release
|
||||
|
||||
`CHANGELOG.md` is the canonical record of user-visible change. The release body on GitHub mirrors it. Aim for:
|
||||
|
||||
- **Bold lead-ins** per major feature or fix, then a sentence-case prose explanation.
|
||||
- **`[BREAKING]` prefix** for any change that requires user action. Always include the workaround inline — never link to a separate doc for the fix.
|
||||
- **Doc links** for major features (relative paths into the repo, e.g. `[setup/lib/install-slug.sh](setup/lib/install-slug.sh)`).
|
||||
- **Inline commands** for actionable steps, in backticks.
|
||||
- **Minor items** as single plain bullets at the bottom of the entry, no bold lead-in.
|
||||
- **No PR numbers** in the user-facing prose. PR references can live in the GitHub Release's `## Contributors` section.
|
||||
|
||||
## Publishing the release
|
||||
|
||||
1. Bump `package.json` and add a `CHANGELOG.md` entry in the same commit (commit message: `chore: bump version to vX.Y.Z`).
|
||||
2. Once the bump commit lands on `main`, open a draft GitHub Release:
|
||||
- **Tag:** `vX.Y.Z`, target `main`.
|
||||
- **Title:** `vX.Y.Z` (bare version — descriptive content lives in the body, matching the CHANGELOG header pattern).
|
||||
- **Body:** copy the CHANGELOG entry verbatim. Append a `## Contributors` section listing every PR author who landed work in the release window. Append a `**Full Changelog**: https://github.com/nanocoai/nanoclaw/compare/<prev-tag>...vX.Y.Z` line at the bottom.
|
||||
3. If anyone in the window opened their first NanoClaw PR, add a `## New Contributors` section above `## Contributors`, with each first-timer's first PR link and an invite to Discord.
|
||||
4. Publish (not just save draft).
|
||||
|
||||
## Rollup releases
|
||||
|
||||
If multiple `package.json` bumps land between two GitHub Releases (as happened between v2.0.54 and v2.0.63), the next release is a rollup: its CHANGELOG entry covers everything merged since the last released tag, and the body opens with a one-line "Rollup release covering vX.Y.Z through vX.Y.W." note. After the catchup, return to one release per bump.
|
||||
|
||||
## Channels and stability
|
||||
|
||||
NanoClaw currently ships a single channel: every published release is a stable release.
|
||||
|
||||
- **Latest** — the most recent release on `main`, shown as "Latest release" on the GitHub Releases page. Consumers that want auto-bump follow GitHub's `/releases/latest` pointer.
|
||||
- **Stable** — currently identical to latest. NanoClaw has no separate stable branch and no pre-release/RC channel.
|
||||
- **Pinned** — any tagged release. Reproducible and the recommended choice for packagers and forks; published tags are not moved or retracted.
|
||||
|
||||
If a pre-release channel is introduced later (e.g. `vX.Y.Z-rc.N`), those releases will be marked "Pre-release" on GitHub so they do not become the `latest` pointer, and this section will be updated to describe the promotion path.
|
||||
|
||||
The tag is the source of truth — a GitHub Release's `target_commitish` always points to a tagged commit.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nanoclaw",
|
||||
"version": "2.0.62",
|
||||
"version": "2.0.63",
|
||||
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
|
||||
+1
-15
@@ -21,6 +21,7 @@ import { formatResponse } from './format.js';
|
||||
import type { RequestFrame } from './frame.js';
|
||||
import { SocketTransport } from './socket-client.js';
|
||||
import type { Transport } from './transport.js';
|
||||
import { formatTransportError } from './transport-errors.js';
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const argv = process.argv.slice(2);
|
||||
@@ -105,21 +106,6 @@ function printUsage(): void {
|
||||
);
|
||||
}
|
||||
|
||||
function formatTransportError(e: unknown): string {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
if (msg.includes('ENOENT') || msg.includes('ECONNREFUSED')) {
|
||||
return [
|
||||
`ncl: cannot reach NanoClaw host (${msg}).`,
|
||||
`Is the host running? Start it with: pnpm run dev`,
|
||||
`Or, if installed as a service:`,
|
||||
` macOS: launchctl kickstart -k gui/$(id -u)/com.nanoclaw`,
|
||||
` Linux: systemctl --user restart nanoclaw`,
|
||||
``,
|
||||
].join('\n');
|
||||
}
|
||||
return `ncl: transport error: ${msg}\n`;
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
process.stderr.write(`ncl: unexpected error: ${err instanceof Error ? err.message : String(err)}\n`);
|
||||
process.exit(2);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { getLaunchdLabel, getSystemdUnit } from '../install-slug.js';
|
||||
import { formatTransportError } from './transport-errors.js';
|
||||
|
||||
describe('formatTransportError', () => {
|
||||
it('renders per-install service names on ENOENT, not the bare v1 names', () => {
|
||||
const out = formatTransportError(new Error('connect ENOENT /tmp/nanoclaw.sock'));
|
||||
|
||||
// Regression for #2484: pre-fix, this string was a hardcoded
|
||||
// `com.nanoclaw` / `nanoclaw`, which doesn't match the actual
|
||||
// v2 per-install slug-suffixed unit and label.
|
||||
expect(out).toContain(`gui/$(id -u)/${getLaunchdLabel()}`);
|
||||
expect(out).toContain(`systemctl --user restart ${getSystemdUnit()}`);
|
||||
expect(out).not.toMatch(/gui\/\$\(id -u\)\/com\.nanoclaw\b(?!-v2)/);
|
||||
expect(out).not.toMatch(/systemctl --user restart nanoclaw\b(?!-v2)/);
|
||||
});
|
||||
|
||||
it('renders the same on ECONNREFUSED', () => {
|
||||
const out = formatTransportError(new Error('connect ECONNREFUSED'));
|
||||
expect(out).toContain(getLaunchdLabel());
|
||||
expect(out).toContain(getSystemdUnit());
|
||||
});
|
||||
|
||||
it('falls back to a generic transport error for other failures', () => {
|
||||
const out = formatTransportError(new Error('some unrelated failure'));
|
||||
expect(out).toBe('ncl: transport error: some unrelated failure\n');
|
||||
expect(out).not.toContain('launchctl');
|
||||
expect(out).not.toContain('systemctl');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { getLaunchdLabel, getSystemdUnit } from '../install-slug.js';
|
||||
|
||||
export function formatTransportError(e: unknown): string {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
if (msg.includes('ENOENT') || msg.includes('ECONNREFUSED')) {
|
||||
// `bin/ncl` cd's to the project root before exec'ing client.ts, so
|
||||
// process.cwd() is the install dir — install-slug helpers pick up
|
||||
// the right per-checkout suffix.
|
||||
return [
|
||||
`ncl: cannot reach NanoClaw host (${msg}).`,
|
||||
`Is the host running? Start it with: pnpm run dev`,
|
||||
`Or, if installed as a service:`,
|
||||
` macOS: launchctl kickstart -k gui/$(id -u)/${getLaunchdLabel()}`,
|
||||
` Linux: systemctl --user restart ${getSystemdUnit()}`,
|
||||
``,
|
||||
].join('\n');
|
||||
}
|
||||
return `ncl: transport error: ${msg}\n`;
|
||||
}
|
||||
Reference in New Issue
Block a user