diff --git a/internal/agent/core/status.go b/internal/agent/core/status.go index e855d4a..6722977 100644 --- a/internal/agent/core/status.go +++ b/internal/agent/core/status.go @@ -103,6 +103,9 @@ func collectKernelStatus( KernelName: meta.KernelName, // AGPL-3.0 §13:状态页向网络使用者展示取得源码的入口。 SourceURL: meta.SourceURL, + // 许可标识与全文:与源码链接一起构成「这是什么许可 + 怎么拿到源码」。 + License: meta.License, + LicenseURL: meta.LicenseURL, }, Runtime: RuntimeStatus{ Goroutines: runtime.NumGoroutine(), diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 8f73cc5..0ecf857 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -65,6 +65,22 @@ var ( // 构建时可用 -ldflags 覆盖,无需改源码: // -X gitcode.com/JianFeeeee/HomeAgent/internal/meta.SourceURL=<你的仓库> SourceURL = "https://gitcode.com/JianFeeeee/HomeAgent" + + // License 是本项目的开源许可标识(SPDX 表达式)。 + // + // AGPL-3.0-only 带**网络条款**:把修改过的版本作为网络服务对外提供时, + // 必须给使用者提供取得 Corresponding Source 的机会(§13)。所以 WebUI + // 总览底部把「许可标识 + 许可全文 + 源码仓库」三者一起渲染成可见的许可卡—— + // 只给一个源码链接、不标明协议名,使用者在界面上根本看不出这是什么许可。 + License = "AGPL-3.0-only" + + // LicenseURL 是上述许可的全文地址。 + // + // 默认指向 GNU 官方的 AGPL-3.0 全文页:与仓库托管方、分支名、文件路径都 + // 无关,换仓库/换分支都不会失效。若你的发行版把 LICENSE 放在别处, + // 用 -ldflags 覆盖即可: + // -X gitcode.com/JianFeeeee/HomeAgent/internal/meta.LicenseURL=<你的链接> + LicenseURL = "https://www.gnu.org/licenses/agpl-3.0.html" ) // FullVersion 返回完整的版本字符串。 diff --git a/internal/plugins/webui/dashboard.css b/internal/plugins/webui/dashboard.css index cbd1e3d..7dc96c2 100644 --- a/internal/plugins/webui/dashboard.css +++ b/internal/plugins/webui/dashboard.css @@ -2855,9 +2855,15 @@ .ov-dot.bad { background: var(--error); } - .ov-foot { - margin-top: 10px; + /* 开源许可卡:协议标识 + 协议全文 + 源码仓库。 + 此前只是 KPI 卡底部一行小链接(.ov-foot),在长页面里基本看不见。 */ + .ov-legal h2 { + margin-bottom: 6px; + } + .ov-legal-note { + margin: 10px 0 0; font-size: 11px; + line-height: 1.55; color: var(--text-muted); } diff --git a/internal/plugins/webui/dashboard.js b/internal/plugins/webui/dashboard.js index 44f91bc..1e61c96 100644 --- a/internal/plugins/webui/dashboard.js +++ b/internal/plugins/webui/dashboard.js @@ -1378,7 +1378,10 @@ ovKpi("memory", OV_ICONS.db, __("记忆", "Memory")) + ovKpi("docs", OV_ICONS.file, __("文档", "Docs")) + ovKpi("runtime", OV_ICONS.cpu, __("运行时", "Runtime")) + - '
'; + '' + + // 开源许可单独成框:之前在 KPI 卡底部只是一行小链接(.ov-foot), + // 几乎看不见,也看不出是许可证还是别的什么。 + ''; } updateOverview(); } @@ -1427,20 +1430,55 @@ "ov-runtime", rt.goroutines ? rt.goroutines + (rt.memory_mb ? " · " + rt.memory_mb + "M" : "") : "—", ); - // 源码链接(AGPL §13):静态文本,第一次填好后不参与轮询刷新。 - var foot = document.getElementById("ov-foot"); - if (foot) { - var src = k && k.build && k.build.source_url; - var want = src - ? '' + + __("许可协议", "License") + + '' + + (licURL + ? '' + escHtml(lic) + "" + : escHtml(lic)) + + ""; + } + if (src) { + rows += + '
' + + __("源码仓库", "Source") + + '' + - __("源码", "Source") + - "" + escHtml(src) + + "
"; + } + // 网络条款只在 AGPL 系的许可下才成立,所以按标识判断,不硬写协议名。 + var note = + lic && lic.toUpperCase().indexOf("AGPL") >= 0 + ? '" + : ""; + var want = rows + ? '

' + __("开源许可", "License") + "

" + rows + note : ""; - if (foot.__html !== want) { - foot.__html = want; - foot.innerHTML = want; + if (legal.__html !== want) { + legal.__html = want; + legal.innerHTML = want; } } } diff --git a/internal/sdk/status.go b/internal/sdk/status.go index e83e8d1..79c628d 100644 --- a/internal/sdk/status.go +++ b/internal/sdk/status.go @@ -251,6 +251,13 @@ type BuildStatus struct { // 服务时,要给他们拿到 Corresponding Source 的机会——WebUI 状态页会把它渲染成 // 可见链接,所以**修改后对外部署的分支必须把这个值改指向自己的源码仓库**。 SourceURL string `json:"source_url,omitempty"` + // License 是开源许可标识(SPDX,如 AGPL-3.0-only)。 + // + // 为何与 SourceURL 分开:只看一个源码链接,使用者没法从界面上看出这是什么 + // 许可、网络服务场景下有哪些义务。许可名与许可全文地址是两个独立事实。 + License string `json:"license,omitempty"` + // LicenseURL 是 License 对应的全文地址。 + LicenseURL string `json:"license_url,omitempty"` } type TrackerStatus struct {