@wlv-zedd/dsh-chatgpt-web 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +140 -140
- package/cordis.patch.yml +4 -4
- package/lib/cli.js +13269 -11860
- package/lib/plugin.js +3 -50
- package/package.json +89 -88
- package/src/adapters/base.ts +16 -16
- package/src/adapters/chatgpt-web/adapter-error.ts +59 -59
- package/src/adapters/chatgpt-web/browser-helper-main.ts +513 -513
- package/src/adapters/chatgpt-web/browser-helper-prompt-selection.ts +27 -27
- package/src/adapters/chatgpt-web/browser-worker.ts +4944 -4944
- package/src/adapters/chatgpt-web/codex-rollout-environment.ts +628 -628
- package/src/adapters/chatgpt-web/compaction-handoff.ts +533 -533
- package/src/adapters/chatgpt-web/compaction-transaction.ts +142 -142
- package/src/adapters/chatgpt-web/concurrency.ts +6 -6
- package/src/adapters/chatgpt-web/conversation-key.ts +58 -58
- package/src/adapters/chatgpt-web/environment.ts +669 -669
- package/src/adapters/chatgpt-web/index.ts +1544 -1544
- package/src/adapters/chatgpt-web/input-tokens.ts +74 -74
- package/src/adapters/chatgpt-web/launcher-helper-client.ts +695 -695
- package/src/adapters/chatgpt-web/markdown.ts +418 -418
- package/src/adapters/chatgpt-web/mcp-main.ts +25 -25
- package/src/adapters/chatgpt-web/mcp-server.ts +933 -933
- package/src/adapters/chatgpt-web/model.ts +70 -70
- package/src/adapters/chatgpt-web/native-compaction-control.ts +74 -74
- package/src/adapters/chatgpt-web/output-validation.ts +62 -62
- package/src/adapters/chatgpt-web/process-line-writer.ts +46 -46
- package/src/adapters/chatgpt-web/prompt.ts +702 -702
- package/src/adapters/chatgpt-web/retry-policy.ts +73 -73
- package/src/adapters/chatgpt-web/rolling-checkpoint.ts +384 -384
- package/src/adapters/chatgpt-web/thread-environment.ts +238 -238
- package/src/adapters/chatgpt-web/tool-stream-parser.ts +1 -1
- package/src/adapters/chatgpt-web/turn-broker.ts +1481 -1481
- package/src/adapters/chatgpt-web/turn-execution.ts +816 -816
- package/src/adapters/chatgpt-web/turn-progress.ts +292 -292
- package/src/adapters/chatgpt-web/usage.ts +121 -121
- package/src/adapters/image.ts +9 -9
- package/src/bridge.ts +1083 -1083
- package/src/browser-login.ts +521 -521
- package/src/chatgpt-session.ts +240 -240
- package/src/chatgpt-web-models.ts +400 -400
- package/src/cli.ts +568 -568
- package/src/codex-integration-document.ts +824 -824
- package/src/codex-integration-journal.ts +212 -212
- package/src/codex-integration-route.ts +515 -515
- package/src/codex-integration-shared.ts +332 -332
- package/src/codex-integration.ts +529 -529
- package/src/codex-interrupt-hook.ts +158 -158
- package/src/config.ts +616 -616
- package/src/dev-chat/cli.ts +432 -432
- package/src/dev-chat/constants.ts +3 -3
- package/src/dev-chat/driver.ts +655 -655
- package/src/dev-chat/profile.ts +223 -223
- package/src/dev-chat/session.ts +287 -287
- package/src/dev-chat/transport.ts +54 -54
- package/src/doctor.ts +237 -237
- package/src/event-queue.ts +45 -45
- package/src/http-body.ts +30 -30
- package/src/launcher-browser-host.ts +695 -695
- package/src/lib/errors.ts +281 -281
- package/src/lib/token-estimate.ts +42 -42
- package/src/login-helper.cjs +1 -1
- package/src/model-catalog.ts +197 -197
- package/src/native-passthrough.ts +261 -261
- package/src/plugin.ts +191 -191
- package/src/process.ts +45 -45
- package/src/responses/compaction.ts +199 -199
- package/src/responses/parser.ts +633 -633
- package/src/responses/reasoning-envelope.ts +49 -49
- package/src/responses/schema.ts +172 -172
- package/src/responses/state.ts +230 -230
- package/src/server.ts +1111 -1111
- package/src/service.ts +315 -315
- package/src/setup.ts +671 -671
- package/src/stall-timeout.ts +23 -23
- package/src/tunnel-service.ts +160 -160
- package/src/tunnel.ts +417 -417
- package/src/turndown-plugin-gfm.d.ts +5 -5
- package/src/types.ts +307 -307
- package/src/usage/totals.ts +12 -12
- package/src/version.ts +1 -1
package/src/dev-chat/profile.ts
CHANGED
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
import { execFileSync, spawn } from "node:child_process";
|
|
2
|
-
import { readFileSync, statSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
5
|
-
import { expandUserPath, getConfigPath } from "../config";
|
|
6
|
-
import {
|
|
7
|
-
readLauncherBrowserHostDescriptor,
|
|
8
|
-
type LauncherBrowserHostDescriptor,
|
|
9
|
-
} from "../launcher-browser-host";
|
|
10
|
-
|
|
11
|
-
import { DEV_LAUNCHER_PROFILE } from "./constants";
|
|
12
|
-
|
|
13
|
-
export interface DevProfilePaths {
|
|
14
|
-
home: string;
|
|
15
|
-
codexHome: string;
|
|
16
|
-
launcherUserData: string;
|
|
17
|
-
launcherStatePath: string;
|
|
18
|
-
descriptorPath: string;
|
|
19
|
-
chatsPath: string;
|
|
20
|
-
runtimePath: string;
|
|
21
|
-
configPath: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const WINDOWS_LAUNCHER_GUID = "d1a6026a-6210-588e-9a2b-da3936f94e02";
|
|
25
|
-
|
|
26
|
-
function registeredWindowsLauncherInstallLocation(): string | undefined {
|
|
27
|
-
try {
|
|
28
|
-
const output = execFileSync(
|
|
29
|
-
"reg.exe",
|
|
30
|
-
["query", `HKCU\\Software\\${WINDOWS_LAUNCHER_GUID}`, "/v", "InstallLocation"],
|
|
31
|
-
{ encoding: "utf8", windowsHide: true },
|
|
32
|
-
);
|
|
33
|
-
const match = output.match(/^\s*InstallLocation\s+REG_SZ\s+(.+?)\s*$/mi);
|
|
34
|
-
return match && win32.isAbsolute(match[1]) ? match[1] : undefined;
|
|
35
|
-
} catch {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function resolveDevProfilePaths({
|
|
41
|
-
environment = process.env,
|
|
42
|
-
homeDirectory = homedir(),
|
|
43
|
-
}: {
|
|
44
|
-
environment?: NodeJS.ProcessEnv;
|
|
45
|
-
homeDirectory?: string;
|
|
46
|
-
} = {}): DevProfilePaths {
|
|
47
|
-
const home = resolve(expandUserPath(
|
|
48
|
-
environment.DSH_CHATGPT_FREE_DEV_HOME?.trim()
|
|
49
|
-
|| join(homeDirectory, ".dsh", "storages", "chatgpt-web-dev"),
|
|
50
|
-
));
|
|
51
|
-
const productionHome = resolve(expandUserPath(
|
|
52
|
-
environment.DSH_CHATGPT_FREE_HOME?.trim()
|
|
53
|
-
|| environment.DSH_CHATGPT_WEB_HOME?.trim()
|
|
54
|
-
|| join(homeDirectory, ".dsh", "storages", "chatgpt-web"),
|
|
55
|
-
));
|
|
56
|
-
if (home === productionHome) {
|
|
57
|
-
throw new Error("DEV profile home must differ from the production home");
|
|
58
|
-
}
|
|
59
|
-
const launcherUserData = join(home, "launcher");
|
|
60
|
-
return {
|
|
61
|
-
home,
|
|
62
|
-
codexHome: join(home, "codex-home"),
|
|
63
|
-
launcherUserData,
|
|
64
|
-
launcherStatePath: join(launcherUserData, "launcher-state.json"),
|
|
65
|
-
descriptorPath: join(home, "runtime", "launcher-browser.json"),
|
|
66
|
-
chatsPath: join(home, "chats"),
|
|
67
|
-
runtimePath: join(home, "runtime", "dev-chat"),
|
|
68
|
-
configPath: join(home, "config.json"),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface DevChatExperimentalFeatures {
|
|
73
|
-
biggerContext: boolean;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Read the canonical DEV runtime setting consumed by repository chat commands. */
|
|
77
|
-
export function readDevChatExperimentalFeatures(
|
|
78
|
-
paths = resolveDevProfilePaths(),
|
|
79
|
-
): DevChatExperimentalFeatures {
|
|
80
|
-
let value: unknown;
|
|
81
|
-
try {
|
|
82
|
-
value = JSON.parse(readFileSync(paths.configPath, "utf8"));
|
|
83
|
-
} catch (error) {
|
|
84
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return { biggerContext: false };
|
|
85
|
-
throw new Error(
|
|
86
|
-
`Could not read DEV runtime settings from ${paths.configPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || (value as { version?: unknown }).version !== 3) {
|
|
90
|
-
throw new Error(`Invalid DEV runtime settings in ${paths.configPath}`);
|
|
91
|
-
}
|
|
92
|
-
const enabled = (value as { experimentalBiggerContext?: unknown }).experimentalBiggerContext;
|
|
93
|
-
if (enabled !== undefined && typeof enabled !== "boolean") {
|
|
94
|
-
throw new Error(`Invalid Bigger Context preference in ${paths.configPath}`);
|
|
95
|
-
}
|
|
96
|
-
return { biggerContext: enabled === true };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function activateDevProfileEnvironment(paths = resolveDevProfilePaths()): DevProfilePaths {
|
|
100
|
-
process.env.DSH_CHATGPT_FREE_DEV_HOME = paths.home;
|
|
101
|
-
process.env.DSH_CHATGPT_FREE_HOME = paths.home;
|
|
102
|
-
if (getConfigPath() !== paths.configPath) {
|
|
103
|
-
throw new Error("DEV profile environment did not resolve to its isolated configuration path");
|
|
104
|
-
}
|
|
105
|
-
return paths;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function executableFile(path: string): boolean {
|
|
109
|
-
try {
|
|
110
|
-
const stat = statSync(path);
|
|
111
|
-
return stat.isFile() && (process.platform === "win32" || (stat.mode & 0o111) !== 0);
|
|
112
|
-
} catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function installedLauncherCandidates({
|
|
118
|
-
environment = process.env,
|
|
119
|
-
homeDirectory = homedir(),
|
|
120
|
-
platform = process.platform,
|
|
121
|
-
windowsInstallLocation,
|
|
122
|
-
}: {
|
|
123
|
-
environment?: NodeJS.ProcessEnv;
|
|
124
|
-
homeDirectory?: string;
|
|
125
|
-
platform?: NodeJS.Platform;
|
|
126
|
-
windowsInstallLocation?: string;
|
|
127
|
-
} = {}): string[] {
|
|
128
|
-
const override = environment.CODEX_WEB_GPT_LAUNCHER_EXECUTABLE?.trim();
|
|
129
|
-
const candidates = override ? [expandUserPath(override)] : [];
|
|
130
|
-
const targetPath = platform === "win32" ? win32 : posix;
|
|
131
|
-
if (platform === "darwin") {
|
|
132
|
-
candidates.push(
|
|
133
|
-
"/Applications/Codex Web GPT.app/Contents/MacOS/Codex Web GPT",
|
|
134
|
-
posix.join(homeDirectory, "Applications", "Codex Web GPT.app", "Contents", "MacOS", "Codex Web GPT"),
|
|
135
|
-
);
|
|
136
|
-
} else if (platform === "win32") {
|
|
137
|
-
const registeredLocation = windowsInstallLocation?.trim()
|
|
138
|
-
|| (process.platform === "win32" ? registeredWindowsLauncherInstallLocation() : undefined);
|
|
139
|
-
if (registeredLocation && win32.isAbsolute(registeredLocation)) {
|
|
140
|
-
candidates.push(win32.join(registeredLocation, "Codex Web GPT.exe"));
|
|
141
|
-
} else {
|
|
142
|
-
const localAppData = environment.LOCALAPPDATA?.trim();
|
|
143
|
-
if (localAppData) {
|
|
144
|
-
candidates.push(win32.join(localAppData, "Programs", "Codex Web GPT", "Codex Web GPT.exe"));
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} else if (platform === "linux") {
|
|
148
|
-
candidates.push(posix.join(homeDirectory, ".local", "bin", "codex-web-gpt"));
|
|
149
|
-
for (const entry of (environment.PATH || "").split(":").filter(Boolean)) {
|
|
150
|
-
candidates.push(posix.join(entry, "codex-web-gpt"));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return [...new Set(candidates.map(candidate => targetPath.resolve(candidate)))];
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function findInstalledLauncherExecutable(options: Parameters<typeof installedLauncherCandidates>[0] = {}): string {
|
|
157
|
-
const candidates = installedLauncherCandidates(options);
|
|
158
|
-
const executable = candidates.find(executableFile);
|
|
159
|
-
if (executable) return executable;
|
|
160
|
-
throw new Error(
|
|
161
|
-
"Installed Codex Web GPT launcher was not found. Install it first or set CODEX_WEB_GPT_LAUNCHER_EXECUTABLE to its absolute executable path."
|
|
162
|
-
+ ` Checked: ${candidates.join(", ") || "no platform candidates"}`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function devLauncherEnvironment(
|
|
167
|
-
paths: DevProfilePaths,
|
|
168
|
-
environment: NodeJS.ProcessEnv = process.env,
|
|
169
|
-
): NodeJS.ProcessEnv {
|
|
170
|
-
const childEnvironment = { ...environment };
|
|
171
|
-
delete childEnvironment.DSH_CHATGPT_FREE_HOME;
|
|
172
|
-
delete childEnvironment.DSH_CHATGPT_WEB_HOME;
|
|
173
|
-
childEnvironment.DSH_CHATGPT_FREE_DEV_HOME = paths.home;
|
|
174
|
-
return childEnvironment;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function devDescriptor(path: string): LauncherBrowserHostDescriptor {
|
|
178
|
-
const descriptor = readLauncherBrowserHostDescriptor(path);
|
|
179
|
-
if (descriptor.profile !== DEV_LAUNCHER_PROFILE) {
|
|
180
|
-
throw new Error(`Launcher descriptor belongs to ${descriptor.profile}, not the isolated DEV profile`);
|
|
181
|
-
}
|
|
182
|
-
return descriptor;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export async function waitForDevLauncher(
|
|
186
|
-
descriptorPath: string,
|
|
187
|
-
timeoutMs = 30_000,
|
|
188
|
-
): Promise<LauncherBrowserHostDescriptor> {
|
|
189
|
-
const deadline = Date.now() + timeoutMs;
|
|
190
|
-
let lastError = "descriptor is not ready";
|
|
191
|
-
while (Date.now() < deadline) {
|
|
192
|
-
try {
|
|
193
|
-
return devDescriptor(descriptorPath);
|
|
194
|
-
} catch (error) {
|
|
195
|
-
lastError = error instanceof Error ? error.message : String(error);
|
|
196
|
-
}
|
|
197
|
-
await new Promise(resolveWait => setTimeout(resolveWait, 100));
|
|
198
|
-
}
|
|
199
|
-
throw new Error(`DEV launcher did not become ready within ${timeoutMs}ms: ${lastError}`);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export async function launchDevProfile(
|
|
203
|
-
paths = resolveDevProfilePaths(),
|
|
204
|
-
options: { executable?: string; timeoutMs?: number } = {},
|
|
205
|
-
): Promise<{ descriptor: LauncherBrowserHostDescriptor; executable: string; alreadyRunning: boolean }> {
|
|
206
|
-
let existing: LauncherBrowserHostDescriptor | undefined;
|
|
207
|
-
try { existing = devDescriptor(paths.descriptorPath); }
|
|
208
|
-
catch { /* A stale or absent descriptor is replaced only by its owning launcher. */ }
|
|
209
|
-
|
|
210
|
-
const executable = options.executable ? resolve(options.executable) : findInstalledLauncherExecutable();
|
|
211
|
-
if (!isAbsolute(executable) || !executableFile(executable)) {
|
|
212
|
-
throw new Error(`DEV launcher executable is not an executable regular file: ${executable}`);
|
|
213
|
-
}
|
|
214
|
-
const child = spawn(executable, ["--dev-profile"], {
|
|
215
|
-
detached: true,
|
|
216
|
-
env: devLauncherEnvironment(paths),
|
|
217
|
-
stdio: "ignore",
|
|
218
|
-
windowsHide: false,
|
|
219
|
-
});
|
|
220
|
-
child.unref();
|
|
221
|
-
const descriptor = await waitForDevLauncher(paths.descriptorPath, options.timeoutMs);
|
|
222
|
-
return { descriptor, executable, alreadyRunning: existing?.pid === descriptor.pid };
|
|
223
|
-
}
|
|
1
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
2
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
5
|
+
import { expandUserPath, getConfigPath } from "../config";
|
|
6
|
+
import {
|
|
7
|
+
readLauncherBrowserHostDescriptor,
|
|
8
|
+
type LauncherBrowserHostDescriptor,
|
|
9
|
+
} from "../launcher-browser-host";
|
|
10
|
+
|
|
11
|
+
import { DEV_LAUNCHER_PROFILE } from "./constants";
|
|
12
|
+
|
|
13
|
+
export interface DevProfilePaths {
|
|
14
|
+
home: string;
|
|
15
|
+
codexHome: string;
|
|
16
|
+
launcherUserData: string;
|
|
17
|
+
launcherStatePath: string;
|
|
18
|
+
descriptorPath: string;
|
|
19
|
+
chatsPath: string;
|
|
20
|
+
runtimePath: string;
|
|
21
|
+
configPath: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const WINDOWS_LAUNCHER_GUID = "d1a6026a-6210-588e-9a2b-da3936f94e02";
|
|
25
|
+
|
|
26
|
+
function registeredWindowsLauncherInstallLocation(): string | undefined {
|
|
27
|
+
try {
|
|
28
|
+
const output = execFileSync(
|
|
29
|
+
"reg.exe",
|
|
30
|
+
["query", `HKCU\\Software\\${WINDOWS_LAUNCHER_GUID}`, "/v", "InstallLocation"],
|
|
31
|
+
{ encoding: "utf8", windowsHide: true },
|
|
32
|
+
);
|
|
33
|
+
const match = output.match(/^\s*InstallLocation\s+REG_SZ\s+(.+?)\s*$/mi);
|
|
34
|
+
return match && win32.isAbsolute(match[1]) ? match[1] : undefined;
|
|
35
|
+
} catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveDevProfilePaths({
|
|
41
|
+
environment = process.env,
|
|
42
|
+
homeDirectory = homedir(),
|
|
43
|
+
}: {
|
|
44
|
+
environment?: NodeJS.ProcessEnv;
|
|
45
|
+
homeDirectory?: string;
|
|
46
|
+
} = {}): DevProfilePaths {
|
|
47
|
+
const home = resolve(expandUserPath(
|
|
48
|
+
environment.DSH_CHATGPT_FREE_DEV_HOME?.trim()
|
|
49
|
+
|| join(homeDirectory, ".dsh", "storages", "chatgpt-web-dev"),
|
|
50
|
+
));
|
|
51
|
+
const productionHome = resolve(expandUserPath(
|
|
52
|
+
environment.DSH_CHATGPT_FREE_HOME?.trim()
|
|
53
|
+
|| environment.DSH_CHATGPT_WEB_HOME?.trim()
|
|
54
|
+
|| join(homeDirectory, ".dsh", "storages", "chatgpt-web"),
|
|
55
|
+
));
|
|
56
|
+
if (home === productionHome) {
|
|
57
|
+
throw new Error("DEV profile home must differ from the production home");
|
|
58
|
+
}
|
|
59
|
+
const launcherUserData = join(home, "launcher");
|
|
60
|
+
return {
|
|
61
|
+
home,
|
|
62
|
+
codexHome: join(home, "codex-home"),
|
|
63
|
+
launcherUserData,
|
|
64
|
+
launcherStatePath: join(launcherUserData, "launcher-state.json"),
|
|
65
|
+
descriptorPath: join(home, "runtime", "launcher-browser.json"),
|
|
66
|
+
chatsPath: join(home, "chats"),
|
|
67
|
+
runtimePath: join(home, "runtime", "dev-chat"),
|
|
68
|
+
configPath: join(home, "config.json"),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface DevChatExperimentalFeatures {
|
|
73
|
+
biggerContext: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Read the canonical DEV runtime setting consumed by repository chat commands. */
|
|
77
|
+
export function readDevChatExperimentalFeatures(
|
|
78
|
+
paths = resolveDevProfilePaths(),
|
|
79
|
+
): DevChatExperimentalFeatures {
|
|
80
|
+
let value: unknown;
|
|
81
|
+
try {
|
|
82
|
+
value = JSON.parse(readFileSync(paths.configPath, "utf8"));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return { biggerContext: false };
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Could not read DEV runtime settings from ${paths.configPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || (value as { version?: unknown }).version !== 3) {
|
|
90
|
+
throw new Error(`Invalid DEV runtime settings in ${paths.configPath}`);
|
|
91
|
+
}
|
|
92
|
+
const enabled = (value as { experimentalBiggerContext?: unknown }).experimentalBiggerContext;
|
|
93
|
+
if (enabled !== undefined && typeof enabled !== "boolean") {
|
|
94
|
+
throw new Error(`Invalid Bigger Context preference in ${paths.configPath}`);
|
|
95
|
+
}
|
|
96
|
+
return { biggerContext: enabled === true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function activateDevProfileEnvironment(paths = resolveDevProfilePaths()): DevProfilePaths {
|
|
100
|
+
process.env.DSH_CHATGPT_FREE_DEV_HOME = paths.home;
|
|
101
|
+
process.env.DSH_CHATGPT_FREE_HOME = paths.home;
|
|
102
|
+
if (getConfigPath() !== paths.configPath) {
|
|
103
|
+
throw new Error("DEV profile environment did not resolve to its isolated configuration path");
|
|
104
|
+
}
|
|
105
|
+
return paths;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function executableFile(path: string): boolean {
|
|
109
|
+
try {
|
|
110
|
+
const stat = statSync(path);
|
|
111
|
+
return stat.isFile() && (process.platform === "win32" || (stat.mode & 0o111) !== 0);
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function installedLauncherCandidates({
|
|
118
|
+
environment = process.env,
|
|
119
|
+
homeDirectory = homedir(),
|
|
120
|
+
platform = process.platform,
|
|
121
|
+
windowsInstallLocation,
|
|
122
|
+
}: {
|
|
123
|
+
environment?: NodeJS.ProcessEnv;
|
|
124
|
+
homeDirectory?: string;
|
|
125
|
+
platform?: NodeJS.Platform;
|
|
126
|
+
windowsInstallLocation?: string;
|
|
127
|
+
} = {}): string[] {
|
|
128
|
+
const override = environment.CODEX_WEB_GPT_LAUNCHER_EXECUTABLE?.trim();
|
|
129
|
+
const candidates = override ? [expandUserPath(override)] : [];
|
|
130
|
+
const targetPath = platform === "win32" ? win32 : posix;
|
|
131
|
+
if (platform === "darwin") {
|
|
132
|
+
candidates.push(
|
|
133
|
+
"/Applications/Codex Web GPT.app/Contents/MacOS/Codex Web GPT",
|
|
134
|
+
posix.join(homeDirectory, "Applications", "Codex Web GPT.app", "Contents", "MacOS", "Codex Web GPT"),
|
|
135
|
+
);
|
|
136
|
+
} else if (platform === "win32") {
|
|
137
|
+
const registeredLocation = windowsInstallLocation?.trim()
|
|
138
|
+
|| (process.platform === "win32" ? registeredWindowsLauncherInstallLocation() : undefined);
|
|
139
|
+
if (registeredLocation && win32.isAbsolute(registeredLocation)) {
|
|
140
|
+
candidates.push(win32.join(registeredLocation, "Codex Web GPT.exe"));
|
|
141
|
+
} else {
|
|
142
|
+
const localAppData = environment.LOCALAPPDATA?.trim();
|
|
143
|
+
if (localAppData) {
|
|
144
|
+
candidates.push(win32.join(localAppData, "Programs", "Codex Web GPT", "Codex Web GPT.exe"));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else if (platform === "linux") {
|
|
148
|
+
candidates.push(posix.join(homeDirectory, ".local", "bin", "codex-web-gpt"));
|
|
149
|
+
for (const entry of (environment.PATH || "").split(":").filter(Boolean)) {
|
|
150
|
+
candidates.push(posix.join(entry, "codex-web-gpt"));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return [...new Set(candidates.map(candidate => targetPath.resolve(candidate)))];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function findInstalledLauncherExecutable(options: Parameters<typeof installedLauncherCandidates>[0] = {}): string {
|
|
157
|
+
const candidates = installedLauncherCandidates(options);
|
|
158
|
+
const executable = candidates.find(executableFile);
|
|
159
|
+
if (executable) return executable;
|
|
160
|
+
throw new Error(
|
|
161
|
+
"Installed Codex Web GPT launcher was not found. Install it first or set CODEX_WEB_GPT_LAUNCHER_EXECUTABLE to its absolute executable path."
|
|
162
|
+
+ ` Checked: ${candidates.join(", ") || "no platform candidates"}`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function devLauncherEnvironment(
|
|
167
|
+
paths: DevProfilePaths,
|
|
168
|
+
environment: NodeJS.ProcessEnv = process.env,
|
|
169
|
+
): NodeJS.ProcessEnv {
|
|
170
|
+
const childEnvironment = { ...environment };
|
|
171
|
+
delete childEnvironment.DSH_CHATGPT_FREE_HOME;
|
|
172
|
+
delete childEnvironment.DSH_CHATGPT_WEB_HOME;
|
|
173
|
+
childEnvironment.DSH_CHATGPT_FREE_DEV_HOME = paths.home;
|
|
174
|
+
return childEnvironment;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function devDescriptor(path: string): LauncherBrowserHostDescriptor {
|
|
178
|
+
const descriptor = readLauncherBrowserHostDescriptor(path);
|
|
179
|
+
if (descriptor.profile !== DEV_LAUNCHER_PROFILE) {
|
|
180
|
+
throw new Error(`Launcher descriptor belongs to ${descriptor.profile}, not the isolated DEV profile`);
|
|
181
|
+
}
|
|
182
|
+
return descriptor;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function waitForDevLauncher(
|
|
186
|
+
descriptorPath: string,
|
|
187
|
+
timeoutMs = 30_000,
|
|
188
|
+
): Promise<LauncherBrowserHostDescriptor> {
|
|
189
|
+
const deadline = Date.now() + timeoutMs;
|
|
190
|
+
let lastError = "descriptor is not ready";
|
|
191
|
+
while (Date.now() < deadline) {
|
|
192
|
+
try {
|
|
193
|
+
return devDescriptor(descriptorPath);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
196
|
+
}
|
|
197
|
+
await new Promise(resolveWait => setTimeout(resolveWait, 100));
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`DEV launcher did not become ready within ${timeoutMs}ms: ${lastError}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export async function launchDevProfile(
|
|
203
|
+
paths = resolveDevProfilePaths(),
|
|
204
|
+
options: { executable?: string; timeoutMs?: number } = {},
|
|
205
|
+
): Promise<{ descriptor: LauncherBrowserHostDescriptor; executable: string; alreadyRunning: boolean }> {
|
|
206
|
+
let existing: LauncherBrowserHostDescriptor | undefined;
|
|
207
|
+
try { existing = devDescriptor(paths.descriptorPath); }
|
|
208
|
+
catch { /* A stale or absent descriptor is replaced only by its owning launcher. */ }
|
|
209
|
+
|
|
210
|
+
const executable = options.executable ? resolve(options.executable) : findInstalledLauncherExecutable();
|
|
211
|
+
if (!isAbsolute(executable) || !executableFile(executable)) {
|
|
212
|
+
throw new Error(`DEV launcher executable is not an executable regular file: ${executable}`);
|
|
213
|
+
}
|
|
214
|
+
const child = spawn(executable, ["--dev-profile"], {
|
|
215
|
+
detached: true,
|
|
216
|
+
env: devLauncherEnvironment(paths),
|
|
217
|
+
stdio: "ignore",
|
|
218
|
+
windowsHide: false,
|
|
219
|
+
});
|
|
220
|
+
child.unref();
|
|
221
|
+
const descriptor = await waitForDevLauncher(paths.descriptorPath, options.timeoutMs);
|
|
222
|
+
return { descriptor, executable, alreadyRunning: existing?.pid === descriptor.pid };
|
|
223
|
+
}
|