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.
- repr/__init__.py +1 -1
- repr/cli.py +185 -51
- repr/dashboard/dist/assets/index-B-aCjaCw.js +384 -0
- repr/dashboard/dist/assets/index-C7Gzxc4f.js +384 -0
- repr/dashboard/dist/assets/index-CQdMXo6g.js +391 -0
- repr/dashboard/dist/assets/index-Cs8ofFGd.js +384 -0
- repr/dashboard/dist/assets/index-DwN0SeMc.css +1 -0
- repr/dashboard/dist/assets/index-YFch_e0S.js +384 -0
- repr/dashboard/dist/index.html +2 -2
- repr/dashboard/server.py +191 -0
- {repr_cli-0.2.17.dist-info → repr_cli-0.2.19.dist-info}/METADATA +1 -1
- {repr_cli-0.2.17.dist-info → repr_cli-0.2.19.dist-info}/RECORD +16 -10
- {repr_cli-0.2.17.dist-info → repr_cli-0.2.19.dist-info}/WHEEL +0 -0
- {repr_cli-0.2.17.dist-info → repr_cli-0.2.19.dist-info}/entry_points.txt +0 -0
- {repr_cli-0.2.17.dist-info → repr_cli-0.2.19.dist-info}/licenses/LICENSE +0 -0
- {repr_cli-0.2.17.dist-info → repr_cli-0.2.19.dist-info}/top_level.txt +0 -0
repr/dashboard/dist/index.html
CHANGED
|
@@ -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-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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,9 +1,9 @@
|
|
|
1
|
-
repr/__init__.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
48
|
-
repr_cli-0.2.
|
|
49
|
-
repr_cli-0.2.
|
|
50
|
-
repr_cli-0.2.
|
|
51
|
-
repr_cli-0.2.
|
|
52
|
-
repr_cli-0.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|