biz-a-cli 2.3.79-15208 → 2.3.79-15212

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.
@@ -1121,6 +1121,7 @@ export async function retryEffect(payload) {
1121
1121
  length: -1,
1122
1122
  columns: [
1123
1123
  { data: "SYS$BPM_EFFECT_LOG.ID", key: "id" },
1124
+ { data: "SYS$BPM_EFFECT_LOG.STATUS", key: "status" },
1124
1125
  { data: "SYS$BPM_EFFECT_LOG.RETRY_COUNT", key: "retry_count" },
1125
1126
  ],
1126
1127
  filter: [
@@ -1132,18 +1133,19 @@ export async function retryEffect(payload) {
1132
1133
  },
1133
1134
  ],
1134
1135
  };
1135
- const effectRes = await queryData(effectQuery, apiConfig);
1136
1136
 
1137
- if (!effectRes.data || effectRes.data.length === 0) {
1138
- throw new Error("Effect not found.");
1137
+ const effectRes = await queryData(effectQuery, apiConfig);
1138
+ if (!effectRes || effectRes.length !== 1) {
1139
+ throw new Error("Effect not found or found duplicate effects");
1139
1140
  }
1140
1141
 
1141
- const currentRetry = parseInt(effectRes.data[0].retry_count, 10) || 0;
1142
+ if (effectRes[0].status !== "FAILED") {
1143
+ throw new Error("Action Denied: You can only retry FAILED effects.");
1144
+ }
1142
1145
 
1143
1146
  const dbModel = {
1144
1147
  id: effectId,
1145
1148
  status: "RETRYING",
1146
- retry_count: currentRetry + 1,
1147
1149
  error_message: "",
1148
1150
  };
1149
1151
 
@@ -0,0 +1,62 @@
1
+ import { io } from "socket.io-client";
2
+ import { workerEvents, workerStats } from "../worker/cliWorkerPool.js";
3
+
4
+ export class CLI_Agent {
5
+ constructor(dispatcherUrl, services = "ALL") {
6
+ const url = dispatcherUrl.startsWith("http")
7
+ ? dispatcherUrl
8
+ : `http://${dispatcherUrl}`;
9
+
10
+ this.services = services.split(",").map((s) => s.trim().toUpperCase());
11
+
12
+ console.log(`[CLI Agent] Connecting to Message Broker at ${url}...`);
13
+
14
+ this.socketBus = io(`${url}/message-broker`, {
15
+ reconnectionDelay: 5000,
16
+ reconnectionDelayMax: 10000,
17
+ });
18
+
19
+ this.socketBus.on("connect", () => {
20
+ console.log(
21
+ `[CLI Agent] Successfully connected. Listening for triggers...`,
22
+ );
23
+
24
+ // 1. Register Capabilities
25
+ this.socketBus.emit("ESB_AGENT_REGISTER", {
26
+ services: this.services,
27
+ });
28
+
29
+ // 2. The Workload Heartbeat
30
+ if (this.statsInterval) clearInterval(this.statsInterval);
31
+ this.statsInterval = setInterval(() => {
32
+ const stats = workerStats();
33
+ const active = stats.cliScript
34
+ ? stats.cliScript.activeTasks
35
+ : 0;
36
+ const pending = stats.cliScript
37
+ ? stats.cliScript.pendingTasks
38
+ : 0;
39
+ this.socketBus.emit("ESB_AGENT_STATS", {
40
+ activeTasks: active + pending,
41
+ });
42
+ }, 2000);
43
+ });
44
+
45
+ this.socketBus.on("disconnect", () => {
46
+ if (this.statsInterval) clearInterval(this.statsInterval);
47
+ });
48
+
49
+ // 3. Strict Upstream Routing
50
+ workerEvents.on("IPC_RELAY", (topic, payload) => {
51
+ this.socketBus.emit("ESB_UPSTREAM_RELAY", { topic, payload });
52
+ });
53
+ }
54
+
55
+ on(eventName, callback) {
56
+ this.socketBus.on(eventName, callback);
57
+ }
58
+
59
+ publish(topic, payload = null, ackCallback = null) {
60
+ this.socketBus.emit(topic, payload, ackCallback);
61
+ }
62
+ }
@@ -0,0 +1,137 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { workerEvents } from "../worker/cliWorkerPool.js";
3
+
4
+ export class CLI_Dispatcher {
5
+ constructor(mode, ioServerInstance = null) {
6
+ this.mode = mode || "monolithic";
7
+ this.agentRegistry = new Map(); // The Scoreboard
8
+
9
+ if (this.mode === "monolithic") {
10
+ this.localBus = new EventEmitter();
11
+ }
12
+
13
+ console.log(`[Message Broker] Initialized in '${this.mode}' mode.`);
14
+
15
+ if (this.mode === "dispatcher" && ioServerInstance) {
16
+ this.agentNamespace = ioServerInstance.of("/message-broker");
17
+
18
+ this.agentNamespace.on("connection", (socket) => {
19
+ console.log(
20
+ `[Message Broker] Remote Agent connected: ${socket.id}`,
21
+ );
22
+
23
+ this.agentRegistry.set(socket.id, {
24
+ activeTasks: 0,
25
+ services: [],
26
+ });
27
+
28
+ socket.on("ESB_AGENT_REGISTER", (data) => {
29
+ if (this.agentRegistry.has(socket.id)) {
30
+ this.agentRegistry.get(socket.id).services =
31
+ data.services || [];
32
+ }
33
+ });
34
+
35
+ socket.on("ESB_AGENT_STATS", (data) => {
36
+ if (this.agentRegistry.has(socket.id)) {
37
+ this.agentRegistry.get(socket.id).activeTasks =
38
+ data.activeTasks || 0;
39
+ }
40
+ });
41
+
42
+ socket.on("ESB_UPSTREAM_RELAY", ({ topic, payload }) => {
43
+ this.balanceAndDispatchEvent(topic, payload);
44
+ });
45
+
46
+ socket.on("disconnect", () => {
47
+ console.log(
48
+ `[Message Broker] Remote Agent disconnected: ${socket.id}`,
49
+ );
50
+ this.agentRegistry.delete(socket.id);
51
+ });
52
+ });
53
+ }
54
+
55
+ workerEvents.on("IPC_RELAY", (topic, payload) => {
56
+ this.balanceAndDispatchEvent(topic, payload);
57
+ });
58
+ }
59
+
60
+ balanceAndDispatchEvent(topic, payload) {
61
+ // --- EMIT TO SPECIFIC BIZ-A CLIENT ---
62
+ // ex: this.lib.emitClient(scriptData.clientSockId, scriptData.eventUUIDName, { status: "Done!" });
63
+ if (topic === "emitClient") {
64
+ console.log(
65
+ `\n[Future Dev] Target: Biz-A Client Socket ID [${payload?.clientSockId}]`,
66
+ );
67
+ console.log(`[Future Dev] Event: ${payload?.eventUUIDName}`);
68
+ console.log(`[Future Dev] Payload:`, payload?.payload, `\n`);
69
+ // TODO: this.agentNamespace.to(payload.clientSockId).emit(payload.eventUUIDName, payload.payload);
70
+ return;
71
+ }
72
+
73
+ // --- EMIT TO ALL BIZ-A CLIENT ---
74
+ // ex: this.lib.broadcastClient("ui:alert", { message: "System maintenance in 5 mins" });
75
+ if (topic === "broadcastClient") {
76
+ console.log(`\n[Future Dev] Broadcast: ALL Clients in CLIENT_ROOM`);
77
+ console.log(`[Future Dev] Event: ${payload?.topic}`);
78
+ console.log(`[Future Dev] Payload:`, payload?.payload, `\n`);
79
+ // TODO: this.agentNamespace.to("clientRoom").emit(payload.topic, payload.payload);
80
+ return;
81
+ }
82
+
83
+ // --- INTERNAL SYSTEM ROUTING ---
84
+ // ex : this.lib.emitCLI("email:send", data);
85
+ if (this.mode === "monolithic" && this.localBus) {
86
+ this.localBus.emit(topic, payload);
87
+ return;
88
+ }
89
+
90
+ // --- ROUTING TO BEST AGENT (based on workload at each agent) ---
91
+ if (this.mode === "dispatcher" && this.agentNamespace) {
92
+ const requiredService = topic.split(":")[0].toUpperCase();
93
+ let bestAgentId = null;
94
+ let minTasks = Infinity;
95
+
96
+ for (const [socketId, stats] of this.agentRegistry.entries()) {
97
+ const canHandle =
98
+ stats.services.includes("ALL") ||
99
+ stats.services.includes(requiredService);
100
+ if (canHandle && stats.activeTasks < minTasks) {
101
+ minTasks = stats.activeTasks;
102
+ bestAgentId = socketId;
103
+ }
104
+ }
105
+
106
+ if (bestAgentId) {
107
+ console.log(
108
+ `[Load Balancer] Routing '${topic}' to Agent ${bestAgentId} (Active Tasks: ${minTasks})`,
109
+ );
110
+ this.agentNamespace.to(bestAgentId).emit(topic, payload);
111
+ } else {
112
+ console.error(
113
+ `[Load Balancer] WARNING: No connected Agent supports '${requiredService}'`,
114
+ );
115
+ }
116
+ }
117
+ }
118
+
119
+ start(intervalMs = 5000) {
120
+ setInterval(() => {
121
+ this.publish("sys:tick");
122
+ }, intervalMs);
123
+ this.publish("sys:tick");
124
+ }
125
+
126
+ on(eventName, callback) {
127
+ if (this.localBus) this.localBus.on(eventName, callback);
128
+ }
129
+
130
+ publish(topic, payload = null, ackCallback = null) {
131
+ if (this.mode === "monolithic" && this.localBus) {
132
+ this.localBus.emit(topic, payload, ackCallback);
133
+ } else if (this.mode === "dispatcher" && this.agentNamespace) {
134
+ this.agentNamespace.emit(topic, payload, ackCallback);
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,23 @@
1
+ import { CLI_Dispatcher } from "./dispatcher.js";
2
+ import { CLI_Agent } from "./agent.js";
3
+ import { initBpmAgent } from "../engine/bpm/bpm-agent.js";
4
+
5
+ export function bootMicroservices(argv, ioServerInstance = null) {
6
+ const services = argv.services || "ALL";
7
+
8
+ if (argv.mode === "monolithic") {
9
+ const broker = new CLI_Dispatcher("monolithic");
10
+ initBpmAgent(broker, "ALL");
11
+ broker.start(5000);
12
+ return broker;
13
+ } else if (argv.mode === "dispatcher") {
14
+ const broker = new CLI_Dispatcher("dispatcher", ioServerInstance);
15
+ initBpmAgent(broker, services);
16
+ broker.start(5000);
17
+ return broker;
18
+ } else if (argv.mode === "agent") {
19
+ const genericAgent = new CLI_Agent(argv.server, services);
20
+ initBpmAgent(genericAgent, services);
21
+ return genericAgent;
22
+ }
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biz-a-cli",
3
- "version": "2.3.79-15208",
3
+ "version": "2.3.79-15212",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "type": "module",
@@ -14,6 +14,7 @@
14
14
  "engine/**/*",
15
15
  "envs/**/*",
16
16
  "key/**/*",
17
+ "message-broker/**/*",
17
18
  "migrations/**/*",
18
19
  "scheduler/**/*",
19
20
  "worker/**/*",
package/readme.md CHANGED
@@ -83,9 +83,9 @@
83
83
 
84
84
  Example :
85
85
  hub --server https://server.biz-a.id --sub imamatek --hostname localhost --port 212 --publish
86
-
87
-
88
- ## IV. Scalability (work in progress)
86
+
87
+
88
+ ## IV. Scalability
89
89
 
90
90
  ### a. Monolithic Mode (Default)
91
91
  hub --mode monolithic --server [BizA Hub Server] --sub [subdomain]