arc-builder-kit 0.2.0__py3-none-any.whl
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.
- arc_builder_kit/__init__.py +4 -0
- arc_builder_kit/__main__.py +6 -0
- arc_builder_kit/_paths.py +47 -0
- arc_builder_kit/cli.py +277 -0
- arc_builder_kit/config/arc_testnet.facts.json +31 -0
- arc_builder_kit/doctor.py +936 -0
- arc_builder_kit/examples/agent-commerce-components/components.js +200 -0
- arc_builder_kit/examples/agent-commerce-components/index.html +120 -0
- arc_builder_kit/examples/agent-commerce-flows/flows.js +271 -0
- arc_builder_kit/examples/agent-commerce-flows/index.html +114 -0
- arc_builder_kit/examples/agent-commerce-live/commerce-live.js +190 -0
- arc_builder_kit/examples/agent-commerce-live/index.html +105 -0
- arc_builder_kit/examples/agent-commerce-review-packet/index.html +96 -0
- arc_builder_kit/examples/agent-commerce-review-packet/packet.js +125 -0
- arc_builder_kit/examples/agent-identity-profile-preview/identity.js +126 -0
- arc_builder_kit/examples/agent-identity-profile-preview/index.html +104 -0
- arc_builder_kit/examples/arc-agent-treasury-lab/index.html +152 -0
- arc_builder_kit/examples/arc-agent-treasury-lab/treasury.js +532 -0
- arc_builder_kit/examples/arc-testnet-operator-evidence/evidence.example.json +47 -0
- arc_builder_kit/examples/arc-testnet-wallet-send-gate/index.html +233 -0
- arc_builder_kit/examples/arc-testnet-wallet-send-gate/live-infrastructure-policy.example.json +59 -0
- arc_builder_kit/examples/arc-testnet-wallet-send-gate/wallet-send-gate.js +472 -0
- arc_builder_kit/examples/circle-wallet-integration/index.html +155 -0
- arc_builder_kit/examples/circle-wallet-integration/wallet-lab.js +91 -0
- arc_builder_kit/examples/job-escrow-simulator/index.html +121 -0
- arc_builder_kit/examples/job-escrow-simulator/simulator.js +162 -0
- arc_builder_kit/examples/payment-intent-demo/index.html +132 -0
- arc_builder_kit/examples/payment-intent-playground/index.html +301 -0
- arc_builder_kit/examples/payment-intent-playground/playground.js +835 -0
- arc_builder_kit/examples/payment-intent-receipt-matcher/index.html +157 -0
- arc_builder_kit/examples/payment-intent-receipt-matcher/matcher.js +877 -0
- arc_builder_kit/examples/receipt-verifier-playground/index.html +120 -0
- arc_builder_kit/examples/receipt-verifier-playground/verifier.js +226 -0
- arc_builder_kit/examples/receipt-viewer/index.html +138 -0
- arc_builder_kit/examples/receipt-viewer/receipt-viewer.js +472 -0
- arc_builder_kit/examples/transaction-status-playground/index.html +135 -0
- arc_builder_kit/examples/transaction-status-playground/status.js +518 -0
- arc_builder_kit/examples/x402-local-challenge-server/.env.example +25 -0
- arc_builder_kit/examples/x402-local-challenge-server/README.md +111 -0
- arc_builder_kit/examples/x402-local-challenge-server/server.py +711 -0
- arc_builder_kit/mcp_server.py +463 -0
- arc_builder_kit/release_packet.py +469 -0
- arc_builder_kit/templates/README.md +25 -0
- arc_builder_kit/templates/job-escrow-starter/README.md +25 -0
- arc_builder_kit/templates/job-escrow-starter/index.html +41 -0
- arc_builder_kit/templates/job-escrow-starter/index.js +14 -0
- arc_builder_kit/templates/payment-intent-starter/README.md +25 -0
- arc_builder_kit/templates/payment-intent-starter/index.html +42 -0
- arc_builder_kit/templates/payment-intent-starter/index.js +7 -0
- arc_builder_kit/templates/x402-agent-starter/README.md +29 -0
- arc_builder_kit/templates/x402-agent-starter/server.py +201 -0
- arc_builder_kit/validate_repo.py +2212 -0
- arc_builder_kit-0.2.0.dist-info/METADATA +543 -0
- arc_builder_kit-0.2.0.dist-info/RECORD +58 -0
- arc_builder_kit-0.2.0.dist-info/WHEEL +5 -0
- arc_builder_kit-0.2.0.dist-info/entry_points.txt +3 -0
- arc_builder_kit-0.2.0.dist-info/licenses/LICENSE +21 -0
- arc_builder_kit-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Arc x402 Paid-Agent Starter
|
|
2
|
+
|
|
3
|
+
Minimal local x402-style paid-agent boundary for Arc Testnet. It returns a `402 Payment Required` challenge and accepts a deterministic local demo proof.
|
|
4
|
+
|
|
5
|
+
This is a starter template from the Arc MCP Builder Assistant. No funds move; no wallet or signing is required.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- `server.py` — dependency-free Python HTTP server.
|
|
10
|
+
|
|
11
|
+
## Use
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python3 server.py --port 8091
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
In another terminal:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
curl -s http://127.0.0.1:8091/protected
|
|
21
|
+
python3 server.py --print-manifest
|
|
22
|
+
python3 server.py --print-challenge
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Safety notes
|
|
26
|
+
|
|
27
|
+
- `X402_DEMO_MAINNET_ENABLED` must stay `false`; any `true` value exits safely.
|
|
28
|
+
- Asset is pinned to `USDC` and network to `arc-testnet`.
|
|
29
|
+
- The `local-demo` proof is a deterministic demo switch, not a real payment credential.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Minimal local x402 paid-agent boundary starter for Arc Testnet.
|
|
3
|
+
|
|
4
|
+
Dependency-free. No wallet, signing, or broadcast. The local demo proof is a
|
|
5
|
+
deterministic switch, not a real payment credential.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from urllib.parse import urlparse
|
|
19
|
+
|
|
20
|
+
NETWORK = "arc-testnet"
|
|
21
|
+
ASSET = "USDC"
|
|
22
|
+
DEFAULT_AMOUNT = "0.01"
|
|
23
|
+
DEFAULT_PAY_TO = "0xA11CE00000000000000000000000000000000000"
|
|
24
|
+
MAX_AMOUNT_LEN = 24
|
|
25
|
+
MAX_PAYMENT_HEADER = 4096
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _load_config() -> dict[str, str]:
|
|
29
|
+
mainnet = os.environ.get("X402_DEMO_MAINNET_ENABLED", "false").lower()
|
|
30
|
+
if mainnet in ("1", "true", "yes"):
|
|
31
|
+
print("error: mainnet is disabled in this starter", file=sys.stderr)
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
amount = os.environ.get("X402_DEMO_AMOUNT", DEFAULT_AMOUNT)
|
|
34
|
+
pay_to = os.environ.get("X402_DEMO_PAY_TO", DEFAULT_PAY_TO)
|
|
35
|
+
if not _valid_amount(amount):
|
|
36
|
+
raise ValueError(f"invalid amount: {amount}")
|
|
37
|
+
if not _valid_address(pay_to):
|
|
38
|
+
raise ValueError(f"invalid pay-to address: {pay_to}")
|
|
39
|
+
return {"network": NETWORK, "asset": ASSET, "amount": amount, "pay_to": pay_to}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _valid_amount(amount: str) -> bool:
|
|
43
|
+
if not isinstance(amount, str) or not amount:
|
|
44
|
+
return False
|
|
45
|
+
if len(amount) > MAX_AMOUNT_LEN:
|
|
46
|
+
return False
|
|
47
|
+
return bool(re.fullmatch(r"[0-9]+(\.[0-9]{1,6})?", amount)) and float(amount) > 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _valid_address(addr: str) -> bool:
|
|
51
|
+
return (
|
|
52
|
+
isinstance(addr, str)
|
|
53
|
+
and len(addr) == 42
|
|
54
|
+
and addr.startswith("0x")
|
|
55
|
+
and addr.lower() != "0x" + "0" * 40
|
|
56
|
+
and bool(re.fullmatch(r"0x[0-9a-fA-F]{40}", addr))
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _challenge_id(config: dict[str, str]) -> str:
|
|
61
|
+
payload = f"{config['network']}:{config['asset']}:{config['amount']}:{config['pay_to']}:starter"
|
|
62
|
+
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:32]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _proof_hint(config: dict[str, str]) -> str:
|
|
66
|
+
return f"local-demo:{_challenge_id(config)}:{config['amount']}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Handler(BaseHTTPRequestHandler):
|
|
70
|
+
config: dict[str, str]
|
|
71
|
+
|
|
72
|
+
def log_message(self, format: str, *args: object) -> None: # noqa: A002
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def _json(self, status: int, body: object) -> None:
|
|
76
|
+
self.send_response(status)
|
|
77
|
+
self.send_header("Content-Type", "application/json")
|
|
78
|
+
self.end_headers()
|
|
79
|
+
self.wfile.write(json.dumps(body, indent=2).encode("utf-8"))
|
|
80
|
+
|
|
81
|
+
def do_GET(self) -> None: # noqa: N802
|
|
82
|
+
parsed = urlparse(self.path)
|
|
83
|
+
if parsed.path == "/protected":
|
|
84
|
+
self._handle_protected()
|
|
85
|
+
elif parsed.path == "/manifest":
|
|
86
|
+
self._handle_manifest()
|
|
87
|
+
else:
|
|
88
|
+
self._json(404, {"error": "not found"})
|
|
89
|
+
|
|
90
|
+
def _handle_protected(self) -> None:
|
|
91
|
+
payment = self.headers.get("X-Payment", "")
|
|
92
|
+
if _valid_payment(payment, self.config):
|
|
93
|
+
self._json(200, {
|
|
94
|
+
"message": "paid resource",
|
|
95
|
+
"receipt": {
|
|
96
|
+
"settled": False,
|
|
97
|
+
"transactionBroadcast": False,
|
|
98
|
+
"network": self.config["network"],
|
|
99
|
+
"asset": self.config["asset"],
|
|
100
|
+
"amount": self.config["amount"],
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
return
|
|
104
|
+
self._json(402, {
|
|
105
|
+
"id": _challenge_id(self.config),
|
|
106
|
+
"accepts": {
|
|
107
|
+
"network": self.config["network"],
|
|
108
|
+
"asset": self.config["asset"],
|
|
109
|
+
"amount": self.config["amount"],
|
|
110
|
+
"payTo": self.config["pay_to"],
|
|
111
|
+
},
|
|
112
|
+
"localDemoProof": _proof_hint(self.config),
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
def _handle_manifest(self) -> None:
|
|
116
|
+
self._json(200, {
|
|
117
|
+
"name": "arc-x402-agent-starter",
|
|
118
|
+
"network": self.config["network"],
|
|
119
|
+
"asset": self.config["asset"],
|
|
120
|
+
"amount": self.config["amount"],
|
|
121
|
+
"payTo": self.config["pay_to"],
|
|
122
|
+
"safety": {
|
|
123
|
+
"testnetOnly": True,
|
|
124
|
+
"humanApproval": True,
|
|
125
|
+
"noPrivateKeys": True,
|
|
126
|
+
"noAutonomousSpending": True,
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _valid_payment(header: str, config: dict[str, str]) -> bool:
|
|
132
|
+
if not isinstance(header, str) or len(header) > MAX_PAYMENT_HEADER:
|
|
133
|
+
return False
|
|
134
|
+
if re.search(r"[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]", header):
|
|
135
|
+
return False
|
|
136
|
+
parts = header.split(":")
|
|
137
|
+
if len(parts) != 3:
|
|
138
|
+
return False
|
|
139
|
+
scheme, challenge, amount = parts
|
|
140
|
+
return (
|
|
141
|
+
scheme == "local-demo"
|
|
142
|
+
and challenge == _challenge_id(config)
|
|
143
|
+
and amount == config["amount"]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _print_manifest(config: dict[str, str]) -> None:
|
|
148
|
+
print(json.dumps({
|
|
149
|
+
"name": "arc-x402-agent-starter",
|
|
150
|
+
"network": config["network"],
|
|
151
|
+
"asset": config["asset"],
|
|
152
|
+
"amount": config["amount"],
|
|
153
|
+
"payTo": config["pay_to"],
|
|
154
|
+
"localDemoProof": _proof_hint(config),
|
|
155
|
+
"safety": {
|
|
156
|
+
"testnetOnly": True,
|
|
157
|
+
"humanApproval": True,
|
|
158
|
+
"noPrivateKeys": True,
|
|
159
|
+
"noAutonomousSpending": True,
|
|
160
|
+
},
|
|
161
|
+
}, indent=2))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _print_challenge(config: dict[str, str]) -> None:
|
|
165
|
+
print(json.dumps({
|
|
166
|
+
"id": _challenge_id(config),
|
|
167
|
+
"accepts": {
|
|
168
|
+
"network": config["network"],
|
|
169
|
+
"asset": config["asset"],
|
|
170
|
+
"amount": config["amount"],
|
|
171
|
+
"payTo": config["pay_to"],
|
|
172
|
+
},
|
|
173
|
+
"localDemoProof": _proof_hint(config),
|
|
174
|
+
}, indent=2))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def main(argv: list[str] | None = None) -> int:
|
|
178
|
+
parser = argparse.ArgumentParser(description="Arc x402 paid-agent starter")
|
|
179
|
+
parser.add_argument("--port", type=int, default=8091)
|
|
180
|
+
parser.add_argument("--print-manifest", action="store_true")
|
|
181
|
+
parser.add_argument("--print-challenge", action="store_true")
|
|
182
|
+
args = parser.parse_args(argv)
|
|
183
|
+
config = _load_config()
|
|
184
|
+
if args.print_manifest:
|
|
185
|
+
_print_manifest(config)
|
|
186
|
+
return 0
|
|
187
|
+
if args.print_challenge:
|
|
188
|
+
_print_challenge(config)
|
|
189
|
+
return 0
|
|
190
|
+
Handler.config = config
|
|
191
|
+
server = HTTPServer(("127.0.0.1", args.port), Handler)
|
|
192
|
+
print(f"Arc x402 starter running on http://127.0.0.1:{args.port}/protected")
|
|
193
|
+
try:
|
|
194
|
+
server.serve_forever()
|
|
195
|
+
except KeyboardInterrupt:
|
|
196
|
+
print("\nshutting down")
|
|
197
|
+
return 0
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
sys.exit(main())
|