impel-cli 0.18.15-beta.2 → 0.18.15-beta.4
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/package.json +1 -1
- package/src/apps.js +33 -9
- package/src/commands/apps.js +43 -8
- package/src/windowsApps.js +61 -17
package/package.json
CHANGED
package/src/apps.js
CHANGED
|
@@ -81,17 +81,27 @@ export const PINNED_VENDOR_APPS = Object.freeze({
|
|
|
81
81
|
}),
|
|
82
82
|
}),
|
|
83
83
|
chatgpt: Object.freeze({
|
|
84
|
-
version: "26.
|
|
85
|
-
codexVersion: "0.146.0-alpha.
|
|
84
|
+
version: "26.727.40816",
|
|
85
|
+
codexVersion: "0.146.0-alpha.9.2",
|
|
86
86
|
bundleName: "ChatGPT.app",
|
|
87
|
+
windows: Object.freeze({
|
|
88
|
+
storeProductId: "9PLM9XGG6VKS",
|
|
89
|
+
packageName: "OpenAI.Codex",
|
|
90
|
+
packageVersion: "26.721.11231.0",
|
|
91
|
+
advertisedPackageVersion: "26.727.4816.0",
|
|
92
|
+
codexVersion: "0.146.0-alpha.3.1",
|
|
93
|
+
publisherId: "2p2nqsd0c76g0",
|
|
94
|
+
executable: "app\\ChatGPT.exe",
|
|
95
|
+
updateManifestUrl: "https://persistent.oaistatic.com/codex-app-prod/windows-store-update.json",
|
|
96
|
+
}),
|
|
87
97
|
downloads: Object.freeze({
|
|
88
98
|
arm64: Object.freeze({
|
|
89
|
-
url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-arm64-26.
|
|
90
|
-
sha256: "
|
|
99
|
+
url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-arm64-26.727.40816.zip",
|
|
100
|
+
sha256: "fdbede9b8a28b5bf3bbf1213fa04291724faa6ed2d1d61bb04853bbdfebce219",
|
|
91
101
|
}),
|
|
92
102
|
x64: Object.freeze({
|
|
93
|
-
url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-x64-26.
|
|
94
|
-
sha256: "
|
|
103
|
+
url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-x64-26.727.40816.zip",
|
|
104
|
+
sha256: "12622d916a3993d61fb52a318dcff7af7449eea5070cdd7e094da29f380e3374",
|
|
95
105
|
}),
|
|
96
106
|
}),
|
|
97
107
|
}),
|
|
@@ -245,7 +255,7 @@ export const CURRENT_CONFIG_VERSION = 25;
|
|
|
245
255
|
// — which is what made every `impel update` re-trigger macOS permission
|
|
246
256
|
// prompts. Bump this ONLY when a code change alters the bytes of a built
|
|
247
257
|
// bundle; leave it alone for changes that don't touch bundle contents.
|
|
248
|
-
export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-07-
|
|
258
|
+
export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-07-31.1";
|
|
249
259
|
|
|
250
260
|
/** Parse the tenant's install manifest, or null when absent/corrupt. */
|
|
251
261
|
export function readTenantManifest(homeDir = os.homedir(), tenantId = null) {
|
|
@@ -679,7 +689,7 @@ export function migrateLegacyClaudeAppSessions(userData, homeDir = os.homedir())
|
|
|
679
689
|
return copied;
|
|
680
690
|
}
|
|
681
691
|
|
|
682
|
-
export async function fetchGatewayModels(config, fetchImpl = fetch) {
|
|
692
|
+
export async function fetchGatewayModels(config, fetchImpl = fetch, { allowEmpty = false } = {}) {
|
|
683
693
|
const controller = new AbortController();
|
|
684
694
|
const timeout = setTimeout(() => controller.abort(), 20000);
|
|
685
695
|
try {
|
|
@@ -697,7 +707,9 @@ export async function fetchGatewayModels(config, fetchImpl = fetch) {
|
|
|
697
707
|
}
|
|
698
708
|
if (!Array.isArray(payload?.data)) throw new Error("response has no model data array");
|
|
699
709
|
const models = payload.data.filter(isGatewayModel);
|
|
700
|
-
if (models.length === 0
|
|
710
|
+
if (models.length === 0 && !(allowEmpty && isExplicitNoSeatCatalog(payload))) {
|
|
711
|
+
throw new Error("gateway returned no supported models");
|
|
712
|
+
}
|
|
701
713
|
return { models, source: "gateway", version: payload.version ?? null };
|
|
702
714
|
} finally {
|
|
703
715
|
clearTimeout(timeout);
|
|
@@ -2213,6 +2225,7 @@ HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
|
2213
2225
|
VENDOR_APP="$HERE/../Resources/"${shellQuote(vendorBundleName)}
|
|
2214
2226
|
export CODEX_HOME=${shellQuote(paths.chatgpt.codexHome)}
|
|
2215
2227
|
export CODEX_AUTHAPI_BASE_URL=${shellQuote(codexAccountBaseUrl)}
|
|
2228
|
+
export CODEX_SPARKLE_ENABLED=false
|
|
2216
2229
|
unset CODEX_ACCESS_TOKEN CODEX_API_KEY OPENAI_API_KEY OPENAI_BASE_URL
|
|
2217
2230
|
secure_codex_home() {
|
|
2218
2231
|
[ ! -L "$CODEX_HOME" ] || exit 1
|
|
@@ -2547,6 +2560,17 @@ function isGatewayModel(model) {
|
|
|
2547
2560
|
return model && typeof model.id === "string" && (model.provider === "claude" || model.provider === "codex");
|
|
2548
2561
|
}
|
|
2549
2562
|
|
|
2563
|
+
function isExplicitNoSeatCatalog(payload) {
|
|
2564
|
+
const statuses = payload?.provider_status;
|
|
2565
|
+
return Boolean(statuses
|
|
2566
|
+
&& typeof statuses === "object"
|
|
2567
|
+
&& !Array.isArray(statuses)
|
|
2568
|
+
&& ["claude", "codex"].every((provider) => (
|
|
2569
|
+
statuses[provider]?.state === "no_seat"
|
|
2570
|
+
&& statuses[provider]?.routable === false
|
|
2571
|
+
)));
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2550
2574
|
function writeAtomic(target, contents, mode) {
|
|
2551
2575
|
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
2552
2576
|
if (fs.existsSync(target) && fs.readFileSync(target, "utf8") === contents) {
|
package/src/commands/apps.js
CHANGED
|
@@ -76,7 +76,7 @@ export function selectCatalogAppTargets(
|
|
|
76
76
|
requestedTargets,
|
|
77
77
|
models,
|
|
78
78
|
config,
|
|
79
|
-
{ log = console.log } = {},
|
|
79
|
+
{ log = console.log, allowNone = false } = {},
|
|
80
80
|
) {
|
|
81
81
|
const supported = appTargetsSupportedByModels(
|
|
82
82
|
requestedTargets,
|
|
@@ -87,6 +87,14 @@ export function selectCatalogAppTargets(
|
|
|
87
87
|
const unavailable = requestedTargets.filter((target) => !supportedSet.has(target));
|
|
88
88
|
if (unavailable.length === 0) return supported;
|
|
89
89
|
|
|
90
|
+
if (allowNone && supported.length === 0) {
|
|
91
|
+
for (const target of unavailable) {
|
|
92
|
+
const capability = APP_CAPABILITIES[target];
|
|
93
|
+
log(`Skipping ${capability.label}: the selected tenant has no ${capability.provider} models.`);
|
|
94
|
+
}
|
|
95
|
+
return supported;
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
if (requestedTargets.length === 1 || supported.length === 0) {
|
|
91
99
|
const target = unavailable[0];
|
|
92
100
|
const capability = APP_CAPABILITIES[target];
|
|
@@ -297,9 +305,9 @@ export function openManagedLauncher(launcher, {
|
|
|
297
305
|
return false;
|
|
298
306
|
}
|
|
299
307
|
|
|
300
|
-
async function fetchWindowsCatalog(config, io) {
|
|
308
|
+
async function fetchWindowsCatalog(config, io, { allowEmpty = false } = {}) {
|
|
301
309
|
try {
|
|
302
|
-
return await io.fetchModels(config);
|
|
310
|
+
return await io.fetchModels(config, undefined, { allowEmpty });
|
|
303
311
|
} catch (error) {
|
|
304
312
|
throw new Error(`tenant model catalog is unavailable (${redactSecretText(error.message)}); the Impel app profiles were not changed`);
|
|
305
313
|
}
|
|
@@ -374,12 +382,25 @@ export async function reconcileWindowsTenantApps({
|
|
|
374
382
|
...overrides,
|
|
375
383
|
};
|
|
376
384
|
const config = explicitTenantAppConfig(baseConfig, tenant, targets);
|
|
377
|
-
const catalog = await fetchWindowsCatalog(config, io);
|
|
378
|
-
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
385
|
+
const catalog = await fetchWindowsCatalog(config, io, { allowEmpty: true });
|
|
386
|
+
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
387
|
+
log: io.log,
|
|
388
|
+
allowNone: true,
|
|
389
|
+
});
|
|
379
390
|
const supported = new Set(actionTargets);
|
|
380
391
|
const unsupported = targets.filter((target) => !supported.has(target));
|
|
381
392
|
const userData = io.claudeUserData(environment, config.tenantId);
|
|
382
393
|
const paths = appPaths(homeDir, config.tenantId, { claudeUserData: userData, tenantName: config.tenantName });
|
|
394
|
+
if (actionTargets.length === 0) {
|
|
395
|
+
return {
|
|
396
|
+
tenantId: config.tenantId,
|
|
397
|
+
targets: [],
|
|
398
|
+
unsupported,
|
|
399
|
+
paths,
|
|
400
|
+
verification: {},
|
|
401
|
+
failed: [],
|
|
402
|
+
};
|
|
403
|
+
}
|
|
383
404
|
const vendorPaths = {};
|
|
384
405
|
const preparedTargets = new Set(skipVendor ? actionTargets : skipVendorTargets);
|
|
385
406
|
|
|
@@ -501,13 +522,28 @@ export async function reconcileMacTenantApps({
|
|
|
501
522
|
const config = explicitTenantAppConfig(baseConfig, tenant, targets);
|
|
502
523
|
let catalog;
|
|
503
524
|
try {
|
|
504
|
-
catalog = await io.fetchModels(config);
|
|
525
|
+
catalog = await io.fetchModels(config, undefined, { allowEmpty: true });
|
|
505
526
|
} catch (error) {
|
|
506
527
|
throw new Error(`tenant model catalog is unavailable (${redactSecretText(error?.message || error)}); no Impel app files were changed`);
|
|
507
528
|
}
|
|
508
|
-
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
529
|
+
const actionTargets = selectCatalogAppTargets(targets, catalog.models, config, {
|
|
530
|
+
log: io.log,
|
|
531
|
+
allowNone: true,
|
|
532
|
+
});
|
|
509
533
|
const supported = new Set(actionTargets);
|
|
510
534
|
const unsupported = targets.filter((target) => !supported.has(target));
|
|
535
|
+
const paths = appPaths(homeDir, config.tenantId, { tenantName: config.tenantName });
|
|
536
|
+
if (actionTargets.length === 0) {
|
|
537
|
+
return {
|
|
538
|
+
tenantId: config.tenantId,
|
|
539
|
+
targets: [],
|
|
540
|
+
unsupported,
|
|
541
|
+
paths,
|
|
542
|
+
verification: {},
|
|
543
|
+
installed: [],
|
|
544
|
+
failed: [],
|
|
545
|
+
};
|
|
546
|
+
}
|
|
511
547
|
const vendorPaths = {};
|
|
512
548
|
const statuses = io.status(actionTargets, homeDir, config.tenantId, config.tenantName);
|
|
513
549
|
for (const status of statuses) vendorPaths[status.target] ||= status.vendorPath;
|
|
@@ -549,7 +585,6 @@ export async function reconcileMacTenantApps({
|
|
|
549
585
|
vendorPaths,
|
|
550
586
|
writeBundles: staleTargets,
|
|
551
587
|
}) : [];
|
|
552
|
-
const paths = appPaths(homeDir, config.tenantId, { tenantName: config.tenantName });
|
|
553
588
|
const agentItems = [];
|
|
554
589
|
for (const item of installed) {
|
|
555
590
|
const { client, env, label } = appSkillTarget(item.target, paths);
|
package/src/windowsApps.js
CHANGED
|
@@ -11,9 +11,10 @@ import { environmentValue, nativeCommandInvocation } from "./nativeProcess.js";
|
|
|
11
11
|
import { normalizeTenantId } from "./tenants.js";
|
|
12
12
|
import { brandedEnvironmentName, RUNTIME_BRAND } from "./runtimeBrand.js";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
14
|
+
const WINDOWS_CHATGPT_PIN = PINNED_VENDOR_APPS.chatgpt.windows;
|
|
15
|
+
export const WINDOWS_CHATGPT_PACKAGE = WINDOWS_CHATGPT_PIN.storeProductId;
|
|
16
|
+
export const WINDOWS_CHATGPT_PACKAGE_NAME = WINDOWS_CHATGPT_PIN.packageName;
|
|
17
|
+
export const WINDOWS_CHATGPT_PUBLISHER_ID = WINDOWS_CHATGPT_PIN.publisherId;
|
|
17
18
|
const WINDOWS_CLAUDE_DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1000;
|
|
18
19
|
const WINDOWS_WINGET_UPDATE_NOT_APPLICABLE = 0x8A15002B;
|
|
19
20
|
|
|
@@ -60,20 +61,26 @@ function versionedSquirrelCandidates(root, readDirectory = fs.readdirSync) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
function installedMsixChatGPT(environment, run = spawnSync) {
|
|
64
|
+
function installedMsixChatGPT(environment, run = spawnSync, pin = null) {
|
|
64
65
|
const script = [
|
|
65
66
|
`$package = Get-AppxPackage -Name '${WINDOWS_CHATGPT_PACKAGE_NAME}' -ErrorAction SilentlyContinue`,
|
|
66
|
-
`$package = $package | Where-Object PublisherId -
|
|
67
|
+
`$package = $package | Where-Object { $_.PublisherId -ceq '${WINDOWS_CHATGPT_PUBLISHER_ID}' -and (-not $env:IMPEL_CHATGPT_PACKAGE_VERSION -or $_.Version.ToString() -ceq $env:IMPEL_CHATGPT_PACKAGE_VERSION) } | Sort-Object Version -Descending | Select-Object -First 1`,
|
|
67
68
|
"if ($package) {",
|
|
68
69
|
" $manifest = Get-AppxPackageManifest -Package $package",
|
|
69
70
|
" $relative = $manifest.Package.Applications.Application | Where-Object Id -eq 'App' | Select-Object -First 1 -ExpandProperty Executable",
|
|
70
|
-
"
|
|
71
|
+
" $normalized = ([string]$relative).Replace('/', '\\')",
|
|
72
|
+
" $expected = ([string]$env:IMPEL_CHATGPT_PACKAGE_EXECUTABLE).Replace('/', '\\')",
|
|
73
|
+
" if ($relative -and (-not $expected -or $normalized -ieq $expected)) { Join-Path $package.InstallLocation $relative }",
|
|
71
74
|
"}",
|
|
72
75
|
].join("; ");
|
|
73
76
|
try {
|
|
74
77
|
const result = run("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
|
|
75
78
|
encoding: "utf8",
|
|
76
|
-
env:
|
|
79
|
+
env: {
|
|
80
|
+
...windowsPowerShellEnvironment(environment),
|
|
81
|
+
IMPEL_CHATGPT_PACKAGE_VERSION: pin?.packageVersion || "",
|
|
82
|
+
IMPEL_CHATGPT_PACKAGE_EXECUTABLE: pin?.executable || "",
|
|
83
|
+
},
|
|
77
84
|
stdio: ["ignore", "pipe", "ignore"],
|
|
78
85
|
windowsHide: true,
|
|
79
86
|
});
|
|
@@ -141,9 +148,23 @@ export function findWindowsChatGPTApp(environment = process.env, dependencies =
|
|
|
141
148
|
return msix && exists(msix) ? path.win32.normalize(msix) : null;
|
|
142
149
|
}
|
|
143
150
|
|
|
144
|
-
function
|
|
151
|
+
function pinnedWindowsChatGPTApp(environment = process.env, dependencies = {}) {
|
|
152
|
+
const exists = dependencies.isFile || isFile;
|
|
153
|
+
const pin = dependencies.pin || WINDOWS_CHATGPT_PIN;
|
|
154
|
+
const msix = (dependencies.queryMsix || installedMsixChatGPT)(environment, dependencies.run, pin);
|
|
155
|
+
return msix && exists(msix) ? path.win32.normalize(msix) : null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function wingetInvocation(action, environment, {
|
|
159
|
+
packageId,
|
|
160
|
+
source = "winget",
|
|
161
|
+
scope = "user",
|
|
162
|
+
}) {
|
|
145
163
|
const verb = action === "update" ? "upgrade" : "install";
|
|
146
164
|
const scopeArgs = scope ? ["--scope", scope] : [];
|
|
165
|
+
// Microsoft Store packages commonly report Version "Unknown" to winget.
|
|
166
|
+
// Without this switch winget refuses to consider their available update.
|
|
167
|
+
const updateArgs = action === "update" ? ["--include-unknown"] : [];
|
|
147
168
|
return nativeCommandInvocation(
|
|
148
169
|
"winget",
|
|
149
170
|
[
|
|
@@ -154,6 +175,7 @@ function wingetInvocation(action, environment, { packageId, source = "winget", s
|
|
|
154
175
|
"--source",
|
|
155
176
|
source,
|
|
156
177
|
...scopeArgs,
|
|
178
|
+
...updateArgs,
|
|
157
179
|
"--silent",
|
|
158
180
|
"--disable-interactivity",
|
|
159
181
|
"--accept-package-agreements",
|
|
@@ -372,21 +394,31 @@ export async function ensureWindowsClaudeApp(_options = {}, dependencies = {}) {
|
|
|
372
394
|
|
|
373
395
|
/** Install/update OpenAI's official ChatGPT/Codex Store package. */
|
|
374
396
|
export function ensureWindowsChatGPTApp({ update = false } = {}, dependencies = {}) {
|
|
397
|
+
const find = dependencies.find || findWindowsChatGPTApp;
|
|
398
|
+
const findPinned = dependencies.findPinned || dependencies.find || pinnedWindowsChatGPTApp;
|
|
375
399
|
const io = {
|
|
376
400
|
environment: process.env,
|
|
377
|
-
find
|
|
401
|
+
find,
|
|
402
|
+
findPinned,
|
|
378
403
|
logger: console,
|
|
404
|
+
pin: WINDOWS_CHATGPT_PIN,
|
|
379
405
|
run: spawnSync,
|
|
380
406
|
...dependencies,
|
|
381
407
|
};
|
|
382
408
|
const before = io.find(io.environment);
|
|
383
|
-
|
|
409
|
+
const pinnedBefore = io.findPinned(io.environment, { pin: io.pin, run: io.run });
|
|
410
|
+
if (pinnedBefore && !update) {
|
|
411
|
+
return { binary: pinnedBefore, action: "existing", result: null };
|
|
412
|
+
}
|
|
384
413
|
|
|
385
414
|
// `impel update` also repairs missing surfaces. A missing Store package must
|
|
386
415
|
// be installed, not sent to `winget upgrade` (which returns 0x8A150014).
|
|
387
416
|
const shouldUpdate = Boolean(update && before);
|
|
417
|
+
// The msstore source rejects its AppX package version as a winget --version
|
|
418
|
+
// selector. Install the current Store package, then fail closed below unless
|
|
419
|
+
// Windows registered the exact reviewed package version and executable.
|
|
388
420
|
const invocation = wingetInvocation(shouldUpdate ? "update" : "install", io.environment, {
|
|
389
|
-
packageId:
|
|
421
|
+
packageId: io.pin.storeProductId,
|
|
390
422
|
source: "msstore",
|
|
391
423
|
scope: null,
|
|
392
424
|
});
|
|
@@ -400,26 +432,36 @@ export function ensureWindowsChatGPTApp({ update = false } = {}, dependencies =
|
|
|
400
432
|
} catch (error) {
|
|
401
433
|
result = { status: null, error };
|
|
402
434
|
}
|
|
403
|
-
|
|
435
|
+
let resultBinary = io.findPinned(io.environment, { pin: io.pin, run: io.run });
|
|
404
436
|
// winget uses an error HRESULT when `upgrade` finds an installed package
|
|
405
437
|
// with no newer Store release. That is a healthy idempotent update result,
|
|
406
438
|
// not an installation failure (APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE).
|
|
407
439
|
const alreadyUpToDate = Boolean(
|
|
408
440
|
shouldUpdate
|
|
409
|
-
&&
|
|
441
|
+
&& resultBinary
|
|
410
442
|
&& !result?.error
|
|
411
443
|
&& Number.isInteger(result?.status)
|
|
412
444
|
&& (result.status >>> 0) === WINDOWS_WINGET_UPDATE_NOT_APPLICABLE,
|
|
413
445
|
);
|
|
414
446
|
if (alreadyUpToDate) {
|
|
415
|
-
io.logger.log(
|
|
447
|
+
io.logger.log(`ChatGPT/Codex: Microsoft Store package ${io.pin.packageVersion} already up to date.`);
|
|
448
|
+
}
|
|
449
|
+
const commandSucceeded = (result?.status === 0 && !result?.error) || alreadyUpToDate;
|
|
450
|
+
if (commandSucceeded && !resultBinary) {
|
|
451
|
+
result = {
|
|
452
|
+
...result,
|
|
453
|
+
error: new Error(
|
|
454
|
+
`ChatGPT/Codex Store package ${io.pin.packageVersion} did not appear after winget completed`,
|
|
455
|
+
),
|
|
456
|
+
};
|
|
416
457
|
}
|
|
417
|
-
const succeeded =
|
|
458
|
+
const succeeded = commandSucceeded && Boolean(resultBinary);
|
|
459
|
+
if (!succeeded) resultBinary = null;
|
|
418
460
|
return {
|
|
419
|
-
binary,
|
|
461
|
+
binary: resultBinary,
|
|
420
462
|
action: alreadyUpToDate
|
|
421
463
|
? "existing"
|
|
422
|
-
: succeeded ? (shouldUpdate ? "updated" : "installed") :
|
|
464
|
+
: succeeded ? (shouldUpdate ? "updated" : "installed") : "install-failed",
|
|
423
465
|
result,
|
|
424
466
|
};
|
|
425
467
|
}
|
|
@@ -617,6 +659,7 @@ export function launchWindowsChatGPTApp(binary, {
|
|
|
617
659
|
"CODEX_AUTHAPI_BASE_URL",
|
|
618
660
|
"CODEX_ELECTRON_USER_DATA_PATH",
|
|
619
661
|
"CODEX_HOME",
|
|
662
|
+
"CODEX_SPARKLE_ENABLED",
|
|
620
663
|
"OPENAI_API_KEY",
|
|
621
664
|
"OPENAI_BASE_URL",
|
|
622
665
|
]);
|
|
@@ -625,6 +668,7 @@ export function launchWindowsChatGPTApp(binary, {
|
|
|
625
668
|
}
|
|
626
669
|
environment.CODEX_HOME = codexHome;
|
|
627
670
|
environment.CODEX_AUTHAPI_BASE_URL = `${gatewayUrl}/chatgpt_passthrough/backend-api`;
|
|
671
|
+
environment.CODEX_SPARKLE_ENABLED = "false";
|
|
628
672
|
// Codex Desktop resolves this before acquiring Electron's single-instance
|
|
629
673
|
// lock. Keep --user-data-dir below as a compatibility fallback for builds
|
|
630
674
|
// that predate the dedicated desktop override.
|