hyperagents 1.2.3rc5.post3__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 (361) hide show
  1. agents/__init__.py +31 -0
  2. agents/background_monitor.py +453 -0
  3. agents/learning.py +460 -0
  4. agents/planner.py +421 -0
  5. agents/transcriber.py +341 -0
  6. civilization/__init__.py +51 -0
  7. civilization/api/__init__.py +7 -0
  8. civilization/api/civilization_api.py +361 -0
  9. civilization/graph/__init__.py +19 -0
  10. civilization/graph/knowledge_linker.py +348 -0
  11. civilization/graph/org_graph.py +348 -0
  12. civilization/graph/process_graph.py +386 -0
  13. civilization/ingestion/__init__.py +20 -0
  14. civilization/ingestion/chunker.py +273 -0
  15. civilization/ingestion/document_ingestor.py +358 -0
  16. civilization/ingestion/embedder.py +262 -0
  17. civilization/ingestion/extractor.py +224 -0
  18. civilization/interview/__init__.py +19 -0
  19. civilization/interview/gap_detector.py +380 -0
  20. civilization/interview/interviewer.py +323 -0
  21. civilization/interview/questioner.py +363 -0
  22. civilization/retrieval/__init__.py +17 -0
  23. civilization/retrieval/civilization_rag.py +316 -0
  24. civilization/retrieval/context_injector.py +342 -0
  25. civilization/retrieval/relevance_ranker.py +287 -0
  26. civilization/schema.py +299 -0
  27. civilization/store.py +177 -0
  28. civilization/sync/__init__.py +16 -0
  29. civilization/sync/confluence_sync.py +317 -0
  30. civilization/sync/gdrive_sync.py +262 -0
  31. civilization/sync/notion_sync.py +237 -0
  32. civilization/versioning/__init__.py +19 -0
  33. civilization/versioning/diff_engine.py +351 -0
  34. civilization/versioning/staleness_detector.py +303 -0
  35. civilization/versioning/version_manager.py +293 -0
  36. cli/__init__.py +1 -0
  37. cli/doctor.py +334 -0
  38. cli/hyperclaw.py +1357 -0
  39. cli/onboarding.py +469 -0
  40. core/__init__.py +1 -0
  41. core/connectors/__init__.py +119 -0
  42. core/connectors/infrastructure.py +772 -0
  43. core/connectors/messaging.py +586 -0
  44. core/connectors/productivity.py +764 -0
  45. core/connectors/smarthome.py +528 -0
  46. core/connectors/social.py +730 -0
  47. core/hyperrouter/__init__.py +23 -0
  48. core/hyperrouter/bandit.py +218 -0
  49. core/hyperrouter/fast_loop.py +68 -0
  50. core/hyperrouter/slow_loop.py +143 -0
  51. core/hyperstate/__init__.py +24 -0
  52. core/hyperstate/certifier.py +154 -0
  53. core/hyperstate/schema.py +160 -0
  54. core/hyperstate/state_manager.py +103 -0
  55. core/hyperstate/store.py +163 -0
  56. core/providers/__init__.py +121 -0
  57. core/providers/embeddings.py +389 -0
  58. core/providers/image.py +534 -0
  59. core/providers/llm.py +1935 -0
  60. core/providers/search.py +608 -0
  61. core/providers/voice.py +474 -0
  62. hyperagents-1.2.3rc5.post3.dist-info/METADATA +469 -0
  63. hyperagents-1.2.3rc5.post3.dist-info/RECORD +361 -0
  64. hyperagents-1.2.3rc5.post3.dist-info/WHEEL +5 -0
  65. hyperagents-1.2.3rc5.post3.dist-info/entry_points.txt +6 -0
  66. hyperagents-1.2.3rc5.post3.dist-info/licenses/LICENSE +21 -0
  67. hyperagents-1.2.3rc5.post3.dist-info/top_level.txt +13 -0
  68. hyperclaw/__init__.py +67 -0
  69. hyperclaw/__main__.py +220 -0
  70. hyperclaw/agent.py +775 -0
  71. hyperclaw/agent_coordinator.py +577 -0
  72. hyperclaw/anthropic_params.py +225 -0
  73. hyperclaw/api_utils.py +85 -0
  74. hyperclaw/approval_telegram.py +209 -0
  75. hyperclaw/approval_terminal.py +80 -0
  76. hyperclaw/attachments.py +177 -0
  77. hyperclaw/branding.py +55 -0
  78. hyperclaw/brief/__init__.py +11 -0
  79. hyperclaw/brief/cli.py +204 -0
  80. hyperclaw/brief/collect.py +146 -0
  81. hyperclaw/brief/compose.py +333 -0
  82. hyperclaw/brief/diff.py +110 -0
  83. hyperclaw/brief/errors.py +99 -0
  84. hyperclaw/brief/google.py +269 -0
  85. hyperclaw/brief/google_auth.py +294 -0
  86. hyperclaw/brief/links.py +124 -0
  87. hyperclaw/brief/service.py +424 -0
  88. hyperclaw/brief/store.py +205 -0
  89. hyperclaw/brief/tasks.py +200 -0
  90. hyperclaw/chat_scope.py +115 -0
  91. hyperclaw/cli_tui.py +497 -0
  92. hyperclaw/client_guard.py +717 -0
  93. hyperclaw/connection_check.py +113 -0
  94. hyperclaw/cost_guard.py +1728 -0
  95. hyperclaw/default_config/agents.yaml +534 -0
  96. hyperclaw/default_config/hyperclaw.yaml +27 -0
  97. hyperclaw/default_config/models.yaml +161 -0
  98. hyperclaw/deployment.py +39 -0
  99. hyperclaw/digest/__init__.py +9 -0
  100. hyperclaw/digest/cli.py +117 -0
  101. hyperclaw/digest/errors.py +100 -0
  102. hyperclaw/digest/gitlab.py +187 -0
  103. hyperclaw/digest/http.py +113 -0
  104. hyperclaw/digest/redact.py +32 -0
  105. hyperclaw/digest/service.py +271 -0
  106. hyperclaw/digest/slack.py +223 -0
  107. hyperclaw/digest/state.py +124 -0
  108. hyperclaw/digest/summarize.py +198 -0
  109. hyperclaw/digest/tokens.py +143 -0
  110. hyperclaw/distribution.py +46 -0
  111. hyperclaw/documents.py +351 -0
  112. hyperclaw/feeds.py +339 -0
  113. hyperclaw/google_workspace/__init__.py +5 -0
  114. hyperclaw/google_workspace/api.py +151 -0
  115. hyperclaw/google_workspace/auth.py +397 -0
  116. hyperclaw/google_workspace/calendar.py +157 -0
  117. hyperclaw/google_workspace/cli.py +72 -0
  118. hyperclaw/google_workspace/drive.py +236 -0
  119. hyperclaw/google_workspace/errors.py +58 -0
  120. hyperclaw/google_workspace/gmail.py +295 -0
  121. hyperclaw/google_workspace/meet.py +122 -0
  122. hyperclaw/google_workspace/tools.py +319 -0
  123. hyperclaw/hyperspeed.py +57 -0
  124. hyperclaw/integrations_layer.py +1427 -0
  125. hyperclaw/live_state.py +188 -0
  126. hyperclaw/managed_config.py +245 -0
  127. hyperclaw/media_hub.py +179 -0
  128. hyperclaw/memory_manager.py +584 -0
  129. hyperclaw/model_catalog.py +551 -0
  130. hyperclaw/model_policy.py +211 -0
  131. hyperclaw/model_router.py +595 -0
  132. hyperclaw/model_selector.py +72 -0
  133. hyperclaw/naming.py +284 -0
  134. hyperclaw/notices.py +65 -0
  135. hyperclaw/oai_stream.py +106 -0
  136. hyperclaw/orchestrator.py +591 -0
  137. hyperclaw/outbox.py +82 -0
  138. hyperclaw/persona.py +367 -0
  139. hyperclaw/prometheus.py +219 -0
  140. hyperclaw/prompt_assembly.py +157 -0
  141. hyperclaw/provider_setup.py +146 -0
  142. hyperclaw/providers.py +340 -0
  143. hyperclaw/qualification.py +341 -0
  144. hyperclaw/qualification_keys/hyper-d292fd127cef5a3e.pub +1 -0
  145. hyperclaw/qualification_records/anthropic--claude-sonnet-5-5.json +14 -0
  146. hyperclaw/reasoning_display.py +35 -0
  147. hyperclaw/scheduler.py +454 -0
  148. hyperclaw/server.py +847 -0
  149. hyperclaw/setup.py +627 -0
  150. hyperclaw/skills.py +717 -0
  151. hyperclaw/solomon.py +442 -0
  152. hyperclaw/swarm.py +533 -0
  153. hyperclaw/taint.py +832 -0
  154. hyperclaw/team/__init__.py +28 -0
  155. hyperclaw/team/dispatcher.py +431 -0
  156. hyperclaw/team/genesis.py +106 -0
  157. hyperclaw/team/lead.py +110 -0
  158. hyperclaw/team/model.py +135 -0
  159. hyperclaw/team/morning.py +238 -0
  160. hyperclaw/team/repos.py +66 -0
  161. hyperclaw/team/roster.py +264 -0
  162. hyperclaw/team/specialist.py +235 -0
  163. hyperclaw/team/status.py +163 -0
  164. hyperclaw/team/watchers.py +112 -0
  165. hyperclaw/teamsetup/__init__.py +17 -0
  166. hyperclaw/teamsetup/checks.py +208 -0
  167. hyperclaw/teamsetup/item.py +391 -0
  168. hyperclaw/teamsetup/oauth_loopback.py +84 -0
  169. hyperclaw/teamsetup/onboard.py +550 -0
  170. hyperclaw/teamsetup/profile.py +82 -0
  171. hyperclaw/teamsetup/secrets.py +313 -0
  172. hyperclaw/teamsetup/selfcheck.py +295 -0
  173. hyperclaw/teamsetup/service.py +495 -0
  174. hyperclaw/teamsetup/slack_oauth.py +442 -0
  175. hyperclaw/teamsetup/tty.py +62 -0
  176. hyperclaw/telegram_bot.py +1106 -0
  177. hyperclaw/telegram_intake.py +126 -0
  178. hyperclaw/telegram_pairing.py +360 -0
  179. hyperclaw/terminal_provider.py +323 -0
  180. hyperclaw/tools/__init__.py +1 -0
  181. hyperclaw/tools/approval.py +949 -0
  182. hyperclaw/tools/args.py +212 -0
  183. hyperclaw/tools/catalog.py +244 -0
  184. hyperclaw/tools/classify.py +266 -0
  185. hyperclaw/tools/effects.py +724 -0
  186. hyperclaw/tools/egress.py +210 -0
  187. hyperclaw/tools/envelope.py +188 -0
  188. hyperclaw/tools/guard.py +367 -0
  189. hyperclaw/tools/ledger.py +339 -0
  190. hyperclaw/tools/receipts.py +70 -0
  191. hyperclaw/tools/registry.py +318 -0
  192. hyperclaw/tools/service_guard.py +151 -0
  193. hyperclaw/tools/surfaces.py +54 -0
  194. hyperclaw/tools/workspace.py +87 -0
  195. hyperclaw/trading/__init__.py +77 -0
  196. hyperclaw/truth_guard.py +791 -0
  197. hyperclaw/tts.py +124 -0
  198. hyperclaw/tui.py +4255 -0
  199. hyperclaw/tui_bridge.py +938 -0
  200. hyperclaw/unattended.py +60 -0
  201. hyperclaw/updates/__init__.py +54 -0
  202. hyperclaw/updates/check.py +131 -0
  203. hyperclaw/updates/index.py +178 -0
  204. hyperclaw/updates/install.py +110 -0
  205. hyperclaw/updates/managed.py +161 -0
  206. hyperclaw/updates/manifest.py +138 -0
  207. hyperclaw/updates/notices.py +86 -0
  208. hyperclaw/updates/paths.py +53 -0
  209. hyperclaw/updates/release.py +146 -0
  210. hyperclaw/updates/upgrade.py +253 -0
  211. hyperclaw/updates/versioning.py +104 -0
  212. hyperclaw/userconfig.py +119 -0
  213. hyperclaw/xai.py +83 -0
  214. integrations/__init__.py +31 -0
  215. integrations/automation/__init__.py +1 -0
  216. integrations/automation/make.py +87 -0
  217. integrations/automation/zapier.py +87 -0
  218. integrations/base.py +476 -0
  219. integrations/communication/__init__.py +1 -0
  220. integrations/communication/twilio.py +279 -0
  221. integrations/communication/zoom.py +290 -0
  222. integrations/data/__init__.py +1 -0
  223. integrations/data/google_analytics.py +135 -0
  224. integrations/data/postgres.py +182 -0
  225. integrations/data/supabase_connector.py +202 -0
  226. integrations/data/weather_feeds.py +450 -0
  227. integrations/developer/__init__.py +9 -0
  228. integrations/developer/github.py +406 -0
  229. integrations/developer/gitlab.py +298 -0
  230. integrations/enterprise/__init__.py +13 -0
  231. integrations/enterprise/confluence.py +266 -0
  232. integrations/enterprise/hubspot.py +293 -0
  233. integrations/enterprise/jira.py +314 -0
  234. integrations/enterprise/salesforce.py +286 -0
  235. integrations/finance/__init__.py +1 -0
  236. integrations/finance/polymarket_client.py +247 -0
  237. integrations/finance/quickbooks.py +281 -0
  238. integrations/finance/stripe.py +261 -0
  239. integrations/gateway.py +238 -0
  240. integrations/messaging/__init__.py +32 -0
  241. integrations/messaging/discord.py +159 -0
  242. integrations/messaging/email.py +401 -0
  243. integrations/messaging/imessage.py +443 -0
  244. integrations/messaging/signal.py +175 -0
  245. integrations/messaging/slack.py +217 -0
  246. integrations/messaging/sms.py +175 -0
  247. integrations/messaging/teams.py +208 -0
  248. integrations/messaging/telegram.py +260 -0
  249. integrations/messaging/whatsapp.py +203 -0
  250. integrations/productivity/__init__.py +17 -0
  251. integrations/productivity/airtable.py +217 -0
  252. integrations/productivity/asana.py +248 -0
  253. integrations/productivity/google/__init__.py +19 -0
  254. integrations/productivity/google/base.py +89 -0
  255. integrations/productivity/google/calendar.py +272 -0
  256. integrations/productivity/google/docs.py +202 -0
  257. integrations/productivity/google/drive.py +268 -0
  258. integrations/productivity/google/gmail.py +279 -0
  259. integrations/productivity/google/meet.py +218 -0
  260. integrations/productivity/google/sheets.py +222 -0
  261. integrations/productivity/google/tasks.py +199 -0
  262. integrations/productivity/linear.py +279 -0
  263. integrations/productivity/microsoft/__init__.py +13 -0
  264. integrations/productivity/microsoft/base.py +93 -0
  265. integrations/productivity/microsoft/calendar.py +207 -0
  266. integrations/productivity/microsoft/onedrive.py +222 -0
  267. integrations/productivity/microsoft/outlook.py +241 -0
  268. integrations/productivity/microsoft/sharepoint.py +211 -0
  269. integrations/productivity/notion.py +243 -0
  270. integrations/productivity/todoist.py +250 -0
  271. integrations/productivity/trello.py +244 -0
  272. integrations/storage/__init__.py +1 -0
  273. integrations/storage/box.py +279 -0
  274. integrations/storage/dropbox.py +215 -0
  275. integrations/telegram_inbound.py +179 -0
  276. integrations/telegram_poller.py +151 -0
  277. memory/__init__.py +1 -0
  278. memory/causal_graph.py +125 -0
  279. memory/impact_tracker.py +88 -0
  280. memory/vector_memory.py +436 -0
  281. models/__init__.py +12 -0
  282. models/chatjimmy_client.py +210 -0
  283. models/claude_client.py +235 -0
  284. models/claude_code_subagent.py +192 -0
  285. models/openai_client.py +120 -0
  286. models/router.py +162 -0
  287. recursive/discovery_loop.py +118 -0
  288. recursive/optimization_loop.py +135 -0
  289. recursive/validation_loop.py +38 -0
  290. security/__init__.py +1 -0
  291. security/audit_logger.py +106 -0
  292. security/filesystem_guard.py +100 -0
  293. security/hypershield.py +99 -0
  294. security/network_guard.py +97 -0
  295. security/policy_engine.py +198 -0
  296. swarm/__init__.py +1 -0
  297. swarm/agents/__init__.py +0 -0
  298. swarm/agents/base.py +230 -0
  299. swarm/agents/business/__init__.py +0 -0
  300. swarm/agents/business/counsel.py +31 -0
  301. swarm/agents/business/herald.py +32 -0
  302. swarm/agents/business/ledger.py +26 -0
  303. swarm/agents/business/nexus.py +30 -0
  304. swarm/agents/business/ops.py +30 -0
  305. swarm/agents/business/pipeline.py +28 -0
  306. swarm/agents/business/revenue.py +30 -0
  307. swarm/agents/business/sovereign.py +31 -0
  308. swarm/agents/business/strategos.py +27 -0
  309. swarm/agents/business/talent.py +27 -0
  310. swarm/agents/business/venture.py +32 -0
  311. swarm/agents/comms/__init__.py +1 -0
  312. swarm/agents/comms/cipher.py +32 -0
  313. swarm/agents/comms/echo_comms.py +31 -0
  314. swarm/agents/comms/envoy.py +31 -0
  315. swarm/agents/comms/herald_scheduler.py +33 -0
  316. swarm/agents/comms/pulse.py +29 -0
  317. swarm/agents/creative/__init__.py +0 -0
  318. swarm/agents/creative/author.py +27 -0
  319. swarm/agents/creative/lens.py +25 -0
  320. swarm/agents/executive/__init__.py +1 -0
  321. swarm/agents/intelligence/__init__.py +1 -0
  322. swarm/agents/intelligence/arbiter.py +30 -0
  323. swarm/agents/intelligence/sentinel.py +30 -0
  324. swarm/agents/media/__init__.py +0 -0
  325. swarm/agents/personal/__init__.py +0 -0
  326. swarm/agents/personal/atlas.py +27 -0
  327. swarm/agents/personal/hearth.py +26 -0
  328. swarm/agents/personal/midas.py +27 -0
  329. swarm/agents/personal/navigator.py +27 -0
  330. swarm/agents/personal/nourish.py +31 -0
  331. swarm/agents/personal/vitals.py +33 -0
  332. swarm/agents/recursive/__init__.py +0 -0
  333. swarm/agents/recursive/alchemist.py +73 -0
  334. swarm/agents/recursive/calibrator.py +45 -0
  335. swarm/agents/recursive/scout.py +97 -0
  336. swarm/agents/scientific/__init__.py +0 -0
  337. swarm/agents/scientific/cosmos.py +36 -0
  338. swarm/agents/scientific/gaia.py +26 -0
  339. swarm/agents/scientific/medicus.py +33 -0
  340. swarm/agents/scientific/oracle_agent.py +39 -0
  341. swarm/agents/scientific/scribe.py +39 -0
  342. swarm/agents/talent/__init__.py +0 -0
  343. swarm/agents/talent/deal.py +28 -0
  344. swarm/agents/talent/roster.py +29 -0
  345. swarm/agents/talent/scout.py +29 -0
  346. swarm/agents/talent/stage.py +27 -0
  347. swarm/agents/tech/__init__.py +0 -0
  348. swarm/agents/tech/aegis.py +28 -0
  349. swarm/agents/tech/bridge.py +28 -0
  350. swarm/agents/tech/forge.py +28 -0
  351. swarm/agents/trading/__init__.py +1 -0
  352. swarm/agents/trading/global_prediction_engine.py +611 -0
  353. swarm/agents/trading/polymarket_trader.py +165 -0
  354. swarm/agents/trading/prediction_strategist.py +218 -0
  355. swarm/autogen_bridge.py +68 -0
  356. swarm/bid_protocol.py +196 -0
  357. swarm/dispatcher.py +527 -0
  358. swarm/nexus.py +181 -0
  359. swarm/registry.py +112 -0
  360. telegram_listener.py +403 -0
  361. ui/tui/app.py +214 -0
@@ -0,0 +1,182 @@
1
+ """PostgresConnector — PostgreSQL database integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from integrations.base import (
8
+ BaseConnector,
9
+ ConnectorCapability,
10
+ ConnectorInfo,
11
+ ConnectorUnavailableError,
12
+ )
13
+
14
+ logger = logging.getLogger("hyperclaw.integrations.postgres")
15
+
16
+
17
+ class PostgresConnector(BaseConnector):
18
+ """
19
+ PostgreSQL database connector using asyncpg.
20
+
21
+ Required config:
22
+ - connection_string: PostgreSQL connection URL
23
+ """
24
+
25
+ def __init__(self, config: dict) -> None:
26
+ self.config = config
27
+ self.connection_string = config.get("connection_string", "")
28
+
29
+ if config.get("enabled", False) and not self.connection_string:
30
+ raise ValueError("PostgresConnector requires connection_string")
31
+
32
+ self._pool = None
33
+
34
+ @property
35
+ def info(self) -> ConnectorInfo:
36
+ return ConnectorInfo(
37
+ connector_id="postgres",
38
+ platform="postgres",
39
+ category="data",
40
+ capabilities=frozenset(
41
+ [
42
+ ConnectorCapability.READ_DATA,
43
+ ConnectorCapability.WRITE_DATA,
44
+ ConnectorCapability.DELETE_DATA,
45
+ ConnectorCapability.LIST_DATA,
46
+ ConnectorCapability.SEARCH,
47
+ ]
48
+ ),
49
+ auth_type="api_key",
50
+ rate_limit_per_minute=1000,
51
+ )
52
+
53
+ async def _ensure_pool(self):
54
+ """Ensure connection pool is created."""
55
+ if self._pool is None:
56
+ try:
57
+ import asyncpg
58
+ except ImportError:
59
+ raise ConnectorUnavailableError("asyncpg not installed")
60
+
61
+ self._pool = await asyncpg.create_pool(
62
+ self.connection_string,
63
+ min_size=1,
64
+ max_size=10,
65
+ )
66
+
67
+ async def health(self) -> bool:
68
+ """Check if PostgreSQL connection is available."""
69
+ try:
70
+ import asyncpg # noqa: F401
71
+ except ImportError:
72
+ return False
73
+
74
+ try:
75
+ await self._ensure_pool()
76
+ async with self._pool.acquire() as conn:
77
+ await conn.fetchval("SELECT 1")
78
+ return True
79
+ except Exception as e:
80
+ logger.error(f"PostgreSQL health check failed: {e}")
81
+ return False
82
+
83
+ async def query(self, sql: str, params: list | None = None) -> list[dict]:
84
+ """Execute a SQL query and return results."""
85
+ try:
86
+ import asyncpg # noqa: F401
87
+ except ImportError:
88
+ raise ConnectorUnavailableError("asyncpg not installed")
89
+
90
+ await self._ensure_pool()
91
+
92
+ async with self._pool.acquire() as conn:
93
+ if params:
94
+ rows = await conn.fetch(sql, *params)
95
+ else:
96
+ rows = await conn.fetch(sql)
97
+
98
+ return [dict(row) for row in rows]
99
+
100
+ async def execute(self, sql: str, params: list | None = None) -> dict:
101
+ """Execute a SQL statement (INSERT/UPDATE/DELETE)."""
102
+ try:
103
+ import asyncpg # noqa: F401
104
+ except ImportError:
105
+ raise ConnectorUnavailableError("asyncpg not installed")
106
+
107
+ await self._ensure_pool()
108
+
109
+ async with self._pool.acquire() as conn:
110
+ if params:
111
+ result = await conn.execute(sql, *params)
112
+ else:
113
+ result = await conn.execute(sql)
114
+
115
+ return {"status": result}
116
+
117
+ async def list_tables(self) -> list[dict]:
118
+ """List all tables in the database."""
119
+ sql = """
120
+ SELECT table_name, table_type
121
+ FROM information_schema.tables
122
+ WHERE table_schema = 'public'
123
+ ORDER BY table_name
124
+ """
125
+ return await self.query(sql)
126
+
127
+ async def _read_impl(self, resource_id: str, **kwargs) -> dict:
128
+ """Read a row by ID."""
129
+ table = kwargs.get("table", "")
130
+ if not table:
131
+ raise ValueError("table parameter required")
132
+
133
+ result = await self.query(
134
+ f"SELECT * FROM {table} WHERE id = $1",
135
+ [resource_id],
136
+ )
137
+ return result[0] if result else {}
138
+
139
+ async def _write_impl(self, resource_type: str, data: dict, **kwargs) -> dict:
140
+ """Insert a row."""
141
+ columns = ", ".join(data.keys())
142
+ placeholders = ", ".join(f"${i+1}" for i in range(len(data)))
143
+ values = list(data.values())
144
+
145
+ result = await self.query(
146
+ f"INSERT INTO {resource_type} ({columns}) VALUES ({placeholders}) RETURNING *",
147
+ values,
148
+ )
149
+ return result[0] if result else {}
150
+
151
+ async def _delete_impl(self, resource_id: str, **kwargs) -> bool:
152
+ """Delete a row."""
153
+ table = kwargs.get("table", "")
154
+ if not table:
155
+ raise ValueError("table parameter required")
156
+
157
+ await self.execute(
158
+ f"DELETE FROM {table} WHERE id = $1",
159
+ [resource_id],
160
+ )
161
+ return True
162
+
163
+ async def _list_impl(
164
+ self, resource_type: str, filters: dict, **kwargs
165
+ ) -> list[dict]:
166
+ """List rows from a table."""
167
+ sql = f"SELECT * FROM {resource_type}"
168
+
169
+ if filters:
170
+ conditions = []
171
+ params = []
172
+ for i, (key, value) in enumerate(filters.items()):
173
+ conditions.append(f"{key} = ${i+1}")
174
+ params.append(value)
175
+ sql += " WHERE " + " AND ".join(conditions)
176
+ return await self.query(sql, params)
177
+
178
+ return await self.query(sql)
179
+
180
+ async def _search_impl(self, query: str, **kwargs) -> list[dict]:
181
+ """Execute a raw SQL query."""
182
+ return await self.query(query)
@@ -0,0 +1,202 @@
1
+ """SupabaseConnector — Supabase database integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ import httpx
8
+
9
+ from integrations.base import (
10
+ BaseConnector,
11
+ ConnectorCapability,
12
+ ConnectorInfo,
13
+ )
14
+
15
+ logger = logging.getLogger("hyperclaw.integrations.supabase")
16
+
17
+
18
+ class SupabaseConnector(BaseConnector):
19
+ """
20
+ Supabase database connector.
21
+
22
+ Required config:
23
+ - url: Supabase project URL
24
+ - service_role_key: Supabase service role key
25
+ """
26
+
27
+ def __init__(self, config: dict) -> None:
28
+ self.config = config
29
+ self.url = config.get("url", "").rstrip("/")
30
+ self.service_role_key = config.get("service_role_key", "")
31
+
32
+ if config.get("enabled", False):
33
+ if not self.url or not self.service_role_key:
34
+ raise ValueError(
35
+ "SupabaseConnector requires url and service_role_key"
36
+ )
37
+
38
+ self._client: httpx.AsyncClient | None = None
39
+
40
+ @property
41
+ def info(self) -> ConnectorInfo:
42
+ return ConnectorInfo(
43
+ connector_id="supabase",
44
+ platform="supabase",
45
+ category="data",
46
+ capabilities=frozenset(
47
+ [
48
+ ConnectorCapability.READ_DATA,
49
+ ConnectorCapability.WRITE_DATA,
50
+ ConnectorCapability.DELETE_DATA,
51
+ ConnectorCapability.LIST_DATA,
52
+ ConnectorCapability.SEARCH,
53
+ ]
54
+ ),
55
+ auth_type="api_key",
56
+ rate_limit_per_minute=60,
57
+ )
58
+
59
+ async def _get_client(self) -> httpx.AsyncClient:
60
+ if self._client is None:
61
+ self._client = httpx.AsyncClient(
62
+ timeout=30.0,
63
+ headers={
64
+ "apikey": self.service_role_key,
65
+ "Authorization": f"Bearer {self.service_role_key}",
66
+ "Content-Type": "application/json",
67
+ "Prefer": "return=representation",
68
+ },
69
+ )
70
+ return self._client
71
+
72
+ async def health(self) -> bool:
73
+ """Check if Supabase API is accessible."""
74
+ try:
75
+ client = await self._get_client()
76
+ response = await client.get(f"{self.url}/rest/v1/")
77
+ return response.status_code == 200
78
+ except Exception as e:
79
+ logger.error(f"Supabase health check failed: {e}")
80
+ return False
81
+
82
+ async def query(self, table: str, filters: dict | None = None) -> list[dict]:
83
+ """Query a table with optional filters."""
84
+ client = await self._get_client()
85
+
86
+ url = f"{self.url}/rest/v1/{table}"
87
+ params = {}
88
+
89
+ if filters:
90
+ for key, value in filters.items():
91
+ params[key] = f"eq.{value}"
92
+
93
+ response = await client.get(url, params=params)
94
+
95
+ if response.status_code != 200:
96
+ raise Exception(f"Supabase query failed: {response.text}")
97
+
98
+ return response.json()
99
+
100
+ async def insert(self, table: str, data: dict) -> dict:
101
+ """Insert a row into a table."""
102
+ client = await self._get_client()
103
+
104
+ response = await client.post(
105
+ f"{self.url}/rest/v1/{table}",
106
+ json=data,
107
+ )
108
+
109
+ if response.status_code not in (200, 201):
110
+ raise Exception(f"Supabase insert failed: {response.text}")
111
+
112
+ result = response.json()
113
+ return result[0] if isinstance(result, list) else result
114
+
115
+ async def update(self, table: str, id: str, data: dict) -> dict:
116
+ """Update a row in a table."""
117
+ client = await self._get_client()
118
+
119
+ response = await client.patch(
120
+ f"{self.url}/rest/v1/{table}",
121
+ params={"id": f"eq.{id}"},
122
+ json=data,
123
+ )
124
+
125
+ if response.status_code not in (200, 204):
126
+ raise Exception(f"Supabase update failed: {response.text}")
127
+
128
+ result = response.json()
129
+ return result[0] if isinstance(result, list) and result else {}
130
+
131
+ async def delete(self, table: str, id: str) -> bool:
132
+ """Delete a row from a table."""
133
+ client = await self._get_client()
134
+
135
+ response = await client.delete(
136
+ f"{self.url}/rest/v1/{table}",
137
+ params={"id": f"eq.{id}"},
138
+ )
139
+
140
+ if response.status_code not in (200, 204):
141
+ raise Exception(f"Supabase delete failed: {response.text}")
142
+
143
+ return True
144
+
145
+ async def rpc(self, function_name: str, params: dict | None = None) -> dict:
146
+ """Call a Supabase RPC function."""
147
+ client = await self._get_client()
148
+
149
+ response = await client.post(
150
+ f"{self.url}/rest/v1/rpc/{function_name}",
151
+ json=params or {},
152
+ )
153
+
154
+ if response.status_code != 200:
155
+ raise Exception(f"Supabase rpc failed: {response.text}")
156
+
157
+ return response.json()
158
+
159
+ async def _read_impl(self, resource_id: str, **kwargs) -> dict:
160
+ """Read a row by ID."""
161
+ table = kwargs.get("table", "")
162
+ if not table:
163
+ raise ValueError("table parameter required")
164
+
165
+ result = await self.query(table, {"id": resource_id})
166
+ return result[0] if result else {}
167
+
168
+ async def _write_impl(self, resource_type: str, data: dict, **kwargs) -> dict:
169
+ """Insert a row."""
170
+ return await self.insert(resource_type, data)
171
+
172
+ async def _delete_impl(self, resource_id: str, **kwargs) -> bool:
173
+ """Delete a row."""
174
+ table = kwargs.get("table", "")
175
+ if not table:
176
+ raise ValueError("table parameter required")
177
+
178
+ return await self.delete(table, resource_id)
179
+
180
+ async def _list_impl(
181
+ self, resource_type: str, filters: dict, **kwargs
182
+ ) -> list[dict]:
183
+ """List rows from a table."""
184
+ return await self.query(resource_type, filters)
185
+
186
+ async def _search_impl(self, query: str, **kwargs) -> list[dict]:
187
+ """Search using RPC or full-text search."""
188
+ table = kwargs.get("table", "")
189
+ column = kwargs.get("column", "")
190
+
191
+ if table and column:
192
+ # Use PostgREST full-text search
193
+ client = await self._get_client()
194
+ response = await client.get(
195
+ f"{self.url}/rest/v1/{table}",
196
+ params={column: f"ilike.*{query}*"},
197
+ )
198
+ if response.status_code == 200:
199
+ return response.json()
200
+
201
+ # Fallback to RPC if available
202
+ return await self.rpc("search", {"query": query})