@aplus-frontend/api-generator
v1.0.7
Published
APlus API code generator from OpenAPI documentation
Readme
@aplus-frontend/api-generator
从 OpenAPI 文档生成 TypeScript 接口代码的工具,支持 CLI 和 API两种使用方式。
安装
pnpm add @aplus-frontend/api-generator安装包会通过 postinstall 尝试安装随包发布的 Codex Skill。项目内安装时,目标为
<项目根目录>/.agents/skills/aplus-api-generator;全局安装时,目标为
~/.agents/skills/aplus-api-generator。自动安装失败只会打印警告,不会中断 CLI 包安装。
如果包管理器禁用了生命周期脚本,可在安装 CLI 后显式执行:
npx aplus-gen skill install该命令默认安装到当前项目,也可以指定项目根目录或执行全局安装:
npx aplus-gen skill install --dir /path/to/project
npx aplus-gen skill install --global--dir 与 --global 不能同时使用。
Skill 使用托管标记判断是否可以安全更新。检测到无标记目录或本地修改时会保留现有内容并
返回 SKILL_INSTALL_CONFLICT;确认需要由当前 CLI 接管时,显式覆盖:
npx aplus-gen skill install --forceAgent/CI 可使用 npx aplus-gen skill install --json 获取单个 JSON 对象。安装或更新后需新建
Codex 任务以重新加载 Skill。
CLI 使用
初始化配置
在项目根目录下创建 api-generate.json 和 api-doc.json 配置文件:
npx aplus-gen init生成代码
# 使用内置模板
npx aplus-gen generate
# 指定项目目录
npx aplus-gen generate --dir /path/to/project
# 使用自定义模板
npx aplus-gen generate --template /path/to/templates
# 从 Apifox 指定目录拉取文档并生成代码
npx aplus-gen generate \
--project-id 123456 \
--folder-id 789012 \
--target src/api \
--service-name /api/v1
# 后续使用 index.ts 元数据生成
npx aplus-gen generate --target src/api
# 非交互环境确认覆盖已有元数据
npx aplus-gen generate --target src/api --service-name /api/v2 --yes
# Agent/CI:只输出一个 JSON 对象,并且不读取终端输入
npx aplus-gen generate --json --no-input
# Agent/CI:明确允许覆盖已有元数据
npx aplus-gen generate --target src/api --service-name /api/v2 --json --no-input --yes
# 只读检查项目是否具备生成条件,不联网、不写文件
npx aplus-gen inspect --json
# 检查 Node.js、工作区、配置、模板、文档和输出路径
npx aplus-gen doctor --json
# 完整预演生成并返回文件差异,但不写文件
npx aplus-gen generate --dry-run --json --no-inputInspect、Doctor 和 Dry Run
inspect只读取配置、文档、模板和已有生成元数据,返回ready、mode、inputs、outputs、confirmationRequired和issues。它不请求 Apifox,也不修改文件。检查成功执行时 退出码为0,即使ready为false;调用方应读取 JSON 中的ready。doctor在inspect的基础上返回逐项checks,检查 Node.js 版本、工作区、配置、模板、 API 文档、Apifox 认证和输出路径。健康时退出码为0,否则为1。generate --dry-run运行与真实生成相同的解析、模板和合并流程,返回createdFiles、modifiedFiles和unchangedFiles,但所有写入都保存在内存中。Apifox 模式会发出真实的只读 文档请求,但不会覆盖api-doc.json或生成文件。
--dry-run 不会交互询问。发现 Apifox 元数据变化时返回
APIFOX_OVERRIDE_CONFIRMATION_REQUIRED;只有显式增加 --yes 才会继续完整预演。
Agent 和 CI 调用
--json 会让 init、inspect、doctor 或 generate 只向标准输出写入一个 JSON 对象,不输出彩色日志。
JSON 模式自动启用非交互行为,--no-input 可在非 JSON 模式下显式禁止终端提示。当已有
Apifox 元数据需要覆盖时,非交互调用会失败;只有同时传入 --yes 才会执行覆盖。
--json 既可以放在子命令前,也可以放在子命令选项中,以下调用等价:
npx aplus-gen --json generate
npx aplus-gen generate --json帮助和版本信息也遵守单对象 JSON 契约:
npx aplus-gen --json generate --help
# {"success":true,"help":"Usage: aplus-gen generate [options]\n..."}
npx aplus-gen --json --version
# {"success":true,"version":"<package-version>"}CLI 版本直接来自包元数据,与 package.json 中的版本保持一致。
生成成功:
{
"success": true,
"files": ["/project/src/api/index.ts", "/project/src/api/interface.ts"],
"warnings": []
}生成失败:
{
"success": false,
"code": "CONFIG_NOT_FOUND_OR_INVALID",
"message": "配置文件不存在或格式错误",
"warnings": [],
"details": { "path": "/project/api-generate.json" }
}稳定错误码和进程退出码:
| JSON code | 退出码 | 说明 |
|------|------:|------|
| INVALID_ARGUMENT | 1 | CLI 参数无效 |
| INITIALIZATION_FAILED、GENERATION_FAILED | 1 | 初始化或未分类的生成错误 |
| APIFOX_REQUEST_FAILED、TEMPLATE_GENERATION_FAILED、OPERATION_GENERATION_FAILED | 1 | 执行阶段失败 |
| MISSING_REQUIRED_OPTIONS、CONFIG_NOT_FOUND_OR_INVALID、CONFIG_INCOMPLETE | 2 | 参数或配置错误 |
| OUTPUT_PATH_OUTSIDE_WORKSPACE | 2 | 输出路径越过工作区边界 |
| APIFOX_AUTH_MISSING | 2 | Apifox 认证配置缺失 |
| API_DOCUMENT_NOT_FOUND_OR_INVALID、APIFOX_METADATA_INVALID | 3 | 输入文档或元数据无效 |
| APIFOX_RESPONSE_INVALID、OPENAPI_REFERENCE_NOT_FOUND | 3 | 远程响应或 OpenAPI 引用无效 |
| APIFOX_OVERRIDE_CONFIRMATION_REQUIRED | 4 | 非交互模式下需要确认;可按需传入 --yes |
| SKILL_INSTALL_FAILED | 1 | Codex Skill 来源、权限或复制失败 |
| SKILL_INSTALL_CONFLICT | 4 | 目标 Skill 未托管或已修改;确认后可传入 --force |
错误码是稳定的机器契约,只会新增,不会重命名。message 和包 API 中的 error 仅用于
人类阅读,可能调整或本地化,Agent 不得解析其内容。Agent 应根据 code、details 和进程
退出码决定后续操作。--json 与 --no-input 一起使用可以显式表达机器调用意图,即使
--json 本身已经不会读取终端输入。
API 使用
配置文件和 OpenAPI 文档使用固定文件名,路径均由 workspaceRoot 决定:
<workspaceRoot>/api-generate.json<workspaceRoot>/api-doc.json
生成选项不提供自定义配置或文档路径;Apifox 远程模式也会将文档写入上述
api-doc.json。
import {
doctor,
generate,
generateFromApifox,
inspect,
initConfig,
getBuiltinTemplateDir,
} from '@aplus-frontend/api-generator';
// 初始化配置
await initConfig({
workspaceRoot: '/path/to/project',
});
// 生成代码
const result = await generate({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
});
if (result.success) {
console.log('生成成功:', result.indexPath, result.interfacePath);
} else {
// 按稳定错误码分支;error 只用于展示。
console.error('生成失败:', result.code, result.error, result.details);
}只读检查和环境诊断:
const inspection = await inspect({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
});
const diagnosis = await doctor({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
});
const preview = await generate({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
dryRun: true,
});失败结果保持向后兼容,并增加稳定错误码和结构化详情:
type GenerateFailure = {
success: false;
code: ApiGeneratorErrorCode;
error: string;
details?: Record<string, unknown>;
};常见详情字段包括 fields、path、status、projectId、folderId、changedFields、
current、next、fileName、type、templateDir、ref、method 和 path。
Apifox Token 不会写入错误详情或 JSON 输出。
generate() 只读取本地 api-doc.json。从 Apifox 拉取文档时使用独立的
generateFromApifox() 入口,首次调用需传入输出路径和远程信息:
const result = await generateFromApifox({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
projectId: 123456,
folderId: 789012,
apiPath: 'src/api',
serviceName: '/api/v1',
});成功后,index.ts 会以 JSDoc 元数据记录 projectId、folderId 和 serviceName。后续只需传入 apiPath:
const result = await generateFromApifox({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
apiPath: 'src/api',
});显式参数与已有元数据不同时,包 API 必须通过 confirmOverride 回调确认覆盖。CLI 会交互询问;在 CI 等非交互环境中使用 --yes 确认。
const result = await generateFromApifox({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
apiPath: 'src/api',
serviceName: '/api/v2',
confirmOverride: async ({ current, next, changedFields }) => {
console.warn('Apifox 元数据将被覆盖', { current, next, changedFields });
return true;
},
});为兼容已有调用,generate() 暂时仍接受远程参数并委托给 generateFromApifox(),这些参数
已标记为 deprecated。新代码应始终使用独立远程入口。
配置说明
api-generate.json
{
"apiPath": "src/api",
"serviceName": "/api/v1",
"defPath": "@/utils/http",
"hooks": true,
"apifoxToken": "your-apifox-access-token"
}| 字段 | 类型 | 说明 |
|------|------|------|
| apiPath | string | API 文件存放路径 |
| serviceName | string | 服务路径前缀 |
| defPath | string | HTTP 工具导入路径 |
| hooks | boolean | 是否生成 vue-request hooks |
| apifoxToken | string | Apifox Access Token,仅远程模式使用,不包含 Bearer 前缀 |
apiPath 和 CLI 的 --target 必须指向 workspaceRoot 内部。越界的绝对路径、包含 .. 的
越界路径,以及通过符号链接指向工作区外部的目录都会被拒绝。默认文件系统使用同目录临时
文件和原子替换写入配置、文档及生成结果;多个生成文件中途写入失败时会恢复已替换的文件,
生成失败也不会预先留下空的目标文件。
建议不要将包含真实 apifoxToken 的配置文件提交到公共代码仓库。
generateFromApifox() 仅从配置文件读取 defPath、hooks 和 apifoxToken。apiPath
始终使用调用参数,其他远程信息来自调用参数或 index.ts 元数据。
api-doc.json
OpenAPI/Swagger JSON 文档内容。
本地模式直接读取该文件。Apifox 远程模式拉取成功后会用最新的 OpenAPI 3.0 JSON 覆盖该文件,再执行代码生成。
两个入口都会在生成的 index.ts 顶部写入元数据。本地首次生成时 projectId、folderId
为空字符串;后续生成会保留已有 ID,并使用当前配置更新 serviceName。元数据只接受下方的
JSDoc 格式,旧的单行 // @apifox {...} 会被视为格式错误。
生成的文件结构
src/api/
├── index.ts # 接口函数
└── interface.ts # TypeScript 类型定义Apifox 远程模式会在 index.ts 文件头记录来源,重复生成时更新该注释:
/**
* @apifox {"projectId":"123456","folderId":"789012","serviceName":"/api/v1"}
*/
import ...自定义模板
模板使用 Nunjucks 语法,包含以下模板文件:
interface.njk- 类型定义模板service.njk- 接口函数模板
自定义模板时,保持相同的变量结构即可。
高级用法
自定义文件系统
import type { FileSystem } from '@aplus-frontend/api-generator';
class CustomFileSystem implements FileSystem {
async readFile(filePath: string): Promise<string> { /* ... */ }
async writeFile(filePath: string, content: string): Promise<void> { /* ... */ }
async stat(filePath: string) { /* ... */ }
async mkdir(dirPath: string, options?: { recursive: boolean }): Promise<void> { /* ... */ }
async exists(filePath: string): Promise<boolean> { /* ... */ }
}
const result = await generate({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
fs: new CustomFileSystem(),
});自定义消息处理
import type { MessageHandler } from '@aplus-frontend/api-generator';
const result = await generate({
workspaceRoot: '/path/to/project',
templateDir: getBuiltinTemplateDir(),
messageHandler: {
info: (msg) => console.log('[INFO]', msg),
error: (msg) => console.error('[ERROR]', msg),
warn: (msg) => console.warn('[WARN]', msg),
},
});License
MIT
