mcpField
E
eve-app-cli

eve-app-cli

github.com/incept5开发0162 次安装17 周前更新
代码仓库

关于 eve-app-cli

介绍如何为兼容 Eve 的应用构建领域专用 CLI,面向开发者,帮助 Agent 通过命令访问应用 API,并说明命令设计、输出、认证和分发约定。

主要功能

  1. 01按业务领域设计命令
  2. 02基于环境变量进行 API 认证
  3. 03支持可读文本与 JSON 输出
  4. 04提供逐级命令帮助
  5. 05打包文件或镜像分发

适用场景

  1. 01构建供 Agent 使用的应用 CLI
  2. 02用命令替代原始 REST 请求
  3. 03为 Eve 任务打包 CLI

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/incept5/eve-skillpacks --skill eve-app-cli

安装后在 Claude Code 中输入/eve-app-cli确认已加载。

详细介绍

Eve App CLI

为兼容 Eve 的应用构建特定领域的 CLI,让 Agent 通过命令而非原始 REST 调用进行交互。

原因

Agent 每次通过 REST 交互时,都会在构造 URL、引用 JSON、设置身份验证标头和解析错误上浪费 3-5 次大模型调用。CLI 可将其减少到 1 次调用:

# Before (3-5 calls, error-prone)
curl -X POST "$EVE_APP_API_URL_API/projects/$PID/changesets" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $EVE_JOB_TOKEN" \
  -d @/tmp/changeset.json

# After (1 call, self-documenting)
eden changeset create --project $PID --file /tmp/changeset.json

快速开始

1. 创建 CLI 包
your-app/
  cli/
    src/
      index.ts          # Entry point
      client.ts         # API client (reads env vars)
      commands/
        projects.ts     # Domain commands
    bin/
      your-app          # Built artifact (single-file bundle)
    package.json
    tsconfig.json
2. 实现 API 客户端
// cli/src/client.ts — Copy this, change SERVICE name
const SERVICE = 'API';

export function getApiUrl(): string {
  const url = process.env[`EVE_APP_API_URL_${SERVICE}`];
  if (!url) {
    console.error(`Error: EVE_APP_API_URL_${SERVICE} not set.`);
    console.error('Are you running inside an Eve job with with_apis: [api]?');
    process.exit(1);
  }
  return url;
}

export async function api<T = unknown>(
  method: string,
  path: string,
  body?: unknown,
): Promise<T> {
  const url = getApiUrl();
  const token = process.env.EVE_JOB_TOKEN;
  const res = await fetch(`${url}${path}`, {
    method,
    headers: {
      'Content-Type': 'application/json',
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({} as Record<string, string>));
    console.error(`${method} ${path} → ${res.status}: ${err.message || res.statusText}`);
    process.exit(1);
  }
  return res.json() as Promise<T>;
}
3. 定义命令
// cli/src/index.ts
import { Command } from 'commander';
import { api } from './client.js';
import { readFile } from 'node:fs/promises';

const program = new Command();
program.name('myapp').description('My App CLI').version('1.0.0');

program.command('items')
  .command('list')
  .option('--json', 'JSON output')
  .action(async (opts) => {
    const items = await api('GET', '/items');
    if (opts.json) return console.log(JSON.stringify(items, null, 2));
    for (const i of items) console.log(`${i.id}  ${i.name}`);
  });

program.command('items')
  .command('create')
  .requiredOption('--file <path>', 'JSON file')
  .action(async (opts) => {
    const body = JSON.parse(await readFile(opts.file, 'utf8'));
    const result = await api('POST', '/items', body);
    console.log(`Created: ${result.id}`);
  });

program.parse();
4. 打包,实现零依赖分发

创建构建脚本(cli/build.mjs):

import { build } from 'esbuild';
import { readFile, writeFile, chmod } from 'node:fs/promises';

await build({
  entryPoints: ['cli/src/index.ts'],
  bundle: true,
  platform: 'node',
  target: 'node20',
  format: 'cjs',          // CJS — commander uses require() internally
  outfile: 'cli/bin/myapp',
});

// Prepend shebang (esbuild banner escapes the !)
const code = await readFile('cli/bin/myapp', 'utf8');
await writeFile('cli/bin/myapp', '#!/usr/bin/env node\n' + code);
await chmod('cli/bin/myapp', 0o755);

添加到 package.json:

{
  "scripts": {
    "build": "node build.mjs"
  }
}

重要: 不要在 package.json 中设置 "type": "module"——这会导致运行时出现 require() 错误。请为构建脚本使用 .mjs 扩展名。

5. 在清单中声明
# .eve/manifest.yaml
services:
  api:
    build:
      context: ./apps/api
    ports: [3000]
    x-eve:
      api_spec:
        type: openapi
      cli:
        name: myapp           # Binary name on $PATH
        bin: cli/bin/myapp     # Path relative to repo root

平台会自动向使用 with_apis: [api] 的 Agent 提供此 CLI。

设计规则

命令结构

根据你的领域映射 CLI 命令,而不是映射 HTTP 端点:

# Good — domain vocabulary
eden map show
eden changeset create --file data.json
eden changeset accept CS-45

# Bad — HTTP vocabulary
eden get /projects/123/map
eden post /changesets --body data.json
输出约定
  • 默认:便于人类阅读(表格、摘要)
  • --json:在 stdout 上输出机器可读的 JSON
  • 错误:输出到 stderr,退出代码为 1,并提供可执行的消息
eden projects list              # Table: ID  NAME  CREATED
eden projects list --json       # [{"id":"...","name":"..."}]
eden changeset accept BAD-ID    # stderr: "Changeset BAD-ID not found"
自动检测模式

当只有一个资源时,自动检测,而不是要求用户指定标志:

async function autoDetectProject(): Promise<string> {
  const projects = await api('GET', '/projects');
  if (projects.length === 1) return projects[0].id;
  if (projects.length === 0) {
    console.error('No projects found.');
    process.exit(1);
  }
  console.error('Multiple projects. Use --project <id>:');
  for (const p of projects) console.error(`  ${p.id}  ${p.name}`);
  process.exit(1);
}
渐进式帮助

每个命令和子命令都有 --help:

$ eden --help
Eden story map CLI

Commands:
  projects    Manage projects
  map         View story map
  changeset   Create and review changesets
  persona     Manage personas
  question    Manage questions
  search      Search the map
  export      Export project data

$ eden changeset --help
Commands:
  create   Create a changeset from JSON file
  accept   Accept a pending changeset
  reject   Reject a pending changeset
  list     List changesets for a project

环境变量

CLI 会从环境中读取以下变量(由 Eve 自动注入):

变量用途设置方
EVE_APP_API_URL_{SERVICE}应用 API 的基础 URL平台(--with-apis)
EVE_JOB_TOKENBearer 身份验证令牌平台(按作业设置)
EVE_PROJECT_IDEve 平台项目 ID平台
EVE_ORG_IDEve 平台组织 ID平台

CLI 无需手动配置。

本地测试

设置环境变量并直接运行:

export EVE_APP_API_URL_API=http://localhost:3000
export EVE_JOB_TOKEN=$(eve auth token)

# Test individual commands
./cli/bin/myapp projects list
./cli/bin/myapp items create --file test-data.json

打包详情

使用 esbuild 生成单个文件,不包含运行时依赖:

  • --bundle 内联所有导入(包括 commander)
  • --platform=node 以 Node.js 内置功能为目标
  • --target=node20 与 Eve 运行环境匹配
  • --format=cjs 使用 CommonJS(commander 内部使用 require())
  • 单独添加 Shebang(esbuild 的 --banner 会转义 #!/usr/bin/env 中的 !)
  • 结果:50-200KB 的单个文件,运行时无需 node_modules

将 cli/bin/myapp 提交到代码库,使其在克隆后即可使用。

基于镜像的分发(编译型 CLI)

适用于 Go、Rust 或其他编译型 CLI:

services:
  api:
    x-eve:
      cli:
        name: myapp
        image: ghcr.io/org/myapp-cli:latest

构建一个 Docker 镜像,并将 CLI 二进制文件放在 /cli/bin/myapp:

FROM rust:1.77 AS build
COPY . .
RUN cargo build --release

FROM busybox:stable
COPY --from=build /app/target/release/myapp /cli/bin/myapp

平台通过 init 容器注入它(与工具链使用相同模式,延迟约 2-5 秒)。

另请参阅

  • eve-read-eve-docs 中的 references/app-cli.md,查看完整技术参考
  • references/manifest.md,了解清单架构详情
  • references/eve-sdk.md,了解 Eve Auth SDK(服务端令牌验证)

评论

支持 Markdown