Merge pull request #1771 from meeech/feat/v2-pnpm-migration

chore: migrate v2 from npm to pnpm
This commit is contained in:
gavrielc
2026-04-17 09:54:40 +03:00
committed by GitHub
100 changed files with 8508 additions and 12507 deletions

View File

@@ -6,17 +6,17 @@
"allow": [
"Bash(bash setup.sh*)",
"Bash(git remote *)",
"Bash(npx tsx setup/index.ts*)",
"Bash(npx tsx scripts/init-first-agent.ts*)",
"Bash(npm install @chat-adapter/*)",
"Bash(npm install chat-adapter-imessage*)",
"Bash(npm install @bitbasti/chat-adapter-webex*)",
"Bash(npm install @resend/chat-sdk-adapter*)",
"Bash(npm install @whiskeysockets/baileys*)",
"Bash(npm install @beeper/chat-adapter-matrix*)",
"Bash(npm install @nanoco/nanoclaw-dashboard*)",
"Bash(npm ci*)",
"Bash(npm run build*)",
"Bash(pnpm exec tsx setup/index.ts*)",
"Bash(pnpm exec tsx scripts/init-first-agent.ts*)",
"Bash(pnpm install @chat-adapter/*)",
"Bash(pnpm install chat-adapter-imessage*)",
"Bash(pnpm install @bitbasti/chat-adapter-webex*)",
"Bash(pnpm install @resend/chat-sdk-adapter*)",
"Bash(pnpm install @whiskeysockets/baileys*)",
"Bash(pnpm install @beeper/chat-adapter-matrix*)",
"Bash(pnpm install @nanoco/nanoclaw-dashboard*)",
"Bash(pnpm install --frozen-lockfile*)",
"Bash(pnpm run build*)",
"Bash(curl -fsSL onecli.sh*)",
"Bash(onecli *)",
"Bash(grep -q *)",

View File

@@ -39,8 +39,8 @@ This adds:
### Validate
```bash
npm test
npm run build
pnpm test
pnpm run build
```
### Rebuild container
@@ -60,7 +60,7 @@ launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
### Integration Test
1. Start NanoClaw in dev mode: `npm run dev`
1. Start NanoClaw in dev mode: `pnpm run dev`
2. From the **main group** (self-chat), send exactly: `/compact`
3. Verify:
- The agent acknowledges compaction (e.g., "Conversation compacted.")
@@ -104,8 +104,8 @@ launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
git clone <your-fork> /tmp/nanoclaw-test
cd /tmp/nanoclaw-test
claude # then run /add-compact
npm run build
npm test
pnpm run build
pnpm test
./container/build.sh
# Manual: send /compact from main group, verify compaction + continuation
# Manual: send @<assistant> /compact from non-main as non-admin, verify denial

View File

@@ -25,7 +25,7 @@ NanoClaw (pusher) Dashboard (npm package)
### 1. Install the npm package
```bash
npm install @nanoco/nanoclaw-dashboard
pnpm install @nanoco/nanoclaw-dashboard
```
### 2. Copy the pusher module
@@ -94,7 +94,7 @@ Generate the secret: `node -e "console.log('nc-' + require('crypto').randomBytes
### 6. Build and restart
```bash
npm run build
pnpm run build
systemctl --user restart nanoclaw # Linux
# or: launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
```
@@ -130,9 +130,9 @@ Open `http://localhost:3100/dashboard` in a browser.
## Removal
```bash
npm uninstall @nanoco/nanoclaw-dashboard
pnpm uninstall @nanoco/nanoclaw-dashboard
rm src/dashboard-pusher.ts
# Remove the dashboard block from src/index.ts
# Remove DASHBOARD_SECRET and DASHBOARD_PORT from .env
npm run build
pnpm run build
```

View File

@@ -26,7 +26,7 @@ import './discord.js';
### Build
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -40,8 +40,8 @@ git remote add discord https://github.com/qwibitai/nanoclaw-discord.git
```bash
git fetch discord main
git merge discord/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -58,9 +58,9 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/channels/discord.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/channels/discord.test.ts
```
All tests must pass (including the new Discord tests) and build must be clean before proceeding.
@@ -108,7 +108,7 @@ The container reads environment from `data/env/env`, not `.env` directly.
### Build and restart
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
```
@@ -130,18 +130,18 @@ Wait for the user to provide the channel ID (format: `dc:1234567890123456`).
### Register the channel
The channel ID, name, and folder name are needed. Use `npx tsx setup/index.ts --step register` with the appropriate flags.
The channel ID, name, and folder name are needed. Use `pnpm exec tsx setup/index.ts --step register` with the appropriate flags.
For a main channel (responds to all messages):
```bash
npx tsx setup/index.ts --step register -- --jid "dc:<channel-id>" --name "<server-name> #<channel-name>" --folder "discord_main" --trigger "@${ASSISTANT_NAME}" --channel discord --no-trigger-required --is-main
pnpm exec tsx setup/index.ts --step register -- --jid "dc:<channel-id>" --name "<server-name> #<channel-name>" --folder "discord_main" --trigger "@${ASSISTANT_NAME}" --channel discord --no-trigger-required --is-main
```
For additional channels (trigger-only):
```bash
npx tsx setup/index.ts --step register -- --jid "dc:<channel-id>" --name "<server-name> #<channel-name>" --folder "discord_<channel-name>" --trigger "@${ASSISTANT_NAME}" --channel discord
pnpm exec tsx setup/index.ts --step register -- --jid "dc:<channel-id>" --name "<server-name> #<channel-name>" --folder "discord_<channel-name>" --trigger "@${ASSISTANT_NAME}" --channel discord
```
## Phase 5: Verify

View File

@@ -51,12 +51,12 @@ git fetch upstream skill/emacs
git merge upstream/skill/emacs
```
If there are merge conflicts on `package-lock.json`, resolve them by accepting the incoming
If there are merge conflicts on `pnpm-lock.yaml`, resolve them by accepting the incoming
version and continuing:
```bash
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
```
@@ -73,8 +73,8 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm run build
npx vitest run src/channels/emacs.test.ts
pnpm run build
pnpm exec vitest run src/channels/emacs.test.ts
```
Build must be clean and tests must pass before proceeding.
@@ -158,7 +158,7 @@ If `EMACS_CHANNEL_PORT` was changed from the default, also add:
### Restart NanoClaw
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```
@@ -246,18 +246,18 @@ If NanoClaw is cloned elsewhere, update the `load`/`load-file` path in your Emac
## After Setup
If running `npm run dev` while the service is active:
If running `pnpm run dev` while the service is active:
```bash
# macOS:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
npm run dev
pnpm run dev
# When done testing:
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
# Linux:
# systemctl --user stop nanoclaw
# npm run dev
# pnpm run dev
# systemctl --user start nanoclaw
```
@@ -286,4 +286,4 @@ To remove the Emacs channel:
3. Remove the NanoClaw block from your Emacs config file
4. Remove Emacs registration from SQLite: `sqlite3 store/messages.db "DELETE FROM registered_groups WHERE jid = 'emacs:default'"`
5. Remove `EMACS_CHANNEL_PORT` and `EMACS_AUTH_TOKEN` from `.env` if set
6. Rebuild: `npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `npm run build && systemctl --user restart nanoclaw` (Linux)
6. Rebuild: `pnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `pnpm run build && systemctl --user restart nanoclaw` (Linux)

View File

@@ -2,5 +2,5 @@
1. Comment out `import './gchat.js'` in `src/channels/index.ts`
2. Remove `GCHAT_CREDENTIALS` from `.env`
3. `npm uninstall @chat-adapter/gchat`
3. `pnpm uninstall @chat-adapter/gchat`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/gchat.ts` exists and the import is uncommented in `src/ch
## Install
```bash
npm install @chat-adapter/gchat
pnpm install @chat-adapter/gchat
```
Uncomment the Google Chat import in `src/channels/index.ts`:
@@ -24,7 +24,7 @@ import './gchat.js';
```
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -2,5 +2,5 @@
1. Comment out `import './github.js'` in `src/channels/index.ts`
2. Remove `GITHUB_TOKEN` and `GITHUB_WEBHOOK_SECRET` from `.env`
3. `npm uninstall @chat-adapter/github`
3. `pnpm uninstall @chat-adapter/github`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/github.ts` exists and the import is uncommented in `src/c
## Install
```bash
npm install @chat-adapter/github
pnpm install @chat-adapter/github
```
Uncomment the GitHub import in `src/channels/index.ts`:
@@ -24,7 +24,7 @@ import './github.js';
```
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -41,8 +41,8 @@ git remote add gmail https://github.com/qwibitai/nanoclaw-gmail.git
```bash
git fetch gmail main
git merge gmail/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -70,9 +70,9 @@ When you receive an email notification (messages starting with `[Email from ...`
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/channels/gmail.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/channels/gmail.test.ts
```
All tests must pass (including the new Gmail tests) and build must be clean before proceeding.
@@ -136,10 +136,10 @@ Tell the user:
Run the authorization:
```bash
npx -y @gongrzhe/server-gmail-autoauth-mcp auth
pnpm dlx @gongrzhe/server-gmail-autoauth-mcp auth
```
If that fails (some versions don't have an auth subcommand), try `timeout 60 npx -y @gongrzhe/server-gmail-autoauth-mcp || true`. Verify with `ls ~/.gmail-mcp/credentials.json`.
If that fails (some versions don't have an auth subcommand), try `timeout 60 pnpm dlx @gongrzhe/server-gmail-autoauth-mcp || true`. Verify with `ls ~/.gmail-mcp/credentials.json`.
### Build and restart
@@ -158,7 +158,7 @@ cd container && ./build.sh
Then compile and restart:
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```
@@ -192,7 +192,7 @@ tail -f logs/nanoclaw.log
Test directly:
```bash
npx -y @gongrzhe/server-gmail-autoauth-mcp
pnpm dlx @gongrzhe/server-gmail-autoauth-mcp
```
### OAuth token expired
@@ -201,7 +201,7 @@ Re-authorize:
```bash
rm ~/.gmail-mcp/credentials.json
npx -y @gongrzhe/server-gmail-autoauth-mcp
pnpm dlx @gongrzhe/server-gmail-autoauth-mcp
```
### Container can't access Gmail
@@ -222,7 +222,7 @@ npx -y @gongrzhe/server-gmail-autoauth-mcp
2. Remove `gmail` MCP server and `mcp__gmail__*` from `container/agent-runner/src/index.ts`
3. Rebuild and restart
4. Clear stale agent-runner copies: `rm -r data/sessions/*/agent-runner-src 2>/dev/null || true`
5. Rebuild: `cd container && ./build.sh && cd .. && npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
5. Rebuild: `cd container && ./build.sh && cd .. && pnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
### Channel mode
@@ -230,7 +230,7 @@ npx -y @gongrzhe/server-gmail-autoauth-mcp
2. Remove `import './gmail.js'` from `src/channels/index.ts`
3. Remove `~/.gmail-mcp` mount from `src/container-runner.ts`
4. Remove `gmail` MCP server and `mcp__gmail__*` from `container/agent-runner/src/index.ts`
5. Uninstall: `npm uninstall googleapis`
5. Uninstall: `pnpm uninstall googleapis`
6. Rebuild and restart
7. Clear stale agent-runner copies: `rm -r data/sessions/*/agent-runner-src 2>/dev/null || true`
8. Rebuild: `cd container && ./build.sh && cd .. && npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
8. Rebuild: `cd container && ./build.sh && cd .. && pnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)

View File

@@ -33,8 +33,8 @@ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
```bash
git fetch whatsapp skill/image-vision
git merge whatsapp/skill/image-vision || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -52,9 +52,9 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/image.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/image.test.ts
```
All tests must pass and build must be clean before proceeding.
@@ -90,5 +90,5 @@ All tests must pass and build must be clean before proceeding.
## Troubleshooting
- **"Image - download failed"**: Check WhatsApp connection stability. The download may timeout on slow connections.
- **"Image - processing failed"**: Sharp may not be installed correctly. Run `npm ls sharp` to verify.
- **"Image - processing failed"**: Sharp may not be installed correctly. Run `pnpm ls sharp` to verify.
- **Agent doesn't mention image content**: Check container logs for "Loaded image" messages. If missing, ensure agent-runner source was synced to group caches.

View File

@@ -2,5 +2,5 @@
1. Comment out `import './imessage.js'` in `src/channels/index.ts`
2. Remove iMessage env vars (`IMESSAGE_ENABLED`, `IMESSAGE_LOCAL`, `IMESSAGE_SERVER_URL`, `IMESSAGE_API_KEY`) from `.env`
3. `npm uninstall chat-adapter-imessage`
3. `pnpm uninstall chat-adapter-imessage`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/imessage.ts` exists and the import is uncommented in `src
## Install
```bash
npm install chat-adapter-imessage
pnpm install chat-adapter-imessage
```
Uncomment the iMessage import in `src/channels/index.ts`:
@@ -24,7 +24,7 @@ import './imessage.js';
```
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -19,7 +19,7 @@ AskUserQuestion: "Which group should have the wiki?"
2. **Dedicated group** — create a new group just for the wiki
3. **Other** — pick an existing group
If dedicated: ask which channel and chat, then register with `npx tsx setup/index.ts --step register`.
If dedicated: ask which channel and chat, then register with `pnpm exec tsx setup/index.ts --step register`.
## Step 3: Design collaboratively
@@ -74,7 +74,7 @@ AskUserQuestion: "Want periodic wiki health checks?"
If yes, create a NanoClaw scheduled task that runs in the wiki group. This is NOT a Claude Code cron job — it's a NanoClaw group task that runs in the agent container. Insert it into the SQLite database:
```bash
npx tsx -e "
pnpm exec tsx -e "
const Database = require('better-sqlite3');
const { CronExpressionParser } = require('cron-parser');
const db = new Database('store/messages.db');
@@ -101,7 +101,7 @@ Use the group's `folder` and `chat_jid` from the registered groups table. Cron e
## Step 6: Build and restart
```bash
npm run build
pnpm run build
./container/build.sh
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw

View File

@@ -2,5 +2,5 @@
1. Comment out `import './linear.js'` in `src/channels/index.ts`
2. Remove `LINEAR_API_KEY` and `LINEAR_WEBHOOK_SECRET` from `.env`
3. `npm uninstall @chat-adapter/linear`
3. `pnpm uninstall @chat-adapter/linear`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/linear.ts` exists and the import is uncommented in `src/c
## Install
```bash
npm install @chat-adapter/linear
pnpm install @chat-adapter/linear
```
Uncomment the Linear import in `src/channels/index.ts`:
@@ -24,7 +24,7 @@ import './linear.js';
```
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -2,5 +2,5 @@
1. Comment out `import './matrix.js'` in `src/channels/index.ts`
2. Remove `MATRIX_BASE_URL`, `MATRIX_ACCESS_TOKEN`, `MATRIX_USER_ID`, `MATRIX_BOT_USERNAME` from `.env`
3. `npm uninstall @beeper/chat-adapter-matrix`
3. `pnpm uninstall @beeper/chat-adapter-matrix`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/matrix.ts` exists and the import is uncommented in `src/c
## Install
```bash
npm install @beeper/chat-adapter-matrix
pnpm install @beeper/chat-adapter-matrix
```
Uncomment the Matrix import in `src/channels/index.ts`:
@@ -24,7 +24,7 @@ import './matrix.js';
```
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -87,7 +87,7 @@ done
### Validate code changes
```bash
npm run build
pnpm run build
./container/build.sh
```

View File

@@ -232,7 +232,7 @@ echo '{}' | docker run -i --entrypoint /bin/echo nanoclaw-agent:latest "Containe
Rebuild the main app and restart:
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```
@@ -286,5 +286,5 @@ To remove Parallel AI integration:
1. Remove from .env: `sed -i.bak '/PARALLEL_API_KEY/d' .env`
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 && npm run build`
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)

View File

@@ -31,8 +31,8 @@ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
```bash
git fetch whatsapp skill/pdf-reader
git merge whatsapp/skill/pdf-reader || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -49,8 +49,8 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate
```bash
npm run build
npx vitest run src/channels/whatsapp.test.ts
pnpm run build
pnpm exec vitest run src/channels/whatsapp.test.ts
```
### Rebuild container

View File

@@ -38,8 +38,8 @@ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
```bash
git fetch whatsapp skill/reactions
git merge whatsapp/skill/reactions || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -54,14 +54,14 @@ This adds:
### Run database migration
```bash
npx tsx scripts/migrate-reactions.ts
pnpm exec tsx scripts/migrate-reactions.ts
```
### Validate code changes
```bash
npm test
npm run build
pnpm test
pnpm run build
```
All tests must pass and build must be clean before proceeding.
@@ -71,7 +71,7 @@ All tests must pass and build must be clean before proceeding.
### Build and restart
```bash
npm run build
pnpm run build
```
Linux:

View File

@@ -2,5 +2,5 @@
1. Comment out `import './resend.js'` in `src/channels/index.ts`
2. Remove `RESEND_API_KEY`, `RESEND_FROM_ADDRESS`, `RESEND_FROM_NAME`, `RESEND_WEBHOOK_SECRET` from `.env`
3. `npm uninstall @resend/chat-sdk-adapter`
3. `pnpm uninstall @resend/chat-sdk-adapter`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/resend.ts` exists and the import is uncommented in `src/c
## Install
```bash
npm install @resend/chat-sdk-adapter
pnpm install @resend/chat-sdk-adapter
```
Uncomment the Resend import in `src/channels/index.ts`:
@@ -26,7 +26,7 @@ import './resend.js';
Build:
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -2,5 +2,5 @@
1. Comment out `import './slack.js'` in `src/channels/index.ts`
2. Remove `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` from `.env`
3. `npm uninstall @chat-adapter/slack`
3. `pnpm uninstall @chat-adapter/slack`
4. Rebuild and restart

View File

@@ -16,7 +16,7 @@ Check if `src/channels/slack.ts` exists and the import is uncommented in `src/ch
### Install the adapter package
```bash
npm install @chat-adapter/slack
pnpm install @chat-adapter/slack
```
### Enable the channel
@@ -30,7 +30,7 @@ import './slack.js';
### Build
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -36,8 +36,8 @@ git remote add slack https://github.com/qwibitai/nanoclaw-slack.git
```bash
git fetch slack main
git merge slack/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -54,9 +54,9 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/channels/slack.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/channels/slack.test.ts
```
All tests must pass (including the new Slack tests) and build must be clean before proceeding.
@@ -98,7 +98,7 @@ The container reads environment from `data/env/env`, not `.env` directly.
### Build and restart
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
```
@@ -118,18 +118,18 @@ Wait for the user to provide the channel ID.
### Register the channel
The channel ID, name, and folder name are needed. Use `npx tsx setup/index.ts --step register` with the appropriate flags.
The channel ID, name, and folder name are needed. Use `pnpm exec tsx setup/index.ts --step register` with the appropriate flags.
For a main channel (responds to all messages):
```bash
npx tsx setup/index.ts --step register -- --jid "slack:<channel-id>" --name "<channel-name>" --folder "slack_main" --trigger "@${ASSISTANT_NAME}" --channel slack --no-trigger-required --is-main
pnpm exec tsx setup/index.ts --step register -- --jid "slack:<channel-id>" --name "<channel-name>" --folder "slack_main" --trigger "@${ASSISTANT_NAME}" --channel slack --no-trigger-required --is-main
```
For additional channels (trigger-only):
```bash
npx tsx setup/index.ts --step register -- --jid "slack:<channel-id>" --name "<channel-name>" --folder "slack_<channel-name>" --trigger "@${ASSISTANT_NAME}" --channel slack
pnpm exec tsx setup/index.ts --step register -- --jid "slack:<channel-id>" --name "<channel-name>" --folder "slack_<channel-name>" --trigger "@${ASSISTANT_NAME}" --channel slack
```
## Phase 5: Verify

View File

@@ -2,5 +2,5 @@
1. Comment out `import './teams.js'` in `src/channels/index.ts`
2. Remove `TEAMS_APP_ID` and `TEAMS_APP_PASSWORD` from `.env`
3. `npm uninstall @chat-adapter/teams`
3. `pnpm uninstall @chat-adapter/teams`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/teams.ts` exists and the import is uncommented in `src/ch
## Install
```bash
npm install @chat-adapter/teams
pnpm install @chat-adapter/teams
```
Uncomment the Teams import in `src/channels/index.ts`:
@@ -26,7 +26,7 @@ import './teams.js';
Build:
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -319,7 +319,7 @@ Also add `TELEGRAM_BOT_POOL` to the launchd plist (`~/Library/LaunchAgents/com.n
### Step 7: Rebuild and Restart
```bash
npm run build
pnpm run build
./container/build.sh # Required — MCP tool changed
# macOS:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
@@ -381,4 +381,4 @@ To remove Agent Swarm support while keeping basic Telegram:
5. Remove `sender` param from MCP tool in `container/agent-runner/src/ipc-mcp-stdio.ts`
6. Remove Agent Teams section from group CLAUDE.md files
7. Remove `TELEGRAM_BOT_POOL` from `.env`, `data/env/env`, and launchd plist/systemd unit
8. Rebuild: `npm run build && ./container/build.sh && launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist && launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist` (macOS) or `npm run build && ./container/build.sh && systemctl --user restart nanoclaw` (Linux)
8. Rebuild: `pnpm run build && ./container/build.sh && launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist && launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist` (macOS) or `pnpm run build && ./container/build.sh && systemctl --user restart nanoclaw` (Linux)

View File

@@ -2,5 +2,5 @@
1. Comment out `import './telegram.js'` in `src/channels/index.ts`
2. Remove `TELEGRAM_BOT_TOKEN` from `.env`
3. `npm uninstall @chat-adapter/telegram`
3. `pnpm uninstall @chat-adapter/telegram`
4. Rebuild and restart

View File

@@ -16,7 +16,7 @@ Check if `src/channels/telegram.ts` exists and the import is uncommented in `src
### Install the adapter package
```bash
npm install @chat-adapter/telegram
pnpm install @chat-adapter/telegram
```
### Enable the channel
@@ -30,7 +30,7 @@ import './telegram.js';
### Build
```bash
npm run build
pnpm run build
```
## Credentials
@@ -68,7 +68,7 @@ Otherwise, run `/manage-channels` to wire this channel to an agent group.
- **type**: `telegram`
- **terminology**: Telegram calls them "groups" and "chats." A "group" has multiple members; a "chat" is a 1:1 conversation with the bot.
- **how-to-find-id**: Do NOT ask the user for a chat ID. Telegram registration uses pairing — run `npx tsx setup/index.ts --step pair-telegram -- --intent <main|wire-to:folder|new-agent:folder>`, show the user the 4-digit `CODE` from the `PAIR_TELEGRAM_ISSUED` block (follow the `REMINDER_TO_ASSISTANT` line in that block), and tell them to send just the 4 digits as a message from the chat they want to register (DM the bot for `main`, post in the group otherwise). In groups with Group Privacy ON, prefix with the bot handle: `@<botname> CODE`. Wrong guesses invalidate the code — if a `PAIR_TELEGRAM_ATTEMPT` block arrives with a mismatched `RECEIVED_CODE`, a `PAIR_TELEGRAM_NEW_CODE` block will follow automatically (up to 5 regenerations); show the new code. On `PAIR_TELEGRAM STATUS=failed ERROR=max-regenerations-exceeded`, ask the user if they want to try again and re-invoke the step — each invocation starts a fresh 5-attempt batch. Success emits `PAIR_TELEGRAM STATUS=success` with `PLATFORM_ID`, `IS_GROUP`, and `ADMIN_USER_ID`. The service must be running for this to work (the polling adapter is what observes the code).
- **how-to-find-id**: Do NOT ask the user for a chat ID. Telegram registration uses pairing — run `pnpm exec tsx setup/index.ts --step pair-telegram -- --intent <main|wire-to:folder|new-agent:folder>`, show the user the 4-digit `CODE` from the `PAIR_TELEGRAM_ISSUED` block (follow the `REMINDER_TO_ASSISTANT` line in that block), and tell them to send just the 4 digits as a message from the chat they want to register (DM the bot for `main`, post in the group otherwise). In groups with Group Privacy ON, prefix with the bot handle: `@<botname> CODE`. Wrong guesses invalidate the code — if a `PAIR_TELEGRAM_ATTEMPT` block arrives with a mismatched `RECEIVED_CODE`, a `PAIR_TELEGRAM_NEW_CODE` block will follow automatically (up to 5 regenerations); show the new code. On `PAIR_TELEGRAM STATUS=failed ERROR=max-regenerations-exceeded`, ask the user if they want to try again and re-invoke the step — each invocation starts a fresh 5-attempt batch. Success emits `PAIR_TELEGRAM STATUS=success` with `PLATFORM_ID`, `IS_GROUP`, and `ADMIN_USER_ID`. The service must be running for this to work (the polling adapter is what observes the code).
- **supports-threads**: no
- **typical-use**: Interactive chat — direct messages or small groups
- **default-isolation**: Same agent group if you're the only participant across multiple chats. Separate agent group if different people are in different groups.

View File

@@ -40,8 +40,8 @@ git remote add telegram https://github.com/qwibitai/nanoclaw-telegram.git
```bash
git fetch telegram main
git merge telegram/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -58,9 +58,9 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/channels/telegram.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/channels/telegram.test.ts
```
All tests must pass (including the new Telegram tests) and build must be clean before proceeding.
@@ -114,7 +114,7 @@ Tell the user:
### Build and restart
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```
@@ -133,18 +133,18 @@ Wait for the user to provide the chat ID (format: `tg:123456789` or `tg:-1001234
### Register the chat
The chat ID, name, and folder name are needed. Use `npx tsx setup/index.ts --step register` with the appropriate flags.
The chat ID, name, and folder name are needed. Use `pnpm exec tsx setup/index.ts --step register` with the appropriate flags.
For a main chat (responds to all messages):
```bash
npx tsx setup/index.ts --step register -- --jid "tg:<chat-id>" --name "<chat-name>" --folder "telegram_main" --trigger "@${ASSISTANT_NAME}" --channel telegram --no-trigger-required --is-main
pnpm exec tsx setup/index.ts --step register -- --jid "tg:<chat-id>" --name "<chat-name>" --folder "telegram_main" --trigger "@${ASSISTANT_NAME}" --channel telegram --no-trigger-required --is-main
```
For additional chats (trigger-only):
```bash
npx tsx setup/index.ts --step register -- --jid "tg:<chat-id>" --name "<chat-name>" --folder "telegram_<group-name>" --trigger "@${ASSISTANT_NAME}" --channel telegram
pnpm exec tsx setup/index.ts --step register -- --jid "tg:<chat-id>" --name "<chat-name>" --folder "telegram_<group-name>" --trigger "@${ASSISTANT_NAME}" --channel telegram
```
## Phase 5: Verify
@@ -189,16 +189,16 @@ If `/chatid` doesn't work:
## After Setup
If running `npm run dev` while the service is active:
If running `pnpm run dev` while the service is active:
```bash
# macOS:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
npm run dev
pnpm run dev
# When done testing:
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
# Linux:
# systemctl --user stop nanoclaw
# npm run dev
# pnpm run dev
# systemctl --user start nanoclaw
```
@@ -210,5 +210,5 @@ To remove Telegram integration:
2. Remove `import './telegram.js'` from `src/channels/index.ts`
3. Remove `TELEGRAM_BOT_TOKEN` from `.env`
4. Remove Telegram registrations from SQLite: `sqlite3 store/messages.db "DELETE FROM registered_groups WHERE jid LIKE 'tg:%'"`
5. Uninstall: `npm uninstall grammy`
6. Rebuild: `npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `npm run build && systemctl --user restart nanoclaw` (Linux)
5. Uninstall: `pnpm uninstall grammy`
6. Rebuild: `pnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `pnpm run build && systemctl --user restart nanoclaw` (Linux)

View File

@@ -42,8 +42,8 @@ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
```bash
git fetch whatsapp skill/voice-transcription
git merge whatsapp/skill/voice-transcription || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -60,9 +60,9 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm install --legacy-peer-deps
npm run build
npx vitest run src/channels/whatsapp.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/channels/whatsapp.test.ts
```
All tests must pass and build must be clean before proceeding.
@@ -103,7 +103,7 @@ The container reads environment from `data/env/env`, not `.env` directly.
### Build and restart
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```

View File

@@ -2,5 +2,5 @@
1. Comment out `import './webex.js'` in `src/channels/index.ts`
2. Remove `WEBEX_BOT_TOKEN` and `WEBEX_WEBHOOK_SECRET` from `.env`
3. `npm uninstall @bitbasti/chat-adapter-webex`
3. `pnpm uninstall @bitbasti/chat-adapter-webex`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/webex.ts` exists and the import is uncommented in `src/ch
## Install
```bash
npm install @bitbasti/chat-adapter-webex
pnpm install @bitbasti/chat-adapter-webex
```
Uncomment the Webex import in `src/channels/index.ts`:
@@ -24,7 +24,7 @@ import './webex.js';
```
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -2,5 +2,5 @@
1. Comment out `import './whatsapp-cloud.js'` in `src/channels/index.ts`
2. Remove `WHATSAPP_ACCESS_TOKEN`, `WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_APP_SECRET`, `WHATSAPP_VERIFY_TOKEN` from `.env`
3. `npm uninstall @chat-adapter/whatsapp`
3. `pnpm uninstall @chat-adapter/whatsapp`
4. Rebuild and restart

View File

@@ -14,7 +14,7 @@ Check if `src/channels/whatsapp-cloud.ts` exists and the import is uncommented i
## Install
```bash
npm install @chat-adapter/whatsapp
pnpm install @chat-adapter/whatsapp
```
Uncomment the WhatsApp Cloud API import in `src/channels/index.ts`:
@@ -26,7 +26,7 @@ import './whatsapp-cloud.js';
Build:
```bash
npm run build
pnpm run build
```
## Credentials

View File

@@ -16,7 +16,7 @@ Check if `src/channels/whatsapp.ts` exists and the import is uncommented in `src
### Install the adapter packages
```bash
npm install @whiskeysockets/baileys@^6.7.21 pino@^9.6.0 qrcode@^1.5.4 @types/qrcode@^1.5.6
pnpm install @whiskeysockets/baileys@^6.7.21 pino@^9.6.0 qrcode@^1.5.4 @types/qrcode@^1.5.6
```
### Enable the channel
@@ -39,7 +39,7 @@ import './whatsapp.js';
### Build
```bash
npm run build
pnpm run build
```
## Credentials
@@ -90,7 +90,7 @@ rm -rf store/auth/
For QR code in browser (recommended):
```bash
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
```
(Bash timeout: 150000ms)
@@ -106,7 +106,7 @@ Tell the user:
For QR code in terminal:
```bash
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
```
(Bash timeout: 150000ms)
@@ -123,7 +123,7 @@ Tell the user to have WhatsApp open on **Settings > Linked Devices > Link a Devi
Run the auth process in the background and poll `store/pairing-code.txt` for the code:
```bash
rm -f store/pairing-code.txt && npx tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
rm -f store/pairing-code.txt && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
```
Then immediately poll for the code (do NOT wait for the background command to finish):
@@ -199,7 +199,7 @@ Not supported (WhatsApp linked device limitation): edit messages, delete message
QR codes expire after ~60 seconds. Re-run the auth command:
```bash
rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
rm -rf store/auth/ && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
```
### Pairing code not working
@@ -207,7 +207,7 @@ rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr
Codes expire in ~60 seconds. Delete auth and retry:
```bash
rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <phone>
rm -rf store/auth/ && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <phone>
```
Ensure: digits only (no `+`), phone has internet, WhatsApp is updated.
@@ -215,7 +215,7 @@ Ensure: digits only (no `+`), phone has internet, WhatsApp is updated.
If pairing code keeps failing, switch to QR-browser auth instead:
```bash
rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
rm -rf store/auth/ && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
```
### "waiting for this message" on reactions

View File

@@ -63,8 +63,8 @@ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
```bash
git fetch whatsapp main
git merge whatsapp/main || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -84,9 +84,9 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/channels/whatsapp.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/channels/whatsapp.test.ts
```
All tests must pass and build must be clean before proceeding.
@@ -104,7 +104,7 @@ rm -rf store/auth/
For QR code in browser (recommended):
```bash
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
```
(Bash timeout: 150000ms)
@@ -120,10 +120,10 @@ Tell the user:
For QR code in terminal:
```bash
npx tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
```
Tell the user to run `npm run auth` in another terminal, then:
Tell the user to run `pnpm run auth` in another terminal, then:
> 1. Open WhatsApp > **Settings** > **Linked Devices** > **Link a Device**
> 2. Scan the QR code displayed in the terminal
@@ -135,7 +135,7 @@ Tell the user to have WhatsApp open on **Settings > Linked Devices > Link a Devi
Run the auth process in the background and poll `store/pairing-code.txt` for the code:
```bash
rm -f store/pairing-code.txt && npx tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
rm -f store/pairing-code.txt && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
```
Then immediately poll for the code (do NOT wait for the background command to finish):
@@ -221,8 +221,8 @@ node -e "const c=JSON.parse(require('fs').readFileSync('store/auth/creds.json','
**Group (solo, existing):** Run group sync and list available groups:
```bash
npx tsx setup/index.ts --step groups
npx tsx setup/index.ts --step groups --list
pnpm exec tsx setup/index.ts --step groups
pnpm exec tsx setup/index.ts --step groups --list
```
The output shows `JID|GroupName` pairs. Present candidates as AskUserQuestion (names only, not JIDs).
@@ -230,7 +230,7 @@ The output shows `JID|GroupName` pairs. Present candidates as AskUserQuestion (n
### Register the chat
```bash
npx tsx setup/index.ts --step register \
pnpm exec tsx setup/index.ts --step register \
--jid "<jid>" \
--name "<chat-name>" \
--trigger "@<trigger>" \
@@ -244,7 +244,7 @@ npx tsx setup/index.ts --step register \
For additional groups (trigger-required):
```bash
npx tsx setup/index.ts --step register \
pnpm exec tsx setup/index.ts --step register \
--jid "<group-jid>" \
--name "<group-name>" \
--trigger "@<trigger>" \
@@ -257,7 +257,7 @@ npx tsx setup/index.ts --step register \
### Build and restart
```bash
npm run build
pnpm run build
```
Restart the service:
@@ -296,7 +296,7 @@ tail -f logs/nanoclaw.log
QR codes expire after ~60 seconds. Re-run the auth command:
```bash
rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts
rm -rf store/auth/ && pnpm exec tsx src/whatsapp-auth.ts
```
### Pairing code not working
@@ -304,7 +304,7 @@ rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts
Codes expire in ~60 seconds. To retry:
```bash
rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts --pairing-code --phone <phone>
rm -rf store/auth/ && pnpm exec tsx src/whatsapp-auth.ts --pairing-code --phone <phone>
```
Enter the code **immediately** when it appears. Also ensure:
@@ -315,7 +315,7 @@ Enter the code **immediately** when it appears. Also ensure:
If pairing code keeps failing, switch to QR-browser auth instead:
```bash
rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
rm -rf store/auth/ && pnpm exec tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
```
### "conflict" disconnection
@@ -340,25 +340,25 @@ Check:
Run group metadata sync:
```bash
npx tsx setup/index.ts --step groups
pnpm exec tsx setup/index.ts --step groups
```
This fetches all group names from WhatsApp. Runs automatically every 24 hours.
## After Setup
If running `npm run dev` while the service is active:
If running `pnpm run dev` while the service is active:
```bash
# macOS:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
npm run dev
pnpm run dev
# When done testing:
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
# Linux:
# systemctl --user stop nanoclaw
# npm run dev
# pnpm run dev
# systemctl --user start nanoclaw
```
@@ -369,4 +369,4 @@ To remove WhatsApp integration:
1. Delete auth credentials: `rm -rf store/auth/`
2. Remove WhatsApp registrations: `sqlite3 store/messages.db "DELETE FROM registered_groups WHERE jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"`
3. Sync env: `mkdir -p data/env && cp .env data/env/env`
4. Rebuild and restart: `npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `npm run build && systemctl --user restart nanoclaw` (Linux)
4. Rebuild and restart: `pnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `pnpm run build && systemctl --user restart nanoclaw` (Linux)

View File

@@ -51,12 +51,12 @@ git fetch upstream skill/channel-formatting
git merge upstream/skill/channel-formatting
```
If there are merge conflicts on `package-lock.json`, resolve them by accepting the incoming
If there are merge conflicts on `pnpm-lock.yaml`, resolve them by accepting the incoming
version and continuing:
```bash
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
```
@@ -74,9 +74,9 @@ This merge adds:
### Validate
```bash
npm install
npm run build
npx vitest run src/formatting.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/formatting.test.ts
```
All 73 tests should pass and the build should be clean before continuing.
@@ -86,7 +86,7 @@ All 73 tests should pass and the build should be clean before continuing.
### Rebuild and restart
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```
@@ -133,5 +133,5 @@ git checkout upstream/main -- src/router.ts
# Revert the index.ts sendMessage call sites to plain formatOutbound(rawText)
# (edit manually or: git checkout upstream/main -- src/index.ts)
npm run build
pnpm run build
```

View File

@@ -80,8 +80,8 @@ If the merge reports conflicts, resolve them by reading the conflicted files and
### Validate code changes
```bash
npm test
npm run build
pnpm test
pnpm run build
```
All tests must pass and build must be clean before proceeding.
@@ -172,7 +172,7 @@ Expected: Both operations succeed.
### Full integration test
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
```

View File

@@ -91,7 +91,7 @@ Implementation:
Always tell the user:
```bash
# Rebuild and restart
npm run build
pnpm run build
# macOS:
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist

View File

@@ -41,7 +41,7 @@ Set `LOG_LEVEL=debug` for verbose output:
```bash
# For development
LOG_LEVEL=debug npm run dev
LOG_LEVEL=debug pnpm run dev
# For launchd service (macOS), add to plist EnvironmentVariables:
<key>LOG_LEVEL</key>
@@ -231,7 +231,7 @@ query({
```bash
# Rebuild main app
npm run build
pnpm run build
# Rebuild container (use --no-cache for clean rebuild)
./container/build.sh

View File

@@ -231,10 +231,10 @@ Ask them to let you know when done.
## Phase 4: Build and restart
```bash
npm run build
pnpm run build
```
If build fails, diagnose and fix. Common issue: `@onecli-sh/sdk` not installed — run `npm install` first.
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`

View File

@@ -43,7 +43,7 @@ Use the channel's `typical-use` and `default-isolation` fields to pick the recom
### Register Command
```bash
npx tsx setup/index.ts --step register -- \
pnpm exec tsx setup/index.ts --step register -- \
--platform-id "<id>" --name "<name>" \
--folder "<folder>" --channel "<type>" \
--session-mode "<shared|agent-shared|per-thread>" \
@@ -58,10 +58,10 @@ For separate agents, also ask for a folder name and optionally a different assis
When adding another group/chat on an already-configured platform (e.g. a second Telegram group):
1. **Telegram:** ask the isolation question first to determine intent (`wire-to:<folder>` for an existing agent, `new-agent:<folder>` for a fresh one). Run `npx tsx setup/index.ts --step pair-telegram -- --intent <intent>`, show the CODE (follow the `REMINDER_TO_ASSISTANT` line in the `PAIR_TELEGRAM_ISSUED` block) and tell the user to post `@<botname> CODE` in the target group (or DM the bot for a private chat). Wait for the `PAIR_TELEGRAM` block. The inbound interceptor has already created the `messaging_groups` row with `unknown_sender_policy = 'strict'` and upserted the paired user — `register` only needs to add the wiring:
1. **Telegram:** ask the isolation question first to determine intent (`wire-to:<folder>` for an existing agent, `new-agent:<folder>` for a fresh one). Run `pnpm exec tsx setup/index.ts --step pair-telegram -- --intent <intent>`, show the CODE (follow the `REMINDER_TO_ASSISTANT` line in the `PAIR_TELEGRAM_ISSUED` block) and tell the user to post `@<botname> CODE` in the target group (or DM the bot for a private chat). Wait for the `PAIR_TELEGRAM` block. The inbound interceptor has already created the `messaging_groups` row with `unknown_sender_policy = 'strict'` and upserted the paired user — `register` only needs to add the wiring:
```bash
npx tsx setup/index.ts --step register -- \
pnpm exec tsx setup/index.ts --step register -- \
--platform-id "<PLATFORM_ID>" --name "<group-name>" \
--folder "<folder>" --channel "telegram" \
--session-mode "<shared|agent-shared|per-thread>" \

View File

@@ -68,7 +68,7 @@ Source: `src/db.ts`
Insert directly into the SQLite database. This requires groups to be registered first (Phase 1). Use the registered group's `folder` and `chat_jid`:
```bash
npx tsx -e "
pnpm exec tsx -e "
const Database = require('better-sqlite3');
const { CronExpressionParser } = require('cron-parser');
const db = new Database('store/messages.db');

View File

@@ -36,7 +36,7 @@ Keep it factual and terse — this is for machine recovery after compaction, not
Run the discovery script to find and summarize the OpenClaw installation:
```bash
npx tsx ${CLAUDE_SKILL_DIR}/scripts/discover-openclaw.ts
pnpm exec tsx ${CLAUDE_SKILL_DIR}/scripts/discover-openclaw.ts
```
If the user specifies a custom path, pass it: `--state-dir <path>`
@@ -106,7 +106,7 @@ The discovery script provides detected groups in the GROUPS field (format: `chan
For each group the user wants to bring over, pre-register it:
```bash
npx tsx setup/index.ts --step register -- --jid "<nanoclaw_jid>" --name "<group_name>" --folder "<channel>_<slug>" --trigger "@<confirmed_name>" --channel <channel> --assistant-name "<confirmed_name>"
pnpm exec tsx setup/index.ts --step register -- --jid "<nanoclaw_jid>" --name "<group_name>" --folder "<channel>_<slug>" --trigger "@<confirmed_name>" --channel <channel> --assistant-name "<confirmed_name>"
```
Only pass `--assistant-name` on the first registration (it updates all CLAUDE.md templates globally).
@@ -115,7 +115,7 @@ Folder naming: `<channel>_<name-slug>` (e.g. `whatsapp_family-chat`, `telegram_d
For the first/primary group, add `--is-main --no-trigger-required`. Other groups default to requiring a trigger prefix.
**Important:** Registration requires the database to exist. If the environment step hasn't been run yet, run it first: `npx tsx setup/index.ts --step environment`. Registration also creates the group folder under `groups/` and copies the CLAUDE.md template.
**Important:** Registration requires the database to exist. If the environment step hasn't been run yet, run it first: `pnpm exec tsx setup/index.ts --step environment`. Registration also creates the group folder under `groups/` and copies the CLAUDE.md template.
Register groups from all channels — including channels NanoClaw doesn't yet support (signal, matrix, etc.). The registration stores the JID and metadata in the database, ready for when that channel is added later. Groups won't receive messages until their channel code is installed, but the registration, group folder, and CLAUDE.md will be ready.
@@ -295,7 +295,7 @@ For each detected plugin, present the name to the user and discuss whether to se
1. **If NanoClaw has a matching skill** — check the available NanoClaw skills list for an equivalent (e.g. `/add-voice-transcription` for whisper). If found, save the API key to `.env` and invoke that skill.
2. **If the OpenClaw plugin was an MCP server** — read its config to find the exact package name and command. Install the same MCP server (e.g. `npx -y <exact-package-from-config>`). Don't search for or guess at MCP packages — only install what was explicitly configured.
2. **If the OpenClaw plugin was an MCP server** — read its config to find the exact package name and command. Install the same MCP server (e.g. `pnpm dlx <exact-package-from-config>`). Don't search for or guess at MCP packages — only install what was explicitly configured.
3. **If the OpenClaw plugin was a CLI tool** — read the config to identify the exact tool. If it's an npm package, add it to the container's Dockerfile. Add a note to the group's CLAUDE.md that the tool is available and how to invoke it.
@@ -324,7 +324,7 @@ Run the credential extraction script with `--write-env .env` so it writes creden
First, run without `--write-env` to preview:
```bash
npx tsx ${CLAUDE_SKILL_DIR}/scripts/extract-channel-credentials.ts --state-dir <STATE_DIR> --channel <name>
pnpm exec tsx ${CLAUDE_SKILL_DIR}/scripts/extract-channel-credentials.ts --state-dir <STATE_DIR> --channel <name>
```
Parse the status block. Key fields: HAS_CREDENTIAL, CREDENTIAL_MASKED, NANOCLAW_ENV_VAR.
@@ -339,7 +339,7 @@ If HAS_CREDENTIAL=true: Show the masked credential (`CREDENTIAL_MASKED`). AskUse
If using the credential:
```bash
npx tsx ${CLAUDE_SKILL_DIR}/scripts/extract-channel-credentials.ts --state-dir <STATE_DIR> --channel <name> --write-env .env
pnpm exec tsx ${CLAUDE_SKILL_DIR}/scripts/extract-channel-credentials.ts --state-dir <STATE_DIR> --channel <name> --write-env .env
```
The script writes the credential directly to `.env` using the correct NanoClaw variable name (e.g. `TELEGRAM_BOT_TOKEN`). Check the status block for `WRITTEN_TO` and `WRITTEN_COUNT` to confirm.

View File

@@ -1,7 +1,7 @@
/**
* Discover an existing OpenClaw installation and emit a structured summary.
*
* Usage: npx tsx .claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts [--state-dir <path>]
* Usage: pnpm exec tsx .claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts [--state-dir <path>]
*
* Checks (in order): --state-dir arg, $OPENCLAW_STATE_DIR, ~/.openclaw, ~/.clawdbot
* Parses openclaw.json (JSON5-tolerant), scans workspace for identity/memory files,

View File

@@ -2,7 +2,7 @@
* Extract a channel credential from an OpenClaw configuration and write it
* directly to the NanoClaw .env file.
*
* Usage: npx tsx .claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts \
* Usage: pnpm exec tsx .claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts \
* --channel telegram --state-dir ~/.openclaw --write-env .env
*
* Handles OpenClaw SecretRef formats:

View File

@@ -391,7 +391,7 @@ For behavior customizations (CLAUDE.md files): copy from the main tree. These ar
## 2.6 Validate in worktree
```bash
cd "$WORKTREE" && npm install && npm run build && npm test
cd "$WORKTREE" && pnpm install && pnpm run build && pnpm test
```
If build fails, show the error. Fix only issues caused by the migration. If unclear, ask the user.
@@ -417,7 +417,7 @@ If testing live:
ln -s "$PROJECT_ROOT/.env" "$WORKTREE/.env"
```
3. Start from worktree: `cd "$WORKTREE" && npm run dev`
3. Start from worktree: `cd "$WORKTREE" && pnpm run dev`
4. Ask the user to send a test message from their phone. Wait for them to confirm it works.
@@ -461,7 +461,7 @@ Do NOT use `git checkout -B` to create an intermediate branch — this caused is
## 2.9 Post-upgrade
Run `npm install && npm run build` in the main tree to confirm.
Run `npm install && pnpm run build` in the main tree to confirm.
Restart the service:
```bash

View File

@@ -55,7 +55,7 @@ Run `bash setup.sh` and parse the status block.
## 2. Check Environment
Run `npx tsx setup/index.ts --step environment` and parse the status block.
Run `pnpm exec tsx setup/index.ts --step environment` and parse the status block.
- If HAS_AUTH=true → WhatsApp is already configured, note for step 5
- If HAS_REGISTERED_GROUPS=true → note existing config, offer to skip or reconfigure
@@ -73,9 +73,9 @@ If "Migrate now": invoke `/migrate-from-openclaw`, then return here and continue
## 2a. Timezone
Run `npx tsx setup/index.ts --step timezone` and parse the status block.
Run `pnpm exec tsx setup/index.ts --step timezone` and parse the status block.
- If NEEDS_USER_INPUT=true → The system timezone could not be autodetected (e.g. POSIX-style TZ like `IST-2`). AskUserQuestion: "What is your timezone?" with common options (America/New_York, Europe/London, Asia/Jerusalem, Asia/Tokyo) and an "Other" escape. Then re-run: `npx tsx setup/index.ts --step timezone -- --tz <their-answer>`.
- If NEEDS_USER_INPUT=true → The system timezone could not be autodetected (e.g. POSIX-style TZ like `IST-2`). AskUserQuestion: "What is your timezone?" with common options (America/New_York, Europe/London, Asia/Jerusalem, Asia/Tokyo) and an "Other" escape. Then re-run: `pnpm exec tsx setup/index.ts --step timezone -- --tz <their-answer>`.
- If STATUS=success and RESOLVED_TZ is `UTC` or `Etc/UTC` → confirm with the user: "Your system timezone is UTC — is that correct, or are you on a remote server?" If wrong, ask for their actual timezone and re-run with `--tz`.
- If STATUS=success → Timezone is configured. Note RESOLVED_TZ for reference.
@@ -91,7 +91,7 @@ Run `npx tsx setup/index.ts --step timezone` and parse the status block.
### 3b. Build and test
Run `npx tsx setup/index.ts --step container -- --runtime docker` and parse the status block.
Run `pnpm exec tsx setup/index.ts --step container -- --runtime docker` and parse the status block.
**If BUILD_OK=false:** Read `logs/setup.log` tail for the build error.
- Cache issue (stale layers): `docker builder prune -f`. Retry.
@@ -220,7 +220,7 @@ The skill will:
**After the channel skill completes**, install dependencies and rebuild — channel merges may introduce new packages:
```bash
npm install && npm run build
pnpm install && pnpm run build
```
If the build fails, read the error output and fix it (usually a missing dependency). Then continue to step 5a.
@@ -230,7 +230,7 @@ If the build fails, read the error output and fix it (usually a missing dependen
Set empty mount allowlist (agents only access their own workspace). Users can configure mounts later with `/manage-mounts`.
```bash
npx tsx setup/index.ts --step mounts -- --empty
pnpm exec tsx setup/index.ts --step mounts -- --empty
```
## 7. Start Service
@@ -239,7 +239,7 @@ If service already running: unload first.
- macOS: `launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist`
- Linux: `systemctl --user stop nanoclaw` (or `systemctl stop nanoclaw` if root)
Run `npx tsx setup/index.ts --step service` and parse the status block.
Run `pnpm exec tsx setup/index.ts --step service` and parse the status block.
**If FALLBACK=wsl_no_systemd:** WSL without systemd detected. Tell user they can either enable systemd in WSL (`echo -e "[boot]\nsystemd=true" | sudo tee /etc/wsl.conf` then restart WSL) or use the generated `start-nanoclaw.sh` wrapper.
@@ -287,10 +287,10 @@ If yes: invoke `/add-vercel`.
## 8. Verify
Run `npx tsx setup/index.ts --step verify` and parse the status block.
Run `pnpm exec tsx setup/index.ts --step verify` and parse the status block.
**If STATUS=failed, fix each:**
- SERVICE=stopped → `npm run build`, then restart: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux) or `bash start-nanoclaw.sh` (WSL nohup)
- SERVICE=stopped → `pnpm run build`, then restart: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux) or `bash start-nanoclaw.sh` (WSL nohup)
- SERVICE=not_found → re-run step 7
- CREDENTIALS=missing → re-run step 4 (check `onecli secrets list`)
- CHANNEL_AUTH shows `not_found` for any channel → re-invoke that channel's skill (e.g. `/add-telegram`)
@@ -303,7 +303,7 @@ Tell user to test: send a message in their registered chat. Show: `tail -f logs/
**Container agent fails ("Claude Code process exited with code 1"):** Ensure Docker is running — `open -a Docker` (macOS) or `sudo systemctl start docker` (Linux). Check container logs in `groups/main/logs/container-*.log`.
**No response to messages:** Check trigger pattern. Main channel doesn't need prefix. Check DB: `npx tsx setup/index.ts --step verify`. Check `logs/nanoclaw.log`.
**No response to messages:** Check trigger pattern. Main channel doesn't need prefix. Check DB: `pnpm exec tsx setup/index.ts --step verify`. Check `logs/nanoclaw.log`.
**Channel not connecting:** Verify the channel's credentials are set in `.env`. Channels auto-enable when their credentials are present. For WhatsApp: check `store/auth/creds.json` exists. For token-based channels: check token values in `.env`. Restart the service after any `.env` change.

View File

@@ -30,7 +30,7 @@ Run `/update-nanoclaw` in Claude Code.
**Conflict resolution**: opens only conflicted files, resolves the conflict markers, keeps your local customizations intact.
**Validation**: runs `npm run build` and `npm test`.
**Validation**: runs `pnpm run build` and `pnpm test`.
**Breaking changes check**: after validation, reads CHANGELOG.md for any `[BREAKING]` entries introduced by the update. If found, shows each breaking change and offers to run the recommended skill to migrate.
@@ -109,7 +109,7 @@ Show file-level impact from upstream:
Bucket the upstream changed files:
- **Skills** (`.claude/skills/`): unlikely to conflict unless the user edited an upstream skill
- **Source** (`src/`): may conflict if user modified the same files
- **Build/config** (`package.json`, `package-lock.json`, `tsconfig*.json`, `container/`, `launchd/`): review needed
- **Build/config** (`package.json`, `pnpm-lock.yaml`, `tsconfig*.json`, `container/`, `launchd/`): review needed
- **Other**: docs, tests, misc
**Large drift check:** If the upstream commit count and age suggest the user has a lot of catching up to do, mention that `/migrate-nanoclaw` might be a better fit — it extracts customizations and reapplies them on clean upstream instead of merging. Offer it as an option but don't push.
@@ -175,8 +175,8 @@ If it gets messy (more than 3 rounds of conflicts):
# Step 5: Validation
Run:
- `npm run build`
- `npm test` (do not fail the flow if tests are not configured)
- `pnpm run build`
- `pnpm test` (do not fail the flow if tests are not configured)
If build fails:
- Show the error.
@@ -234,7 +234,7 @@ Tell the user:
- Backup branch also exists: `backup/pre-update-<HASH>-<TIMESTAMP>`
- Restart the service to apply changes:
- If using launchd: `launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist && launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist`
- If running manually: restart `npm run dev`
- If running manually: restart `pnpm run dev`
## Diagnostics

View File

@@ -110,8 +110,8 @@ If a merge fails badly (e.g., cannot resolve conflicts):
# Step 4: Validation
After all selected skills are merged:
- `npm run build`
- `npm test` (do not fail the flow if tests are not configured)
- `pnpm run build`
- `pnpm test` (do not fail the flow if tests are not configured)
If build fails:
- Show the error.

View File

@@ -76,8 +76,8 @@ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
```bash
git fetch whatsapp skill/local-whisper
git merge whatsapp/skill/local-whisper || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -87,7 +87,7 @@ This modifies `src/transcription.ts` to use the `whisper-cli` binary instead of
### Validate
```bash
npm run build
pnpm run build
```
## Phase 3: Verify
@@ -110,7 +110,7 @@ launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
### Build and restart
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
```

View File

@@ -48,8 +48,8 @@ git remote add upstream https://github.com/qwibitai/nanoclaw.git
```bash
git fetch upstream skill/native-credential-proxy
git merge upstream/skill/native-credential-proxy || {
git checkout --theirs package-lock.json
git add package-lock.json
git checkout --theirs pnpm-lock.yaml
git add pnpm-lock.yaml
git merge --continue
}
```
@@ -62,7 +62,7 @@ This merges in:
- Restored platform-aware proxy bind address detection
- Reverted setup skill to `.env`-based credential instructions
If the merge reports conflicts beyond `package-lock.json`, resolve them by reading the conflicted files and understanding the intent of both sides.
If the merge reports conflicts beyond `pnpm-lock.yaml`, resolve them by reading the conflicted files and understanding the intent of both sides.
### Update main group CLAUDE.md
@@ -77,9 +77,9 @@ with:
### Validate code changes
```bash
npm install
npm run build
npx vitest run src/credential-proxy.test.ts src/container-runner.test.ts
pnpm install
pnpm run build
pnpm exec vitest run src/credential-proxy.test.ts src/container-runner.test.ts
```
All tests must pass and build must be clean before proceeding.
@@ -125,7 +125,7 @@ echo 'ANTHROPIC_API_KEY=<key>' >> .env
1. Rebuild and restart:
```bash
npm run build
pnpm run build
```
Then restart the service:
@@ -161,7 +161,7 @@ To revert to OneCLI gateway:
1. Find the merge commit: `git log --oneline --merges -5`
2. Revert it: `git revert <merge-commit> -m 1` (undoes the skill branch merge, keeps your other changes)
3. `npm install` (re-adds `@onecli-sh/sdk`)
4. `npm run build`
3. `pnpm install` (re-adds `@onecli-sh/sdk`)
4. `pnpm run build`
5. Follow `/setup` step 4 to configure OneCLI credentials
6. Remove `ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN` from `.env`

View File

@@ -26,7 +26,7 @@ Before using this skill, ensure:
1. **NanoClaw is installed and running** - WhatsApp connected, service active
2. **Dependencies installed**:
```bash
npm ls playwright dotenv-cli || npm install playwright dotenv-cli
pnpm ls playwright dotenv-cli || pnpm install playwright dotenv-cli
```
3. **CHROME_PATH configured** in `.env` (if Chrome is not at default location):
```bash
@@ -40,7 +40,7 @@ Before using this skill, ensure:
```bash
# 1. Setup authentication (interactive)
npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/setup.ts
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
# Verify: data/x-auth.json should exist after successful login
# 2. Rebuild container to include skill
@@ -48,7 +48,7 @@ npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/setup.ts
# Verify: Output shows "COPY .claude/skills/x-integration/agent.ts"
# 3. Rebuild host and restart service
npm run build
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)
@@ -225,7 +225,7 @@ COPY container/agent-runner/package*.json ./
COPY container/agent-runner/ ./
```
Then add COPY line after `COPY container/agent-runner/ ./` and before `RUN npm run build`:
Then add COPY line after `COPY container/agent-runner/ ./` and before `RUN pnpm run build`:
```dockerfile
# Copy skill MCP tools
COPY .claude/skills/x-integration/agent.ts ./src/skills/x-integration/
@@ -247,7 +247,7 @@ echo "Chrome not found - update CHROME_PATH in .env"
### 2. Run Authentication
```bash
npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/setup.ts
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
```
This opens Chrome for manual X login. Session saved to `data/x-browser-profile/`.
@@ -271,7 +271,7 @@ cat data/x-auth.json # Should show {"authenticated": true, ...}
### 4. Restart Service
```bash
npm run build
pnpm run build
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
# Linux: systemctl --user restart nanoclaw
```
@@ -317,26 +317,26 @@ ls -la data/x-browser-profile/ 2>/dev/null | head -5
### Re-authenticate (if expired)
```bash
npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/setup.ts
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
```
### Test Post (will actually post)
```bash
echo '{"content":"Test tweet - please ignore"}' | npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/post.ts
echo '{"content":"Test tweet - please ignore"}' | pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/post.ts
```
### Test Like
```bash
echo '{"tweetUrl":"https://x.com/user/status/123"}' | npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/like.ts
echo '{"tweetUrl":"https://x.com/user/status/123"}' | pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/like.ts
```
Or export `CHROME_PATH` manually before running:
```bash
export CHROME_PATH="/path/to/chrome"
echo '{"content":"Test"}' | npx tsx .claude/skills/x-integration/scripts/post.ts
echo '{"content":"Test"}' | pnpm exec tsx .claude/skills/x-integration/scripts/post.ts
```
## Troubleshooting
@@ -344,7 +344,7 @@ echo '{"content":"Test"}' | npx tsx .claude/skills/x-integration/scripts/post.ts
### Authentication Expired
```bash
npx dotenv -e .env -- npx tsx .claude/skills/x-integration/scripts/setup.ts
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
```

View File

@@ -22,7 +22,7 @@ async function runScript(script: string, args: object): Promise<SkillResult> {
const scriptPath = path.join(process.cwd(), '.claude', 'skills', 'x-integration', 'scripts', `${script}.ts`);
return new Promise((resolve) => {
const proc = spawn('npx', ['tsx', scriptPath], {
const proc = spawn('pnpm', ['exec', 'tsx', scriptPath], {
cwd: process.cwd(),
env: { ...process.env, NANOCLAW_ROOT: process.cwd() },
stdio: ['pipe', 'pipe', 'pipe']

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env npx tsx
#!/usr/bin/env pnpm exec tsx
/**
* X Integration - Like Tweet
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123"}' | npx tsx like.ts
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123"}' | pnpm exec tsx like.ts
*/
import { getBrowserContext, navigateToTweet, runScript, config, ScriptResult } from '../lib/browser.js';

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env npx tsx
#!/usr/bin/env pnpm exec tsx
/**
* X Integration - Post Tweet
* Usage: echo '{"content":"Hello world"}' | npx tsx post.ts
* Usage: echo '{"content":"Hello world"}' | pnpm exec tsx post.ts
*/
import { getBrowserContext, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env npx tsx
#!/usr/bin/env pnpm exec tsx
/**
* X Integration - Quote Tweet
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123","comment":"My thoughts"}' | npx tsx quote.ts
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123","comment":"My thoughts"}' | pnpm exec tsx quote.ts
*/
import { getBrowserContext, navigateToTweet, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env npx tsx
#!/usr/bin/env pnpm exec tsx
/**
* X Integration - Reply to Tweet
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123","content":"Great post!"}' | npx tsx reply.ts
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123","content":"Great post!"}' | pnpm exec tsx reply.ts
*/
import { getBrowserContext, navigateToTweet, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env npx tsx
#!/usr/bin/env pnpm exec tsx
/**
* X Integration - Retweet
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123"}' | npx tsx retweet.ts
* Usage: echo '{"tweetUrl":"https://x.com/user/status/123"}' | pnpm exec tsx retweet.ts
*/
import { getBrowserContext, navigateToTweet, runScript, config, ScriptResult } from '../lib/browser.js';

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env npx tsx
#!/usr/bin/env pnpm exec tsx
/**
* X Integration - Authentication Setup
* Usage: npx tsx setup.ts
* Usage: pnpm exec tsx setup.ts
*
* Interactive script - opens browser for manual login
*/

View File

@@ -20,10 +20,12 @@ jobs:
with:
token: ${{ steps.app-token.outputs.token }}
- uses: pnpm/action-setup@v4
- name: Bump patch version
run: |
npm version patch --no-git-tag-version
git add package.json package-lock.json
pnpm version patch --no-git-tag-version
git add package.json
git diff --cached --quiet && exit 0
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

View File

@@ -9,17 +9,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Format check
run: npm run format:check
run: pnpm run format:check
- name: Typecheck
run: npx tsc --noEmit
run: pnpm exec tsc --noEmit
- name: Tests
run: npx vitest run
run: pnpm exec vitest run

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
# Dependencies
node_modules/
.npm-cache/
# pnpm content-addressable store (created when running in sandbox mode)
.pnpm-store/
# Build output
dist/

View File

@@ -1 +1 @@
npm run format:fix
pnpm run format:fix

4
.npmrc
View File

@@ -1 +1,3 @@
minReleaseAge=7d
# Safety net — pnpm-workspace.yaml has the authoritative minimumReleaseAge (4320 min = 3 days)
# This .npmrc value is a fallback if npm is ever invoked directly
minReleaseAge=3d

View File

@@ -102,13 +102,13 @@ Before creating a PR, adding a skill, or preparing any contribution, you MUST re
Run commands directly — don't tell the user to run them.
```bash
npm run dev # Host with hot reload
npm run build # Compile host TypeScript (src/)
./container/build.sh # Rebuild agent container image (nanoclaw-agent:latest)
npm test # Host tests
pnpm run dev # Host with hot reload
pnpm run build # Compile host TypeScript (src/)
./container/build.sh # Rebuild agent container image (nanoclaw-agent:latest)
pnpm test # Host tests
```
Container typecheck is a separate tsconfig — if you edit `container/agent-runner/src/`, run `npx tsc -p container/agent-runner/tsconfig.json --noEmit` to check it.
Container typecheck is a separate tsconfig — if you edit `container/agent-runner/src/`, run `pnpm exec tsc -p container/agent-runner/tsconfig.json --noEmit` to check it.
Service management:
```bash
@@ -123,6 +123,15 @@ systemctl --user start|stop|restart nanoclaw
Host logs: `logs/nanoclaw.log` (normal) and `logs/nanoclaw.error.log` (errors only — some delivery/approval failures only show up here).
## Supply Chain Security (pnpm)
This project uses pnpm with `minimumReleaseAge: 4320` (3 days) in `pnpm-workspace.yaml`. New package versions must exist on the npm registry for 3 days before pnpm will resolve them.
**Rules — do not bypass without explicit human approval:**
- **`minimumReleaseAgeExclude`**: Never add entries without human sign-off. If a package must bypass the release age gate, the human must approve and the entry must pin the exact version being excluded (e.g. `package@1.2.3`), never a range.
- **`onlyBuiltDependencies`**: Never add packages to this list without human approval — build scripts execute arbitrary code during install.
- **`pnpm install --frozen-lockfile`** should be used in CI, automation, and container builds. Never run bare `pnpm install` in those contexts.
## v2 Docs Index
| Doc | Purpose |

2
container/.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
agent-runner/node_modules
agent-runner/dist

View File

@@ -30,23 +30,29 @@ RUN apt-get update && apt-get install -y \
ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium
ENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
# Install agent-browser and claude-code globally
RUN npm install -g agent-browser @anthropic-ai/claude-code vercel
# Enable pnpm via corepack
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
# Allow agent-browser build scripts for global install, then install
RUN echo "only-built-dependencies[]=agent-browser" > /root/.npmrc && \
pnpm install -g agent-browser @anthropic-ai/claude-code vercel
# Create app directory
WORKDIR /app
# Copy package files first for better caching
COPY agent-runner/package*.json ./
COPY agent-runner/package.json agent-runner/pnpm-lock.yaml agent-runner/pnpm-workspace.yaml ./
# Install dependencies
RUN npm install
RUN pnpm install --frozen-lockfile
# Copy source code
COPY agent-runner/ ./
# Build TypeScript
RUN npm run build
RUN pnpm run build
# Create workspace directories
RUN mkdir -p /workspace/group /workspace/global /workspace/extra
@@ -56,7 +62,7 @@ RUN mkdir -p /workspace/group /workspace/global /workspace/extra
# recompiles on startup — this lets host source edits and skill installs
# take effect without rebuilding the image. All IO goes through the session
# DBs (inbound.db / outbound.db) mounted into /workspace.
RUN printf '#!/bin/bash\nset -e\ncd /app && npx tsc --outDir /tmp/dist 2>&1 >&2\nln -s /app/node_modules /tmp/dist/node_modules\nchmod -R a-w /tmp/dist\ncat > /tmp/input.json\nnode /tmp/dist/index.js < /tmp/input.json\n' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh
RUN printf '#!/bin/bash\nset -e\ncd /app && pnpm exec tsc --outDir /tmp/dist 2>&1 >&2\nln -s /app/node_modules /tmp/dist/node_modules\nchmod -R a-w /tmp/dist\ncat > /tmp/input.json\nnode /tmp/dist/index.js < /tmp/input.json\n' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh
# Set ownership to node user (non-root) for writable directories
RUN chown -R node:node /workspace && chmod 777 /home/node

File diff suppressed because it is too large Load Diff

1309
container/agent-runner/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
onlyBuiltDependencies:
- better-sqlite3
pnpm:
minimumReleaseAge: 4320

View File

@@ -66,7 +66,7 @@ Every frontend task follows this sequence. Do not skip steps.
Run the build and fix ALL errors:
```bash
npm run build 2>&1
pnpm run build 2>&1
```
If it fails, **fix it**. Do not deploy broken builds. Do not disable ESLint rules or TypeScript checks to make it pass.
@@ -76,7 +76,7 @@ If it fails, **fix it**. Do not deploy broken builds. Do not disable ESLint rule
Start the dev server and test in a real browser:
```bash
npm run dev &
pnpm run dev &
DEV_PID=$!
sleep 3
```

View File

@@ -87,4 +87,4 @@ User: "Can you transcribe audio?"
- **The change is for a one-off task** — just do it in your workspace, don't modify the container
- **The request is ambiguous** — ask the user what they actually need before spinning up builders or requesting installs
- **You don't know if it will work** — prototype in your workspace first (`npm install` in `/workspace/agent/`), then promote to container-level install if it proves useful
- **You don't know if it will work** — prototype in your workspace first (`pnpm install` in `/workspace/agent/`), then promote to container-level install if it proves useful

View File

@@ -117,7 +117,7 @@ After sending, tell the user you've handed it off and will share the result when
## Best Practices
- Run `npm run build` locally before deploying to catch build errors early
- Run `pnpm run build` locally before deploying to catch build errors early
- Use `--cwd` instead of `cd` to keep your working directory stable
- For Next.js projects, `vercel deploy` auto-detects the framework — no extra config needed
- Use `vercel.json` only when you need custom build settings, rewrites, or headers

View File

@@ -57,7 +57,7 @@ The same files conflict every time:
| File | Resolution |
|------|------------|
| `package.json` | Take main's version + keep fork/branch-specific deps |
| `package-lock.json` | `git checkout main -- package-lock.json && npm install` |
| `pnpm-lock.yaml` | `git checkout main -- pnpm-lock.yaml && pnpm install` |
| `.env.example` | Combine: main's entries + fork/branch-specific entries |
| `repo-tokens/badge.svg` | Take main's version (auto-generated) |

View File

@@ -148,7 +148,7 @@ grep 'QR\|authentication required\|qr' logs/nanoclaw.log | tail -5
ls -la store/auth/
# Re-authenticate if needed
npm run auth
pnpm run auth
```
## Service Management
@@ -167,5 +167,5 @@ launchctl bootout gui/$(id -u)/com.nanoclaw
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.nanoclaw.plist
# Rebuild after code changes
npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw
pnpm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw
```

View File

@@ -123,3 +123,39 @@ Each NanoClaw group gets its own OneCLI agent identity. This allows different cr
│ • No real credentials in environment or filesystem │
└──────────────────────────────────────────────────────────────────┘
```
## Supply Chain Security (pnpm)
NanoClaw uses pnpm with two supply chain defenses configured in `pnpm-workspace.yaml`:
### Minimum Release Age
`minimumReleaseAge: 4320` (3 days). pnpm will refuse to resolve any package version published less than 3 days ago. This defends against typosquatting and compromised maintainer accounts — most malicious publishes are detected and pulled within 72 hours.
**Excluding a package from the release age gate** (`minimumReleaseAgeExclude`):
This should be rare. When a zero-day fix or critical dependency requires an immediate update:
1. The exclusion must be reviewed and approved by a human maintainer
2. The entry must pin the **exact version** being excluded — never a range or wildcard
```yaml
minimumReleaseAgeExclude:
some-package: "1.2.3" # Approved by @user, 2026-04-14 — CVE-XXXX-YYYY fix
```
3. The exclusion should be removed once the version ages past the threshold (i.e. after 3 days)
4. Automated agents (Claude, CI bots) must never add exclusions without human sign-off
### Build Script Allowlist
`onlyBuiltDependencies` restricts which packages can execute install/postinstall scripts. Only packages on this list are permitted to run build scripts during `pnpm install`. Currently allowed:
- `better-sqlite3` — compiles native SQLite bindings
- `esbuild` — downloads platform-specific binary
- `protobufjs` — generates protobuf bindings (used by Baileys/libsignal)
- `sharp` — downloads platform-specific image processing binary
Adding a package to this list requires human approval — build scripts execute arbitrary code with the installing user's permissions.
### `.npmrc` Safety Net
The `.npmrc` file contains `minReleaseAge=3d` as a fallback. The authoritative setting is in `pnpm-workspace.yaml`, but `.npmrc` provides defense-in-depth if npm is ever invoked directly (e.g. by a tool that doesn't respect pnpm).

View File

@@ -404,7 +404,7 @@ Only the authentication variables (`CLAUDE_CODE_OAUTH_TOKEN` and `ANTHROPIC_API_
Set the `ASSISTANT_NAME` environment variable:
```bash
ASSISTANT_NAME=Bot npm start
ASSISTANT_NAME=Bot pnpm start
```
Or edit the default in `src/config.ts`. This changes:
@@ -779,7 +779,7 @@ chmod 700 groups/
Run manually for verbose output:
```bash
npm run dev
pnpm run dev
# or
node dist/index.js
```

View File

@@ -87,8 +87,8 @@ mv nanoclaw "$WORKSPACE/nanoclaw"
cd "$WORKSPACE/nanoclaw"
# Install dependencies
npm install
npm install https-proxy-agent
pnpm install
pnpm install https-proxy-agent
```
## Step 4: Apply Proxy and Sandbox Patches
@@ -97,7 +97,7 @@ NanoClaw needs several patches to work inside a Docker Sandbox. These handle pro
### 4a. Dockerfile — proxy args for container image build
`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`:
`pnpm 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`:
Add these lines after the `FROM` line:
@@ -111,7 +111,7 @@ ARG npm_config_strict_ssl=true
RUN npm config set strict-ssl ${npm_config_strict_ssl}
```
And after the `RUN npm install` line:
And after the `RUN pnpm install` line:
```dockerfile
RUN npm config set strict-ssl true
@@ -185,7 +185,7 @@ Patch `setup/container.ts` to pass the same proxy `--build-arg` flags as `build.
## Step 5: Build
```bash
npm run build
pnpm run build
bash container/build.sh
```
@@ -195,10 +195,10 @@ bash container/build.sh
```bash
# Apply the Telegram skill
npx tsx scripts/apply-skill.ts .claude/skills/add-telegram
pnpm exec tsx scripts/apply-skill.ts .claude/skills/add-telegram
# Rebuild after applying the skill
npm run build
pnpm run build
# Configure .env
cat > .env << EOF
@@ -209,7 +209,7 @@ EOF
mkdir -p data/env && cp .env data/env/env
# Register your chat
npx tsx setup/index.ts --step register \
pnpm exec tsx setup/index.ts --step register \
--jid "tg:<your-chat-id>" \
--name "My Chat" \
--trigger "@nanoclaw" \
@@ -235,10 +235,10 @@ Make sure you configured proxy bypass in [Step 1](#step-1-create-the-sandbox) fi
```bash
# Apply the WhatsApp skill
npx tsx scripts/apply-skill.ts .claude/skills/add-whatsapp
pnpm exec tsx scripts/apply-skill.ts .claude/skills/add-whatsapp
# Rebuild
npm run build
pnpm run build
# Configure .env
cat > .env << EOF
@@ -250,13 +250,13 @@ mkdir -p data/env && cp .env data/env/env
# Authenticate (choose one):
# QR code — scan with WhatsApp camera:
npx tsx src/whatsapp-auth.ts
pnpm exec tsx src/whatsapp-auth.ts
# OR pairing code — enter code in WhatsApp > Linked Devices > Link with phone number:
npx tsx src/whatsapp-auth.ts --pairing-code --phone <phone-number-no-plus>
pnpm exec tsx src/whatsapp-auth.ts --pairing-code --phone <phone-number-no-plus>
# Register your chat (JID = your phone number + @s.whatsapp.net)
npx tsx setup/index.ts --step register \
pnpm exec tsx setup/index.ts --step register \
--jid "<phone>@s.whatsapp.net" \
--name "My Chat" \
--trigger "@nanoclaw" \
@@ -276,7 +276,7 @@ Apply both skills, patch both for proxy support, combine the `.env` variables, a
## Step 7: Run
```bash
npm start
pnpm start
```
You don't need to set `ANTHROPIC_API_KEY` manually. The sandbox proxy intercepts requests and replaces `proxy-managed` with your real key automatically.
@@ -306,7 +306,7 @@ The workspace is mounted via virtiofs. Git's pack file handling can corrupt over
## Troubleshooting
### npm install fails with SELF_SIGNED_CERT_IN_CHAIN
### pnpm install fails with SELF_SIGNED_CERT_IN_CHAIN
```bash
npm config set strict-ssl false
```
@@ -355,5 +355,5 @@ Run the auth command interactively inside the sandbox (not piped through `docker
```bash
docker sandbox run shell-nanoclaw-workspace
# Then inside:
npx tsx src/whatsapp-auth.ts
pnpm exec tsx src/whatsapp-auth.ts
```

View File

@@ -98,7 +98,7 @@ Standard Markdown works: `**bold**`, `*italic*`, `[links](url)`, `# headings`.
## Installing Packages & Tools
Your container is ephemeral — anything installed via `apt-get` or `npm install -g` is lost on restart. To install packages that persist, use the self-modification tools:
Your container is ephemeral — anything installed via `apt-get` or `pnpm install -g` is lost on restart. To install packages that persist, use the self-modification tools:
1. **`install_packages`** — request system (apt) or global npm packages. Requires admin approval.
2. **`request_rebuild`** — rebuild your container image so approved packages are baked in. Always call this after `install_packages` to apply the changes.
@@ -111,16 +111,16 @@ request_rebuild({ reason: "Apply ffmpeg + transformers" })
# → Admin approves → image rebuilt with the packages
```
**When to use this vs workspace npm install:**
- `npm install` in `/workspace/agent/` persists on disk (it's mounted) but isn't on the global PATH — use it for project-level dependencies
**When to use this vs workspace pnpm install:**
- `pnpm install` in `/workspace/agent/` persists on disk (it's mounted) but isn't on the global PATH — use it for project-level dependencies
- `install_packages` is for system tools (ffmpeg, imagemagick) and global npm packages that need to be on PATH
### MCP Servers
Use **`add_mcp_server`** to add an MCP server to your configuration, then **`request_rebuild`** to apply. Browse available servers at https://mcp.so — it's a curated directory of high-quality MCP servers. Most Node.js servers run via `npx`, e.g.:
Use **`add_mcp_server`** to add an MCP server to your configuration, then **`request_rebuild`** to apply. Browse available servers at https://mcp.so — it's a curated directory of high-quality MCP servers. Most Node.js servers run via `pnpm dlx`, e.g.:
```
add_mcp_server({ name: "memory", command: "npx", args: ["@modelcontextprotocol/server-memory"] })
add_mcp_server({ name: "memory", command: "pnpm", args: ["dlx", "@modelcontextprotocol/server-memory"] })
request_rebuild({ reason: "Add memory MCP server" })
```

10160
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
"version": "1.2.52",
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
"type": "module",
"packageManager": "pnpm@10.33.0",
"main": "dist/index.js",
"scripts": {
"build": "tsc",

6792
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

8
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,8 @@
onlyBuiltDependencies:
- better-sqlite3
- esbuild
- protobufjs
- sharp
pnpm:
minimumReleaseAge: 4320

View File

@@ -10,7 +10,7 @@
* channel adapters, so there's no Gateway conflict.
*
* Usage:
* npx tsx scripts/init-first-agent.ts \
* pnpm exec tsx scripts/init-first-agent.ts \
* --channel discord \
* --user-id discord:1470183333427675709 \
* --platform-id discord:@me:1491573333382523708 \

View File

@@ -22,7 +22,7 @@
*
* Idempotent — safe to re-run.
*
* Usage: npx tsx scripts/migrate-group-claude-md.ts
* Usage: pnpm exec tsx scripts/migrate-group-claude-md.ts
*/
import fs from 'fs';
import path from 'path';

View File

@@ -1,7 +1,7 @@
/**
* Seed the v2 central DB with a Discord agent group + messaging group.
*
* Usage: npx tsx scripts/seed-discord.ts
* Usage: pnpm exec tsx scripts/seed-discord.ts
*/
import path from 'path';
@@ -73,4 +73,4 @@ try {
}
}
console.log('Done! Run: npm run build && node dist/index.js');
console.log('Done! Run: pnpm run build && node dist/index.js');

View File

@@ -2,7 +2,7 @@
* Quick integration test: create a session DB, insert a message,
* run the v2 poll loop with the Claude provider, verify output.
*
* Usage: npx tsx scripts/test-v2-agent.ts
* Usage: pnpm exec tsx scripts/test-v2-agent.ts
*/
import Database from 'better-sqlite3';
import fs from 'fs';

View File

@@ -4,7 +4,7 @@
* Mock adapter → onInbound → router → session DB → Docker container →
* agent-runner → Claude → messages_out → delivery → mock adapter.deliver()
*
* Usage: npx tsx scripts/test-v2-channel-e2e.ts
* Usage: pnpm exec tsx scripts/test-v2-channel-e2e.ts
*/
import Database from 'better-sqlite3';
import fs from 'fs';

View File

@@ -6,7 +6,7 @@
* 3. Container runs v2 agent-runner, polls inbound.db, queries Claude, writes outbound.db
* 4. Poll outbound.db for messages_out response
*
* Usage: npx tsx scripts/test-v2-host.ts
* Usage: pnpm exec tsx scripts/test-v2-host.ts
*/
import Database from 'better-sqlite3';
import fs from 'fs';

View File

@@ -2,7 +2,7 @@
set -euo pipefail
# setup.sh — Bootstrap script for NanoClaw
# Handles Node.js/npm setup, then hands off to the Node.js setup modules.
# Handles Node.js/pnpm setup, then hands off to the Node.js setup modules.
# This is the only bash script in the setup flow.
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -59,32 +59,29 @@ check_node() {
fi
}
# --- npm install ---
# --- pnpm install ---
install_deps() {
DEPS_OK="false"
NATIVE_OK="false"
if [ "$NODE_OK" = "false" ]; then
log "Skipping npm install — Node not available"
log "Skipping pnpm install — Node not available"
return
fi
cd "$PROJECT_ROOT"
# npm install with --unsafe-perm if root (needed for native modules)
local npm_flags=""
if [ "$IS_ROOT" = "true" ]; then
npm_flags="--unsafe-perm"
log "Running as root, using --unsafe-perm"
fi
# Enable corepack for pnpm
log "Enabling corepack"
corepack enable >> "$LOG_FILE" 2>&1 || true
log "Running npm ci $npm_flags"
if npm ci $npm_flags >> "$LOG_FILE" 2>&1; then
log "Running pnpm install --frozen-lockfile"
if pnpm install --frozen-lockfile >> "$LOG_FILE" 2>&1; then
DEPS_OK="true"
log "npm install succeeded"
log "pnpm install succeeded"
else
log "npm install failed"
log "pnpm install failed"
return
fi

View File

@@ -87,7 +87,7 @@ async function syncGroups(projectRoot: string): Promise<void> {
log.info('Building TypeScript');
let buildOk = false;
try {
execSync('npm run build', {
execSync('pnpm run build', {
cwd: projectRoot,
stdio: ['ignore', 'pipe', 'pipe'],
});

View File

@@ -1,6 +1,6 @@
/**
* Setup CLI entry point.
* Usage: npx tsx setup/index.ts --step <name> [args...]
* Usage: pnpm exec tsx setup/index.ts --step <name> [args...]
*/
import { log } from '../src/log.js';
import { emitStatus } from './status.js';
@@ -27,7 +27,7 @@ async function main(): Promise<void> {
if (stepIdx === -1 || !args[stepIdx + 1]) {
console.error(
`Usage: npx tsx setup/index.ts --step <${Object.keys(STEPS).join('|')}> [args...]`,
`Usage: pnpm exec tsx setup/index.ts --step <${Object.keys(STEPS).join('|')}> [args...]`,
);
process.exit(1);
}

View File

@@ -31,7 +31,7 @@ export async function run(_args: string[]): Promise<void> {
// Build first
log.info('Building TypeScript');
try {
execSync('npm run build', {
execSync('pnpm run build', {
cwd: projectRoot,
stdio: ['ignore', 'pipe', 'pipe'],
});

View File

@@ -298,7 +298,7 @@ async function buildContainerArgs(
args.push(
'-c',
'cd /app && npx tsc --outDir /tmp/dist 2>&1 >&2 && ln -sf /app/node_modules /tmp/dist/node_modules && node /tmp/dist/index.js',
'cd /app && pnpm exec tsc --outDir /tmp/dist 2>&1 >&2 && ln -sf /app/node_modules /tmp/dist/node_modules && node /tmp/dist/index.js',
);
return args;
@@ -322,7 +322,12 @@ export async function buildAgentGroupImage(agentGroupId: string): Promise<void>
dockerfile += `RUN apt-get update && apt-get install -y ${aptPackages.join(' ')} && rm -rf /var/lib/apt/lists/*\n`;
}
if (npmPackages.length > 0) {
dockerfile += `RUN npm install -g ${npmPackages.join(' ')}\n`;
// pnpm skips build scripts unless packages are allowlisted. Append each
// to /root/.npmrc (base image sets it up for agent-browser) so packages
// with postinstall — e.g. playwright, puppeteer, native addons — don't
// install silently broken.
const allowlist = npmPackages.map((p) => `echo 'only-built-dependencies[]=${p}' >> /root/.npmrc`).join(' && ');
dockerfile += `RUN ${allowlist} && pnpm install -g ${npmPackages.join(' ')}\n`;
}
dockerfile += 'USER node\n';