Skip to content

Commit 4d3d9cc

Browse files
committed
Add Bun bundle docs and Telegram grammY support
1 parent 7b77e9f commit 4d3d9cc

16 files changed

Lines changed: 883 additions & 9 deletions

docs/mac/bun.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Bundled Bun runtime (mac app only)
2+
3+
Date: 2025-12-07 · Owner: steipete · Scope: packaged mac app runtime
4+
5+
## What we ship
6+
- The mac menu-bar app embeds an **arm64 Bun runtime** under `Contents/Resources/Relay/` only for the packaged app. Dev/CI keep using pnpm+node.
7+
- Payload: `bun` binary (defaults to `/opt/homebrew/bin/bun`, override with `BUN_PATH=/path/to/bun`), `dist/` output, production `node_modules/`, and the root `package.json`/`pnpm-lock.yaml` for provenance.
8+
- We prune dev/build tooling (vite, rolldown, biome, vitest, tsc/tsx, @types, etc.) and drop all non-macOS sharp vendors so only `sharp-darwin-arm64` + `sharp-libvips-darwin-arm64` remain.
9+
10+
## Build/packaging flow
11+
- Run `scripts/package-mac-app.sh` (or `BUN_PATH=/custom/bun scripts/package-mac-app.sh`).
12+
- Ensures deps via `pnpm install`, then `pnpm exec tsc`.
13+
- Builds the Swift app and stages `dist/`, Bun, and production `node_modules` into `Contents/Resources/Relay/` using a temp deploy (hoisted layout, no dev deps).
14+
- Prunes optional tooling + extra sharp vendors, then codesigns binaries and native addons.
15+
- Architecture: **arm64 only**. Ship a separate bundle if you need Rosetta/x64.
16+
17+
## Runtime behavior
18+
- `CommandResolver` prefers the bundled `bun dist/index.js <subcommand>` when present; falls back to system `clawdis`/pnpm/node otherwise.
19+
- `RelayProcessManager` runs in the bundled cwd/PATH so native deps (sharp, undici) resolve without installing anything on the host.
20+
21+
## Testing the bundle
22+
- After packaging: `cd dist/Clawdis.app/Contents/Resources/Relay && ./bun dist/index.js --help` should print the CLI help without missing-module errors.
23+
- If sharp fails to load, confirm the remaining `@img/sharp-darwin-arm64` + `@img/sharp-libvips-darwin-arm64` directories exist and are codesigned.
24+
25+
## Notes / limits
26+
- Bundle is mac-app-only; keep using pnpm+node for dev/test.
27+
- Packaging stops early if Bun or `pnpm build` prerequisites are missing.
28+
29+
## FAQ
30+
- **What does `--legacy` do?** When used with `pnpm deploy`, `--legacy` builds a classic flattened `node_modules` layout instead of pnpm's symlinked structure. We no longer need it in the current packaging flow because we create a self-contained hoisted install directly in the temp deploy dir.

docs/telegram.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Telegram (Bot API)
2+
3+
Updated: 2025-12-07
4+
5+
Status: ready for bot-mode use with grammY (long-poll + webhook). Text + media send, proxy, and webhook helpers all ship in-tree.
6+
7+
## Goals
8+
- Let you talk to Clawdis via a Telegram bot in DMs and groups.
9+
- Share the same `main` session used by WhatsApp/WebChat; groups stay isolated as `group:<chatId>`.
10+
- Keep transport routing deterministic: replies always go back to the surface they arrived on.
11+
12+
## How it will work (Bot API)
13+
1) Create a bot with @BotFather and grab the token.
14+
2) Configure Clawdis with `TELEGRAM_BOT_TOKEN` (or `telegram.botToken` in `~/.clawdis/clawdis.json`).
15+
3) Run the relay with provider `telegram` via `clawdis relay:telegram` (grammY long-poll). Webhook mode: `clawdis relay:telegram --webhook --port 8787 --webhook-secret <secret>` (optionally `--webhook-url` when the public URL differs).
16+
4) Direct chats: user sends the first message; all subsequent turns land in the shared `main` session (default, no extra config).
17+
5) Groups: add the bot, disable privacy mode (or make it admin) so it can read messages; group threads stay on `group:<chatId>` and require mention/command to trigger replies.
18+
6) Optional allowlist: reuse `inbound.allowFrom` for direct chats by chat id (`123456789` or `telegram:123456789`).
19+
20+
## Capabilities & limits (Bot API)
21+
- Sees only messages sent after it’s added to a chat; no pre-history access.
22+
- Cannot DM users first; they must initiate. Channels are receive-only unless the bot is an admin poster.
23+
- File size caps follow Telegram Bot API (up to 2 GB for documents; smaller for some media types).
24+
- Typing indicators (`sendChatAction`) supported; inline reply/threading supported where Telegram allows.
25+
26+
## Planned implementation details
27+
- Library: grammY is the only client for send + relay (fetch fallback removed).
28+
- Inbound normalization: maps Bot API updates to `MsgContext` with `Surface: "telegram"`, `ChatType: direct|group`, `SenderName`, `MediaPath`/`MediaType` when attachments arrive, and `Timestamp`; groups require @bot mention by default.
29+
- Outbound: text and media (photo/video/audio/document) with optional caption; chunked to limits. Typing cue sent best-effort.
30+
- Config: `TELEGRAM_BOT_TOKEN` env or `telegram.botToken` required; `telegram.requireMention`, `telegram.allowFrom`, `telegram.mediaMaxMb`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl` supported.
31+
32+
Example config:
33+
```json5
34+
{
35+
telegram: {
36+
botToken: "123:abc",
37+
requireMention: true,
38+
allowFrom: ["123456789"], // direct chat ids allowed (or "*")
39+
mediaMaxMb: 5,
40+
proxy: "socks5://localhost:9050",
41+
webhookSecret: "mysecret",
42+
webhookUrl: "https://yourdomain.com/telegram-webhook"
43+
}
44+
}
45+
```
46+
- Tests: grammY-based paths in `src/telegram/*.test.ts` cover DM + group gating; add more media and webhook cases as needed.
47+
48+
## Group etiquette
49+
- Keep privacy mode off if you expect the bot to read all messages; with privacy on, it only sees commands/mentions.
50+
- Make the bot an admin if you need it to send in restricted groups or channels.
51+
- Mention the bot (`@yourbot`) or use commands to trigger; we’ll honor `group.requireMention` by default to avoid noise.
52+
53+
## Roadmap
54+
- ✅ Design and defaults (this doc)
55+
- ✅ grammY long-poll relay + text/media send
56+
- ✅ Proxy + webhook helpers (setWebhook/deleteWebhook, health endpoint, optional public URL)
57+
- ⏳ Add more grammY coverage (webhook payloads, media edge cases)
58+
59+
## Safety & ops
60+
- Treat the bot token as a secret (equivalent to account control); store under `~/.clawdis/credentials/` with 0600 perms.
61+
- Respect Telegram rate limits (429s); we’ll add throttling in the provider to stay below flood thresholds.
62+
- Use a test bot for development to avoid hitting production chats.

package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@mariozechner/pi-coding-agent": "^0.13.2",
3434
"@whiskeysockets/baileys": "7.0.0-rc.9",
3535
"body-parser": "^2.2.1",
36+
"@grammyjs/transformer-throttler": "^1.2.1",
3637
"chalk": "^5.6.2",
3738
"commander": "^14.0.2",
3839
"dotenv": "^17.2.3",
@@ -42,7 +43,9 @@
4243
"tslog": "^4.9.3",
4344
"qrcode-terminal": "^0.12.0",
4445
"sharp": "^0.34.5",
45-
"zod": "^4.1.13"
46+
"undici": "^6.20.1",
47+
"zod": "^4.1.13",
48+
"grammy": "^1.27.0"
4649
},
4750
"devDependencies": {
4851
"@biomejs/biome": "^2.3.8",
@@ -82,10 +85,16 @@
8285
"src/**/*.test.ts"
8386
]
8487
},
88+
"include": [
89+
"src/**/*.test.ts"
90+
],
8591
"exclude": [
8692
"dist/**",
8793
"apps/macos/**",
88-
"apps/macos/.build/**"
94+
"apps/macos/.build/**",
95+
"**/vendor/**",
96+
"apps/macos/.build/**",
97+
"dist/Clawdis.app/**"
8998
]
9099
}
91100
}

scripts/package-mac-app.sh

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,32 @@ pnpm install \
117117
--config.enable-pre-post-scripts=true \
118118
--config.ignore-workspace-root-check=true \
119119
--config.shared-workspace-lockfile=false \
120-
--lockfile-dir "$ROOT_DIR" \
120+
--config.node-linker=hoisted \
121+
--lockfile-dir "$TMP_DEPLOY" \
121122
--dir "$TMP_DEPLOY"
122123
PNPM_STORE_DIR="$TMP_DEPLOY/.pnpm-store" \
123124
PNPM_HOME="$HOME/Library/pnpm" \
124125
pnpm rebuild sharp --config.ignore-workspace-root-check=true --dir "$TMP_DEPLOY"
125-
rsync -aL "$TMP_DEPLOY/node_modules/" "$RELAY_DIR/node_modules/"
126-
# Flatten sharp copies and prune dev artifacts
127-
find "$RELAY_DIR/node_modules/.pnpm" -maxdepth 1 -name "*sharp*" -type d -print0 | xargs -0 -I{} rsync -a --delete "{}/node_modules/@img/sharp-darwin-arm64" "$RELAY_DIR/node_modules/@img/" 2>/dev/null || true
128-
find "$RELAY_DIR/node_modules/.pnpm" -maxdepth 1 -name "*sharp-libvips*" -type d -print0 | xargs -0 -I{} rsync -a --delete "{}/node_modules/@img/sharp-libvips-darwin-arm64" "$RELAY_DIR/node_modules/@img/" 2>/dev/null || true
129-
rm -rf "$RELAY_DIR/node_modules/.pnpm"/*sharp* "$RELAY_DIR/node_modules/.pnpm/node_modules/@img" 2>/dev/null || true
130-
rm -f "$RELAY_DIR/node_modules/.bin"/vite "$RELAY_DIR/node_modules/.bin"/rolldown "$RELAY_DIR/node_modules/.bin"/biome 2>/dev/null || true
126+
rsync -a "$TMP_DEPLOY/node_modules/" "$RELAY_DIR/node_modules/"
127+
128+
# Keep only the arm64 macOS sharp vendor payloads to shrink the bundle
129+
SHARP_VENDOR_DIR="$RELAY_DIR/node_modules/@img"
130+
if [ -d "$SHARP_VENDOR_DIR" ]; then
131+
find "$SHARP_VENDOR_DIR" -maxdepth 1 -type d -name "sharp-*" \
132+
! -name "sharp-darwin-arm64" \
133+
! -name "sharp-libvips-darwin-arm64" -exec rm -rf {} +
134+
fi
135+
136+
# Prune obvious dev/build tooling to keep size down
137+
rm -rf \
138+
"$RELAY_DIR/node_modules/.bin"/vite \
139+
"$RELAY_DIR/node_modules/.bin"/rolldown \
140+
"$RELAY_DIR/node_modules/.bin"/biome \
141+
"$RELAY_DIR/node_modules/.bin"/vitest \
142+
"$RELAY_DIR/node_modules/.bin"/tsc \
143+
"$RELAY_DIR/node_modules/.bin"/tsx 2>/dev/null || true
144+
rm -rf \
145+
"$RELAY_DIR/node_modules"/{vite,rolldown,vitest,ts-node,ts-node-dev,typescript,@types,docx-preview,jszip,lucide,ollama} 2>/dev/null || true
131146
rm -rf "$TMP_DEPLOY"
132147

133148
if [ -f "$CLI_BIN" ]; then

0 commit comments

Comments
 (0)