-// write replaces the token file atomically. The file has mode 0600.
-func (a *ChatGPTAuth) write(tokens ChatGPTTokenFile) error {
- data, err := json.Marshal(tokens)
- if err != nil {
- return fmt.Errorf("encode tokens: %w", err)
- }
-
- err = writeFileAtomic(a.path, append(data, '\n'))
- if err != nil {
- return fmt.Errorf("write token file: %w", err)
- }
-
- return nil
-}
-
-// post posts body to the path of the login service and decodes a 200 answer
-// into result. It returns the HTTP status, or 0 when no answer came. The error
-// holds the status, but not the body, which can hold a token.
-func (a *ChatGPTAuth) post(ctx context.Context, path, contentType string, body []byte, result any) (status int, err error) {
- request, err := http.NewRequestWithContext(ctx, http.MethodPost, a.issuer+path, bytes.NewReader(body))
- if err != nil {
- return 0, fmt.Errorf("create request: %w", err)
- }
-
- request.Header.Set("Content-Type", contentType)
-
- response, err := a.http.Do(request)
- if err != nil {
- return 0, fmt.Errorf("send request: %w", err)
- }
-
- defer func() {
- err = errors.Join(err, response.Body.Close())
- }()
-
- if response.StatusCode != http.StatusOK {
- return response.StatusCode, fmt.Errorf("HTTP %d", response.StatusCode)
- }
-
- err = json.NewDecoder(response.Body).Decode(result)
- if err != nil {
- return response.StatusCode, fmt.Errorf("decode answer: %w", err)
- }
-
- return response.StatusCode, nil
-}
-
-// chatgptLogin runs "onechat login": the device code login into the token file
-// of the data directory. It holds the lock of the token file, so it does not
-// run while the daemon runs.
-func chatgptLogin(ctx context.Context, out io.Writer) (err error) {
- dir, err := dataDir()
- if err != nil {
- return err
- }
-
- tokens, err := lockTokens(dir)
- if err != nil {
- return err
- }
-
- defer func() {
- err = errors.Join(err, tokens.Close())
- }()
-
- return newChatGPTAuth(filepath.Join(dir, chatgptTokenFileName), chatgptIssuer).Login(ctx, out)
-}
-