@buoy-gg/sync-broker 6.0.1 → 7.0.0
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/README.md +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +99 -30
- package/dist/index.mjs +99 -30
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -51,5 +51,7 @@ Topology and message flow:
|
|
|
51
51
|
- Dashboards `devtool-watch` tools; the broker **consolidates** watch counts so a device receives a single `watching` true/false per `(device, tool)` regardless of how many dashboards are watching.
|
|
52
52
|
- Dashboards invoke `devtool-action`; devices reply `devtool-action-result`.
|
|
53
53
|
- Devices push `devtool-sync` snapshots; the broker forwards them to dashboards.
|
|
54
|
+
- The broker emits `broker-log` entries to dashboards (handshakes, disconnect reasons, duplicate-name renames, protocol-version mismatches), buffering the last 200 so a dashboard that connects late still sees earlier failures.
|
|
55
|
+
- Dashboards emit `forget-device` to remove an **offline** device from history; offline devices also age out automatically after 24h (`User.lastSeenAt`).
|
|
54
56
|
|
|
55
57
|
> **Security:** dev-only. No authentication, CORS is open (`*`), intended for loopback / trusted LAN.
|
package/dist/index.d.mts
CHANGED
|
@@ -158,6 +158,8 @@ interface User {
|
|
|
158
158
|
connectionNonce?: string;
|
|
159
159
|
/** The socket.io reason for this client's most recent disconnect. */
|
|
160
160
|
lastDisconnectReason?: string;
|
|
161
|
+
/** Epoch ms of the last connect/reconnect/disconnect; drives age-out. */
|
|
162
|
+
lastSeenAt?: number;
|
|
161
163
|
/** Whether the device announced an active Buoy Pro license. */
|
|
162
164
|
isPro?: boolean;
|
|
163
165
|
/** The device's Buoy Pro license key, if any. Local dev connection only. */
|
package/dist/index.d.ts
CHANGED
|
@@ -158,6 +158,8 @@ interface User {
|
|
|
158
158
|
connectionNonce?: string;
|
|
159
159
|
/** The socket.io reason for this client's most recent disconnect. */
|
|
160
160
|
lastDisconnectReason?: string;
|
|
161
|
+
/** Epoch ms of the last connect/reconnect/disconnect; drives age-out. */
|
|
162
|
+
lastSeenAt?: number;
|
|
161
163
|
/** Whether the device announced an active Buoy Pro license. */
|
|
162
164
|
isPro?: boolean;
|
|
163
165
|
/** The device's Buoy Pro license key, if any. Local dev connection only. */
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,9 @@ __export(index_exports, {
|
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
40
|
|
|
41
|
+
// src/protocol.ts
|
|
42
|
+
var SYNC_PROTOCOL_VERSION = 1;
|
|
43
|
+
|
|
41
44
|
// src/socketHandle.ts
|
|
42
45
|
var users = [];
|
|
43
46
|
var allDevices = [];
|
|
@@ -49,8 +52,36 @@ var dashboardWatches = /* @__PURE__ */ new Map();
|
|
|
49
52
|
var watchCounts = /* @__PURE__ */ new Map();
|
|
50
53
|
var toWatchKey = (targetDeviceId, toolId) => JSON.stringify([targetDeviceId, toolId]);
|
|
51
54
|
var fromWatchKey = (key) => JSON.parse(key);
|
|
55
|
+
var brokerLogBuffer = [];
|
|
56
|
+
var BROKER_LOG_BUFFER_MAX = 200;
|
|
57
|
+
var MAX_OFFLINE_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
52
58
|
function socketHandle({ io }) {
|
|
53
59
|
const LOG_PREFIX = "[SERVER]";
|
|
60
|
+
function brokerLog(level, message, data) {
|
|
61
|
+
console.log(`${LOG_PREFIX} ${message}`);
|
|
62
|
+
const entry = { ts: Date.now(), level, message, data };
|
|
63
|
+
brokerLogBuffer.push(entry);
|
|
64
|
+
if (brokerLogBuffer.length > BROKER_LOG_BUFFER_MAX) {
|
|
65
|
+
brokerLogBuffer.shift();
|
|
66
|
+
}
|
|
67
|
+
forwardToDashboards("broker-log", entry);
|
|
68
|
+
}
|
|
69
|
+
function emitAllDevices() {
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
for (let i = allDevices.length - 1; i >= 0; i--) {
|
|
72
|
+
const device = allDevices[i];
|
|
73
|
+
if (!device.isConnected && device.lastSeenAt && now - device.lastSeenAt > MAX_OFFLINE_AGE_MS) {
|
|
74
|
+
allDevices.splice(i, 1);
|
|
75
|
+
if (device.deviceId) {
|
|
76
|
+
deviceCapabilities.delete(device.deviceId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
io.emit(
|
|
81
|
+
"all-devices-update",
|
|
82
|
+
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
83
|
+
);
|
|
84
|
+
}
|
|
54
85
|
function handleClose(id, reason) {
|
|
55
86
|
const user = users.find((user2) => user2.id === id);
|
|
56
87
|
const deviceId = deviceIdMap.get(id);
|
|
@@ -66,6 +97,7 @@ function socketHandle({ io }) {
|
|
|
66
97
|
if (deviceIndex !== -1) {
|
|
67
98
|
allDevices[deviceIndex].isConnected = false;
|
|
68
99
|
allDevices[deviceIndex].lastDisconnectReason = reason;
|
|
100
|
+
allDevices[deviceIndex].lastSeenAt = Date.now();
|
|
69
101
|
activeConnectionsMap.delete(deviceId);
|
|
70
102
|
}
|
|
71
103
|
}
|
|
@@ -78,13 +110,12 @@ function socketHandle({ io }) {
|
|
|
78
110
|
const wasDashboard = dashboardClients.includes(id);
|
|
79
111
|
dashboardClients = dashboardClients.filter((clientId) => clientId !== id);
|
|
80
112
|
releaseDashboardWatches(id);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"all-devices-update",
|
|
86
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
113
|
+
brokerLog(
|
|
114
|
+
wasDashboard ? "debug" : "warn",
|
|
115
|
+
`Disconnected: ${user?.deviceName || id} (${reason ?? "unknown"})`,
|
|
116
|
+
{ socketId: id, reason, wasDashboard }
|
|
87
117
|
);
|
|
118
|
+
emitAllDevices();
|
|
88
119
|
console.log(
|
|
89
120
|
`${LOG_PREFIX} Updated users list: ${users.map((u) => u.deviceName).join(", ")}`
|
|
90
121
|
);
|
|
@@ -109,8 +140,10 @@ function socketHandle({ io }) {
|
|
|
109
140
|
(device) => device.deviceId === deviceId
|
|
110
141
|
);
|
|
111
142
|
if (existingUserIndex !== -1) {
|
|
112
|
-
|
|
113
|
-
|
|
143
|
+
brokerLog(
|
|
144
|
+
"success",
|
|
145
|
+
`Reconnected: ${deviceName} (${platform || "unknown platform"})`,
|
|
146
|
+
{ socketId: id, deviceId }
|
|
114
147
|
);
|
|
115
148
|
users[existingUserIndex].id = id;
|
|
116
149
|
if (platform) {
|
|
@@ -128,6 +161,7 @@ function socketHandle({ io }) {
|
|
|
128
161
|
allDevices[existingDeviceIndex].id = id;
|
|
129
162
|
allDevices[existingDeviceIndex].isConnected = true;
|
|
130
163
|
allDevices[existingDeviceIndex].connectionNonce = connectionNonce;
|
|
164
|
+
allDevices[existingDeviceIndex].lastSeenAt = Date.now();
|
|
131
165
|
if (platform) {
|
|
132
166
|
allDevices[existingDeviceIndex].platform = platform;
|
|
133
167
|
}
|
|
@@ -145,13 +179,11 @@ function socketHandle({ io }) {
|
|
|
145
179
|
platform,
|
|
146
180
|
isConnected: true,
|
|
147
181
|
extraDeviceInfo,
|
|
148
|
-
envVariables
|
|
182
|
+
envVariables,
|
|
183
|
+
lastSeenAt: Date.now()
|
|
149
184
|
});
|
|
150
185
|
}
|
|
151
|
-
|
|
152
|
-
"all-devices-update",
|
|
153
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
154
|
-
);
|
|
186
|
+
emitAllDevices();
|
|
155
187
|
console.log(
|
|
156
188
|
`${LOG_PREFIX} Updated users list: ${users.map((u) => u.deviceName).join(", ")}`
|
|
157
189
|
);
|
|
@@ -172,7 +204,13 @@ function socketHandle({ io }) {
|
|
|
172
204
|
}
|
|
173
205
|
});
|
|
174
206
|
if (highestNumber > 0 && !isDashboard) {
|
|
175
|
-
|
|
207
|
+
const renamed = `${deviceName} #${highestNumber + 1}`;
|
|
208
|
+
brokerLog(
|
|
209
|
+
"warn",
|
|
210
|
+
`Duplicate device name "${deviceName}" \u2014 renamed to "${renamed}"`,
|
|
211
|
+
{ socketId: id, deviceId }
|
|
212
|
+
);
|
|
213
|
+
deviceName = renamed;
|
|
176
214
|
}
|
|
177
215
|
if (id) {
|
|
178
216
|
const newUser = {
|
|
@@ -183,7 +221,8 @@ function socketHandle({ io }) {
|
|
|
183
221
|
isConnected: true,
|
|
184
222
|
extraDeviceInfo,
|
|
185
223
|
envVariables,
|
|
186
|
-
connectionNonce
|
|
224
|
+
connectionNonce,
|
|
225
|
+
lastSeenAt: Date.now()
|
|
187
226
|
};
|
|
188
227
|
users.push(newUser);
|
|
189
228
|
if (deviceName !== "Dashboard" && deviceId) {
|
|
@@ -196,17 +235,16 @@ function socketHandle({ io }) {
|
|
|
196
235
|
allDevices.push({ ...newUser });
|
|
197
236
|
}
|
|
198
237
|
}
|
|
199
|
-
|
|
200
|
-
|
|
238
|
+
brokerLog(
|
|
239
|
+
isDashboard ? "debug" : "info",
|
|
240
|
+
`Connected: ${deviceName}${platform ? ` (${platform})` : ""}`,
|
|
241
|
+
{ socketId: id, deviceId }
|
|
201
242
|
);
|
|
202
243
|
if (isDashboard && id) {
|
|
203
244
|
dashboardClients.push(id);
|
|
204
245
|
console.log(`${LOG_PREFIX} Registered dashboard client with ID: ${id}`);
|
|
205
246
|
}
|
|
206
|
-
|
|
207
|
-
"all-devices-update",
|
|
208
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
209
|
-
);
|
|
247
|
+
emitAllDevices();
|
|
210
248
|
console.log(
|
|
211
249
|
`${LOG_PREFIX} Updated device list: ${users.map((u) => u.deviceName).join(", ")}`
|
|
212
250
|
);
|
|
@@ -336,6 +374,9 @@ function socketHandle({ io }) {
|
|
|
336
374
|
for (const capabilities of deviceCapabilities.values()) {
|
|
337
375
|
socket.emit("devtool-capabilities", capabilities);
|
|
338
376
|
}
|
|
377
|
+
for (const entry of brokerLogBuffer) {
|
|
378
|
+
socket.emit("broker-log", entry);
|
|
379
|
+
}
|
|
339
380
|
}
|
|
340
381
|
socket.on(
|
|
341
382
|
"device-license",
|
|
@@ -355,13 +396,12 @@ function socketHandle({ io }) {
|
|
|
355
396
|
device.licenseKey = nextIsPro ? nextKey : void 0;
|
|
356
397
|
}
|
|
357
398
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
"all-devices-update",
|
|
363
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
399
|
+
brokerLog(
|
|
400
|
+
"debug",
|
|
401
|
+
`device-license from ${user?.deviceName || socket.id}: isPro=${nextIsPro}${nextKey ? ", key set" : ""}`,
|
|
402
|
+
{ socketId: socket.id }
|
|
364
403
|
);
|
|
404
|
+
emitAllDevices();
|
|
365
405
|
}
|
|
366
406
|
);
|
|
367
407
|
socket.on(
|
|
@@ -375,6 +415,13 @@ function socketHandle({ io }) {
|
|
|
375
415
|
console.log(
|
|
376
416
|
`${LOG_PREFIX} Capabilities from ${message.persistentDeviceId}: ${message.tools.map((tool) => `${tool.toolId}@v${tool.version}`).join(", ")}`
|
|
377
417
|
);
|
|
418
|
+
if (message.protocolVersion !== SYNC_PROTOCOL_VERSION) {
|
|
419
|
+
brokerLog(
|
|
420
|
+
"warn",
|
|
421
|
+
`Protocol version mismatch from ${message.persistentDeviceId}: device speaks v${message.protocolVersion}, broker speaks v${SYNC_PROTOCOL_VERSION} \u2014 update @buoy-gg packages or Buoy Desktop`,
|
|
422
|
+
{ deviceId: message.persistentDeviceId }
|
|
423
|
+
);
|
|
424
|
+
}
|
|
378
425
|
deviceCapabilities.set(message.persistentDeviceId, message);
|
|
379
426
|
forwardToDashboards("devtool-capabilities", message);
|
|
380
427
|
for (const key of watchCounts.keys()) {
|
|
@@ -410,6 +457,31 @@ function socketHandle({ io }) {
|
|
|
410
457
|
`devtool state request ${message.toolId}`
|
|
411
458
|
);
|
|
412
459
|
});
|
|
460
|
+
socket.on("forget-device", (payload) => {
|
|
461
|
+
const targetDeviceId = payload?.deviceId;
|
|
462
|
+
if (!dashboardClients.includes(socket.id)) return;
|
|
463
|
+
if (!targetDeviceId || activeConnectionsMap.has(targetDeviceId)) return;
|
|
464
|
+
const target = allDevices.find((d) => d.deviceId === targetDeviceId);
|
|
465
|
+
users = users.filter((u) => u.deviceId !== targetDeviceId);
|
|
466
|
+
const historyIndex = allDevices.findIndex(
|
|
467
|
+
(d) => d.deviceId === targetDeviceId
|
|
468
|
+
);
|
|
469
|
+
if (historyIndex !== -1) {
|
|
470
|
+
allDevices.splice(historyIndex, 1);
|
|
471
|
+
}
|
|
472
|
+
deviceCapabilities.delete(targetDeviceId);
|
|
473
|
+
for (const [socketId, mappedDeviceId] of deviceIdMap) {
|
|
474
|
+
if (mappedDeviceId === targetDeviceId) {
|
|
475
|
+
deviceIdMap.delete(socketId);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
brokerLog(
|
|
479
|
+
"info",
|
|
480
|
+
`Forgot device: ${target?.deviceName ?? targetDeviceId}`,
|
|
481
|
+
{ deviceId: targetDeviceId }
|
|
482
|
+
);
|
|
483
|
+
emitAllDevices();
|
|
484
|
+
});
|
|
413
485
|
socket.on("devtool-action", (message) => {
|
|
414
486
|
console.log(
|
|
415
487
|
`${LOG_PREFIX} Devtool action from dashboard: ${message.toolId}.${message.action} -> ${message.targetDeviceId}`
|
|
@@ -469,9 +541,6 @@ function createBroker(options = {}) {
|
|
|
469
541
|
});
|
|
470
542
|
return { io, httpServer, port, close };
|
|
471
543
|
}
|
|
472
|
-
|
|
473
|
-
// src/protocol.ts
|
|
474
|
-
var SYNC_PROTOCOL_VERSION = 1;
|
|
475
544
|
// Annotate the CommonJS export names for ESM import in node:
|
|
476
545
|
0 && (module.exports = {
|
|
477
546
|
DEFAULT_BROKER_PORT,
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/protocol.ts
|
|
2
|
+
var SYNC_PROTOCOL_VERSION = 1;
|
|
3
|
+
|
|
1
4
|
// src/socketHandle.ts
|
|
2
5
|
var users = [];
|
|
3
6
|
var allDevices = [];
|
|
@@ -9,8 +12,36 @@ var dashboardWatches = /* @__PURE__ */ new Map();
|
|
|
9
12
|
var watchCounts = /* @__PURE__ */ new Map();
|
|
10
13
|
var toWatchKey = (targetDeviceId, toolId) => JSON.stringify([targetDeviceId, toolId]);
|
|
11
14
|
var fromWatchKey = (key) => JSON.parse(key);
|
|
15
|
+
var brokerLogBuffer = [];
|
|
16
|
+
var BROKER_LOG_BUFFER_MAX = 200;
|
|
17
|
+
var MAX_OFFLINE_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12
18
|
function socketHandle({ io }) {
|
|
13
19
|
const LOG_PREFIX = "[SERVER]";
|
|
20
|
+
function brokerLog(level, message, data) {
|
|
21
|
+
console.log(`${LOG_PREFIX} ${message}`);
|
|
22
|
+
const entry = { ts: Date.now(), level, message, data };
|
|
23
|
+
brokerLogBuffer.push(entry);
|
|
24
|
+
if (brokerLogBuffer.length > BROKER_LOG_BUFFER_MAX) {
|
|
25
|
+
brokerLogBuffer.shift();
|
|
26
|
+
}
|
|
27
|
+
forwardToDashboards("broker-log", entry);
|
|
28
|
+
}
|
|
29
|
+
function emitAllDevices() {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
for (let i = allDevices.length - 1; i >= 0; i--) {
|
|
32
|
+
const device = allDevices[i];
|
|
33
|
+
if (!device.isConnected && device.lastSeenAt && now - device.lastSeenAt > MAX_OFFLINE_AGE_MS) {
|
|
34
|
+
allDevices.splice(i, 1);
|
|
35
|
+
if (device.deviceId) {
|
|
36
|
+
deviceCapabilities.delete(device.deviceId);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
io.emit(
|
|
41
|
+
"all-devices-update",
|
|
42
|
+
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
43
|
+
);
|
|
44
|
+
}
|
|
14
45
|
function handleClose(id, reason) {
|
|
15
46
|
const user = users.find((user2) => user2.id === id);
|
|
16
47
|
const deviceId = deviceIdMap.get(id);
|
|
@@ -26,6 +57,7 @@ function socketHandle({ io }) {
|
|
|
26
57
|
if (deviceIndex !== -1) {
|
|
27
58
|
allDevices[deviceIndex].isConnected = false;
|
|
28
59
|
allDevices[deviceIndex].lastDisconnectReason = reason;
|
|
60
|
+
allDevices[deviceIndex].lastSeenAt = Date.now();
|
|
29
61
|
activeConnectionsMap.delete(deviceId);
|
|
30
62
|
}
|
|
31
63
|
}
|
|
@@ -38,13 +70,12 @@ function socketHandle({ io }) {
|
|
|
38
70
|
const wasDashboard = dashboardClients.includes(id);
|
|
39
71
|
dashboardClients = dashboardClients.filter((clientId) => clientId !== id);
|
|
40
72
|
releaseDashboardWatches(id);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"all-devices-update",
|
|
46
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
73
|
+
brokerLog(
|
|
74
|
+
wasDashboard ? "debug" : "warn",
|
|
75
|
+
`Disconnected: ${user?.deviceName || id} (${reason ?? "unknown"})`,
|
|
76
|
+
{ socketId: id, reason, wasDashboard }
|
|
47
77
|
);
|
|
78
|
+
emitAllDevices();
|
|
48
79
|
console.log(
|
|
49
80
|
`${LOG_PREFIX} Updated users list: ${users.map((u) => u.deviceName).join(", ")}`
|
|
50
81
|
);
|
|
@@ -69,8 +100,10 @@ function socketHandle({ io }) {
|
|
|
69
100
|
(device) => device.deviceId === deviceId
|
|
70
101
|
);
|
|
71
102
|
if (existingUserIndex !== -1) {
|
|
72
|
-
|
|
73
|
-
|
|
103
|
+
brokerLog(
|
|
104
|
+
"success",
|
|
105
|
+
`Reconnected: ${deviceName} (${platform || "unknown platform"})`,
|
|
106
|
+
{ socketId: id, deviceId }
|
|
74
107
|
);
|
|
75
108
|
users[existingUserIndex].id = id;
|
|
76
109
|
if (platform) {
|
|
@@ -88,6 +121,7 @@ function socketHandle({ io }) {
|
|
|
88
121
|
allDevices[existingDeviceIndex].id = id;
|
|
89
122
|
allDevices[existingDeviceIndex].isConnected = true;
|
|
90
123
|
allDevices[existingDeviceIndex].connectionNonce = connectionNonce;
|
|
124
|
+
allDevices[existingDeviceIndex].lastSeenAt = Date.now();
|
|
91
125
|
if (platform) {
|
|
92
126
|
allDevices[existingDeviceIndex].platform = platform;
|
|
93
127
|
}
|
|
@@ -105,13 +139,11 @@ function socketHandle({ io }) {
|
|
|
105
139
|
platform,
|
|
106
140
|
isConnected: true,
|
|
107
141
|
extraDeviceInfo,
|
|
108
|
-
envVariables
|
|
142
|
+
envVariables,
|
|
143
|
+
lastSeenAt: Date.now()
|
|
109
144
|
});
|
|
110
145
|
}
|
|
111
|
-
|
|
112
|
-
"all-devices-update",
|
|
113
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
114
|
-
);
|
|
146
|
+
emitAllDevices();
|
|
115
147
|
console.log(
|
|
116
148
|
`${LOG_PREFIX} Updated users list: ${users.map((u) => u.deviceName).join(", ")}`
|
|
117
149
|
);
|
|
@@ -132,7 +164,13 @@ function socketHandle({ io }) {
|
|
|
132
164
|
}
|
|
133
165
|
});
|
|
134
166
|
if (highestNumber > 0 && !isDashboard) {
|
|
135
|
-
|
|
167
|
+
const renamed = `${deviceName} #${highestNumber + 1}`;
|
|
168
|
+
brokerLog(
|
|
169
|
+
"warn",
|
|
170
|
+
`Duplicate device name "${deviceName}" \u2014 renamed to "${renamed}"`,
|
|
171
|
+
{ socketId: id, deviceId }
|
|
172
|
+
);
|
|
173
|
+
deviceName = renamed;
|
|
136
174
|
}
|
|
137
175
|
if (id) {
|
|
138
176
|
const newUser = {
|
|
@@ -143,7 +181,8 @@ function socketHandle({ io }) {
|
|
|
143
181
|
isConnected: true,
|
|
144
182
|
extraDeviceInfo,
|
|
145
183
|
envVariables,
|
|
146
|
-
connectionNonce
|
|
184
|
+
connectionNonce,
|
|
185
|
+
lastSeenAt: Date.now()
|
|
147
186
|
};
|
|
148
187
|
users.push(newUser);
|
|
149
188
|
if (deviceName !== "Dashboard" && deviceId) {
|
|
@@ -156,17 +195,16 @@ function socketHandle({ io }) {
|
|
|
156
195
|
allDevices.push({ ...newUser });
|
|
157
196
|
}
|
|
158
197
|
}
|
|
159
|
-
|
|
160
|
-
|
|
198
|
+
brokerLog(
|
|
199
|
+
isDashboard ? "debug" : "info",
|
|
200
|
+
`Connected: ${deviceName}${platform ? ` (${platform})` : ""}`,
|
|
201
|
+
{ socketId: id, deviceId }
|
|
161
202
|
);
|
|
162
203
|
if (isDashboard && id) {
|
|
163
204
|
dashboardClients.push(id);
|
|
164
205
|
console.log(`${LOG_PREFIX} Registered dashboard client with ID: ${id}`);
|
|
165
206
|
}
|
|
166
|
-
|
|
167
|
-
"all-devices-update",
|
|
168
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
169
|
-
);
|
|
207
|
+
emitAllDevices();
|
|
170
208
|
console.log(
|
|
171
209
|
`${LOG_PREFIX} Updated device list: ${users.map((u) => u.deviceName).join(", ")}`
|
|
172
210
|
);
|
|
@@ -296,6 +334,9 @@ function socketHandle({ io }) {
|
|
|
296
334
|
for (const capabilities of deviceCapabilities.values()) {
|
|
297
335
|
socket.emit("devtool-capabilities", capabilities);
|
|
298
336
|
}
|
|
337
|
+
for (const entry of brokerLogBuffer) {
|
|
338
|
+
socket.emit("broker-log", entry);
|
|
339
|
+
}
|
|
299
340
|
}
|
|
300
341
|
socket.on(
|
|
301
342
|
"device-license",
|
|
@@ -315,13 +356,12 @@ function socketHandle({ io }) {
|
|
|
315
356
|
device.licenseKey = nextIsPro ? nextKey : void 0;
|
|
316
357
|
}
|
|
317
358
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
"all-devices-update",
|
|
323
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
359
|
+
brokerLog(
|
|
360
|
+
"debug",
|
|
361
|
+
`device-license from ${user?.deviceName || socket.id}: isPro=${nextIsPro}${nextKey ? ", key set" : ""}`,
|
|
362
|
+
{ socketId: socket.id }
|
|
324
363
|
);
|
|
364
|
+
emitAllDevices();
|
|
325
365
|
}
|
|
326
366
|
);
|
|
327
367
|
socket.on(
|
|
@@ -335,6 +375,13 @@ function socketHandle({ io }) {
|
|
|
335
375
|
console.log(
|
|
336
376
|
`${LOG_PREFIX} Capabilities from ${message.persistentDeviceId}: ${message.tools.map((tool) => `${tool.toolId}@v${tool.version}`).join(", ")}`
|
|
337
377
|
);
|
|
378
|
+
if (message.protocolVersion !== SYNC_PROTOCOL_VERSION) {
|
|
379
|
+
brokerLog(
|
|
380
|
+
"warn",
|
|
381
|
+
`Protocol version mismatch from ${message.persistentDeviceId}: device speaks v${message.protocolVersion}, broker speaks v${SYNC_PROTOCOL_VERSION} \u2014 update @buoy-gg packages or Buoy Desktop`,
|
|
382
|
+
{ deviceId: message.persistentDeviceId }
|
|
383
|
+
);
|
|
384
|
+
}
|
|
338
385
|
deviceCapabilities.set(message.persistentDeviceId, message);
|
|
339
386
|
forwardToDashboards("devtool-capabilities", message);
|
|
340
387
|
for (const key of watchCounts.keys()) {
|
|
@@ -370,6 +417,31 @@ function socketHandle({ io }) {
|
|
|
370
417
|
`devtool state request ${message.toolId}`
|
|
371
418
|
);
|
|
372
419
|
});
|
|
420
|
+
socket.on("forget-device", (payload) => {
|
|
421
|
+
const targetDeviceId = payload?.deviceId;
|
|
422
|
+
if (!dashboardClients.includes(socket.id)) return;
|
|
423
|
+
if (!targetDeviceId || activeConnectionsMap.has(targetDeviceId)) return;
|
|
424
|
+
const target = allDevices.find((d) => d.deviceId === targetDeviceId);
|
|
425
|
+
users = users.filter((u) => u.deviceId !== targetDeviceId);
|
|
426
|
+
const historyIndex = allDevices.findIndex(
|
|
427
|
+
(d) => d.deviceId === targetDeviceId
|
|
428
|
+
);
|
|
429
|
+
if (historyIndex !== -1) {
|
|
430
|
+
allDevices.splice(historyIndex, 1);
|
|
431
|
+
}
|
|
432
|
+
deviceCapabilities.delete(targetDeviceId);
|
|
433
|
+
for (const [socketId, mappedDeviceId] of deviceIdMap) {
|
|
434
|
+
if (mappedDeviceId === targetDeviceId) {
|
|
435
|
+
deviceIdMap.delete(socketId);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
brokerLog(
|
|
439
|
+
"info",
|
|
440
|
+
`Forgot device: ${target?.deviceName ?? targetDeviceId}`,
|
|
441
|
+
{ deviceId: targetDeviceId }
|
|
442
|
+
);
|
|
443
|
+
emitAllDevices();
|
|
444
|
+
});
|
|
373
445
|
socket.on("devtool-action", (message) => {
|
|
374
446
|
console.log(
|
|
375
447
|
`${LOG_PREFIX} Devtool action from dashboard: ${message.toolId}.${message.action} -> ${message.targetDeviceId}`
|
|
@@ -429,9 +501,6 @@ function createBroker(options = {}) {
|
|
|
429
501
|
});
|
|
430
502
|
return { io, httpServer, port, close };
|
|
431
503
|
}
|
|
432
|
-
|
|
433
|
-
// src/protocol.ts
|
|
434
|
-
var SYNC_PROTOCOL_VERSION = 1;
|
|
435
504
|
export {
|
|
436
505
|
DEFAULT_BROKER_PORT,
|
|
437
506
|
SOCKET_CONFIG,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/sync-broker",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Headless Socket.IO broker for the Buoy devtool sync protocol (v1) — routes messages between devices and dashboard/MCP clients",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/
|
|
40
|
-
"directory": "packages/sync-broker"
|
|
39
|
+
"url": "https://github.com/Buoy-gg/buoy.git"
|
|
41
40
|
},
|
|
42
41
|
"publishConfig": {
|
|
43
42
|
"access": "public",
|
|
44
43
|
"tag": "latest"
|
|
45
44
|
},
|
|
45
|
+
"homepage": "https://buoy.gg",
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup",
|
|
48
48
|
"typecheck": "tsc --noEmit",
|