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.
- package/LICENSE +1 -0
- package/README.md +36 -129
- package/dist/browserAuth.d.ts +20 -0
- package/dist/browserAuth.js +156 -0
- package/dist/browserAuth.js.map +1 -0
- package/dist/browserAuth.test.d.ts +1 -0
- package/dist/browserAuth.test.js +83 -0
- package/dist/browserAuth.test.js.map +1 -0
- package/dist/cli/buildInfo.d.ts +3 -0
- package/dist/cli/buildInfo.js +103 -0
- package/dist/cli/buildInfo.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +9 -0
- package/dist/cli/commands/auth.js +75 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/delete.d.ts +11 -0
- package/dist/cli/commands/delete.js +67 -0
- package/dist/cli/commands/delete.js.map +1 -0
- package/dist/cli/commands/delete.test.d.ts +1 -0
- package/dist/cli/commands/delete.test.js +52 -0
- package/dist/cli/commands/delete.test.js.map +1 -0
- package/dist/cli/commands/publish.d.ts +9 -0
- package/dist/cli/commands/publish.js +87 -0
- package/dist/cli/commands/publish.js.map +1 -0
- package/dist/cli/commands/publish.test.d.ts +1 -0
- package/dist/cli/commands/publish.test.js +104 -0
- package/dist/cli/commands/publish.test.js.map +1 -0
- package/dist/cli/commands/skills.d.ts +23 -0
- package/dist/cli/commands/skills.js +298 -0
- package/dist/cli/commands/skills.js.map +1 -0
- package/dist/cli/commands/skills.test.d.ts +1 -0
- package/dist/cli/commands/skills.test.js +156 -0
- package/dist/cli/commands/skills.test.js.map +1 -0
- package/dist/cli/commands/star.d.ts +8 -0
- package/dist/cli/commands/star.js +38 -0
- package/dist/cli/commands/star.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.js +160 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/sync.test.d.ts +1 -0
- package/dist/cli/commands/sync.test.js +277 -0
- package/dist/cli/commands/sync.test.js.map +1 -0
- package/dist/cli/commands/syncHelpers.d.ts +76 -0
- package/dist/cli/commands/syncHelpers.js +349 -0
- package/dist/cli/commands/syncHelpers.js.map +1 -0
- package/dist/cli/commands/syncHelpers.test.d.ts +1 -0
- package/dist/cli/commands/syncHelpers.test.js +22 -0
- package/dist/cli/commands/syncHelpers.test.js.map +1 -0
- package/dist/cli/commands/syncTypes.d.ts +24 -0
- package/dist/cli/commands/syncTypes.js +2 -0
- package/dist/cli/commands/syncTypes.js.map +1 -0
- package/dist/cli/commands/unstar.d.ts +8 -0
- package/dist/cli/commands/unstar.js +38 -0
- package/dist/cli/commands/unstar.js.map +1 -0
- package/dist/cli/helpStyle.d.ts +13 -0
- package/dist/cli/helpStyle.js +38 -0
- package/dist/cli/helpStyle.js.map +1 -0
- package/dist/cli/pilotbotConfig.d.ts +6 -0
- package/dist/cli/pilotbotConfig.js +110 -0
- package/dist/cli/pilotbotConfig.js.map +1 -0
- package/dist/cli/pilotbotConfig.test.d.ts +1 -0
- package/dist/cli/pilotbotConfig.test.js +133 -0
- package/dist/cli/pilotbotConfig.test.js.map +1 -0
- package/dist/cli/registry.d.ts +7 -0
- package/dist/cli/registry.js +42 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli/registry.test.d.ts +1 -0
- package/dist/cli/registry.test.js +48 -0
- package/dist/cli/registry.test.js.map +1 -0
- package/dist/cli/scanSkills.d.ts +7 -0
- package/dist/cli/scanSkills.js +75 -0
- package/dist/cli/scanSkills.js.map +1 -0
- package/dist/cli/scanSkills.test.d.ts +1 -0
- package/dist/cli/scanSkills.test.js +60 -0
- package/dist/cli/scanSkills.test.js.map +1 -0
- package/dist/cli/slug.d.ts +2 -0
- package/dist/cli/slug.js +16 -0
- package/dist/cli/slug.js.map +1 -0
- package/dist/cli/types.d.ts +15 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/ui.d.ts +7 -0
- package/dist/cli/ui.js +72 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +268 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -0
- package/dist/discovery.d.ts +5 -0
- package/dist/discovery.js +21 -0
- package/dist/discovery.js.map +1 -0
- package/dist/discovery.test.d.ts +1 -0
- package/dist/discovery.test.js +46 -0
- package/dist/discovery.test.js.map +1 -0
- package/dist/http.d.ts +32 -0
- package/dist/http.js +261 -0
- package/dist/http.js.map +1 -0
- package/dist/http.test.d.ts +1 -0
- package/dist/http.test.js +135 -0
- package/dist/http.test.js.map +1 -0
- package/dist/schema/ark.js.map +1 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/routes.js.map +1 -0
- package/{packages/schema/dist → dist/schema}/schemas.d.ts +0 -39
- package/{packages/schema/dist → dist/schema}/schemas.js +0 -22
- package/dist/schema/schemas.js.map +1 -0
- package/dist/schema/textFiles.js.map +1 -0
- package/dist/schema/textFiles.test.d.ts +1 -0
- package/dist/schema/textFiles.test.js +20 -0
- package/dist/schema/textFiles.test.js.map +1 -0
- package/dist/skills.d.ts +43 -0
- package/dist/skills.js +163 -0
- package/dist/skills.js.map +1 -0
- package/dist/skills.test.d.ts +1 -0
- package/dist/skills.test.js +144 -0
- package/dist/skills.test.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +27 -70
- package/.env.local.example +0 -19
- package/.github/workflows/ci.yml +0 -40
- package/.oxlintrc.json +0 -3
- package/AGENTS.md +0 -45
- package/CHANGELOG.md +0 -138
- package/DEPRECATIONS.md +0 -7
- package/biome.json +0 -41
- package/convex/_generated/api.d.ts +0 -153
- package/convex/_generated/api.js +0 -23
- package/convex/_generated/dataModel.d.ts +0 -60
- package/convex/_generated/server.d.ts +0 -143
- package/convex/_generated/server.js +0 -93
- package/convex/auth.config.ts +0 -8
- package/convex/auth.ts +0 -19
- package/convex/comments.ts +0 -88
- package/convex/crons.ts +0 -34
- package/convex/devSeed.ts +0 -459
- package/convex/devSeedExtra.ts +0 -541
- package/convex/downloads.ts +0 -78
- package/convex/githubBackups.ts +0 -170
- package/convex/githubBackupsNode.ts +0 -183
- package/convex/githubImport.ts +0 -317
- package/convex/githubSoulBackups.ts +0 -170
- package/convex/githubSoulBackupsNode.ts +0 -186
- package/convex/http.ts +0 -194
- package/convex/httpApi.handlers.test.ts +0 -488
- package/convex/httpApi.test.ts +0 -70
- package/convex/httpApi.ts +0 -305
- package/convex/httpApiV1.handlers.test.ts +0 -584
- package/convex/httpApiV1.ts +0 -1172
- package/convex/leaderboards.ts +0 -39
- package/convex/lib/access.ts +0 -36
- package/convex/lib/apiTokenAuth.ts +0 -36
- package/convex/lib/badges.ts +0 -50
- package/convex/lib/changelog.test.ts +0 -34
- package/convex/lib/changelog.ts +0 -278
- package/convex/lib/embeddings.ts +0 -38
- package/convex/lib/githubBackup.ts +0 -443
- package/convex/lib/githubImport.test.ts +0 -247
- package/convex/lib/githubImport.ts +0 -425
- package/convex/lib/githubSoulBackup.ts +0 -443
- package/convex/lib/leaderboards.ts +0 -103
- package/convex/lib/moderation.ts +0 -42
- package/convex/lib/public.ts +0 -89
- package/convex/lib/searchText.test.ts +0 -46
- package/convex/lib/searchText.ts +0 -27
- package/convex/lib/skillBackfill.test.ts +0 -34
- package/convex/lib/skillBackfill.ts +0 -67
- package/convex/lib/skillPublish.test.ts +0 -28
- package/convex/lib/skillPublish.ts +0 -284
- package/convex/lib/skillStats.ts +0 -80
- package/convex/lib/skills.test.ts +0 -197
- package/convex/lib/skills.ts +0 -273
- package/convex/lib/soulChangelog.ts +0 -273
- package/convex/lib/soulPublish.ts +0 -236
- package/convex/lib/tokens.test.ts +0 -33
- package/convex/lib/tokens.ts +0 -51
- package/convex/lib/webhooks.test.ts +0 -91
- package/convex/lib/webhooks.ts +0 -112
- package/convex/maintenance.test.ts +0 -270
- package/convex/maintenance.ts +0 -840
- package/convex/rateLimits.ts +0 -50
- package/convex/schema.ts +0 -472
- package/convex/search.test.ts +0 -12
- package/convex/search.ts +0 -254
- package/convex/seed.test.ts +0 -37
- package/convex/seed.ts +0 -254
- package/convex/seedSouls.ts +0 -111
- package/convex/skillStatEvents.ts +0 -568
- package/convex/skills.ts +0 -1606
- package/convex/soulComments.ts +0 -88
- package/convex/soulDownloads.ts +0 -14
- package/convex/soulStars.ts +0 -71
- package/convex/souls.ts +0 -570
- package/convex/stars.ts +0 -108
- package/convex/statsMaintenance.ts +0 -205
- package/convex/telemetry.ts +0 -434
- package/convex/tokens.ts +0 -88
- package/convex/tsconfig.json +0 -7
- package/convex/uploads.ts +0 -20
- package/convex/users.ts +0 -122
- package/convex/webhooks.ts +0 -50
- package/convex.json +0 -3
- package/docs/README.md +0 -32
- package/docs/api.md +0 -51
- package/docs/architecture.md +0 -61
- package/docs/auth.md +0 -54
- package/docs/cli.md +0 -117
- package/docs/deploy.md +0 -78
- package/docs/diffing.md +0 -84
- package/docs/github-import.md +0 -171
- package/docs/http-api.md +0 -187
- package/docs/manual-testing.md +0 -64
- package/docs/mintlify.md +0 -43
- package/docs/quickstart.md +0 -120
- package/docs/skill-format.md +0 -58
- package/docs/soul-format.md +0 -37
- package/docs/spec.md +0 -177
- package/docs/telemetry.md +0 -91
- package/docs/troubleshooting.md +0 -49
- package/docs/webhook.md +0 -51
- package/e2e/menu-smoke.pw.test.ts +0 -49
- package/e2e/pilothub.e2e.test.ts +0 -494
- package/e2e/search-exact.pw.test.ts +0 -97
- package/packages/pilothub/LICENSE +0 -22
- package/packages/pilothub/README.md +0 -57
- package/packages/pilothub/package.json +0 -41
- package/packages/pilothub/src/browserAuth.test.ts +0 -96
- package/packages/pilothub/src/browserAuth.ts +0 -174
- package/packages/pilothub/src/cli/buildInfo.ts +0 -94
- package/packages/pilothub/src/cli/commands/auth.ts +0 -97
- package/packages/pilothub/src/cli/commands/delete.test.ts +0 -73
- package/packages/pilothub/src/cli/commands/delete.ts +0 -83
- package/packages/pilothub/src/cli/commands/publish.test.ts +0 -122
- package/packages/pilothub/src/cli/commands/publish.ts +0 -108
- package/packages/pilothub/src/cli/commands/skills.test.ts +0 -191
- package/packages/pilothub/src/cli/commands/skills.ts +0 -380
- package/packages/pilothub/src/cli/commands/star.ts +0 -46
- package/packages/pilothub/src/cli/commands/sync.test.ts +0 -310
- package/packages/pilothub/src/cli/commands/sync.ts +0 -200
- package/packages/pilothub/src/cli/commands/syncHelpers.test.ts +0 -26
- package/packages/pilothub/src/cli/commands/syncHelpers.ts +0 -427
- package/packages/pilothub/src/cli/commands/syncTypes.ts +0 -27
- package/packages/pilothub/src/cli/commands/unstar.ts +0 -48
- package/packages/pilothub/src/cli/helpStyle.ts +0 -45
- package/packages/pilothub/src/cli/pilotbotConfig.test.ts +0 -159
- package/packages/pilothub/src/cli/pilotbotConfig.ts +0 -147
- package/packages/pilothub/src/cli/registry.test.ts +0 -63
- package/packages/pilothub/src/cli/registry.ts +0 -43
- package/packages/pilothub/src/cli/scanSkills.test.ts +0 -64
- package/packages/pilothub/src/cli/scanSkills.ts +0 -84
- package/packages/pilothub/src/cli/slug.ts +0 -16
- package/packages/pilothub/src/cli/types.ts +0 -12
- package/packages/pilothub/src/cli/ui.ts +0 -75
- package/packages/pilothub/src/cli.ts +0 -311
- package/packages/pilothub/src/config.ts +0 -36
- package/packages/pilothub/src/discovery.test.ts +0 -75
- package/packages/pilothub/src/discovery.ts +0 -19
- package/packages/pilothub/src/http.test.ts +0 -156
- package/packages/pilothub/src/http.ts +0 -301
- package/packages/pilothub/src/schema/ark.ts +0 -29
- package/packages/pilothub/src/schema/index.ts +0 -5
- package/packages/pilothub/src/schema/routes.ts +0 -22
- package/packages/pilothub/src/schema/schemas.ts +0 -260
- package/packages/pilothub/src/schema/textFiles.test.ts +0 -23
- package/packages/pilothub/src/schema/textFiles.ts +0 -66
- package/packages/pilothub/src/skills.test.ts +0 -191
- package/packages/pilothub/src/skills.ts +0 -172
- package/packages/pilothub/src/types.ts +0 -10
- package/packages/pilothub/tsconfig.json +0 -14
- package/packages/schema/README.md +0 -3
- package/packages/schema/dist/ark.js.map +0 -1
- package/packages/schema/dist/index.js.map +0 -1
- package/packages/schema/dist/routes.js.map +0 -1
- package/packages/schema/dist/schemas.js.map +0 -1
- package/packages/schema/dist/textFiles.js.map +0 -1
- package/packages/schema/package.json +0 -26
- package/packages/schema/src/ark.ts +0 -29
- package/packages/schema/src/index.ts +0 -5
- package/packages/schema/src/routes.ts +0 -22
- package/packages/schema/src/schemas.test.ts +0 -123
- package/packages/schema/src/schemas.ts +0 -287
- package/packages/schema/src/textFiles.test.ts +0 -23
- package/packages/schema/src/textFiles.ts +0 -66
- package/packages/schema/tsconfig.json +0 -15
- package/pilothub +0 -46
- package/playwright.config.ts +0 -33
- package/public/.well-known/pilothub.json +0 -6
- package/public/api/v1/openapi.json +0 -379
- package/public/favicon.ico +0 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +0 -25
- package/public/og.png +0 -0
- package/public/og.svg +0 -98
- package/public/pilot-logo.png +0 -0
- package/public/pilot-mark.png +0 -0
- package/public/robots.txt +0 -3
- package/public/tanstack-circle-logo.png +0 -0
- package/public/tanstack-word-logo-white.svg +0 -1
- package/scripts/check-peer-deps.ts +0 -56
- package/scripts/docs-list.ts +0 -148
- package/scripts/run-playwright-local.sh +0 -14
- package/server/og/fetchSkillOgMeta.ts +0 -27
- package/server/og/fetchSoulOgMeta.ts +0 -27
- package/server/og/ogAssets.ts +0 -80
- package/server/og/skillOgSvg.test.ts +0 -59
- package/server/og/skillOgSvg.ts +0 -258
- package/server/og/soulOgSvg.ts +0 -209
- package/server/routes/og/skill.png.ts +0 -103
- package/server/routes/og/soul.png.ts +0 -111
- package/src/__tests__/skill-detail-page.test.tsx +0 -86
- package/src/__tests__/skills-index.test.tsx +0 -145
- package/src/__tests__/upload.route.test.tsx +0 -228
- package/src/components/AppProviders.tsx +0 -19
- package/src/components/ClientOnly.tsx +0 -18
- package/src/components/Footer.tsx +0 -29
- package/src/components/Header.tsx +0 -295
- package/src/components/InstallSwitcher.tsx +0 -53
- package/src/components/SkillCard.tsx +0 -36
- package/src/components/SkillDetailPage.tsx +0 -817
- package/src/components/SkillDiffCard.tsx +0 -485
- package/src/components/SoulCard.tsx +0 -19
- package/src/components/SoulDetailPage.tsx +0 -263
- package/src/components/UserBootstrap.tsx +0 -18
- package/src/components/ui/dropdown-menu.tsx +0 -67
- package/src/components/ui/toggle-group.tsx +0 -35
- package/src/convex/client.ts +0 -3
- package/src/lib/badges.ts +0 -29
- package/src/lib/diffing.test.ts +0 -163
- package/src/lib/diffing.ts +0 -106
- package/src/lib/gravatar.test.ts +0 -9
- package/src/lib/gravatar.ts +0 -158
- package/src/lib/og.test.ts +0 -142
- package/src/lib/og.ts +0 -156
- package/src/lib/publicUser.ts +0 -39
- package/src/lib/roles.ts +0 -19
- package/src/lib/site.test.ts +0 -130
- package/src/lib/site.ts +0 -84
- package/src/lib/theme-transition.test.ts +0 -134
- package/src/lib/theme-transition.ts +0 -134
- package/src/lib/theme.test.tsx +0 -88
- package/src/lib/theme.ts +0 -43
- package/src/lib/uploadFiles.jsdom.test.ts +0 -33
- package/src/lib/uploadFiles.test.ts +0 -123
- package/src/lib/uploadFiles.ts +0 -245
- package/src/lib/uploadUtils.test.ts +0 -78
- package/src/lib/uploadUtils.ts +0 -93
- package/src/lib/useAuthStatus.ts +0 -12
- package/src/lib/utils.test.ts +0 -9
- package/src/lib/utils.ts +0 -6
- package/src/logo.svg +0 -12
- package/src/routeTree.gen.ts +0 -345
- package/src/router.tsx +0 -17
- package/src/routes/$owner/$slug.tsx +0 -55
- package/src/routes/__root.tsx +0 -136
- package/src/routes/admin.tsx +0 -11
- package/src/routes/cli/auth.tsx +0 -168
- package/src/routes/dashboard.tsx +0 -97
- package/src/routes/import.tsx +0 -415
- package/src/routes/index.tsx +0 -252
- package/src/routes/management.tsx +0 -529
- package/src/routes/settings.tsx +0 -203
- package/src/routes/skills/index.tsx +0 -422
- package/src/routes/souls/$slug.tsx +0 -55
- package/src/routes/souls/index.tsx +0 -243
- package/src/routes/stars.tsx +0 -68
- package/src/routes/u/$handle.tsx +0 -307
- package/src/routes/upload/utils.ts +0 -81
- package/src/routes/upload.tsx +0 -499
- package/src/styles.css +0 -2718
- package/tsconfig.json +0 -24
- package/tsconfig.oxlint.json +0 -16
- package/vercel.json +0 -8
- package/vite.config.ts +0 -48
- package/vitest.config.ts +0 -47
- package/vitest.e2e.config.ts +0 -11
- package/vitest.setup.ts +0 -1
- /package/{packages/pilothub/bin → bin}/pilothub.js +0 -0
- /package/{packages/schema/dist → dist/schema}/ark.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/ark.js +0 -0
- /package/{packages/schema/dist → dist/schema}/index.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/index.js +0 -0
- /package/{packages/schema/dist → dist/schema}/routes.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/routes.js +0 -0
- /package/{packages/schema/dist → dist/schema}/textFiles.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/textFiles.js +0 -0
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
'use node'
|
|
2
|
-
|
|
3
|
-
import { createPrivateKey, createSign } from 'node:crypto'
|
|
4
|
-
import type { Id } from '../_generated/dataModel'
|
|
5
|
-
import type { ActionCtx } from '../_generated/server'
|
|
6
|
-
|
|
7
|
-
const GITHUB_API = 'https://api.github.com'
|
|
8
|
-
const DEFAULT_REPO = 'pilotbot/souls'
|
|
9
|
-
const DEFAULT_ROOT = 'souls'
|
|
10
|
-
const META_FILENAME = '_meta.json'
|
|
11
|
-
const USER_AGENT = 'pilothub/souls-backup'
|
|
12
|
-
|
|
13
|
-
type BackupFile = {
|
|
14
|
-
path: string
|
|
15
|
-
size: number
|
|
16
|
-
storageId: Id<'_storage'>
|
|
17
|
-
sha256: string
|
|
18
|
-
contentType?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type BackupParams = {
|
|
22
|
-
slug: string
|
|
23
|
-
version: string
|
|
24
|
-
displayName: string
|
|
25
|
-
ownerHandle: string
|
|
26
|
-
files: BackupFile[]
|
|
27
|
-
publishedAt: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type RepoInfo = {
|
|
31
|
-
default_branch?: string
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type GitRef = {
|
|
35
|
-
object: { sha: string }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type GitCommit = {
|
|
39
|
-
sha: string
|
|
40
|
-
tree: { sha: string }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type GitTreeEntry = {
|
|
44
|
-
path?: string
|
|
45
|
-
type?: string
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type GitTree = {
|
|
49
|
-
tree?: GitTreeEntry[]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
type MetaFile = {
|
|
53
|
-
owner: string
|
|
54
|
-
slug: string
|
|
55
|
-
displayName: string
|
|
56
|
-
latest: {
|
|
57
|
-
version: string
|
|
58
|
-
publishedAt: number
|
|
59
|
-
commit: string | null
|
|
60
|
-
}
|
|
61
|
-
history: Array<{
|
|
62
|
-
version: string
|
|
63
|
-
publishedAt: number
|
|
64
|
-
commit: string
|
|
65
|
-
}>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type GitHubBackupContext = {
|
|
69
|
-
token: string
|
|
70
|
-
repo: string
|
|
71
|
-
repoOwner: string
|
|
72
|
-
repoName: string
|
|
73
|
-
branch: string
|
|
74
|
-
root: string
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function isGitHubSoulBackupConfigured() {
|
|
78
|
-
return Boolean(
|
|
79
|
-
process.env.GITHUB_APP_ID &&
|
|
80
|
-
process.env.GITHUB_APP_PRIVATE_KEY &&
|
|
81
|
-
process.env.GITHUB_APP_INSTALLATION_ID,
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function getGitHubSoulBackupContext(): Promise<GitHubBackupContext> {
|
|
86
|
-
const repo = process.env.GITHUB_SOULS_REPO ?? DEFAULT_REPO
|
|
87
|
-
const root = process.env.GITHUB_SOULS_ROOT ?? DEFAULT_ROOT
|
|
88
|
-
const [repoOwner, repoName] = parseRepo(repo)
|
|
89
|
-
const token = await createInstallationToken()
|
|
90
|
-
const repoInfo = await githubGet<RepoInfo>(token, `/repos/${repoOwner}/${repoName}`)
|
|
91
|
-
const branch = repoInfo.default_branch ?? 'main'
|
|
92
|
-
|
|
93
|
-
return { token, repo, repoOwner, repoName, branch, root }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function fetchGitHubSoulMeta(
|
|
97
|
-
context: GitHubBackupContext,
|
|
98
|
-
ownerHandle: string,
|
|
99
|
-
slug: string,
|
|
100
|
-
): Promise<MetaFile | null> {
|
|
101
|
-
const soulRoot = buildSoulRoot(context.root, ownerHandle, slug)
|
|
102
|
-
return fetchMetaFile(
|
|
103
|
-
context.token,
|
|
104
|
-
context.repoOwner,
|
|
105
|
-
context.repoName,
|
|
106
|
-
`${soulRoot}/${META_FILENAME}`,
|
|
107
|
-
context.branch,
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function backupSoulToGitHub(
|
|
112
|
-
ctx: ActionCtx,
|
|
113
|
-
params: BackupParams,
|
|
114
|
-
context?: GitHubBackupContext,
|
|
115
|
-
) {
|
|
116
|
-
if (!isGitHubSoulBackupConfigured()) return
|
|
117
|
-
|
|
118
|
-
const resolved = context ?? (await getGitHubSoulBackupContext())
|
|
119
|
-
const soulRoot = buildSoulRoot(resolved.root, params.ownerHandle, params.slug)
|
|
120
|
-
const ref = await githubGet<GitRef>(
|
|
121
|
-
resolved.token,
|
|
122
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/ref/heads/${resolved.branch}`,
|
|
123
|
-
)
|
|
124
|
-
const baseCommitSha = ref.object.sha
|
|
125
|
-
const baseCommit = await githubGet<GitCommit>(
|
|
126
|
-
resolved.token,
|
|
127
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/commits/${baseCommitSha}`,
|
|
128
|
-
)
|
|
129
|
-
const baseTreeSha = baseCommit.tree.sha
|
|
130
|
-
const existingTree = await githubGet<GitTree>(
|
|
131
|
-
resolved.token,
|
|
132
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/trees/${baseTreeSha}?recursive=1`,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
const prefix = `${soulRoot}/`
|
|
136
|
-
const existingPaths = new Set(
|
|
137
|
-
(existingTree.tree ?? [])
|
|
138
|
-
.filter((entry) => entry.type === 'blob' && entry.path?.startsWith(prefix))
|
|
139
|
-
.map((entry) => entry.path ?? ''),
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
const newPaths = new Set<string>()
|
|
143
|
-
const treeEntries: Array<{
|
|
144
|
-
path: string
|
|
145
|
-
mode: '100644'
|
|
146
|
-
type: 'blob'
|
|
147
|
-
sha: string | null
|
|
148
|
-
}> = []
|
|
149
|
-
|
|
150
|
-
for (const file of params.files) {
|
|
151
|
-
const content = await fetchStorageBase64(ctx, file.storageId)
|
|
152
|
-
const blobSha = await createBlob(resolved.token, resolved.repoOwner, resolved.repoName, content)
|
|
153
|
-
const path = `${soulRoot}/${file.path}`
|
|
154
|
-
newPaths.add(path)
|
|
155
|
-
treeEntries.push({ path, mode: '100644', type: 'blob', sha: blobSha })
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const existingMeta = await fetchMetaFile(
|
|
159
|
-
resolved.token,
|
|
160
|
-
resolved.repoOwner,
|
|
161
|
-
resolved.repoName,
|
|
162
|
-
`${soulRoot}/${META_FILENAME}`,
|
|
163
|
-
resolved.branch,
|
|
164
|
-
)
|
|
165
|
-
const metaPath = `${soulRoot}/${META_FILENAME}`
|
|
166
|
-
const metaDraft = buildMetaFile(params, existingMeta, resolved.repo, baseCommitSha, null)
|
|
167
|
-
const metaDraftContent = `${JSON.stringify(metaDraft, null, 2)}\n`
|
|
168
|
-
const metaDraftSha = await createBlob(
|
|
169
|
-
resolved.token,
|
|
170
|
-
resolved.repoOwner,
|
|
171
|
-
resolved.repoName,
|
|
172
|
-
toBase64(metaDraftContent),
|
|
173
|
-
)
|
|
174
|
-
newPaths.add(metaPath)
|
|
175
|
-
treeEntries.push({ path: metaPath, mode: '100644', type: 'blob', sha: metaDraftSha })
|
|
176
|
-
|
|
177
|
-
for (const path of existingPaths) {
|
|
178
|
-
if (newPaths.has(path)) continue
|
|
179
|
-
treeEntries.push({ path, mode: '100644', type: 'blob', sha: null })
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const newTree = await githubPost<{ sha: string }>(
|
|
183
|
-
resolved.token,
|
|
184
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/trees`,
|
|
185
|
-
{
|
|
186
|
-
base_tree: baseTreeSha,
|
|
187
|
-
tree: treeEntries,
|
|
188
|
-
},
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
const commit = await githubPost<GitCommit>(
|
|
192
|
-
resolved.token,
|
|
193
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/commits`,
|
|
194
|
-
{
|
|
195
|
-
message: `soul: ${params.slug} v${params.version}`,
|
|
196
|
-
tree: newTree.sha,
|
|
197
|
-
parents: [baseCommitSha],
|
|
198
|
-
},
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
const metaFinal = buildMetaFile(params, existingMeta, resolved.repo, baseCommitSha, commit.sha)
|
|
202
|
-
const metaFinalContent = `${JSON.stringify(metaFinal, null, 2)}\n`
|
|
203
|
-
const metaFinalSha = await createBlob(
|
|
204
|
-
resolved.token,
|
|
205
|
-
resolved.repoOwner,
|
|
206
|
-
resolved.repoName,
|
|
207
|
-
toBase64(metaFinalContent),
|
|
208
|
-
)
|
|
209
|
-
const metaTree = await githubPost<{ sha: string }>(
|
|
210
|
-
resolved.token,
|
|
211
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/trees`,
|
|
212
|
-
{
|
|
213
|
-
base_tree: commit.tree.sha,
|
|
214
|
-
tree: [{ path: metaPath, mode: '100644', type: 'blob', sha: metaFinalSha }],
|
|
215
|
-
},
|
|
216
|
-
)
|
|
217
|
-
const metaCommit = await githubPost<GitCommit>(
|
|
218
|
-
resolved.token,
|
|
219
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/commits`,
|
|
220
|
-
{
|
|
221
|
-
message: `meta: ${params.slug} v${params.version}`,
|
|
222
|
-
tree: metaTree.sha,
|
|
223
|
-
parents: [commit.sha],
|
|
224
|
-
},
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
await githubPatch(
|
|
228
|
-
resolved.token,
|
|
229
|
-
`/repos/${resolved.repoOwner}/${resolved.repoName}/git/refs/heads/${resolved.branch}`,
|
|
230
|
-
{
|
|
231
|
-
sha: metaCommit.sha,
|
|
232
|
-
},
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function buildMetaFile(
|
|
237
|
-
params: BackupParams,
|
|
238
|
-
existing: MetaFile | null,
|
|
239
|
-
repo: string,
|
|
240
|
-
baseCommitSha: string,
|
|
241
|
-
latestCommitSha: string | null,
|
|
242
|
-
): MetaFile {
|
|
243
|
-
let history = [...(existing?.history ?? [])]
|
|
244
|
-
if (existing?.latest?.version) {
|
|
245
|
-
const previousCommit = existing.latest.commit ?? commitUrl(repo, baseCommitSha)
|
|
246
|
-
const previous = {
|
|
247
|
-
version: existing.latest.version,
|
|
248
|
-
publishedAt: existing.latest.publishedAt,
|
|
249
|
-
commit: previousCommit,
|
|
250
|
-
}
|
|
251
|
-
history = [previous, ...history.filter((entry) => entry.version !== previous.version)]
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
owner: normalizeOwner(params.ownerHandle),
|
|
256
|
-
slug: params.slug,
|
|
257
|
-
displayName: params.displayName,
|
|
258
|
-
latest: {
|
|
259
|
-
version: params.version,
|
|
260
|
-
publishedAt: params.publishedAt,
|
|
261
|
-
commit: latestCommitSha ? commitUrl(repo, latestCommitSha) : null,
|
|
262
|
-
},
|
|
263
|
-
history: history.slice(0, 200),
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
async function fetchMetaFile(
|
|
268
|
-
token: string,
|
|
269
|
-
repoOwner: string,
|
|
270
|
-
repoName: string,
|
|
271
|
-
path: string,
|
|
272
|
-
branch: string,
|
|
273
|
-
): Promise<MetaFile | null> {
|
|
274
|
-
try {
|
|
275
|
-
const response = await githubGet<{ content?: string }>(
|
|
276
|
-
token,
|
|
277
|
-
`/repos/${repoOwner}/${repoName}/contents/${encodePath(path)}?ref=${branch}`,
|
|
278
|
-
)
|
|
279
|
-
if (!response.content) return null
|
|
280
|
-
const raw = fromBase64(response.content)
|
|
281
|
-
return JSON.parse(raw) as MetaFile
|
|
282
|
-
} catch (error) {
|
|
283
|
-
if (isNotFoundError(error)) return null
|
|
284
|
-
throw error
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async function fetchStorageBase64(ctx: ActionCtx, storageId: Id<'_storage'>) {
|
|
289
|
-
const blob = await ctx.storage.get(storageId)
|
|
290
|
-
if (!blob) throw new Error('File missing in storage')
|
|
291
|
-
const buffer = Buffer.from(await blob.arrayBuffer())
|
|
292
|
-
return buffer.toString('base64')
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async function createInstallationToken() {
|
|
296
|
-
const appId = process.env.GITHUB_APP_ID
|
|
297
|
-
const installationId = process.env.GITHUB_APP_INSTALLATION_ID
|
|
298
|
-
if (!appId || !installationId) {
|
|
299
|
-
throw new Error('GitHub App credentials missing')
|
|
300
|
-
}
|
|
301
|
-
const jwt = createAppJwt(appId)
|
|
302
|
-
const response = await fetch(`${GITHUB_API}/app/installations/${installationId}/access_tokens`, {
|
|
303
|
-
method: 'POST',
|
|
304
|
-
headers: buildHeaders(jwt, true),
|
|
305
|
-
})
|
|
306
|
-
if (!response.ok) {
|
|
307
|
-
const message = await response.text()
|
|
308
|
-
throw new Error(`GitHub App token failed: ${message}`)
|
|
309
|
-
}
|
|
310
|
-
const payload = (await response.json()) as { token?: string }
|
|
311
|
-
if (!payload.token) throw new Error('GitHub App token missing')
|
|
312
|
-
return payload.token
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function createAppJwt(appId: string) {
|
|
316
|
-
const privateKey = loadPrivateKey()
|
|
317
|
-
const now = Math.floor(Date.now() / 1000)
|
|
318
|
-
const header = { alg: 'RS256', typ: 'JWT' }
|
|
319
|
-
const payload = { iat: now - 60, exp: now + 9 * 60, iss: appId }
|
|
320
|
-
const encodedHeader = base64Url(JSON.stringify(header))
|
|
321
|
-
const encodedPayload = base64Url(JSON.stringify(payload))
|
|
322
|
-
const signingInput = `${encodedHeader}.${encodedPayload}`
|
|
323
|
-
const sign = createSign('RSA-SHA256')
|
|
324
|
-
sign.update(signingInput)
|
|
325
|
-
sign.end()
|
|
326
|
-
const signature = sign.sign(privateKey)
|
|
327
|
-
return `${signingInput}.${base64Url(signature)}`
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function loadPrivateKey() {
|
|
331
|
-
const raw = process.env.GITHUB_APP_PRIVATE_KEY
|
|
332
|
-
if (!raw) throw new Error('GITHUB_APP_PRIVATE_KEY is not configured')
|
|
333
|
-
const normalized = raw.replace(/\\n/g, '\n')
|
|
334
|
-
return createPrivateKey(normalized)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async function createBlob(token: string, repoOwner: string, repoName: string, content: string) {
|
|
338
|
-
const result = await githubPost<{ sha: string }>(
|
|
339
|
-
token,
|
|
340
|
-
`/repos/${repoOwner}/${repoName}/git/blobs`,
|
|
341
|
-
{
|
|
342
|
-
content,
|
|
343
|
-
encoding: 'base64',
|
|
344
|
-
},
|
|
345
|
-
)
|
|
346
|
-
if (!result.sha) throw new Error('GitHub blob missing sha')
|
|
347
|
-
return result.sha
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function githubGet<T>(token: string, path: string): Promise<T> {
|
|
351
|
-
const response = await fetch(`${GITHUB_API}${path}`, {
|
|
352
|
-
headers: buildHeaders(token),
|
|
353
|
-
})
|
|
354
|
-
if (!response.ok) {
|
|
355
|
-
const message = await response.text()
|
|
356
|
-
throw new Error(`GitHub GET ${path} failed: ${message}`)
|
|
357
|
-
}
|
|
358
|
-
return (await response.json()) as T
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
async function githubPost<T>(token: string, path: string, body: unknown): Promise<T> {
|
|
362
|
-
const response = await fetch(`${GITHUB_API}${path}`, {
|
|
363
|
-
method: 'POST',
|
|
364
|
-
headers: buildHeaders(token),
|
|
365
|
-
body: JSON.stringify(body),
|
|
366
|
-
})
|
|
367
|
-
if (!response.ok) {
|
|
368
|
-
const message = await response.text()
|
|
369
|
-
throw new Error(`GitHub POST ${path} failed: ${message}`)
|
|
370
|
-
}
|
|
371
|
-
return (await response.json()) as T
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
async function githubPatch(token: string, path: string, body: unknown) {
|
|
375
|
-
const response = await fetch(`${GITHUB_API}${path}`, {
|
|
376
|
-
method: 'PATCH',
|
|
377
|
-
headers: buildHeaders(token),
|
|
378
|
-
body: JSON.stringify(body),
|
|
379
|
-
})
|
|
380
|
-
if (!response.ok) {
|
|
381
|
-
const message = await response.text()
|
|
382
|
-
throw new Error(`GitHub PATCH ${path} failed: ${message}`)
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function buildHeaders(token: string, isAppJwt = false) {
|
|
387
|
-
return {
|
|
388
|
-
Authorization: `${isAppJwt ? 'Bearer' : 'token'} ${token}`,
|
|
389
|
-
Accept: 'application/vnd.github+json',
|
|
390
|
-
'User-Agent': USER_AGENT,
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function parseRepo(repo: string) {
|
|
395
|
-
const [owner, name] = repo.split('/')
|
|
396
|
-
if (!owner || !name) throw new Error('GITHUB_SOULS_REPO must be owner/repo')
|
|
397
|
-
return [owner, name] as const
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function normalizeOwner(value: string) {
|
|
401
|
-
const normalized = value
|
|
402
|
-
.trim()
|
|
403
|
-
.toLowerCase()
|
|
404
|
-
.replace(/[^a-z0-9-]/g, '-')
|
|
405
|
-
.replace(/-+/g, '-')
|
|
406
|
-
.replace(/^-+|-+$/g, '')
|
|
407
|
-
return normalized || 'unknown'
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function commitUrl(repo: string, sha: string) {
|
|
411
|
-
return `https://github.com/${repo}/commit/${sha}`
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function buildSoulRoot(root: string, ownerHandle: string, slug: string) {
|
|
415
|
-
const ownerSegment = normalizeOwner(ownerHandle)
|
|
416
|
-
return `${root}/${ownerSegment}/${slug}`
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function encodePath(path: string) {
|
|
420
|
-
return path
|
|
421
|
-
.split('/')
|
|
422
|
-
.map((segment) => encodeURIComponent(segment))
|
|
423
|
-
.join('/')
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
function base64Url(value: string | Buffer) {
|
|
427
|
-
const buffer = typeof value === 'string' ? Buffer.from(value) : value
|
|
428
|
-
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function toBase64(value: string) {
|
|
432
|
-
return Buffer.from(value).toString('base64')
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function fromBase64(value: string) {
|
|
436
|
-
return Buffer.from(value, 'base64').toString('utf8')
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function isNotFoundError(error: unknown) {
|
|
440
|
-
return (
|
|
441
|
-
error instanceof Error && (error.message.includes('404') || error.message.includes('Not Found'))
|
|
442
|
-
)
|
|
443
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { Id } from '../_generated/dataModel'
|
|
2
|
-
import type { MutationCtx, QueryCtx } from '../_generated/server'
|
|
3
|
-
|
|
4
|
-
const DAY_MS = 24 * 60 * 60 * 1000
|
|
5
|
-
export const TRENDING_DAYS = 7
|
|
6
|
-
|
|
7
|
-
type LeaderboardEntry = {
|
|
8
|
-
skillId: Id<'skills'>
|
|
9
|
-
score: number
|
|
10
|
-
installs: number
|
|
11
|
-
downloads: number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function toDayKey(timestamp: number) {
|
|
15
|
-
return Math.floor(timestamp / DAY_MS)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getTrendingRange(now: number) {
|
|
19
|
-
const endDay = toDayKey(now)
|
|
20
|
-
const startDay = endDay - (TRENDING_DAYS - 1)
|
|
21
|
-
return { startDay, endDay }
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export async function buildTrendingLeaderboard(
|
|
25
|
-
ctx: QueryCtx | MutationCtx,
|
|
26
|
-
params: { limit: number; now?: number },
|
|
27
|
-
) {
|
|
28
|
-
const now = params.now ?? Date.now()
|
|
29
|
-
const { startDay, endDay } = getTrendingRange(now)
|
|
30
|
-
const rows = await ctx.db
|
|
31
|
-
.query('skillDailyStats')
|
|
32
|
-
.withIndex('by_day', (q) => q.gte('day', startDay).lte('day', endDay))
|
|
33
|
-
.collect()
|
|
34
|
-
|
|
35
|
-
const totals = new Map<Id<'skills'>, { installs: number; downloads: number }>()
|
|
36
|
-
for (const row of rows) {
|
|
37
|
-
const current = totals.get(row.skillId) ?? { installs: 0, downloads: 0 }
|
|
38
|
-
current.installs += row.installs
|
|
39
|
-
current.downloads += row.downloads
|
|
40
|
-
totals.set(row.skillId, current)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const entries = Array.from(totals, ([skillId, totalsEntry]) => ({
|
|
44
|
-
skillId,
|
|
45
|
-
installs: totalsEntry.installs,
|
|
46
|
-
downloads: totalsEntry.downloads,
|
|
47
|
-
score: totalsEntry.installs,
|
|
48
|
-
}))
|
|
49
|
-
|
|
50
|
-
const items = topN(entries, params.limit, compareTrendingEntries).sort((a, b) =>
|
|
51
|
-
compareTrendingEntries(b, a),
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
return { startDay, endDay, items }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function compareTrendingEntries(a: LeaderboardEntry, b: LeaderboardEntry) {
|
|
58
|
-
if (a.score !== b.score) return a.score - b.score
|
|
59
|
-
if (a.downloads !== b.downloads) return a.downloads - b.downloads
|
|
60
|
-
return 0
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function topN<T>(entries: T[], limit: number, compare: (a: T, b: T) => number) {
|
|
64
|
-
if (entries.length <= limit) return entries.slice()
|
|
65
|
-
|
|
66
|
-
const heap: T[] = []
|
|
67
|
-
for (const entry of entries) {
|
|
68
|
-
if (heap.length < limit) {
|
|
69
|
-
heap.push(entry)
|
|
70
|
-
siftUp(heap, heap.length - 1, compare)
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
if (compare(entry, heap[0]) <= 0) continue
|
|
74
|
-
heap[0] = entry
|
|
75
|
-
siftDown(heap, 0, compare)
|
|
76
|
-
}
|
|
77
|
-
return heap
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function siftUp<T>(heap: T[], index: number, compare: (a: T, b: T) => number) {
|
|
81
|
-
let current = index
|
|
82
|
-
while (current > 0) {
|
|
83
|
-
const parent = Math.floor((current - 1) / 2)
|
|
84
|
-
if (compare(heap[current], heap[parent]) >= 0) break
|
|
85
|
-
;[heap[current], heap[parent]] = [heap[parent], heap[current]]
|
|
86
|
-
current = parent
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function siftDown<T>(heap: T[], index: number, compare: (a: T, b: T) => number) {
|
|
91
|
-
let current = index
|
|
92
|
-
const length = heap.length
|
|
93
|
-
while (true) {
|
|
94
|
-
const left = current * 2 + 1
|
|
95
|
-
const right = current * 2 + 2
|
|
96
|
-
let smallest = current
|
|
97
|
-
if (left < length && compare(heap[left], heap[smallest]) < 0) smallest = left
|
|
98
|
-
if (right < length && compare(heap[right], heap[smallest]) < 0) smallest = right
|
|
99
|
-
if (smallest === current) break
|
|
100
|
-
;[heap[current], heap[smallest]] = [heap[smallest], heap[current]]
|
|
101
|
-
current = smallest
|
|
102
|
-
}
|
|
103
|
-
}
|
package/convex/lib/moderation.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { Doc } from '../_generated/dataModel'
|
|
2
|
-
|
|
3
|
-
const FLAG_RULES: Array<{ flag: string; pattern: RegExp }> = [
|
|
4
|
-
{ flag: 'suspicious.keyword', pattern: /(malware|stealer|phish|phishing|keylogger)/i },
|
|
5
|
-
{ flag: 'suspicious.secrets', pattern: /(api[-_ ]?key|token|password|private key|secret)/i },
|
|
6
|
-
{ flag: 'suspicious.crypto', pattern: /(wallet|seed phrase|mnemonic|crypto)/i },
|
|
7
|
-
{ flag: 'suspicious.webhook', pattern: /(discord\.gg|webhook|hooks\.slack)/i },
|
|
8
|
-
{ flag: 'suspicious.script', pattern: /(curl[^\n]+\|\s*(sh|bash))/i },
|
|
9
|
-
{ flag: 'suspicious.url_shortener', pattern: /(bit\.ly|tinyurl\.com|t\.co|goo\.gl|is\.gd)/i },
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
export function deriveModerationFlags({
|
|
13
|
-
skill,
|
|
14
|
-
parsed,
|
|
15
|
-
files,
|
|
16
|
-
}: {
|
|
17
|
-
skill: Pick<Doc<'skills'>, 'slug' | 'displayName' | 'summary'>
|
|
18
|
-
parsed: Doc<'skillVersions'>['parsed']
|
|
19
|
-
files: Doc<'skillVersions'>['files']
|
|
20
|
-
}) {
|
|
21
|
-
const text = [
|
|
22
|
-
skill.slug,
|
|
23
|
-
skill.displayName,
|
|
24
|
-
skill.summary ?? '',
|
|
25
|
-
JSON.stringify(parsed?.frontmatter ?? {}),
|
|
26
|
-
JSON.stringify(parsed?.metadata ?? {}),
|
|
27
|
-
JSON.stringify((parsed as { pilotbot?: unknown } | undefined)?.pilotbot ?? {}),
|
|
28
|
-
...files.map((file) => file.path),
|
|
29
|
-
]
|
|
30
|
-
.filter(Boolean)
|
|
31
|
-
.join('\n')
|
|
32
|
-
|
|
33
|
-
const flags = new Set<string>()
|
|
34
|
-
|
|
35
|
-
for (const rule of FLAG_RULES) {
|
|
36
|
-
if (rule.pattern.test(text)) {
|
|
37
|
-
flags.add(rule.flag)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return Array.from(flags)
|
|
42
|
-
}
|
package/convex/lib/public.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { Doc } from '../_generated/dataModel'
|
|
2
|
-
|
|
3
|
-
export type PublicUser = Pick<
|
|
4
|
-
Doc<'users'>,
|
|
5
|
-
'_id' | '_creationTime' | 'handle' | 'name' | 'displayName' | 'image' | 'bio'
|
|
6
|
-
>
|
|
7
|
-
|
|
8
|
-
export type PublicSkill = Pick<
|
|
9
|
-
Doc<'skills'>,
|
|
10
|
-
| '_id'
|
|
11
|
-
| '_creationTime'
|
|
12
|
-
| 'slug'
|
|
13
|
-
| 'displayName'
|
|
14
|
-
| 'summary'
|
|
15
|
-
| 'ownerUserId'
|
|
16
|
-
| 'canonicalSkillId'
|
|
17
|
-
| 'forkOf'
|
|
18
|
-
| 'latestVersionId'
|
|
19
|
-
| 'tags'
|
|
20
|
-
| 'badges'
|
|
21
|
-
| 'stats'
|
|
22
|
-
| 'createdAt'
|
|
23
|
-
| 'updatedAt'
|
|
24
|
-
>
|
|
25
|
-
|
|
26
|
-
export type PublicSoul = Pick<
|
|
27
|
-
Doc<'souls'>,
|
|
28
|
-
| '_id'
|
|
29
|
-
| '_creationTime'
|
|
30
|
-
| 'slug'
|
|
31
|
-
| 'displayName'
|
|
32
|
-
| 'summary'
|
|
33
|
-
| 'ownerUserId'
|
|
34
|
-
| 'latestVersionId'
|
|
35
|
-
| 'tags'
|
|
36
|
-
| 'stats'
|
|
37
|
-
| 'createdAt'
|
|
38
|
-
| 'updatedAt'
|
|
39
|
-
>
|
|
40
|
-
|
|
41
|
-
export function toPublicUser(user: Doc<'users'> | null | undefined): PublicUser | null {
|
|
42
|
-
if (!user || user.deletedAt) return null
|
|
43
|
-
return {
|
|
44
|
-
_id: user._id,
|
|
45
|
-
_creationTime: user._creationTime,
|
|
46
|
-
handle: user.handle,
|
|
47
|
-
name: user.name,
|
|
48
|
-
displayName: user.displayName,
|
|
49
|
-
image: user.image,
|
|
50
|
-
bio: user.bio,
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function toPublicSkill(skill: Doc<'skills'> | null | undefined): PublicSkill | null {
|
|
55
|
-
if (!skill || skill.softDeletedAt) return null
|
|
56
|
-
return {
|
|
57
|
-
_id: skill._id,
|
|
58
|
-
_creationTime: skill._creationTime,
|
|
59
|
-
slug: skill.slug,
|
|
60
|
-
displayName: skill.displayName,
|
|
61
|
-
summary: skill.summary,
|
|
62
|
-
ownerUserId: skill.ownerUserId,
|
|
63
|
-
canonicalSkillId: skill.canonicalSkillId,
|
|
64
|
-
forkOf: skill.forkOf,
|
|
65
|
-
latestVersionId: skill.latestVersionId,
|
|
66
|
-
tags: skill.tags,
|
|
67
|
-
badges: skill.badges,
|
|
68
|
-
stats: skill.stats,
|
|
69
|
-
createdAt: skill.createdAt,
|
|
70
|
-
updatedAt: skill.updatedAt,
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function toPublicSoul(soul: Doc<'souls'> | null | undefined): PublicSoul | null {
|
|
75
|
-
if (!soul || soul.softDeletedAt) return null
|
|
76
|
-
return {
|
|
77
|
-
_id: soul._id,
|
|
78
|
-
_creationTime: soul._creationTime,
|
|
79
|
-
slug: soul.slug,
|
|
80
|
-
displayName: soul.displayName,
|
|
81
|
-
summary: soul.summary,
|
|
82
|
-
ownerUserId: soul.ownerUserId,
|
|
83
|
-
latestVersionId: soul.latestVersionId,
|
|
84
|
-
tags: soul.tags,
|
|
85
|
-
stats: soul.stats,
|
|
86
|
-
createdAt: soul.createdAt,
|
|
87
|
-
updatedAt: soul.updatedAt,
|
|
88
|
-
}
|
|
89
|
-
}
|