mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
fix(core): align ChatGPT token sharing with updated partner guide (#52162)
This commit is contained in:
@@ -39,11 +39,12 @@ const fallbackModels = new Set([
|
||||
])
|
||||
const nonRetryableSharingCodes = [
|
||||
"subscription_sharing_usage_limit_exceeded",
|
||||
"subscription_sharing_v2_user_not_eligible",
|
||||
"subscription_sharing_user_not_eligible",
|
||||
"subscription_sharing_unsupported_capability",
|
||||
"subscription_sharing_v2_client_not_enabled",
|
||||
"subscription_sharing_v2_route_not_supported",
|
||||
"subscription_sharing_v2_invalid_user",
|
||||
"subscription_sharing_route_not_supported",
|
||||
"subscription_sharing_invalid_user",
|
||||
"chatpass_v2_scope_not_authorized",
|
||||
"chatpass_v2_invalid_authorization_context",
|
||||
]
|
||||
type Pkce = {
|
||||
verifier: string
|
||||
@@ -155,7 +156,11 @@ const signIn = (app: App.Info, savedClientID: () => string | undefined, storage:
|
||||
)
|
||||
return yield* Effect.gen(function* () {
|
||||
// Reauthorization callbacks may omit the client ID; reuse the one this attempt started with.
|
||||
const clientID = result.clientID ?? savedID
|
||||
if (savedID && result.clientID && result.clientID !== savedID)
|
||||
return yield* Effect.fail(
|
||||
new Error("ChatGPT returned a different client than this connection. Connect again."),
|
||||
)
|
||||
const clientID = savedID ?? result.clientID
|
||||
if (!clientID)
|
||||
return yield* Effect.fail(new Error("ChatGPT sign-in did not return a client ID. Connect again."))
|
||||
const tokens = yield* exchange(result.code, clientID, redirect, pkce, app)
|
||||
|
||||
@@ -8,19 +8,21 @@ import { ModelResolver } from "../model-resolver.js"
|
||||
import { SessionRunnerModel } from "./runner/model.js"
|
||||
|
||||
const tokenSharingMessages = {
|
||||
subscription_sharing_v2_user_not_eligible:
|
||||
subscription_sharing_user_not_eligible:
|
||||
"ChatGPT token sharing isn't available for this account. Connect with an API key or choose another provider.",
|
||||
subscription_sharing_usage_limit_exceeded:
|
||||
"ChatGPT usage limit reached. Try again after your allowance resets; check ChatGPT Settings → Usage for details.",
|
||||
subscription_sharing_usage_unavailable: "ChatGPT usage can't be checked right now. Try again later.",
|
||||
subscription_sharing_unsupported_capability:
|
||||
"This request uses a feature ChatGPT token sharing doesn't support. Remove the unsupported feature and try again.",
|
||||
subscription_sharing_v2_client_not_enabled:
|
||||
"This ChatGPT client isn't enabled for token sharing. Contact the app maintainer or choose another connection.",
|
||||
subscription_sharing_v2_route_not_supported:
|
||||
subscription_sharing_route_not_supported:
|
||||
"ChatGPT token sharing doesn't support this API route. Check the configured endpoint and HTTP method.",
|
||||
subscription_sharing_v2_invalid_user: "This ChatGPT connection is no longer valid. Reconnect to ChatGPT.",
|
||||
subscription_sharing_v2_user_unavailable: "Your ChatGPT account is temporarily unavailable. Try again later.",
|
||||
subscription_sharing_invalid_user: "This ChatGPT connection is no longer valid. Reconnect to ChatGPT.",
|
||||
subscription_sharing_user_unavailable: "Your ChatGPT account is temporarily unavailable. Try again later.",
|
||||
chatpass_v2_scope_not_authorized:
|
||||
"This ChatGPT connection isn't authorized for this request. Reconnect to ChatGPT or choose another connection.",
|
||||
chatpass_v2_invalid_authorization_context:
|
||||
"This ChatGPT connection isn't authorized for this request. Reconnect to ChatGPT or choose another connection.",
|
||||
}
|
||||
|
||||
export function toSessionError(cause: unknown): SessionError.Error {
|
||||
|
||||
@@ -356,13 +356,14 @@ describe("ChatGPTPlugin", () => {
|
||||
const hooks = yield* PluginHooks.Service
|
||||
const decide = yield* SessionRunnerRetry.policy(Session.ID.make("ses_sharing_errors"))
|
||||
const cases = [
|
||||
["subscription_sharing_v2_user_not_eligible", false],
|
||||
["subscription_sharing_user_not_eligible", false],
|
||||
["subscription_sharing_unsupported_capability", false],
|
||||
["subscription_sharing_v2_client_not_enabled", false],
|
||||
["subscription_sharing_v2_route_not_supported", false],
|
||||
["subscription_sharing_v2_invalid_user", false],
|
||||
["subscription_sharing_route_not_supported", false],
|
||||
["subscription_sharing_invalid_user", false],
|
||||
["chatpass_v2_scope_not_authorized", false],
|
||||
["chatpass_v2_invalid_authorization_context", false],
|
||||
["subscription_sharing_usage_unavailable", true],
|
||||
["subscription_sharing_v2_user_unavailable", true],
|
||||
["subscription_sharing_user_unavailable", true],
|
||||
] as const
|
||||
for (const [code, retry] of cases) {
|
||||
const cause = new AIError({
|
||||
@@ -458,6 +459,39 @@ describe("ChatGPTPlugin", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("rejects a reauthorization callback that returns a different client ID", () =>
|
||||
Effect.gen(function* () {
|
||||
const credentials = yield* Credential.Service
|
||||
yield* credentials.create({
|
||||
integrationID: Integration.ID.make("openai"),
|
||||
value: Credential.OAuth.make({
|
||||
type: "oauth",
|
||||
methodID: Integration.MethodID.make("chatgpt-token-sharing"),
|
||||
access: "chatgpt-token",
|
||||
refresh: "refresh",
|
||||
expires: Date.now() + 60 * 60_000,
|
||||
metadata: { clientID: "oaiapp_issued" },
|
||||
}),
|
||||
})
|
||||
yield* addPlugin()
|
||||
const integrations = yield* Integration.Service
|
||||
const attempt = yield* integrations.oauth.connect({
|
||||
integrationID: Integration.ID.make("openai"),
|
||||
methodID: Integration.MethodID.make("chatgpt-token-sharing"),
|
||||
})
|
||||
const authorization = new URL(attempt.url)
|
||||
expect(authorization.searchParams.get("client_id")).toBe("oaiapp_issued")
|
||||
const callback = new URL(authorization.searchParams.get("redirect_uri") ?? "")
|
||||
callback.searchParams.set("code", "test-code")
|
||||
callback.searchParams.set("state", authorization.searchParams.get("state") ?? "")
|
||||
callback.searchParams.set("client_id", "oaiapp_other")
|
||||
const response = yield* Effect.promise(() => fetch(callback))
|
||||
expect(response.status).toBe(400)
|
||||
expect(yield* Effect.promise(() => response.text())).toContain("different client")
|
||||
expect(yield* credentials.list(Integration.ID.make("openai"))).toHaveLength(1)
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("moves only token-sharing credentials to the new method ID and caches their saved models", () =>
|
||||
Effect.gen(function* () {
|
||||
const credentials = yield* Credential.Service
|
||||
|
||||
@@ -138,14 +138,15 @@ describe("toSessionError", () => {
|
||||
|
||||
test("maps token-sharing failures while preserving HTTP and stream bodies", () => {
|
||||
const cases = [
|
||||
["subscription_sharing_v2_user_not_eligible", "token sharing isn't available for this account"],
|
||||
["subscription_sharing_user_not_eligible", "token sharing isn't available for this account"],
|
||||
["subscription_sharing_usage_limit_exceeded", "ChatGPT usage limit reached"],
|
||||
["subscription_sharing_usage_unavailable", "Try again later"],
|
||||
["subscription_sharing_unsupported_capability", "Remove the unsupported feature"],
|
||||
["subscription_sharing_v2_client_not_enabled", "Contact the app maintainer"],
|
||||
["subscription_sharing_v2_route_not_supported", "Check the configured endpoint"],
|
||||
["subscription_sharing_v2_invalid_user", "Reconnect to ChatGPT"],
|
||||
["subscription_sharing_v2_user_unavailable", "Try again later"],
|
||||
["subscription_sharing_route_not_supported", "Check the configured endpoint"],
|
||||
["subscription_sharing_invalid_user", "Reconnect to ChatGPT"],
|
||||
["subscription_sharing_user_unavailable", "Try again later"],
|
||||
["chatpass_v2_scope_not_authorized", "isn't authorized for this request"],
|
||||
["chatpass_v2_invalid_authorization_context", "isn't authorized for this request"],
|
||||
] as const
|
||||
for (const [code, guidance] of cases) {
|
||||
for (const body of [
|
||||
|
||||
Reference in New Issue
Block a user