vercel-open-agents
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVercel Open Agents
Vercel Open Agents
Skill by ara.so — AI Agent Skills collection.
Open Agents is an open-source template for building cloud-based coding agents that run as durable workflows on Vercel. It provides web UI, agent runtime, sandbox orchestration, and GitHub integration to execute multi-step coding tasks without local execution.
由ara.so提供的Skill——AI Agent技能合集。
Open Agents是一个开源模板,用于构建基于云的编码Agent,这些Agent在Vercel上作为持久化工作流运行。它提供Web UI、Agent运行时、沙箱编排和GitHub集成,无需本地执行即可完成多步编码任务。
Architecture
架构
Open Agents uses a three-layer architecture:
text
Web App (Next.js) → Agent Workflow (Vercel Workflow SDK) → Sandbox VM (isolated execution)Key principle: The agent runs outside the sandbox and interacts with it through tools. This separation enables:
- Durable execution beyond single request lifecycles
- Independent sandbox hibernation and resume
- Flexible model/provider swapping
- Clean separation of concerns
Open Agents采用三层架构:
text
Web App (Next.js) → Agent Workflow (Vercel Workflow SDK) → Sandbox VM (isolated execution)核心原则:Agent在沙箱外部运行,并通过工具与其交互。这种分离实现了:
- 超越单次请求生命周期的持久化执行
- 独立的沙箱休眠与恢复
- 灵活的模型/提供商切换
- 清晰的关注点分离
Installation
安装
Deploy to Vercel
部署到Vercel
-
Fork and import the repository to Vercel
-
Generate secrets:bash
openssl rand -base64 32 # for BETTER_AUTH_SECRET -
Set required environment variables:env
POSTGRES_URL=your_neon_postgres_url BETTER_AUTH_SECRET=your_generated_secret -
Deploy to get production URL
-
Create Vercel OAuth app at https://vercel.com/account/settings/oauth-apps
- Callback:
https://YOUR_DOMAIN/api/auth/callback/vercel
- Callback:
-
Add OAuth credentials:env
NEXT_PUBLIC_VERCEL_APP_CLIENT_ID=your_client_id VERCEL_APP_CLIENT_SECRET=your_client_secret -
Create GitHub App (optional, for repo access):
- Homepage:
https://YOUR_DOMAIN - Callback:
https://YOUR_DOMAIN/api/auth/callback/github - Setup URL:
https://YOUR_DOMAIN/api/github/app/callback - Make app public for org installs
- Homepage:
-
Add GitHub credentials:env
NEXT_PUBLIC_GITHUB_CLIENT_ID=your_github_app_client_id GITHUB_CLIENT_SECRET=your_github_app_client_secret GITHUB_APP_ID=your_app_id GITHUB_APP_PRIVATE_KEY=your_pem_private_key NEXT_PUBLIC_GITHUB_APP_SLUG=your_app_slug GITHUB_WEBHOOK_SECRET=your_webhook_secret
-
复刻并导入仓库到Vercel
-
生成密钥:bash
openssl rand -base64 32 # for BETTER_AUTH_SECRET -
设置必要的环境变量:env
POSTGRES_URL=your_neon_postgres_url BETTER_AUTH_SECRET=your_generated_secret -
部署以获取生产环境URL
-
在https://vercel.com/account/settings/oauth-apps 创建Vercel OAuth应用
- 回调地址:
https://YOUR_DOMAIN/api/auth/callback/vercel
- 回调地址:
-
添加OAuth凭证:env
NEXT_PUBLIC_VERCEL_APP_CLIENT_ID=your_client_id VERCEL_APP_CLIENT_SECRET=your_client_secret -
创建GitHub应用(可选,用于仓库访问):
- 主页:
https://YOUR_DOMAIN - 回调地址:
https://YOUR_DOMAIN/api/auth/callback/github - 设置URL:
https://YOUR_DOMAIN/api/github/app/callback - 将应用设为公开,以便组织安装
- 主页:
-
添加GitHub凭证:env
NEXT_PUBLIC_GITHUB_CLIENT_ID=your_github_app_client_id GITHUB_CLIENT_SECRET=your_github_app_client_secret GITHUB_APP_ID=your_app_id GITHUB_APP_PRIVATE_KEY=your_pem_private_key NEXT_PUBLIC_GITHUB_APP_SLUG=your_app_slug GITHUB_WEBHOOK_SECRET=your_webhook_secret
Local Development
本地开发
bash
undefinedbash
undefinedInstall dependencies
安装依赖
bun install
bun install
Copy environment template
复制环境变量模板
cp apps/web/.env.example apps/web/.env
cp apps/web/.env.example apps/web/.env
Fill in required variables in apps/web/.env
在apps/web/.env中填写必要变量
Start dev server
启动开发服务器
bun run web
For existing Vercel projects:
```bash
vc env pullbun run web
对于已有的Vercel项目:
```bash
vc env pullCore Concepts
核心概念
Workflows
工作流
Agent runs execute as Vercel Workflow SDK workflows, enabling:
- Durable execution: survives across steps and deployments
- Streaming: real-time updates to the UI
- Cancellation: user-initiated stop
- Resume: reconnect to active runs
Example workflow structure:
typescript
// apps/web/app/api/workflows/agent/route.ts
import { createWorkflow } from '@vercel/workflow-sdk';
export const POST = createWorkflow(async (context, data) => {
const { message, sessionId } = data;
// Agent execution happens in workflow steps
const result = await context.run('agent-step', async () => {
return await runAgent(message, sessionId);
});
return result;
});Agent运行以Vercel Workflow SDK工作流的形式执行,支持:
- 持久化执行:在多步骤和部署中保持运行状态
- 流式传输:向UI实时推送更新
- 取消:用户主动终止运行
- 恢复:重新连接到活跃的运行任务
示例工作流结构:
typescript
// apps/web/app/api/workflows/agent/route.ts
import { createWorkflow } from '@vercel/workflow-sdk';
export const POST = createWorkflow(async (context, data) => {
const { message, sessionId } = data;
// Agent执行在工作流步骤中进行
const result = await context.run('agent-step', async () => {
return await runAgent(message, sessionId);
});
return result;
});Sandboxes
沙箱
Sandboxes provide isolated execution environments with:
- Filesystem access: read, write, search files
- Shell execution: run commands
- Git operations: clone, branch, commit, push
- Port exposure: ,
3000,5173,43218000 - Snapshot-based resume: hibernate and restore state
typescript
// packages/sandbox/src/client.ts
import { createSandbox } from '@vercel-labs/open-agents/sandbox';
const sandbox = await createSandbox({
baseSnapshotId: process.env.VERCEL_SANDBOX_BASE_SNAPSHOT_ID,
});
// Execute commands
const result = await sandbox.exec('npm install');
// Read files
const content = await sandbox.readFile('package.json');
// Write files
await sandbox.writeFile('src/config.ts', configContent);沙箱提供隔离的执行环境,具备以下能力:
- 文件系统访问:读取、写入、搜索文件
- Shell执行:运行命令
- Git操作:克隆、分支、提交、推送
- 端口暴露:,
3000,5173,43218000 - 基于快照的恢复:休眠与恢复状态
typescript
// packages/sandbox/src/client.ts
import { createSandbox } from '@vercel-labs/open-agents/sandbox';
const sandbox = await createSandbox({
baseSnapshotId: process.env.VERCEL_SANDBOX_BASE_SNAPSHOT_ID,
});
// 执行命令
const result = await sandbox.exec('npm install');
// 读取文件
const content = await sandbox.readFile('package.json');
// 写入文件
await sandbox.writeFile('src/config.ts', configContent);Tools
工具
Agents interact with sandboxes through tools:
typescript
// packages/agent/src/tools/file-edit.ts
export const fileEditTool = {
name: 'file_edit',
description: 'Edit a file with search-and-replace',
parameters: z.object({
path: z.string(),
search: z.string(),
replace: z.string(),
}),
execute: async ({ path, search, replace }, context) => {
const content = await context.sandbox.readFile(path);
const updated = content.replace(search, replace);
await context.sandbox.writeFile(path, updated);
return { success: true };
},
};Available tools:
- file_read: Read file contents
- file_write: Write entire file
- file_edit: Search-and-replace edits
- file_search: Search file contents
- shell: Execute shell commands
- task: Create subtasks for the agent
- web: Fetch web content
- skill: Access domain-specific skills
Agent通过工具与沙箱交互:
typescript
// packages/agent/src/tools/file-edit.ts
export const fileEditTool = {
name: 'file_edit',
description: 'Edit a file with search-and-replace',
parameters: z.object({
path: z.string(),
search: z.string(),
replace: z.string(),
}),
execute: async ({ path, search, replace }, context) => {
const content = await context.sandbox.readFile(path);
const updated = content.replace(search, replace);
await context.sandbox.writeFile(path, updated);
return { success: true };
},
};可用工具:
- file_read: 读取文件内容
- file_write: 写入整个文件
- file_edit: 搜索替换编辑
- file_search: 搜索文件内容
- shell: 执行Shell命令
- task: 为Agent创建子任务
- web: 获取网页内容
- skill: 访问领域特定技能
Creating Custom Tools
创建自定义工具
- Define the tool schema:
typescript
// packages/agent/src/tools/my-tool.ts
import { z } from 'zod';
export const myCustomTool = {
name: 'my_tool',
description: 'Does something specific',
parameters: z.object({
input: z.string().describe('The input parameter'),
}),
execute: async ({ input }, context) => {
// Access sandbox
const result = await context.sandbox.exec(`echo ${input}`);
// Return structured data
return {
output: result.stdout,
exitCode: result.exitCode,
};
},
};- Register the tool:
typescript
// packages/agent/src/tools/index.ts
import { myCustomTool } from './my-tool';
export const tools = [
fileReadTool,
fileWriteTool,
shellTool,
myCustomTool, // Add your tool
// ...
];- 定义工具Schema:
typescript
// packages/agent/src/tools/my-tool.ts
import { z } from 'zod';
export const myCustomTool = {
name: 'my_tool',
description: 'Does something specific',
parameters: z.object({
input: z.string().describe('The input parameter'),
}),
execute: async ({ input }, context) => {
// 访问沙箱
const result = await context.sandbox.exec(`echo ${input}`);
// 返回结构化数据
return {
output: result.stdout,
exitCode: result.exitCode,
};
},
};- 注册工具:
typescript
// packages/agent/src/tools/index.ts
import { myCustomTool } from './my-tool';
export const tools = [
fileReadTool,
fileWriteTool,
shellTool,
myCustomTool, // 添加你的工具
// ...
];Skills System
技能系统
Skills are domain-specific knowledge modules loaded into agent context:
typescript
// packages/agent/src/skills/registry.ts
export interface Skill {
id: string;
name: string;
description: string;
content: string; // Markdown content
triggers: string[];
}
// Skills are cached in Redis/KV or in-memory
const skillsCache = new SkillsCache({
redis: process.env.REDIS_URL,
});
// Retrieve skill by trigger
const skill = await skillsCache.findByTrigger('nextjs app router');Create a custom skill:
typescript
// packages/agent/src/skills/custom-skill.ts
export const customSkill: Skill = {
id: 'custom-framework',
name: 'Custom Framework',
description: 'Expertise in CustomFramework usage',
content: `技能是加载到Agent上下文的领域特定知识模块:
typescript
// packages/agent/src/skills/registry.ts
export interface Skill {
id: string;
name: string;
description: string;
content: string; // Markdown内容
triggers: string[];
}
// 技能缓存于Redis/KV或内存中
const skillsCache = new SkillsCache({
redis: process.env.REDIS_URL,
});
// 通过触发词检索技能
const skill = await skillsCache.findByTrigger('nextjs app router');创建自定义技能:
typescript
// packages/agent/src/skills/custom-skill.ts
export const customSkill: Skill = {
id: 'custom-framework',
name: 'Custom Framework',
description: 'Expertise in CustomFramework usage',
content: `Custom Framework Skill
Custom Framework Skill
Installation
Installation
```bash
npm install custom-framework
```
```bash
npm install custom-framework
```
Usage
Usage
```typescript
import { Framework } from 'custom-framework';
const app = new Framework();
app.start();
```
`,
triggers: [
'use custom framework',
'setup custom framework',
'configure custom framework',
],
};
undefined```typescript
import { Framework } from 'custom-framework';
const app = new Framework();
app.start();
```
`,
triggers: [
'use custom framework',
'setup custom framework',
'configure custom framework',
],
};
undefinedGitHub Integration
GitHub集成
Auto-commit and PR Creation
自动提交与PR创建
Configure agent to automatically commit and create PRs:
typescript
// packages/agent/src/workflows/agent-workflow.ts
export async function runAgentWorkflow(
message: string,
options: {
autoCommit?: boolean;
autoPR?: boolean;
repository?: string;
branch?: string;
}
) {
const result = await agent.run(message);
if (options.autoCommit && result.success) {
await sandbox.exec('git add .');
await sandbox.exec(`git commit -m "Agent: ${message}"`);
if (options.autoPR) {
await createPullRequest({
repo: options.repository,
branch: options.branch,
title: `AI Agent: ${message}`,
body: result.summary,
});
}
}
}配置Agent自动提交并创建PR:
typescript
// packages/agent/src/workflows/agent-workflow.ts
export async function runAgentWorkflow(
message: string,
options: {
autoCommit?: boolean;
autoPR?: boolean;
repository?: string;
branch?: string;
}
) {
const result = await agent.run(message);
if (options.autoCommit && result.success) {
await sandbox.exec('git add .');
await sandbox.exec(`git commit -m "Agent: ${message}"`);
if (options.autoPR) {
await createPullRequest({
repo: options.repository,
branch: options.branch,
title: `AI Agent: ${message}`,
body: result.summary,
});
}
}
}GitHub App Installation
GitHub应用安装
typescript
// apps/web/lib/github/app.ts
import { App } from '@octokit/app';
const app = new App({
appId: process.env.GITHUB_APP_ID,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
});
// Get installation token
const installation = await app.octokit.request(
'GET /users/{username}/installation',
{ username: 'user' }
);
const token = await app.octokit.auth({
type: 'installation',
installationId: installation.data.id,
});typescript
// apps/web/lib/github/app.ts
import { App } from '@octokit/app';
const app = new App({
appId: process.env.GITHUB_APP_ID,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
});
// 获取安装令牌
const installation = await app.octokit.request(
'GET /users/{username}/installation',
{ username: 'user' }
);
const token = await app.octokit.auth({
type: 'installation',
installationId: installation.data.id,
});Configuration
配置
Resource Profiles
资源配置文件
For Hobby tier deployments:
env
OPEN_AGENTS_RESOURCE_PROFILE=hobbyThis adjusts chat and sandbox resource limits to Hobby-compatible values.
对于Hobby层级部署:
env
OPEN_AGENTS_RESOURCE_PROFILE=hobby这会调整聊天和沙箱的资源限制,使其适配Hobby层级的可用值。
Sandbox Base Snapshot
沙箱基础快照
Use a preconfigured sandbox image:
env
VERCEL_SANDBOX_BASE_SNAPSHOT_ID=your_snapshot_idCreate base snapshot:
bash
bun run sandbox:snapshot-base使用预配置的沙箱镜像:
env
VERCEL_SANDBOX_BASE_SNAPSHOT_ID=your_snapshot_id创建基础快照:
bash
bun run sandbox:snapshot-baseVoice Input
语音输入
Enable voice transcription with ElevenLabs:
env
ELEVENLABS_API_KEY=your_api_key通过ElevenLabs启用语音转录:
env
ELEVENLABS_API_KEY=your_api_keyOptional Caching
可选缓存
Enable Redis/KV for skills metadata:
env
REDIS_URL=your_redis_url
KV_URL=your_kv_url启用Redis/KV存储技能元数据:
env
REDIS_URL=your_redis_url
KV_URL=your_kv_urlCommon Patterns
常见模式
Multi-step Agent Task
多步骤Agent任务
typescript
// packages/agent/src/agent.ts
export async function runMultiStepTask(goal: string) {
const steps = await planSteps(goal);
for (const step of steps) {
const result = await executeStep(step, {
tools: availableTools,
sandbox: currentSandbox,
});
if (!result.success) {
// Handle failure, retry, or replan
return { success: false, error: result.error };
}
}
return { success: true, steps };
}typescript
// packages/agent/src/agent.ts
export async function runMultiStepTask(goal: string) {
const steps = await planSteps(goal);
for (const step of steps) {
const result = await executeStep(step, {
tools: availableTools,
sandbox: currentSandbox,
});
if (!result.success) {
// 处理失败、重试或重新规划
return { success: false, error: result.error };
}
}
return { success: true, steps };
}Streaming Agent Response
流式Agent响应
typescript
// apps/web/app/api/chat/route.ts
import { streamText } from 'ai';
export async function POST(req: Request) {
const { messages, sessionId } = await req.json();
const result = await streamText({
model: openai('gpt-4'),
messages,
tools: {
file_read: fileReadTool,
file_write: fileWriteTool,
shell: shellTool,
},
onFinish: async ({ text, toolCalls }) => {
// Save to session
await saveMessage(sessionId, { text, toolCalls });
},
});
return result.toDataStreamResponse();
}typescript
// apps/web/app/api/chat/route.ts
import { streamText } from 'ai';
export async function POST(req: Request) {
const { messages, sessionId } = await req.json();
const result = await streamText({
model: openai('gpt-4'),
messages,
tools: {
file_read: fileReadTool,
file_write: fileWriteTool,
shell: shellTool,
},
onFinish: async ({ text, toolCalls }) => {
// 保存到会话
await saveMessage(sessionId, { text, toolCalls });
},
});
return result.toDataStreamResponse();
}Session Management
会话管理
typescript
// apps/web/lib/sessions.ts
import { db } from './db';
export async function createSession(userId: string, metadata: object) {
const session = await db.session.create({
data: {
userId,
metadata,
createdAt: new Date(),
},
});
return session;
}
export async function shareSession(sessionId: string) {
const shareToken = await generateShareToken(sessionId);
return {
url: `${process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL}/share/${shareToken}`,
token: shareToken,
};
}typescript
// apps/web/lib/sessions.ts
import { db } from './db';
export async function createSession(userId: string, metadata: object) {
const session = await db.session.create({
data: {
userId,
metadata,
createdAt: new Date(),
},
});
return session;
}
export async function shareSession(sessionId: string) {
const shareToken = await generateShareToken(sessionId);
return {
url: `${process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL}/share/${shareToken}`,
token: shareToken,
};
}Sandbox Lifecycle
沙箱生命周期
typescript
// packages/sandbox/src/lifecycle.ts
export class SandboxLifecycle {
async create() {
this.sandbox = await createSandbox({
baseSnapshotId: this.baseSnapshotId,
});
return this.sandbox;
}
async hibernate() {
const snapshot = await this.sandbox.createSnapshot();
await this.sandbox.stop();
return snapshot.id;
}
async resume(snapshotId: string) {
this.sandbox = await createSandbox({
baseSnapshotId: snapshotId,
});
return this.sandbox;
}
async destroy() {
await this.sandbox.stop();
this.sandbox = null;
}
}typescript
// packages/sandbox/src/lifecycle.ts
export class SandboxLifecycle {
async create() {
this.sandbox = await createSandbox({
baseSnapshotId: this.baseSnapshotId,
});
return this.sandbox;
}
async hibernate() {
const snapshot = await this.sandbox.createSnapshot();
await this.sandbox.stop();
return snapshot.id;
}
async resume(snapshotId: string) {
this.sandbox = await createSandbox({
baseSnapshotId: snapshotId,
});
return this.sandbox;
}
async destroy() {
await this.sandbox.stop();
this.sandbox = null;
}
}Troubleshooting
故障排查
Workflow Execution Fails
工作流执行失败
Problem: Workflow steps timeout or fail
typescript
// Check workflow logs
const logs = await getWorkflowLogs(workflowId);
console.log(logs);
// Increase timeout
export const POST = createWorkflow(
async (context, data) => {
// ...
},
{ timeout: 300000 } // 5 minutes
);问题: 工作流步骤超时或失败
typescript
// 查看工作流日志
const logs = await getWorkflowLogs(workflowId);
console.log(logs);
// 增加超时时间
export const POST = createWorkflow(
async (context, data) => {
// ...
},
{ timeout: 300000 } // 5分钟
);Sandbox Connection Issues
沙箱连接问题
Problem: Cannot connect to sandbox
typescript
// Verify sandbox is running
const status = await sandbox.status();
console.log('Sandbox status:', status);
// Recreate sandbox
await sandbox.stop();
const newSandbox = await createSandbox();问题: 无法连接到沙箱
typescript
// 验证沙箱是否运行
const status = await sandbox.status();
console.log('Sandbox status:', status);
// 重新创建沙箱
await sandbox.stop();
const newSandbox = await createSandbox();GitHub App Permissions
GitHub应用权限
Problem: Cannot access repositories
- Verify app installation:
https://github.com/settings/installations - Check app permissions include:
- Repository contents: Read & write
- Pull requests: Read & write
- Metadata: Read-only
问题: 无法访问仓库
- 验证应用安装状态:
https://github.com/settings/installations - 检查应用权限是否包含:
- 仓库内容: 读写
- Pull请求: 读写
- 元数据: 只读
Database Migrations
数据库迁移
Problem: Schema out of sync
bash
undefined问题: 架构不同步
bash
undefinedCheck migration status
检查迁移状态
bun run ci
bun run ci
Apply migrations manually if needed
必要时手动应用迁移
cd apps/web
npx drizzle-kit push:pg
undefinedcd apps/web
npx drizzle-kit push:pg
undefinedAuthentication Issues
认证问题
Problem: OAuth callback fails
- Verify callback URLs match exactly (including protocol)
- Check is set and consistent across deployments
BETTER_AUTH_SECRET - Confirm OAuth app credentials are correct
问题: OAuth回调失败
- 验证回调URL完全匹配(包括协议)
- 检查已设置且在所有部署中保持一致
BETTER_AUTH_SECRET - 确认OAuth应用凭证正确
Port Conflicts in Sandbox
沙箱端口冲突
Problem: Preview port already in use
typescript
// Use alternative ports
const ports = [3000, 5173, 4321, 8000];
for (const port of ports) {
try {
await sandbox.exec(`lsof -ti:${port} | xargs kill -9`);
} catch {
// Port was free
}
}问题: 预览端口已被占用
typescript
// 使用备用端口
const ports = [3000, 5173, 4321, 8000];
for (const port of ports) {
try {
await sandbox.exec(`lsof -ti:${port} | xargs kill -9`);
} catch {
// 端口未被占用
}
}Development Commands
开发命令
bash
undefinedbash
undefinedStart development server
启动开发服务器
bun run web
bun run web
Lint and format check
检查代码规范与格式
bun run check
bun run check
Auto-fix linting and formatting
自动修复代码规范与格式问题
bun run fix
bun run fix
Type check all packages
类型检查所有包
bun run typecheck
bun run typecheck
Full CI suite
完整CI套件
bun run ci
bun run ci
Refresh sandbox base snapshot
刷新沙箱基础快照
bun run sandbox:snapshot-base
undefinedbun run sandbox:snapshot-base
undefinedAPI Reference
API参考
Core Types
核心类型
typescript
// Agent context passed to tools
interface AgentContext {
sandbox: Sandbox;
sessionId: string;
userId: string;
metadata: Record<string, unknown>;
}
// Tool definition
interface Tool<T = unknown> {
name: string;
description: string;
parameters: z.ZodSchema<T>;
execute: (params: T, context: AgentContext) => Promise<unknown>;
}
// Sandbox interface
interface Sandbox {
exec(command: string): Promise<ExecResult>;
readFile(path: string): Promise<string>;
writeFile(path: string, content: string): Promise<void>;
search(pattern: string): Promise<SearchResult[]>;
createSnapshot(): Promise<Snapshot>;
status(): Promise<SandboxStatus>;
stop(): Promise<void>;
}typescript
// 传递给工具的Agent上下文
interface AgentContext {
sandbox: Sandbox;
sessionId: string;
userId: string;
metadata: Record<string, unknown>;
}
// 工具定义
interface Tool<T = unknown> {
name: string;
description: string;
parameters: z.ZodSchema<T>;
execute: (params: T, context: AgentContext) => Promise<unknown>;
}
// 沙箱接口
interface Sandbox {
exec(command: string): Promise<ExecResult>;
readFile(path: string): Promise<string>;
writeFile(path: string, content: string): Promise<void>;
search(pattern: string): Promise<SearchResult[]>;
createSnapshot(): Promise<Snapshot>;
status(): Promise<SandboxStatus>;
stop(): Promise<void>;
}Resources
资源
- Homepage: https://open-agents.dev
- Repository: https://github.com/vercel-labs/open-agents
- Vercel Workflow SDK: https://vercel.com/docs/workflow
- Better Auth: https://www.better-auth.com/
- GitHub Apps: https://docs.github.com/en/apps
- 主页: https://open-agents.dev
- 仓库: https://github.com/vercel-labs/open-agents
- Vercel Workflow SDK: https://vercel.com/docs/workflow
- Better Auth: https://www.better-auth.com/
- GitHub Apps: https://docs.github.com/en/apps