runCreateAppFlow runs the "create new app" flow via OpenClaw device flow. If brandOverride is non-empty, skip the interactive brand selection.
(ctx context.Context, f *cmdutil.Factory, brandOverride core.LarkBrand, msg *initMsg)
| 149 | // runCreateAppFlow runs the "create new app" flow via OpenClaw device flow. |
| 150 | // If brandOverride is non-empty, skip the interactive brand selection. |
| 151 | func runCreateAppFlow(ctx context.Context, f *cmdutil.Factory, brandOverride core.LarkBrand, msg *initMsg) (*configInitResult, error) { |
| 152 | var larkBrand core.LarkBrand |
| 153 | if brandOverride != "" { |
| 154 | larkBrand = brandOverride |
| 155 | } else { |
| 156 | // Phase 2: Brand selection |
| 157 | var brand string |
| 158 | form2 := huh.NewForm( |
| 159 | huh.NewGroup( |
| 160 | huh.NewSelect[string](). |
| 161 | Title(msg.SelectPlatform). |
| 162 | Options( |
| 163 | huh.NewOption(msg.Feishu, "feishu"), |
| 164 | huh.NewOption("Lark", "lark"), |
| 165 | ). |
| 166 | Value(&brand), |
| 167 | ), |
| 168 | ).WithTheme(cmdutil.ThemeFeishu()) |
| 169 | |
| 170 | if err := form2.Run(); err != nil { |
| 171 | if err == huh.ErrUserAborted { |
| 172 | return nil, output.ErrBare(1) |
| 173 | } |
| 174 | return nil, err |
| 175 | } |
| 176 | larkBrand = parseBrand(brand) |
| 177 | } |
| 178 | |
| 179 | // Step 1: Request app registration (begin) |
| 180 | // Use the shared proxy-plugin-aware transport so registration traffic is not |
| 181 | // a bypass of proxy plugin mode. |
| 182 | httpClient := transport.NewHTTPClient(0) |
| 183 | authResp, err := larkauth.RequestAppRegistration(httpClient, larkBrand, f.IOStreams.ErrOut) |
| 184 | if err != nil { |
| 185 | return nil, errs.NewConfigError(errs.SubtypeInvalidClient, "app registration failed: %v", err).WithCause(err) |
| 186 | } |
| 187 | |
| 188 | // Step 2: Build and display verification URL + QR code |
| 189 | verificationURL := larkauth.BuildVerificationURL(authResp.VerificationUriComplete, build.Version) |
| 190 | |
| 191 | // Branch on TTY: human-friendly copy in interactive terminals, |
| 192 | // preserve original copy for AI / non-interactive callers. |
| 193 | if f.IOStreams.IsTerminal { |
| 194 | fmt.Fprintf(f.IOStreams.ErrOut, "%s", msg.ScanQRCode) |
| 195 | qr, qrErr := qrcode.New(verificationURL, qrcode.Medium) |
| 196 | if qrErr == nil { |
| 197 | fmt.Fprint(f.IOStreams.ErrOut, qr.ToSmallString(false)) |
| 198 | } |
| 199 | fmt.Fprintf(f.IOStreams.ErrOut, "%s", msg.ScanOrOpenLink) |
| 200 | fmt.Fprintf(f.IOStreams.ErrOut, " %s\n\n", verificationURL) |
| 201 | fmt.Fprintf(f.IOStreams.ErrOut, "%s\n", msg.WaitingForScan) |
| 202 | } else { |
| 203 | qr, qrErr := qrcode.New(verificationURL, qrcode.Medium) |
| 204 | if qrErr == nil { |
| 205 | fmt.Fprint(f.IOStreams.ErrOut, qr.ToSmallString(false)) |
| 206 | } |
| 207 | fmt.Fprintf(f.IOStreams.ErrOut, "%s", msg.OpenLinkNonTTY) |
| 208 | fmt.Fprintf(f.IOStreams.ErrOut, " %s\n\n", verificationURL) |
no test coverage detected