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

250 lines
8.1 KiB
Go

package api
import (
"context"
"sort"
"strings"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
workspaceapi "github.com/tutti-os/tutti/services/tuttid/api/workspace"
"github.com/tutti-os/tutti/services/tuttid/apierrors"
workspacebiz "github.com/tutti-os/tutti/services/tuttid/biz/workspace"
cliservice "github.com/tutti-os/tutti/services/tuttid/service/cli"
)
func (api DaemonAPI) ListWorkspaceAppMentionCandidates(ctx context.Context, request tuttigenerated.ListWorkspaceAppMentionCandidatesRequestObject) (tuttigenerated.ListWorkspaceAppMentionCandidatesResponseObject, error) {
if api.AppCenterService == nil {
return tuttigenerated.ListWorkspaceAppMentionCandidates503JSONResponse{
ServiceUnavailableErrorJSONResponse: workspaceAppServiceUnavailableError(),
}, nil
}
workspaceID := strings.TrimSpace(string(request.WorkspaceID))
if workspaceID == "" {
return tuttigenerated.ListWorkspaceAppMentionCandidates400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(
apierrors.MissingWorkspaceID(
apierrors.WithDeveloperMessage("workspace id is required"),
apierrors.WithParams(map[string]any{"field": "workspaceId"}),
),
),
}, nil
}
apps, err := api.AppCenterService.List(ctx, workspaceID)
if err != nil {
return writeListWorkspaceAppMentionCandidatesError(err), nil
}
cliAppsByID := workspaceAppMentionCLIAppsByID(ctx, api.CLIRegistry, workspaceID)
candidates := make([]tuttigenerated.WorkspaceAppMentionCandidate, 0, len(apps)+len(cliAppsByID))
knownAppIDs := make(map[string]struct{}, len(apps))
for _, app := range apps {
appID := normalizedWorkspaceAppMentionID(app.Package.AppID)
if appID == "" {
continue
}
knownAppIDs[appID] = struct{}{}
if app.Installation == nil || !app.Installation.Enabled {
continue
}
cliApp, hasCLIApp := cliAppsByID[appID]
if !hasCLIApp || cliApp.metadata.CommandCount == 0 {
continue
}
candidates = append(
candidates,
workspaceAppMentionCandidateFromApp(app, cliApp),
)
}
for appID, cliApp := range cliAppsByID {
if _, exists := knownAppIDs[appID]; exists {
continue
}
candidate, ok := workspaceAppMentionCandidateFromCLIApp(cliApp)
if ok {
candidates = append(candidates, candidate)
}
}
sort.SliceStable(candidates, func(i, j int) bool {
return strings.ToLower(candidates[i].AppId) < strings.ToLower(candidates[j].AppId)
})
return tuttigenerated.ListWorkspaceAppMentionCandidates200JSONResponse{
WorkspaceId: workspaceID,
Apps: candidates,
}, nil
}
type workspaceAppMentionCLIApp struct {
appID string
displayName string
description string
iconURL string
metadata tuttigenerated.WorkspaceAppMentionCliMetadata
}
func workspaceAppMentionCLIAppsByID(ctx context.Context, registry *cliservice.Registry, workspaceID string) map[string]workspaceAppMentionCLIApp {
if registry == nil {
return map[string]workspaceAppMentionCLIApp{}
}
capabilities := registry.Capabilities(ctx, cliservice.InvokeContext{
Source: "agent-context",
WorkspaceID: workspaceID,
SkipCapabilityFilters: true,
})
appsByID := make(map[string]workspaceAppMentionCLIApp)
for _, capability := range capabilities {
if capability.Source.Kind != cliservice.CapabilitySourceApp {
continue
}
appID := strings.TrimSpace(capability.Source.AppID)
normalizedAppID := normalizedWorkspaceAppMentionID(appID)
if normalizedAppID == "" {
continue
}
cliApp := appsByID[normalizedAppID]
if cliApp.appID == "" {
cliApp = workspaceAppMentionCLIApp{
appID: appID,
displayName: firstNonBlank(capability.Source.AppName, appID),
metadata: emptyWorkspaceAppMentionCliMetadata(),
}
}
if iconURL := strings.TrimSpace(capability.Source.IconURL); iconURL != "" {
cliApp.iconURL = iconURL
}
if cliApp.description == "" {
cliApp.description = firstNonBlank(
capability.Source.CLIDescription,
capability.Source.AppDescription,
capability.Description,
)
}
cliApp.metadata.CommandCount++
cliApp.metadata.Scopes = appendUniqueNonBlank(
cliApp.metadata.Scopes,
firstCapabilityPathSegment(capability),
)
cliApp.metadata.CommandPaths = appendUniqueNonBlank(
cliApp.metadata.CommandPaths,
strings.Join(capability.Path, " "),
)
cliApp.metadata.CommandSummaries = appendUniqueNonBlank(
cliApp.metadata.CommandSummaries,
capability.Summary,
)
cliApp.metadata.CommandDescriptions = appendUniqueNonBlank(
cliApp.metadata.CommandDescriptions,
capability.Description,
)
appsByID[normalizedAppID] = cliApp
}
return appsByID
}
func workspaceAppMentionCandidateFromApp(app workspacebiz.WorkspaceApp, cliApp workspaceAppMentionCLIApp) tuttigenerated.WorkspaceAppMentionCandidate {
cliMetadata := cliApp.metadata
if cliMetadata.CommandCount == 0 {
cliMetadata = emptyWorkspaceAppMentionCliMetadata()
}
return tuttigenerated.WorkspaceAppMentionCandidate{
AppId: app.Package.AppID,
DisplayName: app.Package.DisplayName(),
Description: app.Package.Description(),
IconUrl: app.ResolvedIconURL(),
AvailableIconUrl: app.AvailableIconURL,
Installed: true,
Enabled: true,
Source: tuttigenerated.WorkspaceAppMentionCandidateSourceWorkspaceApp,
Localizations: workspaceapi.GeneratedAppLocalizationsFromBiz(app.Package.Localizations()),
References: tuttigenerated.WorkspaceAppReferencesState{
ListSupported: app.Package.ReferenceListSupported(),
SearchSupported: app.Package.ReferenceSearchSupported(),
},
Cli: cliMetadata,
}
}
func workspaceAppMentionCandidateFromCLIApp(cliApp workspaceAppMentionCLIApp) (tuttigenerated.WorkspaceAppMentionCandidate, bool) {
appID := strings.TrimSpace(cliApp.appID)
if appID == "" {
return tuttigenerated.WorkspaceAppMentionCandidate{}, false
}
displayName := firstNonBlank(cliApp.displayName, appID)
return tuttigenerated.WorkspaceAppMentionCandidate{
AppId: appID,
DisplayName: displayName,
Description: strings.TrimSpace(cliApp.description),
IconUrl: stringPointerIfNotBlank(strings.TrimSpace(cliApp.iconURL)),
AvailableIconUrl: nil,
Installed: true,
Enabled: true,
Source: tuttigenerated.WorkspaceAppMentionCandidateSourceCliApp,
Localizations: []tuttigenerated.WorkspaceAppLocalization{},
References: tuttigenerated.WorkspaceAppReferencesState{},
Cli: cliApp.metadata,
}, true
}
func emptyWorkspaceAppMentionCliMetadata() tuttigenerated.WorkspaceAppMentionCliMetadata {
return tuttigenerated.WorkspaceAppMentionCliMetadata{
CommandDescriptions: []string{},
CommandPaths: []string{},
CommandSummaries: []string{},
Scopes: []string{},
}
}
func normalizedWorkspaceAppMentionID(value string) string {
return strings.ToLower(strings.TrimSpace(value))
}
func firstCapabilityPathSegment(capability cliservice.Capability) string {
if len(capability.Path) == 0 {
return ""
}
return capability.Path[0]
}
func firstNonBlank(values ...string) string {
for _, value := range values {
if trimmed := strings.TrimSpace(value); trimmed != "" {
return trimmed
}
}
return ""
}
func appendUniqueNonBlank(values []string, value string) []string {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return values
}
for _, existing := range values {
if existing == trimmed {
return values
}
}
return append(values, trimmed)
}
func writeListWorkspaceAppMentionCandidatesError(err error) tuttigenerated.ListWorkspaceAppMentionCandidatesResponseObject {
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.WorkspaceNotFound:
return tuttigenerated.ListWorkspaceAppMentionCandidates404JSONResponse{
WorkspaceNotFoundErrorJSONResponse: workspaceNotFoundError(protocolErr),
}
case tuttigenerated.InvalidRequest:
return tuttigenerated.ListWorkspaceAppMentionCandidates400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(protocolErr),
}
default:
return tuttigenerated.ListWorkspaceAppMentionCandidates502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(protocolErr),
}
}
}