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
@@ -1,205 +0,0 @@
1
- import { v } from 'convex/values'
2
- import { internal } from './_generated/api'
3
- import type { Doc } from './_generated/dataModel'
4
- import type { ActionCtx } from './_generated/server'
5
- import { internalAction, internalMutation, internalQuery } from './_generated/server'
6
-
7
- const DEFAULT_BATCH_SIZE = 200
8
- const MAX_BATCH_SIZE = 1000
9
- const DEFAULT_MAX_BATCHES = 5
10
- const MAX_MAX_BATCHES = 50
11
- const BACKFILL_STATE_KEY = 'default'
12
-
13
- export const backfillSkillStatFieldsInternal = internalMutation({
14
- args: {
15
- cursor: v.optional(v.string()),
16
- batchSize: v.optional(v.number()),
17
- },
18
- handler: async (ctx, args) => {
19
- const batchSize = clampInt(args.batchSize ?? DEFAULT_BATCH_SIZE, 1, MAX_BATCH_SIZE)
20
- const { page, isDone, continueCursor } = await ctx.db
21
- .query('skills')
22
- .order('asc')
23
- .paginate({ cursor: args.cursor ?? null, numItems: batchSize })
24
-
25
- let patched = 0
26
- for (const skill of page) {
27
- const next = buildSkillStatPatch(skill)
28
- if (!next) continue
29
- await ctx.db.patch(skill._id, next)
30
- patched += 1
31
- }
32
-
33
- return {
34
- ok: true as const,
35
- scanned: page.length,
36
- patched,
37
- cursor: isDone ? null : continueCursor,
38
- isDone,
39
- }
40
- },
41
- })
42
-
43
- type BackfillState = {
44
- cursor: string | null
45
- doneAt?: number
46
- }
47
-
48
- type BackfillActionArgs = {
49
- batchSize?: number
50
- maxBatches?: number
51
- resetCursor?: boolean
52
- }
53
-
54
- type BackfillStats = {
55
- scanned: number
56
- patched: number
57
- batches: number
58
- }
59
-
60
- type BackfillActionResult = {
61
- ok: true
62
- isDone: boolean
63
- cursor: string | null
64
- stats: BackfillStats
65
- }
66
-
67
- export const getSkillStatBackfillStateInternal = internalQuery({
68
- args: {},
69
- handler: async (ctx): Promise<BackfillState> => {
70
- const state = await ctx.db
71
- .query('skillStatBackfillState')
72
- .withIndex('by_key', (q) => q.eq('key', BACKFILL_STATE_KEY))
73
- .unique()
74
- return { cursor: state?.cursor ?? null, doneAt: state?.doneAt }
75
- },
76
- })
77
-
78
- export const setSkillStatBackfillStateInternal = internalMutation({
79
- args: {
80
- cursor: v.optional(v.string()),
81
- doneAt: v.optional(v.number()),
82
- },
83
- handler: async (ctx, args) => {
84
- const now = Date.now()
85
- const state = await ctx.db
86
- .query('skillStatBackfillState')
87
- .withIndex('by_key', (q) => q.eq('key', BACKFILL_STATE_KEY))
88
- .unique()
89
-
90
- if (!state) {
91
- await ctx.db.insert('skillStatBackfillState', {
92
- key: BACKFILL_STATE_KEY,
93
- cursor: args.cursor,
94
- doneAt: args.doneAt,
95
- updatedAt: now,
96
- })
97
- return { ok: true as const }
98
- }
99
-
100
- await ctx.db.patch(state._id, {
101
- cursor: args.cursor,
102
- doneAt: args.doneAt,
103
- updatedAt: now,
104
- })
105
-
106
- return { ok: true as const }
107
- },
108
- })
109
-
110
- async function runSkillStatBackfillInternalHandler(
111
- ctx: ActionCtx,
112
- args: BackfillActionArgs,
113
- ): Promise<BackfillActionResult> {
114
- const batchSize = clampInt(args.batchSize ?? DEFAULT_BATCH_SIZE, 1, MAX_BATCH_SIZE)
115
- const maxBatches = clampInt(args.maxBatches ?? DEFAULT_MAX_BATCHES, 1, MAX_MAX_BATCHES)
116
-
117
- if (args.resetCursor) {
118
- await ctx.runMutation(internal.statsMaintenance.setSkillStatBackfillStateInternal, {
119
- cursor: undefined,
120
- doneAt: undefined,
121
- })
122
- }
123
-
124
- const state = (await ctx.runQuery(
125
- internal.statsMaintenance.getSkillStatBackfillStateInternal,
126
- {},
127
- )) as BackfillState
128
- if (state.doneAt && !args.resetCursor) {
129
- return {
130
- ok: true,
131
- isDone: true,
132
- cursor: null,
133
- stats: { scanned: 0, patched: 0, batches: 0 },
134
- }
135
- }
136
-
137
- let cursor: string | null = state.cursor ?? null
138
- const stats: BackfillStats = { scanned: 0, patched: 0, batches: 0 }
139
-
140
- for (let i = 0; i < maxBatches; i += 1) {
141
- const result = (await ctx.runMutation(
142
- internal.statsMaintenance.backfillSkillStatFieldsInternal,
143
- {
144
- cursor: cursor ?? undefined,
145
- batchSize,
146
- },
147
- )) as { scanned: number; patched: number; cursor: string | null; isDone: boolean }
148
- stats.scanned += result.scanned
149
- stats.patched += result.patched
150
- stats.batches += 1
151
- cursor = result.cursor
152
-
153
- if (result.isDone) {
154
- await ctx.runMutation(internal.statsMaintenance.setSkillStatBackfillStateInternal, {
155
- cursor: undefined,
156
- doneAt: Date.now(),
157
- })
158
- return { ok: true, isDone: true, cursor: null, stats }
159
- }
160
-
161
- await ctx.runMutation(internal.statsMaintenance.setSkillStatBackfillStateInternal, {
162
- cursor: cursor ?? undefined,
163
- doneAt: undefined,
164
- })
165
- }
166
-
167
- return { ok: true, isDone: false, cursor, stats }
168
- }
169
-
170
- export const runSkillStatBackfillInternal: ReturnType<typeof internalAction> = internalAction({
171
- args: {
172
- batchSize: v.optional(v.number()),
173
- maxBatches: v.optional(v.number()),
174
- resetCursor: v.optional(v.boolean()),
175
- },
176
- handler: runSkillStatBackfillInternalHandler,
177
- })
178
-
179
- function buildSkillStatPatch(skill: Doc<'skills'>) {
180
- const stats = skill.stats
181
- const nextDownloads = stats.downloads
182
- const nextStars = stats.stars
183
- const nextInstallsCurrent = stats.installsCurrent ?? 0
184
- const nextInstallsAllTime = stats.installsAllTime ?? 0
185
-
186
- if (
187
- skill.statsDownloads === nextDownloads &&
188
- skill.statsStars === nextStars &&
189
- skill.statsInstallsCurrent === nextInstallsCurrent &&
190
- skill.statsInstallsAllTime === nextInstallsAllTime
191
- ) {
192
- return null
193
- }
194
-
195
- return {
196
- statsDownloads: nextDownloads,
197
- statsStars: nextStars,
198
- statsInstallsCurrent: nextInstallsCurrent,
199
- statsInstallsAllTime: nextInstallsAllTime,
200
- }
201
- }
202
-
203
- function clampInt(value: number, min: number, max: number) {
204
- return Math.min(Math.max(value, min), max)
205
- }
@@ -1,434 +0,0 @@
1
- import { getAuthUserId } from '@convex-dev/auth/server'
2
- import { v } from 'convex/values'
3
- import type { Id } from './_generated/dataModel'
4
- import type { MutationCtx, QueryCtx } from './_generated/server'
5
- import { internalMutation, mutation, query } from './_generated/server'
6
- import { requireUser } from './lib/access'
7
- import { insertStatEvent } from './skillStatEvents'
8
-
9
- const TELEMETRY_STALE_MS = 120 * 24 * 60 * 60 * 1000
10
-
11
- type RootPayload = {
12
- rootId: string
13
- label: string
14
- skills: Array<{ slug: string; version?: string | null }>
15
- }
16
-
17
- export const reportCliSyncInternal = internalMutation({
18
- args: {
19
- userId: v.id('users'),
20
- roots: v.array(
21
- v.object({
22
- rootId: v.string(),
23
- label: v.string(),
24
- skills: v.array(
25
- v.object({
26
- slug: v.string(),
27
- version: v.optional(v.string()),
28
- }),
29
- ),
30
- }),
31
- ),
32
- },
33
- handler: async (ctx, args) => {
34
- const now = Date.now()
35
- const stalenessCutoff = now - TELEMETRY_STALE_MS
36
-
37
- await expireStaleRoots(ctx, { userId: args.userId, stalenessCutoff, now })
38
-
39
- const roots = normalizeRoots(args.roots)
40
- const skillsBySlug = await resolveSkillsBySlug(ctx, roots)
41
-
42
- for (const root of roots) {
43
- await upsertRoot(ctx, { userId: args.userId, rootId: root.rootId, now, label: root.label })
44
- await applyRootReport(ctx, {
45
- userId: args.userId,
46
- root,
47
- skillsBySlug,
48
- now,
49
- })
50
- }
51
- },
52
- })
53
-
54
- export const clearMyTelemetry = mutation({
55
- args: {},
56
- handler: async (ctx) => {
57
- const { userId } = await requireUser(ctx)
58
- await clearTelemetryForUser(ctx, { userId })
59
- },
60
- })
61
-
62
- export const clearUserTelemetryInternal = internalMutation({
63
- args: { userId: v.id('users') },
64
- handler: async (ctx, args) => {
65
- await clearTelemetryForUser(ctx, { userId: args.userId })
66
- },
67
- })
68
-
69
- export const getMyInstalled = query({
70
- args: {
71
- includeRemoved: v.optional(v.boolean()),
72
- },
73
- handler: async (ctx, args) => {
74
- const userId = await getAuthUserId(ctx)
75
- if (!userId) return null
76
-
77
- const roots = await ctx.db
78
- .query('userSyncRoots')
79
- .withIndex('by_user', (q) => q.eq('userId', userId))
80
- .order('desc')
81
- .take(200)
82
-
83
- const includeRemoved = Boolean(args.includeRemoved)
84
- const resultRoots: Array<{
85
- rootId: string
86
- label: string
87
- firstSeenAt: number
88
- lastSeenAt: number
89
- expiredAt?: number
90
- skills: Array<{
91
- skill: {
92
- slug: string
93
- displayName: string
94
- summary?: string
95
- stats: unknown
96
- ownerUserId: Id<'users'>
97
- }
98
- firstSeenAt: number
99
- lastSeenAt: number
100
- lastVersion?: string
101
- removedAt?: number
102
- }>
103
- }> = []
104
-
105
- for (const root of roots) {
106
- const installs = await ctx.db
107
- .query('userSkillRootInstalls')
108
- .withIndex('by_user_root', (q) => q.eq('userId', userId).eq('rootId', root.rootId))
109
- .order('desc')
110
- .take(2000)
111
-
112
- const filtered = includeRemoved ? installs : installs.filter((entry) => !entry.removedAt)
113
- const skills: Array<{
114
- skill: {
115
- slug: string
116
- displayName: string
117
- summary?: string
118
- stats: unknown
119
- ownerUserId: Id<'users'>
120
- }
121
- firstSeenAt: number
122
- lastSeenAt: number
123
- lastVersion?: string
124
- removedAt?: number
125
- }> = []
126
-
127
- for (const entry of filtered) {
128
- const skill = await ctx.db.get(entry.skillId)
129
- if (!skill) continue
130
- skills.push({
131
- skill: {
132
- slug: skill.slug,
133
- displayName: skill.displayName,
134
- summary: skill.summary,
135
- stats: skill.stats,
136
- ownerUserId: skill.ownerUserId,
137
- },
138
- firstSeenAt: entry.firstSeenAt,
139
- lastSeenAt: entry.lastSeenAt,
140
- lastVersion: entry.lastVersion,
141
- removedAt: entry.removedAt,
142
- })
143
- }
144
-
145
- resultRoots.push({
146
- rootId: root.rootId,
147
- label: root.label,
148
- firstSeenAt: root.firstSeenAt,
149
- lastSeenAt: root.lastSeenAt,
150
- expiredAt: root.expiredAt,
151
- skills,
152
- })
153
- }
154
-
155
- return {
156
- roots: resultRoots,
157
- cutoffDays: 120,
158
- }
159
- },
160
- })
161
-
162
- async function clearTelemetryForUser(ctx: MutationCtx, params: { userId: Id<'users'> }) {
163
- const installs = await ctx.db
164
- .query('userSkillInstalls')
165
- .withIndex('by_user', (q) => q.eq('userId', params.userId))
166
- .take(5000)
167
-
168
- for (const entry of installs) {
169
- const skill = await ctx.db.get(entry.skillId)
170
- if (!skill) {
171
- await ctx.db.delete(entry._id)
172
- continue
173
- }
174
- await insertStatEvent(ctx, {
175
- skillId: skill._id,
176
- kind: 'install_clear',
177
- delta: {
178
- allTime: -1,
179
- current: entry.activeRoots > 0 ? -1 : 0,
180
- },
181
- })
182
- await ctx.db.delete(entry._id)
183
- }
184
-
185
- const roots = await ctx.db
186
- .query('userSyncRoots')
187
- .withIndex('by_user', (q) => q.eq('userId', params.userId))
188
- .take(5000)
189
- for (const root of roots) {
190
- await ctx.db.delete(root._id)
191
- }
192
-
193
- const rootInstalls = await ctx.db
194
- .query('userSkillRootInstalls')
195
- .withIndex('by_user', (q) => q.eq('userId', params.userId))
196
- .take(10000)
197
- for (const entry of rootInstalls) {
198
- await ctx.db.delete(entry._id)
199
- }
200
- }
201
-
202
- function normalizeRoots(roots: RootPayload[]): RootPayload[] {
203
- const seen = new Set<string>()
204
- const unique: RootPayload[] = []
205
- for (const root of roots) {
206
- const id = root.rootId.trim()
207
- if (!id) continue
208
- if (seen.has(id)) continue
209
- seen.add(id)
210
- unique.push({
211
- rootId: id,
212
- label: root.label.trim() || 'Unknown',
213
- skills: root.skills
214
- .map((skill) => ({
215
- slug: skill.slug.trim().toLowerCase(),
216
- version: skill.version ?? null,
217
- }))
218
- .filter((skill) => Boolean(skill.slug)),
219
- })
220
- }
221
- return unique
222
- }
223
-
224
- async function upsertRoot(
225
- ctx: MutationCtx,
226
- params: { userId: Id<'users'>; rootId: string; now: number; label: string },
227
- ) {
228
- const existing = await ctx.db
229
- .query('userSyncRoots')
230
- .withIndex('by_user_root', (q) => q.eq('userId', params.userId).eq('rootId', params.rootId))
231
- .unique()
232
- if (existing) {
233
- await ctx.db.patch(existing._id, {
234
- label: params.label,
235
- lastSeenAt: params.now,
236
- expiredAt: undefined,
237
- })
238
- return
239
- }
240
- await ctx.db.insert('userSyncRoots', {
241
- userId: params.userId,
242
- rootId: params.rootId,
243
- label: params.label,
244
- firstSeenAt: params.now,
245
- lastSeenAt: params.now,
246
- expiredAt: undefined,
247
- })
248
- }
249
-
250
- async function applyRootReport(
251
- ctx: MutationCtx,
252
- params: {
253
- userId: Id<'users'>
254
- root: RootPayload
255
- skillsBySlug: Map<string, { skillId: Id<'skills'> }>
256
- now: number
257
- },
258
- ) {
259
- const expected = new Set<Id<'skills'>>()
260
- const versionsBySkill = new Map<Id<'skills'>, string | undefined>()
261
- for (const entry of params.root.skills) {
262
- const resolved = params.skillsBySlug.get(entry.slug)
263
- if (!resolved) continue
264
- expected.add(resolved.skillId)
265
- const version = entry.version?.trim() || undefined
266
- if (version) versionsBySkill.set(resolved.skillId, version)
267
- }
268
-
269
- const previous = await ctx.db
270
- .query('userSkillRootInstalls')
271
- .withIndex('by_user_root', (q) =>
272
- q.eq('userId', params.userId).eq('rootId', params.root.rootId),
273
- )
274
- .take(5000)
275
-
276
- const active = previous.filter((entry) => !entry.removedAt)
277
-
278
- for (const skillId of expected) {
279
- const existing = await ctx.db
280
- .query('userSkillRootInstalls')
281
- .withIndex('by_user_root_skill', (q) =>
282
- q.eq('userId', params.userId).eq('rootId', params.root.rootId).eq('skillId', skillId),
283
- )
284
- .unique()
285
-
286
- const reportedVersion = versionsBySkill.get(skillId)
287
-
288
- if (existing) {
289
- const wasRemoved = Boolean(existing.removedAt)
290
- await ctx.db.patch(existing._id, {
291
- lastSeenAt: params.now,
292
- lastVersion: reportedVersion ?? existing.lastVersion,
293
- removedAt: undefined,
294
- })
295
- if (wasRemoved) {
296
- await incrementActiveRoots(ctx, {
297
- userId: params.userId,
298
- skillId,
299
- now: params.now,
300
- version: reportedVersion,
301
- })
302
- }
303
- continue
304
- }
305
-
306
- await ctx.db.insert('userSkillRootInstalls', {
307
- userId: params.userId,
308
- rootId: params.root.rootId,
309
- skillId,
310
- firstSeenAt: params.now,
311
- lastSeenAt: params.now,
312
- lastVersion: reportedVersion,
313
- })
314
- await incrementActiveRoots(ctx, {
315
- userId: params.userId,
316
- skillId,
317
- now: params.now,
318
- version: reportedVersion,
319
- })
320
- }
321
-
322
- for (const entry of active) {
323
- if (expected.has(entry.skillId)) continue
324
- await ctx.db.patch(entry._id, { removedAt: params.now })
325
- await decrementActiveRoots(ctx, { userId: params.userId, skillId: entry.skillId })
326
- }
327
- }
328
-
329
- async function incrementActiveRoots(
330
- ctx: MutationCtx,
331
- params: { userId: Id<'users'>; skillId: Id<'skills'>; now: number; version?: string },
332
- ) {
333
- const existing = await ctx.db
334
- .query('userSkillInstalls')
335
- .withIndex('by_user_skill', (q) => q.eq('userId', params.userId).eq('skillId', params.skillId))
336
- .unique()
337
-
338
- if (!existing) {
339
- await ctx.db.insert('userSkillInstalls', {
340
- userId: params.userId,
341
- skillId: params.skillId,
342
- firstSeenAt: params.now,
343
- lastSeenAt: params.now,
344
- activeRoots: 1,
345
- lastVersion: params.version,
346
- })
347
- await bumpSkillInstallCounts(ctx, { skillId: params.skillId, deltaAllTime: 1, deltaCurrent: 1 })
348
- return
349
- }
350
-
351
- const nextActive = Math.max(0, (existing.activeRoots ?? 0) + 1)
352
- await ctx.db.patch(existing._id, {
353
- activeRoots: nextActive,
354
- lastSeenAt: params.now,
355
- lastVersion: params.version ?? existing.lastVersion,
356
- })
357
- if ((existing.activeRoots ?? 0) === 0 && nextActive > 0) {
358
- await bumpSkillInstallCounts(ctx, { skillId: params.skillId, deltaAllTime: 0, deltaCurrent: 1 })
359
- }
360
- }
361
-
362
- async function decrementActiveRoots(
363
- ctx: MutationCtx,
364
- params: { userId: Id<'users'>; skillId: Id<'skills'> },
365
- ) {
366
- const existing = await ctx.db
367
- .query('userSkillInstalls')
368
- .withIndex('by_user_skill', (q) => q.eq('userId', params.userId).eq('skillId', params.skillId))
369
- .unique()
370
- if (!existing) return
371
-
372
- const nextActive = Math.max(0, (existing.activeRoots ?? 0) - 1)
373
- await ctx.db.patch(existing._id, { activeRoots: nextActive })
374
- if ((existing.activeRoots ?? 0) > 0 && nextActive === 0) {
375
- await bumpSkillInstallCounts(ctx, {
376
- skillId: params.skillId,
377
- deltaAllTime: 0,
378
- deltaCurrent: -1,
379
- })
380
- }
381
- }
382
-
383
- async function bumpSkillInstallCounts(
384
- ctx: MutationCtx,
385
- params: { skillId: Id<'skills'>; deltaAllTime: number; deltaCurrent: number },
386
- ) {
387
- if (params.deltaAllTime === 1 && params.deltaCurrent === 1) {
388
- await insertStatEvent(ctx, { skillId: params.skillId, kind: 'install_new' })
389
- } else if (params.deltaAllTime === 0 && params.deltaCurrent === 1) {
390
- await insertStatEvent(ctx, { skillId: params.skillId, kind: 'install_reactivate' })
391
- } else if (params.deltaAllTime === 0 && params.deltaCurrent === -1) {
392
- await insertStatEvent(ctx, { skillId: params.skillId, kind: 'install_deactivate' })
393
- }
394
- }
395
-
396
- async function expireStaleRoots(
397
- ctx: MutationCtx,
398
- params: { userId: Id<'users'>; stalenessCutoff: number; now: number },
399
- ) {
400
- const roots = await ctx.db
401
- .query('userSyncRoots')
402
- .withIndex('by_user', (q) => q.eq('userId', params.userId))
403
- .take(5000)
404
-
405
- const stale = roots.filter((root) => !root.expiredAt && root.lastSeenAt < params.stalenessCutoff)
406
- for (const root of stale) {
407
- await ctx.db.patch(root._id, { expiredAt: params.now })
408
- const installs = await ctx.db
409
- .query('userSkillRootInstalls')
410
- .withIndex('by_user_root', (q) => q.eq('userId', params.userId).eq('rootId', root.rootId))
411
- .take(5000)
412
- for (const entry of installs) {
413
- if (entry.removedAt) continue
414
- await ctx.db.patch(entry._id, { removedAt: params.now })
415
- await decrementActiveRoots(ctx, { userId: params.userId, skillId: entry.skillId })
416
- }
417
- }
418
- }
419
-
420
- async function resolveSkillsBySlug(ctx: QueryCtx | MutationCtx, roots: RootPayload[]) {
421
- const slugs = new Set<string>()
422
- for (const root of roots) {
423
- for (const entry of root.skills) slugs.add(entry.slug)
424
- }
425
- const map = new Map<string, { skillId: Id<'skills'> }>()
426
- for (const slug of slugs) {
427
- const skill = await ctx.db
428
- .query('skills')
429
- .withIndex('by_slug', (q) => q.eq('slug', slug))
430
- .unique()
431
- if (skill && !skill.softDeletedAt) map.set(slug, { skillId: skill._id })
432
- }
433
- return map
434
- }