🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

桌面 Agent 工作台 harness-home/harness-ai-desktop

DeepSeek Harness
harness-home68 天前有提交已提供中文文档扫描:高危

在本机跑 Agent 运行时,手机远程审批与托管会话

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/8/28 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

该插件未发布到 npm,按作者文档从 GitHub 源安装即可(静态检查只覆盖 npm 路径,不代表插件不可用)。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add harness-home/harness-ai-desktop
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · market
装得上吗
实装验证未通过(unsupported_dep_spec),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 68 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(unsupported_dep_spec · 2026/10/19) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包harness-ai-desktop(未发布到 npm,仅可源码安装)
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖要求 catalog:dsh · 最新 0.2.1-rc.2 兼容
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/20 04:29:58

🔴 安全扫描发现高危能力自动 · 静态分析源码
v2.7.0
🔴 扫描:高危尚无人工评估

静态扫描发现安装期即执行代码,或命令参数由运行时变量拼接(参数来源无法静态判定,从严处理)。

安装时即执行脚本可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 47 处,展示前 47 条)
  • 执行外部命令来自配置/变量
    scripts/install-electron.mjs:12
    const result = spawnSync(process.execPath, [installer], { stdio: 'inherit' })
  • 读取本地文件
    plugins/windows-pwsh-sandbox/src/index.ts:28
    return candidates.find(candidate => existsSync(candidate))
  • 发起网络请求
    scripts/acceptance.mjs:16
    const res = await fetch(`${BASE}/api/${method}`, {
  • 发起网络请求
    scripts/acceptance.mjs:27
    const res = await fetch(`${BASE}/api/respond`, {
  • 读取密钥环境变量
    scripts/acceptance.mjs:88
    if (!process.env.DSKEY) fail('DSKEY env var missing')
  • 读取密钥环境变量
    scripts/acceptance.mjs:89
    await call('credentials.set', { ref: 'DEEPSEEK_API_KEY', value: process.env.DSKEY })
  • 发起网络请求
    scripts/app-click.mjs:12
    const targets = await (await fetch(`http://127.0.0.1:${port}/json/list`)).json()
  • 读取本地文件
    scripts/app-eval.mjs:13
    const expression = args[0] === '--file' ? readFileSync(args[1], 'utf8') : args.join(' ')
  • 发起网络请求
    scripts/app-eval.mjs:15
    const targets = await (await fetch(`http://127.0.0.1:${port}/json/list`)).json()
  • 读取本地文件
    scripts/gen-notices.mjs:24
    return existsSync(join(dir, 'package.json')) ? dir : undefined
  • 读取本地文件
    scripts/gen-notices.mjs:44
    const queue = Object.keys(JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).dependencies ?? {})
  • 读取本地文件
    scripts/gen-notices.mjs:62
    const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'))
  • 写入本地文件
    scripts/gen-notices.mjs:86
    writeFileSync(join(root, 'THIRD_PARTY_NOTICES.md'), `# Third-Party Notices
  • 写入本地文件
    scripts/generate-icon.mjs:244
    mkdirSync(out, { recursive: true })
  • 写入本地文件
    scripts/generate-icon.mjs:245
    writeFileSync(join(out, 'icon.png'), renderPng(256))
  • 写入本地文件
    scripts/generate-icon.mjs:246
    writeFileSync(join(out, 'icon.ico'), renderIco([16, 24, 32, 48, 64, 128, 256]))
  • 读取本地文件
    scripts/runtime-closure.mjs:15
    return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : undefined
  • 读取本地文件
    scripts/runtime-closure.mjs:15
    return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : undefined
  • 读取本地文件
    scripts/runtime-closure.mjs:19
    const app = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
  • 读取本地文件
    scripts/runtime-closure.mjs:40
    const declared = new Set(Object.keys(JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).dependencies ?? {}))
  • 读取本地文件
    scripts/set-dsh-version.mjs:43
    const content = readFileSync(workspaceFile, 'utf8')
  • 写入本地文件
    scripts/set-dsh-version.mjs:67
    writeFileSync(workspaceFile, lines.join('\n'))
  • 读取本地文件
    scripts/smoke-packaged.mjs:14
    if (!existsSync(join(unpacked, exeName))) {
  • 删除本地文件递归
    scripts/smoke-packaged.mjs:20
    rmSync(stage, { recursive: true, force: true })
  • 写入本地文件
    scripts/smoke-packaged.mjs:21
    mkdirSync(stage, { recursive: true })
  • 执行外部命令来自配置/变量
    scripts/smoke-packaged.mjs:25
    const child = spawn(join(stage, exeName), [], { stdio: ['ignore', 'pipe', 'pipe'] })
  • 发起网络请求
    scripts/smoke-packaged.mjs:37
    const res = await fetch(`http://127.0.0.1:${String(port)}/`, { signal: AbortSignal.timeout(2000) })
  • 发起网络请求
    scripts/smoke-packaged.mjs:53
    const bundle = await fetch(`http://127.0.0.1:${String(endpoint.port)}/plugins/@harness-ai/${plugin}/client.js`)
  • 删除本地文件递归
    scripts/smoke-packaged.mjs:64
    rmSync(stage, { recursive: true, force: true, maxRetries: 10, retryDelay: 500 })
  • 读取本地文件
    scripts/smoke.mjs:18
    if (!existsSync(command)) {
  • 执行外部命令来自配置/变量
    scripts/smoke.mjs:23
    const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] })
  • 发起网络请求
    scripts/smoke.mjs:31
    const response = await fetch(`http://127.0.0.1:${String(port)}/`, { signal: AbortSignal.timeout(2000) })
  • 发起网络请求
    scripts/smoke.mjs:58
    const bundle = await fetch(`http://127.0.0.1:${String(endpoint.port)}/plugins/@harness-ai/harness-brand/client.js`)
  • 读取本地文件
    src/main/crash.ts:62
    return readdirSync(dumpDir()).filter((name) => name.endsWith('.dmp')).length
  • 读取本地文件
    src/main/crash.ts:71
    const parsed: unknown = JSON.parse(readFileSync(markerPath(), 'utf8'))
  • 写入本地文件
    src/main/crash.ts:114
    mkdirSync(app.getPath('userData'), { recursive: true })
  • 写入本地文件
    src/main/crash.ts:115
    writeFileSync(markerPath(), JSON.stringify(marker), 'utf8')
  • 读取本地文件
    src/main/crash.ts:128
    if (existsSync(markerPath())) rmSync(markerPath())
  • 删除本地文件
    src/main/crash.ts:128
    if (existsSync(markerPath())) rmSync(markerPath())
  • 写入本地文件
    src/main/log.ts:21
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    src/main/log.ts:24
    if (statSync(file).size > MAX_LOG_BYTES) renameSync(file, join(dir, 'main.previous.log'))
  • 读取本地文件
    src/main/log.ts:24
    if (statSync(file).size > MAX_LOG_BYTES) renameSync(file, join(dir, 'main.previous.log'))
  • 写入本地文件
    src/main/log.ts:28
    stream = createWriteStream(file, { flags: 'a' })
  • 读取本地文件
    src/main/runtime-config.ts:158
    if (existsSync(path)) return readFileSync(path, 'utf8')
  • 读取本地文件
    src/main/runtime-config.ts:158
    if (existsSync(path)) return readFileSync(path, 'utf8')
  • 读取本地文件
    src/main/updater.ts:150
    if (!existsSync(path)) return undefined
  • 读取本地文件
    src/main/updater.ts:152
    return readFileSync(path, 'utf8')
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cordis-plugin-group@deepseek-ai/cordis-plugin-include@deepseek-ai/cordis-plugin-loader@deepseek-ai/cordis-plugin-timer@deepseek-ai/dsh@deepseek-ai/dsh-anonymous-user-id@deepseek-ai/dsh-app-boot@deepseek-ai/dsh-atomic-write@deepseek-ai/dsh-authorization@deepseek-ai/dsh-base@deepseek-ai/dsh-bash-local
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 本机启动智能体运行时
  • 手机远程审批
  • 托管会话

Harness AI 桌面客户端——Electron 应用,在回环地址上以进程内方式启动 DeepSeek Harness(dsh)智能体运行时,支持托管会话、远程审批和带门槛的插件市场。

README

由 DeepSeek 最新模型翻译生成
English · 简体中文 工作原理 · 功能 · 安全模型 · 构建 · 发版 · 目录结构 · 路线图 harness-ai-desktop 是 Harness AI 的桌面客户端:基于 DeepSeek Harness(dsh)的类 Codex / Claude Code 桌面 Agent 工作台。 Agent 运行时跑在这个应用里、跑在你自己的机器上——就在 Electron 主进程内,只绑定回环地址,不开放任何入站端口。客户端补上的是运行时本身不提供的部分:账号、能跟到手机上的托管会话、远程审批,以及一个前置了供应链闸门的插件市场。 [!IMPORTANT] 开发者预览。 上游运行时处于 developer preview,官方明示会有破坏性变更;本客户端以固定版本跟随。安装包发布在 Releases 页——未做代码签名,每次发布都附 SHA-256 供核对——也可以从源码构建。
展开完整文档(README 剩余 1 节)
English · 简体中文

工作原理 · 功能 · 安全模型 · 构建 · 发版 · 目录结构 · 路线图

harness-ai-desktop 是 Harness AI 的桌面客户端:基于 DeepSeek Harness(dsh)的类 Codex / Claude Code 桌面 Agent 工作台。

Agent 运行时跑在这个应用里、跑在你自己的机器上——就在 Electron 主进程内,只绑定回环地址,不开放任何入站端口。客户端补上的是运行时本身不提供的部分:账号、能跟到手机上的托管会话、远程审批,以及一个前置了供应链闸门的插件市场。

[!IMPORTANT]
开发者预览。 上游运行时处于 developer preview,官方明示会有破坏性变更;本客户端以固定版本跟随。安装包发布在 Releases 页——未做代码签名,每次发布都附 SHA-256 供核对——也可以从源码构建。

工作原理

flowchart LR
subgraph machine["你的机器"] direction TB
ui["内嵌 dsh Web UI+ Harness AI 插件"]
main["Electron 主进程"]
dsh["dsh 运行时(同进程)127.0.0.1:43110"]
files["工作区文件 · 凭据 · 会话日志"]
main --> dsh
ui dsh
dsh --> files
end

main -.->|"只出不进"| server["harness-ai-server账号 · 托管会话 · 市场目录"] server -.-> phone["harness-ai-app查看 · 回答 · 审批"]
dsh -.->|"密钥取自本机凭据存储"| model["模型服务"]

支撑这张图的三条性质:

1. 运行时不在回环之外监听。 手机端能看到的一切,都是这个客户端主动向外推送的结果;没有任何连接是拨进来的。
2. 壳只经一层窄适配(HarnessAdapter)对接运行时,经本地 /api 对接托管服务,不碰 dsh 内部实现——这样升级上游是改版本号,而不是重写。
3. 我们自己的功能也是插件。 品牌、账号面板、市场面板、原生目录选择器、Windows 沙箱执行器都是叠加在上游 profile 上的 Cordis 插件,用的是和第三方插件同一套扩展机制。

功能

运行时托管

| 同进程启动 | 用官方 bundle 分层组合出桌面 profile,在 Electron 主进程内启动 dsh Host——没有子运行时,也不需要第二个 Node。 |
| 回环绑定 | 绑定 127.0.0.1:43110;端口被占用则顺延到下一个空闲端口,最多探测 20 次。 |
| 版本钉死 | 所有 @deepseek-ai/ 依赖统一走 pnpm catalog,升级上游是一行改动(pnpm dsh:version),而不是逐条改三十个依赖。 |
| Electron 宿主修复 | 上游有两处用 process.execPath 拉起 Node,在 Electron 下等于再开一个应用实例*。两处都在接缝上修好:原生目录选择器 worker 走 child_process 注入,Windows ACL PowerShell 沙箱执行器走 trampoline。 |

账号、托管会话与远程控制

| 登录 | 应用内登录;每台设备有独立身份,可在服务端吊销。 |
| 会话托管 | 本地会话事件镜像到 harness-ai-server,同一段对话可在手机端查看并接续。 |
| 上传前脱敏 | 密钥形状的字符串在上传之前遮蔽;工作目录命中 denylist 的会话根本不同步;大块字节不走事件通道。 |
| 附件 | Agent 产出的图片按内容寻址(sha256:…),走独立通道排在事件之后上传,按账号去重并受服务端配额约束。 |
| 远程审批与指令 | 越出工作区的工具调用会先发起审批而不是直接执行——在桌面前处理,或者在手机上处理。桌面离线期间发来的指令由服务端暂存,重连即投递。 |

插件市场与供应链闸门

运行时的权限体系管的是工具调用,不是插件带来的代码。所以所有防线都前移到安装之前:

- 每个目录条目上的风险标记:安装期脚本、原生构建、无来源证明、无许可证、低采用率、新包。
- 每次安装前的告知闸门——包括网页经 harness-ai://install?listing= 交接过来的安装——明说插件拥有与客户端相同的访问权限。
- 写入任何文件之前,回注册表复核完整性摘要:目录记录的 integrity 必须仍然对得上。这一步补的正是版本号钉死补不上的洞——同一个版本号可以被重新发布,字节已经变了。
- 显式写出 --ignore-scripts,不依赖某个可能被改动的配置文件。
- 安装完成后扫描实际能力并回报:网络访问、写文件、原生模块。
- 安装日志:每次改动前先把 profile manifest 原文入日志,失败或崩溃后整份还原——只还原 manifest 文本,不删 node_modules。

可靠性

单实例锁 · 上次运行的崩溃审计 · 脱敏后的文件日志 · 系统托盘 · 启动失败时的恢复页(重试 / 打开日志 / 退出) · 以及一个按进度而不是按墙钟计时的启动看门狗(20 秒无进度 / 180 秒绝对上限),免得把慢机器当成卡死。

应用更新

客户端在启动后不久检查一次新版本,之后每 6 小时一次,发现后在后台下载,过程在托盘里可见。绝不背着用户安装。 下载完成时会问你一次:

| 选择 | 会发生什么 |
| 立即重启并安装 | 马上重启进入新版本。 |
| 稍后 | 保留更新,下次退出时装上。 |
| 取消 | 保留已下载的文件,但不再自动安装。托盘里随时可以手动装。 |

关掉这个窗口等同于「取消」。你自己发起的检查一定会给答复,包括「已是最新」。

安全模型

| 红线 | 保证 |
| 只走回环 | 运行时绑定 127.0.0.1。没有任何入站连接能到达你的机器——服务端不能,手机也不能。 |
| 模型密钥不出本机 | API Key 存在本机的 dsh 凭据存储里,永不上传。 |
| 上传前脱敏 | 遮蔽与工作目录 denylist 都在客户端执行,被过滤掉的内容托管服务根本收不到。 |
| 审批必须显式 | 越出工作区的动作需要人来回答,且每个决定都可审计。 |
| 安装必须告知 | 没有闸门就不会安装;网页只能交接一个目录 id,指定不了包名和版本。 |

发现安全问题请看 SECURITY.md。

安装后的配置

有些设置属于网络环境而不属于某个人,改错了也不该重新出一版客户端。这类设置放在安装目录下、与 Harness AI.exe 同级的 harness-ai.config.json 里。改完重启客户端,日志里的 config: plugin registry … 会写明实际生效的值。

| 键 | 默认值 | 用途 |
| pluginRegistry | https://registry.npmjs.org/ | 插件市场解析和下载插件所用的 npm registry。公共 registry 慢或不可达时,改指向它的镜像即可,例如 https://registry.npmmirror.com/。 |
| serverUrl | https://api.harnessai.io | 本客户端登录、同步托管会话所连的服务端。自托管部署改成自己的地址即可——自己跑服务端不需要自己出一版客户端。 |

指向镜像不会降低市场的门槛:完整性复核会从当前配置的这个 registry 读取,也就是从 tarball 真正会被下载的地方读——镜像给出的字节与目录记录的不一致就直接拒绝,而不是照单全收。但它必须是公共 registry 的镜像:一个用同样的包名提供自家包的私有 registry 过不了这道校验。

文件写坏了也不会让客户端起不来:该值被丢弃、原因进日志、回落到默认值。HARNESS_PLUGIN_REGISTRY 环境变量可以对单次运行覆盖文件里的值。应用更新会把这个文件重写回随包默认值,更新后需要重新改一遍。

从源码构建

前置条件 —— Node ^22.19.0 || >=24、pnpm 11、Windows x64(目前唯一的打包目标),以及一个 DeepSeek API Key(凡是要接模型的环节都需要)。

与移动端、服务端共用的线上契约以 @harness-ai/contracts 从 npm 获取,所以直接克隆本仓库就能装依赖、能构建,不需要工作区里的其它仓库。

pnpm install # postinstall 拉取 Electron 二进制
pnpm typecheck
pnpm test # 146 项单测,离线
pnpm dev # 先构建仓内插件,再启动壳

| 命令 | 作用 |
| pnpm dev | 生成图标 → 构建仓内插件 → electron-vite dev。 |
| pnpm build | main / preload / renderer 与插件的生产构建。 |
| pnpm typecheck | tsc --noEmit,必须零错误。 |
| pnpm test | Vitest 单测——刻意保持离线且快。 |
| pnpm test:e2e | 需要网络或真实 registry 的验证:插件安装、托管附件全链路。 |
| pnpm dist:win | 产出 NSIS 安装包到 dist/,前置第三方声明闸门与 afterPack 硬校验。 |
| pnpm smoke:packaged | 启动打包产物,断言回环端点、运行时页面与品牌插件。 |
| pnpm dsh:version | 一步把所有钉死的 dsh 依赖切到新的上游版本。 |

完整发版清单(含人工步骤)见 docs/acceptance.md。

发版

每次 push 与 PR 都会在 Windows 上跑一遍类型检查、单测与生产构建——客户端实际发布的平台。

发版由 tag 驱动。推一个 v 标签会跑同一道闸门,打出 NSIS 安装包,并连同应用内更新器要读的 latest.yml 与 blockmap 一起发布:

git tag v0.1.5 && git push origin v0.1.5

标签必须与 package.json 里的 version 一致,且 CHANGELOG.md 里必须有对应版本的条目——两项都在开始构建之前检查,那段条目会直接成为发布说明。安装包未做代码签名——SmartScreen 会告警,发布说明里带了 SHA-256 供核对。

目录结构

src/main/ Electron 主进程:启动、托盘、更新、崩溃审计、日志、深链 harness/ dsh 接缝——适配层、启动、托管桥、市场与安装闸门 account/ 账号服务与设备身份 src/preload/ 上下文隔离的渲染进程桥 src/renderer/ 包住内嵌运行时 UI 的壳 src/shared/ i18n(en-US / zh-CN)与壳 API 类型 plugins/ brand/ 运行时 UI 内的产品标识(托盘、主题、侧栏) account-ui/ 登录与设备面板 market-ui/ 插件市场面板、风险标记与安装闸门 electron-directory-picker/ 原生工作区选择器 windows-pwsh-sandbox/ Windows ACL 沙箱执行器(已适配 Electron 宿主) scripts/ 图标生成、打包校验、冒烟与验收驱动脚本

路线图

| ✅ 已完成 | 同进程运行时托管 · 账号与设备身份 · 托管会话同步 · 附件同步 · 远程审批与指令排队 · 带安装闸门的插件市场 · Windows 打包 |
| 🚧 进行中 | 稳定的更新源——发布物已带 latest.yml,但 feed 地址在分发位置定下来之前仍是占位 |
| 📋 计划中 | macOS 打包与代码签名 · 若确有必要,在同一层适配下接入第二种 harness |

参与贡献

欢迎提 issue 和 PR,请先读 CONTRIBUTING.md:提交约定、源码全英文规则,以及涉及上游行为的改动应当如何分级(先插件,补丁层放最后)。

致谢

构建于 DeepSeek Harness(MIT)与 Cordis 之上。打包产物的第三方声明在发版时生成到 THIRD_PARTY_NOTICES.md。

许可

MIT © harness-home
预览分享卡片长什么样 ↓harness-home/harness-ai-desktop
分享 · DeepSeek Harness Hub
harness-home/harness-ai-desktop

在本机跑 Agent 运行时,手机远程审批与托管会话

这是你的仓库?认领条目

回到顶部