dsh-github-flow 0.2.1 → 0.4.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 (102) hide show
  1. package/README.en.md +234 -130
  2. package/README.md +217 -112
  3. package/docs/UPGRADE-NOTES.md +15 -9
  4. package/docs/architecture.md +120 -108
  5. package/docs/images/2.png +0 -0
  6. package/docs/npm-publish-guide.md +12 -1
  7. package/lib/api/routes-actions.js +117 -0
  8. package/lib/api/routes-overview.js +198 -3
  9. package/lib/cache.d.ts +4 -0
  10. package/lib/cache.js +14 -0
  11. package/lib/client.d.ts +13 -1
  12. package/lib/client.js +3162 -2162
  13. package/lib/executor/git-parser.d.ts +25 -0
  14. package/lib/executor/git-parser.js +143 -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 +131 -17
  22. package/lib/executor.js +411 -223
  23. package/lib/git-watcher.d.ts +36 -0
  24. package/lib/git-watcher.js +91 -0
  25. package/lib/main.js +11 -0
  26. package/lib/renderers/cache.d.ts +8 -0
  27. package/lib/renderers/cache.js +66 -0
  28. package/lib/renderers/common.d.ts +24 -0
  29. package/lib/renderers/common.js +88 -0
  30. package/lib/renderers/gist.d.ts +15 -0
  31. package/lib/renderers/gist.js +99 -0
  32. package/lib/renderers/issue.d.ts +4 -0
  33. package/lib/renderers/issue.js +70 -0
  34. package/lib/renderers/label.d.ts +4 -0
  35. package/lib/renderers/label.js +48 -0
  36. package/lib/renderers/markdown.d.ts +15 -26
  37. package/lib/renderers/markdown.js +15 -517
  38. package/lib/renderers/pr.d.ts +4 -0
  39. package/lib/renderers/pr.js +107 -0
  40. package/lib/renderers/release.d.ts +4 -0
  41. package/lib/renderers/release.js +74 -0
  42. package/lib/renderers/repo.d.ts +4 -0
  43. package/lib/renderers/repo.js +149 -0
  44. package/lib/renderers/ruleset.d.ts +4 -0
  45. package/lib/renderers/ruleset.js +74 -0
  46. package/lib/renderers/run.d.ts +4 -0
  47. package/lib/renderers/run.js +79 -0
  48. package/lib/renderers/skill.d.ts +4 -0
  49. package/lib/renderers/skill.js +61 -0
  50. package/lib/renderers/status.d.ts +23 -0
  51. package/lib/renderers/status.js +124 -0
  52. package/lib/tools/cache.d.ts +64 -0
  53. package/lib/tools/cache.js +186 -0
  54. package/lib/tools/gist.d.ts +155 -0
  55. package/lib/tools/gist.js +190 -0
  56. package/lib/tools/help.js +233 -11
  57. package/lib/tools/image-utils.d.ts +8 -0
  58. package/lib/tools/image-utils.js +34 -0
  59. package/lib/tools/issue.d.ts +17 -1
  60. package/lib/tools/issue.js +114 -8
  61. package/lib/tools/label.d.ts +80 -0
  62. package/lib/tools/label.js +237 -0
  63. package/lib/tools/pr.d.ts +12 -0
  64. package/lib/tools/pr.js +96 -4
  65. package/lib/tools/repo.d.ts +8 -0
  66. package/lib/tools/repo.js +127 -3
  67. package/lib/tools/ruleset.d.ts +80 -0
  68. package/lib/tools/ruleset.js +177 -0
  69. package/lib/tools/run.d.ts +8 -0
  70. package/lib/tools/run.js +104 -3
  71. package/lib/tools/secret.d.ts +5 -0
  72. package/lib/tools/secret.js +108 -23
  73. package/lib/tools/status.d.ts +39 -0
  74. package/lib/tools/status.js +53 -0
  75. package/lib/types.d.ts +112 -0
  76. package/lib/ui/auth-guide-card.d.ts +4 -0
  77. package/lib/ui/auth-guide-card.js +161 -0
  78. package/lib/ui/branch-selector.d.ts +10 -0
  79. package/lib/ui/branch-selector.js +163 -0
  80. package/lib/ui/commit-modal.d.ts +8 -0
  81. package/lib/ui/commit-modal.js +89 -0
  82. package/lib/ui/icons.d.ts +7 -0
  83. package/lib/ui/icons.js +53 -0
  84. package/lib/ui/network-banner.d.ts +10 -0
  85. package/lib/ui/network-banner.js +54 -0
  86. package/lib/ui/storage.d.ts +26 -0
  87. package/lib/ui/storage.js +134 -0
  88. package/lib/ui/styles.d.ts +6 -0
  89. package/lib/ui/styles.js +1285 -0
  90. package/lib/ui/sync-settings-modal.d.ts +11 -0
  91. package/lib/ui/sync-settings-modal.js +350 -0
  92. package/lib/ui/sync-status-badge.d.ts +8 -0
  93. package/lib/ui/sync-status-badge.js +64 -0
  94. package/lib/ui/upgrade-card.d.ts +7 -0
  95. package/lib/ui/upgrade-card.js +197 -0
  96. package/lib/ui/utils.d.ts +10 -0
  97. package/lib/ui/utils.js +66 -0
  98. package/lib/ui/workspace-panel.d.ts +4 -0
  99. package/lib/ui/workspace-panel.js +464 -0
  100. package/lib/version.d.ts +1 -1
  101. package/lib/version.js +1 -1
  102. package/package.json +1 -1
@@ -0,0 +1,10 @@
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;
@@ -0,0 +1,66 @@
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
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 全局工作区仪表盘主面板组件 (Dashboard View)
3
+ */
4
+ export declare function GithubWorkspacePanel(): any;
@@ -0,0 +1,464 @@
1
+ /**
2
+ * 全局工作区仪表盘主面板组件 (Dashboard View)
3
+ */
4
+ export function GithubWorkspacePanel() {
5
+ var initialSettings = React.useMemo(() => getLocalSyncSettings(), []);
6
+ var initialSnapshot = React.useMemo(() => getStoredGlobalOverview(), []);
7
+ var [syncSettings, setSyncSettings] = React.useState(initialSettings);
8
+ var [data, setData] = React.useState(initialSnapshot?.data || null);
9
+ var [loading, setLoading] = React.useState(!initialSnapshot);
10
+ var [syncStatus, setSyncStatus] = React.useState(initialSnapshot ? "cached" : "loading");
11
+ var [lastSyncedAt, setLastSyncedAt] = React.useState(initialSnapshot?.savedAt || 0);
12
+ var [isSettingsModalOpen, setIsSettingsModalOpen] = React.useState(false);
13
+ var [settingsInitialSection, setSettingsInitialSection] = React.useState("sync");
14
+ var [error, setError] = React.useState(null);
15
+ var [updateInfo, setUpdateInfo] = React.useState(null);
16
+ var [activeTab, setActiveTab] = React.useState("workspaces");
17
+ var [searchQuery, setSearchQuery] = React.useState("");
18
+ var [issueFilter, setIssueFilter] = React.useState("all");
19
+ var [toastMsg, setToastMsg] = React.useState(null);
20
+ var cloudTimerRef = React.useRef(null);
21
+ var dataRef = React.useRef(data);
22
+ dataRef.current = data;
23
+ var showToast = (msg) => {
24
+ setToastMsg(msg);
25
+ setTimeout(() => setToastMsg(null), 5000);
26
+ };
27
+ var [proxyActive, setProxyActive] = React.useState(null);
28
+ var refreshProxyBadge = React.useCallback(function () {
29
+ fetch("/api/github/proxy")
30
+ .then(function (r) { return r.json(); })
31
+ .then(function (j) {
32
+ if (j && j.ok && j.state && j.state.config) {
33
+ setProxyActive({ enabled: Boolean(j.state.config.url), url: j.state.config.url || "" });
34
+ }
35
+ else {
36
+ setProxyActive({ enabled: false, url: "" });
37
+ }
38
+ })
39
+ .catch(function () { });
40
+ }, []);
41
+ React.useEffect(function () { refreshProxyBadge(); }, [refreshProxyBadge]);
42
+ React.useEffect(() => {
43
+ var timer = setTimeout(function () {
44
+ fetch("/api/github/check-update")
45
+ .then(function (res) { return res.json(); })
46
+ .then(function (json) {
47
+ if (json && json.ok && json.data) {
48
+ setUpdateInfo(json.data);
49
+ }
50
+ })
51
+ .catch(function () { });
52
+ }, 300);
53
+ return function () { clearTimeout(timer); };
54
+ }, []);
55
+ var panelRef = React.useRef(null);
56
+ React.useEffect(() => {
57
+ var onOpenAbout = () => {
58
+ var el = panelRef && panelRef.current;
59
+ if (!el)
60
+ return;
61
+ var rect = el.getBoundingClientRect();
62
+ if (rect.width > 0 && rect.height > 0) {
63
+ setSettingsInitialSection("about");
64
+ setIsSettingsModalOpen(true);
65
+ }
66
+ };
67
+ var onOpenProxy = () => {
68
+ var el = panelRef && panelRef.current;
69
+ if (!el)
70
+ return;
71
+ var rect = el.getBoundingClientRect();
72
+ if (rect.width > 0 && rect.height > 0) {
73
+ setSettingsInitialSection("proxy");
74
+ setIsSettingsModalOpen(true);
75
+ }
76
+ };
77
+ window.addEventListener("dsh-github-open-about", onOpenAbout);
78
+ window.addEventListener("dsh-github-open-proxy", onOpenProxy);
79
+ return () => {
80
+ window.removeEventListener("dsh-github-open-about", onOpenAbout);
81
+ window.removeEventListener("dsh-github-open-proxy", onOpenProxy);
82
+ };
83
+ }, []);
84
+ var openSettingsDefault = () => {
85
+ setSettingsInitialSection("sync");
86
+ setIsSettingsModalOpen(true);
87
+ };
88
+ var loadData = React.useCallback(async (forceRefresh = false) => {
89
+ if (!forceRefresh && syncSettings.strategy === "manual" && dataRef.current) {
90
+ return;
91
+ }
92
+ if (forceRefresh || !dataRef.current) {
93
+ setLoading(true);
94
+ }
95
+ setSyncStatus(dataRef.current ? "syncing" : "loading");
96
+ setError(null);
97
+ var controller = new AbortController();
98
+ var timedOut = false;
99
+ var timer = setTimeout(function () { timedOut = true; controller.abort(); }, 45000);
100
+ try {
101
+ if (forceRefresh)
102
+ await fetch("/api/github/refresh", { method: "POST", signal: controller.signal }).catch(() => { });
103
+ var url = "/api/github/global-overview";
104
+ if (forceRefresh) {
105
+ url += "?force=true";
106
+ }
107
+ var res = await fetch(url, { signal: controller.signal });
108
+ if (!res.ok) {
109
+ var errText = await res.text().catch(() => "");
110
+ var failMsg = `获取全局数据失败(HTTP ${res.status})${errText ? ": " + errText : ""}`;
111
+ setSyncStatus("failed");
112
+ setError(failMsg);
113
+ if (forceRefresh)
114
+ showToast("❌ 刷新失败:" + failMsg);
115
+ return;
116
+ }
117
+ var json = await res.json();
118
+ if (json.ok && json.data) {
119
+ var isFail = Boolean(json.partialFailure ||
120
+ json.data.partialFailure ||
121
+ json.data.networkError ||
122
+ json.data.auth?.networkError ||
123
+ json.error);
124
+ var failReason = String(json.error ||
125
+ json.fetchError ||
126
+ json.data.networkErrorReason ||
127
+ json.data.auth?.networkErrorReason ||
128
+ "拉取全局数据失败");
129
+ if (isFail) {
130
+ setSyncStatus("failed");
131
+ setError(failReason);
132
+ var curData = dataRef.current;
133
+ if (curData && (curData.userRepos?.length > 0 || curData.myPrs?.length > 0 || curData.myIssues?.length > 0)) {
134
+ if (json.data.userRepos?.length > 0 || json.data.myPrs?.length > 0) {
135
+ setData(json.data);
136
+ }
137
+ }
138
+ else {
139
+ setData(json.data);
140
+ }
141
+ if (forceRefresh)
142
+ showToast("❌ 刷新失败:" + failReason);
143
+ }
144
+ else {
145
+ setData(json.data);
146
+ saveStoredGlobalOverview(json.data);
147
+ var syncTs = json.fromCache && json.data.lastUpdated
148
+ ? (Date.parse(json.data.lastUpdated) || Date.now())
149
+ : Date.now();
150
+ setLastSyncedAt(syncTs);
151
+ setSyncStatus(json.fromCache ? "cached" : "fresh");
152
+ setError(null);
153
+ if (forceRefresh)
154
+ showToast("✅ 刷新成功,已同步最新数据");
155
+ }
156
+ if (json.data.updateInfo) {
157
+ setUpdateInfo(json.data.updateInfo);
158
+ }
159
+ if (json.data.workspaceError) {
160
+ setError("工作区列表读取失败:" + json.data.workspaceError);
161
+ }
162
+ }
163
+ else {
164
+ setSyncStatus("failed");
165
+ var errPayload = String(json?.error || "获取全局数据失败");
166
+ setError(errPayload);
167
+ if (forceRefresh)
168
+ showToast("❌ 刷新失败:" + errPayload);
169
+ }
170
+ }
171
+ catch (err) {
172
+ setSyncStatus("failed");
173
+ var failReason = (timedOut || err.name === "AbortError")
174
+ ? "获取超时(45 秒):本机 gh 命令响应过慢或网络代理劫持了回环请求。可稍后重试,或在「⚙️ 设置 → 🌐 网络代理」确认代理已放行 127.0.0.1。"
175
+ : ("无法连接后端服务:" + (err.message || err));
176
+ setError(failReason);
177
+ if (forceRefresh)
178
+ showToast("❌ 刷新失败:" + failReason);
179
+ }
180
+ finally {
181
+ clearTimeout(timer);
182
+ setLoading(false);
183
+ if (syncSettings.strategy === "auto") {
184
+ scheduleNextCloudSync();
185
+ }
186
+ }
187
+ }, [syncSettings.strategy]);
188
+ // 云端定时自动同步:拉取结算后才启动计时器,倒计时到期后再执行下一次,并做息屏避让
189
+ var scheduleNextCloudSync = React.useCallback(() => {
190
+ if (cloudTimerRef.current)
191
+ clearTimeout(cloudTimerRef.current);
192
+ if (syncSettings.strategy !== "auto")
193
+ return;
194
+ var interval = Math.max(300000, Number(syncSettings.cloudSyncIntervalMs) || 600000);
195
+ cloudTimerRef.current = setTimeout(() => {
196
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
197
+ var onVisible = () => {
198
+ if (document.visibilityState === "visible") {
199
+ document.removeEventListener("visibilitychange", onVisible);
200
+ loadData(false);
201
+ }
202
+ };
203
+ document.addEventListener("visibilitychange", onVisible);
204
+ return;
205
+ }
206
+ loadData(false);
207
+ }, interval);
208
+ }, [syncSettings.strategy, syncSettings.cloudSyncIntervalMs, loadData]);
209
+ // 挂载加载调度:开启 SWR 或 开启云端定时时,首屏均立即静默拉新;
210
+ // 云端定时在首屏拉取结算后自动启动下一次倒计时。首页坚决不挂载任何文件监控!
211
+ React.useEffect(() => {
212
+ if (cloudTimerRef.current)
213
+ clearTimeout(cloudTimerRef.current);
214
+ if (syncSettings.strategy === "swr" || syncSettings.strategy === "auto") {
215
+ var timer = setTimeout(function () { loadData(false); }, 300);
216
+ return function () {
217
+ clearTimeout(timer);
218
+ if (cloudTimerRef.current)
219
+ clearTimeout(cloudTimerRef.current);
220
+ };
221
+ }
222
+ else if (!dataRef.current) {
223
+ loadData(false);
224
+ }
225
+ return function () {
226
+ if (cloudTimerRef.current)
227
+ clearTimeout(cloudTimerRef.current);
228
+ };
229
+ }, [syncSettings.strategy, syncSettings.cloudSyncIntervalMs, loadData]);
230
+ React.useEffect(() => {
231
+ var onFocus = () => {
232
+ if (!data?.auth?.loggedIn) {
233
+ fetch("/api/github/auth?force=true")
234
+ .then(function (res) { return res.json(); })
235
+ .then(function (json) {
236
+ if (json && json.ok && json.auth && json.auth.loggedIn) {
237
+ clearStoredGlobalOverview();
238
+ loadData(true);
239
+ }
240
+ })
241
+ .catch(function () { });
242
+ }
243
+ };
244
+ window.addEventListener("focus", onFocus);
245
+ return () => window.removeEventListener("focus", onFocus);
246
+ }, [data, loadData]);
247
+ var h = React.createElement;
248
+ 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
249
+ ? h("span", {
250
+ style: {
251
+ display: "inline-flex",
252
+ alignItems: "center",
253
+ gap: "4px",
254
+ fontSize: "11px",
255
+ fontWeight: 500,
256
+ padding: "2px 8px",
257
+ borderRadius: "999px",
258
+ background: "rgba(9, 105, 218, 0.1)",
259
+ border: "1px solid rgba(9, 105, 218, 0.35)",
260
+ color: "var(--dsw-alias-link, #0969da)",
261
+ cursor: "default",
262
+ },
263
+ title: "插件网络代理已启用: " + proxyActive.url + "(GitHub 数据链路与工具调用经此代理;点击 ⚙️ 可修改)",
264
+ }, "🌐 代理")
265
+ : null), h("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, h("button", {
266
+ className: "dsh-github-refresh-btn",
267
+ onClick: () => loadData(true),
268
+ disabled: loading,
269
+ }, loading ? "同步中..." : "🔄 刷新全局数据"), h("button", {
270
+ className: "dsh-github-btn-sm",
271
+ onClick: openSettingsDefault,
272
+ title: "配置数据同步节奏、本地缓存、网络代理与插件信息",
273
+ style: { padding: "6px 12px" },
274
+ }, "⚙️ 设置")));
275
+ if (loading && !data) {
276
+ return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, h("div", { className: "dsh-github-empty" }, "正在加载全局 GitHub 账号与工作区数据..."));
277
+ }
278
+ var effectiveUpdateInfo = updateInfo || data?.updateInfo;
279
+ var upgradeBannerEl = effectiveUpdateInfo && effectiveUpdateInfo.hasUpdate
280
+ ? h(UpgradeNotificationCard, { updateInfo: effectiveUpdateInfo, isRightbar: false })
281
+ : null;
282
+ var settingsModalEl = h(GlobalSyncSettingsModal, {
283
+ isOpen: isSettingsModalOpen,
284
+ initialSection: settingsInitialSection,
285
+ onClose: () => {
286
+ setIsSettingsModalOpen(false);
287
+ refreshProxyBadge();
288
+ },
289
+ currentSettings: syncSettings,
290
+ onSave: (newSettings) => {
291
+ setSyncSettings(newSettings);
292
+ },
293
+ onClearCache: () => {
294
+ setSyncStatus("loading");
295
+ setData(null);
296
+ setLastSyncedAt(0);
297
+ },
298
+ });
299
+ if (!data && error) {
300
+ return h("div", { className: "dsh-github-panel", ref: panelRef }, headerEl, upgradeBannerEl, h("div", {
301
+ style: {
302
+ padding: "24px",
303
+ borderRadius: "8px",
304
+ background: "rgba(248, 81, 73, 0.08)",
305
+ border: "1px solid rgba(248, 81, 73, 0.3)",
306
+ margin: "16px 0",
307
+ textAlign: "center",
308
+ },
309
+ }, h("div", { style: { fontSize: "16px", fontWeight: 600, color: "#f85149", marginBottom: "8px" } }, "⚠️ 后端服务加载异常"), h("div", { style: { fontSize: "13px", color: "var(--dsw-alias-label-secondary, #656d76)", marginBottom: "16px" } }, error), h("button", {
310
+ className: "dsh-github-btn-sm",
311
+ onClick: () => loadData(true),
312
+ style: { padding: "6px 18px", background: "var(--dsw-alias-link, #0969da)", color: "#ffffff", borderColor: "var(--dsw-alias-link, #0969da)" },
313
+ }, "🔄 重新请求")), settingsModalEl);
314
+ }
315
+ var auth = data ? data.auth : null;
316
+ var userRepos = (data && data.userRepos) || [];
317
+ var workspaceMatrix = (data && data.workspaceMatrix) || [];
318
+ var myPrs = (data && data.myPrs) || [];
319
+ var myIssues = (data && data.myIssues) || [];
320
+ 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;
321
+ 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?.networkError
322
+ ? (auth.user ? `👤 ${auth.user} (离线)` : "🌐 网络连接受限")
323
+ : auth?.loggedIn
324
+ ? `👤 ${auth.user}`
325
+ : !auth?.installed
326
+ ? "❌ 未安装 CLI"
327
+ : "⚠️ 未登录", auth?.networkError
328
+ ? h("span", { className: "dsh-github-badge dsh-badge-yellow" }, "Offline")
329
+ : auth?.loggedIn
330
+ ? h("span", { className: "dsh-github-badge dsh-badge-green" }, "Active")
331
+ : !auth?.installed
332
+ ? h("span", { className: "dsh-github-badge dsh-badge-red" }, "Missing")
333
+ : 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} 个已关联)`)), h("div", { className: "dsh-github-card" }, h("div", { className: "dsh-github-card-title" }, "📥 待处理 Issues"), (data && data.issueStats && (data.issueStats.reported || data.issueStats.assigned || data.issueStats.created))
334
+ ? h("div", { className: "dsh-github-card-value", style: { fontSize: "14px" } }, `📥 用户提单 ${data.issueStats.reported || 0} · 🎯 分配给我 ${data.issueStats.assigned || 0} · ✍️ 我创建 ${data.issueStats.created || 0}`)
335
+ : h("div", { className: "dsh-github-card-value" }, "无待办 ✓")));
336
+ var authGuideEl = (!auth?.loggedIn && !auth?.networkError && syncStatus !== "failed")
337
+ ? h(AuthGuideCard, { auth, onRefresh: () => loadData(true), isRightbar: false })
338
+ : 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})`));
340
+ var contentEl = null;
341
+ if (activeTab === 'workspaces') {
342
+ 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
343
+ ? h("span", { className: "dsh-github-ws-repo-link" }, renderOctocat(13, "currentColor"), `关联远程: ${ws.repo.nameWithOwner} (${ws.repo.defaultBranch})`)
344
+ : h("span", { className: "dsh-github-ws-path" }, ws.hasGit ? "本地有 Git,未关联 GitHub 远程" : "本地无 Git 仓库"), ws.repo ? h("span", { className: "dsh-github-badge dsh-badge-green" }, "已关联") : null), ws.gitStatus
345
+ ? h("div", {
346
+ style: { fontSize: "11px", display: "flex", gap: "8px", alignItems: "center", opacity: 0.85 },
347
+ title: "ahead/behind 相对上次 git fetch 的快照(非实时)",
348
+ }, h("span", { style: { display: "inline-flex", alignItems: "center", gap: "4px" } }, renderGitIcon(12, "#1296db"), ws.gitStatus.branch), ws.gitStatus.hasUpstream
349
+ ? h("span", null, `${ws.gitStatus.ahead > 0 ? "↑" + ws.gitStatus.ahead + " " : ""}${ws.gitStatus.behind > 0 ? "↓" + ws.gitStatus.behind : ""}${ws.gitStatus.ahead === 0 && ws.gitStatus.behind === 0 ? "✓ 同步" : ""}`)
350
+ : h("span", { style: { opacity: 0.7 } }, "↑? ↓?"), ws.gitStatus.dirtyCount > 0
351
+ ? h("span", null, `${ws.gitStatus.dirtyCount} 文件未提交`)
352
+ : h("span", null, "干净"))
353
+ : null, ws.gitStatus && ws.gitStatus.dirtyFiles && ws.gitStatus.dirtyFiles.length > 0
354
+ ? h("details", { style: { fontSize: "11px" } }, h("summary", { style: { cursor: "pointer", opacity: 0.8 } }, `未提交清单(${ws.gitStatus.dirtyFiles.length})`), h("div", { style: { padding: "4px 8px", borderRadius: "6px", background: "var(--dsw-alias-bg-module-platform, #f6f8fa)", lineHeight: "1.6", marginTop: "4px" } }, ws.gitStatus.dirtyFiles.map(function (f, i) {
355
+ return h("div", { key: i }, h("span", { style: { opacity: 0.7, marginRight: "6px" } }, f.status === "??" ? "??" : f.status), f.path);
356
+ })))
357
+ : null), h("div", { style: { display: "flex", gap: "8px" } }, ws.repo
358
+ ? h("a", { className: "dsh-github-btn-sm", href: ws.repo.url, target: "_blank" }, "在 GitHub 打开")
359
+ : null))));
360
+ }
361
+ else if (activeTab === 'repos') {
362
+ if (!auth?.loggedIn) {
363
+ contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取云端仓库列表。请先完成上方的安装或登录指引。");
364
+ }
365
+ else {
366
+ var filteredRepos = userRepos.filter((r) => !searchQuery || r.nameWithOwner.toLowerCase().includes(searchQuery.toLowerCase()) || r.description?.toLowerCase().includes(searchQuery.toLowerCase()));
367
+ contentEl = h("div", null, h("input", {
368
+ className: "dsh-github-search-input",
369
+ placeholder: "🔍 搜索我的 GitHub 仓库...",
370
+ value: searchQuery,
371
+ onChange: (e) => setSearchQuery(e.target.value)
372
+ }), 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}`), formatRelativeTime(r.updatedAt)
373
+ ? h("span", { title: `更新于 ${r.updatedAt}` }, `活跃于 ${formatRelativeTime(r.updatedAt)}`)
374
+ : null)))));
375
+ }
376
+ }
377
+ else {
378
+ if (!auth?.loggedIn) {
379
+ contentEl = h("div", { className: "dsh-github-empty" }, "当前尚未认证登录 GitHub 账号,无法读取个人待办 PR 与 Issues。请先完成上方的安装或登录指引。");
380
+ }
381
+ else {
382
+ var issueStats = (data && data.issueStats) || {
383
+ total: myIssues.length,
384
+ reported: myIssues.filter((i) => (i.categories || []).includes('reported')).length,
385
+ assigned: myIssues.filter((i) => (i.categories || []).includes('assigned')).length,
386
+ created: myIssues.filter((i) => (i.categories || []).includes('created')).length,
387
+ };
388
+ var filteredIssues = myIssues.filter((is) => {
389
+ if (issueFilter === 'all')
390
+ return true;
391
+ var cats = is.categories || [];
392
+ return cats.includes(issueFilter);
393
+ });
394
+ var issuePillsEl = h("div", { className: "dsh-github-pills" }, h("button", {
395
+ className: `dsh-github-pill-btn ${issueFilter === 'all' ? 'active' : ''}`,
396
+ onClick: () => setIssueFilter('all'),
397
+ }, `全部 (${issueStats.total || myIssues.length})`), h("button", {
398
+ className: `dsh-github-pill-btn ${issueFilter === 'reported' ? 'active' : ''}`,
399
+ onClick: () => setIssueFilter('reported'),
400
+ }, `📥 用户提起的 (${issueStats.reported || 0})`), h("button", {
401
+ className: `dsh-github-pill-btn ${issueFilter === 'assigned' ? 'active' : ''}`,
402
+ onClick: () => setIssueFilter('assigned'),
403
+ }, `🎯 分配给我的 (${issueStats.assigned || 0})`), h("button", {
404
+ className: `dsh-github-pill-btn ${issueFilter === 'created' ? 'active' : ''}`,
405
+ onClick: () => setIssueFilter('created'),
406
+ }, `✍️ 我创建的 (${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
+ })));
432
+ }
433
+ }
434
+ var toastEl = toastMsg
435
+ ? h("div", {
436
+ style: {
437
+ padding: "8px 12px",
438
+ marginBottom: "12px",
439
+ borderRadius: "6px",
440
+ fontSize: "12px",
441
+ background: toastMsg.startsWith("✅") ? "rgba(46, 160, 67, 0.15)" : "rgba(207, 34, 46, 0.15)",
442
+ color: toastMsg.startsWith("✅") ? "var(--dsw-alias-state-success, #2ea043)" : "var(--dsw-alias-state-error-primary, #cf222e)",
443
+ border: "1px solid " + (toastMsg.startsWith("✅") ? "rgba(46, 160, 67, 0.35)" : "rgba(207, 34, 46, 0.35)"),
444
+ display: "flex",
445
+ alignItems: "center",
446
+ justifyContent: "space-between",
447
+ gap: "8px",
448
+ },
449
+ }, h("span", null, toastMsg), h("button", { onClick: () => setToastMsg(null), style: { background: "none", border: "none", cursor: "pointer", color: "inherit", fontSize: "14px" } }, "✕"))
450
+ : null;
451
+ var networkAlertBannerEl = (syncStatus === "failed" || auth?.networkError || (error && data))
452
+ ? h(NetworkAlertBanner, {
453
+ isRightbar: false,
454
+ networkErrorReason: auth?.networkErrorReason || error || "连接 GitHub API 超时或网络不可达,本次未能获取最新数据。",
455
+ lastSyncedAt,
456
+ onRefresh: () => loadData(true),
457
+ onOpenProxy: function () {
458
+ setSettingsInitialSection("proxy");
459
+ setIsSettingsModalOpen(true);
460
+ },
461
+ })
462
+ : null;
463
+ return h("div", { className: "dsh-github-panel", ref: panelRef }, toastEl, headerEl, networkAlertBannerEl, upgradeBannerEl, errorEl, statsCardsEl, authGuideEl, tabsEl, contentEl, settingsModalEl);
464
+ }
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.2.1";
9
+ export declare const CURRENT_PLUGIN_VERSION = "0.4.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.2.1';
9
+ export const CURRENT_PLUGIN_VERSION = '0.4.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,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-github-flow",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "icon": "icon.svg",
5
5
  "description": "GitHub 工作流引擎:为 DeepSeek Harness 提供原生 GitHub CLI (gh) 集成——9 大 Agent 领域工具(PR 审查合并、Issue 任务流转、CI 失败诊断、发版交付、密钥管理、Agent Skills 生态)、/gh 人类快捷指令、Web UI 全景驾驶舱与一键跨平台自动升级;零凭证侵入复用宿主机 gh 登录态,内置 Token 防溢出截断、高危操作二次确认与密文安全输入通道。",
6
6
  "type": "module",