From 529d2db8e27f7eb8327ecd221f54082c17b60daf Mon Sep 17 00:00:00 2001 From: glifocat Date: Fri, 15 May 2026 17:03:54 +0200 Subject: [PATCH] docs(skill): fix sqlite3/json invocations in gmail/gcal mount steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues with the DB-edit steps that ship in #2489: - `'$[#]'` was double-quoted in the surrounding bash string, so bash arith-expanded `$#` (positional-arg count, 0 in interactive shell) before sqlite ever saw it — silently overwrote index 0 instead of appending. Now escaped as `'\$[#]'`. - `sqlite3` CLI replaced with `pnpm exec tsx scripts/q.ts` — clean installs have no sqlite3 binary; setup/verify.ts:5 codifies that NanoClaw avoids depending on it. - `strftime('%s','now')` replaced with `datetime('now')` — the column stores ISO strings everywhere else; mixing epoch ints made any consumer doing `datetime(updated_at)` parse those rows as 1970. Also: reworded the "approval-gated" wording to distinguish container vs host-operator-shell invocation, and added the "Why this can't be container.json" note to add-gcal-tool (gmail had it, gcal didn't). --- .claude/skills/add-gcal-tool/SKILL.md | 18 ++++++++++-------- .claude/skills/add-gmail-tool/SKILL.md | 16 ++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.claude/skills/add-gcal-tool/SKILL.md b/.claude/skills/add-gcal-tool/SKILL.md index 03b4ba22e..04cebff2b 100644 --- a/.claude/skills/add-gcal-tool/SKILL.md +++ b/.claude/skills/add-gcal-tool/SKILL.md @@ -145,28 +145,30 @@ ncl groups config add-mcp-server \ --env '{"GOOGLE_OAUTH_CREDENTIALS":"/workspace/extra/.calendar-mcp/gcp-oauth.keys.json","GOOGLE_CALENDAR_MCP_TOKEN_PATH":"/workspace/extra/.calendar-mcp/credentials.json"}' ``` -This is an approval-gated write — an admin must approve before it lands. +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 `.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: +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='' HOST_PATH="$HOME/.calendar-mcp" MOUNT=$(jq -cn --arg h "$HOST_PATH" '{hostPath:$h, containerPath:".calendar-mcp", readonly:false}') -sqlite3 data/v2.db "UPDATE container_configs \ - SET additional_mounts = json_insert(additional_mounts, '$[#]', json('$MOUNT')), \ - updated_at = strftime('%s','now') \ +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). +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/`). +**Why this can't be `groups//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 @@ -211,10 +213,10 @@ Common signals: ``` 2. Remove the `.calendar-mcp` mount from the DB (no `remove-mount` verb yet — same #2395 dependency): ```bash - sqlite3 data/v2.db "UPDATE container_configs \ + 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 = strftime('%s','now') \ + updated_at = datetime('now') \ WHERE agent_group_id = '';" ``` 3. Remove `CALENDAR_MCP_VERSION` ARG and the calendar package from the Dockerfile install block. diff --git a/.claude/skills/add-gmail-tool/SKILL.md b/.claude/skills/add-gmail-tool/SKILL.md index 64ac5c51a..3111cfcf9 100644 --- a/.claude/skills/add-gmail-tool/SKILL.md +++ b/.claude/skills/add-gmail-tool/SKILL.md @@ -164,23 +164,23 @@ ncl groups config add-mcp-server \ --env '{"GMAIL_OAUTH_PATH":"/workspace/extra/.gmail-mcp/gcp-oauth.keys.json","GMAIL_CREDENTIALS_PATH":"/workspace/extra/.gmail-mcp/credentials.json"}' ``` -This is an approval-gated write — an admin must approve before it lands. +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: +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='' HOST_PATH="$HOME/.gmail-mcp" MOUNT=$(jq -cn --arg h "$HOST_PATH" '{hostPath:$h, containerPath:".gmail-mcp", readonly:false}') -sqlite3 data/v2.db "UPDATE container_configs \ - SET additional_mounts = json_insert(additional_mounts, '$[#]', json('$MOUNT')), \ - updated_at = strftime('%s','now') \ +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 appends to the JSON array. +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. @@ -228,10 +228,10 @@ Common signals: ``` 2. Remove the `.gmail-mcp` mount from the DB (no `remove-mount` verb yet — same #2395 dependency): ```bash - sqlite3 data/v2.db "UPDATE container_configs \ + 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 = strftime('%s','now') \ + updated_at = datetime('now') \ WHERE agent_group_id = '';" ``` 3. Remove the `GMAIL_MCP_VERSION` ARG and the `pnpm install -g @gongrzhe/server-gmail-autoauth-mcp` block from `container/Dockerfile`.