clinch-core 0.2.0 → 0.3.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
@@ -77,3 +77,22 @@ export declare class ClinchCore extends EventEmitter {
77
77
  private solvePoW;
78
78
  private hasLeadingZeroBits;
79
79
  }
80
+ export interface SellerRecord {
81
+ agent_id: string;
82
+ endpoint: string;
83
+ supported_modes: string[];
84
+ categories: string[];
85
+ capabilities: string[];
86
+ }
87
+ export declare class ClinchSeller extends EventEmitter {
88
+ private config;
89
+ private cachedRegistryUrl;
90
+ private identityPrivKey;
91
+ identityPubKey: string;
92
+ constructor(config?: ClinchConfig & {
93
+ privateKeyHex?: string;
94
+ });
95
+ private resolveRegistry;
96
+ registerEndpoint(record: SellerRecord): Promise<any>;
97
+ verifyBuyerSignature(payload: any, signatureHex: string, buyerPubKeyHex: string): boolean;
98
+ }
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");
@@ -501,7 +501,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
501
501
  }
502
502
  parseAddress(address) {
503
503
  if (!address.includes('.'))
504
- throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/A.amazon.anp)");
504
+ throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/C.amazon.anp)");
505
505
  const firstDotIdx = address.indexOf('.');
506
506
  const mode = address.substring(0, firstDotIdx);
507
507
  const domain = address.substring(firstDotIdx + 1).toLowerCase();
@@ -537,3 +537,68 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
537
537
  }
538
538
  }
539
539
  exports.ClinchCore = ClinchCore;
540
+ class ClinchSeller extends events_1.EventEmitter {
541
+ config;
542
+ cachedRegistryUrl = null;
543
+ identityPrivKey;
544
+ identityPubKey;
545
+ constructor(config = {}) {
546
+ super();
547
+ this.config = { timeoutMs: 8000, ...config };
548
+ if (config.privateKeyHex) {
549
+ this.identityPrivKey = fromHex(config.privateKeyHex);
550
+ const kp = tweetnacl_1.default.sign.keyPair.fromSecretKey(this.identityPrivKey);
551
+ this.identityPubKey = toHex(kp.publicKey);
552
+ this.emit('log', `[Seller] Loaded permanent identity. PubKey: ${this.identityPubKey.substring(0, 12)}...`);
553
+ }
554
+ else {
555
+ const kp = tweetnacl_1.default.sign.keyPair();
556
+ this.identityPrivKey = kp.secretKey;
557
+ this.identityPubKey = toHex(kp.publicKey);
558
+ console.warn('[Seller] ⚠️ No privateKeyHex provided — using ephemeral key. Registry will reject updates unless this key is pre-registered.');
559
+ }
560
+ if (this.config.registryUrl) {
561
+ this.cachedRegistryUrl = this.config.registryUrl;
562
+ }
563
+ }
564
+ async resolveRegistry() {
565
+ if (this.cachedRegistryUrl)
566
+ return this.cachedRegistryUrl;
567
+ const res = await fetch(FIREBASE_CONFIG_URL);
568
+ const cfg = JSON.parse(await res.text());
569
+ this.cachedRegistryUrl = cfg.registry_nodes[PROTOCOL_VERSION];
570
+ return this.cachedRegistryUrl;
571
+ }
572
+ async registerEndpoint(record) {
573
+ const registry = await this.resolveRegistry();
574
+ const payload = { ...record, timestamp: Date.now() };
575
+ const msgUint8 = new TextEncoder().encode(JSON.stringify(payload));
576
+ const signature = toHex(tweetnacl_1.default.sign.detached(msgUint8, this.identityPrivKey));
577
+ const res = await fetch(`${registry}/api/sellers/update-endpoint`, {
578
+ method: 'POST',
579
+ headers: { 'Content-Type': 'application/json' },
580
+ body: JSON.stringify({
581
+ payload,
582
+ public_key: this.identityPubKey,
583
+ signature
584
+ })
585
+ });
586
+ if (!res.ok)
587
+ throw new Error(`Endpoint registration failed: ${await res.text()}`);
588
+ const data = await res.json();
589
+ this.emit('log', `[Seller] Registered: ${record.agent_id} → ${record.endpoint}`);
590
+ return data;
591
+ }
592
+ verifyBuyerSignature(payload, signatureHex, buyerPubKeyHex) {
593
+ try {
594
+ const msg = new TextEncoder().encode(JSON.stringify(payload));
595
+ const sig = fromHex(signatureHex);
596
+ const pubKey = fromHex(buyerPubKeyHex);
597
+ return tweetnacl_1.default.sign.detached.verify(msg, sig, pubKey);
598
+ }
599
+ catch {
600
+ return false;
601
+ }
602
+ }
603
+ }
604
+ 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.3.0",
4
4
 
5
5
  "description": "Clinch Protocol Edge Client",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -574,7 +574,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
574
574
  }
575
575
 
576
576
  public parseAddress(address: string): ParsedAddress {
577
- if (!address.includes('.')) throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/A.amazon.anp)");
577
+ if (!address.includes('.')) throw new Error("Invalid Address Format. Expected MODE.domain (e.g. ANP/C.amazon.anp)");
578
578
 
579
579
  const firstDotIdx = address.indexOf('.');
580
580
  const mode = address.substring(0, firstDotIdx);
@@ -608,3 +608,84 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
608
608
  return zeroBits >= bits;
609
609
  }
610
610
  }
611
+
612
+ // ============================================================================
613
+ // THE CLINCH SELLER LIBRARY (Server-Side)
614
+ // ============================================================================
615
+ export interface SellerRecord {
616
+ agent_id: string;
617
+ endpoint: string;
618
+ supported_modes: string[];
619
+ categories: string[];
620
+ capabilities: string[];
621
+ }
622
+
623
+ export class ClinchSeller extends EventEmitter {
624
+ private config: ClinchConfig;
625
+ private cachedRegistryUrl: string | null = null;
626
+ private identityPrivKey: Uint8Array;
627
+ public identityPubKey: string;
628
+
629
+ constructor(config: ClinchConfig & { privateKeyHex?: string } = {}) {
630
+ super();
631
+ this.config = { timeoutMs: 8000, ...config };
632
+
633
+ if (config.privateKeyHex) {
634
+ this.identityPrivKey = fromHex(config.privateKeyHex);
635
+ const kp = nacl.sign.keyPair.fromSecretKey(this.identityPrivKey);
636
+ this.identityPubKey = toHex(kp.publicKey);
637
+ this.emit('log', `[Seller] Loaded permanent identity. PubKey: ${this.identityPubKey.substring(0, 12)}...`);
638
+ } else {
639
+ const kp = nacl.sign.keyPair();
640
+ this.identityPrivKey = kp.secretKey;
641
+ this.identityPubKey = toHex(kp.publicKey);
642
+ console.warn('[Seller] ⚠️ No privateKeyHex provided — using ephemeral key. Registry will reject updates unless this key is pre-registered.');
643
+ }
644
+
645
+ if (this.config.registryUrl) {
646
+ this.cachedRegistryUrl = this.config.registryUrl;
647
+ }
648
+ }
649
+
650
+ private async resolveRegistry(): Promise<string> {
651
+ if (this.cachedRegistryUrl) return this.cachedRegistryUrl;
652
+ const res = await fetch(FIREBASE_CONFIG_URL);
653
+ const cfg = JSON.parse(await res.text());
654
+ this.cachedRegistryUrl = cfg.registry_nodes[PROTOCOL_VERSION];
655
+ return this.cachedRegistryUrl!;
656
+ }
657
+
658
+ async registerEndpoint(record: SellerRecord): Promise<any> {
659
+ const registry = await this.resolveRegistry();
660
+
661
+ const payload = { ...record, timestamp: Date.now() };
662
+ const msgUint8 = new TextEncoder().encode(JSON.stringify(payload));
663
+ const signature = toHex(nacl.sign.detached(msgUint8, this.identityPrivKey));
664
+
665
+ const res = await fetch(`${registry}/api/sellers/update-endpoint`, {
666
+ method: 'POST',
667
+ headers: { 'Content-Type': 'application/json' },
668
+ body: JSON.stringify({
669
+ payload,
670
+ public_key: this.identityPubKey,
671
+ signature
672
+ })
673
+ });
674
+
675
+ if (!res.ok) throw new Error(`Endpoint registration failed: ${await res.text()}`);
676
+ const data = await res.json();
677
+ this.emit('log', `[Seller] Registered: ${record.agent_id} → ${record.endpoint}`);
678
+ return data;
679
+ }
680
+
681
+ verifyBuyerSignature(payload: any, signatureHex: string, buyerPubKeyHex: string): boolean {
682
+ try {
683
+ const msg = new TextEncoder().encode(JSON.stringify(payload));
684
+ const sig = fromHex(signatureHex);
685
+ const pubKey = fromHex(buyerPubKeyHex);
686
+ return nacl.sign.detached.verify(msg, sig, pubKey);
687
+ } catch {
688
+ return false;
689
+ }
690
+ }
691
+ }