Skip to content

Commit f13b320

Browse files
committed
vendor wherenow and pilegram
1 parent 7cd0e81 commit f13b320

50 files changed

Lines changed: 7399 additions & 76 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

flake.lock

Lines changed: 1 addition & 57 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,6 @@
1616
url = "github:nix-community/nixvim/nixos-26.05";
1717
inputs.nixpkgs.follows = "nixpkgs";
1818
};
19-
# my pi ⇄ Telegram gateway (the assistant's bridge). Deliberately does NOT
20-
# follow our nixpkgs, even though both track 26.05: it's a hermetic flake
21-
# whose node_modules is a fixed-output derivation pinned to its own nixpkgs'
22-
# bun, and repinning bun (even across 26.05 revs) would break that hash.
23-
pilegram.url = "github:ngalaiko/pilegram";
24-
# wherenow: my self-hosted "Where Now?" location backend. Pure Go, so unlike
25-
# pilegram it follows our nixpkgs. Runs as the assistant and writes each
26-
# position straight into the Obsidian vault as a note — see
27-
# hosts/exedev/users/assistant.nix.
28-
wherenow = {
29-
url = "github:ngalaiko/wherenow";
30-
inputs.nixpkgs.follows = "nixpkgs";
31-
};
3219
# Installs and owns the Homebrew prefix itself, so a fresh Mac doesn't need
3320
# brew pre-installed — the first darwin-rebuild switch bootstraps it. The
3421
# nix-darwin `homebrew` module only manages an already-installed brew.

hosts/exedev/users/assistant.nix

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ let
33
# pi (the coding agent) is an npm CLI, packaged from its published tarball.
44
# MIT, all-JS deps, so it builds against the pinned nixpkgs directly.
55
pi = import ../../../packages/pi { inherit pkgs; };
6-
# pilegram: my pi ⇄ Telegram gateway (Bun), consumed as a flake input. It's a
6+
# pilegram: my pi ⇄ Telegram gateway (Bun), packaged in this repo. It's a
77
# hermetic package — it bundles bun, ffmpeg, whisper.cpp and a pinned
88
# node_modules that includes its own pi 0.83.0 — so it needs nothing else from
99
# this account and survives recreations regardless of backup.
10-
pilegram = inputs.pilegram.packages.${pkgs.system}.default;
11-
# wherenow: the "Where Now?" location backend (flake input). Pure Go; writes
12-
# positions into the vault as notes (no database).
13-
wherenow = inputs.wherenow.packages.${pkgs.system}.default;
10+
pilegram = import ../../../packages/pilegram { inherit pkgs; };
11+
# wherenow: the "Where Now?" location backend, packaged in this repo. Pure Go;
12+
# writes positions into the vault as notes (no database).
13+
wherenow = import ../../../packages/wherenow { inherit pkgs; };
1414
# Official Obsidian Sync headless CLI, packaged here so the assistant can run
1515
# on-demand vault syncs without fetching npm packages at runtime.
1616
obsidian-headless = import ../../../packages/obsidian-headless { inherit pkgs; };
@@ -220,7 +220,7 @@ in
220220
'';
221221
};
222222

223-
# Telegram bridge for pi, via pilegram (the flake input above). Long-polling
223+
# Telegram bridge for pi, via pilegram (packaged above). Long-polling
224224
# needs only outbound HTTPS, so nothing is exposed on the tailnet or the image.
225225
# pilegram reads pi's provider keys from ~/.pi and keeps its own state under
226226
# ~/.config/pilegram; run in the foreground so s6 supervises it.

packages/pilegram/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# The only secret. Everything else is a CLI flag (see `bun run src/index.ts --help`).
2+
# Bun auto-loads this .env.
3+
TELEGRAM_BOT_TOKEN=123456:AA...
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [master]
7+
8+
jobs:
9+
check:
10+
name: Typecheck and test
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v5
15+
16+
- name: Install Nix
17+
uses: cachix/install-nix-action@v31
18+
with:
19+
nix_path: nixpkgs=channel:nixos-26.05
20+
21+
- name: Install dependencies
22+
run: nix shell nixpkgs#bun -c bun install --frozen-lockfile
23+
24+
- name: Typecheck
25+
run: nix shell nixpkgs#bun -c bun run typecheck
26+
27+
- name: Test
28+
run: nix shell nixpkgs#bun -c bun run test
29+
30+
- name: Build package
31+
run: nix build .#default

packages/pilegram/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
node_modules/
2+
.env
3+
.env.*
4+
!.env.example
5+
data/
6+
*.db
7+
*.db-wal
8+
*.db-shm
9+
dist/
10+
.DS_Store
11+
*.log

packages/pilegram/README.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# pilegram
2+
3+
<p align="center">
4+
<img src="assets/logo.png" alt="pilegram" width="200">
5+
</p>
6+
7+
A personal [pi](https://github.com/earendil-works/pi) coding agent, over Telegram.
8+
9+
## Features
10+
11+
- **Streaming replies** via an editable provisional message, so you can still send `/stop` or steer the agent mid-turn.
12+
- **Topics = sessions**: each Telegram topic is its own agent session with its own
13+
workspace and context. The agent can list known threads, create threads, and,
14+
after explicit confirmation, delete them; it also keeps each topic's **title and
15+
icon** relevant on its own.
16+
- **Markdown → Telegram HTML**: headings, code, lists, tables, links render properly.
17+
- **Images** in (vision) & out, **documents** in (path + inlined text) & out, **albums** both ways.
18+
- **Voice**, fully local: voice notes are transcribed with **whisper.cpp**; replies
19+
come back as voice notes synthesized with **Supertonic** (in-process ONNX). Text
20+
always available as a fallback.
21+
- **Reactions**: the agent can react to your messages instead of replying in text.
22+
- **Steering**: a message sent mid-turn is injected into the running turn.
23+
- **Rich inbound handling**: replies/quotes, forwards, edited messages, stickers,
24+
locations, contacts, polls, dice.
25+
26+
The only bot command is **`/stop`** (abort the current turn). Everything else you
27+
type (or say, or send) is a prompt.
28+
29+
## Prerequisites
30+
31+
pilegram runs on **[Bun](https://bun.sh)** and shells out to three tools for voice:
32+
33+
| Tool | Why | Provides |
34+
| --------------- | -------------------------- | ------------------- |
35+
| **bun** ≥ 1.3 | runtime ||
36+
| **ffmpeg** | transcode audio ↔ OGG/Opus | `ffmpeg`, `ffprobe` |
37+
| **whisper.cpp** | speech-to-text | `whisper-cli` |
38+
39+
TTS (Supertonic) runs in-process via the `onnxruntime-node` npm dependency — no
40+
extra system package. Speech model weights download automatically on first use.
41+
42+
## Install
43+
44+
### Without Nix
45+
46+
Install the three tools with your package manager, then the app:
47+
48+
```bash
49+
# macOS (Homebrew)
50+
brew install oven-sh/bun/bun ffmpeg whisper-cpp
51+
52+
# Debian/Ubuntu: bun via https://bun.sh; ffmpeg via apt; whisper.cpp from source
53+
# (must expose a `whisper-cli` binary on PATH)
54+
sudo apt install ffmpeg
55+
56+
git clone <this repo> && cd pilegram
57+
bun install
58+
export TELEGRAM_BOT_TOKEN=123456:AA... # the one secret (or put it in .env)
59+
bun run src/index.ts --allow 111222333 # your Telegram user id(s)
60+
```
61+
62+
`whisper-cli`, `ffmpeg`, and `ffprobe` must be on your `PATH`. That's the only
63+
requirement voice has — everything else is handled by `bun install`.
64+
65+
### With Nix
66+
67+
The flake builds a **hermetic** package — pinned toolchain (bun + ffmpeg +
68+
whisper.cpp) _and_ pinned `node_modules` — so it runs from anywhere, not just the
69+
repo:
70+
71+
```bash
72+
# one-shot: builds the package and runs it
73+
TELEGRAM_BOT_TOKEN=… nix run github:you/pilegram -- --allow 111222333
74+
75+
# or install it onto your PATH
76+
nix profile install github:you/pilegram
77+
TELEGRAM_BOT_TOKEN=… pilegram --allow 111222333
78+
79+
# or a dev shell (bun, ffmpeg, whisper-cli), to run from source
80+
nix develop
81+
bun install
82+
bun run src/index.ts --allow 111222333
83+
```
84+
85+
`node_modules` is a fixed-output derivation: `bun install --frozen-lockfile`
86+
whose result is pinned by content hash (this keeps the native `onnxruntime-node`
87+
dylib intact — a real node_modules layout, unlike `bun build --compile`). **When
88+
you change dependencies**, bump that hash: run `nix build .#node-modules`, copy
89+
the `got: sha256-…` it prints into `outputHash` in `flake.nix`.
90+
91+
## Telegram setup (BotFather)
92+
93+
1. Create a bot with [@BotFather](https://t.me/BotFather) → copy the **token**.
94+
2. **Enable topics**: BotFather → `/mybots` → your bot → _Bot Settings_ → turn on
95+
**Threaded Mode**. (Without this, pilegram runs a single-session DM.)
96+
3. Find **your numeric user id** (e.g. via [@userinfobot](https://t.me/userinfobot)).
97+
98+
## Configuration
99+
100+
The one **secret** is `TELEGRAM_BOT_TOKEN` (from the environment / `.env`, never a
101+
flag — argv is visible in `ps`). Everything else is a **CLI flag** (`--help`):
102+
103+
| Flag | Required | Default | Notes |
104+
| ------------------------ | -------- | ------------------------------------------------------- | --------------------------------------------------------------------------- |
105+
| `--allow <ids>` ||| comma-separated Telegram user ids; everyone else is dropped silently |
106+
| `--state-dir <path>` | | `$XDG_CONFIG_HOME/pilegram` (else `~/.config/pilegram`) | SQLite db |
107+
| `--models-dir <path>` | | `$XDG_CACHE_HOME/pilegram` (else `~/.cache/pilegram`) | downloaded speech models (~2 GB cache) |
108+
| `--db-path <path>` | | `<state-dir>/pilegram.db` | override the db path |
109+
| `--whisper-model <name>` | | `large-v3-turbo` | whisper.cpp ggml model (e.g. `base.en` for a fast/small English-only start) |
110+
| `--voice <id>` | | `M1` | Supertonic voice: `M1``M5`, `F1``F5` |
111+
| `--tz <zone>` | | `Europe/Stockholm` | local time shown to the agent |
112+
| `--poll-timeout <sec>` | | `30` | long-poll seconds |
113+
| `--log-level <level>` | | `info` | `debug`\|`info`\|`warn`\|`error` |
114+
115+
## Trust model
116+
117+
pilegram is single-user by design. Only the Telegram user ids in `--allow` are
118+
served; every other update is logged and dropped.
119+
120+
The agent runs with **full trust and no approval gate** — it runs shell commands,
121+
reads and writes files, and sends messages on your behalf without asking. It runs
122+
as _your_ user, with the same filesystem and network access you have: its working
123+
directory is your home directory, **not a sandbox**, and tools such as
124+
`tg_send_document` accept absolute paths. Only `--allow` ids you
125+
trust to drive an unsandboxed agent, and run pilegram as a dedicated,
126+
least-privileged user (or in a container) if that reach concerns you.

packages/pilegram/assets/logo.png

10.8 KB
Loading

0 commit comments

Comments
 (0)