repr-cli 0.2.16__py3-none-any.whl → 0.2.17__py3-none-any.whl

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.
Files changed (43) hide show
  1. repr/__init__.py +1 -1
  2. repr/api.py +363 -62
  3. repr/auth.py +47 -38
  4. repr/change_synthesis.py +478 -0
  5. repr/cli.py +4099 -280
  6. repr/config.py +119 -11
  7. repr/configure.py +889 -0
  8. repr/cron.py +419 -0
  9. repr/dashboard/__init__.py +9 -0
  10. repr/dashboard/build.py +126 -0
  11. repr/dashboard/dist/assets/index-BYFVbEev.css +1 -0
  12. repr/dashboard/dist/assets/index-BrrhyJFO.css +1 -0
  13. repr/dashboard/dist/assets/index-CcEg74ts.js +270 -0
  14. repr/dashboard/dist/assets/index-Cerc-iA_.js +377 -0
  15. repr/dashboard/dist/assets/index-CjVcBW2L.css +1 -0
  16. repr/dashboard/dist/assets/index-Dfl3mR5E.js +377 -0
  17. repr/dashboard/dist/favicon.svg +4 -0
  18. repr/dashboard/dist/index.html +14 -0
  19. repr/dashboard/manager.py +234 -0
  20. repr/dashboard/server.py +1298 -0
  21. repr/db.py +980 -0
  22. repr/hooks.py +3 -2
  23. repr/loaders/__init__.py +22 -0
  24. repr/loaders/base.py +156 -0
  25. repr/loaders/claude_code.py +287 -0
  26. repr/loaders/clawdbot.py +313 -0
  27. repr/loaders/gemini_antigravity.py +381 -0
  28. repr/mcp_server.py +1196 -0
  29. repr/models.py +503 -0
  30. repr/openai_analysis.py +25 -0
  31. repr/session_extractor.py +481 -0
  32. repr/storage.py +328 -0
  33. repr/story_synthesis.py +1296 -0
  34. repr/templates.py +68 -4
  35. repr/timeline.py +710 -0
  36. repr/tools.py +17 -8
  37. {repr_cli-0.2.16.dist-info → repr_cli-0.2.17.dist-info}/METADATA +48 -10
  38. repr_cli-0.2.17.dist-info/RECORD +52 -0
  39. {repr_cli-0.2.16.dist-info → repr_cli-0.2.17.dist-info}/WHEEL +1 -1
  40. {repr_cli-0.2.16.dist-info → repr_cli-0.2.17.dist-info}/entry_points.txt +1 -0
  41. repr_cli-0.2.16.dist-info/RECORD +0 -26
  42. {repr_cli-0.2.16.dist-info → repr_cli-0.2.17.dist-info}/licenses/LICENSE +0 -0
  43. {repr_cli-0.2.16.dist-info → repr_cli-0.2.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="6" fill="#0ea5e9"/>
3
+ <text x="16" y="22" text-anchor="middle" font-family="system-ui" font-size="14" font-weight="bold" fill="white">R</text>
4
+ </svg>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>repr dashboard</title>
8
+ <script type="module" crossorigin src="/assets/index-Cerc-iA_.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BYFVbEev.css">
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,234 @@
1
+ """
2
+ Dashboard version manager.
3
+
4
+ Handles downloading and updating the Vue dashboard from GitHub releases.
5
+ Falls back to bundled dist/ if download unavailable.
6
+ """
7
+
8
+ import json
9
+ import shutil
10
+ import tarfile
11
+ import tempfile
12
+ from pathlib import Path
13
+ from urllib.request import urlopen, Request
14
+ from urllib.error import URLError
15
+
16
+ # Configuration
17
+ GITHUB_REPO = "everdraft/repr-dashboard" # Update with actual repo
18
+ RELEASE_URL = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
19
+ DOWNLOAD_TIMEOUT = 30 # seconds
20
+
21
+ # Paths
22
+ USER_DASHBOARD_DIR = Path.home() / ".repr" / "dashboard"
23
+ BUNDLED_DASHBOARD_DIR = Path(__file__).parent / "dist"
24
+ VERSION_FILE = "version.json"
25
+
26
+
27
+ def get_user_dashboard_path() -> Path:
28
+ """Get path to user-installed dashboard, or None if not installed."""
29
+ version_file = USER_DASHBOARD_DIR / VERSION_FILE
30
+ index_file = USER_DASHBOARD_DIR / "index.html"
31
+
32
+ if version_file.exists() and index_file.exists():
33
+ return USER_DASHBOARD_DIR
34
+ return None
35
+
36
+
37
+ def get_bundled_dashboard_path() -> Path:
38
+ """Get path to bundled dashboard."""
39
+ if (BUNDLED_DASHBOARD_DIR / "index.html").exists():
40
+ return BUNDLED_DASHBOARD_DIR
41
+ return None
42
+
43
+
44
+ def get_dashboard_path() -> Path:
45
+ """
46
+ Get the best available dashboard path.
47
+
48
+ Priority:
49
+ 1. User-installed dashboard (~/.repr/dashboard/) if exists
50
+ 2. Bundled dashboard (repr/dashboard/dist/) as fallback
51
+
52
+ Returns None if neither exists.
53
+ """
54
+ user_path = get_user_dashboard_path()
55
+ if user_path:
56
+ return user_path
57
+
58
+ return get_bundled_dashboard_path()
59
+
60
+
61
+ def get_installed_version() -> str | None:
62
+ """Get version of user-installed dashboard."""
63
+ version_file = USER_DASHBOARD_DIR / VERSION_FILE
64
+ if not version_file.exists():
65
+ return None
66
+
67
+ try:
68
+ data = json.loads(version_file.read_text())
69
+ return data.get("version")
70
+ except Exception:
71
+ return None
72
+
73
+
74
+ def get_bundled_version() -> str | None:
75
+ """Get version of bundled dashboard."""
76
+ version_file = BUNDLED_DASHBOARD_DIR / VERSION_FILE
77
+ if not version_file.exists():
78
+ return None
79
+
80
+ try:
81
+ data = json.loads(version_file.read_text())
82
+ return data.get("version")
83
+ except Exception:
84
+ return None
85
+
86
+
87
+ def get_latest_release_info() -> dict | None:
88
+ """
89
+ Fetch latest release info from GitHub.
90
+
91
+ Returns dict with 'version' and 'download_url' or None if unavailable.
92
+ """
93
+ try:
94
+ req = Request(
95
+ RELEASE_URL,
96
+ headers={"Accept": "application/vnd.github.v3+json", "User-Agent": "repr-cli"}
97
+ )
98
+ with urlopen(req, timeout=10) as resp:
99
+ data = json.loads(resp.read().decode())
100
+
101
+ version = data.get("tag_name", "").lstrip("v")
102
+
103
+ # Find the tarball asset
104
+ download_url = None
105
+ for asset in data.get("assets", []):
106
+ if asset["name"].endswith(".tar.gz"):
107
+ download_url = asset["browser_download_url"]
108
+ break
109
+
110
+ # Fallback to source tarball if no asset
111
+ if not download_url:
112
+ download_url = data.get("tarball_url")
113
+
114
+ if version and download_url:
115
+ return {"version": version, "download_url": download_url}
116
+ except (URLError, json.JSONDecodeError, KeyError):
117
+ pass
118
+
119
+ return None
120
+
121
+
122
+ def download_and_install(download_url: str, version: str) -> bool:
123
+ """
124
+ Download and install dashboard from URL.
125
+
126
+ Returns True on success, False on failure.
127
+ """
128
+ try:
129
+ # Create temp directory for download
130
+ with tempfile.TemporaryDirectory() as tmpdir:
131
+ tmpdir_path = Path(tmpdir)
132
+ tarball_path = tmpdir_path / "dashboard.tar.gz"
133
+
134
+ # Download
135
+ print(f" Downloading dashboard v{version}...")
136
+ req = Request(download_url, headers={"User-Agent": "repr-cli"})
137
+ with urlopen(req, timeout=DOWNLOAD_TIMEOUT) as resp:
138
+ tarball_path.write_bytes(resp.read())
139
+
140
+ # Extract
141
+ print(" Extracting...")
142
+ extract_dir = tmpdir_path / "extracted"
143
+ extract_dir.mkdir()
144
+
145
+ with tarfile.open(tarball_path, "r:gz") as tar:
146
+ tar.extractall(extract_dir)
147
+
148
+ # Find the dist directory (might be nested)
149
+ dist_dir = None
150
+ for item in extract_dir.rglob("index.html"):
151
+ dist_dir = item.parent
152
+ break
153
+
154
+ if not dist_dir:
155
+ print(" Error: Could not find index.html in archive")
156
+ return False
157
+
158
+ # Install to user directory
159
+ USER_DASHBOARD_DIR.parent.mkdir(parents=True, exist_ok=True)
160
+
161
+ if USER_DASHBOARD_DIR.exists():
162
+ shutil.rmtree(USER_DASHBOARD_DIR)
163
+
164
+ shutil.copytree(dist_dir, USER_DASHBOARD_DIR)
165
+
166
+ # Write version file
167
+ version_data = {"version": version}
168
+ (USER_DASHBOARD_DIR / VERSION_FILE).write_text(json.dumps(version_data))
169
+
170
+ print(f" Dashboard v{version} installed to {USER_DASHBOARD_DIR}")
171
+ return True
172
+
173
+ except Exception as e:
174
+ print(f" Error installing dashboard: {e}")
175
+ return False
176
+
177
+
178
+ def check_for_updates(quiet: bool = False) -> bool:
179
+ """
180
+ Check for dashboard updates and install if available.
181
+
182
+ Returns True if an update was installed.
183
+ """
184
+ installed = get_installed_version()
185
+
186
+ release = get_latest_release_info()
187
+ if not release:
188
+ if not quiet:
189
+ print(" Could not check for dashboard updates (offline?)")
190
+ return False
191
+
192
+ latest = release["version"]
193
+
194
+ # Compare versions (simple string comparison, assumes semver)
195
+ if installed and installed >= latest:
196
+ if not quiet:
197
+ print(f" Dashboard is up to date (v{installed})")
198
+ return False
199
+
200
+ if not quiet:
201
+ if installed:
202
+ print(f" New dashboard version available: v{installed} -> v{latest}")
203
+ else:
204
+ print(f" Installing dashboard v{latest}...")
205
+
206
+ return download_and_install(release["download_url"], latest)
207
+
208
+
209
+ def ensure_dashboard() -> Path:
210
+ """
211
+ Ensure dashboard is available, downloading if needed.
212
+
213
+ Returns path to dashboard directory.
214
+ Raises RuntimeError if no dashboard available.
215
+ """
216
+ # First, try user-installed
217
+ user_path = get_user_dashboard_path()
218
+ if user_path:
219
+ return user_path
220
+
221
+ # Try to download latest
222
+ release = get_latest_release_info()
223
+ if release:
224
+ if download_and_install(release["download_url"], release["version"]):
225
+ return USER_DASHBOARD_DIR
226
+
227
+ # Fall back to bundled
228
+ bundled_path = get_bundled_dashboard_path()
229
+ if bundled_path:
230
+ return bundled_path
231
+
232
+ raise RuntimeError(
233
+ "No dashboard available. Bundled dashboard not found and could not download from GitHub."
234
+ )