@mindexec/cli 0.2.465 → 0.2.467

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.
Files changed (29) hide show
  1. package/README.md +3 -0
  2. package/codex-runtime.js +546 -39
  3. package/package.json +38 -34
  4. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  5. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  6. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  7. package/scripts/board-render-ownership-smoke.mjs +85 -0
  8. package/scripts/codex-runtime-home-smoke.mjs +58 -0
  9. package/scripts/codex-sdk-image-live-smoke.mjs +43 -0
  10. package/scripts/codex-sdk-image-runtime-smoke.mjs +122 -0
  11. package/server.js +17 -0
  12. package/wwwroot/_content/MindExecution.Shared/js/mind-map-board-render-ownership.js +81 -0
  13. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +85 -32
  14. package/wwwroot/_framework/MindExecution.Core.7oa1zr128r.dll +0 -0
  15. package/wwwroot/_framework/{MindExecution.Kernel.98wpeg0ntq.dll → MindExecution.Kernel.dqr12udjsr.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.Admin.y25y20jkiv.dll → MindExecution.Plugins.Admin.6bt51zpdq7.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Plugins.Business.guko3zr1rb.dll → MindExecution.Plugins.Business.e92nh6aw24.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Plugins.Concept.qmin06apyt.dll → MindExecution.Plugins.Concept.z5rfp7c5gn.dll} +0 -0
  19. package/wwwroot/_framework/{MindExecution.Plugins.Directory.6rw2l9utyr.dll → MindExecution.Plugins.Directory.6qmfycdkxt.dll} +0 -0
  20. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.72ir717kho.dll → MindExecution.Plugins.PlanMaster.lbbov61zg3.dll} +0 -0
  21. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.rkge49vbw6.dll → MindExecution.Plugins.YouTube.7vjisvqu3j.dll} +0 -0
  22. package/wwwroot/_framework/{MindExecution.Shared.q4dgev2e3z.dll → MindExecution.Shared.ydgyfah35e.dll} +0 -0
  23. package/wwwroot/_framework/MindExecution.Web.b68gpm21e3.dll +0 -0
  24. package/wwwroot/_framework/blazor.boot.json +21 -21
  25. package/wwwroot/index.html +2 -1
  26. package/wwwroot/service-worker-assets.js +28 -24
  27. package/wwwroot/service-worker.js +1 -1
  28. package/wwwroot/_framework/MindExecution.Core.o29fdql6j5.dll +0 -0
  29. package/wwwroot/_framework/MindExecution.Web.ckdrqv28k2.dll +0 -0
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.465",
3
+ "version": "0.2.467",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
7
7
  "files": [
8
8
  "start-bridge.bat",
9
9
  "start-bridge.sh",
10
- "launch-bridge.cjs",
11
- "desktop-workspace-state.cjs",
12
- "server.js",
10
+ "launch-bridge.cjs",
11
+ "desktop-workspace-state.cjs",
12
+ "server.js",
13
13
  "remote-hub.js",
14
14
  "lzo1x.js",
15
15
  "mode4-atlas.js",
@@ -27,17 +27,21 @@
27
27
  "scripts": {
28
28
  "start": "node launch-bridge.cjs",
29
29
  "dev": "node launch-bridge.cjs --watch",
30
- "desktop:start": "electron electron/main.cjs",
31
- "desktop:build:win": "electron-builder --win --x64 --publish never",
32
- "desktop:inspect:update:dev": "node scripts/publish-mindexec-desktop-updates.mjs --channel dev --platform windows-x64 --dir ../artifacts/electron --allow-unsigned",
33
- "desktop:publish:update:dev": "node scripts/publish-mindexec-desktop-updates.mjs --channel dev --platform windows-x64 --dir ../artifacts/electron --allow-unsigned --publish",
34
- "desktop:deploy:update-worker": "node ../r2-auth-proxy/node_modules/wrangler/wrangler-dist/cli.js deploy --config ../workers/mindexec-desktop-updates/wrangler.jsonc",
35
- "desktop:check:update-worker": "node ../r2-auth-proxy/node_modules/wrangler/wrangler-dist/cli.js deploy --dry-run --config ../workers/mindexec-desktop-updates/wrangler.jsonc",
36
- "test:desktop:update": "node --test electron/update-manager-smoke.mjs electron/recurring-update-owner-smoke.mjs scripts/desktop-update-worker-smoke.mjs scripts/desktop-update-publisher-smoke.mjs",
37
- "test:desktop": "node electron/source-smoke.mjs && node scripts/desktop-workspace-state-smoke.mjs && npm run test:desktop:update",
30
+ "desktop:start": "electron electron/main.cjs",
31
+ "desktop:build:win": "electron-builder --win --x64 --publish never",
32
+ "desktop:inspect:update:dev": "node scripts/publish-mindexec-desktop-updates.mjs --channel dev --platform windows-x64 --dir ../artifacts/electron --allow-unsigned",
33
+ "desktop:publish:update:dev": "node scripts/publish-mindexec-desktop-updates.mjs --channel dev --platform windows-x64 --dir ../artifacts/electron --allow-unsigned --publish",
34
+ "desktop:deploy:update-worker": "node ../r2-auth-proxy/node_modules/wrangler/wrangler-dist/cli.js deploy --config ../workers/mindexec-desktop-updates/wrangler.jsonc",
35
+ "desktop:check:update-worker": "node ../r2-auth-proxy/node_modules/wrangler/wrangler-dist/cli.js deploy --dry-run --config ../workers/mindexec-desktop-updates/wrangler.jsonc",
36
+ "test:desktop:update": "node --test electron/update-manager-smoke.mjs electron/recurring-update-owner-smoke.mjs scripts/desktop-update-worker-smoke.mjs scripts/desktop-update-publisher-smoke.mjs",
37
+ "test:desktop": "node electron/source-smoke.mjs && node scripts/desktop-workspace-state-smoke.mjs && npm run test:desktop:update",
38
38
  "test:desktop:win": "node electron/windows-package-smoke.mjs",
39
- "test:syntax": "node --check server.js && node --check remote-hub.js && node --check lzo1x.js && node --check mode4-atlas.js && node --check mode4-atlas-worker.js && node --check codex-runtime.js && node --check codex-model-catalog.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check desktop-workspace-state.cjs && node --check electron/main.cjs && node --check electron/preload.cjs && node --check electron/update-manager.cjs && node --check electron/update-feed-resolver.cjs && node --check electron/recurring-update-owner.cjs && node --check electron/source-smoke.mjs && node --check electron/windows-package-smoke.mjs && node --check electron/update-manager-smoke.mjs && node --check electron/recurring-update-owner-smoke.mjs && node --check scripts/desktop-workspace-state-smoke.mjs && node --check scripts/desktop-update-worker-smoke.mjs && node --check scripts/desktop-update-publisher-smoke.mjs && node --check scripts/publish-mindexec-desktop-updates.mjs && node --check ../workers/mindexec-desktop-updates/src/index.mjs && node --check scripts/setup-tree-sitter-grammars.mjs && node --check scripts/npm-fresh-install-smoke.mjs && node --check scripts/auth-session-smoke.mjs && node --check scripts/codex-model-catalog-smoke.mjs && node --check scripts/codex-sdk-runtime-smoke.mjs && node --check scripts/remote-hub-smoke.mjs && node --check scripts/remote-hub-scale-smoke.mjs && node --check scripts/remote-fleet-render-smoke.mjs && node --check scripts/remote-http-smoke.mjs && node --check scripts/remote-frame-ws-smoke.mjs && node --check scripts/remote-input-ws-smoke.mjs && node --check scripts/remote-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-smoke.mjs && node --check scripts/remote-registry-follower-smoke.mjs && node --check scripts/remote-agent-package-smoke.mjs && node --check scripts/remote-fast-live-rate-smoke.mjs && node --check scripts/remote-fast-mdm-browser-smoke.mjs",
40
- "test:codex-sdk": "node scripts/codex-sdk-runtime-smoke.mjs",
39
+ "test:syntax": "node --check server.js && node --check remote-hub.js && node --check lzo1x.js && node --check mode4-atlas.js && node --check mode4-atlas-worker.js && node --check codex-runtime.js && node --check codex-model-catalog.js && node --check launch-bridge.cjs && node --check port-guard.cjs && node --check desktop-workspace-state.cjs && node --check electron/main.cjs && node --check electron/preload.cjs && node --check electron/update-manager.cjs && node --check electron/update-feed-resolver.cjs && node --check electron/recurring-update-owner.cjs && node --check electron/source-smoke.mjs && node --check electron/windows-package-smoke.mjs && node --check electron/update-manager-smoke.mjs && node --check electron/recurring-update-owner-smoke.mjs && node --check scripts/desktop-workspace-state-smoke.mjs && node --check scripts/desktop-update-worker-smoke.mjs && node --check scripts/desktop-update-publisher-smoke.mjs && node --check scripts/board-render-ownership-smoke.mjs && node --check ../workers/mindexec-desktop-updates/src/index.mjs && node --check scripts/setup-tree-sitter-grammars.mjs && node --check scripts/npm-fresh-install-smoke.mjs && node --check scripts/auth-session-smoke.mjs && node --check scripts/codex-model-catalog-smoke.mjs && node --check scripts/codex-sdk-runtime-smoke.mjs && node --check scripts/codex-sdk-image-runtime-smoke.mjs && node --check scripts/codex-sdk-image-live-smoke.mjs && node --check scripts/codex-runtime-home-smoke.mjs && node --check scripts/remote-hub-smoke.mjs && node --check scripts/remote-hub-scale-smoke.mjs && node --check scripts/remote-fleet-render-smoke.mjs && node --check scripts/remote-http-smoke.mjs && node --check scripts/remote-frame-ws-smoke.mjs && node --check scripts/remote-input-ws-smoke.mjs && node --check scripts/remote-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-smoke.mjs && node --check scripts/remote-registry-follower-smoke.mjs && node --check scripts/remote-agent-package-smoke.mjs && node --check scripts/remote-fast-live-rate-smoke.mjs && node --check scripts/remote-fast-mdm-browser-smoke.mjs",
40
+ "test:canvas:board-owner": "node scripts/board-render-ownership-smoke.mjs",
41
+ "test:codex-sdk": "node scripts/codex-sdk-runtime-smoke.mjs",
42
+ "test:codex-sdk:image": "node scripts/codex-sdk-image-runtime-smoke.mjs",
43
+ "test:codex-sdk:image:live": "node scripts/codex-sdk-image-live-smoke.mjs --live",
44
+ "test:codex-sdk:home": "node scripts/codex-runtime-home-smoke.mjs",
41
45
  "test:codex-sdk:live": "node scripts/codex-sdk-runtime-smoke.mjs --live",
42
46
  "test:codex-sdk:live-tools": "node scripts/codex-sdk-runtime-smoke.mjs --live-tools",
43
47
  "test:codex-model-catalog": "node scripts/codex-model-catalog-smoke.mjs",
@@ -83,17 +87,17 @@
83
87
  "cors": "^2.8.5",
84
88
  "express": "^4.18.2",
85
89
  "ffmpeg-static": "^5.3.0",
86
- "multer": "^2.2.0",
87
- "electron-updater": "^6.8.9",
88
- "playwright-core": "^1.53.0",
90
+ "multer": "^2.2.0",
91
+ "electron-updater": "^6.8.9",
92
+ "playwright-core": "^1.53.0",
89
93
  "sharp": "^0.35.3",
90
94
  "web-tree-sitter": "^0.22.6",
91
95
  "ws": "^8.16.0"
92
96
  },
93
- "overrides": {
94
- "body-parser": "1.20.6",
95
- "js-yaml": "4.3.1"
96
- },
97
+ "overrides": {
98
+ "body-parser": "1.20.6",
99
+ "js-yaml": "4.3.1"
100
+ },
97
101
  "bin": {
98
102
  "mindexec": "launch-bridge.cjs",
99
103
  "mind-bridge": "launch-bridge.cjs"
@@ -108,9 +112,9 @@
108
112
  "productName": "MindExec",
109
113
  "copyright": "Copyright © 2026 lovecrdm77",
110
114
  "asar": false,
111
- "extraMetadata": {
112
- "main": "electron/main.cjs",
113
- "desktopUpdateChannel": "dev"
115
+ "extraMetadata": {
116
+ "main": "electron/main.cjs",
117
+ "desktopUpdateChannel": "dev"
114
118
  },
115
119
  "directories": {
116
120
  "output": "../artifacts/electron"
@@ -119,7 +123,7 @@
119
123
  "*.js",
120
124
  "*.cjs",
121
125
  "*.json",
122
- "electron/**/*",
126
+ "electron/**/*",
123
127
  "remote-fast/**/*",
124
128
  "scripts/**/*",
125
129
  "tree-sitter-grammars/**/*",
@@ -144,8 +148,8 @@
144
148
  }
145
149
  ]
146
150
  },
147
- "nsis": {
148
- "artifactName": "MindExec-${version}-dev-${arch}.${ext}",
151
+ "nsis": {
152
+ "artifactName": "MindExec-${version}-dev-${arch}.${ext}",
149
153
  "oneClick": false,
150
154
  "perMachine": false,
151
155
  "allowToChangeInstallationDirectory": true,
@@ -153,12 +157,12 @@
153
157
  "createStartMenuShortcut": true,
154
158
  "shortcutName": "MindExec"
155
159
  },
156
- "portable": {
157
- "artifactName": "MindExec-Portable-${version}-${arch}.${ext}"
158
- },
159
- "publish": {
160
- "provider": "generic",
161
- "url": "https://mindexec.lovecrdm.workers.dev/dev/windows/x64"
162
- }
160
+ "portable": {
161
+ "artifactName": "MindExec-Portable-${version}-${arch}.${ext}"
162
+ },
163
+ "publish": {
164
+ "provider": "generic",
165
+ "url": "https://mindexec.lovecrdm.workers.dev/dev/windows/x64"
166
+ }
163
167
  }
164
168
  }
@@ -0,0 +1,85 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import vm from 'node:vm';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
8
+ const sourcePath = path.resolve(
9
+ scriptDir,
10
+ '..',
11
+ '..',
12
+ 'MindExecution.Shared',
13
+ 'wwwroot',
14
+ 'js',
15
+ 'mind-map-board-render-ownership.js');
16
+ const source = fs.readFileSync(sourcePath, 'utf8');
17
+ const traces = [];
18
+ const window = {
19
+ MindCanvasTrace: { emit: (type, data) => traces.push({ type, data }) },
20
+ MindCanvasBuildInfo: { registerPart: () => { } }
21
+ };
22
+
23
+ vm.runInNewContext(source, { window, Map, Object, Number, String, Math });
24
+
25
+ const makeMesh = (count = 8) => ({
26
+ visible: true,
27
+ geometry: {
28
+ instanceCount: count,
29
+ drawRange: { start: 0, count: count },
30
+ setDrawRange(start, nextCount) {
31
+ this.drawRange = { start, count: nextCount };
32
+ }
33
+ }
34
+ });
35
+ const renderer = {
36
+ resetCalls: 0,
37
+ resetState() { this.resetCalls++; },
38
+ pendingNodes: new Map([['stale-node', {}]]),
39
+ instancedHaloMesh: makeMesh(),
40
+ instancedMesh: makeMesh(),
41
+ instancedLineMesh: makeMesh(),
42
+ instancedBorderLineMesh: makeMesh(),
43
+ instancedConsoleMesh: makeMesh(),
44
+ instancedImageMesh: makeMesh(),
45
+ requestResidentPatch(reason) { this.patchReason = reason; }
46
+ };
47
+ const module = { _forceUpdateFrames: 0 };
48
+
49
+ assert.equal(window.MindMapBoardRenderOwnership.retireResidentBoard(renderer, module, {
50
+ fromBoardId: 'BOARD-B',
51
+ toBoardId: 'BOARD-A'
52
+ }), true);
53
+ assert.equal(renderer.resetCalls, 1);
54
+ assert.equal(renderer.pendingNodes.size, 0);
55
+ assert.equal(renderer._activeBoardRenderId, 'board-a');
56
+ assert.equal(renderer._residentBuilt, false);
57
+ assert.equal(renderer._residentBuildPhase, 'board-switch');
58
+ for (const mesh of [
59
+ renderer.instancedHaloMesh,
60
+ renderer.instancedMesh,
61
+ renderer.instancedLineMesh,
62
+ renderer.instancedBorderLineMesh,
63
+ renderer.instancedConsoleMesh,
64
+ renderer.instancedImageMesh
65
+ ]) {
66
+ assert.equal(mesh.visible, false);
67
+ assert.equal(mesh.geometry.instanceCount, 0);
68
+ }
69
+ assert.equal(renderer.instancedBorderLineMesh.geometry.drawRange.count, 0);
70
+ assert.equal(traces.at(-1)?.type, 'board.resident.retired');
71
+
72
+ const lineDataCache = new Map([['node-a', [1, 2, 3]]]);
73
+ assert.equal(window.MindMapBoardRenderOwnership.prepareCachedBoard(renderer, module, {
74
+ boardId: 'BOARD-A',
75
+ lineDataCache
76
+ }), true);
77
+ assert.equal(renderer.lineDataCache, lineDataCache);
78
+ assert.equal(renderer.patchReason, 'board-reload');
79
+
80
+ console.log(JSON.stringify({
81
+ ok: true,
82
+ retiredResidentCount: 0,
83
+ activeBoardRenderId: renderer._activeBoardRenderId,
84
+ patchReason: renderer.patchReason
85
+ }));
@@ -0,0 +1,58 @@
1
+ import assert from 'node:assert/strict';
2
+ import { promises as fs } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import {
6
+ buildCodexChildEnv,
7
+ ensureCodexRuntimeHome,
8
+ resolveCodexRuntimeHome
9
+ } from '../codex-runtime.js';
10
+
11
+ const previousCodexHome = process.env.CODEX_HOME;
12
+ const previousMindExecCodexHome = process.env.MINDEXEC_CODEX_HOME;
13
+ const sharedHome = await fs.mkdtemp(path.join(os.tmpdir(), 'mindexec-codex-shared-home-'));
14
+ const isolatedHome = await fs.mkdtemp(path.join(os.tmpdir(), 'mindexec-codex-isolated-home-'));
15
+
16
+ try {
17
+ process.env.CODEX_HOME = sharedHome;
18
+ delete process.env.MINDEXEC_CODEX_HOME;
19
+
20
+ assert.equal(resolveCodexRuntimeHome(), path.resolve(sharedHome));
21
+ assert.equal(await ensureCodexRuntimeHome(), path.resolve(sharedHome));
22
+ assert.equal(await fs.stat(path.join(sharedHome, 'sessions')).then(() => true), true);
23
+ assert.equal(await fs.stat(path.join(sharedHome, 'generated_images')).then(() => true), true);
24
+ assert.equal(await fs.stat(path.join(sharedHome, 'config.toml')).then(() => true).catch(() => false), false);
25
+
26
+ const sharedEnv = buildCodexChildEnv();
27
+ assert.equal(sharedEnv.CODEX_HOME, path.resolve(sharedHome));
28
+ assert.equal(sharedEnv.MINDEXEC_CODEX_ISOLATED_HOME, undefined);
29
+
30
+ process.env.MINDEXEC_CODEX_HOME = isolatedHome;
31
+ assert.equal(resolveCodexRuntimeHome(), path.resolve(isolatedHome));
32
+ assert.equal(await ensureCodexRuntimeHome(), path.resolve(isolatedHome));
33
+ const isolatedConfig = await fs.readFile(path.join(isolatedHome, 'config.toml'), 'utf8');
34
+ assert.match(isolatedConfig, /Generated by MindExec LocalBridge for isolated AI node runs/);
35
+
36
+ const isolatedEnv = buildCodexChildEnv();
37
+ assert.equal(isolatedEnv.CODEX_HOME, path.resolve(isolatedHome));
38
+ assert.equal(isolatedEnv.MINDEXEC_CODEX_ISOLATED_HOME, '1');
39
+
40
+ console.log(JSON.stringify({
41
+ ok: true,
42
+ sharedCodexLoginHome: true,
43
+ explicitIsolationOnly: true
44
+ }));
45
+ } finally {
46
+ if (previousCodexHome === undefined) {
47
+ delete process.env.CODEX_HOME;
48
+ } else {
49
+ process.env.CODEX_HOME = previousCodexHome;
50
+ }
51
+ if (previousMindExecCodexHome === undefined) {
52
+ delete process.env.MINDEXEC_CODEX_HOME;
53
+ } else {
54
+ process.env.MINDEXEC_CODEX_HOME = previousMindExecCodexHome;
55
+ }
56
+ await fs.rm(sharedHome, { recursive: true, force: true });
57
+ await fs.rm(isolatedHome, { recursive: true, force: true });
58
+ }
@@ -0,0 +1,43 @@
1
+ import assert from 'node:assert/strict';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { createCodexRuntime, resolveCodexRuntimeHome } from '../codex-runtime.js';
5
+
6
+ if (!process.argv.includes('--live')) {
7
+ console.log(JSON.stringify({ ok: true, liveImage: false }));
8
+ process.exit(0);
9
+ }
10
+
11
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
12
+ const packageRoot = path.resolve(scriptDir, '..');
13
+ const workspaceRoot = path.resolve(packageRoot, '..');
14
+ const runtime = createCodexRuntime({
15
+ packageRoot,
16
+ resolveWorkingDirectory: async () => workspaceRoot,
17
+ getCurrentRuntime: () => ({}),
18
+ getModelCatalog: async () => ({ models: [], source: 'live-smoke', updatedAt: new Date().toISOString() }),
19
+ log: () => {}
20
+ });
21
+
22
+ const result = await runtime.runImage({
23
+ prompt: 'Create a clean square test image with one solid blue circle centered on a white background. No text.',
24
+ size: '1024x1024',
25
+ quality: 'low'
26
+ });
27
+
28
+ assert.equal(result.success, true, result.error);
29
+ assert.equal(result.providerKind, 'TypeScriptSdk');
30
+ assert.equal(result.authenticationRequired, false);
31
+ assert.equal(result.loginOpened, false, 'The existing Codex login should be reused without opening sign-in.');
32
+ assert.ok(result.imageBase64.length > 1000, 'The live SDK image result was unexpectedly small.');
33
+ assert.match(result.mimeType, /^image\//);
34
+
35
+ console.log(JSON.stringify({
36
+ ok: true,
37
+ liveImage: true,
38
+ providerKind: result.providerKind,
39
+ sharedCodexHome: resolveCodexRuntimeHome(),
40
+ loginOpened: result.loginOpened,
41
+ mimeType: result.mimeType,
42
+ imageBytesApprox: Math.floor(result.imageBase64.length * 0.75)
43
+ }));
@@ -0,0 +1,122 @@
1
+ import assert from 'node:assert/strict';
2
+ import { promises as fs } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { createCodexRuntime } from '../codex-runtime.js';
7
+
8
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
9
+ const packageRoot = path.resolve(scriptDir, '..');
10
+ const workspaceRoot = path.resolve(packageRoot, '..');
11
+ const runtimeHome = await fs.mkdtemp(path.join(os.tmpdir(), 'mindexec-codex-sdk-image-'));
12
+ const tinyPngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jX8kAAAAASUVORK5CYII=';
13
+
14
+ function imageEvents() {
15
+ return (async function* generateEvents() {
16
+ yield { type: 'thread.started', thread_id: 'thread-image-smoke' };
17
+ yield {
18
+ type: 'event_msg',
19
+ payload: {
20
+ type: 'image_generation_end',
21
+ result: `data:image/png;base64,${tinyPngBase64}`
22
+ }
23
+ };
24
+ yield { type: 'turn.completed' };
25
+ })();
26
+ }
27
+
28
+ function createFakeSdk(runStreamed, captures) {
29
+ return {
30
+ Codex: class FakeCodex {
31
+ constructor(options) {
32
+ captures.codexOptions.push(options);
33
+ }
34
+
35
+ startThread(options) {
36
+ captures.threadOptions.push(options);
37
+ return {
38
+ runStreamed: async input => {
39
+ captures.inputs.push(input);
40
+ return { events: runStreamed() };
41
+ }
42
+ };
43
+ }
44
+ }
45
+ };
46
+ }
47
+
48
+ function createRuntime(fakeSdk, loginCodex) {
49
+ return createCodexRuntime({
50
+ packageRoot,
51
+ resolveWorkingDirectory: async () => workspaceRoot,
52
+ getCurrentRuntime: () => ({}),
53
+ getModelCatalog: async () => ({ models: [], source: 'smoke', updatedAt: new Date().toISOString() }),
54
+ loadCodexSdk: async () => fakeSdk,
55
+ ensureCodexRuntimeHome: async () => {
56
+ await fs.mkdir(path.join(runtimeHome, 'generated_images'), { recursive: true });
57
+ return runtimeHome;
58
+ },
59
+ buildCodexChildEnv: () => ({ CODEX_HOME: runtimeHome }),
60
+ loginCodex,
61
+ log: () => {}
62
+ });
63
+ }
64
+
65
+ try {
66
+ const captures = { codexOptions: [], threadOptions: [], inputs: [] };
67
+ const runtime = createRuntime(createFakeSdk(imageEvents, captures), async () => {
68
+ throw new Error('Login must not run for an authenticated image request.');
69
+ });
70
+ const generated = await runtime.runImage({
71
+ prompt: 'Create a blue product icon.',
72
+ model: 'gpt-5.5',
73
+ size: '1024x1024',
74
+ quality: 'high',
75
+ referenceImages: [{ base64: tinyPngBase64, mimeType: 'image/png', fileName: 'source.png' }]
76
+ });
77
+
78
+ assert.equal(generated.success, true, generated.error);
79
+ assert.equal(generated.providerKind, 'TypeScriptSdk');
80
+ assert.equal(generated.imageBase64, tinyPngBase64);
81
+ assert.equal(generated.mimeType, 'image/png');
82
+ assert.equal(captures.codexOptions[0].config.features.image_generation, true);
83
+ assert.deepEqual(captures.codexOptions[0].config.mcp_servers, {});
84
+ assert.equal(captures.threadOptions[0].sandboxMode, 'read-only');
85
+ assert.equal(captures.inputs[0][0].type, 'text');
86
+ assert.equal(captures.inputs[0][1].type, 'local_image');
87
+ assert.equal(await fs.stat(captures.inputs[0][1].path).then(() => true).catch(() => false), false);
88
+
89
+ let attempts = 0;
90
+ let loginCount = 0;
91
+ const authCaptures = { codexOptions: [], threadOptions: [], inputs: [] };
92
+ const authSdk = createFakeSdk(() => {
93
+ attempts += 1;
94
+ if (attempts === 1) {
95
+ return (async function* invalidAuth() {
96
+ throw new Error('401 Unauthorized: invalid_refresh_token. Please log out and sign in again.');
97
+ })();
98
+ }
99
+ return imageEvents();
100
+ }, authCaptures);
101
+ const authRuntime = createRuntime(authSdk, async () => {
102
+ loginCount += 1;
103
+ return { success: true, exitCode: 0 };
104
+ });
105
+ const retried = await authRuntime.runImage({ prompt: 'Create one test image.' });
106
+
107
+ assert.equal(loginCount, 1);
108
+ assert.equal(attempts, 2);
109
+ assert.equal(retried.success, true, retried.error);
110
+ assert.equal(retried.loginOpened, true);
111
+ assert.equal(retried.authenticationRequired, false);
112
+ assert.equal(retried.providerKind, 'TypeScriptSdk');
113
+
114
+ console.log(JSON.stringify({
115
+ ok: true,
116
+ providerKind: generated.providerKind,
117
+ referenceAttachment: true,
118
+ browserLoginRetry: true
119
+ }));
120
+ } finally {
121
+ await fs.rm(runtimeHome, { recursive: true, force: true });
122
+ }
package/server.js CHANGED
@@ -13022,6 +13022,23 @@ app.post('/api/codex/thread/run', async (req, res) => {
13022
13022
  }
13023
13023
  });
13024
13024
 
13025
+ app.post('/api/codex/image/run', async (req, res) => {
13026
+ try {
13027
+ res.json(await getCodexRuntime().runImage(req.body || {}));
13028
+ } catch (err) {
13029
+ console.error('[Codex/ImageRun] Error:', err);
13030
+ res.status(err.statusCode || 500).json({
13031
+ success: false,
13032
+ providerKind: 'Unavailable',
13033
+ imageBase64: '',
13034
+ mimeType: '',
13035
+ error: err?.message || String(err),
13036
+ authenticationRequired: false,
13037
+ loginOpened: false
13038
+ });
13039
+ }
13040
+ });
13041
+
13025
13042
  app.post('/api/codex/thread/resume', async (req, res) => {
13026
13043
  try {
13027
13044
  res.json(await getCodexRuntime().resumeThread(req.body || {}));
@@ -0,0 +1,81 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ const BUILD_ID = '20260819-board-render-owner-v1';
5
+
6
+ function normalizeBoardId(value) {
7
+ return String(value || '').trim().toLowerCase();
8
+ }
9
+
10
+ function clearResidentGeometry(mesh, useDrawRange = false) {
11
+ if (!mesh) return;
12
+
13
+ mesh.visible = false;
14
+ const geometry = mesh.geometry;
15
+ if (!geometry) return;
16
+
17
+ if ('instanceCount' in geometry) {
18
+ geometry.instanceCount = 0;
19
+ }
20
+ if (useDrawRange && typeof geometry.setDrawRange === 'function') {
21
+ geometry.setDrawRange(0, 0);
22
+ }
23
+ }
24
+
25
+ function retireResidentBoard(renderer, module, details = {}) {
26
+ if (!renderer) return false;
27
+
28
+ renderer.resetState?.();
29
+ renderer.pendingNodes?.clear?.();
30
+
31
+ clearResidentGeometry(renderer.instancedHaloMesh);
32
+ clearResidentGeometry(renderer.instancedMesh);
33
+ clearResidentGeometry(renderer.instancedLineMesh);
34
+ clearResidentGeometry(renderer.instancedBorderLineMesh, true);
35
+ clearResidentGeometry(renderer.instancedConsoleMesh);
36
+ clearResidentGeometry(renderer.instancedImageMesh);
37
+
38
+ renderer._activeBoardRenderId = normalizeBoardId(details.toBoardId);
39
+ renderer._boardRenderEpoch = Math.max(0, Number(renderer._boardRenderEpoch || 0)) + 1;
40
+ renderer._residentBuilt = false;
41
+ renderer._residentBuildPhase = 'board-switch';
42
+
43
+ if (module) {
44
+ module._forceUpdateFrames = Math.max(Number(module._forceUpdateFrames || 0), 2);
45
+ }
46
+
47
+ window.MindCanvasTrace?.emit?.('board.resident.retired', {
48
+ fromBoardId: details.fromBoardId || null,
49
+ toBoardId: details.toBoardId || null,
50
+ reason: details.reason || 'board-switch',
51
+ epoch: renderer._boardRenderEpoch
52
+ });
53
+ return true;
54
+ }
55
+
56
+ function prepareCachedBoard(renderer, module, details = {}) {
57
+ if (!renderer) return false;
58
+
59
+ const boardId = normalizeBoardId(details.boardId);
60
+ renderer._activeBoardRenderId = boardId;
61
+ if (details.lineDataCache instanceof Map) {
62
+ renderer.lineDataCache = details.lineDataCache;
63
+ }
64
+ renderer.requestResidentPatch?.('board-reload');
65
+
66
+ if (module) {
67
+ module._forceUpdateFrames = Math.max(Number(module._forceUpdateFrames || 0), 2);
68
+ }
69
+ return true;
70
+ }
71
+
72
+ window.MindMapBoardRenderOwnership = Object.freeze({
73
+ BUILD_ID,
74
+ normalizeBoardId,
75
+ retireResidentBoard,
76
+ prepareCachedBoard
77
+ });
78
+ window.MindCanvasBuildInfo?.registerPart?.('boardRenderOwnership', BUILD_ID, {
79
+ file: 'mind-map-board-render-ownership.js'
80
+ });
81
+ })();