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,197 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
buildEmbeddingText,
|
|
4
|
-
getFrontmatterMetadata,
|
|
5
|
-
getFrontmatterValue,
|
|
6
|
-
hashSkillFiles,
|
|
7
|
-
isTextFile,
|
|
8
|
-
parseFrontmatter,
|
|
9
|
-
parsePilotbotMetadata,
|
|
10
|
-
sanitizePath,
|
|
11
|
-
} from './skills'
|
|
12
|
-
|
|
13
|
-
describe('skills utils', () => {
|
|
14
|
-
it('parses frontmatter', () => {
|
|
15
|
-
const frontmatter = parseFrontmatter(`---\nname: demo\ndescription: Hello\n---\nBody`)
|
|
16
|
-
expect(frontmatter.name).toBe('demo')
|
|
17
|
-
expect(frontmatter.description).toBe('Hello')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('handles missing or invalid frontmatter blocks', () => {
|
|
21
|
-
expect(parseFrontmatter('nope')).toEqual({})
|
|
22
|
-
expect(parseFrontmatter('---\nname: demo\nBody without end')).toEqual({})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('strips quotes in frontmatter values', () => {
|
|
26
|
-
const frontmatter = parseFrontmatter(`---\nname: "demo"\ndescription: 'Hello'\n---\nBody`)
|
|
27
|
-
expect(frontmatter.name).toBe('demo')
|
|
28
|
-
expect(frontmatter.description).toBe('Hello')
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('parses block scalars in frontmatter', () => {
|
|
32
|
-
const folded = parseFrontmatter(
|
|
33
|
-
`---\nname: demo\ndescription: >\n Hello\n world.\n\n Next paragraph.\n---\nBody`,
|
|
34
|
-
)
|
|
35
|
-
expect(folded.description).toBe('Hello world.\nNext paragraph.')
|
|
36
|
-
|
|
37
|
-
const literal = parseFrontmatter(
|
|
38
|
-
`---\nname: demo\ndescription: |\n Hello\n world.\n---\nBody`,
|
|
39
|
-
)
|
|
40
|
-
expect(literal.description).toBe('Hello\nworld.')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('keeps structured YAML values in frontmatter', () => {
|
|
44
|
-
const frontmatter = parseFrontmatter(
|
|
45
|
-
`---\nname: demo\ncount: 3\nnums: [1, 2]\nobj:\n a: b\n---\nBody`,
|
|
46
|
-
)
|
|
47
|
-
expect(frontmatter.nums).toEqual([1, 2])
|
|
48
|
-
expect(frontmatter.obj).toEqual({ a: 'b' })
|
|
49
|
-
expect(frontmatter.name).toBe('demo')
|
|
50
|
-
expect(frontmatter.count).toBe(3)
|
|
51
|
-
expect(getFrontmatterValue(frontmatter, 'count')).toBeUndefined()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('parses pilotbot metadata', () => {
|
|
55
|
-
const frontmatter = parseFrontmatter(
|
|
56
|
-
`---\nmetadata: {"pilotbot":{"requires":{"bins":["rg"]},"emoji":"🦞"}}\n---\nBody`,
|
|
57
|
-
)
|
|
58
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
59
|
-
expect(pilotbot?.emoji).toBe('🦞')
|
|
60
|
-
expect(pilotbot?.requires?.bins).toEqual(['rg'])
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('ignores invalid pilotbot metadata', () => {
|
|
64
|
-
const frontmatter = parseFrontmatter(`---\nmetadata: not-json\n---\nBody`)
|
|
65
|
-
expect(parsePilotbotMetadata(frontmatter)).toBeUndefined()
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('accepts metadata as YAML object (no JSON string)', () => {
|
|
69
|
-
const frontmatter = parseFrontmatter(
|
|
70
|
-
`---\nmetadata:\n pilotbot:\n emoji: "🦞"\n requires:\n bins:\n - rg\n---\nBody`,
|
|
71
|
-
)
|
|
72
|
-
expect(getFrontmatterMetadata(frontmatter)).toEqual({
|
|
73
|
-
pilotbot: { emoji: '🦞', requires: { bins: ['rg'] } },
|
|
74
|
-
})
|
|
75
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
76
|
-
expect(pilotbot?.emoji).toBe('🦞')
|
|
77
|
-
expect(pilotbot?.requires?.bins).toEqual(['rg'])
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('accepts pilotbot as top-level YAML key', () => {
|
|
81
|
-
const frontmatter = parseFrontmatter(
|
|
82
|
-
`---\npilotbot:\n emoji: "🦞"\n requires:\n anyBins: [rg, fd]\n---\nBody`,
|
|
83
|
-
)
|
|
84
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
85
|
-
expect(pilotbot?.emoji).toBe('🦞')
|
|
86
|
-
expect(pilotbot?.requires?.anyBins).toEqual(['rg', 'fd'])
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('accepts legacy metadata JSON string (quoted)', () => {
|
|
90
|
-
const frontmatter = parseFrontmatter(
|
|
91
|
-
`---\nmetadata: '{"pilotbot":{"emoji":"🦞","requires":{"bins":["rg"]}}}'\n---\nBody`,
|
|
92
|
-
)
|
|
93
|
-
const metadata = getFrontmatterMetadata(frontmatter)
|
|
94
|
-
expect(metadata).toEqual({ pilotbot: { emoji: '🦞', requires: { bins: ['rg'] } } })
|
|
95
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
96
|
-
expect(pilotbot?.emoji).toBe('🦞')
|
|
97
|
-
expect(pilotbot?.requires?.bins).toEqual(['rg'])
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('parses pilotbot install specs and os', () => {
|
|
101
|
-
const frontmatter = parseFrontmatter(
|
|
102
|
-
`---\nmetadata: {"pilotbot":{"install":[{"kind":"brew","formula":"rg"},{"kind":"nope"},{"kind":"node","package":"x"}],"os":"macos,linux","requires":{"anyBins":["rg","fd"]}}}\n---\nBody`,
|
|
103
|
-
)
|
|
104
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
105
|
-
expect(pilotbot?.install?.map((entry) => entry.kind)).toEqual(['brew', 'node'])
|
|
106
|
-
expect(pilotbot?.os).toEqual(['macos', 'linux'])
|
|
107
|
-
expect(pilotbot?.requires?.anyBins).toEqual(['rg', 'fd'])
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('parses pilotbot metadata with nix plugin pointer', () => {
|
|
111
|
-
const frontmatter = parseFrontmatter(
|
|
112
|
-
`---\nmetadata: {"pilotbot":{"nix":{"plugin":"github:pilotbot/nix-steipete-tools?dir=tools/peekaboo","systems":["aarch64-darwin"]}}}\n---\nBody`,
|
|
113
|
-
)
|
|
114
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
115
|
-
expect(pilotbot?.nix?.plugin).toBe('github:pilotbot/nix-steipete-tools?dir=tools/peekaboo')
|
|
116
|
-
expect(pilotbot?.nix?.systems).toEqual(['aarch64-darwin'])
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('parses pilotbot config requirements with example', () => {
|
|
120
|
-
const frontmatter = parseFrontmatter(
|
|
121
|
-
`---\nmetadata: {"pilotbot":{"config":{"requiredEnv":["PADEL_AUTH_FILE"],"stateDirs":[".config/padel"],"example":"config = { env = { PADEL_AUTH_FILE = \\"/run/agenix/padel-auth\\"; }; };"}}}\n---\nBody`,
|
|
122
|
-
)
|
|
123
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
124
|
-
expect(pilotbot?.config?.requiredEnv).toEqual(['PADEL_AUTH_FILE'])
|
|
125
|
-
expect(pilotbot?.config?.stateDirs).toEqual(['.config/padel'])
|
|
126
|
-
expect(pilotbot?.config?.example).toBe(
|
|
127
|
-
'config = { env = { PADEL_AUTH_FILE = "/run/agenix/padel-auth"; }; };',
|
|
128
|
-
)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('parses cli help output', () => {
|
|
132
|
-
const frontmatter = parseFrontmatter(
|
|
133
|
-
`---\nmetadata: {"pilotbot":{"cliHelp":"padel --help\\nUsage: padel [command]\\n"}}\n---\nBody`,
|
|
134
|
-
)
|
|
135
|
-
const pilotbot = parsePilotbotMetadata(frontmatter)
|
|
136
|
-
expect(pilotbot?.cliHelp).toBe('padel --help\nUsage: padel [command]')
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('sanitizes file paths', () => {
|
|
140
|
-
expect(sanitizePath('good/file.md')).toBe('good/file.md')
|
|
141
|
-
expect(sanitizePath('../bad/file.md')).toBeNull()
|
|
142
|
-
expect(sanitizePath('/rooted.txt')).toBe('rooted.txt')
|
|
143
|
-
expect(sanitizePath('bad\\path.txt')).toBeNull()
|
|
144
|
-
expect(sanitizePath('')).toBeNull()
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('detects text files', () => {
|
|
148
|
-
expect(isTextFile('SKILL.md')).toBe(true)
|
|
149
|
-
expect(isTextFile('image.png')).toBe(false)
|
|
150
|
-
expect(isTextFile('note.txt', 'text/plain')).toBe(true)
|
|
151
|
-
expect(isTextFile('data.any', 'application/json')).toBe(true)
|
|
152
|
-
expect(isTextFile('data.json')).toBe(true)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('builds embedding text', () => {
|
|
156
|
-
const frontmatter = { name: 'Demo', description: 'Hello' }
|
|
157
|
-
const text = buildEmbeddingText({
|
|
158
|
-
frontmatter,
|
|
159
|
-
readme: 'Readme body',
|
|
160
|
-
otherFiles: [{ path: 'a.txt', content: 'File text' }],
|
|
161
|
-
})
|
|
162
|
-
expect(text).toContain('Demo')
|
|
163
|
-
expect(text).toContain('Readme body')
|
|
164
|
-
expect(text).toContain('a.txt')
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('truncates embedding text by maxChars', () => {
|
|
168
|
-
const text = buildEmbeddingText({
|
|
169
|
-
frontmatter: {},
|
|
170
|
-
readme: 'x'.repeat(50),
|
|
171
|
-
otherFiles: [],
|
|
172
|
-
maxChars: 10,
|
|
173
|
-
})
|
|
174
|
-
expect(text.length).toBe(10)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('truncates embedding text by default max chars', () => {
|
|
178
|
-
const text = buildEmbeddingText({
|
|
179
|
-
frontmatter: {},
|
|
180
|
-
readme: 'x'.repeat(40_000),
|
|
181
|
-
otherFiles: [],
|
|
182
|
-
})
|
|
183
|
-
expect(text.length).toBeLessThanOrEqual(12_000)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('hashes skill files deterministically', async () => {
|
|
187
|
-
const a = await hashSkillFiles([
|
|
188
|
-
{ path: 'b.txt', sha256: 'b' },
|
|
189
|
-
{ path: 'a.txt', sha256: 'a' },
|
|
190
|
-
])
|
|
191
|
-
const b = await hashSkillFiles([
|
|
192
|
-
{ path: 'a.txt', sha256: 'a' },
|
|
193
|
-
{ path: 'b.txt', sha256: 'b' },
|
|
194
|
-
])
|
|
195
|
-
expect(a).toBe(b)
|
|
196
|
-
})
|
|
197
|
-
})
|
package/convex/lib/skills.ts
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isTextContentType,
|
|
3
|
-
type PilotbotConfigSpec,
|
|
4
|
-
type PilotbotSkillMetadata,
|
|
5
|
-
PilotbotSkillMetadataSchema,
|
|
6
|
-
type NixPluginSpec,
|
|
7
|
-
parseArk,
|
|
8
|
-
type SkillInstallSpec,
|
|
9
|
-
TEXT_FILE_EXTENSION_SET,
|
|
10
|
-
} from 'pilothub-schema'
|
|
11
|
-
import { parse as parseYaml } from 'yaml'
|
|
12
|
-
|
|
13
|
-
export type ParsedSkillFrontmatter = Record<string, unknown>
|
|
14
|
-
export type { PilotbotSkillMetadata, SkillInstallSpec }
|
|
15
|
-
|
|
16
|
-
const FRONTMATTER_START = '---'
|
|
17
|
-
const DEFAULT_EMBEDDING_MAX_CHARS = 12_000
|
|
18
|
-
|
|
19
|
-
export function parseFrontmatter(content: string): ParsedSkillFrontmatter {
|
|
20
|
-
const frontmatter: ParsedSkillFrontmatter = {}
|
|
21
|
-
const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
|
|
22
|
-
if (!normalized.startsWith(FRONTMATTER_START)) return frontmatter
|
|
23
|
-
const endIndex = normalized.indexOf(`\n${FRONTMATTER_START}`, 3)
|
|
24
|
-
if (endIndex === -1) return frontmatter
|
|
25
|
-
const block = normalized.slice(4, endIndex)
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const parsed = parseYaml(block) as unknown
|
|
29
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return frontmatter
|
|
30
|
-
for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {
|
|
31
|
-
if (!/^[\w-]+$/.test(key)) continue
|
|
32
|
-
const jsonValue = toJsonValue(value)
|
|
33
|
-
if (jsonValue !== undefined) frontmatter[key] = jsonValue
|
|
34
|
-
}
|
|
35
|
-
} catch {
|
|
36
|
-
return frontmatter
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return frontmatter
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function getFrontmatterValue(frontmatter: ParsedSkillFrontmatter, key: string) {
|
|
43
|
-
const raw = frontmatter[key]
|
|
44
|
-
return typeof raw === 'string' ? raw : undefined
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function getFrontmatterMetadata(frontmatter: ParsedSkillFrontmatter) {
|
|
48
|
-
const raw = frontmatter.metadata
|
|
49
|
-
if (!raw) return undefined
|
|
50
|
-
if (typeof raw === 'string') {
|
|
51
|
-
try {
|
|
52
|
-
const parsed = JSON.parse(raw) as unknown
|
|
53
|
-
return parsed ?? undefined
|
|
54
|
-
} catch {
|
|
55
|
-
return undefined
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (typeof raw === 'object') return raw
|
|
59
|
-
return undefined
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function parsePilotbotMetadata(frontmatter: ParsedSkillFrontmatter) {
|
|
63
|
-
const metadata = getFrontmatterMetadata(frontmatter)
|
|
64
|
-
const metadataRecord =
|
|
65
|
-
metadata && typeof metadata === 'object' && !Array.isArray(metadata)
|
|
66
|
-
? (metadata as Record<string, unknown>)
|
|
67
|
-
: undefined
|
|
68
|
-
const pilotbotMeta = metadataRecord?.pilotbot
|
|
69
|
-
const metadataSource =
|
|
70
|
-
pilotbotMeta && typeof pilotbotMeta === 'object' && !Array.isArray(pilotbotMeta)
|
|
71
|
-
? (pilotbotMeta as Record<string, unknown>)
|
|
72
|
-
: undefined
|
|
73
|
-
const pilotbotRaw = metadataSource ?? frontmatter.pilotbot
|
|
74
|
-
if (!pilotbotRaw || typeof pilotbotRaw !== 'object' || Array.isArray(pilotbotRaw)) return undefined
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const pilotbotObj = pilotbotRaw as Record<string, unknown>
|
|
78
|
-
const requiresRaw =
|
|
79
|
-
typeof pilotbotObj.requires === 'object' && pilotbotObj.requires !== null
|
|
80
|
-
? (pilotbotObj.requires as Record<string, unknown>)
|
|
81
|
-
: undefined
|
|
82
|
-
const installRaw = Array.isArray(pilotbotObj.install) ? (pilotbotObj.install as unknown[]) : []
|
|
83
|
-
const install = installRaw
|
|
84
|
-
.map((entry) => parseInstallSpec(entry))
|
|
85
|
-
.filter((entry): entry is SkillInstallSpec => Boolean(entry))
|
|
86
|
-
const osRaw = normalizeStringList(pilotbotObj.os)
|
|
87
|
-
|
|
88
|
-
const metadata: PilotbotSkillMetadata = {}
|
|
89
|
-
if (typeof pilotbotObj.always === 'boolean') metadata.always = pilotbotObj.always
|
|
90
|
-
if (typeof pilotbotObj.emoji === 'string') metadata.emoji = pilotbotObj.emoji
|
|
91
|
-
if (typeof pilotbotObj.homepage === 'string') metadata.homepage = pilotbotObj.homepage
|
|
92
|
-
if (typeof pilotbotObj.skillKey === 'string') metadata.skillKey = pilotbotObj.skillKey
|
|
93
|
-
if (typeof pilotbotObj.primaryEnv === 'string') metadata.primaryEnv = pilotbotObj.primaryEnv
|
|
94
|
-
if (typeof pilotbotObj.cliHelp === 'string') metadata.cliHelp = pilotbotObj.cliHelp
|
|
95
|
-
if (osRaw.length > 0) metadata.os = osRaw
|
|
96
|
-
|
|
97
|
-
if (requiresRaw) {
|
|
98
|
-
const bins = normalizeStringList(requiresRaw.bins)
|
|
99
|
-
const anyBins = normalizeStringList(requiresRaw.anyBins)
|
|
100
|
-
const env = normalizeStringList(requiresRaw.env)
|
|
101
|
-
const config = normalizeStringList(requiresRaw.config)
|
|
102
|
-
if (bins.length || anyBins.length || env.length || config.length) {
|
|
103
|
-
metadata.requires = {}
|
|
104
|
-
if (bins.length) metadata.requires.bins = bins
|
|
105
|
-
if (anyBins.length) metadata.requires.anyBins = anyBins
|
|
106
|
-
if (env.length) metadata.requires.env = env
|
|
107
|
-
if (config.length) metadata.requires.config = config
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (install.length > 0) metadata.install = install
|
|
112
|
-
const nix = parseNixPluginSpec(pilotbotObj.nix)
|
|
113
|
-
if (nix) metadata.nix = nix
|
|
114
|
-
const config = parsePilotbotConfigSpec(pilotbotObj.config)
|
|
115
|
-
if (config) metadata.config = config
|
|
116
|
-
|
|
117
|
-
return parseArk(PilotbotSkillMetadataSchema, metadata, 'Pilotbot metadata')
|
|
118
|
-
} catch {
|
|
119
|
-
return undefined
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function isTextFile(path: string, contentType?: string | null) {
|
|
124
|
-
const trimmed = path.trim().toLowerCase()
|
|
125
|
-
if (!trimmed) return false
|
|
126
|
-
const parts = trimmed.split('.')
|
|
127
|
-
const extension = parts.length > 1 ? (parts.at(-1) ?? '') : ''
|
|
128
|
-
if (contentType) {
|
|
129
|
-
if (isTextContentType(contentType)) return true
|
|
130
|
-
}
|
|
131
|
-
if (extension && TEXT_FILE_EXTENSION_SET.has(extension)) return true
|
|
132
|
-
return false
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function sanitizePath(path: string) {
|
|
136
|
-
const trimmed = path.trim().replace(/^\/+/, '')
|
|
137
|
-
if (!trimmed || trimmed.includes('..') || trimmed.includes('\\')) {
|
|
138
|
-
return null
|
|
139
|
-
}
|
|
140
|
-
return trimmed
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function buildEmbeddingText(params: {
|
|
144
|
-
frontmatter: ParsedSkillFrontmatter
|
|
145
|
-
readme: string
|
|
146
|
-
otherFiles: Array<{ path: string; content: string }>
|
|
147
|
-
maxChars?: number
|
|
148
|
-
}) {
|
|
149
|
-
const { frontmatter, readme, otherFiles, maxChars = DEFAULT_EMBEDDING_MAX_CHARS } = params
|
|
150
|
-
const headerParts = [
|
|
151
|
-
getFrontmatterValue(frontmatter, 'name'),
|
|
152
|
-
getFrontmatterValue(frontmatter, 'description'),
|
|
153
|
-
getFrontmatterValue(frontmatter, 'homepage'),
|
|
154
|
-
getFrontmatterValue(frontmatter, 'website'),
|
|
155
|
-
getFrontmatterValue(frontmatter, 'url'),
|
|
156
|
-
getFrontmatterValue(frontmatter, 'emoji'),
|
|
157
|
-
].filter(Boolean)
|
|
158
|
-
const fileParts = otherFiles.map((file) => `# ${file.path}\n${file.content}`)
|
|
159
|
-
const raw = [headerParts.join('\n'), readme, ...fileParts].filter(Boolean).join('\n\n')
|
|
160
|
-
if (raw.length <= maxChars) return raw
|
|
161
|
-
return raw.slice(0, maxChars)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const encoder = new TextEncoder()
|
|
165
|
-
|
|
166
|
-
export async function hashSkillFiles(files: Array<{ path: string; sha256: string }>) {
|
|
167
|
-
const normalized = files
|
|
168
|
-
.filter((file) => Boolean(file.path) && Boolean(file.sha256))
|
|
169
|
-
.map((file) => ({ path: file.path, sha256: file.sha256 }))
|
|
170
|
-
.sort((a, b) => a.path.localeCompare(b.path))
|
|
171
|
-
const payload = normalized.map((file) => `${file.path}:${file.sha256}`).join('\n')
|
|
172
|
-
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(payload))
|
|
173
|
-
return toHex(new Uint8Array(digest))
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function toJsonValue(value: unknown): unknown {
|
|
177
|
-
if (value === null) return null
|
|
178
|
-
if (value === undefined) return undefined
|
|
179
|
-
if (typeof value === 'string') {
|
|
180
|
-
const trimmedEnd = value.trimEnd()
|
|
181
|
-
return trimmedEnd.trim() ? trimmedEnd : undefined
|
|
182
|
-
}
|
|
183
|
-
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
|
|
184
|
-
if (typeof value === 'boolean') return value
|
|
185
|
-
if (typeof value === 'bigint') return value.toString()
|
|
186
|
-
if (value instanceof Date) return value.toISOString()
|
|
187
|
-
if (Array.isArray(value)) {
|
|
188
|
-
return value.map((entry) => {
|
|
189
|
-
const next = toJsonValue(entry)
|
|
190
|
-
return next === undefined ? null : next
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
if (isPlainObject(value)) {
|
|
194
|
-
const out: Record<string, unknown> = {}
|
|
195
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
196
|
-
const next = toJsonValue(entry)
|
|
197
|
-
if (next !== undefined) out[key] = next
|
|
198
|
-
}
|
|
199
|
-
return out
|
|
200
|
-
}
|
|
201
|
-
return undefined
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function normalizeStringList(input: unknown): string[] {
|
|
205
|
-
if (!input) return []
|
|
206
|
-
if (Array.isArray(input)) {
|
|
207
|
-
return input.map((value) => String(value).trim()).filter(Boolean)
|
|
208
|
-
}
|
|
209
|
-
if (typeof input === 'string') {
|
|
210
|
-
return input
|
|
211
|
-
.split(',')
|
|
212
|
-
.map((value) => value.trim())
|
|
213
|
-
.filter(Boolean)
|
|
214
|
-
}
|
|
215
|
-
return []
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function parseInstallSpec(input: unknown): SkillInstallSpec | undefined {
|
|
219
|
-
if (!input || typeof input !== 'object') return undefined
|
|
220
|
-
const raw = input as Record<string, unknown>
|
|
221
|
-
const kindRaw =
|
|
222
|
-
typeof raw.kind === 'string' ? raw.kind : typeof raw.type === 'string' ? raw.type : ''
|
|
223
|
-
const kind = kindRaw.trim().toLowerCase()
|
|
224
|
-
if (kind !== 'brew' && kind !== 'node' && kind !== 'go' && kind !== 'uv') return undefined
|
|
225
|
-
|
|
226
|
-
const spec: SkillInstallSpec = { kind: kind as SkillInstallSpec['kind'] }
|
|
227
|
-
if (typeof raw.id === 'string') spec.id = raw.id
|
|
228
|
-
if (typeof raw.label === 'string') spec.label = raw.label
|
|
229
|
-
const bins = normalizeStringList(raw.bins)
|
|
230
|
-
if (bins.length > 0) spec.bins = bins
|
|
231
|
-
if (typeof raw.formula === 'string') spec.formula = raw.formula
|
|
232
|
-
if (typeof raw.tap === 'string') spec.tap = raw.tap
|
|
233
|
-
if (typeof raw.package === 'string') spec.package = raw.package
|
|
234
|
-
if (typeof raw.module === 'string') spec.module = raw.module
|
|
235
|
-
return spec
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function parseNixPluginSpec(input: unknown): NixPluginSpec | undefined {
|
|
239
|
-
if (!input || typeof input !== 'object') return undefined
|
|
240
|
-
const raw = input as Record<string, unknown>
|
|
241
|
-
if (typeof raw.plugin !== 'string') return undefined
|
|
242
|
-
const plugin = raw.plugin.trim()
|
|
243
|
-
if (!plugin) return undefined
|
|
244
|
-
const systems = normalizeStringList(raw.systems)
|
|
245
|
-
const spec: NixPluginSpec = { plugin }
|
|
246
|
-
if (systems.length > 0) spec.systems = systems
|
|
247
|
-
return spec
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function parsePilotbotConfigSpec(input: unknown): PilotbotConfigSpec | undefined {
|
|
251
|
-
if (!input || typeof input !== 'object') return undefined
|
|
252
|
-
const raw = input as Record<string, unknown>
|
|
253
|
-
const requiredEnv = normalizeStringList(raw.requiredEnv)
|
|
254
|
-
const stateDirs = normalizeStringList(raw.stateDirs)
|
|
255
|
-
const example = typeof raw.example === 'string' ? raw.example.trim() : ''
|
|
256
|
-
const spec: PilotbotConfigSpec = {}
|
|
257
|
-
if (requiredEnv.length > 0) spec.requiredEnv = requiredEnv
|
|
258
|
-
if (stateDirs.length > 0) spec.stateDirs = stateDirs
|
|
259
|
-
if (example) spec.example = example
|
|
260
|
-
return Object.keys(spec).length > 0 ? spec : undefined
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function toHex(bytes: Uint8Array) {
|
|
264
|
-
let out = ''
|
|
265
|
-
for (const byte of bytes) out += byte.toString(16).padStart(2, '0')
|
|
266
|
-
return out
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
270
|
-
if (!value || typeof value !== 'object') return false
|
|
271
|
-
const proto = Object.getPrototypeOf(value)
|
|
272
|
-
return proto === Object.prototype || proto === null
|
|
273
|
-
}
|