@mindexec/cli 0.2.132 → 0.2.134
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/package.json +3 -2
- package/scripts/remote-fast-live-rate-smoke.mjs +2 -2
- package/scripts/remote-fast-mdm-browser-smoke.mjs +1 -1
- package/scripts/remote-registry-follower-smoke.mjs +322 -0
- package/server.js +598 -9
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +1 -1
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.lxunwcdjgq.dll → MindExecution.Plugins.Concept.5gltgdqyo3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.2sugb66h95.dll → MindExecution.Plugins.PlanMaster.cn6ppe6qoz.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.vy0nsbcoo4.dll → MindExecution.Plugins.YouTube.d7nnsxrftv.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.vki6dm13ef.dll → MindExecution.Shared.mnpwk5h86c.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.994sb3nzuy.dll → MindExecution.Web.ygql0sg6b3.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +11 -11
- package/wwwroot/index.html +3 -3
- package/wwwroot/service-worker-assets.js +14 -14
- package/wwwroot/service-worker.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindexec/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.134",
|
|
4
4
|
"description": "MindExec local runtime and bridge CLI",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node launch-bridge.cjs",
|
|
23
23
|
"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/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-agent-ws-smoke.mjs && node --check scripts/remote-agent-managed-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",
|
|
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/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-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
25
|
"test:auth": "node scripts/auth-session-smoke.mjs",
|
|
26
26
|
"test:remote": "node scripts/remote-hub-smoke.mjs",
|
|
27
27
|
"test:remote:scale": "node scripts/remote-hub-scale-smoke.mjs",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"test:remote:frame-ws": "node scripts/remote-frame-ws-smoke.mjs",
|
|
31
31
|
"test:remote:agent-ws": "node scripts/remote-agent-ws-smoke.mjs",
|
|
32
32
|
"test:remote:managed": "node scripts/remote-agent-managed-smoke.mjs",
|
|
33
|
+
"test:remote:registry": "node scripts/remote-registry-follower-smoke.mjs",
|
|
33
34
|
"test:remote:package": "node scripts/remote-agent-package-smoke.mjs",
|
|
34
35
|
"test:remote:live-rate": "node scripts/remote-fast-live-rate-smoke.mjs",
|
|
35
36
|
"test:remote:mdm-browser": "node scripts/remote-fast-mdm-browser-smoke.mjs",
|
|
@@ -13,9 +13,9 @@ const BRIDGE_TOKEN = 'remote-fast-live-rate-smoke-token';
|
|
|
13
13
|
const PAIR_TOKEN = 'remote-fast-live-rate-pair-token';
|
|
14
14
|
const LEASE_ID = 'remote-fast-live-rate-lease';
|
|
15
15
|
const LOCAL_BRIDGE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
16
|
-
const REQUESTED_FPS = Number(process.env.MINDEXEC_REMOTE_LIVE_RATE_REQUEST_FPS ||
|
|
16
|
+
const REQUESTED_FPS = Number(process.env.MINDEXEC_REMOTE_LIVE_RATE_REQUEST_FPS || 12);
|
|
17
17
|
const SAMPLE_MS = Number(process.env.MINDEXEC_REMOTE_LIVE_RATE_SAMPLE_MS || 3500);
|
|
18
|
-
const MIN_EFFECTIVE_FPS = Number(process.env.MINDEXEC_REMOTE_LIVE_RATE_MIN_FPS ||
|
|
18
|
+
const MIN_EFFECTIVE_FPS = Number(process.env.MINDEXEC_REMOTE_LIVE_RATE_MIN_FPS || 10);
|
|
19
19
|
|
|
20
20
|
function wait(ms) {
|
|
21
21
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -12,7 +12,7 @@ const BRIDGE_TOKEN = 'remote-fast-mdm-browser-smoke-token';
|
|
|
12
12
|
const PAIR_TOKEN = 'remote-fast-mdm-browser-pair-token';
|
|
13
13
|
const LEASE_ID = 'remote-fast-mdm-browser-lease';
|
|
14
14
|
const LOCAL_BRIDGE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
|
-
const REQUESTED_FPS = Number(process.env.MINDEXEC_REMOTE_MDM_BROWSER_REQUEST_FPS ||
|
|
15
|
+
const REQUESTED_FPS = Number(process.env.MINDEXEC_REMOTE_MDM_BROWSER_REQUEST_FPS || 12);
|
|
16
16
|
|
|
17
17
|
function wait(ms) {
|
|
18
18
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { createServer } from 'node:http';
|
|
7
|
+
import net from 'node:net';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const BRIDGE_TOKEN = 'remote-registry-follower-smoke-token';
|
|
13
|
+
const PAIR_TOKEN = 'remote-registry-follower-pair-token';
|
|
14
|
+
const USER_ID = '11111111-2222-4333-8444-555555555555';
|
|
15
|
+
const SUPABASE_KEY = 'remote-registry-follower-key';
|
|
16
|
+
const ACCESS_TOKEN = 'remote-registry-follower-access-token';
|
|
17
|
+
const LOCAL_BRIDGE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
18
|
+
|
|
19
|
+
function wait(ms) {
|
|
20
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function findFreePort() {
|
|
24
|
+
return await new Promise((resolve, reject) => {
|
|
25
|
+
const server = net.createServer();
|
|
26
|
+
server.unref();
|
|
27
|
+
server.once('error', reject);
|
|
28
|
+
server.listen(0, '127.0.0.1', () => {
|
|
29
|
+
const address = server.address();
|
|
30
|
+
const port = typeof address === 'object' && address ? address.port : 0;
|
|
31
|
+
server.close(() => resolve(port));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function fetchJson(url, options = {}) {
|
|
37
|
+
const response = await fetch(url, {
|
|
38
|
+
...options,
|
|
39
|
+
headers: {
|
|
40
|
+
...(options.body ? { 'Content-Type': 'application/json' } : {}),
|
|
41
|
+
'X-Bridge-Token': options.token || BRIDGE_TOKEN,
|
|
42
|
+
...(options.headers || {})
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
let payload = null;
|
|
46
|
+
try {
|
|
47
|
+
payload = await response.json();
|
|
48
|
+
} catch {
|
|
49
|
+
// Empty responses are allowed in failure diagnostics.
|
|
50
|
+
}
|
|
51
|
+
return { ok: response.ok, status: response.status, payload };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function waitFor(predicate, timeoutMs, label) {
|
|
55
|
+
const startedAt = Date.now();
|
|
56
|
+
let lastError = null;
|
|
57
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
58
|
+
try {
|
|
59
|
+
const value = await predicate();
|
|
60
|
+
if (value) {
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
lastError = error;
|
|
65
|
+
}
|
|
66
|
+
await wait(100);
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Timed out waiting for ${label}${lastError ? `: ${lastError.message}` : ''}.`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createRegistryTarget({ endpoint, endpointCandidates, leaseId, active = true }) {
|
|
72
|
+
return {
|
|
73
|
+
user_id: USER_ID,
|
|
74
|
+
active,
|
|
75
|
+
endpoint,
|
|
76
|
+
endpoint_candidates: endpointCandidates,
|
|
77
|
+
pair_token: PAIR_TOKEN,
|
|
78
|
+
lease_id: leaseId,
|
|
79
|
+
node_id: 'remote-registry-follower-node',
|
|
80
|
+
host_instance_id: `host-${leaseId}`,
|
|
81
|
+
expires_at: new Date(Date.now() + 60_000).toISOString()
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function startFakeSupabase(getTarget) {
|
|
86
|
+
const requests = [];
|
|
87
|
+
const server = createServer((req, res) => {
|
|
88
|
+
const parsed = new URL(req.url || '/', 'http://127.0.0.1');
|
|
89
|
+
requests.push({
|
|
90
|
+
method: req.method,
|
|
91
|
+
pathname: parsed.pathname,
|
|
92
|
+
authorization: req.headers.authorization || '',
|
|
93
|
+
apikey: req.headers.apikey || ''
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (req.method === 'GET' && parsed.pathname === '/rest/v1/remote_host_targets') {
|
|
97
|
+
assert.equal(String(req.headers.apikey || ''), SUPABASE_KEY);
|
|
98
|
+
assert.equal(String(req.headers.authorization || ''), `Bearer ${ACCESS_TOKEN}`);
|
|
99
|
+
res.writeHead(200, {
|
|
100
|
+
'Content-Type': 'application/json',
|
|
101
|
+
'Cache-Control': 'no-store'
|
|
102
|
+
});
|
|
103
|
+
const target = getTarget();
|
|
104
|
+
res.end(JSON.stringify(target ? [target] : []));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
109
|
+
res.end(JSON.stringify({ error: 'not-found' }));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
server.once('error', reject);
|
|
114
|
+
server.listen(0, '127.0.0.1', () => {
|
|
115
|
+
const address = server.address();
|
|
116
|
+
const port = typeof address === 'object' && address ? address.port : 0;
|
|
117
|
+
resolve({
|
|
118
|
+
url: `http://127.0.0.1:${port}`,
|
|
119
|
+
requests,
|
|
120
|
+
stop: () => new Promise(done => server.close(done))
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function spawnBridge({ bridgePort, remoteHubPort, workspacePath, authRoot, label, supabaseUrl = '', follower = false }) {
|
|
127
|
+
const child = spawn(process.execPath, ['server.js'], {
|
|
128
|
+
cwd: LOCAL_BRIDGE_DIR,
|
|
129
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
130
|
+
windowsHide: true,
|
|
131
|
+
env: {
|
|
132
|
+
...process.env,
|
|
133
|
+
BRIDGE_PORT: String(bridgePort),
|
|
134
|
+
BRIDGE_TOKEN,
|
|
135
|
+
BRIDGE_REQUIRE_TOKEN: '1',
|
|
136
|
+
MINDEXEC_REMOTE_HUB: '1',
|
|
137
|
+
REMOTE_HUB_HOST: '127.0.0.1',
|
|
138
|
+
REMOTE_HUB_PORT: String(remoteHubPort),
|
|
139
|
+
REMOTE_HUB_PAIR_TOKEN: PAIR_TOKEN,
|
|
140
|
+
WORKSPACE_PATH: workspacePath,
|
|
141
|
+
MINDEXEC_AUTH_DATA_ROOT: authRoot,
|
|
142
|
+
MINDEXEC_REMOTE_REGISTRY_FOLLOWER: follower ? '1' : '0',
|
|
143
|
+
MINDEXEC_REMOTE_REGISTRY_POLL_MS: '500',
|
|
144
|
+
MINDEXEC_REMOTE_REGISTRY_FAST_RETRY_MS: '250',
|
|
145
|
+
SUPABASE_URL: supabaseUrl,
|
|
146
|
+
SUPABASE_KEY,
|
|
147
|
+
NO_COLOR: '1'
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
let stdout = '';
|
|
152
|
+
let stderr = '';
|
|
153
|
+
child.stdout.on('data', chunk => {
|
|
154
|
+
stdout += chunk.toString();
|
|
155
|
+
});
|
|
156
|
+
child.stderr.on('data', chunk => {
|
|
157
|
+
stderr += chunk.toString();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const exitPromise = new Promise(resolve => child.once('exit', resolve));
|
|
161
|
+
const details = () => `${label} stdout=${stdout}\n${label} stderr=${stderr}`;
|
|
162
|
+
const stop = async () => {
|
|
163
|
+
if (child.exitCode === null && !child.killed) {
|
|
164
|
+
child.kill('SIGTERM');
|
|
165
|
+
await Promise.race([exitPromise, wait(5000)]);
|
|
166
|
+
if (child.exitCode === null && !child.killed) {
|
|
167
|
+
child.kill('SIGKILL');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
baseUrl: `http://127.0.0.1:${bridgePort}`,
|
|
174
|
+
child,
|
|
175
|
+
details,
|
|
176
|
+
stop
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function waitForBridge(bridge) {
|
|
181
|
+
return await waitFor(async () => {
|
|
182
|
+
const result = await fetchJson(`${bridge.baseUrl}/api/status`);
|
|
183
|
+
return result.ok && result.payload?.status === 'ok' ? result.payload : null;
|
|
184
|
+
}, 30000, `bridge startup\n${bridge.details()}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function writeSession(authRoot) {
|
|
188
|
+
await mkdir(authRoot, { recursive: true });
|
|
189
|
+
await writeFile(path.join(authRoot, 'supabase-session.json'), JSON.stringify({
|
|
190
|
+
access_token: ACCESS_TOKEN,
|
|
191
|
+
expires_at: Math.floor(Date.now() / 1000) + 3600,
|
|
192
|
+
user: {
|
|
193
|
+
id: USER_ID,
|
|
194
|
+
email: 'remote-registry-follower@example.test'
|
|
195
|
+
}
|
|
196
|
+
}), 'utf8');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function waitForManagedAgent(bridge, managerEndpoint, label) {
|
|
200
|
+
return await waitFor(async () => {
|
|
201
|
+
const result = await fetchJson(`${bridge.baseUrl}/api/remote/agent/status`);
|
|
202
|
+
const agent = result.payload;
|
|
203
|
+
return agent?.running === true
|
|
204
|
+
&& agent?.ready === true
|
|
205
|
+
&& String(agent.manager || '') === managerEndpoint
|
|
206
|
+
? agent
|
|
207
|
+
: null;
|
|
208
|
+
}, 20000, `${label} managed agent\n${bridge.details()}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function waitForConnectedDevice(bridge, label) {
|
|
212
|
+
return await waitFor(async () => {
|
|
213
|
+
const result = await fetchJson(`${bridge.baseUrl}/api/remote/devices`);
|
|
214
|
+
return result.payload?.devices?.find(device => device.connected === true) || null;
|
|
215
|
+
}, 12000, `${label} connected device\n${bridge.details()}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function main() {
|
|
219
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'mindexec-remote-registry-smoke-'));
|
|
220
|
+
let fakeSupabase = null;
|
|
221
|
+
let hostA = null;
|
|
222
|
+
let hostB = null;
|
|
223
|
+
let client = null;
|
|
224
|
+
|
|
225
|
+
const hostABridgePort = await findFreePort();
|
|
226
|
+
const hostARemotePort = await findFreePort();
|
|
227
|
+
const hostBBridgePort = await findFreePort();
|
|
228
|
+
const hostBRemotePort = await findFreePort();
|
|
229
|
+
const clientBridgePort = await findFreePort();
|
|
230
|
+
const clientRemotePort = await findFreePort();
|
|
231
|
+
const stalePort = await findFreePort();
|
|
232
|
+
const hostAEndpoint = `127.0.0.1:${hostARemotePort}`;
|
|
233
|
+
const hostBEndpoint = `127.0.0.1:${hostBRemotePort}`;
|
|
234
|
+
const staleEndpoint = `127.0.0.1:${stalePort}`;
|
|
235
|
+
let currentTarget = createRegistryTarget({
|
|
236
|
+
endpoint: hostAEndpoint,
|
|
237
|
+
endpointCandidates: [staleEndpoint, hostAEndpoint],
|
|
238
|
+
leaseId: 'lease-a'
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
fakeSupabase = await startFakeSupabase(() => currentTarget);
|
|
243
|
+
|
|
244
|
+
const hostAAuth = path.join(tempRoot, 'auth-host-a');
|
|
245
|
+
const hostBAuth = path.join(tempRoot, 'auth-host-b');
|
|
246
|
+
const clientAuth = path.join(tempRoot, 'auth-client');
|
|
247
|
+
await writeSession(clientAuth);
|
|
248
|
+
|
|
249
|
+
hostA = spawnBridge({
|
|
250
|
+
bridgePort: hostABridgePort,
|
|
251
|
+
remoteHubPort: hostARemotePort,
|
|
252
|
+
workspacePath: path.join(tempRoot, 'host-a'),
|
|
253
|
+
authRoot: hostAAuth,
|
|
254
|
+
label: 'host-a'
|
|
255
|
+
});
|
|
256
|
+
hostB = spawnBridge({
|
|
257
|
+
bridgePort: hostBBridgePort,
|
|
258
|
+
remoteHubPort: hostBRemotePort,
|
|
259
|
+
workspacePath: path.join(tempRoot, 'host-b'),
|
|
260
|
+
authRoot: hostBAuth,
|
|
261
|
+
label: 'host-b'
|
|
262
|
+
});
|
|
263
|
+
await waitForBridge(hostA);
|
|
264
|
+
await waitForBridge(hostB);
|
|
265
|
+
|
|
266
|
+
client = spawnBridge({
|
|
267
|
+
bridgePort: clientBridgePort,
|
|
268
|
+
remoteHubPort: clientRemotePort,
|
|
269
|
+
workspacePath: path.join(tempRoot, 'client'),
|
|
270
|
+
authRoot: clientAuth,
|
|
271
|
+
label: 'client',
|
|
272
|
+
supabaseUrl: fakeSupabase.url,
|
|
273
|
+
follower: true
|
|
274
|
+
});
|
|
275
|
+
await waitForBridge(client);
|
|
276
|
+
|
|
277
|
+
const firstAgent = await waitForManagedAgent(client, hostAEndpoint, 'host-a');
|
|
278
|
+
assert.equal(firstAgent.usingNpx, false, JSON.stringify(firstAgent));
|
|
279
|
+
assert.match(String(firstAgent.launcher || ''), /mindexec-remote-fast/i);
|
|
280
|
+
assert.equal(firstAgent.manager, hostAEndpoint);
|
|
281
|
+
assert.equal(firstAgent.managerCandidates?.[0], staleEndpoint, JSON.stringify(firstAgent.managerCandidates));
|
|
282
|
+
await waitForConnectedDevice(hostA, 'host-a');
|
|
283
|
+
|
|
284
|
+
currentTarget = createRegistryTarget({
|
|
285
|
+
endpoint: hostBEndpoint,
|
|
286
|
+
endpointCandidates: [hostBEndpoint],
|
|
287
|
+
leaseId: 'lease-b'
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const secondAgent = await waitForManagedAgent(client, hostBEndpoint, 'host-b');
|
|
291
|
+
assert.equal(secondAgent.usingNpx, false, JSON.stringify(secondAgent));
|
|
292
|
+
assert.match(String(secondAgent.launcher || ''), /mindexec-remote-fast/i);
|
|
293
|
+
assert.equal(secondAgent.manager, hostBEndpoint);
|
|
294
|
+
assert.equal(secondAgent.leaseId, 'lease-b');
|
|
295
|
+
await waitForConnectedDevice(hostB, 'host-b');
|
|
296
|
+
|
|
297
|
+
currentTarget = {
|
|
298
|
+
...currentTarget,
|
|
299
|
+
active: false,
|
|
300
|
+
expires_at: new Date(Date.now() - 1000).toISOString()
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
await waitFor(async () => {
|
|
304
|
+
const result = await fetchJson(`${client.baseUrl}/api/remote/agent/status`);
|
|
305
|
+
return result.payload?.running === false
|
|
306
|
+
&& String(result.payload?.lastError || '') === 'registry-inactive'
|
|
307
|
+
? result.payload
|
|
308
|
+
: null;
|
|
309
|
+
}, 15000, `registry inactive stop\n${client.details()}`);
|
|
310
|
+
|
|
311
|
+
assert.ok(fakeSupabase.requests.length >= 2, JSON.stringify(fakeSupabase.requests));
|
|
312
|
+
console.log('Remote registry follower smoke OK');
|
|
313
|
+
} finally {
|
|
314
|
+
if (client) await client.stop();
|
|
315
|
+
if (hostB) await hostB.stop();
|
|
316
|
+
if (hostA) await hostA.stop();
|
|
317
|
+
if (fakeSupabase) await fakeSupabase.stop();
|
|
318
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
await main();
|
package/server.js
CHANGED
|
@@ -41,7 +41,7 @@ const VERBOSE_CODEX_TRACE = /^(1|true|yes|on)$/i.test(String(process.env.BRIDGE_
|
|
|
41
41
|
const COLOR_LOGS_ENABLED = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
42
42
|
const DEFAULT_WEB_APP_ROOT = path.join(BRIDGE_ROOT, 'wwwroot');
|
|
43
43
|
|
|
44
|
-
const ANSI = {
|
|
44
|
+
const ANSI = {
|
|
45
45
|
reset: '\x1b[0m',
|
|
46
46
|
bold: '\x1b[1m',
|
|
47
47
|
dim: '\x1b[2m',
|
|
@@ -52,8 +52,10 @@ const ANSI = {
|
|
|
52
52
|
blue: '\x1b[34m',
|
|
53
53
|
magenta: '\x1b[35m',
|
|
54
54
|
cyan: '\x1b[36m',
|
|
55
|
-
white: '\x1b[37m'
|
|
56
|
-
};
|
|
55
|
+
white: '\x1b[37m'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let isShuttingDown = false;
|
|
57
59
|
|
|
58
60
|
function paint(text, ...styles) {
|
|
59
61
|
const value = String(text ?? '');
|
|
@@ -2042,6 +2044,7 @@ app.post('/api/auth/session', async (req, res) => {
|
|
|
2042
2044
|
try {
|
|
2043
2045
|
const content = req.body?.content;
|
|
2044
2046
|
await writeStableAuthSessionPayload(content);
|
|
2047
|
+
await wakeRemoteRegistryFollower('auth-session-saved');
|
|
2045
2048
|
res.json({
|
|
2046
2049
|
success: true
|
|
2047
2050
|
});
|
|
@@ -2058,6 +2061,7 @@ app.post('/api/auth/session', async (req, res) => {
|
|
|
2058
2061
|
app.delete('/api/auth/session', async (req, res) => {
|
|
2059
2062
|
try {
|
|
2060
2063
|
await deleteStableAuthSessionPayload();
|
|
2064
|
+
await wakeRemoteRegistryFollower('auth-session-cleared');
|
|
2061
2065
|
res.json({
|
|
2062
2066
|
success: true
|
|
2063
2067
|
});
|
|
@@ -2912,6 +2916,10 @@ const REMOTE_AGENT_MAX_PARALLEL_CANDIDATES = 6;
|
|
|
2912
2916
|
const REMOTE_AGENT_RECENT_MANAGER_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
2913
2917
|
const REMOTE_AGENT_RECENT_MANAGER_CACHE_LIMIT = 64;
|
|
2914
2918
|
const REMOTE_AGENT_RECENT_MANAGER_DEFAULT_KEY = 'default';
|
|
2919
|
+
const REMOTE_REGISTRY_FOLLOWER_ENABLED = !/^(0|false|no|off)$/i.test(String(process.env.MINDEXEC_REMOTE_REGISTRY_FOLLOWER || 'true'));
|
|
2920
|
+
const REMOTE_REGISTRY_FOLLOWER_POLL_MS = Math.max(1500, Number(process.env.MINDEXEC_REMOTE_REGISTRY_POLL_MS || 5000) || 5000);
|
|
2921
|
+
const REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS = Math.max(500, Number(process.env.MINDEXEC_REMOTE_REGISTRY_FAST_RETRY_MS || 1200) || 1200);
|
|
2922
|
+
const REMOTE_REGISTRY_FOLLOWER_MISSING_SESSION_STOP_COUNT = 3;
|
|
2915
2923
|
let remoteAgentState = createRemoteAgentIdleState();
|
|
2916
2924
|
let remoteAgentSyncReportState = null;
|
|
2917
2925
|
let remoteAgentSyncReportLogKey = '';
|
|
@@ -2920,6 +2928,23 @@ let remoteAgentConnectPromise = null;
|
|
|
2920
2928
|
const remoteAgentRecentSuccessfulManagers = new Map();
|
|
2921
2929
|
let remoteAgentRecentSuccessfulManagersLoaded = false;
|
|
2922
2930
|
let remoteAgentRecentSuccessfulManagersSavePromise = null;
|
|
2931
|
+
let remoteRegistryFollowerTimer = null;
|
|
2932
|
+
let remoteRegistryFollowerInFlight = false;
|
|
2933
|
+
let remoteRegistryFollowerStarted = false;
|
|
2934
|
+
let remoteRegistryFollowerConsecutiveMissingSession = 0;
|
|
2935
|
+
let remoteRegistryFollowerState = {
|
|
2936
|
+
enabled: REMOTE_REGISTRY_FOLLOWER_ENABLED,
|
|
2937
|
+
status: REMOTE_REGISTRY_FOLLOWER_ENABLED ? 'idle' : 'disabled',
|
|
2938
|
+
reason: '',
|
|
2939
|
+
lastAttemptAt: '',
|
|
2940
|
+
lastSuccessAt: '',
|
|
2941
|
+
lastError: '',
|
|
2942
|
+
targetEndpoint: '',
|
|
2943
|
+
targetEndpointCandidates: [],
|
|
2944
|
+
targetLeaseId: '',
|
|
2945
|
+
targetNodeId: '',
|
|
2946
|
+
authenticated: false
|
|
2947
|
+
};
|
|
2923
2948
|
|
|
2924
2949
|
function createRemoteAgentIdleState(overrides = {}) {
|
|
2925
2950
|
return {
|
|
@@ -4154,11 +4179,569 @@ async function startRemoteAgentConnectionAttempt(options = {}) {
|
|
|
4154
4179
|
return { ok: true, alreadyRunning: false, agent: serializeRemoteAgentState() };
|
|
4155
4180
|
}
|
|
4156
4181
|
|
|
4182
|
+
function serializeRemoteRegistryFollowerState() {
|
|
4183
|
+
return {
|
|
4184
|
+
...remoteRegistryFollowerState,
|
|
4185
|
+
pollMs: REMOTE_REGISTRY_FOLLOWER_POLL_MS,
|
|
4186
|
+
fastRetryMs: REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
function updateRemoteRegistryFollowerState(patch = {}) {
|
|
4191
|
+
remoteRegistryFollowerState = {
|
|
4192
|
+
...remoteRegistryFollowerState,
|
|
4193
|
+
...patch,
|
|
4194
|
+
enabled: REMOTE_REGISTRY_FOLLOWER_ENABLED
|
|
4195
|
+
};
|
|
4196
|
+
emitBridgeEvent('RemoteRegistryFollowerUpdated', serializeRemoteRegistryFollowerState());
|
|
4197
|
+
}
|
|
4198
|
+
|
|
4199
|
+
function readSupabaseRuntimeConfig() {
|
|
4200
|
+
const envUrl = String(process.env.SUPABASE_URL || process.env.MINDEXEC_SUPABASE_URL || '').trim();
|
|
4201
|
+
const envKey = String(
|
|
4202
|
+
process.env.SUPABASE_KEY
|
|
4203
|
+
|| process.env.SUPABASE_ANON_KEY
|
|
4204
|
+
|| process.env.SUPABASE_PUBLISHABLE_KEY
|
|
4205
|
+
|| process.env.MINDEXEC_SUPABASE_KEY
|
|
4206
|
+
|| '').trim();
|
|
4207
|
+
if (envUrl && envKey) {
|
|
4208
|
+
return { url: envUrl.replace(/\/+$/, ''), key: envKey };
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
const candidates = [
|
|
4212
|
+
path.join(WEB_APP_ROOT || '', 'appsettings.json'),
|
|
4213
|
+
path.join(DEFAULT_WEB_APP_ROOT, 'appsettings.json'),
|
|
4214
|
+
path.resolve(BRIDGE_ROOT, '..', 'MindExecution.Web', 'wwwroot', 'appsettings.json')
|
|
4215
|
+
].filter(Boolean);
|
|
4216
|
+
|
|
4217
|
+
for (const candidate of Array.from(new Set(candidates.map(item => path.resolve(item))))) {
|
|
4218
|
+
try {
|
|
4219
|
+
const payload = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
4220
|
+
const url = String(payload?.Supabase?.Url || '').trim().replace(/\/+$/, '');
|
|
4221
|
+
const key = String(payload?.Supabase?.Key || payload?.Supabase?.AnonKey || payload?.Supabase?.PublishableKey || '').trim();
|
|
4222
|
+
if (url && key) {
|
|
4223
|
+
return { url, key };
|
|
4224
|
+
}
|
|
4225
|
+
} catch {
|
|
4226
|
+
// Try the next appsettings candidate.
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
return { url: '', key: '' };
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
function readSupabaseSessionField(source, ...keys) {
|
|
4234
|
+
for (const key of keys) {
|
|
4235
|
+
const value = source?.[key];
|
|
4236
|
+
if (value !== undefined && value !== null && String(value).trim()) {
|
|
4237
|
+
return value;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
return '';
|
|
4241
|
+
}
|
|
4242
|
+
|
|
4243
|
+
function parseSupabaseSessionForRegistry(content) {
|
|
4244
|
+
let session = null;
|
|
4245
|
+
try {
|
|
4246
|
+
session = JSON.parse(String(content || ''));
|
|
4247
|
+
} catch {
|
|
4248
|
+
return null;
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
const user = session?.user || session?.User || {};
|
|
4252
|
+
const accessToken = String(readSupabaseSessionField(session, 'access_token', 'accessToken', 'AccessToken')).trim();
|
|
4253
|
+
const userId = String(
|
|
4254
|
+
readSupabaseSessionField(user, 'id', 'Id')
|
|
4255
|
+
|| readSupabaseSessionField(session, 'user_id', 'userId', 'UserId')).trim();
|
|
4256
|
+
const expiresAtRaw = Number(readSupabaseSessionField(session, 'expires_at', 'expiresAt', 'ExpiresAt') || 0);
|
|
4257
|
+
const expiresAtMs = Number.isFinite(expiresAtRaw) && expiresAtRaw > 0
|
|
4258
|
+
? (expiresAtRaw > 9999999999 ? expiresAtRaw : expiresAtRaw * 1000)
|
|
4259
|
+
: 0;
|
|
4260
|
+
|
|
4261
|
+
if (!accessToken || !userId) {
|
|
4262
|
+
return null;
|
|
4263
|
+
}
|
|
4264
|
+
|
|
4265
|
+
return {
|
|
4266
|
+
accessToken,
|
|
4267
|
+
userId,
|
|
4268
|
+
expiresAtMs
|
|
4269
|
+
};
|
|
4270
|
+
}
|
|
4271
|
+
|
|
4272
|
+
function readRegistryTargetField(target, ...keys) {
|
|
4273
|
+
for (const key of keys) {
|
|
4274
|
+
const value = target?.[key];
|
|
4275
|
+
if (value !== undefined && value !== null) {
|
|
4276
|
+
return value;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
return '';
|
|
4280
|
+
}
|
|
4281
|
+
|
|
4282
|
+
function parseRegistryEndpointCandidates(value) {
|
|
4283
|
+
if (Array.isArray(value)) {
|
|
4284
|
+
return value.map(item => String(item || '').trim()).filter(Boolean);
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
const text = String(value || '').trim();
|
|
4288
|
+
if (!text) {
|
|
4289
|
+
return [];
|
|
4290
|
+
}
|
|
4291
|
+
|
|
4292
|
+
try {
|
|
4293
|
+
const parsed = JSON.parse(text);
|
|
4294
|
+
if (Array.isArray(parsed)) {
|
|
4295
|
+
return parsed.map(item => String(item || '').trim()).filter(Boolean);
|
|
4296
|
+
}
|
|
4297
|
+
} catch {
|
|
4298
|
+
// Fall through to comma/newline split.
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
return text.split(/[,\n]/g).map(item => item.trim()).filter(Boolean);
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
function normalizeRemoteRegistryTarget(row) {
|
|
4305
|
+
if (!row || typeof row !== 'object') {
|
|
4306
|
+
return null;
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
const activeRaw = readRegistryTargetField(row, 'active', 'Active');
|
|
4310
|
+
const active = activeRaw === true || /^(1|true|yes|on)$/i.test(String(activeRaw || ''));
|
|
4311
|
+
const endpoint = String(readRegistryTargetField(row, 'endpoint', 'Endpoint') || '').trim();
|
|
4312
|
+
const endpointCandidates = normalizeRemoteManagerEndpointList(
|
|
4313
|
+
parseRegistryEndpointCandidates(readRegistryTargetField(row, 'endpoint_candidates', 'endpointCandidates', 'EndpointCandidates')),
|
|
4314
|
+
endpoint);
|
|
4315
|
+
const pairToken = String(readRegistryTargetField(row, 'pair_token', 'pairToken', 'PairToken') || '').trim();
|
|
4316
|
+
const leaseId = String(readRegistryTargetField(row, 'lease_id', 'leaseId', 'LeaseId') || '').trim();
|
|
4317
|
+
const nodeId = String(readRegistryTargetField(row, 'node_id', 'nodeId', 'NodeId') || '').trim();
|
|
4318
|
+
const hostInstanceId = String(readRegistryTargetField(row, 'host_instance_id', 'hostInstanceId', 'HostInstanceId') || '').trim();
|
|
4319
|
+
const expiresAt = String(readRegistryTargetField(row, 'expires_at', 'expiresAt', 'ExpiresAt') || '').trim();
|
|
4320
|
+
const expiresAtMs = Date.parse(expiresAt);
|
|
4321
|
+
|
|
4322
|
+
return {
|
|
4323
|
+
active,
|
|
4324
|
+
endpoint,
|
|
4325
|
+
endpointCandidates,
|
|
4326
|
+
pairToken,
|
|
4327
|
+
leaseId,
|
|
4328
|
+
nodeId,
|
|
4329
|
+
hostInstanceId,
|
|
4330
|
+
expiresAt,
|
|
4331
|
+
expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : 0
|
|
4332
|
+
};
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4335
|
+
function isRemoteRegistryTargetExpired(target) {
|
|
4336
|
+
return !target?.expiresAtMs || target.expiresAtMs <= Date.now();
|
|
4337
|
+
}
|
|
4338
|
+
|
|
4339
|
+
function isRemoteRegistryTargetSameAsLocalHost(localHub, target) {
|
|
4340
|
+
const localLeaseId = safeRemoteAgentField(localHub?.hostTargetLeaseId, 128);
|
|
4341
|
+
const localHostInstanceId = safeRemoteAgentField(localHub?.hostTargetHostInstanceId || localHub?.hostInstanceId, 128);
|
|
4342
|
+
return !!localLeaseId
|
|
4343
|
+
&& !!localHostInstanceId
|
|
4344
|
+
&& !!target?.leaseId
|
|
4345
|
+
&& !!target?.hostInstanceId
|
|
4346
|
+
&& localLeaseId.toLowerCase() === String(target.leaseId).toLowerCase()
|
|
4347
|
+
&& localHostInstanceId.toLowerCase() === String(target.hostInstanceId).toLowerCase();
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4350
|
+
async function fetchRemoteRegistryTarget(config, session) {
|
|
4351
|
+
const url = new URL('/rest/v1/remote_host_targets', config.url);
|
|
4352
|
+
url.searchParams.set('select', '*');
|
|
4353
|
+
url.searchParams.set('user_id', `eq.${session.userId}`);
|
|
4354
|
+
url.searchParams.set('limit', '1');
|
|
4355
|
+
|
|
4356
|
+
const response = await fetch(url.toString(), {
|
|
4357
|
+
method: 'GET',
|
|
4358
|
+
headers: {
|
|
4359
|
+
apikey: config.key,
|
|
4360
|
+
Authorization: `Bearer ${session.accessToken}`,
|
|
4361
|
+
Accept: 'application/json'
|
|
4362
|
+
}
|
|
4363
|
+
});
|
|
4364
|
+
|
|
4365
|
+
if (!response.ok) {
|
|
4366
|
+
const text = await response.text().catch(() => '');
|
|
4367
|
+
const error = new Error(`registry-rest-${response.status}${text ? `:${shortenText(text, 160)}` : ''}`);
|
|
4368
|
+
error.statusCode = response.status;
|
|
4369
|
+
throw error;
|
|
4370
|
+
}
|
|
4371
|
+
|
|
4372
|
+
const payload = await response.json();
|
|
4373
|
+
return normalizeRemoteRegistryTarget(Array.isArray(payload) ? payload[0] : payload);
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4376
|
+
async function reportRemoteRegistryFollowerSync(payload = {}) {
|
|
4377
|
+
const report = createRemoteAgentSyncReport(payload);
|
|
4378
|
+
rememberRemoteAgentSyncReport(report);
|
|
4379
|
+
return report;
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
function scheduleRemoteRegistryFollower(delayMs = REMOTE_REGISTRY_FOLLOWER_POLL_MS, reason = 'timer') {
|
|
4383
|
+
if (!REMOTE_REGISTRY_FOLLOWER_ENABLED || isShuttingDown) {
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
if (remoteRegistryFollowerTimer) {
|
|
4388
|
+
clearTimeout(remoteRegistryFollowerTimer);
|
|
4389
|
+
remoteRegistryFollowerTimer = null;
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
remoteRegistryFollowerTimer = setTimeout(() => {
|
|
4393
|
+
remoteRegistryFollowerTimer = null;
|
|
4394
|
+
runRemoteRegistryFollowerOnce(reason).catch(error => {
|
|
4395
|
+
logWarn('remote', `registry follower failed: ${error?.message || error}`);
|
|
4396
|
+
updateRemoteRegistryFollowerState({
|
|
4397
|
+
status: 'error',
|
|
4398
|
+
reason: 'follower-error',
|
|
4399
|
+
lastError: error?.message || String(error || ''),
|
|
4400
|
+
lastAttemptAt: new Date().toISOString()
|
|
4401
|
+
});
|
|
4402
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS, 'error-retry');
|
|
4403
|
+
});
|
|
4404
|
+
}, Math.max(0, Number(delayMs) || 0));
|
|
4405
|
+
remoteRegistryFollowerTimer?.unref?.();
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
async function wakeRemoteRegistryFollower(reason = 'wake') {
|
|
4409
|
+
if (!REMOTE_REGISTRY_FOLLOWER_ENABLED) {
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
4412
|
+
scheduleRemoteRegistryFollower(0, reason);
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4415
|
+
async function runRemoteRegistryFollowerOnce(trigger = 'timer') {
|
|
4416
|
+
if (!REMOTE_REGISTRY_FOLLOWER_ENABLED || remoteRegistryFollowerInFlight) {
|
|
4417
|
+
return serializeRemoteRegistryFollowerState();
|
|
4418
|
+
}
|
|
4419
|
+
|
|
4420
|
+
remoteRegistryFollowerInFlight = true;
|
|
4421
|
+
const attemptedAt = new Date().toISOString();
|
|
4422
|
+
try {
|
|
4423
|
+
const config = readSupabaseRuntimeConfig();
|
|
4424
|
+
if (!config.url || !config.key) {
|
|
4425
|
+
updateRemoteRegistryFollowerState({
|
|
4426
|
+
status: 'skipped',
|
|
4427
|
+
reason: 'supabase-config-missing',
|
|
4428
|
+
authenticated: false,
|
|
4429
|
+
lastAttemptAt: attemptedAt,
|
|
4430
|
+
lastError: ''
|
|
4431
|
+
});
|
|
4432
|
+
await reportRemoteRegistryFollowerSync({
|
|
4433
|
+
ok: false,
|
|
4434
|
+
skipped: true,
|
|
4435
|
+
reason: 'supabase-config-missing',
|
|
4436
|
+
trigger,
|
|
4437
|
+
authenticated: false
|
|
4438
|
+
});
|
|
4439
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'config-missing');
|
|
4440
|
+
return serializeRemoteRegistryFollowerState();
|
|
4441
|
+
}
|
|
4442
|
+
|
|
4443
|
+
const sessionPayload = await readStableAuthSessionPayload();
|
|
4444
|
+
const session = sessionPayload.found ? parseSupabaseSessionForRegistry(sessionPayload.content) : null;
|
|
4445
|
+
if (!session) {
|
|
4446
|
+
remoteRegistryFollowerConsecutiveMissingSession += 1;
|
|
4447
|
+
if (remoteRegistryFollowerConsecutiveMissingSession >= REMOTE_REGISTRY_FOLLOWER_MISSING_SESSION_STOP_COUNT
|
|
4448
|
+
&& isRemoteAgentProcessRunning()) {
|
|
4449
|
+
await stopRemoteAgentConnection('registry-not-authenticated');
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4452
|
+
updateRemoteRegistryFollowerState({
|
|
4453
|
+
status: 'skipped',
|
|
4454
|
+
reason: 'registry-not-authenticated',
|
|
4455
|
+
authenticated: false,
|
|
4456
|
+
lastAttemptAt: attemptedAt,
|
|
4457
|
+
lastError: '',
|
|
4458
|
+
targetEndpoint: '',
|
|
4459
|
+
targetEndpointCandidates: [],
|
|
4460
|
+
targetLeaseId: '',
|
|
4461
|
+
targetNodeId: ''
|
|
4462
|
+
});
|
|
4463
|
+
await reportRemoteRegistryFollowerSync({
|
|
4464
|
+
ok: true,
|
|
4465
|
+
skipped: true,
|
|
4466
|
+
reason: 'registry-not-authenticated',
|
|
4467
|
+
trigger,
|
|
4468
|
+
authenticated: false
|
|
4469
|
+
});
|
|
4470
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'auth-missing');
|
|
4471
|
+
return serializeRemoteRegistryFollowerState();
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
remoteRegistryFollowerConsecutiveMissingSession = 0;
|
|
4475
|
+
if (session.expiresAtMs && session.expiresAtMs <= Date.now()) {
|
|
4476
|
+
updateRemoteRegistryFollowerState({
|
|
4477
|
+
status: 'skipped',
|
|
4478
|
+
reason: 'session-expired',
|
|
4479
|
+
authenticated: false,
|
|
4480
|
+
lastAttemptAt: attemptedAt,
|
|
4481
|
+
lastError: ''
|
|
4482
|
+
});
|
|
4483
|
+
await reportRemoteRegistryFollowerSync({
|
|
4484
|
+
ok: true,
|
|
4485
|
+
skipped: true,
|
|
4486
|
+
reason: 'session-expired',
|
|
4487
|
+
trigger,
|
|
4488
|
+
authenticated: false
|
|
4489
|
+
});
|
|
4490
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'session-expired');
|
|
4491
|
+
return serializeRemoteRegistryFollowerState();
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4494
|
+
const localHub = remoteHub.getStatus({ includeSecrets: false });
|
|
4495
|
+
const target = await fetchRemoteRegistryTarget(config, session);
|
|
4496
|
+
|
|
4497
|
+
if (localHub?.hostTargetActive === true) {
|
|
4498
|
+
if (target?.active === true
|
|
4499
|
+
&& !isRemoteRegistryTargetExpired(target)
|
|
4500
|
+
&& !isRemoteRegistryTargetSameAsLocalHost(localHub, target)) {
|
|
4501
|
+
remoteHub.setHostTarget({
|
|
4502
|
+
enabled: false,
|
|
4503
|
+
nodeId: localHub.hostTargetNodeId
|
|
4504
|
+
});
|
|
4505
|
+
updateRemoteRegistryFollowerState({
|
|
4506
|
+
status: 'local-host-superseded',
|
|
4507
|
+
reason: 'local-host-superseded',
|
|
4508
|
+
authenticated: true,
|
|
4509
|
+
lastAttemptAt: attemptedAt,
|
|
4510
|
+
lastSuccessAt: attemptedAt,
|
|
4511
|
+
targetEndpoint: target.endpoint,
|
|
4512
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4513
|
+
targetLeaseId: target.leaseId,
|
|
4514
|
+
targetNodeId: target.nodeId,
|
|
4515
|
+
lastError: ''
|
|
4516
|
+
});
|
|
4517
|
+
await reportRemoteRegistryFollowerSync({
|
|
4518
|
+
ok: true,
|
|
4519
|
+
attempted: true,
|
|
4520
|
+
disconnected: true,
|
|
4521
|
+
reason: 'local-host-superseded',
|
|
4522
|
+
trigger,
|
|
4523
|
+
authenticated: true,
|
|
4524
|
+
targetActive: true,
|
|
4525
|
+
targetEndpoint: target.endpoint,
|
|
4526
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4527
|
+
targetLeaseId: target.leaseId,
|
|
4528
|
+
targetNodeId: target.nodeId,
|
|
4529
|
+
targetExpiresAt: target.expiresAt
|
|
4530
|
+
});
|
|
4531
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS, 'local-host-cleared');
|
|
4532
|
+
return serializeRemoteRegistryFollowerState();
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
if (isRemoteAgentProcessRunning()) {
|
|
4536
|
+
await stopRemoteAgentConnection('local-host-target-active');
|
|
4537
|
+
}
|
|
4538
|
+
updateRemoteRegistryFollowerState({
|
|
4539
|
+
status: 'skipped',
|
|
4540
|
+
reason: 'local-monitor-is-host',
|
|
4541
|
+
authenticated: true,
|
|
4542
|
+
lastAttemptAt: attemptedAt,
|
|
4543
|
+
lastSuccessAt: attemptedAt,
|
|
4544
|
+
lastError: ''
|
|
4545
|
+
});
|
|
4546
|
+
await reportRemoteRegistryFollowerSync({
|
|
4547
|
+
ok: true,
|
|
4548
|
+
skipped: true,
|
|
4549
|
+
reason: 'local-monitor-is-host',
|
|
4550
|
+
trigger,
|
|
4551
|
+
authenticated: true,
|
|
4552
|
+
localHostTargetActive: true,
|
|
4553
|
+
localHostTargetLeaseId: localHub.hostTargetLeaseId,
|
|
4554
|
+
localHostTargetNodeId: localHub.hostTargetNodeId
|
|
4555
|
+
});
|
|
4556
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'local-host');
|
|
4557
|
+
return serializeRemoteRegistryFollowerState();
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4560
|
+
if (!target?.active || isRemoteRegistryTargetExpired(target)) {
|
|
4561
|
+
if (isRemoteAgentProcessRunning()) {
|
|
4562
|
+
await stopRemoteAgentConnection('registry-inactive');
|
|
4563
|
+
}
|
|
4564
|
+
updateRemoteRegistryFollowerState({
|
|
4565
|
+
status: 'idle',
|
|
4566
|
+
reason: 'no-active-target',
|
|
4567
|
+
authenticated: true,
|
|
4568
|
+
lastAttemptAt: attemptedAt,
|
|
4569
|
+
lastSuccessAt: attemptedAt,
|
|
4570
|
+
targetEndpoint: '',
|
|
4571
|
+
targetEndpointCandidates: [],
|
|
4572
|
+
targetLeaseId: '',
|
|
4573
|
+
targetNodeId: '',
|
|
4574
|
+
lastError: ''
|
|
4575
|
+
});
|
|
4576
|
+
await reportRemoteRegistryFollowerSync({
|
|
4577
|
+
ok: true,
|
|
4578
|
+
skipped: true,
|
|
4579
|
+
reason: 'no-active-target',
|
|
4580
|
+
trigger,
|
|
4581
|
+
authenticated: true
|
|
4582
|
+
});
|
|
4583
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'no-target');
|
|
4584
|
+
return serializeRemoteRegistryFollowerState();
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4587
|
+
if (isRemoteRegistryTargetSameAsLocalHost(localHub, target)) {
|
|
4588
|
+
updateRemoteRegistryFollowerState({
|
|
4589
|
+
status: 'skipped',
|
|
4590
|
+
reason: 'same-host-lease',
|
|
4591
|
+
authenticated: true,
|
|
4592
|
+
lastAttemptAt: attemptedAt,
|
|
4593
|
+
lastSuccessAt: attemptedAt,
|
|
4594
|
+
targetEndpoint: target.endpoint,
|
|
4595
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4596
|
+
targetLeaseId: target.leaseId,
|
|
4597
|
+
targetNodeId: target.nodeId,
|
|
4598
|
+
lastError: ''
|
|
4599
|
+
});
|
|
4600
|
+
await reportRemoteRegistryFollowerSync({
|
|
4601
|
+
ok: true,
|
|
4602
|
+
skipped: true,
|
|
4603
|
+
reason: 'same-host-lease',
|
|
4604
|
+
trigger,
|
|
4605
|
+
authenticated: true,
|
|
4606
|
+
targetActive: true,
|
|
4607
|
+
targetEndpoint: target.endpoint,
|
|
4608
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4609
|
+
targetLeaseId: target.leaseId,
|
|
4610
|
+
targetNodeId: target.nodeId,
|
|
4611
|
+
targetExpiresAt: target.expiresAt
|
|
4612
|
+
});
|
|
4613
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'same-host');
|
|
4614
|
+
return serializeRemoteRegistryFollowerState();
|
|
4615
|
+
}
|
|
4616
|
+
|
|
4617
|
+
if (target.endpointCandidates.length === 0 || !target.pairToken) {
|
|
4618
|
+
updateRemoteRegistryFollowerState({
|
|
4619
|
+
status: 'skipped',
|
|
4620
|
+
reason: 'target-missing-endpoint-or-pair',
|
|
4621
|
+
authenticated: true,
|
|
4622
|
+
lastAttemptAt: attemptedAt,
|
|
4623
|
+
lastSuccessAt: attemptedAt,
|
|
4624
|
+
targetEndpoint: target.endpoint,
|
|
4625
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4626
|
+
targetLeaseId: target.leaseId,
|
|
4627
|
+
targetNodeId: target.nodeId,
|
|
4628
|
+
lastError: ''
|
|
4629
|
+
});
|
|
4630
|
+
await reportRemoteRegistryFollowerSync({
|
|
4631
|
+
ok: true,
|
|
4632
|
+
skipped: true,
|
|
4633
|
+
reason: 'target-missing-endpoint-or-pair',
|
|
4634
|
+
trigger,
|
|
4635
|
+
authenticated: true,
|
|
4636
|
+
targetActive: true,
|
|
4637
|
+
targetEndpoint: target.endpoint,
|
|
4638
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4639
|
+
targetLeaseId: target.leaseId,
|
|
4640
|
+
targetNodeId: target.nodeId,
|
|
4641
|
+
targetExpiresAt: target.expiresAt
|
|
4642
|
+
});
|
|
4643
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'target-incomplete');
|
|
4644
|
+
return serializeRemoteRegistryFollowerState();
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
const connect = await startRemoteAgentConnection({
|
|
4648
|
+
manager: target.endpointCandidates[0],
|
|
4649
|
+
managerCandidates: target.endpointCandidates,
|
|
4650
|
+
pairToken: target.pairToken,
|
|
4651
|
+
leaseId: target.leaseId,
|
|
4652
|
+
nodeId: target.nodeId,
|
|
4653
|
+
engine: 'auto',
|
|
4654
|
+
source: 'local-bridge-registry'
|
|
4655
|
+
});
|
|
4656
|
+
|
|
4657
|
+
const ok = connect?.ok === true;
|
|
4658
|
+
updateRemoteRegistryFollowerState({
|
|
4659
|
+
status: ok ? 'connected' : 'connect-failed',
|
|
4660
|
+
reason: ok ? (connect.alreadyRunning ? 'already-running' : 'connected') : 'connect-failed',
|
|
4661
|
+
authenticated: true,
|
|
4662
|
+
lastAttemptAt: attemptedAt,
|
|
4663
|
+
lastSuccessAt: ok ? new Date().toISOString() : remoteRegistryFollowerState.lastSuccessAt,
|
|
4664
|
+
targetEndpoint: target.endpointCandidates[0],
|
|
4665
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4666
|
+
targetLeaseId: target.leaseId,
|
|
4667
|
+
targetNodeId: target.nodeId,
|
|
4668
|
+
lastError: ok ? '' : (connect?.error || 'remote-agent-connect-failed')
|
|
4669
|
+
});
|
|
4670
|
+
await reportRemoteRegistryFollowerSync({
|
|
4671
|
+
ok,
|
|
4672
|
+
attempted: true,
|
|
4673
|
+
connected: ok,
|
|
4674
|
+
skipped: connect?.alreadyRunning === true,
|
|
4675
|
+
reason: ok ? (connect.alreadyRunning ? 'already-running' : 'connected') : 'connect-failed',
|
|
4676
|
+
error: ok ? '' : connect?.error,
|
|
4677
|
+
trigger,
|
|
4678
|
+
authenticated: true,
|
|
4679
|
+
targetActive: true,
|
|
4680
|
+
targetEndpoint: target.endpointCandidates[0],
|
|
4681
|
+
targetEndpointCandidates: target.endpointCandidates,
|
|
4682
|
+
targetLeaseId: target.leaseId,
|
|
4683
|
+
targetNodeId: target.nodeId,
|
|
4684
|
+
targetExpiresAt: target.expiresAt
|
|
4685
|
+
});
|
|
4686
|
+
|
|
4687
|
+
scheduleRemoteRegistryFollower(ok ? REMOTE_REGISTRY_FOLLOWER_POLL_MS : REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS, ok ? 'connected' : 'connect-failed');
|
|
4688
|
+
return serializeRemoteRegistryFollowerState();
|
|
4689
|
+
} catch (error) {
|
|
4690
|
+
const transientAuth = error?.statusCode === 401 || error?.statusCode === 403;
|
|
4691
|
+
updateRemoteRegistryFollowerState({
|
|
4692
|
+
status: transientAuth ? 'auth-pending' : 'error',
|
|
4693
|
+
reason: transientAuth ? 'registry-auth-pending' : 'registry-sync-failed',
|
|
4694
|
+
authenticated: false,
|
|
4695
|
+
lastAttemptAt: attemptedAt,
|
|
4696
|
+
lastError: error?.message || String(error || '')
|
|
4697
|
+
});
|
|
4698
|
+
await reportRemoteRegistryFollowerSync({
|
|
4699
|
+
ok: isRemoteAgentProcessRunning(),
|
|
4700
|
+
skipped: true,
|
|
4701
|
+
reason: transientAuth ? 'registry-auth-pending-agent-kept' : 'registry-sync-failed',
|
|
4702
|
+
error: error?.message || String(error || ''),
|
|
4703
|
+
trigger,
|
|
4704
|
+
authenticated: false
|
|
4705
|
+
});
|
|
4706
|
+
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS, transientAuth ? 'auth-pending' : 'sync-error');
|
|
4707
|
+
return serializeRemoteRegistryFollowerState();
|
|
4708
|
+
} finally {
|
|
4709
|
+
remoteRegistryFollowerInFlight = false;
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
|
|
4713
|
+
function startRemoteRegistryFollower() {
|
|
4714
|
+
if (!REMOTE_REGISTRY_FOLLOWER_ENABLED || remoteRegistryFollowerStarted) {
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4718
|
+
remoteRegistryFollowerStarted = true;
|
|
4719
|
+
updateRemoteRegistryFollowerState({
|
|
4720
|
+
status: 'starting',
|
|
4721
|
+
reason: 'startup',
|
|
4722
|
+
lastAttemptAt: '',
|
|
4723
|
+
lastError: ''
|
|
4724
|
+
});
|
|
4725
|
+
scheduleRemoteRegistryFollower(250, 'startup');
|
|
4726
|
+
}
|
|
4727
|
+
|
|
4728
|
+
function stopRemoteRegistryFollower() {
|
|
4729
|
+
if (remoteRegistryFollowerTimer) {
|
|
4730
|
+
clearTimeout(remoteRegistryFollowerTimer);
|
|
4731
|
+
remoteRegistryFollowerTimer = null;
|
|
4732
|
+
}
|
|
4733
|
+
remoteRegistryFollowerStarted = false;
|
|
4734
|
+
updateRemoteRegistryFollowerState({
|
|
4735
|
+
status: REMOTE_REGISTRY_FOLLOWER_ENABLED ? 'stopped' : 'disabled',
|
|
4736
|
+
reason: 'bridge-shutdown'
|
|
4737
|
+
});
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4157
4740
|
function trimShellOutput(value, maxLength) {
|
|
4158
4741
|
const text = String(value || '');
|
|
4159
4742
|
if (text.length <= maxLength) {
|
|
4160
|
-
return text;
|
|
4161
|
-
}
|
|
4743
|
+
return text;
|
|
4744
|
+
}
|
|
4162
4745
|
|
|
4163
4746
|
return text.slice(text.length - maxLength);
|
|
4164
4747
|
}
|
|
@@ -8610,6 +9193,7 @@ app.get('/api/status', async (req, res) => {
|
|
|
8610
9193
|
bridgeAuthRequired,
|
|
8611
9194
|
remoteHub: remoteHub.getStatus({ includeSecrets: false }),
|
|
8612
9195
|
remoteAgent: serializeRemoteAgentState(),
|
|
9196
|
+
remoteRegistryFollower: serializeRemoteRegistryFollowerState(),
|
|
8613
9197
|
shellJobsPath: '/api/shell/jobs',
|
|
8614
9198
|
companyCore: {
|
|
8615
9199
|
baseUrl: companyCoreBaseUrl,
|
|
@@ -10400,15 +10984,14 @@ async function startBridgeServer() {
|
|
|
10400
10984
|
`${tone('listen', 'muted')} ${tone(`http://127.0.0.1:${PORT}`, 'path')}`,
|
|
10401
10985
|
`${tone('stop', 'muted')} Ctrl+C`
|
|
10402
10986
|
]);
|
|
10987
|
+
startRemoteRegistryFollower();
|
|
10403
10988
|
});
|
|
10404
10989
|
}
|
|
10405
10990
|
|
|
10406
10991
|
await startBridgeServer();
|
|
10407
10992
|
|
|
10408
|
-
// Graceful shutdown
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
async function shutdownBridge(signal) {
|
|
10993
|
+
// Graceful shutdown
|
|
10994
|
+
async function shutdownBridge(signal) {
|
|
10412
10995
|
if (isShuttingDown) return;
|
|
10413
10996
|
isShuttingDown = true;
|
|
10414
10997
|
|
|
@@ -10435,6 +11018,12 @@ async function shutdownBridge(signal) {
|
|
|
10435
11018
|
// Ignore if already closed
|
|
10436
11019
|
}
|
|
10437
11020
|
|
|
11021
|
+
try {
|
|
11022
|
+
stopRemoteRegistryFollower();
|
|
11023
|
+
} catch {
|
|
11024
|
+
// Ignore registry follower shutdown errors
|
|
11025
|
+
}
|
|
11026
|
+
|
|
10438
11027
|
try {
|
|
10439
11028
|
await stopRemoteAgentConnection('bridge-shutdown');
|
|
10440
11029
|
} catch {
|
|
@@ -13451,7 +13451,7 @@
|
|
|
13451
13451
|
const REMOTE_FLEET_MONITOR_REFRESH_MS = 10000;
|
|
13452
13452
|
const REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS = 2500;
|
|
13453
13453
|
const REMOTE_FLEET_LIVE_REFRESH_MS = 30000;
|
|
13454
|
-
const REMOTE_FLEET_MONITOR_LIVE_FPS =
|
|
13454
|
+
const REMOTE_FLEET_MONITOR_LIVE_FPS = 12;
|
|
13455
13455
|
const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_MONITOR_LIVE_FPS);
|
|
13456
13456
|
const REMOTE_FLEET_CONTROL_LIVE_FPS = 20;
|
|
13457
13457
|
const REMOTE_FLEET_CONTROL_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_CONTROL_LIVE_FPS);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/wwwroot/_framework/{MindExecution.Web.994sb3nzuy.dll → MindExecution.Web.ygql0sg6b3.dll}
RENAMED
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mainAssemblyName": "MindExecution.Web",
|
|
3
3
|
"resources": {
|
|
4
|
-
"hash": "sha256-
|
|
4
|
+
"hash": "sha256-exyWukcppwSmHQvoV122RgnUj4KZOvdS3LHrc3Z6BTY=",
|
|
5
5
|
"fingerprinting": {
|
|
6
6
|
"Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
|
|
7
7
|
"Markdig.d1j7v41cl1.dll": "Markdig.dll",
|
|
@@ -127,12 +127,12 @@
|
|
|
127
127
|
"MindExecution.Kernel.3zom09ir6j.dll": "MindExecution.Kernel.dll",
|
|
128
128
|
"MindExecution.Plugins.Admin.bk23zzy6wc.dll": "MindExecution.Plugins.Admin.dll",
|
|
129
129
|
"MindExecution.Plugins.Business.zwcvc8wufe.dll": "MindExecution.Plugins.Business.dll",
|
|
130
|
-
"MindExecution.Plugins.Concept.
|
|
130
|
+
"MindExecution.Plugins.Concept.5gltgdqyo3.dll": "MindExecution.Plugins.Concept.dll",
|
|
131
131
|
"MindExecution.Plugins.Directory.jc0g7fvyzv.dll": "MindExecution.Plugins.Directory.dll",
|
|
132
|
-
"MindExecution.Plugins.PlanMaster.
|
|
133
|
-
"MindExecution.Plugins.YouTube.
|
|
134
|
-
"MindExecution.Shared.
|
|
135
|
-
"MindExecution.Web.
|
|
132
|
+
"MindExecution.Plugins.PlanMaster.cn6ppe6qoz.dll": "MindExecution.Plugins.PlanMaster.dll",
|
|
133
|
+
"MindExecution.Plugins.YouTube.d7nnsxrftv.dll": "MindExecution.Plugins.YouTube.dll",
|
|
134
|
+
"MindExecution.Shared.mnpwk5h86c.dll": "MindExecution.Shared.dll",
|
|
135
|
+
"MindExecution.Web.ygql0sg6b3.dll": "MindExecution.Web.dll",
|
|
136
136
|
"dotnet.js": "dotnet.js",
|
|
137
137
|
"dotnet.native.qc8g39g30v.js": "dotnet.native.js",
|
|
138
138
|
"dotnet.native.boem75ye5i.wasm": "dotnet.native.wasm",
|
|
@@ -280,16 +280,16 @@
|
|
|
280
280
|
"netstandard.yvr3prsx0x.dll": "sha256-EksNn8Luo4bOWqJ6X7dIe9qG9oOqwOVzjH2xYyMNi+E=",
|
|
281
281
|
"MindExecution.Core.asayb4hw1o.dll": "sha256-5gla4JTR/zbvkGpneSt6S7ItE51+MjWgjW6L+B6Yevk=",
|
|
282
282
|
"MindExecution.Kernel.3zom09ir6j.dll": "sha256-78vR2SNkF9FwdQv0fEItSyz4HzgyzwGUROJAnpripPA=",
|
|
283
|
-
"MindExecution.Plugins.Concept.
|
|
284
|
-
"MindExecution.Plugins.PlanMaster.
|
|
285
|
-
"MindExecution.Shared.
|
|
286
|
-
"MindExecution.Web.
|
|
283
|
+
"MindExecution.Plugins.Concept.5gltgdqyo3.dll": "sha256-1ZVpFoyPoidSyJz38jH40AFealONt+qQpToJKgqoWW8=",
|
|
284
|
+
"MindExecution.Plugins.PlanMaster.cn6ppe6qoz.dll": "sha256-GIFH/f4ilVTFDJVEDLLiWkkI7yGeeQl/T5/iuivI87Q=",
|
|
285
|
+
"MindExecution.Shared.mnpwk5h86c.dll": "sha256-Ht4Kl9nQePXwyDEwL4jBxo2K3F9R4B2OGnSuhHmZ+eU=",
|
|
286
|
+
"MindExecution.Web.ygql0sg6b3.dll": "sha256-vhb5mjLTaEStzJ9ggN4jBqtd1Z7FAkKHp7bx6Pz33Mc="
|
|
287
287
|
},
|
|
288
288
|
"lazyAssembly": {
|
|
289
289
|
"MindExecution.Plugins.Admin.bk23zzy6wc.dll": "sha256-uz7Nz0OZ8BBESp2Nr9YocyzV0dUrn7szPuD4/m/RWL8=",
|
|
290
290
|
"MindExecution.Plugins.Business.zwcvc8wufe.dll": "sha256-3YDsB2/uNI+feWqJk+cesLHgNt8s2/8hXxGPO6CRZZE=",
|
|
291
291
|
"MindExecution.Plugins.Directory.jc0g7fvyzv.dll": "sha256-PSe7135JPpTBmu4j+rGLF0Dc/6w/wDJNPi8zlfTmSMk=",
|
|
292
|
-
"MindExecution.Plugins.YouTube.
|
|
292
|
+
"MindExecution.Plugins.YouTube.d7nnsxrftv.dll": "sha256-OQVjt1f240FGxRV8b2FpxDv4i3U4a7GGA+dBBiZBih8="
|
|
293
293
|
}
|
|
294
294
|
},
|
|
295
295
|
"cacheBootResources": true,
|
package/wwwroot/index.html
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<title>MindExec | Run your ideas as AI task graphs</title>
|
|
8
8
|
<meta name="description" content="MindExec is an AI execution canvas for solo builders, researchers, developers, and creators. Start with free browser tools, then move serious work into saved MindCanvas projects." />
|
|
9
9
|
<base href="/" />
|
|
10
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260616-mdm-binary-canvas-
|
|
11
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-binary-canvas-
|
|
10
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260616-mdm-binary-canvas-v579" />
|
|
11
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260616-mdm-binary-canvas-v579" />
|
|
12
12
|
<!-- ?쇄뼹??Font Awesome (local) ?쇄뼹??-->
|
|
13
13
|
<link rel="stylesheet" href="_content/MindExecution.Shared/lib/font-awesome/css/all.min.css" />
|
|
14
14
|
<!-- ?꿎뼯??-->
|
|
@@ -579,7 +579,7 @@
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
const base = '_content/MindExecution.Shared/js/';
|
|
582
|
-
const scriptVersion = '20260616-mdm-binary-canvas-
|
|
582
|
+
const scriptVersion = '20260616-mdm-binary-canvas-v579';
|
|
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": "yUK/iR3X",
|
|
3
3
|
"assets": [
|
|
4
4
|
{
|
|
5
5
|
"hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
|
|
@@ -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-RLe5Ta1um44TU9QYuOpzKHrl8t1tOWQdkL+zwH79MFA=",
|
|
90
90
|
"url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -426,28 +426,28 @@
|
|
|
426
426
|
"url": "_framework/MindExecution.Plugins.Business.zwcvc8wufe.dll"
|
|
427
427
|
},
|
|
428
428
|
{
|
|
429
|
-
"hash": "sha256-
|
|
430
|
-
"url": "_framework/MindExecution.Plugins.Concept.
|
|
429
|
+
"hash": "sha256-1ZVpFoyPoidSyJz38jH40AFealONt+qQpToJKgqoWW8=",
|
|
430
|
+
"url": "_framework/MindExecution.Plugins.Concept.5gltgdqyo3.dll"
|
|
431
431
|
},
|
|
432
432
|
{
|
|
433
433
|
"hash": "sha256-PSe7135JPpTBmu4j+rGLF0Dc/6w/wDJNPi8zlfTmSMk=",
|
|
434
434
|
"url": "_framework/MindExecution.Plugins.Directory.jc0g7fvyzv.dll"
|
|
435
435
|
},
|
|
436
436
|
{
|
|
437
|
-
"hash": "sha256-
|
|
438
|
-
"url": "_framework/MindExecution.Plugins.PlanMaster.
|
|
437
|
+
"hash": "sha256-GIFH/f4ilVTFDJVEDLLiWkkI7yGeeQl/T5/iuivI87Q=",
|
|
438
|
+
"url": "_framework/MindExecution.Plugins.PlanMaster.cn6ppe6qoz.dll"
|
|
439
439
|
},
|
|
440
440
|
{
|
|
441
|
-
"hash": "sha256-
|
|
442
|
-
"url": "_framework/MindExecution.Plugins.YouTube.
|
|
441
|
+
"hash": "sha256-OQVjt1f240FGxRV8b2FpxDv4i3U4a7GGA+dBBiZBih8=",
|
|
442
|
+
"url": "_framework/MindExecution.Plugins.YouTube.d7nnsxrftv.dll"
|
|
443
443
|
},
|
|
444
444
|
{
|
|
445
|
-
"hash": "sha256-
|
|
446
|
-
"url": "_framework/MindExecution.Shared.
|
|
445
|
+
"hash": "sha256-Ht4Kl9nQePXwyDEwL4jBxo2K3F9R4B2OGnSuhHmZ+eU=",
|
|
446
|
+
"url": "_framework/MindExecution.Shared.mnpwk5h86c.dll"
|
|
447
447
|
},
|
|
448
448
|
{
|
|
449
|
-
"hash": "sha256-
|
|
450
|
-
"url": "_framework/MindExecution.Web.
|
|
449
|
+
"hash": "sha256-vhb5mjLTaEStzJ9ggN4jBqtd1Z7FAkKHp7bx6Pz33Mc=",
|
|
450
|
+
"url": "_framework/MindExecution.Web.ygql0sg6b3.dll"
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
"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-FgsKNE/RFoMI9OioRZ4cgImxpXXkbc2D4LpHp7W2L7A=",
|
|
774
774
|
"url": "_framework/blazor.boot.json"
|
|
775
775
|
},
|
|
776
776
|
{
|
|
@@ -834,7 +834,7 @@
|
|
|
834
834
|
"url": "image-manifest.json"
|
|
835
835
|
},
|
|
836
836
|
{
|
|
837
|
-
"hash": "sha256-
|
|
837
|
+
"hash": "sha256-0O54YE8cYfnYOu/2NbpFuJs/cJRKBw/OAc963M3JI6E=",
|
|
838
838
|
"url": "index.html"
|
|
839
839
|
},
|
|
840
840
|
{
|