feat: add app-owned Copilot and Grok subscription connections

This commit is contained in:
amosblomqvist
2026-10-25 00:27:52 +03:00
parent e8c81faad9
commit d6e317c6fe
49 changed files with 1709 additions and 248 deletions
+30 -1
View File
@@ -27,6 +27,8 @@
const { app, BrowserWindow, Menu, ipcMain, session, shell, dialog } = require("electron");
const { createChatGPT, fileStore } = require("./chatgpt.cjs");
const { attachChatGPTChild, trustedSender, chatGPTControl } = require("./chatgpt-ipc.cjs");
const { createSubscriptions, loadSubscriptionSDK } = require("./subscriptions.cjs");
const { attachSubscriptionChild, subscriptionControl } = require("./subscription-ipc.cjs");
const { desktopAPIHeaders } = require("./desktop-api.cjs");
const { randomBytes } = require("node:crypto");
const { spawn } = require("node:child_process");
@@ -134,6 +136,8 @@ if (process.argv.includes("--browser-service")) {
let serverProcess = null;
let chatgpt = null;
let chatgptChild = null;
let subscriptions = null;
let subscriptionChild = null;
// Per-launch request capability, never OAuth tokens and never renderer state.
let desktopAPISecret = null;
const trustedAppWindows = new Set();
@@ -222,6 +226,7 @@ function startServer(port) {
);
serverProcess = child;
chatgptChild = attachChatGPTChild(child, chatgpt);
subscriptionChild = attachSubscriptionChild(child, subscriptions);
const fwd = (buf) => buf.toString().trimEnd().split("\n").forEach((l) => l && log(`server: ${l}`));
child.stdout.on("data", fwd);
child.stderr.on("data", fwd);
@@ -230,7 +235,10 @@ function startServer(port) {
serverProcess = null;
chatgptChild?.dispose();
chatgptChild = null;
subscriptionChild?.dispose();
subscriptionChild = null;
chatgpt?.close();
subscriptions?.close();
});
resolve(child);
});
@@ -255,6 +263,7 @@ function waitUntilUp(url, tries = 150) {
function killServer() {
chatgpt?.close();
subscriptions?.close();
if (!serverProcess) return;
const child = serverProcess;
serverProcess = null;
@@ -569,6 +578,7 @@ function installMenu() {
async function main() {
app.on("window-all-closed", () => {
chatgpt?.pause();
subscriptions?.pause();
// macOS convention: the app stays resident, reactivated from the Dock.
if (process.platform !== "darwin") app.quit();
});
@@ -600,6 +610,16 @@ async function main() {
for (const win of BrowserWindow.getAllWindows()) win.webContents.send("lh:chatgpt:changed");
},
});
subscriptions = createSubscriptions({
directory: app.getPath("userData"),
loadSDK: () => loadSubscriptionSDK(path.dirname(serverEntry())),
openExternal: (target) => shell.openExternal(target),
onChange: (snapshot) => {
subscriptionChild?.send(snapshot);
for (const win of BrowserWindow.getAllWindows()) win.webContents.send("lh:subscriptions:changed");
},
});
await subscriptions.ready;
log(`starting server: ${serverEntry()}`);
log(`data dir: ${DATA_DIR()}${fs.existsSync(AUTH_FILE()) ? " · auth.json: present" : " · auth.json: ABSENT (no BYOK keys yet)"}`);
await startServer(new URL(url).port);
@@ -617,6 +637,15 @@ async function main() {
return chatGPTControl(command, chatgpt);
});
ipcMain.handle("lh:subscriptions:control", async (event, command) => {
if (!trustedAppWindows.has(BrowserWindow.fromWebContents(event.sender)) || !trustedSender(event, new URL(url).origin, BrowserWindow)) throw new Error("Untrusted desktop request.");
if (!subscriptions || !serverProcess?.connected) {
if (command?.op === "status") return [];
throw new Error("Subscription sign-in requires the desktop app's private server connection.");
}
return subscriptionControl(command, subscriptions);
});
installMenu();
// Auto-update (Wave C): wire the updater before the window so the
@@ -642,7 +671,7 @@ async function main() {
if (width < target) win.setBounds({ x, y, width: target, height });
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) { chatgpt?.resume(); createWindow(url); }
if (BrowserWindow.getAllWindows().length === 0) { chatgpt?.resume(); subscriptions?.resume(); createWindow(url); }
});
createWindow(url);
log(`window open: ${url}`);