@4mica/cli 0.2.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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/index.js +628 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +68 -0
  6. package/snapshot.json +100 -0
  7. package/templates/agent-buyer-node/.env.example +16 -0
  8. package/templates/agent-buyer-node/README.md +26 -0
  9. package/templates/agent-buyer-node/_gitignore +6 -0
  10. package/templates/agent-buyer-node/package.json +19 -0
  11. package/templates/agent-buyer-node/src/brain.ts +133 -0
  12. package/templates/agent-buyer-node/src/critic.ts +195 -0
  13. package/templates/agent-buyer-node/src/pay.ts +54 -0
  14. package/templates/agent-buyer-node/tsconfig.json +21 -0
  15. package/templates/agent-seller-express/.env.example +16 -0
  16. package/templates/agent-seller-express/README.md +26 -0
  17. package/templates/agent-seller-express/_gitignore +6 -0
  18. package/templates/agent-seller-express/package.json +22 -0
  19. package/templates/agent-seller-express/src/brain.ts +121 -0
  20. package/templates/agent-seller-express/src/comedian.ts +153 -0
  21. package/templates/agent-seller-express/tsconfig.json +21 -0
  22. package/templates/buyer-express/.env.example +12 -0
  23. package/templates/buyer-express/README.md +26 -0
  24. package/templates/buyer-express/_gitignore +6 -0
  25. package/templates/buyer-express/package.json +18 -0
  26. package/templates/buyer-express/src/buyer.ts +82 -0
  27. package/templates/buyer-express/tsconfig.json +21 -0
  28. package/templates/buyer-hono/.env.example +12 -0
  29. package/templates/buyer-hono/README.md +26 -0
  30. package/templates/buyer-hono/_gitignore +6 -0
  31. package/templates/buyer-hono/package.json +18 -0
  32. package/templates/buyer-hono/src/buyer.ts +82 -0
  33. package/templates/buyer-hono/tsconfig.json +21 -0
  34. package/templates/buyer-next/.env.example +12 -0
  35. package/templates/buyer-next/README.md +26 -0
  36. package/templates/buyer-next/_gitignore +6 -0
  37. package/templates/buyer-next/package.json +18 -0
  38. package/templates/buyer-next/src/buyer.ts +82 -0
  39. package/templates/buyer-next/tsconfig.json +21 -0
  40. package/templates/seller-express/.env.example +12 -0
  41. package/templates/seller-express/README.md +26 -0
  42. package/templates/seller-express/_gitignore +6 -0
  43. package/templates/seller-express/package.json +22 -0
  44. package/templates/seller-express/src/server.ts +64 -0
  45. package/templates/seller-express/tsconfig.json +21 -0
  46. package/templates/seller-hono/.env.example +12 -0
  47. package/templates/seller-hono/README.md +26 -0
  48. package/templates/seller-hono/_gitignore +6 -0
  49. package/templates/seller-hono/package.json +22 -0
  50. package/templates/seller-hono/src/server.ts +69 -0
  51. package/templates/seller-hono/tsconfig.json +21 -0
  52. package/templates/seller-next/.env.example +12 -0
  53. package/templates/seller-next/README.md +26 -0
  54. package/templates/seller-next/_gitignore +6 -0
  55. package/templates/seller-next/app/api/protected/route.ts +27 -0
  56. package/templates/seller-next/app/api/session/route.ts +8 -0
  57. package/templates/seller-next/app/globals.css +556 -0
  58. package/templates/seller-next/app/icon.svg +5 -0
  59. package/templates/seller-next/app/layout.tsx +22 -0
  60. package/templates/seller-next/app/page.tsx +189 -0
  61. package/templates/seller-next/next.config.mjs +3 -0
  62. package/templates/seller-next/package.json +25 -0
  63. package/templates/seller-next/public/logo-light.svg +19 -0
  64. package/templates/seller-next/public/logo.svg +19 -0
  65. package/templates/seller-next/scripts/serve.mjs +46 -0
  66. package/templates/seller-next/tsconfig.json +30 -0
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "description": "__DESCRIPTION__",
6
+ "scripts": {
7
+ "start": "tsx src/buyer.ts"
8
+ },
9
+ "dependencies": {
10
+ "@4mica/sdk": "workspace:*",
11
+ "@4mica/sdk-node": "workspace:*"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "catalog:node24",
15
+ "tsx": "catalog:",
16
+ "typescript": "catalog:typescript-latest"
17
+ }
18
+ }
@@ -0,0 +1,82 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ function resolveSellerUrl() {
6
+ if (process.env.SELLER_URL) return process.env.SELLER_URL;
7
+ try {
8
+ const base = readFileSync(
9
+ join(tmpdir(), "__TMPFILE__"),
10
+ "utf8",
11
+ ).trim();
12
+ if (base) return `${base}/premium`;
13
+ } catch {
14
+ return "http://localhost:__PORT__/premium";
15
+ }
16
+ return "http://localhost:__PORT__/premium";
17
+ }
18
+
19
+ const SELLER_URL = resolveSellerUrl();
20
+
21
+ type X402PaymentRequired = {
22
+ x402Version: number;
23
+ accepts: Array<{
24
+ scheme: string;
25
+ network: string;
26
+ asset: string;
27
+ amount: string;
28
+ payTo: string;
29
+ extra?: { tabEndpoint?: string };
30
+ }>;
31
+ };
32
+
33
+ function demoPaymentHeader(
34
+ requirement: X402PaymentRequired["accepts"][number],
35
+ ) {
36
+ const envelope = {
37
+ x402Version: 1,
38
+ scheme: requirement.scheme,
39
+ network: requirement.network,
40
+ payload: {
41
+ claims: {
42
+ version: "v1",
43
+ user_address: "0x2222222222222222222222222222222222222222",
44
+ recipient_address: requirement.payTo,
45
+ req_id: "0x1",
46
+ amount: `0x${BigInt(requirement.amount).toString(16)}`,
47
+ asset_address: requirement.asset,
48
+ timestamp: Math.floor(Date.now() / 1000),
49
+ },
50
+ signature: "0xdemoSignature",
51
+ scheme: "eip712",
52
+ },
53
+ };
54
+ return Buffer.from(JSON.stringify(envelope)).toString("base64");
55
+ }
56
+
57
+ async function main() {
58
+ const first = await fetch(SELLER_URL);
59
+ console.log(`[buyer] GET ${SELLER_URL} → ${first.status}`);
60
+ if (first.status !== 402) {
61
+ console.log("[buyer] unexpected: not paywalled?", await first.text());
62
+ return;
63
+ }
64
+ const required = (await first.json()) as X402PaymentRequired;
65
+ const requirement = required.accepts[0];
66
+ console.log("[buyer] payment required:", requirement);
67
+
68
+ const header = demoPaymentHeader(requirement);
69
+
70
+ const paid = await fetch(SELLER_URL, { headers: { "X-PAYMENT": header } });
71
+ console.log(`[buyer] GET ${SELLER_URL} (paid) → ${paid.status}`);
72
+ console.log(
73
+ "[buyer] X-PAYMENT-RESPONSE:",
74
+ paid.headers.get("X-PAYMENT-RESPONSE"),
75
+ );
76
+ console.log("[buyer] body:", await paid.json());
77
+ }
78
+
79
+ main().catch((err) => {
80
+ console.error("[buyer] failed:", err);
81
+ process.exitCode = 1;
82
+ });
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": [
7
+ "ES2022",
8
+ "DOM"
9
+ ],
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "noEmit": true,
14
+ "types": [
15
+ "node"
16
+ ]
17
+ },
18
+ "include": [
19
+ "src"
20
+ ]
21
+ }
@@ -0,0 +1,12 @@
1
+ # 4Mica runs in DEMO MODE with zero config (mock verifier / demo payment).
2
+ # To go live, swap the mock verifier for createClient() from @4mica/sdk-node
3
+ # and fill these in. Docs: https://4mica.io/docs
4
+
5
+ # 4MICA_WALLET_PRIVATE_KEY=0x...
6
+ # 4MICA_NETWORK=base-sepolia
7
+ # 4MICA_RPC_URL=
8
+ # 4MICA_ETHEREUM_HTTP_RPC_URL=
9
+ # 4MICA_CONTRACT_ADDRESS=
10
+ # 4MICA_ADMIN_API_KEY=
11
+ # 4MICA_BEARER_TOKEN=
12
+ # 4MICA_AUTH_URL=
@@ -0,0 +1,26 @@
1
+ # __PROJECT_NAME__
2
+
3
+ __DESCRIPTION__
4
+
5
+ Generated with `4mica init` — part of the [4Mica](https://4mica.io) x402 payment network.
6
+
7
+ ## Run (demo mode — no config)
8
+
9
+ ```bash
10
+ npm install
11
+ npm run start
12
+ ```
13
+
14
+ Demo mode uses a mock verifier and a locally-built payment header, so it runs
15
+ with **zero credentials**. Start the matching seller first, then run this to pay for the gated route.
16
+
17
+ ## Go live
18
+
19
+ Copy `.env.example` → `.env`, fill in your `4MICA_*` credentials, and swap the
20
+ mock verifier for `createClient()` from `@4mica/sdk-node`.
21
+
22
+ ## Manage agents & transactions
23
+
24
+ ```bash
25
+ 4mica dashboard
26
+ ```
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .env.local
5
+ .next
6
+ *.log
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "description": "__DESCRIPTION__",
6
+ "scripts": {
7
+ "start": "tsx src/buyer.ts"
8
+ },
9
+ "dependencies": {
10
+ "@4mica/sdk": "workspace:*",
11
+ "@4mica/sdk-node": "workspace:*"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "catalog:node24",
15
+ "tsx": "catalog:",
16
+ "typescript": "catalog:typescript-latest"
17
+ }
18
+ }
@@ -0,0 +1,82 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ function resolveSellerUrl() {
6
+ if (process.env.SELLER_URL) return process.env.SELLER_URL;
7
+ try {
8
+ const base = readFileSync(
9
+ join(tmpdir(), "__TMPFILE__"),
10
+ "utf8",
11
+ ).trim();
12
+ if (base) return `${base}/premium`;
13
+ } catch {
14
+ return "http://localhost:__PORT__/premium";
15
+ }
16
+ return "http://localhost:__PORT__/premium";
17
+ }
18
+
19
+ const SELLER_URL = resolveSellerUrl();
20
+
21
+ type X402PaymentRequired = {
22
+ x402Version: number;
23
+ accepts: Array<{
24
+ scheme: string;
25
+ network: string;
26
+ asset: string;
27
+ amount: string;
28
+ payTo: string;
29
+ extra?: { tabEndpoint?: string };
30
+ }>;
31
+ };
32
+
33
+ function demoPaymentHeader(
34
+ requirement: X402PaymentRequired["accepts"][number],
35
+ ) {
36
+ const envelope = {
37
+ x402Version: 1,
38
+ scheme: requirement.scheme,
39
+ network: requirement.network,
40
+ payload: {
41
+ claims: {
42
+ version: "v1",
43
+ user_address: "0x2222222222222222222222222222222222222222",
44
+ recipient_address: requirement.payTo,
45
+ req_id: "0x1",
46
+ amount: `0x${BigInt(requirement.amount).toString(16)}`,
47
+ asset_address: requirement.asset,
48
+ timestamp: Math.floor(Date.now() / 1000),
49
+ },
50
+ signature: "0xdemoSignature",
51
+ scheme: "eip712",
52
+ },
53
+ };
54
+ return Buffer.from(JSON.stringify(envelope)).toString("base64");
55
+ }
56
+
57
+ async function main() {
58
+ const first = await fetch(SELLER_URL);
59
+ console.log(`[buyer] GET ${SELLER_URL} → ${first.status}`);
60
+ if (first.status !== 402) {
61
+ console.log("[buyer] unexpected: not paywalled?", await first.text());
62
+ return;
63
+ }
64
+ const required = (await first.json()) as X402PaymentRequired;
65
+ const requirement = required.accepts[0];
66
+ console.log("[buyer] payment required:", requirement);
67
+
68
+ const header = demoPaymentHeader(requirement);
69
+
70
+ const paid = await fetch(SELLER_URL, { headers: { "X-PAYMENT": header } });
71
+ console.log(`[buyer] GET ${SELLER_URL} (paid) → ${paid.status}`);
72
+ console.log(
73
+ "[buyer] X-PAYMENT-RESPONSE:",
74
+ paid.headers.get("X-PAYMENT-RESPONSE"),
75
+ );
76
+ console.log("[buyer] body:", await paid.json());
77
+ }
78
+
79
+ main().catch((err) => {
80
+ console.error("[buyer] failed:", err);
81
+ process.exitCode = 1;
82
+ });
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": [
7
+ "ES2022",
8
+ "DOM"
9
+ ],
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "noEmit": true,
14
+ "types": [
15
+ "node"
16
+ ]
17
+ },
18
+ "include": [
19
+ "src"
20
+ ]
21
+ }
@@ -0,0 +1,12 @@
1
+ # 4Mica runs in DEMO MODE with zero config (mock verifier / demo payment).
2
+ # To go live, swap the mock verifier for createClient() from @4mica/sdk-node
3
+ # and fill these in. Docs: https://4mica.io/docs
4
+
5
+ # 4MICA_WALLET_PRIVATE_KEY=0x...
6
+ # 4MICA_NETWORK=base-sepolia
7
+ # 4MICA_RPC_URL=
8
+ # 4MICA_ETHEREUM_HTTP_RPC_URL=
9
+ # 4MICA_CONTRACT_ADDRESS=
10
+ # 4MICA_ADMIN_API_KEY=
11
+ # 4MICA_BEARER_TOKEN=
12
+ # 4MICA_AUTH_URL=
@@ -0,0 +1,26 @@
1
+ # __PROJECT_NAME__
2
+
3
+ __DESCRIPTION__
4
+
5
+ Generated with `4mica init` — part of the [4Mica](https://4mica.io) x402 payment network.
6
+
7
+ ## Run (demo mode — no config)
8
+
9
+ ```bash
10
+ npm install
11
+ npm run start
12
+ ```
13
+
14
+ Demo mode uses a mock verifier and a locally-built payment header, so it runs
15
+ with **zero credentials**. Start the matching seller first, then run this to pay for the gated route.
16
+
17
+ ## Go live
18
+
19
+ Copy `.env.example` → `.env`, fill in your `4MICA_*` credentials, and swap the
20
+ mock verifier for `createClient()` from `@4mica/sdk-node`.
21
+
22
+ ## Manage agents & transactions
23
+
24
+ ```bash
25
+ 4mica dashboard
26
+ ```
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .env.local
5
+ .next
6
+ *.log
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "description": "__DESCRIPTION__",
6
+ "scripts": {
7
+ "start": "tsx src/buyer.ts"
8
+ },
9
+ "dependencies": {
10
+ "@4mica/sdk": "workspace:*",
11
+ "@4mica/sdk-node": "workspace:*"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "catalog:node24",
15
+ "tsx": "catalog:",
16
+ "typescript": "catalog:typescript-latest"
17
+ }
18
+ }
@@ -0,0 +1,82 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ function resolveSellerUrl() {
6
+ if (process.env.SELLER_URL) return process.env.SELLER_URL;
7
+ try {
8
+ const base = readFileSync(
9
+ join(tmpdir(), "__TMPFILE__"),
10
+ "utf8",
11
+ ).trim();
12
+ if (base) return `${base}/api/protected`;
13
+ } catch {
14
+ return "http://localhost:__PORT__/api/protected";
15
+ }
16
+ return "http://localhost:__PORT__/api/protected";
17
+ }
18
+
19
+ const SELLER_URL = resolveSellerUrl();
20
+
21
+ type X402PaymentRequired = {
22
+ x402Version: number;
23
+ accepts: Array<{
24
+ scheme: string;
25
+ network: string;
26
+ asset: string;
27
+ amount: string;
28
+ payTo: string;
29
+ extra?: { tabEndpoint?: string };
30
+ }>;
31
+ };
32
+
33
+ function demoPaymentHeader(
34
+ requirement: X402PaymentRequired["accepts"][number],
35
+ ) {
36
+ const envelope = {
37
+ x402Version: 1,
38
+ scheme: requirement.scheme,
39
+ network: requirement.network,
40
+ payload: {
41
+ claims: {
42
+ version: "v1",
43
+ user_address: "0x2222222222222222222222222222222222222222",
44
+ recipient_address: requirement.payTo,
45
+ req_id: "0x1",
46
+ amount: `0x${BigInt(requirement.amount).toString(16)}`,
47
+ asset_address: requirement.asset,
48
+ timestamp: Math.floor(Date.now() / 1000),
49
+ },
50
+ signature: "0xdemoSignature",
51
+ scheme: "eip712",
52
+ },
53
+ };
54
+ return Buffer.from(JSON.stringify(envelope)).toString("base64");
55
+ }
56
+
57
+ async function main() {
58
+ const first = await fetch(SELLER_URL);
59
+ console.log(`[buyer] GET ${SELLER_URL} → ${first.status}`);
60
+ if (first.status !== 402) {
61
+ console.log("[buyer] unexpected: not paywalled?", await first.text());
62
+ return;
63
+ }
64
+ const required = (await first.json()) as X402PaymentRequired;
65
+ const requirement = required.accepts[0];
66
+ console.log("[buyer] payment required:", requirement);
67
+
68
+ const header = demoPaymentHeader(requirement);
69
+
70
+ const paid = await fetch(SELLER_URL, { headers: { "X-PAYMENT": header } });
71
+ console.log(`[buyer] GET ${SELLER_URL} (paid) → ${paid.status}`);
72
+ console.log(
73
+ "[buyer] X-PAYMENT-RESPONSE:",
74
+ paid.headers.get("X-PAYMENT-RESPONSE"),
75
+ );
76
+ console.log("[buyer] body:", await paid.json());
77
+ }
78
+
79
+ main().catch((err) => {
80
+ console.error("[buyer] failed:", err);
81
+ process.exitCode = 1;
82
+ });
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": [
7
+ "ES2022",
8
+ "DOM"
9
+ ],
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "noEmit": true,
14
+ "types": [
15
+ "node"
16
+ ]
17
+ },
18
+ "include": [
19
+ "src"
20
+ ]
21
+ }
@@ -0,0 +1,12 @@
1
+ # 4Mica runs in DEMO MODE with zero config (mock verifier / demo payment).
2
+ # To go live, swap the mock verifier for createClient() from @4mica/sdk-node
3
+ # and fill these in. Docs: https://4mica.io/docs
4
+
5
+ # 4MICA_WALLET_PRIVATE_KEY=0x...
6
+ # 4MICA_NETWORK=base-sepolia
7
+ # 4MICA_RPC_URL=
8
+ # 4MICA_ETHEREUM_HTTP_RPC_URL=
9
+ # 4MICA_CONTRACT_ADDRESS=
10
+ # 4MICA_ADMIN_API_KEY=
11
+ # 4MICA_BEARER_TOKEN=
12
+ # 4MICA_AUTH_URL=
@@ -0,0 +1,26 @@
1
+ # __PROJECT_NAME__
2
+
3
+ __DESCRIPTION__
4
+
5
+ Generated with `4mica init` — part of the [4Mica](https://4mica.io) x402 payment network.
6
+
7
+ ## Run (demo mode — no config)
8
+
9
+ ```bash
10
+ npm install
11
+ npm run dev
12
+ ```
13
+
14
+ Demo mode uses a mock verifier and a locally-built payment header, so it runs
15
+ with **zero credentials**. Start this, then run a matching buyer to exercise the 402 → 200 handshake.
16
+
17
+ ## Go live
18
+
19
+ Copy `.env.example` → `.env`, fill in your `4MICA_*` credentials, and swap the
20
+ mock verifier for `createClient()` from `@4mica/sdk-node`.
21
+
22
+ ## Manage agents & transactions
23
+
24
+ ```bash
25
+ 4mica dashboard
26
+ ```
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .env.local
5
+ .next
6
+ *.log
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "description": "__DESCRIPTION__",
6
+ "scripts": {
7
+ "dev": "tsx watch src/server.ts",
8
+ "start": "tsx src/server.ts"
9
+ },
10
+ "dependencies": {
11
+ "@4mica/sdk": "workspace:*",
12
+ "@4mica/sdk-express": "workspace:*",
13
+ "@4mica/sdk-node": "workspace:*",
14
+ "express": "catalog:"
15
+ },
16
+ "devDependencies": {
17
+ "@types/express": "catalog:",
18
+ "@types/node": "catalog:node24",
19
+ "tsx": "catalog:",
20
+ "typescript": "catalog:typescript-latest"
21
+ }
22
+ }
@@ -0,0 +1,64 @@
1
+ import { rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import type { PaywallConfig, PaywallVerifier } from "@4mica/sdk-express";
5
+ import { paywall } from "@4mica/sdk-express";
6
+ import express from "express";
7
+
8
+ const PORT_FILE = join(tmpdir(), "__TMPFILE__");
9
+
10
+ const verifier: PaywallVerifier = {
11
+ async issueGuarantee(payload) {
12
+ console.log("[seller] verifying payment payload:", payload);
13
+ return { claims: "0xdemoClaims", signature: "0xdemoSignature" };
14
+ },
15
+ };
16
+
17
+ export const PAYWALL_CONFIG: PaywallConfig = {
18
+ payTo: "0x1111111111111111111111111111111111111111",
19
+ asset: "0x0000000000000000000000000000000000000000",
20
+ network: "base-sepolia",
21
+ amount: "1000",
22
+ tabEndpoint: "http://localhost:__PORT__/session",
23
+ description: "Premium market data feed",
24
+ };
25
+
26
+ const app = express();
27
+ app.use(express.json());
28
+
29
+ app.post("/session", (_req, res) => {
30
+ res.json({
31
+ userAddress: "0x2222222222222222222222222222222222222222",
32
+ nextReqId: "0x1",
33
+ });
34
+ });
35
+
36
+ app.get("/premium", paywall(verifier, PAYWALL_CONFIG), (_req, res) => {
37
+ res.json({ ok: true, data: "🔓 premium market data unlocked" });
38
+ });
39
+
40
+ function listen(port: number, attemptsLeft = 20) {
41
+ const server = app.listen(port);
42
+ server.once("listening", () => {
43
+ const url = `http://localhost:${port}`;
44
+ PAYWALL_CONFIG.tabEndpoint = `${url}/session`;
45
+ writeFileSync(PORT_FILE, url);
46
+ console.log(`[seller-express] listening on ${url}`);
47
+ console.log(` GET /premium is paywalled — run the buyer to pay for it.`);
48
+ });
49
+ server.once("error", (err: NodeJS.ErrnoException) => {
50
+ if (err.code === "EADDRINUSE" && attemptsLeft > 0) {
51
+ console.log(`[seller-express] port ${port} in use, trying ${port + 1}…`);
52
+ listen(port + 1, attemptsLeft - 1);
53
+ } else {
54
+ throw err;
55
+ }
56
+ });
57
+ }
58
+
59
+ const cleanup = () => rmSync(PORT_FILE, { force: true });
60
+ process.on("exit", cleanup);
61
+ process.on("SIGINT", () => process.exit(0));
62
+ process.on("SIGTERM", () => process.exit(0));
63
+
64
+ listen(Number(process.env.PORT ?? __PORT__));
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": [
7
+ "ES2022",
8
+ "DOM"
9
+ ],
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "noEmit": true,
14
+ "types": [
15
+ "node"
16
+ ]
17
+ },
18
+ "include": [
19
+ "src"
20
+ ]
21
+ }
@@ -0,0 +1,12 @@
1
+ # 4Mica runs in DEMO MODE with zero config (mock verifier / demo payment).
2
+ # To go live, swap the mock verifier for createClient() from @4mica/sdk-node
3
+ # and fill these in. Docs: https://4mica.io/docs
4
+
5
+ # 4MICA_WALLET_PRIVATE_KEY=0x...
6
+ # 4MICA_NETWORK=base-sepolia
7
+ # 4MICA_RPC_URL=
8
+ # 4MICA_ETHEREUM_HTTP_RPC_URL=
9
+ # 4MICA_CONTRACT_ADDRESS=
10
+ # 4MICA_ADMIN_API_KEY=
11
+ # 4MICA_BEARER_TOKEN=
12
+ # 4MICA_AUTH_URL=
@@ -0,0 +1,26 @@
1
+ # __PROJECT_NAME__
2
+
3
+ __DESCRIPTION__
4
+
5
+ Generated with `4mica init` — part of the [4Mica](https://4mica.io) x402 payment network.
6
+
7
+ ## Run (demo mode — no config)
8
+
9
+ ```bash
10
+ npm install
11
+ npm run dev
12
+ ```
13
+
14
+ Demo mode uses a mock verifier and a locally-built payment header, so it runs
15
+ with **zero credentials**. Start this, then run a matching buyer to exercise the 402 → 200 handshake.
16
+
17
+ ## Go live
18
+
19
+ Copy `.env.example` → `.env`, fill in your `4MICA_*` credentials, and swap the
20
+ mock verifier for `createClient()` from `@4mica/sdk-node`.
21
+
22
+ ## Manage agents & transactions
23
+
24
+ ```bash
25
+ 4mica dashboard
26
+ ```
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .env.local
5
+ .next
6
+ *.log