impel-cli 0.20.49 → 0.20.50
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/RELEASE_NOTES.md +22 -0
- package/package.json +1 -1
- package/src/apps.js +6 -2
- package/src/desktopTasks.js +125 -31
- package/src/managedProfileVersion.js +5 -3
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## 0.20.50 — Restore ChatGPT Tasks safely
|
|
4
|
+
|
|
5
|
+
- Restores one embedded Tasks entry in the managed ChatGPT sidebar while
|
|
6
|
+
keeping Claude's production preload inert and preserving normal ChatGPT
|
|
7
|
+
startup and navigation.
|
|
8
|
+
- Disables the native fallback in the production ChatGPT profile, so Tasks
|
|
9
|
+
cannot create a second control view or cover the vendor app during startup.
|
|
10
|
+
- Debounces sidebar discovery, makes attached-control checks constant-time,
|
|
11
|
+
and removes the outer reinjection retry chain that could repeatedly scan the
|
|
12
|
+
renderer while its sidebar anchor was late.
|
|
13
|
+
- Injects and accepts Tasks navigation only in the main vendor frame; subframes
|
|
14
|
+
cannot open, resize, or bootstrap the authenticated board.
|
|
15
|
+
- Deduplicates paired Electron navigation events and reuses the loaded board,
|
|
16
|
+
keeping one tenant-bound credential exchange across ordinary hide/reopen
|
|
17
|
+
cycles while allowing an expired or failed board session to recover.
|
|
18
|
+
- Advances the managed profile contract to v41 so existing macOS ChatGPT apps
|
|
19
|
+
rewrite and reload the repaired preload without rebuilding vendor bundles.
|
|
20
|
+
- Adds a known-bad `0.20.48` control and a real packaged-app gate with screenshot
|
|
21
|
+
artifacts for the normal surface, one Tasks tab, and the interactive board;
|
|
22
|
+
it also externally observes the board target becoming compositor-hidden when
|
|
23
|
+
host navigation is restored, with zero fallback controls and one bootstrap.
|
|
24
|
+
|
|
3
25
|
## 0.20.49 — Restore managed app startup
|
|
4
26
|
|
|
5
27
|
- Restores normal ChatGPT and Claude startup by disabling the experimental
|
package/package.json
CHANGED
package/src/apps.js
CHANGED
|
@@ -660,9 +660,12 @@ export function appPaths(homeDir = os.homedir(), tenantId = null, {
|
|
|
660
660
|
};
|
|
661
661
|
}
|
|
662
662
|
|
|
663
|
-
function writeDesktopTasksProfileAssets(root, appUrl) {
|
|
663
|
+
function writeDesktopTasksProfileAssets(root, appUrl, { enableEmbed = false } = {}) {
|
|
664
664
|
const assets = desktopTasksAssetPaths(root);
|
|
665
|
-
writeAtomic(assets.mainPreload, desktopTasksMainPreload(appUrl
|
|
665
|
+
writeAtomic(assets.mainPreload, desktopTasksMainPreload(appUrl, {
|
|
666
|
+
enableEmbed,
|
|
667
|
+
enableNativeFallback: false,
|
|
668
|
+
}), 0o600);
|
|
666
669
|
writeAtomic(assets.viewPreload, desktopTasksViewPreload(), 0o600);
|
|
667
670
|
// This exact path is Impel-owned. Remove the former local MCP Apps host when
|
|
668
671
|
// a profile is regenerated; old released clients keep the immutable server
|
|
@@ -879,6 +882,7 @@ export function installManagedAppFiles({
|
|
|
879
882
|
writeDesktopTasksProfileAssets(
|
|
880
883
|
paths[target].root,
|
|
881
884
|
config.appUrl || RUNTIME_BRAND.controlPlane.defaultOrigin,
|
|
885
|
+
{ enableEmbed: target === "chatgpt" },
|
|
882
886
|
);
|
|
883
887
|
// Non-bundle targets are the background-refresh / fast-open path: configs,
|
|
884
888
|
// token helper, catalog, and manifest only. Bundle swaps require the app
|
package/src/desktopTasks.js
CHANGED
|
@@ -87,6 +87,7 @@ export function parseDesktopTasksCredential(output, tenantId, {
|
|
|
87
87
|
|
|
88
88
|
const NAV_ANCHOR_RULES_SOURCE = JSON.stringify(DESKTOP_TASKS_NAV_ANCHOR_RULES);
|
|
89
89
|
const TASKS_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"></rect><path d="M9 3v18"></path><path d="M15 3v18"></path></svg>';
|
|
90
|
+
const NAVIGATION_NONCE_PLACEHOLDER = "IMPEL_DESKTOP_TASKS_NAVIGATION_NONCE_PLACEHOLDER";
|
|
90
91
|
|
|
91
92
|
const NAVIGATION_SOURCE = String.raw`(() => {
|
|
92
93
|
const NAV_ID = "impel-desktop-tasks-nav";
|
|
@@ -104,6 +105,9 @@ const NAVIGATION_SOURCE = String.raw`(() => {
|
|
|
104
105
|
let active = false;
|
|
105
106
|
let lastAnchor = null;
|
|
106
107
|
let readySignalled = false;
|
|
108
|
+
let ensureTimer = null;
|
|
109
|
+
const signalSequenceKey = "__impelDesktopTasksSignalSequence";
|
|
110
|
+
const navigationNonce = ${JSON.stringify(NAVIGATION_NONCE_PLACEHOLDER)};
|
|
107
111
|
const roots = () => {
|
|
108
112
|
const result = [document];
|
|
109
113
|
for (let index = 0; index < result.length; index += 1) {
|
|
@@ -157,6 +161,10 @@ const NAVIGATION_SOURCE = String.raw`(() => {
|
|
|
157
161
|
};
|
|
158
162
|
const signal = (action) => {
|
|
159
163
|
const query = new URLSearchParams(Object.entries(mainBounds()).map(([key, value]) => [key, String(value)]));
|
|
164
|
+
const signalSequence = Number(window[signalSequenceKey] || 0) + 1;
|
|
165
|
+
window[signalSequenceKey] = signalSequence;
|
|
166
|
+
query.set("event", Date.now().toString(36) + "-" + signalSequence.toString(36));
|
|
167
|
+
query.set("nonce", navigationNonce);
|
|
160
168
|
location.href = "impel-tasks://" + action + "?" + query;
|
|
161
169
|
};
|
|
162
170
|
const select = (selected) => {
|
|
@@ -210,6 +218,10 @@ const NAVIGATION_SOURCE = String.raw`(() => {
|
|
|
210
218
|
return button;
|
|
211
219
|
};
|
|
212
220
|
const ensureNav = () => {
|
|
221
|
+
if (nav?.isConnected) {
|
|
222
|
+
select(active);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
213
225
|
let rule = null;
|
|
214
226
|
let anchor = null;
|
|
215
227
|
for (const candidate of RULES) {
|
|
@@ -253,11 +265,19 @@ const NAVIGATION_SOURCE = String.raw`(() => {
|
|
|
253
265
|
const onResize = () => { if (active) signal("layout"); };
|
|
254
266
|
window.addEventListener("click", onClick, true);
|
|
255
267
|
window.addEventListener("resize", onResize);
|
|
256
|
-
const
|
|
268
|
+
const scheduleEnsure = () => {
|
|
269
|
+
if (ensureTimer !== null) return;
|
|
270
|
+
ensureTimer = setTimeout(() => {
|
|
271
|
+
ensureTimer = null;
|
|
272
|
+
ensureNav();
|
|
273
|
+
}, 250);
|
|
274
|
+
};
|
|
275
|
+
const observer = new MutationObserver(scheduleEnsure);
|
|
257
276
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
258
277
|
ensureNav();
|
|
259
278
|
window.__impelDesktopTasksCleanup = () => {
|
|
260
279
|
observer.disconnect();
|
|
280
|
+
if (ensureTimer !== null) clearTimeout(ensureTimer);
|
|
261
281
|
window.removeEventListener("click", onClick, true);
|
|
262
282
|
window.removeEventListener("resize", onResize);
|
|
263
283
|
nav?.remove();
|
|
@@ -275,7 +295,10 @@ const NAVIGATION_SOURCE = String.raw`(() => {
|
|
|
275
295
|
|
|
276
296
|
const NATIVE_NAVIGATION_HTML = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none';script-src 'unsafe-inline';style-src 'unsafe-inline'"><style>:root{color-scheme:light dark;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}*{box-sizing:border-box}html,body{width:100%;height:100%;margin:0;overflow:hidden}button{width:100%;height:100%;display:flex;align-items:center;gap:9px;border:0;border-radius:8px;padding:0 12px;background:color-mix(in srgb,Canvas 92%,CanvasText 8%);color:CanvasText;cursor:pointer}svg{width:16px;height:16px}</style></head><body><button id="tasks" type="button" aria-label="Tasks">${TASKS_ICON}<span>Tasks</span></button><script>const button=document.getElementById("tasks");button.addEventListener("click",()=>{button.dataset.active="true";window.impelDesktopTasks.show()});window.__impelDesktopTasksSetActive=active=>{button.dataset.active=String(Boolean(active))}</script></body></html>`;
|
|
277
297
|
|
|
278
|
-
export function desktopTasksMainPreload(appUrl, {
|
|
298
|
+
export function desktopTasksMainPreload(appUrl, {
|
|
299
|
+
enableEmbed = false,
|
|
300
|
+
enableNativeFallback = false,
|
|
301
|
+
} = {}) {
|
|
279
302
|
const appOrigin = desktopTasksAppOrigin(appUrl);
|
|
280
303
|
if (!enableEmbed) return `"use strict";\n`;
|
|
281
304
|
const parserSource = parseDesktopTasksCredential.toString();
|
|
@@ -294,12 +317,16 @@ if (
|
|
|
294
317
|
const fs = require("node:fs");
|
|
295
318
|
const path = require("node:path");
|
|
296
319
|
const { spawn } = require("node:child_process");
|
|
320
|
+
const { randomBytes } = require("node:crypto");
|
|
297
321
|
const navSource = ${JSON.stringify(NAVIGATION_SOURCE)};
|
|
322
|
+
const navigationNoncePlaceholder = ${JSON.stringify(NAVIGATION_NONCE_PLACEHOLDER)};
|
|
298
323
|
const nativeNavUrl = "data:text/html;charset=utf-8," + encodeURIComponent(${JSON.stringify(NATIVE_NAVIGATION_HTML)});
|
|
299
324
|
const appOrigin = ${JSON.stringify(appOrigin)};
|
|
300
325
|
const bootstrapUrl = appOrigin + ${JSON.stringify(DESKTOP_TASKS_BOOTSTRAP_PATH)};
|
|
301
326
|
const tasksUrl = appOrigin + ${JSON.stringify(DESKTOP_TASKS_BOARD_PATH)};
|
|
302
327
|
const readyMarker = ${JSON.stringify(DESKTOP_TASKS_READY_MARKER)};
|
|
328
|
+
const nativeFallbackEnabled = ${JSON.stringify(enableNativeFallback)};
|
|
329
|
+
const boardSessionMaxAgeMs = 7 * 60 * 60 * 1000;
|
|
303
330
|
const tenantId = process.env.IMPEL_DESKTOP_TASKS_TENANT;
|
|
304
331
|
const nativeShowChannel = "impel-desktop-tasks:native-show";
|
|
305
332
|
const runtimeStatusPath = path.join(__dirname, "runtime-status.json");
|
|
@@ -383,7 +410,7 @@ if (
|
|
|
383
410
|
const stateFor = (window) => {
|
|
384
411
|
let state = windows.get(window.id);
|
|
385
412
|
if (!state) {
|
|
386
|
-
state = { view: null, bounds: null, visible: false, nativeNav: null, hasDomNavigation: false, loading: null, bootstrapping: false };
|
|
413
|
+
state = { view: null, bounds: null, visible: false, nativeNav: null, hasDomNavigation: false, loaded: false, loadedAt: 0, loading: null, bootstrapping: false };
|
|
387
414
|
windows.set(window.id, state);
|
|
388
415
|
}
|
|
389
416
|
return state;
|
|
@@ -425,7 +452,16 @@ if (
|
|
|
425
452
|
const errorDocument = "data:text/html;charset=utf-8," + encodeURIComponent(
|
|
426
453
|
"<!doctype html><html><body style='font:14px system-ui;padding:32px'>Impel Tasks could not be loaded. Hide Tasks, check your connection, and try again.</body></html>",
|
|
427
454
|
);
|
|
455
|
+
const loadErrorDocument = async (state) => {
|
|
456
|
+
const contents = state.view?.webContents;
|
|
457
|
+
if (!contents || contents.isDestroyed()) return;
|
|
458
|
+
await contents.loadURL(errorDocument).catch(() => {});
|
|
459
|
+
};
|
|
428
460
|
const loadBoard = async (state) => {
|
|
461
|
+
const contents = state.view?.webContents;
|
|
462
|
+
if (!contents || contents.isDestroyed()) return;
|
|
463
|
+
state.loaded = false;
|
|
464
|
+
state.loadedAt = 0;
|
|
429
465
|
state.bootstrapping = true;
|
|
430
466
|
let pat = null;
|
|
431
467
|
try {
|
|
@@ -439,27 +475,32 @@ if (
|
|
|
439
475
|
].join("\\n"),
|
|
440
476
|
postData: [{ type: "rawData", bytes: Buffer.alloc(0) }],
|
|
441
477
|
};
|
|
442
|
-
await
|
|
443
|
-
if (
|
|
444
|
-
const ready = await
|
|
478
|
+
await contents.loadURL(bootstrapUrl, request);
|
|
479
|
+
if (contents.isDestroyed() || contents.getURL() !== bootstrapUrl) throw new Error("bootstrap redirected");
|
|
480
|
+
const ready = await contents.executeJavaScript(
|
|
445
481
|
"document.body?.textContent?.trim() === " + JSON.stringify(readyMarker),
|
|
446
482
|
true,
|
|
447
483
|
);
|
|
448
484
|
if (!ready) throw new Error("bootstrap marker missing");
|
|
449
485
|
} catch {
|
|
450
486
|
record("board-bootstrap-failed");
|
|
451
|
-
await state
|
|
487
|
+
await loadErrorDocument(state);
|
|
452
488
|
return;
|
|
453
489
|
} finally {
|
|
454
490
|
pat = null;
|
|
455
491
|
state.bootstrapping = false;
|
|
456
492
|
}
|
|
457
493
|
try {
|
|
458
|
-
await
|
|
494
|
+
await contents.loadURL(tasksUrl);
|
|
495
|
+
if (contents.isDestroyed()) return;
|
|
496
|
+
state.loaded = true;
|
|
497
|
+
state.loadedAt = Date.now();
|
|
459
498
|
record("board-loaded");
|
|
460
499
|
} catch {
|
|
500
|
+
state.loaded = false;
|
|
501
|
+
state.loadedAt = 0;
|
|
461
502
|
record("board-load-failed");
|
|
462
|
-
await state
|
|
503
|
+
await loadErrorDocument(state);
|
|
463
504
|
}
|
|
464
505
|
};
|
|
465
506
|
const deactivateNavigation = () => {
|
|
@@ -468,9 +509,10 @@ if (
|
|
|
468
509
|
}
|
|
469
510
|
for (const contents of webContents.getAllWebContents()) {
|
|
470
511
|
if (internalContents.has(contents) || contents.isDestroyed()) continue;
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
512
|
+
contents.mainFrame?.executeJavaScript?.(
|
|
513
|
+
"window.__impelDesktopTasksSetActive?.(false)",
|
|
514
|
+
true,
|
|
515
|
+
)?.catch(() => {});
|
|
474
516
|
}
|
|
475
517
|
};
|
|
476
518
|
const isHostWindow = (window) => {
|
|
@@ -498,6 +540,7 @@ if (
|
|
|
498
540
|
state.view?.setVisible(false);
|
|
499
541
|
window.webContents.focus();
|
|
500
542
|
deactivateNavigation();
|
|
543
|
+
record("board-hidden");
|
|
501
544
|
};
|
|
502
545
|
const isPrimaryWindow = (window) => {
|
|
503
546
|
return isHostWindow(window) && window === primaryHostWindow() && window.isVisible?.() !== false;
|
|
@@ -520,6 +563,8 @@ if (
|
|
|
520
563
|
},
|
|
521
564
|
});
|
|
522
565
|
state.view = view;
|
|
566
|
+
state.loaded = false;
|
|
567
|
+
state.loadedAt = 0;
|
|
523
568
|
internalContents.add(view.webContents);
|
|
524
569
|
configureSession(view.webContents.session);
|
|
525
570
|
window.contentView.addChildView(view);
|
|
@@ -547,7 +592,12 @@ if (
|
|
|
547
592
|
view.webContents.on("destroyed", () => {
|
|
548
593
|
internalContents.delete(view.webContents);
|
|
549
594
|
try { window.contentView.removeChildView(view); } catch {}
|
|
550
|
-
if (state.view === view)
|
|
595
|
+
if (state.view === view) {
|
|
596
|
+
state.view = null;
|
|
597
|
+
state.loaded = false;
|
|
598
|
+
state.loadedAt = 0;
|
|
599
|
+
state.loading = null;
|
|
600
|
+
}
|
|
551
601
|
});
|
|
552
602
|
return state;
|
|
553
603
|
};
|
|
@@ -560,7 +610,20 @@ if (
|
|
|
560
610
|
state.view.webContents.focus();
|
|
561
611
|
if (state.visible) return;
|
|
562
612
|
state.visible = true;
|
|
563
|
-
|
|
613
|
+
let boardRoute = false;
|
|
614
|
+
try {
|
|
615
|
+
const current = new URL(state.view.webContents.getURL());
|
|
616
|
+
boardRoute = current.origin === appOrigin
|
|
617
|
+
&& (current.pathname === tasksUrl.slice(appOrigin.length)
|
|
618
|
+
|| current.pathname.startsWith(tasksUrl.slice(appOrigin.length) + "/"));
|
|
619
|
+
} catch {}
|
|
620
|
+
const reusableBoard = state.loaded
|
|
621
|
+
&& Date.now() - state.loadedAt < boardSessionMaxAgeMs
|
|
622
|
+
&& boardRoute;
|
|
623
|
+
if (!reusableBoard) state.loaded = false;
|
|
624
|
+
if (!state.loaded && !state.loading) {
|
|
625
|
+
state.loading = loadBoard(state).finally(() => { state.loading = null; });
|
|
626
|
+
}
|
|
564
627
|
record("board-shown");
|
|
565
628
|
};
|
|
566
629
|
const layoutBoard = (window, bounds = null) => {
|
|
@@ -661,43 +724,74 @@ if (
|
|
|
661
724
|
const attachContents = (contents) => {
|
|
662
725
|
if (!contents || contents.isDestroyed() || contents.__impelDesktopTasksAttached || internalContents.has(contents)) return;
|
|
663
726
|
contents.__impelDesktopTasksAttached = true;
|
|
664
|
-
const
|
|
727
|
+
const navigationNonce = randomBytes(32).toString("base64url");
|
|
728
|
+
const contentsNavSource = navSource.replace(
|
|
729
|
+
JSON.stringify(navigationNoncePlaceholder),
|
|
730
|
+
JSON.stringify(navigationNonce),
|
|
731
|
+
);
|
|
732
|
+
const inject = () => {
|
|
665
733
|
if (internalContents.has(contents)) return;
|
|
666
|
-
|
|
667
|
-
|
|
734
|
+
const frame = contents.mainFrame;
|
|
735
|
+
if (!frame || typeof frame.executeJavaScript !== "function") return;
|
|
736
|
+
frame.executeJavaScript(contentsNavSource, true)
|
|
737
|
+
.then((result) => {
|
|
668
738
|
const window = ownerWindow(contents);
|
|
669
739
|
if (!window || window !== primaryHostWindow()) return;
|
|
670
740
|
const state = stateFor(window);
|
|
671
|
-
|
|
672
|
-
if (results.some((result) => result?.anchor)) {
|
|
741
|
+
if (result?.anchor) {
|
|
673
742
|
markDomNavigation(window);
|
|
674
|
-
} else if (
|
|
675
|
-
|
|
676
|
-
|
|
743
|
+
} else if (result?.shellReady && !state.hasDomNavigation && !hasDomNavigation && nativeFallbackEnabled) {
|
|
744
|
+
ensureNativeNav(window);
|
|
745
|
+
}
|
|
677
746
|
})
|
|
678
747
|
.catch(() => record("navigation-injection-failed"));
|
|
679
748
|
};
|
|
680
749
|
contents.on("did-finish-load", () => inject());
|
|
681
|
-
contents.on("did-frame-finish-load", () => inject());
|
|
682
750
|
if (!contents.isLoadingMainFrame()) setTimeout(() => inject(), 0);
|
|
683
|
-
|
|
751
|
+
const handledNavigationEvents = new Set();
|
|
752
|
+
let handledLegacyNavigation = null;
|
|
684
753
|
const handleNavigation = (event, url) => {
|
|
685
754
|
if (!String(url).startsWith("impel-tasks://")) return;
|
|
686
755
|
event.preventDefault();
|
|
687
756
|
const window = ownerWindow(contents);
|
|
688
757
|
if (!window || window !== primaryHostWindow()) return;
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
758
|
+
let navigation;
|
|
759
|
+
try { navigation = new URL(url); } catch { return; }
|
|
760
|
+
if (navigation.searchParams.get("nonce") !== navigationNonce) return;
|
|
761
|
+
const action = navigation.hostname;
|
|
762
|
+
const eventId = navigation.searchParams.get("event");
|
|
763
|
+
if (eventId) {
|
|
764
|
+
const navigationKey = action + ":" + eventId;
|
|
765
|
+
if (handledNavigationEvents.has(navigationKey)) return;
|
|
766
|
+
handledNavigationEvents.add(navigationKey);
|
|
767
|
+
if (handledNavigationEvents.size > 128) {
|
|
768
|
+
handledNavigationEvents.delete(handledNavigationEvents.values().next().value);
|
|
769
|
+
}
|
|
770
|
+
} else {
|
|
771
|
+
if (handledLegacyNavigation === url) return;
|
|
772
|
+
handledLegacyNavigation = url;
|
|
773
|
+
queueMicrotask(() => { if (handledLegacyNavigation === url) handledLegacyNavigation = null; });
|
|
774
|
+
}
|
|
694
775
|
if (action === "ready") markDomNavigation(window);
|
|
695
776
|
else if (action === "hide") hide(window);
|
|
696
777
|
else if (action === "show") show(window, parseBounds(url, window));
|
|
697
778
|
else if (action === "layout") layoutBoard(window, parseBounds(url, window));
|
|
698
779
|
};
|
|
699
|
-
|
|
700
|
-
|
|
780
|
+
const eventUrl = (event, deprecatedUrl) => (
|
|
781
|
+
typeof event?.url === "string" ? event.url : String(deprecatedUrl || "")
|
|
782
|
+
);
|
|
783
|
+
contents.on("will-navigate", (event, deprecatedUrl) => {
|
|
784
|
+
handleNavigation(event, eventUrl(event, deprecatedUrl));
|
|
785
|
+
});
|
|
786
|
+
contents.on("will-frame-navigate", (event, deprecatedUrl, _isInPlace, deprecatedIsMainFrame) => {
|
|
787
|
+
const url = eventUrl(event, deprecatedUrl);
|
|
788
|
+
if (!url.startsWith("impel-tasks://")) return;
|
|
789
|
+
event.preventDefault();
|
|
790
|
+
const isMainFrame = typeof event?.isMainFrame === "boolean"
|
|
791
|
+
? event.isMainFrame
|
|
792
|
+
: deprecatedIsMainFrame === true;
|
|
793
|
+
if (isMainFrame) handleNavigation(event, url);
|
|
794
|
+
});
|
|
701
795
|
};
|
|
702
796
|
const attachWindow = (window) => {
|
|
703
797
|
if (!window || window.isDestroyed() || window.getParentWindow?.() || window.__impelDesktopTasksWindowAttached) return;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Fleet generation shared by managed-profile writers and upstream clients.
|
|
2
2
|
// Keep this isolated from apps.js so latency-sensitive transports do not load
|
|
3
3
|
// desktop bundle machinery just to identify their managed config contract.
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
4
|
+
// v41 restores ChatGPT's embedded Tasks entry through its real sidebar only.
|
|
5
|
+
// The native fallback stays disabled, and a valid authenticated board is
|
|
6
|
+
// reused across hide/show cycles so it cannot obscure or repeatedly bootstrap
|
|
7
|
+
// the host.
|
|
8
|
+
export const CURRENT_CONFIG_VERSION = 41;
|