- Implemented AssistantReloadFunc to enable hot-reloading of assistants after deployment, improving deployment flexibility. - Enhanced gRPC authProvider to include workspace and sandbox IDs from incoming context metadata, enriching the authentication context. - Updated tools to support new agent-related functionalities, including listing, downloading, deploying, and connecting agents. - Expanded system tools documentation to include new agent commands, ensuring comprehensive guidance for users.
111 lines
2.5 KiB
Go
111 lines
2.5 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/yaoapp/gou/process"
|
|
taiworkspace "github.com/yaoapp/yao/tai/workspace"
|
|
ws "github.com/yaoapp/yao/workspace"
|
|
"google.golang.org/grpc/metadata"
|
|
)
|
|
|
|
//go:embed list_schema.json
|
|
var ListSchemaJSON []byte
|
|
|
|
//go:embed download_schema.json
|
|
var DownloadSchemaJSON []byte
|
|
|
|
//go:embed deploy_schema.json
|
|
var DeploySchemaJSON []byte
|
|
|
|
//go:embed connectors_schema.json
|
|
var ConnectorsSchemaJSON []byte
|
|
|
|
const allowedDeployNamespace = "smith"
|
|
|
|
type agentInfo struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Capabilities string `json:"capabilities,omitempty"`
|
|
}
|
|
|
|
type packageDSL struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Capabilities string `json:"capabilities"`
|
|
}
|
|
|
|
func resolveWorkspaceFS(proc *process.Process) (taiworkspace.FS, error) {
|
|
workspaceID := extractWorkspaceID(proc)
|
|
if workspaceID == "" {
|
|
return nil, fmt.Errorf("workspace_id not available (container must set CTX_WORKSPACE_ID)")
|
|
}
|
|
|
|
fs, err := ws.M().FS(context.Background(), workspaceID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("workspace %s: %w", workspaceID, err)
|
|
}
|
|
return fs, nil
|
|
}
|
|
|
|
func extractWorkspaceID(proc *process.Process) string {
|
|
if proc.Context == nil {
|
|
return ""
|
|
}
|
|
md, ok := metadata.FromIncomingContext(proc.Context)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
ids := md.Get("x-workspace-id")
|
|
if len(ids) > 0 && ids[0] != "" {
|
|
return ids[0]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func validateID(id string) error {
|
|
if strings.Contains(id, "..") {
|
|
return fmt.Errorf("invalid id: path traversal not allowed")
|
|
}
|
|
if strings.ContainsAny(id, "/\\") {
|
|
return fmt.Errorf("invalid id: use dot notation (e.g. 'yao.slides')")
|
|
}
|
|
parts := strings.SplitN(id, ".", 2)
|
|
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
|
return fmt.Errorf("invalid id format: expected 'namespace.name' (e.g. 'yao.slides')")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func idToPath(id string) string {
|
|
return strings.Replace(id, ".", "/", 1)
|
|
}
|
|
|
|
func settingStr(setting map[string]interface{}, key string) string {
|
|
if v, ok := setting[key]; ok {
|
|
if s, ok := v.(string); ok {
|
|
return s
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func sanitizeCapabilities(caps interface{}) interface{} {
|
|
data, err := json.Marshal(caps)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var m map[string]interface{}
|
|
if err := json.Unmarshal(data, &m); err != nil {
|
|
return caps
|
|
}
|
|
delete(m, "key")
|
|
delete(m, "secret")
|
|
delete(m, "token")
|
|
return m
|
|
}
|