# @catalyst-cloud/sdk 中文文档

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

版本：0\.16\.24
npm 元数据更新时间：2026-11-06T17:58:28.689Z
中文译文更新时间：2026-11-09T04:35:39.422Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40catalyst-cloud/sdk
原文对照：https://npmcn.com/package/%40catalyst-cloud/sdk/?lang=original
项目主页：https://github.com/coalesce-labs/catalyst-cloud-sdk#readme
源码仓库：https://github.com/coalesce-labs/catalyst-cloud-sdk

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

- 包体积（解压后）：1.8 MB，159 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22
- 运行依赖：0 个
- peer 依赖：better-sqlite3 \*，@opentelemetry/api \>=1\.3\.0 \<2，@catalyst-cloud/schema 0\.16\.20，@sqlite\.org/sqlite-wasm \*，@catalyst-cloud/replicate 0\.16\.20，@catalyst-cloud/read-model ^0\.16\.0
- 首次发布：2026-06-26T00:50:13.400Z
- 最新版本发布：2026-11-06T17:58:28.299Z
- 近 30 天下载量：69551（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：106（近 12 个月 106 个）

## 中文简介

读取并更新您的催化云数据,并接收您的应用程序的活化变化.

## 安装

```sh
npm install @catalyst-cloud/sdk
```

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

## README

# @ catalyst- cloud/ sdk (中文(简体) )

构建读取和更新您催化云数据的应用程序.

GitHub 拉动请求.

安装 : `npm install @catalyst-cloud/sdk`

[SDK 文档](https://github.com/coalesce-labs/catalyst-cloud-sdk#readme)

```ts
import { createTenantClient } from "@catalyst-cloud/sdk";

const client = createTenantClient({
  baseUrl: "https://staging.catalystcloud.dev",
  auth: { kind: "bearer", getToken: async () => credentialProvider.accessToken() },
});
const result = await client.issues.get("CTC-4504");
if (result.outcome === "ok") console.log(result.issue);
```

使用您现有的证书提供者刷新符号 。 一个本地桌面进程拥有它的无记名并返回输入到网页视图中的数据 。 内部的Web BFF是 cookie- 唯一的,不是支持的SDK合同. 阅读方法保留已部署的租户合同;这一释放并不发明API研究提出的预测或分批路线.

## 进口

| 导入 | 目的 | 地方数据库 |
| --- | --- | --- |
| `@catalyst-cloud/sdk` | 输入的 HTTP 和活运输 | 无 |
| `@catalyst-cloud/sdk/http` | 只使用 HTTP 客户端 | 无 |
| `@catalyst-cloud/sdk/live` | 独立的 WebSocket 传输 | 无 |
| `@catalyst-cloud/sdk-replica-node` | 可选节点/本复制 | 原生 SQLite 语句 |
| `@catalyst-cloud/sdk-replica-browser` | 可选浏览器复制 | SQLite WASM 和 OPFS |
| `@catalyst-cloud/sdk/events` | 直接事件等待和可选节点/缓存 | 无直接等待; 本地 JSONL 文件缓存等待 |

`LiveSyncClient` 是一种换食运输。 呼叫器提供光标和再同步处理器. 导入或构建它不会打开数据库 。 消费者可以使用修改来使云视图无效;回放后流需要刷新这些视图及其光标,而不是种子 SQL. 权威域-事件流与此行种子相分离.

## 可选复制品和从0.13移出

本地同步对每个客户端都是可选的 。 没有线下浏览的承诺。 断开的缓存已停滞,无法授权写入 。 执行主机可能保留其共享的复制品,同时测量云容量. 本地桌面缓存使用与CLI和主机相同的被监管的每租户守护进程. 桌面窗口打开只读取的读取器,而不是创建第二个写取器.

```sh
# Only when local SQL is required
npm install @catalyst-cloud/sdk-replica-node
# Browser SQL also needs its explicit driver peer
npm install @catalyst-cloud/sdk-replica-browser @sqlite.org/sqlite-wasm
```

复制模组拥有了schema,复制和共享-query依赖. 节点模块使用Bun或节点22上的内置SQLite; `better-sqlite3` 是一个可选的注入驱动端。 浏览器模块分别声明其 SQLite WASM 端口. npm没有每个导出端的依赖性,因此这些是单独的可选包表,而不是假装子路径可以拥有端.

现有 `/node`, (中文(简体) ), `/browser`, (中文(简体) ), `/browser/db-worker` 财务报告和审定财务报表 `/events` 路径仍然是兼容导出。 从0.14起,这些复制路径的用户必须安装相应的可选模块或明确安装他们的SQL同位素. 新的明确 `/replica/node`, (中文(简体) ), `/replica/browser` 财务报告和审定财务报表 `/replica/browser/db-worker` 别名表示选入。 默认的 HTTP/ 活安装不需要这些同位素 。 root和HTTP声明捆绑包含被绑的有线视图类型,因此TypeScript的消费者不需要SQL包来仅仅读取一个响应.

在更新催化剂-云消费者之前,调整其图案-publish门,以验证可选的复制模块,并在任何相容复制导入的同时安装适当的模块. 目前的门只从SDK硬依赖中刻意地读取了schema pins,因此它会拒绝一个0.14只核心的接头作为未识别. 将现有的云消费者保持在0.13,直到迁移结束.

寻常 `publish.yml` 发布工作流程构建,测试并打包所有三个软件包,然后发布核心后再发布节点和浏览器模块. 重运行只当其注册完整性与已包装的文物相匹配时, 才会跳过已发布的版本 。 不同的字节需要版本的凸起. 舱载货单是.. `modules/`;它们的版本和SDK核心同位素必须匹配.

为首次公布两个新名称,将工作流程主要发送至 `bootstrap_modules` 启用和临时 `SDK_BOOTSTRAP_TOKEN` 存储器密钥 。 核心出版物仍然使用其现有的npm可信赖的出版商. 初始模块发布后,配置每个模块可信赖的发布器,用于 `coalesce-labs/catalyst-cloud-sdk`工作流程 `publish.yml`,然后去掉那个临时秘密。 后来的放出都使用GitHub OIDC,用于所有三个包. 不得将可选单元的第一份出版物误认为完成了上面的催化剂-云依赖门迁移.

其余的例子描述了可选择的迁移和可复制的生命周期.

## 覆盖范围

-   **线性** 问题、项目、周期、倡议、评论、标签、历史和关系. **成熟无边.**
-   **GitHub 图像** ——拉动请求,检查,承诺状况,审查. **造型.**
-   **知识库** ——思想/记忆. **在地图上.**

## 所需资源

催化云账户和认证符 (在浏览器中,使用同源的会话饼干取而代之——在页面中没有符号. )

```sh
npm install @catalyst-cloud/sdk
```

## 使用量

### 后端/ 代理主机

用符牌认证,并登陆每一次更改为本地的 SQLite 复制品. 在第一次运行(或者在长时间断开之后)时,SDK要求您从快照中重新播种;在此之后您只需应用每个被推动的更改.

```ts
import { Database } from "bun:sqlite"; // Node: better-sqlite3 with the same calls
import { LiveSyncClient } from "@catalyst-cloud/sdk";

const db = new Database("replica.sqlite");
db.run("CREATE TABLE IF NOT EXISTS issues (id TEXT PRIMARY KEY, row TEXT, seq INTEGER)");
db.run("CREATE TABLE IF NOT EXISTS sync_state (k TEXT PRIMARY KEY, cursor INTEGER)");

const client = new LiveSyncClient({
  baseUrl: process.env.CATALYST_CLOUD_BASE_URL!,
  accountId: "tenant-0",
  auth: { kind: "token", token: process.env.CATALYST_CLOUD_TOKEN! },

  // Full re-seed: fetch the current snapshot and rewrite the replica; resolve to the fresh cursor.
  reseed: async () => {
    const snap = (await (
      await fetch(`${process.env.CATALYST_CLOUD_BASE_URL}/snapshot?account=tenant-0`, {
        headers: { authorization: `Bearer ${process.env.CATALYST_CLOUD_TOKEN}` },
      })
    ).json()) as { cursor: number; issues: { id: string }[] };
    const tx = db.transaction(() => {
      db.run("DELETE FROM issues");
      const ins = db.prepare("INSERT INTO issues (id, row, seq) VALUES (?, ?, ?)");
      for (const row of snap.issues) ins.run(row.id, JSON.stringify(row), snap.cursor);
      db.run(
        "INSERT INTO sync_state (k, cursor) VALUES ('replica', ?) ON CONFLICT(k) DO UPDATE SET cursor = excluded.cursor",
        [snap.cursor],
      );
    });
    tx();
    return snap.cursor;
  },

  getCursor: () =>
    (db.query("SELECT cursor FROM sync_state WHERE k = 'replica'").get() as
      | { cursor: number }
      | undefined)?.cursor ?? null,

  // Apply each pushed change to your local copy.
  onChange: (frame) => {
    if (frame.op === "delete") db.run("DELETE FROM issues WHERE id = ?", [frame.entityId]);
    else
      db.run(
        "INSERT INTO issues (id, row, seq) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET row = excluded.row, seq = excluded.seq",
        [frame.entityId, JSON.stringify(frame.row), frame.seq],
      );
    db.run("UPDATE sync_state SET cursor = ? WHERE k = 'replica'", [frame.seq]);
  },
});

await client.start(); // resolves only when stop() is called — keeps the process alive between changes
```

### 浏览器

在浏览器中没有符号:同源会话 cookie认证了连接,而SDK从不将任何秘密放入URL. 存储器是你的(这里,a `Map`;在真实的应用程序中,OPFS / IndexedDB).

```ts
import { LiveSyncClient } from "@catalyst-cloud/sdk";

const issues = new Map<string, unknown>();
let cursor: number | null = null;

const client = new LiveSyncClient({
  baseUrl: "https://app.example.com/api/v1", // same origin as the page
  accountId: "tenant-0",
  auth: { kind: "cookie" },
  reseed: async () => {
    const snap = (await (await fetch("/api/v1/snapshot?account=tenant-0")).json()) as {
      cursor: number;
      issues: { id: string }[];
    };
    issues.clear();
    for (const row of snap.issues) issues.set(row.id, row);
    cursor = snap.cursor;
    return snap.cursor;
  },
  getCursor: () => cursor,
  onChange: (frame) => {
    if (frame.op === "delete") issues.delete(frame.entityId);
    else issues.set(frame.entityId, frame.row);
    cursor = frame.seq;
  },
  onStatus: (s) => setConnectionBadge(s), // "live" / "reconnecting" / …
});

void client.start(); // never await in the browser
// client.stop() on teardown
```

需要一个平台 `WebSocket` (浏览器, Bun, 或 Node 22). 在更古老的节点上注入 `wsFactory` 包起来 [`ws`](https://www.npmjs.com/package/ws) 软件包.

### 浏览器 — 管理 OPFS 复制版 (`/browser`(中文(简体) )

`BrowserReplica` 是浏览器双 `/node`是因为 `CatalystReplica`: 一个由 OPFS 所复制的 SQLite 复制品, 在一个专门的 Web Worker 中, 种子出自 `/snapshot` 在有界分流的批量中,保持相同的变换饲料的活性,并读取相同的内容 `@catalyst-cloud/read-model` 视图 —— 在您的应用程序中没有手写工人, 应用或种子代码 .

```ts
import {
  BrowserReplica,
  isBrowserReplicaSupported,
  type ReplicaStatus,
} from "@catalyst-cloud/sdk/browser";

let status: ReplicaStatus = "loading";

if (isBrowserReplicaSupported()) {
  const replica = new BrowserReplica(
    {
      onChanged: () => void refreshList(), // after the seed and after every applied delta drain
      onStatus: (s) => {
        status = s; // "loading" | "live" | "reconnecting" | "error" | "secondary" | …
        setBadge(s);
      },
    },
    {
      baseUrl: "/api/v1", // same-origin, cookie-authed; accountId optional
      // REQUIRED. The OPFS database is shared by every replica on the origin and a warm cursor skips
      // the snapshot, so without a fence a change of signed-in user serves the PREVIOUS user's rows.
      // Pass a stable per-user id from your session — e.g. the WorkOS `user.id`. It never leaves the
      // browser; it is only compared against the value stamped in the local DB, and a mismatch wipes
      // the replica and forces a re-seed.
      identity: session.user.id,
    },
  );
  // AWAIT it. start() suspends on the Web Lock before the worker exists, so a query issued against a
  // not-yet-started replica rejects with "replica client closed".
  await replica.start(); // resolves once seeded — or immediately, with status "secondary"

  // Another tab already owns the origin's replica. That is a normal outcome, not an error: this tab
  // should read via its usual fetch path and leave the local DB alone.
  if (status !== "secondary") {
    const rows = await replica.queryIssues(); // read-model IssueView[], served locally
  }
  // replica.close() on teardown — cooperatively releases the OPFS handles, then terminates the worker
}
```

`status` 上面是无论你 `onStatus` 上次记录的处理者. `start()` 如果靴子失效( 来源不良、 工人缺失、 OPFS 无法使用) —— 它不会解入破损状态, 所以 `try`编号 :`catch` 周围是向用户显示错误的地方.

内置, 因为浏览器需要它们 :

-   **单一所有者选举** (Web Locks):OPFS SAHPool是单连接的,每个来源,所以完全一个标签靴子复制;其他标签都得到状态 `"secondary"` (一个干净的状态,而不是一个错误),并且应该通过其正常的取取路径来读取.
-   **后压**:活地三角洲被组合成分批,单飞应用有边框缓冲;积压过深,无法重放而升级为新鲜快照而非生长.

**同伴依赖关系**: 安装 [`@sqlite.org/sqlite-wasm`](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm) - SDK从不捆绑那块地.

**炸弹**: 工人与 `new Worker(new URL("./db.worker.js", import.meta.url), { type: "module" })`,Vite,webpack 5 和 Rollup 检测到静态并分裂成自己的块(没有与主题相关的触动您的主捆; `/node` 消费者根本看不出来). 注释:

-   **维特**: 在框外工作 `build`在dev中,如果预捆绑会干扰工人的URL,则添加 `optimizeDeps: { exclude: ["@catalyst-cloud/sdk"] }`。 。 。 .
    
-   **网络包装 5 / 滚动**页:1 `new Worker(new URL(...))` 语法由本地支持(Rollup在配置中需要工人支持,例如:. `@surma/rollup-plugin-off-main-thread` 或等同).
    
-   **异形捆绑器**: 通过 `createWorker` 内 `BrowserReplicaOptions` 并按你的工具链来构建工人。 工人模块在专用子路径发布 **`@catalyst-cloud/sdk/browser/db-worker`** ——该说明是支持的切入点:
    
    ```ts
    // Vite
    import DbWorker from "@catalyst-cloud/sdk/browser/db-worker?worker";
    createWorker: () => new DbWorker();
    
    // webpack 5 / anything that resolves a bare specifier to a URL
    createWorker: () =>
      new Worker(
        new URL("@catalyst-cloud/sdk/browser/db-worker", import.meta.url),
        { type: "module" },
      );
    ```
    

工人是个 **副作用模块** (它登记一个邮件处理器, 并输出无), 因此它被列在包的 `sideEffects` 数组 —— 不要配置您的捆绑器来震动它, 或它会加载和注册什么, 每一次复制呼叫都会挂起 .

### 租户客户端- 输入读取和写入 HTTP

`createTenantClient` 是每个租户读写的一个执行,所以一个CLI,一个技能脚本,一个MCP工具以及你自己的代码共享,而不是每个携带一个无记名者,一个基础URL和一个响应解析器. 每一次呼吁都会解决歧视工会的问题 `outcome` ——没有为服务器的答案投出任何东西——和每个 `agent.*` 写作途径由租户自己的合同解决(`GET /api/v1/agent/contract`,在文档本身的ETag政策下读取一次并缓存一次,从不写字.

```ts
import { createTenantClient, stageIdForSlot, teamForTicket } from "@catalyst-cloud/sdk";

const client = createTenantClient({
  key: process.env.CATALYST_CLOUD_TOKEN!,        // an organization-tier key for agent.* writes
  baseUrl: "https://staging.catalystcloud.dev",
});

// Or drive it with the CTC-2111 AuthStrategy — the same shape LiveSyncClient/CatalystReplica take —
// so one credential provider serves every SDK surface. `bearer` is resolved FRESH on every request.
const clientWithAuth = createTenantClient({
  auth: { kind: "bearer", getToken: () => credentialProvider.accessToken() },
  baseUrl: "https://staging.catalystcloud.dev",
});

// A keyset-paged read: follow `nextCursor` (typed, read off X-Mirror-Next-Cursor) until it is null.
let after = undefined;
do {
  const page = await client.issues.list({ teamKey: "ENG", state: "active", limit: 100, after });
  if (page.outcome !== "ok") throw new Error(`${page.outcome}: ${"reason" in page ? page.reason : ""}`);
  for (const row of page.rows) console.log(row.identifier, row.title);
  after = page.nextCursor ?? undefined;
} while (after);

// A write as the app actor. The state id comes from the contract by SLOT, never by a display name.
const contract = await client.contract();
if (contract.outcome !== "ok") throw new Error(`contract: ${contract.outcome}`);
const team = teamForTicket(contract.doc, "ENG-12");
const stage = team.outcome === "ok" ? stageIdForSlot(team.team, "research") : team;
if (stage.outcome !== "ok") throw new Error(stage.outcome);   // "slot-unmapped" / "slot-stale" / "team-unknown"

const issue = await client.issues.get("ENG-12");
if (issue.outcome !== "ok") throw new Error(issue.outcome);
const moved = await client.agent.issueState({ issueId: issue.issue.id, stateId: stage.stateId });
switch (moved.outcome) {
  case "succeeded":     break;                                  // moved.attempts
  case "rate-limited":  /* moved.retryAfterSeconds may be null */ break;
  case "forbidden":     /* moved.required names a missing scope */ break;
  case "route-unknown": /* this tenant's contract lists no issue-state route */ break;
  default:              /* rejected | exhausted | failed | unauthorized | network | shape | http | stale | missing */ break;
}
```

`client.messages.read({ ticket: "CTC-1234", since: 932140 })` 读取信件机构、发送者(`from`)和时间戳(`sentAt`),最古老的一页,最多50页. `since` 是一个包容性的主干线序列 `comms.message.posted` 事件; 省略第一页, 然后通过返回 `next` 作为 `since` 原文如下。 停止拨号 `messages` 为空或 `next` 实值 `null`。空页可以保留数字 `next`; 保存到其他消息到达时恢复。 使用您自己的密钥: 此读取的路由拒绝 。 你们队伍外的一张票回来 `forbidden` 有服务器的理由; 没有路由的服务器返回 `unsupported`。 。 。 .

合同缓存默认存在于每个客户端的内存; pass `contractCache: { get, set }` 将其保留在磁盘上. `fetch`, (中文(简体) ), `now` 财务报告和审定财务报表 `timeoutMs` 可以注射,所以测试不会打开套接字.

**CTC-2132 — 捆绑自运的剩余路线:**

| 命名空间 | 方法 |
| --- | --- |
| `client.issues.execution(identifier)` | `GET /api/v1/issues/:id/execution` ——票房自有执行/遥测报告. |
| `client.diagnostics.*` | `workEligibility`, (中文(简体) ), `dispatchQueue`, (中文(简体) ), `fleetActivity`, (中文(简体) ), `agentRoster`, (中文(简体) ), `leaseAttributions`, (中文(简体) ), `codingAccounts` ——顾客代表代理自己的"为什么什么都不动"/"我的舰队在做什么"的答案. |
| `client.cycles.list()` | `GET /api/v1/cycles`。 。 。 . |
| `client.search({ q, limit })` | `GET /api/v1/search` 问题/推/项目/倡议桶. |
| `client.workflowStages()` | `GET /api/v1/workflow-stages` (钥匙认证双胞胎). |
| `client.changes.stream({ since, signal })` 编号 : `client.changes.list(...)` | `GET /api/v1/changes` NDJSON的变换信号; `stream()`发电机可能会在运输断层上投入中位点, `list()` 从来没有扔过. `stream()`"好的手臂也带着" `close()`: 一个读取 `head` 并且不会耗尽 `rows` 必须叫它释放尸体. |
| `client.snapshot.head()` | `GET /api/v1/snapshot?head=1` 廉价的头部探测器 永远不要回到一个完整的快照. |
| `client.request({ method, path, query, headers, body })` | 通用授权请求的逃生舱 用于无人列出的路线. **`path` 必须是一个绝对路径 在客户端自己的来源下** (例如:. `"/api/v1/…"`),从不提供URL——在发送任何信息之前拒绝提供逃避的值. |

以上每一条都来自云端传出本套件无法被编译(`catalyst-cloud` 因此每个响应类型都带有索引签名,运行时检查只验证响应的 _集装箱_ 形状(一个数组,或带有所记录的密钥的物体)——从不单一字段——因此,云边字段添加绝不被拒绝作为形状错误.

个人 GitHub 和 Linear 赠款使用会员自己的密钥或设备登录证书. 租户账户密钥无法识别该成员. `start` 返回成员在浏览器中打开的短命的 URL; SDK 不完成提供者的同意或返回提供者的令牌。 检查 `status` 之后,并治疗 `unavailable` 作为未知的提供者检查,而不是缺席的赠款.

```ts
const member = createTenantClient({
  key: process.env.CATALYST_CLOUD_TOKEN!, // personal key or device-login JWT
  baseUrl: "https://staging.catalystcloud.dev",
});
const started = await member.personalConnections.start("github");
if (started.outcome === "ok") console.log(started.authorizationUrl);
const grant = await member.personalConnections.status("github");
if (grant.outcome === "connected") console.log(grant.githubLogin);
```

个人线性同意通常对成员与提供者取景者具有约束力. 对于一个无与伦比的成员, `linearIdentity.get()` 返回当前身份和任何可用的名册选择。 选择一个用 `linearIdentity.set(id)` 然后读回来 这些电话需要个人证书,不接受会员选择,不能取代自动解决的身份. 冲突区分 `already_resolved`, (中文(简体) ), `already_claimed`,以及 `identity_changed`; 重试前重新装入.

```ts
const identity = await member.linearIdentity.get();
if (identity.outcome === "ok") console.log(identity.identity, identity.options);
// After the member chooses an ID from the offered roster:
const saved = await member.linearIdentity.set(selectedLinearUserId);
if (saved.outcome === "ok") console.log(await member.linearIdentity.get());
```

经纪人可以录下询问的主人在聊天时给出的答案 `agent.recordProxyAskAnswer({ askIssueId, ownerQuote, saidIn, saidAtMs, optionIndex?, acceptedByRole? })`确切地引用了主人的话 别管了 `optionIndex` 免费的回答. `acceptedByRole` 可选地命名录音代理的角色(一行,最多64个字符). 答案记录为由业主代为给出的,从不作为业主自己给出的. 拒绝 `proxy-answering-off` 直到主人打开它 `askProxyAnswering.set(true)`;默认关闭。 店主可以带回录音的答案 `askProxyAnswering.disown(askId)`,可以重开询问。 这个 `askProxyAnswering` 电话需要拥有者的个人证书.

```ts
const opted = await member.askProxyAnswering.set(true);
const recorded = await agentClient.agent.recordProxyAskAnswer({
  askIssueId: "ENG-50",
  ownerQuote: "Go with B.",
  optionIndex: 1,
  saidIn: chatSessionId,
  saidAtMs: Date.now(),
});
if (recorded.outcome === "refused") console.log(recorded.reason);
```

一个存储器的流量度量标准来自 `flow.series({ repo, period?, granularity? })`。 。 。 . `repo` 实值 `owner/name`。 。 。 . `period` 这是其中之一 `FLOW_PERIODS`从 `24h` 改为 `26w`;略出出后,云答出后两星期. `granularity` 实值 `hour` 或者说 `day`; 省出,取出桶宽. 每个条目 `series.columns` 一个桶与 `startMs` 财务报告和审定财务报表 `endMs` (划时代毫秒,端独活)和 `pr.open`,拉取请求打开 就在桶结束, 包括草稿. `pr` 实值 `null` 当工作空间无法将拉动请求归属到寄存器时。 最后一个桶结束于 `generatedAt` 已经 `partial`。任何其他颗粒是 `rejected` 与 `error: "invalid-granularity"` 财务报告和审定财务报表 `allowed: ["hour", "day"]` 在发出请求之前。 长时间的小时桶可以超过云的响应预算 `rejected` 有400个身份 并且有每日桶或更短时间的理由.

```ts
import { createTenantClient } from "@catalyst-cloud/sdk";

const client = createTenantClient({ key: process.env.CATALYST_API_KEY!, baseUrl: "https://staging.catalystcloud.dev" });

const flow = await client.flow.series({ repo: "coalesce-labs/catalyst-cloud", period: "1w", granularity: "hour" });
if (flow.outcome === "ok") {
  for (const bucket of flow.series.columns) console.log(new Date(bucket.endMs).toISOString(), bucket.pr?.open ?? "unavailable");
} else {
  console.log(flow.outcome, flow.reason);
}
```

### 车票租赁——取票、续票、发票、阅读和名单

`createTicketLeaseClient` 为代理人和其他座位持有者包罗出云的出票租赁路线. 打电话 `take` 要求一张票。 拒绝的结果是正常的答案,而不是错误:检查 `refusal.reason` 和重试前的当前持有人。 以新的进步和当代人来更新。 完成后释放这一代人 阅读一票或列出一队检查持票人. HTTP 失败 `TicketLeaseHttpError` 以服务器的状态和代码.

```ts
import { createTicketLeaseClient, TicketLeaseHttpError } from "@catalyst-cloud/sdk";

const leases = createTicketLeaseClient({
  baseUrl: "https://staging.catalystcloud.dev",
  token: process.env.CATALYST_CLOUD_TOKEN!,
  // fetch and timeoutMs are optional; timeoutMs defaults to 15 s.
});

const taken = await leases.take({
  ticket: "CTC-6480",
  agent: "factory-droid",
  holderDetails: { kind: "factory" },
});
if (taken.outcome === "taken") {
  console.log(taken.lease.holderDetails?.owner); // the server derives the owner from the credential
} else {
  console.log(taken.refusal.reason, taken.refusal.current?.holderDetails); // e.g. ticket_lease_held
}

const renewed = await leases.renew({
  ticket: "CTC-6480",
  agent: "factory-droid",
  generation: taken.outcome === "taken" ? taken.lease.generation : 0,
  progress: { kind: "pr", ref: "15901" },
  holderDetails: { externalId: "s_91aa", contact: null }, // a value replaces, null clears, omitted stays
});
await leases.release({ ticket: "CTC-6480", agent: "factory-droid", generation: renewed.outcome === "renewed" ? renewed.lease.generation : 0 });

const read = await leases.read("CTC-6480"); // lease is null when never leased; holderDetails/lastHolderDetails survive
const page = await leases.list({ team: "CTC" }); // state "live" (default) or "recent", optional limit and cursor
```

持有者详细信息是持有者声明和服务器接受的内容:类型,显示名称,会话 URL,外部ID,一个联系链接,加上服务器来自证书的拥有者(客户端的拥有者被忽略). 在服务器记录细节之前的租约, 或通过不声明细节的路线持有, 内容为 `holderDetails: null`验证失败 — 需要链接的电子邮件, 未知类型, 类型更改 中代 — 抛出 :

```ts
try {
  await leases.take({ ticket: "CTC-6480", agent: "factory-droid", holderDetails: { kind: "factory", contact: "ops@example.com" } });
} catch (error) {
  if (error instanceof TicketLeaseHttpError) console.log(error.status, error.code, error.field); // 400 holder_contact_invalid contact
  else throw error;
}
```

`list` 返回一页; 通过 `nextCursor` 下回没有变化。 页面按租借期限排序, 租借期限在续签时移动, 因此一个页面边界可以重复或跳过在读取之间续签的租赁 。 每通电话都发出一个请求 这需要有一个催化云服务器,提供售票租赁持有人的细节和团队租赁名单(合同2.65).

### 节点和 Bun 持久事件缓存

`@catalyst-cloud/sdk/events` 将租户确切的耐用催化剂事件主干镜像成仅由附件本地缓存. 它使用有边框的HTTP以闲置回放来重放;本地消费者会跟踪文件并永远不投票给GitHub,Linear,或云表. 从目前需要租户而不是安装另一个永久守护进程开始并停止它.

```ts
import { CatalystEventSync, tailCachedEvents } from "@catalyst-cloud/sdk/events";

const sync = new CatalystEventSync({
  baseUrl: "https://staging.catalystcloud.dev",
  auth: { kind: "token", token: process.env.CATALYST_CLOUD_TOKEN! },
  tenantId: "tenant-1",
});
const running = sync.start();
for await (const event of tailCachedEvents({ tenantId: "tenant-1", signal })) {
  console.log(event.type, event.sequence);
}
await sync.stop();
await running;
```

默认路径是 `$XDG_STATE_HOME/catalyst/events/<tenant>/backbone/`后退为 `~/.local/state/catalyst/events/<tenant>/backbone/`日报 `YYYY-MM-DD[-NNN].jsonl` 区段包含云层 `CatalystEvent` 信封不变. `cursor.json` 属于一个同步编译器; 读者保留自己的偏移位置, 不写入此目录。 稳定的重放身份是 `(tenantId, sequence, eventId)`。 。 。 .

编剧Fsyncs在解剖推进光标前附上了记录. 在重新启动时,它会截断一个不完整的最后一行,并从上次完整的记录中取出恢复光标,使附件和检查站之间的崩溃安全。 服务器存档漏洞并读取本地保留层升起后 `EventHistoryGapError` sDK不会默默地跳过历史。 新的缓存在当前的主干头上 。 闭合的日片段保留了7天,或直到缓存达到256个米B,以先发生者为准. 活性段会在同一字节绑定之前旋转,因此不能无限制地生长; 2; `retainDays` 财务报告和审定财务报表 `maxCacheBytes` 配置这些界限 .

这个API只读取耐用的主干. 它不公布事件,也不将原始提供者事件、遗产协调信息或分析样本作为执行或开具账单的权力。 利用现有授权的云操作报告结果和变化.

#### 等待活动

`waitForEvents` 直接在云上等待,有自己的订阅,没有本地缓存. 它的默认超时, `DEFAULT_EVENT_WAIT_TIMEOUT_MS`四个小时 套接字保存和偶尔回放检查保持了等待的健康;它不产生输出,在静静地等待期间不会返回. 提供代号或刷新持证人证书。 阶段会话证书可以在服务器承认时订阅.

```ts
import { waitForEvents, EVENT_WAIT_EXIT_CODES } from "@catalyst-cloud/sdk/events";

const result = await waitForEvents({
  baseUrl: "https://staging.catalystcloud.dev",
  auth: { kind: "token", token: process.env.CATALYST_API_KEY! },
  tenantId: "workspace-1", // your workspace ID
  filter: {
    type: "pr.conflict.detected",
    ref: "acme/web:feature/checkout",
  },
  timeoutMs: 4 * 60 * 60 * 1000,
});
if (result.outcome === "matched") {
  for (const event of result.events) console.log(JSON.stringify(event));
}
process.exitCode = EVENT_WAIT_EXIT_CODES[result.outcome];
```

过滤器至少需要一个密钥 。 一个密钥的值是 OR'ed; 不同的密钥是 &'ed. 每个键接受一个字符串或一个最多50个不同值的清单,但 `to`,仅接受 `"me"`。 。 。 . `events.query` 接受相同的密钥,并分别发送每个值.

| 过滤密钥 | 示例 |
| --- | --- |
| `type` | `"pr.conflict.detected"` 或家庭前缀, `"pr.*"` |
| `ticket` | `"CTC-1234"` |
| `repo` | `"coalesce-labs/catalyst-cloud"` |
| `ref` | `"acme/web:feature/checkout"` |
| `pr` | `"coalesce-labs/catalyst-cloud#9001"` |
| `session` | `"CTC-1234/implement#7"` |
| `to` | `"me"`,用云解决 与你的证书 |

在打开其套接字前直接等待检查访问, 并返回服务器在密钥无法达到请求范围时的拒绝 。 连接中断后, 它会从光标中重放, 等待索引覆盖, 并在允许的情况下回到主干线重放 。 如果保留的历史无法覆盖光标,它明确报告历史差距.

匹配的结果包含最古老的事件,最多 `maxEvents` (默认100). 保存其内容 `cursor` 传为 `after` 下个等 : 后期匹配一次。 光标只涵盖已经交付或证明不存在的事件;封顶的批次会留下以后的事件进行下一次呼叫. 超时和中止结果还包括最后被证明的光标,这可能是 `null` 如果还没有建立.

| 结果 | 含义 | `EVENT_WAIT_EXIT_CODES` |
| --- | --- | --- |
| `matched` | 一个或多个匹配事件及恢复光标 | `0` |
| `failed` | 历史空白、 不支持的服务器、 无效的响应或无法使用的缓存; 检查 `code` 财务报告和审定财务报表 `reason` | `1` |
| `refused` | 过滤器无效或拒绝访问云; 检查 `source`, (中文(简体) ), `filter` 财务报告和审定财务报表 `reason` | `3` |
| `timeout` | 在截止日期之前没有匹配;包括 `waitedMs` 恢复光标 | `124` |
| `aborted` | 呼叫者中止其信号; 包括恢复光标 | `130` |

对于连续交付, `watchEvents({ ...options, signal })` 产量匹配一个订阅的批次而不超时。 它在出现拒绝、失败或中止后完成。 突破环路会关闭订阅.

```ts
import { watchEvents } from "@catalyst-cloud/sdk/events";

const controller = new AbortController();
for await (const batch of watchEvents({
  baseUrl: "https://staging.catalystcloud.dev",
  auth: { kind: "token", token: process.env.CATALYST_API_KEY! },
  tenantId: "workspace-1",
  filter: { type: "comms.message.posted", ticket: "CTC-1234" },
  signal: controller.signal,
})) {
  if (batch.outcome === "matched") console.log(batch.events, batch.cursor);
  else console.error(batch);
}
```

一个密钥最多可以同时持有20个事件套接字 。 在许多等待者之间共享一个上游连接,运行一个 `CatalystEventSync` 编剧和使用 `waitForCachedEvents` 每个读者:

```ts
import { waitForCachedEvents } from "@catalyst-cloud/sdk/events";

const result = await waitForCachedEvents({
  tenantId: "workspace-1",
  filter: { type: "comms.message.posted", ticket: "CTC-1234" },
  // directory: "/path/to/existing/event-cache", // optional override
});
```

缓存等待会递增读取, 不打开上游连接, 并且从未创建或写入缓存 。 立即失败 `cache-missing` 或者说 `cache-writer-stopped` 当缓存或其活写器缺席时。 缓存只保留它保留的历史, 所以光标比历史返回的时间长 `history-gap`。它支持相同的光标、超时和分批选项. `to: "me"` 被本地拒绝,因为只有云可以解析您的身份; 直接等待过滤器.

## API 密码

| 导出 | 这是什么 |
| --- | --- |
| `LiveSyncClient` | 客户 - `new LiveSyncClient(opts)`,则 `start()` 编号 : `stop()`。 。 。 . |
| `LiveSyncClientOptions` | 选项形状( auth, 重播, getCursor, 关于 Change, 关于Status, 回放, wsFactory, log) . |
| `AuthStrategy` | `{ kind: "token"; token }` (后端)或 `{ kind: "cookie" }` (浏览器). |
| `LiveSyncStatus` | `"connecting"` • 妇女 `"live"` • 妇女 `"reconnecting"` • 妇女 `"resyncing"` • 妇女 `"error"` • 妇女 `"stopped"`。 。 。 . |
| `ChangeFrame` • 妇女 `EntityName` • 妇女 `ChangeOp` | 变化形状+实体/操作合同. |
| `createTenantClient` | 输入的 HTTP 客户端 - `contract()`, (中文(简体) ), `me()`, (中文(简体) ), `request()`, (中文(简体) ), `issues.list/get/execution`, (中文(简体) ), `pulls.list/get`, (中文(简体) ), `projects.list`, (中文(简体) ), `personalConnections.start/status`, (中文(简体) ), `linearIdentity.get/set`, (中文(简体) ), `askProxyAnswering.get/set/disown`, (中文(简体) ), `cycles.list`, (中文(简体) ), `flow.series`, (中文(简体) ), `search`, (中文(简体) ), `workflowStages`, (中文(简体) ), `changes.stream/list`, (中文(简体) ), `events.query/pages`, (中文(简体) ), `messages.read`, (中文(简体) ), `snapshot.head`, (中文(简体) ), `diagnostics.*` (工作资格、调度队列、车队活动、代理Roster、租赁代理、编码账户), `agent.*` (问题状态, 问题标签, 问题Comment, 问题创建, 反应, 附加, 附加, 会话, 问, 询问接受, 记录 ProxyAskAnswer, project Repository Register, project Repository Remove, message). 接受一个 `key: string` 或反恐委员会2111 `auth: AuthStrategy`。 。 。 . |
| `TenantContract` • 妇女 `routeByName` • 妇女 `teamByKey` • 妇女 `teamForTicket` • 妇女 `stageIdForSlot` • 妇女 `labelIdFor` | 合同文件的外形和上面的纯接通器,每个都返回打出一个打错,而不是扔出. |
| `createTicketLeaseClient` • 妇女 `TicketLeaseHttpError` | 机票租赁客户 - `take({ ticket, agent, holderDetails? })`, (中文(简体) ), `renew({ …, generation, progress })`, (中文(简体) ), `release({ …, generation })`, (中文(简体) ), `read(ticket)`, (中文(简体) ), `list({ team, state?, limit?, cursor? })` 从包根。 存储拒绝解析 `outcome`; HTTP 失败随着服务器的状态和代码而抛出. |
| `TenantClientFailure` • 妇女 `PageCursor` • 妇女 `pageCursor` • 妇女 `memoryContractCache` | 共享失败臂,不透明的页面符和默认的合同缓存商店. |
| `CatalystEventSync` • 妇女 `readCachedEvents` • 妇女 `tailCachedEvents` • 妇女 `EventHistoryGapError` | 节点/ Bun 耐用后骨重放, 本地缓存读取器, 以及清晰的历史- gap 处理从 `@catalyst-cloud/sdk/events`。 。 。 . |
| `waitForEvents` • 妇女 `watchEvents` • 妇女 `waitForCachedEvents` | 一发和连续过滤的等待从 `@catalyst-cloud/sdk/events`。 。 。 . |
| `EventWaitOptions` • 妇女 `EventWatchOptions` • 妇女 `EventWaitResult` • 妇女 `WaitedEvent` • 妇女 `CachedEventWaitOptions` | 等待输入、事件批次和输入终端结果 `@catalyst-cloud/sdk/events`。 。 。 . |
| `DEFAULT_EVENT_WAIT_TIMEOUT_MS` • 妇女 `EVENT_WAIT_EXIT_CODES` | 4小时默认和结果到结果代码映射 `@catalyst-cloud/sdk/events`。 。 。 . |
| `EventScopeFilter` • 妇女 `EventScopeFilterInput` • 妇女 `checkEventFilter` • 妇女 `eventScopeKeys` • 妇女 `matchesEventFilter` • 妇女 `matchesEventType` | 共享过滤语法、范围衍生和匹配 `@catalyst-cloud/sdk/events`。 。 。 . |
| `EventFilterKey` • 妇女 `EventFilterCheck` • 妇女 `EventScopeKeys` • 妇女 `EVENT_FILTER_MAX_VALUES` • 妇女 `eventFilterSocketParams` • 妇女 `eventFilterQueryParams` | 过滤验证类型、限制和序列化 `@catalyst-cloud/sdk/events`。 。 。 . |
| `MessagesReadParams` • 妇女 `MessagesReadResult` • 妇女 `AgentMessageRecord` | 消息从包根读取输入,结果和记录. |

`start()` 只在 `stop()` 名为. 在一个后端, `await` 在浏览器中,永远不要等待它 `stop()` 撕毁.

## 许可证

麻省理工学院Coalesce实验室——见 [自由主义](https://npmcn.com/package/%40catalyst-cloud/sdk/LICENSE)。 。 。 .

## 常见问题

### @catalyst-cloud/sdk 是什么？

读取并更新您的催化云数据,并接收您的应用程序的活化变化\.

### 如何安装 @catalyst-cloud/sdk？

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

### @catalyst-cloud/sdk 支持 TypeScript 吗？

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

### @catalyst-cloud/sdk 支持 ESM 还是 CommonJS？

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

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

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

### @catalyst-cloud/sdk 可以商用吗？

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

### @catalyst-cloud/sdk 的下载量有多少？

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

### @catalyst-cloud/sdk 还在维护吗？

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

