@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 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
  };
@@ -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
- this._emit = createEmitter(
215
- options.kafkaBootstrapServers ?? null,
216
- options.kafkaTopic ?? "wytness-events",
217
- options.fallbackLogPath ?? "./audit_fallback.jsonl"
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 auditTool(client, taskId = "default") {
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 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;
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
- createEmitter,
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 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;
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
- declare function auditTool(client: AuditClient, taskId?: string): <T extends (...args: unknown[]) => unknown>(fn: T, fnName?: string) => T;
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, createEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
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 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;
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
- declare function auditTool(client: AuditClient, taskId?: string): <T extends (...args: unknown[]) => unknown>(fn: T, fnName?: string) => T;
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, createEmitter, generateKeypair, signEvent, verifyChain, verifyEvent };
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 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;
@@ -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
- writeFileSync2(keyPath, Buffer.from(kp.secretKey));
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 auditTool(client, taskId = "default") {
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 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;
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
- createEmitter,
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.1.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.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"