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 +21 -0
- package/dist/index.js +71 -2
- package/package.json +1 -1
- package/src/index.ts +88 -1
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/
|
|
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
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/
|
|
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
|
+
}
|