deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
DeepSeek Harness (DSH) 插件:把一个安卓项目文件夹直接构建成 APK。 缺少的工具链(JDK / Android SDK / Gradle)会自动下载到工作文件夹内,不污染用户目录。
更新记录见 CHANGELOG.md。
settings.gradle / settings.gradle.kts / build.gradle),可选支持 Gradle Wrapper。JAVA_HOME/PATH 里有满足要求的 Java(AGP 8 / Gradle 8 需要 ≥17,否则 ≥11,插件会先探测工程里的 AGP 版本)就直接用;否则按当前系统与架构下载对应的 Temurin JDK(Windows/Linux/macOS × x64/aarch64…,Windows 取 .zip、其它取 .tar.gz)到下载文件夹,并复用上次下载的 JDK,不重复下载。若请求的版本在该平台没有官方构建(例如 Temurin 没有 windows/aarch64 的 JDK 17),会自动沿 LTS 阶梯回退到有构建的版本(17 → 21 → 25)并在日志里说明原因。local.properties 的 sdk.dir、ANDROID_HOME、ANDROID_SDK_ROOT、%LOCALAPPDATA%\Android\Sdk;都没有则下载 commandline-tools + platform-tools + build-tools + platform 到下载文件夹,并自动写入 local.properties 和接受许可。local.properties 里失效的 sdk.dir(指向已删除的 SDK)会被自动修正。gradle-wrapper.properties 里的版本(或配置的 gradleVersion)下载发行版。assemble<Variant>(默认 debug,可用 release),把产物 APK 复制到输出文件夹。android.jar)时,自动把工程和 SDK 复制到一个可用的 ASCII 临时目录(%TEMP%\dsh-android-build)里构建,再把 APK 复制回你的输出文件夹。staged SDK 会缓存复用,不重复下载。GRADLE_USER_HOME)都放在插件自己的 toolchain/ 目录(<插件安装目录>/toolchain,可用 downloadDir 覆盖)。放在插件根目录意味着所有会话、所有工程共用同一套工具链——不会每个工作区各留一份几百 MB 的缓存,也不污染用户目录。在 DSH 宿主机器上(需要 pnpm 在 PATH 上)。把 <插件路径> 换成这个 tarball 在你机器上的实际位置,
<profile> 换成你要装的 profile 名:
dsh plugin --profile <profile> add "<插件路径>/dsh-plugin-android-apk-0.2.4.tgz"
<profile>填什么? 桌面端常见的是desktop,Web 端常见的是web,但名字由你自己决定, 取决于当前跑的是哪个 profile——填错不会报错,只是"装到另一个 profile 去了",当前会话看不到这个工具。 查看本机有哪些 profile:ls ~/.dsh/profiles(Windows:dir %USERPROFILE%\.dsh\profiles)。 想知道当前会话用的是哪个,可在任务管理器里看 DSH 主进程的命令行,末尾那个路径就是...\.dsh\profiles\<名字>。插件与 profile 无关:
cordis.patch.yml按包名插入一行插件,代码里没有写死任何 profile 名, 所以desktop/web/ 自定义 profile 都同样可用。唯一的差别是每个 profile 要各装一次—— 它属于 profile 级依赖;如果你同时用桌面端和 Web 端(通常就是两个 profile),两边都装才会都出现。
说明:
dsh plugin实际是在 profile 目录里执行pnpm add <spec>,然后把声明了dsh.bundle.patch的包加入dsh.profile.bundles层。手动操作等价于: 在~/.dsh/profiles/<profile>执行pnpm add <spec>,再把包名加进package.json的dsh.profile.bundles。
⚠️ 依赖声明为
peerDependencies(0.2.1 起):@deepseek-ai/cordis/@deepseek-ai/dsh-tools/@deepseek-ai/schemastery由 DSH 宿主提供并做解析拦截, 插件不能自己装一份——否则 profile 里的旧副本会遮蔽宿主自己的tools行,导致1 required plugin did not activate、桌面端无法启动。请不要在 profile 里手动pnpm add这三个包。
装好后,直接对 Agent 说“把 xxx 文件夹构建成 APK”,或显式调用工具:
build_android_apk(project="myapp", variant="debug", clean=false)
参数:
| 参数 | 说明 | 默认 |
|---|---|---|
project |
安卓工程文件夹路径(相对路径基于会话工作目录) | 必填 |
variant |
Gradle 变体(debug / release 等,大小写保留:stagingDebug → assembleStagingDebug) |
debug |
clean |
是否先 clean 全量重建 | false |
downloadDir |
工具链下载目录(相对路径基于会话工作目录) | <插件目录>/toolchain |
apkOutputDir |
APK 复制目录 | <workspace>/apk |
gradleVersion |
无可用 Wrapper 时下载的 Gradle 版本 | 8.9 |
compileSdk |
覆盖自动检测的 compileSdk | 自动检测,未知时 34 |
返回:ok、message、apks[](原始路径 + 复制路径 + 大小 + staged 标记)、logTail、durationMs 等。只返回本次构建产出的 APK(按修改时间过滤,不会把上次的旧包一起报上来)。
插件级配置(cordis.patch.yml 里的 config,可选):
config:
defaultVariant: debug
jdkMajor: 17
gradleVersion: "8.9"
| 组件 | 默认源 | 回退镜像 |
|---|---|---|
| JDK (Temurin) | api.adoptium.net | mirrors.tuna.tsinghua.edu.cn/Adoptium |
| Gradle | services.gradle.org | mirrors.cloud.tencent.com/gradle、mirrors.aliyun.com |
| Android cmdline-tools | dl.google.com | mirrors.cloud.tencent.com/AndroidSDK |
网络受限(如国内直连 Google 不稳)时,插件会自动尝试回退镜像;也可以先用代理保证
dl.google.com / services.gradle.org / api.adoptium.net 可达。
下载完整性校验:拿得到上游官方摘要时会逐个比对,不匹配的镜像直接跳过并换下一个——
| 组件 | 校验来源 | 算法 |
|---|---|---|
| JDK (Temurin) | Adoptium assets API 的 checksum(清华镜像为 <归档>.sha256.txt) |
SHA-256 |
| Gradle | <发行版>.sha256(与 zip 同目录) |
SHA-256 |
| Android cmdline-tools | Google repository2-*.xml 里的 <checksum> |
SHA-1 / SHA-256 |
摘要源拿不到(镜像未提供、dl.google.com 不可达)时跳过校验、照常下载,不会阻塞构建;
校验通过会在 logTail 里打印 [dl] … checksum verified …。
安装包(tarball)内包含:
package/
├── package.json # dsh.bundle.patch 声明(profile 层识别依据)
├── cordis.patch.yml # 插件行:android-apk-builder
├── lib/
│ ├── index.js # Cordis 插件入口 + build_android_apk 工具定义
│ ├── build.js # 构建编排(检测/下载/ASCII staging/assemble/复制 APK)
│ └── download.js # 下载/解压/镜像回退/校验助手(仅用 Node 内置模块)
└── README.md
仓库里(不随 tarball 发布):
package1/
├── CHANGELOG.md # 每个版本的改动记录(每次发版必更新)
├── RELEASES.txt # 发布台账:维护约定 + 各版本 tgz 的 SHA-256
├── dsh-plugin-android-apk-<version>.tgz # 历史版本全部保留,不删除
└── test/
├── <最小 Android 工程> # settings.gradle / app/ …,用于真实构建验证
├── run-build.mjs # 无头驱动:脱离 DSH 直接跑 buildApk
└── progress-throttle.test.mjs # 下载进度与监听器泄漏的回归测试
test/、CHANGELOG.md、RELEASES.txt与历史 tgz 都随package.json的files白名单排除,不会打进 tarball;它们只存在于源码仓库。
dsh plugin 需要 pnpm 在 PATH。userRender is not a function / 注册失败 / 输出被拒:DSH 要求每个工具声明
output.render(一个返回 [{ type: "text", text }] 数组的函数)。本插件自 0.2.1 起已内置;
若你看到该错误,说明装的是旧版本,升级到 ≥0.2.1。同理 apks[] 的 staged 字段也已在
output.schema 中声明(additionalProperties: false 下缺字段会导致 INVALID_TOOL_OUTPUT)。1 required plugin did not activate / tools failed to import):
这是 0.2.0 及以前把 @deepseek-ai/* 写成 dependencies 导致的——pnpm 会往 profile 装一份
旧的 dsh-tools,遮蔽宿主自己的 tools 行。升级到 ≥0.2.1(已改为 peerDependencies),
并删除 profile 里的残留副本
~/.dsh/profiles/<profile>/node_modules/@deepseek-ai/dsh-tools。gradle.properties
加 android.overridePathCheck=true),无需手动处理;staged SDK 会缓存复用,APK 仍复制回你的输出文件夹。dl.google.com 拉取,网络不稳时可能超时;
插件使用下载文件夹里已缓存的依赖,可先手动 gradle assembleDebug 预热,或配置代理。toolchain/ 里的缓存;依赖缓存也在该目录(gradle-user-home)。toolchain/ 在插件自己的目录里,而 pnpm 升级插件时会重建该目录,
所以升版本会丢掉缓存(约 460MB 需重下)。想让它跨版本存活,就在 cordis.patch.yml 里把
downloadRoot 指到一个固定路径(如 D:/android-toolchain),或用工具参数 downloadDir。sdkmanager --list 里挑与 compileSdk 同大版本的最新
build-tools;可传 compileSdk 覆盖检测结果。jdkMajor 只在需要下载时生效)。需要 JDK 8 的老工程请自行配好 JAVA_HOME(插件最低要求 Java 11)。java(which 定位)、
sdkmanager 用 sdkmanager 脚本(自动 chmod +x)、Gradle 用 gradlew/gradle,下载按
平台取 Adoptium 三元组(Linux/macOS 为 .tar.gz),解压回退链为 unzip → python3 -m zipfile → tar。
社区已在 Termux (aarch64, Android 15) 上端到端跑通:ok: true、产出真实 APK(含
classes.dex / AndroidManifest.xml 等)。注意该实测用的是预装的 JDK 21 + SDK 34.0.4,
Linux 上"从零下载 SDK"那条路径未被该次实测覆盖。CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。