@kernel-sig/console 0.1.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 (198) hide show
  1. package/.env.example +50 -0
  2. package/LICENSE +127 -0
  3. package/Makefile +66 -0
  4. package/README.md +279 -0
  5. package/backend/.dockerignore +12 -0
  6. package/backend/Dockerfile +43 -0
  7. package/backend/alembic/env.py +75 -0
  8. package/backend/alembic/script.py.mako +25 -0
  9. package/backend/alembic/versions/0001_initial.py +88 -0
  10. package/backend/alembic/versions/350e2d9b6553_add_pr_comment_table.py +48 -0
  11. package/backend/alembic/versions/4f8342e727fe_sig_info_roster_fields_and_branch_.py +39 -0
  12. package/backend/alembic/versions/5a31ade90136_add_repository_credential_pull_request_.py +293 -0
  13. package/backend/alembic/versions/87a008142f17_add_classification_attention_and_ai_.py +436 -0
  14. package/backend/alembic/versions/a1c7d90e4b52_branch_belongs_to_a_repository.py +72 -0
  15. package/backend/alembic/versions/b41c9d7e5f28_extend_pr_kind_categories.py +43 -0
  16. package/backend/alembic/versions/c8e4f2a71b93_add_release_kind.py +40 -0
  17. package/backend/alembic/versions/d1275091dfdc_add_needs_detail_flag_to_pull_request.py +44 -0
  18. package/backend/alembic/versions/e5b27c9d3a41_downgrade_cve_without_ids.py +54 -0
  19. package/backend/alembic/versions/ee3159a4eff0_add_sig_meeting_member_and_release_.py +164 -0
  20. package/backend/alembic/versions/fcf3c186d63b_attention_rule_subscriptions.py +42 -0
  21. package/backend/alembic.ini +40 -0
  22. package/backend/app/__init__.py +0 -0
  23. package/backend/app/api/__init__.py +0 -0
  24. package/backend/app/api/deps.py +74 -0
  25. package/backend/app/api/v1/__init__.py +0 -0
  26. package/backend/app/api/v1/ai.py +386 -0
  27. package/backend/app/api/v1/analytics.py +48 -0
  28. package/backend/app/api/v1/attention.py +142 -0
  29. package/backend/app/api/v1/auth.py +106 -0
  30. package/backend/app/api/v1/classification.py +192 -0
  31. package/backend/app/api/v1/health.py +29 -0
  32. package/backend/app/api/v1/issues.py +143 -0
  33. package/backend/app/api/v1/pulls.py +388 -0
  34. package/backend/app/api/v1/repositories.py +273 -0
  35. package/backend/app/api/v1/router.py +32 -0
  36. package/backend/app/api/v1/sig.py +565 -0
  37. package/backend/app/api/v1/users.py +87 -0
  38. package/backend/app/api/v1/webhooks.py +135 -0
  39. package/backend/app/core/__init__.py +0 -0
  40. package/backend/app/core/config.py +72 -0
  41. package/backend/app/core/crypto.py +74 -0
  42. package/backend/app/core/db.py +79 -0
  43. package/backend/app/core/exceptions.py +60 -0
  44. package/backend/app/core/logging.py +69 -0
  45. package/backend/app/core/permissions.py +87 -0
  46. package/backend/app/core/queue.py +57 -0
  47. package/backend/app/core/security.py +69 -0
  48. package/backend/app/domain/__init__.py +0 -0
  49. package/backend/app/domain/attention.py +618 -0
  50. package/backend/app/domain/classification.py +689 -0
  51. package/backend/app/domain/meeting.py +408 -0
  52. package/backend/app/domain/release.py +163 -0
  53. package/backend/app/domain/review.py +416 -0
  54. package/backend/app/domain/sig.py +178 -0
  55. package/backend/app/domain/sig_info.py +201 -0
  56. package/backend/app/integrations/__init__.py +0 -0
  57. package/backend/app/integrations/atomgit/__init__.py +0 -0
  58. package/backend/app/integrations/atomgit/client.py +505 -0
  59. package/backend/app/integrations/atomgit/models.py +311 -0
  60. package/backend/app/integrations/llm/__init__.py +0 -0
  61. package/backend/app/integrations/llm/prompts.py +222 -0
  62. package/backend/app/integrations/llm/provider.py +326 -0
  63. package/backend/app/main.py +242 -0
  64. package/backend/app/middleware/__init__.py +0 -0
  65. package/backend/app/middleware/audit.py +129 -0
  66. package/backend/app/middleware/request_context.py +42 -0
  67. package/backend/app/models/__init__.py +112 -0
  68. package/backend/app/models/ai.py +214 -0
  69. package/backend/app/models/attention.py +131 -0
  70. package/backend/app/models/attention_settings.py +61 -0
  71. package/backend/app/models/audit.py +53 -0
  72. package/backend/app/models/base.py +35 -0
  73. package/backend/app/models/classification.py +144 -0
  74. package/backend/app/models/credential.py +56 -0
  75. package/backend/app/models/issue.py +132 -0
  76. package/backend/app/models/meeting.py +189 -0
  77. package/backend/app/models/pull_request.py +288 -0
  78. package/backend/app/models/repository.py +196 -0
  79. package/backend/app/models/sig.py +194 -0
  80. package/backend/app/models/user.py +44 -0
  81. package/backend/app/schemas/__init__.py +0 -0
  82. package/backend/app/schemas/ai.py +120 -0
  83. package/backend/app/schemas/attention.py +43 -0
  84. package/backend/app/schemas/auth.py +26 -0
  85. package/backend/app/schemas/classification.py +58 -0
  86. package/backend/app/schemas/common.py +43 -0
  87. package/backend/app/schemas/pull_request.py +214 -0
  88. package/backend/app/schemas/repository.py +94 -0
  89. package/backend/app/schemas/sig.py +246 -0
  90. package/backend/app/schemas/user.py +79 -0
  91. package/backend/app/services/__init__.py +0 -0
  92. package/backend/app/services/ai_service.py +569 -0
  93. package/backend/app/services/analytics_service.py +390 -0
  94. package/backend/app/services/attention_queue.py +451 -0
  95. package/backend/app/services/attention_service.py +432 -0
  96. package/backend/app/services/auth_service.py +74 -0
  97. package/backend/app/services/classification_service.py +658 -0
  98. package/backend/app/services/credential_service.py +105 -0
  99. package/backend/app/services/pull_query.py +273 -0
  100. package/backend/app/services/release_service.py +446 -0
  101. package/backend/app/services/repository_service.py +132 -0
  102. package/backend/app/services/sig_service.py +385 -0
  103. package/backend/app/services/sync_service.py +752 -0
  104. package/backend/app/services/user_service.py +68 -0
  105. package/backend/app/worker.py +389 -0
  106. package/backend/entrypoint.sh +10 -0
  107. package/backend/pyproject.toml +68 -0
  108. package/backend/tests/test_analysis_api.py +154 -0
  109. package/backend/tests/test_atomgit_client.py +360 -0
  110. package/backend/tests/test_atomgit_models.py +257 -0
  111. package/backend/tests/test_attention.py +291 -0
  112. package/backend/tests/test_audit_middleware.py +135 -0
  113. package/backend/tests/test_classification.py +498 -0
  114. package/backend/tests/test_config.py +41 -0
  115. package/backend/tests/test_crypto.py +68 -0
  116. package/backend/tests/test_exceptions.py +62 -0
  117. package/backend/tests/test_health.py +63 -0
  118. package/backend/tests/test_llm_provider.py +320 -0
  119. package/backend/tests/test_meeting_domain.py +169 -0
  120. package/backend/tests/test_permissions.py +69 -0
  121. package/backend/tests/test_pull_query_wiring.py +66 -0
  122. package/backend/tests/test_pull_schemas.py +82 -0
  123. package/backend/tests/test_release_domain.py +82 -0
  124. package/backend/tests/test_review_parser.py +301 -0
  125. package/backend/tests/test_schemas_user.py +97 -0
  126. package/backend/tests/test_security.py +92 -0
  127. package/cli/index.js +338 -0
  128. package/compose.yaml +105 -0
  129. package/frontend/.dockerignore +4 -0
  130. package/frontend/Dockerfile +27 -0
  131. package/frontend/index.html +14 -0
  132. package/frontend/nginx.conf +47 -0
  133. package/frontend/package-lock.json +5020 -0
  134. package/frontend/package.json +35 -0
  135. package/frontend/src/api/ai.ts +124 -0
  136. package/frontend/src/api/analytics.ts +66 -0
  137. package/frontend/src/api/attention.ts +181 -0
  138. package/frontend/src/api/auth.ts +74 -0
  139. package/frontend/src/api/classification.ts +170 -0
  140. package/frontend/src/api/issues.ts +90 -0
  141. package/frontend/src/api/pulls.ts +233 -0
  142. package/frontend/src/api/repositories.ts +95 -0
  143. package/frontend/src/api/sig.ts +232 -0
  144. package/frontend/src/app/antd-theme.ts +94 -0
  145. package/frontend/src/app/providers.tsx +59 -0
  146. package/frontend/src/app/router.tsx +411 -0
  147. package/frontend/src/app/search.ts +30 -0
  148. package/frontend/src/components/ClassificationBadge.tsx +58 -0
  149. package/frontend/src/components/GateBadge.tsx +13 -0
  150. package/frontend/src/components/SeverityBadge.tsx +20 -0
  151. package/frontend/src/components/layout/AppShell.tsx +16 -0
  152. package/frontend/src/components/layout/AuthLayout.tsx +32 -0
  153. package/frontend/src/components/layout/Sidebar.tsx +223 -0
  154. package/frontend/src/components/layout/TopBar.tsx +47 -0
  155. package/frontend/src/components/pulls/DiscussionTimeline.tsx +145 -0
  156. package/frontend/src/components/pulls/FacetRail.tsx +199 -0
  157. package/frontend/src/components/pulls/LabelChips.tsx +87 -0
  158. package/frontend/src/components/ui/alert.tsx +30 -0
  159. package/frontend/src/components/ui/badge.tsx +53 -0
  160. package/frontend/src/components/ui/button.tsx +51 -0
  161. package/frontend/src/components/ui/card.tsx +64 -0
  162. package/frontend/src/components/ui/chart-theme.ts +65 -0
  163. package/frontend/src/components/ui/data-table.tsx +39 -0
  164. package/frontend/src/components/ui/echart.tsx +70 -0
  165. package/frontend/src/components/ui/empty-state.tsx +22 -0
  166. package/frontend/src/components/ui/input.tsx +39 -0
  167. package/frontend/src/components/ui/lazy-chart.tsx +21 -0
  168. package/frontend/src/components/ui/skeleton.tsx +19 -0
  169. package/frontend/src/hooks/use-current-repository.ts +44 -0
  170. package/frontend/src/hooks/use-current-user.ts +38 -0
  171. package/frontend/src/lib/api-client.ts +93 -0
  172. package/frontend/src/lib/css-color.ts +60 -0
  173. package/frontend/src/lib/utils.ts +45 -0
  174. package/frontend/src/main.tsx +22 -0
  175. package/frontend/src/pages/attention/AttentionQueuePage.tsx +316 -0
  176. package/frontend/src/pages/attention/RuleSettingsPanel.tsx +177 -0
  177. package/frontend/src/pages/branches/BranchDetailPage.tsx +632 -0
  178. package/frontend/src/pages/branches/BranchListPage.tsx +308 -0
  179. package/frontend/src/pages/dashboard/DashboardPage.tsx +657 -0
  180. package/frontend/src/pages/issues/IssueListPage.tsx +284 -0
  181. package/frontend/src/pages/login/LoginPage.tsx +95 -0
  182. package/frontend/src/pages/meetings/MeetingDetailPage.tsx +403 -0
  183. package/frontend/src/pages/meetings/MeetingListPage.tsx +264 -0
  184. package/frontend/src/pages/members/MembersPage.tsx +534 -0
  185. package/frontend/src/pages/pulls/PullDetailPage.tsx +833 -0
  186. package/frontend/src/pages/pulls/PullListPage.tsx +399 -0
  187. package/frontend/src/pages/settings/AiSettingsPage.tsx +449 -0
  188. package/frontend/src/pages/settings/SettingsPage.tsx +321 -0
  189. package/frontend/src/pages/setup/SetupPage.tsx +144 -0
  190. package/frontend/src/styles/globals.css +147 -0
  191. package/frontend/tsconfig.json +22 -0
  192. package/frontend/vite.config.ts +57 -0
  193. package/package.json +48 -0
  194. package/scripts/e2e-auth-flow.py +131 -0
  195. package/scripts/e2e-pr-detail.py +128 -0
  196. package/scripts/e2e-verify.py +773 -0
  197. package/scripts/verify-ai-pipeline.py +616 -0
  198. package/scripts/verify-analysis-pipeline.py +303 -0
@@ -0,0 +1,177 @@
1
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
2
+ import { RotateCcw } from 'lucide-react'
3
+ import { InputNumber, Select } from 'antd'
4
+
5
+ import { attentionApi, attentionQueueApi, type AttentionRule, type AttentionRuleSetting } from '@/api/attention'
6
+ import { Badge } from '@/components/ui/badge'
7
+ import { Button } from '@/components/ui/button'
8
+ import { Skeleton } from '@/components/ui/skeleton'
9
+ import { can, useCurrentUser } from '@/hooks/use-current-user'
10
+ import { cn, formatNumber } from '@/lib/utils'
11
+
12
+ /**
13
+ * 关注规则的订阅设置。
14
+ *
15
+ * 阈值写死在代码里的结果是没人用这个功能:第一条不符合自己节奏的提醒
16
+ * 就会让人关掉整个队列。所以开关、阈值、适用对象都放在这里由使用者决定。
17
+ *
18
+ * 每条规则旁边给出当前条数 —— 关掉一条规则之前,应该先看到它拦下了多少东西。
19
+ * 设置只影响下一次重算(后端每 15 分钟一次),所以这里同时给出「立即重算」:
20
+ * 只告诉使用者"等一会儿"却不给催的办法,改完没看到变化就会被当成没生效。
21
+ */
22
+ export function RuleSettingsPanel({ repositoryId }: { repositoryId: string }) {
23
+ const queryClient = useQueryClient()
24
+ const { user } = useCurrentUser()
25
+
26
+ const { data: settings, isLoading } = useQuery({
27
+ queryKey: ['attention-rule-settings'],
28
+ queryFn: attentionQueueApi.ruleSettings,
29
+ })
30
+
31
+ const refresh = () => {
32
+ queryClient.invalidateQueries({ queryKey: ['attention-rule-settings'] })
33
+ queryClient.invalidateQueries({ queryKey: ['attention-queue'] })
34
+ queryClient.invalidateQueries({ queryKey: ['attention-summary'] })
35
+ }
36
+
37
+ const update = useMutation({
38
+ mutationFn: ({
39
+ rule,
40
+ patch,
41
+ }: {
42
+ rule: AttentionRule
43
+ patch: { enabled?: boolean; days?: number; subject_scope?: string }
44
+ }) => attentionQueueApi.updateRuleSetting(rule, patch),
45
+ onSuccess: refresh,
46
+ })
47
+
48
+ const recompute = useMutation({
49
+ mutationFn: () => attentionApi.recompute(repositoryId),
50
+ onSuccess: refresh,
51
+ })
52
+
53
+ if (isLoading) return <Skeleton className="h-40 w-full" />
54
+
55
+ return (
56
+ <section className="rounded-[var(--radius-card)] border border-[var(--color-border-subtle)] bg-[var(--color-surface-1)]">
57
+ <header className="flex flex-wrap items-center justify-between gap-2 border-b border-[var(--color-border-subtle)] px-4 py-3">
58
+ <h2 className="text-sm font-semibold">关注设置</h2>
59
+ <div className="flex items-center gap-3">
60
+ <p className="text-xs text-[var(--color-text-muted)]">
61
+ 改动在关注项下一次重算后生效(后台每 15 分钟一次)
62
+ </p>
63
+ {can(user, 'classify') && (
64
+ <Button
65
+ variant="secondary"
66
+ size="sm"
67
+ loading={recompute.isPending}
68
+ onClick={() => recompute.mutate()}
69
+ >
70
+ 立即重算
71
+ </Button>
72
+ )}
73
+ </div>
74
+ </header>
75
+
76
+ {update.isError && (
77
+ <p className="border-b border-[var(--color-border-subtle)] px-4 py-2 text-xs text-[var(--color-danger)]">
78
+ 保存失败,改动未生效。请稍后重试。
79
+ </p>
80
+ )}
81
+
82
+ <ul className="divide-y divide-[var(--color-border-subtle)]">
83
+ {(settings ?? []).map((setting) => (
84
+ <SettingRow
85
+ key={setting.rule}
86
+ setting={setting}
87
+ pending={update.isPending}
88
+ onChange={(patch) => update.mutate({ rule: setting.rule, patch })}
89
+ />
90
+ ))}
91
+ </ul>
92
+ </section>
93
+ )
94
+ }
95
+
96
+ function SettingRow({
97
+ setting,
98
+ pending,
99
+ onChange,
100
+ }: {
101
+ setting: AttentionRuleSetting
102
+ pending: boolean
103
+ onChange: (patch: { enabled?: boolean; days?: number; subject_scope?: string }) => void
104
+ }) {
105
+ return (
106
+ <li className={cn('flex flex-wrap items-start gap-x-4 gap-y-2 px-4 py-3', !setting.enabled && 'opacity-60')}>
107
+ <label className="flex min-w-[220px] flex-1 cursor-pointer items-start gap-2.5">
108
+ <input
109
+ type="checkbox"
110
+ checked={setting.enabled}
111
+ disabled={pending}
112
+ onChange={(event) => onChange({ enabled: event.target.checked })}
113
+ className="mt-1 size-3.5 shrink-0 accent-[var(--color-accent)]"
114
+ />
115
+ <span className="min-w-0">
116
+ <span className="flex flex-wrap items-center gap-2">
117
+ <span className="text-sm font-medium">{setting.label}</span>
118
+ <Badge tone="neutral" className="font-normal">
119
+ {setting.subject === 'issue' ? 'Issue' : 'PR'}
120
+ </Badge>
121
+ <span className="font-mono text-xs tabular-nums text-[var(--color-text-muted)]">
122
+ {formatNumber(setting.open_count)}
123
+ </span>
124
+ </span>
125
+ <span className="mt-0.5 block text-xs leading-relaxed text-[var(--color-text-muted)]">
126
+ {setting.meaning}
127
+ </span>
128
+ </span>
129
+ </label>
130
+
131
+ <div className="flex shrink-0 items-center gap-2">
132
+ <label className="flex items-center gap-1.5 text-xs text-[var(--color-text-muted)]">
133
+ 阈值
134
+ {/* InputNumber 而不是 type=number 的输入框:原生数字输入框在
135
+ 清空时给出的是空字符串,而 `Number('')` 是 0 —— 阈值被清空一次
136
+ 就静默变成"0 天",那条规则从此不再触发任何东西。 */}
137
+ <InputNumber
138
+ aria-label={`${setting.label} 阈值天数`}
139
+ size="small"
140
+ min={0}
141
+ max={3650}
142
+ value={setting.days}
143
+ disabled={pending || !setting.enabled}
144
+ // 空值不提交:受控的 InputNumber 会把当前值显示回去,也就是
145
+ // "清空这一步不算数"。这比让它落到 0 好。
146
+ onChange={(value) => value != null && onChange({ days: value })}
147
+ className="w-20"
148
+ />
149
+
150
+ </label>
151
+ <Select
152
+ aria-label={`${setting.label} 适用范围`}
153
+ size="small"
154
+ value={setting.subject_scope}
155
+ disabled={pending || !setting.enabled}
156
+ options={[
157
+ { value: 'all', label: '全部' },
158
+ { value: 'pull_request', label: '仅 PR' },
159
+ { value: 'issue', label: '仅 Issue' },
160
+ ]}
161
+ onChange={(value) => onChange({ subject_scope: value })}
162
+ />
163
+ {setting.days !== setting.default_days && (
164
+ <button
165
+ type="button"
166
+ onClick={() => onChange({ days: setting.default_days })}
167
+ title={`恢复默认值 ${setting.default_days} 天`}
168
+ className="inline-flex items-center gap-1 text-2xs text-[var(--color-text-muted)] hover:text-[var(--color-accent)]"
169
+ >
170
+ <RotateCcw className="size-3" aria-hidden />
171
+ 默认
172
+ </button>
173
+ )}
174
+ </div>
175
+ </li>
176
+ )
177
+ }