monora-ai 2.1.0 → 2.1.3
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 +333 -159
- package/dist/aims_governance.d.ts +238 -0
- package/dist/aims_governance.d.ts.map +1 -0
- package/dist/aims_governance.js +922 -0
- package/dist/alerts.d.ts +16 -0
- package/dist/alerts.d.ts.map +1 -1
- package/dist/alerts.js +16 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +6 -0
- package/dist/assessment.d.ts +85 -0
- package/dist/assessment.d.ts.map +1 -1
- package/dist/assessment.js +506 -13
- package/dist/attribution.d.ts +44 -3
- package/dist/attribution.d.ts.map +1 -1
- package/dist/attribution.js +197 -10
- package/dist/autodetect.d.ts +68 -0
- package/dist/autodetect.d.ts.map +1 -1
- package/dist/autodetect.js +639 -0
- package/dist/bias.d.ts +130 -0
- package/dist/bias.d.ts.map +1 -0
- package/dist/bias.js +223 -0
- package/dist/cli/diagnostics.d.ts +5 -1
- package/dist/cli/diagnostics.d.ts.map +1 -1
- package/dist/cli/diagnostics.js +23 -6
- package/dist/cli/doctor.d.ts +25 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +381 -0
- package/dist/cli/fix.d.ts +16 -0
- package/dist/cli/fix.d.ts.map +1 -0
- package/dist/cli/fix.js +284 -0
- package/dist/cli/init.d.ts +57 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +205 -0
- package/dist/cli.js +1550 -176
- package/dist/complianceTargets.d.ts +111 -0
- package/dist/complianceTargets.d.ts.map +1 -0
- package/dist/complianceTargets.js +521 -0
- package/dist/config.d.ts +261 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +381 -32
- package/dist/config_migrations.d.ts.map +1 -1
- package/dist/config_migrations.js +38 -1
- package/dist/config_schema.d.ts +2490 -1035
- package/dist/config_schema.d.ts.map +1 -1
- package/dist/config_schema.js +233 -64
- package/dist/context.d.ts +34 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +118 -7
- package/dist/control_backbone.d.ts +122 -0
- package/dist/control_backbone.d.ts.map +1 -0
- package/dist/control_backbone.js +698 -0
- package/dist/data-governance.d.ts +187 -0
- package/dist/data-governance.d.ts.map +1 -0
- package/dist/data-governance.js +424 -0
- package/dist/dataResidency.d.ts +44 -0
- package/dist/dataResidency.d.ts.map +1 -0
- package/dist/dataResidency.js +203 -0
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +17 -5
- package/dist/evidence_store.d.ts +103 -0
- package/dist/evidence_store.d.ts.map +1 -0
- package/dist/evidence_store.js +459 -0
- package/dist/executiveSummary.d.ts +15 -0
- package/dist/executiveSummary.d.ts.map +1 -1
- package/dist/executiveSummary.js +135 -22
- package/dist/identity.d.ts +143 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +231 -0
- package/dist/impact-assessment.d.ts +350 -0
- package/dist/impact-assessment.d.ts.map +1 -0
- package/dist/impact-assessment.js +580 -0
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +247 -5
- package/dist/instrumentation.d.ts +1 -1
- package/dist/instrumentation.d.ts.map +1 -1
- package/dist/instrumentation.js +123 -22
- package/dist/integrations/anthropic.d.ts +3 -0
- package/dist/integrations/anthropic.d.ts.map +1 -1
- package/dist/integrations/anthropic.js +282 -80
- package/dist/integrations/governance.d.ts +33 -0
- package/dist/integrations/governance.d.ts.map +1 -0
- package/dist/integrations/governance.js +208 -0
- package/dist/integrations/langchain.d.ts +4 -0
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +362 -142
- package/dist/integrations/openai.d.ts +9 -0
- package/dist/integrations/openai.d.ts.map +1 -1
- package/dist/integrations/openai.js +673 -73
- package/dist/iso42001_consolidation.d.ts +16 -0
- package/dist/iso42001_consolidation.d.ts.map +1 -0
- package/dist/iso42001_consolidation.js +413 -0
- package/dist/iso42001_workflows.d.ts +263 -0
- package/dist/iso42001_workflows.d.ts.map +1 -0
- package/dist/iso42001_workflows.js +781 -0
- package/dist/lifecycle.d.ts +299 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +624 -0
- package/dist/lineage.d.ts +2 -2
- package/dist/lineage.d.ts.map +1 -1
- package/dist/lineage.js +9 -16
- package/dist/middleware/express.d.ts.map +1 -1
- package/dist/middleware/express.js +18 -3
- package/dist/middleware/nextjs.js +2 -2
- package/dist/model.d.ts +143 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +371 -0
- package/dist/onboarding.d.ts +42 -0
- package/dist/onboarding.d.ts.map +1 -0
- package/dist/onboarding.js +1022 -0
- package/dist/oversight.d.ts +264 -0
- package/dist/oversight.d.ts.map +1 -0
- package/dist/oversight.js +497 -0
- package/dist/presets.js +7 -7
- package/dist/quotas.d.ts +171 -0
- package/dist/quotas.d.ts.map +1 -0
- package/dist/quotas.js +259 -0
- package/dist/register.d.ts +13 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +99 -0
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +7 -0
- package/dist/registryData.json +43 -6
- package/dist/report.d.ts +2 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +189 -2
- package/dist/reporting.d.ts +125 -0
- package/dist/reporting.d.ts.map +1 -1
- package/dist/reporting.js +192 -2
- package/dist/resources.d.ts +285 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +643 -0
- package/dist/risk.d.ts +120 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +220 -0
- package/dist/runtime.d.ts +73 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +415 -18
- package/dist/schemaInference.d.ts +92 -0
- package/dist/schemaInference.d.ts.map +1 -0
- package/dist/schemaInference.js +466 -0
- package/dist/schema_validation.js +2 -2
- package/dist/schemas/config.schema.json +118 -4
- package/dist/security_report.js +4 -4
- package/dist/signing.d.ts +1 -1
- package/dist/signing.d.ts.map +1 -1
- package/dist/signing.js +4 -0
- package/dist/sinks/file.d.ts +19 -1
- package/dist/sinks/file.d.ts.map +1 -1
- package/dist/sinks/file.js +82 -13
- package/dist/sinks/https.d.ts +10 -0
- package/dist/sinks/https.d.ts.map +1 -1
- package/dist/sinks/https.js +76 -16
- package/dist/sinks/stdout.d.ts +1 -0
- package/dist/sinks/stdout.d.ts.map +1 -1
- package/dist/sinks/stdout.js +12 -1
- package/dist/spec.d.ts +159 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +391 -0
- package/dist/stakeholders.d.ts +199 -0
- package/dist/stakeholders.d.ts.map +1 -0
- package/dist/stakeholders.js +398 -0
- package/dist/standards.d.ts.map +1 -1
- package/dist/standards.js +160 -2
- package/dist/standards_ingest.d.ts.map +1 -1
- package/dist/standards_ingest.js +1 -4
- package/dist/telemetry.d.ts +16 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +77 -14
- package/dist/templates/controls/iso42001_control_catalog.json +1443 -0
- package/dist/traced_emitter.d.ts.map +1 -1
- package/dist/traced_emitter.js +19 -9
- package/dist/trust_package.d.ts +19 -1
- package/dist/trust_package.d.ts.map +1 -1
- package/dist/trust_package.js +89 -2
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +9 -2
- package/dist/wal.d.ts.map +1 -1
- package/dist/wal.js +2 -1
- package/package.json +14 -1
- package/scripts/postinstall.js +105 -210
- package/templates/controls/iso42001_control_catalog.json +1443 -0
package/dist/cli.js
CHANGED
|
@@ -46,6 +46,7 @@ const readline = __importStar(require("readline"));
|
|
|
46
46
|
const yaml = __importStar(require("js-yaml"));
|
|
47
47
|
const axios_1 = __importDefault(require("axios"));
|
|
48
48
|
const config_1 = require("./config");
|
|
49
|
+
const init_1 = require("./cli/init");
|
|
49
50
|
const autodetect_1 = require("./autodetect");
|
|
50
51
|
const report_1 = require("./report");
|
|
51
52
|
const ai_act_report_1 = require("./ai_act_report");
|
|
@@ -55,29 +56,50 @@ const trust_package_1 = require("./trust_package");
|
|
|
55
56
|
const schema_validation_1 = require("./schema_validation");
|
|
56
57
|
const verify_1 = require("./verify");
|
|
57
58
|
const signing_1 = require("./signing");
|
|
59
|
+
const aims_governance_1 = require("./aims_governance");
|
|
60
|
+
const control_backbone_1 = require("./control_backbone");
|
|
58
61
|
const diagnostics_1 = require("./cli/diagnostics");
|
|
62
|
+
const doctor_1 = require("./cli/doctor");
|
|
63
|
+
const fix_1 = require("./cli/fix");
|
|
59
64
|
const compat_1 = require("./compat");
|
|
60
65
|
const standards_1 = require("./standards");
|
|
61
66
|
const standards_ingest_1 = require("./standards_ingest");
|
|
67
|
+
const runtime_1 = require("./runtime");
|
|
68
|
+
const onboarding_1 = require("./onboarding");
|
|
69
|
+
const schemaInference_1 = require("./schemaInference");
|
|
70
|
+
const model_1 = require("./model");
|
|
71
|
+
const spec_1 = require("./spec");
|
|
72
|
+
const complianceTargets_1 = require("./complianceTargets");
|
|
62
73
|
function usage() {
|
|
63
|
-
console.log('Usage:');
|
|
64
|
-
console.log('
|
|
65
|
-
console.log('
|
|
66
|
-
console.log('
|
|
67
|
-
console.log('
|
|
68
|
-
console.log('
|
|
69
|
-
console.log('
|
|
70
|
-
console.log('
|
|
71
|
-
console.log('
|
|
72
|
-
console.log('
|
|
73
|
-
console.log('
|
|
74
|
-
console.log('
|
|
75
|
-
console.log('
|
|
76
|
-
console.log('
|
|
77
|
-
console.log('
|
|
78
|
-
console.log('
|
|
79
|
-
console.log('
|
|
80
|
-
console.log('
|
|
74
|
+
console.log('Usage: npx monora-ai <command> [options]\n');
|
|
75
|
+
console.log('Commands:');
|
|
76
|
+
console.log(' init [--path monora.yml] [--format yaml|json] [--yes] [--force] [--advanced] [--preset minimal|dev|production]');
|
|
77
|
+
console.log(' start [-- <command>] Zero-code instrumentation');
|
|
78
|
+
console.log(' validate [--config monora.yml] [--json] [--mode strict|lenient|warn-only]');
|
|
79
|
+
console.log(' doctor [--config monora.yml] [--json] [--no-network]');
|
|
80
|
+
console.log(' config fix [--config monora.yml] [--dry-run] [--no-network]');
|
|
81
|
+
console.log(' report --input <events.jsonl> --output <report.json> [--format json|markdown] [--config monora.yml] [--no-schema] [--no-verify] [--no-signature-verify]');
|
|
82
|
+
console.log(' usage-report --input <events.jsonl> --output <usage.json> [--format json|markdown] [--no-schema]');
|
|
83
|
+
console.log(' security-review --input <events.jsonl> --output <security.json> [--config monora.yml] [--no-schema] [--sign gpg --gpg-key <id> --bundle <bundle.json>]');
|
|
84
|
+
console.log(' trust-package --input <events.jsonl> --trace-id <trace_id> --output <trust.json> [--config monora.yml] [options...]');
|
|
85
|
+
console.log(' aims <subcommand> [--state <aims_state.json>]');
|
|
86
|
+
console.log(' ai-act-report --input <events.jsonl> --output <report.json> [--format json|markdown] [--config monora.yml] [--no-schema]');
|
|
87
|
+
console.log(' verify --input <events.jsonl> [--config monora.yml] [--no-schema] [--no-verify] [--no-signature-verify] [--no-sequence] [--pretty]');
|
|
88
|
+
console.log(' retry-queue [--config monora.yml] [--path ./monora_http_queue] [--json] [--clear]');
|
|
89
|
+
console.log(' compat-report [--baseline <manifest.json>] [--output <report.json>] [--pretty]');
|
|
90
|
+
console.log(' standards-wizard [--standard SOC2] [--template <claims.json>] [--output <claims.json>] [--input <events.jsonl>]');
|
|
91
|
+
console.log(' onboard <init|validate|complete|status> [--config monora.yml] [options]');
|
|
92
|
+
console.log(' schema infer --input <events.jsonl> --output <spec.json> [--model-name <name>] [--model-version <ver>] [--compliance-target <framework>]... [--sample-size <n>] [--no-pii] [--report <inference_report.json>] [--contract <schema_contract.json>]');
|
|
93
|
+
console.log(' model create --input <events.jsonl> --output <model.json> [--model-name <name>] [--model-version <ver>] [--compliance-target <framework>]... [--risk-category minimal|limited|high|unacceptable] [--intended-use <text>] [--owner <id>] [--tag <value>]... [--sample-size <n>] [--no-pii] [--config-out <monora.yml|json>] [--config-format yaml|json] [--contract-out <schema_contract.json>]');
|
|
94
|
+
console.log(' production-check [--config monora.yml] [--json] [--fix]');
|
|
95
|
+
console.log(' detect-env [--json]');
|
|
96
|
+
console.log(' upgrade-preset --from <preset> --to <preset> [--output monora.yml] [--json]');
|
|
97
|
+
console.log('\nExamples:');
|
|
98
|
+
console.log(' npx monora-ai init # Quick setup wizard');
|
|
99
|
+
console.log(' npx monora-ai init --preset minimal # Minimal config preset');
|
|
100
|
+
console.log(' npx monora-ai onboard init # Initialize onboarding contract');
|
|
101
|
+
console.log(' npx monora-ai start -- npm run dev # Zero-code instrumentation');
|
|
102
|
+
console.log(' npx monora-ai doctor # Diagnose config issues');
|
|
81
103
|
}
|
|
82
104
|
function parseArgs(argv) {
|
|
83
105
|
const args = argv.slice(2);
|
|
@@ -96,6 +118,21 @@ function getFlagValue(args, flag) {
|
|
|
96
118
|
function hasFlag(args, flag) {
|
|
97
119
|
return args.includes(flag);
|
|
98
120
|
}
|
|
121
|
+
function getFlagValues(args, flag) {
|
|
122
|
+
const values = [];
|
|
123
|
+
for (let i = 0; i < args.length; i++) {
|
|
124
|
+
if (args[i] !== flag) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const next = args[i + 1];
|
|
128
|
+
if (!next || next.startsWith('--')) {
|
|
129
|
+
throw new Error(`Missing value for ${flag}`);
|
|
130
|
+
}
|
|
131
|
+
values.push(next);
|
|
132
|
+
i += 1;
|
|
133
|
+
}
|
|
134
|
+
return values;
|
|
135
|
+
}
|
|
99
136
|
function createInterface() {
|
|
100
137
|
return readline.createInterface({
|
|
101
138
|
input: process.stdin,
|
|
@@ -130,6 +167,108 @@ function parseList(raw) {
|
|
|
130
167
|
.map((item) => item.trim())
|
|
131
168
|
.filter((item) => item.length > 0);
|
|
132
169
|
}
|
|
170
|
+
function parseOptionalList(raw) {
|
|
171
|
+
if (!raw) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
return parseList(raw.replace(/;/g, ','));
|
|
175
|
+
}
|
|
176
|
+
function parseJsonObject(raw) {
|
|
177
|
+
if (!raw) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
let payload;
|
|
181
|
+
try {
|
|
182
|
+
payload = JSON.parse(raw);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
throw new Error(`Invalid metadata JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
186
|
+
}
|
|
187
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
188
|
+
throw new Error('Metadata JSON must be an object');
|
|
189
|
+
}
|
|
190
|
+
return payload;
|
|
191
|
+
}
|
|
192
|
+
function normalizeInitPreset(value) {
|
|
193
|
+
if (!value) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
const normalized = value.toLowerCase();
|
|
197
|
+
if (normalized === 'development') {
|
|
198
|
+
return 'dev';
|
|
199
|
+
}
|
|
200
|
+
if (normalized === 'dev' || normalized === 'minimal' || normalized === 'production') {
|
|
201
|
+
return normalized;
|
|
202
|
+
}
|
|
203
|
+
throw new Error(`Invalid preset '${value}'. Valid presets: minimal, dev, production`);
|
|
204
|
+
}
|
|
205
|
+
function normalizeValidationMode(value) {
|
|
206
|
+
if (!value) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
const normalized = value.toLowerCase();
|
|
210
|
+
if (normalized === 'strict')
|
|
211
|
+
return 'strict';
|
|
212
|
+
if (normalized === 'lenient')
|
|
213
|
+
return 'lenient';
|
|
214
|
+
if (normalized === 'warn-only' || normalized === 'warn' || normalized === 'warn_only') {
|
|
215
|
+
return 'warn-only';
|
|
216
|
+
}
|
|
217
|
+
throw new Error(`Invalid validation mode '${value}'. Valid modes: strict, lenient, warn-only`);
|
|
218
|
+
}
|
|
219
|
+
const ROTATION_COMMENT = [
|
|
220
|
+
'# File sink rotation options:',
|
|
221
|
+
'# - rotation: none (default), daily, size',
|
|
222
|
+
'# - When rotation is enabled, set symlink: true to keep stable paths:',
|
|
223
|
+
'# - <path> (e.g., monora_events.jsonl)',
|
|
224
|
+
'# - <path>.latest (e.g., monora_events.latest.jsonl)',
|
|
225
|
+
'#',
|
|
226
|
+
].join('\n');
|
|
227
|
+
function renderConfigOutput(config, format) {
|
|
228
|
+
if (format === 'json') {
|
|
229
|
+
return JSON.stringify(config, null, 2);
|
|
230
|
+
}
|
|
231
|
+
const body = yaml.dump(config, { sortKeys: false, noRefs: true });
|
|
232
|
+
return `${ROTATION_COMMENT}\n${body}`;
|
|
233
|
+
}
|
|
234
|
+
async function askSinkSelection(rl, defaults) {
|
|
235
|
+
console.log('? Which sinks do you want to enable? (Use space to select)');
|
|
236
|
+
const markers = {
|
|
237
|
+
file: defaults.includes('file') ? '◉' : '◯',
|
|
238
|
+
stdout: defaults.includes('stdout') ? '◉' : '◯',
|
|
239
|
+
https: defaults.includes('https') ? '◉' : '◯',
|
|
240
|
+
};
|
|
241
|
+
console.log(` ${markers.file} File (monora_events.jsonl)`);
|
|
242
|
+
console.log(` ${markers.stdout} Stdout (pretty to console)`);
|
|
243
|
+
console.log(` ${markers.https} HTTPS (requires endpoint URL)`);
|
|
244
|
+
const raw = await ask(rl, 'Selections (file stdout https)', defaults.join(' '));
|
|
245
|
+
const normalized = raw
|
|
246
|
+
.split(/[\\s,]+/)
|
|
247
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
248
|
+
.filter((entry) => entry.length > 0);
|
|
249
|
+
const selected = new Set();
|
|
250
|
+
const add = (value) => {
|
|
251
|
+
selected.add(value);
|
|
252
|
+
};
|
|
253
|
+
for (const token of normalized) {
|
|
254
|
+
if (token === '1' || token.startsWith('f')) {
|
|
255
|
+
add('file');
|
|
256
|
+
}
|
|
257
|
+
else if (token === '2' || token.startsWith('s')) {
|
|
258
|
+
add('stdout');
|
|
259
|
+
}
|
|
260
|
+
else if (token === '3' || token.startsWith('h')) {
|
|
261
|
+
add('https');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (selected.size === 0) {
|
|
265
|
+
defaults.forEach((item) => add(item));
|
|
266
|
+
}
|
|
267
|
+
if (selected.size === 0) {
|
|
268
|
+
add('file');
|
|
269
|
+
}
|
|
270
|
+
return Array.from(selected.values());
|
|
271
|
+
}
|
|
133
272
|
const ENV_PATTERN = /\$\{([^}]+)\}/g;
|
|
134
273
|
function isValidEndpoint(endpoint) {
|
|
135
274
|
try {
|
|
@@ -220,135 +359,228 @@ async function testHttpEndpoint(endpoint, headers, label) {
|
|
|
220
359
|
console.warn(`Warning: test POST to ${label} failed: ${message}`);
|
|
221
360
|
}
|
|
222
361
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
});
|
|
362
|
+
/**
|
|
363
|
+
* Streamlined 3-question wizard for quick setup.
|
|
364
|
+
* Uses AI-powered detection for smart defaults.
|
|
365
|
+
*/
|
|
366
|
+
async function runStreamlinedWizard(configPath, format, assumeYes) {
|
|
367
|
+
console.log('\n\x1b[36m=== Monora Setup ===\x1b[0m\n');
|
|
368
|
+
console.log('Analyzing your project...\n');
|
|
369
|
+
// Run comprehensive detection
|
|
370
|
+
const detection = (0, autodetect_1.detectFramework)();
|
|
371
|
+
const serviceName = (0, autodetect_1.detectServiceName)() || 'monora-app';
|
|
372
|
+
const env = (0, autodetect_1.detectEnvironment)();
|
|
373
|
+
// Display detection results
|
|
374
|
+
console.log('\x1b[32mDetected:\x1b[0m');
|
|
375
|
+
console.log(` Service: ${serviceName}`);
|
|
376
|
+
if (detection.framework) {
|
|
377
|
+
console.log(` Framework: ${detection.framework}`);
|
|
378
|
+
}
|
|
379
|
+
console.log(` Environment: ${env}`);
|
|
380
|
+
if (detection.aiSdks.length > 0) {
|
|
381
|
+
console.log(` AI SDKs: ${detection.aiSdks.map(s => s.name).join(', ')}`);
|
|
382
|
+
}
|
|
383
|
+
if (detection.cloudPlatform) {
|
|
384
|
+
console.log(` Platform: ${detection.cloudPlatform}`);
|
|
385
|
+
}
|
|
386
|
+
if (detection.typescript) {
|
|
387
|
+
console.log(` TypeScript: yes`);
|
|
243
388
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
389
|
+
console.log('');
|
|
390
|
+
// Run AI analysis if API key available
|
|
391
|
+
let aiAnalysis = null;
|
|
392
|
+
const hasAIKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
393
|
+
const aiAnalysisEnabled = ['true', '1', 'yes'].includes(String(process.env.MONORA_AI_ANALYSIS_ENABLED || '').toLowerCase());
|
|
394
|
+
if (hasAIKey && aiAnalysisEnabled) {
|
|
395
|
+
console.log('Running AI-powered analysis...');
|
|
396
|
+
aiAnalysis = await (0, autodetect_1.analyzeProjectWithAI)();
|
|
397
|
+
if (aiAnalysis) {
|
|
398
|
+
console.log(`\x1b[33mAI suggests:\x1b[0m ${aiAnalysis.reasoning}`);
|
|
399
|
+
if (aiAnalysis.complianceHints.length > 0) {
|
|
400
|
+
console.log(` Compliance: ${aiAnalysis.complianceHints.join(', ')}`);
|
|
401
|
+
}
|
|
248
402
|
}
|
|
249
|
-
|
|
250
|
-
type: 'https',
|
|
251
|
-
endpoint: answers.https_endpoint,
|
|
252
|
-
headers,
|
|
253
|
-
batch_size: 50,
|
|
254
|
-
timeout_sec: 10.0,
|
|
255
|
-
retry_attempts: 3,
|
|
256
|
-
backoff_base_sec: 0.5,
|
|
257
|
-
});
|
|
403
|
+
console.log('');
|
|
258
404
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
...config.policies,
|
|
262
|
-
model_allowlist: answers.allowlist || [],
|
|
263
|
-
model_denylist: answers.denylist || [],
|
|
264
|
-
enforce: true,
|
|
265
|
-
};
|
|
266
|
-
config.instrumentation = {
|
|
267
|
-
...config.instrumentation,
|
|
268
|
-
enabled: answers.enable_instrumentation,
|
|
269
|
-
default_purpose: answers.instrumentation_purpose,
|
|
270
|
-
};
|
|
271
|
-
config.data_handling = {
|
|
272
|
-
...config.data_handling,
|
|
273
|
-
enabled: answers.enable_data_handling,
|
|
274
|
-
mode: (answers.data_handling_mode || 'redact'),
|
|
275
|
-
rules: answers.enable_data_handling
|
|
276
|
-
? [
|
|
277
|
-
{
|
|
278
|
-
name: 'email',
|
|
279
|
-
pattern: '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}',
|
|
280
|
-
replace: '[REDACTED_EMAIL]',
|
|
281
|
-
classifications: ['confidential', 'secret'],
|
|
282
|
-
apply_to: ['request', 'response'],
|
|
283
|
-
},
|
|
284
|
-
]
|
|
285
|
-
: [],
|
|
286
|
-
};
|
|
287
|
-
config.alerts = {
|
|
288
|
-
...config.alerts,
|
|
289
|
-
violation_webhook: answers.violation_webhook || undefined,
|
|
290
|
-
headers: answers.alerts_auth_header ? { Authorization: answers.alerts_auth_header } : {},
|
|
291
|
-
};
|
|
292
|
-
config.error_handling = {
|
|
293
|
-
...config.error_handling,
|
|
294
|
-
queue_full_mode: answers.queue_full_mode,
|
|
295
|
-
};
|
|
296
|
-
config.buffering = {
|
|
297
|
-
...config.buffering,
|
|
298
|
-
queue_full_timeout_sec: answers.queue_full_timeout_sec === undefined ? config.buffering?.queue_full_timeout_sec : answers.queue_full_timeout_sec,
|
|
299
|
-
};
|
|
300
|
-
config.attribution = {
|
|
301
|
-
...config.attribution,
|
|
302
|
-
enabled: Boolean(answers.enable_attribution),
|
|
303
|
-
};
|
|
304
|
-
const project = {
|
|
305
|
-
company: answers.attribution_company || undefined,
|
|
306
|
-
role: answers.attribution_role || undefined,
|
|
307
|
-
email: answers.attribution_email || undefined,
|
|
308
|
-
source: answers.attribution_source || undefined,
|
|
309
|
-
use_case: answers.attribution_use_case || undefined,
|
|
310
|
-
team_size: answers.attribution_team_size || undefined,
|
|
311
|
-
business_owner: answers.attribution_business_owner || undefined,
|
|
312
|
-
data_categories: answers.attribution_data_categories || [],
|
|
313
|
-
};
|
|
314
|
-
const hasProject = Object.values(project).some((value) => {
|
|
315
|
-
if (value === undefined || value === null)
|
|
316
|
-
return false;
|
|
317
|
-
if (Array.isArray(value))
|
|
318
|
-
return value.length > 0;
|
|
319
|
-
return String(value).trim().length > 0;
|
|
320
|
-
});
|
|
321
|
-
if (hasProject) {
|
|
322
|
-
config.attribution.project = project;
|
|
323
|
-
}
|
|
324
|
-
if (answers.telemetry_enabled || answers.telemetry_send_data) {
|
|
325
|
-
config.attribution.telemetry = {
|
|
326
|
-
enabled: Boolean(answers.telemetry_enabled),
|
|
327
|
-
send_data: Boolean(answers.telemetry_send_data),
|
|
328
|
-
data_residency: answers.telemetry_data_residency || undefined,
|
|
329
|
-
};
|
|
405
|
+
else if (hasAIKey && !aiAnalysisEnabled) {
|
|
406
|
+
console.log('AI analysis is disabled. Set MONORA_AI_ANALYSIS_ENABLED=true to enable it.\n');
|
|
330
407
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
408
|
+
// Collect 3 required questions
|
|
409
|
+
let email = '';
|
|
410
|
+
let company = '';
|
|
411
|
+
let role = '';
|
|
412
|
+
if (assumeYes) {
|
|
413
|
+
// Use environment variables or empty
|
|
414
|
+
email = process.env.MONORA_EMAIL || '';
|
|
415
|
+
company = process.env.MONORA_COMPANY || '';
|
|
416
|
+
role = process.env.MONORA_ROLE || '';
|
|
417
|
+
console.log('Using environment variables for attribution...');
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
const rl = createInterface();
|
|
421
|
+
try {
|
|
422
|
+
console.log('\x1b[36mQuick setup (3 questions):\x1b[0m');
|
|
423
|
+
email = (await ask(rl, ' Work email (for updates)', '')).trim();
|
|
424
|
+
company = (await ask(rl, ' Company name', '')).trim();
|
|
425
|
+
role = (await ask(rl, ' Your role', '')).trim();
|
|
426
|
+
}
|
|
427
|
+
finally {
|
|
428
|
+
rl.close();
|
|
429
|
+
}
|
|
342
430
|
}
|
|
343
|
-
|
|
431
|
+
console.log('');
|
|
432
|
+
// Build smart configuration
|
|
433
|
+
const config = (0, autodetect_1.buildSmartConfig)(detection, aiAnalysis, { email, company, role });
|
|
434
|
+
// Write configuration
|
|
435
|
+
const output = renderConfigOutput(config, format);
|
|
436
|
+
fs.writeFileSync(configPath, output, 'utf-8');
|
|
437
|
+
console.log(`\x1b[32mConfiguration saved to ${configPath}\x1b[0m\n`);
|
|
438
|
+
// Clear next steps
|
|
439
|
+
console.log('\x1b[36m=== Next Steps ===\x1b[0m\n');
|
|
440
|
+
console.log('To start tracing with zero code changes:');
|
|
441
|
+
console.log('\x1b[33m npx monora-ai start\x1b[0m\n');
|
|
442
|
+
console.log('Or wrap your existing command:');
|
|
443
|
+
console.log('\x1b[33m npx monora-ai start -- npm run dev\x1b[0m\n');
|
|
444
|
+
console.log('Or add to package.json scripts:');
|
|
445
|
+
console.log(' "scripts": {');
|
|
446
|
+
console.log(' "dev": "npx monora-ai start -- node server.js"');
|
|
447
|
+
console.log(' }\n');
|
|
448
|
+
if (detection.aiSdks.length > 0) {
|
|
449
|
+
console.log(`Auto-instrumentation enabled for: ${detection.aiSdks.map(s => s.name).join(', ')}`);
|
|
450
|
+
}
|
|
451
|
+
console.log('\nFor advanced configuration, run: \x1b[33mnpx monora-ai init --advanced\x1b[0m');
|
|
344
452
|
}
|
|
345
|
-
|
|
453
|
+
/**
|
|
454
|
+
* Run a command with Monora instrumentation enabled.
|
|
455
|
+
* Zero-code instrumentation using NODE_OPTIONS.
|
|
456
|
+
*/
|
|
457
|
+
async function runStart(args) {
|
|
458
|
+
const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
459
|
+
// Find the separator '--' for the user command
|
|
460
|
+
const separatorIndex = args.indexOf('--');
|
|
461
|
+
let userCommand;
|
|
462
|
+
if (separatorIndex >= 0) {
|
|
463
|
+
userCommand = args.slice(separatorIndex + 1);
|
|
464
|
+
}
|
|
465
|
+
else if (args.length > 0) {
|
|
466
|
+
userCommand = args;
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
// No command provided - try to find package.json start script
|
|
470
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
471
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
472
|
+
try {
|
|
473
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
474
|
+
if (pkg.scripts?.start) {
|
|
475
|
+
console.log('\x1b[36mNo command specified, running npm start...\x1b[0m\n');
|
|
476
|
+
userCommand = ['npm', 'start'];
|
|
477
|
+
}
|
|
478
|
+
else if (pkg.scripts?.dev) {
|
|
479
|
+
console.log('\x1b[36mNo command specified, running npm run dev...\x1b[0m\n');
|
|
480
|
+
userCommand = ['npm', 'run', 'dev'];
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
console.error('Error: No command specified and no start/dev script found in package.json');
|
|
484
|
+
console.log('\nUsage: npx monora-ai start -- <command>');
|
|
485
|
+
console.log(' npx monora-ai start -- npm run dev');
|
|
486
|
+
console.log(' npx monora-ai start -- node server.js');
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
console.error('Error: No command specified');
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
console.error('Error: No command specified');
|
|
497
|
+
console.log('\nUsage: npx monora-ai start -- <command>');
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Find the register module path
|
|
502
|
+
let registerPath;
|
|
503
|
+
try {
|
|
504
|
+
// Try to find monora-ai/register
|
|
505
|
+
registerPath = require.resolve('monora-ai/register');
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// Fall back to local path for development
|
|
509
|
+
registerPath = path.resolve(__dirname, 'register.js');
|
|
510
|
+
if (!fs.existsSync(registerPath)) {
|
|
511
|
+
// Try TypeScript version
|
|
512
|
+
registerPath = path.resolve(__dirname, 'register.ts');
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// Check for config file
|
|
516
|
+
const configPath = fs.existsSync('monora.yml')
|
|
517
|
+
? 'monora.yml'
|
|
518
|
+
: fs.existsSync('monora.json')
|
|
519
|
+
? 'monora.json'
|
|
520
|
+
: null;
|
|
521
|
+
// Build environment
|
|
522
|
+
const env = {
|
|
523
|
+
...process.env,
|
|
524
|
+
MONORA_AUTO_INIT: 'true',
|
|
525
|
+
};
|
|
526
|
+
if (configPath) {
|
|
527
|
+
env.MONORA_CONFIG_PATH = configPath;
|
|
528
|
+
}
|
|
529
|
+
// Add register to NODE_OPTIONS
|
|
530
|
+
const existingNodeOptions = process.env.NODE_OPTIONS || '';
|
|
531
|
+
const escapedRegisterPath = registerPath.replace(/"/g, '\\"');
|
|
532
|
+
env.NODE_OPTIONS = `${existingNodeOptions} --require "${escapedRegisterPath}"`.trim();
|
|
533
|
+
console.log('\x1b[36m=== Monora Start ===\x1b[0m\n');
|
|
534
|
+
console.log('Instrumentation: \x1b[32menabled\x1b[0m');
|
|
535
|
+
if (configPath) {
|
|
536
|
+
console.log(`Config: ${configPath}`);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
console.log('Config: auto-detected');
|
|
540
|
+
}
|
|
541
|
+
console.log(`Running: ${userCommand.join(' ')}\n`);
|
|
542
|
+
console.log('\x1b[90m--- Application Output ---\x1b[0m\n');
|
|
543
|
+
// Spawn the user command
|
|
544
|
+
const child = spawn(userCommand[0], userCommand.slice(1), {
|
|
545
|
+
env,
|
|
546
|
+
stdio: 'inherit',
|
|
547
|
+
shell: true,
|
|
548
|
+
});
|
|
549
|
+
child.on('exit', (code) => {
|
|
550
|
+
process.exit(code ?? 0);
|
|
551
|
+
});
|
|
552
|
+
child.on('error', (err) => {
|
|
553
|
+
console.error(`Failed to start command: ${err.message}`);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
async function runInitWizard(configPath, format, assumeYes, force, advanced = false, preset) {
|
|
346
558
|
if (fs.existsSync(configPath) && !force) {
|
|
347
559
|
if (assumeYes) {
|
|
348
560
|
console.error(`${configPath} already exists. Use --force to overwrite.`);
|
|
349
561
|
process.exit(1);
|
|
350
562
|
}
|
|
351
563
|
}
|
|
564
|
+
if (preset) {
|
|
565
|
+
const detectedService = (0, autodetect_1.detectServiceName)() || path.basename(process.cwd()) || 'monora-app';
|
|
566
|
+
const config = (0, init_1.buildPresetConfig)(preset, { serviceName: detectedService });
|
|
567
|
+
const output = renderConfigOutput(config, format);
|
|
568
|
+
fs.writeFileSync(configPath, output, 'utf-8');
|
|
569
|
+
console.log(`\nMonora config written to ${configPath}`);
|
|
570
|
+
console.log(`Preset: ${preset}`);
|
|
571
|
+
console.log('\n=== Next Steps ===\n');
|
|
572
|
+
console.log('Use the generated config file:');
|
|
573
|
+
console.log(` import { init } from 'monora-ai';`);
|
|
574
|
+
console.log(` init({ configPath: '${configPath}' });`);
|
|
575
|
+
console.log('');
|
|
576
|
+
console.log('Generate reports with:');
|
|
577
|
+
console.log(' npx monora-ai report --input ./monora_events.jsonl --output monora_report.json');
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
// Use streamlined wizard by default (unless --advanced)
|
|
581
|
+
if (!advanced) {
|
|
582
|
+
return runStreamlinedWizard(configPath, format, assumeYes);
|
|
583
|
+
}
|
|
352
584
|
// Auto-detect project info
|
|
353
585
|
const detectedService = (0, autodetect_1.detectServiceName)() || path.basename(process.cwd()) || 'monora-app';
|
|
354
586
|
const detectedEnv = (0, autodetect_1.detectEnvironment)();
|
|
@@ -366,26 +598,22 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
366
598
|
}
|
|
367
599
|
console.log('');
|
|
368
600
|
// Generate smart defaults based on detection
|
|
369
|
-
const smartAllowlist =
|
|
370
|
-
? ['gpt-4*', 'gpt-4o*', 'claude-3-*']
|
|
371
|
-
: detectedSdks.includes('openai')
|
|
372
|
-
? ['gpt-4*', 'gpt-4o*', 'o1-*']
|
|
373
|
-
: detectedSdks.includes('anthropic')
|
|
374
|
-
? ['claude-3-*', 'claude-sonnet-4-*', 'claude-opus-4-*']
|
|
375
|
-
: [];
|
|
601
|
+
const smartAllowlist = [...init_1.DEFAULT_ALLOWLIST_PATTERNS];
|
|
376
602
|
let answers;
|
|
377
603
|
if (assumeYes) {
|
|
378
604
|
// Smart defaults for --yes mode
|
|
379
605
|
answers = {
|
|
380
606
|
service_name: detectedService,
|
|
381
607
|
environment: detectedEnv,
|
|
382
|
-
stdout_sink:
|
|
608
|
+
stdout_sink: false,
|
|
609
|
+
stdout_format: 'pretty',
|
|
383
610
|
file_sink: true,
|
|
384
611
|
file_path: detectedEnv === 'production'
|
|
385
612
|
? './logs/monora_events.jsonl'
|
|
386
613
|
: detectedEnv === 'poc'
|
|
387
614
|
? './monora_poc_events.jsonl'
|
|
388
615
|
: './monora_events.jsonl',
|
|
616
|
+
file_rotation: 'none',
|
|
389
617
|
https_sink: false,
|
|
390
618
|
enable_policies: smartAllowlist.length > 0,
|
|
391
619
|
allowlist: smartAllowlist,
|
|
@@ -394,30 +622,30 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
394
622
|
instrumentation_purpose: 'general',
|
|
395
623
|
enable_data_handling: false,
|
|
396
624
|
data_handling_mode: 'redact',
|
|
397
|
-
violation_webhook:
|
|
625
|
+
violation_webhook: undefined,
|
|
398
626
|
queue_full_mode: 'warn',
|
|
399
|
-
queue_full_timeout_sec:
|
|
627
|
+
queue_full_timeout_sec: undefined,
|
|
400
628
|
enable_attribution: false,
|
|
401
|
-
attribution_company:
|
|
402
|
-
attribution_role:
|
|
403
|
-
attribution_email:
|
|
404
|
-
attribution_source:
|
|
405
|
-
attribution_use_case:
|
|
406
|
-
attribution_team_size:
|
|
407
|
-
attribution_business_owner:
|
|
629
|
+
attribution_company: undefined,
|
|
630
|
+
attribution_role: undefined,
|
|
631
|
+
attribution_email: undefined,
|
|
632
|
+
attribution_source: undefined,
|
|
633
|
+
attribution_use_case: undefined,
|
|
634
|
+
attribution_team_size: undefined,
|
|
635
|
+
attribution_business_owner: undefined,
|
|
408
636
|
attribution_data_categories: [],
|
|
409
637
|
telemetry_enabled: false,
|
|
410
638
|
telemetry_send_data: false,
|
|
411
|
-
telemetry_data_residency:
|
|
639
|
+
telemetry_data_residency: undefined,
|
|
412
640
|
enable_audit_metadata: false,
|
|
413
|
-
audit_use_case_name:
|
|
414
|
-
audit_business_owner:
|
|
641
|
+
audit_use_case_name: undefined,
|
|
642
|
+
audit_business_owner: undefined,
|
|
415
643
|
audit_data_categories: [],
|
|
416
644
|
audit_risk_level: 'medium',
|
|
417
645
|
audit_compliance_frameworks: [],
|
|
418
|
-
audit_review_date:
|
|
419
|
-
audit_reviewer:
|
|
420
|
-
audit_notes:
|
|
646
|
+
audit_review_date: undefined,
|
|
647
|
+
audit_reviewer: undefined,
|
|
648
|
+
audit_notes: undefined,
|
|
421
649
|
};
|
|
422
650
|
console.log('Using smart defaults based on auto-detection...\n');
|
|
423
651
|
if (answers.file_sink && answers.file_path) {
|
|
@@ -431,12 +659,19 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
431
659
|
else {
|
|
432
660
|
const rl = createInterface();
|
|
433
661
|
try {
|
|
662
|
+
const selectedSinks = await askSinkSelection(rl, ['file']);
|
|
663
|
+
let stdoutSelected = selectedSinks.includes('stdout');
|
|
664
|
+
if (!stdoutSelected) {
|
|
665
|
+
stdoutSelected = await confirm(rl, 'Enable console output?', false);
|
|
666
|
+
}
|
|
434
667
|
answers = {
|
|
435
668
|
service_name: await ask(rl, 'Service name', detectedService),
|
|
436
669
|
environment: await ask(rl, 'Environment (dev/poc/staging/production)', detectedEnv),
|
|
437
|
-
stdout_sink:
|
|
438
|
-
|
|
439
|
-
|
|
670
|
+
stdout_sink: stdoutSelected,
|
|
671
|
+
stdout_format: 'pretty',
|
|
672
|
+
file_sink: selectedSinks.includes('file'),
|
|
673
|
+
file_rotation: 'none',
|
|
674
|
+
https_sink: selectedSinks.includes('https'),
|
|
440
675
|
enable_policies: await confirm(rl, 'Configure model allowlist?', smartAllowlist.length > 0),
|
|
441
676
|
allowlist: [],
|
|
442
677
|
denylist: [],
|
|
@@ -489,7 +724,7 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
489
724
|
if (answers.enable_policies) {
|
|
490
725
|
const defaultAllowlist = smartAllowlist.length > 0
|
|
491
726
|
? smartAllowlist.join(',')
|
|
492
|
-
: '
|
|
727
|
+
: init_1.DEFAULT_ALLOWLIST_PATTERNS.join(',');
|
|
493
728
|
answers.allowlist = parseList(await ask(rl, 'Allowlist patterns (comma-separated)', defaultAllowlist));
|
|
494
729
|
answers.denylist = parseList(await ask(rl, 'Denylist patterns (comma-separated)', ''));
|
|
495
730
|
}
|
|
@@ -533,7 +768,7 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
533
768
|
while (true) {
|
|
534
769
|
const timeout = await ask(rl, 'Queue full timeout (seconds, blank for no timeout)', '');
|
|
535
770
|
if (!timeout) {
|
|
536
|
-
answers.queue_full_timeout_sec =
|
|
771
|
+
answers.queue_full_timeout_sec = undefined;
|
|
537
772
|
break;
|
|
538
773
|
}
|
|
539
774
|
const parsed = Number(timeout);
|
|
@@ -565,12 +800,12 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
565
800
|
answers.telemetry_data_residency = (await ask(rl, 'Telemetry data residency (us/eu)', 'us')).trim();
|
|
566
801
|
}
|
|
567
802
|
else {
|
|
568
|
-
answers.telemetry_data_residency =
|
|
803
|
+
answers.telemetry_data_residency = undefined;
|
|
569
804
|
}
|
|
570
805
|
}
|
|
571
806
|
else {
|
|
572
807
|
answers.telemetry_send_data = false;
|
|
573
|
-
answers.telemetry_data_residency =
|
|
808
|
+
answers.telemetry_data_residency = undefined;
|
|
574
809
|
}
|
|
575
810
|
answers.enable_audit_metadata = await confirm(rl, 'Add audit metadata for compliance?', false);
|
|
576
811
|
if (answers.enable_audit_metadata) {
|
|
@@ -590,10 +825,8 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
590
825
|
rl.close();
|
|
591
826
|
}
|
|
592
827
|
}
|
|
593
|
-
const config =
|
|
594
|
-
const output = format
|
|
595
|
-
? JSON.stringify(config, null, 2)
|
|
596
|
-
: yaml.dump(config, { sortKeys: false, noRefs: true });
|
|
828
|
+
const config = (0, init_1.buildWizardConfig)(answers);
|
|
829
|
+
const output = renderConfigOutput(config, format);
|
|
597
830
|
fs.writeFileSync(configPath, output, 'utf-8');
|
|
598
831
|
console.log(`\nMonora config written to ${configPath}`);
|
|
599
832
|
console.log('\n=== Next Steps ===\n');
|
|
@@ -607,6 +840,10 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
607
840
|
console.log(` import { init } from 'monora-ai';`);
|
|
608
841
|
console.log(` init({ configPath: '${configPath}' });`);
|
|
609
842
|
console.log('');
|
|
843
|
+
console.log('Generate reports with:');
|
|
844
|
+
console.log(' npx monora-ai report --input ./monora_events.jsonl --output monora_report.json');
|
|
845
|
+
console.log(' (If rotation is enabled, use ./monora_events.latest.jsonl)');
|
|
846
|
+
console.log('');
|
|
610
847
|
if (detectedSdks.length > 0) {
|
|
611
848
|
console.log(`Detected SDKs (${detectedSdks.join(', ')}) will be auto-instrumented.`);
|
|
612
849
|
}
|
|
@@ -614,11 +851,14 @@ async function runInitWizard(configPath, format, assumeYes, force) {
|
|
|
614
851
|
function parseInitOptions(args) {
|
|
615
852
|
const configPath = getFlagValue(args, '--path') || 'monora.yml';
|
|
616
853
|
const format = getFlagValue(args, '--format') || 'yaml';
|
|
854
|
+
const preset = normalizeInitPreset(getFlagValue(args, '--preset'));
|
|
617
855
|
return {
|
|
618
856
|
path: configPath,
|
|
619
857
|
format: format === 'json' ? 'json' : 'yaml',
|
|
620
858
|
yes: hasFlag(args, '--yes'),
|
|
621
859
|
force: hasFlag(args, '--force'),
|
|
860
|
+
advanced: hasFlag(args, '--advanced'),
|
|
861
|
+
preset,
|
|
622
862
|
};
|
|
623
863
|
}
|
|
624
864
|
function parseReportOptions(args) {
|
|
@@ -664,6 +904,7 @@ function parseSecurityReviewOptions(args) {
|
|
|
664
904
|
};
|
|
665
905
|
}
|
|
666
906
|
function parseTrustPackageOptions(args) {
|
|
907
|
+
const target = Number(getFlagValue(args, '--control-coverage-target') || '0.9');
|
|
667
908
|
return {
|
|
668
909
|
input: getFlagValue(args, '--input'),
|
|
669
910
|
traceId: getFlagValue(args, '--trace-id'),
|
|
@@ -672,6 +913,17 @@ function parseTrustPackageOptions(args) {
|
|
|
672
913
|
sign: getFlagValue(args, '--sign') || undefined,
|
|
673
914
|
gpgKey: getFlagValue(args, '--gpg-key'),
|
|
674
915
|
gpgHome: getFlagValue(args, '--gpg-home'),
|
|
916
|
+
evidenceManifest: getFlagValue(args, '--evidence-manifest'),
|
|
917
|
+
evidenceRuntime: hasFlag(args, '--evidence-runtime'),
|
|
918
|
+
evidenceStandard: getFlagValue(args, '--evidence-standard') || undefined,
|
|
919
|
+
evidenceNoLineage: hasFlag(args, '--evidence-no-lineage'),
|
|
920
|
+
evidenceNoHashChain: hasFlag(args, '--evidence-no-hash-chain'),
|
|
921
|
+
evidenceNoWorkflowState: hasFlag(args, '--evidence-no-workflow-state'),
|
|
922
|
+
evidenceNoAimsState: hasFlag(args, '--evidence-no-aims-state'),
|
|
923
|
+
controlCatalog: getFlagValue(args, '--control-catalog'),
|
|
924
|
+
controlWorkflowState: getFlagValue(args, '--control-workflow-state'),
|
|
925
|
+
controlCoverageTarget: Number.isFinite(target) ? target : 0.9,
|
|
926
|
+
controlCoverage: getFlagValue(args, '--control-coverage'),
|
|
675
927
|
schema: !hasFlag(args, '--no-schema'),
|
|
676
928
|
verify: !hasFlag(args, '--no-verify'),
|
|
677
929
|
signatureVerify: !hasFlag(args, '--no-signature-verify'),
|
|
@@ -978,7 +1230,7 @@ async function runReport(options) {
|
|
|
978
1230
|
verifySignatures(events, config);
|
|
979
1231
|
}
|
|
980
1232
|
const policies = config.policies || undefined;
|
|
981
|
-
const report = (0, report_1.buildReport)(events, policies);
|
|
1233
|
+
const report = (0, report_1.buildReport)(events, policies, config.registry);
|
|
982
1234
|
if (options.format === 'markdown') {
|
|
983
1235
|
(0, report_1.writeMarkdown)(options.output, report);
|
|
984
1236
|
}
|
|
@@ -1072,7 +1324,19 @@ async function runTrustPackage(options) {
|
|
|
1072
1324
|
if (options.signatureVerify) {
|
|
1073
1325
|
verifySignatures(events, config);
|
|
1074
1326
|
}
|
|
1075
|
-
let trustPackage = (0, trust_package_1.buildTrustPackage)(options.traceId, events, config
|
|
1327
|
+
let trustPackage = (0, trust_package_1.buildTrustPackage)(options.traceId, events, config, {
|
|
1328
|
+
evidenceManifestPath: options.evidenceManifest,
|
|
1329
|
+
evidenceManifestAuto: options.evidenceRuntime,
|
|
1330
|
+
evidenceManifestStandard: options.evidenceStandard,
|
|
1331
|
+
evidenceManifestIncludeLineage: !options.evidenceNoLineage,
|
|
1332
|
+
evidenceManifestIncludeHashChain: !options.evidenceNoHashChain,
|
|
1333
|
+
evidenceManifestIncludeWorkflowState: !options.evidenceNoWorkflowState,
|
|
1334
|
+
evidenceManifestIncludeAimsState: !options.evidenceNoAimsState,
|
|
1335
|
+
controlCatalogPath: options.controlCatalog,
|
|
1336
|
+
controlWorkflowStatePath: options.controlWorkflowState,
|
|
1337
|
+
controlCoverageTarget: options.controlCoverageTarget,
|
|
1338
|
+
controlCoveragePath: options.controlCoverage,
|
|
1339
|
+
});
|
|
1076
1340
|
if (options.sign) {
|
|
1077
1341
|
if (options.sign !== 'gpg') {
|
|
1078
1342
|
console.error(`Unsupported signing type: ${options.sign}`);
|
|
@@ -1093,6 +1357,342 @@ async function runTrustPackage(options) {
|
|
|
1093
1357
|
(0, trust_package_1.writeTrustPackage)(options.output, trustPackage);
|
|
1094
1358
|
console.log(`Trust package generated: ${options.output}`);
|
|
1095
1359
|
}
|
|
1360
|
+
function loadAimsStateIfPresent(statePath) {
|
|
1361
|
+
if (!statePath) {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if (!fs.existsSync(statePath)) {
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
(0, aims_governance_1.loadAimsGovernanceState)(statePath, { replaceRuntime: true });
|
|
1368
|
+
}
|
|
1369
|
+
function saveAimsStateIfPresent(statePath) {
|
|
1370
|
+
if (!statePath) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
(0, aims_governance_1.exportAimsGovernanceState)(statePath);
|
|
1374
|
+
}
|
|
1375
|
+
function emitAimsJson(payload) {
|
|
1376
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1377
|
+
}
|
|
1378
|
+
async function runAims(args) {
|
|
1379
|
+
const subcommand = args[0];
|
|
1380
|
+
const subArgs = args.slice(1);
|
|
1381
|
+
if (!subcommand) {
|
|
1382
|
+
usage();
|
|
1383
|
+
process.exit(1);
|
|
1384
|
+
}
|
|
1385
|
+
const statePath = getFlagValue(subArgs, '--state');
|
|
1386
|
+
loadAimsStateIfPresent(statePath);
|
|
1387
|
+
const required = (flag) => {
|
|
1388
|
+
const value = getFlagValue(subArgs, flag);
|
|
1389
|
+
if (!value) {
|
|
1390
|
+
throw new Error(`Missing required flag: ${flag}`);
|
|
1391
|
+
}
|
|
1392
|
+
return value;
|
|
1393
|
+
};
|
|
1394
|
+
if (subcommand === 'summary') {
|
|
1395
|
+
emitAimsJson((0, aims_governance_1.getAimsGovernanceSummary)());
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (subcommand === 'export-state') {
|
|
1399
|
+
const outputPath = required('--output');
|
|
1400
|
+
const payload = (0, aims_governance_1.exportAimsGovernanceState)(outputPath);
|
|
1401
|
+
emitAimsJson({ state_type: payload.state_type, output: outputPath });
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
if (subcommand === 'import-state') {
|
|
1405
|
+
const inputPath = required('--input');
|
|
1406
|
+
const payload = (0, aims_governance_1.loadAimsGovernanceState)(inputPath, { replaceRuntime: true });
|
|
1407
|
+
const policyCount = Array.isArray(payload.policies) ? payload.policies.length : 0;
|
|
1408
|
+
const concernCount = Array.isArray(payload.concern_reports) ? payload.concern_reports.length : 0;
|
|
1409
|
+
emitAimsJson({
|
|
1410
|
+
input: inputPath,
|
|
1411
|
+
policies: policyCount,
|
|
1412
|
+
concerns: concernCount,
|
|
1413
|
+
});
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
if (subcommand === 'policy-create') {
|
|
1417
|
+
const policy = (0, aims_governance_1.createPolicyDocument)({
|
|
1418
|
+
policyId: required('--policy-id'),
|
|
1419
|
+
title: required('--title'),
|
|
1420
|
+
owner: required('--owner'),
|
|
1421
|
+
reviewFrequencyDays: Number(getFlagValue(subArgs, '--review-frequency-days') || '365'),
|
|
1422
|
+
controlIds: parseOptionalList(getFlagValue(subArgs, '--control-ids')),
|
|
1423
|
+
metadata: parseJsonObject(getFlagValue(subArgs, '--metadata')),
|
|
1424
|
+
});
|
|
1425
|
+
saveAimsStateIfPresent(statePath);
|
|
1426
|
+
emitAimsJson(policy);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (subcommand === 'policy-add-version') {
|
|
1430
|
+
const version = (0, aims_governance_1.addPolicyVersion)({
|
|
1431
|
+
policyId: required('--policy-id'),
|
|
1432
|
+
changeSummary: required('--change-summary'),
|
|
1433
|
+
createdBy: required('--created-by'),
|
|
1434
|
+
content: getFlagValue(subArgs, '--content'),
|
|
1435
|
+
artifactPath: getFlagValue(subArgs, '--artifact-path'),
|
|
1436
|
+
requiredReviewers: parseOptionalList(getFlagValue(subArgs, '--required-reviewers')),
|
|
1437
|
+
metadata: parseJsonObject(getFlagValue(subArgs, '--metadata')),
|
|
1438
|
+
});
|
|
1439
|
+
saveAimsStateIfPresent(statePath);
|
|
1440
|
+
emitAimsJson(version);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
if (subcommand === 'policy-submit') {
|
|
1444
|
+
const version = (0, aims_governance_1.submitPolicyVersion)({
|
|
1445
|
+
policyId: required('--policy-id'),
|
|
1446
|
+
versionId: required('--version-id'),
|
|
1447
|
+
submittedBy: required('--submitted-by'),
|
|
1448
|
+
reviewerChain: parseOptionalList(getFlagValue(subArgs, '--reviewer-chain')),
|
|
1449
|
+
note: getFlagValue(subArgs, '--note'),
|
|
1450
|
+
});
|
|
1451
|
+
saveAimsStateIfPresent(statePath);
|
|
1452
|
+
emitAimsJson(version);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
if (subcommand === 'policy-approve') {
|
|
1456
|
+
const version = (0, aims_governance_1.approvePolicyVersion)({
|
|
1457
|
+
policyId: required('--policy-id'),
|
|
1458
|
+
versionId: required('--version-id'),
|
|
1459
|
+
approver: required('--approver'),
|
|
1460
|
+
note: getFlagValue(subArgs, '--note'),
|
|
1461
|
+
force: hasFlag(subArgs, '--force'),
|
|
1462
|
+
});
|
|
1463
|
+
saveAimsStateIfPresent(statePath);
|
|
1464
|
+
emitAimsJson(version);
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
if (subcommand === 'policy-reject') {
|
|
1468
|
+
const version = (0, aims_governance_1.rejectPolicyVersion)({
|
|
1469
|
+
policyId: required('--policy-id'),
|
|
1470
|
+
versionId: required('--version-id'),
|
|
1471
|
+
reviewer: required('--reviewer'),
|
|
1472
|
+
note: required('--note'),
|
|
1473
|
+
});
|
|
1474
|
+
saveAimsStateIfPresent(statePath);
|
|
1475
|
+
emitAimsJson(version);
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
if (subcommand === 'policy-review') {
|
|
1479
|
+
const version = (0, aims_governance_1.recordPolicyReview)({
|
|
1480
|
+
policyId: required('--policy-id'),
|
|
1481
|
+
versionId: required('--version-id'),
|
|
1482
|
+
reviewer: required('--reviewer'),
|
|
1483
|
+
summary: required('--summary'),
|
|
1484
|
+
outcome: getFlagValue(subArgs, '--outcome') || 'accepted',
|
|
1485
|
+
});
|
|
1486
|
+
saveAimsStateIfPresent(statePath);
|
|
1487
|
+
emitAimsJson(version);
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (subcommand === 'policy-list') {
|
|
1491
|
+
emitAimsJson((0, aims_governance_1.listPolicyDocuments)());
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
if (subcommand === 'policy-get') {
|
|
1495
|
+
const policy = (0, aims_governance_1.getPolicyDocument)(required('--policy-id'));
|
|
1496
|
+
if (!policy) {
|
|
1497
|
+
throw new Error(`Policy not found: ${required('--policy-id')}`);
|
|
1498
|
+
}
|
|
1499
|
+
emitAimsJson(policy);
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
if (subcommand === 'policy-workflows') {
|
|
1503
|
+
const policyId = getFlagValue(subArgs, '--policy-id');
|
|
1504
|
+
const buildStatus = (policy) => {
|
|
1505
|
+
const workflowMap = { ...(policy.workflow_ids || {}) };
|
|
1506
|
+
if (Object.keys(workflowMap).length === 0) {
|
|
1507
|
+
const workflows = (0, control_backbone_1.listWorkflowTasks)({}).filter((workflow) => workflow.metadata?.policy_id === policy.policy_id);
|
|
1508
|
+
for (const workflow of workflows) {
|
|
1509
|
+
workflowMap[workflow.control_id] = workflow.workflow_id;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
const entries = Object.entries(workflowMap).sort(([a], [b]) => a.localeCompare(b)).map(([controlId, workflowId]) => {
|
|
1513
|
+
const workflow = (0, control_backbone_1.getWorkflowTask)(workflowId);
|
|
1514
|
+
if (!workflow) {
|
|
1515
|
+
return { control_id: controlId, workflow_id: workflowId, status: 'missing' };
|
|
1516
|
+
}
|
|
1517
|
+
return {
|
|
1518
|
+
control_id: controlId,
|
|
1519
|
+
workflow_id: workflow.workflow_id,
|
|
1520
|
+
status: workflow.status,
|
|
1521
|
+
owner: workflow.owner,
|
|
1522
|
+
due_at: workflow.due_at,
|
|
1523
|
+
updated_at: workflow.updated_at,
|
|
1524
|
+
};
|
|
1525
|
+
});
|
|
1526
|
+
return {
|
|
1527
|
+
policy_id: policy.policy_id,
|
|
1528
|
+
title: policy.title,
|
|
1529
|
+
owner: policy.owner,
|
|
1530
|
+
active_version_id: policy.active_version_id,
|
|
1531
|
+
workflows: entries,
|
|
1532
|
+
};
|
|
1533
|
+
};
|
|
1534
|
+
if (policyId) {
|
|
1535
|
+
const policy = (0, aims_governance_1.getPolicyDocument)(policyId);
|
|
1536
|
+
if (!policy) {
|
|
1537
|
+
throw new Error(`Policy not found: ${policyId}`);
|
|
1538
|
+
}
|
|
1539
|
+
emitAimsJson(buildStatus(policy));
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
emitAimsJson((0, aims_governance_1.listPolicyDocuments)().map((policy) => buildStatus(policy)));
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
if (subcommand === 'policy-approvals') {
|
|
1546
|
+
const policyId = getFlagValue(subArgs, '--policy-id');
|
|
1547
|
+
const versionId = getFlagValue(subArgs, '--version-id');
|
|
1548
|
+
if (versionId && !policyId) {
|
|
1549
|
+
throw new Error('--version-id requires --policy-id');
|
|
1550
|
+
}
|
|
1551
|
+
const policies = policyId ? [(0, aims_governance_1.getPolicyDocument)(policyId)] : (0, aims_governance_1.listPolicyDocuments)();
|
|
1552
|
+
const payload = policies.map((policy) => {
|
|
1553
|
+
if (!policy) {
|
|
1554
|
+
throw new Error(`Policy not found: ${policyId}`);
|
|
1555
|
+
}
|
|
1556
|
+
const versions = (policy.versions || [])
|
|
1557
|
+
.filter((version) => !versionId || version.version_id === versionId)
|
|
1558
|
+
.map((version) => ({
|
|
1559
|
+
version_id: version.version_id,
|
|
1560
|
+
version_number: version.version_number,
|
|
1561
|
+
status: version.status,
|
|
1562
|
+
submitted_at: version.submitted_at,
|
|
1563
|
+
approved_at: version.approved_at,
|
|
1564
|
+
approved_by: version.approved_by,
|
|
1565
|
+
required_reviewers: version.required_reviewers,
|
|
1566
|
+
approvals: version.approvals,
|
|
1567
|
+
review_history: version.review_history,
|
|
1568
|
+
}));
|
|
1569
|
+
return {
|
|
1570
|
+
policy_id: policy.policy_id,
|
|
1571
|
+
title: policy.title,
|
|
1572
|
+
owner: policy.owner,
|
|
1573
|
+
active_version_id: policy.active_version_id,
|
|
1574
|
+
versions,
|
|
1575
|
+
};
|
|
1576
|
+
});
|
|
1577
|
+
emitAimsJson(payload);
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
if (subcommand === 'policy-due') {
|
|
1581
|
+
emitAimsJson((0, aims_governance_1.getDuePolicyReviews)({
|
|
1582
|
+
referenceTime: getFlagValue(subArgs, '--reference-time'),
|
|
1583
|
+
}));
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
if (subcommand === 'context-record') {
|
|
1587
|
+
const record = (0, aims_governance_1.recordAimsContext)({
|
|
1588
|
+
scope: required('--scope'),
|
|
1589
|
+
approvedBy: required('--approved-by'),
|
|
1590
|
+
interestedParties: parseOptionalList(getFlagValue(subArgs, '--interested-parties')),
|
|
1591
|
+
internalIssues: parseOptionalList(getFlagValue(subArgs, '--internal-issues')),
|
|
1592
|
+
externalIssues: parseOptionalList(getFlagValue(subArgs, '--external-issues')),
|
|
1593
|
+
objectives: parseOptionalList(getFlagValue(subArgs, '--objectives')),
|
|
1594
|
+
reviewFrequencyDays: Number(getFlagValue(subArgs, '--review-frequency-days') || '365'),
|
|
1595
|
+
metadata: parseJsonObject(getFlagValue(subArgs, '--metadata')),
|
|
1596
|
+
});
|
|
1597
|
+
saveAimsStateIfPresent(statePath);
|
|
1598
|
+
emitAimsJson(record);
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
if (subcommand === 'context-get') {
|
|
1602
|
+
emitAimsJson((0, aims_governance_1.getAimsContext)() || {});
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (subcommand === 'raci-register') {
|
|
1606
|
+
const entry = (0, aims_governance_1.registerRaciEntry)({
|
|
1607
|
+
functionName: required('--function-name'),
|
|
1608
|
+
owner: required('--owner'),
|
|
1609
|
+
accountable: parseOptionalList(required('--accountable')),
|
|
1610
|
+
responsible: parseOptionalList(required('--responsible')),
|
|
1611
|
+
consulted: parseOptionalList(getFlagValue(subArgs, '--consulted')),
|
|
1612
|
+
informed: parseOptionalList(getFlagValue(subArgs, '--informed')),
|
|
1613
|
+
controlIds: parseOptionalList(getFlagValue(subArgs, '--control-ids')),
|
|
1614
|
+
notes: getFlagValue(subArgs, '--notes'),
|
|
1615
|
+
metadata: parseJsonObject(getFlagValue(subArgs, '--metadata')),
|
|
1616
|
+
});
|
|
1617
|
+
saveAimsStateIfPresent(statePath);
|
|
1618
|
+
emitAimsJson(entry);
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
if (subcommand === 'raci-list') {
|
|
1622
|
+
emitAimsJson((0, aims_governance_1.listRaciEntries)());
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
if (subcommand === 'training-record') {
|
|
1626
|
+
const record = (0, aims_governance_1.recordGovernanceTraining)({
|
|
1627
|
+
userId: required('--user-id'),
|
|
1628
|
+
topic: required('--topic'),
|
|
1629
|
+
recordedBy: required('--recorded-by'),
|
|
1630
|
+
completedAt: getFlagValue(subArgs, '--completed-at'),
|
|
1631
|
+
controlIds: parseOptionalList(getFlagValue(subArgs, '--control-ids')),
|
|
1632
|
+
notes: getFlagValue(subArgs, '--notes'),
|
|
1633
|
+
metadata: parseJsonObject(getFlagValue(subArgs, '--metadata')),
|
|
1634
|
+
});
|
|
1635
|
+
saveAimsStateIfPresent(statePath);
|
|
1636
|
+
emitAimsJson(record);
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
if (subcommand === 'training-list') {
|
|
1640
|
+
emitAimsJson((0, aims_governance_1.listGovernanceTrainingRecords)());
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
if (subcommand === 'concern-file') {
|
|
1644
|
+
const report = (0, aims_governance_1.fileConcernReport)({
|
|
1645
|
+
title: required('--title'),
|
|
1646
|
+
description: required('--description'),
|
|
1647
|
+
reportedBy: required('--reported-by'),
|
|
1648
|
+
severity: getFlagValue(subArgs, '--severity') || 'medium',
|
|
1649
|
+
assignedTo: getFlagValue(subArgs, '--assigned-to'),
|
|
1650
|
+
controlIds: parseOptionalList(getFlagValue(subArgs, '--control-ids')),
|
|
1651
|
+
tags: parseOptionalList(getFlagValue(subArgs, '--tags')),
|
|
1652
|
+
metadata: parseJsonObject(getFlagValue(subArgs, '--metadata')),
|
|
1653
|
+
});
|
|
1654
|
+
saveAimsStateIfPresent(statePath);
|
|
1655
|
+
emitAimsJson(report);
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
if (subcommand === 'concern-update') {
|
|
1659
|
+
const report = (0, aims_governance_1.updateConcernReport)({
|
|
1660
|
+
concernId: required('--concern-id'),
|
|
1661
|
+
actor: required('--actor'),
|
|
1662
|
+
status: required('--status'),
|
|
1663
|
+
note: getFlagValue(subArgs, '--note'),
|
|
1664
|
+
assignedTo: getFlagValue(subArgs, '--assigned-to'),
|
|
1665
|
+
});
|
|
1666
|
+
saveAimsStateIfPresent(statePath);
|
|
1667
|
+
emitAimsJson(report);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
if (subcommand === 'concern-resolve') {
|
|
1671
|
+
const report = (0, aims_governance_1.resolveConcernReport)({
|
|
1672
|
+
concernId: required('--concern-id'),
|
|
1673
|
+
resolvedBy: required('--resolved-by'),
|
|
1674
|
+
resolutionSummary: required('--resolution-summary'),
|
|
1675
|
+
close: !hasFlag(subArgs, '--no-close'),
|
|
1676
|
+
});
|
|
1677
|
+
saveAimsStateIfPresent(statePath);
|
|
1678
|
+
emitAimsJson(report);
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
if (subcommand === 'concern-list') {
|
|
1682
|
+
const status = getFlagValue(subArgs, '--status');
|
|
1683
|
+
emitAimsJson((0, aims_governance_1.listConcernReports)({ status: status }));
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
if (subcommand === 'concern-get') {
|
|
1687
|
+
const report = (0, aims_governance_1.getConcernReport)(required('--concern-id'));
|
|
1688
|
+
if (!report) {
|
|
1689
|
+
throw new Error(`Concern not found: ${required('--concern-id')}`);
|
|
1690
|
+
}
|
|
1691
|
+
emitAimsJson(report);
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
throw new Error(`Unsupported aims subcommand: ${subcommand}`);
|
|
1695
|
+
}
|
|
1096
1696
|
async function runVerify(options) {
|
|
1097
1697
|
if (!options.input) {
|
|
1098
1698
|
usage();
|
|
@@ -1197,9 +1797,12 @@ async function runVerify(options) {
|
|
|
1197
1797
|
async function runValidate(args) {
|
|
1198
1798
|
const configPath = getFlagValue(args, '--config') || 'monora.yml';
|
|
1199
1799
|
const asJson = hasFlag(args, '--json');
|
|
1800
|
+
const modeArg = normalizeValidationMode(getFlagValue(args, '--mode'));
|
|
1200
1801
|
try {
|
|
1201
1802
|
const config = (0, config_1.loadConfig)({ configPath });
|
|
1202
|
-
const
|
|
1803
|
+
const env = config.defaults?.environment || 'dev';
|
|
1804
|
+
const mode = modeArg || (env === 'production' ? 'strict' : 'lenient');
|
|
1805
|
+
const results = (0, diagnostics_1.validateConfig)(config, { mode });
|
|
1203
1806
|
const exitCode = (0, diagnostics_1.emitResults)(results, { json: asJson });
|
|
1204
1807
|
process.exit(exitCode);
|
|
1205
1808
|
}
|
|
@@ -1213,9 +1816,8 @@ async function runDoctor(args) {
|
|
|
1213
1816
|
const asJson = hasFlag(args, '--json');
|
|
1214
1817
|
const noNetwork = hasFlag(args, '--no-network');
|
|
1215
1818
|
try {
|
|
1216
|
-
const
|
|
1217
|
-
const
|
|
1218
|
-
const exitCode = (0, diagnostics_1.emitResults)(results, { json: asJson });
|
|
1819
|
+
const results = await (0, doctor_1.doctorChecks)(configPath, { checkNetwork: !noNetwork });
|
|
1820
|
+
const exitCode = (0, doctor_1.emitDoctorResults)(results, { json: asJson });
|
|
1219
1821
|
process.exit(exitCode);
|
|
1220
1822
|
}
|
|
1221
1823
|
catch (error) {
|
|
@@ -1223,6 +1825,49 @@ async function runDoctor(args) {
|
|
|
1223
1825
|
process.exit(1);
|
|
1224
1826
|
}
|
|
1225
1827
|
}
|
|
1828
|
+
async function runConfigFix(args) {
|
|
1829
|
+
const configPath = getFlagValue(args, '--config') || 'monora.yml';
|
|
1830
|
+
const dryRun = hasFlag(args, '--dry-run');
|
|
1831
|
+
const noNetwork = hasFlag(args, '--no-network');
|
|
1832
|
+
try {
|
|
1833
|
+
const result = await (0, fix_1.fixConfigFile)(configPath, {
|
|
1834
|
+
dryRun,
|
|
1835
|
+
checkNetwork: !noNetwork,
|
|
1836
|
+
});
|
|
1837
|
+
if (!result.ok) {
|
|
1838
|
+
for (const error of result.errors) {
|
|
1839
|
+
console.error(`Error: ${error}`);
|
|
1840
|
+
}
|
|
1841
|
+
process.exit(1);
|
|
1842
|
+
}
|
|
1843
|
+
if (result.changes.length === 0) {
|
|
1844
|
+
console.log('No fixes required.');
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
console.log('Applied fixes:');
|
|
1848
|
+
for (const change of result.changes) {
|
|
1849
|
+
console.log(` - ${change}`);
|
|
1850
|
+
}
|
|
1851
|
+
if (result.warnings.length > 0) {
|
|
1852
|
+
console.log('Warnings:');
|
|
1853
|
+
for (const warning of result.warnings) {
|
|
1854
|
+
console.log(` - ${warning}`);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
if (dryRun) {
|
|
1858
|
+
console.log('\nDry run only (no files written).');
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
if (result.backupPath) {
|
|
1862
|
+
console.log(`Backup written to ${result.backupPath}`);
|
|
1863
|
+
}
|
|
1864
|
+
console.log(`Updated config saved to ${configPath}`);
|
|
1865
|
+
}
|
|
1866
|
+
catch (error) {
|
|
1867
|
+
console.error(`Error: ${error?.message || error}`);
|
|
1868
|
+
process.exit(1);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1226
1871
|
async function runCompatReport(args) {
|
|
1227
1872
|
const baselinePath = getFlagValue(args, '--baseline');
|
|
1228
1873
|
const outputPath = getFlagValue(args, '--output');
|
|
@@ -1605,6 +2250,415 @@ async function runStandardsCheck(args) {
|
|
|
1605
2250
|
process.exit(1);
|
|
1606
2251
|
}
|
|
1607
2252
|
}
|
|
2253
|
+
function normalizeComplianceTargets(values) {
|
|
2254
|
+
const normalized = values
|
|
2255
|
+
.map((value) => value.trim().toLowerCase().replace(/-/g, '_'))
|
|
2256
|
+
.filter(Boolean);
|
|
2257
|
+
const invalid = normalized.filter((value) => !(0, complianceTargets_1.isFrameworkSupported)(value));
|
|
2258
|
+
if (invalid.length > 0) {
|
|
2259
|
+
throw new Error(`Unsupported compliance target(s): ${invalid.join(', ')}`);
|
|
2260
|
+
}
|
|
2261
|
+
return normalized;
|
|
2262
|
+
}
|
|
2263
|
+
async function runSchema(args) {
|
|
2264
|
+
const subcommand = args[0];
|
|
2265
|
+
const subArgs = args.slice(1);
|
|
2266
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
2267
|
+
console.log('Usage: npx monora-ai schema infer [options]');
|
|
2268
|
+
console.log(' infer --input <events.jsonl> --output <spec.json> [--model-name <name>] [--model-version <ver>] [--compliance-target <framework>]... [--sample-size <n>] [--no-pii] [--report <inference_report.json>] [--contract <schema_contract.json>]');
|
|
2269
|
+
process.exit(1);
|
|
2270
|
+
}
|
|
2271
|
+
if (subcommand !== 'infer') {
|
|
2272
|
+
console.error(`Unknown schema subcommand: ${subcommand}`);
|
|
2273
|
+
process.exit(1);
|
|
2274
|
+
}
|
|
2275
|
+
const inputPath = getFlagValue(subArgs, '--input');
|
|
2276
|
+
const outputPath = getFlagValue(subArgs, '--output');
|
|
2277
|
+
const modelName = getFlagValue(subArgs, '--model-name');
|
|
2278
|
+
const modelVersion = getFlagValue(subArgs, '--model-version') || '1.0.0';
|
|
2279
|
+
const sampleSizeRaw = getFlagValue(subArgs, '--sample-size');
|
|
2280
|
+
const sampleSize = sampleSizeRaw ? Number(sampleSizeRaw) : 100;
|
|
2281
|
+
const noPii = hasFlag(subArgs, '--no-pii');
|
|
2282
|
+
const reportPath = getFlagValue(subArgs, '--report');
|
|
2283
|
+
const contractPath = getFlagValue(subArgs, '--contract');
|
|
2284
|
+
if (!inputPath || !outputPath) {
|
|
2285
|
+
console.error('Error: --input and --output are required for schema infer.');
|
|
2286
|
+
process.exit(1);
|
|
2287
|
+
}
|
|
2288
|
+
if (!Number.isFinite(sampleSize) || sampleSize <= 0) {
|
|
2289
|
+
console.error('Error: --sample-size must be > 0.');
|
|
2290
|
+
process.exit(1);
|
|
2291
|
+
}
|
|
2292
|
+
let complianceTargets = [];
|
|
2293
|
+
try {
|
|
2294
|
+
complianceTargets = normalizeComplianceTargets(getFlagValues(subArgs, '--compliance-target'));
|
|
2295
|
+
}
|
|
2296
|
+
catch (error) {
|
|
2297
|
+
console.error(`Error: ${error?.message || error}`);
|
|
2298
|
+
process.exit(1);
|
|
2299
|
+
}
|
|
2300
|
+
try {
|
|
2301
|
+
const spec = (0, schemaInference_1.inferSchemaFromFile)(inputPath, {
|
|
2302
|
+
modelName: modelName || undefined,
|
|
2303
|
+
modelVersion,
|
|
2304
|
+
complianceTargets,
|
|
2305
|
+
detectPii: !noPii,
|
|
2306
|
+
sampleSize,
|
|
2307
|
+
});
|
|
2308
|
+
const outputResolved = path.resolve(outputPath);
|
|
2309
|
+
fs.mkdirSync(path.dirname(outputResolved), { recursive: true });
|
|
2310
|
+
fs.writeFileSync(outputResolved, JSON.stringify((0, spec_1.specToDict)(spec), null, 2), 'utf-8');
|
|
2311
|
+
console.log(`Wrote inferred schema spec to ${outputPath}`);
|
|
2312
|
+
if (contractPath) {
|
|
2313
|
+
const contractResolved = path.resolve(contractPath);
|
|
2314
|
+
fs.mkdirSync(path.dirname(contractResolved), { recursive: true });
|
|
2315
|
+
fs.writeFileSync(contractResolved, JSON.stringify((0, spec_1.specToSchemaContract)(spec), null, 2), 'utf-8');
|
|
2316
|
+
console.log(`Wrote schema contract to ${contractPath}`);
|
|
2317
|
+
}
|
|
2318
|
+
if (reportPath) {
|
|
2319
|
+
const reportResolved = path.resolve(reportPath);
|
|
2320
|
+
fs.mkdirSync(path.dirname(reportResolved), { recursive: true });
|
|
2321
|
+
const events = (0, schemaInference_1.loadEventsFromFile)(inputPath);
|
|
2322
|
+
const report = (0, schemaInference_1.generateInferenceReport)(events, sampleSize);
|
|
2323
|
+
fs.writeFileSync(reportResolved, JSON.stringify(report, null, 2), 'utf-8');
|
|
2324
|
+
console.log(`Wrote inference report to ${reportPath}`);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
catch (error) {
|
|
2328
|
+
console.error(`Error: ${error?.message || error}`);
|
|
2329
|
+
process.exit(1);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
async function runModel(args) {
|
|
2333
|
+
const subcommand = args[0];
|
|
2334
|
+
const subArgs = args.slice(1);
|
|
2335
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
2336
|
+
console.log('Usage: npx monora-ai model create [options]');
|
|
2337
|
+
console.log(' create --input <events.jsonl> --output <model.json> [--model-name <name>] [--model-version <ver>] [--compliance-target <framework>]... [--risk-category minimal|limited|high|unacceptable] [--intended-use <text>] [--owner <id>] [--tag <value>]... [--sample-size <n>] [--no-pii] [--config-out <monora.yml|json>] [--config-format yaml|json] [--contract-out <schema_contract.json>]');
|
|
2338
|
+
process.exit(1);
|
|
2339
|
+
}
|
|
2340
|
+
if (subcommand !== 'create') {
|
|
2341
|
+
console.error(`Unknown model subcommand: ${subcommand}`);
|
|
2342
|
+
process.exit(1);
|
|
2343
|
+
}
|
|
2344
|
+
const inputPath = getFlagValue(subArgs, '--input');
|
|
2345
|
+
const outputPath = getFlagValue(subArgs, '--output');
|
|
2346
|
+
const modelName = getFlagValue(subArgs, '--model-name') || 'unnamed-model';
|
|
2347
|
+
const modelVersion = getFlagValue(subArgs, '--model-version') || '1.0.0';
|
|
2348
|
+
const riskCategory = getFlagValue(subArgs, '--risk-category') || 'limited';
|
|
2349
|
+
const intendedUse = getFlagValue(subArgs, '--intended-use');
|
|
2350
|
+
const owner = getFlagValue(subArgs, '--owner');
|
|
2351
|
+
const sampleSizeRaw = getFlagValue(subArgs, '--sample-size');
|
|
2352
|
+
const sampleSize = sampleSizeRaw ? Number(sampleSizeRaw) : 100;
|
|
2353
|
+
const noPii = hasFlag(subArgs, '--no-pii');
|
|
2354
|
+
const configOut = getFlagValue(subArgs, '--config-out');
|
|
2355
|
+
const configFormat = (getFlagValue(subArgs, '--config-format') || 'yaml').toLowerCase();
|
|
2356
|
+
const contractOut = getFlagValue(subArgs, '--contract-out');
|
|
2357
|
+
if (!inputPath || !outputPath) {
|
|
2358
|
+
console.error('Error: --input and --output are required for model create.');
|
|
2359
|
+
process.exit(1);
|
|
2360
|
+
}
|
|
2361
|
+
if (!Number.isFinite(sampleSize) || sampleSize <= 0) {
|
|
2362
|
+
console.error('Error: --sample-size must be > 0.');
|
|
2363
|
+
process.exit(1);
|
|
2364
|
+
}
|
|
2365
|
+
if (!['minimal', 'limited', 'high', 'unacceptable'].includes(riskCategory)) {
|
|
2366
|
+
console.error("Error: --risk-category must be one of minimal|limited|high|unacceptable.");
|
|
2367
|
+
process.exit(1);
|
|
2368
|
+
}
|
|
2369
|
+
if (!['yaml', 'json'].includes(configFormat)) {
|
|
2370
|
+
console.error("Error: --config-format must be one of yaml|json.");
|
|
2371
|
+
process.exit(1);
|
|
2372
|
+
}
|
|
2373
|
+
let complianceTargets = [];
|
|
2374
|
+
let tags = [];
|
|
2375
|
+
try {
|
|
2376
|
+
complianceTargets = normalizeComplianceTargets(getFlagValues(subArgs, '--compliance-target'));
|
|
2377
|
+
tags = getFlagValues(subArgs, '--tag');
|
|
2378
|
+
}
|
|
2379
|
+
catch (error) {
|
|
2380
|
+
console.error(`Error: ${error?.message || error}`);
|
|
2381
|
+
process.exit(1);
|
|
2382
|
+
}
|
|
2383
|
+
try {
|
|
2384
|
+
const model = (0, model_1.modelFromData)({
|
|
2385
|
+
source: inputPath,
|
|
2386
|
+
modelName,
|
|
2387
|
+
modelVersion,
|
|
2388
|
+
complianceTargets,
|
|
2389
|
+
riskCategory: riskCategory,
|
|
2390
|
+
intendedUse: intendedUse || undefined,
|
|
2391
|
+
owner: owner || undefined,
|
|
2392
|
+
tags,
|
|
2393
|
+
detectPii: !noPii,
|
|
2394
|
+
sampleSize,
|
|
2395
|
+
});
|
|
2396
|
+
(0, model_1.saveModel)(model, outputPath);
|
|
2397
|
+
console.log(`Wrote model to ${outputPath}`);
|
|
2398
|
+
if (configOut) {
|
|
2399
|
+
(0, model_1.saveConfig)(model, configOut, configFormat);
|
|
2400
|
+
console.log(`Wrote generated config to ${configOut}`);
|
|
2401
|
+
}
|
|
2402
|
+
if (contractOut) {
|
|
2403
|
+
(0, model_1.saveSchemaContract)(model, contractOut);
|
|
2404
|
+
console.log(`Wrote schema contract to ${contractOut}`);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
catch (error) {
|
|
2408
|
+
console.error(`Error: ${error?.message || error}`);
|
|
2409
|
+
process.exit(1);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
async function runOnboard(args) {
|
|
2413
|
+
const subcommand = args[0];
|
|
2414
|
+
const subArgs = args.slice(1);
|
|
2415
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
2416
|
+
console.log('Usage: npx monora-ai onboard <init|validate|complete|status> [options]');
|
|
2417
|
+
console.log(' init [--config monora.yml] [--yes] [--force]');
|
|
2418
|
+
console.log(' validate [--config monora.yml] [--input events.jsonl] [--output result.json] [--pretty]');
|
|
2419
|
+
console.log(' complete [--config monora.yml] [--completed-by name] [--output result.json] [--pretty]');
|
|
2420
|
+
console.log(' status [--config monora.yml] [--pretty]');
|
|
2421
|
+
process.exit(1);
|
|
2422
|
+
}
|
|
2423
|
+
if (subcommand === 'init') {
|
|
2424
|
+
const configPath = getFlagValue(subArgs, '--config') || 'monora.yml';
|
|
2425
|
+
const acceptDefaults = hasFlag(subArgs, '--yes');
|
|
2426
|
+
const force = hasFlag(subArgs, '--force');
|
|
2427
|
+
if (fs.existsSync(configPath) && !force) {
|
|
2428
|
+
if (acceptDefaults) {
|
|
2429
|
+
console.error(`${configPath} already exists. Use --force to overwrite.`);
|
|
2430
|
+
process.exit(1);
|
|
2431
|
+
}
|
|
2432
|
+
const rl = createInterface();
|
|
2433
|
+
const overwrite = await confirm(rl, `${configPath} already exists. Overwrite?`, false);
|
|
2434
|
+
rl.close();
|
|
2435
|
+
if (!overwrite) {
|
|
2436
|
+
console.error('Aborted.');
|
|
2437
|
+
process.exit(1);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
const config = (0, config_1.loadConfig)({ configPath });
|
|
2441
|
+
const onboardingDefaults = (config.onboarding || {});
|
|
2442
|
+
let onboardingEnabled = true;
|
|
2443
|
+
let requiredInProd = true;
|
|
2444
|
+
let logsPath = String(onboardingDefaults?.artifacts?.production_logs_path || './monora_events.jsonl');
|
|
2445
|
+
let schemaPath = String(onboardingDefaults?.artifacts?.schema_contract_path || './onboarding/schema_contract.json');
|
|
2446
|
+
let datasetSamplePath = null;
|
|
2447
|
+
let baselineReportsDir = String(onboardingDefaults?.artifacts?.baseline_reports_dir || './monora_reports/onboarding');
|
|
2448
|
+
let standards = ['SOC2', 'GDPR', 'ISO27001'];
|
|
2449
|
+
let bundles = ['core_observability', 'soc2_access', 'gdpr_privacy', 'iso27001_security'];
|
|
2450
|
+
let toggles = {
|
|
2451
|
+
identity_tracking: true,
|
|
2452
|
+
risk_tracking: true,
|
|
2453
|
+
bias_tracking: false,
|
|
2454
|
+
oversight_tracking: true,
|
|
2455
|
+
data_governance_tracking: true,
|
|
2456
|
+
lifecycle_tracking: true,
|
|
2457
|
+
};
|
|
2458
|
+
let roles = {
|
|
2459
|
+
inputs: ['body.prompt'],
|
|
2460
|
+
outputs: ['body.response'],
|
|
2461
|
+
metadata: ['event_type', 'service_name', 'timestamp'],
|
|
2462
|
+
identifiers: ['event_id', 'trace_id', 'span_id'],
|
|
2463
|
+
};
|
|
2464
|
+
if (!acceptDefaults) {
|
|
2465
|
+
const rl = createInterface();
|
|
2466
|
+
try {
|
|
2467
|
+
onboardingEnabled = await confirm(rl, 'Enable onboarding?', true);
|
|
2468
|
+
requiredInProd = await confirm(rl, 'Require completed onboarding in production?', true);
|
|
2469
|
+
logsPath = await ask(rl, 'Production logs path', logsPath);
|
|
2470
|
+
schemaPath = await ask(rl, 'Schema contract path', schemaPath);
|
|
2471
|
+
const datasetRaw = (await ask(rl, 'Dataset sample path (optional)', '')).trim();
|
|
2472
|
+
datasetSamplePath = datasetRaw || null;
|
|
2473
|
+
baselineReportsDir = await ask(rl, 'Baseline reports output directory', baselineReportsDir);
|
|
2474
|
+
standards = parseOptionalList(await ask(rl, 'Standards (comma-separated)', 'SOC2,GDPR,ISO27001'));
|
|
2475
|
+
if (standards.length === 0) {
|
|
2476
|
+
standards = ['SOC2', 'GDPR', 'ISO27001'];
|
|
2477
|
+
}
|
|
2478
|
+
bundles = parseOptionalList(await ask(rl, 'Enrichment bundles (comma-separated)', 'core_observability,soc2_access,gdpr_privacy,iso27001_security'));
|
|
2479
|
+
if (bundles.length === 0) {
|
|
2480
|
+
bundles = ['core_observability', 'soc2_access', 'gdpr_privacy', 'iso27001_security'];
|
|
2481
|
+
}
|
|
2482
|
+
toggles = {
|
|
2483
|
+
identity_tracking: await confirm(rl, 'Enable identity tracking?', true),
|
|
2484
|
+
risk_tracking: await confirm(rl, 'Enable risk tracking?', true),
|
|
2485
|
+
bias_tracking: await confirm(rl, 'Enable bias tracking?', false),
|
|
2486
|
+
oversight_tracking: await confirm(rl, 'Enable oversight tracking?', true),
|
|
2487
|
+
data_governance_tracking: await confirm(rl, 'Enable data governance tracking?', true),
|
|
2488
|
+
lifecycle_tracking: await confirm(rl, 'Enable lifecycle tracking?', true),
|
|
2489
|
+
};
|
|
2490
|
+
const inputs = parseOptionalList(await ask(rl, 'Model spec input fields (comma-separated)', 'body.prompt'));
|
|
2491
|
+
const outputs = parseOptionalList(await ask(rl, 'Model spec output fields (comma-separated)', 'body.response'));
|
|
2492
|
+
const metadata = parseOptionalList(await ask(rl, 'Model spec metadata fields (comma-separated)', 'event_type,service_name,timestamp'));
|
|
2493
|
+
const identifiers = parseOptionalList(await ask(rl, 'Model spec identifiers (comma-separated)', 'event_id,trace_id,span_id'));
|
|
2494
|
+
roles = {
|
|
2495
|
+
inputs: inputs.length > 0 ? inputs : ['body.prompt'],
|
|
2496
|
+
outputs: outputs.length > 0 ? outputs : ['body.response'],
|
|
2497
|
+
metadata: metadata.length > 0 ? metadata : ['event_type', 'service_name', 'timestamp'],
|
|
2498
|
+
identifiers: identifiers.length > 0 ? identifiers : ['event_id', 'trace_id', 'span_id'],
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
finally {
|
|
2502
|
+
rl.close();
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
else {
|
|
2506
|
+
const enrichmentsDefaults = (config.enrichments || {});
|
|
2507
|
+
const bundlesDefault = Array.isArray(enrichmentsDefaults.bundles)
|
|
2508
|
+
? enrichmentsDefaults.bundles
|
|
2509
|
+
: bundles;
|
|
2510
|
+
bundles = bundlesDefault.map((value) => String(value).trim()).filter(Boolean);
|
|
2511
|
+
if (bundles.length === 0) {
|
|
2512
|
+
bundles = ['core_observability', 'soc2_access', 'gdpr_privacy', 'iso27001_security'];
|
|
2513
|
+
}
|
|
2514
|
+
const toggleDefaults = enrichmentsDefaults.toggles && typeof enrichmentsDefaults.toggles === 'object'
|
|
2515
|
+
? enrichmentsDefaults.toggles
|
|
2516
|
+
: {};
|
|
2517
|
+
toggles = {
|
|
2518
|
+
identity_tracking: Boolean(toggleDefaults.identity_tracking ?? toggles.identity_tracking),
|
|
2519
|
+
risk_tracking: Boolean(toggleDefaults.risk_tracking ?? toggles.risk_tracking),
|
|
2520
|
+
bias_tracking: Boolean(toggleDefaults.bias_tracking ?? toggles.bias_tracking),
|
|
2521
|
+
oversight_tracking: Boolean(toggleDefaults.oversight_tracking ?? toggles.oversight_tracking),
|
|
2522
|
+
data_governance_tracking: Boolean(toggleDefaults.data_governance_tracking ?? toggles.data_governance_tracking),
|
|
2523
|
+
lifecycle_tracking: Boolean(toggleDefaults.lifecycle_tracking ?? toggles.lifecycle_tracking),
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
config.onboarding = {
|
|
2527
|
+
enabled: onboardingEnabled,
|
|
2528
|
+
required_in_production: requiredInProd,
|
|
2529
|
+
status: 'draft',
|
|
2530
|
+
standards,
|
|
2531
|
+
artifacts: {
|
|
2532
|
+
production_logs_path: logsPath,
|
|
2533
|
+
schema_contract_path: schemaPath,
|
|
2534
|
+
dataset_sample_path: datasetSamplePath,
|
|
2535
|
+
baseline_reports_dir: baselineReportsDir,
|
|
2536
|
+
},
|
|
2537
|
+
validation: {
|
|
2538
|
+
min_log_records: 100,
|
|
2539
|
+
required_field_presence_threshold: 0.95,
|
|
2540
|
+
type_conformance_threshold: 0.90,
|
|
2541
|
+
},
|
|
2542
|
+
completion: {
|
|
2543
|
+
completed_at: null,
|
|
2544
|
+
completed_by: null,
|
|
2545
|
+
last_validated_at: null,
|
|
2546
|
+
},
|
|
2547
|
+
};
|
|
2548
|
+
config.model_spec = (0, onboarding_1.buildModelSpec)({
|
|
2549
|
+
schemaRef: schemaPath,
|
|
2550
|
+
roles,
|
|
2551
|
+
});
|
|
2552
|
+
config.enrichments = {
|
|
2553
|
+
profile: 'recommended',
|
|
2554
|
+
bundles,
|
|
2555
|
+
toggles,
|
|
2556
|
+
};
|
|
2557
|
+
const enrichmentPlan = (0, onboarding_1.resolveEnrichmentPlan)(config, { apply: true });
|
|
2558
|
+
const ext = path.extname(configPath).toLowerCase();
|
|
2559
|
+
const raw = ext === '.json'
|
|
2560
|
+
? JSON.stringify(config, null, 2)
|
|
2561
|
+
: yaml.dump(config, { sortKeys: false });
|
|
2562
|
+
fs.writeFileSync(configPath, raw, 'utf-8');
|
|
2563
|
+
const configDir = path.dirname(path.resolve(configPath));
|
|
2564
|
+
const schemaAbs = path.resolve(configDir, schemaPath);
|
|
2565
|
+
fs.mkdirSync(path.dirname(schemaAbs), { recursive: true });
|
|
2566
|
+
if (!fs.existsSync(schemaAbs)) {
|
|
2567
|
+
const schemaTemplate = {
|
|
2568
|
+
schema: {
|
|
2569
|
+
type: 'object',
|
|
2570
|
+
properties: {
|
|
2571
|
+
body: {
|
|
2572
|
+
type: 'object',
|
|
2573
|
+
properties: {
|
|
2574
|
+
prompt: { type: 'string' },
|
|
2575
|
+
response: { type: 'string' },
|
|
2576
|
+
},
|
|
2577
|
+
},
|
|
2578
|
+
},
|
|
2579
|
+
},
|
|
2580
|
+
roles,
|
|
2581
|
+
};
|
|
2582
|
+
fs.writeFileSync(schemaAbs, JSON.stringify(schemaTemplate, null, 2), 'utf-8');
|
|
2583
|
+
}
|
|
2584
|
+
try {
|
|
2585
|
+
const state = (0, runtime_1.getState)();
|
|
2586
|
+
if (state && typeof state.emitInternal === 'function') {
|
|
2587
|
+
state.emitInternal({
|
|
2588
|
+
event_type: 'onboarding_started',
|
|
2589
|
+
body: {
|
|
2590
|
+
config_path: configPath,
|
|
2591
|
+
standards,
|
|
2592
|
+
schema_contract_path: schemaPath,
|
|
2593
|
+
bundles,
|
|
2594
|
+
},
|
|
2595
|
+
purpose: 'governance',
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
catch {
|
|
2600
|
+
// no-op
|
|
2601
|
+
}
|
|
2602
|
+
console.log(`Onboarding config initialized in ${configPath}`);
|
|
2603
|
+
console.log(`Schema contract template: ${schemaAbs}`);
|
|
2604
|
+
if (Array.isArray(enrichmentPlan.unapplied_settings) && enrichmentPlan.unapplied_settings.length > 0) {
|
|
2605
|
+
console.log(`Warning: unresolved enrichment settings: ${enrichmentPlan.unapplied_settings.length}`);
|
|
2606
|
+
}
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
if (subcommand === 'validate') {
|
|
2610
|
+
const configPath = getFlagValue(subArgs, '--config') || 'monora.yml';
|
|
2611
|
+
const inputPathOverride = getFlagValue(subArgs, '--input');
|
|
2612
|
+
const outputPath = getFlagValue(subArgs, '--output');
|
|
2613
|
+
const pretty = hasFlag(subArgs, '--pretty');
|
|
2614
|
+
const result = (0, onboarding_1.validateOnboarding)({
|
|
2615
|
+
configPath,
|
|
2616
|
+
inputPathOverride: inputPathOverride || undefined,
|
|
2617
|
+
persist: true,
|
|
2618
|
+
});
|
|
2619
|
+
const output = JSON.stringify(result, null, pretty ? 2 : undefined);
|
|
2620
|
+
if (outputPath) {
|
|
2621
|
+
fs.writeFileSync(outputPath, output, 'utf-8');
|
|
2622
|
+
}
|
|
2623
|
+
else {
|
|
2624
|
+
console.log(output);
|
|
2625
|
+
}
|
|
2626
|
+
if (result.status !== 'validated') {
|
|
2627
|
+
process.exit(1);
|
|
2628
|
+
}
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
if (subcommand === 'complete') {
|
|
2632
|
+
const configPath = getFlagValue(subArgs, '--config') || 'monora.yml';
|
|
2633
|
+
const completedBy = getFlagValue(subArgs, '--completed-by');
|
|
2634
|
+
const outputPath = getFlagValue(subArgs, '--output');
|
|
2635
|
+
const pretty = hasFlag(subArgs, '--pretty');
|
|
2636
|
+
const result = (0, onboarding_1.completeOnboarding)({
|
|
2637
|
+
configPath,
|
|
2638
|
+
completedBy: completedBy || undefined,
|
|
2639
|
+
});
|
|
2640
|
+
const output = JSON.stringify(result, null, pretty ? 2 : undefined);
|
|
2641
|
+
if (outputPath) {
|
|
2642
|
+
fs.writeFileSync(outputPath, output, 'utf-8');
|
|
2643
|
+
}
|
|
2644
|
+
else {
|
|
2645
|
+
console.log(output);
|
|
2646
|
+
}
|
|
2647
|
+
if (result.status !== 'completed') {
|
|
2648
|
+
process.exit(1);
|
|
2649
|
+
}
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
if (subcommand === 'status') {
|
|
2653
|
+
const configPath = getFlagValue(subArgs, '--config') || 'monora.yml';
|
|
2654
|
+
const pretty = hasFlag(subArgs, '--pretty');
|
|
2655
|
+
const result = (0, onboarding_1.getOnboardingStatus)({ configPath });
|
|
2656
|
+
console.log(JSON.stringify(result, null, pretty ? 2 : undefined));
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
console.error(`Unknown onboard subcommand: ${subcommand}`);
|
|
2660
|
+
process.exit(1);
|
|
2661
|
+
}
|
|
1608
2662
|
const RETRY_QUEUE_STATS_FILENAME = '.monora_retry_queue_stats.json';
|
|
1609
2663
|
function resolveRetryQueuePaths(config, overridePath) {
|
|
1610
2664
|
const warnings = [];
|
|
@@ -1785,6 +2839,284 @@ async function runRetryQueue(args) {
|
|
|
1785
2839
|
process.exit(1);
|
|
1786
2840
|
}
|
|
1787
2841
|
}
|
|
2842
|
+
async function runProductionCheck(args) {
|
|
2843
|
+
const configPath = getFlagValue(args, '--config') || 'monora.yml';
|
|
2844
|
+
const jsonOutput = hasFlag(args, '--json');
|
|
2845
|
+
const showFix = hasFlag(args, '--fix');
|
|
2846
|
+
// Detect environment
|
|
2847
|
+
const envDetection = (0, config_1.detectEnvironment)();
|
|
2848
|
+
const suggestedPreset = (0, config_1.suggestPreset)();
|
|
2849
|
+
// Load config
|
|
2850
|
+
let config;
|
|
2851
|
+
try {
|
|
2852
|
+
config = (0, config_1.loadConfig)({ configPath });
|
|
2853
|
+
}
|
|
2854
|
+
catch {
|
|
2855
|
+
config = (0, config_1.loadConfig)({});
|
|
2856
|
+
}
|
|
2857
|
+
const checks = [];
|
|
2858
|
+
// Check 1: Signing enabled
|
|
2859
|
+
const signingEnabled = config.signing?.enabled === true;
|
|
2860
|
+
checks.push({
|
|
2861
|
+
name: 'signing_enabled',
|
|
2862
|
+
passed: signingEnabled,
|
|
2863
|
+
message: signingEnabled
|
|
2864
|
+
? 'Event signing is enabled'
|
|
2865
|
+
: 'Event signing is disabled - events cannot be verified for authenticity',
|
|
2866
|
+
fix: signingEnabled ? undefined : 'Set signing.enabled: true and configure signing.key_env or signing.key_file',
|
|
2867
|
+
});
|
|
2868
|
+
// Check 2: WAL enabled
|
|
2869
|
+
const walEnabled = config.wal?.enabled === true;
|
|
2870
|
+
checks.push({
|
|
2871
|
+
name: 'wal_enabled',
|
|
2872
|
+
passed: walEnabled,
|
|
2873
|
+
message: walEnabled
|
|
2874
|
+
? 'Write-ahead log is enabled for crash resilience'
|
|
2875
|
+
: 'Write-ahead log is disabled - events may be lost on crash',
|
|
2876
|
+
fix: walEnabled ? undefined : 'Set wal.enabled: true',
|
|
2877
|
+
});
|
|
2878
|
+
// Check 3: Hash chain verification
|
|
2879
|
+
const verifyOnShutdown = config.immutability?.verify_on_shutdown === true;
|
|
2880
|
+
const verifyOnEmit = config.immutability?.verify_on_emit === true;
|
|
2881
|
+
const hasVerification = verifyOnShutdown || verifyOnEmit;
|
|
2882
|
+
checks.push({
|
|
2883
|
+
name: 'hash_verification',
|
|
2884
|
+
passed: hasVerification,
|
|
2885
|
+
message: hasVerification
|
|
2886
|
+
? `Hash chain verification enabled (on_emit: ${verifyOnEmit}, on_shutdown: ${verifyOnShutdown})`
|
|
2887
|
+
: 'Hash chain verification is disabled - tampering may go undetected',
|
|
2888
|
+
fix: hasVerification ? undefined : 'Set immutability.verify_on_shutdown: true',
|
|
2889
|
+
});
|
|
2890
|
+
// Check 4: No stdout sink in production
|
|
2891
|
+
const sinks = config.sinks || [];
|
|
2892
|
+
const hasStdoutSink = sinks.some((s) => s.type === 'stdout');
|
|
2893
|
+
const stdoutOk = envDetection.environment !== 'production' || !hasStdoutSink;
|
|
2894
|
+
checks.push({
|
|
2895
|
+
name: 'no_stdout_in_production',
|
|
2896
|
+
passed: stdoutOk,
|
|
2897
|
+
message: stdoutOk
|
|
2898
|
+
? hasStdoutSink
|
|
2899
|
+
? 'Stdout sink is acceptable in non-production environment'
|
|
2900
|
+
: 'No stdout sink configured'
|
|
2901
|
+
: 'Stdout sink detected in production - events may be lost',
|
|
2902
|
+
fix: stdoutOk ? undefined : 'Replace stdout sink with file or https sink for production',
|
|
2903
|
+
});
|
|
2904
|
+
// Check 5: Has persistent sink
|
|
2905
|
+
const hasPersistentSink = sinks.some((s) => s.type === 'file' || s.type === 'https');
|
|
2906
|
+
checks.push({
|
|
2907
|
+
name: 'persistent_sink',
|
|
2908
|
+
passed: hasPersistentSink,
|
|
2909
|
+
message: hasPersistentSink
|
|
2910
|
+
? 'Persistent sink (file or https) is configured'
|
|
2911
|
+
: 'No persistent sink configured - events will not be stored',
|
|
2912
|
+
fix: hasPersistentSink ? undefined : 'Add a file or https sink to persist events',
|
|
2913
|
+
});
|
|
2914
|
+
// Check 6: Environment explicitly set
|
|
2915
|
+
const envExplicit = config.defaults?.environment && config.defaults.environment !== 'dev';
|
|
2916
|
+
checks.push({
|
|
2917
|
+
name: 'environment_set',
|
|
2918
|
+
passed: !!envExplicit,
|
|
2919
|
+
message: envExplicit
|
|
2920
|
+
? `Environment explicitly set to: ${config.defaults?.environment}`
|
|
2921
|
+
: 'Environment defaults to "dev" - consider setting explicitly',
|
|
2922
|
+
fix: envExplicit ? undefined : 'Set defaults.environment to your target environment',
|
|
2923
|
+
});
|
|
2924
|
+
// Check 7: Service name set
|
|
2925
|
+
const hasServiceName = !!config.defaults?.service_name;
|
|
2926
|
+
checks.push({
|
|
2927
|
+
name: 'service_name_set',
|
|
2928
|
+
passed: hasServiceName,
|
|
2929
|
+
message: hasServiceName
|
|
2930
|
+
? `Service name set: ${config.defaults?.service_name}`
|
|
2931
|
+
: 'Service name not set - events will be harder to identify',
|
|
2932
|
+
fix: hasServiceName ? undefined : 'Set defaults.service_name to identify your service',
|
|
2933
|
+
});
|
|
2934
|
+
// Build result
|
|
2935
|
+
const passedCount = checks.filter((c) => c.passed).length;
|
|
2936
|
+
const result = {
|
|
2937
|
+
passed: passedCount === checks.length,
|
|
2938
|
+
environment: {
|
|
2939
|
+
detected: envDetection.environment,
|
|
2940
|
+
source: envDetection.source,
|
|
2941
|
+
confidence: envDetection.confidence,
|
|
2942
|
+
suggested_preset: suggestedPreset,
|
|
2943
|
+
},
|
|
2944
|
+
checks,
|
|
2945
|
+
summary: {
|
|
2946
|
+
total: checks.length,
|
|
2947
|
+
passed: passedCount,
|
|
2948
|
+
failed: checks.length - passedCount,
|
|
2949
|
+
},
|
|
2950
|
+
};
|
|
2951
|
+
if (jsonOutput) {
|
|
2952
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2953
|
+
}
|
|
2954
|
+
else {
|
|
2955
|
+
console.log('\n=== Monora Production Readiness Check ===\n');
|
|
2956
|
+
console.log('Environment Detection:');
|
|
2957
|
+
console.log(` Detected: ${result.environment.detected}`);
|
|
2958
|
+
console.log(` Source: ${result.environment.source}`);
|
|
2959
|
+
console.log(` Confidence: ${result.environment.confidence}`);
|
|
2960
|
+
console.log(` Suggested preset: ${result.environment.suggested_preset}`);
|
|
2961
|
+
console.log('');
|
|
2962
|
+
console.log('Checks:');
|
|
2963
|
+
for (const check of checks) {
|
|
2964
|
+
const status = check.passed ? '✓' : '✗';
|
|
2965
|
+
const color = check.passed ? '\x1b[32m' : '\x1b[31m';
|
|
2966
|
+
const reset = '\x1b[0m';
|
|
2967
|
+
console.log(` ${color}${status}${reset} ${check.name}: ${check.message}`);
|
|
2968
|
+
if (!check.passed && showFix && check.fix) {
|
|
2969
|
+
console.log(` → Fix: ${check.fix}`);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
console.log('');
|
|
2973
|
+
console.log('Summary:');
|
|
2974
|
+
console.log(` Total: ${result.summary.total}`);
|
|
2975
|
+
console.log(` Passed: ${result.summary.passed}`);
|
|
2976
|
+
console.log(` Failed: ${result.summary.failed}`);
|
|
2977
|
+
console.log('');
|
|
2978
|
+
if (result.passed) {
|
|
2979
|
+
console.log('\x1b[32m✓ All production readiness checks passed!\x1b[0m');
|
|
2980
|
+
}
|
|
2981
|
+
else {
|
|
2982
|
+
console.log('\x1b[33m⚠ Some checks failed. Run with --fix to see remediation steps.\x1b[0m');
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
if (!result.passed) {
|
|
2986
|
+
process.exit(1);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
async function runDetectEnv(args) {
|
|
2990
|
+
const jsonOutput = hasFlag(args, '--json');
|
|
2991
|
+
const detection = (0, config_1.detectEnvironment)();
|
|
2992
|
+
const suggested = (0, config_1.suggestPreset)();
|
|
2993
|
+
if (jsonOutput) {
|
|
2994
|
+
console.log(JSON.stringify({
|
|
2995
|
+
...detection,
|
|
2996
|
+
suggested_preset: suggested,
|
|
2997
|
+
}, null, 2));
|
|
2998
|
+
}
|
|
2999
|
+
else {
|
|
3000
|
+
console.log(`Environment: ${detection.environment}`);
|
|
3001
|
+
console.log(`Source: ${detection.source}`);
|
|
3002
|
+
console.log(`Confidence: ${detection.confidence}`);
|
|
3003
|
+
console.log(`Suggested preset: ${suggested}`);
|
|
3004
|
+
if (Object.keys(detection.context || {}).length > 0) {
|
|
3005
|
+
console.log('Context:');
|
|
3006
|
+
for (const [key, value] of Object.entries(detection.context || {})) {
|
|
3007
|
+
console.log(` ${key}: ${value}`);
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
function diffConfigs(from, to, prefix = '') {
|
|
3013
|
+
const diffs = [];
|
|
3014
|
+
const allKeys = new Set([...Object.keys(from), ...Object.keys(to)]);
|
|
3015
|
+
for (const key of allKeys) {
|
|
3016
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
3017
|
+
const fromVal = from[key];
|
|
3018
|
+
const toVal = to[key];
|
|
3019
|
+
if (fromVal === undefined && toVal !== undefined) {
|
|
3020
|
+
diffs.push({ path, from: undefined, to: toVal, action: 'add' });
|
|
3021
|
+
}
|
|
3022
|
+
else if (fromVal !== undefined && toVal === undefined) {
|
|
3023
|
+
diffs.push({ path, from: fromVal, to: undefined, action: 'remove' });
|
|
3024
|
+
}
|
|
3025
|
+
else if (typeof fromVal === 'object' && typeof toVal === 'object' && fromVal !== null && toVal !== null && !Array.isArray(fromVal) && !Array.isArray(toVal)) {
|
|
3026
|
+
diffs.push(...diffConfigs(fromVal, toVal, path));
|
|
3027
|
+
}
|
|
3028
|
+
else if (JSON.stringify(fromVal) !== JSON.stringify(toVal)) {
|
|
3029
|
+
diffs.push({ path, from: fromVal, to: toVal, action: 'change' });
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
return diffs;
|
|
3033
|
+
}
|
|
3034
|
+
const VALID_PRESETS = [
|
|
3035
|
+
'minimal', 'dev', 'development', 'production', 'compliance', 'poc',
|
|
3036
|
+
'strict_enterprise', 'default_secure', 'experimental', 'audit_first', 'low_latency'
|
|
3037
|
+
];
|
|
3038
|
+
async function runUpgradePreset(args) {
|
|
3039
|
+
const fromPreset = getFlagValue(args, '--from');
|
|
3040
|
+
const toPreset = getFlagValue(args, '--to');
|
|
3041
|
+
const outputPath = getFlagValue(args, '--output');
|
|
3042
|
+
const jsonOutput = hasFlag(args, '--json');
|
|
3043
|
+
if (!fromPreset || !toPreset) {
|
|
3044
|
+
console.error('Error: --from and --to presets are required');
|
|
3045
|
+
console.error('Usage: monora upgrade-preset --from <preset> --to <preset>');
|
|
3046
|
+
console.error(`Valid presets: ${VALID_PRESETS.join(', ')}`);
|
|
3047
|
+
process.exit(1);
|
|
3048
|
+
}
|
|
3049
|
+
if (!VALID_PRESETS.includes(fromPreset)) {
|
|
3050
|
+
console.error(`Error: Invalid --from preset '${fromPreset}'`);
|
|
3051
|
+
console.error(`Valid presets: ${VALID_PRESETS.join(', ')}`);
|
|
3052
|
+
process.exit(1);
|
|
3053
|
+
}
|
|
3054
|
+
if (!VALID_PRESETS.includes(toPreset)) {
|
|
3055
|
+
console.error(`Error: Invalid --to preset '${toPreset}'`);
|
|
3056
|
+
console.error(`Valid presets: ${VALID_PRESETS.join(', ')}`);
|
|
3057
|
+
process.exit(1);
|
|
3058
|
+
}
|
|
3059
|
+
const fromConfig = (0, config_1.getPresetConfig)(fromPreset);
|
|
3060
|
+
const toConfig = (0, config_1.getPresetConfig)(toPreset);
|
|
3061
|
+
const diffs = diffConfigs(fromConfig, toConfig);
|
|
3062
|
+
const result = {
|
|
3063
|
+
from_preset: fromPreset,
|
|
3064
|
+
to_preset: toPreset,
|
|
3065
|
+
changes: diffs.map(d => ({
|
|
3066
|
+
path: d.path,
|
|
3067
|
+
action: d.action,
|
|
3068
|
+
from: d.from,
|
|
3069
|
+
to: d.to,
|
|
3070
|
+
})),
|
|
3071
|
+
summary: {
|
|
3072
|
+
total: diffs.length,
|
|
3073
|
+
added: diffs.filter(d => d.action === 'add').length,
|
|
3074
|
+
removed: diffs.filter(d => d.action === 'remove').length,
|
|
3075
|
+
changed: diffs.filter(d => d.action === 'change').length,
|
|
3076
|
+
},
|
|
3077
|
+
};
|
|
3078
|
+
if (jsonOutput) {
|
|
3079
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3080
|
+
}
|
|
3081
|
+
else {
|
|
3082
|
+
console.log(`\n=== Upgrade from '${fromPreset}' to '${toPreset}' ===\n`);
|
|
3083
|
+
if (diffs.length === 0) {
|
|
3084
|
+
console.log('No configuration changes required.');
|
|
3085
|
+
}
|
|
3086
|
+
else {
|
|
3087
|
+
console.log('Configuration changes:\n');
|
|
3088
|
+
for (const diff of diffs) {
|
|
3089
|
+
const actionColor = diff.action === 'add' ? '\x1b[32m+' : diff.action === 'remove' ? '\x1b[31m-' : '\x1b[33m~';
|
|
3090
|
+
const reset = '\x1b[0m';
|
|
3091
|
+
console.log(`${actionColor} ${diff.path}${reset}`);
|
|
3092
|
+
if (diff.action === 'change') {
|
|
3093
|
+
console.log(` from: ${JSON.stringify(diff.from)}`);
|
|
3094
|
+
console.log(` to: ${JSON.stringify(diff.to)}`);
|
|
3095
|
+
}
|
|
3096
|
+
else if (diff.action === 'add') {
|
|
3097
|
+
console.log(` value: ${JSON.stringify(diff.to)}`);
|
|
3098
|
+
}
|
|
3099
|
+
else if (diff.action === 'remove') {
|
|
3100
|
+
console.log(` was: ${JSON.stringify(diff.from)}`);
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
console.log(`\nSummary: ${result.summary.total} changes`);
|
|
3104
|
+
console.log(` Added: ${result.summary.added}`);
|
|
3105
|
+
console.log(` Removed: ${result.summary.removed}`);
|
|
3106
|
+
console.log(` Changed: ${result.summary.changed}`);
|
|
3107
|
+
}
|
|
3108
|
+
if (outputPath) {
|
|
3109
|
+
const ext = path.extname(outputPath).toLowerCase();
|
|
3110
|
+
const content = ext === '.json' ? JSON.stringify(toConfig, null, 2) : yaml.dump(toConfig);
|
|
3111
|
+
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
3112
|
+
console.log(`\nWrote '${toPreset}' preset configuration to: ${outputPath}`);
|
|
3113
|
+
}
|
|
3114
|
+
else {
|
|
3115
|
+
console.log('\nTo apply this preset, run:');
|
|
3116
|
+
console.log(` monora upgrade-preset --from ${fromPreset} --to ${toPreset} --output monora.yml`);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
1788
3120
|
async function main() {
|
|
1789
3121
|
const parsed = parseArgs(process.argv);
|
|
1790
3122
|
if (!parsed.command || parsed.command === '--help' || parsed.command === '-h') {
|
|
@@ -1799,6 +3131,15 @@ async function main() {
|
|
|
1799
3131
|
await runDoctor(parsed.args);
|
|
1800
3132
|
return;
|
|
1801
3133
|
}
|
|
3134
|
+
if (parsed.command === 'config') {
|
|
3135
|
+
const subcommand = parsed.args[0];
|
|
3136
|
+
if (subcommand === 'fix') {
|
|
3137
|
+
await runConfigFix(parsed.args.slice(1));
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
usage();
|
|
3141
|
+
process.exit(1);
|
|
3142
|
+
}
|
|
1802
3143
|
if (parsed.command === 'init') {
|
|
1803
3144
|
const options = parseInitOptions(parsed.args);
|
|
1804
3145
|
if (fs.existsSync(options.path) && !options.force && !options.yes) {
|
|
@@ -1810,7 +3151,12 @@ async function main() {
|
|
|
1810
3151
|
process.exit(1);
|
|
1811
3152
|
}
|
|
1812
3153
|
}
|
|
1813
|
-
await runInitWizard(options.path, options.format, options.yes, options.force);
|
|
3154
|
+
await runInitWizard(options.path, options.format, options.yes, options.force, options.advanced, options.preset);
|
|
3155
|
+
return;
|
|
3156
|
+
}
|
|
3157
|
+
// New 'start' command for zero-code instrumentation
|
|
3158
|
+
if (parsed.command === 'start') {
|
|
3159
|
+
await runStart(parsed.args);
|
|
1814
3160
|
return;
|
|
1815
3161
|
}
|
|
1816
3162
|
if (parsed.command === 'report') {
|
|
@@ -1829,6 +3175,10 @@ async function main() {
|
|
|
1829
3175
|
await runTrustPackage(parseTrustPackageOptions(parsed.args));
|
|
1830
3176
|
return;
|
|
1831
3177
|
}
|
|
3178
|
+
if (parsed.command === 'aims') {
|
|
3179
|
+
await runAims(parsed.args);
|
|
3180
|
+
return;
|
|
3181
|
+
}
|
|
1832
3182
|
if (parsed.command === 'ai-act-report') {
|
|
1833
3183
|
await runAIActReport(parseAIActReportOptions(parsed.args));
|
|
1834
3184
|
return;
|
|
@@ -1869,6 +3219,30 @@ async function main() {
|
|
|
1869
3219
|
await runStandardsCheck(parsed.args);
|
|
1870
3220
|
return;
|
|
1871
3221
|
}
|
|
3222
|
+
if (parsed.command === 'onboard') {
|
|
3223
|
+
await runOnboard(parsed.args);
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
if (parsed.command === 'schema') {
|
|
3227
|
+
await runSchema(parsed.args);
|
|
3228
|
+
return;
|
|
3229
|
+
}
|
|
3230
|
+
if (parsed.command === 'model') {
|
|
3231
|
+
await runModel(parsed.args);
|
|
3232
|
+
return;
|
|
3233
|
+
}
|
|
3234
|
+
if (parsed.command === 'production-check') {
|
|
3235
|
+
await runProductionCheck(parsed.args);
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
if (parsed.command === 'detect-env') {
|
|
3239
|
+
await runDetectEnv(parsed.args);
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
if (parsed.command === 'upgrade-preset') {
|
|
3243
|
+
await runUpgradePreset(parsed.args);
|
|
3244
|
+
return;
|
|
3245
|
+
}
|
|
1872
3246
|
usage();
|
|
1873
3247
|
}
|
|
1874
3248
|
main().catch((err) => {
|