Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

83 lines
2.7 KiB
Go

package implementationhost
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
market "github.com/tutti-os/tutti/packages/connector/daemon/core"
connectorruntime "github.com/tutti-os/tutti/packages/connector/runtime"
)
func (host *Host) deactivateConnector(request market.RuntimeDeactivationRequest) error {
match := func(route connectorruntime.ManagedRoute) bool {
candidate, ok := route.(*connectorRoute)
return ok && candidate.connectorKey == request.ConnectorKey
}
routeErr := host.routes.RemoveMatching(match, request.Generation, request.Deadline)
host.authorizationMu.Lock()
authorizationRoutes := make([]*connectorRoute, 0)
for key, route := range host.authorizationRoutes {
if route.connectorKey != request.ConnectorKey {
continue
}
delete(host.authorizationRoutes, key)
authorizationRoutes = append(authorizationRoutes, route)
}
host.authorizationMu.Unlock()
var authorizationErrs []error
for _, route := range authorizationRoutes {
host.authorizationProvider.cancelAuthorizationSessionByRoute(route.id)
route.Fence()
if err := route.Close(request.Deadline); err != nil {
// Keep a fenced route reachable so an idempotent uninstall retry can
// finish snapshot/process cleanup after a transient close failure.
host.authorizationMu.Lock()
if host.authorizationRoutes[route.id] == nil {
host.authorizationRoutes[route.id] = route
}
host.authorizationMu.Unlock()
authorizationErrs = append(authorizationErrs, err)
}
}
shimErr := host.removeOrphanedConnectorCLIShim(request.ConnectorKey)
host.notifyRouteChanged()
return errors.Join(routeErr, errors.Join(authorizationErrs...), shimErr)
}
func (host *Host) removeOrphanedConnectorCLIShim(connectorKey string) error {
if !hostIdentityPattern.MatchString(connectorKey) {
return errors.New("connector CLI shim removal identity is invalid")
}
if !filepath.IsAbs(host.binDir) {
return nil
}
command := "tutti-connector-" + connectorKey
if runtime.GOOS == "windows" {
command += ".cmd"
}
path := filepath.Join(host.binDir, command)
content, err := os.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return fmt.Errorf("read connector CLI shim before removal: %w", err)
}
managedMarker := "export TUTTI_CONNECTOR_KEY=" + shellQuote(connectorKey) + "\n"
if runtime.GOOS == "windows" {
managedMarker = "set \"TUTTI_CONNECTOR_KEY=" + connectorKey + "\"\r\n"
}
if !strings.Contains(string(content), managedMarker) {
return errors.New("connector CLI shim removal target is not Tutti-managed")
}
if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("remove connector CLI shim: %w", err)
}
return nil
}