add: sign in with chatgpt credits (#974)
This commit is contained in:
@@ -3,6 +3,7 @@ import type { Request, Response } from "express";
|
||||
|
||||
import { ApiKeyPrompt, WaitingForAuth } from "./get-api-key-components";
|
||||
import { clearTerminal } from "./terminal";
|
||||
import chalk from "chalk";
|
||||
import express from "express";
|
||||
import fs from "fs/promises";
|
||||
import { render } from "ink";
|
||||
@@ -189,10 +190,10 @@ async function handleCallback(
|
||||
);
|
||||
const chatgptPlanType =
|
||||
accessTokenClaims["https://api.openai.com/auth"]?.chatgpt_plan_type;
|
||||
let needsSetup = false;
|
||||
if (chatgptPlanType === "plus" || chatgptPlanType === "pro") {
|
||||
needsSetup = !completedOnboarding;
|
||||
}
|
||||
const isOrgOwner = Boolean(
|
||||
idTokenClaims["https://api.openai.com/auth"]?.is_org_owner,
|
||||
);
|
||||
const needsSetup = !completedOnboarding && isOrgOwner;
|
||||
|
||||
// Build the success URL on the same host/port as the callback and
|
||||
// include the required query parameters for the front-end page.
|
||||
@@ -230,6 +231,58 @@ async function handleCallback(
|
||||
console.warn("Unable to save auth file:", err);
|
||||
}
|
||||
|
||||
if (
|
||||
!needsSetup &&
|
||||
(chatgptPlanType === "plus" || chatgptPlanType === "pro")
|
||||
) {
|
||||
const apiHost =
|
||||
issuer === "https://auth.openai.com"
|
||||
? "https://api.openai.com"
|
||||
: "https://api.openai.org";
|
||||
|
||||
try {
|
||||
const redeemRes = await fetch(`${apiHost}/v1/billing/redeem_credits`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ id_token: tokenData.id_token }),
|
||||
});
|
||||
|
||||
if (!redeemRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Credit redemption request failed: ${redeemRes.status} ${redeemRes.statusText}`,
|
||||
);
|
||||
} else {
|
||||
// Attempt to parse the JSON response and surface a success message
|
||||
try {
|
||||
const redeemData = (await redeemRes.json()) as {
|
||||
granted_chatgpt_subscriber_api_credits?: number;
|
||||
};
|
||||
const granted =
|
||||
redeemData?.granted_chatgpt_subscriber_api_credits ?? 0;
|
||||
if (granted > 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
chalk.green(
|
||||
`\u2728 Granted ${chatgptPlanType === "plus" ? "$5" : "$50"} in API credits for being a ChatGPT ${
|
||||
chatgptPlanType === "plus" ? "Plus" : "Pro"
|
||||
} subscriber!`,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (parseErr) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn("Unable to parse credit redemption response:", parseErr);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn("Unable to redeem ChatGPT subscriber API credits:", err);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
access_token: exchanged.access_token,
|
||||
success_url: successUrl.toString(),
|
||||
@@ -363,11 +416,62 @@ const LOGIN_SUCCESS_HTML = String.raw`
|
||||
</div>
|
||||
<div class="title">Signed in to Codex CLI</div>
|
||||
</div>
|
||||
<div class="close-box">
|
||||
<div class="close-box" style="display: none;">
|
||||
<div class="setup-description">You may now close this page</div>
|
||||
</div>
|
||||
<div class="setup-box" style="display: none;">
|
||||
<div class="setup-content">
|
||||
<div class="setup-text">
|
||||
<div class="setup-title">Finish setting up your API organization</div>
|
||||
<div class="setup-description">Add a payment method to use your organization.</div>
|
||||
</div>
|
||||
<div class="redirect-box">
|
||||
<div data-hasendicon="false" data-hasstarticon="false" data-ishovered="false" data-isinactive="false" data-ispressed="false" data-size="large" data-type="primary" class="redirect-button">
|
||||
<div class="redirect-text">Redirecting in 3s...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const needsSetup = params.get('needs_setup') === 'true';
|
||||
const platformUrl = params.get('platform_url') || 'https://platform.openai.com';
|
||||
const orgId = params.get('org_id');
|
||||
const projectId = params.get('project_id');
|
||||
const planType = params.get('plan_type');
|
||||
const idToken = params.get('id_token');
|
||||
// Show different message and optional redirect when setup is required
|
||||
if (needsSetup) {
|
||||
const setupBox = document.querySelector('.setup-box');
|
||||
setupBox.style.display = 'flex';
|
||||
const redirectUrlObj = new URL('/org-setup', platformUrl);
|
||||
redirectUrlObj.searchParams.set('p', planType);
|
||||
redirectUrlObj.searchParams.set('t', idToken);
|
||||
redirectUrlObj.searchParams.set('with_org', orgId);
|
||||
redirectUrlObj.searchParams.set('project_id', projectId);
|
||||
const redirectUrl = redirectUrlObj.toString();
|
||||
const message = document.querySelector('.redirect-text');
|
||||
let countdown = 3;
|
||||
function tick() {
|
||||
message.textContent =
|
||||
'Redirecting in ' + countdown + 's…';
|
||||
if (countdown === 0) {
|
||||
window.location.replace(redirectUrl);
|
||||
} else {
|
||||
countdown -= 1;
|
||||
setTimeout(tick, 1000);
|
||||
}
|
||||
}
|
||||
tick();
|
||||
} else {
|
||||
const closeBox = document.querySelector('.close-box');
|
||||
closeBox.style.display = 'flex';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user