From 66f37a41c24208963a3c15a730fcf15c2eac10de Mon Sep 17 00:00:00 2001 From: Mateus Silva Date: Tue, 7 May 2024 16:38:19 -0300 Subject: [PATCH 1/4] Update npm dependencies for eventsource package --- package-lock.json | 27 +++++++++++++++++++++++++++ package.json | 2 ++ tsconfig.json | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 449a490..104dfa9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "commander": "^11.1.0", "dotenv": "^16.3.1", "envfile": "^6.18.0", + "eventsource": "^2.0.2", "kleur": "^4.1.5", "lodash": "^4.17.21", "luxon": "^3.4.3", @@ -30,6 +31,7 @@ }, "devDependencies": { "@types/async": "^3.2.23", + "@types/eventsource": "^1.1.15", "@types/jest": "29.5.8", "@types/lodash": "^4.14.201", "@types/luxon": "^3.3.4", @@ -1805,6 +1807,12 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/eventsource": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.1.15.tgz", + "integrity": "sha512-XQmGcbnxUNa06HR3VBVkc9+A2Vpi9ZyLJcdS5dwaQQ/4ZMWFO+5c90FnMUpbtMZwB/FChoYHwuVg8TvkECacTA==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -4536,6 +4544,14 @@ "node": ">=0.10.0" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -10380,6 +10396,12 @@ "@types/responselike": "^1.0.0" } }, + "@types/eventsource": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.1.15.tgz", + "integrity": "sha512-XQmGcbnxUNa06HR3VBVkc9+A2Vpi9ZyLJcdS5dwaQQ/4ZMWFO+5c90FnMUpbtMZwB/FChoYHwuVg8TvkECacTA==", + "dev": true + }, "@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -12250,6 +12272,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==" + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", diff --git a/package.json b/package.json index f12cae8..999dac8 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "commander": "^11.1.0", "dotenv": "^16.3.1", "envfile": "^6.18.0", + "eventsource": "^2.0.2", "kleur": "^4.1.5", "lodash": "^4.17.21", "luxon": "^3.4.3", @@ -57,6 +58,7 @@ }, "devDependencies": { "@types/async": "^3.2.23", + "@types/eventsource": "^1.1.15", "@types/jest": "29.5.8", "@types/lodash": "^4.14.201", "@types/luxon": "^3.3.4", diff --git a/tsconfig.json b/tsconfig.json index 7285824..adc4005 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ES2021", "module": "CommonJS", - "lib": ["ES2022"], + "lib": ["ES2022", "DOM"], "allowJs": true, "outDir": "./build", "noImplicitAny": true, From 5aeb8931e866283984ad9a2b32543d19cf7efd25 Mon Sep 17 00:00:00 2001 From: Mateus Silva Date: Tue, 7 May 2024 16:39:20 -0300 Subject: [PATCH 2/4] Refactor device-live-inspector to use SSE instead of socket.io --- src/commands/devices/device-live-inspector.ts | 74 +++++++------------ 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/src/commands/devices/device-live-inspector.ts b/src/commands/devices/device-live-inspector.ts index af2e346..ea9aaf1 100644 --- a/src/commands/devices/device-live-inspector.ts +++ b/src/commands/devices/device-live-inspector.ts @@ -1,28 +1,21 @@ -import { io } from "socket.io-client"; +import EventSource from "eventsource"; import { Account, Device } from "@tago-io/sdk"; import { DeviceInfo } from "@tago-io/sdk/lib/types"; import { getEnvironmentConfig } from "../../lib/config-file"; -import { errorHandler, highlightMSG, infoMSG, successMSG } from "../../lib/messages"; +import { errorHandler, highlightMSG, successMSG } from "../../lib/messages"; import { pickDeviceIDFromTagoIO } from "../../prompt/pick-device-id-from-tagoio"; /** - * Creates a new socket connection to the TagoIO Realtime API. + * Creates a new SSE connection to the TagoIO Realtime API. * @param profileToken - The user's profile token. - * @returns A socket instance connected to the TagoIO Realtime API. + * @returns An EventSource instance connected to the TagoIO Realtime API. */ -function apiSocket(profileToken: string) { - const socket = io("wss://realtime.tago.io", { - reconnectionDelay: 10_000, - reconnection: true, - transports: ["websocket"], - query: { - token: profileToken, - }, - }); - - return socket; +function apiSSE(profileToken: string) { + const sse = new EventSource(`https://realtime.tago.io?token=${profileToken}`); + + return sse; } interface ScopeContent { @@ -47,43 +40,32 @@ function displayMessage(scope: ScopeContent) { } /** - * Sets up the socket connection and event listeners for device live inspection. - * @param socket - The socket connection to TagoIO. + * Sets up the SSE connection and event listeners for device live inspection. + * @param sse - The SSE connection to TagoIO. * @param deviceIdOrToken - The ID or token of the device to inspect. * @param deviceInfo - Information about the device being inspected. */ -function setupSocket(socket: ReturnType, deviceIdOrToken: string, deviceInfo: DeviceInfo) { - socket.on("connect", () => { - successMSG("Connected to TagoIO, Getting device information..."); - socket.emit("attach", "device", deviceIdOrToken); - socket.emit("attach", { - resourceName: "device", - resourceID: deviceIdOrToken, - }); - }); - - socket.on("disconnect", () => infoMSG("Disconnected from TagoIO.\n\n")); +function setupSSE(sse: ReturnType, deviceIdOrToken: string, deviceInfo: DeviceInfo) { + sse.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data.resourceName === "device" && data.resourceID === deviceIdOrToken) { + if (Array.isArray(data.scope)) { + for (const item of data.scope) { + displayMessage(item); + } + } else { + displayMessage(data.scope); + } + } + }; - socket.on("error", (e: Error) => errorHandler(`Connection error: ${e}`)); + sse.onerror = (error) => errorHandler(`Connection error: ${JSON.stringify(error)}`); - socket.on("ready", () => { + sse.onopen = () => { const deviceName = deviceInfo?.name || deviceIdOrToken; successMSG(`Device [${highlightMSG(deviceName)}] found successfully.`); successMSG(`Waiting for logs...`); - }); - - /** - * Event listener for device inspection messages. - */ - socket.on("device::inspection", (scope: ScopeContent | ScopeContent[]) => { - if (Array.isArray(scope)) { - for (const item of scope) { - displayMessage(item); - } - } else { - displayMessage(scope); - } - }); + }; } interface IOptions { @@ -125,8 +107,8 @@ async function inspectorConnection(deviceIdOrToken: string, options: IOptions) { deviceIdOrToken = deviceInfo.id; } - const socket = apiSocket(config.profileToken); - setupSocket(socket, deviceIdOrToken, deviceInfo); + const sse = apiSSE(config.profileToken); + setupSSE(sse, deviceIdOrToken, deviceInfo); } export { inspectorConnection }; From 5d62548f3763c2312576046024cfe66c50722ea8 Mon Sep 17 00:00:00 2001 From: Mateus Silva Date: Wed, 8 May 2024 10:14:50 -0300 Subject: [PATCH 3/4] Update device-live-inspector to use SSE for real-time updates --- src/commands/devices/device-live-inspector.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/commands/devices/device-live-inspector.ts b/src/commands/devices/device-live-inspector.ts index ea9aaf1..bf795f8 100644 --- a/src/commands/devices/device-live-inspector.ts +++ b/src/commands/devices/device-live-inspector.ts @@ -12,16 +12,18 @@ import { pickDeviceIDFromTagoIO } from "../../prompt/pick-device-id-from-tagoio" * @param profileToken - The user's profile token. * @returns An EventSource instance connected to the TagoIO Realtime API. */ -function apiSSE(profileToken: string) { - const sse = new EventSource(`https://realtime.tago.io?token=${profileToken}`); +function apiSSE(profileToken: string, deviceID: string) { + const sse = new EventSource(`https://sse.tago.io/events?channel=device_inspector.${deviceID}&token=${profileToken}`); return sse; } interface ScopeContent { + connection_id: string; + content: string; + device_id: string; timestamp: string; title: string; - content: string; } /** @@ -47,19 +49,20 @@ function displayMessage(scope: ScopeContent) { */ function setupSSE(sse: ReturnType, deviceIdOrToken: string, deviceInfo: DeviceInfo) { sse.onmessage = (event) => { - const data = JSON.parse(event.data); - if (data.resourceName === "device" && data.resourceID === deviceIdOrToken) { - if (Array.isArray(data.scope)) { - for (const item of data.scope) { - displayMessage(item); - } - } else { - displayMessage(data.scope); + const scope = JSON.parse(event.data).payload as ScopeContent; + if (Array.isArray(scope)) { + for (const item of scope) { + displayMessage(item); } + } else { + displayMessage(scope); } }; - sse.onerror = (error) => errorHandler(`Connection error: ${JSON.stringify(error)}`); + sse.onerror = (_error) => { + errorHandler("Connection error"); + console.error(_error); + }; sse.onopen = () => { const deviceName = deviceInfo?.name || deviceIdOrToken; @@ -107,7 +110,7 @@ async function inspectorConnection(deviceIdOrToken: string, options: IOptions) { deviceIdOrToken = deviceInfo.id; } - const sse = apiSSE(config.profileToken); + const sse = apiSSE(config.profileToken, deviceInfo.id); setupSSE(sse, deviceIdOrToken, deviceInfo); } From 07b64ce13afe487b88971c0db60cba242470e18e Mon Sep 17 00:00:00 2001 From: Mateus Silva Date: Wed, 8 May 2024 11:31:00 -0300 Subject: [PATCH 4/4] Refactor analysis console to use SSE for real-time updates --- src/commands/analysis/analysis-console.ts | 69 ++++++++----------- src/commands/devices/device-live-inspector.ts | 6 +- 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/commands/analysis/analysis-console.ts b/src/commands/analysis/analysis-console.ts index 3730581..e2d4f16 100644 --- a/src/commands/analysis/analysis-console.ts +++ b/src/commands/analysis/analysis-console.ts @@ -1,6 +1,6 @@ -import { connect } from "socket.io-client"; - +import EventSource from "eventsource"; import { Account } from "@tago-io/sdk"; +import { AnalysisInfo } from "@tago-io/sdk/lib/types"; import { getEnvironmentConfig, IEnvironment } from "../../lib/config-file"; import { errorHandler, highlightMSG, infoMSG, successMSG } from "../../lib/messages"; @@ -8,21 +8,15 @@ import { searchName } from "../../lib/search-name"; import { pickAnalysisFromConfig } from "../../prompt/pick-analysis-from-config"; /** - * Creates a WebSocket connection to the TagoIO Realtime API. - * @param profileToken The user's profile token. - * @returns The WebSocket instance. + * Creates a new SSE connection to the TagoIO Realtime API. + * @param profileToken - The user's profile token. + * @param analysisID - The ID of the analysis script to connect to. + * @returns An EventSource instance connected to the TagoIO Realtime API. */ -function apiSocket(profileToken: string) { - const socket = connect("wss://realtime.tago.io", { - reconnectionDelay: 10_000, - reconnection: true, - transports: ["websocket"], - query: { - token: profileToken, - }, - }); +function apiSSE(profileToken: string, analysisID: string) { + const sse = new EventSource(`https://sse.tago.io/events?channel=analysis_console.${analysisID}&token=${profileToken}`); - return socket; + return sse; } /** @@ -43,34 +37,29 @@ async function getScriptObj(scriptName: string | void, analysisList: IEnvironmen } return scriptObj; } + /** - * Sets up a socket connection to TagoIO and attaches to an analysis script. - * @param socket - The socket connection to TagoIO. - * @param scriptId - The ID of the analysis script to attach to. - * @param analysis_info - Information about the analysis script. + * Sets up the SSE connection and event listeners for device live inspection. + * @param sse - The SSE connection to TagoIO. + * @param deviceIdOrToken - The ID or token of the device to inspect. + * @param deviceInfo - Information about the device being inspected. */ -function setupSocket(socket: ReturnType, scriptId: string, analysis_info: any) { - socket.on("connect", () => { - infoMSG("Connected to TagoIO, Getting analysis information..."); - socket.emit("attach", "analysis", scriptId); - socket.emit("attach", { - resourceName: "analysis", - resourceID: scriptId, - }); - }); - - socket.on("disconnect", () => console.info("Disconnected from TagoIO.\n\n")); +function setupSSE(sse: ReturnType, scriptId: string, analysis_info: AnalysisInfo) { + sse.onmessage = (event) => { + const scope = JSON.parse(event.data).payload; + console.log(`\x1b[35m${new Date(scope.timestamp).toISOString()} \x1b[0m ${scope.message}`); + }; - socket.on("error", (e: Error) => { + sse.onerror = (error) => { errorHandler("Connection error"); - console.error(e); - }); - - socket.on("ready", () => successMSG(`Analysis [${highlightMSG(analysis_info.name)}] found succesfully. ${highlightMSG("Waiting for logs...")}`)); + console.error(error); + }; - socket.on("analysis::console", (scope: any) => { - console.log(`\x1b[35m${new Date(scope.timestamp).toISOString()} \x1b[0m ${scope.message}`); - }); + sse.onopen = () => { + infoMSG("Connected to TagoIO, Getting analysis information..."); + successMSG(`Analysis [${highlightMSG(analysis_info.name)}] found successfully.`); + successMSG(`Waiting for logs...`); + }; } /** @@ -99,8 +88,8 @@ async function connectAnalysisConsole(scriptName: string | void, options: { envi return; } - const socket = apiSocket(config.profileToken); - setupSocket(socket, scriptObj.id, analysis_info); + const sse = apiSSE(config.profileToken, analysis_info.id); + setupSSE(sse, scriptObj.id, analysis_info); } export { connectAnalysisConsole }; diff --git a/src/commands/devices/device-live-inspector.ts b/src/commands/devices/device-live-inspector.ts index bf795f8..fa7fc64 100644 --- a/src/commands/devices/device-live-inspector.ts +++ b/src/commands/devices/device-live-inspector.ts @@ -1,5 +1,4 @@ import EventSource from "eventsource"; - import { Account, Device } from "@tago-io/sdk"; import { DeviceInfo } from "@tago-io/sdk/lib/types"; @@ -10,6 +9,7 @@ import { pickDeviceIDFromTagoIO } from "../../prompt/pick-device-id-from-tagoio" /** * Creates a new SSE connection to the TagoIO Realtime API. * @param profileToken - The user's profile token. + * @param deviceID - The ID of the device to inspect. * @returns An EventSource instance connected to the TagoIO Realtime API. */ function apiSSE(profileToken: string, deviceID: string) { @@ -59,9 +59,9 @@ function setupSSE(sse: ReturnType, deviceIdOrToken: string, devic } }; - sse.onerror = (_error) => { + sse.onerror = (error) => { errorHandler("Connection error"); - console.error(_error); + console.error(error); }; sse.onopen = () => {