@omicron-x/daedalus-sdk 1.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 ADDED
@@ -0,0 +1,63 @@
1
+ # Daedalus Node.js Worker SDK
2
+
3
+ Node.js/TypeScript SDK for interacting with the Daedalus Orchestrator.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ cd sdk/nodejs-sdk
9
+ npm install
10
+ ```
11
+
12
+ ## How to Build
13
+
14
+ You can compile the SDK using Nx from the project root:
15
+
16
+ ```bash
17
+ nx run server:build-sdk-nodejs
18
+ ```
19
+
20
+ Or manually within the `sdk/nodejs-sdk` folder:
21
+
22
+ ```bash
23
+ npm run build
24
+ ```
25
+
26
+ ## Examples
27
+
28
+ ### Simple Worker
29
+
30
+ To run the simple worker example:
31
+
32
+ ```bash
33
+ nx run server:run-nodejs-simple-worker
34
+ ```
35
+
36
+ Or manually:
37
+
38
+ ```bash
39
+ cd sdk/nodejs-sdk
40
+ npx ts-node examples/simple-worker/index.ts
41
+ ```
42
+
43
+ ### Assert Resources
44
+
45
+ To run the assert resources example:
46
+
47
+ ```bash
48
+ nx run server:run-nodejs-assert-resources
49
+ ```
50
+
51
+ Demonstrates how to upsert a tenant, exchange, queue, and binding using the `assertTenant`, `assertExchange`, `assertQueue`, and `assertBinding` methods.
52
+
53
+ ```bash
54
+ cd sdk/nodejs-sdk
55
+ npm run example:assert-resources
56
+ ```
57
+
58
+ Or manually:
59
+
60
+ ```bash
61
+ cd sdk/nodejs-sdk
62
+ npx ts-node examples/assert-resources/index.ts
63
+ ```
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const index_1 = require("../../src/index");
37
+ const si = __importStar(require("systeminformation"));
38
+ async function getSystemInfo() {
39
+ try {
40
+ const cpu = await si.currentLoad();
41
+ const mem = await si.mem();
42
+ const disk = await si.fsSize();
43
+ const info = {
44
+ "CPU": cpu.currentLoad.toFixed(2),
45
+ "Memory": ((mem.active / mem.total) * 100).toFixed(2),
46
+ "Disk": disk[0].use.toFixed(2),
47
+ "OS": String(process.platform),
48
+ "Hostname": await si.osInfo().then(info => String(info.hostname))
49
+ };
50
+ return info;
51
+ }
52
+ catch (err) {
53
+ console.error('❌ Error gathering system info:', err);
54
+ return {
55
+ "Error": "Failed to gather system info"
56
+ };
57
+ }
58
+ }
59
+ async function main() {
60
+ const daedalusSDK = new index_1.DaedalusSDK({
61
+ uri: 'http://localhost:4000',
62
+ username: 'admin',
63
+ password: 'admin'
64
+ });
65
+ await daedalusSDK.connect();
66
+ await daedalusSDK.createWorker({
67
+ workerName: 'Simple Node.js Worker 2',
68
+ intervalMs: 10000,
69
+ information: getSystemInfo,
70
+ capacityPolicies: [
71
+ {
72
+ maxQueueMessages: 10,
73
+ currentQueueMessages: 0,
74
+ claimWorkFilter: {}
75
+ }
76
+ ],
77
+ onMessage: async (message, ack) => {
78
+ console.log('👷 Processing message:', message);
79
+ console.log('📝 Content:', message);
80
+ // Simulate processing
81
+ await new Promise(resolve => setTimeout(resolve, 1000));
82
+ // Acknowledge the message after processing
83
+ console.log('✅ Message processed, sending ACK...');
84
+ await ack();
85
+ }
86
+ });
87
+ console.log('✅ Worker is running. Press Ctrl+C to stop.');
88
+ }
89
+ main().catch(err => {
90
+ console.error('💥 Fatal error:', err);
91
+ });
@@ -0,0 +1,73 @@
1
+ export interface ClaimWorkFilter {
2
+ tenantCodes?: string[];
3
+ excludeTenantCodes?: string[];
4
+ tenantPatterns?: string[];
5
+ excludeTenantPatterns?: string[];
6
+ vNamespaces?: string[];
7
+ excludeVNamespaces?: string[];
8
+ vNamespacePatterns?: string[];
9
+ excludeVNamespacePatterns?: string[];
10
+ queueCodes?: string[];
11
+ excludeQueueCodes?: string[];
12
+ queuePatterns?: string[];
13
+ excludeQueuePatterns?: string[];
14
+ }
15
+ export interface ClaimWorkCapacityPolicy {
16
+ maxQueueMessages: number;
17
+ currentQueueMessages: number;
18
+ claimWorkFilter?: ClaimWorkFilter;
19
+ }
20
+ export interface QueueMessage {
21
+ id: string;
22
+ messageId: string;
23
+ content: string;
24
+ contentType: string;
25
+ headers: Record<string, string>;
26
+ queueId: string;
27
+ priority: number;
28
+ handler: string;
29
+ parameters: Record<string, string>;
30
+ vNamespace: string;
31
+ createdAt: string;
32
+ }
33
+ export interface QueueMessageLease {
34
+ id: string;
35
+ queueMessageId: string;
36
+ workerId: string;
37
+ leaseUntil: string;
38
+ }
39
+ export interface ClaimedMessage {
40
+ message: QueueMessage;
41
+ lease: QueueMessageLease;
42
+ tenantCode: string;
43
+ }
44
+ export interface AckCallback {
45
+ (): Promise<void>;
46
+ }
47
+ export interface WorkerOptions {
48
+ workerName: string;
49
+ information?: Record<string, string> | (() => Promise<Record<string, string>> | Record<string, string>);
50
+ capacityPolicies: ClaimWorkCapacityPolicy[];
51
+ intervalMs?: number;
52
+ onMessage?: (claimedMessage: ClaimedMessage, ack: AckCallback) => Promise<void> | void;
53
+ }
54
+ export declare class DaedalusSDK {
55
+ private config;
56
+ private jobWorkerClient;
57
+ private authClient;
58
+ private token;
59
+ private jobWorkerProtoPath;
60
+ private authProtoPath;
61
+ constructor(config: {
62
+ uri: string;
63
+ username: string;
64
+ password: string;
65
+ });
66
+ private loadProto;
67
+ connect(): Promise<void>;
68
+ private login;
69
+ disconnect(): Promise<void>;
70
+ private getMetadata;
71
+ ackMessage(leaseID: string, tenantCode: string): Promise<void>;
72
+ createWorker(options: WorkerOptions): Promise<void>;
73
+ }
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DaedalusSDK = void 0;
37
+ const grpc = __importStar(require("@grpc/grpc-js"));
38
+ const protoLoader = __importStar(require("@grpc/proto-loader"));
39
+ const path = __importStar(require("path"));
40
+ const crypto = require('crypto');
41
+ class DaedalusSDK {
42
+ config;
43
+ jobWorkerClient;
44
+ authClient;
45
+ token = null;
46
+ jobWorkerProtoPath;
47
+ authProtoPath;
48
+ constructor(config) {
49
+ this.config = config;
50
+ this.jobWorkerProtoPath = path.resolve(__dirname, '../../../server/internal/infrastructure/server/grpc/proto/definitions/jobworker.proto');
51
+ this.authProtoPath = path.resolve(__dirname, '../../../server/internal/infrastructure/server/grpc/proto/definitions/auth.proto');
52
+ }
53
+ loadProto(protoPath) {
54
+ const packageDefinition = protoLoader.loadSync(protoPath, {
55
+ keepCase: true,
56
+ longs: String,
57
+ enums: String,
58
+ defaults: true,
59
+ oneofs: true
60
+ });
61
+ return grpc.loadPackageDefinition(packageDefinition);
62
+ }
63
+ async connect() {
64
+ const target = this.config.uri.replace('http://', '').replace('https://', '');
65
+ // Load Auth Proto and create client
66
+ const authProtoDescriptor = this.loadProto(this.authProtoPath);
67
+ this.authClient = new authProtoDescriptor.auth.AuthService(target, grpc.credentials.createInsecure());
68
+ // Perform Initial Login
69
+ await this.login();
70
+ // Load JobWorker Proto and create client
71
+ const jobWorkerProtoDescriptor = this.loadProto(this.jobWorkerProtoPath);
72
+ this.jobWorkerClient = new jobWorkerProtoDescriptor.jobworker.JobWorkerService(target, grpc.credentials.createInsecure());
73
+ }
74
+ async login() {
75
+ console.log(`🔐 Logging in as ${this.config.username}...`);
76
+ try {
77
+ const loginResponse = await new Promise((resolve, reject) => {
78
+ this.authClient.Login({
79
+ usernameOrEmail: this.config.username,
80
+ password: this.config.password
81
+ }, (err, response) => {
82
+ if (err)
83
+ return reject(err);
84
+ resolve(response);
85
+ });
86
+ });
87
+ this.token = loginResponse.token;
88
+ console.log('✅ Logged in successfully');
89
+ }
90
+ catch (err) {
91
+ console.error('❌ Login failed:', err.message);
92
+ throw err;
93
+ }
94
+ }
95
+ async disconnect() {
96
+ if (this.jobWorkerClient) {
97
+ this.jobWorkerClient.close();
98
+ }
99
+ if (this.authClient) {
100
+ this.authClient.close();
101
+ }
102
+ }
103
+ getMetadata() {
104
+ const metadata = new grpc.Metadata();
105
+ if (this.token) {
106
+ metadata.add('Authorization', `Bearer ${this.token}`);
107
+ }
108
+ return metadata;
109
+ }
110
+ async ackMessage(leaseID, tenantCode) {
111
+ return new Promise((resolve, reject) => {
112
+ this.jobWorkerClient.AckMessage({ leaseID, tenantCode }, this.getMetadata(), (err, response) => {
113
+ if (err) {
114
+ console.error('❌ Failed to ack message:', err.message);
115
+ return reject(err);
116
+ }
117
+ if (!response.success) {
118
+ console.error('❌ Ack message failed:', response.message);
119
+ return reject(new Error(response.message));
120
+ }
121
+ console.log('✅ Message acknowledged successfully');
122
+ resolve();
123
+ });
124
+ });
125
+ }
126
+ async createWorker(options) {
127
+ const { workerName, information, capacityPolicies, intervalMs = 10000, // 10 seconds
128
+ onMessage } = options;
129
+ const workerId = `${crypto.randomUUID()}-${Date.now()}`;
130
+ const run = async () => {
131
+ try {
132
+ if (!this.token) {
133
+ console.log('⚠️ Not authenticated. Attempting to log in...');
134
+ await this.login();
135
+ }
136
+ // Create bidirectional stream
137
+ const call = this.jobWorkerClient.ClaimWork(this.getMetadata());
138
+ console.log(`🔌 Opening bidirectional stream for worker ${workerId}...`);
139
+ // Handle incoming messages from server
140
+ call.on('data', async (streamMessage) => {
141
+ if (streamMessage.ack) {
142
+ console.log('✅ Connected to server:', streamMessage.ack.knowledge);
143
+ }
144
+ else if (streamMessage.claimedMessage) {
145
+ const claimed = streamMessage.claimedMessage;
146
+ console.log(`📬 Received message: ${claimed.message.ID} from tenant ${claimed.tenantCode}`);
147
+ if (onMessage) {
148
+ try {
149
+ const claimedMessage = {
150
+ message: {
151
+ id: claimed.message.ID,
152
+ messageId: claimed.message.MessageID,
153
+ content: claimed.message.Content,
154
+ contentType: claimed.message.ContentType,
155
+ headers: claimed.message.Headers || {},
156
+ queueId: claimed.message.QueueID,
157
+ priority: claimed.message.Priority,
158
+ handler: claimed.message.Handler,
159
+ parameters: claimed.message.Parameters || {},
160
+ vNamespace: claimed.message.VNamespace,
161
+ createdAt: claimed.message.CreatedAt
162
+ },
163
+ lease: {
164
+ id: claimed.lease.ID,
165
+ queueMessageId: claimed.lease.QueueMessageID,
166
+ workerId: claimed.lease.WorkerID,
167
+ leaseUntil: claimed.lease.LeaseUntil
168
+ },
169
+ tenantCode: claimed.tenantCode
170
+ };
171
+ const ackCallback = async () => {
172
+ await this.ackMessage(claimed.lease.ID, claimed.tenantCode);
173
+ };
174
+ await onMessage(claimedMessage, ackCallback);
175
+ }
176
+ catch (handlerError) {
177
+ console.error('❌ Error in onMessage handler:', handlerError.message);
178
+ }
179
+ }
180
+ }
181
+ });
182
+ call.on('error', (err) => {
183
+ if (err.code === 16) { // UNAUTHENTICATED
184
+ console.warn('🔄 Session expired (Error 16). Refreshing token...');
185
+ this.token = null;
186
+ }
187
+ else if (err.code === 1) { // CANCELLED
188
+ console.log('🚫 Stream cancelled');
189
+ }
190
+ else {
191
+ console.error('❌ Stream error:', err.message);
192
+ }
193
+ });
194
+ call.on('end', () => {
195
+ console.log('🔌 Stream ended, will reconnect...');
196
+ });
197
+ // Function to send claim request
198
+ const sendClaimRequest = async () => {
199
+ let currentInformation = {};
200
+ if (typeof information === 'function') {
201
+ currentInformation = await information();
202
+ }
203
+ else if (information) {
204
+ currentInformation = information;
205
+ }
206
+ const request = {
207
+ workerID: workerId,
208
+ workerName: workerName,
209
+ information: currentInformation,
210
+ capacityPolicies: capacityPolicies
211
+ };
212
+ call.write(request, (err) => {
213
+ if (err) {
214
+ console.error('❌ Error sending claim request:', err.message);
215
+ }
216
+ });
217
+ };
218
+ // Send initial claim request
219
+ await sendClaimRequest();
220
+ // Send claim requests periodically
221
+ const claimInterval = setInterval(async () => {
222
+ await sendClaimRequest();
223
+ }, intervalMs);
224
+ // Wait for stream to end
225
+ await new Promise((resolve) => {
226
+ call.on('end', () => {
227
+ clearInterval(claimInterval);
228
+ resolve();
229
+ });
230
+ call.on('error', () => {
231
+ clearInterval(claimInterval);
232
+ resolve();
233
+ });
234
+ });
235
+ }
236
+ catch (err) {
237
+ console.error('❌ Unexpected error in worker loop:', err.message);
238
+ }
239
+ // Reconnect after delay
240
+ console.log(`⏳ Reconnecting in ${intervalMs}ms...`);
241
+ setTimeout(run, intervalMs);
242
+ };
243
+ console.log(`🚀 Starting worker ${workerName} (${workerId}) with ${intervalMs}ms interval...`);
244
+ run();
245
+ }
246
+ }
247
+ exports.DaedalusSDK = DaedalusSDK;
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@omicron-x/daedalus-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Daedalus Orchestrator Node.js Worker",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "license": "Apache-2.0",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "example:simple-worker": "ts-node examples/simple-worker/index.ts",
17
+ "example:assert-resources": "ts-node examples/assert-resources/index.ts"
18
+ },
19
+ "dependencies": {
20
+ "@grpc/grpc-js": "^1.9.0",
21
+ "@grpc/proto-loader": "^0.7.8",
22
+ "systeminformation": "^5.21.0"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "^5.0.0",
26
+ "ts-node": "^10.9.0",
27
+ "@types/node": "^20.0.0"
28
+ }
29
+ }