Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions gateway/runtime/electron/official-net-fetch-statsig-hook.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
const {
registerOfficialElectronModuleOverride,
} = require("./official-electron-module-hook.cjs");
const { diagnosticLog } = require("../core/diagnostics.cjs");

// Electron main 的 net.fetch 是官方隐藏 renderer 所有 Statsig/遥测请求的最终出口。
// 无外网出口的服务器上,对 ab.chatgpt.com / chatgpt.com 遥测的 TCP 连接会一直黑洞挂起,
// Statsig 初始化永不返回,官方路由被 Suspense 永久挂起(浏览器镜像端只剩转圈);
// 用 /etc/hosts 把它指回本地又会变成快速失败并打挂页面。唯一稳的做法是在这里本地短路:
// initialize 回一份合法 gate 配置(与 web-shell polyfill 默认值一致),遥测/异常上报回空对象,
// 其余 URL 原样透传给官方 net.fetch。
const STATSIG_DEFAULT_FEATURES_CONFIG = "statsig_default_enable_features";
const STATSIG_I18N_LAYER_CONFIG = "72216192";
const STATSIG_I18N_LAYER_VALUES = { enable_i18n: true, locale_source: "IDE" };
// 505458 是官方"新工作树"入口门;Web 快照必须保留该能力,取值与 polyfill 保持一致。
const STATSIG_DEFAULT_FEATURE_OVERRIDES = {
"3903742690": true,
"505458": true,
artifacts: true,
};
// 官方 bundle 在 authed-route 模块初始化时调用 app-primary 的 side-effect 导出。真实网络下 Statsig
// 初始化有 100ms+ 往返,天然给官方 side-effect 模块留出注册窗口;若 0ms 返回会抢跑,概率性触发
// "n is not a function"。给合成响应加一个小延迟,复刻真实网络节奏,消除该竞态。
const STATSIG_INITIALIZE_DELAY_MS = Math.max(0, Number(process.env.OPENCODEX_STATSIG_INITIALIZE_DELAY_MS ?? 400) || 0);

function buildStatsigInitializeNetResponse() {
const feature_gates = {};
const dynamic_configs = {
[STATSIG_DEFAULT_FEATURES_CONFIG]: {
name: STATSIG_DEFAULT_FEATURES_CONFIG,
value: { ...STATSIG_DEFAULT_FEATURE_OVERRIDES },
rule_id: "gateway_override",
secondary_exposures: [],
},
};
for (const [name, value] of Object.entries(STATSIG_DEFAULT_FEATURE_OVERRIDES)) {
feature_gates[name] = { name, value, rule_id: "gateway_override", secondary_exposures: [] };
}
return {
has_updates: true,
time: Date.now(),
hash_used: "djb2",
feature_gates,
dynamic_configs,
layer_configs: {
[STATSIG_I18N_LAYER_CONFIG]: {
name: STATSIG_I18N_LAYER_CONFIG,
value: { ...STATSIG_I18N_LAYER_VALUES },
rule_id: "gateway_override",
secondary_exposures: [],
},
},
param_stores: {},
exposures: {},
sdk_flags: {},
};
}

// 返回本地响应体字符串;空串表示该 URL 不属于 Statsig 控制面,必须透传给官方实现。
function statsigLocalResponseBodyForUrl(rawUrl) {
let parsed;
try {
parsed = new URL(String(rawUrl || ""));
} catch {
return "";
}
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return "";
const pathname = parsed.pathname.replace(/\/+$/, "");
if (parsed.hostname === "ab.chatgpt.com") {
if (pathname === "/v1/initialize") return JSON.stringify(buildStatsigInitializeNetResponse());
if (pathname === "/v1/sdk_exception") return "{}";
return "";
}
if (parsed.hostname === "chatgpt.com" && (pathname === "/ces/v1/rgstr" || pathname === "/ces/v1/log_event")) {
return "{}";
}
return "";
}

function extractUrlFromNetFetchArgs(args) {
const first = args && args[0];
if (typeof first === "string") return first;
if (first && typeof first === "object") {
if (typeof first.url === "string") return first.url;
if (typeof first.href === "string") return first.href;
try {
return first.toString();
} catch {
return "";
}
}
return "";
}

// 构造官方 httpFetch 能消费的响应;优先用全局 Response,缺失时退回最小鸭子类型形状。
function buildStatsigNetResponse(bodyJson, url, ResponseCtor) {
if (ResponseCtor) {
return new ResponseCtor(bodyJson, {
status: 200,
headers: { "content-type": "application/json; charset=utf-8" },
});
}
const buffer = Buffer.from(bodyJson, "utf-8");
return {
ok: true,
status: 200,
statusText: "OK",
url,
headers: { get: (name) => (String(name).toLowerCase() === "content-type" ? "application/json; charset=utf-8" : null) },
json: async () => JSON.parse(bodyJson),
text: async () => bodyJson,
arrayBuffer: async () => buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength),
};
}

function installOfficialNetFetchStatsigHook(electronModule, options = {}) {
const onIntercept = typeof options.onIntercept === "function" ? options.onIntercept : null;
const nativeNet = electronModule && electronModule.net;
if (!nativeNet || typeof nativeNet.fetch !== "function") {
return { installed: false, reason: "net.fetch unavailable" };
}
const nativeFetch = nativeNet.fetch.bind(nativeNet);
const ResponseCtor = typeof Response === "function" ? Response : null;
const hookedNet = Object.assign(Object.create(Object.getPrototypeOf(nativeNet)), nativeNet, {
fetch(...args) {
const url = extractUrlFromNetFetchArgs(args);
const bodyJson = statsigLocalResponseBodyForUrl(url);
if (!bodyJson) return nativeFetch(...args);
if (onIntercept) onIntercept(url);
diagnosticLog("statsig-net-fetch", "net_fetch_served_local", { url: String(url).split("?")[0] });
const deliver = () => buildStatsigNetResponse(bodyJson, url, ResponseCtor);
// 仅初始化响应加延迟以复刻真实往返、规避官方模块初始化竞态;遥测/异常上报保持即时。
if (String(url).includes("/v1/initialize") && STATSIG_INITIALIZE_DELAY_MS > 0) {
return new Promise((resolve) => setTimeout(() => resolve(deliver()), STATSIG_INITIALIZE_DELAY_MS));
}
return Promise.resolve(deliver());
},
});
registerOfficialElectronModuleOverride(electronModule, "net", hookedNet);
// 返回覆写后的 net 便于单测直接断言;线上官方代码通过 require("electron") 拿到同一包装对象。
return { installed: true, net: hookedNet };
}

module.exports = {
installOfficialNetFetchStatsigHook,
__test: { statsigLocalResponseBodyForUrl, buildStatsigInitializeNetResponse },
};
151 changes: 151 additions & 0 deletions gateway/runtime/ipc/official-runtime.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const {
officialElectronModuleHookStatus,
} = require("../electron/official-electron-module-hook.cjs");
const { hiddenTrayHookStatus, installOfficialTrayHook } = require("../electron/official-tray-hook.cjs");
const { installOfficialNetFetchStatsigHook } = require("../electron/official-net-fetch-statsig-hook.cjs");
const { createOfficialLiveObserver } = require("./official-live-observer.cjs");
const {
gateway: gatewayPointRefs,
Expand Down Expand Up @@ -1471,6 +1472,143 @@ function fetchMessageFromIpcArgs(args) {
if (payload.type !== "fetch") return null;
return typeof payload.url === "string" ? payload : null;
}
// Statsig 遥测(/ces/v1/rgstr 设备注册、/ces/v1/log_event 事件上报)由 renderer 通过
// IPC fetch 委托给 Electron main 进程真实发 HTTP。受限网络下 Cloudflare 会返回 403 challenge,
// 回包被转发回 renderer 后 Statsig 反复报错重试,控制台持续刷 NetworkError。
// 这里在 IPC 层直接本地短路:不回官方 handler,按官方 fetch-response 协议回一个 200 成功,
// 让 Statsig 认为上报完成,从源头消除该请求与控制台噪音。
function isStatsigTelemetryFetchUrl(url) {
try {
const parsed = new URL(String(url || ""));
const pathname = parsed.pathname.replace(/\/+$/, "");
return parsed.hostname === "chatgpt.com" && (pathname === "/ces/v1/rgstr" || pathname === "/ces/v1/log_event");
} catch {
return false;
}
}

function sendStatsigTelemetryNoopResponse(message) {
const requestId = stringRouteId(message && message.requestId);
if (!requestId) return false;
routeOfficialWebContentsSend(MESSAGE_FOR_VIEW_CHANNEL, [
{
type: "fetch-response",
responseType: "success",
requestId,
status: 200,
headers: { "content-type": "application/json" },
bodyJsonString: "{}",
},
]);
return true;
}

function maybeHandleStatsigTelemetryFetchNoop(channel, args) {
if (channel !== MESSAGE_FROM_VIEW_CHANNEL) return false;
const message = fetchMessageFromIpcArgs(args);
if (!message || !isStatsigTelemetryFetchUrl(message.url)) return false;
diagnosticLog("statsig-telemetry", "fetch_blocked_local", {
method: message.method || "",
url: String(message.url).split("?")[0],
});
return sendStatsigTelemetryNoopResponse(message);
}

// Statsig 功能开关初始化(ab.chatgpt.com/v1/initialize)同样经 renderer→main 的 IPC fetch 通道发出。
// 受限网络(服务器无外网出口)下 main 的 net.fetch 会一直卡在 TCP 连接上,Statsig 初始化永不 resolve,
// 官方路由被 Suspense 边界永久挂起,页面只剩转圈白屏。polyfill 只补了 window.fetch/XHR/beacon,
// 覆盖不到这条 relay→IPC 通道,因此必须在 gateway 侧本地短路:回一份合法的空 gate 配置
// (保留 new-worktree 等关键门,取值与 polyfill 默认值一致),让 Statsig 认为初始化成功。
// SDK 异常上报(ab.chatgpt.com/v1/sdk_exception)一并吞掉,消除失败重试噪音。
const STATSIG_DEFAULT_FEATURES_CONFIG = "statsig_default_enable_features";
const STATSIG_I18N_LAYER_CONFIG = "72216192";
const STATSIG_I18N_LAYER_VALUES = { enable_i18n: true, locale_source: "IDE" };
// 与 codex-bridge-polyfill 的默认门保持一致:505458 是官方"新工作树"入口,Web 快照必须保留该能力。
const STATSIG_DEFAULT_FEATURE_OVERRIDES = {
"3903742690": true,
"505458": true,
artifacts: true,
};

// 构造与 polyfill.buildStatsigInitializeResponse 同形状的合法初始化响应,供无出口环境下本地兜底。
function buildStatsigInitializeGatewayResponse() {
const feature_gates = {};
const dynamic_configs = {
[STATSIG_DEFAULT_FEATURES_CONFIG]: {
name: STATSIG_DEFAULT_FEATURES_CONFIG,
value: { ...STATSIG_DEFAULT_FEATURE_OVERRIDES },
rule_id: "gateway_override",
secondary_exposures: [],
},
};
for (const [name, value] of Object.entries(STATSIG_DEFAULT_FEATURE_OVERRIDES)) {
feature_gates[name] = { name, value, rule_id: "gateway_override", secondary_exposures: [] };
}
return {
has_updates: true,
time: Date.now(),
hash_used: "djb2",
feature_gates,
dynamic_configs,
layer_configs: {
[STATSIG_I18N_LAYER_CONFIG]: {
name: STATSIG_I18N_LAYER_CONFIG,
value: { ...STATSIG_I18N_LAYER_VALUES },
rule_id: "gateway_override",
secondary_exposures: [],
},
},
param_stores: {},
exposures: {},
sdk_flags: {},
};
}

// 只认 ab.chatgpt.com 上的初始化/异常上报两条控制面路径;其余 fetch 一律放行给官方 handler。
function classifyStatsigControlPlaneFetchUrl(url) {
try {
const parsed = new URL(String(url || ""));
if (parsed.hostname !== "ab.chatgpt.com") return "";
const pathname = parsed.pathname.replace(/\/+$/, "");
if (pathname === "/v1/initialize") return "initialize";
if (pathname === "/v1/sdk_exception") return "sdk_exception";
return "";
} catch {
return "";
}
}

function sendStatsigControlPlaneNoopResponse(message, kind) {
const requestId = stringRouteId(message && message.requestId);
if (!requestId) return false;
routeOfficialWebContentsSend(MESSAGE_FOR_VIEW_CHANNEL, [
{
type: "fetch-response",
responseType: "success",
requestId,
status: 200,
headers: { "content-type": "application/json; charset=utf-8" },
// 初始化必须回合法 gate 配置;SDK 异常上报回空对象即可。
bodyJsonString:
kind === "initialize" ? JSON.stringify(buildStatsigInitializeGatewayResponse()) : "{}",
},
]);
return true;
}

function maybeHandleStatsigControlPlaneFetchNoop(channel, args) {
if (channel !== MESSAGE_FROM_VIEW_CHANNEL) return false;
const message = fetchMessageFromIpcArgs(args);
if (!message) return false;
const kind = classifyStatsigControlPlaneFetchUrl(message.url);
if (!kind) return false;
diagnosticLog("statsig-telemetry", "fetch_blocked_local", {
method: message.method || "",
url: String(message.url).split("?")[0],
kind,
});
return sendStatsigControlPlaneNoopResponse(message, kind);
}

function parseJsonLike(value) {
if (value && typeof value === "object" && !Array.isArray(value)) return value;
Expand Down Expand Up @@ -2158,6 +2296,8 @@ async function invokeOfficialIpc(channel, args = [], context = {}) {
// Computer Use 锁屏授权由官方 Installer 决定;这里额外记录同进程直接 status,方便和官方回包对照。
logComputerUseAuthRequest(channel, invokeArgs);
if (maybeHandleComputerUseAuthWriteNoop(channel, invokeArgs)) return true;
if (maybeHandleStatsigTelemetryFetchNoop(channel, invokeArgs)) return true;
if (maybeHandleStatsigControlPlaneFetchNoop(channel, invokeArgs)) return true;
logDesktopFeatureAvailability(channel, invokeArgs);
const handler = officialIpc.handlers.get(channel);
if (handler) {
Expand Down Expand Up @@ -2607,6 +2747,15 @@ function startOfficialRuntime(options = {}) {
onIntercept: () => recordRuntimeCompatibilityHit(gatewayPointRefs.tray),
})
);
// 官方隐藏 renderer 的 Statsig 初始化 / 遥测通过 Electron main 的 net.fetch 真实发 HTTP。
// 无外网出口的服务器上这条连接黑洞挂起,初始化永不 resolve,浏览器端被 Suspense 卡在加载页。
// 这里覆写 electron.net,对 ab.chatgpt.com 初始化/异常上报和 chatgpt.com 遥测本地短路,其余透传。
runRuntimeCompatibilityCapability(
gatewayPointRefs.netFetchStatsig,
() => installOfficialNetFetchStatsigHook(electron, {
onIntercept: () => recordRuntimeCompatibilityHit(gatewayPointRefs.netFetchStatsig),
})
);
runRuntimeCompatibilityCapability(
gatewayPointRefs.singleInstance,
patchOfficialAppSingleton
Expand Down Expand Up @@ -2675,5 +2824,7 @@ module.exports = {
threadListInvalidationEnvelope,
threadListInvalidationForOfficialMessage,
threadListInvalidationRequest,
classifyStatsigControlPlaneFetchUrl,
buildStatsigInitializeGatewayResponse,
},
};
1 change: 1 addition & 0 deletions gateway/runtime/modification/point-refs.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const gateway = Object.freeze({
hiddenChromiumServices: requiredPoint("gateway.runtime.chromium.hidden-services"),
gcmProfile: requiredPoint("gateway.runtime.chromium.gcm-profile"),
electronModuleLoader: requiredPoint("gateway.runtime.node.electron-module-loader"),
netFetchStatsig: requiredPoint("gateway.runtime.electron.net-fetch-statsig"),
notification: requiredPoint("gateway.runtime.electron.notification"),
tray: requiredPoint("gateway.runtime.electron.tray"),
ipcMain: requiredPoint("gateway.runtime.electron.ipc-main"),
Expand Down
1 change: 1 addition & 0 deletions gateway/src/modification/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export const POINT_DEFINITIONS = Object.freeze([
point("gateway.runtime.chromium.hidden-services", "关闭隐藏 Runtime 无效后台服务", "official-runtime", G.backgroundEfficiency, A.officialEnvironment),
point("gateway.runtime.chromium.gcm-profile", "隔离隐藏 Runtime 的 GCM Profile", "official-runtime", G.notifications, A.officialEnvironment),
point("gateway.runtime.node.electron-module-loader", "包装官方 electron 模块导出", "official-runtime", G.gatewayRuntime, A.electronApi),
point("gateway.runtime.electron.net-fetch-statsig", "本地短路隐藏 Renderer 的 Statsig 控制面", "official-runtime", G.gatewayRuntime, A.electronApi),
point("gateway.runtime.electron.notification", "替换官方 Notification", "official-runtime", G.notifications, A.electronApi),
point("gateway.runtime.electron.tray", "替换官方 Tray", "official-runtime", G.notifications, A.electronApi),
point("gateway.runtime.electron.ipc-main", "捕获官方 ipcMain 注册", "official-runtime", G.rendererCore, A.gatewayIpc),
Expand Down
4 changes: 2 additions & 2 deletions gateway/test/compatibility-registry.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function sourceFiles(directory) {
}

test("compatibility catalog declares groups and adapter chains for every stable point", () => {
assert.equal(POINT_DEFINITIONS.length, 103);
assert.equal(POINT_DEFINITIONS.length, 104);
assert.equal(POINT_GROUP_DEFINITIONS.length, 17);
assert.equal(ADAPTER_DEFINITIONS.length, 23);
assert.equal(new Set(POINT_DEFINITIONS.map((point) => point.id)).size, POINT_DEFINITIONS.length);
Expand All @@ -97,7 +97,7 @@ test("compatibility catalog declares groups and adapter chains for every stable
const registry = registerCompatibilityCatalog(createCompatibilityRegistry());
const snapshot = registry.snapshot();
assert.equal(snapshot.schemaVersion, 2);
assert.equal(snapshot.points.length, 103);
assert.equal(snapshot.points.length, 104);
assert.equal(snapshot.groups.length, 17);
assert.equal(snapshot.adapterTypes.length, 23);
assert.equal(snapshot.status, "pending");
Expand Down
6 changes: 3 additions & 3 deletions gateway/test/compatibility-service.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ test("public compatibility API exposes only the read-only sanitized snapshot", (
service,
), true);
assert.equal(getResponse.status, 200);
assert.equal(JSON.parse(getResponse.body).compatibility.points.length, 103);
assert.equal(JSON.parse(getResponse.body).compatibility.points.length, 104);

const reportResponse = responseRecorder();
assert.equal(handlePublicRuntimeCompatibilityApi(
Expand Down Expand Up @@ -399,7 +399,7 @@ test("authenticated API accepts only validated Browser Kernel reports", async ()
service,
), true);
assert.equal(getResponse.status, 200);
assert.equal(JSON.parse(getResponse.body).compatibility.points.length, 103);
assert.equal(JSON.parse(getResponse.body).compatibility.points.length, 104);

const point = browserKernelPoint("web.runtime.bridge.desktop-api", { active: true });
const reportResponse = responseRecorder();
Expand Down Expand Up @@ -470,7 +470,7 @@ test("authenticated Browser reports merge external Plugin SDK points into diagno

assert.equal(response.status, 200);
const snapshot = service.snapshot();
assert.equal(snapshot.points.length, 104);
assert.equal(snapshot.points.length, 105);
assert.deepEqual(
snapshot.points.find((point) => point.id === fixture.point.id).plugin,
fixture.catalog.plugin,
Expand Down
Loading