@ucptools/validator 1.0.0 → 1.1.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 (330) hide show
  1. package/dist/auth/config.d.ts +20 -0
  2. package/dist/auth/config.d.ts.map +1 -0
  3. package/dist/auth/config.js +114 -0
  4. package/dist/auth/config.js.map +1 -0
  5. package/dist/auth/index.d.ts +5 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +17 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/middleware.d.ts +45 -0
  10. package/dist/auth/middleware.d.ts.map +1 -0
  11. package/dist/auth/middleware.js +170 -0
  12. package/dist/auth/middleware.js.map +1 -0
  13. package/dist/auth/service.d.ts +80 -0
  14. package/dist/auth/service.d.ts.map +1 -0
  15. package/dist/auth/service.js +298 -0
  16. package/dist/auth/service.js.map +1 -0
  17. package/dist/cli/index.d.ts +6 -0
  18. package/dist/cli/index.d.ts.map +1 -0
  19. package/dist/cli/index.js +375 -0
  20. package/dist/cli/index.js.map +1 -0
  21. package/dist/cli/mock-server.d.ts +20 -0
  22. package/dist/cli/mock-server.d.ts.map +1 -0
  23. package/dist/cli/mock-server.js +261 -0
  24. package/dist/cli/mock-server.js.map +1 -0
  25. package/dist/compliance/compliance-generator.d.ts +34 -0
  26. package/dist/compliance/compliance-generator.d.ts.map +1 -0
  27. package/dist/compliance/compliance-generator.js +320 -0
  28. package/dist/compliance/compliance-generator.js.map +1 -0
  29. package/dist/compliance/index.d.ts +8 -0
  30. package/dist/compliance/index.d.ts.map +1 -0
  31. package/dist/compliance/index.js +17 -0
  32. package/dist/compliance/index.js.map +1 -0
  33. package/dist/compliance/templates.d.ts +34 -0
  34. package/dist/compliance/templates.d.ts.map +1 -0
  35. package/{src/compliance/templates.ts → dist/compliance/templates.js} +117 -155
  36. package/dist/compliance/templates.js.map +1 -0
  37. package/dist/compliance/types.d.ts +64 -0
  38. package/dist/compliance/types.d.ts.map +1 -0
  39. package/dist/compliance/types.js +64 -0
  40. package/dist/compliance/types.js.map +1 -0
  41. package/dist/db/index.d.ts +17 -0
  42. package/dist/db/index.d.ts.map +1 -0
  43. package/dist/db/index.js +80 -0
  44. package/dist/db/index.js.map +1 -0
  45. package/dist/db/schema.d.ts +3886 -0
  46. package/dist/db/schema.d.ts.map +1 -0
  47. package/dist/db/schema.js +425 -0
  48. package/dist/db/schema.js.map +1 -0
  49. package/dist/db/utils.d.ts +252 -0
  50. package/dist/db/utils.d.ts.map +1 -0
  51. package/dist/db/utils.js +295 -0
  52. package/dist/db/utils.js.map +1 -0
  53. package/dist/feed-analyzer/feed-analyzer.d.ts +26 -0
  54. package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -0
  55. package/{src/feed-analyzer/feed-analyzer.ts → dist/feed-analyzer/feed-analyzer.js} +856 -726
  56. package/dist/feed-analyzer/feed-analyzer.js.map +1 -0
  57. package/dist/feed-analyzer/index.d.ts +8 -0
  58. package/dist/feed-analyzer/index.d.ts.map +1 -0
  59. package/dist/feed-analyzer/index.js +19 -0
  60. package/dist/feed-analyzer/index.js.map +1 -0
  61. package/dist/feed-analyzer/types.d.ts +285 -0
  62. package/dist/feed-analyzer/types.d.ts.map +1 -0
  63. package/dist/feed-analyzer/types.js +175 -0
  64. package/dist/feed-analyzer/types.js.map +1 -0
  65. package/{src/generator/index.ts → dist/generator/index.d.ts} +1 -1
  66. package/dist/generator/index.d.ts.map +1 -0
  67. package/dist/generator/index.js +13 -0
  68. package/dist/generator/index.js.map +1 -0
  69. package/dist/generator/key-generator.d.ts +24 -0
  70. package/dist/generator/key-generator.d.ts.map +1 -0
  71. package/dist/generator/key-generator.js +144 -0
  72. package/dist/generator/key-generator.js.map +1 -0
  73. package/dist/generator/profile-builder.d.ts +15 -0
  74. package/dist/generator/profile-builder.d.ts.map +1 -0
  75. package/dist/generator/profile-builder.js +338 -0
  76. package/dist/generator/profile-builder.js.map +1 -0
  77. package/dist/hosting/artifacts-generator.d.ts +10 -0
  78. package/dist/hosting/artifacts-generator.d.ts.map +1 -0
  79. package/{src/hosting/artifacts-generator.ts → dist/hosting/artifacts-generator.js} +191 -241
  80. package/dist/hosting/artifacts-generator.js.map +1 -0
  81. package/{src/hosting/index.ts → dist/hosting/index.d.ts} +1 -1
  82. package/dist/hosting/index.d.ts.map +1 -0
  83. package/dist/hosting/index.js +10 -0
  84. package/dist/hosting/index.js.map +1 -0
  85. package/dist/index.d.ts +18 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +78 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/lib/analytics.d.ts +337 -0
  90. package/dist/lib/analytics.d.ts.map +1 -0
  91. package/dist/lib/analytics.js +188 -0
  92. package/dist/lib/analytics.js.map +1 -0
  93. package/{src/security/index.ts → dist/security/index.d.ts} +8 -15
  94. package/dist/security/index.d.ts.map +1 -0
  95. package/dist/security/index.js +12 -0
  96. package/dist/security/index.js.map +1 -0
  97. package/dist/security/security-scanner.d.ts +10 -0
  98. package/dist/security/security-scanner.d.ts.map +1 -0
  99. package/dist/security/security-scanner.js +669 -0
  100. package/dist/security/security-scanner.js.map +1 -0
  101. package/dist/security/types.d.ts +80 -0
  102. package/dist/security/types.d.ts.map +1 -0
  103. package/dist/security/types.js +21 -0
  104. package/dist/security/types.js.map +1 -0
  105. package/dist/services/analytics.d.ts +114 -0
  106. package/dist/services/analytics.d.ts.map +1 -0
  107. package/dist/services/analytics.js +862 -0
  108. package/dist/services/analytics.js.map +1 -0
  109. package/dist/services/badge.d.ts +31 -0
  110. package/dist/services/badge.d.ts.map +1 -0
  111. package/dist/services/badge.js +152 -0
  112. package/dist/services/badge.js.map +1 -0
  113. package/dist/services/cron.d.ts +125 -0
  114. package/dist/services/cron.d.ts.map +1 -0
  115. package/dist/services/cron.js +613 -0
  116. package/dist/services/cron.js.map +1 -0
  117. package/dist/services/directory.d.ts +106 -0
  118. package/dist/services/directory.d.ts.map +1 -0
  119. package/dist/services/directory.js +351 -0
  120. package/dist/services/directory.js.map +1 -0
  121. package/dist/services/email.d.ts +112 -0
  122. package/dist/services/email.d.ts.map +1 -0
  123. package/dist/services/email.js +772 -0
  124. package/dist/services/email.js.map +1 -0
  125. package/dist/services/hosted-profiles.d.ts +77 -0
  126. package/dist/services/hosted-profiles.d.ts.map +1 -0
  127. package/dist/services/hosted-profiles.js +433 -0
  128. package/dist/services/hosted-profiles.js.map +1 -0
  129. package/dist/services/latency.d.ts +67 -0
  130. package/dist/services/latency.d.ts.map +1 -0
  131. package/dist/services/latency.js +274 -0
  132. package/dist/services/latency.js.map +1 -0
  133. package/dist/services/manifest-compliance.d.ts +64 -0
  134. package/dist/services/manifest-compliance.d.ts.map +1 -0
  135. package/dist/services/manifest-compliance.js +271 -0
  136. package/dist/services/manifest-compliance.js.map +1 -0
  137. package/dist/services/monitoring-diff.d.ts +31 -0
  138. package/dist/services/monitoring-diff.d.ts.map +1 -0
  139. package/dist/services/monitoring-diff.js +189 -0
  140. package/dist/services/monitoring-diff.js.map +1 -0
  141. package/dist/services/notifications.d.ts +46 -0
  142. package/dist/services/notifications.d.ts.map +1 -0
  143. package/dist/services/notifications.js +88 -0
  144. package/dist/services/notifications.js.map +1 -0
  145. package/dist/services/stripe.d.ts +93 -0
  146. package/dist/services/stripe.d.ts.map +1 -0
  147. package/dist/services/stripe.js +490 -0
  148. package/dist/services/stripe.js.map +1 -0
  149. package/dist/services/validation-history.d.ts +99 -0
  150. package/dist/services/validation-history.d.ts.map +1 -0
  151. package/dist/services/validation-history.js +344 -0
  152. package/dist/services/validation-history.js.map +1 -0
  153. package/dist/services/validation-logging.d.ts +103 -0
  154. package/dist/services/validation-logging.d.ts.map +1 -0
  155. package/dist/services/validation-logging.js +210 -0
  156. package/dist/services/validation-logging.js.map +1 -0
  157. package/dist/services/validation.d.ts +119 -0
  158. package/dist/services/validation.d.ts.map +1 -0
  159. package/dist/services/validation.js +1185 -0
  160. package/dist/services/validation.js.map +1 -0
  161. package/dist/simulator/agent-simulator.d.ts +69 -0
  162. package/dist/simulator/agent-simulator.d.ts.map +1 -0
  163. package/dist/simulator/agent-simulator.js +870 -0
  164. package/dist/simulator/agent-simulator.js.map +1 -0
  165. package/{src/simulator/index.ts → dist/simulator/index.d.ts} +7 -7
  166. package/dist/simulator/index.d.ts.map +1 -0
  167. package/dist/simulator/index.js +23 -0
  168. package/dist/simulator/index.js.map +1 -0
  169. package/{src/simulator/types.ts → dist/simulator/types.d.ts} +171 -170
  170. package/dist/simulator/types.d.ts.map +1 -0
  171. package/dist/simulator/types.js +18 -0
  172. package/dist/simulator/types.js.map +1 -0
  173. package/dist/types/acp-validation.d.ts +87 -0
  174. package/dist/types/acp-validation.d.ts.map +1 -0
  175. package/dist/types/acp-validation.js +40 -0
  176. package/dist/types/acp-validation.js.map +1 -0
  177. package/dist/types/analytics.d.ts +182 -0
  178. package/dist/types/analytics.d.ts.map +1 -0
  179. package/dist/types/analytics.js +7 -0
  180. package/dist/types/analytics.js.map +1 -0
  181. package/dist/types/generator.d.ts +106 -0
  182. package/dist/types/generator.d.ts.map +1 -0
  183. package/dist/types/generator.js +6 -0
  184. package/dist/types/generator.js.map +1 -0
  185. package/{src/types/index.ts → dist/types/index.d.ts} +1 -1
  186. package/dist/types/index.d.ts.map +1 -0
  187. package/dist/types/index.js +23 -0
  188. package/dist/types/index.js.map +1 -0
  189. package/dist/types/ucp-profile.d.ts +111 -0
  190. package/dist/types/ucp-profile.d.ts.map +1 -0
  191. package/dist/types/ucp-profile.js +45 -0
  192. package/dist/types/ucp-profile.js.map +1 -0
  193. package/dist/types/validation.d.ts +76 -0
  194. package/dist/types/validation.d.ts.map +1 -0
  195. package/dist/types/validation.js +42 -0
  196. package/dist/types/validation.js.map +1 -0
  197. package/dist/validator/acp/index.d.ts +31 -0
  198. package/dist/validator/acp/index.d.ts.map +1 -0
  199. package/dist/validator/acp/index.js +574 -0
  200. package/dist/validator/acp/index.js.map +1 -0
  201. package/dist/validator/index.d.ts +26 -0
  202. package/dist/validator/index.d.ts.map +1 -0
  203. package/dist/validator/index.js +161 -0
  204. package/dist/validator/index.js.map +1 -0
  205. package/dist/validator/network-validator.d.ts +28 -0
  206. package/dist/validator/network-validator.d.ts.map +1 -0
  207. package/dist/validator/network-validator.js +319 -0
  208. package/dist/validator/network-validator.js.map +1 -0
  209. package/dist/validator/rules-validator.d.ts +19 -0
  210. package/dist/validator/rules-validator.d.ts.map +1 -0
  211. package/dist/validator/rules-validator.js +306 -0
  212. package/dist/validator/rules-validator.js.map +1 -0
  213. package/dist/validator/sdk-validator.d.ts +58 -0
  214. package/dist/validator/sdk-validator.d.ts.map +1 -0
  215. package/{src/validator/sdk-validator.ts → dist/validator/sdk-validator.js} +273 -330
  216. package/dist/validator/sdk-validator.js.map +1 -0
  217. package/dist/validator/structural-validator.d.ts +11 -0
  218. package/dist/validator/structural-validator.d.ts.map +1 -0
  219. package/dist/validator/structural-validator.js +549 -0
  220. package/dist/validator/structural-validator.js.map +1 -0
  221. package/dist/validator/utils.d.ts +51 -0
  222. package/dist/validator/utils.d.ts.map +1 -0
  223. package/dist/validator/utils.js +132 -0
  224. package/dist/validator/utils.js.map +1 -0
  225. package/package.json +44 -12
  226. package/CLAUDE.md +0 -109
  227. package/api/analyze-feed.js +0 -140
  228. package/api/badge.js +0 -185
  229. package/api/benchmark.js +0 -177
  230. package/api/directory-stats.ts +0 -29
  231. package/api/directory.ts +0 -73
  232. package/api/generate-compliance.js +0 -143
  233. package/api/generate-schema.js +0 -457
  234. package/api/generate.js +0 -132
  235. package/api/security-scan.js +0 -133
  236. package/api/simulate.js +0 -187
  237. package/api/tsconfig.json +0 -10
  238. package/api/validate.js +0 -1351
  239. package/apify-actor/.actor/actor.json +0 -68
  240. package/apify-actor/.actor/input_schema.json +0 -32
  241. package/apify-actor/APIFY-STORE-LISTING.md +0 -412
  242. package/apify-actor/Dockerfile +0 -8
  243. package/apify-actor/README.md +0 -166
  244. package/apify-actor/main.ts +0 -111
  245. package/apify-actor/package.json +0 -17
  246. package/apify-actor/src/main.js +0 -199
  247. package/docs/BRAND-IDENTITY.md +0 -238
  248. package/docs/BRAND-STYLE-GUIDE.md +0 -356
  249. package/drizzle/0000_black_king_cobra.sql +0 -39
  250. package/drizzle/meta/0000_snapshot.json +0 -309
  251. package/drizzle/meta/_journal.json +0 -13
  252. package/drizzle.config.ts +0 -10
  253. package/public/.well-known/ucp +0 -25
  254. package/public/android-chrome-192x192.png +0 -0
  255. package/public/android-chrome-512x512.png +0 -0
  256. package/public/apple-touch-icon.png +0 -0
  257. package/public/brand.css +0 -321
  258. package/public/directory.html +0 -701
  259. package/public/favicon-16x16.png +0 -0
  260. package/public/favicon-32x32.png +0 -0
  261. package/public/favicon.ico +0 -0
  262. package/public/guides/bigcommerce.html +0 -743
  263. package/public/guides/fastucp.html +0 -838
  264. package/public/guides/magento.html +0 -779
  265. package/public/guides/shopify.html +0 -726
  266. package/public/guides/squarespace.html +0 -749
  267. package/public/guides/wix.html +0 -747
  268. package/public/guides/woocommerce.html +0 -733
  269. package/public/index.html +0 -3835
  270. package/public/learn.html +0 -396
  271. package/public/logo.jpeg +0 -0
  272. package/public/og-image-icon.png +0 -0
  273. package/public/og-image.png +0 -0
  274. package/public/robots.txt +0 -6
  275. package/public/site.webmanifest +0 -31
  276. package/public/sitemap.xml +0 -69
  277. package/public/social/linkedin-banner-1128x191.png +0 -0
  278. package/public/social/temp.PNG +0 -0
  279. package/public/social/x-header-1500x500.png +0 -0
  280. package/public/verify.html +0 -410
  281. package/scripts/generate-favicons.js +0 -44
  282. package/scripts/generate-ico.js +0 -23
  283. package/scripts/generate-og-image.js +0 -45
  284. package/scripts/reset-db.ts +0 -77
  285. package/scripts/seed-db.ts +0 -71
  286. package/scripts/setup-benchmark-db.js +0 -70
  287. package/src/api/server.ts +0 -266
  288. package/src/cli/index.ts +0 -302
  289. package/src/compliance/compliance-generator.ts +0 -452
  290. package/src/compliance/index.ts +0 -28
  291. package/src/compliance/types.ts +0 -170
  292. package/src/db/index.ts +0 -28
  293. package/src/db/schema.ts +0 -84
  294. package/src/feed-analyzer/index.ts +0 -34
  295. package/src/feed-analyzer/types.ts +0 -354
  296. package/src/generator/key-generator.ts +0 -124
  297. package/src/generator/profile-builder.ts +0 -402
  298. package/src/index.ts +0 -105
  299. package/src/security/security-scanner.ts +0 -604
  300. package/src/security/types.ts +0 -55
  301. package/src/services/directory.ts +0 -434
  302. package/src/simulator/agent-simulator.ts +0 -941
  303. package/src/types/generator.ts +0 -140
  304. package/src/types/ucp-profile.ts +0 -140
  305. package/src/types/validation.ts +0 -89
  306. package/src/validator/index.ts +0 -194
  307. package/src/validator/network-validator.ts +0 -417
  308. package/src/validator/rules-validator.ts +0 -297
  309. package/src/validator/structural-validator.ts +0 -476
  310. package/tests/fixtures/non-compliant-profile.json +0 -25
  311. package/tests/fixtures/official-sample-profile.json +0 -75
  312. package/tests/integration/benchmark.test.ts +0 -207
  313. package/tests/integration/database.test.ts +0 -163
  314. package/tests/integration/directory-api.test.ts +0 -268
  315. package/tests/integration/simulate-api.test.ts +0 -230
  316. package/tests/integration/validate-api.test.ts +0 -269
  317. package/tests/setup.ts +0 -15
  318. package/tests/unit/agent-simulator.test.ts +0 -575
  319. package/tests/unit/compliance-generator.test.ts +0 -374
  320. package/tests/unit/directory-service.test.ts +0 -272
  321. package/tests/unit/feed-analyzer.test.ts +0 -517
  322. package/tests/unit/lint-suggestions.test.ts +0 -423
  323. package/tests/unit/official-samples.test.ts +0 -211
  324. package/tests/unit/pdf-report.test.ts +0 -390
  325. package/tests/unit/sdk-validator.test.ts +0 -531
  326. package/tests/unit/security-scanner.test.ts +0 -410
  327. package/tests/unit/validation.test.ts +0 -390
  328. package/tsconfig.json +0 -20
  329. package/vercel.json +0 -34
  330. package/vitest.config.ts +0 -22
@@ -0,0 +1,870 @@
1
+ "use strict";
2
+ /**
3
+ * AI Agent Simulator
4
+ * Simulates how an AI agent discovers and interacts with UCP-enabled merchants
5
+ *
6
+ * The goal is to test real-world functionality, not just spec compliance.
7
+ * This proves that a UCP implementation actually works for AI agent commerce.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.simulateDiscoveryFlow = simulateDiscoveryFlow;
11
+ exports.inspectCapabilities = inspectCapabilities;
12
+ exports.inspectServices = inspectServices;
13
+ exports.simulateRestApi = simulateRestApi;
14
+ exports.simulateCheckoutFlow = simulateCheckoutFlow;
15
+ exports.simulatePaymentReadiness = simulatePaymentReadiness;
16
+ exports.simulateAgentInteraction = simulateAgentInteraction;
17
+ exports.fetchWithTimeout = fetchWithTimeout;
18
+ exports.checkEndpointResponsive = checkEndpointResponsive;
19
+ exports.generateRecommendations = generateRecommendations;
20
+ exports.calculateScore = calculateScore;
21
+ exports.getGrade = getGrade;
22
+ const utils_js_1 = require("../validator/utils.js");
23
+ const DEFAULT_TIMEOUT_MS = 30000;
24
+ const DEFAULT_FETCH_TIMEOUT_MS = 10000;
25
+ /**
26
+ * Normalize capability to extract the name string
27
+ * Handles both string format and object format capabilities
28
+ */
29
+ function getCapabilityName(cap) {
30
+ if (typeof cap === 'string') {
31
+ return cap;
32
+ }
33
+ if (cap && typeof cap === 'object' && 'name' in cap) {
34
+ return String(cap.name);
35
+ }
36
+ return '';
37
+ }
38
+ /**
39
+ * Get capability object (for accessing schema, version, etc.)
40
+ * Returns null for string-only capabilities
41
+ */
42
+ function getCapabilityObject(cap) {
43
+ if (cap && typeof cap === 'object' && 'name' in cap) {
44
+ return cap;
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Step builder helper
50
+ */
51
+ function createStep(step, status, message, details, durationMs, data) {
52
+ return { step, status, message, details, durationMs, data };
53
+ }
54
+ /**
55
+ * Fetch with timeout
56
+ */
57
+ async function fetchWithTimeout(url, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
58
+ const controller = new AbortController();
59
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
60
+ try {
61
+ const response = await fetch(url, {
62
+ signal: controller.signal,
63
+ headers: {
64
+ 'User-Agent': 'UCP-Agent-Simulator/1.0',
65
+ 'Accept': 'application/json',
66
+ },
67
+ });
68
+ clearTimeout(timeoutId);
69
+ if (!response.ok) {
70
+ return { ok: false, status: response.status, error: `HTTP ${response.status}` };
71
+ }
72
+ const contentType = response.headers.get('content-type') || '';
73
+ if (contentType.includes('application/json')) {
74
+ const data = await response.json();
75
+ return { ok: true, status: response.status, data };
76
+ }
77
+ return { ok: true, status: response.status };
78
+ }
79
+ catch (error) {
80
+ clearTimeout(timeoutId);
81
+ const message = error instanceof Error ? error.message : 'Unknown error';
82
+ return { ok: false, error: message };
83
+ }
84
+ }
85
+ /**
86
+ * HEAD request to check endpoint responsiveness
87
+ */
88
+ async function checkEndpointResponsive(url, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
89
+ const controller = new AbortController();
90
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
91
+ try {
92
+ const response = await fetch(url, {
93
+ method: 'HEAD',
94
+ signal: controller.signal,
95
+ headers: {
96
+ 'User-Agent': 'UCP-Agent-Simulator/1.0',
97
+ },
98
+ });
99
+ clearTimeout(timeoutId);
100
+ // Accept various success codes - we just want to know endpoint exists
101
+ return { ok: response.status < 500, status: response.status };
102
+ }
103
+ catch (error) {
104
+ clearTimeout(timeoutId);
105
+ const message = error instanceof Error ? error.message : 'Unknown error';
106
+ return { ok: false, error: message };
107
+ }
108
+ }
109
+ /**
110
+ * Simulate discovery flow - how an AI agent discovers a UCP merchant
111
+ */
112
+ async function simulateDiscoveryFlow(domain, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
113
+ const steps = [];
114
+ let profileUrl;
115
+ let profile = null;
116
+ const capabilities = [];
117
+ const services = [];
118
+ const transports = [];
119
+ // Step 1: Try to fetch /.well-known/ucp
120
+ const startTime = Date.now();
121
+ const urls = [
122
+ `https://${domain}/.well-known/ucp`,
123
+ `https://${domain}/.well-known/ucp.json`,
124
+ ];
125
+ let foundProfile = false;
126
+ for (const url of urls) {
127
+ const fetchStart = Date.now();
128
+ const result = await fetchWithTimeout(url, timeoutMs);
129
+ const fetchDuration = Date.now() - fetchStart;
130
+ if (result.ok && result.data && typeof result.data === 'object') {
131
+ const data = result.data;
132
+ if (data.ucp) {
133
+ profile = data;
134
+ profileUrl = url;
135
+ foundProfile = true;
136
+ steps.push(createStep('discover_profile', 'passed', `Found UCP profile at ${url}`, `Response time: ${fetchDuration}ms`, fetchDuration, { url, responseTime: fetchDuration }));
137
+ break;
138
+ }
139
+ }
140
+ }
141
+ if (!foundProfile) {
142
+ steps.push(createStep('discover_profile', 'failed', 'Could not find UCP profile', `Tried: ${urls.join(', ')}`));
143
+ return { success: false, steps, capabilities, services, transports };
144
+ }
145
+ // Step 2: Parse UCP version
146
+ if (profile?.ucp?.version) {
147
+ steps.push(createStep('parse_version', 'passed', `UCP version: ${profile.ucp.version}`, undefined, undefined, { version: profile.ucp.version }));
148
+ }
149
+ else {
150
+ steps.push(createStep('parse_version', 'failed', 'Missing UCP version', 'AI agent cannot determine protocol version'));
151
+ }
152
+ // Step 3: Enumerate services
153
+ const serviceEntries = Object.entries(profile?.ucp?.services || {});
154
+ if (serviceEntries.length > 0) {
155
+ for (const [serviceName, service] of serviceEntries) {
156
+ services.push(serviceName);
157
+ // Track transports
158
+ if (service.rest)
159
+ transports.push('rest');
160
+ if (service.mcp)
161
+ transports.push('mcp');
162
+ if (service.a2a)
163
+ transports.push('a2a');
164
+ if (service.embedded)
165
+ transports.push('embedded');
166
+ }
167
+ steps.push(createStep('enumerate_services', 'passed', `Found ${serviceEntries.length} service(s): ${services.join(', ')}`, `Available transports: ${[...new Set(transports)].join(', ')}`, undefined, { services, transports: [...new Set(transports)] }));
168
+ }
169
+ else {
170
+ steps.push(createStep('enumerate_services', 'failed', 'No services found', 'AI agent has no entry point for commerce operations'));
171
+ }
172
+ // Step 4: Enumerate capabilities
173
+ const capList = (0, utils_js_1.normalizeCapabilities)(profile?.ucp?.capabilities);
174
+ if (capList.length > 0) {
175
+ for (const cap of capList) {
176
+ const capName = getCapabilityName(cap);
177
+ if (capName)
178
+ capabilities.push(capName);
179
+ }
180
+ // Check for required checkout capability
181
+ const hasCheckout = capabilities.some(c => c && c.includes('checkout'));
182
+ const hasOrder = capabilities.some(c => c && c.includes('order'));
183
+ steps.push(createStep('enumerate_capabilities', 'passed', `Found ${capList.length} capability/ies: ${capabilities.join(', ')}`, hasCheckout ? 'Checkout capability present - commerce ready' : 'No checkout capability - limited commerce support', undefined, { capabilities, hasCheckout, hasOrder }));
184
+ }
185
+ else {
186
+ steps.push(createStep('enumerate_capabilities', 'warning', 'No capabilities declared', 'AI agent cannot determine supported operations'));
187
+ }
188
+ const totalDuration = Date.now() - startTime;
189
+ return {
190
+ success: foundProfile && serviceEntries.length > 0,
191
+ steps,
192
+ profileUrl,
193
+ capabilities,
194
+ services,
195
+ transports: [...new Set(transports)],
196
+ };
197
+ }
198
+ /**
199
+ * Inspect capabilities in detail
200
+ */
201
+ async function inspectCapabilities(profile, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
202
+ const results = [];
203
+ const capList = (0, utils_js_1.normalizeCapabilities)(profile.ucp?.capabilities);
204
+ for (const cap of capList) {
205
+ const capName = getCapabilityName(cap);
206
+ const capObj = getCapabilityObject(cap);
207
+ let schemaAccessible = false;
208
+ let specAccessible = false;
209
+ // Check schema URL (only for object-format capabilities)
210
+ if (capObj?.schema) {
211
+ const schemaResult = await fetchWithTimeout(capObj.schema, timeoutMs);
212
+ schemaAccessible = schemaResult.ok;
213
+ }
214
+ // Check spec URL (only for object-format capabilities)
215
+ if (capObj?.spec) {
216
+ const specResult = await checkEndpointResponsive(capObj.spec, timeoutMs);
217
+ specAccessible = specResult.ok;
218
+ }
219
+ results.push({
220
+ name: capName,
221
+ version: capObj?.version || 'unknown',
222
+ schemaAccessible,
223
+ specAccessible,
224
+ isExtension: !!capObj?.extends,
225
+ parentCapability: capObj?.extends,
226
+ });
227
+ }
228
+ return results;
229
+ }
230
+ /**
231
+ * Inspect services and their transports
232
+ */
233
+ async function inspectServices(profile, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
234
+ const results = [];
235
+ const serviceEntries = Object.entries(profile.ucp?.services || {});
236
+ for (const [name, service] of serviceEntries) {
237
+ const svc = service;
238
+ const result = {
239
+ name,
240
+ version: svc.version,
241
+ transports: {},
242
+ };
243
+ // Check REST transport
244
+ if (svc.rest) {
245
+ const schemaCheck = svc.rest.schema
246
+ ? await fetchWithTimeout(svc.rest.schema, timeoutMs)
247
+ : { ok: false };
248
+ const endpointCheck = await checkEndpointResponsive(svc.rest.endpoint, timeoutMs);
249
+ result.transports.rest = {
250
+ endpoint: svc.rest.endpoint,
251
+ schemaAccessible: schemaCheck.ok,
252
+ endpointResponsive: endpointCheck.ok,
253
+ };
254
+ }
255
+ // Check MCP transport
256
+ if (svc.mcp) {
257
+ const schemaCheck = svc.mcp.schema
258
+ ? await fetchWithTimeout(svc.mcp.schema, timeoutMs)
259
+ : { ok: false };
260
+ result.transports.mcp = {
261
+ endpoint: svc.mcp.endpoint,
262
+ schemaAccessible: schemaCheck.ok,
263
+ };
264
+ }
265
+ // Check A2A transport
266
+ if (svc.a2a) {
267
+ const agentCardCheck = await fetchWithTimeout(svc.a2a.agentCard, timeoutMs);
268
+ result.transports.a2a = {
269
+ agentCard: svc.a2a.agentCard,
270
+ agentCardAccessible: agentCardCheck.ok,
271
+ };
272
+ }
273
+ results.push(result);
274
+ }
275
+ return results;
276
+ }
277
+ /**
278
+ * Simulate REST API interaction
279
+ */
280
+ async function simulateRestApi(profile, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
281
+ const steps = [];
282
+ const sampleOperations = [];
283
+ let schemaLoaded = false;
284
+ let endpointAccessible = false;
285
+ // Find REST service
286
+ const shoppingService = profile.ucp?.services?.['dev.ucp.shopping'];
287
+ if (!shoppingService?.rest) {
288
+ steps.push(createStep('find_rest_service', 'skipped', 'No REST service configured', 'Merchant may use MCP or A2A transport instead'));
289
+ return { success: false, steps, schemaLoaded, endpointAccessible, sampleOperations };
290
+ }
291
+ // Step 1: Load OpenAPI schema
292
+ if (shoppingService.rest.schema) {
293
+ const schemaStart = Date.now();
294
+ const schemaResult = await fetchWithTimeout(shoppingService.rest.schema, timeoutMs);
295
+ const schemaDuration = Date.now() - schemaStart;
296
+ if (schemaResult.ok && schemaResult.data) {
297
+ schemaLoaded = true;
298
+ const schema = schemaResult.data;
299
+ steps.push(createStep('load_openapi_schema', 'passed', `Loaded OpenAPI schema from ${shoppingService.rest.schema}`, `Response time: ${schemaDuration}ms`, schemaDuration, { schemaUrl: shoppingService.rest.schema }));
300
+ // Check for expected paths in schema
301
+ const paths = schema.paths;
302
+ if (paths) {
303
+ const pathCount = Object.keys(paths).length;
304
+ steps.push(createStep('analyze_schema_paths', pathCount > 0 ? 'passed' : 'warning', `Schema defines ${pathCount} operation path(s)`, pathCount > 0 ? `Paths: ${Object.keys(paths).slice(0, 5).join(', ')}${pathCount > 5 ? '...' : ''}` : undefined));
305
+ }
306
+ }
307
+ else {
308
+ steps.push(createStep('load_openapi_schema', 'failed', 'Could not load OpenAPI schema', schemaResult.error || 'Unknown error'));
309
+ }
310
+ }
311
+ else {
312
+ steps.push(createStep('load_openapi_schema', 'warning', 'No schema URL provided', 'AI agent cannot discover available operations'));
313
+ }
314
+ // Step 2: Check endpoint responsiveness
315
+ const endpointStart = Date.now();
316
+ const endpointResult = await checkEndpointResponsive(shoppingService.rest.endpoint, timeoutMs);
317
+ const endpointDuration = Date.now() - endpointStart;
318
+ if (endpointResult.ok) {
319
+ endpointAccessible = true;
320
+ steps.push(createStep('check_endpoint', 'passed', `REST endpoint responsive: ${shoppingService.rest.endpoint}`, `Status: ${endpointResult.status}, Response time: ${endpointDuration}ms`, endpointDuration));
321
+ }
322
+ else {
323
+ steps.push(createStep('check_endpoint', 'failed', `REST endpoint not accessible: ${shoppingService.rest.endpoint}`, endpointResult.error || `Status: ${endpointResult.status}`));
324
+ }
325
+ return {
326
+ success: schemaLoaded && endpointAccessible,
327
+ steps,
328
+ schemaLoaded,
329
+ endpointAccessible,
330
+ sampleOperations,
331
+ };
332
+ }
333
+ /**
334
+ * Simulate checkout flow capability
335
+ */
336
+ async function simulateCheckoutFlow(profile, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
337
+ const steps = [];
338
+ let canCreateCheckout = false;
339
+ let checkoutSchemaValid = false;
340
+ let orderFlowSupported = false;
341
+ let fulfillmentSupported = false;
342
+ const capabilities = (0, utils_js_1.normalizeCapabilities)(profile.ucp?.capabilities);
343
+ // Check for checkout capability (handle both string and object formats)
344
+ const checkoutCapRaw = capabilities.find((c) => getCapabilityName(c).includes('checkout'));
345
+ const checkoutCapName = checkoutCapRaw ? getCapabilityName(checkoutCapRaw) : null;
346
+ const checkoutCapObj = checkoutCapRaw ? getCapabilityObject(checkoutCapRaw) : null;
347
+ if (checkoutCapName) {
348
+ canCreateCheckout = true;
349
+ steps.push(createStep('find_checkout_capability', 'passed', `Found checkout capability: ${checkoutCapName}`, checkoutCapObj?.version ? `Version: ${checkoutCapObj.version}` : 'String-format capability'));
350
+ // Validate checkout schema (only for object-format capabilities)
351
+ if (checkoutCapObj?.schema) {
352
+ const schemaResult = await fetchWithTimeout(checkoutCapObj.schema, timeoutMs);
353
+ if (schemaResult.ok && schemaResult.data) {
354
+ checkoutSchemaValid = true;
355
+ const schema = schemaResult.data;
356
+ // Check for required checkout properties
357
+ const properties = (schema.properties || schema.$defs);
358
+ const hasCheckoutProps = properties && (properties.checkout_id ||
359
+ properties.items ||
360
+ properties.CheckoutSession);
361
+ steps.push(createStep('validate_checkout_schema', hasCheckoutProps ? 'passed' : 'warning', `Checkout schema ${hasCheckoutProps ? 'has expected structure' : 'loaded but structure unclear'}`, hasCheckoutProps ? 'AI agent can create checkout sessions' : 'Schema may need review'));
362
+ }
363
+ else {
364
+ steps.push(createStep('validate_checkout_schema', 'failed', 'Could not load checkout schema', schemaResult.error || 'Unknown error'));
365
+ }
366
+ }
367
+ else {
368
+ // String-format capability - no schema to validate, mark as valid for basic functionality
369
+ checkoutSchemaValid = true;
370
+ steps.push(createStep('validate_checkout_schema', 'info', 'No schema URL in capability (string format)', 'Capability declared but schema validation skipped'));
371
+ }
372
+ }
373
+ else {
374
+ steps.push(createStep('find_checkout_capability', 'failed', 'No checkout capability found', 'AI agent cannot create checkout sessions'));
375
+ }
376
+ // Check for order capability
377
+ const orderCapRaw = capabilities.find((c) => getCapabilityName(c).includes('order'));
378
+ const orderCapName = orderCapRaw ? getCapabilityName(orderCapRaw) : null;
379
+ if (orderCapName) {
380
+ orderFlowSupported = true;
381
+ steps.push(createStep('find_order_capability', 'passed', `Found order capability: ${orderCapName}`, 'AI agent can track order status'));
382
+ }
383
+ else {
384
+ steps.push(createStep('find_order_capability', 'warning', 'No order capability found', 'Order tracking may not be available'));
385
+ }
386
+ // Check for fulfillment capability
387
+ const fulfillmentCapRaw = capabilities.find((c) => getCapabilityName(c).includes('fulfillment'));
388
+ const fulfillmentCapName = fulfillmentCapRaw ? getCapabilityName(fulfillmentCapRaw) : null;
389
+ if (fulfillmentCapName) {
390
+ fulfillmentSupported = true;
391
+ steps.push(createStep('find_fulfillment_capability', 'passed', `Found fulfillment capability: ${fulfillmentCapName}`, 'AI agent can track shipping and delivery'));
392
+ }
393
+ else {
394
+ steps.push(createStep('find_fulfillment_capability', 'info', 'No fulfillment capability found', 'Fulfillment tracking not available via UCP'));
395
+ }
396
+ return {
397
+ success: canCreateCheckout && checkoutSchemaValid,
398
+ steps,
399
+ canCreateCheckout,
400
+ checkoutSchemaValid,
401
+ orderFlowSupported,
402
+ fulfillmentSupported,
403
+ };
404
+ }
405
+ /**
406
+ * Check payment readiness
407
+ */
408
+ async function simulatePaymentReadiness(profile, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
409
+ const steps = [];
410
+ let handlersFound = 0;
411
+ let webhookVerifiable = false;
412
+ let signingKeyValid = false;
413
+ // Check payment handlers
414
+ const handlers = profile.payment?.handlers || [];
415
+ handlersFound = handlers.length;
416
+ if (handlersFound > 0) {
417
+ const handlerNames = handlers.map(h => h.name).join(', ');
418
+ steps.push(createStep('find_payment_handlers', 'passed', `Found ${handlersFound} payment handler(s): ${handlerNames}`, 'AI agent can initiate payments'));
419
+ // Check handler configs
420
+ for (const handler of handlers) {
421
+ if (handler.config_schema) {
422
+ const schemaResult = await checkEndpointResponsive(handler.config_schema, timeoutMs);
423
+ steps.push(createStep(`check_handler_${handler.id}`, schemaResult.ok ? 'passed' : 'warning', `Payment handler "${handler.name}" config schema ${schemaResult.ok ? 'accessible' : 'not accessible'}`, handler.config_schema));
424
+ }
425
+ }
426
+ }
427
+ else {
428
+ steps.push(createStep('find_payment_handlers', 'warning', 'No payment handlers configured', 'Payment processing may use external flow'));
429
+ }
430
+ // Check signing keys for webhook verification
431
+ const signingKeys = profile.signing_keys;
432
+ if (signingKeys && Array.isArray(signingKeys) && signingKeys.length > 0) {
433
+ webhookVerifiable = true;
434
+ // Validate key structure
435
+ const validKeys = signingKeys.filter(key => key.kty && key.kid && ((key.kty === 'EC' && key.crv && key.x && key.y) ||
436
+ (key.kty === 'RSA' && key.n && key.e)));
437
+ signingKeyValid = validKeys.length > 0;
438
+ steps.push(createStep('check_signing_keys', signingKeyValid ? 'passed' : 'warning', `Found ${signingKeys.length} signing key(s), ${validKeys.length} valid`, signingKeyValid
439
+ ? 'AI agent can verify webhook signatures'
440
+ : 'Signing keys present but may be incomplete'));
441
+ }
442
+ else {
443
+ steps.push(createStep('check_signing_keys', 'warning', 'No signing keys found', 'Webhook verification not available'));
444
+ }
445
+ return {
446
+ success: handlersFound > 0 || webhookVerifiable,
447
+ steps,
448
+ handlersFound,
449
+ webhookVerifiable,
450
+ signingKeyValid,
451
+ };
452
+ }
453
+ /**
454
+ * Generate recommendations based on simulation results
455
+ */
456
+ function generateRecommendations(discovery, capabilities, services, restApi, checkout, payment) {
457
+ const recommendations = [];
458
+ // Discovery issues
459
+ if (!discovery.success) {
460
+ recommendations.push('Ensure UCP profile is accessible at /.well-known/ucp');
461
+ }
462
+ // Service issues
463
+ if (discovery.services.length === 0) {
464
+ recommendations.push('Add at least one service (e.g., dev.ucp.shopping) to enable commerce');
465
+ }
466
+ // Transport issues
467
+ if (discovery.transports.length === 0) {
468
+ recommendations.push('Configure at least one transport binding (REST, MCP, or A2A)');
469
+ }
470
+ // Capability issues
471
+ const inaccessibleSchemas = capabilities.filter(c => !c.schemaAccessible);
472
+ if (inaccessibleSchemas.length > 0) {
473
+ recommendations.push(`Fix inaccessible capability schemas: ${inaccessibleSchemas.map(c => c.name).join(', ')}`);
474
+ }
475
+ // REST API issues
476
+ if (restApi && !restApi.schemaLoaded) {
477
+ recommendations.push('Provide accessible OpenAPI schema for REST service');
478
+ }
479
+ if (restApi && !restApi.endpointAccessible) {
480
+ recommendations.push('Ensure REST endpoint is publicly accessible');
481
+ }
482
+ // Checkout issues
483
+ if (checkout && !checkout.canCreateCheckout) {
484
+ recommendations.push('Add checkout capability (dev.ucp.shopping.checkout) to enable purchases');
485
+ }
486
+ if (checkout && checkout.canCreateCheckout && !checkout.checkoutSchemaValid) {
487
+ recommendations.push('Ensure checkout schema is accessible and valid');
488
+ }
489
+ // Payment issues
490
+ if (payment && payment.handlersFound === 0) {
491
+ recommendations.push('Configure payment handlers in the profile');
492
+ }
493
+ if (payment && !payment.signingKeyValid) {
494
+ recommendations.push('Add valid signing keys (EC or RSA JWK) for webhook verification');
495
+ }
496
+ // Add positive note if everything looks good
497
+ if (recommendations.length === 0) {
498
+ recommendations.push('Profile is well-configured for AI agent commerce!');
499
+ }
500
+ return recommendations;
501
+ }
502
+ /**
503
+ * Calculate overall AI readiness score
504
+ */
505
+ function calculateScore(discovery, capabilities, services, restApi, checkout, payment) {
506
+ let score = 0;
507
+ const maxScore = 100;
508
+ // Discovery (25 points)
509
+ if (discovery.success)
510
+ score += 15;
511
+ if (discovery.services.length > 0)
512
+ score += 5;
513
+ if (discovery.capabilities.length > 0)
514
+ score += 5;
515
+ // Capabilities (25 points)
516
+ const accessibleCapabilities = capabilities.filter(c => c.schemaAccessible);
517
+ if (capabilities.length > 0) {
518
+ score += Math.round((accessibleCapabilities.length / capabilities.length) * 15);
519
+ }
520
+ // Bonus for having checkout
521
+ if (capabilities.some(c => c.name.includes('checkout')))
522
+ score += 10;
523
+ // Services & Transport (25 points)
524
+ for (const service of services) {
525
+ if (service.transports.rest?.endpointResponsive)
526
+ score += 10;
527
+ if (service.transports.rest?.schemaAccessible)
528
+ score += 5;
529
+ if (service.transports.mcp)
530
+ score += 5;
531
+ if (service.transports.a2a?.agentCardAccessible)
532
+ score += 5;
533
+ }
534
+ // Cap at 25
535
+ score = Math.min(score, 75);
536
+ // Checkout (15 points)
537
+ if (checkout?.canCreateCheckout)
538
+ score += 8;
539
+ if (checkout?.checkoutSchemaValid)
540
+ score += 4;
541
+ if (checkout?.orderFlowSupported)
542
+ score += 3;
543
+ // Payment (10 points)
544
+ if (payment?.handlersFound || 0 > 0)
545
+ score += 5;
546
+ if (payment?.signingKeyValid)
547
+ score += 5;
548
+ return Math.min(score, maxScore);
549
+ }
550
+ /**
551
+ * Get grade from score (aligned with all other scoring models)
552
+ */
553
+ function getGrade(score) {
554
+ if (score >= 90)
555
+ return 'A';
556
+ if (score >= 80)
557
+ return 'B';
558
+ if (score >= 70)
559
+ return 'C';
560
+ if (score >= 60)
561
+ return 'D';
562
+ return 'F';
563
+ }
564
+ /**
565
+ * Generate status message for a simulator category
566
+ */
567
+ function generateCategoryStatus(categoryName, score, maxScore) {
568
+ const percent = maxScore > 0 ? Math.round((score / maxScore) * 100) : 0;
569
+ const statusMessages = {
570
+ discovery: {
571
+ excellent: 'Profile fully discoverable with rich metadata',
572
+ good: 'Profile discoverable with good capability support',
573
+ partial: 'Profile found but missing some metadata',
574
+ missing: 'Profile discovery needs configuration',
575
+ },
576
+ api_access: {
577
+ excellent: 'All API endpoints accessible with valid schemas',
578
+ good: 'API accessible with minor improvements possible',
579
+ partial: 'Some API access issues detected',
580
+ missing: 'API access needs configuration',
581
+ },
582
+ checkout_flow: {
583
+ excellent: 'Complete checkout flow with order tracking',
584
+ good: 'Checkout supported with room for enhancement',
585
+ partial: 'Basic checkout capability present',
586
+ missing: 'Checkout capability needs implementation',
587
+ },
588
+ payment_ready: {
589
+ excellent: 'Payment handlers and verification fully configured',
590
+ good: 'Payment support configured',
591
+ partial: 'Basic payment support present',
592
+ missing: 'Payment configuration needed',
593
+ },
594
+ };
595
+ const messages = statusMessages[categoryName] || {
596
+ excellent: 'Excellent',
597
+ good: 'Good',
598
+ partial: 'Partial',
599
+ missing: 'Needs attention',
600
+ };
601
+ if (percent >= 90)
602
+ return messages.excellent;
603
+ if (percent >= 70)
604
+ return messages.good;
605
+ if (percent >= 40)
606
+ return messages.partial;
607
+ return messages.missing;
608
+ }
609
+ /**
610
+ * Calculate simulator score breakdown (additive model)
611
+ */
612
+ function calculateScoreBreakdown(discovery, capabilities, services, restApi, checkout, payment) {
613
+ // Discovery category (30 pts max)
614
+ const discoveryCategory = {
615
+ score: 0,
616
+ maxScore: 30,
617
+ status: '',
618
+ checks: [],
619
+ };
620
+ // Profile found (15 pts)
621
+ const profileFound = discovery.success;
622
+ discoveryCategory.checks.push({
623
+ name: 'Profile Discovery',
624
+ score: profileFound ? 15 : 0,
625
+ maxScore: 15,
626
+ passed: profileFound,
627
+ });
628
+ if (profileFound)
629
+ discoveryCategory.score += 15;
630
+ // Services enumerated (10 pts)
631
+ const hasServices = discovery.services.length > 0;
632
+ discoveryCategory.checks.push({
633
+ name: 'Services Enumerated',
634
+ score: hasServices ? 10 : 0,
635
+ maxScore: 10,
636
+ passed: hasServices,
637
+ });
638
+ if (hasServices)
639
+ discoveryCategory.score += 10;
640
+ // Capabilities declared (5 pts)
641
+ const hasCapabilities = discovery.capabilities.length > 0;
642
+ discoveryCategory.checks.push({
643
+ name: 'Capabilities Declared',
644
+ score: hasCapabilities ? 5 : 0,
645
+ maxScore: 5,
646
+ passed: hasCapabilities,
647
+ });
648
+ if (hasCapabilities)
649
+ discoveryCategory.score += 5;
650
+ discoveryCategory.status = generateCategoryStatus('discovery', discoveryCategory.score, discoveryCategory.maxScore);
651
+ // API Access category (30 pts max)
652
+ const apiCategory = {
653
+ score: 0,
654
+ maxScore: 30,
655
+ status: '',
656
+ checks: [],
657
+ };
658
+ // Schema loaded (12 pts)
659
+ const schemaLoaded = restApi?.schemaLoaded ?? false;
660
+ apiCategory.checks.push({
661
+ name: 'Schema Loaded',
662
+ score: schemaLoaded ? 12 : 0,
663
+ maxScore: 12,
664
+ passed: schemaLoaded,
665
+ });
666
+ if (schemaLoaded)
667
+ apiCategory.score += 12;
668
+ // Endpoint accessible (12 pts)
669
+ const endpointAccessible = restApi?.endpointAccessible ?? false;
670
+ apiCategory.checks.push({
671
+ name: 'Endpoint Accessible',
672
+ score: endpointAccessible ? 12 : 0,
673
+ maxScore: 12,
674
+ passed: endpointAccessible,
675
+ });
676
+ if (endpointAccessible)
677
+ apiCategory.score += 12;
678
+ // Service transports (6 pts)
679
+ const hasTransports = services.some(s => s.transports.rest?.endpointResponsive ||
680
+ s.transports.mcp ||
681
+ s.transports.a2a?.agentCardAccessible);
682
+ apiCategory.checks.push({
683
+ name: 'Transport Available',
684
+ score: hasTransports ? 6 : 0,
685
+ maxScore: 6,
686
+ passed: hasTransports,
687
+ });
688
+ if (hasTransports)
689
+ apiCategory.score += 6;
690
+ apiCategory.status = generateCategoryStatus('api_access', apiCategory.score, apiCategory.maxScore);
691
+ // Checkout Flow category (25 pts max)
692
+ const checkoutCategory = {
693
+ score: 0,
694
+ maxScore: 25,
695
+ status: '',
696
+ checks: [],
697
+ };
698
+ // Checkout capability (12 pts)
699
+ const hasCheckout = checkout?.canCreateCheckout ?? false;
700
+ checkoutCategory.checks.push({
701
+ name: 'Checkout Capability',
702
+ score: hasCheckout ? 12 : 0,
703
+ maxScore: 12,
704
+ passed: hasCheckout,
705
+ });
706
+ if (hasCheckout)
707
+ checkoutCategory.score += 12;
708
+ // Checkout schema valid (5 pts)
709
+ const schemaValid = checkout?.checkoutSchemaValid ?? false;
710
+ checkoutCategory.checks.push({
711
+ name: 'Schema Valid',
712
+ score: schemaValid ? 5 : 0,
713
+ maxScore: 5,
714
+ passed: schemaValid,
715
+ });
716
+ if (schemaValid)
717
+ checkoutCategory.score += 5;
718
+ // Order flow (5 pts)
719
+ const hasOrder = checkout?.orderFlowSupported ?? false;
720
+ checkoutCategory.checks.push({
721
+ name: 'Order Flow',
722
+ score: hasOrder ? 5 : 0,
723
+ maxScore: 5,
724
+ passed: hasOrder,
725
+ });
726
+ if (hasOrder)
727
+ checkoutCategory.score += 5;
728
+ // Fulfillment (3 pts)
729
+ const hasFulfillment = checkout?.fulfillmentSupported ?? false;
730
+ checkoutCategory.checks.push({
731
+ name: 'Fulfillment Support',
732
+ score: hasFulfillment ? 3 : 0,
733
+ maxScore: 3,
734
+ passed: hasFulfillment,
735
+ });
736
+ if (hasFulfillment)
737
+ checkoutCategory.score += 3;
738
+ checkoutCategory.status = generateCategoryStatus('checkout_flow', checkoutCategory.score, checkoutCategory.maxScore);
739
+ // Payment Ready category (15 pts max)
740
+ const paymentCategory = {
741
+ score: 0,
742
+ maxScore: 15,
743
+ status: '',
744
+ checks: [],
745
+ };
746
+ // Payment handlers (8 pts)
747
+ const hasHandlers = (payment?.handlersFound ?? 0) > 0;
748
+ paymentCategory.checks.push({
749
+ name: 'Payment Handlers',
750
+ score: hasHandlers ? 8 : 0,
751
+ maxScore: 8,
752
+ passed: hasHandlers,
753
+ });
754
+ if (hasHandlers)
755
+ paymentCategory.score += 8;
756
+ // Signing keys (7 pts)
757
+ const hasSigningKeys = payment?.signingKeyValid ?? false;
758
+ paymentCategory.checks.push({
759
+ name: 'Signing Keys',
760
+ score: hasSigningKeys ? 7 : 0,
761
+ maxScore: 7,
762
+ passed: hasSigningKeys,
763
+ });
764
+ if (hasSigningKeys)
765
+ paymentCategory.score += 7;
766
+ paymentCategory.status = generateCategoryStatus('payment_ready', paymentCategory.score, paymentCategory.maxScore);
767
+ // Calculate totals
768
+ const total = discoveryCategory.score + apiCategory.score + checkoutCategory.score + paymentCategory.score;
769
+ const maxTotal = discoveryCategory.maxScore + apiCategory.maxScore + checkoutCategory.maxScore + paymentCategory.maxScore;
770
+ return {
771
+ discovery: discoveryCategory,
772
+ api_access: apiCategory,
773
+ checkout_flow: checkoutCategory,
774
+ payment_ready: paymentCategory,
775
+ total,
776
+ maxTotal,
777
+ };
778
+ }
779
+ /**
780
+ * Main simulation entry point
781
+ * Simulates a complete AI agent interaction with a UCP-enabled merchant
782
+ */
783
+ async function simulateAgentInteraction(domain, options = {}) {
784
+ const startTime = Date.now();
785
+ const timeoutMs = options.timeoutMs || DEFAULT_TIMEOUT_MS;
786
+ const fetchTimeout = Math.min(timeoutMs / 3, DEFAULT_FETCH_TIMEOUT_MS);
787
+ // Step 1: Discovery flow
788
+ const discovery = await simulateDiscoveryFlow(domain, fetchTimeout);
789
+ // Early exit if discovery failed
790
+ if (!discovery.success || !discovery.profileUrl) {
791
+ const durationMs = Date.now() - startTime;
792
+ const score_breakdown = calculateScoreBreakdown(discovery, [], [], undefined, undefined, undefined);
793
+ return {
794
+ ok: false,
795
+ domain,
796
+ simulatedAt: new Date().toISOString(),
797
+ durationMs,
798
+ overallScore: score_breakdown.total,
799
+ grade: 'F',
800
+ score_breakdown,
801
+ discovery,
802
+ capabilities: [],
803
+ services: [],
804
+ summary: {
805
+ totalSteps: discovery.steps.length,
806
+ passedSteps: discovery.steps.filter(s => s.status === 'passed').length,
807
+ failedSteps: discovery.steps.filter(s => s.status === 'failed').length,
808
+ warningSteps: discovery.steps.filter(s => s.status === 'warning').length,
809
+ skippedSteps: discovery.steps.filter(s => s.status === 'skipped').length,
810
+ },
811
+ recommendations: ['Ensure UCP profile is accessible at /.well-known/ucp or /.well-known/ucp.json'],
812
+ };
813
+ }
814
+ // Fetch full profile for detailed inspection
815
+ const profileResult = await fetchWithTimeout(discovery.profileUrl, fetchTimeout);
816
+ const profile = profileResult.data;
817
+ // Step 2: Inspect capabilities
818
+ const capabilities = await inspectCapabilities(profile, fetchTimeout);
819
+ // Step 3: Inspect services
820
+ const services = await inspectServices(profile, fetchTimeout);
821
+ // Step 4: REST API simulation (if applicable)
822
+ let restApi;
823
+ if (!options.skipRestApiTest) {
824
+ restApi = await simulateRestApi(profile, fetchTimeout);
825
+ }
826
+ // Step 5: Checkout flow simulation
827
+ let checkout;
828
+ if (options.testCheckoutFlow !== false) {
829
+ checkout = await simulateCheckoutFlow(profile, fetchTimeout);
830
+ }
831
+ // Step 6: Payment readiness check
832
+ const payment = await simulatePaymentReadiness(profile, fetchTimeout);
833
+ // Collect all steps
834
+ const allSteps = [
835
+ ...discovery.steps,
836
+ ...(restApi?.steps || []),
837
+ ...(checkout?.steps || []),
838
+ ...payment.steps,
839
+ ];
840
+ const durationMs = Date.now() - startTime;
841
+ // Generate recommendations
842
+ const recommendations = generateRecommendations(discovery, capabilities, services, restApi, checkout, payment);
843
+ // Calculate score breakdown (additive model)
844
+ const score_breakdown = calculateScoreBreakdown(discovery, capabilities, services, restApi, checkout, payment);
845
+ const overallScore = score_breakdown.total; // Use additive score as primary
846
+ return {
847
+ ok: discovery.success && (checkout?.success ?? true),
848
+ domain,
849
+ simulatedAt: new Date().toISOString(),
850
+ durationMs,
851
+ overallScore,
852
+ grade: getGrade(overallScore),
853
+ score_breakdown,
854
+ discovery,
855
+ capabilities,
856
+ services,
857
+ restApi,
858
+ checkout,
859
+ payment,
860
+ summary: {
861
+ totalSteps: allSteps.length,
862
+ passedSteps: allSteps.filter(s => s.status === 'passed').length,
863
+ failedSteps: allSteps.filter(s => s.status === 'failed').length,
864
+ warningSteps: allSteps.filter(s => s.status === 'warning').length,
865
+ skippedSteps: allSteps.filter(s => s.status === 'skipped').length,
866
+ },
867
+ recommendations,
868
+ };
869
+ }
870
+ //# sourceMappingURL=agent-simulator.js.map