palmier 0.9.6 → 0.9.7

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 (250) hide show
  1. package/README.md +28 -13
  2. package/dist/agents/agent.d.ts +0 -1
  3. package/dist/agents/agent.js +0 -1
  4. package/dist/agents/aider.d.ts +0 -1
  5. package/dist/agents/aider.js +0 -1
  6. package/dist/agents/claude.d.ts +0 -1
  7. package/dist/agents/claude.js +0 -1
  8. package/dist/agents/cline.d.ts +0 -1
  9. package/dist/agents/cline.js +0 -1
  10. package/dist/agents/codex.d.ts +0 -1
  11. package/dist/agents/codex.js +0 -1
  12. package/dist/agents/copilot.d.ts +0 -1
  13. package/dist/agents/copilot.js +0 -1
  14. package/dist/agents/cursor.d.ts +0 -1
  15. package/dist/agents/cursor.js +0 -1
  16. package/dist/agents/deepagents.d.ts +0 -1
  17. package/dist/agents/deepagents.js +0 -1
  18. package/dist/agents/droid.d.ts +0 -1
  19. package/dist/agents/droid.js +0 -1
  20. package/dist/agents/gemini.d.ts +0 -1
  21. package/dist/agents/gemini.js +0 -1
  22. package/dist/agents/goose.d.ts +0 -1
  23. package/dist/agents/goose.js +0 -1
  24. package/dist/agents/hermes.d.ts +0 -1
  25. package/dist/agents/hermes.js +0 -1
  26. package/dist/agents/kimi.d.ts +0 -1
  27. package/dist/agents/kimi.js +0 -1
  28. package/dist/agents/kiro.d.ts +0 -1
  29. package/dist/agents/kiro.js +0 -1
  30. package/dist/agents/openclaw.d.ts +0 -1
  31. package/dist/agents/openclaw.js +0 -1
  32. package/dist/agents/opencode.d.ts +0 -1
  33. package/dist/agents/opencode.js +0 -1
  34. package/dist/agents/qoder.d.ts +0 -1
  35. package/dist/agents/qoder.js +0 -1
  36. package/dist/agents/qwen.d.ts +0 -1
  37. package/dist/agents/qwen.js +0 -1
  38. package/dist/agents/shared-prompt.d.ts +0 -1
  39. package/dist/agents/shared-prompt.js +0 -1
  40. package/dist/client-store.d.ts +0 -1
  41. package/dist/client-store.js +0 -1
  42. package/dist/commands/clients.d.ts +0 -1
  43. package/dist/commands/clients.js +0 -1
  44. package/dist/commands/info.d.ts +0 -1
  45. package/dist/commands/info.js +0 -1
  46. package/dist/commands/init.d.ts +0 -1
  47. package/dist/commands/init.js +1 -2
  48. package/dist/commands/pair.d.ts +0 -1
  49. package/dist/commands/pair.js +0 -1
  50. package/dist/commands/restart.d.ts +0 -1
  51. package/dist/commands/restart.js +0 -1
  52. package/dist/commands/run.d.ts +0 -1
  53. package/dist/commands/run.js +0 -1
  54. package/dist/commands/serve.d.ts +0 -1
  55. package/dist/commands/serve.js +0 -1
  56. package/dist/commands/uninstall.d.ts +0 -1
  57. package/dist/commands/uninstall.js +0 -1
  58. package/dist/config.d.ts +0 -1
  59. package/dist/config.js +0 -1
  60. package/dist/event-queues.d.ts +0 -1
  61. package/dist/event-queues.js +0 -1
  62. package/dist/events.d.ts +0 -1
  63. package/dist/events.js +0 -1
  64. package/dist/index.d.ts +0 -1
  65. package/dist/index.js +0 -1
  66. package/dist/linked-device.d.ts +0 -1
  67. package/dist/linked-device.js +0 -1
  68. package/dist/mcp-handler.d.ts +0 -1
  69. package/dist/mcp-handler.js +0 -1
  70. package/dist/mcp-tools.d.ts +0 -1
  71. package/dist/mcp-tools.js +0 -1
  72. package/dist/nats-client.d.ts +0 -1
  73. package/dist/nats-client.js +0 -1
  74. package/dist/network.d.ts +0 -1
  75. package/dist/network.js +0 -1
  76. package/dist/notification-store.d.ts +0 -1
  77. package/dist/notification-store.js +0 -1
  78. package/dist/pending-requests.d.ts +0 -1
  79. package/dist/pending-requests.js +0 -1
  80. package/dist/platform/index.d.ts +0 -1
  81. package/dist/platform/index.js +0 -1
  82. package/dist/platform/linux.d.ts +0 -1
  83. package/dist/platform/linux.js +0 -1
  84. package/dist/platform/macos.d.ts +0 -1
  85. package/dist/platform/macos.js +0 -1
  86. package/dist/platform/platform.d.ts +0 -1
  87. package/dist/platform/platform.js +0 -1
  88. package/dist/platform/windows.d.ts +0 -1
  89. package/dist/platform/windows.js +0 -1
  90. package/dist/pwa/assets/{index-MLEFUP3r.js → index-DWvRAUiy.js} +31 -31
  91. package/dist/pwa/assets/{web-B1sKCc7e.js → web-C4iZbqTC.js} +1 -1
  92. package/dist/pwa/assets/{web-ETD-8ZHd.js → web-CBFqJGX6.js} +1 -1
  93. package/dist/pwa/assets/{web-B4xEa6WO.js → web-DL4uXOpS.js} +1 -1
  94. package/dist/pwa/index.html +2 -2
  95. package/dist/pwa/service-worker.js +1 -1
  96. package/dist/rpc-handler.d.ts +0 -1
  97. package/dist/rpc-handler.js +0 -1
  98. package/dist/sms-store.d.ts +0 -1
  99. package/dist/sms-store.js +0 -1
  100. package/dist/spawn-command.d.ts +0 -1
  101. package/dist/spawn-command.js +0 -1
  102. package/dist/task.d.ts +0 -1
  103. package/dist/task.js +0 -1
  104. package/dist/transports/http-transport.d.ts +0 -1
  105. package/dist/transports/http-transport.js +0 -1
  106. package/dist/transports/nats-transport.d.ts +0 -1
  107. package/dist/transports/nats-transport.js +0 -1
  108. package/dist/types.d.ts +0 -1
  109. package/dist/types.js +0 -1
  110. package/dist/update-checker.d.ts +0 -1
  111. package/dist/update-checker.js +0 -1
  112. package/package.json +5 -1
  113. package/.github/workflows/ci.yml +0 -16
  114. package/.github/workflows/publish.yml +0 -37
  115. package/CLAUDE.md +0 -22
  116. package/palmier-server/.github/workflows/ci.yml +0 -21
  117. package/palmier-server/.github/workflows/deploy.yml +0 -38
  118. package/palmier-server/CLAUDE.md +0 -17
  119. package/palmier-server/PRODUCTION.md +0 -358
  120. package/palmier-server/README.md +0 -231
  121. package/palmier-server/nats.conf +0 -19
  122. package/palmier-server/package.json +0 -15
  123. package/palmier-server/pnpm-lock.yaml +0 -7639
  124. package/palmier-server/pnpm-workspace.yaml +0 -3
  125. package/palmier-server/pwa/index.html +0 -16
  126. package/palmier-server/pwa/logo/logo_20260421.png +0 -0
  127. package/palmier-server/pwa/package.json +0 -34
  128. package/palmier-server/pwa/public/apple-touch-icon.png +0 -0
  129. package/palmier-server/pwa/public/favicon.ico +0 -0
  130. package/palmier-server/pwa/public/pwa-192x192.png +0 -0
  131. package/palmier-server/pwa/public/pwa-512x512.png +0 -0
  132. package/palmier-server/pwa/src/App.css +0 -3012
  133. package/palmier-server/pwa/src/App.tsx +0 -59
  134. package/palmier-server/pwa/src/agentLabels.ts +0 -11
  135. package/palmier-server/pwa/src/api.ts +0 -67
  136. package/palmier-server/pwa/src/components/CapabilityToggles.tsx +0 -170
  137. package/palmier-server/pwa/src/components/ConnectionStatusIcon.tsx +0 -113
  138. package/palmier-server/pwa/src/components/HostMenu.tsx +0 -429
  139. package/palmier-server/pwa/src/components/PermissionsDialog.tsx +0 -34
  140. package/palmier-server/pwa/src/components/PullToRefreshIndicator.tsx +0 -46
  141. package/palmier-server/pwa/src/components/RunDetailView.tsx +0 -343
  142. package/palmier-server/pwa/src/components/SessionComposer.tsx +0 -157
  143. package/palmier-server/pwa/src/components/SessionsView.tsx +0 -326
  144. package/palmier-server/pwa/src/components/SwipeToDeleteRow.tsx +0 -170
  145. package/palmier-server/pwa/src/components/TabBar.tsx +0 -40
  146. package/palmier-server/pwa/src/components/TaskCard.tsx +0 -255
  147. package/palmier-server/pwa/src/components/TaskForm.tsx +0 -766
  148. package/palmier-server/pwa/src/components/TasksView.tsx +0 -179
  149. package/palmier-server/pwa/src/constants.ts +0 -2
  150. package/palmier-server/pwa/src/contexts/HostConnectionContext.tsx +0 -432
  151. package/palmier-server/pwa/src/contexts/HostStoreContext.tsx +0 -124
  152. package/palmier-server/pwa/src/draftGuard.ts +0 -24
  153. package/palmier-server/pwa/src/formatTime.ts +0 -44
  154. package/palmier-server/pwa/src/hooks/useBackClose.ts +0 -75
  155. package/palmier-server/pwa/src/hooks/useMediaQuery.ts +0 -17
  156. package/palmier-server/pwa/src/hooks/usePullToRefresh.ts +0 -102
  157. package/palmier-server/pwa/src/hooks/usePushSubscription.ts +0 -77
  158. package/palmier-server/pwa/src/main.tsx +0 -14
  159. package/palmier-server/pwa/src/native/Device.ts +0 -49
  160. package/palmier-server/pwa/src/pages/Dashboard.tsx +0 -542
  161. package/palmier-server/pwa/src/pages/PairHost.tsx +0 -232
  162. package/palmier-server/pwa/src/pages/PairSetup.tsx +0 -134
  163. package/palmier-server/pwa/src/service-worker.ts +0 -142
  164. package/palmier-server/pwa/src/types.ts +0 -75
  165. package/palmier-server/pwa/src/vite-env.d.ts +0 -11
  166. package/palmier-server/pwa/tsconfig.json +0 -21
  167. package/palmier-server/pwa/tsconfig.node.json +0 -19
  168. package/palmier-server/pwa/vite.config.ts +0 -47
  169. package/palmier-server/server/.env.example +0 -20
  170. package/palmier-server/server/package.json +0 -36
  171. package/palmier-server/server/src/db.ts +0 -44
  172. package/palmier-server/server/src/fcm.ts +0 -74
  173. package/palmier-server/server/src/index.ts +0 -688
  174. package/palmier-server/server/src/nats-jwt.ts +0 -299
  175. package/palmier-server/server/src/nats-setup.ts +0 -48
  176. package/palmier-server/server/src/nats.ts +0 -33
  177. package/palmier-server/server/src/notify.ts +0 -34
  178. package/palmier-server/server/src/push.ts +0 -68
  179. package/palmier-server/server/src/routes/device.ts +0 -224
  180. package/palmier-server/server/src/routes/fcm.ts +0 -64
  181. package/palmier-server/server/src/routes/hosts.ts +0 -56
  182. package/palmier-server/server/src/routes/push.ts +0 -101
  183. package/palmier-server/server/tsconfig.json +0 -20
  184. package/palmier-server/spec.md +0 -533
  185. package/src/agents/agent-instructions.md +0 -28
  186. package/src/agents/agent.ts +0 -114
  187. package/src/agents/aider.ts +0 -35
  188. package/src/agents/claude.ts +0 -39
  189. package/src/agents/cline.ts +0 -35
  190. package/src/agents/codex.ts +0 -40
  191. package/src/agents/copilot.ts +0 -37
  192. package/src/agents/cursor.ts +0 -36
  193. package/src/agents/deepagents.ts +0 -36
  194. package/src/agents/droid.ts +0 -35
  195. package/src/agents/gemini.ts +0 -43
  196. package/src/agents/goose.ts +0 -33
  197. package/src/agents/hermes.ts +0 -36
  198. package/src/agents/kimi.ts +0 -35
  199. package/src/agents/kiro.ts +0 -36
  200. package/src/agents/openclaw.ts +0 -29
  201. package/src/agents/opencode.ts +0 -36
  202. package/src/agents/qoder.ts +0 -36
  203. package/src/agents/qwen.ts +0 -32
  204. package/src/agents/shared-prompt.ts +0 -30
  205. package/src/client-store.ts +0 -68
  206. package/src/commands/clients.ts +0 -29
  207. package/src/commands/info.ts +0 -29
  208. package/src/commands/init.ts +0 -165
  209. package/src/commands/pair.ts +0 -137
  210. package/src/commands/restart.ts +0 -6
  211. package/src/commands/run.ts +0 -608
  212. package/src/commands/serve.ts +0 -211
  213. package/src/commands/uninstall.ts +0 -9
  214. package/src/config.ts +0 -36
  215. package/src/cross-spawn.d.ts +0 -5
  216. package/src/event-queues.ts +0 -41
  217. package/src/events.ts +0 -29
  218. package/src/index.ts +0 -111
  219. package/src/linked-device.ts +0 -52
  220. package/src/mcp-handler.ts +0 -200
  221. package/src/mcp-tools.ts +0 -839
  222. package/src/nats-client.ts +0 -19
  223. package/src/network.ts +0 -96
  224. package/src/notification-store.ts +0 -30
  225. package/src/pending-requests.ts +0 -73
  226. package/src/platform/index.ts +0 -20
  227. package/src/platform/linux.ts +0 -296
  228. package/src/platform/macos.ts +0 -329
  229. package/src/platform/platform.ts +0 -31
  230. package/src/platform/windows.ts +0 -299
  231. package/src/rpc-handler.ts +0 -691
  232. package/src/sms-store.ts +0 -28
  233. package/src/spawn-command.ts +0 -123
  234. package/src/task.ts +0 -343
  235. package/src/transports/http-transport.ts +0 -478
  236. package/src/transports/nats-transport.ts +0 -76
  237. package/src/types.ts +0 -89
  238. package/src/update-checker.ts +0 -40
  239. package/test/agent-instructions.test.ts +0 -209
  240. package/test/agent-output-parsing.test.ts +0 -74
  241. package/test/linux-cron.test.ts +0 -41
  242. package/test/macos-plist.test.ts +0 -112
  243. package/test/notification-store.test.ts +0 -57
  244. package/test/pairing.test.ts +0 -35
  245. package/test/result-state.test.ts +0 -110
  246. package/test/task-parsing.test.ts +0 -82
  247. package/test/taskrun-messages.test.ts +0 -224
  248. package/test/tsconfig.json +0 -9
  249. package/test/windows-xml.test.ts +0 -89
  250. package/tsconfig.json +0 -19
@@ -1,358 +0,0 @@
1
- # Production Deployment Guide
2
-
3
- Single-instance deployment on Ubuntu 24 with TLS support. NATS and PostgreSQL run in Docker; the Palmier web server runs directly on the host via systemd. Caddy handles TLS termination with automatic Let's Encrypt certificates. Deployments are automated via GitHub Actions — pushing to `main` triggers a CI build, then deploys to the VPS over SSH.
4
-
5
- ## Architecture
6
-
7
- ```
8
- Internet
9
-
10
- ├── palmier.me → Cloudflare → Caddy → redirect to www
11
- ├── www.palmier.me → Cloudflare Pages (landing site, static)
12
- ├── app.palmier.me → Cloudflare → Caddy → localhost:3000 (Palmier server, HTTPS)
13
- └── nats.palmier.me → Caddy (direct) → localhost:9222 (NATS WebSocket)
14
- → port 4222 direct (NATS TCP for hosts)
15
-
16
- Caddy (ports 80/443, auto TLS)
17
-
18
- Docker
19
- ├── NATS (ports 4222 TCP public, 9222 WS localhost)
20
- └── PostgreSQL (port 5432 localhost)
21
-
22
- systemd
23
- └── Palmier Server (port 3000 localhost)
24
- ```
25
-
26
- ## Prerequisites
27
-
28
- Three DNS records pointing to your server's public IP:
29
-
30
- - `palmier.me` — root domain (Cloudflare proxied, redirects to `www`)
31
- - `www.palmier.me` — Landing site (Cloudflare Pages, static)
32
- - `app.palmier.me` — PWA + API over HTTPS (Cloudflare proxied)
33
- - `nats.palmier.me` — NATS WebSocket + TCP (DNS only, no Cloudflare proxy)
34
-
35
- ### Install Node.js 24+
36
-
37
- ```bash
38
- curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
39
- sudo apt install -y nodejs
40
- ```
41
-
42
- ### Install pnpm
43
-
44
- ```bash
45
- sudo npm install -g pnpm
46
- ```
47
-
48
- ### Install Docker
49
-
50
- ```bash
51
- sudo apt install -y docker.io docker-compose-v2
52
- sudo usermod -aG docker $USER
53
- # Log out and back in for group to take effect
54
- ```
55
-
56
- ### Install Caddy
57
-
58
- ```bash
59
- sudo apt install -y caddy
60
- ```
61
-
62
- ## 1. TLS with Caddy
63
-
64
- Caddy auto-provisions Let's Encrypt certificates with zero configuration.
65
-
66
- Create `/etc/caddy/Caddyfile`:
67
-
68
- ```
69
- palmier.me {
70
- redir https://www.palmier.me{uri} permanent
71
- }
72
-
73
- app.palmier.me {
74
- reverse_proxy localhost:3000
75
- }
76
-
77
- nats.palmier.me {
78
- @websocket {
79
- header Connection *Upgrade*
80
- header Upgrade websocket
81
- }
82
- reverse_proxy @websocket localhost:9222
83
- }
84
- ```
85
-
86
- This gives you:
87
-
88
- - `https://app.palmier.me` — PWA + API (auto TLS, Cloudflare CDN for static assets)
89
- - `wss://nats.palmier.me` — NATS WebSocket (auto TLS, direct to VPS)
90
- - `https://www.palmier.me` — Landing site (Cloudflare Pages, no VPS involvement)
91
-
92
- Local mode (loopback) serves the bundled PWA from the palmier host binary on `http://localhost:<port>`, so no separate domain is needed.
93
-
94
- Caddy auto-provisions Let's Encrypt certs for both domains. Set Cloudflare SSL/TLS mode to **Full (strict)** so Cloudflare verifies the origin cert when connecting to your VPS.
95
-
96
- No need to configure TLS in NATS itself.
97
-
98
- ```bash
99
- sudo systemctl enable --now caddy
100
- ```
101
-
102
- ## 2. NATS + PostgreSQL
103
-
104
- Create a directory for production Docker config:
105
-
106
- ```bash
107
- mkdir -p ~/palmier-prod
108
- ```
109
-
110
- ### 2a. Generate NATS auth keys and config
111
-
112
- ```bash
113
- cd server && pnpm nats-setup
114
- ```
115
-
116
- Follow the on-screen instructions — it outputs the `NATS_ACCOUNT_SEED` env var (for step 6) and the NATS config snippet (for `nats.conf` below). Store the operator seed securely as a backup.
117
-
118
- ### 2b. nats.conf
119
-
120
- Create `~/palmier-prod/nats.conf`. Paste the auth snippet from step 2a after the websocket block:
121
-
122
- ```
123
- listen: 0.0.0.0:4222
124
-
125
- websocket {
126
- listen: "0.0.0.0:9222"
127
- no_tls: true
128
- }
129
-
130
- # Paste the operator/resolver/resolver_preload output from nats-setup here
131
- ```
132
-
133
- ### 2c. docker-compose.yml
134
-
135
- Create `~/palmier-prod/docker-compose.yml`:
136
-
137
- ```yaml
138
- services:
139
- nats:
140
- image: nats:2-alpine
141
- restart: unless-stopped
142
- ports:
143
- - "4222:4222"
144
- - "127.0.0.1:9222:9222"
145
- volumes:
146
- - ./nats.conf:/etc/nats/nats.conf:ro
147
- - nats-data:/data
148
- command: ["-c", "/etc/nats/nats.conf"]
149
-
150
- postgres:
151
- image: postgres:17-alpine
152
- restart: unless-stopped
153
- ports:
154
- - "127.0.0.1:5432:5432"
155
- environment:
156
- POSTGRES_DB: palmier
157
- POSTGRES_USER: palmier
158
- POSTGRES_PASSWORD: <generate-a-strong-password>
159
- volumes:
160
- - pgdata:/var/lib/postgresql/data
161
-
162
- volumes:
163
- nats-data:
164
- pgdata:
165
- ```
166
-
167
- Generate a Postgres password:
168
-
169
- ```bash
170
- openssl rand -hex 16
171
- ```
172
-
173
- ### 2d. Start containers
174
-
175
- ```bash
176
- cd ~/palmier-prod
177
- docker compose up -d
178
- ```
179
-
180
- ## 3. NATS TCP for Remote Hosts
181
-
182
- Hosts connect to NATS over TCP (port 4222). Since Caddy only handles HTTP/WebSocket, port 4222 must be open on the firewall for hosts to connect. Each host authenticates with its own JWT (issued during `palmier init`), scoped to only its own NATS subjects.
183
-
184
- For encrypted host connections, you can add a TLS-terminating TCP proxy (e.g., nginx stream block) in front of port 4222.
185
-
186
- ## 4. Firewall
187
-
188
- ```bash
189
- sudo ufw allow 22/tcp # SSH (must allow before enabling!)
190
- sudo ufw allow 80/tcp # Caddy HTTP -> HTTPS redirect
191
- sudo ufw allow 443/tcp # Caddy HTTPS
192
- sudo ufw allow 4222/tcp # NATS TCP for hosts
193
- sudo ufw enable
194
- ```
195
-
196
- ## 5. Clone the Repository
197
-
198
- Set up VPS git access first (see [VPS Git Access](#vps-git-access)), then clone:
199
-
200
- ```bash
201
- mkdir -p ~/source
202
- cd ~/source
203
- git clone git@github.com:caihongxu/palmier-server.git
204
- cd palmier-server
205
- ```
206
-
207
- The first push to `main` will build and start the server automatically via GitHub Actions.
208
-
209
- ## 6. Configure Environment
210
-
211
- Create `server/.env`:
212
-
213
- ```bash
214
- PORT=3000
215
- DATABASE_URL=postgresql://palmier:<your-pg-password>@localhost:5432/palmier
216
-
217
- NATS_URL=nats://localhost:4222
218
- NATS_HOST_URL=nats://nats.palmier.me:4222
219
- NATS_WS_URL=wss://nats.palmier.me
220
- NATS_ACCOUNT_SEED=<from-nats-setup>
221
-
222
- VAPID_PUBLIC_KEY=<generated>
223
- VAPID_PRIVATE_KEY=<generated>
224
- VAPID_MAILTO=mailto:you@palmier.me
225
- ```
226
-
227
- Key differences from development:
228
-
229
- - `NATS_WS_URL` uses `wss://` through Caddy (not direct `ws://`)
230
- - `NATS_HOST_URL` uses your public domain so remote hosts can reach NATS
231
- - `NATS_ACCOUNT_SEED` from step 2a (`pnpm nats-setup`)
232
-
233
- Generate VAPID keys:
234
-
235
- ```bash
236
- npx web-push generate-vapid-keys
237
- ```
238
-
239
- ## 7. Palmier systemd Service
240
-
241
- Create `/etc/systemd/system/palmier-server.service`:
242
-
243
- ```ini
244
- [Unit]
245
- Description=Palmier Server
246
- After=network.target docker.service
247
-
248
- [Service]
249
- Type=simple
250
- User=hongxu
251
- WorkingDirectory=/home/hongxu/source/palmier-server/server
252
- ExecStart=/usr/bin/node dist/index.js
253
- Restart=on-failure
254
- RestartSec=5
255
- EnvironmentFile=/home/hongxu/source/palmier-server/server/.env
256
-
257
- [Install]
258
- WantedBy=multi-user.target
259
- ```
260
-
261
- Enable and start:
262
-
263
- ```bash
264
- sudo systemctl daemon-reload
265
- sudo systemctl enable --now palmier-server
266
- ```
267
-
268
- ## 8. Verify
269
-
270
- At this point, all services should be running (Docker from step 2, Caddy from step 1, Palmier from step 7). Verify:
271
-
272
- ```bash
273
- sudo systemctl status caddy
274
- sudo systemctl status palmier-server
275
- cd ~/palmier-prod && docker compose ps
276
- ```
277
-
278
- - `https://app.palmier.me` — should load the PWA (server mode)
279
- - `https://app.palmier.me/health` — should return OK
280
- - Logs: `journalctl -u palmier-server -f`
281
- - Docker logs: `cd ~/palmier-prod && docker compose logs -f`
282
-
283
- ## CI/CD with GitHub Actions
284
-
285
- Deployments are automated via two GitHub Actions workflows in `.github/workflows/`:
286
-
287
- - **`ci.yml`** — runs on every push (except `main`) and pull requests. Builds server + PWA to catch errors early.
288
- - **`deploy.yml`** — runs on push to `main`. Builds in CI first, then SSHs into the VPS to pull, build, and restart the service.
289
-
290
- ### GitHub Secrets
291
-
292
- Add these in the repo settings (Settings → Secrets and variables → Actions):
293
-
294
- | Secret | Description |
295
- |---|---|
296
- | `VPS_HOST` | Droplet IP address |
297
- | `VPS_USER` | SSH username (e.g., `hongxu`) |
298
- | `SSH_PRIVATE_KEY` | Ed25519 private key for SSH access |
299
-
300
- ### VPS SSH Setup
301
-
302
- Generate a deploy key pair and add the public key to the VPS:
303
-
304
- ```bash
305
- ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/palmier-deploy -N ""
306
- ```
307
-
308
- Append the public key to the VPS `~/.ssh/authorized_keys`. Add the private key as the `SSH_PRIVATE_KEY` GitHub secret.
309
-
310
- ### VPS Git Access
311
-
312
- The VPS needs to pull from the private repo. Add a read-only deploy key:
313
-
314
- 1. Generate a key on the VPS: `ssh-keygen -t ed25519 -C "palmier-server-deploy" -f ~/.ssh/deploy-key -N ""`
315
- 2. Add the public key to the repo (Settings → Deploy keys, read-only)
316
- 3. Configure SSH on the VPS to use the key for GitHub:
317
- ```
318
- # ~/.ssh/config
319
- Host github.com
320
- IdentityFile ~/.ssh/deploy-key
321
- ```
322
-
323
- ### Passwordless sudo for deploy
324
-
325
- The deploy workflow needs to restart the systemd service. Allow the deploy user to do this without a password:
326
-
327
- ```bash
328
- sudo visudo -f /etc/sudoers.d/palmier-deploy
329
- ```
330
-
331
- Add:
332
-
333
- ```
334
- hongxu ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart palmier-server
335
- ```
336
-
337
- ### Deploying
338
-
339
- Push to `main` and the deploy workflow handles everything automatically. To deploy manually:
340
-
341
- ```bash
342
- ssh hongxu@<vps-ip>
343
- cd ~/source/palmier-server
344
- git pull
345
- pnpm install --frozen-lockfile
346
- cd pwa && pnpm build && cd ..
347
- cd server && pnpm build && cd ..
348
- sudo systemctl restart palmier-server
349
- ```
350
-
351
- ## Component Summary
352
-
353
- | Component | Runtime | Ports |
354
- |---|---|---|
355
- | PostgreSQL | Docker | 5432 (localhost only) |
356
- | NATS | Docker | 4222 (public, for hosts), 9222 (localhost, proxied by Caddy) |
357
- | Palmier Server | systemd + Node.js | 3000 (localhost only) |
358
- | Caddy | systemd | 80, 443 (public) |
@@ -1,231 +0,0 @@
1
- # Palmier Server
2
-
3
- ![Deploy](https://github.com/caihongxu/palmier-server/actions/workflows/deploy.yml/badge.svg)
4
-
5
- Palmier is a platform for remotely scheduling, managing, and executing autonomous AI tasks on host machines via a progressive web app. Uses NATS for real-time communication (pub/sub and request-reply) and push notifications for task progress.
6
-
7
- ## Architecture
8
-
9
- ```
10
- +-----------+ +----------------+ +------------------+
11
- | | HTTP | | NATS | |
12
- | PWA |------>| Web Server |<----->| NATS Server |
13
- | (React) | | (Express) | | |
14
- | | | | | |
15
- +-----------+ +----------------+ +------------------+
16
- | | ^
17
- | NATS WebSocket | PostgreSQL | NATS
18
- +-------------------------------------------> |
19
- | |
20
- +----v----+ +-------+-------+
21
- | | | |
22
- | DB | | Host(s) |
23
- | (PG) | | (separate |
24
- | | | repo) |
25
- +---------+ +---------------+
26
- ```
27
-
28
- - **PWA** -- React 19 + Vite progressive web app. Connects to NATS over WebSocket for real-time task updates and to the web server for host registration and push notifications. No user accounts — paired hosts are stored in localStorage.
29
- - **Web Server** -- Express + TypeScript API server. Handles host registration, push notifications (subscribes to `host-event.>` pub/sub for confirmation and completion events), and push notification relay (for host CLI requests via NATS). In production, also serves the built PWA static files.
30
- - **NATS Server** -- Message broker. Provides pub/sub messaging and request-reply for real-time communication between all components.
31
- - **Host** -- Runs on remote Linux/Windows machines to execute tasks via pluggable agent tools (e.g., Claude Code, Codex, Gemini). Each agent implements an `AgentTool` interface that handles command construction. Communicates with the platform over NATS and exposes a local MCP server (`/mcp`, streamable HTTP) with auto-generated REST endpoints for tools and resources. See the [palmier](https://github.com/caihongxu/palmier) repo.
32
- - **Android App** -- Native Android wrapper (Capacitor) for the PWA. Provides FCM push messaging and native device capabilities — GPS, notifications, SMS, contacts, calendar, alarms, battery, and ringer control. All capabilities work in the background via FCM data messages. See the [palmier-android](https://github.com/caihongxu/palmier-android) repo.
33
-
34
- ## Prerequisites
35
-
36
- - Node.js 24+
37
- - pnpm (`npm install -g pnpm`)
38
- - PostgreSQL 17
39
- - NATS server with WebSocket enabled
40
-
41
- ## Getting Started
42
-
43
- 1. **Clone the repository**
44
-
45
- ```bash
46
- git clone https://github.com/caihongxu/palmier-server.git
47
- cd palmier-server
48
- ```
49
-
50
- 2. **Install dependencies** (uses pnpm workspaces)
51
-
52
- ```bash
53
- pnpm install
54
- ```
55
-
56
- 3. **Configure environment variables**
57
-
58
- ```bash
59
- cp server/.env.example server/.env
60
- ```
61
-
62
- Edit `server/.env` and fill in the values (see table below).
63
-
64
- 4. **Generate VAPID keys** for web push notifications
65
-
66
- ```bash
67
- npx web-push generate-vapid-keys
68
- ```
69
-
70
- Copy the public and private keys into `server/.env`.
71
-
72
- 5. **Create the PostgreSQL database**
73
-
74
- ```bash
75
- createdb palmier
76
- ```
77
-
78
- Update `DATABASE_URL` in `server/.env` with your connection string. Tables are created automatically on server startup.
79
-
80
- 6. **Start the NATS server** with the included config
81
-
82
- ```bash
83
- nats-server -c nats.conf
84
- ```
85
-
86
- The config enables WebSocket on port 9222. See **NATS JWT Auth Setup** below.
87
-
88
- ## Development
89
-
90
- Development uses two servers: Vite for the PWA (with hot module replacement) and Express for the API. Vite proxies `/api/*` requests to Express so everything appears same-origin.
91
-
92
- ```bash
93
- # Terminal 1 -- API server (port 3000)
94
- cd server
95
- pnpm dev
96
-
97
- # Terminal 2 -- PWA dev server (port 5173, proxies /api to :3000)
98
- cd pwa
99
- pnpm dev
100
-
101
- # To proxy to a remote API server instead of localhost:
102
- # Linux / macOS:
103
- API_URL=https://app.palmier.me pnpm dev
104
- # Windows (PowerShell):
105
- $env:API_URL="https://app.palmier.me"; pnpm dev
106
- ```
107
-
108
- Open `http://localhost:5173` in your browser.
109
-
110
- ### Production
111
-
112
- In production, Express serves the built PWA static files directly — one server, one port.
113
-
114
- ```bash
115
- # Build the PWA
116
- cd pwa && pnpm build
117
-
118
- # Start the server (serves API + PWA on port 3000)
119
- cd server && pnpm start
120
- ```
121
-
122
- Open `http://localhost:3000`.
123
-
124
- ### Host Setup
125
-
126
- The host runs on a separate Linux machine. See the [palmier README](https://github.com/caihongxu/palmier) for full details.
127
-
128
- 1. On the host machine, in your project directory, run:
129
- ```bash
130
- palmier init
131
- ```
132
- The interactive wizard detects agents, asks for the HTTP port, detects the default network interface, shows a summary for confirmation, registers with the server, saves config to `~/.config/palmier/host.json`, installs a background daemon, and generates a pairing code.
133
-
134
- 2. Enter the pairing code in the PWA to connect your device to the host.
135
-
136
- ## Environment Variables
137
-
138
- | Variable | Description | Example |
139
- |---|---|---|
140
- | `PORT` | HTTP server port | `3000` |
141
- | `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:password@localhost:5432/palmier` |
142
- | `NATS_URL` | NATS server URL (TCP, for server's own connection) | `nats://localhost:4222` |
143
- | `NATS_HOST_URL` | NATS URL sent to hosts during registration (use LAN IP) | `nats://192.168.1.100:4222` |
144
- | `NATS_WS_URL` | NATS WebSocket URL sent to PWA clients | `wss://nats.palmier.me` (prod) or `ws://192.168.1.100:9222` (LAN) |
145
- | `NATS_ACCOUNT_SEED` | NATS account NKey seed for signing JWTs | *(from nats-setup)* |
146
- | `VAPID_PUBLIC_KEY` | VAPID public key for web push | *(generated via web-push)* |
147
- | `VAPID_PRIVATE_KEY` | VAPID private key for web push | *(generated via web-push)* |
148
- | `VAPID_MAILTO` | Contact email for VAPID | `mailto:admin@example.com` |
149
- | `GOOGLE_APPLICATION_CREDENTIALS` | Path to Firebase service account JSON (for FCM) | `/path/to/service-account.json` |
150
-
151
- > **LAN setup note:** `NATS_URL` uses `localhost` because the server connects to NATS locally. `NATS_HOST_URL`, `NATS_WS_URL` must use the LAN IP so remote hosts and browsers can reach them. Firewall must allow inbound on ports 3000 (HTTP), 4222 (NATS TCP), 5173 (Vite dev), and 9222 (NATS WebSocket).
152
-
153
- ## API Endpoints
154
-
155
- All endpoints are prefixed with `/api`. No user authentication is required.
156
-
157
- | Method | Path | Description |
158
- |---|---|---|
159
- | `POST` | `/api/hosts/register` | Register a new host (returns hostId + NATS JWT credentials) |
160
- | `GET` | `/api/config` | Get pairing-only NATS credentials (can only publish to `pair.*`) |
161
- | `GET` | `/api/nats-credentials/:hostId` | Get host-scoped NATS credentials for PWA (RPC + events for one host) |
162
- | `POST` | `/api/push/subscribe` | Register a push notification subscription |
163
- | `DELETE` | `/api/push/subscribe` | Remove a push notification subscription |
164
- | `GET` | `/api/push/vapid-key` | Get the VAPID public key |
165
- | `POST` | `/api/push/respond` | Respond to a pending task confirmation via push notification |
166
- | `POST` | `/api/fcm/register` | Register an FCM token for a host (Android device) |
167
- | `POST` | `/api/fcm/geolocation-response` | Receive device location from Android, forward via NATS |
168
- | `POST` | `/api/device/notifications` | Relay device notification from Android to host via NATS |
169
- | `POST` | `/api/device/sms` | Relay incoming SMS from Android to host via NATS |
170
- | `POST` | `/api/device/contacts-response` | Relay contacts response from Android to host via NATS |
171
- | `POST` | `/api/device/calendar-response` | Relay calendar response from Android to host via NATS |
172
- | `POST` | `/api/device/sms-response` | Relay SMS send response from Android to host via NATS |
173
- | `POST` | `/api/device/alarm-response` | Relay alarm response from Android to host via NATS |
174
- | `POST` | `/api/device/battery-response` | Relay battery response from Android to host via NATS |
175
- | `POST` | `/api/device/ringer-response` | Relay ringer mode response from Android to host via NATS |
176
- | `GET` | `/health` | Health check |
177
-
178
-
179
- ## Key Implementation Notes
180
-
181
- - **No user accounts** — the PWA stores paired hosts in localStorage. Each device pairs with a host via a pairing code and receives a client token.
182
- - **Client tokens** are generated and validated on the host, not the server. They are included in every NATS RPC payload and as Bearer tokens for HTTP requests.
183
- - **NATS RPC** — the RPC method is derived from the NATS subject (e.g., `...rpc.task.list` → `task.list`), not the message body. The body contains request parameters plus `clientToken`. All NATS requests go through a centralized `request()` helper in `HostConnectionContext` that handles encoding/decoding and logging.
184
- - **Pairing** — `palmier pair` (or auto-pair after `palmier init`) generates a 6-char pairing code. The PWA enters the code, which routes to the host via NATS (`pair.<CODE>`) or HTTP (`POST /pair`). The host validates the code and returns a client token.
185
- - **Task IDs** are generated by the host as UUIDs.
186
- - **Schedule can be enabled/disabled** — the `schedule_enabled` frontmatter field (default `true`) controls whether the schedule is active. When disabled, timers are removed and device events are ignored for that task, but it can still be run manually. The schedule lives in two flat fields: `schedule_type` (`"crons"`, `"specific_times"`, `"on_new_notification"`, or `"on_new_sms"`) and `schedule_values` (array of cron expressions or local datetime strings — only used by `"crons"` and `"specific_times"`). The two `on_new_*` types have no `schedule_values`; they fire in response to device notifications/SMS relayed over NATS, with the daemon owning a per-task FIFO queue that `palmier run` drains via `POST /task-event/pop`.
187
- - **Host responses** return flat task objects (frontmatter fields at the top level, not nested) for `task.list`, `task.create`, and `task.update`.
188
- - **NATS "503"** means "no responders" — the dashboard silently handles this when no host is connected, showing an empty task list instead of an error.
189
- - **Helmet CSP** is disabled (`contentSecurityPolicy: false`) to allow NATS WebSocket connections and inline Vite scripts during dev.
190
- - **Static file serving** is conditional — Express only serves `pwa/dist/` if the directory exists, so it doesn't interfere during dev when using Vite.
191
- - **No CORS** needed — Vite proxy handles same-origin in dev, Express static serving handles it in production.
192
- - **Push notifications** — the PWA registers a service worker (`injectManifest` strategy via vite-plugin-pwa) and subscribes the browser for Web Push. The Web Server subscribes to `host-event.>` and sends push notifications for confirmation requests, task completions, and task failures.
193
- - **Markdown rendering** — Task results are rendered as rich formatted text using `react-markdown` with `remark-gfm` (GitHub Flavored Markdown), supporting tables, strikethrough, task lists, and autolinks.
194
- - **Pending prompts** — Dashboard fetches `host.info` once per connection and seeds any already-open confirmation/permission/input prompts from the response's `pending_prompts` array. Each entry carries a `meta` field with a unified `session_name` label (agent name for confirm/input, task name for permission) so modals render without needing the task list. After connect, live prompts arrive via NATS events (`confirm-request`, `permission-request`, `input-request`). The Dashboard owns the subscription and renders modals via React portal so they surface regardless of which tab is active. Push notification action buttons trigger `POST /api/push/respond`, which forwards to the `task.user_input` NATS RPC.
195
- - **Host-scoped routes** — all authenticated views live under `/hosts/:hostId/...` so the URL is the source of truth for the active host. Sessions tab is `/hosts/:hostId`, Tasks tab is `/hosts/:hostId/tasks`, run detail is `/hosts/:hostId/runs/:taskId/:runId`. `/` redirects to the first paired host (or `/pair` when no hosts are paired). Notification deep links include the host in the path so tapping a notification switches the active host even if another host was selected at the time.
196
- - **Lazy tab loads** — Sessions tab (default) fetches `taskrun.list` on mount; Tasks tab fetches `task.list` on mount. Neither is called at startup — only `host.info` is. Task lifecycle events are persisted to `status.json` on the host (for crash detection) and broadcast via `host-event.<host_id>.<task_id>` pub/sub and HTTP SSE.
197
- - **NATS auth** uses decentralized JWT/NKey authentication. Each host receives scoped credentials (can only access its own subjects). PWA clients get two-phase credentials: pairing-only JWT for the pairing flow, then host-scoped JWT after pairing. The server signs all user JWTs with the account key. Run `pnpm --filter palmier-server nats-setup` to generate keys and NATS config. See **NATS JWT Auth Setup** below.
198
-
199
- ## NATS JWT Auth Setup
200
-
201
- NATS uses decentralized JWT/NKey authentication. Each host gets scoped credentials that restrict it to its own subjects — one host cannot read or impersonate another.
202
-
203
- **One-time setup:**
204
-
205
- ```bash
206
- # Generate operator/account NKey pairs and NATS config
207
- cd server && pnpm nats-setup
208
- ```
209
-
210
- This outputs:
211
- 1. An `NATS_ACCOUNT_SEED` value — add it to `server/.env`
212
- 2. A NATS config snippet — replace the `authorization` block in `nats.conf`
213
-
214
- **How it works:**
215
-
216
- | Role | Publish | Subscribe |
217
- |------|---------|-----------|
218
- | **Host** (id=X) | `host-event.X.>`, `host.X.>` | `host.X.>`, `pair.*` |
219
- | **PWA** (pairing) | `pair.*` | *(none)* |
220
- | **PWA** (connected to X) | `host.X.rpc.>` | `host-event.X.>` |
221
- | **Server** | `>` | `>` |
222
-
223
- - The **operator** key signs the **account** JWT (embedded in NATS server config, one-time)
224
- - The **account** key signs **user** JWTs at runtime (per host registration, per PWA session)
225
- - Host credentials are generated during `POST /api/hosts/register` and stored in `~/.config/palmier/host.json`
226
- - PWA uses two-phase credentials: `GET /api/config` returns pairing-only JWT, then `GET /api/nats-credentials/:hostId` returns host-scoped JWT after pairing. A PWA client can only access the specific host it paired with.
227
-
228
- ## Related Repositories
229
-
230
- - [palmier](https://github.com/caihongxu/palmier) -- The host binary, published as `palmier` on npm. Install with `npm install -g palmier`. Uses npm (not pnpm).
231
- - [palmier-android](https://github.com/caihongxu/palmier-android) -- Native Android wrapper (Capacitor) for the PWA. Provides FCM and native device capabilities (GPS, notifications, SMS, contacts, calendar, alarms, battery, ringer).
@@ -1,19 +0,0 @@
1
- # NATS Server Configuration for Palmier
2
-
3
- # TCP listener (for server + host)
4
- listen: 0.0.0.0:4222
5
-
6
- # WebSocket listener (for PWA browser clients)
7
- websocket {
8
- listen: "0.0.0.0:9222"
9
- no_tls: true
10
- }
11
-
12
- # JWT/NKey authentication
13
- # Generate these values by running: cd server && pnpm nats-setup
14
- # Paste the auth snippet from the output below.
15
- # operator: <OPERATOR_JWT>
16
- # resolver: MEMORY
17
- # resolver_preload: {
18
- # <ACCOUNT_PUBLIC_KEY>: <ACCOUNT_JWT>
19
- # }
@@ -1,15 +0,0 @@
1
- {
2
- "name": "palmier-server",
3
- "private": true,
4
- "packageManager": "pnpm@10.32.1",
5
- "pnpm": {
6
- "onlyBuiltDependencies": [
7
- "bcrypt",
8
- "esbuild"
9
- ]
10
- },
11
- "dependencies": {
12
- "firebase-admin": "^13.8.0",
13
- "nkeys.js": "^1.1.0"
14
- }
15
- }