lockin-mcp 1.0.0

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.
Files changed (175) hide show
  1. package/README.md +349 -0
  2. package/assets/icon-256.png +0 -0
  3. package/assets/icon-32.png +0 -0
  4. package/assets/icon.svg +4 -0
  5. package/dist/blocker.d.ts +23 -0
  6. package/dist/blocker.d.ts.map +1 -0
  7. package/dist/blocker.js +184 -0
  8. package/dist/blocker.js.map +1 -0
  9. package/dist/brand.d.ts +12 -0
  10. package/dist/brand.d.ts.map +1 -0
  11. package/dist/brand.js +12 -0
  12. package/dist/brand.js.map +1 -0
  13. package/dist/hosts-manager.d.ts +14 -0
  14. package/dist/hosts-manager.d.ts.map +1 -0
  15. package/dist/hosts-manager.js +233 -0
  16. package/dist/hosts-manager.js.map +1 -0
  17. package/dist/hosts-manager.test.d.ts +2 -0
  18. package/dist/hosts-manager.test.d.ts.map +1 -0
  19. package/dist/hosts-manager.test.js +19 -0
  20. package/dist/hosts-manager.test.js.map +1 -0
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +296 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/install.d.ts +3 -0
  26. package/dist/install.d.ts.map +1 -0
  27. package/dist/install.js +15 -0
  28. package/dist/install.js.map +1 -0
  29. package/dist/installer/agent-instructions.d.ts +25 -0
  30. package/dist/installer/agent-instructions.d.ts.map +1 -0
  31. package/dist/installer/agent-instructions.js +36 -0
  32. package/dist/installer/agent-instructions.js.map +1 -0
  33. package/dist/installer/agents.d.ts +9 -0
  34. package/dist/installer/agents.d.ts.map +1 -0
  35. package/dist/installer/agents.js +223 -0
  36. package/dist/installer/agents.js.map +1 -0
  37. package/dist/installer/background-service.d.ts +11 -0
  38. package/dist/installer/background-service.d.ts.map +1 -0
  39. package/dist/installer/background-service.js +47 -0
  40. package/dist/installer/background-service.js.map +1 -0
  41. package/dist/installer/clipboard.d.ts +2 -0
  42. package/dist/installer/clipboard.d.ts.map +1 -0
  43. package/dist/installer/clipboard.js +28 -0
  44. package/dist/installer/clipboard.js.map +1 -0
  45. package/dist/installer/daemon-linux.d.ts +10 -0
  46. package/dist/installer/daemon-linux.d.ts.map +1 -0
  47. package/dist/installer/daemon-linux.js +66 -0
  48. package/dist/installer/daemon-linux.js.map +1 -0
  49. package/dist/installer/daemon-windows.d.ts +10 -0
  50. package/dist/installer/daemon-windows.d.ts.map +1 -0
  51. package/dist/installer/daemon-windows.js +88 -0
  52. package/dist/installer/daemon-windows.js.map +1 -0
  53. package/dist/installer/daemon.d.ts +10 -0
  54. package/dist/installer/daemon.d.ts.map +1 -0
  55. package/dist/installer/daemon.js +96 -0
  56. package/dist/installer/daemon.js.map +1 -0
  57. package/dist/installer/hosts-sudo.d.ts +6 -0
  58. package/dist/installer/hosts-sudo.d.ts.map +1 -0
  59. package/dist/installer/hosts-sudo.js +111 -0
  60. package/dist/installer/hosts-sudo.js.map +1 -0
  61. package/dist/installer/legacy-tunnels.d.ts +2 -0
  62. package/dist/installer/legacy-tunnels.d.ts.map +1 -0
  63. package/dist/installer/legacy-tunnels.js +27 -0
  64. package/dist/installer/legacy-tunnels.js.map +1 -0
  65. package/dist/installer/license.d.ts +9 -0
  66. package/dist/installer/license.d.ts.map +1 -0
  67. package/dist/installer/license.js +193 -0
  68. package/dist/installer/license.js.map +1 -0
  69. package/dist/installer/mcp-cleanup.d.ts +3 -0
  70. package/dist/installer/mcp-cleanup.d.ts.map +1 -0
  71. package/dist/installer/mcp-cleanup.js +28 -0
  72. package/dist/installer/mcp-cleanup.js.map +1 -0
  73. package/dist/installer/platform.d.ts +7 -0
  74. package/dist/installer/platform.d.ts.map +1 -0
  75. package/dist/installer/platform.js +76 -0
  76. package/dist/installer/platform.js.map +1 -0
  77. package/dist/installer/poke-daemon.d.ts +14 -0
  78. package/dist/installer/poke-daemon.d.ts.map +1 -0
  79. package/dist/installer/poke-daemon.js +161 -0
  80. package/dist/installer/poke-daemon.js.map +1 -0
  81. package/dist/installer/poke.d.ts +43 -0
  82. package/dist/installer/poke.d.ts.map +1 -0
  83. package/dist/installer/poke.js +154 -0
  84. package/dist/installer/poke.js.map +1 -0
  85. package/dist/installer/port.d.ts +6 -0
  86. package/dist/installer/port.d.ts.map +1 -0
  87. package/dist/installer/port.js +76 -0
  88. package/dist/installer/port.js.map +1 -0
  89. package/dist/installer/presets.d.ts +5 -0
  90. package/dist/installer/presets.d.ts.map +1 -0
  91. package/dist/installer/presets.js +22 -0
  92. package/dist/installer/presets.js.map +1 -0
  93. package/dist/installer/prompts.d.ts +8 -0
  94. package/dist/installer/prompts.d.ts.map +1 -0
  95. package/dist/installer/prompts.js +128 -0
  96. package/dist/installer/prompts.js.map +1 -0
  97. package/dist/installer/relay-credentials.d.ts +26 -0
  98. package/dist/installer/relay-credentials.d.ts.map +1 -0
  99. package/dist/installer/relay-credentials.js +75 -0
  100. package/dist/installer/relay-credentials.js.map +1 -0
  101. package/dist/installer/relay-provision.d.ts +26 -0
  102. package/dist/installer/relay-provision.d.ts.map +1 -0
  103. package/dist/installer/relay-provision.js +57 -0
  104. package/dist/installer/relay-provision.js.map +1 -0
  105. package/dist/installer/run.d.ts +2 -0
  106. package/dist/installer/run.d.ts.map +1 -0
  107. package/dist/installer/run.js +365 -0
  108. package/dist/installer/run.js.map +1 -0
  109. package/dist/installer/tty.d.ts +7 -0
  110. package/dist/installer/tty.d.ts.map +1 -0
  111. package/dist/installer/tty.js +37 -0
  112. package/dist/installer/tty.js.map +1 -0
  113. package/dist/installer/tui.d.ts +14 -0
  114. package/dist/installer/tui.d.ts.map +1 -0
  115. package/dist/installer/tui.js +150 -0
  116. package/dist/installer/tui.js.map +1 -0
  117. package/dist/installer/tunnel-daemon.d.ts +13 -0
  118. package/dist/installer/tunnel-daemon.d.ts.map +1 -0
  119. package/dist/installer/tunnel-daemon.js +129 -0
  120. package/dist/installer/tunnel-daemon.js.map +1 -0
  121. package/dist/installer/tunnel-provision.d.ts +14 -0
  122. package/dist/installer/tunnel-provision.d.ts.map +1 -0
  123. package/dist/installer/tunnel-provision.js +31 -0
  124. package/dist/installer/tunnel-provision.js.map +1 -0
  125. package/dist/installer/tunnel.d.ts +12 -0
  126. package/dist/installer/tunnel.d.ts.map +1 -0
  127. package/dist/installer/tunnel.js +91 -0
  128. package/dist/installer/tunnel.js.map +1 -0
  129. package/dist/installer/types.d.ts +59 -0
  130. package/dist/installer/types.d.ts.map +1 -0
  131. package/dist/installer/types.js +2 -0
  132. package/dist/installer/types.js.map +1 -0
  133. package/dist/license-check.d.ts +12 -0
  134. package/dist/license-check.d.ts.map +1 -0
  135. package/dist/license-check.js +59 -0
  136. package/dist/license-check.js.map +1 -0
  137. package/dist/license-guard.d.ts +14 -0
  138. package/dist/license-guard.d.ts.map +1 -0
  139. package/dist/license-guard.js +56 -0
  140. package/dist/license-guard.js.map +1 -0
  141. package/dist/license-guard.test.d.ts +2 -0
  142. package/dist/license-guard.test.d.ts.map +1 -0
  143. package/dist/license-guard.test.js +34 -0
  144. package/dist/license-guard.test.js.map +1 -0
  145. package/dist/relay-client.d.ts +32 -0
  146. package/dist/relay-client.d.ts.map +1 -0
  147. package/dist/relay-client.js +249 -0
  148. package/dist/relay-client.js.map +1 -0
  149. package/dist/relay-types.d.ts +30 -0
  150. package/dist/relay-types.d.ts.map +1 -0
  151. package/dist/relay-types.js +2 -0
  152. package/dist/relay-types.js.map +1 -0
  153. package/dist/relay-urls.d.ts +22 -0
  154. package/dist/relay-urls.d.ts.map +1 -0
  155. package/dist/relay-urls.js +57 -0
  156. package/dist/relay-urls.js.map +1 -0
  157. package/dist/server.d.ts +6 -0
  158. package/dist/server.d.ts.map +1 -0
  159. package/dist/server.js +164 -0
  160. package/dist/server.js.map +1 -0
  161. package/dist/state.d.ts +12 -0
  162. package/dist/state.d.ts.map +1 -0
  163. package/dist/state.js +91 -0
  164. package/dist/state.js.map +1 -0
  165. package/dist/types.d.ts +40 -0
  166. package/dist/types.d.ts.map +1 -0
  167. package/dist/types.js +41 -0
  168. package/dist/types.js.map +1 -0
  169. package/dist/types.test.d.ts +2 -0
  170. package/dist/types.test.d.ts.map +1 -0
  171. package/dist/types.test.js +13 -0
  172. package/dist/types.test.js.map +1 -0
  173. package/install.ps1 +41 -0
  174. package/install.sh +39 -0
  175. package/package.json +64 -0
package/README.md ADDED
@@ -0,0 +1,349 @@
1
+ # LockIn MCP
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that blocks distracting websites at the **system level** by managing entries in `/etc/hosts`. Use it with [Poke](https://poke.com), Claude Desktop, or ChatGPT custom MCP connectors to stay focused.
4
+
5
+ ## Features
6
+
7
+ | Tool | Description |
8
+ |------|-------------|
9
+ | `block_domains` | Block a list of domains (e.g. `youtube.com`, `x.com`, `tiktok.com`) |
10
+ | `unblock_domains` | Permanently unblock specific domains |
11
+ | `temporarily_unblock_domains` | Allow access for a limited time, then auto re-block |
12
+ | `enter_focus_mode` | Block common distraction sites in one action |
13
+ | `get_block_status` | Show what is blocked, temporarily allowed, and system readiness |
14
+
15
+ Blocking redirects domains to `127.0.0.1` and `::1`, including `www.` variants.
16
+
17
+ ## Requirements
18
+
19
+ - **macOS / Linux / Windows** (hosts-based blocking; macOS uses `dscacheutil` for DNS flush)
20
+ - **Node.js 18+**
21
+ - **Administrator privileges** to modify `/etc/hosts` (see below)
22
+
23
+ ## Web App & Pro Licensing
24
+
25
+ The `web/` directory contains the landing page, user accounts, Stripe checkout, and license API.
26
+
27
+ ### Quick start (local)
28
+
29
+ ```bash
30
+ # Terminal 1 — web app
31
+ cd web
32
+ cp ../.env.example .env.local # edit with your Stripe keys
33
+ npm install
34
+ npm run dev # http://localhost:3001
35
+
36
+ # Terminal 2 — CLI installer (points at local API by default)
37
+ export MDB_APP_URL=http://localhost:3001
38
+ export MDB_LICENSE_API_URL=http://localhost:3001/api/v1
39
+ npm run build
40
+ npm run install:local
41
+ # or from GitHub (no clone):
42
+ # npx -y lockin-mcp mdb-install
43
+ ```
44
+
45
+ ### Architecture
46
+
47
+ | Component | Purpose |
48
+ |-----------|---------|
49
+ | Landing page (`/`) | Features, how-it-works, pricing |
50
+ | Device auth (`/device`) | Browser login for CLI OAuth device flow |
51
+ | Dashboard (`/dashboard`) | License keys, Pro status, install instructions |
52
+ | API (`/api/v1/*`) | License verify, OAuth device flow, token status |
53
+ | Stripe webhook | Pro upgrade + license key generation |
54
+
55
+ ### CLI auth handshake
56
+
57
+ 1. Installer calls `POST /api/v1/oauth/device` → receives `user_code` + `device_code`
58
+ 2. Browser opens `/device?code=ABCD-1234`
59
+ 3. User signs in and approves (requires Pro)
60
+ 4. Installer polls `POST /api/v1/oauth/token` → receives `access_token`
61
+ 5. Daemon verifies token via `POST /api/v1/license/status` on startup and hourly
62
+
63
+ ### Environment variables
64
+
65
+ See [`.env.example`](.env.example) for all configuration options.
66
+
67
+ ---
68
+
69
+ ## One-Line Installer
70
+
71
+ The fastest way to set up blocking, tunneling, and Poke integration:
72
+
73
+ ### macOS / Linux (curl)
74
+
75
+ ```bash
76
+ curl -fsSL https://raw.githubusercontent.com/Kiog-Aser/mac-distraction-blocker-mcp/main/install.sh | bash
77
+ ```
78
+
79
+ ### Windows (PowerShell)
80
+
81
+ ```powershell
82
+ iwr -useb https://raw.githubusercontent.com/Kiog-Aser/mac-distraction-blocker-mcp/main/install.ps1 | iex
83
+ ```
84
+
85
+ ### npm
86
+
87
+ ```bash
88
+ npx -y lockin-mcp mdb-install
89
+ # or from a local clone:
90
+ npm run install:local
91
+ ```
92
+
93
+ ### What the installer does
94
+
95
+ 1. **Detects your platform** (macOS, Linux, or Windows)
96
+ 2. **Verifies your $9.99 Pro license** via browser login or license key
97
+ 3. **Prompts for distraction sites** — popular presets pre-selected (YouTube, X, TikTok, Instagram, Reddit) plus custom domains
98
+ 4. **Applies initial blocks** to `/etc/hosts`
99
+ 5. **Starts the MCP HTTP server** locally with an outbound relay connection (background LaunchAgent on macOS)
100
+ 6. **Provisions a stable relay URL** (`https://relay.lockinmcp.com/device/{id}/mcp`) so Poke, Claude, and ChatGPT can reach your Mac
101
+ 7. **Connects your AI agent** — Poke, Claude Desktop, or ChatGPT (URL + API key from manifest)
102
+
103
+ ### Installer flags (non-interactive)
104
+
105
+ ```bash
106
+ curl -fsSL .../install.sh | bash -s -- --yes --browser-login --poke-recipe
107
+ npx -y lockin-mcp mdb-install --license-key mdb_pro_xxx --sites all --poke-recipe
108
+ ```
109
+
110
+ | Flag | Description |
111
+ |------|-------------|
112
+ | `--yes` | Non-interactive with defaults |
113
+ | `--license-key <key>` | Pro license key |
114
+ | `--browser-login` | Verify license via browser device flow (default) |
115
+ | `--skip-tunnel` | Local MCP only (no relay URL) |
116
+ | `--sites all` | Block all presets |
117
+ | `--custom-domains a.com,b.com` | Extra domains |
118
+ | `--poke-recipe` | Generate distraction-coach recipe URL |
119
+ | `--mcp-auth-token <token>` | Custom bearer token for tunneled MCP |
120
+
121
+ Setup details are saved to `~/.mac-distraction-blocker/setup-manifest.json`.
122
+
123
+ ### Pro licensing ($9.99 lifetime — 50% off $19.99)
124
+
125
+ - **Purchase**: Sign up (email or Google/GitHub), verify email, pay via Stripe
126
+ - **License key**: Available on dashboard after purchase; pass `--license-key` to installer
127
+ - **Browser login**: Default installer flow — OAuth device authorization in your browser
128
+ - **Auth**: Google and GitHub OAuth supported; email/password requires verification before checkout
129
+
130
+ ---
131
+
132
+ ## Quick Start (manual)
133
+
134
+ ```bash
135
+ git clone <this-repo>
136
+ cd mac-distraction-blocker-mcp
137
+ npm install
138
+ npm run build
139
+ ```
140
+
141
+ ### Run locally (stdio — advanced)
142
+
143
+ For MCP clients that spawn a local subprocess:
144
+
145
+ ```bash
146
+ node dist/index.js --stdio
147
+ ```
148
+
149
+ ## MCP Client Configuration (after installer)
150
+
151
+ Your MCP URL and API key are in `~/.mac-distraction-blocker/setup-manifest.json`:
152
+
153
+ - **MCP URL:** from manifest — e.g. `https://relay.lockinmcp.com/device/abc123/mcp` (Poke, Claude, ChatGPT)
154
+ - **API key:** `mdb_…`
155
+
156
+ ### Poke
157
+
158
+ ```bash
159
+ npx poke@latest login
160
+ npx poke@latest mcp add 'https://YOUR_RELAY_HOST/device/YOUR_ID/mcp' -n 'LockIn MCP' --api-key 'mdb_...'
161
+ ```
162
+
163
+ Or set URL + API key in [poke.com/integrations](https://poke.com/integrations).
164
+
165
+ ### Claude Desktop
166
+
167
+ `~/Library/Application Support/Claude/claude_desktop_config.json`:
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "lockin-mcp": {
173
+ "url": "https://YOUR_TUNNEL_HOST/mcp",
174
+ "headers": {
175
+ "Authorization": "Bearer mdb_..."
176
+ }
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### ChatGPT
183
+
184
+ 1. Open ChatGPT → Settings → Connectors
185
+ 2. Add MCP server URL from your manifest (e.g. `https://….trycloudflare.com/mcp`)
186
+ 3. Paste API key when prompted: `mdb_...`
187
+
188
+ ### Relay deployment (operators)
189
+
190
+ The `relay/` package is a Cloudflare Worker + Durable Object that bridges remote MCP HTTP requests to each Mac daemon over an outbound WebSocket.
191
+
192
+ ```bash
193
+ cd relay
194
+ npm install
195
+ npx wrangler secret put REGISTRATION_SECRET
196
+ npx wrangler secret put MDB_JWT_SECRET # optional, same as web app
197
+ npm run deploy
198
+ ```
199
+
200
+ Set `MDB_RELAY_URL`, `MDB_RELAY_PUBLIC_URL`, and `REGISTRATION_SECRET` in `web/.env.local` and sync to Convex via `./scripts/sync-convex-env.sh`.
201
+
202
+ ## Privileges & `/etc/hosts`
203
+
204
+ The server writes marked entries between:
205
+
206
+ ```
207
+ # mac-distraction-blocker-mcp BEGIN
208
+ ...
209
+ # mac-distraction-blocker-mcp END
210
+ ```
211
+
212
+ Only those lines are touched; the rest of `/etc/hosts` is preserved.
213
+
214
+ ### Option A: Run the server with `sudo` (simplest)
215
+
216
+ ```bash
217
+ sudo node dist/index.js --http --port 3000
218
+ ```
219
+
220
+ For stdio MCP clients, configure the client to launch with `sudo` (see client config examples below).
221
+
222
+ ### Option B: Passwordless `sudo` for hosts updates (recommended)
223
+
224
+ The server falls back to `sudo cp` when it cannot write `/etc/hosts` directly. To avoid prompts during tool calls, allow your user to copy a temp file into hosts without a password:
225
+
226
+ ```bash
227
+ sudo visudo -f /etc/sudoers.d/mac-distraction-blocker
228
+ ```
229
+
230
+ Add (replace `YOUR_USER` with your macOS username):
231
+
232
+ ```
233
+ YOUR_USER ALL=(ALL) NOPASSWD: /bin/cp /tmp/mac-distraction-blocker-hosts-* /etc/hosts
234
+ YOUR_USER ALL=(ALL) NOPASSWD: /usr/sbin/dscacheutil -flushcache
235
+ YOUR_USER ALL=(ALL) NOPASSWD: /usr/bin/killall -HUP mDNSResponder
236
+ ```
237
+
238
+ ### Verify permissions
239
+
240
+ Call `get_block_status` — `hostsFileWritable: true` means the server can update blocking without extra setup.
241
+
242
+ ## MCP Client Configuration
243
+
244
+ See **MCP Client Configuration (after installer)** above.
245
+
246
+ ### Legacy: local stdio (Claude Desktop)
247
+
248
+ `~/Library/Application Support/Claude/claude_desktop_config.json`:
249
+
250
+ ```json
251
+ {
252
+ "mcpServers": {
253
+ "lockin-mcp": {
254
+ "command": "node",
255
+ "args": ["/absolute/path/to/mac-distraction-blocker-mcp/dist/index.js", "--stdio"]
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ ## Hooking up to [poke.com/kitchen](https://poke.com/kitchen)
262
+
263
+ The installer tunnels your local MCP over HTTPS so Poke can connect remotely — no extra terminal tab.
264
+
265
+ ```bash
266
+ npx poke@latest login
267
+ npx poke@latest mcp add 'https://YOUR_RELAY_HOST/device/YOUR_ID/mcp' -n 'LockIn MCP' --api-key 'mdb_...'
268
+ ```
269
+
270
+ The installer runs this automatically when you choose Poke. Credentials are in `setup-manifest.json`.
271
+
272
+ ### Recipe / Kitchen (recommended)
273
+
274
+ **Install the official LockIn recipe** for the best Poke experience:
275
+
276
+ https://poke.com/r/KQ7myvC_Xpo
277
+
278
+ The recipe connects your MCP tools and coaches you through focus sessions. You can also build custom recipes in [poke.com/kitchen](https://poke.com/kitchen) using `block_domains`, `get_block_status`, etc.
279
+
280
+ ## Tool Examples
281
+
282
+ ### Block sites
283
+
284
+ ```json
285
+ {
286
+ "domains": ["youtube.com", "x.com", "tiktok.com", "reddit.com"]
287
+ }
288
+ ```
289
+
290
+ ### Unblock one site
291
+
292
+ ```json
293
+ {
294
+ "domains": ["youtube.com"]
295
+ }
296
+ ```
297
+
298
+ ### 15-minute break
299
+
300
+ ```json
301
+ {
302
+ "domains": ["youtube.com"],
303
+ "duration_seconds": 900
304
+ }
305
+ ```
306
+
307
+ ### Check status
308
+
309
+ No arguments. Returns blocked list, temporary allowances with expiry timestamps, and whether hosts is writable.
310
+
311
+ ## How it works
312
+
313
+ ```mermaid
314
+ flowchart LR
315
+ A[MCP Client / Poke] --> R[Relay Worker]
316
+ R --> B[MCP Server on Mac]
317
+ B --> C[State JSON\n~/.mac-distraction-blocker/]
318
+ B --> D[/etc/hosts]
319
+ B --> E[DNS cache flush]
320
+ C --> B
321
+ ```
322
+
323
+ 1. **State** — Block list and temporary unblocks live in `~/.mac-distraction-blocker/state.json`
324
+ 2. **Hosts sync** — Active blocks are written to `/etc/hosts` with a managed marker
325
+ 3. **Expiry watcher** — A background timer re-applies blocks when temporary unblocks expire (server process must be running)
326
+ 4. **DNS flush** — Best-effort `dscacheutil` / `mDNSResponder` refresh after changes
327
+
328
+ ## Development
329
+
330
+ ```bash
331
+ npm install
332
+ npm run dev # watch TypeScript
333
+ npm run build
334
+ npm run lint
335
+ ```
336
+
337
+ ## Troubleshooting
338
+
339
+ | Issue | Fix |
340
+ |-------|-----|
341
+ | `EACCES` on block/unblock | Run with `sudo` or configure passwordless sudo (see above) |
342
+ | Site still loads | Hard-refresh browser; DNS cache may take a moment. Try private window. |
343
+ | Temp unblock didn't re-block | Keep the MCP server process running (expiry watcher runs in-process) |
344
+ | Poke can't connect | Check manifest relay URL + `/health` returns `lockin-mcp`; verify relay device is online |
345
+ | Wrong platform | Designed for macOS; Linux may work with `/etc/hosts` but DNS flush differs |
346
+
347
+ ## License
348
+
349
+ MIT
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="#111827">
2
+ <rect width="256" height="256" rx="48" fill="#111827"/>
3
+ <path fill="#ffffff" d="M 144 224 L 43.598 224 L 144 123.598 Z M 224 183.5 L 176 224 L 176 48 L 16 48 L 56 8 L 224 8 Z M 16 180.402 L 16 96 L 100.402 96 Z"/>
4
+ </svg>
@@ -0,0 +1,23 @@
1
+ import { type BlockStatusResult, type BlockerState, type DomainStatus } from "./types.js";
2
+ export declare function startExpiryWatcher(intervalMs?: number): void;
3
+ export declare function stopExpiryWatcher(): void;
4
+ export declare function blockDomains(domains: string[]): Promise<{
5
+ blocked: string[];
6
+ message: string;
7
+ }>;
8
+ export declare function unblockDomains(domains: string[]): Promise<{
9
+ unblocked: string[];
10
+ message: string;
11
+ }>;
12
+ export declare function temporarilyUnblockDomains(domains: string[], durationSeconds: number): Promise<{
13
+ temporarilyUnblocked: Array<{
14
+ domain: string;
15
+ expiresAt: number;
16
+ }>;
17
+ message: string;
18
+ }>;
19
+ export declare function getBlockStatus(): Promise<BlockStatusResult>;
20
+ export declare function listAllKnownDomains(): Promise<string[]>;
21
+ export declare function describeDomain(domain: string, state: BlockerState): DomainStatus;
22
+ export declare function isCurrentlyBlocked(domain: string): Promise<boolean>;
23
+ //# sourceMappingURL=blocker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocker.d.ts","sourceRoot":"","sources":["../src/blocker.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,YAAY,EAGlB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,kBAAkB,CAChC,UAAU,SAAS,GAClB,IAAI,CAUN;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC;AAsCD,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CA4CD;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC/D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CA8BD;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EAAE,EACjB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC;IACT,oBAAoB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CA4CD;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAiCjE;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAG7D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CAgBhF;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGzE"}
@@ -0,0 +1,184 @@
1
+ import { canWriteHostsFile, syncHostsFile, } from "./hosts-manager.js";
2
+ import { dedupeDomains, getDomainsToBlockInHosts, getActiveTemporaryUnblock, isDomainBlocked, loadState, pruneExpiredUnblocks, saveState, appendAuditEntry, } from "./state.js";
3
+ import { isValidDomain, normalizeDomain, } from "./types.js";
4
+ let expiryTimer;
5
+ let lastSyncedAt = null;
6
+ export function startExpiryWatcher(intervalMs = 10_000) {
7
+ if (expiryTimer) {
8
+ return;
9
+ }
10
+ expiryTimer = setInterval(() => {
11
+ void refreshExpiredUnblocks();
12
+ }, intervalMs);
13
+ expiryTimer.unref();
14
+ }
15
+ export function stopExpiryWatcher() {
16
+ if (expiryTimer) {
17
+ clearInterval(expiryTimer);
18
+ expiryTimer = undefined;
19
+ }
20
+ }
21
+ async function refreshExpiredUnblocks() {
22
+ const state = await loadState();
23
+ const pruned = pruneExpiredUnblocks(state.temporaryUnblocks);
24
+ if (pruned.length === state.temporaryUnblocks.length) {
25
+ return false;
26
+ }
27
+ const nextState = {
28
+ ...state,
29
+ temporaryUnblocks: pruned,
30
+ };
31
+ await saveState(nextState);
32
+ await applyHostsFromState(nextState);
33
+ return true;
34
+ }
35
+ async function applyHostsFromState(state) {
36
+ const domains = getDomainsToBlockInHosts(state);
37
+ const sync = await syncHostsFile(domains);
38
+ if (sync.method !== "unchanged") {
39
+ lastSyncedAt = Date.now();
40
+ }
41
+ return sync.method;
42
+ }
43
+ function validateDomains(domains) {
44
+ const normalized = dedupeDomains(domains);
45
+ const invalid = normalized.filter((domain) => !isValidDomain(domain));
46
+ if (invalid.length > 0) {
47
+ throw new Error(`Invalid domain(s): ${invalid.join(", ")}`);
48
+ }
49
+ return normalized;
50
+ }
51
+ export async function blockDomains(domains) {
52
+ const validated = validateDomains(domains);
53
+ const state = await loadState();
54
+ const blockedSet = new Set(state.blockedDomains);
55
+ const newDomains = validated.filter((domain) => !blockedSet.has(domain));
56
+ for (const domain of validated) {
57
+ blockedSet.add(domain);
58
+ }
59
+ const nextState = appendAuditEntry({
60
+ blockedDomains: [...blockedSet].sort(),
61
+ temporaryUnblocks: state.temporaryUnblocks.filter((entry) => !validated.includes(entry.domain)),
62
+ updatedAt: Date.now(),
63
+ }, "block_domains", validated);
64
+ const stateUnchanged = newDomains.length === 0 &&
65
+ nextState.blockedDomains.length === state.blockedDomains.length &&
66
+ nextState.temporaryUnblocks.length === state.temporaryUnblocks.length;
67
+ if (!stateUnchanged) {
68
+ await saveState(nextState);
69
+ }
70
+ const method = await applyHostsFromState(nextState);
71
+ if (method === "unchanged" && newDomains.length === 0) {
72
+ return {
73
+ blocked: validated,
74
+ message: `${validated.length} domain(s) already blocked.`,
75
+ };
76
+ }
77
+ return {
78
+ blocked: validated,
79
+ message: `Blocked ${validated.length} domain(s) via /etc/hosts (${method}).`,
80
+ };
81
+ }
82
+ export async function unblockDomains(domains) {
83
+ const validated = validateDomains(domains);
84
+ const state = await loadState();
85
+ const removeSet = new Set(validated);
86
+ const unblocked = state.blockedDomains.filter((domain) => removeSet.has(domain));
87
+ const nextState = appendAuditEntry({
88
+ blockedDomains: state.blockedDomains.filter((domain) => !removeSet.has(domain)),
89
+ temporaryUnblocks: state.temporaryUnblocks.filter((entry) => !removeSet.has(entry.domain)),
90
+ updatedAt: Date.now(),
91
+ }, "unblock_domains", unblocked);
92
+ await saveState(nextState);
93
+ const method = await applyHostsFromState(nextState);
94
+ return {
95
+ unblocked,
96
+ message: `Unblocked ${unblocked.length} domain(s) and updated /etc/hosts (${method}).`,
97
+ };
98
+ }
99
+ export async function temporarilyUnblockDomains(domains, durationSeconds) {
100
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
101
+ throw new Error("duration_seconds must be a positive number");
102
+ }
103
+ const validated = validateDomains(domains);
104
+ const state = await loadState();
105
+ const expiresAt = Date.now() + durationSeconds * 1000;
106
+ const temporaryUnblocks = [...state.temporaryUnblocks];
107
+ for (const domain of validated) {
108
+ if (!state.blockedDomains.includes(domain)) {
109
+ throw new Error(`Cannot temporarily unblock "${domain}" because it is not in the blocked list`);
110
+ }
111
+ const existingIndex = temporaryUnblocks.findIndex((entry) => entry.domain === domain);
112
+ const entry = { domain, expiresAt };
113
+ if (existingIndex >= 0) {
114
+ temporaryUnblocks[existingIndex] = entry;
115
+ }
116
+ else {
117
+ temporaryUnblocks.push(entry);
118
+ }
119
+ }
120
+ const nextState = appendAuditEntry({
121
+ ...state,
122
+ temporaryUnblocks,
123
+ }, "temporarily_unblock_domains", validated);
124
+ await saveState(nextState);
125
+ const method = await applyHostsFromState(nextState);
126
+ return {
127
+ temporarilyUnblocked: validated.map((domain) => ({ domain, expiresAt })),
128
+ message: `Temporarily unblocked ${validated.length} domain(s) for ${durationSeconds}s (${method}).`,
129
+ };
130
+ }
131
+ export async function getBlockStatus() {
132
+ await refreshExpiredUnblocks();
133
+ const state = await loadState();
134
+ const now = Date.now();
135
+ const hostsFileWritable = await canWriteHostsFile();
136
+ const blocked = [];
137
+ const temporarilyUnblocked = [];
138
+ for (const domain of state.blockedDomains) {
139
+ const temp = getActiveTemporaryUnblock(domain, state.temporaryUnblocks, now);
140
+ if (temp) {
141
+ temporarilyUnblocked.push({
142
+ domain,
143
+ status: "temporarily_unblocked",
144
+ expiresAt: temp.expiresAt,
145
+ });
146
+ continue;
147
+ }
148
+ blocked.push({
149
+ domain,
150
+ status: "blocked",
151
+ });
152
+ }
153
+ return {
154
+ blocked,
155
+ temporarilyUnblocked,
156
+ notBlocked: [],
157
+ hostsFileWritable,
158
+ lastSyncedAt,
159
+ };
160
+ }
161
+ export async function listAllKnownDomains() {
162
+ const state = await loadState();
163
+ return state.blockedDomains;
164
+ }
165
+ export function describeDomain(domain, state) {
166
+ const normalized = normalizeDomain(domain);
167
+ if (!state.blockedDomains.includes(normalized)) {
168
+ return { domain: normalized, status: "not_blocked" };
169
+ }
170
+ const temp = getActiveTemporaryUnblock(normalized, state.temporaryUnblocks);
171
+ if (temp) {
172
+ return {
173
+ domain: normalized,
174
+ status: "temporarily_unblocked",
175
+ expiresAt: temp.expiresAt,
176
+ };
177
+ }
178
+ return { domain: normalized, status: "blocked" };
179
+ }
180
+ export async function isCurrentlyBlocked(domain) {
181
+ const state = await loadState();
182
+ return isDomainBlocked(domain, state);
183
+ }
184
+ //# sourceMappingURL=blocker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocker.js","sourceRoot":"","sources":["../src/blocker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const APP_NAME = "LockIn MCP";
2
+ export declare const APP_SLUG = "lockin-mcp";
3
+ export declare const APP_TAGLINE = "Block distractions. Lock in focus.";
4
+ export declare const APP_DOMAIN = "lockinmcp.com";
5
+ export declare const RELAY_HOST = "relay.lockinmcp.com";
6
+ export declare const RELAY_PUBLIC_ORIGIN = "https://relay.lockinmcp.com";
7
+ export declare const LICENSE_PRICE_ORIGINAL = "$19.99";
8
+ export declare const LICENSE_PRICE = "$9.99";
9
+ export declare const LICENSE_DISCOUNT_LABEL = "50% off \u2014 forever";
10
+ export declare const POKE_LOCKIN_RECIPE_URL = "https://poke.com/r/KQ7myvC_Xpo";
11
+ export declare const ICON_URL = "https://lockinmcp.com/icon.png";
12
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,eAAe,CAAC;AACrC,eAAO,MAAM,QAAQ,eAAe,CAAC;AACrC,eAAO,MAAM,WAAW,uCAAuC,CAAC;AAChE,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAC1C,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAChD,eAAO,MAAM,mBAAmB,gCAA0B,CAAC;AAC3D,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAC/C,eAAO,MAAM,aAAa,UAAU,CAAC;AACrC,eAAO,MAAM,sBAAsB,2BAAsB,CAAC;AAC1D,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AACvE,eAAO,MAAM,QAAQ,mCAAmC,CAAC"}
package/dist/brand.js ADDED
@@ -0,0 +1,12 @@
1
+ export const APP_NAME = "LockIn MCP";
2
+ export const APP_SLUG = "lockin-mcp";
3
+ export const APP_TAGLINE = "Block distractions. Lock in focus.";
4
+ export const APP_DOMAIN = "lockinmcp.com";
5
+ export const RELAY_HOST = `relay.${APP_DOMAIN}`;
6
+ export const RELAY_PUBLIC_ORIGIN = `https://${RELAY_HOST}`;
7
+ export const LICENSE_PRICE_ORIGINAL = "$19.99";
8
+ export const LICENSE_PRICE = "$9.99";
9
+ export const LICENSE_DISCOUNT_LABEL = "50% off — forever";
10
+ export const POKE_LOCKIN_RECIPE_URL = "https://poke.com/r/KQ7myvC_Xpo";
11
+ export const ICON_URL = `https://${APP_DOMAIN}/icon.png`;
12
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,UAAU,EAAE,CAAC;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;AAC/C,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAgC,CAAC;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,UAAU,WAAW,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare function canWriteHostsFile(): Promise<boolean>;
2
+ export declare function readHostsFile(): Promise<string>;
3
+ export declare function stripManagedEntries(hostsContent: string): string;
4
+ export declare function buildManagedSection(domains: string[]): string;
5
+ export declare function composeHostsFile(existingContent: string, blockedDomains: string[]): string;
6
+ export declare function writeHostsFile(content: string): Promise<"direct" | "sudo">;
7
+ export declare function flushDnsCache(): Promise<void>;
8
+ export declare function syncHostsFile(blockedDomains: string[]): Promise<{
9
+ method: "direct" | "sudo" | "unchanged";
10
+ blockedCount: number;
11
+ }>;
12
+ /** Reconcile hosts file with saved state on daemon startup. */
13
+ export declare function reconcileHostsOnStartup(): Promise<void>;
14
+ //# sourceMappingURL=hosts-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosts-manager.d.ts","sourceRoot":"","sources":["../src/hosts-manager.ts"],"names":[],"mappings":"AA4DA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS1D;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAErD;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA+BhE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAyB7D;AAED,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,CAOR;AA6BD,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,CAehF;AA2CD,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnD;AAED,wBAAsB,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACrE,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAiBD;AAED,+DAA+D;AAC/D,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7D"}