- Changed token generation from "sandbox:mcp" to "grpc:mcp" for both access and refresh tokens, aligning with updated service requirements. - Enhanced ClaudeRunner to copy skills from the specified directory to the ".claude/skills" path, improving skill management. - Updated MCP configuration file path to ".claude/mcp.json" for better organization and consistency in file handling. - Added error logging for skill copying and exit code handling in stream execution, enhancing debugging capabilities. Made-with: Cursor
90 lines
2.1 KiB
Go
90 lines
2.1 KiB
Go
package sandboxv2
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
lrustore "github.com/yaoapp/gou/store/lru"
|
|
"github.com/yaoapp/yao/agent/sandbox/v2/types"
|
|
"github.com/yaoapp/yao/openapi/oauth"
|
|
)
|
|
|
|
const (
|
|
accessTokenTTL = 2 * time.Hour
|
|
refreshTokenTTL = 30 * 24 * time.Hour // 30 days
|
|
tokenCacheSize = 1024
|
|
)
|
|
|
|
var tokenCache *lrustore.Cache
|
|
|
|
func init() {
|
|
c, err := lrustore.New(tokenCacheSize)
|
|
if err != nil {
|
|
panic("sandbox token cache init failed: " + err.Error())
|
|
}
|
|
tokenCache = c
|
|
}
|
|
|
|
func cacheKey(teamID, userID string) string {
|
|
if teamID == "" {
|
|
return userID
|
|
}
|
|
return teamID + "/" + userID
|
|
}
|
|
|
|
func getToken(teamID, userID string) *types.SandboxToken {
|
|
val, ok := tokenCache.Get(cacheKey(teamID, userID))
|
|
if !ok {
|
|
return nil
|
|
}
|
|
tok, _ := val.(*types.SandboxToken)
|
|
return tok
|
|
}
|
|
|
|
func setToken(teamID, userID string, tok *types.SandboxToken, ttl time.Duration) {
|
|
tokenCache.Set(cacheKey(teamID, userID), tok, ttl)
|
|
}
|
|
|
|
// IssueSandboxToken returns a valid identity token for the given user.
|
|
// Tokens are cached by (teamID, userID); a new token is only issued on
|
|
// cache miss or expiry. Returns nil without error when oauth.OAuth is nil.
|
|
func IssueSandboxToken(teamID, userID string) (*types.SandboxToken, error) {
|
|
if tok := getToken(teamID, userID); tok != nil {
|
|
return tok, nil
|
|
}
|
|
|
|
svc := oauth.OAuth
|
|
if svc == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
subject, err := svc.Subject("__yao.sandbox", userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sandbox token: derive subject: %w", err)
|
|
}
|
|
|
|
extraClaims := map[string]interface{}{
|
|
"user_id": userID,
|
|
}
|
|
if teamID != "" {
|
|
extraClaims["team_id"] = teamID
|
|
}
|
|
|
|
tokenStr, err := svc.MakeAccessToken("__yao.sandbox", "grpc:mcp", subject,
|
|
int(accessTokenTTL.Seconds()), extraClaims)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sandbox token: issue access token: %w", err)
|
|
}
|
|
|
|
tok := &types.SandboxToken{Token: tokenStr}
|
|
|
|
refreshStr, err := svc.MakeRefreshToken("__yao.sandbox", "grpc:mcp", subject,
|
|
int(refreshTokenTTL.Seconds()), extraClaims)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sandbox token: issue refresh token: %w", err)
|
|
}
|
|
tok.RefreshToken = refreshStr
|
|
|
|
setToken(teamID, userID, tok, accessTokenTTL)
|
|
return tok, nil
|
|
}
|