@mindexec/cli 0.2.437 → 0.2.439
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/codex-model-catalog.js +202 -0
- package/codex-runtime.js +5 -2
- package/package.json +6 -3
- package/scripts/codex-model-catalog-smoke.mjs +150 -0
- package/scripts/remote-registry-follower-smoke.mjs +4 -3
- package/server.js +35 -69
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +4 -12
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +24 -31
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +2 -1
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.vbnlpkxcoo.dll → MindExecution.Plugins.Concept.cvmglrxsjb.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.k3xu6iqktj.dll → MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.dqlboez3y6.dll → MindExecution.Plugins.YouTube.xb9sou9ox0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.6xss0iwsn1.dll → MindExecution.Shared.pqab76cjb6.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.22pax8j6l6.dll → MindExecution.Web.mc24u4bzol.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +11 -11
- package/wwwroot/index.html +1 -1
- package/wwwroot/service-worker-assets.js +16 -16
- package/wwwroot/service-worker.js +1 -1
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
const DEFAULT_CACHE_TTL_MS = 15 * 60 * 1000;
|
|
8
|
+
const DEFAULT_FAILURE_CACHE_TTL_MS = 60 * 1000;
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 20 * 1000;
|
|
10
|
+
const DEFAULT_MAX_BUFFER = 16 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
function getPathEnvironmentKey(env) {
|
|
13
|
+
return Object.keys(env).find(key => key.toLowerCase() === 'path') || 'PATH';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isPackageBinPath(value) {
|
|
17
|
+
const normalized = path.normalize(String(value || '').trim()).replace(/[\\/]+$/, '');
|
|
18
|
+
return /[\\/]node_modules[\\/]\.bin$/i.test(normalized);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The LocalBridge npm package carries a version-pinned Codex CLI through
|
|
23
|
+
* @openai/codex-sdk. That CLI is the execution runtime, but it must not shadow
|
|
24
|
+
* the user's newer installed Codex CLI when resolving the authoritative model
|
|
25
|
+
* catalog.
|
|
26
|
+
*/
|
|
27
|
+
export function buildExternalCodexCatalogEnv(sourceEnv = process.env) {
|
|
28
|
+
const env = { ...sourceEnv };
|
|
29
|
+
const pathKey = getPathEnvironmentKey(env);
|
|
30
|
+
const pathValue = String(env[pathKey] || '');
|
|
31
|
+
env[pathKey] = pathValue
|
|
32
|
+
.split(path.delimiter)
|
|
33
|
+
.map(item => item.trim())
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.filter(item => !isPackageBinPath(item))
|
|
36
|
+
.join(path.delimiter);
|
|
37
|
+
return env;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function normalizeCodexDebugModel(item) {
|
|
41
|
+
if (!item || typeof item !== 'object') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const id = String(item.slug || '').trim();
|
|
46
|
+
if (!id || String(item.visibility || '').toLowerCase() !== 'list') {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
displayName: String(item.display_name || id).trim(),
|
|
53
|
+
description: String(item.description || '').trim(),
|
|
54
|
+
priority: Number.isFinite(Number(item.priority)) ? Number(item.priority) : 999,
|
|
55
|
+
defaultReasoningLevel: String(item.default_reasoning_level || '').trim(),
|
|
56
|
+
supportedReasoningLevels: Array.isArray(item.supported_reasoning_levels)
|
|
57
|
+
? item.supported_reasoning_levels
|
|
58
|
+
.map(level => String(level?.effort || '').trim())
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
: [],
|
|
61
|
+
supportedInApi: Boolean(item.supported_in_api)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseCodexDebugModels(stdout) {
|
|
66
|
+
const parsed = JSON.parse(String(stdout || '{}'));
|
|
67
|
+
const models = Array.isArray(parsed.models)
|
|
68
|
+
? parsed.models
|
|
69
|
+
.map(normalizeCodexDebugModel)
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id))
|
|
72
|
+
: [];
|
|
73
|
+
|
|
74
|
+
if (models.length === 0) {
|
|
75
|
+
throw new Error('Codex live model catalog returned no list-visible models.');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return models;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function formatAttemptError(error) {
|
|
82
|
+
const message = String(error?.stderr || error?.message || error || 'unknown-error').trim();
|
|
83
|
+
return message.length > 1200 ? `${message.slice(0, 1200)}...` : message;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Contract:
|
|
88
|
+
* - `forceRefresh` always bypasses the completed-result TTL.
|
|
89
|
+
* - Concurrent refreshes share one authoritative CLI request sequence.
|
|
90
|
+
* - The user's external Codex CLI is tried before the SDK-pinned package CLI.
|
|
91
|
+
* - A failed refresh never replaces a previously verified non-empty catalog.
|
|
92
|
+
*/
|
|
93
|
+
export function createCodexModelCatalog(options = {}) {
|
|
94
|
+
const execute = options.execute || execAsync;
|
|
95
|
+
const sourceEnv = options.env || process.env;
|
|
96
|
+
const packageRoot = options.packageRoot || process.cwd();
|
|
97
|
+
const cacheTtlMs = Number(options.cacheTtlMs) > 0
|
|
98
|
+
? Number(options.cacheTtlMs)
|
|
99
|
+
: DEFAULT_CACHE_TTL_MS;
|
|
100
|
+
const failureCacheTtlMs = Number(options.failureCacheTtlMs) > 0
|
|
101
|
+
? Number(options.failureCacheTtlMs)
|
|
102
|
+
: DEFAULT_FAILURE_CACHE_TTL_MS;
|
|
103
|
+
const timeoutMs = Number(options.timeoutMs) > 0
|
|
104
|
+
? Number(options.timeoutMs)
|
|
105
|
+
: DEFAULT_TIMEOUT_MS;
|
|
106
|
+
const now = typeof options.now === 'function' ? options.now : Date.now;
|
|
107
|
+
|
|
108
|
+
let cachedCatalog = null;
|
|
109
|
+
let cachedCatalogExpiresAt = 0;
|
|
110
|
+
let refreshPromise = null;
|
|
111
|
+
|
|
112
|
+
async function refresh() {
|
|
113
|
+
const attemptedAtMs = now();
|
|
114
|
+
const attemptedAt = new Date(attemptedAtMs).toISOString();
|
|
115
|
+
const attempts = [
|
|
116
|
+
{
|
|
117
|
+
commandSource: 'external',
|
|
118
|
+
command: 'codex debug models',
|
|
119
|
+
env: buildExternalCodexCatalogEnv(sourceEnv)
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
commandSource: 'package-local',
|
|
123
|
+
// Older SDK-pinned CLIs may not understand a newer configured
|
|
124
|
+
// reasoning level. The catalog itself is independent of this
|
|
125
|
+
// preference, so use a broadly supported value for fallback.
|
|
126
|
+
command: 'codex debug models --config model_reasoning_effort=high',
|
|
127
|
+
env: { ...sourceEnv }
|
|
128
|
+
}
|
|
129
|
+
];
|
|
130
|
+
const attemptErrors = [];
|
|
131
|
+
|
|
132
|
+
for (const attempt of attempts) {
|
|
133
|
+
try {
|
|
134
|
+
const { stdout } = await execute(attempt.command, {
|
|
135
|
+
cwd: packageRoot,
|
|
136
|
+
env: attempt.env,
|
|
137
|
+
timeout: timeoutMs,
|
|
138
|
+
maxBuffer: DEFAULT_MAX_BUFFER,
|
|
139
|
+
windowsHide: true
|
|
140
|
+
});
|
|
141
|
+
const models = parseCodexDebugModels(stdout);
|
|
142
|
+
cachedCatalog = {
|
|
143
|
+
source: 'codex debug models',
|
|
144
|
+
commandSource: attempt.commandSource,
|
|
145
|
+
updatedAt: attemptedAt,
|
|
146
|
+
refreshAttemptedAt: attemptedAt,
|
|
147
|
+
stale: false,
|
|
148
|
+
error: null,
|
|
149
|
+
models
|
|
150
|
+
};
|
|
151
|
+
cachedCatalogExpiresAt = attemptedAtMs + cacheTtlMs;
|
|
152
|
+
return cachedCatalog;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
attemptErrors.push(`${attempt.commandSource}: ${formatAttemptError(error)}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const error = attemptErrors.join(' | ');
|
|
159
|
+
cachedCatalogExpiresAt = attemptedAtMs + failureCacheTtlMs;
|
|
160
|
+
if (cachedCatalog?.models?.length > 0) {
|
|
161
|
+
cachedCatalog = {
|
|
162
|
+
...cachedCatalog,
|
|
163
|
+
refreshAttemptedAt: attemptedAt,
|
|
164
|
+
stale: true,
|
|
165
|
+
error
|
|
166
|
+
};
|
|
167
|
+
return cachedCatalog;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
cachedCatalog = {
|
|
171
|
+
source: 'codex debug models',
|
|
172
|
+
commandSource: null,
|
|
173
|
+
updatedAt: null,
|
|
174
|
+
refreshAttemptedAt: attemptedAt,
|
|
175
|
+
stale: true,
|
|
176
|
+
error,
|
|
177
|
+
models: []
|
|
178
|
+
};
|
|
179
|
+
return cachedCatalog;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function getCatalog(requestOptions = {}) {
|
|
183
|
+
const forceRefresh = Boolean(requestOptions.forceRefresh);
|
|
184
|
+
if (!forceRefresh && cachedCatalog && cachedCatalogExpiresAt > now()) {
|
|
185
|
+
return cachedCatalog;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (refreshPromise) {
|
|
189
|
+
return refreshPromise;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
refreshPromise = refresh().finally(() => {
|
|
193
|
+
refreshPromise = null;
|
|
194
|
+
});
|
|
195
|
+
return refreshPromise;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
getCatalog,
|
|
200
|
+
peek: () => cachedCatalog
|
|
201
|
+
};
|
|
202
|
+
}
|
package/codex-runtime.js
CHANGED
|
@@ -558,11 +558,11 @@ export function createCodexRuntime(options) {
|
|
|
558
558
|
const threads = new Map();
|
|
559
559
|
const activeTurns = new Map();
|
|
560
560
|
|
|
561
|
-
async function getCapabilities() {
|
|
561
|
+
async function getCapabilities(options = {}) {
|
|
562
562
|
const [sdk, legacy, modelCatalog] = await Promise.all([
|
|
563
563
|
checkSdkAvailability(packageRoot),
|
|
564
564
|
checkLegacyAvailability(),
|
|
565
|
-
getModelCatalog()
|
|
565
|
+
getModelCatalog({ forceRefresh: Boolean(options.forceRefresh) })
|
|
566
566
|
]);
|
|
567
567
|
const preferredProviderKind = sdk.available
|
|
568
568
|
? PROVIDER_KIND.typeScriptSdk
|
|
@@ -595,7 +595,10 @@ export function createCodexRuntime(options) {
|
|
|
595
595
|
runtime: getCurrentRuntime(),
|
|
596
596
|
models: modelCatalog.models || [],
|
|
597
597
|
modelCatalogSource: modelCatalog.source,
|
|
598
|
+
modelCatalogCommandSource: modelCatalog.commandSource || null,
|
|
598
599
|
modelCatalogUpdatedAt: modelCatalog.updatedAt,
|
|
600
|
+
modelCatalogRefreshAttemptedAt: modelCatalog.refreshAttemptedAt || null,
|
|
601
|
+
modelCatalogStale: modelCatalog.stale === true,
|
|
599
602
|
modelCatalogError: modelCatalog.error || null
|
|
600
603
|
};
|
|
601
604
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindexec/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.439",
|
|
4
4
|
"description": "MindExec local runtime and bridge CLI",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"server.js",
|
|
12
12
|
"remote-hub.js",
|
|
13
13
|
"codex-runtime.js",
|
|
14
|
+
"codex-model-catalog.js",
|
|
14
15
|
"port-guard.cjs",
|
|
15
16
|
"remote-fast/",
|
|
16
17
|
"wwwroot/",
|
|
@@ -21,7 +22,9 @@
|
|
|
21
22
|
"scripts": {
|
|
22
23
|
"start": "node launch-bridge.cjs",
|
|
23
24
|
"dev": "node launch-bridge.cjs --watch",
|
|
24
|
-
"test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check launch-bridge.cjs && node --check port-guard.cjs && 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/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",
|
|
25
|
+
"test:syntax": "node --check server.js && node --check remote-hub.js && node --check codex-runtime.js && node --check codex-model-catalog.js && node --check launch-bridge.cjs && node --check port-guard.cjs && 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/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",
|
|
26
|
+
"test:codex-model-catalog": "node scripts/codex-model-catalog-smoke.mjs",
|
|
27
|
+
"test:codex-model-catalog:live": "node scripts/codex-model-catalog-smoke.mjs --live",
|
|
25
28
|
"test:auth": "node scripts/auth-session-smoke.mjs",
|
|
26
29
|
"test:remote": "node scripts/remote-hub-smoke.mjs",
|
|
27
30
|
"test:remote:scale": "node scripts/remote-hub-scale-smoke.mjs",
|
|
@@ -57,7 +60,7 @@
|
|
|
57
60
|
},
|
|
58
61
|
"dependencies": {
|
|
59
62
|
"@mindexec/remote": "^0.1.17",
|
|
60
|
-
"@openai/codex-sdk": "^0.
|
|
63
|
+
"@openai/codex-sdk": "^0.144.1",
|
|
61
64
|
"chokidar": "^3.6.0",
|
|
62
65
|
"cors": "^2.8.5",
|
|
63
66
|
"express": "^4.18.2",
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import {
|
|
5
|
+
buildExternalCodexCatalogEnv,
|
|
6
|
+
createCodexModelCatalog
|
|
7
|
+
} from '../codex-model-catalog.js';
|
|
8
|
+
|
|
9
|
+
function catalogJson(models) {
|
|
10
|
+
return JSON.stringify({ models });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function listed(slug, priority, displayName = slug) {
|
|
14
|
+
return {
|
|
15
|
+
slug,
|
|
16
|
+
display_name: displayName,
|
|
17
|
+
visibility: 'list',
|
|
18
|
+
priority,
|
|
19
|
+
supported_in_api: true,
|
|
20
|
+
supported_reasoning_levels: [{ effort: 'medium' }, { effort: 'high' }]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function testExternalPathWinsAndForceRefreshBypassesCache() {
|
|
25
|
+
const packageBin = path.join('C:', 'tmp', 'package', 'node_modules', '.bin');
|
|
26
|
+
const globalBin = path.join('C:', 'tools', 'codex');
|
|
27
|
+
const env = { PATH: [packageBin, globalBin].join(path.delimiter) };
|
|
28
|
+
const externalEnv = buildExternalCodexCatalogEnv(env);
|
|
29
|
+
assert.equal(externalEnv.PATH.includes('node_modules'), false);
|
|
30
|
+
assert.equal(externalEnv.PATH.includes(globalBin), true);
|
|
31
|
+
|
|
32
|
+
let calls = 0;
|
|
33
|
+
const execute = async (command, options) => {
|
|
34
|
+
calls += 1;
|
|
35
|
+
assert.equal(command, 'codex debug models');
|
|
36
|
+
assert.equal(String(options.env.PATH).includes('node_modules'), false);
|
|
37
|
+
const current = calls === 1 ? 'gpt-current-a' : 'gpt-current-b';
|
|
38
|
+
return {
|
|
39
|
+
stdout: catalogJson([
|
|
40
|
+
listed('hidden-model', 0),
|
|
41
|
+
listed(current, 1, current.toUpperCase()),
|
|
42
|
+
{ ...listed('not-listed', 2), visibility: 'hide' }
|
|
43
|
+
]),
|
|
44
|
+
stderr: ''
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const catalog = createCodexModelCatalog({ execute, env, packageRoot: process.cwd() });
|
|
48
|
+
|
|
49
|
+
const first = await catalog.getCatalog();
|
|
50
|
+
const cached = await catalog.getCatalog();
|
|
51
|
+
assert.equal(calls, 1);
|
|
52
|
+
assert.strictEqual(cached, first);
|
|
53
|
+
assert.deepEqual(first.models.map(model => model.id), ['hidden-model', 'gpt-current-a']);
|
|
54
|
+
assert.equal(first.commandSource, 'external');
|
|
55
|
+
assert.equal(first.stale, false);
|
|
56
|
+
|
|
57
|
+
const refreshed = await catalog.getCatalog({ forceRefresh: true });
|
|
58
|
+
assert.equal(calls, 2);
|
|
59
|
+
assert.deepEqual(refreshed.models.map(model => model.id), ['hidden-model', 'gpt-current-b']);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function testConcurrentRefreshIsSingleFlight() {
|
|
63
|
+
let calls = 0;
|
|
64
|
+
let release;
|
|
65
|
+
const gate = new Promise(resolve => {
|
|
66
|
+
release = resolve;
|
|
67
|
+
});
|
|
68
|
+
const catalog = createCodexModelCatalog({
|
|
69
|
+
execute: async () => {
|
|
70
|
+
calls += 1;
|
|
71
|
+
await gate;
|
|
72
|
+
return { stdout: catalogJson([listed('gpt-concurrent', 1)]), stderr: '' };
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const first = catalog.getCatalog({ forceRefresh: true });
|
|
77
|
+
const second = catalog.getCatalog({ forceRefresh: true });
|
|
78
|
+
await Promise.resolve();
|
|
79
|
+
assert.equal(calls, 1);
|
|
80
|
+
release();
|
|
81
|
+
const [firstResult, secondResult] = await Promise.all([first, second]);
|
|
82
|
+
assert.strictEqual(firstResult, secondResult);
|
|
83
|
+
assert.equal(calls, 1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function testPackageFallbackNormalizesNewerConfigPreference() {
|
|
87
|
+
const commands = [];
|
|
88
|
+
const catalog = createCodexModelCatalog({
|
|
89
|
+
execute: async command => {
|
|
90
|
+
commands.push(command);
|
|
91
|
+
if (commands.length === 1) {
|
|
92
|
+
throw new Error('external Codex CLI unavailable');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { stdout: catalogJson([listed('gpt-package-fallback', 1)]), stderr: '' };
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const result = await catalog.getCatalog({ forceRefresh: true });
|
|
100
|
+
assert.equal(commands.length, 2);
|
|
101
|
+
assert.match(commands[1], /model_reasoning_effort=high/);
|
|
102
|
+
assert.equal(result.commandSource, 'package-local');
|
|
103
|
+
assert.deepEqual(result.models.map(model => model.id), ['gpt-package-fallback']);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function testFailedRefreshPreservesLastVerifiedCatalog() {
|
|
107
|
+
let shouldFail = false;
|
|
108
|
+
const catalog = createCodexModelCatalog({
|
|
109
|
+
execute: async () => {
|
|
110
|
+
if (shouldFail) {
|
|
111
|
+
throw new Error('temporary catalog failure');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { stdout: catalogJson([listed('gpt-last-known-good', 1)]), stderr: '' };
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const verified = await catalog.getCatalog({ forceRefresh: true });
|
|
119
|
+
shouldFail = true;
|
|
120
|
+
const stale = await catalog.getCatalog({ forceRefresh: true });
|
|
121
|
+
assert.deepEqual(stale.models, verified.models);
|
|
122
|
+
assert.equal(stale.updatedAt, verified.updatedAt);
|
|
123
|
+
assert.equal(stale.stale, true);
|
|
124
|
+
assert.match(stale.error, /external:.*temporary catalog failure/);
|
|
125
|
+
assert.match(stale.error, /package-local:.*temporary catalog failure/);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function runDeterministicSmoke() {
|
|
129
|
+
await testExternalPathWinsAndForceRefreshBypassesCache();
|
|
130
|
+
await testConcurrentRefreshIsSingleFlight();
|
|
131
|
+
await testPackageFallbackNormalizesNewerConfigPreference();
|
|
132
|
+
await testFailedRefreshPreservesLastVerifiedCatalog();
|
|
133
|
+
console.log('[codex-model-catalog-smoke] deterministic catalog contracts passed');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function runLiveSmoke() {
|
|
137
|
+
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
138
|
+
const catalog = createCodexModelCatalog({ packageRoot: path.resolve(scriptDirectory, '..') });
|
|
139
|
+
const result = await catalog.getCatalog({ forceRefresh: true });
|
|
140
|
+
assert.ok(result.models.length > 0, result.error || 'expected a non-empty live Codex catalog');
|
|
141
|
+
assert.equal(result.commandSource, 'external');
|
|
142
|
+
assert.equal(result.stale, false);
|
|
143
|
+
console.log(`[codex-model-catalog-smoke] live ${result.source} (${result.commandSource}): ${result.models.map(model => model.id).join(', ')}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (process.argv.includes('--live')) {
|
|
147
|
+
await runLiveSmoke();
|
|
148
|
+
} else {
|
|
149
|
+
await runDeterministicSmoke();
|
|
150
|
+
}
|
|
@@ -106,12 +106,13 @@ function startFakeSupabase(getTarget, setTarget = null) {
|
|
|
106
106
|
req.on('end', () => resolve(body));
|
|
107
107
|
});
|
|
108
108
|
const parsed = new URL(req.url || '/', 'http://127.0.0.1');
|
|
109
|
-
|
|
109
|
+
const requestRecord = {
|
|
110
110
|
method: req.method,
|
|
111
111
|
pathname: parsed.pathname,
|
|
112
112
|
authorization: req.headers.authorization || '',
|
|
113
113
|
apikey: req.headers.apikey || ''
|
|
114
|
-
}
|
|
114
|
+
};
|
|
115
|
+
requests.push(requestRecord);
|
|
115
116
|
|
|
116
117
|
if (req.method === 'GET' && parsed.pathname === '/rest/v1/remote_host_targets') {
|
|
117
118
|
assertRegistryAuth(req);
|
|
@@ -127,7 +128,7 @@ function startFakeSupabase(getTarget, setTarget = null) {
|
|
|
127
128
|
if (req.method === 'POST' && parsed.pathname === '/rest/v1/rpc/set_remote_host_target') {
|
|
128
129
|
assertRegistryAuth(req);
|
|
129
130
|
const body = JSON.parse(await readBody() || '{}');
|
|
130
|
-
|
|
131
|
+
requestRecord.body = body;
|
|
131
132
|
const now = Date.now();
|
|
132
133
|
const existing = getTarget();
|
|
133
134
|
const sameLease = existing
|
package/server.js
CHANGED
|
@@ -22,6 +22,7 @@ import chokidar from 'chokidar';
|
|
|
22
22
|
import Parser from 'web-tree-sitter';
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import { createCodexRuntime } from './codex-runtime.js';
|
|
25
|
+
import { createCodexModelCatalog } from './codex-model-catalog.js';
|
|
25
26
|
import { createRemoteHub } from './remote-hub.js';
|
|
26
27
|
import portGuard from './port-guard.cjs';
|
|
27
28
|
|
|
@@ -3167,14 +3168,14 @@ function extractCodexOutputPath(command) {
|
|
|
3167
3168
|
return match[2] || match[3] || match[4] || null;
|
|
3168
3169
|
}
|
|
3169
3170
|
|
|
3170
|
-
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
3171
|
-
|
|
3172
|
-
let
|
|
3173
|
-
let cachedCodexConfigMtimeMs = -1;
|
|
3174
|
-
let cachedCodexModelCatalog = null;
|
|
3175
|
-
let cachedCodexModelCatalogExpiresAt = 0;
|
|
3171
|
+
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
3172
|
+
let cachedCodexConfig = null;
|
|
3173
|
+
let cachedCodexConfigMtimeMs = -1;
|
|
3176
3174
|
let lastCodexExecRuntime = null;
|
|
3177
3175
|
let codexRuntime = null;
|
|
3176
|
+
const codexModelCatalog = createCodexModelCatalog({
|
|
3177
|
+
packageRoot: BRIDGE_ROOT
|
|
3178
|
+
});
|
|
3178
3179
|
|
|
3179
3180
|
function readCodexConfig(options = {}) {
|
|
3180
3181
|
const forceRefresh = Boolean(options.forceRefresh);
|
|
@@ -3281,68 +3282,17 @@ function formatCodexRuntime(runtime) {
|
|
|
3281
3282
|
return parts.join(' ??');
|
|
3282
3283
|
}
|
|
3283
3284
|
|
|
3284
|
-
function normalizeCodexDebugModel(item) {
|
|
3285
|
-
if (!item || typeof item !== 'object') {
|
|
3286
|
-
return null;
|
|
3287
|
-
}
|
|
3288
|
-
|
|
3289
|
-
const id = String(item.slug || '').trim();
|
|
3290
|
-
if (!id || String(item.visibility || '').toLowerCase() !== 'list') {
|
|
3291
|
-
return null;
|
|
3292
|
-
}
|
|
3293
|
-
|
|
3294
|
-
return {
|
|
3295
|
-
id,
|
|
3296
|
-
displayName: String(item.display_name || id).trim(),
|
|
3297
|
-
description: String(item.description || '').trim(),
|
|
3298
|
-
priority: Number.isFinite(Number(item.priority)) ? Number(item.priority) : 999,
|
|
3299
|
-
defaultReasoningLevel: String(item.default_reasoning_level || '').trim(),
|
|
3300
|
-
supportedReasoningLevels: Array.isArray(item.supported_reasoning_levels)
|
|
3301
|
-
? item.supported_reasoning_levels
|
|
3302
|
-
.map(level => String(level?.effort || '').trim())
|
|
3303
|
-
.filter(Boolean)
|
|
3304
|
-
: [],
|
|
3305
|
-
supportedInApi: Boolean(item.supported_in_api)
|
|
3306
|
-
};
|
|
3307
|
-
}
|
|
3308
|
-
|
|
3309
3285
|
async function getCodexModelCatalog(options = {}) {
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
timeout: 10000,
|
|
3318
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
3319
|
-
windowsHide: true
|
|
3320
|
-
});
|
|
3321
|
-
const parsed = JSON.parse(String(stdout || '{}'));
|
|
3322
|
-
const models = Array.isArray(parsed.models)
|
|
3323
|
-
? parsed.models
|
|
3324
|
-
.map(normalizeCodexDebugModel)
|
|
3325
|
-
.filter(Boolean)
|
|
3326
|
-
.sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id))
|
|
3327
|
-
: [];
|
|
3328
|
-
|
|
3329
|
-
cachedCodexModelCatalog = {
|
|
3330
|
-
source: 'codex debug models',
|
|
3331
|
-
updatedAt: new Date().toISOString(),
|
|
3332
|
-
models
|
|
3333
|
-
};
|
|
3334
|
-
cachedCodexModelCatalogExpiresAt = now + CODEX_MODEL_CATALOG_TTL_MS;
|
|
3335
|
-
return cachedCodexModelCatalog;
|
|
3336
|
-
} catch (err) {
|
|
3337
|
-
cachedCodexModelCatalog = {
|
|
3338
|
-
source: 'codex debug models',
|
|
3339
|
-
updatedAt: new Date().toISOString(),
|
|
3340
|
-
error: err?.message || String(err),
|
|
3341
|
-
models: []
|
|
3342
|
-
};
|
|
3343
|
-
cachedCodexModelCatalogExpiresAt = now + Math.min(CODEX_MODEL_CATALOG_TTL_MS, 60 * 1000);
|
|
3344
|
-
return cachedCodexModelCatalog;
|
|
3286
|
+
return await codexModelCatalog.getCatalog(options);
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
function shouldForceCodexCatalogRefresh(value, defaultValue = true) {
|
|
3290
|
+
const normalized = String(value ?? '').trim().toLowerCase();
|
|
3291
|
+
if (!normalized) {
|
|
3292
|
+
return defaultValue;
|
|
3345
3293
|
}
|
|
3294
|
+
|
|
3295
|
+
return !['0', 'false', 'no', 'off', 'cache', 'cached'].includes(normalized);
|
|
3346
3296
|
}
|
|
3347
3297
|
|
|
3348
3298
|
function getCodexRuntime() {
|
|
@@ -12426,7 +12376,10 @@ app.get('/api/status', async (req, res) => {
|
|
|
12426
12376
|
res.setHeader('Pragma', 'no-cache');
|
|
12427
12377
|
res.setHeader('Expires', '0');
|
|
12428
12378
|
|
|
12429
|
-
|
|
12379
|
+
// `/api/status` is the LocalBridge connection handshake used by browser and
|
|
12380
|
+
// app clients. Refresh the authoritative Codex catalog for every handshake;
|
|
12381
|
+
// callers doing a non-connection diagnostic poll can opt into `refresh=cache`.
|
|
12382
|
+
const forceRefresh = shouldForceCodexCatalogRefresh(req.query.refresh, true);
|
|
12430
12383
|
const currentCodexRuntime = getCurrentCodexRuntime({
|
|
12431
12384
|
forceRefresh,
|
|
12432
12385
|
forceConfigRefresh: forceRefresh
|
|
@@ -12478,7 +12431,10 @@ app.get('/api/status', async (req, res) => {
|
|
|
12478
12431
|
preferredProviderKind: codexCapabilities.preferredProviderKind,
|
|
12479
12432
|
availableModels: codexModelCatalog.models,
|
|
12480
12433
|
modelCatalogSource: codexModelCatalog.source,
|
|
12434
|
+
modelCatalogCommandSource: codexModelCatalog.commandSource,
|
|
12481
12435
|
modelCatalogUpdatedAt: codexModelCatalog.updatedAt,
|
|
12436
|
+
modelCatalogRefreshAttemptedAt: codexModelCatalog.refreshAttemptedAt,
|
|
12437
|
+
modelCatalogStale: codexModelCatalog.stale === true,
|
|
12482
12438
|
modelCatalogError: codexModelCatalog.error
|
|
12483
12439
|
},
|
|
12484
12440
|
codex: {
|
|
@@ -12865,7 +12821,9 @@ app.post('/api/remote/devices/:deviceId/live/stop', (req, res) => {
|
|
|
12865
12821
|
|
|
12866
12822
|
app.get('/api/codex/capabilities', async (req, res) => {
|
|
12867
12823
|
try {
|
|
12868
|
-
res.
|
|
12824
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, max-age=0, must-revalidate');
|
|
12825
|
+
const forceRefresh = shouldForceCodexCatalogRefresh(req.query.refresh, true);
|
|
12826
|
+
res.json(await getCodexRuntime().getCapabilities({ forceRefresh }));
|
|
12869
12827
|
} catch (err) {
|
|
12870
12828
|
console.error('[Codex/Capabilities] Error:', err);
|
|
12871
12829
|
res.status(500).json({
|
|
@@ -14285,13 +14243,21 @@ async function startBridgeServer() {
|
|
|
14285
14243
|
logError('workspace', 'failed to initialize .mindexec layout.', layoutErr);
|
|
14286
14244
|
}
|
|
14287
14245
|
|
|
14288
|
-
const startupCodexRuntime = getCurrentCodexRuntime(
|
|
14246
|
+
const startupCodexRuntime = getCurrentCodexRuntime({
|
|
14247
|
+
forceRefresh: true,
|
|
14248
|
+
forceConfigRefresh: true
|
|
14249
|
+
});
|
|
14250
|
+
const startupCodexCatalog = await getCodexModelCatalog({ forceRefresh: true });
|
|
14251
|
+
if (startupCodexCatalog.error) {
|
|
14252
|
+
logWarn('codex', `model catalog refresh issue ${shortenText(startupCodexCatalog.error, 320)}`);
|
|
14253
|
+
}
|
|
14289
14254
|
const remoteHubStatus = remoteHub.getStatus({ includeSecrets: true });
|
|
14290
14255
|
logSection('MindExec Local Bridge', [
|
|
14291
14256
|
formatKeyValue('port', tone(PORT, 'accent')),
|
|
14292
14257
|
formatKeyValue('workspace', tone(normalizePathForClient(workspacePath), 'path')),
|
|
14293
14258
|
formatKeyValue('status', tone('ready', 'success')),
|
|
14294
14259
|
formatKeyValue('exec', tone(formatCodexRuntime(startupCodexRuntime), 'accent')),
|
|
14260
|
+
formatKeyValue('codexModels', startupCodexCatalog.models?.length || 0),
|
|
14295
14261
|
formatKeyValue('remote', remoteHubStatus.started
|
|
14296
14262
|
? tone(`tcp://${remoteHubStatus.host}:${remoteHubStatus.port}`, 'accent')
|
|
14297
14263
|
: tone(remoteHubStatus.enabled ? 'failed' : 'disabled', 'warn')),
|
|
@@ -6058,10 +6058,6 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
|
|
|
6058
6058
|
(panEndedThisFrame && isPanSmoothingActive !== true);
|
|
6059
6059
|
if (shouldFinalizeMotionThisFrame) {
|
|
6060
6060
|
this._lastMotionEndAt = frameStart;
|
|
6061
|
-
if (this._cursorDomDeferredForMotion === true) {
|
|
6062
|
-
updateCursorDomElement(this);
|
|
6063
|
-
this._cursorDomDeferredForMotion = false;
|
|
6064
|
-
}
|
|
6065
6061
|
if (zoomEndedThisFrame === true) {
|
|
6066
6062
|
const shouldReconcileLocalSnapGridAfterZoom = !!(
|
|
6067
6063
|
this._isLocalSnapGridThemeEnabled?.() === true &&
|
|
@@ -6847,14 +6843,10 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
|
|
|
6847
6843
|
|
|
6848
6844
|
// 5. Cursor DOM position
|
|
6849
6845
|
// ----------------------------------------------------------------
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
this._cursorDomDeferredForMotion = true;
|
|
6855
|
-
} else {
|
|
6856
|
-
updateCursorDomElement(this);
|
|
6857
|
-
}
|
|
6846
|
+
// The creation cursor is a world-space anchor. Project it from
|
|
6847
|
+
// the canonical camera on every rendered motion frame so it
|
|
6848
|
+
// travels with the board while pan/zoom lerp is settling.
|
|
6849
|
+
updateCursorDomElement(this);
|
|
6858
6850
|
|
|
6859
6851
|
// 6. Theme Animation
|
|
6860
6852
|
// ----------------------------------------------------------------
|
|
@@ -24229,7 +24229,6 @@
|
|
|
24229
24229
|
return css3dObj;
|
|
24230
24230
|
}
|
|
24231
24231
|
|
|
24232
|
-
// ▼▼▼ [New] Sync CSS3D DOM content from model ▼▼▼
|
|
24233
24232
|
function syncCss3dContent(nodeModel, cssObject) {
|
|
24234
24233
|
if (!nodeModel || !cssObject || !cssObject.element) return;
|
|
24235
24234
|
const nodeId = String(getNodeId(nodeModel) || '').trim();
|
|
@@ -24319,9 +24318,7 @@
|
|
|
24319
24318
|
restoreCss3dMediaOverlayState(nodeModel, cssObject);
|
|
24320
24319
|
}
|
|
24321
24320
|
let content = getPreferredCssMediaSource(nodeModel, contentTypeLower)
|
|
24322
|
-
|| nodeModel.response
|
|
24323
|
-
|| nodeModel.Response
|
|
24324
|
-
|| '';
|
|
24321
|
+
|| (nodeModel.response ?? nodeModel.Response ?? '');
|
|
24325
24322
|
const isLoading = !!(nodeModel.isLoading ?? nodeModel.IsLoading ?? false);
|
|
24326
24323
|
let hasContent = !!(`${content}`.trim());
|
|
24327
24324
|
const displayPrompt = getDisplayPrompt(nodeModel, prompt);
|
|
@@ -24594,23 +24591,26 @@
|
|
|
24594
24591
|
return;
|
|
24595
24592
|
}
|
|
24596
24593
|
|
|
24597
|
-
|
|
24598
|
-
|
|
24594
|
+
const responseEl = el.querySelector(`[id^="node-response-"]`) || el.querySelector('.node-response');
|
|
24595
|
+
const ownsInlineEditor = !!responseEl?.querySelector('.note-textarea, [id^="node-textarea-"]');
|
|
24596
|
+
const hasPendingMarkdownContent = Object.prototype.hasOwnProperty.call(cssObject.userData, 'pendingMarkdownContent');
|
|
24597
|
+
if (!ownsInlineEditor && hasPendingMarkdownContent) {
|
|
24598
|
+
content = String(cssObject.userData.pendingMarkdownContent ?? '');
|
|
24599
|
+
hasContent = !!content.trim();
|
|
24600
|
+
delete cssObject.userData.pendingMarkdownContent;
|
|
24601
|
+
}
|
|
24599
24602
|
const textarea = el.querySelector('textarea');
|
|
24600
|
-
if (!content && textarea?.value) {
|
|
24603
|
+
if (!ownsInlineEditor && !content && textarea?.value) {
|
|
24601
24604
|
content = textarea.value;
|
|
24602
24605
|
hasContent = !!(`${content}`.trim());
|
|
24603
24606
|
}
|
|
24604
|
-
|
|
24605
|
-
|
|
24606
|
-
|
|
24607
|
-
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
// 3. Handle Markdown/Text Output
|
|
24611
|
-
const responseEl = el.querySelector(`[id^="node-response-"]`) || el.querySelector('.node-response');
|
|
24607
|
+
|
|
24608
|
+
if (!ownsInlineEditor && textarea && content && textarea.value !== content) {
|
|
24609
|
+
textarea.value = content;
|
|
24610
|
+
}
|
|
24611
|
+
|
|
24612
24612
|
const existingResponseSource = String(responseEl?.dataset?.src || '');
|
|
24613
|
-
if (!content && existingResponseSource) {
|
|
24613
|
+
if (!ownsInlineEditor && !hasPendingMarkdownContent && !content && existingResponseSource) {
|
|
24614
24614
|
content = existingResponseSource;
|
|
24615
24615
|
hasContent = !!(`${content}`.trim());
|
|
24616
24616
|
}
|
|
@@ -24619,7 +24619,9 @@
|
|
|
24619
24619
|
const isCodeNode = contentTypeLower === 'code';
|
|
24620
24620
|
const isCsvTableNode = contentTypeLower === CSV_TABLE_CONTENT_TYPE;
|
|
24621
24621
|
|
|
24622
|
-
if (
|
|
24622
|
+
if (ownsInlineEditor) {
|
|
24623
|
+
cssObject.userData.pendingMarkdownContent = content;
|
|
24624
|
+
} else if (isCsvTableNode) {
|
|
24623
24625
|
if (!responseEl.classList.contains('csv-table-scroll')) {
|
|
24624
24626
|
responseEl.className = 'node-response csv-table-scroll';
|
|
24625
24627
|
}
|
|
@@ -24629,13 +24631,13 @@
|
|
|
24629
24631
|
} else if (isCodeNode) {
|
|
24630
24632
|
renderCodeBodyContent(responseEl, content);
|
|
24631
24633
|
} else {
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
// Preserve existing server-rendered HTML on first sync (avoid style shifts)
|
|
24634
|
+
const lastContent = cssObject.userData.lastMarkdownContent;
|
|
24635
|
+
|
|
24636
24636
|
if (lastContent === undefined && responseEl.innerHTML && content && existingResponseSource === content) {
|
|
24637
|
+
delete cssObject.userData.pendingMarkdownContent;
|
|
24637
24638
|
cssObject.userData.lastMarkdownContent = content;
|
|
24638
24639
|
} else if (lastContent !== content) {
|
|
24640
|
+
delete cssObject.userData.pendingMarkdownContent;
|
|
24639
24641
|
const cachedHtml = getCachedMarkdownHtml(nodeModel, content);
|
|
24640
24642
|
if (cachedHtml) {
|
|
24641
24643
|
if (responseEl.innerHTML !== cachedHtml) {
|
|
@@ -24662,22 +24664,13 @@
|
|
|
24662
24664
|
bindInlineEditableNodeCanvasNavigation(inlineEditableRoot, nodeModel, `${contentTypeLower}-inline-editable`);
|
|
24663
24665
|
}
|
|
24664
24666
|
|
|
24665
|
-
// Hide loading indicator if present
|
|
24666
24667
|
const loadingEl = el.querySelector(`[id^="node-loading-"]`);
|
|
24667
24668
|
if (loadingEl) {
|
|
24668
24669
|
renderNodeLoadingRow(loadingEl, nodeModel, contentTypeLower, isLoading, hasContent);
|
|
24669
24670
|
}
|
|
24670
24671
|
renderBusinessAutomationChrome(resolveBusinessAutomationChromeContainer(el), nodeModel);
|
|
24671
24672
|
}
|
|
24672
|
-
//
|
|
24673
|
-
|
|
24674
|
-
// ▲▲▲ [New] ▲▲▲
|
|
24675
|
-
|
|
24676
|
-
// ▼▼▼ [New] Update loop for synchronizing CSS3D positions with WebGL ▼▼▼
|
|
24677
|
-
// Fixes the issue where CSS3D nodes "jump" or detach from their Glow/WebGL counterparts
|
|
24678
|
-
// during deep zoom or camera movement due to matrix calculation timing discrepancies.
|
|
24679
|
-
|
|
24680
|
-
// Reusable objects for Frustum Culling (avoid GC)
|
|
24673
|
+
// Reusable objects for Frustum Culling (avoid GC)
|
|
24681
24674
|
const _frustum = new THREE.Frustum();
|
|
24682
24675
|
const _projScreenMatrix = new THREE.Matrix4();
|
|
24683
24676
|
const _worldPos = new THREE.Vector3();
|
|
@@ -7628,6 +7628,7 @@ window.MindMapInteractions = (function () {
|
|
|
7628
7628
|
if (didChange) {
|
|
7629
7629
|
// Update Model
|
|
7630
7630
|
nodeEntry.model.response = newValue;
|
|
7631
|
+
delete nodeEntry.cssObject?.userData?.pendingMarkdownContent;
|
|
7631
7632
|
// Update Texture Cache Info
|
|
7632
7633
|
if (window.MindMapNodes && window.MindMapNodes.pendingNodeUpdates) {
|
|
7633
7634
|
window.MindMapNodes.pendingNodeUpdates.set(nodeEntry.model.id, {
|
|
@@ -7676,7 +7677,7 @@ window.MindMapInteractions = (function () {
|
|
|
7676
7677
|
responseDiv.style.scrollbarGutter = previousResponseStyle.scrollbarGutter;
|
|
7677
7678
|
// ▲▲▲ [Fix] ▲▲▲
|
|
7678
7679
|
|
|
7679
|
-
if (
|
|
7680
|
+
if (window.MindMapCss3DManager?.syncCss3dContent) {
|
|
7680
7681
|
window.MindMapCss3DManager.syncCss3dContent(nodeEntry.model, nodeEntry.cssObject);
|
|
7681
7682
|
}
|
|
7682
7683
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/wwwroot/_framework/{MindExecution.Web.22pax8j6l6.dll → MindExecution.Web.mc24u4bzol.dll}
RENAMED
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mainAssemblyName": "MindExecution.Web",
|
|
3
3
|
"resources": {
|
|
4
|
-
"hash": "sha256-
|
|
4
|
+
"hash": "sha256-cyYstxxu9/Yn3zgm26aHsm55DzzVHsZnB734jEONmCw=",
|
|
5
5
|
"fingerprinting": {
|
|
6
6
|
"Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
|
|
7
7
|
"Markdig.d1j7v41cl1.dll": "Markdig.dll",
|
|
@@ -126,12 +126,12 @@
|
|
|
126
126
|
"MindExecution.Kernel.75hxk08tt0.dll": "MindExecution.Kernel.dll",
|
|
127
127
|
"MindExecution.Plugins.Admin.0zkmk8wahq.dll": "MindExecution.Plugins.Admin.dll",
|
|
128
128
|
"MindExecution.Plugins.Business.vyw3769hrw.dll": "MindExecution.Plugins.Business.dll",
|
|
129
|
-
"MindExecution.Plugins.Concept.
|
|
129
|
+
"MindExecution.Plugins.Concept.cvmglrxsjb.dll": "MindExecution.Plugins.Concept.dll",
|
|
130
130
|
"MindExecution.Plugins.Directory.jtw9q1dm9n.dll": "MindExecution.Plugins.Directory.dll",
|
|
131
|
-
"MindExecution.Plugins.PlanMaster.
|
|
132
|
-
"MindExecution.Plugins.YouTube.
|
|
133
|
-
"MindExecution.Shared.
|
|
134
|
-
"MindExecution.Web.
|
|
131
|
+
"MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll": "MindExecution.Plugins.PlanMaster.dll",
|
|
132
|
+
"MindExecution.Plugins.YouTube.xb9sou9ox0.dll": "MindExecution.Plugins.YouTube.dll",
|
|
133
|
+
"MindExecution.Shared.pqab76cjb6.dll": "MindExecution.Shared.dll",
|
|
134
|
+
"MindExecution.Web.mc24u4bzol.dll": "MindExecution.Web.dll",
|
|
135
135
|
"dotnet.native.566r55w3xu.js": "dotnet.native.js",
|
|
136
136
|
"dotnet.native.x6q5aixc38.wasm": "dotnet.native.wasm",
|
|
137
137
|
"dotnet.js": "dotnet.js",
|
|
@@ -279,15 +279,15 @@
|
|
|
279
279
|
"MindExecution.Core.808jj176e1.dll": "sha256-uMmP6rEgsbuwnsZ6Cnxrpmzio4usBtoJERfkOvz25iY=",
|
|
280
280
|
"MindExecution.Kernel.75hxk08tt0.dll": "sha256-9db04cSFp4z4iblswVb0UWgpjeluVqZUa7g27Oj3p40=",
|
|
281
281
|
"MindExecution.Plugins.Business.vyw3769hrw.dll": "sha256-WSc1w7WNOhnmzD3DcIgg4PCYzHeeU5rc7KK7eLgsu+M=",
|
|
282
|
-
"MindExecution.Plugins.Concept.
|
|
283
|
-
"MindExecution.Plugins.PlanMaster.
|
|
284
|
-
"MindExecution.Shared.
|
|
285
|
-
"MindExecution.Web.
|
|
282
|
+
"MindExecution.Plugins.Concept.cvmglrxsjb.dll": "sha256-yGhEvwMzCTsmRB6D80lOSfOUWCegs7b/GRvXEdIss5s=",
|
|
283
|
+
"MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll": "sha256-NDlttv/8WOvjy9hud+07v5ul3ZJhf2n/cf2CgMfhBnA=",
|
|
284
|
+
"MindExecution.Shared.pqab76cjb6.dll": "sha256-3zUucQiJHN3pl4KBqsG0D0j054ojYss5D69/xCNTngY=",
|
|
285
|
+
"MindExecution.Web.mc24u4bzol.dll": "sha256-5sJydtlQZKVkPUzMIYPO5q2iWWpstxPNVn6Kyk3fBrk="
|
|
286
286
|
},
|
|
287
287
|
"lazyAssembly": {
|
|
288
288
|
"MindExecution.Plugins.Admin.0zkmk8wahq.dll": "sha256-VLiwzLIr+tjKr6+yQfhMVQHq2Yx26QenAkC092+fEdw=",
|
|
289
289
|
"MindExecution.Plugins.Directory.jtw9q1dm9n.dll": "sha256-Z1x+H7sTj3w9XjWxfqGsd3Jv+qlzwQxAur6pJIP4l20=",
|
|
290
|
-
"MindExecution.Plugins.YouTube.
|
|
290
|
+
"MindExecution.Plugins.YouTube.xb9sou9ox0.dll": "sha256-Pf4IWNwd62loUPGvRnJ8ZgJ1X/h87tNDeBwS8ajlwyk="
|
|
291
291
|
}
|
|
292
292
|
},
|
|
293
293
|
"cacheBootResources": true,
|
package/wwwroot/index.html
CHANGED
|
@@ -579,7 +579,7 @@
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
const base = '_content/MindExecution.Shared/js/';
|
|
582
|
-
const scriptVersion = '20260713-
|
|
582
|
+
const scriptVersion = '20260713-ai-stream-edit-ownership-v934';
|
|
583
583
|
const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
|
|
584
584
|
console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
|
|
585
585
|
const criticalScripts = [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
self.assetsManifest = {
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "+woAgnNO",
|
|
3
3
|
"assets": [
|
|
4
4
|
{
|
|
5
5
|
"hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"url": "_content/MindExecution.Shared/js/marked.min.js"
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
|
-
"hash": "sha256-
|
|
81
|
+
"hash": "sha256-bBRq3Vtbchai8bpYEoThsNN2ONQiy3ktOmgeKe0U6lI=",
|
|
82
82
|
"url": "_content/MindExecution.Shared/js/mind-map-core.js"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
"hash": "sha256-
|
|
89
|
+
"hash": "sha256-dr+i09+KUFx+ddmSDqilzqJD0FDVye29n24Xz+YY1WU=",
|
|
90
90
|
"url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"url": "_content/MindExecution.Shared/js/mind-map-glow-shader.js"
|
|
107
107
|
},
|
|
108
108
|
{
|
|
109
|
-
"hash": "sha256-
|
|
109
|
+
"hash": "sha256-60RAF7CCMbT2wOwnimQMNs2QsRzd8m2Vnx9yeMLDG98=",
|
|
110
110
|
"url": "_content/MindExecution.Shared/js/mind-map-interactions.js"
|
|
111
111
|
},
|
|
112
112
|
{
|
|
@@ -430,28 +430,28 @@
|
|
|
430
430
|
"url": "_framework/MindExecution.Plugins.Business.vyw3769hrw.dll"
|
|
431
431
|
},
|
|
432
432
|
{
|
|
433
|
-
"hash": "sha256-
|
|
434
|
-
"url": "_framework/MindExecution.Plugins.Concept.
|
|
433
|
+
"hash": "sha256-yGhEvwMzCTsmRB6D80lOSfOUWCegs7b/GRvXEdIss5s=",
|
|
434
|
+
"url": "_framework/MindExecution.Plugins.Concept.cvmglrxsjb.dll"
|
|
435
435
|
},
|
|
436
436
|
{
|
|
437
437
|
"hash": "sha256-Z1x+H7sTj3w9XjWxfqGsd3Jv+qlzwQxAur6pJIP4l20=",
|
|
438
438
|
"url": "_framework/MindExecution.Plugins.Directory.jtw9q1dm9n.dll"
|
|
439
439
|
},
|
|
440
440
|
{
|
|
441
|
-
"hash": "sha256-
|
|
442
|
-
"url": "_framework/MindExecution.Plugins.PlanMaster.
|
|
441
|
+
"hash": "sha256-NDlttv/8WOvjy9hud+07v5ul3ZJhf2n/cf2CgMfhBnA=",
|
|
442
|
+
"url": "_framework/MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll"
|
|
443
443
|
},
|
|
444
444
|
{
|
|
445
|
-
"hash": "sha256-
|
|
446
|
-
"url": "_framework/MindExecution.Plugins.YouTube.
|
|
445
|
+
"hash": "sha256-Pf4IWNwd62loUPGvRnJ8ZgJ1X/h87tNDeBwS8ajlwyk=",
|
|
446
|
+
"url": "_framework/MindExecution.Plugins.YouTube.xb9sou9ox0.dll"
|
|
447
447
|
},
|
|
448
448
|
{
|
|
449
|
-
"hash": "sha256-
|
|
450
|
-
"url": "_framework/MindExecution.Shared.
|
|
449
|
+
"hash": "sha256-3zUucQiJHN3pl4KBqsG0D0j054ojYss5D69/xCNTngY=",
|
|
450
|
+
"url": "_framework/MindExecution.Shared.pqab76cjb6.dll"
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
|
-
"hash": "sha256-
|
|
454
|
-
"url": "_framework/MindExecution.Web.
|
|
453
|
+
"hash": "sha256-5sJydtlQZKVkPUzMIYPO5q2iWWpstxPNVn6Kyk3fBrk=",
|
|
454
|
+
"url": "_framework/MindExecution.Web.mc24u4bzol.dll"
|
|
455
455
|
},
|
|
456
456
|
{
|
|
457
457
|
"hash": "sha256-IsZJ91/OW+fHzNqIgEc7Y072ns8z9dGritiSyvR9Wgc=",
|
|
@@ -770,7 +770,7 @@
|
|
|
770
770
|
"url": "_framework/Websocket.Client.vapounvmnl.dll"
|
|
771
771
|
},
|
|
772
772
|
{
|
|
773
|
-
"hash": "sha256-
|
|
773
|
+
"hash": "sha256-BhRb0qMTAHsNxt77FQUjETyjbzdhjkbZN5P0GDBi7vU=",
|
|
774
774
|
"url": "_framework/blazor.boot.json"
|
|
775
775
|
},
|
|
776
776
|
{
|
|
@@ -830,7 +830,7 @@
|
|
|
830
830
|
"url": "icon-512.png"
|
|
831
831
|
},
|
|
832
832
|
{
|
|
833
|
-
"hash": "sha256-
|
|
833
|
+
"hash": "sha256-x3feeu4dFQjGF+xdT9qMumctaRJpYoC1pplyiuUZyzU=",
|
|
834
834
|
"url": "index.html"
|
|
835
835
|
},
|
|
836
836
|
{
|