在通知里直接审批命令并填写多问题表单
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/7 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/realDGD/dsh-macos-notify.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 25 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
🔴 安全扫描发现高危能力自动 · 静态分析源码
v2.7.0🔴 扫描:高危尚无人工评估
静态扫描发现安装期即执行代码,或命令参数由运行时变量拼接(参数来源无法静态判定,从严处理)。
安装时即执行脚本可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 129 处,展示前 103 条)
- 读取本地文件lib/index.js:16
const version = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))).version - 读取本地文件lib/index.js:45
if (statSync(path).size > limit) return null - 读取本地文件lib/index.js:46
return JSON.parse(readFileSync(path, 'utf8')) - 写入本地文件lib/index.js:106
mkdirSync(dir, { recursive: true, mode: 0o700 }) - 写入本地文件lib/index.js:109
writeFileSync(temp, JSON.stringify({ ...receipt, confirmedAt: Date.now() }) + '\n', { mode: 0o600 }) - 写入本地文件lib/index.js:110
renameSync(temp, resultPath) - 写入本地文件lib/index.js:116
writeFileSync(temporary, JSON.stringify({ ...receipt, confirmedAt: Date.now() }) + '\n', { mode: 0o600 }); renameSync(temporary, path) - 写入本地文件lib/index.js:116
writeFileSync(temporary, JSON.stringify({ ...receipt, confirmedAt: Date.now() }) + '\n', { mode: 0o600 }); renameSync(temporary, path) - 发起网络请求lib/index.js:130
async fetch(request) { - 执行外部命令固定命令scripts/install.mjs:21
const release = execFileSync('/usr/bin/sw_vers', ['-productVersion'], { encoding: 'utf8' }).trim() - 读取本地文件scripts/install.mjs:34
for (const entry of readdirSync(join(folder, relative), { withFileTypes: true }).sort((a,b) => a.name.localeCompare(b.name, 'en'))) { - 读取本地文件scripts/install.mjs:39
hash.update(path); hash.update('\0'); hash.update(String(lstatSync(join(folder,path)).mode & 0o777)); hash.update('\0'); hash.update(readFileSync(join - 读取本地文件scripts/install.mjs:39
hash.update(path); hash.update('\0'); hash.update(String(lstatSync(join(folder,path)).mode & 0o777)); hash.update('\0'); hash.update(readFileSync(join - 读取本地文件scripts/install.mjs:44
const owned = app => { try { return readFileSync(join(app, 'Contents/Info.plist'), 'utf8').includes('<string>' + label + '</string>') } catch { return - 读取本地文件scripts/install.mjs:45
const agentOwned = () => { try { return readFileSync(agent, 'utf8').includes('<!-- dsh-macos-notify managed -->') } catch { return false } } - 读取本地文件scripts/install.mjs:48
if (!existsSync(manifest)) throw new Error('No installer ownership record; refusing to remove files.') - 执行外部命令固定命令scripts/install.mjs:63
const lines = execFileSync('ps', ['-axww', '-o', 'pid=,command='], { encoding: 'utf8' }).split('\n') - 执行外部命令固定命令scripts/install.mjs:81
if (agentOwned()) { try { execFileSync('launchctl', ['bootout', `gui/${process.getuid()}/${label}`], { stdio: 'pipe' }) } catch {} } - 删除本地文件递归scripts/install.mjs:97
if (existsSync(uninstallApp)) rmSync(uninstallApp, { recursive: true }) - 删除本地文件scripts/install.mjs:98
if (agentOwned()) rmSync(agent) - 删除本地文件scripts/install.mjs:99
rmSync(manifest) - 写入本地文件scripts/install.mjs:103
mkdirSync(dir, { recursive: true, mode: 0o700 }) - 执行外部命令固定命令scripts/install.mjs:112
if (!dry) execFileSync('codesign', ['--verify', '--deep', '--strict', target], { stdio: 'pipe' }) - 执行外部命令固定命令scripts/install.mjs:122
} else execFileSync('bash', [join(root, 'macos/build.sh'), staging], { stdio: 'inherit' }) - 执行外部命令固定命令scripts/install.mjs:123
if (!dry) execFileSync('codesign', ['--verify', '--deep', '--strict', staging], { stdio: 'inherit' }) - 写入本地文件scripts/install.mjs:125
const backupDir = join(dir, 'backups'); mkdirSync(backupDir, { recursive: true, mode: 0o700 }) - 写入本地文件scripts/install.mjs:127
renameSync(agent, join(backupDir, `${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}.plist`)) - 写入本地文件scripts/install.mjs:135
writeFileSync(temporaryManifest, JSON.stringify(record) + '\n', { mode: 0o600 }) - 写入本地文件scripts/install.mjs:143
renameSync(previous, retired) - 删除本地文件递归scripts/install.mjs:144
} catch (error) { if (existsSync(backup)) rmSync(backup, { recursive: true }); throw error } - 写入本地文件scripts/install.mjs:148
renameSync(staging, target); replaced = true - 删除本地文件递归scripts/install.mjs:151
if (replaced) rmSync(target, { recursive: true }) - 删除本地文件递归scripts/install.mjs:154
rmSync(backup, { recursive: true }) - 读取本地文件lib/locale.js:3
export const uiStrings=JSON.parse(readFileSync(new URL('../macos/assets/ui-strings.json',import.meta.url),'utf8')) - 读取本地文件lib/locale.js:5
try {const p=join(stateDir,'ui-language.json');if(statSync(p).size>1024)return 'en';const v=JSON.parse(readFileSync(p,'utf8'));return v.version===1&&[ - 读取本地文件lib/locale.js:5
try {const p=join(stateDir,'ui-language.json');if(statSync(p).size>1024)return 'en';const v=JSON.parse(readFileSync(p,'utf8'));return v.version===1&&[ - 写入本地文件lib/locale.js:16
mkdirSync(stateDir,{recursive:true,mode:0o700});chmodSync(stateDir,0o700) - 写入本地文件lib/locale.js:16
mkdirSync(stateDir,{recursive:true,mode:0o700});chmodSync(stateDir,0o700) - 写入本地文件lib/locale.js:18
writeFileSync(tmp,JSON.stringify({version:1,locale:payload.locale,updatedAt:Date.now()})+'\n',{mode:0o600});renameSync(tmp,target) - 写入本地文件lib/locale.js:18
writeFileSync(tmp,JSON.stringify({version:1,locale:payload.locale,updatedAt:Date.now()})+'\n',{mode:0o600});renameSync(tmp,target) - 写入本地文件lib/interactions.js:10
writeFileSync(temp, JSON.stringify(value) + '\n', { mode: 0o600 }) - 写入本地文件lib/interactions.js:11
chmodSync(temp, 0o600) - 写入本地文件lib/interactions.js:12
renameSync(temp, path) - 写入本地文件lib/interactions.js:81
for (const dir of [stateDir, commands, results]) { mkdirSync(dir, { recursive: true, mode: 0o700 }); chmodSync(dir, 0o700) } - 写入本地文件lib/interactions.js:81
for (const dir of [stateDir, commands, results]) { mkdirSync(dir, { recursive: true, mode: 0o700 }); chmodSync(dir, 0o700) } - 读取本地文件lib/interactions.js:168
for (const file of readdirSync(commands)) { - 删除本地文件lib/interactions.js:175
if (statSync(path).size > 4 * 1024 * 1024) { unlinkSync(path); continue } - 读取本地文件lib/interactions.js:175
if (statSync(path).size > 4 * 1024 * 1024) { unlinkSync(path); continue } - 读取本地文件lib/interactions.js:176
command = JSON.parse(readFileSync(path, 'utf8')) - 删除本地文件lib/interactions.js:177
} catch { try { unlinkSync(path) } catch {}; continue } - 删除本地文件lib/interactions.js:178
try { unlinkSync(path) } catch { continue } - 读取本地文件lib/interactions.js:202
if (statSync(path).size <= 65536) { - 读取本地文件lib/interactions.js:203
const lease = JSON.parse(readFileSync(path, 'utf8')) - 读取本地文件lib/interactions.js:251
for (const file of readdirSync(results)) { - 删除本地文件lib/interactions.js:252
if (/^[A-Za-z0-9-]{1,128} json$/.test(file) && Date.now() - statSync(join(results, file)).mtimeMs > 600000) unlinkSync(join(results, file)) - 写入本地文件lib/settings.js:6
mkdirSync(stateDir, { recursive: true, mode: 0o700 }); chmodSync(stateDir, 0o700) - 写入本地文件lib/settings.js:6
mkdirSync(stateDir, { recursive: true, mode: 0o700 }); chmodSync(stateDir, 0o700) - 读取本地文件lib/settings.js:14
try { if (statSync(file).size > 4096) return { ...defaults }; return { ...defaults, ...validate(JSON.parse(readFileSync(file, 'utf8'))) } } - 读取本地文件lib/settings.js:14
try { if (statSync(file).size > 4096) return { ...defaults }; return { ...defaults, ...validate(JSON.parse(readFileSync(file, 'utf8'))) } } - 写入本地文件lib/settings.js:20
writeFileSync(temp, JSON.stringify(value) + '\n', { mode: 0o600 }); renameSync(temp, file) - 写入本地文件lib/settings.js:20
writeFileSync(temp, JSON.stringify(value) + '\n', { mode: 0o600 }); renameSync(temp, file) - 写入本地文件lib/notifications.js:9
mkdirSync(queue, { recursive: true, mode: 0o700 }); chmodSync(queue, 0o700) - 写入本地文件lib/notifications.js:9
mkdirSync(queue, { recursive: true, mode: 0o700 }); chmodSync(queue, 0o700) - 写入本地文件lib/notifications.js:47
writeFileSync(temporary, JSON.stringify({ id, sessionId: session.id, createdAt: record.createdAt }) + '\n', { mode: 0o600 }); renameSync(temporary, me - 写入本地文件lib/notifications.js:47
writeFileSync(temporary, JSON.stringify({ id, sessionId: session.id, createdAt: record.createdAt }) + '\n', { mode: 0o600 }); renameSync(temporary, me - 写入本地文件lib/notifications.js:49
writeFileSync(temp, JSON.stringify(record) + '\n', { mode: 0o600 }); renameSync(temp, target); counts.posted++; return record - 写入本地文件lib/notifications.js:49
writeFileSync(temp, JSON.stringify(record) + '\n', { mode: 0o600 }); renameSync(temp, target); counts.posted++; return record - 读取本地文件lib/diagnostics.js:5
try { if (statSync(path).size > 4096) return null; return JSON.parse(readFileSync(path, 'utf8')) } catch { return null } - 读取本地文件lib/diagnostics.js:5
try { if (statSync(path).size > 4096) return null; return JSON.parse(readFileSync(path, 'utf8')) } catch { return null } - 写入本地文件lib/menu-host.js:56
mkdirSync(stateDir,{recursive:true,mode:0o700});chmodSync(stateDir,0o700) - 写入本地文件lib/menu-host.js:56
mkdirSync(stateDir,{recursive:true,mode:0o700});chmodSync(stateDir,0o700) - 写入本地文件lib/menu-host.js:58
writeFileSync(temp,bytes,{mode:0o600});chmodSync(temp,0o600);renameSync(temp,file) - 写入本地文件lib/menu-host.js:58
writeFileSync(temp,bytes,{mode:0o600});chmodSync(temp,0o600);renameSync(temp,file) - 写入本地文件lib/menu-host.js:58
writeFileSync(temp,bytes,{mode:0o600});chmodSync(temp,0o600);renameSync(temp,file) - 读取本地文件lib/menu-control.js:5
const read=path=>{try{const info=lstatSync(path);if(!info.isFile()||info.size>4096)return null;return JSON.parse(readFileSync(path,'utf8'))}catch{retu - 读取本地文件lib/menu-control.js:5
const read=path=>{try{const info=lstatSync(path);if(!info.isFile()||info.size>4096)return null;return JSON.parse(readFileSync(path,'utf8'))}catch{retu - 写入本地文件lib/menu-control.js:6
const atomic=(file,value)=>{const temp=file+'.tmp-'+process.pid;writeFileSync(temp,JSON.stringify(value)+'\n',{mode:0o600});chmodSync(temp,0o600);rena - 写入本地文件lib/menu-control.js:6
const atomic=(file,value)=>{const temp=file+'.tmp-'+process.pid;writeFileSync(temp,JSON.stringify(value)+'\n',{mode:0o600});chmodSync(temp,0o600);rena - 写入本地文件lib/menu-control.js:6
const atomic=(file,value)=>{const temp=file+'.tmp-'+process.pid;writeFileSync(temp,JSON.stringify(value)+'\n',{mode:0o600});chmodSync(temp,0o600);rena - 写入本地文件lib/menu-control.js:9
for(const dir of [commands,results]){mkdirSync(dir,{recursive:true,mode:0o700});chmodSync(dir,0o700)} - 写入本地文件lib/menu-control.js:9
for(const dir of [commands,results]){mkdirSync(dir,{recursive:true,mode:0o700});chmodSync(dir,0o700)} - 读取本地文件lib/menu-control.js:14
for(const name of readdirSync(commands)) { - 读取本地文件lib/menu-control.js:17
if(!lstatSync(file).isFile())continue - 删除本地文件lib/menu-control.js:19
unlinkSync(file) - 读取本地文件lib/menu-control.js:32
for(const name of readdirSync(results)) { - 删除本地文件lib/menu-control.js:36
if(now()-result.completedAt>600000)unlinkSync(join(results,name)) - 删除本地文件lib/menu-control.js:40
for(const entry of retained.slice(128))unlinkSync(join(results,entry.name)) - 读取本地文件lib/helper-lifecycle.js:35
return statSync(info).size<=65536 && readFileSync(info,'utf8').includes('<string>com.dgd.dsh-jump-notifier</string>') && existsSync(join(candidate,'Co - 读取本地文件lib/helper-lifecycle.js:35
return statSync(info).size<=65536 && readFileSync(info,'utf8').includes('<string>com.dgd.dsh-jump-notifier</string>') && existsSync(join(candidate,'Co - 读取本地文件lib/helper-lifecycle.js:35
return statSync(info).size<=65536 && readFileSync(info,'utf8').includes('<string>com.dgd.dsh-jump-notifier</string>') && existsSync(join(candidate,'Co - 读取本地文件lib/helper-lifecycle.js:40
if(statSync(status).size<=4096) { - 读取本地文件lib/helper-lifecycle.js:41
const value=JSON.parse(readFileSync(status,'utf8')) - 读取本地文件scripts/release-check.mjs:4
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))) - 执行外部命令固定命令scripts/release-check.mjs:5
const packed = JSON.parse(execFileSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], { encoding: 'utf8' }))[0] - 执行外部命令来自配置/变量scripts/release-check.mjs:10
execFileSync(process.execPath, [new URL('sync-locales.mjs', import.meta.url).pathname, '--check'], { stdio: 'pipe' }) - 读取本地文件scripts/release-check.mjs:16
assert.deepEqual(JSON.parse(readFileSync(new URL('../npm-shrinkwrap.json', import.meta.url))), JSON.parse(readFileSync(new URL('../package-lock.json', - 读取本地文件scripts/release-check.mjs:17
const plist = readFileSync(new URL('../macos/Info.plist', import.meta.url), 'utf8') - 执行外部命令固定命令scripts/release-check.mjs:20
const paths = execFileSync('git', ['ls-files','--cached','--others','--exclude-standard'], { encoding: 'utf8' }).trim().split('\n').filter(Boolean) - 读取本地文件scripts/release-check.mjs:24
const content = readFileSync(path, 'utf8') - 读取本地文件scripts/sync-locales.mjs:4
const strings=JSON.parse(readFileSync(new URL('macos/assets/ui-strings.json',root),'utf8')) - 读取本地文件scripts/sync-locales.mjs:6
const clientPath=new URL('lib/client.js',root),client=readFileSync(clientPath,'utf8') - 读取本地文件scripts/sync-locales.mjs:10
if(process.argv.includes('--check')) {if(readFileSync(path,'utf8')!==content)throw Error('Generated locale source is stale: '+path)}else writeFileSync - 写入本地文件scripts/sync-locales.mjs:10
if(process.argv.includes('--check')) {if(readFileSync(path,'utf8')!==content)throw Error('Generated locale source is stale: '+path)}else writeFileSync
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 原生macOS交互式通知
- 通知内审批命令
- 填写多问题表单
为 DeepSeek Harness Desktop 提供原生 macOS 交互式通知、审批和完整问题表单
README
由 DeepSeek 最新模型翻译生成DSH macOS Notify
为 DeepSeek Harness Desktop 提供原生交互式 macOS 通知。DSH 插件与后台 DSH Notify.app 协同工作;点击通知即可返回 Desktop 对应的会话。
原生 macOS 通知、审批与完整问答:点击通知返回 DSH Desktop 对应会话,无须浏览器或额外模型服务。
- 在审批通知里直接「允许一次」或「拒绝」;默认点击打开完整审批详情,包含命令、格式化参数(可查看并复制原始内容)以及相关上下文。
- 一条问题提醒打开可滚动的多问题表单。每个问题都支持原有选项和独立的多行文本输入;一次提交整批内容。
- 命令与参数自动换行、会话名称、带表格和 LaTeX 公式的 Markdown 上下文,以及 Shell/JSON 高亮。显示格式不会改变提交的选项标签或复制的命令。
- DSH 始终是决策方。第一个被接受的答案生效;已答复或已取消的请求会被撤回,过期按钮无法重复触发操作。临时断连会保留未提交的草稿。
- 独立的完成/错误事件生产者、可选的子任务摘要、按类型开关、声音,以及当前会话前台静音模式。
- DSH Settings → DSH Notify 显示连接/助手/权限/版本状态,以及一个安全的测试通知按钮。测试状态会区分排队中、macOS 已接收、等待点击,以及已确认 Desktop 会话选择。
展开完整文档(README 剩余 1 节)
DSH macOS Notify 为 DeepSeek Harness Desktop 提供原生交互式 macOS 通知。DSH 插件与后台 DSH Notify.app 协同工作;点击通知即可返回 Desktop 对应的会话。 原生 macOS 通知、审批与完整问答:点击通知返回 DSH Desktop 对应会话,无须浏览器或额外模型服务。 - 在审批通知里直接「允许一次」或「拒绝」;默认点击打开完整审批详情,包含命令、格式化参数(可查看并复制原始内容)以及相关上下文。 - 一条问题提醒打开可滚动的多问题表单。每个问题都支持原有选项和独立的多行文本输入;一次提交整批内容。 - 命令与参数自动换行、会话名称、带表格和 LaTeX 公式的 Markdown 上下文,以及 Shell/JSON 高亮。显示格式不会改变提交的选项标签或复制的命令。 - DSH 始终是决策方。第一个被接受的答案生效;已答复或已取消的请求会被撤回,过期按钮无法重复触发操作。临时断连会保留未提交的草稿。 - 独立的完成/错误事件生产者、可选的子任务摘要、按类型开关、声音,以及当前会话前台静音模式。 - DSH Settings → DSH Notify 显示连接/助手/权限/版本状态,以及一个安全的测试通知按钮。测试状态会区分排队中、macOS 已接收、等待点击,以及已确认 Desktop 会话选择。 这是社区插件,不是 DeepSeek 官方产品。目前已在 DSH Desktop 0.2.0-rc.2 上验证。参见兼容性与验证。 安装 要求:macOS 13 或更高版本、官方 DSH Desktop、Node.js 22+ 以及 Apple 的 Command Line Tools。本地源码构建不需要完整 Xcode,也不需要付费签名证书。当前本地验证机器为 macOS 27.0.1 / Apple Silicon;较旧的 macOS 和 Intel 机器还需要额外的真机验证。 1. 克隆此仓库: git clone https://github.com/realDGD/dsh-macos-notify.git cd dsh-macos-notify 2. 构建并安装原生助手: Run once if Command Line Tools are not installed: xcode-select --install bash macos/install.sh 安装会创建 ~/Applications/DSH Notify.app 和一个登录 LaunchAgent。macOS 询问时,请允许 DSH Notify 发送通知。如果 Desktop 安装在别的位置,请在构建前将 DSH_DESKTOP_APP 设为其应用路径。构建使用你本地安装的官方 Desktop 中的图标;本仓库或发布版本不包含官方二进制/图标资源。 3. 在 DSH Desktop 中,打开左侧边栏的 Plugins → Add plugin,输入此克隆目录的绝对路径。安装并启用,然后重启 Desktop 一次。此路径仅在安装期间需要。 DSH 的包安装器也支持 GitHub 安装。输入 github:realDGD/dsh-macos-notify#v0.3.1,然后从匹配的 checkout 单独安装原生 helper。现有 JavaScript 已提交;不会运行包安装构建钩子。 4. 打开 Settings → DSH Notify。确认 helper 正在运行,且通知已获授权。发送安全测试通知,然后真正点击它。只有 Desktop 验证目标会话后,测试状态才会变为已确认;仅排队或 macOS 接受,并不能证明横幅可见或导航成功。 命令行安装时,等价的插件步骤是 dsh plugin --profile <your-profile> add <path-to-checkout>。Desktop 用户不需要再安装一个 npm DSH 可执行文件。GitHub Releases 中带版本号的 .tgz 源码包,包含相同的 helper 构建/安装脚本。 升级 把 checkout 更新到匹配的 release,关闭所有原生提问/审批窗口,然后再次运行 bash macos/install.sh。安装器会把之前的 helper 应用保留在私有状态目录下,保留偏好设置,并在替换前确认之前的 helper 已退出。 如有必要,用 DSH 的插件管理器从新 release 重新安装插件,并重启 Desktop 以加载新的 Host/client 模块。在 DSH Notify 中查看实际的插件和 helper 版本。不要只看源码版本就判断成功。 现有 dsh-notify-web / DSH Jump.app 用户注意:helper 安装会把已知的旧应用移入备份。启用这个独立包之前,先从活跃的 DSH bundle 列表中移除旧插件及其旧的原生完成/错误中继,以免出现重复通知。其他插件和会话历史不受影响。历史 helper bundle ID 和本地状态命名空间有意保留,这样本地通知权限和导航兼容性可以挺过迁移。 卸载 在 DSH 的插件管理器中移除 dsh-macos-notify,然后运行: bash macos/uninstall.sh 安装器只移除自己拥有的 helper 和启动文件。它会保留偏好设置和应用备份。重启 Desktop 以卸载 Host 插件。未知的应用/启动项绝不会被覆盖或移除。 隐私与行为 不使用额外的网络监听器、webhook、云服务或模型会话。官方已认证的 DSH Connection 承载导航和设置 RPC。同一用户的 Host/helper 记录在私有目录(0700,文件 0600)中原子交换。原生表单需要本地提问/审批内容;诊断不会导出这些内容。macOS 通知预览可能在锁屏上暴露显示的文字;请按你的偏好设置 macOS 预览。 静默模式只压制提醒。手动打开的表单仍然可用,它绝不会自动批准、拒绝或应答请求。关掉某类通知,并不会停用对应的 DSH 交互。 任务聚合会等待当前插件观察到的后代运行。新启动的根回合会取消它此前持有的完成状态。已中止、已打断、已阻塞的回合保持静默。冷安装不会重放旧的空闲会话。较旧的回合事件无法结束较新的子运行,后台窗口也无法清除另一个窗口的前台静默状态。 开发 npm ci npm test npm run test:native # macOS only; real AppKit components npm run check:release npm pack --dry-run Node 测试使用固定版本的 DSH Cordis 依赖,不会静默跳过生命周期测试。要针对某个已安装的 Desktop 产物做验证,把 DSH_CORDIS_MODULE 设为该产物的 Cordis 模块。公共 CI 会检查 Node 版本、原生编译与布局,以及包与隐私的完整性。真实的 macOS 通知验收和 Desktop 会话选择仍是独立的实机检查。 问题与选项标签使用原生 Markdown 文本格式。展开的上下文面板用内置的离线 Markdown 与 KaTeX 渲染标题、列表、引用、表格、代码和公式($…$、$$…$$、\(...\)、\[…\])。长上下文始终可以完整滚动;长表格单元格自动换行。宽公式有自己的滚动区域,保证可读。Shell/JSON 有语义配色;其他代码块保持等宽字体。远程图片显示为文本占位符,原始 HTML 不生效,只有明确点击的 HTTP(S) 链接才会在外部打开。 审批参数按缩进展示,不重新编码 JSON 键、数值或转义。查看原文 切换显示内容;复制原始参数 复制完全原始的内容。参数和命令都软换行,不出现横向滚动条。这些改动只影响显示,绝不影响底层的工具请求。 License MIT,见 LICENSE。DSH 名称与本地来源的官方图标用于标识与 Desktop 的集成;本仓库不授予第三方品牌的使用权。现有的本地通知权限使用一个历史 bundle identifier,它不是凭据。
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
realDGD/dsh-macos-notify
在通知里直接审批命令并填写多问题表单
相关插件 · 安全 / 权限
智能体逆向工程morluto/rea
用智能体逆向工程任何东西,从应用行为到原生二进制。
★ 415,267已汉化DSH
73.3
智能体集群框架ruvnet/ruflo
面向 Claude Code 与 Codex 的智能体元基座meta-harness。
★ 80,414已汉化MCP
71.9
WorkBuddy 模型接入corrinehu/dsh-workbuddy-connect
2026 年 10 月 20 日起,WorkBuddy…
★ 1,604已汉化DSH
71.7
破甲一键通z91772524-ai/pojia-next
十五个 AI…
★ 967已汉化DSH
71.6
AI 代理虚拟终端strukto-ai/mirage
Mirage 是给 AI 智能体用的虚拟终端:一层 shell,统管数据、命令行和运行时。Unix…
★ 3,756已汉化DSH
70.8
Agent 操作层agentic-os-org/ANOLISA
ANOLISA(Agentic Nexus Operating Layer & Interface System Architecture)|智能体操作系统,带运行时、安全、可观测性,以及 Tokenless 响应压缩,降低 token 用量与成本。
★ 649已汉化DSH
70.7