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,425 +0,0 @@
|
|
|
1
|
-
import { zipSync } from 'fflate'
|
|
2
|
-
import { TEXT_FILE_EXTENSION_SET } from 'pilothub-schema'
|
|
3
|
-
import semver from 'semver'
|
|
4
|
-
import { parseFrontmatter } from './skills'
|
|
5
|
-
|
|
6
|
-
export type GitHubImportUrl = {
|
|
7
|
-
owner: string
|
|
8
|
-
repo: string
|
|
9
|
-
ref?: string
|
|
10
|
-
path?: string
|
|
11
|
-
originalUrl: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type GitHubImportResolved = {
|
|
15
|
-
owner: string
|
|
16
|
-
repo: string
|
|
17
|
-
ref: string
|
|
18
|
-
commit: string
|
|
19
|
-
path: string
|
|
20
|
-
repoUrl: string
|
|
21
|
-
originalUrl: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type GitHubImportCandidate = {
|
|
25
|
-
path: string
|
|
26
|
-
readmePath: string
|
|
27
|
-
name?: string
|
|
28
|
-
description?: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type GitHubImportFileEntry = {
|
|
32
|
-
path: string
|
|
33
|
-
size: number
|
|
34
|
-
defaultSelected: boolean
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const MAX_REDIRECTS = 6
|
|
38
|
-
const GITHUB_HOST = 'github.com'
|
|
39
|
-
const CODELOAD_HOST = 'codeload.github.com'
|
|
40
|
-
const SKILL_FILENAMES = ['skill.md', 'skills.md']
|
|
41
|
-
|
|
42
|
-
export function parseGitHubImportUrl(input: string): GitHubImportUrl {
|
|
43
|
-
const originalUrl = input.trim()
|
|
44
|
-
let url: URL
|
|
45
|
-
try {
|
|
46
|
-
url = new URL(originalUrl)
|
|
47
|
-
} catch {
|
|
48
|
-
throw new Error('Invalid URL')
|
|
49
|
-
}
|
|
50
|
-
if (url.protocol !== 'https:') throw new Error('Only https:// URLs are supported')
|
|
51
|
-
if (url.hostname !== GITHUB_HOST) throw new Error('Only github.com URLs are supported')
|
|
52
|
-
|
|
53
|
-
const segments = url.pathname
|
|
54
|
-
.split('/')
|
|
55
|
-
.map((segment) => segment.trim())
|
|
56
|
-
.filter(Boolean)
|
|
57
|
-
.map((segment) => {
|
|
58
|
-
try {
|
|
59
|
-
return decodeURIComponent(segment)
|
|
60
|
-
} catch {
|
|
61
|
-
throw new Error('Invalid URL')
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
const owner = segments[0] ?? ''
|
|
66
|
-
const repo = (segments[1] ?? '').replace(/\.git$/, '')
|
|
67
|
-
if (!owner || !repo) throw new Error('GitHub URL must be /<owner>/<repo>')
|
|
68
|
-
|
|
69
|
-
const kind = segments[2] ?? ''
|
|
70
|
-
if (!kind) return { owner, repo, originalUrl }
|
|
71
|
-
if (kind !== 'tree' && kind !== 'blob') {
|
|
72
|
-
return { owner, repo, originalUrl }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const ref = segments[3] ?? ''
|
|
76
|
-
if (!ref) throw new Error('Missing ref in GitHub URL')
|
|
77
|
-
|
|
78
|
-
const rest = segments.slice(4).join('/')
|
|
79
|
-
const normalizedRest = normalizeRepoPath(rest)
|
|
80
|
-
|
|
81
|
-
if (kind === 'blob') {
|
|
82
|
-
if (!rest) throw new Error('Missing path in GitHub URL')
|
|
83
|
-
if (!normalizedRest) throw new Error('Invalid path in GitHub URL')
|
|
84
|
-
const dir = normalizedRest.split('/').slice(0, -1).join('/')
|
|
85
|
-
return { owner, repo, ref, path: dir || undefined, originalUrl }
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (rest && !normalizedRest) throw new Error('Invalid path in GitHub URL')
|
|
89
|
-
return { owner, repo, ref, path: normalizedRest || undefined, originalUrl }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export async function resolveGitHubCommit(
|
|
93
|
-
parsed: GitHubImportUrl,
|
|
94
|
-
fetcher: typeof fetch,
|
|
95
|
-
): Promise<GitHubImportResolved> {
|
|
96
|
-
const repoUrl = `https://${GITHUB_HOST}/${parsed.owner}/${parsed.repo}`
|
|
97
|
-
const ref = parsed.ref?.trim() || 'HEAD'
|
|
98
|
-
const path = normalizeRepoPath(parsed.path ?? '')
|
|
99
|
-
|
|
100
|
-
const commit =
|
|
101
|
-
ref === 'HEAD'
|
|
102
|
-
? await resolveHeadCommit(parsed, fetcher)
|
|
103
|
-
: await resolveRefCommit(parsed, ref, fetcher)
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
owner: parsed.owner,
|
|
107
|
-
repo: parsed.repo,
|
|
108
|
-
ref,
|
|
109
|
-
commit,
|
|
110
|
-
path,
|
|
111
|
-
repoUrl,
|
|
112
|
-
originalUrl: parsed.originalUrl,
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function resolveRefCommit(parsed: GitHubImportUrl, ref: string, fetcher: typeof fetch) {
|
|
117
|
-
const apiUrl = `https://api.github.com/repos/${parsed.owner}/${parsed.repo}/commits/${encodeURIComponent(ref)}`
|
|
118
|
-
const response = await fetcher(apiUrl, {
|
|
119
|
-
headers: {
|
|
120
|
-
Accept: 'application/vnd.github+json',
|
|
121
|
-
'User-Agent': 'pilothub/github-import',
|
|
122
|
-
},
|
|
123
|
-
})
|
|
124
|
-
if (!response.ok) throw new Error('GitHub ref not found')
|
|
125
|
-
const body = (await response.json()) as { sha?: unknown }
|
|
126
|
-
const sha = typeof body.sha === 'string' ? body.sha : ''
|
|
127
|
-
if (!/^[a-f0-9]{40}$/i.test(sha)) throw new Error('GitHub commit sha missing')
|
|
128
|
-
return sha.toLowerCase()
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function resolveHeadCommit(parsed: GitHubImportUrl, fetcher: typeof fetch) {
|
|
132
|
-
let url = `https://${GITHUB_HOST}/${parsed.owner}/${parsed.repo}/archive/HEAD.zip`
|
|
133
|
-
for (let i = 0; i < MAX_REDIRECTS; i += 1) {
|
|
134
|
-
const response = await fetcher(url, { redirect: 'manual' })
|
|
135
|
-
const location = response.headers.get('location')
|
|
136
|
-
if (!location) break
|
|
137
|
-
const next = new URL(location, url)
|
|
138
|
-
if (next.hostname !== GITHUB_HOST && next.hostname !== CODELOAD_HOST) {
|
|
139
|
-
throw new Error('Unexpected redirect host')
|
|
140
|
-
}
|
|
141
|
-
url = next.toString()
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const maybe = url.split('/').at(-1) ?? ''
|
|
145
|
-
if (!/^[a-f0-9]{40}$/i.test(maybe)) {
|
|
146
|
-
throw new Error('Could not resolve commit for HEAD')
|
|
147
|
-
}
|
|
148
|
-
return maybe.toLowerCase()
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export async function fetchGitHubZipBytes(
|
|
152
|
-
resolved: GitHubImportResolved,
|
|
153
|
-
fetcher: typeof fetch,
|
|
154
|
-
limits?: { maxZipBytes?: number },
|
|
155
|
-
): Promise<Uint8Array> {
|
|
156
|
-
const maxZipBytes = limits?.maxZipBytes ?? 25 * 1024 * 1024
|
|
157
|
-
const url = `https://${CODELOAD_HOST}/${resolved.owner}/${resolved.repo}/zip/${resolved.commit}`
|
|
158
|
-
const response = await fetcher(url, {
|
|
159
|
-
headers: { 'User-Agent': 'pilothub/github-import' },
|
|
160
|
-
})
|
|
161
|
-
if (!response.ok) throw new Error('GitHub archive download failed')
|
|
162
|
-
|
|
163
|
-
const lengthHeader = response.headers.get('content-length')
|
|
164
|
-
if (lengthHeader) {
|
|
165
|
-
const contentLength = Number.parseInt(lengthHeader, 10)
|
|
166
|
-
if (Number.isFinite(contentLength) && contentLength > maxZipBytes) {
|
|
167
|
-
throw new Error('GitHub archive too large')
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const reader = response.body?.getReader()
|
|
172
|
-
if (!reader) {
|
|
173
|
-
const buffer = new Uint8Array(await response.arrayBuffer())
|
|
174
|
-
if (buffer.byteLength > maxZipBytes) throw new Error('GitHub archive too large')
|
|
175
|
-
return buffer
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const chunks: Uint8Array[] = []
|
|
179
|
-
let total = 0
|
|
180
|
-
while (true) {
|
|
181
|
-
const { done, value } = await reader.read()
|
|
182
|
-
if (done) break
|
|
183
|
-
if (!value) continue
|
|
184
|
-
total += value.byteLength
|
|
185
|
-
if (total > maxZipBytes) throw new Error('GitHub archive too large')
|
|
186
|
-
chunks.push(value)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const out = new Uint8Array(total)
|
|
190
|
-
let offset = 0
|
|
191
|
-
for (const chunk of chunks) {
|
|
192
|
-
out.set(chunk, offset)
|
|
193
|
-
offset += chunk.byteLength
|
|
194
|
-
}
|
|
195
|
-
return out
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export type ZipEntryMap = Record<string, Uint8Array>
|
|
199
|
-
|
|
200
|
-
export function buildGitHubZipForTests(entries: Record<string, string>) {
|
|
201
|
-
const asBytes = Object.fromEntries(
|
|
202
|
-
Object.entries(entries).map(([path, text]) => [path, new TextEncoder().encode(text)]),
|
|
203
|
-
)
|
|
204
|
-
return Uint8Array.from(zipSync(asBytes, { level: 1 }))
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function stripGitHubZipRoot(entries: ZipEntryMap): ZipEntryMap {
|
|
208
|
-
const paths = Object.keys(entries)
|
|
209
|
-
if (paths.length === 0) return {}
|
|
210
|
-
const first = paths[0] ?? ''
|
|
211
|
-
const firstRoot = first.split('/')[0] ?? ''
|
|
212
|
-
if (!firstRoot) return entries
|
|
213
|
-
const prefix = `${firstRoot}/`
|
|
214
|
-
if (!paths.every((path) => path.startsWith(prefix))) return entries
|
|
215
|
-
const out: ZipEntryMap = {}
|
|
216
|
-
for (const [path, data] of Object.entries(entries)) {
|
|
217
|
-
const stripped = path.slice(prefix.length)
|
|
218
|
-
if (!stripped) continue
|
|
219
|
-
out[stripped] = data
|
|
220
|
-
}
|
|
221
|
-
return out
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function detectGitHubImportCandidates(entries: ZipEntryMap): GitHubImportCandidate[] {
|
|
225
|
-
const candidates: GitHubImportCandidate[] = []
|
|
226
|
-
for (const path of Object.keys(entries)) {
|
|
227
|
-
const normalized = normalizeRepoPath(path)
|
|
228
|
-
const lower = normalized.toLowerCase()
|
|
229
|
-
const isSkill = SKILL_FILENAMES.some((name) => lower === name || lower.endsWith(`/${name}`))
|
|
230
|
-
if (!isSkill) continue
|
|
231
|
-
const dir = normalized.split('/').slice(0, -1).join('/')
|
|
232
|
-
const readmePath = normalized
|
|
233
|
-
const raw = new TextDecoder().decode(entries[path] ?? new Uint8Array())
|
|
234
|
-
const frontmatter = parseFrontmatter(raw)
|
|
235
|
-
const name = typeof frontmatter.name === 'string' ? frontmatter.name : undefined
|
|
236
|
-
const description =
|
|
237
|
-
typeof frontmatter.description === 'string' ? frontmatter.description : undefined
|
|
238
|
-
candidates.push({
|
|
239
|
-
path: normalizeRepoPath(dir),
|
|
240
|
-
readmePath,
|
|
241
|
-
name: name?.trim() || undefined,
|
|
242
|
-
description: description?.trim() || undefined,
|
|
243
|
-
})
|
|
244
|
-
}
|
|
245
|
-
return uniqCandidates(candidates)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function uniqCandidates(candidates: GitHubImportCandidate[]) {
|
|
249
|
-
const seen = new Set<string>()
|
|
250
|
-
const out: GitHubImportCandidate[] = []
|
|
251
|
-
for (const candidate of candidates) {
|
|
252
|
-
const key = `${candidate.path}::${candidate.readmePath}`
|
|
253
|
-
if (seen.has(key)) continue
|
|
254
|
-
seen.add(key)
|
|
255
|
-
out.push(candidate)
|
|
256
|
-
}
|
|
257
|
-
return out.sort((a, b) => a.path.localeCompare(b.path))
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export function listTextFilesUnderCandidate(
|
|
261
|
-
entries: ZipEntryMap,
|
|
262
|
-
candidatePath: string,
|
|
263
|
-
): Array<{ path: string; bytes: Uint8Array }> {
|
|
264
|
-
const root = normalizeCandidateRoot(candidatePath)
|
|
265
|
-
const out: Array<{ path: string; bytes: Uint8Array }> = []
|
|
266
|
-
for (const [path, bytes] of Object.entries(entries)) {
|
|
267
|
-
const normalized = normalizeRepoPath(path)
|
|
268
|
-
if (!isUnderRoot(normalized, root)) continue
|
|
269
|
-
if (!isTextPath(normalized)) continue
|
|
270
|
-
out.push({ path: normalized, bytes })
|
|
271
|
-
}
|
|
272
|
-
return out.sort((a, b) => a.path.localeCompare(b.path))
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
export function computeDefaultSelectedPaths(params: {
|
|
276
|
-
candidate: GitHubImportCandidate
|
|
277
|
-
files: Array<{ path: string; bytes: Uint8Array }>
|
|
278
|
-
maxDepth?: number
|
|
279
|
-
maxAdds?: number
|
|
280
|
-
}) {
|
|
281
|
-
const maxDepth = params.maxDepth ?? 4
|
|
282
|
-
const maxAdds = params.maxAdds ?? 200
|
|
283
|
-
const byPath = new Map(params.files.map((file) => [file.path, file.bytes]))
|
|
284
|
-
const candidateRoot = normalizeCandidateRoot(params.candidate.path)
|
|
285
|
-
const selected = new Set<string>()
|
|
286
|
-
let added = 0
|
|
287
|
-
|
|
288
|
-
const add = (path: string) => {
|
|
289
|
-
const normalized = normalizeRepoPath(path)
|
|
290
|
-
if (!isUnderRoot(normalized, candidateRoot)) return
|
|
291
|
-
if (!byPath.has(normalized)) return
|
|
292
|
-
if (!selected.has(normalized)) {
|
|
293
|
-
selected.add(normalized)
|
|
294
|
-
added += 1
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
add(params.candidate.readmePath)
|
|
299
|
-
|
|
300
|
-
const visited = new Set<string>()
|
|
301
|
-
const queue: Array<{ path: string; depth: number }> = [
|
|
302
|
-
{ path: params.candidate.readmePath, depth: 0 },
|
|
303
|
-
]
|
|
304
|
-
|
|
305
|
-
while (queue.length > 0) {
|
|
306
|
-
const item = queue.shift()
|
|
307
|
-
if (!item) break
|
|
308
|
-
if (item.depth >= maxDepth) continue
|
|
309
|
-
if (visited.has(item.path)) continue
|
|
310
|
-
visited.add(item.path)
|
|
311
|
-
|
|
312
|
-
const bytes = byPath.get(item.path)
|
|
313
|
-
if (!bytes) continue
|
|
314
|
-
if (!item.path.toLowerCase().endsWith('.md')) continue
|
|
315
|
-
|
|
316
|
-
const text = new TextDecoder().decode(bytes)
|
|
317
|
-
const refs = extractMarkdownRelativeTargets(text)
|
|
318
|
-
for (const ref of refs) {
|
|
319
|
-
if (added >= maxAdds) break
|
|
320
|
-
const resolved = resolveMarkdownTarget(item.path, ref)
|
|
321
|
-
if (!resolved) continue
|
|
322
|
-
add(resolved)
|
|
323
|
-
if (resolved.toLowerCase().endsWith('.md') && byPath.has(resolved)) {
|
|
324
|
-
queue.push({ path: resolved, depth: item.depth + 1 })
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
if (added >= maxAdds) break
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return Array.from(selected).sort()
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
export function buildGitHubImportFileList(params: {
|
|
334
|
-
candidate: GitHubImportCandidate
|
|
335
|
-
files: Array<{ path: string; bytes: Uint8Array }>
|
|
336
|
-
defaultSelectedPaths: string[]
|
|
337
|
-
}): GitHubImportFileEntry[] {
|
|
338
|
-
const selected = new Set(params.defaultSelectedPaths)
|
|
339
|
-
return params.files.map((file) => ({
|
|
340
|
-
path: file.path,
|
|
341
|
-
size: file.bytes.byteLength,
|
|
342
|
-
defaultSelected: selected.has(file.path),
|
|
343
|
-
}))
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export function normalizeRepoPath(path: string) {
|
|
347
|
-
const stripped = path.replace(/^\/+/, '').trim()
|
|
348
|
-
if (!stripped) return ''
|
|
349
|
-
const cleaned = stripped.split('/').filter(Boolean).join('/')
|
|
350
|
-
if (!cleaned || cleaned.includes('\\') || cleaned.includes('..')) return ''
|
|
351
|
-
return cleaned
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export function normalizeCandidateRoot(candidatePath: string) {
|
|
355
|
-
const normalized = normalizeRepoPath(candidatePath)
|
|
356
|
-
return normalized ? `${normalized}/` : ''
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function isUnderRoot(path: string, rootWithSlash: string) {
|
|
360
|
-
if (!rootWithSlash) return true
|
|
361
|
-
return path === rootWithSlash.slice(0, -1) || path.startsWith(rootWithSlash)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function isTextPath(path: string) {
|
|
365
|
-
const lower = path.toLowerCase()
|
|
366
|
-
const ext = lower.split('.').at(-1) ?? ''
|
|
367
|
-
if (!ext) return false
|
|
368
|
-
return TEXT_FILE_EXTENSION_SET.has(ext)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export function suggestDisplayName(candidate: GitHubImportCandidate, fallbackBase: string) {
|
|
372
|
-
const base = candidate.name?.trim() || fallbackBase.trim()
|
|
373
|
-
if (!base) return ''
|
|
374
|
-
return base
|
|
375
|
-
.replace(/[-_]+/g, ' ')
|
|
376
|
-
.replace(/\s+/g, ' ')
|
|
377
|
-
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export function suggestVersion(latestVersion?: string | null) {
|
|
381
|
-
const latest = latestVersion?.trim() || ''
|
|
382
|
-
if (latest && semver.valid(latest)) {
|
|
383
|
-
return semver.inc(latest, 'patch') ?? '0.1.0'
|
|
384
|
-
}
|
|
385
|
-
return '0.1.0'
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
export function extractMarkdownRelativeTargets(markdown: string): string[] {
|
|
389
|
-
const out: string[] = []
|
|
390
|
-
const pattern = /!?\[[^\]]*]\(([^)]+)\)/g
|
|
391
|
-
for (const match of markdown.matchAll(pattern)) {
|
|
392
|
-
const raw = (match[1] ?? '').trim()
|
|
393
|
-
if (!raw) continue
|
|
394
|
-
const isAngleWrapped = raw.startsWith('<') && raw.endsWith('>')
|
|
395
|
-
const cleaned = raw.replace(/^<|>$/g, '').trim()
|
|
396
|
-
if (!cleaned) continue
|
|
397
|
-
const target = isAngleWrapped ? cleaned : (cleaned.split(/\s+/)[0] ?? '')
|
|
398
|
-
if (!target) continue
|
|
399
|
-
if (target.startsWith('#')) continue
|
|
400
|
-
const lower = target.toLowerCase()
|
|
401
|
-
if (lower.startsWith('http:') || lower.startsWith('https:')) continue
|
|
402
|
-
if (lower.startsWith('mailto:')) continue
|
|
403
|
-
out.push(target)
|
|
404
|
-
}
|
|
405
|
-
return out
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export function resolveMarkdownTarget(fromPath: string, target: string) {
|
|
409
|
-
const withoutHash = target.split('#')[0] ?? ''
|
|
410
|
-
const withoutQuery = (withoutHash.split('?')[0] ?? '').trim()
|
|
411
|
-
if (!withoutQuery) return null
|
|
412
|
-
if (withoutQuery.startsWith('/')) return null
|
|
413
|
-
if (withoutQuery.includes('\\') || withoutQuery.includes('..')) return null
|
|
414
|
-
|
|
415
|
-
const fromDirParts = normalizeRepoPath(fromPath).split('/').slice(0, -1)
|
|
416
|
-
const targetParts = withoutQuery.split('/').filter(Boolean)
|
|
417
|
-
const combined = [...fromDirParts, ...targetParts]
|
|
418
|
-
const normalized: string[] = []
|
|
419
|
-
for (const part of combined) {
|
|
420
|
-
if (part === '.') continue
|
|
421
|
-
if (part === '..') return null
|
|
422
|
-
normalized.push(part)
|
|
423
|
-
}
|
|
424
|
-
return normalizeRepoPath(normalized.join('/')) || null
|
|
425
|
-
}
|