This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

weir / scripts / devlog-poc
3.4 kB 98 lines
1#!/usr/bin/env -S uv run --script --quiet 2# /// script 3# requires-python = ">=3.12" 4# dependencies = ["httpx", "websockets"] 5# /// 6"""proof of concept: orchestration records on a stock bluesky PDS. 7 8writes io.prefect.* records to an ordinary bsky.social account and shows 9them arriving on the public jetstream — i.e. identity-authenticated writes 10and a free event stream, no infrastructure of ours. 11 12usage: 13 ATPROTO_DEVLOG_HANDLE=zzstoatzzdevlog.bsky.social \ 14 ATPROTO_DEVLOG_PASSWORD=... ./scripts/devlog-poc 15""" 16import asyncio 17import json 18import os 19import sys 20from datetime import datetime, timezone 21 22import httpx 23import websockets 24 25PDS = "https://bsky.social" 26JETSTREAM = "wss://jetstream2.us-east.bsky.network/subscribe" 27HANDLE = os.environ.get("ATPROTO_DEVLOG_HANDLE", "zzstoatzzdevlog.bsky.social") 28PASSWORD = os.environ["ATPROTO_DEVLOG_PASSWORD"] 29 30COLLECTION = "io.prefect.v0.event" 31 32 33def now() -> str: 34 return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") 35 36 37async def main() -> None: 38 async with httpx.AsyncClient() as http: 39 session = ( 40 await http.post( 41 f"{PDS}/xrpc/com.atproto.server.createSession", 42 json={"identifier": HANDLE, "password": PASSWORD}, 43 ) 44 ).raise_for_status().json() 45 did = session["did"] 46 headers = {"Authorization": f"Bearer {session['accessJwt']}"} 47 print(f"authenticated as {HANDLE} ({did})") 48 49 record = { 50 "$type": COLLECTION, 51 "occurred": now(), 52 "event": "prefect.flow-run.Completed", 53 "resource": { 54 "prefect.resource.id": "prefect.flow-run.demo-run-0001", 55 "prefect.resource.name": "demo", 56 }, 57 "payload": { 58 "validated_state": {"type": "COMPLETED", "name": "Completed", "timestamp": now()}, 59 }, 60 } 61 62 async with websockets.connect( 63 f"{JETSTREAM}?wantedCollections={COLLECTION}&dids={did}" 64 ) as ws: 65 print(f"listening on jetstream for {COLLECTION} from {did} ...") 66 67 created = ( 68 await http.post( 69 f"{PDS}/xrpc/com.atproto.repo.createRecord", 70 headers=headers, 71 json={"repo": did, "collection": COLLECTION, "record": record}, 72 ) 73 ).raise_for_status().json() 74 print(f"wrote record: {created['uri']} (cid {created['cid'][:16]}...)") 75 76 event = json.loads(await asyncio.wait_for(ws.recv(), timeout=30)) 77 commit = event.get("commit", {}) 78 print("jetstream event received:") 79 print(f" did: {event.get('did')}") 80 print(f" collection: {commit.get('collection')}") 81 print(f" rkey: {commit.get('rkey')}") 82 print(f" event: {commit.get('record', {}).get('event')}") 83 84 listed = ( 85 await http.get( 86 f"{PDS}/xrpc/com.atproto.repo.listRecords", 87 params={"repo": did, "collection": COLLECTION}, 88 ) 89 ).raise_for_status().json() 90 print(f"records now in {COLLECTION}: {len(listed['records'])}") 91 print("=== a stock bluesky pds just served as orchestration storage + event stream ===") 92 93 94if __name__ == "__main__": 95 try: 96 asyncio.run(main()) 97 except KeyError: 98 sys.exit("set ATPROTO_DEVLOG_PASSWORD")