ghosttrap-cli 0.3.10__tar.gz → 0.3.11__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.10 → ghosttrap_cli-0.3.11}/PKG-INFO +1 -1
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli/cli.py +46 -19
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli.egg-info/PKG-INFO +1 -1
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/pyproject.toml +1 -1
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/README.md +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli/__init__.py +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli.egg-info/SOURCES.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli.egg-info/dependency_links.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli.egg-info/entry_points.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli.egg-info/requires.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/ghosttrap_cli.egg-info/top_level.txt +0 -0
- {ghosttrap_cli-0.3.10 → ghosttrap_cli-0.3.11}/setup.cfg +0 -0
|
@@ -18,9 +18,10 @@ KNOWN_SKILL_HASHES = {
|
|
|
18
18
|
"8564b65b8ab5c63283cda1706e30ca62bc4e111d33ba8918220f4b556ad01da1", # v0.3.1..v0.3.3
|
|
19
19
|
"5759b2e0dc8ca47c3801915fd688cc8da878a7ab8d405f5183ffd7e8c8df4c55", # v0.3.4..v0.3.7
|
|
20
20
|
"0651bb4247cf5c68960ff5b63d6a5d0c85ff1ce08e7966ab4823601ff02cf1f4", # v0.3.9
|
|
21
|
+
"38810f43867a2a91420cc3dacbc71d2acabd7125596fd5b43f222b49725c9696", # v0.3.10
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
__version__ = "0.3.
|
|
24
|
+
__version__ = "0.3.11"
|
|
24
25
|
|
|
25
26
|
GHOSTTRAP_SERVER = "wss://ghosttrap.io/stream/"
|
|
26
27
|
CONFIG_DIR = os.path.expanduser("~/.ghosttrap")
|
|
@@ -60,13 +61,13 @@ description: Production error monitoring via ghosttrap.io. Trigger when starting
|
|
|
60
61
|
# Ghosttrap
|
|
61
62
|
|
|
62
63
|
Read `~/.ghosttrap/config.json` for state. It contains:
|
|
63
|
-
- `repos`: map
|
|
64
|
+
- `repos`: map keyed by GitHub repo id (stringified int) to `{"github_id": int, "owner": str, "name": str, "token": "t_xxx", "sdk_installed": bool, "sdk_version": str, "init_file": str}`. Older configs may still be keyed by `"owner/name"` — same shape inside.
|
|
64
65
|
- `cursor`: last seen error ID
|
|
65
66
|
|
|
66
67
|
## On session start
|
|
67
68
|
|
|
68
|
-
1. Detect the current repo from `git config --get remote.origin.url
|
|
69
|
-
2.
|
|
69
|
+
1. Detect the current repo from `git config --get remote.origin.url` (returns `owner/name`).
|
|
70
|
+
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.)
|
|
70
71
|
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.
|
|
71
72
|
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.
|
|
72
73
|
|
|
@@ -103,16 +104,35 @@ def _save_config(config):
|
|
|
103
104
|
json.dump(config, f, indent=2)
|
|
104
105
|
|
|
105
106
|
|
|
106
|
-
def
|
|
107
|
-
|
|
107
|
+
def _repo_key(r):
|
|
108
|
+
gid = r.get("github_id")
|
|
109
|
+
if gid is not None:
|
|
110
|
+
return str(gid)
|
|
111
|
+
return f"{r.get('owner')}/{r.get('name')}"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _is_known_repo(config, repo_entry):
|
|
115
|
+
return _repo_key(repo_entry) in config.get("repos", {})
|
|
108
116
|
|
|
109
117
|
|
|
110
118
|
def _save_repos(config, repos):
|
|
111
119
|
if "repos" not in config:
|
|
112
120
|
config["repos"] = {}
|
|
113
121
|
for r in repos:
|
|
114
|
-
key =
|
|
115
|
-
config["repos"]
|
|
122
|
+
key = _repo_key(r)
|
|
123
|
+
existing = config["repos"].get(key, {})
|
|
124
|
+
# Drop any legacy slug-keyed entry now superseded by a github_id key.
|
|
125
|
+
if r.get("github_id") is not None:
|
|
126
|
+
legacy_key = f"{r.get('owner')}/{r.get('name')}"
|
|
127
|
+
if legacy_key in config["repos"] and legacy_key != key:
|
|
128
|
+
existing = {**config["repos"].pop(legacy_key), **existing}
|
|
129
|
+
existing.update({
|
|
130
|
+
"github_id": r.get("github_id"),
|
|
131
|
+
"owner": r["owner"],
|
|
132
|
+
"name": r["name"],
|
|
133
|
+
"token": r["token"],
|
|
134
|
+
})
|
|
135
|
+
config["repos"][key] = existing
|
|
116
136
|
_save_config(config)
|
|
117
137
|
|
|
118
138
|
|
|
@@ -145,7 +165,7 @@ def _find_target_repo(repos):
|
|
|
145
165
|
cwd_slug = _detect_repo_from_cwd()
|
|
146
166
|
if cwd_slug:
|
|
147
167
|
for r in repos:
|
|
148
|
-
if f"{r
|
|
168
|
+
if f"{r.get('owner')}/{r.get('name')}" == cwd_slug:
|
|
149
169
|
return r
|
|
150
170
|
return repos[0] if repos else None
|
|
151
171
|
|
|
@@ -184,11 +204,14 @@ def get_gh_token():
|
|
|
184
204
|
|
|
185
205
|
def _get_repo_token(config):
|
|
186
206
|
"""Get the repo token for the current directory from config."""
|
|
187
|
-
cwd_repo = _detect_repo_from_cwd()
|
|
188
|
-
if cwd_repo and cwd_repo in config.get("repos", {}):
|
|
189
|
-
return config["repos"][cwd_repo]["token"]
|
|
190
|
-
# Fall back to first repo in config
|
|
191
207
|
repos = config.get("repos", {})
|
|
208
|
+
cwd_repo = _detect_repo_from_cwd()
|
|
209
|
+
if cwd_repo:
|
|
210
|
+
for entry in repos.values():
|
|
211
|
+
if f"{entry.get('owner')}/{entry.get('name')}" == cwd_repo:
|
|
212
|
+
return entry["token"]
|
|
213
|
+
if cwd_repo in repos:
|
|
214
|
+
return repos[cwd_repo]["token"]
|
|
192
215
|
if repos:
|
|
193
216
|
return next(iter(repos.values()))["token"]
|
|
194
217
|
print("error: no repos configured. run 'ghosttrap setup' first.", file=sys.stderr)
|
|
@@ -210,9 +233,10 @@ async def _connect_and_handle(server_url, token, config, once=False):
|
|
|
210
233
|
repos = event.get("repos", [])
|
|
211
234
|
print(f"watching {len(repos)} repo(s)", file=sys.stderr)
|
|
212
235
|
|
|
213
|
-
new_repos = [r for r in repos if not _is_known_repo(config, r
|
|
236
|
+
new_repos = [r for r in repos if not _is_known_repo(config, r)]
|
|
237
|
+
# Always sync — picks up renamed/transferred repos by github_id.
|
|
238
|
+
_save_repos(config, repos)
|
|
214
239
|
if new_repos:
|
|
215
|
-
_save_repos(config, repos)
|
|
216
240
|
target = _find_target_repo(new_repos)
|
|
217
241
|
if target:
|
|
218
242
|
_print_setup_snippet(target)
|
|
@@ -220,10 +244,13 @@ async def _connect_and_handle(server_url, token, config, once=False):
|
|
|
220
244
|
sdk_latest = event.get("sdk_latest")
|
|
221
245
|
if sdk_latest:
|
|
222
246
|
cwd_repo = _detect_repo_from_cwd()
|
|
223
|
-
if cwd_repo
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
247
|
+
if cwd_repo:
|
|
248
|
+
for entry in config.get("repos", {}).values():
|
|
249
|
+
if f"{entry.get('owner')}/{entry.get('name')}" == cwd_repo:
|
|
250
|
+
installed = entry.get("sdk_version")
|
|
251
|
+
if installed and installed != sdk_latest:
|
|
252
|
+
print(f"ghosttrap-sdk {sdk_latest} available (you have {installed})", file=sys.stderr)
|
|
253
|
+
break
|
|
227
254
|
|
|
228
255
|
if not once:
|
|
229
256
|
print(f"waiting for errors...", file=sys.stderr)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|