fix(sandbox): update token generation and improve ClaudeRunner file handling

- 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
This commit is contained in:
Max 2026-03-15 17:22:44 +08:00
parent 89e5840490
commit fddd137806
2 changed files with 17 additions and 13 deletions

View file

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"os"
"path" "path"
"strings" "strings"
"time" "time"
@ -41,17 +42,17 @@ func (r *ClaudeRunner) Prepare(ctx context.Context, req *types.PrepareRequest) e
r.mode = "cli" r.mode = "cli"
} }
env := resolveOSEnv(req.Computer, req.Config)
steps := append([]types.PrepareStep{}, req.Config.Prepare...) steps := append([]types.PrepareStep{}, req.Config.Prepare...)
if req.SkillsDir != "" { if req.SkillsDir != "" {
claudeDir := env.pathJoin(env.WorkDir, ".claude") ws := req.Computer.Workplace()
steps = append(steps, types.PrepareStep{ if ws != nil {
Action: "exec", src := "local:///" + req.SkillsDir
Cmd: env.mkdirCmd(claudeDir), dst := ".claude/skills"
Once: true, if _, err := ws.Copy(src, dst); err != nil {
}) fmt.Fprintf(os.Stderr, "[claude] warn: copy skills %s -> %s: %v\n", src, dst, err)
}
}
} }
if len(req.MCPServers) > 0 { if len(req.MCPServers) > 0 {
@ -60,7 +61,7 @@ func (r *ClaudeRunner) Prepare(ctx context.Context, req *types.PrepareRequest) e
mcpJSON := buildMCPConfig(req.MCPServers) mcpJSON := buildMCPConfig(req.MCPServers)
steps = append(steps, types.PrepareStep{ steps = append(steps, types.PrepareStep{
Action: "file", Action: "file",
Path: env.pathJoin(env.WorkDir, ".mcp.json"), Path: ".claude/mcp.json",
Content: mcpJSON, Content: mcpJSON,
}) })
} }
@ -103,6 +104,8 @@ func (r *ClaudeRunner) Stream(ctx context.Context, req *types.StreamRequest, han
streamOpts = append(streamOpts, infra.WithStdin(stdin)) streamOpts = append(streamOpts, infra.WithStdin(stdin))
} }
fmt.Fprintf(os.Stderr, "[claude] Stream cmd=%v hasMCP=%v isContinuation=%v stdinLen=%d\n", cmd, r.hasMCP, isContinuation, len(stdin))
execStream, err := computer.Stream(ctx, cmd, streamOpts...) execStream, err := computer.Stream(ctx, cmd, streamOpts...)
if err != nil { if err != nil {
return fmt.Errorf("computer.Stream: %w", err) return fmt.Errorf("computer.Stream: %w", err)
@ -157,6 +160,7 @@ func (r *ClaudeRunner) Stream(ctx context.Context, req *types.StreamRequest, han
return waitErr return waitErr
} }
if exitCode != 0 { if exitCode != 0 {
fmt.Fprintf(os.Stderr, "[claude] exit code=%d parseErr=%v waitErr=%v stderr=%q\n", exitCode, parseErr, waitErr, stderrStr)
if stderrStr != "" { if stderrStr != "" {
return fmt.Errorf("claude CLI exited with code %d: %s", exitCode, stderrStr) return fmt.Errorf("claude CLI exited with code %d: %s", exitCode, stderrStr)
} }
@ -302,7 +306,7 @@ func (r *ClaudeRunner) buildCLICommand(req *types.StreamRequest, oe *osEnv, isCo
} }
if r.hasMCP { if r.hasMCP {
mcpPath := oe.pathJoin(oe.WorkDir, ".mcp.json") mcpPath := oe.pathJoin(oe.WorkDir, ".claude", "mcp.json")
args = append(args, "--mcp-config", mcpPath) args = append(args, "--mcp-config", mcpPath)
if r.mcpToolPattern != "" { if r.mcpToolPattern != "" {
args = append(args, "--allowedTools", r.mcpToolPattern) args = append(args, "--allowedTools", r.mcpToolPattern)
@ -327,7 +331,7 @@ func buildMCPConfig(servers []types.MCPServer) []byte {
} }
mcpServers[name] = map[string]any{ mcpServers[name] = map[string]any{
"command": "tai", "command": "tai",
"args": []string{"mcp"}, "args": []string{"mcp", name},
} }
} }
if len(mcpServers) == 0 { if len(mcpServers) == 0 {

View file

@ -70,7 +70,7 @@ func IssueSandboxToken(teamID, userID string) (*types.SandboxToken, error) {
extraClaims["team_id"] = teamID extraClaims["team_id"] = teamID
} }
tokenStr, err := svc.MakeAccessToken("__yao.sandbox", "sandbox:mcp", subject, tokenStr, err := svc.MakeAccessToken("__yao.sandbox", "grpc:mcp", subject,
int(accessTokenTTL.Seconds()), extraClaims) int(accessTokenTTL.Seconds()), extraClaims)
if err != nil { if err != nil {
return nil, fmt.Errorf("sandbox token: issue access token: %w", err) return nil, fmt.Errorf("sandbox token: issue access token: %w", err)
@ -78,7 +78,7 @@ func IssueSandboxToken(teamID, userID string) (*types.SandboxToken, error) {
tok := &types.SandboxToken{Token: tokenStr} tok := &types.SandboxToken{Token: tokenStr}
refreshStr, err := svc.MakeRefreshToken("__yao.sandbox", "sandbox:mcp", subject, refreshStr, err := svc.MakeRefreshToken("__yao.sandbox", "grpc:mcp", subject,
int(refreshTokenTTL.Seconds()), extraClaims) int(refreshTokenTTL.Seconds()), extraClaims)
if err != nil { if err != nil {
return nil, fmt.Errorf("sandbox token: issue refresh token: %w", err) return nil, fmt.Errorf("sandbox token: issue refresh token: %w", err)