Universal Command Pattern

定义一次命令,自动部署至CLI、API和MCP,确保接口一致性。

已扫描
适合谁
Supernal平台开发者、需要多端接口一致性的工具开发者
不适合谁
仅需单次脚本执行的用户、第三方集成项目开发者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @ianderrington/universal-command

Skill 说明

命令、参数、文件名以原文为准

@supernal/universal-command

定义一次,随处部署。 CLI、API 和 MCP 接口的统一源。

⚠️ 重要提示:使用此方案,不要重复构建

如果你正在为 Supernal 构建命令,请直接使用本包。不要为 CLI、API 或 MCP 分别创建独立实现。

安装

npm install @supernal/universal-command

快速开始

定义一个命令

import { UniversalCommand } from '@supernal/universal-command';

export const userCreate = new UniversalCommand({
  name: 'user create',
  description: '创建新用户',

  input: {
    parameters: [
      { name: 'name', type: 'string', required: true },
      { name: 'email', type: 'string', required: true },
      { name: 'role', type: 'string', default: 'user', enum: ['user', 'admin'] },
    ],
  },

  output: { type: 'json' },

  handler: async (args, context) => {
    return await createUser(args);
  },
});

部署到所有平台

// CLI
program.addCommand(userCreate.toCLI());
// → mycli user create --name "Alice" --email "alice@example.com"

// Next.js API
export const POST = userCreate.toNextAPI();
// → POST /api/users/create

// MCP 工具
const mcpTool = userCreate.toMCP();
// → 供 AI 代理使用的 user_create 工具

核心概念

单一处理器

只需编写一次逻辑。处理器接收已验证的参数并返回结果:

handler: async (args, context) => {
  // 相同代码同时适用于 CLI、API 和 MCP
  return await doThing(args);
}

输入 Schema

定义一次参数 —— 验证规则、CLI 选项、API 参数和 MCP 模式将自动生成:

input: {
  parameters: [
    { name: 'id', type: 'string', required: true },
    { name: 'status', type: 'string', enum: ['draft', 'active', 'done'] },
    { name: 'limit', type: 'number', min: 1, max: 100, default: 10 },
  ],
}

接口特定配置

在需要时可针对不同接口覆盖行为:

cli: {
  format: (data) => formatForTerminal(data),
  streaming: true,
},

api: {
  method: 'GET',
  cacheControl: { maxAge: 300 },
  auth: { required: true, roles: ['admin'] },
},

mcp: {
  resourceLinks: ['export://results'],
},

注册表模式

用于多个命令的场景:

import { CommandRegistry } from '@supernal/universal-command';

const registry = new CommandRegistry();
registry.register(userCreate);
registry.register(userList);
registry.register(userDelete);

// 生成所有 CLI 命令
for (const cmd of registry.getAll()) {
  program.addCommand(cmd.toCLI());
}

// 生成所有 API 路由
await generateNextRoutes(registry, { outputDir: 'app/api' });

// 生成 MCP 服务
const server = createMCPServer(registry);

运行时服务器

对于无需代码生成的简单场景:

import { createRuntimeServer } from '@supernal/universal-command';

const server = createRuntimeServer();
server.register(userCreate);
server.register(userList);

// 作为 Next.js 使用
export const GET = server.getNextHandlers().GET;
export const POST = server.getNextHandlers().POST;

// 或作为 Express
app.use('/api', server.getExpressRouter());

// 或作为 MCP
await server.startMCP({ name: 'my-server', transport: 'stdio' });

执行上下文

了解当前调用接口类型:

handler: async (args, context) => {
  if (context.interface === 'cli') {
    // CLI 特定逻辑
  } else if (context.interface === 'api') {
    const userId = context.request.headers.get('x-user-id');
  }
  return result;
}

测试

测试一次,处处可用:

import { userCreate } from './user-create';

test('创建用户', async () => {
  const result = await userCreate.execute(
    { name: 'Alice', email: 'alice@example.com' },
    { interface: 'test' }
  );
  expect(result.name).toBe('Alice');
});

架构图

┌─────────────────────────────────────────┐
│      UniversalCommand 定义              │
│  name, description, input, handler      │
└────────────────┬────────────────────────┘
                 │
        ┌────────┼────────┐
        ▼        ▼        ▼
     ┌─────┐  ┌─────┐  ┌─────┐
     │ CLI │  │ API │  │ MCP │
     └─────┘  └─────┘  └─────┘

适用场景

✅ 构建任何新的 Supernal 命令/工具

✅ 为已有逻辑添加 CLI 接口

✅ 向 AI 代理暴露功能(MCP)

✅ 创建具有统一模式的 REST API

❌ 简单的一次性脚本(过度设计)

❌ 与第三方集成且已有自身规范的情况

与 sc 和 si 的集成

sc(supernal-coding)和 si(supernal-interface)均在底层使用 universal-command。当向这些工具添加新命令时,请将其定义为 UniversalCommand。

API 参考

class UniversalCommand<TInput, TOutput> {
  execute(args: TInput, context: ExecutionContext): Promise<TOutput>;
  toCLI(): Command;           // Commander.js Command
  toNextAPI(): NextAPIRoute;  // Next.js 路由处理器
  toExpressAPI(): ExpressRoute;
  toMCP(): MCPToolDefinition;
  validateArgs(args: unknown): ValidationResult<TInput>;
}

class CommandRegistry {
  register(command: UniversalCommand): void;
  getAll(): UniversalCommand[];
}

function createRuntimeServer(): RuntimeServer;
function generateNextRoutes(registry: CommandRegistry, options: CodegenOptions): Promise<void>;
function createMCPServer(registry: CommandRegistry, options: MCPOptions): MCPServer;

来源

  • 包名:@supernal/universal-command
  • npm:https://www.npmjs.com/package/@supernal/universal-command

*不要重新构建此模式。请直接使用!*

I
@ianderrington

已收录 1 个 Skill

相关推荐