colonies-ts 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,944 @@
1
+ // src/crypto.ts
2
+ import { sha3_256 } from "@noble/hashes/sha3";
3
+ import { sha256 } from "@noble/hashes/sha256";
4
+ import { hmac } from "@noble/hashes/hmac";
5
+ import { bytesToHex, hexToBytes, randomBytes } from "@noble/hashes/utils";
6
+ var A = 0n;
7
+ var N = 115792089237316195423570985008687907852837564279074904382605163141518161494337n;
8
+ var Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
9
+ var Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
10
+ var G = [Gx, Gy];
11
+ var P = 2n ** 256n - 2n ** 32n - 977n;
12
+ function pad32(value) {
13
+ if (value.length >= 32) return value.slice(0, 32);
14
+ const padded = new Uint8Array(32);
15
+ padded.set(value, 32 - value.length);
16
+ return padded;
17
+ }
18
+ function intToBigEndian(value) {
19
+ if (value === 0n) return new Uint8Array([0]);
20
+ const hex = value.toString(16);
21
+ const paddedHex = hex.length % 2 ? "0" + hex : hex;
22
+ return hexToBytes(paddedHex);
23
+ }
24
+ function bigEndianToInt(value) {
25
+ if (value.length === 0) return 0n;
26
+ return BigInt("0x" + bytesToHex(value));
27
+ }
28
+ function inv(a, n) {
29
+ if (a === 0n) return 0n;
30
+ let lm = 1n, hm = 0n;
31
+ let low = (a % n + n) % n, high = n;
32
+ while (low > 1n) {
33
+ const r = high / low;
34
+ const nm = hm - lm * r;
35
+ const newVal = high - low * r;
36
+ hm = lm;
37
+ high = low;
38
+ lm = nm;
39
+ low = newVal;
40
+ }
41
+ return (lm % n + n) % n;
42
+ }
43
+ function toJacobian(p) {
44
+ return [p[0], p[1], 1n];
45
+ }
46
+ function fromJacobian(p) {
47
+ const z = inv(p[2], P);
48
+ const z2 = z * z % P;
49
+ const z3 = z2 * z % P;
50
+ return [(p[0] * z2 % P + P) % P, (p[1] * z3 % P + P) % P];
51
+ }
52
+ function jacobianDouble(p) {
53
+ if (p[1] === 0n) return [0n, 0n, 0n];
54
+ const ysq = p[1] ** 2n % P;
55
+ const S = 4n * p[0] * ysq % P;
56
+ const M = (3n * p[0] ** 2n + A * p[2] ** 4n) % P;
57
+ const nx = ((M ** 2n - 2n * S) % P + P) % P;
58
+ const ny = ((M * (S - nx) - 8n * ysq ** 2n) % P + P) % P;
59
+ const nz = 2n * p[1] * p[2] % P;
60
+ return [nx, ny, nz];
61
+ }
62
+ function jacobianAdd(p, q) {
63
+ if (p[1] === 0n) return q;
64
+ if (q[1] === 0n) return p;
65
+ const U1 = p[0] * q[2] ** 2n % P;
66
+ const U2 = q[0] * p[2] ** 2n % P;
67
+ const S1 = p[1] * q[2] ** 3n % P;
68
+ const S2 = q[1] * p[2] ** 3n % P;
69
+ if (U1 === U2) {
70
+ if (S1 !== S2) return [0n, 0n, 1n];
71
+ return jacobianDouble(p);
72
+ }
73
+ const H = ((U2 - U1) % P + P) % P;
74
+ const R = ((S2 - S1) % P + P) % P;
75
+ const H2 = H * H % P;
76
+ const H3 = H * H2 % P;
77
+ const U1H2 = U1 * H2 % P;
78
+ const nx = ((R ** 2n - H3 - 2n * U1H2) % P + P) % P;
79
+ const ny = ((R * (U1H2 - nx) - S1 * H3) % P + P) % P;
80
+ const nz = H * p[2] * q[2] % P;
81
+ return [nx, ny, nz];
82
+ }
83
+ function jacobianMultiply(a, n) {
84
+ if (a[1] === 0n || n === 0n) return [0n, 0n, 1n];
85
+ if (n === 1n) return a;
86
+ if (n < 0n || n >= N) return jacobianMultiply(a, (n % N + N) % N);
87
+ if (n % 2n === 0n) {
88
+ return jacobianDouble(jacobianMultiply(a, n / 2n));
89
+ } else {
90
+ return jacobianAdd(jacobianDouble(jacobianMultiply(a, n / 2n)), a);
91
+ }
92
+ }
93
+ function fastMultiply(a, n) {
94
+ return fromJacobian(jacobianMultiply(toJacobian(a), n));
95
+ }
96
+ function encodeRawPublicKey(rawPublicKey) {
97
+ const left = pad32(intToBigEndian(rawPublicKey[0]));
98
+ const right = pad32(intToBigEndian(rawPublicKey[1]));
99
+ const result = new Uint8Array(64);
100
+ result.set(left, 0);
101
+ result.set(right, 32);
102
+ return result;
103
+ }
104
+ function privateKeyToPublicKey(privateKeyBytes) {
105
+ const privateKeyAsNum = bigEndianToInt(privateKeyBytes);
106
+ if (privateKeyAsNum >= N) {
107
+ throw new Error("Invalid private key");
108
+ }
109
+ const rawPublicKey = fastMultiply(G, privateKeyAsNum);
110
+ return encodeRawPublicKey(rawPublicKey);
111
+ }
112
+ function deterministicGenerateK(msgHash, privateKeyBytes) {
113
+ const v0 = new Uint8Array(32).fill(1);
114
+ const k0 = new Uint8Array(32).fill(0);
115
+ const data1 = new Uint8Array(v0.length + 1 + privateKeyBytes.length + msgHash.length);
116
+ data1.set(v0, 0);
117
+ data1[v0.length] = 0;
118
+ data1.set(privateKeyBytes, v0.length + 1);
119
+ data1.set(msgHash, v0.length + 1 + privateKeyBytes.length);
120
+ const k1 = hmac(sha256, k0, data1);
121
+ const v1 = hmac(sha256, k1, v0);
122
+ const data2 = new Uint8Array(v1.length + 1 + privateKeyBytes.length + msgHash.length);
123
+ data2.set(v1, 0);
124
+ data2[v1.length] = 1;
125
+ data2.set(privateKeyBytes, v1.length + 1);
126
+ data2.set(msgHash, v1.length + 1 + privateKeyBytes.length);
127
+ const k2 = hmac(sha256, k1, data2);
128
+ const v2 = hmac(sha256, k2, v1);
129
+ const kb = hmac(sha256, k2, v2);
130
+ return bigEndianToInt(kb);
131
+ }
132
+ function ecdsaRawSign(msgHash, privateKeyBytes) {
133
+ const z = bigEndianToInt(msgHash);
134
+ const k = deterministicGenerateK(msgHash, privateKeyBytes);
135
+ const [r, y] = fastMultiply(G, k);
136
+ const privKeyNum = bigEndianToInt(privateKeyBytes);
137
+ const sRaw = inv(k, N) * (z + r * privKeyNum) % N;
138
+ const v = 27 + Number(y % 2n ^ (sRaw * 2n < N ? 0n : 1n));
139
+ const s = sRaw * 2n < N ? sRaw : N - sRaw;
140
+ return [v - 27, r, s];
141
+ }
142
+ function generatePrivateKey() {
143
+ const randomData = randomBytes(32);
144
+ const hash = sha3_256(randomData);
145
+ return bytesToHex(hash);
146
+ }
147
+ function deriveId(privateKey) {
148
+ const privateKeyBytes = hexToBytes(privateKey);
149
+ const publicKey = privateKeyToPublicKey(privateKeyBytes);
150
+ const publicKeyHex = "04" + bytesToHex(publicKey);
151
+ const encoder = new TextEncoder();
152
+ const hash = sha3_256(encoder.encode(publicKeyHex));
153
+ return bytesToHex(hash);
154
+ }
155
+ function sign(message, privateKey) {
156
+ const privateKeyBytes = hexToBytes(privateKey);
157
+ const encoder = new TextEncoder();
158
+ const msgHash = sha3_256(encoder.encode(message));
159
+ const [v, r, s] = ecdsaRawSign(msgHash, privateKeyBytes);
160
+ const vBytes = new Uint8Array([v]);
161
+ const rBytes = pad32(intToBigEndian(r));
162
+ const sBytes = pad32(intToBigEndian(s));
163
+ const signature = new Uint8Array(65);
164
+ signature.set(rBytes, 0);
165
+ signature.set(sBytes, 32);
166
+ signature.set(vBytes, 64);
167
+ return bytesToHex(signature);
168
+ }
169
+ var Crypto = class {
170
+ generatePrivateKey() {
171
+ return generatePrivateKey();
172
+ }
173
+ id(privateKey) {
174
+ return deriveId(privateKey);
175
+ }
176
+ sign(message, privateKey) {
177
+ return sign(message, privateKey);
178
+ }
179
+ };
180
+
181
+ // src/client.ts
182
+ function decodeBase64Utf8(base64) {
183
+ const binaryStr = atob(base64);
184
+ const bytes = new Uint8Array(binaryStr.length);
185
+ for (let i = 0; i < binaryStr.length; i++) {
186
+ bytes[i] = binaryStr.charCodeAt(i);
187
+ }
188
+ return new TextDecoder("utf-8").decode(bytes);
189
+ }
190
+ function encodeBase64Utf8(str) {
191
+ const utf8Bytes = new TextEncoder().encode(str);
192
+ const binaryStr = String.fromCharCode(...utf8Bytes);
193
+ return btoa(binaryStr);
194
+ }
195
+ var ProcessState = /* @__PURE__ */ ((ProcessState2) => {
196
+ ProcessState2[ProcessState2["WAITING"] = 0] = "WAITING";
197
+ ProcessState2[ProcessState2["RUNNING"] = 1] = "RUNNING";
198
+ ProcessState2[ProcessState2["SUCCESS"] = 2] = "SUCCESS";
199
+ ProcessState2[ProcessState2["FAILED"] = 3] = "FAILED";
200
+ return ProcessState2;
201
+ })(ProcessState || {});
202
+ var ColoniesClient = class {
203
+ constructor(config) {
204
+ this.privateKey = null;
205
+ this.host = config.host;
206
+ this.port = config.port;
207
+ this.tls = config.tls ?? false;
208
+ this.crypto = new Crypto();
209
+ }
210
+ setPrivateKey(privateKey) {
211
+ this.privateKey = privateKey;
212
+ }
213
+ getBaseUrl() {
214
+ const protocol = this.tls ? "https" : "http";
215
+ return `${protocol}://${this.host}:${this.port}/api`;
216
+ }
217
+ createRPCMsg(msg) {
218
+ if (!this.privateKey) {
219
+ throw new Error("Private key not set. Call setPrivateKey() first.");
220
+ }
221
+ const payload = encodeBase64Utf8(JSON.stringify(msg));
222
+ const signature = this.crypto.sign(payload, this.privateKey);
223
+ return {
224
+ payloadtype: msg.msgtype,
225
+ payload,
226
+ signature
227
+ };
228
+ }
229
+ async sendRPC(rpcMessage) {
230
+ const url = this.getBaseUrl();
231
+ const response = await fetch(url, {
232
+ method: "POST",
233
+ headers: {
234
+ "Content-Type": "application/json"
235
+ },
236
+ body: JSON.stringify(rpcMessage)
237
+ });
238
+ if (!response.ok) {
239
+ const errorText = await response.text();
240
+ let errorObj;
241
+ try {
242
+ errorObj = JSON.parse(errorText);
243
+ } catch {
244
+ throw new Error(`Request failed with status ${response.status}: ${response.statusText}`);
245
+ }
246
+ if (errorObj.payload) {
247
+ try {
248
+ const decodedPayload = decodeBase64Utf8(errorObj.payload);
249
+ const decodedError = JSON.parse(decodedPayload);
250
+ throw new Error(decodedError.message || JSON.stringify(decodedError));
251
+ } catch (e) {
252
+ if (e instanceof Error && e.message !== "Request failed") {
253
+ throw e;
254
+ }
255
+ }
256
+ }
257
+ throw new Error(JSON.stringify(errorObj));
258
+ }
259
+ const responseText = await response.text();
260
+ if (!responseText || responseText.trim() === "") {
261
+ throw new Error("Server returned empty response");
262
+ }
263
+ const rpcReplyMsg = JSON.parse(responseText);
264
+ const msg = JSON.parse(decodeBase64Utf8(rpcReplyMsg.payload));
265
+ if (rpcReplyMsg.error === true) {
266
+ const errorMessage = typeof msg === "object" && msg.message ? msg.message : JSON.stringify(msg);
267
+ throw new Error(errorMessage);
268
+ }
269
+ return msg;
270
+ }
271
+ // ==================== Colony Methods ====================
272
+ async getColonies() {
273
+ const msg = { msgtype: "getcoloniesmsg" };
274
+ return this.sendRPC(this.createRPCMsg(msg));
275
+ }
276
+ async getStatistics() {
277
+ const msg = { msgtype: "getstatisticsmsg" };
278
+ return this.sendRPC(this.createRPCMsg(msg));
279
+ }
280
+ /**
281
+ * Add a new colony (requires server private key)
282
+ * @param colony - Colony object with colonyid and name
283
+ */
284
+ async addColony(colony) {
285
+ const msg = {
286
+ msgtype: "addcolonymsg",
287
+ colony
288
+ };
289
+ return this.sendRPC(this.createRPCMsg(msg));
290
+ }
291
+ /**
292
+ * Remove a colony (requires server private key)
293
+ * @param colonyName - Name of the colony to remove
294
+ */
295
+ async removeColony(colonyName) {
296
+ const msg = {
297
+ msgtype: "removecolonymsg",
298
+ colonyname: colonyName
299
+ };
300
+ return this.sendRPC(this.createRPCMsg(msg));
301
+ }
302
+ // ==================== Executor Methods ====================
303
+ async getExecutors(colonyName) {
304
+ const msg = {
305
+ msgtype: "getexecutorsmsg",
306
+ colonyname: colonyName
307
+ };
308
+ return this.sendRPC(this.createRPCMsg(msg));
309
+ }
310
+ async getExecutor(colonyName, executorName) {
311
+ const msg = {
312
+ msgtype: "getexecutormsg",
313
+ colonyname: colonyName,
314
+ executorname: executorName
315
+ };
316
+ return this.sendRPC(this.createRPCMsg(msg));
317
+ }
318
+ async addExecutor(executor) {
319
+ const msg = {
320
+ msgtype: "addexecutormsg",
321
+ executor
322
+ };
323
+ return this.sendRPC(this.createRPCMsg(msg));
324
+ }
325
+ async approveExecutor(colonyName, executorName) {
326
+ const msg = {
327
+ msgtype: "approveexecutormsg",
328
+ colonyname: colonyName,
329
+ executorname: executorName
330
+ };
331
+ return this.sendRPC(this.createRPCMsg(msg));
332
+ }
333
+ async removeExecutor(colonyName, executorName) {
334
+ const msg = {
335
+ msgtype: "removeexecutormsg",
336
+ colonyname: colonyName,
337
+ executorname: executorName
338
+ };
339
+ return this.sendRPC(this.createRPCMsg(msg));
340
+ }
341
+ // ==================== Process Methods ====================
342
+ async submitFunctionSpec(spec) {
343
+ const msg = {
344
+ msgtype: "submitfuncspecmsg",
345
+ spec
346
+ };
347
+ return this.sendRPC(this.createRPCMsg(msg));
348
+ }
349
+ async getProcess(processId) {
350
+ const msg = {
351
+ msgtype: "getprocessmsg",
352
+ processid: processId
353
+ };
354
+ return this.sendRPC(this.createRPCMsg(msg));
355
+ }
356
+ async getProcesses(colonyName, count, state) {
357
+ const msg = {
358
+ msgtype: "getprocessesmsg",
359
+ colonyname: colonyName,
360
+ count,
361
+ state
362
+ };
363
+ return this.sendRPC(this.createRPCMsg(msg));
364
+ }
365
+ async removeProcess(processId) {
366
+ const msg = {
367
+ msgtype: "removeprocessmsg",
368
+ processid: processId,
369
+ all: false
370
+ };
371
+ return this.sendRPC(this.createRPCMsg(msg));
372
+ }
373
+ async removeAllProcesses(colonyName, state = -1) {
374
+ const msg = {
375
+ msgtype: "removeallprocessesmsg",
376
+ colonyname: colonyName,
377
+ state
378
+ };
379
+ return this.sendRPC(this.createRPCMsg(msg));
380
+ }
381
+ async assign(colonyName, timeout, executorPrvKey) {
382
+ const originalKey = this.privateKey;
383
+ this.setPrivateKey(executorPrvKey);
384
+ try {
385
+ const msg = {
386
+ msgtype: "assignprocessmsg",
387
+ colonyname: colonyName,
388
+ timeout
389
+ };
390
+ return this.sendRPC(this.createRPCMsg(msg));
391
+ } finally {
392
+ if (originalKey) {
393
+ this.setPrivateKey(originalKey);
394
+ }
395
+ }
396
+ }
397
+ async closeProcess(processId, output) {
398
+ const msg = {
399
+ msgtype: "closesuccessfulmsg",
400
+ processid: processId,
401
+ out: output
402
+ };
403
+ return this.sendRPC(this.createRPCMsg(msg));
404
+ }
405
+ async failProcess(processId, errors) {
406
+ const msg = {
407
+ msgtype: "closefailedmsg",
408
+ processid: processId,
409
+ errors
410
+ };
411
+ return this.sendRPC(this.createRPCMsg(msg));
412
+ }
413
+ // ==================== Workflow Methods ====================
414
+ async submitWorkflowSpec(workflowSpec) {
415
+ const msg = {
416
+ msgtype: "submitworkflowspecmsg",
417
+ spec: workflowSpec
418
+ };
419
+ return this.sendRPC(this.createRPCMsg(msg));
420
+ }
421
+ async getProcessGraph(processGraphId) {
422
+ const msg = {
423
+ msgtype: "getprocessgraphmsg",
424
+ processgraphid: processGraphId
425
+ };
426
+ return this.sendRPC(this.createRPCMsg(msg));
427
+ }
428
+ async getProcessGraphs(colonyName, count, state) {
429
+ const msg = {
430
+ msgtype: "getprocessgraphsmsg",
431
+ colonyname: colonyName,
432
+ count
433
+ };
434
+ if (state !== void 0) {
435
+ msg.state = state;
436
+ }
437
+ return this.sendRPC(this.createRPCMsg(msg));
438
+ }
439
+ async removeProcessGraph(processGraphId) {
440
+ const msg = {
441
+ msgtype: "removeprocessgraphmsg",
442
+ processgraphid: processGraphId
443
+ };
444
+ return this.sendRPC(this.createRPCMsg(msg));
445
+ }
446
+ async removeAllProcessGraphs(colonyName, state) {
447
+ const msg = {
448
+ msgtype: "removeallprocessgraphsmsg",
449
+ colonyname: colonyName
450
+ };
451
+ if (state !== void 0) {
452
+ msg.state = state;
453
+ }
454
+ return this.sendRPC(this.createRPCMsg(msg));
455
+ }
456
+ // ==================== Log Methods ====================
457
+ async addLog(processId, message, executorPrvKey) {
458
+ const originalKey = this.privateKey;
459
+ this.setPrivateKey(executorPrvKey);
460
+ try {
461
+ const msg = {
462
+ msgtype: "addlogmsg",
463
+ processid: processId,
464
+ message
465
+ };
466
+ return this.sendRPC(this.createRPCMsg(msg));
467
+ } finally {
468
+ if (originalKey) {
469
+ this.setPrivateKey(originalKey);
470
+ }
471
+ }
472
+ }
473
+ async getLogs(colonyName, processId, executorName, count = 100, since = 0) {
474
+ const msg = {
475
+ msgtype: "getlogsmsg",
476
+ colonyname: colonyName,
477
+ processid: processId,
478
+ executorname: executorName,
479
+ count,
480
+ since
481
+ };
482
+ return this.sendRPC(this.createRPCMsg(msg));
483
+ }
484
+ // ==================== Function Methods ====================
485
+ async getFunctions(executorName, colonyName) {
486
+ const msg = {
487
+ msgtype: "getfunctionsmsg",
488
+ executorname: executorName,
489
+ colonyname: colonyName
490
+ };
491
+ return this.sendRPC(this.createRPCMsg(msg));
492
+ }
493
+ // ==================== Cron Methods ====================
494
+ async getCrons(colonyName, count = 100) {
495
+ const msg = {
496
+ msgtype: "getcronsmsg",
497
+ colonyname: colonyName,
498
+ count
499
+ };
500
+ return this.sendRPC(this.createRPCMsg(msg));
501
+ }
502
+ async getCron(cronId) {
503
+ const msg = {
504
+ msgtype: "getcronmsg",
505
+ cronid: cronId
506
+ };
507
+ return this.sendRPC(this.createRPCMsg(msg));
508
+ }
509
+ async addCron(cronSpec) {
510
+ const msg = {
511
+ msgtype: "addcronmsg",
512
+ cron: cronSpec
513
+ };
514
+ return this.sendRPC(this.createRPCMsg(msg));
515
+ }
516
+ async removeCron(cronId) {
517
+ const msg = {
518
+ msgtype: "removecronmsg",
519
+ cronid: cronId
520
+ };
521
+ return this.sendRPC(this.createRPCMsg(msg));
522
+ }
523
+ // ==================== Generator Methods ====================
524
+ async getGenerators(colonyName, count = 100) {
525
+ const msg = {
526
+ msgtype: "getgeneratorsmsg",
527
+ colonyname: colonyName,
528
+ count
529
+ };
530
+ return this.sendRPC(this.createRPCMsg(msg));
531
+ }
532
+ async getGenerator(generatorId) {
533
+ const msg = {
534
+ msgtype: "getgeneratormsg",
535
+ generatorid: generatorId
536
+ };
537
+ return this.sendRPC(this.createRPCMsg(msg));
538
+ }
539
+ async addGenerator(generatorSpec) {
540
+ const msg = {
541
+ msgtype: "addgeneratormsg",
542
+ generator: generatorSpec
543
+ };
544
+ return this.sendRPC(this.createRPCMsg(msg));
545
+ }
546
+ // ==================== User Methods ====================
547
+ async getUsers(colonyName) {
548
+ const msg = {
549
+ msgtype: "getusersmsg",
550
+ colonyname: colonyName
551
+ };
552
+ return this.sendRPC(this.createRPCMsg(msg));
553
+ }
554
+ async addUser(user) {
555
+ const msg = {
556
+ msgtype: "addusermsg",
557
+ user
558
+ };
559
+ return this.sendRPC(this.createRPCMsg(msg));
560
+ }
561
+ async removeUser(colonyName, name) {
562
+ const msg = {
563
+ msgtype: "removeusermsg",
564
+ colonyname: colonyName,
565
+ name
566
+ };
567
+ return this.sendRPC(this.createRPCMsg(msg));
568
+ }
569
+ // ==================== File Methods ====================
570
+ async getFileLabels(colonyName, name = "", exact = false) {
571
+ const msg = {
572
+ msgtype: "getfilelabelsmsg",
573
+ colonyname: colonyName,
574
+ name,
575
+ exact
576
+ };
577
+ return this.sendRPC(this.createRPCMsg(msg));
578
+ }
579
+ async getFiles(colonyName, label) {
580
+ const msg = {
581
+ msgtype: "getfilesmsg",
582
+ colonyname: colonyName,
583
+ label
584
+ };
585
+ return this.sendRPC(this.createRPCMsg(msg));
586
+ }
587
+ // ==================== Attribute Methods ====================
588
+ async addAttribute(attribute) {
589
+ const msg = {
590
+ msgtype: "addattributemsg",
591
+ attribute
592
+ };
593
+ return this.sendRPC(this.createRPCMsg(msg));
594
+ }
595
+ async getAttribute(attributeId) {
596
+ const msg = {
597
+ msgtype: "getattributemsg",
598
+ attributeid: attributeId
599
+ };
600
+ return this.sendRPC(this.createRPCMsg(msg));
601
+ }
602
+ // ==================== Channel Methods ====================
603
+ /**
604
+ * Append a message to a process channel
605
+ * @param processId - ID of the process
606
+ * @param channelName - Name of the channel
607
+ * @param sequence - Client-assigned sequence number
608
+ * @param inReplyTo - Sequence number this message is replying to (0 if not a reply)
609
+ * @param payload - Message content (string or Uint8Array)
610
+ */
611
+ async channelAppend(processId, channelName, sequence, inReplyTo, payload) {
612
+ let payloadBytes;
613
+ if (typeof payload === "string") {
614
+ const encoder = new TextEncoder();
615
+ payloadBytes = Array.from(encoder.encode(payload));
616
+ } else {
617
+ payloadBytes = Array.from(payload);
618
+ }
619
+ const msg = {
620
+ msgtype: "channelappendmsg",
621
+ processid: processId,
622
+ name: channelName,
623
+ sequence,
624
+ inreplyto: inReplyTo,
625
+ payload: payloadBytes
626
+ };
627
+ return this.sendRPC(this.createRPCMsg(msg));
628
+ }
629
+ /**
630
+ * Read messages from a process channel
631
+ * @param processId - ID of the process
632
+ * @param channelName - Name of the channel
633
+ * @param afterSeq - Read messages after this sequence number (use 0 for all)
634
+ * @param limit - Maximum number of messages to return (0 for no limit)
635
+ */
636
+ async channelRead(processId, channelName, afterSeq, limit) {
637
+ const msg = {
638
+ msgtype: "channelreadmsg",
639
+ processid: processId,
640
+ name: channelName,
641
+ afterseq: afterSeq,
642
+ limit
643
+ };
644
+ const response = await this.sendRPC(this.createRPCMsg(msg));
645
+ if (Array.isArray(response)) {
646
+ return response.map((entry) => ({
647
+ ...entry,
648
+ payload: typeof entry.payload === "string" ? (() => {
649
+ try {
650
+ const binaryStr = atob(entry.payload);
651
+ const bytes = new Uint8Array(binaryStr.length);
652
+ for (let i = 0; i < binaryStr.length; i++) {
653
+ bytes[i] = binaryStr.charCodeAt(i);
654
+ }
655
+ return new TextDecoder("utf-8").decode(bytes);
656
+ } catch {
657
+ return entry.payload;
658
+ }
659
+ })() : Array.isArray(entry.payload) ? new TextDecoder("utf-8").decode(new Uint8Array(entry.payload)) : entry.payload
660
+ }));
661
+ }
662
+ return response || [];
663
+ }
664
+ /**
665
+ * Subscribe to a channel using WebSocket for real-time updates
666
+ * @param processId - ID of the process
667
+ * @param channelName - Name of the channel
668
+ * @param afterSeq - Start reading after this sequence number
669
+ * @param timeout - Timeout in seconds for the subscription
670
+ * @param onMessage - Callback for new messages
671
+ * @param onError - Callback for errors
672
+ * @param onClose - Callback when connection closes
673
+ * @returns WebSocket instance for cleanup
674
+ */
675
+ subscribeChannel(processId, channelName, afterSeq, timeout, onMessage, onError, onClose) {
676
+ if (!this.privateKey) {
677
+ throw new Error("Private key not set. Call setPrivateKey() first.");
678
+ }
679
+ const wsProtocol = this.tls ? "wss" : "ws";
680
+ const wsUrl = `${wsProtocol}://${this.host}:${this.port}/pubsub`;
681
+ const ws = new WebSocket(wsUrl);
682
+ ws.onopen = () => {
683
+ const msg = {
684
+ msgtype: "subscribechannelmsg",
685
+ processid: processId,
686
+ name: channelName,
687
+ afterseq: afterSeq,
688
+ timeout
689
+ };
690
+ const rpcMsg = this.createRPCMsg(msg);
691
+ ws.send(JSON.stringify(rpcMsg));
692
+ };
693
+ ws.onmessage = (event) => {
694
+ try {
695
+ const rpcReply = JSON.parse(event.data);
696
+ if (rpcReply.error) {
697
+ const errorPayload = JSON.parse(decodeBase64Utf8(rpcReply.payload));
698
+ onError(new Error(errorPayload.message || "WebSocket error"));
699
+ return;
700
+ }
701
+ const data = JSON.parse(decodeBase64Utf8(rpcReply.payload));
702
+ if (Array.isArray(data)) {
703
+ const entries = data.map((entry) => ({
704
+ ...entry,
705
+ payload: typeof entry.payload === "string" ? (() => {
706
+ try {
707
+ const binaryStr = atob(entry.payload);
708
+ const bytes = new Uint8Array(binaryStr.length);
709
+ for (let i = 0; i < binaryStr.length; i++) {
710
+ bytes[i] = binaryStr.charCodeAt(i);
711
+ }
712
+ return new TextDecoder("utf-8").decode(bytes);
713
+ } catch {
714
+ return entry.payload;
715
+ }
716
+ })() : Array.isArray(entry.payload) ? new TextDecoder("utf-8").decode(new Uint8Array(entry.payload)) : entry.payload
717
+ }));
718
+ const errorEntry = entries.find((e) => e.error);
719
+ if (errorEntry) {
720
+ onError(new Error(errorEntry.error));
721
+ return;
722
+ }
723
+ onMessage(entries);
724
+ }
725
+ } catch (err) {
726
+ onError(err instanceof Error ? err : new Error(String(err)));
727
+ }
728
+ };
729
+ ws.onerror = () => {
730
+ onError(new Error("WebSocket connection error"));
731
+ };
732
+ ws.onclose = () => {
733
+ onClose();
734
+ };
735
+ return ws;
736
+ }
737
+ // ==================== Blueprint Definition Methods ====================
738
+ /**
739
+ * Add a blueprint definition
740
+ * @param definition - Blueprint definition object
741
+ */
742
+ async addBlueprintDefinition(definition) {
743
+ const msg = {
744
+ msgtype: "addblueprintdefinitionmsg",
745
+ blueprintdefinition: definition
746
+ };
747
+ return this.sendRPC(this.createRPCMsg(msg));
748
+ }
749
+ /**
750
+ * Get a blueprint definition by name
751
+ * @param colonyName - Name of the colony
752
+ * @param name - Name of the blueprint definition
753
+ */
754
+ async getBlueprintDefinition(colonyName, name) {
755
+ const msg = {
756
+ msgtype: "getblueprintdefinitionmsg",
757
+ colonyname: colonyName,
758
+ name
759
+ };
760
+ return this.sendRPC(this.createRPCMsg(msg));
761
+ }
762
+ /**
763
+ * Get all blueprint definitions in a colony
764
+ * @param colonyName - Name of the colony
765
+ */
766
+ async getBlueprintDefinitions(colonyName) {
767
+ const msg = {
768
+ msgtype: "getblueprintdefinitionsmsg",
769
+ colonyname: colonyName
770
+ };
771
+ return this.sendRPC(this.createRPCMsg(msg));
772
+ }
773
+ /**
774
+ * Remove a blueprint definition
775
+ * @param colonyName - Name of the colony (namespace)
776
+ * @param name - Name of the blueprint definition to remove
777
+ */
778
+ async removeBlueprintDefinition(colonyName, name) {
779
+ const msg = {
780
+ msgtype: "removeblueprintdefinitionmsg",
781
+ namespace: colonyName,
782
+ name
783
+ };
784
+ await this.sendRPC(this.createRPCMsg(msg));
785
+ }
786
+ // ==================== Blueprint Methods ====================
787
+ /**
788
+ * Add a blueprint instance
789
+ * @param blueprint - Blueprint object
790
+ */
791
+ async addBlueprint(blueprint) {
792
+ const msg = {
793
+ msgtype: "addblueprintmsg",
794
+ blueprint
795
+ };
796
+ return this.sendRPC(this.createRPCMsg(msg));
797
+ }
798
+ /**
799
+ * Get a blueprint by name
800
+ * @param colonyName - Name of the colony (namespace)
801
+ * @param name - Name of the blueprint
802
+ */
803
+ async getBlueprint(colonyName, name) {
804
+ const msg = {
805
+ msgtype: "getblueprintmsg",
806
+ namespace: colonyName,
807
+ name
808
+ };
809
+ return this.sendRPC(this.createRPCMsg(msg));
810
+ }
811
+ /**
812
+ * Get blueprints in a colony, optionally filtered by kind and location
813
+ * @param colonyName - Name of the colony (namespace)
814
+ * @param kind - Optional kind filter
815
+ * @param location - Optional location filter
816
+ */
817
+ async getBlueprints(colonyName, kind, location) {
818
+ const msg = {
819
+ msgtype: "getblueprintsmsg",
820
+ namespace: colonyName
821
+ };
822
+ if (kind) msg.kind = kind;
823
+ if (location) msg.locationname = location;
824
+ return this.sendRPC(this.createRPCMsg(msg));
825
+ }
826
+ /**
827
+ * Update an existing blueprint
828
+ * @param blueprint - Updated blueprint object
829
+ * @param forceGeneration - Force generation bump even if spec unchanged
830
+ */
831
+ async updateBlueprint(blueprint, forceGeneration = false) {
832
+ const msg = {
833
+ msgtype: "updateblueprintmsg",
834
+ blueprint,
835
+ forcegeneration: forceGeneration
836
+ };
837
+ return this.sendRPC(this.createRPCMsg(msg));
838
+ }
839
+ /**
840
+ * Remove a blueprint
841
+ * @param colonyName - Name of the colony (namespace)
842
+ * @param name - Name of the blueprint to remove
843
+ */
844
+ async removeBlueprint(colonyName, name) {
845
+ const msg = {
846
+ msgtype: "removeblueprintmsg",
847
+ namespace: colonyName,
848
+ name
849
+ };
850
+ await this.sendRPC(this.createRPCMsg(msg));
851
+ }
852
+ /**
853
+ * Update blueprint status (current state)
854
+ * @param colonyName - Name of the colony
855
+ * @param name - Name of the blueprint
856
+ * @param status - Status object representing current state
857
+ */
858
+ async updateBlueprintStatus(colonyName, name, status) {
859
+ const msg = {
860
+ msgtype: "updateblueprintstatusmsg",
861
+ colonyname: colonyName,
862
+ blueprintname: name,
863
+ status
864
+ };
865
+ await this.sendRPC(this.createRPCMsg(msg));
866
+ }
867
+ /**
868
+ * Trigger reconciliation for a blueprint
869
+ * @param colonyName - Name of the colony (namespace)
870
+ * @param name - Name of the blueprint
871
+ * @param force - Force reconciliation even if no changes detected
872
+ */
873
+ async reconcileBlueprint(colonyName, name, force = false) {
874
+ const msg = {
875
+ msgtype: "reconcileblueprintmsg",
876
+ namespace: colonyName,
877
+ name,
878
+ force
879
+ };
880
+ return this.sendRPC(this.createRPCMsg(msg));
881
+ }
882
+ /**
883
+ * Subscribe to process state changes using WebSocket
884
+ * Use this to wait for a process to be assigned (RUNNING state) before subscribing to channels
885
+ * @param colonyName - Name of the colony
886
+ * @param processId - ID of the process to watch
887
+ * @param state - Target state to wait for (0=WAITING, 1=RUNNING, 2=SUCCESS, 3=FAILED)
888
+ * @param timeout - Timeout in seconds for the subscription
889
+ * @param onProcess - Callback when process reaches the target state
890
+ * @param onError - Callback for errors
891
+ * @param onClose - Callback when connection closes
892
+ * @returns WebSocket instance for cleanup
893
+ */
894
+ subscribeProcess(colonyName, processId, state, timeout, onProcess, onError, onClose) {
895
+ if (!this.privateKey) {
896
+ throw new Error("Private key not set. Call setPrivateKey() first.");
897
+ }
898
+ const wsProtocol = this.tls ? "wss" : "ws";
899
+ const wsUrl = `${wsProtocol}://${this.host}:${this.port}/pubsub`;
900
+ const ws = new WebSocket(wsUrl);
901
+ ws.onopen = () => {
902
+ const msg = {
903
+ msgtype: "subscribeprocessmsg",
904
+ colonyname: colonyName,
905
+ processid: processId,
906
+ executortype: "",
907
+ state,
908
+ timeout
909
+ };
910
+ const rpcMsg = this.createRPCMsg(msg);
911
+ ws.send(JSON.stringify(rpcMsg));
912
+ };
913
+ ws.onmessage = (event) => {
914
+ try {
915
+ const rpcReply = JSON.parse(event.data);
916
+ if (rpcReply.error) {
917
+ const errorPayload = JSON.parse(decodeBase64Utf8(rpcReply.payload));
918
+ onError(new Error(errorPayload.message || "WebSocket error"));
919
+ return;
920
+ }
921
+ const process = JSON.parse(decodeBase64Utf8(rpcReply.payload));
922
+ onProcess(process);
923
+ } catch (err) {
924
+ onError(err instanceof Error ? err : new Error(String(err)));
925
+ }
926
+ };
927
+ ws.onerror = () => {
928
+ onError(new Error("WebSocket connection error"));
929
+ };
930
+ ws.onclose = () => {
931
+ onClose();
932
+ };
933
+ return ws;
934
+ }
935
+ };
936
+ export {
937
+ ColoniesClient,
938
+ Crypto,
939
+ ProcessState,
940
+ deriveId,
941
+ generatePrivateKey,
942
+ sign
943
+ };
944
+ //# sourceMappingURL=index.mjs.map