@nomosprotocol/sdk 1.0.0 → 1.1.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/dist/index.cjs +539 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +360 -0
- package/dist/index.d.ts +360 -4
- package/dist/index.js +508 -2
- package/dist/index.js.map +1 -1
- package/package.json +25 -7
- package/dist/errors.d.ts +0 -31
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -54
- package/dist/errors.js.map +0 -1
- package/dist/http-client.d.ts +0 -14
- package/dist/http-client.d.ts.map +0 -1
- package/dist/http-client.js +0 -70
- package/dist/http-client.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/nomos.d.ts +0 -13
- package/dist/nomos.d.ts.map +0 -1
- package/dist/nomos.js +0 -26
- package/dist/nomos.js.map +0 -1
- package/dist/resources/artifacts.d.ts +0 -22
- package/dist/resources/artifacts.d.ts.map +0 -1
- package/dist/resources/artifacts.js +0 -46
- package/dist/resources/artifacts.js.map +0 -1
- package/dist/resources/decisions.d.ts +0 -24
- package/dist/resources/decisions.d.ts.map +0 -1
- package/dist/resources/decisions.js +0 -53
- package/dist/resources/decisions.js.map +0 -1
- package/dist/resources/governance.d.ts +0 -30
- package/dist/resources/governance.d.ts.map +0 -1
- package/dist/resources/governance.js +0 -33
- package/dist/resources/governance.js.map +0 -1
- package/dist/types.d.ts +0 -117
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/util.d.ts +0 -4
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -11
- package/dist/util.js.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Nomos: () => Nomos,
|
|
24
|
+
NomosAPIError: () => NomosAPIError,
|
|
25
|
+
NomosAuthenticationError: () => NomosAuthenticationError,
|
|
26
|
+
NomosAuthorizationError: () => NomosAuthorizationError,
|
|
27
|
+
NomosError: () => NomosError,
|
|
28
|
+
NomosNetworkError: () => NomosNetworkError,
|
|
29
|
+
NomosNotFoundError: () => NomosNotFoundError,
|
|
30
|
+
NomosRateLimitError: () => NomosRateLimitError,
|
|
31
|
+
NomosValidationError: () => NomosValidationError,
|
|
32
|
+
NomosWebhookSignatureError: () => NomosWebhookSignatureError
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/errors.ts
|
|
37
|
+
var errors_exports = {};
|
|
38
|
+
__export(errors_exports, {
|
|
39
|
+
NomosAPIError: () => NomosAPIError,
|
|
40
|
+
NomosAuthenticationError: () => NomosAuthenticationError,
|
|
41
|
+
NomosAuthorizationError: () => NomosAuthorizationError,
|
|
42
|
+
NomosError: () => NomosError,
|
|
43
|
+
NomosNetworkError: () => NomosNetworkError,
|
|
44
|
+
NomosNotFoundError: () => NomosNotFoundError,
|
|
45
|
+
NomosRateLimitError: () => NomosRateLimitError,
|
|
46
|
+
NomosValidationError: () => NomosValidationError,
|
|
47
|
+
NomosWebhookSignatureError: () => NomosWebhookSignatureError
|
|
48
|
+
});
|
|
49
|
+
var NomosError = class extends Error {
|
|
50
|
+
constructor(message, code, status, requestId) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "NomosError";
|
|
53
|
+
this.code = code;
|
|
54
|
+
this.status = status;
|
|
55
|
+
this.requestId = requestId;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var NomosAuthenticationError = class extends NomosError {
|
|
59
|
+
constructor(message = "Invalid API key", requestId) {
|
|
60
|
+
super(message, "authentication_error", 401, requestId);
|
|
61
|
+
this.name = "NomosAuthenticationError";
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var NomosAuthorizationError = class extends NomosError {
|
|
65
|
+
constructor(message = "Access denied", requestId) {
|
|
66
|
+
super(message, "authorization_error", 403, requestId);
|
|
67
|
+
this.name = "NomosAuthorizationError";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var NomosRateLimitError = class extends NomosError {
|
|
71
|
+
constructor(message = "Rate limit exceeded", retryAfter, requestId) {
|
|
72
|
+
super(message, "rate_limit_error", 429, requestId);
|
|
73
|
+
this.name = "NomosRateLimitError";
|
|
74
|
+
this.retryAfter = retryAfter;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var NomosValidationError = class extends NomosError {
|
|
78
|
+
constructor(message, details, requestId) {
|
|
79
|
+
super(message, "validation_error", 400, requestId);
|
|
80
|
+
this.name = "NomosValidationError";
|
|
81
|
+
this.details = details;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var NomosNotFoundError = class extends NomosError {
|
|
85
|
+
constructor(message, requestId) {
|
|
86
|
+
super(message, "not_found", 404, requestId);
|
|
87
|
+
this.name = "NomosNotFoundError";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var NomosAPIError = class extends NomosError {
|
|
91
|
+
constructor(message, status, requestId) {
|
|
92
|
+
super(message, "api_error", status, requestId);
|
|
93
|
+
this.name = "NomosAPIError";
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var NomosNetworkError = class extends NomosError {
|
|
97
|
+
constructor(message) {
|
|
98
|
+
super(message, "network_error");
|
|
99
|
+
this.name = "NomosNetworkError";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var NomosWebhookSignatureError = class extends NomosError {
|
|
103
|
+
constructor(message) {
|
|
104
|
+
super(message, "webhook_signature_error", 400);
|
|
105
|
+
this.name = "NomosWebhookSignatureError";
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/util.ts
|
|
110
|
+
async function sleep(ms) {
|
|
111
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
112
|
+
}
|
|
113
|
+
function jitteredBackoff(attempt, baseMs = 500) {
|
|
114
|
+
const exp = Math.min(baseMs * 2 ** attempt, 16e3);
|
|
115
|
+
return exp * (0.75 + Math.random() * 0.5);
|
|
116
|
+
}
|
|
117
|
+
function isRetryable(status) {
|
|
118
|
+
return status === 429 || status >= 500;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/http-client.ts
|
|
122
|
+
var SDK_VERSION = "1.1.0";
|
|
123
|
+
var HttpClient = class {
|
|
124
|
+
constructor(opts) {
|
|
125
|
+
this.opts = opts;
|
|
126
|
+
}
|
|
127
|
+
async get(path, reqOpts) {
|
|
128
|
+
return this.request("GET", path, void 0, reqOpts);
|
|
129
|
+
}
|
|
130
|
+
async post(path, body, reqOpts) {
|
|
131
|
+
return this.request("POST", path, body, reqOpts);
|
|
132
|
+
}
|
|
133
|
+
async request(method, path, body, reqOpts, attempt = 0) {
|
|
134
|
+
const apiKey = reqOpts?.apiKey ?? this.opts.apiKey;
|
|
135
|
+
const timeout = reqOpts?.timeout ?? this.opts.timeout;
|
|
136
|
+
const maxRetries = reqOpts?.maxRetries ?? this.opts.maxRetries;
|
|
137
|
+
const url = `${this.opts.baseUrl}${path}`;
|
|
138
|
+
const controller = new AbortController();
|
|
139
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
140
|
+
const headers = {
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
"X-Nomos-Api-Key": apiKey,
|
|
143
|
+
"User-Agent": `nomos-node/${SDK_VERSION}`,
|
|
144
|
+
"NOMOS-SDK-Version": SDK_VERSION
|
|
145
|
+
};
|
|
146
|
+
if (reqOpts?.idempotencyKey) {
|
|
147
|
+
headers["Idempotency-Key"] = reqOpts.idempotencyKey;
|
|
148
|
+
}
|
|
149
|
+
let res;
|
|
150
|
+
try {
|
|
151
|
+
res = await fetch(url, {
|
|
152
|
+
method,
|
|
153
|
+
signal: controller.signal,
|
|
154
|
+
headers,
|
|
155
|
+
body: body != null ? JSON.stringify(body) : void 0
|
|
156
|
+
});
|
|
157
|
+
} catch (err) {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
if (err.name === "AbortError") {
|
|
160
|
+
throw new NomosNetworkError(`Request timed out after ${timeout}ms`);
|
|
161
|
+
}
|
|
162
|
+
throw new NomosNetworkError(err.message ?? "Network error");
|
|
163
|
+
} finally {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
}
|
|
166
|
+
const requestId = res.headers.get("x-request-id") ?? "";
|
|
167
|
+
const rawHeaders = {};
|
|
168
|
+
res.headers.forEach((value, key) => {
|
|
169
|
+
rawHeaders[key] = value;
|
|
170
|
+
});
|
|
171
|
+
const lastResponse = {
|
|
172
|
+
requestId,
|
|
173
|
+
statusCode: res.status,
|
|
174
|
+
headers: rawHeaders
|
|
175
|
+
};
|
|
176
|
+
if (res.ok) {
|
|
177
|
+
const data = await res.json();
|
|
178
|
+
return { data, lastResponse };
|
|
179
|
+
}
|
|
180
|
+
if (isRetryable(res.status) && attempt < maxRetries) {
|
|
181
|
+
const retryAfter = res.headers.get("retry-after");
|
|
182
|
+
const delay = retryAfter ? parseInt(retryAfter, 10) * 1e3 : jitteredBackoff(attempt);
|
|
183
|
+
await sleep(delay);
|
|
184
|
+
return this.request(method, path, body, reqOpts, attempt + 1);
|
|
185
|
+
}
|
|
186
|
+
let errBody = {};
|
|
187
|
+
try {
|
|
188
|
+
errBody = await res.json();
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
const message = errBody?.error ?? errBody?.message ?? res.statusText;
|
|
192
|
+
switch (res.status) {
|
|
193
|
+
case 401:
|
|
194
|
+
throw new NomosAuthenticationError(message, requestId);
|
|
195
|
+
case 403:
|
|
196
|
+
throw new NomosAuthorizationError(message, requestId);
|
|
197
|
+
case 404:
|
|
198
|
+
throw new NomosNotFoundError(message, requestId);
|
|
199
|
+
case 422:
|
|
200
|
+
case 400:
|
|
201
|
+
throw new NomosValidationError(message, errBody?.details, requestId);
|
|
202
|
+
case 429:
|
|
203
|
+
throw new NomosRateLimitError(
|
|
204
|
+
message,
|
|
205
|
+
res.headers.get("retry-after") ? parseInt(res.headers.get("retry-after"), 10) : void 0,
|
|
206
|
+
requestId
|
|
207
|
+
);
|
|
208
|
+
default:
|
|
209
|
+
throw new NomosAPIError(message, res.status, requestId);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/resources/decisions.ts
|
|
215
|
+
var Decisions = class {
|
|
216
|
+
constructor(http) {
|
|
217
|
+
this.http = http;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Verify whether an AI decision is permitted under a governance artifact.
|
|
221
|
+
*
|
|
222
|
+
* Public demo artifacts (`pub_*`) work without an API key.
|
|
223
|
+
* Custom artifacts require a valid API key.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* const result = await nomos.decisions.verify({
|
|
227
|
+
* artifact_id: 'pub_lending_v1',
|
|
228
|
+
* decision_context: { credit_score: 720, loan_amount: 50000 },
|
|
229
|
+
* });
|
|
230
|
+
*
|
|
231
|
+
* if (!result.allowed) {
|
|
232
|
+
* throw new Error(`Blocked: ${result.verdict_description}`);
|
|
233
|
+
* }
|
|
234
|
+
*
|
|
235
|
+
* // Debugging / tracing
|
|
236
|
+
* console.log(result.lastResponse.requestId);
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* // Per-request options
|
|
240
|
+
* const result = await nomos.decisions.verify(params, {
|
|
241
|
+
* idempotencyKey: `verify-${transactionId}`,
|
|
242
|
+
* timeout: 5000,
|
|
243
|
+
* });
|
|
244
|
+
*/
|
|
245
|
+
async verify(params, opts) {
|
|
246
|
+
const { artifact_id, decision_context, proposed_decision, correlation_id } = params;
|
|
247
|
+
const isPublic = artifact_id.startsWith("pub_");
|
|
248
|
+
let httpResp;
|
|
249
|
+
if (isPublic) {
|
|
250
|
+
httpResp = await this.http.post("/api/v1/verify", {
|
|
251
|
+
artifact_id,
|
|
252
|
+
decision: proposed_decision ?? "evaluate",
|
|
253
|
+
inputs: decision_context
|
|
254
|
+
}, opts);
|
|
255
|
+
} else {
|
|
256
|
+
httpResp = await this.http.post("/api/v1/verify-decision", {
|
|
257
|
+
artifact_id,
|
|
258
|
+
decision_context,
|
|
259
|
+
proposed_decision,
|
|
260
|
+
correlation_id: correlation_id ?? globalThis.crypto.randomUUID()
|
|
261
|
+
}, opts);
|
|
262
|
+
}
|
|
263
|
+
const raw = httpResp.data;
|
|
264
|
+
return {
|
|
265
|
+
allowed: raw.allowed ?? false,
|
|
266
|
+
verdict: raw.outcome ?? raw.verdict ?? "pending_human_review",
|
|
267
|
+
verdict_description: raw.verdict_description ?? "",
|
|
268
|
+
rule_reference: raw.rule_reference ?? raw.rule_applied,
|
|
269
|
+
audit_record: raw.audit_record ?? raw.audit_hash,
|
|
270
|
+
artifact_id: raw.artifact_id ?? artifact_id,
|
|
271
|
+
execution_id: raw.execution_id,
|
|
272
|
+
decided_at: raw.decided_at ?? raw.evaluated_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
273
|
+
lastResponse: httpResp.lastResponse
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// src/resources/artifacts.ts
|
|
279
|
+
var Artifacts = class {
|
|
280
|
+
constructor(http) {
|
|
281
|
+
this.http = http;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Retrieve a single artifact by ID.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* const artifact = await nomos.artifacts.retrieve('loan_approval_v1');
|
|
288
|
+
* console.log(artifact.ari_score, artifact.autonomy_band);
|
|
289
|
+
* console.log(artifact.lastResponse.requestId); // for support tickets
|
|
290
|
+
*/
|
|
291
|
+
async retrieve(artifactId, opts) {
|
|
292
|
+
const { data, lastResponse } = await this.http.get(
|
|
293
|
+
`/api/exchange/artifacts/${encodeURIComponent(artifactId)}`,
|
|
294
|
+
opts
|
|
295
|
+
);
|
|
296
|
+
return { ...data, lastResponse };
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* List published artifacts on the Exchange with optional filters.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* const { data, total } = await nomos.artifacts.list({ band: 'autonomous', limit: 10 });
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* // With per-request options
|
|
306
|
+
* const page = await nomos.artifacts.list({ limit: 20 }, { timeout: 5000 });
|
|
307
|
+
*/
|
|
308
|
+
async list(params = {}, opts) {
|
|
309
|
+
const qs = new URLSearchParams();
|
|
310
|
+
if (params.band) qs.set("band", params.band);
|
|
311
|
+
if (params.domain) qs.set("domain", params.domain);
|
|
312
|
+
if (params.jurisdiction) qs.set("jurisdiction", params.jurisdiction);
|
|
313
|
+
if (params.sort) qs.set("sort", params.sort);
|
|
314
|
+
if (params.limit != null) qs.set("limit", String(params.limit));
|
|
315
|
+
if (params.offset != null) qs.set("offset", String(params.offset));
|
|
316
|
+
const query = qs.toString();
|
|
317
|
+
const { data: raw, lastResponse } = await this.http.get(
|
|
318
|
+
`/api/exchange/artifacts${query ? `?${query}` : ""}`,
|
|
319
|
+
opts
|
|
320
|
+
);
|
|
321
|
+
const normalized = {
|
|
322
|
+
data: Array.isArray(raw) ? raw : raw.artifacts ?? raw.data ?? [],
|
|
323
|
+
total: raw.total ?? (Array.isArray(raw) ? raw.length : 0),
|
|
324
|
+
limit: params.limit ?? 20,
|
|
325
|
+
offset: params.offset ?? 0
|
|
326
|
+
};
|
|
327
|
+
return { ...normalized, lastResponse };
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Auto-paginating iterator — yields every artifact across all pages.
|
|
331
|
+
* Stops automatically when the last page is reached.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* for await (const artifact of nomos.artifacts.listAutoPaging({ band: 'autonomous' })) {
|
|
335
|
+
* console.log(artifact.title, artifact.ari_score);
|
|
336
|
+
* }
|
|
337
|
+
*/
|
|
338
|
+
async *listAutoPaging(params = {}, opts) {
|
|
339
|
+
let offset = params.offset ?? 0;
|
|
340
|
+
const limit = params.limit ?? 20;
|
|
341
|
+
while (true) {
|
|
342
|
+
const page = await this.list({ ...params, offset, limit }, opts);
|
|
343
|
+
for (const item of page.data) yield item;
|
|
344
|
+
if (page.data.length < limit || offset + page.data.length >= page.total) break;
|
|
345
|
+
offset += page.data.length;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// src/resources/governance.ts
|
|
351
|
+
var Governance = class {
|
|
352
|
+
constructor(http) {
|
|
353
|
+
this.http = http;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Generate a sealed .nomos governance artifact from policy documents
|
|
357
|
+
* and optional historical decision logs.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* const artifact = await nomos.governance.generate({
|
|
361
|
+
* policies: [policyText],
|
|
362
|
+
* decision_logs: historicalRows,
|
|
363
|
+
* domain: 'loan_approval',
|
|
364
|
+
* });
|
|
365
|
+
* console.log(artifact.ari_score, artifact.seal_hash);
|
|
366
|
+
*/
|
|
367
|
+
async generate(params, opts) {
|
|
368
|
+
const { data, lastResponse } = await this.http.post(
|
|
369
|
+
"/api/v1/generate-governance",
|
|
370
|
+
params,
|
|
371
|
+
opts
|
|
372
|
+
);
|
|
373
|
+
return { ...data, lastResponse };
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Detect contradictions and conflicts in a rule set before sealing.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* const report = await nomos.governance.detectContradictions({ rules });
|
|
380
|
+
* if (!report.clean) {
|
|
381
|
+
* console.log(report.contradictions);
|
|
382
|
+
* }
|
|
383
|
+
*/
|
|
384
|
+
async detectContradictions(params, opts) {
|
|
385
|
+
const { data, lastResponse } = await this.http.post(
|
|
386
|
+
"/api/v1/detect-contradictions",
|
|
387
|
+
params,
|
|
388
|
+
opts
|
|
389
|
+
);
|
|
390
|
+
return { ...data, lastResponse };
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// src/resources/webhooks.ts
|
|
395
|
+
function hexToBytes(hex) {
|
|
396
|
+
if (hex.length % 2 !== 0) throw new NomosWebhookSignatureError("Invalid hex encoding in signature");
|
|
397
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
398
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
399
|
+
const byte = parseInt(hex.slice(i, i + 2), 16);
|
|
400
|
+
if (isNaN(byte)) throw new NomosWebhookSignatureError("Invalid hex encoding in signature");
|
|
401
|
+
bytes[i / 2] = byte;
|
|
402
|
+
}
|
|
403
|
+
return bytes;
|
|
404
|
+
}
|
|
405
|
+
function safeEqual(a, b) {
|
|
406
|
+
if (a.length !== b.length) return false;
|
|
407
|
+
let diff = 0;
|
|
408
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
409
|
+
return diff === 0;
|
|
410
|
+
}
|
|
411
|
+
var Webhooks = class {
|
|
412
|
+
/**
|
|
413
|
+
* Verify a NOMOS webhook signature and return the parsed event.
|
|
414
|
+
*
|
|
415
|
+
* Signature header format: `t=<unix_ts>,v1=<hmac_sha256_hex>`
|
|
416
|
+
* Signed payload: `<timestamp>.<raw_body>`
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* // Express
|
|
420
|
+
* app.post('/webhooks/nomos', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
421
|
+
* const event = await nomos.webhooks.constructEvent(
|
|
422
|
+
* req.body,
|
|
423
|
+
* req.headers['nomos-signature'] as string,
|
|
424
|
+
* process.env.NOMOS_WEBHOOK_SECRET!,
|
|
425
|
+
* );
|
|
426
|
+
* console.log(event.type, event.data);
|
|
427
|
+
* res.sendStatus(200);
|
|
428
|
+
* });
|
|
429
|
+
*
|
|
430
|
+
* @throws {NomosWebhookSignatureError} if signature is invalid, expired, or malformed
|
|
431
|
+
*/
|
|
432
|
+
async constructEvent(payload, signature, secret, toleranceSeconds = 300) {
|
|
433
|
+
const body = typeof payload === "string" ? payload : new TextDecoder().decode(payload);
|
|
434
|
+
const parts = {};
|
|
435
|
+
for (const part of signature.split(",")) {
|
|
436
|
+
const eq = part.indexOf("=");
|
|
437
|
+
if (eq > -1) parts[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();
|
|
438
|
+
}
|
|
439
|
+
if (!parts["t"] || !parts["v1"]) {
|
|
440
|
+
throw new NomosWebhookSignatureError(
|
|
441
|
+
'Invalid signature format \u2014 expected "t=<timestamp>,v1=<hmac>"'
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
const timestamp = parseInt(parts["t"], 10);
|
|
445
|
+
if (isNaN(timestamp)) {
|
|
446
|
+
throw new NomosWebhookSignatureError("Invalid timestamp in webhook signature");
|
|
447
|
+
}
|
|
448
|
+
const ageSecs = Math.abs(Date.now() / 1e3 - timestamp);
|
|
449
|
+
if (toleranceSeconds > 0 && ageSecs > toleranceSeconds) {
|
|
450
|
+
throw new NomosWebhookSignatureError(
|
|
451
|
+
`Webhook timestamp too old (${Math.round(ageSecs)}s > ${toleranceSeconds}s tolerance). Ensure your server clock is synced and the event is not being replayed.`
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
const enc = new TextEncoder();
|
|
455
|
+
const keyMaterial = await globalThis.crypto.subtle.importKey(
|
|
456
|
+
"raw",
|
|
457
|
+
enc.encode(secret),
|
|
458
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
459
|
+
false,
|
|
460
|
+
["sign"]
|
|
461
|
+
);
|
|
462
|
+
const signedPayload = `${parts["t"]}.${body}`;
|
|
463
|
+
const expectedBuf = await globalThis.crypto.subtle.sign(
|
|
464
|
+
"HMAC",
|
|
465
|
+
keyMaterial,
|
|
466
|
+
enc.encode(signedPayload)
|
|
467
|
+
);
|
|
468
|
+
const expected = new Uint8Array(expectedBuf);
|
|
469
|
+
const received = hexToBytes(parts["v1"]);
|
|
470
|
+
if (!safeEqual(expected, received)) {
|
|
471
|
+
throw new NomosWebhookSignatureError(
|
|
472
|
+
"Webhook signature mismatch \u2014 verify your NOMOS_WEBHOOK_SECRET matches the one in Studio."
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
return JSON.parse(body);
|
|
477
|
+
} catch {
|
|
478
|
+
throw new NomosWebhookSignatureError("Webhook payload is not valid JSON");
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Verify a webhook signature without parsing the event.
|
|
483
|
+
* Returns `true` if valid, `false` if not (never throws).
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* const valid = await nomos.webhooks.verifySignature(rawBody, sig, secret);
|
|
487
|
+
* if (!valid) return res.sendStatus(400);
|
|
488
|
+
*/
|
|
489
|
+
async verifySignature(payload, signature, secret, toleranceSeconds = 300) {
|
|
490
|
+
try {
|
|
491
|
+
await this.constructEvent(payload, signature, secret, toleranceSeconds);
|
|
492
|
+
return true;
|
|
493
|
+
} catch {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/nomos.ts
|
|
500
|
+
var DEFAULT_BASE_URL = "https://nomos.ai";
|
|
501
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
502
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
503
|
+
var Nomos = class {
|
|
504
|
+
static {
|
|
505
|
+
this.errors = errors_exports;
|
|
506
|
+
}
|
|
507
|
+
constructor(apiKey, config = {}) {
|
|
508
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
509
|
+
throw new NomosAuthenticationError(
|
|
510
|
+
'A valid API key is required. Pass it as the first argument: new Nomos("nms_live_...")'
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
this.mode = apiKey.startsWith("nms_test_") ? "test" : "live";
|
|
514
|
+
const http = new HttpClient({
|
|
515
|
+
apiKey,
|
|
516
|
+
baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, ""),
|
|
517
|
+
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
518
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
519
|
+
});
|
|
520
|
+
this.decisions = new Decisions(http);
|
|
521
|
+
this.artifacts = new Artifacts(http);
|
|
522
|
+
this.governance = new Governance(http);
|
|
523
|
+
this.webhooks = new Webhooks();
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
527
|
+
0 && (module.exports = {
|
|
528
|
+
Nomos,
|
|
529
|
+
NomosAPIError,
|
|
530
|
+
NomosAuthenticationError,
|
|
531
|
+
NomosAuthorizationError,
|
|
532
|
+
NomosError,
|
|
533
|
+
NomosNetworkError,
|
|
534
|
+
NomosNotFoundError,
|
|
535
|
+
NomosRateLimitError,
|
|
536
|
+
NomosValidationError,
|
|
537
|
+
NomosWebhookSignatureError
|
|
538
|
+
});
|
|
539
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/util.ts","../src/http-client.ts","../src/resources/decisions.ts","../src/resources/artifacts.ts","../src/resources/governance.ts","../src/resources/webhooks.ts","../src/nomos.ts"],"sourcesContent":["export { Nomos } from './nomos.js';\nexport * from './errors.js';\nexport type {\n NomosConfig,\n RequestOptions,\n NomosRawResponse,\n WithLastResponse,\n VerifyDecisionParams,\n VerifyDecisionResult,\n ListArtifactsParams,\n ArtifactList,\n Artifact,\n GenerateGovernanceParams,\n GenerateGovernanceResult,\n DetectContradictionsParams,\n DetectContradictionsResult,\n Contradiction,\n Verdict,\n NomosWebhookEvent,\n NomosWebhookEventType,\n} from './types.js';\n","export type NomosErrorCode =\n | 'authentication_error'\n | 'authorization_error'\n | 'rate_limit_error'\n | 'validation_error'\n | 'not_found'\n | 'api_error'\n | 'network_error'\n | 'webhook_signature_error';\n\nexport class NomosError extends Error {\n readonly code: NomosErrorCode;\n readonly status?: number;\n readonly requestId?: string;\n\n constructor(message: string, code: NomosErrorCode, status?: number, requestId?: string) {\n super(message);\n this.name = 'NomosError';\n this.code = code;\n this.status = status;\n this.requestId = requestId;\n }\n}\n\nexport class NomosAuthenticationError extends NomosError {\n constructor(message = 'Invalid API key', requestId?: string) {\n super(message, 'authentication_error', 401, requestId);\n this.name = 'NomosAuthenticationError';\n }\n}\n\nexport class NomosAuthorizationError extends NomosError {\n constructor(message = 'Access denied', requestId?: string) {\n super(message, 'authorization_error', 403, requestId);\n this.name = 'NomosAuthorizationError';\n }\n}\n\nexport class NomosRateLimitError extends NomosError {\n readonly retryAfter?: number;\n\n constructor(message = 'Rate limit exceeded', retryAfter?: number, requestId?: string) {\n super(message, 'rate_limit_error', 429, requestId);\n this.name = 'NomosRateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class NomosValidationError extends NomosError {\n readonly details?: unknown;\n\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 'validation_error', 400, requestId);\n this.name = 'NomosValidationError';\n this.details = details;\n }\n}\n\nexport class NomosNotFoundError extends NomosError {\n constructor(message: string, requestId?: string) {\n super(message, 'not_found', 404, requestId);\n this.name = 'NomosNotFoundError';\n }\n}\n\nexport class NomosAPIError extends NomosError {\n constructor(message: string, status: number, requestId?: string) {\n super(message, 'api_error', status, requestId);\n this.name = 'NomosAPIError';\n }\n}\n\nexport class NomosNetworkError extends NomosError {\n constructor(message: string) {\n super(message, 'network_error');\n this.name = 'NomosNetworkError';\n }\n}\n\nexport class NomosWebhookSignatureError extends NomosError {\n constructor(message: string) {\n super(message, 'webhook_signature_error', 400);\n this.name = 'NomosWebhookSignatureError';\n }\n}\n","export async function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nexport function jitteredBackoff(attempt: number, baseMs = 500): number {\n const exp = Math.min(baseMs * 2 ** attempt, 16000);\n return exp * (0.75 + Math.random() * 0.5); // ±25% jitter\n}\n\nexport function isRetryable(status: number): boolean {\n return status === 429 || status >= 500;\n}\n","import {\n NomosAuthenticationError,\n NomosAuthorizationError,\n NomosRateLimitError,\n NomosValidationError,\n NomosNotFoundError,\n NomosAPIError,\n NomosNetworkError,\n} from './errors.js';\nimport { sleep, jitteredBackoff, isRetryable } from './util.js';\nimport type { RequestOptions, NomosRawResponse } from './types.js';\n\nconst SDK_VERSION = '1.1.0';\n\nexport interface HttpClientOptions {\n apiKey: string;\n baseUrl: string;\n maxRetries: number;\n timeout: number;\n}\n\nexport interface HttpResponse<T> {\n data: T;\n lastResponse: NomosRawResponse;\n}\n\nexport class HttpClient {\n constructor(private readonly opts: HttpClientOptions) {}\n\n async get<T>(path: string, reqOpts?: RequestOptions): Promise<HttpResponse<T>> {\n return this.request<T>('GET', path, undefined, reqOpts);\n }\n\n async post<T>(path: string, body: unknown, reqOpts?: RequestOptions): Promise<HttpResponse<T>> {\n return this.request<T>('POST', path, body, reqOpts);\n }\n\n private async request<T>(\n method: string,\n path: string,\n body: unknown,\n reqOpts?: RequestOptions,\n attempt = 0,\n ): Promise<HttpResponse<T>> {\n const apiKey = reqOpts?.apiKey ?? this.opts.apiKey;\n const timeout = reqOpts?.timeout ?? this.opts.timeout;\n const maxRetries = reqOpts?.maxRetries ?? this.opts.maxRetries;\n\n const url = `${this.opts.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Nomos-Api-Key': apiKey,\n 'User-Agent': `nomos-node/${SDK_VERSION}`,\n 'NOMOS-SDK-Version': SDK_VERSION,\n };\n if (reqOpts?.idempotencyKey) {\n headers['Idempotency-Key'] = reqOpts.idempotencyKey;\n }\n\n let res: Response;\n try {\n res = await fetch(url, {\n method,\n signal: controller.signal,\n headers,\n body: body != null ? JSON.stringify(body) : undefined,\n });\n } catch (err: any) {\n clearTimeout(timer);\n if (err.name === 'AbortError') {\n throw new NomosNetworkError(`Request timed out after ${timeout}ms`);\n }\n throw new NomosNetworkError(err.message ?? 'Network error');\n } finally {\n clearTimeout(timer);\n }\n\n const requestId = res.headers.get('x-request-id') ?? '';\n const rawHeaders: Record<string, string> = {};\n res.headers.forEach((value, key) => { rawHeaders[key] = value; });\n const lastResponse: NomosRawResponse = {\n requestId,\n statusCode: res.status,\n headers: rawHeaders,\n };\n\n if (res.ok) {\n const data = await res.json() as T;\n return { data, lastResponse };\n }\n\n // Retry on 429 / 5xx\n if (isRetryable(res.status) && attempt < maxRetries) {\n const retryAfter = res.headers.get('retry-after');\n const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : jitteredBackoff(attempt);\n await sleep(delay);\n return this.request<T>(method, path, body, reqOpts, attempt + 1);\n }\n\n let errBody: any = {};\n try { errBody = await res.json(); } catch {}\n const message: string = errBody?.error ?? errBody?.message ?? res.statusText;\n\n switch (res.status) {\n case 401: throw new NomosAuthenticationError(message, requestId);\n case 403: throw new NomosAuthorizationError(message, requestId);\n case 404: throw new NomosNotFoundError(message, requestId);\n case 422:\n case 400: throw new NomosValidationError(message, errBody?.details, requestId);\n case 429: throw new NomosRateLimitError(\n message,\n res.headers.get('retry-after') ? parseInt(res.headers.get('retry-after')!, 10) : undefined,\n requestId,\n );\n default: throw new NomosAPIError(message, res.status, requestId);\n }\n }\n}\n","import type { HttpClient } from '../http-client.js';\nimport type {\n VerifyDecisionParams,\n VerifyDecisionResult,\n RequestOptions,\n WithLastResponse,\n} from '../types.js';\n\nexport class Decisions {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Verify whether an AI decision is permitted under a governance artifact.\n *\n * Public demo artifacts (`pub_*`) work without an API key.\n * Custom artifacts require a valid API key.\n *\n * @example\n * const result = await nomos.decisions.verify({\n * artifact_id: 'pub_lending_v1',\n * decision_context: { credit_score: 720, loan_amount: 50000 },\n * });\n *\n * if (!result.allowed) {\n * throw new Error(`Blocked: ${result.verdict_description}`);\n * }\n *\n * // Debugging / tracing\n * console.log(result.lastResponse.requestId);\n *\n * @example\n * // Per-request options\n * const result = await nomos.decisions.verify(params, {\n * idempotencyKey: `verify-${transactionId}`,\n * timeout: 5000,\n * });\n */\n async verify(\n params: VerifyDecisionParams,\n opts?: RequestOptions,\n ): Promise<WithLastResponse<VerifyDecisionResult>> {\n const { artifact_id, decision_context, proposed_decision, correlation_id } = params;\n\n // Public artifacts (pub_*) use the open /verify endpoint — no API key required\n const isPublic = artifact_id.startsWith('pub_');\n let httpResp: Awaited<ReturnType<HttpClient['post']>>;\n\n if (isPublic) {\n httpResp = await this.http.post<any>('/api/v1/verify', {\n artifact_id,\n decision: proposed_decision ?? 'evaluate',\n inputs: decision_context,\n }, opts);\n } else {\n httpResp = await this.http.post<any>('/api/v1/verify-decision', {\n artifact_id,\n decision_context,\n proposed_decision,\n correlation_id: correlation_id ?? globalThis.crypto.randomUUID(),\n }, opts);\n }\n\n const raw: any = httpResp.data;\n\n return {\n allowed: raw.allowed ?? false,\n verdict: raw.outcome ?? raw.verdict ?? 'pending_human_review',\n verdict_description: raw.verdict_description ?? '',\n rule_reference: raw.rule_reference ?? raw.rule_applied,\n audit_record: raw.audit_record ?? raw.audit_hash,\n artifact_id: raw.artifact_id ?? artifact_id,\n execution_id: raw.execution_id,\n decided_at: raw.decided_at ?? raw.evaluated_at ?? new Date().toISOString(),\n lastResponse: httpResp.lastResponse,\n };\n }\n}\n","import type { HttpClient } from '../http-client.js';\nimport type {\n Artifact,\n ArtifactList,\n ListArtifactsParams,\n RequestOptions,\n WithLastResponse,\n} from '../types.js';\n\nexport class Artifacts {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Retrieve a single artifact by ID.\n *\n * @example\n * const artifact = await nomos.artifacts.retrieve('loan_approval_v1');\n * console.log(artifact.ari_score, artifact.autonomy_band);\n * console.log(artifact.lastResponse.requestId); // for support tickets\n */\n async retrieve(\n artifactId: string,\n opts?: RequestOptions,\n ): Promise<WithLastResponse<Artifact>> {\n const { data, lastResponse } = await this.http.get<Artifact>(\n `/api/exchange/artifacts/${encodeURIComponent(artifactId)}`,\n opts,\n );\n return { ...data, lastResponse };\n }\n\n /**\n * List published artifacts on the Exchange with optional filters.\n *\n * @example\n * const { data, total } = await nomos.artifacts.list({ band: 'autonomous', limit: 10 });\n *\n * @example\n * // With per-request options\n * const page = await nomos.artifacts.list({ limit: 20 }, { timeout: 5000 });\n */\n async list(\n params: ListArtifactsParams = {},\n opts?: RequestOptions,\n ): Promise<WithLastResponse<ArtifactList>> {\n const qs = new URLSearchParams();\n if (params.band) qs.set('band', params.band);\n if (params.domain) qs.set('domain', params.domain);\n if (params.jurisdiction) qs.set('jurisdiction', params.jurisdiction);\n if (params.sort) qs.set('sort', params.sort);\n if (params.limit != null) qs.set('limit', String(params.limit));\n if (params.offset != null) qs.set('offset', String(params.offset));\n const query = qs.toString();\n\n const { data: raw, lastResponse } = await this.http.get<any>(\n `/api/exchange/artifacts${query ? `?${query}` : ''}`,\n opts,\n );\n\n // API returns { artifacts: [], total: N } — normalise to { data: [], total: N }\n const normalized: ArtifactList = {\n data: Array.isArray(raw) ? raw : (raw.artifacts ?? raw.data ?? []),\n total: raw.total ?? (Array.isArray(raw) ? raw.length : 0),\n limit: params.limit ?? 20,\n offset: params.offset ?? 0,\n };\n\n return { ...normalized, lastResponse };\n }\n\n /**\n * Auto-paginating iterator — yields every artifact across all pages.\n * Stops automatically when the last page is reached.\n *\n * @example\n * for await (const artifact of nomos.artifacts.listAutoPaging({ band: 'autonomous' })) {\n * console.log(artifact.title, artifact.ari_score);\n * }\n */\n async *listAutoPaging(\n params: ListArtifactsParams = {},\n opts?: RequestOptions,\n ): AsyncGenerator<Artifact> {\n let offset = params.offset ?? 0;\n const limit = params.limit ?? 20;\n\n while (true) {\n const page = await this.list({ ...params, offset, limit }, opts);\n for (const item of page.data) yield item;\n if (page.data.length < limit || offset + page.data.length >= page.total) break;\n offset += page.data.length;\n }\n }\n}\n","import type { HttpClient } from '../http-client.js';\nimport type {\n GenerateGovernanceParams,\n GenerateGovernanceResult,\n DetectContradictionsParams,\n DetectContradictionsResult,\n RequestOptions,\n WithLastResponse,\n} from '../types.js';\n\nexport class Governance {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Generate a sealed .nomos governance artifact from policy documents\n * and optional historical decision logs.\n *\n * @example\n * const artifact = await nomos.governance.generate({\n * policies: [policyText],\n * decision_logs: historicalRows,\n * domain: 'loan_approval',\n * });\n * console.log(artifact.ari_score, artifact.seal_hash);\n */\n async generate(\n params: GenerateGovernanceParams,\n opts?: RequestOptions,\n ): Promise<WithLastResponse<GenerateGovernanceResult>> {\n const { data, lastResponse } = await this.http.post<GenerateGovernanceResult>(\n '/api/v1/generate-governance',\n params,\n opts,\n );\n return { ...data, lastResponse };\n }\n\n /**\n * Detect contradictions and conflicts in a rule set before sealing.\n *\n * @example\n * const report = await nomos.governance.detectContradictions({ rules });\n * if (!report.clean) {\n * console.log(report.contradictions);\n * }\n */\n async detectContradictions(\n params: DetectContradictionsParams,\n opts?: RequestOptions,\n ): Promise<WithLastResponse<DetectContradictionsResult>> {\n const { data, lastResponse } = await this.http.post<DetectContradictionsResult>(\n '/api/v1/detect-contradictions',\n params,\n opts,\n );\n return { ...data, lastResponse };\n }\n}\n","import { NomosWebhookSignatureError } from '../errors.js';\nimport type { NomosWebhookEvent } from '../types.js';\n\n// Hex string → Uint8Array (no Buffer dependency — works in Node and browsers)\nfunction hexToBytes(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) throw new NomosWebhookSignatureError('Invalid hex encoding in signature');\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n const byte = parseInt(hex.slice(i, i + 2), 16);\n if (isNaN(byte)) throw new NomosWebhookSignatureError('Invalid hex encoding in signature');\n bytes[i / 2] = byte;\n }\n return bytes;\n}\n\n// Constant-time comparison (prevents timing attacks)\nfunction safeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];\n return diff === 0;\n}\n\nexport class Webhooks {\n /**\n * Verify a NOMOS webhook signature and return the parsed event.\n *\n * Signature header format: `t=<unix_ts>,v1=<hmac_sha256_hex>`\n * Signed payload: `<timestamp>.<raw_body>`\n *\n * @example\n * // Express\n * app.post('/webhooks/nomos', express.raw({ type: 'application/json' }), async (req, res) => {\n * const event = await nomos.webhooks.constructEvent(\n * req.body,\n * req.headers['nomos-signature'] as string,\n * process.env.NOMOS_WEBHOOK_SECRET!,\n * );\n * console.log(event.type, event.data);\n * res.sendStatus(200);\n * });\n *\n * @throws {NomosWebhookSignatureError} if signature is invalid, expired, or malformed\n */\n async constructEvent(\n payload: string | Uint8Array,\n signature: string,\n secret: string,\n toleranceSeconds = 300,\n ): Promise<NomosWebhookEvent> {\n const body = typeof payload === 'string'\n ? payload\n : new TextDecoder().decode(payload);\n\n // Parse \"t=<ts>,v1=<hex>\" header\n const parts: Record<string, string> = {};\n for (const part of signature.split(',')) {\n const eq = part.indexOf('=');\n if (eq > -1) parts[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();\n }\n\n if (!parts['t'] || !parts['v1']) {\n throw new NomosWebhookSignatureError(\n 'Invalid signature format — expected \"t=<timestamp>,v1=<hmac>\"',\n );\n }\n\n const timestamp = parseInt(parts['t'], 10);\n if (isNaN(timestamp)) {\n throw new NomosWebhookSignatureError('Invalid timestamp in webhook signature');\n }\n\n const ageSecs = Math.abs(Date.now() / 1000 - timestamp);\n if (toleranceSeconds > 0 && ageSecs > toleranceSeconds) {\n throw new NomosWebhookSignatureError(\n `Webhook timestamp too old (${Math.round(ageSecs)}s > ${toleranceSeconds}s tolerance). ` +\n 'Ensure your server clock is synced and the event is not being replayed.',\n );\n }\n\n // HMAC-SHA256 via Web Crypto API (Node 18+ / modern browsers — no dependencies)\n const enc = new TextEncoder();\n const keyMaterial = await globalThis.crypto.subtle.importKey(\n 'raw',\n enc.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signedPayload = `${parts['t']}.${body}`;\n const expectedBuf = await globalThis.crypto.subtle.sign(\n 'HMAC',\n keyMaterial,\n enc.encode(signedPayload),\n );\n const expected = new Uint8Array(expectedBuf);\n const received = hexToBytes(parts['v1']);\n\n if (!safeEqual(expected, received)) {\n throw new NomosWebhookSignatureError(\n 'Webhook signature mismatch — verify your NOMOS_WEBHOOK_SECRET matches the one in Studio.',\n );\n }\n\n try {\n return JSON.parse(body) as NomosWebhookEvent;\n } catch {\n throw new NomosWebhookSignatureError('Webhook payload is not valid JSON');\n }\n }\n\n /**\n * Verify a webhook signature without parsing the event.\n * Returns `true` if valid, `false` if not (never throws).\n *\n * @example\n * const valid = await nomos.webhooks.verifySignature(rawBody, sig, secret);\n * if (!valid) return res.sendStatus(400);\n */\n async verifySignature(\n payload: string | Uint8Array,\n signature: string,\n secret: string,\n toleranceSeconds = 300,\n ): Promise<boolean> {\n try {\n await this.constructEvent(payload, signature, secret, toleranceSeconds);\n return true;\n } catch {\n return false;\n }\n }\n}\n","import { HttpClient } from './http-client.js';\nimport { Decisions } from './resources/decisions.js';\nimport { Artifacts } from './resources/artifacts.js';\nimport { Governance } from './resources/governance.js';\nimport { Webhooks } from './resources/webhooks.js';\nimport * as errors from './errors.js';\nimport type { NomosConfig } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://nomos.ai';\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_TIMEOUT = 30_000;\n\nexport class Nomos {\n readonly decisions: Decisions;\n readonly artifacts: Artifacts;\n readonly governance: Governance;\n readonly webhooks: Webhooks;\n\n /** Whether this instance is using a live or test API key. */\n readonly mode: 'live' | 'test';\n\n static readonly errors = errors;\n\n constructor(apiKey: string, config: NomosConfig = {}) {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new errors.NomosAuthenticationError(\n 'A valid API key is required. Pass it as the first argument: new Nomos(\"nms_live_...\")',\n );\n }\n\n this.mode = apiKey.startsWith('nms_test_') ? 'test' : 'live';\n\n const http = new HttpClient({\n apiKey,\n baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, ''),\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n });\n\n this.decisions = new Decisions(http);\n this.artifacts = new Artifacts(http);\n this.governance = new Governance(http);\n this.webhooks = new Webhooks();\n }\n}\n"],"mappings":"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","names":[]}
|