Spaces:
Paused
Paused
File size: 3,842 Bytes
5d0a52f 85aec43 347f81b 5d0a52f 85aec43 5d0a52f 85aec43 5d0a52f 85aec43 5d0a52f 85aec43 5d0a52f 347f81b 85aec43 5d0a52f 85aec43 5d0a52f 85aec43 347f81b 85aec43 347f81b 85aec43 5d0a52f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | /**
* Fingerprint manager — builds headers that mimic the Codex Desktop client.
*
* Based on Codex source: applyDesktopAuthHeaders / buildDesktopUserAgent
*/
import { getConfig, getFingerprint } from "../config.js";
import { extractChatGptAccountId } from "../auth/jwt-utils.js";
/**
* Reorder headers according to the fingerprint header_order config.
* Keys not in the order list are appended at the end.
*/
function orderHeaders(
headers: Record<string, string>,
order: string[],
): Record<string, string> {
const ordered: Record<string, string> = {};
for (const key of order) {
if (key in headers) {
ordered[key] = headers[key];
}
}
for (const key of Object.keys(headers)) {
if (!(key in ordered)) {
ordered[key] = headers[key];
}
}
return ordered;
}
/**
* Build the dynamic sec-ch-ua value based on chromium_version from config.
*/
function buildSecChUa(): string {
const cv = getConfig().client.chromium_version;
return `"Chromium";v="${cv}", "Not:A-Brand";v="24"`;
}
/**
* Build the User-Agent string from config + fingerprint template.
*/
function buildUserAgent(): string {
const config = getConfig();
const fp = getFingerprint();
return fp.user_agent_template
.replace("{version}", config.client.app_version)
.replace("{platform}", config.client.platform)
.replace("{arch}", config.client.arch);
}
/**
* Build raw headers (unordered) with all fingerprint fields.
* Does NOT include Authorization, ChatGPT-Account-Id, Content-Type, or Accept.
*/
function buildRawDefaultHeaders(): Record<string, string> {
const fp = getFingerprint();
const raw: Record<string, string> = {};
raw["User-Agent"] = buildUserAgent();
raw["sec-ch-ua"] = buildSecChUa();
// Add static default headers (Accept-Encoding, Accept-Language, sec-fetch-*, etc.)
if (fp.default_headers) {
for (const [key, value] of Object.entries(fp.default_headers)) {
raw[key] = value;
}
}
return raw;
}
/**
* Build anonymous headers for non-authenticated requests (OAuth, appcast, etc.).
* Contains User-Agent, sec-ch-ua, Accept-Encoding, Accept-Language, sec-fetch-*
* but NOT Authorization, Cookie, or ChatGPT-Account-Id.
* Headers are ordered per fingerprint config.
*/
export function buildAnonymousHeaders(): Record<string, string> {
const fp = getFingerprint();
const raw = buildRawDefaultHeaders();
return orderHeaders(raw, fp.header_order);
}
export function buildHeaders(
token: string,
accountId?: string | null,
): Record<string, string> {
const config = getConfig();
const fp = getFingerprint();
const raw: Record<string, string> = {};
raw["Authorization"] = `Bearer ${token}`;
const acctId = accountId ?? extractChatGptAccountId(token);
if (acctId) raw["ChatGPT-Account-Id"] = acctId;
raw["originator"] = config.client.originator;
// Merge default headers (User-Agent, sec-ch-ua, Accept-Encoding, etc.)
const defaults = buildRawDefaultHeaders();
for (const [key, value] of Object.entries(defaults)) {
raw[key] = value;
}
return orderHeaders(raw, fp.header_order);
}
export function buildHeadersWithContentType(
token: string,
accountId?: string | null,
): Record<string, string> {
const fp = getFingerprint();
const config = getConfig();
const raw: Record<string, string> = {};
raw["Authorization"] = `Bearer ${token}`;
const acctId = accountId ?? extractChatGptAccountId(token);
if (acctId) raw["ChatGPT-Account-Id"] = acctId;
raw["originator"] = config.client.originator;
// Merge default headers
const defaults = buildRawDefaultHeaders();
for (const [key, value] of Object.entries(defaults)) {
raw[key] = value;
}
raw["Content-Type"] = "application/json";
// Single orderHeaders call (no double-sorting)
return orderHeaders(raw, fp.header_order);
}
|