quorus-ai 0.5.0__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.
Files changed (250) hide show
  1. quorus_ai-0.5.0/.dockerignore +14 -0
  2. quorus_ai-0.5.0/.env.example +29 -0
  3. quorus_ai-0.5.0/.env.prod.example +26 -0
  4. quorus_ai-0.5.0/.gitignore +32 -0
  5. quorus_ai-0.5.0/.graphifyignore +96 -0
  6. quorus_ai-0.5.0/.hatchignore +13 -0
  7. quorus_ai-0.5.0/AGENTS.md +13 -0
  8. quorus_ai-0.5.0/CHANGELOG.md +186 -0
  9. quorus_ai-0.5.0/CLAUDE.md +59 -0
  10. quorus_ai-0.5.0/CONTRIBUTING.md +58 -0
  11. quorus_ai-0.5.0/Dockerfile +70 -0
  12. quorus_ai-0.5.0/LICENSE +21 -0
  13. quorus_ai-0.5.0/PKG-INFO +265 -0
  14. quorus_ai-0.5.0/README.md +215 -0
  15. quorus_ai-0.5.0/alembic.ini +37 -0
  16. quorus_ai-0.5.0/conftest.py +13 -0
  17. quorus_ai-0.5.0/docker-compose.prod.yml +105 -0
  18. quorus_ai-0.5.0/docker-compose.yml +67 -0
  19. quorus_ai-0.5.0/examples/demo_swarm.py +166 -0
  20. quorus_ai-0.5.0/fly.toml +71 -0
  21. quorus_ai-0.5.0/packages/README.md +51 -0
  22. quorus_ai-0.5.0/packages/cli/README.md +24 -0
  23. quorus_ai-0.5.0/packages/cli/pyproject.toml +40 -0
  24. quorus_ai-0.5.0/packages/cli/quorus_cli/__init__.py +5 -0
  25. quorus_ai-0.5.0/packages/cli/quorus_cli/agent_cmd.py +498 -0
  26. quorus_ai-0.5.0/packages/cli/quorus_cli/agent_proc.py +96 -0
  27. quorus_ai-0.5.0/packages/cli/quorus_cli/bundle_cmd.py +92 -0
  28. quorus_ai-0.5.0/packages/cli/quorus_cli/claude_agent.py +1137 -0
  29. quorus_ai-0.5.0/packages/cli/quorus_cli/cli.py +9979 -0
  30. quorus_ai-0.5.0/packages/cli/quorus_cli/codex_agent.py +1126 -0
  31. quorus_ai-0.5.0/packages/cli/quorus_cli/decorators.py +189 -0
  32. quorus_ai-0.5.0/packages/cli/quorus_cli/demo_cmd.py +345 -0
  33. quorus_ai-0.5.0/packages/cli/quorus_cli/duel_cmd.py +281 -0
  34. quorus_ai-0.5.0/packages/cli/quorus_cli/gemini_agent.py +897 -0
  35. quorus_ai-0.5.0/packages/cli/quorus_cli/hooks.py +566 -0
  36. quorus_ai-0.5.0/packages/cli/quorus_cli/live_sessions.py +128 -0
  37. quorus_ai-0.5.0/packages/cli/quorus_cli/mcp_writers.py +596 -0
  38. quorus_ai-0.5.0/packages/cli/quorus_cli/phone_cmd.py +144 -0
  39. quorus_ai-0.5.0/packages/cli/quorus_cli/quickstart.py +134 -0
  40. quorus_ai-0.5.0/packages/cli/quorus_cli/reflex_status.py +370 -0
  41. quorus_ai-0.5.0/packages/cli/quorus_cli/replay_cmd.py +196 -0
  42. quorus_ai-0.5.0/packages/cli/quorus_cli/retro_cmd.py +60 -0
  43. quorus_ai-0.5.0/packages/cli/quorus_cli/scorecard_cmd.py +79 -0
  44. quorus_ai-0.5.0/packages/cli/quorus_cli/ui.py +330 -0
  45. quorus_ai-0.5.0/packages/cli/quorus_cli/watcher.py +257 -0
  46. quorus_ai-0.5.0/packages/mcp/README.md +3 -0
  47. quorus_ai-0.5.0/packages/mcp/pyproject.toml +40 -0
  48. quorus_ai-0.5.0/packages/mcp/quorus_mcp/__init__.py +31 -0
  49. quorus_ai-0.5.0/packages/mcp/quorus_mcp/bundle_tools.py +80 -0
  50. quorus_ai-0.5.0/packages/mcp/quorus_mcp/channel.py +72 -0
  51. quorus_ai-0.5.0/packages/mcp/quorus_mcp/phase1_registry.py +124 -0
  52. quorus_ai-0.5.0/packages/mcp/quorus_mcp/phase1_tools.py +543 -0
  53. quorus_ai-0.5.0/packages/mcp/quorus_mcp/runtime.py +384 -0
  54. quorus_ai-0.5.0/packages/mcp/quorus_mcp/server.py +448 -0
  55. quorus_ai-0.5.0/packages/mcp/quorus_mcp/sse.py +178 -0
  56. quorus_ai-0.5.0/packages/mcp/quorus_mcp/tools.py +504 -0
  57. quorus_ai-0.5.0/packages/sdk/README.md +28 -0
  58. quorus_ai-0.5.0/packages/sdk/pyproject.toml +39 -0
  59. quorus_ai-0.5.0/packages/sdk/quorus_sdk/__init__.py +6 -0
  60. quorus_ai-0.5.0/packages/sdk/quorus_sdk/http_agent.py +204 -0
  61. quorus_ai-0.5.0/packages/sdk/quorus_sdk/sdk.py +415 -0
  62. quorus_ai-0.5.0/packages/tui/README.md +22 -0
  63. quorus_ai-0.5.0/packages/tui/pyproject.toml +40 -0
  64. quorus_ai-0.5.0/packages/tui/quorus_tui/__init__.py +5 -0
  65. quorus_ai-0.5.0/packages/tui/quorus_tui/approvals_panel.py +112 -0
  66. quorus_ai-0.5.0/packages/tui/quorus_tui/autocomplete.py +397 -0
  67. quorus_ai-0.5.0/packages/tui/quorus_tui/catchup.py +114 -0
  68. quorus_ai-0.5.0/packages/tui/quorus_tui/chat.py +821 -0
  69. quorus_ai-0.5.0/packages/tui/quorus_tui/chat_widgets.py +856 -0
  70. quorus_ai-0.5.0/packages/tui/quorus_tui/dm_inbox.py +149 -0
  71. quorus_ai-0.5.0/packages/tui/quorus_tui/hub.py +4051 -0
  72. quorus_ai-0.5.0/packages/tui/quorus_tui/render.py +143 -0
  73. quorus_ai-0.5.0/packages/tui/quorus_tui/slash.py +243 -0
  74. quorus_ai-0.5.0/packages/tui/quorus_tui/welcome.py +324 -0
  75. quorus_ai-0.5.0/packages/tui/quorus_tui/work_queue_panel.py +217 -0
  76. quorus_ai-0.5.0/packages/tui/quorus_tui/working.py +112 -0
  77. quorus_ai-0.5.0/pyproject.toml +144 -0
  78. quorus_ai-0.5.0/quorus/__init__.py +16 -0
  79. quorus_ai-0.5.0/quorus/admin/__init__.py +1 -0
  80. quorus_ai-0.5.0/quorus/admin/models.py +247 -0
  81. quorus_ai-0.5.0/quorus/admin/routes.py +424 -0
  82. quorus_ai-0.5.0/quorus/analytics.py +106 -0
  83. quorus_ai-0.5.0/quorus/auth/__init__.py +1 -0
  84. quorus_ai-0.5.0/quorus/auth/middleware.py +263 -0
  85. quorus_ai-0.5.0/quorus/auth/participant_keys.py +125 -0
  86. quorus_ai-0.5.0/quorus/auth/participant_routes.py +90 -0
  87. quorus_ai-0.5.0/quorus/auth/policy.py +448 -0
  88. quorus_ai-0.5.0/quorus/auth/routes.py +628 -0
  89. quorus_ai-0.5.0/quorus/auth/tokens.py +138 -0
  90. quorus_ai-0.5.0/quorus/backends/__init__.py +4 -0
  91. quorus_ai-0.5.0/quorus/backends/memory.py +1292 -0
  92. quorus_ai-0.5.0/quorus/backends/postgres_history.py +302 -0
  93. quorus_ai-0.5.0/quorus/backends/protocol.py +628 -0
  94. quorus_ai-0.5.0/quorus/backends/redis_backends.py +1947 -0
  95. quorus_ai-0.5.0/quorus/backends/redis_client.py +110 -0
  96. quorus_ai-0.5.0/quorus/backends/sqlite_history.py +287 -0
  97. quorus_ai-0.5.0/quorus/bundle_client.py +228 -0
  98. quorus_ai-0.5.0/quorus/cli.py +27 -0
  99. quorus_ai-0.5.0/quorus/config.py +310 -0
  100. quorus_ai-0.5.0/quorus/dashboard.py +274 -0
  101. quorus_ai-0.5.0/quorus/decorators.py +19 -0
  102. quorus_ai-0.5.0/quorus/integrations/__init__.py +0 -0
  103. quorus_ai-0.5.0/quorus/integrations/http_agent.py +12 -0
  104. quorus_ai-0.5.0/quorus/integrations/quorus-client.ts +116 -0
  105. quorus_ai-0.5.0/quorus/integrations/watcher.py +134 -0
  106. quorus_ai-0.5.0/quorus/ledger.py +297 -0
  107. quorus_ai-0.5.0/quorus/mcp_server.py +27 -0
  108. quorus_ai-0.5.0/quorus/migrations/env.py +79 -0
  109. quorus_ai-0.5.0/quorus/migrations/script.py.mako +24 -0
  110. quorus_ai-0.5.0/quorus/migrations/versions/001_initial_admin_schema.py +77 -0
  111. quorus_ai-0.5.0/quorus/migrations/versions/002_rooms_and_messages.py +140 -0
  112. quorus_ai-0.5.0/quorus/migrations/versions/003_add_reply_to.py +32 -0
  113. quorus_ai-0.5.0/quorus/migrations/versions/004_drop_room_fk.py +39 -0
  114. quorus_ai-0.5.0/quorus/migrations/versions/005_add_room_name.py +30 -0
  115. quorus_ai-0.5.0/quorus/migrations/versions/006_message_outbox.py +82 -0
  116. quorus_ai-0.5.0/quorus/migrations/versions/007_audit_ledger.py +75 -0
  117. quorus_ai-0.5.0/quorus/migrations/versions/008_account_identity.py +126 -0
  118. quorus_ai-0.5.0/quorus/migrations/versions/009_audit_event_created_idx.py +39 -0
  119. quorus_ai-0.5.0/quorus/migrations/versions/010_join_codes.py +56 -0
  120. quorus_ai-0.5.0/quorus/migrations/versions/011_audit_hash_chain.py +31 -0
  121. quorus_ai-0.5.0/quorus/migrations/versions/012_outbox_next_attempt_at.py +55 -0
  122. quorus_ai-0.5.0/quorus/models/__init__.py +21 -0
  123. quorus_ai-0.5.0/quorus/models/account.py +118 -0
  124. quorus_ai-0.5.0/quorus/models/api_key.py +177 -0
  125. quorus_ai-0.5.0/quorus/models/audit.py +106 -0
  126. quorus_ai-0.5.0/quorus/models/join_code.py +34 -0
  127. quorus_ai-0.5.0/quorus/models/outbox.py +92 -0
  128. quorus_ai-0.5.0/quorus/notifications/__init__.py +11 -0
  129. quorus_ai-0.5.0/quorus/notifications/agent_events.py +120 -0
  130. quorus_ai-0.5.0/quorus/notifications/native.py +333 -0
  131. quorus_ai-0.5.0/quorus/observability/__init__.py +1 -0
  132. quorus_ai-0.5.0/quorus/observability/redaction.py +65 -0
  133. quorus_ai-0.5.0/quorus/observability/sentry.py +107 -0
  134. quorus_ai-0.5.0/quorus/operating_discipline.py +133 -0
  135. quorus_ai-0.5.0/quorus/private_files.py +39 -0
  136. quorus_ai-0.5.0/quorus/profiles.py +203 -0
  137. quorus_ai-0.5.0/quorus/protocol/__init__.py +36 -0
  138. quorus_ai-0.5.0/quorus/protocol/social_verbs.py +229 -0
  139. quorus_ai-0.5.0/quorus/relay.py +945 -0
  140. quorus_ai-0.5.0/quorus/relay_cli.py +64 -0
  141. quorus_ai-0.5.0/quorus/relay_routes.py +45 -0
  142. quorus_ai-0.5.0/quorus/retro.py +149 -0
  143. quorus_ai-0.5.0/quorus/routes/__init__.py +50 -0
  144. quorus_ai-0.5.0/quorus/routes/admin_dashboard.py +470 -0
  145. quorus_ai-0.5.0/quorus/routes/admin_metrics.py +243 -0
  146. quorus_ai-0.5.0/quorus/routes/agent_dm.py +568 -0
  147. quorus_ai-0.5.0/quorus/routes/agents.py +97 -0
  148. quorus_ai-0.5.0/quorus/routes/analytics.py +91 -0
  149. quorus_ai-0.5.0/quorus/routes/approvals.py +247 -0
  150. quorus_ai-0.5.0/quorus/routes/audit.py +169 -0
  151. quorus_ai-0.5.0/quorus/routes/bundles.py +112 -0
  152. quorus_ai-0.5.0/quorus/routes/capabilities.py +229 -0
  153. quorus_ai-0.5.0/quorus/routes/health.py +97 -0
  154. quorus_ai-0.5.0/quorus/routes/helpers.py +100 -0
  155. quorus_ai-0.5.0/quorus/routes/invites.py +187 -0
  156. quorus_ai-0.5.0/quorus/routes/join_codes.py +351 -0
  157. quorus_ai-0.5.0/quorus/routes/messages.py +209 -0
  158. quorus_ai-0.5.0/quorus/routes/models.py +208 -0
  159. quorus_ai-0.5.0/quorus/routes/persistent_memory.py +329 -0
  160. quorus_ai-0.5.0/quorus/routes/presence.py +62 -0
  161. quorus_ai-0.5.0/quorus/routes/room_auth.py +68 -0
  162. quorus_ai-0.5.0/quorus/routes/room_messages.py +304 -0
  163. quorus_ai-0.5.0/quorus/routes/room_state.py +442 -0
  164. quorus_ai-0.5.0/quorus/routes/rooms.py +270 -0
  165. quorus_ai-0.5.0/quorus/routes/social.py +403 -0
  166. quorus_ai-0.5.0/quorus/routes/sse.py +146 -0
  167. quorus_ai-0.5.0/quorus/routes/tool_catalog.py +276 -0
  168. quorus_ai-0.5.0/quorus/routes/triage.py +482 -0
  169. quorus_ai-0.5.0/quorus/routes/triage_redis.py +231 -0
  170. quorus_ai-0.5.0/quorus/routes/usage.py +181 -0
  171. quorus_ai-0.5.0/quorus/routes/webhooks.py +146 -0
  172. quorus_ai-0.5.0/quorus/routes/work_queue.py +343 -0
  173. quorus_ai-0.5.0/quorus/runtime/__init__.py +31 -0
  174. quorus_ai-0.5.0/quorus/runtime/failover.py +162 -0
  175. quorus_ai-0.5.0/quorus/runtime/launchd.py +96 -0
  176. quorus_ai-0.5.0/quorus/runtime/local_relay.py +80 -0
  177. quorus_ai-0.5.0/quorus/runtime/memory.py +541 -0
  178. quorus_ai-0.5.0/quorus/runtime/opencode_free.py +68 -0
  179. quorus_ai-0.5.0/quorus/runtime/review_share.py +93 -0
  180. quorus_ai-0.5.0/quorus/runtime/supervisor.py +347 -0
  181. quorus_ai-0.5.0/quorus/runtime/turnguard.py +241 -0
  182. quorus_ai-0.5.0/quorus/sdk.py +12 -0
  183. quorus_ai-0.5.0/quorus/services/__init__.py +1 -0
  184. quorus_ai-0.5.0/quorus/services/analytics_svc.py +27 -0
  185. quorus_ai-0.5.0/quorus/services/approval_svc.py +261 -0
  186. quorus_ai-0.5.0/quorus/services/audit_svc.py +435 -0
  187. quorus_ai-0.5.0/quorus/services/bundle_svc.py +301 -0
  188. quorus_ai-0.5.0/quorus/services/capability_svc.py +176 -0
  189. quorus_ai-0.5.0/quorus/services/invite_svc.py +76 -0
  190. quorus_ai-0.5.0/quorus/services/join_code_svc.py +274 -0
  191. quorus_ai-0.5.0/quorus/services/message_svc.py +459 -0
  192. quorus_ai-0.5.0/quorus/services/notification_svc.py +157 -0
  193. quorus_ai-0.5.0/quorus/services/outbox_svc.py +440 -0
  194. quorus_ai-0.5.0/quorus/services/p1_persistence.py +195 -0
  195. quorus_ai-0.5.0/quorus/services/persistent_memory_svc.py +266 -0
  196. quorus_ai-0.5.0/quorus/services/presence_svc.py +31 -0
  197. quorus_ai-0.5.0/quorus/services/rate_limit_svc.py +117 -0
  198. quorus_ai-0.5.0/quorus/services/room_msg_svc.py +391 -0
  199. quorus_ai-0.5.0/quorus/services/room_svc.py +280 -0
  200. quorus_ai-0.5.0/quorus/services/social_svc.py +567 -0
  201. quorus_ai-0.5.0/quorus/services/sse_svc.py +161 -0
  202. quorus_ai-0.5.0/quorus/services/tool_catalog_svc.py +226 -0
  203. quorus_ai-0.5.0/quorus/services/webhook_svc.py +664 -0
  204. quorus_ai-0.5.0/quorus/services/work_queue_svc.py +671 -0
  205. quorus_ai-0.5.0/quorus/storage/__init__.py +1 -0
  206. quorus_ai-0.5.0/quorus/storage/base.py +16 -0
  207. quorus_ai-0.5.0/quorus/storage/postgres.py +114 -0
  208. quorus_ai-0.5.0/quorus/tui_hub.py +17 -0
  209. quorus_ai-0.5.0/railway.json +14 -0
  210. quorus_ai-0.5.0/railway.toml +27 -0
  211. quorus_ai-0.5.0/render.yaml +38 -0
  212. quorus_ai-0.5.0/requirements-dev.txt +4 -0
  213. quorus_ai-0.5.0/requirements.lock +166 -0
  214. quorus_ai-0.5.0/requirements.txt +5 -0
  215. quorus_ai-0.5.0/scripts/_mint_aarya.sh +29 -0
  216. quorus_ai-0.5.0/scripts/autopull.sh +14 -0
  217. quorus_ai-0.5.0/scripts/cold_install_smoke.py +412 -0
  218. quorus_ai-0.5.0/scripts/cold_install_smoke.sh +129 -0
  219. quorus_ai-0.5.0/scripts/demo_reflex.sh +513 -0
  220. quorus_ai-0.5.0/scripts/demo_wake.sh +281 -0
  221. quorus_ai-0.5.0/scripts/dogfood.sh +294 -0
  222. quorus_ai-0.5.0/scripts/e2e_live.py +402 -0
  223. quorus_ai-0.5.0/scripts/fix_editable_pth.sh +58 -0
  224. quorus_ai-0.5.0/scripts/heartbeat-local.sh +33 -0
  225. quorus_ai-0.5.0/scripts/heartbeat.sh +31 -0
  226. quorus_ai-0.5.0/scripts/install.sh +70 -0
  227. quorus_ai-0.5.0/scripts/kliment_demo.sh +384 -0
  228. quorus_ai-0.5.0/scripts/launch_smoke.sh +161 -0
  229. quorus_ai-0.5.0/scripts/loop.sh +5 -0
  230. quorus_ai-0.5.0/scripts/patch_entry_points.sh +70 -0
  231. quorus_ai-0.5.0/scripts/quorus-autosync.sh +83 -0
  232. quorus_ai-0.5.0/scripts/quorus-coordination.skill +0 -0
  233. quorus_ai-0.5.0/scripts/quorus_codex_loop.py +396 -0
  234. quorus_ai-0.5.0/scripts/record_demo_cast.sh +421 -0
  235. quorus_ai-0.5.0/scripts/record_kliment_cast.sh +219 -0
  236. quorus_ai-0.5.0/scripts/reflexd.py +4397 -0
  237. quorus_ai-0.5.0/scripts/reflexd_models.py +321 -0
  238. quorus_ai-0.5.0/scripts/reflexd_readonly.py +162 -0
  239. quorus_ai-0.5.0/scripts/reflexd_smoke.sh +108 -0
  240. quorus_ai-0.5.0/scripts/reflexd_steps.py +191 -0
  241. quorus_ai-0.5.0/scripts/reflexd_streamb.py +197 -0
  242. quorus_ai-0.5.0/scripts/reflexd_triage.py +607 -0
  243. quorus_ai-0.5.0/scripts/reflexd_wake.py +800 -0
  244. quorus_ai-0.5.0/scripts/rehearse_runbook.sh +249 -0
  245. quorus_ai-0.5.0/scripts/replay_kliment_cast.sh +55 -0
  246. quorus_ai-0.5.0/scripts/scenarios.py +559 -0
  247. quorus_ai-0.5.0/scripts/stall_demo_local.sh +557 -0
  248. quorus_ai-0.5.0/scripts/stall_install_smoketest.sh +114 -0
  249. quorus_ai-0.5.0/scripts/stall_setup_aarya.sh +215 -0
  250. quorus_ai-0.5.0/setup.sh +178 -0
@@ -0,0 +1,14 @@
1
+ __pycache__
2
+ *.pyc
3
+ .pytest_cache
4
+ .ruff_cache
5
+ .git
6
+ .gitignore
7
+ tests/
8
+ CLAUDE.md
9
+ docs/internal/
10
+ CONTRIBUTING.md
11
+ .claude/
12
+ .mcp.json
13
+ messages.json
14
+ uv.lock
@@ -0,0 +1,29 @@
1
+ # Murmur Relay — Environment Variables
2
+ # Copy to .env and fill in values, then: docker-compose up
3
+
4
+ # Required: Shared secret for legacy auth (agents without API keys)
5
+ RELAY_SECRET=change-me-to-a-long-random-string
6
+
7
+ # Required: Secret for signing JWTs (min 32 chars)
8
+ JWT_SECRET=change-me-to-a-long-random-secret-min-32-chars
9
+
10
+ # Required: Bootstrap secret for creating the first tenant
11
+ BOOTSTRAP_SECRET=change-me-bootstrap-secret
12
+
13
+ # Optional: Server port (default 8080)
14
+ PORT=8080
15
+
16
+ # Optional: Postgres (defaults to local docker-compose postgres)
17
+ # DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/murmur
18
+
19
+ # Optional: Redis (defaults to local docker-compose redis)
20
+ # REDIS_URL=redis://host:6379/0
21
+
22
+ # Optional: Logging level (DEBUG, INFO, WARNING, ERROR)
23
+ LOG_LEVEL=INFO
24
+
25
+ # Optional: Message limits
26
+ MAX_MESSAGES=1000
27
+ MESSAGE_TTL_SECONDS=86400
28
+ RATE_LIMIT_MAX=60
29
+ MAX_ROOM_MEMBERS=50
@@ -0,0 +1,26 @@
1
+ # Production environment template
2
+ # Copy to .env.prod and fill in values
3
+
4
+ # Required secrets (generate with: openssl rand -hex 32)
5
+ JWT_SECRET=your-jwt-secret-min-32-chars-here
6
+ BOOTSTRAP_SECRET=your-bootstrap-secret-here
7
+ RELAY_SECRET=your-relay-secret-here
8
+ POSTGRES_PASSWORD=your-strong-postgres-password
9
+ REDIS_PASSWORD=your-strong-redis-password
10
+
11
+ # Optional: Redis memory limit (default 512mb)
12
+ # REDIS_MAXMEMORY=1gb
13
+
14
+ # Optional: Relay settings
15
+ # PORT=8080
16
+ # LOG_LEVEL=WARNING
17
+ # MESSAGE_TTL_SECONDS=86400
18
+ # VISIBILITY_TIMEOUT_SECONDS=60
19
+ # IDEMPOTENCY_TTL_SECONDS=300
20
+ # RATE_LIMIT_WINDOW=60
21
+ # RATE_LIMIT_MAX=60
22
+
23
+ # Sentry — set in production via `flyctl secrets set SENTRY_DSN=...`.
24
+ # Leave unset locally; init_sentry() is a no-op without a DSN.
25
+ # SENTRY_DSN=
26
+ # SENTRY_TRACES_SAMPLE_RATE=0.05
@@ -0,0 +1,32 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .venv/
11
+ .env
12
+ messages.json
13
+ .claude/
14
+ uv.lock
15
+ .DS_Store
16
+ ROADMAP.md
17
+ docs/launch/
18
+ .mcp.json
19
+ .murmur/
20
+ .vercel
21
+ *.mcp.*.json
22
+ website/vite.config.d.ts
23
+ website/vite.config.js
24
+ website/*.tsbuildinfo
25
+ MagicMock/
26
+
27
+ # Graphify local knowledge graph (do not commit)
28
+ # Share only the readable graph report and page; rebuild the rest with `graphify update .`
29
+ graphify-out/*
30
+ !graphify-out/GRAPH_REPORT.md
31
+ !graphify-out/graph.html
32
+ .graphify/
@@ -0,0 +1,96 @@
1
+ # --- generated by the graphify sweep, 2026-08-28 ---
2
+ # Mirrors Rokan's .graphifyignore. The secrets block at the bottom matters
3
+ # only once a semantic pass runs: the free AST pass reads symbol names,
4
+ # but a semantic pass reads file CONTENT and sends it to a model.
5
+
6
+ # Design assets (not code)
7
+ design/
8
+ *.html
9
+ *.pdf
10
+ *.png
11
+ *.jpg
12
+ *.jpeg
13
+ *.svg
14
+ *.gif
15
+ *.mp4
16
+ *.mov
17
+
18
+ # Dependencies
19
+ node_modules/
20
+ __pycache__/
21
+ .venv/
22
+ venv/
23
+ target/
24
+ Pods/
25
+ vendor/
26
+
27
+ # Build outputs
28
+ dist/
29
+ build/
30
+ .next/
31
+ .expo/
32
+ out/
33
+ DerivedData/
34
+
35
+ # Lock files
36
+ package-lock.json
37
+ yarn.lock
38
+ pnpm-lock.yaml
39
+ Cargo.lock
40
+ Podfile.lock
41
+ poetry.lock
42
+ uv.lock
43
+
44
+ # Generated
45
+ *.tsbuildinfo
46
+ *.generated.ts
47
+ *.generated.js
48
+ *.pb.go
49
+ *_pb2.py
50
+
51
+ # Test fixtures (boilerplate, low signal)
52
+ **/__fixtures__/
53
+ **/fixtures/
54
+ **/snapshots/
55
+
56
+ # Cache
57
+ .cache/
58
+ .parcel-cache/
59
+ .pytest_cache/
60
+ .mypy_cache/
61
+ .ruff_cache/
62
+
63
+ # Git
64
+ .git/
65
+
66
+ # Scratch
67
+ memory/
68
+ scratch/
69
+ experiments/
70
+ tmp/
71
+
72
+ # --- secrets and credentials: never into a graph, never to a model ---
73
+ .env
74
+ .env.*
75
+ *.pem
76
+ *.key
77
+ *.p12
78
+ *.keystore
79
+ *.jks
80
+ *.mobileprovision
81
+ secrets/
82
+ credentials/
83
+ *credentials*.json
84
+ service-account*.json
85
+ GoogleService-Info.plist
86
+ google-services.json
87
+ *.sqlite
88
+ *.db
89
+
90
+ # --- patient / personal data, by convention ---
91
+ # No real PHI is believed to live in these repos. Listed anyway, because a
92
+ # convention only protects you if it predates the file that needs it.
93
+ phi/
94
+ patient_data/
95
+ *patients*.csv
96
+ *patients*.json
@@ -0,0 +1,13 @@
1
+ .murmur/
2
+ *.db
3
+ *.db-shm
4
+ *.db-wal
5
+ dist/
6
+ .venv/
7
+ __pycache__/
8
+ *.pyc
9
+ .github/
10
+ tests/
11
+ website/
12
+ docs/
13
+ *.md
@@ -0,0 +1,13 @@
1
+ # AGENTS.md
2
+
3
+ Read CLAUDE.md: it holds the project rules, test and lint commands for every agent.
4
+
5
+ The essentials, for tools that don't read CLAUDE.md:
6
+
7
+ - Python lives in `~/dev/Quorus/.venv`. Don't look for another one, and never
8
+ search the whole disk (`find /`) to find it.
9
+ - Tests, from an agent worktree:
10
+ `PYTHONPATH=.:packages/cli:packages/mcp:packages/sdk:packages/tui ~/dev/Quorus/.venv/bin/python -m pytest -q -x <test files>`
11
+ - Run only the test files you touched; the laptop is shared, so no full-suite
12
+ runs unless you're asked to.
13
+ - Lint: `~/dev/Quorus/.venv/bin/ruff check .`
@@ -0,0 +1,186 @@
1
+ # Changelog
2
+
3
+ All notable changes to Quorus are documented here. This project follows
4
+ [Semantic Versioning](https://semver.org/) and [Keep a Changelog](https://keepachangelog.com/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.5.0] — 2026-10-17
9
+
10
+ First PyPI release, as `quorus-ai` (`pipx install quorus-ai`; the command is still `quorus`).
11
+
12
+ ### Added
13
+
14
+ - **Any model.** `quorus agent add opencode --model <provider/model>` runs any model
15
+ opencode supports: free open-source models (DeepSeek, Qwen), OpenRouter, local Ollama.
16
+ `quorus agent add gemini` runs Gemini CLI (needs a free AI Studio API key). They get the
17
+ same room tools, permission modes and session memory as Claude Code and Codex.
18
+ - **As many agents as you want.** `--name backend` adds `<you>-backend-claude` next to
19
+ `<you>-claude`. `--model` picks the model for any tool.
20
+ - **Every agent answers you.** A human message wakes every agent in the room; agent chatter
21
+ still picks one.
22
+ - **Team lead.** `quorus agent add claude --room R --lead`: the lead splits your request
23
+ into tasks and assigns them, the others answer briefly and wait for an assignment,
24
+ results flow back to the lead, and it tells you when everything is done.
25
+ - **Notifications that reach you.** Your agents ping your desktop when they ship to main,
26
+ ask you something, or get stuck, even with Quorus closed (`QUORUS_NOTIFY=0` to mute).
27
+ - **See who is working.** `⋯ @you-claude working 1m 20s` in the chat while a wake runs.
28
+ - **Catch up.** The home screen shows what shipped, who needs you and who is stuck since
29
+ you last opened Quorus.
30
+ - **Readable chat.** Agent markdown (bold, code, bullets, headings) renders as clean text.
31
+ - Warnings before adding an agent that can't wake (no Gemini key, Ollama not running or
32
+ missing the model).
33
+
34
+ ### Fixed
35
+
36
+ - opencode agents wrote files into the daemon's working directory instead of the repo, and
37
+ manual (read-only) rooms weren't read-only on opencode 2.x.
38
+ - A project's own `opencode.json` could point an agent's room tools at the wrong relay.
39
+ - Codex couldn't post to the room itself: its MCP calls needed an approval nobody could give,
40
+ and a stale `quorus` entry in `~/.codex/config.toml` overrode the agent's identity.
41
+ - Agents committed generated files (`__pycache__`, `.pyc`) to your main branch.
42
+ - Each agent now logs to its own file; a second Ctrl-C no longer prints a traceback.
43
+ - Gemini agents failed on any machine with an old Google login in `~/.gemini`; each Gemini
44
+ agent now gets its own Gemini home and signs in with your API key.
45
+ - `quorus agent remove` crashed on Linux and `quorus agent list` showed nothing there.
46
+ - opencode 1.x (still what `npm i -g opencode-ai` installs) failed every wake.
47
+
48
+ ## [0.4.0] — 2026-10-10
49
+
50
+ Never published to PyPI.
51
+
52
+ ### Added
53
+
54
+ - **Agents that wake on their own.** `quorus agent add claude|codex --room R [--repo P]`
55
+ starts a background agent that wakes when the room needs it, works in its own git
56
+ worktree, publishes to your repo's main branch, and asks a teammate to review.
57
+ - **The agent decides whether to answer.** Every message not addressed to someone else
58
+ wakes one agent, chosen by auction. It answers, or replies `NO_REPLY` to stay quiet.
59
+ Unaddressed agent chatter stops after 6 agent messages in a row.
60
+ - **Permission modes per room:** `--mode default|manual|autonomous`. Manual asks you
61
+ before risky actions (`quorus approvals`, `quorus approve <id>`).
62
+ - **First run just works:** `quorus` with no config sets up a local relay with a generated
63
+ secret, or joins a teammate's relay from an invite.
64
+ - `quorus agent list|remove`, `quorus --version`, readable errors with exit codes
65
+ (2 unreachable, 3 auth, 4 not found, 5 refused).
66
+
67
+ ### Known limits
68
+
69
+ - No hosted relay yet: teammates must reach your relay's address.
70
+ - On a shared-secret relay, manual-mode approvals and kicks are advisory. Use account
71
+ mode for real separation.
72
+ - The background agent uses launchd on macOS and a detached process elsewhere.
73
+
74
+ ### Earlier work included in 0.4.0
75
+
76
+ ### Added — Wave 9 (TUI)
77
+
78
+ - **Work-queue panel** — `Ctrl-W` toggles a fold-up panel above the chat
79
+ feed showing every active task in the current room (`task_id`,
80
+ `claimed_by`, `status`, `started_at`, `eta`). Refetches from
81
+ `GET /v1/work_queue/{room_id}` on open; cached for 30 s so rapid
82
+ toggles don't hammer the relay.
83
+ - **Agent-DM inbox** — `Ctrl-N` (originally `Ctrl-D` in the spec, but
84
+ `Ctrl-D` already binds to QUIT in the TUI input loop and `Ctrl-I`
85
+ shadows Tab; `Ctrl-N` for "Notifications/iNbox" was free of all
86
+ collisions) toggles an iMessage-style preview of the last 8 agent
87
+ DMs. A dedicated background thread subscribes to
88
+ `/stream/dm/{participant}` and appends events into a bounded deque.
89
+ - **Threaded replies** — when any message in the feed carries
90
+ `thread_root_id`, `render_bubble_feed` now reorders so children
91
+ render directly under their root with a `└─` connector and 5-cell
92
+ left indent. Threads default to **collapsed** (a 1-line
93
+ `@sender: snippet · N replies (/expand <id>)` rollup); the new
94
+ `/expand <root_id|all>` slash command flips an entry to expanded.
95
+
96
+ Public beta launch. Full rebrand from Murmur, coordination-layer positioning.
97
+
98
+ ### Added
99
+
100
+ - **Rebrand to Quorus** across package name, CLI entry (`quorus`), config dir
101
+ (`~/.quorus/`), API key prefix (`quorus_sk_`), join token prefix
102
+ (`quorus_join_`), and MCP server name.
103
+ - New `quorus-mcp` console entry point — matches the MCP snippet shown on the
104
+ website so `"command": "quorus-mcp"` works after install.
105
+ - First-class Windsurf integration: `quorus connect windsurf` emits MCP
106
+ config for `~/.codeium/windsurf/mcp_config.json`.
107
+ - `quorus init --config-dir PATH` flag for multi-profile setups (also honors
108
+ `$QUORUS_CONFIG_DIR`).
109
+ - Open Graph / Twitter Card social metadata + `og.svg` on the marketing site.
110
+ - `CHANGELOG.md` (this file), `sitemap.xml`, and `robots.txt` for launch
111
+ hygiene.
112
+ - Copyable one-line install command visible on the website hero.
113
+ - Relay prints a human-friendly listen banner on startup.
114
+
115
+ ### Changed
116
+
117
+ - **Default `USE_OUTBOX=true`** — fan-out goes through the durable outbox by
118
+ default; silent message loss on Redis hiccups is gone.
119
+ - Audit service exceptions propagate (per CLAUDE.md Lessons Learned). Callers
120
+ wrap business + audit writes in a single transaction.
121
+ - `audit_svc.record()` and `record_batch()` accept an optional `session`
122
+ param so outbox/room writers thread their transaction through — no more
123
+ nested-session split-brain ledger.
124
+ - Content-Security-Policy tightened: no more `'unsafe-inline'` on
125
+ `script-src`; added `base-uri`, `form-action`, `img-src data:`.
126
+ - SSE token mint binds `recipient` to the caller identity; legacy auth
127
+ without admin role can no longer mint tokens for arbitrary recipients.
128
+ - JWT decode errors now log the reason (`ExpiredSignatureError` /
129
+ `InvalidTokenError`) instead of silently falling through.
130
+ - 404 rate limiter's in-memory dicts are guarded by `asyncio.Lock` — no more
131
+ read-modify-write races under 404 floods.
132
+ - Doctor separates required vs optional checks (`11/11 required · 0/1
133
+ optional`) so the opt-in hook doesn't read as failure.
134
+ - Website: removed unverified "sub-100ms latency" claim, Copilot logo (no
135
+ real MCP path), stale "6+ platforms" (now honest 5), and CTA `870+ tests`
136
+ pill. `quorus-ai` → `quorus` everywhere.
137
+ - `render.yaml` wires JWT_SECRET, BOOTSTRAP_SECRET, DATABASE_URL, REDIS_URL,
138
+ USE_OUTBOX, ALLOW_LEGACY_AUTH, CORS_ORIGINS.
139
+
140
+ ### Fixed
141
+
142
+ - `/v1/usage` 500 crash for non-admins (`list_by_member` → `list_for_member`).
143
+ - Cursor + Gemini `quorus connect` commands emitted MCP configs pointing at a
144
+ nonexistent path; now emit `"command": "quorus-mcp"`.
145
+ - `quorus join <quorus_join_...>` previously rejected new-prefix tokens; now
146
+ accepts both `quorus_join_` and legacy `murm_join_`.
147
+ - Config file TOCTOU race — now `os.open(..., 0o600)` atomic write.
148
+ - Hero particle canvas respects `prefers-reduced-motion` and pauses when
149
+ off-screen (battery + INP).
150
+ - Waitlist emails: rebrand sender defaults to `@quorus.dev`, use `waitUntil`
151
+ so Vercel serverless doesn't freeze before send completes.
152
+ - Footer anchor `#howit` pointed at a non-rendered section; now `#architecture`.
153
+ - AgentShowcase branding banner: MURMUR → QUORUS.
154
+ - TUI guard against `selected is None` crash on fresh install.
155
+ - Vite sourcemaps no longer shipped as public `.map` files.
156
+
157
+ ### Security
158
+
159
+ - See `Changed` above for CSP, SSE-binding, JWT logging, and 404 race fixes.
160
+ - Four critical items fixed this cycle: osascript injection in watcher,
161
+ agent-name shell injection in spawn, bootstrap-secret timing attack
162
+ (`hmac.compare_digest`), config-write TOCTOU.
163
+ - IP rate limits added to invite-join (10/min) and admin tenant creation
164
+ (5/min).
165
+ - Invite page template values are `html.escape`d.
166
+
167
+ ### Known limitations (beta-framed)
168
+
169
+ - Not on PyPI yet — install via `pipx install "quorus @ git+..."`.
170
+ - Load/chaos tests not yet run. Concurrency envelope unknown.
171
+ - Webhook egress relies on app-level private-IP blocklist (no network-level
172
+ proxy).
173
+ - Admin tooling for DLQ replay and tenant suspension is CLI-only.
174
+
175
+ ---
176
+
177
+ ## [0.3.0] — 2026-04-13 (pre-rebrand, Murmur)
178
+
179
+ - Monorepo split into packages/sdk, packages/cli, packages/mcp, packages/tui.
180
+ - Transactional outbox pattern (optional, `USE_OUTBOX=true`).
181
+ - Audit ledger with `/v1/audit/*` endpoints.
182
+ - Account-based identity — `participant_id` in JWT claims + FK columns.
183
+
184
+ ## [0.2.x] — earlier (pre-outbox)
185
+
186
+ See git history.
@@ -0,0 +1,59 @@
1
+ # CLAUDE.md — Quorus
2
+
3
+ ## Shared Context Protocol
4
+
5
+ **Read `docs/internal/CONTEXT.md` at the start of every session.** It contains the current project state, what's in progress, recent changes, architectural decisions, and next priorities. It is the shared memory between all contributors' Claude instances.
6
+
7
+ **Update `docs/internal/CONTEXT.md` after every significant change:**
8
+
9
+ - Add your changes to "Recent Changes" (keep last 10, drop oldest)
10
+ - Update "In Progress" when starting/finishing work
11
+ - Update "Current State" if the system's capabilities changed
12
+ - Add to "Decisions Made" when architectural choices are made
13
+ - Update "Next Up" when priorities shift
14
+
15
+ ---
16
+
17
+ ## Project
18
+
19
+ Quorus — coordination layer for AI agent swarms. Real-time rooms, messaging, distributed locks.
20
+
21
+ **Stack:** Python 3.10+, FastAPI, asyncio, httpx, mcp (FastMCP), pytest, ruff, rich
22
+
23
+ **Run tests:** `pytest -v`
24
+ **From an agent worktree** (no venv of its own): `PYTHONPATH=.:packages/cli:packages/mcp:packages/sdk:packages/tui ~/dev/Quorus/.venv/bin/python -m pytest -q`
25
+ **Lint:** `ruff check .`
26
+ **Lint fix:** `ruff check . --fix`
27
+
28
+ ---
29
+
30
+ ## Architecture
31
+
32
+ ```
33
+ Claude Code A -> MCP Server (stdio) -> HTTP -> [Quorus Relay (FastAPI)] -> HTTP -> MCP Server (stdio) -> Claude Code B
34
+ ```
35
+
36
+ - `quorus/relay.py` — Central relay (FastAPI). Rooms, SSE fan-out, persistence, rate limiting.
37
+ - `quorus_mcp/server.py` — MCP server. 12 tools for coordination.
38
+ - `quorus/config.py` — Config loading. Priority: env vars > ~/.quorus/config.json > legacy fallback.
39
+ - `quorus_cli/cli.py` — CLI commands. `quorus` opens TUI by default.
40
+
41
+ ---
42
+
43
+ ## Rules
44
+
45
+ - Files under 500 lines. Split if larger.
46
+ - Async-first. Use `asyncio.to_thread` for blocking I/O.
47
+ - All external input validated before use.
48
+ - Never log secrets or tokens.
49
+ - Tests required for new features and bug fixes.
50
+ - Conventional commits, under 50 chars, imperative mood.
51
+
52
+ ## graphify
53
+
54
+ This project has a graphify knowledge graph at graphify-out/.
55
+
56
+ Rules:
57
+ - Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
58
+ - If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
59
+ - After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)
@@ -0,0 +1,58 @@
1
+ # Contributing to Murmur
2
+
3
+ Thanks for your interest in contributing to Murmur!
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/Quorus-dev/Quorus.git
9
+ cd Quorus
10
+ python3 -m venv .venv && source .venv/bin/activate
11
+ pip install -e ".[dev]" -e packages/sdk -e packages/cli -e packages/mcp -e packages/tui
12
+ # macOS only: undo a hatchling-editable + Python 3.14 + macOS interaction
13
+ # that hides .pth files via com.apple.provenance + UF_HIDDEN. Idempotent.
14
+ bash scripts/fix_editable_pth.sh
15
+ python -c "import quorus, quorus_sdk, quorus_cli, quorus_mcp, quorus_tui" # must print nothing
16
+ ```
17
+
18
+ ## Running Tests
19
+
20
+ ```bash
21
+ pytest -v
22
+ ```
23
+
24
+ ## Running the E2E Test
25
+
26
+ ```bash
27
+ python scripts/e2e_live.py
28
+ ```
29
+
30
+ ## Code Standards
31
+
32
+ - Python 3.10+, async-first
33
+ - Ruff for linting: `ruff check .`
34
+ - Max 100 char lines, files under 500 lines
35
+ - All external input validated before use
36
+ - Never log secrets or tokens
37
+ - Tests required for new features and bug fixes
38
+
39
+ ## Commit Conventions
40
+
41
+ - Conventional commits: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`
42
+ - Under 50 chars, imperative mood
43
+ - One logical change per commit
44
+
45
+ ## Submitting Changes
46
+
47
+ 1. Fork the repo and create a branch
48
+ 2. Make your changes with tests
49
+ 3. Run `ruff check .` and `pytest -v`
50
+ 4. Submit a pull request
51
+
52
+ ## Architecture
53
+
54
+ See `docs/internal/CONTEXT.md` for current architecture and design decisions.
55
+
56
+ ## License
57
+
58
+ By contributing, you agree that your contributions will be licensed under the MIT License.
@@ -0,0 +1,70 @@
1
+ # syntax=docker/dockerfile:1.7
2
+ # ---------------------------------------------------------------------------
3
+ # Stage 1 — builder: install pinned dependencies + build the wheel.
4
+ # Ships pip + build deps + cached wheels.
5
+ # ---------------------------------------------------------------------------
6
+ FROM python:3.12-slim AS builder
7
+
8
+ WORKDIR /build
9
+
10
+ # Build deps + uv (10x faster pip resolver, deterministic locks).
11
+ ENV PIP_NO_CACHE_DIR=1 \
12
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
13
+ PYTHONDONTWRITEBYTECODE=1
14
+
15
+ # Install into an isolated prefix so we can copy the resolved tree forward
16
+ # into the runtime stage without dragging pip / wheels along with it.
17
+ ENV PREFIX=/install
18
+ RUN mkdir -p "$PREFIX"
19
+
20
+ # Pinned deps from the lockfile (single source of truth — see pyproject.toml
21
+ # header). The lockfile must be regenerated via ``uv pip compile`` whenever
22
+ # pyproject.toml changes. Both files commit in the same PR.
23
+ COPY requirements.lock ./
24
+ RUN pip install --no-cache-dir --prefix="$PREFIX" -r requirements.lock
25
+
26
+ # Source layout: hatchling's wheel pulls every package listed in
27
+ # [tool.hatch.build.targets.wheel].packages — quorus/ + packages/* monorepo.
28
+ COPY pyproject.toml README.md alembic.ini ./
29
+ COPY quorus/ quorus/
30
+ COPY packages/ packages/
31
+
32
+ # Install Quorus itself (no deps — those came from the lockfile above).
33
+ RUN pip install --no-cache-dir --prefix="$PREFIX" --no-deps .
34
+
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Stage 2 — runtime: lean image, no pip / build deps / wheel cache.
38
+ # ---------------------------------------------------------------------------
39
+ FROM python:3.12-slim AS runtime
40
+
41
+ WORKDIR /app
42
+
43
+ # Carry the resolved Python install forward; pip itself is left behind.
44
+ COPY --from=builder /install /usr/local
45
+ # Fly release_command runs from /app, so Alembic's config and migration
46
+ # scripts must exist outside the installed wheel.
47
+ COPY --from=builder /build/alembic.ini /app/alembic.ini
48
+ COPY --from=builder /build/quorus/migrations /app/quorus/migrations
49
+
50
+ # Non-root user for security. Match the data dir's UID so volumes mounted
51
+ # with the default UID:GID (1000:1000) work without a bind-mount chown.
52
+ RUN useradd --create-home --shell /bin/bash --uid 1000 quorus \
53
+ && mkdir -p /app/data \
54
+ && chown -R quorus:quorus /app/data
55
+
56
+ USER quorus
57
+
58
+ # Persist messages across restarts (fallback when Postgres unavailable).
59
+ VOLUME ["/app/data"]
60
+ ENV MESSAGES_FILE=/app/data/messages.json
61
+
62
+ EXPOSE 8080
63
+ ENV PORT=8080 \
64
+ PYTHONDONTWRITEBYTECODE=1 \
65
+ PYTHONUNBUFFERED=1
66
+
67
+ HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
68
+ CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"
69
+
70
+ CMD python -m uvicorn quorus.relay:app --host 0.0.0.0 --port ${PORT:-8080}
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arav Kekane, Aarya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.