moltspay 0.8.1 → 0.8.2
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/.env.example +10 -0
- package/dist/cli/index.js +121 -37
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +140 -50
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +217 -133
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +236 -152
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +10 -5
- package/dist/server/index.d.ts +10 -5
- package/dist/server/index.js +131 -97
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +129 -98
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/cli/index.ts
|
|
4
10
|
import { Command } from "commander";
|
|
5
11
|
import { homedir as homedir2 } from "os";
|
|
6
|
-
import { join as
|
|
7
|
-
import { existsSync as
|
|
12
|
+
import { join as join3, dirname, resolve } from "path";
|
|
13
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync3, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
|
|
8
14
|
import { spawn } from "child_process";
|
|
9
15
|
|
|
10
16
|
// src/client/index.ts
|
|
@@ -342,30 +348,97 @@ var MoltsPayClient = class {
|
|
|
342
348
|
};
|
|
343
349
|
|
|
344
350
|
// src/server/index.ts
|
|
345
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
351
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
346
352
|
import { createServer } from "http";
|
|
353
|
+
import * as path from "path";
|
|
347
354
|
var X402_VERSION2 = 2;
|
|
348
355
|
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
349
356
|
var PAYMENT_HEADER2 = "x-payment";
|
|
350
357
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
351
|
-
var
|
|
358
|
+
var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
|
|
359
|
+
var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
|
|
360
|
+
function loadEnvFiles() {
|
|
361
|
+
try {
|
|
362
|
+
const dotenv = __require("dotenv");
|
|
363
|
+
const envPaths = [
|
|
364
|
+
path.join(process.cwd(), ".env"),
|
|
365
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
366
|
+
];
|
|
367
|
+
for (const envPath of envPaths) {
|
|
368
|
+
if (existsSync2(envPath)) {
|
|
369
|
+
dotenv.config({ path: envPath });
|
|
370
|
+
console.log(`[MoltsPay] Loaded config from ${envPath}`);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} catch {
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function getCDPConfig() {
|
|
378
|
+
loadEnvFiles();
|
|
379
|
+
return {
|
|
380
|
+
useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
|
|
381
|
+
apiKeyId: process.env.CDP_API_KEY_ID,
|
|
382
|
+
apiKeySecret: process.env.CDP_API_KEY_SECRET
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
async function getCDPAuthHeaders(method, urlPath, body) {
|
|
386
|
+
const config = getCDPConfig();
|
|
387
|
+
if (!config.apiKeyId || !config.apiKeySecret) {
|
|
388
|
+
throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
392
|
+
const headers = await getAuthHeaders({
|
|
393
|
+
apiKeyId: config.apiKeyId,
|
|
394
|
+
apiKeySecret: config.apiKeySecret,
|
|
395
|
+
requestMethod: method,
|
|
396
|
+
requestHost: "api.cdp.coinbase.com",
|
|
397
|
+
requestPath: urlPath,
|
|
398
|
+
requestBody: body
|
|
399
|
+
});
|
|
400
|
+
return headers;
|
|
401
|
+
} catch (err) {
|
|
402
|
+
console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
|
|
403
|
+
throw err;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
352
406
|
var MoltsPayServer = class {
|
|
353
407
|
manifest;
|
|
354
408
|
skills = /* @__PURE__ */ new Map();
|
|
355
409
|
options;
|
|
410
|
+
cdpConfig;
|
|
356
411
|
facilitatorUrl;
|
|
412
|
+
networkId;
|
|
357
413
|
constructor(servicesPath, options = {}) {
|
|
414
|
+
this.cdpConfig = getCDPConfig();
|
|
358
415
|
const content = readFileSync2(servicesPath, "utf-8");
|
|
359
416
|
this.manifest = JSON.parse(content);
|
|
360
417
|
this.options = {
|
|
361
418
|
port: options.port || 3e3,
|
|
362
419
|
host: options.host || "0.0.0.0"
|
|
363
420
|
};
|
|
364
|
-
this.
|
|
421
|
+
if (this.cdpConfig.useMainnet) {
|
|
422
|
+
if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
|
|
423
|
+
console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
|
|
424
|
+
console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
|
|
425
|
+
}
|
|
426
|
+
this.facilitatorUrl = FACILITATOR_MAINNET;
|
|
427
|
+
this.networkId = "eip155:8453";
|
|
428
|
+
} else {
|
|
429
|
+
this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
|
|
430
|
+
this.networkId = "eip155:84532";
|
|
431
|
+
}
|
|
432
|
+
const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
433
|
+
const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
|
|
365
434
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
366
435
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
367
436
|
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
368
|
-
console.log(`[MoltsPay]
|
|
437
|
+
console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
|
|
438
|
+
console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
|
|
439
|
+
if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
|
|
440
|
+
console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
|
|
441
|
+
}
|
|
369
442
|
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
370
443
|
}
|
|
371
444
|
/**
|
|
@@ -426,7 +499,6 @@ var MoltsPayServer = class {
|
|
|
426
499
|
* GET /services - List available services
|
|
427
500
|
*/
|
|
428
501
|
handleGetServices(res) {
|
|
429
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
430
502
|
const services = this.manifest.services.map((s) => ({
|
|
431
503
|
id: s.id,
|
|
432
504
|
name: s.name,
|
|
@@ -442,16 +514,15 @@ var MoltsPayServer = class {
|
|
|
442
514
|
services,
|
|
443
515
|
x402: {
|
|
444
516
|
version: X402_VERSION2,
|
|
445
|
-
network:
|
|
517
|
+
network: this.networkId,
|
|
446
518
|
schemes: ["exact"],
|
|
447
|
-
facilitator: this.
|
|
519
|
+
facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
|
|
520
|
+
mainnet: this.cdpConfig.useMainnet
|
|
448
521
|
}
|
|
449
522
|
});
|
|
450
523
|
}
|
|
451
524
|
/**
|
|
452
525
|
* POST /execute - Execute service with x402 payment
|
|
453
|
-
* Body: { service: string, params: object }
|
|
454
|
-
* Header: X-Payment (optional - if missing, returns 402)
|
|
455
526
|
*/
|
|
456
527
|
async handleExecute(body, paymentHeader, res) {
|
|
457
528
|
const { service, params } = body;
|
|
@@ -526,16 +597,17 @@ var MoltsPayServer = class {
|
|
|
526
597
|
* Return 402 with x402 payment requirements
|
|
527
598
|
*/
|
|
528
599
|
sendPaymentRequired(config, res) {
|
|
529
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
530
600
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
531
601
|
const requirements = [{
|
|
532
602
|
scheme: "exact",
|
|
533
|
-
network:
|
|
603
|
+
network: this.networkId,
|
|
534
604
|
maxAmountRequired: amountInUnits,
|
|
535
605
|
resource: this.manifest.provider.wallet,
|
|
536
606
|
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
537
|
-
|
|
538
|
-
|
|
607
|
+
extra: JSON.stringify({
|
|
608
|
+
facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
|
|
609
|
+
mainnet: this.cdpConfig.useMainnet
|
|
610
|
+
})
|
|
539
611
|
}];
|
|
540
612
|
const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
|
|
541
613
|
res.writeHead(402, {
|
|
@@ -549,7 +621,7 @@ var MoltsPayServer = class {
|
|
|
549
621
|
}, null, 2));
|
|
550
622
|
}
|
|
551
623
|
/**
|
|
552
|
-
* Basic payment validation
|
|
624
|
+
* Basic payment validation
|
|
553
625
|
*/
|
|
554
626
|
validatePayment(payment, config) {
|
|
555
627
|
if (payment.x402Version !== X402_VERSION2) {
|
|
@@ -558,37 +630,45 @@ var MoltsPayServer = class {
|
|
|
558
630
|
if (payment.scheme !== "exact") {
|
|
559
631
|
return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
|
|
560
632
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
if (payment.network !== expectedNetwork) {
|
|
564
|
-
return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
|
|
633
|
+
if (payment.network !== this.networkId) {
|
|
634
|
+
return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };
|
|
565
635
|
}
|
|
566
636
|
return { valid: true };
|
|
567
637
|
}
|
|
568
638
|
/**
|
|
569
|
-
* Verify payment with facilitator
|
|
639
|
+
* Verify payment with facilitator (testnet or CDP)
|
|
570
640
|
*/
|
|
571
641
|
async verifyWithFacilitator(payment, config) {
|
|
572
642
|
try {
|
|
573
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
574
643
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
575
644
|
const requirements = {
|
|
576
645
|
scheme: "exact",
|
|
577
|
-
network:
|
|
646
|
+
network: this.networkId,
|
|
578
647
|
maxAmountRequired: amountInUnits,
|
|
579
|
-
resource: this.manifest.provider.wallet
|
|
648
|
+
resource: this.manifest.provider.wallet,
|
|
649
|
+
payTo: this.manifest.provider.wallet
|
|
650
|
+
};
|
|
651
|
+
const requestBody = {
|
|
652
|
+
paymentPayload: payment,
|
|
653
|
+
paymentRequirements: requirements
|
|
580
654
|
};
|
|
655
|
+
let headers = { "Content-Type": "application/json" };
|
|
656
|
+
if (this.cdpConfig.useMainnet) {
|
|
657
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
658
|
+
"POST",
|
|
659
|
+
"/platform/v2/x402/verify",
|
|
660
|
+
requestBody
|
|
661
|
+
);
|
|
662
|
+
headers = { ...headers, ...authHeaders };
|
|
663
|
+
}
|
|
581
664
|
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
582
665
|
method: "POST",
|
|
583
|
-
headers
|
|
584
|
-
body: JSON.stringify(
|
|
585
|
-
paymentPayload: payment,
|
|
586
|
-
paymentRequirements: requirements
|
|
587
|
-
})
|
|
666
|
+
headers,
|
|
667
|
+
body: JSON.stringify(requestBody)
|
|
588
668
|
});
|
|
589
669
|
const result = await response.json();
|
|
590
670
|
if (!response.ok || !result.isValid) {
|
|
591
|
-
return { valid: false, error: result.invalidReason || "Verification failed" };
|
|
671
|
+
return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
|
|
592
672
|
}
|
|
593
673
|
return { valid: true };
|
|
594
674
|
} catch (err) {
|
|
@@ -599,25 +679,35 @@ var MoltsPayServer = class {
|
|
|
599
679
|
* Settle payment with facilitator (execute on-chain transfer)
|
|
600
680
|
*/
|
|
601
681
|
async settleWithFacilitator(payment, config) {
|
|
602
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
603
682
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
604
683
|
const requirements = {
|
|
605
684
|
scheme: "exact",
|
|
606
|
-
network:
|
|
685
|
+
network: this.networkId,
|
|
607
686
|
maxAmountRequired: amountInUnits,
|
|
608
|
-
resource: this.manifest.provider.wallet
|
|
687
|
+
resource: this.manifest.provider.wallet,
|
|
688
|
+
payTo: this.manifest.provider.wallet
|
|
609
689
|
};
|
|
690
|
+
const requestBody = {
|
|
691
|
+
paymentPayload: payment,
|
|
692
|
+
paymentRequirements: requirements
|
|
693
|
+
};
|
|
694
|
+
let headers = { "Content-Type": "application/json" };
|
|
695
|
+
if (this.cdpConfig.useMainnet) {
|
|
696
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
697
|
+
"POST",
|
|
698
|
+
"/platform/v2/x402/settle",
|
|
699
|
+
requestBody
|
|
700
|
+
);
|
|
701
|
+
headers = { ...headers, ...authHeaders };
|
|
702
|
+
}
|
|
610
703
|
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
611
704
|
method: "POST",
|
|
612
|
-
headers
|
|
613
|
-
body: JSON.stringify(
|
|
614
|
-
paymentPayload: payment,
|
|
615
|
-
paymentRequirements: requirements
|
|
616
|
-
})
|
|
705
|
+
headers,
|
|
706
|
+
body: JSON.stringify(requestBody)
|
|
617
707
|
});
|
|
618
708
|
const result = await response.json();
|
|
619
|
-
if (!response.ok) {
|
|
620
|
-
throw new Error(result.error || "Settlement failed");
|
|
709
|
+
if (!response.ok || !result.success) {
|
|
710
|
+
throw new Error(result.error || result.errorReason || "Settlement failed");
|
|
621
711
|
}
|
|
622
712
|
return {
|
|
623
713
|
transaction: result.transaction,
|
|
@@ -651,9 +741,9 @@ var MoltsPayServer = class {
|
|
|
651
741
|
// src/cli/index.ts
|
|
652
742
|
import * as readline from "readline";
|
|
653
743
|
var program = new Command();
|
|
654
|
-
var DEFAULT_CONFIG_DIR =
|
|
655
|
-
var PID_FILE =
|
|
656
|
-
if (!
|
|
744
|
+
var DEFAULT_CONFIG_DIR = join3(homedir2(), ".moltspay");
|
|
745
|
+
var PID_FILE = join3(DEFAULT_CONFIG_DIR, "server.pid");
|
|
746
|
+
if (!existsSync3(DEFAULT_CONFIG_DIR)) {
|
|
657
747
|
mkdirSync2(DEFAULT_CONFIG_DIR, { recursive: true });
|
|
658
748
|
}
|
|
659
749
|
function prompt(question) {
|
|
@@ -671,7 +761,7 @@ function prompt(question) {
|
|
|
671
761
|
program.name("moltspay").description("MoltsPay - Payment infrastructure for AI Agents").version("1.0.0");
|
|
672
762
|
program.command("init").description("Initialize MoltsPay client (create wallet, set limits)").option("--chain <chain>", "Blockchain to use", "base").option("--max-per-tx <amount>", "Max amount per transaction").option("--max-per-day <amount>", "Max amount per day").option("--config-dir <dir>", "Config directory", DEFAULT_CONFIG_DIR).action(async (options) => {
|
|
673
763
|
console.log("\n\u{1F510} MoltsPay Client Setup\n");
|
|
674
|
-
if (
|
|
764
|
+
if (existsSync3(join3(options.configDir, "wallet.json"))) {
|
|
675
765
|
console.log('\u26A0\uFE0F Already initialized. Use "moltspay config" to update settings.');
|
|
676
766
|
console.log(` Config dir: ${options.configDir}`);
|
|
677
767
|
return;
|
|
@@ -698,7 +788,7 @@ program.command("init").description("Initialize MoltsPay client (create wallet,
|
|
|
698
788
|
console.log(`
|
|
699
789
|
\u{1F4C1} Config saved to: ${result.configDir}`);
|
|
700
790
|
console.log(`
|
|
701
|
-
\u26A0\uFE0F IMPORTANT: Back up ${
|
|
791
|
+
\u26A0\uFE0F IMPORTANT: Back up ${join3(result.configDir, "wallet.json")}`);
|
|
702
792
|
console.log(` This file contains your private key!
|
|
703
793
|
`);
|
|
704
794
|
console.log(`\u{1F4B0} Fund your wallet with USDC on ${chain} to start using services.
|
|
@@ -806,7 +896,7 @@ program.command("services <url>").description("List services from a provider").o
|
|
|
806
896
|
});
|
|
807
897
|
program.command("start <manifest>").description("Start MoltsPay server from services manifest").option("-p, --port <port>", "Port to listen on", "3000").option("--host <host>", "Host to bind", "0.0.0.0").option("--facilitator <url>", "x402 facilitator URL (default: https://x402.org/facilitator)").action(async (manifest, options) => {
|
|
808
898
|
const manifestPath = resolve(manifest);
|
|
809
|
-
if (!
|
|
899
|
+
if (!existsSync3(manifestPath)) {
|
|
810
900
|
console.error(`\u274C Manifest not found: ${manifestPath}`);
|
|
811
901
|
process.exit(1);
|
|
812
902
|
}
|
|
@@ -870,7 +960,7 @@ program.command("start <manifest>").description("Start MoltsPay server from serv
|
|
|
870
960
|
server.listen(port);
|
|
871
961
|
const cleanup = () => {
|
|
872
962
|
try {
|
|
873
|
-
if (
|
|
963
|
+
if (existsSync3(PID_FILE)) {
|
|
874
964
|
unlinkSync(PID_FILE);
|
|
875
965
|
}
|
|
876
966
|
} catch {
|
|
@@ -893,7 +983,7 @@ program.command("start <manifest>").description("Start MoltsPay server from serv
|
|
|
893
983
|
}
|
|
894
984
|
});
|
|
895
985
|
program.command("stop").description("Stop the running MoltsPay server").action(async () => {
|
|
896
|
-
if (!
|
|
986
|
+
if (!existsSync3(PID_FILE)) {
|
|
897
987
|
console.log("\u274C No running server found (no PID file)");
|
|
898
988
|
process.exit(1);
|
|
899
989
|
}
|
|
@@ -923,7 +1013,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
923
1013
|
process.kill(pid, "SIGKILL");
|
|
924
1014
|
} catch {
|
|
925
1015
|
}
|
|
926
|
-
if (
|
|
1016
|
+
if (existsSync3(PID_FILE)) {
|
|
927
1017
|
unlinkSync(PID_FILE);
|
|
928
1018
|
}
|
|
929
1019
|
console.log("\u2705 Server stopped\n");
|
|
@@ -954,7 +1044,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
954
1044
|
params.image_url = imagePath;
|
|
955
1045
|
} else {
|
|
956
1046
|
const filePath = resolve(imagePath);
|
|
957
|
-
if (!
|
|
1047
|
+
if (!existsSync3(filePath)) {
|
|
958
1048
|
console.error(`\u274C Image file not found: ${filePath}`);
|
|
959
1049
|
process.exit(1);
|
|
960
1050
|
}
|