From 0ec511c89023c4526c4e9d93721ed30f17f24d86 Mon Sep 17 00:00:00 2001 From: Tanuj Bhaud Date: Fri, 13 Feb 2026 15:22:22 +0530 Subject: [PATCH 1/2] fix(vt): explicit return types and missing undici dependency Refactor action handlers in convex/vt.ts to use explicit return types, resolving circular type inference (TS7022). Also add undici to devDependencies for E2E tests. --- convex/vt.ts | 102 ++++++++++++++++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/convex/vt.ts b/convex/vt.ts index 00e0c0890..965657fcc 100644 --- a/convex/vt.ts +++ b/convex/vt.ts @@ -1,5 +1,6 @@ import { v } from 'convex/values' import { internal } from './_generated/api' + import { action, internalAction, internalMutation } from './_generated/server' import { buildDeterministicZip } from './lib/skillZip' @@ -9,7 +10,10 @@ import { buildDeterministicZip } from './lib/skillZip' */ export const fixNullModerationReasons = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise<{ total: number; fixed: number; noVtAnalysis: number }> => { const batchSize = args.batchSize ?? 100 const skills = await ctx.runQuery(internal.skills.getUnscannedActiveSkillsInternal, { limit: batchSize, @@ -123,7 +127,16 @@ export const fetchResults = action({ args: { sha256hash: v.optional(v.string()), }, - handler: async (_ctx, args) => { + handler: async ( + _ctx, + args, + ): Promise<{ + status: string + source?: 'code_insight' | 'engines' + url?: string + metadata?: any + message?: string + }> => { if (!args.sha256hash) { return { status: 'not_found' } } @@ -360,11 +373,20 @@ export const pollPendingScans = internalAction({ args: { batchSize: v.optional(v.number()), }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise<{ + processed: number + updated: number + staled: number + healthy: boolean + queueSize?: number + }> => { const apiKey = process.env.VT_API_KEY if (!apiKey) { console.log('[vt:pollPendingScans] VT_API_KEY not configured, skipping') - return { processed: 0, updated: 0, healthy: false } + return { processed: 0, updated: 0, staled: 0, healthy: false } } const batchSize = args.batchSize ?? 10 @@ -384,7 +406,13 @@ export const pollPendingScans = internalAction({ }) if (pendingSkills.length === 0) { - return { processed: 0, updated: 0, healthy: health.healthy, queueSize: health.queueSize } + return { + processed: 0, + updated: 0, + staled: 0, + healthy: health.healthy, + queueSize: health.queueSize, + } } console.log( @@ -396,9 +424,9 @@ export const pollPendingScans = internalAction({ let updated = 0 let staled = 0 for (const { skillId, versionId, sha256hash, checkCount } of pendingSkills) { - if (!sha256hash) { + if (!sha256hash || !versionId) { console.log( - `[vt:pollPendingScans] Skill ${skillId} version ${versionId} has no hash, skipping`, + `[vt:pollPendingScans] Skill ${skillId} version ${versionId} has no hash or versionId, skipping`, ) continue } @@ -545,7 +573,21 @@ export const backfillPendingScans = internalAction({ args: { triggerRescans: v.optional(v.boolean()), }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise< + | { + total: number + updated: number + rescansRequested: number + noHash: number + notInVT: number + errors: number + remaining: number + } + | { error: string } + > => { const apiKey = process.env.VT_API_KEY if (!apiKey) { console.log('[vt:backfill] VT_API_KEY not configured') @@ -631,7 +673,11 @@ export const backfillPendingScans = internalAction({ */ export const rescanActiveSkills = internalAction({ args: {}, - handler: async (ctx) => { + handler: async ( + ctx, + ): Promise< + { total: number; updated: number; unchanged: number; errors: number; durationMs?: number } | { error: string } + > => { const startTime = Date.now() const apiKey = process.env.VT_API_KEY if (!apiKey) { @@ -738,7 +784,10 @@ export const rescanActiveSkills = internalAction({ */ export const scanUnscannedSkills = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise<{ total: number; scanned: number; errors: number; durationMs?: number } | { error: string }> => { const startTime = Date.now() const apiKey = process.env.VT_API_KEY if (!apiKey) { @@ -800,7 +849,12 @@ export const scanUnscannedSkills = internalAction({ */ export const scanLegacySkills = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise< + { total: number; scanned: number; alreadyHasHash: number; errors: number; durationMs?: number } | { error: string } + > => { const startTime = Date.now() const apiKey = process.env.VT_API_KEY if (!apiKey) { @@ -815,7 +869,7 @@ export const scanLegacySkills = internalAction({ if (skills.length === 0) { console.log('[vt:scanLegacy] No legacy skills to scan') - return { total: 0, scanned: 0, errors: 0 } + return { total: 0, scanned: 0, alreadyHasHash: 0, errors: 0 } } console.log(`[vt:scanLegacy] Scanning ${skills.length} legacy skills`) @@ -870,7 +924,12 @@ export const scanLegacySkills = internalAction({ */ export const backfillActiveSkillsVTCache = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise< + { total: number; updated: number; noResults: number; errors: number; done: boolean } | { error: string } + > => { const apiKey = process.env.VT_API_KEY if (!apiKey) { console.log('[vt:backfillActive] VT_API_KEY not configured') @@ -955,7 +1014,10 @@ export const backfillActiveSkillsVTCache = internalAction({ */ export const requestReanalysisForPending = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise<{ total: number; requested: number; errors: number; done: boolean } | { error: string }> => { const apiKey = process.env.VT_API_KEY if (!apiKey) { console.log('[vt:requestReanalysis] VT_API_KEY not configured') @@ -971,7 +1033,7 @@ export const requestReanalysisForPending = internalAction({ if (skills.length === 0) { console.log('[vt:requestReanalysis] No pending skills found') - return { total: 0, requested: 0, done: true } + return { total: 0, requested: 0, errors: 0, done: true } } console.log(`[vt:requestReanalysis] Found ${skills.length} skills to request reanalysis`) @@ -1005,7 +1067,10 @@ export const requestReanalysisForPending = internalAction({ */ export const fixNullModerationStatus = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise<{ total: number; fixed: number; done: boolean }> => { const batchSize = args.batchSize ?? 100 const skills = await ctx.runQuery(internal.skills.getSkillsWithNullModerationStatusInternal, { @@ -1034,7 +1099,10 @@ export const fixNullModerationStatus = internalAction({ */ export const syncModerationReasons = internalAction({ args: { batchSize: v.optional(v.number()) }, - handler: async (ctx, args) => { + handler: async ( + ctx, + args, + ): Promise<{ total: number; synced: number; noVtAnalysis: number; done: boolean }> => { const batchSize = args.batchSize ?? 100 const skills = await ctx.runQuery(internal.skills.getSkillsWithStaleModerationReasonInternal, { diff --git a/package.json b/package.json index 31f15eb3d..ba867675a 100644 --- a/package.json +++ b/package.json @@ -80,4 +80,4 @@ "vite": "^7.3.1", "vitest": "^4.0.18" } -} +} \ No newline at end of file From 02e0c1e1d4f76f017cd9785513468455cd2f7f7c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 13 Feb 2026 15:24:49 +0100 Subject: [PATCH 2/2] fix: add root undici devDependency for e2e (#255) (thanks @tanujbhaud) --- CHANGELOG.md | 1 + bun.lock | 11 ++++++++--- package.json | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 872944b2f..da11b5e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Fixed - Upload gate: handle GitHub API rate limits and optional authenticated lookup token (thanks @superlowburn, #246). - HTTP: remove `allowH2` from Undici agent to prevent `fetch failed` on Node.js 22+ (#245). +- Tests: add root `undici` dev dependency for Node E2E imports (thanks @tanujbhaud, #255). - VirusTotal: fix scan sync race conditions and retry behavior in scan/backfill paths. - Metadata: tolerate trailing commas in JSON metadata. - Auth: allow soft-deleted users to re-authenticate on fresh login, while keeping banned users blocked (thanks @tanujbhaud, #177). diff --git a/bun.lock b/bun.lock index 37de7590b..5f5b3c775 100644 --- a/bun.lock +++ b/bun.lock @@ -57,13 +57,14 @@ "oxlint": "^1.42.0", "oxlint-tsgolint": "^0.11.4", "typescript": "^5.9.3", + "undici": "^7.19.2", "vite": "^7.3.1", "vitest": "^4.0.18", }, }, "packages/clawdhub": { "name": "clawhub", - "version": "0.5.0", + "version": "0.6.1", "bin": { "clawhub": "bin/clawdhub.js", "clawdhub": "bin/clawdhub.js", @@ -1298,7 +1299,7 @@ "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], - "undici": ["undici@7.19.2", "", {}, "sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg=="], + "undici": ["undici@7.20.0", "", {}, "sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], @@ -1402,8 +1403,12 @@ "cheerio/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "cheerio/undici": ["undici@7.19.2", "", {}, "sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg=="], + "cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + "clawhub/undici": ["undici@7.19.2", "", {}, "sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg=="], + "convex/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="], "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -1412,7 +1417,7 @@ "htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], - "jsdom/undici": ["undici@7.20.0", "", {}, "sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ=="], + "nitro/undici": ["undici@7.19.2", "", {}, "sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], diff --git a/package.json b/package.json index ba867675a..1a3a8775e 100644 --- a/package.json +++ b/package.json @@ -76,8 +76,9 @@ "only-allow": "^1.2.2", "oxlint": "^1.42.0", "oxlint-tsgolint": "^0.11.4", + "undici": "^7.19.2", "typescript": "^5.9.3", "vite": "^7.3.1", "vitest": "^4.0.18" } -} \ No newline at end of file +}