pilothub 0.0.1 → 0.0.2

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 (388) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +36 -129
  3. package/dist/browserAuth.d.ts +20 -0
  4. package/dist/browserAuth.js +156 -0
  5. package/dist/browserAuth.js.map +1 -0
  6. package/dist/browserAuth.test.d.ts +1 -0
  7. package/dist/browserAuth.test.js +83 -0
  8. package/dist/browserAuth.test.js.map +1 -0
  9. package/dist/cli/buildInfo.d.ts +3 -0
  10. package/dist/cli/buildInfo.js +103 -0
  11. package/dist/cli/buildInfo.js.map +1 -0
  12. package/dist/cli/commands/auth.d.ts +9 -0
  13. package/dist/cli/commands/auth.js +75 -0
  14. package/dist/cli/commands/auth.js.map +1 -0
  15. package/dist/cli/commands/delete.d.ts +11 -0
  16. package/dist/cli/commands/delete.js +67 -0
  17. package/dist/cli/commands/delete.js.map +1 -0
  18. package/dist/cli/commands/delete.test.d.ts +1 -0
  19. package/dist/cli/commands/delete.test.js +52 -0
  20. package/dist/cli/commands/delete.test.js.map +1 -0
  21. package/dist/cli/commands/publish.d.ts +9 -0
  22. package/dist/cli/commands/publish.js +87 -0
  23. package/dist/cli/commands/publish.js.map +1 -0
  24. package/dist/cli/commands/publish.test.d.ts +1 -0
  25. package/dist/cli/commands/publish.test.js +104 -0
  26. package/dist/cli/commands/publish.test.js.map +1 -0
  27. package/dist/cli/commands/skills.d.ts +23 -0
  28. package/dist/cli/commands/skills.js +298 -0
  29. package/dist/cli/commands/skills.js.map +1 -0
  30. package/dist/cli/commands/skills.test.d.ts +1 -0
  31. package/dist/cli/commands/skills.test.js +156 -0
  32. package/dist/cli/commands/skills.test.js.map +1 -0
  33. package/dist/cli/commands/star.d.ts +8 -0
  34. package/dist/cli/commands/star.js +38 -0
  35. package/dist/cli/commands/star.js.map +1 -0
  36. package/dist/cli/commands/sync.d.ts +3 -0
  37. package/dist/cli/commands/sync.js +160 -0
  38. package/dist/cli/commands/sync.js.map +1 -0
  39. package/dist/cli/commands/sync.test.d.ts +1 -0
  40. package/dist/cli/commands/sync.test.js +277 -0
  41. package/dist/cli/commands/sync.test.js.map +1 -0
  42. package/dist/cli/commands/syncHelpers.d.ts +76 -0
  43. package/dist/cli/commands/syncHelpers.js +349 -0
  44. package/dist/cli/commands/syncHelpers.js.map +1 -0
  45. package/dist/cli/commands/syncHelpers.test.d.ts +1 -0
  46. package/dist/cli/commands/syncHelpers.test.js +22 -0
  47. package/dist/cli/commands/syncHelpers.test.js.map +1 -0
  48. package/dist/cli/commands/syncTypes.d.ts +24 -0
  49. package/dist/cli/commands/syncTypes.js +2 -0
  50. package/dist/cli/commands/syncTypes.js.map +1 -0
  51. package/dist/cli/commands/unstar.d.ts +8 -0
  52. package/dist/cli/commands/unstar.js +38 -0
  53. package/dist/cli/commands/unstar.js.map +1 -0
  54. package/dist/cli/helpStyle.d.ts +13 -0
  55. package/dist/cli/helpStyle.js +38 -0
  56. package/dist/cli/helpStyle.js.map +1 -0
  57. package/dist/cli/pilotbotConfig.d.ts +6 -0
  58. package/dist/cli/pilotbotConfig.js +110 -0
  59. package/dist/cli/pilotbotConfig.js.map +1 -0
  60. package/dist/cli/pilotbotConfig.test.d.ts +1 -0
  61. package/dist/cli/pilotbotConfig.test.js +133 -0
  62. package/dist/cli/pilotbotConfig.test.js.map +1 -0
  63. package/dist/cli/registry.d.ts +7 -0
  64. package/dist/cli/registry.js +42 -0
  65. package/dist/cli/registry.js.map +1 -0
  66. package/dist/cli/registry.test.d.ts +1 -0
  67. package/dist/cli/registry.test.js +48 -0
  68. package/dist/cli/registry.test.js.map +1 -0
  69. package/dist/cli/scanSkills.d.ts +7 -0
  70. package/dist/cli/scanSkills.js +75 -0
  71. package/dist/cli/scanSkills.js.map +1 -0
  72. package/dist/cli/scanSkills.test.d.ts +1 -0
  73. package/dist/cli/scanSkills.test.js +60 -0
  74. package/dist/cli/scanSkills.test.js.map +1 -0
  75. package/dist/cli/slug.d.ts +2 -0
  76. package/dist/cli/slug.js +16 -0
  77. package/dist/cli/slug.js.map +1 -0
  78. package/dist/cli/types.d.ts +15 -0
  79. package/dist/cli/types.js +2 -0
  80. package/dist/cli/types.js.map +1 -0
  81. package/dist/cli/ui.d.ts +7 -0
  82. package/dist/cli/ui.js +72 -0
  83. package/dist/cli/ui.js.map +1 -0
  84. package/dist/cli.d.ts +2 -0
  85. package/dist/cli.js +268 -0
  86. package/dist/cli.js.map +1 -0
  87. package/dist/config.d.ts +4 -0
  88. package/dist/config.js +38 -0
  89. package/dist/config.js.map +1 -0
  90. package/dist/discovery.d.ts +5 -0
  91. package/dist/discovery.js +21 -0
  92. package/dist/discovery.js.map +1 -0
  93. package/dist/discovery.test.d.ts +1 -0
  94. package/dist/discovery.test.js +46 -0
  95. package/dist/discovery.test.js.map +1 -0
  96. package/dist/http.d.ts +32 -0
  97. package/dist/http.js +261 -0
  98. package/dist/http.js.map +1 -0
  99. package/dist/http.test.d.ts +1 -0
  100. package/dist/http.test.js +135 -0
  101. package/dist/http.test.js.map +1 -0
  102. package/dist/schema/ark.js.map +1 -0
  103. package/dist/schema/index.js.map +1 -0
  104. package/dist/schema/routes.js.map +1 -0
  105. package/{packages/schema/dist → dist/schema}/schemas.d.ts +0 -39
  106. package/{packages/schema/dist → dist/schema}/schemas.js +0 -22
  107. package/dist/schema/schemas.js.map +1 -0
  108. package/dist/schema/textFiles.js.map +1 -0
  109. package/dist/schema/textFiles.test.d.ts +1 -0
  110. package/dist/schema/textFiles.test.js +20 -0
  111. package/dist/schema/textFiles.test.js.map +1 -0
  112. package/dist/skills.d.ts +43 -0
  113. package/dist/skills.js +163 -0
  114. package/dist/skills.js.map +1 -0
  115. package/dist/skills.test.d.ts +1 -0
  116. package/dist/skills.test.js +144 -0
  117. package/dist/skills.test.js.map +1 -0
  118. package/dist/types.d.ts +7 -0
  119. package/dist/types.js +2 -0
  120. package/dist/types.js.map +1 -0
  121. package/package.json +27 -70
  122. package/.env.local.example +0 -19
  123. package/.github/workflows/ci.yml +0 -40
  124. package/.oxlintrc.json +0 -3
  125. package/AGENTS.md +0 -45
  126. package/CHANGELOG.md +0 -138
  127. package/DEPRECATIONS.md +0 -7
  128. package/biome.json +0 -41
  129. package/convex/_generated/api.d.ts +0 -153
  130. package/convex/_generated/api.js +0 -23
  131. package/convex/_generated/dataModel.d.ts +0 -60
  132. package/convex/_generated/server.d.ts +0 -143
  133. package/convex/_generated/server.js +0 -93
  134. package/convex/auth.config.ts +0 -8
  135. package/convex/auth.ts +0 -19
  136. package/convex/comments.ts +0 -88
  137. package/convex/crons.ts +0 -34
  138. package/convex/devSeed.ts +0 -459
  139. package/convex/devSeedExtra.ts +0 -541
  140. package/convex/downloads.ts +0 -78
  141. package/convex/githubBackups.ts +0 -170
  142. package/convex/githubBackupsNode.ts +0 -183
  143. package/convex/githubImport.ts +0 -317
  144. package/convex/githubSoulBackups.ts +0 -170
  145. package/convex/githubSoulBackupsNode.ts +0 -186
  146. package/convex/http.ts +0 -194
  147. package/convex/httpApi.handlers.test.ts +0 -488
  148. package/convex/httpApi.test.ts +0 -70
  149. package/convex/httpApi.ts +0 -305
  150. package/convex/httpApiV1.handlers.test.ts +0 -584
  151. package/convex/httpApiV1.ts +0 -1172
  152. package/convex/leaderboards.ts +0 -39
  153. package/convex/lib/access.ts +0 -36
  154. package/convex/lib/apiTokenAuth.ts +0 -36
  155. package/convex/lib/badges.ts +0 -50
  156. package/convex/lib/changelog.test.ts +0 -34
  157. package/convex/lib/changelog.ts +0 -278
  158. package/convex/lib/embeddings.ts +0 -38
  159. package/convex/lib/githubBackup.ts +0 -443
  160. package/convex/lib/githubImport.test.ts +0 -247
  161. package/convex/lib/githubImport.ts +0 -425
  162. package/convex/lib/githubSoulBackup.ts +0 -443
  163. package/convex/lib/leaderboards.ts +0 -103
  164. package/convex/lib/moderation.ts +0 -42
  165. package/convex/lib/public.ts +0 -89
  166. package/convex/lib/searchText.test.ts +0 -46
  167. package/convex/lib/searchText.ts +0 -27
  168. package/convex/lib/skillBackfill.test.ts +0 -34
  169. package/convex/lib/skillBackfill.ts +0 -67
  170. package/convex/lib/skillPublish.test.ts +0 -28
  171. package/convex/lib/skillPublish.ts +0 -284
  172. package/convex/lib/skillStats.ts +0 -80
  173. package/convex/lib/skills.test.ts +0 -197
  174. package/convex/lib/skills.ts +0 -273
  175. package/convex/lib/soulChangelog.ts +0 -273
  176. package/convex/lib/soulPublish.ts +0 -236
  177. package/convex/lib/tokens.test.ts +0 -33
  178. package/convex/lib/tokens.ts +0 -51
  179. package/convex/lib/webhooks.test.ts +0 -91
  180. package/convex/lib/webhooks.ts +0 -112
  181. package/convex/maintenance.test.ts +0 -270
  182. package/convex/maintenance.ts +0 -840
  183. package/convex/rateLimits.ts +0 -50
  184. package/convex/schema.ts +0 -472
  185. package/convex/search.test.ts +0 -12
  186. package/convex/search.ts +0 -254
  187. package/convex/seed.test.ts +0 -37
  188. package/convex/seed.ts +0 -254
  189. package/convex/seedSouls.ts +0 -111
  190. package/convex/skillStatEvents.ts +0 -568
  191. package/convex/skills.ts +0 -1606
  192. package/convex/soulComments.ts +0 -88
  193. package/convex/soulDownloads.ts +0 -14
  194. package/convex/soulStars.ts +0 -71
  195. package/convex/souls.ts +0 -570
  196. package/convex/stars.ts +0 -108
  197. package/convex/statsMaintenance.ts +0 -205
  198. package/convex/telemetry.ts +0 -434
  199. package/convex/tokens.ts +0 -88
  200. package/convex/tsconfig.json +0 -7
  201. package/convex/uploads.ts +0 -20
  202. package/convex/users.ts +0 -122
  203. package/convex/webhooks.ts +0 -50
  204. package/convex.json +0 -3
  205. package/docs/README.md +0 -32
  206. package/docs/api.md +0 -51
  207. package/docs/architecture.md +0 -61
  208. package/docs/auth.md +0 -54
  209. package/docs/cli.md +0 -117
  210. package/docs/deploy.md +0 -78
  211. package/docs/diffing.md +0 -84
  212. package/docs/github-import.md +0 -171
  213. package/docs/http-api.md +0 -187
  214. package/docs/manual-testing.md +0 -64
  215. package/docs/mintlify.md +0 -43
  216. package/docs/quickstart.md +0 -120
  217. package/docs/skill-format.md +0 -58
  218. package/docs/soul-format.md +0 -37
  219. package/docs/spec.md +0 -177
  220. package/docs/telemetry.md +0 -91
  221. package/docs/troubleshooting.md +0 -49
  222. package/docs/webhook.md +0 -51
  223. package/e2e/menu-smoke.pw.test.ts +0 -49
  224. package/e2e/pilothub.e2e.test.ts +0 -494
  225. package/e2e/search-exact.pw.test.ts +0 -97
  226. package/packages/pilothub/LICENSE +0 -22
  227. package/packages/pilothub/README.md +0 -57
  228. package/packages/pilothub/package.json +0 -41
  229. package/packages/pilothub/src/browserAuth.test.ts +0 -96
  230. package/packages/pilothub/src/browserAuth.ts +0 -174
  231. package/packages/pilothub/src/cli/buildInfo.ts +0 -94
  232. package/packages/pilothub/src/cli/commands/auth.ts +0 -97
  233. package/packages/pilothub/src/cli/commands/delete.test.ts +0 -73
  234. package/packages/pilothub/src/cli/commands/delete.ts +0 -83
  235. package/packages/pilothub/src/cli/commands/publish.test.ts +0 -122
  236. package/packages/pilothub/src/cli/commands/publish.ts +0 -108
  237. package/packages/pilothub/src/cli/commands/skills.test.ts +0 -191
  238. package/packages/pilothub/src/cli/commands/skills.ts +0 -380
  239. package/packages/pilothub/src/cli/commands/star.ts +0 -46
  240. package/packages/pilothub/src/cli/commands/sync.test.ts +0 -310
  241. package/packages/pilothub/src/cli/commands/sync.ts +0 -200
  242. package/packages/pilothub/src/cli/commands/syncHelpers.test.ts +0 -26
  243. package/packages/pilothub/src/cli/commands/syncHelpers.ts +0 -427
  244. package/packages/pilothub/src/cli/commands/syncTypes.ts +0 -27
  245. package/packages/pilothub/src/cli/commands/unstar.ts +0 -48
  246. package/packages/pilothub/src/cli/helpStyle.ts +0 -45
  247. package/packages/pilothub/src/cli/pilotbotConfig.test.ts +0 -159
  248. package/packages/pilothub/src/cli/pilotbotConfig.ts +0 -147
  249. package/packages/pilothub/src/cli/registry.test.ts +0 -63
  250. package/packages/pilothub/src/cli/registry.ts +0 -43
  251. package/packages/pilothub/src/cli/scanSkills.test.ts +0 -64
  252. package/packages/pilothub/src/cli/scanSkills.ts +0 -84
  253. package/packages/pilothub/src/cli/slug.ts +0 -16
  254. package/packages/pilothub/src/cli/types.ts +0 -12
  255. package/packages/pilothub/src/cli/ui.ts +0 -75
  256. package/packages/pilothub/src/cli.ts +0 -311
  257. package/packages/pilothub/src/config.ts +0 -36
  258. package/packages/pilothub/src/discovery.test.ts +0 -75
  259. package/packages/pilothub/src/discovery.ts +0 -19
  260. package/packages/pilothub/src/http.test.ts +0 -156
  261. package/packages/pilothub/src/http.ts +0 -301
  262. package/packages/pilothub/src/schema/ark.ts +0 -29
  263. package/packages/pilothub/src/schema/index.ts +0 -5
  264. package/packages/pilothub/src/schema/routes.ts +0 -22
  265. package/packages/pilothub/src/schema/schemas.ts +0 -260
  266. package/packages/pilothub/src/schema/textFiles.test.ts +0 -23
  267. package/packages/pilothub/src/schema/textFiles.ts +0 -66
  268. package/packages/pilothub/src/skills.test.ts +0 -191
  269. package/packages/pilothub/src/skills.ts +0 -172
  270. package/packages/pilothub/src/types.ts +0 -10
  271. package/packages/pilothub/tsconfig.json +0 -14
  272. package/packages/schema/README.md +0 -3
  273. package/packages/schema/dist/ark.js.map +0 -1
  274. package/packages/schema/dist/index.js.map +0 -1
  275. package/packages/schema/dist/routes.js.map +0 -1
  276. package/packages/schema/dist/schemas.js.map +0 -1
  277. package/packages/schema/dist/textFiles.js.map +0 -1
  278. package/packages/schema/package.json +0 -26
  279. package/packages/schema/src/ark.ts +0 -29
  280. package/packages/schema/src/index.ts +0 -5
  281. package/packages/schema/src/routes.ts +0 -22
  282. package/packages/schema/src/schemas.test.ts +0 -123
  283. package/packages/schema/src/schemas.ts +0 -287
  284. package/packages/schema/src/textFiles.test.ts +0 -23
  285. package/packages/schema/src/textFiles.ts +0 -66
  286. package/packages/schema/tsconfig.json +0 -15
  287. package/pilothub +0 -46
  288. package/playwright.config.ts +0 -33
  289. package/public/.well-known/pilothub.json +0 -6
  290. package/public/api/v1/openapi.json +0 -379
  291. package/public/favicon.ico +0 -0
  292. package/public/logo192.png +0 -0
  293. package/public/logo512.png +0 -0
  294. package/public/manifest.json +0 -25
  295. package/public/og.png +0 -0
  296. package/public/og.svg +0 -98
  297. package/public/pilot-logo.png +0 -0
  298. package/public/pilot-mark.png +0 -0
  299. package/public/robots.txt +0 -3
  300. package/public/tanstack-circle-logo.png +0 -0
  301. package/public/tanstack-word-logo-white.svg +0 -1
  302. package/scripts/check-peer-deps.ts +0 -56
  303. package/scripts/docs-list.ts +0 -148
  304. package/scripts/run-playwright-local.sh +0 -14
  305. package/server/og/fetchSkillOgMeta.ts +0 -27
  306. package/server/og/fetchSoulOgMeta.ts +0 -27
  307. package/server/og/ogAssets.ts +0 -80
  308. package/server/og/skillOgSvg.test.ts +0 -59
  309. package/server/og/skillOgSvg.ts +0 -258
  310. package/server/og/soulOgSvg.ts +0 -209
  311. package/server/routes/og/skill.png.ts +0 -103
  312. package/server/routes/og/soul.png.ts +0 -111
  313. package/src/__tests__/skill-detail-page.test.tsx +0 -86
  314. package/src/__tests__/skills-index.test.tsx +0 -145
  315. package/src/__tests__/upload.route.test.tsx +0 -228
  316. package/src/components/AppProviders.tsx +0 -19
  317. package/src/components/ClientOnly.tsx +0 -18
  318. package/src/components/Footer.tsx +0 -29
  319. package/src/components/Header.tsx +0 -295
  320. package/src/components/InstallSwitcher.tsx +0 -53
  321. package/src/components/SkillCard.tsx +0 -36
  322. package/src/components/SkillDetailPage.tsx +0 -817
  323. package/src/components/SkillDiffCard.tsx +0 -485
  324. package/src/components/SoulCard.tsx +0 -19
  325. package/src/components/SoulDetailPage.tsx +0 -263
  326. package/src/components/UserBootstrap.tsx +0 -18
  327. package/src/components/ui/dropdown-menu.tsx +0 -67
  328. package/src/components/ui/toggle-group.tsx +0 -35
  329. package/src/convex/client.ts +0 -3
  330. package/src/lib/badges.ts +0 -29
  331. package/src/lib/diffing.test.ts +0 -163
  332. package/src/lib/diffing.ts +0 -106
  333. package/src/lib/gravatar.test.ts +0 -9
  334. package/src/lib/gravatar.ts +0 -158
  335. package/src/lib/og.test.ts +0 -142
  336. package/src/lib/og.ts +0 -156
  337. package/src/lib/publicUser.ts +0 -39
  338. package/src/lib/roles.ts +0 -19
  339. package/src/lib/site.test.ts +0 -130
  340. package/src/lib/site.ts +0 -84
  341. package/src/lib/theme-transition.test.ts +0 -134
  342. package/src/lib/theme-transition.ts +0 -134
  343. package/src/lib/theme.test.tsx +0 -88
  344. package/src/lib/theme.ts +0 -43
  345. package/src/lib/uploadFiles.jsdom.test.ts +0 -33
  346. package/src/lib/uploadFiles.test.ts +0 -123
  347. package/src/lib/uploadFiles.ts +0 -245
  348. package/src/lib/uploadUtils.test.ts +0 -78
  349. package/src/lib/uploadUtils.ts +0 -93
  350. package/src/lib/useAuthStatus.ts +0 -12
  351. package/src/lib/utils.test.ts +0 -9
  352. package/src/lib/utils.ts +0 -6
  353. package/src/logo.svg +0 -12
  354. package/src/routeTree.gen.ts +0 -345
  355. package/src/router.tsx +0 -17
  356. package/src/routes/$owner/$slug.tsx +0 -55
  357. package/src/routes/__root.tsx +0 -136
  358. package/src/routes/admin.tsx +0 -11
  359. package/src/routes/cli/auth.tsx +0 -168
  360. package/src/routes/dashboard.tsx +0 -97
  361. package/src/routes/import.tsx +0 -415
  362. package/src/routes/index.tsx +0 -252
  363. package/src/routes/management.tsx +0 -529
  364. package/src/routes/settings.tsx +0 -203
  365. package/src/routes/skills/index.tsx +0 -422
  366. package/src/routes/souls/$slug.tsx +0 -55
  367. package/src/routes/souls/index.tsx +0 -243
  368. package/src/routes/stars.tsx +0 -68
  369. package/src/routes/u/$handle.tsx +0 -307
  370. package/src/routes/upload/utils.ts +0 -81
  371. package/src/routes/upload.tsx +0 -499
  372. package/src/styles.css +0 -2718
  373. package/tsconfig.json +0 -24
  374. package/tsconfig.oxlint.json +0 -16
  375. package/vercel.json +0 -8
  376. package/vite.config.ts +0 -48
  377. package/vitest.config.ts +0 -47
  378. package/vitest.e2e.config.ts +0 -11
  379. package/vitest.setup.ts +0 -1
  380. /package/{packages/pilothub/bin → bin}/pilothub.js +0 -0
  381. /package/{packages/schema/dist → dist/schema}/ark.d.ts +0 -0
  382. /package/{packages/schema/dist → dist/schema}/ark.js +0 -0
  383. /package/{packages/schema/dist → dist/schema}/index.d.ts +0 -0
  384. /package/{packages/schema/dist → dist/schema}/index.js +0 -0
  385. /package/{packages/schema/dist → dist/schema}/routes.d.ts +0 -0
  386. /package/{packages/schema/dist → dist/schema}/routes.js +0 -0
  387. /package/{packages/schema/dist → dist/schema}/textFiles.d.ts +0 -0
  388. /package/{packages/schema/dist → dist/schema}/textFiles.js +0 -0
package/convex/search.ts DELETED
@@ -1,254 +0,0 @@
1
- import { v } from 'convex/values'
2
- import { internal } from './_generated/api'
3
- import type { Doc, Id } from './_generated/dataModel'
4
- import { action, internalQuery } from './_generated/server'
5
- import { getSkillBadgeMaps, isSkillHighlighted, type SkillBadgeMap } from './lib/badges'
6
- import { generateEmbedding } from './lib/embeddings'
7
- import { toPublicSkill, toPublicSoul } from './lib/public'
8
- import { matchesExactTokens, tokenize } from './lib/searchText'
9
-
10
- type HydratedEntry = {
11
- embeddingId: Id<'skillEmbeddings'>
12
- skill: NonNullable<ReturnType<typeof toPublicSkill>>
13
- version: Doc<'skillVersions'> | null
14
- ownerHandle: string | null
15
- }
16
-
17
- type SearchResult = HydratedEntry & { score: number }
18
-
19
- function getNextCandidateLimit(current: number, max: number) {
20
- const next = Math.min(current * 2, max)
21
- return next > current ? next : null
22
- }
23
-
24
- export const searchSkills: ReturnType<typeof action> = action({
25
- args: {
26
- query: v.string(),
27
- limit: v.optional(v.number()),
28
- highlightedOnly: v.optional(v.boolean()),
29
- },
30
- handler: async (ctx, args): Promise<SearchResult[]> => {
31
- const query = args.query.trim()
32
- if (!query) return []
33
- const queryTokens = tokenize(query)
34
- if (queryTokens.length === 0) return []
35
- let vector: number[]
36
- try {
37
- vector = await generateEmbedding(query)
38
- } catch (error) {
39
- console.warn('Search embedding generation failed', error)
40
- return []
41
- }
42
- const limit = args.limit ?? 10
43
- // Convex vectorSearch max limit is 256; clamp candidate sizes accordingly.
44
- const maxCandidate = Math.min(Math.max(limit * 10, 200), 256)
45
- let candidateLimit = Math.min(Math.max(limit * 3, 50), 256)
46
- let hydrated: HydratedEntry[] = []
47
- let scoreById = new Map<Id<'skillEmbeddings'>, number>()
48
- let exactMatches: HydratedEntry[] = []
49
-
50
- while (candidateLimit <= maxCandidate) {
51
- const results = await ctx.vectorSearch('skillEmbeddings', 'by_embedding', {
52
- vector,
53
- limit: candidateLimit,
54
- filter: (q) => q.or(q.eq('visibility', 'latest'), q.eq('visibility', 'latest-approved')),
55
- })
56
-
57
- hydrated = (await ctx.runQuery(internal.search.hydrateResults, {
58
- embeddingIds: results.map((result) => result._id),
59
- })) as HydratedEntry[]
60
-
61
- scoreById = new Map<Id<'skillEmbeddings'>, number>(
62
- results.map((result) => [result._id, result._score]),
63
- )
64
-
65
- const badgeMapEntries = (await ctx.runQuery(internal.search.getSkillBadgeMapsInternal, {
66
- skillIds: hydrated.map((entry) => entry.skill._id),
67
- })) as Array<[Id<'skills'>, SkillBadgeMap]>
68
- const badgeMapBySkillId = new Map(badgeMapEntries)
69
- const hydratedWithBadges = hydrated.map((entry) => ({
70
- ...entry,
71
- skill: {
72
- ...entry.skill,
73
- badges: badgeMapBySkillId.get(entry.skill._id) ?? {},
74
- },
75
- }))
76
-
77
- const filtered = args.highlightedOnly
78
- ? hydratedWithBadges.filter((entry) => isSkillHighlighted(entry.skill))
79
- : hydratedWithBadges
80
-
81
- exactMatches = filtered.filter((entry) =>
82
- matchesExactTokens(queryTokens, [
83
- entry.skill.displayName,
84
- entry.skill.slug,
85
- entry.skill.summary,
86
- ]),
87
- )
88
-
89
- if (exactMatches.length >= limit || results.length < candidateLimit) {
90
- break
91
- }
92
-
93
- const nextLimit = getNextCandidateLimit(candidateLimit, maxCandidate)
94
- if (!nextLimit) break
95
- candidateLimit = nextLimit
96
- }
97
-
98
- return exactMatches
99
- .map((entry) => ({
100
- ...entry,
101
- score: scoreById.get(entry.embeddingId) ?? 0,
102
- }))
103
- .filter((entry) => entry.skill)
104
- .slice(0, limit)
105
- },
106
- })
107
-
108
- export const getBadgeMapsForSkills = internalQuery({
109
- args: { skillIds: v.array(v.id('skills')) },
110
- handler: async (ctx, args): Promise<Array<[Id<'skills'>, SkillBadgeMap]>> => {
111
- const badgeMap = await getSkillBadgeMaps(ctx, args.skillIds)
112
- return Array.from(badgeMap.entries())
113
- },
114
- })
115
-
116
- export const hydrateResults = internalQuery({
117
- args: { embeddingIds: v.array(v.id('skillEmbeddings')) },
118
- handler: async (ctx, args): Promise<HydratedEntry[]> => {
119
- const ownerHandleCache = new Map<Id<'users'>, Promise<string | null>>()
120
-
121
- const getOwnerHandle = (ownerUserId: Id<'users'>) => {
122
- const cached = ownerHandleCache.get(ownerUserId)
123
- if (cached) return cached
124
- const handlePromise = ctx.db
125
- .get(ownerUserId)
126
- .then((owner) => owner?.handle ?? owner?._id ?? null)
127
- ownerHandleCache.set(ownerUserId, handlePromise)
128
- return handlePromise
129
- }
130
-
131
- const entries = await Promise.all(
132
- args.embeddingIds.map(async (embeddingId) => {
133
- const embedding = await ctx.db.get(embeddingId)
134
- if (!embedding) return null
135
- const skill = await ctx.db.get(embedding.skillId)
136
- if (!skill || skill.softDeletedAt) return null
137
- const [version, ownerHandle] = await Promise.all([
138
- ctx.db.get(embedding.versionId),
139
- getOwnerHandle(skill.ownerUserId),
140
- ])
141
- const publicSkill = toPublicSkill(skill)
142
- if (!publicSkill) return null
143
- return { embeddingId, skill: publicSkill, version, ownerHandle }
144
- }),
145
- )
146
-
147
- return entries.filter((entry): entry is HydratedEntry => entry !== null)
148
- },
149
- })
150
-
151
- type HydratedSoulEntry = {
152
- embeddingId: Id<'soulEmbeddings'>
153
- soul: NonNullable<ReturnType<typeof toPublicSoul>>
154
- version: Doc<'soulVersions'> | null
155
- }
156
-
157
- type SoulSearchResult = HydratedSoulEntry & { score: number }
158
-
159
- export const searchSouls: ReturnType<typeof action> = action({
160
- args: {
161
- query: v.string(),
162
- limit: v.optional(v.number()),
163
- },
164
- handler: async (ctx, args): Promise<SoulSearchResult[]> => {
165
- const query = args.query.trim()
166
- if (!query) return []
167
- const queryTokens = tokenize(query)
168
- if (queryTokens.length === 0) return []
169
- let vector: number[]
170
- try {
171
- vector = await generateEmbedding(query)
172
- } catch (error) {
173
- console.warn('Search embedding generation failed', error)
174
- return []
175
- }
176
- const limit = args.limit ?? 10
177
- // Convex vectorSearch max limit is 256; clamp candidate sizes accordingly.
178
- const maxCandidate = Math.min(Math.max(limit * 10, 200), 256)
179
- let candidateLimit = Math.min(Math.max(limit * 3, 50), 256)
180
- let hydrated: HydratedSoulEntry[] = []
181
- let scoreById = new Map<Id<'soulEmbeddings'>, number>()
182
- let exactMatches: HydratedSoulEntry[] = []
183
-
184
- while (candidateLimit <= maxCandidate) {
185
- const results = await ctx.vectorSearch('soulEmbeddings', 'by_embedding', {
186
- vector,
187
- limit: candidateLimit,
188
- filter: (q) => q.or(q.eq('visibility', 'latest'), q.eq('visibility', 'latest-approved')),
189
- })
190
-
191
- hydrated = (await ctx.runQuery(internal.search.hydrateSoulResults, {
192
- embeddingIds: results.map((result) => result._id),
193
- })) as HydratedSoulEntry[]
194
-
195
- scoreById = new Map<Id<'soulEmbeddings'>, number>(
196
- results.map((result) => [result._id, result._score]),
197
- )
198
-
199
- exactMatches = hydrated.filter((entry) =>
200
- matchesExactTokens(queryTokens, [
201
- entry.soul.displayName,
202
- entry.soul.slug,
203
- entry.soul.summary,
204
- ]),
205
- )
206
-
207
- if (exactMatches.length >= limit || results.length < candidateLimit) {
208
- break
209
- }
210
-
211
- const nextLimit = getNextCandidateLimit(candidateLimit, maxCandidate)
212
- if (!nextLimit) break
213
- candidateLimit = nextLimit
214
- }
215
-
216
- return exactMatches
217
- .map((entry) => ({
218
- ...entry,
219
- score: scoreById.get(entry.embeddingId) ?? 0,
220
- }))
221
- .filter((entry) => entry.soul)
222
- .slice(0, limit)
223
- },
224
- })
225
-
226
- export const hydrateSoulResults = internalQuery({
227
- args: { embeddingIds: v.array(v.id('soulEmbeddings')) },
228
- handler: async (ctx, args): Promise<HydratedSoulEntry[]> => {
229
- const entries: HydratedSoulEntry[] = []
230
-
231
- for (const embeddingId of args.embeddingIds) {
232
- const embedding = await ctx.db.get(embeddingId)
233
- if (!embedding) continue
234
- const soul = await ctx.db.get(embedding.soulId)
235
- if (soul?.softDeletedAt) continue
236
- const version = await ctx.db.get(embedding.versionId)
237
- const publicSoul = toPublicSoul(soul)
238
- if (!publicSoul) continue
239
- entries.push({ embeddingId, soul: publicSoul, version })
240
- }
241
-
242
- return entries
243
- },
244
- })
245
-
246
- export const getSkillBadgeMapsInternal = internalQuery({
247
- args: { skillIds: v.array(v.id('skills')) },
248
- handler: async (ctx, args) => {
249
- const badgeMap = await getSkillBadgeMaps(ctx, args.skillIds)
250
- return Array.from(badgeMap.entries())
251
- },
252
- })
253
-
254
- export const __test = { getNextCandidateLimit }
@@ -1,37 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import type { Doc } from './_generated/dataModel'
3
- import { decideSeedStart } from './seed'
4
-
5
- function seedState(cursor: string, updatedAt: number) {
6
- return { cursor, updatedAt } as unknown as Doc<'githubBackupSyncState'>
7
- }
8
-
9
- describe('decideSeedStart', () => {
10
- it('returns done when done', () => {
11
- expect(decideSeedStart(seedState('done', Date.now()), Date.now())).toEqual({
12
- started: false,
13
- reason: 'done',
14
- })
15
- })
16
-
17
- it('returns running when lock fresh', () => {
18
- const now = Date.now()
19
- expect(decideSeedStart(seedState('running', now), now + 1000)).toEqual({
20
- started: false,
21
- reason: 'running',
22
- })
23
- })
24
-
25
- it('starts when lock stale', () => {
26
- const now = Date.now()
27
- const stale = now - 10 * 60 * 1000 - 1
28
- expect(decideSeedStart(seedState('running', stale), now)).toEqual({
29
- started: true,
30
- reason: 'patched',
31
- })
32
- })
33
-
34
- it('starts when missing', () => {
35
- expect(decideSeedStart(null, Date.now())).toEqual({ started: true, reason: 'inserted' })
36
- })
37
- })
package/convex/seed.ts DELETED
@@ -1,254 +0,0 @@
1
- import { v } from 'convex/values'
2
- import { internal } from './_generated/api'
3
- import type { Doc, Id } from './_generated/dataModel'
4
- import type { ActionCtx, DatabaseReader, DatabaseWriter } from './_generated/server'
5
- import { action, internalMutation, internalQuery } from './_generated/server'
6
- import { publishSoulVersionForUser } from './lib/soulPublish'
7
- import { SOUL_SEED_DISPLAY_NAME, SOUL_SEED_HANDLE, SOUL_SEED_KEY, SOUL_SEEDS } from './seedSouls'
8
-
9
- const SEED_LOCK_STALE_MS = 10 * 60 * 1000
10
-
11
- type SeedStateDoc = Doc<'githubBackupSyncState'>
12
-
13
- type SeedStartDecision = {
14
- started: boolean
15
- reason: 'done' | 'running' | 'patched' | 'inserted'
16
- }
17
-
18
- async function getSeedState(ctx: { db: DatabaseReader }): Promise<SeedStateDoc | null> {
19
- const entries = (await ctx.db
20
- .query('githubBackupSyncState')
21
- .withIndex('by_key', (q) => q.eq('key', SOUL_SEED_KEY))
22
- .order('desc')
23
- .take(2)) as SeedStateDoc[]
24
- return entries[0] ?? null
25
- }
26
-
27
- async function cleanupSeedState(ctx: { db: DatabaseWriter }, keepId: Id<'githubBackupSyncState'>) {
28
- const entries = (await ctx.db
29
- .query('githubBackupSyncState')
30
- .withIndex('by_key', (q) => q.eq('key', SOUL_SEED_KEY))
31
- .order('desc')
32
- .take(50)) as SeedStateDoc[]
33
-
34
- for (const entry of entries) {
35
- if (entry._id === keepId) continue
36
- await ctx.db.delete(entry._id)
37
- }
38
- }
39
-
40
- export function decideSeedStart(existing: SeedStateDoc | null, now: number): SeedStartDecision {
41
- const cursor = existing?.cursor ?? null
42
- if (cursor === 'done') return { started: false, reason: 'done' }
43
- if (cursor === 'running' && existing && now - existing.updatedAt < SEED_LOCK_STALE_MS) {
44
- return { started: false, reason: 'running' }
45
- }
46
- return existing ? { started: true, reason: 'patched' } : { started: true, reason: 'inserted' }
47
- }
48
-
49
- export const getSoulSeedStateInternal = internalQuery({
50
- args: {},
51
- handler: async (ctx) => getSeedState(ctx),
52
- })
53
-
54
- export const setSoulSeedStateInternal = internalMutation({
55
- args: { status: v.string() },
56
- handler: async (ctx, args) => {
57
- const existing = await getSeedState(ctx)
58
- const now = Date.now()
59
- if (existing) {
60
- await ctx.db.patch(existing._id, { cursor: args.status, updatedAt: now })
61
- await cleanupSeedState(ctx, existing._id)
62
- return existing._id
63
- }
64
- const id = await ctx.db.insert('githubBackupSyncState', {
65
- key: SOUL_SEED_KEY,
66
- cursor: args.status,
67
- updatedAt: now,
68
- })
69
- await cleanupSeedState(ctx, id)
70
- return id
71
- },
72
- })
73
-
74
- export const tryStartSoulSeedInternal = internalMutation({
75
- args: {},
76
- handler: async (ctx) => {
77
- const now = Date.now()
78
- const existing = await getSeedState(ctx)
79
- const decision = decideSeedStart(existing, now)
80
-
81
- if (!decision.started) return decision
82
-
83
- if (existing) {
84
- await ctx.db.patch(existing._id, { cursor: 'running', updatedAt: now })
85
- await cleanupSeedState(ctx, existing._id)
86
- return { started: true, reason: 'patched' as const }
87
- }
88
-
89
- const id = await ctx.db.insert('githubBackupSyncState', {
90
- key: SOUL_SEED_KEY,
91
- cursor: 'running',
92
- updatedAt: now,
93
- })
94
- await cleanupSeedState(ctx, id)
95
- return { started: true, reason: 'inserted' as const }
96
- },
97
- })
98
-
99
- export const hasAnySoulsInternal = internalQuery({
100
- args: {},
101
- handler: async (ctx) => {
102
- const entry = await ctx.db.query('souls').take(1)
103
- return entry.length > 0
104
- },
105
- })
106
-
107
- export const ensureSoulSeeds = action({
108
- args: {},
109
- handler: async (ctx) => {
110
- const started = (await ctx.runMutation(internal.seed.tryStartSoulSeedInternal, {})) as {
111
- started: boolean
112
- reason: 'done' | 'running' | 'patched' | 'inserted'
113
- }
114
- if (!started.started) {
115
- if (started.reason === 'done') return { seeded: false, reason: 'already-seeded' as const }
116
- return { seeded: false, reason: 'in-progress' as const }
117
- }
118
-
119
- const hasSouls = (await ctx.runQuery(internal.seed.hasAnySoulsInternal, {})) as boolean
120
- if (hasSouls) {
121
- await ctx.runMutation(internal.seed.setSoulSeedStateInternal, { status: 'done' })
122
- return { seeded: false, reason: 'souls-exist' as const }
123
- }
124
-
125
- try {
126
- const result = await runSeed(ctx)
127
- await ctx.runMutation(internal.seed.setSoulSeedStateInternal, { status: 'done' })
128
- return { seeded: true, reason: 'seeded' as const, ...result }
129
- } catch (error) {
130
- await ctx.runMutation(internal.seed.setSoulSeedStateInternal, { status: 'error' })
131
- throw error
132
- }
133
- },
134
- })
135
-
136
- export const seed = action({
137
- args: {},
138
- handler: async (ctx) => runSeed(ctx),
139
- })
140
-
141
- async function runSeed(ctx: ActionCtx) {
142
- const userId = (await ctx.runMutation(internal.seed.ensureSeedUserInternal, {
143
- handle: SOUL_SEED_HANDLE,
144
- displayName: SOUL_SEED_DISPLAY_NAME,
145
- })) as Id<'users'>
146
-
147
- const created: string[] = []
148
- const skipped: string[] = []
149
-
150
- for (const seedEntry of SOUL_SEEDS) {
151
- const existing = (await ctx.runQuery(internal.souls.getSoulBySlugInternal, {
152
- slug: seedEntry.slug,
153
- })) as Doc<'souls'> | null
154
- if (existing) {
155
- if (existing.softDeletedAt && existing.ownerUserId === userId) {
156
- await ctx.runMutation(internal.souls.setSoulSoftDeletedInternal, {
157
- userId,
158
- slug: seedEntry.slug,
159
- deleted: false,
160
- })
161
- }
162
- skipped.push(seedEntry.slug)
163
- continue
164
- }
165
-
166
- const body = seedEntry.readme
167
- if (!body) {
168
- skipped.push(seedEntry.slug)
169
- continue
170
- }
171
-
172
- const bytes = new TextEncoder().encode(body)
173
- const sha256 = await sha256Hex(bytes)
174
- const storageId = await ctx.storage.store(new Blob([bytes], { type: 'text/markdown' }))
175
-
176
- try {
177
- await publishSoulVersionForUser(ctx, userId, {
178
- slug: seedEntry.slug,
179
- displayName: seedEntry.displayName,
180
- version: seedEntry.version,
181
- changelog: '',
182
- tags: seedEntry.tags,
183
- files: [
184
- {
185
- path: 'SOUL.md',
186
- size: bytes.byteLength,
187
- storageId,
188
- sha256,
189
- contentType: 'text/markdown',
190
- },
191
- ],
192
- })
193
- created.push(seedEntry.slug)
194
- } catch (error) {
195
- if (!isExpectedSeedSkipError(error)) throw error
196
- skipped.push(seedEntry.slug)
197
- }
198
- }
199
-
200
- return { created, skipped }
201
- }
202
-
203
- function isExpectedSeedSkipError(error: unknown) {
204
- const message = error instanceof Error ? error.message : String(error)
205
- return (
206
- message.includes('Version already exists') || message.includes('Only the owner can publish')
207
- )
208
- }
209
-
210
- export const ensureSeedUserInternal = internalMutation({
211
- args: {
212
- handle: v.string(),
213
- displayName: v.string(),
214
- },
215
- handler: async (ctx, args) => {
216
- const baseHandle = args.handle.trim()
217
- const displayName = args.displayName.trim()
218
- const candidates = [baseHandle, `${baseHandle}-bot`]
219
- for (let i = 2; i <= 6; i += 1) candidates.push(`${baseHandle}-bot-${i}`)
220
-
221
- for (const candidate of candidates) {
222
- const existing = await ctx.db
223
- .query('users')
224
- .withIndex('handle', (q) => q.eq('handle', candidate))
225
- .take(2)
226
- const user = (existing[0] ?? null) as Doc<'users'> | null
227
- if (user) {
228
- if ((user.displayName ?? user.name) === displayName) return user._id
229
- continue
230
- }
231
-
232
- return ctx.db.insert('users', {
233
- handle: candidate,
234
- displayName,
235
- createdAt: Date.now(),
236
- updatedAt: Date.now(),
237
- })
238
- }
239
-
240
- throw new Error('Unable to allocate seed user handle')
241
- },
242
- })
243
-
244
- async function sha256Hex(bytes: Uint8Array) {
245
- const data = new Uint8Array(bytes)
246
- const digest = await crypto.subtle.digest('SHA-256', data)
247
- return toHex(new Uint8Array(digest))
248
- }
249
-
250
- function toHex(bytes: Uint8Array) {
251
- let out = ''
252
- for (const byte of bytes) out += byte.toString(16).padStart(2, '0')
253
- return out
254
- }