Files
tutti/services/tuttid/service/workspace/app_cli_runtime.go
T
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

116 lines
3.6 KiB
Go

package workspace
import (
"context"
"errors"
"log/slog"
"strings"
"time"
workspacebiz "github.com/tutti-os/tutti/services/tuttid/biz/workspace"
appcliservice "github.com/tutti-os/tutti/services/tuttid/service/cli/appcli"
)
func (s *AppCenterService) activateAppCLI(ctx context.Context, workspaceID string, appPackage workspacebiz.AppPackage, baseURL string) workspacebiz.AppCLIState {
if s.AppCLIRegistry == nil {
if appPackage.Manifest.CLI != nil {
return workspacebiz.AppCLIState{Status: workspacebiz.AppCLIStatusPending}
}
return workspacebiz.AppCLIState{Status: workspacebiz.AppCLIStatusNone}
}
return s.AppCLIRegistry.Activate(ctx, appcliservice.Activation{
WorkspaceID: workspaceID,
AppPackage: appPackage,
BaseURL: baseURL,
})
}
func (s *AppCenterService) appCLIState(workspaceID string, app workspacebiz.WorkspaceApp) workspacebiz.AppCLIState {
if s.AppCLIRegistry == nil {
if app.Installation != nil && app.Package.Manifest.CLI != nil {
return workspacebiz.AppCLIState{Status: workspacebiz.AppCLIStatusPending}
}
return workspacebiz.AppCLIState{Status: workspacebiz.AppCLIStatusNone}
}
return s.AppCLIRegistry.Status(workspaceID, app)
}
func (s *AppCenterService) deactivateAppCLI(workspaceID string, appID string) {
if s.AppCLIRegistry != nil {
s.AppCLIRegistry.Deactivate(workspaceID, appID)
}
}
func (s *AppCenterService) deactivateAppCLIForApp(appID string) {
if s.AppCLIRegistry != nil {
s.AppCLIRegistry.DeactivateApp(appID)
}
}
func (s *AppCenterService) deactivateWorkspaceAppCLI(ctx context.Context, workspaceID string) {
if s.AppCLIRegistry == nil || s.Store == nil {
return
}
installations, err := s.Store.ListWorkspaceAppInstallations(ctx, workspaceID)
if err != nil {
slog.Warn("workspace app cli deactivate skipped; installation lookup failed", "workspaceId", workspaceID, "error", err)
return
}
for _, installation := range installations {
s.AppCLIRegistry.Deactivate(workspaceID, installation.AppID)
}
}
func (s *AppCenterService) EnsureAppRunningForCLI(ctx context.Context, workspaceID string, appID string) (string, error) {
appPackage, installation, err := s.installedPackage(ctx, workspaceID, appID)
if err != nil {
return "", err
}
if !installation.Enabled {
return "", errors.New("workspace app is disabled")
}
state := s.runner().State(workspaceID, appPackage.AppID)
if state.Status != workspacebiz.AppRuntimeStatusRunning || state.LaunchURL == nil {
if _, err := s.Launch(ctx, workspaceID, appID); err != nil {
return "", err
}
state, err = s.waitForAppRunning(ctx, workspaceID, appPackage.AppID)
if err != nil {
return "", err
}
}
if state.LaunchURL == nil || strings.TrimSpace(*state.LaunchURL) == "" {
return "", errors.New("workspace app runtime launch url is unavailable")
}
return *state.LaunchURL, nil
}
func (s *AppCenterService) waitForAppRunning(ctx context.Context, workspaceID string, appID string) (workspacebiz.AppRuntimeState, error) {
waitCtx := ctx
cancel := func() {}
if _, ok := ctx.Deadline(); !ok {
waitCtx, cancel = context.WithTimeout(ctx, defaultAppHealthcheckTimeout+5*time.Second)
}
defer cancel()
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
state := s.runner().State(workspaceID, appID)
switch state.Status {
case workspacebiz.AppRuntimeStatusRunning:
return state, nil
case workspacebiz.AppRuntimeStatusFailed:
if state.LastError != nil {
return state, errors.New(*state.LastError)
}
return state, errors.New("workspace app runtime failed")
}
select {
case <-waitCtx.Done():
return state, waitCtx.Err()
case <-ticker.C:
}
}
}