Skip to main content
SDK

TypeScript SDK

安装和使用 Qoder Cloud Agents TypeScript SDK,了解类型、异步调用、流式响应、取消、分页和自定义 fetch。

Qoder Cloud Agents TypeScript SDK 可用于 TypeScript 和 JavaScript 应用,提供 Forward、Managed 客户端、请求与响应类型、异步分页和 SSE 流式读取。包同时提供 ES Modules 和 CommonJS 入口。

安装

需要 Node.js 20.12 或更高版本:
npm install qca-sdk
将包版本和锁文件纳入版本控制。本文示例运行在 Node.js 服务端。

基本用法

将个人访问令牌(PAT)保存到环境变量:
export QODER_PAT="替换为你的 PAT"
使用 Forward 客户端创建模板和身份,发起会话并发送消息。以下代码可保存为 .mjs 文件直接运行,也可用于支持 ES Modules 的 TypeScript 项目:
import { ForwardClient } from 'qca-sdk';

const client = new ForwardClient();

const env = await client.environments.create({
  name: 'demo',
  config: { type: 'cloud' },
});

const identity = await client.identities.create({
  external_id: 'demo-user',
  name: 'Demo User',
});

const template = await client.templates.create({
  name: 'assistant',
  model: 'auto',
  environment_id: env.id,
});

const session = await client.sessions.create({
  identity_id: identity.id,
  template_id: template.id,
});

await client.sessions.events.send(session.id, {
  events: [{
    type: 'user.message',
    content: [{ type: 'text', text: '你好' }],
  }],
});

console.log(session.id);
使用 Managed 客户端创建 Agent 和环境,发起会话并发送消息:
import { ManagedClient } from 'qca-sdk';

const client = new ManagedClient();

const agent = await client.agents.create({
  name: 'assistant',
  model: 'auto',
});

const env = await client.environments.create({
  name: 'demo',
  config: { type: 'cloud' },
});

const session = await client.sessions.create({
  agent: agent.id,
  environment_id: env.id,
});

await client.sessions.events.send(session.id, {
  events: [{
    type: 'user.message',
    content: [{ type: 'text', text: '你好' }],
  }],
});

console.log(session.id);
也可以从 qca-sdk/forward、qca-sdk/managed 导入对应客户端。CommonJS 项目使用 require('qca-sdk'),并把 await 调用放入异步函数。 后续片段中未重新创建的 client 指已配置的 Forward 客户端。sess_example 等 ID 需要替换为当前凭证可访问的真实 ID。更多接入说明见 Managed 快速入门和 Forward API 参考。

客户端配置

配置ForwardManaged
客户端ForwardClientManagedClient
Base URL 环境变量QODER_FORWARD_BASE_URLQODER_BASE_URL
Global 默认地址https://api.qoder.com/api/v1/forwardhttps://api.qoder.com/api/v1/cloud
CN 地址https://api.qoder.com.cn/api/v1/forwardhttps://api.qoder.com.cn/api/v1/cloud
显式配置优先于环境变量。默认 PAT 模式先读取 QODER_ACCESS_TOKEN,再读取 QODER_PAT。SDK 不自动加载 .env 文件。Base URL 需要包含完整 API 前缀,与账号、凭证及资源所在区域一致。
import { ForwardClient } from 'qca-sdk';

const client = new ForwardClient({
  baseURL: 'https://api.qoder.com.cn/api/v1/forward',
  timeout: 30_000,
  maxRetries: 2,
  defaultHeaders: { 'X-Application': 'my-app' },
});

const result = await client.models.list({ timeout: 5_000 });
console.log(result.data.length);
时间单位为毫秒。单次调用的请求配置通常位于方法的最后一个参数;有查询或请求体参数的方法需要先传入该参数,不能将两者混用。

复用配置

withOptions() 创建同类型的新客户端,保留没有覆盖的配置,原客户端保持原配置:
const slowClient = client.withOptions({ timeout: 60_000, maxRetries: 1 });
const result = await slowClient.sessions.list({ limit: 20 });
console.log(result.data.length);
传入新的 defaultHeaders、defaultQuery 或 middleware 时,会替换对应的整个配置值。单次请求配置仍优先于派生客户端的默认值。

鉴权

PAT

使用 pat 显式传入个人访问令牌,或使用环境变量。访问令牌保存在受信任的服务端环境中,前端应用通过自己的业务服务访问 QCA。

Service Account

设置 QODER_SA_KEY 后,显式创建凭证组件。组件负责置换、缓存 SAT(服务账号令牌),并在后续请求需要时更新:
import {
  ForwardClient,
  ManagedClient,
  ServiceAccountCredential,
} from 'qca-sdk';

const credential = ServiceAccountCredential.fromEnv({
  region: 'global',
  scopes: ['qca.access', 'forward.access'],
});

const forward = new ForwardClient({
  credential,
  baseURL: 'https://api.qoder.com/api/v1/forward',
});
const managed = new ManagedClient({
  credential,
  baseURL: 'https://api.qoder.com/api/v1/cloud',
});
多个客户端可以共享同一凭证实例和缓存。中国站需要同时设置 region: 'cn' 和 CN Base URL。仅访问 Managed 时可以申请 qca.access scope;权限及工作空间绑定需在服务端配置。 普通客户端不会因为环境中存在 QODER_SA_KEY 就自动启用置换,SA Key 也不能直接作为业务接口的访问令牌。SA 鉴权失败不会回退到 PAT。 应用自行管理 SAT 时使用 { authMode: 'sa', accessToken: token }。accessToken 也接受返回令牌或 Promise<string> 的函数,在每次请求尝试前重新求值。静态 SAT 的更新由应用负责;不要同时配置 accessToken、pat 和 credential 等多个来源。更多说明见认证文档。

请求与响应类型

请求与响应类型从对应模式的子路径导入,字段保留 API 的 snake_case 命名:
import type { SessionUpdateParams } from 'qca-sdk/forward';

const omitted: SessionUpdateParams = {};
const explicitNull: SessionUpdateParams = { title: null };
const emptyString: SessionUpdateParams = { title: '' };
JavaScript 值JSON 对象字段的行为
省略属性,或值为 undefined不发送该字段
null发送 null
false、0、''原样发送
空数组、空对象发送 []、{}
字段是否接受 null,以及如何处理该值,以对应接口定义为准。联合类型应通过其判别字段或属性检查收窄后再读取具体字段。 服务端新增的未知响应字段会保留,但不一定已经包含在当前 TypeScript 类型声明中。需要使用它们时,先检查实际值,再声明应用所需的扩展类型。TypeScript 类型本身不等于运行时数据校验。

流式响应

streamEvents() 返回可异步迭代的 SSE 流。使用 for await...of 读取,并在 finally 中释放连接:
const stream = await client.sessions.events.streamEvents('sess_example');
try {
  for await (const event of stream) {
    console.log(event.type);
  }
} finally {
  await stream.close();
}
普通流在 EOF 时结束。需要在可恢复的断连后继续订阅时,显式使用 resumableStream():
const stream = client.sessions.events.resumableStream(
  'sess_example',
  { last_event_id: 'evt_checkpoint' },
  { signal: AbortSignal.timeout(60_000) },
);

try {
  for await (const event of stream) {
    console.log(event.type);
    // 应用成功处理事件后,再持久化该恢复位置。
    console.log('cursor:', stream.lastEventID);
  }
} finally {
  await stream.close();
}
将 evt_checkpoint 替换为有效的恢复位置;首次订阅可省略 last_event_id。resumableStream() 直接返回异步可迭代对象,创建时不需要 await。示例中的 AbortSignal 将整个订阅限制为 60 秒;达到期限的取消会按取消错误向调用方传播。 恢复流保留最近交付事件的 SSE 游标,并在重连时继续使用。它不会重新发送业务请求,也不保证“恰好一次”处理。同一事件的多个增量可能共用 ID,不应仅按 ID 丢弃所有重复帧。 没有游标时从当前事件流尾部开始;补读历史需要有效的历史恢复位置。HTTP 请求重试次数不等于整个订阅的重连上限。具体事件含义和任务完成条件见对应 API 定义。 关闭流或取消本地 HTTP 请求只结束当前订阅,不会自动停止云端已经开始的执行。

错误处理与 Request ID

使用 instanceof 区分调用方取消、超时、HTTP 错误等情况:
import {
  APIConnectionTimeoutError,
  APIError,
  APIUserAbortError,
} from 'qca-sdk';

try {
  const result = await client.sessions.retrieve('sess_example');
  console.log(result._request_id);
} catch (error) {
  if (error instanceof APIUserAbortError) {
    console.log('请求已取消');
  } else if (error instanceof APIConnectionTimeoutError) {
    console.log('请求超时');
  } else if (error instanceof APIError) {
    console.log(error.status, error.code, error.request_id);
  } else {
    throw error;
  }
}
APIError 覆盖 HTTP、连接、超时和取消错误。没有收到 HTTP 响应时,status、headers 和 response 可能为 undefined,不能假设它们始终存在。客户端配置错误属于更广泛的 QoderError。 常见 HTTP 子类包括 BadRequestError(400)、AuthenticationError(401)、PermissionDeniedError(403)、NotFoundError(404)、ConflictError(409)、RateLimitError(429)和 InternalServerError(5xx)。 普通 JSON 对象响应的 _request_id 来自响应头,缺失时为 null。它是不可枚举属性,不会出现在 JSON.stringify() 或对象展开结果中。HTTP 错误通过 request_id 提供标识;需要与分页或流一起读取响应元数据时,使用 .withResponse()。单个流事件和嵌套对象不自动携带这份元数据。

重试与幂等性

默认最多重试 2 次,即最多尝试 3 次。自动重试要求请求可安全重放:
请求默认可重试情况
GET、HEAD,或携带幂等键的请求连接错误、超时、408、429、5xx
未携带幂等键的其他请求429
409 响应不自动重试
请求体为不可重复读取的 ReadableStream不自动重放
在这些规则内,SDK 处理服务端重试提示和等待时间。部分接口有更严格的规则。通过客户端的 maxRetries 配置默认次数,或在单次调用的请求配置中覆盖:
const result = await client.sessions.retrieve('sess_example', { maxRetries: 0 });
console.log(result.id);
写请求通过请求配置中的 idempotencyKey 传入幂等键。同一次逻辑操作的所有重试复用同一个键,新的操作使用新的键。

超时与取消

默认超时为 10 分钟。timeout 控制每次底层 fetch 等待响应到达的时间:计时在调用 fetch 前开始,收到响应头后结束,不包含此前的凭证获取、中间件执行和后续响应体读取。 因此,SSE 或下载读取可能超过 timeout。需要控制整个操作的总时长时,传入 AbortSignal:
const result = await client.sessions.retrieve('sess_example', {
  timeout: 10_000,
  signal: AbortSignal.timeout(30_000),
});
console.log(result.id);
应用也可以持有 AbortController,在用户取消时调用 abort():
const controller = new AbortController();
const pending = client.sessions.retrieve('sess_example', {
  signal: controller.signal,
});

// 在应用的取消事件处理中调用 controller.abort()。
const result = await pending;
console.log(result.id);
信号取消会结束该调用的等待、重试及响应体读取。自定义中间件或外部凭证组件自己的工作,也应遵循其取消和资源管理约定。

分页

列表方法支持异步迭代,按需读取后续页:
for await (const item of client.sessions.list({ limit: 20 })) {
  console.log(item.id);
}
需要逐页处理时,读取 .data,并先检查是否存在下一页:
const page = await client.sessions.list({ limit: 20 });
console.log(page.data.length);

if (page.hasNextPage()) {
  const next = await page.getNextPage();
  console.log(next.data.length);
}
也可以使用 for await (const currentPage of page.iterPages()) 遍历页面。getNextPage() 在没有下一页时抛出错误,不返回 null;自动迭代会在末页正常结束。分页器保留筛选条件和请求配置。

高级配置

原始响应

普通请求返回 APIPromise。直接 await 得到解析后的数据;.withResponse() 同时返回数据、原始响应和 Request ID:
const { data, response, request_id } = await client.sessions
  .retrieve('sess_example')
  .withResponse();

console.log(data.id, response.status, request_id);
.asResponse() 在不读取响应体的情况下返回原始 Response,调用方负责消费或取消响应体:
const response = await client.sessions.retrieve('sess_example').asResponse();
try {
  console.log(response.status, response.headers.get('x-request-id'));
} finally {
  await response.body?.cancel();
}

自定义 fetch 和中间件

使用 fetch 注入 HTTP 实现。代理、TLS 和连接池设置由所选实现负责。中间件用于观察或调整每次 API 请求尝试:
import { ForwardClient } from 'qca-sdk';

const client = new ForwardClient({
  fetch: globalThis.fetch,
  middleware: [
    async (request, next) => {
      const started = Date.now();
      const response = await next(request);
      console.log(request.method, response.status, Date.now() - started);
      return response;
    },
  ],
});
这里记录的是收到响应时的耗时,不代表 SSE 消费完成。中间件属于 API 请求链路,不应当作覆盖所有外部连接的全局拦截器。 额外 Header 和 Query 使用请求配置中的 headers、query。类型尚未覆盖的请求字段可以放入参数对象;只有服务端已经支持的字段才能生效。普通调用优先使用已有的类型化方法。