# @apocaliss92/nodedreame 中文文档

规范地址：https://npmcn.com/package/%40apocaliss92/nodedreame/

版本：1\.12\.5
npm 元数据更新时间：2026-09-26T09:52:18.575Z
中文译文更新时间：2026-11-06T19:59:49.965Z
软件包许可：MIT

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

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

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

- 包体积（解压后）：2.4 MB，9 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：4 个
- 首次发布：2026-06-05T09:33:05.683Z
- 最新版本发布：2026-09-26T09:52:18.380Z
- 近 30 天下载量：2413（2026-10-07 至 2026-11-05，npm 官方下载统计）
- 已发布版本数：32（近 12 个月 32 个）

## 中文简介

用于 Dreame 真空和割草机的节点.js/TypeScript 客户端

## 安装

```sh
npm install @apocaliss92/nodedreame
```

也可以使用：`pnpm add @apocaliss92/nodedreame`、`yarn add @apocaliss92/nodedreame`、`bun add @apocaliss92/nodedreame`

## README

# 点心

用于Dreame机器人真空和割草机的Node.js/TypeScript客户端通过Dreamehome云.

> 工作正在进行中。 统一,事件驱动库——不是UI.

## 状态

`discoverDevices()` 返回键入 `VacuumDevice` 页:1 `dreame.vacuum.*` 模型 键入 `MowerDevice` 页:1 `dreame.mower.*` 模型,每个模型都有解码状态 获取器和能力命令。 除此之外:

-   **地图** ——活式真空图解码(I/P-帧流)+PNG渲染,割草机图+SVG.
-   **连通性** ——每台设备在线/链接类型/经纪人/公司软件/系列视图 (单位:千美元)[连通性](https://npmcn.com/package/%40apocaliss92/nodedreame/#readme-connectivity)) (中文(简体) ).
-   **WiFi 信号** ——机器人的WiFi覆盖热映射,在机器人那里取样 用于当前符号读取的姿势([WiFi 信号](https://npmcn.com/package/%40apocaliss92/nodedreame/#readme-wifi-signal)) (中文(简体) ).
-   **相机视频( V)** (X40/X50级)——自主冷起动"连通视觉" RTMP流与 H.264+AAC 帧输出,相机动作(驱动/预设/声调/) 和双向对讲机 (单位:千美元)[相机和视频流](https://npmcn.com/package/%40apocaliss92/nodedreame/#readme-camera--video-streaming)) (中文(简体) ).

盖下:

-   OAuth 密码赠出登录, 并主动更新符号( 共享会话) 过期前刷新~100s,每个设备的 MQTT 推为 以透明的方式重新认证新标志).
-   每台设备 MQTT 推取 **持久重联** - 它与备份重接 在意外的下降中重建连接 刷新点 A 类 `get_properties` 民意调查倒计时只在推倒时进行 一旦重新连接就停止.
-   所有云和 MQTT 回复均经过验证 `zod` 在边界,所以 错误的响应以明显的错误快速失败, 而不是传播 `undefined`。 。 。 .

## 使用量

```ts
import { Nodreame } from '@apocaliss92/nodedreame';

const client = new Nodreame({
  username: process.env.DREAME_USERNAME!,
  password: process.env.DREAME_PASSWORD!,
  region: 'eu',
});

await client.login();
const devices = await client.discoverDevices();

for (const device of devices) {
  console.log(device.deviceId, device.model, device.name);

  // Live-read a couple of MIoT properties (siid.piid are model-specific).
  await device.refreshProperties([{ siid: 2, piid: 1 }]);
  console.log('state:', device.getProperty(2, 1)?.value);

  // React to pushed updates.
  device.on('stateChanged', (e) => console.log('changed', e.changes));
}

// The shared session auto-refreshes ~100s before expiry; every device's MQTT
// push is re-authenticated with the new token transparently.

await client.close(); // closes all pushes, clears all timers
```

> 每个手柄上都有通用的MIOT原始相: `refreshProperties` 编号 : `getProperty` (缓存) / `setProperty` 编号 : `callAction`。 。 。 . 摩尔`startMowing`/附表)处理和活地地图后期到达.

## 真空

对于 `dreame.vacuum.*` 型号, `discoverDevices()` 返回 a `VacuumDevice` 联合国 用于添加打入、解码状态获取器和 在原始的MIOT原始的上方有能力命令.

```ts
import { Nodreame, VacuumDevice, SuctionLevel } from '@apocaliss92/nodedreame';

const client = new Nodreame({
  username: process.env.DREAME_USERNAME!,
  password: process.env.DREAME_PASSWORD!,
  region: 'eu',
});

await client.login();
const devices = await client.discoverDevices();

const vacuum = devices.find((d): d is VacuumDevice => d instanceof VacuumDevice);
if (vacuum) {
  // Seed the cache with the vacuum's known properties (one live read).
  await vacuum.refreshProperties([...VacuumDevice.DEFAULT_PROPS]);

  // Typed, decoded state. Each getter returns null until the matching
  // property has landed (via the seed read above or a pushed update).
  console.log('status:', vacuum.status); // MiotState | null
  console.log('battery:', vacuum.battery); // number | null (%)
  console.log('suction:', vacuum.suction); // SuctionLevel | null
  console.log('water:', vacuum.water); // WaterVolume | null
  console.log('docked:', vacuum.isDocked);
  console.log('faults:', vacuum.faults); // number[]

  // Capability-gated commands (all async).
  await vacuum.setSuction(SuctionLevel.Max);
  await vacuum.startCleaning(); // begins a clean (NOT the lifecycle start())
  // await vacuum.pause();
  // await vacuum.stop();
  // await vacuum.dock();         // return to dock / charge
  // await vacuum.locate();       // make the robot beep
  // await vacuum.cleanSegments([1, 2]); // per-room, gated by canCleanPerRoom
}

await client.close();
```

> **`startCleaning()`没有 `start()`。 。 。 .** `start()` 是继承的生命周期 打开 MQTT 推力的方法; 清除命令是 `startCleaning()`。 。 。 . 所有指挥方法都是 `async`。 。 。 .

### 诚实的警告

-   **返回状态获取器 `null` 直到数据着陆.** 打电话 `refreshProperties([...VacuumDevice.DEFAULT_PROPS])` (或等被推倒) 在读取前更新; 未种子获取器是 `null`,以及一个外程生 值还解码为 `null` (原始整数仍可通过 `*Raw` 获得者,例如:. `vacuum.suctionRaw`) (中文(简体) ).
-   **`r2538z` 能力是假定的,没有经过核实.** 用户的 `dreame.vacuum.r2538z` 能力记录从其 `r2532a` (X50) (中文(简体) ) 师兄,所以 `vacuum.vacuumCapabilities.verified === false`。处理其特点 旗帜是最佳假说,直到确认在设备上.
-   **清末写作通过建筑安全.** `setCleaningMode()` 写入 简单 `CLEAN_MODE_SETTING` 属性(siid 2 piid 6); 未加工 `0x1400`\- 装模作样 bitfield(siid 4 piid 23)在此仅读取,从未直接写入.
-   **一些动作映射是假设的.** `pause`编号 :`stop`编号 :`locate`编号 :`clearWarning` 在r2532a兄弟身上有线核查; `startCleaning`编号 :`dock` 编辑 目标清洁有效载荷`cleanSegments`编号 :`cleanZones`编号 :`cleanSpot`) 已移植 从Tashack开始 还没有对所有模型进行现场验证.
-   **还没有地图生成状态 .** 每个房间/当前区域的数据和现场地图都来了 从映射层(第五阶段);它们不在第三阶段暴露.

## 汽车

对于 `dreame.mower.*` 型号, `discoverDevices()` 返回 a `MowerDevice` 联合国 普通手柄的子类),带有已打入、已解码的状态获取器和 能力命令,镜像真空表面.

```ts
import { Nodreame, MowerDevice } from '@apocaliss92/nodedreame';

const client = new Nodreame({
  username: process.env.DREAME_USERNAME!,
  password: process.env.DREAME_PASSWORD!,
  region: 'eu',
});

await client.login();
const devices = await client.discoverDevices();

const mower = devices.find((d): d is MowerDevice => d instanceof MowerDevice);
if (mower) {
  // Seed the cache with the mower's known properties (one live read).
  await mower.refreshProperties([...MowerDevice.DEFAULT_PROPS]);

  // Typed, decoded state. Each getter returns null until the matching
  // property has landed (via the seed read above or a pushed update).
  console.log('status:', mower.status); // MowerStatus | null
  console.log('battery:', mower.battery); // number | null (%)
  console.log('charging:', mower.charging); // MowerChargingStatus | null
  console.log('docked:', mower.isDocked); // boolean
  console.log('mowing:', mower.isMowing); // boolean
  console.log('task:', mower.task); // MowerTaskDescriptor | null (2:50)
  console.log('coverage target %:', mower.coverageTargetPct); // number | null
  console.log('control action:', mower.controlAction); // MowerControlAction | null

  // Capability-gated commands (all async).
  await mower.startMowing(); // begins mowing (NOT the lifecycle start())
  // await mower.pause();
  // await mower.stop();
  // await mower.dock();   // return to dock / charge
  // await mower.resume(); // resume after a pause (continueControl opcode)
  // Targeted starts (gated by the model's capability flags):
  // await mower.startMowingAllArea(mapId);   // whole map
  // await mower.startMowingZones([1, 3]);    // selected zones
  // await mower.startMowingEdges([[1, 0]]);  // edge / contour pairs
  // await mower.startMowingSpots([5]);       // spot areas
}

await client.close();
```

> **`startMowing()`没有 `start()`。 。 。 .** `start()` 是继承的生命周期 打开 MQTT 推力的方法; 剪切命令是 `startMowing()`全部数据 命令方法为 `async`。 。 。 .

### 诚实的警告

-   **返回状态获取器 `null` 直到数据着陆.** 打电话 `refreshProperties([...MowerDevice.DEFAULT_PROPS])` (或等被推倒) 在读取前更新; 未种子获取器是 `null`,以及一个外程生 值还解码为 `null` (原始整数仍可通过 `*Raw` 获得者,例如:. `mower.statusRaw`, (中文(简体) ), `mower.chargingRaw`, (中文(简体) ), `mower.taskStatusRaw`) (中文(简体) ).
-   **`dreame.mower.p2255` (Dreame A1)能力是假设的,没有核实.** 捐助方的一体化没有每个模型的割草机能力矩阵,因此 从指挥表面看,有目标地升起旗帜是一种保守的假设; `mower.mowerCapabilities.verified === false` 直到确认在设备上. 不支持的目标开始抛出 `DreameError`。 。 。 .
-   **进步是覆盖的尺度,而不是地图轨道.** `coverageTargetPct` 显示列表任务描述符的覆盖目标( q)`d.o`, 2:50) 字节精确的姿势/覆盖轨道几何和活图来自地图 在此不解码.

错误类别和核心域类型也被导出,以便消费者能够捕捉 和类型云失败 :

```ts
import {
  DreameError,
  DreameAuthError,
  DreameApiError,
  DreameDeviceOfflineError,
  DreameTransportError,
} from '@apocaliss92/nodedreame';
import type { DreameSession, DreameDevice, MiotProp } from '@apocaliss92/nodedreame';
```

## 地图

两个设备家庭都解码了他们的安装设备地图,并将其制作成图像。 这个 二进制/JSON解码器、开放源码软件签名接取器和每个中间步骤 保持私人状态——通过设备手柄和(可选的) 两个出炉器.

### 真空图-巴布亚新几内亚

`VacuumDevice.getMap()` 解决已保存/ 启用的地图块、 解密和充气 二进制信封,解析 27 字节标题和 `fsm:1` 像素网格,以及 返回结构 `VacuumMap` ——分会/会所,清洁道,AI 屏障、虚拟墙、禁行区/禁行区、潜行区、每室墙和 清理区域覆盖. `renderVacuumPng(map)` 把它给巴布亚新几内亚人 `Buffer` 通过 `pngjs`。 。 。 .

```ts
import { renderVacuumPng } from '@apocaliss92/nodedreame';
import type { VacuumMap } from '@apocaliss92/nodedreame';
import { writeFile } from 'node:fs/promises';

// `filename` is the OSS object name advertised on the map PATH push (siid 6,
// piid 3); resolve it from a `mapInfo` push before calling getMap().
const map: VacuumMap = await vacuum.getMap({ filename });
const png = renderVacuumPng(map); // Buffer (optional { scale } upscale)
await writeFile('map.png', png);

// The most-recently-decoded map is cached, and the active room id is derived:
vacuum.lastMap; // VacuumMap | null
vacuum.currentSegmentId; // number | null (id of the active segment)
```

> **活地地图数据需要一个醒地机器人.** 沉睡的真空无所新鲜 鲍勃. `getMap()` 解码单一框架; 连续活框 **P-框架 流线** (随着机器人的移动而合并三角框)是一个文件 后续行动—— `applyVacuumPFrame` 合并原始船只,并经过单位测试, 这样的工作是添加剂,而不是重写.

### 摩尔地图 → SVG

`MowerDevice.getMap()` 重新组合批次 `MAP.*` 编号 : `M_PATH.*` JSON块 把他们描述成 `MowerMap` 禁区、禁区、禁区, 导航路径、轮廓、割草路径和地图边界. `mower.mapSvg()` (或免费的) `renderMowerSvg(map)`)使SVG具有确定性 取出几何的字符串.

```ts
import { renderMowerSvg } from '@apocaliss92/nodedreame';
import type { MowerMap } from '@apocaliss92/nodedreame';
import { writeFile } from 'node:fs/promises';

const map: MowerMap = await mower.getMap();
const svg = await mower.mapSvg(); // or renderMowerSvg(map)
await writeFile('map.svg', svg);
```

> **同样的清醒机器人警告.** 没有新鲜货. 此外,混凝土 **有记录的活批量云端点 后续行动**:其道路在捐助方一体化中模糊不清,尚未 已恢复,所以默认的拾取者将抛出`getMap()` 然后,他们否认真理。 地图 **剖析器和 SVG 渲染器已完全运出并测试单元** 对批次 固定装置,以及 `MowerDevice` 接受注入的批量取取取器接合,所以 已经知道路径的呼叫者可以驱动今天的活地图.

### 归属

真空映射解码器从 [马拉德/节点-梦想](https://github.com/malard/node-dreame); 割草机地图 解析器和 SVG 渲染器 [antondaubert/冷冻机](https://github.com/antondaubert/dreame-mower)两者 麻省理工学院——见 `LICENSE`。 。 。 .

## 连通性

每个 `DreameDevice` 从 `listDevices()` (和后面的记录) `discoverDevices()`)搭载了蒸馏接通视图——所有 梦云实际报:

```ts
import { listDevices } from '@apocaliss92/nodedreame';

for (const d of await listDevices({ session, region: 'eu' })) {
  console.log(d.name, d.connectivity);
  // {
  //   online: true,
  //   connectionType: 'WIFI',            // or 'BLE' (mowers)
  //   mac, broker: { host, port },       // assigned MQTT broker (bindDomain)
  //   region, cloudVendor, firmwareVersion, serialNumber, subModel,
  //   battery, statusCode
  // }
}
```

`parseConnectivity(rawRecord)` 用于解析您已经持有的记录. 注:云录有 **没有** 暴露无线甚高频/ SSID/ 本地IP—— 这些 仅是本地公司的软件。 对于信号强度,使用下面的WiFi地图.

## WiFi 信号

机器人没有RSSI的活性属性;其唯一的信号数据是a **WiFi 覆盖 地图** 它在清洁时会建起 点头取出 **上次存储** 映射( 无) 机器人运动——应用程序显示的同样内容),解码每个细胞的信号 级别,并且——由于地图头带有机器人的姿势——报告信号在 机器人现在的位置.

```ts
const bars = await vacuum.getCurrentSignal();       // 0–4 (0 = unreached), or null
const png  = await vacuum.getWifiSignalImage();     // Buffer: heatmap PNG (ready to show)
const map  = await vacuum.getWifiSignalMap();       // full data:
//   map.dimensions {left,top,width,height,gridSize}
//   map.robot / map.dock  (poses, mm world frame)
//   map.cells            (Uint8Array, raw nibble per cell)
//   map.signalAt(x, y)   -> bars 1–4 / 0 unreached / null (no data / off-map)
//   map.currentSignal    -> bars at the robot pose
```

每个呼叫问题 `requestWMap` (地图服务行动) `6/4`以( 重) 缩写上 储存的地图、民意测验 `PropWifiMap` (单位:千美元)`6/15`用于 OSS 对象,并通过 正常的OSS地图管道。 信号级别 : `10` 未触及, `11`– 说吧 –`14` =: 1–4条栏. 如果设备从未建立 WiFi 映射, 则丢弃 .

## 相机和视频流

相机真空(X40/X50级,Link Visual/Aliyun后端), 点心 **自动冷启动活流** 没有Dreamehome应用软件,以及 任何消费者(ffmpeg、scripted、camstack)都可以使用H.264 + AAC框架 使用时.

### 如何运作(高级)

```
DreameCameraController.open()          DreameCameraStream            consumer
─────────────────────────────         ──────────────────           ────────
1. getAccessCodeLaunch (prime)   ┐
2. initCameraSdk (boot agent)    │  MIoT actions on siid 10001
3. startMonitor  ── code -1 ─────┤  over the device's MQTT channel
4. verifyAccessCode(sha256 PIN)  │  (the camera has a per-session
5. startMonitor (retry) ── ok ───┘  privacy gate: PIN is mandatory)
6. keep-alive loop (~10s)                    │
7. stream/query (Aliyun) → RTMP relay URL ───┤
                                             ▼
                              LvRtmpClient connects to the relay
                              (private-mode RTMP), demuxes FLV →
                              emits: videoAccessUnit (H.264 Annex-B),
                                     audioFrame (AAC/ADTS), audioInfo
                                             │
                                             ▼  (scrypted wraps these as RFC4571;
                                                camstack consumes frames directly)
```

这个 **访问码门** 是关键设备突变:一空 `startMonitor` 返回时 `code:-1`; 您必须首先 `verifyAccessCode` 与配对PIN的SHA-256, 接下来 `startMonitor` 被接受,阿里云中继薄荷则使用RTMP URL。 整个 顺序,保持活性, 和拆解由 `DreameCameraController`。 。 。 .

### 快开始

```ts
const controller = await vacuum.createCameraController({ accessCode: '0000' });
const { rtmpUrl } = await controller.open();   // cold-start → live relay URL (H.264+AAC)
// … hand rtmpUrl to ffmpeg, or use DreameCameraStream for frames …
await controller.close();                      // stops keep-alive + releases the monitor
```

框架级管线(凸起/被撕裂所依托):

```ts
import { DreameCameraStream } from '@apocaliss92/nodedreame';

const stream = new DreameCameraStream({ controller });
stream.on('videoAccessUnit', (au) => { /* au.data = H.264 Annex-B, au.isKeyframe */ });
stream.on('audioFrame', (buf) => { /* AAC ADTS */ });
await stream.start();   // cold-start + connect + emit frames
// … later …
await stream.stop();
```

### 相机动作(控制平面)

全部开始 `DreameCameraController`,大多数门开到开口的溪流:

| 方法 | 效果 |
| --- | --- |
| `driveDirection('forward'|'left'|'right'|'turnAround'|'stop')` 编号 : `drive(spdv, spdw)` | 远程驱动机器人( 保持时发送~ 1 Hz; 相机固定, 机器人移动) |
| `returnToDock()` • 妇女 `locate()` | 发送回家/ 哔声以定位 |
| `spotClean()` • 妇女 `startPersonFollow()` 编号 : `stopPersonFollow()` • 妇女 `findPet()` • 妇女 `goToPoint(sp, tp)` | 工作模式行动 |
| `stopWork()` | 通用停站(跟随/即地清扫/巡航) |
| `playPetSound('meow'|'bark'|'footsteps'|'purring'|'tickTock')` 编号 : `playSound(id)` | 播放声音剪辑 |
| `setFillLight(level)` 编号 : `setFillLightAuto(full)` | 充光亮度/ 全光亮亮亮度 |
| `takePhoto()` | 设备侧快照( 上载到云) |
| `startIntercom()` 编号 : `stopIntercom()` | 双向音频会话( 控制) |
| `runVacuumAction('startClean'|'pauseClean'|'stopClean'|'dockWash'|'autoEmpty')` | 共同的全机器人行动 |

### 双向对讲机(回声)

麦克风上行链路是 RTMP 类型 8 音频 在同一播放会话上向上推 (G.711 A-law, 8 kHz). 在溪流上:

```ts
await stream.startTalk();                 // MIoT intercom start + wait for TalkReady
stream.sendTalkPcm(pcm16le8kMono);        // encodes to A-law + pushes upstream
await stream.stopTalk();
```

### 对象检测

机器人自己跟随的人`10001/110`和障碍`10001/112`框为 驱动作为相机服务属性;用 `parsePersonFollow` 编号 : `parseObstacleData` (订阅设备的) `propertyChanged`) (中文(简体) ).

## 诊断堆(只读)

`nodedreame` 可以记录设备在运行和导出时暴露的内容 **匿名** JSON 您可以附加在 GitHub 上, 帮助映射无文件 代码(如割草机) `taskStatus` 2/3/10/13号文件。 垃圾箱很严格 **只读** —— 说 它从不发出命令 也从不唤醒机器人 它只订阅 设备的事件流( e)`on`编号 :`off`),读取缓存(`getProperty`),以及 取出云影来`refreshFromCache`) (中文(简体) ).

```ts
import { Nodreame, createDumper } from '@apocaliss92/nodedreame';

const client = new Nodreame({ username, password, region: 'eu' });
await client.login();
const [device] = await client.discoverDevices();

const dumper = createDumper(device);
await dumper.start(); // hooks the live stream + periodic cloud-shadow read
// ...operate the device normally for a few minutes...
await dumper.stop();

console.log(dumper.exportJson()); // pretty, anonymized — safe to share
await client.close();
```

它捕捉到的东西:每个财产 独特的价值集 `unmapped` 旗帜 (值与无已知的enum相匹配——最高值信号),MIoT事件,a 静态命令/能力目录。 设备/账号 代号, MAC、序列、Wi-Fi/IP、GPS、房间名称和任何自定义设备名称(全部) `[redacted]`) (中文(简体) ). `firmware`编号 :`region` 省略( 不被设备浮出水面) 手柄尚未打开).

立即将整个账户用 `createClientDumper(client)`,返回一个 每个发现装置的倾卸器:

```ts
import { createClientDumper } from '@apocaliss92/nodedreame';

const dumpers = createClientDumper(client);
await Promise.all(dumpers.map((d) => d.start()));
// ...observe...
await Promise.all(dumpers.map((d) => d.stop()));
const dumps = dumpers.map((d) => d.exportJson());
```

将导出 JSON 共享到库中 表格来标记新代码并将其折叠到库中.

## 安装

```bash
npm install @apocaliss92/nodedreame
```

## 许可证

麻省理工学院. 前端口从 [塔斯夏克/德雷姆真空](https://github.com/Tasshack/dreame-vacuum), (中文(简体) ), [antondaubert/冷冻机](https://github.com/antondaubert/dreame-mower),以及 [马拉德/节点-梦想](https://github.com/malard/node-dreame); 见 `LICENSE`。 。 。 .

## 常见问题

### @apocaliss92/nodedreame 是什么？

用于 Dreame 真空和割草机的节点\.js/TypeScript 客户端

### 如何安装 @apocaliss92/nodedreame？

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

### @apocaliss92/nodedreame 支持 TypeScript 吗？

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

### @apocaliss92/nodedreame 支持 ESM 还是 CommonJS？

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

### @apocaliss92/nodedreame 需要什么版本的 Node\.js？

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

### @apocaliss92/nodedreame 可以商用吗？

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

### @apocaliss92/nodedreame 的下载量有多少？

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

### @apocaliss92/nodedreame 还在维护吗？

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

