viberadar 0.3.199 → 0.3.201

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.
@@ -1104,26 +1104,108 @@ function buildObsSuppressPatternPrompt(pattern, recommendation, catalog) {
1104
1104
  })
1105
1105
  .slice(0, 15);
1106
1106
  return [
1107
- `Убери шумные лог-вызовы уровня INFO/DEBUG/TRACE.`,
1107
+ `Проведи ревизию шумных лог-вызовов уровня INFO/DEBUG/TRACE. Не применяй рекомендации VibeRadar автоматически.`,
1108
1108
  ``,
1109
1109
  SUPPRESS_GUARD,
1110
1110
  ``,
1111
1111
  `Конкретный паттерн для поиска: "${pattern}"`,
1112
+ recommendation ? `Рекомендация радара: ${recommendation}. Считай её гипотезой, а не командой.` : '',
1112
1113
  ``,
1113
1114
  relatedModules.length > 0
1114
1115
  ? `Модули где встречается шум (с примерами сообщений):\n${relatedModules.join('\n')}`
1115
1116
  : '',
1116
1117
  ``,
1117
- `Что сделать с каждым найденным вызовом logger.info/debug/trace, порождающим этот паттерн:`,
1118
- `- УДАЛИ полностью, если это lifecycle-мусор: "started", "done", "ok", "loaded", "ready", "ping"`,
1119
- `- СТРУКТУРИРУЙ в logger.debug({ service, event_name, outcome, ...данные }), если несёт диагностическую ценность`,
1118
+ `Сначала классифицируй каждый найденный logger.info/debug/trace:`,
1119
+ `- safe_to_remove lifecycle-мусор без бизнес-смысла: "started", "done", "ok", "loaded", "ready", "ping"`,
1120
+ `- safe_to_downgrade полезно только для локальной диагностики, можно перевести в debug`,
1121
+ `- keep — audit/security/payment/auth/business-event, reconciliation, user action или важный lifecycle flow`,
1122
+ `- needs_human_review — контекст спорный или уверенность ниже высокой`,
1123
+ ``,
1124
+ `Меняй код только для safe_to_remove/safe_to_downgrade:`,
1125
+ `- УДАЛИ полностью только очевидный lifecycle-мусор без диагностической ценности`,
1126
+ `- СТРУКТУРИРУЙ в logger.debug({ service, event_name, outcome, ...данные }), если лог несёт диагностическую ценность`,
1127
+ `- ОСТАВЬ как есть, если это бизнес-событие, аудит, безопасность, платежи, авторизация, интеграция, очередь или reconciliation`,
1120
1128
  `- НЕ ТРОГАЙ, если это logger.warn / logger.error / logger.fatal — даже если сообщение похоже на шум`,
1129
+ `- Перед изменениями выведи краткий список решений: файл, паттерн/строка, действие, причина`,
1121
1130
  ``,
1122
1131
  `⛔ НЕ ЗАПУСКАЙ npm test / vitest / playwright — это лог-правка, не изменение логики. Единственная нужная проверка: \`npm run check\` (tsc).`,
1123
1132
  ``,
1124
1133
  `\n${LOGGING_STANDARD_INLINE}`,
1125
1134
  ].filter(Boolean).join('\n');
1126
1135
  }
1136
+ function decodeJwtPayload(token) {
1137
+ const part = token.split('.')[1];
1138
+ if (!part)
1139
+ return null;
1140
+ try {
1141
+ const normalized = part.replace(/-/g, '+').replace(/_/g, '/');
1142
+ const padded = normalized + '='.repeat((4 - normalized.length % 4) % 4);
1143
+ return JSON.parse(Buffer.from(padded, 'base64').toString('utf-8'));
1144
+ }
1145
+ catch {
1146
+ return null;
1147
+ }
1148
+ }
1149
+ function loadCodexAccountInfo() {
1150
+ const result = { email: null, name: null, accountId: null, raw: '' };
1151
+ try {
1152
+ const authPath = path.join(os.homedir(), '.codex', 'auth.json');
1153
+ const auth = JSON.parse(fs.readFileSync(authPath, 'utf-8'));
1154
+ const payload = typeof auth?.tokens?.id_token === 'string'
1155
+ ? decodeJwtPayload(auth.tokens.id_token)
1156
+ : null;
1157
+ result.email = payload?.email || payload?.https?.email || null;
1158
+ result.name = payload?.name || payload?.nickname || payload?.preferred_username || null;
1159
+ result.accountId = auth?.tokens?.account_id || payload?.sub || null;
1160
+ result.raw = result.email || result.name || result.accountId || '';
1161
+ }
1162
+ catch { }
1163
+ return result;
1164
+ }
1165
+ function buildObsPromptForTask(task, meta, obs) {
1166
+ if (task === 'obs-suppress-pattern') {
1167
+ if (!meta?.pattern)
1168
+ return null;
1169
+ return buildObsSuppressPatternPrompt(meta.pattern, meta.recommendation || 'suppress', obs.catalog);
1170
+ }
1171
+ if (task === 'obs-add-critical-logs') {
1172
+ if (!meta?.modulePath)
1173
+ return null;
1174
+ const v2Item = (obs.missingCriticalLogsV2 || []).find((m) => m.modulePath === meta.modulePath);
1175
+ return v2Item
1176
+ ? buildObsAddCriticalLogsPromptV2(v2Item, obs.catalog)
1177
+ : buildObsAddCriticalLogsPrompt(meta.modulePath, obs.catalog);
1178
+ }
1179
+ if (task === 'obs-enrich-field') {
1180
+ if (!meta?.fieldName)
1181
+ return null;
1182
+ return buildObsEnrichFieldPrompt(meta.fieldName, obs.catalog);
1183
+ }
1184
+ if (task === 'obs-batch-recommendation') {
1185
+ if (!meta?.recommendationType)
1186
+ return null;
1187
+ return buildObsBatchRecommendationPrompt(meta.recommendationType, obs.catalog);
1188
+ }
1189
+ if (task === 'obs-fix-module') {
1190
+ const modulePath = meta?.modulePath;
1191
+ const catalogItem = modulePath ? obs.catalog.find(c => c.modulePath === modulePath) : null;
1192
+ return catalogItem ? buildObsFixModulePrompt(catalogItem.modulePath, catalogItem) : null;
1193
+ }
1194
+ if (task === 'obs-fix-selected') {
1195
+ const missingLogIndices = Array.isArray(meta?.missingLogIndices) ? meta.missingLogIndices : [];
1196
+ const indices = Array.isArray(meta?.catalogIndices) ? meta.catalogIndices : [];
1197
+ if (missingLogIndices.length > 0) {
1198
+ const selectedV2 = missingLogIndices.map(i => obs.missingCriticalLogsV2[i]).filter(Boolean);
1199
+ return selectedV2.length > 0 ? buildObsBatchAddCriticalLogsPrompt(selectedV2, obs.catalog) : null;
1200
+ }
1201
+ const paths = Array.isArray(meta?.catalogPaths) ? meta.catalogPaths : [];
1202
+ const selectedItems = paths.length > 0
1203
+ ? paths.map(p => obs.catalog.find(c => c.modulePath === p)).filter(Boolean)
1204
+ : indices.map(i => obs.catalog[i]).filter(Boolean);
1205
+ return selectedItems.length > 0 ? buildObsFixSelectedPrompt(selectedItems, meta || {}) : null;
1206
+ }
1207
+ return null;
1208
+ }
1127
1209
  function buildObsAddCriticalLogsPrompt(modulePath, catalog) {
1128
1210
  const moduleItem = catalog.find(c => c.modulePath === modulePath);
1129
1211
  const missingFields = moduleItem?.missingFields || [];
@@ -3302,6 +3384,34 @@ function startServer({ data: initialData, port, projectRoot }) {
3302
3384
  res.end(JSON.stringify({ ...currentData, testErrors, hasPlaywright: hasPlaywright(projectRoot), e2ePlansExist, agentRuntime }));
3303
3385
  return;
3304
3386
  }
3387
+ if (url === '/api/obs-build-prompt' && req.method === 'POST') {
3388
+ let body = '';
3389
+ req.on('data', d => body += d);
3390
+ req.on('end', () => {
3391
+ try {
3392
+ const { task, meta } = JSON.parse(body || '{}');
3393
+ const obs = currentData.observability;
3394
+ if (!obs) {
3395
+ res.writeHead(400, jsonH);
3396
+ res.end(JSON.stringify({ error: 'Нет данных observability' }));
3397
+ return;
3398
+ }
3399
+ const prompt = buildObsPromptForTask(task, meta || {}, obs);
3400
+ if (!prompt) {
3401
+ res.writeHead(400, jsonH);
3402
+ res.end(JSON.stringify({ error: 'Не удалось собрать prompt для выбранной рекомендации' }));
3403
+ return;
3404
+ }
3405
+ res.writeHead(200, jsonH);
3406
+ res.end(JSON.stringify({ ok: true, prompt }));
3407
+ }
3408
+ catch (err) {
3409
+ res.writeHead(400, jsonH);
3410
+ res.end(JSON.stringify({ error: err.message }));
3411
+ }
3412
+ });
3413
+ return;
3414
+ }
3305
3415
  if (url === '/api/service-map' && req.method === 'GET') {
3306
3416
  const format = parsedUrl.searchParams.get('format') || 'json';
3307
3417
  const sm = currentData?.serviceMap;
@@ -3634,6 +3744,27 @@ function startServer({ data: initialData, port, projectRoot }) {
3634
3744
  return;
3635
3745
  }
3636
3746
  if (url === '/api/agent-whoami' && req.method === 'GET') {
3747
+ const requestedAgent = parsedUrl.searchParams.get('agent') || currentData.agent || 'claude';
3748
+ if (requestedAgent === 'codex') {
3749
+ const cmd = WIN ? 'codex.cmd login status' : 'codex login status';
3750
+ let out = '';
3751
+ const proc = (0, child_process_1.spawn)(cmd, [], { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });
3752
+ proc.stdout?.on('data', (d) => { out += d.toString(); });
3753
+ proc.stderr?.on('data', (d) => { out += d.toString(); });
3754
+ proc.on('close', () => {
3755
+ const account = loadCodexAccountInfo();
3756
+ res.writeHead(200, jsonH);
3757
+ res.end(JSON.stringify({
3758
+ agent: 'codex',
3759
+ loggedIn: /logged in/i.test(out),
3760
+ email: account.email,
3761
+ name: account.name,
3762
+ accountId: account.accountId,
3763
+ raw: account.raw || out.trim().slice(0, 300),
3764
+ }));
3765
+ });
3766
+ return;
3767
+ }
3637
3768
  const cmd = WIN ? 'claude.cmd auth status' : 'claude auth status';
3638
3769
  let out = '';
3639
3770
  const proc = (0, child_process_1.spawn)(cmd, [], { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });
@@ -3646,7 +3777,7 @@ function startServer({ data: initialData, port, projectRoot }) {
3646
3777
  || out.match(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/);
3647
3778
  const email = match ? match[1] : null;
3648
3779
  res.writeHead(200, jsonH);
3649
- res.end(JSON.stringify({ email, raw: out.trim().slice(0, 300) }));
3780
+ res.end(JSON.stringify({ agent: 'claude', email, raw: out.trim().slice(0, 300) }));
3650
3781
  });
3651
3782
  return;
3652
3783
  }