关于 eve-app-cli
介绍如何为兼容 Eve 的应用构建领域专用 CLI,面向开发者,帮助 Agent 通过命令访问应用 API,并说明命令设计、输出、认证和分发约定。
主要功能
- 01按业务领域设计命令
- 02基于环境变量进行 API 认证
- 03支持可读文本与 JSON 输出
- 04提供逐级命令帮助
- 05打包文件或镜像分发
适用场景
- 01构建供 Agent 使用的应用 CLI
- 02用命令替代原始 REST 请求
- 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_TOKEN | Bearer 身份验证令牌 | 平台(按作业设置) |
EVE_PROJECT_ID | Eve 平台项目 ID | 平台 |
EVE_ORG_ID | Eve 平台组织 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