@iola_adm/iola-cli 0.1.76 → 0.1.78
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/package.json +1 -1
- package/src/cli.js +99 -15
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -774,7 +774,7 @@ async function startAgentReadline() {
|
|
|
774
774
|
}
|
|
775
775
|
|
|
776
776
|
async function startAgentRawInput() {
|
|
777
|
-
const state = { history: [], buffer: "", selected: 0, slashOffset: 0, slashOpen: false, running: false, renderedInputLines: 0, renderedLines: 0, rawMode: true, pendingOutput: "", aiStatus: null };
|
|
777
|
+
const state = { history: [], buffer: "", selected: 0, slashOffset: 0, slashOpen: false, running: false, renderedInputLines: 0, renderedLines: 0, cursorRowsBelow: 0, rawMode: true, pendingOutput: "", aiStatus: null };
|
|
778
778
|
const wasRaw = input.isRaw;
|
|
779
779
|
activateRawInput(input);
|
|
780
780
|
|
|
@@ -1331,7 +1331,8 @@ function renderAgentInput(state) {
|
|
|
1331
1331
|
const renderedLines = [...inputLines, "", ...menuLines, cwdLine];
|
|
1332
1332
|
output.write(renderedLines.join("\n"));
|
|
1333
1333
|
if (output.isTTY) {
|
|
1334
|
-
|
|
1334
|
+
state.cursorRowsBelow = 2 + menuLines.length;
|
|
1335
|
+
output.write(`\x1b[${state.cursorRowsBelow}A`);
|
|
1335
1336
|
}
|
|
1336
1337
|
if (output.isTTY) {
|
|
1337
1338
|
const cursorColumn = visibleLength(inputLines[inputLines.length - 1]);
|
|
@@ -1339,16 +1340,20 @@ function renderAgentInput(state) {
|
|
|
1339
1340
|
}
|
|
1340
1341
|
state.renderedInputLines = inputLines.length;
|
|
1341
1342
|
state.renderedLines = renderedLines.length;
|
|
1343
|
+
if (!output.isTTY) state.cursorRowsBelow = 0;
|
|
1342
1344
|
}
|
|
1343
1345
|
|
|
1344
1346
|
function clearAgentInputArea(state = null) {
|
|
1345
1347
|
if (!output.isTTY) return;
|
|
1346
1348
|
const renderedLines = Math.max(1, Number(state?.renderedLines || state?.renderedInputLines || 1));
|
|
1349
|
+
const cursorRowsBelow = Math.max(0, Number(state?.cursorRowsBelow || 0));
|
|
1350
|
+
if (cursorRowsBelow > 0) output.write(`\x1b[${cursorRowsBelow}B`);
|
|
1347
1351
|
if (renderedLines > 1) output.write(`\x1b[${renderedLines - 1}A`);
|
|
1348
1352
|
output.write("\r\x1b[0J");
|
|
1349
1353
|
if (state) {
|
|
1350
1354
|
state.renderedInputLines = 0;
|
|
1351
1355
|
state.renderedLines = 0;
|
|
1356
|
+
state.cursorRowsBelow = 0;
|
|
1352
1357
|
}
|
|
1353
1358
|
}
|
|
1354
1359
|
|
|
@@ -6042,24 +6047,103 @@ async function aiAsk(args, context = {}) {
|
|
|
6042
6047
|
|
|
6043
6048
|
function buildDirectDataAnswer(question, dataContext) {
|
|
6044
6049
|
const normalized = question.toLocaleLowerCase("ru-RU");
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
if (terms.length < 2) return "";
|
|
6050
|
+
const requestedFields = detectDirectDataFields(normalized);
|
|
6051
|
+
if (requestedFields.length === 0) return "";
|
|
6048
6052
|
const rows = [
|
|
6049
6053
|
...dataContext.schools.map((item) => ({ layer: "schools", layerName: "школы", ...item })),
|
|
6050
6054
|
...dataContext.kindergartens.map((item) => ({ layer: "kindergartens", layerName: "детские сады", ...item })),
|
|
6051
6055
|
];
|
|
6052
|
-
const
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6056
|
+
const item = pickDirectDataItem(question, dataContext, rows);
|
|
6057
|
+
if (!item) return "";
|
|
6058
|
+
const lines = requestedFields
|
|
6059
|
+
.map((field) => formatDirectDataField(field, item))
|
|
6060
|
+
.filter(Boolean);
|
|
6061
|
+
if (lines.length === 0) return "";
|
|
6062
|
+
const name = getDirectDataItemName(item);
|
|
6058
6063
|
return [
|
|
6059
|
-
|
|
6060
|
-
item.
|
|
6061
|
-
|
|
6062
|
-
|
|
6064
|
+
...lines,
|
|
6065
|
+
`Источник: слой ${item.layer}, ${name}, ИНН ${item.inn || "-"}.`,
|
|
6066
|
+
].join("\n");
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
function detectDirectDataFields(normalizedQuestion) {
|
|
6070
|
+
const fields = [];
|
|
6071
|
+
if (/(директор|руководител|заведующ|кто возглавляет)/iu.test(normalizedQuestion)) fields.push("head");
|
|
6072
|
+
if (/(сайт|website|url|ссылка)/iu.test(normalizedQuestion)) fields.push("website");
|
|
6073
|
+
if (/(телефон|номер телефона|позвонить)/iu.test(normalizedQuestion)) fields.push("phone");
|
|
6074
|
+
if (/(почт|email|e-mail|имейл|электронн)/iu.test(normalizedQuestion)) fields.push("email");
|
|
6075
|
+
if (/(адрес|где находится|расположен)/iu.test(normalizedQuestion)) fields.push("address");
|
|
6076
|
+
if (/(инн)/iu.test(normalizedQuestion)) fields.push("inn");
|
|
6077
|
+
if (/(лиценз)/iu.test(normalizedQuestion)) fields.push("license");
|
|
6078
|
+
return [...new Set(fields)];
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
function pickDirectDataItem(question, dataContext, rows) {
|
|
6082
|
+
const patterns = dataContext.query?.patterns || extractStructuredPatterns(question);
|
|
6083
|
+
const targetLayers = patterns.targetLayers || [];
|
|
6084
|
+
const scopedRows = targetLayers.length > 0 ? rows.filter((item) => targetLayers.includes(item.layer)) : rows;
|
|
6085
|
+
|
|
6086
|
+
for (const inn of patterns.inns || []) {
|
|
6087
|
+
const match = scopedRows.find((item) => String(item.inn || "") === inn);
|
|
6088
|
+
if (match) return match;
|
|
6089
|
+
}
|
|
6090
|
+
|
|
6091
|
+
for (const number of patterns.numbers || []) {
|
|
6092
|
+
const exact = scopedRows.find((item) => itemNameHasNumber(item, number));
|
|
6093
|
+
if (exact) return exact;
|
|
6094
|
+
}
|
|
6095
|
+
|
|
6096
|
+
const terms = extractSearchTerms(question).filter((term) => !/^\d+$/.test(term));
|
|
6097
|
+
if (terms.length > 0) {
|
|
6098
|
+
const personMatches = scopedRows.filter((item) => {
|
|
6099
|
+
const head = String(item.head || item.fns_head_name || "").toLocaleLowerCase("ru-RU");
|
|
6100
|
+
return terms.every((term) => head.includes(term.toLocaleLowerCase("ru-RU")));
|
|
6101
|
+
});
|
|
6102
|
+
if (personMatches.length === 1) return personMatches[0];
|
|
6103
|
+
}
|
|
6104
|
+
|
|
6105
|
+
const confidentRows = scopedRows.filter((item) => {
|
|
6106
|
+
const confidence = Number(item._match?.confidence ?? item.match?.confidence ?? 0);
|
|
6107
|
+
const score = Number(item._match?.score ?? item.match?.score ?? 0);
|
|
6108
|
+
return confidence >= 0.8 || score >= 30;
|
|
6109
|
+
});
|
|
6110
|
+
if (confidentRows.length === 1) return confidentRows[0];
|
|
6111
|
+
|
|
6112
|
+
return null;
|
|
6113
|
+
}
|
|
6114
|
+
|
|
6115
|
+
function itemNameHasNumber(item, number) {
|
|
6116
|
+
const name = String(item.name || item.title || item.fns_full_name || item.fns_short_name || "").toLocaleLowerCase("ru-RU");
|
|
6117
|
+
const escaped = String(number).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6118
|
+
return new RegExp(`(?:№\\s*${escaped}|\\b(?:школа|сош|лицей|гимназия|сад|детский сад)\\s*№?\\s*${escaped}\\b)`, "iu").test(name);
|
|
6119
|
+
}
|
|
6120
|
+
|
|
6121
|
+
function formatDirectDataField(field, item) {
|
|
6122
|
+
const name = getDirectDataItemName(item);
|
|
6123
|
+
if (field === "head") {
|
|
6124
|
+
const head = item.head || item.fns_head_name;
|
|
6125
|
+
if (!head) return "";
|
|
6126
|
+
const position = item.fns_head_position || (item.layer === "kindergartens" ? "заведующий" : "директор");
|
|
6127
|
+
return `${position}: ${head} (${name}).`;
|
|
6128
|
+
}
|
|
6129
|
+
if (field === "website") return item.website ? `Сайт: ${item.website}` : `Сайт для ${name} в открытых данных не указан.`;
|
|
6130
|
+
if (field === "phone") return item.phone ? `Телефон: ${item.phone}` : `Телефон для ${name} в открытых данных не указан.`;
|
|
6131
|
+
if (field === "email") return item.email ? `Email: ${item.email}` : `Email для ${name} в открытых данных не указан.`;
|
|
6132
|
+
if (field === "address") return item.address || item.legal_address ? `Адрес: ${item.address || item.legal_address}` : `Адрес для ${name} в открытых данных не указан.`;
|
|
6133
|
+
if (field === "inn") return item.inn ? `ИНН: ${item.inn}` : `ИНН для ${name} в открытых данных не указан.`;
|
|
6134
|
+
if (field === "license") {
|
|
6135
|
+
const parts = [
|
|
6136
|
+
item.license_number ? `номер ${item.license_number}` : "",
|
|
6137
|
+
item.license_status ? `статус: ${item.license_status}` : "",
|
|
6138
|
+
item.license_date ? `дата: ${item.license_date}` : "",
|
|
6139
|
+
].filter(Boolean);
|
|
6140
|
+
return parts.length > 0 ? `Лицензия: ${parts.join(", ")}.` : `Лицензия для ${name} в открытых данных не указана.`;
|
|
6141
|
+
}
|
|
6142
|
+
return "";
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6145
|
+
function getDirectDataItemName(item) {
|
|
6146
|
+
return item.name || item.title || item.fns_short_name || item.fns_full_name || "организация";
|
|
6063
6147
|
}
|
|
6064
6148
|
|
|
6065
6149
|
async function resolveUsableAiProfile(config, options = {}) {
|