@cosmoner/sdk

中文文档
1.20.1发布于 2026/11/09MITTypeScriptESM + CJS构建来源已验证近 30 天下载 2174

Cosmoner SDK 用于节点.js

中文使用文档
文档目录19 节
  1. 安装
  2. 使用量
  3. 配置
  4. 检索
  5. 电子邮件
  6. client.email.send(params)
  7. 应用程序
  8. client.apps.deploy(appId, params?)
  9. client.apps.waitForDeployment(appId, deploymentId, params?)
  10. client.apps.listDeployments(appId, params?) 财务报告和审定财务报表 cli…
  11. 托管
  12. 站点图像、 PHP 版本和电子邮件
  13. 资源命名空间
  14. 订购已付资源
  15. 秘诀
  16. 变量
  17. 部署文件
  18. validateDeployment(source, options?)
  19. 处理错误

@ cosmoner/ sdk (英语)

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

安装

npm install @cosmoner/sdk

使用量

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: "[email protected]",
  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: ["[email protected]", "[email protected]"],
  subject: "Team Update",
  text: "Plain text email body",
  replyTo: "[email protected]",
});

配置

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

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

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 拒绝一个混合它们的呼叫,客户端拒绝一个 发送任何消息之前的运行时间 .

参数类型需求说明
credentialIdstring对SMTP 证书标识
tostring | string[]对接收者,最多50人
subjectstring对*电子邮件主题行
htmlstring无*HTML 正文
textstring无*纯文本正文
templateEmailTemplateRef无*要发送的已存储模板
replyTostring | string[]没有对地址的答复,最多5次
projectIdstring没有覆盖客户端级项目

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

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

const { data } = await client.email.send({
  credentialId: "your-credential-id",
  to: "[email protected]",
  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.

应用程序

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。 。 。 .

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

过 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。 。 。 .

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。 。 。 .

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。 。 。 .

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。 。 。 .

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。 。 。 .

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 的方法拒绝空的 .

命名空间读取变动
projectslist(), (中文(简体) ), get(project) ——由id或shult制作update(project, { name }), (中文(简体) ), delete(project)
appsget(appId), (中文(简体) ), logs(appId, { type: "BUILD" | "RUN" }),同时 list() 和部署方法update(appId, changes), (中文(简体) ), delete(appId)
serverslist(), (中文(简体) ), get(serverId) - 添加安装的 sshKeysupdate(serverId, { name }), (中文(简体) ), powerOn(serverId), (中文(简体) ), powerOff(serverId), (中文(简体) ), reboot(serverId), (中文(简体) ), delete(serverId)
sshKeyslist()create({ name, publicKey }), (中文(简体) ), delete(sshKeyId)
databaseslist() (每一种), listDedicated(), (中文(简体) ), getDedicated(databaseId), (中文(简体) ), listShared(), (中文(简体) ), getShared(tenantId)rotateSharedPassword(tenantId), (中文(简体) ), deleteDedicated(databaseId), (中文(简体) ), deleteShared(tenantId)
redislist(), (中文(简体) ), get(redisId)delete(redisId)
domainslist(), (中文(简体) ), get(domain) - 通过身份或名称,例如: example.comcreate(name), (中文(简体) ), verify(domain), (中文(简体) ), delete(domain)
bucketslist() - 没有读到单纸盒delete(bucketId)
registrieslist(), (中文(简体) ), get(registryId)delete(registryId)
iamlist(), (中文(简体) ), get(iamUserName) - 名单保持 credentials + 部分失败 errorsdelete(iamUserName)
memberslist() - 成员和待发邀请
emaillistDomains(), (中文(简体) ), 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 改变一切.

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 改变一切.

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密钥或网络调用.

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或一个承诺前钩, 不安装任何东西:

npx @cosmoner/cli validate --strict

validateDeployment(source, options?)

参数类型
sourcestring文件是写成的——原始文本,而不是解析对象.
options.strictboolean将警告视为错误 。 默认为 false。 。 。 .

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

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

处理错误

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

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
  }
}
错误抛出
ValidationError400,422 (中文(简体) )
AuthenticationError401 (中文(简体) )
InsufficientScopeError403 — API 密钥缺少 a resource:action
NotFoundError404 (韩语)
ConflictError409个
RateLimitError429个
ServerError5 (简体中文)
CosmonerTimeoutError超过请求 timeout
CosmonerConnectionErrorDNS, TCP, TLS 或套接字失败

版本与发布记录

@cosmoner/sdk 共发布 25 个版本,首次发布于 2026/09/16;过去 12 个月发布了 25 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.20.1latest2026/11/092 天前
1.20.02026/11/074 天前
1.19.12026/11/047 天前
1.19.02026/11/038 天前
1.18.12026/10/3011 天前
1.18.02026/10/2814 天前

关于 @cosmoner/sdk 的常见问题

@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 和提交记录判断。