moltspay 0.8.1 → 0.8.3
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 +141 -52
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +160 -65
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +237 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +256 -167
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +14 -5
- package/dist/server/index.d.ts +14 -5
- package/dist/server/index.js +151 -112
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +149 -113
- 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,107 @@ 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
|
+
var USDC_ADDRESSES = {
|
|
361
|
+
"eip155:8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
362
|
+
// Base mainnet
|
|
363
|
+
"eip155:84532": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
364
|
+
// Base Sepolia
|
|
365
|
+
};
|
|
366
|
+
var USDC_DOMAIN = {
|
|
367
|
+
name: "USD Coin",
|
|
368
|
+
version: "2"
|
|
369
|
+
};
|
|
370
|
+
function loadEnvFiles() {
|
|
371
|
+
try {
|
|
372
|
+
const dotenv = __require("dotenv");
|
|
373
|
+
const envPaths = [
|
|
374
|
+
path.join(process.cwd(), ".env"),
|
|
375
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
376
|
+
];
|
|
377
|
+
for (const envPath of envPaths) {
|
|
378
|
+
if (existsSync2(envPath)) {
|
|
379
|
+
dotenv.config({ path: envPath });
|
|
380
|
+
console.log(`[MoltsPay] Loaded config from ${envPath}`);
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} catch {
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function getCDPConfig() {
|
|
388
|
+
loadEnvFiles();
|
|
389
|
+
return {
|
|
390
|
+
useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
|
|
391
|
+
apiKeyId: process.env.CDP_API_KEY_ID,
|
|
392
|
+
apiKeySecret: process.env.CDP_API_KEY_SECRET
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
async function getCDPAuthHeaders(method, urlPath, body) {
|
|
396
|
+
const config = getCDPConfig();
|
|
397
|
+
if (!config.apiKeyId || !config.apiKeySecret) {
|
|
398
|
+
throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
402
|
+
const headers = await getAuthHeaders({
|
|
403
|
+
apiKeyId: config.apiKeyId,
|
|
404
|
+
apiKeySecret: config.apiKeySecret,
|
|
405
|
+
requestMethod: method,
|
|
406
|
+
requestHost: "api.cdp.coinbase.com",
|
|
407
|
+
requestPath: urlPath,
|
|
408
|
+
requestBody: body
|
|
409
|
+
});
|
|
410
|
+
return headers;
|
|
411
|
+
} catch (err) {
|
|
412
|
+
console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
|
|
413
|
+
throw err;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
352
416
|
var MoltsPayServer = class {
|
|
353
417
|
manifest;
|
|
354
418
|
skills = /* @__PURE__ */ new Map();
|
|
355
419
|
options;
|
|
420
|
+
cdpConfig;
|
|
356
421
|
facilitatorUrl;
|
|
422
|
+
networkId;
|
|
357
423
|
constructor(servicesPath, options = {}) {
|
|
424
|
+
this.cdpConfig = getCDPConfig();
|
|
358
425
|
const content = readFileSync2(servicesPath, "utf-8");
|
|
359
426
|
this.manifest = JSON.parse(content);
|
|
360
427
|
this.options = {
|
|
361
428
|
port: options.port || 3e3,
|
|
362
429
|
host: options.host || "0.0.0.0"
|
|
363
430
|
};
|
|
364
|
-
this.
|
|
431
|
+
if (this.cdpConfig.useMainnet) {
|
|
432
|
+
if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
|
|
433
|
+
console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
|
|
434
|
+
console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
|
|
435
|
+
}
|
|
436
|
+
this.facilitatorUrl = FACILITATOR_MAINNET;
|
|
437
|
+
this.networkId = "eip155:8453";
|
|
438
|
+
} else {
|
|
439
|
+
this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
|
|
440
|
+
this.networkId = "eip155:84532";
|
|
441
|
+
}
|
|
442
|
+
const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
443
|
+
const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
|
|
365
444
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
366
445
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
367
446
|
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
368
|
-
console.log(`[MoltsPay]
|
|
447
|
+
console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
|
|
448
|
+
console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
|
|
449
|
+
if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
|
|
450
|
+
console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
|
|
451
|
+
}
|
|
369
452
|
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
370
453
|
}
|
|
371
454
|
/**
|
|
@@ -426,7 +509,6 @@ var MoltsPayServer = class {
|
|
|
426
509
|
* GET /services - List available services
|
|
427
510
|
*/
|
|
428
511
|
handleGetServices(res) {
|
|
429
|
-
const chain = getChain(this.manifest.provider.chain);
|
|
430
512
|
const services = this.manifest.services.map((s) => ({
|
|
431
513
|
id: s.id,
|
|
432
514
|
name: s.name,
|
|
@@ -442,16 +524,15 @@ var MoltsPayServer = class {
|
|
|
442
524
|
services,
|
|
443
525
|
x402: {
|
|
444
526
|
version: X402_VERSION2,
|
|
445
|
-
network:
|
|
527
|
+
network: this.networkId,
|
|
446
528
|
schemes: ["exact"],
|
|
447
|
-
facilitator: this.
|
|
529
|
+
facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
|
|
530
|
+
mainnet: this.cdpConfig.useMainnet
|
|
448
531
|
}
|
|
449
532
|
});
|
|
450
533
|
}
|
|
451
534
|
/**
|
|
452
535
|
* POST /execute - Execute service with x402 payment
|
|
453
|
-
* Body: { service: string, params: object }
|
|
454
|
-
* Header: X-Payment (optional - if missing, returns 402)
|
|
455
536
|
*/
|
|
456
537
|
async handleExecute(body, paymentHeader, res) {
|
|
457
538
|
const { service, params } = body;
|
|
@@ -526,17 +607,8 @@ var MoltsPayServer = class {
|
|
|
526
607
|
* Return 402 with x402 payment requirements
|
|
527
608
|
*/
|
|
528
609
|
sendPaymentRequired(config, res) {
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
const requirements = [{
|
|
532
|
-
scheme: "exact",
|
|
533
|
-
network: `eip155:${chain.chainId}`,
|
|
534
|
-
maxAmountRequired: amountInUnits,
|
|
535
|
-
resource: this.manifest.provider.wallet,
|
|
536
|
-
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
537
|
-
// Include facilitator info for client
|
|
538
|
-
extra: JSON.stringify({ facilitator: this.facilitatorUrl })
|
|
539
|
-
}];
|
|
610
|
+
const requirements = [this.buildPaymentRequirements(config)];
|
|
611
|
+
requirements[0].description = `${config.name} - $${config.price} ${config.currency}`;
|
|
540
612
|
const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
|
|
541
613
|
res.writeHead(402, {
|
|
542
614
|
"Content-Type": "application/json",
|
|
@@ -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,57 @@ 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
|
-
*
|
|
639
|
+
* Build complete payment requirements for facilitator
|
|
640
|
+
*/
|
|
641
|
+
buildPaymentRequirements(config) {
|
|
642
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
643
|
+
const usdcAddress = USDC_ADDRESSES[this.networkId];
|
|
644
|
+
return {
|
|
645
|
+
scheme: "exact",
|
|
646
|
+
network: this.networkId,
|
|
647
|
+
maxAmountRequired: amountInUnits,
|
|
648
|
+
amount: amountInUnits,
|
|
649
|
+
asset: usdcAddress,
|
|
650
|
+
payTo: this.manifest.provider.wallet,
|
|
651
|
+
maxTimeoutSeconds: 300,
|
|
652
|
+
extra: USDC_DOMAIN
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Verify payment with facilitator (testnet or CDP)
|
|
570
657
|
*/
|
|
571
658
|
async verifyWithFacilitator(payment, config) {
|
|
572
659
|
try {
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
network: `eip155:${chain.chainId}`,
|
|
578
|
-
maxAmountRequired: amountInUnits,
|
|
579
|
-
resource: this.manifest.provider.wallet
|
|
660
|
+
const requirements = this.buildPaymentRequirements(config);
|
|
661
|
+
const requestBody = {
|
|
662
|
+
paymentPayload: payment,
|
|
663
|
+
paymentRequirements: requirements
|
|
580
664
|
};
|
|
665
|
+
console.log("[MoltsPay] Verify request:", JSON.stringify(requestBody, null, 2));
|
|
666
|
+
let headers = { "Content-Type": "application/json" };
|
|
667
|
+
if (this.cdpConfig.useMainnet) {
|
|
668
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
669
|
+
"POST",
|
|
670
|
+
"/platform/v2/x402/verify",
|
|
671
|
+
requestBody
|
|
672
|
+
);
|
|
673
|
+
headers = { ...headers, ...authHeaders };
|
|
674
|
+
}
|
|
581
675
|
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
582
676
|
method: "POST",
|
|
583
|
-
headers
|
|
584
|
-
body: JSON.stringify(
|
|
585
|
-
paymentPayload: payment,
|
|
586
|
-
paymentRequirements: requirements
|
|
587
|
-
})
|
|
677
|
+
headers,
|
|
678
|
+
body: JSON.stringify(requestBody)
|
|
588
679
|
});
|
|
589
680
|
const result = await response.json();
|
|
681
|
+
console.log("[MoltsPay] Verify response:", JSON.stringify(result, null, 2));
|
|
590
682
|
if (!response.ok || !result.isValid) {
|
|
591
|
-
return { valid: false, error: result.invalidReason || "Verification failed" };
|
|
683
|
+
return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
|
|
592
684
|
}
|
|
593
685
|
return { valid: true };
|
|
594
686
|
} catch (err) {
|
|
@@ -599,25 +691,28 @@ var MoltsPayServer = class {
|
|
|
599
691
|
* Settle payment with facilitator (execute on-chain transfer)
|
|
600
692
|
*/
|
|
601
693
|
async settleWithFacilitator(payment, config) {
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
network: `eip155:${chain.chainId}`,
|
|
607
|
-
maxAmountRequired: amountInUnits,
|
|
608
|
-
resource: this.manifest.provider.wallet
|
|
694
|
+
const requirements = this.buildPaymentRequirements(config);
|
|
695
|
+
const requestBody = {
|
|
696
|
+
paymentPayload: payment,
|
|
697
|
+
paymentRequirements: requirements
|
|
609
698
|
};
|
|
699
|
+
let headers = { "Content-Type": "application/json" };
|
|
700
|
+
if (this.cdpConfig.useMainnet) {
|
|
701
|
+
const authHeaders = await getCDPAuthHeaders(
|
|
702
|
+
"POST",
|
|
703
|
+
"/platform/v2/x402/settle",
|
|
704
|
+
requestBody
|
|
705
|
+
);
|
|
706
|
+
headers = { ...headers, ...authHeaders };
|
|
707
|
+
}
|
|
610
708
|
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
611
709
|
method: "POST",
|
|
612
|
-
headers
|
|
613
|
-
body: JSON.stringify(
|
|
614
|
-
paymentPayload: payment,
|
|
615
|
-
paymentRequirements: requirements
|
|
616
|
-
})
|
|
710
|
+
headers,
|
|
711
|
+
body: JSON.stringify(requestBody)
|
|
617
712
|
});
|
|
618
713
|
const result = await response.json();
|
|
619
|
-
if (!response.ok) {
|
|
620
|
-
throw new Error(result.error || "Settlement failed");
|
|
714
|
+
if (!response.ok || !result.success) {
|
|
715
|
+
throw new Error(result.error || result.errorReason || "Settlement failed");
|
|
621
716
|
}
|
|
622
717
|
return {
|
|
623
718
|
transaction: result.transaction,
|
|
@@ -651,9 +746,9 @@ var MoltsPayServer = class {
|
|
|
651
746
|
// src/cli/index.ts
|
|
652
747
|
import * as readline from "readline";
|
|
653
748
|
var program = new Command();
|
|
654
|
-
var DEFAULT_CONFIG_DIR =
|
|
655
|
-
var PID_FILE =
|
|
656
|
-
if (!
|
|
749
|
+
var DEFAULT_CONFIG_DIR = join3(homedir2(), ".moltspay");
|
|
750
|
+
var PID_FILE = join3(DEFAULT_CONFIG_DIR, "server.pid");
|
|
751
|
+
if (!existsSync3(DEFAULT_CONFIG_DIR)) {
|
|
657
752
|
mkdirSync2(DEFAULT_CONFIG_DIR, { recursive: true });
|
|
658
753
|
}
|
|
659
754
|
function prompt(question) {
|
|
@@ -671,7 +766,7 @@ function prompt(question) {
|
|
|
671
766
|
program.name("moltspay").description("MoltsPay - Payment infrastructure for AI Agents").version("1.0.0");
|
|
672
767
|
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
768
|
console.log("\n\u{1F510} MoltsPay Client Setup\n");
|
|
674
|
-
if (
|
|
769
|
+
if (existsSync3(join3(options.configDir, "wallet.json"))) {
|
|
675
770
|
console.log('\u26A0\uFE0F Already initialized. Use "moltspay config" to update settings.');
|
|
676
771
|
console.log(` Config dir: ${options.configDir}`);
|
|
677
772
|
return;
|
|
@@ -698,7 +793,7 @@ program.command("init").description("Initialize MoltsPay client (create wallet,
|
|
|
698
793
|
console.log(`
|
|
699
794
|
\u{1F4C1} Config saved to: ${result.configDir}`);
|
|
700
795
|
console.log(`
|
|
701
|
-
\u26A0\uFE0F IMPORTANT: Back up ${
|
|
796
|
+
\u26A0\uFE0F IMPORTANT: Back up ${join3(result.configDir, "wallet.json")}`);
|
|
702
797
|
console.log(` This file contains your private key!
|
|
703
798
|
`);
|
|
704
799
|
console.log(`\u{1F4B0} Fund your wallet with USDC on ${chain} to start using services.
|
|
@@ -806,7 +901,7 @@ program.command("services <url>").description("List services from a provider").o
|
|
|
806
901
|
});
|
|
807
902
|
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
903
|
const manifestPath = resolve(manifest);
|
|
809
|
-
if (!
|
|
904
|
+
if (!existsSync3(manifestPath)) {
|
|
810
905
|
console.error(`\u274C Manifest not found: ${manifestPath}`);
|
|
811
906
|
process.exit(1);
|
|
812
907
|
}
|
|
@@ -870,7 +965,7 @@ program.command("start <manifest>").description("Start MoltsPay server from serv
|
|
|
870
965
|
server.listen(port);
|
|
871
966
|
const cleanup = () => {
|
|
872
967
|
try {
|
|
873
|
-
if (
|
|
968
|
+
if (existsSync3(PID_FILE)) {
|
|
874
969
|
unlinkSync(PID_FILE);
|
|
875
970
|
}
|
|
876
971
|
} catch {
|
|
@@ -893,7 +988,7 @@ program.command("start <manifest>").description("Start MoltsPay server from serv
|
|
|
893
988
|
}
|
|
894
989
|
});
|
|
895
990
|
program.command("stop").description("Stop the running MoltsPay server").action(async () => {
|
|
896
|
-
if (!
|
|
991
|
+
if (!existsSync3(PID_FILE)) {
|
|
897
992
|
console.log("\u274C No running server found (no PID file)");
|
|
898
993
|
process.exit(1);
|
|
899
994
|
}
|
|
@@ -923,7 +1018,7 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
|
|
|
923
1018
|
process.kill(pid, "SIGKILL");
|
|
924
1019
|
} catch {
|
|
925
1020
|
}
|
|
926
|
-
if (
|
|
1021
|
+
if (existsSync3(PID_FILE)) {
|
|
927
1022
|
unlinkSync(PID_FILE);
|
|
928
1023
|
}
|
|
929
1024
|
console.log("\u2705 Server stopped\n");
|
|
@@ -954,7 +1049,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
|
|
|
954
1049
|
params.image_url = imagePath;
|
|
955
1050
|
} else {
|
|
956
1051
|
const filePath = resolve(imagePath);
|
|
957
|
-
if (!
|
|
1052
|
+
if (!existsSync3(filePath)) {
|
|
958
1053
|
console.error(`\u274C Image file not found: ${filePath}`);
|
|
959
1054
|
process.exit(1);
|
|
960
1055
|
}
|