
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
source-code-mgmt
Advanced tools
DSH plugin: source code management. Registers「代码管理」as a card in DSH's OWN right sidebar (the official guide page, beside 工作区文件 / 新建终端 / 浏览器) and as a tab page in that column — there is no separate button. Older hosts degrade gracefully: dsh-better-sidebar
English | 中文
版本:v1.25.0 | 更新日志见文末「版本历史」
界面语言跟随 DSH 设置实时切换:面板与 host 端消息自动使用 DSH 的语言(设置 → 通用 → 语言),中文 ↔ 英文即时生效,无需重启。
DSH Web GUI 源代码管理插件:一个「代码管理」面板把 ① 环境检查 → ② SSH 密钥与连接 → ③ 代码管理 → ④ 克隆仓库 → ⑤ 发布 npm 包 全流程串起来,GitHub / Gitee 双平台通用。环境检查自动检测 Git / GitHub CLI / SSH,缺工具可一键安装(按平台自适应、尽量免 sudo;gh 支持镜像下载源加速,Windows 亦可用),并带「检查更新」跟进新版本。
入口默认注册进 DSH 自带的右侧栏(不再保留单独的按钮):在侧边栏「开始」页上,「代码管理」是与 工作区文件 / 新建终端 / 浏览器 / 壁纸引擎 并列的一张卡片,点开即在该栏里以页面形式打开面板。老宿主自动降级:装了 dsh-better-sidebar 时注册成它的侧边栏 Tab;两者都没有时(旧版宿主)才回退到右上角的 header 入口(有会话时放在「Session 日志」旁;无会话/新对话时钉在右上角,与有会话时逐像素同高)。不会占用左边栏。
集成入口(按可用性自动选择,无需手动切换;任何时刻只有一个入口):
DSH 自带右侧栏(默认,harness ≥ 0.1.5):「代码管理」作为侧边栏「开始」页(guide)上的一张卡片 + 该栏里的一个页面。点卡片即在右侧栏打开面板(官方行为:openTab(kind, { replaceTab: true }),替换掉「开始」页本身,像浏览器起始页那样)。卡片顺序排在内置三张(工作区文件 10 / 新建终端 20 / 浏览器 30)与其它插件(壁纸引擎 100)之后 = 110。
sidebar.right.pane.tab 存在即代表宿主自带右侧栏,此时插件完全不注册 header 入口 —— 这正是「去掉单独按钮」的落点。title: () => t("代码管理")),由宿主每次渲染时求值 ⇒ 切语言无需重新注册。variant: "tab":填满该栏、不画自己的 ✕(关闭由宿主的 tab 栏负责)。已安装 dsh-better-sidebar(旧宿主降级):「代码管理」注册为它侧边栏的一个新 Tab 页面,点击侧边栏 Tab 直接打开面板;
两者都没有(旧宿主降级):入口只注册在会话 header 里(两处严格互斥,任何时刻只有一个「代码管理」可见)——
conversation.session.header.utilities 槽位放在「Session 日志」旁(同款胶囊、间距 8px 一致);shell.overlay 槽注册一个常驻按钮,钉在右上角、且与「有会话时」那枚按钮完全同高。
.header 的 padding-top: 10px + .titleRow 的 min-height: 30px(居中)⇒ 标题行占 y=10..40、中心 25px;按钮高 32px ⇒ top = 10 + (30 - 32)/2 = 9px。空态按钮取同一个 9px,所以两种形态切换时按钮不跳位。桌面壳里再叠加 --dsh-windows-titlebar-height(普通浏览器为 0),写法是 calc(var(--dsh-windows-titlebar-height, 0px) + 9px)。right: 48px,让开空态 header 右上角已有的「右侧栏展开按钮」:空态下 .headerBlank .headerCorner 带 margin-left: auto,把 ui-sidebar-right 的 ExpandButton(28×28 圆钮)顶到最右,其盒子位于视口右侧 12px~40px(.header 的 padding-right: 28px 减去 .headerCorner 的 margin-right: -16px 得到右缘 12px)。48 = 40 + 8(8px 与 header 内其它控件的间距一致),整个按钮落在圆钮左侧、互不相交。降级入口点击都打开同一个 dsh-better-sidebar 外观的右侧集成面板(内容放同一面板),并把主内容区往左推挤。
座位「晚到」有宽限窗口(600ms):
sidebar.right.pane.tab座位属于另一个包(ui-sidebar-right),加载器不保证顺序。若座位比本插件晚声明,这段时间里先不挂兜底按钮 —— 否则官方宿主上会先闪一个「代码管理」按钮、随后才被侧边栏卡片取代。窗口内座位出现 → 永远不挂;窗口耗尽仍没有 → 才按老宿主降级(老宿主本来就吃这条路径,晚 0.6s 出现按钮不可见)。全程零 I/O,只是一个计时器。
关闭方式:右侧栏形态由宿主的 tab 栏负责(点 ✕ / 切页);降级面板有三种 —— 面板右上角 ✕(标题行固定不滚动,内容滚到哪都在视口内)、按 Esc、或再次点「代码管理」按钮。
降级面板顶部会让开窗口控件那条带(
--dsh-frame-chrome-top):桌面壳里 ─ □ ✕ 是 OS 绘制的覆盖层,任何贴到右上角的网页按钮都会被它压住、点不到(v1.24.1 修的正是这个「打开后关不掉」),所以面板整体从该带下方开始;原生全屏时该变量归零,面板仍用满整屏。
刻意不注册进 DSH 自带左边栏(
sidebar.footer.action)——那个入口会挤在左边栏「设置」上方,与侧栏自身的项混在一起。也刻意不去抢 DSH 自己的单占位槽:
conversation.session.header.corner已被自带右侧栏开关(ui-sidebar-right 的 ExpandButton)占用,conversation.session.header.leading被自带侧栏开关(ui-sidebar 的 HeaderLeadingControls)占用;sidebar.panellist的每一项 id 必须是真实主面板 id(会交给layout.selectPanel),本插件没有主面板,注册进去只会得到点不动的死按钮。
检测只是激活时两次内存读取(座位声明 +
ctx.get('sidebarRightTabs')),零 I/O、零网络,不影响 DSH 启动速度。
面板分五步(①②③ 为核心三步,④⑤ 为新扩展板块,默认折叠按需展开):
Windows / macOS / Linux,对应底层 Node 平台标识 win32 / darwin / linux)git version 2.55.0、gh version 2.97.0)ssh 即显示「已找到」)~/.local/bin/gh(目录不存在会自动创建,全程不需要管理员权限);失败时自动回退系统包管理器。sudo -n;先探测 sudo 是否免密,需要密码时不再盲目执行,而是给出「请在终端手动执行」的指引 + 完整命令)。Git(macOS):brew,无 brew 时走 Xcode 命令行工具(xcode-select --install,系统自带安装,含 git/ssh)。~/.local/bin)时,弹窗提示并显示「重启 DSH」按钮——点击后 host 通过 detached 辅助进程按原启动命令自动拉起新进程(机制同 dsh-update),页面短暂断开后刷新即可;若 host 已能识别(如设置了 DSH_SCM_GH 或 ~/.local/bin 在 PATH),则不打扰直接完成。~/.ssh/*.pub 中已存在的 ed25519 公钥——优先用 id_ed25519;否则用找到的第一个(支持任意命名的密钥,如 github_ed25519);都没有则默认名 id_ed25519。状态行显示实际检测到的密钥文件名,SSH config 的 IdentityFile 也用它github.com → ssh.github.com:443;Gitee 写 gitee.com:443。用于绕过封锁 22 端口的网络。github.com → github.com:22(标准端点)。家宽 / 办公室 / VPS 通常直连即可。~/.ssh/config 里对应的 Host 块(不会留下重复块,也不动其它 Host);状态行显示的是实际配置的端口(host 端解析 ~/.ssh/config 得出),不再是写死的 (443)。ssh -T -p <port>),失败时提示「若是网络封锁 22 端口,请改用 443」。ssh -T -p <port> git@<host>(GitHub 走 ssh.github.com / github.com,Gitee 走 gitee.com)gh 是否已登录及账号443 与「是否有代理」无关(常见误解)。 普通 HTTP 代理只对 git-over-HTTPS / curl 生效;OpenSSH 完全无视
HTTP_PROXY/HTTPS_PROXY(要走代理得配ProxyCommand)。所以「我开了代理」并不等于「22 端口通了」——该不该用 443 取决于你的网络是否封锁 22 端口,因此这里交给用户自己选,而不是由插件猜测。海外/正常网络需要 443 吗?——不需要。 「写 SSH config」本身是可选的(只有你点按钮才写
~/.ssh/config)。GitHub 标准端点就是git@github.com走 22 端口,正常网络开箱即用:生成密钥 → 把公钥贴到 GitHub → 测试连接 → 推送,全程 22 端口。443(Host github.com → HostName ssh.github.com, Port 443)是 GitHub 官方支持的兜底方案,典型场景是国内网络、部分公司/校园网;只有当 443 也被封锁时才会反而不通(极少数网络)。Gitee 是国内平台,海外用户基本只会用到 GitHub。
gh CLI,Gitee 走 Gitee OpenAPI)projects 权限)→ 保存在本机 ~/.dsh/storages/source-code-mgmt-gitee.json(0600,不写入插件目录、不回传到浏览器/日志);可一键清除;令牌无效会自动清掉~/.dsh/storages/source-code-mgmt-dirs.json,不写入插件目录,开源不泄漏个人路径),下次打开无需重新选择;手动添加的目录会以自定义目录徽标显示,末端带 ✕ 可一键删除该下拉记录(只删记录,不删实际文件夹)git status 的 XY 状态区分 staged/unstaged),并显示「已暂存 / 未暂存」标记checkoutgh 账号如 Zhucy123 名下,Gitee 平台 = Gitee 令牌账号如 Zhucy2100 名下)。这样:
deepseek-ai/deepseek-harness)不会被当作「用户自己的远程」显示,ahead/behind 也不对它计算;git init(+设默认身份),不拉取不推送,由用户自行决定下一步是拉取还是推送git add + 生成一个初始提交再创建,避免推送时报 "no commits found"
gh repo create --private|--public --source=. --pushPOST /user/repos 建仓,再设置 SSH 远程 git@gitee.com:<owner>/<name>.git 并 git push(走 ② 已配的 SSH 密钥).gitignore 的不重复添加,并显示「未上传原因」gh,Gitee 走 OpenAPI)名下所有远程仓库列出来,并自动标记本地是否已有同名 git 仓库(默认按「默认工作区 + 已登记工作区 + 自定义目录」判定)git@github.com:… / git@gitee.com:…),成功后自动加入自定义目录列表,③ 里可直接选中npm config get registry → ② 查看认证配置 npm config list(自动脱敏 token/auth/password)→ ③ 验证身份 npm whoami → ④ 预览打包 npm pack --dry-run(先看会发布哪些文件,不真正打包发布)→ ⑤ 发布 npm publishhttps://registry.npmjs.org:即使你的全局 npm 配置是镜像源(如 registry.npmmirror.com——镜像只同步、不接受发布),whoami / npm login / npm publish 也统一带 --registry=https://registry.npmjs.org;状态区在检测到镜像配置时会黄色提示「发布将使用官方源」。konsole / gnome-terminal / xterm 系等),再 spawn(并挂 error 监听兜底)——修复了旧版 spawn 不存在二进制时异步 ENOENT 未捕获、直接把 dsh host 进程打崩的问题;KDE 下用 konsole --separate 开独立窗口,不与宿主所在终端实例纠缠。找不到终端时明确返回手动命令指引,不再假装成功。DSH 打开时不联网同步仓库,只预取静态的环境/SSH/工作区列表。打开插件、切换工作区、刷新状态、以及推送/拉取/暂存/提交等操作后,都会联网获取对应工作区的最新仓库状态,并显示「⟳ 刷新中…」提示——避免打开/重开/切换时显示可能过期的旧数据(如旧的「无改动」)。关闭面板再打开也会重新同步,不会停留在旧状态。
本插件以 Profile Bundle 形态分发:
package.json声明了dsh.bundle(携带cordis.patch.yml配置层),所以dsh plugin --profile web add一条命令装完即自动激活——无需手动编辑任何配置文件。
最省事的方式——只需一条命令,且不用先进 profile 目录,推荐给普通使用者。
在任意目录执行:
# 用 DSH 自带的插件命令(自动定位/初始化 web profile)
dsh plugin --profile web add source-code-mgmt
该命令在 web profile 目录里执行
pnpm add,成功后对账插件层:检测到本插件声明dsh.bundle,会自动把它追加进dsh.profile.bundles(见~/.dsh/profiles/web/package.json)并注册进 Cordis loader 树,一步装完即用。
装完完全重启 dsh web(不是刷新页面,而是要停掉旧进程后重新启动),然后浏览器 F5 刷新,「代码管理」入口即出现(已装 dsh-better-sidebar 时为侧边栏 Tab,未装时为右上角 Session log 旁的「代码管理」按钮 + 右侧集成面板)。
Windows (PowerShell):
# 用 DSH 插件命令安装本地源码(link: 协议,符号链接,改源码即生效)
dsh plugin --profile web add link:C:/path/to/source-code-mgmt
dsh web
Linux / macOS:
dsh plugin --profile web add link:/home/yourname/path/to/source-code-mgmt
dsh web
Windows / Linux / macOS 通用:
dsh plugin --profile web add git+https://github.com/Zhucy123/source-code-mgmt.git
dsh web
git 安装会把源码实际拷贝到 node_modules,改动源码需重新
dsh plugin --profile web add ...拉取(不像link:是符号链接、改源码即生效)。
安装并重启后,可以核对以下几点:
~/.dsh/profiles/web/package.json 的 dependencies 里应有 source-code-mgmt。~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 列表里应有 source-code-mgmt(dsh plugin add 自动写入,无需手动编辑)。link: 方式):~/.dsh/profiles/web/node_modules/source-code-mgmt 指向源码目录(Windows 显示为 Junction)。| 现象 | 原因 / 处理 |
|---|---|
已 dsh plugin add 并重启,但按钮不出现 | 最常见:装完没有完全重启(不是刷新)。停掉旧 dsh web 进程再启动(旧进程还占着 3080 端口时,新实例起不来)。 |
| 安装时提示「declares no dsh.bundle」 | 装到的版本缺少 bundle 声明(旧版或打包遗漏 cordis.patch.yml)。确认版本 ≥ 1.9.0 后重新安装/更新。 |
| 出现「Failed to load plugins」 | 插件 host 端 index.js 启动报错(多为依赖解析问题)。查看启动日志,确认 node_modules 依赖已装齐。 |
| 路由 | 方法 | 说明 |
|---|---|---|
/api/source-code-mgmt/env | GET | 环境检查(git/gh 版本) |
/api/source-code-mgmt/install-tool | POST | 一键安装缺失工具(body tool: git/gh/ssh;gh 可另传 source: official 或 gh-proxy.com/ghfast.top/ghproxy.net 等镜像 id,按平台自适应:gh 在 Linux/macOS 免 sudo 装到 ~/.local/bin;git/ssh 走系统包管理器 / brew / Xcode CLT / winget;响应为 NDJSON 事件流:step/out/result,供进度弹窗实时展示) |
/api/source-code-mgmt/install-command | POST | 按工具与下载源返回可复制的安装命令(body tool + 可选 source;与「安装」同源逻辑) |
/api/source-code-mgmt/check-update | POST | 检查 gh / git 是否有可用更新(body tool;返回 {current, latest, hasUpdate}) |
/api/source-code-mgmt/ssh | GET | SSH 密钥 / config / gh 登录状态 |
/api/source-code-mgmt/gh/login | POST | 打开终端运行 gh auth login(交互式登录需用户在弹出终端完成;找不到终端时返回手动命令指引) |
/api/source-code-mgmt/gen-key | POST | 生成 ed25519 密钥 |
/api/source-code-mgmt/write-config | POST | 写入 SSH config(body provider: github 默认 / gitee) |
/api/source-code-mgmt/ssh-test | POST | 测试 SSH 连接(body provider: github 默认 / gitee) |
/api/source-code-mgmt/default-dir | GET | 当前工作区目录 |
/api/source-code-mgmt/workspaces | GET | 列出所有工作区目录 + 自定义目录集合(DSH 工作区 + 插件自定义目录) |
/api/source-code-mgmt/pick-dir | POST | 宿主端弹出原生文件夹选择对话框,返回选中的路径 |
/api/source-code-mgmt/add-workspace | POST | 校验目录存在并持久化加入插件自定义目录列表,返回合并后的工作区列表 |
/api/source-code-mgmt/remove-workspace | POST | 仅删除自定义目录的下拉记录(不删实际文件夹),返回更新后的列表 |
/api/source-code-mgmt/align | POST | 强制对齐:git fetch + git reset --hard origin/<branch>,本地完全重置为远程状态 |
/api/source-code-mgmt/init-git | POST | 仅 git init + 设置默认身份,不拉取不推送(由用户决定下一步) |
/api/source-code-mgmt/repo-exists | POST | 检测同名仓库是否存在(body provider: github/gitee) |
/api/source-code-mgmt/repo?dir= | GET | 获取仓库状态(query provider: github/gitee) |
/api/source-code-mgmt/repo-diff?dir=&path= | GET | 按需返回单个改动文件的 unified diff 文本 |
/api/source-code-mgmt/stage | POST | 暂存改动(body dir、path;path 空=全部) |
/api/source-code-mgmt/unstage | POST | 取消暂存(body dir、path;path 空=全部) |
/api/source-code-mgmt/commit | POST | 用自定义信息提交(body dir、message、paths?;沿用 pushFlow 的无身份兜底逻辑) |
/api/source-code-mgmt/branches | POST | 列出分支(当前分支在前) |
/api/source-code-mgmt/checkout | POST | 切换分支(body dir、branch) |
/api/source-code-mgmt/log | POST | 最近提交历史(body dir、count?,返回 hash/subject/author/date) |
/api/source-code-mgmt/revert | POST | revert 某提交(body dir、hash) |
/api/source-code-mgmt/cherrypick | POST | cherry-pick 某提交(body dir、hash) |
/api/source-code-mgmt/commit-diff | POST | 返回某提交的完整 patch(body dir、hash) |
/api/source-code-mgmt/push | POST | 提交并推送(git 操作,平台无关) |
/api/source-code-mgmt/pull | POST | 从远程拉取更新(git pull --ff-only,已最新/成功/冲突反馈) |
/api/source-code-mgmt/merge-push | POST | 拉取并推送(git pull --rebase + git push,本地有更改且远程有更新时合并推送) |
/api/source-code-mgmt/force-push | POST | 强制推送(git push --force,覆盖远程为本地状态) |
/api/source-code-mgmt/force-pull | POST | 强制拉取(git pull --force,拉入远程更新) |
/api/source-code-mgmt/create | POST | 新建仓库并推送(body provider;GitHub 走 gh repo create,Gitee 走 OpenAPI + SSH push) |
/api/source-code-mgmt/set-visibility | POST | 修改仓库可见性(body provider;GitHub 走 gh repo edit,Gitee 走 PATCH /repos/{owner}/{repo}) |
/api/source-code-mgmt/gitee-token | GET/POST | GET:令牌是否已配置 + 账号;POST:保存({token})或清除({clear:true})Gitee 令牌 |
所有路由均为 loopback-only(sec-fetch-site + Origin 校验),仅本机浏览器可访问,LAN/手机来源一律 403——与控制面板同一策略。
process.platform 检测平台~/.ssh 通过 homedir() 定位(Windows: C:\Users\用户名\ .ssh,Linux/macOS: /home/用户名/.ssh 或 /Users/用户名/.ssh).exe)→(仅 Windows)③Git 自带目录(usr\bin / bin)回退,最后兜底用裸命令名。因此只要装了 Git,即使 ssh 不在 PATH 里也能正常工作,换设备无需额外配置。DSH_SCM_GIT / DSH_SCM_GH / DSH_SCM_SSH / DSH_SCM_SSH_KEYGENssh.exe(usr\bin\ssh.exe)在被 detached/agent 进程调用时可能报 couldn't create signal pipe, Win32 error 5,导致 git push/git pull 失败。插件执行 git 远程命令时会自动注入 GIT_SSH 指向解析到的可用 ssh(通常为系统 OpenSSH C:\Windows\System32\OpenSSH\ssh.exe),避免该问题。core.quotepath 会把含非 ASCII 字节的路径输出成八进制转义的引号串(显示为乱码)。插件对所有 git 调用统一注入 -c core.quotepath=false(直接输出原始 UTF-8 路径),并对仍带引号转义的路径做 parseGitPath() 反转义兜底——改动列表 / diff 里的中文文件名显示正常~/.local/bin/gh,失败回退系统包管理器);git/ssh 在 Linux 走 apt-get / dnf / pacman(先探测 sudo 免密,需要密码时给出手动命令指引而非静默失败),macOS 走 brew / Xcode 命令行工具,Windows 走 winget / 内置功能(回退 choco/scoop)。安装过程有实时进度弹窗,需要重启时提供一键重启 dsh。SSH 密钥探测、本地 Git 工作流、并排 diff 等在三个平台行为一致;「浏览目录」的原生选择器仅 Windows 可用,macOS/Linux 上请在输入框直接填路径(可手动输入粘贴)。git clone https://github.com/Zhucy123/source-code-mgmt.git
cd source-code-mgmt
# 在本地 DSH 测试(安装到 web profile,自动激活)
dsh plugin --profile web add link:$(pwd)
lib/client.js(浏览器端)→ 刷新页面即生效lib/index.js(host/Node 端)→ 刷新页面即可让面板重新检测(toolInstalled 按当前 PATH / ~/.local/bin 实测);如 host 端 API 仍为旧版,重启 dsh web 进程一次变更:入口默认注册进 DSH 自带的右侧栏,「代码管理」不再是一个单独的按钮:
sidebar.right.pane.tab 座位 + sidebarRightTabs.register({ guide })),并不是 sidebar.footer.action(左边栏底部)——左边栏早在 v1.1.2 起就明确不碰。slots.inject('sidebar.right.pane.tab', …) —— 座位声明了就是这个宿主自带右侧栏,回调跑没跑即天然的能力门(不嗅探版本号,少一个漂移源)。回调里:
ctx.get('sidebarRightTabs').register({ id: 'source-code-mgmt/sidebar', kind: 'source-code-mgmt', title: () => t('代码管理'), guide: [{ id: 'panel', order: 110, title, description, icon: RepoIcon }] }) —— 这张 guide 条目就是「开始」页上的卡片;slots.register({ name: 'sidebar.right.pane.tab', key: 'source-code-mgmt/sidebar' }, () => h(ScmPanel, { variant: 'tab' })) —— 卡片点开后在该栏里渲染的页面。key 必须等于 tab 类型的 id:宿主 TabSlot 用 definition.id 作 entryKey 派发 tab 本体,写错就会「卡片点开了但面板空白」。order: 110:内置三张是 10 / 20 / 30,壁纸引擎(dsh-plugin-wallpaper-engine)是 100,所以本卡片排在其后、落在列表末尾。title / description 是函数,宿主每次渲染都求值 ⇒ 切语言不需要重新注册(旧的 better-sidebar 路径因为收的是字符串,才需要 subscribe 后重注册)。sidebar.right.pane.tab 属于另一个包,可能比本插件晚声明。新增 SCM_FALLBACK_GRACE_MS = 600:这段窗口内先不挂兜底按钮(否则官方宿主上会先闪一个按钮再被卡片取代),窗口内座位出现 → 永远不挂,窗口耗尽才降级。全程零 I/O。sidebarRightTabs 服务已就绪,故 ctx.get 短轮询(250ms × 40 ≈ 10s)后才放弃并回落 ③;tab 本体注册包在 ctx.effect 里,让 DSH 在插件卸载时自动注销(避免 fiber 已 dispose 时抛 INACTIVE_EFFECT)。verify-entry-runtime.test.mjs(32 项,三段分支 + 卡片形状 + 图标尺寸 + 流体布局 + 卸载清理)、verify-sidebar-entry.test.mjs(16 项,「忠实宿主模型」:座位按宣告纪元重跑注入、keyed 座位拒绝重复 key、ctx.effect 按注入作用域回收、卸载按 LIFO / FIFO 两种顺序都验、注册抛异常路径,以及「兜底只能从路由里挂」的结构断言)、verify-sidebar-adversarial.test.mjs(16 项对抗式:能不能让官方宿主冒出独立按钮 / 能不能把用户搞成「零入口」)、verify-sidebar-postfix.test.mjs(3 项,独立复核者所写)。这些测试文件已在本版发布前删除(见下方「变更范围」),此处保留它们的结论作为改动依据的记录。app.asar 逐条核对宿主契约(座位名/guide 排序/entryKey = definition.id/图标 size 22|26/openTab(kind,{replaceTab:true})/无 patterns 也能按 kind 打开/keepMounted 确实有语义),并用对抗用例查出 4 个真实缺陷,随后全部修掉:
betterEntryActive(入口归属)与 hasBetterSidebar(是否安装,面板据此隐藏自带 Git 工作流)两个概念,官方成功路径同时 teardownBetterTab()。stoppedByHost=true,恢复逻辑被跳过 ⇒ 一个入口都不剩。修法:座位回收不再设该标记,改为走统一的 routeToFallback() 恢复;stoppedByHost 只留给「插件卸载」与「② 已接管」。[500,500,750,1000,1500,2000,3000,5000×7] 因此挤在头 5s 内烧完(注释却写「约 44s」)。后果:官方座位在、服务 10s 后才发布的宿主永远停在兜底按钮上。修法:改为累加调度(retryAt += interval,末级 ≈44s),并让重试在服务真正就绪时重新尝试官方注册。officialGaveUp 曾被当作永久放弃。修法:它只表示「这一轮 40×250ms 探测结束」,重试阶梯看到服务已就绪会把官方卡片换上、拆掉兜底。
另修一处口径错误:installHeaderFallback 原本用 hasBetterSidebar 当门,而该标记在官方入口赢下后仍为 true ⇒ 兜底永远挂不上(与缺陷 2 同类的不可达)。tabs.register 会抛)的 catch 分支直接调 installHeaderFallback(),绕过了「② 已接管入口」的判断 —— 若此刻 ② 的 Tab 正挂着,屏幕上就是「Tab + header 按钮」两个入口。修法:该分支改走 routeToFallback(true)。(第一轮我曾以为 installHeaderFallback 里的 betterEntryActive 门是冗余防御,这个判断是错的:它在「抛异常」这条路径上是唯一的拦截点。)
6. 死状态:officialGaveUp 在改成「非永久」之后所有读取都被移除,只剩写入 ⇒ 是误导下一个读者的死变量(相关注释还在描述已不存在的门)。修法:整个删除,重入只由 officialCleanup/officialInstalled 两个条件表达。betterEntryActive 门)下用例确实失败 —— 说明该不变量被测到。ctx.effect 归属与 carriers() 短路),并复跑了受影响结论。installHeaderFallback 的 betterEntryActive 门与「catch 走路由」这两处,单独改任一处都无法从行为上观察到(路由先拦住)。除了保留双变异用例,另加一条结构断言:全文件里 installHeaderFallback( 只能有 1 处调用、且必须在 routeToFallback 内部 —— 这样将来有人在别处直调(正是第二轮那个重复入口 bug 的形态)会立刻失败。实测:单独把 catch 改回直调,这条断言即失败。*.test.mjs + smoke-test.mjs + verify-en-dict.mjs)。保留 tools/ 下的 i18n 工具链(i18n-apply.mjs、i18n-extract.mjs、client-en.json、host-en.json)—— 它们是生成 lib/client.js 内联英文词典的脚本与数据源,删掉后词典就只能手改。上面的测试结论与变异测试结果作为本轮改动的历史依据保留。lib/client.js(桌面版无需操作,自动热重载;host 端未改)、tools/client-en.json、README.md、README.en.md、package.json,以及删除 tools/ 下 15 个测试文件。版本号 1.24.4 → 1.25.0(新功能用次版本号)。修复:面板打开后,窗口顶栏仍然有一条白缝(v1.24.3 的补带只补了面板那一段):
panelW(跟面板同宽),但白缝的位置并不都在面板宽度内 —— 面板宽度随拖拽变化,补带宽度跟着变就总会留下没补到的段。left: 0; right: 0 横贯整个窗口宽度。左半部分本来就是同色的原生顶栏,重复铺同色无副作用;右半部分把缺的那段补齐。不再依赖面板宽度 —— 拖拽改宽、开合面板都不会在顶栏留下任何缝。顺带删掉了为同步宽度而加的 chromeRef/getChromeEl(不再需要)。left: 0; right: 0 横贯整宽(变异测试验证过:改回 panelW 宽会失败)。lib/client.js(桌面版无需操作,自动热重载)、tools/verify-close-button.test.mjs、README.md、README.en.md、package.json。版本号 1.24.3 → 1.24.4。修复:面板打开后,窗口顶栏那一段出现明显色差(白条):
#f9fafb —— 中间在面板左缘形成一条硬缝。.BynINW_frame(及其 :before)用 --dsw-specific-sidebar-fill(= --dsw-static-neutral-bluish-50,#f9fafb)画出来的,而 frame 在 #root 里。本插件为了让位,用 .LAYOUT_CSS 把 #root 收窄了 panelW:#root { margin-right: var(--scm-push); width: calc(100% - var(--scm-push)) }。顶栏跟着 #root 一起被收窄,于是在面板左缘戛然而止;面板上方那一段没人画,露出页面底色(--dsw-alias-bg-layer-1 = #ffffff)。不是面板盖住了顶栏,而是顶栏被推挤截断了。var(--dsw-specific-sidebar-fill, transparent) 填充,顶栏于是横贯整个窗口宽度、看不出接缝。
pointerEvents: "none" —— 这条带只负责上色,鼠标事件照旧穿透给底下的原生拖拽区域,不改变顶栏原有的拖动行为;SCM_CHROME_TOP(普通浏览器下为 0px → 高度 0、整块不可见,与改造前一致);原生全屏下同样归零,不会多出一条带;makeResizeHandler 相应多接一个 getChromeEl,逐帧同时写面板与色带的 width。tools/verify-close-button.test.mjs,9 项,本次新增 1 项):断言 portal 里必须存在这条带、用的是 --dsw-specific-sidebar-fill、高度等于 SCM_CHROME_TOP、pointerEvents: "none"、带 ref 且 makeResizeHandler 拿到了它(并用变异测试验证:去掉色带的背景后该断言确实失败)。lib/client.js(刷新页面即生效)、tools/verify-close-button.test.mjs、README.md、README.en.md、package.json。版本号 1.24.2 → 1.24.3。修复:面板顶部那条灰带盖住了说明文字(v1.24.1 引入的回归):
✕ 常驻,把标题行做成了 position: sticky 并给了它一层底色(--dsw-alias-bg-layer-1)。sticky 行是浮在内容之上的——它不会把后续内容推开,只是悬停在那里,于是底色正好压住从下面滚上来的文字;同时 padding 又让标题行多占高度,把说明文字整体下推。用「浮层 + 底色」去解决「常驻可见」,方向本身就错了。drawer 下面板根节点改为 overflow: hidden(面板本身不滚动),标题行是普通静态行(flex: 0 0 auto,不带任何底色),只有它下面的 body 容器 overflow: auto 负责滚动。标题行在正常流里、不重叠内容,因此 ✕ 照样常驻可见,却不可能盖住任何文字——与原生侧边栏同一做法。tab 变体保持整块滚动(滚动语义交给宿主 dsh-better-sidebar),不受影响。tools/verify-close-button.test.mjs,8 项)相应重写:断言 ScmPanel 内不得再出现 position: "sticky"(断言范围限定在 ScmPanel——插件自己的 diff 查看器用 sticky 是正确的,不能误伤)、headStyle 不得带 background、drawer 根节点必须是 overflow: hidden、滚动 body 必须排在标题行之后、✕ 仍在且 tab 变体不渲染。lib/client.js(刷新页面即生效)、tools/verify-close-button.test.mjs、README.md、README.en.md、package.json。版本号 1.24.1 → 1.24.2。修复:换成 DSH 官方桌面版后,右侧面板打开却关不掉:
✕ 一直都在(variant === "drawer" 时渲染),但桌面壳会把 --dsh-windows-titlebar-height: 40px 写到 documentElement 上,而窗口控件(─ □ ✕)是 OS 绘制的覆盖层——它永远画在网页之上,z-index 加到多大都没用。面板原本是 position: fixed; inset: 0,它自己的 ✕ 正好落在 y≈20..42、x≈右边 20..50 那一带,被原生 ✕ 完整压住:按钮在 DOM 里、也响应 hover,就是收不到点击。网页版没有这条 OS 带,所以旧版一直没暴露。inset: 0 改为 top: var(--dsh-frame-chrome-top, var(--dsh-windows-titlebar-height, 0px)) + right/bottom/left: 0。优先用 DSH 自己发布的 --dsh-frame-chrome-top——它正是 DSH 用来「让模态遮罩不要画到 Windows 顶栏上」的量,原生全屏时自动归零,面板在全屏下仍能用满整屏;只发布旧变量的壳回退到 --dsh-windows-titlebar-height;普通浏览器两者皆无 → 0px,与改造前逐像素一致。✕ 常驻可见:面板内容很长(①②③④⑤),普通滚动会把标题行连同 ✕ 一起带出视口,得一路滚回顶部才能关。注:本版最初用 position: sticky + 底色实现,方向是错的(sticky 是浮层,底色会盖住下面滚上来的文字)——已在 v1.24.2 改为「标题行固定 + 只有 body 滚动」的结构性分离。tab 变体保持原样——它由宿主 dsh-better-sidebar 负责关闭,本就不渲染 ✕。keydown 监听,按 Esc 关闭(关闭/卸载时摘除)。桌面壳里任何贴边的网页控件都可能被 OS 覆盖层压住,键盘是永远可用的退路。setOpen(true) 改为 toggleOpen(再点一次收起,并加 aria-expanded)。它挂在 header 里、不在被窗口控件压住的那条带上,是最不容易失灵的关闭入口。tools/verify-close-button.test.mjs(7 项,v1.24.2 重写为 8 项):断言 SCM_CHROME_TOP 的变量链与回退顺序、遮罩必须用 top: SCM_CHROME_TOP、drawer 里不得再出现 inset: 0(断言范围限定在 HeaderScmAction 内——插件自己的居中弹窗用整屏 mask 是正确的,不能误伤)、标题行 sticky 且 ✕ 在滚动容器内、tab 变体不渲染 ✕、拖拽条 zIndex 必须高于当时的 sticky 标题行(标题行吸顶后会盖住拖拽条内侧 4px,造成顶部一段拖不动的盲区;已把拖拽条从 zIndex: 1 抬到 3,并用变异测试验证过该断言会失败)、Esc 监听的挂载与清理、入口按钮必须是 toggle 而非单向 open。lib/client.js(刷新页面即生效,无需重启)、tools/verify-close-button.test.mjs(新增)、README.md、README.en.md、package.json。版本号 1.24.0 → 1.24.1。③ 加载提速:把「每个文件一次 git」改成批量调用(实测每次加载省 ~1.2s):
git@github.com:...),而 OpenSSH 不使用 HTTP_PROXY/HTTPS_PROXY/git http.proxy——已实测(ssh -G 在设了这些变量后不产生任何代理指令)。代理只对 git-over-HTTPS 生效。所以对 SSH 远端,开系统代理不会改变速度;这是协议决定的,不是配置问题。要让 SSH 走代理必须显式配 ProxyCommand(需要 ncat/connect 之类的辅助程序,本机没有)。| 阶段 | 耗时 |
|---|---|
| 本地 git 调用(branch/status/ls-files/remote/rev-list) | ~330 ms |
其中「每个改动文件一次 git diff --numstat」 | ~1250 ms(30 个改动 → 30 次进程) |
git fetch(联网,仅 full 模式) | ~4000 ms |
| SSH 握手本身 | ~3200 ms |
根因:Windows 上每次 git 进程约 35ms,旧代码对「工作区副本不存在」的改动文件逐个跑 git diff --numstat -- <path>,是典型 N+1。 |
viewableMap():二进制判定批量做——先直接读文件头 8KB 嗅探(零进程),只有确实读不到的文件才汇总成至多一次 git diff --numstat(按暂存/未暂存分两批)。providerRemoteInfo():用一次 git remote -v 同时拿到远程名与 URL,替换原先「git remote + 每个远程一次 get-url」以及 repoStatus 里又一次 get-url。tools/verify-viewable-batch.test.mjs(9 项),在真实临时仓库里同时跑「旧实现」与「新实现」,断言两者对每个文件给出完全相同的结论。这个对照测试当场抓出一个我引入的回归:已删除文件上 git diff --numstat 不输出任何行,旧逻辑判为「不可预览」(正确——没有工作区副本可看),我的批量版一开始误判为「可预览」。已修回与旧实现一致。fetch 那 4s 是网络与 SSH 握手成本,代理帮不上,只能靠「少 fetch」——插件已默认在快速模式不联网 fetch(用本地 tracking ref 算 ahead/behind),只有点「刷新状态」才联网。scan-keys / scan-host-keys / 两个计时脚本)和 1 个在本机跑不起来的旧测试(verify-cn-paths.test.mjs 硬编码了 C:\Users\Administrator\workspace)。带断言的 verify-*.test.mjs 与 i18n 工作流全部保留。lib/index.js(需重启 dsh web 生效)、README.md、README.en.md、package.json。版本号 1.23.0 → 1.24.0。SSH 端口改为用户可选(443 穿墙 / 22 标准):
HTTP_PROXY / HTTPS_PROXY——用 ssh -G 验证过,设了这些环境变量后 OpenSSH 不会产生任何代理指令(要走代理必须配 ProxyCommand)。所以「有代理」≠「22 端口通」;443 的真正意义是绕过封锁 22 端口的网络。因此做成用户按网络情况自己选,而不是由插件按「有没有代理」去猜。lib/index.js):
providerCfg(provider, port) 接受端口,并据此决定 Hostname——443 用 GitHub 的 ssh.github.com,22 用标准 github.com(Gitee 两种都用自身域名)。normalizePort():只接受 22 / 443,其余一律回退 443(避免脏输入写坏配置)。writeSshConfig(provider, port):端口变了会就地改写 ~/.ssh/config 里对应的 Host 块(用新增的 hostBlockSpan() 定位行区间),不产生重复块、不影响其它 Host;端口未变则仍是幂等 no-op。sshTest(provider, port):显式带 -p <port>(443 时直接指向 git@ssh.github.com),不再依赖配置文件先写好。/write-config、/ssh-test 透传端口;checkSsh() 新增 sshGitHubPort / sshGiteePort(从配置解析实际端口)。lib/client.js):② 的「SSH 配置」行加端口下拉(443(穿墙) / 22(标准));状态文案显示实际配置的端口(不再写死 (443));首次读到已有配置时选择器跟随一次,之后以用户选择为准(不覆盖用户手动切换);「测试连接」按所选端口测,失败提示「若是网络封锁 22 端口,请改用 443」。tools/verify-ssh-port.test.mjs(16 项:端口归一化、Host 块解析/端口读取、相邻块不误吞、github.com.evil 之类相似域名不误匹配、路由透传);新增 tools/verify-ssh-port-e2e.test.mjs(7 项:在临时目录里真实跑「写 443 → 幂等 → 切 22 就地改写 → 切回 443 → 保留其它 Host → 追加 Gitee 不干扰」,不触碰真实 ~/.ssh/config);新增 tools/verify-ssh-crlf.test.mjs(4 项)与 tools/verify-ssh-crlf-write.test.mjs(5 项);新增 tools/verify-en-dict.mjs(校验新 UI 文案的英文翻译已进内联词典,280 → 287)。~/.ssh/config 常见是 CRLF 换行。原先的「就地改写」把文件按 \n 切开再按 \n 拼回,会让被改写的那个 Host 块退化成 LF,整个文件变成 CRLF/LF 混合(在 dotfiles 仓库里表现为整段 diff)。现已改为按文件自身的换行符切分与拼接(const eol = current.includes('\r\n') ? '\r\n' : '\n'),新写入的块、追加的块、以及拼接缝都保持原样。这组 CRLF 用例是拿本机真实配置(确认就是 CRLF)验证出来的。lib/index.js(需重启 dsh web 生效)、lib/client.js(刷新页面生效)、README.md、package.json。版本号 1.22.4 → 1.23.0(新功能,次版本号 +1)。空态按钮下移到与「有会话时」完全相同的高度:
top 对齐到了侧栏开关那一行(calc((var(--dsh-windows-titlebar-height, 40px) - 28px) / 2) = 6px),但「有会话时」的按钮挂在 header 的 utilities 行里,其高度由另一套几何决定:.header 的 padding-top: 10px + .titleRow 的 min-height: 30px(居中)⇒ 标题行 y=10..40、中心 25px;按钮高 32px ⇒ top = 10 + (30 - 32)/2 = 9px。两者差 3px。top = SCM_HEADER_PAD_TOP + (SCM_TITLE_ROW_H - SCM_BUTTON_H) / 2 = 9px(常量 10 / 30 / 32 分别对应 .header 的 padding-top、.titleRow 的 min-height、headerBtnStyle 的 height),桌面壳再叠加 --dsh-windows-titlebar-height(普通浏览器回退 0px,即精确 9px)。两种形态切换时按钮逐像素同位、不跳位。headerBtnStyle 的 32px」、「叠加标题栏偏移」三项,并断言旧的 6px 公式不得残留(共 13 项);用变异测试验证过改回 6px 会被抓出。另抽出 stripComments() 助手,让「禁止残留」类断言只匹配代码、不被解释性注释误伤。lib/client.js(刷新页面即生效)、README.md、package.json(版本号)。版本号 1.22.3 → 1.22.4。本轮(v1.22.0 → v1.22.4)新增的回归测试文件(随包发布,不在
files白名单内,仅供本地校验):
tools/verify-entry-placement.test.mjs—— 入口几何位置与槽位选择回归(13 项)。tools/verify-entry-runtime.test.mjs—— 在模拟 cordis 运行时里真实执行apply(ctx)、并用模拟 session 快照真实渲染每个注册项,验证两个分支与入口互斥(13 项)。运行:
node tools/verify-entry-placement.test.mjs/node tools/verify-entry-runtime.test.mjs⚠️ 以上测试文件已在 v1.25.0 删除(用户要求清理插件目录),下面各历史版本里提到的测试文件同样已不存在;当时的结论保留作改动依据。
空态入口让开右上角的「右侧栏展开按钮」(修掉横向重叠):
right: 12px 后,它与 DSH 空态 header 右上角已有的按钮叠在一起。.headerBlank .headerCorner 带 margin-left: auto,把它顶到最右;按 DSH 的 CSS 算它的盒子:.header 是 padding-right: 28px、.headerCorner 是 margin-right: -16px、圆钮宽 28px ⇒ 它占据视口右侧 12px~40px。right: 12 正好把「代码管理」的右缘压在同一条线上。right: 48px(常量 SCM_TOPRIGHT_OFFSET = 48)= 圆钮左缘 40px + 8px 间距(与 header 内其它控件的 8px 间距一致),整个按钮落在圆钮左侧、互不相交。纵向当时仍复用 DSH 自己的公式(该纵向写法已在 v1.22.4 被替换为「与有会话时同高」,见上)。SCM_TOPRIGHT_OFFSET >= 40,并断言 right: 12 不得再出现)、偏移量恰为 40 + 8;并用变异测试验证过把偏移改回 12 会被抓出。(当时共 14 项;v1.22.4 重排纵向断言后为 13 项。)lib/client.js(刷新页面即生效)、README.md。版本号 1.22.2 → 1.22.3。空态入口改回右上角、与 DSH 自带控件同一行:
top: 84px; right: 28px),用户要求改回右上角——即与 DSH 自带侧栏开关同一条水平线上。position: fixed; top: calc((var(--dsh-windows-titlebar-height) - 28px) / 2); left: 12px(ui-sidebar 的 SidebarRoot.module.css,「新会话」按钮同一条公式、left: 48px)。本插件复用同一条公式(带 40px 回退值,取自 apps/desktop 的 WINDOWS_TITLEBAR_HEIGHT),所以标题栏高度变化时按钮自动跟着走、不会错位;普通浏览器下变量缺失时退化为 6px,与桌面壳完全一致。left: 12px,插件按钮贴右侧 right: 12px,两者分居两端,不会叠在一起。verify-entry-placement.test.mjs 同步更新:新增「复用 DSH 的纵向公式」「公式与 DSH 逐字一致(只差回退值)」「浏览器回退与桌面壳同为 6px」「不再钉在 header 下方」四项断言(共 12 项)。lib/client.js(刷新页面即生效)、README.md。版本号 1.22.1 → 1.22.2。修复「同时出现两个『代码管理』入口」+ 移除左边栏入口:
sidebar.footer.action)——那个入口会挤在左边栏「设置」上方。同时删掉只为此存在的跨 React 根订阅桥(openScmPanel()/scmOpenListeners)和 SidebarFooterScmAction 组件。入口现在只保留会话 header 里的两处。sessions.list.getSnapshot() 返回的是 SessionListState,形状为 { ids, byId, phase, subagentsByParent, jobsBySession }——没有 current 字段。而空态常驻入口的显隐判断读的是 snap.current,永远拿到 undefined,于是 hasActiveSession 恒为 false:即使主视图正展示一个活跃会话,常驻按钮也照样渲染,与 header utilities 里的按钮同时出现两个。retainedBy.mainView > 0(ui-layout/DocumentTitle、ui-workspace/WorkspaceBrowser、ui-settings-general 等 5 处都这么写),新增 activeMainSession(snap) helper;并给「DSH 版本未提供 retainedBy」加了兜底(退回「非空白会话里 updatedAt 最新的一条」),拿不准时宁可认为有活跃会话,保证任何时候都不出现两个按钮。HeaderScmEntry——只有主视图展示「非空白」会话时才渲染;常驻入口只在否则情况下渲染。两者由同一个信号(activeMainSession)决定,不再依赖 DSH 的 hideChrome 时机,从构造上不可能同时出现。verify-entry-runtime.test.mjs 会用模拟的 session 快照真实渲染每个注册项,断言「非空白会话 / 空白会话 / 无会话 / 非主视图空白会话 / 缺 retainedBy」五种状态都恰好只显示一个 header 入口、且没有任何入口渲染在 header 之外(共 13 项);并用变异测试验证过这些断言确实能抓出该 bug。verify-entry-placement.test.mjs 增至 12 项(含「不得注册进左边栏」「恰好两个入口」)。lib/client.js(刷新页面即生效)、README.md。版本号 1.22.0 → 1.22.1。修复「未安装 dsh-better-sidebar 时,『代码管理』按钮压住 DSH 自带侧边栏按钮」:
position: fixed; top: 8px; right: 12px(按钮高 32px → 纵向 y=8..40)。而 DSH 自带侧栏开关(ui-sidebar 的 HeaderLeadingControls,注册在 conversation.session.header.leading)位于会话 header 的 30px 标题行内——.header 是 padding: 10px 28px 0 20px,即自带按钮落在 y≈10..40;空态下 .headerCorner 还带 margin-left: auto,右侧同行同样被占。两条带直接相交,于是按钮叠在一起。top: 84px; right: 28px,即 .header 的 76px 高度 + 8px、右缩进对齐 header 的 28px 内边距),与自带侧栏按钮在纵向和横向都不相交;有会话时的入口(Session 日志旁)本来就与自带按钮不在同一侧,保持不变。conversation.session.header.corner 是 single 槽且已被自带右侧栏开关(ui-sidebar-right 的 ExpandButton)占用;conversation.session.header.leading 是 single 槽、被自带侧栏开关占用;sidebar.panellist 的每一项 id 必须是真实主面板 id(会交给 layout.selectPanel),本插件没有主面板。tools/verify-entry-placement.test.mjs(几何 + 注册位回归)与 tools/verify-entry-runtime.test.mjs(在模拟 cordis 运行时里真实跑 apply(ctx),断言两个分支的注册结果)。lib/client.js(刷新页面即生效)、README.md。host 端 /api 路由未改动。版本号 1.21.0 → 1.22.0。彻底移除 Gitee CLI + Gitee 私人令牌配置移到 ②:
GITEE 常量、env.gitee 检测、installHints.gitee、checkSsh 的 giteeLoggedIn/giteeAccount、giteeUserLevelScript、checkGiteeUpdate、giteeLoginTerminal、/gitee/login 与 /gitee/import-token 路由、installCommandSystem/installCommand/installToolFlow/toolInstalled//check-update 里的 gitee 分支;installCommandSystem 对未知工具(含 gitee)直接返回 null,避免误落到 gh 的包名。/gitee-token 存储,保存/清除后即时通知 ③④ 刷新);③ 只读显示令牌配置状态(未配置时提示去 ②),移除「从 Gitee CLI 导入」按钮。~/.dsh/storages,0600)、建仓/push/clone、SSH 443 配置全部保留。lib/index.js(重启 dsh web 生效)、lib/client.js(刷新即生效)。版本号 1.20.0 → 1.21.0。① 环境检查新增 Git 检查更新(仅 Windows)(复用 gh/gitee 的「检查更新」交互):
git --version 与 Git 官方最新稳定版本,有更新则 window.confirm 确认后一键升级(进度弹窗实时展示)。git/git 没有 release,/releases/latest 是 404),改用其 tags API 并过滤预发布/RC(v2.56.0-rc0 之类会被剔除,避免诱导更新到 RC)。winget upgrade Git.Git;需要 root 时沿用 canSudo() 探测(Windows 走 winget 自处理提权)。POST /check-update 的 tool=git 分支(返回 {ok, current, latest, hasUpdate})。lib/index.js(刷新/重启一次生效)、lib/client.js(刷新即生效)。版本号 1.19.0 → 1.20.0。GitHub CLI 下载源选择扩展到 Windows(此前仅 Linux/macOS):
gh-proxy.com)。gh_<版本>_windows_<arch>.msi(实测约 5-6MB/s),再用 msiexec /qn 全静默安装——与 winget 效果一致(gh 注册进 Program Files 与 PATH),但避开了直连 GitHub 的慢速段。「一键安装」分步进度 /「复制安装命令」/「检查更新」均跟随所选镜像。msiexec 用 PowerShell -EncodedCommand(UTF-16LE base64)经 Start-Process -Verb RunAs -Wait 触发 UAC 提权安装(DSH 进程通常非提权)。curl: (3) URL rejected: Port number...:最初用 cmd /c 拼字符串执行 curl,Node spawn 在 Windows 会重排引号,把镜像 URL 的第二个 https:// 误判成「host:端口」导致 curl 拒绝。已改为参数数组直接调用 curl.exe(runLive(bin, args)),逐个传值,彻底绕开 shell 引号重排。gh-proxy.com(快,推荐)→ ghfast.top / ghproxy.net(可用,较慢)→ 官网(慢);与 Linux/macOS 共用同一份 GH_MIRRORS,仅下载资产从 .tar.gz 换成 .msi。ghSource,若默认落官网则很慢)——Linux/macOS/Windows 都存在。现改为 gh 行常驻「下载源」下拉:未安装时影响一键安装,已安装时影响「检查更新」触发的覆盖安装;并修复默认镜像的竞态(改为独立 useEffect 监听 env,保证「已安装点更新」也不会竞态落到官网)。lib/index.js(刷新/重启一次生效)、lib/client.js(刷新即生效)。版本号 1.18.0 → 1.19.0。① 环境检查新增 CLI 检查更新(GitHub CLI / Gitee CLI):
window.confirm 展示「发现新版本:x → y,是否立即更新?」;确认后复用一键安装流程(/install-tool 带 force,跳过「已安装」短路)——gh 走用户级安装脚本 + 所选下载源(镜像),gitee 走官方安装脚本,进度弹窗实时展示,装完自动重新检测。/install-tool 路由最初未透传 force(更新确认后命中「已安装」短路,显示"已安装 执行成功"但版本不变),已修复——force 完整透传到安装流程,更新真正覆盖安装到最新版。POST /check-update 路由(body tool: gh/gitee → {ok, current, latest, hasUpdate})。lib/index.js、lib/client.js。版本号 1.17.0 → 1.18.0。Gitee 平台改用官方 Gitee CLI 检测与登录(对齐 GitHub 的 gh 体验):
gitee,官方 oschina/gitee-cli) 而不是 GitHub CLI(gh);切回 GitHub 恢复检测 gh。Gitee CLI 未安装时可一键安装(Linux/macOS 走官方安装脚本免 sudo 装到 ~/.local/bin/gitee,gitee.com 国内直连无需镜像;Windows 走 npm install -g @gitee/gitee-cli),安装后点「重新检查」即可。gitee auth status 结果——未登录提供「登录 Gitee」按钮(host 打开终端运行 gitee auth login,粘贴私人令牌即可)+「复制登录命令」+「重新检查」;CLI 未装时提示「请在 ① 环境检查安装 Gitee CLI」,并显示令牌兜底状态(Gitee API 仍需要私人令牌,可在 ③ 配置)。gitee auth token)一键导入插件存储(~/.dsh/storages),免去手动复制粘贴;「新建仓库并推送」等 OpenAPI 功能随即可用。lib/index.js、lib/client.js。版本号 1.16.0 → 1.17.0。GitHub CLI 下载提速 + gh 登录按钮 + 移除一键重启:
gh-proxy.com(实测约 2-3.5MB/s、稳定)→ ghfast.top / ghproxy.net(可用但偏慢)→ 官网(慢)。下载 URL 由 host 按所选源构造(镜像 = 前缀 + 官方 Releases URL),「一键安装」与「复制安装命令」都跟随所选下载源。镜像列表由 /env 的 ghMirrors 下发,后续增删镜像无需改浏览器端。/restart 路由、restartLaunch / scheduleRestart / restartNeededFor 等)。gh auth login,KDE konsole 独立窗口、找不到终端给出手动命令指引)+「复制登录命令」+「重新检查」;登录完成后点「重新检查」即可看到账号。lib/client.js(刷新即生效)、lib/index.js(刷新/重启一次生效)。版本号 1.15.1 → 1.16.0。⑤ 发布 npm 包功能修复(v1.15.0 的补丁):
spawn 不存在的终端二进制(如 SteamOS 上没有 x-terminal-emulator)时,异步 ENOENT 错误无监听器被抛成未捕获异常,直接把 dsh host 进程打崩(表现:运行 pnpm dsh web 的终端跟着结束)。现改为先 findTerminal() 探测 PATH 中真实存在的终端(konsole / gnome-terminal / xterm 系等),并对每个 child 挂 error 监听器兜底——绝不再崩宿主。--separate 开独立窗口:不与宿主所在的 konsole 实例纠缠;找不到任何终端时明确返回「请手动运行:<命令>」指引,不再假装打开成功。npm login / npm whoami / npm publish 统一带 --registry=https://registry.npmjs.org(镜像源只同步、不接受发布);npmStatus 新增 publishRegistry / mirrorConfigured,面板在检测到镜像配置(如 npmmirror)时黄色提示「发布将使用官方源」;「复制登录命令」也同步为官方源版本。lib/index.js(需重启 dsh web)、lib/client.js(刷新即生效)。版本号 1.15.0 → 1.15.1。一键安装全面升级:平台自适应 + 尽量免 sudo + 实时进度 + 一键重启。
/env 返回的 installHints(与「安装」按钮同一套平台自适应逻辑);非 Windows 且无可用方式时留空,不再误导。installCommand() 对 gh 优先返回官方二进制下载脚本(查最新版本 → 按平台/架构下载 gh_<ver>_linux_amd64 / macOS_arm64 等 → 解压 → 装到 ~/.local/bin/gh),一键安装失败自动回退系统包管理器。resolveBin 新增 ~/.local/bin 探测——插件加载时即使 PATH 里没有该目录也能找到用户级 gh(不再依赖改 PATH / 设环境变量)。canSudo() 用 sudo -n true 探测;需要密码时不再盲目执行(-n 必然失败),返回「请在终端手动执行:<命令>」指引 + 原因,配合复制按钮即可完成。xcode-select --install,系统自带安装、含 git/ssh),弹窗以「⏳ 已触发,按系统提示完成后点重新检查」呈现。toolInstalled() 改为按当前 PATH/落点直接探测(git/ssh 走 --version、gh 走 ~/.local/bin 优先),不再依赖模块加载时缓存的历史解析结果——装完立即识别,修掉「装好了还报失败」的隐患。/install-tool 改为 NDJSON 事件流(step/out/result,runLive() spawn 流式转发),浏览器端 jpostStream() 逐行消费;弹窗逐步显示「▶ 查询最新版本 → 下载 → 解压 → 安装 → 清理」与实时输出尾部。restartNeededFor() 智能判定(DSH_SCM_GH 显式指定或 ~/.local/bin 在 PATH 时无需重启);需要时弹窗显示「重启 DSH」按钮——新增 POST /restart 路由(机制同 dsh-update:detached helper 等端口释放后按原启动命令拉起新进程,POSIX detached spawn / Windows PowerShell 隐藏窗口;严格 loopback + origin 同源校验;systemd supervisor 托管或 DSH_SCM_RESTART=0 时禁用),点击后页面短暂断开、新进程自动起来。lib/index.js(需重启 dsh web)、lib/client.js(刷新即生效)。版本号 1.14.0 → 1.15.0。按你的要求移除了 ⑤「提交 PR」功能:
lib/client.js 删除整个 PrSection(含 ⑤ 区块的渲染与面板导语中的「⑤提交 PR」文案),一并清理了对应的 i18n 键(EN_DICT)。lib/index.js 删除整套 PR 实现——/pr/targets、/pr/rule、/pr/analyze、/pr/generate、/pr/execute 五个路由,prAnalyzeFlow / prGenerateFlow / prExecuteFlow,PR 目标仓库本地存储(source-code-mgmt-pr-targets.json)、PR 规则缓存读写(rules/ 下的 readPrRule/writePrRule 等)、parseRepoUrl、fillPrTemplate、安全校验(safeEntryPath / safeRegenerateCommand)、PRESET_PR_RULES 与启动时的规则预置写入。llmComplete() 这一通用 LLM 调用助手(与 PR 无关,属可复用能力,本轮暂未用到可直接忽略)。package.json 描述同步去掉「AI-assisted Pull Requests」。克隆板块的「按地址克隆」「本地已有判定」与 ⑥ npm(默认空目录)等仍保持 v1.13.0 行为不变。lib/client.js(刷新即生效)、lib/index.js(需重启 dsh web)。说明:
rules/目录下旧预置文件rules/awesome-dsh-plugin__awesome-dsh-plugin.json已不再被任何代码引用,可自行删除或保留(不影响功能)。
按第二轮反馈继续精化(④⑤⑥ 三处交互收敛):
workspace),明明已存在的仓库仍标「可克隆」。现在新增 localRepoExistsAnywhere():除了当前选中目录,还会遍历 ③ 代码管理登记过的所有工作区 / 自定义目录(~/.dsh/storages/workspace.json + source-code-mgmt-dirs.json)逐一判定——任一被登记的位置已有同名仓库即标「本地已有」;克隆成功后目标目录本就会记入自定义目录,因此「某位置已有该仓库」的知识会自动沉淀下来,下次列表直接命中。.git)并 POST /clone/run 克隆到所选位置;复用「选择目录…」按钮(可输入绝对路径或弹系统文件夹选择器,确认后记住到自定义目录)。目标目录默认沿用顶部下拉,未选择时给出提示。https://github.com/awesome-dsh-plugin/awesome-dsh-plugin,挂载时只读加载一次其预置收录规则(不消耗 AI)并展示加载状态。保留下部:插件信息(名称/分类/描述 en/zh)→「生成 PR 内容(AI)」(命中预置模板可免 AI 填充、仍可编辑)→ 可编辑的标题/描述/条目文件内容 + 工作目录 →「执行 PR」(逐步日志 + PR 链接)。host 端 /pr/analyze、/pr/targets 等路由与预置规则文件保持不变,仅前端做了收敛。本次更新(新增④克隆 / ⑤提交 PR / ⑥发布 npm 三大板块):
gh repo list / Gitee OpenAPI user/repos)名下所有远程仓库并标记本地是否已有;默认目标目录 = 用户主目录(home),不选即克隆到 home;每一行目录都有与 ③ 代码管理一致的「选择目录…」按钮(输入绝对路径或系统文件夹选择器,确认后记住到自定义目录);克隆走 SSH URL,成功后自动加入自定义目录列表。host 新增 GET /clone/default-dir、GET /clone/home、GET /clone/repos、POST /clone/run;本地已存在判定同时覆盖「目录名 == 仓库名且含 .git」的场景~/.dsh/storages/source-code-mgmt-pr-targets.json(不写入插件目录);输入网址即自动读取该仓库的本地规则(GET /pr/rule,只读缓存、不消耗 AI):命中预置/缓存规则就显示对应专属 PR 功能,未命中则显示通用流程并提示「分析 PR 规则」;预置 rules/awesome-dsh-plugin__awesome-dsh-plugin.json——输入 https://github.com/awesome-dsh-plugin/awesome-dsh-plugin 直接给出它的收录规则(fork → clone → 在 data/plugins/<owner>__<repo>.yml 写条目 → npm ci && node scripts/generate-readme.mjs 重生成 README → commit → push → PR,规则写在插件目录);「分析 PR 规则」抓取目标仓库 README/CONTRIBUTING/PR 模板/package.json 交给 DSH 默认模型思考该仓库应如何 PR,输出结构化步骤规则并缓存到插件目录 rules/<owner>__<repo>.json(下次直接执行、不再消耗 AI;「重新分析(忽略缓存)」可强制重想);「生成 PR 内容」按规则模板产出标题/描述/条目文件内容(带预置模板时可免 AI 直接填充、仍可编辑),生成后可改、绝不直接提交;「执行 PR」按 fork→clone→加 upstream→fetch→建分支→写条目→重生成 README→commit→push→gh pr create(Gitee 走 OpenAPI) 逐步执行并显示日志与 PR 链接。host 新增 GET /pr/targets、POST /pr/targets、POST /pr/rule、POST /pr/analyze、POST /pr/generate、POST /pr/executenpm config get registry → npm config list(脱敏 token/auth/password)→ npm whoami → npm pack --dry-run → npm publish),发布前需勾选确认;可切换包目录(同样带「选择目录…」按钮);未登录/未配置时明确提示:显示当前 registry / whoami,未登录给出「打开终端执行 npm login」(host 弹系统终端、进入目标目录执行,POST /npm/login)与「复制登录命令」,登完回面板点「重新检查」;GET /npm/status 会报告目录是否存在、是否含 package.json 并给出对应警告。host 新增 GET /npm/status、POST /npm/step、POST /npm/loginllmComplete()——通过 ctx.get('llm') 调 DSH 的 LLM 运行时,默认模型取 ctx.get('settings').get('agent-default-model')(兜底解析 ~/.dsh/settings.yaml),消息格式适配 GenerateOptions(text-delta 组装)Object.assign 并入 EN_DICT / HOST_EN,不改动原超长字典行;新增 inputStyle() 共享输入框样式;node --check 双端通过C:\Program Files\nodejs\npm.cmd)含空格时,shell 模式会把命令截断成 'C:\Program' 不是内部命令。run() 现对含空格的可执行文件路径加引号包裹(只包裹路径本身,参数仍按数组安全传递),npm registry / whoami / version 实测通过;/pr/analyze 路由此前未把 force 传给分析流程,导致该按钮永远走缓存。现已透传 body.force;parseRepoUrl 此前把 owner/my.repo 截断成 my(正则排除点号),现允许点号、仅剥离尾部 .git;safeEntryPath() 拦截条目文件路径穿越(.. / 绝对路径),safeRegenerateCommand() 只放行白名单构建命令并拒绝 shell 元字符——防止恶意仓库文档诱导 AI 产出危险规则后自动执行;gh repo fork 失败时此前仍显示 ✅,现仅在真实成功或「already exists」时显示成功;user/repos 默认每页 20,此前只列前 20 个;现按每页 100 分页拉全(上限 10 页 ≈ 1000 个,与 GitHub 对齐);tools/smoke-test.mjs 至 31 项(覆盖点号仓库名、路径穿越、命令白名单等边界),全绿;node --check 双端通过os.homedir()(Windows 为 C:\Users\<用户名>,Linux/macOS 为用户主目录),本地已存在判定补上「基目录名 == 仓库名 且含 .git」;GET /pr/rule 读缓存规则(不消耗 AI)——有规则显示专属流程、无规则显示通用流程并提示分析;预置 awesome-dsh-plugin 收录规则(写入插件目录 rules/),输入官方仓库网址即命中;npm whoami 未登录时面板给出明确警告,可一键弹出系统终端执行 npm login(POST /npm/login,Windows 用 cmd /k、其他平台回退常见终端模拟器)或复制登录命令,完成后「重新检查」即可继续发布流程;rules/;仓库已有本地规则时点击「分析 PR 规则」会先询问「是否重新分析」(选否则不做任何事)。本次更新(改动文件查看体验 + 中文文件名兼容性):
git diff --numstat(二进制条目 -\t-)判断。core.quotepath 会把中文路径输出成八进制转义的引号串(如 "\346\270\270…md"),导致改动列表与 diff 头部的中文文件名显示成乱码。现在所有 git 调用统一注入 -c core.quotepath=false(直接输出 UTF-8 路径),并新增 parseGitPath() 反转义兜底——改动列表、ls-files -z、diff --name-only、git diff/git show 头部路径等解析点全覆盖。tools/verify-cn-paths.test.mjs 回归测试(中文路径解析、文本/二进制可查看性、新增文件 diff 生成、已删除文件判断)。(该文件硬编码了 C:\Users\Administrator\workspace,在本机跑不起来,已于 v1.24.0 删除;相关覆盖由 tools/verify-viewable-batch.test.mjs 承担。)本次更新(修复 better-sidebar 集成与「未安装 better-sidebar」时入口的若干问题):
entryUnmount,slots 路径漏了——导致切到「侧边栏 Tab」形态时 header 入口没被拆掉。现已把 slots.inject 返回的 disposer 记进 entryUnmount,切到 Tab 时正确拆除(顺带修正同类清理逻辑)。conversation.session.header.utilities(scope: 'session'),而空态时整个会话 header 被 hideChrome 隐藏,按钮出不来。现在改为右上角常驻——有活跃会话时放在「Session 日志」旁(会话 header 的右对齐列表),新对话/无会话空态时改为 shell.overlay 常驻槽里 position: fixed 钉在右上角的按钮(仅空白/无会话时显示,避免与 header 按钮重复)。captureSessions() 延迟 1.2s 才捕获到 session 列表,捕获前误判为「无会话」而提前点亮常驻按钮。现在未捕获到 session 列表前不渲染常驻按钮,杜绝刷新闪叠。sessions.list.getSnapshot().byId[current].blank)——活跃(非空白)会话才隐藏常驻按钮(交给 header 内的按钮),空白/无会话则显示,替代原来不准确的「current === undefined」判断。以上均为客户端(
lib/client.js)改动,刷新页面即生效;host 端/api路由与推送/忽略逻辑未改动。
本次更新:
ctx.locale 订阅实时重渲染,Tab 标题随动),无需刷新/重启;host 端按请求的 ?lang= 经 AsyncLocalStorage 按请求返回对应语言,并发不串扰。中文界面与 v1.9.0 完全一致,英文界面为完整翻译(含 >100MB 忽略原因、Gitee 令牌提示、git 命令失败回退等全部消息)。新增 tools/ 下的 i18n 提取/应用/测试脚本便于后续维护本次更新:
package.json 的 dsh.bundle 从裸字符串 "./lib/index.js" 改为对象形态 { "patch": "./cordis.patch.yml" },并新增 cordis.patch.yml(insert source-code-mgmt 行)。现在 dsh plugin --profile web add source-code-mgmt 一步装完即被自动加入 dsh.profile.bundles 并注册进 Cordis loader 树,不再需要手动编辑 cordis.patch.yml 激活;README 中「安装 ≠ 激活」说明与 PowerShell 激活脚本已删除。功能行为零变化(仍是同一份 lib/index.js host 端 + lib/client.js 浏览器端)本次更新:
chore: update <文件夹名>)提交,然后推送到远程;不会像「推送更改」那样自动暂存所有未暂存的改动。推送成功后自动刷新状态并显示「⟳ 刷新中…」,界面显示本次提交的信息与 hash。host 端新增 POST /push-staged 路由(pushStagedFlow,沿用本插件 run()/GIT_SSH)本次更新:
syncAllRepos 与「命中缓存秒显」分支本次更新:
conversation.session.header.utilities 槽位注册,出现在右上角 Session log 旁边的右对齐列表里(与 Session log 同款胶囊样式、间距 8px 不挤在一起);点击后在右侧展开一个 dsh-better-sidebar 外观的集成面板(复用原话术:环境检查 / SSH / 代码管理三步),并把主内容区 #root 往左推挤(margin-right + width calc)。slots 服务不可用时降级为右上角浮动按钮 + 右侧面板。改动仅限 lib/client.js(浏览器端),刷新页面即生效,host 端 /api 路由未动本次更新:
POST /install-tool 路由(best-effort,回传执行命令与输出)sudo -n(已是 root 则省略,避免权限失败挂起);decodeSessionDir 按平台区分 Windows 盘符路径与 POSIX 绝对路径,macOS / Linux 下也能正确从 session 目录还原工作区本次更新:
~/.ssh/*.pub 中的 ed25519 公钥——优先用 id_ed25519,否则用找到的第一个(支持任意命名密钥);都没有才用默认名 id_ed25519 新建。② 状态行显示实际检测到的密钥名,SSH config 的 IdentityFile 也用它,有自定义命名密钥也能正确识别、配置并推送本次更新(补齐本地 Git 工作流,未改动远程同步逻辑):
② SSH 默认折叠 + 标题行切平台:② SSH 部分默认折叠,标题行内嵌「平台」下拉(折叠时也能切),切换后自动展开该部分以继续配置,③ 跟随平台
选择性暂存 + 自定义提交信息:「改动」弹窗每个文件行新增暂存 / 取消暂存按钮(按 git status --porcelain 的 XY 状态区分 staged/unstaged),并显示「已暂存 / 未暂存」;③ 面板仓库名上方新增提交信息输入框 + 「提交」按钮,提交不再用固定 chore: update workspace via DSH...(留空则自动生成基于文件夹名的信息);沿用 pushFlow 的无身份兜底逻辑
分支切换 + 提交历史 + revert / cherry-pick:「分支」行旁新增「切换」(弹窗列出分支,点选 checkout)与「历史」(弹窗列出提交,每条可「查看」「revert」「cherry-pick」——后两者带确认框)
真·并排 diff:把 unified diff 解析成(旧行/新行)配对,左右两栏渲染(删除行红底、新增行绿底);改动文件展开与历史点某提交(/commit-diff)都用并排视图;保留纯文本 renderDiff 作降级(二进制/无法解析时)
「查看改动」秒出:加载仓库状态时后台并发预取所有改动文件的 diff(并发≤3、纯本地 git 读取)并缓存,点开文件行立即显示,不再等「加载中…」
提交历史右键菜单:「历史」弹窗每条提交可点**「⋯」或右键**打开菜单——查看提交差异、复制短哈希、复制完整哈希、复制提交信息、还原此提交(revert)、拾取此提交(cherry-pick,后两者带确认框)
本地 Git 功能按 better-sidebar 自适应隐藏:已安装并激活 dsh-better-sidebar 时,③ 面板隐藏本插件自带的本地 Git 工作流(暂存/取消暂存、自定义提交、分支「切换」、提交「历史」+ revert/cherry-pick、并排 diff),因为这些能力 better-sidebar 的 Git 面板已覆盖,避免重复;此情况下「查看改动」弹窗只列改动文件,并提示具体改动内容请到 dsh-better-sidebar 的「源代码管理 / Git 面板」查看;未安装时则展示完整本地 Git 功能
host 端新增 /stage /unstage /commit /branches /checkout /log /revert /cherrypick /commit-diff 路由(全部走 isLoopbackRequest 校验 + run()/GIT_SSH)
本次更新:
ctx.betterSidebar.registerTab 把「代码管理」注册为该侧边栏的一个新 Tab 页面;未安装时,在 DSH 页面右上角显示一个浮动按钮,点击展开右侧栏抽屉(形态类似 dsh-better-sidebar 的右栏),内容为同一面板ctx.get('betterSidebar')),零 I/O、零网络,毫秒级,不影响 DSH 启动;并对激活顺序做了兜底延迟重试,保证最终落在正确的形态sidebar.footer.action 插槽注册触发按钮;ScmPanel 支持 variant(tab / drawer)以分别适配侧边栏 Tab 与右侧抽屉布局,面板 UI 与 host 端 /api 路由完全复用、未改动本次更新:
-、新增行绿色带 +、上下文灰色;untracked 新文件无 diff 只列名称(后端对每个改动文件追加 git diff/git diff --cached 内容)本次更新:
git@github.com:...」的问题;本地没有对应平台远程时显示「(无)」且不计算同步github.com/gitee.com 域名匹配远程,导致 deepseek-ai/deepseek-harness 这类别人的远程被当成用户自己的远程显示、还能点「推送更改」。现在校验远程 owner 必须等于当前登录账号(GitHub=gh 账号、Gitee=令牌账号),不属于自己的远程一律不算,界面回到「同名仓库检测 + 新建仓库」形态;同时无远程时「同步」行显示「(无)」,不再误显示「与远程一致」git pull --ff-only,而是复用「强制对齐」的 git fetch + git reset --hard origin/<branch> 逻辑——本地干净没有可丢失的改动,reset 等同快速前进到远程最新,且不弹确认框直接执行,结果更可靠(可绕过 rebase/merge 常见失败场景)git fetch + git reset --hard,本地完全重置为远程),与用户实际想要的「一键对齐」一致setErr 再刷新仓库状态,随后 loadRepo 把错误提示无条件清空,导致失败时结果被立即抹掉、界面像什么都没发生;现在错误/结果会保留显示(切换目录 / 切换平台 / 手动刷新状态仍会清空旧提示),对齐失败等也能看到真实原因本次更新:
github.com → ssh.github.com:443;Gitee:gitee.com 443)ssh -T git@github.com / ssh -T git@gitee.comcurl 调用),令牌在 ③ 输入并保存到本机 ~/.dsh/storages/source-code-mgmt-gitee.json(0600),可清除GET /repos/{owner}/{name};可见性读取/修改:Gitee GET/PATCH /repos/{owner}/{name}POST /user/repos 建仓 + SSH 远程 git@gitee.com:<owner>/<name>.git 推送本次更新:
Windows / macOS / Linux(对应底层 win32 / darwin / linux)首发版本,包含以下功能与修复:
git / gh / ssh / ssh-keygen 按 ①环境变量覆盖 → ②PATH 查找 → ③(仅 Windows)Git 自带目录回退 自动定位,换设备无需额外配置,即使 ssh 不在 PATH 也能工作gh 报 "no commits found"100MB 文件自动忽略(整目录或单文件)并显示未上传原因
MIT
FAQs
DSH plugin: source code management. Registers「代码管理」as a card in DSH's OWN right sidebar (the official guide page, beside 工作区文件 / 新建终端 / 浏览器) and as a tab page in that column — there is no separate button. Older hosts degrade gracefully: dsh-better-sidebar
The npm package source-code-mgmt receives a total of 27 weekly downloads. As such, source-code-mgmt popularity was classified as not popular.
We found that source-code-mgmt demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.