fix: require name field in TPMJS spec, add workflow permissions

Breaking change for TPMJS spec:
- Remove `exportName` field support from TpmjsToolDefinitionSchema
- Make `name` field required (replaces deprecated `exportName`)
- Update create-basic-tools to generate `name` field
- Update sync routes to use `name` from validated schema
- Add `permissions: contents: write` to Vercel registry sync workflow

Packages using the old `exportName` field must update to use `name`.
This commit is contained in:
Ajax Davis 2025-12-17 16:20:20 +10:00
parent b7ea9bf4d6
commit 5e795bc086
5 changed files with 22 additions and 38 deletions

View file

@ -12,6 +12,9 @@ on:
paths:
- 'sync-vercel-registry.ts'
permissions:
contents: write
jobs:
sync-vercel:
runs-on: ubuntu-latest

View file

@ -167,8 +167,8 @@ export async function POST(request: NextRequest) {
// Upsert each tool
for (const toolDef of toolsToProcess) {
// Use 'name' field (new) or fall back to 'exportName' (legacy support)
const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName;
// Get tool name from validated schema
const toolName = toolDef.name;
if (!toolName) {
console.warn(`Skipping tool without name in ${pkg.name}`);
continue;
@ -255,10 +255,7 @@ export async function POST(request: NextRequest) {
// Delete orphaned tools (tools removed from package.json)
const orphanedTools = existingTools.filter(
(existingTool) =>
!toolsToProcess.some((toolDef) => {
const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName;
return toolName === existingTool.exportName;
})
!toolsToProcess.some((toolDef) => toolDef.name === existingTool.exportName)
);
if (orphanedTools.length > 0) {

View file

@ -186,8 +186,8 @@ export async function POST(request: NextRequest) {
// Upsert each tool
for (const toolDef of toolsToProcess) {
// Use 'name' field (new) or fall back to 'exportName' (legacy support)
const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName;
// Get tool name from validated schema
const toolName = toolDef.name;
if (!toolName) {
console.warn(`Skipping tool without name in ${pkg.name}`);
continue;
@ -274,10 +274,7 @@ export async function POST(request: NextRequest) {
// Delete orphaned tools (tools removed from package.json)
const orphanedTools = existingTools.filter(
(existingTool) =>
!toolsToProcess.some((toolDef) => {
const toolName = toolDef.name || (toolDef as { exportName?: string }).exportName;
return toolName === existingTool.exportName;
})
!toolsToProcess.some((toolDef) => toolDef.name === existingTool.exportName)
);
if (orphanedTools.length > 0) {

View file

@ -24,11 +24,8 @@ export function generatePackageJson(config: GeneratorConfig): string {
tpmjs: {
category: packageInfo.category,
tools: tools.map((tool) => ({
exportName: tool.exportName,
name: tool.exportName,
description: tool.description,
...(tool.parameters && { parameters: tool.parameters }),
...(tool.returns && { returns: tool.returns }),
...(tool.aiAgent && { aiAgent: tool.aiAgent }),
})),
...(tools.some((t) => t.env) && {
env: tools.flatMap((t) => t.env || []),

View file

@ -90,28 +90,18 @@ export type TpmjsAiAgent = z.infer<typeof TpmjsAiAgentSchema>;
* - returns: Tool return type - auto-extracted from tool
* - aiAgent: AI agent guidance - auto-extracted from tool
*/
export const TpmjsToolDefinitionSchema = z
.object({
// New field name (preferred)
name: z.string().min(1).optional(),
// @deprecated - use 'name' instead. Kept for backward compatibility.
exportName: z.string().min(1).optional(),
// Optional - auto-extracted from tool if not provided
description: z
.string()
.min(20, 'Description must be at least 20 characters')
.max(500)
.optional(),
// @deprecated - now auto-extracted from tool's inputSchema
parameters: z.array(TpmjsParameterSchema).optional(),
// @deprecated - now auto-extracted from tool
returns: TpmjsReturnsSchema.optional(),
// @deprecated - now auto-extracted from tool
aiAgent: TpmjsAiAgentSchema.optional(),
})
.refine((data) => data.name || data.exportName, {
message: 'Either name or exportName is required',
});
export const TpmjsToolDefinitionSchema = z.object({
// Required: The export name of the tool from the package
name: z.string().min(1),
// Optional - auto-extracted from tool if not provided
description: z.string().min(20, 'Description must be at least 20 characters').max(500).optional(),
// @deprecated - now auto-extracted from tool's inputSchema
parameters: z.array(TpmjsParameterSchema).optional(),
// @deprecated - now auto-extracted from tool
returns: TpmjsReturnsSchema.optional(),
// @deprecated - now auto-extracted from tool
aiAgent: TpmjsAiAgentSchema.optional(),
});
export type TpmjsToolDefinition = z.infer<typeof TpmjsToolDefinitionSchema>;