@payclaw/badge 0.7.0 → 0.8.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 (51) hide show
  1. package/README.md +68 -2
  2. package/dist/api/client.d.ts +7 -0
  3. package/dist/api/client.js +34 -3
  4. package/dist/api/client.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +27 -12
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/device-auth.d.ts +30 -0
  9. package/dist/lib/device-auth.js +92 -0
  10. package/dist/lib/device-auth.js.map +1 -0
  11. package/dist/lib/parse-outcome.d.ts +5 -0
  12. package/dist/lib/parse-outcome.js +38 -0
  13. package/dist/lib/parse-outcome.js.map +1 -0
  14. package/dist/lib/parse-outcome.test.d.ts +1 -0
  15. package/dist/lib/parse-outcome.test.js +47 -0
  16. package/dist/lib/parse-outcome.test.js.map +1 -0
  17. package/dist/lib/report-badge-presented-handler.d.ts +7 -1
  18. package/dist/lib/report-badge-presented-handler.js +13 -2
  19. package/dist/lib/report-badge-presented-handler.js.map +1 -1
  20. package/dist/lib/report-badge.d.ts +3 -2
  21. package/dist/lib/report-badge.js +12 -8
  22. package/dist/lib/report-badge.js.map +1 -1
  23. package/dist/lib/report-badge.test.d.ts +1 -0
  24. package/dist/lib/report-badge.test.js +109 -0
  25. package/dist/lib/report-badge.test.js.map +1 -0
  26. package/dist/lib/storage.d.ts +17 -0
  27. package/dist/lib/storage.js +86 -0
  28. package/dist/lib/storage.js.map +1 -0
  29. package/dist/lib/ucp-manifest.d.ts +32 -0
  30. package/dist/lib/ucp-manifest.js +117 -0
  31. package/dist/lib/ucp-manifest.js.map +1 -0
  32. package/dist/lib/ucp-manifest.test.d.ts +1 -0
  33. package/dist/lib/ucp-manifest.test.js +92 -0
  34. package/dist/lib/ucp-manifest.test.js.map +1 -0
  35. package/dist/sampling.d.ts +19 -1
  36. package/dist/sampling.js +74 -33
  37. package/dist/sampling.js.map +1 -1
  38. package/dist/sampling.test.d.ts +1 -0
  39. package/dist/sampling.test.js +150 -0
  40. package/dist/sampling.test.js.map +1 -0
  41. package/dist/tools/getAgentIdentity.d.ts +16 -2
  42. package/dist/tools/getAgentIdentity.js +179 -12
  43. package/dist/tools/getAgentIdentity.js.map +1 -1
  44. package/dist/types.d.ts +1 -0
  45. package/dist/verify.d.ts +34 -0
  46. package/dist/verify.js +161 -0
  47. package/dist/verify.js.map +1 -0
  48. package/dist/verify.test.d.ts +1 -0
  49. package/dist/verify.test.js +177 -0
  50. package/dist/verify.test.js.map +1 -0
  51. package/package.json +10 -4
package/dist/sampling.js CHANGED
@@ -1,9 +1,8 @@
1
+ import { getBaseUrl } from "./api/client.js";
2
+ import { parseResponse } from "./lib/parse-outcome.js";
3
+ import { getStoredConsentKey } from "./lib/storage.js";
1
4
  const SAMPLING_DELAY_MS = 7000; // 7 seconds after identity_presented
2
5
  const SAMPLING_TIMEOUT_MS = 15000; // 15 seconds to respond
3
- const FAILURE_SIGNALS = [
4
- "yes", "blocked", "denied", "failed", "403", "error",
5
- "rejected", "banned", "forbidden", "captcha", "stopped",
6
- ];
7
6
  // In-memory state — max 100 active trips
8
7
  const activeTrips = new Map();
9
8
  const MAX_TRIPS = 100;
@@ -14,12 +13,16 @@ let serverRef = null;
14
13
  let samplingAvailable = false;
15
14
  export function initSampling(server) {
16
15
  serverRef = server;
17
- // Detect sampling support after connection
18
- // We'll check on first use since capabilities aren't available until connected
19
- samplingAvailable = true; // Optimistic — will catch errors on first attempt
16
+ // Extended Auth: only use sampling (agent confirmation prompt) when explicitly enabled.
17
+ // Otherwise, agent reports outcome via payclaw_reportBadgeOutcome.
18
+ const useExtendedAuth = process.env.PAYCLAW_EXTENDED_AUTH === "true" ||
19
+ process.env.PAYCLAW_EXTENDED_AUTH === "1";
20
+ samplingAvailable = useExtendedAuth; // Will catch errors on first attempt if enabled
20
21
  if (!reaperStarted) {
21
22
  reaperStarted = true;
22
- setInterval(() => reapStaleTrips(), REAPER_INTERVAL_MS);
23
+ if (process.env.VITEST !== "true") {
24
+ setInterval(() => reapStaleTrips(), REAPER_INTERVAL_MS);
25
+ }
23
26
  }
24
27
  }
25
28
  export function onTripStarted(token, merchant) {
@@ -65,9 +68,6 @@ export function onIdentityPresented(token, merchant) {
65
68
  clearTimeout(t.samplingTimer);
66
69
  t.samplingTimer = setTimeout(() => sampleAgent(token, merchant), SAMPLING_DELAY_MS);
67
70
  }
68
- export function reportOutcomeFromAgent(token, merchant, outcome) {
69
- resolveTrip(token, outcome, "agent_reported");
70
- }
71
71
  async function sampleAgent(token, merchant) {
72
72
  const trip = activeTrips.get(token);
73
73
  if (!trip || trip.outcome)
@@ -130,22 +130,6 @@ async function sampleAgent(token, merchant) {
130
130
  }
131
131
  }
132
132
  }
133
- function parseResponse(text) {
134
- if (!text || text.trim().length === 0)
135
- return "inconclusive";
136
- const lower = text.toLowerCase().trim();
137
- // Check for denial signals
138
- if (FAILURE_SIGNALS.some((s) => lower.includes(s))) {
139
- // But "no" alone means "no, I was not denied" = accepted
140
- if (lower === "no" || lower === "no." || lower === "no,")
141
- return "accepted";
142
- return "denied";
143
- }
144
- // "no" variants = not denied = accepted
145
- if (lower.startsWith("no"))
146
- return "accepted";
147
- return "inconclusive";
148
- }
149
133
  function resolveTrip(token, outcome, detail) {
150
134
  const trip = activeTrips.get(token);
151
135
  if (!trip)
@@ -161,15 +145,15 @@ function resolveTrip(token, outcome, detail) {
161
145
  activeTrips.delete(token);
162
146
  }
163
147
  async function reportOutcome(token, outcome, merchant, detail) {
164
- const apiUrl = process.env.PAYCLAW_API_URL || "https://payclaw.io";
165
- const apiKey = process.env.PAYCLAW_API_KEY;
166
- if (!apiKey)
148
+ const apiUrl = getBaseUrl();
149
+ const key = getStoredConsentKey();
150
+ if (!key)
167
151
  return;
168
152
  const eventType = outcome === "denied" ? "trip_failure" : "trip_success";
169
153
  const res = await fetch(`${apiUrl}/api/badge/report`, {
170
154
  method: "POST",
171
155
  headers: {
172
- Authorization: `Bearer ${apiKey}`,
156
+ Authorization: `Bearer ${key}`,
173
157
  "Content-Type": "application/json",
174
158
  },
175
159
  body: JSON.stringify({
@@ -187,25 +171,82 @@ async function reportOutcome(token, outcome, merchant, detail) {
187
171
  }
188
172
  function reapStaleTrips() {
189
173
  const now = Date.now();
174
+ let reaped = 0;
190
175
  for (const [token, trip] of activeTrips) {
191
176
  if (now - trip.startedAt > STALE_TRIP_MS) {
177
+ const ageMin = Math.round((now - trip.startedAt) / 60000);
192
178
  if (trip.presented && !trip.outcome) {
179
+ process.stderr.write(`[PayClaw] Reaped stale trip: ${token.slice(0, 10)}** (${trip.merchant.slice(0, 64)}, age: ${ageMin}m)\n`);
193
180
  resolveTrip(token, "inconclusive", "stale_trip_reaped");
181
+ reaped++;
194
182
  }
195
183
  else {
196
184
  activeTrips.delete(token);
185
+ reaped++;
197
186
  }
198
187
  }
199
188
  }
189
+ if (activeTrips.size > 0 || reaped > 0) {
190
+ process.stderr.write(`[PayClaw] Active trips: ${activeTrips.size} | Reaped: ${reaped}\n`);
191
+ }
200
192
  }
201
193
  // Called when MCP client disconnects
202
194
  export function onServerClose() {
203
195
  for (const [token, trip] of activeTrips) {
204
196
  if (trip.presented && !trip.outcome) {
205
- // Agent finished normally assume success
206
- resolveTrip(token, "accepted", "server_close_clean_exit");
197
+ // Agent disconnectedoutcome unknown
198
+ resolveTrip(token, "inconclusive", "server_close");
207
199
  }
208
200
  }
209
201
  activeTrips.clear();
210
202
  }
203
+ /** Test-only: reset state between tests. No-op when VITEST not set. */
204
+ export function resetSamplingState() {
205
+ if (process.env.VITEST !== "true")
206
+ return;
207
+ for (const trip of activeTrips.values()) {
208
+ if (trip.samplingTimer)
209
+ clearTimeout(trip.samplingTimer);
210
+ }
211
+ activeTrips.clear();
212
+ serverRef = null;
213
+ samplingAvailable = true;
214
+ reaperStarted = false;
215
+ }
216
+ /** Test-only: get trip for assertions. Returns undefined when VITEST not set. */
217
+ export function getActiveTrip(token) {
218
+ if (process.env.VITEST !== "true")
219
+ return undefined;
220
+ return activeTrips.get(token);
221
+ }
222
+ /**
223
+ * Report outcome from agent (payclaw_reportBadgeOutcome tool).
224
+ * Agent-only path — no sampling prompt. Resolves trip and POSTs to API.
225
+ * When token not in activeTrips (e.g. after restart), looks up by merchant or POSTs directly.
226
+ */
227
+ export function reportOutcomeFromAgent(token, merchant, outcome) {
228
+ if (activeTrips.has(token)) {
229
+ resolveTrip(token, outcome, "agent_reported");
230
+ return;
231
+ }
232
+ // Token may be from before restart — try to find a unique trip by merchant
233
+ let matchToken = null;
234
+ let matchCount = 0;
235
+ for (const [t, trip] of activeTrips) {
236
+ if (trip.merchant === merchant && trip.presented && !trip.outcome) {
237
+ matchToken = t;
238
+ matchCount++;
239
+ if (matchCount > 1)
240
+ break;
241
+ }
242
+ }
243
+ if (matchCount === 1 && matchToken) {
244
+ resolveTrip(matchToken, outcome, "agent_reported");
245
+ return;
246
+ }
247
+ // No matching trip — still report to API so outcome is recorded
248
+ reportOutcome(token, outcome, merchant, "agent_reported").catch((err) => {
249
+ process.stderr.write(`[BADGE] Failed to report outcome: ${err}\n`);
250
+ });
251
+ }
211
252
  //# sourceMappingURL=sampling.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,150 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach, } from "vitest";
2
+ import { onTripStarted, onIdentityPresented, onServerClose, resetSamplingState, getActiveTrip, reportOutcomeFromAgent, } from "./sampling.js";
3
+ import * as storage from "./lib/storage.js";
4
+ vi.mock("./lib/storage.js", () => ({
5
+ getStoredConsentKey: vi.fn(),
6
+ storeConsentKey: vi.fn(),
7
+ }));
8
+ vi.mock("./api/client.js", () => ({
9
+ getBaseUrl: vi.fn().mockReturnValue("https://payclaw.io"),
10
+ }));
11
+ describe("sampling — multi-merchant trip lifecycle", () => {
12
+ let originalVitest;
13
+ const mockFetch = vi.fn();
14
+ beforeEach(() => {
15
+ originalVitest = process.env.VITEST;
16
+ process.env.VITEST = "true";
17
+ vi.stubGlobal("fetch", mockFetch);
18
+ mockFetch.mockResolvedValue({ ok: true });
19
+ vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
20
+ resetSamplingState();
21
+ });
22
+ afterEach(() => {
23
+ vi.useRealTimers();
24
+ resetSamplingState();
25
+ vi.unstubAllGlobals();
26
+ if (originalVitest !== undefined) {
27
+ process.env.VITEST = originalVitest;
28
+ }
29
+ else {
30
+ delete process.env.VITEST;
31
+ }
32
+ });
33
+ it("starting trip B resolves presented trip A as agent_moved_to_new_merchant", () => {
34
+ onTripStarted("tok_a", "amazon.com");
35
+ onIdentityPresented("tok_a", "amazon.com");
36
+ // Trip A is active and presented
37
+ expect(getActiveTrip("tok_a")).toBeDefined();
38
+ expect(getActiveTrip("tok_a").presented).toBe(true);
39
+ // Start trip B at a different merchant
40
+ onTripStarted("tok_b", "target.com");
41
+ // Trip A should be resolved and evicted (agent moved on)
42
+ expect(getActiveTrip("tok_a")).toBeUndefined();
43
+ // Trip B should be active
44
+ const tripB = getActiveTrip("tok_b");
45
+ expect(tripB).toBeDefined();
46
+ expect(tripB.merchant).toBe("target.com");
47
+ expect(tripB.presented).toBe(false);
48
+ });
49
+ it("trip B can be presented and resolved after trip A is auto-resolved", () => {
50
+ onTripStarted("tok_a", "amazon.com");
51
+ onIdentityPresented("tok_a", "amazon.com");
52
+ onTripStarted("tok_b", "target.com");
53
+ onIdentityPresented("tok_b", "target.com");
54
+ const tripB = getActiveTrip("tok_b");
55
+ expect(tripB).toBeDefined();
56
+ expect(tripB.presented).toBe(true);
57
+ // Resolve trip B via agent report
58
+ reportOutcomeFromAgent("tok_b", "target.com", "accepted");
59
+ expect(getActiveTrip("tok_b")).toBeUndefined();
60
+ });
61
+ it("non-presented trip A is NOT resolved when trip B starts", () => {
62
+ onTripStarted("tok_a", "amazon.com");
63
+ // Never call onIdentityPresented for tok_a
64
+ onTripStarted("tok_b", "target.com");
65
+ // Trip A should still exist (not presented, so agent_moved logic skips it)
66
+ expect(getActiveTrip("tok_a")).toBeDefined();
67
+ expect(getActiveTrip("tok_b")).toBeDefined();
68
+ });
69
+ it("same-merchant trip restart does not resolve existing trip", () => {
70
+ onTripStarted("tok_a", "amazon.com");
71
+ onIdentityPresented("tok_a", "amazon.com");
72
+ // Start another trip at SAME merchant — should NOT resolve trip A
73
+ onTripStarted("tok_b", "amazon.com");
74
+ // Trip A still active (same merchant = not "moved to new merchant")
75
+ expect(getActiveTrip("tok_a")).toBeDefined();
76
+ expect(getActiveTrip("tok_b")).toBeDefined();
77
+ });
78
+ it("three-merchant chain resolves each previous trip correctly", () => {
79
+ onTripStarted("tok_1", "amazon.com");
80
+ onIdentityPresented("tok_1", "amazon.com");
81
+ onTripStarted("tok_2", "target.com");
82
+ expect(getActiveTrip("tok_1")).toBeUndefined(); // resolved
83
+ onIdentityPresented("tok_2", "target.com");
84
+ onTripStarted("tok_3", "walmart.com");
85
+ expect(getActiveTrip("tok_2")).toBeUndefined(); // resolved
86
+ onIdentityPresented("tok_3", "walmart.com");
87
+ // Only tok_3 should remain
88
+ expect(getActiveTrip("tok_3")).toBeDefined();
89
+ expect(getActiveTrip("tok_3").merchant).toBe("walmart.com");
90
+ });
91
+ it("reportOutcomeFromAgent falls back to merchant search when token unknown", () => {
92
+ onTripStarted("tok_a", "amazon.com");
93
+ onIdentityPresented("tok_a", "amazon.com");
94
+ // Report with a different token but matching merchant
95
+ reportOutcomeFromAgent("unknown_tok", "amazon.com", "accepted");
96
+ // Trip should be resolved via merchant search fallback
97
+ expect(getActiveTrip("tok_a")).toBeUndefined();
98
+ });
99
+ it("reportOutcomeFromAgent with no matching trip still POSTs to API", () => {
100
+ mockFetch.mockClear();
101
+ reportOutcomeFromAgent("orphan_tok", "orphan.com", "denied");
102
+ // Should have called fetch to POST directly
103
+ expect(mockFetch).toHaveBeenCalledTimes(1);
104
+ const [url, opts] = mockFetch.mock.calls[0];
105
+ expect(url).toContain("/api/badge/report");
106
+ expect(JSON.parse(opts.body)).toMatchObject({
107
+ verification_token: "orphan_tok",
108
+ merchant: "orphan.com",
109
+ outcome: "denied",
110
+ });
111
+ });
112
+ it("onServerClose resolves all presented trips as inconclusive", () => {
113
+ onTripStarted("tok_a", "amazon.com");
114
+ onIdentityPresented("tok_a", "amazon.com");
115
+ onTripStarted("tok_b", "amazon.com"); // Same merchant — no auto-resolve
116
+ onIdentityPresented("tok_b", "amazon.com");
117
+ onServerClose();
118
+ expect(getActiveTrip("tok_a")).toBeUndefined();
119
+ expect(getActiveTrip("tok_b")).toBeUndefined();
120
+ });
121
+ it("reportOutcomeFromAgent with ambiguous merchant match falls through to API POST", () => {
122
+ mockFetch.mockClear();
123
+ // Two trips at same merchant, both presented
124
+ onTripStarted("tok_a", "amazon.com");
125
+ onIdentityPresented("tok_a", "amazon.com");
126
+ onTripStarted("tok_b", "amazon.com");
127
+ onIdentityPresented("tok_b", "amazon.com");
128
+ mockFetch.mockClear();
129
+ reportOutcomeFromAgent("unknown_tok", "amazon.com", "accepted");
130
+ // Should NOT resolve either trip (ambiguous) — falls through to direct POST
131
+ expect(getActiveTrip("tok_a")).toBeDefined();
132
+ expect(getActiveTrip("tok_b")).toBeDefined();
133
+ expect(mockFetch).toHaveBeenCalledTimes(1);
134
+ expect(String(mockFetch.mock.calls[0][0])).toContain("/api/badge/report");
135
+ });
136
+ it("API report includes correct event_type for outcome", () => {
137
+ mockFetch.mockClear();
138
+ onTripStarted("tok_a", "amazon.com");
139
+ onIdentityPresented("tok_a", "amazon.com");
140
+ // Move to new merchant — resolves trip A as "accepted"
141
+ onTripStarted("tok_b", "target.com");
142
+ // Check the fetch call for trip A resolution
143
+ const reportCalls = mockFetch.mock.calls.filter((c) => String(c[0]).includes("/api/badge/report"));
144
+ expect(reportCalls.length).toBeGreaterThanOrEqual(1);
145
+ const body = JSON.parse(reportCalls[0][1].body);
146
+ expect(body.event_type).toBe("trip_success");
147
+ expect(body.detail).toBe("agent_moved_to_new_merchant");
148
+ });
149
+ });
150
+ //# sourceMappingURL=sampling.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampling.test.js","sourceRoot":"","sources":["../src/sampling.test.ts"],"names":[],"mappings":"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"}
@@ -12,10 +12,24 @@ export interface IdentityResult {
12
12
  merchant?: string;
13
13
  instructions?: string;
14
14
  message?: string;
15
+ /** Internal: activation flow — agent should display this to user */
16
+ activation_required?: boolean;
17
+ /** UCP: merchant supports io.payclaw.common.identity */
18
+ ucpCapable?: boolean;
19
+ /** UCP: merchant requires PayClaw credential */
20
+ requiredByMerchant?: boolean;
21
+ /** UCP: checkout patch to merge into checkout payload */
22
+ checkoutPatch?: Record<string, unknown>;
23
+ /** UCP: warning when version mismatch etc. */
24
+ ucpWarning?: string;
15
25
  }
16
- export declare function getAgentIdentity(merchant?: string): Promise<IdentityResult>;
26
+ /**
27
+ * Get agent identity token — Badge by PayClaw.
28
+ * When no consent key exists: initiates device flow, returns activation instructions,
29
+ * polls in background. On approval, stores key. Next call uses stored key.
30
+ */
31
+ export declare function getAgentIdentity(merchant?: string, merchantUrl?: string): Promise<IdentityResult>;
17
32
  /**
18
33
  * Format identity result as human-readable text for CLI/agent display.
19
- * Claude reads this and relays key info naturally to the user.
20
34
  */
21
35
  export declare function formatIdentityResponse(r: IdentityResult): string;
@@ -1,18 +1,107 @@
1
+ // Canonical: badge-server | Synced: PRD-3 | mcp-server syncs from here
1
2
  import * as api from "../api/client.js";
3
+ import { getStoredConsentKey } from "../lib/storage.js";
4
+ import { initiateDeviceAuth, pollForApproval } from "../lib/device-auth.js";
5
+ import { fetchUCPManifest, findPayClawCapability, isVersionCompatible } from "../lib/ucp-manifest.js";
2
6
  const MOCK_TOKEN_PREFIX = "pc_v1_sand";
7
+ /** Must match the kid in the JWKS published at payclaw.io/.well-known/ucp (BUILD 3 / PRD-1) */
8
+ const PAYCLAW_KID = "payclaw-badge-v1";
3
9
  function getMockDisclosure(scope = "BROWSE") {
4
10
  return `This agent is using PayClaw Badge: Agent Intent for Ecommerce. The principal user token is a SHA-256 starting ${MOCK_TOKEN_PREFIX}***. Intent has been expressly user-authorized for this session for [${scope}]. For inquiries, please message agent_identity@payclaw.io`;
5
11
  }
6
- export async function getAgentIdentity(merchant) {
7
- if (!process.env.PAYCLAW_API_KEY) {
12
+ /** Build disclosure from OAuth token prefix (matches app disclosure format). */
13
+ function getDisclosureFromToken(token, scope = "BROWSE") {
14
+ const prefix = token.slice(0, 11);
15
+ return `This agent is using PayClaw Badge: Agent Intent for Ecommerce. The principal user token is a SHA-256 starting ${prefix}***. Intent has been expressly user-authorized for this session for [${scope}]. For inquiries, please message agent_identity@payclaw.io`;
16
+ }
17
+ /** Build identity result from OAuth token (when API doesn't accept OAuth Bearer yet). */
18
+ function identityFromOAuthToken(token, _assuranceLevel, merchant, assumeVerified = true) {
19
+ return {
20
+ product_name: "PayClaw Badge",
21
+ status: assumeVerified ? "active" : "pending",
22
+ agent_disclosure: getDisclosureFromToken(token),
23
+ verification_token: token,
24
+ trust_url: "https://payclaw.io/trust",
25
+ contact: "agent_identity@payclaw.io",
26
+ principal_verified: assumeVerified,
27
+ mfa_confirmed: false,
28
+ spend_available: false,
29
+ spend_cta: "For agent payments, use @payclaw/mcp-server — payclaw.io/docs",
30
+ merchant,
31
+ };
32
+ }
33
+ let pendingActivation = null;
34
+ /**
35
+ * Get agent identity token — Badge by PayClaw.
36
+ * When no consent key exists: initiates device flow, returns activation instructions,
37
+ * polls in background. On approval, stores key. Next call uses stored key.
38
+ */
39
+ export async function getAgentIdentity(merchant, merchantUrl) {
40
+ const consentKey = getStoredConsentKey();
41
+ let result;
42
+ // Backward compat: PAYCLAW_API_KEY set → use it, device flow never triggers
43
+ if (consentKey && process.env.PAYCLAW_API_KEY) {
44
+ result = await callWithKey(consentKey, merchant);
45
+ }
46
+ else if (!consentKey) {
47
+ // No key: initiate device flow (reuse pending to avoid duplicate pollers)
48
+ if (pendingActivation)
49
+ return pendingActivation;
50
+ const p = startActivationFlow(merchant);
51
+ pendingActivation = p;
52
+ try {
53
+ result = await p;
54
+ }
55
+ finally {
56
+ pendingActivation = null;
57
+ }
58
+ }
59
+ else {
60
+ // Key from file/memory (OAuth token from device flow)
61
+ result = await callWithOAuthToken(consentKey, merchant);
62
+ }
63
+ // UCP enrichment: check merchant manifest when merchantUrl provided
64
+ // Skip for mock/sandbox tokens — don't generate checkoutPatch for non-real credentials
65
+ if (merchantUrl &&
66
+ result.verification_token &&
67
+ !result.activation_required &&
68
+ !result.verification_token.startsWith(MOCK_TOKEN_PREFIX)) {
69
+ result = await enrichWithUCP(result, merchantUrl);
70
+ }
71
+ return result;
72
+ }
73
+ async function enrichWithUCP(result, merchantUrl) {
74
+ const manifest = await fetchUCPManifest(merchantUrl);
75
+ if (!manifest) {
76
+ return { ...result, ucpCapable: false };
77
+ }
78
+ const capability = findPayClawCapability(manifest);
79
+ if (!capability) {
80
+ return { ...result, ucpCapable: false };
81
+ }
82
+ if (!isVersionCompatible(capability.version)) {
8
83
  return {
9
- product_name: "PayClaw Badge",
10
- status: "error",
11
- message: "PAYCLAW_API_KEY is not set. Get your key at payclaw.io/dashboard/badge",
84
+ ...result,
85
+ ucpCapable: false,
86
+ ucpWarning: `version mismatch: merchant declares ${capability.version}`,
12
87
  };
13
88
  }
89
+ const checkoutPatch = {
90
+ "io.payclaw.common.identity": {
91
+ token: result.verification_token,
92
+ kid: PAYCLAW_KID,
93
+ },
94
+ };
95
+ return {
96
+ ...result,
97
+ ucpCapable: true,
98
+ requiredByMerchant: capability.required,
99
+ checkoutPatch,
100
+ instructions: "Merge checkoutPatch into your checkout payload, then call payclaw_reportBadgePresented with the merchantUrl and token.",
101
+ };
102
+ }
103
+ async function callWithKey(apiKey, merchant) {
14
104
  if (!api.isApiMode()) {
15
- // Mock mode — return sandbox identity for local testing
16
105
  return {
17
106
  product_name: "PayClaw Badge",
18
107
  status: "active",
@@ -42,25 +131,103 @@ export async function getAgentIdentity(merchant) {
42
131
  };
43
132
  }
44
133
  }
134
+ async function callWithOAuthToken(token, merchant) {
135
+ if (!api.isApiMode()) {
136
+ return identityFromOAuthToken(token, undefined, merchant);
137
+ }
138
+ try {
139
+ const result = await api.getAgentIdentityWithToken(api.getBaseUrl(), token, merchant);
140
+ return {
141
+ product_name: "PayClaw Badge",
142
+ status: "active",
143
+ merchant: merchant || undefined,
144
+ ...result,
145
+ };
146
+ }
147
+ catch {
148
+ // API call failed — do not mark as verified when falling back to local identity
149
+ return identityFromOAuthToken(token, undefined, merchant, false);
150
+ }
151
+ }
152
+ async function startActivationFlow(merchant) {
153
+ try {
154
+ const deviceAuth = await initiateDeviceAuth();
155
+ const message = [
156
+ "[PayClaw MCP Server initializing...]",
157
+ "",
158
+ "🛡️ Merchants block anonymous bots. PayClaw proves your agent is authorized.",
159
+ "🔗 To issue your agent's Consent Key, we need your approval.",
160
+ "",
161
+ `👉 Go to: ${deviceAuth.verification_uri}`,
162
+ `🔑 Enter code: ${deviceAuth.user_code}`,
163
+ "",
164
+ "[⏳ Waiting for your approval...]",
165
+ ].join("\n");
166
+ // Start polling in background — do not await
167
+ pollForApproval(deviceAuth.device_code, deviceAuth.interval, deviceAuth.expires_in, () => {
168
+ process.stderr.write([
169
+ "",
170
+ "[✅ Approval received]",
171
+ "",
172
+ "🎉 Consent Key generated and stored securely.",
173
+ "🐾 Avatar assigned: Starter Ghost (0 trips)",
174
+ "🔐 Your agent is now an authorized actor.",
175
+ "",
176
+ "Ready to shop. Agents are not bots.",
177
+ "",
178
+ ].join("\n"));
179
+ }).catch(() => {
180
+ // Poll failed (expired, etc.) — user will need to retry
181
+ });
182
+ return {
183
+ product_name: "PayClaw Badge",
184
+ status: "activation_required",
185
+ activation_required: true,
186
+ message,
187
+ merchant: merchant || undefined,
188
+ };
189
+ }
190
+ catch (err) {
191
+ return {
192
+ product_name: "PayClaw Badge",
193
+ status: "error",
194
+ message: err instanceof Error ? err.message : String(err),
195
+ };
196
+ }
197
+ }
45
198
  /**
46
199
  * Format identity result as human-readable text for CLI/agent display.
47
- * Claude reads this and relays key info naturally to the user.
48
200
  */
49
201
  export function formatIdentityResponse(r) {
202
+ if (r.activation_required && r.message) {
203
+ return r.message;
204
+ }
50
205
  if (r.status === "error") {
51
206
  return `✗ BADGE ERROR\n\n ${r.message}`;
52
207
  }
53
208
  const lines = [
54
209
  `✓ DECLARED — Your agent is now an authorized actor`,
55
210
  ``,
56
- ` Token: ${r.verification_token ? r.verification_token.slice(0, 10) + '**' : 'N/A'}`,
57
- ` Principal: ${r.principal_verified ? 'Verified ✓' : 'Unverified'}`,
211
+ ` Token: ${r.verification_token ? r.verification_token.slice(0, 10) + "**" : "N/A"}`,
212
+ ` Principal: ${r.principal_verified ? "Verified ✓" : "Unverified"}`,
58
213
  ` Scope: [BROWSE]`,
59
214
  ];
60
215
  if (r.merchant) {
61
216
  lines.push(` Merchant: ${r.merchant}`);
62
217
  }
63
- lines.push(` Status: ACTIVE`, ` Trust: ${r.trust_url || 'https://payclaw.io/trust'}`, ``, ` Disclosure (present to merchants):`, ` "${r.agent_disclosure}"`);
218
+ lines.push(` Status: ACTIVE`, ` Trust: ${r.trust_url || "https://payclaw.io/trust"}`, ``, ` Disclosure (present to merchants):`, ` "${r.agent_disclosure}"`);
219
+ if (r.ucpCapable) {
220
+ lines.push(``, ` UCP: Supported`, ` Required: ${r.requiredByMerchant ? "Yes" : "No"}`);
221
+ if (r.instructions) {
222
+ lines.push(` Action: ${r.instructions}`);
223
+ }
224
+ }
225
+ else if (r.ucpCapable === false) {
226
+ lines.push(``, ` UCP: Not supported`);
227
+ if (r.ucpWarning) {
228
+ lines.push(` Warning: ${r.ucpWarning}`);
229
+ }
230
+ }
64
231
  if (r.spend_available) {
65
232
  lines.push(``, ` 💳 Spend is available — call payclaw_getCard when ready to pay.`);
66
233
  }
@@ -68,8 +235,8 @@ export function formatIdentityResponse(r) {
68
235
  lines.push(``, ` ℹ️ ${r.spend_cta}`);
69
236
  }
70
237
  else {
71
- lines.push(``, ` ℹ️ Identity only. Fund your wallet at payclaw.io to enable payments.`);
238
+ lines.push(``, ` ℹ️ Identity only. For agent payments, use @payclaw/mcp-server — payclaw.io/docs`);
72
239
  }
73
- return lines.join('\n');
240
+ return lines.join("\n");
74
241
  }
75
242
  //# sourceMappingURL=getAgentIdentity.js.map