# @codefusion-cc/console 中文文档

规范地址：https://npmcn.com/package/%40codefusion-cc/console/

版本：0\.34\.0
npm 元数据更新时间：2026-10-18T00:01:43.928Z
中文译文更新时间：2026-11-10T06:44:43.209Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40codefusion-cc/console
原文对照：https://npmcn.com/package/%40codefusion-cc/console/?lang=original
项目主页：https://github.com/codefusion-cc/codefusion-console/tree/main/packages/console#readme
源码仓库：https://github.com/codefusion-cc/codefusion-console

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

- 包体积（解压后）：643 kB，121 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：4 个
- 首次发布：2026-09-27T16:32:17.572Z
- 最新版本发布：2026-10-18T00:01:43.728Z
- 近 30 天下载量：2798（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：41（近 12 个月 41 个）

## 中文简介

连接一个 Cloudflare 工人应用软件到 CodeFusion Console : 管理来自 JSON 列表的屏幕、 工作人员、访问统计、浏览器故障、诊断及其部署版本; 以及 编码- Console 命令行

## 安装

```sh
npm install @codefusion-cc/console
```

也可以使用：`pnpm add @codefusion-cc/console`、`yarn add @codefusion-cc/console`、`bun add @codefusion-cc/console`

## README

# @ codumation-cc/ 控制台

连接 Cloudflare 工人应用程序 [密码聚合控制台](https://console.codefusion.cc/)。应用程序没有 自己的控制台代码: 它在 JSON 列表中宣布它的管理屏幕, 这个软件包为它们服务 控制台监视器的其余部分.

| 控制台的页面 | 应用程序提供什么 |
| --- | --- |
| 资源(清单、记录、行动) | `manifest.json` 每个资源和行动的处理者`consoleAdmin`(中文(简体) ) |
| 故障、健康、记录 | 它的工人是控制台的尾巴消费者. 浏览器出错 `connectBrowser` 财务报告和审定财务报表 `createConsoleRoutes` |
| 保存日志搜索, 端点检查 | `diagnostics` 在仪表和行中 `diagnosticLog` |
| 机密( 缺失、 新值、 实时和 R2 证书) | `secrets` 在表里:每个工人都需要的秘密和如何计算价值 |
| 访问 | `connectBrowser` 页 次 `createConsoleRoutes` 在工人中 |
| 呼叫( 在 Cloudflare 实时的 WebRTC) | `createMediaSampler` 每个呼叫连接和 `recordMediaReport` 它的服务器从它那里听到 |
| 部署 | `/version.json` 从 `versionFile()` 由GitHub Actions部署,这个部署任务命名它 `environment:` (操纵台) [文件/电信](https://github.com/codefusion-cc/codefusion-console/blob/main/docs/TELEMETRY.md#github-actions-deploys)(中文(简体) ) |
| 应用程序内的工作人员 | `staffDirectory` 任务、邀请、小组、访问、审计记录和月份实时预算 |

## 安装

```sh
npm install @codefusion-cc/console
```

## 名单

`manifest.json` 描述应用程序的资源、其字段、列表、过滤器、记录页和动作,加 选项 `diagnostics` 财务报告和审定财务报表 `secrets`控制台 [文件/地图](https://github.com/codefusion-cc/codefusion-console/blob/main/docs/MANIFEST.md) 文档格式。 编辑完成 :

```json
{ "$schema": "../node_modules/@codefusion-cc/console/app-manifest.schema.json", "manifestVersion": 1, "app": { "id": "myapp", "name": "My app" }, "resources": [] }
```

`parseManifest(value)` 从 `@codefusion-cc/console` 验证代码中的显示.

## 工人组织

```js
// worker/index.js
import { consoleAdmin, createConsoleRoutes } from '@codefusion-cc/console/worker'
import manifest from './console/manifest.json' with { type: 'json' }

// What the console reads and moderates the app through. Each handler gets { env, actor }; actions also get
// { ids, input, reason }. The manifest's permission, scope and requireReason are checked before a handler runs.
export const ConsoleAdmin = consoleAdmin({
  manifest,
  resources: {
    users: {
      list: (query, { env }) => listUsers(env, query), // { items, nextCursor?, total? }
      get: (id, { env }) => getUser(env, id), // a record, or null
      actions: { ban: ({ env, ids, reason }) => banUsers(env, ids, reason) }, // { ok, message? }
    },
  },
})

// POST /api/visits and /api/browser-failures, from the app's own pages.
const consoleRoutes = createConsoleRoutes({
  appId: 'myapp',
  pages: ['home', 'settings'], // the page names the browser side reports
  bindings: { visitorSecret: 'VISITOR_HASH_SECRET' }, // optional: count consenting visitors apart
})

export default {
  async fetch(request, env, ctx) {
    const answer = await consoleRoutes.fetch(request, env, ctx) // null for any other path
    if (answer) return answer
    // …the app's own routes
  },
}
```

两条路由默认只接受页面本身的出处;通过应用程序自己的检查作为第四个参数 (单位:千美元)`consoleRoutes.fetch(request, env, ctx, origin => allowed.has(origin))`) (中文(简体) ). 他们回答204 无论发生什么, 在部署中不做任何 缺少他们需要的束缚.

程序内部的工作人员

```js
import { diagnosticLog, staffDirectory } from '@codefusion-cc/console/worker'

const staff = staffDirectory(env, { appId: 'myapp' })
const roles = await staff.roles(accountId) // { role, permissions, … }, or null: the console says not staff
// It rejects when the console fails or is slow, so an outage never reads as "not staff": a check of who may act,
// or who is protected from it, refuses. Only a caller that merely shows staff tools catches it:
const showTools = await staff.roles(accountId).catch(() => null)
// Asked on every request of a staff member? `cacheMs` keeps each answer that long per isolate,
// and a role changed in the console reaches the app that much later.
const cachedStaff = staffDirectory(env, { appId: 'myapp', cacheMs: 30_000 })
await staff.recordAction({ accountId, kind: 'record.action', resource: 'users', action: 'ban', outcome: 'ok' })
const result = await staff.redeemInvite(token, accountId) // from the console's invite link
// An app the console links by address (below) calls this at each sign-in whose provider verified the address:
const link = await staff.linkVerifiedEmail(accountId, verifiedEmail) // linked | already | none | refused + code
await staff.unlinkAccount(accountId) // when the app deletes an account: unlinked | none | refused + code
// An admin's own page inside the app, always as the acting account; the console checks its permissions.
const team = await staff.team(actorAccountId) // members:manage: { ok, team } or { ok: false, code }
await staff.setTeamRole(actorAccountId, accountId, 'moderator', 'Their name') // null removes; staff inside the app only
const visits = await staff.visits(actorAccountId, '7d') // telemetry:read: the Visits page's counts

const pushLog = diagnosticLog('push') // a manifest search with "prefix": "push " finds these
pushLog('failed', 'web.push.apple.com 410', 'warn')
```

### 工作人员经核实地址

一个在控制台上的应用程序 `config/apps.json` 说 `"staffLink": "verified-email"` 不需要邀请链接. 所有人在应用程序的员工网页上添加个人地址; 个人与一个供应商在应用程序上签名 该应用程序告诉控制台:

```js
// Only right after a sign-in whose provider said the address is verified, with the address from that token:
// never an address the account typed, changed, or signed up with by password or a mail link.
const link = await staff.linkVerifiedEmail(account.publicId, google.email)
```

| 答复 | 手段 | 应用程序 |
| --- | --- | --- |
| `linked` | 地址是会员在这个应用程序上扮演的角色(或拥有者);账户现在是他们的 | 询问 `roles()` |
| `already` | 这个账户在之前与该成员有联系 | 询问 `roles()` |
| `none` | 工作人员中没有人有这个地址(或他们残疾):控制台没有被保留和记录 | 任何顾客都一样 |
| `refused`, (中文(简体) ), `member-linked` | 成员链接到该应用程序的另一账户;该链接将保留,工作人员页面将显示该账户旁边 | 继续没有工作人员工具; 管理工作人员的人将那里的旧账户解开链接 |
| `refused`, (中文(简体) ), `account-linked` | 另一通电话在同一时间将账户与另一个成员联系起来 | 询问 `roles()`;下一个签入就解决了 |
| `refused`, (中文(简体) ), `not-enabled` | 控制台不通过地址链接此应用程序 | 配置错误: 报告为失败 |
| `refused`, (中文(简体) ), `caller` | 绑定的名称没有应用程序, 或另一个比 `appId` | 配置错误: 报告为失败 |
| `refused`, (中文(简体) ), `invalid` | 无账户 ID (`[A-Za-z0-9_-]{8,128}`),或无地址:没有地址,或有空间、控制或隐形字符的地址 | 编程错误: 报告为失败 |

它拒绝,就像 `roles()`,当控制台失败时,不回答 `timeoutMs`,或回答其他问题; 没有约束 它回答 `none`。一个签注永远不会失败,因为它: 抓住拒绝,去没有 工作人员工具,并让下一个登录呼叫再次(一次两次连接一次并回答一次) `already`) (中文(简体) ). 任何答案 或拒绝删除账户缓存 `roles()`。 。 。 .

```js
let staffRoles = null
try {
  const link = await staff.linkVerifiedEmail(account.publicId, google.email)
  if (link.status === 'refused' && link.code !== 'member-linked') console.error('staff link refused', link.code)
  staffRoles = await staff.roles(account.publicId) // whatever the answer: the console decides who is staff
} catch (error) {
  console.error('staff link failed', error) // the console is down or slow: signed in, no staff tools this time
}
```

**地址规则.** 地址必须是工作人员页面上的地址,字符,但ASCII除外 两种情况下的字母匹配。 其它没有折叠:没有点或加标签(`a.b@gmail.com` 财务报告和审定财务报表 `ab@gmail.com` 是两个 这里的地址,虽然是Gmail的一个邮箱,ASCII之外没有信件,也没有它的情况,没有像样的信,没有域名 在其其他拼写。 地址有空间、控制器或隐形字符 `invalid`: 无 已经修剪。 总是失事 `none`因此,该规则的成本是工作人员页需要地址作为 个人的谷歌账户有它.

**什么改变了链接.** 工作人员网页上改变的角色是下一个账户的角色 `roles()` (之后) (中文(简体) ) `cacheMs`,当应用程序保存答案时). 一个被移除的角色也结束了链接: 给回,它需要一个新的登录,并且 以前的情况并非工作人员本身。 在另一个地址下签字的账户,而不是其成员的 不管新地址是谁 都会失去联系 当应用程序删除一个账户时,它会调用 `staff.unlinkAccount(accountId)`,所以地址下的下一个账户是链接而不是 `member-linked`; 内容 拒绝类似链接,然后将留下的链接显示并在工作人员页面上结束.

**操纵台所信任的.** 谁能派一个工人去Cloudflare帐户,谁是受信任的 `props` 由调用工人的部署设定 且其代码在运行时不起作用) 只有一个地址,它的登录供应商 核实的账户在该登录。 控制台无法检查 第二:一个通过未经核实的地址的应用程序将使任何能够输入工作人员地址的人成为 工作人员。 谷歌意味着 `email_verified` 是真的,对于gmail.com以外的地址,不是 工作空间账户的(`hd`),Google本身并不保证邮箱仍然是个人的. 每一个链接,每一个 链接控制台或应用程序结束,每个被拒绝的账户或绑定都在控制台的审计日志中(`member.link`, (中文(简体) ), `member.unlink`与应用程序、成员和账户id、一次被拒绝的账户和一次被拒绝的约束 天数; `none` 所以控制台对顾客一无所知.

绑定名称 : `props`,只有应用程序 `wrangler.jsonc` 集及其代码不能在运行时更改 时间。 将一个应用程序命名为应用程序的绑定器只在每一次呼叫中作为应用程序使用,而通过地址链接的应用程序是 没有其他约束性:

```jsonc
{ "binding": "CONSOLE", "service": "codefusion-console", "entrypoint": "StaffDirectory", "props": { "appId": "myapp" } }
```

### (原始内容存档于2017-09-26). wrangler.jsonc

```jsonc
{
  // Failures, Health and Logs: the console reads every invocation.
  "tail_consumers": [{ "service": "codefusion-console" }],
  "observability": { "enabled": true },
  "services": [
    { "binding": "CONSOLE", "service": "codefusion-console", "entrypoint": "StaffDirectory" },
    { "binding": "CONSOLE_TELEMETRY", "service": "codefusion-console", "entrypoint": "AppTelemetry" }
  ],
  "analytics_engine_datasets": [
    { "binding": "VISITS", "dataset": "cfc_visits" },
    { "binding": "MEDIA_USAGE", "dataset": "cfc_media" } // only for an app with calls
  ],
  "ratelimits": [{ "name": "BROWSER_FAILURE_RATE_LIMITER", "namespace_id": "…", "simple": { "limit": 10, "period": 60 } }],
  "vars": { "APP_ENV": "production" }, // as the console's config/apps.json names this deployment
  // With privateSourceMaps(): the Worker, not the asset store, answers /_console/*, so the maps stay private.
  "assets": { "binding": "ASSETS", "directory": "./dist", "run_worker_first": ["/api/*", "/_console/*"] }
}
```

其他约束性名称进入 `createConsoleRoutes({ bindings: { environment, visits, telemetry, failureRateLimiter, assets } })` 财务报告和审定财务报表 `staffDirectory(env, { binding })`。 。 。 .

两种报告路径都是公开的,总是回答204。 访问最多2个基B,失败报告16个基B (单位:千美元)`VISIT_MAX_BYTES`, (中文(简体) ), `BROWSER_FAILURE_MAX_BYTES`),在读取身体时以UTF-8的字节来计算: 不管Length说些什么 都会被取消 这页剪掉了参观运动和UTM 字段和要匹配的报表堆栈,所以长链接或其他脚本中的堆栈到达的时间会更短,而不是掉入. `BROWSER_FAILURE_RATE_LIMITER` 在读取每个网络的报告(IPv6地址由它的64)之前, 因此,连接在极限 成本不读.

## 页面

```ts
// src/console.ts, imported early, before routing rewrites the address (it carries the campaign)
import { connectBrowser } from '@codefusion-cc/console/browser'

import { updates } from './updates.ts' // watchAppUpdates() from @codefusion-cc/app-update/browser

export const reporting = connectBrowser({ appId: 'myapp', version: `v${VERSION} · ${COMMIT}`, updates })
reporting.installBrowserFailureReporting()

// On each screen: reporting.setErrorPage('settings'); reporting.recordView('settings', signedIn)
// On sign-in: reporting.recordSignIn(). In a render error boundary: reporting.reportBrowserFailure(error, 'render')
// A consent banner: statsChoice(), setStatsChoice('granted' | 'denied'), reopenStatsChoice(), subscribeStatsChoice(fn),
// and recordConsent(page, signedIn) right after a yes.
```

重新装入新构建的标签 `updates` (单位:千美元)[@ codumation- cc/ app- update](https://www.npmjs.com/package/@codefusion-cc/app-update)),而失败就是 未报告; 重新装入没有修正 .

其错误可以命名为私人的应用程序,只能识别(某人打开的,文件路径)通过 拥有 `mask`,它运行在每个消息上,堆放和拒绝值的字段在包的遮盖前: `connectBrowser({ appId, version, mask: (text) => text.replace(/"[^"\n]*"/g, '"…"') })`。 。 。 .

错误报告在离开页面前遮盖电子邮件地址、查询字符串、碎片和长ID,跳过 浏览器扩展错误(他们的脚本,或者他们的API自己的单词,作为Safari无堆叠的"Invalid call to runtime.sendMessage ()),其他站点的脚本,当没有帧是页面自己的(除了app's,任何方案的一个帧), 例如网络视频 `iabjs://` 脚本,是另一个的), 和一个窗口或框架错误的拒绝 没有堆栈(注入脚本或iframe's). 串、 普通对象或有被拒绝的堆栈的错误 因为它是应用程序自己的代码。 每份报告都附有其证据(所抛出的价值是否为错误, 堆栈, 堆栈的框来自何处), 失败的页面和 `get_failure` 显示: 框中 页面本身的 HTML (`@/:3:185`)是内置脚本,应用程序内浏览器或扩展可以也注入. `connectBrowser({ ignore: (failure) => failure.stack?.includes('/vendor-widget/') === true })` 是应用程序自己说的 其它的不值得报告;它得到的失败,因为它会发出它的证据。 每页装入10后停止. 视图仅于 允许访问者统计; id保存最多180天 `<appId>-visitor-v1`选择 下级 `<appId>-stats-consent-v1`。工人存储了 ID 的密钥散列, 从未存储 ID, 也没有 IP 地址 或用户代理。 在应用程序的隐私政策中描述两者.

## 提供者的使用

一个将音频或文本发送给一个对流有限制的提供者的应用程序(今天的Soniox)报告每个流 因此,它的Soniox页面可以显示每个应用程序的溪流、拒绝、使用、成本和速度的高峰,并说: 问供养者多取. 将数据集添加到 `wrangler.jsonc`数字 :

```jsonc
"analytics_engine_datasets": [{ "binding": "PROVIDER_USAGE", "dataset": "cfc_provider" }]
```

```ts
import { createProviderUsage, usageOutcomeOf } from '@codefusion-cc/console/worker'

const usage = createProviderUsage({ appId: 'neko', env: env.ENVIRONMENT, provider: 'soniox', dataset: env.PROVIDER_USAGE })

const stream = usage.open({ kind: 'stt', model: 'stt-rt-v5' }) // when the socket to the provider is open
stream.firstResult() // at the first transcript (or the first audio)
stream.beat() // wherever the app already loops; writes at most every 20 s
stream.add({ unitsIn: audioMs, unitsOut: chars, costUsd }) // what was used since the last call; it adds up
stream.end('ok') // once, however the stream ends; 'limit' | 'auth' | 'timeout' | 'budget' | 'error' otherwise
usage.refused('stt', usageOutcomeOf(429)) // the provider turned a stream away before it opened
```

每一次通话都是同步的,在最多一个数据点上写,从不扔入溪流,没有写字 数据集。 没有音频或文本离开应用程序:一个数据点持有应用程序,一个流ID,计数,时间和结果. 一个溪流 其应用在无 `end` 停止计算75秒后最后一拍。 一个 `open` 是给提供者的套接字: a 滴水后再次打开流是新的 `open`,每个位置从开放到 `end`。 。 。 .

## 电话

一个通过WebRTC SFU(Cloudflare Realtime)调用的应用程序,显示其中谁在其中,质量如何,以及 他们发送和接收 在控制台的呼叫页面。 每个浏览器连接样本 自己的计数器和 将样本交给两个已经说话的应用程序的服务器, 说连接的恒活性:没有新的 ,最多每个连接一个数据点 `MEDIA_REPORT_MS` (30s) (中文(简体) ).

```ts
// The page, per RTCPeerConnection: 'send' for the one that publishes, 'receive' for the one that subscribes.
import { createMediaSampler } from '@codefusion-cc/console/browser'

const stats = createMediaSampler(pc, 'send')
// With each keep-alive: the sample read last time, if any. Reading happens in the background, so this never waits.
const sample = stats.take()
socket.send(JSON.stringify({ op: 'ping', ...(sample && { stats: sample }) }))
```

```ts
// The server, where it takes the keep-alive (a Durable Object, say). Never throws.
import { mediaReportDue, recordMediaReport } from '@codefusion-cc/console/worker'

const since = connection.statsAt ?? connection.startedAt // kept with the connection
if (frame.stats !== undefined && mediaReportDue(since, now)) {
  connection.statsAt = now // in the write the keep-alive already makes
  await recordMediaReport({ appId: 'myapp', env: env.APP_ENV, dataset: env.MEDIA_USAGE, secret: env.MY_HASH_SECRET }, {
    call: roomId,               // counts calls apart
    participant: connectionId,  // counts people apart: a socket or session, never an account id
    direction: 'send',          // as the browser's sampler
    tier: 'hd',                 // the quality the app gave this person, when it has tiers
    sample: frame.stats,        // as it arrived
    since, now,
  })
}
```

服务器决定连接报告的频率( 在上次报告后不早于 20 s) 以及每次报告的时间 报告可能涵盖(自2002年以来) `since`);字节被切到一个连接在那个时间可以携带什么,所以 一个被修改的客户端只能过度描述自己的流量,而且只能如此多. 没有数据集,就什么都写不出来 还是秘密.

样本中包含有音频和视频的字节、携带这些字节的轨迹、最大的图片(短于 侧面: 720 为 1280x720 或 720x1280, 设定一秒, 是否通过 TURN 中继器, 以及其中有多少 其字节已发生( n)`relayedBytes`),以及为什么浏览器发送量少于被询问量(`bandwidth`, (中文(简体) ), `cpu`) (中文(简体) ). 电话和 参与者ID只作为按键散列存储 。 没有其他关于这个人的信息.

**端对端.** 其媒体在人与人之间直线贯通的电话与人之间有联系. 给采样员一个列出它们的功能,一个取样员发送,一个取样员接收,这个人保留一个 发送器和一个接收器在呼叫页面上, 不管他们与许多人交谈。 服务器是这么说的 `transport: 'p2p'`: Cloudflare 帐单只收过一个中继器的帐单, 页面从中算起 `relayedBytes` (连接的任一端的中继计数).

```ts
const sent = createMediaSampler(() => peers.values(), 'send')
const received = createMediaSampler(() => peers.values(), 'receive')
// On the server: recordMediaReport(config, { ...report, transport: 'p2p', kind: 'random', sample })
```

**有点叫.** `kind` 是该类调用程序的名称( t)`random`, (中文(简体) ), `call`, (中文(简体) ), `room`: 小写,最多 24个字符),和"呼叫"页面分别显示;控制台的 `config/apps.json` 为人取名 (单位:千美元)`"calls": { "kinds": { "random": "Random chats 1:1" } }`) (中文(简体) ).

**留在免费津贴之内.** `staffDirectory(env, { appId }).realtimeBudget()` 这个月的答案 控制台 Cloudflare 账户的实时 Egress( SFU and TURN) 对应自由 1 000 GB, 如 Cloudflare 数起来: `{ ok: true, budget: { usedBytes, freeBytes, lastDayBytes, projectedBytes, monthStart, monthEnd, readAt } }`,或 `{ ok: false, code: 'unavailable' }` 当控制台不能说。 它从不拒绝,呼叫者 在同一时间 共享一个问题, 一个孤立保留答案 `budgetCacheMs` (一分钟;失败) 页:1 之后,最后的答案一时出现,同时要求一个新的答案,它继续使用,同时 控制台不能说,因为 `budgetStaleMs` (一个小时;0等待每个回答):在热道上的呼叫者从来不等待 在操纵台上。 当月接近津贴结束时,应用程序可以将其呼叫对等端发送到对等端.

## 大楼

```js
// vite.config.js
import { versionFile } from '@codefusion-cc/console/vite'

export default defineConfig({ plugins: [react(), versionFile(), privateSourceMaps()] }) // serves /version.json { version, commit }
```

`privateSourceMaps()` 构建隐藏源地图并移动到 `_console/source-maps/` 在建筑,没有 源文本( 代码 :`sourcesContent`: 映射堆栈只需要文件和行. 这个 页面不指着他们,和 `createConsoleRoutes` 答404,一次,请回答 `run_worker_first` 覆盖 `/_console/*` (上文)工人把这些要求交给它。 一个只发送一些路径给 `consoleRoutes.fetch` 检查 `isConsoleRoute(pathname)`,或者其资产商店为地图服务。 当一页报告失败时,工人会从它的地图读取 `ASSETS` 和报告 应用程序自身文件中的堆栈( q)`at src/notifications/push.ts:66:47`),所以控制台显示,和链接, 失败的行。 地图从不离开应用程序.

`buildIdentity()` 给同样的 `{ version, commit }` 对于应用自己的构建定义。 犯罪是CI的错 名称( Workers Builds, GitHub 动作, Cloudflare Pages), 其它 git 头; 在 GitHub 动作中 `pull_request` 盖起它 是拉请求自己的头承诺,而不是合并承诺 GitHub 构建它,因为它没有分支,所以预览 显示一个承诺 它的分支有.

部署后,工作将等待完成,直至完成工作,但从不完成(这条路线仍在旧址上) 版本,缓存),取而代之的是自身卷曲循环:

```yaml
- run: npx codefusion-console wait-for-deploy https://shop.example   # its /version.json; --within 60 (seconds)
```

它每隔5秒问一次缓存, 说网站同时服务的内容, 结尾是 GitHub 错误注释 点名. `--commit <sha>` 等待另一个承诺.

没有Vite( 构建, 脚本) 构建的建筑, 与 `writeVersionFile(dir)` 财务报告和审定财务报表 `moveSourceMaps(dir, { to, root })`: 绘制地图时没有 `sourceMappingURL` 注释(构建) `sourcemap: 'external'`, (中文(简体) ), `sourcesContent: false`然后移动它们. `to` 放在工人自己的静态中 用于其他页面,如 Cloudflare Pages ,以及 `root` 使来源相对 存储器,无论建筑将其脚本放在哪里:

```js
// scripts/build.mjs
import { moveSourceMaps, writeVersionFile } from '@codefusion-cc/console/vite'

const { commit } = writeVersionFile('site/dist') // define it for the page as __APP_COMMIT__
moveSourceMaps('site/dist', { to: 'server/private-assets', root: '.' })
// server/wrangler.jsonc: "assets": { "binding": "ASSETS", "directory": "./private-assets", "run_worker_first": true }
```

## 测试

```js
import { consoleAdminErrors } from '@codefusion-cc/console/worker'
// Every resource and action in the manifest has a handler, and the manifest is valid:
expect(consoleAdminErrors({ manifest, resources })).toEqual([])
```

该应用的工人测试可以绑定一个站台用于控制台,记录该应用发送的内容:

```js
// vitest.workers.config
miniflare: { workers: [{
  name: 'codefusion-console', modules: true, compatibilityDate: '2026-07-23',
  scriptPath: './node_modules/@codefusion-cc/console/dist/testing/consoleStub.js',
}] }
// env.CONSOLE.setStaff({ accountId, role }), env.CONSOLE.addInvite({ token, role }), env.CONSOLE.recorded(),
// env.CONSOLE_TELEMETRY.reported()
// Linking by address: env.CONSOLE.setStaffEmail({ email, role }) makes linkVerifiedEmail answer `linked` (then
// `already`, and `refused` for a second account); any other address is `none`, and ends a link the account had;
// unlinkAccount answers as the console; env.CONSOLE.unlink({ accountId }) is the Staff page's unlink;
// env.CONSOLE.setLinkRefusal({ code }) refuses every link; env.CONSOLE.setStaffLookupsFailing({ failing: true }) is the console down.
```

`/worker` 进口 `cloudflare:workers`。在平地节点加载的测试(外部) `@cloudflare/vitest-plugin`)别名 套件的备用模块(`WorkerEntrypoint` 财务报告和审定财务报表 `DurableObject`,所以应用程序自己的切入点和 持久对象类也加载)并内嵌到包中,所以别名到达:

```js
// vitest.config
resolve: { alias: { 'cloudflare:workers': fileURLToPath(import.meta.resolve('@codefusion-cc/console/testing/cloudflare-workers')) } },
test: { server: { deps: { inline: ['@codefusion-cc/console'] } } },
```

## 在操纵台上

将应用程序添加到控制台本身是一个拉出请求: 输入 `config/apps.json` (工人, 环境、地址和存放处)和对其有约束力的服务 `ConsoleAdmin` 内 `wrangler.jsonc`。 。 。 . 参见控制台的读取.

## 命令行

软件包也安装 `codefusion-console`:控制台的MCP工具(故障,日志,请求跟踪, 从弹壳,如你。 在应用程序的仓库中运行最新版本: 赤裸裸 `npx codefusion-console` 运行应用程序的自带副本,这些副本可以是控制台后面的版本.

```sh
npx -y -p @codefusion-cc/console@latest codefusion-console login                # once: the browser signs in through Cloudflare Access
npx -y -p @codefusion-cc/console@latest codefusion-console list_failures myapp  # every tool is a command
npx -y -p @codefusion-cc/console@latest codefusion-console help get_failure     # its options, from the console
npx -y -p @codefusion-cc/console@latest codefusion-console search_logs myapp --level error --range 24h > errors.txt
```

签入会自我更新,只要控制台的Access授权持续. 一个改变事物的工具 首先,或采取 `--yes`需要npm的第二个因素(`npm_publishing`)打开浏览器中的 npm 页面 请用您的安全钥匙、 密码或验证器确认, 并等待它 脚本它打印页面和 `--continue` 以同样的命令运行。 在npm不提供页面的地方,它要求一个 密码从您的验证器,或取 `--otp`答案包含: 图片或文件( e)`dispatch_attachment`以 `--out <path>`。退出代码:0回答,1个失败或被拒绝,2个 错误的命令,需要三个登录. `--console <url>` 到达另一个控制台,例如:. `http://localhost:8787` 页:1 `npm run dev`对.

在VM CI公司使用的工作里,这个Mac在CodeFusion公司自己的机器上运行的测试套房太重了 未承诺的工作,没有推进:

```sh
codefusion-console test-remote --suite "npm ci && npm run build && npm run test:browser"
```

它会打印出失败和末行,将输出全部写入它命名的日志文件,并退出 套房状况(75年,每台机器都忙着说,什么时候可能有空; `--wait 10m` 等待一个代替. `--image` 在 CI 容器图像中运行套件, `--size` 财务报告和审定财务报表 `--timeout` 设定任务, `--full` 打印所有输出, Ctrl-C取消,并且 `--attach <id>` 继续跑步 文件 `.gitignore` 永远不要离开麦克.

在终端等你的指令可以从您的电话中, 而不是从它的调度项中回答:

```sh
npm install -g @codefusion-cc/console && codefusion-console relay install   # once per Mac (launchd)
codefusion-console run --item pr-93 -- sudo xcodebuild -license accept      # its prompts show on item pr-93
```

`run` 在此显示命令, 如往常一样 ; 隐藏提示得到密码框, `y/N` 按钮,“新闻ENTER”按钮和 签名将打开按钮链接,您的答案被封入浏览器中与这个 Mac 的密钥。 npm 的确认页面 显示为 npm 上的确认。 与 `--renew-npm-link`为了让安全带再次运行, `run` 重新开始为新的 npm 耗尽时的链接(连续三次,然后等待您). 它现在需要macOS。 新继电器( 每个 `relay install` 一旦您在调度中信任它("相信这个Mac", 并使用您的密码),即可连接; `relay install` 等待这个,和 `relay status` 在等待的时候说.

控制台可以要求中继器执行一个命名的工作,从不执行自己的命令: `npm-trust` (npm的每个包 以Npm这个Mac上的一个确认, `automerge-key` (单位:千美元)`codefusion-console github-app-key automerge`: 密码化 - 自动化 App的ID和密钥来自 `~/.config/codefusion-automerge`,发送一次到控制台, 它只需要在10分钟内 业主 在其 Autommerge 页面上导入,但从未打印)和 `relay-update`。它们也用手操作:

Claude Code自己的许可提示 也从电话中回复到调度中心 用它的许可请求钩 (单位:千美元)`codefusion-console permission-hook`输入 `~/.claude/settings.json` 与 `"timeout": 600`:终端中的提示 留下,第一个答案算数; 允许拿走所有者的密码; 它从来不允许自己.

```sh
codefusion-console npm-trust --dry-run   # what it would change
codefusion-console relay update          # the newest @codefusion-cc/console, and the relay restarted
codefusion-console github-app-key automerge   # after Import on the console's Automerge page
```

`codefusion-console turbo-env` 写入 `~/.config/codefusion/turbo.env` (只有你才能读出它) 用什么Turborepo 需要读取 monrepo 的远程缓存: 读取代号以及控制台为所有者保存的签名密钥, 读取 过你自己的签注。 它取代了自己的线路 并保存你的;加载它 `set -a; . ~/.config/codefusion/turbo.env; set +a`。 。 。 .

一个需要 Cloudflare 的命令会得到它自己的一个短命的符号, 精确地用它命名的瞄准镜来刻出(r2, 工人,d1,kv,页; `:read` 或者说 `:edit`· ; `tail:read` 财务报告和审定财务报表 `logs:read`· ; `routes:edit` 财务报告和审定财务报表 `domains:edit` 部署 路线或自定义域; `tunnels:edit` npm egress的设置),被封入到这个Mac的接力键上,出局后被删除. 一个 写着要先等待所有者在调度器上的自带

```sh
codefusion-console run --item you-photo-upload --cloudflare r2:edit -- npm run images:upload
codefusion-console cloudflare workers:edit,d1:edit --ttl 30m -- npx wrangler deploy
```

中继器还把这个Mac的克洛德代码会话的谈话发送到他们的调度会话页上,就像它们一样 写作. 操纵台 [文件/文件](https://github.com/codefusion-cc/codefusion-console/blob/main/docs/DISPATCH.md#terminal-prompts) 拥有如何运作。 操纵台 [文献/磁共振](https://github.com/codefusion-cc/codefusion-console/blob/main/docs/MCP.md) 还有其他的.

## 常见问题

### @codefusion-cc/console 是什么？

连接一个 Cloudflare 工人应用软件到 CodeFusion Console : 管理来自 JSON 列表的屏幕、 工作人员、访问统计、浏览器故障、诊断及其部署版本; 以及 编码- Console 命令行

### 如何安装 @codefusion-cc/console？

在项目目录运行 npm install @codefusion-cc/console，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 codefusion-console，可以用 npx codefusion-console 直接运行。

### @codefusion-cc/console 支持 TypeScript 吗？

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

### @codefusion-cc/console 支持 ESM 还是 CommonJS？

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

### @codefusion-cc/console 需要什么版本的 Node\.js？

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

### @codefusion-cc/console 可以商用吗？

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

### @codefusion-cc/console 的下载量有多少？

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

### @codefusion-cc/console 还在维护吗？

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

