@mindexec/cli 0.2.476 → 0.2.478
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 +7690 -7690
- package/start-bridge.bat +32 -32
- package/start-bridge.sh +81 -81
- package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -546
- package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -251
- package/wwwroot/_content/MindExecution.Shared/css/app.css +1 -1
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +3789 -3789
- 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 -10517
- 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-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 +7065 -7065
- 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 +9956 -9956
- 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-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-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.9h59ukbel7.dll → Google.Protobuf.33g2gb5is0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Core.cx2pre7qz2.dll → MindExecution.Core.z100xo4mg0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.engfih12kr.dll → MindExecution.Kernel.i9cldrx8yc.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.xfunemn9zi.dll → MindExecution.Plugins.Admin.48lzqddb0a.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.ja94a5ze3i.dll → MindExecution.Plugins.Business.7m8u1q142p.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.3q05vzviqb.dll → MindExecution.Plugins.Concept.cl5wmr53od.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.re4jjs91d1.dll → MindExecution.Plugins.Directory.slipi39041.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.89etwfquz1.dll → MindExecution.Plugins.PlanMaster.qfge05l671.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.aqf4vztjji.dll → MindExecution.Plugins.YouTube.tn7v29ut77.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.9b1rucdxvv.dll → MindExecution.Shared.rjqlf0umjn.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.ssp0n0xf1e.dll → MindExecution.Web.rwawrxxk38.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +23 -23
- 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 +78 -106
- package/wwwroot/service-worker.js +33 -33
- package/tree-sitter-grammars/README.md +0 -18
- 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.Directory/background.png +0 -0
- 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/mind-map-core.js.backup +0 -1059
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +0 -434
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +0 -372
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +0 -1258
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import test from 'node:test';
|
|
7
|
-
import {
|
|
8
|
-
createWranglerPutCommand,
|
|
9
|
-
parseUpdateManifest,
|
|
10
|
-
readReleasePlan
|
|
11
|
-
} from './publish-mindexec-desktop-updates.mjs';
|
|
12
|
-
|
|
13
|
-
test('publisher launches Wrangler directly and targets the private update bucket', () => {
|
|
14
|
-
const command = createWranglerPutCommand({
|
|
15
|
-
key: 'dev/windows/x64/MindExec-0.2.461-dev-x64.exe',
|
|
16
|
-
filePath: 'C:\\release\\MindExec-0.2.461-dev-x64.exe',
|
|
17
|
-
immutable: true
|
|
18
|
-
});
|
|
19
|
-
assert.equal(command.executable, process.execPath);
|
|
20
|
-
assert.match(command.args[0], /wrangler(?:-dist)?[\\/]cli\.js$/i);
|
|
21
|
-
assert.ok(command.args.includes('mindexec-desktop-updates/dev/windows/x64/MindExec-0.2.461-dev-x64.exe'));
|
|
22
|
-
assert.equal(command.args.includes('npx.cmd'), false);
|
|
23
|
-
assert.equal(command.args.at(-1), '--remote');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('publisher validates version, size, and SHA-512 before upload', async () => {
|
|
27
|
-
const directory = await mkdtemp(join(tmpdir(), 'mindexec-update-publisher-'));
|
|
28
|
-
try {
|
|
29
|
-
const artifact = 'MindExec-1.2.3-dev-x64.exe';
|
|
30
|
-
const bytes = Buffer.from('unsigned-development-artifact');
|
|
31
|
-
const sha512 = createHash('sha512').update(bytes).digest('base64');
|
|
32
|
-
await writeFile(join(directory, artifact), bytes);
|
|
33
|
-
await writeFile(join(directory, 'latest.yml'), [
|
|
34
|
-
'version: 1.2.3',
|
|
35
|
-
'files:',
|
|
36
|
-
` - url: ${artifact}`,
|
|
37
|
-
` sha512: ${sha512}`,
|
|
38
|
-
` size: ${bytes.length}`,
|
|
39
|
-
`path: ${artifact}`,
|
|
40
|
-
`sha512: ${sha512}`,
|
|
41
|
-
'releaseDate: 2026-08-16T00:00:00.000Z',
|
|
42
|
-
''
|
|
43
|
-
].join('\n'));
|
|
44
|
-
|
|
45
|
-
const parsed = parseUpdateManifest(await (await import('node:fs/promises')).readFile(join(directory, 'latest.yml'), 'utf8'));
|
|
46
|
-
assert.equal(parsed.version, '1.2.3');
|
|
47
|
-
assert.equal(parsed.files[0].name, artifact);
|
|
48
|
-
assert.throws(
|
|
49
|
-
() => readReleasePlan({
|
|
50
|
-
platform: 'windows-x64',
|
|
51
|
-
directory,
|
|
52
|
-
packageVersion: '1.2.3',
|
|
53
|
-
channel: 'dev'
|
|
54
|
-
}),
|
|
55
|
-
/desktop-update-dev-unsigned-ack-required/
|
|
56
|
-
);
|
|
57
|
-
const plan = readReleasePlan({
|
|
58
|
-
platform: 'windows-x64',
|
|
59
|
-
directory,
|
|
60
|
-
packageVersion: '1.2.3',
|
|
61
|
-
channel: 'dev',
|
|
62
|
-
allowUnsigned: true
|
|
63
|
-
});
|
|
64
|
-
assert.equal(plan.prefix, 'dev/windows/x64');
|
|
65
|
-
assert.deepEqual(plan.artifacts.map(item => item.fileName), [artifact]);
|
|
66
|
-
assert.throws(
|
|
67
|
-
() => readReleasePlan({
|
|
68
|
-
platform: 'windows-x64',
|
|
69
|
-
directory,
|
|
70
|
-
packageVersion: '1.2.4',
|
|
71
|
-
channel: 'dev',
|
|
72
|
-
allowUnsigned: true
|
|
73
|
-
}),
|
|
74
|
-
/desktop-update-version-mismatch/
|
|
75
|
-
);
|
|
76
|
-
assert.throws(
|
|
77
|
-
() => readReleasePlan({
|
|
78
|
-
platform: 'windows-x64',
|
|
79
|
-
directory,
|
|
80
|
-
packageVersion: '1.2.3',
|
|
81
|
-
channel: 'stable',
|
|
82
|
-
allowUnsigned: true
|
|
83
|
-
}),
|
|
84
|
-
/desktop-update-stable-unsigned-forbidden/
|
|
85
|
-
);
|
|
86
|
-
} finally {
|
|
87
|
-
await rm(directory, { recursive: true, force: true });
|
|
88
|
-
}
|
|
89
|
-
});
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import {
|
|
8
|
+
createWranglerPutCommand,
|
|
9
|
+
parseUpdateManifest,
|
|
10
|
+
readReleasePlan
|
|
11
|
+
} from './publish-mindexec-desktop-updates.mjs';
|
|
12
|
+
|
|
13
|
+
test('publisher launches Wrangler directly and targets the private update bucket', () => {
|
|
14
|
+
const command = createWranglerPutCommand({
|
|
15
|
+
key: 'dev/windows/x64/MindExec-0.2.461-dev-x64.exe',
|
|
16
|
+
filePath: 'C:\\release\\MindExec-0.2.461-dev-x64.exe',
|
|
17
|
+
immutable: true
|
|
18
|
+
});
|
|
19
|
+
assert.equal(command.executable, process.execPath);
|
|
20
|
+
assert.match(command.args[0], /wrangler(?:-dist)?[\\/]cli\.js$/i);
|
|
21
|
+
assert.ok(command.args.includes('mindexec-desktop-updates/dev/windows/x64/MindExec-0.2.461-dev-x64.exe'));
|
|
22
|
+
assert.equal(command.args.includes('npx.cmd'), false);
|
|
23
|
+
assert.equal(command.args.at(-1), '--remote');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('publisher validates version, size, and SHA-512 before upload', async () => {
|
|
27
|
+
const directory = await mkdtemp(join(tmpdir(), 'mindexec-update-publisher-'));
|
|
28
|
+
try {
|
|
29
|
+
const artifact = 'MindExec-1.2.3-dev-x64.exe';
|
|
30
|
+
const bytes = Buffer.from('unsigned-development-artifact');
|
|
31
|
+
const sha512 = createHash('sha512').update(bytes).digest('base64');
|
|
32
|
+
await writeFile(join(directory, artifact), bytes);
|
|
33
|
+
await writeFile(join(directory, 'latest.yml'), [
|
|
34
|
+
'version: 1.2.3',
|
|
35
|
+
'files:',
|
|
36
|
+
` - url: ${artifact}`,
|
|
37
|
+
` sha512: ${sha512}`,
|
|
38
|
+
` size: ${bytes.length}`,
|
|
39
|
+
`path: ${artifact}`,
|
|
40
|
+
`sha512: ${sha512}`,
|
|
41
|
+
'releaseDate: 2026-08-16T00:00:00.000Z',
|
|
42
|
+
''
|
|
43
|
+
].join('\n'));
|
|
44
|
+
|
|
45
|
+
const parsed = parseUpdateManifest(await (await import('node:fs/promises')).readFile(join(directory, 'latest.yml'), 'utf8'));
|
|
46
|
+
assert.equal(parsed.version, '1.2.3');
|
|
47
|
+
assert.equal(parsed.files[0].name, artifact);
|
|
48
|
+
assert.throws(
|
|
49
|
+
() => readReleasePlan({
|
|
50
|
+
platform: 'windows-x64',
|
|
51
|
+
directory,
|
|
52
|
+
packageVersion: '1.2.3',
|
|
53
|
+
channel: 'dev'
|
|
54
|
+
}),
|
|
55
|
+
/desktop-update-dev-unsigned-ack-required/
|
|
56
|
+
);
|
|
57
|
+
const plan = readReleasePlan({
|
|
58
|
+
platform: 'windows-x64',
|
|
59
|
+
directory,
|
|
60
|
+
packageVersion: '1.2.3',
|
|
61
|
+
channel: 'dev',
|
|
62
|
+
allowUnsigned: true
|
|
63
|
+
});
|
|
64
|
+
assert.equal(plan.prefix, 'dev/windows/x64');
|
|
65
|
+
assert.deepEqual(plan.artifacts.map(item => item.fileName), [artifact]);
|
|
66
|
+
assert.throws(
|
|
67
|
+
() => readReleasePlan({
|
|
68
|
+
platform: 'windows-x64',
|
|
69
|
+
directory,
|
|
70
|
+
packageVersion: '1.2.4',
|
|
71
|
+
channel: 'dev',
|
|
72
|
+
allowUnsigned: true
|
|
73
|
+
}),
|
|
74
|
+
/desktop-update-version-mismatch/
|
|
75
|
+
);
|
|
76
|
+
assert.throws(
|
|
77
|
+
() => readReleasePlan({
|
|
78
|
+
platform: 'windows-x64',
|
|
79
|
+
directory,
|
|
80
|
+
packageVersion: '1.2.3',
|
|
81
|
+
channel: 'stable',
|
|
82
|
+
allowUnsigned: true
|
|
83
|
+
}),
|
|
84
|
+
/desktop-update-stable-unsigned-forbidden/
|
|
85
|
+
);
|
|
86
|
+
} finally {
|
|
87
|
+
await rm(directory, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import test from 'node:test';
|
|
3
|
-
import worker, { resolveUpdateObjectKey } from '../../workers/mindexec-desktop-updates/src/index.mjs';
|
|
4
|
-
|
|
5
|
-
function storedObject(body = 'release') {
|
|
6
|
-
const bytes = new TextEncoder().encode(body);
|
|
7
|
-
return {
|
|
8
|
-
body: new ReadableStream({ start(controller) { controller.enqueue(bytes); controller.close(); } }),
|
|
9
|
-
size: bytes.byteLength,
|
|
10
|
-
httpEtag: '"release-etag"',
|
|
11
|
-
writeHttpMetadata(headers) { headers.set('content-type', 'application/octet-stream'); }
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
test('only fixed Windows stable and development update paths are public', () => {
|
|
16
|
-
assert.equal(resolveUpdateObjectKey('/stable/windows/x64/latest.yml'), 'stable/windows/x64/latest.yml');
|
|
17
|
-
assert.equal(resolveUpdateObjectKey('/dev/windows/x64/latest.yml'), 'dev/windows/x64/latest.yml');
|
|
18
|
-
assert.equal(
|
|
19
|
-
resolveUpdateObjectKey('/dev/windows/x64/MindExec-0.2.461-dev-x64.exe'),
|
|
20
|
-
'dev/windows/x64/MindExec-0.2.461-dev-x64.exe'
|
|
21
|
-
);
|
|
22
|
-
assert.equal(resolveUpdateObjectKey('/private/secret.txt'), '');
|
|
23
|
-
assert.equal(resolveUpdateObjectKey('/dev/windows/x64/../secret.txt'), '');
|
|
24
|
-
assert.equal(resolveUpdateObjectKey('/preview/windows/x64/latest.yml'), '');
|
|
25
|
-
assert.equal(resolveUpdateObjectKey('/dev/macos/arm64/latest-mac.yml'), '');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('worker exposes health but rejects writes and bucket listing', async () => {
|
|
29
|
-
const env = { UPDATE_BUCKET: { head: async () => null, get: async () => null } };
|
|
30
|
-
assert.equal((await worker.fetch(new Request('https://updates.example/healthz'), env)).status, 200);
|
|
31
|
-
assert.equal((await worker.fetch(new Request('https://updates.example/', { method: 'PUT' }), env)).status, 405);
|
|
32
|
-
assert.equal((await worker.fetch(new Request('https://updates.example/'), env)).status, 404);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('worker streams a manifest without caching it', async () => {
|
|
36
|
-
const object = storedObject('version: 0.2.461');
|
|
37
|
-
let observedOptions = null;
|
|
38
|
-
const env = {
|
|
39
|
-
UPDATE_BUCKET: {
|
|
40
|
-
head: async () => object,
|
|
41
|
-
get: async (_key, options) => { observedOptions = options; return object; }
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const response = await worker.fetch(new Request('https://updates.example/dev/windows/x64/latest.yml'), env);
|
|
45
|
-
assert.equal(response.status, 200);
|
|
46
|
-
assert.equal('range' in observedOptions, false);
|
|
47
|
-
assert.equal(await response.text(), 'version: 0.2.461');
|
|
48
|
-
assert.equal(response.headers.get('cache-control'), 'no-store, max-age=0');
|
|
49
|
-
assert.equal(response.headers.get('etag'), '"release-etag"');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('worker preserves resumable byte-range responses for large installers', async () => {
|
|
53
|
-
const object = storedObject('part');
|
|
54
|
-
object.size = 20;
|
|
55
|
-
object.range = { offset: 8, length: 4 };
|
|
56
|
-
const env = {
|
|
57
|
-
UPDATE_BUCKET: {
|
|
58
|
-
head: async () => object,
|
|
59
|
-
get: async (_key, options) => {
|
|
60
|
-
assert.ok(options.range, 'an explicit Range request must reach R2');
|
|
61
|
-
return object;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
const response = await worker.fetch(new Request(
|
|
66
|
-
'https://updates.example/dev/windows/x64/MindExec-0.2.461-dev-x64.exe',
|
|
67
|
-
{ headers: { range: 'bytes=8-11' } }
|
|
68
|
-
), env);
|
|
69
|
-
assert.equal(response.status, 206);
|
|
70
|
-
assert.equal(response.headers.get('content-range'), 'bytes 8-11/20');
|
|
71
|
-
assert.equal(response.headers.get('content-length'), '4');
|
|
72
|
-
});
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import worker, { resolveUpdateObjectKey } from '../../workers/mindexec-desktop-updates/src/index.mjs';
|
|
4
|
+
|
|
5
|
+
function storedObject(body = 'release') {
|
|
6
|
+
const bytes = new TextEncoder().encode(body);
|
|
7
|
+
return {
|
|
8
|
+
body: new ReadableStream({ start(controller) { controller.enqueue(bytes); controller.close(); } }),
|
|
9
|
+
size: bytes.byteLength,
|
|
10
|
+
httpEtag: '"release-etag"',
|
|
11
|
+
writeHttpMetadata(headers) { headers.set('content-type', 'application/octet-stream'); }
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test('only fixed Windows stable and development update paths are public', () => {
|
|
16
|
+
assert.equal(resolveUpdateObjectKey('/stable/windows/x64/latest.yml'), 'stable/windows/x64/latest.yml');
|
|
17
|
+
assert.equal(resolveUpdateObjectKey('/dev/windows/x64/latest.yml'), 'dev/windows/x64/latest.yml');
|
|
18
|
+
assert.equal(
|
|
19
|
+
resolveUpdateObjectKey('/dev/windows/x64/MindExec-0.2.461-dev-x64.exe'),
|
|
20
|
+
'dev/windows/x64/MindExec-0.2.461-dev-x64.exe'
|
|
21
|
+
);
|
|
22
|
+
assert.equal(resolveUpdateObjectKey('/private/secret.txt'), '');
|
|
23
|
+
assert.equal(resolveUpdateObjectKey('/dev/windows/x64/../secret.txt'), '');
|
|
24
|
+
assert.equal(resolveUpdateObjectKey('/preview/windows/x64/latest.yml'), '');
|
|
25
|
+
assert.equal(resolveUpdateObjectKey('/dev/macos/arm64/latest-mac.yml'), '');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('worker exposes health but rejects writes and bucket listing', async () => {
|
|
29
|
+
const env = { UPDATE_BUCKET: { head: async () => null, get: async () => null } };
|
|
30
|
+
assert.equal((await worker.fetch(new Request('https://updates.example/healthz'), env)).status, 200);
|
|
31
|
+
assert.equal((await worker.fetch(new Request('https://updates.example/', { method: 'PUT' }), env)).status, 405);
|
|
32
|
+
assert.equal((await worker.fetch(new Request('https://updates.example/'), env)).status, 404);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('worker streams a manifest without caching it', async () => {
|
|
36
|
+
const object = storedObject('version: 0.2.461');
|
|
37
|
+
let observedOptions = null;
|
|
38
|
+
const env = {
|
|
39
|
+
UPDATE_BUCKET: {
|
|
40
|
+
head: async () => object,
|
|
41
|
+
get: async (_key, options) => { observedOptions = options; return object; }
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const response = await worker.fetch(new Request('https://updates.example/dev/windows/x64/latest.yml'), env);
|
|
45
|
+
assert.equal(response.status, 200);
|
|
46
|
+
assert.equal('range' in observedOptions, false);
|
|
47
|
+
assert.equal(await response.text(), 'version: 0.2.461');
|
|
48
|
+
assert.equal(response.headers.get('cache-control'), 'no-store, max-age=0');
|
|
49
|
+
assert.equal(response.headers.get('etag'), '"release-etag"');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('worker preserves resumable byte-range responses for large installers', async () => {
|
|
53
|
+
const object = storedObject('part');
|
|
54
|
+
object.size = 20;
|
|
55
|
+
object.range = { offset: 8, length: 4 };
|
|
56
|
+
const env = {
|
|
57
|
+
UPDATE_BUCKET: {
|
|
58
|
+
head: async () => object,
|
|
59
|
+
get: async (_key, options) => {
|
|
60
|
+
assert.ok(options.range, 'an explicit Range request must reach R2');
|
|
61
|
+
return object;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const response = await worker.fetch(new Request(
|
|
66
|
+
'https://updates.example/dev/windows/x64/MindExec-0.2.461-dev-x64.exe',
|
|
67
|
+
{ headers: { range: 'bytes=8-11' } }
|
|
68
|
+
), env);
|
|
69
|
+
assert.equal(response.status, 206);
|
|
70
|
+
assert.equal(response.headers.get('content-range'), 'bytes 8-11/20');
|
|
71
|
+
assert.equal(response.headers.get('content-length'), '4');
|
|
72
|
+
});
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import desktopWorkspaceState from '../desktop-workspace-state.cjs';
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
loadDesktopWorkspaceState,
|
|
9
|
-
resolveDesktopWorkspace,
|
|
10
|
-
saveDesktopWorkspaceState
|
|
11
|
-
} = desktopWorkspaceState;
|
|
12
|
-
|
|
13
|
-
const smokeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mindexec-workspace-state-'));
|
|
14
|
-
const savedWorkspace = path.join(smokeRoot, 'saved-workspace');
|
|
15
|
-
const explicitWorkspace = path.join(smokeRoot, 'explicit-workspace');
|
|
16
|
-
const fallbackWorkspace = path.join(smokeRoot, 'fallback-workspace');
|
|
17
|
-
const missingWorkspace = path.join(smokeRoot, 'missing-workspace');
|
|
18
|
-
const statePath = path.join(smokeRoot, 'profile', 'workspace-state.json');
|
|
19
|
-
|
|
20
|
-
fs.mkdirSync(savedWorkspace, { recursive: true });
|
|
21
|
-
fs.mkdirSync(explicitWorkspace, { recursive: true });
|
|
22
|
-
fs.mkdirSync(fallbackWorkspace, { recursive: true });
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
assert.equal(saveDesktopWorkspaceState('', savedWorkspace), false, 'Non-desktop runtimes must remain a no-op.');
|
|
26
|
-
assert.equal(saveDesktopWorkspaceState(statePath, savedWorkspace), true);
|
|
27
|
-
assert.deepEqual(loadDesktopWorkspaceState(statePath), {
|
|
28
|
-
version: 1,
|
|
29
|
-
workspacePath: path.resolve(savedWorkspace)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
assert.equal(saveDesktopWorkspaceState(statePath, explicitWorkspace), true);
|
|
33
|
-
assert.equal(loadDesktopWorkspaceState(statePath)?.workspacePath, path.resolve(explicitWorkspace),
|
|
34
|
-
'Changing folders must replace the prior desktop workspace state.');
|
|
35
|
-
assert.equal(saveDesktopWorkspaceState(statePath, savedWorkspace), true);
|
|
36
|
-
|
|
37
|
-
assert.deepEqual(resolveDesktopWorkspace({
|
|
38
|
-
statePath,
|
|
39
|
-
fallbackWorkspace
|
|
40
|
-
}), {
|
|
41
|
-
workspacePath: path.resolve(savedWorkspace),
|
|
42
|
-
source: 'saved'
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
assert.deepEqual(resolveDesktopWorkspace({
|
|
46
|
-
explicitWorkspace,
|
|
47
|
-
statePath,
|
|
48
|
-
fallbackWorkspace
|
|
49
|
-
}), {
|
|
50
|
-
workspacePath: path.resolve(explicitWorkspace),
|
|
51
|
-
source: 'explicit'
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
fs.writeFileSync(statePath, `${JSON.stringify({ version: 1, workspacePath: missingWorkspace })}\n`, 'utf8');
|
|
55
|
-
assert.deepEqual(resolveDesktopWorkspace({
|
|
56
|
-
statePath,
|
|
57
|
-
fallbackWorkspace
|
|
58
|
-
}), {
|
|
59
|
-
workspacePath: path.resolve(fallbackWorkspace),
|
|
60
|
-
source: 'fallback'
|
|
61
|
-
});
|
|
62
|
-
assert.equal(fs.existsSync(missingWorkspace), false, 'A deleted saved workspace must not be recreated.');
|
|
63
|
-
|
|
64
|
-
fs.writeFileSync(statePath, '{broken json', 'utf8');
|
|
65
|
-
assert.deepEqual(resolveDesktopWorkspace({
|
|
66
|
-
statePath,
|
|
67
|
-
fallbackWorkspace
|
|
68
|
-
}), {
|
|
69
|
-
workspacePath: path.resolve(fallbackWorkspace),
|
|
70
|
-
source: 'fallback'
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
console.log(JSON.stringify({
|
|
74
|
-
ok: true,
|
|
75
|
-
restoredWorkspace: path.resolve(savedWorkspace),
|
|
76
|
-
invalidSavedWorkspaceFallsBack: true,
|
|
77
|
-
explicitWorkspaceWins: true
|
|
78
|
-
}));
|
|
79
|
-
} finally {
|
|
80
|
-
fs.rmSync(smokeRoot, { recursive: true, force: true });
|
|
81
|
-
}
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import desktopWorkspaceState from '../desktop-workspace-state.cjs';
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
loadDesktopWorkspaceState,
|
|
9
|
+
resolveDesktopWorkspace,
|
|
10
|
+
saveDesktopWorkspaceState
|
|
11
|
+
} = desktopWorkspaceState;
|
|
12
|
+
|
|
13
|
+
const smokeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mindexec-workspace-state-'));
|
|
14
|
+
const savedWorkspace = path.join(smokeRoot, 'saved-workspace');
|
|
15
|
+
const explicitWorkspace = path.join(smokeRoot, 'explicit-workspace');
|
|
16
|
+
const fallbackWorkspace = path.join(smokeRoot, 'fallback-workspace');
|
|
17
|
+
const missingWorkspace = path.join(smokeRoot, 'missing-workspace');
|
|
18
|
+
const statePath = path.join(smokeRoot, 'profile', 'workspace-state.json');
|
|
19
|
+
|
|
20
|
+
fs.mkdirSync(savedWorkspace, { recursive: true });
|
|
21
|
+
fs.mkdirSync(explicitWorkspace, { recursive: true });
|
|
22
|
+
fs.mkdirSync(fallbackWorkspace, { recursive: true });
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
assert.equal(saveDesktopWorkspaceState('', savedWorkspace), false, 'Non-desktop runtimes must remain a no-op.');
|
|
26
|
+
assert.equal(saveDesktopWorkspaceState(statePath, savedWorkspace), true);
|
|
27
|
+
assert.deepEqual(loadDesktopWorkspaceState(statePath), {
|
|
28
|
+
version: 1,
|
|
29
|
+
workspacePath: path.resolve(savedWorkspace)
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
assert.equal(saveDesktopWorkspaceState(statePath, explicitWorkspace), true);
|
|
33
|
+
assert.equal(loadDesktopWorkspaceState(statePath)?.workspacePath, path.resolve(explicitWorkspace),
|
|
34
|
+
'Changing folders must replace the prior desktop workspace state.');
|
|
35
|
+
assert.equal(saveDesktopWorkspaceState(statePath, savedWorkspace), true);
|
|
36
|
+
|
|
37
|
+
assert.deepEqual(resolveDesktopWorkspace({
|
|
38
|
+
statePath,
|
|
39
|
+
fallbackWorkspace
|
|
40
|
+
}), {
|
|
41
|
+
workspacePath: path.resolve(savedWorkspace),
|
|
42
|
+
source: 'saved'
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
assert.deepEqual(resolveDesktopWorkspace({
|
|
46
|
+
explicitWorkspace,
|
|
47
|
+
statePath,
|
|
48
|
+
fallbackWorkspace
|
|
49
|
+
}), {
|
|
50
|
+
workspacePath: path.resolve(explicitWorkspace),
|
|
51
|
+
source: 'explicit'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
fs.writeFileSync(statePath, `${JSON.stringify({ version: 1, workspacePath: missingWorkspace })}\n`, 'utf8');
|
|
55
|
+
assert.deepEqual(resolveDesktopWorkspace({
|
|
56
|
+
statePath,
|
|
57
|
+
fallbackWorkspace
|
|
58
|
+
}), {
|
|
59
|
+
workspacePath: path.resolve(fallbackWorkspace),
|
|
60
|
+
source: 'fallback'
|
|
61
|
+
});
|
|
62
|
+
assert.equal(fs.existsSync(missingWorkspace), false, 'A deleted saved workspace must not be recreated.');
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(statePath, '{broken json', 'utf8');
|
|
65
|
+
assert.deepEqual(resolveDesktopWorkspace({
|
|
66
|
+
statePath,
|
|
67
|
+
fallbackWorkspace
|
|
68
|
+
}), {
|
|
69
|
+
workspacePath: path.resolve(fallbackWorkspace),
|
|
70
|
+
source: 'fallback'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log(JSON.stringify({
|
|
74
|
+
ok: true,
|
|
75
|
+
restoredWorkspace: path.resolve(savedWorkspace),
|
|
76
|
+
invalidSavedWorkspaceFallsBack: true,
|
|
77
|
+
explicitWorkspaceWins: true
|
|
78
|
+
}));
|
|
79
|
+
} finally {
|
|
80
|
+
fs.rmSync(smokeRoot, { recursive: true, force: true });
|
|
81
|
+
}
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import { chromium } from 'playwright-core';
|
|
2
|
-
|
|
3
|
-
function readArgument(name, fallback = '') {
|
|
4
|
-
const index = process.argv.indexOf(name);
|
|
5
|
-
return index >= 0 ? String(process.argv[index + 1] || fallback) : fallback;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const debugPort = Number.parseInt(readArgument('--debug-port'), 10);
|
|
9
|
-
const expectedVersion = readArgument('--expected-version');
|
|
10
|
-
const timeoutMs = Number.parseInt(readArgument('--timeout-ms', '300000'), 10);
|
|
11
|
-
if (!Number.isInteger(debugPort) || debugPort <= 0 || !expectedVersion) {
|
|
12
|
-
throw new Error('usage: --debug-port <port> --expected-version <version> [--timeout-ms <ms>]');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const endpoint = `http://127.0.0.1:${debugPort}`;
|
|
16
|
-
const deadline = Date.now() + timeoutMs;
|
|
17
|
-
let browser = null;
|
|
18
|
-
let page = null;
|
|
19
|
-
let lastError = null;
|
|
20
|
-
|
|
21
|
-
while (Date.now() < deadline && !page) {
|
|
22
|
-
try {
|
|
23
|
-
browser = await chromium.connectOverCDP(endpoint);
|
|
24
|
-
const contexts = browser.contexts();
|
|
25
|
-
page = contexts.flatMap(context => context.pages()).find(candidate =>
|
|
26
|
-
candidate.url().startsWith('http://127.0.0.1:')) || contexts[0]?.pages()[0] || null;
|
|
27
|
-
if (!page) throw new Error('desktop-page-not-ready');
|
|
28
|
-
await page.waitForFunction(() => Boolean(window.mindExecDesktop?.updates), null, { timeout: 5_000 });
|
|
29
|
-
} catch (error) {
|
|
30
|
-
lastError = error;
|
|
31
|
-
page = null;
|
|
32
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (!page) throw new Error(`desktop-cdp-not-ready:${lastError?.message || 'timeout'}`);
|
|
36
|
-
|
|
37
|
-
await page.evaluate(() => window.mindExecDesktop.updates.check());
|
|
38
|
-
let status = null;
|
|
39
|
-
while (Date.now() < deadline) {
|
|
40
|
-
status = await page.evaluate(() => window.mindExecDesktop.updates.getStatus());
|
|
41
|
-
if (status?.state === 'error') throw new Error(`desktop-update-check-failed:${status.error}`);
|
|
42
|
-
if (status?.downloaded && status?.availableVersion === expectedVersion) break;
|
|
43
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
44
|
-
}
|
|
45
|
-
if (!status?.downloaded || status?.availableVersion !== expectedVersion) {
|
|
46
|
-
throw new Error(`desktop-update-not-downloaded:${JSON.stringify(status)}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let rendererClosedDuringInstall = false;
|
|
50
|
-
try {
|
|
51
|
-
await page.evaluate(() => window.mindExecDesktop.updates.install());
|
|
52
|
-
} catch (error) {
|
|
53
|
-
const message = String(error?.message || error);
|
|
54
|
-
if (!/Target page, context or browser has been closed|Execution context was destroyed|Protocol error|closed/i.test(message)) {
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
rendererClosedDuringInstall = true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log(JSON.stringify({
|
|
61
|
-
ok: true,
|
|
62
|
-
downloadedVersion: status.availableVersion,
|
|
63
|
-
progress: status.progress,
|
|
64
|
-
installInvoked: true,
|
|
65
|
-
rendererClosedDuringInstall
|
|
66
|
-
}));
|
|
67
|
-
process.exit(0);
|
|
1
|
+
import { chromium } from 'playwright-core';
|
|
2
|
+
|
|
3
|
+
function readArgument(name, fallback = '') {
|
|
4
|
+
const index = process.argv.indexOf(name);
|
|
5
|
+
return index >= 0 ? String(process.argv[index + 1] || fallback) : fallback;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const debugPort = Number.parseInt(readArgument('--debug-port'), 10);
|
|
9
|
+
const expectedVersion = readArgument('--expected-version');
|
|
10
|
+
const timeoutMs = Number.parseInt(readArgument('--timeout-ms', '300000'), 10);
|
|
11
|
+
if (!Number.isInteger(debugPort) || debugPort <= 0 || !expectedVersion) {
|
|
12
|
+
throw new Error('usage: --debug-port <port> --expected-version <version> [--timeout-ms <ms>]');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const endpoint = `http://127.0.0.1:${debugPort}`;
|
|
16
|
+
const deadline = Date.now() + timeoutMs;
|
|
17
|
+
let browser = null;
|
|
18
|
+
let page = null;
|
|
19
|
+
let lastError = null;
|
|
20
|
+
|
|
21
|
+
while (Date.now() < deadline && !page) {
|
|
22
|
+
try {
|
|
23
|
+
browser = await chromium.connectOverCDP(endpoint);
|
|
24
|
+
const contexts = browser.contexts();
|
|
25
|
+
page = contexts.flatMap(context => context.pages()).find(candidate =>
|
|
26
|
+
candidate.url().startsWith('http://127.0.0.1:')) || contexts[0]?.pages()[0] || null;
|
|
27
|
+
if (!page) throw new Error('desktop-page-not-ready');
|
|
28
|
+
await page.waitForFunction(() => Boolean(window.mindExecDesktop?.updates), null, { timeout: 5_000 });
|
|
29
|
+
} catch (error) {
|
|
30
|
+
lastError = error;
|
|
31
|
+
page = null;
|
|
32
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!page) throw new Error(`desktop-cdp-not-ready:${lastError?.message || 'timeout'}`);
|
|
36
|
+
|
|
37
|
+
await page.evaluate(() => window.mindExecDesktop.updates.check());
|
|
38
|
+
let status = null;
|
|
39
|
+
while (Date.now() < deadline) {
|
|
40
|
+
status = await page.evaluate(() => window.mindExecDesktop.updates.getStatus());
|
|
41
|
+
if (status?.state === 'error') throw new Error(`desktop-update-check-failed:${status.error}`);
|
|
42
|
+
if (status?.downloaded && status?.availableVersion === expectedVersion) break;
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
44
|
+
}
|
|
45
|
+
if (!status?.downloaded || status?.availableVersion !== expectedVersion) {
|
|
46
|
+
throw new Error(`desktop-update-not-downloaded:${JSON.stringify(status)}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let rendererClosedDuringInstall = false;
|
|
50
|
+
try {
|
|
51
|
+
await page.evaluate(() => window.mindExecDesktop.updates.install());
|
|
52
|
+
} catch (error) {
|
|
53
|
+
const message = String(error?.message || error);
|
|
54
|
+
if (!/Target page, context or browser has been closed|Execution context was destroyed|Protocol error|closed/i.test(message)) {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
rendererClosedDuringInstall = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(JSON.stringify({
|
|
61
|
+
ok: true,
|
|
62
|
+
downloadedVersion: status.availableVersion,
|
|
63
|
+
progress: status.progress,
|
|
64
|
+
installInvoked: true,
|
|
65
|
+
rendererClosedDuringInstall
|
|
66
|
+
}));
|
|
67
|
+
process.exit(0);
|