@mindexec/cli 0.2.23 → 0.2.25
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 +1 -1
- package/remote-hub.js +134 -1
- package/scripts/remote-fleet-render-smoke.mjs +87 -53
- package/scripts/remote-http-smoke.mjs +219 -39
- package/scripts/remote-hub-smoke.mjs +139 -0
- package/server.js +20 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +18 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +670 -395
- package/wwwroot/_framework/{Microsoft.CSharp.qrvp77qmhs.dll → Microsoft.CSharp.8bsm8vx6su.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Core.6rfnfdndxq.dll +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.badrt1tkvv.dll → MindExecution.Kernel.z56elxihok.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.73w1bvz4r1.dll → MindExecution.Plugins.Admin.p5cs4ap87v.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.dvd82y422m.dll → MindExecution.Plugins.Business.s35og5uz44.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.m3ukc0xvom.dll → MindExecution.Plugins.Concept.zczca3fsxz.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.23tm2uvfvu.dll → MindExecution.Plugins.Directory.y74f55e8x3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.8nrc7ge4ob.dll → MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.3ox59073d8.dll → MindExecution.Plugins.YouTube.8nz4wv2nsj.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.va1gxp0crd.dll → MindExecution.Shared.v6ani8nfp8.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Web.0cs29v57jl.dll +0 -0
- package/wwwroot/_framework/{System.Collections.x53e19vfsj.dll → System.Collections.23yxgetbju.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.Concurrent.y1zmvuyipi.dll → System.Collections.Concurrent.vow3rm1dku.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.Immutable.ug3j698qms.dll → System.Collections.Immutable.ap9596utv5.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.NonGeneric.h66hj3863h.dll → System.Collections.NonGeneric.npjkaz40oc.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.Specialized.umr3y27ntj.dll → System.Collections.Specialized.ugkjbs6p02.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.Annotations.tz6gnt4ebt.dll → System.ComponentModel.Annotations.clwo0z2oyu.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.Primitives.j7tiphu4rg.dll → System.ComponentModel.Primitives.end4v0xe2c.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.TypeConverter.ujlztox1gx.dll → System.ComponentModel.TypeConverter.tgtp5sm4iv.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.x9xz0ojfb6.dll → System.ComponentModel.wupoltkk1t.dll} +0 -0
- package/wwwroot/_framework/{System.Console.ijzpqmj7ne.dll → System.Console.9dik0wogo2.dll} +0 -0
- package/wwwroot/_framework/{System.Data.Common.1r0sqffq1p.dll → System.Data.Common.4v8jejsiu0.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.DiagnosticSource.9upoqwq09o.dll → System.Diagnostics.DiagnosticSource.e2q75ondtq.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.Process.m99azzntjm.dll → System.Diagnostics.Process.9736yjnxs8.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.TraceSource.pl7wv26myr.dll → System.Diagnostics.TraceSource.h9al53gbbw.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.Tracing.crlhfx6tut.dll → System.Diagnostics.Tracing.h4bcp2fo98.dll} +0 -0
- package/wwwroot/_framework/{System.Drawing.mi7d8hwowb.dll → System.Drawing.64oovy8qts.dll} +0 -0
- package/wwwroot/_framework/{System.Drawing.Primitives.22e4y9ikq9.dll → System.Drawing.Primitives.o6jiqpgbgl.dll} +0 -0
- package/wwwroot/_framework/{System.Formats.Asn1.jx23sjiqnn.dll → System.Formats.Asn1.rylx5ipd40.dll} +0 -0
- package/wwwroot/_framework/{System.IO.Compression.6fyoii3uej.dll → System.IO.Compression.iceabaupns.dll} +0 -0
- package/wwwroot/_framework/{System.IO.Pipelines.vg77t4cd4d.dll → System.IO.Pipelines.uw8csd3mlz.dll} +0 -0
- package/wwwroot/_framework/{System.Linq.Expressions.24xqiypwdt.dll → System.Linq.Expressions.ty95ava37f.dll} +0 -0
- package/wwwroot/_framework/{System.Linq.Queryable.hvd01d6rsa.dll → System.Linq.Queryable.hs2195jrwy.dll} +0 -0
- package/wwwroot/_framework/{System.Linq.1bkoxlqgmq.dll → System.Linq.hssodjwmlf.dll} +0 -0
- package/wwwroot/_framework/{System.Memory.8dx3lwgym4.dll → System.Memory.1k78n7wdxb.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Http.eitrz660my.dll → System.Net.Http.3br8rfql4c.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Http.Json.3mhdm9l1rf.dll → System.Net.Http.Json.860wbh17d8.dll} +0 -0
- package/wwwroot/_framework/{System.Net.NetworkInformation.3pkuofcv9r.dll → System.Net.NetworkInformation.e3wr00853o.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Ping.8clj5pklrp.dll → System.Net.Ping.r5cw4mf1a4.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Primitives.qrp4wcjz1p.dll → System.Net.Primitives.ksxwiwlvhu.dll} +0 -0
- package/wwwroot/_framework/{System.Net.WebSockets.qp6u31zvm5.dll → System.Net.WebSockets.6rt3n3gl2q.dll} +0 -0
- package/wwwroot/_framework/{System.Net.WebSockets.Client.2u6pv01g69.dll → System.Net.WebSockets.Client.z3usrzo7rz.dll} +0 -0
- package/wwwroot/_framework/{System.Numerics.Vectors.kc7ufp2j4l.dll → System.Numerics.Vectors.lbdzx8reja.dll} +0 -0
- package/wwwroot/_framework/{System.ObjectModel.qv82fot1ib.dll → System.ObjectModel.yct1gdirzf.dll} +0 -0
- package/wwwroot/_framework/{System.Private.CoreLib.rkafq04oma.dll → System.Private.CoreLib.ns29bor93l.dll} +0 -0
- package/wwwroot/_framework/{System.Private.Uri.t9542hmr6j.dll → System.Private.Uri.zp9kmg0z93.dll} +0 -0
- package/wwwroot/_framework/{System.Private.Xml.Linq.n8n3ptrbwu.dll → System.Private.Xml.Linq.lgv2n0akl4.dll} +0 -0
- package/wwwroot/_framework/{System.Private.Xml.rxd3tytisn.dll → System.Private.Xml.dpsk8g304y.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Emit.ILGeneration.stxyk8zoo1.dll → System.Reflection.Emit.ILGeneration.1tcuz2cmbk.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Emit.Lightweight.6xrd5v8vg0.dll → System.Reflection.Emit.Lightweight.ddt2wylovg.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Emit.9tjhp6y0j3.dll → System.Reflection.Emit.d8vkadiwhg.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Primitives.wgn8fpwwvv.dll → System.Reflection.Primitives.cpsl71xd1z.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.InteropServices.te07xr2we9.dll → System.Runtime.InteropServices.31vjgsfk1o.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.InteropServices.JavaScript.sliym526xh.dll → System.Runtime.InteropServices.JavaScript.pn2wvizzet.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.InteropServices.RuntimeInformation.oji7zut14z.dll → System.Runtime.InteropServices.RuntimeInformation.l5rk496q70.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Intrinsics.507y4h8nzq.dll → System.Runtime.Intrinsics.0zee9qcqfy.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Loader.v7gk4bse0k.dll → System.Runtime.Loader.xw2jr9wl92.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Numerics.eqy5xjv3nd.dll → System.Runtime.Numerics.ezj1dfyvbj.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Serialization.Formatters.zpkrub8lab.dll → System.Runtime.Serialization.Formatters.0y019e9zkr.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Serialization.Primitives.vhkpnbxjip.dll → System.Runtime.Serialization.Primitives.bia5wb62c8.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.jn319d5nyg.dll → System.Runtime.h9cduidfkh.dll} +0 -0
- package/wwwroot/_framework/{System.Security.Claims.0ztig1q9vo.dll → System.Security.Claims.2r18wim2rl.dll} +0 -0
- package/wwwroot/_framework/{System.Security.Cryptography.vttizqc9ho.dll → System.Security.Cryptography.qqgybpoucx.dll} +0 -0
- package/wwwroot/_framework/{System.Text.Encoding.Extensions.utdd47ny8f.dll → System.Text.Encoding.Extensions.9t3hs6kyll.dll} +0 -0
- package/wwwroot/_framework/{System.Text.Encodings.Web.wah8r1zoe0.dll → System.Text.Encodings.Web.wftjfh2crk.dll} +0 -0
- package/wwwroot/_framework/{System.Text.Json.kxlfxj0wrs.dll → System.Text.Json.4s25e3op6i.dll} +0 -0
- package/wwwroot/_framework/{System.Text.RegularExpressions.dbqn58klox.dll → System.Text.RegularExpressions.cvkox11l6l.dll} +0 -0
- package/wwwroot/_framework/{System.Threading.Channels.hfa7j0uv2w.dll → System.Threading.Channels.419493szqu.dll} +0 -0
- package/wwwroot/_framework/{System.Threading.Thread.caul0pdqul.dll → System.Threading.Thread.xhmys87xh5.dll} +0 -0
- package/wwwroot/_framework/{System.Threading.42ao9vi047.dll → System.Threading.b66vzsz9g1.dll} +0 -0
- package/wwwroot/_framework/{System.Transactions.Local.fimi2hamzo.dll → System.Transactions.Local.7zfffdvnwf.dll} +0 -0
- package/wwwroot/_framework/{System.Web.HttpUtility.gq8yz50p2e.dll → System.Web.HttpUtility.9up6xfbtuq.dll} +0 -0
- package/wwwroot/_framework/{System.Xml.Linq.kitin4zjoj.dll → System.Xml.Linq.n5rzv9nbf7.dll} +0 -0
- package/wwwroot/_framework/{System.Xml.ReaderWriter.kzvw3qgxb0.dll → System.Xml.ReaderWriter.ag8pilllob.dll} +0 -0
- package/wwwroot/_framework/{System.Xml.XDocument.c539ki6cuq.dll → System.Xml.XDocument.sn51jas17n.dll} +0 -0
- package/wwwroot/_framework/{System.m05i39uvk9.dll → System.brmz7yk5qh.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +161 -161
- package/wwwroot/_framework/dotnet.js +1 -1
- package/wwwroot/_framework/{dotnet.native.vz0adxojrz.wasm → dotnet.native.boem75ye5i.wasm} +0 -0
- package/wwwroot/_framework/{dotnet.native.xsn1d6x2kd.js → dotnet.native.qc8g39g30v.js} +1 -1
- package/wwwroot/_framework/{dotnet.runtime.dstopyvqzi.js → dotnet.runtime.opaiwunc3t.js} +1 -1
- package/wwwroot/_framework/{netstandard.0xet7jg7ky.dll → netstandard.yvr3prsx0x.dll} +0 -0
- package/wwwroot/index.html +1 -1
- package/wwwroot/service-worker-assets.js +166 -166
- package/wwwroot/service-worker.js +1 -1
- package/wwwroot/_framework/MindExecution.Core.c9fyqe953v.dll +0 -0
- package/wwwroot/_framework/MindExecution.Web.jmawk7z8d3.dll +0 -0
|
@@ -51,11 +51,11 @@ async function fetchJson(url, options = {}) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
async function waitForBridge(baseUrl, getFailureDetails) {
|
|
54
|
+
async function waitForBridge(baseUrl, getFailureDetails, bridgeToken = BRIDGE_TOKEN) {
|
|
55
55
|
const startedAt = Date.now();
|
|
56
56
|
while (Date.now() - startedAt < 30000) {
|
|
57
57
|
try {
|
|
58
|
-
const result = await fetchJson(`${baseUrl}/api/remote/status`, { token:
|
|
58
|
+
const result = await fetchJson(`${baseUrl}/api/remote/status`, { token: bridgeToken });
|
|
59
59
|
if (result.ok && result.payload?.started === true) {
|
|
60
60
|
return result.payload;
|
|
61
61
|
}
|
|
@@ -70,47 +70,96 @@ async function waitForBridge(baseUrl, getFailureDetails) {
|
|
|
70
70
|
|
|
71
71
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
72
72
|
const bridgeRoot = path.resolve(__dirname, '..');
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
env
|
|
73
|
+
|
|
74
|
+
function spawnBridge({
|
|
75
|
+
bridgePort,
|
|
76
|
+
remoteHubPort,
|
|
77
|
+
bridgeToken = BRIDGE_TOKEN,
|
|
78
|
+
pairToken = PAIR_TOKEN,
|
|
79
|
+
syntheticFleetEnabled = false
|
|
80
|
+
}) {
|
|
81
|
+
const env = {
|
|
82
82
|
...process.env,
|
|
83
83
|
BRIDGE_PORT: String(bridgePort),
|
|
84
|
-
BRIDGE_TOKEN,
|
|
84
|
+
BRIDGE_TOKEN: bridgeToken,
|
|
85
85
|
BRIDGE_REQUIRE_TOKEN: '1',
|
|
86
86
|
MINDEXEC_REMOTE_HUB: '1',
|
|
87
87
|
REMOTE_HUB_HOST: '127.0.0.1',
|
|
88
88
|
REMOTE_HUB_PORT: String(remoteHubPort),
|
|
89
|
-
REMOTE_HUB_PAIR_TOKEN:
|
|
90
|
-
MINDEXEC_REMOTE_SYNTHETIC_FLEET: '1',
|
|
89
|
+
REMOTE_HUB_PAIR_TOKEN: pairToken,
|
|
91
90
|
NO_COLOR: '1'
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
delete env.MINDEXEC_REMOTE_SYNTHETIC_FLEET;
|
|
94
|
+
delete env.REMOTE_HUB_SYNTHETIC_FLEET;
|
|
95
|
+
if (syntheticFleetEnabled) {
|
|
96
|
+
env.MINDEXEC_REMOTE_SYNTHETIC_FLEET = '1';
|
|
92
97
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
|
|
99
|
+
const child = spawn(process.execPath, ['server.js'], {
|
|
100
|
+
cwd: bridgeRoot,
|
|
101
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
102
|
+
windowsHide: true,
|
|
103
|
+
env
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
let stdout = '';
|
|
107
|
+
let stderr = '';
|
|
108
|
+
child.stdout.on('data', chunk => {
|
|
109
|
+
stdout += chunk.toString();
|
|
110
|
+
});
|
|
111
|
+
child.stderr.on('data', chunk => {
|
|
112
|
+
stderr += chunk.toString();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const exitPromise = new Promise(resolve => child.once('exit', resolve));
|
|
116
|
+
const details = () => `stdout=${stdout}\nstderr=${stderr}`;
|
|
117
|
+
const stop = async () => {
|
|
118
|
+
if (child.exitCode === null && !child.killed) {
|
|
119
|
+
child.kill('SIGTERM');
|
|
120
|
+
await Promise.race([
|
|
121
|
+
exitPromise,
|
|
122
|
+
wait(5000)
|
|
123
|
+
]);
|
|
124
|
+
if (child.exitCode === null && !child.killed) {
|
|
125
|
+
child.kill('SIGKILL');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
baseUrl: `http://127.0.0.1:${bridgePort}`,
|
|
132
|
+
bridgePort,
|
|
133
|
+
remoteHubPort,
|
|
134
|
+
bridgeToken,
|
|
135
|
+
pairToken,
|
|
136
|
+
child,
|
|
137
|
+
details,
|
|
138
|
+
stop
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function runSyntheticEnabledSmoke() {
|
|
143
|
+
const bridgePort = await findFreePort();
|
|
144
|
+
const remoteHubPort = await findFreePort();
|
|
145
|
+
const bridge = spawnBridge({
|
|
146
|
+
bridgePort,
|
|
147
|
+
remoteHubPort,
|
|
148
|
+
syntheticFleetEnabled: true
|
|
149
|
+
});
|
|
150
|
+
const { baseUrl } = bridge;
|
|
151
|
+
const details = bridge.details;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const status = await waitForBridge(baseUrl, details, bridge.bridgeToken);
|
|
109
155
|
assert.equal(status.started, true);
|
|
110
156
|
assert.equal(status.port, remoteHubPort);
|
|
157
|
+
assert.equal(status.managerPackage, '@mindexec/cli');
|
|
158
|
+
assert.equal(status.managerVersion, '0.2.25');
|
|
111
159
|
assert.equal(status.agentPackage, '@mindexec/remote');
|
|
112
160
|
assert.equal(status.canvasPagination, 'none');
|
|
113
161
|
assert.equal(status.canvasDeviceListMode, 'all-devices');
|
|
162
|
+
assert.equal(status.hostTargetActive, false);
|
|
114
163
|
assert.equal(status.pairToken, PAIR_TOKEN);
|
|
115
164
|
|
|
116
165
|
const unauthorizedStatus = await fetchJson(`${baseUrl}/api/remote/status`);
|
|
@@ -122,6 +171,58 @@ try {
|
|
|
122
171
|
});
|
|
123
172
|
assert.equal(unauthorizedDelete.status, 401);
|
|
124
173
|
|
|
174
|
+
const unauthorizedHost = await fetchJson(`${baseUrl}/api/remote/host-target`, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
body: JSON.stringify({
|
|
177
|
+
nodeId: 'remote-fleet-host-unauthorized',
|
|
178
|
+
enabled: true
|
|
179
|
+
})
|
|
180
|
+
});
|
|
181
|
+
assert.equal(unauthorizedHost.status, 401);
|
|
182
|
+
|
|
183
|
+
const setHost = await fetchJson(`${baseUrl}/api/remote/host-target`, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
token: BRIDGE_TOKEN,
|
|
186
|
+
body: JSON.stringify({
|
|
187
|
+
nodeId: 'remote-fleet-monitor-a',
|
|
188
|
+
enabled: true,
|
|
189
|
+
leaseMs: 30000
|
|
190
|
+
})
|
|
191
|
+
});
|
|
192
|
+
assert.equal(setHost.ok, true, JSON.stringify(setHost.payload));
|
|
193
|
+
assert.equal(setHost.payload?.ok, true);
|
|
194
|
+
assert.equal(setHost.payload?.active, true);
|
|
195
|
+
assert.equal(setHost.payload?.hostTarget?.nodeId, 'remote-fleet-monitor-a');
|
|
196
|
+
assert.equal(setHost.payload?.hostTarget?.endpoint, `127.0.0.1:${remoteHubPort}`);
|
|
197
|
+
|
|
198
|
+
const hostStatus = await fetchJson(`${baseUrl}/api/remote/status`, { token: BRIDGE_TOKEN });
|
|
199
|
+
assert.equal(hostStatus.ok, true, JSON.stringify(hostStatus.payload));
|
|
200
|
+
assert.equal(hostStatus.payload?.hostTargetActive, true);
|
|
201
|
+
assert.equal(hostStatus.payload?.hostTargetNodeId, 'remote-fleet-monitor-a');
|
|
202
|
+
assert.equal(hostStatus.payload?.hostTargetEndpoint, `127.0.0.1:${remoteHubPort}`);
|
|
203
|
+
|
|
204
|
+
const clearHostMismatch = await fetchJson(`${baseUrl}/api/remote/host-target`, {
|
|
205
|
+
method: 'DELETE',
|
|
206
|
+
token: BRIDGE_TOKEN,
|
|
207
|
+
body: JSON.stringify({
|
|
208
|
+
nodeId: 'remote-fleet-monitor-b'
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
assert.equal(clearHostMismatch.ok, true, JSON.stringify(clearHostMismatch.payload));
|
|
212
|
+
assert.equal(clearHostMismatch.payload?.ok, false);
|
|
213
|
+
assert.equal(clearHostMismatch.payload?.error, 'host-target-node-mismatch');
|
|
214
|
+
|
|
215
|
+
const clearHost = await fetchJson(`${baseUrl}/api/remote/host-target`, {
|
|
216
|
+
method: 'DELETE',
|
|
217
|
+
token: BRIDGE_TOKEN,
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
nodeId: 'remote-fleet-monitor-a'
|
|
220
|
+
})
|
|
221
|
+
});
|
|
222
|
+
assert.equal(clearHost.ok, true, JSON.stringify(clearHost.payload));
|
|
223
|
+
assert.equal(clearHost.payload?.ok, true);
|
|
224
|
+
assert.equal(clearHost.payload?.active, false);
|
|
225
|
+
|
|
125
226
|
const seed = await fetchJson(`${baseUrl}/api/remote/synthetic/seed`, {
|
|
126
227
|
method: 'POST',
|
|
127
228
|
token: BRIDGE_TOKEN,
|
|
@@ -281,14 +382,93 @@ try {
|
|
|
281
382
|
|
|
282
383
|
console.log(`RemoteHub HTTP smoke OK (${SYNTHETIC_COUNT} synthetic devices, bridge ${bridgePort}, remote ${remoteHubPort})`);
|
|
283
384
|
} finally {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
385
|
+
await bridge.stop();
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function runSyntheticDisabledSmoke() {
|
|
390
|
+
const bridgePort = await findFreePort();
|
|
391
|
+
const remoteHubPort = await findFreePort();
|
|
392
|
+
const bridgeToken = `${BRIDGE_TOKEN}-disabled`;
|
|
393
|
+
const pairToken = `${PAIR_TOKEN}-disabled`;
|
|
394
|
+
const bridge = spawnBridge({
|
|
395
|
+
bridgePort,
|
|
396
|
+
remoteHubPort,
|
|
397
|
+
bridgeToken,
|
|
398
|
+
pairToken,
|
|
399
|
+
syntheticFleetEnabled: false
|
|
400
|
+
});
|
|
401
|
+
const { baseUrl } = bridge;
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
const status = await waitForBridge(baseUrl, bridge.details, bridge.bridgeToken);
|
|
405
|
+
assert.equal(status.started, true);
|
|
406
|
+
assert.equal(status.port, remoteHubPort);
|
|
407
|
+
assert.equal(status.pairToken, pairToken);
|
|
408
|
+
assert.equal(status.canvasPagination, 'none');
|
|
409
|
+
assert.equal(status.canvasDeviceListMode, 'all-devices');
|
|
410
|
+
assert.equal(status.hostTargetActive, false);
|
|
411
|
+
|
|
412
|
+
const seedDenied = await fetchJson(`${baseUrl}/api/remote/synthetic/seed`, {
|
|
413
|
+
method: 'POST',
|
|
414
|
+
token: bridge.bridgeToken,
|
|
415
|
+
body: JSON.stringify({
|
|
416
|
+
count: 3,
|
|
417
|
+
replace: true
|
|
418
|
+
})
|
|
419
|
+
});
|
|
420
|
+
assert.equal(seedDenied.status, 403);
|
|
421
|
+
assert.equal(seedDenied.payload?.ok, false);
|
|
422
|
+
assert.equal(seedDenied.payload?.error, 'synthetic-fleet-disabled');
|
|
423
|
+
|
|
424
|
+
const clearDenied = await fetchJson(`${baseUrl}/api/remote/synthetic`, {
|
|
425
|
+
method: 'DELETE',
|
|
426
|
+
token: bridge.bridgeToken
|
|
427
|
+
});
|
|
428
|
+
assert.equal(clearDenied.status, 403);
|
|
429
|
+
assert.equal(clearDenied.payload?.ok, false);
|
|
430
|
+
assert.equal(clearDenied.payload?.error, 'synthetic-fleet-disabled');
|
|
431
|
+
|
|
432
|
+
const devicesResult = await fetchJson(`${baseUrl}/api/remote/devices`, { token: bridge.bridgeToken });
|
|
433
|
+
assert.equal(devicesResult.ok, true, JSON.stringify(devicesResult.payload));
|
|
434
|
+
assert.equal(devicesResult.payload?.total, 0);
|
|
435
|
+
assert.equal(devicesResult.payload?.pagination, 'none');
|
|
436
|
+
assert.equal(devicesResult.payload?.canvasDeviceListMode, 'all-devices');
|
|
437
|
+
assert.equal(devicesResult.payload?.devices?.length, 0);
|
|
438
|
+
|
|
439
|
+
const emptyBatch = await fetchJson(`${baseUrl}/api/remote/tasks`, {
|
|
440
|
+
method: 'POST',
|
|
441
|
+
token: bridge.bridgeToken,
|
|
442
|
+
body: JSON.stringify({
|
|
443
|
+
allConnected: true,
|
|
444
|
+
title: 'HTTP smoke no target batch',
|
|
445
|
+
instruction: 'This should not queue because no remote devices are connected.',
|
|
446
|
+
approvalLevel: 'task-only'
|
|
447
|
+
})
|
|
448
|
+
});
|
|
449
|
+
assert.equal(emptyBatch.ok, true, JSON.stringify(emptyBatch.payload));
|
|
450
|
+
assert.equal(emptyBatch.payload?.ok, false);
|
|
451
|
+
assert.equal(emptyBatch.payload?.total, 0);
|
|
452
|
+
assert.equal(emptyBatch.payload?.queued, 0);
|
|
453
|
+
assert.equal(emptyBatch.payload?.error, 'no-target-devices');
|
|
454
|
+
|
|
455
|
+
const hostTarget = await fetchJson(`${baseUrl}/api/remote/host-target`, {
|
|
456
|
+
method: 'POST',
|
|
457
|
+
token: bridge.bridgeToken,
|
|
458
|
+
body: JSON.stringify({
|
|
459
|
+
nodeId: 'disabled-synthetic-host',
|
|
460
|
+
enabled: true
|
|
461
|
+
})
|
|
462
|
+
});
|
|
463
|
+
assert.equal(hostTarget.ok, true, JSON.stringify(hostTarget.payload));
|
|
464
|
+
assert.equal(hostTarget.payload?.ok, true);
|
|
465
|
+
assert.equal(hostTarget.payload?.active, true);
|
|
466
|
+
|
|
467
|
+
console.log(`RemoteHub HTTP safety smoke OK (synthetic disabled, bridge ${bridgePort}, remote ${remoteHubPort})`);
|
|
468
|
+
} finally {
|
|
469
|
+
await bridge.stop();
|
|
293
470
|
}
|
|
294
471
|
}
|
|
472
|
+
|
|
473
|
+
await runSyntheticEnabledSmoke();
|
|
474
|
+
await runSyntheticDisabledSmoke();
|
|
@@ -47,6 +47,7 @@ try {
|
|
|
47
47
|
socket.once('connect', resolve);
|
|
48
48
|
socket.once('error', reject);
|
|
49
49
|
});
|
|
50
|
+
socket.on('error', () => { });
|
|
50
51
|
|
|
51
52
|
writeJsonLine(socket, {
|
|
52
53
|
type: 'hello',
|
|
@@ -306,7 +307,145 @@ try {
|
|
|
306
307
|
assert.equal(lateTimeoutDevice.latestTask.resultSummary, 'task-timeout');
|
|
307
308
|
assert.equal(lateTimeoutDevice.counters.taskResultsReceived, 4);
|
|
308
309
|
|
|
310
|
+
const timeoutBatch = hub.requestAgentTaskBatch(['smoke-device'], {
|
|
311
|
+
instruction: 'This batch item intentionally times out before a late result arrives.',
|
|
312
|
+
title: 'Timeout batch'
|
|
313
|
+
});
|
|
314
|
+
assert.equal(timeoutBatch.ok, true);
|
|
315
|
+
assert.equal(timeoutBatch.total, 1);
|
|
316
|
+
assert.equal(timeoutBatch.queued, 1);
|
|
317
|
+
assert.equal(timeoutBatch.batch?.pending, 1);
|
|
318
|
+
const timeoutBatchItem = timeoutBatch.results.find(result => result.deviceId === 'smoke-device');
|
|
319
|
+
assert.ok(timeoutBatchItem?.commandId);
|
|
320
|
+
assert.ok(timeoutBatchItem?.taskId);
|
|
321
|
+
|
|
322
|
+
const timedOutBatch = await waitFor(() => {
|
|
323
|
+
const latest = hub.getLatestTaskBatch();
|
|
324
|
+
return latest?.batchId === timeoutBatch.batchId
|
|
325
|
+
&& latest.timedOut === 1
|
|
326
|
+
&& latest.pending === 0
|
|
327
|
+
? latest
|
|
328
|
+
: null;
|
|
329
|
+
}, 1000);
|
|
330
|
+
assert.equal(timedOutBatch.status, 'completed-with-failures');
|
|
331
|
+
assert.equal(timedOutBatch.failed, 1);
|
|
332
|
+
assert.equal(timedOutBatch.results[0]?.status, 'failed');
|
|
333
|
+
assert.equal(timedOutBatch.results[0]?.error, 'task-timeout');
|
|
334
|
+
|
|
335
|
+
writeJsonLine(socket, {
|
|
336
|
+
type: 'command.result',
|
|
337
|
+
commandId: timeoutBatchItem.commandId,
|
|
338
|
+
result: {
|
|
339
|
+
kind: 'agent.task',
|
|
340
|
+
taskId: timeoutBatchItem.taskId,
|
|
341
|
+
status: 'completed',
|
|
342
|
+
summary: 'This late batch timeout result must be ignored.',
|
|
343
|
+
completedAt: new Date().toISOString()
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
await wait(100);
|
|
347
|
+
const lateTimedOutBatch = hub.getLatestTaskBatch();
|
|
348
|
+
const lateTimedOutDevice = hub.listDevices()[0];
|
|
349
|
+
assert.equal(lateTimedOutBatch.batchId, timeoutBatch.batchId);
|
|
350
|
+
assert.equal(lateTimedOutBatch.status, 'completed-with-failures');
|
|
351
|
+
assert.equal(lateTimedOutBatch.completed, 0);
|
|
352
|
+
assert.equal(lateTimedOutBatch.failed, 1);
|
|
353
|
+
assert.equal(lateTimedOutBatch.timedOut, 1);
|
|
354
|
+
assert.equal(lateTimedOutBatch.results[0]?.status, 'failed');
|
|
355
|
+
assert.equal(lateTimedOutBatch.results[0]?.error, 'task-timeout');
|
|
356
|
+
assert.equal(lateTimedOutDevice.latestTask.taskId, timeoutBatchItem.taskId);
|
|
357
|
+
assert.equal(lateTimedOutDevice.latestTask.status, 'failed');
|
|
358
|
+
assert.equal(lateTimedOutDevice.latestTask.error, 'task-timeout');
|
|
359
|
+
assert.equal(lateTimedOutDevice.latestTask.resultSummary, 'task-timeout');
|
|
360
|
+
assert.equal(lateTimedOutDevice.counters.taskResultsReceived, 5);
|
|
361
|
+
assert.equal(lateTimedOutDevice.counters.taskResultsFailed, 2);
|
|
362
|
+
assert.equal(lateTimedOutDevice.counters.taskResultsTimedOut, 2);
|
|
363
|
+
|
|
364
|
+
const staleSessionTask = hub.requestAgentTask('smoke-device', {
|
|
365
|
+
instruction: 'This old session task must not survive reconnect.',
|
|
366
|
+
title: 'Reconnect stale task'
|
|
367
|
+
});
|
|
368
|
+
assert.equal(staleSessionTask.ok, true);
|
|
369
|
+
const oldSessionId = lateTimedOutDevice.sessionId;
|
|
370
|
+
const replacementSocket = net.createConnection({ host: '127.0.0.1', port: status.port });
|
|
371
|
+
replacementSocket.setEncoding('utf8');
|
|
372
|
+
await new Promise((resolve, reject) => {
|
|
373
|
+
replacementSocket.once('connect', resolve);
|
|
374
|
+
replacementSocket.once('error', reject);
|
|
375
|
+
});
|
|
376
|
+
replacementSocket.on('error', () => { });
|
|
377
|
+
writeJsonLine(replacementSocket, {
|
|
378
|
+
type: 'hello',
|
|
379
|
+
pairToken: 'smoke-token',
|
|
380
|
+
deviceId: 'smoke-device',
|
|
381
|
+
deviceName: 'Smoke Device Reconnected',
|
|
382
|
+
hostname: 'smoke-host-reconnected',
|
|
383
|
+
platform: process.platform,
|
|
384
|
+
arch: process.arch,
|
|
385
|
+
pid: process.pid,
|
|
386
|
+
agentVersion: '0.0.0-smoke-reconnected',
|
|
387
|
+
capabilities: {
|
|
388
|
+
status: true,
|
|
389
|
+
thumbnail: false,
|
|
390
|
+
control: false,
|
|
391
|
+
liveStream: true,
|
|
392
|
+
computerAgent: true,
|
|
393
|
+
taskDispatch: true,
|
|
394
|
+
aiAssist: true,
|
|
395
|
+
aiModel: 'fake-ai'
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
writeJsonLine(replacementSocket, {
|
|
399
|
+
type: 'status',
|
|
400
|
+
status: {
|
|
401
|
+
uptimeSec: 2,
|
|
402
|
+
totalMem: 4,
|
|
403
|
+
freeMem: 3
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const reconnectedDevice = await waitFor(() => {
|
|
408
|
+
const current = hub.listDevices();
|
|
409
|
+
return current.length === 1
|
|
410
|
+
&& current[0]?.deviceName === 'Smoke Device Reconnected'
|
|
411
|
+
&& current[0]?.status?.uptimeSec === 2
|
|
412
|
+
? current[0]
|
|
413
|
+
: null;
|
|
414
|
+
});
|
|
415
|
+
assert.notEqual(reconnectedDevice.sessionId, oldSessionId);
|
|
416
|
+
assert.equal(reconnectedDevice.connected, true);
|
|
417
|
+
assert.equal(reconnectedDevice.latestTask, null);
|
|
418
|
+
assert.equal(reconnectedDevice.counters.taskResultsReceived, 0);
|
|
419
|
+
|
|
420
|
+
writeJsonLine(socket, {
|
|
421
|
+
type: 'status',
|
|
422
|
+
status: {
|
|
423
|
+
uptimeSec: 999,
|
|
424
|
+
totalMem: 999,
|
|
425
|
+
freeMem: 0
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
writeJsonLine(socket, {
|
|
429
|
+
type: 'command.result',
|
|
430
|
+
commandId: staleSessionTask.commandId,
|
|
431
|
+
result: {
|
|
432
|
+
kind: 'agent.task',
|
|
433
|
+
taskId: staleSessionTask.taskId,
|
|
434
|
+
status: 'completed',
|
|
435
|
+
summary: 'This old-session result must not mutate the reconnected device.',
|
|
436
|
+
completedAt: new Date().toISOString()
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
await wait(100);
|
|
440
|
+
const staleSessionIgnoredDevice = hub.listDevices()[0];
|
|
441
|
+
assert.equal(staleSessionIgnoredDevice.sessionId, reconnectedDevice.sessionId);
|
|
442
|
+
assert.equal(staleSessionIgnoredDevice.deviceName, 'Smoke Device Reconnected');
|
|
443
|
+
assert.equal(staleSessionIgnoredDevice.status.uptimeSec, 2);
|
|
444
|
+
assert.equal(staleSessionIgnoredDevice.latestTask, null);
|
|
445
|
+
assert.equal(staleSessionIgnoredDevice.counters.taskResultsReceived, 0);
|
|
446
|
+
|
|
309
447
|
socket.destroy();
|
|
448
|
+
replacementSocket.destroy();
|
|
310
449
|
await waitFor(() => hub.listDevices()[0]?.connected === false);
|
|
311
450
|
console.log('RemoteHub smoke OK');
|
|
312
451
|
} finally {
|
package/server.js
CHANGED
|
@@ -2464,7 +2464,9 @@ const remoteHub = createRemoteHub({
|
|
|
2464
2464
|
logEvent,
|
|
2465
2465
|
logWarn,
|
|
2466
2466
|
logError,
|
|
2467
|
-
emitEvent: emitBridgeEvent
|
|
2467
|
+
emitEvent: emitBridgeEvent,
|
|
2468
|
+
managerPackage: BRIDGE_PACKAGE_NAME,
|
|
2469
|
+
managerVersion: BRIDGE_VERSION
|
|
2468
2470
|
});
|
|
2469
2471
|
|
|
2470
2472
|
function trimShellOutput(value, maxLength) {
|
|
@@ -6976,6 +6978,23 @@ app.get('/api/remote/devices', (req, res) => {
|
|
|
6976
6978
|
});
|
|
6977
6979
|
});
|
|
6978
6980
|
|
|
6981
|
+
app.post('/api/remote/host-target', (req, res) => {
|
|
6982
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
6983
|
+
res.json(remoteHub.setHostTarget({
|
|
6984
|
+
enabled: req.body?.enabled !== false,
|
|
6985
|
+
nodeId: req.body?.nodeId,
|
|
6986
|
+
leaseMs: req.body?.leaseMs
|
|
6987
|
+
}));
|
|
6988
|
+
});
|
|
6989
|
+
|
|
6990
|
+
app.delete('/api/remote/host-target', (req, res) => {
|
|
6991
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
6992
|
+
res.json(remoteHub.setHostTarget({
|
|
6993
|
+
enabled: false,
|
|
6994
|
+
nodeId: req.body?.nodeId
|
|
6995
|
+
}));
|
|
6996
|
+
});
|
|
6997
|
+
|
|
6979
6998
|
function isSyntheticRemoteFleetEnabled() {
|
|
6980
6999
|
return /^(1|true|yes|on)$/i.test(String(
|
|
6981
7000
|
process.env.MINDEXEC_REMOTE_SYNTHETIC_FLEET
|
|
@@ -6280,7 +6280,24 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
|
|
|
6280
6280
|
|
|
6281
6281
|
const x = Number(moduleInstance.cursorPosition?.x ?? moduleInstance.camera?.position?.x ?? 0);
|
|
6282
6282
|
const y = Number(moduleInstance.cursorPosition?.y ?? moduleInstance.camera?.position?.y ?? 0);
|
|
6283
|
-
|
|
6283
|
+
const result = await moduleInstance.dotNetHelper.invokeMethodAsync('AddRemoteFleetMonitorNodeFromJs', x, y);
|
|
6284
|
+
const nodeId = String(result?.nodeId ?? result?.NodeId ?? '').trim();
|
|
6285
|
+
if (nodeId) {
|
|
6286
|
+
const alreadyExists = result?.alreadyExists === true || result?.AlreadyExists === true;
|
|
6287
|
+
const focusExistingMonitor = () => window.mindMap?.focusNode?.(nodeId, {
|
|
6288
|
+
selectNode: true,
|
|
6289
|
+
preferReadableZoom: true,
|
|
6290
|
+
highlightSearchFocus: alreadyExists
|
|
6291
|
+
}) === true;
|
|
6292
|
+
if (!focusExistingMonitor()) {
|
|
6293
|
+
setTimeout(focusExistingMonitor, 120);
|
|
6294
|
+
}
|
|
6295
|
+
window.RuntimeTrace?.emit?.('remote.monitor.focused', {
|
|
6296
|
+
nodeId,
|
|
6297
|
+
alreadyExists
|
|
6298
|
+
});
|
|
6299
|
+
}
|
|
6300
|
+
return result;
|
|
6284
6301
|
},
|
|
6285
6302
|
// ▼▼▼ [Perf] Expose nodeObjectsById for will-change optimization ▼▼▼
|
|
6286
6303
|
getNodeObjectsById: () => moduleInstance?.nodeObjectsById || null,
|