openserv-agent-sdk

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenServ Agent SDK

OpenServ Agent SDK

Build and deploy custom AI agents for the OpenServ platform using TypeScript.
Reference files:
  • reference.md
    - Quick reference for common patterns
  • troubleshooting.md
    - Common issues and solutions
  • examples/
    - Complete code examples
使用TypeScript为OpenServ平台构建并部署自定义AI Agent。
参考文件:
  • reference.md
    - 常见模式快速参考
  • troubleshooting.md
    - 常见问题与解决方案
  • examples/
    - 完整代码示例

What's New in v2.3

v2.3版本新特性

  • DISABLE_TUNNEL
    Env Var
    - Set
    DISABLE_TUNNEL=true
    for production deployments (e.g. Cloud Run).
    run()
    starts only the HTTP server, no WebSocket tunnel.
  • FORCE_TUNNEL
    Env Var
    - Set
    FORCE_TUNNEL=true
    to force tunnel mode even when an
    endpointUrl
    is configured.
  • Public Health Check - The
    /health
    route now responds before auth middleware, so the platform health cron can reach it without credentials.
  • Binary Tunnel Responses - Tunnel responses are sent as binary frames, preserving gzip, images, and other binary payloads transparently.
  • DISABLE_TUNNEL
    环境变量
    - 生产部署(如Cloud Run)时设置
    DISABLE_TUNNEL=true
    run()
    仅启动HTTP服务器,不开启WebSocket隧道。
  • FORCE_TUNNEL
    环境变量
    - 设置
    FORCE_TUNNEL=true
    可强制启用隧道模式,即使已配置
    endpointUrl
  • 公开健康检查端点 -
    /health
    路由现在会在身份验证中间件之前响应,因此平台健康检查定时任务无需凭证即可访问它。
  • 二进制隧道响应 - 隧道响应以二进制帧形式发送,透明保留gzip、图像和其他二进制负载。

Earlier in v2.x

v2.x早期版本特性

  • Built-in Tunnel - The
    run()
    function auto-connects to
    agents-proxy.openserv.ai
  • No Endpoint URL Needed - Skip
    endpointUrl
    in
    provision()
    during development
  • Automatic Port Fallback - If your port is busy, the agent finds an available one
  • Direct Credential Binding -
    provision()
    can bind credentials directly to agent instance via
    agent.instance
  • setCredentials()
    Method
    - Manually bind API key and auth token to agent
  • 内置隧道 -
    run()
    函数会自动连接到
    agents-proxy.openserv.ai
  • 无需端点URL - 开发期间调用
    provision()
    时可省略
    endpointUrl
  • 自动端口回退 - 如果指定端口被占用,Agent会自动寻找可用端口
  • 直接凭证绑定 -
    provision()
    可通过
    agent.instance
    将凭证直接绑定到Agent实例
  • setCredentials()
    方法
    - 手动将API密钥和身份验证令牌绑定到Agent

Quick Start

快速开始

Installation

安装

bash
npm install @openserv-labs/sdk @openserv-labs/client zod openai
Note: The SDK requires
openai@^5.x
as a peer dependency.
bash
npm install @openserv-labs/sdk @openserv-labs/client zod openai
注意: SDK需要
openai@^5.x
作为对等依赖。

Minimal Agent

最简Agent示例

See
examples/basic-agent.ts
for a complete runnable example.
The pattern is simple:
  1. Create an
    Agent
    with a system prompt
  2. Add capabilities with
    agent.addCapability()
  3. Call
    provision()
    to register on the platform (pass
    agent.instance
    to bind credentials)
  4. Call
    run(agent)
    to start

完整可运行示例请查看
examples/basic-agent.ts
实现流程非常简单:
  1. 使用系统提示创建
    Agent
    实例
  2. 调用
    agent.addCapability()
    添加功能
  3. 调用
    provision()
    在平台上注册(传入
    agent.instance
    以绑定凭证)
  4. 调用
    run(agent)
    启动Agent

Complete Agent Template

完整Agent模板

File Structure

文件结构

my-agent/
├── src/agent.ts
├── .env
├── .gitignore
├── package.json
└── tsconfig.json
my-agent/
├── src/agent.ts
├── .env
├── .gitignore
├── package.json
└── tsconfig.json

Dependencies

依赖安装

bash
npm init -y && npm pkg set type=module
npm i @openserv-labs/sdk @openserv-labs/client dotenv openai zod
npm i -D @types/node tsx typescript
Note: The project must use
"type": "module"
in
package.json
. Add a
"dev": "tsx src/agent.ts"
script for local development.
bash
npm init -y && npm pkg set type=module
npm i @openserv-labs/sdk @openserv-labs/client dotenv openai zod
npm i -D @types/node tsx typescript
注意: 项目必须在
package.json
中设置
"type": "module"
。添加
"dev": "tsx src/agent.ts"
脚本用于本地开发。

.env

.env配置

env
OPENAI_API_KEY=your-openai-key
env
OPENAI_API_KEY=your-openai-key

Auto-populated by provision():

由provision()自动填充:

WALLET_PRIVATE_KEY= OPENSERV_API_KEY= OPENSERV_AUTH_TOKEN= PORT=7378
WALLET_PRIVATE_KEY= OPENSERV_API_KEY= OPENSERV_AUTH_TOKEN= PORT=7378

Production: skip tunnel and run HTTP server only

生产环境:跳过隧道,仅运行HTTP服务器

DISABLE_TUNNEL=true

DISABLE_TUNNEL=true

Force tunnel even when endpointUrl is set

即使设置了endpointUrl也强制启用隧道

FORCE_TUNNEL=true

FORCE_TUNNEL=true


---

---

Capabilities

功能模块(Capabilities)

Capabilities are functions your agent can execute. Each requires:
  • name
    - Unique identifier
  • description
    - What it does (helps AI decide when to use it)
  • schema
    - Zod schema defining parameters
  • run
    - Function returning a string
See
examples/capability-example.ts
for basic capabilities.
功能模块是Agent可以执行的函数,每个模块需要包含:
  • name
    - 唯一标识符
  • description
    - 功能说明(帮助AI判断何时使用该模块)
  • schema
    - 定义参数的Zod schema
  • run
    - 返回字符串的函数
基础功能模块示例请查看
examples/capability-example.ts

Using Agent Methods

使用Agent方法

Access
this
in capabilities to use agent methods like
addLogToTask()
,
uploadFile()
, etc.
See
examples/capability-with-agent-methods.ts
for logging and file upload patterns.

在功能模块中通过
this
访问Agent方法,如
addLogToTask()
uploadFile()
等。
日志记录和文件上传示例请查看
examples/capability-with-agent-methods.ts

Agent Methods

Agent方法

Task Management

任务管理

typescript
await agent.createTask({ workspaceId, assignee, description, body, input, dependencies })
await agent.updateTaskStatus({ workspaceId, taskId, status: 'in-progress' })
await agent.addLogToTask({ workspaceId, taskId, severity: 'info', type: 'text', body: '...' })
await agent.markTaskAsErrored({ workspaceId, taskId, error: 'Something went wrong' })
const task = await agent.getTaskDetail({ workspaceId, taskId })
const tasks = await agent.getTasks({ workspaceId })
typescript
await agent.createTask({ workspaceId, assignee, description, body, input, dependencies })
await agent.updateTaskStatus({ workspaceId, taskId, status: 'in-progress' })
await agent.addLogToTask({ workspaceId, taskId, severity: 'info', type: 'text', body: '...' })
await agent.markTaskAsErrored({ workspaceId, taskId, error: 'Something went wrong' })
const task = await agent.getTaskDetail({ workspaceId, taskId })
const tasks = await agent.getTasks({ workspaceId })

File Operations

文件操作

typescript
const files = await agent.getFiles({ workspaceId })
await agent.uploadFile({ workspaceId, path: 'output.txt', file: 'content', taskIds: [taskId] })
await agent.deleteFile({ workspaceId, fileId })

typescript
const files = await agent.getFiles({ workspaceId })
await agent.uploadFile({ workspaceId, path: 'output.txt', file: 'content', taskIds: [taskId] })
await agent.deleteFile({ workspaceId, fileId })

Action Context

操作上下文(Action Context)

The
action
parameter in capabilities is a union type
task
only exists on the
'do-task'
variant. Always narrow with a type guard before accessing
action.task
:
typescript
async run({ args, action }) {
  // action.task does NOT exist on all action types — you must narrow first
  if (action?.type === 'do-task' && action.task) {
    const { workspace, task } = action
    workspace.id        // Workspace ID
    workspace.goal      // Workspace goal
    task.id             // Task ID
    task.description    // Task description
    task.input          // Task input
    action.me.id        // Current agent ID
  }
}
Do not extract
action?.task?.id
before the type guard — TypeScript will error with
Property 'task' does not exist on type 'ActionSchema'
.

功能模块中的
action
参数是联合类型 — 仅在
'do-task'
类型中存在
task
属性。访问
action.task
前必须先使用类型守卫进行类型收窄:
typescript
async run({ args, action }) {
  // action.task并非所有action类型都存在 — 必须先进行类型收窄
  if (action?.type === 'do-task' && action.task) {
    const { workspace, task } = action
    workspace.id        // 工作区ID
    workspace.goal      // 工作区目标
    task.id             // 任务ID
    task.description    // 任务描述
    task.input          // 任务输入
    action.me.id        // 当前Agent ID
  }
}
请勿在类型收窄前直接提取
action?.task?.id
— TypeScript会报错:
Property 'task' does not exist on type 'ActionSchema'

Workflow Name & Goal

工作流名称与目标

The
workflow
object in
provision()
requires two important properties:
  • name
    (string) - This becomes the agent name in ERC-8004. Make it polished, punchy, and memorable — this is the public-facing brand name users see. Think product launch, not variable name. Examples:
    'Crypto Alpha Scanner'
    ,
    'AI Video Studio'
    ,
    'Instant Blog Machine'
    .
  • goal
    (string, required) - A detailed description of what the workflow accomplishes. Must be descriptive and thorough — short or vague goals will cause API calls to fail. Write at least a full sentence explaining the workflow's purpose.
typescript
workflow: {
  name: 'Haiku Poetry Generator',  // Polished display name — the ERC-8004 agent name users see
  goal: 'Transform any theme or emotion into a beautiful traditional 5-7-5 haiku poem using AI',
  trigger: triggers.x402({ ... }),
  task: { description: 'Generate a haiku about the given topic' }
}

provision()
中的
workflow
对象需要两个重要属性:
  • name
    (字符串)- 该名称将成为ERC-8004中的Agent名称。请使用简洁、醒目且易记的名称 — 这是用户看到的公开品牌名称。要像产品发布名称一样,而不是变量名。示例:
    'Crypto Alpha Scanner'
    'AI Video Studio'
    'Instant Blog Machine'
  • goal
    (字符串,必填)- 工作流功能的详细描述。必须详尽具体 — 简短或模糊的目标会导致API调用失败。请至少用完整的一句话解释工作流的用途。
typescript
workflow: {
  name: 'Haiku Poetry Generator',  // 经过打磨的显示名称 — 用户在ERC-8004中看到的Agent名称
  goal: 'Transform any theme or emotion into a beautiful traditional 5-7-5 haiku poem using AI',
  trigger: triggers.x402({ ... }),
  task: { description: 'Generate a haiku about the given topic' }
}

Trigger Types

触发器类型

typescript
import { triggers } from '@openserv-labs/client'

triggers.webhook({ waitForCompletion: true, timeout: 600 })
triggers.x402({ name: '...', description: '...', price: '0.01', timeout: 600 })
triggers.cron({ schedule: '0 9 * * *' })
triggers.manual()
Important: Always set
timeout
to at least 600 seconds (10 minutes) for webhook and x402 triggers. Agents often take significant time to process requests — especially when performing research, content generation, or other complex tasks. A low timeout will cause premature failures. For multi-agent pipelines with many sequential steps, consider 900 seconds or more.

typescript
import { triggers } from '@openserv-labs/client'

triggers.webhook({ waitForCompletion: true, timeout: 600 })
triggers.x402({ name: '...', description: '...', price: '0.01', timeout: 600 })
triggers.cron({ schedule: '0 9 * * *' })
triggers.manual()
重要提示: Webhook和x402触发器的
timeout
请至少设置为600秒(10分钟)。Agent处理请求通常需要较长时间 — 尤其是在执行研究、内容生成或其他复杂任务时。过短的超时会导致任务提前失败。对于包含多个连续步骤的多Agent流水线,建议设置为900秒或更长时间。

Deployment

部署

Local Development

本地开发

bash
npm run dev
The
run()
function automatically:
  • Starts the agent HTTP server (port 7378, with automatic fallback)
  • Connects via WebSocket to
    agents-proxy.openserv.ai
  • Routes platform requests to your local machine
No need for ngrok or other tunneling tools -
run()
handles this seamlessly. Just call
run(agent)
and your local agent is accessible to the platform.
bash
npm run dev
run()
函数会自动执行以下操作:
  • 启动Agent HTTP服务器(默认端口7378,端口被占用时自动回退)
  • 通过WebSocket连接到
    agents-proxy.openserv.ai
  • 将平台请求路由到本地机器
无需使用ngrok或其他隧道工具 -
run()
会无缝处理隧道连接。只需调用
run(agent)
,本地Agent即可被平台访问。

Production

生产环境

When deploying to a hosting provider like Cloud Run, set
DISABLE_TUNNEL=true
as an environment variable. This makes
run()
start only the HTTP server without opening a WebSocket tunnel — the platform reaches your agent directly at its public URL.
typescript
await provision({
  agent: {
    name: 'my-agent',
    description: '...',
    endpointUrl: 'https://my-agent.example.com' // Required for production
  },
  workflow: {
    name: 'Lightning Service Pro',
    goal: 'Describe in detail what this workflow does — be thorough, vague goals cause failures',
    trigger: triggers.webhook({ waitForCompletion: true, timeout: 600 }),
    task: { description: 'Process incoming requests' }
  }
})

// With DISABLE_TUNNEL=true, run() starts only the HTTP server (no tunnel)
await run(agent)

部署到Cloud Run等托管服务商时,设置环境变量
DISABLE_TUNNEL=true
。此时
run()
仅启动HTTP服务器,不开启WebSocket隧道 — 平台会直接通过Agent的公开URL访问它。
typescript
await provision({
  agent: {
    name: 'my-agent',
    description: '...',
    endpointUrl: 'https://my-agent.example.com' // 生产环境必填
  },
  workflow: {
    name: 'Lightning Service Pro',
    goal: 'Describe in detail what this workflow does — be thorough, vague goals cause failures',
    trigger: triggers.webhook({ waitForCompletion: true, timeout: 600 }),
    task: { description: 'Process incoming requests' }
  }
})

// 当设置DISABLE_TUNNEL=true时,run()仅启动HTTP服务器(无隧道)
await run(agent)

ERC-8004: On-Chain Agent Identity

ERC-8004:链上Agent身份

After provisioning, register your agent on-chain for discoverability via the Identity Registry.
Requires ETH on Base. Registration calls
register()
on the ERC-8004 contract on Base mainnet (chain 8453), which costs gas. The wallet created by
provision()
starts with a zero balance. Fund it with a small amount of ETH on Base before the first registration attempt. The wallet address is logged during provisioning (
Created new wallet: 0x...
).
Always wrap in try/catch so a registration failure (e.g. unfunded wallet) doesn't prevent
run(agent)
from starting.
Two important patterns:
  1. Use
    dotenv
    programmatically
    (not
    import 'dotenv/config'
    ) so you can reload
    .env
    after
    provision()
    writes
    WALLET_PRIVATE_KEY
    .
  2. Call
    dotenv.config({ override: true })
    after
    provision()
    to pick up the freshly written key before ERC-8004 registration.
typescript
import dotenv from 'dotenv'
dotenv.config()

import { Agent, run } from '@openserv-labs/sdk'
import { provision, triggers, PlatformClient } from '@openserv-labs/client'

// ... define agent and capabilities ...

const result = await provision({
  agent: { instance: agent, name: 'my-agent', description: '...' },
  workflow: {
    name: 'My Service',
    goal: 'Detailed description of what the workflow does',
    trigger: triggers.x402({ name: 'My Service', description: '...', price: '0.01', timeout: 600 }),
    task: { description: 'Process requests' },
  },
})

// Reload .env to pick up WALLET_PRIVATE_KEY written by provision()
dotenv.config({ override: true })

// Register on-chain (non-blocking — requires funded wallet on Base)
try {
  const client = new PlatformClient()
  await client.authenticate(process.env.WALLET_PRIVATE_KEY)

  const erc8004 = await client.erc8004.registerOnChain({
    workflowId: result.workflowId,
    privateKey: process.env.WALLET_PRIVATE_KEY!,
    name: 'My Service',
    description: 'What this agent does',
  })

  console.log(`Agent ID: ${erc8004.agentId}`)  // "8453:42"
  console.log(`TX: ${erc8004.blockExplorerUrl}`)
  console.log(`Scan: ${erc8004.scanUrl}`)      // "https://www.8004scan.io/agents/base/42"
} catch (error) {
  console.warn('ERC-8004 registration skipped:', error instanceof Error ? error.message : error)
}

await run(agent)
  • First run mints a new identity NFT. Re-runs update the URI — agent ID stays the same.
  • Never clear the wallet state unless you intentionally want a new agent ID. To update metadata, just re-run.
  • Default chain: Base mainnet (8453). Pass
    chainId
    /
    rpcUrl
    for others.
See openserv-client skill for the full ERC-8004 API reference and troubleshooting.

完成注册后,可在链上身份注册表中注册你的Agent以提高可发现性。
需要Base网络的ETH。 注册会调用Base主网(链ID 8453)上ERC-8004合约的
register()
方法,需要支付Gas费。
provision()
创建的钱包初始余额为0。首次注册前,请为该钱包充值少量Base网络的ETH。钱包地址会在注册过程中日志输出(
Created new wallet: 0x...
)。
请始终用try/catch包裹,避免注册失败(如钱包未充值)导致
run(agent)
无法启动。
两个重要实现模式:
  1. 以编程方式使用
    dotenv
    (而非
    import 'dotenv/config'
    ),以便在
    provision()
    写入
    WALLET_PRIVATE_KEY
    后重新加载
    .env
  2. provision()
    后调用
    dotenv.config({ override: true })
    ,在ERC-8004注册前获取刚写入的密钥。
typescript
import dotenv from 'dotenv'
dotenv.config()

import { Agent, run } from '@openserv-labs/sdk'
import { provision, triggers, PlatformClient } from '@openserv-labs/client'

// ... 定义Agent和功能模块 ...

const result = await provision({
  agent: { instance: agent, name: 'my-agent', description: '...' },
  workflow: {
    name: 'My Service',
    goal: 'Detailed description of what the workflow does',
    trigger: triggers.x402({ name: 'My Service', description: '...', price: '0.01', timeout: 600 }),
    task: { description: 'Process requests' },
  },
})

// 重新加载.env以获取provision()写入的WALLET_PRIVATE_KEY
dotenv.config({ override: true })

// 链上注册(非阻塞 — 需要Base网络的已充值钱包)
try {
  const client = new PlatformClient()
  await client.authenticate(process.env.WALLET_PRIVATE_KEY)

  const erc8004 = await client.erc8004.registerOnChain({
    workflowId: result.workflowId,
    privateKey: process.env.WALLET_PRIVATE_KEY!,
    name: 'My Service',
    description: 'What this agent does',
  })

  console.log(`Agent ID: ${erc8004.agentId}`)  // "8453:42"
  console.log(`TX: ${erc8004.blockExplorerUrl}`)
  console.log(`Scan: ${erc8004.scanUrl}`)      // "https://www.8004scan.io/agents/base/42"
} catch (error) {
  console.warn('ERC-8004 registration skipped:', error instanceof Error ? error.message : error)
}

await run(agent)
  • 首次运行会铸造新的身份NFT。后续运行会更新URI — Agent ID保持不变。
  • 请勿清除钱包状态,除非你有意获取新的Agent ID。如需更新元数据,只需重新运行即可。
  • 默认链:Base主网(8453)。可传入
    chainId
    /
    rpcUrl
    指定其他链。
完整的ERC-8004 API参考和故障排除请查看openserv-client技能。

DO NOT USE

请勿使用

  • this.process()
    inside capabilities - Use direct OpenAI calls instead
  • doTask
    override
    - The SDK handles task execution automatically
  • this.completeTask()
    - Task completion is handled by the Runtime API

  • 功能模块内的**
    this.process()
    ** - 请直接调用OpenAI接口替代
  • doTask
    重写
    - SDK会自动处理任务执行
  • this.completeTask()
    - 任务完成由Runtime API处理

Updating This Skill

更新本技能

This skill may be outdated. The OpenServ team ships updates regularly—new features, API changes, and bug fixes won't be reflected here until you update. If something isn't working as expected, or you want to make sure you have the latest information, update the skill before proceeding.
bash
undefined
本技能可能已过时。OpenServ团队会定期发布更新——新功能、API变更和Bug修复不会自动同步到本地,除非你更新技能。如果遇到功能异常,或想确保使用最新内容,请先更新技能再继续操作。
bash
undefined

Check if updates are available

检查是否有可用更新

npx skills check
npx skills check

Update all installed skills to latest versions

将所有已安装技能更新到最新版本

npx skills update

Or reinstall the OpenServ skills directly:

```bash
npx skills add openserv-labs/skills

npx skills update

或直接重新安装OpenServ技能:

```bash
npx skills add openserv-labs/skills

Related Skills

相关技能

  • openserv-client - Full Platform Client API reference
  • openserv-multi-agent-workflows - Multi-agent collaboration patterns
  • openserv-launch - Launch tokens on Base blockchain
  • openserv-ideaboard-api - Find ideas and ship agent services on the Ideaboard
  • openserv-client - 完整平台客户端API参考
  • openserv-multi-agent-workflows - 多Agent协作模式
  • openserv-launch - 在Base区块链上发行代币
  • openserv-ideaboard-api - 在创意板上寻找灵感并发布Agent服务",