@wytness/sdk 0.1.0 → 0.2.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 +1 -1
- package/dist/index.cjs +92 -84
- package/dist/index.d.cts +22 -5
- package/dist/index.d.ts +22 -5
- package/dist/index.js +92 -93
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ Send events directly to the Wytness API using your API key:
|
|
|
44
44
|
const client = new AuditClient({
|
|
45
45
|
agentId: "my-agent",
|
|
46
46
|
humanOperatorId: "user-123",
|
|
47
|
-
httpEndpoint: "https://api.wytness.
|
|
47
|
+
httpEndpoint: "https://api.wytness.dev",
|
|
48
48
|
httpApiKey: "aa_live_...",
|
|
49
49
|
});
|
|
50
50
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,8 @@ __export(src_exports, {
|
|
|
34
34
|
AuditEventSchema: () => AuditEventSchema,
|
|
35
35
|
auditTool: () => auditTool,
|
|
36
36
|
computeEventHash: () => computeEventHash,
|
|
37
|
-
|
|
37
|
+
createFileEmitter: () => createFileEmitter,
|
|
38
|
+
createHttpEmitter: () => createHttpEmitter,
|
|
38
39
|
generateKeypair: () => generateKeypair,
|
|
39
40
|
signEvent: () => signEvent,
|
|
40
41
|
verifyChain: () => verifyChain,
|
|
@@ -142,43 +143,33 @@ function fileEmit(path, eventDict) {
|
|
|
142
143
|
(0, import_fs.mkdirSync)((0, import_path.dirname)(path), { recursive: true });
|
|
143
144
|
(0, import_fs.appendFileSync)(path, JSON.stringify(eventDict) + "\n");
|
|
144
145
|
}
|
|
145
|
-
function
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
console.error(`Kafka error: ${e}`);
|
|
173
|
-
fileEmit(fallbackPath, eventDict);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
doSend().catch(() => fileEmit(fallbackPath, eventDict));
|
|
177
|
-
};
|
|
178
|
-
} catch (e) {
|
|
179
|
-
console.warn(`Kafka unavailable (${e}), using file fallback`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
146
|
+
function createHttpEmitter(apiUrl, apiKey, fallbackPath) {
|
|
147
|
+
return (eventDict) => {
|
|
148
|
+
const doSend = async () => {
|
|
149
|
+
try {
|
|
150
|
+
const body = JSON.stringify(eventDict);
|
|
151
|
+
const resp = await fetch(`${apiUrl}/ingest`, {
|
|
152
|
+
method: "POST",
|
|
153
|
+
headers: {
|
|
154
|
+
"Content-Type": "application/json",
|
|
155
|
+
"X-API-Key": apiKey
|
|
156
|
+
},
|
|
157
|
+
body,
|
|
158
|
+
signal: AbortSignal.timeout(1e4)
|
|
159
|
+
});
|
|
160
|
+
if (resp.status !== 201) {
|
|
161
|
+
console.error(`HTTP ingest failed: ${resp.status}`);
|
|
162
|
+
fileEmit(fallbackPath, eventDict);
|
|
163
|
+
}
|
|
164
|
+
} catch (e) {
|
|
165
|
+
console.error(`HTTP ingest error: ${e}`);
|
|
166
|
+
fileEmit(fallbackPath, eventDict);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
doSend().catch(() => fileEmit(fallbackPath, eventDict));
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function createFileEmitter(fallbackPath) {
|
|
182
173
|
return (eventDict) => {
|
|
183
174
|
fileEmit(fallbackPath, eventDict);
|
|
184
175
|
};
|
|
@@ -211,11 +202,16 @@ var AuditClient = class {
|
|
|
211
202
|
(0, import_fs2.mkdirSync)((0, import_path2.dirname)(keyPath), { recursive: true });
|
|
212
203
|
(0, import_fs2.writeFileSync)(keyPath, Buffer.from(kp.secretKey));
|
|
213
204
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
205
|
+
const fallback = options.fallbackLogPath ?? "./audit_fallback.jsonl";
|
|
206
|
+
if (options.httpApiKey) {
|
|
207
|
+
this._emit = createHttpEmitter(
|
|
208
|
+
options.httpEndpoint ?? "https://api.wytness.dev",
|
|
209
|
+
options.httpApiKey,
|
|
210
|
+
fallback
|
|
211
|
+
);
|
|
212
|
+
} else {
|
|
213
|
+
this._emit = createFileEmitter(fallback);
|
|
214
|
+
}
|
|
219
215
|
}
|
|
220
216
|
get sessionId() {
|
|
221
217
|
return this._sessionId;
|
|
@@ -242,47 +238,58 @@ function sanitise(params) {
|
|
|
242
238
|
function hashValue(value) {
|
|
243
239
|
return (0, import_crypto4.createHash)("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
|
|
244
240
|
}
|
|
245
|
-
function
|
|
241
|
+
function wrapFn(client, fn, toolName, taskId) {
|
|
242
|
+
const wrapped = function(...args) {
|
|
243
|
+
const start = performance.now();
|
|
244
|
+
let status = "success";
|
|
245
|
+
let errorCode = null;
|
|
246
|
+
let result;
|
|
247
|
+
const inputsHash = hashValue(args);
|
|
248
|
+
try {
|
|
249
|
+
result = fn.apply(this, args);
|
|
250
|
+
} catch (e) {
|
|
251
|
+
status = "failure";
|
|
252
|
+
errorCode = e.constructor.name;
|
|
253
|
+
throw e;
|
|
254
|
+
} finally {
|
|
255
|
+
const durationMs = Math.round(performance.now() - start);
|
|
256
|
+
const params = {};
|
|
257
|
+
args.forEach((arg, i) => {
|
|
258
|
+
params[`arg${i}`] = arg;
|
|
259
|
+
});
|
|
260
|
+
const event = AuditEventSchema.parse({
|
|
261
|
+
agent_id: client.agentId,
|
|
262
|
+
agent_version: client.agentVersion,
|
|
263
|
+
human_operator_id: client.humanOperatorId,
|
|
264
|
+
task_id: taskId,
|
|
265
|
+
session_id: client.sessionId,
|
|
266
|
+
tool_name: toolName,
|
|
267
|
+
tool_parameters: sanitise(params),
|
|
268
|
+
inputs_hash: inputsHash,
|
|
269
|
+
outputs_hash: result != null ? hashValue(result) : "",
|
|
270
|
+
status,
|
|
271
|
+
error_code: errorCode,
|
|
272
|
+
duration_ms: durationMs
|
|
273
|
+
});
|
|
274
|
+
client.record(event);
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
};
|
|
278
|
+
Object.defineProperty(wrapped, "name", { value: toolName });
|
|
279
|
+
return wrapped;
|
|
280
|
+
}
|
|
281
|
+
function auditTool(client, fnOrTaskId, options) {
|
|
282
|
+
if (typeof fnOrTaskId === "function") {
|
|
283
|
+
const fn = fnOrTaskId;
|
|
284
|
+
const opts = typeof options === "object" ? options : {};
|
|
285
|
+
const toolName = opts.toolName ?? fn.name ?? "anonymous";
|
|
286
|
+
const taskId2 = opts.taskId ?? "default";
|
|
287
|
+
return wrapFn(client, fn, toolName, taskId2);
|
|
288
|
+
}
|
|
289
|
+
const taskId = typeof fnOrTaskId === "string" ? fnOrTaskId : "default";
|
|
246
290
|
return function(fn, fnName) {
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
const start = performance.now();
|
|
250
|
-
let status = "success";
|
|
251
|
-
let errorCode = null;
|
|
252
|
-
let result;
|
|
253
|
-
const inputsHash = hashValue(args);
|
|
254
|
-
try {
|
|
255
|
-
result = fn.apply(this, args);
|
|
256
|
-
} catch (e) {
|
|
257
|
-
status = "failure";
|
|
258
|
-
errorCode = e.constructor.name;
|
|
259
|
-
throw e;
|
|
260
|
-
} finally {
|
|
261
|
-
const durationMs = Math.round(performance.now() - start);
|
|
262
|
-
const params = {};
|
|
263
|
-
args.forEach((arg, i) => {
|
|
264
|
-
params[`arg${i}`] = arg;
|
|
265
|
-
});
|
|
266
|
-
const event = AuditEventSchema.parse({
|
|
267
|
-
agent_id: client.agentId,
|
|
268
|
-
agent_version: client.agentVersion,
|
|
269
|
-
human_operator_id: client.humanOperatorId,
|
|
270
|
-
task_id: taskId,
|
|
271
|
-
session_id: client.sessionId,
|
|
272
|
-
tool_name: name,
|
|
273
|
-
tool_parameters: sanitise(params),
|
|
274
|
-
inputs_hash: inputsHash,
|
|
275
|
-
outputs_hash: result != null ? hashValue(result) : "",
|
|
276
|
-
status,
|
|
277
|
-
error_code: errorCode,
|
|
278
|
-
duration_ms: durationMs
|
|
279
|
-
});
|
|
280
|
-
client.record(event);
|
|
281
|
-
}
|
|
282
|
-
return result;
|
|
283
|
-
};
|
|
284
|
-
Object.defineProperty(wrapped, "name", { value: name });
|
|
285
|
-
return wrapped;
|
|
291
|
+
const toolName = fnName ?? fn.name ?? "anonymous";
|
|
292
|
+
return wrapFn(client, fn, toolName, taskId);
|
|
286
293
|
};
|
|
287
294
|
}
|
|
288
295
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -291,7 +298,8 @@ function auditTool(client, taskId = "default") {
|
|
|
291
298
|
AuditEventSchema,
|
|
292
299
|
auditTool,
|
|
293
300
|
computeEventHash,
|
|
294
|
-
|
|
301
|
+
createFileEmitter,
|
|
302
|
+
createHttpEmitter,
|
|
295
303
|
generateKeypair,
|
|
296
304
|
signEvent,
|
|
297
305
|
verifyChain,
|
package/dist/index.d.cts
CHANGED
|
@@ -97,16 +97,17 @@ declare function verifyChain(events: Record<string, unknown>[]): {
|
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
type EmitFn = (eventDict: Record<string, unknown>) => void;
|
|
100
|
-
declare function
|
|
100
|
+
declare function createHttpEmitter(apiUrl: string, apiKey: string, fallbackPath: string): EmitFn;
|
|
101
|
+
declare function createFileEmitter(fallbackPath: string): EmitFn;
|
|
101
102
|
|
|
102
103
|
interface AuditClientOptions {
|
|
103
104
|
agentId: string;
|
|
104
105
|
agentVersion?: string;
|
|
105
106
|
humanOperatorId?: string;
|
|
106
107
|
signingKeyPath?: string;
|
|
107
|
-
kafkaBootstrapServers?: string | null;
|
|
108
|
-
kafkaTopic?: string;
|
|
109
108
|
fallbackLogPath?: string;
|
|
109
|
+
httpApiKey?: string;
|
|
110
|
+
httpEndpoint?: string;
|
|
110
111
|
}
|
|
111
112
|
declare class AuditClient {
|
|
112
113
|
readonly agentId: string;
|
|
@@ -121,6 +122,22 @@ declare class AuditClient {
|
|
|
121
122
|
record(event: AuditEvent): void;
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
interface AuditToolOptions {
|
|
126
|
+
toolName?: string;
|
|
127
|
+
taskId?: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Wrap a function with audit logging.
|
|
131
|
+
*
|
|
132
|
+
* Supports two calling styles:
|
|
133
|
+
*
|
|
134
|
+
* 1. Direct (documented API):
|
|
135
|
+
* auditTool(client, fn, { toolName: "name", taskId: "task" })
|
|
136
|
+
*
|
|
137
|
+
* 2. Curried (legacy):
|
|
138
|
+
* auditTool(client)(fn, "name")
|
|
139
|
+
* auditTool(client, "taskId")(fn, "name")
|
|
140
|
+
*/
|
|
141
|
+
declare function auditTool(client: AuditClient, fnOrTaskId?: ((...args: unknown[]) => unknown) | string, options?: AuditToolOptions | string): any;
|
|
125
142
|
|
|
126
|
-
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash,
|
|
143
|
+
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
|
package/dist/index.d.ts
CHANGED
|
@@ -97,16 +97,17 @@ declare function verifyChain(events: Record<string, unknown>[]): {
|
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
type EmitFn = (eventDict: Record<string, unknown>) => void;
|
|
100
|
-
declare function
|
|
100
|
+
declare function createHttpEmitter(apiUrl: string, apiKey: string, fallbackPath: string): EmitFn;
|
|
101
|
+
declare function createFileEmitter(fallbackPath: string): EmitFn;
|
|
101
102
|
|
|
102
103
|
interface AuditClientOptions {
|
|
103
104
|
agentId: string;
|
|
104
105
|
agentVersion?: string;
|
|
105
106
|
humanOperatorId?: string;
|
|
106
107
|
signingKeyPath?: string;
|
|
107
|
-
kafkaBootstrapServers?: string | null;
|
|
108
|
-
kafkaTopic?: string;
|
|
109
108
|
fallbackLogPath?: string;
|
|
109
|
+
httpApiKey?: string;
|
|
110
|
+
httpEndpoint?: string;
|
|
110
111
|
}
|
|
111
112
|
declare class AuditClient {
|
|
112
113
|
readonly agentId: string;
|
|
@@ -121,6 +122,22 @@ declare class AuditClient {
|
|
|
121
122
|
record(event: AuditEvent): void;
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
interface AuditToolOptions {
|
|
126
|
+
toolName?: string;
|
|
127
|
+
taskId?: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Wrap a function with audit logging.
|
|
131
|
+
*
|
|
132
|
+
* Supports two calling styles:
|
|
133
|
+
*
|
|
134
|
+
* 1. Direct (documented API):
|
|
135
|
+
* auditTool(client, fn, { toolName: "name", taskId: "task" })
|
|
136
|
+
*
|
|
137
|
+
* 2. Curried (legacy):
|
|
138
|
+
* auditTool(client)(fn, "name")
|
|
139
|
+
* auditTool(client, "taskId")(fn, "name")
|
|
140
|
+
*/
|
|
141
|
+
declare function auditTool(client: AuditClient, fnOrTaskId?: ((...args: unknown[]) => unknown) | string, options?: AuditToolOptions | string): any;
|
|
125
142
|
|
|
126
|
-
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash,
|
|
143
|
+
export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined")
|
|
5
|
-
return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
|
-
|
|
9
1
|
// src/models.ts
|
|
10
2
|
import { z } from "zod";
|
|
11
3
|
import { randomUUID } from "crypto";
|
|
@@ -106,43 +98,33 @@ function fileEmit(path, eventDict) {
|
|
|
106
98
|
mkdirSync(dirname(path), { recursive: true });
|
|
107
99
|
appendFileSync(path, JSON.stringify(eventDict) + "\n");
|
|
108
100
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
console.error(`Kafka error: ${e}`);
|
|
137
|
-
fileEmit(fallbackPath, eventDict);
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
doSend().catch(() => fileEmit(fallbackPath, eventDict));
|
|
141
|
-
};
|
|
142
|
-
} catch (e) {
|
|
143
|
-
console.warn(`Kafka unavailable (${e}), using file fallback`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
101
|
+
function createHttpEmitter(apiUrl, apiKey, fallbackPath) {
|
|
102
|
+
return (eventDict) => {
|
|
103
|
+
const doSend = async () => {
|
|
104
|
+
try {
|
|
105
|
+
const body = JSON.stringify(eventDict);
|
|
106
|
+
const resp = await fetch(`${apiUrl}/ingest`, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
"X-API-Key": apiKey
|
|
111
|
+
},
|
|
112
|
+
body,
|
|
113
|
+
signal: AbortSignal.timeout(1e4)
|
|
114
|
+
});
|
|
115
|
+
if (resp.status !== 201) {
|
|
116
|
+
console.error(`HTTP ingest failed: ${resp.status}`);
|
|
117
|
+
fileEmit(fallbackPath, eventDict);
|
|
118
|
+
}
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(`HTTP ingest error: ${e}`);
|
|
121
|
+
fileEmit(fallbackPath, eventDict);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
doSend().catch(() => fileEmit(fallbackPath, eventDict));
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function createFileEmitter(fallbackPath) {
|
|
146
128
|
return (eventDict) => {
|
|
147
129
|
fileEmit(fallbackPath, eventDict);
|
|
148
130
|
};
|
|
@@ -150,7 +132,7 @@ function createEmitter(bootstrapServers, topic, fallbackPath) {
|
|
|
150
132
|
|
|
151
133
|
// src/client.ts
|
|
152
134
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
153
|
-
import { existsSync, readFileSync, writeFileSync
|
|
135
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
154
136
|
import { dirname as dirname2 } from "path";
|
|
155
137
|
var AuditClient = class {
|
|
156
138
|
agentId;
|
|
@@ -173,13 +155,18 @@ var AuditClient = class {
|
|
|
173
155
|
const kp = generateKeypair();
|
|
174
156
|
this._secretKey = kp.secretKey;
|
|
175
157
|
mkdirSync2(dirname2(keyPath), { recursive: true });
|
|
176
|
-
|
|
158
|
+
writeFileSync(keyPath, Buffer.from(kp.secretKey));
|
|
159
|
+
}
|
|
160
|
+
const fallback = options.fallbackLogPath ?? "./audit_fallback.jsonl";
|
|
161
|
+
if (options.httpApiKey) {
|
|
162
|
+
this._emit = createHttpEmitter(
|
|
163
|
+
options.httpEndpoint ?? "https://api.wytness.dev",
|
|
164
|
+
options.httpApiKey,
|
|
165
|
+
fallback
|
|
166
|
+
);
|
|
167
|
+
} else {
|
|
168
|
+
this._emit = createFileEmitter(fallback);
|
|
177
169
|
}
|
|
178
|
-
this._emit = createEmitter(
|
|
179
|
-
options.kafkaBootstrapServers ?? null,
|
|
180
|
-
options.kafkaTopic ?? "wytness-events",
|
|
181
|
-
options.fallbackLogPath ?? "./audit_fallback.jsonl"
|
|
182
|
-
);
|
|
183
170
|
}
|
|
184
171
|
get sessionId() {
|
|
185
172
|
return this._sessionId;
|
|
@@ -206,47 +193,58 @@ function sanitise(params) {
|
|
|
206
193
|
function hashValue(value) {
|
|
207
194
|
return createHash2("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
|
|
208
195
|
}
|
|
209
|
-
function
|
|
196
|
+
function wrapFn(client, fn, toolName, taskId) {
|
|
197
|
+
const wrapped = function(...args) {
|
|
198
|
+
const start = performance.now();
|
|
199
|
+
let status = "success";
|
|
200
|
+
let errorCode = null;
|
|
201
|
+
let result;
|
|
202
|
+
const inputsHash = hashValue(args);
|
|
203
|
+
try {
|
|
204
|
+
result = fn.apply(this, args);
|
|
205
|
+
} catch (e) {
|
|
206
|
+
status = "failure";
|
|
207
|
+
errorCode = e.constructor.name;
|
|
208
|
+
throw e;
|
|
209
|
+
} finally {
|
|
210
|
+
const durationMs = Math.round(performance.now() - start);
|
|
211
|
+
const params = {};
|
|
212
|
+
args.forEach((arg, i) => {
|
|
213
|
+
params[`arg${i}`] = arg;
|
|
214
|
+
});
|
|
215
|
+
const event = AuditEventSchema.parse({
|
|
216
|
+
agent_id: client.agentId,
|
|
217
|
+
agent_version: client.agentVersion,
|
|
218
|
+
human_operator_id: client.humanOperatorId,
|
|
219
|
+
task_id: taskId,
|
|
220
|
+
session_id: client.sessionId,
|
|
221
|
+
tool_name: toolName,
|
|
222
|
+
tool_parameters: sanitise(params),
|
|
223
|
+
inputs_hash: inputsHash,
|
|
224
|
+
outputs_hash: result != null ? hashValue(result) : "",
|
|
225
|
+
status,
|
|
226
|
+
error_code: errorCode,
|
|
227
|
+
duration_ms: durationMs
|
|
228
|
+
});
|
|
229
|
+
client.record(event);
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
};
|
|
233
|
+
Object.defineProperty(wrapped, "name", { value: toolName });
|
|
234
|
+
return wrapped;
|
|
235
|
+
}
|
|
236
|
+
function auditTool(client, fnOrTaskId, options) {
|
|
237
|
+
if (typeof fnOrTaskId === "function") {
|
|
238
|
+
const fn = fnOrTaskId;
|
|
239
|
+
const opts = typeof options === "object" ? options : {};
|
|
240
|
+
const toolName = opts.toolName ?? fn.name ?? "anonymous";
|
|
241
|
+
const taskId2 = opts.taskId ?? "default";
|
|
242
|
+
return wrapFn(client, fn, toolName, taskId2);
|
|
243
|
+
}
|
|
244
|
+
const taskId = typeof fnOrTaskId === "string" ? fnOrTaskId : "default";
|
|
210
245
|
return function(fn, fnName) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
const start = performance.now();
|
|
214
|
-
let status = "success";
|
|
215
|
-
let errorCode = null;
|
|
216
|
-
let result;
|
|
217
|
-
const inputsHash = hashValue(args);
|
|
218
|
-
try {
|
|
219
|
-
result = fn.apply(this, args);
|
|
220
|
-
} catch (e) {
|
|
221
|
-
status = "failure";
|
|
222
|
-
errorCode = e.constructor.name;
|
|
223
|
-
throw e;
|
|
224
|
-
} finally {
|
|
225
|
-
const durationMs = Math.round(performance.now() - start);
|
|
226
|
-
const params = {};
|
|
227
|
-
args.forEach((arg, i) => {
|
|
228
|
-
params[`arg${i}`] = arg;
|
|
229
|
-
});
|
|
230
|
-
const event = AuditEventSchema.parse({
|
|
231
|
-
agent_id: client.agentId,
|
|
232
|
-
agent_version: client.agentVersion,
|
|
233
|
-
human_operator_id: client.humanOperatorId,
|
|
234
|
-
task_id: taskId,
|
|
235
|
-
session_id: client.sessionId,
|
|
236
|
-
tool_name: name,
|
|
237
|
-
tool_parameters: sanitise(params),
|
|
238
|
-
inputs_hash: inputsHash,
|
|
239
|
-
outputs_hash: result != null ? hashValue(result) : "",
|
|
240
|
-
status,
|
|
241
|
-
error_code: errorCode,
|
|
242
|
-
duration_ms: durationMs
|
|
243
|
-
});
|
|
244
|
-
client.record(event);
|
|
245
|
-
}
|
|
246
|
-
return result;
|
|
247
|
-
};
|
|
248
|
-
Object.defineProperty(wrapped, "name", { value: name });
|
|
249
|
-
return wrapped;
|
|
246
|
+
const toolName = fnName ?? fn.name ?? "anonymous";
|
|
247
|
+
return wrapFn(client, fn, toolName, taskId);
|
|
250
248
|
};
|
|
251
249
|
}
|
|
252
250
|
export {
|
|
@@ -254,7 +252,8 @@ export {
|
|
|
254
252
|
AuditEventSchema,
|
|
255
253
|
auditTool,
|
|
256
254
|
computeEventHash,
|
|
257
|
-
|
|
255
|
+
createFileEmitter,
|
|
256
|
+
createHttpEmitter,
|
|
258
257
|
generateKeypair,
|
|
259
258
|
signEvent,
|
|
260
259
|
verifyChain,
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wytness/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TypeScript SDK for Wytness — audit logging for AI agents with cryptographic signing and chain integrity",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "Wytness <hello@wytness.
|
|
6
|
+
"author": "Wytness <hello@wytness.dev>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/wytness/wytness-sdk",
|
|
10
10
|
"directory": "sdk-typescript"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://wytness.
|
|
12
|
+
"homepage": "https://wytness.dev",
|
|
13
13
|
"keywords": ["audit", "logging", "ai", "agents", "compliance", "security", "typescript"],
|
|
14
14
|
"type": "module",
|
|
15
15
|
"main": "./dist/index.cjs",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"test": "vitest run"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"kafkajs": "2.2.4",
|
|
35
34
|
"tweetnacl": "1.0.3",
|
|
36
35
|
"tweetnacl-util": "0.15.1",
|
|
37
36
|
"zod": "3.23.6"
|