 85bf1341f3
			
		
	
	85bf1341f3
	
	
	
		
			
			Frontend Enhancements: - Complete React TypeScript frontend with modern UI components - Distributed workflows management interface with real-time updates - Socket.IO integration for live agent status monitoring - Agent management dashboard with cluster visualization - Project management interface with metrics and task tracking - Responsive design with proper error handling and loading states Backend Infrastructure: - Distributed coordinator for multi-agent workflow orchestration - Cluster management API with comprehensive agent operations - Enhanced database models for agents and projects - Project service for filesystem-based project discovery - Performance monitoring and metrics collection - Comprehensive API documentation and error handling Documentation: - Complete distributed development guide (README_DISTRIBUTED.md) - Comprehensive development report with architecture insights - System configuration templates and deployment guides The platform now provides a complete web interface for managing the distributed AI cluster with real-time monitoring, workflow orchestration, and agent coordination capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			317 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { Emitter } from "@socket.io/component-emitter";
 | |
| import { deconstructPacket, reconstructPacket } from "./binary.js";
 | |
| import { isBinary, hasBinary } from "./is-binary.js";
 | |
| import debugModule from "debug"; // debug()
 | |
| const debug = debugModule("socket.io-parser"); // debug()
 | |
| /**
 | |
|  * These strings must not be used as event names, as they have a special meaning.
 | |
|  */
 | |
| const RESERVED_EVENTS = [
 | |
|     "connect",
 | |
|     "connect_error",
 | |
|     "disconnect",
 | |
|     "disconnecting",
 | |
|     "newListener",
 | |
|     "removeListener", // used by the Node.js EventEmitter
 | |
| ];
 | |
| /**
 | |
|  * Protocol version.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| export const protocol = 5;
 | |
| export var PacketType;
 | |
| (function (PacketType) {
 | |
|     PacketType[PacketType["CONNECT"] = 0] = "CONNECT";
 | |
|     PacketType[PacketType["DISCONNECT"] = 1] = "DISCONNECT";
 | |
|     PacketType[PacketType["EVENT"] = 2] = "EVENT";
 | |
|     PacketType[PacketType["ACK"] = 3] = "ACK";
 | |
|     PacketType[PacketType["CONNECT_ERROR"] = 4] = "CONNECT_ERROR";
 | |
|     PacketType[PacketType["BINARY_EVENT"] = 5] = "BINARY_EVENT";
 | |
|     PacketType[PacketType["BINARY_ACK"] = 6] = "BINARY_ACK";
 | |
| })(PacketType || (PacketType = {}));
 | |
| /**
 | |
|  * A socket.io Encoder instance
 | |
|  */
 | |
| export class Encoder {
 | |
|     /**
 | |
|      * Encoder constructor
 | |
|      *
 | |
|      * @param {function} replacer - custom replacer to pass down to JSON.parse
 | |
|      */
 | |
|     constructor(replacer) {
 | |
|         this.replacer = replacer;
 | |
|     }
 | |
|     /**
 | |
|      * Encode a packet as a single string if non-binary, or as a
 | |
|      * buffer sequence, depending on packet type.
 | |
|      *
 | |
|      * @param {Object} obj - packet object
 | |
|      */
 | |
|     encode(obj) {
 | |
|         debug("encoding packet %j", obj);
 | |
|         if (obj.type === PacketType.EVENT || obj.type === PacketType.ACK) {
 | |
|             if (hasBinary(obj)) {
 | |
|                 return this.encodeAsBinary({
 | |
|                     type: obj.type === PacketType.EVENT
 | |
|                         ? PacketType.BINARY_EVENT
 | |
|                         : PacketType.BINARY_ACK,
 | |
|                     nsp: obj.nsp,
 | |
|                     data: obj.data,
 | |
|                     id: obj.id,
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         return [this.encodeAsString(obj)];
 | |
|     }
 | |
|     /**
 | |
|      * Encode packet as string.
 | |
|      */
 | |
|     encodeAsString(obj) {
 | |
|         // first is type
 | |
|         let str = "" + obj.type;
 | |
|         // attachments if we have them
 | |
|         if (obj.type === PacketType.BINARY_EVENT ||
 | |
|             obj.type === PacketType.BINARY_ACK) {
 | |
|             str += obj.attachments + "-";
 | |
|         }
 | |
|         // if we have a namespace other than `/`
 | |
|         // we append it followed by a comma `,`
 | |
|         if (obj.nsp && "/" !== obj.nsp) {
 | |
|             str += obj.nsp + ",";
 | |
|         }
 | |
|         // immediately followed by the id
 | |
|         if (null != obj.id) {
 | |
|             str += obj.id;
 | |
|         }
 | |
|         // json data
 | |
|         if (null != obj.data) {
 | |
|             str += JSON.stringify(obj.data, this.replacer);
 | |
|         }
 | |
|         debug("encoded %j as %s", obj, str);
 | |
|         return str;
 | |
|     }
 | |
|     /**
 | |
|      * Encode packet as 'buffer sequence' by removing blobs, and
 | |
|      * deconstructing packet into object with placeholders and
 | |
|      * a list of buffers.
 | |
|      */
 | |
|     encodeAsBinary(obj) {
 | |
|         const deconstruction = deconstructPacket(obj);
 | |
|         const pack = this.encodeAsString(deconstruction.packet);
 | |
|         const buffers = deconstruction.buffers;
 | |
|         buffers.unshift(pack); // add packet info to beginning of data list
 | |
|         return buffers; // write all the buffers
 | |
|     }
 | |
| }
 | |
| // see https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript
 | |
| function isObject(value) {
 | |
|     return Object.prototype.toString.call(value) === "[object Object]";
 | |
| }
 | |
| /**
 | |
|  * A socket.io Decoder instance
 | |
|  *
 | |
|  * @return {Object} decoder
 | |
|  */
 | |
| export class Decoder extends Emitter {
 | |
|     /**
 | |
|      * Decoder constructor
 | |
|      *
 | |
|      * @param {function} reviver - custom reviver to pass down to JSON.stringify
 | |
|      */
 | |
|     constructor(reviver) {
 | |
|         super();
 | |
|         this.reviver = reviver;
 | |
|     }
 | |
|     /**
 | |
|      * Decodes an encoded packet string into packet JSON.
 | |
|      *
 | |
|      * @param {String} obj - encoded packet
 | |
|      */
 | |
|     add(obj) {
 | |
|         let packet;
 | |
|         if (typeof obj === "string") {
 | |
|             if (this.reconstructor) {
 | |
|                 throw new Error("got plaintext data when reconstructing a packet");
 | |
|             }
 | |
|             packet = this.decodeString(obj);
 | |
|             const isBinaryEvent = packet.type === PacketType.BINARY_EVENT;
 | |
|             if (isBinaryEvent || packet.type === PacketType.BINARY_ACK) {
 | |
|                 packet.type = isBinaryEvent ? PacketType.EVENT : PacketType.ACK;
 | |
|                 // binary packet's json
 | |
|                 this.reconstructor = new BinaryReconstructor(packet);
 | |
|                 // no attachments, labeled binary but no binary data to follow
 | |
|                 if (packet.attachments === 0) {
 | |
|                     super.emitReserved("decoded", packet);
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 // non-binary full packet
 | |
|                 super.emitReserved("decoded", packet);
 | |
|             }
 | |
|         }
 | |
|         else if (isBinary(obj) || obj.base64) {
 | |
|             // raw binary data
 | |
|             if (!this.reconstructor) {
 | |
|                 throw new Error("got binary data when not reconstructing a packet");
 | |
|             }
 | |
|             else {
 | |
|                 packet = this.reconstructor.takeBinaryData(obj);
 | |
|                 if (packet) {
 | |
|                     // received final buffer
 | |
|                     this.reconstructor = null;
 | |
|                     super.emitReserved("decoded", packet);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             throw new Error("Unknown type: " + obj);
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Decode a packet String (JSON data)
 | |
|      *
 | |
|      * @param {String} str
 | |
|      * @return {Object} packet
 | |
|      */
 | |
|     decodeString(str) {
 | |
|         let i = 0;
 | |
|         // look up type
 | |
|         const p = {
 | |
|             type: Number(str.charAt(0)),
 | |
|         };
 | |
|         if (PacketType[p.type] === undefined) {
 | |
|             throw new Error("unknown packet type " + p.type);
 | |
|         }
 | |
|         // look up attachments if type binary
 | |
|         if (p.type === PacketType.BINARY_EVENT ||
 | |
|             p.type === PacketType.BINARY_ACK) {
 | |
|             const start = i + 1;
 | |
|             while (str.charAt(++i) !== "-" && i != str.length) { }
 | |
|             const buf = str.substring(start, i);
 | |
|             if (buf != Number(buf) || str.charAt(i) !== "-") {
 | |
|                 throw new Error("Illegal attachments");
 | |
|             }
 | |
|             p.attachments = Number(buf);
 | |
|         }
 | |
|         // look up namespace (if any)
 | |
|         if ("/" === str.charAt(i + 1)) {
 | |
|             const start = i + 1;
 | |
|             while (++i) {
 | |
|                 const c = str.charAt(i);
 | |
|                 if ("," === c)
 | |
|                     break;
 | |
|                 if (i === str.length)
 | |
|                     break;
 | |
|             }
 | |
|             p.nsp = str.substring(start, i);
 | |
|         }
 | |
|         else {
 | |
|             p.nsp = "/";
 | |
|         }
 | |
|         // look up id
 | |
|         const next = str.charAt(i + 1);
 | |
|         if ("" !== next && Number(next) == next) {
 | |
|             const start = i + 1;
 | |
|             while (++i) {
 | |
|                 const c = str.charAt(i);
 | |
|                 if (null == c || Number(c) != c) {
 | |
|                     --i;
 | |
|                     break;
 | |
|                 }
 | |
|                 if (i === str.length)
 | |
|                     break;
 | |
|             }
 | |
|             p.id = Number(str.substring(start, i + 1));
 | |
|         }
 | |
|         // look up json data
 | |
|         if (str.charAt(++i)) {
 | |
|             const payload = this.tryParse(str.substr(i));
 | |
|             if (Decoder.isPayloadValid(p.type, payload)) {
 | |
|                 p.data = payload;
 | |
|             }
 | |
|             else {
 | |
|                 throw new Error("invalid payload");
 | |
|             }
 | |
|         }
 | |
|         debug("decoded %s as %j", str, p);
 | |
|         return p;
 | |
|     }
 | |
|     tryParse(str) {
 | |
|         try {
 | |
|             return JSON.parse(str, this.reviver);
 | |
|         }
 | |
|         catch (e) {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
|     static isPayloadValid(type, payload) {
 | |
|         switch (type) {
 | |
|             case PacketType.CONNECT:
 | |
|                 return isObject(payload);
 | |
|             case PacketType.DISCONNECT:
 | |
|                 return payload === undefined;
 | |
|             case PacketType.CONNECT_ERROR:
 | |
|                 return typeof payload === "string" || isObject(payload);
 | |
|             case PacketType.EVENT:
 | |
|             case PacketType.BINARY_EVENT:
 | |
|                 return (Array.isArray(payload) &&
 | |
|                     (typeof payload[0] === "number" ||
 | |
|                         (typeof payload[0] === "string" &&
 | |
|                             RESERVED_EVENTS.indexOf(payload[0]) === -1)));
 | |
|             case PacketType.ACK:
 | |
|             case PacketType.BINARY_ACK:
 | |
|                 return Array.isArray(payload);
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Deallocates a parser's resources
 | |
|      */
 | |
|     destroy() {
 | |
|         if (this.reconstructor) {
 | |
|             this.reconstructor.finishedReconstruction();
 | |
|             this.reconstructor = null;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| /**
 | |
|  * A manager of a binary event's 'buffer sequence'. Should
 | |
|  * be constructed whenever a packet of type BINARY_EVENT is
 | |
|  * decoded.
 | |
|  *
 | |
|  * @param {Object} packet
 | |
|  * @return {BinaryReconstructor} initialized reconstructor
 | |
|  */
 | |
| class BinaryReconstructor {
 | |
|     constructor(packet) {
 | |
|         this.packet = packet;
 | |
|         this.buffers = [];
 | |
|         this.reconPack = packet;
 | |
|     }
 | |
|     /**
 | |
|      * Method to be called when binary data received from connection
 | |
|      * after a BINARY_EVENT packet.
 | |
|      *
 | |
|      * @param {Buffer | ArrayBuffer} binData - the raw binary data received
 | |
|      * @return {null | Object} returns null if more binary data is expected or
 | |
|      *   a reconstructed packet object if all buffers have been received.
 | |
|      */
 | |
|     takeBinaryData(binData) {
 | |
|         this.buffers.push(binData);
 | |
|         if (this.buffers.length === this.reconPack.attachments) {
 | |
|             // done with buffer list
 | |
|             const packet = reconstructPacket(this.reconPack, this.buffers);
 | |
|             this.finishedReconstruction();
 | |
|             return packet;
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
|     /**
 | |
|      * Cleans up binary packet reconstruction variables.
 | |
|      */
 | |
|     finishedReconstruction() {
 | |
|         this.reconPack = null;
 | |
|         this.buffers = [];
 | |
|     }
 | |
| }
 |