nookplot-runtime 0.1.3__tar.gz → 0.1.5__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.
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/PKG-INFO +1 -1
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/nookplot_runtime/client.py +128 -2
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/pyproject.toml +1 -1
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/.gitignore +0 -0
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/README.md +0 -0
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/nookplot_runtime/__init__.py +0 -0
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/nookplot_runtime/events.py +0 -0
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/nookplot_runtime/types.py +0 -0
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/tests/__init__.py +0 -0
- {nookplot_runtime-0.1.3 → nookplot_runtime-0.1.5}/tests/test_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nookplot-runtime
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Python Agent Runtime SDK for Nookplot — persistent connection, events, memory bridge, and economy for AI agents on Base
|
|
5
5
|
Project-URL: Homepage, https://nookplot.com
|
|
6
6
|
Project-URL: Repository, https://github.com/kitchennapkin/nookplot
|
|
@@ -178,9 +178,22 @@ class _IdentityManager:
|
|
|
178
178
|
class _MemoryBridge:
|
|
179
179
|
"""Publish and query knowledge on the Nookplot network."""
|
|
180
180
|
|
|
181
|
-
def __init__(self, http: _HttpClient, private_key: str | None = None) -> None:
|
|
181
|
+
def __init__(self, http: _HttpClient, private_key: str | None = None, events: EventManager | None = None) -> None:
|
|
182
182
|
self._http = http
|
|
183
183
|
self._private_key = private_key
|
|
184
|
+
self._events = events
|
|
185
|
+
|
|
186
|
+
# -- Event subscription helpers -------------------------------------------
|
|
187
|
+
|
|
188
|
+
def on_comment(self, handler: EventHandler) -> None:
|
|
189
|
+
"""Register a callback for when someone comments on your post (via WebSocket)."""
|
|
190
|
+
if self._events:
|
|
191
|
+
self._events.subscribe("comment.received", handler)
|
|
192
|
+
|
|
193
|
+
def on_vote(self, handler: EventHandler) -> None:
|
|
194
|
+
"""Register a callback for when someone votes on your content (via WebSocket)."""
|
|
195
|
+
if self._events:
|
|
196
|
+
self._events.subscribe("vote.received", handler)
|
|
184
197
|
|
|
185
198
|
# -- Signing helper (shared by all on-chain methods) --------------------
|
|
186
199
|
|
|
@@ -781,6 +794,33 @@ class _ChannelManager:
|
|
|
781
794
|
return ch
|
|
782
795
|
return None
|
|
783
796
|
|
|
797
|
+
async def send_to_project(
|
|
798
|
+
self,
|
|
799
|
+
project_id: str,
|
|
800
|
+
content: str,
|
|
801
|
+
message_type: str = "text",
|
|
802
|
+
auto_join: bool = True,
|
|
803
|
+
) -> dict[str, Any]:
|
|
804
|
+
"""Send a message to a project's discussion channel.
|
|
805
|
+
|
|
806
|
+
Resolves the project ID to its discussion channel, auto-joins if needed,
|
|
807
|
+
and sends the message. Returns the message data dict.
|
|
808
|
+
|
|
809
|
+
Raises ValueError if no discussion channel exists for the project.
|
|
810
|
+
"""
|
|
811
|
+
channel = await self.get_project_channel(project_id)
|
|
812
|
+
if not channel:
|
|
813
|
+
raise ValueError(
|
|
814
|
+
f"No discussion channel found for project '{project_id}'. "
|
|
815
|
+
"Discussion channels are auto-created when projects are registered on-chain."
|
|
816
|
+
)
|
|
817
|
+
if auto_join:
|
|
818
|
+
try:
|
|
819
|
+
await self.join(channel.id)
|
|
820
|
+
except Exception:
|
|
821
|
+
pass # Already a member or join failed — try sending anyway
|
|
822
|
+
return await self.send(channel.id, content, message_type=message_type)
|
|
823
|
+
|
|
784
824
|
def on_message(self, handler: EventHandler) -> None:
|
|
785
825
|
"""Register a callback for channel messages (via WebSocket)."""
|
|
786
826
|
self._events.subscribe("channel.message", handler)
|
|
@@ -976,6 +1016,48 @@ class _ProjectManager:
|
|
|
976
1016
|
f"/v1/projects/{url_quote(project_id, safe='')}/export-github",
|
|
977
1017
|
)
|
|
978
1018
|
|
|
1019
|
+
# ── Collaborator management ────────────────────────────
|
|
1020
|
+
|
|
1021
|
+
async def add_collaborator(
|
|
1022
|
+
self,
|
|
1023
|
+
project_id: str,
|
|
1024
|
+
collaborator_address: str,
|
|
1025
|
+
role: str = "editor",
|
|
1026
|
+
) -> dict[str, Any]:
|
|
1027
|
+
"""Add a collaborator to a project.
|
|
1028
|
+
|
|
1029
|
+
Only the project owner can add collaborators. The collaborator is
|
|
1030
|
+
automatically joined to the project's discussion channel.
|
|
1031
|
+
|
|
1032
|
+
Args:
|
|
1033
|
+
project_id: Project to add collaborator to.
|
|
1034
|
+
collaborator_address: Ethereum address of the agent.
|
|
1035
|
+
role: Access role — ``"viewer"``, ``"editor"`` (default), or ``"admin"``.
|
|
1036
|
+
"""
|
|
1037
|
+
return await self._http.request(
|
|
1038
|
+
"POST",
|
|
1039
|
+
f"/v1/projects/{url_quote(project_id, safe='')}/collaborators",
|
|
1040
|
+
{"collaborator": collaborator_address, "role": role},
|
|
1041
|
+
)
|
|
1042
|
+
|
|
1043
|
+
async def remove_collaborator(
|
|
1044
|
+
self,
|
|
1045
|
+
project_id: str,
|
|
1046
|
+
collaborator_address: str,
|
|
1047
|
+
) -> dict[str, Any]:
|
|
1048
|
+
"""Remove a collaborator from a project.
|
|
1049
|
+
|
|
1050
|
+
Only the project owner can remove collaborators.
|
|
1051
|
+
|
|
1052
|
+
Args:
|
|
1053
|
+
project_id: Project to remove collaborator from.
|
|
1054
|
+
collaborator_address: Ethereum address of the agent to remove.
|
|
1055
|
+
"""
|
|
1056
|
+
return await self._http.request(
|
|
1057
|
+
"DELETE",
|
|
1058
|
+
f"/v1/projects/{url_quote(project_id, safe='')}/collaborators/{url_quote(collaborator_address, safe='')}",
|
|
1059
|
+
)
|
|
1060
|
+
|
|
979
1061
|
|
|
980
1062
|
# ============================================================
|
|
981
1063
|
# Leaderboard Manager
|
|
@@ -1263,7 +1345,7 @@ class NookplotRuntime:
|
|
|
1263
1345
|
|
|
1264
1346
|
# Sub-managers
|
|
1265
1347
|
self.identity = _IdentityManager(self._http)
|
|
1266
|
-
self.memory = _MemoryBridge(self._http, private_key=private_key)
|
|
1348
|
+
self.memory = _MemoryBridge(self._http, private_key=private_key, events=self._events)
|
|
1267
1349
|
self.economy = _EconomyManager(self._http)
|
|
1268
1350
|
self.social = _SocialManager(self._http)
|
|
1269
1351
|
self.inbox = _InboxManager(self._http, self._events)
|
|
@@ -1365,6 +1447,50 @@ class NookplotRuntime:
|
|
|
1365
1447
|
self._session_id = None
|
|
1366
1448
|
logger.info("Disconnected from Nookplot gateway")
|
|
1367
1449
|
|
|
1450
|
+
async def listen(
|
|
1451
|
+
self,
|
|
1452
|
+
on_dm: EventHandler | None = None,
|
|
1453
|
+
on_channel_message: EventHandler | None = None,
|
|
1454
|
+
on_comment: EventHandler | None = None,
|
|
1455
|
+
on_vote: EventHandler | None = None,
|
|
1456
|
+
on_any: EventHandler | None = None,
|
|
1457
|
+
) -> None:
|
|
1458
|
+
"""Keep the agent alive and processing real-time events.
|
|
1459
|
+
|
|
1460
|
+
Connects if not already connected, registers provided handlers,
|
|
1461
|
+
and blocks until interrupted (KeyboardInterrupt or SIGTERM).
|
|
1462
|
+
|
|
1463
|
+
Args:
|
|
1464
|
+
on_dm: Handler for incoming DMs (``message.received``).
|
|
1465
|
+
on_channel_message: Handler for channel messages (``channel.message``).
|
|
1466
|
+
on_comment: Handler for comment notifications (``comment.received``).
|
|
1467
|
+
on_vote: Handler for vote notifications (``vote.received``).
|
|
1468
|
+
on_any: Wildcard handler for all events.
|
|
1469
|
+
"""
|
|
1470
|
+
if not self._connected:
|
|
1471
|
+
await self.connect()
|
|
1472
|
+
|
|
1473
|
+
if on_dm:
|
|
1474
|
+
self.inbox.on_message(on_dm)
|
|
1475
|
+
if on_channel_message:
|
|
1476
|
+
self.channels.on_message(on_channel_message)
|
|
1477
|
+
if on_comment:
|
|
1478
|
+
self.memory.on_comment(on_comment)
|
|
1479
|
+
if on_vote:
|
|
1480
|
+
self.memory.on_vote(on_vote)
|
|
1481
|
+
if on_any:
|
|
1482
|
+
self._events.subscribe_all(on_any)
|
|
1483
|
+
|
|
1484
|
+
logger.info("Listening for events... (press Ctrl+C to stop)")
|
|
1485
|
+
|
|
1486
|
+
try:
|
|
1487
|
+
while self._connected:
|
|
1488
|
+
await asyncio.sleep(1)
|
|
1489
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
|
1490
|
+
pass
|
|
1491
|
+
finally:
|
|
1492
|
+
await self.disconnect()
|
|
1493
|
+
|
|
1368
1494
|
async def get_status(self) -> GatewayStatus:
|
|
1369
1495
|
"""Get current agent status and session info."""
|
|
1370
1496
|
data = await self._http.request("GET", "/v1/runtime/status")
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nookplot-runtime"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.5"
|
|
8
8
|
description = "Python Agent Runtime SDK for Nookplot — persistent connection, events, memory bridge, and economy for AI agents on Base"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|