feat: add hot-swappable executor support for collections and agents
- Add executor configuration to Collection and Agent models in Prisma schema - Create ExecutorConfigPanel component for selecting default or custom executors - Add executor resolution logic with cascade (Agent → Collection → System Default) - Create /api/executors/verify endpoint to test custom executor connectivity - Add executor documentation page at /docs/executors with API specification - Create deployable Vercel executor template in templates/vercel-executor/ - Update MCP handlers and agent tool execution to use configurable executors - Add executor types and schemas to @tpmjs/types package Users can now deploy their own executor instances and configure collections or agents to use custom executors instead of the TPMJS default executor. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
84d894e920
commit
bc36d366bc
27 changed files with 1909 additions and 85 deletions
|
|
@ -36,6 +36,10 @@
|
|||
"./user": {
|
||||
"types": "./dist/user.d.ts",
|
||||
"default": "./dist/user.js"
|
||||
},
|
||||
"./executor": {
|
||||
"types": "./dist/executor.d.ts",
|
||||
"default": "./dist/executor.js"
|
||||
}
|
||||
},
|
||||
"files": ["dist"],
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { ExecutorTypeSchema } from './executor';
|
||||
|
||||
// Regex for valid agent UID: lowercase alphanumeric and hyphens
|
||||
const UID_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
||||
|
||||
// Executor config for updates (simplified schema that maps to database JSON)
|
||||
const ExecutorConfigUpdateSchema = z
|
||||
.object({
|
||||
url: z.string().url(),
|
||||
apiKey: z.string().optional(),
|
||||
})
|
||||
.nullable()
|
||||
.optional();
|
||||
|
||||
// ============================================================================
|
||||
// Enums
|
||||
// ============================================================================
|
||||
|
|
@ -49,6 +60,9 @@ export const UpdateAgentSchema = z.object({
|
|||
maxToolCallsPerTurn: z.number().int().min(1).max(100).optional(),
|
||||
maxMessagesInContext: z.number().int().min(1).max(100).optional(),
|
||||
isPublic: z.boolean().optional(),
|
||||
// Executor configuration
|
||||
executorType: ExecutorTypeSchema.nullable().optional(),
|
||||
executorConfig: ExecutorConfigUpdateSchema,
|
||||
});
|
||||
|
||||
export const AddCollectionToAgentSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { ExecutorTypeSchema } from './executor';
|
||||
|
||||
// Regex for valid collection names: letters, numbers, spaces, hyphens, underscores
|
||||
const NAME_REGEX = /^[a-zA-Z0-9\s\-_]+$/;
|
||||
|
||||
// Executor config for updates (simplified schema that maps to database JSON)
|
||||
const ExecutorConfigUpdateSchema = z
|
||||
.object({
|
||||
url: z.string().url(),
|
||||
apiKey: z.string().optional(),
|
||||
})
|
||||
.nullable()
|
||||
.optional();
|
||||
|
||||
// ============================================================================
|
||||
// Collection Schemas
|
||||
// ============================================================================
|
||||
|
|
@ -30,6 +41,9 @@ export const UpdateCollectionSchema = z.object({
|
|||
.nullable()
|
||||
.optional(),
|
||||
isPublic: z.boolean().optional(),
|
||||
// Executor configuration
|
||||
executorType: ExecutorTypeSchema.nullable().optional(),
|
||||
executorConfig: ExecutorConfigUpdateSchema,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
146
packages/types/src/executor.ts
Normal file
146
packages/types/src/executor.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Executor API Types
|
||||
*
|
||||
* These types define the contract between TPMJS and any executor service.
|
||||
* Custom executors must implement the ExecuteToolRequest/Response interface.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
// =============================================================================
|
||||
// Executor API Specification
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Request payload for POST /execute-tool
|
||||
*/
|
||||
export interface ExecuteToolRequest {
|
||||
/** NPM package name, e.g., "@tpmjs/hello" */
|
||||
packageName: string;
|
||||
/** Tool name within the package, e.g., "helloWorld" */
|
||||
name: string;
|
||||
/** Package version, e.g., "1.0.0" or "latest" */
|
||||
version?: string;
|
||||
/** Direct esm.sh URL override for the package */
|
||||
importUrl?: string;
|
||||
/** Tool parameters to pass to execute() */
|
||||
params: Record<string, unknown>;
|
||||
/** Environment variables to inject during execution */
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from POST /execute-tool
|
||||
*/
|
||||
export interface ExecuteToolResponse {
|
||||
/** Whether the execution succeeded */
|
||||
success: boolean;
|
||||
/** Tool output on success */
|
||||
output?: unknown;
|
||||
/** Error message on failure */
|
||||
error?: string;
|
||||
/** Execution duration in milliseconds */
|
||||
executionTimeMs: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from GET /health (optional but recommended)
|
||||
*/
|
||||
export interface ExecutorHealthResponse {
|
||||
/** Executor status */
|
||||
status: 'ok' | 'degraded' | 'error';
|
||||
/** Executor version string */
|
||||
version?: string;
|
||||
/** Optional additional info */
|
||||
info?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Executor Configuration Schemas
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Executor type enum
|
||||
*/
|
||||
export const ExecutorTypeSchema = z.enum(['default', 'custom_url']);
|
||||
export type ExecutorType = z.infer<typeof ExecutorTypeSchema>;
|
||||
|
||||
/**
|
||||
* Default executor config (uses TPMJS Railway executor)
|
||||
*/
|
||||
export const DefaultExecutorConfigSchema = z.object({
|
||||
type: z.literal('default'),
|
||||
});
|
||||
|
||||
/**
|
||||
* Custom URL executor config
|
||||
*/
|
||||
export const CustomUrlExecutorConfigSchema = z.object({
|
||||
type: z.literal('custom_url'),
|
||||
/** URL of the custom executor (must be HTTPS in production) */
|
||||
url: z.string().url(),
|
||||
/** Optional API key for Bearer token authentication */
|
||||
apiKey: z.string().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Union of all executor config types
|
||||
*/
|
||||
export const ExecutorConfigSchema = z.discriminatedUnion('type', [
|
||||
DefaultExecutorConfigSchema,
|
||||
CustomUrlExecutorConfigSchema,
|
||||
]);
|
||||
|
||||
export type ExecutorConfig = z.infer<typeof ExecutorConfigSchema>;
|
||||
export type DefaultExecutorConfig = z.infer<typeof DefaultExecutorConfigSchema>;
|
||||
export type CustomUrlExecutorConfig = z.infer<typeof CustomUrlExecutorConfigSchema>;
|
||||
|
||||
// =============================================================================
|
||||
// Zod Schemas for Request/Response Validation
|
||||
// =============================================================================
|
||||
|
||||
export const ExecuteToolRequestSchema = z.object({
|
||||
packageName: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
version: z.string().optional(),
|
||||
importUrl: z.string().url().optional(),
|
||||
params: z.record(z.string(), z.unknown()),
|
||||
env: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
|
||||
export const ExecuteToolResponseSchema = z.object({
|
||||
success: z.boolean(),
|
||||
output: z.unknown().optional(),
|
||||
error: z.string().optional(),
|
||||
executionTimeMs: z.number(),
|
||||
});
|
||||
|
||||
export const ExecutorHealthResponseSchema = z.object({
|
||||
status: z.enum(['ok', 'degraded', 'error']),
|
||||
version: z.string().optional(),
|
||||
info: z.record(z.string(), z.unknown()).optional(),
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// Executor Verification
|
||||
// =============================================================================
|
||||
|
||||
export const VerifyExecutorRequestSchema = z.object({
|
||||
url: z.string().url(),
|
||||
apiKey: z.string().optional(),
|
||||
});
|
||||
|
||||
export interface VerifyExecutorRequest {
|
||||
url: string;
|
||||
apiKey?: string;
|
||||
}
|
||||
|
||||
export interface VerifyExecutorResponse {
|
||||
valid: boolean;
|
||||
healthCheck?: ExecutorHealthResponse;
|
||||
testExecution?: {
|
||||
success: boolean;
|
||||
executionTimeMs: number;
|
||||
};
|
||||
errors?: string[];
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ export default defineConfig({
|
|||
'src/collection.ts',
|
||||
'src/agent.ts',
|
||||
'src/user.ts',
|
||||
'src/executor.ts',
|
||||
],
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue