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

519 lines
22 KiB
Go

package api
import (
"context"
"errors"
"log/slog"
"strings"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
"github.com/tutti-os/tutti/services/tuttid/apierrors"
agentproviderbiz "github.com/tutti-os/tutti/services/tuttid/biz/agentprovider"
preferencesbiz "github.com/tutti-os/tutti/services/tuttid/biz/preferences"
agentstatusservice "github.com/tutti-os/tutti/services/tuttid/service/agentstatus"
)
func agentStatusServiceUnavailableError() tuttigenerated.ServiceUnavailableErrorJSONResponse {
return serviceUnavailableError(
apierrors.ServiceUnavailable(
"agent_status_service_unavailable",
apierrors.WithDeveloperMessage("agent provider status service is unavailable"),
),
)
}
func (api DaemonAPI) GetAgentProviderStatuses(ctx context.Context, request tuttigenerated.GetAgentProviderStatusesRequestObject) (tuttigenerated.GetAgentProviderStatusesResponseObject, error) {
if api.AgentStatusService == nil {
return tuttigenerated.GetAgentProviderStatuses503JSONResponse{
ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError(),
}, nil
}
snapshot, err := api.AgentStatusService.List(ctx, agentstatusservice.ListInput{
Providers: generatedAgentStatusProviders(request.Params.Providers),
IncludeNetwork: request.Params.IncludeNetwork != nil && *request.Params.IncludeNetwork,
IncludeUpdates: request.Params.IncludeUpdates != nil && *request.Params.IncludeUpdates,
ForceRefresh: request.Params.Refresh != nil && *request.Params.Refresh,
RefreshUpdates: request.Params.RefreshUpdates != nil && *request.Params.RefreshUpdates,
})
if err != nil {
return writeGetAgentProviderStatusesError(err), nil
}
return tuttigenerated.GetAgentProviderStatuses200JSONResponse(
generatedAgentProviderStatusList(snapshot, api.defaultAgentProvider(ctx)),
), nil
}
func (api DaemonAPI) ProbeAgentProvider(ctx context.Context, request tuttigenerated.ProbeAgentProviderRequestObject) (tuttigenerated.ProbeAgentProviderResponseObject, error) {
if api.AgentStatusService == nil {
return tuttigenerated.ProbeAgentProvider503JSONResponse{
ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError(),
}, nil
}
result, err := api.AgentStatusService.Probe(ctx, agentstatusservice.ProbeInput{
Provider: string(request.Provider),
})
if err != nil {
return writeProbeAgentProviderError(err), nil
}
return tuttigenerated.ProbeAgentProvider200JSONResponse(
generatedAgentProviderProbe(result),
), nil
}
func (api DaemonAPI) GetAgentProviderRuntimeCandidates(ctx context.Context, request tuttigenerated.GetAgentProviderRuntimeCandidatesRequestObject) (tuttigenerated.GetAgentProviderRuntimeCandidatesResponseObject, error) {
if api.AgentStatusService == nil {
return tuttigenerated.GetAgentProviderRuntimeCandidates503JSONResponse{ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError()}, nil
}
catalog, err := api.AgentStatusService.GetCodexRuntimeCatalog(ctx, string(request.Provider))
if err != nil {
return writeGetAgentProviderRuntimeCandidatesError(err), nil
}
return tuttigenerated.GetAgentProviderRuntimeCandidates200JSONResponse(generatedAgentProviderRuntimeCatalog(catalog)), nil
}
func (api DaemonAPI) SetAgentProviderRuntimeSelection(ctx context.Context, request tuttigenerated.SetAgentProviderRuntimeSelectionRequestObject) (tuttigenerated.SetAgentProviderRuntimeSelectionResponseObject, error) {
if api.AgentStatusService == nil {
return tuttigenerated.SetAgentProviderRuntimeSelection503JSONResponse{ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError()}, nil
}
if request.Body == nil {
return tuttigenerated.SetAgentProviderRuntimeSelection400JSONResponse{InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.EmptyBody(apierrors.WithDeveloperMessage("empty body")))}, nil
}
input := agentstatusservice.SetCodexRuntimeSelectionInput{
Provider: string(request.Provider),
CandidateID: strings.TrimSpace(request.Body.CandidateId),
Revision: strings.TrimSpace(request.Body.Revision),
}
if input.CandidateID == "" || input.Revision == "" {
return tuttigenerated.SetAgentProviderRuntimeSelection400JSONResponse{InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.MalformedRequest(apierrors.WithDeveloperMessage("candidateId and revision are required for runtime selection")))}, nil
}
catalog, err := api.AgentStatusService.SetCodexRuntimeSelection(ctx, input)
if err != nil {
return writeSetAgentProviderRuntimeSelectionError(err), nil
}
return tuttigenerated.SetAgentProviderRuntimeSelection200JSONResponse(generatedAgentProviderRuntimeCatalog(catalog)), nil
}
func (api DaemonAPI) RunAgentProviderAction(ctx context.Context, request tuttigenerated.RunAgentProviderActionRequestObject) (tuttigenerated.RunAgentProviderActionResponseObject, error) {
if api.AgentStatusService == nil {
return tuttigenerated.RunAgentProviderAction503JSONResponse{
ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError(),
}, nil
}
result, err := api.AgentStatusService.RunAction(ctx, agentstatusservice.RunActionInput{
Provider: string(request.Provider),
ActionID: agentstatusservice.ActionID(request.ActionID),
})
if err != nil {
return writeRunAgentProviderActionError(err), nil
}
if api.TuttiAgentReadiness != nil {
api.TuttiAgentReadiness.ProviderActionCompleted(result)
}
return tuttigenerated.RunAgentProviderAction200JSONResponse(
generatedAgentProviderActionRun(result),
), nil
}
func writeGetAgentProviderStatusesError(err error) tuttigenerated.GetAgentProviderStatusesResponseObject {
if errors.Is(err, agentstatusservice.ErrInvalidProvider) {
return tuttigenerated.GetAgentProviderStatuses400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(
apierrors.MalformedRequest(apierrors.WithCause(err)),
),
}
}
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.InvalidRequest:
return tuttigenerated.GetAgentProviderStatuses400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(protocolErr),
}
case tuttigenerated.ServiceUnavailable:
return tuttigenerated.GetAgentProviderStatuses503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(protocolErr),
}
default:
return tuttigenerated.GetAgentProviderStatuses502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(protocolErr),
}
}
}
func writeProbeAgentProviderError(err error) tuttigenerated.ProbeAgentProviderResponseObject {
if errors.Is(err, agentstatusservice.ErrInvalidProvider) {
return tuttigenerated.ProbeAgentProvider400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(
apierrors.MalformedRequest(apierrors.WithCause(err)),
),
}
}
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.InvalidRequest:
return tuttigenerated.ProbeAgentProvider400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(protocolErr),
}
case tuttigenerated.ServiceUnavailable:
return tuttigenerated.ProbeAgentProvider503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(protocolErr),
}
default:
return tuttigenerated.ProbeAgentProvider502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(protocolErr),
}
}
}
func writeGetAgentProviderRuntimeCandidatesError(err error) tuttigenerated.GetAgentProviderRuntimeCandidatesResponseObject {
if errors.Is(err, agentstatusservice.ErrInvalidProvider) {
return tuttigenerated.GetAgentProviderRuntimeCandidates400JSONResponse{InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.MalformedRequest(apierrors.WithCause(err)))}
}
if errors.Is(err, agentstatusservice.ErrRuntimeSelectionStoreUnavailable) {
return tuttigenerated.GetAgentProviderRuntimeCandidates503JSONResponse{ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError()}
}
return tuttigenerated.GetAgentProviderRuntimeCandidates502JSONResponse{WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err)))}
}
func writeSetAgentProviderRuntimeSelectionError(err error) tuttigenerated.SetAgentProviderRuntimeSelectionResponseObject {
if errors.Is(err, agentstatusservice.ErrInvalidProvider) ||
errors.Is(err, agentstatusservice.ErrRuntimeCatalogRevisionConflict) ||
errors.Is(err, agentstatusservice.ErrRuntimeCandidateNotFound) ||
errors.Is(err, agentstatusservice.ErrRuntimeCandidateNotLaunchable) {
return tuttigenerated.SetAgentProviderRuntimeSelection400JSONResponse{InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.MalformedRequest(apierrors.WithCause(err)))}
}
if errors.Is(err, agentstatusservice.ErrRuntimeSelectionStoreUnavailable) {
return tuttigenerated.SetAgentProviderRuntimeSelection503JSONResponse{ServiceUnavailableErrorJSONResponse: agentStatusServiceUnavailableError()}
}
return tuttigenerated.SetAgentProviderRuntimeSelection502JSONResponse{WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err)))}
}
func writeRunAgentProviderActionError(err error) tuttigenerated.RunAgentProviderActionResponseObject {
if errors.Is(err, agentstatusservice.ErrInvalidProvider) || errors.Is(err, agentstatusservice.ErrInvalidAction) {
return tuttigenerated.RunAgentProviderAction400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(
apierrors.MalformedRequest(apierrors.WithCause(err)),
),
}
}
protocolErr := apierrors.Classify(err)
switch protocolErr.Code {
case tuttigenerated.InvalidRequest:
return tuttigenerated.RunAgentProviderAction400JSONResponse{
InvalidRequestErrorJSONResponse: invalidRequestError(protocolErr),
}
case tuttigenerated.ServiceUnavailable:
return tuttigenerated.RunAgentProviderAction503JSONResponse{
ServiceUnavailableErrorJSONResponse: serviceUnavailableError(protocolErr),
}
default:
return tuttigenerated.RunAgentProviderAction502JSONResponse{
WorkspaceOperationErrorJSONResponse: workspaceOperationError(protocolErr),
}
}
}
func generatedAgentStatusProviders(providers *[]tuttigenerated.WorkspaceAgentProvider) []string {
if providers == nil || len(*providers) == 0 {
return nil
}
result := make([]string, 0, len(*providers))
for _, provider := range *providers {
result = append(result, string(provider))
}
return result
}
func (api DaemonAPI) defaultAgentProvider(ctx context.Context) tuttigenerated.WorkspaceAgentProvider {
defaultProvider := preferencesbiz.DefaultDesktopPreferences().DefaultAgentProvider
if api.PreferencesService != nil {
if preferences, err := api.PreferencesService.Get(ctx); err == nil {
defaultProvider = preferences.DefaultAgentProvider
}
}
normalized := agentproviderbiz.Normalize(defaultProvider)
if normalized == "" {
normalized = preferencesbiz.DefaultDesktopPreferences().DefaultAgentProvider
}
return tuttigenerated.WorkspaceAgentProvider(normalized)
}
func generatedAgentProviderStatusList(snapshot agentstatusservice.Snapshot, defaultProvider tuttigenerated.WorkspaceAgentProvider) tuttigenerated.AgentProviderStatusListResponse {
return tuttigenerated.AgentProviderStatusListResponse{
CapturedAt: snapshot.CapturedAt,
DefaultProvider: defaultProvider,
Providers: generatedAgentProviderStatuses(snapshot.Providers),
}
}
func generatedAgentProviderRuntimeCatalog(catalog agentstatusservice.CodexRuntimeCatalog) tuttigenerated.AgentProviderRuntimeCatalogResponse {
candidates := make([]tuttigenerated.AgentProviderRuntimeCandidate, 0, len(catalog.Candidates))
for _, candidate := range catalog.Candidates {
sources := make([]tuttigenerated.AgentProviderRuntimeCandidateSources, 0, len(candidate.Sources))
for _, source := range candidate.Sources {
sources = append(sources, tuttigenerated.AgentProviderRuntimeCandidateSources(source))
}
candidates = append(candidates, tuttigenerated.AgentProviderRuntimeCandidate{
AppServerReady: candidate.AppServerReady,
Id: candidate.ID,
LauncherPath: candidate.LauncherPath,
PackageLayoutOk: candidate.PackageLayoutOK,
PackageRoot: stringPointerIfNotBlank(candidate.PackageRoot),
ReasonCode: stringPointerIfNotBlank(candidate.ReasonCode),
Sources: sources,
State: tuttigenerated.AgentProviderRuntimeCandidateState(candidate.State),
Version: stringPointerIfNotBlank(candidate.Version),
})
}
return tuttigenerated.AgentProviderRuntimeCatalogResponse{
Candidates: candidates,
CapturedAt: catalog.CapturedAt,
Provider: tuttigenerated.WorkspaceAgentProvider(catalog.Provider),
Revision: catalog.Revision,
Selection: tuttigenerated.AgentProviderRuntimeSelection{
CandidateId: stringPointerIfNotBlank(catalog.Selection.CandidateID),
LauncherPath: stringPointerIfNotBlank(catalog.Selection.LauncherPath),
State: tuttigenerated.AgentProviderRuntimeSelectionState(catalog.Selection.State),
UpdatedAt: catalog.Selection.UpdatedAt,
},
}
}
func generatedAgentProviderActionRun(result agentstatusservice.RunActionResult) tuttigenerated.AgentProviderActionRunResponse {
return tuttigenerated.AgentProviderActionRunResponse{
ActionID: tuttigenerated.AgentProviderActionID(result.ActionID),
Command: stringPointerIfNotBlank(result.Command),
CompletedAt: result.CompletedAt,
ExitCode: result.ExitCode,
Message: stringPointerIfNotBlank(result.Message),
Probe: generatedAgentProviderProbePointer(result.Probe),
Provider: tuttigenerated.WorkspaceAgentProvider(result.Provider),
ReasonCode: stringPointerIfNotBlank(result.ReasonCode),
Status: tuttigenerated.AgentProviderActionRunStatus(result.Status),
Stderr: stringPointerIfNotBlank(result.Stderr),
Stdout: stringPointerIfNotBlank(result.Stdout),
}
}
func generatedAgentProviderProbe(result agentstatusservice.ProbeResult) tuttigenerated.AgentProviderProbeResponse {
return tuttigenerated.AgentProviderProbeResponse{
BinaryPath: stringPointerIfNotBlank(result.BinaryPath),
CheckedAt: result.CheckedAt,
Command: cloneGeneratedStrings(result.Command),
Message: stringPointerIfNotBlank(result.Message),
Provider: tuttigenerated.WorkspaceAgentProvider(result.Provider),
ReasonCode: stringPointerIfNotBlank(result.ReasonCode),
Status: tuttigenerated.AgentProviderProbeStatus(result.Status),
}
}
func generatedAgentProviderProbePointer(result *agentstatusservice.ProbeResult) *tuttigenerated.AgentProviderProbeResponse {
if result == nil {
return nil
}
generated := generatedAgentProviderProbe(*result)
return &generated
}
func generatedAgentProviderStatuses(statuses []agentstatusservice.ProviderStatus) []tuttigenerated.AgentProviderStatus {
if len(statuses) == 0 {
return []tuttigenerated.AgentProviderStatus{}
}
result := make([]tuttigenerated.AgentProviderStatus, 0, len(statuses))
for _, status := range statuses {
result = append(result, generatedAgentProviderStatus(status))
}
return result
}
func generatedAgentProviderStatus(status agentstatusservice.ProviderStatus) tuttigenerated.AgentProviderStatus {
return tuttigenerated.AgentProviderStatus{
ActiveAction: generatedAgentProviderActiveAction(status.Provider, status.ActiveAction),
Actions: generatedAgentProviderActions(status.Actions),
Adapter: generatedAgentProviderAdapterStatus(status.Adapter),
Auth: generatedAgentProviderAuthInfo(status.Auth),
Availability: generatedAgentProviderAvailability(status.Availability),
Cli: generatedAgentProviderCLIStatus(status.CLI),
Network: generatedAgentProviderNetworkStatus(status.Network),
Provider: tuttigenerated.WorkspaceAgentProvider(status.Provider),
Update: generatedAgentProviderUpdateStatus(status.Update),
}
}
func generatedAgentProviderUpdateStatus(status agentstatusservice.UpdateStatus) tuttigenerated.AgentProviderUpdateStatus {
var source *tuttigenerated.AgentProviderUpdateSource
if status.Source != "" {
value := tuttigenerated.AgentProviderUpdateSource(status.Source)
source = &value
}
return tuttigenerated.AgentProviderUpdateStatus{
Capability: tuttigenerated.AgentProviderUpdateCapability(status.Capability),
Source: source,
CurrentVersion: stringPointerIfNotBlank(status.CurrentVersion),
LatestVersion: stringPointerIfNotBlank(status.LatestVersion),
UpdateAvailable: status.UpdateAvailable,
UnsupportedReason: stringPointerIfNotBlank(status.UnsupportedReason),
LastCheckedAt: status.LastCheckedAt,
ReasonCode: stringPointerIfNotBlank(status.ReasonCode),
}
}
func generatedAgentProviderActiveAction(provider string, action *agentstatusservice.ActiveAction) *tuttigenerated.AgentProviderActiveAction {
if action == nil {
return nil
}
logLines := activeActionLog(action.Stdout)
phase := activeActionPhase(action.ID, action.Step)
slog.Info(
"agent provider API mapped active action",
"event", "tutti.agent_provider.api.active_action_mapped",
"provider", provider,
"phase", phase,
"step", action.Step,
"registryPresent", strings.TrimSpace(action.Registry) != "",
"logLines", len(logLines),
)
return &tuttigenerated.AgentProviderActiveAction{
Error: nil,
Log: logLines,
Phase: phase,
Registry: stringPointerIfNotBlank(action.Registry),
Steps: []tuttigenerated.AgentProviderActiveActionStep{},
}
}
func activeActionPhase(actionID agentstatusservice.ActionID, step string) tuttigenerated.AgentProviderActiveActionPhase {
if actionID == agentstatusservice.ActionUpdate {
return tuttigenerated.AgentProviderActiveActionPhaseUpdate
}
switch strings.TrimSpace(step) {
case "verify":
return tuttigenerated.AgentProviderActiveActionPhaseVerify
default:
return tuttigenerated.AgentProviderActiveActionPhaseInstall
}
}
func activeActionLog(output string) []string {
output = strings.TrimSpace(output)
if output == "" {
return []string{}
}
lines := strings.Split(output, "\n")
result := make([]string, 0, len(lines))
for _, line := range lines {
if trimmed := strings.TrimSpace(line); trimmed != "" {
result = append(result, trimmed)
}
}
return result
}
func generatedAgentProviderNetworkStatus(network *agentstatusservice.NetworkStatus) *tuttigenerated.AgentProviderNetworkStatus {
if network == nil {
return nil
}
result := tuttigenerated.AgentProviderNetworkStatus{
Registry: generatedAgentProviderNetworkEndpoint(network.Registry),
}
if network.ProviderAPI != nil {
api := generatedAgentProviderNetworkEndpoint(*network.ProviderAPI)
result.ProviderApi = &api
}
if network.Proxy != nil {
result.Proxy = &tuttigenerated.AgentProviderNetworkProxy{
Configured: network.Proxy.Configured,
Reachable: network.Proxy.Reachable,
Url: stringPointerIfNotBlank(network.Proxy.URL),
ReasonCode: stringPointerIfNotBlank(network.Proxy.ReasonCode),
}
}
return &result
}
func generatedAgentProviderNetworkEndpoint(endpoint agentstatusservice.NetworkEndpointStatus) tuttigenerated.AgentProviderNetworkEndpoint {
return tuttigenerated.AgentProviderNetworkEndpoint{
Reachable: endpoint.Reachable,
Endpoint: stringPointerIfNotBlank(endpoint.Endpoint),
ReasonCode: stringPointerIfNotBlank(endpoint.ReasonCode),
}
}
func generatedAgentProviderAvailability(availability agentstatusservice.Availability) tuttigenerated.AgentProviderAvailability {
return tuttigenerated.AgentProviderAvailability{
CheckedAt: availability.CheckedAt,
ReasonCode: stringPointerIfNotBlank(availability.ReasonCode),
Status: tuttigenerated.AgentProviderAvailabilityStatus(availability.Status),
}
}
func generatedAgentProviderCLIStatus(status agentstatusservice.CLIStatus) tuttigenerated.AgentProviderCliStatus {
return tuttigenerated.AgentProviderCliStatus{
BinaryPath: stringPointerIfNotBlank(status.BinaryPath),
Installed: status.Installed,
Version: stringPointerIfNotBlank(status.Version),
MinVersion: stringPointerIfNotBlank(status.MinVersion),
}
}
func generatedAgentProviderAdapterStatus(status agentstatusservice.AdapterStatus) tuttigenerated.AgentProviderAdapterStatus {
return tuttigenerated.AgentProviderAdapterStatus{
BinaryPath: stringPointerIfNotBlank(status.BinaryPath),
Command: cloneGeneratedStrings(status.Command),
Installed: status.Installed,
Version: stringPointerIfNotBlank(status.Version),
RequiredVersion: stringPointerIfNotBlank(status.RequiredVersion),
}
}
func generatedAgentProviderAuthInfo(auth agentstatusservice.AuthInfo) tuttigenerated.AgentProviderAuthInfo {
return tuttigenerated.AgentProviderAuthInfo{
AccountLabel: stringPointerIfNotBlank(auth.AccountLabel),
AuthMethod: stringPointerIfNotBlank(auth.AuthMethod),
Status: tuttigenerated.AgentProviderAuthStatus(auth.Status),
}
}
func generatedAgentProviderActions(actions []agentstatusservice.Action) []tuttigenerated.AgentProviderAction {
if len(actions) == 0 {
return []tuttigenerated.AgentProviderAction{}
}
result := make([]tuttigenerated.AgentProviderAction, 0, len(actions))
for _, action := range actions {
result = append(result, tuttigenerated.AgentProviderAction{
Command: generatedAgentProviderTerminalCommand(action.Command),
Id: tuttigenerated.AgentProviderActionID(action.ID),
Kind: tuttigenerated.AgentProviderActionKind(action.Kind),
})
}
return result
}
func generatedAgentProviderTerminalCommand(command *agentstatusservice.TerminalCommand) *tuttigenerated.AgentProviderTerminalCommand {
if command == nil {
return nil
}
return &tuttigenerated.AgentProviderTerminalCommand{
Cwd: stringPointerIfNotBlank(command.CWD),
Input: command.Input,
}
}
func stringPointerIfNotBlank(value string) *string {
if value == "" {
return nil
}
return stringPointer(value)
}
func cloneGeneratedStrings(input []string) []string {
if len(input) == 0 {
return []string{}
}
result := make([]string, len(input))
copy(result, input)
return result
}