@mindexec/cli 0.2.478 → 0.2.479
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/README.md +323 -323
- package/codex-model-catalog.js +202 -202
- package/codex-runtime.js +1608 -1608
- package/desktop-workspace-state.cjs +120 -120
- package/electron/main.cjs +517 -517
- package/electron/preload.cjs +17 -17
- package/electron/recurring-update-owner-smoke.mjs +55 -55
- package/electron/recurring-update-owner.cjs +97 -97
- package/electron/source-smoke.mjs +108 -108
- package/electron/update-feed-resolver.cjs +79 -79
- package/electron/update-feed.json +10 -10
- package/electron/update-manager-smoke.mjs +146 -146
- package/electron/update-manager.cjs +297 -297
- package/electron/windows-package-smoke.mjs +156 -156
- package/launch-bridge.cjs +236 -236
- package/lzo1x.js +110 -110
- package/mode4-atlas-worker.js +440 -440
- package/mode4-atlas.js +130 -130
- package/package.json +1 -1
- package/port-guard.cjs +285 -285
- package/remote-fast/osx-arm64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-arm64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-arm64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-arm64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/osx-x64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-x64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-x64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-x64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
- package/remote-hub.js +3698 -3698
- package/scripts/auth-session-smoke.mjs +262 -262
- package/scripts/board-render-ownership-smoke.mjs +85 -85
- package/scripts/codex-model-catalog-smoke.mjs +150 -150
- package/scripts/codex-runtime-home-smoke.mjs +58 -58
- package/scripts/codex-sdk-image-live-smoke.mjs +43 -43
- package/scripts/codex-sdk-image-runtime-smoke.mjs +122 -122
- package/scripts/codex-sdk-runtime-smoke.mjs +89 -89
- package/scripts/desktop-update-publisher-smoke.mjs +89 -89
- package/scripts/desktop-update-worker-smoke.mjs +72 -72
- package/scripts/desktop-workspace-state-smoke.mjs +81 -81
- package/scripts/drive-installed-update-smoke.mjs +67 -67
- package/scripts/npm-fresh-install-smoke.mjs +216 -216
- package/scripts/publish-mindexec-desktop-updates.mjs +224 -224
- package/scripts/remote-agent-managed-smoke.mjs +318 -318
- package/scripts/remote-agent-package-smoke.mjs +64 -64
- package/scripts/remote-agent-ws-smoke.mjs +269 -269
- package/scripts/remote-fast-live-rate-smoke.mjs +343 -343
- package/scripts/remote-fast-mdm-browser-smoke.mjs +453 -453
- package/scripts/remote-fleet-render-smoke.mjs +1350 -1350
- package/scripts/remote-frame-ws-smoke.mjs +234 -234
- package/scripts/remote-http-smoke.mjs +592 -592
- package/scripts/remote-hub-identity-smoke.mjs +146 -146
- package/scripts/remote-hub-scale-smoke.mjs +124 -124
- package/scripts/remote-hub-smoke.mjs +699 -699
- package/scripts/remote-input-ws-smoke.mjs +263 -263
- package/scripts/remote-registry-follower-smoke.mjs +815 -815
- package/scripts/setup-tree-sitter-grammars.mjs +59 -59
- package/server.js +7510 -7510
- package/start-bridge.bat +32 -32
- package/start-bridge.sh +81 -81
- package/tree-sitter-grammars/README.md +18 -0
- package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
- package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -546
- package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
- package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -251
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +3789 -3789
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +27 -27
- package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1449 -1449
- package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -8316
- package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +338 -338
- package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -69
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-animated-image-preview.js +270 -270
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-automation-edge-motion.js +170 -170
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-board-render-ownership.js +81 -81
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +10506 -10506
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +1059 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -902
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +24637 -24637
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -325
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1491 -1491
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +434 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +70 -70
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-image-policy.js +218 -218
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7078 -7078
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -160
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9958 -9958
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-video-placeholder.js +107 -107
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +1004 -1004
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1351 -1351
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +865 -865
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +554 -554
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4744 -4744
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-normal-boundary-optimizer.js +624 -624
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +241 -241
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +372 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2290 -2290
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +202 -202
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-render-plan.js +1971 -1971
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +93 -93
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2391 -2391
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +1258 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-video-playback-controls.js +95 -95
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +895 -895
- package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +636 -636
- package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +45 -45
- package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +117 -117
- package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +114 -114
- package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +10 -10
- package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +7 -7
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +2 -2
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +8 -8
- package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -30522
- package/wwwroot/_framework/{Google.Protobuf.33g2gb5is0.dll → Google.Protobuf.9h59ukbel7.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Core.z100xo4mg0.dll → MindExecution.Core.xxsj2s456b.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.i9cldrx8yc.dll → MindExecution.Kernel.5jofnc0wrd.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.48lzqddb0a.dll → MindExecution.Plugins.Admin.t7m4jnqjg3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.7m8u1q142p.dll → MindExecution.Plugins.Business.6yjlhhavap.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.cl5wmr53od.dll → MindExecution.Plugins.Concept.iikqq5zxu0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.slipi39041.dll → MindExecution.Plugins.Directory.c5ribyne4m.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.qfge05l671.dll → MindExecution.Plugins.PlanMaster.365bbuifr9.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.tn7v29ut77.dll → MindExecution.Plugins.YouTube.ax415cilpk.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Shared.21e2nvf86u.dll +0 -0
- package/wwwroot/_framework/{MindExecution.Web.rwawrxxk38.dll → MindExecution.Web.ppgm8rey59.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.23yxgetbju.dll → System.Collections.zyvbaq6mpd.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +25 -25
- package/wwwroot/_headers +40 -40
- package/wwwroot/_redirects +1 -1
- package/wwwroot/index.html +772 -772
- package/wwwroot/js/marketing-tool.js +180 -180
- package/wwwroot/manifest.webmanifest +23 -23
- package/wwwroot/service-worker-assets.js +109 -81
- package/wwwroot/service-worker.js +33 -33
- package/wwwroot/_framework/MindExecution.Shared.rjqlf0umjn.dll +0 -0
package/electron/preload.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { contextBridge, ipcRenderer } = require('electron');
|
|
4
|
-
|
|
5
|
-
contextBridge.exposeInMainWorld('mindExecDesktop', {
|
|
6
|
-
updates: {
|
|
7
|
-
getStatus: () => ipcRenderer.invoke('updates:get-status'),
|
|
8
|
-
check: () => ipcRenderer.invoke('updates:check'),
|
|
9
|
-
install: () => ipcRenderer.invoke('updates:install'),
|
|
10
|
-
onStatus: callback => {
|
|
11
|
-
if (typeof callback !== 'function') return () => undefined;
|
|
12
|
-
const listener = (_event, payload) => callback(payload);
|
|
13
|
-
ipcRenderer.on('updates:status', listener);
|
|
14
|
-
return () => ipcRenderer.removeListener('updates:status', listener);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
});
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { contextBridge, ipcRenderer } = require('electron');
|
|
4
|
+
|
|
5
|
+
contextBridge.exposeInMainWorld('mindExecDesktop', {
|
|
6
|
+
updates: {
|
|
7
|
+
getStatus: () => ipcRenderer.invoke('updates:get-status'),
|
|
8
|
+
check: () => ipcRenderer.invoke('updates:check'),
|
|
9
|
+
install: () => ipcRenderer.invoke('updates:install'),
|
|
10
|
+
onStatus: callback => {
|
|
11
|
+
if (typeof callback !== 'function') return () => undefined;
|
|
12
|
+
const listener = (_event, payload) => callback(payload);
|
|
13
|
+
ipcRenderer.on('updates:status', listener);
|
|
14
|
+
return () => ipcRenderer.removeListener('updates:status', listener);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import test from 'node:test';
|
|
3
|
-
import recurringOwnerModule from './recurring-update-owner.cjs';
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
DEFAULT_UPDATE_INTERVAL_MS,
|
|
7
|
-
createRecurringProductUpdateOwner
|
|
8
|
-
} = recurringOwnerModule;
|
|
9
|
-
|
|
10
|
-
test('default recurring update cadence is one minute after the startup check', () => {
|
|
11
|
-
const owner = createRecurringProductUpdateOwner({
|
|
12
|
-
manager: { check: async () => ({ state: 'current' }) },
|
|
13
|
-
setTimer: () => ({ unref() {} }),
|
|
14
|
-
clearTimer: () => undefined
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
owner.start();
|
|
18
|
-
assert.equal(DEFAULT_UPDATE_INTERVAL_MS, 60_000);
|
|
19
|
-
assert.equal(owner.inspect().initialDelayMs, 60_000);
|
|
20
|
-
assert.equal(owner.inspect().intervalMs, 60_000);
|
|
21
|
-
owner.stop();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('one recurring owner checks once and never installs without user approval', async () => {
|
|
25
|
-
let checkCalls = 0;
|
|
26
|
-
let installCalls = 0;
|
|
27
|
-
let timerCallback = null;
|
|
28
|
-
const manager = {
|
|
29
|
-
getStatus: () => ({ state: 'downloaded', downloaded: true }),
|
|
30
|
-
check: async () => {
|
|
31
|
-
checkCalls += 1;
|
|
32
|
-
await new Promise(resolve => setTimeout(resolve, 5));
|
|
33
|
-
return { state: 'downloaded', downloaded: true };
|
|
34
|
-
},
|
|
35
|
-
install: async () => { installCalls += 1; }
|
|
36
|
-
};
|
|
37
|
-
const owner = createRecurringProductUpdateOwner({
|
|
38
|
-
manager,
|
|
39
|
-
initialDelayMs: 1_000,
|
|
40
|
-
intervalMs: 1_000,
|
|
41
|
-
setTimer: callback => { timerCallback = callback; return { unref() {} }; },
|
|
42
|
-
clearTimer: () => { timerCallback = null; }
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
owner.start();
|
|
46
|
-
assert.equal(owner.inspect().timerCount, 1);
|
|
47
|
-
const [first, second] = await Promise.all([owner.runNow(), owner.runNow()]);
|
|
48
|
-
assert.equal(first.downloaded, true);
|
|
49
|
-
assert.equal(second.downloaded, true);
|
|
50
|
-
assert.equal(checkCalls, 1);
|
|
51
|
-
assert.equal(installCalls, 0);
|
|
52
|
-
assert.ok(timerCallback, 'the single next check must be re-armed');
|
|
53
|
-
owner.stop();
|
|
54
|
-
assert.equal(owner.inspect().timerCount, 0);
|
|
55
|
-
});
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import recurringOwnerModule from './recurring-update-owner.cjs';
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
DEFAULT_UPDATE_INTERVAL_MS,
|
|
7
|
+
createRecurringProductUpdateOwner
|
|
8
|
+
} = recurringOwnerModule;
|
|
9
|
+
|
|
10
|
+
test('default recurring update cadence is one minute after the startup check', () => {
|
|
11
|
+
const owner = createRecurringProductUpdateOwner({
|
|
12
|
+
manager: { check: async () => ({ state: 'current' }) },
|
|
13
|
+
setTimer: () => ({ unref() {} }),
|
|
14
|
+
clearTimer: () => undefined
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
owner.start();
|
|
18
|
+
assert.equal(DEFAULT_UPDATE_INTERVAL_MS, 60_000);
|
|
19
|
+
assert.equal(owner.inspect().initialDelayMs, 60_000);
|
|
20
|
+
assert.equal(owner.inspect().intervalMs, 60_000);
|
|
21
|
+
owner.stop();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('one recurring owner checks once and never installs without user approval', async () => {
|
|
25
|
+
let checkCalls = 0;
|
|
26
|
+
let installCalls = 0;
|
|
27
|
+
let timerCallback = null;
|
|
28
|
+
const manager = {
|
|
29
|
+
getStatus: () => ({ state: 'downloaded', downloaded: true }),
|
|
30
|
+
check: async () => {
|
|
31
|
+
checkCalls += 1;
|
|
32
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
33
|
+
return { state: 'downloaded', downloaded: true };
|
|
34
|
+
},
|
|
35
|
+
install: async () => { installCalls += 1; }
|
|
36
|
+
};
|
|
37
|
+
const owner = createRecurringProductUpdateOwner({
|
|
38
|
+
manager,
|
|
39
|
+
initialDelayMs: 1_000,
|
|
40
|
+
intervalMs: 1_000,
|
|
41
|
+
setTimer: callback => { timerCallback = callback; return { unref() {} }; },
|
|
42
|
+
clearTimer: () => { timerCallback = null; }
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
owner.start();
|
|
46
|
+
assert.equal(owner.inspect().timerCount, 1);
|
|
47
|
+
const [first, second] = await Promise.all([owner.runNow(), owner.runNow()]);
|
|
48
|
+
assert.equal(first.downloaded, true);
|
|
49
|
+
assert.equal(second.downloaded, true);
|
|
50
|
+
assert.equal(checkCalls, 1);
|
|
51
|
+
assert.equal(installCalls, 0);
|
|
52
|
+
assert.ok(timerCallback, 'the single next check must be re-armed');
|
|
53
|
+
owner.stop();
|
|
54
|
+
assert.equal(owner.inspect().timerCount, 0);
|
|
55
|
+
});
|
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const MINIMUM_UPDATE_DELAY_MS = 1_000;
|
|
4
|
-
const DEFAULT_UPDATE_INTERVAL_MS = 60_000;
|
|
5
|
-
|
|
6
|
-
function normalizeDelay(value, fallback) {
|
|
7
|
-
const delay = Number(value);
|
|
8
|
-
return Number.isFinite(delay) && delay >= MINIMUM_UPDATE_DELAY_MS
|
|
9
|
-
? Math.round(delay)
|
|
10
|
-
: fallback;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function createRecurringProductUpdateOwner({
|
|
14
|
-
manager,
|
|
15
|
-
canRun = () => true,
|
|
16
|
-
initialDelayMs = DEFAULT_UPDATE_INTERVAL_MS,
|
|
17
|
-
intervalMs = DEFAULT_UPDATE_INTERVAL_MS,
|
|
18
|
-
setTimer = setTimeout,
|
|
19
|
-
clearTimer = clearTimeout,
|
|
20
|
-
onError = () => undefined
|
|
21
|
-
} = {}) {
|
|
22
|
-
if (!manager || typeof manager.check !== 'function') {
|
|
23
|
-
throw new TypeError('recurring-product-update-manager-required');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const initialDelay = normalizeDelay(initialDelayMs, DEFAULT_UPDATE_INTERVAL_MS);
|
|
27
|
-
const interval = normalizeDelay(intervalMs, DEFAULT_UPDATE_INTERVAL_MS);
|
|
28
|
-
let stopped = true;
|
|
29
|
-
let generation = 0;
|
|
30
|
-
let timer = null;
|
|
31
|
-
let running = null;
|
|
32
|
-
|
|
33
|
-
const clearOwnedTimer = () => {
|
|
34
|
-
if (!timer) return;
|
|
35
|
-
clearTimer(timer);
|
|
36
|
-
timer = null;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const arm = (delay, ownerGeneration) => {
|
|
40
|
-
if (stopped || ownerGeneration !== generation || !canRun()) return;
|
|
41
|
-
clearOwnedTimer();
|
|
42
|
-
timer = setTimer(() => {
|
|
43
|
-
if (stopped || ownerGeneration !== generation) return;
|
|
44
|
-
timer = null;
|
|
45
|
-
void runCycle(ownerGeneration);
|
|
46
|
-
}, delay);
|
|
47
|
-
timer?.unref?.();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const runCycle = ownerGeneration => {
|
|
51
|
-
if (stopped || ownerGeneration !== generation || !canRun()) {
|
|
52
|
-
return Promise.resolve(manager.getStatus?.() || { state: 'stopped' });
|
|
53
|
-
}
|
|
54
|
-
if (running) return running;
|
|
55
|
-
|
|
56
|
-
const operation = Promise.resolve(manager.check());
|
|
57
|
-
running = operation;
|
|
58
|
-
void operation.catch(error => onError(error)).finally(() => {
|
|
59
|
-
if (running === operation) running = null;
|
|
60
|
-
arm(interval, ownerGeneration);
|
|
61
|
-
});
|
|
62
|
-
return operation;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
start() {
|
|
67
|
-
if (!stopped) return;
|
|
68
|
-
stopped = false;
|
|
69
|
-
generation += 1;
|
|
70
|
-
arm(initialDelay, generation);
|
|
71
|
-
},
|
|
72
|
-
stop() {
|
|
73
|
-
stopped = true;
|
|
74
|
-
generation += 1;
|
|
75
|
-
clearOwnedTimer();
|
|
76
|
-
},
|
|
77
|
-
runNow() {
|
|
78
|
-
clearOwnedTimer();
|
|
79
|
-
return runCycle(generation);
|
|
80
|
-
},
|
|
81
|
-
inspect() {
|
|
82
|
-
return {
|
|
83
|
-
stopped,
|
|
84
|
-
generation,
|
|
85
|
-
timerCount: timer ? 1 : 0,
|
|
86
|
-
running: Boolean(running),
|
|
87
|
-
initialDelayMs: initialDelay,
|
|
88
|
-
intervalMs: interval
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module.exports = {
|
|
95
|
-
DEFAULT_UPDATE_INTERVAL_MS,
|
|
96
|
-
createRecurringProductUpdateOwner
|
|
97
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MINIMUM_UPDATE_DELAY_MS = 1_000;
|
|
4
|
+
const DEFAULT_UPDATE_INTERVAL_MS = 60_000;
|
|
5
|
+
|
|
6
|
+
function normalizeDelay(value, fallback) {
|
|
7
|
+
const delay = Number(value);
|
|
8
|
+
return Number.isFinite(delay) && delay >= MINIMUM_UPDATE_DELAY_MS
|
|
9
|
+
? Math.round(delay)
|
|
10
|
+
: fallback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function createRecurringProductUpdateOwner({
|
|
14
|
+
manager,
|
|
15
|
+
canRun = () => true,
|
|
16
|
+
initialDelayMs = DEFAULT_UPDATE_INTERVAL_MS,
|
|
17
|
+
intervalMs = DEFAULT_UPDATE_INTERVAL_MS,
|
|
18
|
+
setTimer = setTimeout,
|
|
19
|
+
clearTimer = clearTimeout,
|
|
20
|
+
onError = () => undefined
|
|
21
|
+
} = {}) {
|
|
22
|
+
if (!manager || typeof manager.check !== 'function') {
|
|
23
|
+
throw new TypeError('recurring-product-update-manager-required');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const initialDelay = normalizeDelay(initialDelayMs, DEFAULT_UPDATE_INTERVAL_MS);
|
|
27
|
+
const interval = normalizeDelay(intervalMs, DEFAULT_UPDATE_INTERVAL_MS);
|
|
28
|
+
let stopped = true;
|
|
29
|
+
let generation = 0;
|
|
30
|
+
let timer = null;
|
|
31
|
+
let running = null;
|
|
32
|
+
|
|
33
|
+
const clearOwnedTimer = () => {
|
|
34
|
+
if (!timer) return;
|
|
35
|
+
clearTimer(timer);
|
|
36
|
+
timer = null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const arm = (delay, ownerGeneration) => {
|
|
40
|
+
if (stopped || ownerGeneration !== generation || !canRun()) return;
|
|
41
|
+
clearOwnedTimer();
|
|
42
|
+
timer = setTimer(() => {
|
|
43
|
+
if (stopped || ownerGeneration !== generation) return;
|
|
44
|
+
timer = null;
|
|
45
|
+
void runCycle(ownerGeneration);
|
|
46
|
+
}, delay);
|
|
47
|
+
timer?.unref?.();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const runCycle = ownerGeneration => {
|
|
51
|
+
if (stopped || ownerGeneration !== generation || !canRun()) {
|
|
52
|
+
return Promise.resolve(manager.getStatus?.() || { state: 'stopped' });
|
|
53
|
+
}
|
|
54
|
+
if (running) return running;
|
|
55
|
+
|
|
56
|
+
const operation = Promise.resolve(manager.check());
|
|
57
|
+
running = operation;
|
|
58
|
+
void operation.catch(error => onError(error)).finally(() => {
|
|
59
|
+
if (running === operation) running = null;
|
|
60
|
+
arm(interval, ownerGeneration);
|
|
61
|
+
});
|
|
62
|
+
return operation;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
start() {
|
|
67
|
+
if (!stopped) return;
|
|
68
|
+
stopped = false;
|
|
69
|
+
generation += 1;
|
|
70
|
+
arm(initialDelay, generation);
|
|
71
|
+
},
|
|
72
|
+
stop() {
|
|
73
|
+
stopped = true;
|
|
74
|
+
generation += 1;
|
|
75
|
+
clearOwnedTimer();
|
|
76
|
+
},
|
|
77
|
+
runNow() {
|
|
78
|
+
clearOwnedTimer();
|
|
79
|
+
return runCycle(generation);
|
|
80
|
+
},
|
|
81
|
+
inspect() {
|
|
82
|
+
return {
|
|
83
|
+
stopped,
|
|
84
|
+
generation,
|
|
85
|
+
timerCount: timer ? 1 : 0,
|
|
86
|
+
running: Boolean(running),
|
|
87
|
+
initialDelayMs: initialDelay,
|
|
88
|
+
intervalMs: interval
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
DEFAULT_UPDATE_INTERVAL_MS,
|
|
96
|
+
createRecurringProductUpdateOwner
|
|
97
|
+
};
|
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import sharp from 'sharp';
|
|
6
|
-
|
|
7
|
-
const electronDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const packageRoot = path.resolve(electronDirectory, '..');
|
|
9
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
10
|
-
const mainSource = fs.readFileSync(path.join(electronDirectory, 'main.cjs'), 'utf8');
|
|
11
|
-
const preloadSource = fs.readFileSync(path.join(electronDirectory, 'preload.cjs'), 'utf8');
|
|
12
|
-
const updateManagerSource = fs.readFileSync(path.join(electronDirectory, 'update-manager.cjs'), 'utf8');
|
|
13
|
-
const updateFeed = JSON.parse(fs.readFileSync(path.join(electronDirectory, 'update-feed.json'), 'utf8'));
|
|
14
|
-
const serverSource = fs.readFileSync(path.join(packageRoot, 'server.js'), 'utf8');
|
|
15
|
-
|
|
16
|
-
assert.equal(packageJson.build?.appId, 'com.mindexec.desktop', 'Stable desktop appId is required.');
|
|
17
|
-
assert.equal(packageJson.build?.productName, 'MindExec', 'Desktop product name changed unexpectedly.');
|
|
18
|
-
assert.match(mainSource, /title:\s*'MindExec'/, 'The Electron window title must use the MindExec app brand.');
|
|
19
|
-
assert.equal(packageJson.build?.extraMetadata?.main, 'electron/main.cjs', 'Packaged Electron entry point is missing.');
|
|
20
|
-
assert.equal(packageJson.build?.asar, false, 'Bundled runtime executables require an unpacked app directory.');
|
|
21
|
-
assert.ok(packageJson.files?.includes('electron/'), 'The npm package must include the Electron source contract.');
|
|
22
|
-
assert.ok(packageJson.files?.includes('desktop-workspace-state.cjs'), 'The npm package must include desktop workspace state.');
|
|
23
|
-
assert.match(packageJson.scripts?.['desktop:build:win'] || '', /electron-builder/, 'Windows build script is missing.');
|
|
24
|
-
assert.match(packageJson.scripts?.['test:desktop:win'] || '', /windows-package-smoke/, 'Packaged smoke test is missing.');
|
|
25
|
-
assert.match(packageJson.scripts?.['desktop:publish:update:dev'] || '', /--channel dev[\s\S]*--allow-unsigned[\s\S]*--publish/,
|
|
26
|
-
'The explicit development update publishing command is missing.');
|
|
27
|
-
assert.equal(packageJson.dependencies?.['electron-updater'], '^6.8.9', 'The packaged updater dependency is missing.');
|
|
28
|
-
assert.equal(packageJson.build?.extraMetadata?.desktopUpdateChannel, 'dev', 'Development installers must stay on the dev feed.');
|
|
29
|
-
assert.equal(packageJson.build?.publish?.provider, 'generic', 'Desktop updates must use the generic R2-backed provider.');
|
|
30
|
-
assert.equal(
|
|
31
|
-
packageJson.build?.publish?.url,
|
|
32
|
-
'https://mindexec.lovecrdm.workers.dev/dev/windows/x64',
|
|
33
|
-
'The packaged development update URL changed unexpectedly.'
|
|
34
|
-
);
|
|
35
|
-
assert.equal(updateFeed.baseUrl, 'https://mindexec.lovecrdm.workers.dev', 'MindExec update hostname has a typo.');
|
|
36
|
-
assert.equal(updateFeed.channel, 'dev', 'The packaged update feed must default to the isolated dev channel.');
|
|
37
|
-
|
|
38
|
-
assert.equal(
|
|
39
|
-
packageJson.build?.win?.icon,
|
|
40
|
-
'../MindExecution.Web/wwwroot/app-icon-1024.png',
|
|
41
|
-
'Electron must build from the canonical 1024px MindExec icon.'
|
|
42
|
-
);
|
|
43
|
-
const builderIconPath = path.resolve(packageRoot, packageJson.build.win.icon);
|
|
44
|
-
assert.ok(fs.existsSync(builderIconPath), `Electron builder icon is missing: ${builderIconPath}`);
|
|
45
|
-
const builderIconMetadata = await sharp(builderIconPath).metadata();
|
|
46
|
-
assert.equal(builderIconMetadata.width, 1024, 'Electron builder icon must be 1024px wide.');
|
|
47
|
-
assert.equal(builderIconMetadata.height, 1024, 'Electron builder icon must be 1024px tall.');
|
|
48
|
-
assert.equal(builderIconMetadata.hasAlpha, true, 'Electron builder icon must preserve transparency.');
|
|
49
|
-
const { data: builderIconPixels, info: builderIconInfo } = await sharp(builderIconPath)
|
|
50
|
-
.ensureAlpha()
|
|
51
|
-
.raw()
|
|
52
|
-
.toBuffer({ resolveWithObject: true });
|
|
53
|
-
const builderIconCornerAlpha = [
|
|
54
|
-
builderIconPixels[3],
|
|
55
|
-
builderIconPixels[(builderIconInfo.width - 1) * 4 + 3],
|
|
56
|
-
builderIconPixels[(builderIconInfo.height - 1) * builderIconInfo.width * 4 + 3],
|
|
57
|
-
builderIconPixels[(builderIconInfo.width * builderIconInfo.height - 1) * 4 + 3]
|
|
58
|
-
];
|
|
59
|
-
assert.ok(
|
|
60
|
-
builderIconCornerAlpha.every((alpha) => alpha === 0),
|
|
61
|
-
'Electron builder icon must have fully transparent corners.'
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const targets = packageJson.build?.win?.target || [];
|
|
65
|
-
assert.ok(targets.some((target) => target.target === 'nsis'), 'NSIS target is required.');
|
|
66
|
-
assert.ok(targets.some((target) => target.target === 'portable'), 'Portable target is required.');
|
|
67
|
-
|
|
68
|
-
assert.match(mainSource, /contextIsolation:\s*true/, 'Electron renderer isolation must remain enabled.');
|
|
69
|
-
assert.match(mainSource, /nodeIntegration:\s*false/, 'Node integration must remain disabled.');
|
|
70
|
-
assert.match(mainSource, /sandbox:\s*true/, 'Electron renderer sandbox must remain enabled.');
|
|
71
|
-
assert.match(mainSource, /preload:\s*path\.join\(__dirname, 'preload\.cjs'\)/, 'The isolated updater preload is missing.');
|
|
72
|
-
assert.match(mainSource, /runtimeMode:\s*'bundled'/, 'Packaged runtime must report bundled mode.');
|
|
73
|
-
assert.match(mainSource, /spawnBundledBridge/, 'Electron must start its bundled LocalBridge.');
|
|
74
|
-
assert.match(mainSource, /wwwroot.*index\.html/s, 'Electron must verify its bundled frontend.');
|
|
75
|
-
assert.match(mainSource, /resolveDedicatedPort/, 'Electron must allocate dedicated local ports.');
|
|
76
|
-
assert.doesNotMatch(mainSource, /adopted|probeBridge/, 'Electron must not adopt an external LocalBridge.');
|
|
77
|
-
assert.match(mainSource, /MINDEXEC_DESKTOP_SMOKE_RESULT/, 'Packaged runtime smoke contract is missing.');
|
|
78
|
-
assert.match(mainSource, /icon:\s*resolveDesktopIconPath\(\)/, 'Electron windows must use the packaged MindExec icon.');
|
|
79
|
-
assert.match(mainSource, /app\.setAppUserModelId\(DESKTOP_APP_ID\)/, 'Windows taskbar identity must use the stable app id.');
|
|
80
|
-
assert.match(mainSource, /resolveDesktopWorkspace/, 'Electron startup must restore the last desktop workspace.');
|
|
81
|
-
assert.match(mainSource, /MINDEXEC_DESKTOP_WORKSPACE_STATE_PATH/, 'Electron must share its workspace state path with LocalBridge.');
|
|
82
|
-
assert.match(mainSource, /resolveDesktopInstallKind/, 'Installed, portable, and unpacked update behavior must remain separate.');
|
|
83
|
-
assert.match(mainSource, /createProductUpdateManager/, 'The Electron update manager is not wired into the app.');
|
|
84
|
-
assert.match(mainSource, /updates:get-status/, 'The updater status IPC contract is missing.');
|
|
85
|
-
assert.match(mainSource, /updates:check/, 'The manual updater check IPC contract is missing.');
|
|
86
|
-
assert.match(mainSource, /updates:install/, 'The updater install IPC contract is missing.');
|
|
87
|
-
assert.match(mainSource, /await stopBundledBridge\(\)[\s\S]{0,180}await waitForBundledRuntimePortsReleased\(\)/,
|
|
88
|
-
'The Bridge and RemoteHub ports must reach zero before the installer starts.');
|
|
89
|
-
assert.match(mainSource, /Restart and update/, 'A downloaded update must ask before restarting active work.');
|
|
90
|
-
assert.match(preloadSource, /mindExecDesktop/, 'The updater preload namespace changed unexpectedly.');
|
|
91
|
-
assert.match(preloadSource, /updates:\s*\{/, 'The updater preload API is missing.');
|
|
92
|
-
assert.match(updateManagerSource, /autoDownload\s*=\s*false/, 'Updates must not download through an unowned implicit path.');
|
|
93
|
-
assert.match(updateManagerSource, /autoInstallOnAppQuit\s*=\s*false/, 'Updates must not restart without the explicit prompt path.');
|
|
94
|
-
assert.match(updateManagerSource, /quitAndInstall\(true, true\)/, 'Downloaded updates must relaunch silently after approval.');
|
|
95
|
-
assert.match(updateManagerSource, /desktop-updates-require-installed-windows-app/,
|
|
96
|
-
'Portable and unpacked builds must fail closed instead of replacing themselves.');
|
|
97
|
-
assert.match(serverSource, /saveDesktopWorkspaceState\(DESKTOP_WORKSPACE_STATE_PATH, resolvedPath\)/,
|
|
98
|
-
'A successful desktop workspace switch must be saved for the next launch.');
|
|
99
|
-
|
|
100
|
-
console.log(JSON.stringify({
|
|
101
|
-
ok: true,
|
|
102
|
-
appId: packageJson.build.appId,
|
|
103
|
-
version: packageJson.version,
|
|
104
|
-
runtimeMode: 'bundled',
|
|
105
|
-
icon: packageJson.build.win.icon,
|
|
106
|
-
updateFeed: `${updateFeed.baseUrl}/${updateFeed.channel}/windows/x64`,
|
|
107
|
-
targets: targets.map((target) => `${target.target}:${target.arch.join(',')}`)
|
|
108
|
-
}));
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import sharp from 'sharp';
|
|
6
|
+
|
|
7
|
+
const electronDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRoot = path.resolve(electronDirectory, '..');
|
|
9
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
10
|
+
const mainSource = fs.readFileSync(path.join(electronDirectory, 'main.cjs'), 'utf8');
|
|
11
|
+
const preloadSource = fs.readFileSync(path.join(electronDirectory, 'preload.cjs'), 'utf8');
|
|
12
|
+
const updateManagerSource = fs.readFileSync(path.join(electronDirectory, 'update-manager.cjs'), 'utf8');
|
|
13
|
+
const updateFeed = JSON.parse(fs.readFileSync(path.join(electronDirectory, 'update-feed.json'), 'utf8'));
|
|
14
|
+
const serverSource = fs.readFileSync(path.join(packageRoot, 'server.js'), 'utf8');
|
|
15
|
+
|
|
16
|
+
assert.equal(packageJson.build?.appId, 'com.mindexec.desktop', 'Stable desktop appId is required.');
|
|
17
|
+
assert.equal(packageJson.build?.productName, 'MindExec', 'Desktop product name changed unexpectedly.');
|
|
18
|
+
assert.match(mainSource, /title:\s*'MindExec'/, 'The Electron window title must use the MindExec app brand.');
|
|
19
|
+
assert.equal(packageJson.build?.extraMetadata?.main, 'electron/main.cjs', 'Packaged Electron entry point is missing.');
|
|
20
|
+
assert.equal(packageJson.build?.asar, false, 'Bundled runtime executables require an unpacked app directory.');
|
|
21
|
+
assert.ok(packageJson.files?.includes('electron/'), 'The npm package must include the Electron source contract.');
|
|
22
|
+
assert.ok(packageJson.files?.includes('desktop-workspace-state.cjs'), 'The npm package must include desktop workspace state.');
|
|
23
|
+
assert.match(packageJson.scripts?.['desktop:build:win'] || '', /electron-builder/, 'Windows build script is missing.');
|
|
24
|
+
assert.match(packageJson.scripts?.['test:desktop:win'] || '', /windows-package-smoke/, 'Packaged smoke test is missing.');
|
|
25
|
+
assert.match(packageJson.scripts?.['desktop:publish:update:dev'] || '', /--channel dev[\s\S]*--allow-unsigned[\s\S]*--publish/,
|
|
26
|
+
'The explicit development update publishing command is missing.');
|
|
27
|
+
assert.equal(packageJson.dependencies?.['electron-updater'], '^6.8.9', 'The packaged updater dependency is missing.');
|
|
28
|
+
assert.equal(packageJson.build?.extraMetadata?.desktopUpdateChannel, 'dev', 'Development installers must stay on the dev feed.');
|
|
29
|
+
assert.equal(packageJson.build?.publish?.provider, 'generic', 'Desktop updates must use the generic R2-backed provider.');
|
|
30
|
+
assert.equal(
|
|
31
|
+
packageJson.build?.publish?.url,
|
|
32
|
+
'https://mindexec.lovecrdm.workers.dev/dev/windows/x64',
|
|
33
|
+
'The packaged development update URL changed unexpectedly.'
|
|
34
|
+
);
|
|
35
|
+
assert.equal(updateFeed.baseUrl, 'https://mindexec.lovecrdm.workers.dev', 'MindExec update hostname has a typo.');
|
|
36
|
+
assert.equal(updateFeed.channel, 'dev', 'The packaged update feed must default to the isolated dev channel.');
|
|
37
|
+
|
|
38
|
+
assert.equal(
|
|
39
|
+
packageJson.build?.win?.icon,
|
|
40
|
+
'../MindExecution.Web/wwwroot/app-icon-1024.png',
|
|
41
|
+
'Electron must build from the canonical 1024px MindExec icon.'
|
|
42
|
+
);
|
|
43
|
+
const builderIconPath = path.resolve(packageRoot, packageJson.build.win.icon);
|
|
44
|
+
assert.ok(fs.existsSync(builderIconPath), `Electron builder icon is missing: ${builderIconPath}`);
|
|
45
|
+
const builderIconMetadata = await sharp(builderIconPath).metadata();
|
|
46
|
+
assert.equal(builderIconMetadata.width, 1024, 'Electron builder icon must be 1024px wide.');
|
|
47
|
+
assert.equal(builderIconMetadata.height, 1024, 'Electron builder icon must be 1024px tall.');
|
|
48
|
+
assert.equal(builderIconMetadata.hasAlpha, true, 'Electron builder icon must preserve transparency.');
|
|
49
|
+
const { data: builderIconPixels, info: builderIconInfo } = await sharp(builderIconPath)
|
|
50
|
+
.ensureAlpha()
|
|
51
|
+
.raw()
|
|
52
|
+
.toBuffer({ resolveWithObject: true });
|
|
53
|
+
const builderIconCornerAlpha = [
|
|
54
|
+
builderIconPixels[3],
|
|
55
|
+
builderIconPixels[(builderIconInfo.width - 1) * 4 + 3],
|
|
56
|
+
builderIconPixels[(builderIconInfo.height - 1) * builderIconInfo.width * 4 + 3],
|
|
57
|
+
builderIconPixels[(builderIconInfo.width * builderIconInfo.height - 1) * 4 + 3]
|
|
58
|
+
];
|
|
59
|
+
assert.ok(
|
|
60
|
+
builderIconCornerAlpha.every((alpha) => alpha === 0),
|
|
61
|
+
'Electron builder icon must have fully transparent corners.'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const targets = packageJson.build?.win?.target || [];
|
|
65
|
+
assert.ok(targets.some((target) => target.target === 'nsis'), 'NSIS target is required.');
|
|
66
|
+
assert.ok(targets.some((target) => target.target === 'portable'), 'Portable target is required.');
|
|
67
|
+
|
|
68
|
+
assert.match(mainSource, /contextIsolation:\s*true/, 'Electron renderer isolation must remain enabled.');
|
|
69
|
+
assert.match(mainSource, /nodeIntegration:\s*false/, 'Node integration must remain disabled.');
|
|
70
|
+
assert.match(mainSource, /sandbox:\s*true/, 'Electron renderer sandbox must remain enabled.');
|
|
71
|
+
assert.match(mainSource, /preload:\s*path\.join\(__dirname, 'preload\.cjs'\)/, 'The isolated updater preload is missing.');
|
|
72
|
+
assert.match(mainSource, /runtimeMode:\s*'bundled'/, 'Packaged runtime must report bundled mode.');
|
|
73
|
+
assert.match(mainSource, /spawnBundledBridge/, 'Electron must start its bundled LocalBridge.');
|
|
74
|
+
assert.match(mainSource, /wwwroot.*index\.html/s, 'Electron must verify its bundled frontend.');
|
|
75
|
+
assert.match(mainSource, /resolveDedicatedPort/, 'Electron must allocate dedicated local ports.');
|
|
76
|
+
assert.doesNotMatch(mainSource, /adopted|probeBridge/, 'Electron must not adopt an external LocalBridge.');
|
|
77
|
+
assert.match(mainSource, /MINDEXEC_DESKTOP_SMOKE_RESULT/, 'Packaged runtime smoke contract is missing.');
|
|
78
|
+
assert.match(mainSource, /icon:\s*resolveDesktopIconPath\(\)/, 'Electron windows must use the packaged MindExec icon.');
|
|
79
|
+
assert.match(mainSource, /app\.setAppUserModelId\(DESKTOP_APP_ID\)/, 'Windows taskbar identity must use the stable app id.');
|
|
80
|
+
assert.match(mainSource, /resolveDesktopWorkspace/, 'Electron startup must restore the last desktop workspace.');
|
|
81
|
+
assert.match(mainSource, /MINDEXEC_DESKTOP_WORKSPACE_STATE_PATH/, 'Electron must share its workspace state path with LocalBridge.');
|
|
82
|
+
assert.match(mainSource, /resolveDesktopInstallKind/, 'Installed, portable, and unpacked update behavior must remain separate.');
|
|
83
|
+
assert.match(mainSource, /createProductUpdateManager/, 'The Electron update manager is not wired into the app.');
|
|
84
|
+
assert.match(mainSource, /updates:get-status/, 'The updater status IPC contract is missing.');
|
|
85
|
+
assert.match(mainSource, /updates:check/, 'The manual updater check IPC contract is missing.');
|
|
86
|
+
assert.match(mainSource, /updates:install/, 'The updater install IPC contract is missing.');
|
|
87
|
+
assert.match(mainSource, /await stopBundledBridge\(\)[\s\S]{0,180}await waitForBundledRuntimePortsReleased\(\)/,
|
|
88
|
+
'The Bridge and RemoteHub ports must reach zero before the installer starts.');
|
|
89
|
+
assert.match(mainSource, /Restart and update/, 'A downloaded update must ask before restarting active work.');
|
|
90
|
+
assert.match(preloadSource, /mindExecDesktop/, 'The updater preload namespace changed unexpectedly.');
|
|
91
|
+
assert.match(preloadSource, /updates:\s*\{/, 'The updater preload API is missing.');
|
|
92
|
+
assert.match(updateManagerSource, /autoDownload\s*=\s*false/, 'Updates must not download through an unowned implicit path.');
|
|
93
|
+
assert.match(updateManagerSource, /autoInstallOnAppQuit\s*=\s*false/, 'Updates must not restart without the explicit prompt path.');
|
|
94
|
+
assert.match(updateManagerSource, /quitAndInstall\(true, true\)/, 'Downloaded updates must relaunch silently after approval.');
|
|
95
|
+
assert.match(updateManagerSource, /desktop-updates-require-installed-windows-app/,
|
|
96
|
+
'Portable and unpacked builds must fail closed instead of replacing themselves.');
|
|
97
|
+
assert.match(serverSource, /saveDesktopWorkspaceState\(DESKTOP_WORKSPACE_STATE_PATH, resolvedPath\)/,
|
|
98
|
+
'A successful desktop workspace switch must be saved for the next launch.');
|
|
99
|
+
|
|
100
|
+
console.log(JSON.stringify({
|
|
101
|
+
ok: true,
|
|
102
|
+
appId: packageJson.build.appId,
|
|
103
|
+
version: packageJson.version,
|
|
104
|
+
runtimeMode: 'bundled',
|
|
105
|
+
icon: packageJson.build.win.icon,
|
|
106
|
+
updateFeed: `${updateFeed.baseUrl}/${updateFeed.channel}/windows/x64`,
|
|
107
|
+
targets: targets.map((target) => `${target.target}:${target.arch.join(',')}`)
|
|
108
|
+
}));
|