greptile 2.3.0 → 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.
package/health-server.js DELETED
@@ -1,228 +0,0 @@
1
- #!/usr/bin/env node
2
- const http = require('http')
3
- const https = require('https')
4
- const fs = require('fs')
5
- const path = require('path')
6
- const { spawn } = require('child_process')
7
-
8
- const PORT = 4747
9
-
10
- /**
11
- * Compare two semver strings. Returns true if b is newer than a.
12
- * Handles x.y.z format; ignores pre-release suffixes.
13
- */
14
- function isNewerVersion(current, latest) {
15
- const parse = (v) => (v || '').replace(/^v/, '').split('-')[0].split('.').map(Number)
16
- const a = parse(current)
17
- const b = parse(latest)
18
- for (let i = 0; i < 3; i++) {
19
- if ((b[i] || 0) > (a[i] || 0)) return true
20
- if ((b[i] || 0) < (a[i] || 0)) return false
21
- }
22
- return false
23
- }
24
-
25
- // Self-destruct: if the package has been uninstalled, clean up and exit.
26
- // Checks that package.json next to this script still exists.
27
- // Note: we intentionally don't check `command -v greptile-fix` because
28
- // launchd runs with a minimal PATH that doesn't include npm global bin.
29
- const PACKAGE_MARKER = path.join(__dirname, 'package.json')
30
- const SELF_CHECK_INTERVAL_MS = 30_000
31
- const UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000 // 6 hours
32
- let isUpdating = false
33
-
34
- function selfCheck() {
35
- // Don't self-destruct during an update — npm temporarily removes the package dir
36
- if (isUpdating) return
37
-
38
- const markerGone = !fs.existsSync(PACKAGE_MARKER)
39
-
40
- if (markerGone) {
41
- console.log('Package uninstalled — cleaning up and exiting.')
42
- const { execSync } = require('child_process')
43
- const home = process.env.HOME || ''
44
- const plistPath = path.join(home, 'Library/LaunchAgents/com.greptile.health.plist')
45
- // Remove .app bundle and plist file
46
- try {
47
- fs.rmSync(path.join(home, 'Applications/Greptile Fix.app'), { recursive: true, force: true })
48
- } catch {}
49
- try {
50
- fs.unlinkSync(plistPath)
51
- } catch {}
52
- server.close()
53
- // Remove the launchd job by label so KeepAlive won't restart us
54
- try {
55
- execSync('launchctl remove com.greptile.health 2>/dev/null')
56
- } catch {}
57
- process.exit(0)
58
- }
59
- }
60
-
61
- // Auto-update: periodically check npm registry for a newer version and install it.
62
- // After a successful update, the process exits so launchd restarts it with the new code.
63
- function checkForUpdate() {
64
- if (isUpdating) return
65
- let localVersion
66
- try {
67
- const pkg = JSON.parse(fs.readFileSync(PACKAGE_MARKER, 'utf8'))
68
- localVersion = pkg.version
69
- } catch {
70
- return // Can't read local version, skip
71
- }
72
-
73
- const req = https.get('https://registry.npmjs.org/greptile/latest', { timeout: 10_000 }, (res) => {
74
- if (res.statusCode !== 200) {
75
- res.resume()
76
- return
77
- }
78
- let data = ''
79
- const MAX_BODY = 100_000 // 100KB safety limit
80
- res.on('data', (chunk) => {
81
- data += chunk
82
- if (data.length > MAX_BODY) {
83
- req.destroy()
84
- return
85
- }
86
- })
87
- res.on('end', () => {
88
- try {
89
- const latest = JSON.parse(data)
90
- if (latest.version && isNewerVersion(localVersion, latest.version)) {
91
- console.log(`Update available: ${localVersion} → ${latest.version}. Installing...`)
92
- // Resolve npm path — launchd has minimal PATH.
93
- // Check well-known locations including node version managers.
94
- const home = process.env.HOME || ''
95
- const npmPaths = [
96
- '/opt/homebrew/bin/npm',
97
- '/usr/local/bin/npm',
98
- ...(home ? [
99
- path.join(home, '.volta/bin/npm'),
100
- path.join(home, '.asdf/shims/npm'),
101
- path.join(home, '.local/bin/npm'),
102
- ] : []),
103
- ]
104
- // Also check nvm: find the current default node version's npm
105
- if (home) {
106
- try {
107
- const nvmDir = path.join(home, '.nvm/versions/node')
108
- if (fs.existsSync(nvmDir)) {
109
- const versions = fs.readdirSync(nvmDir).sort().reverse()
110
- for (const v of versions) {
111
- const candidate = path.join(nvmDir, v, 'bin/npm')
112
- if (fs.existsSync(candidate)) {
113
- npmPaths.push(candidate)
114
- break
115
- }
116
- }
117
- }
118
- } catch {}
119
- // Also check fnm
120
- try {
121
- const fnmDir = path.join(home, '.local/share/fnm/node-versions')
122
- if (fs.existsSync(fnmDir)) {
123
- const versions = fs.readdirSync(fnmDir).sort().reverse()
124
- for (const v of versions) {
125
- const candidate = path.join(fnmDir, v, 'installation/bin/npm')
126
- if (fs.existsSync(candidate)) {
127
- npmPaths.push(candidate)
128
- break
129
- }
130
- }
131
- }
132
- } catch {}
133
- }
134
- let npmBin = 'npm'
135
- for (const p of npmPaths) {
136
- if (fs.existsSync(p)) {
137
- npmBin = p
138
- break
139
- }
140
- }
141
- isUpdating = true
142
- let child
143
- try {
144
- child = spawn(npmBin, ['install', '-g', 'greptile@latest'], {
145
- stdio: 'inherit',
146
- })
147
- } catch (err) {
148
- isUpdating = false
149
- console.error('Failed to spawn npm:', err.message)
150
- return
151
- }
152
- child.on('close', (code) => {
153
- isUpdating = false
154
- if (code === 0) {
155
- console.log(`Updated to greptile@${latest.version}. Restarting...`)
156
- // Exit so launchd restarts the server with the new code
157
- setTimeout(() => process.exit(0), 2000)
158
- } else {
159
- console.error(`Update failed with exit code ${code}`)
160
- }
161
- })
162
- child.on('error', (err) => {
163
- isUpdating = false
164
- console.error('Update spawn error:', err.message)
165
- })
166
- }
167
- } catch {}
168
- })
169
- })
170
- req.on('error', () => {}) // Silently ignore network errors
171
- req.on('timeout', () => {
172
- req.destroy()
173
- })
174
- }
175
-
176
- const ALLOWED_ORIGINS = ['https://app.greptile.com', 'https://app.staging.greptile.com', 'http://localhost:3000']
177
-
178
- function isAllowedOrigin(origin) {
179
- if (ALLOWED_ORIGINS.includes(origin)) return true
180
- // Allow Vercel preview deployments (e.g. https://greptilia-xyz.vercel.app)
181
- if (/^https:\/\/[a-z0-9-]+\.vercel\.app$/.test(origin)) return true
182
- return false
183
- }
184
-
185
- const server = http.createServer((req, res) => {
186
- const origin = req.headers.origin || ''
187
- const allowed = isAllowedOrigin(origin)
188
-
189
- if (allowed) {
190
- res.setHeader('Access-Control-Allow-Origin', origin)
191
- res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
192
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
193
- res.setHeader('Access-Control-Allow-Private-Network', 'true')
194
- }
195
-
196
- if (req.method === 'OPTIONS') {
197
- res.writeHead(allowed ? 204 : 403)
198
- res.end()
199
- return
200
- }
201
-
202
- if (req.method === 'GET' && req.url === '/health') {
203
- res.writeHead(200, { 'Content-Type': 'application/json' })
204
- res.end(JSON.stringify({ status: 'ok' }))
205
- return
206
- }
207
-
208
- res.writeHead(404)
209
- res.end()
210
- })
211
-
212
- server.on('error', (err) => {
213
- if (err.code === 'EADDRINUSE') {
214
- console.error(`Port ${PORT} is already in use. Exiting.`)
215
- process.exit(0) // Exit cleanly so launchd KeepAlive doesn't restart in a tight loop
216
- }
217
- console.error('Health server error:', err.message)
218
- process.exit(1)
219
- })
220
-
221
- server.listen(PORT, '127.0.0.1', () => {
222
- console.log(`Greptile health server listening on http://127.0.0.1:${PORT}`)
223
- // Start self-check loop after server is up
224
- setInterval(selfCheck, SELF_CHECK_INTERVAL_MS)
225
- // Check for updates on startup and then every 6 hours
226
- checkForUpdate()
227
- setInterval(checkForUpdate, UPDATE_CHECK_INTERVAL_MS)
228
- })
package/postinstall.sh DELETED
@@ -1,77 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # postinstall.sh — Builds and registers the Greptile Fix .app bundle after npm install.
4
- #
5
- # The .app bundle handles the greptile:// URL scheme on macOS.
6
- # It is installed to ~/Applications so it persists independently of npm.
7
-
8
- set -euo pipefail
9
-
10
- # Only run on macOS
11
- if [ "$(uname)" != "Darwin" ]; then
12
- echo "greptile: Skipping .app bundle setup (macOS only)"
13
- exit 0
14
- fi
15
-
16
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
17
- APP_DIR="$HOME/Applications"
18
-
19
- # Ensure ~/Applications exists
20
- mkdir -p "$APP_DIR"
21
-
22
- # Build the .app bundle into ~/Applications
23
- if ! bash "$SCRIPT_DIR/build-app.sh" "$APP_DIR"; then
24
- echo "greptile: Warning — failed to build .app bundle. The greptile:// URL scheme may not work."
25
- echo "greptile: You can retry by running: bash $(printf '%q' "$SCRIPT_DIR/build-app.sh") $(printf '%q' "$APP_DIR")"
26
- fi
27
-
28
- # --- Health server LaunchAgent ---
29
- PLIST_NAME="com.greptile.health.plist"
30
- PLIST_SRC="$SCRIPT_DIR/$PLIST_NAME"
31
- PLIST_DST="$HOME/Library/LaunchAgents/$PLIST_NAME"
32
-
33
- if [ -f "$PLIST_SRC" ]; then
34
- # Ensure LaunchAgents directory exists
35
- mkdir -p "$HOME/Library/LaunchAgents"
36
-
37
- # Resolve the node binary path (check common locations for Apple Silicon and Intel Macs)
38
- NODE_BIN="$(command -v node 2>/dev/null || echo "")"
39
- if [ -z "$NODE_BIN" ]; then
40
- for candidate in /opt/homebrew/bin/node /usr/local/bin/node; do
41
- if [ -x "$candidate" ]; then
42
- NODE_BIN="$candidate"
43
- break
44
- fi
45
- done
46
- fi
47
- if [ -z "$NODE_BIN" ]; then
48
- echo "greptile: Warning — could not find node binary. Health server may not start."
49
- NODE_BIN="/usr/local/bin/node"
50
- fi
51
-
52
- # Ensure log directory exists
53
- mkdir -p "$HOME/.cache/greptile"
54
- chmod 700 "$HOME/.cache/greptile"
55
-
56
- # Create a configured copy with the correct paths
57
- sed -e "s|__PACKAGE_DIR__|$SCRIPT_DIR|g" \
58
- -e "s|__HOME__|$HOME|g" \
59
- -e "s|/usr/local/bin/node|$NODE_BIN|g" \
60
- "$PLIST_SRC" > "$PLIST_DST"
61
-
62
- # Load the agent (unload first in case it's already loaded)
63
- launchctl unload "$PLIST_DST" 2>/dev/null || true
64
- if launchctl load "$PLIST_DST" 2>/dev/null; then
65
- echo "Greptile health server installed and started."
66
- else
67
- echo "greptile: Warning — failed to load health server LaunchAgent."
68
- echo "greptile: You can retry by running: launchctl load $(printf '%q' "$PLIST_DST")"
69
- fi
70
- fi
71
-
72
- echo ""
73
- echo "✓ greptile-cli installed successfully."
74
- echo ""
75
- echo "Repo path mappings are stored in ~/.greptile/repos.json."
76
- echo "The first time you click 'Fix in Claude Code' for a repo,"
77
- echo "you'll be asked to select the local folder for that repo."
package/preuninstall.sh DELETED
@@ -1,23 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # preuninstall.sh — Removes the Greptile Fix .app bundle on npm uninstall.
4
-
5
- # Only run on macOS
6
- if [ "$(uname)" != "Darwin" ]; then
7
- exit 0
8
- fi
9
-
10
- APP_PATH="$HOME/Applications/Greptile Fix.app"
11
-
12
- if [ -d "$APP_PATH" ]; then
13
- rm -rf "$APP_PATH"
14
- fi
15
-
16
- # --- Health server LaunchAgent ---
17
- PLIST_PATH="$HOME/Library/LaunchAgents/com.greptile.health.plist"
18
- if [ -f "$PLIST_PATH" ]; then
19
- launchctl unload "$PLIST_PATH" 2>/dev/null || true
20
- rm -f "$PLIST_PATH"
21
- fi
22
-
23
- echo "✓ greptile-cli uninstalled successfully."