@probemesh/sdk 0.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 (83) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +160 -0
  3. package/dist/chunk-257Y7LN2.js +827 -0
  4. package/dist/chunk-257Y7LN2.js.map +1 -0
  5. package/dist/chunk-2ASMVLG4.js +56 -0
  6. package/dist/chunk-2ASMVLG4.js.map +1 -0
  7. package/dist/chunk-2KWGHJYP.js +285 -0
  8. package/dist/chunk-2KWGHJYP.js.map +1 -0
  9. package/dist/chunk-3H7UGVI6.js +1117 -0
  10. package/dist/chunk-3H7UGVI6.js.map +1 -0
  11. package/dist/chunk-5Q3PDYIA.js +657 -0
  12. package/dist/chunk-5Q3PDYIA.js.map +1 -0
  13. package/dist/chunk-CXZOO3U4.js +550 -0
  14. package/dist/chunk-CXZOO3U4.js.map +1 -0
  15. package/dist/chunk-FRFBK4SY.js +270 -0
  16. package/dist/chunk-FRFBK4SY.js.map +1 -0
  17. package/dist/chunk-HJK52QJF.js +994 -0
  18. package/dist/chunk-HJK52QJF.js.map +1 -0
  19. package/dist/chunk-HNBDX7IU.js +705 -0
  20. package/dist/chunk-HNBDX7IU.js.map +1 -0
  21. package/dist/chunk-NYTV263W.js +116 -0
  22. package/dist/chunk-NYTV263W.js.map +1 -0
  23. package/dist/chunk-PXG54XOG.js +595 -0
  24. package/dist/chunk-PXG54XOG.js.map +1 -0
  25. package/dist/chunk-TDOBAMYM.js +607 -0
  26. package/dist/chunk-TDOBAMYM.js.map +1 -0
  27. package/dist/chunk-TV42EZSI.js +2157 -0
  28. package/dist/chunk-TV42EZSI.js.map +1 -0
  29. package/dist/chunk-UU2ZG7P7.js +408 -0
  30. package/dist/chunk-UU2ZG7P7.js.map +1 -0
  31. package/dist/chunk-WKN7QOCA.js +977 -0
  32. package/dist/chunk-WKN7QOCA.js.map +1 -0
  33. package/dist/chunk-ZJOLPBJJ.js +1091 -0
  34. package/dist/chunk-ZJOLPBJJ.js.map +1 -0
  35. package/dist/cli/audit-trail-export.cjs +1193 -0
  36. package/dist/cli/audit-trail-export.cjs.map +1 -0
  37. package/dist/cli/audit-trail-export.d.cts +1 -0
  38. package/dist/cli/audit-trail-export.d.ts +1 -0
  39. package/dist/cli/audit-trail-export.js +24 -0
  40. package/dist/cli/audit-trail-export.js.map +1 -0
  41. package/dist/cli/catalog-check.cjs +2687 -0
  42. package/dist/cli/catalog-check.cjs.map +1 -0
  43. package/dist/cli/catalog-check.d.cts +1 -0
  44. package/dist/cli/catalog-check.d.ts +1 -0
  45. package/dist/cli/catalog-check.js +26 -0
  46. package/dist/cli/catalog-check.js.map +1 -0
  47. package/dist/cli/probemesh-init.cjs +1049 -0
  48. package/dist/cli/probemesh-init.cjs.map +1 -0
  49. package/dist/cli/probemesh-init.d.cts +1 -0
  50. package/dist/cli/probemesh-init.d.ts +1 -0
  51. package/dist/cli/probemesh-init.js +22 -0
  52. package/dist/cli/probemesh-init.js.map +1 -0
  53. package/dist/cli/provider-conformance.cjs +6180 -0
  54. package/dist/cli/provider-conformance.cjs.map +1 -0
  55. package/dist/cli/provider-conformance.d.cts +1 -0
  56. package/dist/cli/provider-conformance.d.ts +1 -0
  57. package/dist/cli/provider-conformance.js +29 -0
  58. package/dist/cli/provider-conformance.js.map +1 -0
  59. package/dist/cli/provider-dossier-check.cjs +2978 -0
  60. package/dist/cli/provider-dossier-check.cjs.map +1 -0
  61. package/dist/cli/provider-dossier-check.d.cts +1 -0
  62. package/dist/cli/provider-dossier-check.d.ts +1 -0
  63. package/dist/cli/provider-dossier-check.js +27 -0
  64. package/dist/cli/provider-dossier-check.js.map +1 -0
  65. package/dist/cli/provider-dossier.cjs +1753 -0
  66. package/dist/cli/provider-dossier.cjs.map +1 -0
  67. package/dist/cli/provider-dossier.d.cts +1 -0
  68. package/dist/cli/provider-dossier.d.ts +1 -0
  69. package/dist/cli/provider-dossier.js +26 -0
  70. package/dist/cli/provider-dossier.js.map +1 -0
  71. package/dist/cli/x402-accept.cjs +6009 -0
  72. package/dist/cli/x402-accept.cjs.map +1 -0
  73. package/dist/cli/x402-accept.d.cts +1 -0
  74. package/dist/cli/x402-accept.d.ts +1 -0
  75. package/dist/cli/x402-accept.js +28 -0
  76. package/dist/cli/x402-accept.js.map +1 -0
  77. package/dist/index.cjs +13671 -0
  78. package/dist/index.cjs.map +1 -0
  79. package/dist/index.d.cts +2026 -0
  80. package/dist/index.d.ts +2026 -0
  81. package/dist/index.js +2560 -0
  82. package/dist/index.js.map +1 -0
  83. package/package.json +111 -0
@@ -0,0 +1,977 @@
1
+ import {
2
+ createProviderConformanceArtifact,
3
+ formatProviderConformanceArtifact
4
+ } from "./chunk-UU2ZG7P7.js";
5
+ import {
6
+ createProbeMesh,
7
+ startHostedRouter
8
+ } from "./chunk-TV42EZSI.js";
9
+ import {
10
+ isSharedJsonSchemaLike,
11
+ validateProviderManifest
12
+ } from "./chunk-3H7UGVI6.js";
13
+ import {
14
+ loadCliConfigDefault,
15
+ redactX402Secrets
16
+ } from "./chunk-NYTV263W.js";
17
+ import {
18
+ ProbeMeshError,
19
+ isProbeMeshError
20
+ } from "./chunk-2ASMVLG4.js";
21
+
22
+ // src/schemaValidation.ts
23
+ function validateJsonSchemaLikeValue(value, schema, options = {}) {
24
+ const errors = [];
25
+ validateValue(value, schema, options.path ?? "$", errors);
26
+ return {
27
+ valid: errors.length === 0,
28
+ errors
29
+ };
30
+ }
31
+ function assertJsonSchemaLikeValue(value, schema, options = {}) {
32
+ const result = validateJsonSchemaLikeValue(value, schema, options);
33
+ if (result.valid) {
34
+ return;
35
+ }
36
+ throw new ProbeMeshError({
37
+ code: "invalid_request",
38
+ message: `JSON schema-lite validation failed: ${formatValidationErrors(
39
+ result.errors
40
+ )}`
41
+ });
42
+ }
43
+ function validateValue(value, schema, path, errors) {
44
+ if (!isRecord(schema)) {
45
+ errors.push({
46
+ path,
47
+ message: "schema must be an object."
48
+ });
49
+ return;
50
+ }
51
+ if (schema.enum !== void 0) {
52
+ if (!Array.isArray(schema.enum)) {
53
+ errors.push({
54
+ path,
55
+ message: "schema enum must be an array."
56
+ });
57
+ } else if (!schema.enum.some((candidate) => deepEqual(candidate, value))) {
58
+ errors.push({
59
+ path,
60
+ message: "value must match one of the schema enum values."
61
+ });
62
+ }
63
+ }
64
+ if (schema.type !== void 0) {
65
+ validateType(value, schema.type, path, errors);
66
+ }
67
+ if (isRecord(value)) {
68
+ validateObjectValue(value, schema, path, errors);
69
+ }
70
+ if (Array.isArray(value)) {
71
+ validateArrayValue(value, schema, path, errors);
72
+ }
73
+ }
74
+ function validateType(value, type, path, errors) {
75
+ if (typeof type !== "string") {
76
+ errors.push({
77
+ path,
78
+ message: "schema type must be a string."
79
+ });
80
+ return;
81
+ }
82
+ const valid = type === "object" ? isRecord(value) : type === "array" ? Array.isArray(value) : type === "string" ? typeof value === "string" : type === "number" ? typeof value === "number" && Number.isFinite(value) : type === "integer" ? Number.isInteger(value) : type === "boolean" ? typeof value === "boolean" : type === "null" ? value === null : true;
83
+ if (!valid) {
84
+ errors.push({
85
+ path,
86
+ message: `value must be ${type}.`
87
+ });
88
+ }
89
+ }
90
+ function validateObjectValue(value, schema, path, errors) {
91
+ if (schema.required !== void 0) {
92
+ if (!Array.isArray(schema.required)) {
93
+ errors.push({
94
+ path,
95
+ message: "schema required must be an array."
96
+ });
97
+ } else {
98
+ for (const requiredKey of schema.required) {
99
+ if (typeof requiredKey !== "string") {
100
+ errors.push({
101
+ path,
102
+ message: "schema required entries must be strings."
103
+ });
104
+ continue;
105
+ }
106
+ if (!(requiredKey in value)) {
107
+ errors.push({
108
+ path: `${path}.${requiredKey}`,
109
+ message: "required property is missing."
110
+ });
111
+ }
112
+ }
113
+ }
114
+ }
115
+ const properties = isRecord(schema.properties) ? schema.properties : void 0;
116
+ if (properties) {
117
+ for (const [key, propertySchema] of Object.entries(properties)) {
118
+ if (key in value) {
119
+ validateValue(
120
+ value[key],
121
+ propertySchema,
122
+ `${path}.${key}`,
123
+ errors
124
+ );
125
+ }
126
+ }
127
+ }
128
+ if (schema.additionalProperties === false && properties) {
129
+ for (const key of Object.keys(value)) {
130
+ if (!(key in properties)) {
131
+ errors.push({
132
+ path: `${path}.${key}`,
133
+ message: "additional property is not allowed."
134
+ });
135
+ }
136
+ }
137
+ } else if (isRecord(schema.additionalProperties)) {
138
+ const additionalSchema = schema.additionalProperties;
139
+ for (const key of Object.keys(value)) {
140
+ if (!properties || !(key in properties)) {
141
+ validateValue(value[key], additionalSchema, `${path}.${key}`, errors);
142
+ }
143
+ }
144
+ }
145
+ }
146
+ function validateArrayValue(value, schema, path, errors) {
147
+ if (schema.items === void 0) {
148
+ return;
149
+ }
150
+ if (!isRecord(schema.items)) {
151
+ errors.push({
152
+ path,
153
+ message: "schema items must be an object."
154
+ });
155
+ return;
156
+ }
157
+ value.forEach((item, index) => {
158
+ validateValue(item, schema.items, `${path}.${index}`, errors);
159
+ });
160
+ }
161
+ function isRecord(value) {
162
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
163
+ }
164
+ function deepEqual(left, right) {
165
+ return JSON.stringify(left) === JSON.stringify(right);
166
+ }
167
+ function formatValidationErrors(errors) {
168
+ return errors.map((error) => `${error.path}: ${error.message}`).join("; ");
169
+ }
170
+
171
+ // src/providerConformance.ts
172
+ var DEFAULT_REQUIRED_RECEIPT_TYPES = ["provider_delivery"];
173
+ async function runProviderConformance(options) {
174
+ assertConformanceOptions(options);
175
+ const requiredReceiptTypes = options.requiredReceiptTypes ?? DEFAULT_REQUIRED_RECEIPT_TYPES;
176
+ const schemaValidation = options.schemaValidation ?? {};
177
+ const forbiddenOutputValues = options.forbiddenOutputValues ?? [];
178
+ const manifestValidation = validateProviderManifest(options.manifest);
179
+ const setupChecks = [
180
+ manifestValidation.valid ? pass("manifest_valid", "Provider manifest is valid.") : fail("manifest_valid", "Provider manifest is invalid.", {
181
+ errors: manifestValidation.errors
182
+ })
183
+ ];
184
+ const inputSchema = schemaValidation.input ? schemaForCapability(options.manifest.inputSchema, options.request.capability) : void 0;
185
+ if (inputSchema) {
186
+ const inputValidation = validateJsonSchemaLikeValue(
187
+ options.request.input,
188
+ inputSchema,
189
+ {
190
+ path: "$.input"
191
+ }
192
+ );
193
+ setupChecks.push(
194
+ inputValidation.valid ? pass("input_schema_valid", "Request input matches the declared schema.") : fail("input_schema_valid", "Request input does not match the declared schema.", {
195
+ errors: inputValidation.errors
196
+ })
197
+ );
198
+ } else if (schemaValidation.input) {
199
+ setupChecks.push(
200
+ fail("input_schema_valid", "No input schema is declared for this capability.")
201
+ );
202
+ }
203
+ if (setupChecks.some((check) => check.status === "failed")) {
204
+ const checks2 = [
205
+ ...setupChecks,
206
+ createSecretSafeOutputCheck({
207
+ checks: setupChecks,
208
+ requiredReceiptTypes,
209
+ telemetryEvents: [],
210
+ forbiddenOutputValues
211
+ })
212
+ ];
213
+ const status2 = statusFromChecks(checks2);
214
+ const sanitizedChecks2 = sanitizeConformanceOutput(checks2, forbiddenOutputValues);
215
+ return {
216
+ status: status2,
217
+ checks: sanitizedChecks2,
218
+ telemetryEvents: [],
219
+ summary: createSummary({
220
+ status: status2,
221
+ checks: sanitizedChecks2,
222
+ telemetryEvents: [],
223
+ requiredReceiptTypes
224
+ })
225
+ };
226
+ }
227
+ const telemetryEvents = [];
228
+ const hostedRouter = await startHostedRouter({
229
+ port: 0,
230
+ adapters: options.adapters,
231
+ apiKeys: options.apiKey ? [options.apiKey] : void 0,
232
+ onTelemetryEvent(event) {
233
+ telemetryEvents.push(event);
234
+ }
235
+ });
236
+ let response;
237
+ let error;
238
+ try {
239
+ const probemesh = createProbeMesh({
240
+ hosted: {
241
+ baseUrl: hostedRouter.url,
242
+ apiKey: options.apiKey
243
+ }
244
+ });
245
+ try {
246
+ response = await probemesh.call(options.request);
247
+ } catch (caught) {
248
+ error = toConformanceError(caught);
249
+ }
250
+ } finally {
251
+ await hostedRouter.close();
252
+ }
253
+ const baseChecks = [
254
+ ...setupChecks,
255
+ ...createExecutionChecks({
256
+ response,
257
+ error,
258
+ telemetryEvents,
259
+ expectedProviderId: options.expectedProviderId ?? options.manifest.id,
260
+ requiredReceiptTypes
261
+ })
262
+ ];
263
+ const outputSchema = schemaValidation.output ? schemaForCapability(options.manifest.outputSchema, options.request.capability) : void 0;
264
+ if (outputSchema) {
265
+ baseChecks.push(outputSchemaCheck(response, outputSchema));
266
+ } else if (schemaValidation.output) {
267
+ baseChecks.push(
268
+ fail("output_schema_valid", "No output schema is declared for this capability.")
269
+ );
270
+ }
271
+ const checks = [
272
+ ...baseChecks,
273
+ createSecretSafeOutputCheck({
274
+ response,
275
+ error,
276
+ telemetryEvents,
277
+ checks: baseChecks,
278
+ requiredReceiptTypes,
279
+ forbiddenOutputValues
280
+ })
281
+ ];
282
+ const status = statusFromChecks(checks);
283
+ const sanitizedChecks = sanitizeConformanceOutput(checks, forbiddenOutputValues);
284
+ const sanitizedResponse = sanitizeConformanceOutput(response, forbiddenOutputValues);
285
+ const sanitizedError = sanitizeConformanceOutput(error, forbiddenOutputValues);
286
+ const sanitizedTelemetryEvents = sanitizeConformanceOutput(
287
+ telemetryEvents,
288
+ forbiddenOutputValues
289
+ );
290
+ return {
291
+ status,
292
+ checks: sanitizedChecks,
293
+ response: sanitizedResponse,
294
+ error: sanitizedError,
295
+ telemetryEvents: sanitizedTelemetryEvents,
296
+ summary: createSummary({
297
+ status,
298
+ checks: sanitizedChecks,
299
+ response: sanitizedResponse,
300
+ error: sanitizedError,
301
+ telemetryEvents: sanitizedTelemetryEvents,
302
+ requiredReceiptTypes
303
+ })
304
+ };
305
+ }
306
+ function createProviderConformanceReport(result, options = {}) {
307
+ const receiptRefs = result.response?.receiptRefs ?? result.error?.receiptRefs;
308
+ const report = {
309
+ title: options.title ?? "ProbeMesh Provider Conformance Report",
310
+ status: result.status,
311
+ summary: result.summary,
312
+ checks: result.checks,
313
+ receipts: summarizeReceipts(receiptRefs),
314
+ route: result.summary.route,
315
+ timelineEventTypes: result.summary.timelineEventTypes,
316
+ telemetryEventTypes: result.summary.telemetryEventTypes,
317
+ safety: result.summary.safety,
318
+ retrySafety: result.summary.retrySafety,
319
+ error: result.error
320
+ };
321
+ return sanitizeConformanceOutput(report, options.forbiddenOutputValues ?? []);
322
+ }
323
+ function formatProviderConformanceReport(report, options = {}) {
324
+ const sanitizedReport = sanitizeConformanceOutput(report, []);
325
+ if ((options.format ?? "markdown") === "json") {
326
+ return JSON.stringify(sanitizedReport, null, 2);
327
+ }
328
+ return formatConformanceReportMarkdown(sanitizedReport);
329
+ }
330
+ function createExecutionChecks(options) {
331
+ return [
332
+ completedResponseCheck(options.response, options.error),
333
+ selectedProviderCheck(
334
+ options.response,
335
+ options.error,
336
+ options.expectedProviderId
337
+ ),
338
+ routeMetadataCheck(options.response, options.error),
339
+ timelineEventsCheck(options.response, options.error),
340
+ requiredReceiptsCheck(options.response, options.error, options.requiredReceiptTypes),
341
+ safetyMetadataCheck(options.response, options.error, options.requiredReceiptTypes),
342
+ retrySafetyCheck(options.response, options.error),
343
+ telemetryMatchesTimelineCheck(options.response, options.telemetryEvents)
344
+ ];
345
+ }
346
+ function completedResponseCheck(response, error) {
347
+ if (response?.status === "completed") {
348
+ return pass("completed_response", "Hosted provider call completed.");
349
+ }
350
+ return fail(
351
+ "completed_response",
352
+ error ? `Hosted provider call failed with ${error.code}.` : "Hosted provider call did not return a completed response.",
353
+ {
354
+ errorCode: error?.code,
355
+ errorMessage: error?.message
356
+ }
357
+ );
358
+ }
359
+ function selectedProviderCheck(response, error, expectedProviderId) {
360
+ const providerId = response?.provider.id;
361
+ if (providerId && (!expectedProviderId || providerId === expectedProviderId)) {
362
+ return pass("selected_provider", "Hosted route selected the expected provider.", {
363
+ providerId
364
+ });
365
+ }
366
+ return fail("selected_provider", "Hosted route did not select the expected provider.", {
367
+ expectedProviderId,
368
+ providerId,
369
+ errorProvider: error?.provider
370
+ });
371
+ }
372
+ function routeMetadataCheck(response, error) {
373
+ if (response?.route?.mode === "hosted" && typeof response.route.routeId === "string" && response.route.routeId.length > 0) {
374
+ return pass("route_metadata", "Hosted route metadata is present.", {
375
+ routeId: response.route.routeId,
376
+ selectedProviderId: response.route.selectedProviderId
377
+ });
378
+ }
379
+ return fail("route_metadata", "Hosted route metadata is missing.", {
380
+ errorRouteId: error?.route?.routeId
381
+ });
382
+ }
383
+ function timelineEventsCheck(response, error) {
384
+ const timelineEventTypes = response?.timeline?.events.map((event) => event.type) ?? [];
385
+ const requiredEvents = [
386
+ "call.requested",
387
+ "provider.attempted",
388
+ "provider.selected",
389
+ "call.completed"
390
+ ];
391
+ const missingEvents = requiredEvents.filter(
392
+ (eventType) => !timelineEventTypes.includes(eventType)
393
+ );
394
+ if (missingEvents.length === 0) {
395
+ return pass("timeline_events", "Hosted timeline contains the provider conformance events.", {
396
+ timelineEventTypes
397
+ });
398
+ }
399
+ return fail("timeline_events", "Hosted timeline is missing required events.", {
400
+ missingEvents,
401
+ timelineEventTypes,
402
+ errorTimelineEventTypes: error?.timeline?.events.map((event) => event.type)
403
+ });
404
+ }
405
+ function requiredReceiptsCheck(response, error, requiredReceiptTypes) {
406
+ const receiptTypes = getReceiptTypes(response?.receiptRefs);
407
+ const missingReceiptTypes = requiredReceiptTypes.filter(
408
+ (receiptType) => !receiptTypes.includes(receiptType)
409
+ );
410
+ if (missingReceiptTypes.length === 0) {
411
+ return pass("required_receipts", "Required receipts are present.", {
412
+ receiptTypes
413
+ });
414
+ }
415
+ return fail("required_receipts", "Required receipts are missing.", {
416
+ missingReceiptTypes,
417
+ receiptTypes,
418
+ errorReceiptTypes: getReceiptTypes(error?.receiptRefs)
419
+ });
420
+ }
421
+ function safetyMetadataCheck(response, error, requiredReceiptTypes) {
422
+ const requiresPaymentSafety = requiredReceiptTypes.some(
423
+ (receiptType) => ["payment_proof", "settlement_confirmation"].includes(receiptType)
424
+ );
425
+ if (!requiresPaymentSafety) {
426
+ return pass("safety_metadata", "Payment safety metadata is not required for this conformance run.");
427
+ }
428
+ if (response?.safety) {
429
+ return pass("safety_metadata", "Safety metadata is present for paid conformance.", {
430
+ safety: response.safety
431
+ });
432
+ }
433
+ return fail("safety_metadata", "Safety metadata is missing for paid conformance.", {
434
+ errorSafety: error?.safety
435
+ });
436
+ }
437
+ function retrySafetyCheck(response, error) {
438
+ if (response?.retrySafety) {
439
+ return pass("retry_safety", "Hosted response includes retry safety metadata.", {
440
+ retrySafety: response.retrySafety
441
+ });
442
+ }
443
+ return fail("retry_safety", "Hosted response retry safety metadata is missing.", {
444
+ errorRetrySafety: error?.retrySafety
445
+ });
446
+ }
447
+ function telemetryMatchesTimelineCheck(response, telemetryEvents) {
448
+ const timelineEventTypes = response?.timeline?.events.map((event) => event.type) ?? [];
449
+ const telemetryEventTypes = telemetryEvents.map((event) => event.type);
450
+ const matches = timelineEventTypes.length > 0 && timelineEventTypes.length === telemetryEventTypes.length && timelineEventTypes.every(
451
+ (eventType, index) => eventType === telemetryEventTypes[index]
452
+ );
453
+ if (matches) {
454
+ return pass("telemetry_matches_timeline", "Telemetry mirrors the hosted timeline.", {
455
+ telemetryEventTypes
456
+ });
457
+ }
458
+ return fail("telemetry_matches_timeline", "Telemetry does not mirror the hosted timeline.", {
459
+ timelineEventTypes,
460
+ telemetryEventTypes
461
+ });
462
+ }
463
+ function outputSchemaCheck(response, schema) {
464
+ if (!response) {
465
+ return fail("output_schema_valid", "Cannot validate output schema without a response.");
466
+ }
467
+ const result = validateJsonSchemaLikeValue(response.data, schema, {
468
+ path: "$.data"
469
+ });
470
+ if (result.valid) {
471
+ return pass("output_schema_valid", "Provider response data matches the declared schema.");
472
+ }
473
+ return fail("output_schema_valid", "Provider response data does not match the declared schema.", {
474
+ errors: result.errors
475
+ });
476
+ }
477
+ function createSecretSafeOutputCheck(options) {
478
+ const forbiddenOutputValues = options.forbiddenOutputValues.filter(
479
+ (value) => value.length > 0
480
+ );
481
+ const candidate = {
482
+ response: options.response,
483
+ error: options.error,
484
+ telemetryEvents: options.telemetryEvents,
485
+ checks: options.checks,
486
+ summary: createSummary({
487
+ status: statusFromChecks(options.checks),
488
+ checks: options.checks,
489
+ response: options.response,
490
+ error: options.error,
491
+ telemetryEvents: options.telemetryEvents,
492
+ requiredReceiptTypes: options.requiredReceiptTypes
493
+ })
494
+ };
495
+ const serialized = JSON.stringify(candidate);
496
+ const leakedValues = forbiddenOutputValues.filter(
497
+ (value) => serialized.includes(value)
498
+ );
499
+ if (leakedValues.length === 0) {
500
+ return pass("secret_safe_output", "Serialized conformance output contains no forbidden values.", {
501
+ checkedForbiddenValueCount: forbiddenOutputValues.length
502
+ });
503
+ }
504
+ return fail("secret_safe_output", "Serialized conformance output contains forbidden values.", {
505
+ leakedValueCount: leakedValues.length
506
+ });
507
+ }
508
+ function createSummary(options) {
509
+ const receiptRefs = options.response?.receiptRefs ?? options.error?.receiptRefs;
510
+ const timeline = options.response?.timeline ?? options.error?.timeline;
511
+ const route = options.response?.route ?? options.error?.route;
512
+ const failedCheckNames = options.checks.filter((check) => check.status === "failed").map((check) => check.name);
513
+ return {
514
+ status: options.status,
515
+ totalChecks: options.checks.length,
516
+ passedChecks: options.checks.length - failedCheckNames.length,
517
+ failedChecks: failedCheckNames.length,
518
+ failedCheckNames,
519
+ callId: options.response?.callId ?? options.error?.callId,
520
+ providerId: options.response?.provider.id ?? options.error?.provider,
521
+ errorCode: options.error?.code,
522
+ receiptTypes: getReceiptTypes(receiptRefs),
523
+ requiredReceiptTypes: [...options.requiredReceiptTypes],
524
+ timelineEventTypes: timeline?.events.map((event) => event.type) ?? [],
525
+ telemetryEventTypes: options.telemetryEvents.map((event) => event.type),
526
+ safety: options.response?.safety ?? options.error?.safety,
527
+ retrySafety: options.response?.retrySafety ?? options.error?.retrySafety,
528
+ route: route ? {
529
+ routeId: route.routeId,
530
+ selectedProviderId: route.selectedProviderId,
531
+ fallbackUsed: route.explanation?.fallbackUsed,
532
+ fallbackCount: route.explanation?.fallbackCount,
533
+ stopReason: route.explanation?.stopReason
534
+ } : void 0
535
+ };
536
+ }
537
+ function summarizeReceipts(receiptRefs) {
538
+ const receipts = receiptRefs ?? [];
539
+ const byType = {};
540
+ for (const receiptRef of receipts) {
541
+ byType[receiptRef.type] = (byType[receiptRef.type] ?? 0) + 1;
542
+ }
543
+ return {
544
+ total: receipts.length,
545
+ types: getReceiptTypes(receipts),
546
+ providers: [
547
+ ...new Set(receipts.map((receiptRef) => receiptRef.provider))
548
+ ],
549
+ byType,
550
+ hasPaymentProof: receipts.some(
551
+ (receiptRef) => receiptRef.type === "payment_proof"
552
+ ),
553
+ hasSettlementConfirmation: receipts.some(
554
+ (receiptRef) => receiptRef.type === "settlement_confirmation"
555
+ ),
556
+ hasProviderDelivery: receipts.some(
557
+ (receiptRef) => receiptRef.type === "provider_delivery"
558
+ )
559
+ };
560
+ }
561
+ function formatConformanceReportMarkdown(report) {
562
+ const lines = [
563
+ `# ${report.title}`,
564
+ "",
565
+ `Conformance: ${report.status}`,
566
+ `Provider: ${report.summary.providerId ?? "unknown"}`,
567
+ `Call: ${report.summary.callId ?? "unknown"}`,
568
+ `Error: ${report.summary.errorCode ?? "none"}`,
569
+ "",
570
+ "## Checks",
571
+ ...report.checks.map(
572
+ (check) => `- [${check.status === "passed" ? "x" : " "}] ${check.name}: ${check.message}`
573
+ ),
574
+ "",
575
+ "## Receipts",
576
+ `- Total: ${report.receipts.total}`,
577
+ `- Types: ${formatInlineList(report.receipts.types)}`,
578
+ `- Providers: ${formatInlineList(report.receipts.providers)}`,
579
+ "",
580
+ "## Route",
581
+ `- Route ID: ${report.route?.routeId ?? "none"}`,
582
+ `- Selected provider: ${report.route?.selectedProviderId ?? "none"}`,
583
+ `- Stop reason: ${report.route?.stopReason ?? "none"}`,
584
+ "",
585
+ "## Safety",
586
+ `- Payment: ${report.safety?.paymentStatus ?? "unknown"}`,
587
+ `- Settlement: ${report.safety?.settlementStatus ?? "unknown"}`,
588
+ `- Delivery: ${report.safety?.deliveryStatus ?? "unknown"}`,
589
+ `- Money may have moved: ${String(
590
+ report.safety?.moneyMayHaveMoved ?? false
591
+ )}`,
592
+ "",
593
+ "## Retry Safety",
594
+ `- Retryable: ${String(report.retrySafety?.retryable ?? false)}`,
595
+ `- Safe to auto retry: ${String(
596
+ report.retrySafety?.safeToAutoRetry ?? false
597
+ )}`,
598
+ `- Reason: ${report.retrySafety?.reason ?? "unknown"}`,
599
+ "",
600
+ "## Timeline",
601
+ `- Events: ${formatInlineList(report.timelineEventTypes)}`,
602
+ `- Telemetry: ${formatInlineList(report.telemetryEventTypes)}`
603
+ ];
604
+ return `${lines.join("\n")}
605
+ `;
606
+ }
607
+ function schemaForCapability(declaration, capability) {
608
+ if (isSharedJsonSchemaLike(declaration)) {
609
+ return declaration;
610
+ }
611
+ return declaration[capability];
612
+ }
613
+ function toConformanceError(error) {
614
+ if (isProbeMeshError(error)) {
615
+ return {
616
+ name: "ProbeMeshError",
617
+ code: error.code,
618
+ message: error.message,
619
+ status: "failed",
620
+ capability: error.capability,
621
+ provider: error.provider,
622
+ callId: error.callId,
623
+ receiptRefs: error.receiptRefs,
624
+ safety: error.safety,
625
+ timeline: error.timeline,
626
+ retrySafety: error.retrySafety,
627
+ route: error.route
628
+ };
629
+ }
630
+ return {
631
+ name: "ProbeMeshError",
632
+ code: "provider_unavailable",
633
+ message: error instanceof Error ? error.message : "provider conformance call failed.",
634
+ status: "failed"
635
+ };
636
+ }
637
+ function sanitizeConformanceOutput(value, forbiddenOutputValues) {
638
+ const forbiddenValues = forbiddenOutputValues.filter((entry) => entry.length > 0);
639
+ return redactX402Secrets(
640
+ replaceForbiddenValues(value, forbiddenValues, /* @__PURE__ */ new WeakSet())
641
+ );
642
+ }
643
+ function replaceForbiddenValues(value, forbiddenValues, seen) {
644
+ if (typeof value === "string") {
645
+ return forbiddenValues.reduce(
646
+ (current, forbidden) => current.split(forbidden).join("[REDACTED]"),
647
+ value
648
+ );
649
+ }
650
+ if (value === null || typeof value !== "object" || value instanceof Date) {
651
+ return value;
652
+ }
653
+ if (seen.has(value)) {
654
+ return "[Circular]";
655
+ }
656
+ seen.add(value);
657
+ if (Array.isArray(value)) {
658
+ const entries2 = value.map(
659
+ (entry) => replaceForbiddenValues(entry, forbiddenValues, seen)
660
+ );
661
+ seen.delete(value);
662
+ return entries2;
663
+ }
664
+ const entries = Object.fromEntries(
665
+ Object.entries(value).map(([key, entry]) => [
666
+ key,
667
+ replaceForbiddenValues(entry, forbiddenValues, seen)
668
+ ])
669
+ );
670
+ seen.delete(value);
671
+ return entries;
672
+ }
673
+ function assertConformanceOptions(options) {
674
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
675
+ throw new ProbeMeshError({
676
+ code: "invalid_request",
677
+ message: "Provider conformance options must be an object."
678
+ });
679
+ }
680
+ if (!Array.isArray(options.adapters)) {
681
+ throw new ProbeMeshError({
682
+ code: "invalid_request",
683
+ message: "Provider conformance adapters must be an array."
684
+ });
685
+ }
686
+ if (!options.request || typeof options.request !== "object" || Array.isArray(options.request)) {
687
+ throw new ProbeMeshError({
688
+ code: "invalid_request",
689
+ message: "Provider conformance request must be an object."
690
+ });
691
+ }
692
+ }
693
+ function getReceiptTypes(receiptRefs) {
694
+ return [...new Set((receiptRefs ?? []).map((receiptRef) => receiptRef.type))];
695
+ }
696
+ function statusFromChecks(checks) {
697
+ return checks.every((check) => check.status === "passed") ? "passed" : "failed";
698
+ }
699
+ function formatInlineList(values) {
700
+ return values.length > 0 ? values.join(", ") : "none";
701
+ }
702
+ function pass(name, message, details) {
703
+ return {
704
+ name,
705
+ status: "passed",
706
+ message,
707
+ details
708
+ };
709
+ }
710
+ function fail(name, message, details) {
711
+ return {
712
+ name,
713
+ status: "failed",
714
+ message,
715
+ details
716
+ };
717
+ }
718
+
719
+ // src/cli/providerConformanceCli.ts
720
+ import { mkdir, writeFile } from "fs/promises";
721
+ import { dirname, resolve } from "path";
722
+ var USAGE = "Usage: probemesh-conformance --config <path> [--format markdown|json] [--out-json <path>] [--out-md <path>] [--out-artifact <path>] [--title <text>] [--artifact-id <text>] [--generated-at <iso>]";
723
+ async function runProviderConformanceCli(options = {}) {
724
+ const argv = options.argv ?? [];
725
+ const cwd = options.cwd ?? process.cwd();
726
+ try {
727
+ const parsedArgs = parseCliArgs(argv);
728
+ if (parsedArgs.help) {
729
+ return {
730
+ exitCode: 0,
731
+ skipped: true,
732
+ stdout: `${USAGE}
733
+ `
734
+ };
735
+ }
736
+ if (!parsedArgs.configPath) {
737
+ return cliFailure("--config <path> is required.");
738
+ }
739
+ const configExport = await loadCliConfig(parsedArgs.configPath, {
740
+ argv,
741
+ cwd
742
+ });
743
+ if (isCliSkip(configExport)) {
744
+ return {
745
+ exitCode: 0,
746
+ skipped: true,
747
+ skip: configExport,
748
+ stdout: `provider conformance skipped: ${configExport.reason}
749
+ `
750
+ };
751
+ }
752
+ assertCliConfig(configExport);
753
+ const result = await runProviderConformance(configExport.conformance);
754
+ const report = createProviderConformanceReport(result, {
755
+ ...configExport.report ?? {},
756
+ title: parsedArgs.title ?? configExport.report?.title
757
+ });
758
+ const outputs = {
759
+ json: parsedArgs.outJson ?? configExport.outputs?.json,
760
+ markdown: parsedArgs.outMarkdown ?? configExport.outputs?.markdown,
761
+ artifact: parsedArgs.outArtifact ?? configExport.outputs?.artifact
762
+ };
763
+ const artifact = createOptionalArtifact({
764
+ config: configExport,
765
+ report,
766
+ artifactId: parsedArgs.artifactId,
767
+ generatedAt: parsedArgs.generatedAt,
768
+ requestedArtifactOutput: outputs.artifact
769
+ });
770
+ const selectedFormat = parsedArgs.format ?? configExport.report?.format ?? "markdown";
771
+ const stdout = formatProviderConformanceReport(report, {
772
+ format: selectedFormat
773
+ });
774
+ const artifactPaths = await writeArtifacts({
775
+ cwd,
776
+ report,
777
+ artifact,
778
+ outputs
779
+ });
780
+ return {
781
+ exitCode: result.status === "passed" ? 0 : 1,
782
+ skipped: false,
783
+ result,
784
+ report,
785
+ artifact,
786
+ stdout,
787
+ artifactPaths
788
+ };
789
+ } catch (error) {
790
+ return cliFailure(
791
+ error instanceof Error ? error.message : "Unknown provider conformance CLI failure."
792
+ );
793
+ }
794
+ }
795
+ function parseCliArgs(argv) {
796
+ const parsedArgs = {};
797
+ for (let index = 0; index < argv.length; index += 1) {
798
+ const arg = argv[index];
799
+ if (arg === "--") {
800
+ continue;
801
+ }
802
+ if (arg === "--help" || arg === "-h") {
803
+ parsedArgs.help = true;
804
+ continue;
805
+ }
806
+ if (arg === "--config") {
807
+ parsedArgs.configPath = requireValue(argv, index, arg);
808
+ index += 1;
809
+ continue;
810
+ }
811
+ if (arg === "--format") {
812
+ const format = requireValue(argv, index, arg);
813
+ if (format !== "markdown" && format !== "json") {
814
+ throw new Error("--format must be markdown or json.");
815
+ }
816
+ parsedArgs.format = format;
817
+ index += 1;
818
+ continue;
819
+ }
820
+ if (arg === "--out-json") {
821
+ parsedArgs.outJson = requireValue(argv, index, arg);
822
+ index += 1;
823
+ continue;
824
+ }
825
+ if (arg === "--out-md") {
826
+ parsedArgs.outMarkdown = requireValue(argv, index, arg);
827
+ index += 1;
828
+ continue;
829
+ }
830
+ if (arg === "--out-artifact") {
831
+ parsedArgs.outArtifact = requireValue(argv, index, arg);
832
+ index += 1;
833
+ continue;
834
+ }
835
+ if (arg === "--title") {
836
+ parsedArgs.title = requireValue(argv, index, arg);
837
+ index += 1;
838
+ continue;
839
+ }
840
+ if (arg === "--artifact-id") {
841
+ parsedArgs.artifactId = requireValue(argv, index, arg);
842
+ index += 1;
843
+ continue;
844
+ }
845
+ if (arg === "--generated-at") {
846
+ parsedArgs.generatedAt = requireValue(argv, index, arg);
847
+ index += 1;
848
+ continue;
849
+ }
850
+ throw new Error(`Unknown argument "${arg}".`);
851
+ }
852
+ return parsedArgs;
853
+ }
854
+ function requireValue(argv, index, flag) {
855
+ const value = argv[index + 1];
856
+ if (!value || value.startsWith("--")) {
857
+ throw new Error(`${flag} requires a value.`);
858
+ }
859
+ return value;
860
+ }
861
+ async function loadCliConfig(configPath, context) {
862
+ const configExport = await loadCliConfigDefault(configPath, context.cwd);
863
+ if (typeof configExport === "function") {
864
+ return configExport(context);
865
+ }
866
+ return configExport;
867
+ }
868
+ function isCliSkip(value) {
869
+ return !!value && typeof value === "object" && !Array.isArray(value) && value.skip === true && typeof value.reason === "string";
870
+ }
871
+ function assertCliConfig(config) {
872
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
873
+ throw new Error("Provider conformance CLI config must be an object.");
874
+ }
875
+ const candidate = config;
876
+ if (!candidate.conformance || typeof candidate.conformance !== "object" || Array.isArray(candidate.conformance)) {
877
+ throw new Error("Provider conformance CLI config requires conformance options.");
878
+ }
879
+ if (candidate.report !== void 0 && !isRecord2(candidate.report)) {
880
+ throw new Error("Provider conformance CLI report config must be an object.");
881
+ }
882
+ if (candidate.artifact !== void 0 && !isRecord2(candidate.artifact)) {
883
+ throw new Error("Provider conformance CLI artifact config must be an object.");
884
+ }
885
+ if (candidate.outputs !== void 0) {
886
+ assertOutputs(candidate.outputs);
887
+ }
888
+ }
889
+ function assertOutputs(outputs) {
890
+ if (!isRecord2(outputs)) {
891
+ throw new Error("Provider conformance CLI outputs must be an object.");
892
+ }
893
+ for (const [key, value] of Object.entries(outputs)) {
894
+ if (!["json", "markdown", "artifact"].includes(key) || value !== void 0 && typeof value !== "string") {
895
+ throw new Error(
896
+ "Provider conformance CLI outputs may only include json, markdown, and artifact string paths."
897
+ );
898
+ }
899
+ }
900
+ }
901
+ function createOptionalArtifact(options) {
902
+ if (!options.config.artifact && !options.requestedArtifactOutput) {
903
+ return void 0;
904
+ }
905
+ return createProviderConformanceArtifact({
906
+ manifest: options.config.conformance.manifest,
907
+ report: options.report,
908
+ artifactId: options.artifactId ?? options.config.artifact?.artifactId,
909
+ generatedAt: options.generatedAt ?? options.config.artifact?.generatedAt,
910
+ forbiddenOutputValues: options.config.artifact?.forbiddenOutputValues ?? options.config.report?.forbiddenOutputValues ?? options.config.conformance.forbiddenOutputValues
911
+ });
912
+ }
913
+ async function writeArtifacts(options) {
914
+ const artifactPaths = {};
915
+ if (options.outputs.json) {
916
+ const path = resolve(options.cwd, options.outputs.json);
917
+ await writeTextFile(
918
+ path,
919
+ formatProviderConformanceReport(options.report, {
920
+ format: "json"
921
+ })
922
+ );
923
+ artifactPaths.json = path;
924
+ }
925
+ if (options.outputs.markdown) {
926
+ const path = resolve(options.cwd, options.outputs.markdown);
927
+ await writeTextFile(
928
+ path,
929
+ formatProviderConformanceReport(options.report, {
930
+ format: "markdown"
931
+ })
932
+ );
933
+ artifactPaths.markdown = path;
934
+ }
935
+ if (options.outputs.artifact) {
936
+ if (!options.artifact) {
937
+ throw new Error("Provider conformance artifact output requested but no artifact was created.");
938
+ }
939
+ const path = resolve(options.cwd, options.outputs.artifact);
940
+ await writeTextFile(
941
+ path,
942
+ formatProviderConformanceArtifact(options.artifact, {
943
+ format: "json"
944
+ })
945
+ );
946
+ artifactPaths.artifact = path;
947
+ }
948
+ return artifactPaths;
949
+ }
950
+ async function writeTextFile(path, body) {
951
+ await mkdir(dirname(path), {
952
+ recursive: true
953
+ });
954
+ await writeFile(path, body);
955
+ }
956
+ function cliFailure(message) {
957
+ return {
958
+ exitCode: 2,
959
+ skipped: false,
960
+ stdout: "",
961
+ stderr: `Provider conformance CLI failed: ${message}
962
+ `
963
+ };
964
+ }
965
+ function isRecord2(value) {
966
+ return typeof value === "object" && value !== null && !Array.isArray(value);
967
+ }
968
+
969
+ export {
970
+ validateJsonSchemaLikeValue,
971
+ assertJsonSchemaLikeValue,
972
+ runProviderConformance,
973
+ createProviderConformanceReport,
974
+ formatProviderConformanceReport,
975
+ runProviderConformanceCli
976
+ };
977
+ //# sourceMappingURL=chunk-WKN7QOCA.js.map