Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion convex/devSeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { internal } from './_generated/api'
import type { ActionCtx } from './_generated/server'
import { internalAction, internalMutation } from './_generated/server'
import { EMBEDDING_DIMENSIONS } from './lib/embeddings'
import { parseMoltbotMetadata, parseFrontmatter } from './lib/skills'
import { parseFrontmatter, parseMoltbotMetadata } from './lib/skills'

type SeedSkillSpec = {
slug: string
Expand Down
2 changes: 1 addition & 1 deletion convex/devSeedExtra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { internal } from './_generated/api'
import type { Id } from './_generated/dataModel'
import type { ActionCtx } from './_generated/server'
import { internalAction, internalMutation } from './_generated/server'
import { parseMoltbotMetadata, parseFrontmatter } from './lib/skills'
import { parseFrontmatter, parseMoltbotMetadata } from './lib/skills'

type SeedSkillSpec = {
slug: string
Expand Down
2 changes: 1 addition & 1 deletion convex/http.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiRoutes, LegacyApiRoutes } from 'molthub-schema'
import { httpRouter } from 'convex/server'
import { ApiRoutes, LegacyApiRoutes } from 'molthub-schema'
import { auth } from './auth'
import { downloadZip } from './downloads'
import {
Expand Down
4 changes: 1 addition & 3 deletions convex/lib/badges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import type { QueryCtx } from '../_generated/server'

type BadgeKind = Doc<'skillBadges'>['kind']

export type SkillBadgeMap = Partial<
Record<BadgeKind, { byUserId: Id<'users'>; at: number }>
>
export type SkillBadgeMap = Partial<Record<BadgeKind, { byUserId: Id<'users'>; at: number }>>

export type SkillBadgeSource = { badges?: SkillBadgeMap | null }

Expand Down
2 changes: 1 addition & 1 deletion convex/lib/githubImport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TEXT_FILE_EXTENSION_SET } from 'molthub-schema'
import { zipSync } from 'fflate'
import { TEXT_FILE_EXTENSION_SET } from 'molthub-schema'
import semver from 'semver'
import { parseFrontmatter } from './skills'

Expand Down
2 changes: 1 addition & 1 deletion convex/lib/skillBackfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {
getFrontmatterMetadata,
getFrontmatterValue,
type ParsedSkillFrontmatter,
parseMoltbotMetadata,
parseFrontmatter,
parseMoltbotMetadata,
} from './skills'

export type ParsedSkillData = {
Expand Down
10 changes: 5 additions & 5 deletions convex/lib/skillPublish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import semver from 'semver'
import { api, internal } from '../_generated/api'
import type { Doc, Id } from '../_generated/dataModel'
import type { ActionCtx, MutationCtx } from '../_generated/server'
import { getSkillBadgeMap, isSkillHighlighted } from './badges'
import { generateChangelogForPublish } from './changelog'
import { generateEmbedding } from './embeddings'
import { getSkillBadgeMap, isSkillHighlighted } from './badges'
import type { PublicUser } from './public'
import {
buildEmbeddingText,
getFrontmatterMetadata,
hashSkillFiles,
isTextFile,
parseMoltbotMetadata,
parseFrontmatter,
parseMoltbotMetadata,
sanitizePath,
} from './skills'
import type { WebhookSkillPayload } from './webhooks'
Expand Down Expand Up @@ -166,9 +166,9 @@ export async function publishVersionForUser(
embedding,
})) as PublishResult

const owner = (await ctx.runQuery(internal.users.getByIdInternal, { userId })) as
| Doc<'users'>
| null
const owner = (await ctx.runQuery(internal.users.getByIdInternal, {
userId,
})) as Doc<'users'> | null
const ownerHandle = owner?.handle ?? owner?.displayName ?? owner?.name ?? 'unknown'

void ctx.scheduler
Expand Down
2 changes: 1 addition & 1 deletion convex/lib/skills.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
getFrontmatterValue,
hashSkillFiles,
isTextFile,
parseMoltbotMetadata,
parseFrontmatter,
parseMoltbotMetadata,
sanitizePath,
} from './skills'

Expand Down
2 changes: 1 addition & 1 deletion convex/lib/skills.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
isTextContentType,
type MoltbotConfigSpec,
type MoltbotSkillMetadata,
MoltbotSkillMetadataSchema,
isTextContentType,
type NixPluginSpec,
parseArk,
type SkillInstallSpec,
Expand Down
6 changes: 3 additions & 3 deletions convex/lib/soulPublish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ export async function publishSoulVersionForUser(
embedding,
})) as PublishResult

const owner = (await ctx.runQuery(internal.users.getByIdInternal, { userId })) as
| Doc<'users'>
| null
const owner = (await ctx.runQuery(internal.users.getByIdInternal, {
userId,
})) as Doc<'users'> | null
const ownerHandle = owner?.handle ?? owner?.name ?? userId

void ctx.scheduler
Expand Down
29 changes: 14 additions & 15 deletions convex/maintenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,10 @@ export const backfillSkillBadges: ReturnType<typeof action> = action({
handler: async (ctx, args): Promise<BadgeBackfillActionResult> => {
const { user } = await requireUserFromAction(ctx)
assertRole(user, ['admin'])
return ctx.runAction(internal.maintenance.backfillSkillBadgesInternal, args) as Promise<
BadgeBackfillActionResult
>
return ctx.runAction(
internal.maintenance.backfillSkillBadgesInternal,
args,
) as Promise<BadgeBackfillActionResult>
},
})

Expand Down Expand Up @@ -771,15 +772,12 @@ export async function backfillSkillBadgeTableInternalHandler(
if (dryRun) continue

for (const entry of entries) {
const result = await ctx.runMutation(
internal.maintenance.upsertSkillBadgeRecordInternal,
{
skillId: item.skillId,
kind: entry.kind,
byUserId: entry.byUserId,
at: entry.at,
},
)
const result = await ctx.runMutation(internal.maintenance.upsertSkillBadgeRecordInternal, {
skillId: item.skillId,
kind: entry.kind,
byUserId: entry.byUserId,
at: entry.at,
})
if (result.inserted) {
totals.recordsInserted++
}
Expand Down Expand Up @@ -814,9 +812,10 @@ export const backfillSkillBadgeTable: ReturnType<typeof action> = action({
handler: async (ctx, args): Promise<SkillBadgeTableBackfillActionResult> => {
const { user } = await requireUserFromAction(ctx)
assertRole(user, ['admin'])
return ctx.runAction(internal.maintenance.backfillSkillBadgeTableInternal, args) as Promise<
SkillBadgeTableBackfillActionResult
>
return ctx.runAction(
internal.maintenance.backfillSkillBadgeTableInternal,
args,
) as Promise<SkillBadgeTableBackfillActionResult>
},
})

Expand Down
16 changes: 12 additions & 4 deletions convex/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { v } from 'convex/values'
import { internal } from './_generated/api'
import type { Doc, Id } from './_generated/dataModel'
import { action, internalQuery } from './_generated/server'
import { generateEmbedding } from './lib/embeddings'
import { getSkillBadgeMaps, isSkillHighlighted, type SkillBadgeMap } from './lib/badges'
import { matchesExactTokens, tokenize } from './lib/searchText'
import { generateEmbedding } from './lib/embeddings'
import { toPublicSkill, toPublicSoul } from './lib/public'
import { matchesExactTokens, tokenize } from './lib/searchText'

type HydratedEntry = {
embeddingId: Id<'skillEmbeddings'>
Expand Down Expand Up @@ -79,7 +79,11 @@ export const searchSkills: ReturnType<typeof action> = action({
: hydratedWithBadges

exactMatches = filtered.filter((entry) =>
matchesExactTokens(queryTokens, [entry.skill.displayName, entry.skill.slug, entry.skill.summary]),
matchesExactTokens(queryTokens, [
entry.skill.displayName,
entry.skill.slug,
entry.skill.summary,
]),
)

if (exactMatches.length >= limit || results.length < candidateLimit) {
Expand Down Expand Up @@ -185,7 +189,11 @@ export const searchSouls: ReturnType<typeof action> = action({
)

exactMatches = hydrated.filter((entry) =>
matchesExactTokens(queryTokens, [entry.soul.displayName, entry.soul.slug, entry.soul.summary]),
matchesExactTokens(queryTokens, [
entry.soul.displayName,
entry.soul.slug,
entry.soul.summary,
]),
)

if (exactMatches.length >= limit || results.length < candidateLimit) {
Expand Down
27 changes: 17 additions & 10 deletions convex/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import type { Doc, Id } from './_generated/dataModel'
import type { MutationCtx, QueryCtx } from './_generated/server'
import { action, internalMutation, internalQuery, mutation, query } from './_generated/server'
import { assertAdmin, assertModerator, requireUser, requireUserFromAction } from './lib/access'
import { toPublicSkill, toPublicUser } from './lib/public'
import { getSkillBadgeMap, getSkillBadgeMaps, isSkillHighlighted } from './lib/badges'
import { generateChangelogPreview as buildChangelogPreview } from './lib/changelog'
import { buildTrendingLeaderboard } from './lib/leaderboards'
import { deriveModerationFlags } from './lib/moderation'
import { toPublicSkill, toPublicUser } from './lib/public'
import {
fetchText,
type PublishResult,
Expand Down Expand Up @@ -321,8 +321,7 @@ export const listWithLatest = query({
const ordered =
args.batch === 'highlighted'
? [...withBadges].sort(
(a, b) =>
(b.badges?.highlighted?.at ?? 0) - (a.badges?.highlighted?.at ?? 0),
(a, b) => (b.badges?.highlighted?.at ?? 0) - (a.badges?.highlighted?.at ?? 0),
)
: withBadges
const limited = ordered.slice(0, limit)
Expand All @@ -332,10 +331,14 @@ export const listWithLatest = query({
latestVersion: skill.latestVersionId ? await ctx.db.get(skill.latestVersionId) : null,
})),
)
return items.filter((item): item is {
skill: NonNullable<ReturnType<typeof toPublicSkill>>
latestVersion: Doc<'skillVersions'> | null
} => Boolean(item.skill))
return items.filter(
(
item,
): item is {
skill: NonNullable<ReturnType<typeof toPublicSkill>>
latestVersion: Doc<'skillVersions'> | null
} => Boolean(item.skill),
)
},
})

Expand All @@ -350,8 +353,9 @@ export const listForManagement = query({
const limit = clampInt(args.limit ?? 50, 1, MAX_LIST_BULK_LIMIT)
const takeLimit = Math.min(limit * 5, MAX_LIST_TAKE)
const entries = await ctx.db.query('skills').order('desc').take(takeLimit)
const filtered = (args.includeDeleted ? entries : entries.filter((skill) => !skill.softDeletedAt))
.slice(0, limit)
const filtered = (
args.includeDeleted ? entries : entries.filter((skill) => !skill.softDeletedAt)
).slice(0, limit)
return buildManagementSkillEntries(ctx, filtered)
},
})
Expand All @@ -362,7 +366,10 @@ export const listRecentVersions = query({
const { user } = await requireUser(ctx)
assertModerator(user)
const limit = clampInt(args.limit ?? 20, 1, MAX_LIST_BULK_LIMIT)
const versions = await ctx.db.query('skillVersions').order('desc').take(limit * 2)
const versions = await ctx.db
.query('skillVersions')
.order('desc')
.take(limit * 2)
const entries = versions.filter((version) => !version.softDeletedAt).slice(0, limit)

const results: Array<{
Expand Down
5 changes: 4 additions & 1 deletion convex/souls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ export const listPublicPage = query({
.order('desc')
.paginate({ cursor: args.cursor ?? null, numItems: limit })

const items: Array<{ soul: NonNullable<ReturnType<typeof toPublicSoul>>; latestVersion: Doc<'soulVersions'> | null }> = []
const items: Array<{
soul: NonNullable<ReturnType<typeof toPublicSoul>>
latestVersion: Doc<'soulVersions'> | null
}> = []

for (const soul of page) {
if (soul.softDeletedAt) continue
Expand Down
2 changes: 1 addition & 1 deletion e2e/molthub.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { spawnSync } from 'node:child_process'
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { unzipSync } from 'fflate'
import {
ApiRoutes,
ApiV1SearchResponseSchema,
ApiV1WhoamiResponseSchema,
parseArk,
} from 'molthub-schema'
import { unzipSync } from 'fflate'
import { Agent, setGlobalDispatcher } from 'undici'
import { describe, expect, it } from 'vitest'
import { readGlobalConfig } from '../packages/molthub/src/config'
Expand Down
2 changes: 1 addition & 1 deletion packages/molthub/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { stat } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { Command } from 'commander'
import { getCliBuildLabel, getCliVersion } from './cli/buildInfo.js'
import { resolveMoltbotDefaultWorkspace } from './cli/moltbotConfig.js'
import { cmdLoginFlow, cmdLogout, cmdWhoami } from './cli/commands/auth.js'
import { cmdDeleteSkill, cmdUndeleteSkill } from './cli/commands/delete.js'
import { cmdPublish } from './cli/commands/publish.js'
Expand All @@ -12,6 +11,7 @@ import { cmdStarSkill } from './cli/commands/star.js'
import { cmdSync } from './cli/commands/sync.js'
import { cmdUnstarSkill } from './cli/commands/unstar.js'
import { configureCommanderHelp, styleEnvBlock, styleTitle } from './cli/helpStyle.js'
import { resolveMoltbotDefaultWorkspace } from './cli/moltbotConfig.js'
import { DEFAULT_REGISTRY, DEFAULT_SITE } from './cli/registry.js'
import type { GlobalOpts } from './cli/types.js'
import { fail } from './cli/ui.js'
Expand Down
6 changes: 2 additions & 4 deletions src/components/SkillDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Link, useNavigate } from '@tanstack/react-router'
import type { MoltbotSkillMetadata, SkillInstallSpec } from 'molthub-schema'
import { useAction, useMutation, useQuery } from 'convex/react'
import type { MoltbotSkillMetadata, SkillInstallSpec } from 'molthub-schema'
import { useEffect, useMemo, useState } from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
Expand Down Expand Up @@ -650,9 +650,7 @@ export function SkillDetailPage({
<strong>@{entry.user?.handle ?? entry.user?.name ?? 'user'}</strong>
<div style={{ color: '#5c554e' }}>{entry.comment.body}</div>
</div>
{isAuthenticated &&
me &&
(me._id === entry.comment.userId || isModerator(me)) ? (
{isAuthenticated && me && (me._id === entry.comment.userId || isModerator(me)) ? (
<button
className="btn"
type="button"
Expand Down
4 changes: 1 addition & 3 deletions src/components/SoulDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,7 @@ export function SoulDetailPage({ slug }: SoulDetailPageProps) {
<strong>@{entry.user?.handle ?? entry.user?.name ?? 'user'}</strong>
<div style={{ color: '#5c554e' }}>{entry.comment.body}</div>
</div>
{isAuthenticated &&
me &&
(me._id === entry.comment.userId || isModerator(me)) ? (
{isAuthenticated && me && (me._id === entry.comment.userId || isModerator(me)) ? (
<button
className="btn"
type="button"
Expand Down
2 changes: 1 addition & 1 deletion src/lib/uploadFiles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TEXT_FILE_EXTENSION_SET } from 'molthub-schema'
import { gunzipSync, unzipSync } from 'fflate'
import { TEXT_FILE_EXTENSION_SET } from 'molthub-schema'

const TEXT_TYPES = new Map([
['md', 'text/markdown'],
Expand Down
Loading