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.
Files changed (58) hide show
  1. arc_builder_kit/__init__.py +4 -0
  2. arc_builder_kit/__main__.py +6 -0
  3. arc_builder_kit/_paths.py +47 -0
  4. arc_builder_kit/cli.py +277 -0
  5. arc_builder_kit/config/arc_testnet.facts.json +31 -0
  6. arc_builder_kit/doctor.py +936 -0
  7. arc_builder_kit/examples/agent-commerce-components/components.js +200 -0
  8. arc_builder_kit/examples/agent-commerce-components/index.html +120 -0
  9. arc_builder_kit/examples/agent-commerce-flows/flows.js +271 -0
  10. arc_builder_kit/examples/agent-commerce-flows/index.html +114 -0
  11. arc_builder_kit/examples/agent-commerce-live/commerce-live.js +190 -0
  12. arc_builder_kit/examples/agent-commerce-live/index.html +105 -0
  13. arc_builder_kit/examples/agent-commerce-review-packet/index.html +96 -0
  14. arc_builder_kit/examples/agent-commerce-review-packet/packet.js +125 -0
  15. arc_builder_kit/examples/agent-identity-profile-preview/identity.js +126 -0
  16. arc_builder_kit/examples/agent-identity-profile-preview/index.html +104 -0
  17. arc_builder_kit/examples/arc-agent-treasury-lab/index.html +152 -0
  18. arc_builder_kit/examples/arc-agent-treasury-lab/treasury.js +532 -0
  19. arc_builder_kit/examples/arc-testnet-operator-evidence/evidence.example.json +47 -0
  20. arc_builder_kit/examples/arc-testnet-wallet-send-gate/index.html +233 -0
  21. arc_builder_kit/examples/arc-testnet-wallet-send-gate/live-infrastructure-policy.example.json +59 -0
  22. arc_builder_kit/examples/arc-testnet-wallet-send-gate/wallet-send-gate.js +472 -0
  23. arc_builder_kit/examples/circle-wallet-integration/index.html +155 -0
  24. arc_builder_kit/examples/circle-wallet-integration/wallet-lab.js +91 -0
  25. arc_builder_kit/examples/job-escrow-simulator/index.html +121 -0
  26. arc_builder_kit/examples/job-escrow-simulator/simulator.js +162 -0
  27. arc_builder_kit/examples/payment-intent-demo/index.html +132 -0
  28. arc_builder_kit/examples/payment-intent-playground/index.html +301 -0
  29. arc_builder_kit/examples/payment-intent-playground/playground.js +835 -0
  30. arc_builder_kit/examples/payment-intent-receipt-matcher/index.html +157 -0
  31. arc_builder_kit/examples/payment-intent-receipt-matcher/matcher.js +877 -0
  32. arc_builder_kit/examples/receipt-verifier-playground/index.html +120 -0
  33. arc_builder_kit/examples/receipt-verifier-playground/verifier.js +226 -0
  34. arc_builder_kit/examples/receipt-viewer/index.html +138 -0
  35. arc_builder_kit/examples/receipt-viewer/receipt-viewer.js +472 -0
  36. arc_builder_kit/examples/transaction-status-playground/index.html +135 -0
  37. arc_builder_kit/examples/transaction-status-playground/status.js +518 -0
  38. arc_builder_kit/examples/x402-local-challenge-server/.env.example +25 -0
  39. arc_builder_kit/examples/x402-local-challenge-server/README.md +111 -0
  40. arc_builder_kit/examples/x402-local-challenge-server/server.py +711 -0
  41. arc_builder_kit/mcp_server.py +463 -0
  42. arc_builder_kit/release_packet.py +469 -0
  43. arc_builder_kit/templates/README.md +25 -0
  44. arc_builder_kit/templates/job-escrow-starter/README.md +25 -0
  45. arc_builder_kit/templates/job-escrow-starter/index.html +41 -0
  46. arc_builder_kit/templates/job-escrow-starter/index.js +14 -0
  47. arc_builder_kit/templates/payment-intent-starter/README.md +25 -0
  48. arc_builder_kit/templates/payment-intent-starter/index.html +42 -0
  49. arc_builder_kit/templates/payment-intent-starter/index.js +7 -0
  50. arc_builder_kit/templates/x402-agent-starter/README.md +29 -0
  51. arc_builder_kit/templates/x402-agent-starter/server.py +201 -0
  52. arc_builder_kit/validate_repo.py +2212 -0
  53. arc_builder_kit-0.2.0.dist-info/METADATA +543 -0
  54. arc_builder_kit-0.2.0.dist-info/RECORD +58 -0
  55. arc_builder_kit-0.2.0.dist-info/WHEEL +5 -0
  56. arc_builder_kit-0.2.0.dist-info/entry_points.txt +3 -0
  57. arc_builder_kit-0.2.0.dist-info/licenses/LICENSE +21 -0
  58. 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())