greptile 2.3.0 → 2.4.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/build-app.sh CHANGED
@@ -45,7 +45,7 @@ TEMP_SCRIPT="$(mktemp /tmp/greptile-fix-XXXXXX.applescript)"
45
45
  trap 'rm -f "$TEMP_SCRIPT"' EXIT
46
46
 
47
47
  # Escape sed special characters in the directory path (& \ /)
48
- ESCAPED_DIR="$(printf '%s' "$GREPTILE_FIX_DIR" | sed 's/[&/\]/\\&/g')"
48
+ ESCAPED_DIR="$(printf '%s' "$GREPTILE_FIX_DIR" | sed 's/[&\\/]/\\&/g')"
49
49
  sed "s|PATH=/opt/homebrew/bin:/usr/local/bin:|PATH=$ESCAPED_DIR:/opt/homebrew/bin:/usr/local/bin:|" \
50
50
  "$SCRIPT_DIR/greptile-fix.applescript" > "$TEMP_SCRIPT"
51
51
 
package/greptile-fix CHANGED
@@ -187,6 +187,7 @@ esac
187
187
  # Parse URL parameters
188
188
  PROMPT=$(parse_url_param "$URL" "prompt")
189
189
  REPO=$(parse_url_param "$URL" "repo")
190
+ ACK_ID=$(parse_url_param "$URL" "ack")
190
191
 
191
192
  if [ -z "$PROMPT" ]; then
192
193
  log "ERROR: No prompt parameter in URL"
@@ -234,5 +235,9 @@ log "Runner script: $RUNNER"
234
235
  # Print the runner path to stdout — the AppleScript app reads this
235
236
  # and uses it to open Terminal (which requires Automation permission
236
237
  # that only the .app bundle has).
238
+ # Line 1: runner path, Line 2 (optional): ACK ID for auto-redirect
237
239
  echo "$RUNNER"
240
+ if [ -n "$ACK_ID" ]; then
241
+ echo "$ACK_ID"
242
+ fi
238
243
  log "Done"
@@ -12,8 +12,16 @@ on open location theURL
12
12
  try
13
13
  -- Ensure log directory exists
14
14
  do shell script "mkdir -p $HOME/.cache/greptile"
15
- -- Call greptile-fix to parse URL and create runner script; it prints the runner path to stdout
16
- set runnerPath to do shell script "PATH=/opt/homebrew/bin:/usr/local/bin:$PATH greptile-fix " & quoted form of theURL & " 2>> $HOME/.cache/greptile/greptile-fix.log"
15
+ -- Call greptile-fix to parse URL and create runner script
16
+ -- Output: line 1 = runner path, line 2 (optional) = ACK ID for auto-redirect
17
+ set fixOutput to do shell script "PATH=/opt/homebrew/bin:/usr/local/bin:$PATH greptile-fix " & quoted form of theURL & " 2>> $HOME/.cache/greptile/greptile-fix.log"
18
+
19
+ set outputLines to paragraphs of fixOutput
20
+ set runnerPath to item 1 of outputLines
21
+ set ackId to ""
22
+ if (count of outputLines) > 1 then
23
+ set ackId to item 2 of outputLines
24
+ end if
17
25
 
18
26
  if runnerPath is not "" then
19
27
  -- Detect terminal by checking running processes via shell (no accessibility permissions needed).
@@ -56,6 +64,13 @@ on open location theURL
56
64
  -- Terminal.app, iTerm, and Warp natively execute scripts via "open -a"
57
65
  do shell script "open -a " & quoted form of termApp & " " & quoted form of runnerPath
58
66
  end if
67
+
68
+ -- Send ACK to health server so the web page can auto-redirect back to the PR
69
+ if ackId is not "" then
70
+ try
71
+ do shell script "curl -s -X POST http://127.0.0.1:4747/ack/" & quoted form of ackId & " --max-time 2 &>/dev/null &"
72
+ end try
73
+ end if
59
74
  end if
60
75
  on error errMsg
61
76
  do shell script "echo '[ERROR] " & quoted form of errMsg & "' >> $HOME/.cache/greptile/greptile-fix.log"
package/health-server.js CHANGED
@@ -95,11 +95,13 @@ function checkForUpdate() {
95
95
  const npmPaths = [
96
96
  '/opt/homebrew/bin/npm',
97
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
- ] : []),
98
+ ...(home
99
+ ? [
100
+ path.join(home, '.volta/bin/npm'),
101
+ path.join(home, '.asdf/shims/npm'),
102
+ path.join(home, '.local/bin/npm'),
103
+ ]
104
+ : []),
103
105
  ]
104
106
  // Also check nvm: find the current default node version's npm
105
107
  if (home) {
@@ -173,12 +175,26 @@ function checkForUpdate() {
173
175
  })
174
176
  }
175
177
 
178
+ // --- ACK store for "Fix in X" auto-redirect ---
179
+ const ackStore = new Map() // id → timestamp
180
+ const ACK_TTL_MS = 60_000
181
+
182
+ function pruneAcks() {
183
+ const now = Date.now()
184
+ for (const [id, ts] of ackStore) {
185
+ if (now - ts > ACK_TTL_MS) ackStore.delete(id)
186
+ }
187
+ }
188
+ setInterval(pruneAcks, 30_000)
189
+
190
+ const ACK_PATH_RE = /^\/ack\/([a-zA-Z0-9_-]+)$/
191
+
176
192
  const ALLOWED_ORIGINS = ['https://app.greptile.com', 'https://app.staging.greptile.com', 'http://localhost:3000']
177
193
 
178
194
  function isAllowedOrigin(origin) {
179
195
  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
196
+ // Allow Vercel preview deployments (e.g. https://greptilia-abc123.vercel.app)
197
+ if (/^https:\/\/greptilia-[a-z0-9-]+\.vercel\.app$/.test(origin)) return true
182
198
  return false
183
199
  }
184
200
 
@@ -188,7 +204,7 @@ const server = http.createServer((req, res) => {
188
204
 
189
205
  if (allowed) {
190
206
  res.setHeader('Access-Control-Allow-Origin', origin)
191
- res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
207
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
192
208
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
193
209
  res.setHeader('Access-Control-Allow-Private-Network', 'true')
194
210
  }
@@ -205,6 +221,26 @@ const server = http.createServer((req, res) => {
205
221
  return
206
222
  }
207
223
 
224
+ // ACK endpoints for "Fix in X" auto-redirect
225
+ const urlPath = (req.url || '').split('?')[0]
226
+ const ackMatch = urlPath.match(ACK_PATH_RE)
227
+ if (ackMatch) {
228
+ const id = ackMatch[1]
229
+ if (req.method === 'POST') {
230
+ ackStore.set(id, Date.now())
231
+ res.writeHead(200, { 'Content-Type': 'application/json' })
232
+ res.end(JSON.stringify({ ok: true }))
233
+ return
234
+ }
235
+ if (req.method === 'GET') {
236
+ const acked = ackStore.has(id)
237
+ if (acked) ackStore.delete(id)
238
+ res.writeHead(200, { 'Content-Type': 'application/json' })
239
+ res.end(JSON.stringify({ acked }))
240
+ return
241
+ }
242
+ }
243
+
208
244
  res.writeHead(404)
209
245
  res.end()
210
246
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greptile",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Bridge for Greptile code review 'Fix in Claude Code' and 'Fix in Codex' links",
5
5
  "bin": {
6
6
  "greptile-fix": "bin/greptile-fix.js"