dsh-github-flow 0.3.0 → 0.5.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 (121) hide show
  1. package/README.en.md +287 -143
  2. package/README.md +270 -125
  3. package/docs/UPGRADE-NOTES.md +12 -9
  4. package/docs/architecture.md +126 -126
  5. package/lib/api/routes-actions.js +249 -0
  6. package/lib/api/routes-overview.js +94 -2
  7. package/lib/cache.d.ts +4 -0
  8. package/lib/cache.js +14 -0
  9. package/lib/client.d.ts +14 -1
  10. package/lib/client.js +4287 -2500
  11. package/lib/commands/gh.d.ts +6 -1
  12. package/lib/commands/gh.js +144 -71
  13. package/lib/executor/git-parser.d.ts +57 -0
  14. package/lib/executor/git-parser.js +255 -0
  15. package/lib/executor/platform-guide.d.ts +14 -0
  16. package/lib/executor/platform-guide.js +40 -0
  17. package/lib/executor/process-utils.d.ts +51 -0
  18. package/lib/executor/process-utils.js +162 -0
  19. package/lib/executor/version-utils.d.ts +12 -0
  20. package/lib/executor/version-utils.js +27 -0
  21. package/lib/executor.d.ts +70 -48
  22. package/lib/executor.js +371 -356
  23. package/lib/git-watcher.d.ts +36 -0
  24. package/lib/git-watcher.js +91 -0
  25. package/lib/main.js +37 -3
  26. package/lib/renderers/agent-task.d.ts +16 -0
  27. package/lib/renderers/agent-task.js +107 -0
  28. package/lib/renderers/cache.d.ts +8 -0
  29. package/lib/renderers/cache.js +66 -0
  30. package/lib/renderers/codespace.d.ts +16 -0
  31. package/lib/renderers/codespace.js +103 -0
  32. package/lib/renderers/common.d.ts +24 -0
  33. package/lib/renderers/common.js +88 -0
  34. package/lib/renderers/discussion.d.ts +16 -0
  35. package/lib/renderers/discussion.js +101 -0
  36. package/lib/renderers/gist.d.ts +15 -0
  37. package/lib/renderers/gist.js +99 -0
  38. package/lib/renderers/issue.d.ts +4 -0
  39. package/lib/renderers/issue.js +70 -0
  40. package/lib/renderers/label.d.ts +4 -0
  41. package/lib/renderers/label.js +48 -0
  42. package/lib/renderers/markdown.d.ts +19 -32
  43. package/lib/renderers/markdown.js +19 -624
  44. package/lib/renderers/pr.d.ts +4 -0
  45. package/lib/renderers/pr.js +107 -0
  46. package/lib/renderers/project.d.ts +20 -0
  47. package/lib/renderers/project.js +128 -0
  48. package/lib/renderers/release.d.ts +4 -0
  49. package/lib/renderers/release.js +74 -0
  50. package/lib/renderers/repo.d.ts +4 -0
  51. package/lib/renderers/repo.js +149 -0
  52. package/lib/renderers/ruleset.d.ts +4 -0
  53. package/lib/renderers/ruleset.js +74 -0
  54. package/lib/renderers/run.d.ts +4 -0
  55. package/lib/renderers/run.js +79 -0
  56. package/lib/renderers/skill.d.ts +34 -0
  57. package/lib/renderers/skill.js +241 -0
  58. package/lib/renderers/status.d.ts +28 -0
  59. package/lib/renderers/status.js +176 -0
  60. package/lib/telemetry.d.ts +70 -0
  61. package/lib/telemetry.js +161 -0
  62. package/lib/tools/agent-task.d.ts +54 -0
  63. package/lib/tools/agent-task.js +135 -0
  64. package/lib/tools/cache.d.ts +64 -0
  65. package/lib/tools/cache.js +186 -0
  66. package/lib/tools/codespace.d.ts +51 -0
  67. package/lib/tools/codespace.js +154 -0
  68. package/lib/tools/discussion.d.ts +70 -0
  69. package/lib/tools/discussion.js +195 -0
  70. package/lib/tools/gist.d.ts +155 -0
  71. package/lib/tools/gist.js +190 -0
  72. package/lib/tools/help.js +214 -13
  73. package/lib/tools/image-utils.d.ts +8 -0
  74. package/lib/tools/image-utils.js +34 -0
  75. package/lib/tools/issue.d.ts +8 -1
  76. package/lib/tools/issue.js +26 -4
  77. package/lib/tools/label.d.ts +80 -0
  78. package/lib/tools/label.js +237 -0
  79. package/lib/tools/pr.d.ts +8 -0
  80. package/lib/tools/pr.js +48 -3
  81. package/lib/tools/project.d.ts +80 -0
  82. package/lib/tools/project.js +241 -0
  83. package/lib/tools/repo.js +13 -1
  84. package/lib/tools/ruleset.d.ts +80 -0
  85. package/lib/tools/ruleset.js +177 -0
  86. package/lib/tools/skill.d.ts +24 -1
  87. package/lib/tools/skill.js +115 -5
  88. package/lib/tools/status.d.ts +40 -0
  89. package/lib/tools/status.js +58 -0
  90. package/lib/types.d.ts +44 -0
  91. package/lib/ui/auth-guide-card.d.ts +4 -0
  92. package/lib/ui/auth-guide-card.js +161 -0
  93. package/lib/ui/branch-selector.d.ts +10 -0
  94. package/lib/ui/branch-selector.js +163 -0
  95. package/lib/ui/commit-modal.d.ts +8 -0
  96. package/lib/ui/commit-modal.js +219 -0
  97. package/lib/ui/help-modal.d.ts +15 -0
  98. package/lib/ui/help-modal.js +202 -0
  99. package/lib/ui/icons.d.ts +7 -0
  100. package/lib/ui/icons.js +53 -0
  101. package/lib/ui/network-banner.d.ts +10 -0
  102. package/lib/ui/network-banner.js +54 -0
  103. package/lib/ui/storage.d.ts +35 -0
  104. package/lib/ui/storage.js +158 -0
  105. package/lib/ui/styles.d.ts +6 -0
  106. package/lib/ui/styles.js +1544 -0
  107. package/lib/ui/sync-settings-modal.d.ts +11 -0
  108. package/lib/ui/sync-settings-modal.js +350 -0
  109. package/lib/ui/sync-status-badge.d.ts +8 -0
  110. package/lib/ui/sync-status-badge.js +64 -0
  111. package/lib/ui/upgrade-card.d.ts +7 -0
  112. package/lib/ui/upgrade-card.js +222 -0
  113. package/lib/ui/utils.d.ts +45 -0
  114. package/lib/ui/utils.js +226 -0
  115. package/lib/ui/workspace-panel.d.ts +4 -0
  116. package/lib/ui/workspace-panel.js +489 -0
  117. package/lib/upgrade-manager.d.ts +5 -1
  118. package/lib/upgrade-manager.js +24 -1
  119. package/lib/version.d.ts +1 -1
  120. package/lib/version.js +1 -1
  121. package/package.json +2 -2
@@ -0,0 +1,222 @@
1
+ /**
2
+ * 通用升级通知与一键升级卡片组件(自适应右侧栏紧凑模式与全景大面板模式)
3
+ */
4
+ export function UpgradeNotificationCard(props) {
5
+ var updateInfo = props.updateInfo;
6
+ var isRightbar = Boolean(props.isRightbar);
7
+ if (!updateInfo || !updateInfo.hasUpdate)
8
+ return null;
9
+ var storageKey = "dsh_dismissed_update_" + (updateInfo.latestVersion || "latest");
10
+ var [dismissed, setDismissed] = React.useState(function () {
11
+ try {
12
+ return localStorage.getItem(storageKey) === "true";
13
+ }
14
+ catch (e) {
15
+ return false;
16
+ }
17
+ });
18
+ var [status, setStatus] = React.useState("idle");
19
+ var [msg, setMsg] = React.useState("");
20
+ React.useEffect(function () {
21
+ fetch("/api/github/upgrade-status")
22
+ .then(function (res) { return res.json(); })
23
+ .then(function (json) {
24
+ if (json && json.ok && json.data) {
25
+ if (json.data.state === "upgrading") {
26
+ setStatus("upgrading");
27
+ setMsg("正在拉取最新版本(约需 5~15 秒)...");
28
+ }
29
+ else if (json.data.state === "success") {
30
+ setStatus("success");
31
+ setMsg(json.data.message || "升级成功!已安装最新依赖。");
32
+ }
33
+ }
34
+ })
35
+ .catch(function () { });
36
+ }, []);
37
+ if (dismissed)
38
+ return null;
39
+ var handleUpgrade = async function () {
40
+ if (status === "upgrading")
41
+ return;
42
+ setStatus("upgrading");
43
+ setMsg("正在拉取最新版本(约需 5~15 秒)...");
44
+ try {
45
+ var res = await fetch("/api/github/upgrade", { method: "POST" });
46
+ var json = await res.json().catch(function () { return {}; });
47
+ if (res.ok && json.ok) {
48
+ setStatus("success");
49
+ setMsg(json.message || "升级成功!已安装最新依赖。");
50
+ }
51
+ else {
52
+ setStatus("failed");
53
+ var logText = String(json.log || "");
54
+ var isAgeViolation = json.errorType === "MINIMUM_RELEASE_AGE_VIOLATION" ||
55
+ logText.includes("ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION") ||
56
+ logText.includes("minimumReleaseAge") ||
57
+ (typeof json.message === "string" && (json.message.includes("24 小时安全保护期") || json.message.includes("防投毒")));
58
+ if (isAgeViolation) {
59
+ // 命中 DSH 内置包管理器 24 小时防投毒策略:收起英文报错堆栈,呈现全中文专业自愈指引
60
+ setMsg("🛡️ 触发 DSH 新版本 24 小时安全保护期(npm 供应链防投毒策略)。检测到目标版本刚刚发布未满 24 小时,DSH 内置包管理器暂缓了自动升级以保障系统安全。\n\n" +
61
+ "【推荐操作】:\n" +
62
+ "1. 立即尝鲜:请先【完全退出关闭 DSH 应用程序】,然后在系统终端执行手动安装命令(点击右侧「📋 复制命令」获取);\n" +
63
+ "2. 稍后升级:无需任何操作,发版满 24 小时后直接在此点击「⚡ 一键自动升级」即可平滑完成。");
64
+ }
65
+ else {
66
+ var logTail = "";
67
+ if (json.log && typeof json.log === "string") {
68
+ var lines = json.log.trim().split("\n").filter(function (l) { return l.trim(); });
69
+ if (lines.length > 0) {
70
+ logTail = "\n\n📝 日志末尾: " + lines.slice(-4).join(" ⏎ ").slice(0, 400);
71
+ }
72
+ }
73
+ setMsg((json.message || "升级命令执行失败,请在宿主终端手动执行。") + logTail);
74
+ }
75
+ }
76
+ }
77
+ catch (err) {
78
+ setStatus("failed");
79
+ setMsg("无法连接后端执行升级: " + (err.message || err));
80
+ }
81
+ };
82
+ var handleDismiss = function () {
83
+ try {
84
+ localStorage.setItem(storageKey, "true");
85
+ }
86
+ catch (e) { }
87
+ setDismissed(true);
88
+ };
89
+ var handleCopy = function () {
90
+ var cmd = updateInfo.upgradeCommand || "dsh plugin --profile desktop add dsh-github-flow@latest";
91
+ if (navigator.clipboard) {
92
+ navigator.clipboard.writeText(cmd);
93
+ alert("已复制升级命令到剪贴板:\n" + cmd);
94
+ }
95
+ else {
96
+ prompt("请复制升级命令:", cmd);
97
+ }
98
+ };
99
+ var handleReload = function () {
100
+ window.location.reload();
101
+ };
102
+ var h = React.createElement;
103
+ var isAgeViolation = msg.includes("24 小时安全保护期") || msg.includes("防投毒");
104
+ if (isRightbar) {
105
+ return h("div", {
106
+ className: "dsh-github-upgrade-strip",
107
+ style: {
108
+ padding: "10px 12px",
109
+ marginBottom: "12px",
110
+ borderRadius: "8px",
111
+ background: status === "success"
112
+ ? "var(--dsw-alias-state-success-subtle, rgba(46, 160, 67, 0.12))"
113
+ : (isAgeViolation
114
+ ? "rgba(217, 119, 6, 0.12)"
115
+ : "var(--dsw-alias-bg-module-platform, rgba(9, 105, 218, 0.08))"),
116
+ border: "1px solid " + (status === "success"
117
+ ? "var(--dsw-alias-state-success-border, rgba(46, 160, 67, 0.35))"
118
+ : (isAgeViolation
119
+ ? "rgba(217, 119, 6, 0.35)"
120
+ : "var(--dsw-alias-border-l2, rgba(9, 105, 218, 0.25))")),
121
+ fontSize: "12px",
122
+ },
123
+ }, h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "6px" } }, h("span", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success" ? "🎉 升级完成" : (isAgeViolation ? "🛡️ 处于 24h 安全保护期" : "🚀 发现新版 v" + updateInfo.latestVersion)), h("button", {
124
+ onClick: handleDismiss,
125
+ title: "不再提醒此版本",
126
+ style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "14px", padding: 0 },
127
+ }, "✕")), h("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "8px", lineHeight: "1.4" } }, msg || (status === "success"
128
+ ? "需重启 DSH 服务彻底生效后端工具,或刷新网页预览新界面。"
129
+ : "当前: v" + updateInfo.currentVersion + ",推荐升级以获取最新功能。")), h("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" } }, status === "success"
130
+ ? h("button", {
131
+ className: "dsh-github-btn-sm",
132
+ onClick: handleReload,
133
+ style: { flex: 1, justifyContent: "center", background: "#2ea043", color: "#fff", borderColor: "#2ea043" },
134
+ }, "🔄 刷新页面")
135
+ : h("button", {
136
+ className: "dsh-github-btn-sm",
137
+ disabled: status === "upgrading",
138
+ onClick: handleUpgrade,
139
+ style: {
140
+ flex: 1,
141
+ justifyContent: "center",
142
+ background: "var(--dsw-alias-link, #0969da)",
143
+ color: "#fff",
144
+ borderColor: "var(--dsw-alias-link, #0969da)",
145
+ opacity: status === "upgrading" ? 0.7 : 1,
146
+ },
147
+ }, status === "upgrading" ? "升级中..." : "⚡ 一键升级"), h("button", {
148
+ className: "dsh-github-btn-sm",
149
+ onClick: handleCopy,
150
+ style: {
151
+ fontSize: "11px",
152
+ padding: "4px 8px",
153
+ ...(status === "failed" ? {
154
+ background: "var(--dsw-alias-link, #0969da)",
155
+ color: "#ffffff",
156
+ borderColor: "var(--dsw-alias-link, #0969da)",
157
+ fontWeight: 600,
158
+ } : {}),
159
+ },
160
+ }, "📋 复制命令")));
161
+ }
162
+ return h("div", {
163
+ className: "dsh-github-upgrade-banner",
164
+ style: {
165
+ display: "flex",
166
+ alignItems: "center",
167
+ justifyContent: "space-between",
168
+ padding: "12px 18px",
169
+ marginBottom: "16px",
170
+ borderRadius: "8px",
171
+ background: status === "success"
172
+ ? "linear-gradient(90deg, rgba(46, 160, 67, 0.15) 0%, rgba(9, 105, 218, 0.12) 100%)"
173
+ : (isAgeViolation
174
+ ? "linear-gradient(90deg, rgba(217, 119, 6, 0.15) 0%, rgba(245, 158, 11, 0.12) 100%)"
175
+ : "linear-gradient(90deg, rgba(9, 105, 218, 0.12) 0%, rgba(137, 87, 229, 0.12) 100%)"),
176
+ border: "1px solid " + (status === "success" ? "rgba(46, 160, 67, 0.35)" : (isAgeViolation ? "rgba(217, 119, 6, 0.35)" : "rgba(9, 105, 218, 0.3)")),
177
+ fontSize: "13px",
178
+ },
179
+ }, h("div", { style: { display: "flex", alignItems: "center", gap: "10px" } }, h("span", { style: { fontSize: "20px" } }, status === "success" ? "🎉" : (isAgeViolation ? "🛡️" : "🚀")), h("div", null, h("div", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary, #1f2328)" } }, status === "success"
180
+ ? "升级完成!新版本 v" + updateInfo.latestVersion + " 已写入"
181
+ : (isAgeViolation
182
+ ? "🛡️ 触发 DSH 新版本 24 小时安全保护期 (供应链防投毒策略)"
183
+ : "发现 dsh-github-flow 新版本 v" + updateInfo.latestVersion + "(当前版本: v" + updateInfo.currentVersion + ")")), h("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-secondary, #656d76)", marginTop: "2px" } }, msg || (status === "success"
184
+ ? "💡 提示:DSH 后端驻留了旧版内存,需重启 DSH 服务彻底生效后端工具;若仅预览界面改动可先刷新网页。"
185
+ : "包含最新 GitHub 工具特性与性能优化,点击右侧可一键自动升级。")))), h("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 } }, status === "success"
186
+ ? h("button", {
187
+ className: "dsh-github-btn-sm",
188
+ onClick: handleReload,
189
+ style: { background: "#2ea043", color: "#fff", borderColor: "#2ea043", padding: "6px 14px", fontWeight: 600 },
190
+ }, "🔄 立即刷新页面")
191
+ : h("button", {
192
+ className: "dsh-github-btn-sm",
193
+ disabled: status === "upgrading",
194
+ onClick: handleUpgrade,
195
+ style: {
196
+ background: "var(--dsw-alias-link, #0969da)",
197
+ color: "#fff",
198
+ borderColor: "var(--dsw-alias-link, #0969da)",
199
+ padding: "6px 14px",
200
+ fontWeight: 600,
201
+ opacity: status === "upgrading" ? 0.7 : 1,
202
+ },
203
+ }, status === "upgrading" ? "正在自动升级中..." : "⚡ 一键自动升级"), h("button", {
204
+ className: "dsh-github-btn-sm",
205
+ onClick: handleCopy,
206
+ style: {
207
+ padding: "6px 12px",
208
+ ...(status === "failed" ? {
209
+ background: "var(--dsw-alias-link, #0969da)",
210
+ color: "#ffffff",
211
+ borderColor: "var(--dsw-alias-link, #0969da)",
212
+ fontWeight: 600,
213
+ } : {}),
214
+ },
215
+ }, status === "failed"
216
+ ? (isAgeViolation ? "📋 复制命令 (需先退出DSH)" : "📋 复制命令手动升级")
217
+ : "📋 复制命令"), h("button", {
218
+ onClick: handleDismiss,
219
+ title: "不再提醒此版本",
220
+ style: { background: "none", border: "none", cursor: "pointer", color: "var(--dsw-alias-label-tertiary, #8c8c8c)", fontSize: "16px", padding: "0 4px" },
221
+ }, "✕")));
222
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 面板公共工具函数(纯函数,可被单元测试直接覆盖)
3
+ */
4
+ export declare function formatRelativeTime(iso: any): string;
5
+ export declare function rollupCiStatus(checks: any): "pass" | "fail" | "pending" | null;
6
+ export declare function reviewDecisionBadge(decision: any): {
7
+ text: string;
8
+ kind: "green" | "red" | "yellow";
9
+ } | null;
10
+ export declare function eventLabel(event: any): string;
11
+ export interface FetchRetryConfig {
12
+ maxRetries?: number;
13
+ baseDelayMs?: number;
14
+ onRetry?: (attempt: number, delayMs: number, reason: string) => void;
15
+ signal?: AbortSignal;
16
+ }
17
+ /**
18
+ * 带有阶梯退避重试(Backoff Retry)的通用异步拉取函数
19
+ *
20
+ * 核心设计理由与约束:
21
+ * 1. 应对代理节点不稳定、TLS 握手卡顿、短时网络抖动(半秒至两秒微断流);
22
+ * 2. 阶梯退避时序:第 1 次失败等 1s (1000ms),第 2 次等 2s (2000ms),第 3 次等 4s (4000ms),附加 0~200ms Jitter;
23
+ * 3. 智能区分软硬错误:401/403/404/400 确定性错误坚决不重试,仅在网络超时、不可达、或带有 networkError 时重试;
24
+ * 4. 严格响应外部 AbortSignal,组件卸载或用户手动打断时立即退出等待,绝无内存泄漏。
25
+ */
26
+ export declare function fetchWithRetry(url: string, options?: RequestInit, config?: FetchRetryConfig): Promise<{
27
+ res: Response;
28
+ json: any;
29
+ }>;
30
+ export interface GitStatItem {
31
+ path: string;
32
+ total: number;
33
+ additions: number;
34
+ deletions: number;
35
+ isBinary: boolean;
36
+ rawRight?: string;
37
+ }
38
+ export declare function parseGitStatText(statText: string): {
39
+ files: GitStatItem[];
40
+ summary: {
41
+ filesChanged: number;
42
+ insertions: number;
43
+ deletions: number;
44
+ } | null;
45
+ } | null;
@@ -0,0 +1,226 @@
1
+ /**
2
+ * 面板公共工具函数(纯函数,可被单元测试直接覆盖)
3
+ */
4
+ export function formatRelativeTime(iso) {
5
+ if (!iso || typeof iso !== "string")
6
+ return "";
7
+ var t = Date.parse(iso);
8
+ if (isNaN(t))
9
+ return "";
10
+ var diff = Date.now() - t;
11
+ if (diff < 0)
12
+ return "刚刚";
13
+ var MIN = 60 * 1000, HOUR = 60 * MIN, DAY = 24 * HOUR;
14
+ if (diff < MIN)
15
+ return "刚刚";
16
+ if (diff < HOUR)
17
+ return Math.floor(diff / MIN) + "分钟前";
18
+ if (diff < DAY)
19
+ return Math.floor(diff / HOUR) + "小时前";
20
+ if (diff < 7 * DAY)
21
+ return Math.floor(diff / DAY) + "天前";
22
+ var d = new Date(t);
23
+ return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
24
+ }
25
+ export function rollupCiStatus(checks) {
26
+ if (!Array.isArray(checks) || checks.length === 0)
27
+ return null;
28
+ var hasFail = false, hasPending = false;
29
+ for (var i = 0; i < checks.length; i++) {
30
+ var c = checks[i] || {};
31
+ var status = String(c.status || "").toLowerCase();
32
+ var conclusion = String(c.conclusion || "").toLowerCase();
33
+ if (conclusion === "failure" || conclusion === "timed_out" || conclusion === "cancelled")
34
+ hasFail = true;
35
+ else if (conclusion === "success" || conclusion === "skipped" || conclusion === "neutral") { }
36
+ else if (status === "in_progress" || status === "queued" || conclusion === "pending" || (!conclusion && status))
37
+ hasPending = true;
38
+ }
39
+ if (hasFail)
40
+ return "fail";
41
+ if (hasPending)
42
+ return "pending";
43
+ return "pass";
44
+ }
45
+ export function reviewDecisionBadge(decision) {
46
+ var d = String(decision || "").toUpperCase();
47
+ if (d === "APPROVED")
48
+ return { text: "✔ 已批准", kind: "green" };
49
+ if (d === "CHANGES_REQUESTED")
50
+ return { text: "✋ 需修改", kind: "red" };
51
+ if (d === "REVIEW_REQUIRED")
52
+ return { text: "👀 待审查", kind: "yellow" };
53
+ return null;
54
+ }
55
+ export function eventLabel(event) {
56
+ var e = String(event || "");
57
+ if (e === "push")
58
+ return "⇡ push";
59
+ if (e === "schedule")
60
+ return "⏰ 定时";
61
+ if (e === "workflow_dispatch")
62
+ return "▶ 手动";
63
+ if (e === "pull_request")
64
+ return "🔀 PR";
65
+ return e || "";
66
+ }
67
+ /**
68
+ * 带有阶梯退避重试(Backoff Retry)的通用异步拉取函数
69
+ *
70
+ * 核心设计理由与约束:
71
+ * 1. 应对代理节点不稳定、TLS 握手卡顿、短时网络抖动(半秒至两秒微断流);
72
+ * 2. 阶梯退避时序:第 1 次失败等 1s (1000ms),第 2 次等 2s (2000ms),第 3 次等 4s (4000ms),附加 0~200ms Jitter;
73
+ * 3. 智能区分软硬错误:401/403/404/400 确定性错误坚决不重试,仅在网络超时、不可达、或带有 networkError 时重试;
74
+ * 4. 严格响应外部 AbortSignal,组件卸载或用户手动打断时立即退出等待,绝无内存泄漏。
75
+ */
76
+ export async function fetchWithRetry(url, options = {}, config = {}) {
77
+ var maxRetries = config.maxRetries !== undefined ? config.maxRetries : 3;
78
+ var baseDelayMs = config.baseDelayMs !== undefined ? config.baseDelayMs : 1000;
79
+ var signal = config.signal || options.signal;
80
+ var attempt = 0;
81
+ while (true) {
82
+ attempt++;
83
+ var currentRes = null;
84
+ var currentJson = null;
85
+ var failureReason = "";
86
+ var shouldRetry = false;
87
+ try {
88
+ if (signal && signal.aborted) {
89
+ throw new Error("Aborted");
90
+ }
91
+ currentRes = await fetch(url, { ...options, signal });
92
+ // 硬错误守卫:客户端确定性错误(如未授权、权限拒绝、不存在、参数错误)坚决不重试
93
+ if (currentRes.status === 401 || currentRes.status === 403 || currentRes.status === 404 || currentRes.status === 400) {
94
+ currentJson = await currentRes.json().catch(() => ({}));
95
+ return { res: currentRes, json: currentJson };
96
+ }
97
+ if (!currentRes.ok) {
98
+ failureReason = "HTTP " + currentRes.status + " " + currentRes.statusText;
99
+ shouldRetry = true;
100
+ }
101
+ else {
102
+ currentJson = await currentRes.json().catch(() => null);
103
+ // 业务层软错误检测(后端返回了 200,但标记了 networkError 或 partialFailure)
104
+ var isNetworkError = Boolean(currentJson && (currentJson.partialFailure ||
105
+ currentJson.data?.partialFailure ||
106
+ currentJson.data?.networkError ||
107
+ currentJson.data?.auth?.networkError));
108
+ if (isNetworkError) {
109
+ failureReason = currentJson.error || currentJson.data?.networkErrorReason || "云端接口网络抖动";
110
+ shouldRetry = true;
111
+ }
112
+ else {
113
+ // 成功获取完整且无网络错误的数据
114
+ return { res: currentRes, json: currentJson };
115
+ }
116
+ }
117
+ }
118
+ catch (err) {
119
+ if ((signal && signal.aborted) || err.name === "AbortError") {
120
+ throw err;
121
+ }
122
+ failureReason = err.message || "网络连接中断或超时";
123
+ shouldRetry = true;
124
+ }
125
+ // 判断是否已耗尽重试次数
126
+ if (!shouldRetry || attempt > maxRetries) {
127
+ if (currentRes) {
128
+ return { res: currentRes, json: currentJson || {} };
129
+ }
130
+ throw new Error(failureReason || "网络请求失败");
131
+ }
132
+ // 计算阶梯退避等待时间:baseDelayMs * 2^(attempt - 1) + Jitter 自适应微扰
133
+ var exponentialDelay = baseDelayMs * Math.pow(2, attempt - 1);
134
+ var maxJitter = Math.min(200, Math.floor(exponentialDelay * 0.2));
135
+ var jitter = Math.floor(Math.random() * (maxJitter + 1));
136
+ var totalDelay = exponentialDelay + jitter;
137
+ if (config.onRetry) {
138
+ config.onRetry(attempt, totalDelay, failureReason);
139
+ }
140
+ // 可中断的睡眠等待
141
+ await new Promise((resolve, reject) => {
142
+ var timer = setTimeout(resolve, totalDelay);
143
+ if (signal) {
144
+ signal.addEventListener("abort", () => {
145
+ clearTimeout(timer);
146
+ reject(new Error("Aborted"));
147
+ }, { once: true });
148
+ }
149
+ });
150
+ }
151
+ }
152
+ export function parseGitStatText(statText) {
153
+ if (!statText || typeof statText !== "string")
154
+ return null;
155
+ var lines = statText.split("\n");
156
+ var files = [];
157
+ var summary = null;
158
+ for (var i = 0; i < lines.length; i++) {
159
+ var line = lines[i].trim();
160
+ if (!line)
161
+ continue;
162
+ var summaryMatch = line.match(/(\d+)\s+files?\s+changed(?:,\s+(\d+)\s+insertions?\(\+\))?(?:,\s+(\d+)\s+deletions?\(-\))?/i);
163
+ if (summaryMatch) {
164
+ summary = {
165
+ filesChanged: parseInt(summaryMatch[1], 10),
166
+ insertions: parseInt(summaryMatch[2] || "0", 10),
167
+ deletions: parseInt(summaryMatch[3] || "0", 10),
168
+ };
169
+ continue;
170
+ }
171
+ var pipeIdx = line.indexOf("|");
172
+ if (pipeIdx > 0) {
173
+ var left = line.slice(0, pipeIdx).trim();
174
+ var right = line.slice(pipeIdx + 1).trim();
175
+ if (left.startsWith('"') && left.endsWith('"') && left.length >= 2) {
176
+ left = left.slice(1, -1);
177
+ }
178
+ if (/^Bin\b/i.test(right)) {
179
+ files.push({ path: left, total: 0, additions: 0, deletions: 0, isBinary: true, rawRight: right });
180
+ }
181
+ else {
182
+ var numMatch = right.match(/^(\d+)(.*)$/);
183
+ if (numMatch) {
184
+ var total = parseInt(numMatch[1], 10);
185
+ var graph = numMatch[2].trim();
186
+ var plusCount = 0;
187
+ var minusCount = 0;
188
+ for (var j = 0; j < graph.length; j++) {
189
+ if (graph[j] === "+")
190
+ plusCount++;
191
+ else if (graph[j] === "-")
192
+ minusCount++;
193
+ }
194
+ var additions = 0;
195
+ var deletions = 0;
196
+ if (plusCount === 0 && minusCount === 0) {
197
+ additions = total;
198
+ }
199
+ else if (minusCount === 0) {
200
+ additions = total;
201
+ deletions = 0;
202
+ }
203
+ else if (plusCount === 0) {
204
+ additions = 0;
205
+ deletions = total;
206
+ }
207
+ else {
208
+ if (plusCount + minusCount === total) {
209
+ additions = plusCount;
210
+ deletions = minusCount;
211
+ }
212
+ else {
213
+ var addExact = Math.round((total * plusCount) / (plusCount + minusCount));
214
+ additions = addExact;
215
+ deletions = Math.max(0, total - addExact);
216
+ }
217
+ }
218
+ files.push({ path: left, total, additions, deletions, isBinary: false });
219
+ }
220
+ }
221
+ }
222
+ }
223
+ if (files.length === 0 && !summary)
224
+ return null;
225
+ return { files, summary };
226
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 全局工作区仪表盘主面板组件 (Dashboard View)
3
+ */
4
+ export declare function GithubWorkspacePanel(): any;