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,60 @@
1
+ CREATE TABLE IF NOT EXISTS work_resources (
2
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
4
+ resource_type TEXT NOT NULL,
5
+ resource_key TEXT NOT NULL,
6
+ label TEXT NOT NULL,
7
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
8
+ UNIQUE (org_id, resource_type, resource_key)
9
+ );
10
+
11
+ CREATE INDEX IF NOT EXISTS idx_work_resources_org
12
+ ON work_resources(org_id, created_at DESC);
13
+
14
+ CREATE TABLE IF NOT EXISTS work_claims (
15
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
16
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
17
+ agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
18
+ resource_id UUID NOT NULL REFERENCES work_resources(id) ON DELETE CASCADE,
19
+ start_at TIMESTAMPTZ NOT NULL,
20
+ end_at TIMESTAMPTZ NOT NULL,
21
+ status TEXT NOT NULL DEFAULT 'active'
22
+ CHECK (status IN ('active', 'superseded', 'released', 'expired', 'blocked')),
23
+ reason TEXT,
24
+ metadata JSONB,
25
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
26
+ CONSTRAINT chk_work_claim_order CHECK (end_at > start_at)
27
+ );
28
+
29
+ CREATE INDEX IF NOT EXISTS idx_work_claims_org_created
30
+ ON work_claims(org_id, created_at DESC);
31
+
32
+ CREATE INDEX IF NOT EXISTS idx_work_claims_resource_time
33
+ ON work_claims(resource_id, start_at, end_at)
34
+ WHERE status = 'active';
35
+
36
+ CREATE INDEX IF NOT EXISTS idx_work_claims_agent
37
+ ON work_claims(agent_id, created_at DESC);
38
+
39
+ CREATE TABLE IF NOT EXISTS work_claim_events (
40
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
41
+ claim_id UUID REFERENCES work_claims(id) ON DELETE CASCADE,
42
+ event_type TEXT NOT NULL
43
+ CHECK (event_type IN ('created', 'blocked', 'superseded', 'released', 'expired')),
44
+ agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
45
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
46
+ resource_id UUID REFERENCES work_resources(id) ON DELETE CASCADE,
47
+ conflicting_claim_id UUID REFERENCES work_claims(id) ON DELETE SET NULL,
48
+ rule_applied TEXT,
49
+ metadata JSONB,
50
+ actor_type TEXT,
51
+ actor_id UUID,
52
+ actor_label TEXT,
53
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
54
+ );
55
+
56
+ CREATE INDEX IF NOT EXISTS idx_work_claim_events_org
57
+ ON work_claim_events(org_id, created_at DESC);
58
+
59
+ CREATE INDEX IF NOT EXISTS idx_work_claim_events_resource
60
+ ON work_claim_events(resource_id, created_at DESC);
@@ -0,0 +1,20 @@
1
+ ALTER TABLE work_claims ADD COLUMN IF NOT EXISTS last_renewed_at TIMESTAMPTZ;
2
+ ALTER TABLE work_claims ADD COLUMN IF NOT EXISTS renewal_count INTEGER NOT NULL DEFAULT 0;
3
+ ALTER TABLE work_claims ADD COLUMN IF NOT EXISTS idempotency_key TEXT;
4
+ ALTER TABLE work_claims ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ;
5
+
6
+ UPDATE work_claims
7
+ SET last_renewed_at = COALESCE(last_renewed_at, created_at),
8
+ expires_at = COALESCE(expires_at, end_at);
9
+
10
+ ALTER TABLE work_claims ALTER COLUMN last_renewed_at SET NOT NULL;
11
+ ALTER TABLE work_claims ALTER COLUMN expires_at SET NOT NULL;
12
+
13
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_work_claims_idempotency_key
14
+ ON work_claims(org_id, agent_id, idempotency_key)
15
+ WHERE idempotency_key IS NOT NULL;
16
+
17
+ ALTER TABLE work_claim_events DROP CONSTRAINT IF EXISTS work_claim_events_event_type_check;
18
+ ALTER TABLE work_claim_events
19
+ ADD CONSTRAINT work_claim_events_event_type_check
20
+ CHECK (event_type IN ('created', 'blocked', 'superseded', 'released', 'expired', 'extended'));
@@ -0,0 +1,23 @@
1
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS subscription_status TEXT NOT NULL DEFAULT 'free';
2
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS current_period_start TIMESTAMPTZ;
3
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS current_period_end TIMESTAMPTZ;
4
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS cancel_at_period_end BOOLEAN NOT NULL DEFAULT false;
5
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS billing_email TEXT;
6
+
7
+ ALTER TABLE orgs DROP CONSTRAINT IF EXISTS orgs_subscription_status_check;
8
+ ALTER TABLE orgs ADD CONSTRAINT orgs_subscription_status_check
9
+ CHECK (subscription_status IN (
10
+ 'free',
11
+ 'incomplete',
12
+ 'incomplete_expired',
13
+ 'trialing',
14
+ 'active',
15
+ 'past_due',
16
+ 'canceled',
17
+ 'unpaid',
18
+ 'paused'
19
+ ));
20
+
21
+ UPDATE orgs
22
+ SET subscription_status = CASE WHEN plan = 'free' THEN 'free' ELSE 'active' END
23
+ WHERE subscription_status IS NULL OR subscription_status = 'free';
@@ -0,0 +1,10 @@
1
+ ALTER TABLE agents ADD COLUMN IF NOT EXISTS api_key_prefix TEXT;
2
+
3
+ UPDATE agents
4
+ SET api_key_prefix = substring(replace(id::text, '-', '') from 1 for 16)
5
+ WHERE api_key_prefix IS NULL;
6
+
7
+ ALTER TABLE agents ALTER COLUMN api_key_prefix SET NOT NULL;
8
+
9
+ DROP INDEX IF EXISTS idx_agents_api_key_prefix;
10
+ CREATE UNIQUE INDEX idx_agents_api_key_prefix ON agents(api_key_prefix);
@@ -0,0 +1,11 @@
1
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS verified_at TIMESTAMPTZ;
2
+
3
+ ALTER TABLE work_claim_events DROP CONSTRAINT IF EXISTS work_claim_events_claim_id_fkey;
4
+ ALTER TABLE work_claim_events
5
+ ADD CONSTRAINT work_claim_events_claim_id_fkey
6
+ FOREIGN KEY (claim_id) REFERENCES work_claims(id) ON DELETE SET NULL;
7
+
8
+ ALTER TABLE work_claim_events DROP CONSTRAINT IF EXISTS work_claim_events_resource_id_fkey;
9
+ ALTER TABLE work_claim_events
10
+ ADD CONSTRAINT work_claim_events_resource_id_fkey
11
+ FOREIGN KEY (resource_id) REFERENCES work_resources(id) ON DELETE SET NULL;
@@ -0,0 +1,12 @@
1
+ ALTER TABLE agents
2
+ ADD COLUMN IF NOT EXISTS enforcement_mode TEXT NOT NULL DEFAULT 'enforce';
3
+
4
+ ALTER TABLE agents DROP CONSTRAINT IF EXISTS agents_enforcement_mode_check;
5
+ ALTER TABLE agents
6
+ ADD CONSTRAINT agents_enforcement_mode_check
7
+ CHECK (enforcement_mode IN ('enforce', 'observe'));
8
+
9
+ ALTER TABLE work_claim_events DROP CONSTRAINT IF EXISTS work_claim_events_event_type_check;
10
+ ALTER TABLE work_claim_events
11
+ ADD CONSTRAINT work_claim_events_event_type_check
12
+ CHECK (event_type IN ('created', 'blocked', 'superseded', 'released', 'expired', 'extended', 'shadow_blocked', 'shadow_allowed'));
@@ -0,0 +1,21 @@
1
+ CREATE TABLE IF NOT EXISTS support_tickets (
2
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
4
+ user_id UUID REFERENCES workspace_users(id) ON DELETE SET NULL,
5
+ email TEXT NOT NULL,
6
+ subject TEXT NOT NULL,
7
+ category TEXT NOT NULL CHECK (category IN ('billing', 'setup', 'sdk', 'agent_error', 'bug', 'other')),
8
+ message TEXT NOT NULL,
9
+ status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'in_review', 'closed')),
10
+ priority TEXT NOT NULL DEFAULT 'normal' CHECK (priority IN ('normal', 'high')),
11
+ plan TEXT NOT NULL,
12
+ context JSONB NOT NULL DEFAULT '{}',
13
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
14
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
15
+ );
16
+
17
+ CREATE INDEX IF NOT EXISTS idx_support_tickets_status_created
18
+ ON support_tickets(status, created_at DESC);
19
+
20
+ CREATE INDEX IF NOT EXISTS idx_support_tickets_org_created
21
+ ON support_tickets(org_id, created_at DESC);
@@ -0,0 +1,23 @@
1
+ CREATE TABLE IF NOT EXISTS paid_plan_waitlist (
2
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3
+ org_id UUID REFERENCES orgs(id) ON DELETE SET NULL,
4
+ user_id UUID REFERENCES workspace_users(id) ON DELETE SET NULL,
5
+ email TEXT NOT NULL,
6
+ plan TEXT NOT NULL CHECK (plan IN ('individual', 'team')),
7
+ source TEXT NOT NULL DEFAULT 'pricing',
8
+ message TEXT NOT NULL DEFAULT '',
9
+ status TEXT NOT NULL DEFAULT 'new' CHECK (status IN ('new', 'contacted', 'closed')),
10
+ context JSONB NOT NULL DEFAULT '{}',
11
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
12
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
13
+ );
14
+
15
+ CREATE INDEX IF NOT EXISTS idx_paid_plan_waitlist_status_created
16
+ ON paid_plan_waitlist(status, created_at DESC);
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_paid_plan_waitlist_org_created
19
+ ON paid_plan_waitlist(org_id, created_at DESC);
20
+
21
+ CREATE INDEX IF NOT EXISTS idx_paid_plan_waitlist_email_created
22
+ ON paid_plan_waitlist(email, created_at DESC);
23
+
@@ -0,0 +1,2 @@
1
+ ALTER TABLE agents
2
+ ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMPTZ;
@@ -0,0 +1,164 @@
1
+ import crypto from 'crypto';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import type { Pool, PoolClient } from 'pg';
5
+ import pool from './client';
6
+ import { log } from '../lib/logger';
7
+
8
+ const MIGRATION_LOCK_KEY = 275019923;
9
+
10
+ export type MigrationSummary = {
11
+ total: number;
12
+ applied: number;
13
+ failed: number;
14
+ pending: number;
15
+ latest: {
16
+ filename: string;
17
+ status: string;
18
+ finished_at: Date | null;
19
+ error_message: string | null;
20
+ } | null;
21
+ };
22
+
23
+ export function findMigrationsDir() {
24
+ return [
25
+ path.resolve(__dirname, 'migrations'),
26
+ path.resolve(process.cwd(), 'src/db/migrations'),
27
+ ].find((candidate) => fs.existsSync(candidate));
28
+ }
29
+
30
+ function checksum(sql: string) {
31
+ return crypto.createHash('sha256').update(sql).digest('hex');
32
+ }
33
+
34
+ async function ensureMigrationTable(client: PoolClient) {
35
+ await client.query(`
36
+ CREATE TABLE IF NOT EXISTS schema_migrations (
37
+ filename TEXT PRIMARY KEY,
38
+ checksum TEXT NOT NULL,
39
+ started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
40
+ finished_at TIMESTAMPTZ,
41
+ status TEXT NOT NULL CHECK (status IN ('running', 'applied', 'failed')),
42
+ error_message TEXT
43
+ )
44
+ `);
45
+ }
46
+
47
+ export async function runMigrations(db: Pool = pool, migrationsDir = findMigrationsDir()) {
48
+ const resolvedMigrationsDir = migrationsDir;
49
+ if (!resolvedMigrationsDir) {
50
+ log.warn('No migrations directory found', {
51
+ paths: [
52
+ path.resolve(__dirname, 'migrations'),
53
+ path.resolve(process.cwd(), 'src/db/migrations'),
54
+ ],
55
+ });
56
+ return;
57
+ }
58
+
59
+ const files = fs.readdirSync(resolvedMigrationsDir).filter((file) => file.endsWith('.sql')).sort();
60
+ const client = await db.connect();
61
+ try {
62
+ await client.query('SELECT pg_advisory_lock($1)', [MIGRATION_LOCK_KEY]);
63
+ await ensureMigrationTable(client);
64
+
65
+ for (const file of files) {
66
+ const sql = fs.readFileSync(path.join(resolvedMigrationsDir, file), 'utf8');
67
+ const hash = checksum(sql);
68
+ const existing = await client.query(
69
+ `SELECT checksum, status
70
+ FROM schema_migrations
71
+ WHERE filename = $1`,
72
+ [file],
73
+ );
74
+
75
+ if (existing.rows[0]?.status === 'applied') {
76
+ if (existing.rows[0].checksum !== hash) {
77
+ throw new Error(`Migration checksum changed after apply: ${file}`);
78
+ }
79
+ continue;
80
+ }
81
+
82
+ await client.query(
83
+ `INSERT INTO schema_migrations (filename, checksum, started_at, status)
84
+ VALUES ($1, $2, NOW(), 'running')
85
+ ON CONFLICT (filename)
86
+ DO UPDATE SET checksum = EXCLUDED.checksum,
87
+ started_at = NOW(),
88
+ finished_at = NULL,
89
+ status = 'running',
90
+ error_message = NULL`,
91
+ [file, hash],
92
+ );
93
+
94
+ try {
95
+ await client.query('BEGIN');
96
+ await client.query(sql);
97
+ await client.query(
98
+ `UPDATE schema_migrations
99
+ SET status = 'applied',
100
+ finished_at = NOW(),
101
+ error_message = NULL
102
+ WHERE filename = $1`,
103
+ [file],
104
+ );
105
+ await client.query('COMMIT');
106
+ log.info('[migration] applied', { filename: file });
107
+ } catch (err) {
108
+ await client.query('ROLLBACK').catch(() => {});
109
+ const message = (err as Error).message;
110
+ await client.query(
111
+ `UPDATE schema_migrations
112
+ SET status = 'failed',
113
+ finished_at = NOW(),
114
+ error_message = $2
115
+ WHERE filename = $1`,
116
+ [file, message],
117
+ );
118
+ log.error('[migration] failed', { filename: file, error: message });
119
+ throw new Error(`Migration failed in ${file}: ${message}`);
120
+ }
121
+ }
122
+ } finally {
123
+ await client.query('SELECT pg_advisory_unlock($1)', [MIGRATION_LOCK_KEY]).catch(() => {});
124
+ client.release();
125
+ }
126
+ }
127
+
128
+ export async function migrationSummary(db: Pool = pool, migrationsDir = findMigrationsDir()): Promise<MigrationSummary> {
129
+ const resolvedMigrationsDir = migrationsDir;
130
+ const total = resolvedMigrationsDir
131
+ ? fs.readdirSync(resolvedMigrationsDir).filter((file) => file.endsWith('.sql')).length
132
+ : 0;
133
+
134
+ try {
135
+ const exists = await db.query("SELECT to_regclass('public.schema_migrations') AS table_name");
136
+ if (!exists.rows[0]?.table_name) {
137
+ return { total, applied: 0, failed: 0, pending: total, latest: null };
138
+ }
139
+
140
+ const result = await db.query(
141
+ `SELECT
142
+ COUNT(*) FILTER (WHERE status = 'applied')::int AS applied,
143
+ COUNT(*) FILTER (WHERE status = 'failed')::int AS failed
144
+ FROM schema_migrations`,
145
+ );
146
+ const latest = await db.query(
147
+ `SELECT filename, status, finished_at, error_message
148
+ FROM schema_migrations
149
+ ORDER BY COALESCE(finished_at, started_at) DESC
150
+ LIMIT 1`,
151
+ );
152
+ const applied = result.rows[0]?.applied ?? 0;
153
+ const failed = result.rows[0]?.failed ?? 0;
154
+ return {
155
+ total,
156
+ applied,
157
+ failed,
158
+ pending: Math.max(0, total - applied),
159
+ latest: latest.rows[0] ?? null,
160
+ };
161
+ } catch {
162
+ return { total, applied: 0, failed: 1, pending: total, latest: null };
163
+ }
164
+ }
@@ -0,0 +1,13 @@
1
+ import pool from './client';
2
+ import { runMigrations } from './migrations';
3
+ import { log } from '../lib/logger';
4
+
5
+ runMigrations()
6
+ .then(async () => {
7
+ await pool.end();
8
+ })
9
+ .catch(async (err) => {
10
+ log.error('[migration] runner failed', { message: (err as Error).message });
11
+ await pool.end().catch(() => {});
12
+ process.exit(1);
13
+ });
package/src/index.ts ADDED
@@ -0,0 +1,183 @@
1
+ import express from 'express';
2
+ import type { Request, Response, NextFunction } from 'express';
3
+ import helmet from 'helmet';
4
+ import cors from 'cors';
5
+ import rateLimit from 'express-rate-limit';
6
+ import { createHash } from 'crypto';
7
+ import pool from './db/client';
8
+ import { runMigrations, migrationSummary } from './db/migrations';
9
+ import { appVersion } from './lib/appInfo';
10
+ import { envReadiness } from './lib/env';
11
+ import { log } from './lib/logger';
12
+ import { requestIdFrom, requestIdMiddleware } from './middleware/requestId';
13
+ import availabilityRouter from './routes/availability';
14
+ import holdsRouter from './routes/holds';
15
+ import preferencesRouter from './routes/preferences';
16
+ import orgsRouter from './routes/orgs';
17
+ import billingRouter, { handleStripeWebhook } from './routes/billing';
18
+ import sessionRouter from './routes/session';
19
+ import auditRouter from './routes/audit';
20
+ import authRouter from './routes/auth';
21
+ import mcpRouter from './routes/mcp';
22
+ import conflictsRouter from './routes/conflicts';
23
+ import metricsRouter from './routes/metrics';
24
+ import valueRouter from './routes/value';
25
+ import activityRouter from './routes/activity';
26
+ import workRouter from './routes/work';
27
+ import adminRouter from './routes/admin';
28
+ import onboardingRouter from './routes/onboarding';
29
+ import supportRouter from './routes/support';
30
+ import accountRouter from './routes/account';
31
+ import waitlistRouter from './routes/waitlist';
32
+
33
+ const app = express();
34
+
35
+ export function validateEnv() {
36
+ const readiness = envReadiness();
37
+ if (!readiness.ok) {
38
+ if (process.env.NODE_ENV === 'production') {
39
+ log.error('Missing required environment variables', { missing: readiness.missing });
40
+ process.exit(1);
41
+ } else {
42
+ log.warn('Missing environment variables (non-fatal in dev)', { missing: readiness.missing });
43
+ }
44
+ }
45
+ }
46
+
47
+ // --- Security middleware ---
48
+
49
+ app.use(helmet({
50
+ contentSecurityPolicy: false,
51
+ }));
52
+
53
+ app.use(requestIdMiddleware);
54
+
55
+ app.use(cors({
56
+ origin: (process.env.CORS_ORIGINS || 'http://localhost:3001,http://127.0.0.1:3001')
57
+ .split(',')
58
+ .map((o) => o.trim())
59
+ .filter(Boolean),
60
+ credentials: true,
61
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
62
+ allowedHeaders: ['Content-Type', 'Authorization', 'Idempotency-Key'],
63
+ }));
64
+
65
+ // --- Rate limiting ---
66
+
67
+ const apiLimiter = rateLimit({
68
+ windowMs: 60 * 1000,
69
+ max: 100,
70
+ standardHeaders: true,
71
+ legacyHeaders: false,
72
+ keyGenerator: (req) => {
73
+ const authorization = req.header('Authorization');
74
+ if (!authorization) return 'anonymous';
75
+ return `auth:${createHash('sha256').update(authorization).digest('hex')}`;
76
+ },
77
+ message: { error: 'rate_limited', message: 'Too many requests, please try again later' },
78
+ validate: false,
79
+ });
80
+
81
+ const orgCreateLimiter = rateLimit({
82
+ windowMs: 60 * 1000,
83
+ max: 10,
84
+ standardHeaders: true,
85
+ legacyHeaders: false,
86
+ message: { error: 'rate_limited', message: 'Too many org creation attempts' },
87
+ validate: false,
88
+ });
89
+
90
+ // --- Stripe webhook (raw body, BEFORE json middleware) ---
91
+
92
+ app.post('/v1/billing/webhook', express.raw({ type: 'application/json' }), handleStripeWebhook);
93
+
94
+ // --- Body parsing ---
95
+
96
+ app.use(express.json());
97
+
98
+ // --- Health check ---
99
+
100
+ app.get('/health', async (_req, res) => {
101
+ const startedAt = Date.now();
102
+ try {
103
+ await pool.query('SELECT 1');
104
+ const migrations = await migrationSummary();
105
+ const status = migrations.failed > 0 || migrations.pending > 0 ? 'degraded' : 'ok';
106
+ res.status(status === 'ok' ? 200 : 503).json({
107
+ status,
108
+ db: 'connected',
109
+ migrations,
110
+ version: appVersion(),
111
+ uptime_seconds: Math.floor(process.uptime()),
112
+ latency_ms: Date.now() - startedAt,
113
+ timestamp: new Date().toISOString(),
114
+ });
115
+ } catch (err) {
116
+ res.status(503).json({
117
+ status: 'error',
118
+ db: 'disconnected',
119
+ error: 'database_unavailable',
120
+ request_id: requestIdFrom(res),
121
+ version: appVersion(),
122
+ uptime_seconds: Math.floor(process.uptime()),
123
+ timestamp: new Date().toISOString(),
124
+ });
125
+ }
126
+ });
127
+
128
+ // --- Rate limiters on route groups ---
129
+
130
+ app.post('/v1/orgs', orgCreateLimiter);
131
+ app.post('/v1/auth/signup', orgCreateLimiter);
132
+ app.use('/v1', apiLimiter);
133
+
134
+ // --- Routes ---
135
+
136
+ app.use('/v1', authRouter);
137
+ app.use('/v1', orgsRouter);
138
+ app.use('/v1', billingRouter);
139
+ app.use('/v1', sessionRouter);
140
+ app.use('/v1', availabilityRouter);
141
+ app.use('/v1', holdsRouter);
142
+ app.use('/v1', conflictsRouter);
143
+ app.use('/v1', preferencesRouter);
144
+ app.use('/v1', auditRouter);
145
+ app.use('/v1', mcpRouter);
146
+ app.use('/v1', metricsRouter);
147
+ app.use('/v1', valueRouter);
148
+ app.use('/v1', activityRouter);
149
+ app.use('/v1', workRouter);
150
+ app.use('/v1', onboardingRouter);
151
+ app.use('/v1', waitlistRouter);
152
+ app.use('/v1', supportRouter);
153
+ app.use('/v1', accountRouter);
154
+ app.use('/v1', adminRouter);
155
+
156
+ // --- Global error handler ---
157
+
158
+ app.use((err: Error, req: Request, res: Response, _next: NextFunction) => {
159
+ const requestId = requestIdFrom(res);
160
+ log.error(`${req.method} ${req.path}`, { request_id: requestId, message: err.message, stack: err.stack });
161
+ if (process.env.NODE_ENV === 'development') {
162
+ return res.status(500).json({ error: 'internal_error', message: err.message, stack: err.stack, request_id: requestId });
163
+ }
164
+ return res.status(500).json({ error: 'internal_error', message: 'An unexpected error occurred', request_id: requestId });
165
+ });
166
+
167
+ // --- Start server ---
168
+
169
+ const PORT = process.env.PORT || 3000;
170
+ if (require.main === module && process.env.NODE_ENV !== 'test') {
171
+ validateEnv();
172
+
173
+ runMigrations()
174
+ .then(() => {
175
+ app.listen(PORT, () => log.info(`Availsync running on :${PORT}`));
176
+ })
177
+ .catch((err) => {
178
+ log.error('Migration failed', { message: err.message });
179
+ process.exit(1);
180
+ });
181
+ }
182
+
183
+ export default app;