ghosttrap-cli 0.3.14__tar.gz → 0.3.16__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.14 → ghosttrap_cli-0.3.16}/PKG-INFO +11 -4
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/README.md +10 -3
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli/cli.py +31 -17
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli.egg-info/PKG-INFO +11 -4
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/pyproject.toml +1 -1
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli/__init__.py +0 -0
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli.egg-info/SOURCES.txt +0 -0
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli.egg-info/dependency_links.txt +0 -0
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli.egg-info/entry_points.txt +0 -0
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli.egg-info/requires.txt +0 -0
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/ghosttrap_cli.egg-info/top_level.txt +0 -0
- {ghosttrap_cli-0.3.14 → ghosttrap_cli-0.3.16}/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.16
|
|
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
|
|
@@ -54,12 +54,15 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
|
|
|
54
54
|
| `ghosttrap clear` | Skip all outstanding errors |
|
|
55
55
|
| `ghosttrap nuke` | Permanently delete every server-side row for the current repo (errors + token). Requires typed confirmation. |
|
|
56
56
|
|
|
57
|
+
`peek`, `watch`, `last`, and `clear` accept `--repo owner/name` to target a specific claimed repo when you're not inside its working tree (e.g. `ghosttrap peek --repo alex-rowley/ghosttrap-cli`). Otherwise they detect the repo from cwd. `nuke` is intentionally cwd-locked.
|
|
58
|
+
|
|
57
59
|
## How it works
|
|
58
60
|
|
|
59
|
-
- **Setup** authenticates with GitHub to prove you
|
|
61
|
+
- **Setup** authenticates with GitHub (via the active `gh` account) to prove you have access to the repo, then saves a repo token locally. If your active `gh` account can't see the repo, setup fails with a clear message; switch with `gh auth switch` and retry.
|
|
60
62
|
- **Peek** and **watch** connect to ghosttrap.io using that token — no GitHub auth needed after setup
|
|
61
63
|
- Errors that arrive while you're offline are replayed on next connect (cursor-based, no duplicates)
|
|
62
|
-
-
|
|
64
|
+
- Repos are tracked by GitHub's immutable repo id, so a rename or transfer doesn't require any action — the next connect picks up the new `owner/name` and your token keeps working
|
|
65
|
+
- Local state is stored in `~/.ghosttrap/config.json`, keyed by GitHub repo id
|
|
63
66
|
|
|
64
67
|
## Requirements
|
|
65
68
|
|
|
@@ -70,4 +73,8 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
|
|
|
70
73
|
|
|
71
74
|
## Privacy
|
|
72
75
|
|
|
73
|
-
Error data (tracebacks, exception messages, file paths) is routed through ghosttrap.io. The server is not open source yet — if there's demand for self-hosting, we'll open it up. Your GitHub token is used only during `setup` to verify repo
|
|
76
|
+
Error data (tracebacks, exception messages, file paths) is routed through ghosttrap.io. The server is not open source yet — if there's demand for self-hosting, we'll open it up. Your GitHub token is used only during `setup` to verify repo access; it's never stored on the server. After setup, all communication uses a repo-specific token that grants access only to that repo's error stream — it cannot access your GitHub account.
|
|
77
|
+
|
|
78
|
+
User context (Django user id + username) is **never** sent unless you opt in with `ghosttrap.init(token, send_user=True)` in your app. Server hostname is captured automatically.
|
|
79
|
+
|
|
80
|
+
Run `ghosttrap nuke` from inside a repo to permanently delete every server-side row for that repo (errors + the token itself). Requires typing the repo name to confirm.
|
|
@@ -45,12 +45,15 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
|
|
|
45
45
|
| `ghosttrap clear` | Skip all outstanding errors |
|
|
46
46
|
| `ghosttrap nuke` | Permanently delete every server-side row for the current repo (errors + token). Requires typed confirmation. |
|
|
47
47
|
|
|
48
|
+
`peek`, `watch`, `last`, and `clear` accept `--repo owner/name` to target a specific claimed repo when you're not inside its working tree (e.g. `ghosttrap peek --repo alex-rowley/ghosttrap-cli`). Otherwise they detect the repo from cwd. `nuke` is intentionally cwd-locked.
|
|
49
|
+
|
|
48
50
|
## How it works
|
|
49
51
|
|
|
50
|
-
- **Setup** authenticates with GitHub to prove you
|
|
52
|
+
- **Setup** authenticates with GitHub (via the active `gh` account) to prove you have access to the repo, then saves a repo token locally. If your active `gh` account can't see the repo, setup fails with a clear message; switch with `gh auth switch` and retry.
|
|
51
53
|
- **Peek** and **watch** connect to ghosttrap.io using that token — no GitHub auth needed after setup
|
|
52
54
|
- Errors that arrive while you're offline are replayed on next connect (cursor-based, no duplicates)
|
|
53
|
-
-
|
|
55
|
+
- Repos are tracked by GitHub's immutable repo id, so a rename or transfer doesn't require any action — the next connect picks up the new `owner/name` and your token keeps working
|
|
56
|
+
- Local state is stored in `~/.ghosttrap/config.json`, keyed by GitHub repo id
|
|
54
57
|
|
|
55
58
|
## Requirements
|
|
56
59
|
|
|
@@ -61,4 +64,8 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
|
|
|
61
64
|
|
|
62
65
|
## Privacy
|
|
63
66
|
|
|
64
|
-
Error data (tracebacks, exception messages, file paths) is routed through ghosttrap.io. The server is not open source yet — if there's demand for self-hosting, we'll open it up. Your GitHub token is used only during `setup` to verify repo
|
|
67
|
+
Error data (tracebacks, exception messages, file paths) is routed through ghosttrap.io. The server is not open source yet — if there's demand for self-hosting, we'll open it up. Your GitHub token is used only during `setup` to verify repo access; it's never stored on the server. After setup, all communication uses a repo-specific token that grants access only to that repo's error stream — it cannot access your GitHub account.
|
|
68
|
+
|
|
69
|
+
User context (Django user id + username) is **never** sent unless you opt in with `ghosttrap.init(token, send_user=True)` in your app. Server hostname is captured automatically.
|
|
70
|
+
|
|
71
|
+
Run `ghosttrap nuke` from inside a repo to permanently delete every server-side row for that repo (errors + the token itself). Requires typing the repo name to confirm.
|
|
@@ -21,9 +21,10 @@ KNOWN_SKILL_HASHES = {
|
|
|
21
21
|
"38810f43867a2a91420cc3dacbc71d2acabd7125596fd5b43f222b49725c9696", # v0.3.10
|
|
22
22
|
"19b67d913dc5214ee4db3610bd8749da67324c174b904b5da71ee6de13e23e63", # v0.3.11
|
|
23
23
|
"bf7768c3de266b7018d5c722c6c9991b487e7897786b3a406c460842cdcde8b5", # v0.3.12
|
|
24
|
+
"d3f594c4c3601a4594c18ebc5e16dfd4abad4ab97d56285ecc20634b919b6731", # v0.3.14
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
__version__ = "0.3.
|
|
27
|
+
__version__ = "0.3.16"
|
|
27
28
|
|
|
28
29
|
GHOSTTRAP_SERVER = "wss://ghosttrap.io/stream/"
|
|
29
30
|
CONFIG_DIR = os.path.expanduser("~/.ghosttrap")
|
|
@@ -70,7 +71,7 @@ Read `~/.ghosttrap/config.json` for state. It contains:
|
|
|
70
71
|
|
|
71
72
|
1. Detect the current repo from `git config --get remote.origin.url` (returns `owner/name`).
|
|
72
73
|
2. Find a matching entry in config by looking for one whose `owner`/`name` equals the detected slug. If no match, tell the user to run `ghosttrap setup`. (The owner/name on a config entry auto-refreshes from the server when the repo is renamed or transferred, so always match against the entry's stored owner/name, not the config key.)
|
|
73
|
-
3. If `sdk_installed` is false or missing: install the SDK (`pip install ghosttrap-sdk`), wire `ghosttrap.init("<token>")` into the app startup. For Django projects, also add `"ghosttrap.django.GhostTrapApp"` to INSTALLED_APPS (re-attaches logging handler after Django's dictConfig) and `"ghosttrap.django.GhostTrapMiddleware"` to MIDDLEWARE (catches unhandled view exceptions). The SDK auto-hooks into Celery task_failure if Celery is installed, and attaches a logging handler for logger.exception() calls. Use whatever pattern the project already uses for configuration (env vars, settings files, hardcoded — match the existing style). Then update the config: set `sdk_installed: true`, `sdk_version`, `init_file` to record what you did.
|
|
74
|
+
3. If `sdk_installed` is false or missing: install the SDK (`pip install ghosttrap-sdk`), wire `ghosttrap.init("<token>")` into the app startup. For Django projects, also add `"ghosttrap.django.GhostTrapApp"` to INSTALLED_APPS (re-attaches logging handler after Django's dictConfig) and `"ghosttrap.django.GhostTrapMiddleware"` to MIDDLEWARE (catches unhandled view exceptions). The SDK auto-hooks into Celery task_failure if Celery is installed, and attaches a logging handler for logger.exception() calls. Use whatever pattern the project already uses for configuration (env vars, settings files, hardcoded — match the existing style). Then update the config: set `sdk_installed: true`, `sdk_version`, `init_file` to record what you did. Only pass `send_user=True` to `init()` if the user explicitly asks for user context in reports — it's PII and stays off by default.
|
|
74
75
|
4. Run `ghosttrap peek --clear` with `run_in_background: true`. The `--clear` flag skips any stale backlog from prior sessions so you only get fresh errors.
|
|
75
76
|
|
|
76
77
|
## When peek returns
|
|
@@ -200,18 +201,26 @@ def get_gh_token():
|
|
|
200
201
|
sys.exit(1)
|
|
201
202
|
|
|
202
203
|
|
|
203
|
-
def _get_repo_token(config):
|
|
204
|
-
"""Get the repo token
|
|
204
|
+
def _get_repo_token(config, requested=None):
|
|
205
|
+
"""Get the repo token. If `requested` is 'owner/name', match strictly. Else cwd, else first."""
|
|
205
206
|
repos = config.get("repos", {})
|
|
207
|
+
if not repos:
|
|
208
|
+
print("error: no repos configured. run 'ghosttrap setup' first.", file=sys.stderr)
|
|
209
|
+
sys.exit(1)
|
|
210
|
+
if requested:
|
|
211
|
+
for entry in repos.values():
|
|
212
|
+
if f"{entry.get('owner')}/{entry.get('name')}" == requested:
|
|
213
|
+
return entry["token"]
|
|
214
|
+
available = sorted(f"{e['owner']}/{e['name']}" for e in repos.values())
|
|
215
|
+
print(f"error: '{requested}' is not in your config.", file=sys.stderr)
|
|
216
|
+
print(f"available: {', '.join(available)}", file=sys.stderr)
|
|
217
|
+
sys.exit(1)
|
|
206
218
|
cwd_repo = _detect_repo_from_cwd()
|
|
207
219
|
if cwd_repo:
|
|
208
220
|
for entry in repos.values():
|
|
209
221
|
if f"{entry.get('owner')}/{entry.get('name')}" == cwd_repo:
|
|
210
222
|
return entry["token"]
|
|
211
|
-
|
|
212
|
-
return next(iter(repos.values()))["token"]
|
|
213
|
-
print("error: no repos configured. run 'ghosttrap setup' first.", file=sys.stderr)
|
|
214
|
-
sys.exit(1)
|
|
223
|
+
return next(iter(repos.values()))["token"]
|
|
215
224
|
|
|
216
225
|
|
|
217
226
|
async def _connect_and_handle(server_url, token, config, once=False):
|
|
@@ -387,10 +396,10 @@ def _advance_cursor(config, token):
|
|
|
387
396
|
return pending
|
|
388
397
|
|
|
389
398
|
|
|
390
|
-
def clear():
|
|
399
|
+
def clear(requested=None):
|
|
391
400
|
_require_setup()
|
|
392
401
|
config = _load_config()
|
|
393
|
-
token = _get_repo_token(config)
|
|
402
|
+
token = _get_repo_token(config, requested)
|
|
394
403
|
try:
|
|
395
404
|
pending = _advance_cursor(config, token)
|
|
396
405
|
if pending:
|
|
@@ -455,11 +464,11 @@ def nuke():
|
|
|
455
464
|
_save_config(config)
|
|
456
465
|
|
|
457
466
|
|
|
458
|
-
def last(do_clear=False):
|
|
467
|
+
def last(do_clear=False, requested=None):
|
|
459
468
|
_require_setup()
|
|
460
469
|
config = _load_config()
|
|
461
470
|
_check_cli_version(config)
|
|
462
|
-
token = _get_repo_token(config)
|
|
471
|
+
token = _get_repo_token(config, requested)
|
|
463
472
|
server = GHOSTTRAP_SERVER.replace("wss://", "https://").replace("/stream/", "")
|
|
464
473
|
url = f"{server}/last/{token}/"
|
|
465
474
|
try:
|
|
@@ -500,17 +509,22 @@ def main():
|
|
|
500
509
|
sub = parser.add_subparsers(dest="command")
|
|
501
510
|
|
|
502
511
|
sub.add_parser("setup", help="Claim repos and install Claude Code skill")
|
|
503
|
-
|
|
512
|
+
|
|
513
|
+
clear_parser = sub.add_parser("clear", help="Skip all outstanding errors")
|
|
514
|
+
clear_parser.add_argument("--repo", help="Target repo as owner/name (overrides cwd detection)")
|
|
504
515
|
|
|
505
516
|
watch_parser = sub.add_parser("watch", help="Stream errors in real time")
|
|
506
517
|
watch_parser.add_argument("--server", default=GHOSTTRAP_SERVER, help="WebSocket server URL")
|
|
518
|
+
watch_parser.add_argument("--repo", help="Target repo as owner/name (overrides cwd detection)")
|
|
507
519
|
|
|
508
520
|
peek_parser = sub.add_parser("peek", help="Wait for the next error then exit")
|
|
509
521
|
peek_parser.add_argument("--server", default=GHOSTTRAP_SERVER, help="WebSocket server URL")
|
|
510
522
|
peek_parser.add_argument("--clear", action="store_true", help="Skip outstanding errors before waiting")
|
|
523
|
+
peek_parser.add_argument("--repo", help="Target repo as owner/name (overrides cwd detection)")
|
|
511
524
|
|
|
512
525
|
last_parser = sub.add_parser("last", help="Fetch the most recent error then exit")
|
|
513
526
|
last_parser.add_argument("--clear", action="store_true", help="Also skip remaining outstanding errors")
|
|
527
|
+
last_parser.add_argument("--repo", help="Target repo as owner/name (overrides cwd detection)")
|
|
514
528
|
|
|
515
529
|
sub.add_parser("nuke", help="Permanently delete all server data for the current repo")
|
|
516
530
|
|
|
@@ -520,18 +534,18 @@ def main():
|
|
|
520
534
|
token = get_gh_token()
|
|
521
535
|
asyncio.run(setup(GHOSTTRAP_SERVER, token))
|
|
522
536
|
elif args.command == "clear":
|
|
523
|
-
clear()
|
|
537
|
+
clear(requested=args.repo)
|
|
524
538
|
elif args.command == "watch":
|
|
525
539
|
_require_setup()
|
|
526
540
|
_refresh_skill_if_stale()
|
|
527
541
|
config = _load_config()
|
|
528
|
-
token = _get_repo_token(config)
|
|
542
|
+
token = _get_repo_token(config, args.repo)
|
|
529
543
|
asyncio.run(watch(args.server, token))
|
|
530
544
|
elif args.command == "peek":
|
|
531
545
|
_require_setup()
|
|
532
546
|
_refresh_skill_if_stale()
|
|
533
547
|
config = _load_config()
|
|
534
|
-
token = _get_repo_token(config)
|
|
548
|
+
token = _get_repo_token(config, args.repo)
|
|
535
549
|
if args.clear:
|
|
536
550
|
try:
|
|
537
551
|
_advance_cursor(config, token)
|
|
@@ -541,7 +555,7 @@ def main():
|
|
|
541
555
|
asyncio.run(peek(args.server, token))
|
|
542
556
|
elif args.command == "last":
|
|
543
557
|
_refresh_skill_if_stale()
|
|
544
|
-
last(do_clear=args.clear)
|
|
558
|
+
last(do_clear=args.clear, requested=args.repo)
|
|
545
559
|
elif args.command == "nuke":
|
|
546
560
|
nuke()
|
|
547
561
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghosttrap-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.16
|
|
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
|
|
@@ -54,12 +54,15 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
|
|
|
54
54
|
| `ghosttrap clear` | Skip all outstanding errors |
|
|
55
55
|
| `ghosttrap nuke` | Permanently delete every server-side row for the current repo (errors + token). Requires typed confirmation. |
|
|
56
56
|
|
|
57
|
+
`peek`, `watch`, `last`, and `clear` accept `--repo owner/name` to target a specific claimed repo when you're not inside its working tree (e.g. `ghosttrap peek --repo alex-rowley/ghosttrap-cli`). Otherwise they detect the repo from cwd. `nuke` is intentionally cwd-locked.
|
|
58
|
+
|
|
57
59
|
## How it works
|
|
58
60
|
|
|
59
|
-
- **Setup** authenticates with GitHub to prove you
|
|
61
|
+
- **Setup** authenticates with GitHub (via the active `gh` account) to prove you have access to the repo, then saves a repo token locally. If your active `gh` account can't see the repo, setup fails with a clear message; switch with `gh auth switch` and retry.
|
|
60
62
|
- **Peek** and **watch** connect to ghosttrap.io using that token — no GitHub auth needed after setup
|
|
61
63
|
- Errors that arrive while you're offline are replayed on next connect (cursor-based, no duplicates)
|
|
62
|
-
-
|
|
64
|
+
- Repos are tracked by GitHub's immutable repo id, so a rename or transfer doesn't require any action — the next connect picks up the new `owner/name` and your token keeps working
|
|
65
|
+
- Local state is stored in `~/.ghosttrap/config.json`, keyed by GitHub repo id
|
|
63
66
|
|
|
64
67
|
## Requirements
|
|
65
68
|
|
|
@@ -70,4 +73,8 @@ Your app needs [ghosttrap-sdk](https://github.com/alex-rowley/ghosttrap-sdk) to
|
|
|
70
73
|
|
|
71
74
|
## Privacy
|
|
72
75
|
|
|
73
|
-
Error data (tracebacks, exception messages, file paths) is routed through ghosttrap.io. The server is not open source yet — if there's demand for self-hosting, we'll open it up. Your GitHub token is used only during `setup` to verify repo
|
|
76
|
+
Error data (tracebacks, exception messages, file paths) is routed through ghosttrap.io. The server is not open source yet — if there's demand for self-hosting, we'll open it up. Your GitHub token is used only during `setup` to verify repo access; it's never stored on the server. After setup, all communication uses a repo-specific token that grants access only to that repo's error stream — it cannot access your GitHub account.
|
|
77
|
+
|
|
78
|
+
User context (Django user id + username) is **never** sent unless you opt in with `ghosttrap.init(token, send_user=True)` in your app. Server hostname is captured automatically.
|
|
79
|
+
|
|
80
|
+
Run `ghosttrap nuke` from inside a repo to permanently delete every server-side row for that repo (errors + the token itself). Requires typing the repo name to confirm.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|