@hexidecibel/companion 0.0.1

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 (109) hide show
  1. package/dist/__tests__/task-parser.test.d.ts +2 -0
  2. package/dist/__tests__/task-parser.test.d.ts.map +1 -0
  3. package/dist/__tests__/task-parser.test.js +79 -0
  4. package/dist/__tests__/task-parser.test.js.map +1 -0
  5. package/dist/anthropic-usage.d.ts +5 -0
  6. package/dist/anthropic-usage.d.ts.map +1 -0
  7. package/dist/anthropic-usage.js +112 -0
  8. package/dist/anthropic-usage.js.map +1 -0
  9. package/dist/cert-generator.d.ts +15 -0
  10. package/dist/cert-generator.d.ts.map +1 -0
  11. package/dist/cert-generator.js +298 -0
  12. package/dist/cert-generator.js.map +1 -0
  13. package/dist/config.d.ts +4 -0
  14. package/dist/config.d.ts.map +1 -0
  15. package/dist/config.js +122 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/encryption.d.ts +28 -0
  18. package/dist/encryption.d.ts.map +1 -0
  19. package/dist/encryption.js +95 -0
  20. package/dist/encryption.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 +211 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/input-injector.d.ts +21 -0
  26. package/dist/input-injector.d.ts.map +1 -0
  27. package/dist/input-injector.js +126 -0
  28. package/dist/input-injector.js.map +1 -0
  29. package/dist/mdns.d.ts +11 -0
  30. package/dist/mdns.d.ts.map +1 -0
  31. package/dist/mdns.js +93 -0
  32. package/dist/mdns.js.map +1 -0
  33. package/dist/parser.d.ts +43 -0
  34. package/dist/parser.d.ts.map +1 -0
  35. package/dist/parser.js +800 -0
  36. package/dist/parser.js.map +1 -0
  37. package/dist/push.d.ts +38 -0
  38. package/dist/push.d.ts.map +1 -0
  39. package/dist/push.js +359 -0
  40. package/dist/push.js.map +1 -0
  41. package/dist/qr-server.d.ts +13 -0
  42. package/dist/qr-server.d.ts.map +1 -0
  43. package/dist/qr-server.js +421 -0
  44. package/dist/qr-server.js.map +1 -0
  45. package/dist/scaffold/generator.d.ts +11 -0
  46. package/dist/scaffold/generator.d.ts.map +1 -0
  47. package/dist/scaffold/generator.js +206 -0
  48. package/dist/scaffold/generator.js.map +1 -0
  49. package/dist/scaffold/templates/index.d.ts +5 -0
  50. package/dist/scaffold/templates/index.d.ts.map +1 -0
  51. package/dist/scaffold/templates/index.js +22 -0
  52. package/dist/scaffold/templates/index.js.map +1 -0
  53. package/dist/scaffold/templates/node-express.d.ts +3 -0
  54. package/dist/scaffold/templates/node-express.d.ts.map +1 -0
  55. package/dist/scaffold/templates/node-express.js +218 -0
  56. package/dist/scaffold/templates/node-express.js.map +1 -0
  57. package/dist/scaffold/templates/python-fastapi.d.ts +3 -0
  58. package/dist/scaffold/templates/python-fastapi.d.ts.map +1 -0
  59. package/dist/scaffold/templates/python-fastapi.js +302 -0
  60. package/dist/scaffold/templates/python-fastapi.js.map +1 -0
  61. package/dist/scaffold/templates/react-mui-website.d.ts +3 -0
  62. package/dist/scaffold/templates/react-mui-website.d.ts.map +1 -0
  63. package/dist/scaffold/templates/react-mui-website.js +405 -0
  64. package/dist/scaffold/templates/react-mui-website.js.map +1 -0
  65. package/dist/scaffold/templates/react-typescript.d.ts +3 -0
  66. package/dist/scaffold/templates/react-typescript.d.ts.map +1 -0
  67. package/dist/scaffold/templates/react-typescript.js +275 -0
  68. package/dist/scaffold/templates/react-typescript.js.map +1 -0
  69. package/dist/scaffold/types.d.ts +55 -0
  70. package/dist/scaffold/types.d.ts.map +1 -0
  71. package/dist/scaffold/types.js +3 -0
  72. package/dist/scaffold/types.js.map +1 -0
  73. package/dist/subagent-watcher.d.ts +24 -0
  74. package/dist/subagent-watcher.d.ts.map +1 -0
  75. package/dist/subagent-watcher.js +307 -0
  76. package/dist/subagent-watcher.js.map +1 -0
  77. package/dist/tls.d.ts +10 -0
  78. package/dist/tls.d.ts.map +1 -0
  79. package/dist/tls.js +77 -0
  80. package/dist/tls.js.map +1 -0
  81. package/dist/tmux-manager.d.ts +71 -0
  82. package/dist/tmux-manager.d.ts.map +1 -0
  83. package/dist/tmux-manager.js +243 -0
  84. package/dist/tmux-manager.js.map +1 -0
  85. package/dist/tool-config.d.ts +33 -0
  86. package/dist/tool-config.d.ts.map +1 -0
  87. package/dist/tool-config.js +211 -0
  88. package/dist/tool-config.js.map +1 -0
  89. package/dist/types.d.ts +218 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +3 -0
  92. package/dist/types.js.map +1 -0
  93. package/dist/watcher.d.ts +63 -0
  94. package/dist/watcher.d.ts.map +1 -0
  95. package/dist/watcher.js +596 -0
  96. package/dist/watcher.js.map +1 -0
  97. package/dist/watcher.test.d.ts +2 -0
  98. package/dist/watcher.test.d.ts.map +1 -0
  99. package/dist/watcher.test.js +110 -0
  100. package/dist/watcher.test.js.map +1 -0
  101. package/dist/websocket.d.ts +62 -0
  102. package/dist/websocket.d.ts.map +1 -0
  103. package/dist/websocket.js +1695 -0
  104. package/dist/websocket.js.map +1 -0
  105. package/package.json +71 -0
  106. package/scripts/build.sh +23 -0
  107. package/scripts/install-remote.sh +18 -0
  108. package/scripts/install.sh +558 -0
  109. package/scripts/uninstall.sh +113 -0
@@ -0,0 +1,558 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Colors for output
5
+ RED='\033[0;31m'
6
+ GREEN='\033[0;32m'
7
+ YELLOW='\033[1;33m'
8
+ BLUE='\033[0;34m'
9
+ NC='\033[0m' # No Color
10
+
11
+ echo -e "${BLUE}"
12
+ echo "╔══════════════════════════════════════════════════════════════╗"
13
+ echo "║ Companion Daemon Installer ║"
14
+ echo "╚══════════════════════════════════════════════════════════════╝"
15
+ echo -e "${NC}"
16
+
17
+ # Detect OS
18
+ detect_os() {
19
+ if [[ "$OSTYPE" == "darwin"* ]]; then
20
+ OS="macos"
21
+ PACKAGE_MANAGER="brew"
22
+ elif [[ -f /etc/debian_version ]]; then
23
+ OS="debian"
24
+ PACKAGE_MANAGER="apt"
25
+ elif [[ -f /etc/redhat-release ]]; then
26
+ OS="redhat"
27
+ PACKAGE_MANAGER="dnf"
28
+ # Fall back to yum for older systems
29
+ if ! command -v dnf &> /dev/null; then
30
+ PACKAGE_MANAGER="yum"
31
+ fi
32
+ elif [[ -f /etc/arch-release ]]; then
33
+ OS="arch"
34
+ PACKAGE_MANAGER="pacman"
35
+ elif [[ -f /etc/alpine-release ]]; then
36
+ OS="alpine"
37
+ PACKAGE_MANAGER="apk"
38
+ else
39
+ OS="unknown"
40
+ PACKAGE_MANAGER="unknown"
41
+ fi
42
+ echo -e "${GREEN}Detected OS:${NC} $OS ($PACKAGE_MANAGER)"
43
+ }
44
+
45
+ # Check if running as root/sudo (not needed on macOS for user install)
46
+ check_permissions() {
47
+ if [[ "$OS" == "macos" ]]; then
48
+ # macOS: running as user is fine for user-level install
49
+ ACTUAL_USER="$USER"
50
+ ACTUAL_HOME="$HOME"
51
+ INSTALL_DIR="$HOME/.companion"
52
+ CONFIG_DIR="$HOME/.companion"
53
+ NEEDS_SUDO=false
54
+ else
55
+ # Linux: prefer system-wide install with sudo
56
+ if [ "$EUID" -ne 0 ]; then
57
+ echo -e "${YELLOW}Running without sudo - will install to user directory${NC}"
58
+ ACTUAL_USER="$USER"
59
+ ACTUAL_HOME="$HOME"
60
+ INSTALL_DIR="$HOME/.companion"
61
+ CONFIG_DIR="$HOME/.companion"
62
+ NEEDS_SUDO=false
63
+ else
64
+ ACTUAL_USER="${SUDO_USER:-$USER}"
65
+ ACTUAL_HOME=$(eval echo "~$ACTUAL_USER")
66
+ INSTALL_DIR="/opt/companion"
67
+ CONFIG_DIR="/etc/companion"
68
+ NEEDS_SUDO=true
69
+ fi
70
+ fi
71
+
72
+ echo -e "${GREEN}Installing for user:${NC} $ACTUAL_USER"
73
+ echo -e "${GREEN}Install directory:${NC} $INSTALL_DIR"
74
+ echo -e "${GREEN}Config directory:${NC} $CONFIG_DIR"
75
+ echo ""
76
+ }
77
+
78
+ # Install system dependencies
79
+ install_dependencies() {
80
+ echo -e "${BLUE}Checking dependencies...${NC}"
81
+
82
+ local DEPS_TO_INSTALL=()
83
+
84
+ # Check Node.js
85
+ if ! command -v node &> /dev/null; then
86
+ DEPS_TO_INSTALL+=("nodejs")
87
+ else
88
+ NODE_VERSION=$(node -v)
89
+ echo -e " ${GREEN}✓${NC} Node.js $NODE_VERSION"
90
+ fi
91
+
92
+ # Check npm
93
+ if ! command -v npm &> /dev/null; then
94
+ DEPS_TO_INSTALL+=("npm")
95
+ else
96
+ NPM_VERSION=$(npm -v)
97
+ echo -e " ${GREEN}✓${NC} npm $NPM_VERSION"
98
+ fi
99
+
100
+ # Check tmux
101
+ if ! command -v tmux &> /dev/null; then
102
+ DEPS_TO_INSTALL+=("tmux")
103
+ else
104
+ TMUX_VERSION=$(tmux -V)
105
+ echo -e " ${GREEN}✓${NC} $TMUX_VERSION"
106
+ fi
107
+
108
+ # Check git (needed for clone)
109
+ if ! command -v git &> /dev/null; then
110
+ DEPS_TO_INSTALL+=("git")
111
+ else
112
+ echo -e " ${GREEN}✓${NC} git $(git --version | cut -d' ' -f3)"
113
+ fi
114
+
115
+ # Check openssl (for token generation)
116
+ if ! command -v openssl &> /dev/null; then
117
+ DEPS_TO_INSTALL+=("openssl")
118
+ else
119
+ echo -e " ${GREEN}✓${NC} openssl"
120
+ fi
121
+
122
+ if [ ${#DEPS_TO_INSTALL[@]} -eq 0 ]; then
123
+ echo -e "${GREEN}All dependencies satisfied!${NC}"
124
+ return 0
125
+ fi
126
+
127
+ echo ""
128
+ echo -e "${YELLOW}Installing missing dependencies: ${DEPS_TO_INSTALL[*]}${NC}"
129
+
130
+ case "$PACKAGE_MANAGER" in
131
+ brew)
132
+ # macOS with Homebrew
133
+ if ! command -v brew &> /dev/null; then
134
+ echo -e "${YELLOW}Homebrew not found. Installing Homebrew...${NC}"
135
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
136
+ fi
137
+ for dep in "${DEPS_TO_INSTALL[@]}"; do
138
+ case "$dep" in
139
+ nodejs) brew install node ;;
140
+ npm) ;; # Comes with node
141
+ *) brew install "$dep" ;;
142
+ esac
143
+ done
144
+ ;;
145
+ apt)
146
+ # Debian/Ubuntu
147
+ if $NEEDS_SUDO; then
148
+ apt-get update
149
+ for dep in "${DEPS_TO_INSTALL[@]}"; do
150
+ case "$dep" in
151
+ nodejs)
152
+ # Use NodeSource for newer Node.js
153
+ curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
154
+ apt-get install -y nodejs
155
+ ;;
156
+ npm) ;; # Comes with nodejs from NodeSource
157
+ *) apt-get install -y "$dep" ;;
158
+ esac
159
+ done
160
+ else
161
+ echo -e "${RED}Cannot install system packages without sudo.${NC}"
162
+ echo "Please install manually: sudo apt install ${DEPS_TO_INSTALL[*]}"
163
+ exit 1
164
+ fi
165
+ ;;
166
+ dnf|yum)
167
+ # RHEL/CentOS/Fedora
168
+ if $NEEDS_SUDO; then
169
+ for dep in "${DEPS_TO_INSTALL[@]}"; do
170
+ case "$dep" in
171
+ nodejs)
172
+ curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
173
+ $PACKAGE_MANAGER install -y nodejs
174
+ ;;
175
+ npm) ;; # Comes with nodejs
176
+ *) $PACKAGE_MANAGER install -y "$dep" ;;
177
+ esac
178
+ done
179
+ else
180
+ echo -e "${RED}Cannot install system packages without sudo.${NC}"
181
+ echo "Please install manually: sudo $PACKAGE_MANAGER install ${DEPS_TO_INSTALL[*]}"
182
+ exit 1
183
+ fi
184
+ ;;
185
+ pacman)
186
+ # Arch Linux
187
+ if $NEEDS_SUDO; then
188
+ pacman -Sy --noconfirm "${DEPS_TO_INSTALL[@]}"
189
+ else
190
+ echo -e "${RED}Cannot install system packages without sudo.${NC}"
191
+ echo "Please install manually: sudo pacman -S ${DEPS_TO_INSTALL[*]}"
192
+ exit 1
193
+ fi
194
+ ;;
195
+ apk)
196
+ # Alpine
197
+ if $NEEDS_SUDO; then
198
+ apk add --no-cache "${DEPS_TO_INSTALL[@]}"
199
+ else
200
+ echo -e "${RED}Cannot install system packages without sudo.${NC}"
201
+ echo "Please install manually: sudo apk add ${DEPS_TO_INSTALL[*]}"
202
+ exit 1
203
+ fi
204
+ ;;
205
+ *)
206
+ echo -e "${RED}Unknown package manager. Please install manually:${NC}"
207
+ echo " - Node.js 18+ (https://nodejs.org)"
208
+ echo " - tmux"
209
+ echo " - git"
210
+ echo " - openssl"
211
+ exit 1
212
+ ;;
213
+ esac
214
+
215
+ echo -e "${GREEN}Dependencies installed!${NC}"
216
+ }
217
+
218
+ # Find or download the source code
219
+ setup_source() {
220
+ echo ""
221
+ echo -e "${BLUE}Setting up source code...${NC}"
222
+
223
+ # Check if we're already in the daemon directory
224
+ if [[ -f "./package.json" ]] && grep -q "companion-daemon" "./package.json" 2>/dev/null; then
225
+ SOURCE_DIR="$(pwd)"
226
+ echo -e " ${GREEN}✓${NC} Using current directory: $SOURCE_DIR"
227
+ elif [[ -f "../daemon/package.json" ]] && grep -q "companion-daemon" "../daemon/package.json" 2>/dev/null; then
228
+ SOURCE_DIR="$(cd ../daemon && pwd)"
229
+ echo -e " ${GREEN}✓${NC} Using parent daemon directory: $SOURCE_DIR"
230
+ else
231
+ # Need to clone
232
+ echo -e " ${YELLOW}Source not found locally. Cloning repository...${NC}"
233
+ TEMP_DIR=$(mktemp -d)
234
+ git clone --depth 1 https://github.com/Hexidecibel/companion.git "$TEMP_DIR/companion"
235
+ SOURCE_DIR="$TEMP_DIR/companion/daemon"
236
+ CLEANUP_TEMP=true
237
+ fi
238
+
239
+ cd "$SOURCE_DIR"
240
+ }
241
+
242
+ # Build the daemon
243
+ build_daemon() {
244
+ echo ""
245
+ echo -e "${BLUE}Building daemon...${NC}"
246
+
247
+ cd "$SOURCE_DIR"
248
+
249
+ # Check if already built
250
+ if [[ -f "./dist/index.js" ]]; then
251
+ echo -e " ${GREEN}✓${NC} Build already exists"
252
+
253
+ # Check if source is newer than build
254
+ if [[ "./src/index.ts" -nt "./dist/index.js" ]]; then
255
+ echo -e " ${YELLOW}Source is newer than build, rebuilding...${NC}"
256
+ npm install
257
+ npm run build
258
+ fi
259
+ else
260
+ echo -e " Installing npm dependencies..."
261
+ npm install
262
+
263
+ echo -e " Compiling TypeScript..."
264
+ npm run build
265
+ fi
266
+
267
+ if [[ ! -f "./dist/index.js" ]]; then
268
+ echo -e "${RED}Build failed! dist/index.js not found${NC}"
269
+ exit 1
270
+ fi
271
+
272
+ echo -e " ${GREEN}✓${NC} Build complete"
273
+ }
274
+
275
+ # Install to target directory
276
+ install_files() {
277
+ echo ""
278
+ echo -e "${BLUE}Installing files...${NC}"
279
+
280
+ # Create directories
281
+ mkdir -p "$INSTALL_DIR"
282
+ mkdir -p "$CONFIG_DIR"
283
+ mkdir -p "$CONFIG_DIR/certs"
284
+ chmod 700 "$CONFIG_DIR/certs"
285
+
286
+ # Copy files
287
+ cp -r "$SOURCE_DIR/dist" "$INSTALL_DIR/"
288
+ cp "$SOURCE_DIR/package.json" "$INSTALL_DIR/"
289
+ cp "$SOURCE_DIR/package-lock.json" "$INSTALL_DIR/" 2>/dev/null || true
290
+
291
+ # Install production dependencies
292
+ cd "$INSTALL_DIR"
293
+ npm install --production --silent
294
+
295
+ echo -e " ${GREEN}✓${NC} Files installed to $INSTALL_DIR"
296
+ }
297
+
298
+ # Generate or preserve config
299
+ setup_config() {
300
+ echo ""
301
+ echo -e "${BLUE}Setting up configuration...${NC}"
302
+
303
+ CONFIG_FILE="$CONFIG_DIR/config.json"
304
+
305
+ if [[ -f "$CONFIG_FILE" ]]; then
306
+ echo -e " ${GREEN}✓${NC} Config file already exists"
307
+ TOKEN=$(grep -o '"token": *"[^"]*"' "$CONFIG_FILE" | cut -d'"' -f4)
308
+ else
309
+ # Generate new token
310
+ TOKEN=$(openssl rand -hex 32)
311
+
312
+ cat << EOF > "$CONFIG_FILE"
313
+ {
314
+ "port": 9877,
315
+ "token": "$TOKEN",
316
+ "tls": true,
317
+ "cert_path": "$CONFIG_DIR/certs/cert.pem",
318
+ "key_path": "$CONFIG_DIR/certs/key.pem",
319
+ "tmux_session": "companion",
320
+ "code_home": "$ACTUAL_HOME/.claude",
321
+ "mdns_enabled": true,
322
+ "push_delay_ms": 60000
323
+ }
324
+ EOF
325
+
326
+ echo -e " ${GREEN}✓${NC} Config file created"
327
+ fi
328
+ }
329
+
330
+ # Setup service (systemd or launchd)
331
+ setup_service() {
332
+ echo ""
333
+ echo -e "${BLUE}Setting up service...${NC}"
334
+
335
+ if [[ "$OS" == "macos" ]]; then
336
+ setup_launchd
337
+ else
338
+ setup_systemd
339
+ fi
340
+ }
341
+
342
+ # macOS launchd setup
343
+ setup_launchd() {
344
+ PLIST_PATH="$HOME/Library/LaunchAgents/com.companion.daemon.plist"
345
+ mkdir -p "$HOME/Library/LaunchAgents"
346
+
347
+ cat << EOF > "$PLIST_PATH"
348
+ <?xml version="1.0" encoding="UTF-8"?>
349
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
350
+ <plist version="1.0">
351
+ <dict>
352
+ <key>Label</key>
353
+ <string>com.companion.daemon</string>
354
+ <key>ProgramArguments</key>
355
+ <array>
356
+ <string>/usr/local/bin/node</string>
357
+ <string>$INSTALL_DIR/dist/index.js</string>
358
+ </array>
359
+ <key>EnvironmentVariables</key>
360
+ <dict>
361
+ <key>CONFIG_PATH</key>
362
+ <string>$CONFIG_DIR/config.json</string>
363
+ <key>NODE_ENV</key>
364
+ <string>production</string>
365
+ </dict>
366
+ <key>WorkingDirectory</key>
367
+ <string>$INSTALL_DIR</string>
368
+ <key>RunAtLoad</key>
369
+ <true/>
370
+ <key>KeepAlive</key>
371
+ <true/>
372
+ <key>StandardOutPath</key>
373
+ <string>$HOME/Library/Logs/companion.log</string>
374
+ <key>StandardErrorPath</key>
375
+ <string>$HOME/Library/Logs/companion.error.log</string>
376
+ </dict>
377
+ </plist>
378
+ EOF
379
+
380
+ # Find node path
381
+ NODE_PATH=$(which node)
382
+ sed -i '' "s|/usr/local/bin/node|$NODE_PATH|g" "$PLIST_PATH"
383
+
384
+ # Load the service
385
+ launchctl unload "$PLIST_PATH" 2>/dev/null || true
386
+ launchctl load "$PLIST_PATH"
387
+
388
+ echo -e " ${GREEN}✓${NC} LaunchAgent created and loaded"
389
+
390
+ SERVICE_TYPE="launchd"
391
+ }
392
+
393
+ # Linux systemd setup
394
+ setup_systemd() {
395
+ if ! command -v systemctl &> /dev/null; then
396
+ echo -e " ${YELLOW}systemd not found, skipping service setup${NC}"
397
+ echo -e " You can run manually: node $INSTALL_DIR/dist/index.js"
398
+ SERVICE_TYPE="manual"
399
+ return
400
+ fi
401
+
402
+ if $NEEDS_SUDO; then
403
+ SERVICE_FILE="/etc/systemd/system/companion.service"
404
+ else
405
+ mkdir -p "$HOME/.config/systemd/user"
406
+ SERVICE_FILE="$HOME/.config/systemd/user/companion.service"
407
+ fi
408
+
409
+ cat << EOF > "$SERVICE_FILE"
410
+ [Unit]
411
+ Description=Companion Daemon
412
+ After=network.target
413
+
414
+ [Service]
415
+ Type=simple
416
+ WorkingDirectory=$INSTALL_DIR
417
+ ExecStart=$(which node) $INSTALL_DIR/dist/index.js
418
+ Restart=always
419
+ RestartSec=5
420
+ Environment=CONFIG_PATH=$CONFIG_DIR/config.json
421
+ Environment=NODE_ENV=production
422
+
423
+ [Install]
424
+ WantedBy=default.target
425
+ EOF
426
+
427
+ if $NEEDS_SUDO; then
428
+ systemctl daemon-reload
429
+ systemctl enable companion
430
+ systemctl start companion
431
+ echo -e " ${GREEN}✓${NC} Systemd service created (system-wide)"
432
+ else
433
+ systemctl --user daemon-reload
434
+ systemctl --user enable companion
435
+ systemctl --user start companion
436
+ echo -e " ${GREEN}✓${NC} Systemd service created (user-level)"
437
+ fi
438
+
439
+ SERVICE_TYPE="systemd"
440
+ }
441
+
442
+ # Verify installation
443
+ verify_installation() {
444
+ echo ""
445
+ echo -e "${BLUE}Verifying installation...${NC}"
446
+
447
+ sleep 2
448
+
449
+ # Check if process is running
450
+ if pgrep -f "node.*companion" > /dev/null; then
451
+ echo -e " ${GREEN}✓${NC} Daemon is running"
452
+ else
453
+ echo -e " ${RED}✗${NC} Daemon is not running"
454
+ echo ""
455
+ echo "Check logs:"
456
+ if [[ "$OS" == "macos" ]]; then
457
+ echo " cat ~/Library/Logs/companion.log"
458
+ echo " cat ~/Library/Logs/companion.error.log"
459
+ elif [[ "$SERVICE_TYPE" == "systemd" ]]; then
460
+ if $NEEDS_SUDO; then
461
+ echo " sudo journalctl -u companion -e"
462
+ else
463
+ echo " journalctl --user -u companion -e"
464
+ fi
465
+ fi
466
+ return 1
467
+ fi
468
+
469
+ # Check if port is listening
470
+ if command -v lsof &> /dev/null; then
471
+ if lsof -i :9877 > /dev/null 2>&1; then
472
+ echo -e " ${GREEN}✓${NC} Listening on port 9877"
473
+ fi
474
+ elif command -v ss &> /dev/null; then
475
+ if ss -tln | grep -q ":9877"; then
476
+ echo -e " ${GREEN}✓${NC} Listening on port 9877"
477
+ fi
478
+ fi
479
+
480
+ return 0
481
+ }
482
+
483
+ # Print success message
484
+ print_success() {
485
+ echo ""
486
+ echo -e "${GREEN}"
487
+ echo "╔══════════════════════════════════════════════════════════════╗"
488
+ echo "║ Installation Complete! ║"
489
+ echo "╚══════════════════════════════════════════════════════════════╝"
490
+ echo -e "${NC}"
491
+ echo ""
492
+ echo -e "${GREEN}Your authentication token:${NC}"
493
+ echo -e " ${YELLOW}$TOKEN${NC}"
494
+ echo ""
495
+ echo -e "${GREEN}Add this to your Companion mobile app.${NC}"
496
+ echo ""
497
+ echo -e "${BLUE}Service commands:${NC}"
498
+
499
+ if [[ "$OS" == "macos" ]]; then
500
+ echo " View logs: tail -f ~/Library/Logs/companion.log"
501
+ echo " Restart: launchctl kickstart -k gui/\$(id -u)/com.companion.daemon"
502
+ echo " Stop: launchctl unload ~/Library/LaunchAgents/com.companion.daemon.plist"
503
+ echo " Start: launchctl load ~/Library/LaunchAgents/com.companion.daemon.plist"
504
+ elif [[ "$SERVICE_TYPE" == "systemd" ]]; then
505
+ if $NEEDS_SUDO; then
506
+ echo " View logs: sudo journalctl -u companion -f"
507
+ echo " Restart: sudo systemctl restart companion"
508
+ echo " Stop: sudo systemctl stop companion"
509
+ echo " Status: sudo systemctl status companion"
510
+ else
511
+ echo " View logs: journalctl --user -u companion -f"
512
+ echo " Restart: systemctl --user restart companion"
513
+ echo " Stop: systemctl --user stop companion"
514
+ echo " Status: systemctl --user status companion"
515
+ fi
516
+ else
517
+ echo " Run manually: CONFIG_PATH=$CONFIG_DIR/config.json node $INSTALL_DIR/dist/index.js"
518
+ fi
519
+
520
+ echo ""
521
+ echo -e "${BLUE}Config file:${NC} $CONFIG_DIR/config.json"
522
+ echo ""
523
+ echo -e "${YELLOW}Next steps:${NC}"
524
+ echo " 1. Make sure tmux is running: tmux new -s companion"
525
+ echo " 2. Start your coding session in tmux"
526
+ echo " 3. Connect from the mobile app using your token"
527
+ }
528
+
529
+ # Cleanup
530
+ cleanup() {
531
+ if [[ "${CLEANUP_TEMP:-false}" == "true" ]] && [[ -n "${TEMP_DIR:-}" ]]; then
532
+ rm -rf "$TEMP_DIR"
533
+ fi
534
+ }
535
+
536
+ trap cleanup EXIT
537
+
538
+ # Main installation flow
539
+ main() {
540
+ detect_os
541
+ check_permissions
542
+ install_dependencies
543
+ setup_source
544
+ build_daemon
545
+ install_files
546
+ setup_config
547
+ setup_service
548
+
549
+ if verify_installation; then
550
+ print_success
551
+ else
552
+ echo ""
553
+ echo -e "${YELLOW}Installation completed with warnings. Please check the logs.${NC}"
554
+ echo -e "Token: ${YELLOW}$TOKEN${NC}"
555
+ fi
556
+ }
557
+
558
+ main "$@"
@@ -0,0 +1,113 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Colors
5
+ RED='\033[0;31m'
6
+ GREEN='\033[0;32m'
7
+ YELLOW='\033[1;33m'
8
+ BLUE='\033[0;34m'
9
+ NC='\033[0m'
10
+
11
+ echo -e "${BLUE}"
12
+ echo "╔══════════════════════════════════════════════════════════════╗"
13
+ echo "║ Companion Daemon Uninstaller ║"
14
+ echo "╚══════════════════════════════════════════════════════════════╝"
15
+ echo -e "${NC}"
16
+
17
+ # Detect OS
18
+ if [[ "$OSTYPE" == "darwin"* ]]; then
19
+ OS="macos"
20
+ else
21
+ OS="linux"
22
+ fi
23
+
24
+ # Confirm
25
+ echo -e "${YELLOW}This will remove Companion daemon.${NC}"
26
+ echo ""
27
+ read -p "Continue? (y/N) " -n 1 -r
28
+ echo
29
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
30
+ echo "Cancelled."
31
+ exit 0
32
+ fi
33
+
34
+ echo ""
35
+ echo -e "${BLUE}Stopping service...${NC}"
36
+
37
+ if [[ "$OS" == "macos" ]]; then
38
+ # macOS: unload launchd
39
+ PLIST_PATH="$HOME/Library/LaunchAgents/com.companion.daemon.plist"
40
+ if [[ -f "$PLIST_PATH" ]]; then
41
+ launchctl unload "$PLIST_PATH" 2>/dev/null || true
42
+ rm -f "$PLIST_PATH"
43
+ echo -e " ${GREEN}✓${NC} LaunchAgent removed"
44
+ fi
45
+ else
46
+ # Linux: stop systemd
47
+ if systemctl --user is-active companion &>/dev/null; then
48
+ systemctl --user stop companion
49
+ systemctl --user disable companion
50
+ rm -f "$HOME/.config/systemd/user/companion.service"
51
+ systemctl --user daemon-reload
52
+ echo -e " ${GREEN}✓${NC} User systemd service removed"
53
+ elif systemctl is-active companion &>/dev/null 2>&1; then
54
+ if [ "$EUID" -eq 0 ]; then
55
+ systemctl stop companion
56
+ systemctl disable companion
57
+ rm -f /etc/systemd/system/companion.service
58
+ systemctl daemon-reload
59
+ echo -e " ${GREEN}✓${NC} System systemd service removed"
60
+ else
61
+ echo -e " ${YELLOW}System service found. Run with sudo to remove.${NC}"
62
+ fi
63
+ fi
64
+ fi
65
+
66
+ # Kill any running processes
67
+ pkill -f "node.*companion" 2>/dev/null || true
68
+
69
+ echo ""
70
+ echo -e "${BLUE}Removing files...${NC}"
71
+
72
+ # Remove install directories
73
+ DIRS_TO_CHECK=(
74
+ "$HOME/.companion"
75
+ "/opt/companion"
76
+ )
77
+
78
+ for dir in "${DIRS_TO_CHECK[@]}"; do
79
+ if [[ -d "$dir" ]]; then
80
+ rm -rf "$dir"
81
+ echo -e " ${GREEN}✓${NC} Removed $dir"
82
+ fi
83
+ done
84
+
85
+ # Ask about config
86
+ echo ""
87
+ read -p "Remove configuration and certificates? (y/N) " -n 1 -r
88
+ echo
89
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
90
+ CONFIG_DIRS=(
91
+ "$HOME/.companion"
92
+ "/etc/companion"
93
+ )
94
+ for dir in "${CONFIG_DIRS[@]}"; do
95
+ if [[ -d "$dir" ]]; then
96
+ if [[ "$dir" == "/etc/companion" ]] && [ "$EUID" -ne 0 ]; then
97
+ echo -e " ${YELLOW}Cannot remove $dir without sudo${NC}"
98
+ else
99
+ rm -rf "$dir"
100
+ echo -e " ${GREEN}✓${NC} Removed $dir"
101
+ fi
102
+ fi
103
+ done
104
+ fi
105
+
106
+ # Remove logs on macOS
107
+ if [[ "$OS" == "macos" ]]; then
108
+ rm -f "$HOME/Library/Logs/companion.log" 2>/dev/null
109
+ rm -f "$HOME/Library/Logs/companion.error.log" 2>/dev/null
110
+ fi
111
+
112
+ echo ""
113
+ echo -e "${GREEN}Companion daemon has been uninstalled.${NC}"