ghosttrap-cli 0.3.12__tar.gz → 0.3.14__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ghosttrap-cli
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: Watch for errors streaming from ghosttrap.io
5
5
  Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
6
6
  Requires-Python: >=3.10
@@ -52,6 +52,7 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
52
52
  | `ghosttrap last --clear` | Fetch the most recent error and skip everything older |
53
53
  | `ghosttrap watch` | Stream all errors continuously |
54
54
  | `ghosttrap clear` | Skip all outstanding errors |
55
+ | `ghosttrap nuke` | Permanently delete every server-side row for the current repo (errors + token). Requires typed confirmation. |
55
56
 
56
57
  ## How it works
57
58
 
@@ -43,6 +43,7 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
43
43
  | `ghosttrap last --clear` | Fetch the most recent error and skip everything older |
44
44
  | `ghosttrap watch` | Stream all errors continuously |
45
45
  | `ghosttrap clear` | Skip all outstanding errors |
46
+ | `ghosttrap nuke` | Permanently delete every server-side row for the current repo (errors + token). Requires typed confirmation. |
46
47
 
47
48
  ## How it works
48
49
 
@@ -20,9 +20,10 @@ KNOWN_SKILL_HASHES = {
20
20
  "0651bb4247cf5c68960ff5b63d6a5d0c85ff1ce08e7966ab4823601ff02cf1f4", # v0.3.9
21
21
  "38810f43867a2a91420cc3dacbc71d2acabd7125596fd5b43f222b49725c9696", # v0.3.10
22
22
  "19b67d913dc5214ee4db3610bd8749da67324c174b904b5da71ee6de13e23e63", # v0.3.11
23
+ "bf7768c3de266b7018d5c722c6c9991b487e7897786b3a406c460842cdcde8b5", # v0.3.12
23
24
  }
24
25
 
25
- __version__ = "0.3.12"
26
+ __version__ = "0.3.14"
26
27
 
27
28
  GHOSTTRAP_SERVER = "wss://ghosttrap.io/stream/"
28
29
  CONFIG_DIR = os.path.expanduser("~/.ghosttrap")
@@ -82,6 +83,7 @@ Read `~/.ghosttrap/config.json` for state. It contains:
82
83
 
83
84
  - `ghosttrap last` — fetch the single most recent error and exit immediately, no waiting. Useful when the user wants to look at the latest error without starting a watch. Add `--clear` to also skip everything older in one shot.
84
85
  - `ghosttrap clear` — manually skip outstanding errors without waiting. Useful if the user explicitly wants to drop the queue.
86
+ - `ghosttrap nuke` — permanently delete every server-side row for the current repo (errors + the Repo row + its token). Requires the user to type the repo name `owner/name` to confirm. Only run if the user explicitly asks to wipe server data — never proactively. After it succeeds the token is dead; the user would need to `ghosttrap setup` again to use this repo.
85
87
 
86
88
  ## Rules
87
89
 
@@ -223,6 +225,10 @@ async def _connect_and_handle(server_url, token, config, once=False):
223
225
  async for message in ws:
224
226
  event = json.loads(message)
225
227
 
228
+ if event.get("type") == "rejected":
229
+ print(f"error: {event.get('message', event.get('code', 'rejected by server'))}", file=sys.stderr)
230
+ sys.exit(1)
231
+
226
232
  if event.get("type") == "subscribed":
227
233
  repos = event.get("repos", [])
228
234
  print(f"watching {len(repos)} repo(s)", file=sys.stderr)
@@ -314,8 +320,12 @@ async def setup(server_url, token):
314
320
  message = await asyncio.wait_for(ws.recv(), timeout=30)
315
321
  event = json.loads(message)
316
322
 
323
+ if event.get("type") == "rejected":
324
+ print(f"error: {event.get('message', event.get('code', 'rejected by server'))}", file=sys.stderr)
325
+ sys.exit(1)
326
+
317
327
  if event.get("type") != "subscribed":
318
- print("error: unexpected response from server", file=sys.stderr)
328
+ print(f"error: unexpected response from server: {event}", file=sys.stderr)
319
329
  sys.exit(1)
320
330
 
321
331
  repos = event.get("repos", [])
@@ -332,6 +342,8 @@ async def setup(server_url, token):
332
342
 
333
343
  print("done — Claude Code will take it from here\n", file=sys.stderr)
334
344
 
345
+ except SystemExit:
346
+ raise
335
347
  except Exception as e:
336
348
  print(f"error: {e}", file=sys.stderr)
337
349
  sys.exit(1)
@@ -390,6 +402,59 @@ def clear():
390
402
  sys.exit(1)
391
403
 
392
404
 
405
+ def nuke():
406
+ _require_setup()
407
+ config = _load_config()
408
+ repos = config.get("repos", {})
409
+ cwd_repo = _detect_repo_from_cwd()
410
+ if not cwd_repo:
411
+ print("error: not in a git repo with a github remote", file=sys.stderr)
412
+ sys.exit(1)
413
+
414
+ entry_key = None
415
+ entry = None
416
+ for k, e in repos.items():
417
+ if f"{e.get('owner')}/{e.get('name')}" == cwd_repo:
418
+ entry_key, entry = k, e
419
+ break
420
+ if entry is None:
421
+ print(f"error: {cwd_repo} is not in your config. run 'ghosttrap setup' to claim it first.", file=sys.stderr)
422
+ sys.exit(1)
423
+
424
+ canonical = f"{entry['owner']}/{entry['name']}"
425
+ print(f"\nthis will permanently delete ALL data on the server for {canonical}:", file=sys.stderr)
426
+ print(f" - every Error row for this repo", file=sys.stderr)
427
+ print(f" - the Repo row itself (token will stop working)", file=sys.stderr)
428
+ print(f"\ntype the repo name to confirm: ", file=sys.stderr, end="")
429
+ sys.stderr.flush()
430
+ try:
431
+ typed = input().strip()
432
+ except (EOFError, KeyboardInterrupt):
433
+ print("\naborted", file=sys.stderr)
434
+ sys.exit(1)
435
+ if typed != canonical:
436
+ print("aborted (did not match)", file=sys.stderr)
437
+ sys.exit(1)
438
+
439
+ server = GHOSTTRAP_SERVER.replace("wss://", "https://").replace("/stream/", "")
440
+ url = f"{server}/nuke/{entry['token']}/"
441
+ try:
442
+ req = urllib.request.Request(url, method="DELETE", headers={"User-Agent": "ghosttrap-cli"})
443
+ with urllib.request.urlopen(req, timeout=10) as resp:
444
+ data = json.loads(resp.read())
445
+ except Exception as e:
446
+ print(f"error: {e}", file=sys.stderr)
447
+ sys.exit(1)
448
+
449
+ print(json.dumps(data))
450
+ print(f"\nnuked {data.get('repo')}:", file=sys.stderr)
451
+ print(f" errors deleted: {data.get('errors_deleted')}", file=sys.stderr)
452
+ print(f" repos deleted: {data.get('repo_deleted')}", file=sys.stderr)
453
+
454
+ repos.pop(entry_key, None)
455
+ _save_config(config)
456
+
457
+
393
458
  def last(do_clear=False):
394
459
  _require_setup()
395
460
  config = _load_config()
@@ -447,6 +512,8 @@ def main():
447
512
  last_parser = sub.add_parser("last", help="Fetch the most recent error then exit")
448
513
  last_parser.add_argument("--clear", action="store_true", help="Also skip remaining outstanding errors")
449
514
 
515
+ sub.add_parser("nuke", help="Permanently delete all server data for the current repo")
516
+
450
517
  args = parser.parse_args()
451
518
 
452
519
  if args.command == "setup":
@@ -475,6 +542,8 @@ def main():
475
542
  elif args.command == "last":
476
543
  _refresh_skill_if_stale()
477
544
  last(do_clear=args.clear)
545
+ elif args.command == "nuke":
546
+ nuke()
478
547
  else:
479
548
  parser.print_help()
480
549
  sys.exit(1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ghosttrap-cli
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: Watch for errors streaming from ghosttrap.io
5
5
  Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
6
6
  Requires-Python: >=3.10
@@ -52,6 +52,7 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
52
52
  | `ghosttrap last --clear` | Fetch the most recent error and skip everything older |
53
53
  | `ghosttrap watch` | Stream all errors continuously |
54
54
  | `ghosttrap clear` | Skip all outstanding errors |
55
+ | `ghosttrap nuke` | Permanently delete every server-side row for the current repo (errors + token). Requires typed confirmation. |
55
56
 
56
57
  ## How it works
57
58
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ghosttrap-cli"
7
- version = "0.3.12"
7
+ version = "0.3.14"
8
8
  description = "Watch for errors streaming from ghosttrap.io"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes