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.
Files changed (184) hide show
  1. package/README.md +333 -159
  2. package/dist/aims_governance.d.ts +238 -0
  3. package/dist/aims_governance.d.ts.map +1 -0
  4. package/dist/aims_governance.js +922 -0
  5. package/dist/alerts.d.ts +16 -0
  6. package/dist/alerts.d.ts.map +1 -1
  7. package/dist/alerts.js +16 -0
  8. package/dist/api.d.ts +6 -0
  9. package/dist/api.d.ts.map +1 -1
  10. package/dist/api.js +6 -0
  11. package/dist/assessment.d.ts +85 -0
  12. package/dist/assessment.d.ts.map +1 -1
  13. package/dist/assessment.js +506 -13
  14. package/dist/attribution.d.ts +44 -3
  15. package/dist/attribution.d.ts.map +1 -1
  16. package/dist/attribution.js +197 -10
  17. package/dist/autodetect.d.ts +68 -0
  18. package/dist/autodetect.d.ts.map +1 -1
  19. package/dist/autodetect.js +639 -0
  20. package/dist/bias.d.ts +130 -0
  21. package/dist/bias.d.ts.map +1 -0
  22. package/dist/bias.js +223 -0
  23. package/dist/cli/diagnostics.d.ts +5 -1
  24. package/dist/cli/diagnostics.d.ts.map +1 -1
  25. package/dist/cli/diagnostics.js +23 -6
  26. package/dist/cli/doctor.d.ts +25 -0
  27. package/dist/cli/doctor.d.ts.map +1 -0
  28. package/dist/cli/doctor.js +381 -0
  29. package/dist/cli/fix.d.ts +16 -0
  30. package/dist/cli/fix.d.ts.map +1 -0
  31. package/dist/cli/fix.js +284 -0
  32. package/dist/cli/init.d.ts +57 -0
  33. package/dist/cli/init.d.ts.map +1 -0
  34. package/dist/cli/init.js +205 -0
  35. package/dist/cli.js +1550 -176
  36. package/dist/complianceTargets.d.ts +111 -0
  37. package/dist/complianceTargets.d.ts.map +1 -0
  38. package/dist/complianceTargets.js +521 -0
  39. package/dist/config.d.ts +261 -16
  40. package/dist/config.d.ts.map +1 -1
  41. package/dist/config.js +381 -32
  42. package/dist/config_migrations.d.ts.map +1 -1
  43. package/dist/config_migrations.js +38 -1
  44. package/dist/config_schema.d.ts +2490 -1035
  45. package/dist/config_schema.d.ts.map +1 -1
  46. package/dist/config_schema.js +233 -64
  47. package/dist/context.d.ts +34 -0
  48. package/dist/context.d.ts.map +1 -1
  49. package/dist/context.js +118 -7
  50. package/dist/control_backbone.d.ts +122 -0
  51. package/dist/control_backbone.d.ts.map +1 -0
  52. package/dist/control_backbone.js +698 -0
  53. package/dist/data-governance.d.ts +187 -0
  54. package/dist/data-governance.d.ts.map +1 -0
  55. package/dist/data-governance.js +424 -0
  56. package/dist/dataResidency.d.ts +44 -0
  57. package/dist/dataResidency.d.ts.map +1 -0
  58. package/dist/dataResidency.js +203 -0
  59. package/dist/dispatcher.d.ts.map +1 -1
  60. package/dist/dispatcher.js +17 -5
  61. package/dist/evidence_store.d.ts +103 -0
  62. package/dist/evidence_store.d.ts.map +1 -0
  63. package/dist/evidence_store.js +459 -0
  64. package/dist/executiveSummary.d.ts +15 -0
  65. package/dist/executiveSummary.d.ts.map +1 -1
  66. package/dist/executiveSummary.js +135 -22
  67. package/dist/identity.d.ts +143 -0
  68. package/dist/identity.d.ts.map +1 -0
  69. package/dist/identity.js +231 -0
  70. package/dist/impact-assessment.d.ts +350 -0
  71. package/dist/impact-assessment.d.ts.map +1 -0
  72. package/dist/impact-assessment.js +580 -0
  73. package/dist/index.d.ts +20 -4
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +247 -5
  76. package/dist/instrumentation.d.ts +1 -1
  77. package/dist/instrumentation.d.ts.map +1 -1
  78. package/dist/instrumentation.js +123 -22
  79. package/dist/integrations/anthropic.d.ts +3 -0
  80. package/dist/integrations/anthropic.d.ts.map +1 -1
  81. package/dist/integrations/anthropic.js +282 -80
  82. package/dist/integrations/governance.d.ts +33 -0
  83. package/dist/integrations/governance.d.ts.map +1 -0
  84. package/dist/integrations/governance.js +208 -0
  85. package/dist/integrations/langchain.d.ts +4 -0
  86. package/dist/integrations/langchain.d.ts.map +1 -1
  87. package/dist/integrations/langchain.js +362 -142
  88. package/dist/integrations/openai.d.ts +9 -0
  89. package/dist/integrations/openai.d.ts.map +1 -1
  90. package/dist/integrations/openai.js +673 -73
  91. package/dist/iso42001_consolidation.d.ts +16 -0
  92. package/dist/iso42001_consolidation.d.ts.map +1 -0
  93. package/dist/iso42001_consolidation.js +413 -0
  94. package/dist/iso42001_workflows.d.ts +263 -0
  95. package/dist/iso42001_workflows.d.ts.map +1 -0
  96. package/dist/iso42001_workflows.js +781 -0
  97. package/dist/lifecycle.d.ts +299 -0
  98. package/dist/lifecycle.d.ts.map +1 -0
  99. package/dist/lifecycle.js +624 -0
  100. package/dist/lineage.d.ts +2 -2
  101. package/dist/lineage.d.ts.map +1 -1
  102. package/dist/lineage.js +9 -16
  103. package/dist/middleware/express.d.ts.map +1 -1
  104. package/dist/middleware/express.js +18 -3
  105. package/dist/middleware/nextjs.js +2 -2
  106. package/dist/model.d.ts +143 -0
  107. package/dist/model.d.ts.map +1 -0
  108. package/dist/model.js +371 -0
  109. package/dist/onboarding.d.ts +42 -0
  110. package/dist/onboarding.d.ts.map +1 -0
  111. package/dist/onboarding.js +1022 -0
  112. package/dist/oversight.d.ts +264 -0
  113. package/dist/oversight.d.ts.map +1 -0
  114. package/dist/oversight.js +497 -0
  115. package/dist/presets.js +7 -7
  116. package/dist/quotas.d.ts +171 -0
  117. package/dist/quotas.d.ts.map +1 -0
  118. package/dist/quotas.js +259 -0
  119. package/dist/register.d.ts +13 -0
  120. package/dist/register.d.ts.map +1 -0
  121. package/dist/register.js +99 -0
  122. package/dist/registry.d.ts +1 -0
  123. package/dist/registry.d.ts.map +1 -1
  124. package/dist/registry.js +7 -0
  125. package/dist/registryData.json +43 -6
  126. package/dist/report.d.ts +2 -1
  127. package/dist/report.d.ts.map +1 -1
  128. package/dist/report.js +189 -2
  129. package/dist/reporting.d.ts +125 -0
  130. package/dist/reporting.d.ts.map +1 -1
  131. package/dist/reporting.js +192 -2
  132. package/dist/resources.d.ts +285 -0
  133. package/dist/resources.d.ts.map +1 -0
  134. package/dist/resources.js +643 -0
  135. package/dist/risk.d.ts +120 -0
  136. package/dist/risk.d.ts.map +1 -0
  137. package/dist/risk.js +220 -0
  138. package/dist/runtime.d.ts +73 -0
  139. package/dist/runtime.d.ts.map +1 -1
  140. package/dist/runtime.js +415 -18
  141. package/dist/schemaInference.d.ts +92 -0
  142. package/dist/schemaInference.d.ts.map +1 -0
  143. package/dist/schemaInference.js +466 -0
  144. package/dist/schema_validation.js +2 -2
  145. package/dist/schemas/config.schema.json +118 -4
  146. package/dist/security_report.js +4 -4
  147. package/dist/signing.d.ts +1 -1
  148. package/dist/signing.d.ts.map +1 -1
  149. package/dist/signing.js +4 -0
  150. package/dist/sinks/file.d.ts +19 -1
  151. package/dist/sinks/file.d.ts.map +1 -1
  152. package/dist/sinks/file.js +82 -13
  153. package/dist/sinks/https.d.ts +10 -0
  154. package/dist/sinks/https.d.ts.map +1 -1
  155. package/dist/sinks/https.js +76 -16
  156. package/dist/sinks/stdout.d.ts +1 -0
  157. package/dist/sinks/stdout.d.ts.map +1 -1
  158. package/dist/sinks/stdout.js +12 -1
  159. package/dist/spec.d.ts +159 -0
  160. package/dist/spec.d.ts.map +1 -0
  161. package/dist/spec.js +391 -0
  162. package/dist/stakeholders.d.ts +199 -0
  163. package/dist/stakeholders.d.ts.map +1 -0
  164. package/dist/stakeholders.js +398 -0
  165. package/dist/standards.d.ts.map +1 -1
  166. package/dist/standards.js +160 -2
  167. package/dist/standards_ingest.d.ts.map +1 -1
  168. package/dist/standards_ingest.js +1 -4
  169. package/dist/telemetry.d.ts +16 -2
  170. package/dist/telemetry.d.ts.map +1 -1
  171. package/dist/telemetry.js +77 -14
  172. package/dist/templates/controls/iso42001_control_catalog.json +1443 -0
  173. package/dist/traced_emitter.d.ts.map +1 -1
  174. package/dist/traced_emitter.js +19 -9
  175. package/dist/trust_package.d.ts +19 -1
  176. package/dist/trust_package.d.ts.map +1 -1
  177. package/dist/trust_package.js +89 -2
  178. package/dist/verify.d.ts.map +1 -1
  179. package/dist/verify.js +9 -2
  180. package/dist/wal.d.ts.map +1 -1
  181. package/dist/wal.js +2 -1
  182. package/package.json +14 -1
  183. package/scripts/postinstall.js +105 -210
  184. package/templates/controls/iso42001_control_catalog.json +1443 -0
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Auto-detection utilities for zero-config initialization.
4
+ *
5
+ * Provides intelligent project analysis for streamlined setup:
6
+ * - Framework detection (Next.js, Express, FastAPI, etc.)
7
+ * - Cloud platform detection (Vercel, AWS, GCP, etc.)
8
+ * - AI SDK detection with model suggestions
9
+ * - Optional AI-powered analysis using Claude/GPT
4
10
  */
5
11
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
12
  if (k2 === undefined) k2 = k;
@@ -41,8 +47,162 @@ exports.detectEnvironment = detectEnvironment;
41
47
  exports.detectServiceName = detectServiceName;
42
48
  exports.autoDetectConfig = autoDetectConfig;
43
49
  exports.selectPresetForEnvironment = selectPresetForEnvironment;
50
+ exports.detectFramework = detectFramework;
51
+ exports.analyzeProjectWithAI = analyzeProjectWithAI;
52
+ exports.buildSmartConfig = buildSmartConfig;
44
53
  const fs = __importStar(require("fs"));
45
54
  const path = __importStar(require("path"));
55
+ const logger_1 = require("./logger");
56
+ const AI_ANALYSIS_ENV_FLAG = 'MONORA_AI_ANALYSIS_ENABLED';
57
+ const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
58
+ const SECRET_KEY_PATTERN = /api[_-]?key|token|secret|password|authorization|bearer|private[_-]?key|client[_-]?secret|access[_-]?key/i;
59
+ const ALLOWED_SUGGESTED_CONFIG_KEYS = new Set([
60
+ 'version',
61
+ 'config_version',
62
+ 'preset',
63
+ 'defaults',
64
+ 'sinks',
65
+ 'immutability',
66
+ 'attestation',
67
+ 'registry',
68
+ 'instrumentation',
69
+ 'reporting',
70
+ 'data_handling',
71
+ 'policies',
72
+ 'alerts',
73
+ 'error_handling',
74
+ 'buffering',
75
+ 'identity',
76
+ 'risk_register',
77
+ 'bias',
78
+ 'human_oversight',
79
+ 'tracing',
80
+ 'telemetry',
81
+ 'redaction',
82
+ 'data_residency',
83
+ 'data_residency_action',
84
+ 'wal',
85
+ 'signing',
86
+ 'ai_act',
87
+ ]);
88
+ function isAiAnalysisEnabled() {
89
+ const value = String(process.env[AI_ANALYSIS_ENV_FLAG] || '').toLowerCase();
90
+ return value === 'true' || value === '1' || value === 'yes';
91
+ }
92
+ function isPlainObject(value) {
93
+ if (!value || typeof value !== 'object') {
94
+ return false;
95
+ }
96
+ const proto = Object.getPrototypeOf(value);
97
+ return proto === Object.prototype || proto === null;
98
+ }
99
+ function redactSensitiveStrings(input) {
100
+ if (!input) {
101
+ return input;
102
+ }
103
+ let output = input;
104
+ output = output.replace(/(api[_-]?key|token|secret|password|authorization|bearer|private[_-]?key)\s*([:=]\s*)(['"`]?)[^\s'"`]+\3/gi, '$1$2$3[REDACTED]$3');
105
+ output = output.replace(/(['"`])(sk-[A-Za-z0-9]{10,}|AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|ghp_[0-9A-Za-z]{36}|xox[baprs]-[0-9A-Za-z-]{10,}|ya29\.[0-9A-Za-z_-]+)\1/g, '$1[REDACTED]$1');
106
+ return output;
107
+ }
108
+ function sanitizeJsonValue(value, depth = 0) {
109
+ if (depth > 6) {
110
+ return '[REDACTED]';
111
+ }
112
+ if (value === null || typeof value === 'number' || typeof value === 'boolean') {
113
+ return value;
114
+ }
115
+ if (typeof value === 'string') {
116
+ return redactSensitiveStrings(value);
117
+ }
118
+ if (Array.isArray(value)) {
119
+ return value.map((item) => sanitizeJsonValue(item, depth + 1));
120
+ }
121
+ if (isPlainObject(value)) {
122
+ const output = {};
123
+ for (const [key, child] of Object.entries(value)) {
124
+ if (UNSAFE_KEYS.has(key)) {
125
+ continue;
126
+ }
127
+ if (SECRET_KEY_PATTERN.test(key)) {
128
+ output[key] = '[REDACTED]';
129
+ continue;
130
+ }
131
+ const sanitized = sanitizeJsonValue(child, depth + 1);
132
+ output[key] = sanitized;
133
+ }
134
+ return output;
135
+ }
136
+ return '[REDACTED]';
137
+ }
138
+ function sanitizePackageJsonContent(content) {
139
+ if (!content) {
140
+ return '';
141
+ }
142
+ try {
143
+ const parsed = JSON.parse(content);
144
+ const sanitized = sanitizeJsonValue(parsed);
145
+ return redactSensitiveStrings(JSON.stringify(sanitized, null, 2));
146
+ }
147
+ catch {
148
+ return redactSensitiveStrings(content);
149
+ }
150
+ }
151
+ function sanitizeSuggestedValue(value, depth = 0) {
152
+ if (depth > 6) {
153
+ return undefined;
154
+ }
155
+ if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
156
+ return value;
157
+ }
158
+ if (typeof value === 'function' || Array.isArray(value)) {
159
+ return undefined;
160
+ }
161
+ if (isPlainObject(value)) {
162
+ const output = {};
163
+ for (const [key, child] of Object.entries(value)) {
164
+ if (UNSAFE_KEYS.has(key)) {
165
+ continue;
166
+ }
167
+ const sanitized = sanitizeSuggestedValue(child, depth + 1);
168
+ if (sanitized !== undefined) {
169
+ output[key] = sanitized;
170
+ }
171
+ }
172
+ return output;
173
+ }
174
+ return undefined;
175
+ }
176
+ function sanitizeSuggestedConfig(input) {
177
+ if (!isPlainObject(input)) {
178
+ return {};
179
+ }
180
+ const output = {};
181
+ for (const [key, value] of Object.entries(input)) {
182
+ if (UNSAFE_KEYS.has(key) || !ALLOWED_SUGGESTED_CONFIG_KEYS.has(key)) {
183
+ continue;
184
+ }
185
+ const sanitized = sanitizeSuggestedValue(value);
186
+ if (sanitized !== undefined) {
187
+ output[key] = sanitized;
188
+ }
189
+ }
190
+ return output;
191
+ }
192
+ function safeDeepMerge(base, updates) {
193
+ for (const [key, value] of Object.entries(updates)) {
194
+ if (UNSAFE_KEYS.has(key)) {
195
+ continue;
196
+ }
197
+ if (isPlainObject(value) && isPlainObject(base[key])) {
198
+ safeDeepMerge(base[key], value);
199
+ continue;
200
+ }
201
+ if (value !== undefined && !Array.isArray(value) && typeof value !== 'function') {
202
+ base[key] = value;
203
+ }
204
+ }
205
+ }
46
206
  /**
47
207
  * Detect which LLM SDKs are installed.
48
208
  */
@@ -209,3 +369,482 @@ function selectPresetForEnvironment(env) {
209
369
  };
210
370
  return presetMap[env.toLowerCase()] || 'development';
211
371
  }
372
+ /**
373
+ * Detect framework, cloud platform, and AI SDKs from project files.
374
+ *
375
+ * This performs comprehensive project analysis without requiring user input.
376
+ */
377
+ function detectFramework() {
378
+ const cwd = process.cwd();
379
+ const result = {
380
+ framework: null,
381
+ projectType: 'unknown',
382
+ cloudPlatform: null,
383
+ aiSdks: [],
384
+ suggestedModels: [],
385
+ runtime: 'node',
386
+ entryPoint: null,
387
+ typescript: false,
388
+ packageManager: 'npm',
389
+ };
390
+ // Read package.json for dependency analysis
391
+ const packageJsonPath = path.join(cwd, 'package.json');
392
+ let packageJson = {};
393
+ if (fs.existsSync(packageJsonPath)) {
394
+ try {
395
+ packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
396
+ }
397
+ catch {
398
+ // Ignore parse errors
399
+ }
400
+ }
401
+ const allDeps = {
402
+ ...packageJson.dependencies,
403
+ ...packageJson.devDependencies,
404
+ };
405
+ // Detect TypeScript
406
+ result.typescript = 'typescript' in allDeps || fs.existsSync(path.join(cwd, 'tsconfig.json'));
407
+ // Detect package manager
408
+ if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {
409
+ result.packageManager = 'pnpm';
410
+ }
411
+ else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {
412
+ result.packageManager = 'yarn';
413
+ }
414
+ else if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {
415
+ result.packageManager = 'bun';
416
+ }
417
+ // Detect framework from config files and dependencies
418
+ const frameworkDetectors = [
419
+ // Next.js
420
+ {
421
+ check: () => fs.existsSync(path.join(cwd, 'next.config.js')) ||
422
+ fs.existsSync(path.join(cwd, 'next.config.mjs')) ||
423
+ fs.existsSync(path.join(cwd, 'next.config.ts')) ||
424
+ 'next' in allDeps,
425
+ framework: 'next',
426
+ type: 'web-app',
427
+ },
428
+ // Nuxt
429
+ {
430
+ check: () => fs.existsSync(path.join(cwd, 'nuxt.config.js')) ||
431
+ fs.existsSync(path.join(cwd, 'nuxt.config.ts')) ||
432
+ 'nuxt' in allDeps,
433
+ framework: 'nuxt',
434
+ type: 'web-app',
435
+ },
436
+ // Remix
437
+ {
438
+ check: () => '@remix-run/node' in allDeps || '@remix-run/react' in allDeps,
439
+ framework: 'remix',
440
+ type: 'web-app',
441
+ },
442
+ // NestJS
443
+ {
444
+ check: () => '@nestjs/core' in allDeps,
445
+ framework: 'nest',
446
+ type: 'api',
447
+ },
448
+ // Fastify
449
+ {
450
+ check: () => 'fastify' in allDeps,
451
+ framework: 'fastify',
452
+ type: 'api',
453
+ },
454
+ // Express
455
+ {
456
+ check: () => 'express' in allDeps,
457
+ framework: 'express',
458
+ type: 'api',
459
+ },
460
+ // Koa
461
+ {
462
+ check: () => 'koa' in allDeps,
463
+ framework: 'koa',
464
+ type: 'api',
465
+ },
466
+ // Hono
467
+ {
468
+ check: () => 'hono' in allDeps,
469
+ framework: 'hono',
470
+ type: 'api',
471
+ },
472
+ ];
473
+ for (const detector of frameworkDetectors) {
474
+ if (detector.check()) {
475
+ result.framework = detector.framework;
476
+ result.projectType = detector.type;
477
+ break;
478
+ }
479
+ }
480
+ // Detect if it's a library or CLI
481
+ if (!result.framework) {
482
+ if (packageJson.bin) {
483
+ result.projectType = 'cli';
484
+ }
485
+ else if (packageJson.main || packageJson.module || packageJson.exports) {
486
+ result.projectType = 'library';
487
+ }
488
+ }
489
+ // Detect monorepo
490
+ if (fs.existsSync(path.join(cwd, 'lerna.json')) ||
491
+ fs.existsSync(path.join(cwd, 'pnpm-workspace.yaml')) ||
492
+ packageJson.workspaces) {
493
+ result.projectType = 'monorepo';
494
+ }
495
+ // Detect cloud platform
496
+ const cloudDetectors = [
497
+ { check: () => fs.existsSync(path.join(cwd, 'vercel.json')) || !!process.env.VERCEL, platform: 'vercel' },
498
+ { check: () => fs.existsSync(path.join(cwd, 'netlify.toml')) || !!process.env.NETLIFY, platform: 'netlify' },
499
+ { check: () => fs.existsSync(path.join(cwd, 'fly.toml')), platform: 'fly' },
500
+ { check: () => fs.existsSync(path.join(cwd, 'railway.json')) || !!process.env.RAILWAY_ENVIRONMENT, platform: 'railway' },
501
+ { check: () => fs.existsSync(path.join(cwd, 'render.yaml')), platform: 'render' },
502
+ { check: () => fs.existsSync(path.join(cwd, 'serverless.yml')) || fs.existsSync(path.join(cwd, 'serverless.yaml')), platform: 'aws-lambda' },
503
+ { check: () => fs.existsSync(path.join(cwd, 'app.yaml')) && fs.readFileSync(path.join(cwd, 'app.yaml'), 'utf-8').includes('runtime:'), platform: 'gcp-appengine' },
504
+ { check: () => !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.AWS_EXECUTION_ENV, platform: 'aws-lambda' },
505
+ { check: () => !!process.env.GOOGLE_CLOUD_PROJECT || !!process.env.GCP_PROJECT, platform: 'gcp' },
506
+ { check: () => !!process.env.AZURE_FUNCTIONS_ENVIRONMENT, platform: 'azure' },
507
+ { check: () => !!process.env.DYNO, platform: 'heroku' },
508
+ ];
509
+ for (const detector of cloudDetectors) {
510
+ try {
511
+ if (detector.check()) {
512
+ result.cloudPlatform = detector.platform;
513
+ break;
514
+ }
515
+ }
516
+ catch {
517
+ // Ignore file read errors
518
+ }
519
+ }
520
+ // Detect AI SDKs with versions
521
+ const aiSdkMap = {
522
+ 'openai': 'openai',
523
+ '@anthropic-ai/sdk': 'anthropic',
524
+ 'anthropic': 'anthropic',
525
+ '@ai-sdk/openai': 'vercel-ai-openai',
526
+ '@ai-sdk/anthropic': 'vercel-ai-anthropic',
527
+ 'ai': 'vercel-ai',
528
+ 'langchain': 'langchain',
529
+ '@langchain/core': 'langchain',
530
+ '@langchain/openai': 'langchain-openai',
531
+ '@langchain/anthropic': 'langchain-anthropic',
532
+ 'llamaindex': 'llamaindex',
533
+ 'cohere-ai': 'cohere',
534
+ '@google/generative-ai': 'google-genai',
535
+ '@mistralai/mistralai': 'mistral',
536
+ };
537
+ for (const [pkg, name] of Object.entries(aiSdkMap)) {
538
+ if (pkg in allDeps) {
539
+ const version = allDeps[pkg];
540
+ // Avoid duplicates (e.g., anthropic and @anthropic-ai/sdk)
541
+ if (!result.aiSdks.find(sdk => sdk.name === name)) {
542
+ result.aiSdks.push({ name, version: typeof version === 'string' ? version : undefined });
543
+ }
544
+ }
545
+ }
546
+ // Generate suggested models based on detected SDKs
547
+ // Use broad patterns to match all model variants (gpt-4, gpt-4o, gpt-4o-mini, etc.)
548
+ const sdkNames = result.aiSdks.map(sdk => sdk.name);
549
+ if (sdkNames.includes('openai') || sdkNames.includes('vercel-ai-openai') || sdkNames.includes('langchain-openai')) {
550
+ result.suggestedModels.push('gpt-*', 'o1-*', 'o3-*', 'chatgpt-*');
551
+ }
552
+ if (sdkNames.includes('anthropic') || sdkNames.includes('vercel-ai-anthropic') || sdkNames.includes('langchain-anthropic')) {
553
+ result.suggestedModels.push('claude-*');
554
+ }
555
+ if (sdkNames.includes('google-genai')) {
556
+ result.suggestedModels.push('gemini-*');
557
+ }
558
+ if (sdkNames.includes('mistral')) {
559
+ result.suggestedModels.push('mistral-*', 'codestral-*');
560
+ }
561
+ if (sdkNames.includes('cohere')) {
562
+ result.suggestedModels.push('command-*');
563
+ }
564
+ if (sdkNames.includes('deepseek')) {
565
+ result.suggestedModels.push('deepseek-*');
566
+ }
567
+ // Detect entry point
568
+ const entryPoints = [
569
+ 'src/index.ts', 'src/index.js', 'src/main.ts', 'src/main.js',
570
+ 'index.ts', 'index.js', 'server.ts', 'server.js', 'app.ts', 'app.js',
571
+ 'src/app.ts', 'src/app.js', 'src/server.ts', 'src/server.js',
572
+ ];
573
+ for (const entry of entryPoints) {
574
+ if (fs.existsSync(path.join(cwd, entry))) {
575
+ result.entryPoint = entry;
576
+ break;
577
+ }
578
+ }
579
+ // Check for Python files (mixed project)
580
+ const hasPython = fs.existsSync(path.join(cwd, 'requirements.txt')) ||
581
+ fs.existsSync(path.join(cwd, 'pyproject.toml')) ||
582
+ fs.existsSync(path.join(cwd, 'setup.py'));
583
+ if (hasPython && Object.keys(packageJson).length > 0) {
584
+ result.runtime = 'mixed';
585
+ }
586
+ else if (hasPython && Object.keys(packageJson).length === 0) {
587
+ result.runtime = 'python';
588
+ }
589
+ return result;
590
+ }
591
+ /**
592
+ * Analyze project using AI (Claude or GPT) for intelligent configuration suggestions.
593
+ *
594
+ * This is optional and requires an API key to be available.
595
+ * Returns null if no AI API key is found or analysis fails.
596
+ */
597
+ async function analyzeProjectWithAI() {
598
+ const cwd = process.cwd();
599
+ logger_1.logger.warning('AI analysis invoked; project metadata may be sent to external providers when explicitly enabled.');
600
+ if (!isAiAnalysisEnabled()) {
601
+ logger_1.logger.warning(`AI analysis is disabled. Set ${AI_ANALYSIS_ENV_FLAG}=true to enable external analysis.`);
602
+ return null;
603
+ }
604
+ // Check for available API keys
605
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
606
+ const openaiKey = process.env.OPENAI_API_KEY;
607
+ if (!anthropicKey && !openaiKey) {
608
+ return null;
609
+ }
610
+ // Gather project context
611
+ let packageJsonContent = '';
612
+ let entryFileContent = '';
613
+ let fileStructure = '';
614
+ try {
615
+ const packageJsonPath = path.join(cwd, 'package.json');
616
+ if (fs.existsSync(packageJsonPath)) {
617
+ packageJsonContent = sanitizePackageJsonContent(fs.readFileSync(packageJsonPath, 'utf-8'));
618
+ }
619
+ // Get file structure (limited depth)
620
+ fileStructure = getDirectoryStructure(cwd, 2);
621
+ // Read entry point if exists
622
+ const detection = detectFramework();
623
+ if (detection.entryPoint) {
624
+ const entryPath = path.join(cwd, detection.entryPoint);
625
+ if (fs.existsSync(entryPath)) {
626
+ const content = fs.readFileSync(entryPath, 'utf-8');
627
+ // Limit to first 200 lines
628
+ entryFileContent = redactSensitiveStrings(content.split('\n').slice(0, 200).join('\n'));
629
+ }
630
+ }
631
+ }
632
+ catch {
633
+ // Ignore file read errors
634
+ }
635
+ const prompt = `Analyze this Node.js/TypeScript project for AI governance configuration.
636
+
637
+ Project structure:
638
+ ${fileStructure}
639
+
640
+ package.json:
641
+ ${packageJsonContent.slice(0, 2000)}
642
+
643
+ Entry point (first 200 lines):
644
+ ${entryFileContent.slice(0, 3000)}
645
+
646
+ Based on this project, suggest:
647
+ 1. Which Monora preset to use (development, poc, staging, production, enterprise)
648
+ 2. Any specific configuration overrides needed
649
+ 3. Compliance requirements (SOC2, HIPAA, GDPR, ISO42001, EU_AI_ACT)
650
+
651
+ Respond in JSON format:
652
+ {
653
+ "suggestedPreset": "string",
654
+ "suggestedConfig": { "key": "value" },
655
+ "reasoning": "brief explanation",
656
+ "confidence": 0.0-1.0,
657
+ "complianceHints": ["SOC2", "GDPR"]
658
+ }`;
659
+ try {
660
+ logger_1.logger.warning('Sending sanitized project context to external AI provider for analysis.');
661
+ if (anthropicKey) {
662
+ return await callAnthropicForAnalysis(anthropicKey, prompt);
663
+ }
664
+ else if (openaiKey) {
665
+ return await callOpenAIForAnalysis(openaiKey, prompt);
666
+ }
667
+ }
668
+ catch {
669
+ // AI analysis failed, return null
670
+ return null;
671
+ }
672
+ return null;
673
+ }
674
+ /**
675
+ * Get directory structure as a string (limited depth).
676
+ */
677
+ function getDirectoryStructure(dir, maxDepth, currentDepth = 0, prefix = '') {
678
+ if (currentDepth >= maxDepth) {
679
+ return '';
680
+ }
681
+ const ignoreDirs = ['node_modules', '.git', 'dist', 'build', '.next', '__pycache__', '.venv', 'venv'];
682
+ let result = '';
683
+ try {
684
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
685
+ const filteredEntries = entries
686
+ .filter(e => !ignoreDirs.includes(e.name) && !e.name.startsWith('.'))
687
+ .slice(0, 20); // Limit entries
688
+ for (const entry of filteredEntries) {
689
+ const icon = entry.isDirectory() ? '📁' : '📄';
690
+ result += `${prefix}${icon} ${entry.name}\n`;
691
+ if (entry.isDirectory()) {
692
+ result += getDirectoryStructure(path.join(dir, entry.name), maxDepth, currentDepth + 1, prefix + ' ');
693
+ }
694
+ }
695
+ }
696
+ catch {
697
+ // Ignore permission errors
698
+ }
699
+ return result;
700
+ }
701
+ /**
702
+ * Call Anthropic API for project analysis.
703
+ */
704
+ async function callAnthropicForAnalysis(apiKey, prompt) {
705
+ try {
706
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
707
+ method: 'POST',
708
+ headers: {
709
+ 'Content-Type': 'application/json',
710
+ 'x-api-key': apiKey,
711
+ 'anthropic-version': '2023-06-01',
712
+ },
713
+ body: JSON.stringify({
714
+ model: 'claude-haiku-4-5-20251001',
715
+ max_tokens: 1024,
716
+ messages: [{ role: 'user', content: prompt }],
717
+ }),
718
+ });
719
+ if (!response.ok) {
720
+ return null;
721
+ }
722
+ const data = await response.json();
723
+ const text = data.content?.[0]?.text;
724
+ if (!text) {
725
+ return null;
726
+ }
727
+ // Extract JSON from response
728
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
729
+ if (!jsonMatch) {
730
+ return null;
731
+ }
732
+ const parsed = JSON.parse(jsonMatch[0]);
733
+ return {
734
+ suggestedPreset: parsed.suggestedPreset || 'development',
735
+ suggestedConfig: parsed.suggestedConfig || {},
736
+ reasoning: parsed.reasoning || '',
737
+ confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
738
+ complianceHints: Array.isArray(parsed.complianceHints) ? parsed.complianceHints : [],
739
+ };
740
+ }
741
+ catch {
742
+ return null;
743
+ }
744
+ }
745
+ /**
746
+ * Call OpenAI API for project analysis.
747
+ */
748
+ async function callOpenAIForAnalysis(apiKey, prompt) {
749
+ try {
750
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
751
+ method: 'POST',
752
+ headers: {
753
+ 'Content-Type': 'application/json',
754
+ 'Authorization': `Bearer ${apiKey}`,
755
+ },
756
+ body: JSON.stringify({
757
+ model: 'gpt-4o-mini',
758
+ max_tokens: 1024,
759
+ messages: [{ role: 'user', content: prompt }],
760
+ }),
761
+ });
762
+ if (!response.ok) {
763
+ return null;
764
+ }
765
+ const data = await response.json();
766
+ const text = data.choices?.[0]?.message?.content;
767
+ if (!text) {
768
+ return null;
769
+ }
770
+ // Extract JSON from response
771
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
772
+ if (!jsonMatch) {
773
+ return null;
774
+ }
775
+ const parsed = JSON.parse(jsonMatch[0]);
776
+ return {
777
+ suggestedPreset: parsed.suggestedPreset || 'development',
778
+ suggestedConfig: parsed.suggestedConfig || {},
779
+ reasoning: parsed.reasoning || '',
780
+ confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
781
+ complianceHints: Array.isArray(parsed.complianceHints) ? parsed.complianceHints : [],
782
+ };
783
+ }
784
+ catch {
785
+ return null;
786
+ }
787
+ }
788
+ /**
789
+ * Build a smart configuration based on all detection results.
790
+ */
791
+ function buildSmartConfig(detection, aiAnalysis, attribution) {
792
+ const env = detectEnvironment();
793
+ const preset = aiAnalysis?.suggestedPreset || selectPresetForEnvironment(env);
794
+ const config = {
795
+ config_version: '1.0.0',
796
+ preset,
797
+ defaults: {
798
+ service_name: detectServiceName() || 'monora-app',
799
+ environment: env,
800
+ },
801
+ // Default to file-only sink for cleaner output
802
+ // Users can add stdout sink manually if needed via: { type: 'stdout', format: 'pretty' }
803
+ sinks: [
804
+ { type: 'file', path: './monora_events.jsonl', rotation: 'none' },
805
+ ],
806
+ };
807
+ if (['dev', 'development', 'poc'].includes(env)) {
808
+ config.telemetry = { enabled: true, backend: 'minimal' };
809
+ }
810
+ // Add model allowlist from detection
811
+ if (detection.suggestedModels.length > 0) {
812
+ config.policies = {
813
+ model_allowlist: detection.suggestedModels,
814
+ enforce: true,
815
+ };
816
+ }
817
+ // Enable instrumentation for detected SDKs
818
+ if (detection.aiSdks.length > 0) {
819
+ config.instrumentation = {
820
+ enabled: true,
821
+ targets: detection.aiSdks.map(sdk => sdk.name),
822
+ };
823
+ }
824
+ // Add attribution
825
+ if (attribution.email || attribution.company || attribution.role) {
826
+ config.attribution = {
827
+ enabled: true,
828
+ project: {
829
+ email: attribution.email,
830
+ company: attribution.company,
831
+ role: attribution.role,
832
+ },
833
+ };
834
+ }
835
+ // Apply AI suggestions
836
+ if (aiAnalysis) {
837
+ const sanitizedSuggestedConfig = sanitizeSuggestedConfig(aiAnalysis.suggestedConfig);
838
+ safeDeepMerge(config, sanitizedSuggestedConfig);
839
+ // Enable compliance features based on hints
840
+ if (aiAnalysis.complianceHints.includes('SOC2')) {
841
+ config.identity = { enabled: true, require_identity: false };
842
+ config.risk_register = { enabled: true };
843
+ }
844
+ if (aiAnalysis.complianceHints.includes('ISO42001') || aiAnalysis.complianceHints.includes('EU_AI_ACT')) {
845
+ config.bias = { enabled: true };
846
+ config.human_oversight = { enabled: true };
847
+ }
848
+ }
849
+ return config;
850
+ }