repr-cli 0.2.17__py3-none-any.whl → 0.2.19__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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
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">
8
+ <script type="module" crossorigin src="/assets/index-Cs8ofFGd.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DwN0SeMc.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="app"></div>
repr/dashboard/server.py CHANGED
@@ -231,6 +231,7 @@ def _get_auth_status() -> dict:
231
231
  return {
232
232
  "authenticated": False,
233
233
  "user": None,
234
+ "token": None,
234
235
  }
235
236
 
236
237
  auth = get_auth()
@@ -242,6 +243,7 @@ def _get_auth_status() -> dict:
242
243
  "username": auth.get("username"),
243
244
  "authenticated_at": auth.get("authenticated_at"),
244
245
  },
246
+ "token": auth.get("access_token"),
245
247
  }
246
248
 
247
249
 
@@ -655,6 +657,110 @@ def _set_story_visibility(story_id: str, visibility: str) -> dict:
655
657
  return {"success": True, "story_id": story_id, "visibility": visibility}
656
658
 
657
659
 
660
+ # ============================================================================
661
+ # Publish API helpers
662
+ # ============================================================================
663
+
664
+ def _get_stories_to_publish(
665
+ scope: str,
666
+ repo_name: str | None = None,
667
+ story_id: str | None = None,
668
+ include_pushed: bool = False,
669
+ ) -> list:
670
+ """Get stories to publish based on scope."""
671
+ from ..db import get_db
672
+
673
+ db = get_db()
674
+
675
+ if scope == "story" and story_id:
676
+ story = db.get_story(story_id)
677
+ if story:
678
+ return [story]
679
+ return []
680
+
681
+ elif scope == "repo" and repo_name:
682
+ projects = db.list_projects()
683
+ project_ids = [p["id"] for p in projects if p["name"] == repo_name]
684
+ if not project_ids:
685
+ return []
686
+ return [s for s in db.list_stories(limit=10000) if s.project_id in project_ids]
687
+
688
+ else: # scope == "all"
689
+ return db.list_stories(limit=10000)
690
+
691
+
692
+ def _publish_preview(data: dict) -> dict:
693
+ """Preview what would be published."""
694
+ scope = data.get("scope", "all")
695
+ repo_name = data.get("repo_name")
696
+ story_id = data.get("story_id")
697
+ include_pushed = data.get("include_pushed", False)
698
+
699
+ stories = _get_stories_to_publish(scope, repo_name, story_id, include_pushed)
700
+
701
+ return {
702
+ "count": len(stories),
703
+ "stories": [
704
+ {
705
+ "id": s.id,
706
+ "title": s.title,
707
+ "visibility": s.visibility or "private",
708
+ "repo_name": getattr(s, "repo_name", None),
709
+ }
710
+ for s in stories[:100] # Limit preview to 100 stories
711
+ ],
712
+ }
713
+
714
+
715
+ def _publish_stories(data: dict) -> dict:
716
+ """Publish stories to repr.dev."""
717
+ import asyncio
718
+ from ..api import push_stories_batch, APIError, AuthError
719
+ from ..config import is_authenticated, get_access_token
720
+
721
+ # Check authentication
722
+ if not is_authenticated():
723
+ return {"success": False, "error": "Not authenticated. Please login first."}
724
+
725
+ scope = data.get("scope", "all")
726
+ repo_name = data.get("repo_name")
727
+ story_id = data.get("story_id")
728
+ visibility_override = data.get("visibility")
729
+ include_pushed = data.get("include_pushed", False)
730
+
731
+ stories = _get_stories_to_publish(scope, repo_name, story_id, include_pushed)
732
+
733
+ if not stories:
734
+ return {"success": True, "published_count": 0, "failed_count": 0, "message": "No stories to publish"}
735
+
736
+ # Build batch payload
737
+ stories_payload = []
738
+ for story in stories:
739
+ payload = story.model_dump(mode="json")
740
+ # Use override visibility or story's current visibility
741
+ payload["visibility"] = visibility_override or story.visibility or "private"
742
+ payload["client_id"] = story.id
743
+ stories_payload.append(payload)
744
+
745
+ try:
746
+ result = asyncio.run(push_stories_batch(stories_payload))
747
+ pushed = result.get("pushed", 0)
748
+ failed = result.get("failed", 0)
749
+
750
+ return {
751
+ "success": True,
752
+ "published_count": pushed,
753
+ "failed_count": failed,
754
+ }
755
+
756
+ except AuthError as e:
757
+ return {"success": False, "error": f"Authentication error: {str(e)}"}
758
+ except APIError as e:
759
+ return {"success": False, "error": f"API error: {str(e)}"}
760
+ except Exception as e:
761
+ return {"success": False, "error": str(e)}
762
+
763
+
658
764
  class TimelineHandler(http.server.BaseHTTPRequestHandler):
659
765
  """HTTP handler for story dashboard."""
660
766
 
@@ -685,6 +791,8 @@ class TimelineHandler(http.server.BaseHTTPRequestHandler):
685
791
  self.check_username()
686
792
  elif self.path == "/api/visibility":
687
793
  self.serve_visibility_settings()
794
+ elif self.path.startswith("/api/publish/preview"):
795
+ self.serve_publish_preview()
688
796
  else:
689
797
  self.send_error(404, "API Endpoint Not Found")
690
798
  elif "." in self.path.split("/")[-1]:
@@ -729,6 +837,10 @@ class TimelineHandler(http.server.BaseHTTPRequestHandler):
729
837
  self.update_visibility_settings()
730
838
  elif self.path.startswith("/api/stories/") and self.path.endswith("/visibility"):
731
839
  self.update_story_visibility()
840
+ elif self.path == "/api/publish":
841
+ self.do_publish()
842
+ elif self.path == "/api/publish/mark-pushed":
843
+ self.mark_stories_pushed()
732
844
  else:
733
845
  self.send_error(404, "API Endpoint Not Found")
734
846
 
@@ -1267,6 +1379,85 @@ class TimelineHandler(http.server.BaseHTTPRequestHandler):
1267
1379
  except Exception as e:
1268
1380
  self.send_error(500, str(e))
1269
1381
 
1382
+ # =========================================================================
1383
+ # Publish endpoints
1384
+ # =========================================================================
1385
+
1386
+ def serve_publish_preview(self):
1387
+ """Preview what would be published."""
1388
+ from urllib.parse import urlparse, parse_qs
1389
+
1390
+ try:
1391
+ query = parse_qs(urlparse(self.path).query)
1392
+ data = {
1393
+ "scope": query.get("scope", ["all"])[0],
1394
+ "repo_name": query.get("repo_name", [None])[0],
1395
+ "story_id": query.get("story_id", [None])[0],
1396
+ "include_pushed": query.get("include_pushed", ["false"])[0] == "true",
1397
+ }
1398
+
1399
+ result = _publish_preview(data)
1400
+ body = json.dumps(result)
1401
+ self.send_response(200)
1402
+ self.send_header("Content-Type", "application/json")
1403
+ self.send_header("Access-Control-Allow-Origin", "*")
1404
+ self.send_header("Content-Length", len(body.encode()))
1405
+ self.end_headers()
1406
+ self.wfile.write(body.encode())
1407
+ except Exception as e:
1408
+ self.send_error(500, str(e))
1409
+
1410
+ def do_publish(self):
1411
+ """Publish stories to repr.dev."""
1412
+ try:
1413
+ content_length = int(self.headers.get("Content-Length", 0))
1414
+ body = self.rfile.read(content_length)
1415
+ data = json.loads(body.decode()) if body else {}
1416
+
1417
+ result = _publish_stories(data)
1418
+ response_body = json.dumps(result)
1419
+ self.send_response(200)
1420
+ self.send_header("Content-Type", "application/json")
1421
+ self.send_header("Access-Control-Allow-Origin", "*")
1422
+ self.send_header("Content-Length", len(response_body.encode()))
1423
+ self.end_headers()
1424
+ self.wfile.write(response_body.encode())
1425
+ except json.JSONDecodeError:
1426
+ self.send_error(400, "Invalid JSON")
1427
+ except Exception as e:
1428
+ self.send_error(500, str(e))
1429
+
1430
+ def mark_stories_pushed(self):
1431
+ """Mark stories as pushed in local DB."""
1432
+ try:
1433
+ content_length = int(self.headers.get("Content-Length", 0))
1434
+ body = self.rfile.read(content_length)
1435
+ data = json.loads(body.decode()) if body else {}
1436
+
1437
+ story_ids = data.get("story_ids", [])
1438
+ if story_ids:
1439
+ from datetime import datetime
1440
+ now = datetime.utcnow().isoformat()
1441
+ db = _get_db()
1442
+ placeholders = ",".join("?" * len(story_ids))
1443
+ db.execute(
1444
+ f"UPDATE stories SET pushed_at = ? WHERE id IN ({placeholders})",
1445
+ [now] + story_ids
1446
+ )
1447
+ db.commit()
1448
+
1449
+ response_body = json.dumps({"success": True, "marked": len(story_ids)})
1450
+ self.send_response(200)
1451
+ self.send_header("Content-Type", "application/json")
1452
+ self.send_header("Access-Control-Allow-Origin", "*")
1453
+ self.send_header("Content-Length", len(response_body.encode()))
1454
+ self.end_headers()
1455
+ self.wfile.write(response_body.encode())
1456
+ except json.JSONDecodeError:
1457
+ self.send_error(400, "Invalid JSON")
1458
+ except Exception as e:
1459
+ self.send_error(500, str(e))
1460
+
1270
1461
 
1271
1462
  def run_server(port: int, host: str, skip_update_check: bool = False) -> None:
1272
1463
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repr-cli
3
- Version: 0.2.17
3
+ Version: 0.2.19
4
4
  Summary: A beautiful, privacy-first CLI that analyzes your code repositories and generates a compelling developer profile
5
5
  Author-email: Repr <hello@repr.dev>
6
6
  License: MIT License
@@ -1,9 +1,9 @@
1
- repr/__init__.py,sha256=QXYSonUMTuWOhNS476SmCmXEw3-XC93Vrg_Kqacptog,447
1
+ repr/__init__.py,sha256=_YVX4f3_NtxGhDquXGSiaxkfAG2BdWZlB4MbobLsO74,447
2
2
  repr/__main__.py,sha256=N7amYwdGB3yzk2ZJJbtH2hhESNkDuhDL11dDEm5Kl60,166
3
3
  repr/api.py,sha256=rJRn_4xZXipdBFMrsZbQPWfZKfPLWJpTI0uYUyvjFhw,22814
4
4
  repr/auth.py,sha256=TpqwqwZ3tAEolcSYu-zD8oHhzfwHALkauPP1xg5VTiY,12208
5
5
  repr/change_synthesis.py,sha256=z7GmCeEHQFlnqLtKDGDvlM7p9MAWl_ByeIJstEVAhbU,15223
6
- repr/cli.py,sha256=aU8aN-h4z3sflabL65RiLce7ClOEl56iF5KftfBnnjs,220016
6
+ repr/cli.py,sha256=tcw_ben1VPE1wswiOLtGJ6esgh4lPgmCNb9eVjTQ7pU,225758
7
7
  repr/config.py,sha256=S69hdgFdvcHoIO2zihuvsSAQf2Gp41JtC5GGlE4Cy78,34233
8
8
  repr/configure.py,sha256=GnwjOC64F0uDD90IjA6LJNev8FlHHAHARuSLwBqI6k0,26860
9
9
  repr/cron.py,sha256=Hvo9ssVmGn09dLIHKWqzorKkW7eXdLQnQlBzagTX2Ko,11402
@@ -30,23 +30,29 @@ repr/updater.py,sha256=rybVVIxDk6RmKaswyKogVun8egVaonyH9nh_q2Rr0Vk,7335
30
30
  repr/dashboard/__init__.py,sha256=gnS9cajkmGD-HFiYSDe5vKlzLOpsysicK-YU7TnpAMI,162
31
31
  repr/dashboard/build.py,sha256=k01__9ECsdlsPR-0AAfv0p920VOUBQ68-xPkwF4o69g,3519
32
32
  repr/dashboard/manager.py,sha256=HFRM2WUqvg3kzPx5K9AIdxWejMTTBEcd-zg8VCxtvaY,6879
33
- repr/dashboard/server.py,sha256=0pWbeGVcnPGfpcfM09A35Sj-jLk7sjMFZMrgrdCN4iE,48299
33
+ repr/dashboard/server.py,sha256=bzh8WF2JrbxP7xIUjq4wrHiUEoLVi9RTf4RrYeamGyA,55319
34
34
  repr/dashboard/dist/favicon.svg,sha256=_GU6Mv99D6MDjMP0TYIbtP1AJWoliAshDg-M47jdWjY,246
35
- repr/dashboard/dist/index.html,sha256=2RcsFg5f2FmRzAsOxBJiCCQPkDb8MoIgtD8N49fIuR8,457
35
+ repr/dashboard/dist/index.html,sha256=PcdV2dKqNFOfuVrOmwgwPnszakLxyLYSyx_sIOsIe60,457
36
+ repr/dashboard/dist/assets/index-B-aCjaCw.js,sha256=v9vra_751KIpzs5z88UjcqyQ2cFYCH2wszJ32-X1aUw,154193
36
37
  repr/dashboard/dist/assets/index-BYFVbEev.css,sha256=4oJsN21PA3lcgNF_Odx-3SalE0vU8Olot827hrB-Jrk,35198
37
38
  repr/dashboard/dist/assets/index-BrrhyJFO.css,sha256=B0YN9FVh4hNlb5Sq-oZR8EvLTbj2boTit5VHYBgYYOs,30794
39
+ repr/dashboard/dist/assets/index-C7Gzxc4f.js,sha256=GyBY5jBflG97EhHLVcrqet2zUeE_KxejgHCUdmLBhiE,154295
40
+ repr/dashboard/dist/assets/index-CQdMXo6g.js,sha256=vT5yZHkZMx14hR2KLg07ecR0l7OVmqr9HvBiP-NLtiE,153831
38
41
  repr/dashboard/dist/assets/index-CcEg74ts.js,sha256=3q45hpLvDBnr1BXgJqMsR0SR136wqq6LRsFFXxRFobc,131744
39
42
  repr/dashboard/dist/assets/index-Cerc-iA_.js,sha256=SYypLBfj__-UvtdnH63TlQOqnrWJMNE3bfw162KWO74,145935
40
43
  repr/dashboard/dist/assets/index-CjVcBW2L.css,sha256=A2-2av1DjlTaXSGCGwtZ5Re-9e_9evZXnPWEmObE4zo,35183
44
+ repr/dashboard/dist/assets/index-Cs8ofFGd.js,sha256=FXtpRH-rXtvkNI3NLSab7m-ammiL1fukhio9i4A8cFU,153438
41
45
  repr/dashboard/dist/assets/index-Dfl3mR5E.js,sha256=2cJfa7nndn6gJUD4q0sUbfZ4bXGzWvVJJ_PGH94dvII,141440
46
+ repr/dashboard/dist/assets/index-DwN0SeMc.css,sha256=iOFXiTDPv_gcm81Bi1zlLayBKs-5xizz0zhgeK-_i-A,36564
47
+ repr/dashboard/dist/assets/index-YFch_e0S.js,sha256=0Zj1ogi_yAQo-19it1wuKySgdhXySLEHYszEJ-ukZgs,154295
42
48
  repr/loaders/__init__.py,sha256=GJLT6NlrcMOg7C64cD8Yiu70rTMXqs2ayK-Iz9HpwOY,647
43
49
  repr/loaders/base.py,sha256=AE9lFr8ZvPYt6KDwBTkNv3JF5A2QakVn9gA_ha77GLU,4308
44
50
  repr/loaders/claude_code.py,sha256=sWAiQgNVWsdw9qUDcfHDBi5k6jBL7D8_SI3NAEsL-io,11106
45
51
  repr/loaders/clawdbot.py,sha256=daKfTjI16tZrlwGUNaVOnLwxKyV6eW102CgIOu4mwAw,12064
46
52
  repr/loaders/gemini_antigravity.py,sha256=_0HhtC1TwB2gSu20Bcco_W-V3Bt6v9O2iqOL6kIHQLU,13766
47
- repr_cli-0.2.17.dist-info/licenses/LICENSE,sha256=tI16Ry3IQhjsde6weJ_in6czzWW2EF4Chz1uicyDLAA,1061
48
- repr_cli-0.2.17.dist-info/METADATA,sha256=0ePZdROx0eL579fcHZn3ycixGptVRzttleOjLVUkUXM,13387
49
- repr_cli-0.2.17.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
50
- repr_cli-0.2.17.dist-info/entry_points.txt,sha256=dlI-TCeDTW2rBC_nvOvMhwLihU4qsgD5r4Ot5BuVqSw,56
51
- repr_cli-0.2.17.dist-info/top_level.txt,sha256=LNgPqdJPQnlicRve7uzI4a6rEUdcxHrNkUq_2w7eeiA,5
52
- repr_cli-0.2.17.dist-info/RECORD,,
53
+ repr_cli-0.2.19.dist-info/licenses/LICENSE,sha256=tI16Ry3IQhjsde6weJ_in6czzWW2EF4Chz1uicyDLAA,1061
54
+ repr_cli-0.2.19.dist-info/METADATA,sha256=Cn6k6tYTtW0VIlbJMpLJMIB9bjifdNgOiSp2IXyguSQ,13387
55
+ repr_cli-0.2.19.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
56
+ repr_cli-0.2.19.dist-info/entry_points.txt,sha256=dlI-TCeDTW2rBC_nvOvMhwLihU4qsgD5r4Ot5BuVqSw,56
57
+ repr_cli-0.2.19.dist-info/top_level.txt,sha256=LNgPqdJPQnlicRve7uzI4a6rEUdcxHrNkUq_2w7eeiA,5
58
+ repr_cli-0.2.19.dist-info/RECORD,,