截图标注、计算器、剪贴板与系统监控一站搞定
29.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/26 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/ddxl123/dsh-mac-tools.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 129 处,展示前 119 条)
- 读取本地文件src/index.js:73
const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) - 写入本地文件src/index.js:105
mkdirSync(saveDirectory, { recursive: true }) - 写入本地文件src/index.js:116
mkdirSync(directory, { recursive: true, mode: 0o700 }) - 写入剪贴板client/index.js:6501
Promise.resolve(clipboard.writeText(url)).then(() => { - 读取本地文件src/capture.js:33
if (candidate.includes('/') && existsSync(candidate)) return candidate - 执行外部命令来自配置/变量src/capture.js:88
const child = spawn(command, args, { stdio: ['pipe', 'pipe', 'pipe'] }) - 读取本地文件src/capture.js:466
info = await stat(filePath) - 读取本地文件src/capture.js:475
const buffer = await readFile(filePath) - 执行外部命令来自配置/变量src/capture.js:549
const child = spawn(AFPLAY, [`${SOUNDS_DIRECTORY}/${name}.aiff`], { stdio: 'ignore', detached: true }) - 读取本地文件src/hotkey.js:160
if (existsSync(candidate)) return candidate - 读取本地文件src/hotkey.js:187
const source = readFileSync(sourcePath) - 读取本地文件src/hotkey.js:190
if (existsSync(binary)) return binary - 写入本地文件src/hotkey.js:191
mkdirSync(cacheDirectory, { recursive: true, mode: 0o700 }) - 写入本地文件src/hotkey.js:197
chmodSync(binary, 0o700) - 删除本地文件src/hotkey.js:299
rmSync(socketPath, { force: true }) - 写入本地文件src/hotkey.js:330
chmodSync(socketPath, 0o600) - 删除本地文件src/hotkey.js:463
rmSync(this.socketPath, { force: true }) - 读取本地文件src/hotkey.js:519
const document = JSON.parse(readFileSync(statePath, 'utf8')) - 写入本地文件src/hotkey.js:539
mkdirSync(dirname(statePath), { recursive: true, mode: 0o700 }) - 写入本地文件src/hotkey.js:541
writeFileSync(temporary, `${JSON.stringify({ accelerator }, null, 2)}\n`, { mode: 0o600 }) - 写入本地文件src/hotkey.js:542
renameSync(temporary, statePath) - 删除本地文件src/hotkey.js:552
rmSync(statePath, { force: true }) - 写入本地文件src/history.js:43
mkdirSync(this.directory, { recursive: true, mode: 0o700 }) - 读取本地文件src/history.js:56
document = JSON.parse(readFileSync(this.indexPath, 'utf8')) - 读取本地文件src/history.js:64
return existsSync(join(this.directory, entry.file)) - 写入本地文件src/history.js:72
writeFileSync(temporary, `${JSON.stringify({ version: 1, entries: this.entries }, null, 2)}\n`, { mode: 0o600 }) - 写入本地文件src/history.js:73
renameSync(temporary, this.indexPath) - 写入本地文件src/history.js:87
writeFileSync(join(this.directory, file), capture.buffer, { mode: 0o600 }) - 删除本地文件src/history.js:121
rmSync(join(this.directory, entry.file), { force: true }) - 读取本地文件src/history.js:165
return readFileSync(join(this.directory, entry.file)) - 读取本地文件src/history.js:197
total += statSync(join(this.directory, entry.file)).size - 读取本地文件src/ops.js:352
return { buffer: readFileSync(record.path), path: record.path, bytes: record.bytes, source: 'shot', record } - 写入本地文件src/ops.js:373
mkdirSync(directory, { recursive: true }) - 写入本地文件src/ops.js:375
writeFileSync(path, source.buffer, { mode: 0o644 }) - 读取本地文件src/ops.js:415
if (!existsSync(absolute)) throw new OpsError('not_found', `the file does not exist: ${absolute}`, { path: absolute }) - 读取本地文件src/ops.js:460
await pins.pin(readFileSync(record.path), record.rect === undefined ? {} : { rect: record.rect }) - 读取本地文件src/ops.js:498
buffer: readFileSync(record.path), - 写入本地文件src/ops.js:522
writeFileSync(path, buffer, { mode: 0o600 }) - 读取本地文件src/ops.js:543
const buffer = readFileSync(pinRecord.path) - 读取本地文件src/pin.js:106
const source = readFileSync(sourcePath) - 读取本地文件src/pin.js:109
if (existsSync(binary)) return binary - 写入本地文件src/pin.js:110
mkdirSync(cacheDirectory, { recursive: true, mode: 0o700 }) - 写入本地文件src/pin.js:116
chmodSync(binary, 0o700) - 写入本地文件src/pin.js:183
mkdirSync(this.tempDirectory, { recursive: true, mode: 0o700 }) - 写入本地文件src/pin.js:185
writeFileSync(imagePath, buffer, { mode: 0o600 }) - 删除本地文件src/pin.js:248
rmSync(this.eventSocketPath, { force: true }) - 写入本地文件src/pin.js:281
chmodSync(this.eventSocketPath, 0o600) - 删除本地文件src/pin.js:355
rmSync(imagePath, { force: true }) - 删除本地文件src/pin.js:381
rmSync(this.eventSocketPath, { force: true }) - 删除本地文件递归src/pin.js:386
rmSync(this.tempDirectory, { recursive: true, force: true }) - 发起网络请求src/route.js:52
async fetch(request) { - 写入本地文件src/store.js:62
mkdirSync(this.directory, { recursive: true, mode: 0o700 }) - 读取本地文件src/store.js:78
const info = statSync(capture.path) - 写入本地文件src/store.js:106
writeFileSync(path, buffer, { mode: 0o600 }) - 读取本地文件src/store.js:132
return readFileSync(record.path) - 删除本地文件src/store.js:141
unlinkSync(dropped.path) - 删除本地文件递归src/store.js:153
rmSync(this.directory, { recursive: true, force: true }) - 读取本地文件src/transfer.js:232
if (!existsSync(join(directory, name))) return name - 读取本地文件src/transfer.js:237
if (!existsSync(join(directory, candidate))) return candidate - 读取本地文件src/transfer.js:265
if (code === 'EEXIST' || existsSync(target)) { - 写入本地文件src/transfer.js:270
renameSync(tempPath, target) - 删除本地文件src/transfer.js:274
rmSync(tempPath, { force: true }) - 写入本地文件src/transfer.js:536
const output = createWriteStream(filePath, { mode: 0o600 }) - 读取本地文件src/transfer.js:587
if (item.kind === 'file' && (typeof item.path !== 'string' || item.path === '' || !existsSync(item.path))) return false - 写入本地文件src/transfer.js:807
mkdirSync(this.directory, { recursive: true, mode: 0o700 }) - 写入本地文件src/transfer.js:808
mkdirSync(this.stateDirectory, { recursive: true, mode: 0o700 }) - 执行外部命令固定命令src/transfer.js:1079
const child = spawn('/usr/bin/caffeinate', ['-i', '-s', '-w', String(process.pid)], { stdio: 'ignore' }) - 读取本地文件src/transfer.js:1147
document = JSON.parse(readFileSync(this.indexPath, 'utf8')) - 写入本地文件src/transfer.js:1185
writeFileSync(temporary, payload, { mode: 0o600 }) - 写入本地文件src/transfer.js:1186
renameSync(temporary, this.indexPath) - 删除本地文件src/transfer.js:1189
rmSync(temporary, { force: true }) - 删除本地文件src/transfer.js:1256
rmSync(item.path, { force: true }) - 删除本地文件src/transfer.js:1279
rmSync(item.path, { force: true }) - 读取本地文件src/transfer.js:1323
if (!existsSync(item.path)) throw new TransferError('not_found', 'the received file no longer exists', { id, path: item.path }) - 执行外部命令固定命令src/transfer.js:1325
const child = spawn('/usr/bin/open', ['-R', item.path], { stdio: 'ignore', detached: true }) - 删除本地文件src/transfer.js:1740
rmSync(tempPath, { force: true }) - 删除本地文件src/transfer.js:1756
rmSync(tempPath, { force: true }) - 读取本地文件src/transfer-qr.js:52
if (existsSync(candidate)) return candidate - 执行外部命令来自配置/变量src/transfer-qr.js:76
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] }) - 读取本地文件src/transfer-qr.js:154
source = readFileSync(sourcePath) - 读取本地文件src/transfer-qr.js:160
if (!existsSync(binary)) { - 写入本地文件src/transfer-qr.js:162
mkdirSync(cacheDirectory, { recursive: true, mode: 0o700 }) - 读取本地文件src/transfer-qr.js:175
compiledBytes = statSync(temporary).size - 写入本地文件src/transfer-qr.js:183
chmodSync(temporary, 0o700) - 写入本地文件src/transfer-qr.js:188
renameSync(temporary, binary) - 读取本地文件src/transfer-qr.js:191
if (!existsSync(binary)) throw failure - 删除本地文件src/transfer-qr.js:192
rmSync(temporary, { force: true }) - 删除本地文件src/transfer-qr.js:196
rmSync(temporary, { force: true }) - 读取本地文件src/transfer-qr.js:203
if (!existsSync(binary)) { - 读取本地文件src/visibility.js:35
if (candidate.includes('/') && existsSync(candidate)) return candidate - 执行外部命令来自配置/变量src/visibility.js:291
const child = spawn(OSASCRIPT, ['-l', 'JavaScript', '-e', buildVisibilityScript(this.target, 'show')], { - 读取本地文件src/selection.js:24
const OSASCRIPT = existsSync('/usr/bin/osascript') ? '/usr/bin/osascript' : 'osascript' - 删除本地文件src/selection.js:174
rmSync(this.outputPath, { force: true }) - 读取本地文件src/selection.js:195
return parseSelectionReport(readFileSync(this.outputPath, 'utf8')) - 删除本地文件递归src/selection.js:240
rmSync(this.directory, { recursive: true, force: true }) - 执行外部命令来自配置/变量src/windows.js:143
execFile(command, args, { timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => { - 读取本地文件src/alerts.js:104
text = readFileSync(statePath, 'utf8') - 写入本地文件src/alerts.js:246
mkdirSync(dirname(this.statePath), { recursive: true, mode: 0o700 }) - 写入本地文件src/alerts.js:248
writeFileSync(temporary, `${JSON.stringify({ version: SETTINGS_VERSION, settings: this.settings }, null, 2)}\n`, { mode: 0o600 }) - 写入本地文件src/alerts.js:249
renameSync(temporary, this.statePath) - 读取本地文件src/fans.js:564
const parsed = JSON.parse(readFileSync(this.presetsPath, 'utf8')) - 写入本地文件src/fans.js:587
mkdirSync(dirname(this.presetsPath), { recursive: true, mode: 0o700 }) - 写入本地文件src/fans.js:589
writeFileSync(temporary, `${JSON.stringify({ version: 1, presets: this.customPresets }, null, 2)}\n`, { mode: 0o600 }) - 写入本地文件src/fans.js:590
renameSync(temporary, this.presetsPath) - 读取本地文件src/fans.js:652
return existsSync(this.plistPath) && existsSync(this.installBinary) - 写入本地文件src/fans.js:781
writeFileSync(scriptPath, script, { mode: 0o700 }) - 删除本地文件递归src/fans.js:803
rmSync(scriptDirectory, { recursive: true, force: true }) - 发起网络请求src/fans.js:849
await this.request({ op: 'autoAll' }, 2000).catch(() => {}) - 写入本地文件src/fans.js:856
writeFileSync(scriptPath, buildUninstallScript({ - 删除本地文件递归src/fans.js:876
rmSync(scriptDirectory, { recursive: true, force: true }) - 发起网络请求src/fans.js:917
this.request({ op: 'ping' }, 3000).then((response) => { - 发起网络请求src/fans.js:1045
await this.request({ op: 'autoAll' }, 5000).catch(() => {}) - 发起网络请求src/fans.js:1100
const response = await this.request({ op: 'set', fan: index, rpm }, 5000) - 执行外部命令来自配置/变量src/smc.js:195
const child = spawn(binary, ['--once'], { stdio: ['ignore', 'pipe', 'pipe'] }) - 读取本地文件src/swift.js:32
if (existsSync(candidate)) return candidate - 读取本地文件src/swift.js:79
for (const file of sources) digest.update(readFileSync(file)) - 读取本地文件src/swift.js:82
if (existsSync(binary)) return binary - 写入本地文件src/swift.js:83
mkdirSync(cacheDirectory, { recursive: true, mode: 0o700 }) - 写入本地文件src/swift.js:92
chmodSync(binary, 0o700)
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成Mac 工具箱(DeepSeek Harness 插件)
一个 DeepSeek Harness 插件,装好即是一整套 本机 Mac 工具箱:
- 截图(类 QQ)——用 macOS 原生区域/窗口选择框选任意应用,然后进入真正的编辑器:裁剪、箭头、矩形、椭圆、画笔、文字、马赛克、高亮,支持撤销/重做、复制到剪贴板、保存到磁盘。
- 计算器——完整的计算器面板,以及智能体可以精确调用的表达式引擎。
- 手机互传——手机和 Mac 连同一个 Wi-Fi,用手机相机扫二维码,就能用浏览器收发文本、文件、照片;不用装 App,文件不出局域网。
- 剪贴板——在面板或智能体里读写 macOS 文本剪贴板。
- 运行状态——类 App Store 应用 State 的实时监控:CPU/内存/磁盘/网络/电池曲线、系统信息、SMC 温度与风扇、进程排行、阈值告警;以及类 Macs Fan Control 的风扇转速控制(自动 / 手动转速 / 温度曲线 / 预设),首次启用需一次性管理员授权。原「系统信息」页已并入这里。
全部在本机完成。这是一个纯面板插件:只在左侧栏增加 Mac 工具箱 入口,不向智能体注册任何工具。
安装
这是一个普通的 DSH bundle。
展开完整文档(README 剩余 1 节)
Mac 工具箱(DeepSeek Harness 插件)
一个 DeepSeek Harness 插件,装好即是一整套 本机 Mac 工具箱:
- 截图(类 QQ)——用 macOS 原生区域/窗口选择框选任意应用,然后进入真正的编辑器:裁剪、箭头、矩形、椭圆、画笔、文字、马赛克、高亮,支持撤销/重做、复制到剪贴板、保存到磁盘。
- 计算器——完整的计算器面板,以及智能体可以精确调用的表达式引擎。
- 手机互传——手机和 Mac 连同一个 Wi-Fi,用手机相机扫二维码,就能用浏览器收发文本、文件、照片;不用装 App,文件不出局域网。
- 剪贴板——在面板或智能体里读写 macOS 文本剪贴板。
- 运行状态——类 App Store 应用 State 的实时监控:CPU/内存/磁盘/网络/电池曲线、系统信息、SMC 温度与风扇、进程排行、阈值告警;以及类 Macs Fan Control 的风扇转速控制(自动 / 手动转速 / 温度曲线 / 预设),首次启用需一次性管理员授权。原「系统信息」页已并入这里。
全部在本机完成。这是一个纯面板插件:只在左侧栏增加 Mac 工具箱 入口,不向智能体注册任何工具。
安装
这是一个普通的 DSH bundle。在任意会话里对插件目录安装即可:
plugin_manager install_bundle target: /绝对路径/dsh-mac-tools
或使用 CLI:
dsh plugin --profile desktop add /绝对路径/dsh-mac-tools
本地目录安装是链接方式。安装后刷新 Web 页面,左侧栏会出现 Mac 工具箱。如果安装时 Host 已经在运行,最稳妥的方式是重启一次 DeepSeek Harness,让两个半端版本一致。在创造模式(启用 HMR)下无需重启:在插件页把 mac-tools 行关掉再打开,会触发配置重组并原地重新加载链接的 Host 代码。
权限
截图需要 系统设置 → 隐私与安全性 → 屏幕录制 → 允许 DeepSeek Harness。没有授权时 macOS 会静默返回一张只有壁纸的图片(或拒绝窗口截图),面板会显示 permission 失败码并提示去哪个开关授权。
不需要辅助功能(Accessibility)权限:区域/窗口选择用的是 macOS 自带的原生选择界面,插件只额外读写剪贴板。
面板
从左侧栏打开,或按 ⌥⌘M。⇧⌘A 直接开始一次交互截图,并打开面板进入编辑器。
⌃⌥A(可配置)是系统级截图快捷键:在任何其它应用拥有焦点时也能按。
它会拉起 macOS 原生区域/窗口选择,然后把截图贴回原来的位置(置顶悬浮窗,覆盖刚才框选的屏幕区域),同时复制到剪贴板、保存到截图目录,并播放 Glass 提示音(不需要自动贴图可设 pinAfterGlobalCapture: false)。
不需要额外权限;原生小助手首次会用 Xcode Command Line Tools 编译一次(开发者 Mac 通常已装)。
快捷键可以在面板里直接自定义:截图按钮下方的「全局截图快捷键」一行点 修改,然后按下新组合(Control/Option/Command + 一个按键;Esc 取消)。自定义结果保存在 profile 的插件状态目录(<profile home>/dsh-mac-tools/hotkey.json),重启不丢失;点 恢复默认 回到 globalHotkey 配置值。
面板内的快速截图快捷键(默认 ⇧⌘A)是 DSH 普通快捷键,可在 设置 → 快捷键 中改键。
截图页
- 区域 / 窗口截图——屏幕变暗后拖拽框选;按 空格 切换到窗口点选模式,按 Esc 取消。
- 全屏截图、窗口截图(点选一个窗口)、3 秒后全屏(延时截图)、选择应用窗口(列出所有可见应用窗口,可搜索,前台窗口有标记,点击即截取)。
- 历史截图:每一次截图(面板截图或 ⌥A 全局截图)都会自动持久保存,并在编辑器下方以缩略图列表展示;点击可重新载入编辑器,也可直接贴图 / 复制 / 删除。
- 贴图可当场标注:双击贴图(或按 A、右键「标注图片」),贴图上会浮出小工具栏——画笔 / 箭头 / 矩形 / 椭圆 / 高亮、颜色、粗细、撤销、清空、完成;在贴图上直接画,复制/保存会包含标注。想用完整面板编辑器时,右键选择「在面板中编辑」即可。
- 「截图时隐藏 DSH」(默认开启):屏幕截图时自动隐藏 DeepSeek Harness 窗口,截完立即恢复,避免自己的窗口挡住要截的内容;按钮就在截图工具栏上,可随时关闭。
该开关同样作用于全局快捷键截图——快捷键截图没有页面可询问,所以由 Host 记住这个选择(当前 DSH 进程内有效)。
- 编辑器支持 裁剪、箭头、矩形、椭圆、画笔、文字、马赛克、高亮,六种颜色、三档线宽,撤销/重做/还原,复制(PNG 进剪贴板)、保存(保存到配置目录,并可在 Finder 中显示)与 贴图:把编辑后的图片作为置顶悬浮窗钉在桌面上,位置就是截图时框选的屏幕区域。之后可拖动、滚轮缩放、右键菜单(复制 / 保存 / 不透明度 / 关闭),Esc 关闭,⌘C 复制。
- 编辑器快捷键:⌘Z 撤销、⇧⌘Z 重做、Enter 应用裁剪、Esc 先取消裁剪再关闭截图。
计算器页
显示屏 + 键盘 + 历史记录。计算走 Host 的引擎,因此面板和工具对同一个表达式给出相同结果。支持 + - * / % ^、括号、正负号、隐式乘法(2(3+4))、百分号(50%)、常量 pi/e/tau,以及函数 sqrt, cbrt, abs, round, floor, ceil, trunc, sign, exp, ln, log, log2, sin, cos, tan, asin, acos, atan, atan2, pow, hypot, min, max, mod。三角函数默认按角度制计算。
手机互传页
一个只在局域网内工作的 Mac ↔ 手机中转站。点「开启手机互传」后,Host 会临时起一个 HTTP 监听,显示二维码(和 URL),并开始倒计时。用手机系统相机扫码(或手动输入 URL)即可打开一个免登录网页——不用装 App、不用账号、不走云。
- 手机 → Mac:发文本(进面板收件箱,可一键复制到 macOS 剪贴板,也可配置自动复制)、选文件/照片,或直接拍照上传。上传带进度,文件保存到传输目录(默认 ~/Downloads/DSH 手机互传),智能体可以像普通文件一样读取处理。
- Mac → 手机:输入文本发送、用系统选择框选任意文件,或在最近截图上一键「发送到手机」;手机页面会列出这些内容,可预览、复制或下载。截图直接取自历史记录。
- 安全:每次会话一个随机 token,30 分钟过期;监听只在会话期间存在;服务端校验 Host 头、净化文件名,不向公网暴露。下载响应固定为 attachment + application/octet-stream(绝不当网页渲染),交给手机浏览器/下载管理器原生保存。
- 注意:手机和 Mac 必须连同一个 Wi-Fi(开了 AP 隔离的访客网络不行);第一次开启时 macOS 可能询问是否允许 DeepSeek Harness 接受传入连接;手机 VPN 可能拦截局域网访问;较新的 macOS 还可能询问本地网络权限。传输期间别让 Mac 休眠。
- 排障:如果手机上传一直停在 0%,或点下载没反应,先把页面重新打开一次再重试;手机页会把浏览器异常、上传超时/停滞、下载失败上报到 <插件状态目录>/transfer/diagnostics.log(即 ~/.dsh/dsh-mac-tools/transfer/diagnostics.log),同时记录每次上传开始/完成与下载请求,不用远程调试就能看出是哪一步失败。
配置
bundle 行支持以下配置(写在 cordis.patch.yml 或之后的 patch 层),都有默认值:
| 键 | 默认值 | 含义 |
| saveDirectory | ~/Pictures/DSH Mac Tools | 截图保存目录,支持 ~ 展开。面板的“保存”和工具都写到这里。 |
| maxShots | 20 | 面板临时保留的截图数量,超过后淘汰最旧的;已保存文件不会被淘汰。 |
| interactiveTimeoutMs | 180000 | 原生选择界面等待操作者的最长时间。 |
| hideAppDuringCapture | true | 屏幕截图时短暂隐藏 DeepSeek Harness 窗口(仅当它是前台应用)并在截完后恢复;面板上有单次开关。 |
| globalHotkey | ctrl+alt+a | 系统级截图快捷键的默认值,任何应用在前台时都可用;面板里修改的值会覆盖它(保存到插件状态文件);留空则初始不启用全局快捷键。 |
| globalHotkeySound | true | 全局快捷键截图成功后播放 Glass 提示音。 |
| pinAfterGlobalCapture | true | 全局快捷键截图后自动贴图到屏幕(面板编辑器里也始终有手动「贴图」按钮)。 |
| historyLimit | 60 | 历史截图最多保留多少张,超出后删除最旧的。 |
| transferPort | 8791 | 手机互传临时监听的优先端口;被占用时顺延尝试后面 9 个。 |
| transferDirectory | ~/Downloads/DSH 手机互传 | 手机发来的文件保存目录,支持 ~ 展开。 |
| transferSessionMinutes | 30 | 一次手机互传会话的有效期,到期后二维码失效、监听停止。 |
| transferMaxFileMB | 512 | 手机单次上传文件大小上限(MB)。 |
| transferAutoCopyText | false | 收到手机文本时自动复制到 macOS 剪贴板。 |
| monitorSampleMs | 1000 | 「运行状态」页 Host 侧采样间隔(毫秒,500–10000)。软件指标最多每 2 秒采集一次,面板轮询频率可以更快或更慢。 |
| alertCpuPercent | 90 | 首次运行的 CPU 占用告警阈值(%)。面板运行时可改,持久化的选择优先于该默认值。 |
| alertTemperatureCelsius | 95 | 首次运行的温度告警阈值(°C,取 SMC 温度组)。 |
| alertMemoryPercent | 90 | 首次运行的内存占用告警阈值(%)。 |
| alertCooldownSeconds | 300 | 同一指标两次告警之间的最短间隔(秒)。 |
| alertNotify | true | 告警触发时是否发 macOS 通知。 |
- id: mac-tools
config:
saveDirectory: ~/Desktop/Screenshots
maxShots: 30
实现说明
- 原生小助手(assets/):hotkey.swift 监听系统级快捷键,pin.swift 把贴图显示为浮动的 AppKit 窗口,qrcode.swift 用 CoreImage 生成手机互传二维码;三者首次用 swiftc 编译并缓存到临时目录。smc-core.swift 是共享的只读 IOKit AppleSMC 传输层;state.swift 是常驻采样器,每个采样输出一行 JSON(风扇 + 全部可读温度键);fan-daemon.swift 是风扇卡片安装的特权助手——只提供 Unix socket JSON 的 F0Md/F0Tg 写入,目标转速夹在 SMC 上下限内,并带 12 秒看门狗自动恢复系统自动控制。
- Host 半端(src/):在 Connection 的 /api 通道上注册受认证的 /api/dsh-mac-tools 路由,维护临时截图注册表,用 /usr/sbin/screencapture 截图、用 /usr/bin/osascript 获取窗口列表、写图片剪贴板和打开文件选择框。「运行状态」页是一条采样循环(src/monitor.js),底层是 src/metrics.js(CPU/内存/磁盘/网络/电池)、src/smc.js(采样器子进程)、src/alerts.js(阈值)与 src/fans.js(风扇控制与 socket 协议)。手机互传监听(src/transfer.js)是一个独立的、按需启动的 node:http 服务,只在会话期间绑定局域网;它提供手机网页和上传/下载 API,收发索引存在插件状态目录。不注册任何智能体工具。
- Client 半端(client/index.js):一个由 window.__ModuleLoader__ 加载的纯 React bundle,注册左侧栏入口和 main 面板,渲染四个分页,只通过上面的路由与 Host 通信。
- 截图引擎是对系统工具的一层薄封装——区域/窗口选择、取消、窗口点选都是 macOS 原生行为,不是重新实现。
POST /api/dsh-mac-tools { "endpoint": "...", "payload": { ... } }
GET /api/dsh-mac-tools?shot=<id> → image/png
GET /api/dsh-mac-tools?history=<id> → image/png
GET /api/dsh-mac-tools?inbox=<id> → 手机发来文件的字节
?inbox= 预览只对安全的栅格图片(png、jpeg、gif、webp、avif、bmp)内联返回;其它文件一律以 application/octet-stream 附件返回,手机上传的 HTML/SVG 因此不可能在 Web UI 源上执行。
端点:status、windows、capture、cancelCapture、copy、save、calc、clipboard、system、reveal,手机互传的 transferStatus、transferStart、transferStop、transferInbox、transferOutbox,运行状态的 monitor、monitorProcesses、monitorKill、monitorAlertSave、monitorAlertTest,以及风扇控制的 fanInstall、fanUninstall、fanApply、fanPreset。返回 { ok: true, value } 或 { ok: false, error: { code, message, details } }。
手机访问的是它自己的临时服务 http://<Mac局域网IP>:<端口>/t/<token>/(页面、api/state、api/text、api/upload、api/items/<id>),那不是 Harness 的 API,也不携带 Harness 会话。
开发
npm run check # 所有模块 node --check + node --test test/
Host 半端除 dsh 自带的 @deepseek-ai/schemastery 配置 schema 外无第三方依赖;Client 半端只需要 react。
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
ddxl123/dsh-mac-tools
截图标注、计算器、剪贴板与系统监控一站搞定
相关插件 · 视觉能力
智能体工作台Devin-AXIS/iPolloWork
企业级、本地优先的智能体工作台,面向个人和智能体团队——一个工作区,容纳多个智能体引擎;一套统一体系,管理插件和技能;支…
★ 7,359已汉化DSH
72
开源设计工作台nexu-io/open-design
OpenDesign:首个协作式 AI 设计智能体工作区
★ 105,514已汉化DSH
71.8
多智能体编码工作区spacering-net/codeg
多智能体协作的 AI 编程工作区:汇总 Claude Code、Codex、OpenCode、Pi、Grok Build 等来源的会话。支持桌面应用、自托管服务器或 Docker。
★ 4,289已汉化DSH
71.6
GPT 图像提示词库freestylefly/awesome-gpt-image-2
Prompt as Code | GPT Image 2 / 2.5 提示词与案例库,530+ 个案例、20+…
★ 34,534已汉化DSH
71.5
架构图生成器tt-a1i/archify
把你想理解、规划或分享的事,变成可以互动的可视化作品。
★ 92,675已汉化DSH
71.2
AI 用量桌面小部件Javis603/token-monitor
EN | 简 | 繁 | KO | JA | PT
★ 3,522已汉化DSH
71
这是你的仓库?认领条目