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,200 @@
1
+ const ARC_COMPONENT_EXPECTATIONS = Object.freeze({
2
+ network: 'arc-testnet',
3
+ chainId: 5042002,
4
+ chainIdHex: '0x4cef52',
5
+ nativeGasAsset: 'USDC',
6
+ nativeGasDecimals: 18,
7
+ erc20UsdcAddress: '0x3600000000000000000000000000000000000000',
8
+ erc20UsdcDecimals: 6,
9
+ });
10
+
11
+ const fields = {
12
+ agentName: document.querySelector('#agent-name'),
13
+ agentRole: document.querySelector('#agent-role'),
14
+ purpose: document.querySelector('#purpose'),
15
+ amount: document.querySelector('#amount'),
16
+ recipient: document.querySelector('#recipient'),
17
+ reviewNote: document.querySelector('#review-note'),
18
+ };
19
+ const cards = {
20
+ agent: document.querySelector('#agent-card'),
21
+ payment: document.querySelector('#payment-card'),
22
+ receipt: document.querySelector('#receipt-card'),
23
+ combined: document.querySelector('#components-json'),
24
+ events: document.querySelector('#event-log'),
25
+ status: document.querySelector('#status-pill'),
26
+ };
27
+ const buttons = {
28
+ freeze: document.querySelector('#freeze-request'),
29
+ approve: document.querySelector('#mark-approved'),
30
+ receipt: document.querySelector('#mark-receipt'),
31
+ reset: document.querySelector('#reset-components'),
32
+ };
33
+
34
+ let status = 'draft';
35
+ let frozenAt = null;
36
+ let approvedAt = null;
37
+ let receiptAt = null;
38
+ let events = [];
39
+
40
+ function nowIso() {
41
+ return new Date().toISOString();
42
+ }
43
+
44
+ function amountIsValid(value) {
45
+ return /^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/.test(String(value || '').trim())
46
+ && Number(value) > 0;
47
+ }
48
+
49
+ function recipientIsValid(value) {
50
+ return /^0x[a-fA-F0-9]{40}$/.test(String(value || '').trim())
51
+ && !/^0x0{40}$/i.test(String(value || '').trim());
52
+ }
53
+
54
+ function normalizedAmount() {
55
+ return amountIsValid(fields.amount.value) ? Number(fields.amount.value).toFixed(2) : '0.00';
56
+ }
57
+
58
+ function addEvent(event, actor = 'system') {
59
+ events = [...events, {
60
+ at: nowIso(),
61
+ actor,
62
+ event,
63
+ requiresHumanReview: true,
64
+ walletActionEnabled: false,
65
+ transactionBroadcast: false,
66
+ }];
67
+ }
68
+
69
+ function agentCard() {
70
+ return {
71
+ agentId: `${fields.agentRole.value}.local`,
72
+ displayName: fields.agentName.value.trim(),
73
+ role: fields.agentRole.value,
74
+ trustLevel: 'unverified-local-demo',
75
+ sourceNotes: 'Review Arc docs/MCP context before claiming production capability.',
76
+ walletAuthority: false,
77
+ custodyAuthority: false,
78
+ };
79
+ }
80
+
81
+ function paymentRequestCard() {
82
+ return {
83
+ intentId: 'intent-local-components-001',
84
+ purpose: fields.purpose.value.trim(),
85
+ recipient: fields.recipient.value.trim(),
86
+ amount: normalizedAmount(),
87
+ asset: 'USDC',
88
+ assetDecimals: ARC_COMPONENT_EXPECTATIONS.erc20UsdcDecimals,
89
+ network: ARC_COMPONENT_EXPECTATIONS.network,
90
+ chainId: ARC_COMPONENT_EXPECTATIONS.chainId,
91
+ expiresAt: 'review-before-wallet-handoff',
92
+ frozenAt,
93
+ humanApprovalRequired: true,
94
+ moneyFieldsFrozenBeforeWallet: Boolean(frozenAt),
95
+ };
96
+ }
97
+
98
+ function receiptCard() {
99
+ return {
100
+ receiptId: 'receipt-local-components-001',
101
+ intentId: paymentRequestCard().intentId,
102
+ status: receiptAt ? 'simulated' : approvedAt ? 'approved_local_no_broadcast' : 'not_checked',
103
+ transactionHash: null,
104
+ explorerUrl: null,
105
+ checkedAt: receiptAt,
106
+ checks: [
107
+ { field: 'chainId', expected: ARC_COMPONENT_EXPECTATIONS.chainId, passed: true },
108
+ { field: 'asset', expected: 'USDC', passed: true },
109
+ { field: 'humanApprovalRequired', expected: true, passed: true },
110
+ { field: 'transactionBroadcast', expected: false, passed: true },
111
+ ],
112
+ transactionBroadcast: false,
113
+ };
114
+ }
115
+
116
+ function combinedObject() {
117
+ return {
118
+ schema: 'arc-mcp-builder-assistant.agentCommerce.components.v1',
119
+ status,
120
+ network: ARC_COMPONENT_EXPECTATIONS,
121
+ agent: agentCard(),
122
+ paymentRequest: paymentRequestCard(),
123
+ receipt: receiptCard(),
124
+ review: {
125
+ note: fields.reviewNote.value.trim(),
126
+ humanApprovalRequired: true,
127
+ localOnly: true,
128
+ walletConnected: false,
129
+ walletActionEnabled: false,
130
+ signingEnabled: false,
131
+ transactionBroadcast: false,
132
+ backendCalls: false,
133
+ mainnetEnabled: false,
134
+ },
135
+ events,
136
+ };
137
+ }
138
+
139
+ function render() {
140
+ const moneyFieldsFrozen = status !== 'draft';
141
+ cards.status.textContent = status === 'draft' ? 'Draft review object' : status === 'frozen' ? 'Money fields frozen' : status === 'approved' ? 'Local approval recorded' : 'Simulated receipt added';
142
+ cards.agent.textContent = JSON.stringify(agentCard(), null, 2);
143
+ cards.payment.textContent = JSON.stringify(paymentRequestCard(), null, 2);
144
+ cards.receipt.textContent = JSON.stringify(receiptCard(), null, 2);
145
+ cards.combined.textContent = JSON.stringify(combinedObject(), null, 2);
146
+ cards.events.replaceChildren(...events.map((entry) => {
147
+ const li = document.createElement('li');
148
+ li.textContent = `${entry.actor}: ${entry.event} · ${entry.at}`;
149
+ return li;
150
+ }));
151
+ for (const field of [fields.purpose, fields.amount, fields.recipient]) {
152
+ field.disabled = moneyFieldsFrozen;
153
+ }
154
+ buttons.freeze.disabled = status !== 'draft'
155
+ || !amountIsValid(fields.amount.value)
156
+ || !recipientIsValid(fields.recipient.value);
157
+ buttons.approve.disabled = status !== 'frozen';
158
+ buttons.receipt.disabled = status !== 'approved';
159
+ }
160
+
161
+ function reset() {
162
+ status = 'draft';
163
+ frozenAt = null;
164
+ approvedAt = null;
165
+ receiptAt = null;
166
+ events = [{
167
+ at: nowIso(),
168
+ actor: 'system',
169
+ event: 'Draft local component object created',
170
+ requiresHumanReview: true,
171
+ walletActionEnabled: false,
172
+ transactionBroadcast: false,
173
+ }];
174
+ render();
175
+ }
176
+
177
+ buttons.freeze.addEventListener('click', () => {
178
+ status = 'frozen';
179
+ frozenAt = nowIso();
180
+ addEvent('Human froze payment request fields before wallet handoff', 'human');
181
+ render();
182
+ });
183
+ buttons.approve.addEventListener('click', () => {
184
+ status = 'approved';
185
+ approvedAt = nowIso();
186
+ addEvent('Human recorded local approval; no wallet action was enabled', 'human');
187
+ render();
188
+ });
189
+ buttons.receipt.addEventListener('click', () => {
190
+ status = 'receipt_simulated';
191
+ receiptAt = nowIso();
192
+ addEvent('System added simulated receipt card without transaction broadcast', 'system');
193
+ render();
194
+ });
195
+ buttons.reset.addEventListener('click', reset);
196
+ for (const field of Object.values(fields)) {
197
+ field.addEventListener('input', render);
198
+ field.addEventListener('change', render);
199
+ }
200
+ reset();
@@ -0,0 +1,120 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="description" content="Local-only reusable agent commerce components for Arc MCP Builder Assistant." />
7
+ <title>Agent Commerce Components · Arc MCP Builder Assistant</title>
8
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; upgrade-insecure-requests" />
9
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%232563eb'/%3E%3Cstop offset='1' stop-color='%2306b6d4'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='16' fill='url(%23g)'/%3E%3Ctext x='50%25' y='55%25' text-anchor='middle' font-family='Inter,Arial,sans-serif' font-size='38' font-weight='900' fill='white'%3EA%3C/text%3E%3C/svg%3E" />
10
+ <style>
11
+ :root { color-scheme: dark; --bg: #080a12; --panel: rgba(255,255,255,.07); --line: rgba(255,255,255,.14); --text: #f7f3ff; --muted: #beb6d8; --accent: #7cf7d4; --accent2: #b388ff; --warn: #ffd166; --good: #7cf7a8; }
12
+ * { box-sizing: border-box; }
13
+ html { scroll-behavior: smooth; }
14
+ body { margin: 0; min-height: 100vh; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: var(--text); background: radial-gradient(circle at 18% 0%, rgba(124,247,212,.18), transparent 32rem), radial-gradient(circle at 88% 8%, rgba(179,136,255,.2), transparent 34rem), linear-gradient(180deg, #080a12 0%, #11152a 62%, #080a12 100%); line-height: 1.65; }
15
+ a { color: var(--accent); text-decoration: none; }
16
+ a:hover { text-decoration: underline; }
17
+ a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
18
+ .skip-link { position: absolute; left: -9999px; top: 12px; z-index: 10; padding: 10px 14px; border-radius: 999px; background: var(--accent); color: #06110d; font-weight: 900; }
19
+ .skip-link:focus { left: 12px; }
20
+ .wrap { width: min(1160px, calc(100% - 32px)); margin: 0 auto; }
21
+ header { padding: 26px 0 10px; }
22
+ .topbar, .hero, .grid, .actions, .cards { display: flex; gap: 16px; }
23
+ .topbar { align-items: center; justify-content: space-between; flex-wrap: wrap; }
24
+ .actions { flex-wrap: wrap; }
25
+ .brand { color: var(--text); font-weight: 950; letter-spacing: -.02em; }
26
+ nav { display: flex; gap: 10px; flex-wrap: wrap; }
27
+ nav a, button, .button { border: 1px solid var(--line); border-radius: 999px; background: rgba(255,255,255,.055); color: var(--text); padding: 10px 14px; font-weight: 850; cursor: pointer; }
28
+ button.primary { background: var(--accent); color: #07110f; border-color: transparent; }
29
+ button:disabled { cursor: not-allowed; opacity: .46; }
30
+ .hero { padding: 34px 0 22px; align-items: stretch; }
31
+ .hero > * { flex: 1; }
32
+ .eyebrow { color: var(--accent); font-size: .78rem; font-weight: 950; text-transform: uppercase; letter-spacing: .16em; }
33
+ h1, h2, h3 { letter-spacing: -.035em; line-height: 1.08; }
34
+ h1 { margin: 12px 0 14px; font-size: clamp(2.1rem, 7vw, 4.8rem); }
35
+ h2 { margin-top: 0; font-size: clamp(1.35rem, 3vw, 2.05rem); }
36
+ p, li, label, .meta { color: var(--muted); }
37
+ .panel, .component-card { border: 1px solid var(--line); border-radius: 28px; background: linear-gradient(180deg, rgba(255,255,255,.09), var(--panel)); box-shadow: 0 24px 80px rgba(0,0,0,.34); padding: clamp(20px, 4vw, 32px); }
38
+ .grid { display: grid; grid-template-columns: .95fr 1.05fr; align-items: start; padding-bottom: 72px; }
39
+ .cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
40
+ .wrap > *, .grid > *, .cards > * { min-width: 0; }
41
+ .field { display: grid; gap: 8px; margin-bottom: 14px; }
42
+ input, textarea, select { width: 100%; border: 1px solid var(--line); border-radius: 16px; background: rgba(0,0,0,.24); color: var(--text); padding: 12px 14px; font: inherit; }
43
+ textarea { min-height: 88px; resize: vertical; }
44
+ pre { white-space: pre-wrap; word-break: break-word; overflow-x: auto; padding: 18px; border-radius: 18px; border: 1px solid var(--line); background: rgba(0,0,0,.38); color: #eafff8; }
45
+ code { font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; }
46
+ .status { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 999px; border: 1px solid rgba(124,247,212,.3); color: var(--accent); background: rgba(124,247,212,.08); font-weight: 900; }
47
+ .warning { border-color: rgba(255,209,102,.38); background: rgba(255,209,102,.08); }
48
+ .meta { font-size: .9rem; }
49
+ .component-card h3 { margin-top: 0; }
50
+ .event-log { list-style: none; padding: 0; display: grid; gap: 10px; }
51
+ .event-log li { border-left: 3px solid var(--good); padding-left: 12px; }
52
+ @media (max-width: 900px) { .hero, .grid, .cards { display: grid; grid-template-columns: 1fr; } nav { display: none; } }
53
+ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation: none !important; transition: none !important; } }
54
+ </style>
55
+ </head>
56
+ <body>
57
+ <a class="skip-link" href="#content">Skip to content</a>
58
+ <header class="wrap">
59
+ <div class="topbar">
60
+ <a class="brand" href="../../">Arc MCP Builder Assistant</a>
61
+ <nav aria-label="Component navigation">
62
+ <a href="../../">Home</a>
63
+ <a href="../../docs/view.html#agent-commerce-components.md">Component docs</a>
64
+ <a href="../../docs/view.html#agent-commerce-use-cases.md">Use cases</a>
65
+ <a href="../job-escrow-simulator/">Job escrow</a>
66
+ </nav>
67
+ </div>
68
+ </header>
69
+ <main id="content" class="wrap">
70
+ <section class="hero">
71
+ <div>
72
+ <p class="eyebrow">Local-only starter kit · no wallet connection</p>
73
+ <h1>Reusable agent commerce cards.</h1>
74
+ <p>Compose an agent card, payment request, receipt preview, and event log without invoking the separate guarded Arc Testnet wallet-send lab. The output is a reviewable local JSON contract.</p>
75
+ <p class="status" id="status-pill">Draft review object</p>
76
+ </div>
77
+ <div class="panel warning">
78
+ <h2>Safety boundary</h2>
79
+ <ul>
80
+ <li>Human approval is required before every wallet action.</li>
81
+ <li>No private keys, no custody, no wallet permissions, no backend calls.</li>
82
+ <li>No transaction broadcast and no mainnet path.</li>
83
+ <li>Arc Testnet facts stay explicit: chain ID 5042002, USDC, ERC-20 USDC 6 decimals.</li>
84
+ </ul>
85
+ </div>
86
+ </section>
87
+ <section class="grid">
88
+ <div class="panel">
89
+ <h2>Scenario builder</h2>
90
+ <div class="field"><label for="agent-name">Agent name</label><input id="agent-name" value="Research Agent" /></div>
91
+ <div class="field"><label for="agent-role">Agent role</label><select id="agent-role"><option value="docs-grounded-research">Docs-grounded research</option><option value="paid-api-gateway">Paid API gateway</option><option value="creator-payout-agent">Creator payout agent</option><option value="escrow-worker">Escrow worker</option></select></div>
92
+ <div class="field"><label for="purpose">Payment purpose</label><input id="purpose" value="Pay for a cited Arc docs summary" /></div>
93
+ <div class="field"><label for="amount">Amount, USDC</label><input id="amount" type="number" min="0.01" step="0.01" value="3.50" /></div>
94
+ <div class="field"><label for="recipient">Recipient placeholder</label><input id="recipient" value="0x1111111111111111111111111111111111111111" /></div>
95
+ <div class="field"><label for="review-note">Review note</label><textarea id="review-note">Human reviewer checks recipient, amount, asset, chain, expiry, and source notes before any wallet handoff.</textarea></div>
96
+ <div class="actions">
97
+ <button class="primary" id="freeze-request">Freeze request</button>
98
+ <button id="mark-approved" disabled>Mark local approval</button>
99
+ <button id="mark-receipt" disabled>Add simulated receipt</button>
100
+ <button id="reset-components">Reset</button>
101
+ </div>
102
+ </div>
103
+ <div>
104
+ <div class="cards" aria-label="Reusable component cards">
105
+ <article class="component-card"><h3>Agent card</h3><pre id="agent-card"></pre></article>
106
+ <article class="component-card"><h3>Payment request</h3><pre id="payment-card"></pre></article>
107
+ <article class="component-card"><h3>Receipt card</h3><pre id="receipt-card"></pre></article>
108
+ <article class="component-card"><h3>Event log</h3><ol class="event-log" id="event-log"></ol></article>
109
+ </div>
110
+ <div class="panel" style="margin-top:16px">
111
+ <h2>Combined review object</h2>
112
+ <pre id="components-json"></pre>
113
+ <p class="meta">This JSON is the handoff: any separately reviewed wallet integration should consume the frozen fields instead of asking an AI model to regenerate money-critical values.</p>
114
+ </div>
115
+ </div>
116
+ </section>
117
+ </main>
118
+ <script src="./components.js" defer></script>
119
+ </body>
120
+ </html>
@@ -0,0 +1,271 @@
1
+ const ARC_FLOW_EXPECTATIONS = Object.freeze({
2
+ network: 'arc-testnet',
3
+ chainId: 5042002,
4
+ chainIdHex: '0x4cef52',
5
+ nativeGasAsset: 'USDC',
6
+ nativeGasDecimals: 18,
7
+ erc20UsdcAddress: '0x3600000000000000000000000000000000000000',
8
+ erc20UsdcDecimals: 6,
9
+ });
10
+
11
+ const FLOW_TEMPLATES = Object.freeze({
12
+ 'paid-api-call': {
13
+ title: 'Paid API call',
14
+ description: 'A research agent asks to buy one paid data response under a human-reviewed cost cap.',
15
+ agentName: 'Research Buyer Agent',
16
+ agentRole: 'paid-api-gateway',
17
+ recipient: '0x3333333333333333333333333333333333333333',
18
+ amount: '1.25',
19
+ purpose: 'Buy one market-data response for a cited report',
20
+ evidence: 'Reviewer checks endpoint purpose, quote, recipient, amount, and freshness before any future x402/Gateway proof path.',
21
+ receiptStatus: 'api_response_release_simulated',
22
+ },
23
+ 'creator-payout': {
24
+ title: 'Creator payout',
25
+ description: 'A project lead reviews a contributor payout line item before any stablecoin settlement work.',
26
+ agentName: 'Payout Review Agent',
27
+ agentRole: 'creator-payout-agent',
28
+ recipient: '0x1111111111111111111111111111111111111111',
29
+ amount: '75.00',
30
+ purpose: 'Pay contributor for edited Arc builder demo clip',
31
+ evidence: 'Reviewer checks deliverable URL, contributor identity, payout reason, and amount before any future wallet handoff.',
32
+ receiptStatus: 'payout_note_simulated',
33
+ },
34
+ 'ai-agent-commerce': {
35
+ title: 'AI-agent commerce',
36
+ description: 'One agent requests a specialist agent action under a user-approved spend cap.',
37
+ agentName: 'Coordinator Agent',
38
+ agentRole: 'agent-to-agent-coordinator',
39
+ recipient: '0x2222222222222222222222222222222222222222',
40
+ amount: '3.50',
41
+ purpose: 'Call specialist code-review agent for one bounded review task',
42
+ evidence: 'Reviewer checks specialist trust notes, quoted task, cap, and receipt expectations before any future paid-agent proof path.',
43
+ receiptStatus: 'specialist_output_simulated',
44
+ },
45
+ });
46
+
47
+ const fields = {
48
+ agentName: document.querySelector('#agent-name'),
49
+ recipient: document.querySelector('#recipient'),
50
+ amount: document.querySelector('#amount'),
51
+ purpose: document.querySelector('#purpose'),
52
+ evidence: document.querySelector('#evidence'),
53
+ };
54
+ const nodes = {
55
+ title: document.querySelector('#selected-title'),
56
+ description: document.querySelector('#selected-description'),
57
+ nav: document.querySelector('#flow-nav'),
58
+ status: document.querySelector('#flow-status'),
59
+ agent: document.querySelector('#agent-json'),
60
+ request: document.querySelector('#request-json'),
61
+ receipt: document.querySelector('#receipt-json'),
62
+ events: document.querySelector('#event-log'),
63
+ combined: document.querySelector('#flow-json'),
64
+ };
65
+ const buttons = {
66
+ freeze: document.querySelector('#freeze-flow'),
67
+ approve: document.querySelector('#approve-flow'),
68
+ receipt: document.querySelector('#simulate-receipt'),
69
+ reset: document.querySelector('#reset-flow'),
70
+ };
71
+
72
+ let selectedFlowId = 'paid-api-call';
73
+ let state = 'draft_review';
74
+ let frozenAt = null;
75
+ let approvedAt = null;
76
+ let receiptAt = null;
77
+ let events = [];
78
+
79
+ function nowIso() {
80
+ return new Date().toISOString();
81
+ }
82
+
83
+ function activeTemplate() {
84
+ return FLOW_TEMPLATES[selectedFlowId];
85
+ }
86
+
87
+ function amountIsValid(value) {
88
+ return /^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/.test(String(value || '').trim())
89
+ && Number(value) > 0;
90
+ }
91
+
92
+ function recipientIsValid(value) {
93
+ return /^0x[a-fA-F0-9]{40}$/.test(String(value || '').trim())
94
+ && !/^0x0{40}$/i.test(String(value || '').trim());
95
+ }
96
+
97
+ function normalizedAmount() {
98
+ return amountIsValid(fields.amount.value) ? Number(fields.amount.value).toFixed(2) : '0.00';
99
+ }
100
+
101
+ function addEvent(event, actor = 'system') {
102
+ events = [...events, {
103
+ at: nowIso(),
104
+ actor,
105
+ event,
106
+ humanApprovalRequired: true,
107
+ walletActionEnabled: false,
108
+ transactionBroadcast: false,
109
+ }];
110
+ }
111
+
112
+ function loadFlow(flowId) {
113
+ selectedFlowId = flowId;
114
+ const flow = activeTemplate();
115
+ fields.agentName.value = flow.agentName;
116
+ fields.recipient.value = flow.recipient;
117
+ fields.amount.value = flow.amount;
118
+ fields.purpose.value = flow.purpose;
119
+ fields.evidence.value = flow.evidence;
120
+ state = 'draft_review';
121
+ frozenAt = null;
122
+ approvedAt = null;
123
+ receiptAt = null;
124
+ events = [{
125
+ at: nowIso(),
126
+ actor: 'system',
127
+ event: `${flow.title} local flow loaded`,
128
+ humanApprovalRequired: true,
129
+ walletActionEnabled: false,
130
+ transactionBroadcast: false,
131
+ }];
132
+ render();
133
+ }
134
+
135
+ function agentObject() {
136
+ const flow = activeTemplate();
137
+ return {
138
+ agentId: `${selectedFlowId}.local`,
139
+ displayName: fields.agentName.value.trim(),
140
+ role: flow.agentRole,
141
+ trustLevel: 'unverified-local-demo',
142
+ sourceNotes: fields.evidence.value.trim(),
143
+ walletAuthority: false,
144
+ custodyAuthority: false,
145
+ };
146
+ }
147
+
148
+ function requestObject() {
149
+ return {
150
+ intentId: `intent-${selectedFlowId}-001`,
151
+ flowId: selectedFlowId,
152
+ purpose: fields.purpose.value.trim(),
153
+ recipient: fields.recipient.value.trim(),
154
+ amount: normalizedAmount(),
155
+ asset: 'USDC',
156
+ assetDecimals: ARC_FLOW_EXPECTATIONS.erc20UsdcDecimals,
157
+ network: ARC_FLOW_EXPECTATIONS.network,
158
+ chainId: ARC_FLOW_EXPECTATIONS.chainId,
159
+ expiresAt: 'review-before-wallet-handoff',
160
+ humanApprovalRequired: true,
161
+ frozenBeforeWallet: Boolean(frozenAt),
162
+ frozenAt,
163
+ };
164
+ }
165
+
166
+ function receiptObject() {
167
+ const flow = activeTemplate();
168
+ return {
169
+ receiptId: `receipt-${selectedFlowId}-001`,
170
+ intentId: requestObject().intentId,
171
+ status: receiptAt ? flow.receiptStatus : approvedAt ? 'approved_local_no_broadcast' : 'not_checked',
172
+ transactionHash: null,
173
+ explorerUrl: null,
174
+ checkedAt: receiptAt,
175
+ checks: [
176
+ { field: 'flowId', expected: selectedFlowId, passed: true },
177
+ { field: 'chainId', expected: ARC_FLOW_EXPECTATIONS.chainId, passed: true },
178
+ { field: 'asset', expected: 'USDC', passed: true },
179
+ { field: 'humanApprovalRequired', expected: true, passed: true },
180
+ { field: 'transactionBroadcast', expected: false, passed: true },
181
+ ],
182
+ transactionBroadcast: false,
183
+ };
184
+ }
185
+
186
+ function combinedObject() {
187
+ return {
188
+ schema: 'arc-mcp-builder-assistant.agentCommerce.flow.v1',
189
+ state,
190
+ flowId: selectedFlowId,
191
+ network: ARC_FLOW_EXPECTATIONS,
192
+ agent: agentObject(),
193
+ request: requestObject(),
194
+ receipt: receiptObject(),
195
+ safety: {
196
+ localOnly: true,
197
+ humanApprovalRequired: true,
198
+ walletConnected: false,
199
+ walletActionEnabled: false,
200
+ signingEnabled: false,
201
+ transactionBroadcast: false,
202
+ backendCalls: false,
203
+ remoteRpcCalls: false,
204
+ liveX402Verification: false,
205
+ mainnetEnabled: false,
206
+ },
207
+ events,
208
+ };
209
+ }
210
+
211
+ function renderFlowNav() {
212
+ nodes.nav.replaceChildren(...Object.entries(FLOW_TEMPLATES).map(([flowId, flow]) => {
213
+ const button = document.createElement('button');
214
+ button.type = 'button';
215
+ button.className = `pill${flowId === selectedFlowId ? ' active' : ''}`;
216
+ button.textContent = flow.title;
217
+ button.addEventListener('click', () => loadFlow(flowId));
218
+ return button;
219
+ }));
220
+ }
221
+
222
+ function render() {
223
+ const flow = activeTemplate();
224
+ const moneyFieldsFrozen = state !== 'draft_review';
225
+ nodes.title.textContent = flow.title;
226
+ nodes.description.textContent = flow.description;
227
+ nodes.status.textContent = state;
228
+ nodes.agent.textContent = JSON.stringify(agentObject(), null, 2);
229
+ nodes.request.textContent = JSON.stringify(requestObject(), null, 2);
230
+ nodes.receipt.textContent = JSON.stringify(receiptObject(), null, 2);
231
+ nodes.combined.textContent = JSON.stringify(combinedObject(), null, 2);
232
+ nodes.events.replaceChildren(...events.map((entry) => {
233
+ const li = document.createElement('li');
234
+ li.textContent = `${entry.actor}: ${entry.event} · ${entry.at}`;
235
+ return li;
236
+ }));
237
+ for (const field of [fields.recipient, fields.amount, fields.purpose]) {
238
+ field.disabled = moneyFieldsFrozen;
239
+ }
240
+ buttons.freeze.disabled = state !== 'draft_review'
241
+ || !amountIsValid(fields.amount.value)
242
+ || !recipientIsValid(fields.recipient.value);
243
+ buttons.approve.disabled = state !== 'fields_frozen';
244
+ buttons.receipt.disabled = state !== 'approved_local_no_broadcast';
245
+ renderFlowNav();
246
+ }
247
+
248
+ buttons.freeze.addEventListener('click', () => {
249
+ state = 'fields_frozen';
250
+ frozenAt = nowIso();
251
+ addEvent('Human froze flow money fields before wallet handoff', 'human');
252
+ render();
253
+ });
254
+ buttons.approve.addEventListener('click', () => {
255
+ state = 'approved_local_no_broadcast';
256
+ approvedAt = nowIso();
257
+ addEvent('Human recorded local approval; no transaction was broadcast', 'human');
258
+ render();
259
+ });
260
+ buttons.receipt.addEventListener('click', () => {
261
+ state = 'receipt_simulated';
262
+ receiptAt = nowIso();
263
+ addEvent('System generated simulated receipt for the selected flow', 'system');
264
+ render();
265
+ });
266
+ buttons.reset.addEventListener('click', () => loadFlow(selectedFlowId));
267
+ for (const field of Object.values(fields)) {
268
+ field.addEventListener('input', render);
269
+ field.addEventListener('change', render);
270
+ }
271
+ loadFlow(selectedFlowId);