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

移动云端工作台 H2O-YAOZE/dsh-mobile-workbench

DeepSeek Harness
H2O-YAOZE21 天前有提交已提供中文文档扫描:含敏感能力

在手机上管理云端项目,让智能体改代码扩功能

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

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

自动兼容检查未通过:缺少 main/exports/bin 入口声明;engines.node 要求 >=24,不满足 Node 22.19.0;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add H2O-YAOZE/dsh-mobile-workbench
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · ui
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 21 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明;engines.node 要求 >=24,不满足 Node 22.19.0;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

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

✗npm 包dsh-mobile-workbench(未发布到 npm,仅可源码安装)
✗Node 引擎要求 >=24 · 基线 Node 22.19 不满足
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

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

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/14 13:15:00

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 77 处,展示前 70 条)
  • 执行外部命令来自配置/变量
    packages/document-reader/lib/worker-client.js:34
    const child=spawn(process.execPath,args,{stdio:['pipe','pipe','ignore'],
  • 发起网络请求
    packages/mobile-ui/dsh-push.mjs:106
    const res = await fetch(`http://127.0.0.1:${gatewayPort}/pwa/push/send`, {
  • 发起网络请求
    packages/mobile-ui/lib/client-routes.js:299
    const fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));
  • 发起网络请求
    packages/mobile-ui/lib/client.js:4166
    singleton ??= createSharesStore((input, init) => fetch(input, init));
  • 发起网络请求
    packages/mobile-ui/lib/client.js:4266
    res = await fetch(buildShareExportQuery(session, range), { cache: 'no-store' });
  • 发起网络请求
    packages/mobile-ui/lib/client.js:7463
    const response = await fetch((0, attach_upload_ts_1.uploadUrl)(sessionId, file.name), {
  • 发起网络请求
    packages/mobile-ui/lib/client.js:11934
    cached ??= fetch(exports.CLIENT_CONFIG_ROUTE, { cache: 'no-store' })
  • 发起网络请求
    packages/mobile-ui/lib/client.js:15962
    const res = await fetch(url, {
  • 发起网络请求
    packages/mobile-ui/lib/client.js:16067
    fetch(settings_form_ts_1.ADMIN_STATUS_ROUTE)
  • 写入本地文件
    packages/mobile-ui/lib/code-translation.js:21
    await mkdir(directory, { recursive: true, mode: 0o700 });
  • 写入本地文件
    packages/mobile-ui/lib/code-translation.js:23
    await writeFile(tmp, JSON.stringify(state), { mode: 0o600 });
  • 写入本地文件
    packages/mobile-ui/lib/code-translation.js:24
    await rename(tmp, file);
  • 读取本地文件
    packages/mobile-ui/lib/code-translation.js:32
    const raw = await readFile(filename(id), 'utf8');
  • 读取本地文件
    packages/mobile-ui/lib/config.js:262
    const parsed = JSON.parse(readFileSync(join(home, 'lan-gate.config.json'), 'utf8'));
  • 读取本地文件
    packages/mobile-ui/lib/fingerprint.js:228
    const text = readFileSync(file, 'utf8');
  • 写入本地文件
    packages/mobile-ui/lib/index.js:253
    await mkdir(path, { mode: 0o700 });
  • 读取本地文件
    packages/mobile-ui/lib/index.js:259
    const entry = await lstat(path);
  • 读取本地文件
    packages/mobile-ui/lib/index.js:267
    const canonical = await realpath(path);
  • 读取本地文件
    packages/mobile-ui/lib/index.js:293
    const workspace = await realpath(visibleWorkspace);
  • 删除本地文件
    packages/mobile-ui/lib/index.js:368
    await rm(path, { force: true }).catch(() => { });
  • 读取密钥环境变量
    packages/mobile-ui/lib/lan-gate-server.cjs:67
    var url = process.env.LAN_GATE_UPSTREAM_TOKEN_URL
  • 读取密钥环境变量
    packages/mobile-ui/lib/lan-gate-server.cjs:97
    var RELAY_SECRET = String(process.env.LAN_GATE_RELAY_SECRET || '')
  • 读取本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:164
    try { raw = JSON.parse(fs.readFileSync(stateFile(), 'utf8')) } catch (e) { raw = null }
  • 写入本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:193
    try { fs.renameSync(stateFile(), stateFile() + '.v1.bak'); console.log('[lan-gate] archived v1 state to lan-gate-state.json.v1.bak') } catch (e) {}
  • 写入本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:199
    fs.mkdirSync(dshHome(), { recursive: true })
  • 写入本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:201
    fs.writeFileSync(tmp, JSON.stringify(snapshot || state, null, 2), { encoding: 'utf8', mode: 0o600 })
  • 写入本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:202
    fs.chmodSync(tmp, 0o600)
  • 写入本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:203
    fs.renameSync(tmp, stateFile())
  • 发起网络请求
    packages/mobile-ui/lib/lan-gate-server.cjs:504
    var upstream = http.request({ host: TARGET_HOST, port: TARGET_PORT, method: 'GET', path: upstreamPath, headers: { host: TARGET_HOST + ':' + TARGET_POR
  • 读取本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:637
    try { body = fs.readFileSync(abs) } catch (e) { return false }
  • 读取本地文件
    packages/mobile-ui/lib/lan-gate-server.cjs:657
    function readPwaText(name) { try { return fs.readFileSync(path.join(PWA_DIR, name), 'utf8') } catch (e) { return '' } }
  • 发起网络请求
    packages/mobile-ui/lib/lan-gate-server.cjs:780
    var pReq = mod.request({ hostname: u.hostname, port: u.port, path: u.pathname + u.search, method: details.method, headers: details.headers }, function
  • 发起网络请求
    packages/mobile-ui/lib/lan-gate-server.cjs:928
    var exchange = http.request({
  • 发起网络请求
    packages/mobile-ui/lib/lan-gate-server.cjs:968
    var upstream = http.request({ host: TARGET_HOST, port: TARGET_PORT, method: req.method, path: req.url, headers: headers }, function (upRes) {
  • 发起网络请求
    packages/mobile-ui/lib/relay-client.js:250
    const fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));
  • 读取本地文件
    packages/mobile-ui/lib/relay-server.js:165
    const parsed = JSON.parse(readFileSync(file, 'utf8'));
  • 写入本地文件
    packages/mobile-ui/lib/relay-server.js:177
    mkdirSync(home, { recursive: true });
  • 写入本地文件
    packages/mobile-ui/lib/relay-server.js:178
    writeFileSync(`${file}.tmp`, JSON.stringify({ serverId: id }));
  • 写入本地文件
    packages/mobile-ui/lib/relay-server.js:179
    renameSync(`${file}.tmp`, file);
  • 读取本地文件
    packages/mobile-ui/lib/share-store.js:75
    raw = readFileSync(file, 'utf8');
  • 写入本地文件
    packages/mobile-ui/lib/share-store.js:103
    renameSync(file, `${file}.corrupt-${now()}`);
  • 写入本地文件
    packages/mobile-ui/lib/share-store.js:133
    mkdirSync(dirname(file), { recursive: true });
  • 写入本地文件
    packages/mobile-ui/lib/share-store.js:134
    writeFileSync(`${file}.tmp`, JSON.stringify({ version: FILE_VERSION, sessions: fileSessions }));
  • 写入本地文件
    packages/mobile-ui/lib/share-store.js:135
    renameSync(`${file}.tmp`, file);
  • 执行外部命令来自配置/变量
    packages/mobile-ui/persistent-gateway.mjs:10
    const child=spawn(nodePath,[serverFile],{cwd,env,stdio:['ignore','pipe','pipe'],shell:false})
  • 发起网络请求
    packages/mobile-ui/src/client-routes.ts:366
    const fetchImpl: FetchLike = options.fetchImpl ?? ((url, init) => fetch(url, init))
  • 写入本地文件
    packages/mobile-ui/src/code-translation.ts:31
    await mkdir(directory, { recursive: true, mode: 0o700 })
  • 写入本地文件
    packages/mobile-ui/src/code-translation.ts:33
    await writeFile(tmp, JSON.stringify(state), { mode: 0o600 }); await rename(tmp, file)
  • 写入本地文件
    packages/mobile-ui/src/code-translation.ts:33
    await writeFile(tmp, JSON.stringify(state), { mode: 0o600 }); await rename(tmp, file)
  • 读取本地文件
    packages/mobile-ui/src/code-translation.ts:39
    const raw = await readFile(filename(id), 'utf8')
  • 读取本地文件
    packages/mobile-ui/src/config.ts:363
    const parsed: unknown = JSON.parse(readFileSync(join(home, 'lan-gate.config.json'), 'utf8'))
  • 读取本地文件
    packages/mobile-ui/src/fingerprint.ts:274
    const text = readFileSync(file, 'utf8')
  • 写入本地文件
    packages/mobile-ui/src/index.ts:266
    await mkdir(path, { mode: 0o700 })
  • 读取本地文件
    packages/mobile-ui/src/index.ts:270
    const entry = await lstat(path)
  • 读取本地文件
    packages/mobile-ui/src/index.ts:277
    const canonical = await realpath(path)
  • 读取本地文件
    packages/mobile-ui/src/index.ts:304
    const workspace = await realpath(visibleWorkspace)
  • 删除本地文件
    packages/mobile-ui/src/index.ts:372
    await rm(path, { force: true }).catch(() => {})
  • 发起网络请求
    packages/mobile-ui/src/relay-client.ts:462
    const fetchImpl: typeof fetch = options.fetchImpl ?? ((url, init) => fetch(url, init))
  • 读取本地文件
    packages/mobile-ui/src/relay-server.ts:312
    const parsed: unknown = JSON.parse(readFileSync(file, 'utf8'))
  • 写入本地文件
    packages/mobile-ui/src/relay-server.ts:322
    mkdirSync(home, { recursive: true })
  • 写入本地文件
    packages/mobile-ui/src/relay-server.ts:323
    writeFileSync(`${file}.tmp`, JSON.stringify({ serverId: id }))
  • 写入本地文件
    packages/mobile-ui/src/relay-server.ts:324
    renameSync(`${file}.tmp`, file)
  • 读取本地文件
    packages/mobile-ui/src/share-store.ts:162
    raw = readFileSync(file, 'utf8')
  • 写入本地文件
    packages/mobile-ui/src/share-store.ts:185
    renameSync(file, `${file}.corrupt-${now()}`)
  • 写入本地文件
    packages/mobile-ui/src/share-store.ts:215
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    packages/mobile-ui/src/share-store.ts:216
    writeFileSync(`${file}.tmp`, JSON.stringify({ version: FILE_VERSION, sessions: fileSessions }))
  • 写入本地文件
    packages/mobile-ui/src/share-store.ts:217
    renameSync(`${file}.tmp`, file)
  • 执行外部命令固定命令
    packages/workbench/cloud-meter.js:67
    const child=compressed?spawn('nice',['-n','10','zstd','-dc','-M32MB','--',file],{stdio:['ignore','pipe','ignore']}):null;
  • 执行外部命令固定命令
    packages/workbench/projects.js:200
    if(pathname==='/projects/api/mail-refresh'&&req.method==='POST'){if(mailScanProcess)throw Error('邮箱目录正在更新');mailScanError='';mailScanProcess=execFile(
  • 发起网络请求
    packages/workbench/workspace.js:13
    async function api(url,body){const r=await fetch(url,{credentials:'same-origin',...(body!==undefined?{method:'POST',headers:{'Content-Type':'applicati
扫描于 2026/11/3(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

基于 DeepSeek Harness 的自托管移动工作台:项目、产物、代码翻译与用量。

README

由 DeepSeek 最新模型翻译生成
DSH Mobile Workbench · DSH 移动云端工作台 基于 DeepSeek Harness 的独立扩展项目:云端运行智能体,手机随时访问、管理项目,也可以通过对话提出需求,让智能体修改和扩展工作台自身的网页与插件。包含项目与文件来源管理、中文代码翻译和用量视图。 这不是 DeepSeek 官方产品,也不包含模型权重。当前为首个开源实验版(0.1.0-alpha.1)。需要自行提供服务器、HTTPS 域名和模型账户。 English · 安装说明 · 发布检查 操作演示 观看手机端操作视频(2 分 36 秒) 个人部署实录:项目浏览、网站使用、文件入口、对话和用量视图。展示中的个人网站与数据不包含在开源代码中。 为什么做 使用近两个月后,我希望把分散的 AI 项目和文件集中管理,并在不打开个人电脑时,通过手机继续修改云端项目。项目工作台把文件、产生文件的对话和后续修改关联起来;代码翻译便于阅读项目结构和改动。 模型、智能体,以及可以继续修改自身的工作台 我对未来 AI 解决方案的理解是:模型和智能体会成为两层基础。模型提供语言、推理、代码、图像等能力,智能体把模型与工具、上下文、权限和执行流程连接起来。不同的模型有不同的长处,不同的智能体也有不同的组织和执行方式;把它们组合起来,才能适配不同的任务场景。
展开完整文档(README 剩余 1 节)
DSH Mobile Workbench · DSH 移动云端工作台

基于 DeepSeek Harness 的独立扩展项目:云端运行智能体,手机随时访问、管理项目,也可以通过对话提出需求,让智能体修改和扩展工作台自身的网页与插件。包含项目与文件来源管理、中文代码翻译和用量视图。

这不是 DeepSeek 官方产品,也不包含模型权重。当前为首个开源实验版(0.1.0-alpha.1)。需要自行提供服务器、HTTPS 域名和模型账户。

English · 安装说明 · 发布检查

操作演示

观看手机端操作视频(2 分 36 秒)

个人部署实录:项目浏览、网站使用、文件入口、对话和用量视图。展示中的个人网站与数据不包含在开源代码中。

为什么做

使用近两个月后,我希望把分散的 AI 项目和文件集中管理,并在不打开个人电脑时,通过手机继续修改云端项目。项目工作台把文件、产生文件的对话和后续修改关联起来;代码翻译便于阅读项目结构和改动。

模型、智能体,以及可以继续修改自身的工作台

我对未来 AI 解决方案的理解是:模型和智能体会成为两层基础。模型提供语言、推理、代码、图像等能力,智能体把模型与工具、上下文、权限和执行流程连接起来。不同的模型有不同的长处,不同的智能体也有不同的组织和执行方式;把它们组合起来,才能适配不同的任务场景。

这也是我选择 DeepSeek Harness 的重要原因。它的「一切皆插件」,让我想到:既然智能体已经可以帮我修改项目,那在开放相应源码和权限之后,能不能也让它继续修改自己的工作台?

在手机上,我可以直接提出需求:给上传图片和上传文件分别做一个入口、增加一个 PDF 转 Markdown 的功能,或者调整深色/浅色界面和操作流程。智能体可以修改对应的网页、插件和服务代码,再构建、验证并应用更新;必要时重启相关服务。这里「改自己」修改的是智能体工作台的软件功能、工具和界面。

APK 主要承载云端 Web 界面,因此多数网页和插件更新可以直接在云端生效,不需要每次重新安装 APK;原生桥接、系统权限等变化仍需新版本 APK。

所以我希望这个 Personal Agent 能一边积累自己的项目和上下文,一边根据实际需求继续扩展。今天需要处理文档,就补文档能力;明天需要新的模型或工具,就继续接入。模型与智能体的组合方式可以变化,而自己的项目和使用环境能够持续保留下来。这是本项目的产品方向;当前已经开放的是这套云端工作台、插件扩展和手机访问实现。

包含什么

- 移动界面、配对网关和通知接口:packages/mobile-ui。
- 项目工作台、文件来源关联、按需中文翻译、邮箱附件目录、用量视图和手机操作协议:packages/workbench。
- 文档读取插件及读取预算限制:packages/document-reader。
- Android WebView 外壳、文件选择、原生通知及需要用户授权的手机操作:android。

没有聊天记录、个人项目、邮箱目录或附件、生产配置、密钥、模型账户信息。示例目录为空。邮箱集成需要你自行授权自己的邮箱;代码不会向作者邮箱发送内容。

网络安全防御工程

项目部署到公网以后,我开始注意到持续出现的陌生扫描和探测请求。按我的日常观察,有时一天能看到几十个扫描来源;这不是经过统一去重的统计,也不能仅凭请求判断对方是否使用了 AI 智能体。把项目长期放在云端,除了方便调用,也要保护项目文件、对话和账户信息。

因此,我在自己的部署中配套做了网络安全防御工程:收紧公网端口和访问入口、修复未授权文件访问、检查凭据与依赖风险,并保存服务器之外的加密备份。在授权范围内,可以让智能体协助检查自己的项目、分析异常和修复问题;实际访问拦截由服务器防火墙和认证规则执行,模型判断不能替代这些规则。需要处理的发现应形成可追踪的记录和提醒,修复后还要验证。

开源包与个人部署的范围不同。 本仓库包含配对与设备认证、项目文件访问边界、私密页面不落入 PWA 离线缓存、受授权约束的手机操作等工作台防护。主机防火墙、扫描监测与告警、异地备份和恢复流程属于配套部署工程,目前没有随本仓库打包成一键安装的防御系统。扫描请求不等于成功入侵;本项目不提供自动反击,也不承诺阻止所有攻击。

架构与限制

手机 → HTTPS / 配对网关 → Harness / 插件 → 已注册项目。模型通过 API 调用,服务器不加载大模型权重。项目文件在文件系统,索引保存关联关系;不把所有源码塞进聊天数据库。

Web UI / 插件更新通常不必重装 APK;原生权限、桥接和系统通知能力更新仍需新 APK。服务器可持续运行不等于任务永不停止,任务占用仍须限制。电脑用量入口是上传的快照,不保证所有工具的实时计量。翻译需要模型 Token,首次按需生成并缓存;打开目录本身不发模型请求。

安全能力表述限于认证、授权、隔离和资源保护。本仓库不提供对第三方系统自动反击的能力,也不能保证网站永不被攻击。生产服务器的完整防御工程不是本次导出范围。

开发验证

npm ci --ignore-scripts
npm test
npm run verify:ui
npm run build:ui

依赖构建和从零部署的实际验证结果见 发布检查。安装步骤见 配置说明,从既有官方 web profile 加载本仓库扩展。尚未提供经过完整验收的一键部署。

Android 要求 JDK 17、Android SDK 34 和 Gradle 8.9(当前不附 wrapper):

cd android
gradle assembleDebug -PdshBaseUrl=https://your-domain.example

默认域名为 example.invalid;发布版请自行签名。不同 applicationId 的公开示例不能覆盖安装你的私人 APK。

安全与开源范围

参见 SECURITY、上游与许可、配置说明。部署前必须核对认证入口、仅绑定内网的服务端口、项目注册范围和独立备份。不要把智能体以不受约束的 root 权限提供给任意访客。
预览分享卡片长什么样 ↓H2O-YAOZE/dsh-mobile-workbench
分享 · DeepSeek Harness Hub
H2O-YAOZE/dsh-mobile-workbench

在手机上管理云端项目,让智能体改代码扩功能

这是你的仓库?认领条目

回到顶部