mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-04 10:16:34 +00:00
fix(cliproxy): attach entitlement evidence to agy failures
This commit is contained in:
@@ -185,6 +185,7 @@ interface ProjectLookupResult {
|
|||||||
tier?: AccountTier;
|
tier?: AccountTier;
|
||||||
rawTierId?: string | null;
|
rawTierId?: string | null;
|
||||||
rawTierLabel?: string | null;
|
rawTierLabel?: string | null;
|
||||||
|
entitlement?: ProviderEntitlementEvidence;
|
||||||
error?: string;
|
error?: string;
|
||||||
errorCode?: string;
|
errorCode?: string;
|
||||||
errorDetail?: string;
|
errorDetail?: string;
|
||||||
@@ -219,7 +220,14 @@ function buildAntigravityFailure(
|
|||||||
bodyText?: string
|
bodyText?: string
|
||||||
): Pick<
|
): Pick<
|
||||||
QuotaResult,
|
QuotaResult,
|
||||||
'error' | 'errorCode' | 'errorDetail' | 'actionHint' | 'retryable' | 'httpStatus' | 'needsReauth'
|
| 'error'
|
||||||
|
| 'errorCode'
|
||||||
|
| 'errorDetail'
|
||||||
|
| 'actionHint'
|
||||||
|
| 'retryable'
|
||||||
|
| 'httpStatus'
|
||||||
|
| 'needsReauth'
|
||||||
|
| 'entitlement'
|
||||||
> & { isForbidden?: boolean } {
|
> & { isForbidden?: boolean } {
|
||||||
const detail = normalizeErrorDetail(bodyText || '');
|
const detail = normalizeErrorDetail(bodyText || '');
|
||||||
|
|
||||||
@@ -232,6 +240,13 @@ function buildAntigravityFailure(
|
|||||||
'Re-authenticate this account. If CLIProxy is running, retry after the proxy finishes refreshing the token.',
|
'Re-authenticate this account. If CLIProxy is running, retry after the proxy finishes refreshing the token.',
|
||||||
needsReauth: true,
|
needsReauth: true,
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'medium',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'unknown',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +258,13 @@ function buildAntigravityFailure(
|
|||||||
actionHint: 'This account does not have Gemini Code Assist quota access.',
|
actionHint: 'This account does not have Gemini Code Assist quota access.',
|
||||||
isForbidden: true,
|
isForbidden: true,
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'medium',
|
||||||
|
accessState: 'not_entitled',
|
||||||
|
capacityState: 'unknown',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +276,13 @@ function buildAntigravityFailure(
|
|||||||
actionHint: 'Retry later. This looks temporary.',
|
actionHint: 'Retry later. This looks temporary.',
|
||||||
retryable: true,
|
retryable: true,
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'low',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'rate_limited',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +294,13 @@ function buildAntigravityFailure(
|
|||||||
actionHint: 'Retry later. This looks temporary.',
|
actionHint: 'Retry later. This looks temporary.',
|
||||||
retryable: true,
|
retryable: true,
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'low',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'temporarily_unavailable',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,6 +312,13 @@ function buildAntigravityFailure(
|
|||||||
actionHint: 'Retry later. The provider appears unavailable.',
|
actionHint: 'Retry later. The provider appears unavailable.',
|
||||||
retryable: true,
|
retryable: true,
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'low',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'temporarily_unavailable',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +328,13 @@ function buildAntigravityFailure(
|
|||||||
error: `API error: ${status}`,
|
error: `API error: ${status}`,
|
||||||
errorCode: 'quota_request_failed',
|
errorCode: 'quota_request_failed',
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'low',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'unknown',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +342,13 @@ function buildAntigravityFailure(
|
|||||||
error: 'Quota request failed',
|
error: 'Quota request failed',
|
||||||
errorCode: 'quota_request_failed',
|
errorCode: 'quota_request_failed',
|
||||||
errorDetail: detail,
|
errorDetail: detail,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'low',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'unknown',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,6 +638,14 @@ async function getProjectId(accountId: string, accessToken: string): Promise<Pro
|
|||||||
errorCode: 'account_unprovisioned',
|
errorCode: 'account_unprovisioned',
|
||||||
actionHint: 'Complete sign-in in the Antigravity app, then retry quota refresh.',
|
actionHint: 'Complete sign-in in the Antigravity app, then retry quota refresh.',
|
||||||
isUnprovisioned: true,
|
isUnprovisioned: true,
|
||||||
|
entitlement: buildProviderEntitlementEvidence({
|
||||||
|
normalizedTier: 'unknown',
|
||||||
|
source: 'runtime_inference',
|
||||||
|
confidence: 'medium',
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'unknown',
|
||||||
|
notes: 'Project provisioning is incomplete for this account.',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,6 +946,10 @@ export async function fetchAllProviderQuotas(
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const __testExports = {
|
||||||
|
buildAntigravityFailure,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find available account with remaining quota
|
* Find available account with remaining quota
|
||||||
* Used by preflight check for auto-switching
|
* Used by preflight check for auto-switching
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { describe, expect, it } from 'bun:test';
|
||||||
|
|
||||||
|
async function loadAntigravityQuotaTestExports() {
|
||||||
|
const moduleId = Date.now() + Math.random();
|
||||||
|
const mod = await import(`../../../src/cliproxy/quota-fetcher?agy-quota-fetcher=${moduleId}`);
|
||||||
|
return mod.__testExports;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Antigravity quota failure metadata', () => {
|
||||||
|
it('marks 403 failures as not entitled', async () => {
|
||||||
|
const { buildAntigravityFailure } = await loadAntigravityQuotaTestExports();
|
||||||
|
|
||||||
|
const result = buildAntigravityFailure(403, 'forbidden');
|
||||||
|
|
||||||
|
expect(result.entitlement).toMatchObject({
|
||||||
|
accessState: 'not_entitled',
|
||||||
|
capacityState: 'unknown',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks 429 failures as rate limited', async () => {
|
||||||
|
const { buildAntigravityFailure } = await loadAntigravityQuotaTestExports();
|
||||||
|
|
||||||
|
const result = buildAntigravityFailure(429, 'rate limited');
|
||||||
|
|
||||||
|
expect(result.entitlement).toMatchObject({
|
||||||
|
accessState: 'unknown',
|
||||||
|
capacityState: 'rate_limited',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user