@wytness/sdk 0.2.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/dist/index.cjs +53 -38
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +53 -38
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -192,15 +192,19 @@ var AuditClient = class {
|
|
|
192
192
|
this.agentVersion = options.agentVersion ?? "0.1.0";
|
|
193
193
|
this.humanOperatorId = options.humanOperatorId ?? "unknown";
|
|
194
194
|
this._sessionId = (0, import_crypto3.randomUUID)();
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const raw = (0, import_fs2.readFileSync)(keyPath);
|
|
198
|
-
this._secretKey = new Uint8Array(raw);
|
|
195
|
+
if (options.signingKey) {
|
|
196
|
+
this._secretKey = new Uint8Array(Buffer.from(options.signingKey, "base64"));
|
|
199
197
|
} else {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
}
|
|
204
208
|
}
|
|
205
209
|
const fallback = options.fallbackLogPath ?? "./audit_fallback.jsonl";
|
|
206
210
|
if (options.httpApiKey) {
|
|
@@ -216,12 +220,17 @@ var AuditClient = class {
|
|
|
216
220
|
get sessionId() {
|
|
217
221
|
return this._sessionId;
|
|
218
222
|
}
|
|
223
|
+
/** Record an audit event. Never throws — errors are logged and swallowed. */
|
|
219
224
|
record(event) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
+
}
|
|
225
234
|
}
|
|
226
235
|
};
|
|
227
236
|
|
|
@@ -238,41 +247,47 @@ function sanitise(params) {
|
|
|
238
247
|
function hashValue(value) {
|
|
239
248
|
return (0, import_crypto4.createHash)("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
|
|
240
249
|
}
|
|
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
|
+
}
|
|
241
271
|
function wrapFn(client, fn, toolName, taskId) {
|
|
242
272
|
const wrapped = function(...args) {
|
|
243
273
|
const start = performance.now();
|
|
244
|
-
let status = "success";
|
|
245
|
-
let errorCode = null;
|
|
246
274
|
let result;
|
|
247
|
-
const inputsHash = hashValue(args);
|
|
248
275
|
try {
|
|
249
276
|
result = fn.apply(this, args);
|
|
250
277
|
} catch (e) {
|
|
251
|
-
|
|
252
|
-
errorCode = e.constructor.name;
|
|
278
|
+
recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
|
|
253
279
|
throw e;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
|
|
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
|
|
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);
|
|
287
|
+
throw e;
|
|
273
288
|
});
|
|
274
|
-
client.record(event);
|
|
275
289
|
}
|
|
290
|
+
recordEvent(client, toolName, taskId, args, start, "success", null, result);
|
|
276
291
|
return result;
|
|
277
292
|
};
|
|
278
293
|
Object.defineProperty(wrapped, "name", { value: toolName });
|
package/dist/index.d.cts
CHANGED
|
@@ -104,6 +104,7 @@ interface AuditClientOptions {
|
|
|
104
104
|
agentId: string;
|
|
105
105
|
agentVersion?: string;
|
|
106
106
|
humanOperatorId?: string;
|
|
107
|
+
signingKey?: string;
|
|
107
108
|
signingKeyPath?: string;
|
|
108
109
|
fallbackLogPath?: string;
|
|
109
110
|
httpApiKey?: string;
|
|
@@ -119,6 +120,7 @@ declare class AuditClient {
|
|
|
119
120
|
private _emit;
|
|
120
121
|
constructor(options: AuditClientOptions);
|
|
121
122
|
get sessionId(): string;
|
|
123
|
+
/** Record an audit event. Never throws — errors are logged and swallowed. */
|
|
122
124
|
record(event: AuditEvent): void;
|
|
123
125
|
}
|
|
124
126
|
|
|
@@ -127,7 +129,7 @@ interface AuditToolOptions {
|
|
|
127
129
|
taskId?: string;
|
|
128
130
|
}
|
|
129
131
|
/**
|
|
130
|
-
* Wrap a function with audit logging.
|
|
132
|
+
* Wrap a function with audit logging. Works with both sync and async functions.
|
|
131
133
|
*
|
|
132
134
|
* Supports two calling styles:
|
|
133
135
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -104,6 +104,7 @@ interface AuditClientOptions {
|
|
|
104
104
|
agentId: string;
|
|
105
105
|
agentVersion?: string;
|
|
106
106
|
humanOperatorId?: string;
|
|
107
|
+
signingKey?: string;
|
|
107
108
|
signingKeyPath?: string;
|
|
108
109
|
fallbackLogPath?: string;
|
|
109
110
|
httpApiKey?: string;
|
|
@@ -119,6 +120,7 @@ declare class AuditClient {
|
|
|
119
120
|
private _emit;
|
|
120
121
|
constructor(options: AuditClientOptions);
|
|
121
122
|
get sessionId(): string;
|
|
123
|
+
/** Record an audit event. Never throws — errors are logged and swallowed. */
|
|
122
124
|
record(event: AuditEvent): void;
|
|
123
125
|
}
|
|
124
126
|
|
|
@@ -127,7 +129,7 @@ interface AuditToolOptions {
|
|
|
127
129
|
taskId?: string;
|
|
128
130
|
}
|
|
129
131
|
/**
|
|
130
|
-
* Wrap a function with audit logging.
|
|
132
|
+
* Wrap a function with audit logging. Works with both sync and async functions.
|
|
131
133
|
*
|
|
132
134
|
* Supports two calling styles:
|
|
133
135
|
*
|
package/dist/index.js
CHANGED
|
@@ -147,15 +147,19 @@ var AuditClient = class {
|
|
|
147
147
|
this.agentVersion = options.agentVersion ?? "0.1.0";
|
|
148
148
|
this.humanOperatorId = options.humanOperatorId ?? "unknown";
|
|
149
149
|
this._sessionId = randomUUID2();
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const raw = readFileSync(keyPath);
|
|
153
|
-
this._secretKey = new Uint8Array(raw);
|
|
150
|
+
if (options.signingKey) {
|
|
151
|
+
this._secretKey = new Uint8Array(Buffer.from(options.signingKey, "base64"));
|
|
154
152
|
} else {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
+
}
|
|
159
163
|
}
|
|
160
164
|
const fallback = options.fallbackLogPath ?? "./audit_fallback.jsonl";
|
|
161
165
|
if (options.httpApiKey) {
|
|
@@ -171,12 +175,17 @@ var AuditClient = class {
|
|
|
171
175
|
get sessionId() {
|
|
172
176
|
return this._sessionId;
|
|
173
177
|
}
|
|
178
|
+
/** Record an audit event. Never throws — errors are logged and swallowed. */
|
|
174
179
|
record(event) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
}
|
|
180
189
|
}
|
|
181
190
|
};
|
|
182
191
|
|
|
@@ -193,41 +202,47 @@ function sanitise(params) {
|
|
|
193
202
|
function hashValue(value) {
|
|
194
203
|
return createHash2("sha256").update(JSON.stringify(value, null, 0)).digest("hex");
|
|
195
204
|
}
|
|
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
|
+
}
|
|
196
226
|
function wrapFn(client, fn, toolName, taskId) {
|
|
197
227
|
const wrapped = function(...args) {
|
|
198
228
|
const start = performance.now();
|
|
199
|
-
let status = "success";
|
|
200
|
-
let errorCode = null;
|
|
201
229
|
let result;
|
|
202
|
-
const inputsHash = hashValue(args);
|
|
203
230
|
try {
|
|
204
231
|
result = fn.apply(this, args);
|
|
205
232
|
} catch (e) {
|
|
206
|
-
|
|
207
|
-
errorCode = e.constructor.name;
|
|
233
|
+
recordEvent(client, toolName, taskId, args, start, "failure", e.constructor.name, null);
|
|
208
234
|
throw e;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
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);
|
|
242
|
+
throw e;
|
|
228
243
|
});
|
|
229
|
-
client.record(event);
|
|
230
244
|
}
|
|
245
|
+
recordEvent(client, toolName, taskId, args, start, "success", null, result);
|
|
231
246
|
return result;
|
|
232
247
|
};
|
|
233
248
|
Object.defineProperty(wrapped, "name", { value: toolName });
|
package/package.json
CHANGED