dsh-github-flow 0.4.0 → 0.6.0

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.
Files changed (82) hide show
  1. package/README.en.md +91 -12
  2. package/README.md +91 -12
  3. package/docs/UPGRADE-NOTES.md +9 -12
  4. package/docs/architecture.md +63 -29
  5. package/docs/images/1.png +0 -0
  6. package/lib/api/routes-actions.js +268 -5
  7. package/lib/api/routes-overview.js +147 -29
  8. package/lib/cache.d.ts +77 -4
  9. package/lib/cache.js +114 -7
  10. package/lib/client.d.ts +9 -4
  11. package/lib/client.js +5293 -942
  12. package/lib/commands/gh.d.ts +6 -1
  13. package/lib/commands/gh.js +144 -71
  14. package/lib/executor/error-classifier.d.ts +55 -0
  15. package/lib/executor/error-classifier.js +199 -0
  16. package/lib/executor/git-parser.d.ts +157 -1
  17. package/lib/executor/git-parser.js +513 -4
  18. package/lib/executor.d.ts +147 -4
  19. package/lib/executor.js +866 -77
  20. package/lib/main.js +28 -4
  21. package/lib/renderers/agent-task.d.ts +16 -0
  22. package/lib/renderers/agent-task.js +107 -0
  23. package/lib/renderers/codespace.d.ts +16 -0
  24. package/lib/renderers/codespace.js +103 -0
  25. package/lib/renderers/discussion.d.ts +16 -0
  26. package/lib/renderers/discussion.js +101 -0
  27. package/lib/renderers/markdown.d.ts +4 -0
  28. package/lib/renderers/markdown.js +4 -0
  29. package/lib/renderers/project.d.ts +20 -0
  30. package/lib/renderers/project.js +128 -0
  31. package/lib/renderers/skill.d.ts +30 -0
  32. package/lib/renderers/skill.js +180 -0
  33. package/lib/renderers/status.d.ts +5 -0
  34. package/lib/renderers/status.js +52 -0
  35. package/lib/telemetry.d.ts +70 -0
  36. package/lib/telemetry.js +161 -0
  37. package/lib/tools/agent-task.d.ts +54 -0
  38. package/lib/tools/agent-task.js +135 -0
  39. package/lib/tools/codespace.d.ts +51 -0
  40. package/lib/tools/codespace.js +154 -0
  41. package/lib/tools/discussion.d.ts +70 -0
  42. package/lib/tools/discussion.js +195 -0
  43. package/lib/tools/help.js +129 -14
  44. package/lib/tools/image-utils.d.ts +27 -0
  45. package/lib/tools/image-utils.js +73 -3
  46. package/lib/tools/project.d.ts +80 -0
  47. package/lib/tools/project.js +241 -0
  48. package/lib/tools/repo.js +13 -1
  49. package/lib/tools/skill.d.ts +24 -1
  50. package/lib/tools/skill.js +115 -5
  51. package/lib/tools/status.d.ts +3 -2
  52. package/lib/tools/status.js +9 -4
  53. package/lib/types.d.ts +336 -0
  54. package/lib/ui/commit-modal.js +144 -14
  55. package/lib/ui/help-modal.d.ts +21 -0
  56. package/lib/ui/help-modal.js +300 -0
  57. package/lib/ui/issue-modal.d.ts +14 -0
  58. package/lib/ui/issue-modal.js +366 -0
  59. package/lib/ui/markdown-lite.d.ts +66 -0
  60. package/lib/ui/markdown-lite.js +312 -0
  61. package/lib/ui/network-banner.d.ts +12 -0
  62. package/lib/ui/network-banner.js +35 -0
  63. package/lib/ui/pr-modal.d.ts +17 -0
  64. package/lib/ui/pr-modal.js +516 -0
  65. package/lib/ui/repo-badges.d.ts +43 -0
  66. package/lib/ui/repo-badges.js +82 -0
  67. package/lib/ui/repo-selector.d.ts +14 -0
  68. package/lib/ui/repo-selector.js +112 -0
  69. package/lib/ui/storage.d.ts +56 -0
  70. package/lib/ui/storage.js +163 -0
  71. package/lib/ui/styles.d.ts +1 -1
  72. package/lib/ui/styles.js +1219 -0
  73. package/lib/ui/sync-settings-modal.js +1 -1
  74. package/lib/ui/upgrade-card.js +39 -14
  75. package/lib/ui/utils.d.ts +35 -0
  76. package/lib/ui/utils.js +172 -0
  77. package/lib/ui/workspace-panel.js +198 -31
  78. package/lib/upgrade-manager.d.ts +5 -1
  79. package/lib/upgrade-manager.js +24 -1
  80. package/lib/version.d.ts +1 -1
  81. package/lib/version.js +1 -1
  82. package/package.json +2 -2
@@ -11,12 +11,21 @@ export function GithubWorkspacePanel() {
11
11
  var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
12
12
  var [isSettingsModalOpen, setIsSettingsModalOpen] = React.useState(false);
13
13
  var [settingsInitialSection, setSettingsInitialSection] = React.useState("sync");
14
+ var [isHelpModalOpen, setIsHelpModalOpen] = React.useState(false);
14
15
  var [error, setError] = React.useState(null);
15
16
  var [updateInfo, setUpdateInfo] = React.useState(null);
16
17
  var [activeTab, setActiveTab] = React.useState("workspaces");
17
18
  var [searchQuery, setSearchQuery] = React.useState("");
18
19
  var [issueFilter, setIssueFilter] = React.useState("all");
20
+ // PR 状态筛选:默认「全部」而不是「Open」。
21
+ // 实测账号可能零 open PR(当前账号 7 个 PR 全为 merged/closed),
22
+ // 默认 Open 会让列表恒为空白,等于没修「看不到数据」这个原始问题。
23
+ var [prStateFilter, setPrStateFilter] = React.useState("all");
19
24
  var [toastMsg, setToastMsg] = React.useState(null);
25
+ // PR 在线审阅弹窗状态:{ number, repo } 二元组;为 null 时弹窗不渲染
26
+ var [activePrModal, setActivePrModal] = React.useState(null);
27
+ // Issue 在线查看弹窗状态:与 PR 完全同构的 { number, repo } 二元组
28
+ var [activeIssueModal, setActiveIssueModal] = React.useState(null);
20
29
  var cloudTimerRef = React.useRef(null);
21
30
  var dataRef = React.useRef(data);
22
31
  dataRef.current = data;
@@ -104,9 +113,18 @@ export function GithubWorkspacePanel() {
104
113
  if (forceRefresh) {
105
114
  url += "?force=true";
106
115
  }
107
- var res = await fetch(url, { signal: controller.signal });
116
+ // 接入 1s/2s/4s 阶梯退避重试,平滑吸收代理节点与网络的偶发抖动断流
117
+ var { res, json } = await fetchWithRetry(url, {}, {
118
+ signal: controller.signal,
119
+ maxRetries: 3,
120
+ baseDelayMs: 1000,
121
+ onRetry: (attempt) => {
122
+ setSyncStatus("syncing");
123
+ setError(`网络抖动,正在自愈重试 (${attempt}/3)...`);
124
+ },
125
+ });
108
126
  if (!res.ok) {
109
- var errText = await res.text().catch(() => "");
127
+ var errText = (typeof json === "string" ? json : JSON.stringify(json)) || res.statusText;
110
128
  var failMsg = `获取全局数据失败(HTTP ${res.status})${errText ? ": " + errText : ""}`;
111
129
  setSyncStatus("failed");
112
130
  setError(failMsg);
@@ -114,7 +132,6 @@ export function GithubWorkspacePanel() {
114
132
  showToast("❌ 刷新失败:" + failMsg);
115
133
  return;
116
134
  }
117
- var json = await res.json();
118
135
  if (json.ok && json.data) {
119
136
  var isFail = Boolean(json.partialFailure ||
120
137
  json.data.partialFailure ||
@@ -241,8 +258,16 @@ export function GithubWorkspacePanel() {
241
258
  .catch(function () { });
242
259
  }
243
260
  };
261
+ var onOnline = () => {
262
+ // 监听网络由断网恢复连接瞬间,自动静默发起一次全量同步,消除离线警告横幅并拉新
263
+ loadData(false);
264
+ };
244
265
  window.addEventListener("focus", onFocus);
245
- return () => window.removeEventListener("focus", onFocus);
266
+ window.addEventListener("online", onOnline);
267
+ return () => {
268
+ window.removeEventListener("focus", onFocus);
269
+ window.removeEventListener("online", onOnline);
270
+ };
246
271
  }, [data, loadData]);
247
272
  var h = React.createElement;
248
273
  var headerEl = h("div", { className: "dsh-github-header" }, h("div", { className: "dsh-github-title" }, renderOctocatBadge(32), h("h2", null, "GitHub 全景驾驶舱"), renderPluginBadge(h), h(SyncStatusBadge, { syncStatus, lastSyncedAt }), proxyActive && proxyActive.enabled
@@ -267,6 +292,11 @@ export function GithubWorkspacePanel() {
267
292
  onClick: () => loadData(true),
268
293
  disabled: loading,
269
294
  }, loading ? "同步中..." : "🔄 刷新全局数据"), h("button", {
295
+ className: "dsh-github-btn-sm",
296
+ onClick: () => setIsHelpModalOpen(true),
297
+ title: "打开使用帮助与实战手册大窗口(Esc 可退出)",
298
+ style: { padding: "6px 12px" },
299
+ }, "📖 使用指南"), h("button", {
270
300
  className: "dsh-github-btn-sm",
271
301
  onClick: openSettingsDefault,
272
302
  title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
@@ -336,7 +366,7 @@ export function GithubWorkspacePanel() {
336
366
  var authGuideEl = (!auth?.loggedIn && !auth?.networkError && syncStatus !== "failed")
337
367
  ? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
338
368
  : null;
339
- 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})`));
369
+ 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 === 'prs' ? 'active' : ''}`, onClick: () => setActiveTab('prs') }, `🔀 Pull Requests (${myPrs.length})`), h("button", { className: `dsh-github-tab-btn ${activeTab === 'issues' ? 'active' : ''}`, onClick: () => setActiveTab('issues') }, `📝 任务与待办 Issues (${myIssues.length})`));
340
370
  var contentEl = null;
341
371
  if (activeTab === 'workspaces') {
342
372
  contentEl = h("div", { className: "dsh-github-list" }, workspaceMatrix.map((ws) => h("div", { key: ws.id, className: "dsh-github-card dsh-github-workspace-card" }, h("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, h("div", { className: "dsh-github-ws-title" }, ws.title), h("div", { className: "dsh-github-ws-path" }, ws.path), h("div", { style: { fontSize: "12px", marginTop: "4px", display: "flex", gap: "8px", alignItems: "center" } }, ws.repo
@@ -374,6 +404,92 @@ export function GithubWorkspacePanel() {
374
404
  : null)))));
375
405
  }
376
406
  }
407
+ else if (activeTab === 'prs') {
408
+ // ---- PR 独立标签页:完整大屏空间,点击卡片在线唤起审阅工作台 ----
409
+ if (!auth?.loggedIn) {
410
+ contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人 PR 列表。请先完成上方的安装或登录指引。");
411
+ }
412
+ else {
413
+ // 状态分组统计(供胶囊计数与空态判断)
414
+ var prByState = { open: 0, merged: 0, closed: 0 };
415
+ myPrs.forEach(function (pr) {
416
+ var s = String(pr.state || '').toLowerCase();
417
+ if (s === 'open' || s === 'merged' || s === 'closed')
418
+ prByState[s]++;
419
+ });
420
+ var filteredPrs = myPrs.filter(function (pr) {
421
+ if (prStateFilter === 'all')
422
+ return true;
423
+ return String(pr.state || '').toLowerCase() === prStateFilter;
424
+ });
425
+ var prPillsEl = h("div", { className: "dsh-github-pills" }, h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'all' ? 'active' : ''}`, onClick: () => setPrStateFilter('all') }, `全部 (${myPrs.length})`), h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'open' ? 'active' : ''}`, onClick: () => setPrStateFilter('open') }, `🟢 Open (${prByState.open})`), h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'merged' ? 'active' : ''}`, onClick: () => setPrStateFilter('merged') }, `🟣 Merged (${prByState.merged})`), h("button", { className: `dsh-github-pill-btn ${prStateFilter === 'closed' ? 'active' : ''}`, onClick: () => setPrStateFilter('closed') }, `⚪ Closed (${prByState.closed})`));
426
+ // 空态必须区分「账号确实没有」与「当前筛选下没有」——否则用户会把筛空当成数据丢失
427
+ var prEmptyEl = myPrs.length === 0
428
+ ? h("div", { className: "dsh-github-empty" }, "该账号下暂无任何 Pull Request 记录")
429
+ : h("div", { className: "dsh-github-empty" }, `当前筛选「${prStateFilter}」下无结果,共 ${myPrs.length} 条记录(切换到「全部」查看)`);
430
+ contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `🔀 我发起与参与的 Pull Requests (${filteredPrs.length} / ${myPrs.length})`), prPillsEl,
431
+ // 触顶提示:让用户知道列表被截断了,而不是以为历史 PR 凭空消失
432
+ data && data.prsTruncated
433
+ ? h("div", {
434
+ style: {
435
+ margin: "8px 0",
436
+ padding: "6px 12px",
437
+ borderRadius: "6px",
438
+ fontSize: "11px",
439
+ lineHeight: 1.5,
440
+ background: "rgba(210, 153, 34, 0.12)",
441
+ border: "1px solid rgba(210, 153, 34, 0.35)",
442
+ color: "var(--dsw-alias-state-attention-primary, #9a6700)",
443
+ },
444
+ }, `⚠️ 仅显示最近 ${myPrs.length} 条 PR(已触及检索上限)。更早的记录请到 GitHub 查看。`)
445
+ : null, filteredPrs.length === 0 ? prEmptyEl : h("div", { className: "dsh-github-list" }, filteredPrs.map((pr) => {
446
+ // 点击卡片主体 → 在线审阅弹窗
447
+ return h("div", {
448
+ key: pr.number,
449
+ className: "dsh-github-item",
450
+ /*
451
+ * 只加 cursor,绝不覆盖 display/alignItems/flexWrap。
452
+ * 这里的 inline style 曾写成 display:flex + alignItems:center + flexWrap:wrap,
453
+ * 直接把 .dsh-github-item 的 flex-direction:column 顶掉,
454
+ * 导致标题、状态徽章、时间全部横向挤成一行并整体居中——
455
+ * 卡片布局就是被这一行内联样式毁掉的。
456
+ */
457
+ style: { cursor: "pointer" },
458
+ title: "点击在线审阅该 PR(描述 / 评论 / 文件 Diff)",
459
+ onClick: () => {
460
+ setActivePrModal({
461
+ number: Number(pr.number),
462
+ repo: pr.repository?.nameWithOwner || '',
463
+ });
464
+ },
465
+ },
466
+ // 标题行:标题 → 状态徽章 → GitHub 跳转按钮,全部同一行
467
+ h("div", { className: "dsh-github-pr-title-row" }, h("span", { className: "dsh-github-item-title", style: { cursor: "pointer" } }, `${pr.isDraft ? "⚪ " : ""}[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`), pr.isDraft ? h("span", { className: "dsh-github-badge dsh-badge-yellow", style: { flexShrink: 0 } }, "DRAFT") : null,
468
+ // 状态徽章:列表含 merged/closed,必须标明才能一眼区分
469
+ (() => {
470
+ var s = String(pr.state || '').toUpperCase();
471
+ if (s === 'MERGED')
472
+ return h("span", { className: "dsh-github-badge dsh-badge-purple", style: { flexShrink: 0 } }, "MERGED");
473
+ if (s === 'CLOSED')
474
+ return h("span", { className: "dsh-github-badge dsh-badge-red", style: { flexShrink: 0 } }, "CLOSED");
475
+ if (s === 'OPEN')
476
+ return h("span", { className: "dsh-github-badge dsh-badge-green", style: { flexShrink: 0 } }, "OPEN");
477
+ return null;
478
+ })(), h("a", {
479
+ className: "dsh-github-pr-gh-btn",
480
+ href: pr.url,
481
+ target: "_blank",
482
+ rel: "noopener noreferrer",
483
+ onClick: (e) => e.stopPropagation(),
484
+ title: "在浏览器打开该 PR(不进入在线审阅)",
485
+ }, "GitHub ↗")),
486
+ // 第二行只保留时间,避免与标题行的徽章重复
487
+ formatRelativeTime(pr.updatedAt)
488
+ ? h("div", { className: "dsh-github-item-meta" }, h("span", { style: { whiteSpace: "nowrap" } }, formatRelativeTime(pr.updatedAt)))
489
+ : null);
490
+ })));
491
+ }
492
+ }
377
493
  else {
378
494
  if (!auth?.loggedIn) {
379
495
  contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人待办 PR 与 Issues。请先完成上方的安装或登录指引。");
@@ -404,31 +520,61 @@ export function GithubWorkspacePanel() {
404
520
  className: `dsh-github-pill-btn ${issueFilter === 'created' ? 'active' : ''}`,
405
521
  onClick: () => setIssueFilter('created'),
406
522
  }, `✍️ 我创建的 (${issueStats.created || 0})`));
407
- 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", style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" } }, h("a", {
408
- className: "dsh-github-item-title",
409
- href: pr.url,
410
- target: "_blank",
411
- title: pr.updatedAt ? `更新于 ${pr.updatedAt}` : undefined,
412
- }, `${pr.isDraft ? "⚪ " : ""}[${pr.repository?.nameWithOwner || 'Repo'}] #${pr.number} ${pr.title}`), pr.isDraft ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "DRAFT") : null, formatRelativeTime(pr.updatedAt)
413
- ? h("span", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", whiteSpace: "nowrap" } }, formatRelativeTime(pr.updatedAt))
414
- : null))), h("h3", { className: "dsh-github-subheading" }, `📝 任务与待办 Issues (${filteredIssues.length})`), issuePillsEl, filteredIssues.length === 0 ? h("div", { className: "dsh-github-empty" }, "当前分类下暂无开放中的 Issue") : h("div", { className: "dsh-github-list" }, filteredIssues.map((is) => {
415
- var cats = is.categories || [];
416
- var badges = [];
417
- if (cats.includes("reported")) {
418
- badges.push(h("span", { key: "rep", className: "dsh-github-badge dsh-badge-purple" }, "用户提单"));
419
- }
420
- if (cats.includes("assigned")) {
421
- badges.push(h("span", { key: "asn", className: "dsh-github-badge dsh-badge-blue" }, "分配待办"));
422
- }
423
- if (cats.includes("created")) {
424
- badges.push(h("span", { key: "crt", className: "dsh-github-badge dsh-badge-green" }, "我发起的"));
425
- }
426
- return h("div", { key: is.url || is.number, className: "dsh-github-item" }, h("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" } }, h("a", { className: "dsh-github-item-title", href: is.url, target: "_blank" }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`), ...badges), h("div", { className: "dsh-github-item-meta" }, is.author ? h("span", null, `由 @${is.author.login} 提出`) : null, is.assignees && is.assignees.length > 0
427
- ? h("span", null, `指派: ${is.assignees.map((a) => '@' + a.login).join(', ')}`)
428
- : null, is.labels && is.labels.length > 0
429
- ? is.labels.map((lbl) => h("span", { key: lbl.name, className: "dsh-github-badge dsh-badge-yellow" }, lbl.name))
430
- : null, is.updatedAt ? h("span", null, `更新于: ${new Date(is.updatedAt).toLocaleDateString()}`) : null));
431
- })));
523
+ contentEl = h("div", null, h("h3", { className: "dsh-github-subheading" }, `📝 我发起与参与的 Issues (${filteredIssues.length} / ${myIssues.length})`), issuePillsEl, filteredIssues.length === 0
524
+ ? (myIssues.length === 0
525
+ ? h("div", { className: "dsh-github-empty" }, "该账号下暂无任何 Issue 记录")
526
+ : h("div", { className: "dsh-github-empty" }, `当前分类「${issueFilter}」下无结果,共 ${myIssues.length} 条记录(切换到「全部」查看)`))
527
+ : h("div", { className: "dsh-github-list" }, filteredIssues.map((is) => {
528
+ var cats = is.categories || [];
529
+ var badges = [];
530
+ if (cats.includes("reported")) {
531
+ badges.push(h("span", { key: "rep", className: "dsh-github-badge dsh-badge-purple" }, "用户提单"));
532
+ }
533
+ if (cats.includes("assigned")) {
534
+ badges.push(h("span", { key: "asn", className: "dsh-github-badge dsh-badge-blue" }, "分配待办"));
535
+ }
536
+ if (cats.includes("created")) {
537
+ badges.push(h("span", { key: "crt", className: "dsh-github-badge dsh-badge-green" }, "我发起的"));
538
+ }
539
+ return h("div", {
540
+ key: is.url || is.number,
541
+ className: "dsh-github-item",
542
+ /*
543
+ * 只加 cursor,绝不覆盖 display/alignItems/flexWrap。
544
+ * .dsh-github-item 是 flex-direction:column,任何内联 flex 布局
545
+ * 都会把标题、徽章、元信息横向挤成一行(PR 卡片上踩过的坑)。
546
+ */
547
+ style: { cursor: "pointer" },
548
+ title: "点击在线查看该 Issue(正文 / 评论时间线 / 标签与里程碑)",
549
+ onClick: () => {
550
+ setActiveIssueModal({
551
+ number: Number(is.number),
552
+ repo: is.repository?.nameWithOwner || '',
553
+ });
554
+ },
555
+ },
556
+ // 标题行:标题 → 分类徽章 → 状态徽章 → GitHub 外跳,全部同一行
557
+ h("div", { className: "dsh-github-pr-title-row" }, h("span", { className: "dsh-github-item-title", style: { cursor: "pointer" } }, `[${is.repository?.nameWithOwner || 'Repo'}] #${is.number} ${is.title}`), ...badges, (() => {
558
+ // 状态徽章:全局检索含 closed,不标注会让用户把已关闭议题当成待办
559
+ var s = String(is.state || '').toUpperCase();
560
+ if (s === 'CLOSED')
561
+ return h("span", { className: "dsh-github-badge dsh-badge-red", style: { flexShrink: 0 } }, "CLOSED");
562
+ if (s === 'OPEN')
563
+ return h("span", { className: "dsh-github-badge dsh-badge-green", style: { flexShrink: 0 } }, "OPEN");
564
+ return null;
565
+ })(), h("a", {
566
+ className: "dsh-github-pr-gh-btn",
567
+ href: is.url,
568
+ target: "_blank",
569
+ rel: "noopener noreferrer",
570
+ onClick: (e) => e.stopPropagation(),
571
+ title: "在浏览器打开该 Issue(不进入在线查看)",
572
+ }, "GitHub ↗")), h("div", { className: "dsh-github-item-meta" }, is.author ? h("span", null, `由 @${is.author.login} 提出`) : null, is.assignees && is.assignees.length > 0
573
+ ? h("span", null, `指派: ${is.assignees.map((a) => '@' + a.login).join(', ')}`)
574
+ : null, is.labels && is.labels.length > 0
575
+ ? is.labels.map((lbl) => h("span", { key: lbl.name, className: "dsh-github-badge dsh-badge-yellow" }, lbl.name))
576
+ : null, is.updatedAt ? h("span", null, `更新于: ${new Date(is.updatedAt).toLocaleDateString()}`) : null));
577
+ })));
432
578
  }
433
579
  }
434
580
  var toastEl = toastMsg
@@ -460,5 +606,26 @@ export function GithubWorkspacePanel() {
460
606
  },
461
607
  })
462
608
  : null;
463
- return h("div", { className: "dsh-github-panel", ref: panelRef }, toastEl, headerEl, networkAlertBannerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
609
+ return h("div", { className: "dsh-github-panel", ref: panelRef }, toastEl, headerEl, networkAlertBannerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl, h(GithubHelpModal, {
610
+ isOpen: isHelpModalOpen,
611
+ onClose: () => setIsHelpModalOpen(false),
612
+ }),
613
+ // PR 在线审阅工作台:仅在用户点击某个 PR 时挂载,关闭即卸载(零常驻开销)
614
+ activePrModal
615
+ ? h(PrDetailModal, {
616
+ prNumber: activePrModal.number,
617
+ repoNameWithOwner: activePrModal.repo,
618
+ onClose: () => setActivePrModal(null),
619
+ showToast: showToast,
620
+ })
621
+ : null,
622
+ // Issue 在线查看弹窗:同上,点击即挂载、关闭即卸载
623
+ activeIssueModal
624
+ ? h(IssueDetailModal, {
625
+ issueNumber: activeIssueModal.number,
626
+ repoNameWithOwner: activeIssueModal.repo,
627
+ onClose: () => setActiveIssueModal(null),
628
+ showToast: showToast,
629
+ })
630
+ : null);
464
631
  }
@@ -1,5 +1,9 @@
1
- import type { UpgradeExecutionResult, UpgradeStatusResult } from './types.js';
1
+ import type { UpgradeExecutionResult, UpgradeStatusResult, UpgradeErrorType } from './types.js';
2
2
  import type { ProxyManager } from './proxy.js';
3
+ /**
4
+ * 分析升级失败日志中的深层错误特征(重点识别 DSH 内置包管理器 minimumReleaseAge 24 小时防投毒策略)
5
+ */
6
+ export declare function detectUpgradeErrorType(log?: string): UpgradeErrorType;
3
7
  /**
4
8
  * 构建跨平台环境自愈增强的 ProcessEnv (重点解决 macOS GUI 应用丢失 PATH 的痛点)
5
9
  *
@@ -4,6 +4,20 @@ import fs from 'node:fs';
4
4
  import os from 'node:os';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { PLUGIN_PACKAGE_NAME } from './version.js';
7
+ /**
8
+ * 分析升级失败日志中的深层错误特征(重点识别 DSH 内置包管理器 minimumReleaseAge 24 小时防投毒策略)
9
+ */
10
+ export function detectUpgradeErrorType(log) {
11
+ if (!log)
12
+ return 'UNKNOWN';
13
+ if (/ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION|minimumReleaseAge/i.test(log)) {
14
+ return 'MINIMUM_RELEASE_AGE_VIOLATION';
15
+ }
16
+ if (/ETIMEDOUT|timeout|执行超时/i.test(log)) {
17
+ return 'TIMEOUT';
18
+ }
19
+ return 'UNKNOWN';
20
+ }
7
21
  /**
8
22
  * 构建跨平台环境自愈增强的 ProcessEnv (重点解决 macOS GUI 应用丢失 PATH 的痛点)
9
23
  *
@@ -370,10 +384,18 @@ export class UpgradeManager {
370
384
  this.lastResult = { state: 'success', targetVersion: wantVersion, message: successMsg, completedAt, environment: env.isDesktop ? 'desktop' : 'web' };
371
385
  }
372
386
  else {
387
+ const fullLog = this.logBuffer.join('\n');
388
+ const errorType = detectUpgradeErrorType(fullLog);
389
+ let failMsg = `升级失败(安装命令返回非零退出码)。${targetVersion ? `目标版本 ${targetVersion} 未安装成功。` : ''}${actionHint},或稍后重试一键升级。`;
390
+ // 智能识别:DSH 内置包管理器 minimumReleaseAge 24 小时防投毒安全锁机制
391
+ if (errorType === 'MINIMUM_RELEASE_AGE_VIOLATION') {
392
+ failMsg = `🛡️ 触发 DSH 新版本 24 小时安全保护期(npm 供应链防投毒策略)。检测到目标版本刚刚发布未满 24 小时,DSH 内置包管理器暂缓了自动静默升级以保障系统安全。\n【推荐解决步骤】:\n1. 立即尝鲜:请先【完全退出关闭 DSH 应用程序】,然后在系统终端执行手动安装命令(点击「📋 复制命令」获取);\n2. 稍后升级:无需操作,发版满 24 小时后直接在此点击「⚡ 一键自动升级」即可平滑完成。`;
393
+ }
373
394
  this.lastResult = {
374
395
  state: 'failed',
375
396
  targetVersion,
376
- message: `升级失败(安装命令返回非零退出码)。${targetVersion ? `目标版本 ${targetVersion} 未安装成功。` : ''}${actionHint},或稍后重试一键升级。`,
397
+ errorType,
398
+ message: failMsg,
377
399
  completedAt,
378
400
  environment: env.isDesktop ? 'desktop' : 'web',
379
401
  };
@@ -581,6 +603,7 @@ export class UpgradeManager {
581
603
  ok: false,
582
604
  message: this.lastResult.message || `升级退出码异常 (${code})`,
583
605
  needRestart: false,
606
+ errorType: this.lastResult.errorType,
584
607
  environment: env.isDesktop ? 'desktop' : 'web',
585
608
  log: this.logBuffer.join('\n'),
586
609
  };
package/lib/version.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * 2. 避免在 ESM 运行时动态读取 package.json 引入 fs 权限和路径解析边界问题;
7
7
  * 3. 方便在 CI 发布流水线或 build 脚本中自动化校准。
8
8
  */
9
- export declare const CURRENT_PLUGIN_VERSION = "0.4.0";
9
+ export declare const CURRENT_PLUGIN_VERSION = "0.6.0";
10
10
  export declare const PLUGIN_PACKAGE_NAME = "dsh-github-flow";
11
11
  export declare const NPM_PACKAGE_URL = "https://www.npmjs.com/package/dsh-github-flow";
12
12
  export declare const NPM_REGISTRY_MIRROR = "https://registry.npmmirror.com";
package/lib/version.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * 2. 避免在 ESM 运行时动态读取 package.json 引入 fs 权限和路径解析边界问题;
7
7
  * 3. 方便在 CI 发布流水线或 build 脚本中自动化校准。
8
8
  */
9
- export const CURRENT_PLUGIN_VERSION = '0.4.0';
9
+ export const CURRENT_PLUGIN_VERSION = '0.6.0';
10
10
  export const PLUGIN_PACKAGE_NAME = 'dsh-github-flow';
11
11
  export const NPM_PACKAGE_URL = 'https://www.npmjs.com/package/dsh-github-flow';
12
12
  export const NPM_REGISTRY_MIRROR = 'https://registry.npmmirror.com';
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dsh-github-flow",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "icon": "icon.svg",
5
- "description": "GitHub 工作流引擎:为 DeepSeek Harness 提供原生 GitHub CLI (gh) 集成——9 大 Agent 领域工具(PR 审查合并、Issue 任务流转、CI 失败诊断、发版交付、密钥管理、Agent Skills 生态)、/gh 人类快捷指令、Web UI 全景驾驶舱与一键跨平台自动升级;零凭证侵入复用宿主机 gh 登录态,内置 Token 防溢出截断、高危操作二次确认与密文安全输入通道。",
5
+ "description": "GitHub 工作流引擎:为 DeepSeek Harness 提供原生 GitHub CLI (gh) 集成——18 大 Agent 领域工具(PR 审查合并、Issue 任务流转、CI 失败诊断、发版交付、密钥管理、Agent Skills、标签与保护规则集、构建缓存治理、代码片段 Gist、跨仓待办大盘、Copilot 智能体调度、社区 Discussions、Projects v2 看板、云端 Codespaces、运行时遥测与自愈健康度)、/gh 人类快捷指令、Web UI 全景驾驶舱与一键跨平台自动升级;零凭证侵入复用宿主机 gh 登录态,内置 Token 防溢出截断、高危操作二次确认与密文安全输入通道。",
6
6
  "type": "module",
7
7
  "main": "./lib/main.js",
8
8
  "repository": {