@wytness/sdk 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/index.cjs +37 -10
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +36 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,9 +30,11 @@ await sendEmail("team@company.com", "Report", "Weekly summary");
|
|
|
30
30
|
|
|
31
31
|
## Features
|
|
32
32
|
|
|
33
|
+
- **Response capture** — AI agent responses are automatically captured, truncated to 5K chars, and PII-redacted
|
|
34
|
+
- **PII redaction** — emails, SSN, TFN, credit cards, and phone numbers are automatically redacted from responses and parameter values
|
|
33
35
|
- **Cryptographic signing** — every event is signed with Ed25519 (tweetnacl)
|
|
34
36
|
- **Hash chaining** — tamper-evident chain of events per agent session
|
|
35
|
-
- **Automatic redaction** — secrets in
|
|
37
|
+
- **Automatic secret redaction** — secrets in parameter names are automatically redacted
|
|
36
38
|
- **HTTP transport** — stream events to api.wytness.dev over HTTPS
|
|
37
39
|
- **File fallback** — events are saved locally if the API is unreachable
|
|
38
40
|
|
|
@@ -66,7 +68,7 @@ const client = new AuditClient({
|
|
|
66
68
|
|
|
67
69
|
### `auditTool(client, fn, options)`
|
|
68
70
|
|
|
69
|
-
Wraps a function to automatically log audit events. Captures function name, parameters, execution duration, success/failure status, cryptographic signature, and hash chain link.
|
|
71
|
+
Wraps a function to automatically log audit events. Captures function name, parameters, **response text** (truncated to 5K chars, PII-redacted), execution duration, success/failure status, cryptographic signature, and hash chain link.
|
|
70
72
|
|
|
71
73
|
### `client.sessionId`
|
|
72
74
|
|
|
@@ -114,6 +116,7 @@ import {
|
|
|
114
116
|
AuditClientOptions,
|
|
115
117
|
auditTool,
|
|
116
118
|
hashValue,
|
|
119
|
+
redactPii,
|
|
117
120
|
AuditEvent,
|
|
118
121
|
AuditEventSchema,
|
|
119
122
|
generateKeypair,
|
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(src_exports, {
|
|
|
38
38
|
createHttpEmitter: () => createHttpEmitter,
|
|
39
39
|
generateKeypair: () => generateKeypair,
|
|
40
40
|
hashValue: () => hashValue,
|
|
41
|
+
redactPii: () => redactPii,
|
|
41
42
|
signEvent: () => signEvent,
|
|
42
43
|
verifyChain: () => verifyChain,
|
|
43
44
|
verifyEvent: () => verifyEvent
|
|
@@ -72,6 +73,7 @@ var AuditEventSchema = import_zod.z.object({
|
|
|
72
73
|
inputs_source: import_zod.z.string().default(""),
|
|
73
74
|
outputs_hash: import_zod.z.string().default(""),
|
|
74
75
|
outputs_destination: import_zod.z.string().default(""),
|
|
76
|
+
response: import_zod.z.string().default(""),
|
|
75
77
|
// Layer 5: Outcome
|
|
76
78
|
status: import_zod.z.string().default("success"),
|
|
77
79
|
error_code: import_zod.z.string().nullable().default(null),
|
|
@@ -299,21 +301,42 @@ var AuditClient = class {
|
|
|
299
301
|
// src/decorator.ts
|
|
300
302
|
var import_crypto4 = require("crypto");
|
|
301
303
|
var SECRET_PATTERN = /key|secret|token|password|passwd|pwd|credential|auth/i;
|
|
304
|
+
var PII_PATTERNS = [
|
|
305
|
+
[/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, "[EMAIL_REDACTED]"],
|
|
306
|
+
[/\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g, "[SSN_REDACTED]"],
|
|
307
|
+
[/\b\d{3}[-.\s]?\d{3}[-.\s]?\d{3}\b/g, "[TFN_REDACTED]"],
|
|
308
|
+
[/\b(?:\d{4}[-\s]?){3}\d{4}\b/g, "[CARD_REDACTED]"],
|
|
309
|
+
[/\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g, "[PHONE_REDACTED]"]
|
|
310
|
+
];
|
|
311
|
+
var RESPONSE_MAX_CHARS = 5e3;
|
|
312
|
+
function redactPii(text) {
|
|
313
|
+
for (const [pattern, replacement] of PII_PATTERNS) {
|
|
314
|
+
pattern.lastIndex = 0;
|
|
315
|
+
text = text.replace(pattern, replacement);
|
|
316
|
+
}
|
|
317
|
+
return text;
|
|
318
|
+
}
|
|
302
319
|
function sanitise(params) {
|
|
303
320
|
const result = {};
|
|
304
321
|
for (const [k, v] of Object.entries(params)) {
|
|
305
|
-
|
|
322
|
+
if (SECRET_PATTERN.test(k)) {
|
|
323
|
+
result[k] = "[REDACTED]";
|
|
324
|
+
} else {
|
|
325
|
+
result[k] = redactPii(String(v).slice(0, 500));
|
|
326
|
+
}
|
|
306
327
|
}
|
|
307
328
|
return result;
|
|
308
329
|
}
|
|
309
330
|
function hashValue(value) {
|
|
310
331
|
return (0, import_crypto4.createHash)("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
|
|
311
332
|
}
|
|
312
|
-
function recordEvent(client, toolName, taskId, args, start, status, errorCode, result) {
|
|
333
|
+
function recordEvent(client, toolName, taskId, prompt, args, start, status, errorCode, result) {
|
|
313
334
|
const params = {};
|
|
314
335
|
args.forEach((arg, i) => {
|
|
315
336
|
params[`arg${i}`] = arg;
|
|
316
337
|
});
|
|
338
|
+
const outputsHash = result != null ? hashValue(result) : "";
|
|
339
|
+
const response = result != null ? redactPii(String(result).slice(0, RESPONSE_MAX_CHARS)) : "";
|
|
317
340
|
const event = AuditEventSchema.parse({
|
|
318
341
|
agent_id: client.agentId,
|
|
319
342
|
agent_version: client.agentVersion,
|
|
@@ -322,34 +345,36 @@ function recordEvent(client, toolName, taskId, args, start, status, errorCode, r
|
|
|
322
345
|
session_id: client.sessionId,
|
|
323
346
|
tool_name: toolName,
|
|
324
347
|
tool_parameters: sanitise(params),
|
|
348
|
+
prompt,
|
|
325
349
|
inputs_hash: hashValue(args),
|
|
326
|
-
outputs_hash:
|
|
350
|
+
outputs_hash: outputsHash,
|
|
351
|
+
response,
|
|
327
352
|
status,
|
|
328
353
|
error_code: errorCode,
|
|
329
354
|
duration_ms: Math.round(performance.now() - start)
|
|
330
355
|
});
|
|
331
356
|
client.record(event);
|
|
332
357
|
}
|
|
333
|
-
function wrapFn(client, fn, toolName, taskId) {
|
|
358
|
+
function wrapFn(client, fn, toolName, taskId, prompt) {
|
|
334
359
|
const wrapped = function(...args) {
|
|
335
360
|
const start = performance.now();
|
|
336
361
|
let result;
|
|
337
362
|
try {
|
|
338
363
|
result = fn.apply(this, args);
|
|
339
364
|
} catch (e) {
|
|
340
|
-
recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
|
|
365
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "failure", e.constructor.name, null);
|
|
341
366
|
throw e;
|
|
342
367
|
}
|
|
343
368
|
if (result && typeof result.then === "function") {
|
|
344
369
|
return result.then((resolved) => {
|
|
345
|
-
recordEvent(client, toolName, taskId, args, start, "success", null, resolved);
|
|
370
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "success", null, resolved);
|
|
346
371
|
return resolved;
|
|
347
372
|
}).catch((e) => {
|
|
348
|
-
recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
|
|
373
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "failure", e.constructor.name, null);
|
|
349
374
|
throw e;
|
|
350
375
|
});
|
|
351
376
|
}
|
|
352
|
-
recordEvent(client, toolName, taskId, args, start, "success", null, result);
|
|
377
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "success", null, result);
|
|
353
378
|
return result;
|
|
354
379
|
};
|
|
355
380
|
Object.defineProperty(wrapped, "name", { value: toolName });
|
|
@@ -361,12 +386,13 @@ function auditTool(client, fnOrTaskId, options) {
|
|
|
361
386
|
const opts = typeof options === "object" ? options : {};
|
|
362
387
|
const toolName = opts.toolName ?? fn.name ?? "anonymous";
|
|
363
388
|
const taskId2 = opts.taskId ?? "default";
|
|
364
|
-
|
|
389
|
+
const prompt = opts.prompt ?? "";
|
|
390
|
+
return wrapFn(client, fn, toolName, taskId2, prompt);
|
|
365
391
|
}
|
|
366
392
|
const taskId = typeof fnOrTaskId === "string" ? fnOrTaskId : "default";
|
|
367
393
|
return function(fn, fnName) {
|
|
368
394
|
const toolName = fnName ?? fn.name ?? "anonymous";
|
|
369
|
-
return wrapFn(client, fn, toolName, taskId);
|
|
395
|
+
return wrapFn(client, fn, toolName, taskId, "");
|
|
370
396
|
};
|
|
371
397
|
}
|
|
372
398
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -379,6 +405,7 @@ function auditTool(client, fnOrTaskId, options) {
|
|
|
379
405
|
createHttpEmitter,
|
|
380
406
|
generateKeypair,
|
|
381
407
|
hashValue,
|
|
408
|
+
redactPii,
|
|
382
409
|
signEvent,
|
|
383
410
|
verifyChain,
|
|
384
411
|
verifyEvent
|
package/dist/index.d.cts
CHANGED
|
@@ -21,6 +21,7 @@ declare const AuditEventSchema: z.ZodObject<{
|
|
|
21
21
|
inputs_source: z.ZodDefault<z.ZodString>;
|
|
22
22
|
outputs_hash: z.ZodDefault<z.ZodString>;
|
|
23
23
|
outputs_destination: z.ZodDefault<z.ZodString>;
|
|
24
|
+
response: z.ZodDefault<z.ZodString>;
|
|
24
25
|
status: z.ZodDefault<z.ZodString>;
|
|
25
26
|
error_code: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
26
27
|
duration_ms: z.ZodDefault<z.ZodNumber>;
|
|
@@ -49,6 +50,7 @@ declare const AuditEventSchema: z.ZodObject<{
|
|
|
49
50
|
inputs_source: string;
|
|
50
51
|
outputs_hash: string;
|
|
51
52
|
outputs_destination: string;
|
|
53
|
+
response: string;
|
|
52
54
|
error_code: string | null;
|
|
53
55
|
duration_ms: number;
|
|
54
56
|
retry_count: number;
|
|
@@ -76,6 +78,7 @@ declare const AuditEventSchema: z.ZodObject<{
|
|
|
76
78
|
inputs_source?: string | undefined;
|
|
77
79
|
outputs_hash?: string | undefined;
|
|
78
80
|
outputs_destination?: string | undefined;
|
|
81
|
+
response?: string | undefined;
|
|
79
82
|
error_code?: string | null | undefined;
|
|
80
83
|
duration_ms?: number | undefined;
|
|
81
84
|
retry_count?: number | undefined;
|
|
@@ -133,10 +136,12 @@ declare class AuditClient {
|
|
|
133
136
|
flush(): Promise<void>;
|
|
134
137
|
}
|
|
135
138
|
|
|
139
|
+
declare function redactPii(text: string): string;
|
|
136
140
|
declare function hashValue(value: unknown): string;
|
|
137
141
|
interface AuditToolOptions {
|
|
138
142
|
toolName?: string;
|
|
139
143
|
taskId?: string;
|
|
144
|
+
prompt?: string;
|
|
140
145
|
}
|
|
141
146
|
/**
|
|
142
147
|
* Wrap a function with audit logging. Works with both sync and async functions.
|
|
@@ -152,4 +157,4 @@ interface AuditToolOptions {
|
|
|
152
157
|
*/
|
|
153
158
|
declare function auditTool(client: AuditClient, fnOrTaskId?: ((...args: unknown[]) => unknown) | string, options?: AuditToolOptions | string): any;
|
|
154
159
|
|
|
155
|
-
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, hashValue, signEvent, verifyChain, verifyEvent };
|
|
160
|
+
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, hashValue, redactPii, signEvent, verifyChain, verifyEvent };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ declare const AuditEventSchema: z.ZodObject<{
|
|
|
21
21
|
inputs_source: z.ZodDefault<z.ZodString>;
|
|
22
22
|
outputs_hash: z.ZodDefault<z.ZodString>;
|
|
23
23
|
outputs_destination: z.ZodDefault<z.ZodString>;
|
|
24
|
+
response: z.ZodDefault<z.ZodString>;
|
|
24
25
|
status: z.ZodDefault<z.ZodString>;
|
|
25
26
|
error_code: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
26
27
|
duration_ms: z.ZodDefault<z.ZodNumber>;
|
|
@@ -49,6 +50,7 @@ declare const AuditEventSchema: z.ZodObject<{
|
|
|
49
50
|
inputs_source: string;
|
|
50
51
|
outputs_hash: string;
|
|
51
52
|
outputs_destination: string;
|
|
53
|
+
response: string;
|
|
52
54
|
error_code: string | null;
|
|
53
55
|
duration_ms: number;
|
|
54
56
|
retry_count: number;
|
|
@@ -76,6 +78,7 @@ declare const AuditEventSchema: z.ZodObject<{
|
|
|
76
78
|
inputs_source?: string | undefined;
|
|
77
79
|
outputs_hash?: string | undefined;
|
|
78
80
|
outputs_destination?: string | undefined;
|
|
81
|
+
response?: string | undefined;
|
|
79
82
|
error_code?: string | null | undefined;
|
|
80
83
|
duration_ms?: number | undefined;
|
|
81
84
|
retry_count?: number | undefined;
|
|
@@ -133,10 +136,12 @@ declare class AuditClient {
|
|
|
133
136
|
flush(): Promise<void>;
|
|
134
137
|
}
|
|
135
138
|
|
|
139
|
+
declare function redactPii(text: string): string;
|
|
136
140
|
declare function hashValue(value: unknown): string;
|
|
137
141
|
interface AuditToolOptions {
|
|
138
142
|
toolName?: string;
|
|
139
143
|
taskId?: string;
|
|
144
|
+
prompt?: string;
|
|
140
145
|
}
|
|
141
146
|
/**
|
|
142
147
|
* Wrap a function with audit logging. Works with both sync and async functions.
|
|
@@ -152,4 +157,4 @@ interface AuditToolOptions {
|
|
|
152
157
|
*/
|
|
153
158
|
declare function auditTool(client: AuditClient, fnOrTaskId?: ((...args: unknown[]) => unknown) | string, options?: AuditToolOptions | string): any;
|
|
154
159
|
|
|
155
|
-
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, hashValue, signEvent, verifyChain, verifyEvent };
|
|
160
|
+
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, hashValue, redactPii, signEvent, verifyChain, verifyEvent };
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ var AuditEventSchema = z.object({
|
|
|
26
26
|
inputs_source: z.string().default(""),
|
|
27
27
|
outputs_hash: z.string().default(""),
|
|
28
28
|
outputs_destination: z.string().default(""),
|
|
29
|
+
response: z.string().default(""),
|
|
29
30
|
// Layer 5: Outcome
|
|
30
31
|
status: z.string().default("success"),
|
|
31
32
|
error_code: z.string().nullable().default(null),
|
|
@@ -253,21 +254,42 @@ var AuditClient = class {
|
|
|
253
254
|
// src/decorator.ts
|
|
254
255
|
import { createHash as createHash2 } from "crypto";
|
|
255
256
|
var SECRET_PATTERN = /key|secret|token|password|passwd|pwd|credential|auth/i;
|
|
257
|
+
var PII_PATTERNS = [
|
|
258
|
+
[/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, "[EMAIL_REDACTED]"],
|
|
259
|
+
[/\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g, "[SSN_REDACTED]"],
|
|
260
|
+
[/\b\d{3}[-.\s]?\d{3}[-.\s]?\d{3}\b/g, "[TFN_REDACTED]"],
|
|
261
|
+
[/\b(?:\d{4}[-\s]?){3}\d{4}\b/g, "[CARD_REDACTED]"],
|
|
262
|
+
[/\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g, "[PHONE_REDACTED]"]
|
|
263
|
+
];
|
|
264
|
+
var RESPONSE_MAX_CHARS = 5e3;
|
|
265
|
+
function redactPii(text) {
|
|
266
|
+
for (const [pattern, replacement] of PII_PATTERNS) {
|
|
267
|
+
pattern.lastIndex = 0;
|
|
268
|
+
text = text.replace(pattern, replacement);
|
|
269
|
+
}
|
|
270
|
+
return text;
|
|
271
|
+
}
|
|
256
272
|
function sanitise(params) {
|
|
257
273
|
const result = {};
|
|
258
274
|
for (const [k, v] of Object.entries(params)) {
|
|
259
|
-
|
|
275
|
+
if (SECRET_PATTERN.test(k)) {
|
|
276
|
+
result[k] = "[REDACTED]";
|
|
277
|
+
} else {
|
|
278
|
+
result[k] = redactPii(String(v).slice(0, 500));
|
|
279
|
+
}
|
|
260
280
|
}
|
|
261
281
|
return result;
|
|
262
282
|
}
|
|
263
283
|
function hashValue(value) {
|
|
264
284
|
return createHash2("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
|
|
265
285
|
}
|
|
266
|
-
function recordEvent(client, toolName, taskId, args, start, status, errorCode, result) {
|
|
286
|
+
function recordEvent(client, toolName, taskId, prompt, args, start, status, errorCode, result) {
|
|
267
287
|
const params = {};
|
|
268
288
|
args.forEach((arg, i) => {
|
|
269
289
|
params[`arg${i}`] = arg;
|
|
270
290
|
});
|
|
291
|
+
const outputsHash = result != null ? hashValue(result) : "";
|
|
292
|
+
const response = result != null ? redactPii(String(result).slice(0, RESPONSE_MAX_CHARS)) : "";
|
|
271
293
|
const event = AuditEventSchema.parse({
|
|
272
294
|
agent_id: client.agentId,
|
|
273
295
|
agent_version: client.agentVersion,
|
|
@@ -276,34 +298,36 @@ function recordEvent(client, toolName, taskId, args, start, status, errorCode, r
|
|
|
276
298
|
session_id: client.sessionId,
|
|
277
299
|
tool_name: toolName,
|
|
278
300
|
tool_parameters: sanitise(params),
|
|
301
|
+
prompt,
|
|
279
302
|
inputs_hash: hashValue(args),
|
|
280
|
-
outputs_hash:
|
|
303
|
+
outputs_hash: outputsHash,
|
|
304
|
+
response,
|
|
281
305
|
status,
|
|
282
306
|
error_code: errorCode,
|
|
283
307
|
duration_ms: Math.round(performance.now() - start)
|
|
284
308
|
});
|
|
285
309
|
client.record(event);
|
|
286
310
|
}
|
|
287
|
-
function wrapFn(client, fn, toolName, taskId) {
|
|
311
|
+
function wrapFn(client, fn, toolName, taskId, prompt) {
|
|
288
312
|
const wrapped = function(...args) {
|
|
289
313
|
const start = performance.now();
|
|
290
314
|
let result;
|
|
291
315
|
try {
|
|
292
316
|
result = fn.apply(this, args);
|
|
293
317
|
} catch (e) {
|
|
294
|
-
recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
|
|
318
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "failure", e.constructor.name, null);
|
|
295
319
|
throw e;
|
|
296
320
|
}
|
|
297
321
|
if (result && typeof result.then === "function") {
|
|
298
322
|
return result.then((resolved) => {
|
|
299
|
-
recordEvent(client, toolName, taskId, args, start, "success", null, resolved);
|
|
323
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "success", null, resolved);
|
|
300
324
|
return resolved;
|
|
301
325
|
}).catch((e) => {
|
|
302
|
-
recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
|
|
326
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "failure", e.constructor.name, null);
|
|
303
327
|
throw e;
|
|
304
328
|
});
|
|
305
329
|
}
|
|
306
|
-
recordEvent(client, toolName, taskId, args, start, "success", null, result);
|
|
330
|
+
recordEvent(client, toolName, taskId, prompt, args, start, "success", null, result);
|
|
307
331
|
return result;
|
|
308
332
|
};
|
|
309
333
|
Object.defineProperty(wrapped, "name", { value: toolName });
|
|
@@ -315,12 +339,13 @@ function auditTool(client, fnOrTaskId, options) {
|
|
|
315
339
|
const opts = typeof options === "object" ? options : {};
|
|
316
340
|
const toolName = opts.toolName ?? fn.name ?? "anonymous";
|
|
317
341
|
const taskId2 = opts.taskId ?? "default";
|
|
318
|
-
|
|
342
|
+
const prompt = opts.prompt ?? "";
|
|
343
|
+
return wrapFn(client, fn, toolName, taskId2, prompt);
|
|
319
344
|
}
|
|
320
345
|
const taskId = typeof fnOrTaskId === "string" ? fnOrTaskId : "default";
|
|
321
346
|
return function(fn, fnName) {
|
|
322
347
|
const toolName = fnName ?? fn.name ?? "anonymous";
|
|
323
|
-
return wrapFn(client, fn, toolName, taskId);
|
|
348
|
+
return wrapFn(client, fn, toolName, taskId, "");
|
|
324
349
|
};
|
|
325
350
|
}
|
|
326
351
|
export {
|
|
@@ -332,6 +357,7 @@ export {
|
|
|
332
357
|
createHttpEmitter,
|
|
333
358
|
generateKeypair,
|
|
334
359
|
hashValue,
|
|
360
|
+
redactPii,
|
|
335
361
|
signEvent,
|
|
336
362
|
verifyChain,
|
|
337
363
|
verifyEvent
|
package/package.json
CHANGED