docs: add bun install support
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,6 +3,8 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
*.bun-build
|
*.bun-build
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
bun.lock
|
||||||
|
bun.lockb
|
||||||
coverage
|
coverage
|
||||||
.pnpm-store
|
.pnpm-store
|
||||||
.worktrees/
|
.worktrees/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
- Timestamps in agent envelopes are now UTC (compact `YYYY-MM-DDTHH:mmZ`); removed `messages.timestampPrefix`. Add `agent.userTimezone` to tell the model the user’s local time (system prompt only).
|
- Timestamps in agent envelopes are now UTC (compact `YYYY-MM-DDTHH:mmZ`); removed `messages.timestampPrefix`. Add `agent.userTimezone` to tell the model the user’s local time (system prompt only).
|
||||||
- Model config schema changes (auth profiles + model lists); doctor auto-migrates and the gateway rewrites legacy configs on startup.
|
- Model config schema changes (auth profiles + model lists); doctor auto-migrates and the gateway rewrites legacy configs on startup.
|
||||||
- Commands: gate all slash commands to authorized senders; add `/compact` to manually compact session context.
|
- Commands: gate all slash commands to authorized senders; add `/compact` to manually compact session context.
|
||||||
|
- Groups: `whatsapp.groups`, `telegram.groups`, and `imessage.groups` now act as allowlists when set. Add `"*"` to keep allow-all behavior.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Onboarding: resolve CLI entrypoint when running via `npx` so gateway daemon install works without a build step.
|
- Onboarding: resolve CLI entrypoint when running via `npx` so gateway daemon install works without a build step.
|
||||||
@@ -85,6 +86,7 @@
|
|||||||
- Agent tools: new `image` tool routed to the image model (when configured).
|
- Agent tools: new `image` tool routed to the image model (when configured).
|
||||||
- Config: default model shorthands (`opus`, `sonnet`, `gpt`, `gpt-mini`, `gemini`, `gemini-flash`).
|
- Config: default model shorthands (`opus`, `sonnet`, `gpt`, `gpt-mini`, `gemini`, `gemini-flash`).
|
||||||
- Docs: document built-in model shorthands + precedence (user config wins).
|
- Docs: document built-in model shorthands + precedence (user config wins).
|
||||||
|
- Bun: optional local install/build workflow without maintaining a Bun lockfile (see `docs/bun.md`).
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Control UI: render Markdown in tool result cards.
|
- Control UI: render Markdown in tool result cards.
|
||||||
@@ -108,6 +110,11 @@
|
|||||||
- Agent tools: honor `agent.tools` allow/deny policy even when sandbox is off.
|
- Agent tools: honor `agent.tools` allow/deny policy even when sandbox is off.
|
||||||
- Discord: avoid duplicate replies when OpenAI emits repeated `message_end` events.
|
- Discord: avoid duplicate replies when OpenAI emits repeated `message_end` events.
|
||||||
- Commands: unify /status (inline) and command auth across providers; group bypass for authorized control commands; remove Discord /clawd slash handler.
|
- Commands: unify /status (inline) and command auth across providers; group bypass for authorized control commands; remove Discord /clawd slash handler.
|
||||||
|
- CLI: run `clawdbot agent` via the Gateway by default; use `--local` to force embedded mode.
|
||||||
|
|
||||||
|
## 2026.1.5
|
||||||
|
|
||||||
|
### Fixes
|
||||||
- Control UI: render Markdown in chat messages (sanitized).
|
- Control UI: render Markdown in chat messages (sanitized).
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -48,35 +48,43 @@ pnpm clawdbot onboard
|
|||||||
|
|
||||||
## Quick start (from source)
|
## Quick start (from source)
|
||||||
|
|
||||||
Runtime: **Node ≥22** + **pnpm**.
|
Runtime: **Node ≥22**.
|
||||||
|
|
||||||
|
From source, **pnpm** is the default workflow. Bun is supported as an optional local workflow; see [`docs/bun.md`](docs/bun.md).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm install
|
# Install deps (no Bun lockfile)
|
||||||
pnpm build
|
bun install --no-save
|
||||||
pnpm ui:build
|
|
||||||
|
# Build TypeScript
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
# Build Control UI
|
||||||
|
bun install --cwd ui --no-save
|
||||||
|
bun run --cwd ui build
|
||||||
|
|
||||||
# Recommended: run the onboarding wizard
|
# Recommended: run the onboarding wizard
|
||||||
pnpm clawdbot onboard
|
bun run clawdbot onboard
|
||||||
|
|
||||||
# Link WhatsApp (stores creds in ~/.clawdbot/credentials)
|
# Link WhatsApp (stores creds in ~/.clawdbot/credentials)
|
||||||
pnpm clawdbot login
|
bun run clawdbot login
|
||||||
|
|
||||||
# Start the gateway
|
# Start the gateway
|
||||||
pnpm clawdbot gateway --port 18789 --verbose
|
bun run clawdbot gateway --port 18789 --verbose
|
||||||
|
|
||||||
# Dev loop (auto-reload on TS changes)
|
# Dev loop (auto-reload on TS changes)
|
||||||
pnpm gateway:watch
|
bun run gateway:watch
|
||||||
|
|
||||||
# Send a message
|
# Send a message
|
||||||
pnpm clawdbot send --to +1234567890 --message "Hello from Clawdbot"
|
bun run clawdbot send --to +1234567890 --message "Hello from Clawdbot"
|
||||||
|
|
||||||
# Talk to the assistant (optionally deliver back to WhatsApp/Telegram/Slack/Discord)
|
# Talk to the assistant (optionally deliver back to WhatsApp/Telegram/Slack/Discord)
|
||||||
pnpm clawdbot agent --message "Ship checklist" --thinking high
|
bun run clawdbot agent --message "Ship checklist" --thinking high
|
||||||
```
|
```
|
||||||
|
|
||||||
Upgrading? `clawdbot doctor`.
|
Upgrading? `clawdbot doctor`.
|
||||||
|
|
||||||
If you run from source, prefer `pnpm clawdbot …` (not global `clawdbot`).
|
If you run from source, prefer `bun run clawdbot …` or `pnpm clawdbot …` (not global `clawdbot`).
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
|
|
||||||
|
|||||||
56
docs/bun.md
Normal file
56
docs/bun.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Bun (optional)
|
||||||
|
|
||||||
|
Goal: allow running this repo with Bun without maintaining a Bun lockfile or losing pnpm patch behavior.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- pnpm remains the primary package manager/runtime for this repo.
|
||||||
|
- Bun can be used for local installs/builds/tests, but Bun currently **cannot use** `pnpm-lock.yaml` and will ignore it.
|
||||||
|
|
||||||
|
## Install (no Bun lockfile)
|
||||||
|
|
||||||
|
Use Bun without writing `bun.lock`/`bun.lockb`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun install --no-save
|
||||||
|
```
|
||||||
|
|
||||||
|
This avoids maintaining two lockfiles. (`bun.lock`/`bun.lockb` are gitignored.)
|
||||||
|
|
||||||
|
## Build / Test (Bun)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun run build
|
||||||
|
bun run vitest run
|
||||||
|
```
|
||||||
|
|
||||||
|
## pnpm patchedDependencies under Bun
|
||||||
|
|
||||||
|
pnpm supports `package.json#pnpm.patchedDependencies` and records it in `pnpm-lock.yaml`.
|
||||||
|
Bun does not support pnpm patches, so we apply them in `postinstall` when Bun is detected:
|
||||||
|
|
||||||
|
- `scripts/postinstall.js` runs only for Bun installs and applies every entry from `package.json#pnpm.patchedDependencies` into `node_modules/...` using `git apply` (idempotent).
|
||||||
|
|
||||||
|
To add a new patch that works in both pnpm + Bun:
|
||||||
|
|
||||||
|
1. Add an entry to `package.json#pnpm.patchedDependencies`
|
||||||
|
2. Add the patch file under `patches/`
|
||||||
|
3. Run `pnpm install` (updates `pnpm-lock.yaml` patch hash)
|
||||||
|
|
||||||
|
## Bun lifecycle scripts (blocked by default)
|
||||||
|
|
||||||
|
Bun may block dependency lifecycle scripts unless explicitly trusted (`bun pm untrusted` / `bun pm trust`).
|
||||||
|
For this repo, the commonly blocked scripts are not required:
|
||||||
|
|
||||||
|
- `@whiskeysockets/baileys` `preinstall`: checks Node major >= 20 (we run Node 22+).
|
||||||
|
- `protobufjs` `postinstall`: emits warnings about incompatible version schemes (no build artifacts).
|
||||||
|
|
||||||
|
If you hit a real runtime issue that requires these scripts, trust them explicitly:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun pm trust @whiskeysockets/baileys protobufjs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
- Some scripts still hardcode pnpm (e.g. `docs:build`, `ui:*`, `protocol:check`). Run those via pnpm for now.
|
||||||
@@ -9,7 +9,7 @@ CLAWDBOT reads an optional **JSON5** config from `~/.clawdbot/clawdbot.json` (co
|
|||||||
|
|
||||||
If the file is missing, CLAWDBOT uses safe-ish defaults (embedded Pi agent + per-sender sessions + workspace `~/clawd`). You usually only need a config to:
|
If the file is missing, CLAWDBOT uses safe-ish defaults (embedded Pi agent + per-sender sessions + workspace `~/clawd`). You usually only need a config to:
|
||||||
- restrict who can trigger the bot (`whatsapp.allowFrom`, `telegram.allowFrom`, etc.)
|
- restrict who can trigger the bot (`whatsapp.allowFrom`, `telegram.allowFrom`, etc.)
|
||||||
- control group mention behavior (`whatsapp.groups`, `telegram.groups`, `discord.guilds`, `routing.groupChat`)
|
- control group allowlists + mention behavior (`whatsapp.groups`, `telegram.groups`, `discord.guilds`, `routing.groupChat`)
|
||||||
- customize message prefixes (`messages`)
|
- customize message prefixes (`messages`)
|
||||||
- set the agent's workspace (`agent.workspace`)
|
- set the agent's workspace (`agent.workspace`)
|
||||||
- tune the embedded agent (`agent`) and session behavior (`session`)
|
- tune the embedded agent (`agent`) and session behavior (`session`)
|
||||||
@@ -218,7 +218,7 @@ Group messages default to **require mention** (either metadata mention or regex
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Mention gating defaults live per provider (`whatsapp.groups`, `telegram.groups`, `imessage.groups`, `discord.guilds`).
|
Mention gating defaults live per provider (`whatsapp.groups`, `telegram.groups`, `imessage.groups`, `discord.guilds`). When `*.groups` is set, it also acts as a group allowlist; include `"*"` to allow all groups.
|
||||||
|
|
||||||
To respond **only** to specific text triggers (ignoring native @-mentions):
|
To respond **only** to specific text triggers (ignoring native @-mentions):
|
||||||
```json5
|
```json5
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Updated: 2025-12-07
|
|||||||
- **Proxy:** optional `telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
|
- **Proxy:** optional `telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
|
||||||
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `telegram.webhookUrl` is set (otherwise it long-polls).
|
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `telegram.webhookUrl` is set (otherwise it long-polls).
|
||||||
- **Sessions:** direct chats map to `main`; groups map to `telegram:group:<chatId>`; replies route back to the same surface.
|
- **Sessions:** direct chats map to `main`; groups map to `telegram:group:<chatId>`; replies route back to the same surface.
|
||||||
- **Config knobs:** `telegram.botToken`, `telegram.groups`, `telegram.allowFrom`, `telegram.mediaMaxMb`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`.
|
- **Config knobs:** `telegram.botToken`, `telegram.groups` (allowlist + mention defaults), `telegram.allowFrom`, `telegram.mediaMaxMb`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`.
|
||||||
- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.
|
- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.
|
||||||
|
|
||||||
Open questions
|
Open questions
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ Goal: let Clawd sit in WhatsApp groups, wake up only when pinged, and keep that
|
|||||||
Note: `routing.groupChat.mentionPatterns` is now used by Telegram/Discord/Slack/iMessage as well; this doc focuses on WhatsApp-specific behavior.
|
Note: `routing.groupChat.mentionPatterns` is now used by Telegram/Discord/Slack/iMessage as well; this doc focuses on WhatsApp-specific behavior.
|
||||||
|
|
||||||
## What’s implemented (2025-12-03)
|
## What’s implemented (2025-12-03)
|
||||||
- Activation modes: `mention` (default) or `always`. `mention` requires a ping (real WhatsApp @-mentions via `mentionedJids`, regex patterns, or the bot’s E.164 anywhere in the text). `always` wakes the agent on every message but it should reply only when it can add meaningful value; otherwise it returns the silent token `NO_REPLY`. Defaults can be set in config (`whatsapp.groups`) and overridden per group via `/activation`.
|
- Activation modes: `mention` (default) or `always`. `mention` requires a ping (real WhatsApp @-mentions via `mentionedJids`, regex patterns, or the bot’s E.164 anywhere in the text). `always` wakes the agent on every message but it should reply only when it can add meaningful value; otherwise it returns the silent token `NO_REPLY`. Defaults can be set in config (`whatsapp.groups`) and overridden per group via `/activation`. When `whatsapp.groups` is set, it also acts as a group allowlist (include `"*"` to allow all).
|
||||||
- Group allowlist bypass: we still enforce `whatsapp.allowFrom` on the participant at inbox ingest, but group JIDs themselves no longer block replies.
|
- Group allowlist: `whatsapp.groups` gates which group JIDs are allowed; `whatsapp.allowFrom` still gates participants for direct chats.
|
||||||
- Per-group sessions: session keys look like `whatsapp:group:<jid>` so commands such as `/verbose on` or `/think:high` are scoped to that group; personal DM state is untouched. Heartbeats are skipped for group threads.
|
- Per-group sessions: session keys look like `whatsapp:group:<jid>` so commands such as `/verbose on` or `/think:high` are scoped to that group; personal DM state is untouched. Heartbeats are skipped for group threads.
|
||||||
- Context injection: last N (default 50) group messages are prefixed under `[Chat messages since your last reply - for context]`, with the triggering line under `[Current message - respond to this]`.
|
- Context injection: last N (default 50) group messages are prefixed under `[Chat messages since your last reply - for context]`, with the triggering line under `[Current message - respond to this]`.
|
||||||
- Sender surfacing: every group batch now ends with `[from: Sender Name (+E164)]` so Pi knows who is speaking.
|
- Sender surfacing: every group batch now ends with `[from: Sender Name (+E164)]` so Pi knows who is speaking.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Short guide to verify the WhatsApp Web / Baileys stack without guessing.
|
|||||||
## When something fails
|
## When something fails
|
||||||
- `logged out` or status 409–515 → relink with `clawdbot logout` then `clawdbot login`.
|
- `logged out` or status 409–515 → relink with `clawdbot logout` then `clawdbot login`.
|
||||||
- Gateway unreachable → start it: `clawdbot gateway --port 18789` (use `--force` if the port is busy).
|
- Gateway unreachable → start it: `clawdbot gateway --port 18789` (use `--force` if the port is busy).
|
||||||
- No inbound messages → confirm linked phone is online and the sender is allowed (`whatsapp.allowFrom`); for group chats, ensure mention rules match (`routing.groupChat.mentionPatterns` and `whatsapp.groups`).
|
- No inbound messages → confirm linked phone is online and the sender is allowed (`whatsapp.allowFrom`); for group chats, ensure allowlist + mention rules match (`whatsapp.groups`, `routing.groupChat.mentionPatterns`).
|
||||||
|
|
||||||
## Dedicated "health" command
|
## Dedicated "health" command
|
||||||
`clawdbot health --json` asks the running Gateway for its health snapshot (no direct Baileys socket from the CLI). It reports linked creds, auth age, Baileys connect result/status code, session-store summary, and a probe duration. It exits non-zero if the Gateway is unreachable or the probe fails/timeouts. Use `--timeout <ms>` to override the 10s default.
|
`clawdbot health --json` asks the running Gateway for its health snapshot (no direct Baileys socket from the CLI). It reports linked creds, auth age, Baileys connect result/status code, session-store summary, and a probe duration. It exits non-zero if the Gateway is unreachable or the probe fails/timeouts. Use `--timeout <ms>` to override the 10s default.
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ imsg chats --limit 20
|
|||||||
|
|
||||||
## Group chat behavior
|
## Group chat behavior
|
||||||
- Group messages set `ChatType=group`, `GroupSubject`, and `GroupMembers`.
|
- Group messages set `ChatType=group`, `GroupSubject`, and `GroupMembers`.
|
||||||
- Group activation respects `imessage.groups."*".requireMention` and `routing.groupChat.mentionPatterns` (patterns are required to detect mentions on iMessage).
|
- Group activation respects `imessage.groups."*".requireMention` and `routing.groupChat.mentionPatterns` (patterns are required to detect mentions on iMessage). When `imessage.groups` is set, it also acts as a group allowlist; include `"*"` to allow all groups.
|
||||||
- Replies go back to the same `chat_id` (group or direct).
|
- Replies go back to the same `chat_id` (group or direct).
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Status: ready for bot-mode use with grammY (long-polling by default; webhook sup
|
|||||||
- The webhook listener currently binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
- The webhook listener currently binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
||||||
- If you need a different public port/host, set `telegram.webhookUrl` to the externally reachable URL and use a reverse proxy to forward to `:8787`.
|
- If you need a different public port/host, set `telegram.webhookUrl` to the externally reachable URL and use a reverse proxy to forward to `:8787`.
|
||||||
4) Direct chats: user sends the first message; all subsequent turns land in the shared `main` session (default, no extra config).
|
4) Direct chats: user sends the first message; all subsequent turns land in the shared `main` session (default, no extra config).
|
||||||
5) Groups: add the bot, disable privacy mode (or make it admin) so it can read messages; group threads stay on `telegram:group:<chatId>` and require mention/command by default (override via `telegram.groups`).
|
5) Groups: add the bot, disable privacy mode (or make it admin) so it can read messages; group threads stay on `telegram:group:<chatId>`. When `telegram.groups` is set, it becomes a group allowlist (use `"*"` to allow all). Mention/command gating defaults come from `telegram.groups`.
|
||||||
6) Optional allowlist: use `telegram.allowFrom` for direct chats by chat id (`123456789` or `telegram:123456789`).
|
6) Optional allowlist: use `telegram.allowFrom` for direct chats by chat id (`123456789` or `telegram:123456789`).
|
||||||
|
|
||||||
## Capabilities & limits (Bot API)
|
## Capabilities & limits (Bot API)
|
||||||
@@ -37,7 +37,7 @@ Status: ready for bot-mode use with grammY (long-polling by default; webhook sup
|
|||||||
- Library: grammY is the only client for send + gateway (fetch fallback removed); grammY throttler is enabled by default to stay under Bot API limits.
|
- Library: grammY is the only client for send + gateway (fetch fallback removed); grammY throttler is enabled by default to stay under Bot API limits.
|
||||||
- Inbound normalization: maps Bot API updates to `MsgContext` with `Surface: "telegram"`, `ChatType: direct|group`, `SenderName`, `MediaPath`/`MediaType` when attachments arrive, `Timestamp`, and reply-to metadata (`ReplyToId`, `ReplyToBody`, `ReplyToSender`) when the user replies; reply context is appended to `Body` as a `[Replying to ...]` block (includes `id:` when available); groups require @bot mention or a `routing.groupChat.mentionPatterns` match by default (override per chat in config).
|
- Inbound normalization: maps Bot API updates to `MsgContext` with `Surface: "telegram"`, `ChatType: direct|group`, `SenderName`, `MediaPath`/`MediaType` when attachments arrive, `Timestamp`, and reply-to metadata (`ReplyToId`, `ReplyToBody`, `ReplyToSender`) when the user replies; reply context is appended to `Body` as a `[Replying to ...]` block (includes `id:` when available); groups require @bot mention or a `routing.groupChat.mentionPatterns` match by default (override per chat in config).
|
||||||
- Outbound: text and media (photo/video/audio/document) with optional caption; chunked to limits. Typing cue sent best-effort.
|
- Outbound: text and media (photo/video/audio/document) with optional caption; chunked to limits. Typing cue sent best-effort.
|
||||||
- Config: `TELEGRAM_BOT_TOKEN` env or `telegram.botToken` required; `telegram.groups`, `telegram.allowFrom`, `telegram.mediaMaxMb`, `telegram.replyToMode`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`, `telegram.webhookPath` supported.
|
- Config: `TELEGRAM_BOT_TOKEN` env or `telegram.botToken` required; `telegram.groups` (group allowlist + mention defaults), `telegram.allowFrom`, `telegram.mediaMaxMb`, `telegram.replyToMode`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`, `telegram.webhookPath` supported.
|
||||||
- Mention gating precedence (most specific wins): `telegram.groups.<chatId>.requireMention` → `telegram.groups."*".requireMention` → default `true`.
|
- Mention gating precedence (most specific wins): `telegram.groups.<chatId>.requireMention` → `telegram.groups."*".requireMention` → default `true`.
|
||||||
|
|
||||||
Example config:
|
Example config:
|
||||||
@@ -48,7 +48,7 @@ Example config:
|
|||||||
botToken: "123:abc",
|
botToken: "123:abc",
|
||||||
replyToMode: "off",
|
replyToMode: "off",
|
||||||
groups: {
|
groups: {
|
||||||
"*": { requireMention: true },
|
"*": { requireMention: true }, // allow all groups
|
||||||
"123456789": { requireMention: false } // group chat id
|
"123456789": { requireMention: false } // group chat id
|
||||||
},
|
},
|
||||||
allowFrom: ["123456789"], // direct chat ids allowed (or "*")
|
allowFrom: ["123456789"], // direct chat ids allowed (or "*")
|
||||||
@@ -65,7 +65,7 @@ Example config:
|
|||||||
## Group etiquette
|
## Group etiquette
|
||||||
- Keep privacy mode off if you expect the bot to read all messages; with privacy on, it only sees commands/mentions.
|
- Keep privacy mode off if you expect the bot to read all messages; with privacy on, it only sees commands/mentions.
|
||||||
- Make the bot an admin if you need it to send in restricted groups or channels.
|
- Make the bot an admin if you need it to send in restricted groups or channels.
|
||||||
- Mention the bot (`@yourbot`) or use a `routing.groupChat.mentionPatterns` trigger; per-group overrides live in `telegram.groups` if you want always-on behavior.
|
- Mention the bot (`@yourbot`) or use a `routing.groupChat.mentionPatterns` trigger; per-group overrides live in `telegram.groups` if you want always-on behavior. If `telegram.groups` is set, add `"*"` to keep existing allow-all behavior.
|
||||||
|
|
||||||
## Reply tags
|
## Reply tags
|
||||||
To request a threaded reply, the model can include one tag in its output:
|
To request a threaded reply, the model can include one tag in its output:
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ WhatsApp requires a real mobile number for verification. VoIP and virtual number
|
|||||||
|
|
||||||
## Config quick map
|
## Config quick map
|
||||||
- `whatsapp.allowFrom` (DM allowlist).
|
- `whatsapp.allowFrom` (DM allowlist).
|
||||||
- `whatsapp.groups` (group mention gating defaults/overrides)
|
- `whatsapp.groups` (group allowlist + mention gating defaults; use `"*"` to allow all)
|
||||||
- `routing.groupChat.mentionPatterns`
|
- `routing.groupChat.mentionPatterns`
|
||||||
- `routing.groupChat.historyLimit`
|
- `routing.groupChat.historyLimit`
|
||||||
- `messages.messagePrefix` (inbound prefix)
|
- `messages.messagePrefix` (inbound prefix)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx src/entry.ts",
|
"dev": "tsx src/entry.ts",
|
||||||
|
"postinstall": "node scripts/postinstall.js",
|
||||||
"docs:list": "tsx scripts/docs-list.ts",
|
"docs:list": "tsx scripts/docs-list.ts",
|
||||||
"docs:dev": "cd docs && mint dev",
|
"docs:dev": "cd docs && mint dev",
|
||||||
"docs:build": "cd docs && pnpm dlx mint broken-links",
|
"docs:build": "cd docs && pnpm dlx mint broken-links",
|
||||||
|
|||||||
106
scripts/postinstall.js
Normal file
106
scripts/postinstall.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { spawnSync } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
function isBunInstall() {
|
||||||
|
const ua = process.env.npm_config_user_agent ?? "";
|
||||||
|
return ua.includes("bun/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRepoRoot() {
|
||||||
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
return path.resolve(here, "..");
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(cmd, args, opts = {}) {
|
||||||
|
const res = spawnSync(cmd, args, { stdio: "inherit", ...opts });
|
||||||
|
if (typeof res.status === "number") return res.status;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyPatchIfNeeded(opts) {
|
||||||
|
const patchPath = path.resolve(opts.patchPath);
|
||||||
|
if (!fs.existsSync(patchPath)) {
|
||||||
|
throw new Error(`missing patch: ${patchPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetDir = path.resolve(opts.targetDir);
|
||||||
|
if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
|
||||||
|
console.warn(`[postinstall] skip missing target: ${targetDir}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gitArgsBase = ["apply", "--unsafe-paths", "--whitespace=nowarn"];
|
||||||
|
const reverseCheck = [
|
||||||
|
...gitArgsBase,
|
||||||
|
"--reverse",
|
||||||
|
"--check",
|
||||||
|
"--directory",
|
||||||
|
targetDir,
|
||||||
|
patchPath,
|
||||||
|
];
|
||||||
|
const forwardCheck = [
|
||||||
|
...gitArgsBase,
|
||||||
|
"--check",
|
||||||
|
"--directory",
|
||||||
|
targetDir,
|
||||||
|
patchPath,
|
||||||
|
];
|
||||||
|
const apply = [...gitArgsBase, "--directory", targetDir, patchPath];
|
||||||
|
|
||||||
|
// Already applied?
|
||||||
|
if (run("git", reverseCheck, { stdio: "ignore" }) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run("git", forwardCheck, { stdio: "ignore" }) !== 0) {
|
||||||
|
throw new Error(`patch does not apply cleanly: ${path.basename(patchPath)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = run("git", apply);
|
||||||
|
if (status !== 0) {
|
||||||
|
throw new Error(`failed applying patch: ${path.basename(patchPath)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractPackageName(key) {
|
||||||
|
if (key.startsWith("@")) {
|
||||||
|
const idx = key.indexOf("@", 1);
|
||||||
|
if (idx === -1) return key;
|
||||||
|
return key.slice(0, idx);
|
||||||
|
}
|
||||||
|
const idx = key.lastIndexOf("@");
|
||||||
|
if (idx <= 0) return key;
|
||||||
|
return key.slice(0, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
if (!isBunInstall()) return;
|
||||||
|
|
||||||
|
const repoRoot = getRepoRoot();
|
||||||
|
process.chdir(repoRoot);
|
||||||
|
|
||||||
|
const pkgPath = path.join(repoRoot, "package.json");
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
||||||
|
const patched = pkg?.pnpm?.patchedDependencies ?? {};
|
||||||
|
|
||||||
|
// Bun does not support pnpm.patchedDependencies. Apply these patch files to
|
||||||
|
// node_modules packages as a best-effort compatibility layer.
|
||||||
|
for (const [key, relPatchPath] of Object.entries(patched)) {
|
||||||
|
if (typeof relPatchPath !== "string" || !relPatchPath.trim()) continue;
|
||||||
|
const pkgName = extractPackageName(String(key));
|
||||||
|
if (!pkgName) continue;
|
||||||
|
applyPatchIfNeeded({
|
||||||
|
targetDir: path.join("node_modules", ...pkgName.split("/")),
|
||||||
|
patchPath: relPatchPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
main();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(String(err));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user