From fdafd5d6b303e87bfa825dfcd2402c153d6c8b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E6=99=A8?= <135297162+jiangchen1245@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:54:40 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=9D=E5=A7=8B=E5=8C=96=20dsh-enha?= =?UTF-8?q?ncements=20=E7=95=99=E6=A1=A3=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 插件源码:dsh-usage(用户头像 + DeepSeek 官方 API 消耗面板) - 工具:dl-gh.mjs(node fetch 下载 gh,绕开沙箱 curl 限制) - 文档:PLAN.md 路线图 + mmx 视觉桥接补丁说明 - 资产:DeepSeek 鲸鱼图标 --- .gitignore | 19 ++++++ README.md | 42 +++++++++++++ assets/dsh.ico | Bin 0 -> 13520 bytes assets/user-screenshot.png | Bin 0 -> 48432 bytes docs/PLAN.md | 66 ++++++++++++++++++++ docs/mmx-image-bridge-PATCH.md | 53 ++++++++++++++++ plugins/dsh-usage/README.md | 35 +++++++++++ plugins/dsh-usage/client.js | 111 +++++++++++++++++++++++++++++++++ plugins/dsh-usage/host.js | 47 ++++++++++++++ tools/dl-gh.mjs | 21 +++++++ 10 files changed, 394 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/dsh.ico create mode 100644 assets/user-screenshot.png create mode 100644 docs/PLAN.md create mode 100644 docs/mmx-image-bridge-PATCH.md create mode 100644 plugins/dsh-usage/README.md create mode 100644 plugins/dsh-usage/client.js create mode 100644 plugins/dsh-usage/host.js create mode 100644 tools/dl-gh.mjs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96270e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# 依赖与本地缓存 +node_modules/ +tools/gh-download/ +tools/gh-extract/ +*.log + +# 凭据与敏感信息(绝不入库) +.credentials.yaml +*.pem +*.key +.env + +# 系统文件 +Thumbs.db +.DS_Store + +# DSH 本地状态 +sessions/ +storages/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e9f3880 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# dsh-enhancements — DeepSeek Harness 增强插件集 + +把 DeepSeek Harness 升级为 **Codex 级智能助手** 的全部开发成果归档于此: +插件源码、桌面应用壳脚本、视觉桥接补丁、文档与规划。 + +> 由本机 agent 会话持续开发,随开发进度同步推送到 GitHub / Gitea。 + +## 目录结构 + +``` +dsh-enhancements/ +├── README.md # 本文件 +├── docs/ +│ ├── PLAN.md # 总体规划(Codex 化路线图) +│ └── mmx-image-bridge-PATCH.md # 聊天发图→mmx 视觉识别 桥接补丁说明 +├── plugins/ # 每个插件一个目录(源码 + 说明) +│ └── dsh-usage/ # 用户头像 + API 消耗面板 +├── presets/ # Agent 预设(落盘形式,重启可用) +├── tools/ # 辅助脚本 +│ ├── dl-gh.mjs # 用 node fetch 下载 gh CLI(沙箱内 curl 被禁时的替代) +│ └── gh-download/ # 下载缓存(不入库,见 .gitignore) +└── assets/ # 图标等资源 + └── dsh.ico # DeepSeek 鲸鱼图标(托盘/快捷方式用) +``` + +## 已包含 + +- **桌面应用壳**:`C:\Users\a1703\.dsh\bin\dsh-app.ps1`(托盘 + Edge 独立窗口)与桌面快捷方式 `DeepSeek Harness.lnk` —— 见 docs/ 说明 +- **视觉桥接**:聊天窗口发图 → MiniMax M3(NVIDIA 托管)识别 +- **dsh-usage 插件**:侧边栏底部 🐋 用户头像 → DeepSeek 官方余额 API 消耗面板 + +## 安全说明 + +- 本仓库**绝不包含**任何 API Key、Token、密码。 +- 部署机上的凭据位于 `%DSH_HOME%\.credentials.yaml`,由运行时读取,不入库。 +- Gitea 等远程地址的凭据使用系统凭据管理器,不写入 `.git/config`。 + +## 快速开始(新机器) + +1. 安装 Node.js ≥ 18(带全局 fetch) +2. 把 `tools/dl-gh.mjs` 下载的 gh 解压到 PATH,或直接用 git +3. 按 `docs/PLAN.md` 各阶段说明启用插件 diff --git a/assets/dsh.ico b/assets/dsh.ico new file mode 100644 index 0000000000000000000000000000000000000000..9799bafb689b4550f2873f663ace1b644fb2363c GIT binary patch literal 13520 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 literal 0 HcmV?d00001 diff --git a/docs/PLAN.md b/docs/PLAN.md new file mode 100644 index 0000000..b9a3aa1 --- /dev/null +++ b/docs/PLAN.md @@ -0,0 +1,66 @@ +# 总体规划:把 DeepSeek Harness 升级为 Codex 级智能助手 + +> 状态:进行中。本文件是路线图,随开发更新。 + +## 目标 + +用户头像/API 消耗、GitHub/Gitea 集成、定时任务、电脑操作模式(截图+视觉+鼠标键盘)、 +内置浏览器操控、吉祥物悬浮助手,全部成果留档并发布到 GitHub/Gitea 仓库。 + +## 现状盘点(已验证) + +- 桌面应用化:快捷方式 → 托盘(dsh-app.ps1)→ Edge 独立窗口 ✅ +- 聊天发图 + 视觉理解:mmx 桥接(MiniMax M3 via NVIDIA)✅ +- 动态 Cordis 插件(Host/Client 双向)✅ +- git 2.55 + GitHub(jiangchen1245)+ 自建 Gitea(gitea.truestudio.online)✅ +- DeepSeek 官方余额 API 可用(GET https://api.deepseek.com/user/balance)✅ + +## 阶段 + +### 1️⃣ GitHub / Gitea 集成(进行中) +- [x] 验证 Gitea 凭据(git 协议 + API basic auth) +- [ ] 装 gh CLI(沙箱内 github CDN 不通,需换网络或用户手动装) +- [ ] dsh-git 插件:repo_status / gh_issue / gh_pr / gh_commit / gh_push +- [ ] GUI"Git 中心"面板 + +### 2️⃣ 定时任务插件 +- [ ] dsh-scheduler:cron 表达式 + 动作(命令/工具调用) +- [ ] 持久化(重启不丢)+ GUI 管理面板 + +### 3️⃣ 电脑操作模式(核心) +- [ ] screen_capture(截图) +- [ ] screen_vision(mmx M3 视觉识别元素/坐标) +- [ ] screen_click / screen_type / screen_key(user32) +- [ ] screen_list_windows / screen_ui_tree(UI Automation) +- 闭环:截图 → 看懂 → 操作 → 再看 + +### 4️⃣ 内置浏览器操控 +- [ ] Edge CDP 驱动(导航/点击/填表/截页) +- [ ] GUI 内嵌浏览器面板(webServer 反代 iframe) + +### 5️⃣ 吉祥物悬浮助手 +- [ ] shell.overlay 右下角 🐋 浮窗 + 迷你对话 + 快捷按钮 + +### 6️⃣ 体验打磨 +- [ ] Agent Presets(computer-use / dev) +- [ ] 快捷键、布局优化、开机自启 + +### 7️⃣ 留档与发布 +- [x] 本地仓库 D:\project\dsh-enhancements(git init + 结构) +- [ ] 推送到 Gitea jiangchen/dsh-enhancements +- [ ] 推送到 GitHub jiangchen1245/dsh-enhancements +- [ ] 脱敏检查(无 Key/Token/密码) + +## 技术要点 + +- 沙箱:curl/winget 的 HTTPS 被禁,node fetch 可用 → 网络操作统一走 node +- 客户端无 window/document/fetch → 打开外链由 Host 侧 shell 完成 +- 动态插件不持久 → 每个插件同时落盘为 presets/ 文件与源码归档 +- 升级覆盖 → mmx 桥接补丁保留重放脚本(PATCH.md) +- 凭据 → 一律系统凭据管理器,不入库、不进 .git/config + +## 安全红线 + +- 仓库绝不包含 API Key / Token / 密码 +- Gitea remote 不用内嵌密码 URL;用凭据管理器 +- .credentials.yaml 只留在部署机 diff --git a/docs/mmx-image-bridge-PATCH.md b/docs/mmx-image-bridge-PATCH.md new file mode 100644 index 0000000..6e13b8c --- /dev/null +++ b/docs/mmx-image-bridge-PATCH.md @@ -0,0 +1,53 @@ +# MMX 图片桥接补丁(聊天窗口发图 → mmx-vision 技能识别) + +日期:2026-08-12 +目的:让 DeepSeek Harness 的聊天窗口可以直接发送/粘贴图片。图片被持久化为附件, +发给模型时由适配器转成"附件路径文本提示",agent 收到后用 mmx-vision 技能(本地 +Node 脚本调用 NVIDIA 托管的 MiniMax M3)识别或按用户意图取用。 + +## 相关自定义(本机桌面应用壳) + +- `C:\Users\a1703\.dsh\bin\dsh-app.ps1`:托盘应用(无窗口后台运行 dsh web + 系统托盘, + 右键"打开主界面 / 退出",打开用 Edge `--app` 独立窗口)。快捷方式 + `桌面\DeepSeek Harness.lnk` 指向它(powershell -WindowStyle Hidden -File)。 +- `C:\Users\a1703\.dsh\assets\dsh.ico`:多尺寸(16-256)DeepSeek 鲸鱼图标, + 由 `favicon.svg` 经 Edge 无头渲染 + System.Drawing 缩放生成,供托盘/快捷方式使用。 +- 旧版 `C:\Users\a1703\.dsh\bin\dsh-web.cmd`:手动模式(显示控制台窗口),备用。 + +## 修改的文件(全部位于 npx 缓存安装,重启 Harness 生效) + +### 0. @deepseek-ai/dsh-web-frontend/dist(应用窗口图标) +- 新增 `favicon-256.png`、`favicon.ico`(多尺寸鲸鱼图标)。 +- `index.html`:favicon 链接增加 `favicon-256.png`(放在 svg 前)。 +- `manifest.webmanifest`:icons 增加 png/ico 条目。 +- 作用:Edge `--app` 独立窗口与浏览器标签显示 DeepSeek 鲸鱼图标而不是 Edge 图标。 + 静态文件实时生效(服务直接读磁盘),无需重启。 + +### 1. @deepseek-ai/dsh-llm-deepseek/lib/index.js(deepseek 适配器) +- 原行为:内容含 image 块时抛 `UNSUPPORTED_CONTENT`(拒绝整轮对话)。 +- 新行为:`assertTextOnly` 改为 `imageBlocksText`,把每个 image 块序列化成一行 + `[用户发送的图片附件 id=sha256:xxx 类型=image/png 文件=/attachments/v1/objects/xx/xxx]` + 文本;`serializeMessages` 的用户消息文本 = 原文本 + 图片提示。 +- 附件路径规则与 `@deepseek-ai/dsh-attachment-local` 的存储约定一致 + (对象路径 `objects//`,attachmentId 前缀 `sha256:`)。 + +### 2. @deepseek-ai/dsh-host-apiproxy/lib/index.js(host API 代理) +- `prompt` RPC:删除"模型 inputModalities 不含 image 就拒绝(MODEL_DOES_NOT_SUPPORT_IMAGES)"检查, + 图片一律放行(由适配器文本化)。 +- `selectModel` RPC:删除"会话已含图片时禁止切换到不支持图片的模型"检查。 + +## 生效条件 +- 重启 Harness 进程后生效(node_modules 中的运行时文件改动不会热加载)。 +- 备份:同目录 `index.js.mmx-bak`(每个改过的包旁边)。 + +## 升级后重放 +重新安装/升级 @deepseek-ai/dsh 后,以上两处改动会被覆盖。重放步骤: +1. 用 `node --check` 验证改动文件语法。 +2. 对每个包:把 `assertTextOnly` 的抛错逻辑替换为 `imageBlocksText`(见下), + 并删除 api-proxy 两处图片能力检查。 +3. 或直接 diff 当前 `.mmx-bak` 与新文件,把差异搬过去。 + +## 不影响 +- 客户端 UI 无需改动(图片 rail/拖放/粘贴本来就完整)。 +- pi-ai 模型路由(真多模态)不受影响。 +- 会话历史中的图片块在每次模型调用时都会文本化,客户端仍按附件渲染缩略图。 diff --git a/plugins/dsh-usage/README.md b/plugins/dsh-usage/README.md new file mode 100644 index 0000000..7f090c5 --- /dev/null +++ b/plugins/dsh-usage/README.md @@ -0,0 +1,35 @@ +# dsh-usage — 用户头像 + API 消耗面板 + +在 DeepSeek Harness 侧边栏底部(设置旁)添加一个 Codex 式**用户头像**入口, +点击弹出模态面板,展示 **DeepSeek 官方 API 余额/消耗**,并可一键打开官网用量页。 + +## 效果 + +- 侧边栏底部(设置按钮旁):圆形 🐋 头像按钮(窄栏只显图标,宽栏带"API 用量"文字) +- 点击 → 居中模态面板: + - 账户可用状态 + - 各币种:总额 / 赠送余额 / 充值余额(来自官方 `GET https://api.deepseek.com/user/balance`) + - 按钮:刷新 / 打开官网用量页 / 关闭 + +## 实现 + +| 端 | 机制 | +|---|---| +| Host | `harness.handle('usage/balance')`:shell 运行 node 脚本读取 `%DSH_HOME%\.credentials.yaml` 的 `DEEPSEEK_API_KEY`,调用官方余额 API,返回 JSON(密钥不出进程日志);`usage/openPortal`:打开官网用量页 | +| Client | `sidebar.footer.action` 注册头像按钮;`shell.overlay` 注册模态面板;两个组件通过插件内微型 store 共享开关状态;样式用主题 CSS 变量(`--dsw-alias-*`) | + +## 为什么用官方 API + +DeepSeek 官方提供余额接口(`/user/balance`),返回 `is_available` + `balance_infos[]`。 +用量明细官网暂无公开 API,故面板提供"打开官网用量页"(platform.deepseek.com/usage)兜底。 + +## 文件 + +- `host.js` — Host 半(RPC + 官方 API 调用) +- `client.js` — Client 半(头像 + 模态 UI) +- 部署方式:通过动态 Cordis 插件(`cordis_define` / `cordis_run`)或本仓库 `presets/` 下的预设文件 + +## 前置 + +- `%DSH_HOME%\.credentials.yaml` 中存在 `DEEPSEEK_API_KEY` +- 官方余额 API 需可访问(api.deepseek.com) diff --git a/plugins/dsh-usage/client.js b/plugins/dsh-usage/client.js new file mode 100644 index 0000000..59228a3 --- /dev/null +++ b/plugins/dsh-usage/client.js @@ -0,0 +1,111 @@ +// dsh-usage 插件 — Client 半 +// 注册两处 UI: +// sidebar.footer.action 'dsh-usage-avatar' — 设置旁的 🐋 用户头像按钮 +// shell.overlay 'dsh-usage-modal' — 点击头像弹出的官方余额模态面板 +// 两个组件通过插件内微型 store(open + listeners)共享开关状态。 +return { + apply(ctx) { + const slots = ctx.get('slots') + if (slots === undefined) return + styles.insert(` +.dsh-ua-avatar { display:flex; align-items:center; gap:8px; width:100%; padding:7px 10px; background:transparent; border:0; border-radius:8px; color:var(--dsw-alias-label-primary); cursor:pointer; font:inherit; } +.dsh-ua-avatar:hover { background:var(--dsw-alias-bg-layer-2); } +.dsh-ua-avatar.active { background:var(--dsw-alias-bg-layer-2); } +.dsh-ua-avatar-icon { width:24px; height:24px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:15px; flex:none; background:var(--dsw-alias-bg-layer-2); border:1px solid var(--dsw-alias-border-l1); } +.dsh-ua-avatar-label { font-size:12px; color:var(--dsw-alias-label-secondary); } +.dsh-ua-backdrop { position:fixed; inset:0; z-index:9999; background:rgba(0,0,0,.35); display:flex; align-items:center; justify-content:center; pointer-events:auto; } +.dsh-ua-modal { width:380px; max-width:92vw; background:var(--dsw-alias-bg-overlay); border:1px solid var(--dsw-alias-border-l1); border-radius:12px; box-shadow:0 12px 40px rgba(0,0,0,.3); padding:16px; color:var(--dsw-alias-label-primary); pointer-events:auto; font-size:13px; } +.dsh-ua-modal h3 { margin:0 0 12px; font-size:15px; display:flex; align-items:center; gap:8px; } +.dsh-ua-row { display:flex; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--dsw-alias-border-l1); } +.dsh-ua-row:last-child { border-bottom:0; } +.dsh-ua-row .k { color:var(--dsw-alias-label-secondary); } +.dsh-ua-row .v { font-weight:600; } +.dsh-ua-ok { color:var(--dsw-alias-state-success-primary); } +.dsh-ua-err { color:var(--dsw-alias-state-error-primary); } +.dsh-ua-btns { display:flex; gap:8px; margin-top:14px; } +.dsh-ua-btn { flex:1; padding:7px 10px; border-radius:8px; border:1px solid var(--dsw-alias-border-l2); background:var(--dsw-alias-bg-layer-1); color:var(--dsw-alias-label-primary); cursor:pointer; font:inherit; font-size:12px; } +.dsh-ua-btn:hover { background:var(--dsw-alias-bg-layer-2); } +.dsh-ua-btn.primary { background:var(--dsw-alias-brand-primary); color:#fff; border-color:transparent; } +`) + const store = { open: false, listeners: new Set() } + const setOpen = (v) => { store.open = v; store.listeners.forEach((fn) => fn()) } + const useOpen = () => { + const [s, setS] = React.useState(store.open) + React.useEffect(() => { store.listeners.add(setS); return () => store.listeners.delete(setS) }, []) + return s + } + + const UsageModal = (props) => { + const [state, setState] = React.useState({ loading: true, data: null, error: null }) + const load = () => { + setState({ loading: true, data: null, error: null }) + host.call('usage/balance', {}).then((data) => { + setState({ loading: false, data: data, error: null }) + }).catch((e) => { + setState({ loading: false, data: null, error: String((e && e.message) || e) }) + }) + } + React.useEffect(() => { load() }, []) + const openPortal = () => { host.call('usage/openPortal', {}) } + const data = state.data + const rows = [] + if (data && Array.isArray(data.balance_infos)) { + data.balance_infos.forEach((b) => { + rows.push(React.createElement('div', { className: 'dsh-ua-row', key: b.currency + '-total' }, + React.createElement('span', { className: 'k' }, '总额 (' + b.currency + ')'), + React.createElement('span', { className: 'v' }, String(b.total_balance)))) + rows.push(React.createElement('div', { className: 'dsh-ua-row', key: b.currency + '-granted' }, + React.createElement('span', { className: 'k' }, '赠送余额'), + React.createElement('span', { className: 'v' }, String(b.granted_balance)))) + rows.push(React.createElement('div', { className: 'dsh-ua-row', key: b.currency + '-topped' }, + React.createElement('span', { className: 'k' }, '充值余额'), + React.createElement('span', { className: 'v' }, String(b.topped_up_balance)))) + }) + } + let body = null + if (state.loading) { + body = React.createElement('div', { className: 'dsh-ua-row' }, React.createElement('span', { className: 'k' }, '正在查询官方余额…')) + } else if (state.error || (data && data.error)) { + body = React.createElement('div', { className: 'dsh-ua-err' }, String(state.error || (data && data.error))) + } else { + body = React.createElement('div', null, + React.createElement('div', { className: 'dsh-ua-row' }, + React.createElement('span', { className: 'k' }, '账户可用'), + React.createElement('span', { className: 'v ' + (data.is_available ? 'dsh-ua-ok' : 'dsh-ua-err') }, data.is_available ? '可用' : '不可用')), + rows) + } + return React.createElement('div', { className: 'dsh-ua-backdrop', onClick: props.onClose }, + React.createElement('div', { className: 'dsh-ua-modal', onClick: (e) => e.stopPropagation() }, + React.createElement('h3', null, '🐋 DeepSeek API 用量', + React.createElement('span', { style: { marginLeft: 'auto', fontSize: '12px', color: 'var(--dsw-alias-label-secondary)' } }, '官方余额')), + body, + React.createElement('div', { className: 'dsh-ua-btns' }, + React.createElement('button', { className: 'dsh-ua-btn', onClick: load }, '刷新'), + React.createElement('button', { className: 'dsh-ua-btn', onClick: openPortal }, '打开官网用量页'), + React.createElement('button', { className: 'dsh-ua-btn primary', onClick: props.onClose }, '关闭')))) + } + + slots.inject('sidebar.footer.action', () => slots.register( + { name: 'sidebar.footer.action', id: 'dsh-usage-avatar', order: 100, label: 'API 用量' }, + (props) => { + const open = useOpen() + return React.createElement('button', { + className: 'dsh-ua-avatar' + (open ? ' active' : ''), + title: 'API 用量 / 余额', + onClick: () => setOpen(true), + }, + React.createElement('span', { className: 'dsh-ua-avatar-icon' }, '🐋'), + props.wide ? React.createElement('span', { className: 'dsh-ua-avatar-label' }, 'API 用量') : null) + } + )) + + slots.inject('shell.overlay', () => slots.register( + { name: 'shell.overlay', id: 'dsh-usage-modal', order: 100 }, + () => { + const open = useOpen() + if (!open) return null + return React.createElement(UsageModal, { onClose: () => setOpen(false) }) + } + )) + }, +} diff --git a/plugins/dsh-usage/host.js b/plugins/dsh-usage/host.js new file mode 100644 index 0000000..629b3d1 --- /dev/null +++ b/plugins/dsh-usage/host.js @@ -0,0 +1,47 @@ +// dsh-usage 插件 — Host 半 +// 通过 cordis_define 定义(动态 Cordis 插件)。 +// 提供两个 Package-private RPC: +// usage/balance — 读取 %DSH_HOME%\.credentials.yaml 的 DEEPSEEK_API_KEY, +// 调用 DeepSeek 官方余额 API(GET /user/balance),返回 JSON。 +// usage/openPortal— 用系统默认浏览器打开官网用量页。 +// 密钥只在 node 子进程内读取与使用,绝不打印、不入库。 +return { + inject: ['shell'], + apply(ctx) { + harness.handle('usage/balance', async (args) => { + const script = [ + "const fs = require('node:fs');", + "const os = require('node:os');", + "const path = require('node:path');", + "const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');", + "const file = path.join(home, '.credentials.yaml');", + "let key = null;", + "try { const txt = fs.readFileSync(file, 'utf8'); const m = txt.match(/DEEPSEEK_API_KEY:\\s*(\\S+)/); if (m) key = m[1]; } catch (e) {}", + "if (!key) { console.log(JSON.stringify({ error: 'no-api-key', file: file })); process.exit(0); }", + "fetch('https://api.deepseek.com/user/balance', { headers: { Authorization: 'Bearer ' + key, Accept: 'application/json' }, signal: AbortSignal.timeout(15000) })", + " .then(r => r.json().then(j => console.log(JSON.stringify(Object.assign({ status: r.status }, j)))))", + " .catch(e => console.log(JSON.stringify({ error: String((e && e.message) || e) })));", + ].join('\n') + try { + const spec = ctx.shell.resolve({ command: 'node', args: ['-e', script] }) + const res = await ctx.shell.run(spec) + const stdout = String(res.stdout || '') + const lines = stdout.trim().split(/\r?\n/).filter(Boolean) + if (lines.length) return JSON.parse(lines[lines.length - 1]) + return { error: 'empty-output', stderr: String(res.stderr || '').slice(0, 400) } + } catch (e) { + return { error: String((e && e.message) || e) } + } + }) + + harness.handle('usage/openPortal', async () => { + try { + const spec = ctx.shell.resolve({ command: 'cmd', args: ['/c', 'start', '', 'https://platform.deepseek.com/usage'] }) + await ctx.shell.run(spec) + return { ok: true } + } catch (e) { + return { ok: false, error: String((e && e.message) || e) } + } + }) + }, +} diff --git a/tools/dl-gh.mjs b/tools/dl-gh.mjs new file mode 100644 index 0000000..7462b98 --- /dev/null +++ b/tools/dl-gh.mjs @@ -0,0 +1,21 @@ +// 下载 GitHub CLI (gh) 最新 Windows x64 便携版 zip。 +// 用法: node dl-gh.mjs [输出目录] → 打印 { tag, file, bytes } +// 沙箱内 curl/winget 的 HTTPS 被禁,但 node fetch 可用,故用 node 下载。 +import fs from 'node:fs' +import path from 'node:path' + +const outDir = path.resolve(process.argv[2] || '.') +fs.mkdirSync(outDir, { recursive: true }) + +const relUrl = 'https://api.github.com/repos/cli/cli/releases/latest' +const rel = await (await fetch(relUrl, { headers: { 'User-Agent': 'dsh-enhancements' }, signal: AbortSignal.timeout(30000) })).json() +if (!rel.tag_name) throw new Error('bad release response: ' + JSON.stringify(rel).slice(0, 200)) +const asset = rel.assets.find((a) => a.name.endsWith('windows_amd64.zip')) +if (!asset) throw new Error('no windows_amd64.zip asset in ' + rel.tag_name) + +const out = path.join(outDir, asset.name) +const res = await fetch(asset.browser_download_url, { signal: AbortSignal.timeout(300000) }) +if (!res.ok) throw new Error('download failed: HTTP ' + res.status) +const buf = Buffer.from(await res.arrayBuffer()) +fs.writeFileSync(out, buf) +console.log(JSON.stringify({ tag: rel.tag_name, file: out, bytes: buf.length }))