Files
tutti/services/tuttid/api/daemon_workspace_app_agent.go
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

320 lines
13 KiB
Go

package api
import (
"context"
"strings"
"time"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
"github.com/tutti-os/tutti/services/tuttid/apierrors"
agenttargetbiz "github.com/tutti-os/tutti/services/tuttid/biz/agenttarget"
workspacedata "github.com/tutti-os/tutti/services/tuttid/data/workspace"
agentstatusservice "github.com/tutti-os/tutti/services/tuttid/service/agentstatus"
)
func (api DaemonAPI) GetWorkspaceAppAgentPreferences(
ctx context.Context,
request tuttigenerated.GetWorkspaceAppAgentPreferencesRequestObject,
) (tuttigenerated.GetWorkspaceAppAgentPreferencesResponseObject, error) {
workspaceID, appID, errResponse := validateWorkspaceAppPath(request.WorkspaceID, request.AppID)
if errResponse != nil {
return tuttigenerated.GetWorkspaceAppAgentPreferences400JSONResponse{
InvalidRequestErrorJSONResponse: *errResponse,
}, nil
}
workspaceAppVersion, err := api.ensureWorkspaceAppInstalled(ctx, workspaceID, appID)
if err != nil {
return writeGetWorkspaceAppAgentPreferencesError(err), nil
}
api.trackDeprecatedWorkspaceAppAgentAPI(ctx, "preferences/agent", appID, workspaceAppVersion)
if api.PreferencesService == nil {
return tuttigenerated.GetWorkspaceAppAgentPreferences503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(
apierrors.PreferencesServiceUnavailable(
apierrors.WithDeveloperMessage("desktop preferences service is unavailable"),
),
),
}, nil
}
preferences, err := api.PreferencesService.Get(ctx)
if err != nil {
return tuttigenerated.GetWorkspaceAppAgentPreferences502JSONResponse{
PreferencesOperationErrorJSONResponse: preferencesOperationError(
apierrors.PreferencesOperationFailed(apierrors.WithCause(err)),
),
}, nil
}
return tuttigenerated.GetWorkspaceAppAgentPreferences200JSONResponse{
DefaultAgentProvider: tuttigenerated.DesktopDefaultAgentProvider(preferences.DefaultAgentProvider),
}, nil
}
func (api DaemonAPI) GetWorkspaceAppAgentProviderStatuses(
ctx context.Context,
request tuttigenerated.GetWorkspaceAppAgentProviderStatusesRequestObject,
) (tuttigenerated.GetWorkspaceAppAgentProviderStatusesResponseObject, error) {
workspaceID, appID, errResponse := validateWorkspaceAppPath(request.WorkspaceID, request.AppID)
if errResponse != nil {
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses400JSONResponse{
InvalidRequestErrorJSONResponse: *errResponse,
}, nil
}
workspaceAppVersion, err := api.ensureWorkspaceAppInstalled(ctx, workspaceID, appID)
if err != nil {
return writeGetWorkspaceAppAgentProviderStatusesError(err), nil
}
api.trackDeprecatedWorkspaceAppAgentAPI(ctx, "agent-providers/status", appID, workspaceAppVersion)
providers, err := api.workspaceAppAgentStatusProviders(ctx, request.Params.Providers)
if err != nil {
return writeGetWorkspaceAppAgentProviderStatusesError(err), nil
}
if providers != nil && len(*providers) == 0 {
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses200JSONResponse(
generatedAgentProviderStatusList(agentstatusEmptySnapshot(), api.defaultAgentProvider(ctx)),
), nil
}
response, err := api.GetAgentProviderStatuses(ctx, tuttigenerated.GetAgentProviderStatusesRequestObject{
Params: tuttigenerated.GetAgentProviderStatusesParams{
Providers: providers,
IncludeNetwork: request.Params.IncludeNetwork,
Refresh: request.Params.Refresh,
},
})
if err != nil {
return nil, err
}
return mapGetAgentProviderStatusesToWorkspaceApp(response), nil
}
func (api DaemonAPI) workspaceAppAgentStatusProviders(
ctx context.Context,
requestedProviders *[]tuttigenerated.WorkspaceAgentProvider,
) (*[]tuttigenerated.WorkspaceAgentProvider, error) {
if api.AgentTargetService == nil {
return nil, apierrors.ServiceUnavailable(
"agent_target_service_unavailable",
apierrors.WithDeveloperMessage("agent target service is unavailable"),
)
}
targets, err := api.AgentTargetService.List(ctx)
if err != nil {
return nil, err
}
visibleTargets := agenttargetbiz.EnabledTargetsByProvider(targets)
visible := map[string]struct{}{}
ordered := make([]string, 0, len(visibleTargets))
for _, target := range visibleTargets {
visible[target.Provider] = struct{}{}
ordered = append(ordered, target.Provider)
}
providers := make([]tuttigenerated.WorkspaceAgentProvider, 0, len(targets))
seen := map[string]struct{}{}
if requestedProviders != nil && len(*requestedProviders) > 0 {
for _, provider := range *requestedProviders {
normalized := strings.TrimSpace(string(provider))
if _, ok := visible[normalized]; !ok {
continue
}
if _, ok := seen[normalized]; ok {
continue
}
seen[normalized] = struct{}{}
providers = append(providers, tuttigenerated.WorkspaceAgentProvider(normalized))
}
return &providers, nil
}
for _, provider := range ordered {
if _, ok := seen[provider]; ok {
continue
}
seen[provider] = struct{}{}
providers = append(providers, tuttigenerated.WorkspaceAgentProvider(provider))
}
return &providers, nil
}
func agentstatusEmptySnapshot() agentstatusservice.Snapshot {
return agentstatusservice.Snapshot{
CapturedAt: time.Now().UTC(),
Providers: []agentstatusservice.ProviderStatus{},
}
}
func (api DaemonAPI) GetWorkspaceAppAgentProviderComposerOptions(
ctx context.Context,
request tuttigenerated.GetWorkspaceAppAgentProviderComposerOptionsRequestObject,
) (tuttigenerated.GetWorkspaceAppAgentProviderComposerOptionsResponseObject, error) {
workspaceID, appID, errResponse := validateWorkspaceAppPath(request.WorkspaceID, request.AppID)
if errResponse != nil {
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions400JSONResponse{
InvalidRequestErrorJSONResponse: *errResponse,
}, nil
}
workspaceAppVersion, err := api.ensureWorkspaceAppInstalled(ctx, workspaceID, appID)
if err != nil {
return writeGetWorkspaceAppAgentProviderComposerOptionsError(err), nil
}
api.trackDeprecatedWorkspaceAppAgentAPI(ctx, "agent-providers/{provider}/composer-options", appID, workspaceAppVersion)
requestedProviders := []tuttigenerated.WorkspaceAgentProvider{request.Provider}
visibleProviders, err := api.workspaceAppAgentStatusProviders(ctx, &requestedProviders)
if err != nil {
return writeGetWorkspaceAppAgentProviderComposerOptionsError(err), nil
}
if len(*visibleProviders) == 0 {
return writeGetWorkspaceAppAgentProviderComposerOptionsError(
apierrors.InvalidRequest(
apierrors.ReasonAgentProviderUnavailable,
apierrors.WithDeveloperMessage("agent provider is not visible to this workspace app"),
apierrors.WithParams(map[string]any{"provider": request.Provider}),
),
), nil
}
body := request.Body
if body == nil {
body = &tuttigenerated.GetWorkspaceAppAgentProviderComposerOptionsJSONRequestBody{}
}
workspaceIDCopy := workspaceID
body.WorkspaceId = &workspaceIDCopy
response, err := api.GetAgentProviderComposerOptions(ctx, tuttigenerated.GetAgentProviderComposerOptionsRequestObject{
Provider: request.Provider,
Body: body,
})
if err != nil {
return nil, err
}
return mapGetAgentProviderComposerOptionsToWorkspaceApp(response), nil
}
func (api DaemonAPI) ensureWorkspaceAppInstalled(ctx context.Context, workspaceID string, appID string) (string, error) {
if api.AppCenterService == nil {
return "", apierrors.ServiceUnavailable(
"workspace_app_service_unavailable",
apierrors.WithDeveloperMessage("workspace app service is unavailable"),
)
}
apps, err := api.AppCenterService.List(ctx, workspaceID)
if err != nil {
return "", err
}
for _, app := range apps {
if app.Package.AppID == appID && app.Installation != nil {
return app.Package.Version, nil
}
}
return "", workspacedata.ErrWorkspaceAppNotFound
}
func writeGetWorkspaceAppAgentPreferencesError(err error) tuttigenerated.GetWorkspaceAppAgentPreferencesResponseObject {
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.InvalidRequest:
return tuttigenerated.GetWorkspaceAppAgentPreferences400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(protocolErr),
}
case tuttigenerated.WorkspaceNotFound, tuttigenerated.WorkspaceAppNotFound:
return tuttigenerated.GetWorkspaceAppAgentPreferences404JSONResponse{
WorkspaceAppNotFoundErrorJSONResponse: workspaceAppNotFoundError(protocolErr),
}
case tuttigenerated.ServiceUnavailable:
return tuttigenerated.GetWorkspaceAppAgentPreferences503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(protocolErr),
}
default:
return tuttigenerated.GetWorkspaceAppAgentPreferences502JSONResponse{
PreferencesOperationErrorJSONResponse: preferencesOperationError(protocolErr),
}
}
}
func writeGetWorkspaceAppAgentProviderStatusesError(err error) tuttigenerated.GetWorkspaceAppAgentProviderStatusesResponseObject {
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.WorkspaceNotFound, tuttigenerated.WorkspaceAppNotFound:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses404JSONResponse{
WorkspaceAppNotFoundErrorJSONResponse: workspaceAppNotFoundError(protocolErr),
}
case tuttigenerated.ServiceUnavailable:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(protocolErr),
}
default:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(protocolErr),
}
}
}
func writeGetWorkspaceAppAgentProviderComposerOptionsError(err error) tuttigenerated.GetWorkspaceAppAgentProviderComposerOptionsResponseObject {
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.InvalidRequest:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(protocolErr),
}
case tuttigenerated.WorkspaceNotFound, tuttigenerated.WorkspaceAppNotFound:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions404JSONResponse{
WorkspaceAppNotFoundErrorJSONResponse: workspaceAppNotFoundError(protocolErr),
}
case tuttigenerated.ServiceUnavailable:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(protocolErr),
}
default:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(protocolErr),
}
}
}
func mapGetAgentProviderStatusesToWorkspaceApp(
response tuttigenerated.GetAgentProviderStatusesResponseObject,
) tuttigenerated.GetWorkspaceAppAgentProviderStatusesResponseObject {
switch typed := response.(type) {
case tuttigenerated.GetAgentProviderStatuses200JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses200JSONResponse(typed)
case tuttigenerated.GetAgentProviderStatuses400JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses400JSONResponse(typed)
case tuttigenerated.GetAgentProviderStatuses502JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses502JSONResponse(typed)
case tuttigenerated.GetAgentProviderStatuses503JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses503JSONResponse(typed)
default:
return tuttigenerated.GetWorkspaceAppAgentProviderStatuses502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(
apierrors.WorkspaceOperationFailed(
apierrors.WithDeveloperMessage("unexpected agent provider status response"),
),
),
}
}
}
func mapGetAgentProviderComposerOptionsToWorkspaceApp(
response tuttigenerated.GetAgentProviderComposerOptionsResponseObject,
) tuttigenerated.GetWorkspaceAppAgentProviderComposerOptionsResponseObject {
switch typed := response.(type) {
case tuttigenerated.GetAgentProviderComposerOptions200JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions200JSONResponse(typed)
case tuttigenerated.GetAgentProviderComposerOptions400JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions400JSONResponse(typed)
case tuttigenerated.GetAgentProviderComposerOptions502JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions502JSONResponse(typed)
case tuttigenerated.GetAgentProviderComposerOptions503JSONResponse:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions503JSONResponse(typed)
default:
return tuttigenerated.GetWorkspaceAppAgentProviderComposerOptions502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(
apierrors.WorkspaceOperationFailed(
apierrors.WithDeveloperMessage("unexpected agent provider composer options response"),
),
),
}
}
}