mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 23:16:37 +08:00
后端与全仓代码质量清理(golangci 扩展集 · 测试质量 · 并发安全 · 文档同步)
代码质量全量清理,零行为变化:golangci 扩展集 13 类 linter(gosec/modernize/perfsprint/canonicalheader/usestdlibvars/wastedassign/intrange/errorlint/forcetypeassert/recvcheck/exhaustive/unparam)全量修复,测试代码质量(testifylint/thelper/usetesting)25→0,frpc 进程生命周期真 bug(进程组击杀)、全仓 go test -race 6 类数据竞争(含 1 个生产竞争)、SPDX license 头补齐 131 文件、前端测试套件 next-intl 迁移后 44 失败→全绿、过期 swagger 文档重新生成、pnpm-workspace 构建审批。 Experiments: #2-#17, #18, #20, #21, #23 Metric: total_issues 108 → 8 (-92.6%)
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package agent implements the local OpenFlare agent runtime loop.
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package config loads and persists agent daemon configuration.
|
||||
package config
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
|
||||
import edgeconfig "github.com/Rain-kl/Wavelet/internal/apps/edge/config"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
|
||||
// Version is the current agent version string, overridden at build time.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package geoipdata holds shared GeoIP database filename constants.
|
||||
//
|
||||
// MaxMind MMDB files are NOT embedded into the agent binary. Docker images
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package geoipupdate schedules local MaxMind GeoIP database updates for the agent.
|
||||
package geoipupdate
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package geoipupdate
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package heartbeat implements the periodic heartbeat cycle executed by the agent,
|
||||
// including payload preparation, config sync, WAF IP group application, and observability buffering.
|
||||
package heartbeat
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package heartbeat
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package logging configures structured logging for the agent process.
|
||||
package logging
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package nginx manages OpenResty configuration, runtime, and supporting assets.
|
||||
package nginx
|
||||
|
||||
@@ -134,7 +137,7 @@ func (e *PathExecutor) Reload(ctx context.Context) error {
|
||||
slog.Warn("openresty reload reported runtime is not running, starting binary", "path", e.Path)
|
||||
startOutput, startErr := e.Runner.Run(ctx, e.Path, "-c", e.ConfigPath)
|
||||
if startErr != nil {
|
||||
return fmt.Errorf("openresty reload failed: %w: %s; start failed: %v: %s", err, string(output), startErr, string(startOutput))
|
||||
return fmt.Errorf("openresty reload failed: %w: %s; start failed: %w: %s", err, string(output), startErr, string(startOutput))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -355,14 +358,14 @@ func (m *Manager) activateConfig(ctx context.Context) error {
|
||||
func (m *Manager) rollbackAfterFailedApply(ctx context.Context, backup *backupState, applyErr error) ApplyOutcome {
|
||||
slog.Warn("openresty apply failed, restoring previous config", "error", applyErr)
|
||||
if err := m.restore(backup); err != nil {
|
||||
return fatalApplyOutcome(fmt.Errorf("restore openresty backup failed after apply error %v: %w", applyErr, err))
|
||||
return fatalApplyOutcome(fmt.Errorf("restore openresty backup failed after apply error %w: %w", applyErr, err))
|
||||
}
|
||||
if err := m.activateConfig(ctx); err != nil {
|
||||
if backup != nil && backup.MainExisted {
|
||||
return fatalApplyOutcome(fmt.Errorf("apply failed: %v; rollback recovery failed: %w", applyErr, err))
|
||||
return fatalApplyOutcome(fmt.Errorf("apply failed: %w; rollback recovery failed: %w", applyErr, err))
|
||||
}
|
||||
if fallbackErr := m.EnsureSafeFallbackRuntime(ctx, fmt.Sprintf("apply failed: %v; rollback recovery failed: %v", applyErr, err)); fallbackErr != nil {
|
||||
return fatalApplyOutcome(fmt.Errorf("apply failed: %v; rollback recovery failed: %w; fallback recovery failed: %v", applyErr, err, fallbackErr))
|
||||
return fatalApplyOutcome(fmt.Errorf("apply failed: %w; rollback recovery failed: %w; fallback recovery failed: %w", applyErr, err, fallbackErr))
|
||||
}
|
||||
message := fmt.Sprintf("apply failed, but fallback runtime started: %v; rollback recovery failed: %v", applyErr, err)
|
||||
slog.Warn("openresty apply recovered with safe default fallback", "message", message)
|
||||
@@ -516,7 +519,7 @@ func (m *Manager) CurrentChecksum() (string, error) {
|
||||
normalizedMain = strings.ReplaceAll(normalizedMain, listen, openrestyrender.ObservabilityListenPlaceholder)
|
||||
}
|
||||
if m.OpenrestyObservabilityPort > 0 {
|
||||
normalizedMain = strings.ReplaceAll(normalizedMain, fmt.Sprintf("%d", m.OpenrestyObservabilityPort), openrestyrender.ObservabilityPortPlaceholder)
|
||||
normalizedMain = strings.ReplaceAll(normalizedMain, strconv.Itoa(m.OpenrestyObservabilityPort), openrestyrender.ObservabilityPortPlaceholder)
|
||||
}
|
||||
if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" {
|
||||
normalizedMain = strings.ReplaceAll(normalizedMain, resolverDirective, ResolverDirectivePlaceholder)
|
||||
@@ -1463,7 +1466,7 @@ func (m *Manager) renderMainConfig(content string) string {
|
||||
rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityListenPlaceholder, listen)
|
||||
}
|
||||
if m.OpenrestyObservabilityPort > 0 {
|
||||
rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityPortPlaceholder, fmt.Sprintf("%d", m.OpenrestyObservabilityPort))
|
||||
rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityPortPlaceholder, strconv.Itoa(m.OpenrestyObservabilityPort))
|
||||
}
|
||||
if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" {
|
||||
rendered = strings.ReplaceAll(rendered, ResolverDirectivePlaceholder, resolverDirective)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
// DefaultMimeTypes is the embedded nginx mime.types map used by generated configs.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import "github.com/Rain-kl/Wavelet/internal/apps/agent/protocol"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nginx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package observability provides system and service level observability data collection for the agent.
|
||||
package observability
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observability
|
||||
|
||||
import (
|
||||
@@ -62,7 +65,7 @@ func CollectEdgeHealth(ctx context.Context, cfg *config.Config) *EdgeHealthSnaps
|
||||
}
|
||||
|
||||
func fetchLocalJSON(ctx context.Context, client *http.Client, url string, target any) error {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observability
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observability
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package observability
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package protocol defines type aliases and constants for the agent protocol.
|
||||
package protocol
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package runtimeuser defines the shared OS account used by the agent process
|
||||
// and OpenResty worker processes so file ownership stays aligned.
|
||||
package runtimeuser
|
||||
@@ -120,7 +123,7 @@ func ensureWorldTraversablePath(targetDir string) error {
|
||||
if current == "" || current == "." {
|
||||
return nil
|
||||
}
|
||||
for depth := 0; depth < maxDepth; depth++ {
|
||||
for range maxDepth {
|
||||
if err := os.Chmod(current, DefaultDirPerm); err != nil { //nolint:gosec // parent dirs must be traversable by the runtime user
|
||||
if os.IsNotExist(err) || os.IsPermission(err) {
|
||||
break
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runtimeuser
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//go:build unix
|
||||
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runtimeuser
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package state persists agent runtime state and observability snapshots.
|
||||
package state
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/agent/protocol"
|
||||
@@ -198,7 +199,7 @@ func (s *Service) ensurePagesProject(ctx context.Context, snapshot *state.Snapsh
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < pagesLatestPullAttempts; attempt++ {
|
||||
for attempt := range pagesLatestPullAttempts {
|
||||
latest, err := s.client.GetPagesProjectLatestHash(ctx, projectID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch Pages project %d latest hash: %w", projectID, err)
|
||||
@@ -359,10 +360,7 @@ func validatePagesPackageMetadata(
|
||||
if extractedBytes == 0 {
|
||||
extractedBytes = 1
|
||||
}
|
||||
maxFileBytes := extractedBytes
|
||||
if maxFileBytes > agentPagesMaxFileBytes {
|
||||
maxFileBytes = agentPagesMaxFileBytes
|
||||
}
|
||||
maxFileBytes := min(extractedBytes, agentPagesMaxFileBytes)
|
||||
|
||||
return pagesPackageLimits{
|
||||
PackageBytes: metadata.PackageSize,
|
||||
@@ -395,7 +393,7 @@ func (s *Service) downloadPagesProjectPackage(
|
||||
metadata *protocol.PagesProjectLatestHashResponse,
|
||||
maxBytes int64,
|
||||
) (packagePath string, hash string, err error) {
|
||||
releasesRoot := filepath.Join(s.pagesDir, "projects", fmt.Sprintf("%d", projectID), "releases")
|
||||
releasesRoot := filepath.Join(s.pagesDir, "projects", strconv.FormatUint(uint64(projectID), 10), "releases")
|
||||
if err := os.MkdirAll(releasesRoot, pagesDirPerm); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -444,7 +442,7 @@ func cleanupPagesProjectStaleReleases(baseDir string, projectID uint, keepHash s
|
||||
if projectID == 0 || keepHash == "" {
|
||||
return nil
|
||||
}
|
||||
releasesRoot := filepath.Join(baseDir, "projects", fmt.Sprintf("%d", projectID), "releases")
|
||||
releasesRoot := filepath.Join(baseDir, "projects", strconv.FormatUint(uint64(projectID), 10), "releases")
|
||||
entries, err := os.ReadDir(releasesRoot) //nolint:gosec // managed PagesDir
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -985,9 +983,9 @@ func writePagesMarker(dir string, project pagesProjectRef) error {
|
||||
}
|
||||
|
||||
func pagesProjectCurrentDir(baseDir string, projectID uint) string {
|
||||
return filepath.Join(baseDir, "projects", fmt.Sprintf("%d", projectID), "current")
|
||||
return filepath.Join(baseDir, "projects", strconv.FormatUint(uint64(projectID), 10), "current")
|
||||
}
|
||||
|
||||
func pagesProjectReleaseDir(baseDir string, projectID uint, checksum string) string {
|
||||
return filepath.Join(baseDir, "projects", fmt.Sprintf("%d", projectID), "releases", checksum)
|
||||
return filepath.Join(baseDir, "projects", strconv.FormatUint(uint64(projectID), 10), "releases", checksum)
|
||||
}
|
||||
|
||||
@@ -238,10 +238,7 @@ func TestPromoteSameHashReleaseFailureRestoresPreviousCurrent(t *testing.T) {
|
||||
if err := switchPagesProjectCurrentDir(pagesDir, projectID, releaseDir); err != nil {
|
||||
t.Fatalf("seed same-hash current error = %v", err)
|
||||
}
|
||||
stagingDir, err := os.MkdirTemp(filepath.Dir(releaseDir), ".same-hash-*.tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("create same-hash staging error = %v", err)
|
||||
}
|
||||
stagingDir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(stagingDir, "index.html"), []byte("new"), pagesFilePerm); err != nil {
|
||||
t.Fatalf("write repaired same-hash release error = %v", err)
|
||||
}
|
||||
@@ -249,7 +246,7 @@ func TestPromoteSameHashReleaseFailureRestoresPreviousCurrent(t *testing.T) {
|
||||
t.Fatalf("write repaired same-hash marker error = %v", err)
|
||||
}
|
||||
copyErr := errors.New("injected same-hash copy failure")
|
||||
err = promotePagesReleaseWithCopy(
|
||||
err := promotePagesReleaseWithCopy(
|
||||
stagingDir,
|
||||
releaseDir,
|
||||
project,
|
||||
@@ -298,10 +295,7 @@ func TestPromotePagesReleaseRepairsDanglingCurrent(t *testing.T) {
|
||||
}
|
||||
|
||||
requireTestMkdirAll(t, filepath.Dir(releaseDir))
|
||||
stagingDir, err := os.MkdirTemp(filepath.Dir(releaseDir), ".dangling-*.tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("create dangling repair staging error = %v", err)
|
||||
}
|
||||
stagingDir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(stagingDir, "index.html"), []byte("repaired"), pagesFilePerm); err != nil {
|
||||
t.Fatalf("write dangling repair staging error = %v", err)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -363,7 +363,7 @@ func referencedWAFIPGroupIDs(supportFiles []protocol.SupportFile) ([]uint, error
|
||||
for id := range seen {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
slices.Sort(ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ func (s *Service) ensureRuntimeForCurrentConfig(ctx context.Context, mode string
|
||||
snapshot.OpenrestyMessage = "safe default fallback runtime started"
|
||||
return nil
|
||||
}
|
||||
err = fmt.Errorf("%v; fallback recovery failed: %w", err, fallbackErr)
|
||||
err = fmt.Errorf("%w; fallback recovery failed: %w", err, fallbackErr)
|
||||
}
|
||||
snapshot.OpenrestyStatus = protocol.OpenrestyStatusUnhealthy
|
||||
snapshot.OpenrestyMessage = err.Error()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sync
|
||||
|
||||
import (
|
||||
@@ -220,6 +223,9 @@ func updateSnapshotFromApplyOutcome(mode string, snapshot *state.Snapshot, confi
|
||||
snapshot.OpenrestyStatus = protocol.OpenrestyStatusHealthy
|
||||
snapshot.OpenrestyMessage = result.message
|
||||
result.reportResult = ApplyResultWarning
|
||||
case nginx.ApplyStatusFatal:
|
||||
// 致命错误与普通失败同走失败路径:标记阻塞并上报 Unhealthy。
|
||||
fallthrough
|
||||
default:
|
||||
if result.message == "" {
|
||||
result.message = "openresty apply failed"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package updater provides agent self-update integration with the edge updater.
|
||||
package updater
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package wsclient provides the agent-side WebSocket client for connecting to the OpenFlare server.
|
||||
package wsclient
|
||||
|
||||
|
||||
Reference in New Issue
Block a user