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.
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/PKG-INFO +2 -1
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/README.md +1 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli/cli.py +71 -2
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli.egg-info/PKG-INFO +2 -1
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/pyproject.toml +1 -1
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli/__init__.py +0 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli.egg-info/SOURCES.txt +0 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli.egg-info/dependency_links.txt +0 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli.egg-info/entry_points.txt +0 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli.egg-info/requires.txt +0 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/ghosttrap_cli.egg-info/top_level.txt +0 -0
- {ghosttrap_cli-0.3.12 → ghosttrap_cli-0.3.14}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghosttrap-cli
|
|
3
|
-
Version: 0.3.
|
|
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.
|
|
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.
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|