refactor: replace exportName with name throughout codebase

- Update TpmjsToolDefinitionSchema to only use 'name' field
- Add 'sandbox' as valid category for sprites tools
- Update all package.json files to use 'name' instead of 'exportName'
- Update documentation and source files accordingly
- Add 11 new sprites tools for sandbox/code-execution
This commit is contained in:
Ajax Davis 2026-01-14 14:18:36 +10:00
parent b1dd3371cd
commit 2cd2b10cd0
91 changed files with 4019 additions and 195 deletions

View file

@ -21,7 +21,7 @@ Load a tool from esm.sh and return its schema
```json
{
"packageName": "firecrawl-aisdk",
"exportName": "webSearchTool",
"name": "webSearchTool",
"version": "0.7.2",
"importUrl": "https://esm.sh/firecrawl-aisdk@0.7.2"
}
@ -32,7 +32,7 @@ Load a tool from esm.sh and return its schema
{
"success": true,
"tool": {
"exportName": "webSearchTool",
"name": "webSearchTool",
"description": "Search the web using Firecrawl",
"inputSchema": { ... }
}
@ -46,7 +46,7 @@ Execute a tool with parameters
```json
{
"packageName": "firecrawl-aisdk",
"exportName": "webSearchTool",
"name": "webSearchTool",
"version": "0.7.2",
"params": {
"query": "latest AI news"
@ -128,7 +128,7 @@ curl -X POST http://localhost:3001/load-and-describe \
-H "Content-Type: application/json" \
-d '{
"packageName": "@tpmjs/hello",
"exportName": "helloWorldTool",
"name": "helloWorldTool",
"version": "0.1.0"
}'
```
@ -139,7 +139,7 @@ curl -X POST http://localhost:3001/execute-tool \
-H "Content-Type: application/json" \
-d '{
"packageName": "@tpmjs/hello",
"exportName": "helloWorldTool",
"name": "helloWorldTool",
"version": "0.1.0",
"params": {}
}'

View file

@ -34,16 +34,16 @@ app.get('/health', (req, res) => {
* Returns tool metadata (description, schema) without executing
*/
app.post('/load-and-describe', async (req, res) => {
const { packageName, exportName, version, importUrl } = req.body;
const { packageName, name, version, importUrl } = req.body;
if (!packageName || !exportName || !version) {
if (!packageName || !name || !version) {
return res.status(400).json({
success: false,
error: 'Missing required fields: packageName, exportName, version',
error: 'Missing required fields: packageName, name, version',
});
}
const cacheKey = `${packageName}::${exportName}`;
const cacheKey = `${packageName}::${name}`;
try {
let toolModule;
@ -72,13 +72,13 @@ app.post('/load-and-describe', async (req, res) => {
// esm.sh returns ES modules, try to get default or named export
const module = moduleExports.default || moduleExports;
toolModule = module[exportName] || module;
toolModule = module[name] || module;
if (!toolModule) {
console.error(`❌ Export "${exportName}" not found. Available:`, Object.keys(module));
console.error(`❌ Export "${name}" not found. Available:`, Object.keys(module));
return res.status(404).json({
success: false,
error: `Export "${exportName}" not found in module`,
error: `Export "${name}" not found in module`,
availableExports: Object.keys(module),
});
}
@ -108,7 +108,7 @@ app.post('/load-and-describe', async (req, res) => {
res.json({
success: true,
tool: {
exportName,
name,
description: toolModule.description,
inputSchema: toolModule.inputSchema || toolModule.parameters?.shape || {},
},
@ -127,16 +127,16 @@ app.post('/load-and-describe', async (req, res) => {
* Execute a dynamically loaded tool with parameters
*/
app.post('/execute-tool', async (req, res) => {
const { packageName, exportName, version, importUrl, params } = req.body;
const { packageName, name, version, importUrl, params } = req.body;
if (!packageName || !exportName || !version) {
if (!packageName || !name || !version) {
return res.status(400).json({
success: false,
error: 'Missing required fields: packageName, exportName, version',
error: 'Missing required fields: packageName, name, version',
});
}
const cacheKey = `${packageName}::${exportName}`;
const cacheKey = `${packageName}::${name}`;
const startTime = Date.now();
try {
@ -151,7 +151,7 @@ app.post('/execute-tool', async (req, res) => {
console.log(`📦 Importing for execution: ${url}`);
const module = await import(url);
toolModule = module[exportName];
toolModule = module[name];
if (!toolModule || !toolModule.execute) {
return res.status(404).json({

View file

@ -550,9 +550,9 @@ async function executeTool(req: Request): Promise<Response> {
Deno.env.set(key, stringValue);
// ALSO set in Node.js process.env (for npm: imports)
// @ts-ignore - process is available in Node.js compatibility mode
// @ts-expect-error - process is available in Node.js compatibility mode
if (typeof globalThis.process !== 'undefined' && globalThis.process.env) {
// @ts-ignore - process.env exists in Node compat mode
// @ts-expect-error - process.env exists in Node compat mode
globalThis.process.env[key] = stringValue;
}
@ -782,10 +782,10 @@ async function listExports(req: Request): Promise<Response> {
error?: string;
}> = [];
for (const exportName of allExports) {
if (exportName === 'default') continue;
for (const exportKey of allExports) {
if (exportKey === 'default') continue;
let rawExport = module[exportName];
let rawExport = module[exportKey];
// Check if it's a factory function
if (typeof rawExport === 'function' && !rawExport.description && !rawExport.execute) {
@ -809,14 +809,14 @@ async function listExports(req: Request): Promise<Response> {
// Check if it's a valid AI SDK tool
if (rawExport?.description && rawExport?.execute) {
tools.push({
name: exportName,
name: exportKey,
isValidTool: true,
description: rawExport.description,
});
} else if (typeof rawExport === 'object' && rawExport !== null) {
// It's an object but not a valid tool - might be a factory that needs specific config
tools.push({
name: exportName,
name: exportKey,
isValidTool: false,
error: 'Not a valid AI SDK tool (missing description or execute)',
});

View file

@ -10,27 +10,27 @@ interface ToolDetailPageProps {
}
/**
* Parse the URL slug to extract package name and optional export name
* Parse the URL slug to extract package name and optional tool name
*/
function parseSlug(slug: string[]): { packageName: string; exportName?: string } {
function parseSlug(slug: string[]): { packageName: string; toolName?: string } {
// URL-decode slug components (@ comes as %40)
const decodedSlug = slug.map((s) => decodeURIComponent(s));
if (decodedSlug[0]?.startsWith('@')) {
// Scoped package: ['@scope', 'package', 'exportName?']
// Scoped package: ['@scope', 'package', 'toolName?']
const packageName = decodedSlug.slice(0, 2).join('/');
const exportName = decodedSlug[2];
return { packageName, exportName };
const toolName = decodedSlug[2];
return { packageName, toolName };
}
// Unscoped: ['package', 'exportName?']
return { packageName: decodedSlug[0] || '', exportName: decodedSlug[1] };
// Unscoped: ['package', 'toolName?']
return { packageName: decodedSlug[0] || '', toolName: decodedSlug[1] };
}
/**
* Fetch tool data from database
*/
async function getTool(slug: string[]): Promise<Tool | null> {
const { packageName, exportName } = parseSlug(slug);
const { packageName, toolName } = parseSlug(slug);
if (!packageName) {
return null;
@ -49,7 +49,7 @@ async function getTool(slug: string[]): Promise<Tool | null> {
const tool = await prisma.tool.findFirst({
where: {
packageId: pkg.id,
...(exportName && { name: exportName }),
...(toolName && { name: toolName }),
},
include: {
package: true,
@ -122,9 +122,9 @@ export async function generateMetadata({ params }: ToolDetailPageProps): Promise
};
}
const { packageName, exportName } = parseSlug(slug);
const ogPath = exportName
? `/api/og/tool/${encodeURIComponent(packageName)}/${encodeURIComponent(exportName)}`
const { packageName, toolName } = parseSlug(slug);
const ogPath = toolName
? `/api/og/tool/${encodeURIComponent(packageName)}/${encodeURIComponent(toolName)}`
: `/api/og/tool/${encodeURIComponent(packageName)}`;
return {

View file

@ -102,41 +102,41 @@ export function normalizePath(path: string): string {
}
/**
* Parse tool path to extract package name and export name
* Parse tool path to extract package name and tool name
*/
function parseToolPath(path: string): { packageName: string; exportName?: string } {
function parseToolPath(path: string): { packageName: string; toolName?: string } {
// Remove /tool/ prefix
const segments = path.replace(/^\/tool\//, '').split('/');
let packageName: string;
let exportName: string | undefined;
let toolName: string | undefined;
if (segments[0]?.startsWith('@')) {
// Scoped package: @scope/package/export
// Scoped package: @scope/package/toolName
packageName = segments.slice(0, 2).join('/');
exportName = segments[2];
toolName = segments[2];
} else {
// Unscoped: package/export
// Unscoped: package/toolName
packageName = segments[0] || '';
exportName = segments[1];
toolName = segments[1];
}
return { packageName, exportName };
return { packageName, toolName };
}
/**
* Fetch tool data from internal API
*/
async function fetchToolContent(path: string): Promise<PageContent> {
const { packageName, exportName } = parseToolPath(path);
const { packageName, toolName } = parseToolPath(path);
// Build API URL
const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
const apiPath = exportName
? `/api/tools/${encodeURIComponent(packageName)}/${encodeURIComponent(exportName)}`
const apiPath = toolName
? `/api/tools/${encodeURIComponent(packageName)}/${encodeURIComponent(toolName)}`
: `/api/tools/${encodeURIComponent(packageName)}`;
try {
@ -157,11 +157,11 @@ async function fetchToolContent(path: string): Promise<PageContent> {
return {
pageType: 'tool',
title: tool.name || exportName || packageName,
title: tool.name || toolName || packageName,
description: tool.description || `AI tool from ${packageName}`,
keywords: [tool.package?.category || 'tool', 'AI', 'npm', packageName],
tool: {
name: tool.name || exportName || 'Tool',
name: tool.name || toolName || 'Tool',
packageName: tool.package?.npmPackageName || packageName,
category: tool.package?.category || 'other',
description: tool.description || '',
@ -175,11 +175,11 @@ async function fetchToolContent(path: string): Promise<PageContent> {
// Return basic content on failure
return {
pageType: 'tool',
title: exportName || packageName,
title: toolName || packageName,
description: `AI tool from ${packageName}`,
keywords: ['tool', 'AI', 'npm'],
tool: {
name: exportName || 'Tool',
name: toolName || 'Tool',
packageName,
category: 'other',
description: '',