@pylonsync/create-pylon 0.19.0 → 0.20.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 (675) hide show
  1. package/bin/create-pylon.js +30 -7
  2. package/package.json +1 -1
  3. package/templates/agency/.env.example +7 -0
  4. package/templates/agency/README.md +28 -3
  5. package/templates/agency/app/(marketing)/contact-form.tsx +20 -18
  6. package/templates/agency/app/(marketing)/page.tsx +3 -3
  7. package/templates/agency/app/(marketing)/work/page.tsx +3 -3
  8. package/templates/agency/app/auth-form.tsx +4 -4
  9. package/templates/agency/app/dashboard/dashboard-client.tsx +110 -64
  10. package/templates/agency/app/dashboard/page.tsx +14 -16
  11. package/templates/agency/app/icon.svg +3 -0
  12. package/templates/agency/app/login/page.tsx +12 -14
  13. package/templates/agency/components/marketing.tsx +12 -5
  14. package/templates/agency/functions/seedStudioBackoffice.ts +42 -11
  15. package/templates/agency/lib/agency.ts +22 -0
  16. package/templates/agency/lib/demo.ts +118 -0
  17. package/templates/agency/lib/site.config.ts +19 -13
  18. package/templates/agency/public/favicon.ico +0 -0
  19. package/templates/agency/tests/agency.test.ts +57 -0
  20. package/templates/ai-chat/.env.example +13 -27
  21. package/templates/ai-chat/README.md +53 -60
  22. package/templates/ai-chat/app/(chat)/c/[id]/page.tsx +19 -0
  23. package/templates/ai-chat/app/(chat)/chat-client.tsx +657 -622
  24. package/templates/ai-chat/app/(chat)/layout.tsx +6 -43
  25. package/templates/ai-chat/app/(chat)/not-found.tsx +6 -10
  26. package/templates/ai-chat/app/(chat)/page.tsx +7 -11
  27. package/templates/ai-chat/app/auth-form.tsx +45 -38
  28. package/templates/ai-chat/app/error.tsx +9 -12
  29. package/templates/ai-chat/app/globals.css +464 -102
  30. package/templates/ai-chat/app/layout.tsx +11 -19
  31. package/templates/ai-chat/app/login/page.tsx +39 -24
  32. package/templates/ai-chat/app.ts +118 -43
  33. package/templates/ai-chat/components/chat/composer.tsx +193 -0
  34. package/templates/ai-chat/components/chat/empty-state.tsx +65 -0
  35. package/templates/ai-chat/components/chat/icons.tsx +198 -0
  36. package/templates/ai-chat/components/chat/markdown.tsx +164 -0
  37. package/templates/ai-chat/components/chat/message.tsx +183 -0
  38. package/templates/ai-chat/components/chat/setup.tsx +71 -0
  39. package/templates/ai-chat/components/chat/sidebar.tsx +288 -0
  40. package/templates/ai-chat/functions/_beginTurn.ts +145 -0
  41. package/templates/ai-chat/functions/_finishTurn.ts +55 -0
  42. package/templates/ai-chat/functions/assistantStatus.ts +13 -0
  43. package/templates/ai-chat/functions/claimGuestHistory.ts +39 -0
  44. package/templates/ai-chat/functions/createConversation.ts +28 -0
  45. package/templates/ai-chat/functions/deleteConversation.ts +15 -20
  46. package/templates/ai-chat/functions/prepareGuestClaim.ts +32 -0
  47. package/templates/ai-chat/functions/renameConversation.ts +19 -0
  48. package/templates/ai-chat/functions/respond.ts +88 -0
  49. package/templates/ai-chat/functions/seedExamples.ts +46 -0
  50. package/templates/ai-chat/functions/stopResponse.ts +30 -0
  51. package/templates/ai-chat/lib/brand-icon.ts +7 -0
  52. package/templates/ai-chat/lib/chat.ts +277 -0
  53. package/templates/ai-chat/lib/claim.ts +15 -0
  54. package/templates/ai-chat/lib/examples.ts +179 -0
  55. package/templates/ai-chat/lib/highlight.ts +107 -0
  56. package/templates/ai-chat/lib/markdown.ts +317 -0
  57. package/templates/ai-chat/lib/provider.ts +38 -0
  58. package/templates/ai-chat/lib/site.config.ts +55 -39
  59. package/templates/ai-chat/tests/chat.test.ts +174 -0
  60. package/templates/ai-chat/tests/components.test.tsx +57 -0
  61. package/templates/ai-chat/tests/markdown.test.ts +88 -0
  62. package/templates/ai-chat/tests/provider.test.ts +32 -0
  63. package/templates/backend/consumer/apps/api/app/layout.tsx +33 -0
  64. package/templates/backend/consumer/apps/api/app/page.tsx +54 -0
  65. package/templates/backend/consumer/apps/api/app.ts +166 -0
  66. package/templates/backend/consumer/apps/api/functions/addComment.ts +24 -0
  67. package/templates/backend/consumer/apps/api/functions/createPost.ts +19 -36
  68. package/templates/backend/consumer/apps/api/functions/deletePost.ts +15 -13
  69. package/templates/backend/consumer/apps/api/functions/myProfile.ts +5 -8
  70. package/templates/backend/consumer/apps/api/functions/seedDemo.ts +95 -0
  71. package/templates/backend/consumer/apps/api/functions/setFollow.ts +36 -0
  72. package/templates/backend/consumer/apps/api/functions/setLike.ts +36 -0
  73. package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +43 -37
  74. package/templates/backend/consumer/apps/api/lib/seed.ts +89 -0
  75. package/templates/backend/consumer/apps/api/lib/social.ts +50 -0
  76. package/templates/backend/consumer/apps/api/package.json +8 -2
  77. package/templates/backend/consumer/apps/api/public/images/posts/arch.jpg +0 -0
  78. package/templates/backend/consumer/apps/api/public/images/posts/coffee.jpg +0 -0
  79. package/templates/backend/consumer/apps/api/public/images/posts/dog.jpg +0 -0
  80. package/templates/backend/consumer/apps/api/public/images/posts/lisbon.jpg +0 -0
  81. package/templates/backend/consumer/apps/api/public/images/posts/ramen.jpg +0 -0
  82. package/templates/backend/consumer/apps/api/public/images/posts/run.jpg +0 -0
  83. package/templates/backend/consumer/apps/api/public/images/posts/sunset.jpg +0 -0
  84. package/templates/backend/consumer/apps/api/tsconfig.json +12 -2
  85. package/templates/chat/README.md +43 -21
  86. package/templates/chat/app/chat-client.tsx +495 -85
  87. package/templates/chat/app/error.tsx +1 -1
  88. package/templates/chat/app/globals.css +170 -54
  89. package/templates/chat/app/layout.tsx +12 -9
  90. package/templates/chat/app/not-found.tsx +4 -2
  91. package/templates/chat/app/page.tsx +9 -19
  92. package/templates/chat/app.ts +96 -17
  93. package/templates/chat/components/chat/avatar.tsx +45 -0
  94. package/templates/chat/components/chat/composer.tsx +110 -0
  95. package/templates/chat/components/chat/message-list.tsx +285 -0
  96. package/templates/chat/components/chat/sidebar.tsx +190 -0
  97. package/templates/chat/functions/joinChat.ts +83 -0
  98. package/templates/chat/functions/renameMember.ts +25 -0
  99. package/templates/chat/lib/chat.ts +188 -0
  100. package/templates/chat/lib/seed.ts +78 -0
  101. package/templates/chat/tests/chat.test.ts +159 -0
  102. package/templates/chat/tests/composer.test.tsx +54 -0
  103. package/templates/consumer/README.md +3 -3
  104. package/templates/consumer/app/globals.css +10 -3
  105. package/templates/consumer/app/icon.svg +1 -0
  106. package/templates/consumer/components/social/avatar.tsx +25 -11
  107. package/templates/consumer/functions/seedFeed.ts +3 -3
  108. package/templates/consumer/lib/seed.ts +43 -85
  109. package/templates/consumer/public/favicon.ico +0 -0
  110. package/templates/consumer/public/images/posts/arch.jpg +0 -0
  111. package/templates/consumer/public/images/posts/coffee.jpg +0 -0
  112. package/templates/consumer/public/images/posts/dog.jpg +0 -0
  113. package/templates/consumer/public/images/posts/lisbon.jpg +0 -0
  114. package/templates/consumer/public/images/posts/ramen.jpg +0 -0
  115. package/templates/consumer/public/images/posts/run.jpg +0 -0
  116. package/templates/consumer/public/images/posts/sunset.jpg +0 -0
  117. package/templates/consumer/tests/seed.test.ts +39 -0
  118. package/templates/creator/.env.example +7 -0
  119. package/templates/creator/README.md +24 -5
  120. package/templates/creator/app/(marketing)/newsletter-signup.tsx +33 -12
  121. package/templates/creator/app/auth-form.tsx +7 -7
  122. package/templates/creator/app/dashboard/dashboard-client.tsx +137 -100
  123. package/templates/creator/app/dashboard/page.tsx +18 -18
  124. package/templates/creator/app/icon.svg +2 -0
  125. package/templates/creator/app/login/page.tsx +14 -14
  126. package/templates/creator/app.ts +4 -4
  127. package/templates/creator/functions/seedDemo.ts +32 -0
  128. package/templates/creator/functions/subscribe.ts +4 -15
  129. package/templates/creator/lib/demo.ts +94 -0
  130. package/templates/creator/lib/site.config.ts +15 -10
  131. package/templates/creator/lib/stats.ts +35 -0
  132. package/templates/creator/public/favicon.ico +0 -0
  133. package/templates/creator/tests/counter.test.ts +92 -0
  134. package/templates/creator/tests/live-count.test.tsx +45 -0
  135. package/templates/crm/README.md +22 -1
  136. package/templates/crm/app/apple-icon.png +0 -0
  137. package/templates/crm/app/companies/companies-view.tsx +32 -14
  138. package/templates/crm/app/companies/page.tsx +2 -1
  139. package/templates/crm/app/contacts/contacts-view.tsx +34 -10
  140. package/templates/crm/app/contacts/page.tsx +2 -1
  141. package/templates/crm/app/deals/[id]/deal-view.tsx +137 -98
  142. package/templates/crm/app/deals/[id]/page.tsx +2 -1
  143. package/templates/crm/app/globals.css +29 -14
  144. package/templates/crm/app/icon.svg +1 -0
  145. package/templates/crm/app/layout.tsx +7 -5
  146. package/templates/crm/app/page.tsx +2 -1
  147. package/templates/crm/app/pipeline-view.tsx +5 -5
  148. package/templates/crm/app/workspace.tsx +29 -17
  149. package/templates/crm/app.ts +5 -5
  150. package/templates/crm/components/activity-timeline.tsx +14 -4
  151. package/templates/crm/components/app-shell.tsx +94 -0
  152. package/templates/crm/components/auth-form.tsx +35 -5
  153. package/templates/crm/components/avatar.tsx +5 -1
  154. package/templates/crm/components/board-skeleton.tsx +5 -5
  155. package/templates/crm/components/brand-mark.tsx +25 -0
  156. package/templates/crm/components/command-palette.tsx +17 -17
  157. package/templates/crm/components/data-table.tsx +97 -36
  158. package/templates/crm/components/deal-card.tsx +23 -27
  159. package/templates/crm/components/filter-tabs.tsx +101 -0
  160. package/templates/crm/components/metrics-bar.tsx +6 -6
  161. package/templates/crm/components/nav.tsx +15 -0
  162. package/templates/crm/components/page-header.tsx +43 -7
  163. package/templates/crm/components/pipeline-board.tsx +16 -10
  164. package/templates/crm/components/require-auth.tsx +24 -26
  165. package/templates/crm/components/sidebar.tsx +54 -49
  166. package/templates/crm/components/stage-badge.tsx +4 -2
  167. package/templates/crm/components/ui/button.tsx +5 -4
  168. package/templates/crm/components/ui/dialog.tsx +1 -1
  169. package/templates/crm/components/ui/input.tsx +1 -1
  170. package/templates/crm/components/ui/select.tsx +1 -1
  171. package/templates/crm/components/ui/textarea.tsx +1 -1
  172. package/templates/crm/functions/seedWorkspace.ts +32 -17
  173. package/templates/crm/lib/brand.ts +9 -0
  174. package/templates/crm/lib/demo-team.ts +51 -0
  175. package/templates/crm/lib/pipeline.ts +37 -0
  176. package/templates/crm/lib/search.ts +4 -3
  177. package/templates/crm/lib/seed.ts +53 -8
  178. package/templates/crm/public/favicon.ico +0 -0
  179. package/templates/crm/tests/components.test.tsx +5 -5
  180. package/templates/crm/tests/demo-team.test.ts +18 -0
  181. package/templates/crm/tests/pipeline.test.ts +59 -3
  182. package/templates/directory/.env.example +7 -0
  183. package/templates/directory/README.md +22 -3
  184. package/templates/directory/app/(marketing)/directory-browse.tsx +30 -12
  185. package/templates/directory/app/(marketing)/page.tsx +6 -4
  186. package/templates/directory/app/auth-form.tsx +4 -4
  187. package/templates/directory/app/dashboard/dashboard-client.tsx +177 -90
  188. package/templates/directory/app/dashboard/page.tsx +16 -14
  189. package/templates/directory/app/icon.svg +4 -0
  190. package/templates/directory/app/login/page.tsx +16 -15
  191. package/templates/directory/functions/seedDemo.ts +43 -0
  192. package/templates/directory/lib/demo.ts +145 -0
  193. package/templates/directory/lib/directory.ts +27 -0
  194. package/templates/directory/lib/site.config.ts +21 -16
  195. package/templates/directory/public/favicon.ico +0 -0
  196. package/templates/directory/tests/directory.test.ts +46 -0
  197. package/templates/expo/consumer/apps/expo/App.tsx +360 -288
  198. package/templates/expo/consumer/apps/expo/app.json +9 -1
  199. package/templates/expo/consumer/apps/expo/package.json +2 -1
  200. package/templates/helpdesk/README.md +21 -0
  201. package/templates/helpdesk/app/apple-icon.png +0 -0
  202. package/templates/helpdesk/app/customers/customers-view.tsx +23 -7
  203. package/templates/helpdesk/app/customers/page.tsx +2 -1
  204. package/templates/helpdesk/app/globals.css +29 -14
  205. package/templates/helpdesk/app/icon.svg +1 -0
  206. package/templates/helpdesk/app/inbox-view.tsx +66 -47
  207. package/templates/helpdesk/app/layout.tsx +7 -5
  208. package/templates/helpdesk/app/page.tsx +4 -7
  209. package/templates/helpdesk/app/tickets/[id]/page.tsx +2 -1
  210. package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +134 -97
  211. package/templates/helpdesk/app/workspace.tsx +33 -22
  212. package/templates/helpdesk/app.ts +2 -2
  213. package/templates/helpdesk/components/app-shell.tsx +94 -0
  214. package/templates/helpdesk/components/auth-form.tsx +35 -5
  215. package/templates/helpdesk/components/avatar.tsx +5 -1
  216. package/templates/helpdesk/components/brand-mark.tsx +28 -0
  217. package/templates/helpdesk/components/command-palette.tsx +17 -17
  218. package/templates/helpdesk/components/data-table.tsx +97 -36
  219. package/templates/helpdesk/components/filter-tabs.tsx +101 -0
  220. package/templates/helpdesk/components/nav.tsx +14 -0
  221. package/templates/helpdesk/components/page-header.tsx +43 -7
  222. package/templates/helpdesk/components/priority-badge.tsx +49 -6
  223. package/templates/helpdesk/components/require-auth.tsx +24 -26
  224. package/templates/helpdesk/components/sidebar.tsx +54 -48
  225. package/templates/helpdesk/components/sla-indicator.tsx +5 -8
  226. package/templates/helpdesk/components/thread.tsx +7 -7
  227. package/templates/helpdesk/components/ticket-list.tsx +79 -19
  228. package/templates/helpdesk/components/ui/button.tsx +5 -4
  229. package/templates/helpdesk/components/ui/dialog.tsx +1 -1
  230. package/templates/helpdesk/components/ui/input.tsx +1 -1
  231. package/templates/helpdesk/components/ui/select.tsx +1 -1
  232. package/templates/helpdesk/components/ui/textarea.tsx +1 -1
  233. package/templates/helpdesk/functions/seedWorkspace.ts +25 -15
  234. package/templates/helpdesk/lib/brand.ts +9 -0
  235. package/templates/helpdesk/lib/demo-team.ts +51 -0
  236. package/templates/helpdesk/lib/search.ts +4 -3
  237. package/templates/helpdesk/lib/seed.ts +43 -2
  238. package/templates/helpdesk/lib/tickets.ts +68 -20
  239. package/templates/helpdesk/public/favicon.ico +0 -0
  240. package/templates/helpdesk/tests/components.test.tsx +10 -0
  241. package/templates/helpdesk/tests/demo-team.test.ts +18 -0
  242. package/templates/helpdesk/tests/tickets.test.ts +70 -0
  243. package/templates/imessage/.env.example +49 -0
  244. package/templates/imessage/AGENTS.md +153 -0
  245. package/templates/imessage/CLAUDE.md +7 -0
  246. package/templates/imessage/README.md +187 -0
  247. package/templates/imessage/app/console.tsx +382 -0
  248. package/templates/imessage/app/contacts/page.tsx +13 -0
  249. package/templates/imessage/app/error.tsx +16 -0
  250. package/templates/imessage/app/gate.tsx +31 -0
  251. package/templates/imessage/app/globals.css +183 -0
  252. package/templates/imessage/app/layout.tsx +17 -0
  253. package/templates/imessage/app/login/login-form.tsx +115 -0
  254. package/templates/imessage/app/login/page.tsx +30 -0
  255. package/templates/imessage/app/not-found.tsx +13 -0
  256. package/templates/imessage/app/page.tsx +14 -0
  257. package/templates/imessage/app/robots.ts +6 -0
  258. package/templates/imessage/app/setup/page.tsx +13 -0
  259. package/templates/imessage/app/sign-out.tsx +20 -0
  260. package/templates/imessage/app.ts +299 -0
  261. package/templates/imessage/bunfig +4 -0
  262. package/templates/imessage/components/avatar.tsx +46 -0
  263. package/templates/imessage/components/contact-panel.tsx +263 -0
  264. package/templates/imessage/components/contacts-view.tsx +172 -0
  265. package/templates/imessage/components/conversation-list.tsx +129 -0
  266. package/templates/imessage/components/nav-rail.tsx +67 -0
  267. package/templates/imessage/components/setup-view.tsx +408 -0
  268. package/templates/imessage/components/thread.tsx +229 -0
  269. package/templates/imessage/components/ui/badge.tsx +37 -0
  270. package/templates/imessage/components/ui/button.tsx +56 -0
  271. package/templates/imessage/components/ui/card.tsx +90 -0
  272. package/templates/imessage/components/ui/dialog.tsx +110 -0
  273. package/templates/imessage/components/ui/input.tsx +30 -0
  274. package/templates/imessage/components/ui/label.tsx +21 -0
  275. package/templates/imessage/components/ui/select.tsx +39 -0
  276. package/templates/imessage/components/ui/table.tsx +124 -0
  277. package/templates/imessage/components/ui/textarea.tsx +24 -0
  278. package/templates/imessage/components.json +20 -0
  279. package/templates/imessage/functions/answerNow.ts +34 -0
  280. package/templates/imessage/functions/assistant.ts +96 -0
  281. package/templates/imessage/functions/beginTurn.ts +101 -0
  282. package/templates/imessage/functions/cancelReminder.ts +13 -0
  283. package/templates/imessage/functions/claimOutbound.ts +44 -0
  284. package/templates/imessage/functions/clearDemo.ts +18 -0
  285. package/templates/imessage/functions/deleteNote.ts +11 -0
  286. package/templates/imessage/functions/deliverOutbound.ts +55 -0
  287. package/templates/imessage/functions/finishTurn.ts +84 -0
  288. package/templates/imessage/functions/fireReminder.ts +42 -0
  289. package/templates/imessage/functions/ingestInbound.ts +22 -0
  290. package/templates/imessage/functions/ownerAccount.ts +13 -0
  291. package/templates/imessage/functions/processTurn.ts +74 -0
  292. package/templates/imessage/functions/relayApply.ts +102 -0
  293. package/templates/imessage/functions/relaySync.ts +63 -0
  294. package/templates/imessage/functions/releaseOutbound.ts +18 -0
  295. package/templates/imessage/functions/resolveTurn.ts +25 -0
  296. package/templates/imessage/functions/saveContact.ts +27 -0
  297. package/templates/imessage/functions/seedDemo.ts +70 -0
  298. package/templates/imessage/functions/sendOwnerMessage.ts +28 -0
  299. package/templates/imessage/functions/sendblueWebhook.ts +49 -0
  300. package/templates/imessage/functions/setContactStatus.ts +17 -0
  301. package/templates/imessage/functions/settleOutbound.ts +35 -0
  302. package/templates/imessage/functions/setupStatus.ts +33 -0
  303. package/templates/imessage/functions/sweep.ts +58 -0
  304. package/templates/imessage/functions/toolCancelReminder.ts +17 -0
  305. package/templates/imessage/functions/toolForgetNote.ts +15 -0
  306. package/templates/imessage/functions/toolListNotes.ts +16 -0
  307. package/templates/imessage/functions/toolListReminders.ts +17 -0
  308. package/templates/imessage/functions/toolSaveNote.ts +22 -0
  309. package/templates/imessage/functions/toolSetReminder.ts +55 -0
  310. package/templates/imessage/functions/updateSettings.ts +48 -0
  311. package/templates/imessage/gitignore +11 -0
  312. package/templates/imessage/lib/agent-call.ts +251 -0
  313. package/templates/imessage/lib/assistant.ts +74 -0
  314. package/templates/imessage/lib/chunk.ts +135 -0
  315. package/templates/imessage/lib/demo.ts +68 -0
  316. package/templates/imessage/lib/format.ts +170 -0
  317. package/templates/imessage/lib/gate.ts +104 -0
  318. package/templates/imessage/lib/handles.ts +67 -0
  319. package/templates/imessage/lib/provider.ts +38 -0
  320. package/templates/imessage/lib/relay-protocol.ts +95 -0
  321. package/templates/imessage/lib/reminders.ts +41 -0
  322. package/templates/imessage/lib/secrets.ts +59 -0
  323. package/templates/imessage/lib/sendblue.ts +173 -0
  324. package/templates/imessage/lib/store.ts +407 -0
  325. package/templates/imessage/lib/transport.ts +135 -0
  326. package/templates/imessage/lib/utils.ts +10 -0
  327. package/templates/imessage/package.json +40 -0
  328. package/templates/imessage/relay/.env.example +14 -0
  329. package/templates/imessage/relay/applescript.ts +70 -0
  330. package/templates/imessage/relay/chatdb.ts +165 -0
  331. package/templates/imessage/relay/client.ts +41 -0
  332. package/templates/imessage/relay/main.ts +137 -0
  333. package/templates/imessage/relay/state.ts +35 -0
  334. package/templates/imessage/tests/agent-call.test.ts +112 -0
  335. package/templates/imessage/tests/applescript.test.ts +88 -0
  336. package/templates/imessage/tests/chatdb.test.ts +139 -0
  337. package/templates/imessage/tests/chunk.test.ts +61 -0
  338. package/templates/imessage/tests/fake-ctx.ts +88 -0
  339. package/templates/imessage/tests/ingest.test.ts +141 -0
  340. package/templates/imessage/tests/lib.test.ts +99 -0
  341. package/templates/imessage/tests/review-fixes.test.ts +138 -0
  342. package/templates/imessage/tests/secrets.test.ts +36 -0
  343. package/templates/imessage/tests/sendblue.test.ts +143 -0
  344. package/templates/imessage/tests/setup.ts +5 -0
  345. package/templates/imessage/tsconfig.json +18 -0
  346. package/templates/inventory/README.md +21 -0
  347. package/templates/inventory/app/apple-icon.png +0 -0
  348. package/templates/inventory/app/globals.css +29 -14
  349. package/templates/inventory/app/icon.svg +1 -0
  350. package/templates/inventory/app/layout.tsx +7 -5
  351. package/templates/inventory/app/movements/movements-view.tsx +32 -10
  352. package/templates/inventory/app/movements/page.tsx +2 -1
  353. package/templates/inventory/app/page.tsx +4 -6
  354. package/templates/inventory/app/products/[id]/page.tsx +2 -1
  355. package/templates/inventory/app/products/[id]/product-view.tsx +55 -25
  356. package/templates/inventory/app/products-view.tsx +74 -33
  357. package/templates/inventory/app/workspace.tsx +34 -16
  358. package/templates/inventory/app.ts +10 -2
  359. package/templates/inventory/components/app-shell.tsx +94 -0
  360. package/templates/inventory/components/auth-form.tsx +35 -5
  361. package/templates/inventory/components/avatar.tsx +5 -1
  362. package/templates/inventory/components/brand-mark.tsx +25 -0
  363. package/templates/inventory/components/category-tile.tsx +49 -0
  364. package/templates/inventory/components/command-palette.tsx +17 -17
  365. package/templates/inventory/components/data-table.tsx +97 -36
  366. package/templates/inventory/components/filter-tabs.tsx +101 -0
  367. package/templates/inventory/components/nav.tsx +14 -0
  368. package/templates/inventory/components/page-header.tsx +43 -7
  369. package/templates/inventory/components/require-auth.tsx +24 -26
  370. package/templates/inventory/components/sidebar.tsx +54 -48
  371. package/templates/inventory/components/stock-level.tsx +30 -14
  372. package/templates/inventory/components/summary-bar.tsx +8 -15
  373. package/templates/inventory/components/ui/button.tsx +5 -4
  374. package/templates/inventory/components/ui/dialog.tsx +1 -1
  375. package/templates/inventory/components/ui/input.tsx +1 -1
  376. package/templates/inventory/components/ui/select.tsx +1 -1
  377. package/templates/inventory/components/ui/textarea.tsx +1 -1
  378. package/templates/inventory/functions/seedWorkspace.ts +18 -8
  379. package/templates/inventory/lib/brand.ts +9 -0
  380. package/templates/inventory/lib/demo-team.ts +51 -0
  381. package/templates/inventory/lib/search.ts +4 -3
  382. package/templates/inventory/lib/seed.ts +29 -2
  383. package/templates/inventory/lib/stock.ts +32 -0
  384. package/templates/inventory/public/favicon.ico +0 -0
  385. package/templates/inventory/tests/demo-team.test.ts +18 -0
  386. package/templates/inventory/tests/stock.test.ts +29 -0
  387. package/templates/invoices/README.md +16 -0
  388. package/templates/invoices/app/apple-icon.png +0 -0
  389. package/templates/invoices/app/clients/clients-view.tsx +18 -6
  390. package/templates/invoices/app/clients/page.tsx +2 -1
  391. package/templates/invoices/app/globals.css +29 -14
  392. package/templates/invoices/app/icon.svg +1 -0
  393. package/templates/invoices/app/invoices/[id]/invoice-view.tsx +72 -48
  394. package/templates/invoices/app/invoices/[id]/page.tsx +2 -1
  395. package/templates/invoices/app/invoices-view.tsx +81 -32
  396. package/templates/invoices/app/layout.tsx +7 -5
  397. package/templates/invoices/app/page.tsx +4 -6
  398. package/templates/invoices/app/workspace.tsx +26 -16
  399. package/templates/invoices/app.ts +2 -2
  400. package/templates/invoices/components/app-shell.tsx +94 -0
  401. package/templates/invoices/components/auth-form.tsx +35 -5
  402. package/templates/invoices/components/avatar.tsx +5 -1
  403. package/templates/invoices/components/brand-mark.tsx +26 -0
  404. package/templates/invoices/components/command-palette.tsx +17 -17
  405. package/templates/invoices/components/data-table.tsx +97 -36
  406. package/templates/invoices/components/filter-tabs.tsx +101 -0
  407. package/templates/invoices/components/line-items.tsx +19 -14
  408. package/templates/invoices/components/nav.tsx +14 -0
  409. package/templates/invoices/components/page-header.tsx +43 -7
  410. package/templates/invoices/components/require-auth.tsx +24 -26
  411. package/templates/invoices/components/sidebar.tsx +54 -48
  412. package/templates/invoices/components/status-badge.tsx +7 -7
  413. package/templates/invoices/components/summary-bar.tsx +5 -5
  414. package/templates/invoices/components/ui/button.tsx +5 -4
  415. package/templates/invoices/components/ui/dialog.tsx +1 -1
  416. package/templates/invoices/components/ui/input.tsx +1 -1
  417. package/templates/invoices/components/ui/select.tsx +1 -1
  418. package/templates/invoices/components/ui/textarea.tsx +1 -1
  419. package/templates/invoices/functions/recordPayment.ts +3 -0
  420. package/templates/invoices/lib/billing.ts +40 -0
  421. package/templates/invoices/lib/brand.ts +9 -0
  422. package/templates/invoices/lib/search.ts +4 -3
  423. package/templates/invoices/public/favicon.ico +0 -0
  424. package/templates/invoices/tests/billing.test.ts +27 -0
  425. package/templates/ios/consumer/apps/ios/Package.swift +4 -3
  426. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ActivityView.swift +107 -0
  427. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AppModel.swift +171 -0
  428. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AuthView.swift +182 -0
  429. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/CommentsView.swift +136 -0
  430. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ComposeView.swift +152 -0
  431. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ExploreView.swift +117 -0
  432. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +175 -159
  433. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/MainTabView.swift +54 -0
  434. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Media.swift +271 -0
  435. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +101 -4
  436. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/PostCard.swift +245 -0
  437. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileEditorView.swift +208 -0
  438. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileView.swift +183 -0
  439. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +25 -31
  440. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/SocialStore.swift +285 -0
  441. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/StoryViewer.swift +155 -0
  442. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Theme.swift +82 -0
  443. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +4 -47
  444. package/templates/ios/consumer/apps/ios/project.yml +13 -9
  445. package/templates/local-service/.env.example +7 -0
  446. package/templates/local-service/README.md +26 -2
  447. package/templates/local-service/app/(marketing)/booking-widget.tsx +48 -9
  448. package/templates/local-service/app/(marketing)/page.tsx +66 -42
  449. package/templates/local-service/app/auth-form.tsx +8 -8
  450. package/templates/local-service/app/dashboard/dashboard-client.tsx +119 -92
  451. package/templates/local-service/app/dashboard/page.tsx +14 -14
  452. package/templates/local-service/app/icon.svg +2 -0
  453. package/templates/local-service/app/login/page.tsx +15 -15
  454. package/templates/local-service/functions/seedDemo.ts +49 -0
  455. package/templates/local-service/lib/demo.ts +135 -0
  456. package/templates/local-service/lib/hours.ts +44 -0
  457. package/templates/local-service/lib/site.config.ts +8 -3
  458. package/templates/local-service/lib/slots.ts +3 -0
  459. package/templates/local-service/public/favicon.ico +0 -0
  460. package/templates/local-service/tests/booking.test.ts +104 -0
  461. package/templates/marketplace/README.md +23 -15
  462. package/templates/marketplace/app/globals.css +172 -114
  463. package/templates/marketplace/app/layout.tsx +75 -30
  464. package/templates/marketplace/app/listing/[id]/page.tsx +210 -123
  465. package/templates/marketplace/app/me/page.tsx +4 -4
  466. package/templates/marketplace/app/page.tsx +221 -298
  467. package/templates/marketplace/app/sell/page.tsx +8 -11
  468. package/templates/marketplace/app.ts +32 -7
  469. package/templates/marketplace/client/ActivityFeed.tsx +311 -0
  470. package/templates/marketplace/client/AuthNav.tsx +24 -16
  471. package/templates/marketplace/client/ListingCard.tsx +118 -0
  472. package/templates/marketplace/client/LiveGrid.tsx +92 -0
  473. package/templates/marketplace/client/LiveListingStatus.tsx +32 -24
  474. package/templates/marketplace/client/LoginCard.tsx +114 -136
  475. package/templates/marketplace/client/MarketProvider.tsx +3 -5
  476. package/templates/marketplace/client/MyMarket.tsx +371 -185
  477. package/templates/marketplace/client/OfferPanel.tsx +421 -287
  478. package/templates/marketplace/client/SellForm.tsx +271 -192
  479. package/templates/marketplace/client/ThemeToggle.tsx +1 -0
  480. package/templates/marketplace/client/WatchButton.tsx +16 -16
  481. package/templates/marketplace/client/market.ts +61 -14
  482. package/templates/marketplace/functions/seedMarket.ts +143 -36
  483. package/templates/marketplace/functions/seedOffers.ts +101 -0
  484. package/templates/marketplace/lib/catalog.ts +72 -1
  485. package/templates/marketplace/public/images/listings/ceramic-lamp-640.webp +0 -0
  486. package/templates/marketplace/public/images/listings/ceramic-lamp.webp +0 -0
  487. package/templates/marketplace/public/images/listings/duffel-640.webp +0 -0
  488. package/templates/marketplace/public/images/listings/duffel.webp +0 -0
  489. package/templates/marketplace/public/images/listings/film-camera-640.webp +0 -0
  490. package/templates/marketplace/public/images/listings/film-camera.webp +0 -0
  491. package/templates/marketplace/public/images/listings/lounge-chair-640.webp +0 -0
  492. package/templates/marketplace/public/images/listings/lounge-chair.webp +0 -0
  493. package/templates/marketplace/public/images/listings/road-bike-640.webp +0 -0
  494. package/templates/marketplace/public/images/listings/road-bike.webp +0 -0
  495. package/templates/marketplace/public/images/listings/slipper-chair-640.webp +0 -0
  496. package/templates/marketplace/public/images/listings/slipper-chair.webp +0 -0
  497. package/templates/marketplace/public/images/listings/turntable-640.webp +0 -0
  498. package/templates/marketplace/public/images/listings/turntable.webp +0 -0
  499. package/templates/marketplace/public/images/listings/wardrobe-640.webp +0 -0
  500. package/templates/marketplace/public/images/listings/wardrobe.webp +0 -0
  501. package/templates/marketplace/public/images/listings/watch-640.webp +0 -0
  502. package/templates/marketplace/public/images/listings/watch.webp +0 -0
  503. package/templates/marketplace/public/images/listings/weekender-bag-640.webp +0 -0
  504. package/templates/marketplace/public/images/listings/weekender-bag.webp +0 -0
  505. package/templates/marketplace/tests/example.test.ts +64 -2
  506. package/templates/marketplace/tests/listing-card.test.tsx +61 -0
  507. package/templates/projects/README.md +24 -3
  508. package/templates/projects/app/apple-icon.png +0 -0
  509. package/templates/projects/app/clients/clients-view.tsx +20 -6
  510. package/templates/projects/app/clients/page.tsx +2 -1
  511. package/templates/projects/app/globals.css +29 -14
  512. package/templates/projects/app/icon.svg +1 -0
  513. package/templates/projects/app/layout.tsx +7 -5
  514. package/templates/projects/app/page.tsx +3 -5
  515. package/templates/projects/app/projects/[id]/page.tsx +2 -1
  516. package/templates/projects/app/projects/[id]/project-view.tsx +45 -30
  517. package/templates/projects/app/projects-view.tsx +105 -52
  518. package/templates/projects/app/workspace.tsx +41 -20
  519. package/templates/projects/app.ts +2 -2
  520. package/templates/projects/components/app-shell.tsx +94 -0
  521. package/templates/projects/components/auth-form.tsx +35 -5
  522. package/templates/projects/components/avatar.tsx +5 -1
  523. package/templates/projects/components/board-skeleton.tsx +3 -3
  524. package/templates/projects/components/brand-mark.tsx +25 -0
  525. package/templates/projects/components/command-palette.tsx +17 -17
  526. package/templates/projects/components/data-table.tsx +97 -36
  527. package/templates/projects/components/filter-tabs.tsx +101 -0
  528. package/templates/projects/components/meter.tsx +48 -0
  529. package/templates/projects/components/nav.tsx +14 -0
  530. package/templates/projects/components/page-header.tsx +43 -7
  531. package/templates/projects/components/require-auth.tsx +24 -26
  532. package/templates/projects/components/sidebar.tsx +54 -48
  533. package/templates/projects/components/summary-bar.tsx +49 -0
  534. package/templates/projects/components/task-board.tsx +10 -9
  535. package/templates/projects/components/time-dialog.tsx +29 -5
  536. package/templates/projects/components/ui/button.tsx +5 -4
  537. package/templates/projects/components/ui/dialog.tsx +1 -1
  538. package/templates/projects/components/ui/input.tsx +1 -1
  539. package/templates/projects/components/ui/select.tsx +1 -1
  540. package/templates/projects/components/ui/textarea.tsx +1 -1
  541. package/templates/projects/functions/seedWorkspace.ts +26 -12
  542. package/templates/projects/lib/brand.ts +9 -0
  543. package/templates/projects/lib/demo-team.ts +51 -0
  544. package/templates/projects/lib/search.ts +4 -3
  545. package/templates/projects/lib/seed.ts +35 -1
  546. package/templates/projects/lib/work.ts +63 -0
  547. package/templates/projects/public/favicon.ico +0 -0
  548. package/templates/projects/tests/demo-team.test.ts +18 -0
  549. package/templates/projects/tests/work.test.ts +65 -0
  550. package/templates/restaurant/.env.example +7 -0
  551. package/templates/restaurant/README.md +29 -3
  552. package/templates/restaurant/app/(marketing)/layout.tsx +4 -2
  553. package/templates/restaurant/app/(marketing)/page.tsx +47 -23
  554. package/templates/restaurant/app/(marketing)/reservation-widget.tsx +37 -7
  555. package/templates/restaurant/app/auth-form.tsx +8 -8
  556. package/templates/restaurant/app/dashboard/dashboard-client.tsx +241 -109
  557. package/templates/restaurant/app/dashboard/page.tsx +14 -14
  558. package/templates/restaurant/app/icon.svg +4 -0
  559. package/templates/restaurant/app/login/page.tsx +15 -15
  560. package/templates/restaurant/functions/seedDemo.ts +44 -0
  561. package/templates/restaurant/lib/demo.ts +144 -0
  562. package/templates/restaurant/lib/hours.ts +45 -0
  563. package/templates/restaurant/lib/site.config.ts +3 -3
  564. package/templates/restaurant/public/favicon.ico +0 -0
  565. package/templates/restaurant/tests/reservations.test.ts +67 -0
  566. package/templates/saas/README.md +54 -35
  567. package/templates/saas/app/(auth)/auth-form.tsx +25 -23
  568. package/templates/saas/app/(auth)/forgot-password/page.tsx +1 -1
  569. package/templates/saas/app/(auth)/layout.tsx +27 -33
  570. package/templates/saas/app/(auth)/login/page.tsx +1 -1
  571. package/templates/saas/app/(auth)/reset-password/page.tsx +1 -1
  572. package/templates/saas/app/(auth)/signup/page.tsx +1 -1
  573. package/templates/saas/app/(marketing)/layout.tsx +127 -253
  574. package/templates/saas/app/(marketing)/page.tsx +133 -372
  575. package/templates/saas/app/(marketing)/pricing/page.tsx +21 -19
  576. package/templates/saas/app/(marketing)/products/[slug]/page.tsx +40 -95
  577. package/templates/saas/app/dashboard/board-client.tsx +416 -0
  578. package/templates/saas/app/dashboard/dashboard-client.tsx +30 -615
  579. package/templates/saas/app/dashboard/layout.tsx +27 -13
  580. package/templates/saas/app/dashboard/overview-client.tsx +332 -0
  581. package/templates/saas/app/dashboard/page.tsx +29 -35
  582. package/templates/saas/app/dashboard/projects/[id]/page.tsx +29 -0
  583. package/templates/saas/app/dashboard/projects/page.tsx +11 -7
  584. package/templates/saas/app/dashboard/projects-client.tsx +398 -0
  585. package/templates/saas/app/globals.css +5 -4
  586. package/templates/saas/app/icon.svg +1 -0
  587. package/templates/saas/app/onboarding/onboarding-client.tsx +93 -26
  588. package/templates/saas/app/onboarding/page.tsx +6 -2
  589. package/templates/saas/app.ts +68 -13
  590. package/templates/saas/components/dashboard-shell.tsx +212 -132
  591. package/templates/saas/components/marketing.tsx +137 -274
  592. package/templates/saas/components/pricing-table.tsx +12 -11
  593. package/templates/saas/components/setup-checklist.tsx +31 -29
  594. package/templates/saas/components/task-ui.tsx +199 -0
  595. package/templates/saas/functions/createProject.ts +7 -13
  596. package/templates/saas/functions/createTask.ts +67 -0
  597. package/templates/saas/functions/deleteProject.ts +17 -0
  598. package/templates/saas/functions/deleteTask.ts +14 -0
  599. package/templates/saas/functions/seedWorkspace.ts +46 -0
  600. package/templates/saas/functions/setProjectStatus.ts +41 -0
  601. package/templates/saas/functions/updateProfile.ts +13 -0
  602. package/templates/saas/functions/updateProject.ts +4 -5
  603. package/templates/saas/functions/updateTask.ts +88 -0
  604. package/templates/saas/lib/members.ts +53 -0
  605. package/templates/saas/lib/names.ts +53 -0
  606. package/templates/saas/lib/plans.ts +11 -0
  607. package/templates/saas/lib/sample-workspace.ts +117 -0
  608. package/templates/saas/lib/site.config.ts +200 -463
  609. package/templates/saas/lib/tasks.ts +101 -0
  610. package/templates/saas/lib/use-live.ts +25 -0
  611. package/templates/saas/public/favicon.ico +0 -0
  612. package/templates/saas/public/screenshots/board.png +0 -0
  613. package/templates/saas/public/screenshots/members.png +0 -0
  614. package/templates/saas/public/screenshots/overview.png +0 -0
  615. package/templates/saas/public/screenshots/projects.png +0 -0
  616. package/templates/saas/scripts/capture-screenshots.mjs +156 -0
  617. package/templates/saas/tests/names.test.ts +35 -0
  618. package/templates/saas/tests/plans.test.ts +12 -0
  619. package/templates/saas/tests/sample-workspace.test.ts +46 -0
  620. package/templates/saas/tests/tasks.test.ts +81 -0
  621. package/templates/shop/README.md +25 -13
  622. package/templates/shop/app/(marketing)/layout.tsx +94 -67
  623. package/templates/shop/app/(marketing)/not-found.tsx +13 -12
  624. package/templates/shop/app/(marketing)/page.tsx +120 -59
  625. package/templates/shop/app/(marketing)/shop-client.tsx +364 -237
  626. package/templates/shop/app/(marketing)/success/page.tsx +75 -22
  627. package/templates/shop/app/globals.css +94 -1
  628. package/templates/shop/app/layout.tsx +4 -4
  629. package/templates/shop/app/login/page.tsx +4 -4
  630. package/templates/shop/app.ts +18 -6
  631. package/templates/shop/components/cart-button.tsx +33 -0
  632. package/templates/shop/components/marketing.tsx +17 -87
  633. package/templates/shop/components/shelf-count.tsx +36 -0
  634. package/templates/shop/functions/checkout.ts +17 -1
  635. package/templates/shop/lib/cart-store.ts +57 -0
  636. package/templates/shop/lib/shelf-store.ts +35 -0
  637. package/templates/shop/lib/shop.ts +27 -0
  638. package/templates/shop/lib/site.config.ts +108 -98
  639. package/templates/shop/public/images/hero.jpg +0 -0
  640. package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
  641. package/templates/shop/public/images/products/fig-leaf.jpg +0 -0
  642. package/templates/shop/public/images/products/linen.jpg +0 -0
  643. package/templates/shop/public/images/products/sea-salt.jpg +0 -0
  644. package/templates/shop/public/images/products/tobacco-oak.jpg +0 -0
  645. package/templates/shop/tests/shop.test.ts +64 -0
  646. package/templates/waitlist/.env.example +7 -0
  647. package/templates/waitlist/README.md +29 -13
  648. package/templates/waitlist/app/(marketing)/page.tsx +9 -7
  649. package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +64 -31
  650. package/templates/waitlist/app/auth-form.tsx +8 -8
  651. package/templates/waitlist/app/dashboard/dashboard-client.tsx +132 -95
  652. package/templates/waitlist/app/dashboard/page.tsx +17 -16
  653. package/templates/waitlist/app/icon.svg +2 -0
  654. package/templates/waitlist/app/login/page.tsx +14 -14
  655. package/templates/waitlist/app.ts +6 -6
  656. package/templates/waitlist/functions/joinWaitlist.ts +6 -18
  657. package/templates/waitlist/functions/seedDemo.ts +32 -0
  658. package/templates/waitlist/lib/demo.ts +94 -0
  659. package/templates/waitlist/lib/launch.ts +19 -0
  660. package/templates/waitlist/lib/site.config.ts +14 -9
  661. package/templates/waitlist/lib/stats.ts +35 -0
  662. package/templates/waitlist/public/favicon.ico +0 -0
  663. package/templates/waitlist/tests/counter.test.ts +107 -0
  664. package/templates/waitlist/tests/live-count.test.tsx +48 -0
  665. package/templates/ai-chat/tests/example.test.ts +0 -12
  666. package/templates/backend/consumer/apps/api/functions/feed.ts +0 -57
  667. package/templates/backend/consumer/apps/api/functions/profilePosts.ts +0 -17
  668. package/templates/backend/consumer/apps/api/functions/toggleLike.ts +0 -48
  669. package/templates/backend/consumer/apps/api/schema.ts +0 -143
  670. package/templates/chat/functions/_keep.ts +0 -13
  671. package/templates/chat/tests/example.test.ts +0 -12
  672. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +0 -68
  673. package/templates/marketplace/client/LiveTicker.tsx +0 -116
  674. package/templates/marketplace/client/ScrollToListingsLink.tsx +0 -29
  675. package/templates/marketplace/public/images/marketplace-hero.webp +0 -0
@@ -0,0 +1,299 @@
1
+ import {
2
+ auth,
3
+ buildManifest,
4
+ cron,
5
+ discoverAppRoutes,
6
+ discoverFunctions,
7
+ entity,
8
+ field,
9
+ font,
10
+ llm,
11
+ policy,
12
+ } from "@pylonsync/sdk";
13
+ import { ASSISTANT_MODEL } from "./lib/assistant";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // imessage: an AI assistant people text over iMessage.
17
+ //
18
+ // Texts arrive through one of two transports (lib/transport.ts): Sendblue's
19
+ // hosted API or a relay script on your own Mac. Each inbound message is
20
+ // stored, checked against the allowlist and rate limit (lib/gate.ts), and, if
21
+ // it should be answered, handed to the `assistant` agent (functions/assistant.ts),
22
+ // which replies with conversation history and a few tools. Replies go back
23
+ // through the same transport.
24
+ //
25
+ // The dashboard is for one person: the owner, the account whose verified
26
+ // email is in PYLON_ADMIN_EMAILS. Pylon lifts that session to admin, and every
27
+ // entity below is readable by admins only. Anonymous visitors and other
28
+ // signed-in accounts read nothing.
29
+ // ---------------------------------------------------------------------------
30
+
31
+ // A person who texts the number, or whom the owner added to the allowlist.
32
+ // `handle` is normalized (lib/handles.ts): E.164 phone or lowercase email.
33
+ const Contact = entity(
34
+ "Contact",
35
+ {
36
+ handle: field.string(),
37
+ kind: field.string(),
38
+ displayName: field.string().optional(),
39
+ // "allowed" | "blocked" | "unknown". Only allowed contacts get answers.
40
+ status: field.string().default("unknown"),
41
+ // The contact texted STOP. Cleared by START.
42
+ optedOut: field.bool().default(false),
43
+ // When the one-time reply to an unknown sender went out.
44
+ autoReplySentAt: field.datetime().optional(),
45
+ // Last STOP / START confirmation sent. At most one of each per day.
46
+ optOutReplyAt: field.datetime().optional(),
47
+ optInReplyAt: field.datetime().optional(),
48
+ lastMessageAt: field.datetime().optional(),
49
+ demo: field.bool().default(false),
50
+ createdAt: field.datetime().defaultNow(),
51
+ },
52
+ {
53
+ indexes: [
54
+ { name: "by_handle", fields: ["handle"], unique: true },
55
+ { name: "by_status", fields: ["status"], unique: false },
56
+ { name: "by_created", fields: ["createdAt"], unique: false },
57
+ ],
58
+ },
59
+ );
60
+
61
+ // One thread per contact. It also carries the agent turn state: only one turn
62
+ // runs per conversation at a time (functions/beginTurn.ts), and texts that
63
+ // arrive during a turn are answered by the next one.
64
+ const Conversation = entity(
65
+ "Conversation",
66
+ {
67
+ contactId: field.id("Contact"),
68
+ handle: field.string(),
69
+ lastMessageAt: field.datetime().optional(),
70
+ lastPreview: field.string().optional(),
71
+ lastDirection: field.string().optional(),
72
+ // The AgentRun that holds this conversation's model transcript.
73
+ agentRunId: field.string().optional(),
74
+ // "idle" | "running"
75
+ turnState: field.string().default("idle"),
76
+ turnId: field.string().optional(),
77
+ turnStartedAt: field.datetime().optional(),
78
+ needsAnotherTurn: field.bool().default(false),
79
+ lastError: field.string().optional(),
80
+ lastErrorAt: field.datetime().optional(),
81
+ demo: field.bool().default(false),
82
+ createdAt: field.datetime().defaultNow(),
83
+ },
84
+ {
85
+ indexes: [
86
+ { name: "by_contact", fields: ["contactId"], unique: true },
87
+ { name: "by_last_message", fields: ["lastMessageAt"], unique: false },
88
+ { name: "by_agent_run", fields: ["agentRunId"], unique: false },
89
+ ],
90
+ },
91
+ );
92
+
93
+ // Every text in or out.
94
+ // direction "in": status received | processing | answered | ignored |
95
+ // rate_limited | awaiting_setup | failed
96
+ // direction "out": status queued | leased | sent | dry_run | failed
97
+ // kind: inbound | agent | auto_reply | reminder | owner
98
+ const Message = entity(
99
+ "Message",
100
+ {
101
+ conversationId: field.id("Conversation"),
102
+ contactId: field.id("Contact"),
103
+ direction: field.string(),
104
+ kind: field.string(),
105
+ text: field.string(),
106
+ status: field.string(),
107
+ // Why an inbound message was not answered, or why a send failed.
108
+ detail: field.string().optional(),
109
+ transport: field.string().optional(),
110
+ // Transport message id ("sendblue:<handle>" / "relay:<guid>"). Unique, so
111
+ // a retried webhook or a repeated relay batch stores nothing twice.
112
+ externalId: field.string().optional(),
113
+ turnId: field.string().optional(),
114
+ agentRunId: field.string().optional(),
115
+ // Delivery lease: a relay sync or a Sendblue delivery job holds a queued
116
+ // reply until this time. `leaseToken` names the holder, so only it can
117
+ // settle the message.
118
+ leaseUntil: field.datetime().optional(),
119
+ leaseToken: field.string().optional(),
120
+ attempts: field.int().default(0),
121
+ sentAt: field.datetime().optional(),
122
+ demo: field.bool().default(false),
123
+ createdAt: field.datetime().defaultNow(),
124
+ },
125
+ {
126
+ indexes: [
127
+ { name: "by_conversation_created", fields: ["conversationId", "createdAt"], unique: false },
128
+ { name: "by_contact_created", fields: ["contactId", "createdAt"], unique: false },
129
+ { name: "by_external_id", fields: ["externalId"], unique: true },
130
+ { name: "by_status", fields: ["status"], unique: false },
131
+ ],
132
+ // Messages grow forever. The dashboard replica holds the last 30 days.
133
+ sync: { where: 'data.createdAt >= ago("30d")', limit: 5000 },
134
+ },
135
+ );
136
+
137
+ // Short facts the assistant keeps about a contact (functions/assistant.ts tools).
138
+ const Note = entity(
139
+ "Note",
140
+ {
141
+ contactId: field.id("Contact"),
142
+ text: field.string(),
143
+ demo: field.bool().default(false),
144
+ createdAt: field.datetime().defaultNow(),
145
+ },
146
+ { indexes: [{ name: "by_contact", fields: ["contactId"], unique: false }] },
147
+ );
148
+
149
+ // A follow-up text the assistant scheduled. functions/deliverReminder.ts runs
150
+ // at `dueAt` through the job scheduler.
151
+ const Reminder = entity(
152
+ "Reminder",
153
+ {
154
+ contactId: field.id("Contact"),
155
+ conversationId: field.id("Conversation"),
156
+ text: field.string(),
157
+ dueAt: field.datetime(),
158
+ // "scheduled" | "sending" | "sent" | "cancelled" | "failed"
159
+ status: field.string(),
160
+ detail: field.string().optional(),
161
+ sentAt: field.datetime().optional(),
162
+ demo: field.bool().default(false),
163
+ createdAt: field.datetime().defaultNow(),
164
+ },
165
+ {
166
+ indexes: [
167
+ { name: "by_contact_status", fields: ["contactId", "status"], unique: false },
168
+ { name: "by_contact_created", fields: ["contactId", "createdAt"], unique: false },
169
+ { name: "by_due", fields: ["dueAt"], unique: false },
170
+ ],
171
+ },
172
+ );
173
+
174
+ // Owner settings. One row, key "main" (lib/settings.ts fills defaults).
175
+ const Settings = entity(
176
+ "Settings",
177
+ {
178
+ key: field.string(),
179
+ ownerName: field.string().optional(),
180
+ timezone: field.string(),
181
+ // "ignore" | "reply_once"
182
+ unknownSenderPolicy: field.string(),
183
+ unknownSenderReply: field.string(),
184
+ rateLimitPerHour: field.int(),
185
+ paused: field.bool().default(false),
186
+ demoSeededAt: field.datetime().optional(),
187
+ updatedAt: field.datetime().defaultNow(),
188
+ },
189
+ { indexes: [{ name: "by_key", fields: ["key"], unique: true }] },
190
+ );
191
+
192
+ // Last-seen times per transport, for the dashboard's status panel.
193
+ const TransportState = entity(
194
+ "TransportState",
195
+ {
196
+ name: field.string(),
197
+ lastInboundAt: field.datetime().optional(),
198
+ lastOutboundAt: field.datetime().optional(),
199
+ lastError: field.string().optional(),
200
+ lastErrorAt: field.datetime().optional(),
201
+ relayLastSeenAt: field.datetime().optional(),
202
+ relayVersion: field.string().optional(),
203
+ relayHost: field.string().optional(),
204
+ },
205
+ { indexes: [{ name: "by_name", fields: ["name"], unique: true }] },
206
+ );
207
+
208
+ // The owner's account. Sign-in is by email code only (/login): the code
209
+ // proves the address, and Pylon's PYLON_ADMIN_EMAILS check only promotes
210
+ // verified emails. There is deliberately no `passwordHash` column, so the
211
+ // built-in password routes cannot create or sign in to an account. That
212
+ // closes a pre-registration takeover: without it, anyone could register the
213
+ // owner's email with a password before the owner first signs in, and the
214
+ // owner's later code sign-in would mark that attacker-created account
215
+ // verified, making the attacker's password an admin login.
216
+ const User = entity(
217
+ "User",
218
+ {
219
+ email: field.string(),
220
+ displayName: field.string().optional(),
221
+ avatarColor: field.string().optional(),
222
+ emailVerified: field.datetime().optional(),
223
+ createdAt: field.datetime().defaultNow(),
224
+ },
225
+ { indexes: [{ name: "by_email", fields: ["email"], unique: true }] },
226
+ );
227
+
228
+ // Owner-only reads; no client writes. Every write goes through a function.
229
+ const ownerOnly = (name: string, entityName: string) =>
230
+ policy({
231
+ name,
232
+ entity: entityName,
233
+ allowRead: "auth.isAdmin",
234
+ allowInsert: "false",
235
+ allowUpdate: "false",
236
+ allowDelete: "false",
237
+ });
238
+
239
+ const userPolicy = policy({
240
+ name: "user_self",
241
+ entity: "User",
242
+ allowRead: "auth.userId != null && auth.userId == data.id",
243
+ allowInsert: "false",
244
+ allowUpdate: "false",
245
+ allowDelete: "false",
246
+ });
247
+
248
+ const fns = await discoverFunctions();
249
+
250
+ const manifest = buildManifest({
251
+ name: "__APP_NAME__",
252
+ version: "0.1.0",
253
+ entities: [Contact, Conversation, Message, Note, Reminder, Settings, TransportState, User],
254
+ queries: fns.queries,
255
+ actions: fns.actions,
256
+ // discoverFunctions() reports the `assistant` agent; this adds the
257
+ // framework's AgentRun/AgentMessage entities (owner-scoped, synced).
258
+ hasAgents: fns.hasAgents,
259
+ policies: [
260
+ ownerOnly("contact_owner", "Contact"),
261
+ ownerOnly("conversation_owner", "Conversation"),
262
+ ownerOnly("message_owner", "Message"),
263
+ ownerOnly("note_owner", "Note"),
264
+ ownerOnly("reminder_owner", "Reminder"),
265
+ ownerOnly("settings_owner", "Settings"),
266
+ ownerOnly("transport_state_owner", "TransportState"),
267
+ userPolicy,
268
+ ],
269
+ auth: auth(),
270
+ // The one model the assistant uses. ctx.llm refuses any other id.
271
+ llm: llm({ defaultModel: ASSISTANT_MODEL, allowedModels: [ASSISTANT_MODEL] }),
272
+ crons: [
273
+ // Recovers turns whose worker died and relay replies whose lease expired.
274
+ cron("*/5 * * * *", "sweep", { description: "Recover stuck turns and expired relay leases" }),
275
+ ],
276
+ fonts: [
277
+ font({
278
+ family: "Geist",
279
+ variable: "--font-ui",
280
+ weights: ["400", "500", "600", "700"],
281
+ subsets: ["latin"],
282
+ display: "swap",
283
+ preload: true,
284
+ }),
285
+ font({
286
+ family: "Geist Mono",
287
+ variable: "--font-code",
288
+ weights: ["400", "500"],
289
+ subsets: ["latin"],
290
+ display: "swap",
291
+ }),
292
+ ],
293
+ routes: await discoverAppRoutes(),
294
+ });
295
+
296
+ // The CLI runs `bun run app.ts` and reads the manifest from stdout. Keep this line.
297
+ console.log(JSON.stringify(manifest, null, 2));
298
+
299
+ export default manifest;
@@ -0,0 +1,4 @@
1
+ [test]
2
+ # Register a DOM (happy-dom) before any test runs so React component tests can
3
+ # render under `pylon test` / `bun test`. Harmless for pure-logic tests.
4
+ preload = ["./tests/setup.ts"]
@@ -0,0 +1,46 @@
1
+ import React from "react";
2
+ import { initials } from "@/lib/format";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ // Messages-style monogram: a soft gradient disc with initials.
6
+ const TONES = [
7
+ "from-[#a1a1aa] to-[#71717a]",
8
+ "from-[#93c5fd] to-[#3b82f6]",
9
+ "from-[#86efac] to-[#16a34a]",
10
+ "from-[#fcd34d] to-[#d97706]",
11
+ "from-[#f9a8d4] to-[#db2777]",
12
+ "from-[#c4b5fd] to-[#7c3aed]",
13
+ ];
14
+
15
+ function tone(seed: string): string {
16
+ let h = 0;
17
+ for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
18
+ return TONES[h % TONES.length];
19
+ }
20
+
21
+ export function Avatar({
22
+ name,
23
+ handle,
24
+ size = 40,
25
+ className,
26
+ }: {
27
+ name: string | null | undefined;
28
+ handle: string;
29
+ size?: number;
30
+ className?: string;
31
+ }) {
32
+ const known = Boolean(name && name.trim());
33
+ return (
34
+ <span
35
+ aria-hidden
36
+ className={cn(
37
+ "inline-flex shrink-0 select-none items-center justify-center rounded-full bg-gradient-to-b font-semibold text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.25)]",
38
+ known ? tone(handle) : TONES[0],
39
+ className,
40
+ )}
41
+ style={{ width: size, height: size, fontSize: Math.round(size * 0.38) }}
42
+ >
43
+ {initials(name, handle)}
44
+ </span>
45
+ );
46
+ }
@@ -0,0 +1,263 @@
1
+ import React, { useState } from "react";
2
+ import { Bell, NotebookPen, Sparkles, X } from "lucide-react";
3
+ import { Avatar } from "@/components/avatar";
4
+ import { ago } from "@/lib/format";
5
+ import { formatHandle } from "@/lib/handles";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ export interface PanelNote {
9
+ id: string;
10
+ text: string;
11
+ createdAt: string;
12
+ }
13
+ export interface PanelReminder {
14
+ id: string;
15
+ text: string;
16
+ dueAt: string;
17
+ status: string;
18
+ }
19
+ export interface PanelActivity {
20
+ id: string;
21
+ label: string;
22
+ at: string;
23
+ }
24
+ export interface PanelRun {
25
+ status: string;
26
+ steps: number;
27
+ updatedAt: string;
28
+ error: string | null;
29
+ }
30
+
31
+ type Status = "allowed" | "unknown" | "blocked";
32
+
33
+ const STATUS_OPTIONS: { value: Status; label: string }[] = [
34
+ { value: "allowed", label: "Allowed" },
35
+ { value: "unknown", label: "Not listed" },
36
+ { value: "blocked", label: "Blocked" },
37
+ ];
38
+
39
+ export function ContactPanel({
40
+ name,
41
+ handle,
42
+ status,
43
+ optedOut,
44
+ unanswered,
45
+ notes,
46
+ reminders,
47
+ run,
48
+ activity,
49
+ onClose,
50
+ onStatus,
51
+ onRename,
52
+ onAnswerNow,
53
+ onDeleteNote,
54
+ onCancelReminder,
55
+ }: {
56
+ name: string | null;
57
+ handle: string;
58
+ status: Status;
59
+ optedOut: boolean;
60
+ unanswered: number;
61
+ notes: PanelNote[];
62
+ reminders: PanelReminder[];
63
+ run: PanelRun | null;
64
+ activity: PanelActivity[];
65
+ onClose?: () => void;
66
+ onStatus: (status: Status) => void;
67
+ onRename: (name: string) => void;
68
+ onAnswerNow: () => void;
69
+ onDeleteNote: (id: string) => void;
70
+ onCancelReminder: (id: string) => void;
71
+ }) {
72
+ const [editing, setEditing] = useState(false);
73
+ const [draft, setDraft] = useState(name ?? "");
74
+
75
+ return (
76
+ <aside className="flex h-full min-h-0 flex-col overflow-y-auto bg-rail/60 px-4 pb-6 pt-4" aria-label="Contact details">
77
+ {onClose ? (
78
+ <button
79
+ type="button"
80
+ onClick={onClose}
81
+ className="ml-auto rounded-full p-1.5 text-muted-foreground hover:bg-accent"
82
+ aria-label="Close details"
83
+ >
84
+ <X className="size-4" strokeWidth={1.5} />
85
+ </button>
86
+ ) : null}
87
+ <div className="flex flex-col items-center pt-2 text-center">
88
+ <Avatar name={name} handle={handle} size={64} />
89
+ {editing ? (
90
+ <form
91
+ className="mt-3 flex w-full gap-2"
92
+ onSubmit={(e) => {
93
+ e.preventDefault();
94
+ onRename(draft);
95
+ setEditing(false);
96
+ }}
97
+ >
98
+ <input
99
+ autoFocus
100
+ value={draft}
101
+ onChange={(e) => setDraft(e.target.value)}
102
+ aria-label="Name"
103
+ className="h-8 min-w-0 flex-1 rounded-lg border bg-background px-2.5 text-[14px] outline-none focus:ring-2 focus:ring-ring/30"
104
+ />
105
+ <button type="submit" className="rounded-lg bg-foreground px-3 text-[13px] font-medium text-background">
106
+ Save
107
+ </button>
108
+ </form>
109
+ ) : (
110
+ <button
111
+ type="button"
112
+ onClick={() => {
113
+ setDraft(name ?? "");
114
+ setEditing(true);
115
+ }}
116
+ className="mt-3 rounded-md px-1 text-[17px] font-semibold tracking-[-0.015em] hover:bg-accent"
117
+ >
118
+ {name ?? "Add a name"}
119
+ </button>
120
+ )}
121
+ <p className="mt-0.5 font-mono text-[12px] text-muted-foreground">{formatHandle(handle)}</p>
122
+ </div>
123
+
124
+ <Card className="mt-5">
125
+ <p className="text-[13px] font-medium">Assistant replies to this person</p>
126
+ <div className="mt-2.5 grid grid-cols-3 rounded-[0.65rem] bg-black/[0.05] p-0.5 dark:bg-white/[0.06]" role="radiogroup" aria-label="Allowlist status">
127
+ {STATUS_OPTIONS.map((o) => (
128
+ <button
129
+ key={o.value}
130
+ type="button"
131
+ role="radio"
132
+ aria-checked={status === o.value}
133
+ onClick={() => onStatus(o.value)}
134
+ className={cn(
135
+ "rounded-[0.55rem] py-1.5 text-[12px] font-medium transition-all duration-300 ease-[cubic-bezier(0.32,0.72,0,1)]",
136
+ status === o.value
137
+ ? "bg-background text-foreground shadow-[0_1px_2px_rgba(0,0,0,0.08),0_0_0_0.5px_rgba(0,0,0,0.06)]"
138
+ : "text-muted-foreground hover:text-foreground",
139
+ )}
140
+ >
141
+ {o.label}
142
+ </button>
143
+ ))}
144
+ </div>
145
+ <p className="mt-2 text-[12px] leading-snug text-muted-foreground">
146
+ {optedOut
147
+ ? "This person texted STOP. Nothing is sent to them until they text START."
148
+ : status === "allowed"
149
+ ? "Texts from this number get an answer from the assistant."
150
+ : status === "blocked"
151
+ ? "Texts are stored and never answered."
152
+ : "Texts are stored for you to review. The assistant does not answer."}
153
+ </p>
154
+ {unanswered > 0 && status === "allowed" && !optedOut ? (
155
+ <button
156
+ type="button"
157
+ onClick={onAnswerNow}
158
+ className="mt-3 w-full rounded-full bg-bubble-out py-2 text-[13px] font-medium text-white transition-transform duration-300 ease-[cubic-bezier(0.32,0.72,0,1)] active:scale-[0.98]"
159
+ >
160
+ Answer {unanswered} unanswered text{unanswered === 1 ? "" : "s"}
161
+ </button>
162
+ ) : null}
163
+ </Card>
164
+
165
+ <Section icon={<Sparkles className="size-3.5" strokeWidth={1.5} />} title="Assistant activity">
166
+ {run ? (
167
+ <p className="mb-2 text-[12px] text-muted-foreground">
168
+ Last run {run.status === "running" ? "is writing now" : run.status} · {run.steps} step
169
+ {run.steps === 1 ? "" : "s"} · {ago(run.updatedAt)}
170
+ {run.error ? <span className="mt-1 block text-destructive">{run.error}</span> : null}
171
+ </p>
172
+ ) : null}
173
+ {activity.length === 0 ? (
174
+ <Empty>No tool use yet.</Empty>
175
+ ) : (
176
+ <ul className="space-y-1.5">
177
+ {activity.map((a) => (
178
+ <li key={a.id} className="text-[13px] leading-snug">
179
+ {a.label}
180
+ <span className="ml-1.5 text-[11px] text-muted-foreground">{ago(a.at)}</span>
181
+ </li>
182
+ ))}
183
+ </ul>
184
+ )}
185
+ </Section>
186
+
187
+ <Section icon={<NotebookPen className="size-3.5" strokeWidth={1.5} />} title={`Notes${notes.length ? ` (${notes.length})` : ""}`}>
188
+ {notes.length === 0 ? (
189
+ <Empty>The assistant saves facts here when this person mentions them.</Empty>
190
+ ) : (
191
+ <ul className="space-y-1">
192
+ {notes.map((n) => (
193
+ <li key={n.id} className="group flex items-start gap-2 rounded-lg py-1 text-[13px] leading-snug">
194
+ <span className="flex-1">{n.text}</span>
195
+ <button
196
+ type="button"
197
+ onClick={() => onDeleteNote(n.id)}
198
+ className="rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:text-destructive group-hover:opacity-100 focus:opacity-100"
199
+ aria-label={`Delete note: ${n.text}`}
200
+ >
201
+ <X className="size-3.5" strokeWidth={1.5} />
202
+ </button>
203
+ </li>
204
+ ))}
205
+ </ul>
206
+ )}
207
+ </Section>
208
+
209
+ <Section icon={<Bell className="size-3.5" strokeWidth={1.5} />} title="Reminders">
210
+ {reminders.length === 0 ? (
211
+ <Empty>None scheduled.</Empty>
212
+ ) : (
213
+ <ul className="space-y-2">
214
+ {reminders.map((r) => (
215
+ <li key={r.id} className="rounded-lg bg-background/80 px-2.5 py-2 text-[13px] leading-snug ring-1 ring-border">
216
+ <p className="text-[12px] font-medium text-muted-foreground">
217
+ {new Date(r.dueAt).toLocaleString("en-US", { weekday: "short", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" })}
218
+ {r.status !== "scheduled" ? ` · ${r.status}` : ""}
219
+ </p>
220
+ <p className="mt-0.5">{r.text}</p>
221
+ {r.status === "scheduled" ? (
222
+ <button
223
+ type="button"
224
+ onClick={() => onCancelReminder(r.id)}
225
+ className="mt-1 text-[12px] font-medium text-destructive hover:underline"
226
+ >
227
+ Cancel
228
+ </button>
229
+ ) : null}
230
+ </li>
231
+ ))}
232
+ </ul>
233
+ )}
234
+ </Section>
235
+ </aside>
236
+ );
237
+ }
238
+
239
+ function Card({ children, className }: { children: React.ReactNode; className?: string }) {
240
+ return (
241
+ <div className={cn("rounded-[1.1rem] bg-black/[0.03] p-1 ring-1 ring-black/[0.04] dark:bg-white/[0.04] dark:ring-white/[0.06]", className)}>
242
+ <div className="rounded-[calc(1.1rem-0.25rem)] bg-background p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.6),0_1px_2px_rgba(0,0,0,0.03)]">
243
+ {children}
244
+ </div>
245
+ </div>
246
+ );
247
+ }
248
+
249
+ function Section({ icon, title, children }: { icon: React.ReactNode; title: string; children: React.ReactNode }) {
250
+ return (
251
+ <section className="mt-6 px-1">
252
+ <h2 className="mb-2 flex items-center gap-1.5 text-[13px] font-semibold text-foreground">
253
+ <span className="text-muted-foreground">{icon}</span>
254
+ {title}
255
+ </h2>
256
+ {children}
257
+ </section>
258
+ );
259
+ }
260
+
261
+ function Empty({ children }: { children: React.ReactNode }) {
262
+ return <p className="text-[12px] leading-snug text-muted-foreground">{children}</p>;
263
+ }