# @cosmoner/sdk 中文文档

规范地址：https://npmcn.com/package/%40cosmoner/sdk/

版本：1\.20\.1
npm 元数据更新时间：2026-11-09T11:19:15.684Z
中文译文更新时间：2026-11-11T12:14:44.242Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40cosmoner/sdk
原文对照：https://npmcn.com/package/%40cosmoner/sdk/?lang=original
项目主页：https://cosmoner.com/docs/sdk
源码仓库：https://github.com/datablock-dev/cosmoner-sdk

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

- 包体积（解压后）：323 kB，34 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：1 个
- 首次发布：2026-09-15T17:58:17.085Z
- 最新版本发布：2026-11-09T11:19:15.344Z
- 近 30 天下载量：2174（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：25（近 12 个月 25 个）

## 中文简介

Cosmoner SDK 用于节点.js

## 安装

```sh
npm install @cosmoner/sdk
```

也可以使用：`pnpm add @cosmoner/sdk`、`yarn add @cosmoner/sdk`、`bun add @cosmoner/sdk`

## README

# @ cosmoner/ sdk (英语)

官方节点.js SDK [宇宙纳](https://cosmoner.com/), 云台( 云台) 用于应用程序、数据库、存储、域和电子邮件。 完整文档于 [https://cosmoner.com/docs/sdk 页面存档备份,存于互联网档案馆](https://cosmoner.com/docs/sdk)。 。 。 .

## 安装

```bash
npm install @cosmoner/sdk
```

## 使用量

```typescript
import { Cosmoner } from "@cosmoner/sdk";

const client = new Cosmoner({
  apiKey: "your-api-key",
  projectId: "your-project-id",
});

// Send to a single recipient
const result = await client.email.send({
  credentialId: "your-credential-id",
  to: "recipient@example.com",
  subject: "Hello from Cosmoner",
  html: "<h1>Hello!</h1><p>This is a test email.</p>",
});

console.log(result.data.messageId);

// Send to multiple recipients (up to 50)
await client.email.send({
  credentialId: "your-credential-id",
  to: ["alice@example.com", "bob@example.com"],
  subject: "Team Update",
  text: "Plain text email body",
  replyTo: "noreply@yourdomain.com",
});
```

## 配置

| 选项 | 需求 | 默认 | 说明 |
| --- | --- | --- | --- |
| `apiKey` | 对 | —— 说 | 您的宇宙纳 API 密钥 |
| `projectId` | 没有 | —— 说 | 默认工程; 也可以通过每个调用 |
| `baseUrl` | 没有 | `https://api.cosmoner.com` | API 基础 URL 覆盖 |
| `timeout` | 没有 | `30000` | 以毫秒计的请求超时 |
| `maxRetries` | 没有 | `2` | 暂时性故障的检索(见 [检索](https://npmcn.com/package/%40cosmoner/sdk/#readme-retries)(中文(简体) ) |

`projectId` 是可选的,所以一个客户端可以跨越项目。 每个电话传到 覆盖默认 :

```typescript
const client = new Cosmoner({ apiKey: "your-api-key" });

await client.email.send({ projectId: "proj-2", credentialId: "...", to: "...", subject: "...", text: "..." });
```

## 检索

瞬间故障会自动重试, 并有指数回放和满载 抖动, 荣誉 `Retry-After` 当API发送它.

-   **429个** 总是重审——请求在受理之前被驳回, 所以重放它不能重复副作用.
-   **暂停和5xx** 重审时只考虑独能方法。 例如: `email.send` 实值 _没有_ 重放, 因为 API 可能已完成发送 在无法回答之前.

设定 `maxRetries: 0` 以完全禁用重试.

## 电子邮件

### `client.email.send(params)`

使用原始内容(a) `subject` 与 `html`, (中文(简体) ), `text` 或两者兼有——或 存储 `template`提供所有三个。 两种形式是工会类型,所以 TypeScript 拒绝一个混合它们的呼叫,客户端拒绝一个 发送任何消息之前的运行时间 .

| 参数 | 类型 | 需求 | 说明 |
| --- | --- | --- | --- |
| `credentialId` | `string` | 对 | SMTP 证书标识 |
| `to` | `string | string[]` | 对 | 接收者,最多50人 |
| `subject` | `string` | 对\* | 电子邮件主题行 |
| `html` | `string` | 无\* | HTML 正文 |
| `text` | `string` | 无\* | 纯文本正文 |
| `template` | `EmailTemplateRef` | 无\* | 要发送的已存储模板 |
| `replyTo` | `string | string[]` | 没有 | 对地址的答复,最多5次 |
| `projectId` | `string` | 没有 | 覆盖客户端级项目 |

\* 通过 `subject` 至少有一个 `html` 或者说 `text`,或通过 `template` 和没有三个.

`template` 实值 `{ id, version?, variables? }`数字 : `id` 是模板的 id 或它的 别名, `version` 正整数( 已公布的版本被忽略时),以及 `variables` 模板宣布的变量值。 回应 名称 `data.template` (单位:千美元)`id`, (中文(简体) ), `alias`, (中文(简体) ), `version`) (中文(简体) ).

```typescript
const { data } = await client.email.send({
  credentialId: "your-credential-id",
  to: "recipient@example.com",
  template: { id: "welcome", variables: { name: "Ada" } },
});

console.log(data.messageId, data.template?.version);
```

发送模板需要使用 API 密钥 `email-templates:read` 以及 `email:write`。API对未知模板或版本的404进行回答,409 (单位:千美元)`EMAIL_TEMPLATE_NOT_PUBLISHED`当没有出版版本时,422 变量不匹配模板声明或渲染点击的 限制,当模板短暂无法使用时为503.

## 应用程序

```ts
const { data: apps } = await client.apps.list();
const web = apps.find((app) => app.name === "web")!;

const { data: started } = await client.apps.deploy(web.id, { tag: "v2" });
const finished = await client.apps.waitForDeployment(web.id, started.id);

if (finished.phase !== "ACTIVE") throw new Error(finished.error ?? finished.phase);
```

### `client.apps.deploy(appId, params?)`

开始部署图像应用程序——从宇宙器注册处运行图像的应用程序 ——不等待就返回部署. 从寄存器中构建的应用程序是 拒绝。 需要一个 API 密钥, 使用 `apps:write`。 。 。 .

| 参数 | 类型 | 需求 | 说明 |
| --- | --- | --- | --- |
| `tag` | `string` | 没有 | 要从应用程序仓库部署的标记 |
| `digest` | `string` | 没有 | 准确的图像, `sha256:<64 hex characters>` |
| `projectId` | `string` | 没有 | 覆盖客户端级项目 |

过 `tag` 或者说 `digest`而不是两个。 没有, 应用程序的图像已经命名 被再次拉动.

### `client.apps.waitForDeployment(appId, deploymentId, params?)`

投票直到部署结束,并在最后阶段解决—— `ACTIVE`, (中文(简体) ), `ERROR`, (中文(简体) ), `CANCELED` 或者说 `SUPERSEDED`。它只拒绝请求 失败或 `timeout` (默认10分)通过;部署继续进行 就是这样. `interval` 默认为3秒,以及 `onPoll` 每一个结果都要求. 需求 `apps:read`。 。 。 .

`client.apps.list()` 财务报告和审定财务报表 `client.apps.getDeployment(appId, deploymentId)` 为: 里面的单个电话.

### `client.apps.listDeployments(appId, params?)` 财务报告和审定财务报表 `client.apps.rollback(appId, deploymentId, params?)`

`listDeployments` 返回应用程序的最新二十个部署,最新 先说 每个都带着 `current`,无论它的图像是应用程序运行的 现在,和 `rollbackable`,是否应用程序可以回到它。 需求 `apps:read`。 。 。 .

`rollback` 把应用程序放回图像上 一个较早的部署运行,没有 ,然后不等待地返回新的部署。 对应用程序有用 由寄存器和图像应用程序所建。 仅图像返回: app 保留当前环境变量和设置。 需求 `apps:write`。 。 。 .

```ts
const { data: history } = await client.apps.listDeployments(appId);
const previous = history.find((d) => d.rollbackable);
if (previous) {
  const { data: rollback } = await client.apps.rollback(appId, previous.id);
  await client.apps.waitForDeployment(appId, rollback.id);
}
```

## 托管

读取一个项目的共享托管站点以及在哪里可以到达其文档. 需要一个 API 密钥为 `hosting:read`。 。 。 .

```ts
const { data: sites } = await client.hosting.list();
const { data: access } = await client.hosting.access(sites[0].id);
// access.username, access.host, access.sftp.port, access.ssh.{port,enabled}
```

`client.hosting.get(siteId, { credentials: true })` 并返回网站的 `sftpPassword`它不需要超出 `hosting:read`,所以注意钥匙 相应.

### 站点图像、 PHP 版本和电子邮件

一个站点运行一个精确的 站点图像的构建 并且永远不会改变 除了对关键或已知被开发出的脆弱性的补救之外 后夜申请 `forcedUpdate.deadline`。 。 。 .

```ts
const { data: image } = await client.hosting.image(siteId);
// image.current.tag, image.update (null when up to date), image.forcedUpdate,
// image.rollback (with `until`), image.php.{version, securitySupportEnds, offered}

if (image.update) await client.hosting.updateImage(siteId);
await client.hosting.rollbackImage(siteId); // within 7 days of the last change
await client.hosting.setPhpVersion(siteId, { phpVersion: image.php.offered[0] });
```

`updateImage`, (中文(简体) ), `rollbackImage` 财务报告和审定财务报表 `setPhpVersion` 在网站开始前重新启动 他们回答,然后每个人都返回图像状态之后。 只有版本在 `php.offered` 被接受; API 拥有该列表, 因此 SDK 不检查 这个 他们需要 `hosting:write`。 。 。 .

```ts
const { data: mail } = await client.hosting.mail(siteId);
// mail.enabled, mail.fromAddress, mail.domains — the verified email domains it can send from
await client.hosting.enableMail(siteId, { emailDomainId: mail.domains[0].id, localPart: "wordpress" });
await client.hosting.disableMail(siteId);
```

`enableMail` 让网站的 PHP `mail()` 发送为 `<localPart>@<domain>` 通过新的 SMTP 证书, 并且它发送的每封邮件都会在 项目的电子邮件计划. `enableMail` 财务报告和审定财务报表 `disableMail` 需求 `email:write` 还有 作为 `hosting:write`。 。 。 .

```ts
const { data: domains } = await client.hosting.domains(siteId);
const { data: attached } = await client.hosting.attachDomain(siteId, { hostname: "shop.example.com" });
// attached.dnsRecordsCreated — true when Cosmoner runs the domain's DNS and added attached.instructions
await client.hosting.updateDomain(siteId, attached.domainId, { kind: "REDIRECT", redirectTarget: "https://example.com" });
await client.hosting.detachDomain(siteId, attached.domainId);
```

`attachDomain` 取一个工程的已验证域,或一个子域 1;其他任何用422来拒绝. 一个网站从 `<hostname>/public_html` 除非 `path` 命名另一个文件夹. `updateDomain` 替换整个行为,所以 `{ kind: "SITE" }` 一个人把它移回它的 默认文件夹 。 这些需要 `hosting:write`· ; `domains` 需求 `hosting:read`。 。 。 .

## 资源命名空间

每一个都使用一种资源,并返回API信封. 每一个方法 `{ projectId }` 以覆盖客户端默认值,但 `projects`,它读取整个账户,从未使用默认项目. 一种在发送任何信息之前, 使用 ID 的方法拒绝空的 .

| 命名空间 | 读取 | 变动 |
| --- | --- | --- |
| `projects` | `list()`, (中文(简体) ), `get(project)` ——由id或shult制作 | `update(project, { name })`, (中文(简体) ), `delete(project)` |
| `apps` | `get(appId)`, (中文(简体) ), `logs(appId, { type: "BUILD" | "RUN" })`,同时 `list()` 和部署方法 | `update(appId, changes)`, (中文(简体) ), `delete(appId)` |
| `servers` | `list()`, (中文(简体) ), `get(serverId)` - 添加安装的 `sshKeys` | `update(serverId, { name })`, (中文(简体) ), `powerOn(serverId)`, (中文(简体) ), `powerOff(serverId)`, (中文(简体) ), `reboot(serverId)`, (中文(简体) ), `delete(serverId)` |
| `sshKeys` | `list()` | `create({ name, publicKey })`, (中文(简体) ), `delete(sshKeyId)` |
| `databases` | `list()` (每一种), `listDedicated()`, (中文(简体) ), `getDedicated(databaseId)`, (中文(简体) ), `listShared()`, (中文(简体) ), `getShared(tenantId)` | `rotateSharedPassword(tenantId)`, (中文(简体) ), `deleteDedicated(databaseId)`, (中文(简体) ), `deleteShared(tenantId)` |
| `redis` | `list()`, (中文(简体) ), `get(redisId)` | `delete(redisId)` |
| `domains` | `list()`, (中文(简体) ), `get(domain)` - 通过身份或名称,例如: `example.com` | `create(name)`, (中文(简体) ), `verify(domain)`, (中文(简体) ), `delete(domain)` |
| `buckets` | `list()` - 没有读到单纸盒 | `delete(bucketId)` |
| `registries` | `list()`, (中文(简体) ), `get(registryId)` | `delete(registryId)` |
| `iam` | `list()`, (中文(简体) ), `get(iamUserName)` - 名单保持 `credentials` + 部分失败 `errors` | `delete(iamUserName)` |
| `members` | `list()` - 成员和待发邀请 |  |
| `email` | `listDomains()`, (中文(简体) ), `getDomain(emailDomainId)`, (中文(简体) ), `limits()`,同时 `send()` | `createDomain({ domainId })`, (中文(简体) ), `createExternalDomain({ domainName })`, (中文(简体) ), `verifyDomain(emailDomainId)`, (中文(简体) ), `deleteDomain(emailDomainId)` |

其中两个返回一个证书, 每个只需要名称空间读取 范围,所以要守护带它的密钥: `databases.getDedicated` 总是包括 `connectionUri`,与密码完全连接 URI,以及 `redis.get` 总是包括正文 `password`。 。 。 .

### 订购已付资源

`servers.create`, (中文(简体) ), `redis.create`, (中文(简体) ), `databases.createDedicated`, (中文(简体) ), `buckets.create`, (中文(简体) ), `registries.create`, (中文(简体) ), `hosting.create`, (中文(简体) ), `apps.create` 财务报告和审定财务报表 `apps.resize` 立即向保存的卡片收费,但被拒绝 402 在项目无法计费时,任何事物都会被创建. 每个都有 `preview` (单位:千美元)`previewDedicated`, (中文(简体) ), `resizePreview`)首先返回价格: `monthly` 准确的说, `dueToday` 项目一旦有估计数 订阅. `client.catalog` 列出要排序的大小、计划和区域 和,而且不需要项目。 一个应用程序在两个电话中订购: `createDraft` 免费保存它应该是什么 `create` 把草案变成账单 应用.

删除为永久的,删除已计费资源停止计费. `apps.update` 仅免费更改设置;它留下环境变量 到部署文件. `domains.create` 添加您已经拥有的域: 发布 返回时 `verificationRecord`,然后调用 `verify`删除 SSH 密钥 放在服务器上,它已经安装在.. `stillAuthorisedOn` 计数 他们.

电源动作是同步的: 服务器读取 `PROVISIONING` 直至 动作就解决了 所以再读一遍看看结果. `powerOff` 切断电源 完全,一个停止的服务器仍然收费. `projects.delete` 被拒绝 与409, 而项目仍然持有资源. `email.createDomain` 设置 已经包含在项目中的域, `createExternalDomain` dNS 主机主机 其它位置; 发布返回 `dnsRecords`,然后调用 `verifyDomain`,其 第一次成功将项目的电子邮件计划放在订阅上. `databases.rotateSharedPassword` 返回此新密码一次,然后 老家伙马上停止工作.

## 秘诀

存储部署文件提到的值 `from_secret`。需要一个 API 键为 `secrets:read`,以及 `secrets:write` 改变一切.

```ts
const { data: secret } = await client.secrets.create({
  name: "DB_PASSWORD",
  value: "hunter2",
  environment: "production",
});
secret.value; // "hunter2" — returned here and nowhere else
secret.maskedValue; // "hu••••r2", safe to display

const { data: secrets } = await client.secrets.list({ environment: "production" });
// name, environment, version, who changed it and when — never the value
```

一个秘密的值在休息时被加密 并准确返回一次,通过呼叫 这下好了. `list()` 财务报告和审定财务报表 `get()` 描述一个没有价值的秘密,没有 路由解密 1,所以一个丢失的值被替换而不是被回收.

`update(id, { value })` 替换一个值并凸起 `version`· ; `delete(id)` 删除; `usage()` 报告该项目拥有和可能拥有多少秘密; `audit(id)` 读到谁改变了它和何时.

两种API行为在调试前值得知道:

-   **写作需要拥有或管理角色.** API检查关键行为的作用 项目的关键行动与服务 账户的角色,一个个人访问符 与它的持有者。 一个键上 `developer` 服务账户被拒绝,即使它携带403个 `secrets:write`。 。 。 .
-   **创建是限速的** 每个项目每10分钟100个,由 所有在项目上工作的钥匙和机器 以及一个秘密的项目 限制答案 402.

## 变量

简洁的密文兄弟, 对于不敏感的配置一个部署 参考文件为 `from_variable`需求 `variables:read`,以及 `variables:write` 改变一切.

```ts
await client.variables.create({ name: "LOG_LEVEL", value: "debug" });
const { data: variables } = await client.variables.list();
variables[0].value; // "debug" — returned in full on every read
```

这是两种资源之间的区别,而不是一种疏忽:任何东西 值得隐藏属于 `client.secrets`。 。 。 . `update()` 这里需要一个值, a 说明,或两者兼有.

## 部署文件

验证 a `.cosmoner/deployment.yaml` - 你承诺要描述的档案 仓库部署——没有API密钥或网络调用.

```typescript
import { validateDeployment } from "@cosmoner/sdk";
import { readFileSync } from "node:fs";

const { valid, issues, template } = validateDeployment(
  readFileSync(".cosmoner/deployment.yaml", "utf8")
);

for (const issue of issues) {
  console.log(`${issue.severity} ${issue.path}: ${issue.message}`);
}
```

```
error services.0.run_command: Static sites cannot define a run_command
warning services.0.prot: Unknown field "prot" — it will be ignored
```

警告是平台所容忍的 而你可能不是故意的 一个 未知键是主键 : 平台放弃它而不是拒绝 文件,所以为新字段编写的模板仍然应用其已知设置 对抗旧部署 将这里视为致命 将拒绝文件 平台接受,因此被报告为警告,说明后果. 过 `strict` 当你不愿让一个打字—— 它改变了 判决不是结果.

`template` 是平台读取的文件: 默认应用, 未知密钥 掉下来了 将它与你写的东西比起来就是重点——这是设置 这会真正到达.

在命令行上也有同样的检查, 对于CI或一个承诺前钩, 不安装任何东西:

```bash
npx @cosmoner/cli validate --strict
```

### `validateDeployment(source, options?)`

| 参数 | 类型 |  |
| --- | --- | --- |
| `source` | `string` | 文件是写成的——原始文本,而不是解析对象. |
| `options.strict` | `boolean` | 将警告视为错误 。 默认为 `false`。 。 。 . |

回返 `{ valid, issues, template }`。 。 。 . `validateDeploymentDocument(document, options?)` 是对一个已解析对象的同一检查, 对于一个文件 生成而不是从磁盘读取.

`DEPLOYMENT_FILE_PATHS` 列出平台检查的地点,按顺序排列 检查他们,和 `APP_SCHEMA_URL` 是出版的 JSON Schema 编辑器可以是 指点点.

## 处理错误

每一次失败都会投出一个子类 `CosmonerError`,这样你就可以广泛捕捉或 狭义:

```typescript
import { Cosmoner, CosmonerError, RateLimitError } from "@cosmoner/sdk";

try {
  await client.email.send({ ... });
} catch (err) {
  if (err instanceof RateLimitError) {
    console.error(err.retryAfter); // seconds, when the API supplies it
  } else if (err instanceof CosmonerError) {
    console.error(err.code);    // e.g. "INSUFFICIENT_SCOPE"
    console.error(err.status);  // e.g. 403
    console.error(err.details); // field-level validation errors, when present
    console.error(err.docsUrl); // page explaining the fix, when the API links one
    console.error(err.message); // Human-readable message
  }
}
```

| 错误 | 抛出 |
| --- | --- |
| `ValidationError` | 400,422 (中文(简体) ) |
| `AuthenticationError` | 401 (中文(简体) ) |
| `InsufficientScopeError` | 403 — API 密钥缺少 a `resource:action` |
| `NotFoundError` | 404 (韩语) |
| `ConflictError` | 409个 |
| `RateLimitError` | 429个 |
| `ServerError` | 5 (简体中文) |
| `CosmonerTimeoutError` | 超过请求 `timeout` |
| `CosmonerConnectionError` | DNS, TCP, TLS 或套接字失败 |

## 常见问题

### @cosmoner/sdk 是什么？

Cosmoner SDK 用于节点\.js

### 如何安装 @cosmoner/sdk？

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

### @cosmoner/sdk 支持 TypeScript 吗？

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

### @cosmoner/sdk 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

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

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

### @cosmoner/sdk 可以商用吗？

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

### @cosmoner/sdk 的下载量有多少？

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

### @cosmoner/sdk 还在维护吗？

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

