manigent-server 0.1.0__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 (1004) hide show
  1. manigent/_catalog/mcps/chess/README.md +38 -0
  2. manigent/_catalog/mcps/chess/mcp.json +9 -0
  3. manigent/_catalog/mcps/registry.json +9 -0
  4. manigent/_catalog/skills/brief/SKILL.md +339 -0
  5. manigent/_catalog/skills/broaden-options/SKILL.md +133 -0
  6. manigent/_catalog/skills/calendar/SKILL.md +44 -0
  7. manigent/_catalog/skills/calendar/starter_config.jsonc +25 -0
  8. manigent/_catalog/skills/gmail/SKILL.md +73 -0
  9. manigent/_catalog/skills/google-calendar/SKILL.md +44 -0
  10. manigent/_catalog/skills/google-drive/SKILL.md +42 -0
  11. manigent/_catalog/skills/google-drive-write/SKILL.md +45 -0
  12. manigent/_catalog/skills/google-drive-write/starter_config.jsonc +45 -0
  13. manigent/_catalog/skills/grill-me/SKILL.md +173 -0
  14. manigent/_catalog/skills/homekit/SKILL.md +37 -0
  15. manigent/_catalog/skills/http-api/SKILL.md +117 -0
  16. manigent/_catalog/skills/ib/SKILL.md +89 -0
  17. manigent/_catalog/skills/ib/starter_config.jsonc +35 -0
  18. manigent/_catalog/skills/mailbox/SKILL.md +47 -0
  19. manigent/_catalog/skills/mailbox/starter_config.jsonc +38 -0
  20. manigent/_catalog/skills/media/SKILL.md +93 -0
  21. manigent/_catalog/skills/media/starter_config.jsonc +63 -0
  22. manigent/_catalog/skills/osxphotos/SKILL.md +41 -0
  23. manigent/_catalog/skills/photo-story/SKILL.md +285 -0
  24. manigent/_catalog/skills/playwright-browser/SKILL.md +291 -0
  25. manigent/_catalog/skills/playwright-browser/starter_config.jsonc +44 -0
  26. manigent/_catalog/skills/playwright-save-skill/SKILL.md +149 -0
  27. manigent/_catalog/skills/registry.json +166 -0
  28. manigent/_catalog/skills/web-artifacts/SKILL.md +194 -0
  29. manigent/api/action_executor.py +404 -0
  30. manigent/api/action_validator.py +531 -0
  31. manigent/api/actions.py +221 -0
  32. manigent/api/client.py +558 -0
  33. manigent/api/control.py +806 -0
  34. manigent/api/edge_protocol.py +123 -0
  35. manigent/api/fs_protocol.py +450 -0
  36. manigent/api/host_protocol.py +298 -0
  37. manigent/api/managed_job_client.py +124 -0
  38. manigent/api/responses.py +342 -0
  39. manigent/api/retry_transport.py +178 -0
  40. manigent/api/turn_protocol.py +78 -0
  41. manigent/cli_lifecycle.py +467 -0
  42. manigent/cli_server.py +978 -0
  43. manigent/doctor.py +1167 -0
  44. manigent/hub_protocol/canonical.py +375 -0
  45. manigent/hub_protocol/codes.py +171 -0
  46. manigent/hub_protocol/constants.py +71 -0
  47. manigent/hub_protocol/envelope.py +218 -0
  48. manigent/hub_protocol/frames.py +788 -0
  49. manigent/hub_protocol/http.py +260 -0
  50. manigent/hub_protocol/ids.py +170 -0
  51. manigent/hub_protocol/keys.py +195 -0
  52. manigent/hub_protocol/text.py +75 -0
  53. manigent/llm/base.py +1072 -0
  54. manigent/llm/prompt_builder.py +2450 -0
  55. manigent/llm/triggers.py +68 -0
  56. manigent/models/agent.py +2905 -0
  57. manigent/models/agent_bundle.py +298 -0
  58. manigent/models/brief_tab.py +311 -0
  59. manigent/models/chat_message.py +231 -0
  60. manigent/models/chatroom.py +1108 -0
  61. manigent/models/context.py +1159 -0
  62. manigent/models/desk_label.py +836 -0
  63. manigent/models/desk_read_state.py +220 -0
  64. manigent/models/desk_sign_off.py +267 -0
  65. manigent/models/desk_sop.py +965 -0
  66. manigent/models/desk_todo.py +1603 -0
  67. manigent/models/desk_todo_link.py +404 -0
  68. manigent/models/file_cache.py +75 -0
  69. manigent/models/host.py +732 -0
  70. manigent/models/install_token.py +401 -0
  71. manigent/models/invitation_code.py +162 -0
  72. manigent/models/knowledge_pack.py +552 -0
  73. manigent/models/managed_job.py +295 -0
  74. manigent/models/model_config.py +564 -0
  75. manigent/models/model_defaults.py +238 -0
  76. manigent/models/onboarding_plan.py +161 -0
  77. manigent/models/participant.py +449 -0
  78. manigent/models/persistable.py +892 -0
  79. manigent/models/plan_markdown.py +1774 -0
  80. manigent/models/project.py +1343 -0
  81. manigent/models/project_plan.py +8104 -0
  82. manigent/models/project_report.py +145 -0
  83. manigent/models/provenance.py +261 -0
  84. manigent/models/provider_agents.py +101 -0
  85. manigent/models/public_request.py +108 -0
  86. manigent/models/reflection_schedule.py +191 -0
  87. manigent/models/scheduled_message.py +285 -0
  88. manigent/models/share_token.py +190 -0
  89. manigent/models/skill_version.py +31 -0
  90. manigent/models/sop_template.py +187 -0
  91. manigent/models/thinking_partner_plan.py +187 -0
  92. manigent/models/tunnel_binding.py +129 -0
  93. manigent/models/user.py +1276 -0
  94. manigent/models/work_tracker.py +510 -0
  95. manigent/runner/invocation_registry.py +187 -0
  96. manigent/server/agent_capabilities.py +119 -0
  97. manigent/server/agent_visibility.py +119 -0
  98. manigent/server/announcements.py +119 -0
  99. manigent/server/app.py +807 -0
  100. manigent/server/auth.py +355 -0
  101. manigent/server/batch_timeout.py +94 -0
  102. manigent/server/channels/access.py +194 -0
  103. manigent/server/channels/adapter.py +295 -0
  104. manigent/server/channels/config.py +693 -0
  105. manigent/server/channels/contacts.py +39 -0
  106. manigent/server/channels/email_adapter.py +436 -0
  107. manigent/server/channels/email_address.py +145 -0
  108. manigent/server/channels/email_form.py +242 -0
  109. manigent/server/channels/email_format.py +285 -0
  110. manigent/server/channels/email_mail.py +427 -0
  111. manigent/server/channels/email_mailgun.py +164 -0
  112. manigent/server/channels/email_text.py +280 -0
  113. manigent/server/channels/file_links.py +127 -0
  114. manigent/server/channels/gateway.py +2998 -0
  115. manigent/server/channels/hub_delivery.py +211 -0
  116. manigent/server/channels/hub_store.py +1730 -0
  117. manigent/server/channels/line.py +645 -0
  118. manigent/server/channels/line_format.py +589 -0
  119. manigent/server/channels/log_redaction.py +145 -0
  120. manigent/server/channels/store.py +3316 -0
  121. manigent/server/channels/telegram.py +422 -0
  122. manigent/server/channels/telegram_format.py +312 -0
  123. manigent/server/channels/webchat.py +261 -0
  124. manigent/server/channels/webchat_widget.js +1310 -0
  125. manigent/server/channels/whatsapp.py +707 -0
  126. manigent/server/channels/whatsapp_format.py +511 -0
  127. manigent/server/chat_search_index.py +1203 -0
  128. manigent/server/config.py +61 -0
  129. manigent/server/context.py +121 -0
  130. manigent/server/coordinator_rule.py +115 -0
  131. manigent/server/desk_todo_notify.py +300 -0
  132. manigent/server/edge_hub.py +577 -0
  133. manigent/server/email.py +354 -0
  134. manigent/server/errors.py +83 -0
  135. manigent/server/home_fs_relay.py +392 -0
  136. manigent/server/hub/adapter.py +1395 -0
  137. manigent/server/hub/agent_send_guard.py +196 -0
  138. manigent/server/hub/avatar.py +342 -0
  139. manigent/server/hub/browser.py +778 -0
  140. manigent/server/hub/client.py +859 -0
  141. manigent/server/hub/demand.py +49 -0
  142. manigent/server/hub/directory.py +385 -0
  143. manigent/server/hub/enroll.py +400 -0
  144. manigent/server/hub/http.py +134 -0
  145. manigent/server/hub/identity.py +61 -0
  146. manigent/server/hub/keys.py +588 -0
  147. manigent/server/hub/mirror.py +802 -0
  148. manigent/server/hub/operator.py +817 -0
  149. manigent/server/hub/retain.py +361 -0
  150. manigent/server/hub/room_bridge.py +741 -0
  151. manigent/server/hub/runtime.py +691 -0
  152. manigent/server/hub/trust.py +182 -0
  153. manigent/server/hub/views.py +316 -0
  154. manigent/server/idempotency.py +205 -0
  155. manigent/server/in_flight_restore.py +181 -0
  156. manigent/server/inspection.py +816 -0
  157. manigent/server/isolation_gate.py +516 -0
  158. manigent/server/isolation_notice.py +310 -0
  159. manigent/server/managed_job_events.py +242 -0
  160. manigent/server/mcp_registry.py +118 -0
  161. manigent/server/message_forward.py +78 -0
  162. manigent/server/message_scheduler.py +176 -0
  163. manigent/server/notification_assets.py +164 -0
  164. manigent/server/oauth_login/cli_state.py +120 -0
  165. manigent/server/oauth_login/config.py +90 -0
  166. manigent/server/oauth_login/errors.py +78 -0
  167. manigent/server/oauth_login/flow.py +223 -0
  168. manigent/server/oauth_login/handoff.py +63 -0
  169. manigent/server/oauth_login/providers.py +45 -0
  170. manigent/server/oauth_state.py +81 -0
  171. manigent/server/owner_isolation.py +466 -0
  172. manigent/server/participant_liveness.py +82 -0
  173. manigent/server/public_profile.py +149 -0
  174. manigent/server/public_requests/candidates.py +116 -0
  175. manigent/server/public_requests/dispatch.py +121 -0
  176. manigent/server/public_requests/ranking.py +153 -0
  177. manigent/server/recent_activity.py +250 -0
  178. manigent/server/reflection_completion_subscriber.py +151 -0
  179. manigent/server/reflection_scheduler.py +239 -0
  180. manigent/server/requester_key_migration.py +261 -0
  181. manigent/server/room_flow.py +139 -0
  182. manigent/server/routes/_batch.py +218 -0
  183. manigent/server/routes/_fd_routing.py +155 -0
  184. manigent/server/routes/_mentions.py +70 -0
  185. manigent/server/routes/agent_env.py +188 -0
  186. manigent/server/routes/agent_fs.py +450 -0
  187. manigent/server/routes/agents.py +1206 -0
  188. manigent/server/routes/announcements.py +79 -0
  189. manigent/server/routes/auth.py +387 -0
  190. manigent/server/routes/brief_tabs.py +404 -0
  191. manigent/server/routes/changelog.py +237 -0
  192. manigent/server/routes/channels.py +993 -0
  193. manigent/server/routes/chatrooms.py +871 -0
  194. manigent/server/routes/desk_labels.py +298 -0
  195. manigent/server/routes/desk_latest_messages.py +116 -0
  196. manigent/server/routes/desk_read_state.py +110 -0
  197. manigent/server/routes/desk_sign_off.py +88 -0
  198. manigent/server/routes/desk_sops.py +216 -0
  199. manigent/server/routes/desk_todos.py +692 -0
  200. manigent/server/routes/desk_tour.py +44 -0
  201. manigent/server/routes/files.py +572 -0
  202. manigent/server/routes/hosts.py +484 -0
  203. manigent/server/routes/hub.py +534 -0
  204. manigent/server/routes/hub_channels.py +234 -0
  205. manigent/server/routes/inbox.py +581 -0
  206. manigent/server/routes/install.ps1 +436 -0
  207. manigent/server/routes/install.py +324 -0
  208. manigent/server/routes/install.sh +566 -0
  209. manigent/server/routes/isolation_check.py +145 -0
  210. manigent/server/routes/knowledge_packs.py +477 -0
  211. manigent/server/routes/managed_jobs.py +196 -0
  212. manigent/server/routes/mcps.py +271 -0
  213. manigent/server/routes/messages.py +1972 -0
  214. manigent/server/routes/model_defaults.py +279 -0
  215. manigent/server/routes/oauth.py +237 -0
  216. manigent/server/routes/oauth_login.py +351 -0
  217. manigent/server/routes/owner_isolation.py +91 -0
  218. manigent/server/routes/project_plans.py +1521 -0
  219. manigent/server/routes/project_reports.py +235 -0
  220. manigent/server/routes/projects.py +2047 -0
  221. manigent/server/routes/public_agents.py +151 -0
  222. manigent/server/routes/public_requests.py +181 -0
  223. manigent/server/routes/reflection.py +93 -0
  224. manigent/server/routes/scheduled_messages.py +263 -0
  225. manigent/server/routes/search.py +94 -0
  226. manigent/server/routes/sharing.py +156 -0
  227. manigent/server/routes/skills.py +406 -0
  228. manigent/server/routes/templates.py +219 -0
  229. manigent/server/routes/turn_tokens.py +119 -0
  230. manigent/server/routes/webchat.py +406 -0
  231. manigent/server/routes/websocket.py +431 -0
  232. manigent/server/serving_only.py +200 -0
  233. manigent/server/signup_policy.py +120 -0
  234. manigent/server/skill_registry.py +164 -0
  235. manigent/server/socket_events.py +858 -0
  236. manigent/server/timeout_checker.py +446 -0
  237. manigent/server/tunnel_subscriber.py +1000 -0
  238. manigent/server/turn_tokens.py +285 -0
  239. manigent/server/ws_hub.py +556 -0
  240. manigent/server_cli.py +31 -0
  241. manigent/sync/changelog.py +885 -0
  242. manigent/sync/console_subscriber.py +222 -0
  243. manigent/sync/runloop.py +619 -0
  244. manigent/sync/runloop_manager.py +197 -0
  245. manigent/sync/subscriber.py +98 -0
  246. manigent/utils/agent_processes.py +276 -0
  247. manigent/utils/agent_storage.py +125 -0
  248. manigent/utils/email.py +563 -0
  249. manigent/utils/env_store.py +174 -0
  250. manigent/utils/extra_dirs.py +85 -0
  251. manigent/utils/file_io.py +485 -0
  252. manigent/utils/invoke_timeout.py +87 -0
  253. manigent/utils/jsonc.py +78 -0
  254. manigent/utils/knowledge_index.py +148 -0
  255. manigent/utils/nofollow_fs.py +279 -0
  256. manigent/utils/notification_center.py +67 -0
  257. manigent/utils/version.py +57 -0
  258. manigent/web/frontend/.design-sync-entry.tsx +74 -0
  259. manigent/web/frontend/.design-sync-provider.tsx +46 -0
  260. manigent/web/frontend/.eslintrc.cjs +183 -0
  261. manigent/web/frontend/.gitignore +6 -0
  262. manigent/web/frontend/app/page.tsx +39 -0
  263. manigent/web/frontend/hub-terms.html +192 -0
  264. manigent/web/frontend/index.html +15 -0
  265. manigent/web/frontend/landing.html +15 -0
  266. manigent/web/frontend/package-lock.json +8028 -0
  267. manigent/web/frontend/package.json +55 -0
  268. manigent/web/frontend/postcss.config.js +6 -0
  269. manigent/web/frontend/privacy.html +217 -0
  270. manigent/web/frontend/private-beta-announcement.html +19 -0
  271. manigent/web/frontend/public/apple-touch-icon.png +0 -0
  272. manigent/web/frontend/public/brand-hero-banner.png +0 -0
  273. manigent/web/frontend/public/brand-hero.png +0 -0
  274. manigent/web/frontend/public/favicon.png +0 -0
  275. manigent/web/frontend/public/favicon.svg +8 -0
  276. manigent/web/frontend/public/icon-transparent-sm.png +0 -0
  277. manigent/web/frontend/public/icon.png +0 -0
  278. manigent/web/frontend/public/manigent-mark.svg +7 -0
  279. manigent/web/frontend/public/vite.svg +5 -0
  280. manigent/web/frontend/public.html +15 -0
  281. manigent/web/frontend/src/App.activeAccountRedirect.test.tsx +236 -0
  282. manigent/web/frontend/src/App.hubHostDm.test.tsx +119 -0
  283. manigent/web/frontend/src/App.hubHostProject.test.tsx +142 -0
  284. manigent/web/frontend/src/App.hubRoutes.test.tsx +187 -0
  285. manigent/web/frontend/src/App.oauthAddCallback.test.tsx +263 -0
  286. manigent/web/frontend/src/App.tsx +246 -0
  287. manigent/web/frontend/src/api/attachments.ts +32 -0
  288. manigent/web/frontend/src/api/batchLoader.test.ts +212 -0
  289. manigent/web/frontend/src/api/batchLoader.ts +124 -0
  290. manigent/web/frontend/src/api/chatroomsBatch.test.tsx +392 -0
  291. manigent/web/frontend/src/api/client.ts +321 -0
  292. manigent/web/frontend/src/api/crossAccountAuth.test.ts +388 -0
  293. manigent/web/frontend/src/api/endpoints/agentEnv.ts +24 -0
  294. manigent/web/frontend/src/api/endpoints/agentFs.ts +125 -0
  295. manigent/web/frontend/src/api/endpoints/agentSearch.ts +19 -0
  296. manigent/web/frontend/src/api/endpoints/agents.ts +66 -0
  297. manigent/web/frontend/src/api/endpoints/announcements.ts +26 -0
  298. manigent/web/frontend/src/api/endpoints/auth.ts +361 -0
  299. manigent/web/frontend/src/api/endpoints/authLogin.test.ts +236 -0
  300. manigent/web/frontend/src/api/endpoints/batch.ts +64 -0
  301. manigent/web/frontend/src/api/endpoints/briefTabs.ts +81 -0
  302. manigent/web/frontend/src/api/endpoints/channels.ts +285 -0
  303. manigent/web/frontend/src/api/endpoints/computers.ts +212 -0
  304. manigent/web/frontend/src/api/endpoints/deskLabels.ts +151 -0
  305. manigent/web/frontend/src/api/endpoints/deskLatestMessages.ts +23 -0
  306. manigent/web/frontend/src/api/endpoints/deskReadState.ts +41 -0
  307. manigent/web/frontend/src/api/endpoints/deskSignOffs.ts +74 -0
  308. manigent/web/frontend/src/api/endpoints/deskSops.ts +53 -0
  309. manigent/web/frontend/src/api/endpoints/deskTodoAttachments.ts +69 -0
  310. manigent/web/frontend/src/api/endpoints/deskTodoProjects.test.ts +99 -0
  311. manigent/web/frontend/src/api/endpoints/deskTodoProjects.ts +114 -0
  312. manigent/web/frontend/src/api/endpoints/deskTodos.ts +164 -0
  313. manigent/web/frontend/src/api/endpoints/deskTour.ts +16 -0
  314. manigent/web/frontend/src/api/endpoints/dm.ts +137 -0
  315. manigent/web/frontend/src/api/endpoints/files.ts +55 -0
  316. manigent/web/frontend/src/api/endpoints/hub.contract.test.ts +94 -0
  317. manigent/web/frontend/src/api/endpoints/hub.keys.ts +260 -0
  318. manigent/web/frontend/src/api/endpoints/hub.test.ts +203 -0
  319. manigent/web/frontend/src/api/endpoints/hub.ts +654 -0
  320. manigent/web/frontend/src/api/endpoints/index.ts +15 -0
  321. manigent/web/frontend/src/api/endpoints/install.ts +74 -0
  322. manigent/web/frontend/src/api/endpoints/isolationCheck.ts +36 -0
  323. manigent/web/frontend/src/api/endpoints/knowledgePacks.ts +179 -0
  324. manigent/web/frontend/src/api/endpoints/mcps.ts +36 -0
  325. manigent/web/frontend/src/api/endpoints/messages.held.test.ts +43 -0
  326. manigent/web/frontend/src/api/endpoints/messages.ts +95 -0
  327. manigent/web/frontend/src/api/endpoints/modelConfigs.ts +232 -0
  328. manigent/web/frontend/src/api/endpoints/oauthApi.test.ts +110 -0
  329. manigent/web/frontend/src/api/endpoints/projectPlans.ts +608 -0
  330. manigent/web/frontend/src/api/endpoints/projectReports.ts +36 -0
  331. manigent/web/frontend/src/api/endpoints/projects.ts +124 -0
  332. manigent/web/frontend/src/api/endpoints/publicRequests.ts +75 -0
  333. manigent/web/frontend/src/api/endpoints/reflection.ts +32 -0
  334. manigent/web/frontend/src/api/endpoints/roomFlow.ts +14 -0
  335. manigent/web/frontend/src/api/endpoints/scheduledMessages.ts +60 -0
  336. manigent/web/frontend/src/api/endpoints/search.ts +27 -0
  337. manigent/web/frontend/src/api/endpoints/sharing.ts +42 -0
  338. manigent/web/frontend/src/api/endpoints/skills.ts +67 -0
  339. manigent/web/frontend/src/api/endpoints/templates.ts +72 -0
  340. manigent/web/frontend/src/api/loaders.ts +22 -0
  341. manigent/web/frontend/src/api/singleIssuer.test.ts +148 -0
  342. manigent/web/frontend/src/api/transientRetry.test.ts +176 -0
  343. manigent/web/frontend/src/api/transientRetry.ts +140 -0
  344. manigent/web/frontend/src/api/websocket.resync.test.ts +111 -0
  345. manigent/web/frontend/src/api/websocket.ts +335 -0
  346. manigent/web/frontend/src/components/agent/AgentNameLink.tsx +24 -0
  347. manigent/web/frontend/src/components/agent/AgentViewActions.test.tsx +41 -0
  348. manigent/web/frontend/src/components/agent/AgentViewActions.tsx +89 -0
  349. manigent/web/frontend/src/components/agent/CreateAgentPage.tsx +535 -0
  350. manigent/web/frontend/src/components/agent/JsonConfigEditor.tsx +33 -0
  351. manigent/web/frontend/src/components/agent/KnowledgePackList.tsx +103 -0
  352. manigent/web/frontend/src/components/agent/McpCatalog.tsx +261 -0
  353. manigent/web/frontend/src/components/agent/McpConfigModal.tsx +224 -0
  354. manigent/web/frontend/src/components/agent/PreConsentModal.tsx +172 -0
  355. manigent/web/frontend/src/components/agent/SkillCatalog.tsx +364 -0
  356. manigent/web/frontend/src/components/agent/SkillConfigModal.tsx +221 -0
  357. manigent/web/frontend/src/components/auth/AddAccountPage.test.tsx +181 -0
  358. manigent/web/frontend/src/components/auth/AddAccountPage.tsx +201 -0
  359. manigent/web/frontend/src/components/auth/AuthShell.tsx +462 -0
  360. manigent/web/frontend/src/components/auth/LoginPage.test.tsx +384 -0
  361. manigent/web/frontend/src/components/auth/LoginPage.tsx +129 -0
  362. manigent/web/frontend/src/components/auth/OAuthCallbackPage.test.tsx +185 -0
  363. manigent/web/frontend/src/components/auth/OAuthCallbackPage.tsx +154 -0
  364. manigent/web/frontend/src/components/auth/OAuthLinkedBanner.tsx +44 -0
  365. manigent/web/frontend/src/components/auth/ProviderButtons.tsx +94 -0
  366. manigent/web/frontend/src/components/auth/SignupPage.tsx +437 -0
  367. manigent/web/frontend/src/components/auth/StepProgress.tsx +60 -0
  368. manigent/web/frontend/src/components/auth/VerifyEmailPage.test.tsx +155 -0
  369. manigent/web/frontend/src/components/auth/VerifyEmailPage.tsx +211 -0
  370. manigent/web/frontend/src/components/auth/index.ts +3 -0
  371. manigent/web/frontend/src/components/auth/oauthErrors.test.ts +107 -0
  372. manigent/web/frontend/src/components/auth/oauthErrors.ts +82 -0
  373. manigent/web/frontend/src/components/auth/useUsernameCheck.test.tsx +68 -0
  374. manigent/web/frontend/src/components/auth/useUsernameCheck.ts +86 -0
  375. manigent/web/frontend/src/components/beta/BetaPage.tsx +21 -0
  376. manigent/web/frontend/src/components/beta/DayAtMyDesk.tsx +120 -0
  377. manigent/web/frontend/src/components/beta/Footer.tsx +82 -0
  378. manigent/web/frontend/src/components/beta/Hero.tsx +224 -0
  379. manigent/web/frontend/src/components/beta/Nav.tsx +51 -0
  380. manigent/web/frontend/src/components/beta/Pillars.tsx +172 -0
  381. manigent/web/frontend/src/components/beta/TwoModels.tsx +146 -0
  382. manigent/web/frontend/src/components/beta/constants.ts +3 -0
  383. manigent/web/frontend/src/components/briefing/BriefingFrame.test.tsx +208 -0
  384. manigent/web/frontend/src/components/briefing/BriefingFrame.tsx +151 -0
  385. manigent/web/frontend/src/components/briefing/BriefingPage.test.tsx +176 -0
  386. manigent/web/frontend/src/components/briefing/BriefingPage.tsx +157 -0
  387. manigent/web/frontend/src/components/briefing/BriefingPage.wsDelete.test.tsx +177 -0
  388. manigent/web/frontend/src/components/briefing/briefing.css +147 -0
  389. manigent/web/frontend/src/components/briefing/heightBootstrap.test.ts +121 -0
  390. manigent/web/frontend/src/components/briefing/heightBootstrap.ts +224 -0
  391. manigent/web/frontend/src/components/briefing/parseBriefMessage.test.ts +90 -0
  392. manigent/web/frontend/src/components/briefing/sampleBriefing.ts +148 -0
  393. manigent/web/frontend/src/components/channels/ChannelBadge.tsx +35 -0
  394. manigent/web/frontend/src/components/chatroom/ChannelThreadControls.tsx +127 -0
  395. manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.test.tsx +207 -0
  396. manigent/web/frontend/src/components/chatroom/ChannelThreadNotice.tsx +80 -0
  397. manigent/web/frontend/src/components/chatroom/ChatroomView.channel.test.tsx +550 -0
  398. manigent/web/frontend/src/components/chatroom/ChatroomView.flow.test.tsx +186 -0
  399. manigent/web/frontend/src/components/chatroom/ChatroomView.isolation.test.tsx +70 -0
  400. manigent/web/frontend/src/components/chatroom/ChatroomView.tsx +450 -0
  401. manigent/web/frontend/src/components/chatroom/DelegationRow.test.tsx +170 -0
  402. manigent/web/frontend/src/components/chatroom/DelegationRow.tsx +217 -0
  403. manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.test.tsx +33 -0
  404. manigent/web/frontend/src/components/chatroom/DeliveryStatusLine.tsx +46 -0
  405. manigent/web/frontend/src/components/chatroom/ForwardModal.test.tsx +109 -0
  406. manigent/web/frontend/src/components/chatroom/ForwardModal.tsx +304 -0
  407. manigent/web/frontend/src/components/chatroom/HeldNotice.tsx +77 -0
  408. manigent/web/frontend/src/components/chatroom/MentionDropdown.tsx +97 -0
  409. manigent/web/frontend/src/components/chatroom/MessageInput.tsx +408 -0
  410. manigent/web/frontend/src/components/chatroom/MessageItem.tsx +425 -0
  411. manigent/web/frontend/src/components/chatroom/MessageList.outside.test.tsx +98 -0
  412. manigent/web/frontend/src/components/chatroom/MessageList.test.tsx +245 -0
  413. manigent/web/frontend/src/components/chatroom/MessageList.tsx +406 -0
  414. manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.test.tsx +211 -0
  415. manigent/web/frontend/src/components/chatroom/OfflineRemedyNote.tsx +182 -0
  416. manigent/web/frontend/src/components/chatroom/OutsideBadge.tsx +18 -0
  417. manigent/web/frontend/src/components/chatroom/RoomFlowControl.tsx +49 -0
  418. manigent/web/frontend/src/components/chatroom/RoomFlowStrip.test.tsx +47 -0
  419. manigent/web/frontend/src/components/chatroom/RoomFlowStrip.tsx +25 -0
  420. manigent/web/frontend/src/components/chatroom/SaveToPackModal.tsx +350 -0
  421. manigent/web/frontend/src/components/chatroom/StagedAttachments.test.tsx +56 -0
  422. manigent/web/frontend/src/components/chatroom/StagedAttachments.tsx +160 -0
  423. manigent/web/frontend/src/components/chatroom/SystemNote.tsx +18 -0
  424. manigent/web/frontend/src/components/chatroom/contactSender.test.tsx +130 -0
  425. manigent/web/frontend/src/components/chatroom/index.ts +3 -0
  426. manigent/web/frontend/src/components/chatroom/recipientStatus.test.ts +344 -0
  427. manigent/web/frontend/src/components/chatroom/recipientStatus.ts +197 -0
  428. manigent/web/frontend/src/components/common/AnnouncementBanners.test.tsx +221 -0
  429. manigent/web/frontend/src/components/common/AnnouncementBanners.tsx +180 -0
  430. manigent/web/frontend/src/components/common/Avatar.test.tsx +50 -0
  431. manigent/web/frontend/src/components/common/Avatar.tsx +102 -0
  432. manigent/web/frontend/src/components/common/Badge.tsx +30 -0
  433. manigent/web/frontend/src/components/common/BrandName.tsx +16 -0
  434. manigent/web/frontend/src/components/common/ClearHistoryButton.tsx +74 -0
  435. manigent/web/frontend/src/components/common/CollapsibleHeader.tsx +64 -0
  436. manigent/web/frontend/src/components/common/ConnectionStatus.test.tsx +56 -0
  437. manigent/web/frontend/src/components/common/ConnectionStatus.tsx +28 -0
  438. manigent/web/frontend/src/components/common/DropOverlay.tsx +27 -0
  439. manigent/web/frontend/src/components/common/InlineEditableTitle.test.tsx +369 -0
  440. manigent/web/frontend/src/components/common/InlineEditableTitle.tsx +287 -0
  441. manigent/web/frontend/src/components/common/IsolationUnavailablePanel.tsx +19 -0
  442. manigent/web/frontend/src/components/common/Loading.tsx +22 -0
  443. manigent/web/frontend/src/components/common/ModelSelector.tsx +248 -0
  444. manigent/web/frontend/src/components/common/Overlay.tsx +15 -0
  445. manigent/web/frontend/src/components/common/ResizeHandle.tsx +52 -0
  446. manigent/web/frontend/src/components/common/TagAutocompleteInput.test.tsx +92 -0
  447. manigent/web/frontend/src/components/common/TagAutocompleteInput.tsx +246 -0
  448. manigent/web/frontend/src/components/common/TagPillInput.tsx +181 -0
  449. manigent/web/frontend/src/components/common/TeamAndAgentPicker.tsx +83 -0
  450. manigent/web/frontend/src/components/common/Timestamp.tsx +61 -0
  451. manigent/web/frontend/src/components/common/VerifiedBadge.tsx +20 -0
  452. manigent/web/frontend/src/components/common/index.ts +8 -0
  453. manigent/web/frontend/src/components/composer/PhPopover.tsx +239 -0
  454. manigent/web/frontend/src/components/composer/PromptEditor.test.tsx +472 -0
  455. manigent/web/frontend/src/components/composer/PromptEditor.tsx +540 -0
  456. manigent/web/frontend/src/components/composer/caret.ts +159 -0
  457. manigent/web/frontend/src/components/composer/placeholders.test.ts +352 -0
  458. manigent/web/frontend/src/components/composer/placeholders.ts +354 -0
  459. manigent/web/frontend/src/components/composer/promptEditor.css +161 -0
  460. manigent/web/frontend/src/components/computer/ComputerPage.test.tsx +452 -0
  461. manigent/web/frontend/src/components/computer/ComputerPage.tsx +811 -0
  462. manigent/web/frontend/src/components/computer/ConnectComputerModal.tsx +135 -0
  463. manigent/web/frontend/src/components/computer/CopyCommand.tsx +77 -0
  464. manigent/web/frontend/src/components/computer/DisconnectComputerModal.tsx +121 -0
  465. manigent/web/frontend/src/components/computer/SoftwareCard.tsx +145 -0
  466. manigent/web/frontend/src/components/computer/TerminalCard.test.tsx +114 -0
  467. manigent/web/frontend/src/components/computer/TerminalCard.tsx +131 -0
  468. manigent/web/frontend/src/components/computer/TerminalPanel.tsx +226 -0
  469. manigent/web/frontend/src/components/computer/TrustPanel.test.tsx +58 -0
  470. manigent/web/frontend/src/components/computer/TrustPanel.tsx +143 -0
  471. manigent/web/frontend/src/components/computer/computerCopy.test.ts +322 -0
  472. manigent/web/frontend/src/components/computer/computerCopy.ts +413 -0
  473. manigent/web/frontend/src/components/computer/sections.ts +21 -0
  474. manigent/web/frontend/src/components/computer/terminalProtocol.test.ts +135 -0
  475. manigent/web/frontend/src/components/computer/terminalProtocol.ts +170 -0
  476. manigent/web/frontend/src/components/context/ContextGuide.test.tsx +26 -0
  477. manigent/web/frontend/src/components/context/ContextGuide.tsx +144 -0
  478. manigent/web/frontend/src/components/context/ContextLanding.tsx +46 -0
  479. manigent/web/frontend/src/components/desk/CardBand.a11y.test.tsx +138 -0
  480. manigent/web/frontend/src/components/desk/CardBand.tsx +132 -0
  481. manigent/web/frontend/src/components/desk/DashTile.tsx +81 -0
  482. manigent/web/frontend/src/components/desk/DeskAgentPicker.retained.test.tsx +79 -0
  483. manigent/web/frontend/src/components/desk/DeskAgentPicker.tsx +151 -0
  484. manigent/web/frontend/src/components/desk/DeskComposer.detachTodo.test.tsx +233 -0
  485. manigent/web/frontend/src/components/desk/DeskComposer.dispatch.test.tsx +208 -0
  486. manigent/web/frontend/src/components/desk/DeskComposer.loadTodoDraft.test.tsx +242 -0
  487. manigent/web/frontend/src/components/desk/DeskComposer.restage.test.tsx +258 -0
  488. manigent/web/frontend/src/components/desk/DeskComposer.saveTodo.test.tsx +265 -0
  489. manigent/web/frontend/src/components/desk/DeskComposer.tsx +828 -0
  490. manigent/web/frontend/src/components/desk/DeskPage.crossHighlight.test.tsx +427 -0
  491. manigent/web/frontend/src/components/desk/DeskPage.link.test.tsx +271 -0
  492. manigent/web/frontend/src/components/desk/DeskPage.tsx +1066 -0
  493. manigent/web/frontend/src/components/desk/DeskPage.welcomeTour.test.tsx +262 -0
  494. manigent/web/frontend/src/components/desk/DeskSideRail.tsx +100 -0
  495. manigent/web/frontend/src/components/desk/DeskWelcomeTour.tsx +513 -0
  496. manigent/web/frontend/src/components/desk/DeskZeroState.tsx +45 -0
  497. manigent/web/frontend/src/components/desk/LabelChip.tsx +147 -0
  498. manigent/web/frontend/src/components/desk/LabelFilter.tsx +474 -0
  499. manigent/web/frontend/src/components/desk/LabelInput.test.tsx +439 -0
  500. manigent/web/frontend/src/components/desk/LabelInput.tsx +477 -0
  501. manigent/web/frontend/src/components/desk/LabelManageSheet.test.tsx +384 -0
  502. manigent/web/frontend/src/components/desk/LabelManageSheet.tsx +498 -0
  503. manigent/web/frontend/src/components/desk/PhaseRail.test.tsx +170 -0
  504. manigent/web/frontend/src/components/desk/PhaseRail.tsx +120 -0
  505. manigent/web/frontend/src/components/desk/PlanBand.test.tsx +655 -0
  506. manigent/web/frontend/src/components/desk/PlanBand.tsx +216 -0
  507. manigent/web/frontend/src/components/desk/SaveTodoModal.tsx +126 -0
  508. manigent/web/frontend/src/components/desk/SopEditor.tsx +280 -0
  509. manigent/web/frontend/src/components/desk/SopItem.tsx +171 -0
  510. manigent/web/frontend/src/components/desk/SopRail.test.tsx +517 -0
  511. manigent/web/frontend/src/components/desk/SopRail.tsx +250 -0
  512. manigent/web/frontend/src/components/desk/TicketChip.tsx +51 -0
  513. manigent/web/frontend/src/components/desk/TodoItem.attachments.test.tsx +96 -0
  514. manigent/web/frontend/src/components/desk/TodoItem.menu.test.tsx +142 -0
  515. manigent/web/frontend/src/components/desk/TodoItem.tsx +573 -0
  516. manigent/web/frontend/src/components/desk/TodoRail.longpress.test.tsx +396 -0
  517. manigent/web/frontend/src/components/desk/TodoRail.test.tsx +1155 -0
  518. manigent/web/frontend/src/components/desk/TodoRail.tsx +918 -0
  519. manigent/web/frontend/src/components/desk/TodoRowMenu.tsx +147 -0
  520. manigent/web/frontend/src/components/desk/UpdateCard.bands.test.tsx +308 -0
  521. manigent/web/frontend/src/components/desk/UpdateCard.sendBack.test.tsx +444 -0
  522. manigent/web/frontend/src/components/desk/UpdateCard.tsx +943 -0
  523. manigent/web/frontend/src/components/desk/UpdateCard.untrusted.test.tsx +161 -0
  524. manigent/web/frontend/src/components/desk/desk.css +1921 -0
  525. manigent/web/frontend/src/components/desk/desk.css.test.ts +582 -0
  526. manigent/web/frontend/src/components/desk/deskBands.test.ts +389 -0
  527. manigent/web/frontend/src/components/desk/deskBands.ts +226 -0
  528. manigent/web/frontend/src/components/desk/m16Surfaces.test.ts +134 -0
  529. manigent/web/frontend/src/components/desk/sopSyntax.ts +73 -0
  530. manigent/web/frontend/src/components/discovery/AgentDirectory.tsx +71 -0
  531. manigent/web/frontend/src/components/discovery/AgentFilterBar.tsx +46 -0
  532. manigent/web/frontend/src/components/discovery/AgentSearchBar.tsx +56 -0
  533. manigent/web/frontend/src/components/dm/AgentSummaryCard.tsx +154 -0
  534. manigent/web/frontend/src/components/dm/ComposeFields.tsx +145 -0
  535. manigent/web/frontend/src/components/dm/DMChatView.flow.test.tsx +374 -0
  536. manigent/web/frontend/src/components/dm/DMChatView.tsx +841 -0
  537. manigent/web/frontend/src/components/dm/DMZeroState.tsx +204 -0
  538. manigent/web/frontend/src/components/dm/DmComposeModal.tsx +301 -0
  539. manigent/web/frontend/src/components/dm/PersonalizeModal.tsx +366 -0
  540. manigent/web/frontend/src/components/dm/index.ts +1 -0
  541. manigent/web/frontend/src/components/files/FileItem.tsx +138 -0
  542. manigent/web/frontend/src/components/files/FileList.outside.test.tsx +29 -0
  543. manigent/web/frontend/src/components/files/FileList.tsx +48 -0
  544. manigent/web/frontend/src/components/files/FilePreview.test.tsx +75 -0
  545. manigent/web/frontend/src/components/files/FilePreview.tsx +325 -0
  546. manigent/web/frontend/src/components/files/FileViewerModal.test.tsx +157 -0
  547. manigent/web/frontend/src/components/files/FileViewerModal.tsx +261 -0
  548. manigent/web/frontend/src/components/files/HighlightedCode.tsx +39 -0
  549. manigent/web/frontend/src/components/files/SaveToAgentPanel.test.tsx +156 -0
  550. manigent/web/frontend/src/components/files/SaveToAgentPanel.tsx +545 -0
  551. manigent/web/frontend/src/components/files/index.ts +3 -0
  552. manigent/web/frontend/src/components/hero/AgentAvatar.tsx +66 -0
  553. manigent/web/frontend/src/components/hero/Button.tsx +66 -0
  554. manigent/web/frontend/src/components/hero/Hero.tsx +133 -0
  555. manigent/web/frontend/src/components/homefs/HomeFolderBrowser.test.tsx +393 -0
  556. manigent/web/frontend/src/components/homefs/HomeFolderBrowser.tsx +792 -0
  557. manigent/web/frontend/src/components/hub/BridgeDeliveryLine.test.tsx +40 -0
  558. manigent/web/frontend/src/components/hub/BridgeDeliveryLine.tsx +36 -0
  559. manigent/web/frontend/src/components/hub/HubAvatar.test.tsx +93 -0
  560. manigent/web/frontend/src/components/hub/HubAvatar.tsx +33 -0
  561. manigent/web/frontend/src/components/hub/HubChatView.test.tsx +695 -0
  562. manigent/web/frontend/src/components/hub/HubChatView.tsx +504 -0
  563. manigent/web/frontend/src/components/hub/HubContactLabel.test.tsx +92 -0
  564. manigent/web/frontend/src/components/hub/HubContactLabel.tsx +47 -0
  565. manigent/web/frontend/src/components/hub/HubDeliveryLine.tsx +49 -0
  566. manigent/web/frontend/src/components/hub/HubDisclosure.tsx +49 -0
  567. manigent/web/frontend/src/components/hub/HubEnrollmentDialog.test.tsx +144 -0
  568. manigent/web/frontend/src/components/hub/HubEnrollmentDialog.tsx +244 -0
  569. manigent/web/frontend/src/components/hub/HubMessageBody.test.tsx +26 -0
  570. manigent/web/frontend/src/components/hub/HubMessageBody.tsx +14 -0
  571. manigent/web/frontend/src/components/hub/HubOfflineConfirm.tsx +45 -0
  572. manigent/web/frontend/src/components/hub/HubThreadBanner.tsx +33 -0
  573. manigent/web/frontend/src/components/hub/HubThreadGuard.tsx +59 -0
  574. manigent/web/frontend/src/components/hub/RemoteAgentCard.test.tsx +50 -0
  575. manigent/web/frontend/src/components/hub/RemoteAgentCard.tsx +174 -0
  576. manigent/web/frontend/src/components/hub/RemoteAgentSection.test.tsx +446 -0
  577. manigent/web/frontend/src/components/hub/RemoteAgentSection.tsx +219 -0
  578. manigent/web/frontend/src/components/hub/ReportDialog.test.tsx +96 -0
  579. manigent/web/frontend/src/components/hub/ReportDialog.tsx +162 -0
  580. manigent/web/frontend/src/components/hub/RetainButton.test.tsx +103 -0
  581. manigent/web/frontend/src/components/hub/RetainButton.tsx +114 -0
  582. manigent/web/frontend/src/components/hub/RetainDialog.test.tsx +68 -0
  583. manigent/web/frontend/src/components/hub/RetainDialog.tsx +115 -0
  584. manigent/web/frontend/src/components/hub/RetainedList.test.tsx +94 -0
  585. manigent/web/frontend/src/components/hub/RetainedList.tsx +32 -0
  586. manigent/web/frontend/src/components/hub/RetainedRow.tsx +129 -0
  587. manigent/web/frontend/src/components/hub/StandInChip.test.tsx +37 -0
  588. manigent/web/frontend/src/components/hub/StandInChip.tsx +43 -0
  589. manigent/web/frontend/src/components/hub/UnusualNameChip.test.tsx +81 -0
  590. manigent/web/frontend/src/components/hub/UnusualNameChip.tsx +22 -0
  591. manigent/web/frontend/src/components/hub/hubLint.test.ts +265 -0
  592. manigent/web/frontend/src/components/landing/ComparisonSection.tsx +92 -0
  593. manigent/web/frontend/src/components/landing/ConsoleSection.tsx +402 -0
  594. manigent/web/frontend/src/components/landing/CrewSection.tsx +224 -0
  595. manigent/web/frontend/src/components/landing/FAQ.tsx +68 -0
  596. manigent/web/frontend/src/components/landing/Footer.tsx +58 -0
  597. manigent/web/frontend/src/components/landing/HowItWorks.tsx +117 -0
  598. manigent/web/frontend/src/components/landing/KnowledgeSection.tsx +91 -0
  599. manigent/web/frontend/src/components/landing/LandingPage.test.tsx +134 -0
  600. manigent/web/frontend/src/components/landing/LearningSection.tsx +486 -0
  601. manigent/web/frontend/src/components/landing/Nav.tsx +68 -0
  602. manigent/web/frontend/src/components/landing/Pricing.tsx +263 -0
  603. manigent/web/frontend/src/components/landing/Section.tsx +98 -0
  604. manigent/web/frontend/src/components/landing/StaffingSection.tsx +122 -0
  605. manigent/web/frontend/src/components/landing/WhyNowSection.tsx +92 -0
  606. manigent/web/frontend/src/components/landing/WhyTeamSection.tsx +73 -0
  607. manigent/web/frontend/src/components/landing/earlyAccess.ts +23 -0
  608. manigent/web/frontend/src/components/layout/AppLayout.tsx +77 -0
  609. manigent/web/frontend/src/components/layout/Sidebar.tsx +172 -0
  610. manigent/web/frontend/src/components/layout/index.ts +2 -0
  611. manigent/web/frontend/src/components/project/ActivityFeed.tsx +247 -0
  612. manigent/web/frontend/src/components/project/CreateProjectPage.tsx +543 -0
  613. manigent/web/frontend/src/components/project/DeleteProjectButton.tsx +39 -0
  614. manigent/web/frontend/src/components/project/ProjectPlanTab.phase.test.tsx +107 -0
  615. manigent/web/frontend/src/components/project/ProjectPlanTab.test.tsx +994 -0
  616. manigent/web/frontend/src/components/project/ProjectPlanTab.tsx +166 -0
  617. manigent/web/frontend/src/components/project/ProjectReportTab.tsx +139 -0
  618. manigent/web/frontend/src/components/project/ProjectView.tsx +390 -0
  619. manigent/web/frontend/src/components/project/index.ts +2 -0
  620. manigent/web/frontend/src/components/project/plan/PlanAcceptance.test.tsx +460 -0
  621. manigent/web/frontend/src/components/project/plan/PlanAcceptance.tsx +111 -0
  622. manigent/web/frontend/src/components/project/plan/PlanBlockEdit.tsx +111 -0
  623. manigent/web/frontend/src/components/project/plan/PlanBlockEditing.test.tsx +1132 -0
  624. manigent/web/frontend/src/components/project/plan/PlanBody.test.tsx +495 -0
  625. manigent/web/frontend/src/components/project/plan/PlanBody.tsx +992 -0
  626. manigent/web/frontend/src/components/project/plan/PlanCollide.tsx +254 -0
  627. manigent/web/frontend/src/components/project/plan/PlanCommentList.test.tsx +420 -0
  628. manigent/web/frontend/src/components/project/plan/PlanCommentList.tsx +391 -0
  629. manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.test.tsx +226 -0
  630. manigent/web/frontend/src/components/project/plan/PlanDecisionNudge.tsx +128 -0
  631. manigent/web/frontend/src/components/project/plan/PlanDestructiveAccept.test.tsx +209 -0
  632. manigent/web/frontend/src/components/project/plan/PlanDiff.test.tsx +126 -0
  633. manigent/web/frontend/src/components/project/plan/PlanDiff.tsx +131 -0
  634. manigent/web/frontend/src/components/project/plan/PlanDiffSeries.test.tsx +500 -0
  635. manigent/web/frontend/src/components/project/plan/PlanDiffSeries.tsx +125 -0
  636. manigent/web/frontend/src/components/project/plan/PlanEditor.mounts.test.tsx +504 -0
  637. manigent/web/frontend/src/components/project/plan/PlanEditor.tsx +2159 -0
  638. manigent/web/frontend/src/components/project/plan/PlanHistory.tsx +69 -0
  639. manigent/web/frontend/src/components/project/plan/PlanInlineNotes.test.tsx +2398 -0
  640. manigent/web/frontend/src/components/project/plan/PlanNoteBase.test.tsx +215 -0
  641. manigent/web/frontend/src/components/project/plan/PlanNoteRow.test.tsx +562 -0
  642. manigent/web/frontend/src/components/project/plan/PlanNoteRow.tsx +908 -0
  643. manigent/web/frontend/src/components/project/plan/PlanQuotePop.tsx +254 -0
  644. manigent/web/frontend/src/components/project/plan/PlanStaleReconcile.test.tsx +244 -0
  645. manigent/web/frontend/src/components/project/plan/PlanSubmitWindow.test.tsx +281 -0
  646. manigent/web/frontend/src/components/project/plan/PlanTray.tsx +184 -0
  647. manigent/web/frontend/src/components/project/plan/PlanTrayMirror.test.tsx +242 -0
  648. manigent/web/frontend/src/components/project/plan/plan.css +1151 -0
  649. manigent/web/frontend/src/components/project/plan/planAnchor.test.tsx +459 -0
  650. manigent/web/frontend/src/components/project/plan/planAnchor.tsx +277 -0
  651. manigent/web/frontend/src/components/project/plan/planBlocks.test.tsx +470 -0
  652. manigent/web/frontend/src/components/project/plan/planBlocks.ts +242 -0
  653. manigent/web/frontend/src/components/project/plan/planContrast.test.ts +229 -0
  654. manigent/web/frontend/src/components/project/plan/planDiffs.test.ts +455 -0
  655. manigent/web/frontend/src/components/project/plan/planDiffs.ts +358 -0
  656. manigent/web/frontend/src/components/project/plan/planTestServer.ts +741 -0
  657. manigent/web/frontend/src/components/project/plan/planTransport.test.ts +160 -0
  658. manigent/web/frontend/src/components/project/plan/planTrayLogic.test.ts +1201 -0
  659. manigent/web/frontend/src/components/project/plan/planTrayLogic.ts +1164 -0
  660. manigent/web/frontend/src/components/publicAgents/PublicAgentsAccount.tsx +162 -0
  661. manigent/web/frontend/src/components/publicAgents/api.ts +55 -0
  662. manigent/web/frontend/src/components/publicAgents/parts.tsx +54 -0
  663. manigent/web/frontend/src/components/publicAgents/publicAgents.css +53 -0
  664. manigent/web/frontend/src/components/publicAgents/publicAgents.test.tsx +86 -0
  665. manigent/web/frontend/src/components/publicAgents/widget.test.ts +148 -0
  666. manigent/web/frontend/src/components/publicAgents/widget.ts +40 -0
  667. manigent/web/frontend/src/components/publicRequests/AgentDrawer.tsx +101 -0
  668. manigent/web/frontend/src/components/publicRequests/AgentPanel.tsx +173 -0
  669. manigent/web/frontend/src/components/publicRequests/AgentRankList.tsx +61 -0
  670. manigent/web/frontend/src/components/publicRequests/MatchRetainButton.tsx +67 -0
  671. manigent/web/frontend/src/components/publicRequests/ProgressBoard.tsx +74 -0
  672. manigent/web/frontend/src/components/publicRequests/PublicRequestsPage.tsx +57 -0
  673. manigent/web/frontend/src/components/publicRequests/RequestHome.tsx +99 -0
  674. manigent/web/frontend/src/components/publicRequests/RequestRoom.tsx +126 -0
  675. manigent/web/frontend/src/components/publicRequests/agentView.ts +65 -0
  676. manigent/web/frontend/src/components/publicRequests/publicRequests.css +209 -0
  677. manigent/web/frontend/src/components/publicRequests/publicRequests.test.tsx +172 -0
  678. manigent/web/frontend/src/components/publicRequests/requestCopy.ts +121 -0
  679. manigent/web/frontend/src/components/sample-request/SampleIdeaList.test.tsx +190 -0
  680. manigent/web/frontend/src/components/sample-request/SampleIdeaList.tsx +180 -0
  681. manigent/web/frontend/src/components/sample-request/SampleRequestBody.tsx +276 -0
  682. manigent/web/frontend/src/components/sample-request/assistantStarterSamples.ts +62 -0
  683. manigent/web/frontend/src/components/sample-request/sampleIdeaList.css +106 -0
  684. manigent/web/frontend/src/components/scheduled/CronPicker.tsx +80 -0
  685. manigent/web/frontend/src/components/scheduled/ScheduleMessageModal.tsx +341 -0
  686. manigent/web/frontend/src/components/scheduled/ScheduledMessagesList.tsx +157 -0
  687. manigent/web/frontend/src/components/settings/AgentSettingsPage.tsx +366 -0
  688. manigent/web/frontend/src/components/settings/ConnectedAccounts.tsx +277 -0
  689. manigent/web/frontend/src/components/settings/DefaultModelSection.test.tsx +96 -0
  690. manigent/web/frontend/src/components/settings/DefaultModelSection.tsx +212 -0
  691. manigent/web/frontend/src/components/settings/DisconnectConfirmModal.tsx +93 -0
  692. manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.isolation.test.tsx +66 -0
  693. manigent/web/frontend/src/components/settings/FrontDeskSettingsPage.tsx +134 -0
  694. manigent/web/frontend/src/components/settings/InvitableAgentsPicker.tsx +182 -0
  695. manigent/web/frontend/src/components/settings/KnowledgePacksSection.tsx +867 -0
  696. manigent/web/frontend/src/components/settings/ModelConfigDialog.catalogDriven.test.tsx +79 -0
  697. manigent/web/frontend/src/components/settings/ModelConfigDialog.tsx +343 -0
  698. manigent/web/frontend/src/components/settings/ModelConfigSection.smartRouting.test.tsx +109 -0
  699. manigent/web/frontend/src/components/settings/ModelConfigSection.tsx +599 -0
  700. manigent/web/frontend/src/components/settings/ReflectionScheduleSection.tsx +170 -0
  701. manigent/web/frontend/src/components/settings/UserSettingsPage.tsx +168 -0
  702. manigent/web/frontend/src/components/settings/agent/AgentStatusStrip.tsx +71 -0
  703. manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.test.tsx +81 -0
  704. manigent/web/frontend/src/components/settings/agent/BlockedDeploymentsCard.tsx +62 -0
  705. manigent/web/frontend/src/components/settings/agent/ChannelIsolationLine.tsx +21 -0
  706. manigent/web/frontend/src/components/settings/agent/ChannelsSection.test.tsx +879 -0
  707. manigent/web/frontend/src/components/settings/agent/ChannelsSection.tsx +1124 -0
  708. manigent/web/frontend/src/components/settings/agent/EnvVarsCard.test.ts +15 -0
  709. manigent/web/frontend/src/components/settings/agent/EnvVarsCard.tsx +301 -0
  710. manigent/web/frontend/src/components/settings/agent/ExtraDirsList.tsx +85 -0
  711. manigent/web/frontend/src/components/settings/agent/GeneralSection.tsx +88 -0
  712. manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.test.tsx +32 -0
  713. manigent/web/frontend/src/components/settings/agent/HostDisclaimerSlot.tsx +30 -0
  714. manigent/web/frontend/src/components/settings/agent/HubChannelCard.test.tsx +449 -0
  715. manigent/web/frontend/src/components/settings/agent/HubChannelCard.tsx +312 -0
  716. manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.test.tsx +83 -0
  717. manigent/web/frontend/src/components/settings/agent/HubInvitableAgentsCard.tsx +64 -0
  718. manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.test.tsx +69 -0
  719. manigent/web/frontend/src/components/settings/agent/HubSendUsageCard.tsx +42 -0
  720. manigent/web/frontend/src/components/settings/agent/HubTurnOffDialog.tsx +88 -0
  721. manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.test.tsx +26 -0
  722. manigent/web/frontend/src/components/settings/agent/IsolationRecheckButton.tsx +68 -0
  723. manigent/web/frontend/src/components/settings/agent/KnowledgeSection.tsx +68 -0
  724. manigent/web/frontend/src/components/settings/agent/LineChannelCard.test.tsx +428 -0
  725. manigent/web/frontend/src/components/settings/agent/LineChannelCard.tsx +343 -0
  726. manigent/web/frontend/src/components/settings/agent/RuntimeSection.test.tsx +164 -0
  727. manigent/web/frontend/src/components/settings/agent/RuntimeSection.tsx +118 -0
  728. manigent/web/frontend/src/components/settings/agent/StorageSection.tsx +78 -0
  729. manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.test.tsx +253 -0
  730. manigent/web/frontend/src/components/settings/agent/WebChatChannelCard.tsx +401 -0
  731. manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.test.tsx +462 -0
  732. manigent/web/frontend/src/components/settings/agent/WhatsAppChannelCard.tsx +289 -0
  733. manigent/web/frontend/src/components/settings/agent/agentSettings.test.tsx +109 -0
  734. manigent/web/frontend/src/components/settings/agent/channelSetupGuides.test.tsx +162 -0
  735. manigent/web/frontend/src/components/settings/agent/channelSetupGuides.tsx +119 -0
  736. manigent/web/frontend/src/components/settings/agent/channelsShared.ts +83 -0
  737. manigent/web/frontend/src/components/settings/agent/fields.tsx +67 -0
  738. manigent/web/frontend/src/components/settings/agent/sections.ts +36 -0
  739. manigent/web/frontend/src/components/settings/agent/useAgentSettingsDraft.ts +129 -0
  740. manigent/web/frontend/src/components/settings/agent/useInstallSelection.ts +38 -0
  741. manigent/web/frontend/src/components/settings/primitives.tsx +98 -0
  742. manigent/web/frontend/src/components/shared/ExpandToggle.tsx +45 -0
  743. manigent/web/frontend/src/components/shared/MarkdownBody.tsx +218 -0
  744. manigent/web/frontend/src/components/shared/MarkdownBody.untrusted.test.tsx +97 -0
  745. manigent/web/frontend/src/components/shared/RenderBody.tsx +90 -0
  746. manigent/web/frontend/src/components/shared/rehypeHideComments.test.tsx +115 -0
  747. manigent/web/frontend/src/components/shared/rehypeHideComments.ts +124 -0
  748. manigent/web/frontend/src/components/sharing/AcceptSharePage.tsx +141 -0
  749. manigent/web/frontend/src/components/sharing/ShareLandingPage.tsx +89 -0
  750. manigent/web/frontend/src/components/sidebar/AccountChip.test.tsx +337 -0
  751. manigent/web/frontend/src/components/sidebar/AccountChip.tsx +407 -0
  752. manigent/web/frontend/src/components/sidebar/ActiveWorkDot.tsx +9 -0
  753. manigent/web/frontend/src/components/sidebar/AgentProfile.tsx +44 -0
  754. manigent/web/frontend/src/components/sidebar/AgentRow.tsx +71 -0
  755. manigent/web/frontend/src/components/sidebar/AgentsPanel.retained.test.tsx +105 -0
  756. manigent/web/frontend/src/components/sidebar/AgentsPanel.tsx +231 -0
  757. manigent/web/frontend/src/components/sidebar/AssistantHero.tsx +48 -0
  758. manigent/web/frontend/src/components/sidebar/ChatroomItem.tsx +48 -0
  759. manigent/web/frontend/src/components/sidebar/CommandPalette.channelThreads.test.tsx +76 -0
  760. manigent/web/frontend/src/components/sidebar/CommandPalette.reopen.test.tsx +206 -0
  761. manigent/web/frontend/src/components/sidebar/CommandPalette.test.tsx +725 -0
  762. manigent/web/frontend/src/components/sidebar/CommandPalette.tsx +774 -0
  763. manigent/web/frontend/src/components/sidebar/ComputersPanel.tsx +144 -0
  764. manigent/web/frontend/src/components/sidebar/DmIcon.tsx +46 -0
  765. manigent/web/frontend/src/components/sidebar/DmsPanel.tsx +226 -0
  766. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.channels.test.tsx +582 -0
  767. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.test.tsx +151 -0
  768. manigent/web/frontend/src/components/sidebar/FrontDeskPanel.tsx +454 -0
  769. manigent/web/frontend/src/components/sidebar/HotkeysHelp.tsx +101 -0
  770. manigent/web/frontend/src/components/sidebar/MessageHitRow.test.tsx +84 -0
  771. manigent/web/frontend/src/components/sidebar/MessageHitRow.tsx +57 -0
  772. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.crossHighlight.test.tsx +247 -0
  773. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.delete.test.tsx +273 -0
  774. manigent/web/frontend/src/components/sidebar/MyDeskRailPanel.tsx +351 -0
  775. manigent/web/frontend/src/components/sidebar/PanelShell.tsx +48 -0
  776. manigent/web/frontend/src/components/sidebar/ProjectItem.tsx +161 -0
  777. manigent/web/frontend/src/components/sidebar/ProjectRow.tsx +151 -0
  778. manigent/web/frontend/src/components/sidebar/ProjectsPanel.tsx +80 -0
  779. manigent/web/frontend/src/components/sidebar/PublicPanel.counterpart.test.tsx +85 -0
  780. manigent/web/frontend/src/components/sidebar/PublicPanel.hub.test.tsx +147 -0
  781. manigent/web/frontend/src/components/sidebar/PublicPanel.tsx +271 -0
  782. manigent/web/frontend/src/components/sidebar/RecencyHeader.tsx +17 -0
  783. manigent/web/frontend/src/components/sidebar/ScheduledPanel.tsx +58 -0
  784. manigent/web/frontend/src/components/sidebar/SidebarAvatar.tsx +128 -0
  785. manigent/web/frontend/src/components/sidebar/SidebarRail.tsx +163 -0
  786. manigent/web/frontend/src/components/sidebar/TeamGroup.tsx +67 -0
  787. manigent/web/frontend/src/components/sidebar/dmPrefill.test.ts +69 -0
  788. manigent/web/frontend/src/components/sidebar/dmPrefill.ts +80 -0
  789. manigent/web/frontend/src/components/sidebar/index.ts +18 -0
  790. manigent/web/frontend/src/components/sidebar/railShortcuts.ts +18 -0
  791. manigent/web/frontend/src/components/today/lib.test.ts +70 -0
  792. manigent/web/frontend/src/components/today/lib.ts +296 -0
  793. manigent/web/frontend/src/components/today/today.css +113 -0
  794. manigent/web/frontend/src/components/welcome/RunnerSetup.css +253 -0
  795. manigent/web/frontend/src/components/welcome/RunnerSetup.tsx +148 -0
  796. manigent/web/frontend/src/components/welcome/WelcomePage.css +383 -0
  797. manigent/web/frontend/src/components/welcome/WelcomePage.tsx +329 -0
  798. manigent/web/frontend/src/components/welcome/welcomeCopy.test.ts +382 -0
  799. manigent/web/frontend/src/components/welcome/welcomeCopy.ts +313 -0
  800. manigent/web/frontend/src/config/llmProviders.test.ts +161 -0
  801. manigent/web/frontend/src/config/llmProviders.ts +109 -0
  802. manigent/web/frontend/src/config/providerIds.fixture.json +13 -0
  803. manigent/web/frontend/src/hooks/accountNavigation.test.tsx +138 -0
  804. manigent/web/frontend/src/hooks/index.ts +10 -0
  805. manigent/web/frontend/src/hooks/useActiveWorkBootstrap.ts +86 -0
  806. manigent/web/frontend/src/hooks/useAgentAccess.test.tsx +30 -0
  807. manigent/web/frontend/src/hooks/useAgentAccess.ts +204 -0
  808. manigent/web/frontend/src/hooks/useAgentSampleRequests.ts +27 -0
  809. manigent/web/frontend/src/hooks/useAgentSearch.ts +10 -0
  810. manigent/web/frontend/src/hooks/useAgents.ts +46 -0
  811. manigent/web/frontend/src/hooks/useAnnouncements.test.ts +112 -0
  812. manigent/web/frontend/src/hooks/useAnnouncements.ts +100 -0
  813. manigent/web/frontend/src/hooks/useAssistantAgent.ts +24 -0
  814. manigent/web/frontend/src/hooks/useAutoResizeTextarea.ts +18 -0
  815. manigent/web/frontend/src/hooks/useBridgeDeliveries.test.tsx +79 -0
  816. manigent/web/frontend/src/hooks/useBriefTabs.ts +122 -0
  817. manigent/web/frontend/src/hooks/useChannelThreads.ts +69 -0
  818. manigent/web/frontend/src/hooks/useComposeForm.ts +80 -0
  819. manigent/web/frontend/src/hooks/useComputers.ts +123 -0
  820. manigent/web/frontend/src/hooks/useContextFirstItem.ts +118 -0
  821. manigent/web/frontend/src/hooks/useCreateProject.ts +78 -0
  822. manigent/web/frontend/src/hooks/useDebouncedValue.ts +16 -0
  823. manigent/web/frontend/src/hooks/useDeleteProject.ts +27 -0
  824. manigent/web/frontend/src/hooks/useDeskDispatch.test.tsx +170 -0
  825. manigent/web/frontend/src/hooks/useDeskDispatch.ts +135 -0
  826. manigent/web/frontend/src/hooks/useDeskReadState.test.tsx +85 -0
  827. manigent/web/frontend/src/hooks/useDeskReadState.ts +150 -0
  828. manigent/web/frontend/src/hooks/useDeskRecentlyDone.test.tsx +222 -0
  829. manigent/web/frontend/src/hooks/useDeskRecentlyDone.ts +225 -0
  830. manigent/web/frontend/src/hooks/useDeskTour.ts +37 -0
  831. manigent/web/frontend/src/hooks/useDeskUpdates.channelThreads.test.tsx +91 -0
  832. manigent/web/frontend/src/hooks/useDeskUpdates.hubThreads.test.tsx +93 -0
  833. manigent/web/frontend/src/hooks/useDeskUpdates.ts +353 -0
  834. manigent/web/frontend/src/hooks/useDisconnectIntegration.ts +23 -0
  835. manigent/web/frontend/src/hooks/useDmThreads.ts +99 -0
  836. manigent/web/frontend/src/hooks/useFileDropzone.ts +93 -0
  837. manigent/web/frontend/src/hooks/useFileUpload.ts +38 -0
  838. manigent/web/frontend/src/hooks/useFiles.ts +98 -0
  839. manigent/web/frontend/src/hooks/useHub.test.tsx +237 -0
  840. manigent/web/frontend/src/hooks/useHub.ts +502 -0
  841. manigent/web/frontend/src/hooks/useInstall.ts +82 -0
  842. manigent/web/frontend/src/hooks/useLabels.test.tsx +294 -0
  843. manigent/web/frontend/src/hooks/useLabels.ts +287 -0
  844. manigent/web/frontend/src/hooks/useLogout.ts +49 -0
  845. manigent/web/frontend/src/hooks/useMentionDetection.ts +90 -0
  846. manigent/web/frontend/src/hooks/useMessageAttachments.deskLimit.test.ts +264 -0
  847. manigent/web/frontend/src/hooks/useMessageAttachments.ts +283 -0
  848. manigent/web/frontend/src/hooks/useMessageDeepLink.test.tsx +70 -0
  849. manigent/web/frontend/src/hooks/useMessageDeepLink.ts +32 -0
  850. manigent/web/frontend/src/hooks/useMessageSearch.ts +78 -0
  851. manigent/web/frontend/src/hooks/useModelConfigs.ts +178 -0
  852. manigent/web/frontend/src/hooks/useMyAgents.ts +18 -0
  853. manigent/web/frontend/src/hooks/usePointerReorder.ts +270 -0
  854. manigent/web/frontend/src/hooks/usePrefersReducedMotion.ts +18 -0
  855. manigent/web/frontend/src/hooks/useProjectPlan.ts +36 -0
  856. manigent/web/frontend/src/hooks/useProjectReport.ts +19 -0
  857. manigent/web/frontend/src/hooks/useProjects.ts +104 -0
  858. manigent/web/frontend/src/hooks/useProjectsWithChatrooms.ts +97 -0
  859. manigent/web/frontend/src/hooks/usePublicRequests.ts +89 -0
  860. manigent/web/frontend/src/hooks/useRecentAgents.ts +38 -0
  861. manigent/web/frontend/src/hooks/useRenameProject.test.tsx +185 -0
  862. manigent/web/frontend/src/hooks/useRenameProject.ts +192 -0
  863. manigent/web/frontend/src/hooks/useRoomTrust.ts +56 -0
  864. manigent/web/frontend/src/hooks/useSharing.ts +46 -0
  865. manigent/web/frontend/src/hooks/useSopRecents.ts +55 -0
  866. manigent/web/frontend/src/hooks/useSopSchedules.ts +25 -0
  867. manigent/web/frontend/src/hooks/useSops.ts +99 -0
  868. manigent/web/frontend/src/hooks/useSwitchAccount.ts +41 -0
  869. manigent/web/frontend/src/hooks/useTeamGroups.ts +48 -0
  870. manigent/web/frontend/src/hooks/useThreadOutbox.ts +68 -0
  871. manigent/web/frontend/src/hooks/useTodos.add.test.tsx +89 -0
  872. manigent/web/frontend/src/hooks/useTodos.linkProject.test.tsx +115 -0
  873. manigent/web/frontend/src/hooks/useTodos.saveDraft.test.tsx +188 -0
  874. manigent/web/frontend/src/hooks/useTodos.ts +396 -0
  875. manigent/web/frontend/src/hooks/useUntrustedBodies.test.tsx +87 -0
  876. manigent/web/frontend/src/hooks/useUntrustedBodies.ts +51 -0
  877. manigent/web/frontend/src/hooks/useWebSocket.briefTab.test.tsx +148 -0
  878. manigent/web/frontend/src/hooks/useWebSocket.channelThreads.test.tsx +82 -0
  879. manigent/web/frontend/src/hooks/useWebSocket.hostSync.test.tsx +92 -0
  880. manigent/web/frontend/src/hooks/useWebSocket.hub.test.tsx +114 -0
  881. manigent/web/frontend/src/hooks/useWebSocket.labelSync.test.tsx +168 -0
  882. manigent/web/frontend/src/hooks/useWebSocket.plan.test.tsx +168 -0
  883. manigent/web/frontend/src/hooks/useWebSocket.reconnect.test.tsx +166 -0
  884. manigent/web/frontend/src/hooks/useWebSocket.runnerVersions.test.tsx +167 -0
  885. manigent/web/frontend/src/hooks/useWebSocket.ts +495 -0
  886. manigent/web/frontend/src/hooks/useWindowFileDropGuard.ts +33 -0
  887. manigent/web/frontend/src/landing.tsx +11 -0
  888. manigent/web/frontend/src/main.tsx +17 -0
  889. manigent/web/frontend/src/private-beta.tsx +10 -0
  890. manigent/web/frontend/src/public.tsx +42 -0
  891. manigent/web/frontend/src/queryClient.ts +16 -0
  892. manigent/web/frontend/src/stores/activeWorkStore.ts +88 -0
  893. manigent/web/frontend/src/stores/announcementStore.test.ts +217 -0
  894. manigent/web/frontend/src/stores/announcementStore.ts +169 -0
  895. manigent/web/frontend/src/stores/authStore.multiSession.test.ts +304 -0
  896. manigent/web/frontend/src/stores/authStore.test.ts +235 -0
  897. manigent/web/frontend/src/stores/authStore.ts +693 -0
  898. manigent/web/frontend/src/stores/deskLabelViewStore.test.ts +162 -0
  899. manigent/web/frontend/src/stores/deskLabelViewStore.ts +234 -0
  900. manigent/web/frontend/src/stores/deskLinkStore.ts +54 -0
  901. manigent/web/frontend/src/stores/deskStore.test.ts +53 -0
  902. manigent/web/frontend/src/stores/deskStore.ts +93 -0
  903. manigent/web/frontend/src/stores/heldPostsStore.ts +78 -0
  904. manigent/web/frontend/src/stores/mcpAuthStore.ts +52 -0
  905. manigent/web/frontend/src/stores/modelSelectionStore.ts +33 -0
  906. manigent/web/frontend/src/stores/perUserStores.ts +79 -0
  907. manigent/web/frontend/src/stores/runnerVersionStore.test.ts +81 -0
  908. manigent/web/frontend/src/stores/runnerVersionStore.ts +60 -0
  909. manigent/web/frontend/src/stores/skillAuthStore.ts +54 -0
  910. manigent/web/frontend/src/stores/switchReset.test.ts +236 -0
  911. manigent/web/frontend/src/stores/tokenExp.ts +32 -0
  912. manigent/web/frontend/src/stores/uiStore.ts +186 -0
  913. manigent/web/frontend/src/stores/unreadStore.ts +204 -0
  914. manigent/web/frontend/src/stores/unsavedGuard.ts +22 -0
  915. manigent/web/frontend/src/styles/design/fonts/JetBrainsMono-Latin.woff2 +0 -0
  916. manigent/web/frontend/src/styles/design/fonts/Nunito-Latin.woff2 +0 -0
  917. manigent/web/frontend/src/styles/design/fonts/Nunito-LatinExt.woff2 +0 -0
  918. manigent/web/frontend/src/styles/design/tokens.css +160 -0
  919. manigent/web/frontend/src/styles/homepage.css +242 -0
  920. manigent/web/frontend/src/styles/index.css +126 -0
  921. manigent/web/frontend/src/styles/landing.css +52 -0
  922. manigent/web/frontend/src/test/authFixtures.ts +154 -0
  923. manigent/web/frontend/src/test/fetchHarness.ts +203 -0
  924. manigent/web/frontend/src/test/fixtures/channelThreads.smoke.json +42 -0
  925. manigent/web/frontend/src/test/hubFixtures.ts +212 -0
  926. manigent/web/frontend/src/test/queryWrapper.tsx +9 -0
  927. manigent/web/frontend/src/test/suiteHygiene.test.ts +212 -0
  928. manigent/web/frontend/src/types/channelThreadName.test.ts +16 -0
  929. manigent/web/frontend/src/types/index.ts +903 -0
  930. manigent/web/frontend/src/utils/agentAccessCopy.test.ts +53 -0
  931. manigent/web/frontend/src/utils/agentAccessCopy.ts +231 -0
  932. manigent/web/frontend/src/utils/agentDisplay.ts +81 -0
  933. manigent/web/frontend/src/utils/briefSchedule.ts +25 -0
  934. manigent/web/frontend/src/utils/channelThreads.test.ts +109 -0
  935. manigent/web/frontend/src/utils/channelThreads.ts +179 -0
  936. manigent/web/frontend/src/utils/chatroomDisplay.ts +11 -0
  937. manigent/web/frontend/src/utils/deskLinks.test.ts +155 -0
  938. manigent/web/frontend/src/utils/deskLinks.ts +115 -0
  939. manigent/web/frontend/src/utils/deskRecipient.ts +29 -0
  940. manigent/web/frontend/src/utils/formatDate.ts +102 -0
  941. manigent/web/frontend/src/utils/frontDesk.ts +65 -0
  942. manigent/web/frontend/src/utils/hubChannelView.test.ts +178 -0
  943. manigent/web/frontend/src/utils/hubChannelView.ts +307 -0
  944. manigent/web/frontend/src/utils/hubDelivery.test.ts +105 -0
  945. manigent/web/frontend/src/utils/hubDelivery.ts +137 -0
  946. manigent/web/frontend/src/utils/hubDisplay.test.ts +157 -0
  947. manigent/web/frontend/src/utils/hubDisplay.ts +221 -0
  948. manigent/web/frontend/src/utils/hubThread.test.ts +184 -0
  949. manigent/web/frontend/src/utils/hubThread.ts +247 -0
  950. manigent/web/frontend/src/utils/labelErrors.test.ts +127 -0
  951. manigent/web/frontend/src/utils/labelErrors.ts +123 -0
  952. manigent/web/frontend/src/utils/labelView.test.ts +846 -0
  953. manigent/web/frontend/src/utils/labelView.ts +760 -0
  954. manigent/web/frontend/src/utils/legalUrls.ts +11 -0
  955. manigent/web/frontend/src/utils/messageSender.test.ts +32 -0
  956. manigent/web/frontend/src/utils/messageSender.ts +29 -0
  957. manigent/web/frontend/src/utils/oauthScopeCopy.ts +73 -0
  958. manigent/web/frontend/src/utils/personalize.ts +49 -0
  959. manigent/web/frontend/src/utils/reachableAgents.test.ts +36 -0
  960. manigent/web/frontend/src/utils/reachableAgents.ts +24 -0
  961. manigent/web/frontend/src/utils/recencyBuckets.ts +61 -0
  962. manigent/web/frontend/src/utils/roomTrust.test.ts +94 -0
  963. manigent/web/frontend/src/utils/roomTrust.ts +111 -0
  964. manigent/web/frontend/src/utils/snippetHighlight.test.ts +63 -0
  965. manigent/web/frontend/src/utils/snippetHighlight.ts +42 -0
  966. manigent/web/frontend/src/utils/sopRecipient.ts +37 -0
  967. manigent/web/frontend/src/utils/sopRequest.test.ts +91 -0
  968. manigent/web/frontend/src/utils/sopRequest.ts +68 -0
  969. manigent/web/frontend/src/utils/sopSchedule.ts +26 -0
  970. manigent/web/frontend/src/utils/sopSection.test.ts +148 -0
  971. manigent/web/frontend/src/utils/sopSection.ts +143 -0
  972. manigent/web/frontend/src/utils/textSearch.test.ts +60 -0
  973. manigent/web/frontend/src/utils/textSearch.ts +43 -0
  974. manigent/web/frontend/src/utils/thoughtPartner.ts +26 -0
  975. manigent/web/frontend/src/utils/threadName.test.ts +131 -0
  976. manigent/web/frontend/src/utils/threadName.ts +122 -0
  977. manigent/web/frontend/src/utils/ticketView.test.ts +296 -0
  978. manigent/web/frontend/src/utils/ticketView.ts +175 -0
  979. manigent/web/frontend/src/utils/todoAttachments.desiredState.test.ts +180 -0
  980. manigent/web/frontend/src/utils/todoAttachments.ts +126 -0
  981. manigent/web/frontend/src/utils/todoDraft.test.ts +84 -0
  982. manigent/web/frontend/src/utils/todoDraft.ts +46 -0
  983. manigent/web/frontend/src/utils/todoSearch.test.ts +82 -0
  984. manigent/web/frontend/src/utils/todoSearch.ts +43 -0
  985. manigent/web/frontend/src/utils/versionMatch.test.ts +295 -0
  986. manigent/web/frontend/src/utils/versionMatch.ts +245 -0
  987. manigent/web/frontend/src/vite-env.d.ts +1 -0
  988. manigent/web/frontend/tailwind.config.js +86 -0
  989. manigent/web/frontend/tos.html +188 -0
  990. manigent/web/frontend/tsconfig.dts.json +25 -0
  991. manigent/web/frontend/tsconfig.json +26 -0
  992. manigent/web/frontend/tsconfig.node.json +11 -0
  993. manigent/web/frontend/vite.config.ts +41 -0
  994. manigent/web/frontend/vitest.config.ts +16 -0
  995. manigent/ws_edge/app.py +514 -0
  996. manigent/ws_edge/auth.py +104 -0
  997. manigent/ws_edge/link.py +282 -0
  998. manigent/ws_edge/registry.py +206 -0
  999. manigent/ws_edge/terminal_relay.py +269 -0
  1000. manigent_server-0.1.0.dist-info/METADATA +138 -0
  1001. manigent_server-0.1.0.dist-info/RECORD +1004 -0
  1002. manigent_server-0.1.0.dist-info/WHEEL +4 -0
  1003. manigent_server-0.1.0.dist-info/entry_points.txt +2 -0
  1004. manigent_server-0.1.0.dist-info/licenses/LICENSE +68 -0
@@ -0,0 +1,817 @@
1
+ # SPDX-License-Identifier: BUSL-1.1
2
+ """
3
+ manigent/server/hub/operator.py
4
+ What ``manigent server hub register | verify | status`` do, as plain
5
+ functions over the server's data dir (the directory ``create_app`` gets,
6
+ holding ``channels/channels.db``). The CLI only prints what they return, so
7
+ every rule here is tested without a terminal.
8
+
9
+ - ``register``: first registration, or (``reclaim=True``) the email-verified
10
+ new-key reclaim. Refreshes the trusted hub keys, signs a RegisterRequest
11
+ with the identity key, checks the hub's statement, saves the registration.
12
+ - ``verify``: always checks the hub key chain; with a code, also submits the
13
+ emailed code (signed) and marks the registration active.
14
+ - ``status``: read-only, in every mode. Never touches the network, never
15
+ decrypts anything, and never creates a file: ``channels.db`` is opened
16
+ read-only, and only if it already exists.
17
+
18
+ Only ``register`` and ``verify`` make requests, and never before the config
19
+ is known to allow it: with the hub mode ``off`` or the config invalid they
20
+ stop with exit 3, and against a placeholder-pinned hub with exit 4, before
21
+ any read, write or request.
22
+
23
+ A running server re-reads the registration every 5 seconds
24
+ (``runtime.REGISTRATION_POLL_SECONDS``), so what ``register`` and ``verify``
25
+ write is picked up with no restart.
26
+
27
+ - ``rotate``: a chained rotation to a new identity key. ``key_rotate`` is a
28
+ socket frame and a second socket would displace the server's own, so the
29
+ command hands the request to the RUNNING server through a request file
30
+ (``{server_dir}/hub/operator/rotate.request.json``), which the runtime's
31
+ 5-second poll picks up and answers in ``rotate.result.json``. Exit 7 when no
32
+ server picks it up within ``--timeout`` (the request is removed).
33
+ - ``revoke``: the owner's sessionless identity revoke (a thief may hold the
34
+ socket), straight to the hub over HTTP; no running server is needed. The
35
+ registration is then ``held_for_reclaim`` until ``register --reclaim``.
36
+
37
+ ``same_key_reclaim`` / ``accept_same_key_reclaim`` are not commands: they are
38
+ the hub client's one automatic re-registration after close code 4004 (the
39
+ hub no longer knows this deployment), under the SAME identity key, with
40
+ ``reclaim`` and no email, built with ``register``'s own request builder.
41
+
42
+ Failures are :class:`OperatorError` with the CLI's exit code:
43
+ 3 config, 4 untrusted, 5 local state, 6 the hub refused, 7 unreachable,
44
+ 2 usage (an argument the wire schema refuses), 1 internal.
45
+ """
46
+ from __future__ import annotations
47
+
48
+ import hashlib
49
+ import json
50
+ import os
51
+ import secrets
52
+ import sqlite3
53
+ import time
54
+ from dataclasses import asdict, dataclass, replace
55
+ from pathlib import Path
56
+ from typing import Any, Mapping, Optional
57
+
58
+ import httpx
59
+ from cryptography.fernet import InvalidToken
60
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
61
+ from pydantic import ValidationError
62
+
63
+ from manigent.hub_protocol.canonical import b64url, public_key_bytes, sign_object, to_wire
64
+ from manigent.hub_protocol.codes import RegisterStatus, StatementKind
65
+ from manigent.hub_protocol.constants import PROTOCOL_VERSION, TOS_URL, TOS_VERSION
66
+ from manigent.hub_protocol.http import (
67
+ RegisterRequest,
68
+ RegisterResponse,
69
+ VerifyEmailRequest,
70
+ VerifyEmailResponse,
71
+ )
72
+ from manigent.hub_protocol.keys import REVOKED_HUB_KIDS, pinned_trust
73
+ from manigent.server.channels.config import (
74
+ HubConfig,
75
+ HubConfigError,
76
+ inspect_hub_config,
77
+ validate_hub_only,
78
+ )
79
+ from manigent.server.channels.hub_store import HubRegistration, HubStore
80
+ from manigent.server.channels.store import HUB_CHANNEL, OPEN_STATES, ChannelStore, SchemaMismatch
81
+
82
+ from . import keys
83
+ from .http import HubHttp, HubRefused, HubUnreachable
84
+ from .identity import create_identity, drop_identity, key_fingerprint, load_identity
85
+ from .trust import HubUntrusted, refresh_trust, verify_statement
86
+
87
+ # TOS_VERSION / TOS_URL: the terms ``--accept-tos`` accepts. The hub refuses
88
+ # an outdated one (``tos_outdated``, exit 6): upgrade manigent, register again.
89
+
90
+ EXIT_OK = 0
91
+ EXIT_INTERNAL = 1
92
+ EXIT_USAGE = 2
93
+ EXIT_CONFIG = 3
94
+ EXIT_UNTRUSTED = 4
95
+ EXIT_STATE = 5
96
+ EXIT_REFUSED = 6
97
+ EXIT_UNREACHABLE = 7
98
+ #: The running server took a rotate request but no answer came: it may have
99
+ #: rotated. Never reported as "nothing was changed".
100
+ EXIT_UNKNOWN = 8
101
+
102
+ RESTART_HINT = "A running server picks this up within 5 seconds; no restart is needed."
103
+
104
+
105
+ class OperatorError(Exception):
106
+ def __init__(self, exit_code: int, code: str, message: str, *, retry_after: Optional[int] = None) -> None:
107
+ self.exit_code = exit_code
108
+ self.code = code
109
+ self.message = message
110
+ self.retry_after = retry_after
111
+ super().__init__(f"{code}: {message}")
112
+
113
+ def to_json(self) -> dict:
114
+ error: dict[str, Any] = {"code": self.code, "message": self.message}
115
+ if self.retry_after is not None:
116
+ error["retry_after"] = self.retry_after
117
+ return {"ok": False, "error": error}
118
+
119
+
120
+ def fp_tail(fp: Optional[str]) -> Optional[str]:
121
+ """How a fingerprint is shown: its last eight characters."""
122
+ return f"…{fp[-8:]}" if fp else None
123
+
124
+
125
+ @dataclass(frozen=True)
126
+ class RegisterResult:
127
+ hub_url: str
128
+ deployment_id: str
129
+ name: str
130
+ status: str
131
+ fingerprint: str
132
+ reclaimed: bool
133
+
134
+ def to_json(self) -> dict:
135
+ return {"ok": True, **asdict(self), "fingerprint": fp_tail(self.fingerprint)}
136
+
137
+
138
+ @dataclass(frozen=True)
139
+ class VerifyResult:
140
+ hub_url: str
141
+ chain: str # "ok": anything else is an OperatorError
142
+ added_kids: list[str]
143
+ purged_kids: list[str]
144
+ status: str # the registration's status afterwards
145
+ deployment_id: Optional[str]
146
+ code_submitted: bool
147
+
148
+ def to_json(self) -> dict:
149
+ return {"ok": True, **asdict(self)}
150
+
151
+
152
+ @dataclass(frozen=True)
153
+ class StatusReport:
154
+ mode: str
155
+ hub_url: Optional[str]
156
+ hub_url_is_default: bool
157
+ config_problems: list[str]
158
+ warnings: list[str]
159
+ trust: str # placeholder | pinned | configured | none
160
+ trusted_kids: list[str]
161
+ revoked_kids: list[str]
162
+ registration: dict
163
+ hub_link: str
164
+ last_connected_at: Optional[float]
165
+ open_hub_rows: dict[str, int]
166
+ db_problem: Optional[str] = None
167
+
168
+ def to_json(self) -> dict:
169
+ data = {"ok": True, **asdict(self)}
170
+ if data["db_problem"] is None:
171
+ del data["db_problem"]
172
+ return data
173
+
174
+
175
+ @dataclass(frozen=True)
176
+ class RotateResult:
177
+ hub_url: str
178
+ deployment_id: str
179
+ fingerprint: str # the new current key
180
+ resigned: int # open rows re-signed under it
181
+
182
+ def to_json(self) -> dict:
183
+ return {"ok": True, **asdict(self), "fingerprint": fp_tail(self.fingerprint)}
184
+
185
+
186
+ @dataclass(frozen=True)
187
+ class RevokeResult:
188
+ hub_url: str
189
+ deployment_id: str
190
+ revoked: list[str] # fingerprints the hub revoked (tails when shown)
191
+ status: str # held_for_reclaim
192
+
193
+ def to_json(self) -> dict:
194
+ return {"ok": True, **asdict(self), "revoked": [fp_tail(f) for f in self.revoked]}
195
+
196
+
197
+ # ── shared steps ──────────────────────────────────────────────────────────
198
+
199
+ def _hub_config(env: Optional[Mapping[str, str]], server_dir: Optional[Path] = None) -> HubConfig:
200
+ """The config register and verify may act on, or exit 3 / 4 before any I/O.
201
+ In mode ``auto`` the server's generated key file stands in for an unset
202
+ MANIGENT_CHANNEL_SECRET_KEY."""
203
+ seen = inspect_hub_config(env)
204
+ mode_ok = not any(p.startswith("MANIGENT_HUB_MODE") for p in seen.problems)
205
+ if mode_ok and seen.mode == "off":
206
+ raise OperatorError(
207
+ EXIT_CONFIG, "hub_off_on_server",
208
+ "MANIGENT_HUB_MODE is off on this server; unset it (auto) or set discover_only or full "
209
+ "(in the server's environment) first",
210
+ )
211
+ try:
212
+ config = validate_hub_only(env, require_key=True,
213
+ data_dir=Path(server_dir) if server_dir is not None else None)
214
+ except HubConfigError as exc:
215
+ raise OperatorError(EXIT_CONFIG, "config_invalid", "; ".join(exc.problems)) from None
216
+ if config.refusal is not None:
217
+ raise OperatorError(
218
+ EXIT_UNTRUSTED, "hub_untrusted",
219
+ f"{config.hub_url} has only a placeholder key pinned in this release; "
220
+ "this server will not contact it",
221
+ )
222
+ return config
223
+
224
+
225
+ def _open_store(server_dir: Path) -> ChannelStore:
226
+ if not server_dir.is_dir():
227
+ raise OperatorError(
228
+ EXIT_INTERNAL, "internal",
229
+ f"server data dir not found: {server_dir} (run 'manigent server init', or pass --data-dir)",
230
+ )
231
+ store = ChannelStore(server_dir)
232
+ try:
233
+ store.check_schema()
234
+ except SchemaMismatch as exc:
235
+ store.close()
236
+ raise OperatorError(
237
+ EXIT_INTERNAL, "internal", f"channels.db has schema version {exc.found}; this server needs another",
238
+ ) from None
239
+ return store
240
+
241
+
242
+ def _hub_failure(exc: Exception) -> OperatorError:
243
+ if isinstance(exc, HubUntrusted):
244
+ return OperatorError(EXIT_UNTRUSTED, "hub_untrusted", f"cannot trust the hub: {exc}")
245
+ if isinstance(exc, HubRefused):
246
+ message = exc.message or "the hub refused the request"
247
+ return OperatorError(EXIT_REFUSED, exc.code, message, retry_after=exc.retry_after)
248
+ if isinstance(exc, HubUnreachable):
249
+ return OperatorError(EXIT_UNREACHABLE, "hub_unreachable", f"no usable answer from the hub ({exc.reason})")
250
+ if isinstance(exc, ValidationError):
251
+ return OperatorError(EXIT_UNREACHABLE, "hub_unreachable", "no usable answer from the hub (bad_body)")
252
+ raise exc
253
+
254
+
255
+ _HUB_ERRORS = (HubUntrusted, HubRefused, HubUnreachable, ValidationError)
256
+
257
+
258
+ def _email_hash(email: str) -> str:
259
+ return hashlib.sha256(email.strip().lower().encode("utf-8")).hexdigest()
260
+
261
+
262
+ def _register_request(
263
+ config: HubConfig, pubkey: str, *, name: str, label: str, email: str,
264
+ public_url: Optional[str], reclaim: bool, now: float, owner_ref: Optional[str] = None,
265
+ ) -> RegisterRequest:
266
+ try:
267
+ return RegisterRequest(
268
+ v=PROTOCOL_VERSION, aud=config.hub_url, name=name, label=label,
269
+ identity_pubkey=pubkey, contact_email=email,
270
+ tos_version=TOS_VERSION, public_url=public_url, reclaim=True if reclaim else None,
271
+ owner_ref=owner_ref, ts=int(now),
272
+ )
273
+ except ValidationError as exc:
274
+ fields = sorted({str(e["loc"][0]) for e in exc.errors() if e.get("loc")})
275
+ raise OperatorError(
276
+ EXIT_USAGE, "usage",
277
+ "invalid " + ", ".join(f"--{f.replace('contact_email', 'email').replace('_', '-')}" for f in fields)
278
+ + " (name: 3-63 of a-z 0-9 . -; label: 1-80 characters; public URL: https)",
279
+ ) from None
280
+
281
+
282
+ def _signed_register(key: Ed25519PrivateKey, config: HubConfig, **fields: Any) -> dict:
283
+ request = _register_request(config, b64url(public_key_bytes(key)), **fields)
284
+ return sign_object(to_wire(request), key, purpose=RegisterRequest.SIGNING_PURPOSE)
285
+
286
+
287
+ def _accepted(
288
+ raw: dict, *, store_hub: HubStore, config: HubConfig, key: Ed25519PrivateKey, now: float,
289
+ ) -> tuple[RegisterResponse, dict]:
290
+ response = RegisterResponse.model_validate(raw)
291
+ if response.status not in (RegisterStatus.PENDING_EMAIL, RegisterStatus.ACTIVE):
292
+ raise HubUnreachable("unknown_status")
293
+ statement = raw["statement"]
294
+ verify_statement(
295
+ statement, hub=store_hub, config=config, now=now, kind=StatementKind.REGISTRATION_ACCEPTED,
296
+ fingerprint=key_fingerprint(key), deployment_id=response.deployment_id,
297
+ )
298
+ return response, statement
299
+
300
+
301
+ # ── register ──────────────────────────────────────────────────────────────
302
+
303
+ def register(
304
+ server_dir: Path, *, name: str, email: str, label: Optional[str] = None,
305
+ public_url: Optional[str] = None, reclaim: bool = False,
306
+ env: Optional[Mapping[str, str]] = None, transport: Optional[httpx.BaseTransport] = None,
307
+ now: Optional[float] = None,
308
+ ) -> RegisterResult:
309
+ """First registration (``reclaim=False``) or the new-key reclaim."""
310
+ config = _hub_config(env, server_dir)
311
+ assert config.fernet is not None # require_key=True
312
+ now = time.time() if now is None else now
313
+ label = label or name
314
+ # The arguments first, against the wire schema (any key will do): a usage
315
+ # error must not cost a request, a key or a row.
316
+ _register_request(config, "A" * 43, name=name, label=label, email=email,
317
+ public_url=public_url, reclaim=reclaim, now=now)
318
+ store = _open_store(Path(server_dir))
319
+ try:
320
+ hub = HubStore(store)
321
+ reg = hub.registration(config.hub_url)
322
+ registered = reg is not None and reg.status != "none"
323
+ if registered and not reclaim:
324
+ raise OperatorError(
325
+ EXIT_STATE, "already_registered",
326
+ f"this server is already registered with {config.hub_url} (status {reg.status}); "
327
+ "use --reclaim to register it again under a new key",
328
+ )
329
+ if reclaim and not registered:
330
+ raise OperatorError(
331
+ EXIT_STATE, "not_registered",
332
+ f"this server has no registration with {config.hub_url} to reclaim; register without --reclaim",
333
+ )
334
+ with HubHttp(config.hub_url, transport=transport) as client:
335
+ try:
336
+ refresh_trust(hub, config, client, now=now)
337
+ except _HUB_ERRORS as exc:
338
+ raise _hub_failure(exc) from None
339
+ if reclaim:
340
+ assert reg is not None
341
+ return _reclaim(store, hub, config, client, reg, name=name, label=label,
342
+ email=email, public_url=public_url, now=now)
343
+ return _first_register(store, hub, config, client, reg, name=name, label=label,
344
+ email=email, public_url=public_url, now=now)
345
+ finally:
346
+ store.close()
347
+
348
+
349
+ def _first_register(
350
+ store: ChannelStore, hub: HubStore, config: HubConfig, client: HubHttp,
351
+ reg: Optional[HubRegistration], *, name: str, label: str, email: str,
352
+ public_url: Optional[str], now: float,
353
+ ) -> RegisterResult:
354
+ pending, key, body = prepare_first_register(store, hub, config, reg, name=name, label=label, email=email,
355
+ public_url=public_url, now=now)
356
+ try:
357
+ raw = client.register(body)
358
+ except _HUB_ERRORS as exc:
359
+ raise _hub_failure(exc) from None
360
+ return finish_first_register(hub, config, pending, key, raw, now=now)
361
+
362
+
363
+ def prepare_first_register(
364
+ store: ChannelStore, hub: HubStore, config: HubConfig, reg: Optional[HubRegistration], *,
365
+ name: str, label: str, email: str, public_url: Optional[str], now: float,
366
+ owner_user_id: Optional[str] = None,
367
+ ) -> tuple[HubRegistration, Ed25519PrivateKey, dict]:
368
+ """The store half before the request: the identity key (a retried first
369
+ registration keeps its key, so a hub that took the first attempt sees the
370
+ same key again), the pending row, and the signed body. ``owner_user_id``:
371
+ the user enrolling this server from the browser (server/hub/enroll.py);
372
+ their owner_ref rides in the request, so the code that activates the
373
+ deployment verifies them as an owner too.
374
+
375
+ A row the hub already answered (``pending_email``) is NOT rewritten here:
376
+ only the hub's next answer (finish_first_register) changes it. Saving it as
377
+ ``none`` before a request whose answer is lost would forget that the hub
378
+ holds this key under this name, and every later attempt would pick a new
379
+ name the hub refuses (the key is taken)."""
380
+ fernet = config.fernet
381
+ assert fernet is not None
382
+ connection_id = reg.identity_connection_id if reg is not None else None
383
+ if connection_id is not None and store.get_connection(connection_id) is not None:
384
+ try:
385
+ key = load_identity(store, connection_id, fernet)
386
+ except InvalidToken:
387
+ raise OperatorError(
388
+ EXIT_CONFIG, "config_invalid",
389
+ "MANIGENT_CHANNEL_SECRET_KEY does not decrypt this server's hub identity key",
390
+ ) from None
391
+ else:
392
+ connection_id, key = create_identity(store, hub_url=config.hub_url, fernet=fernet, now=now)
393
+ fp = key_fingerprint(key)
394
+ base = reg or HubRegistration(hub_url=config.hub_url, created_at=now, updated_at=now)
395
+ answered = reg is not None and reg.status == "pending_email"
396
+ pending = replace(
397
+ base, name=name, label=label, public_url=public_url, status=reg.status if answered else "none",
398
+ genesis_fp=fp, current_fp=fp, identity_connection_id=connection_id,
399
+ contact_email_hash=_email_hash(email), updated_at=now,
400
+ )
401
+ if not answered:
402
+ hub.save_registration(pending, fernet=fernet) # the reporter secret is made here, once
403
+ owner_ref = hub.owner_ref(config.hub_url, fernet, owner_user_id) if owner_user_id else None
404
+ body = _signed_register(key, config, name=name, label=label, email=email,
405
+ public_url=public_url, reclaim=False, now=now, owner_ref=owner_ref)
406
+ return pending, key, body
407
+
408
+
409
+ def finish_first_register(
410
+ hub: HubStore, config: HubConfig, pending: HubRegistration, key: Ed25519PrivateKey, raw: dict, *,
411
+ now: float,
412
+ ) -> RegisterResult:
413
+ """The store half after the hub's answer: its statement checked, the
414
+ deployment id and status saved."""
415
+ assert config.fernet is not None
416
+ try:
417
+ response, statement = _accepted(raw, store_hub=hub, config=config, key=key, now=now)
418
+ except _HUB_ERRORS as exc:
419
+ raise _hub_failure(exc) from None
420
+ hub.save_registration(replace(
421
+ pending, deployment_id=response.deployment_id, status=str(response.status),
422
+ statement=statement, updated_at=now,
423
+ ), fernet=config.fernet)
424
+ return RegisterResult(
425
+ hub_url=config.hub_url, deployment_id=response.deployment_id, name=pending.name or "",
426
+ status=str(response.status), fingerprint=pending.genesis_fp or "", reclaimed=False,
427
+ )
428
+
429
+
430
+ def _reclaim(
431
+ store: ChannelStore, hub: HubStore, config: HubConfig, client: HubHttp,
432
+ reg: HubRegistration, *, name: str, label: str, email: str,
433
+ public_url: Optional[str], now: float,
434
+ ) -> RegisterResult:
435
+ """The new-key reclaim: a new, unchained key in a NEW connection row. Only
436
+ once the hub accepts it does one commit swap it in (genesis = current =
437
+ the new key) and delete the old row. Any failure deletes the new row and
438
+ leaves everything else exactly as it was. The reporter secret is kept."""
439
+ fernet = config.fernet
440
+ assert fernet is not None
441
+ new_connection_id, key = create_identity(store, hub_url=config.hub_url, fernet=fernet, now=now)
442
+ try:
443
+ body = _signed_register(key, config, name=name, label=label, email=email,
444
+ public_url=public_url, reclaim=True, now=now)
445
+ response, statement = _accepted(client.register(body), store_hub=hub, config=config, key=key, now=now)
446
+ except BaseException as exc:
447
+ drop_identity(store, new_connection_id, now=now)
448
+ if isinstance(exc, _HUB_ERRORS):
449
+ raise _hub_failure(exc) from None
450
+ raise
451
+ fp = key_fingerprint(key)
452
+ hub.save_registration(replace(
453
+ reg, deployment_id=response.deployment_id, name=name, label=label, public_url=public_url,
454
+ status=str(response.status), genesis_fp=fp, current_fp=fp,
455
+ identity_connection_id=new_connection_id, contact_email_hash=_email_hash(email),
456
+ statement=statement, updated_at=now,
457
+ ), fernet=fernet, replaces_identity=reg.identity_connection_id)
458
+ return RegisterResult(
459
+ hub_url=config.hub_url, deployment_id=response.deployment_id, name=name,
460
+ status=str(response.status), fingerprint=fp, reclaimed=True,
461
+ )
462
+
463
+
464
+ # ── the automatic same-key reclaim (close code 4004) ──────────────────────
465
+
466
+ def same_key_reclaim(config: HubConfig, reg: HubRegistration, key: Ed25519PrivateKey, *, now: float) -> dict:
467
+ """The signed RegisterRequest for the automatic reclaim: this deployment's
468
+ name, label and public URL, its CURRENT identity key, reclaim, no email."""
469
+ request = RegisterRequest(
470
+ v=PROTOCOL_VERSION, aud=config.hub_url, name=reg.name or "", label=reg.label or reg.name or "",
471
+ identity_pubkey=b64url(public_key_bytes(key)), contact_email=None,
472
+ tos_version=TOS_VERSION, public_url=reg.public_url, reclaim=True, ts=int(now),
473
+ )
474
+ return sign_object(to_wire(request), key, purpose=RegisterRequest.SIGNING_PURPOSE)
475
+
476
+
477
+ def accept_same_key_reclaim(
478
+ hub: HubStore, config: HubConfig, reg: HubRegistration, key: Ed25519PrivateKey,
479
+ raw: dict, *, now: float,
480
+ ) -> HubRegistration:
481
+ """The hub's answer to ``same_key_reclaim``: its statement checked like a
482
+ registration's, then the registration saved with the (possibly new)
483
+ deployment id and status. The key, genesis and identity row are kept.
484
+ HubUntrusted / ValidationError / HubUnreachable when the answer is not
485
+ usable; nothing is saved then."""
486
+ assert config.fernet is not None
487
+ response, statement = _accepted(raw, store_hub=hub, config=config, key=key, now=now)
488
+ saved = replace(reg, deployment_id=response.deployment_id, status=str(response.status),
489
+ statement=statement, updated_at=now)
490
+ hub.save_registration(saved, fernet=config.fernet)
491
+ return saved
492
+
493
+
494
+ # ── verify ────────────────────────────────────────────────────────────────
495
+
496
+ def _verify_request(config: HubConfig, deployment_id: str, code: str, now: float) -> VerifyEmailRequest:
497
+ try:
498
+ return VerifyEmailRequest(aud=config.hub_url, deployment_id=deployment_id, code=code, ts=int(now))
499
+ except ValidationError:
500
+ raise OperatorError(EXIT_USAGE, "usage", "invalid --code (4-16 letters or digits)") from None
501
+
502
+
503
+ def verify(
504
+ server_dir: Path, *, code: Optional[str] = None, env: Optional[Mapping[str, str]] = None,
505
+ transport: Optional[httpx.BaseTransport] = None, now: Optional[float] = None,
506
+ ) -> VerifyResult:
507
+ """Check the pinned hub key chain; with ``code``, submit the emailed code."""
508
+ config = _hub_config(env, server_dir)
509
+ fernet = config.fernet
510
+ assert fernet is not None
511
+ now = time.time() if now is None else now
512
+ if code is not None:
513
+ _verify_request(config, "deployment", code, now) # a bad --code: exit 2 before any I/O
514
+ store = _open_store(Path(server_dir))
515
+ try:
516
+ hub = HubStore(store)
517
+ reg = hub.registration(config.hub_url)
518
+ if code is not None:
519
+ if reg is None or reg.status == "none" or reg.deployment_id is None:
520
+ raise OperatorError(
521
+ EXIT_STATE, "not_registered",
522
+ f"this server has no registration with {config.hub_url}; run 'hub register' first",
523
+ )
524
+ if reg.status != "pending_email":
525
+ raise OperatorError(
526
+ EXIT_STATE, "not_pending",
527
+ f"the registration is {reg.status}, not waiting for an email code",
528
+ )
529
+ try:
530
+ key = load_identity(store, reg.identity_connection_id or "", fernet)
531
+ except KeyError:
532
+ raise OperatorError(EXIT_STATE, "not_registered", "this server's hub identity key is missing") from None
533
+ except InvalidToken:
534
+ raise OperatorError(
535
+ EXIT_CONFIG, "config_invalid",
536
+ "MANIGENT_CHANNEL_SECRET_KEY does not decrypt this server's hub identity key",
537
+ ) from None
538
+ request = _verify_request(config, reg.deployment_id, code, now)
539
+ with HubHttp(config.hub_url, transport=transport) as client:
540
+ try:
541
+ refreshed = refresh_trust(hub, config, client, now=now)
542
+ if code is None:
543
+ return VerifyResult(
544
+ hub_url=config.hub_url, chain="ok", added_kids=refreshed.added,
545
+ purged_kids=refreshed.purged, status=reg.status if reg else "none",
546
+ deployment_id=reg.deployment_id if reg else None, code_submitted=False,
547
+ )
548
+ assert reg is not None
549
+ submit_code(hub, config, client, reg, key, request, now=now)
550
+ except _HUB_ERRORS as exc:
551
+ raise _hub_failure(exc) from None
552
+ return VerifyResult(
553
+ hub_url=config.hub_url, chain="ok", added_kids=refreshed.added, purged_kids=refreshed.purged,
554
+ status="active", deployment_id=reg.deployment_id, code_submitted=True,
555
+ )
556
+ finally:
557
+ store.close()
558
+
559
+
560
+ def submit_code(
561
+ hub: HubStore, config: HubConfig, client: HubHttp, reg: HubRegistration, key: Ed25519PrivateKey,
562
+ request: VerifyEmailRequest, *, now: float,
563
+ ) -> HubRegistration:
564
+ """Send the registration's emailed code; on the hub's signed yes, save the
565
+ registration active (and verify the owner whose code it was, if one
566
+ enrolled it from the browser). Raises the hub errors (_HUB_ERRORS)."""
567
+ assert config.fernet is not None and reg.deployment_id is not None
568
+ body = sign_object(to_wire(request), key, purpose=VerifyEmailRequest.SIGNING_PURPOSE)
569
+ raw = client.verify_email(reg.deployment_id, body)
570
+ response = VerifyEmailResponse.model_validate(raw)
571
+ if response.status != RegisterStatus.ACTIVE:
572
+ raise HubUnreachable("unknown_status")
573
+ statement = raw["statement"]
574
+ verify_statement(
575
+ statement, hub=hub, config=config, now=now, kind=StatementKind.REGISTRATION_ACCEPTED,
576
+ fingerprint=reg.current_fp or "", deployment_id=reg.deployment_id,
577
+ )
578
+ saved = replace(reg, status="active", statement=statement, updated_at=now)
579
+ hub.save_registration(saved, fernet=config.fernet)
580
+ hub.registration_verified_owners(config.hub_url, now=now)
581
+ return saved
582
+
583
+
584
+ # ── status ────────────────────────────────────────────────────────────────
585
+
586
+ def status(server_dir: Path, *, env: Optional[Mapping[str, str]] = None) -> StatusReport:
587
+ """Read-only, in every mode: no network, no decryption, no file created."""
588
+ config = inspect_hub_config(env)
589
+ server_dir = Path(server_dir)
590
+ if server_dir.exists() and not os.access(server_dir, os.R_OK | os.X_OK):
591
+ raise OperatorError(EXIT_INTERNAL, "internal", f"cannot read the server data dir: {server_dir}")
592
+ url = config.hub_url
593
+ revoked = sorted(REVOKED_HUB_KIDS.get(url, frozenset())) if url else []
594
+ warnings = list(config.warnings)
595
+ if url and not config.is_default:
596
+ warnings.append(f"MANIGENT_HUB_URL is not the default hub ({url})")
597
+
598
+ registration: dict[str, Any] = {"status": "none"}
599
+ stored_kids: list[str] = []
600
+ hub_link, last_connected = "off", None
601
+ open_rows: dict[str, int] = {}
602
+ db_problem: Optional[str] = None
603
+ db_path = server_dir / "channels" / "channels.db"
604
+ if url and db_path.exists():
605
+ try:
606
+ registration, stored_kids, hub_link, last_connected, open_rows = _read_db(db_path, url)
607
+ except sqlite3.DatabaseError as exc:
608
+ if not os.access(db_path, os.R_OK):
609
+ raise OperatorError(EXIT_INTERNAL, "internal", f"cannot read {db_path}") from None
610
+ db_problem = f"channels.db could not be read ({type(exc).__name__})"
611
+
612
+ if config.refusal is not None:
613
+ trust, kids = "placeholder", []
614
+ elif not url:
615
+ trust, kids = "none", []
616
+ elif config.is_default:
617
+ trust, kids = "pinned", stored_kids or sorted(pinned_trust(url))
618
+ elif config.configured_keys or stored_kids:
619
+ trust, kids = "configured", stored_kids or sorted(config.configured_keys)
620
+ else:
621
+ trust, kids = "none", []
622
+ if config.mode == "off":
623
+ hub_link = "off"
624
+ elif config.refusal is not None:
625
+ hub_link = "untrusted"
626
+ return StatusReport(
627
+ mode=config.mode, hub_url=url or None, hub_url_is_default=config.is_default,
628
+ config_problems=list(config.problems), warnings=warnings, trust=trust,
629
+ trusted_kids=kids, revoked_kids=revoked, registration=registration,
630
+ hub_link=hub_link, last_connected_at=last_connected, open_hub_rows=open_rows,
631
+ db_problem=db_problem,
632
+ )
633
+
634
+
635
+ def _read_db(db_path: Path, url: str) -> tuple[dict, list[str], str, Optional[float], dict[str, int]]:
636
+ db = sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True)
637
+ db.row_factory = sqlite3.Row
638
+ try:
639
+ row = db.execute(
640
+ "SELECT status, deployment_id, name, label, public_url, genesis_fp, current_fp,"
641
+ " hub_link, last_connected_at FROM hub_registration WHERE hub_url = ?", (url,),
642
+ ).fetchone()
643
+ registration: dict[str, Any] = {"status": "none"}
644
+ hub_link, last_connected = "off", None
645
+ if row is not None:
646
+ registration = {
647
+ "status": row["status"], "deployment_id": row["deployment_id"], "name": row["name"],
648
+ "label": row["label"], "public_url": row["public_url"],
649
+ "genesis_fp": fp_tail(row["genesis_fp"]), "current_fp": fp_tail(row["current_fp"]),
650
+ }
651
+ hub_link, last_connected = row["hub_link"], row["last_connected_at"]
652
+ kids = sorted(r[0] for r in db.execute(
653
+ "SELECT kid FROM hub_trusted_keys WHERE hub_url = ?", (url,)))
654
+ marks = ", ".join("?" * len(OPEN_STATES))
655
+ open_rows = {r[0]: r[1] for r in db.execute(
656
+ "SELECT o.state, COUNT(*) FROM outbox o JOIN connections c ON c.id = o.connection_id"
657
+ f" AND c.channel = ? WHERE o.state IN ({marks}) GROUP BY o.state ORDER BY o.state",
658
+ (HUB_CHANNEL, *OPEN_STATES),
659
+ )}
660
+ return registration, kids, hub_link, last_connected, open_rows
661
+ finally:
662
+ db.close()
663
+
664
+
665
+ # ── rotate (handed to the running server) ─────────────────────────────────
666
+
667
+ #: Under the server dir: where ``rotate`` leaves its request and finds the answer.
668
+ OPERATOR_REQUEST_DIR = Path("hub") / "operator"
669
+ ROTATE_REQUEST = "rotate.request.json"
670
+ ROTATE_RESULT = "rotate.result.json"
671
+ #: A request older than this is ignored by the server (a CLI long gone).
672
+ REQUEST_MAX_AGE_S = 60.0
673
+ #: How often ``rotate`` looks for the answer.
674
+ RESULT_POLL_S = 0.25
675
+
676
+
677
+ def operator_dir(server_dir: Path) -> Path:
678
+ return Path(server_dir) / OPERATOR_REQUEST_DIR
679
+
680
+
681
+ def _write_private(path: Path, data: dict) -> None:
682
+ """Atomically, mode 0600, in a 0700 directory."""
683
+ path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
684
+ tmp = path.with_name(f".{path.name}.{secrets.token_hex(4)}")
685
+ fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
686
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
687
+ json.dump(data, fh)
688
+ os.replace(tmp, path)
689
+
690
+
691
+ def _read_json(path: Path) -> Optional[dict]:
692
+ try:
693
+ data = json.loads(path.read_text(encoding="utf-8"))
694
+ except (OSError, ValueError):
695
+ return None
696
+ return data if isinstance(data, dict) else None
697
+
698
+
699
+ def _rotation_allowed(store: ChannelStore, config: HubConfig) -> HubRegistration:
700
+ reg = HubStore(store).registration(config.hub_url)
701
+ if reg is None or reg.status != "active" or reg.deployment_id is None:
702
+ raise OperatorError(EXIT_STATE, "not_active",
703
+ f"this server has no active registration with {config.hub_url}")
704
+ if keys.pending_rotations(store, config, reg):
705
+ raise OperatorError(EXIT_STATE, "rotation_pending",
706
+ "an earlier rotation is not settled yet; it settles on the server's next connect")
707
+ return reg
708
+
709
+
710
+ def rotate(
711
+ server_dir: Path, *, env: Optional[Mapping[str, str]] = None, timeout_s: float = 30.0,
712
+ now: Optional[float] = None, sleep=time.sleep,
713
+ ) -> RotateResult:
714
+ """Ask the running server to rotate its identity key (``keys.rotate_chained``
715
+ over its own socket), and wait up to ``timeout_s`` for the answer."""
716
+ config = _hub_config(env, server_dir)
717
+ server_dir = Path(server_dir)
718
+ store = _open_store(server_dir)
719
+ try:
720
+ reg = _rotation_allowed(store, config)
721
+ finally:
722
+ store.close()
723
+ folder = operator_dir(server_dir)
724
+ request_path, result_path = folder / ROTATE_REQUEST, folder / ROTATE_RESULT
725
+ nonce = secrets.token_hex(16)
726
+ result_path.unlink(missing_ok=True)
727
+ _write_private(request_path, {"nonce": nonce, "ts": time.time() if now is None else now,
728
+ "hub_url": config.hub_url})
729
+ deadline = time.monotonic() + timeout_s
730
+ while True:
731
+ answer = _read_json(result_path)
732
+ if answer is not None and answer.get("nonce") == nonce:
733
+ result_path.unlink(missing_ok=True)
734
+ break
735
+ if time.monotonic() >= deadline:
736
+ current = _read_json(request_path)
737
+ if current is not None and current.get("nonce") == nonce:
738
+ request_path.unlink(missing_ok=True)
739
+ raise OperatorError(
740
+ EXIT_UNREACHABLE, "no_server",
741
+ f"no running server picked up the request within {timeout_s:g} s "
742
+ "(start the server with the hub on, then run this again); nothing was changed",
743
+ )
744
+ # Claimed: the server is (or was) rotating, and may have committed.
745
+ raise OperatorError(
746
+ EXIT_UNKNOWN, "outcome_unknown",
747
+ f"the running server took the request but did not answer within {timeout_s:g} s; "
748
+ "the rotation may have happened. Run `manigent server hub status` to see the current key",
749
+ )
750
+ sleep(RESULT_POLL_S)
751
+ if answer.get("ok"):
752
+ return RotateResult(hub_url=config.hub_url, deployment_id=reg.deployment_id or "",
753
+ fingerprint=str(answer.get("fingerprint") or ""),
754
+ resigned=int(answer.get("resigned") or 0))
755
+ code, message = str(answer.get("code") or "internal"), str(answer.get("message") or "")
756
+ exit_code = {"state": EXIT_STATE, "refused": EXIT_REFUSED, "unreachable": EXIT_UNREACHABLE,
757
+ "unknown": EXIT_UNKNOWN}.get(str(answer.get("kind")), EXIT_INTERNAL)
758
+ raise OperatorError(exit_code, code, message or code)
759
+
760
+
761
+ def take_rotate_request(server_dir: Path, *, hub_url: str, now: float) -> Optional[str]:
762
+ """The server's side: claim a waiting request (it is removed), and return
763
+ its nonce, or None when there is none, it is older than
764
+ ``REQUEST_MAX_AGE_S``, or it is for another hub."""
765
+ request_path = operator_dir(server_dir) / ROTATE_REQUEST
766
+ if not request_path.exists():
767
+ return None
768
+ claimed = request_path.with_name(f".{ROTATE_REQUEST}.claimed")
769
+ try:
770
+ os.replace(request_path, claimed)
771
+ except OSError:
772
+ return None
773
+ request = _read_json(claimed)
774
+ claimed.unlink(missing_ok=True)
775
+ if (request is None or not isinstance(request.get("nonce"), str) or request.get("hub_url") != hub_url
776
+ or not isinstance(request.get("ts"), (int, float)) or abs(now - request["ts"]) > REQUEST_MAX_AGE_S):
777
+ return None
778
+ return request["nonce"]
779
+
780
+
781
+ def answer_rotate_request(server_dir: Path, nonce: str, answer: dict) -> None:
782
+ """The server's answer for the waiting ``rotate``."""
783
+ _write_private(operator_dir(server_dir) / ROTATE_RESULT, {**answer, "nonce": nonce})
784
+
785
+
786
+ # ── revoke (sessionless) ──────────────────────────────────────────────────
787
+
788
+ def revoke(
789
+ server_dir: Path, *, env: Optional[Mapping[str, str]] = None,
790
+ transport: Optional[httpx.BaseTransport] = None, now: Optional[float] = None,
791
+ ) -> RevokeResult:
792
+ """Revoke this server's current identity key at the hub, sessionless
793
+ (``keys.revoke_own_identity``). Needs no running server."""
794
+ config = _hub_config(env, server_dir)
795
+ now = time.time() if now is None else now
796
+ store = _open_store(Path(server_dir))
797
+ try:
798
+ reg = HubStore(store).registration(config.hub_url)
799
+ if reg is not None and reg.status == "held_for_reclaim":
800
+ raise OperatorError(
801
+ EXIT_STATE, "already_revoked",
802
+ "this server's key is already revoked; run 'manigent server hub register --reclaim'",
803
+ )
804
+ if reg is None or reg.deployment_id is None or reg.status == "none":
805
+ raise OperatorError(EXIT_STATE, "not_registered",
806
+ f"this server has no registration with {config.hub_url}")
807
+ with HubHttp(config.hub_url, transport=transport) as client:
808
+ try:
809
+ revoked = keys.revoke_own_identity(store, config, client, now=now)
810
+ except keys.RevokeRefused as exc:
811
+ raise OperatorError(EXIT_REFUSED, exc.code, exc.detail or exc.code) from None
812
+ except _HUB_ERRORS as exc:
813
+ raise _hub_failure(exc) from None
814
+ return RevokeResult(hub_url=config.hub_url, deployment_id=reg.deployment_id, revoked=revoked,
815
+ status="held_for_reclaim")
816
+ finally:
817
+ store.close()