This commit is contained in:
2026-04-25 06:45:36 +09:00
commit e77acee8ba
1903 changed files with 513282 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
import {
checkAdminRequestEligibility,
createAdminRequest,
getMyAdminRequests,
} from '../../services/api/adminRequests.js'
import { invalidateOverageCreditGrantCache } from '../../services/api/overageCreditGrant.js'
import { type ExtraUsage, fetchUtilization } from '../../services/api/usage.js'
import { getSubscriptionType } from '../../utils/auth.js'
import { hasClaudeAiBillingAccess } from '../../utils/billing.js'
import { openBrowser } from '../../utils/browser.js'
import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'
import { logError } from '../../utils/log.js'
type ExtraUsageResult =
| { type: 'message'; value: string }
| { type: 'browser-opened'; url: string; opened: boolean }
export async function runExtraUsage(): Promise<ExtraUsageResult> {
if (!getGlobalConfig().hasVisitedExtraUsage) {
saveGlobalConfig(prev => ({ ...prev, hasVisitedExtraUsage: true }))
}
// Invalidate only the current org's entry so a follow-up read refetches
// the granted state. Separate from the visited flag since users may run
// /extra-usage more than once while iterating on the claim flow.
invalidateOverageCreditGrantCache()
const subscriptionType = getSubscriptionType()
const isTeamOrEnterprise =
subscriptionType === 'team' || subscriptionType === 'enterprise'
const hasBillingAccess = hasClaudeAiBillingAccess()
if (!hasBillingAccess && isTeamOrEnterprise) {
// Mirror apps/claude-ai useHasUnlimitedOverage(): if overage is enabled
// with no monthly cap, there is nothing to request. On fetch error, fall
// through and let the user ask (matching web's "err toward show" behavior).
let extraUsage: ExtraUsage | null | undefined
try {
const utilization = await fetchUtilization()
extraUsage = utilization?.extra_usage
} catch (error) {
logError(error as Error)
}
if (extraUsage?.is_enabled && extraUsage.monthly_limit === null) {
return {
type: 'message',
value:
'Your organization already has unlimited extra usage. No request needed.',
}
}
try {
const eligibility = await checkAdminRequestEligibility('limit_increase')
if (eligibility?.is_allowed === false) {
return {
type: 'message',
value: 'Please contact your admin to manage extra usage settings.',
}
}
} catch (error) {
logError(error as Error)
// If eligibility check fails, continue — the create endpoint will enforce if necessary
}
try {
const pendingOrDismissedRequests = await getMyAdminRequests(
'limit_increase',
['pending', 'dismissed'],
)
if (pendingOrDismissedRequests && pendingOrDismissedRequests.length > 0) {
return {
type: 'message',
value:
'You have already submitted a request for extra usage to your admin.',
}
}
} catch (error) {
logError(error as Error)
// Fall through to creating a new request below
}
try {
await createAdminRequest({
request_type: 'limit_increase',
details: null,
})
return {
type: 'message',
value: extraUsage?.is_enabled
? 'Request sent to your admin to increase extra usage.'
: 'Request sent to your admin to enable extra usage.',
}
} catch (error) {
logError(error as Error)
// Fall through to generic message below
}
return {
type: 'message',
value: 'Please contact your admin to manage extra usage settings.',
}
}
const url = isTeamOrEnterprise
? 'https://claude.ai/admin-settings/usage'
: 'https://claude.ai/settings/usage'
try {
const opened = await openBrowser(url)
return { type: 'browser-opened', url, opened }
} catch (error) {
logError(error as Error)
return {
type: 'message',
value: `Failed to open browser. Please visit ${url} to manage extra usage.`,
}
}
}
@@ -0,0 +1,16 @@
import { runExtraUsage } from './extra-usage-core.js'
export async function call(): Promise<{ type: 'text'; value: string }> {
const result = await runExtraUsage()
if (result.type === 'message') {
return { type: 'text', value: result.value }
}
return {
type: 'text',
value: result.opened
? `Browser opened to manage extra usage. If it didn't open, visit: ${result.url}`
: `Please visit ${result.url} to manage extra usage.`,
}
}
+17
View File
@@ -0,0 +1,17 @@
import React from 'react';
import type { LocalJSXCommandContext } from '../../commands.js';
import type { LocalJSXCommandOnDone } from '../../types/command.js';
import { Login } from '../login/login.js';
import { runExtraUsage } from './extra-usage-core.js';
export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext): Promise<React.ReactNode | null> {
const result = await runExtraUsage();
if (result.type === 'message') {
onDone(result.value);
return null;
}
return <Login startingMessage={'Starting new login following /extra-usage. Exit with Ctrl-C to use existing account.'} onDone={success => {
context.onChangeAPIKey();
onDone(success ? 'Login successful' : 'Login interrupted');
}} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJMb2NhbEpTWENvbW1hbmRPbkRvbmUiLCJMb2dpbiIsInJ1bkV4dHJhVXNhZ2UiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIlByb21pc2UiLCJSZWFjdE5vZGUiLCJyZXN1bHQiLCJ0eXBlIiwidmFsdWUiLCJzdWNjZXNzIiwib25DaGFuZ2VBUElLZXkiXSwic291cmNlcyI6WyJleHRyYS11c2FnZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBMb2dpbiB9IGZyb20gJy4uL2xvZ2luL2xvZ2luLmpzJ1xuaW1wb3J0IHsgcnVuRXh0cmFVc2FnZSB9IGZyb20gJy4vZXh0cmEtdXNhZ2UtY29yZS5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGUgfCBudWxsPiB7XG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJ1bkV4dHJhVXNhZ2UoKVxuXG4gIGlmIChyZXN1bHQudHlwZSA9PT0gJ21lc3NhZ2UnKSB7XG4gICAgb25Eb25lKHJlc3VsdC52YWx1ZSlcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8TG9naW5cbiAgICAgIHN0YXJ0aW5nTWVzc2FnZT17XG4gICAgICAgICdTdGFydGluZyBuZXcgbG9naW4gZm9sbG93aW5nIC9leHRyYS11c2FnZS4gRXhpdCB3aXRoIEN0cmwtQyB0byB1c2UgZXhpc3RpbmcgYWNjb3VudC4nXG4gICAgICB9XG4gICAgICBvbkRvbmU9e3N1Y2Nlc3MgPT4ge1xuICAgICAgICBjb250ZXh0Lm9uQ2hhbmdlQVBJS2V5KClcbiAgICAgICAgb25Eb25lKHN1Y2Nlc3MgPyAnTG9naW4gc3VjY2Vzc2Z1bCcgOiAnTG9naW4gaW50ZXJydXB0ZWQnKVxuICAgICAgfX1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLGNBQWNDLHNCQUFzQixRQUFRLG1CQUFtQjtBQUMvRCxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFDbkUsU0FBU0MsS0FBSyxRQUFRLG1CQUFtQjtBQUN6QyxTQUFTQyxhQUFhLFFBQVEsdUJBQXVCO0FBRXJELE9BQU8sZUFBZUMsSUFBSUEsQ0FDeEJDLE1BQU0sRUFBRUoscUJBQXFCLEVBQzdCSyxPQUFPLEVBQUVOLHNCQUFzQixDQUNoQyxFQUFFTyxPQUFPLENBQUNSLEtBQUssQ0FBQ1MsU0FBUyxHQUFHLElBQUksQ0FBQyxDQUFDO0VBQ2pDLE1BQU1DLE1BQU0sR0FBRyxNQUFNTixhQUFhLENBQUMsQ0FBQztFQUVwQyxJQUFJTSxNQUFNLENBQUNDLElBQUksS0FBSyxTQUFTLEVBQUU7SUFDN0JMLE1BQU0sQ0FBQ0ksTUFBTSxDQUFDRSxLQUFLLENBQUM7SUFDcEIsT0FBTyxJQUFJO0VBQ2I7RUFFQSxPQUNFLENBQUMsS0FBSyxDQUNKLGVBQWUsQ0FBQyxDQUNkLHNGQUNGLENBQUMsQ0FDRCxNQUFNLENBQUMsQ0FBQ0MsT0FBTyxJQUFJO0lBQ2pCTixPQUFPLENBQUNPLGNBQWMsQ0FBQyxDQUFDO0lBQ3hCUixNQUFNLENBQUNPLE9BQU8sR0FBRyxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztFQUM1RCxDQUFDLENBQUMsR0FDRjtBQUVOIiwiaWdub3JlTGlzdCI6W119
+31
View File
@@ -0,0 +1,31 @@
import { getIsNonInteractiveSession } from '../../bootstrap/state.js'
import type { Command } from '../../commands.js'
import { isOverageProvisioningAllowed } from '../../utils/auth.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
function isExtraUsageAllowed(): boolean {
if (isEnvTruthy(process.env.DISABLE_EXTRA_USAGE_COMMAND)) {
return false
}
return isOverageProvisioningAllowed()
}
export const extraUsage = {
type: 'local-jsx',
name: 'extra-usage',
description: 'Configure extra usage to keep working when limits are hit',
isEnabled: () => isExtraUsageAllowed() && !getIsNonInteractiveSession(),
load: () => import('./extra-usage.js'),
} satisfies Command
export const extraUsageNonInteractive = {
type: 'local',
name: 'extra-usage',
supportsNonInteractive: true,
description: 'Configure extra usage to keep working when limits are hit',
isEnabled: () => isExtraUsageAllowed() && getIsNonInteractiveSession(),
get isHidden() {
return !getIsNonInteractiveSession()
},
load: () => import('./extra-usage-noninteractive.js'),
} satisfies Command