diff --git a/DENO_NODE_PACKAGE_ISSUE.md b/DENO_NODE_PACKAGE_ISSUE.md new file mode 100644 index 0000000..f8fbc5f --- /dev/null +++ b/DENO_NODE_PACKAGE_ISSUE.md @@ -0,0 +1,306 @@ +# Running `ai-sdk-tool-code-execution` in Deno - Compatibility Issue + +## Problem Summary + +We need to run the npm package `ai-sdk-tool-code-execution` in a Deno runtime environment on Railway. The package requires Node.js built-ins (`node:sqlite`, `undici`) that don't exist in Deno, and we're looking for a solution to make it work. + +## Environment + +- **Runtime:** Deno 1.39.0 on Railway +- **Package:** `ai-sdk-tool-code-execution@0.0.2` +- **Import Method:** Dynamic imports via esm.sh CDN +- **Use Case:** Remote code execution for AI SDK tools + +## What We're Trying to Do + +We have a Deno server that dynamically imports npm packages at runtime to provide AI SDK tools. The workflow is: + +1. User requests a tool (e.g., `executeCode`) +2. Deno server fetches the package from esm.sh or npm +3. Server loads the tool's schema and execution function +4. Server executes the tool with user-provided parameters + +## The Package We Need + +**Package:** `ai-sdk-tool-code-execution` +**Version:** `0.0.2` +**Description:** Execute Python code in a sandboxed environment using Vercel Sandbox +**npm URL:** https://www.npmjs.com/package/ai-sdk-tool-code-execution +**CDN URLs:** +- esm.sh: `https://esm.sh/ai-sdk-tool-code-execution@0.0.2` +- jsdelivr: `https://cdn.jsdelivr.net/npm/ai-sdk-tool-code-execution@0.0.2/+esm` + +**Dependencies (from package.json):** +```json +{ + "dependencies": { + "ai": "^4.0.18", + "better-sqlite3": "^11.8.1", + "undici": "^7.16.0" + } +} +``` + +**Key Issue:** The package depends on: +- `better-sqlite3` → which requires `node:sqlite` (Node.js built-in) +- `undici` → HTTP client that uses Node.js internals + +## What We've Tried + +### Attempt 1: Deno npm: Specifier (Node.js Compatibility Mode) + +**Code:** +```typescript +const npmUrl = `npm:ai-sdk-tool-code-execution@0.0.2`; +const module = await import(npmUrl); +``` + +**Error:** +``` +Loading unprepared module: npm:ai-sdk-tool-code-execution@0.0.2 +``` + +**Why it failed:** Deno's npm compatibility requires the package to be "prepared" (downloaded/cached) before import. Dynamic imports of unprepared npm packages fail. + +### Attempt 2: esm.sh with Node.js Target + +**Code:** +```typescript +const esmUrl = `https://esm.sh/ai-sdk-tool-code-execution@0.0.2?target=esnext`; +const module = await import(esmUrl); +``` + +**Error:** +``` +Module not found "https://esm.sh/node:sqlite?target=esnext" + at https://esm.sh/undici@^7.16.0?target=esnext:25:8 +``` + +**Why it failed:** The package code imports `node:sqlite` which esm.sh tries to load from `https://esm.sh/node:sqlite?target=esnext`, but `node:sqlite` is a Node.js built-in, not an npm package. + +### Attempt 3: Multi-Strategy with Fallback + +**Code:** +```typescript +let module; +let importError; + +// Strategy 1: npm: specifier +try { + const npmUrl = `npm:${packageName}@${version}`; + module = await import(npmUrl); +} catch (error) { + importError = error; + + // Strategy 2: esm.sh with esnext target + try { + const esmUrl = `https://esm.sh/${packageName}@${version}?target=esnext`; + module = await import(esmUrl); + } catch (esmError) { + return { success: false, error: esmError.message }; + } +} +``` + +**Result:** Both strategies fail with the same errors as above. + +## Current Deno Configuration + +**`deno.json`:** +```json +{ + "compilerOptions": { + "allowJs": true, + "lib": ["deno.window"], + "strict": true + }, + "nodeModulesDir": true, + "unstable": ["byonm"], + "imports": { + "zod-to-json-schema": "https://esm.sh/zod-to-json-schema@3.25.0" + } +} +``` + +**Key Settings:** +- `nodeModulesDir: true` - Creates `node_modules` directory for npm packages +- `unstable: ["byonm"]` - Enables "Bring Your Own Node Modules" mode + +## Full Error Details + +### npm: Strategy Error +```json +{ + "success": false, + "error": "Failed to import package: ...", + "details": { + "npmError": "Loading unprepared module: npm:ai-sdk-tool-code-execution@0.0.2, imported from: file:///app/server.ts" + } +} +``` + +### esm.sh Strategy Error +```json +{ + "success": false, + "error": "Failed to import package: Module not found \"https://esm.sh/node:sqlite?target=esnext\"", + "details": { + "esmError": "Module not found \"https://esm.sh/node:sqlite?target=esnext\".\n at https://esm.sh/undici@^7.16.0?target=esnext:25:8" + } +} +``` + +## Technical Deep Dive + +### Why This Package Needs Node.js + +1. **better-sqlite3** - Native Node.js addon for SQLite + - Uses `node:sqlite` built-in + - Compiled C++ bindings + - Not available in Deno without Node compatibility layer + +2. **undici** - Modern HTTP client for Node.js + - Uses Node.js streams and buffer APIs + - Optimized for Node.js internals + - May work in Deno with polyfills, but blocked by sqlite dependency + +### Deno's Node.js Compatibility + +Deno supports many Node.js built-ins via `node:*` imports: +- `node:fs`, `node:path`, `node:http`, `node:crypto`, etc. + +**BUT** it does NOT support: +- `node:sqlite` (not a standard Node.js built-in) +- Native addons (`.node` files) +- Some advanced internal APIs + +### The Import Flow + +1. **Deno tries to import** `npm:ai-sdk-tool-code-execution@0.0.2` +2. **Package resolves to** esm.sh or npm registry +3. **Package imports** `better-sqlite3` +4. **better-sqlite3 imports** `node:sqlite` +5. **FAILURE:** `node:sqlite` doesn't exist in Deno or esm.sh + +## Questions for ChatGPT + +1. **Can Deno's npm compatibility layer handle `better-sqlite3` or `node:sqlite`?** + - Is there a Deno-compatible SQLite library we could alias? + - Can we use import maps to redirect `node:sqlite` to a Deno polyfill? + +2. **Can we "prepare" the npm module in Deno before dynamic import?** + - Is there a way to pre-cache npm packages in Deno? + - Can we use `deno vendor` or similar to prepare the package? + +3. **Can esm.sh or other CDNs provide Node.js built-in polyfills?** + - Does esm.sh have a mode that bundles Node.js built-ins? + - Are there CDN parameters we're missing? + +4. **Could we use Deno's `--node-modules-dir` flag differently?** + - Should we install the package via npm/pnpm first? + - Can we point Deno to pre-installed node_modules? + +5. **Is there a way to patch/bundle the package to remove Node.js dependencies?** + - Could we create a Deno-compatible fork? + - Are there tools to transpile Node.js packages to Deno? + +6. **Alternative: Different code execution package?** + - Are there Deno-native code execution tools? + - Could we use WebAssembly or browser-based sandboxing? + +## What Would Success Look Like + +**Ideal outcome:** +```typescript +// This should work in Deno: +const module = await import('npm:ai-sdk-tool-code-execution@0.0.2'); +const { executeCode } = module; + +// And this should execute: +const result = await executeCode.execute({ + code: 'print(fibonacci(10))', + language: 'python' +}); +``` + +**Acceptable outcome:** +```typescript +// Some preparation step, then: +const module = await import('https://esm.sh/ai-sdk-tool-code-execution@0.0.2'); +// Works without errors +``` + +## Repository Context + +**Project:** TPMJS - Tool Package Manager for AI SDK +**Server:** `apps/railway-executor/server.ts` +**Config:** `apps/railway-executor/deno.json` +**Deployment:** Railway with Deno runtime + +**Server Code (Simplified):** +```typescript +async function loadAndDescribe(req: Request): Promise { + const { packageName, exportName, version, importUrl } = await req.json(); + + // Try npm: specifier first + try { + const npmUrl = `npm:${packageName}@${version}`; + const module = await import(npmUrl); + const tool = module[exportName]; + return Response.json({ success: true, tool }); + } catch (error) { + // Try esm.sh fallback + const esmUrl = `https://esm.sh/${packageName}@${version}?target=esnext`; + const module = await import(esmUrl); + const tool = module[exportName]; + return Response.json({ success: true, tool }); + } +} + +Deno.serve({ port: 3001 }, handler); +``` + +## Live Error Logs + +**Request:** +```bash +curl -X POST https://endearing-commitment-production.up.railway.app/load-and-describe \ + -H "Content-Type: application/json" \ + -d '{ + "packageName": "ai-sdk-tool-code-execution", + "exportName": "executeCode", + "version": "0.0.2", + "importUrl": "https://esm.sh/ai-sdk-tool-code-execution@0.0.2" + }' +``` + +**Response:** +```json +{ + "success": false, + "error": "Failed to import package: Module not found \"https://esm.sh/node:sqlite?target=esnext\"", + "details": { + "npmError": "Loading unprepared module: npm:ai-sdk-tool-code-execution@0.0.2", + "esmError": "Module not found \"https://esm.sh/node:sqlite?target=esnext\"" + } +} +``` + +## Additional Context + +- We successfully load other packages (e.g., `@tpmjs/hello`, `zod-to-json-schema`) +- Only packages with Node.js built-in dependencies fail +- Switching to Node.js would work, but we prefer Deno's security model +- This is for a production tool registry serving AI SDK tools to users + +## Related Resources + +- **Deno npm compatibility:** https://deno.com/manual/node/npm_specifiers +- **Deno Node built-ins:** https://deno.com/manual/node/node_specifiers +- **esm.sh documentation:** https://esm.sh/ +- **Package source:** https://www.npmjs.com/package/ai-sdk-tool-code-execution +- **Deno SQLite libraries:** https://deno.land/x/sqlite@v3.8 + +--- + +**Question for ChatGPT:** Is there any way to make `ai-sdk-tool-code-execution` work in Deno, given these constraints? If not, what's the closest alternative that would work in Deno's runtime? diff --git a/apps/playground/next-env.d.ts b/apps/playground/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/playground/next-env.d.ts +++ b/apps/playground/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/playground/src/components/sidebar/ToolsSidebar.tsx b/apps/playground/src/components/sidebar/ToolsSidebar.tsx index b5586ab..961f824 100644 --- a/apps/playground/src/components/sidebar/ToolsSidebar.tsx +++ b/apps/playground/src/components/sidebar/ToolsSidebar.tsx @@ -3,6 +3,8 @@ import { Badge } from '@tpmjs/ui/Badge/Badge'; import { Card, CardContent, CardHeader, CardTitle } from '@tpmjs/ui/Card/Card'; import { Input } from '@tpmjs/ui/Input/Input'; +import { ToolHealthBadge } from '@tpmjs/ui/ToolHealthBadge/ToolHealthBadge'; +import { ToolHealthBanner } from '@tpmjs/ui/ToolHealthBanner/ToolHealthBanner'; import { useEffect, useState } from 'react'; interface Tool { @@ -16,6 +18,10 @@ interface Tool { frameworks?: string[]; env?: Array<{ name: string; description: string; required?: boolean; default?: string }>; importUrl?: string; + importHealth?: 'HEALTHY' | 'BROKEN' | 'UNKNOWN'; + executionHealth?: 'HEALTHY' | 'BROKEN' | 'UNKNOWN'; + healthCheckError?: string | null; + lastHealthCheck?: string | null; } export function ToolsSidebar(): React.ReactElement { @@ -86,11 +92,16 @@ export function ToolsSidebar(): React.ReactElement {

{tool.description}

-
+
{tool.category} v{tool.version} +
@@ -156,6 +167,15 @@ export function ToolsSidebar(): React.ReactElement {
+ {/* Health warning banner */} + + {/* Description */}

Description

diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/src/app/api/stats/route.ts b/apps/web/src/app/api/stats/route.ts index 268a1f9..0e398ba 100644 --- a/apps/web/src/app/api/stats/route.ts +++ b/apps/web/src/app/api/stats/route.ts @@ -25,7 +25,7 @@ export async function GET() { // Official tools count (isOfficial is at package level) prisma.tool.count({ where: { - package: { isOfficial: true } + package: { isOfficial: true }, }, }), diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index ac6f3cc..d588945 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -5,46 +5,46 @@ @layer base { /* Light mode (default) */ :root { - /* Backgrounds & Surfaces */ - --background: 0 0% 100%; /* Pure white */ - --surface: 210 20% 98%; /* Off-white */ + /* Backgrounds & Surfaces - DRAMATIC CONTRAST */ + --background: 220 15% 96%; /* Light blue-gray background */ + --surface: 0 0% 100%; /* Pure white - cards really pop! */ --surface-elevated: 0 0% 100%; /* White (elevated) */ - --surface-overlay: 0 0% 98%; /* Light gray */ + --surface-overlay: 0 0% 100%; /* White overlays */ /* Foreground (Text) */ --foreground: 222 47% 11%; /* Almost black */ - --foreground-secondary: 215 16% 47%; /* Medium gray */ - --foreground-tertiary: 215 16% 65%; /* Light gray */ - --foreground-muted: 215 16% 75%; /* Very light gray */ + --foreground-secondary: 215 25% 35%; /* Much darker for readability */ + --foreground-tertiary: 215 20% 50%; /* Medium gray */ + --foreground-muted: 215 16% 65%; /* Light gray */ - /* Borders */ - --border: 214 32% 91%; /* Light gray */ - --border-strong: 214 32% 80%; /* Medium gray */ - --border-subtle: 214 20% 95%; /* Very light gray */ + /* Borders - MUCH MORE VISIBLE */ + --border: 214 25% 80%; /* Strong medium gray */ + --border-strong: 214 30% 60%; /* Dark gray for emphasis */ + --border-subtle: 214 20% 88%; /* Subtle but visible */ - /* Interactive States */ - --primary: 222 47% 11%; /* Dark for light mode */ - --primary-foreground: 210 40% 98%; /* Light text */ - --secondary: 210 40% 96%; /* Light secondary */ + /* Interactive States - MODERN & REFINED */ + --primary: 221 83% 53%; /* Sophisticated blue */ + --primary-foreground: 0 0% 100%; /* White text */ + --secondary: 220 15% 90%; /* Subtle gray-blue bg */ --secondary-foreground: 222 47% 11%; /* Dark text */ - --accent: 210 40% 96%; /* Accent bg */ - --accent-foreground: 222 47% 11%; /* Accent text */ - --muted: 210 40% 96%; /* Muted bg */ - --muted-foreground: 215 16% 47%; /* Muted text */ + --accent: 221 75% 95%; /* Soft blue tint */ + --accent-foreground: 221 70% 35%; /* Rich blue text */ + --muted: 220 15% 92%; /* Subtle muted bg */ + --muted-foreground: 215 25% 40%; /* Darker muted text */ - /* Status Colors */ - --success: 142 71% 45%; - --success-foreground: 142 76% 15%; - --error: 0 72% 51%; - --error-foreground: 0 86% 17%; - --warning: 38 92% 50%; - --warning-foreground: 48 96% 19%; - --info: 217 91% 60%; - --info-foreground: 214 95% 23%; + /* Status Colors - Modern Editorial Palette */ + --success: 152 57% 45%; /* Refined emerald green */ + --success-foreground: 0 0% 100%; /* White text on success */ + --error: 0 65% 51%; /* Sophisticated red, less harsh */ + --error-foreground: 0 0% 100%; /* White text on error */ + --warning: 36 100% 50%; /* Warm sophisticated amber */ + --warning-foreground: 0 0% 100%; /* White text on warning */ + --info: 210 100% 56%; /* Cool modern blue */ + --info-foreground: 0 0% 100%; /* White text on info */ /* Destructive (legacy) */ - --destructive: 0 84% 60%; - --destructive-foreground: 210 40% 98%; + --destructive: 0 65% 51%; /* Match error color */ + --destructive-foreground: 0 0% 100%; /* Form Elements */ --input: 214 32% 91%; @@ -52,11 +52,11 @@ --ring-offset: 0 0% 100%; /* Grid/Blueprint Pattern */ - --grid-color: 214 32% 95%; + --grid-color: 214 32% 90%; --grid-size: 24px; /* Grid cell size */ /* Card */ - --card: 0 0% 100%; + --card: 0 0% 100%; /* Pure white - stands out dramatically on blue-gray background */ --card-foreground: 222 47% 11%; /* Border Radii */ @@ -93,9 +93,9 @@ --tracking-wider: 0.05em; --tracking-widest: 0.1em; - /* Brutalist Accent Colors */ - --brutalist-accent: 221 83% 53%; /* #0066ff - Electric blue for light mode */ - --brutalist-accent-hover: 221 83% 43%; + /* Brutalist Accent Colors - Modern Editorial */ + --brutalist-accent: 221 83% 53%; /* Sophisticated blue */ + --brutalist-accent-hover: 221 83% 45%; /* Slightly deeper on hover */ } /* Dark mode (opt-in) - Vercel/Cursor/Perplexity aesthetic */ @@ -127,19 +127,19 @@ --muted: 210 10% 12%; /* Muted background */ --muted-foreground: 210 8% 60%; /* Muted text */ - /* Status Colors (desaturated for dark mode) */ - --success: 142 71% 45%; /* #10b981 - Green */ - --success-foreground: 142 76% 95%; /* Light green text */ - --error: 0 72% 51%; /* #ef4444 - Red */ - --error-foreground: 0 86% 97%; /* Light red text */ - --warning: 38 92% 50%; /* #f59e0b - Amber */ - --warning-foreground: 48 96% 89%; /* Light amber text */ - --info: 217 91% 60%; /* #3b82f6 - Blue */ - --info-foreground: 214 95% 93%; /* Light blue text */ + /* Status Colors - Modern Editorial (Dark Mode) */ + --success: 152 57% 50%; /* Refined emerald - slightly brighter for dark */ + --success-foreground: 0 0% 100%; /* White text */ + --error: 0 65% 58%; /* Sophisticated red - brighter for dark */ + --error-foreground: 0 0% 100%; /* White text */ + --warning: 36 100% 55%; /* Warm amber - brighter for dark */ + --warning-foreground: 0 0% 100%; /* White text */ + --info: 210 100% 60%; /* Cool blue - brighter for dark */ + --info-foreground: 0 0% 100%; /* White text */ /* Destructive (legacy support) */ - --destructive: 0 72% 51%; - --destructive-foreground: 0 86% 97%; + --destructive: 0 65% 58%; + --destructive-foreground: 0 0% 100%; /* Form Elements */ --input: 210 10% 20%; /* Input border */ @@ -153,9 +153,9 @@ --card: 210 10% 8%; --card-foreground: 210 10% 90%; - /* Brutalist Accent Colors */ - --brutalist-accent: 158 100% 50%; /* #00ff88 - Neon green for dark mode */ - --brutalist-accent-hover: 158 100% 40%; + /* Brutalist Accent Colors - Modern Editorial */ + --brutalist-accent: 210 100% 60%; /* Cool sophisticated blue for dark mode */ + --brutalist-accent-hover: 210 100% 65%; /* Slightly brighter on hover */ } /* Base element styles */ diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 6d3bef9..3bc53ed 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -9,21 +9,16 @@ import { HeroSection } from '../components/home/HeroSection'; async function getHomePageData() { try { // Fetch stats in parallel - const [toolCount, simulationCount, featuredTools, categoryStats] = await Promise.all([ + const [packageCount, toolCount, featuredTools, categoryStats] = await Promise.all([ + // Total package count + prisma.package.count(), + // Total tool count prisma.tool.count(), - // Total successful simulations (as proxy for invocations) - prisma.simulation.count({ - where: { status: 'success' }, - }), - // Top 6 featured tools by quality score prisma.tool.findMany({ - orderBy: [ - { qualityScore: 'desc' }, - { package: { npmDownloadsLastMonth: 'desc' } }, - ], + orderBy: [{ qualityScore: 'desc' }, { package: { npmDownloadsLastMonth: 'desc' } }], take: 6, select: { id: true, @@ -50,30 +45,10 @@ async function getHomePageData() { }), ]); - // Calculate average latency from recent successful simulations - const recentSimulations = await prisma.simulation.findMany({ - where: { - status: 'success', - executionTimeMs: { not: null }, - }, - select: { executionTimeMs: true }, - orderBy: { createdAt: 'desc' }, - take: 100, - }); - - const avgLatency = - recentSimulations.length > 0 - ? Math.round( - recentSimulations.reduce((sum, s) => sum + (s.executionTimeMs || 0), 0) / - recentSimulations.length - ) - : 0; - return { stats: { + packageCount, toolCount, - invocations: simulationCount, - avgLatency, categoryCount: categoryStats.length, }, featuredTools, @@ -86,9 +61,8 @@ async function getHomePageData() { console.error('Failed to fetch homepage data:', error); return { stats: { + packageCount: 0, toolCount: 0, - invocations: 0, - avgLatency: 0, categoryCount: 0, }, featuredTools: [], @@ -238,17 +212,30 @@ export default async function HomePage(): Promise {

© 2025 TPMJS. All rights reserved.

- - · - - · - + + · + + GitHub + + · + + Twitter +
diff --git a/apps/web/src/app/tool/[...slug]/page.tsx b/apps/web/src/app/tool/[...slug]/page.tsx index 67b98d3..59b161d 100644 --- a/apps/web/src/app/tool/[...slug]/page.tsx +++ b/apps/web/src/app/tool/[...slug]/page.tsx @@ -464,7 +464,7 @@ export default function ToolDetailPage({ )} {/* Tags */} - {tool.tags.length > 0 && ( + {tool.tags && tool.tags.length > 0 && ( Tags diff --git a/apps/web/src/app/tool/tool-search/page.tsx b/apps/web/src/app/tool/tool-search/page.tsx index fd20a4e..e4331b9 100644 --- a/apps/web/src/app/tool/tool-search/page.tsx +++ b/apps/web/src/app/tool/tool-search/page.tsx @@ -52,6 +52,8 @@ export default function ToolSearchPage(): React.ReactElement { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [availableCategories, setAvailableCategories] = useState([]); + const [totalCount, setTotalCount] = useState(0); + const [officialCount, setOfficialCount] = useState(0); // Fetch tools from API useEffect(() => { @@ -79,14 +81,32 @@ export default function ToolSearchPage(): React.ReactElement { params.set('broken', 'true'); } - const response = await fetch(`/api/tools?${params.toString()}`); - const data = await response.json(); + // Fetch tools and counts in parallel + const [toolsResponse, allCountResponse, officialCountResponse] = await Promise.all([ + fetch(`/api/tools?${params.toString()}`), + fetch('/api/tools'), + fetch('/api/tools?official=true'), + ]); - if (data.success) { - const fetchedTools = data.data; + const [toolsData, allCountData, officialCountData] = await Promise.all([ + toolsResponse.json(), + allCountResponse.json(), + officialCountResponse.json(), + ]); + + if (toolsData.success) { + const fetchedTools = toolsData.data; setTools(fetchedTools); setError(null); + // Update counts + if (allCountData.success) { + setTotalCount(allCountData.data.length); + } + if (officialCountData.success) { + setOfficialCount(officialCountData.data.length); + } + // Extract unique categories from all tools const categories = new Set(); @@ -98,7 +118,7 @@ export default function ToolSearchPage(): React.ReactElement { setAvailableCategories(Array.from(categories).sort()); } else { - setError(data.error || 'Failed to fetch tools'); + setError(toolsData.error || 'Failed to fetch tools'); } } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); @@ -186,11 +206,11 @@ export default function ToolSearchPage(): React.ReactElement { {/* Tabs */} t.package.isOfficial).length, + count: officialCount, }, ]} activeTab={activeTab} diff --git a/apps/web/src/components/home/HeroSection.tsx b/apps/web/src/components/home/HeroSection.tsx index 95b1625..e95e075 100644 --- a/apps/web/src/components/home/HeroSection.tsx +++ b/apps/web/src/components/home/HeroSection.tsx @@ -7,9 +7,8 @@ import { useState } from 'react'; interface HeroSectionProps { stats: { + packageCount: number; toolCount: number; - invocations: number; - avgLatency: number; categoryCount: number; }; } @@ -71,23 +70,15 @@ export function HeroSection({ stats }: HeroSectionProps): React.ReactElement { {/* Live Metrics Strip */}
+
+ {formatNumber(stats.packageCount)} + PACKAGES +
+ /
{formatNumber(stats.toolCount)} TOOLS
- / -
- - {formatNumber(stats.invocations)} - {stats.invocations >= 1000 ? '+' : ''} - - INVOCATIONS -
- / -
- {stats.avgLatency || '--'}ms - AVG LATENCY -
{/* Subheading */} diff --git a/apps/web/src/lib/ai-agent/tool-executor-agent.ts b/apps/web/src/lib/ai-agent/tool-executor-agent.ts index 574987d..86bb256 100644 --- a/apps/web/src/lib/ai-agent/tool-executor-agent.ts +++ b/apps/web/src/lib/ai-agent/tool-executor-agent.ts @@ -109,9 +109,7 @@ export function createToolDefinition(tool: Tool & { package: Package }) { console.log('[createToolDefinition] Created Zod schema:', inputSchema); - const sanitizedName = sanitizeToolName( - `${tool.package.npmPackageName}-${tool.exportName}` - ); + const sanitizedName = sanitizeToolName(`${tool.package.npmPackageName}-${tool.exportName}`); // AI SDK v6 tool definition return { @@ -199,9 +197,7 @@ export async function executeToolWithAgent( onTokenUpdate?: (tokens: Partial) => void ) { const toolDef = createToolDefinition(tool); - const sanitizedToolName = sanitizeToolName( - `${tool.package.npmPackageName}-${tool.exportName}` - ); + const sanitizedToolName = sanitizeToolName(`${tool.package.npmPackageName}-${tool.exportName}`); console.log('[executeToolWithAgent] Tool name:', sanitizedToolName); diff --git a/packages/tools/hello/package.json b/packages/tools/hello/package.json index da3ffcb..c0ae555 100644 --- a/packages/tools/hello/package.json +++ b/packages/tools/hello/package.json @@ -10,17 +10,10 @@ "clean": "rm -rf dist", "type-check": "tsc --noEmit" }, - "keywords": [ - "tpmjs-tool", - "ai-sdk", - "hello", - "example" - ], + "keywords": ["tpmjs-tool", "ai-sdk", "hello", "example"], "tpmjs": { "category": "text-analysis", - "frameworks": [ - "vercel-ai" - ], + "frameworks": ["vercel-ai"], "tools": [ { "exportName": "helloWorldTool", @@ -70,8 +63,5 @@ "tsx": "^4.20.6", "typescript": "^5.9.3" }, - "files": [ - "dist", - "README.md" - ] + "files": ["dist", "README.md"] } diff --git a/packages/tools/search-registry/src/index.ts b/packages/tools/search-registry/src/index.ts index 2e488c4..1cf6c8b 100644 --- a/packages/tools/search-registry/src/index.ts +++ b/packages/tools/search-registry/src/index.ts @@ -118,6 +118,10 @@ export const searchTpmjsToolsTool = tool({ env: tool.package.env, version: tool.package.npmVersion, importUrl: `https://esm.sh/${tool.package.npmPackageName}@${tool.package.npmVersion}`, + importHealth: tool.importHealth, + executionHealth: tool.executionHealth, + healthCheckError: tool.healthCheckError, + lastHealthCheck: tool.lastHealthCheck, })), }; }, diff --git a/packages/types/src/tpmjs.ts b/packages/types/src/tpmjs.ts index def5486..920761c 100644 --- a/packages/types/src/tpmjs.ts +++ b/packages/types/src/tpmjs.ts @@ -170,9 +170,10 @@ export function validateTpmjsField(tpmjs: unknown): ValidationResult { const data = multiResult.data; // Determine tier based on tool richness - const hasRichFields = data.tools.some( - (tool) => tool.parameters || tool.returns || tool.aiAgent - ) || data.env || data.frameworks; + const hasRichFields = + data.tools.some((tool) => tool.parameters || tool.returns || tool.aiAgent) || + data.env || + data.frameworks; return { valid: true, @@ -203,8 +204,11 @@ export function validateTpmjsField(tpmjs: unknown): ValidationResult { }; const hasRichFields = - legacyData.parameters || legacyData.returns || legacyData.env || - legacyData.frameworks || legacyData.aiAgent; + legacyData.parameters || + legacyData.returns || + legacyData.env || + legacyData.frameworks || + legacyData.aiAgent; return { valid: true, diff --git a/packages/ui/package.json b/packages/ui/package.json index 34069fb..b9808fa 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -134,6 +134,14 @@ "./ActivityStream/ActivityStream": { "types": "./dist/ActivityStream/ActivityStream.d.ts", "default": "./dist/ActivityStream/ActivityStream.js" + }, + "./ToolHealthBadge/ToolHealthBadge": { + "types": "./dist/ToolHealthBadge/ToolHealthBadge.d.ts", + "default": "./dist/ToolHealthBadge/ToolHealthBadge.js" + }, + "./ToolHealthBanner/ToolHealthBanner": { + "types": "./dist/ToolHealthBanner/ToolHealthBanner.d.ts", + "default": "./dist/ToolHealthBanner/ToolHealthBanner.js" } }, "files": ["dist"], @@ -152,6 +160,7 @@ }, "dependencies": { "@tpmjs/utils": "workspace:*", + "react-syntax-highlighter": "^16.1.0", "zod": "^4.1.13" }, "devDependencies": { @@ -163,6 +172,7 @@ "@tpmjs/tsconfig": "workspace:*", "@types/react": "^19.0.2", "@types/react-dom": "^19.0.2", + "@types/react-syntax-highlighter": "^15.5.13", "eslint": "^9.39.1", "glob": "^13.0.0", "happy-dom": "^15.11.7", diff --git a/packages/ui/src/CodeBlock/CodeBlock.tsx b/packages/ui/src/CodeBlock/CodeBlock.tsx index e274b92..ea68aa8 100644 --- a/packages/ui/src/CodeBlock/CodeBlock.tsx +++ b/packages/ui/src/CodeBlock/CodeBlock.tsx @@ -1,5 +1,7 @@ import { cn } from '@tpmjs/utils/cn'; import { forwardRef, useState } from 'react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { Icon } from '../Icon/Icon'; import type { CodeBlockProps } from './types'; import { @@ -8,11 +10,75 @@ import { codeBlockCopyButtonVariants, } from './variants'; +// Custom light theme with better contrast +const customLightTheme = { + 'code[class*="language-"]': { + color: '#24292e', + fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace', + fontSize: '1em', + textAlign: 'left', + whiteSpace: 'pre', + wordSpacing: 'normal', + wordBreak: 'normal', + wordWrap: 'normal', + lineHeight: '1.5', + tabSize: '4', + hyphens: 'none', + }, + 'pre[class*="language-"]': { + color: '#24292e', + fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace', + fontSize: '1em', + textAlign: 'left', + whiteSpace: 'pre', + wordSpacing: 'normal', + wordBreak: 'normal', + wordWrap: 'normal', + lineHeight: '1.5', + tabSize: '4', + hyphens: 'none', + padding: '1em', + margin: '0.5em 0', + overflow: 'auto', + }, + comment: { color: '#6a737d', fontStyle: 'italic' }, + prolog: { color: '#6a737d' }, + doctype: { color: '#6a737d' }, + cdata: { color: '#6a737d' }, + punctuation: { color: '#24292e' }, + property: { color: '#005cc5' }, + tag: { color: '#22863a' }, + boolean: { color: '#005cc5' }, + number: { color: '#005cc5' }, + constant: { color: '#005cc5' }, + symbol: { color: '#005cc5' }, + deleted: { color: '#b31d28' }, + selector: { color: '#22863a' }, + 'attr-name': { color: '#6f42c1' }, + string: { color: '#032f62' }, + char: { color: '#032f62' }, + builtin: { color: '#005cc5' }, + inserted: { color: '#22863a' }, + operator: { color: '#d73a49' }, + entity: { color: '#6f42c1' }, + url: { color: '#032f62' }, + variable: { color: '#e36209' }, + atrule: { color: '#d73a49' }, + 'attr-value': { color: '#032f62' }, + function: { color: '#6f42c1' }, + 'class-name': { color: '#6f42c1' }, + keyword: { color: '#d73a49' }, + regex: { color: '#032f62' }, + important: { color: '#d73a49', fontWeight: 'bold' }, + bold: { fontWeight: 'bold' }, + italic: { fontStyle: 'italic' }, +}; + /** * CodeBlock component * - * Displays formatted code with optional copy functionality. - * Includes syntax-highlighted display and copy-to-clipboard button. + * Displays formatted code with syntax highlighting and optional copy functionality. + * Uses Prism syntax highlighter for beautiful code display. * Built with React and JSX. * * @example @@ -32,7 +98,10 @@ import { * ``` */ export const CodeBlock = forwardRef( - ({ className, code, language = 'text', size = 'md', showCopy = true, ...props }, ref) => { + ( + { className, code, language = 'text', size = 'md', showCopy = true, theme = 'light', ...props }, + ref + ) => { const [copied, setCopied] = useState(false); const handleCopy = async () => { @@ -46,16 +115,42 @@ export const CodeBlock = forwardRef( } }; + // Map language aliases to supported languages + const languageMap: Record = { + js: 'javascript', + ts: 'typescript', + jsx: 'jsx', + tsx: 'tsx', + py: 'python', + rb: 'ruby', + sh: 'bash', + yml: 'yaml', + }; + + const normalizedLanguage = languageMap[language] || language; + return (
- - {code} - +
+ + {code} + +
{showCopy && ( + )} +
+
+ + ); +} diff --git a/packages/ui/src/system/formVariants.ts b/packages/ui/src/system/formVariants.ts index 0302f4d..1d019b1 100644 --- a/packages/ui/src/system/formVariants.ts +++ b/packages/ui/src/system/formVariants.ts @@ -14,8 +14,8 @@ export const formInputBase = [ 'font-sans text-base', // Borders and radius 'rounded-md border border-border', - // Colors - 'bg-background text-foreground', + // Colors - Pure white background to stand out + 'bg-surface text-foreground', // Placeholder 'placeholder:text-foreground-tertiary', // Transitions diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6aae816..62b6a34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,10 +131,10 @@ importers: version: 10.4.22(postcss@8.5.6) eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@1.21.7) + version: 9.39.1(jiti@2.6.1) eslint-config-next: specifier: ^16.0.4 - version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) postcss: specifier: ^8.5.1 version: 8.5.6 @@ -571,6 +571,9 @@ importers: '@tpmjs/utils': specifier: workspace:* version: link:../utils + react-syntax-highlighter: + specifier: ^16.1.0 + version: 16.1.0(react@19.2.0) zod: specifier: ^4.1.13 version: 4.1.13 @@ -599,6 +602,9 @@ importers: '@types/react-dom': specifier: ^19.0.2 version: 19.2.3(@types/react@19.2.7) + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 eslint: specifier: ^9.39.1 version: 9.39.1(jiti@2.6.1) @@ -6882,11 +6888,6 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@1.21.7))': - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -7940,23 +7941,6 @@ snapshots: '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 - eslint: 9.39.1(jiti@1.21.7) - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -7974,18 +7958,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 - debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.48.0 @@ -8016,18 +7988,6 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.48.0 @@ -8057,17 +8017,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -9246,32 +9195,12 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): - dependencies: - '@next/eslint-plugin-next': 16.0.4 - eslint: 9.39.1(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react-hooks: 7.0.1(eslint@9.39.1(jiti@1.21.7)) - globals: 16.4.0 - typescript-eslint: 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - eslint-config-next@16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.0.4 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) @@ -9294,22 +9223,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - get-tsconfig: 4.13.0 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.15 - unrs-resolver: 1.11.1 - optionalDependencies: - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -9334,23 +9248,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): - dependencies: - debug: 3.2.7 - optionalDependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -9383,33 +9287,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.9 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.39.1(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 @@ -9421,7 +9298,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9437,25 +9314,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.9 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.11.0 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 9.39.1(jiti@1.21.7) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: aria-query: 5.3.2 @@ -9479,17 +9337,6 @@ snapshots: dependencies: eslint: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - eslint: 9.39.1(jiti@1.21.7) - hermes-parser: 0.25.1 - zod: 4.1.13 - zod-validation-error: 4.0.2(zod@4.1.13) - transitivePeerDependencies: - - supports-color - eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: '@babel/core': 7.28.5 @@ -9501,28 +9348,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.7)): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.39.1(jiti@1.21.7) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -9554,47 +9379,6 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@1.21.7): - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 1.21.7 - transitivePeerDependencies: - - supports-color - eslint@9.39.1(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -12448,17 +12232,6 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - typescript-eslint@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) diff --git a/sync-manual-tools.ts b/sync-manual-tools.ts index 3ffad02..1475672 100644 --- a/sync-manual-tools.ts +++ b/sync-manual-tools.ts @@ -1,12 +1,12 @@ -import { prisma } from './packages/db/src/index.js'; import { manualTools } from './manual-tools.js'; +import { prisma } from './packages/db/src/index.js'; import { fetchLatestPackageWithMetadata } from './packages/npm-client/src/package.js'; async function syncManualTools() { console.log('\n🔧 Starting manual tools sync...\n'); let processed = 0; - let skipped = 0; + const skipped = 0; let errors = 0; for (const manualTool of manualTools) { @@ -26,7 +26,8 @@ async function syncManualTools() { const version = manualTool.npmVersion || npmData.version; // Get published date - const publishedAt = npmData.time?.[version] || npmData.time?.modified || new Date().toISOString(); + const publishedAt = + npmData.time?.[version] || npmData.time?.modified || new Date().toISOString(); // Upsert Package record const packageRecord = await prisma.package.upsert({ @@ -119,7 +120,7 @@ async function syncManualTools() { console.log(` Total manual tools: ${manualTools.length}\n`); } -function calculateTier(tool: typeof manualTools[0]): 'minimal' | 'rich' { +function calculateTier(tool: (typeof manualTools)[0]): 'minimal' | 'rich' { // Tier is 'rich' if tool has parameters OR returns OR aiAgent if (tool.parameters || tool.returns || tool.aiAgent) { return 'rich';