@wytness/sdk 0.1.0 → 0.2.1

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 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.io",
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
- createEmitter: () => createEmitter,
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 createEmitter(bootstrapServers, topic, fallbackPath) {
146
- if (bootstrapServers) {
147
- try {
148
- const { Kafka } = require("kafkajs");
149
- const kafka = new Kafka({
150
- clientId: "wytness-sdk",
151
- brokers: bootstrapServers.split(",")
152
- });
153
- const producer = kafka.producer();
154
- let connected = false;
155
- return (eventDict) => {
156
- const doSend = async () => {
157
- try {
158
- if (!connected) {
159
- await producer.connect();
160
- connected = true;
161
- }
162
- await producer.send({
163
- topic,
164
- messages: [
165
- {
166
- key: eventDict["agent_id"],
167
- value: JSON.stringify(eventDict)
168
- }
169
- ]
170
- });
171
- } catch (e) {
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
  };
@@ -201,31 +192,45 @@ var AuditClient = class {
201
192
  this.agentVersion = options.agentVersion ?? "0.1.0";
202
193
  this.humanOperatorId = options.humanOperatorId ?? "unknown";
203
194
  this._sessionId = (0, import_crypto3.randomUUID)();
204
- const keyPath = options.signingKeyPath ?? "./keys/signing.key";
205
- if ((0, import_fs2.existsSync)(keyPath)) {
206
- const raw = (0, import_fs2.readFileSync)(keyPath);
207
- this._secretKey = new Uint8Array(raw);
195
+ if (options.signingKey) {
196
+ this._secretKey = new Uint8Array(Buffer.from(options.signingKey, "base64"));
208
197
  } else {
209
- const kp = generateKeypair();
210
- this._secretKey = kp.secretKey;
211
- (0, import_fs2.mkdirSync)((0, import_path2.dirname)(keyPath), { recursive: true });
212
- (0, import_fs2.writeFileSync)(keyPath, Buffer.from(kp.secretKey));
198
+ const keyPath = options.signingKeyPath ?? "./keys/signing.key";
199
+ if ((0, import_fs2.existsSync)(keyPath)) {
200
+ const raw = (0, import_fs2.readFileSync)(keyPath);
201
+ this._secretKey = new Uint8Array(raw);
202
+ } else {
203
+ const kp = generateKeypair();
204
+ this._secretKey = kp.secretKey;
205
+ (0, import_fs2.mkdirSync)((0, import_path2.dirname)(keyPath), { recursive: true });
206
+ (0, import_fs2.writeFileSync)(keyPath, Buffer.from(kp.secretKey));
207
+ }
208
+ }
209
+ const fallback = options.fallbackLogPath ?? "./audit_fallback.jsonl";
210
+ if (options.httpApiKey) {
211
+ this._emit = createHttpEmitter(
212
+ options.httpEndpoint ?? "https://api.wytness.dev",
213
+ options.httpApiKey,
214
+ fallback
215
+ );
216
+ } else {
217
+ this._emit = createFileEmitter(fallback);
213
218
  }
214
- this._emit = createEmitter(
215
- options.kafkaBootstrapServers ?? null,
216
- options.kafkaTopic ?? "wytness-events",
217
- options.fallbackLogPath ?? "./audit_fallback.jsonl"
218
- );
219
219
  }
220
220
  get sessionId() {
221
221
  return this._sessionId;
222
222
  }
223
+ /** Record an audit event. Never throws — errors are logged and swallowed. */
223
224
  record(event) {
224
- const d = { ...event };
225
- d["prev_event_hash"] = this._prevEventHash;
226
- d["signature"] = signEvent(d, this._secretKey);
227
- this._prevEventHash = computeEventHash(d);
228
- this._emit(d);
225
+ try {
226
+ const d = { ...event };
227
+ d["prev_event_hash"] = this._prevEventHash;
228
+ d["signature"] = signEvent(d, this._secretKey);
229
+ this._prevEventHash = computeEventHash(d);
230
+ this._emit(d);
231
+ } catch (e) {
232
+ console.error(`wytness: failed to record event: ${e}`);
233
+ }
229
234
  }
230
235
  };
231
236
 
@@ -242,47 +247,64 @@ function sanitise(params) {
242
247
  function hashValue(value) {
243
248
  return (0, import_crypto4.createHash)("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
244
249
  }
245
- function auditTool(client, taskId = "default") {
246
- return function(fn, fnName) {
247
- const name = fnName ?? fn.name ?? "anonymous";
248
- const wrapped = function(...args) {
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;
250
+ function recordEvent(client, toolName, taskId, args, start, status, errorCode, result) {
251
+ const params = {};
252
+ args.forEach((arg, i) => {
253
+ params[`arg${i}`] = arg;
254
+ });
255
+ const event = AuditEventSchema.parse({
256
+ agent_id: client.agentId,
257
+ agent_version: client.agentVersion,
258
+ human_operator_id: client.humanOperatorId,
259
+ task_id: taskId,
260
+ session_id: client.sessionId,
261
+ tool_name: toolName,
262
+ tool_parameters: sanitise(params),
263
+ inputs_hash: hashValue(args),
264
+ outputs_hash: result != null ? hashValue(result) : "",
265
+ status,
266
+ error_code: errorCode,
267
+ duration_ms: Math.round(performance.now() - start)
268
+ });
269
+ client.record(event);
270
+ }
271
+ function wrapFn(client, fn, toolName, taskId) {
272
+ const wrapped = function(...args) {
273
+ const start = performance.now();
274
+ let result;
275
+ try {
276
+ result = fn.apply(this, args);
277
+ } catch (e) {
278
+ recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
279
+ throw e;
280
+ }
281
+ if (result && typeof result.then === "function") {
282
+ return result.then((resolved) => {
283
+ recordEvent(client, toolName, taskId, args, start, "success", null, resolved);
284
+ return resolved;
285
+ }).catch((e) => {
286
+ recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
259
287
  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;
288
+ });
289
+ }
290
+ recordEvent(client, toolName, taskId, args, start, "success", null, result);
291
+ return result;
292
+ };
293
+ Object.defineProperty(wrapped, "name", { value: toolName });
294
+ return wrapped;
295
+ }
296
+ function auditTool(client, fnOrTaskId, options) {
297
+ if (typeof fnOrTaskId === "function") {
298
+ const fn = fnOrTaskId;
299
+ const opts = typeof options === "object" ? options : {};
300
+ const toolName = opts.toolName ?? fn.name ?? "anonymous";
301
+ const taskId2 = opts.taskId ?? "default";
302
+ return wrapFn(client, fn, toolName, taskId2);
303
+ }
304
+ const taskId = typeof fnOrTaskId === "string" ? fnOrTaskId : "default";
305
+ return function(fn, fnName) {
306
+ const toolName = fnName ?? fn.name ?? "anonymous";
307
+ return wrapFn(client, fn, toolName, taskId);
286
308
  };
287
309
  }
288
310
  // Annotate the CommonJS export names for ESM import in node:
@@ -291,7 +313,8 @@ function auditTool(client, taskId = "default") {
291
313
  AuditEventSchema,
292
314
  auditTool,
293
315
  computeEventHash,
294
- createEmitter,
316
+ createFileEmitter,
317
+ createHttpEmitter,
295
318
  generateKeypair,
296
319
  signEvent,
297
320
  verifyChain,
package/dist/index.d.cts CHANGED
@@ -97,16 +97,18 @@ declare function verifyChain(events: Record<string, unknown>[]): {
97
97
  };
98
98
 
99
99
  type EmitFn = (eventDict: Record<string, unknown>) => void;
100
- declare function createEmitter(bootstrapServers: string | null, topic: string, fallbackPath: string): EmitFn;
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;
107
+ signingKey?: string;
106
108
  signingKeyPath?: string;
107
- kafkaBootstrapServers?: string | null;
108
- kafkaTopic?: string;
109
109
  fallbackLogPath?: string;
110
+ httpApiKey?: string;
111
+ httpEndpoint?: string;
110
112
  }
111
113
  declare class AuditClient {
112
114
  readonly agentId: string;
@@ -118,9 +120,26 @@ declare class AuditClient {
118
120
  private _emit;
119
121
  constructor(options: AuditClientOptions);
120
122
  get sessionId(): string;
123
+ /** Record an audit event. Never throws — errors are logged and swallowed. */
121
124
  record(event: AuditEvent): void;
122
125
  }
123
126
 
124
- declare function auditTool(client: AuditClient, taskId?: string): <T extends (...args: unknown[]) => unknown>(fn: T, fnName?: string) => T;
127
+ interface AuditToolOptions {
128
+ toolName?: string;
129
+ taskId?: string;
130
+ }
131
+ /**
132
+ * Wrap a function with audit logging. Works with both sync and async functions.
133
+ *
134
+ * Supports two calling styles:
135
+ *
136
+ * 1. Direct (documented API):
137
+ * auditTool(client, fn, { toolName: "name", taskId: "task" })
138
+ *
139
+ * 2. Curried (legacy):
140
+ * auditTool(client)(fn, "name")
141
+ * auditTool(client, "taskId")(fn, "name")
142
+ */
143
+ declare function auditTool(client: AuditClient, fnOrTaskId?: ((...args: unknown[]) => unknown) | string, options?: AuditToolOptions | string): any;
125
144
 
126
- export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
145
+ export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createFileEmitter, createHttpEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
package/dist/index.d.ts CHANGED
@@ -97,16 +97,18 @@ declare function verifyChain(events: Record<string, unknown>[]): {
97
97
  };
98
98
 
99
99
  type EmitFn = (eventDict: Record<string, unknown>) => void;
100
- declare function createEmitter(bootstrapServers: string | null, topic: string, fallbackPath: string): EmitFn;
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;
107
+ signingKey?: string;
106
108
  signingKeyPath?: string;
107
- kafkaBootstrapServers?: string | null;
108
- kafkaTopic?: string;
109
109
  fallbackLogPath?: string;
110
+ httpApiKey?: string;
111
+ httpEndpoint?: string;
110
112
  }
111
113
  declare class AuditClient {
112
114
  readonly agentId: string;
@@ -118,9 +120,26 @@ declare class AuditClient {
118
120
  private _emit;
119
121
  constructor(options: AuditClientOptions);
120
122
  get sessionId(): string;
123
+ /** Record an audit event. Never throws — errors are logged and swallowed. */
121
124
  record(event: AuditEvent): void;
122
125
  }
123
126
 
124
- declare function auditTool(client: AuditClient, taskId?: string): <T extends (...args: unknown[]) => unknown>(fn: T, fnName?: string) => T;
127
+ interface AuditToolOptions {
128
+ toolName?: string;
129
+ taskId?: string;
130
+ }
131
+ /**
132
+ * Wrap a function with audit logging. Works with both sync and async functions.
133
+ *
134
+ * Supports two calling styles:
135
+ *
136
+ * 1. Direct (documented API):
137
+ * auditTool(client, fn, { toolName: "name", taskId: "task" })
138
+ *
139
+ * 2. Curried (legacy):
140
+ * auditTool(client)(fn, "name")
141
+ * auditTool(client, "taskId")(fn, "name")
142
+ */
143
+ declare function auditTool(client: AuditClient, fnOrTaskId?: ((...args: unknown[]) => unknown) | string, options?: AuditToolOptions | string): any;
125
144
 
126
- export { AuditClient, type AuditClientOptions, type AuditEvent, AuditEventSchema, auditTool, computeEventHash, createEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
145
+ 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 createEmitter(bootstrapServers, topic, fallbackPath) {
110
- if (bootstrapServers) {
111
- try {
112
- const { Kafka } = __require("kafkajs");
113
- const kafka = new Kafka({
114
- clientId: "wytness-sdk",
115
- brokers: bootstrapServers.split(",")
116
- });
117
- const producer = kafka.producer();
118
- let connected = false;
119
- return (eventDict) => {
120
- const doSend = async () => {
121
- try {
122
- if (!connected) {
123
- await producer.connect();
124
- connected = true;
125
- }
126
- await producer.send({
127
- topic,
128
- messages: [
129
- {
130
- key: eventDict["agent_id"],
131
- value: JSON.stringify(eventDict)
132
- }
133
- ]
134
- });
135
- } catch (e) {
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 as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
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;
@@ -165,31 +147,45 @@ var AuditClient = class {
165
147
  this.agentVersion = options.agentVersion ?? "0.1.0";
166
148
  this.humanOperatorId = options.humanOperatorId ?? "unknown";
167
149
  this._sessionId = randomUUID2();
168
- const keyPath = options.signingKeyPath ?? "./keys/signing.key";
169
- if (existsSync(keyPath)) {
170
- const raw = readFileSync(keyPath);
171
- this._secretKey = new Uint8Array(raw);
150
+ if (options.signingKey) {
151
+ this._secretKey = new Uint8Array(Buffer.from(options.signingKey, "base64"));
172
152
  } else {
173
- const kp = generateKeypair();
174
- this._secretKey = kp.secretKey;
175
- mkdirSync2(dirname2(keyPath), { recursive: true });
176
- writeFileSync2(keyPath, Buffer.from(kp.secretKey));
153
+ const keyPath = options.signingKeyPath ?? "./keys/signing.key";
154
+ if (existsSync(keyPath)) {
155
+ const raw = readFileSync(keyPath);
156
+ this._secretKey = new Uint8Array(raw);
157
+ } else {
158
+ const kp = generateKeypair();
159
+ this._secretKey = kp.secretKey;
160
+ mkdirSync2(dirname2(keyPath), { recursive: true });
161
+ writeFileSync(keyPath, Buffer.from(kp.secretKey));
162
+ }
163
+ }
164
+ const fallback = options.fallbackLogPath ?? "./audit_fallback.jsonl";
165
+ if (options.httpApiKey) {
166
+ this._emit = createHttpEmitter(
167
+ options.httpEndpoint ?? "https://api.wytness.dev",
168
+ options.httpApiKey,
169
+ fallback
170
+ );
171
+ } else {
172
+ this._emit = createFileEmitter(fallback);
177
173
  }
178
- this._emit = createEmitter(
179
- options.kafkaBootstrapServers ?? null,
180
- options.kafkaTopic ?? "wytness-events",
181
- options.fallbackLogPath ?? "./audit_fallback.jsonl"
182
- );
183
174
  }
184
175
  get sessionId() {
185
176
  return this._sessionId;
186
177
  }
178
+ /** Record an audit event. Never throws — errors are logged and swallowed. */
187
179
  record(event) {
188
- const d = { ...event };
189
- d["prev_event_hash"] = this._prevEventHash;
190
- d["signature"] = signEvent(d, this._secretKey);
191
- this._prevEventHash = computeEventHash(d);
192
- this._emit(d);
180
+ try {
181
+ const d = { ...event };
182
+ d["prev_event_hash"] = this._prevEventHash;
183
+ d["signature"] = signEvent(d, this._secretKey);
184
+ this._prevEventHash = computeEventHash(d);
185
+ this._emit(d);
186
+ } catch (e) {
187
+ console.error(`wytness: failed to record event: ${e}`);
188
+ }
193
189
  }
194
190
  };
195
191
 
@@ -206,47 +202,64 @@ function sanitise(params) {
206
202
  function hashValue(value) {
207
203
  return createHash2("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
208
204
  }
209
- function auditTool(client, taskId = "default") {
210
- return function(fn, fnName) {
211
- const name = fnName ?? fn.name ?? "anonymous";
212
- const wrapped = function(...args) {
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;
205
+ function recordEvent(client, toolName, taskId, args, start, status, errorCode, result) {
206
+ const params = {};
207
+ args.forEach((arg, i) => {
208
+ params[`arg${i}`] = arg;
209
+ });
210
+ const event = AuditEventSchema.parse({
211
+ agent_id: client.agentId,
212
+ agent_version: client.agentVersion,
213
+ human_operator_id: client.humanOperatorId,
214
+ task_id: taskId,
215
+ session_id: client.sessionId,
216
+ tool_name: toolName,
217
+ tool_parameters: sanitise(params),
218
+ inputs_hash: hashValue(args),
219
+ outputs_hash: result != null ? hashValue(result) : "",
220
+ status,
221
+ error_code: errorCode,
222
+ duration_ms: Math.round(performance.now() - start)
223
+ });
224
+ client.record(event);
225
+ }
226
+ function wrapFn(client, fn, toolName, taskId) {
227
+ const wrapped = function(...args) {
228
+ const start = performance.now();
229
+ let result;
230
+ try {
231
+ result = fn.apply(this, args);
232
+ } catch (e) {
233
+ recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
234
+ throw e;
235
+ }
236
+ if (result && typeof result.then === "function") {
237
+ return result.then((resolved) => {
238
+ recordEvent(client, toolName, taskId, args, start, "success", null, resolved);
239
+ return resolved;
240
+ }).catch((e) => {
241
+ recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
223
242
  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;
243
+ });
244
+ }
245
+ recordEvent(client, toolName, taskId, args, start, "success", null, result);
246
+ return result;
247
+ };
248
+ Object.defineProperty(wrapped, "name", { value: toolName });
249
+ return wrapped;
250
+ }
251
+ function auditTool(client, fnOrTaskId, options) {
252
+ if (typeof fnOrTaskId === "function") {
253
+ const fn = fnOrTaskId;
254
+ const opts = typeof options === "object" ? options : {};
255
+ const toolName = opts.toolName ?? fn.name ?? "anonymous";
256
+ const taskId2 = opts.taskId ?? "default";
257
+ return wrapFn(client, fn, toolName, taskId2);
258
+ }
259
+ const taskId = typeof fnOrTaskId === "string" ? fnOrTaskId : "default";
260
+ return function(fn, fnName) {
261
+ const toolName = fnName ?? fn.name ?? "anonymous";
262
+ return wrapFn(client, fn, toolName, taskId);
250
263
  };
251
264
  }
252
265
  export {
@@ -254,7 +267,8 @@ export {
254
267
  AuditEventSchema,
255
268
  auditTool,
256
269
  computeEventHash,
257
- createEmitter,
270
+ createFileEmitter,
271
+ createHttpEmitter,
258
272
  generateKeypair,
259
273
  signEvent,
260
274
  verifyChain,
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@wytness/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
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.io>",
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.io",
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"