# @edgeone/makers-sdk 中文文档

规范地址：https://npmcn.com/package/%40edgeone/makers-sdk/

版本：0\.2\.0
npm 元数据更新时间：2026-11-11T04:00:46.253Z
中文译文更新时间：2026-11-13T06:42:52.600Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40edgeone/makers-sdk
原文对照：https://npmcn.com/package/%40edgeone/makers-sdk/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：263 kB，31 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：4 个
- 首次发布：2026-08-17T12:19:00.714Z
- 最新版本发布：2026-11-11T04:00:46.051Z
- 近 30 天下载量：4760（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：9（近 12 个月 9 个）

## 中文简介

[联合国](https://npmcn.com/package/%40edgeone/makers-sdk/README.md) | [页:1](https://npmcn.com/package/%40edgeone/makers-sdk/README.zh-CN.md)

## 安装

```sh
npm install @edgeone/makers-sdk
```

也可以使用：`pnpm add @edgeone/makers-sdk`、`yarn add @edgeone/makers-sdk`、`bun add @edgeone/makers-sdk`

## README

# EdgeOne Makers TypeScript SDK

[English](https://npmcn.com/package/%40edgeone/makers-sdk/README.md) | [中文](https://npmcn.com/package/%40edgeone/makers-sdk/README.zh-CN.md)

面向 EdgeOne Makers 项目、环境变量、制品部署与租户 token 的 Node.js 20+ 仅 ESM 客户端。不支持浏览器运行时。

npm 包名：`@edgeone/makers-sdk` · 版本 `0.2.0` · 合同 `0.1.42` · MIT License

## 安装

```sh
npm install @edgeone/makers-sdk
```

```ts
import { Makers } from "@edgeone/makers-sdk";
```

* * *

## Makers

### 构造

```ts
const makers = new Makers({
  token: process.env.MAKERS_API_TOKEN,
  region: "china",
});
```

| 参数 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | :-: | --- | --- |
| `token` | `string` | 否 | — | EdgeOne Makers API Token。未传 `credentials` 时必填 |
| `region` | `"china" | "global"` | 否 | API Token：自动探测；AK/SK：china | API Token：`china` → `pages-api.cloud.tencent.com/v1`；`global` → `pages-api.edgeone.ai/v1`；AK/SK 映射见下文 |
| `timeout` | `number` | 否 | `30` | 单次请求超时，**秒** |
| `retries` | `number` | 否 | `3` | 查询类最大重试次数；写操作不重试 |
| `logger` | `Logger` | 否 | — | 需实现 `debug`、`info`、`warn`、`error` |
| `credentials` | `{ secretId, secretKey }` | 否 | — | 腾讯云 API 凭证。传入后改走云 API 通道，并可调用归属校验、CNAME 与证书方法。两个字段都必须是非空字符串。不是 STS Token |
| `zoneId` | `string` | 传 `credentials` 时必填 | — | Makers 站点 ID。传 `credentials` 时必填；未传 `credentials` 却传入时抛出 `ValidationError` |

自动探测先试 china 再试 global，结果按 Makers 实例缓存。只在 API Token 通道请求时探测。

### 云 API 凭证

```ts
const makers = new Makers({
  credentials: {
    secretId: process.env.TENCENTCLOUD_SECRET_ID!,
    secretKey: process.env.TENCENTCLOUD_SECRET_KEY!,
  },
  zoneId: process.env.EDGEONE_MAKERS_ZONE_ID,
  region: "global",
});
```

传入 `credentials` 与 `zoneId` 后，项目、环境变量、部署、自定义域名的创建、查询、删除，以及 `tokens.create` 走云 API 通道：向 `https://teo.tencentcloudapi.com` 调用 `DescribePagesResources`、`CreatePagesResources`、`ModifyPagesResources` 或 `DeletePagesResources`，请求体顶层带 Makers 站点 `ZoneId`。只传 `token` 时走 Pages API。两条通道的方法签名与返回结果相同，云 API 通道失败后不会改用 API Token 通道重试。

`zoneId` 是 Makers 站点 ID，SDK 不会自动查找。可以调用云 API `DescribeZones` 并按 `zone-type=pages` 过滤获取（开通 Makers 时会自动创建名为 `default-pages-zone` 的站点），或从 `DescribePagesProjects` 读取项目的 `ZoneId`。账号有多个 Makers 站点时，为每个站点各构造一个 `Makers`；项目相关调用必须使用项目所属的站点。

传入 `credentials` 与 `zoneId` 时，`region: "china"` 对应网关 `X-TC-Region: ap-guangzhou`，`region: "global"` 对应 `ap-singapore`；省略时默认广州，不探测 Pages 主机或其他 Region。同时传 token 时仍优先 AK/SK，失败后不切换通道或 Region，查询重试保持原 Region。项目 `area` 与客户端 `region` 独立。六个直接 teo 域名 / 证书方法仍不发送 `X-TC-Region`，主机与签名保持不变。

兼容性说明：此前 AK/SK 下显式 `global` 被忽略，现在会实际选择新加坡；省略 region 的行为不变。新加坡映射已通过协议回归测试，国际账号现网端到端验证仍待完成；Region 选择不保证账号权限或服务可用性。

项目、环境变量、部署、全部 `domains` 方法与 `tokens.create` 均支持只传云 API 凭证。云 API 凭证应留在平台侧，不要传给用租户 token 构造的 `Makers`。

### 公开成员

| 成员 | 类型 | 说明 |
| --- | --- | :-: |
| `makers.projects` | `Projects` | 项目与环境变量操作 |
| `makers.deployments` | `Deployments` | 部署操作 |
| `makers.tokens` | `Tokens` | 签发租户 token（`tokens.create`） |
| `makers.domains` | `Domains` | 自定义域名：创建、查询、删除、归属校验、CNAME 检查与证书 |
| `makers.region` | `Region | undefined` | 只读；配置值或已探测到的值 |

* * *

## Tokens

用**主 API Token** 或账号的云 API 凭证（`credentials` + `zoneId`）签发租户 token，再用返回的 `token` 构造新的 `Makers` 去管理该租户的项目。`tokens.create` 必填 `tenantId`、`name`、`expiresIn`，返回 `{ token, tokenId, expired }`。

`expiresIn` 是相对秒，不是 unix 时间戳。返回的 `expired` 是后端过期时间（unix 秒）。

必须用主 API Token 或账号的云 API 凭证调用 `tokens.create`。传入 `credentials` 时调用 `CreatePagesResources`，`Interface` 为 `pages:CreatePagesUserApiToken`；只传 `token` 时走 Pages API。同一租户 ID 在两条通道上返回同一个租户 token。SDK 不会判断当前 token 是主 Token 还是租户 token，也不会拦截子 Token 再签发。v1 不提供 token 的 list、get 或 delete。

对同一 `tenantId` 再次调用 `tokens.create` 是幂等的：返回同一 `token` 与 `tokenId`，仅 `expired` 可能刷新。

### `tokens.create(options)`

```ts
const platform = new Makers({
  token: process.env.MAKERS_API_TOKEN,
});

const { token, tokenId, expired } = await platform.tokens.create({
  tenantId: "user-open-id",
  name: "user-open-id",
  expiresIn: 86400,
});

const user = new Makers({ token });
const { projectId } = await user.projects.create({ name: "my-site" });
await user.deployments.deploy({
  projectId,
  artifact: { files: { "index.html": "<h1>Hello</h1>" } },
});
```

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | :-: | --- |
| `tenantId` | `string` | 是 | 租户标识；非空，最长 64 |
| `name` | `string` | 是 | Token 名称；1–128 个字符 |
| `expiresIn` | `number` | 是 | 有效期，相对秒（整数 10–315360000），不是 unix 时间戳 |

**返回** `{ token, tokenId, expired }`。`expired` 是后端过期时间（unix 秒）。非法入参在发请求前抛 `ValidationError`。

* * *

## Projects

### `projects.create(options)`

创建项目。返回 `{ projectId }`，不含完整模型——需要名称、域名等信息须再调 `get`。

```ts
const { projectId } = await makers.projects.create({
  name: "docs-site",
  area: "overseas",
  initialEnvVars: [{ key: "API_URL", value: "https://example.com" }],
});
```

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | :-: | --- |
| `name` | `string` | 是 | 与账号内已有项目重名时，目前抛 `MakersError`，`code` 为 `ResourceUnavailable` |
| `area` | `"mainland" | "overseas" | "global"` | 否 | 加速区域；不传则不发送 |
| `initialEnvVars` | `EnvVar[]` | 否 | 建项目时同时设环境变量 |

### `projects.list(options?)`

```ts
const page = await makers.projects.list({
  name: "docs",
  page: 0,
  pageSize: 20,
  order: { field: "createdOn", direction: "desc" },
});
```

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | :-: | --- |
| `projectIds` | `string[]` | — | 按 ID 过滤 |
| `name` | `string` | — | 按名称过滤 |
| `status` | `string` | — | 按状态过滤 |
| `provider` | `string` | — | 按 Provider 过滤 |
| `page` | `number` | `0` | 从 0 开始 |
| `pageSize` | `number` | `20` | 1–100 |
| `order` | `{ field, direction }` | — | `field`: `"createdOn"` 或 `"modifiedOn"`；`direction`: `"asc"` 或 `"desc"` |

**返回** `Page<Project>`：`{ items, page, pageSize, total, hasNext }`。

### `projects.listAll(options?)`

```ts
for await (const project of makers.projects.listAll()) {
  console.log(project.projectId, project.name);
}
```

与 `list` 同参数但无 `page`。返回 `AsyncIterable<Project>`。

### `projects.get({ projectId })`

查询单个项目的完整模型。这是唯一能拿到 `presetDomain`（Production 域名）的地方。

```ts
const project = await makers.projects.get({ projectId });
console.log(project.presetDomain);
```

### `projects.update(options)`

更新项目设置。只传入要改的字段，不传不清空。

```ts
await makers.projects.update({
  projectId,
  name: "new-name",
  rootDir: ".",
  outputDir: "dist",
  buildCmd: "npm run build",
  installCmd: "npm install",
  framework: "other",
  nodejsVersion: "20",
});
```

| 参数 | 类型 | 说明 |
| --- | --- | :-: |
| `projectId` | `string` | 必填 |
| `name` | `string` | 项目名称 |
| `rootDir` | `string` | 根目录 |
| `outputDir` | `string` | 输出目录 |
| `buildCmd` | `string` | 构建命令 |
| `installCmd` | `string` | 安装命令 |
| `framework` | `string` | 框架标识 |
| `nodejsVersion` | `string` | Node.js 版本 |

### `projects.delete({ projectId })`

删除项目。**不可恢复。**

* * *

## 环境变量

方法位于 `makers.projects`。公开字段：`key`、`value`、可选 `comment`。改项目变量使用 `setEnvs`；`deploy` 不接受环境变量。

这些方法管理项目变量，不管理环境资源。不提供 `env` 或作用域选择：传入自定义部署 Slug 不会增加分环境变量能力。SDK 不公开环境资源的创建、查询、修改或删除方法。

### `projects.listEnvs({ projectId })`

返回 `EnvVar[]`。值自动加入脱敏名单。

### `projects.setEnvs({ projectId, envVars })`

批量 upsert。内部先读后写，非原子操作。重复 key 在发请求前抛 `ValidationError`。

```ts
await makers.projects.setEnvs({
  projectId,
  envVars: [
    { key: "API_URL", value: "https://example.com", comment: "origin" },
  ],
});
```

### `projects.deleteEnvs({ projectId, keys })`

按 key 删除。重复 key 抛 `ValidationError`。每个 key 发送为 `{ Key, Value: "-" }`；`Value` 只是占位，不参与匹配。

* * *

## Deployments

### `deployments.deploy(options)`

部署制品。必须且只能传一种变体：

| 变体 | 类型 | 行为 |
| --- | --- | :-: |
| `files` | `Record<string, string | Uint8Array>` | SDK 在内存中打 Zip |
| `directory` | `string`（可选 `excludePatterns: string[]`） | SDK 打包目录 |
| `archive` | `string`（Zip 路径） | 安全校验后直接上传 |

```ts
// 内联文件
await makers.deployments.deploy({
  projectId,
  artifact: { files: { "index.html": "<h1>ok</h1>" } },
  wait: true,
});

// 应用源码（项目根）
await makers.deployments.deploy({
  projectId,
  artifact: { directory: "." },
  wait: true,
});

// 静态构建输出
await makers.deployments.deploy({
  projectId,
  artifact: { directory: "./dist", excludePatterns: ["**/*.map"] },
});

// 预构建 CLI 产物
await makers.deployments.deploy({
  projectId,
  artifact: { directory: "./.edgeone" },
  wait: true,
});

// Zip 文件
await makers.deployments.deploy({
  projectId,
  artifact: { archive: "./site.zip" },
});
```

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | :-: | --- |
| `projectId` | `string` | — | 必填 |
| `artifact` | `Artifact` | — | 必填；三种变体互斥 |
| `env` | `string` | `"Production"` | 非空环境 Slug，例如 `"Production"`、`"Preview"` 或 `"Staging"`。Preview 要求已有 Production 部署 |
| `wait` | `boolean` | `false` | 为 true 时轮询到终态 |
| `timeout` | `number` | `900` | 最长等待时间（秒，15 分钟） |
| `pollInterval` | `number` | `5` | 轮询间隔（秒） |
| `onUploadProgress` | 回调 | — | 上传完成后触发一次 |
| `onStatusChange` | 回调 | — | 每次状态变更触发 |

**返回** `Deployment`。`wait: false` 时只有 `deploymentId`、`projectId`、`env` 三个字段，那不是可打开的站点链接。`wait: true` 到达 `Success` 后，`previewUrl` 是可打开链接，且**会过期**。

**注意**：此处 `timeout` 是整体等待预算（默认 900 秒），与 `new Makers({ timeout: 30 })` 的单请求超时含义不同。

**自定义环境**：SDK 在两种认证通道上原样透传 `env`，不转换大小写，也不去除首尾空白。空字符串、纯空白和非字符串会在打包或请求之前抛出 `ValidationError`。只有精确的 `"Preview"` 会执行已有生产部署的前置检查。自定义环境应提前创建；`deploy` 不会创建或检查环境资源，后端错误仍按 SDK 原有规则处理。公开接口文档目前只列出 Production / Preview；自定义 Slug 透传属于 SDK 扩展，不保证后端支持、环境隔离或页面可访问。

```ts
// Staging must already exist. This does not create an environment or set variables.
const deployment = await makers.deployments.deploy({
  projectId,
  env: "Staging",
  artifact: { files: { "index.html": "<h1>Staging</h1>" } },
});
```

**目录输入**：传入一个目录。常见输入是应用源码（项目根）、静态构建输出（该目录根上必须有 `index.html`），或预构建的 Makers/SSR 产物（本地 CLI build 后传 `./.edgeone`）。应用源码是 SDK 的常规路径：SDK 按默认忽略规则打包上传，不在本地编译，也不会自己执行 `npm run build` 或 `edgeone makers build`。Upload 项目上传后，Pages 后端可能会跑 `edgeone makers build`。SDK 不会跑框架适配器。默认忽略是固定名单，不读 `.gitignore`。调用者提供的 `archive` 原样上传。

**目录安全**：拒绝符号链接；自动忽略 `.git`、`node_modules`、artifact 根下的 `.edgeone` 目录、`.env`、日志、临时文件和系统文件。非 `.edgeone` directory 还会忽略任一以 `.` 开头的路径段；`.well-known` 会保留。更深路径中的 `.edgeone` 会保留。传入名为 `.edgeone` 的目录时按 CLI 布局打包（`<父目录名>/.edgeone/...`，上一级有 `edgeone.json` 则一并打入），并保留嵌套 `node_modules` 与隐藏文件。其它 directory 仍在任意深度丢掉 `node_modules`。默认忽略是固定名单，不是读 `.gitignore`。`excludePatterns` 追加 POSIX glob，不支持取反 (`!`)。

### `deployments.wait(options)`

轮询至终态：`Success`、`Failed`、`Timeout`、`Cancelled`、`Invalid`。到达 `Success` 后，`previewUrl` 是可打开站点 URL（默认上传项目会签名），且**会过期**。`get` / `list` / `wait: false` 不会签名。

```ts
const result = await makers.deployments.wait({
  projectId,
  deploymentId,
  timeout: 900,
  pollInterval: 5,
  onStatusChange: (event) => console.log(event.deployment.status),
});
```

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | :-: | --- |
| `projectId` | `string` | — | 必填 |
| `deploymentId` | `string` | — | 必填 |
| `timeout` | `number` | `900` | 超时抛 `DeploymentTimeoutError`；**不会取消线上部署** |
| `pollInterval` | `number` | `5` |  |
| `onStatusChange` | 回调 | — |  |

### `deployments.list(options)`

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | :-: | --- |
| `projectId` | `string` | — | 必填 |
| `status` | `string[]` | — | 按状态过滤 |
| `timeRange` | `{ start, end }` | — | ISO 8601 |
| `repoBranch` | `string[]` | — | 按分支过滤 |
| `page` | `number` | `0` |  |
| `pageSize` | `number` | `20` |  |
| `order` | `{ field, direction }` | — |  |

### `deployments.listAll(options)`

与 `list` 同参数但无 `page`。返回 `AsyncIterable<Deployment>`。

### `deployments.get({ projectId, deploymentId })`

查询单个部署。不存在时抛 `NotFoundError`。

### `deployments.getLog({ projectId, deploymentId })`

返回 `{ logUrl }` —— 构建日志地址。

* * *

## 自定义域名

自定义域名的创建、查询、删除按 `Makers` 的通道调用。传入 `credentials` 时走云 API 通道：创建为 `CreatePagesResources`，`Interface` 为 `pages:CreatePagesZoneCustomDomain`；查询为 `DescribePagesResources`，`Interface` 为 `pages:DescribePagesZoneCustomDomains`；删除为 `DeletePagesResources`，`Interface` 为 `pages:DeletePagesProjectCustomDomains`。只传 `token` 时走 Pages API。两条通道的参数与返回结果相同。归属校验、CNAME 检查、免费证书的申请和检查，以及证书查询与下发是直接 teo Action，始终需要 `credentials`。

`credentials` 可选。传入时 `secretId` 与 `secretKey` 都必须是非空字符串，并且必须提供 `zoneId`，否则构造时抛出 `ValidationError`。凭证对象不接受 STS Token，云 API 请求也不发送 `X-TC-Token`。未传凭证时，`verifyOwnership`、`checkCname`、`applyFreeCertificate`、`checkFreeCertificate`、`listHostCertificates` 与 `modifyCertificate` 在发请求前抛出 `ValidationError`。

`checkCname`、`applyFreeCertificate`、`checkFreeCertificate`、`listHostCertificates` 与 `modifyCertificate` 的 `zoneId` 可选。未传或为 `null` 时，把构造时的 `zoneId` 作为 `ZoneId` 发送；传入时发送调用值，用于项目属于其他 Makers 站点的域名。该站点是域名所属项目的 Makers 站点。

`projectId`、`domain`、`domains`、`zoneId`、`recordNames` 或 `hosts` 为空时，在发请求前抛出 `ValidationError`。`list`、`verifyOwnership`、`checkCname`、`checkFreeCertificate` 与 `listHostCertificates` 按查询重试。`create`、`delete`、`applyFreeCertificate` 与 `modifyCertificate` 不重试。

### `domains.create({ projectId, domain })`

调用 `CreatePagesZoneCustomDomain`。只返回 `{ domain, id }`。`id` 取响应 `Id`；`Id` 缺失或不是字符串时为 `null`。`Domain` 缺失或不是字符串时抛出 `MakersError`。空字符串仍作为 `domain` 返回。不返回查询字段，也不会为补齐字段再发请求。

### `domains.list({ projectId, domain? })`

调用 `DescribePagesZoneCustomDomains`。`projectId` 必填。`domain` 可选，只有传入时才发送 `Domain`。返回 `PagesDomains` 数组。每一行含 `type`、`domain`、`forceRedirectHttps`、`redirectStatusCode`、`currentCname`、`mainDomain`、`status`、`ownershipVerification`、`cname`、`area`、`zoneId`。传入 `domain` 时只保留该域名的行。业务参数不带 `ZoneId`；云 API 通道由四大接口在请求体顶层带上 Makers 站点 `ZoneId`。字符串在值为 `null`、缺失或不是字符串时为 `null`，空字符串保持空字符串。`redirectStatusCode` 在缺失、`null` 或不是有限数字时为 `null`，数字 `0` 保持 `0`。响应含 `ForceRedirect` 对象时，`forceRedirectHttps` 取 `Switch`，`redirectStatusCode` 取 `RedirectStatusCode`，不再读取平铺的 `ForceRedirectHTTPS` 与 `RedirectStatusCode`。没有该对象时仍读取平铺字段。校验子对象为 `null` 时保持 `null`；子对象存在时，其中的字符串按同样的空值规则。没有 `PagesDomains`、但顶层有 `Domain` 字符串时，视为一行。

### `domains.delete({ projectId, domains })`

调用 `DeletePagesProjectCustomDomains`。`domains` 至少包含一个非空域名，以 `CustomDomains` 发送。成功时没有业务字段。

### `domains.verifyOwnership({ domain })`

调用 `VerifyOwnership`，发往 `https://teo.tencentcloudapi.com`（服务 `teo`，版本 `2022-09-01`）。请求体只有 `{ Domain }`。不发送 `X-TC-Region` 或 `X-TC-Token`。`region: "global"` 仍使用这个主机。

返回 `{ status, message }`。`status` 为 `success` 或 `fail`，校验没通过也是业务结果。`message` 优先取 `Message`，没有则取 `Result`；成功时可以为空。结果不含 `requestId`。

HTTP 200 且响应含 `Response.Error`，以及非 2xx，都会抛错。`AuthFailure` 开头为 `AuthError`，包含 `Invalid` 为 `ValidationError`，包含 `NotFound` 为 `NotFoundError`，`RequestLimitExceeded` 为 `RateLimitError`，其余为 `MakersError`。`requestId` 只出现在异常上。

### `domains.checkCname({ zoneId?, recordNames })`

调用 `CheckCnameStatus`，发往 `https://teo.tencentcloudapi.com`（服务 `teo`，版本 `2022-09-01`）。请求体为 `{ ZoneId, RecordNames }`。`recordNames` 至少包含一个非空域名。不发送 `X-TC-Region` 或 `X-TC-Token`。`region: "global"` 仍使用这个主机。

返回 `{ cnameStatus }`。每一项为 `{ recordName, cname, status }`。`status` 按后端原值返回，不改写。`active`、`moved`、`invalid` 保持原样。结果不含 `requestId`。云 API 失败与 `verifyOwnership` 使用同一套错误映射。

### `domains.applyFreeCertificate({ zoneId?, domain, verificationMethod })`

调用 `ApplyFreeCertificate`，发往 `https://teo.tencentcloudapi.com`（服务 `teo`，版本 `2022-09-01`）。请求体为 `{ ZoneId, Domain, VerificationMethod }`。`verificationMethod` 只能是 `dns_challenge` 或 `http_challenge`，其他值在发请求前抛出 `ValidationError`。不发送 `X-TC-Region` 或 `X-TC-Token`。`region: "global"` 仍使用这个主机。该方法不重试。

返回 `{ dnsVerification, fileVerification }`。字段与查询自定义域名时的 DNS、文件校验子对象相同。缺哪一个，该字段就为 `null`。结果不含 `requestId`。

### `domains.checkFreeCertificate({ zoneId?, domain })`

调用 `CheckFreeCertificateVerification`，发往 `https://teo.tencentcloudapi.com`（服务 `teo`，版本 `2022-09-01`）。请求体为 `{ ZoneId, Domain }`。不发送 `X-TC-Region` 或 `X-TC-Token`。`region: "global"` 仍使用这个主机。该方法按查询重试。

返回 `{ commonName, expireTime, signatureAlgorithm }`。`expireTime` 保持后端字符串。结果不含 `requestId`。`OperationDenied.FreeCertificateVerificationIsEmpty` 抛出 `MakersError`。其他云 API 失败与 `verifyOwnership` 使用同一套错误映射。

### `domains.listHostCertificates(options?)`

调用 `DescribeHostCertificates`，发往 `https://teo.tencentcloudapi.com`（服务 `teo`，版本 `2022-09-01`）。`options` 为 `{ zoneId?, filters? }`，可以省略。请求体为 `{ ZoneId }`，传入 `filters` 时再带 `Filters`。每一项为 `{ name, values }`，发送为 `{ Name, Values }`。只有调用方传入 `fuzzy` 时才发送 `Fuzzy`。该方法用 `Offset` 与 `Limit` `100` 读取全部分页并返回所有行，不接受 page 或 offset。不发送 `X-TC-Region` 或 `X-TC-Token`。`region: "global"` 仍使用这个主机。该方法按查询重试。

返回 `{ certificates, totalCount }`。每个证书为 `{ host, mode, hostCertInfo }`。`hostCertInfo` 的每一项为 `{ alias, certId, deployTime, expireTime, signAlgo, status, type }`。`mode` 按后端原值返回，`disable` 不会改写成 `disabled`。不返回 `ClientCertInfo` 与 `UpstreamCertInfo`。结果不含 `requestId`。

### `domains.modifyCertificate({ zoneId?, hosts, mode })`

调用 `ModifyHostsCertificate`，发往 `https://teo.tencentcloudapi.com`（服务 `teo`，版本 `2022-09-01`）。请求体为 `{ ZoneId, Hosts, Mode }`。`hosts` 至少包含一个非空域名。`mode` 只能是 `disable`、`eofreecert` 或 `eofreecert_manual`，其他值在发请求前抛出 `ValidationError`。不发送 `ServerCertInfo`、`X-TC-Region` 或 `X-TC-Token`。`region: "global"` 仍使用这个主机。该方法不重试。

成功时没有业务字段。

* * *

## 数据模型

### Project

| 字段 | 类型 | 必有 | 说明 |
| --- | --- | :-: | --- |
| `projectId` | `string` | 是 |  |
| `name` | `string` | 是 | 同一账号下唯一 |
| `status` | `string` | 是 |  |
| `area` | `ProjectArea` | 否 | `"mainland"` / `"overseas"` / `"global"` |
| `presetDomain` | `string` | 否 | Production 域名；刚建的项目可能还没分配 |
| `createdOn` | `string` | 是 | ISO 8601 |
| `modifiedOn` | `string` | 是 | ISO 8601 |

### Deployment

| 字段 | 类型 | 必有 | 说明 |
| --- | --- | :-: | --- |
| `deploymentId` | `string` | 是 |  |
| `projectId` | `string` | 是 |  |
| `env` | `string` | 是 | 后端返回的环境 Slug，原样保留 |
| `status` | `string` | 否 | 终态：`Success` / `Failed` / `Timeout` / `Cancelled` / `Invalid` |
| `previewUrl` | `string` | 否 | wait Success 后为可打开站点 URL（会过期）。`get` / `wait: false` 可能仍是后端原始 URL，不可直接打开。 |
| `code` | `number` | 否 | 构建退出码，`0` 表示成功 |
| `createdOn` | `string` | 否 | `wait: false` 时不返回 |
| `modifiedOn` | `string` | 否 | `wait: false` 时不返回 |

### Token

| 字段 | 类型 | 必有 | 说明 |
| --- | --- | :-: | --- |
| `token` | `string` | 是 | 租户 API token；用来构造用户侧 `Makers` |
| `tokenId` | `string` | 是 | Token 标识 |
| `expired` | `number` | 是 | 后端过期时间（unix 秒） |

### EnvVar

| 字段 | 类型 | 必有 |
| --- | --- | :-: |
| `key` | `string` | 是 |
| `value` | `string` | 是 |
| `comment` | `string` | 否 |

### 回调

**UploadProgressEvent**：`{ uploadedBytes, totalBytes, completedFiles, totalFiles }`

**StatusChangeEvent**：`{ deployment: Deployment, previousStatus: string | null }`

* * *

## 错误处理

所有错误继承 `MakersError`，公开字段：`code`、`message`、`requestId`、`httpStatus`、`cause`。

| 异常类 | 触发条件 |
| --- | --- |
| `AuthError` | Code 105 或 HTTP 401 |
| `ValidationError` | Code 含 "Invalid" 或 HTTP 400 |
| `NotFoundError` | Code 含 "NotFound" 或 HTTP 404 |
| `ConflictError` | Code 含 "Conflict" 或 HTTP 409 |
| `RateLimitError` | Code 110 或 HTTP 429 |
| `UploadError` | COS 上传失败 |
| `TimeoutError` | 请求超时 |
| `DeploymentTimeoutError` | 等待部署终态超时（继承 `TimeoutError`） |

```ts
import { MakersError, NotFoundError, DeploymentTimeoutError } from "@edgeone/makers-sdk";

try {
  await makers.projects.get({ projectId: "missing" });
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error(error.code, error.requestId, error.httpStatus);
  }
}
```

### 重试策略

查询类请求遇到网络错误、HTTP 408/429/5xx 或外层 `Code: 110` 时最多重试 `retries` 次（默认 3）。退避策略为全抖动指数退避、上限 10 秒，优先读 `Retry-After` 头。创建、更新和 COS 凭证请求不重试。

* * *

## 开发

```sh
npm install
npm run build
npm test
```

## 常见问题

### @edgeone/makers-sdk 是什么？

联合国 \| 页:1

### 如何安装 @edgeone/makers-sdk？

在项目目录运行 npm install @edgeone/makers-sdk，或使用 pnpm add / yarn add / bun add。

### @edgeone/makers-sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @edgeone/makers-sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @edgeone/makers-sdk 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @edgeone/makers-sdk 可以商用吗？

@edgeone/makers-sdk 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @edgeone/makers-sdk 的下载量有多少？

根据 npm 官方下载统计，@edgeone/makers-sdk 在 2026/10/13 – 2026/11/11 这 30 天内被下载 4,760 次，日均约 159 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @edgeone/makers-sdk 还在维护吗？

最新版本 0\.2\.0 发布于 2026/11/11（2 天前）；至今共发布 9 个版本，其中过去 12 个月发布了 9 个；首次发布于 2026/08/17。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

