dsh-github-flow 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -32,7 +32,12 @@
32
32
  - **Global Cockpit** (`sidebar.panellist`, Order 20): Account-wide repository overview, local workspace matrix, and pending personal PRs/issues.
33
33
  - **Right Sidebar Panel** (`sidebarRightTabs`, Order 40): Shows current repository status, PR list with CI check pills, and a 2x2 quick action grid (Open Repo, View PRs, Create PR, New Issue) with zero popup dependencies.
34
34
  - **Native Light/Dark Dual-Theme Adaptation**: Deeply aligned with DSH official DSW (DeepSeek Web) design tokens, seamlessly reacting to `body[data-ds-dark-theme]` without lag or flickering.
35
- 5. **Standard Cordis Microkernel Compliance**:
35
+ 5. **Robust Environment Diagnosis & Three-State Setup Engine**:
36
+ - Zero-guesswork onboarding: Accurately differentiates `NOT_INSTALLED` (`Missing`), `NOT_LOGGED_IN` (`Required`), and `AUTHENTICATED` (`Active`).
37
+ - OS-aware installation guides: Auto-detects platform (Windows/macOS/Linux) and suggests native package manager commands (`winget install --id GitHub.cli`, `brew install gh`) with official installer links.
38
+ - Interactive helpers: One-click command copying and security-whitelisted terminal launcher.
39
+ - **Focus Auto-Recovery**: Automatically detects authorization completion when returning to the DSH window, transitioning to full workspace view without manual page reloads.
40
+ 6. **Standard Cordis Microkernel Compliance**:
36
41
  - Exposes native `GitHubService` (`ctx.github`) for ecosystem extensibility;
37
42
  - Strongly typed runtime validation via Standard Schema (`Config`);
38
43
  - Domain event broadcasting (`github/pr:create`, `github/issue:create`).
package/README.md CHANGED
@@ -32,7 +32,12 @@
32
32
  - **全局驾驶舱**(主导航栏 `sidebar.panellist`,Order 20):透视账号全局仓库、本地工作区矩阵、个人待办 PR 与 Issues;
33
33
  - **右侧栏工作区面板**(`sidebarRightTabs`,Order 40):常驻呈现当前仓库的 PR 状态、Checks 门禁药丸与 2x2 黄金快捷网格(浏览器打开、查看 PRs、快速创建 PR、新建 Issue),一键直达官方比对与创建页,零弹窗依赖;
34
34
  - **全场景双模式原生自适应**:深度契合 DSH 官方 DSW(DeepSeek Web)设计变量规范,通过 `body[data-ds-dark-theme]` 驱动浅色与深色主题实时零延迟平滑切换。
35
- 5. **符合 Cordis 官方微内核架构**:
35
+ 5. **健全的环境诊断与三态就绪引导 (Three-State Setup Engine)**:
36
+ - 告别空状态困惑:精准建立「未安装 CLI (`Missing`)」➜「已安装未登录 (`Required`)」➜「已认证就绪 (`Active`)」三态状态机;
37
+ - 自动识别宿主机操作系统(Windows/macOS/Linux),按平台提供包管理器安装命令(Windows 下 `winget install --id GitHub.cli`、macOS 下 `brew install gh`)与官方安装包直达;
38
+ - 支持一键复制命令与安全白名单系统终端一键唤起;
39
+ - 具备**窗口焦点自愈(Focus Auto-Recovery)**机制:切到外部终端或浏览器授权完毕后,切回 DSH 界面自动探测刷新,无感恢复。
40
+ 6. **符合 Cordis 官方微内核架构**:
36
41
  - 暴露原生 `GitHubService`(`ctx.github`),允许生态其他插件复用;
37
42
  - 具备符合 Standard Schema 规范的运行时配置校验(`Config`);
38
43
  - 支持领域事件系统(`github/pr:create`, `github/issue:create`)。
@@ -73,3 +73,9 @@
73
73
  - **服务依赖就绪**:声明 `['tools', 'commands', 'webServer', 'workspaceRegistry']`,确保 Web 路由与命令服务在环境就绪后激活;
74
74
  - **生命周期 Effect 闭环**:所有 HTTP 路由与命令注册均持有 Disposer 清理函数,插件热重载或重新挂载时自动彻底注销,零内存泄漏;
75
75
  - **配置自校验与服务暴露**:基于 Standard Schema 规范实现强类型校验与默认值注入,通过 `ctx.provide('github', executor)` 向生态插件暴露单例服务。
76
+
77
+ ### 2.6 三态环境诊断与交互引导状态机 (Setup & Authentication Lifecycle Guard)
78
+ - **三态状态模型 (Three-State Machine)**:将环境就绪判定严格划分为 `NOT_INSTALLED`(未安装 CLI)、`NOT_LOGGED_IN`(已安装未登录)、`AUTHENTICATED`(已认证)三态,彻底消除“把未安装当作未登录”的状态屏蔽隐患;
79
+ - **平台自适应安装策略**:后端探测到 `ENOENT` 时根据 `process.platform` 组装对应平台的安装命令(Windows: `winget`,macOS: `brew`,Linux: `apt/dnf`)与官方安装包地址;
80
+ - **安全辅助与白名单终端唤起**:Web 端提供一键命令复制,后端 `/api/github/action` 提供 `open_terminal` 安全白名单唤起接口(只允许预设命令),避免在前端模拟复杂交互式 PTY 的脆弱性;
81
+ - **焦点感知自愈 (Focus Auto-Recovery)**:前端挂载 `window.focus` 事件监听,用户在外部浏览器或终端完成认证后切回 DSH 界面,自动静默重新探测并无缝更新 UI,实现零摩擦闭环体验。
package/lib/api/routes.js CHANGED
@@ -212,6 +212,41 @@ export function registerApiRoutes(ctxOrWebServer, executor, workspaceRegistry, c
212
212
  sendJson(res, 200, { ok: true, message: '已在浏览器打开' });
213
213
  break;
214
214
  }
215
+ case 'open_terminal': {
216
+ const command = typeof payload.command === 'string' ? payload.command.trim() : 'gh auth login';
217
+ // 严格白名单过滤,严防任意命令注入
218
+ const allowedCommands = [
219
+ 'gh auth login',
220
+ 'winget install --id GitHub.cli',
221
+ 'brew install gh',
222
+ 'sudo apt install gh',
223
+ 'sudo dnf install gh',
224
+ ];
225
+ if (!allowedCommands.includes(command)) {
226
+ throw new Error(`不支持唤起该指令: ${command}`);
227
+ }
228
+ const { execFile } = await import('node:child_process');
229
+ let bin = 'cmd';
230
+ let args = [];
231
+ if (process.platform === 'win32') {
232
+ bin = 'cmd';
233
+ args = ['/c', 'start', 'powershell', '-NoExit', '-Command', command];
234
+ }
235
+ else if (process.platform === 'darwin') {
236
+ bin = 'osascript';
237
+ args = ['-e', `tell application "Terminal" to do script "${command}"`, '-e', 'tell application "Terminal" to activate'];
238
+ }
239
+ else {
240
+ bin = 'x-terminal-emulator';
241
+ args = ['-e', command];
242
+ }
243
+ execFile(bin, args, (err) => {
244
+ if (err)
245
+ console.warn('[dsh-github-flow] 唤起终端失败:', err?.message || err);
246
+ });
247
+ sendJson(res, 200, { ok: true, message: '已请求唤起系统终端' });
248
+ break;
249
+ }
215
250
  default:
216
251
  sendJson(res, 400, { ok: false, message: `未知的操作类型: ${payload.action}` });
217
252
  }
package/lib/client.js CHANGED
@@ -633,6 +633,110 @@ body[data-ds-dark-theme] .dsh-github-repo-link {
633
633
  body[data-ds-dark-theme] .dsh-github-subheading {
634
634
  color: var(--dsw-alias-label-primary, #f0f6fc);
635
635
  }
636
+
637
+ /* 15. 就绪引导横幅 (Setup Guide Banner) */
638
+ .dsh-github-guide-card {
639
+ padding: 16px;
640
+ border-radius: 8px;
641
+ margin-bottom: 16px;
642
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
643
+ background: var(--dsw-alias-bg-layer-1, #f6f8fa);
644
+ }
645
+ .dsh-github-guide-card.warning {
646
+ border-color: rgba(210, 153, 34, 0.4);
647
+ background: rgba(210, 153, 34, 0.08);
648
+ }
649
+ .dsh-github-guide-card.danger {
650
+ border-color: rgba(248, 81, 73, 0.4);
651
+ background: rgba(248, 81, 73, 0.08);
652
+ }
653
+ .dsh-github-guide-header {
654
+ display: flex;
655
+ align-items: center;
656
+ gap: 8px;
657
+ font-size: 15px;
658
+ font-weight: 600;
659
+ margin-bottom: 6px;
660
+ color: var(--dsw-alias-label-primary, #1f2328);
661
+ }
662
+ body[data-ds-dark-theme] .dsh-github-guide-header {
663
+ color: var(--dsw-alias-label-primary, #f0f6fc);
664
+ }
665
+ .dsh-github-guide-desc {
666
+ font-size: 13px;
667
+ color: var(--dsw-alias-label-secondary, #656d76);
668
+ line-height: 1.5;
669
+ margin-bottom: 12px;
670
+ }
671
+ body[data-ds-dark-theme] .dsh-github-guide-desc {
672
+ color: var(--dsw-alias-label-secondary, #8c8c8c);
673
+ }
674
+ .dsh-github-cmd-box {
675
+ display: flex;
676
+ align-items: center;
677
+ justify-content: space-between;
678
+ background: var(--dsw-alias-bg-base, #ffffff);
679
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
680
+ border-radius: 6px;
681
+ padding: 8px 12px;
682
+ font-family: var(--ds-font-family-code, monospace);
683
+ font-size: 13px;
684
+ color: var(--dsw-alias-label-primary, #1f2328);
685
+ margin-bottom: 12px;
686
+ }
687
+ body[data-ds-dark-theme] .dsh-github-cmd-box {
688
+ background: var(--dsw-alias-bg-layer-2, #161b22);
689
+ border-color: var(--dsw-alias-border-l1, #30363d);
690
+ color: var(--dsw-alias-label-primary, #f0f6fc);
691
+ }
692
+ .dsh-github-copy-btn {
693
+ padding: 4px 10px;
694
+ font-size: 12px;
695
+ border-radius: 4px;
696
+ border: 1px solid var(--dsw-alias-border-l2, #d0d7de);
697
+ background: var(--dsw-alias-bg-layer-1, #f6f8fa);
698
+ color: var(--dsw-alias-label-primary, #1f2328);
699
+ cursor: pointer;
700
+ white-space: nowrap;
701
+ transition: all 0.15s ease;
702
+ }
703
+ body[data-ds-dark-theme] .dsh-github-copy-btn {
704
+ background: var(--dsw-alias-bg-layer-2, #21262d);
705
+ border-color: var(--dsw-alias-border-l1, #30363d);
706
+ color: var(--dsw-alias-label-primary, #c9d1d9);
707
+ }
708
+ .dsh-github-copy-btn:hover {
709
+ background: var(--dsw-alias-interactive-bg-hover, #ebeef1);
710
+ }
711
+ body[data-ds-dark-theme] .dsh-github-copy-btn:hover {
712
+ background: #30363d;
713
+ }
714
+ .dsh-github-copy-btn.copied {
715
+ color: #2da44e !important;
716
+ border-color: #2da44e !important;
717
+ background: rgba(45, 164, 78, 0.15) !important;
718
+ }
719
+ .dsh-github-steps-list {
720
+ font-size: 12px;
721
+ color: var(--dsw-alias-label-secondary, #656d76);
722
+ line-height: 1.6;
723
+ margin: 0 0 12px 18px;
724
+ padding: 0;
725
+ }
726
+ body[data-ds-dark-theme] .dsh-github-steps-list {
727
+ color: var(--dsw-alias-label-secondary, #8c8c8c);
728
+ }
729
+ .dsh-github-guide-actions {
730
+ display: flex;
731
+ gap: 8px;
732
+ flex-wrap: wrap;
733
+ }
734
+ .dsh-github-guide-tip {
735
+ font-size: 11px;
736
+ color: var(--dsw-alias-label-tertiary, #8c8c8c);
737
+ margin-top: 10px;
738
+ line-height: 1.4;
739
+ }
636
740
  `;
637
741
  function injectStyles() {
638
742
  var styleId = "dsh-plugin-github-styles";
@@ -672,6 +776,120 @@ body[data-ds-dark-theme] .dsh-github-subheading {
672
776
  }
673
777
  }, renderOctocat(Math.round(size * 0.58), "#ffffff"));
674
778
  }
779
+ // 认证与安装引导卡片组件(响应式自适应中央大面板与右侧栏)
780
+ function AuthGuideCard(props) {
781
+ var auth = props ? props.auth : null;
782
+ var onRefresh = props ? props.onRefresh : () => { };
783
+ var isRightbar = Boolean(props && props.isRightbar);
784
+ var [copied, setCopied] = React.useState(false);
785
+ var [launching, setLaunching] = React.useState(false);
786
+ var [toastMsg, setToastMsg] = React.useState(null);
787
+ var notInstalled = !auth?.installed;
788
+ var guide = auth?.installGuide;
789
+ var defaultCmd = notInstalled
790
+ ? (guide?.command || "winget install --id GitHub.cli")
791
+ : "gh auth login";
792
+ var copyCmd = (cmd) => {
793
+ try {
794
+ if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
795
+ navigator.clipboard.writeText(cmd);
796
+ }
797
+ else {
798
+ var input = document.createElement("textarea");
799
+ input.value = cmd;
800
+ document.body.appendChild(input);
801
+ input.select();
802
+ document.execCommand("copy");
803
+ document.body.removeChild(input);
804
+ }
805
+ setCopied(true);
806
+ setTimeout(() => setCopied(false), 2000);
807
+ }
808
+ catch (e) {
809
+ console.warn("复制失败:", e);
810
+ }
811
+ };
812
+ var launchTerminal = async (cmd) => {
813
+ setLaunching(true);
814
+ setToastMsg(null);
815
+ try {
816
+ var res = await fetch("/api/github/action", {
817
+ method: "POST",
818
+ headers: { "Content-Type": "application/json" },
819
+ body: JSON.stringify({ action: "open_terminal", command: cmd })
820
+ });
821
+ var json = await res.json();
822
+ if (json.ok) {
823
+ setToastMsg("已唤起系统终端,请在弹出窗口中完成操作");
824
+ setTimeout(() => setToastMsg(null), 4000);
825
+ }
826
+ else {
827
+ setToastMsg(json.message || "唤起失败,请在终端手动运行");
828
+ }
829
+ }
830
+ catch (e) {
831
+ setToastMsg("无法连接后台服务,请在终端手动运行");
832
+ }
833
+ finally {
834
+ setLaunching(false);
835
+ }
836
+ };
837
+ var openDownload = async () => {
838
+ var url = guide?.downloadUrl || "https://cli.github.com/";
839
+ try {
840
+ await fetch("/api/github/action", {
841
+ method: "POST",
842
+ headers: { "Content-Type": "application/json" },
843
+ body: JSON.stringify({ action: "open_browser", url })
844
+ });
845
+ }
846
+ catch (e) {
847
+ window.open(url, "_blank", "noopener,noreferrer");
848
+ }
849
+ };
850
+ var h = React.createElement;
851
+ return h("div", {
852
+ className: "dsh-github-guide-card " + (notInstalled ? "danger" : "warning"),
853
+ style: isRightbar ? { padding: "12px", marginBottom: "12px" } : undefined
854
+ },
855
+ // 头部
856
+ h("div", { className: "dsh-github-guide-header" }, notInstalled ? "⚠️ 未检测到 GitHub CLI (gh)" : "🔑 GitHub 账号尚未登录", h("span", { className: "dsh-github-badge " + (notInstalled ? "dsh-badge-red" : "dsh-badge-yellow") }, notInstalled ? "未安装" : "未认证")),
857
+ // 描述
858
+ h("div", { className: "dsh-github-guide-desc" }, notInstalled
859
+ ? (guide?.description || "DSH GitHub Flow 插件依赖官方 GitHub CLI 工具与云端进行安全交互,当前系统 PATH 中未检测到该工具。")
860
+ : "检测到本机已安装 GitHub CLI,请完成账号授权以解锁仓库管理、PR 审查和 Issues 待办。"),
861
+ // 命令代码展示框
862
+ h("div", { className: "dsh-github-cmd-box" }, h("code", null, defaultCmd), h("button", {
863
+ className: "dsh-github-copy-btn " + (copied ? "copied" : ""),
864
+ onClick: () => copyCmd(defaultCmd),
865
+ title: "复制命令到剪贴板"
866
+ }, copied ? "✓ 已复制" : "📋 复制命令")),
867
+ // 步骤提示
868
+ !notInstalled && !isRightbar
869
+ ? h("ol", { className: "dsh-github-steps-list" }, h("li", null, "打开系统终端(PowerShell / CMD / Terminal),运行上述命令;"), h("li", null, "按提示依次选择「GitHub.com」➜「HTTPS」➜「Login with a web browser」;"), h("li", null, "按回车打开浏览器确认授权后,切回本页面即可自动恢复。"))
870
+ : null,
871
+ // 操作按钮区域
872
+ h("div", { className: "dsh-github-guide-actions" }, h("button", {
873
+ className: "dsh-github-action-btn",
874
+ onClick: () => launchTerminal(defaultCmd),
875
+ disabled: launching,
876
+ title: "自动唤起系统终端并执行该命令"
877
+ }, launching ? "正在唤起..." : (notInstalled ? "💻 唤起终端安装" : "💻 唤起终端登录")), notInstalled
878
+ ? h("button", {
879
+ className: "dsh-github-action-btn",
880
+ onClick: openDownload,
881
+ title: "打开 GitHub CLI 官方下载主页获取安装包"
882
+ }, "🌐 官网安装包 (.msi/.pkg)")
883
+ : null, h("button", {
884
+ className: "dsh-github-action-btn",
885
+ onClick: onRefresh,
886
+ title: "重新检测登录与安装状态"
887
+ }, "🔄 刷新状态")), toastMsg
888
+ ? h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-link, #0969da)", marginTop: "8px" } }, `ℹ️ ${toastMsg}`)
889
+ : null, h("div", { className: "dsh-github-guide-tip" }, notInstalled
890
+ ? "💡 提示:使用包管理器安装完成后,若未立即识别,可重启 DeepSeek Harness 应用刷新系统环境变量 PATH。"
891
+ : "💡 提示:凭证由官方 GitHub CLI 加密保存在操作系统安全凭据库中,授权完毕切回本窗口将自动识别刷新。"));
892
+ }
675
893
  // 渲染 GitHub 仓库信息与操作视图的核心函数
676
894
  function renderGithubRepoView(h, data, loading, error, onRefresh, isRightbar = false) {
677
895
  var auth = data ? data.auth : null;
@@ -749,7 +967,10 @@ body[data-ds-dark-theme] .dsh-github-subheading {
749
967
  : r.conclusion === "failure"
750
968
  ? h("span", { className: "dsh-github-badge dsh-badge-red" }, "✕ 失败")
751
969
  : h("span", { className: "dsh-github-badge dsh-badge-yellow" }, r.status))))));
752
- return [headerEl, repoCardEl, actionsBarEl, prSectionEl, runSectionEl];
970
+ var authGuideEl = !auth?.loggedIn
971
+ ? h(AuthGuideCard, { auth, onRefresh, isRightbar })
972
+ : null;
973
+ return [headerEl, authGuideEl, repoCardEl, actionsBarEl, prSectionEl, runSectionEl].filter(Boolean);
753
974
  }
754
975
  // 1. 右侧栏专属视图 (Right Sidebar Tab Body)
755
976
  function GithubRightbarBody(props) {
@@ -806,6 +1027,16 @@ body[data-ds-dark-theme] .dsh-github-subheading {
806
1027
  React.useEffect(() => {
807
1028
  loadData(false);
808
1029
  }, [loadData]);
1030
+ // 窗口切回自动探测自愈
1031
+ React.useEffect(() => {
1032
+ var onFocus = () => {
1033
+ if (!data?.auth?.loggedIn) {
1034
+ loadData(false);
1035
+ }
1036
+ };
1037
+ window.addEventListener("focus", onFocus);
1038
+ return () => window.removeEventListener("focus", onFocus);
1039
+ }, [data, loadData]);
809
1040
  var h = React.createElement;
810
1041
  return h("div", { className: "dsh-github-rightbar-container" }, ...renderGithubRepoView(h, data, loading, error, () => loadData(true), true));
811
1042
  }
@@ -878,6 +1109,16 @@ body[data-ds-dark-theme] .dsh-github-subheading {
878
1109
  }
879
1110
  }, []);
880
1111
  React.useEffect(() => { loadData(false); }, [loadData]);
1112
+ // 窗口切回自动探测自愈
1113
+ React.useEffect(() => {
1114
+ var onFocus = () => {
1115
+ if (!data?.auth?.loggedIn) {
1116
+ loadData(false);
1117
+ }
1118
+ };
1119
+ window.addEventListener("focus", onFocus);
1120
+ return () => window.removeEventListener("focus", onFocus);
1121
+ }, [data, loadData]);
881
1122
  var h = React.createElement;
882
1123
  var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱")), h("button", {
883
1124
  className: "dsh-github-refresh-btn",
@@ -894,7 +1135,19 @@ body[data-ds-dark-theme] .dsh-github-subheading {
894
1135
  var myIssues = (data && data.myIssues) || [];
895
1136
  var errorEl = error ? h("div", { style: { color: "#f85149", background: "rgba(248,81,73,0.1)", border: "1px solid rgba(248,81,73,0.3)", padding: "8px 12px", borderRadius: "6px", marginBottom: "16px", fontSize: "12px" } }, `⚠️ 加载失败: ${error}`) : null;
896
1137
  // 顶部统计卡片
897
- var statsCardsEl = h("div", { className: "dsh-github-overview-cards" }, h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "CLI 认证账号"), h("div", { className: "dsh-github-card-value" }, auth?.loggedIn ? `👤 ${auth.user}` : "未登录", auth?.loggedIn ? h("span", { className: "dsh-github-badge dsh-badge-green" }, "Active") : null)), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "账号云端仓库"), h("div", { className: "dsh-github-card-value" }, `${userRepos.length} 个 Repositories`)), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "本地工作区联动"), h("div", { className: "dsh-github-card-value" }, `${workspaceMatrix.length} 个工作区 (${workspaceMatrix.filter((w) => w.repo).length} 个已关联)`)));
1138
+ var statsCardsEl = h("div", { className: "dsh-github-overview-cards" }, h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "CLI 认证账号"), h("div", { className: "dsh-github-card-value" }, auth?.loggedIn
1139
+ ? `👤 ${auth.user}`
1140
+ : !auth?.installed
1141
+ ? "❌ 未安装 CLI"
1142
+ : "⚠️ 未登录", auth?.loggedIn
1143
+ ? h("span", { className: "dsh-github-badge dsh-badge-green" }, "Active")
1144
+ : !auth?.installed
1145
+ ? h("span", { className: "dsh-github-badge dsh-badge-red" }, "Missing")
1146
+ : h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Required"))), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "账号云端仓库"), h("div", { className: "dsh-github-card-value" }, `${userRepos.length} 个 Repositories`)), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "本地工作区联动"), h("div", { className: "dsh-github-card-value" }, `${workspaceMatrix.length} 个工作区 (${workspaceMatrix.filter((w) => w.repo).length} 个已关联)`)));
1147
+ // 安装与登录引导组件(未登录或未安装时显式展开)
1148
+ var authGuideEl = !auth?.loggedIn
1149
+ ? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
1150
+ : null;
898
1151
  // Tab 栏
899
1152
  var tabsEl = h("div", { className: "dsh-github-tabs" }, h("button", { className: `dsh-github-tab-btn ${activeTab === 'workspaces' ? 'active' : ''}`, onClick: () => setActiveTab('workspaces') }, `📂 本地工作区矩阵 (${workspaceMatrix.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'repos' ? 'active' : ''}`, onClick: () => setActiveTab('repos') }, `☁️ 我的 GitHub 仓库 (${userRepos.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'mywork' ? 'active' : ''}`, onClick: () => setActiveTab('mywork') }, `📋 个人待办 (PR: ${myPrs.length} · Issue: ${myIssues.length})`));
900
1153
  // 内容区
@@ -907,18 +1160,28 @@ body[data-ds-dark-theme] .dsh-github-subheading {
907
1160
  : null))));
908
1161
  }
909
1162
  else if (activeTab === 'repos') {
910
- var filteredRepos = userRepos.filter((r) => !searchQuery || r.nameWithOwner.toLowerCase().includes(searchQuery.toLowerCase()) || r.description?.toLowerCase().includes(searchQuery.toLowerCase()));
911
- contentEl = h("div", null, h("input", {
912
- className: "dsh-github-search-input",
913
- placeholder: "🔍 搜索我的 GitHub 仓库...",
914
- value: searchQuery,
915
- onChange: (e) => setSearchQuery(e.target.value)
916
- }), h("div", { className: "dsh-github-list" }, filteredRepos.map((r) => h("div", { key: r.nameWithOwner, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: r.url, target: "_blank" }, r.nameWithOwner), r.description ? h("div", { className: "dsh-github-item-desc" }, r.description) : null, h("div", { className: "dsh-github-item-meta" }, h("span", null, `默认分支: ${r.defaultBranch}`), r.isPrivate ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Private") : h("span", { className: "dsh-github-badge dsh-badge-green" }, "Public"), h("span", null, `⭐ ${r.stargazerCount}`))))));
1163
+ if (!auth?.loggedIn) {
1164
+ contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取云端仓库列表。请先完成上方的安装或登录指引。");
1165
+ }
1166
+ else {
1167
+ var filteredRepos = userRepos.filter((r) => !searchQuery || r.nameWithOwner.toLowerCase().includes(searchQuery.toLowerCase()) || r.description?.toLowerCase().includes(searchQuery.toLowerCase()));
1168
+ contentEl = h("div", null, h("input", {
1169
+ className: "dsh-github-search-input",
1170
+ placeholder: "🔍 搜索我的 GitHub 仓库...",
1171
+ value: searchQuery,
1172
+ onChange: (e) => setSearchQuery(e.target.value)
1173
+ }), h("div", { className: "dsh-github-list" }, filteredRepos.map((r) => h("div", { key: r.nameWithOwner, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: r.url, target: "_blank" }, r.nameWithOwner), r.description ? h("div", { className: "dsh-github-item-desc" }, r.description) : null, h("div", { className: "dsh-github-item-meta" }, h("span", null, `默认分支: ${r.defaultBranch}`), r.isPrivate ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Private") : h("span", { className: "dsh-github-badge dsh-badge-green" }, "Public"), h("span", null, `⭐ ${r.stargazerCount}`))))));
1174
+ }
917
1175
  }
918
1176
  else {
919
- contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起的 PR (${myPrs.length})`), myPrs.length === 0 ? h("div", { className: "dsh-github-empty", style: { marginBottom: "16px" } }, "暂无开放中的 PR") : h("div", { className: "dsh-github-list", style: { marginBottom: "16px" } }, myPrs.map((pr) => h("div", { key: pr.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`)))), h("h3", { className: "dsh-github-subheading" }, `📝 我的待办 Issues (${myIssues.length})`), myIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, myIssues.map((is) => h("div", { key: is.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: is.url, target: "_blank" }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`)))));
1177
+ if (!auth?.loggedIn) {
1178
+ contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人待办 PR 与 Issues。请先完成上方的安装或登录指引。");
1179
+ }
1180
+ else {
1181
+ contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起的 PR (${myPrs.length})`), myPrs.length === 0 ? h("div", { className: "dsh-github-empty", style: { marginBottom: "16px" } }, "暂无开放中的 PR") : h("div", { className: "dsh-github-list", style: { marginBottom: "16px" } }, myPrs.map((pr) => h("div", { key: pr.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: pr.url, target: "_blank" }, `[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`)))), h("h3", { className: "dsh-github-subheading" }, `📝 我的待办 Issues (${myIssues.length})`), myIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, myIssues.map((is) => h("div", { key: is.number, className: "dsh-github-item" }, h("a", { className: "dsh-github-item-title", href: is.url, target: "_blank" }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`)))));
1182
+ }
920
1183
  }
921
- return h("div", { className: "dsh-github-panel" }, headerEl, errorEl, statsCardsEl, tabsEl, contentEl);
1184
+ return h("div", { className: "dsh-github-panel" }, headerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl);
922
1185
  }
923
1186
  var inject = ["slots", "sidebarRightTabs", "sessions", "layout", "theme"];
924
1187
  function apply(ctx) {
package/lib/executor.d.ts CHANGED
@@ -39,6 +39,8 @@ export declare class GhExecutor {
39
39
  * 而 Web 面板每次挂载都会来问一次,重复执行实测要 5.4 秒,属于纯浪费。
40
40
  */
41
41
  checkAuth(cwd?: string, bypassCache?: boolean): Promise<AuthStatus>;
42
+ /** 生成当前操作系统平台的推荐安装引导与命令 */
43
+ private getPlatformInstallGuide;
42
44
  /** 实际执行 `gh auth status` 并解析结果,不含缓存逻辑 */
43
45
  private probeAuth;
44
46
  /**
package/lib/executor.js CHANGED
@@ -222,19 +222,46 @@ export class GhExecutor {
222
222
  }
223
223
  return value;
224
224
  }
225
+ /** 生成当前操作系统平台的推荐安装引导与命令 */
226
+ getPlatformInstallGuide() {
227
+ const p = process.platform;
228
+ if (p === 'win32') {
229
+ return {
230
+ command: 'winget install --id GitHub.cli',
231
+ altCommand: 'scoop install gh',
232
+ downloadUrl: 'https://cli.github.com/',
233
+ description: '推荐使用 Windows Package Manager (winget) 一键安装,或下载官方 .msi 安装包。',
234
+ };
235
+ }
236
+ if (p === 'darwin') {
237
+ return {
238
+ command: 'brew install gh',
239
+ altCommand: 'port install gh',
240
+ downloadUrl: 'https://cli.github.com/',
241
+ description: '推荐使用 Homebrew 安装 GitHub CLI。',
242
+ };
243
+ }
244
+ return {
245
+ command: 'sudo apt install gh',
246
+ altCommand: 'sudo dnf install gh',
247
+ downloadUrl: 'https://cli.github.com/',
248
+ description: '推荐使用系统包管理器安装(Debian/Ubuntu 运行 apt,Fedora/CentOS 运行 dnf)。',
249
+ };
250
+ }
225
251
  /** 实际执行 `gh auth status` 并解析结果,不含缓存逻辑 */
226
252
  async probeAuth(cwd) {
227
253
  const res = await this.run(['auth', 'status'], { cwd, timeoutMs: 10_000, rawText: true });
228
254
  const raw = (res.rawOutput || '') + '\n' + (res.error || '');
229
255
  const loggedIn = raw.includes('Logged in to') || raw.includes('✓');
230
256
  // 只要已登录或有常规输出,则必然安装了 gh
231
- const isNotInstalled = Boolean(res.error && res.error.includes('系统未检测到 GitHub CLI'));
257
+ const isNotInstalled = Boolean(res.error && (res.error.includes('系统未检测到 GitHub CLI') || res.error.includes('ENOENT')));
232
258
  const installed = loggedIn || !isNotInstalled;
233
259
  if (!installed) {
234
260
  return {
235
261
  installed: false,
236
262
  loggedIn: false,
237
263
  platform: process.platform,
264
+ installGuide: this.getPlatformInstallGuide(),
238
265
  raw: res.error,
239
266
  };
240
267
  }
package/lib/types.d.ts CHANGED
@@ -33,6 +33,16 @@ export interface GhResult<T = unknown> {
33
33
  /** 是否因超出安全阈值而被截断 */
34
34
  truncated?: boolean;
35
35
  }
36
+ export interface AuthInstallGuide {
37
+ /** 推荐包管理器命令,例如 Windows 下 "winget install --id GitHub.cli" */
38
+ command: string;
39
+ /** 备选命令(如 choco/scoop/brew) */
40
+ altCommand?: string;
41
+ /** 官方下载与安装文档链接 */
42
+ downloadUrl: string;
43
+ /** 简明指引提示 */
44
+ description?: string;
45
+ }
36
46
  export interface AuthStatus {
37
47
  installed: boolean;
38
48
  loggedIn: boolean;
@@ -42,6 +52,7 @@ export interface AuthStatus {
42
52
  activeAccount?: boolean;
43
53
  protocol?: string;
44
54
  scopes?: string[];
55
+ installGuide?: AuthInstallGuide;
45
56
  raw?: string;
46
57
  }
47
58
  export interface RepoMetadata {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-github-flow",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "icon": "icon.svg",
5
5
  "description": "GitHub Flow: Native GitHub CLI (gh) integration for DeepSeek Harness — Agent Tools, Slash Commands, and Web UI Workspace",
6
6
  "type": "module",