Files
WeKnora/internal/handler/skill_handler.go
T
wizardchen 738fe1ec53 feat(sandbox): 沙箱后端改为按空间配置,并补齐 E2B 模板与检测诊断
沙箱的后端类型、凭据、模板、超时和私网访问策略此前分散在部署级
WEKNORA_SANDBOX_* 环境变量里,一个实例只能有一套,租户之间无法各用
自己的 E2B 账号或自建 Cube 集群。现在这些配置全部落到空间维度的具名
沙箱配置上:部署环境不再提供覆盖项,未选择配置的智能体直接不启用脚本
执行,而不是静默回落到某个部署默认值——后者会让「这个智能体到底跑在
哪」无从判断。

同时把连接检测从「能不能连上控制面」扩展成可解释的诊断:
- E2B 标准模板显式带上 default 标签构建。创建沙箱时裸模板名/ID 只解析
  default 标签,构建带了别的标签就永远拿不到,表现为一个与「模板不存在」
  无法区分的 404。
- 模板状态不再从历史构建或 spawn 次数反推就绪,只认当前构建;构建完成
  却没有 default 标签的模板单独报成 untagged,因为它在列表里和「仍在构建」
  长得一样,但等待永远不会有结果。
- 创建失败带上 HTTP 状态,404 走模板解释路径,给出具体到「重建模板」还是
  「Key 属于其他团队」的结论。
- 探针脚本改在进程工作目录下暂存。系统临时目录在 macOS 上位于
  /var/folders,Docker 虚拟机并不共享,挂载进容器后是空目录,真实技能执行
  (脚本在 skills/ 下)却不会踩到,检测因此报假失败。
- 检测失败不再只说「命令输出与预期不符」,带上退出码与 stderr 首行。

前端把沙箱设置页与配置抽屉重做:凭据按「是否已配置」展示而非回显掩码,
删除与停用改为就地确认,占用情况走右侧抽屉,列表卡片展示模板状态、凭据、
超时等可判断健康度的事实。
2026-08-11 15:42:02 +08:00

68 lines
1.8 KiB
Go

package handler
import (
"net/http"
"github.com/Tencent/WeKnora/internal/errors"
"github.com/Tencent/WeKnora/internal/logger"
"github.com/Tencent/WeKnora/internal/types/interfaces"
"github.com/gin-gonic/gin"
)
// SkillHandler handles skill-related HTTP requests
type SkillHandler struct {
skillService interfaces.SkillService
}
// NewSkillHandler creates a new skill handler
func NewSkillHandler(skillService interfaces.SkillService) *SkillHandler {
return &SkillHandler{
skillService: skillService,
}
}
// SkillInfoResponse represents the skill info returned to frontend
type SkillInfoResponse struct {
Name string `json:"name"`
Description string `json:"description"`
}
// ListSkills godoc
// @Summary 获取预装Skills列表
// @Description 获取所有预装的Agent Skills元数据
// @Tags Skills
// @Accept json
// @Produce json
// @Success 200 {object} map[string]interface{} "Skills列表"
// @Failure 500 {object} errors.AppError "服务器错误"
// @Security Bearer
// @Security ApiKeyAuth
// @Router /skills [get]
func (h *SkillHandler) ListSkills(c *gin.Context) {
ctx := c.Request.Context()
skillsMetadata, err := h.skillService.ListPreloadedSkills(ctx)
if err != nil {
logger.ErrorWithFields(ctx, err, nil)
c.Error(errors.NewInternalServerError("Failed to list skills: " + err.Error()))
return
}
// Convert to response format
var response []SkillInfoResponse
for _, meta := range skillsMetadata {
response = append(response, SkillInfoResponse{
Name: meta.Name,
Description: meta.Description,
})
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": response,
// Skills can be selected before a workspace sandbox is configured; the
// execution path remains disabled until the agent chooses a config.
"skills_available": true,
})
}