@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/README.md CHANGED
@@ -17,7 +17,7 @@ Add to your MCP client config:
17
17
  "command": "npx",
18
18
  "args": ["-y", "@payclaw/badge"],
19
19
  "env": {
20
- "PAYCLAW_API_URL": "https://api.payclaw.io"
20
+ "PAYCLAW_API_URL": "https://payclaw.io"
21
21
  }
22
22
  }
23
23
  }
@@ -64,6 +64,8 @@ The agent presents this disclosure to merchants. Merchants see a verified identi
64
64
 
65
65
  ## How It Works
66
66
 
67
+ ### First use (device auth)
68
+
67
69
  ```
68
70
  1. Your agent calls payclaw_getAgentIdentity
69
71
  2. No key? Device auth flow triggers — code + URL appear in terminal
@@ -72,7 +74,31 @@ The agent presents this disclosure to merchants. Merchants see a verified identi
72
74
  5. Every subsequent call uses the stored key automatically
73
75
  ```
74
76
 
75
- No card is issued. No money moves. Badge is the identity layer — the credential that lets authorized agents through while bot defenses stay intact.
77
+ ### UCP-aware identity (with merchantUrl)
78
+
79
+ ```
80
+ 1. Agent calls payclaw_getAgentIdentity({ merchantUrl: 'https://store.com' })
81
+ 2. PayClaw fetches store.com/.well-known/ucp manifest
82
+ 3. If merchant declares io.payclaw.common.identity → returns checkoutPatch
83
+ 4. Agent merges checkoutPatch into checkout payload
84
+ 5. Agent calls payclaw_reportBadgePresented({ merchantUrl, verification_token })
85
+ 6. Merchant calls verify(token) → gets PayClawIdentity
86
+ ```
87
+
88
+ If the merchant doesn't support UCP, a valid token is still returned — nothing breaks. No card is issued. No money moves. Badge is the identity layer — the credential that lets authorized agents through while bot defenses stay intact.
89
+
90
+ ### Extended Auth (optional)
91
+
92
+ When enabled, PayClaw checks back with your agent 7 seconds after badge presentation to confirm whether the merchant accepted or denied. Results are logged to your dashboard.
93
+
94
+ ```json
95
+ "env": {
96
+ "PAYCLAW_API_URL": "https://payclaw.io",
97
+ "PAYCLAW_EXTENDED_AUTH": "true"
98
+ }
99
+ ```
100
+
101
+ Without it, your agent reports outcomes via `payclaw_reportBadgeOutcome` when it knows the result.
76
102
 
77
103
  ## Tools
78
104
 
@@ -80,6 +106,46 @@ No card is issued. No money moves. Badge is the identity layer — the credentia
80
106
  |------|-------------|
81
107
  | `payclaw_getAgentIdentity` | Declare identity, get UCP-compatible verification token |
82
108
  | `payclaw_reportBadgePresented` | Signal that you presented your Badge to a merchant |
109
+ | `payclaw_reportBadgeOutcome` | Report whether merchant accepted or denied the badge |
110
+ | `payclaw_reportBadgeNotPresented` | Report that the badge was not presented |
111
+
112
+ ## For Merchants: Verify a Badge
113
+
114
+ Badge is the package merchants install for local JWT verification — no API call to PayClaw, no uptime dependency.
115
+
116
+ ```bash
117
+ npm install @payclaw/badge
118
+ ```
119
+
120
+ ```typescript
121
+ import { verify } from '@payclaw/badge/verify'
122
+
123
+ const identity = await verify(req.body['io.payclaw.common.identity']?.token)
124
+
125
+ if (identity) {
126
+ // Verified — identity.userId, identity.agentId, identity.scopes, etc.
127
+ // Apply tier pricing, skip CAPTCHAs, fast-track checkout
128
+ }
129
+ // null = no badge or invalid — proceed as guest
130
+ ```
131
+
132
+ `verify()` fetches PayClaw's JWKS once, caches for 1 hour, and verifies the ES256 signature locally via Web Crypto API. Zero runtime dependencies. Works in Node.js 18+ and Cloudflare Workers. Never throws — returns `null` on any error.
133
+
134
+ Full verification docs + Python example: [github.com/payclaw/ucp-agent-badge](https://github.com/payclaw/ucp-agent-badge#merchant-verification)
135
+
136
+ ---
137
+
138
+ ## What's New (v0.8.0)
139
+
140
+ | Capability | Description |
141
+ |---|---|
142
+ | `verify()` export | Merchant-side JWT verification — `import { verify } from '@payclaw/badge/verify'`. Zero dependencies, Web Crypto only. |
143
+ | UCP-aware `getAgentIdentity` | Pass `merchantUrl` — fetches merchant manifest, returns `checkoutPatch` when `io.payclaw.common.identity` is declared |
144
+ | `reportBadgePresented` with `merchantUrl` | Preferred over `merchant`; includes optional `checkoutSessionId` for UCP session tracking |
145
+ | SSRF-protected manifest fetcher | HTTPS-only, private IP blocking, 5-minute domain cache |
146
+ | Trip lifecycle hardening | `onServerClose` resolves as `inconclusive`; orphan token recovery on restart |
147
+
148
+ ---
83
149
 
84
150
  ## Need Payment Too?
85
151
 
@@ -1,3 +1,10 @@
1
1
  import type { AgentIdentityResponse } from "../types.js";
2
2
  export declare function getAgentIdentity(sessionId?: string, merchant?: string): Promise<AgentIdentityResponse>;
3
3
  export declare function isApiMode(): boolean;
4
+ /** Base URL for API calls. Defaults to https://payclaw.io. Validates HTTPS for token safety. */
5
+ export declare function getBaseUrl(): string;
6
+ /**
7
+ * Call agent-identity with a Bearer token (API key or OAuth access token).
8
+ * Used when consent key comes from device flow (OAuth token) instead of PAYCLAW_API_KEY.
9
+ */
10
+ export declare function getAgentIdentityWithToken(baseUrl: string, token: string, merchant?: string): Promise<AgentIdentityResponse>;
@@ -1,3 +1,4 @@
1
+ import { getStoredConsentKey } from "../lib/storage.js";
1
2
  class PayClawApiError extends Error {
2
3
  statusCode;
3
4
  constructor(message, statusCode) {
@@ -9,7 +10,7 @@ class PayClawApiError extends Error {
9
10
  const REQUEST_TIMEOUT_MS = 30_000;
10
11
  function getConfig() {
11
12
  const baseUrl = process.env.PAYCLAW_API_URL;
12
- const apiKey = process.env.PAYCLAW_API_KEY;
13
+ const apiKey = getStoredConsentKey();
13
14
  if (!baseUrl)
14
15
  throw new PayClawApiError("PayClaw API URL is not configured.");
15
16
  if (!apiKey)
@@ -44,7 +45,13 @@ async function request(url, init) {
44
45
  clearTimeout(timeout);
45
46
  }
46
47
  if (res.status === 401) {
47
- throw new PayClawApiError("Authentication failed. Check your API key.", 401);
48
+ const authHeader = init.headers instanceof Headers
49
+ ? init.headers.get("Authorization")
50
+ : init.headers?.Authorization;
51
+ const isBearer = typeof authHeader === "string" && authHeader.startsWith("Bearer ");
52
+ throw new PayClawApiError(isBearer
53
+ ? "Authentication failed. Check your access token or OAuth credentials."
54
+ : "Authentication failed. Check your API key.", 401);
48
55
  }
49
56
  if (!res.ok) {
50
57
  let body;
@@ -71,6 +78,30 @@ export async function getAgentIdentity(sessionId, merchant) {
71
78
  });
72
79
  }
73
80
  export function isApiMode() {
74
- return !!process.env.PAYCLAW_API_URL;
81
+ return !!process.env.PAYCLAW_API_URL || !!getStoredConsentKey();
82
+ }
83
+ /** Base URL for API calls. Defaults to https://payclaw.io. Validates HTTPS for token safety. */
84
+ export function getBaseUrl() {
85
+ const url = process.env.PAYCLAW_API_URL;
86
+ if (url && url.trim().length > 0) {
87
+ const trimmed = url.trim().replace(/\/+$/, "");
88
+ if (trimmed.startsWith("https://") || trimmed.startsWith("http://localhost")) {
89
+ return trimmed;
90
+ }
91
+ }
92
+ return "https://payclaw.io";
93
+ }
94
+ /**
95
+ * Call agent-identity with a Bearer token (API key or OAuth access token).
96
+ * Used when consent key comes from device flow (OAuth token) instead of PAYCLAW_API_KEY.
97
+ */
98
+ export async function getAgentIdentityWithToken(baseUrl, token, merchant) {
99
+ return request(`${baseUrl}/api/agent-identity`, {
100
+ method: "POST",
101
+ headers: authHeaders(token),
102
+ body: JSON.stringify({
103
+ ...(merchant ? { merchant } : {}),
104
+ }),
105
+ });
75
106
  }
76
107
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export { verify, type PayClawIdentity, type VerifyOptions } from "./verify.js";
package/dist/index.js CHANGED
@@ -6,9 +6,10 @@ import { getAgentIdentity, formatIdentityResponse } from "./tools/getAgentIdenti
6
6
  import { initSampling, onTripStarted, onServerClose, reportOutcomeFromAgent, } from "./sampling.js";
7
7
  import { handleReportBadgePresented } from "./lib/report-badge-presented-handler.js";
8
8
  import { reportBadgeNotPresented } from "./lib/report-badge.js";
9
+ import { getAuthMode } from "./lib/storage.js";
9
10
  const server = new McpServer({
10
11
  name: "payclaw-badge",
11
- version: "0.7.0",
12
+ version: "0.8.0",
12
13
  });
13
14
  server.tool("payclaw_getAgentIdentity", `You are an authorized actor, not a bot. Prove it before you shop.
14
15
 
@@ -20,34 +21,43 @@ When a merchant challenges you, present the agent_disclosure text from the respo
20
21
 
21
22
  No card is issued. No money moves. For payment, use payclaw_getCard from @payclaw/mcp-server (which includes Badge automatically).`, {
22
23
  merchant: z.string().max(200).optional().describe("The merchant or website the agent intends to visit (e.g., 'starbucks.com', 'Instacart')"),
23
- }, async ({ merchant }) => {
24
- const result = await getAgentIdentity(merchant);
24
+ merchantUrl: z.string().max(500).optional().describe("Merchant base URL (e.g., 'https://starbucks.com'). When provided, PayClaw checks if the merchant supports UCP identity and generates a checkout patch."),
25
+ }, async ({ merchant, merchantUrl }) => {
26
+ const result = await getAgentIdentity(merchant, merchantUrl);
25
27
  // Track trip start for sampling (DQ-54)
26
28
  if (result.verification_token) {
27
29
  onTripStarted(result.verification_token, merchant || "unknown");
28
30
  }
29
31
  const formatted = formatIdentityResponse(result);
32
+ // Omit internal fields from JSON for activation_required
33
+ const { activation_required: _, ...publicResult } = result;
30
34
  return {
31
35
  content: [
32
36
  { type: "text", text: formatted },
33
- { type: "text", text: `\n---\n${JSON.stringify(result, null, 2)}` },
37
+ { type: "text", text: `\n---\n${JSON.stringify(publicResult, null, 2)}` },
34
38
  ],
35
39
  };
36
40
  });
37
- server.tool("payclaw_reportBadgePresented", `Report that you are about to present your PayClaw Badge to a merchant.
38
-
39
- Call this AFTER payclaw_getAgentIdentity and BEFORE including the badge token in a
40
- merchant request. This tells PayClaw you are actively using your badge at a specific
41
- merchant — it starts the outcome tracking window and logs the presentation event.
41
+ server.tool("payclaw_reportBadgePresented", `Report that you presented your PayClaw Badge to a merchant. Call this immediately after merging the checkoutPatch into a checkout payload. Required for PayClaw to record the trip.
42
42
 
43
43
  When Extended Auth is enabled, PayClaw checks back 7 seconds later. Otherwise, call payclaw_reportBadgeOutcome when you know the result.`, {
44
44
  verification_token: z.string().describe("The verification_token returned by payclaw_getAgentIdentity"),
45
- merchant: z.string().max(200).describe("The merchant or website where you are presenting the badge (e.g., 'starbucks.com')"),
45
+ merchant: z.string().max(200).optional().describe("The merchant name (e.g., 'starbucks.com'). Provide merchantUrl or merchant."),
46
+ merchantUrl: z.string().max(500).optional().describe("The merchant base URL (e.g., 'https://starbucks.com'). Preferred over merchant."),
46
47
  context: z
47
48
  .enum(["arrival", "addtocart", "checkout", "other"])
48
49
  .optional()
49
- .describe("Optional: when Extended Auth is enabled, in what state you presented (arrival, addtocart, checkout, other)"),
50
- }, async ({ verification_token, merchant, context }) => handleReportBadgePresented(verification_token, merchant, context));
50
+ .describe("Optional: in what state you presented (arrival, addtocart, checkout, other)"),
51
+ checkoutSessionId: z.string().optional().describe("UCP checkout session ID if available"),
52
+ }, async ({ verification_token, merchant, merchantUrl, context, checkoutSessionId }) => {
53
+ const resolvedMerchant = merchantUrl || merchant;
54
+ if (!resolvedMerchant) {
55
+ return {
56
+ content: [{ type: "text", text: "✗ Error: merchantUrl or merchant is required." }],
57
+ };
58
+ }
59
+ return handleReportBadgePresented(verification_token, resolvedMerchant, context, checkoutSessionId);
60
+ });
51
61
  server.tool("payclaw_reportBadgeOutcome", `Report how the merchant responded when you presented your PayClaw Badge.
52
62
 
53
63
  Call this after payclaw_reportBadgePresented when you know whether the merchant accepted or denied you. Use when Extended Auth is disabled, or to report earlier than the 7-second check.`, {
@@ -97,9 +107,14 @@ async function main() {
97
107
  process.exit(0);
98
108
  });
99
109
  process.stderr.write("PayClaw Badge server running on stdio\n");
110
+ if (process.env.VITEST !== "true") {
111
+ process.stderr.write(`[PayClaw] Auth: ${getAuthMode()}\n`);
112
+ }
100
113
  }
101
114
  main().catch((err) => {
102
115
  process.stderr.write(`Fatal error: ${err}\n`);
103
116
  process.exit(1);
104
117
  });
118
+ // Re-export verify for merchant-side use: import { verify } from '@payclaw/badge'
119
+ export { verify } from "./verify.js";
105
120
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export interface DeviceAuthResponse {
2
+ device_code: string;
3
+ user_code: string;
4
+ verification_uri: string;
5
+ verification_uri_complete: string;
6
+ expires_in: number;
7
+ interval: number;
8
+ }
9
+ /**
10
+ * Initiate device authorization flow.
11
+ * POST /api/oauth/device/authorize — no auth required.
12
+ */
13
+ export declare function initiateDeviceAuth(): Promise<DeviceAuthResponse>;
14
+ export interface TokenSuccessResponse {
15
+ access_token: string;
16
+ token_type: string;
17
+ expires_in: number;
18
+ refresh_token?: string;
19
+ scope?: string;
20
+ credential_provider?: string;
21
+ badge_status?: string;
22
+ assurance_level?: string;
23
+ }
24
+ export type ApprovalCallback = (tokens: TokenSuccessResponse) => void;
25
+ /**
26
+ * Poll token endpoint until user approves or timeout.
27
+ * On success: stores consent key and calls onApproval.
28
+ * RFC 8628: slow_down adds 5s permanently to interval.
29
+ */
30
+ export declare function pollForApproval(deviceCode: string, interval: number, expiresIn: number, onApproval?: ApprovalCallback): Promise<TokenSuccessResponse>;
@@ -0,0 +1,92 @@
1
+ // Canonical: badge-server | Synced: 0.7.3 | Do not edit in mcp-server
2
+ import { storeConsentKey } from "./storage.js";
3
+ const DEFAULT_API_URL = "https://payclaw.io";
4
+ const FETCH_TIMEOUT_MS = 10_000;
5
+ function getBaseUrl() {
6
+ const url = process.env.PAYCLAW_API_URL;
7
+ if (url && url.trim().length > 0) {
8
+ const trimmed = url.trim().replace(/\/+$/, "");
9
+ try {
10
+ const parsed = new URL(trimmed);
11
+ const isLoopback = ["localhost", "127.0.0.1", "::1"].includes(parsed.hostname);
12
+ if (parsed.protocol === "https:" || (parsed.protocol === "http:" && isLoopback)) {
13
+ return trimmed;
14
+ }
15
+ }
16
+ catch {
17
+ // fall through to DEFAULT_API_URL
18
+ }
19
+ }
20
+ return DEFAULT_API_URL;
21
+ }
22
+ function sleep(ms) {
23
+ return new Promise((resolve) => setTimeout(resolve, ms));
24
+ }
25
+ async function fetchWithTimeout(url, init) {
26
+ const controller = new AbortController();
27
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
28
+ try {
29
+ return await fetch(url, { ...init, signal: controller.signal });
30
+ }
31
+ finally {
32
+ clearTimeout(timeout);
33
+ }
34
+ }
35
+ /**
36
+ * Initiate device authorization flow.
37
+ * POST /api/oauth/device/authorize — no auth required.
38
+ */
39
+ export async function initiateDeviceAuth() {
40
+ const baseUrl = getBaseUrl();
41
+ const res = await fetchWithTimeout(`${baseUrl}/api/oauth/device/authorize`, {
42
+ method: "POST",
43
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
44
+ body: new URLSearchParams({ scope: "ucp:scopes:checkout_session" }),
45
+ });
46
+ if (!res.ok) {
47
+ const text = await res.text();
48
+ throw new Error(res.status === 429 ? "Rate limited. Try again in a few minutes." : text || "Device auth failed");
49
+ }
50
+ const data = (await res.json());
51
+ if (!data.device_code || !data.user_code || !data.verification_uri) {
52
+ throw new Error("Invalid device auth response");
53
+ }
54
+ const interval = Math.max(1, Number(data.interval) || 5);
55
+ const expiresIn = Math.max(1, Number(data.expires_in) || 600);
56
+ return { ...data, interval, expires_in: expiresIn };
57
+ }
58
+ /**
59
+ * Poll token endpoint until user approves or timeout.
60
+ * On success: stores consent key and calls onApproval.
61
+ * RFC 8628: slow_down adds 5s permanently to interval.
62
+ */
63
+ export async function pollForApproval(deviceCode, interval, expiresIn, onApproval) {
64
+ const baseUrl = getBaseUrl();
65
+ const deadline = Date.now() + expiresIn * 1000;
66
+ let currentInterval = interval;
67
+ while (Date.now() < deadline) {
68
+ await sleep(currentInterval * 1000);
69
+ const res = await fetchWithTimeout(`${baseUrl}/api/oauth/token`, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
72
+ body: new URLSearchParams({
73
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
74
+ device_code: deviceCode,
75
+ }),
76
+ });
77
+ const data = (await res.json());
78
+ if (res.ok && data.access_token) {
79
+ await storeConsentKey(data.access_token);
80
+ onApproval?.(data);
81
+ return data;
82
+ }
83
+ if (data.error === "slow_down") {
84
+ currentInterval += 5;
85
+ }
86
+ else if (data.error !== "authorization_pending") {
87
+ throw new Error(data.error ?? "Token request failed");
88
+ }
89
+ }
90
+ throw new Error("expired_token");
91
+ }
92
+ //# sourceMappingURL=device-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-auth.js","sourceRoot":"","sources":["../../src/lib/device-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Parse agent response to sampling prompt into outcome bucket.
3
+ * Extracted for testability (BUG-01.1). Synced from mcp-server.
4
+ */
5
+ export declare function parseResponse(text: string): "accepted" | "denied" | "inconclusive";
@@ -0,0 +1,38 @@
1
+ // Canonical: badge-server | Synced: 0.7.3 | Do not edit in mcp-server
2
+ /**
3
+ * Parse agent response to sampling prompt into outcome bucket.
4
+ * Extracted for testability (BUG-01.1). Synced from mcp-server.
5
+ */
6
+ const FAILURE_SIGNALS = [
7
+ "yes",
8
+ "blocked",
9
+ "denied",
10
+ "failed",
11
+ "403",
12
+ "error",
13
+ "rejected",
14
+ "banned",
15
+ "forbidden",
16
+ "captcha",
17
+ "stopped",
18
+ ];
19
+ export function parseResponse(text) {
20
+ if (!text || text.trim().length === 0)
21
+ return "inconclusive";
22
+ const lower = text.toLowerCase().trim();
23
+ // "no, I was not denied" = accepted (check before denial signals)
24
+ if (lower.includes("not denied") || lower.includes("wasn't denied"))
25
+ return "accepted";
26
+ // "yesterday" contains "yes" — exclude false positives (including punctuated variants)
27
+ const normalized = lower.replace(/[.,!?]+$/, "");
28
+ if (normalized === "yesterday")
29
+ return "inconclusive";
30
+ // Denial signals first — "no, I was blocked" must be denied (before any "no" check)
31
+ if (FAILURE_SIGNALS.some((s) => lower.includes(s)))
32
+ return "denied";
33
+ // "no" alone or "no" variants = accepted (boundary-aware to avoid "no, I was blocked")
34
+ if (/^no(?:[.,!\s]|$)/i.test(lower))
35
+ return "accepted";
36
+ return "inconclusive";
37
+ }
38
+ //# sourceMappingURL=parse-outcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-outcome.js","sourceRoot":"","sources":["../../src/lib/parse-outcome.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE;;;GAGG;AAEH,MAAM,eAAe,GAAG;IACtB,KAAK;IACL,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,KAAK;IACL,OAAO;IACP,UAAU;IACV,QAAQ;IACR,WAAW;IACX,SAAS;IACT,SAAS;CACV,CAAC;AAEF,MAAM,UAAU,aAAa,CAC3B,IAAY;IAEZ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAC;IAE7D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAExC,kEAAkE;IAClE,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC;QACjE,OAAO,UAAU,CAAC;IAEpB,uFAAuF;IACvF,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,UAAU,KAAK,WAAW;QAAE,OAAO,cAAc,CAAC;IAEtD,oFAAoF;IACpF,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEpE,uFAAuF;IACvF,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC;IAEvD,OAAO,cAAc,CAAC;AACxB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseResponse } from "./parse-outcome.js";
3
+ describe("parseResponse", () => {
4
+ it('returns "denied" for "yes"', () => {
5
+ expect(parseResponse("yes")).toBe("denied");
6
+ });
7
+ it('returns "accepted" for "no"', () => {
8
+ expect(parseResponse("no")).toBe("accepted");
9
+ });
10
+ it('returns "accepted" for "NO"', () => {
11
+ expect(parseResponse("NO")).toBe("accepted");
12
+ });
13
+ it('returns "denied" for "blocked"', () => {
14
+ expect(parseResponse("blocked")).toBe("denied");
15
+ });
16
+ it('returns "denied" for "403"', () => {
17
+ expect(parseResponse("403")).toBe("denied");
18
+ });
19
+ it('returns "accepted" for "no, I was not denied"', () => {
20
+ expect(parseResponse("no, I was not denied")).toBe("accepted");
21
+ });
22
+ it('returns "inconclusive" for empty string', () => {
23
+ expect(parseResponse("")).toBe("inconclusive");
24
+ });
25
+ it('returns "inconclusive" for gibberish', () => {
26
+ expect(parseResponse("maybe")).toBe("inconclusive");
27
+ });
28
+ it('returns "accepted" for "no."', () => {
29
+ expect(parseResponse("no.")).toBe("accepted");
30
+ });
31
+ it('returns "accepted" for "no,"', () => {
32
+ expect(parseResponse("no,")).toBe("accepted");
33
+ });
34
+ it('returns "denied" for contradictory response "no, I was blocked"', () => {
35
+ expect(parseResponse("no, I was blocked")).toBe("denied");
36
+ });
37
+ it('returns "inconclusive" for "yesterday"', () => {
38
+ expect(parseResponse("yesterday")).toBe("inconclusive");
39
+ });
40
+ it('returns "inconclusive" for "yesterday."', () => {
41
+ expect(parseResponse("yesterday.")).toBe("inconclusive");
42
+ });
43
+ it('returns "inconclusive" for "yesterday!"', () => {
44
+ expect(parseResponse("yesterday!")).toBe("inconclusive");
45
+ });
46
+ });
47
+ //# sourceMappingURL=parse-outcome.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-outcome.test.js","sourceRoot":"","sources":["../../src/lib/parse-outcome.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -2,7 +2,13 @@
2
2
  * Handler for payclaw_reportBadgePresented tool.
3
3
  * Extracted for testability (BUG-01.1 integration tests).
4
4
  */
5
- export declare function handleReportBadgePresented(verification_token: string, merchant: string, context?: "arrival" | "addtocart" | "checkout" | "other"): Promise<{
5
+ /**
6
+ * Idempotency (duplicate row prevention) and expired-token status
7
+ * (`status: 'expired_presentation'`) are enforced by the API server,
8
+ * not here. The MCP server is stateless — it has no DB access and
9
+ * cannot check trip_id uniqueness or decode token expiry authoritatively.
10
+ */
11
+ export declare function handleReportBadgePresented(verification_token: string, merchant: string, context?: "arrival" | "addtocart" | "checkout" | "other", checkoutSessionId?: string): Promise<{
6
12
  content: Array<{
7
13
  type: "text";
8
14
  text: string;
@@ -1,14 +1,25 @@
1
+ // Canonical: badge-server | Synced: 0.7.3 | Do not edit in mcp-server
1
2
  /**
2
3
  * Handler for payclaw_reportBadgePresented tool.
3
4
  * Extracted for testability (BUG-01.1 integration tests).
4
5
  */
5
6
  import { onIdentityPresented } from "../sampling.js";
6
7
  import { reportBadgePresented } from "./report-badge.js";
7
- export async function handleReportBadgePresented(verification_token, merchant, context) {
8
+ /**
9
+ * Idempotency (duplicate row prevention) and expired-token status
10
+ * (`status: 'expired_presentation'`) are enforced by the API server,
11
+ * not here. The MCP server is stateless — it has no DB access and
12
+ * cannot check trip_id uniqueness or decode token expiry authoritatively.
13
+ */
14
+ export async function handleReportBadgePresented(verification_token, merchant, context, checkoutSessionId) {
8
15
  onIdentityPresented(verification_token, merchant);
9
- await reportBadgePresented(verification_token, merchant, context);
16
+ await reportBadgePresented(verification_token, merchant, context, checkoutSessionId);
10
17
  return {
11
18
  content: [
19
+ {
20
+ type: "text",
21
+ text: JSON.stringify({ recorded: true }),
22
+ },
12
23
  {
13
24
  type: "text",
14
25
  text: [
@@ -1 +1 @@
1
- {"version":3,"file":"report-badge-presented-handler.js","sourceRoot":"","sources":["../../src/lib/report-badge-presented-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,kBAA0B,EAC1B,QAAgB,EAChB,OAAwD;IAExD,mBAAmB,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IAClD,MAAM,oBAAoB,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClE,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE;oBACJ,kCAAkC,QAAQ,EAAE;oBAC5C,EAAE;oBACF,eAAe,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI;oBAClD,eAAe,QAAQ,EAAE;oBACzB,iDAAiD;oBACjD,EAAE;oBACF,2DAA2D;oBAC3D,2BAA2B,kBAAkB,EAAE;iBAChD,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;SACF;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"report-badge-presented-handler.js","sourceRoot":"","sources":["../../src/lib/report-badge-presented-handler.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,kBAA0B,EAC1B,QAAgB,EAChB,OAAwD,EACxD,iBAA0B;IAE1B,mBAAmB,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IAClD,MAAM,oBAAoB,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;IACrF,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;aACzC;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE;oBACJ,kCAAkC,QAAQ,EAAE;oBAC5C,EAAE;oBACF,eAAe,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI;oBAClD,eAAe,QAAQ,EAAE;oBACzB,iDAAiD;oBACjD,EAAE;oBACF,2DAA2D;oBAC3D,2BAA2B,kBAAkB,EAAE;iBAChD,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * POST identity_presented to /api/badge/report.
3
- * Simplified for badge-server (no OAuth, just API key).
3
+ * Uses getStoredConsentKey for OAuth users; PAYCLAW_API_KEY for legacy.
4
+ * Synced from mcp-server (BUG-01.1).
4
5
  */
5
- export declare function reportBadgePresented(verificationToken: string, merchant: string, context?: "arrival" | "addtocart" | "checkout" | "other"): Promise<void>;
6
+ export declare function reportBadgePresented(verificationToken: string, merchant: string, context?: "arrival" | "addtocart" | "checkout" | "other", checkoutSessionId?: string): Promise<void>;
6
7
  export declare function reportBadgeNotPresented(verificationToken: string, merchant: string, reason: "abandoned" | "merchant_didnt_ask" | "other"): Promise<void>;