# @constructorfleet/sketchforge3d 中文文档

规范地址：https://npmcn.com/package/%40constructorfleet/sketchforge3d/

版本：1\.30\.0
npm 元数据更新时间：2026-10-28T04:27:11.323Z
中文译文更新时间：2026-11-10T16:21:08.678Z
软件包许可：AGPL-3\.0-only

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

原始 npm 页面：https://www.npmjs.com/package/%40constructorfleet/sketchforge3d
原文对照：https://npmcn.com/package/%40constructorfleet/sketchforge3d/?lang=original
项目主页：https://sketchforge3d.com/
源码仓库：https://github.com/constructorfleet/SketchForge-3D

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

- 包体积（解压后）：417 kB，134 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：3 个
- 首次发布：2026-10-20T09:29:15.338Z
- 最新版本发布：2026-10-28T04:27:10.929Z
- 近 30 天下载量：5074（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：32（近 12 个月 32 个）

## 中文简介

为 SketchForge REST API 键入客户端和宏跑取器.

## 安装

```sh
npm install @constructorfleet/sketchforge3d
```

也可以使用：`pnpm add @constructorfleet/sketchforge3d`、`yarn add @constructorfleet/sketchforge3d`、`bun add @constructorfleet/sketchforge3d`

## README

# @ builtctorfleet/ sketchforge3d

输入客户端 [缩写Forge REST API](https://npmcn.com/package/docs/REST_API.md),并有一个跑者 **宏**: 短参数化脚本来构建一个部分.

```bash
npm install @constructorfleet/sketchforge3d
```

API是无头的——它在服务器侧存储器中读取并写出工程,没有打开浏览器——所以一个宏是设计用的构建脚本,一个Makefile是二进制的.

## MCP桥

此软件包还搭载了 SketchForge MCP 服务器. 有地方性的 默认的工程文件; LLM 可以通过 `url` 个人工具呼叫 瞄准运行中的 SketchForge 服务器 :

```json
{
  "mcpServers": {
    "sketchforge": {
      "command": "npx",
      "args": ["-y", "-p", "@constructorfleet/sketchforge3d", "sketchforge-mcp"],
      "env": { "SKETCHFORGE_PROJECTS_DIR": "/path/to/SketchForgeProjects" }
    }
  }
}
```

它会说Stio JSON -RPC。 无 `url`,项目和几何工具使用 本地引擎和 `SKETCHFORGE_PROJECTS_DIR`,没有浏览器、 HTTP 服务器,或 API标志. 过 `url: "http://127.0.0.1:3000"` 到工具调用运行 服务器;然后编辑工具需要打开编辑器标签(`/?editor=1`) (中文(简体) ). 远程 REST业务也要求 `SKETCHFORGE_API_TOKEN` 在多边协商进程中 环境.

两个工具运行 _宏_ - 这个软件包的运行符执行的脚本 -- 并且只有文件已经坐在 `SKETCHFORGE_MACRO_DIR`。这是故意的 没有接受代码运行的工具 。 Python 宏需求 `sketchforge3d` 已安装 从 PyPI 中, 因为只有退货 带有 Python 源.

## 连接中

```ts
import { SketchForgeClient } from "@constructorfleet/sketchforge3d";

const client = new SketchForgeClient({
  baseUrl: "http://localhost:3000",       // with or without /api/v1
  token: process.env.SKETCHFORGE_API_TOKEN!,
});

const health = await client.check();
if (!health.ok) throw new Error(health.reason);
```

`clientFromEnvironment()` 执行同样从 `SKETCHFORGE_API_TOKEN` 财务报告和审定财务报表 `SKETCHFORGE_URL` (默认) `http://127.0.0.1:3000`) (中文(简体) ). 偏好:指使是服务器上每个项目的密码,不属于脚本或 shell 历史.

服务器需要两个 `SKETCHFORGE_API_TOKEN` 财务报告和审定财务报表 `SKETCHFORGE_PROJECTS_DIR` 设置,或 API 分别回答 404 和 503 . `check()` 告诉你.

## 客户

```ts
new SketchForgeClient({ baseUrl, token, fetch?, timeoutMs?, retryOnBusy?, headers?, expectedRevision? })

client.apiRoot: string
client.check(): Promise<{ ok: true; projects: number } | { ok: false; reason: string }>
client.project(projectId): ProjectHandle

client.projects.list(): Promise<ProjectSummary[]>
client.projects.listPage({ limit?, cursor? }): Promise<{ items: ProjectSummary[]; nextCursor: string | null }>
client.projects.get(projectId): Promise<ProjectDetail>
client.projects.create(name): Promise<ProjectSummary>
client.projects.delete(projectId): Promise<DeleteResult>
client.projects.findByName(name): Promise<ProjectSummary | null>

client.shapes.list(projectId): Promise<ShapeSummary[]>
client.shapes.listPage(projectId, { limit?, cursor? }): Promise<{ items: ShapeSummary[]; nextCursor: string | null }>
client.shapes.create(projectId, shape): Promise<ShapeSummary>
client.shapes.createMany(projectId, shapes): Promise<ShapeWriteResult>
client.shapes.update(projectId, shapeId, changes): Promise<ShapePatchResult>
client.shapes.delete(projectId, shapeId): Promise<DeleteResult>
client.shapes.deleteAll(projectId): Promise<number>

client.edges.list(projectId, shapeId): Promise<EdgeSummary[]>
client.edges.filletableIndexes(projectId, shapeId): Promise<number[]>

client.treatments.apply(projectId, shapeId, { kind, amount, angle?, edges? }): Promise<TreatmentResult>
client.treatments.fillet(projectId, shapeId, radius, edges?): Promise<TreatmentResult>
client.treatments.chamfer(projectId, shapeId, distance, { angle?, edges? }): Promise<TreatmentResult>

client.faces.list(projectId, shapeId, { topology? }?): Promise<FaceSummary[]>
client.faces.facing(projectId, shapeId, direction): Promise<number[]>

client.measurements.list(projectId, shapeId): Promise<MeasurementSummary[]>
client.measurements.withRadius(projectId, shapeId): Promise<MeasurementSummary[]>
client.measurements.between(projectId, from, to): Promise<MeasurementPair>
client.properties.get(projectId, shapeId): Promise<ShapeProperties>
client.interference.check(projectId, { ids?, clearance? }): Promise<InterferenceReport>
client.mirrors.bake(projectId, ids, axis): Promise<BakedMirrorResult>
client.explosions.explode(projectId, shapeId): Promise<ShapeWriteResult>
client.thickness.estimate(projectId, shapeId): Promise<WallThicknessEstimate>
client.drawings.dxf(projectId, { plane, offset, shapeIds?, name? }): Promise<SectionDxfArtifact>
client.exports.export(projectId, { format, shapeIds?, name?, view?, featureAngle?, section? }): Promise<ProjectExportArtifact>

client.booleans.cut(projectId, { solidIds, holeIds }): Promise<BooleanCutResult>
client.groups.group(projectId, ids): Promise<GroupResult>
client.groups.ungroup(projectId, shapeId): Promise<ShapeWriteResult>
client.parts.separate(projectId, shapeId): Promise<ShapeWriteResult>

client.shell.apply(projectId, shapeId, { thickness, openFaces? }): Promise<ShellResult>
client.shell.hollow(projectId, shapeId, thickness): Promise<ShellResult>
client.shell.tray(projectId, shapeId, thickness, openFaces?): Promise<ShellResult>
client.draftPrism.apply(projectId, shapeId, angle): Promise<TreatmentResult>

client.align.apply(projectId, { ids, axis, target, anchorId? }): Promise<AlignResult>
client.align.tops(projectId, ids, anchorId?) | bottoms(...) | centers(projectId, ids, axis, anchorId?)

client.distribute.apply(projectId, { ids, axis?, spacing? }): Promise<DistributeResult>
client.distribute.gaps(projectId, ids, axis?) | pitch(projectId, ids, axis?)

client.patterns.apply(projectId, { ids, kind?, count?, stepX?, stepZ?, centerX?, centerZ?, sweepAngle?, rotateCopies? }): Promise<PatternResult>
client.patterns.row(projectId, ids, count, stepX, stepZ?): Promise<PatternResult>
client.patterns.ring(projectId, ids, count, { x, z }, { sweepAngle?, rotateCopies? }?): Promise<PatternResult>

client.imports.apply(projectId, { fileName, bytes, mediaType? }): Promise<MeshImportResult>
client.imports.readable(fileName): boolean
```

设定 `expectedRevision` 从项目上一个回复发送的修改 `If-Match: "<revision>"` 每一个写请求。 呆滞价值上升 `SketchForgeApiError` 有状态 `409` 代码 `revision_mismatch`; 错误 `revision` 是当前项目的修订。 使用一个客户端配置用于一次一个有条件的写入,然后为下一次写入创建一个带有返回版本的新版本.

`shapes.create` 接受 `SketchSweepShapeRequest` 用于服务器所构建的草图 扫地取道. 内容 `profile` 包含封闭的路由,一个开口的脊椎,以及 选项 `sweepFrames`;该答复保留该数据为 `sketchProfile`。 。 。 . 也让马厩回归 `sketchSourceId` 用于项目特性元数据. REST API 接受重建持续草图扫描的配置更新 。 发送 替换 `sketchProfile` 改为 `PATCH /api/v1/shapes`,针对任一 输出形状 ID 或其 `sketchSourceId`; 重建保留同样的形状 还有源码 见 [REST 扫描更新合同](https://npmcn.com/package/docs/REST_API.md#sketch-sweeps)。 。 。 .

A 类 `ProjectHandle` 与工程 id 绑定是同一个表面,这就是宏通过:

```ts
project.get() | delete() | listShapes() | add(shape) | addMany(shapes) | update(id, changes)
project.remove(id) | clear() | edges(id) | filletableEdges(id)
project.treat(id, treatment) | fillet(id, radius, edges?) | chamfer(id, distance, options?)
project.faces(id, { topology? }?) | facesFacing(id, direction)
project.measurements(id) | measurementsWithRadius(id) | measureBetween(from, to) | propertiesOf(id) | interferenceReport({ ids?, clearance? })
project.sectionDrawing({ plane: "xy", offset: 3, shapeIds? })
project.sectionDxf({ plane: "xy", offset: 3, shapeIds? }) | estimateWallThickness(id) | explodeStepAssembly(id)
project.export({ format, shapeIds?, name?, view?, section? })
project.cut({ solidIds, holeIds }) | group(ids) | ungroup(id) | separate(id)
project.mirrorBaked(ids, axis)
project.shell(id, { thickness, openFaces? }) | hollow(id, thickness) | tray(id, thickness, openFaces?)
project.draftPrism(id, angle)
project.align({ ids, axis, target }) | alignTops(ids, anchorId?) | distribute({ ids, axis?, spacing? })
project.pattern(request) | row(ids, count, stepX, stepZ?) | ring(ids, count, { x, z }, options?)
project.importMesh({ fileName, bytes })
```

形状被描述为参数,每个类型有一个结合,所以属于另一个类型的一个字段是一个编译错误:

```ts
await project.addMany([
  { kind: "box", name: "Plate", width: 120, depth: 40, height: 6 },
  { kind: "cylinder", width: 8, height: 12, sides: 6, x: -40, elevation: -1, hole: true },
  { kind: "thread", size: "M8", length: 10, hole: true, x: 40 },
  { kind: "revolve", profile: [[8, 0], [10, 0], [10, 2], [8, 2]], pitch: 6, turns: 6 },
]);
```

`addMany` 是每个请求的项目包的一则写出,拆分列表比服务器的200形状限制更长——这也意味着更长的列表不再是原子了.

保留草图扫描,替换 `sketchProfile` 用于重建固件,同时保持相同的形状和 source-sketch ID:

```ts
const sweep = await client.shapes.create(projectId, { kind: "sketchSweep", profile });
const widerProfile = {
  ...sweep.sketchProfile!,
  points: sweep.sketchProfile!.points.map((point) =>
    point.sweepRole === "section" ? { ...point, x: point.x * 1.1 } : point,
  ),
};
await client.shapes.update(projectId, sweep.id, { sketchProfile: widerProfile });
```

`exports.export` 涵盖格式 `PROJECT_EXPORT_FORMATS` - 说吧 `stl`, (中文(简体) ), `obj`, (中文(简体) ), `3mf`, (中文(简体) ), `step`, (中文(简体) ), `skf`, (中文(简体) ), `zip`, (中文(简体) ), `svg` 财务报告和审定财务报表 `dxf` -- 并返回整个文件 `data: Uint8Array`,与服务器的 `filename`, (中文(简体) ), `mimeType`, (中文(简体) ), `bytes` 财务报告和审定财务报表 `shapeCount`。STEP出口也报告 `exportedCount` 编辑 `skipped` 形状不能写成精确的固体. `view` (单位:千美元) `EXPORT_VIEWS`)仅适用于SVG, `featureAngle` 在 SVG 预测( 默认 30; 精确的固体忽略它) 上设置网格折叠角从 0 到 180 度 ,以及 `section` (单位:千美元)`{ plane, offset }`)适用于SVG和DXF. 客户端在发送任何信息之前拒绝不支持的选项组合, 并带有服务器自己的消息 :

```ts
import { writeFile } from "node:fs/promises";

const stl = await project.export({ format: "stl", name: "bracket" });
await writeFile(stl.filename, stl.data);
const top = await project.export({ format: "svg", view: "top" });
```

`client.faces.list(projectId, shapeId, { topology: "exact" })` 财务报告和审定财务报表 `project.faces(shapeId, { topology: "exact" })` 选择分析面孔,包括 平地转动。 选择选项保留默认网格列表; `topology: "mesh"` 明确选择它.

`ShapePatch` 还接受 `faceDraft` 在草图上保留一张面部草稿 源头脸是用它的稳定身份命名的 `GET /faces`;草案存储了已签名的本地拉轴和角度。 配置文件只当一个平面符合其原常态时才会编辑重放. 发送 `faceDraft: null` 以清除行动. `.skf` 保留了操作和源框;棱镜草稿和边缘处理尚不能与之结合.

失败的是 `SketchForgeApiError` (单位:千美元)`status`, (中文(简体) ), `detail`附加时 `looksDisabled`, (中文(简体) ), `needsStorage`, (中文(简体) ), `isProjectBusy`,以及 `isLimitExceeded`)或(或) `SketchForgeTransportError`。 。 。 . `isLimitExceeded` 承认 `413 limit_exceeded` 遗产 `too_many_shapes` 代码。 丢失项目锁的文字经过重审; a _锁定形状_答案为409, 原因无关, 而不是.

## 宏

宏是一个文件, 它会输出一个定义, 当它被传递到一个连接的工程时会构建某个东西 :

```js
// macros/bolt-plate.macro.mjs
export default {
  name: "Bolt plate",
  project: { mode: "replace", name: "Bolt plate" },
  params: { holes: 5, spacing: 25, size: "M8", thickness: 8 },

  async run({ project, params, layout, log }) {
    log(`${params.holes} ${params.size} holes`);
    await project.addMany([
      { kind: "box", name: "Plate", width: 200, depth: 40, height: params.thickness },
      ...layout.row(params.holes, params.spacing).map((at, index) => ({
        kind: "thread",
        name: `hole ${index + 1}`,
        size: params.size,
        length: params.thickness + 2,
        hole: true,
        x: at.x,
        z: at.z,
        elevation: -1,
      })),
    ]);
  },
};
```

```bash
SKETCHFORGE_API_TOKEN=... npx sketchforge-macro macros/bolt-plate.macro.mjs --holes=7 --spacing=30
```

环境包含着宏观需要的一切 -- `client`, (中文(简体) ), `project`, (中文(简体) ), `params`, (中文(简体) ), `layout`, (中文(简体) ), `log` - 因此,宏文件不导入任何内容,并且可以在任何文件夹中存在.

### 直接指挥线业务

用同样的 `sketchforge-macro` 用于项目管理和文件传输而无需写入宏的可执行文件 :

```bash
npx sketchforge-macro projects list
npx sketchforge-macro projects create "Bracket"
npx sketchforge-macro projects delete <project-id>
npx sketchforge-macro export --format stl --out bracket.stl --project <project-id>
npx sketchforge-macro import bracket.stl
```

出口用途 `--project` 或者说 `SKETCHFORGE_PROJECT_ID`。当给定时,导入到选定的项目中;如果没有一个,它就会创建以文件命名的项目。 直接文件导入支持STL,OBJ,和SVG;导出支持STL,OBJ,3MF,STEP,SKF,ZIP,SVG,和DXF.

### 项目模式和为什么没有默认

第二次运行是使宏观有用或危险的. 一个附加在它每次发现的一个项目上的脚本将部分翻了一倍,在打开项目之前没有人会注意到. 因此模式被宣布,而跑者,而不是宏体,则对它采取行动:

| `project` | 跑者会先做什么 | 用它来 |
| --- | --- | --- |
| `"fresh"` | 创建新工程, 名称随时间而印出 | 由您生成若干比较 |
| `{ mode: "replace", name }` | 发现项目和 **虚空** 或创造它 | 脚踏实地的部分; 重跑是一能的 |
| `{ mode: "reuse", name }` | 发现或创造它,而留下它的内容 | 将一个特性添加到任何事物中 |
| `"target"` | 要求数 `SKETCHFORGE_PROJECT_ID` 或者说 `--project`,不要碰别的东西 | 在其它命名的项目上运行 |

一个输出一个空函数而不是定义的模块运行在 `fresh` project,因为这是唯一可以安全假设的模式.

### 参数

`params` 既是默认,也是计划。 命令行覆盖被强制用于其默认类型,宏不声明的名称被拒绝 -- -- 所以 `--hoels=7` 是一个错误,而不是一个默默被忽略的字典。 参数在项目创建前就解决了,所以一个坏的不费钱.

## 实例

`examples/` 持有四个有效的宏,作为整合测试的双倍:

| 宏 | 构建 |
| --- | --- |
| `bolt-plate.macro.mjs` | 一个盘子大小 绕一排计算出来的被窃听的孔 |
| `bolt-circle.macro.mjs` | 半径R的一圈有被打孔的花瓶 |
| `filleted-box.macro.mjs` | 一个来自三个数字的盒子,每个可圆形边缘都填入服务器 |
| `tap-drill-plate.macro.mjs` | 用M8线穿过的盘子切 `project.cut`,导出为 STL |

纹孔是 `kind: "thread"` 与 `hole: true`:表型被清除后切得更宽,外形被标记为出洞,因此在编辑器中或经过中与板块相组合 `project.group` 敲打盘子. `project.cut` 也可以直接去掉线网. `tap-drill-plate.macro.mjs` 通过 API 运行整个剪接和输出工作流程,并配有 M8 线程剪接器.

## 发展

```bash
npm run client:build       # compile to dist/, from the repository root
npm run client:typecheck   # including the tests
npm test                   # unit tests, in the repository's vitest run
npm run test:integration   # the examples, against a live server
```

集成测试需要一个真正的服务器, 而不是其中的一部分 `npm test`数字 :

```bash
SKETCHFORGE_PROJECTS_DIR=$(mktemp -d) SKETCHFORGE_API_TOKEN=test-token \
  npx next dev apps/web -p 3100 &
SKETCHFORGE_API_TOKEN=test-token SKETCHFORGE_URL=http://127.0.0.1:3100 npm run test:integration
```

## 出版

见 [文件/资料](https://npmcn.com/package/docs/PUBLISHING_CLIENTS.md) 在发布工作流程发布此软件包之前, npm 上必须配置的内容.

### 本地 MCP 模式

MCP直接对项目文件使用被提取的引擎包,没有浏览器,HTTP服务器或API令牌. 设定 `SKETCHFORGE_PROJECTS_DIR` 中包含 `<projectId>.json` 财务报告和审定财务报表 `<projectId>.skf` 文件 :

```bash
SKETCHFORGE_PROJECTS_DIR="$HOME/SketchForgeProjects" npm run mcp:sketchforge
```

目录使用相同的 `<projectId>.json` 财务报告和审定财务报表 `<projectId>.skf` 文件作为服务器侧项目存储。 在本地模式下, 使用 `sketchforge_list_projects` 发现项目, `sketchforge_create_project` 开始空的,或 `sketchforge_import_project` 带有本地 `.skf` `filePath` 以添加已有的软件包. `sketchforge_import_file` 进口 `.stl`, (中文(简体) ), `.obj`,或 `.svg` 文件输入选定的工程;其选项是 `projectId`, (中文(简体) ), `filePath`选择 `url`。源文件名提供导入的形状名称,颜色和位置使用输入器默认值。 与一个 `url`,将文件从 MCP 主机读取并作为基数64 发送到 `POST /api/v1/imports` 使用 `SKETCHFORGE_API_TOKEN`。 。 。 . `sketchforge_import_stl` 仍然是本地唯一兼容的化名, 也可以接受 `name`, (中文(简体) ), `color`, (中文(简体) ), `x`, (中文(简体) ), `z`,以及 `elevation`它拒绝 `url`。当地进出口路径仅限于: `SKETCHFORGE_MCP_FILES_DIR`,或改为 `SKETCHFORGE_PROJECTS_DIR` 当新设置未设置时。 设定 `overwrite: true` 以替换已存在的导出;否则将保留已存在的文件。 对于相容性,当两个根变量都未被设置时,路径不受限制. 在检查文件根之前解决了共通性 。 需要时会创建配置的目录 。 这些工具返回新的 `projectId` 其他项目工具. `sketchforge_read_scene` 接受 `shapeIds`, (中文(简体) ), `excludeShapeIds`, (中文(简体) ), `includeRawShapes`,以及 `includeRawShapeIds`; 原网格几何默认被省略,可以请求用于所选形状. 变异工具返回压缩形状元数据,同时 `sketchforge_list_objects` 总是返回对象摘要。 STL, OBJ, 3MF, ZIP- kit, SKF, STEP, SVG 出口可以写到 `outputPath`;工程名称提供默认文件名. `sketchforge_project_view` 返回 SVG 绘图本身 – 内核对所存储几何的隐藏行投影,作为四视图网格或任何一种命名的视图 – 这是一个没有视觉的代理的可读部分的图片 . `sketchforge_section_view` 返回区域轮廓为 SVG `xy`, (中文(简体) ), `xz`,或 `yz` 平面和平面;平面必须跨越选定的几何。 精确的外形被内核切出,网状外形被直接切出,在整个选择中可高达2,000,000个网状三角.

本地模式涵盖项目读取和编辑、形状创建/进口、安排、镜像、分组、布林斯、边缘和面部操作、测量和每个形状的质量属性、空心、网格和STEP导出、场景地貌、清理和交叉检查以及JavaScript宏。 图像捕捉光线将相同的图画画给PNG(一个命名的面是一景; `home` 财务报告和审定财务报表 `current` 是网格;它不捕捉编辑器铬或活式取景相机。 npm MCP客户端和 `@constructorfleet/sketchforge-engine` 由发布工作流程一起发布。 编辑器发现和编辑器错误检查需要URL和开放编辑器. Python宏需要URL和HTTP适配器.

## 常见问题

### @constructorfleet/sketchforge3d 是什么？

为 SketchForge REST API 键入客户端和宏跑取器\.

### 如何安装 @constructorfleet/sketchforge3d？

在项目目录运行 npm install @constructorfleet/sketchforge3d，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 sketchforge-mcp、sketchforge-macro，可以用 npx sketchforge-mcp 直接运行。

### @constructorfleet/sketchforge3d 支持 TypeScript 吗？

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

### @constructorfleet/sketchforge3d 支持 ESM 还是 CommonJS？

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

### @constructorfleet/sketchforge3d 需要什么版本的 Node\.js？

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

### @constructorfleet/sketchforge3d 可以商用吗？

@constructorfleet/sketchforge3d 使用 AGPL-3\.0-only 许可。最严格的 Copyleft：除分发外，通过网络向用户提供基于它的服务也需要开源对应源码。闭源 SaaS 使用前务必评估。以上为概括，不构成法律意见，请以许可原文为准。

### @constructorfleet/sketchforge3d 的下载量有多少？

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

### @constructorfleet/sketchforge3d 还在维护吗？

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

