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 +19 -0
- package/dist/index.js +67 -2
- package/package.json +1 -1
- package/src/index.ts +82 -1
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/
|
|
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
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/
|
|
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
|
+
}
|