@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.
@@ -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.437",
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.137.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
- requests.push({
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
- requests[requests.length - 1].body = body;
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
- const CODEX_MODEL_CATALOG_TTL_MS = 15 * 60 * 1000;
3172
- let cachedCodexConfig = null;
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
- const now = Date.now();
3311
- if (!options.forceRefresh && cachedCodexModelCatalog && cachedCodexModelCatalogExpiresAt > now) {
3312
- return cachedCodexModelCatalog;
3313
- }
3314
-
3315
- try {
3316
- const { stdout } = await execAsync('codex debug models', {
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
- const forceRefresh = ['1', 'true', 'yes', 'force'].includes(String(req.query.refresh || '').trim().toLowerCase());
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.json(await getCodexRuntime().getCapabilities());
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
- if (isCameraNavigationOnlyInteractiveFrame === true ||
6851
- this.isPanning === true ||
6852
- this.isZooming === true ||
6853
- isCameraMoving === true) {
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
- // 2. Handle Textarea (Note Input)
24598
- // Prefer existing textarea content if model is empty
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
- // Update textarea (note type) only when we have content
24606
- if (textarea && content && textarea.value !== content) {
24607
- textarea.value = content;
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 (isCsvTableNode) {
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
- // Unified Markdown Rendering with Caching
24633
- const lastContent = cssObject.userData.lastMarkdownContent;
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
- // ▲▲▲ [New] ▲▲▲
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 (didChange && window.MindMapCss3DManager?.syncCss3dContent) {
7680
+ if (window.MindMapCss3DManager?.syncCss3dContent) {
7680
7681
  window.MindMapCss3DManager.syncCss3dContent(nodeEntry.model, nodeEntry.cssObject);
7681
7682
  }
7682
7683
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-UWj0ijDRqn0AbF83B8IcEJMCKPtxWwQ27Ud2G/Zu03k=",
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.vbnlpkxcoo.dll": "MindExecution.Plugins.Concept.dll",
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.k3xu6iqktj.dll": "MindExecution.Plugins.PlanMaster.dll",
132
- "MindExecution.Plugins.YouTube.dqlboez3y6.dll": "MindExecution.Plugins.YouTube.dll",
133
- "MindExecution.Shared.6xss0iwsn1.dll": "MindExecution.Shared.dll",
134
- "MindExecution.Web.22pax8j6l6.dll": "MindExecution.Web.dll",
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.vbnlpkxcoo.dll": "sha256-pf2GETS/+rzZTxBpviTD2zwBY8qAF6NP0sv1WB/pFx8=",
283
- "MindExecution.Plugins.PlanMaster.k3xu6iqktj.dll": "sha256-EPH8PepplQ1KhRkxCphNWRxoNuI7TjKkJJKSs7Xg7nU=",
284
- "MindExecution.Shared.6xss0iwsn1.dll": "sha256-lva/KIyQLLikvw2qN3MdYJ4feScF038KSxVvAfFYhXA=",
285
- "MindExecution.Web.22pax8j6l6.dll": "sha256-Wi5LDm1s08JJMwVLzG2qB4SDNXbvDpdhsHn8o61JQoc="
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.dqlboez3y6.dll": "sha256-xUIofC6WDqpMhHa0TqKftVcU7NuZiTSMT25Hqy73U/k="
290
+ "MindExecution.Plugins.YouTube.xb9sou9ox0.dll": "sha256-Pf4IWNwd62loUPGvRnJ8ZgJ1X/h87tNDeBwS8ajlwyk="
291
291
  }
292
292
  },
293
293
  "cacheBootResources": true,
@@ -579,7 +579,7 @@
579
579
  }
580
580
 
581
581
  const base = '_content/MindExecution.Shared/js/';
582
- const scriptVersion = '20260713-normal-boundary-perf-v932';
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": "664AG46v",
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-EUZYGhkQlsm6gA4b0lgeJhCqmf+jX3HBJUuqdDmeMhU=",
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-KLJWPlXi2Zh9qNcSZCFtU6xgzc79UIPz2MPCVizCL28=",
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-nl8vYaz/QSNryxyzuaeEyQewlPRL25ERqb/vuZSxWrY=",
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-pf2GETS/+rzZTxBpviTD2zwBY8qAF6NP0sv1WB/pFx8=",
434
- "url": "_framework/MindExecution.Plugins.Concept.vbnlpkxcoo.dll"
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-EPH8PepplQ1KhRkxCphNWRxoNuI7TjKkJJKSs7Xg7nU=",
442
- "url": "_framework/MindExecution.Plugins.PlanMaster.k3xu6iqktj.dll"
441
+ "hash": "sha256-NDlttv/8WOvjy9hud+07v5ul3ZJhf2n/cf2CgMfhBnA=",
442
+ "url": "_framework/MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-xUIofC6WDqpMhHa0TqKftVcU7NuZiTSMT25Hqy73U/k=",
446
- "url": "_framework/MindExecution.Plugins.YouTube.dqlboez3y6.dll"
445
+ "hash": "sha256-Pf4IWNwd62loUPGvRnJ8ZgJ1X/h87tNDeBwS8ajlwyk=",
446
+ "url": "_framework/MindExecution.Plugins.YouTube.xb9sou9ox0.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-lva/KIyQLLikvw2qN3MdYJ4feScF038KSxVvAfFYhXA=",
450
- "url": "_framework/MindExecution.Shared.6xss0iwsn1.dll"
449
+ "hash": "sha256-3zUucQiJHN3pl4KBqsG0D0j054ojYss5D69/xCNTngY=",
450
+ "url": "_framework/MindExecution.Shared.pqab76cjb6.dll"
451
451
  },
452
452
  {
453
- "hash": "sha256-Wi5LDm1s08JJMwVLzG2qB4SDNXbvDpdhsHn8o61JQoc=",
454
- "url": "_framework/MindExecution.Web.22pax8j6l6.dll"
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-1byhFXCDdf3da557X7XlShV9C91WRkUAJ4hQ5n1TnGE=",
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-Wk8xAEvQkcT838xkX2NAoC2bypi1+S2OwW6plh/1V0k=",
833
+ "hash": "sha256-x3feeu4dFQjGF+xdT9qMumctaRJpYoC1pplyiuUZyzU=",
834
834
  "url": "index.html"
835
835
  },
836
836
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: 664AG46v */
1
+ /* Manifest version: +woAgnNO */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4