面向 Apple Silicon macOS 的 DeepSeek HarnessDSH非官方本地会议转写插件。
数据截至 2026/10/31 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
dsh plugin --profile web add huliux/dsh-asr-plugin状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
安装兼容性检查需源码安装engines.node 要求 >=24.0.0 <25,不满足 Node 22.19.0;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/29 11:39:02
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
查看扫描证据(共 96 处,展示前 83 条)
- 写入剪贴板site/copy.js:6
await navigator.clipboard.writeText(text); - 读取本地文件src/audio/closed-recording-chunks.ts:77
const canonical = await realpath(path); - 读取本地文件src/audio/closed-recording-chunks.ts:79
const info = await stat(canonical); - 读取本地文件src/audio/closed-recording-chunks.ts:133
const info = await stat(known.id); - 读取本地文件src/audio/closed-recording-chunks.ts:148
const entries = await readdir(root, { withFileTypes: true }); - 读取本地文件src/audio/closed-recording-chunks.ts:155
const path = await realpath(join(root, entry.name)); - 读取本地文件src/audio/closed-recording-chunks.ts:169
const recordingRoot = await realpath(recordingDirectory); - 读取本地文件src/audio/managed-audio-normalizer.ts:113
stats = await lstat(filePath); - 写入本地文件src/audio/managed-audio-normalizer.ts:201
await chmod(input.audioTemporaryPath, 0o600); - 写入本地文件src/audio/managed-audio-normalizer.ts:203
await rename(input.audioTemporaryPath, input.audioPath); - 删除本地文件src/audio/managed-audio-normalizer.ts:216
await rm(input.audioTemporaryPath, { force: true }).catch((error: unknown) => { - 写入本地文件src/audio/managed-audio-normalizer.ts:226
await copyFile(input.sourcePath, input.audioTemporaryPath, constants.COPYFILE_EXCL); - 删除本地文件src/audio/managed-audio-normalizer.ts:236
await rm(input.audioTemporaryPath, { force: true }).catch(() => undefined); - 删除本地文件src/audio/recording-audio-candidate.ts:121
await rm(path, { force: true }); - 删除本地文件src/audio/recording-audio-candidate.ts:131
await rm(path, { force: true }).catch(() => undefined); - 读取本地文件src/audio/recording-audio-candidate.ts:144
for await (const chunk of createReadStream(path)) { - 删除本地文件src/audio/recording-audio-candidate.ts:161
await rm(destination, { force: true }); - 写入本地文件src/audio/recording-audio-candidate.ts:162
await copyFile(source, destination, constants.COPYFILE_FICLONE); - 写入本地文件src/audio/recording-audio-candidate.ts:163
await chmod(destination, 0o600); - 删除本地文件src/audio/recording-audio-candidate.ts:180
await Promise.all(CANDIDATE_FILES.map((name) => rm(join(directory, name), { force: true }))); - 删除本地文件src/audio/recording-audio-candidate.ts:187
await Promise.all(tracks.map(async (track) => rm( - 写入本地文件src/audio/recording-audio-candidate.ts:201
await mkdir(paths.workRecordingDirectory, { recursive: true, mode: 0o700 }); - 写入本地文件src/audio/recording-audio-candidate.ts:202
await chmod(paths.workRecordingDirectory, 0o700); - 读取本地文件src/audio/recording-audio-candidate.ts:230
const sourceSizeBytes = (await stat(audioPath)).size; - 读取本地文件src/audio/recording-audio-candidate.ts:262
const canonicalDirectory = await realpath(directory); - 读取本地文件src/audio/recording-audio-candidate.ts:264
const info = await lstat(path); - 读取本地文件src/audio/recording-audio-candidate.ts:265
if (!info.isFile() || await realpath(path) !== path) { - 读取本地文件src/audio/recording-audio-candidate.ts:278
await lstat(path); - 写入本地文件src/audio/recording-audio-candidate.ts:342
await rename(inspected[index + 1]!.canonical, join(paths.recordingDirectory, `${track}.wav`)); - 写入本地文件src/audio/recording-audio-candidate.ts:345
await rename(audio.canonical, sourcePath); - 写入本地文件src/index.ts:93
await mkdir(databaseDirectory, { recursive: true, mode: 0o700 }); - 写入本地文件src/index.ts:94
await chmod(databaseDirectory, 0o700); - 读取本地文件src/recording/final-cache.ts:94
const info = await lstat(path); - 读取本地文件src/recording/final-cache.ts:96
return await readFile(path); - 删除本地文件src/recording/final-cache.ts:104
await rm(temporary, { force: true }); - 写入本地文件src/recording/final-cache.ts:106
await writeFile(temporary, bytes, { flag: "wx", mode: 0o600 }); - 写入本地文件src/recording/final-cache.ts:107
await rename(temporary, path); - 删除本地文件src/recording/final-cache.ts:109
await rm(temporary, { force: true }).catch(() => undefined); - 写入本地文件src/recording/final-cache.ts:179
await mkdir(directory, { recursive: true, mode: 0o700 }); - 写入本地文件src/recording/final-cache.ts:180
await chmod(directory, 0o700); - 写入本地文件src/recording/helper-client.ts:108
await mkdir(directory, { recursive: true, mode: 0o700 }); - 写入本地文件src/recording/helper-client.ts:118
await rename(temporary, path); - 删除本地文件src/recording/helper-client.ts:122
await unlink(temporary).catch(() => undefined); - 读取本地文件src/recording/permissions.ts:71
parent = await realpath(await mkdtemp(join(tmpdir(), "dsh-asr-permission-"))); - 写入本地文件src/recording/permissions.ts:74
await mkdir(root, { mode: 0o700 }); - 删除本地文件递归src/recording/permissions.ts:77
try { if (parent !== undefined) await rm(parent, { recursive: true, force: true }); } - 写入本地文件src/recording/permissions.ts:89
const cancel = () => { void writeFile(join(root, "cancel"), "", { mode: 0o600 }).catch(() => undefined); }; - 读取本地文件src/recording/permissions.ts:98
const raw = await readFile(join(root, "permissions.json"), "utf8"); - 写入本地文件src/recording/permissions.ts:107
await writeFile(join(root, "cancel"), "", { mode: 0o600 }).catch(() => undefined); - 读取本地文件src/recording/worker-runtime.ts:141
const child = await realpath(join(root, ...parts)); - 读取本地文件src/recording/worker-runtime.ts:143
if (!(await stat(child)).isDirectory()) throw new Error("not a directory"); - 写入本地文件src/storage/data-root-lease.ts:12
await mkdir(dataRoot, { recursive: true, mode: 0o700 }); - 写入本地文件src/storage/data-root-lease.ts:13
await chmod(dataRoot, 0o700); - 写入本地文件src/storage/database.ts:139
chmodSync(filename, 0o600); - 读取本地文件src/storage/managed-audio-store.ts:178
const stats = await lstat(sourcePath); - 读取本地文件src/storage/managed-audio-store.ts:187
const stats = await lstat(sourcePath); - 写入本地文件src/storage/managed-audio-store.ts:234
await mkdir(paths.meetingDirectory, { recursive: true, mode: 0o700 }); - 写入本地文件src/storage/managed-audio-store.ts:235
await mkdir(paths.workDirectory, { recursive: true, mode: 0o700 }); - 写入本地文件src/storage/managed-audio-store.ts:236
await chmod(paths.meetingDirectory, 0o700); - 写入本地文件src/storage/managed-audio-store.ts:237
await chmod(paths.workDirectory, 0o700); - 删除本地文件src/storage/managed-audio-store.ts:238
await rm(paths.sourceTemporaryPath, { force: true }); - 写入本地文件src/storage/managed-audio-store.ts:251
await rename(paths.sourceTemporaryPath, paths.sourcePath); - 删除本地文件src/storage/managed-audio-store.ts:262
await rm(paths.sourceTemporaryPath, { force: true }).catch(() => undefined); - 删除本地文件递归src/storage/managed-audio-store.ts:289
await rm(workRoot, { force: true, recursive: true }); - 写入本地文件src/storage/managed-audio-store.ts:290
await mkdir(workRoot, { recursive: true, mode: 0o700 }); - 删除本地文件递归src/storage/managed-audio-store.ts:300
await rm(workDirectory, { force: true, recursive: true }); - 删除本地文件递归src/storage/managed-audio-store.ts:310
await rm(paths.meetingDirectory, { force: true, recursive: true }); - 删除本地文件递归src/storage/managed-audio-store.ts:311
await rm(paths.workDirectory, { force: true, recursive: true }); - 读取本地文件src/storage/managed-audio-store.ts:466
const dataRoot = await realpath(options.dataRoot); - 读取本地文件src/storage/managed-file-tree.ts:7
stats = await lstat(path); - 读取本地文件src/storage/managed-file-tree.ts:14
const entries = await readdir(path); - 写入本地文件src/storage/sqlite-exclusive-lease.ts:45
await chmod(path, 0o600); - 读取本地文件src/transcript-projection/transcript-export-publisher.ts:57
realpath(input.dataRoot), - 读取本地文件src/transcript-projection/transcript-export-publisher.ts:58
realpath(dirname(input.outputPath)), - 读取本地文件src/transcript-projection/transcript-export-publisher.ts:60
if (!(await stat(parent)).isDirectory()) { - 读取本地文件src/transcript-projection/transcript-export-publisher.ts:75
const value = await lstat(outputPath); - 写入本地文件src/transcript-projection/transcript-export-publisher.ts:158
if (input.overwrite) await rename(temporaryPath, outputPath); - 删除本地文件src/transcript-projection/transcript-export-publisher.ts:161
await unlink(temporaryPath); - 删除本地文件src/transcript-projection/transcript-export-publisher.ts:173
if (temporaryPath !== undefined) await unlink(temporaryPath).catch(() => undefined); - 读取本地文件src/worker/managed-audio.ts:15
const canonical = await realpath(directory); - 读取本地文件src/worker/managed-audio.ts:16
if (!(await stat(canonical)).isDirectory()) throw new Error("not a directory"); - 读取本地文件src/worker/managed-audio.ts:42
canonical = await realpath(audioPath); - 执行外部命令来自配置/变量src/worker/node-spawner.ts:49
const child = spawn(command, args, {
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
@deepseek-ai/dsh-home-paths@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/cordis-plugin-group@deepseek-ai/cordis-plugin-include@deepseek-ai/cordis-plugin-loader@deepseek-ai/dsh-agent@deepseek-ai/dsh-api-session-controller@deepseek-ai/dsh-api-workspace-controller@deepseek-ai/dsh-app-boot@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-locale给这个插件打分还没有人投票↓
界面预览
4 张 · 点击查看大图功能亮点
- 本地会议转录
非官方本地会议转录,为 Apple Silicon macOS 上的 DeepSeek Harness 打造
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
dsh-asr-plugin 项目网站 ·快速开始 面向 Apple Silicon macOS 的 DeepSeek Harness(DSH)非官方本地会议转写插件。 支持导入 WAV、M4A、MP3 文件,录制麦克风与系统音频,生成包含时间戳和说话人标签的转写文本。会议可在 DSH 消息中显式引用,用于阅读与导出。 录音继续进行时引用当前会议生成阶段性总结 示例使用语音合成的虚构会议。在录音继续进行时,通过 @ 引用当前会议,让配置的 DeepSeek 模型读取实时草稿并生成阶段性总结。总结是用户主动请求的当前快照,后续可能随草稿变化。完整案例与环境信息见案例截图。 官方社区展示帖。 兼容性 | 组件 | 要求 | | 宿主 | DSH 0.2.0-rc.2、0.2.1-rc.2、0.2.1-rc.3 或 0.2.1-rc.5 | | 运行时 | Node.js 24 | | 平台 | macOS、Apple Silicon(darwin / arm64) | | 麦克风 API | macOS 13.5 或更高版本 | | 系统音频与双轨 API | macOS 14.2 或更高版本 | 上述 macOS 版本表示 API 可用下限,不代表完整的操作系统测试矩阵。 DSH 桌面版和 Web 模式均在运行 DSH 的 Mac 上处理音频。 Windows、Linux 和 Intel Mac 不受支持。 安装 在 DSH 插件页安装 @huliux/dsh-asr-plugin。Web 配置也支持命令行安装: dsh plugin --profile web add --ignore-scripts @huliux/dsh-asr-plugin@0.1.5 使用 DSH 0.2.1-rc.5 时,请升级至插件 0.1.5。桌面版在插件页选择 添加插件,输入 @huliux/dsh-asr-plugin@0.1.5;Web 用户可再次运行上面的命令。若宿主提示,请重启 DSH。支持的宿主版本无需添加兼容性豁免。 若桌面安装器提示“该插件已安装”,请从 GitHub Release 下载对应版本的 .tgz 和 SHA256SUMS,校验后在添加插件中输入安装包的绝对路径。此更新方式保留现有插件配置和会议数据。 安装包包含原生模块和采用 ad-hoc 签名的录音 Helper。Helper 未经过 Apple 公证;安装和更新后,macOS 可能要求权限或安全确认。请保持系统安全保护开启。 桌面与 Web 配置共用默认会议库。每个数据目录同时运行一个插件宿主;更换安装包时保留会议与模型数据。 安装包校验、本地归档安装与版本渠道见 分发说明。 源码下载后需要完成 源码构建 才能录音。 安装问题请通过 GitHub Issues 反馈,并附插件、DSH、Node.js 和 macOS 版本;请勿附会议数据或凭据。 模型准备 在插件设置中下载基础模型,约 278 MiB。可选标点模型约 274 MiB,通过校验并安装后自动用于新录音、导入与重新转写。活动任务和已有转写保留原处理模式。已安装的标点包损坏时,须修复后才能启动新任务。 模型来自固定的上游版本,安装前校验文件大小和 SHA-256。 权重不包含在 Git、npm 或 GitHub Release 中。启用插件不会触发下载。 下载可取消和重试;失败后可复用已完整下载并通过校验的文件。 来源、代理行为与离线导入方法见 模型资源。 录音与会议访问 通过插件设置中的“录音权限”检查麦克风与系统音频访问。 每次开始录音都会重新检查。系统音频检查失败可能与权限、输出音量或设备路由有关;录音期间的静音不能证明权限被拒绝。 展开录音助手会准备模型,但不采集音频。连续录音可复用已加载的模型资源,闲置五分钟后释放。每场会议的音频、草稿和说话人状态保持独立。 选择 DSH 会话后录音,或要求 DSH Agent 导入本机音频的绝对路径。 通过 @ 选择会议并请求阅读或导出;文件写入使用 DSH 审批。 会议独立于 DSH 会话和工作区保存。 紧凑录音条显示正在采集时的控制项: 正在录音的紧凑组件 展开后可以查看录音计时、音轨状态和实时转写草稿: 展开录音组件查看实时草稿 导入音频或录音结束后,可引用会议的正式转写,提炼决策与行动项: 引用演示会议后提炼决策与行动项 模型准备后,音频推理在本机运行。使用大模型阅读转写时,内容可能发送给 DSH 配置的服务提供方;该操作受宿主配置和提供方条款约束。 限制 - 说话人标签用于区分同一会议中的声音,不构成个人身份识别。 识别与说话人区分效果取决于输入条件;本项目未报告通用准确率基准。 - 三个固定模型权重目前只有一个已验证的提供方。上游故障可能阻止安装;不能用同名但不同字节的模型替代。 - 发送后的消息历史可能显示会议引用的内部链接,选择器与输入框仍显示会议标题。 开发与许可 源码构建和验证见 开发参考,贡献要求见 CONTRIBUTING.md,安装包准备见发布参考。 项目自有源码使用 Apache-2.0。第三方源码与模型保留各自条款;许可和署名见 THIRD_PARTY_NOTICES.md、 third_party/ 与原生模块的 vendored 目录。 支持与联系 - 使用问题、Bug 和功能建议: GitHub Issues。 需要提供的诊断信息见 支持说明。 - 合作与私下联系: dasenrising@gmail.com。 - 安全漏洞:请按 安全报告说明 私下反馈。
预览分享卡片长什么样 ↓
面向 Apple Silicon macOS 的 DeepSeek HarnessDSH非官方本地会议转写插件。