@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.
- package/LICENSE +22 -0
- package/README.md +160 -0
- package/dist/chunk-257Y7LN2.js +827 -0
- package/dist/chunk-257Y7LN2.js.map +1 -0
- package/dist/chunk-2ASMVLG4.js +56 -0
- package/dist/chunk-2ASMVLG4.js.map +1 -0
- package/dist/chunk-2KWGHJYP.js +285 -0
- package/dist/chunk-2KWGHJYP.js.map +1 -0
- package/dist/chunk-3H7UGVI6.js +1117 -0
- package/dist/chunk-3H7UGVI6.js.map +1 -0
- package/dist/chunk-5Q3PDYIA.js +657 -0
- package/dist/chunk-5Q3PDYIA.js.map +1 -0
- package/dist/chunk-CXZOO3U4.js +550 -0
- package/dist/chunk-CXZOO3U4.js.map +1 -0
- package/dist/chunk-FRFBK4SY.js +270 -0
- package/dist/chunk-FRFBK4SY.js.map +1 -0
- package/dist/chunk-HJK52QJF.js +994 -0
- package/dist/chunk-HJK52QJF.js.map +1 -0
- package/dist/chunk-HNBDX7IU.js +705 -0
- package/dist/chunk-HNBDX7IU.js.map +1 -0
- package/dist/chunk-NYTV263W.js +116 -0
- package/dist/chunk-NYTV263W.js.map +1 -0
- package/dist/chunk-PXG54XOG.js +595 -0
- package/dist/chunk-PXG54XOG.js.map +1 -0
- package/dist/chunk-TDOBAMYM.js +607 -0
- package/dist/chunk-TDOBAMYM.js.map +1 -0
- package/dist/chunk-TV42EZSI.js +2157 -0
- package/dist/chunk-TV42EZSI.js.map +1 -0
- package/dist/chunk-UU2ZG7P7.js +408 -0
- package/dist/chunk-UU2ZG7P7.js.map +1 -0
- package/dist/chunk-WKN7QOCA.js +977 -0
- package/dist/chunk-WKN7QOCA.js.map +1 -0
- package/dist/chunk-ZJOLPBJJ.js +1091 -0
- package/dist/chunk-ZJOLPBJJ.js.map +1 -0
- package/dist/cli/audit-trail-export.cjs +1193 -0
- package/dist/cli/audit-trail-export.cjs.map +1 -0
- package/dist/cli/audit-trail-export.d.cts +1 -0
- package/dist/cli/audit-trail-export.d.ts +1 -0
- package/dist/cli/audit-trail-export.js +24 -0
- package/dist/cli/audit-trail-export.js.map +1 -0
- package/dist/cli/catalog-check.cjs +2687 -0
- package/dist/cli/catalog-check.cjs.map +1 -0
- package/dist/cli/catalog-check.d.cts +1 -0
- package/dist/cli/catalog-check.d.ts +1 -0
- package/dist/cli/catalog-check.js +26 -0
- package/dist/cli/catalog-check.js.map +1 -0
- package/dist/cli/probemesh-init.cjs +1049 -0
- package/dist/cli/probemesh-init.cjs.map +1 -0
- package/dist/cli/probemesh-init.d.cts +1 -0
- package/dist/cli/probemesh-init.d.ts +1 -0
- package/dist/cli/probemesh-init.js +22 -0
- package/dist/cli/probemesh-init.js.map +1 -0
- package/dist/cli/provider-conformance.cjs +6180 -0
- package/dist/cli/provider-conformance.cjs.map +1 -0
- package/dist/cli/provider-conformance.d.cts +1 -0
- package/dist/cli/provider-conformance.d.ts +1 -0
- package/dist/cli/provider-conformance.js +29 -0
- package/dist/cli/provider-conformance.js.map +1 -0
- package/dist/cli/provider-dossier-check.cjs +2978 -0
- package/dist/cli/provider-dossier-check.cjs.map +1 -0
- package/dist/cli/provider-dossier-check.d.cts +1 -0
- package/dist/cli/provider-dossier-check.d.ts +1 -0
- package/dist/cli/provider-dossier-check.js +27 -0
- package/dist/cli/provider-dossier-check.js.map +1 -0
- package/dist/cli/provider-dossier.cjs +1753 -0
- package/dist/cli/provider-dossier.cjs.map +1 -0
- package/dist/cli/provider-dossier.d.cts +1 -0
- package/dist/cli/provider-dossier.d.ts +1 -0
- package/dist/cli/provider-dossier.js +26 -0
- package/dist/cli/provider-dossier.js.map +1 -0
- package/dist/cli/x402-accept.cjs +6009 -0
- package/dist/cli/x402-accept.cjs.map +1 -0
- package/dist/cli/x402-accept.d.cts +1 -0
- package/dist/cli/x402-accept.d.ts +1 -0
- package/dist/cli/x402-accept.js +28 -0
- package/dist/cli/x402-accept.js.map +1 -0
- package/dist/index.cjs +13671 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2026 -0
- package/dist/index.d.ts +2026 -0
- package/dist/index.js +2560 -0
- package/dist/index.js.map +1 -0
- package/package.json +111 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
import {
|
|
2
|
+
redactX402Secrets
|
|
3
|
+
} from "./chunk-NYTV263W.js";
|
|
4
|
+
import {
|
|
5
|
+
ProbeMeshError,
|
|
6
|
+
isProbeMeshError
|
|
7
|
+
} from "./chunk-2ASMVLG4.js";
|
|
8
|
+
|
|
9
|
+
// src/callAuditArtifact.ts
|
|
10
|
+
import { createHash } from "crypto";
|
|
11
|
+
var PROBEMESH_CALL_AUDIT_ARTIFACT_SCHEMA_VERSION = "probemesh.call-audit.v1";
|
|
12
|
+
function createProbeMeshCallAuditArtifact(options) {
|
|
13
|
+
assertAuditArtifactOptions(options);
|
|
14
|
+
const requestHash = hashStableJson(options.request);
|
|
15
|
+
const status = options.response ? "completed" : "failed";
|
|
16
|
+
const callId = options.response?.callId ?? options.error?.callId;
|
|
17
|
+
const capability = options.response?.capability ?? options.error?.capability ?? options.request.capability;
|
|
18
|
+
const receiptRefs = cloneSerializable(
|
|
19
|
+
options.response?.receiptRefs ?? options.error?.receiptRefs ?? []
|
|
20
|
+
);
|
|
21
|
+
const safety = cloneSerializable(
|
|
22
|
+
options.response?.safety ?? options.error?.safety
|
|
23
|
+
);
|
|
24
|
+
const retrySafety = cloneSerializable(
|
|
25
|
+
options.retrySafety ?? options.response?.retrySafety ?? options.error?.retrySafety
|
|
26
|
+
);
|
|
27
|
+
const attempt = cloneSerializable(
|
|
28
|
+
options.attempt ?? options.response?.attempt ?? options.error?.attempt
|
|
29
|
+
);
|
|
30
|
+
const route = cloneSerializable(
|
|
31
|
+
options.route ?? options.response?.route ?? options.error?.route
|
|
32
|
+
);
|
|
33
|
+
const timeline = cloneSerializable(
|
|
34
|
+
options.timeline ?? options.response?.timeline ?? options.error?.timeline
|
|
35
|
+
);
|
|
36
|
+
const timelineSummary = summarizeTimeline(timeline);
|
|
37
|
+
const baseContent = sanitizeAuditOutput(
|
|
38
|
+
{
|
|
39
|
+
schemaVersion: PROBEMESH_CALL_AUDIT_ARTIFACT_SCHEMA_VERSION,
|
|
40
|
+
artifactId: options.artifactId ?? `call_audit_${callId ?? requestHash.slice(0, 16)}`,
|
|
41
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
42
|
+
status,
|
|
43
|
+
hashAlgorithm: "sha256",
|
|
44
|
+
call: {
|
|
45
|
+
callId,
|
|
46
|
+
capability,
|
|
47
|
+
status,
|
|
48
|
+
providerId: options.response?.provider.id ?? options.error?.provider,
|
|
49
|
+
errorCode: options.error?.code,
|
|
50
|
+
cost: cloneSerializable(options.response?.cost),
|
|
51
|
+
latencyMs: options.response?.latencyMs,
|
|
52
|
+
attempt
|
|
53
|
+
},
|
|
54
|
+
request: {
|
|
55
|
+
requestHash,
|
|
56
|
+
idempotencyKey: options.request.idempotencyKey
|
|
57
|
+
},
|
|
58
|
+
evidence: {
|
|
59
|
+
receiptSummary: summarizeAuditReceipts(receiptRefs),
|
|
60
|
+
receiptRefs,
|
|
61
|
+
safety,
|
|
62
|
+
retrySafety,
|
|
63
|
+
route,
|
|
64
|
+
timeline: timelineSummary
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
options.forbiddenOutputValues ?? []
|
|
68
|
+
);
|
|
69
|
+
const hashesWithoutArtifact = {
|
|
70
|
+
request: requestHash,
|
|
71
|
+
route: hashStableJson(baseContent.evidence.route ?? null),
|
|
72
|
+
timeline: hashStableJson(baseContent.evidence.timeline),
|
|
73
|
+
receipts: hashStableJson(baseContent.evidence.receiptRefs),
|
|
74
|
+
safety: hashStableJson(baseContent.evidence.safety ?? null),
|
|
75
|
+
retrySafety: hashStableJson(baseContent.evidence.retrySafety ?? null),
|
|
76
|
+
artifact: ""
|
|
77
|
+
};
|
|
78
|
+
const artifact = {
|
|
79
|
+
...baseContent,
|
|
80
|
+
hashes: {
|
|
81
|
+
...hashesWithoutArtifact,
|
|
82
|
+
artifact: hashArtifactContent({
|
|
83
|
+
...baseContent,
|
|
84
|
+
hashes: hashesWithoutArtifact
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
assertProbeMeshCallAuditArtifact(artifact);
|
|
89
|
+
return artifact;
|
|
90
|
+
}
|
|
91
|
+
function validateProbeMeshCallAuditArtifact(artifact) {
|
|
92
|
+
const errors = [];
|
|
93
|
+
if (!isRecord(artifact)) {
|
|
94
|
+
return {
|
|
95
|
+
valid: false,
|
|
96
|
+
errors: [
|
|
97
|
+
{
|
|
98
|
+
path: "$",
|
|
99
|
+
message: "ProbeMesh call audit artifact must be an object."
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (artifact.schemaVersion !== PROBEMESH_CALL_AUDIT_ARTIFACT_SCHEMA_VERSION) {
|
|
105
|
+
errors.push({
|
|
106
|
+
path: "schemaVersion",
|
|
107
|
+
message: `schemaVersion must be "${PROBEMESH_CALL_AUDIT_ARTIFACT_SCHEMA_VERSION}".`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
validateNonEmptyString(artifact.artifactId, "artifactId", errors);
|
|
111
|
+
validateIsoTimestamp(artifact.generatedAt, "generatedAt", errors);
|
|
112
|
+
if (artifact.status !== "completed" && artifact.status !== "failed") {
|
|
113
|
+
errors.push({
|
|
114
|
+
path: "status",
|
|
115
|
+
message: 'status must be "completed" or "failed".'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (artifact.hashAlgorithm !== "sha256") {
|
|
119
|
+
errors.push({
|
|
120
|
+
path: "hashAlgorithm",
|
|
121
|
+
message: 'hashAlgorithm must be "sha256".'
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
validateHashes(artifact.hashes, errors);
|
|
125
|
+
validateCallSummary(artifact.call, artifact.status, errors);
|
|
126
|
+
validateRequestProof(artifact.request, artifact.hashes, errors);
|
|
127
|
+
validateEvidence(artifact.evidence, errors);
|
|
128
|
+
if (errors.length === 0) {
|
|
129
|
+
const typedArtifact = artifact;
|
|
130
|
+
pushHashMismatch(
|
|
131
|
+
errors,
|
|
132
|
+
"hashes.route",
|
|
133
|
+
typedArtifact.hashes.route,
|
|
134
|
+
hashStableJson(typedArtifact.evidence.route ?? null)
|
|
135
|
+
);
|
|
136
|
+
pushHashMismatch(
|
|
137
|
+
errors,
|
|
138
|
+
"hashes.timeline",
|
|
139
|
+
typedArtifact.hashes.timeline,
|
|
140
|
+
hashStableJson(typedArtifact.evidence.timeline)
|
|
141
|
+
);
|
|
142
|
+
pushHashMismatch(
|
|
143
|
+
errors,
|
|
144
|
+
"hashes.receipts",
|
|
145
|
+
typedArtifact.hashes.receipts,
|
|
146
|
+
hashStableJson(typedArtifact.evidence.receiptRefs)
|
|
147
|
+
);
|
|
148
|
+
pushHashMismatch(
|
|
149
|
+
errors,
|
|
150
|
+
"hashes.safety",
|
|
151
|
+
typedArtifact.hashes.safety,
|
|
152
|
+
hashStableJson(typedArtifact.evidence.safety ?? null)
|
|
153
|
+
);
|
|
154
|
+
pushHashMismatch(
|
|
155
|
+
errors,
|
|
156
|
+
"hashes.retrySafety",
|
|
157
|
+
typedArtifact.hashes.retrySafety,
|
|
158
|
+
hashStableJson(typedArtifact.evidence.retrySafety ?? null)
|
|
159
|
+
);
|
|
160
|
+
pushHashMismatch(
|
|
161
|
+
errors,
|
|
162
|
+
"hashes.artifact",
|
|
163
|
+
typedArtifact.hashes.artifact,
|
|
164
|
+
hashArtifactContent(typedArtifact)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
valid: errors.length === 0,
|
|
169
|
+
errors
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function assertProbeMeshCallAuditArtifact(artifact) {
|
|
173
|
+
const result = validateProbeMeshCallAuditArtifact(artifact);
|
|
174
|
+
if (result.valid) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
throw new ProbeMeshError({
|
|
178
|
+
code: "invalid_request",
|
|
179
|
+
message: `Invalid ProbeMesh call audit artifact: ${formatValidationErrors(
|
|
180
|
+
result.errors
|
|
181
|
+
)}`
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function formatProbeMeshCallAuditArtifact(artifact, options = {}) {
|
|
185
|
+
assertProbeMeshCallAuditArtifact(artifact);
|
|
186
|
+
if ((options.format ?? "json") === "markdown") {
|
|
187
|
+
return formatAuditArtifactMarkdown(artifact);
|
|
188
|
+
}
|
|
189
|
+
return JSON.stringify(artifact, null, 2);
|
|
190
|
+
}
|
|
191
|
+
function assertAuditArtifactOptions(options) {
|
|
192
|
+
if (!isRecord(options)) {
|
|
193
|
+
throw new ProbeMeshError({
|
|
194
|
+
code: "invalid_request",
|
|
195
|
+
message: "ProbeMesh call audit artifact options must be an object."
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (!isRecord(options.request)) {
|
|
199
|
+
throw new ProbeMeshError({
|
|
200
|
+
code: "invalid_request",
|
|
201
|
+
message: "ProbeMesh call audit artifact request must be an object."
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (typeof options.request.capability !== "string" || options.request.capability.length === 0) {
|
|
205
|
+
throw new ProbeMeshError({
|
|
206
|
+
code: "invalid_request",
|
|
207
|
+
message: "ProbeMesh call audit artifact request capability is required."
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (!("input" in options.request)) {
|
|
211
|
+
throw new ProbeMeshError({
|
|
212
|
+
code: "invalid_request",
|
|
213
|
+
message: "ProbeMesh call audit artifact request input is required for hashing."
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (options.response === void 0 && options.error === void 0 || options.response !== void 0 && options.error !== void 0) {
|
|
217
|
+
throw new ProbeMeshError({
|
|
218
|
+
code: "invalid_request",
|
|
219
|
+
message: "ProbeMesh call audit artifact requires exactly one response or error."
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (options.error !== void 0 && !isProbeMeshError(options.error)) {
|
|
223
|
+
throw new ProbeMeshError({
|
|
224
|
+
code: "invalid_request",
|
|
225
|
+
message: "ProbeMesh call audit artifact error must be a ProbeMeshError."
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (options.generatedAt !== void 0 && (typeof options.generatedAt !== "string" || Number.isNaN(Date.parse(options.generatedAt)))) {
|
|
229
|
+
throw new ProbeMeshError({
|
|
230
|
+
code: "invalid_request",
|
|
231
|
+
message: "ProbeMesh call audit artifact generatedAt must be an ISO timestamp."
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (options.artifactId !== void 0 && (typeof options.artifactId !== "string" || options.artifactId.length === 0)) {
|
|
235
|
+
throw new ProbeMeshError({
|
|
236
|
+
code: "invalid_request",
|
|
237
|
+
message: "ProbeMesh call audit artifact artifactId must be a non-empty string."
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if (options.forbiddenOutputValues !== void 0 && (!Array.isArray(options.forbiddenOutputValues) || options.forbiddenOutputValues.some((value) => typeof value !== "string"))) {
|
|
241
|
+
throw new ProbeMeshError({
|
|
242
|
+
code: "invalid_request",
|
|
243
|
+
message: "ProbeMesh call audit artifact forbiddenOutputValues must be an array of strings."
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function summarizeAuditReceipts(receiptRefs) {
|
|
248
|
+
const types = [];
|
|
249
|
+
const providers = [];
|
|
250
|
+
const byType = {};
|
|
251
|
+
for (const receiptRef of receiptRefs) {
|
|
252
|
+
if (!types.includes(receiptRef.type)) {
|
|
253
|
+
types.push(receiptRef.type);
|
|
254
|
+
}
|
|
255
|
+
if (!providers.includes(receiptRef.provider)) {
|
|
256
|
+
providers.push(receiptRef.provider);
|
|
257
|
+
}
|
|
258
|
+
byType[receiptRef.type] = (byType[receiptRef.type] ?? 0) + 1;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
total: receiptRefs.length,
|
|
262
|
+
types,
|
|
263
|
+
providers,
|
|
264
|
+
byType,
|
|
265
|
+
hasPaymentProof: receiptRefs.some(
|
|
266
|
+
(receiptRef) => receiptRef.type === "payment_proof"
|
|
267
|
+
),
|
|
268
|
+
hasSettlementConfirmation: receiptRefs.some(
|
|
269
|
+
(receiptRef) => receiptRef.type === "settlement_confirmation"
|
|
270
|
+
),
|
|
271
|
+
hasProviderDelivery: receiptRefs.some(
|
|
272
|
+
(receiptRef) => receiptRef.type === "provider_delivery"
|
|
273
|
+
)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function summarizeTimeline(timeline) {
|
|
277
|
+
if (!timeline) {
|
|
278
|
+
return {
|
|
279
|
+
eventTypes: [],
|
|
280
|
+
events: []
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
routeId: timeline.routeId,
|
|
285
|
+
eventTypes: timeline.events.map((event) => event.type),
|
|
286
|
+
events: timeline.events.map((event) => ({
|
|
287
|
+
type: event.type,
|
|
288
|
+
timestamp: event.timestamp,
|
|
289
|
+
provider: event.provider,
|
|
290
|
+
status: event.status,
|
|
291
|
+
errorCode: event.errorCode,
|
|
292
|
+
receiptType: event.receiptRef?.type,
|
|
293
|
+
attestationBundleId: event.attestation?.bundleId
|
|
294
|
+
}))
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function validateHashes(hashes, errors) {
|
|
298
|
+
if (!isRecord(hashes)) {
|
|
299
|
+
errors.push({
|
|
300
|
+
path: "hashes",
|
|
301
|
+
message: "hashes must be an object."
|
|
302
|
+
});
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
for (const key of [
|
|
306
|
+
"request",
|
|
307
|
+
"route",
|
|
308
|
+
"timeline",
|
|
309
|
+
"receipts",
|
|
310
|
+
"safety",
|
|
311
|
+
"retrySafety",
|
|
312
|
+
"artifact"
|
|
313
|
+
]) {
|
|
314
|
+
validateSha256String(hashes[key], `hashes.${key}`, errors);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function validateCallSummary(call, status, errors) {
|
|
318
|
+
if (!isRecord(call)) {
|
|
319
|
+
errors.push({
|
|
320
|
+
path: "call",
|
|
321
|
+
message: "call must be an object."
|
|
322
|
+
});
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
validateNonEmptyString(call.capability, "call.capability", errors);
|
|
326
|
+
if (call.status !== status) {
|
|
327
|
+
errors.push({
|
|
328
|
+
path: "call.status",
|
|
329
|
+
message: "call.status must match artifact status."
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
if (call.callId !== void 0) {
|
|
333
|
+
validateNonEmptyString(call.callId, "call.callId", errors);
|
|
334
|
+
}
|
|
335
|
+
if (call.providerId !== void 0) {
|
|
336
|
+
validateNonEmptyString(call.providerId, "call.providerId", errors);
|
|
337
|
+
}
|
|
338
|
+
if (call.errorCode !== void 0 && (typeof call.errorCode !== "string" || call.errorCode.length === 0)) {
|
|
339
|
+
errors.push({
|
|
340
|
+
path: "call.errorCode",
|
|
341
|
+
message: "call.errorCode must be a non-empty string when provided."
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
if (call.latencyMs !== void 0 && (typeof call.latencyMs !== "number" || !Number.isFinite(call.latencyMs) || call.latencyMs < 0)) {
|
|
345
|
+
errors.push({
|
|
346
|
+
path: "call.latencyMs",
|
|
347
|
+
message: "call.latencyMs must be a non-negative finite number."
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function validateRequestProof(request, hashes, errors) {
|
|
352
|
+
if (!isRecord(request)) {
|
|
353
|
+
errors.push({
|
|
354
|
+
path: "request",
|
|
355
|
+
message: "request must be an object."
|
|
356
|
+
});
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
validateSha256String(request.requestHash, "request.requestHash", errors);
|
|
360
|
+
if (isRecord(hashes) && hashes.request !== request.requestHash) {
|
|
361
|
+
errors.push({
|
|
362
|
+
path: "hashes.request",
|
|
363
|
+
message: "hashes.request must equal request.requestHash."
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (request.idempotencyKey !== void 0) {
|
|
367
|
+
validateNonEmptyString(
|
|
368
|
+
request.idempotencyKey,
|
|
369
|
+
"request.idempotencyKey",
|
|
370
|
+
errors
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function validateEvidence(evidence, errors) {
|
|
375
|
+
if (!isRecord(evidence)) {
|
|
376
|
+
errors.push({
|
|
377
|
+
path: "evidence",
|
|
378
|
+
message: "evidence must be an object."
|
|
379
|
+
});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
validateReceiptSummary(evidence.receiptSummary, errors);
|
|
383
|
+
validateReceiptRefs(evidence.receiptRefs, errors);
|
|
384
|
+
validateTimelineSummary(evidence.timeline, errors);
|
|
385
|
+
}
|
|
386
|
+
function validateReceiptSummary(summary, errors) {
|
|
387
|
+
if (!isRecord(summary)) {
|
|
388
|
+
errors.push({
|
|
389
|
+
path: "evidence.receiptSummary",
|
|
390
|
+
message: "evidence.receiptSummary must be an object."
|
|
391
|
+
});
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (typeof summary.total !== "number" || !Number.isFinite(summary.total)) {
|
|
395
|
+
errors.push({
|
|
396
|
+
path: "evidence.receiptSummary.total",
|
|
397
|
+
message: "evidence.receiptSummary.total must be a number."
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
validateStringArray(summary.types, "evidence.receiptSummary.types", errors);
|
|
401
|
+
validateStringArray(
|
|
402
|
+
summary.providers,
|
|
403
|
+
"evidence.receiptSummary.providers",
|
|
404
|
+
errors
|
|
405
|
+
);
|
|
406
|
+
if (!isRecord(summary.byType)) {
|
|
407
|
+
errors.push({
|
|
408
|
+
path: "evidence.receiptSummary.byType",
|
|
409
|
+
message: "evidence.receiptSummary.byType must be an object."
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
for (const key of [
|
|
413
|
+
"hasPaymentProof",
|
|
414
|
+
"hasSettlementConfirmation",
|
|
415
|
+
"hasProviderDelivery"
|
|
416
|
+
]) {
|
|
417
|
+
if (typeof summary[key] !== "boolean") {
|
|
418
|
+
errors.push({
|
|
419
|
+
path: `evidence.receiptSummary.${key}`,
|
|
420
|
+
message: `evidence.receiptSummary.${key} must be a boolean.`
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function validateReceiptRefs(receiptRefs, errors) {
|
|
426
|
+
if (!Array.isArray(receiptRefs)) {
|
|
427
|
+
errors.push({
|
|
428
|
+
path: "evidence.receiptRefs",
|
|
429
|
+
message: "evidence.receiptRefs must be an array."
|
|
430
|
+
});
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
receiptRefs.forEach((receiptRef, index) => {
|
|
434
|
+
if (!isRecord(receiptRef)) {
|
|
435
|
+
errors.push({
|
|
436
|
+
path: `evidence.receiptRefs.${index}`,
|
|
437
|
+
message: "receipt ref must be an object."
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
for (const key of ["type", "provider", "status", "ref", "timestamp"]) {
|
|
442
|
+
validateNonEmptyString(
|
|
443
|
+
receiptRef[key],
|
|
444
|
+
`evidence.receiptRefs.${index}.${key}`,
|
|
445
|
+
errors
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
function validateTimelineSummary(timeline, errors) {
|
|
451
|
+
if (!isRecord(timeline)) {
|
|
452
|
+
errors.push({
|
|
453
|
+
path: "evidence.timeline",
|
|
454
|
+
message: "evidence.timeline must be an object."
|
|
455
|
+
});
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (timeline.routeId !== void 0) {
|
|
459
|
+
validateNonEmptyString(timeline.routeId, "evidence.timeline.routeId", errors);
|
|
460
|
+
}
|
|
461
|
+
validateStringArray(
|
|
462
|
+
timeline.eventTypes,
|
|
463
|
+
"evidence.timeline.eventTypes",
|
|
464
|
+
errors
|
|
465
|
+
);
|
|
466
|
+
if (!Array.isArray(timeline.events)) {
|
|
467
|
+
errors.push({
|
|
468
|
+
path: "evidence.timeline.events",
|
|
469
|
+
message: "evidence.timeline.events must be an array."
|
|
470
|
+
});
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
timeline.events.forEach((event, index) => {
|
|
474
|
+
if (!isRecord(event)) {
|
|
475
|
+
errors.push({
|
|
476
|
+
path: `evidence.timeline.events.${index}`,
|
|
477
|
+
message: "timeline event must be an object."
|
|
478
|
+
});
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
validateNonEmptyString(
|
|
482
|
+
event.type,
|
|
483
|
+
`evidence.timeline.events.${index}.type`,
|
|
484
|
+
errors
|
|
485
|
+
);
|
|
486
|
+
validateIsoTimestamp(
|
|
487
|
+
event.timestamp,
|
|
488
|
+
`evidence.timeline.events.${index}.timestamp`,
|
|
489
|
+
errors
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
function hashArtifactContent(artifact) {
|
|
494
|
+
return hashStableJson({
|
|
495
|
+
...artifact,
|
|
496
|
+
hashes: {
|
|
497
|
+
...artifact.hashes,
|
|
498
|
+
artifact: ""
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function pushHashMismatch(errors, path, expected, actual) {
|
|
503
|
+
if (expected === actual) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
errors.push({
|
|
507
|
+
path,
|
|
508
|
+
message: `${path} does not match artifact content.`
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
function hashStableJson(value) {
|
|
512
|
+
return createHash("sha256").update(stableStringify(value)).digest("hex");
|
|
513
|
+
}
|
|
514
|
+
function stableStringify(value) {
|
|
515
|
+
return JSON.stringify(sortSerializable(value));
|
|
516
|
+
}
|
|
517
|
+
function sortSerializable(value) {
|
|
518
|
+
if (Array.isArray(value)) {
|
|
519
|
+
return value.map(sortSerializable);
|
|
520
|
+
}
|
|
521
|
+
if (!isRecord(value)) {
|
|
522
|
+
return value;
|
|
523
|
+
}
|
|
524
|
+
return Object.fromEntries(
|
|
525
|
+
Object.keys(value).sort().filter((key) => value[key] !== void 0).map((key) => [key, sortSerializable(value[key])])
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
function sanitizeAuditOutput(value, forbiddenOutputValues) {
|
|
529
|
+
const forbiddenValues = forbiddenOutputValues.filter((entry) => entry.length > 0);
|
|
530
|
+
return redactX402Secrets(
|
|
531
|
+
replaceForbiddenValues(cloneSerializable(value), forbiddenValues)
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
function replaceForbiddenValues(value, forbiddenValues) {
|
|
535
|
+
if (typeof value === "string") {
|
|
536
|
+
return forbiddenValues.reduce(
|
|
537
|
+
(current, forbidden) => current.split(forbidden).join("[REDACTED]"),
|
|
538
|
+
value
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if (value === null || typeof value !== "object" || value instanceof Date) {
|
|
542
|
+
return value;
|
|
543
|
+
}
|
|
544
|
+
if (Array.isArray(value)) {
|
|
545
|
+
return value.map((entry) => replaceForbiddenValues(entry, forbiddenValues));
|
|
546
|
+
}
|
|
547
|
+
return Object.fromEntries(
|
|
548
|
+
Object.entries(value).map(([key, entry]) => [
|
|
549
|
+
key,
|
|
550
|
+
replaceForbiddenValues(entry, forbiddenValues)
|
|
551
|
+
])
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
function cloneSerializable(value) {
|
|
555
|
+
if (value === void 0) {
|
|
556
|
+
return value;
|
|
557
|
+
}
|
|
558
|
+
return JSON.parse(JSON.stringify(value));
|
|
559
|
+
}
|
|
560
|
+
function formatAuditArtifactMarkdown(artifact) {
|
|
561
|
+
const attestation = artifact.evidence.route?.attestation;
|
|
562
|
+
return [
|
|
563
|
+
`# ProbeMesh Call Audit Artifact`,
|
|
564
|
+
``,
|
|
565
|
+
`Status: ${artifact.status}`,
|
|
566
|
+
`Schema: ${artifact.schemaVersion}`,
|
|
567
|
+
`Artifact ID: ${artifact.artifactId}`,
|
|
568
|
+
`Generated: ${artifact.generatedAt}`,
|
|
569
|
+
``,
|
|
570
|
+
`## Call`,
|
|
571
|
+
`- Call ID: ${artifact.call.callId ?? "none"}`,
|
|
572
|
+
`- Capability: ${artifact.call.capability}`,
|
|
573
|
+
`- Provider: ${artifact.call.providerId ?? "none"}`,
|
|
574
|
+
`- Error code: ${artifact.call.errorCode ?? "none"}`,
|
|
575
|
+
`- Cost USD: ${artifact.call.cost?.amountUsd ?? "none"}`,
|
|
576
|
+
`- Request hash: ${artifact.request.requestHash}`,
|
|
577
|
+
``,
|
|
578
|
+
`## Evidence`,
|
|
579
|
+
`- Receipts: ${artifact.evidence.receiptSummary.total}`,
|
|
580
|
+
`- Receipt types: ${formatInlineList(artifact.evidence.receiptSummary.types)}`,
|
|
581
|
+
`- Payment proof: ${String(artifact.evidence.receiptSummary.hasPaymentProof)}`,
|
|
582
|
+
`- Settlement confirmation: ${String(
|
|
583
|
+
artifact.evidence.receiptSummary.hasSettlementConfirmation
|
|
584
|
+
)}`,
|
|
585
|
+
`- Provider delivery: ${String(
|
|
586
|
+
artifact.evidence.receiptSummary.hasProviderDelivery
|
|
587
|
+
)}`,
|
|
588
|
+
`- Money may have moved: ${String(
|
|
589
|
+
artifact.evidence.safety?.moneyMayHaveMoved ?? false
|
|
590
|
+
)}`,
|
|
591
|
+
`- Retry safe: ${String(
|
|
592
|
+
artifact.evidence.retrySafety?.safeToAutoRetry ?? false
|
|
593
|
+
)}`,
|
|
594
|
+
`- Route stop: ${artifact.evidence.route?.explanation?.stopReason ?? "none"}`,
|
|
595
|
+
`- Bundle: ${attestation?.bundleId ?? "none"}`,
|
|
596
|
+
`- Bundle verified: ${String(attestation?.runtimeVerified ?? false)}`,
|
|
597
|
+
`- Timeline events: ${formatInlineList(
|
|
598
|
+
artifact.evidence.timeline.eventTypes
|
|
599
|
+
)}`,
|
|
600
|
+
``,
|
|
601
|
+
`## Hashes`,
|
|
602
|
+
`- Artifact: ${artifact.hashes.artifact}`,
|
|
603
|
+
`- Route: ${artifact.hashes.route}`,
|
|
604
|
+
`- Timeline: ${artifact.hashes.timeline}`,
|
|
605
|
+
`- Receipts: ${artifact.hashes.receipts}`
|
|
606
|
+
].join("\n");
|
|
607
|
+
}
|
|
608
|
+
function formatInlineList(values) {
|
|
609
|
+
return values.length === 0 ? "none" : values.join(", ");
|
|
610
|
+
}
|
|
611
|
+
function validateSha256String(value, path, errors) {
|
|
612
|
+
if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) {
|
|
613
|
+
errors.push({
|
|
614
|
+
path,
|
|
615
|
+
message: `${path} must be a sha256 hex string.`
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function validateNonEmptyString(value, path, errors) {
|
|
620
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
621
|
+
errors.push({
|
|
622
|
+
path,
|
|
623
|
+
message: `${path} must be a non-empty string.`
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function validateIsoTimestamp(value, path, errors) {
|
|
628
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
|
|
629
|
+
errors.push({
|
|
630
|
+
path,
|
|
631
|
+
message: `${path} must be an ISO timestamp.`
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function validateStringArray(value, path, errors) {
|
|
636
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0)) {
|
|
637
|
+
errors.push({
|
|
638
|
+
path,
|
|
639
|
+
message: `${path} must be an array of non-empty strings.`
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function isRecord(value) {
|
|
644
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
645
|
+
}
|
|
646
|
+
function formatValidationErrors(errors) {
|
|
647
|
+
return errors.map((error) => `${error.path}: ${error.message}`).join("; ");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export {
|
|
651
|
+
PROBEMESH_CALL_AUDIT_ARTIFACT_SCHEMA_VERSION,
|
|
652
|
+
createProbeMeshCallAuditArtifact,
|
|
653
|
+
validateProbeMeshCallAuditArtifact,
|
|
654
|
+
assertProbeMeshCallAuditArtifact,
|
|
655
|
+
formatProbeMeshCallAuditArtifact
|
|
656
|
+
};
|
|
657
|
+
//# sourceMappingURL=chunk-5Q3PDYIA.js.map
|