rms-devremote 3.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 (68) hide show
  1. package/README.md +154 -0
  2. package/dist/commands/attach.d.ts +2 -0
  3. package/dist/commands/attach.js +10 -0
  4. package/dist/commands/check.d.ts +2 -0
  5. package/dist/commands/check.js +210 -0
  6. package/dist/commands/clean.d.ts +2 -0
  7. package/dist/commands/clean.js +177 -0
  8. package/dist/commands/dashboard.d.ts +2 -0
  9. package/dist/commands/dashboard.js +57 -0
  10. package/dist/commands/link.d.ts +2 -0
  11. package/dist/commands/link.js +112 -0
  12. package/dist/commands/ping.d.ts +2 -0
  13. package/dist/commands/ping.js +21 -0
  14. package/dist/commands/setup.d.ts +2 -0
  15. package/dist/commands/setup.js +54 -0
  16. package/dist/commands/status.d.ts +2 -0
  17. package/dist/commands/status.js +65 -0
  18. package/dist/commands/unlink.d.ts +2 -0
  19. package/dist/commands/unlink.js +53 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +55 -0
  22. package/dist/server/auth.d.ts +6 -0
  23. package/dist/server/auth.js +32 -0
  24. package/dist/server/frontend.d.ts +4 -0
  25. package/dist/server/frontend.js +886 -0
  26. package/dist/server/index.d.ts +1 -0
  27. package/dist/server/index.js +283 -0
  28. package/dist/server/terminal.d.ts +14 -0
  29. package/dist/server/terminal.js +43 -0
  30. package/dist/services/battery-worker.d.ts +1 -0
  31. package/dist/services/battery-worker.js +2 -0
  32. package/dist/services/battery.d.ts +27 -0
  33. package/dist/services/battery.js +152 -0
  34. package/dist/services/config.d.ts +63 -0
  35. package/dist/services/config.js +84 -0
  36. package/dist/services/docker.d.ts +25 -0
  37. package/dist/services/docker.js +75 -0
  38. package/dist/services/hooks.d.ts +15 -0
  39. package/dist/services/hooks.js +111 -0
  40. package/dist/services/ntfy.d.ts +19 -0
  41. package/dist/services/ntfy.js +63 -0
  42. package/dist/services/process.d.ts +30 -0
  43. package/dist/services/process.js +90 -0
  44. package/dist/services/proxy-worker.d.ts +1 -0
  45. package/dist/services/proxy-worker.js +12 -0
  46. package/dist/services/proxy.d.ts +4 -0
  47. package/dist/services/proxy.js +195 -0
  48. package/dist/services/shell.d.ts +22 -0
  49. package/dist/services/shell.js +47 -0
  50. package/dist/services/tmux.d.ts +30 -0
  51. package/dist/services/tmux.js +74 -0
  52. package/dist/services/ttyd.d.ts +28 -0
  53. package/dist/services/ttyd.js +71 -0
  54. package/dist/setup-server/routes.d.ts +4 -0
  55. package/dist/setup-server/routes.js +177 -0
  56. package/dist/setup-server/server.d.ts +4 -0
  57. package/dist/setup-server/server.js +32 -0
  58. package/docker/docker-compose.yml +24 -0
  59. package/docker/ntfy/server.yml +6 -0
  60. package/package.json +61 -0
  61. package/scripts/claude-remote.sh +583 -0
  62. package/scripts/hooks/notify.sh +68 -0
  63. package/scripts/notify.sh +54 -0
  64. package/scripts/startup.sh +29 -0
  65. package/scripts/update-check.sh +25 -0
  66. package/src/setup-server/public/index.html +21 -0
  67. package/src/setup-server/public/setup.css +475 -0
  68. package/src/setup-server/public/setup.js +687 -0
@@ -0,0 +1,583 @@
1
+ #!/bin/bash
2
+ # claude-remote -- CLI for managing Claude Remote
3
+ # Data lives in ~/.claude-remote/ (config, .env, projects.conf)
4
+ # Source files live in the repo (Dockerfile, docker-compose, scripts)
5
+
6
+ set -euo pipefail
7
+
8
+ CLAUDE_REMOTE_HOME="${HOME}/.claude-remote"
9
+ # REPO_DIR is where the source files are (Dockerfile, docker-compose, etc.)
10
+ # Resolve symlink to find the real repo location
11
+ SCRIPT_REAL_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
12
+ REPO_DIR="$(cd "$(dirname "$SCRIPT_REAL_PATH")/.." && pwd)"
13
+
14
+ # Colors
15
+ RED='\033[0;31m'
16
+ GREEN='\033[0;32m'
17
+ YELLOW='\033[1;33m'
18
+ NC='\033[0m'
19
+
20
+ info() { echo -e "${GREEN}[claude-remote]${NC} $1"; }
21
+ warn() { echo -e "${YELLOW}[claude-remote]${NC} $1"; }
22
+ error() { echo -e "${RED}[claude-remote]${NC} $1" >&2; }
23
+
24
+ # ── Init check ───────────────────────────────────
25
+ cmd_init() {
26
+ if [ -f "${CLAUDE_REMOTE_HOME}/.env" ]; then
27
+ info "Claude Remote est deja installe"
28
+ echo ""
29
+ source "${CLAUDE_REMOTE_HOME}/.env"
30
+ echo " Domaine VS Code : https://${CLAUDE_DOMAIN:-non configure}"
31
+ echo " Domaine notifications : https://${NOTIFY_DOMAIN:-non configure}"
32
+ echo " Dossier config : ${CLAUDE_REMOTE_HOME}"
33
+ echo " Dossier source : ${REPO_DIR}"
34
+ echo ""
35
+
36
+ # Show projects
37
+ if [ -f "${CLAUDE_REMOTE_HOME}/projects.conf" ] && [ -s "${CLAUDE_REMOTE_HOME}/projects.conf" ]; then
38
+ echo " Projets :"
39
+ while IFS='|' read -r name path; do
40
+ [ -z "$name" ] && continue
41
+ echo " - ${name} -> ${path}"
42
+ done < "${CLAUDE_REMOTE_HOME}/projects.conf"
43
+ else
44
+ echo " Aucun projet (lance 'claude-remote add /chemin/projet')"
45
+ fi
46
+
47
+ echo ""
48
+ # Check if running
49
+ if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "claude-remote"; then
50
+ info "Status : en cours d'execution"
51
+ else
52
+ warn "Status : arrete (lance 'claude-remote up')"
53
+ fi
54
+ else
55
+ info "Claude Remote n'est pas encore installe"
56
+ echo ""
57
+ echo " Lance 'claude-remote setup' pour commencer l'installation."
58
+ fi
59
+ }
60
+
61
+ generate_compose() {
62
+ # Read projects.conf and generate docker-compose.override.yml with project volume mounts
63
+ local OVERRIDE="${CLAUDE_REMOTE_HOME}/docker-compose.override.yml"
64
+ local PROJECTS_CONF="${CLAUDE_REMOTE_HOME}/projects.conf"
65
+
66
+ if [ ! -f "$PROJECTS_CONF" ] || [ ! -s "$PROJECTS_CONF" ]; then
67
+ # No projects, remove override if exists
68
+ rm -f "$OVERRIDE"
69
+ return
70
+ fi
71
+
72
+ # Build volume list from projects.conf
73
+ local VOLUMES=""
74
+ while IFS='|' read -r name path; do
75
+ [ -z "$name" ] && continue
76
+ VOLUMES="${VOLUMES} - ${path}:/home/coder/projects/${name}\n"
77
+ done < "$PROJECTS_CONF"
78
+
79
+ if [ -z "$VOLUMES" ]; then
80
+ rm -f "$OVERRIDE"
81
+ return
82
+ fi
83
+
84
+ # Write override file
85
+ printf "services:\n code-server:\n volumes:\n${VOLUMES}" > "$OVERRIDE"
86
+ }
87
+
88
+ cmd_up() {
89
+ if [ ! -f "${CLAUDE_REMOTE_HOME}/.env" ]; then
90
+ error "Claude Remote n'est pas installe. Lance 'claude-remote setup' d'abord."
91
+ exit 1
92
+ fi
93
+
94
+ # Sync source files from repo to data dir
95
+ sync_files
96
+
97
+ generate_compose
98
+ info "Starting Claude Remote..."
99
+
100
+ local COMPOSE_CMD="docker compose -f ${CLAUDE_REMOTE_HOME}/docker-compose.yml"
101
+ if [ -f "${CLAUDE_REMOTE_HOME}/docker-compose.override.yml" ]; then
102
+ COMPOSE_CMD="${COMPOSE_CMD} -f ${CLAUDE_REMOTE_HOME}/docker-compose.override.yml"
103
+ fi
104
+ ${COMPOSE_CMD} up -d --build
105
+ info "Claude Remote is running"
106
+ }
107
+
108
+ sync_files() {
109
+ # Copy source files from repo to ~/.claude-remote/
110
+ # This ensures updates to the repo are reflected without re-setup
111
+ cp "${REPO_DIR}/docker-compose.yml" "${CLAUDE_REMOTE_HOME}/"
112
+ cp "${REPO_DIR}/Dockerfile" "${CLAUDE_REMOTE_HOME}/"
113
+ cp "${REPO_DIR}/.dockerignore" "${CLAUDE_REMOTE_HOME}/"
114
+ mkdir -p "${CLAUDE_REMOTE_HOME}/scripts/hooks"
115
+ cp "${REPO_DIR}/scripts/update-check.sh" "${CLAUDE_REMOTE_HOME}/scripts/"
116
+ cp "${REPO_DIR}/scripts/startup.sh" "${CLAUDE_REMOTE_HOME}/scripts/"
117
+ cp "${REPO_DIR}/scripts/hooks/notify.sh" "${CLAUDE_REMOTE_HOME}/scripts/hooks/"
118
+ chmod +x "${CLAUDE_REMOTE_HOME}/scripts/update-check.sh"
119
+ chmod +x "${CLAUDE_REMOTE_HOME}/scripts/startup.sh"
120
+ chmod +x "${CLAUDE_REMOTE_HOME}/scripts/hooks/notify.sh"
121
+ # Copy hooks.json if it exists in repo
122
+ if [ -f "${REPO_DIR}/hooks.json" ]; then
123
+ cp "${REPO_DIR}/hooks.json" "${CLAUDE_REMOTE_HOME}/hooks.json"
124
+ fi
125
+ }
126
+
127
+ cmd_down() {
128
+ info "Stopping Claude Remote..."
129
+ docker compose -f "${CLAUDE_REMOTE_HOME}/docker-compose.yml" down
130
+ info "Claude Remote stopped"
131
+ }
132
+
133
+ cmd_status() {
134
+ docker compose -f "${CLAUDE_REMOTE_HOME}/docker-compose.yml" ps
135
+ }
136
+
137
+ cmd_logs() {
138
+ docker compose -f "${CLAUDE_REMOTE_HOME}/docker-compose.yml" logs -f code-server
139
+ }
140
+
141
+ cmd_add() {
142
+ local project_path="$1"
143
+
144
+ if [ ! -d "$project_path" ]; then
145
+ error "Directory not found: $project_path"
146
+ exit 1
147
+ fi
148
+
149
+ # Resolve to absolute path
150
+ project_path="$(cd "$project_path" && pwd)"
151
+ local project_name
152
+ project_name="$(basename "$project_path")"
153
+
154
+ local PROJECTS_CONF="${CLAUDE_REMOTE_HOME}/projects.conf"
155
+ touch "$PROJECTS_CONF"
156
+
157
+ # Check if already exists
158
+ if grep -q "^${project_name}|" "$PROJECTS_CONF" 2>/dev/null; then
159
+ warn "Project '${project_name}' already exists, updating..."
160
+ if [[ "$OSTYPE" == "darwin"* ]]; then
161
+ sed -i '' "/^${project_name}|/d" "$PROJECTS_CONF"
162
+ else
163
+ sed -i "/^${project_name}|/d" "$PROJECTS_CONF"
164
+ fi
165
+ fi
166
+
167
+ echo "${project_name}|${project_path}" >> "$PROJECTS_CONF"
168
+ info "Project '${project_name}' added -> ${project_path}"
169
+ cmd_up
170
+ }
171
+
172
+ cmd_remove() {
173
+ local project_name="$1"
174
+ local PROJECTS_CONF="${CLAUDE_REMOTE_HOME}/projects.conf"
175
+
176
+ if ! grep -q "^${project_name}|" "$PROJECTS_CONF" 2>/dev/null; then
177
+ error "Project '${project_name}' not found"
178
+ exit 1
179
+ fi
180
+
181
+ if [[ "$OSTYPE" == "darwin"* ]]; then
182
+ sed -i '' "/^${project_name}|/d" "$PROJECTS_CONF"
183
+ else
184
+ sed -i "/^${project_name}|/d" "$PROJECTS_CONF"
185
+ fi
186
+
187
+ info "Project '${project_name}' removed"
188
+ cmd_up
189
+ }
190
+
191
+ cmd_setup() {
192
+ echo ""
193
+ echo "╔══════════════════════════════════════════╗"
194
+ echo "║ CLAUDE REMOTE - Setup v1 ║"
195
+ echo "║ Accede a Claude Code depuis ton phone ║"
196
+ echo "╚══════════════════════════════════════════╝"
197
+ echo ""
198
+
199
+ # ── Prerequis ──────────────────────────────────
200
+ echo "── Verification des prerequis ──"
201
+ echo ""
202
+
203
+ if ! command -v docker &> /dev/null; then
204
+ error "Docker non installe. Installe-le : https://docs.docker.com/get-docker/"
205
+ exit 1
206
+ fi
207
+ info "Docker ✓"
208
+
209
+ if ! docker compose version &> /dev/null; then
210
+ error "Docker Compose non disponible. Mets Docker a jour."
211
+ exit 1
212
+ fi
213
+ info "Docker Compose ✓"
214
+
215
+ if [ ! -d "${HOME}/.claude" ]; then
216
+ error "Session Claude Code non trouvee (~/.claude/)"
217
+ echo " Lance 'claude login' d'abord, puis relance ce setup."
218
+ exit 1
219
+ fi
220
+ info "Claude Code ✓"
221
+
222
+ mkdir -p "${CLAUDE_REMOTE_HOME}/ntfy"
223
+ mkdir -p "${CLAUDE_REMOTE_HOME}/scripts/hooks"
224
+
225
+ # ── Domaines ───────────────────────────────────
226
+ echo ""
227
+ echo "── Configuration des domaines ──"
228
+ echo ""
229
+ read -rp " Domaine VS Code (ex: claude.mondomaine.com): " CLAUDE_DOMAIN
230
+ read -rp " Domaine notifications (ex: notify.mondomaine.com): " NOTIFY_DOMAIN
231
+
232
+ # ── Cloudflare Tunnel ────────────────────────
233
+ echo ""
234
+ echo "── Configuration Cloudflare Tunnel ──"
235
+ echo ""
236
+ echo " Si tu as DEJA configure ton tunnel, passe au token."
237
+ echo " Sinon suis ces etapes :"
238
+ echo ""
239
+ echo " 1. Ouvre https://dash.cloudflare.com"
240
+ echo " → Networking → Tunnels → '+ Create a tunnel'"
241
+ echo ""
242
+ echo " 2. Selectionne 'Cloudflared' → Next"
243
+ echo " Nomme le tunnel (ex: claude-remote) → Save tunnel"
244
+ echo ""
245
+ echo " 3. Choisis l'environnement 'Docker'"
246
+ echo " Tu verras une commande :"
247
+ echo " docker run cloudflare/cloudflared ... --token eyJhb..."
248
+ echo " → Copie le token (la longue chaine apres --token)"
249
+ echo ""
250
+ read -rp " Token du tunnel: " CLOUDFLARE_TUNNEL_TOKEN
251
+
252
+ echo ""
253
+ echo " 4. Clique Next → Configure la 1ere route :"
254
+ echo " Subdomain : $(echo "${CLAUDE_DOMAIN}" | cut -d. -f1)"
255
+ echo " Domain : $(echo "${CLAUDE_DOMAIN}" | cut -d. -f2-)"
256
+ echo " Path : (vide)"
257
+ echo " Type : HTTP"
258
+ echo " URL : code-server:8443"
259
+ echo " → Sauvegarde"
260
+ echo ""
261
+ echo " 5. Ajoute la 2eme route :"
262
+ echo " → Clique sur ton tunnel → onglet 'Published application routes'"
263
+ echo " → '+ Add a published application route'"
264
+ echo " Subdomain : $(echo "${NOTIFY_DOMAIN}" | cut -d. -f1)"
265
+ echo " Domain : $(echo "${NOTIFY_DOMAIN}" | cut -d. -f2-)"
266
+ echo " Path : (vide)"
267
+ echo " Type : HTTP"
268
+ echo " URL : ntfy:80"
269
+ echo ""
270
+ read -rp " Appuie sur Entree quand les 2 routes sont ajoutees..."
271
+
272
+ # ── Cloudflare Access ──────────────────────────
273
+ echo ""
274
+ echo "── Protection Cloudflare Access ──"
275
+ echo ""
276
+ echo " 1. Va dans Zero Trust → Access → Applications → Add"
277
+ echo " 2. Type : Self-hosted"
278
+ echo " 3. Application name : claude-remote"
279
+ echo " Session Duration : 24 hours"
280
+ echo " → '+ Add public hostname' → ${CLAUDE_DOMAIN}"
281
+ echo " → NE PAS ajouter ${NOTIFY_DOMAIN}"
282
+ echo " 4. Clique 'Add a policy' :"
283
+ echo " Policy name : allow-me"
284
+ echo " Action : Allow"
285
+ echo " Selector : Emails → ton email"
286
+ echo " 5. Sauvegarde"
287
+ echo ""
288
+ read -rp " Appuie sur Entree quand c'est fait..."
289
+ info "Cloudflare ✓"
290
+
291
+ # ── Mot de passe & config ──────────────────────
292
+ echo ""
293
+ echo "── Configuration locale ──"
294
+ echo ""
295
+ read -rs -p " Mot de passe code-server: " CODE_SERVER_PASSWORD
296
+ echo ""
297
+
298
+ local DEFAULT_TZ="Europe/Paris"
299
+ read -rp " Fuseau horaire [${DEFAULT_TZ}]: " TZ_INPUT
300
+ TZ="${TZ_INPUT:-$DEFAULT_TZ}"
301
+
302
+ NTFY_TOPIC="claude-remote-$(openssl rand -hex 6)"
303
+
304
+ # ── Installation des fichiers ────────────────
305
+ echo ""
306
+ echo "── Installation ──"
307
+ echo ""
308
+
309
+ sync_files
310
+ info "Fichiers copies ✓"
311
+
312
+ # Write ntfy server.yml with user's domain
313
+ cat > "${CLAUDE_REMOTE_HOME}/ntfy/server.yml" << NTFYEOF
314
+ # ntfy server configuration -- generated by claude-remote setup
315
+ base-url: https://${NOTIFY_DOMAIN}
316
+ listen-http: ":80"
317
+ auth-default-access: "deny-all"
318
+ behind-proxy: true
319
+ cache-file: "/var/cache/ntfy/cache.db"
320
+ auth-file: "/var/lib/ntfy/user.db"
321
+ enable-web: false
322
+ NTFYEOF
323
+
324
+ # Step 12: Write .env
325
+ cat > "${CLAUDE_REMOTE_HOME}/.env" << ENVEOF
326
+ # CLAUDE-REMOTE -- Generated by setup
327
+ CODE_SERVER_PASSWORD=${CODE_SERVER_PASSWORD}
328
+ CLOUDFLARE_TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
329
+ CLAUDE_DOMAIN=${CLAUDE_DOMAIN}
330
+ NOTIFY_DOMAIN=${NOTIFY_DOMAIN}
331
+ NTFY_TOPIC=${NTFY_TOPIC}
332
+ NTFY_TOKEN=placeholder
333
+ TZ=${TZ}
334
+ ENVEOF
335
+
336
+ info ".env ✓"
337
+
338
+ # ── Build Docker ───────────────────────────────
339
+ echo ""
340
+ echo "── Build Docker (premiere fois, ca peut prendre quelques minutes) ──"
341
+ echo ""
342
+ docker compose -f "${CLAUDE_REMOTE_HOME}/docker-compose.yml" up -d --build
343
+
344
+ # Step 14: Create ntfy user and token
345
+ info "Configuration auth ntfy..."
346
+ sleep 5
347
+
348
+ # Create admin user with a readable password
349
+ local NTFY_ADMIN_PASS
350
+ NTFY_ADMIN_PASS="$(openssl rand -hex 8)"
351
+ docker exec -e NTFY_PASSWORD="${NTFY_ADMIN_PASS}" claude-ntfy ntfy user add --role=admin --ignore-exists admin
352
+ # If user already existed, update its password
353
+ docker exec -e NTFY_PASSWORD="${NTFY_ADMIN_PASS}" claude-ntfy ntfy user change-pass admin 2>/dev/null || true
354
+
355
+ # Create access token and capture the real token value
356
+ local NTFY_TOKEN_OUTPUT
357
+ NTFY_TOKEN_OUTPUT=$(docker exec claude-ntfy ntfy token add admin 2>&1)
358
+ NTFY_TOKEN=$(echo "$NTFY_TOKEN_OUTPUT" | grep -oE 'tk_[a-zA-Z0-9]+' | head -1)
359
+
360
+ if [ -z "$NTFY_TOKEN" ]; then
361
+ error "Failed to create ntfy token. Output: ${NTFY_TOKEN_OUTPUT}"
362
+ exit 1
363
+ fi
364
+
365
+ # Update .env with real token and admin password
366
+ if [[ "$OSTYPE" == "darwin"* ]]; then
367
+ sed -i '' "s|NTFY_TOKEN=placeholder|NTFY_TOKEN=${NTFY_TOKEN}|" "${CLAUDE_REMOTE_HOME}/.env"
368
+ else
369
+ sed -i "s|NTFY_TOKEN=placeholder|NTFY_TOKEN=${NTFY_TOKEN}|" "${CLAUDE_REMOTE_HOME}/.env"
370
+ fi
371
+
372
+ # Save admin password to .env for reference
373
+ echo "NTFY_ADMIN_PASSWORD=${NTFY_ADMIN_PASS}" >> "${CLAUDE_REMOTE_HOME}/.env"
374
+
375
+ info "Token ntfy ✓"
376
+
377
+ # Restart code-server to pick up the real token
378
+ docker compose -f "${CLAUDE_REMOTE_HOME}/docker-compose.yml" up -d code-server
379
+
380
+ # ── Hooks Claude Code ──────────────────────────
381
+ echo ""
382
+ echo "── Configuration des hooks Claude Code ──"
383
+ echo ""
384
+
385
+ # Write hooks config as a separate file inside ~/.claude-remote/
386
+ # The startup.sh script will copy this into the container's settings
387
+ cat > "${CLAUDE_REMOTE_HOME}/hooks.json" << 'HOOKSEOF'
388
+ {
389
+ "hooks": {
390
+ "Notification": [
391
+ {
392
+ "matcher": "",
393
+ "hooks": [
394
+ {
395
+ "type": "command",
396
+ "command": "/home/coder/scripts/hooks/notify.sh notification \"$CLAUDE_NOTIFICATION\""
397
+ }
398
+ ]
399
+ }
400
+ ],
401
+ "Stop": [
402
+ {
403
+ "matcher": "",
404
+ "hooks": [
405
+ {
406
+ "type": "command",
407
+ "command": "/home/coder/scripts/hooks/notify.sh stop $EXIT_CODE"
408
+ }
409
+ ]
410
+ }
411
+ ]
412
+ }
413
+ }
414
+ HOOKSEOF
415
+
416
+ info "Hooks ✓"
417
+
418
+ # ── Configuration du phone ─────────────────────
419
+ echo ""
420
+ echo "── Configuration de ton phone ──"
421
+ echo ""
422
+ echo " 1. Installe l'app 'ntfy' depuis le Play Store / App Store"
423
+ echo ""
424
+ echo " 2. Configure le compte (AVANT de s'abonner au topic) :"
425
+ echo " - Ouvre l'app -> menu (3 points en haut) -> Settings -> Users"
426
+ echo " - Clique '+' pour ajouter un utilisateur"
427
+ echo " - Service URL : https://${NOTIFY_DOMAIN}"
428
+ echo " - Username : admin"
429
+ echo " - Password : ${NTFY_ADMIN_PASS}"
430
+ echo " - Sauvegarde"
431
+ echo ""
432
+ echo " 3. Abonne-toi au topic :"
433
+ echo " - Retour a l'ecran principal -> appuie sur '+'"
434
+ echo " - Topic name : ${NTFY_TOPIC}"
435
+ echo " - Coche 'Use another server'"
436
+ echo " - Server URL : https://${NOTIFY_DOMAIN}"
437
+ echo " - Coche 'Instant delivery in doze mode'"
438
+ echo " - Appuie sur SUBSCRIBE"
439
+ echo ""
440
+ read -rp "Appuie sur Entree quand c'est fait..."
441
+
442
+ info "Envoi notification de test..."
443
+ docker exec claude-remote sh -c "
444
+ curl -s \
445
+ -H 'Authorization: Bearer ${NTFY_TOKEN}' \
446
+ -H 'Title: Claude Remote' \
447
+ -d 'Test Claude Remote -- les notifications marchent !' \
448
+ http://ntfy:80/${NTFY_TOPIC}
449
+ "
450
+
451
+ echo ""
452
+ read -rp "Tu as recu 'Test Claude Remote' sur ton phone ? (oui/non): " TEST_RESULT
453
+
454
+ if [ "$TEST_RESULT" != "oui" ]; then
455
+ warn "Verifie que :"
456
+ echo " - L'app ntfy.sh est installee"
457
+ echo " - Le serveur est https://${NOTIFY_DOMAIN}"
458
+ echo " - Le topic est ${NTFY_TOPIC}"
459
+ echo " - Le token est saisi dans l'app"
460
+ echo " - Ton phone a du reseau"
461
+ else
462
+ info "Notifications OK"
463
+ fi
464
+
465
+ # Step 17: Install CLI globally (symlink to REPO, not to ~/.claude-remote)
466
+ echo ""
467
+ info "Installation de la commande claude-remote..."
468
+ local CLI_TARGET="${REPO_DIR}/scripts/claude-remote.sh"
469
+ if [ -w "/usr/local/bin" ]; then
470
+ ln -sf "${CLI_TARGET}" /usr/local/bin/claude-remote
471
+ else
472
+ sudo ln -sf "${CLI_TARGET}" /usr/local/bin/claude-remote
473
+ fi
474
+ info "Commande 'claude-remote' installee globalement ✓"
475
+
476
+ echo ""
477
+ info "C'est pret ! Ouvre https://${CLAUDE_DOMAIN}"
478
+ echo ""
479
+ echo "Commandes utiles :"
480
+ echo " claude-remote up # Demarrer"
481
+ echo " claude-remote down # Arreter"
482
+ echo " claude-remote add /chemin/projet # Ajouter un projet"
483
+ echo " claude-remote status # Etat"
484
+ }
485
+
486
+ cmd_ping() {
487
+ info "Test de connexion..."
488
+ echo ""
489
+
490
+ # Check containers are running
491
+ local ALL_OK=true
492
+
493
+ if docker ps --format '{{.Names}}' | grep -q "claude-remote"; then
494
+ info "code-server : OK (running)"
495
+ else
496
+ error "code-server : DOWN"
497
+ ALL_OK=false
498
+ fi
499
+
500
+ if docker ps --format '{{.Names}}' | grep -q "claude-ntfy"; then
501
+ info "ntfy : OK (running)"
502
+ else
503
+ error "ntfy : DOWN"
504
+ ALL_OK=false
505
+ fi
506
+
507
+ if docker ps --format '{{.Names}}' | grep -q "claude-tunnel"; then
508
+ info "tunnel : OK (running)"
509
+ else
510
+ error "tunnel : DOWN"
511
+ ALL_OK=false
512
+ fi
513
+
514
+ # Test ntfy connectivity from code-server
515
+ if [ "$ALL_OK" = true ]; then
516
+ echo ""
517
+ local NTFY_TEST
518
+ NTFY_TEST=$(docker exec claude-remote sh -c "curl -s -o /dev/null -w '%{http_code}' http://ntfy:80/v1/health" 2>/dev/null || echo "000")
519
+ if [ "$NTFY_TEST" = "200" ]; then
520
+ info "ntfy health : OK"
521
+ else
522
+ error "ntfy health : FAIL (HTTP ${NTFY_TEST})"
523
+ ALL_OK=false
524
+ fi
525
+ fi
526
+
527
+ # Test notification
528
+ if [ "$ALL_OK" = true ]; then
529
+ echo ""
530
+ read -rp "Envoyer une notification de test sur ton phone ? (oui/non): " SEND_TEST
531
+ if [ "$SEND_TEST" = "oui" ]; then
532
+ source "${CLAUDE_REMOTE_HOME}/.env"
533
+ docker exec claude-remote sh -c "
534
+ curl -s \
535
+ -H 'Authorization: Bearer ${NTFY_TOKEN}' \
536
+ -H 'Title: Claude Remote' \
537
+ -d 'Ping ! Claude Remote fonctionne.' \
538
+ http://ntfy:80/${NTFY_TOPIC}
539
+ "
540
+ info "Notification envoyee"
541
+ fi
542
+ fi
543
+
544
+ echo ""
545
+ if [ "$ALL_OK" = true ]; then
546
+ info "Tout est OK"
547
+ else
548
+ error "Il y a des problemes. Lance 'claude-remote logs' pour voir les erreurs."
549
+ fi
550
+ }
551
+
552
+ cmd_help() {
553
+ echo "claude-remote -- Accede a Claude Code depuis ton phone"
554
+ echo ""
555
+ echo "Usage: claude-remote <command> [args]"
556
+ echo ""
557
+ echo "Commands:"
558
+ echo " init Voir l'etat de l'installation"
559
+ echo " setup Installation premiere fois"
560
+ echo " up Demarre les containers"
561
+ echo " down Arrete les containers"
562
+ echo " status Etat des containers"
563
+ echo " logs Logs du container principal"
564
+ echo " ping Teste la connexion et les notifications"
565
+ echo " add <path> Ajoute un projet"
566
+ echo " remove <name> Retire un projet"
567
+ echo " help Affiche cette aide"
568
+ }
569
+
570
+ # Main
571
+ case "${1:-init}" in
572
+ init) cmd_init ;;
573
+ setup) cmd_setup ;;
574
+ up) cmd_up ;;
575
+ down) cmd_down ;;
576
+ status) cmd_status ;;
577
+ logs) cmd_logs ;;
578
+ ping) cmd_ping ;;
579
+ add) cmd_add "${2:?Usage: claude-remote add <path>}" ;;
580
+ remove) cmd_remove "${2:?Usage: claude-remote remove <name>}" ;;
581
+ help) cmd_help ;;
582
+ *) error "Unknown command: $1"; cmd_help; exit 1 ;;
583
+ esac
@@ -0,0 +1,68 @@
1
+ #!/bin/bash
2
+ # Claude Code hook: sends push notifications to self-hosted ntfy
3
+ # Called by Claude Code hooks (Notification, Stop)
4
+ #
5
+ # Usage:
6
+ # notify.sh notification "message"
7
+ # notify.sh stop <exit_code>
8
+ # notify.sh test
9
+ #
10
+ # Required env vars: NTFY_URL, NTFY_TOPIC, NTFY_TOKEN, CLAUDE_DOMAIN
11
+
12
+ set -euo pipefail
13
+
14
+ EVENT="${1:-}"
15
+ MESSAGE="${2:-}"
16
+
17
+ # Read JSON input from stdin (Claude Code passes hook data as JSON)
18
+ STDIN_JSON=""
19
+ if [ ! -t 0 ]; then
20
+ STDIN_JSON="$(cat)"
21
+ fi
22
+
23
+ if [ -z "$EVENT" ]; then
24
+ echo "Usage: notify.sh <event> [message]" >&2
25
+ exit 1
26
+ fi
27
+
28
+ send_notification() {
29
+ local body="$1"
30
+ local priority="${2:-default}"
31
+
32
+ curl -s \
33
+ -H "Authorization: Bearer ${NTFY_TOKEN}" \
34
+ -H "Title: Claude Remote" \
35
+ -H "Priority: ${priority}" \
36
+ -H "Click: https://${CLAUDE_DOMAIN}" \
37
+ -d "${body}" \
38
+ "${NTFY_URL}/${NTFY_TOPIC}" > /dev/null 2>&1 || true
39
+ }
40
+
41
+ case "$EVENT" in
42
+ tool-use)
43
+ # Parse tool name from JSON stdin
44
+ TOOL_NAME="unknown"
45
+ if [ -n "$STDIN_JSON" ] && command -v jq &> /dev/null; then
46
+ TOOL_NAME=$(echo "$STDIN_JSON" | jq -r '.tool_name // "unknown"')
47
+ fi
48
+ send_notification "Claude veut utiliser: ${TOOL_NAME}" "high"
49
+ ;;
50
+ notification)
51
+ send_notification "Claude: ${MESSAGE}" "high"
52
+ ;;
53
+ stop)
54
+ EXIT_CODE="${MESSAGE:-0}"
55
+ if [ "$EXIT_CODE" = "0" ]; then
56
+ send_notification "Claude a termine sa tache" "default"
57
+ else
58
+ send_notification "Erreur sur Claude (code: ${EXIT_CODE})" "urgent"
59
+ fi
60
+ ;;
61
+ test)
62
+ send_notification "Test Claude Remote -- les notifications marchent !" "default"
63
+ ;;
64
+ *)
65
+ echo "Unknown event: $EVENT" >&2
66
+ exit 1
67
+ ;;
68
+ esac