clinch-core 0.2.0 → 0.4.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.d.ts CHANGED
@@ -51,6 +51,7 @@ export declare class ClinchCore extends EventEmitter {
51
51
  private isSandboxMode;
52
52
  private sandboxModelContext;
53
53
  private sandboxMaxTurns;
54
+ get activeNegotiationId(): string | null;
54
55
  constructor(config?: ClinchConfig);
55
56
  private setStatus;
56
57
  initialize(cachedToken?: string): Promise<void>;
@@ -77,3 +78,23 @@ export declare class ClinchCore extends EventEmitter {
77
78
  private solvePoW;
78
79
  private hasLeadingZeroBits;
79
80
  }
81
+ export interface SellerRecord {
82
+ agent_id: string;
83
+ endpoint: string;
84
+ supported_modes: string[];
85
+ categories: string[];
86
+ capabilities: string[];
87
+ display_name?: string;
88
+ }
89
+ export declare class ClinchSeller extends EventEmitter {
90
+ private config;
91
+ private cachedRegistryUrl;
92
+ private identityPrivKey;
93
+ identityPubKey: string;
94
+ constructor(config?: ClinchConfig & {
95
+ privateKeyHex?: string;
96
+ });
97
+ private resolveRegistry;
98
+ registerEndpoint(record: SellerRecord): Promise<any>;
99
+ verifyBuyerSignature(payload: any, signatureHex: string, buyerPubKeyHex: string): boolean;
100
+ }
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.ClinchCore = void 0;
39
+ exports.ClinchSeller = exports.ClinchCore = void 0;
40
40
  const events_1 = require("events");
41
41
  const tweetnacl_1 = __importDefault(require("tweetnacl"));
42
42
  const js_sha256_1 = require("js-sha256");
@@ -70,6 +70,10 @@ class ClinchCore extends events_1.EventEmitter {
70
70
  isSandboxMode = false;
71
71
  sandboxModelContext = null;
72
72
  sandboxMaxTurns = 6;
73
+ // Legacy support for single-tenant applications checking the last ID
74
+ get activeNegotiationId() {
75
+ return Array.from(this.activeSessions.keys()).pop() || null;
76
+ }
73
77
  constructor(config = {}) {
74
78
  super();
75
79
  this.config = { timeoutMs: 5000, ...config };
@@ -501,7 +505,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
501
505
  }
502
506
  parseAddress(address) {
503
507
  if (!address.includes('.'))
504
- throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/A.amazon.anp)");
508
+ throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/C.amazon.anp)");
505
509
  const firstDotIdx = address.indexOf('.');
506
510
  const mode = address.substring(0, firstDotIdx);
507
511
  const domain = address.substring(firstDotIdx + 1).toLowerCase();
@@ -537,3 +541,68 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
537
541
  }
538
542
  }
539
543
  exports.ClinchCore = ClinchCore;
544
+ class ClinchSeller extends events_1.EventEmitter {
545
+ config;
546
+ cachedRegistryUrl = null;
547
+ identityPrivKey;
548
+ identityPubKey;
549
+ constructor(config = {}) {
550
+ super();
551
+ this.config = { timeoutMs: 8000, ...config };
552
+ if (config.privateKeyHex) {
553
+ this.identityPrivKey = fromHex(config.privateKeyHex);
554
+ const kp = tweetnacl_1.default.sign.keyPair.fromSecretKey(this.identityPrivKey);
555
+ this.identityPubKey = toHex(kp.publicKey);
556
+ this.emit('log', `[Seller] Loaded permanent identity. PubKey: ${this.identityPubKey.substring(0, 12)}...`);
557
+ }
558
+ else {
559
+ const kp = tweetnacl_1.default.sign.keyPair();
560
+ this.identityPrivKey = kp.secretKey;
561
+ this.identityPubKey = toHex(kp.publicKey);
562
+ console.warn('[Seller] ⚠️ No privateKeyHex provided — using ephemeral key. Registry will reject updates unless this key is pre-registered.');
563
+ }
564
+ if (this.config.registryUrl) {
565
+ this.cachedRegistryUrl = this.config.registryUrl;
566
+ }
567
+ }
568
+ async resolveRegistry() {
569
+ if (this.cachedRegistryUrl)
570
+ return this.cachedRegistryUrl;
571
+ const res = await fetch(FIREBASE_CONFIG_URL);
572
+ const cfg = JSON.parse(await res.text());
573
+ this.cachedRegistryUrl = cfg.registry_nodes[PROTOCOL_VERSION];
574
+ return this.cachedRegistryUrl;
575
+ }
576
+ async registerEndpoint(record) {
577
+ const registry = await this.resolveRegistry();
578
+ const payload = { ...record, timestamp: Date.now() };
579
+ const msgUint8 = new TextEncoder().encode(JSON.stringify(payload));
580
+ const signature = toHex(tweetnacl_1.default.sign.detached(msgUint8, this.identityPrivKey));
581
+ const res = await fetch(`${registry}/api/sellers/update-endpoint`, {
582
+ method: 'POST',
583
+ headers: { 'Content-Type': 'application/json' },
584
+ body: JSON.stringify({
585
+ payload,
586
+ public_key: this.identityPubKey,
587
+ signature
588
+ })
589
+ });
590
+ if (!res.ok)
591
+ throw new Error(`Endpoint registration failed: ${await res.text()}`);
592
+ const data = await res.json();
593
+ this.emit('log', `[Seller] Registered: ${record.agent_id} → ${record.endpoint}`);
594
+ return data;
595
+ }
596
+ verifyBuyerSignature(payload, signatureHex, buyerPubKeyHex) {
597
+ try {
598
+ const msg = new TextEncoder().encode(JSON.stringify(payload));
599
+ const sig = fromHex(signatureHex);
600
+ const pubKey = fromHex(buyerPubKeyHex);
601
+ return tweetnacl_1.default.sign.detached.verify(msg, sig, pubKey);
602
+ }
603
+ catch {
604
+ return false;
605
+ }
606
+ }
607
+ }
608
+ exports.ClinchSeller = ClinchSeller;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clinch-core",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
 
5
5
  "description": "Clinch Protocol Edge Client",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -100,6 +100,11 @@ export class ClinchCore extends EventEmitter {
100
100
  private sandboxModelContext: any = null;
101
101
  private sandboxMaxTurns = 6;
102
102
 
103
+ // Legacy support for single-tenant applications checking the last ID
104
+ public get activeNegotiationId(): string | null {
105
+ return Array.from(this.activeSessions.keys()).pop() || null;
106
+ }
107
+
103
108
  constructor(config: ClinchConfig = {}) {
104
109
  super();
105
110
  this.config = { timeoutMs: 5000, ...config };
@@ -574,7 +579,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
574
579
  }
575
580
 
576
581
  public parseAddress(address: string): ParsedAddress {
577
- if (!address.includes('.')) throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/A.amazon.anp)");
582
+ if (!address.includes('.')) throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/C.amazon.anp)");
578
583
 
579
584
  const firstDotIdx = address.indexOf('.');
580
585
  const mode = address.substring(0, firstDotIdx);
@@ -608,3 +613,85 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
608
613
  return zeroBits >= bits;
609
614
  }
610
615
  }
616
+
617
+ // ============================================================================
618
+ // THE CLINCH SELLER LIBRARY (Server-Side)
619
+ // ============================================================================
620
+ export interface SellerRecord {
621
+ agent_id: string;
622
+ endpoint: string;
623
+ supported_modes: string[];
624
+ categories: string[];
625
+ capabilities: string[];
626
+ display_name?: string; // Optional legacy support
627
+ }
628
+
629
+ export class ClinchSeller extends EventEmitter {
630
+ private config: ClinchConfig;
631
+ private cachedRegistryUrl: string | null = null;
632
+ private identityPrivKey: Uint8Array;
633
+ public identityPubKey: string;
634
+
635
+ constructor(config: ClinchConfig & { privateKeyHex?: string } = {}) {
636
+ super();
637
+ this.config = { timeoutMs: 8000, ...config };
638
+
639
+ if (config.privateKeyHex) {
640
+ this.identityPrivKey = fromHex(config.privateKeyHex);
641
+ const kp = nacl.sign.keyPair.fromSecretKey(this.identityPrivKey);
642
+ this.identityPubKey = toHex(kp.publicKey);
643
+ this.emit('log', `[Seller] Loaded permanent identity. PubKey: ${this.identityPubKey.substring(0, 12)}...`);
644
+ } else {
645
+ const kp = nacl.sign.keyPair();
646
+ this.identityPrivKey = kp.secretKey;
647
+ this.identityPubKey = toHex(kp.publicKey);
648
+ console.warn('[Seller] ⚠️ No privateKeyHex provided — using ephemeral key. Registry will reject updates unless this key is pre-registered.');
649
+ }
650
+
651
+ if (this.config.registryUrl) {
652
+ this.cachedRegistryUrl = this.config.registryUrl;
653
+ }
654
+ }
655
+
656
+ private async resolveRegistry(): Promise<string> {
657
+ if (this.cachedRegistryUrl) return this.cachedRegistryUrl;
658
+ const res = await fetch(FIREBASE_CONFIG_URL);
659
+ const cfg = JSON.parse(await res.text());
660
+ this.cachedRegistryUrl = cfg.registry_nodes[PROTOCOL_VERSION];
661
+ return this.cachedRegistryUrl!;
662
+ }
663
+
664
+ async registerEndpoint(record: SellerRecord): Promise<any> {
665
+ const registry = await this.resolveRegistry();
666
+
667
+ const payload = { ...record, timestamp: Date.now() };
668
+ const msgUint8 = new TextEncoder().encode(JSON.stringify(payload));
669
+ const signature = toHex(nacl.sign.detached(msgUint8, this.identityPrivKey));
670
+
671
+ const res = await fetch(`${registry}/api/sellers/update-endpoint`, {
672
+ method: 'POST',
673
+ headers: { 'Content-Type': 'application/json' },
674
+ body: JSON.stringify({
675
+ payload,
676
+ public_key: this.identityPubKey,
677
+ signature
678
+ })
679
+ });
680
+
681
+ if (!res.ok) throw new Error(`Endpoint registration failed: ${await res.text()}`);
682
+ const data = await res.json();
683
+ this.emit('log', `[Seller] Registered: ${record.agent_id} → ${record.endpoint}`);
684
+ return data;
685
+ }
686
+
687
+ verifyBuyerSignature(payload: any, signatureHex: string, buyerPubKeyHex: string): boolean {
688
+ try {
689
+ const msg = new TextEncoder().encode(JSON.stringify(payload));
690
+ const sig = fromHex(signatureHex);
691
+ const pubKey = fromHex(buyerPubKeyHex);
692
+ return nacl.sign.detached.verify(msg, sig, pubKey);
693
+ } catch {
694
+ return false;
695
+ }
696
+ }
697
+ }