ListenHubSDKs & CLI
JavaScript SDK

配置

为两个 SDK 客户端配置 base URL、超时与重试,并了解响应解包、ListenHubError 与 429 自动重试的行为。

两个 SDK 客户端都在构造函数里接收一个 options 对象。二者共用相同的选项名与相同的响应处理逻辑,但默认值不同——OpenAPIClient 面向服务端场景,超时更长。本页覆盖每一个选项、客户端为你解包的响应信封、错误如何以 ListenHubError 形式抛出、429 的自动重试,以及调用 SDK 尚未封装端点时的 client.api 逃生通道。

选项

ListenHubClient 接收 ClientOptionsOpenAPIClient 接收 OpenAPIClientOptions。所有字段都是可选的。

选项类型OpenAPIClient 默认值ListenHubClient 默认值
apiKeystring环境变量 LISTENHUB_API_KEY—(不适用)
accessTokenstring | (() => string | undefined)—(不适用)无(匿名)
baseURLstringhttps://api.marswave.ai/openapihttps://api.listenhub.ai/api
timeoutnumber(毫秒)6000030000
maxRetriesnumber22

凭证因客户端而异:OpenAPIClientapiKey 鉴权,ListenHubClientaccessToken。如何获取各自的凭证见 鉴权

baseURL

把客户端指向特定的 API 主机。默认值已指向生产环境,所以你很少需要手动设置。当你不传 baseURL 时,每个客户端还会读取一个环境变量作为兜底:

  • OpenAPIClientLISTENHUB_OPENAPI_URL
  • ListenHubClientLISTENHUB_API_URL

显式传入的 baseURL 选项始终优先于环境变量。

import { OpenAPIClient } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient({
  baseURL: 'https://api.marswave.ai/openapi',
});

accessToken(ListenHubClient

accessToken 接收一个静态字符串,或一个 getter 函数 () => string | undefined。当你传入 getter 时,SDK 会在每次请求前调用它,因此一个长生命周期的客户端无需重建就能始终发送当前的 token。当 token 会随时间刷新时,推荐使用这种形式。

import { ListenHubClient } from '@marswave/listenhub-sdk';

// 静态 token——适用于短生命周期的客户端
const client = new ListenHubClient({ accessToken: tokens.accessToken });

// getter——每次请求前调用,返回你手头最新的 token
const live = new ListenHubClient({ accessToken: () => store.accessToken });

token 刷新的生命周期(何时以及如何换取新 token)见 鉴权

apiKey(OpenAPIClient

apiKey 是服务端场景的凭证。可以显式传入,也可以省略并让构造函数从环境读取 LISTENHUB_API_KEY。两者都没有时,构造函数会抛出异常。

import { OpenAPIClient } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient({ apiKey: process.env.LISTENHUB_API_KEY });

API key 是拥有账户完整权限的密钥。请放在服务端——绝不要打进浏览器或移动端代码。面向用户的应用请改用带 OAuth 的 ListenHubClient,让每个请求都在用户自己的账户下执行。

timeout

单次请求的超时时间,单位毫秒。超过该时长的请求会被中止并 reject。OpenAPIClient 默认 60000(60 秒),因为服务端的生成调用可能耗时较长;ListenHubClient 默认 30000(30 秒)。

const client = new OpenAPIClient({ timeout: 120_000 }); // 2 分钟

超时作用于 HTTP 请求本身,而非长耗时的生成任务。生成是异步的——创建调用会很快返回一个 episodeIdtaskId,随后你轮询结果。见 快速开始 中的创建并轮询循环。

maxRetries

客户端在放弃前对 429 Too Many Requests 响应的重试次数。默认 2。设为 0 可禁用重试。只有 429 会被重试——其他错误状态会立即抛出。见下文 429 限流重试

const client = new OpenAPIClient({ maxRetries: 0 }); // 快速失败,不重试

响应解包

每个 ListenHub 响应都包裹在一个信封中:

{ "code": 0, "message": "", "data": { } }

code0 表示成功;任何非零 code 都是错误。两个客户端都会在 afterResponse hook 中为你处理这个信封,因此你的代码可以直接使用 data

  • code 0 时,方法 resolve 为 data——你永远看不到那层包装。
  • code 非零时,方法抛出一个 ListenHubError,携带信封中的 codemessagerequest_id
  • 204 No Content 响应(以及任何非 JSON 的 body)会原样透传。
// SDK 直接返回 data——无需剥离信封
const { items } = await client.listSpeakers({ language: 'en' });

少数方法返回原始的 Response 而非解包后的 JSON,因为它们是流式或二进制——例如 TTS 音频(ttsaudioSpeech)以及 text-stream 端点。这些是例外,其余方法都 resolve 为 data

错误处理

当请求失败时——凭证被拒、校验错误、非零 code,或 HTTP 错误状态——方法会抛出一个 ListenHubError。捕获它并检查其字段:

字段类型含义
statusnumberHTTP 状态码(如 401429500
codestring信封中的后端错误码;非 JSON 失败时为 GATEWAY_ERROR / UNKNOWN_ERROR
messagestring可读的错误信息
requestIdstring | undefined请求标识符——联系支持时请附上
import { OpenAPIClient, ListenHubError } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient();

try {
  const sub = await client.getSubscription();
  console.log(sub);
} catch (err) {
  if (err instanceof ListenHubError) {
    // 来自 API 的结构化错误
    console.error(`[${err.status}] ${err.code}: ${err.message}`);
    if (err.requestId) console.error(`request ${err.requestId}`);

    if (err.status === 401 || err.status === 403) {
      // 凭证被拒——轮换 API key,或刷新 OAuth token
    }
  } else {
    // 网络故障、超时,或其他非 API 错误
    throw err;
  }
}

非 JSON 的失败同样会抛出 ListenHubError。网关或代理返回 HTML 错误页时,错误码为 GATEWAY_ERROR(以页面的 <title> 作为 message);其他无法解析的情形则为 UNKNOWN_ERROR。两种情况下 status 仍反映 HTTP 状态。

429 限流重试

429 Too Many Requests 响应会被自动重试——你无需自己写重试逻辑。客户端会:

  1. 读取 Retry-After 头来决定等待多久。
  2. 最多重试 maxRetries 次(默认 2)。
  3. 若所有重试都用尽,抛出一个 status: 429ListenHubError

因此短暂的限流通常会自行恢复,根本不会以错误形式出现。如果你希望 429 立即抛出,把 maxRetries 设为 0。只有 429 会触发这条路径;其他错误状态在首个响应时就会抛出。

client.api 逃生通道

ListenHubClient 通过 client.api 暴露其底层的 ky 实例。用它来调用 SDK 尚未封装的端点——相同的鉴权头、base URL、信封解包与 429 重试仍然生效,因为你复用的是已配置好的客户端。

import { ListenHubClient } from '@marswave/listenhub-sdk';

const client = new ListenHubClient({ accessToken: tokens.accessToken });

// 路径相对于 baseURL——不带前导斜杠(ky 的要求)
const data = await client.api.get('v1/some/new-endpoint').json();
const created = await client.api
  .post('v1/some/new-endpoint', { json: { foo: 'bar' } })
  .json();

传给 client.api 的路径相对于 baseURL,且不能以前导 / 开头——这是 ky 的要求。用 v1/...,而非 /v1/...

逃生通道在 ListenHubClient 上可用。在 OpenAPIClient 上,优先使用类型化的方法;若你需要某个它未覆盖的端点,请查阅 OpenAPI reference,并用你自己的 HTTP 客户端、以相同的 Authorization: Bearer 头去调用。

下一步

On this page