237 lines
5.8 KiB
Go
237 lines
5.8 KiB
Go
package term
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
shared "plandex-shared"
|
|
|
|
"github.com/fatih/color"
|
|
)
|
|
|
|
var openUnauthenticatedCloudURL func(msg, path string)
|
|
var openAuthenticatedURL func(msg, path string)
|
|
var convertTrial func()
|
|
|
|
func SetOpenUnauthenticatedCloudURLFn(fn func(msg, path string)) {
|
|
openUnauthenticatedCloudURL = fn
|
|
}
|
|
func SetOpenAuthenticatedURLFn(fn func(msg, path string)) {
|
|
openAuthenticatedURL = fn
|
|
}
|
|
func SetConvertTrialFn(fn func()) {
|
|
convertTrial = fn
|
|
}
|
|
|
|
func OutputSimpleError(msg string, args ...interface{}) {
|
|
msg = fmt.Sprintf(msg, args...)
|
|
fmt.Fprintln(os.Stderr, color.New(ColorHiRed, color.Bold).Sprint("🚨 "+shared.Capitalize(msg)))
|
|
}
|
|
|
|
func OutputErrorAndExit(msg string, args ...interface{}) {
|
|
StopSpinner()
|
|
|
|
msg = fmt.Sprintf(msg, args...)
|
|
|
|
msg = strings.ReplaceAll(msg, "status code:", "status code")
|
|
msg = strings.ReplaceAll(msg, ", body:", ":")
|
|
|
|
displayMsg := ""
|
|
errorParts := strings.Split(msg, ": ")
|
|
|
|
addedErrors := map[string]bool{}
|
|
|
|
if len(errorParts) > 1 {
|
|
var lastPart string
|
|
i := 0
|
|
for idx, part := range errorParts {
|
|
// don't repeat the same error message
|
|
if _, ok := addedErrors[strings.ToLower(part)]; ok {
|
|
continue
|
|
}
|
|
|
|
tail := strings.Join(errorParts[idx:], ": ")
|
|
if maybeJSON(tail) {
|
|
prettyJSON := prettyJSON(tail)
|
|
indent := strings.Repeat(" ", i)
|
|
|
|
// prepend indent to **each** line in the pretty JSON
|
|
indentedJSON := strings.ReplaceAll(prettyJSON, "\n", "\n"+indent+" ")
|
|
|
|
// now write the block
|
|
displayMsg += "\n" + indent + "→ " + indentedJSON
|
|
break
|
|
}
|
|
|
|
if len(lastPart) < 10 && i > 0 {
|
|
lastPart = lastPart + ": " + part
|
|
displayMsg += ": " + part
|
|
addedErrors[strings.ToLower(lastPart)] = true
|
|
addedErrors[strings.ToLower(part)] = true
|
|
continue
|
|
}
|
|
|
|
if i != 0 {
|
|
displayMsg += "\n"
|
|
}
|
|
|
|
// indent the error message
|
|
for n := 0; n < i; n++ {
|
|
displayMsg += " "
|
|
}
|
|
if i > 0 {
|
|
displayMsg += "→ "
|
|
}
|
|
|
|
s := shared.Capitalize(part)
|
|
if i == 0 {
|
|
s = color.New(ColorHiRed, color.Bold).Sprint("🚨 " + s)
|
|
}
|
|
|
|
displayMsg += s
|
|
|
|
addedErrors[strings.ToLower(part)] = true
|
|
lastPart = part
|
|
i++
|
|
}
|
|
} else {
|
|
displayMsg = color.New(ColorHiRed, color.Bold).Sprint("🚨 " + msg)
|
|
}
|
|
|
|
fmt.Fprintln(os.Stderr, color.New(ColorHiRed, color.Bold).Sprint(displayMsg))
|
|
os.Exit(1)
|
|
}
|
|
|
|
func OutputUnformattedErrorAndExit(msg string) {
|
|
StopSpinner()
|
|
fmt.Fprintln(os.Stderr, msg)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func OutputNoCurrentPlanErrorAndExit() {
|
|
fmt.Println("🤷♂️ No current plan")
|
|
fmt.Println()
|
|
PrintCmds("", "new", "cd")
|
|
os.Exit(1)
|
|
}
|
|
|
|
func HandleApiError(apiError *shared.ApiError) {
|
|
if apiError.Type == shared.ApiErrorTypeCloudSubscriptionPaused {
|
|
if apiError.BillingError.HasBillingPermission {
|
|
StopSpinner()
|
|
fmt.Println("Your org's Plandex Cloud subscription is paused.")
|
|
res, err := ConfirmYesNo("Go to billing settings?")
|
|
if err != nil {
|
|
OutputErrorAndExit("error getting confirmation")
|
|
}
|
|
if res {
|
|
openAuthenticatedURL("Opening billing settings in your browser.", "/settings/billing")
|
|
os.Exit(0)
|
|
} else {
|
|
os.Exit(0)
|
|
}
|
|
} else {
|
|
OutputErrorAndExit("Your org's subscription is paused. Please contact an org owner to continue.")
|
|
}
|
|
}
|
|
|
|
if apiError.Type == shared.ApiErrorTypeCloudSubscriptionOverdue {
|
|
if apiError.BillingError.HasBillingPermission {
|
|
StopSpinner()
|
|
OutputSimpleError("Your org's Plandex Cloud subscription is overdue.")
|
|
res, err := ConfirmYesNo("Go to billing settings?")
|
|
if err != nil {
|
|
OutputErrorAndExit("error getting confirmation")
|
|
}
|
|
if res {
|
|
openAuthenticatedURL("Opening billing settings in your browser.", "/settings/billing")
|
|
os.Exit(0)
|
|
} else {
|
|
os.Exit(0)
|
|
}
|
|
} else {
|
|
OutputErrorAndExit("Your org's subscription is overdue. Please contact an org owner to continue.")
|
|
}
|
|
}
|
|
|
|
if apiError.Type == shared.ApiErrorTypeCloudMonthlyMaxReached {
|
|
if apiError.BillingError.HasBillingPermission {
|
|
StopSpinner()
|
|
OutputSimpleError("Your org has reached its monthly limit for Plandex Cloud.")
|
|
res, err := ConfirmYesNo("Go to billing settings?")
|
|
if err != nil {
|
|
OutputErrorAndExit("error getting confirmation")
|
|
}
|
|
if res {
|
|
openAuthenticatedURL("Opening billing settings in your browser.", "/settings/billing")
|
|
os.Exit(0)
|
|
} else {
|
|
os.Exit(0)
|
|
}
|
|
} else {
|
|
OutputErrorAndExit("Your org has reached its monthly limit for Plandex Cloud.")
|
|
}
|
|
}
|
|
|
|
if apiError.Type == shared.ApiErrorTypeCloudInsufficientCredits {
|
|
if apiError.BillingError.HasBillingPermission {
|
|
StopSpinner()
|
|
OutputSimpleError("Insufficient credits")
|
|
res, err := ConfirmYesNo("Go to billing settings?")
|
|
if err != nil {
|
|
OutputErrorAndExit("error getting confirmation")
|
|
}
|
|
if res {
|
|
openAuthenticatedURL("Opening billing settings in your browser.", "/settings/billing")
|
|
os.Exit(0)
|
|
} else {
|
|
os.Exit(0)
|
|
}
|
|
} else {
|
|
OutputErrorAndExit("Insufficient credits")
|
|
}
|
|
}
|
|
|
|
if apiError.Type == shared.ApiErrorTypeTrialMessagesExceeded {
|
|
StopSpinner()
|
|
fmt.Fprintf(os.Stderr, "\n🚨 You've reached the Plandex Cloud trial limit of %d messages per plan\n", apiError.TrialMessagesExceededError.MaxReplies)
|
|
|
|
res, err := ConfirmYesNo("Upgrade now?")
|
|
|
|
if err != nil {
|
|
OutputErrorAndExit("Error prompting upgrade trial: %v", err)
|
|
}
|
|
|
|
if res {
|
|
convertTrial()
|
|
PrintCmds("", "continue")
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
StopSpinner()
|
|
OutputErrorAndExit(apiError.Msg)
|
|
}
|
|
|
|
func maybeJSON(s string) bool {
|
|
s = strings.TrimSpace(s)
|
|
if strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}") {
|
|
return true
|
|
}
|
|
if strings.HasPrefix(s, "[") && strings.HasSuffix(s, "]") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func prettyJSON(s string) string {
|
|
var v any
|
|
if err := json.Unmarshal([]byte(s), &v); err != nil {
|
|
return s // not JSON
|
|
}
|
|
out, _ := json.MarshalIndent(v, "", " ")
|
|
return string(out)
|
|
}
|