availsync 0.1.0

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 (460) hide show
  1. package/.adal/skills/stripe-best-practices/SKILL.md +42 -0
  2. package/.adal/skills/stripe-best-practices/references/billing.md +36 -0
  3. package/.adal/skills/stripe-best-practices/references/connect.md +48 -0
  4. package/.adal/skills/stripe-best-practices/references/payments.md +79 -0
  5. package/.adal/skills/stripe-best-practices/references/security.md +109 -0
  6. package/.adal/skills/stripe-best-practices/references/treasury.md +16 -0
  7. package/.adal/skills/stripe-projects/SKILL.md +139 -0
  8. package/.adal/skills/upgrade-stripe/SKILL.md +185 -0
  9. package/.agents/skills/stripe-best-practices/SKILL.md +42 -0
  10. package/.agents/skills/stripe-best-practices/references/billing.md +36 -0
  11. package/.agents/skills/stripe-best-practices/references/connect.md +48 -0
  12. package/.agents/skills/stripe-best-practices/references/payments.md +79 -0
  13. package/.agents/skills/stripe-best-practices/references/security.md +109 -0
  14. package/.agents/skills/stripe-best-practices/references/treasury.md +16 -0
  15. package/.agents/skills/stripe-projects/SKILL.md +139 -0
  16. package/.agents/skills/upgrade-stripe/SKILL.md +185 -0
  17. package/.augment/skills/stripe-best-practices/SKILL.md +42 -0
  18. package/.augment/skills/stripe-best-practices/references/billing.md +36 -0
  19. package/.augment/skills/stripe-best-practices/references/connect.md +48 -0
  20. package/.augment/skills/stripe-best-practices/references/payments.md +79 -0
  21. package/.augment/skills/stripe-best-practices/references/security.md +109 -0
  22. package/.augment/skills/stripe-best-practices/references/treasury.md +16 -0
  23. package/.augment/skills/stripe-projects/SKILL.md +139 -0
  24. package/.augment/skills/upgrade-stripe/SKILL.md +185 -0
  25. package/.bob/skills/stripe-best-practices/SKILL.md +42 -0
  26. package/.bob/skills/stripe-best-practices/references/billing.md +36 -0
  27. package/.bob/skills/stripe-best-practices/references/connect.md +48 -0
  28. package/.bob/skills/stripe-best-practices/references/payments.md +79 -0
  29. package/.bob/skills/stripe-best-practices/references/security.md +109 -0
  30. package/.bob/skills/stripe-best-practices/references/treasury.md +16 -0
  31. package/.bob/skills/stripe-projects/SKILL.md +139 -0
  32. package/.bob/skills/upgrade-stripe/SKILL.md +185 -0
  33. package/.claude/settings.local.json +7 -0
  34. package/.claude/skills/stripe-best-practices/SKILL.md +42 -0
  35. package/.claude/skills/stripe-best-practices/references/billing.md +36 -0
  36. package/.claude/skills/stripe-best-practices/references/connect.md +48 -0
  37. package/.claude/skills/stripe-best-practices/references/payments.md +79 -0
  38. package/.claude/skills/stripe-best-practices/references/security.md +109 -0
  39. package/.claude/skills/stripe-best-practices/references/treasury.md +16 -0
  40. package/.claude/skills/stripe-projects/SKILL.md +139 -0
  41. package/.claude/skills/upgrade-stripe/SKILL.md +185 -0
  42. package/.codebuddy/skills/stripe-best-practices/SKILL.md +42 -0
  43. package/.codebuddy/skills/stripe-best-practices/references/billing.md +36 -0
  44. package/.codebuddy/skills/stripe-best-practices/references/connect.md +48 -0
  45. package/.codebuddy/skills/stripe-best-practices/references/payments.md +79 -0
  46. package/.codebuddy/skills/stripe-best-practices/references/security.md +109 -0
  47. package/.codebuddy/skills/stripe-best-practices/references/treasury.md +16 -0
  48. package/.codebuddy/skills/stripe-projects/SKILL.md +139 -0
  49. package/.codebuddy/skills/upgrade-stripe/SKILL.md +185 -0
  50. package/.commandcode/skills/stripe-best-practices/SKILL.md +42 -0
  51. package/.commandcode/skills/stripe-best-practices/references/billing.md +36 -0
  52. package/.commandcode/skills/stripe-best-practices/references/connect.md +48 -0
  53. package/.commandcode/skills/stripe-best-practices/references/payments.md +79 -0
  54. package/.commandcode/skills/stripe-best-practices/references/security.md +109 -0
  55. package/.commandcode/skills/stripe-best-practices/references/treasury.md +16 -0
  56. package/.commandcode/skills/stripe-projects/SKILL.md +139 -0
  57. package/.commandcode/skills/upgrade-stripe/SKILL.md +185 -0
  58. package/.continue/skills/stripe-best-practices/SKILL.md +42 -0
  59. package/.continue/skills/stripe-best-practices/references/billing.md +36 -0
  60. package/.continue/skills/stripe-best-practices/references/connect.md +48 -0
  61. package/.continue/skills/stripe-best-practices/references/payments.md +79 -0
  62. package/.continue/skills/stripe-best-practices/references/security.md +109 -0
  63. package/.continue/skills/stripe-best-practices/references/treasury.md +16 -0
  64. package/.continue/skills/stripe-projects/SKILL.md +139 -0
  65. package/.continue/skills/upgrade-stripe/SKILL.md +185 -0
  66. package/.cortex/skills/stripe-best-practices/SKILL.md +42 -0
  67. package/.cortex/skills/stripe-best-practices/references/billing.md +36 -0
  68. package/.cortex/skills/stripe-best-practices/references/connect.md +48 -0
  69. package/.cortex/skills/stripe-best-practices/references/payments.md +79 -0
  70. package/.cortex/skills/stripe-best-practices/references/security.md +109 -0
  71. package/.cortex/skills/stripe-best-practices/references/treasury.md +16 -0
  72. package/.cortex/skills/stripe-projects/SKILL.md +139 -0
  73. package/.cortex/skills/upgrade-stripe/SKILL.md +185 -0
  74. package/.crush/skills/stripe-best-practices/SKILL.md +42 -0
  75. package/.crush/skills/stripe-best-practices/references/billing.md +36 -0
  76. package/.crush/skills/stripe-best-practices/references/connect.md +48 -0
  77. package/.crush/skills/stripe-best-practices/references/payments.md +79 -0
  78. package/.crush/skills/stripe-best-practices/references/security.md +109 -0
  79. package/.crush/skills/stripe-best-practices/references/treasury.md +16 -0
  80. package/.crush/skills/stripe-projects/SKILL.md +139 -0
  81. package/.crush/skills/upgrade-stripe/SKILL.md +185 -0
  82. package/.env.example +20 -0
  83. package/.factory/skills/stripe-best-practices/SKILL.md +42 -0
  84. package/.factory/skills/stripe-best-practices/references/billing.md +36 -0
  85. package/.factory/skills/stripe-best-practices/references/connect.md +48 -0
  86. package/.factory/skills/stripe-best-practices/references/payments.md +79 -0
  87. package/.factory/skills/stripe-best-practices/references/security.md +109 -0
  88. package/.factory/skills/stripe-best-practices/references/treasury.md +16 -0
  89. package/.factory/skills/stripe-projects/SKILL.md +139 -0
  90. package/.factory/skills/upgrade-stripe/SKILL.md +185 -0
  91. package/.goose/skills/stripe-best-practices/SKILL.md +42 -0
  92. package/.goose/skills/stripe-best-practices/references/billing.md +36 -0
  93. package/.goose/skills/stripe-best-practices/references/connect.md +48 -0
  94. package/.goose/skills/stripe-best-practices/references/payments.md +79 -0
  95. package/.goose/skills/stripe-best-practices/references/security.md +109 -0
  96. package/.goose/skills/stripe-best-practices/references/treasury.md +16 -0
  97. package/.goose/skills/stripe-projects/SKILL.md +139 -0
  98. package/.goose/skills/upgrade-stripe/SKILL.md +185 -0
  99. package/.iflow/skills/stripe-best-practices/SKILL.md +42 -0
  100. package/.iflow/skills/stripe-best-practices/references/billing.md +36 -0
  101. package/.iflow/skills/stripe-best-practices/references/connect.md +48 -0
  102. package/.iflow/skills/stripe-best-practices/references/payments.md +79 -0
  103. package/.iflow/skills/stripe-best-practices/references/security.md +109 -0
  104. package/.iflow/skills/stripe-best-practices/references/treasury.md +16 -0
  105. package/.iflow/skills/stripe-projects/SKILL.md +139 -0
  106. package/.iflow/skills/upgrade-stripe/SKILL.md +185 -0
  107. package/.junie/skills/stripe-best-practices/SKILL.md +42 -0
  108. package/.junie/skills/stripe-best-practices/references/billing.md +36 -0
  109. package/.junie/skills/stripe-best-practices/references/connect.md +48 -0
  110. package/.junie/skills/stripe-best-practices/references/payments.md +79 -0
  111. package/.junie/skills/stripe-best-practices/references/security.md +109 -0
  112. package/.junie/skills/stripe-best-practices/references/treasury.md +16 -0
  113. package/.junie/skills/stripe-projects/SKILL.md +139 -0
  114. package/.junie/skills/upgrade-stripe/SKILL.md +185 -0
  115. package/.kilocode/skills/stripe-best-practices/SKILL.md +42 -0
  116. package/.kilocode/skills/stripe-best-practices/references/billing.md +36 -0
  117. package/.kilocode/skills/stripe-best-practices/references/connect.md +48 -0
  118. package/.kilocode/skills/stripe-best-practices/references/payments.md +79 -0
  119. package/.kilocode/skills/stripe-best-practices/references/security.md +109 -0
  120. package/.kilocode/skills/stripe-best-practices/references/treasury.md +16 -0
  121. package/.kilocode/skills/stripe-projects/SKILL.md +139 -0
  122. package/.kilocode/skills/upgrade-stripe/SKILL.md +185 -0
  123. package/.kiro/skills/stripe-best-practices/SKILL.md +42 -0
  124. package/.kiro/skills/stripe-best-practices/references/billing.md +36 -0
  125. package/.kiro/skills/stripe-best-practices/references/connect.md +48 -0
  126. package/.kiro/skills/stripe-best-practices/references/payments.md +79 -0
  127. package/.kiro/skills/stripe-best-practices/references/security.md +109 -0
  128. package/.kiro/skills/stripe-best-practices/references/treasury.md +16 -0
  129. package/.kiro/skills/stripe-projects/SKILL.md +139 -0
  130. package/.kiro/skills/upgrade-stripe/SKILL.md +185 -0
  131. package/.kode/skills/stripe-best-practices/SKILL.md +42 -0
  132. package/.kode/skills/stripe-best-practices/references/billing.md +36 -0
  133. package/.kode/skills/stripe-best-practices/references/connect.md +48 -0
  134. package/.kode/skills/stripe-best-practices/references/payments.md +79 -0
  135. package/.kode/skills/stripe-best-practices/references/security.md +109 -0
  136. package/.kode/skills/stripe-best-practices/references/treasury.md +16 -0
  137. package/.kode/skills/stripe-projects/SKILL.md +139 -0
  138. package/.kode/skills/upgrade-stripe/SKILL.md +185 -0
  139. package/.mcpjam/skills/stripe-best-practices/SKILL.md +42 -0
  140. package/.mcpjam/skills/stripe-best-practices/references/billing.md +36 -0
  141. package/.mcpjam/skills/stripe-best-practices/references/connect.md +48 -0
  142. package/.mcpjam/skills/stripe-best-practices/references/payments.md +79 -0
  143. package/.mcpjam/skills/stripe-best-practices/references/security.md +109 -0
  144. package/.mcpjam/skills/stripe-best-practices/references/treasury.md +16 -0
  145. package/.mcpjam/skills/stripe-projects/SKILL.md +139 -0
  146. package/.mcpjam/skills/upgrade-stripe/SKILL.md +185 -0
  147. package/.mux/skills/stripe-best-practices/SKILL.md +42 -0
  148. package/.mux/skills/stripe-best-practices/references/billing.md +36 -0
  149. package/.mux/skills/stripe-best-practices/references/connect.md +48 -0
  150. package/.mux/skills/stripe-best-practices/references/payments.md +79 -0
  151. package/.mux/skills/stripe-best-practices/references/security.md +109 -0
  152. package/.mux/skills/stripe-best-practices/references/treasury.md +16 -0
  153. package/.mux/skills/stripe-projects/SKILL.md +139 -0
  154. package/.mux/skills/upgrade-stripe/SKILL.md +185 -0
  155. package/.neovate/skills/stripe-best-practices/SKILL.md +42 -0
  156. package/.neovate/skills/stripe-best-practices/references/billing.md +36 -0
  157. package/.neovate/skills/stripe-best-practices/references/connect.md +48 -0
  158. package/.neovate/skills/stripe-best-practices/references/payments.md +79 -0
  159. package/.neovate/skills/stripe-best-practices/references/security.md +109 -0
  160. package/.neovate/skills/stripe-best-practices/references/treasury.md +16 -0
  161. package/.neovate/skills/stripe-projects/SKILL.md +139 -0
  162. package/.neovate/skills/upgrade-stripe/SKILL.md +185 -0
  163. package/.nixpacksignore +14 -0
  164. package/.openhands/skills/stripe-best-practices/SKILL.md +42 -0
  165. package/.openhands/skills/stripe-best-practices/references/billing.md +36 -0
  166. package/.openhands/skills/stripe-best-practices/references/connect.md +48 -0
  167. package/.openhands/skills/stripe-best-practices/references/payments.md +79 -0
  168. package/.openhands/skills/stripe-best-practices/references/security.md +109 -0
  169. package/.openhands/skills/stripe-best-practices/references/treasury.md +16 -0
  170. package/.openhands/skills/stripe-projects/SKILL.md +139 -0
  171. package/.openhands/skills/upgrade-stripe/SKILL.md +185 -0
  172. package/.pi/skills/stripe-best-practices/SKILL.md +42 -0
  173. package/.pi/skills/stripe-best-practices/references/billing.md +36 -0
  174. package/.pi/skills/stripe-best-practices/references/connect.md +48 -0
  175. package/.pi/skills/stripe-best-practices/references/payments.md +79 -0
  176. package/.pi/skills/stripe-best-practices/references/security.md +109 -0
  177. package/.pi/skills/stripe-best-practices/references/treasury.md +16 -0
  178. package/.pi/skills/stripe-projects/SKILL.md +139 -0
  179. package/.pi/skills/upgrade-stripe/SKILL.md +185 -0
  180. package/.pochi/skills/stripe-best-practices/SKILL.md +42 -0
  181. package/.pochi/skills/stripe-best-practices/references/billing.md +36 -0
  182. package/.pochi/skills/stripe-best-practices/references/connect.md +48 -0
  183. package/.pochi/skills/stripe-best-practices/references/payments.md +79 -0
  184. package/.pochi/skills/stripe-best-practices/references/security.md +109 -0
  185. package/.pochi/skills/stripe-best-practices/references/treasury.md +16 -0
  186. package/.pochi/skills/stripe-projects/SKILL.md +139 -0
  187. package/.pochi/skills/upgrade-stripe/SKILL.md +185 -0
  188. package/.qoder/skills/stripe-best-practices/SKILL.md +42 -0
  189. package/.qoder/skills/stripe-best-practices/references/billing.md +36 -0
  190. package/.qoder/skills/stripe-best-practices/references/connect.md +48 -0
  191. package/.qoder/skills/stripe-best-practices/references/payments.md +79 -0
  192. package/.qoder/skills/stripe-best-practices/references/security.md +109 -0
  193. package/.qoder/skills/stripe-best-practices/references/treasury.md +16 -0
  194. package/.qoder/skills/stripe-projects/SKILL.md +139 -0
  195. package/.qoder/skills/upgrade-stripe/SKILL.md +185 -0
  196. package/.qwen/skills/stripe-best-practices/SKILL.md +42 -0
  197. package/.qwen/skills/stripe-best-practices/references/billing.md +36 -0
  198. package/.qwen/skills/stripe-best-practices/references/connect.md +48 -0
  199. package/.qwen/skills/stripe-best-practices/references/payments.md +79 -0
  200. package/.qwen/skills/stripe-best-practices/references/security.md +109 -0
  201. package/.qwen/skills/stripe-best-practices/references/treasury.md +16 -0
  202. package/.qwen/skills/stripe-projects/SKILL.md +139 -0
  203. package/.qwen/skills/upgrade-stripe/SKILL.md +185 -0
  204. package/.roo/skills/stripe-best-practices/SKILL.md +42 -0
  205. package/.roo/skills/stripe-best-practices/references/billing.md +36 -0
  206. package/.roo/skills/stripe-best-practices/references/connect.md +48 -0
  207. package/.roo/skills/stripe-best-practices/references/payments.md +79 -0
  208. package/.roo/skills/stripe-best-practices/references/security.md +109 -0
  209. package/.roo/skills/stripe-best-practices/references/treasury.md +16 -0
  210. package/.roo/skills/stripe-projects/SKILL.md +139 -0
  211. package/.roo/skills/upgrade-stripe/SKILL.md +185 -0
  212. package/.trae/skills/stripe-best-practices/SKILL.md +42 -0
  213. package/.trae/skills/stripe-best-practices/references/billing.md +36 -0
  214. package/.trae/skills/stripe-best-practices/references/connect.md +48 -0
  215. package/.trae/skills/stripe-best-practices/references/payments.md +79 -0
  216. package/.trae/skills/stripe-best-practices/references/security.md +109 -0
  217. package/.trae/skills/stripe-best-practices/references/treasury.md +16 -0
  218. package/.trae/skills/stripe-projects/SKILL.md +139 -0
  219. package/.trae/skills/upgrade-stripe/SKILL.md +185 -0
  220. package/.vibe/skills/stripe-best-practices/SKILL.md +42 -0
  221. package/.vibe/skills/stripe-best-practices/references/billing.md +36 -0
  222. package/.vibe/skills/stripe-best-practices/references/connect.md +48 -0
  223. package/.vibe/skills/stripe-best-practices/references/payments.md +79 -0
  224. package/.vibe/skills/stripe-best-practices/references/security.md +109 -0
  225. package/.vibe/skills/stripe-best-practices/references/treasury.md +16 -0
  226. package/.vibe/skills/stripe-projects/SKILL.md +139 -0
  227. package/.vibe/skills/upgrade-stripe/SKILL.md +185 -0
  228. package/.windsurf/skills/stripe-best-practices/SKILL.md +42 -0
  229. package/.windsurf/skills/stripe-best-practices/references/billing.md +36 -0
  230. package/.windsurf/skills/stripe-best-practices/references/connect.md +48 -0
  231. package/.windsurf/skills/stripe-best-practices/references/payments.md +79 -0
  232. package/.windsurf/skills/stripe-best-practices/references/security.md +109 -0
  233. package/.windsurf/skills/stripe-best-practices/references/treasury.md +16 -0
  234. package/.windsurf/skills/stripe-projects/SKILL.md +139 -0
  235. package/.windsurf/skills/upgrade-stripe/SKILL.md +185 -0
  236. package/.zencoder/skills/stripe-best-practices/SKILL.md +42 -0
  237. package/.zencoder/skills/stripe-best-practices/references/billing.md +36 -0
  238. package/.zencoder/skills/stripe-best-practices/references/connect.md +48 -0
  239. package/.zencoder/skills/stripe-best-practices/references/payments.md +79 -0
  240. package/.zencoder/skills/stripe-best-practices/references/security.md +109 -0
  241. package/.zencoder/skills/stripe-best-practices/references/treasury.md +16 -0
  242. package/.zencoder/skills/stripe-projects/SKILL.md +139 -0
  243. package/.zencoder/skills/upgrade-stripe/SKILL.md +185 -0
  244. package/AUDIT.md +95 -0
  245. package/BLOCKERS.md +0 -0
  246. package/COOLIFY.md +51 -0
  247. package/MCP_SETUP.md +23 -0
  248. package/PRODUCTION_CHECKLIST.md +246 -0
  249. package/README.md +47 -0
  250. package/ROADMAP.md +91 -0
  251. package/docs/superpowers/plans/2026-05-11-availsync-frontend-sales-flow.md +2445 -0
  252. package/frontend/.env.example +2 -0
  253. package/frontend/app/admin/layout.tsx +13 -0
  254. package/frontend/app/admin/page.tsx +747 -0
  255. package/frontend/app/app/activity/page.tsx +257 -0
  256. package/frontend/app/app/agents/[agentId]/page.tsx +21 -0
  257. package/frontend/app/app/agents/page.tsx +1155 -0
  258. package/frontend/app/app/audit/page.tsx +225 -0
  259. package/frontend/app/app/availability/page.tsx +840 -0
  260. package/frontend/app/app/holds/page.tsx +262 -0
  261. package/frontend/app/app/layout.tsx +19 -0
  262. package/frontend/app/app/onboarding/page.tsx +10 -0
  263. package/frontend/app/app/onboarding/verify/page.tsx +309 -0
  264. package/frontend/app/app/page.tsx +508 -0
  265. package/frontend/app/app/settings/page.tsx +399 -0
  266. package/frontend/app/app/work/page.tsx +426 -0
  267. package/frontend/app/changelog/page.tsx +93 -0
  268. package/frontend/app/checkout/page.tsx +25 -0
  269. package/frontend/app/docs/api/page.tsx +157 -0
  270. package/frontend/app/docs/page.tsx +296 -0
  271. package/frontend/app/docs/pilot/page.tsx +127 -0
  272. package/frontend/app/docs/quickstart/page.tsx +318 -0
  273. package/frontend/app/docs/reliability/page.tsx +78 -0
  274. package/frontend/app/docs/sdk/node/page.tsx +166 -0
  275. package/frontend/app/globals.css +57 -0
  276. package/frontend/app/icon.png +0 -0
  277. package/frontend/app/layout.tsx +87 -0
  278. package/frontend/app/login/page.tsx +14 -0
  279. package/frontend/app/page.tsx +47 -0
  280. package/frontend/app/pricing/page.tsx +66 -0
  281. package/frontend/app/privacy/page.tsx +52 -0
  282. package/frontend/app/robots.ts +26 -0
  283. package/frontend/app/security/page.tsx +74 -0
  284. package/frontend/app/signup/page.tsx +14 -0
  285. package/frontend/app/sitemap.ts +14 -0
  286. package/frontend/app/terms/page.tsx +51 -0
  287. package/frontend/components/brand/AvailsyncLogo.tsx +56 -0
  288. package/frontend/components/checkout/CheckoutClient.tsx +100 -0
  289. package/frontend/components/dashboard/AgentForm.tsx +59 -0
  290. package/frontend/components/dashboard/AppShell.tsx +291 -0
  291. package/frontend/components/dashboard/AvailabilityChecker.tsx +117 -0
  292. package/frontend/components/dashboard/AvailabilityWindowForm.tsx +40 -0
  293. package/frontend/components/dashboard/HoldForm.tsx +133 -0
  294. package/frontend/components/dashboard/MetricCard.tsx +10 -0
  295. package/frontend/components/login/LoginForm.tsx +95 -0
  296. package/frontend/components/marketing/AgentCoordinationStory.tsx +1530 -0
  297. package/frontend/components/marketing/Faq.tsx +41 -0
  298. package/frontend/components/marketing/Hero.tsx +73 -0
  299. package/frontend/components/marketing/HowItWorks.tsx +28 -0
  300. package/frontend/components/marketing/ObserveModeTeaser.tsx +41 -0
  301. package/frontend/components/marketing/PricingTeaser.tsx +23 -0
  302. package/frontend/components/marketing/ProblemSolution.tsx +36 -0
  303. package/frontend/components/marketing/SiteFooter.tsx +59 -0
  304. package/frontend/components/marketing/SiteHeader.tsx +45 -0
  305. package/frontend/components/marketing/UseCases.tsx +27 -0
  306. package/frontend/components/onboarding/OnboardingClient.tsx +278 -0
  307. package/frontend/components/pricing/PricingCards.tsx +65 -0
  308. package/frontend/components/privacy/CookieConsent.tsx +230 -0
  309. package/frontend/components/privacy/CookieSettingsButton.tsx +15 -0
  310. package/frontend/components/seo/JsonLd.tsx +10 -0
  311. package/frontend/components/signup/SignupForm.tsx +55 -0
  312. package/frontend/components/ui/Badge.tsx +23 -0
  313. package/frontend/components/ui/Button.tsx +37 -0
  314. package/frontend/components/ui/Card.tsx +11 -0
  315. package/frontend/components/ui/ConfirmDialog.tsx +77 -0
  316. package/frontend/components/ui/EmptyState.tsx +24 -0
  317. package/frontend/components/ui/Input.tsx +14 -0
  318. package/frontend/components/ui/KeyDisplay.tsx +49 -0
  319. package/frontend/components/ui/Select.tsx +14 -0
  320. package/frontend/components/ui/Skeleton.tsx +24 -0
  321. package/frontend/components/ui/Tabs.tsx +19 -0
  322. package/frontend/components/ui/Textarea.tsx +14 -0
  323. package/frontend/components/ui/Toast.tsx +78 -0
  324. package/frontend/components/waitlist/WaitlistDialog.tsx +128 -0
  325. package/frontend/lib/api.ts +1282 -0
  326. package/frontend/lib/billing.ts +6 -0
  327. package/frontend/lib/cookieConsent.ts +113 -0
  328. package/frontend/lib/format.ts +16 -0
  329. package/frontend/lib/plans.ts +62 -0
  330. package/frontend/lib/schemas.ts +108 -0
  331. package/frontend/lib/seo.ts +376 -0
  332. package/frontend/lib/setupGuides.ts +630 -0
  333. package/frontend/lib/storage.ts +30 -0
  334. package/frontend/next-env.d.ts +6 -0
  335. package/frontend/next.config.mjs +13 -0
  336. package/frontend/package-lock.json +14409 -0
  337. package/frontend/package.json +41 -0
  338. package/frontend/playwright.config.ts +20 -0
  339. package/frontend/postcss.config.mjs +8 -0
  340. package/frontend/public/.gitkeep +0 -0
  341. package/frontend/public/brand/availsync-logo-board.png +0 -0
  342. package/frontend/public/brand/availsync-logo-dark.png +0 -0
  343. package/frontend/public/brand/availsync-mark-dark.png +0 -0
  344. package/frontend/public/brand/availsync-wordmark-dark.png +0 -0
  345. package/frontend/public/marketing/hero-agent-coordination.png +0 -0
  346. package/frontend/tailwind.config.ts +53 -0
  347. package/frontend/tests/smoke.spec.ts +89 -0
  348. package/frontend/tsconfig.json +23 -0
  349. package/jest.config.js +7 -0
  350. package/nixpacks.toml +11 -0
  351. package/package.json +53 -0
  352. package/packages/mcp/LICENSE +21 -0
  353. package/packages/mcp/README.md +60 -0
  354. package/packages/mcp/jest.config.cjs +8 -0
  355. package/packages/mcp/package.json +54 -0
  356. package/packages/mcp/src/helpers.ts +38 -0
  357. package/packages/mcp/src/index.test.ts +60 -0
  358. package/packages/mcp/src/index.ts +387 -0
  359. package/packages/mcp/tsconfig.json +20 -0
  360. package/packages/mcp/tsconfig.test.json +12 -0
  361. package/packages/node/LICENSE +21 -0
  362. package/packages/node/README.md +120 -0
  363. package/packages/node/jest.config.cjs +8 -0
  364. package/packages/node/package.json +46 -0
  365. package/packages/node/src/index.test.ts +360 -0
  366. package/packages/node/src/index.ts +402 -0
  367. package/packages/node/tsconfig.json +20 -0
  368. package/packages/node/tsconfig.test.json +12 -0
  369. package/plan.md +923 -0
  370. package/skills/stripe-best-practices/SKILL.md +42 -0
  371. package/skills/stripe-best-practices/references/billing.md +36 -0
  372. package/skills/stripe-best-practices/references/connect.md +48 -0
  373. package/skills/stripe-best-practices/references/payments.md +79 -0
  374. package/skills/stripe-best-practices/references/security.md +109 -0
  375. package/skills/stripe-best-practices/references/treasury.md +16 -0
  376. package/skills/stripe-projects/SKILL.md +139 -0
  377. package/skills/upgrade-stripe/SKILL.md +185 -0
  378. package/skills-lock.json +20 -0
  379. package/src/core/availability.ts +178 -0
  380. package/src/core/conflict.ts +209 -0
  381. package/src/core/work.ts +490 -0
  382. package/src/db/client.ts +17 -0
  383. package/src/db/migrations/001_init.sql +88 -0
  384. package/src/db/migrations/002_stripe.sql +2 -0
  385. package/src/db/migrations/003_workspace_auth.sql +19 -0
  386. package/src/db/migrations/004_agent_mcp_status.sql +2 -0
  387. package/src/db/migrations/005_hold_event_actor.sql +4 -0
  388. package/src/db/migrations/006_agent_activity.sql +35 -0
  389. package/src/db/migrations/007_work_coordination.sql +60 -0
  390. package/src/db/migrations/008_work_claim_leases.sql +20 -0
  391. package/src/db/migrations/009_billing_subscription_state.sql +23 -0
  392. package/src/db/migrations/010_agent_api_key_prefix.sql +10 -0
  393. package/src/db/migrations/011_org_verified_and_work_event_retention.sql +11 -0
  394. package/src/db/migrations/012_agent_enforcement_mode.sql +12 -0
  395. package/src/db/migrations/013_support_tickets.sql +21 -0
  396. package/src/db/migrations/014_paid_plan_waitlist.sql +23 -0
  397. package/src/db/migrations/015_agent_last_seen.sql +2 -0
  398. package/src/db/migrations.ts +164 -0
  399. package/src/db/run-migrations.ts +13 -0
  400. package/src/index.ts +183 -0
  401. package/src/lib/activity.ts +137 -0
  402. package/src/lib/apiKeys.ts +32 -0
  403. package/src/lib/appInfo.ts +26 -0
  404. package/src/lib/billingConfig.ts +3 -0
  405. package/src/lib/env.ts +75 -0
  406. package/src/lib/logger.ts +8 -0
  407. package/src/lib/plans.ts +204 -0
  408. package/src/mcp/server.js +5 -0
  409. package/src/mcp/server.ts +350 -0
  410. package/src/middleware/auth.ts +342 -0
  411. package/src/middleware/requestId.ts +16 -0
  412. package/src/routes/account.ts +168 -0
  413. package/src/routes/activity.ts +126 -0
  414. package/src/routes/admin.ts +514 -0
  415. package/src/routes/audit.ts +68 -0
  416. package/src/routes/auth.ts +203 -0
  417. package/src/routes/availability.ts +325 -0
  418. package/src/routes/billing.ts +406 -0
  419. package/src/routes/conflicts.ts +131 -0
  420. package/src/routes/holds.ts +437 -0
  421. package/src/routes/mcp.ts +57 -0
  422. package/src/routes/metrics.ts +39 -0
  423. package/src/routes/onboarding.ts +273 -0
  424. package/src/routes/orgs.ts +981 -0
  425. package/src/routes/preferences.ts +132 -0
  426. package/src/routes/session.ts +16 -0
  427. package/src/routes/support.ts +77 -0
  428. package/src/routes/value.ts +186 -0
  429. package/src/routes/waitlist.ts +63 -0
  430. package/src/routes/work.ts +1578 -0
  431. package/src/server.ts +36 -0
  432. package/src/types/index.ts +109 -0
  433. package/tests/integration/activity.route.test.ts +103 -0
  434. package/tests/integration/admin.route.test.ts +143 -0
  435. package/tests/integration/agent-keys.route.test.ts +237 -0
  436. package/tests/integration/availability.route.test.ts +125 -0
  437. package/tests/integration/billing.route.test.ts +393 -0
  438. package/tests/integration/conflicts.route.test.ts +131 -0
  439. package/tests/integration/flows.test.ts +154 -0
  440. package/tests/integration/helpers.ts +134 -0
  441. package/tests/integration/holds.route.test.ts +185 -0
  442. package/tests/integration/metrics.route.test.ts +100 -0
  443. package/tests/integration/onboarding.verify.route.test.ts +163 -0
  444. package/tests/integration/preferences.route.test.ts +53 -0
  445. package/tests/integration/session.route.test.ts +97 -0
  446. package/tests/integration/system.route.test.ts +92 -0
  447. package/tests/integration/value.route.test.ts +235 -0
  448. package/tests/integration/work.route.test.ts +745 -0
  449. package/tests/setup.ts +4 -0
  450. package/tests/smoke.sh +62 -0
  451. package/tests/unit/auth.test.ts +114 -0
  452. package/tests/unit/availability.test.ts +149 -0
  453. package/tests/unit/conflict.test.ts +118 -0
  454. package/tests/unit/env.test.ts +69 -0
  455. package/tests/unit/migrations.test.ts +135 -0
  456. package/tests/unit/request-id.test.ts +37 -0
  457. package/tmp-mobile-agents.png +0 -0
  458. package/tmp-next-mobile.err.log +10 -0
  459. package/tmp-next-mobile.log +5 -0
  460. package/tsconfig.json +16 -0
@@ -0,0 +1,262 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { loadSession, type StoredSession } from '@/lib/storage';
5
+ import { listAgents, listHolds, createHold, releaseHold } from '@/lib/api';
6
+ import { Badge } from '@/components/ui/Badge';
7
+ import { EmptyState } from '@/components/ui/EmptyState';
8
+ import { SkeletonRow } from '@/components/ui/Skeleton';
9
+ import { useToast } from '@/components/ui/Toast';
10
+ import { CalendarClock, Plus, X } from 'lucide-react';
11
+ import { toLocalInputValue } from '@/lib/format';
12
+ import type { Agent } from '@/lib/schemas';
13
+
14
+ type HoldItem = {
15
+ id: string;
16
+ agent_id: string;
17
+ start_at: string;
18
+ end_at: string;
19
+ reason?: string;
20
+ status: string;
21
+ created_at: string;
22
+ };
23
+
24
+ export default function HoldsPage() {
25
+ const { toast } = useToast();
26
+ const [session, setSession] = useState<StoredSession | null>(null);
27
+ const [agents, setAgents] = useState<Agent[]>([]);
28
+ const [selectedAgent, setSelectedAgent] = useState('');
29
+ const [holds, setHolds] = useState<HoldItem[]>([]);
30
+ const [loading, setLoading] = useState(true);
31
+ const [showForm, setShowForm] = useState(false);
32
+ const [form, setForm] = useState({
33
+ start_at: toLocalInputValue(new Date(Date.now() + 86400000)),
34
+ end_at: toLocalInputValue(new Date(Date.now() + 86400000 + 1800000)),
35
+ reason: '',
36
+ });
37
+ const [creating, setCreating] = useState(false);
38
+
39
+ const fetchHolds = () => {
40
+ const s = loadSession();
41
+ setSession(s);
42
+ if (s) {
43
+ Promise.all([
44
+ listAgents(s.orgId),
45
+ listHolds({ agent_id: selectedAgent || undefined }),
46
+ ])
47
+ .then(([agentRows, holdRows]) => {
48
+ setAgents(agentRows);
49
+ if (!selectedAgent && agentRows.length > 0) {
50
+ setSelectedAgent(agentRows[0].id);
51
+ }
52
+ setHolds(holdRows as unknown as HoldItem[]);
53
+ setLoading(false);
54
+ })
55
+ .catch(() => setLoading(false));
56
+ } else {
57
+ setLoading(false);
58
+ }
59
+ };
60
+
61
+ const fetchHoldsOnly = () => {
62
+ if (session) {
63
+ listHolds({ agent_id: selectedAgent || undefined })
64
+ .then((data) => {
65
+ setHolds(data as unknown as HoldItem[]);
66
+ setLoading(false);
67
+ })
68
+ .catch(() => setLoading(false));
69
+ }
70
+ };
71
+
72
+ useEffect(() => { fetchHolds(); }, []);
73
+ useEffect(() => {
74
+ if (session) fetchHoldsOnly();
75
+ // eslint-disable-next-line react-hooks/exhaustive-deps
76
+ }, [selectedAgent]);
77
+
78
+ const handleCreate = async () => {
79
+ if (!session || !selectedAgent) return;
80
+ setCreating(true);
81
+ try {
82
+ await createHold({
83
+ agent_id: selectedAgent,
84
+ start_at: new Date(form.start_at).toISOString(),
85
+ end_at: new Date(form.end_at).toISOString(),
86
+ reason: form.reason || undefined,
87
+ });
88
+ toast('Hold created', 'success');
89
+ setShowForm(false);
90
+ fetchHolds();
91
+ } catch (err) {
92
+ toast((err as Error).message, 'error');
93
+ } finally {
94
+ setCreating(false);
95
+ }
96
+ };
97
+
98
+ const handleRelease = async (holdId: string) => {
99
+ if (!session) return;
100
+ try {
101
+ await releaseHold(holdId);
102
+ toast('Hold released', 'success');
103
+ fetchHolds();
104
+ } catch (err) {
105
+ toast((err as Error).message, 'error');
106
+ }
107
+ };
108
+
109
+ const statusVariant: Record<string, 'success' | 'warning' | 'neutral'> = {
110
+ confirmed: 'success',
111
+ superseded: 'warning',
112
+ released: 'neutral',
113
+ };
114
+
115
+ return (
116
+ <div className="w-full max-w-none p-4 sm:p-6">
117
+ <div className="mb-6 flex flex-wrap items-center justify-between gap-3">
118
+ <h1 className="text-title font-semibold text-text-primary">Holds</h1>
119
+ <div className="flex flex-wrap items-center gap-2">
120
+ <select
121
+ value={selectedAgent}
122
+ onChange={(e) => setSelectedAgent(e.target.value)}
123
+ className="rounded bg-surface border border-border px-3 py-1.5 text-body text-text-primary outline-none focus:border-border-focus"
124
+ >
125
+ <option value="">All agents</option>
126
+ {agents.map((agent) => (
127
+ <option key={agent.id} value={agent.id}>{agent.name}</option>
128
+ ))}
129
+ </select>
130
+ <button
131
+ onClick={() => setShowForm(!showForm)}
132
+ className="flex items-center gap-1.5 rounded bg-accent text-white px-3 py-1.5 text-body font-medium hover:bg-accent-hover transition-colors active:scale-[0.98]"
133
+ >
134
+ <Plus className="h-3.5 w-3.5" />
135
+ New hold
136
+ </button>
137
+ </div>
138
+ </div>
139
+
140
+ {showForm && (
141
+ <div className="rounded border border-border bg-surface p-4 mb-6 space-y-3">
142
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
143
+ <div>
144
+ <label className="block text-label uppercase text-text-tertiary mb-1">Start</label>
145
+ <input
146
+ type="datetime-local"
147
+ value={form.start_at}
148
+ onChange={(e) => setForm({ ...form, start_at: e.target.value })}
149
+ className="w-full rounded bg-bg border border-border px-3 py-1.5 text-body text-text-primary outline-none focus:border-border-focus"
150
+ />
151
+ </div>
152
+ <div>
153
+ <label className="block text-label uppercase text-text-tertiary mb-1">End</label>
154
+ <input
155
+ type="datetime-local"
156
+ value={form.end_at}
157
+ onChange={(e) => setForm({ ...form, end_at: e.target.value })}
158
+ className="w-full rounded bg-bg border border-border px-3 py-1.5 text-body text-text-primary outline-none focus:border-border-focus"
159
+ />
160
+ </div>
161
+ </div>
162
+ <div>
163
+ <label className="block text-label uppercase text-text-tertiary mb-1">Agent</label>
164
+ <select
165
+ value={selectedAgent}
166
+ onChange={(e) => setSelectedAgent(e.target.value)}
167
+ className="w-full rounded bg-bg border border-border px-3 py-1.5 text-body text-text-primary outline-none focus:border-border-focus"
168
+ >
169
+ {agents.map((agent) => (
170
+ <option key={agent.id} value={agent.id}>{agent.name}</option>
171
+ ))}
172
+ </select>
173
+ </div>
174
+ <div>
175
+ <label className="block text-label uppercase text-text-tertiary mb-1">Reason</label>
176
+ <input
177
+ value={form.reason}
178
+ onChange={(e) => setForm({ ...form, reason: e.target.value })}
179
+ className="w-full rounded bg-bg border border-border px-3 py-1.5 text-body text-text-primary outline-none focus:border-border-focus"
180
+ placeholder="Optional"
181
+ />
182
+ </div>
183
+ <div className="flex gap-2">
184
+ <button
185
+ onClick={handleCreate}
186
+ disabled={creating}
187
+ className="rounded bg-accent text-white px-3 py-1.5 text-body font-medium hover:bg-accent-hover transition-colors disabled:opacity-40"
188
+ >
189
+ {creating ? 'Creating...' : 'Create hold'}
190
+ </button>
191
+ <button
192
+ onClick={() => setShowForm(false)}
193
+ className="rounded px-3 py-1.5 text-body text-text-secondary hover:bg-surface-raised transition-colors"
194
+ >
195
+ Cancel
196
+ </button>
197
+ </div>
198
+ </div>
199
+ )}
200
+
201
+ {loading ? (
202
+ <div className="overflow-hidden rounded border border-border">
203
+ {Array.from({ length: 3 }).map((_, i) => <SkeletonRow key={i} />)}
204
+ </div>
205
+ ) : holds.length === 0 ? (
206
+ <EmptyState
207
+ icon={CalendarClock}
208
+ title="No holds"
209
+ description="Holds are time windows reserved by your agents."
210
+ action={
211
+ <button
212
+ onClick={() => setShowForm(true)}
213
+ className="rounded bg-accent text-white px-3 py-1.5 text-body font-medium hover:bg-accent-hover transition-colors"
214
+ >
215
+ Create hold
216
+ </button>
217
+ }
218
+ />
219
+ ) : (
220
+ <div className="overflow-x-auto rounded border border-border dark-scroll">
221
+ <table className="w-full min-w-[820px]">
222
+ <thead>
223
+ <tr className="border-b border-border bg-surface text-label uppercase text-text-tertiary">
224
+ <th className="text-left px-4 py-2 font-medium">Start</th>
225
+ <th className="text-left px-4 py-2 font-medium">End</th>
226
+ <th className="text-left px-4 py-2 font-medium">Reason</th>
227
+ <th className="text-left px-4 py-2 font-medium">Status</th>
228
+ <th className="text-left px-4 py-2 font-medium"></th>
229
+ </tr>
230
+ </thead>
231
+ <tbody>
232
+ {holds.map((hold) => (
233
+ <tr key={hold.id} className="border-b border-border h-9 hover:bg-surface-raised transition-colors duration-150">
234
+ <td className="px-4 font-mono text-body text-text-secondary">
235
+ {new Date(hold.start_at).toLocaleString()}
236
+ </td>
237
+ <td className="px-4 font-mono text-body text-text-secondary">
238
+ {new Date(hold.end_at).toLocaleString()}
239
+ </td>
240
+ <td className="px-4 text-body text-text-secondary">{hold.reason || '-'}</td>
241
+ <td className="px-4">
242
+ <Badge variant={statusVariant[hold.status] || 'neutral'}>{hold.status}</Badge>
243
+ </td>
244
+ <td className="px-4">
245
+ {hold.status === 'confirmed' && (
246
+ <button
247
+ onClick={() => handleRelease(hold.id)}
248
+ className="text-body text-error hover:text-error/80 transition-colors"
249
+ >
250
+ Release
251
+ </button>
252
+ )}
253
+ </td>
254
+ </tr>
255
+ ))}
256
+ </tbody>
257
+ </table>
258
+ </div>
259
+ )}
260
+ </div>
261
+ );
262
+ }
@@ -0,0 +1,19 @@
1
+ import type { Metadata } from 'next';
2
+ import { AppShell } from '@/components/dashboard/AppShell';
3
+ import { ToastProvider } from '@/components/ui/Toast';
4
+
5
+ export const metadata: Metadata = {
6
+ title: 'Dashboard',
7
+ robots: {
8
+ index: false,
9
+ follow: false,
10
+ },
11
+ };
12
+
13
+ export default function DashboardLayout({ children }: { children: React.ReactNode }) {
14
+ return (
15
+ <ToastProvider>
16
+ <AppShell>{children}</AppShell>
17
+ </ToastProvider>
18
+ );
19
+ }
@@ -0,0 +1,10 @@
1
+ import { Suspense } from 'react';
2
+ import { OnboardingClient } from '@/components/onboarding/OnboardingClient';
3
+
4
+ export default function OnboardingPage() {
5
+ return (
6
+ <Suspense fallback={<main className="min-h-screen bg-bg" />}>
7
+ <OnboardingClient />
8
+ </Suspense>
9
+ );
10
+ }
@@ -0,0 +1,309 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { AlertTriangle, Check, Copy, FlaskConical, Loader2, Users } from 'lucide-react';
6
+ import { Button } from '@/components/ui/Button';
7
+ import { Badge } from '@/components/ui/Badge';
8
+ import { EmptyState } from '@/components/ui/EmptyState';
9
+ import { Skeleton } from '@/components/ui/Skeleton';
10
+ import { listAgents, restoreSession, verifyOnboarding, type OnboardingVerifyResult } from '@/lib/api';
11
+ import { saveSession } from '@/lib/storage';
12
+ import type { Agent } from '@/lib/schemas';
13
+
14
+ const progressRows = ['Sending claim from first agent', 'Sending claim from second agent', 'Resolving conflict'];
15
+
16
+ function isoMinute(value: string) {
17
+ return new Date(value).toISOString().slice(0, 19).replace('T', ' ');
18
+ }
19
+
20
+ function agentTypeLabel(value: string) {
21
+ return value.replaceAll('_', ' ');
22
+ }
23
+
24
+ export default function VerifyOnboardingPage() {
25
+ const router = useRouter();
26
+ const [loading, setLoading] = useState(true);
27
+ const [orgId, setOrgId] = useState('');
28
+ const [agents, setAgents] = useState<Agent[]>([]);
29
+ const [agentAId, setAgentAId] = useState('');
30
+ const [agentBId, setAgentBId] = useState('');
31
+ const [running, setRunning] = useState(false);
32
+ const [progress, setProgress] = useState(0);
33
+ const [result, setResult] = useState<OnboardingVerifyResult | null>(null);
34
+ const [error, setError] = useState('');
35
+
36
+ useEffect(() => {
37
+ restoreSession()
38
+ .then(async (session) => {
39
+ saveSession({ orgId: session.org.id, userId: session.user.id, email: session.user.email });
40
+ setOrgId(session.org.id);
41
+ const rows = await listAgents(session.org.id);
42
+ setAgents(rows);
43
+ setAgentAId(rows[0]?.id ?? '');
44
+ setAgentBId(rows[1]?.id ?? '');
45
+ })
46
+ .catch(() => router.push('/login'))
47
+ .finally(() => setLoading(false));
48
+ }, [router]);
49
+
50
+ const selectedA = agents.find((agent) => agent.id === agentAId);
51
+ const selectedB = agents.find((agent) => agent.id === agentBId);
52
+ const canRun = Boolean(orgId && selectedA && selectedB && selectedA.id !== selectedB.id);
53
+
54
+ const curlExample = useMemo(() => {
55
+ if (!result) return '';
56
+ const appUrl = typeof window === 'undefined' ? 'https://availsync.dev' : window.location.origin;
57
+ return `RESOURCE_KEY="${result.resource_key}"
58
+ START_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
59
+
60
+ curl -s -X POST "${appUrl}/v1/work/claim" \\
61
+ -H "Authorization: Bearer AGENT_A_API_KEY" \\
62
+ -H "Content-Type: application/json" \\
63
+ -d '{"agent_id":"${result.winner.agent_id}","resource_type":"project","resource_key":"'"$RESOURCE_KEY"'","start_at":"'"$START_AT"'","duration_minutes":5}'
64
+
65
+ curl -s -X POST "${appUrl}/v1/work/claim" \\
66
+ -H "Authorization: Bearer AGENT_B_API_KEY" \\
67
+ -H "Content-Type: application/json" \\
68
+ -d '{"agent_id":"${result.loser.agent_id}","resource_type":"project","resource_key":"'"$RESOURCE_KEY"'","start_at":"'"$START_AT"'","duration_minutes":5}'`;
69
+ }, [result]);
70
+
71
+ const runVerification = async () => {
72
+ if (!canRun) return;
73
+ setRunning(true);
74
+ setError('');
75
+ setResult(null);
76
+ setProgress(0);
77
+ const timers = [
78
+ window.setTimeout(() => setProgress(1), 120),
79
+ window.setTimeout(() => setProgress(2), 420),
80
+ window.setTimeout(() => setProgress(3), 720),
81
+ ];
82
+ try {
83
+ const response = await verifyOnboarding({ agent_a_id: agentAId, agent_b_id: agentBId });
84
+ setProgress(3);
85
+ setResult(response);
86
+ } catch (err) {
87
+ setError((err as Error).message);
88
+ } finally {
89
+ timers.forEach((timer) => window.clearTimeout(timer));
90
+ setRunning(false);
91
+ }
92
+ };
93
+
94
+ if (loading) {
95
+ return (
96
+ <div className="w-full max-w-none p-4 sm:p-6">
97
+ <Skeleton className="mb-4 h-8 w-64" />
98
+ <Skeleton className="mb-6 h-4 w-96" />
99
+ <Skeleton className="h-64 w-full" />
100
+ </div>
101
+ );
102
+ }
103
+
104
+ if (agents.length < 2) {
105
+ return (
106
+ <div className="w-full max-w-none p-4 sm:p-6">
107
+ <EmptyState
108
+ icon={Users}
109
+ title="Create one more agent to run verification"
110
+ description="The verification test needs two agents so Availsync can resolve a real conflict between them."
111
+ action={<Button href="/app/agents">Open agents</Button>}
112
+ />
113
+ </div>
114
+ );
115
+ }
116
+
117
+ return (
118
+ <div className="w-full max-w-none p-4 sm:p-6">
119
+ <div className="mb-8">
120
+ <p className="text-label uppercase text-text-tertiary">Onboarding</p>
121
+ <h1 className="mt-2 text-title font-semibold text-text-primary">Verify your integration</h1>
122
+ <p className="mt-2 max-w-2xl text-body leading-6 text-text-secondary">
123
+ We&apos;ll send two claim requests from your agents to a temporary protected repo-style
124
+ resource. Availsync will pick a winner and block the overlapping run. No real data is touched.
125
+ </p>
126
+ </div>
127
+
128
+ <div className="mb-6 rounded border border-border bg-surface p-5">
129
+ <div className="grid gap-3 md:grid-cols-[1fr_1fr_auto] md:items-end">
130
+ <div>
131
+ <label className="mb-1 block text-label uppercase text-text-tertiary">First agent</label>
132
+ <select
133
+ value={agentAId}
134
+ onChange={(event) => setAgentAId(event.target.value)}
135
+ className="w-full rounded border border-border bg-bg px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
136
+ >
137
+ {agents.map((agent) => (
138
+ <option key={agent.id} value={agent.id}>
139
+ {agent.name}
140
+ </option>
141
+ ))}
142
+ </select>
143
+ </div>
144
+ <div>
145
+ <label className="mb-1 block text-label uppercase text-text-tertiary">Second agent</label>
146
+ <select
147
+ value={agentBId}
148
+ onChange={(event) => setAgentBId(event.target.value)}
149
+ className="w-full rounded border border-border bg-bg px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
150
+ >
151
+ {agents.map((agent) => (
152
+ <option key={agent.id} value={agent.id}>
153
+ {agent.name}
154
+ </option>
155
+ ))}
156
+ </select>
157
+ </div>
158
+ <Button disabled={!canRun || running} onClick={runVerification}>
159
+ {running ? <Loader2 className="h-4 w-4 animate-spin" /> : <FlaskConical className="h-4 w-4" />}
160
+ {running ? 'Running...' : 'Run verification'}
161
+ </Button>
162
+ </div>
163
+ {selectedA?.id === selectedB?.id && (
164
+ <p className="mt-3 text-body text-error">Pick two different agents.</p>
165
+ )}
166
+ </div>
167
+
168
+ {(running || progress > 0) && !result && !error && (
169
+ <div className="mb-6 rounded border border-border bg-surface p-5">
170
+ <h2 className="mb-4 text-heading font-medium text-text-primary">Running verification</h2>
171
+ <div className="space-y-3">
172
+ {progressRows.map((row, index) => {
173
+ const done = progress > index;
174
+ return (
175
+ <div className="flex items-center justify-between rounded border border-border bg-bg px-3 py-2" key={row}>
176
+ <span className="text-body text-text-secondary">{row}</span>
177
+ <span className="flex items-center gap-2 font-mono text-[12px] text-text-tertiary">
178
+ {done ? <Check className="h-4 w-4 text-success" /> : <Loader2 className="h-4 w-4 animate-spin" />}
179
+ {new Date().toISOString().slice(11, 19)}
180
+ </span>
181
+ </div>
182
+ );
183
+ })}
184
+ </div>
185
+ </div>
186
+ )}
187
+
188
+ {error && (
189
+ <div className="mb-6 rounded border border-error/40 bg-error/10 p-4 text-body text-error">
190
+ {error}
191
+ <div className="mt-3">
192
+ <Button variant="secondary" onClick={runVerification}>Try again</Button>
193
+ </div>
194
+ </div>
195
+ )}
196
+
197
+ {result && (
198
+ <div className="space-y-6">
199
+ <div className="rounded border border-success/40 bg-success/10 p-5">
200
+ <h2 className="text-heading font-semibold text-success">
201
+ Availsync prevented a conflict in {result.duration_ms}ms
202
+ </h2>
203
+ <p className="mt-1 text-body text-text-secondary">
204
+ Workspace verified at {isoMinute(result.verified_at)}.
205
+ </p>
206
+ </div>
207
+
208
+ <div className="grid gap-4 md:grid-cols-2">
209
+ <ResultCard title="Winner" variant="success" item={result.winner} />
210
+ <ResultCard title="Blocked" variant="error" item={result.loser} />
211
+ </div>
212
+
213
+ <div className="rounded border border-border bg-surface p-5">
214
+ <div className="mb-2 flex items-center gap-2">
215
+ <Badge variant="accent">{result.rule_applied}</Badge>
216
+ <h3 className="text-heading font-medium text-text-primary">Rule applied</h3>
217
+ </div>
218
+ <p className="text-body leading-6 text-text-secondary">{result.rule_explanation}</p>
219
+ </div>
220
+
221
+ {result.tie_warning && (
222
+ <div className="rounded border border-warning/40 bg-warning/10 p-4 text-body leading-6 text-warning">
223
+ <div className="mb-2 flex items-center gap-2 font-medium">
224
+ <AlertTriangle className="h-4 w-4" />
225
+ Tie warning
226
+ </div>
227
+ {result.tie_warning}{' '}
228
+ <a className="underline" href="/app/agents">
229
+ Update agent priority
230
+ </a>
231
+ </div>
232
+ )}
233
+
234
+ <div className="rounded border border-border bg-surface p-5">
235
+ <h3 className="text-heading font-medium text-text-primary">What just happened?</h3>
236
+ <div className="mt-3 grid gap-2 text-body text-text-secondary">
237
+ <p>1. Availsync created a temporary protected resource only for this verification run.</p>
238
+ <p>2. One agent claimed the protected resource and the second agent was blocked by the same conflict rules your real agents use.</p>
239
+ <p>3. The temporary claim and resource were cleaned up, while the audit events remain.</p>
240
+ </div>
241
+ </div>
242
+
243
+ <div className="rounded border border-border bg-surface p-5">
244
+ <div className="mb-3 flex items-center justify-between gap-3">
245
+ <div>
246
+ <h3 className="text-heading font-medium text-text-primary">Want to do this from your own code?</h3>
247
+ <p className="mt-1 text-body text-text-secondary">
248
+ The dashboard demo runs the expected winner first, then proves the second agent is blocked by the
249
+ same conflict engine.
250
+ </p>
251
+ </div>
252
+ <button
253
+ className="rounded border border-border p-2 text-text-tertiary hover:text-text-secondary"
254
+ onClick={() => navigator.clipboard.writeText(curlExample)}
255
+ title="Copy curl example"
256
+ >
257
+ <Copy className="h-4 w-4" />
258
+ </button>
259
+ </div>
260
+ <pre className="max-h-72 overflow-auto rounded border border-border bg-bg p-4 text-[11px] font-mono leading-5 text-text-secondary whitespace-pre-wrap">
261
+ {curlExample}
262
+ </pre>
263
+ </div>
264
+
265
+ <Button href="/app">Continue to dashboard</Button>
266
+ </div>
267
+ )}
268
+ </div>
269
+ );
270
+ }
271
+
272
+ function ResultCard({
273
+ title,
274
+ variant,
275
+ item,
276
+ }: {
277
+ title: string;
278
+ variant: 'success' | 'error';
279
+ item: OnboardingVerifyResult['winner'] | OnboardingVerifyResult['loser'];
280
+ }) {
281
+ return (
282
+ <div className={`rounded border bg-surface p-5 ${variant === 'success' ? 'border-success/50' : 'border-error/50'}`}>
283
+ <div className="mb-3 flex items-center justify-between">
284
+ <h3 className="text-heading font-semibold text-text-primary">{item.agent_name}</h3>
285
+ <Badge variant={variant}>{title}</Badge>
286
+ </div>
287
+ <div className="space-y-2 text-body text-text-secondary">
288
+ <div className="flex justify-between gap-3">
289
+ <span>Type</span>
290
+ <Badge variant="neutral">{agentTypeLabel(item.agent_type)}</Badge>
291
+ </div>
292
+ <div className="flex justify-between gap-3">
293
+ <span>Priority</span>
294
+ <span className="font-mono">{item.priority}</span>
295
+ </div>
296
+ <div className="flex justify-between gap-3">
297
+ <span>{item.outcome === 'claimed' ? 'claim_id' : 'retry_after'}</span>
298
+ <span className="max-w-52 truncate font-mono text-[12px]">
299
+ {item.outcome === 'claimed' ? item.claim_id : item.retry_after ? isoMinute(item.retry_after) : '-'}
300
+ </span>
301
+ </div>
302
+ <div className="flex justify-between gap-3">
303
+ <span>Timestamp</span>
304
+ <span className="font-mono text-[12px]">{isoMinute(item.completed_at)}</span>
305
+ </div>
306
+ </div>
307
+ </div>
308
+ );
309
+ }