monora-ai 1.0.0 → 1.0.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.
Files changed (43) hide show
  1. package/README.md +109 -39
  2. package/dist/alerts.d.ts +40 -0
  3. package/dist/alerts.d.ts.map +1 -0
  4. package/dist/alerts.js +140 -0
  5. package/dist/attestation.d.ts +20 -0
  6. package/dist/attestation.d.ts.map +1 -0
  7. package/dist/attestation.js +160 -0
  8. package/dist/cli/diagnostics.d.ts +17 -0
  9. package/dist/cli/diagnostics.d.ts.map +1 -0
  10. package/dist/cli/diagnostics.js +278 -0
  11. package/dist/cli.d.ts +6 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +442 -0
  14. package/dist/config.d.ts +1 -1
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js +71 -3
  17. package/dist/data_handling.d.ts +49 -0
  18. package/dist/data_handling.d.ts.map +1 -0
  19. package/dist/data_handling.js +192 -0
  20. package/dist/dispatcher.d.ts +4 -0
  21. package/dist/dispatcher.d.ts.map +1 -1
  22. package/dist/dispatcher.js +111 -5
  23. package/dist/hasher.d.ts +2 -0
  24. package/dist/hasher.d.ts.map +1 -1
  25. package/dist/hasher.js +18 -1
  26. package/dist/index.d.ts +8 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +36 -1
  29. package/dist/instrumentation.d.ts +22 -0
  30. package/dist/instrumentation.d.ts.map +1 -0
  31. package/dist/instrumentation.js +88 -0
  32. package/dist/registry.d.ts.map +1 -1
  33. package/dist/registry.js +20 -1
  34. package/dist/report.d.ts +9 -0
  35. package/dist/report.d.ts.map +1 -0
  36. package/dist/report.js +267 -0
  37. package/dist/runtime.d.ts +37 -0
  38. package/dist/runtime.d.ts.map +1 -0
  39. package/dist/runtime.js +561 -0
  40. package/dist/security_report.d.ts +17 -0
  41. package/dist/security_report.d.ts.map +1 -0
  42. package/dist/security_report.js +313 -0
  43. package/package.json +5 -2
package/README.md CHANGED
@@ -21,27 +21,89 @@ npm install monora
21
21
  ## Quick Start
22
22
 
23
23
  ```typescript
24
- import { loadConfig, ModelRegistry, PolicyEngine, trace } from 'monora';
24
+ import { init, llmCall, trace } from 'monora';
25
25
 
26
- // Load configuration
27
- const config = loadConfig({ configPath: './monora.json' });
26
+ // Initialize SDK
27
+ init({ configPath: './monora.yml' });
28
28
 
29
- // Create policy engine
30
- const policyEngine = new PolicyEngine(config.policies || {});
31
-
32
- // Check if a model is allowed
33
- const violation = policyEngine.checkModel('gpt-4o', 'internal');
34
- if (violation) {
35
- console.error('Policy violation:', violation.message);
36
- }
29
+ const ask = llmCall({ purpose: 'support' })(function ask(
30
+ question: string,
31
+ model: string = 'gpt-4o-mini'
32
+ ) {
33
+ return { choices: [{ message: { content: 'ok' } }] };
34
+ });
37
35
 
38
36
  // Create a trace
39
37
  trace('my-ai-task', (span) => {
40
38
  console.log('Trace ID:', span.traceId);
41
- console.log('Span ID:', span.spanId);
39
+ ask('hello');
40
+ });
41
+ ```
42
+
43
+ ### Guided Setup (Wizard)
42
44
 
43
- // Your AI code here
45
+ ```bash
46
+ npx monora-ai init
47
+ # or
48
+ ./node_modules/.bin/monora init
49
+ ```
50
+
51
+ This generates a `monora.yml` you can load with `loadConfig({ configPath: './monora.yml' })`.
52
+
53
+ ### Reports & Security Review
54
+
55
+ ```bash
56
+ npx monora-ai report --input events.jsonl --output report.json
57
+ npx monora-ai report --input events.jsonl --output report.md --format markdown
58
+
59
+ npx monora-ai security-review --input events.jsonl --output security.json
60
+ npx monora-ai security-review --input events.jsonl --output security.json --sign gpg --gpg-key "you@example.com"
61
+ ```
62
+
63
+ ### Data Handling + Alerts
64
+
65
+ ```typescript
66
+ import { DataHandlingEngine, buildDataViolation, ViolationWebhookDispatcher } from 'monora';
67
+
68
+ const dataHandling = new DataHandlingEngine({
69
+ enabled: true,
70
+ mode: 'redact',
71
+ rules: [
72
+ { name: 'email', pattern: '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}' }
73
+ ]
44
74
  });
75
+
76
+ const payload = { message: 'Contact me at person@example.com' };
77
+ const { value, applied } = dataHandling.sanitizePayload('request', payload, 'confidential');
78
+
79
+ const dispatcher = new ViolationWebhookDispatcher({
80
+ endpoint: 'https://hooks.example.com/monora',
81
+ headers: { Authorization: 'Bearer TOKEN' }
82
+ });
83
+ dispatcher.start();
84
+ dispatcher.send({ event_type: 'policy_violation', message: 'Example violation' });
85
+ ```
86
+
87
+ ### High-level Runtime Helpers
88
+
89
+ ```typescript
90
+ import { init, logEvent, toolCall, agentStep, setViolationHandler } from 'monora';
91
+
92
+ init({ configPath: './monora.yml' });
93
+
94
+ setViolationHandler((violation) => {
95
+ console.error('Violation:', violation.message);
96
+ });
97
+
98
+ const fetchTool = toolCall({ toolName: 'fetch', purpose: 'integration' })(async (url: string) => {
99
+ return { ok: true, url };
100
+ });
101
+
102
+ const plan = agentStep({ agentName: 'planner', stepType: 'planning', purpose: 'analysis' })(
103
+ (goal: string) => [`step for ${goal}`]
104
+ );
105
+
106
+ logEvent('custom', { message: 'hello' }, { purpose: 'manual' });
45
107
  ```
46
108
 
47
109
  ## Configuration
@@ -144,46 +206,50 @@ const gaps = detectSequenceGaps(events);
144
206
  console.log('Sequence gaps:', gaps);
145
207
  ```
146
208
 
147
- ### Security Reports (Planned)
209
+ ### Security Reports
148
210
 
149
- Planned HTTP endpoint (see roadmap: https://github.com/monora/monora/issues).
211
+ Generate JSON security review reports locally with CLI:
150
212
 
151
- - **Endpoint**: `POST /v1/reports/security`
152
- - **Auth**: `Authorization: Bearer $MONORA_API_KEY`
153
- - **Request**:
154
- - `events`: array of Monora events
155
- - `config` (optional): policies/registry overrides
156
- - **Response**: `{ overall_risk_level, risk_factors, operational_metrics, ... }`
157
- - **Errors**: `401` unauthorized, `400` invalid payload, `404` endpoint unavailable
213
+ Auth: none (local CLI). Errors: invalid JSONL/config or GPG signing failures.
158
214
 
159
215
  ```bash
160
- curl -X POST "https://api.monora.example.com/v1/reports/security" \
161
- -H "Authorization: Bearer $MONORA_API_KEY" \
162
- -H "Content-Type: application/json" \
163
- -d '{"events": [{"event_type": "llm_call"}], "config": {"policies": {}}}'
216
+ npx monora-ai security-review --input events.jsonl --output security.json
217
+ npx monora-ai security-review --input events.jsonl --output security.json --config monora.yml
164
218
  ```
165
219
 
166
- ### Data Redaction (Planned)
220
+ ### Data Handling
167
221
 
168
- Planned SDK/config surface (see roadmap: https://github.com/monora/monora/issues).
222
+ Use the data handling engine for redaction or blocking decisions (modes: `redact`, `block`, `allow`):
169
223
 
170
- - **Config**: `data_handling.mode`, `data_handling.apply_to`, `data_handling.rules`
171
- - **Auth**: none for local processing
172
- - **Errors**: `DataHandlingViolation` (block mode), invalid regex patterns
224
+ Auth: none. Errors: `DataHandlingViolation` in block mode or invalid regex patterns.
173
225
 
174
226
  ```typescript
175
- // Planned API
176
- // const engine = new DataHandlingEngine(config.data_handling);
177
- // const [redacted, applied] = engine.redact('request', payload, 'confidential');
227
+ import { DataHandlingEngine } from 'monora';
228
+
229
+ const engine = new DataHandlingEngine({
230
+ enabled: true,
231
+ mode: 'redact',
232
+ rules: [{ name: 'email', pattern: '[^@]+@[^@]+\\.[^@]+' }]
233
+ });
234
+ const { value, applied } = engine.sanitizePayload('request', payload, 'confidential');
178
235
  ```
179
236
 
180
- ### Webhook Alerts (Planned)
237
+ ### Webhook Alerts
181
238
 
182
- Planned configuration (see roadmap: https://github.com/monora/monora/issues).
239
+ Send policy violation payloads to a webhook:
183
240
 
184
- - **Config**: `alerts.violation_webhook`, `alerts.headers`, `alerts.retry_attempts`
185
- - **Auth**: set `alerts.headers.Authorization` for your webhook
186
- - **Errors**: invalid URL, non-2xx responses, network timeouts
241
+ Auth: set headers such as `Authorization`. Errors: network failures, non-2xx responses, or queue overflow.
242
+
243
+ ```typescript
244
+ import { ViolationWebhookDispatcher } from 'monora';
245
+
246
+ const dispatcher = new ViolationWebhookDispatcher({
247
+ endpoint: 'https://hooks.example.com/monora',
248
+ headers: { Authorization: 'Bearer TOKEN' }
249
+ });
250
+ dispatcher.start();
251
+ dispatcher.send({ event_type: 'policy_violation', message: 'Blocked model' });
252
+ ```
187
253
 
188
254
  ```json
189
255
  {
@@ -196,6 +262,8 @@ Planned configuration (see roadmap: https://github.com/monora/monora/issues).
196
262
 
197
263
  ### Event Building and Dispatching
198
264
 
265
+ Event builder and dispatcher classes are available in the current Node SDK.
266
+
199
267
  ```typescript
200
268
  import { EventBuilder, EventDispatcher, StdoutSink, FileSink } from 'monora';
201
269
 
@@ -233,6 +301,8 @@ dispatcher.close();
233
301
 
234
302
  ### Sink Options
235
303
 
304
+ These sink implementations are exported and ready for use.
305
+
236
306
  ```typescript
237
307
  // Stdout Sink
238
308
  const stdoutSink = new StdoutSink('pretty'); // or 'json'
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Violation alert dispatching.
3
+ */
4
+ export declare class AlertError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ export declare function buildViolationPayload(event: Record<string, any>): Record<string, any>;
8
+ export declare class ViolationWebhookDispatcher {
9
+ private endpoint;
10
+ private headers;
11
+ private timeoutSec;
12
+ private retries;
13
+ private backoffBaseSec;
14
+ private queue;
15
+ private queueSize;
16
+ private failureMode;
17
+ private queueFullMode;
18
+ private timer?;
19
+ private fatalError?;
20
+ private inFlight;
21
+ constructor(options: {
22
+ endpoint: string;
23
+ headers: Record<string, string>;
24
+ timeoutSec?: number;
25
+ retryAttempts?: number;
26
+ backoffBaseSec?: number;
27
+ queueSize?: number;
28
+ failureMode?: 'warn' | 'raise' | 'ignore';
29
+ queueFullMode?: 'warn' | 'raise' | 'ignore';
30
+ });
31
+ start(): void;
32
+ send(payload: Record<string, any>): void;
33
+ flush(): void;
34
+ close(): void;
35
+ private processQueue;
36
+ private sendPayload;
37
+ private handleQueueFull;
38
+ private handleFailure;
39
+ }
40
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiBrF;AAED,qBAAa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,KAAK,CAAC,CAAiB;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAU;gBAEd,OAAO,EAAE;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;QAC1C,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;KAC7C;IAaD,KAAK,IAAI,IAAI;IASb,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAWxC,KAAK,IAAI,IAAI;IAIb,KAAK,IAAI,IAAI;YAQC,YAAY;YAqBZ,WAAW;IAmBzB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,aAAa;CAStB"}
package/dist/alerts.js ADDED
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * Violation alert dispatching.
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ViolationWebhookDispatcher = exports.AlertError = void 0;
10
+ exports.buildViolationPayload = buildViolationPayload;
11
+ const axios_1 = __importDefault(require("axios"));
12
+ class AlertError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'AlertError';
16
+ }
17
+ }
18
+ exports.AlertError = AlertError;
19
+ function buildViolationPayload(event) {
20
+ const body = event.body || {};
21
+ return {
22
+ event_type: 'policy_violation',
23
+ policy_event_type: event.event_type,
24
+ model: body.model,
25
+ policy_name: body.policy_name,
26
+ message: body.message,
27
+ timestamp: event.timestamp,
28
+ data_classification: event.data_classification,
29
+ rule_names: body.rule_names,
30
+ trace_id: event.trace_id,
31
+ span_id: event.span_id,
32
+ parent_span_id: event.parent_span_id,
33
+ service_name: event.service_name,
34
+ environment: event.environment,
35
+ };
36
+ }
37
+ class ViolationWebhookDispatcher {
38
+ constructor(options) {
39
+ this.endpoint = options.endpoint;
40
+ this.headers = options.headers || {};
41
+ this.timeoutSec = options.timeoutSec ?? 5.0;
42
+ this.retries = options.retryAttempts ?? 3;
43
+ this.backoffBaseSec = options.backoffBaseSec ?? 0.5;
44
+ this.queue = [];
45
+ this.queueSize = options.queueSize ?? 200;
46
+ this.failureMode = options.failureMode ?? 'warn';
47
+ this.queueFullMode = options.queueFullMode ?? 'warn';
48
+ this.inFlight = false;
49
+ }
50
+ start() {
51
+ if (this.timer) {
52
+ return;
53
+ }
54
+ this.timer = setInterval(() => {
55
+ void this.processQueue();
56
+ }, 500);
57
+ }
58
+ send(payload) {
59
+ if (this.fatalError && this.failureMode === 'raise') {
60
+ throw new AlertError('Violation webhook dispatcher failed');
61
+ }
62
+ if (this.queue.length >= this.queueSize) {
63
+ this.handleQueueFull();
64
+ return;
65
+ }
66
+ this.queue.push(payload);
67
+ }
68
+ flush() {
69
+ void this.processQueue(true);
70
+ }
71
+ close() {
72
+ if (this.timer) {
73
+ clearInterval(this.timer);
74
+ this.timer = undefined;
75
+ }
76
+ this.flush();
77
+ }
78
+ async processQueue(drain = false) {
79
+ if (this.inFlight) {
80
+ return;
81
+ }
82
+ this.inFlight = true;
83
+ try {
84
+ while (this.queue.length > 0) {
85
+ const payload = this.queue.shift();
86
+ if (!payload) {
87
+ break;
88
+ }
89
+ await this.sendPayload(payload);
90
+ if (!drain && this.queue.length === 0) {
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ finally {
96
+ this.inFlight = false;
97
+ }
98
+ }
99
+ async sendPayload(payload) {
100
+ for (let attempt = 0; attempt < this.retries; attempt += 1) {
101
+ try {
102
+ await axios_1.default.post(this.endpoint, payload, {
103
+ headers: this.headers,
104
+ timeout: this.timeoutSec * 1000,
105
+ });
106
+ return;
107
+ }
108
+ catch (error) {
109
+ if (attempt === this.retries - 1) {
110
+ this.handleFailure(error instanceof Error ? error : new Error(String(error)));
111
+ return;
112
+ }
113
+ const backoff = this.backoffBaseSec * Math.pow(2, attempt);
114
+ await delay(backoff * 1000);
115
+ }
116
+ }
117
+ }
118
+ handleQueueFull() {
119
+ const message = 'Monora violation webhook queue full; dropping alert';
120
+ if (this.queueFullMode === 'raise') {
121
+ throw new AlertError(message);
122
+ }
123
+ if (this.queueFullMode === 'warn') {
124
+ console.error(message);
125
+ }
126
+ }
127
+ handleFailure(error) {
128
+ if (this.failureMode === 'raise') {
129
+ this.fatalError = error;
130
+ return;
131
+ }
132
+ if (this.failureMode === 'warn') {
133
+ console.error(`Monora violation webhook failed: ${error}`);
134
+ }
135
+ }
136
+ }
137
+ exports.ViolationWebhookDispatcher = ViolationWebhookDispatcher;
138
+ function delay(ms) {
139
+ return new Promise((resolve) => setTimeout(resolve, ms));
140
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Signed attestation bundle helpers.
3
+ */
4
+ export declare class AttestationError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ export interface SignatureResult {
8
+ signature: string;
9
+ signature_type: string;
10
+ key_id?: string;
11
+ fingerprint?: string;
12
+ }
13
+ export declare function serializeReport(report: Record<string, any>): Buffer;
14
+ export declare function computeSha256(payload: Buffer): string;
15
+ export declare function signReportGpg(reportBytes: Buffer, options?: {
16
+ keyId?: string;
17
+ gpgHome?: string;
18
+ }): SignatureResult;
19
+ export declare function buildAttestationBundle(report: Record<string, any>, reportBytes: Buffer, signature: SignatureResult): Record<string, any>;
20
+ //# sourceMappingURL=attestation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAEnE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,eAAe,CAsDjB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,eAAe,GACzB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAcrB"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /**
3
+ * Signed attestation bundle helpers.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AttestationError = void 0;
40
+ exports.serializeReport = serializeReport;
41
+ exports.computeSha256 = computeSha256;
42
+ exports.signReportGpg = signReportGpg;
43
+ exports.buildAttestationBundle = buildAttestationBundle;
44
+ const crypto = __importStar(require("crypto"));
45
+ const fs = __importStar(require("fs"));
46
+ const os = __importStar(require("os"));
47
+ const path = __importStar(require("path"));
48
+ const child_process_1 = require("child_process");
49
+ class AttestationError extends Error {
50
+ constructor(message) {
51
+ super(message);
52
+ this.name = 'AttestationError';
53
+ }
54
+ }
55
+ exports.AttestationError = AttestationError;
56
+ function serializeReport(report) {
57
+ return Buffer.from(JSON.stringify(report, null, 2), 'utf-8');
58
+ }
59
+ function computeSha256(payload) {
60
+ const digest = crypto.createHash('sha256').update(payload).digest('hex');
61
+ return `sha256:${digest}`;
62
+ }
63
+ function signReportGpg(reportBytes, options) {
64
+ const gpg = findGpg();
65
+ if (!gpg) {
66
+ throw new AttestationError('gpg not found on PATH');
67
+ }
68
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'monora-sign-'));
69
+ const reportPath = path.join(tmpDir, 'report.json');
70
+ const sigPath = path.join(tmpDir, 'report.json.asc');
71
+ try {
72
+ fs.writeFileSync(reportPath, reportBytes);
73
+ const args = ['--armor', '--detach-sign', '--output', sigPath];
74
+ if (options?.gpgHome) {
75
+ args.push('--homedir', options.gpgHome);
76
+ }
77
+ if (options?.keyId) {
78
+ args.push('--local-user', options.keyId);
79
+ }
80
+ args.push(reportPath);
81
+ const result = (0, child_process_1.spawnSync)(gpg, args, { encoding: 'utf-8' });
82
+ if (result.status !== 0) {
83
+ throw new AttestationError(result.stderr || 'gpg signing failed');
84
+ }
85
+ const signature = fs.readFileSync(sigPath, 'utf-8');
86
+ const fingerprint = options?.keyId
87
+ ? getGpgFingerprint(gpg, options.keyId, options.gpgHome)
88
+ : undefined;
89
+ return {
90
+ signature,
91
+ signature_type: 'gpg',
92
+ key_id: options?.keyId,
93
+ fingerprint,
94
+ };
95
+ }
96
+ finally {
97
+ try {
98
+ fs.unlinkSync(sigPath);
99
+ }
100
+ catch {
101
+ // ignore cleanup errors
102
+ }
103
+ try {
104
+ fs.unlinkSync(reportPath);
105
+ }
106
+ catch {
107
+ // ignore cleanup errors
108
+ }
109
+ try {
110
+ fs.rmSync(tmpDir, { recursive: true, force: true });
111
+ }
112
+ catch {
113
+ // ignore cleanup errors
114
+ }
115
+ }
116
+ }
117
+ function buildAttestationBundle(report, reportBytes, signature) {
118
+ return {
119
+ bundle_version: '1.0.0',
120
+ generated_at: new Date().toISOString(),
121
+ report_sha256: computeSha256(reportBytes),
122
+ report_json: reportBytes.toString('utf-8'),
123
+ signature: {
124
+ type: signature.signature_type,
125
+ value: signature.signature,
126
+ key_id: signature.key_id,
127
+ fingerprint: signature.fingerprint,
128
+ signed_at: new Date().toISOString(),
129
+ },
130
+ };
131
+ }
132
+ function findGpg() {
133
+ const candidates = ['gpg', 'gpg2'];
134
+ for (const candidate of candidates) {
135
+ const result = (0, child_process_1.spawnSync)(candidate, ['--version'], { encoding: 'utf-8' });
136
+ if (result.status === 0) {
137
+ return candidate;
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ function getGpgFingerprint(gpg, keyId, gpgHome) {
143
+ const args = ['--with-colons', '--fingerprint', keyId];
144
+ if (gpgHome) {
145
+ args.unshift('--homedir', gpgHome);
146
+ }
147
+ const result = (0, child_process_1.spawnSync)(gpg, args, { encoding: 'utf-8' });
148
+ if (result.status !== 0) {
149
+ return undefined;
150
+ }
151
+ const lines = result.stdout.split(/\r?\n/);
152
+ for (const line of lines) {
153
+ if (!line.startsWith('fpr:')) {
154
+ continue;
155
+ }
156
+ const parts = line.split(':').filter(Boolean);
157
+ return parts[parts.length - 1];
158
+ }
159
+ return undefined;
160
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Configuration diagnostics and validation.
3
+ */
4
+ import { MonoraConfig } from '../config';
5
+ interface ValidationResult {
6
+ errors: string[];
7
+ warnings: string[];
8
+ }
9
+ export declare function validateConfig(config: MonoraConfig): ValidationResult;
10
+ export declare function doctorChecks(config: MonoraConfig, options?: {
11
+ checkNetwork?: boolean;
12
+ }): ValidationResult;
13
+ export declare function emitResults(results: ValidationResult, options?: {
14
+ json?: boolean;
15
+ }): number;
16
+ export {};
17
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/cli/diagnostics.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAc,YAAY,EAAE,MAAM,WAAW,CAAC;AAOrD,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAuIrE;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC,gBAAgB,CAqDlB;AA+CD,wBAAgB,WAAW,CACzB,OAAO,EAAE,gBAAgB,EACzB,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,MAAM,CA2BR"}