@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.
- package/README.md +68 -2
- package/dist/api/client.d.ts +7 -0
- package/dist/api/client.js +34 -3
- package/dist/api/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/device-auth.d.ts +30 -0
- package/dist/lib/device-auth.js +92 -0
- package/dist/lib/device-auth.js.map +1 -0
- package/dist/lib/parse-outcome.d.ts +5 -0
- package/dist/lib/parse-outcome.js +38 -0
- package/dist/lib/parse-outcome.js.map +1 -0
- package/dist/lib/parse-outcome.test.d.ts +1 -0
- package/dist/lib/parse-outcome.test.js +47 -0
- package/dist/lib/parse-outcome.test.js.map +1 -0
- package/dist/lib/report-badge-presented-handler.d.ts +7 -1
- package/dist/lib/report-badge-presented-handler.js +13 -2
- package/dist/lib/report-badge-presented-handler.js.map +1 -1
- package/dist/lib/report-badge.d.ts +3 -2
- package/dist/lib/report-badge.js +12 -8
- package/dist/lib/report-badge.js.map +1 -1
- package/dist/lib/report-badge.test.d.ts +1 -0
- package/dist/lib/report-badge.test.js +109 -0
- package/dist/lib/report-badge.test.js.map +1 -0
- package/dist/lib/storage.d.ts +17 -0
- package/dist/lib/storage.js +86 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/ucp-manifest.d.ts +32 -0
- package/dist/lib/ucp-manifest.js +117 -0
- package/dist/lib/ucp-manifest.js.map +1 -0
- package/dist/lib/ucp-manifest.test.d.ts +1 -0
- package/dist/lib/ucp-manifest.test.js +92 -0
- package/dist/lib/ucp-manifest.test.js.map +1 -0
- package/dist/sampling.d.ts +19 -1
- package/dist/sampling.js +74 -33
- package/dist/sampling.js.map +1 -1
- package/dist/sampling.test.d.ts +1 -0
- package/dist/sampling.test.js +150 -0
- package/dist/sampling.test.js.map +1 -0
- package/dist/tools/getAgentIdentity.d.ts +16 -2
- package/dist/tools/getAgentIdentity.js +179 -12
- package/dist/tools/getAgentIdentity.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/verify.d.ts +34 -0
- package/dist/verify.js +161 -0
- package/dist/verify.js.map +1 -0
- package/dist/verify.test.d.ts +1 -0
- package/dist/verify.test.js +177 -0
- package/dist/verify.test.js.map +1 -0
- package/package.json +10 -4
package/dist/lib/report-badge.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
+
// Canonical: badge-server | Synced: 0.7.3 | Do not edit in mcp-server
|
|
1
2
|
/**
|
|
2
3
|
* POST identity_presented to /api/badge/report.
|
|
3
|
-
*
|
|
4
|
+
* Uses getStoredConsentKey for OAuth users; PAYCLAW_API_KEY for legacy.
|
|
5
|
+
* Synced from mcp-server (BUG-01.1).
|
|
4
6
|
*/
|
|
7
|
+
import { getStoredConsentKey } from "./storage.js";
|
|
5
8
|
const DEFAULT_API_URL = "https://payclaw.io";
|
|
6
|
-
export async function reportBadgePresented(verificationToken, merchant, context) {
|
|
9
|
+
export async function reportBadgePresented(verificationToken, merchant, context, checkoutSessionId) {
|
|
7
10
|
const apiUrl = process.env.PAYCLAW_API_URL || DEFAULT_API_URL;
|
|
8
|
-
const
|
|
9
|
-
if (!
|
|
11
|
+
const key = getStoredConsentKey();
|
|
12
|
+
if (!key)
|
|
10
13
|
return;
|
|
11
14
|
try {
|
|
12
15
|
const res = await fetch(`${apiUrl}/api/badge/report`, {
|
|
13
16
|
method: "POST",
|
|
14
17
|
headers: {
|
|
15
|
-
Authorization: `Bearer ${
|
|
18
|
+
Authorization: `Bearer ${key}`,
|
|
16
19
|
"Content-Type": "application/json",
|
|
17
20
|
},
|
|
18
21
|
body: JSON.stringify({
|
|
@@ -20,6 +23,7 @@ export async function reportBadgePresented(verificationToken, merchant, context)
|
|
|
20
23
|
event_type: "identity_presented",
|
|
21
24
|
merchant,
|
|
22
25
|
...(context && { presentation_context: context }),
|
|
26
|
+
...(checkoutSessionId && { checkout_session_id: checkoutSessionId }),
|
|
23
27
|
}),
|
|
24
28
|
});
|
|
25
29
|
if (!res.ok) {
|
|
@@ -33,14 +37,14 @@ export async function reportBadgePresented(verificationToken, merchant, context)
|
|
|
33
37
|
}
|
|
34
38
|
export async function reportBadgeNotPresented(verificationToken, merchant, reason) {
|
|
35
39
|
const apiUrl = process.env.PAYCLAW_API_URL || DEFAULT_API_URL;
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
40
|
+
const key = getStoredConsentKey();
|
|
41
|
+
if (!key)
|
|
38
42
|
return;
|
|
39
43
|
try {
|
|
40
44
|
const res = await fetch(`${apiUrl}/api/badge/report`, {
|
|
41
45
|
method: "POST",
|
|
42
46
|
headers: {
|
|
43
|
-
Authorization: `Bearer ${
|
|
47
|
+
Authorization: `Bearer ${key}`,
|
|
44
48
|
"Content-Type": "application/json",
|
|
45
49
|
},
|
|
46
50
|
body: JSON.stringify({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report-badge.js","sourceRoot":"","sources":["../../src/lib/report-badge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"report-badge.js","sourceRoot":"","sources":["../../src/lib/report-badge.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,iBAAyB,EACzB,QAAgB,EAChB,OAAwD,EACxD,iBAA0B;IAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,eAAe,CAAC;IAC9D,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO;IAEjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,mBAAmB,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,GAAG,EAAE;gBAC9B,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,kBAAkB,EAAE,iBAAiB;gBACrC,UAAU,EAAE,oBAAoB;gBAChC,QAAQ;gBACR,GAAG,CAAC,OAAO,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,CAAC;gBACjD,GAAG,CAAC,iBAAiB,IAAI,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;aACrE,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wCAAwC,GAAG,CAAC,MAAM,MAAM,IAAI,IAAI,CACjE,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,iBAAyB,EACzB,QAAgB,EAChB,MAAoD;IAEpD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,eAAe,CAAC;IAC9D,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO;IAEjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,mBAAmB,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,GAAG,EAAE;gBAC9B,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,kBAAkB,EAAE,iBAAiB;gBACrC,UAAU,EAAE,qBAAqB;gBACjC,QAAQ;gBACR,MAAM;aACP,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2CAA2C,GAAG,CAAC,MAAM,MAAM,IAAI,IAAI,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { reportBadgePresented, reportBadgeNotPresented, } from "./report-badge.js";
|
|
3
|
+
import * as storage from "./storage.js";
|
|
4
|
+
vi.mock("./storage.js", () => ({
|
|
5
|
+
getStoredConsentKey: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
describe("reportBadgePresented", () => {
|
|
8
|
+
const mockFetch = vi.fn();
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.stubGlobal("fetch", mockFetch);
|
|
11
|
+
mockFetch.mockResolvedValue({ ok: true });
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.unstubAllGlobals();
|
|
15
|
+
vi.restoreAllMocks();
|
|
16
|
+
delete process.env.PAYCLAW_API_KEY;
|
|
17
|
+
delete process.env.PAYCLAW_API_URL;
|
|
18
|
+
});
|
|
19
|
+
it("POSTs when consent key available", async () => {
|
|
20
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
21
|
+
await reportBadgePresented("tok123", "merchant.com");
|
|
22
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining("/api/badge/report"), expect.objectContaining({
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: expect.objectContaining({
|
|
25
|
+
Authorization: "Bearer pk_test_xxx",
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
}),
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
verification_token: "tok123",
|
|
30
|
+
event_type: "identity_presented",
|
|
31
|
+
merchant: "merchant.com",
|
|
32
|
+
}),
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
it("POSTs when OAuth consent key exists", async () => {
|
|
36
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("oauth_access_token_xyz");
|
|
37
|
+
await reportBadgePresented("tok456", "other.com");
|
|
38
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
39
|
+
headers: expect.objectContaining({
|
|
40
|
+
Authorization: "Bearer oauth_access_token_xyz",
|
|
41
|
+
}),
|
|
42
|
+
}));
|
|
43
|
+
});
|
|
44
|
+
it("skips POST when no key at all", async () => {
|
|
45
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue(null);
|
|
46
|
+
await reportBadgePresented("tok789", "merchant.com");
|
|
47
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
it("uses PAYCLAW_API_URL when set", async () => {
|
|
50
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
51
|
+
process.env.PAYCLAW_API_URL = "https://custom-payclaw.example";
|
|
52
|
+
await reportBadgePresented("tok", "m");
|
|
53
|
+
expect(mockFetch).toHaveBeenCalledWith("https://custom-payclaw.example/api/badge/report", expect.any(Object));
|
|
54
|
+
});
|
|
55
|
+
it("uses payclaw.io when PAYCLAW_API_URL unset", async () => {
|
|
56
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
57
|
+
delete process.env.PAYCLAW_API_URL;
|
|
58
|
+
await reportBadgePresented("tok", "m");
|
|
59
|
+
expect(mockFetch).toHaveBeenCalledWith("https://payclaw.io/api/badge/report", expect.any(Object));
|
|
60
|
+
});
|
|
61
|
+
it("does not throw when fetch rejects", async () => {
|
|
62
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
63
|
+
mockFetch.mockRejectedValue(new Error("network error"));
|
|
64
|
+
await expect(reportBadgePresented("tok", "m")).resolves.toBeUndefined();
|
|
65
|
+
});
|
|
66
|
+
it("does not throw when POST returns 500", async () => {
|
|
67
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
68
|
+
mockFetch.mockResolvedValue({ ok: false, status: 500, text: async () => "server error" });
|
|
69
|
+
await expect(reportBadgePresented("tok", "m")).resolves.toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
it("includes presentation_context in body when context provided", async () => {
|
|
72
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
73
|
+
await reportBadgePresented("tok", "m", "checkout");
|
|
74
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
75
|
+
expect(body.presentation_context).toBe("checkout");
|
|
76
|
+
expect(body.event_type).toBe("identity_presented");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("reportBadgeNotPresented", () => {
|
|
80
|
+
const mockFetch = vi.fn();
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
vi.stubGlobal("fetch", mockFetch);
|
|
83
|
+
mockFetch.mockResolvedValue({ ok: true });
|
|
84
|
+
vi.mocked(storage.getStoredConsentKey).mockReturnValue("pk_test_xxx");
|
|
85
|
+
});
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
vi.unstubAllGlobals();
|
|
88
|
+
vi.restoreAllMocks();
|
|
89
|
+
});
|
|
90
|
+
it("POSTs with event_type badge_not_presented and reason", async () => {
|
|
91
|
+
await reportBadgeNotPresented("tok", "merchant.com", "abandoned");
|
|
92
|
+
expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining("/api/badge/report"), expect.objectContaining({
|
|
93
|
+
method: "POST",
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
verification_token: "tok",
|
|
96
|
+
event_type: "badge_not_presented",
|
|
97
|
+
merchant: "merchant.com",
|
|
98
|
+
reason: "abandoned",
|
|
99
|
+
}),
|
|
100
|
+
}));
|
|
101
|
+
});
|
|
102
|
+
it("supports all valid reasons", async () => {
|
|
103
|
+
await reportBadgeNotPresented("t", "m", "merchant_didnt_ask");
|
|
104
|
+
expect(JSON.parse(mockFetch.mock.calls[0][1].body).reason).toBe("merchant_didnt_ask");
|
|
105
|
+
await reportBadgeNotPresented("t", "m", "other");
|
|
106
|
+
expect(JSON.parse(mockFetch.mock.calls[1][1].body).reason).toBe("other");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=report-badge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-badge.test.js","sourceRoot":"","sources":["../../src/lib/report-badge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layered consent key lookup:
|
|
3
|
+
* 1. PAYCLAW_API_KEY env (backward compat — device flow never triggers)
|
|
4
|
+
* 2. ~/.payclaw/consent_key file
|
|
5
|
+
* 3. In-memory (current process only)
|
|
6
|
+
*/
|
|
7
|
+
export declare function getStoredConsentKey(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a human-readable description of the active auth mode.
|
|
10
|
+
* Used for startup logging — never exposes full key values.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getAuthMode(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Store consent key to ~/.payclaw/consent_key.
|
|
15
|
+
* Creates directory if needed. Falls back to memory if file write fails.
|
|
16
|
+
*/
|
|
17
|
+
export declare function storeConsentKey(token: string): Promise<void>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Canonical: badge-server | Synced: 0.7.3 | Do not edit in mcp-server
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
const CONSENT_KEY_DIR = ".payclaw";
|
|
6
|
+
const CONSENT_KEY_FILE = "consent_key";
|
|
7
|
+
/** In-memory fallback when file isn't writable. Lost on restart. */
|
|
8
|
+
let memoryConsentKey = null;
|
|
9
|
+
function getConsentKeyPath() {
|
|
10
|
+
const home = os.homedir();
|
|
11
|
+
return path.join(home, CONSENT_KEY_DIR, CONSENT_KEY_FILE);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Layered consent key lookup:
|
|
15
|
+
* 1. PAYCLAW_API_KEY env (backward compat — device flow never triggers)
|
|
16
|
+
* 2. ~/.payclaw/consent_key file
|
|
17
|
+
* 3. In-memory (current process only)
|
|
18
|
+
*/
|
|
19
|
+
export function getStoredConsentKey() {
|
|
20
|
+
const envKey = process.env.PAYCLAW_API_KEY;
|
|
21
|
+
if (envKey && envKey.trim().length > 0) {
|
|
22
|
+
return envKey.trim();
|
|
23
|
+
}
|
|
24
|
+
const filePath = getConsentKeyPath();
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(filePath)) {
|
|
27
|
+
const content = fs.readFileSync(filePath, "utf8").trim();
|
|
28
|
+
if (content.length > 0) {
|
|
29
|
+
return content;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// File read failed — fall back to memory
|
|
35
|
+
}
|
|
36
|
+
return memoryConsentKey;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns a human-readable description of the active auth mode.
|
|
40
|
+
* Used for startup logging — never exposes full key values.
|
|
41
|
+
*/
|
|
42
|
+
export function getAuthMode() {
|
|
43
|
+
const envKey = process.env.PAYCLAW_API_KEY;
|
|
44
|
+
if (envKey && envKey.trim().length > 0) {
|
|
45
|
+
const masked = envKey.trim().substring(0, 8) + "****";
|
|
46
|
+
return `API key (${masked})`;
|
|
47
|
+
}
|
|
48
|
+
const filePath = getConsentKeyPath();
|
|
49
|
+
try {
|
|
50
|
+
if (fs.existsSync(filePath)) {
|
|
51
|
+
const content = fs.readFileSync(filePath, "utf8").trim();
|
|
52
|
+
if (content.length > 0) {
|
|
53
|
+
return "consent key (~/.payclaw/consent_key)";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// File read failed
|
|
59
|
+
}
|
|
60
|
+
if (memoryConsentKey) {
|
|
61
|
+
return "consent key (in-memory)";
|
|
62
|
+
}
|
|
63
|
+
return "none (device flow will trigger on first tool call)";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Store consent key to ~/.payclaw/consent_key.
|
|
67
|
+
* Creates directory if needed. Falls back to memory if file write fails.
|
|
68
|
+
*/
|
|
69
|
+
export async function storeConsentKey(token) {
|
|
70
|
+
const trimmed = token.trim();
|
|
71
|
+
if (trimmed.length === 0)
|
|
72
|
+
return;
|
|
73
|
+
memoryConsentKey = trimmed;
|
|
74
|
+
const filePath = getConsentKeyPath();
|
|
75
|
+
const dir = path.dirname(filePath);
|
|
76
|
+
try {
|
|
77
|
+
if (!fs.existsSync(dir)) {
|
|
78
|
+
fs.mkdirSync(dir, { mode: 0o700, recursive: true });
|
|
79
|
+
}
|
|
80
|
+
fs.writeFileSync(filePath, trimmed, { mode: 0o600, flag: "w" });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// File write failed — key is in memory, will be lost on restart
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/lib/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCP manifest fetcher — checks if a merchant supports io.payclaw.common.identity.
|
|
3
|
+
*
|
|
4
|
+
* Fetches {merchantUrl}/.well-known/ucp, caches per domain for 5 minutes.
|
|
5
|
+
* Never throws — returns null on any error.
|
|
6
|
+
*/
|
|
7
|
+
interface UCPCapability {
|
|
8
|
+
version: string;
|
|
9
|
+
spec?: string;
|
|
10
|
+
schema?: string;
|
|
11
|
+
extends?: string;
|
|
12
|
+
config?: {
|
|
13
|
+
required?: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
interface UCPManifest {
|
|
17
|
+
capabilities?: Record<string, UCPCapability | UCPCapability[]>;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
export declare function fetchUCPManifest(merchantUrl: string): Promise<UCPManifest | null>;
|
|
21
|
+
export interface PayClawCapability {
|
|
22
|
+
version: string;
|
|
23
|
+
required: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function findPayClawCapability(manifest: UCPManifest): PayClawCapability | null;
|
|
26
|
+
export declare function isVersionCompatible(version: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Reset the manifest cache. Useful for testing.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export declare function _resetManifestCache(): void;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCP manifest fetcher — checks if a merchant supports io.payclaw.common.identity.
|
|
3
|
+
*
|
|
4
|
+
* Fetches {merchantUrl}/.well-known/ucp, caches per domain for 5 minutes.
|
|
5
|
+
* Never throws — returns null on any error.
|
|
6
|
+
*/
|
|
7
|
+
const EXTENSION_NAME = "io.payclaw.common.identity";
|
|
8
|
+
const FETCH_TIMEOUT_MS = 3000;
|
|
9
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
10
|
+
const manifestCache = new Map();
|
|
11
|
+
function normalizeDomain(url) {
|
|
12
|
+
try {
|
|
13
|
+
const u = new URL(url.endsWith("/") ? url.slice(0, -1) : url);
|
|
14
|
+
return u.origin;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Bare domain like "starbucks.com" — try with https://
|
|
18
|
+
try {
|
|
19
|
+
return new URL("https://" + url.replace(/\/+$/, "")).origin;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return url.replace(/\/+$/, "");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function isPublicOrigin(origin) {
|
|
27
|
+
let hostname;
|
|
28
|
+
try {
|
|
29
|
+
hostname = new URL(origin).hostname;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// Block non-https (except in tests)
|
|
35
|
+
if (!origin.startsWith("https://") && !process.env.VITEST)
|
|
36
|
+
return false;
|
|
37
|
+
// Block localhost and loopback
|
|
38
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1")
|
|
39
|
+
return false;
|
|
40
|
+
if (hostname.endsWith(".localhost"))
|
|
41
|
+
return false;
|
|
42
|
+
// Block private/reserved IPv4 ranges
|
|
43
|
+
const ipv4Match = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
|
|
44
|
+
if (ipv4Match) {
|
|
45
|
+
const [, a, b] = ipv4Match.map(Number);
|
|
46
|
+
if (a === 10)
|
|
47
|
+
return false; // 10.0.0.0/8
|
|
48
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
49
|
+
return false; // 172.16.0.0/12
|
|
50
|
+
if (a === 192 && b === 168)
|
|
51
|
+
return false; // 192.168.0.0/16
|
|
52
|
+
if (a === 169 && b === 254)
|
|
53
|
+
return false; // 169.254.0.0/16 (link-local + metadata)
|
|
54
|
+
if (a === 0)
|
|
55
|
+
return false; // 0.0.0.0/8
|
|
56
|
+
}
|
|
57
|
+
// Block IPv6 loopback/link-local
|
|
58
|
+
if (hostname.startsWith("[")) {
|
|
59
|
+
const inner = hostname.slice(1, -1).toLowerCase();
|
|
60
|
+
if (inner === "::1" || inner.startsWith("fe80:") || inner.startsWith("fc") || inner.startsWith("fd"))
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
export async function fetchUCPManifest(merchantUrl) {
|
|
66
|
+
const domain = normalizeDomain(merchantUrl);
|
|
67
|
+
// SSRF protection: only fetch from public origins
|
|
68
|
+
if (!isPublicOrigin(domain))
|
|
69
|
+
return null;
|
|
70
|
+
// Check cache
|
|
71
|
+
const cached = manifestCache.get(domain);
|
|
72
|
+
if (cached && (Date.now() - cached.fetchedAt) < CACHE_TTL_MS) {
|
|
73
|
+
return cached.data;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetch(`${domain}/.well-known/ucp`, {
|
|
77
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
78
|
+
});
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
manifestCache.set(domain, { data: null, fetchedAt: Date.now() });
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const data = (await res.json());
|
|
84
|
+
manifestCache.set(domain, { data, fetchedAt: Date.now() });
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
manifestCache.set(domain, { data: null, fetchedAt: Date.now() });
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function findPayClawCapability(manifest) {
|
|
93
|
+
const caps = manifest.capabilities;
|
|
94
|
+
if (!caps || !(EXTENSION_NAME in caps))
|
|
95
|
+
return null;
|
|
96
|
+
const entry = caps[EXTENSION_NAME];
|
|
97
|
+
// Handle both array-wrapped and plain object forms
|
|
98
|
+
const cap = Array.isArray(entry) ? entry[0] : entry;
|
|
99
|
+
if (!cap || typeof cap.version !== "string")
|
|
100
|
+
return null;
|
|
101
|
+
return {
|
|
102
|
+
version: cap.version,
|
|
103
|
+
required: cap.config?.required === true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const COMPATIBLE_VERSIONS = ["2026-01-11"];
|
|
107
|
+
export function isVersionCompatible(version) {
|
|
108
|
+
return COMPATIBLE_VERSIONS.includes(version);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Reset the manifest cache. Useful for testing.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export function _resetManifestCache() {
|
|
115
|
+
manifestCache.clear();
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=ucp-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ucp-manifest.js","sourceRoot":"","sources":["../../src/lib/ucp-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { fetchUCPManifest, findPayClawCapability, isVersionCompatible, _resetManifestCache, } from "./ucp-manifest.js";
|
|
3
|
+
describe("ucp-manifest", () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
_resetManifestCache();
|
|
6
|
+
});
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.restoreAllMocks();
|
|
9
|
+
});
|
|
10
|
+
const VALID_MANIFEST = {
|
|
11
|
+
capabilities: {
|
|
12
|
+
"io.payclaw.common.identity": [
|
|
13
|
+
{
|
|
14
|
+
version: "2026-01-11",
|
|
15
|
+
spec: "https://payclaw.io/ucp/spec/identity",
|
|
16
|
+
schema: "https://payclaw.io/ucp/schemas/identity.json",
|
|
17
|
+
config: { required: false },
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
describe("fetchUCPManifest", () => {
|
|
23
|
+
it("fetches and returns manifest", async () => {
|
|
24
|
+
vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(VALID_MANIFEST), { status: 200 }));
|
|
25
|
+
const result = await fetchUCPManifest("https://shop.example.com");
|
|
26
|
+
expect(result).toEqual(VALID_MANIFEST);
|
|
27
|
+
});
|
|
28
|
+
it("caches manifest per domain", async () => {
|
|
29
|
+
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(VALID_MANIFEST), { status: 200 }));
|
|
30
|
+
await fetchUCPManifest("https://shop.example.com");
|
|
31
|
+
await fetchUCPManifest("https://shop.example.com");
|
|
32
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
33
|
+
});
|
|
34
|
+
it("normalizes trailing slash", async () => {
|
|
35
|
+
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(VALID_MANIFEST), { status: 200 }));
|
|
36
|
+
await fetchUCPManifest("https://shop.example.com/");
|
|
37
|
+
await fetchUCPManifest("https://shop.example.com");
|
|
38
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
39
|
+
});
|
|
40
|
+
it("returns null on network error", async () => {
|
|
41
|
+
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("network error"));
|
|
42
|
+
const result = await fetchUCPManifest("https://shop.example.com");
|
|
43
|
+
expect(result).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
it("returns null on non-200 response", async () => {
|
|
46
|
+
vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("Not Found", { status: 404 }));
|
|
47
|
+
const result = await fetchUCPManifest("https://shop.example.com");
|
|
48
|
+
expect(result).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
it("negative-caches failed fetches", async () => {
|
|
51
|
+
const fetchMock = vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("down"));
|
|
52
|
+
await fetchUCPManifest("https://down.example.com");
|
|
53
|
+
await fetchUCPManifest("https://down.example.com");
|
|
54
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("findPayClawCapability", () => {
|
|
58
|
+
it("finds capability in array-wrapped format", () => {
|
|
59
|
+
const result = findPayClawCapability(VALID_MANIFEST);
|
|
60
|
+
expect(result).toEqual({ version: "2026-01-11", required: false });
|
|
61
|
+
});
|
|
62
|
+
it("finds capability in plain object format", () => {
|
|
63
|
+
const manifest = {
|
|
64
|
+
capabilities: {
|
|
65
|
+
"io.payclaw.common.identity": {
|
|
66
|
+
version: "2026-01-11",
|
|
67
|
+
config: { required: true },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
const result = findPayClawCapability(manifest);
|
|
72
|
+
expect(result).toEqual({ version: "2026-01-11", required: true });
|
|
73
|
+
});
|
|
74
|
+
it("returns null when extension not present", () => {
|
|
75
|
+
const result = findPayClawCapability({ capabilities: {} });
|
|
76
|
+
expect(result).toBeNull();
|
|
77
|
+
});
|
|
78
|
+
it("returns null when no capabilities", () => {
|
|
79
|
+
const result = findPayClawCapability({});
|
|
80
|
+
expect(result).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe("isVersionCompatible", () => {
|
|
84
|
+
it("accepts 2026-01-11", () => {
|
|
85
|
+
expect(isVersionCompatible("2026-01-11")).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it("rejects unknown version", () => {
|
|
88
|
+
expect(isVersionCompatible("2025-01-01")).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=ucp-manifest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ucp-manifest.test.js","sourceRoot":"","sources":["../../src/lib/ucp-manifest.test.ts"],"names":[],"mappings":"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"}
|
package/dist/sampling.d.ts
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
export interface ActiveTrip {
|
|
3
|
+
token: string;
|
|
4
|
+
merchant: string;
|
|
5
|
+
startedAt: number;
|
|
6
|
+
presented: boolean;
|
|
7
|
+
presentedAt?: number;
|
|
8
|
+
outcome?: string;
|
|
9
|
+
samplingTimer?: ReturnType<typeof setTimeout>;
|
|
10
|
+
}
|
|
2
11
|
export declare function initSampling(server: Server): void;
|
|
3
12
|
export declare function onTripStarted(token: string, merchant: string): void;
|
|
4
13
|
export declare function onIdentityPresented(token: string, merchant: string): void;
|
|
5
|
-
export declare function reportOutcomeFromAgent(token: string, merchant: string, outcome: "accepted" | "denied" | "inconclusive"): void;
|
|
6
14
|
export declare function onServerClose(): void;
|
|
15
|
+
/** Test-only: reset state between tests. No-op when VITEST not set. */
|
|
16
|
+
export declare function resetSamplingState(): void;
|
|
17
|
+
/** Test-only: get trip for assertions. Returns undefined when VITEST not set. */
|
|
18
|
+
export declare function getActiveTrip(token: string): ActiveTrip | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Report outcome from agent (payclaw_reportBadgeOutcome tool).
|
|
21
|
+
* Agent-only path — no sampling prompt. Resolves trip and POSTs to API.
|
|
22
|
+
* When token not in activeTrips (e.g. after restart), looks up by merchant or POSTs directly.
|
|
23
|
+
*/
|
|
24
|
+
export declare function reportOutcomeFromAgent(token: string, merchant: string, outcome: "accepted" | "denied" | "inconclusive"): void;
|