Files
heranran 71c7672545 feat: Skill Hub hardening with session usage tracking and egress governance (#163)
* feat(skill-hub): add Skill Hub catalog, publish flow, and lite materialize pipeline

Introduce Skill Hub for browsing, importing, publishing, and installing skills, with lite instance package materialization and runtime sync support.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): remove token-governance hooks from Skill Hub PR

Strip validateManagedRuntimeEnvironmentOverrides, network lock policy sync,
and egress proxy audit wiring that belong to the upcoming token-usage work,
so the Skill Hub branch compiles independently.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): update migration number in materialize docs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): make RuntimeAgentClient test stub and hub tests compile-safe

Add ResyncInstanceSkills to the runtime pool handler fake client, and harden
skill hub payload helpers/tests against nil storage/instance repos so go test passes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add Skill Hub hardening with session usage tracking and egress governance

Unify Skill Hub runtime sync improvements with session-token observability,
egress network policy, and admin/instance usage reporting for reopenable PR.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(skill-hub): repair CI tests and nested skill install

* fix(ci): restore release deployment configuration

---------

Co-authored-by: heshengran <heshengran@ieisystem.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 12:37:01 +08:00

55 lines
1.3 KiB
Go

package utils
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
)
type HubError struct {
Code string
Message string
Details map[string]string
}
func (e *HubError) Error() string {
if e == nil {
return ""
}
if e.Message != "" {
return e.Message
}
return e.Code
}
func NewHubError(code, message string, details map[string]string) *HubError {
return &HubError{Code: code, Message: message, Details: details}
}
// HandleHubError maps skill hub domain errors to HTTP responses.
func HandleHubError(c *gin.Context, err error) {
var hubErr *HubError
if errors.As(err, &hubErr) {
switch hubErr.Code {
case "skill_package_md5_mismatch":
Error(c, http.StatusBadRequest, hubErr.Code)
default:
Error(c, http.StatusBadRequest, hubErr.Error())
}
return
}
switch err.Error() {
case "skill_not_scanned", "skill_risk_blocked", "skill_tags_required", "skill_not_in_library", "skill is not published to hub":
Error(c, http.StatusBadRequest, err.Error())
case "skill_package_pending":
Error(c, http.StatusConflict, err.Error())
case "skill_package_materialize_failed", "skill_package_materializing":
Error(c, http.StatusConflict, err.Error())
case "skill_attach_forbidden", "access denied":
Error(c, http.StatusForbidden, err.Error())
default:
HandleError(c, err)
}
}