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,168 +0,0 @@
1
- import { useAuthActions } from '@convex-dev/auth/react'
2
- import { createFileRoute } from '@tanstack/react-router'
3
- import { useMutation } from 'convex/react'
4
- import { useEffect, useMemo, useRef, useState } from 'react'
5
- import { api } from '../../../convex/_generated/api'
6
- import { useAuthStatus } from '../../lib/useAuthStatus'
7
-
8
- export const Route = createFileRoute('/cli/auth')({
9
- component: CliAuth,
10
- })
11
-
12
- function CliAuth() {
13
- const { isAuthenticated, isLoading, me } = useAuthStatus()
14
- const { signIn } = useAuthActions()
15
- const createToken = useMutation(api.tokens.create)
16
-
17
- const search = Route.useSearch() as {
18
- redirect_uri?: string
19
- label?: string
20
- label_b64?: string
21
- state?: string
22
- }
23
- const [status, setStatus] = useState<string>('Preparing…')
24
- const [token, setToken] = useState<string | null>(null)
25
- const hasRun = useRef(false)
26
-
27
- const redirectUri = search.redirect_uri ?? ''
28
- const label = (decodeLabel(search.label_b64) ?? search.label ?? 'CLI token').trim() || 'CLI token'
29
- const state = typeof search.state === 'string' ? search.state.trim() : ''
30
-
31
- const safeRedirect = useMemo(() => isAllowedRedirectUri(redirectUri), [redirectUri])
32
- const registry = import.meta.env.VITE_CONVEX_SITE_URL as string | undefined
33
-
34
- useEffect(() => {
35
- if (hasRun.current) return
36
- if (!safeRedirect) return
37
- if (!state) return
38
- if (!registry) return
39
- if (!isAuthenticated || !me) return
40
- hasRun.current = true
41
-
42
- const run = async () => {
43
- setStatus('Creating token…')
44
- const result = await createToken({ label })
45
- setToken(result.token)
46
- setStatus('Redirecting to CLI…')
47
- const hash = new URLSearchParams()
48
- hash.set('token', result.token)
49
- hash.set('registry', registry)
50
- hash.set('state', state)
51
- window.location.assign(`${redirectUri}#${hash.toString()}`)
52
- }
53
-
54
- void run().catch((error) => {
55
- const message = error instanceof Error ? error.message : 'Failed to create token'
56
- setStatus(message)
57
- setToken(null)
58
- })
59
- }, [createToken, isAuthenticated, label, me, redirectUri, safeRedirect, state])
60
-
61
- if (!safeRedirect) {
62
- return (
63
- <main className="section">
64
- <div className="card">
65
- <h1 className="section-title" style={{ marginTop: 0 }}>
66
- CLI login
67
- </h1>
68
- <p className="section-subtitle">Invalid redirect URL.</p>
69
- <p className="section-subtitle" style={{ marginBottom: 0 }}>
70
- Run the CLI again to start a fresh login.
71
- </p>
72
- </div>
73
- </main>
74
- )
75
- }
76
-
77
- if (!state) {
78
- return (
79
- <main className="section">
80
- <div className="card">
81
- <h1 className="section-title" style={{ marginTop: 0 }}>
82
- CLI login
83
- </h1>
84
- <p className="section-subtitle">Missing state.</p>
85
- <p className="section-subtitle" style={{ marginBottom: 0 }}>
86
- Run the CLI again to start a fresh login.
87
- </p>
88
- </div>
89
- </main>
90
- )
91
- }
92
-
93
- if (!registry) {
94
- return (
95
- <main className="section">
96
- <div className="card">Missing VITE_CONVEX_SITE_URL configuration.</div>
97
- </main>
98
- )
99
- }
100
-
101
- if (!isAuthenticated || !me) {
102
- return (
103
- <main className="section">
104
- <div className="card">
105
- <h1 className="section-title" style={{ marginTop: 0 }}>
106
- CLI login
107
- </h1>
108
- <p className="section-subtitle">Sign in to create an API token for the CLI.</p>
109
- <button
110
- className="btn btn-primary"
111
- type="button"
112
- disabled={isLoading}
113
- onClick={() => void signIn('github')}
114
- >
115
- Sign in with GitHub
116
- </button>
117
- </div>
118
- </main>
119
- )
120
- }
121
-
122
- return (
123
- <main className="section">
124
- <div className="card">
125
- <h1 className="section-title" style={{ marginTop: 0 }}>
126
- CLI login
127
- </h1>
128
- <p className="section-subtitle">{status}</p>
129
- {token ? (
130
- <div className="stat" style={{ overflowX: 'auto' }}>
131
- <div style={{ marginBottom: 8 }}>If redirect fails, copy this token:</div>
132
- <code>{token}</code>
133
- </div>
134
- ) : null}
135
- </div>
136
- </main>
137
- )
138
- }
139
-
140
- function isAllowedRedirectUri(value: string) {
141
- if (!value) return false
142
- let url: URL
143
- try {
144
- url = new URL(value)
145
- } catch {
146
- return false
147
- }
148
- if (url.protocol !== 'http:') return false
149
- const host = url.hostname.toLowerCase()
150
- return host === '127.0.0.1' || host === 'localhost' || host === '::1' || host === '[::1]'
151
- }
152
-
153
- function decodeLabel(value: string | undefined) {
154
- if (!value) return null
155
- try {
156
- const normalized = value.replace(/-/g, '+').replace(/_/g, '/')
157
- const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=')
158
- const binary = atob(padded)
159
- const bytes = new Uint8Array(binary.length)
160
- for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)
161
- const decoded = new TextDecoder().decode(bytes)
162
- const label = decoded.trim()
163
- if (!label) return null
164
- return label.slice(0, 80)
165
- } catch {
166
- return null
167
- }
168
- }
@@ -1,97 +0,0 @@
1
- import { createFileRoute, Link } from '@tanstack/react-router'
2
- import { useQuery } from 'convex/react'
3
- import { Package, Plus, Upload } from 'lucide-react'
4
- import { api } from '../../convex/_generated/api'
5
- import type { Doc } from '../../convex/_generated/dataModel'
6
- import type { PublicSkill } from '../lib/publicUser'
7
-
8
- export const Route = createFileRoute('/dashboard')({
9
- component: Dashboard,
10
- })
11
-
12
- function Dashboard() {
13
- const me = useQuery(api.users.me) as Doc<'users'> | null | undefined
14
- const mySkills = useQuery(
15
- api.skills.list,
16
- me?._id ? { ownerUserId: me._id, limit: 100 } : 'skip',
17
- ) as PublicSkill[] | undefined
18
-
19
- if (!me) {
20
- return (
21
- <main className="section">
22
- <div className="card">Sign in to access your dashboard.</div>
23
- </main>
24
- )
25
- }
26
-
27
- const skills = mySkills ?? []
28
- const ownerHandle = me.handle ?? me.name ?? me.displayName ?? me._id
29
-
30
- return (
31
- <main className="section">
32
- <div className="dashboard-header">
33
- <h1 className="section-title" style={{ margin: 0 }}>
34
- My Skills
35
- </h1>
36
- <Link to="/upload" search={{ updateSlug: undefined }} className="btn btn-primary">
37
- <Plus className="h-4 w-4" aria-hidden="true" />
38
- Upload New Skill
39
- </Link>
40
- </div>
41
-
42
- {skills.length === 0 ? (
43
- <div className="card dashboard-empty">
44
- <Package className="dashboard-empty-icon" aria-hidden="true" />
45
- <h2>No skills yet</h2>
46
- <p>Upload your first skill to share it with the community.</p>
47
- <Link to="/upload" search={{ updateSlug: undefined }} className="btn btn-primary">
48
- <Upload className="h-4 w-4" aria-hidden="true" />
49
- Upload a Skill
50
- </Link>
51
- </div>
52
- ) : (
53
- <div className="dashboard-grid">
54
- {skills.map((skill) => (
55
- <SkillCard key={skill._id} skill={skill} ownerHandle={ownerHandle} />
56
- ))}
57
- </div>
58
- )}
59
- </main>
60
- )
61
- }
62
-
63
- function SkillCard({ skill, ownerHandle }: { skill: PublicSkill; ownerHandle: string | null }) {
64
- return (
65
- <div className="dashboard-skill-card">
66
- <div className="dashboard-skill-info">
67
- <Link
68
- to="/$owner/$slug"
69
- params={{ owner: ownerHandle ?? 'unknown', slug: skill.slug }}
70
- className="dashboard-skill-name"
71
- >
72
- {skill.displayName}
73
- </Link>
74
- <span className="dashboard-skill-slug">/{skill.slug}</span>
75
- {skill.summary && <p className="dashboard-skill-description">{skill.summary}</p>}
76
- <div className="dashboard-skill-stats">
77
- <span>⤓ {skill.stats.downloads}</span>
78
- <span>★ {skill.stats.stars}</span>
79
- <span>{skill.stats.versions} v</span>
80
- </div>
81
- </div>
82
- <div className="dashboard-skill-actions">
83
- <Link to="/upload" search={{ updateSlug: skill.slug }} className="btn btn-sm">
84
- <Upload className="h-3 w-3" aria-hidden="true" />
85
- New Version
86
- </Link>
87
- <Link
88
- to="/$owner/$slug"
89
- params={{ owner: ownerHandle ?? 'unknown', slug: skill.slug }}
90
- className="btn btn-ghost btn-sm"
91
- >
92
- View
93
- </Link>
94
- </div>
95
- </div>
96
- )
97
- }
@@ -1,415 +0,0 @@
1
- import { createFileRoute, useNavigate } from '@tanstack/react-router'
2
- import { useAction } from 'convex/react'
3
- import { useMemo, useState } from 'react'
4
- import { api } from '../../convex/_generated/api'
5
- import { formatBytes } from '../lib/uploadUtils'
6
- import { useAuthStatus } from '../lib/useAuthStatus'
7
-
8
- export const Route = createFileRoute('/import')({
9
- component: ImportGitHub,
10
- })
11
-
12
- type Candidate = {
13
- path: string
14
- readmePath: string
15
- name: string | null
16
- description: string | null
17
- }
18
-
19
- type CandidatePreview = {
20
- resolved: {
21
- owner: string
22
- repo: string
23
- ref: string
24
- commit: string
25
- path: string
26
- repoUrl: string
27
- originalUrl: string
28
- }
29
- candidate: Candidate
30
- defaults: {
31
- selectedPaths: string[]
32
- slug: string
33
- displayName: string
34
- version: string
35
- tags: string[]
36
- }
37
- files: Array<{ path: string; size: number; defaultSelected: boolean }>
38
- }
39
-
40
- function ImportGitHub() {
41
- const { isAuthenticated, isLoading, me } = useAuthStatus()
42
- const previewImport = useAction(api.githubImport.previewGitHubImport)
43
- const previewCandidate = useAction(api.githubImport.previewGitHubImportCandidate)
44
- const importSkill = useAction(api.githubImport.importGitHubSkill)
45
- const navigate = useNavigate()
46
-
47
- const [url, setUrl] = useState('')
48
- const [candidates, setCandidates] = useState<Candidate[]>([])
49
- const [selectedCandidatePath, setSelectedCandidatePath] = useState<string | null>(null)
50
- const [preview, setPreview] = useState<CandidatePreview | null>(null)
51
- const [selected, setSelected] = useState<Record<string, boolean>>({})
52
-
53
- const [slug, setSlug] = useState('')
54
- const [displayName, setDisplayName] = useState('')
55
- const [version, setVersion] = useState('0.1.0')
56
- const [tags, setTags] = useState('latest')
57
-
58
- const [status, setStatus] = useState<string | null>(null)
59
- const [error, setError] = useState<string | null>(null)
60
- const [isBusy, setIsBusy] = useState(false)
61
-
62
- const selectedCount = useMemo(() => Object.values(selected).filter(Boolean).length, [selected])
63
- const selectedBytes = useMemo(() => {
64
- if (!preview) return 0
65
- let total = 0
66
- for (const file of preview.files) {
67
- if (selected[file.path]) total += file.size
68
- }
69
- return total
70
- }, [preview, selected])
71
-
72
- const detect = async () => {
73
- setError(null)
74
- setStatus(null)
75
- setPreview(null)
76
- setCandidates([])
77
- setSelectedCandidatePath(null)
78
- setSelected({})
79
- setIsBusy(true)
80
- try {
81
- const result = await previewImport({ url: url.trim() })
82
- const items = (result.candidates ?? []) as Candidate[]
83
- setCandidates(items)
84
- if (items.length === 1) {
85
- const only = items[0]
86
- if (only) await loadCandidate(only.path)
87
- } else {
88
- setStatus(`Found ${items.length} skills. Pick one.`)
89
- }
90
- } catch (e) {
91
- setError(e instanceof Error ? e.message : 'Preview failed')
92
- } finally {
93
- setIsBusy(false)
94
- }
95
- }
96
-
97
- const loadCandidate = async (candidatePath: string) => {
98
- setError(null)
99
- setStatus(null)
100
- setPreview(null)
101
- setSelected({})
102
- setSelectedCandidatePath(candidatePath)
103
- setIsBusy(true)
104
- try {
105
- const result = (await previewCandidate({
106
- url: url.trim(),
107
- candidatePath,
108
- })) as CandidatePreview
109
- setPreview(result)
110
- setSlug(result.defaults.slug)
111
- setDisplayName(result.defaults.displayName)
112
- setVersion(result.defaults.version)
113
- setTags((result.defaults.tags ?? ['latest']).join(','))
114
- const nextSelected: Record<string, boolean> = {}
115
- for (const file of result.files) nextSelected[file.path] = file.defaultSelected
116
- setSelected(nextSelected)
117
- setStatus('Ready to import.')
118
- } catch (e) {
119
- setError(e instanceof Error ? e.message : 'Preview failed')
120
- } finally {
121
- setIsBusy(false)
122
- }
123
- }
124
-
125
- const applyDefaultSelection = () => {
126
- if (!preview) return
127
- const set = new Set(preview.defaults.selectedPaths)
128
- const next: Record<string, boolean> = {}
129
- for (const file of preview.files) next[file.path] = set.has(file.path)
130
- setSelected(next)
131
- }
132
-
133
- const selectAll = () => {
134
- if (!preview) return
135
- const next: Record<string, boolean> = {}
136
- for (const file of preview.files) next[file.path] = true
137
- setSelected(next)
138
- }
139
-
140
- const clearAll = () => {
141
- if (!preview) return
142
- const next: Record<string, boolean> = {}
143
- for (const file of preview.files) next[file.path] = false
144
- setSelected(next)
145
- }
146
-
147
- const doImport = async () => {
148
- if (!preview) return
149
- setIsBusy(true)
150
- setError(null)
151
- setStatus('Importing…')
152
- try {
153
- const selectedPaths = preview.files.map((file) => file.path).filter((path) => selected[path])
154
- const tagList = tags
155
- .split(',')
156
- .map((tag) => tag.trim())
157
- .filter(Boolean)
158
- const result = await importSkill({
159
- url: url.trim(),
160
- commit: preview.resolved.commit,
161
- candidatePath: preview.candidate.path,
162
- selectedPaths,
163
- slug: slug.trim(),
164
- displayName: displayName.trim(),
165
- version: version.trim(),
166
- tags: tagList,
167
- })
168
- const nextSlug = result.slug
169
- setStatus('Imported.')
170
- const ownerParam = me?.handle ?? (me?._id ? String(me._id) : 'unknown')
171
- await navigate({ to: '/$owner/$slug', params: { owner: ownerParam, slug: nextSlug } })
172
- } catch (e) {
173
- setError(e instanceof Error ? e.message : 'Import failed')
174
- setStatus(null)
175
- } finally {
176
- setIsBusy(false)
177
- }
178
- }
179
-
180
- if (!isAuthenticated) {
181
- return (
182
- <main className="section">
183
- <div className="card">
184
- {isLoading ? 'Loading…' : 'Sign in to import and publish skills.'}
185
- </div>
186
- </main>
187
- )
188
- }
189
-
190
- return (
191
- <main className="section upload-shell">
192
- <div className="upload-header">
193
- <div>
194
- <div className="upload-kicker">GitHub import</div>
195
- <h1 className="upload-title">Import from GitHub</h1>
196
- <p className="upload-subtitle">Public repos only. Detects SKILL.md automatically.</p>
197
- </div>
198
- <div className="upload-badge">
199
- <div>Public only</div>
200
- <div className="upload-badge-sub">Commit pinned</div>
201
- </div>
202
- </div>
203
-
204
- <div className="upload-card">
205
- <div className="upload-fields">
206
- <label className="upload-field" htmlFor="github-url">
207
- <div className="upload-field-header">
208
- <strong>GitHub URL</strong>
209
- <span className="upload-field-hint">Repo, tree path, or blob</span>
210
- </div>
211
- <input
212
- id="github-url"
213
- className="upload-input"
214
- value={url}
215
- onChange={(e) => setUrl(e.target.value)}
216
- placeholder="https://github.com/owner/repo"
217
- autoCapitalize="none"
218
- autoCorrect="off"
219
- spellCheck={false}
220
- />
221
- </label>
222
- </div>
223
-
224
- <div className="upload-footer">
225
- <button
226
- className="btn btn-primary"
227
- type="button"
228
- disabled={!url.trim() || isBusy}
229
- onClick={() => void detect()}
230
- >
231
- Detect
232
- </button>
233
- {status ? <p className="upload-muted">{status}</p> : null}
234
- </div>
235
-
236
- {error ? (
237
- <div className="upload-validation">
238
- <div className="upload-validation-item upload-error">{error}</div>
239
- </div>
240
- ) : null}
241
- </div>
242
-
243
- {candidates.length > 1 ? (
244
- <div className="card">
245
- <h2 style={{ margin: 0 }}>Pick a skill</h2>
246
- <div className="upload-filelist">
247
- {candidates.map((candidate) => (
248
- <label key={candidate.path} className="upload-file">
249
- <input
250
- type="radio"
251
- name="candidate"
252
- checked={selectedCandidatePath === candidate.path}
253
- onChange={() => void loadCandidate(candidate.path)}
254
- disabled={isBusy}
255
- />
256
- <span className="mono">{candidate.path || '(repo root)'}</span>
257
- <span>
258
- {candidate.name
259
- ? candidate.name
260
- : candidate.description
261
- ? candidate.description
262
- : ''}
263
- </span>
264
- </label>
265
- ))}
266
- </div>
267
- </div>
268
- ) : null}
269
-
270
- {preview ? (
271
- <>
272
- <div className="upload-card">
273
- <div className="upload-grid">
274
- <div className="upload-fields">
275
- <label className="upload-field" htmlFor="slug">
276
- <div className="upload-field-header">
277
- <strong>Slug</strong>
278
- <span className="upload-field-hint">Unique, lowercase</span>
279
- </div>
280
- <input
281
- id="slug"
282
- className="upload-input"
283
- value={slug}
284
- onChange={(e) => setSlug(e.target.value)}
285
- autoCapitalize="none"
286
- autoCorrect="off"
287
- spellCheck={false}
288
- />
289
- </label>
290
- <label className="upload-field" htmlFor="name">
291
- <div className="upload-field-header">
292
- <strong>Display name</strong>
293
- <span className="upload-field-hint">Shown in listings</span>
294
- </div>
295
- <input
296
- id="name"
297
- className="upload-input"
298
- value={displayName}
299
- onChange={(e) => setDisplayName(e.target.value)}
300
- />
301
- </label>
302
- <div className="upload-row">
303
- <label className="upload-field" htmlFor="version">
304
- <div className="upload-field-header">
305
- <strong>Version</strong>
306
- <span className="upload-field-hint">Semver</span>
307
- </div>
308
- <input
309
- id="version"
310
- className="upload-input"
311
- value={version}
312
- onChange={(e) => setVersion(e.target.value)}
313
- autoCapitalize="none"
314
- autoCorrect="off"
315
- spellCheck={false}
316
- />
317
- </label>
318
- <label className="upload-field" htmlFor="tags">
319
- <div className="upload-field-header">
320
- <strong>Tags</strong>
321
- <span className="upload-field-hint">Comma-separated</span>
322
- </div>
323
- <input
324
- id="tags"
325
- className="upload-input"
326
- value={tags}
327
- onChange={(e) => setTags(e.target.value)}
328
- autoCapitalize="none"
329
- autoCorrect="off"
330
- spellCheck={false}
331
- />
332
- </label>
333
- </div>
334
- </div>
335
- <aside className="upload-side">
336
- <div className="upload-summary">
337
- <div className="upload-requirement ok">Commit pinned</div>
338
- <div className="upload-muted">
339
- {preview.resolved.owner}/{preview.resolved.repo}@
340
- {preview.resolved.commit.slice(0, 7)}
341
- </div>
342
- <div className="upload-muted mono">{preview.candidate.path || 'repo root'}</div>
343
- </div>
344
- </aside>
345
- </div>
346
- </div>
347
-
348
- <div className="card">
349
- <div
350
- style={{
351
- display: 'flex',
352
- justifyContent: 'space-between',
353
- gap: 12,
354
- flexWrap: 'wrap',
355
- }}
356
- >
357
- <h2 style={{ margin: 0 }}>Files</h2>
358
- <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
359
- <button
360
- className="btn"
361
- type="button"
362
- disabled={isBusy}
363
- onClick={applyDefaultSelection}
364
- >
365
- Select referenced
366
- </button>
367
- <button className="btn" type="button" disabled={isBusy} onClick={selectAll}>
368
- Select all
369
- </button>
370
- <button className="btn" type="button" disabled={isBusy} onClick={clearAll}>
371
- Clear
372
- </button>
373
- </div>
374
- </div>
375
- <div className="upload-muted">
376
- Selected: {selectedCount}/{preview.files.length} • {formatBytes(selectedBytes)}
377
- </div>
378
- <div className="file-list">
379
- {preview.files.map((file) => (
380
- <label key={file.path} className="file-row">
381
- <input
382
- type="checkbox"
383
- checked={Boolean(selected[file.path])}
384
- onChange={() =>
385
- setSelected((prev) => ({ ...prev, [file.path]: !prev[file.path] }))
386
- }
387
- disabled={isBusy}
388
- />
389
- <span className="mono file-path">{file.path}</span>
390
- <span className="file-meta">{formatBytes(file.size)}</span>
391
- </label>
392
- ))}
393
- </div>
394
- <div className="upload-footer">
395
- <button
396
- className="btn btn-primary"
397
- type="button"
398
- disabled={
399
- isBusy ||
400
- !slug.trim() ||
401
- !displayName.trim() ||
402
- !version.trim() ||
403
- selectedCount === 0
404
- }
405
- onClick={() => void doImport()}
406
- >
407
- Import + publish
408
- </button>
409
- </div>
410
- </div>
411
- </>
412
- ) : null}
413
- </main>
414
- )
415
- }