@buoy-gg/sync-broker 7.0.36 → 7.0.39
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 +27 -34
- package/dist/index.d.mts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +109 -27
- package/dist/index.mjs +109 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,57 +1,50 @@
|
|
|
1
1
|
# @buoy-gg/sync-broker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Host the Socket.IO message router used by Buoy Desktop and MCP. It routes device capabilities, watched snapshots, and remote actions between device and dashboard clients. Most users should run Desktop or MCP instead of hosting this package directly.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @buoy-gg/sync-broker
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This is a Node host package. Use the exported `createBroker`, `socketHandle`, and protocol types when implementing a custom host.
|
|
9
12
|
|
|
10
|
-
##
|
|
13
|
+
## Host a broker
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
The host must independently validate its Buoy account and supply admission through `canUseDesktop`. The default denies access. This integration sketch expects your host's existing `account` object; `hasAccess()` must reflect current verified admission, not merely the presence of a key:
|
|
13
16
|
|
|
14
17
|
```ts
|
|
15
18
|
import { createBroker, DEFAULT_BROKER_PORT } from "@buoy-gg/sync-broker";
|
|
16
19
|
|
|
17
20
|
const broker = createBroker({
|
|
18
|
-
port: DEFAULT_BROKER_PORT,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Another broker (e.g. the desktop app) already owns the port —
|
|
23
|
-
// connect to it as a client instead of hosting your own.
|
|
24
|
-
}
|
|
25
|
-
},
|
|
21
|
+
port: DEFAULT_BROKER_PORT,
|
|
22
|
+
canUseDesktop: () => account.hasAccess(),
|
|
23
|
+
onListening: (port) => console.log(`Broker listening on ${port}`),
|
|
24
|
+
onError: (error) => console.error(error),
|
|
26
25
|
});
|
|
27
26
|
|
|
28
|
-
//
|
|
27
|
+
// Call when your account lifecycle changes admission:
|
|
28
|
+
broker.refreshAccess();
|
|
29
|
+
|
|
30
|
+
// Call during host shutdown:
|
|
29
31
|
await broker.close();
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
Keep account revalidation and expiry handling active for the host's lifetime. Refresh routing when access changes. Device and client admission is separate from the host callback. A listening HTTP endpoint does not prove that a device has been admitted.
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
import { Server } from "socket.io";
|
|
36
|
-
import { socketHandle } from "@buoy-gg/sync-broker";
|
|
36
|
+
Try connecting to an existing broker before starting another. If startup reports EADDRINUSE, connect to the broker already using that port or resolve the conflict. The default port is `42831`.
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
## Existing HTTP server
|
|
39
|
+
|
|
40
|
+
For an existing Socket.IO server, use `socketHandle({ io, canUseDesktop })` and retain its returned `refreshAccess` function. Your host owns the HTTP server, Socket.IO options, account lifecycle, and shutdown. Do not attach routing twice to the same server.
|
|
41
41
|
|
|
42
|
-
##
|
|
42
|
+
## Message flow
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Devices announce capabilities. Dashboard watch requests control adapter subscriptions; the broker consolidates those requests. Device snapshots flow to watching clients, while named actions and their results travel in the opposite directions. Broker logs help diagnose admission, connection, and protocol problems. Offline device history is in memory and can be forgotten or age out.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
## Network and verification
|
|
47
47
|
|
|
48
|
-
-
|
|
49
|
-
- Dashboard clients connect with `deviceName: "Dashboard"` (exact, no suffix).
|
|
50
|
-
- Devices announce `devtool-capabilities`; the broker replays them to dashboards.
|
|
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
|
-
- Dashboards invoke `devtool-action`; devices reply `devtool-action-result`.
|
|
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`).
|
|
48
|
+
The standalone server listens without a loopback-only host restriction. Default CORS allows any origin; CORS is not client authentication. Use a trusted development network and review access controls before exposing a custom host beyond it. Account admission does not establish app-specific authorization for every remote action.
|
|
56
49
|
|
|
57
|
-
|
|
50
|
+
Test with a verified host and device account: connect, watch a known tool, and confirm its snapshot. Then revoke host admission and verify that access closes. Reconnect after restoring valid access and check that watch state resumes. See the [Desktop guide](https://buoy.gg/buoy/latest/docs/desktop) and [MCP guide](https://buoy.gg/buoy/latest/docs/mcp) for supported end-user hosts.
|
package/dist/index.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import http from 'http';
|
|
|
3
3
|
|
|
4
4
|
type DefaultEventsMap = Record<string, (...args: any[]) => void>;
|
|
5
5
|
interface Props {
|
|
6
|
+
canUseDesktop?: () => boolean;
|
|
6
7
|
io: Server<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>;
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
@@ -12,7 +13,9 @@ interface Props {
|
|
|
12
13
|
* - Query state synchronization
|
|
13
14
|
* - Action forwarding
|
|
14
15
|
*/
|
|
15
|
-
declare function socketHandle({ io }: Props):
|
|
16
|
+
declare function socketHandle({ io, canUseDesktop }: Props): {
|
|
17
|
+
refreshAccess(): void;
|
|
18
|
+
};
|
|
16
19
|
|
|
17
20
|
/** Default port the Buoy sync broker listens on. */
|
|
18
21
|
declare const DEFAULT_BROKER_PORT = 42831;
|
|
@@ -24,6 +27,8 @@ declare const SOCKET_CONFIG: Partial<ServerOptions>;
|
|
|
24
27
|
interface CreateBrokerOptions {
|
|
25
28
|
/** Port to listen on. @default DEFAULT_BROKER_PORT (42831) */
|
|
26
29
|
port?: number;
|
|
30
|
+
/** The host must supply its independently verified account admission. */
|
|
31
|
+
canUseDesktop?: () => boolean;
|
|
27
32
|
/**
|
|
28
33
|
* Extra Socket.IO server options merged over {@link SOCKET_CONFIG}. Use to
|
|
29
34
|
* override CORS, transports, etc. for a specific host.
|
|
@@ -48,6 +53,7 @@ interface BrokerHandle {
|
|
|
48
53
|
port: number;
|
|
49
54
|
/** Stop the broker and release the port. */
|
|
50
55
|
close: () => Promise<void>;
|
|
56
|
+
refreshAccess: () => void;
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
53
59
|
* Create and start a headless Buoy sync broker: an HTTP + Socket.IO server with
|
|
@@ -153,10 +159,17 @@ interface DevToolActionMessage {
|
|
|
153
159
|
params?: unknown;
|
|
154
160
|
}
|
|
155
161
|
/**
|
|
156
|
-
* A client's
|
|
157
|
-
*
|
|
162
|
+
* A client's tier. Mirrors `BuoyTier` in @buoy-gg/license; redeclared here
|
|
163
|
+
* because the broker is a standalone node process with no client deps.
|
|
158
164
|
*/
|
|
159
|
-
type BuoyTier = "
|
|
165
|
+
type BuoyTier = "free" | "pro";
|
|
166
|
+
/**
|
|
167
|
+
* What the broker knows about a device's account: its tier once the device
|
|
168
|
+
* has announced an admitted account, or "locked" until then. "locked" is not
|
|
169
|
+
* a tier — it is the broker's default for every socket, and what a device
|
|
170
|
+
* announces when its own account access ends.
|
|
171
|
+
*/
|
|
172
|
+
type DeviceTier = BuoyTier | "locked";
|
|
160
173
|
/**
|
|
161
174
|
* A connected client (device or dashboard) as tracked by the broker.
|
|
162
175
|
* Mirrors the desktop dashboard's `User` type; the dashboard UI carries a few
|
|
@@ -194,12 +207,12 @@ interface User {
|
|
|
194
207
|
* The device's access tier. Absent from clients older than the tier split —
|
|
195
208
|
* derive it from `isPro` in that case (see `tierFromLicensePayload`).
|
|
196
209
|
*/
|
|
197
|
-
tier?:
|
|
210
|
+
tier?: DeviceTier;
|
|
198
211
|
/**
|
|
199
212
|
* The device's Buoy license key, if any. Local dev connection only.
|
|
200
213
|
*
|
|
201
214
|
* Forwarded for free keys too, not just Pro ones, so the desktop dashboard
|
|
202
|
-
* can mirror the device's tier instead of
|
|
215
|
+
* can mirror the device's tier instead of showing it locked while the
|
|
203
216
|
* phone it is attached to is signed in.
|
|
204
217
|
*/
|
|
205
218
|
licenseKey?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import http from 'http';
|
|
|
3
3
|
|
|
4
4
|
type DefaultEventsMap = Record<string, (...args: any[]) => void>;
|
|
5
5
|
interface Props {
|
|
6
|
+
canUseDesktop?: () => boolean;
|
|
6
7
|
io: Server<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>;
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
@@ -12,7 +13,9 @@ interface Props {
|
|
|
12
13
|
* - Query state synchronization
|
|
13
14
|
* - Action forwarding
|
|
14
15
|
*/
|
|
15
|
-
declare function socketHandle({ io }: Props):
|
|
16
|
+
declare function socketHandle({ io, canUseDesktop }: Props): {
|
|
17
|
+
refreshAccess(): void;
|
|
18
|
+
};
|
|
16
19
|
|
|
17
20
|
/** Default port the Buoy sync broker listens on. */
|
|
18
21
|
declare const DEFAULT_BROKER_PORT = 42831;
|
|
@@ -24,6 +27,8 @@ declare const SOCKET_CONFIG: Partial<ServerOptions>;
|
|
|
24
27
|
interface CreateBrokerOptions {
|
|
25
28
|
/** Port to listen on. @default DEFAULT_BROKER_PORT (42831) */
|
|
26
29
|
port?: number;
|
|
30
|
+
/** The host must supply its independently verified account admission. */
|
|
31
|
+
canUseDesktop?: () => boolean;
|
|
27
32
|
/**
|
|
28
33
|
* Extra Socket.IO server options merged over {@link SOCKET_CONFIG}. Use to
|
|
29
34
|
* override CORS, transports, etc. for a specific host.
|
|
@@ -48,6 +53,7 @@ interface BrokerHandle {
|
|
|
48
53
|
port: number;
|
|
49
54
|
/** Stop the broker and release the port. */
|
|
50
55
|
close: () => Promise<void>;
|
|
56
|
+
refreshAccess: () => void;
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
53
59
|
* Create and start a headless Buoy sync broker: an HTTP + Socket.IO server with
|
|
@@ -153,10 +159,17 @@ interface DevToolActionMessage {
|
|
|
153
159
|
params?: unknown;
|
|
154
160
|
}
|
|
155
161
|
/**
|
|
156
|
-
* A client's
|
|
157
|
-
*
|
|
162
|
+
* A client's tier. Mirrors `BuoyTier` in @buoy-gg/license; redeclared here
|
|
163
|
+
* because the broker is a standalone node process with no client deps.
|
|
158
164
|
*/
|
|
159
|
-
type BuoyTier = "
|
|
165
|
+
type BuoyTier = "free" | "pro";
|
|
166
|
+
/**
|
|
167
|
+
* What the broker knows about a device's account: its tier once the device
|
|
168
|
+
* has announced an admitted account, or "locked" until then. "locked" is not
|
|
169
|
+
* a tier — it is the broker's default for every socket, and what a device
|
|
170
|
+
* announces when its own account access ends.
|
|
171
|
+
*/
|
|
172
|
+
type DeviceTier = BuoyTier | "locked";
|
|
160
173
|
/**
|
|
161
174
|
* A connected client (device or dashboard) as tracked by the broker.
|
|
162
175
|
* Mirrors the desktop dashboard's `User` type; the dashboard UI carries a few
|
|
@@ -194,12 +207,12 @@ interface User {
|
|
|
194
207
|
* The device's access tier. Absent from clients older than the tier split —
|
|
195
208
|
* derive it from `isPro` in that case (see `tierFromLicensePayload`).
|
|
196
209
|
*/
|
|
197
|
-
tier?:
|
|
210
|
+
tier?: DeviceTier;
|
|
198
211
|
/**
|
|
199
212
|
* The device's Buoy license key, if any. Local dev connection only.
|
|
200
213
|
*
|
|
201
214
|
* Forwarded for free keys too, not just Pro ones, so the desktop dashboard
|
|
202
|
-
* can mirror the device's tier instead of
|
|
215
|
+
* can mirror the device's tier instead of showing it locked while the
|
|
203
216
|
* phone it is attached to is signed in.
|
|
204
217
|
*/
|
|
205
218
|
licenseKey?: string;
|
package/dist/index.js
CHANGED
|
@@ -41,10 +41,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
41
41
|
// src/protocol.ts
|
|
42
42
|
var SYNC_PROTOCOL_VERSION = 1;
|
|
43
43
|
function tierFromLicensePayload(payload) {
|
|
44
|
-
if (payload?.tier === "pro" || payload?.tier === "free"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return payload?.isPro === "1" || payload?.isPro === true ? "pro" : "anonymous";
|
|
44
|
+
if (payload?.tier === "pro" || payload?.tier === "free") return payload.tier;
|
|
45
|
+
if (payload?.tier === "locked" || payload?.tier === "anonymous") return "locked";
|
|
46
|
+
return payload?.isPro === "1" || payload?.isPro === true ? "pro" : "locked";
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
// src/watchRegistry.ts
|
|
@@ -130,19 +129,31 @@ var WatchRegistry = class {
|
|
|
130
129
|
};
|
|
131
130
|
|
|
132
131
|
// src/socketHandle.ts
|
|
133
|
-
var users = [];
|
|
134
|
-
var allDevices = [];
|
|
135
|
-
var dashboardClients = [];
|
|
136
|
-
var deviceIdMap = /* @__PURE__ */ new Map();
|
|
137
|
-
var activeConnectionsMap = /* @__PURE__ */ new Map();
|
|
138
|
-
var instanceIdMap = /* @__PURE__ */ new Map();
|
|
139
|
-
var deviceCapabilities = /* @__PURE__ */ new Map();
|
|
140
|
-
var watches = new WatchRegistry();
|
|
141
|
-
var brokerLogBuffer = [];
|
|
142
132
|
var BROKER_LOG_BUFFER_MAX = 200;
|
|
143
133
|
var MAX_OFFLINE_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
144
|
-
function
|
|
134
|
+
function redactAccountKeys(raw) {
|
|
135
|
+
if (!raw) return void 0;
|
|
136
|
+
try {
|
|
137
|
+
const value = JSON.parse(raw);
|
|
138
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
139
|
+
for (const key of ["BUOY_LICENSE_KEY", "BUOY_KEY", "EXPO_PUBLIC_BUOY_KEY", "VITE_BUOY_KEY", "REACT_APP_BUOY_KEY", "licenseKey"]) delete value[key];
|
|
140
|
+
return JSON.stringify(value);
|
|
141
|
+
} catch {
|
|
142
|
+
return void 0;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function socketHandle({ io, canUseDesktop = () => false }) {
|
|
145
146
|
const LOG_PREFIX = "[SERVER]";
|
|
147
|
+
let users = [];
|
|
148
|
+
const allDevices = [];
|
|
149
|
+
let dashboardClients = [];
|
|
150
|
+
const deviceIdMap = /* @__PURE__ */ new Map();
|
|
151
|
+
const socketTiers = /* @__PURE__ */ new Map();
|
|
152
|
+
const activeConnectionsMap = /* @__PURE__ */ new Map();
|
|
153
|
+
const instanceIdMap = /* @__PURE__ */ new Map();
|
|
154
|
+
const deviceCapabilities = /* @__PURE__ */ new Map();
|
|
155
|
+
let watches = new WatchRegistry();
|
|
156
|
+
const brokerLogBuffer = [];
|
|
146
157
|
function brokerLog(level, message, data) {
|
|
147
158
|
console.log(`${LOG_PREFIX} ${message}`);
|
|
148
159
|
const entry = { ts: Date.now(), level, message, data };
|
|
@@ -162,6 +173,20 @@ function socketHandle({ io }) {
|
|
|
162
173
|
return instances.size;
|
|
163
174
|
}
|
|
164
175
|
function emitAllDevices() {
|
|
176
|
+
if (!canUseDesktop()) {
|
|
177
|
+
io.emit("all-devices-update", []);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
for (const device of [...users, ...allDevices]) {
|
|
181
|
+
if (device.deviceName === "Dashboard" || !device.isConnected) continue;
|
|
182
|
+
const tier = socketTiers.get(device.id) ?? "locked";
|
|
183
|
+
device.tier = tier === "pro" ? "pro" : tier === "free" ? "free" : "locked";
|
|
184
|
+
device.isPro = tier === "pro";
|
|
185
|
+
if (tier !== "free" && tier !== "pro") {
|
|
186
|
+
delete device.envVariables;
|
|
187
|
+
delete device.extraDeviceInfo;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
165
190
|
const now = Date.now();
|
|
166
191
|
for (const device of allDevices) {
|
|
167
192
|
if (!device.deviceId) continue;
|
|
@@ -191,7 +216,7 @@ function socketHandle({ io }) {
|
|
|
191
216
|
}
|
|
192
217
|
io.emit(
|
|
193
218
|
"all-devices-update",
|
|
194
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
219
|
+
allDevices.filter((device) => device.deviceName !== "Dashboard").map(({ licenseKey: _key, ...device }) => device)
|
|
195
220
|
);
|
|
196
221
|
}
|
|
197
222
|
function handleClose(id, reason) {
|
|
@@ -199,6 +224,7 @@ function socketHandle({ io }) {
|
|
|
199
224
|
const deviceId = deviceIdMap.get(id);
|
|
200
225
|
deviceIdMap.delete(id);
|
|
201
226
|
instanceIdMap.delete(id);
|
|
227
|
+
socketTiers.delete(id);
|
|
202
228
|
let survivor;
|
|
203
229
|
if (deviceId) {
|
|
204
230
|
for (const [socketId, mappedDeviceId] of deviceIdMap) {
|
|
@@ -386,9 +412,11 @@ function socketHandle({ io }) {
|
|
|
386
412
|
}
|
|
387
413
|
function handleUserMessage(message, deviceName) {
|
|
388
414
|
console.log(`${LOG_PREFIX} Message from ${deviceName}: ${message}`);
|
|
415
|
+
if (!canUseDesktop()) return;
|
|
389
416
|
io.emit("message", `Device ${deviceName} sent message: ${message}`);
|
|
390
417
|
}
|
|
391
418
|
function forwardToDashboards(event, message) {
|
|
419
|
+
if (!canUseDesktop()) return;
|
|
392
420
|
dashboardClients = dashboardClients.filter(
|
|
393
421
|
(id) => io.sockets.sockets.has(id)
|
|
394
422
|
);
|
|
@@ -400,6 +428,11 @@ function socketHandle({ io }) {
|
|
|
400
428
|
}
|
|
401
429
|
}
|
|
402
430
|
function withTargetUsers(targetDeviceId, action, actionName) {
|
|
431
|
+
if (!canUseDesktop()) return;
|
|
432
|
+
const admitted = (user) => {
|
|
433
|
+
const tier = socketTiers.get(user.id);
|
|
434
|
+
return tier === "free" || tier === "pro";
|
|
435
|
+
};
|
|
403
436
|
const deviceName = getDeviceFromDeviceId(targetDeviceId)?.deviceName;
|
|
404
437
|
if (targetDeviceId === "No devices available") {
|
|
405
438
|
console.log(
|
|
@@ -415,10 +448,10 @@ function socketHandle({ io }) {
|
|
|
415
448
|
console.log(
|
|
416
449
|
`${LOG_PREFIX} Sending to ${deviceUsers.length} devices: ${deviceUsers.map((u) => u.deviceName).join(", ")}`
|
|
417
450
|
);
|
|
418
|
-
deviceUsers.forEach(action);
|
|
451
|
+
deviceUsers.filter(admitted).forEach(action);
|
|
419
452
|
} else {
|
|
420
453
|
const targetUser = users.find((user) => user.deviceId === targetDeviceId);
|
|
421
|
-
if (targetUser) {
|
|
454
|
+
if (targetUser && admitted(targetUser)) {
|
|
422
455
|
console.log(
|
|
423
456
|
`${LOG_PREFIX} Sending to target device ${targetUser.deviceName} (${targetUser.deviceId}) targetDeviceId: ${targetDeviceId}`
|
|
424
457
|
);
|
|
@@ -469,6 +502,27 @@ function socketHandle({ io }) {
|
|
|
469
502
|
}
|
|
470
503
|
}
|
|
471
504
|
io.on("connection", (socket) => {
|
|
505
|
+
const sendAccess = () => socket.emit("buoy:access", { protocol: 1, ready: canUseDesktop() });
|
|
506
|
+
sendAccess();
|
|
507
|
+
socket.on("buoy:access:request", sendAccess);
|
|
508
|
+
socket.use((_packet, next) => {
|
|
509
|
+
if (_packet[0] === "buoy:access:request") {
|
|
510
|
+
next();
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (!canUseDesktop()) {
|
|
514
|
+
sendAccess();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const control = _packet[0] === "device-license";
|
|
518
|
+
if (control) {
|
|
519
|
+
const payload = _packet[1] ?? {};
|
|
520
|
+
socketTiers.set(socket.id, tierFromLicensePayload(payload));
|
|
521
|
+
}
|
|
522
|
+
const deviceAdmitted = socketTiers.get(socket.id) === "free" || socketTiers.get(socket.id) === "pro";
|
|
523
|
+
if (control || dashboardClients.includes(socket.id) || deviceAdmitted) next();
|
|
524
|
+
else sendAccess();
|
|
525
|
+
});
|
|
472
526
|
const {
|
|
473
527
|
deviceName,
|
|
474
528
|
deviceId,
|
|
@@ -482,18 +536,18 @@ function socketHandle({ io }) {
|
|
|
482
536
|
instanceIdMap.set(socket.id, instanceId);
|
|
483
537
|
}
|
|
484
538
|
console.log(
|
|
485
|
-
`${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}
|
|
539
|
+
`${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}`
|
|
486
540
|
);
|
|
487
541
|
addNewUser({
|
|
488
542
|
id: socket.id,
|
|
489
543
|
deviceName: deviceName || "Unknown Device Name",
|
|
490
544
|
deviceId,
|
|
491
545
|
platform,
|
|
492
|
-
extraDeviceInfo,
|
|
493
|
-
envVariables,
|
|
546
|
+
extraDeviceInfo: void 0,
|
|
547
|
+
envVariables: void 0,
|
|
494
548
|
connectionNonce
|
|
495
549
|
});
|
|
496
|
-
if (deviceName === "Dashboard") {
|
|
550
|
+
if (canUseDesktop() && deviceName === "Dashboard") {
|
|
497
551
|
for (const capabilities of deviceCapabilities.values()) {
|
|
498
552
|
socket.emit("devtool-capabilities", capabilities);
|
|
499
553
|
}
|
|
@@ -506,25 +560,32 @@ function socketHandle({ io }) {
|
|
|
506
560
|
(payload) => {
|
|
507
561
|
const nextTier = tierFromLicensePayload(payload ?? {});
|
|
508
562
|
const nextIsPro = nextTier === "pro";
|
|
509
|
-
|
|
563
|
+
socketTiers.set(socket.id, nextTier);
|
|
510
564
|
const user = users.find((u) => u.id === socket.id);
|
|
511
565
|
if (user) {
|
|
512
566
|
user.isPro = nextIsPro;
|
|
513
567
|
user.tier = nextTier;
|
|
514
|
-
user.licenseKey
|
|
568
|
+
delete user.licenseKey;
|
|
515
569
|
}
|
|
516
570
|
const did = user?.deviceId || deviceIdMap.get(socket.id);
|
|
571
|
+
if (nextTier !== "locked") {
|
|
572
|
+
for (const record of [...users, ...allDevices].filter((record2) => record2.id === socket.id)) {
|
|
573
|
+
record.extraDeviceInfo = redactAccountKeys(extraDeviceInfo);
|
|
574
|
+
record.envVariables = redactAccountKeys(envVariables);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
517
577
|
if (did) {
|
|
578
|
+
if (nextTier === "locked") deviceCapabilities.delete(did);
|
|
518
579
|
const device = allDevices.find((d) => d.deviceId === did);
|
|
519
580
|
if (device) {
|
|
520
581
|
device.isPro = nextIsPro;
|
|
521
582
|
device.tier = nextTier;
|
|
522
|
-
device.licenseKey
|
|
583
|
+
delete device.licenseKey;
|
|
523
584
|
}
|
|
524
585
|
}
|
|
525
586
|
brokerLog(
|
|
526
587
|
"debug",
|
|
527
|
-
`device-license from ${user?.deviceName || socket.id}: tier=${nextTier}
|
|
588
|
+
`device-license from ${user?.deviceName || socket.id}: tier=${nextTier}`,
|
|
528
589
|
{ socketId: socket.id }
|
|
529
590
|
);
|
|
530
591
|
emitAllDevices();
|
|
@@ -538,6 +599,7 @@ function socketHandle({ io }) {
|
|
|
538
599
|
handleUserMessage(msg, deviceName || "Unknown");
|
|
539
600
|
});
|
|
540
601
|
socket.on("devtool-capabilities", (message) => {
|
|
602
|
+
if (!message || !Array.isArray(message.tools) || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
|
|
541
603
|
console.log(
|
|
542
604
|
`${LOG_PREFIX} Capabilities from ${message.persistentDeviceId}: ${message.tools.map((tool) => `${tool.toolId}@v${tool.version}`).join(", ")}`
|
|
543
605
|
);
|
|
@@ -567,11 +629,13 @@ function socketHandle({ io }) {
|
|
|
567
629
|
}
|
|
568
630
|
});
|
|
569
631
|
socket.on("devtool-sync", (message) => {
|
|
632
|
+
if (!message || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
|
|
570
633
|
forwardToDashboards("devtool-sync", message);
|
|
571
634
|
});
|
|
572
635
|
socket.on(
|
|
573
636
|
"devtool-action-result",
|
|
574
637
|
(message) => {
|
|
638
|
+
if (!message || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
|
|
575
639
|
console.log(
|
|
576
640
|
`${LOG_PREFIX} Action result from ${message.persistentDeviceId}: ${message.toolId}.${message.action} -> ${message.ok ? "ok" : `error: ${message.error}`}`
|
|
577
641
|
);
|
|
@@ -624,6 +688,24 @@ function socketHandle({ io }) {
|
|
|
624
688
|
);
|
|
625
689
|
});
|
|
626
690
|
});
|
|
691
|
+
return {
|
|
692
|
+
refreshAccess() {
|
|
693
|
+
const ready = canUseDesktop();
|
|
694
|
+
if (!ready) {
|
|
695
|
+
socketTiers.clear();
|
|
696
|
+
deviceCapabilities.clear();
|
|
697
|
+
brokerLogBuffer.length = 0;
|
|
698
|
+
watches = new WatchRegistry();
|
|
699
|
+
for (const device of [...users, ...allDevices]) {
|
|
700
|
+
delete device.licenseKey;
|
|
701
|
+
delete device.envVariables;
|
|
702
|
+
delete device.extraDeviceInfo;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
io.emit("buoy:access", { protocol: 1, ready });
|
|
706
|
+
emitAllDevices();
|
|
707
|
+
}
|
|
708
|
+
};
|
|
627
709
|
}
|
|
628
710
|
|
|
629
711
|
// src/createBroker.ts
|
|
@@ -658,7 +740,7 @@ function createBroker(options = {}) {
|
|
|
658
740
|
...SOCKET_CONFIG,
|
|
659
741
|
...options.socketOptions
|
|
660
742
|
});
|
|
661
|
-
socketHandle({ io });
|
|
743
|
+
const routing = socketHandle({ io, canUseDesktop: options.canUseDesktop });
|
|
662
744
|
httpServer.on("error", (error) => {
|
|
663
745
|
options.onError?.(error);
|
|
664
746
|
});
|
|
@@ -670,7 +752,7 @@ function createBroker(options = {}) {
|
|
|
670
752
|
httpServer.close(() => resolve());
|
|
671
753
|
});
|
|
672
754
|
});
|
|
673
|
-
return { io, httpServer, port, close };
|
|
755
|
+
return { io, httpServer, port, close, refreshAccess: routing.refreshAccess };
|
|
674
756
|
}
|
|
675
757
|
// Annotate the CommonJS export names for ESM import in node:
|
|
676
758
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// src/protocol.ts
|
|
2
2
|
var SYNC_PROTOCOL_VERSION = 1;
|
|
3
3
|
function tierFromLicensePayload(payload) {
|
|
4
|
-
if (payload?.tier === "pro" || payload?.tier === "free"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return payload?.isPro === "1" || payload?.isPro === true ? "pro" : "anonymous";
|
|
4
|
+
if (payload?.tier === "pro" || payload?.tier === "free") return payload.tier;
|
|
5
|
+
if (payload?.tier === "locked" || payload?.tier === "anonymous") return "locked";
|
|
6
|
+
return payload?.isPro === "1" || payload?.isPro === true ? "pro" : "locked";
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
// src/watchRegistry.ts
|
|
@@ -90,19 +89,31 @@ var WatchRegistry = class {
|
|
|
90
89
|
};
|
|
91
90
|
|
|
92
91
|
// src/socketHandle.ts
|
|
93
|
-
var users = [];
|
|
94
|
-
var allDevices = [];
|
|
95
|
-
var dashboardClients = [];
|
|
96
|
-
var deviceIdMap = /* @__PURE__ */ new Map();
|
|
97
|
-
var activeConnectionsMap = /* @__PURE__ */ new Map();
|
|
98
|
-
var instanceIdMap = /* @__PURE__ */ new Map();
|
|
99
|
-
var deviceCapabilities = /* @__PURE__ */ new Map();
|
|
100
|
-
var watches = new WatchRegistry();
|
|
101
|
-
var brokerLogBuffer = [];
|
|
102
92
|
var BROKER_LOG_BUFFER_MAX = 200;
|
|
103
93
|
var MAX_OFFLINE_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
104
|
-
function
|
|
94
|
+
function redactAccountKeys(raw) {
|
|
95
|
+
if (!raw) return void 0;
|
|
96
|
+
try {
|
|
97
|
+
const value = JSON.parse(raw);
|
|
98
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
99
|
+
for (const key of ["BUOY_LICENSE_KEY", "BUOY_KEY", "EXPO_PUBLIC_BUOY_KEY", "VITE_BUOY_KEY", "REACT_APP_BUOY_KEY", "licenseKey"]) delete value[key];
|
|
100
|
+
return JSON.stringify(value);
|
|
101
|
+
} catch {
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function socketHandle({ io, canUseDesktop = () => false }) {
|
|
105
106
|
const LOG_PREFIX = "[SERVER]";
|
|
107
|
+
let users = [];
|
|
108
|
+
const allDevices = [];
|
|
109
|
+
let dashboardClients = [];
|
|
110
|
+
const deviceIdMap = /* @__PURE__ */ new Map();
|
|
111
|
+
const socketTiers = /* @__PURE__ */ new Map();
|
|
112
|
+
const activeConnectionsMap = /* @__PURE__ */ new Map();
|
|
113
|
+
const instanceIdMap = /* @__PURE__ */ new Map();
|
|
114
|
+
const deviceCapabilities = /* @__PURE__ */ new Map();
|
|
115
|
+
let watches = new WatchRegistry();
|
|
116
|
+
const brokerLogBuffer = [];
|
|
106
117
|
function brokerLog(level, message, data) {
|
|
107
118
|
console.log(`${LOG_PREFIX} ${message}`);
|
|
108
119
|
const entry = { ts: Date.now(), level, message, data };
|
|
@@ -122,6 +133,20 @@ function socketHandle({ io }) {
|
|
|
122
133
|
return instances.size;
|
|
123
134
|
}
|
|
124
135
|
function emitAllDevices() {
|
|
136
|
+
if (!canUseDesktop()) {
|
|
137
|
+
io.emit("all-devices-update", []);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
for (const device of [...users, ...allDevices]) {
|
|
141
|
+
if (device.deviceName === "Dashboard" || !device.isConnected) continue;
|
|
142
|
+
const tier = socketTiers.get(device.id) ?? "locked";
|
|
143
|
+
device.tier = tier === "pro" ? "pro" : tier === "free" ? "free" : "locked";
|
|
144
|
+
device.isPro = tier === "pro";
|
|
145
|
+
if (tier !== "free" && tier !== "pro") {
|
|
146
|
+
delete device.envVariables;
|
|
147
|
+
delete device.extraDeviceInfo;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
125
150
|
const now = Date.now();
|
|
126
151
|
for (const device of allDevices) {
|
|
127
152
|
if (!device.deviceId) continue;
|
|
@@ -151,7 +176,7 @@ function socketHandle({ io }) {
|
|
|
151
176
|
}
|
|
152
177
|
io.emit(
|
|
153
178
|
"all-devices-update",
|
|
154
|
-
allDevices.filter((device) => device.deviceName !== "Dashboard")
|
|
179
|
+
allDevices.filter((device) => device.deviceName !== "Dashboard").map(({ licenseKey: _key, ...device }) => device)
|
|
155
180
|
);
|
|
156
181
|
}
|
|
157
182
|
function handleClose(id, reason) {
|
|
@@ -159,6 +184,7 @@ function socketHandle({ io }) {
|
|
|
159
184
|
const deviceId = deviceIdMap.get(id);
|
|
160
185
|
deviceIdMap.delete(id);
|
|
161
186
|
instanceIdMap.delete(id);
|
|
187
|
+
socketTiers.delete(id);
|
|
162
188
|
let survivor;
|
|
163
189
|
if (deviceId) {
|
|
164
190
|
for (const [socketId, mappedDeviceId] of deviceIdMap) {
|
|
@@ -346,9 +372,11 @@ function socketHandle({ io }) {
|
|
|
346
372
|
}
|
|
347
373
|
function handleUserMessage(message, deviceName) {
|
|
348
374
|
console.log(`${LOG_PREFIX} Message from ${deviceName}: ${message}`);
|
|
375
|
+
if (!canUseDesktop()) return;
|
|
349
376
|
io.emit("message", `Device ${deviceName} sent message: ${message}`);
|
|
350
377
|
}
|
|
351
378
|
function forwardToDashboards(event, message) {
|
|
379
|
+
if (!canUseDesktop()) return;
|
|
352
380
|
dashboardClients = dashboardClients.filter(
|
|
353
381
|
(id) => io.sockets.sockets.has(id)
|
|
354
382
|
);
|
|
@@ -360,6 +388,11 @@ function socketHandle({ io }) {
|
|
|
360
388
|
}
|
|
361
389
|
}
|
|
362
390
|
function withTargetUsers(targetDeviceId, action, actionName) {
|
|
391
|
+
if (!canUseDesktop()) return;
|
|
392
|
+
const admitted = (user) => {
|
|
393
|
+
const tier = socketTiers.get(user.id);
|
|
394
|
+
return tier === "free" || tier === "pro";
|
|
395
|
+
};
|
|
363
396
|
const deviceName = getDeviceFromDeviceId(targetDeviceId)?.deviceName;
|
|
364
397
|
if (targetDeviceId === "No devices available") {
|
|
365
398
|
console.log(
|
|
@@ -375,10 +408,10 @@ function socketHandle({ io }) {
|
|
|
375
408
|
console.log(
|
|
376
409
|
`${LOG_PREFIX} Sending to ${deviceUsers.length} devices: ${deviceUsers.map((u) => u.deviceName).join(", ")}`
|
|
377
410
|
);
|
|
378
|
-
deviceUsers.forEach(action);
|
|
411
|
+
deviceUsers.filter(admitted).forEach(action);
|
|
379
412
|
} else {
|
|
380
413
|
const targetUser = users.find((user) => user.deviceId === targetDeviceId);
|
|
381
|
-
if (targetUser) {
|
|
414
|
+
if (targetUser && admitted(targetUser)) {
|
|
382
415
|
console.log(
|
|
383
416
|
`${LOG_PREFIX} Sending to target device ${targetUser.deviceName} (${targetUser.deviceId}) targetDeviceId: ${targetDeviceId}`
|
|
384
417
|
);
|
|
@@ -429,6 +462,27 @@ function socketHandle({ io }) {
|
|
|
429
462
|
}
|
|
430
463
|
}
|
|
431
464
|
io.on("connection", (socket) => {
|
|
465
|
+
const sendAccess = () => socket.emit("buoy:access", { protocol: 1, ready: canUseDesktop() });
|
|
466
|
+
sendAccess();
|
|
467
|
+
socket.on("buoy:access:request", sendAccess);
|
|
468
|
+
socket.use((_packet, next) => {
|
|
469
|
+
if (_packet[0] === "buoy:access:request") {
|
|
470
|
+
next();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (!canUseDesktop()) {
|
|
474
|
+
sendAccess();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const control = _packet[0] === "device-license";
|
|
478
|
+
if (control) {
|
|
479
|
+
const payload = _packet[1] ?? {};
|
|
480
|
+
socketTiers.set(socket.id, tierFromLicensePayload(payload));
|
|
481
|
+
}
|
|
482
|
+
const deviceAdmitted = socketTiers.get(socket.id) === "free" || socketTiers.get(socket.id) === "pro";
|
|
483
|
+
if (control || dashboardClients.includes(socket.id) || deviceAdmitted) next();
|
|
484
|
+
else sendAccess();
|
|
485
|
+
});
|
|
432
486
|
const {
|
|
433
487
|
deviceName,
|
|
434
488
|
deviceId,
|
|
@@ -442,18 +496,18 @@ function socketHandle({ io }) {
|
|
|
442
496
|
instanceIdMap.set(socket.id, instanceId);
|
|
443
497
|
}
|
|
444
498
|
console.log(
|
|
445
|
-
`${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}
|
|
499
|
+
`${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}`
|
|
446
500
|
);
|
|
447
501
|
addNewUser({
|
|
448
502
|
id: socket.id,
|
|
449
503
|
deviceName: deviceName || "Unknown Device Name",
|
|
450
504
|
deviceId,
|
|
451
505
|
platform,
|
|
452
|
-
extraDeviceInfo,
|
|
453
|
-
envVariables,
|
|
506
|
+
extraDeviceInfo: void 0,
|
|
507
|
+
envVariables: void 0,
|
|
454
508
|
connectionNonce
|
|
455
509
|
});
|
|
456
|
-
if (deviceName === "Dashboard") {
|
|
510
|
+
if (canUseDesktop() && deviceName === "Dashboard") {
|
|
457
511
|
for (const capabilities of deviceCapabilities.values()) {
|
|
458
512
|
socket.emit("devtool-capabilities", capabilities);
|
|
459
513
|
}
|
|
@@ -466,25 +520,32 @@ function socketHandle({ io }) {
|
|
|
466
520
|
(payload) => {
|
|
467
521
|
const nextTier = tierFromLicensePayload(payload ?? {});
|
|
468
522
|
const nextIsPro = nextTier === "pro";
|
|
469
|
-
|
|
523
|
+
socketTiers.set(socket.id, nextTier);
|
|
470
524
|
const user = users.find((u) => u.id === socket.id);
|
|
471
525
|
if (user) {
|
|
472
526
|
user.isPro = nextIsPro;
|
|
473
527
|
user.tier = nextTier;
|
|
474
|
-
user.licenseKey
|
|
528
|
+
delete user.licenseKey;
|
|
475
529
|
}
|
|
476
530
|
const did = user?.deviceId || deviceIdMap.get(socket.id);
|
|
531
|
+
if (nextTier !== "locked") {
|
|
532
|
+
for (const record of [...users, ...allDevices].filter((record2) => record2.id === socket.id)) {
|
|
533
|
+
record.extraDeviceInfo = redactAccountKeys(extraDeviceInfo);
|
|
534
|
+
record.envVariables = redactAccountKeys(envVariables);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
477
537
|
if (did) {
|
|
538
|
+
if (nextTier === "locked") deviceCapabilities.delete(did);
|
|
478
539
|
const device = allDevices.find((d) => d.deviceId === did);
|
|
479
540
|
if (device) {
|
|
480
541
|
device.isPro = nextIsPro;
|
|
481
542
|
device.tier = nextTier;
|
|
482
|
-
device.licenseKey
|
|
543
|
+
delete device.licenseKey;
|
|
483
544
|
}
|
|
484
545
|
}
|
|
485
546
|
brokerLog(
|
|
486
547
|
"debug",
|
|
487
|
-
`device-license from ${user?.deviceName || socket.id}: tier=${nextTier}
|
|
548
|
+
`device-license from ${user?.deviceName || socket.id}: tier=${nextTier}`,
|
|
488
549
|
{ socketId: socket.id }
|
|
489
550
|
);
|
|
490
551
|
emitAllDevices();
|
|
@@ -498,6 +559,7 @@ function socketHandle({ io }) {
|
|
|
498
559
|
handleUserMessage(msg, deviceName || "Unknown");
|
|
499
560
|
});
|
|
500
561
|
socket.on("devtool-capabilities", (message) => {
|
|
562
|
+
if (!message || !Array.isArray(message.tools) || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
|
|
501
563
|
console.log(
|
|
502
564
|
`${LOG_PREFIX} Capabilities from ${message.persistentDeviceId}: ${message.tools.map((tool) => `${tool.toolId}@v${tool.version}`).join(", ")}`
|
|
503
565
|
);
|
|
@@ -527,11 +589,13 @@ function socketHandle({ io }) {
|
|
|
527
589
|
}
|
|
528
590
|
});
|
|
529
591
|
socket.on("devtool-sync", (message) => {
|
|
592
|
+
if (!message || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
|
|
530
593
|
forwardToDashboards("devtool-sync", message);
|
|
531
594
|
});
|
|
532
595
|
socket.on(
|
|
533
596
|
"devtool-action-result",
|
|
534
597
|
(message) => {
|
|
598
|
+
if (!message || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
|
|
535
599
|
console.log(
|
|
536
600
|
`${LOG_PREFIX} Action result from ${message.persistentDeviceId}: ${message.toolId}.${message.action} -> ${message.ok ? "ok" : `error: ${message.error}`}`
|
|
537
601
|
);
|
|
@@ -584,6 +648,24 @@ function socketHandle({ io }) {
|
|
|
584
648
|
);
|
|
585
649
|
});
|
|
586
650
|
});
|
|
651
|
+
return {
|
|
652
|
+
refreshAccess() {
|
|
653
|
+
const ready = canUseDesktop();
|
|
654
|
+
if (!ready) {
|
|
655
|
+
socketTiers.clear();
|
|
656
|
+
deviceCapabilities.clear();
|
|
657
|
+
brokerLogBuffer.length = 0;
|
|
658
|
+
watches = new WatchRegistry();
|
|
659
|
+
for (const device of [...users, ...allDevices]) {
|
|
660
|
+
delete device.licenseKey;
|
|
661
|
+
delete device.envVariables;
|
|
662
|
+
delete device.extraDeviceInfo;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
io.emit("buoy:access", { protocol: 1, ready });
|
|
666
|
+
emitAllDevices();
|
|
667
|
+
}
|
|
668
|
+
};
|
|
587
669
|
}
|
|
588
670
|
|
|
589
671
|
// src/createBroker.ts
|
|
@@ -618,7 +700,7 @@ function createBroker(options = {}) {
|
|
|
618
700
|
...SOCKET_CONFIG,
|
|
619
701
|
...options.socketOptions
|
|
620
702
|
});
|
|
621
|
-
socketHandle({ io });
|
|
703
|
+
const routing = socketHandle({ io, canUseDesktop: options.canUseDesktop });
|
|
622
704
|
httpServer.on("error", (error) => {
|
|
623
705
|
options.onError?.(error);
|
|
624
706
|
});
|
|
@@ -630,7 +712,7 @@ function createBroker(options = {}) {
|
|
|
630
712
|
httpServer.close(() => resolve());
|
|
631
713
|
});
|
|
632
714
|
});
|
|
633
|
-
return { io, httpServer, port, close };
|
|
715
|
+
return { io, httpServer, port, close, refreshAccess: routing.refreshAccess };
|
|
634
716
|
}
|
|
635
717
|
export {
|
|
636
718
|
DEFAULT_BROKER_PORT,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/sync-broker",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.39",
|
|
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",
|