cocoon-sdk 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.cjs ADDED
@@ -0,0 +1,2634 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ APIError: () => APIError,
34
+ AuthenticationError: () => AuthenticationError,
35
+ Cocoon: () => Cocoon,
36
+ CocoonError: () => CocoonError,
37
+ ConnectionError: () => ConnectionError,
38
+ FileAttestationProvider: () => FileAttestationProvider,
39
+ ProtocolError: () => ProtocolError,
40
+ StaticAttestationProvider: () => StaticAttestationProvider,
41
+ Stream: () => Stream,
42
+ TimeoutError: () => TimeoutError
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/core/protocol/session.ts
47
+ var import_node_crypto3 = __toESM(require("crypto"), 1);
48
+ var import_node_events2 = require("events");
49
+
50
+ // src/core/protocol/connection.ts
51
+ var net = __toESM(require("net"), 1);
52
+ var tls = __toESM(require("tls"), 1);
53
+ var import_node_events = require("events");
54
+
55
+ // src/core/error.ts
56
+ var CocoonError = class extends Error {
57
+ constructor(message) {
58
+ super(message);
59
+ this.name = "CocoonError";
60
+ }
61
+ };
62
+ var ConnectionError = class extends CocoonError {
63
+ constructor(message, cause) {
64
+ super(message);
65
+ this.cause = cause;
66
+ this.name = "ConnectionError";
67
+ }
68
+ };
69
+ var ProtocolError = class extends CocoonError {
70
+ constructor(message, code) {
71
+ super(message);
72
+ this.code = code;
73
+ this.name = "ProtocolError";
74
+ }
75
+ };
76
+ var APIError = class extends CocoonError {
77
+ constructor(message, statusCode, errorBody) {
78
+ super(message);
79
+ this.statusCode = statusCode;
80
+ this.errorBody = errorBody;
81
+ this.name = "APIError";
82
+ }
83
+ };
84
+ var AuthenticationError = class extends CocoonError {
85
+ constructor(message, code) {
86
+ super(message);
87
+ this.code = code;
88
+ this.name = "AuthenticationError";
89
+ }
90
+ };
91
+ var TimeoutError = class extends CocoonError {
92
+ constructor(message = "Request timed out") {
93
+ super(message);
94
+ this.name = "TimeoutError";
95
+ }
96
+ };
97
+
98
+ // src/core/protocol/pow.ts
99
+ var import_node_crypto = __toESM(require("crypto"), 1);
100
+ var POW_CHALLENGE_MAGIC = 1099829905;
101
+ var POW_RESPONSE_MAGIC = 25326361;
102
+ var POW_CHALLENGE_SIZE = 24;
103
+ var POW_RESPONSE_SIZE = 12;
104
+ var MAX_DIFFICULTY = 32;
105
+ function parsePowChallenge(data) {
106
+ if (data.length < POW_CHALLENGE_SIZE) {
107
+ throw new Error(
108
+ `PoW challenge too short: ${data.length} bytes, expected ${POW_CHALLENGE_SIZE}`
109
+ );
110
+ }
111
+ const magic = data.readUInt32LE(0);
112
+ if (magic !== POW_CHALLENGE_MAGIC) {
113
+ throw new Error(
114
+ `Invalid PoW magic: 0x${magic.toString(16)}, expected 0x${POW_CHALLENGE_MAGIC.toString(16)}`
115
+ );
116
+ }
117
+ const difficultyBits = data.readInt32LE(4);
118
+ if (difficultyBits < 0 || difficultyBits > MAX_DIFFICULTY) {
119
+ throw new Error(`PoW difficulty out of range: ${difficultyBits}`);
120
+ }
121
+ const salt = Buffer.alloc(16);
122
+ data.copy(salt, 0, 8, 24);
123
+ return { difficultyBits, salt };
124
+ }
125
+ function checkPow(hash, difficultyBits) {
126
+ let remaining = difficultyBits;
127
+ for (let i = 7; i >= 0 && remaining > 0; i--) {
128
+ const byte = hash[i];
129
+ if (remaining >= 8) {
130
+ if (byte !== 0) return false;
131
+ remaining -= 8;
132
+ } else {
133
+ const mask = 255 << 8 - remaining;
134
+ if ((byte & mask) !== 0) return false;
135
+ remaining = 0;
136
+ }
137
+ }
138
+ return true;
139
+ }
140
+ function solvePow(challenge) {
141
+ const { difficultyBits, salt } = challenge;
142
+ if (difficultyBits === 0) return 0n;
143
+ const input = Buffer.alloc(24);
144
+ salt.copy(input, 0);
145
+ for (let nonce = 0n; ; nonce++) {
146
+ input.writeBigUInt64LE(nonce, 16);
147
+ const hash = import_node_crypto.default.createHash("sha256").update(input).digest();
148
+ if (checkPow(hash, difficultyBits)) {
149
+ return nonce;
150
+ }
151
+ }
152
+ }
153
+ function buildPowResponse(nonce) {
154
+ const buf = Buffer.alloc(POW_RESPONSE_SIZE);
155
+ buf.writeUInt32LE(POW_RESPONSE_MAGIC, 0);
156
+ buf.writeBigUInt64LE(nonce, 4);
157
+ return buf;
158
+ }
159
+
160
+ // src/core/protocol/connection.ts
161
+ var CocoonConnection = class extends import_node_events.EventEmitter {
162
+ socket = null;
163
+ rawSocket = null;
164
+ outSeqno = 0;
165
+ inSeqno = 0;
166
+ readBuffer = Buffer.alloc(0);
167
+ connected = false;
168
+ destroyed = false;
169
+ host;
170
+ port;
171
+ useTls;
172
+ timeout;
173
+ tlsCert;
174
+ tlsKey;
175
+ constructor(options) {
176
+ super();
177
+ this.host = options.host;
178
+ this.port = options.port;
179
+ this.useTls = options.useTls ?? true;
180
+ this.timeout = options.timeout ?? 12e4;
181
+ this.tlsCert = options.tlsCert;
182
+ this.tlsKey = options.tlsKey;
183
+ }
184
+ async connect() {
185
+ if (this.connected) return;
186
+ if (this.useTls) {
187
+ await this.connectWithPowAndTls();
188
+ } else {
189
+ await this.connectPlainTcp();
190
+ }
191
+ }
192
+ /**
193
+ * Connect with PoW challenge + TLS upgrade (default for Cocoon proxies).
194
+ */
195
+ async connectWithPowAndTls() {
196
+ const rawSocket = await this.rawTcpConnect();
197
+ this.rawSocket = rawSocket;
198
+ try {
199
+ await this.handlePow(rawSocket);
200
+ this.socket = await this.upgradeTls(rawSocket);
201
+ } catch (err) {
202
+ rawSocket.destroy();
203
+ this.rawSocket = null;
204
+ throw err;
205
+ }
206
+ this.connected = true;
207
+ this.setupSocketHandlers(this.socket);
208
+ this.emit("ready");
209
+ }
210
+ /**
211
+ * Connect with plain TCP (no PoW, no TLS).
212
+ */
213
+ async connectPlainTcp() {
214
+ return new Promise((resolve, reject) => {
215
+ const onConnect = () => {
216
+ this.connected = true;
217
+ this.setupSocketHandlers(this.socket);
218
+ this.emit("ready");
219
+ resolve();
220
+ };
221
+ const onError = (err) => {
222
+ reject(
223
+ new ConnectionError(
224
+ `Failed to connect to ${this.host}:${this.port}: ${err.message}`,
225
+ err
226
+ )
227
+ );
228
+ };
229
+ this.socket = net.connect({ host: this.host, port: this.port }, onConnect);
230
+ this.socket.setTimeout(this.timeout);
231
+ this.socket.once("error", onError);
232
+ });
233
+ }
234
+ rawTcpConnect() {
235
+ return new Promise((resolve, reject) => {
236
+ const sock = net.connect({ host: this.host, port: this.port }, () => {
237
+ sock.removeListener("error", onError);
238
+ resolve(sock);
239
+ });
240
+ sock.setTimeout(this.timeout);
241
+ const onError = (err) => {
242
+ reject(
243
+ new ConnectionError(
244
+ `Failed to connect to ${this.host}:${this.port}: ${err.message}`,
245
+ err
246
+ )
247
+ );
248
+ };
249
+ sock.once("error", onError);
250
+ });
251
+ }
252
+ handlePow(sock) {
253
+ return new Promise((resolve, reject) => {
254
+ let powBuffer = Buffer.alloc(0);
255
+ const timer = setTimeout(() => {
256
+ sock.removeListener("data", onData);
257
+ reject(new ConnectionError("Timeout waiting for PoW challenge"));
258
+ }, 3e4);
259
+ const onData = (chunk) => {
260
+ powBuffer = Buffer.concat([powBuffer, chunk]);
261
+ if (powBuffer.length >= 4) {
262
+ const magic = powBuffer.readUInt32LE(0);
263
+ if (magic !== POW_CHALLENGE_MAGIC) {
264
+ clearTimeout(timer);
265
+ sock.removeListener("data", onData);
266
+ reject(
267
+ new ConnectionError(
268
+ `Unexpected data from proxy (expected PoW challenge): 0x${magic.toString(16)}`
269
+ )
270
+ );
271
+ return;
272
+ }
273
+ }
274
+ if (powBuffer.length >= POW_CHALLENGE_SIZE) {
275
+ clearTimeout(timer);
276
+ sock.removeListener("data", onData);
277
+ try {
278
+ const challenge = parsePowChallenge(powBuffer);
279
+ const nonce = solvePow(challenge);
280
+ const response = buildPowResponse(nonce);
281
+ sock.write(response, (err) => {
282
+ if (err) {
283
+ reject(new ConnectionError(`Failed to send PoW response: ${err.message}`, err));
284
+ } else {
285
+ resolve();
286
+ }
287
+ });
288
+ } catch (err) {
289
+ reject(
290
+ new ConnectionError(
291
+ `PoW failed: ${err instanceof Error ? err.message : err}`,
292
+ err instanceof Error ? err : void 0
293
+ )
294
+ );
295
+ }
296
+ }
297
+ };
298
+ sock.on("data", onData);
299
+ });
300
+ }
301
+ upgradeTls(rawSocket) {
302
+ return new Promise((resolve, reject) => {
303
+ const tlsOptions = {
304
+ socket: rawSocket,
305
+ rejectUnauthorized: false
306
+ // Cocoon uses custom TDX attestation, not standard CA
307
+ };
308
+ if (this.tlsCert) tlsOptions.cert = this.tlsCert;
309
+ if (this.tlsKey) tlsOptions.key = this.tlsKey;
310
+ const tlsSocket = tls.connect(tlsOptions);
311
+ const timer = setTimeout(() => {
312
+ cleanup(() => reject(new ConnectionError("TLS handshake timeout")));
313
+ }, this.timeout);
314
+ const cleanup = (next) => {
315
+ clearTimeout(timer);
316
+ tlsSocket.removeListener("secureConnect", onSecureConnect);
317
+ tlsSocket.removeListener("error", onError);
318
+ tlsSocket.removeListener("close", onClose);
319
+ if (next) next();
320
+ };
321
+ const onSecureConnect = () => {
322
+ cleanup(() => resolve(tlsSocket));
323
+ };
324
+ const onError = (err) => {
325
+ cleanup(() => reject(new ConnectionError(`TLS handshake failed: ${err.message}`, err)));
326
+ };
327
+ const onClose = () => {
328
+ cleanup(() => reject(new ConnectionError("TLS socket closed before handshake completed")));
329
+ };
330
+ tlsSocket.once("secureConnect", onSecureConnect);
331
+ tlsSocket.once("error", onError);
332
+ tlsSocket.once("close", onClose);
333
+ });
334
+ }
335
+ setupSocketHandlers(sock) {
336
+ sock.setTimeout(this.timeout);
337
+ sock.on("data", (data) => this.onData(data));
338
+ sock.on("close", () => this.onClose());
339
+ sock.on("timeout", () => {
340
+ this.destroy(new ConnectionError("Connection timed out"));
341
+ });
342
+ sock.on("error", (err) => {
343
+ if (this.connected) {
344
+ this.destroy(new ConnectionError(`Socket error: ${err.message}`, err));
345
+ }
346
+ });
347
+ }
348
+ /**
349
+ * Send a framed packet: [4b size][4b seqno][payload]
350
+ */
351
+ send(payload) {
352
+ if (!this.socket || this.destroyed) {
353
+ throw new ConnectionError("Not connected");
354
+ }
355
+ const frame = Buffer.alloc(8 + payload.length);
356
+ frame.writeUInt32LE(payload.length, 0);
357
+ frame.writeInt32LE(this.outSeqno, 4);
358
+ payload.copy(frame, 8);
359
+ this.outSeqno++;
360
+ this.socket.write(frame);
361
+ }
362
+ destroy(error) {
363
+ if (this.destroyed) return;
364
+ this.destroyed = true;
365
+ this.connected = false;
366
+ if (this.socket) {
367
+ this.socket.destroy();
368
+ this.socket = null;
369
+ }
370
+ if (this.rawSocket) {
371
+ this.rawSocket.destroy();
372
+ this.rawSocket = null;
373
+ }
374
+ this.emit("close", error);
375
+ }
376
+ get isConnected() {
377
+ return this.connected && !this.destroyed;
378
+ }
379
+ onData(chunk) {
380
+ this.readBuffer = Buffer.concat([this.readBuffer, chunk]);
381
+ this.processFrames();
382
+ }
383
+ processFrames() {
384
+ while (this.readBuffer.length >= 8) {
385
+ const size = this.readBuffer.readUInt32LE(0);
386
+ if (size > 1 << 24 || size < 0) {
387
+ this.destroy(new ConnectionError(`Invalid frame size: ${size}`));
388
+ return;
389
+ }
390
+ const totalFrameSize = 8 + size;
391
+ if (this.readBuffer.length < totalFrameSize) {
392
+ break;
393
+ }
394
+ const seqno = this.readBuffer.readInt32LE(4);
395
+ if (seqno !== this.inSeqno) {
396
+ this.destroy(
397
+ new ConnectionError(`Sequence number mismatch: expected ${this.inSeqno}, got ${seqno}`)
398
+ );
399
+ return;
400
+ }
401
+ const payload = Buffer.alloc(size);
402
+ this.readBuffer.copy(payload, 0, 8, totalFrameSize);
403
+ this.readBuffer = this.readBuffer.subarray(totalFrameSize);
404
+ this.inSeqno++;
405
+ this.emit("frame", payload);
406
+ }
407
+ }
408
+ onClose() {
409
+ if (!this.destroyed) {
410
+ this.destroy();
411
+ }
412
+ }
413
+ };
414
+
415
+ // src/core/tl/schema.ts
416
+ var crc32Table = new Uint32Array(256);
417
+ for (let i = 0; i < 256; i++) {
418
+ let c = i;
419
+ for (let j = 0; j < 8; j++) {
420
+ if (c & 1) {
421
+ c = 3988292384 ^ c >>> 1;
422
+ } else {
423
+ c = c >>> 1;
424
+ }
425
+ }
426
+ crc32Table[i] = c;
427
+ }
428
+ function crc32(str) {
429
+ const bytes = Buffer.from(str, "utf-8");
430
+ let crc = 4294967295;
431
+ for (let i = 0; i < bytes.length; i++) {
432
+ crc = crc32Table[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
433
+ }
434
+ return (crc ^ 4294967295) >>> 0;
435
+ }
436
+ function tlId(definition) {
437
+ return crc32(definition.trim());
438
+ }
439
+ var TL_SCHEMA = {
440
+ // --- TCP Layer ---
441
+ "tcp.ping": {
442
+ id: tlId("tcp.ping id:long = tcp.Packet"),
443
+ fields: [{ name: "id", type: "long" }]
444
+ },
445
+ "tcp.pong": {
446
+ id: tlId("tcp.pong id:long = tcp.Packet"),
447
+ fields: [{ name: "id", type: "long" }]
448
+ },
449
+ "tcp.packet": {
450
+ id: tlId("tcp.packet data:bytes = tcp.Packet"),
451
+ fields: [{ name: "data", type: "bytes" }]
452
+ },
453
+ "tcp.queryAnswer": {
454
+ id: tlId("tcp.queryAnswer id:long data:bytes = tcp.Packet"),
455
+ fields: [
456
+ { name: "id", type: "long" },
457
+ { name: "data", type: "bytes" }
458
+ ]
459
+ },
460
+ "tcp.queryError": {
461
+ id: tlId("tcp.queryError id:long code:int message:string = tcp.Packet"),
462
+ fields: [
463
+ { name: "id", type: "long" },
464
+ { name: "code", type: "int" },
465
+ { name: "message", type: "string" }
466
+ ]
467
+ },
468
+ "tcp.query": {
469
+ id: tlId("tcp.query id:long data:bytes = tcp.Packet"),
470
+ fields: [
471
+ { name: "id", type: "long" },
472
+ { name: "data", type: "bytes" }
473
+ ]
474
+ },
475
+ "tcp.connected": {
476
+ id: tlId("tcp.connected id:long = tcp.Packet"),
477
+ fields: [{ name: "id", type: "long" }]
478
+ },
479
+ "tcp.connect": {
480
+ id: tlId("tcp.connect id:long = tcp.Packet"),
481
+ fields: [{ name: "id", type: "long" }]
482
+ },
483
+ // --- Tokens ---
484
+ tokensUsed: {
485
+ id: tlId(
486
+ "tokensUsed prompt_tokens_used:long cached_tokens_used:long completion_tokens_used:long reasoning_tokens_used:long total_tokens_used:long = TokensUsed"
487
+ ),
488
+ fields: [
489
+ { name: "promptTokensUsed", type: "long" },
490
+ { name: "cachedTokensUsed", type: "long" },
491
+ { name: "completionTokensUsed", type: "long" },
492
+ { name: "reasoningTokensUsed", type: "long" },
493
+ { name: "totalTokensUsed", type: "long" }
494
+ ]
495
+ },
496
+ // --- Client/Proxy Params ---
497
+ // worker.params has explicit ID: #869c73ed
498
+ "worker.params": {
499
+ id: 2258400237,
500
+ fields: [
501
+ { name: "flags", type: "int" },
502
+ { name: "workerOwnerAddress", type: "string" },
503
+ { name: "model", type: "string" },
504
+ { name: "coefficient", type: "int" },
505
+ { name: "isTest", type: "Bool", flag: { field: "flags", bit: 0 } },
506
+ { name: "proxyCnt", type: "int", flag: { field: "flags", bit: 0 } },
507
+ { name: "maxActiveRequests", type: "int", flag: { field: "flags", bit: 0 } },
508
+ { name: "minProtoVersion", type: "int", flag: { field: "flags", bit: 1 } },
509
+ { name: "maxProtoVersion", type: "int", flag: { field: "flags", bit: 1 } }
510
+ ]
511
+ },
512
+ // proxy.params has explicit ID: #d5c5609f
513
+ "proxy.params": {
514
+ id: 3586482335,
515
+ fields: [
516
+ { name: "flags", type: "int" },
517
+ { name: "proxyPublicKey", type: "int256" },
518
+ { name: "proxyOwnerAddress", type: "string" },
519
+ { name: "proxyScAddress", type: "string" },
520
+ { name: "isTest", type: "Bool", flag: { field: "flags", bit: 0 } },
521
+ { name: "protoVersion", type: "int", flag: { field: "flags", bit: 1 } }
522
+ ]
523
+ },
524
+ // client.params has explicit ID: #40fdca64
525
+ "client.params": {
526
+ id: 1090374244,
527
+ fields: [
528
+ { name: "flags", type: "int" },
529
+ { name: "clientOwnerAddress", type: "string" },
530
+ { name: "isTest", type: "Bool", flag: { field: "flags", bit: 0 } },
531
+ { name: "minProtoVersion", type: "int", flag: { field: "flags", bit: 1 } },
532
+ { name: "maxProtoVersion", type: "int", flag: { field: "flags", bit: 1 } }
533
+ ]
534
+ },
535
+ // --- Auth ---
536
+ "client.proxyConnectionAuthShort": {
537
+ id: tlId(
538
+ "client.proxyConnectionAuthShort secret_hash:int256 nonce:long = client.ProxyConnectionAuth"
539
+ ),
540
+ fields: [
541
+ { name: "secretHash", type: "int256" },
542
+ { name: "nonce", type: "long" }
543
+ ]
544
+ },
545
+ "client.proxyConnectionAuthLong": {
546
+ id: tlId("client.proxyConnectionAuthLong nonce:long = client.ProxyConnectionAuth"),
547
+ fields: [{ name: "nonce", type: "long" }]
548
+ },
549
+ // --- Signed Payment ---
550
+ "proxy.signedPayment": {
551
+ id: tlId("proxy.signedPayment data:bytes = proxy.SignedPayment"),
552
+ fields: [{ name: "data", type: "bytes" }]
553
+ },
554
+ "proxy.signedPaymentEmpty": {
555
+ id: tlId("proxy.signedPaymentEmpty = proxy.SignedPayment"),
556
+ fields: []
557
+ },
558
+ // --- Connected To Proxy ---
559
+ "client.connectedToProxy": {
560
+ id: tlId(
561
+ "client.connectedToProxy params:proxy.params client_sc_address:string auth:client.ProxyConnectionAuth signed_payment:proxy.SignedPayment = client.ConnectedToProxy"
562
+ ),
563
+ fields: [
564
+ { name: "params", type: { ref: "proxy.params" } },
565
+ { name: "clientScAddress", type: "string" },
566
+ { name: "auth", type: { ref: "client.ProxyConnectionAuth" } },
567
+ { name: "signedPayment", type: { ref: "proxy.SignedPayment" } }
568
+ ]
569
+ },
570
+ // --- Auth Responses ---
571
+ "client.authorizationWithProxySuccess": {
572
+ id: tlId(
573
+ "client.authorizationWithProxySuccess signed_payment:proxy.SignedPayment tokens_committed_to_db:long max_tokens:long = client.AuthorizationWithProxy"
574
+ ),
575
+ fields: [
576
+ { name: "signedPayment", type: { ref: "proxy.SignedPayment" } },
577
+ { name: "tokensCommittedToDb", type: "long" },
578
+ { name: "maxTokens", type: "long" }
579
+ ]
580
+ },
581
+ "client.authorizationWithProxyFailed": {
582
+ id: tlId(
583
+ "client.authorizationWithProxyFailed error_code:int error:string = client.AuthorizationWithProxy"
584
+ ),
585
+ fields: [
586
+ { name: "errorCode", type: "int" },
587
+ { name: "error", type: "string" }
588
+ ]
589
+ },
590
+ // --- Query Final Info ---
591
+ "client.queryFinalInfo": {
592
+ id: tlId(
593
+ "client.queryFinalInfo flags:# tokens_used:tokensUsed worker_debug:flags.0?string proxy_debug:flags.0?string proxy_start_time:flags.1?double proxy_end_time:flags.1?double worker_start_time:flags.1?double worker_end_time:flags.1?double = client.QueryFinalInfo"
594
+ ),
595
+ fields: [
596
+ { name: "flags", type: "int" },
597
+ { name: "tokensUsed", type: { ref: "tokensUsed" } },
598
+ { name: "workerDebug", type: "string", flag: { field: "flags", bit: 0 } },
599
+ { name: "proxyDebug", type: "string", flag: { field: "flags", bit: 0 } },
600
+ { name: "proxyStartTime", type: "double", flag: { field: "flags", bit: 1 } },
601
+ { name: "proxyEndTime", type: "double", flag: { field: "flags", bit: 1 } },
602
+ { name: "workerStartTime", type: "double", flag: { field: "flags", bit: 1 } },
603
+ { name: "workerEndTime", type: "double", flag: { field: "flags", bit: 1 } }
604
+ ]
605
+ },
606
+ // --- Query Answer Ex variants ---
607
+ // Legacy variants (still sent by some proxy paths):
608
+ "client.queryAnswer": {
609
+ id: tlId(
610
+ "client.queryAnswer answer:bytes is_completed:Bool request_id:int256 request_tokens_used:tokensUsed = client.QueryAnswer"
611
+ ),
612
+ fields: [
613
+ { name: "answer", type: "bytes" },
614
+ { name: "isCompleted", type: "Bool" },
615
+ { name: "requestId", type: "int256" },
616
+ { name: "requestTokensUsed", type: { ref: "tokensUsed" } }
617
+ ]
618
+ },
619
+ "client.queryAnswerError": {
620
+ id: tlId(
621
+ "client.queryAnswerError error_code:int error:string request_id:int256 request_tokens_used:tokensUsed = client.QueryAnswer"
622
+ ),
623
+ fields: [
624
+ { name: "errorCode", type: "int" },
625
+ { name: "error", type: "string" },
626
+ { name: "requestId", type: "int256" },
627
+ { name: "requestTokensUsed", type: { ref: "tokensUsed" } }
628
+ ]
629
+ },
630
+ "client.queryAnswerPart": {
631
+ id: tlId(
632
+ "client.queryAnswerPart answer:bytes is_completed:Bool request_id:int256 request_tokens_used:tokensUsed = client.QueryAnswerPart"
633
+ ),
634
+ fields: [
635
+ { name: "answer", type: "bytes" },
636
+ { name: "isCompleted", type: "Bool" },
637
+ { name: "requestId", type: "int256" },
638
+ { name: "requestTokensUsed", type: { ref: "tokensUsed" } }
639
+ ]
640
+ },
641
+ "client.queryAnswerPartError": {
642
+ id: tlId(
643
+ "client.queryAnswerPartError error_code:int error:string request_id:int256 request_tokens_used:tokensUsed = client.QueryAnswerPart"
644
+ ),
645
+ fields: [
646
+ { name: "errorCode", type: "int" },
647
+ { name: "error", type: "string" },
648
+ { name: "requestId", type: "int256" },
649
+ { name: "requestTokensUsed", type: { ref: "tokensUsed" } }
650
+ ]
651
+ },
652
+ "client.queryAnswerEx": {
653
+ id: tlId(
654
+ "client.queryAnswerEx request_id:int256 answer:bytes flags:# final_info:flags.0?client.queryFinalInfo = client.QueryAnswerEx"
655
+ ),
656
+ fields: [
657
+ { name: "requestId", type: "int256" },
658
+ { name: "answer", type: "bytes" },
659
+ { name: "flags", type: "int" },
660
+ {
661
+ name: "finalInfo",
662
+ type: { ref: "client.queryFinalInfo" },
663
+ flag: { field: "flags", bit: 0 }
664
+ }
665
+ ]
666
+ },
667
+ "client.queryAnswerErrorEx": {
668
+ id: tlId(
669
+ "client.queryAnswerErrorEx request_id:int256 error_code:int error:string flags:# final_info:flags.0?client.queryFinalInfo = client.QueryAnswerEx"
670
+ ),
671
+ fields: [
672
+ { name: "requestId", type: "int256" },
673
+ { name: "errorCode", type: "int" },
674
+ { name: "error", type: "string" },
675
+ { name: "flags", type: "int" },
676
+ {
677
+ name: "finalInfo",
678
+ type: { ref: "client.queryFinalInfo" },
679
+ flag: { field: "flags", bit: 0 }
680
+ }
681
+ ]
682
+ },
683
+ "client.queryAnswerPartEx": {
684
+ id: tlId(
685
+ "client.queryAnswerPartEx request_id:int256 answer:bytes flags:# final_info:flags.0?client.queryFinalInfo = client.QueryAnswerEx"
686
+ ),
687
+ fields: [
688
+ { name: "requestId", type: "int256" },
689
+ { name: "answer", type: "bytes" },
690
+ { name: "flags", type: "int" },
691
+ {
692
+ name: "finalInfo",
693
+ type: { ref: "client.queryFinalInfo" },
694
+ flag: { field: "flags", bit: 0 }
695
+ }
696
+ ]
697
+ },
698
+ // --- Worker Types V2 ---
699
+ // client.workerInstanceV2 has explicit ID: #3ea93d00
700
+ "client.workerInstanceV2": {
701
+ id: 1051278592,
702
+ fields: [
703
+ { name: "flags", type: "int" },
704
+ { name: "coefficient", type: "int" },
705
+ { name: "activeRequests", type: "int" },
706
+ { name: "maxActiveRequests", type: "int" }
707
+ ]
708
+ },
709
+ "client.workerTypeV2": {
710
+ // NOTE: Telegram TL canonical CRC for vector fields uses "vector T" syntax.
711
+ // Equivalent canonical form:
712
+ // "client.workerTypeV2 name:string workers:vector client.workerInstanceV2 = client.WorkerTypeV2"
713
+ id: 2994569623,
714
+ fields: [
715
+ { name: "name", type: "string" },
716
+ { name: "workers", type: { vector: { ref: "client.workerInstanceV2" }, bare: true } }
717
+ ]
718
+ },
719
+ "client.workerTypesV2": {
720
+ // Canonical form:
721
+ // "client.workerTypesV2 types:vector client.workerTypeV2 = client.WorkerTypesV2"
722
+ id: 217111655,
723
+ fields: [{ name: "types", type: { vector: { ref: "client.workerTypeV2" }, bare: true } }]
724
+ },
725
+ // --- Payment ---
726
+ "client.paymentStatus": {
727
+ id: tlId(
728
+ "client.paymentStatus signed_payment:proxy.SignedPayment db_tokens:long max_tokens:long = client.PaymentStatus"
729
+ ),
730
+ fields: [
731
+ { name: "signedPayment", type: { ref: "proxy.SignedPayment" } },
732
+ { name: "dbTokens", type: "long" },
733
+ { name: "maxTokens", type: "long" }
734
+ ]
735
+ },
736
+ // Payment request from proxy to client (may be sent during query flow).
737
+ "proxy.clientRequestPayment": {
738
+ id: tlId(
739
+ "proxy.clientRequestPayment request_id:int256 signed_payment:proxy.SignedPayment db_tokens:long max_tokens:long request_tokens:long = proxy.WorkerRequestPayment"
740
+ ),
741
+ fields: [
742
+ { name: "requestId", type: "int256" },
743
+ { name: "signedPayment", type: { ref: "proxy.SignedPayment" } },
744
+ { name: "dbTokens", type: "long" },
745
+ { name: "maxTokens", type: "long" },
746
+ { name: "requestTokens", type: "long" }
747
+ ]
748
+ },
749
+ "client.refund": {
750
+ id: tlId("client.refund data:bytes = client.Refund"),
751
+ fields: [{ name: "data", type: "bytes" }]
752
+ },
753
+ "client.refundRejected": {
754
+ id: tlId("client.refundRejected active_queries:long = client.Refund"),
755
+ fields: [{ name: "activeQueries", type: "long" }]
756
+ },
757
+ // --- HTTP ---
758
+ "http.header": {
759
+ id: tlId("http.header name:string value:string = http.Header"),
760
+ fields: [
761
+ { name: "name", type: "string" },
762
+ { name: "value", type: "string" }
763
+ ]
764
+ },
765
+ "http.response": {
766
+ // Canonical vector form for CRC:
767
+ // "http.response http_version:string status_code:int reason:string headers:vector http.header payload:bytes = http.Response"
768
+ id: 483443755,
769
+ fields: [
770
+ { name: "httpVersion", type: "string" },
771
+ { name: "statusCode", type: "int" },
772
+ { name: "reason", type: "string" },
773
+ { name: "headers", type: { vector: { ref: "http.header" }, bare: true } },
774
+ { name: "payload", type: "bytes" }
775
+ ]
776
+ },
777
+ "http.request": {
778
+ // Canonical vector form for CRC:
779
+ // "http.request method:string url:string http_version:string headers:vector http.header payload:bytes = http.Response"
780
+ id: 1195978213,
781
+ fields: [
782
+ { name: "method", type: "string" },
783
+ { name: "url", type: "string" },
784
+ { name: "httpVersion", type: "string" },
785
+ { name: "headers", type: { vector: { ref: "http.header" }, bare: true } },
786
+ { name: "payload", type: "bytes" }
787
+ ],
788
+ isFunction: true
789
+ },
790
+ // --- Root Config ---
791
+ "rootConfig.registeredProxy": {
792
+ id: tlId("rootConfig.registeredProxy seqno:int address:string = rootConfig.RegisteredProxy"),
793
+ fields: [
794
+ { name: "seqno", type: "int" },
795
+ { name: "address", type: "string" }
796
+ ]
797
+ },
798
+ // --- Functions ---
799
+ "client.connectToProxy": {
800
+ id: tlId(
801
+ "client.connectToProxy params:client.params min_config_version:int = client.ConnectedToProxy"
802
+ ),
803
+ fields: [
804
+ { name: "params", type: { ref: "client.params" } },
805
+ { name: "minConfigVersion", type: "int" }
806
+ ],
807
+ isFunction: true
808
+ },
809
+ "client.authorizeWithProxyShort": {
810
+ id: tlId("client.authorizeWithProxyShort data:bytes = client.AuthorizationWithProxy"),
811
+ fields: [{ name: "data", type: "bytes" }],
812
+ isFunction: true
813
+ },
814
+ "client.authorizeWithProxyLong": {
815
+ id: tlId("client.authorizeWithProxyLong = client.AuthorizationWithProxy"),
816
+ fields: [],
817
+ isFunction: true
818
+ },
819
+ "client.runQueryEx": {
820
+ id: tlId(
821
+ "client.runQueryEx model_name:string query:bytes max_coefficient:int max_tokens:int timeout:double request_id:int256 min_config_version:int flags:# enable_debug:flags.0?Bool = client.QueryAnswerEx"
822
+ ),
823
+ fields: [
824
+ { name: "modelName", type: "string" },
825
+ { name: "query", type: "bytes" },
826
+ { name: "maxCoefficient", type: "int" },
827
+ { name: "maxTokens", type: "int" },
828
+ { name: "timeout", type: "double" },
829
+ { name: "requestId", type: "int256" },
830
+ { name: "minConfigVersion", type: "int" },
831
+ { name: "flags", type: "int" },
832
+ { name: "enableDebug", type: "Bool", flag: { field: "flags", bit: 0 } }
833
+ ],
834
+ isFunction: true
835
+ },
836
+ "client.getWorkerTypesV2": {
837
+ id: tlId("client.getWorkerTypesV2 = client.WorkerTypesV2"),
838
+ fields: [],
839
+ isFunction: true
840
+ },
841
+ "client.updatePaymentStatus": {
842
+ id: tlId("client.updatePaymentStatus = client.PaymentStatus"),
843
+ fields: [],
844
+ isFunction: true
845
+ },
846
+ "client.requestRefund": {
847
+ id: tlId("client.requestRefund = client.Refund"),
848
+ fields: [],
849
+ isFunction: true
850
+ }
851
+ };
852
+ var CONSTRUCTOR_ID_MAP = /* @__PURE__ */ new Map();
853
+ for (const [name, def] of Object.entries(TL_SCHEMA)) {
854
+ CONSTRUCTOR_ID_MAP.set(def.id, name);
855
+ }
856
+ var POLYMORPHIC_TYPES = {
857
+ "tcp.Packet": [
858
+ "tcp.ping",
859
+ "tcp.pong",
860
+ "tcp.packet",
861
+ "tcp.queryAnswer",
862
+ "tcp.queryError",
863
+ "tcp.query",
864
+ "tcp.connected",
865
+ "tcp.connect"
866
+ ],
867
+ "client.ProxyConnectionAuth": [
868
+ "client.proxyConnectionAuthShort",
869
+ "client.proxyConnectionAuthLong"
870
+ ],
871
+ "proxy.SignedPayment": ["proxy.signedPayment", "proxy.signedPaymentEmpty"],
872
+ "client.AuthorizationWithProxy": [
873
+ "client.authorizationWithProxySuccess",
874
+ "client.authorizationWithProxyFailed"
875
+ ],
876
+ "client.QueryAnswerEx": [
877
+ "client.queryAnswerEx",
878
+ "client.queryAnswerErrorEx",
879
+ "client.queryAnswerPartEx"
880
+ ],
881
+ "client.Refund": ["client.refund", "client.refundRejected"]
882
+ };
883
+
884
+ // src/core/tl/serializer.ts
885
+ var VECTOR_ID = 481674261;
886
+ var BOOL_TRUE_ID = 2574415285;
887
+ var BOOL_FALSE_ID = 3162085175;
888
+ var TLSerializer = class {
889
+ buffers = [];
890
+ totalLength = 0;
891
+ writeInt(value) {
892
+ const buf = Buffer.alloc(4);
893
+ buf.writeInt32LE(value, 0);
894
+ this.push(buf);
895
+ }
896
+ writeUInt(value) {
897
+ const buf = Buffer.alloc(4);
898
+ buf.writeUInt32LE(value, 0);
899
+ this.push(buf);
900
+ }
901
+ writeLong(value) {
902
+ const buf = Buffer.alloc(8);
903
+ buf.writeBigInt64LE(value, 0);
904
+ this.push(buf);
905
+ }
906
+ writeDouble(value) {
907
+ const buf = Buffer.alloc(8);
908
+ buf.writeDoubleLE(value, 0);
909
+ this.push(buf);
910
+ }
911
+ writeInt128(value) {
912
+ if (value.length !== 16) throw new Error(`int128 must be 16 bytes, got ${value.length}`);
913
+ this.push(value);
914
+ }
915
+ writeInt256(value) {
916
+ if (value.length !== 32) throw new Error(`int256 must be 32 bytes, got ${value.length}`);
917
+ this.push(value);
918
+ }
919
+ writeBytes(value) {
920
+ let headerLen;
921
+ if (value.length < 254) {
922
+ const header = Buffer.alloc(1);
923
+ header[0] = value.length;
924
+ this.push(header);
925
+ headerLen = 1;
926
+ } else {
927
+ const header = Buffer.alloc(4);
928
+ header[0] = 254;
929
+ header[1] = value.length & 255;
930
+ header[2] = value.length >> 8 & 255;
931
+ header[3] = value.length >> 16 & 255;
932
+ this.push(header);
933
+ headerLen = 4;
934
+ }
935
+ this.push(value);
936
+ const totalLen = headerLen + value.length;
937
+ const padding = (4 - totalLen % 4) % 4;
938
+ if (padding > 0) {
939
+ this.push(Buffer.alloc(padding));
940
+ }
941
+ }
942
+ writeString(value) {
943
+ this.writeBytes(Buffer.from(value, "utf-8"));
944
+ }
945
+ writeBool(value) {
946
+ this.writeUInt(value ? BOOL_TRUE_ID : BOOL_FALSE_ID);
947
+ }
948
+ writeVector(items, itemType, bare = false) {
949
+ if (!bare) {
950
+ this.writeUInt(VECTOR_ID);
951
+ }
952
+ this.writeInt(items.length);
953
+ for (const item of items) {
954
+ this.writeField(item, itemType);
955
+ }
956
+ }
957
+ writeConstructorId(id) {
958
+ this.writeUInt(id);
959
+ }
960
+ /**
961
+ * Serialize a TL object by its _type, writing constructor ID + fields.
962
+ * boxed=true writes the constructor ID prefix.
963
+ */
964
+ writeObject(obj, boxed = true) {
965
+ const typeName = obj["_type"];
966
+ if (!typeName) throw new Error("TL object must have _type field");
967
+ const schema = TL_SCHEMA[typeName];
968
+ if (!schema) throw new Error(`Unknown TL type: ${typeName}`);
969
+ if (boxed) {
970
+ this.writeConstructorId(schema.id);
971
+ }
972
+ this.writeFields(obj, schema);
973
+ }
974
+ writeFields(obj, schema) {
975
+ const flagValues = {};
976
+ for (const field of schema.fields) {
977
+ if (field.type === "int" && field.name.toLowerCase().includes("flag")) {
978
+ const isFlags = schema.fields.some((f) => f.flag?.field === field.name);
979
+ if (isFlags) {
980
+ let flags = 0;
981
+ for (const f of schema.fields) {
982
+ if (f.flag?.field === field.name && obj[f.name] !== void 0 && obj[f.name] !== null) {
983
+ flags |= 1 << f.flag.bit;
984
+ }
985
+ }
986
+ flagValues[field.name] = flags;
987
+ }
988
+ }
989
+ }
990
+ for (const field of schema.fields) {
991
+ if (field.name in flagValues) {
992
+ this.writeInt(flagValues[field.name]);
993
+ continue;
994
+ }
995
+ if (field.flag) {
996
+ const flagVal = flagValues[field.flag.field] ?? obj[field.flag.field] ?? 0;
997
+ if (!(flagVal & 1 << field.flag.bit)) {
998
+ continue;
999
+ }
1000
+ }
1001
+ const value = obj[field.name];
1002
+ this.writeField(value, field.type);
1003
+ }
1004
+ }
1005
+ writeField(value, type) {
1006
+ if (typeof type === "string") {
1007
+ switch (type) {
1008
+ case "int":
1009
+ this.writeInt(value);
1010
+ break;
1011
+ case "long":
1012
+ this.writeLong(value);
1013
+ break;
1014
+ case "double":
1015
+ this.writeDouble(value);
1016
+ break;
1017
+ case "int128":
1018
+ this.writeInt128(value);
1019
+ break;
1020
+ case "int256":
1021
+ this.writeInt256(value);
1022
+ break;
1023
+ case "string":
1024
+ this.writeString(value);
1025
+ break;
1026
+ case "bytes":
1027
+ this.writeBytes(value);
1028
+ break;
1029
+ case "Bool":
1030
+ this.writeBool(value);
1031
+ break;
1032
+ case "true":
1033
+ break;
1034
+ default:
1035
+ throw new Error(`Unknown primitive type: ${type}`);
1036
+ }
1037
+ } else if ("vector" in type) {
1038
+ this.writeVector(value, type.vector, Boolean(type.bare));
1039
+ } else if ("ref" in type) {
1040
+ const isPolymorphicRef = type.ref in POLYMORPHIC_TYPES;
1041
+ if (isPolymorphicRef) {
1042
+ this.writeObject(value, true);
1043
+ return;
1044
+ }
1045
+ if (!(type.ref in TL_SCHEMA)) {
1046
+ throw new Error(`Unknown TL ref type: ${type.ref}`);
1047
+ }
1048
+ const obj = value;
1049
+ const nestedType = obj["_type"];
1050
+ if (nestedType && nestedType !== type.ref) {
1051
+ throw new Error(`Expected nested type ${type.ref}, got ${nestedType}`);
1052
+ }
1053
+ this.writeObject(nestedType ? obj : { ...obj, _type: type.ref }, false);
1054
+ }
1055
+ }
1056
+ push(buf) {
1057
+ this.buffers.push(buf);
1058
+ this.totalLength += buf.length;
1059
+ }
1060
+ toBuffer() {
1061
+ return Buffer.concat(this.buffers, this.totalLength);
1062
+ }
1063
+ };
1064
+ function serializeTLObject(obj, boxed = true) {
1065
+ const s = new TLSerializer();
1066
+ s.writeObject(obj, boxed);
1067
+ return s.toBuffer();
1068
+ }
1069
+
1070
+ // src/core/tl/deserializer.ts
1071
+ var VECTOR_ID2 = 481674261;
1072
+ var BOOL_TRUE_ID2 = 2574415285;
1073
+ var BOOL_FALSE_ID2 = 3162085175;
1074
+ var TLDeserializer = class {
1075
+ buffer;
1076
+ offset;
1077
+ constructor(buffer) {
1078
+ this.buffer = buffer;
1079
+ this.offset = 0;
1080
+ }
1081
+ get remaining() {
1082
+ return this.buffer.length - this.offset;
1083
+ }
1084
+ readInt() {
1085
+ this.checkRemaining(4);
1086
+ const value = this.buffer.readInt32LE(this.offset);
1087
+ this.offset += 4;
1088
+ return value;
1089
+ }
1090
+ readUInt() {
1091
+ this.checkRemaining(4);
1092
+ const value = this.buffer.readUInt32LE(this.offset);
1093
+ this.offset += 4;
1094
+ return value;
1095
+ }
1096
+ readLong() {
1097
+ this.checkRemaining(8);
1098
+ const value = this.buffer.readBigInt64LE(this.offset);
1099
+ this.offset += 8;
1100
+ return value;
1101
+ }
1102
+ readDouble() {
1103
+ this.checkRemaining(8);
1104
+ const value = this.buffer.readDoubleLE(this.offset);
1105
+ this.offset += 8;
1106
+ return value;
1107
+ }
1108
+ readInt128() {
1109
+ this.checkRemaining(16);
1110
+ const value = Buffer.alloc(16);
1111
+ this.buffer.copy(value, 0, this.offset, this.offset + 16);
1112
+ this.offset += 16;
1113
+ return value;
1114
+ }
1115
+ readInt256() {
1116
+ this.checkRemaining(32);
1117
+ const value = Buffer.alloc(32);
1118
+ this.buffer.copy(value, 0, this.offset, this.offset + 32);
1119
+ this.offset += 32;
1120
+ return value;
1121
+ }
1122
+ readBytes() {
1123
+ this.checkRemaining(1);
1124
+ let length;
1125
+ let headerLen;
1126
+ const firstByte = this.buffer[this.offset];
1127
+ if (firstByte < 254) {
1128
+ length = firstByte;
1129
+ headerLen = 1;
1130
+ this.offset += 1;
1131
+ } else {
1132
+ this.checkRemaining(4);
1133
+ length = this.buffer[this.offset + 1] | this.buffer[this.offset + 2] << 8 | this.buffer[this.offset + 3] << 16;
1134
+ headerLen = 4;
1135
+ this.offset += 4;
1136
+ }
1137
+ this.checkRemaining(length);
1138
+ const value = Buffer.alloc(length);
1139
+ this.buffer.copy(value, 0, this.offset, this.offset + length);
1140
+ this.offset += length;
1141
+ const totalLen = headerLen + length;
1142
+ const padding = (4 - totalLen % 4) % 4;
1143
+ this.offset += padding;
1144
+ return value;
1145
+ }
1146
+ readString() {
1147
+ return this.readBytes().toString("utf-8");
1148
+ }
1149
+ readBool() {
1150
+ const id = this.readUInt();
1151
+ if (id === BOOL_TRUE_ID2) return true;
1152
+ if (id === BOOL_FALSE_ID2) return false;
1153
+ throw new Error(`Invalid Bool constructor ID: 0x${id.toString(16)}`);
1154
+ }
1155
+ readVector(itemType, bare = false) {
1156
+ if (!bare) {
1157
+ const id = this.readUInt();
1158
+ if (id !== VECTOR_ID2) {
1159
+ throw new Error(
1160
+ `Expected vector constructor ID 0x${VECTOR_ID2.toString(16)}, got 0x${id.toString(16)}`
1161
+ );
1162
+ }
1163
+ }
1164
+ const count = this.readInt();
1165
+ const items = [];
1166
+ for (let i = 0; i < count; i++) {
1167
+ items.push(this.readField(itemType));
1168
+ }
1169
+ return items;
1170
+ }
1171
+ /**
1172
+ * Read a boxed TL object: reads constructor ID, looks up schema, reads fields.
1173
+ */
1174
+ readObject() {
1175
+ const constructorId = this.readUInt();
1176
+ const typeName = CONSTRUCTOR_ID_MAP.get(constructorId);
1177
+ if (!typeName) {
1178
+ throw new Error(
1179
+ `Unknown TL constructor ID: 0x${constructorId.toString(16).padStart(8, "0")}`
1180
+ );
1181
+ }
1182
+ const schema = TL_SCHEMA[typeName];
1183
+ return this.readFields(typeName, schema);
1184
+ }
1185
+ /**
1186
+ * Read fields of a known TL type (without reading constructor ID).
1187
+ */
1188
+ readObjectBare(typeName) {
1189
+ const schema = TL_SCHEMA[typeName];
1190
+ if (!schema) throw new Error(`Unknown TL type: ${typeName}`);
1191
+ return this.readFields(typeName, schema);
1192
+ }
1193
+ readFields(typeName, schema) {
1194
+ const result = { _type: typeName };
1195
+ const flagValues = {};
1196
+ for (const field of schema.fields) {
1197
+ if (field.flag) {
1198
+ const flagVal = flagValues[field.flag.field] ?? 0;
1199
+ if (!(flagVal & 1 << field.flag.bit)) {
1200
+ continue;
1201
+ }
1202
+ }
1203
+ const value = this.readField(field.type);
1204
+ result[field.name] = value;
1205
+ if (field.type === "int" && schema.fields.some((f) => f.flag?.field === field.name)) {
1206
+ flagValues[field.name] = value;
1207
+ }
1208
+ }
1209
+ return result;
1210
+ }
1211
+ readField(type) {
1212
+ if (typeof type === "string") {
1213
+ switch (type) {
1214
+ case "int":
1215
+ return this.readInt();
1216
+ case "long":
1217
+ return this.readLong();
1218
+ case "double":
1219
+ return this.readDouble();
1220
+ case "int128":
1221
+ return this.readInt128();
1222
+ case "int256":
1223
+ return this.readInt256();
1224
+ case "string":
1225
+ return this.readString();
1226
+ case "bytes":
1227
+ return this.readBytes();
1228
+ case "Bool":
1229
+ return this.readBool();
1230
+ case "true":
1231
+ return true;
1232
+ // Bare true is present by virtue of the flag being set
1233
+ default:
1234
+ throw new Error(`Unknown primitive type: ${type}`);
1235
+ }
1236
+ } else if ("vector" in type) {
1237
+ return this.readVector(type.vector, Boolean(type.bare));
1238
+ } else if ("ref" in type) {
1239
+ if (type.ref in POLYMORPHIC_TYPES) {
1240
+ return this.readObject();
1241
+ }
1242
+ if (type.ref in TL_SCHEMA) {
1243
+ return this.readObjectBare(type.ref);
1244
+ }
1245
+ throw new Error(`Unknown TL ref type: ${type.ref}`);
1246
+ }
1247
+ throw new Error(`Unhandled field type: ${JSON.stringify(type)}`);
1248
+ }
1249
+ checkRemaining(needed) {
1250
+ if (this.offset + needed > this.buffer.length) {
1251
+ throw new Error(
1252
+ `Buffer underflow: need ${needed} bytes at offset ${this.offset}, but only ${this.buffer.length - this.offset} remain`
1253
+ );
1254
+ }
1255
+ }
1256
+ };
1257
+ function deserializeTLObject(buffer) {
1258
+ const d = new TLDeserializer(buffer);
1259
+ return d.readObject();
1260
+ }
1261
+
1262
+ // src/core/protocol/handshake.ts
1263
+ var import_node_crypto2 = __toESM(require("crypto"), 1);
1264
+ async function performHandshake(conn, ownerAddress, secretString, configVersion, onLongAuthRequired) {
1265
+ const tcpId = import_node_crypto2.default.randomBytes(8).readBigInt64LE();
1266
+ const tcpConnect = { _type: "tcp.connect", id: tcpId };
1267
+ conn.send(serializeTLObject(tcpConnect));
1268
+ const tcpResponse = await waitForFrame(conn, 3e4);
1269
+ const tcpConnected = deserializeTLObject(tcpResponse);
1270
+ if (tcpConnected._type !== "tcp.connected") {
1271
+ throw new ProtocolError(`Expected tcp.connected, got ${tcpConnected._type}`);
1272
+ }
1273
+ const clientParams = {
1274
+ _type: "client.params",
1275
+ flags: 3,
1276
+ // bit 0 (isTest) + bit 1 (proto versions)
1277
+ clientOwnerAddress: ownerAddress,
1278
+ isTest: false,
1279
+ minProtoVersion: 0,
1280
+ maxProtoVersion: 1
1281
+ };
1282
+ const connectReq = {
1283
+ _type: "client.connectToProxy",
1284
+ params: clientParams,
1285
+ minConfigVersion: configVersion
1286
+ };
1287
+ const queryId = import_node_crypto2.default.randomBytes(8).readBigInt64LE();
1288
+ sendQuery(conn, queryId, serializeTLObject(connectReq));
1289
+ const connectResponseData = await waitForQueryAnswer(
1290
+ conn,
1291
+ queryId,
1292
+ 3e4,
1293
+ "connectToProxy response"
1294
+ );
1295
+ const connected = deserializeTLObject(connectResponseData);
1296
+ if (connected._type !== "client.connectedToProxy") {
1297
+ throw new ProtocolError(`Expected client.connectedToProxy, got ${connected._type}`);
1298
+ }
1299
+ const protoVersion = connected.params.protoVersion ?? 0;
1300
+ const auth = connected.auth;
1301
+ let authResult;
1302
+ if (auth._type === "client.proxyConnectionAuthShort") {
1303
+ const ourSecretHash = Buffer.from(import_node_crypto2.default.createHash("sha256").update(secretString).digest());
1304
+ if (ourSecretHash.equals(auth.secretHash)) {
1305
+ const authReq = {
1306
+ _type: "client.authorizeWithProxyShort",
1307
+ data: Buffer.from(secretString, "utf-8")
1308
+ };
1309
+ const authQueryId = import_node_crypto2.default.randomBytes(8).readBigInt64LE();
1310
+ sendQuery(
1311
+ conn,
1312
+ authQueryId,
1313
+ serializeTLObject(authReq)
1314
+ );
1315
+ const authResponseData = await waitForQueryAnswer(
1316
+ conn,
1317
+ authQueryId,
1318
+ 3e5,
1319
+ "short auth response"
1320
+ );
1321
+ authResult = deserializeTLObject(authResponseData);
1322
+ } else {
1323
+ authResult = await performLongAuth(
1324
+ conn,
1325
+ connected,
1326
+ auth.nonce,
1327
+ onLongAuthRequired,
1328
+ "Proxy requested long auth because provided SECRET does not match on-chain secret hash"
1329
+ );
1330
+ }
1331
+ } else {
1332
+ authResult = await performLongAuth(
1333
+ conn,
1334
+ connected,
1335
+ auth.nonce,
1336
+ onLongAuthRequired,
1337
+ "Proxy requires long auth (wallet is not registered for this proxy or SECRET is unavailable)"
1338
+ );
1339
+ }
1340
+ if (authResult._type === "client.authorizationWithProxyFailed") {
1341
+ throw new AuthenticationError(`Proxy auth failed: ${authResult.error}`, authResult.errorCode);
1342
+ }
1343
+ if (authResult._type !== "client.authorizationWithProxySuccess") {
1344
+ throw new ProtocolError(
1345
+ `Unexpected auth response type: ${authResult._type}`
1346
+ );
1347
+ }
1348
+ return {
1349
+ proxyParams: connected.params,
1350
+ clientScAddress: connected.clientScAddress,
1351
+ signedPayment: authResult.signedPayment,
1352
+ tokensCommittedToDb: authResult.tokensCommittedToDb,
1353
+ maxTokens: authResult.maxTokens,
1354
+ protoVersion
1355
+ };
1356
+ }
1357
+ function buildHandshakeCloseError(stage, error) {
1358
+ const suffix = " This usually means RA-TLS/mTLS client credentials were rejected by the proxy.";
1359
+ return new ConnectionError(
1360
+ `Connection closed while waiting for ${stage}.${suffix}`,
1361
+ error
1362
+ );
1363
+ }
1364
+ async function performLongAuth(conn, connected, nonce, onLongAuthRequired, missingHandlerMessage) {
1365
+ if (!onLongAuthRequired) {
1366
+ throw new AuthenticationError(
1367
+ `${missingHandlerMessage}. Provide SECRET for short auth, or keep automatic long-auth registration enabled`
1368
+ );
1369
+ }
1370
+ await onLongAuthRequired({
1371
+ nonce,
1372
+ clientScAddress: connected.clientScAddress,
1373
+ proxyParams: connected.params
1374
+ });
1375
+ const authQueryId = import_node_crypto2.default.randomBytes(8).readBigInt64LE();
1376
+ const authReq = { _type: "client.authorizeWithProxyLong" };
1377
+ sendQuery(conn, authQueryId, serializeTLObject(authReq));
1378
+ const authResponseData = await waitForQueryAnswer(
1379
+ conn,
1380
+ authQueryId,
1381
+ 3e5,
1382
+ "long auth response"
1383
+ );
1384
+ return deserializeTLObject(authResponseData);
1385
+ }
1386
+ function sendQuery(conn, queryId, data) {
1387
+ const queryObj = {
1388
+ _type: "tcp.query",
1389
+ id: queryId,
1390
+ data
1391
+ };
1392
+ conn.send(serializeTLObject(queryObj));
1393
+ }
1394
+ function waitForFrame(conn, timeoutMs) {
1395
+ return new Promise((resolve, reject) => {
1396
+ const timer = setTimeout(() => cleanup(() => reject(new ConnectionError("Timeout waiting for frame"))), timeoutMs);
1397
+ const cleanup = (next) => {
1398
+ clearTimeout(timer);
1399
+ conn.removeListener("frame", onFrame);
1400
+ conn.removeListener("close", onClose);
1401
+ if (next) next();
1402
+ };
1403
+ const onFrame = (data) => {
1404
+ cleanup(() => resolve(data));
1405
+ };
1406
+ const onClose = (error) => {
1407
+ cleanup(() => reject(buildHandshakeCloseError("handshake frame", error)));
1408
+ };
1409
+ conn.once("frame", onFrame);
1410
+ conn.once("close", onClose);
1411
+ });
1412
+ }
1413
+ function waitForQueryAnswer(conn, queryId, timeoutMs, stage = "query answer") {
1414
+ return new Promise((resolve, reject) => {
1415
+ const timer = setTimeout(
1416
+ () => cleanup(() => reject(new ConnectionError(`Timeout waiting for ${stage}`))),
1417
+ timeoutMs
1418
+ );
1419
+ const cleanup = (next) => {
1420
+ clearTimeout(timer);
1421
+ conn.removeListener("frame", onFrame);
1422
+ conn.removeListener("close", onClose);
1423
+ if (next) next();
1424
+ };
1425
+ const onClose = (error) => {
1426
+ cleanup(() => reject(buildHandshakeCloseError(stage, error)));
1427
+ };
1428
+ const onFrame = (data) => {
1429
+ let obj;
1430
+ try {
1431
+ obj = deserializeTLObject(data);
1432
+ } catch (err) {
1433
+ cleanup(
1434
+ () => reject(new ProtocolError(`Failed to deserialize handshake frame: ${String(err)}`))
1435
+ );
1436
+ return;
1437
+ }
1438
+ if (obj["_type"] === "tcp.queryAnswer") {
1439
+ const answerId = obj["id"];
1440
+ if (answerId === queryId) {
1441
+ cleanup(() => resolve(obj["data"]));
1442
+ return;
1443
+ }
1444
+ } else if (obj["_type"] === "tcp.queryError") {
1445
+ const errorId = obj["id"];
1446
+ if (errorId === queryId) {
1447
+ cleanup(
1448
+ () => reject(new ProtocolError(`Query error: ${obj["message"]}`, obj["code"]))
1449
+ );
1450
+ return;
1451
+ }
1452
+ }
1453
+ };
1454
+ conn.on("frame", onFrame);
1455
+ conn.once("close", onClose);
1456
+ });
1457
+ }
1458
+
1459
+ // src/core/protocol/session.ts
1460
+ var CocoonSession = class extends import_node_events2.EventEmitter {
1461
+ conn = null;
1462
+ handshakeResult = null;
1463
+ pendingQueries = /* @__PURE__ */ new Map();
1464
+ keepaliveTimer = null;
1465
+ _connected = false;
1466
+ options;
1467
+ constructor(options) {
1468
+ super();
1469
+ this.options = options;
1470
+ }
1471
+ get connected() {
1472
+ return this._connected;
1473
+ }
1474
+ get protoVersion() {
1475
+ return this.handshakeResult?.protoVersion ?? 0;
1476
+ }
1477
+ async connect() {
1478
+ if (this._connected) return;
1479
+ this.conn = new CocoonConnection(this.options);
1480
+ await this.conn.connect();
1481
+ this.handshakeResult = await performHandshake(
1482
+ this.conn,
1483
+ this.options.ownerAddress,
1484
+ this.options.secretString,
1485
+ this.options.configVersion ?? 0,
1486
+ this.options.onLongAuthRequired
1487
+ );
1488
+ this._connected = true;
1489
+ this.conn.on("frame", (data) => this.handleFrame(data));
1490
+ this.conn.on("close", (error) => {
1491
+ this._connected = false;
1492
+ this.cleanup();
1493
+ this.emit("close", error);
1494
+ });
1495
+ this.startKeepalive();
1496
+ }
1497
+ /**
1498
+ * Send a query and wait for the complete answer.
1499
+ */
1500
+ async sendQuery(modelName, httpRequest, options = {}) {
1501
+ if (!this.conn || !this._connected) {
1502
+ throw new ConnectionError("Not connected");
1503
+ }
1504
+ const requestId = import_node_crypto3.default.randomBytes(32);
1505
+ const timeout = options.timeout ?? this.options.timeout ?? 12e4;
1506
+ const queryPayload = serializeTLObject(httpRequest, true);
1507
+ const runQuery = {
1508
+ _type: "client.runQueryEx",
1509
+ modelName,
1510
+ query: queryPayload,
1511
+ maxCoefficient: options.maxCoefficient ?? 4e3,
1512
+ maxTokens: options.maxTokens ?? 1e3,
1513
+ timeout: timeout / 1e3 * 0.95,
1514
+ requestId,
1515
+ minConfigVersion: this.options.configVersion ?? 0,
1516
+ flags: options.enableDebug ? 1 : 0,
1517
+ enableDebug: options.enableDebug
1518
+ };
1519
+ const tlData = serializeTLObject(runQuery, true);
1520
+ const tcpPacket = {
1521
+ _type: "tcp.packet",
1522
+ data: tlData
1523
+ };
1524
+ this.conn.send(serializeTLObject(tcpPacket));
1525
+ return new Promise((resolve, reject) => {
1526
+ const requestIdHex = requestId.toString("hex");
1527
+ const timer = setTimeout(() => {
1528
+ this.pendingQueries.delete(requestIdHex);
1529
+ reject(new TimeoutError(`Query timed out after ${timeout}ms`));
1530
+ }, timeout);
1531
+ this.pendingQueries.set(requestIdHex, {
1532
+ resolve,
1533
+ reject,
1534
+ onPart: options.onPart,
1535
+ timer
1536
+ });
1537
+ });
1538
+ }
1539
+ /**
1540
+ * Send a raw TL function (for getWorkerTypesV2, etc.)
1541
+ */
1542
+ async sendRpcQuery(tlObject, timeoutMs = 3e4) {
1543
+ if (!this.conn || !this._connected) {
1544
+ throw new ConnectionError("Not connected");
1545
+ }
1546
+ const queryId = import_node_crypto3.default.randomBytes(8).readBigInt64LE();
1547
+ const data = serializeTLObject(tlObject, true);
1548
+ const tcpQuery = {
1549
+ _type: "tcp.query",
1550
+ id: queryId,
1551
+ data
1552
+ };
1553
+ this.conn.send(serializeTLObject(tcpQuery));
1554
+ return new Promise((resolve, reject) => {
1555
+ const timer = setTimeout(() => {
1556
+ this.removeListener(`queryAnswer:${queryId}`, onAnswer);
1557
+ reject(new TimeoutError(`RPC query timed out after ${timeoutMs}ms`));
1558
+ }, timeoutMs);
1559
+ const onAnswer = (result) => {
1560
+ clearTimeout(timer);
1561
+ if (result.error) {
1562
+ reject(new ProtocolError(result.error.message, result.error.code));
1563
+ } else if (result.data) {
1564
+ resolve(deserializeTLObject(result.data));
1565
+ }
1566
+ };
1567
+ this.once(`queryAnswer:${queryId}`, onAnswer);
1568
+ });
1569
+ }
1570
+ async disconnect() {
1571
+ this.cleanup();
1572
+ this.conn?.destroy();
1573
+ this.conn = null;
1574
+ this._connected = false;
1575
+ }
1576
+ handleFrame(data) {
1577
+ if (data.length === 0) {
1578
+ return;
1579
+ }
1580
+ let obj;
1581
+ try {
1582
+ obj = deserializeTLObject(data);
1583
+ } catch (e) {
1584
+ this.emit("error", new ProtocolError(`Failed to deserialize frame: ${e}`));
1585
+ return;
1586
+ }
1587
+ const type = obj["_type"];
1588
+ switch (type) {
1589
+ case "tcp.pong":
1590
+ break;
1591
+ case "tcp.ping": {
1592
+ if (this.conn) {
1593
+ const pong = { _type: "tcp.pong", id: obj["id"] };
1594
+ this.conn.send(serializeTLObject(pong));
1595
+ }
1596
+ break;
1597
+ }
1598
+ case "tcp.queryAnswer": {
1599
+ const queryId = obj["id"];
1600
+ this.emit(`queryAnswer:${queryId}`, { data: obj["data"] });
1601
+ break;
1602
+ }
1603
+ case "tcp.queryError": {
1604
+ const queryId = obj["id"];
1605
+ this.emit(`queryAnswer:${queryId}`, {
1606
+ error: { code: obj["code"], message: obj["message"] }
1607
+ });
1608
+ break;
1609
+ }
1610
+ case "tcp.packet": {
1611
+ const innerData = obj["data"];
1612
+ if (innerData.length === 0) break;
1613
+ let innerObj;
1614
+ try {
1615
+ innerObj = deserializeTLObject(innerData);
1616
+ } catch (e) {
1617
+ this.emit("error", new ProtocolError(`Failed to deserialize inner packet: ${e}`));
1618
+ return;
1619
+ }
1620
+ this.handleInnerPacket(innerObj);
1621
+ break;
1622
+ }
1623
+ default:
1624
+ this.emit("error", new ProtocolError(`Unexpected frame type: ${type}`));
1625
+ }
1626
+ }
1627
+ handleInnerPacket(obj) {
1628
+ const type = obj["_type"];
1629
+ let normalized = obj;
1630
+ if (type === "client.queryAnswer") {
1631
+ normalized = {
1632
+ ...obj,
1633
+ _type: "client.queryAnswerEx"
1634
+ };
1635
+ } else if (type === "client.queryAnswerPart") {
1636
+ normalized = {
1637
+ ...obj,
1638
+ _type: "client.queryAnswerPartEx"
1639
+ };
1640
+ } else if (type === "client.queryAnswerError" || type === "client.queryAnswerPartError") {
1641
+ normalized = {
1642
+ ...obj,
1643
+ _type: "client.queryAnswerErrorEx"
1644
+ };
1645
+ }
1646
+ const normalizedType = normalized["_type"];
1647
+ if (normalizedType === "client.queryAnswerEx" || normalizedType === "client.queryAnswerErrorEx" || normalizedType === "client.queryAnswerPartEx") {
1648
+ const requestId = normalized["requestId"].toString("hex");
1649
+ const pending = this.pendingQueries.get(requestId);
1650
+ if (!pending) {
1651
+ return;
1652
+ }
1653
+ const answer = normalized;
1654
+ if (normalizedType === "client.queryAnswerErrorEx") {
1655
+ clearTimeout(pending.timer);
1656
+ this.pendingQueries.delete(requestId);
1657
+ pending.resolve(answer);
1658
+ return;
1659
+ }
1660
+ pending.onPart?.(answer);
1661
+ if (this.isTerminalQueryAnswer(answer)) {
1662
+ clearTimeout(pending.timer);
1663
+ this.pendingQueries.delete(requestId);
1664
+ pending.resolve(answer);
1665
+ }
1666
+ }
1667
+ }
1668
+ isTerminalQueryAnswer(answer) {
1669
+ if (answer._type === "client.queryAnswerErrorEx") {
1670
+ return true;
1671
+ }
1672
+ const legacyIsCompleted = answer.isCompleted;
1673
+ if (typeof legacyIsCompleted === "boolean") {
1674
+ return legacyIsCompleted;
1675
+ }
1676
+ if ("finalInfo" in answer && answer.finalInfo) {
1677
+ return true;
1678
+ }
1679
+ if ("flags" in answer && typeof answer.flags === "number" && (answer.flags & 1) !== 0) {
1680
+ return true;
1681
+ }
1682
+ if ("answer" in answer && answer.answer) {
1683
+ try {
1684
+ const envelope = deserializeTLObject(answer.answer);
1685
+ if (envelope._type === "http.response") {
1686
+ const transferEncoding = envelope.headers?.find(
1687
+ (h) => h.name.toLowerCase() === "transfer-encoding"
1688
+ )?.value;
1689
+ if (transferEncoding && transferEncoding.toLowerCase().includes("chunked")) {
1690
+ return false;
1691
+ }
1692
+ return true;
1693
+ }
1694
+ } catch {
1695
+ }
1696
+ }
1697
+ if (answer._type === "client.queryAnswerPartEx") {
1698
+ return false;
1699
+ }
1700
+ return true;
1701
+ }
1702
+ startKeepalive() {
1703
+ this.keepaliveTimer = setInterval(() => {
1704
+ if (this.conn?.isConnected) {
1705
+ const pingId = import_node_crypto3.default.randomBytes(8).readBigInt64LE();
1706
+ const ping = { _type: "tcp.ping", id: pingId };
1707
+ try {
1708
+ this.conn.send(serializeTLObject(ping));
1709
+ } catch {
1710
+ }
1711
+ }
1712
+ }, 1e4);
1713
+ }
1714
+ cleanup() {
1715
+ if (this.keepaliveTimer) {
1716
+ clearInterval(this.keepaliveTimer);
1717
+ this.keepaliveTimer = null;
1718
+ }
1719
+ for (const [, pending] of this.pendingQueries) {
1720
+ clearTimeout(pending.timer);
1721
+ pending.reject(new ConnectionError("Connection closed"));
1722
+ }
1723
+ this.pendingQueries.clear();
1724
+ }
1725
+ };
1726
+ function buildHttpRequest(method, url, body, extraHeaders = []) {
1727
+ const hasHeader = (name) => extraHeaders.some((h) => h.name.toLowerCase() === name.toLowerCase());
1728
+ const headers = [...extraHeaders];
1729
+ if (!hasHeader("Content-Type")) {
1730
+ headers.unshift({ _type: "http.header", name: "Content-Type", value: "application/json" });
1731
+ }
1732
+ if (!hasHeader("Content-Length")) {
1733
+ headers.push({ _type: "http.header", name: "Content-Length", value: body.length.toString() });
1734
+ }
1735
+ if (!hasHeader("Host")) {
1736
+ headers.push({ _type: "http.header", name: "Host", value: "api.openai.com" });
1737
+ }
1738
+ return {
1739
+ _type: "http.request",
1740
+ method,
1741
+ url,
1742
+ httpVersion: "HTTP/1.1",
1743
+ headers,
1744
+ payload: body
1745
+ };
1746
+ }
1747
+
1748
+ // src/core/streaming.ts
1749
+ var Stream = class {
1750
+ controller = null;
1751
+ stream;
1752
+ _done = false;
1753
+ constructor() {
1754
+ this.stream = new ReadableStream({
1755
+ start: (controller) => {
1756
+ this.controller = controller;
1757
+ }
1758
+ });
1759
+ }
1760
+ /**
1761
+ * Push a chunk into the stream.
1762
+ */
1763
+ push(chunk) {
1764
+ if (this._done) return;
1765
+ this.controller?.enqueue(chunk);
1766
+ }
1767
+ /**
1768
+ * Signal that the stream is complete.
1769
+ */
1770
+ end() {
1771
+ if (this._done) return;
1772
+ this._done = true;
1773
+ this.controller?.close();
1774
+ }
1775
+ /**
1776
+ * Signal an error on the stream.
1777
+ */
1778
+ error(err) {
1779
+ if (this._done) return;
1780
+ this._done = true;
1781
+ this.controller?.error(err);
1782
+ }
1783
+ get done() {
1784
+ return this._done;
1785
+ }
1786
+ async *[Symbol.asyncIterator]() {
1787
+ const reader = this.stream.getReader();
1788
+ try {
1789
+ while (true) {
1790
+ const { done, value } = await reader.read();
1791
+ if (done) break;
1792
+ yield value;
1793
+ }
1794
+ } finally {
1795
+ reader.releaseLock();
1796
+ }
1797
+ }
1798
+ };
1799
+
1800
+ // src/resources/chat/completions.ts
1801
+ var Completions = class {
1802
+ constructor(getSession) {
1803
+ this.getSession = getSession;
1804
+ }
1805
+ extractHttpData(answer) {
1806
+ try {
1807
+ const httpResponse = deserializeTLObject(answer);
1808
+ if (httpResponse._type === "http.response") {
1809
+ return {
1810
+ payload: httpResponse.payload,
1811
+ statusCode: httpResponse.statusCode,
1812
+ reason: httpResponse.reason
1813
+ };
1814
+ }
1815
+ } catch {
1816
+ }
1817
+ return { payload: answer };
1818
+ }
1819
+ parseErrorPayload(payload, fallbackMessage) {
1820
+ if (payload.length === 0) {
1821
+ return { message: fallbackMessage, body: { error: fallbackMessage } };
1822
+ }
1823
+ const text = payload.toString("utf-8");
1824
+ try {
1825
+ const parsed = JSON.parse(text);
1826
+ const message = typeof parsed.message === "string" ? parsed.message : typeof parsed.error === "string" ? parsed.error : fallbackMessage;
1827
+ return { message, body: parsed };
1828
+ } catch {
1829
+ return { message: text || fallbackMessage, body: { error: text || fallbackMessage } };
1830
+ }
1831
+ }
1832
+ async create(params) {
1833
+ const session = await this.getSession();
1834
+ const body = JSON.stringify({
1835
+ model: params.model,
1836
+ messages: params.messages,
1837
+ stream: params.stream ?? false,
1838
+ ...params.temperature !== void 0 && { temperature: params.temperature },
1839
+ ...params.top_p !== void 0 && { top_p: params.top_p },
1840
+ ...params.max_tokens !== void 0 && { max_tokens: params.max_tokens },
1841
+ ...params.max_completion_tokens !== void 0 && {
1842
+ max_completion_tokens: params.max_completion_tokens
1843
+ },
1844
+ ...params.stop !== void 0 && { stop: params.stop },
1845
+ ...params.presence_penalty !== void 0 && { presence_penalty: params.presence_penalty },
1846
+ ...params.frequency_penalty !== void 0 && {
1847
+ frequency_penalty: params.frequency_penalty
1848
+ }
1849
+ });
1850
+ const httpRequest = buildHttpRequest(
1851
+ "POST",
1852
+ "/v1/chat/completions",
1853
+ Buffer.from(body, "utf-8")
1854
+ );
1855
+ if (params.stream) {
1856
+ return this.createStreaming(session, params, httpRequest);
1857
+ }
1858
+ return this.createNonStreaming(session, params, httpRequest);
1859
+ }
1860
+ async createNonStreaming(session, params, httpRequest) {
1861
+ const chunks = [];
1862
+ let sawAnswer = false;
1863
+ let statusCode;
1864
+ let statusReason;
1865
+ const finalAnswer = await session.sendQuery(params.model, httpRequest, {
1866
+ maxCoefficient: params.max_coefficient,
1867
+ maxTokens: params.max_tokens ?? params.max_completion_tokens,
1868
+ timeout: params.timeout,
1869
+ onPart: (part) => {
1870
+ if (!("answer" in part) || !part.answer) {
1871
+ return;
1872
+ }
1873
+ sawAnswer = true;
1874
+ const extracted = this.extractHttpData(part.answer);
1875
+ if (extracted.statusCode !== void 0) {
1876
+ statusCode = extracted.statusCode;
1877
+ statusReason = extracted.reason;
1878
+ }
1879
+ if (extracted.payload.length > 0) {
1880
+ chunks.push(extracted.payload);
1881
+ }
1882
+ }
1883
+ });
1884
+ if (finalAnswer._type === "client.queryAnswerErrorEx") {
1885
+ throw new APIError(finalAnswer.error, 500, {
1886
+ error_code: finalAnswer.errorCode,
1887
+ error: finalAnswer.error
1888
+ });
1889
+ }
1890
+ if (!sawAnswer && "answer" in finalAnswer && finalAnswer.answer) {
1891
+ const extracted = this.extractHttpData(finalAnswer.answer);
1892
+ if (extracted.statusCode !== void 0) {
1893
+ statusCode = extracted.statusCode;
1894
+ statusReason = extracted.reason;
1895
+ }
1896
+ if (extracted.payload.length > 0) {
1897
+ chunks.push(extracted.payload);
1898
+ }
1899
+ }
1900
+ const fullPayload = Buffer.concat(chunks);
1901
+ if (statusCode !== void 0 && statusCode >= 400) {
1902
+ const fallback = statusReason ? `${statusCode} ${statusReason}` : `HTTP ${statusCode}`;
1903
+ const parsed = this.parseErrorPayload(fullPayload, fallback);
1904
+ throw new APIError(parsed.message, statusCode, parsed.body);
1905
+ }
1906
+ if (fullPayload.length === 0) {
1907
+ throw new ProtocolError("Empty response from proxy");
1908
+ }
1909
+ try {
1910
+ return JSON.parse(fullPayload.toString("utf-8"));
1911
+ } catch (error) {
1912
+ throw new ProtocolError(
1913
+ `Failed to parse completion payload as JSON: ${error instanceof Error ? error.message : String(error)}`
1914
+ );
1915
+ }
1916
+ }
1917
+ async createStreaming(session, params, httpRequest) {
1918
+ const stream = new Stream();
1919
+ let sseBuffer = "";
1920
+ let headersParsed = false;
1921
+ let statusCode;
1922
+ let statusReason;
1923
+ const errorChunks = [];
1924
+ session.sendQuery(params.model, httpRequest, {
1925
+ maxCoefficient: params.max_coefficient,
1926
+ maxTokens: params.max_tokens ?? params.max_completion_tokens,
1927
+ timeout: params.timeout,
1928
+ onPart: (part) => {
1929
+ try {
1930
+ let rawData;
1931
+ if ("answer" in part && part.answer) {
1932
+ const answer = part.answer;
1933
+ if (!headersParsed) {
1934
+ try {
1935
+ const httpResponse = deserializeTLObject(answer);
1936
+ if (httpResponse._type === "http.response") {
1937
+ headersParsed = true;
1938
+ statusCode = httpResponse.statusCode;
1939
+ statusReason = httpResponse.reason;
1940
+ rawData = httpResponse.payload;
1941
+ } else {
1942
+ rawData = answer;
1943
+ }
1944
+ } catch {
1945
+ rawData = answer;
1946
+ headersParsed = true;
1947
+ }
1948
+ } else {
1949
+ rawData = answer;
1950
+ }
1951
+ }
1952
+ if (rawData && rawData.length > 0) {
1953
+ if (statusCode !== void 0 && statusCode >= 400) {
1954
+ errorChunks.push(rawData);
1955
+ return;
1956
+ }
1957
+ sseBuffer += rawData.toString("utf-8");
1958
+ this.processSSEBuffer(sseBuffer, stream);
1959
+ const lastNewline = sseBuffer.lastIndexOf("\n");
1960
+ if (lastNewline >= 0) {
1961
+ sseBuffer = sseBuffer.substring(lastNewline + 1);
1962
+ }
1963
+ }
1964
+ } catch (e) {
1965
+ stream.error(e instanceof Error ? e : new Error(String(e)));
1966
+ }
1967
+ }
1968
+ }).then(() => {
1969
+ if (statusCode !== void 0 && statusCode >= 400) {
1970
+ const parsed = this.parseErrorPayload(
1971
+ Buffer.concat(errorChunks),
1972
+ statusReason ? `${statusCode} ${statusReason}` : `HTTP ${statusCode}`
1973
+ );
1974
+ stream.error(new APIError(parsed.message, statusCode, parsed.body));
1975
+ return;
1976
+ }
1977
+ if (sseBuffer.trim().length > 0) {
1978
+ this.processSSEBuffer(sseBuffer + "\n", stream);
1979
+ }
1980
+ stream.end();
1981
+ }).catch((err) => {
1982
+ stream.error(err instanceof Error ? err : new Error(String(err)));
1983
+ });
1984
+ return stream;
1985
+ }
1986
+ processSSEBuffer(buffer, stream) {
1987
+ const lines = buffer.split("\n");
1988
+ for (const line of lines) {
1989
+ const trimmed = line.trim();
1990
+ if (trimmed === "") continue;
1991
+ if (trimmed === "data: [DONE]") continue;
1992
+ if (trimmed.startsWith("data: ")) {
1993
+ const jsonStr = trimmed.substring(6);
1994
+ try {
1995
+ const chunk = JSON.parse(jsonStr);
1996
+ stream.push(chunk);
1997
+ } catch {
1998
+ }
1999
+ }
2000
+ }
2001
+ }
2002
+ };
2003
+
2004
+ // src/resources/models/models.ts
2005
+ var Models = class {
2006
+ constructor(getSession) {
2007
+ this.getSession = getSession;
2008
+ }
2009
+ /**
2010
+ * List all available models on the network.
2011
+ */
2012
+ async list() {
2013
+ const session = await this.getSession();
2014
+ const result = await session.sendRpcQuery({
2015
+ _type: "client.getWorkerTypesV2"
2016
+ });
2017
+ const workerTypes = result;
2018
+ const models = workerTypes.types.map((wt) => {
2019
+ const totalWorkers = wt.workers.length;
2020
+ const coefficients = wt.workers.map((w) => w.coefficient);
2021
+ const minCoeff = coefficients.length > 0 ? Math.min(...coefficients) : 0;
2022
+ const maxCoeff = coefficients.length > 0 ? Math.max(...coefficients) : 0;
2023
+ return {
2024
+ id: wt.name,
2025
+ object: "model",
2026
+ created: Math.floor(Date.now() / 1e3),
2027
+ owned_by: "cocoon",
2028
+ active_workers: totalWorkers,
2029
+ coefficient_min: minCoeff,
2030
+ coefficient_max: maxCoeff
2031
+ };
2032
+ });
2033
+ return {
2034
+ object: "list",
2035
+ data: models
2036
+ };
2037
+ }
2038
+ };
2039
+
2040
+ // src/ton/wallet.ts
2041
+ var import_crypto = require("@ton/crypto");
2042
+ var import_ton = require("@ton/ton");
2043
+ var import_node_crypto4 = __toESM(require("crypto"), 1);
2044
+ var MnemonicWallet = class {
2045
+ mnemonic;
2046
+ keyPair = null;
2047
+ wallet = null;
2048
+ _address = null;
2049
+ tonClient = null;
2050
+ tonClient4 = null;
2051
+ constructor(mnemonic, network = "mainnet", options) {
2052
+ this.mnemonic = mnemonic.trim().split(/\s+/);
2053
+ if (this.mnemonic.length !== 24) {
2054
+ throw new Error(`Expected 24 mnemonic words, got ${this.mnemonic.length}`);
2055
+ }
2056
+ this.network = network;
2057
+ this.tonEndpoint = options?.tonEndpoint;
2058
+ this.tonV4Endpoint = options?.tonV4Endpoint;
2059
+ }
2060
+ network;
2061
+ tonEndpoint;
2062
+ tonV4Endpoint;
2063
+ async init() {
2064
+ if (this.keyPair) return;
2065
+ this.keyPair = await (0, import_crypto.mnemonicToPrivateKey)(this.mnemonic);
2066
+ this.wallet = import_ton.WalletContractV4.create({
2067
+ workchain: 0,
2068
+ publicKey: this.keyPair.publicKey
2069
+ });
2070
+ this._address = this.wallet.address;
2071
+ }
2072
+ get address() {
2073
+ if (!this._address) throw new Error("Wallet not initialized. Call init() first.");
2074
+ return this._address;
2075
+ }
2076
+ get addressString() {
2077
+ return this.address.toString({ bounceable: true, testOnly: this.network === "testnet" });
2078
+ }
2079
+ get publicKey() {
2080
+ if (!this.keyPair) throw new Error("Wallet not initialized. Call init() first.");
2081
+ return this.keyPair.publicKey;
2082
+ }
2083
+ get secretKey() {
2084
+ if (!this.keyPair) throw new Error("Wallet not initialized. Call init() first.");
2085
+ return this.keyPair.secretKey;
2086
+ }
2087
+ /**
2088
+ * Compute SHA-256 hash of a secret string (for short auth).
2089
+ */
2090
+ secretHash(secret) {
2091
+ return import_node_crypto4.default.createHash("sha256").update(secret).digest();
2092
+ }
2093
+ /**
2094
+ * Get or create a TonClient for blockchain queries.
2095
+ */
2096
+ getTonClient() {
2097
+ if (!this.tonClient) {
2098
+ const endpoint = this.tonEndpoint ?? (this.network === "mainnet" ? "https://toncenter.com/api/v2/jsonRPC" : "https://testnet.toncenter.com/api/v2/jsonRPC");
2099
+ this.tonClient = new import_ton.TonClient({ endpoint });
2100
+ }
2101
+ return this.tonClient;
2102
+ }
2103
+ /**
2104
+ * Get or create a TonClient4 for sending transactions via v4 HTTP API.
2105
+ */
2106
+ getTonClient4() {
2107
+ if (!this.tonClient4) {
2108
+ const endpoint = this.tonV4Endpoint ?? (this.network === "mainnet" ? "https://mainnet-v4.tonhubapi.com" : "https://testnet-v4.tonhubapi.com");
2109
+ this.tonClient4 = new import_ton.TonClient4({ endpoint });
2110
+ }
2111
+ return this.tonClient4;
2112
+ }
2113
+ /**
2114
+ * Create a sender for sending transactions.
2115
+ */
2116
+ async createSender() {
2117
+ if (!this.wallet || !this.keyPair) {
2118
+ throw new Error("Wallet not initialized");
2119
+ }
2120
+ const client = this.getTonClient4();
2121
+ const contract = client.open(this.wallet);
2122
+ return contract.sender(this.keyPair.secretKey);
2123
+ }
2124
+ };
2125
+
2126
+ // src/ton/discovery.ts
2127
+ var import_ton3 = require("@ton/ton");
2128
+
2129
+ // src/ton/contracts/root.ts
2130
+ var import_ton2 = require("@ton/ton");
2131
+ function buildClientParamsCell(params) {
2132
+ const b = (0, import_ton2.beginCell)().storeUint(params.structVersion, 8).storeUint(params.paramsVersion, 32).storeUint(params.uniqueId, 32).storeBit(params.isTest).storeCoins(params.pricePerToken).storeCoins(params.workerFeePerToken);
2133
+ if (params.structVersion >= 2) {
2134
+ if (params.structVersion >= 3) {
2135
+ b.storeUint(params.promptTokensPriceMultiplier, 32);
2136
+ }
2137
+ b.storeUint(params.cachedTokensPriceMultiplier, 32);
2138
+ if (params.structVersion >= 3) {
2139
+ b.storeUint(params.completionTokensPriceMultiplier, 32);
2140
+ }
2141
+ b.storeUint(params.reasoningTokensPriceMultiplier, 32);
2142
+ }
2143
+ b.storeUint(params.proxyDelayBeforeClose, 32);
2144
+ b.storeUint(params.clientDelayBeforeClose, 32);
2145
+ if (params.structVersion >= 1) {
2146
+ b.storeCoins(params.minProxyStake);
2147
+ b.storeCoins(params.minClientStake);
2148
+ }
2149
+ b.storeMaybeRef(null).storeMaybeRef(null).storeMaybeRef(null);
2150
+ return b.endCell();
2151
+ }
2152
+ function proxyInfoValue() {
2153
+ return {
2154
+ serialize: () => {
2155
+ throw new Error("ProxyInfo serialization not needed for read-only usage");
2156
+ },
2157
+ parse: (src) => {
2158
+ src.loadBit();
2159
+ const strlen = src.loadUint(7);
2160
+ const buf = src.loadBuffer(strlen);
2161
+ return { addr: buf.toString("utf-8") };
2162
+ }
2163
+ };
2164
+ }
2165
+ var CocoonRoot = class {
2166
+ constructor(client, address) {
2167
+ this.client = client;
2168
+ this.address = address;
2169
+ }
2170
+ /**
2171
+ * Get all parameters by reading the raw contract state and parsing the Cell.
2172
+ * Returns null if the contract is not initialized.
2173
+ */
2174
+ async getAllParams() {
2175
+ const state = await this.client.getContractState(this.address);
2176
+ if (!state.data) {
2177
+ return null;
2178
+ }
2179
+ const cell = import_ton2.Cell.fromBoc(Buffer.from(state.data))[0];
2180
+ const cs = cell.beginParse();
2181
+ const ownerAddress = cs.loadAddress();
2182
+ const version = cs.loadUint(32);
2183
+ const data = cs.loadRef().beginParse();
2184
+ if (data.loadBit()) data.loadRef();
2185
+ const proxiesDict = data.loadDict(
2186
+ import_ton2.Dictionary.Keys.Uint(32),
2187
+ proxyInfoValue()
2188
+ );
2189
+ const lastProxySeqno = data.loadUint(32);
2190
+ if (data.loadBit()) data.loadRef();
2191
+ if (data.loadBit()) data.loadRef();
2192
+ const pcs = cs.loadRef().beginParse();
2193
+ const structVersion = pcs.loadUint(8);
2194
+ const paramsVersion = pcs.loadUint(32);
2195
+ const uniqueId = pcs.loadUint(32);
2196
+ const isTest = pcs.loadBit();
2197
+ const pricePerToken = pcs.loadCoins();
2198
+ const workerFeePerToken = pcs.loadCoins();
2199
+ let promptTokensPriceMultiplier = 1e4;
2200
+ if (structVersion >= 3) {
2201
+ promptTokensPriceMultiplier = pcs.loadUint(32);
2202
+ }
2203
+ let cachedTokensPriceMultiplier = 1e4;
2204
+ if (structVersion >= 2) {
2205
+ cachedTokensPriceMultiplier = pcs.loadUint(32);
2206
+ }
2207
+ let completionTokensPriceMultiplier = 1e4;
2208
+ if (structVersion >= 3) {
2209
+ completionTokensPriceMultiplier = pcs.loadUint(32);
2210
+ }
2211
+ let reasoningTokensPriceMultiplier = 1e4;
2212
+ if (structVersion >= 2) {
2213
+ reasoningTokensPriceMultiplier = pcs.loadUint(32);
2214
+ }
2215
+ const proxyDelayBeforeClose = pcs.loadUint(32);
2216
+ const clientDelayBeforeClose = pcs.loadUint(32);
2217
+ let minProxyStake = 1000000000n;
2218
+ let minClientStake = 1000000000n;
2219
+ if (structVersion >= 1) {
2220
+ minProxyStake = pcs.loadCoins();
2221
+ minClientStake = pcs.loadCoins();
2222
+ }
2223
+ const proxyScCode = pcs.loadMaybeRef();
2224
+ const workerScCode = pcs.loadMaybeRef();
2225
+ const clientScCode = pcs.loadMaybeRef();
2226
+ const registeredProxies = [];
2227
+ if (proxiesDict) {
2228
+ for (const [seqno, info] of proxiesDict) {
2229
+ registeredProxies.push({ seqno, address: info.addr });
2230
+ }
2231
+ }
2232
+ return {
2233
+ ownerAddress,
2234
+ registeredProxies,
2235
+ lastProxySeqno,
2236
+ version,
2237
+ params: {
2238
+ structVersion,
2239
+ paramsVersion,
2240
+ uniqueId,
2241
+ isTest,
2242
+ pricePerToken,
2243
+ workerFeePerToken,
2244
+ promptTokensPriceMultiplier,
2245
+ cachedTokensPriceMultiplier,
2246
+ completionTokensPriceMultiplier,
2247
+ reasoningTokensPriceMultiplier,
2248
+ proxyDelayBeforeClose,
2249
+ clientDelayBeforeClose,
2250
+ minProxyStake,
2251
+ minClientStake
2252
+ },
2253
+ codes: {
2254
+ proxyScCode,
2255
+ workerScCode,
2256
+ clientScCode
2257
+ }
2258
+ };
2259
+ }
2260
+ };
2261
+
2262
+ // src/ton/discovery.ts
2263
+ var ROOT_CONTRACTS = {
2264
+ mainnet: "EQCns7bYSp0igFvS1wpb5wsZjCKCV19MD5AVzI4EyxsnU73k",
2265
+ testnet: "EQBT4hy4vMEZ9uxSCuhw_gBKh9_AwmHXLe7Wo0O4Vh-4kRjJ"
2266
+ };
2267
+ var ProxyDiscovery = class {
2268
+ tonClient;
2269
+ rootAddress;
2270
+ cachedParams = null;
2271
+ cacheTime = 0;
2272
+ cacheTtl = 6e4;
2273
+ // 1 minute
2274
+ constructor(options) {
2275
+ const endpoint = options.tonApiEndpoint ?? (options.network === "mainnet" ? "https://toncenter.com/api/v2/jsonRPC" : "https://testnet.toncenter.com/api/v2/jsonRPC");
2276
+ this.tonClient = new import_ton3.TonClient({ endpoint });
2277
+ const addr = options.rootContractAddress ?? ROOT_CONTRACTS[options.network];
2278
+ if (!addr) {
2279
+ throw new Error(`No root contract address for network: ${options.network}`);
2280
+ }
2281
+ this.rootAddress = import_ton3.Address.parse(addr);
2282
+ }
2283
+ /**
2284
+ * Get all registered proxies from the root contract.
2285
+ */
2286
+ async getProxies() {
2287
+ const params = await this.getRootParams();
2288
+ return params.registeredProxies;
2289
+ }
2290
+ /**
2291
+ * Get a random proxy for connection.
2292
+ */
2293
+ async getRandomProxy() {
2294
+ const proxies = await this.getProxies();
2295
+ if (proxies.length === 0) {
2296
+ throw new Error("No proxies available in the network");
2297
+ }
2298
+ const idx = Math.floor(Math.random() * proxies.length);
2299
+ return proxies[idx];
2300
+ }
2301
+ /**
2302
+ * Get root contract parameters (cached).
2303
+ */
2304
+ async getRootParams() {
2305
+ const now = Date.now();
2306
+ if (this.cachedParams && now - this.cacheTime < this.cacheTtl) {
2307
+ return this.cachedParams;
2308
+ }
2309
+ const root = new CocoonRoot(this.tonClient, this.rootAddress);
2310
+ const result = await root.getAllParams();
2311
+ if (!result) {
2312
+ throw new Error("Root contract is not initialized");
2313
+ }
2314
+ this.cachedParams = result;
2315
+ this.cacheTime = now;
2316
+ return this.cachedParams;
2317
+ }
2318
+ };
2319
+
2320
+ // src/ton/contracts/client-contract.ts
2321
+ var import_ton4 = require("@ton/ton");
2322
+ var OPCODES = {
2323
+ ownerClientRegister: 3294601019,
2324
+ ownerClientChangeSecretHash: 2838851636,
2325
+ extClientTopUp: 4050839234
2326
+ };
2327
+ var CocoonClientContract = class _CocoonClientContract {
2328
+ constructor(address) {
2329
+ this.address = address;
2330
+ }
2331
+ /**
2332
+ * Create a registration message body for the proxy.
2333
+ * This is sent to the client smart contract to register with a proxy.
2334
+ */
2335
+ static createRegisterBody(nonce, sendExcessesTo, queryId = 0n) {
2336
+ const nonceU64 = BigInt.asUintN(64, nonce);
2337
+ const queryIdU64 = BigInt.asUintN(64, queryId);
2338
+ return (0, import_ton4.beginCell)().storeUint(OPCODES.ownerClientRegister, 32).storeUint(queryIdU64, 64).storeUint(nonceU64, 64).storeAddress(sendExcessesTo).endCell();
2339
+ }
2340
+ /**
2341
+ * Create a message body to change the secret hash.
2342
+ */
2343
+ static createChangeSecretHashBody(secretHash, sendExcessesTo, queryId = 0n) {
2344
+ const queryIdU64 = BigInt.asUintN(64, queryId);
2345
+ return (0, import_ton4.beginCell)().storeUint(OPCODES.ownerClientChangeSecretHash, 32).storeUint(queryIdU64, 64).storeBuffer(secretHash, 32).storeAddress(sendExcessesTo).endCell();
2346
+ }
2347
+ /**
2348
+ * Create a top-up message body.
2349
+ */
2350
+ static createTopUpBody(coins, sendExcessesTo, queryId = 0n) {
2351
+ const queryIdU64 = BigInt.asUintN(64, queryId);
2352
+ return (0, import_ton4.beginCell)().storeUint(OPCODES.extClientTopUp, 32).storeUint(queryIdU64, 64).storeCoins(coins).storeAddress(sendExcessesTo).endCell();
2353
+ }
2354
+ /**
2355
+ * Build client_sc data cell for deployment.
2356
+ * Mirrors ClientContract::init_data_cell in upstream C++ code.
2357
+ */
2358
+ static createDeployDataCell(ownerAddress, proxyScAddress, proxyPublicKey, minClientStake, clientParamsCell) {
2359
+ if (proxyPublicKey.length !== 32) {
2360
+ throw new Error(`proxyPublicKey must be 32 bytes, got ${proxyPublicKey.length}`);
2361
+ }
2362
+ const configDataRef = (0, import_ton4.beginCell)().storeAddress(ownerAddress).storeAddress(proxyScAddress).storeBuffer(proxyPublicKey, 32).endCell();
2363
+ return (0, import_ton4.beginCell)().storeUint(0, 2).storeCoins(0n).storeCoins(minClientStake).storeInt(0n, 64).storeInt(0, 32).storeBuffer(Buffer.alloc(32), 32).storeRef(configDataRef).storeRef(clientParamsCell).endCell();
2364
+ }
2365
+ static createStateInit(clientCode, dataCell) {
2366
+ return {
2367
+ code: clientCode,
2368
+ data: dataCell
2369
+ };
2370
+ }
2371
+ /**
2372
+ * Send a registration transaction to the client contract.
2373
+ */
2374
+ async register(sender, ownerAddress, nonce, amount = (0, import_ton4.toNano)("1"), init) {
2375
+ const body = _CocoonClientContract.createRegisterBody(nonce, ownerAddress);
2376
+ const message = {
2377
+ to: this.address,
2378
+ value: amount,
2379
+ body
2380
+ };
2381
+ if (init) {
2382
+ message.init = init;
2383
+ }
2384
+ await sender.send(message);
2385
+ }
2386
+ /**
2387
+ * Top up the client contract balance.
2388
+ */
2389
+ async topUp(sender, ownerAddress, amount) {
2390
+ const body = _CocoonClientContract.createTopUpBody(amount, ownerAddress);
2391
+ await sender.send({
2392
+ to: this.address,
2393
+ value: amount + (0, import_ton4.toNano)("0.7"),
2394
+ body
2395
+ });
2396
+ }
2397
+ /**
2398
+ * Change the secret hash for short auth.
2399
+ */
2400
+ async changeSecretHash(sender, ownerAddress, secretHash, amount = (0, import_ton4.toNano)("0.7")) {
2401
+ const body = _CocoonClientContract.createChangeSecretHashBody(secretHash, ownerAddress);
2402
+ await sender.send({
2403
+ to: this.address,
2404
+ value: amount,
2405
+ body
2406
+ });
2407
+ }
2408
+ };
2409
+
2410
+ // src/client.ts
2411
+ var import_node_crypto5 = __toESM(require("crypto"), 1);
2412
+ var import_ton5 = require("@ton/ton");
2413
+ var import_core = require("@ton/core");
2414
+ var Cocoon = class {
2415
+ chat;
2416
+ models;
2417
+ mnemonicWallet;
2418
+ discovery;
2419
+ session = null;
2420
+ connecting = null;
2421
+ options;
2422
+ constructor(options) {
2423
+ if (options.tlsCert && !options.tlsKey || !options.tlsCert && options.tlsKey) {
2424
+ throw new ConnectionError(
2425
+ "Both tlsCert and tlsKey must be provided together for mTLS"
2426
+ );
2427
+ }
2428
+ if (options.attestationProvider && (options.tlsCert || options.tlsKey)) {
2429
+ throw new ConnectionError(
2430
+ "Use either attestationProvider or tlsCert/tlsKey, not both"
2431
+ );
2432
+ }
2433
+ this.options = {
2434
+ network: "mainnet",
2435
+ timeout: 12e4,
2436
+ useTls: true,
2437
+ autoRegisterOnLongAuth: true,
2438
+ longAuthRegisterAmountTon: "1",
2439
+ ...options
2440
+ };
2441
+ this.mnemonicWallet = new MnemonicWallet(options.wallet, this.options.network, {
2442
+ tonEndpoint: this.options.tonEndpoint,
2443
+ tonV4Endpoint: this.options.tonV4Endpoint
2444
+ });
2445
+ if (!options.proxyUrl) {
2446
+ this.discovery = new ProxyDiscovery({ network: this.options.network });
2447
+ } else {
2448
+ this.discovery = null;
2449
+ }
2450
+ const getSession = () => this.ensureSession();
2451
+ this.chat = { completions: new Completions(getSession) };
2452
+ this.models = new Models(getSession);
2453
+ }
2454
+ /**
2455
+ * Explicitly connect to a proxy. Called lazily on first API call if not called manually.
2456
+ */
2457
+ async connect() {
2458
+ await this.ensureSession();
2459
+ }
2460
+ /**
2461
+ * Disconnect from the proxy.
2462
+ */
2463
+ async disconnect() {
2464
+ if (this.session) {
2465
+ await this.session.disconnect();
2466
+ this.session = null;
2467
+ }
2468
+ this.connecting = null;
2469
+ }
2470
+ async ensureSession() {
2471
+ if (this.session?.connected) {
2472
+ return this.session;
2473
+ }
2474
+ if (this.connecting) {
2475
+ return this.connecting;
2476
+ }
2477
+ this.connecting = this.createSession();
2478
+ try {
2479
+ this.session = await this.connecting;
2480
+ return this.session;
2481
+ } finally {
2482
+ this.connecting = null;
2483
+ }
2484
+ }
2485
+ async createSession() {
2486
+ await this.mnemonicWallet.init();
2487
+ let host;
2488
+ let port;
2489
+ if (this.options.proxyUrl) {
2490
+ const parts = this.options.proxyUrl.split(":");
2491
+ host = parts.slice(0, -1).join(":");
2492
+ port = parseInt(parts[parts.length - 1], 10);
2493
+ if (!host || isNaN(port)) {
2494
+ throw new ConnectionError(`Invalid proxyUrl: ${this.options.proxyUrl}`);
2495
+ }
2496
+ } else if (this.discovery) {
2497
+ const proxy = await this.discovery.getRandomProxy();
2498
+ const entries = proxy.address.trim().split(/\s+/);
2499
+ const clientEntry = entries.length > 1 ? entries[1] : entries[0];
2500
+ const lastColon = clientEntry.lastIndexOf(":");
2501
+ host = clientEntry.substring(0, lastColon);
2502
+ port = parseInt(clientEntry.substring(lastColon + 1), 10);
2503
+ if (!host || isNaN(port)) {
2504
+ throw new ConnectionError(`Invalid proxy address from discovery: ${proxy.address}`);
2505
+ }
2506
+ } else {
2507
+ throw new ConnectionError("No proxy URL or discovery available");
2508
+ }
2509
+ let tlsCert = this.options.tlsCert;
2510
+ let tlsKey = this.options.tlsKey;
2511
+ if (this.options.attestationProvider) {
2512
+ const context = {
2513
+ host,
2514
+ port,
2515
+ network: this.options.network
2516
+ };
2517
+ const credentials = await this.options.attestationProvider.getClientTlsCredentials(context);
2518
+ tlsCert = credentials.cert;
2519
+ tlsKey = credentials.key;
2520
+ }
2521
+ if (tlsCert && !tlsKey || !tlsCert && tlsKey) {
2522
+ throw new ConnectionError("Attestation provider returned incomplete TLS credentials");
2523
+ }
2524
+ const secretString = this.options.secretString ?? import_node_crypto5.default.randomBytes(32).toString("hex");
2525
+ const onLongAuthRequired = this.options.autoRegisterOnLongAuth ? async (context) => {
2526
+ try {
2527
+ const sender = await this.mnemonicWallet.createSender();
2528
+ const clientScAddress = import_ton5.Address.parse(context.clientScAddress);
2529
+ const clientContract = new CocoonClientContract(clientScAddress);
2530
+ const v4Client = this.mnemonicWallet.getTonClient4();
2531
+ const last = await v4Client.getLastBlock();
2532
+ const account = await v4Client.getAccount(last.last.seqno, clientScAddress);
2533
+ let init;
2534
+ if (account.account.state.type !== "active") {
2535
+ const discovery = this.discovery ?? new ProxyDiscovery({
2536
+ network: this.options.network,
2537
+ tonApiEndpoint: this.options.tonEndpoint
2538
+ });
2539
+ const rootParams = await discovery.getRootParams();
2540
+ const clientCode = rootParams.codes.clientScCode;
2541
+ if (!clientCode) {
2542
+ throw new Error(
2543
+ "Root contract does not contain client_sc_code; cannot deploy client contract"
2544
+ );
2545
+ }
2546
+ const clientParamsCell = buildClientParamsCell(rootParams.params);
2547
+ const dataCell = CocoonClientContract.createDeployDataCell(
2548
+ this.mnemonicWallet.address,
2549
+ import_ton5.Address.parse(context.proxyParams.proxyScAddress),
2550
+ context.proxyParams.proxyPublicKey,
2551
+ rootParams.params.minClientStake,
2552
+ clientParamsCell
2553
+ );
2554
+ init = CocoonClientContract.createStateInit(clientCode, dataCell);
2555
+ const expectedAddress = (0, import_core.contractAddress)(0, init);
2556
+ if (!expectedAddress.equals(clientScAddress)) {
2557
+ throw new Error(
2558
+ `Computed client_sc mismatch: expected ${context.clientScAddress}, got ${expectedAddress.toString({ bounceable: true, testOnly: this.options.network === "testnet" })}`
2559
+ );
2560
+ }
2561
+ }
2562
+ await clientContract.register(
2563
+ sender,
2564
+ this.mnemonicWallet.address,
2565
+ context.nonce,
2566
+ (0, import_ton5.toNano)(this.options.longAuthRegisterAmountTon),
2567
+ init
2568
+ );
2569
+ } catch (err) {
2570
+ throw new ConnectionError(
2571
+ `Auto long-auth registration failed: ${err instanceof Error ? err.message : String(err)}. Configure tonEndpoint / wallet balance or provide a valid SECRET`,
2572
+ err instanceof Error ? err : void 0
2573
+ );
2574
+ }
2575
+ } : void 0;
2576
+ const sessionOptions = {
2577
+ host,
2578
+ port,
2579
+ useTls: this.options.useTls,
2580
+ timeout: this.options.timeout,
2581
+ ownerAddress: this.mnemonicWallet.addressString,
2582
+ secretString,
2583
+ tlsCert,
2584
+ tlsKey,
2585
+ onLongAuthRequired
2586
+ };
2587
+ const session = new CocoonSession(sessionOptions);
2588
+ await session.connect();
2589
+ session.on("close", () => {
2590
+ if (this.session === session) {
2591
+ this.session = null;
2592
+ }
2593
+ });
2594
+ return session;
2595
+ }
2596
+ };
2597
+
2598
+ // src/core/protocol/attestation.ts
2599
+ var import_promises = require("fs/promises");
2600
+ var StaticAttestationProvider = class {
2601
+ constructor(credentials) {
2602
+ this.credentials = credentials;
2603
+ }
2604
+ async getClientTlsCredentials() {
2605
+ return this.credentials;
2606
+ }
2607
+ };
2608
+ var FileAttestationProvider = class {
2609
+ constructor(certPath, keyPath) {
2610
+ this.certPath = certPath;
2611
+ this.keyPath = keyPath;
2612
+ }
2613
+ async getClientTlsCredentials() {
2614
+ const [cert, key] = await Promise.all([
2615
+ (0, import_promises.readFile)(this.certPath),
2616
+ (0, import_promises.readFile)(this.keyPath)
2617
+ ]);
2618
+ return { cert, key };
2619
+ }
2620
+ };
2621
+ // Annotate the CommonJS export names for ESM import in node:
2622
+ 0 && (module.exports = {
2623
+ APIError,
2624
+ AuthenticationError,
2625
+ Cocoon,
2626
+ CocoonError,
2627
+ ConnectionError,
2628
+ FileAttestationProvider,
2629
+ ProtocolError,
2630
+ StaticAttestationProvider,
2631
+ Stream,
2632
+ TimeoutError
2633
+ });
2634
+ //# sourceMappingURL=index.cjs.map