wachi 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -17
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,38 +1,225 @@
|
|
|
1
1
|
# wachi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/wachi)
|
|
4
|
+
[](https://github.com/ysm-dev/wachi/actions)
|
|
5
|
+
[](https://github.com/ysm-dev/wachi/blob/main/LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
**Subscribe any link and get notified on change.**
|
|
8
|
+
|
|
9
|
+
wachi monitors any URL for new content and pushes notifications to 90+ services via [apprise](https://github.com/caronc/apprise). It auto-discovers RSS feeds when available, and uses LLM-powered CSS selector identification for everything else.
|
|
10
|
+
|
|
11
|
+
- **Zero config for RSS** -- point at a blog, wachi finds the feed
|
|
12
|
+
- **LLM-powered for the rest** -- no RSS? wachi uses AI to identify content selectors via accessibility tree analysis
|
|
13
|
+
- **90+ notification services** -- Slack, Discord, Telegram, email, and [more](https://github.com/caronc/apprise/wiki)
|
|
14
|
+
- **Stateless by design** -- `wachi check` is a one-shot command, perfect for cron
|
|
15
|
+
- **No interactive prompts** -- built for automation and AI agents
|
|
16
|
+
|
|
17
|
+
## Install
|
|
6
18
|
|
|
7
19
|
```bash
|
|
8
|
-
bun
|
|
9
|
-
|
|
20
|
+
# npm / bun
|
|
21
|
+
npx wachi --help
|
|
22
|
+
bunx wachi --help
|
|
23
|
+
|
|
24
|
+
# or install globally
|
|
25
|
+
npm i -g wachi
|
|
26
|
+
bun i -g wachi
|
|
27
|
+
|
|
28
|
+
# shell script
|
|
29
|
+
curl -fsSL https://raw.githubusercontent.com/ysm-dev/wachi/main/install.sh | sh
|
|
30
|
+
|
|
31
|
+
# homebrew
|
|
32
|
+
brew tap ysm-dev/tap && brew install wachi
|
|
10
33
|
```
|
|
11
34
|
|
|
12
|
-
##
|
|
35
|
+
## Quick Start
|
|
13
36
|
|
|
14
37
|
```bash
|
|
15
|
-
|
|
16
|
-
wachi
|
|
17
|
-
|
|
38
|
+
# 1. Subscribe to any URL (auto-discovers RSS)
|
|
39
|
+
wachi sub "slack://xoxb-token/channel" "https://blog.example.com"
|
|
40
|
+
|
|
41
|
+
# 2. Check for new content (run this on a schedule)
|
|
18
42
|
wachi check
|
|
19
|
-
|
|
20
|
-
|
|
43
|
+
|
|
44
|
+
# That's it. New posts get pushed to your Slack channel.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## How It Works
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
wachi sub <apprise-url> <url>
|
|
51
|
+
│
|
|
52
|
+
▼
|
|
53
|
+
Is it RSS? ───yes───▶ Store as RSS subscription
|
|
54
|
+
│no
|
|
55
|
+
▼
|
|
56
|
+
Auto-discover RSS ───found───▶ Store URL + discovered feed
|
|
57
|
+
(link tags, common paths)
|
|
58
|
+
│not found
|
|
59
|
+
▼
|
|
60
|
+
LLM identifies content via accessibility tree
|
|
61
|
+
│
|
|
62
|
+
Derive CSS selectors from DOM (deterministic)
|
|
63
|
+
│
|
|
64
|
+
Validate selectors against raw HTTP
|
|
65
|
+
│
|
|
66
|
+
Store URL + selectors + baseline
|
|
21
67
|
```
|
|
22
68
|
|
|
23
|
-
|
|
69
|
+
On `wachi check`, each subscription is fetched and compared against a dedup table. New items trigger notifications via apprise. Old items are skipped. That's it.
|
|
70
|
+
|
|
71
|
+
## Commands
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
wachi sub <apprise-url> <url> Subscribe a URL to a notification channel
|
|
75
|
+
-e, --send-existing Send all current items on next check (skip baseline)
|
|
76
|
+
|
|
77
|
+
wachi unsub <apprise-url> [url] Unsubscribe a URL or remove entire channel
|
|
78
|
+
|
|
79
|
+
wachi ls List all channels and subscriptions
|
|
80
|
+
|
|
81
|
+
wachi check Check all subscriptions for changes
|
|
82
|
+
-c, --channel <apprise-url> Check specific channel only
|
|
83
|
+
-n, --concurrency <number> Max concurrent checks (default: 10)
|
|
84
|
+
-d, --dry-run Preview without sending or recording
|
|
85
|
+
|
|
86
|
+
wachi test <apprise-url> Send a test notification
|
|
87
|
+
|
|
88
|
+
wachi upgrade Update wachi to latest version
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Global flags:** `--json` / `-j` for machine-readable output, `--verbose` / `-V` for detailed logs, `--config` / `-C` for custom config path.
|
|
92
|
+
|
|
93
|
+
## Examples
|
|
24
94
|
|
|
25
95
|
```bash
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
96
|
+
# Blog (auto-discovers RSS)
|
|
97
|
+
wachi sub "slack://xoxb-token/channel" "https://blog.example.com"
|
|
98
|
+
|
|
99
|
+
# Hacker News front page (LLM identifies content selectors)
|
|
100
|
+
wachi sub "discord://webhook-id/token" "https://news.ycombinator.com"
|
|
101
|
+
|
|
102
|
+
# YouTube channel
|
|
103
|
+
wachi sub "tgram://bot-token/chat-id" "https://youtube.com/@channel"
|
|
104
|
+
|
|
105
|
+
# URL without https:// (auto-prepended)
|
|
106
|
+
wachi sub "slack://token/channel" "blog.example.com"
|
|
107
|
+
|
|
108
|
+
# Send all existing items on next check (no baseline)
|
|
109
|
+
wachi sub -e "discord://webhook-id/token" "https://news.ycombinator.com"
|
|
110
|
+
|
|
111
|
+
# Dry-run: see what would be sent
|
|
112
|
+
wachi check -d
|
|
113
|
+
|
|
114
|
+
# Check specific channel only
|
|
115
|
+
wachi check -c "slack://xoxb-token/channel"
|
|
116
|
+
|
|
117
|
+
# Run every 5 minutes with crnd
|
|
118
|
+
crnd "*/5 * * * *" wachi check
|
|
119
|
+
|
|
120
|
+
# System cron
|
|
121
|
+
crontab -e
|
|
122
|
+
# */5 * * * * wachi check
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Notifications
|
|
126
|
+
|
|
127
|
+
wachi uses [apprise](https://github.com/caronc/apprise) for delivery -- Slack, Discord, Telegram, Email, Pushover, Gotify, ntfy, and [90+ more](https://github.com/caronc/apprise/wiki).
|
|
128
|
+
|
|
129
|
+
Each new item is sent as a separate message:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
https://blog.example.com/post/new-feature
|
|
133
|
+
|
|
134
|
+
New Feature: Faster Builds with Incremental Compilation
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Test your channel before subscribing:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
wachi test "slack://xoxb-token/channel"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
Config lives at `~/.config/wachi/config.yml` (XDG-compliant). Auto-created on first `wachi sub`.
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
# LLM config (only needed for non-RSS sites)
|
|
149
|
+
# Also settable via WACHI_LLM_API_KEY, WACHI_LLM_MODEL env vars
|
|
150
|
+
llm:
|
|
151
|
+
api_key: "sk-..."
|
|
152
|
+
model: "gpt-4.1-mini"
|
|
153
|
+
|
|
154
|
+
# Optional: summarize articles before sending
|
|
155
|
+
summary:
|
|
156
|
+
enabled: true
|
|
157
|
+
language: "en"
|
|
158
|
+
min_reading_time: 3 # minutes
|
|
159
|
+
|
|
160
|
+
# Channels and subscriptions (managed by wachi sub/unsub)
|
|
161
|
+
channels:
|
|
162
|
+
- apprise_url: "slack://xoxb-token/channel"
|
|
163
|
+
subscriptions:
|
|
164
|
+
- url: "https://blog.example.com"
|
|
165
|
+
rss_url: "https://blog.example.com/feed.xml"
|
|
166
|
+
- url: "https://news.ycombinator.com"
|
|
167
|
+
item_selector: "tr.athing"
|
|
168
|
+
title_selector: ".titleline > a"
|
|
169
|
+
link_selector: ".titleline > a"
|
|
30
170
|
```
|
|
31
171
|
|
|
32
|
-
|
|
172
|
+
All fields are optional with sensible defaults. An empty config file is valid.
|
|
173
|
+
|
|
174
|
+
| Variable | Purpose |
|
|
175
|
+
|----------|---------|
|
|
176
|
+
| `WACHI_LLM_API_KEY` | LLM API key |
|
|
177
|
+
| `WACHI_LLM_MODEL` | LLM model name |
|
|
178
|
+
| `WACHI_LLM_BASE_URL` | LLM API base URL (default: OpenAI) |
|
|
179
|
+
| `WACHI_NO_AUTO_UPDATE` | Set to `1` to disable auto-update |
|
|
180
|
+
|
|
181
|
+
## Design
|
|
182
|
+
|
|
183
|
+
- **Stateless checks** -- `wachi check` is a one-shot command. Bring your own scheduler (cron, crnd, systemd, launchd)
|
|
184
|
+
- **Dedup, not state** -- items tracked by `sha256(link + title + channel)`. If the hash exists, it was already sent
|
|
185
|
+
- **No interactive prompts** -- ever. Errors tell you exactly what to set and where (What / Why / Fix pattern)
|
|
186
|
+
- **Baseline seeding** -- on subscribe, all current items are pre-seeded so your channel isn't flooded
|
|
187
|
+
- **Auto-healing** -- CSS selectors go stale? After 3 consecutive failures, wachi re-identifies them automatically
|
|
188
|
+
- **SQLite WAL mode** -- safe concurrent reads. Two cron jobs won't conflict
|
|
189
|
+
- **Atomic config writes** -- write to temp, then rename. No corruption from concurrent access
|
|
190
|
+
- **JSON envelope** -- `--json` on all commands returns `{"ok": true, "data": {...}}` or `{"ok": false, "error": {"what", "why", "fix"}}`
|
|
191
|
+
|
|
192
|
+
## Development
|
|
33
193
|
|
|
34
194
|
```bash
|
|
195
|
+
bun install
|
|
196
|
+
bun run src/index.ts --help
|
|
197
|
+
|
|
198
|
+
# Quality checks
|
|
199
|
+
bun run lint # Biome v2
|
|
200
|
+
bun run typecheck # tsgo
|
|
201
|
+
bun test # Bun test runner
|
|
202
|
+
bun run knip # Dead code detection
|
|
203
|
+
|
|
204
|
+
# Database migrations
|
|
35
205
|
bun run db:generate
|
|
36
206
|
```
|
|
37
207
|
|
|
38
|
-
|
|
208
|
+
### Tech Stack
|
|
209
|
+
|
|
210
|
+
| Component | Choice |
|
|
211
|
+
|-----------|--------|
|
|
212
|
+
| Runtime | Bun (`bun:sqlite`, `bun build --compile`) |
|
|
213
|
+
| Type checker | tsgo (`@typescript/native-preview`) |
|
|
214
|
+
| CLI | [citty](https://github.com/unjs/citty) |
|
|
215
|
+
| Database | [drizzle-orm](https://github.com/drizzle-team/drizzle-orm) + bun:sqlite |
|
|
216
|
+
| HTTP | [ofetch](https://github.com/unjs/ofetch) |
|
|
217
|
+
| LLM | [Vercel AI SDK](https://github.com/vercel/ai) v6 |
|
|
218
|
+
| RSS | [rss-parser](https://github.com/rbren/rss-parser) |
|
|
219
|
+
| HTML | [cheerio](https://github.com/cheeriojs/cheerio) |
|
|
220
|
+
| Notifications | [apprise](https://github.com/caronc/apprise) via uvx |
|
|
221
|
+
| Linter | [Biome](https://biomejs.dev/) v2 |
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wachi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Subscribe any link and get notified on change",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"zod-validation-error": "5.0.0"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
|
-
"@wachi-cli/darwin-arm64": "0.1.
|
|
57
|
-
"@wachi-cli/darwin-x64": "0.1.
|
|
58
|
-
"@wachi-cli/linux-arm64": "0.1.
|
|
59
|
-
"@wachi-cli/linux-x64": "0.1.
|
|
60
|
-
"@wachi-cli/win32-x64": "0.1.
|
|
56
|
+
"@wachi-cli/darwin-arm64": "0.1.2",
|
|
57
|
+
"@wachi-cli/darwin-x64": "0.1.2",
|
|
58
|
+
"@wachi-cli/linux-arm64": "0.1.2",
|
|
59
|
+
"@wachi-cli/linux-x64": "0.1.2",
|
|
60
|
+
"@wachi-cli/win32-x64": "0.1.2"
|
|
61
61
|
}
|
|
62
62
|
}
|