answer42 0.2.28__tar.gz → 0.2.30__tar.gz
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.
- {answer42-0.2.28 → answer42-0.2.30}/PKG-INFO +1 -1
- {answer42-0.2.28 → answer42-0.2.30}/pyproject.toml +1 -1
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl +252 -27
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/protocol.py +1 -1
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/server.py +132 -17
- {answer42-0.2.28 → answer42-0.2.30}/.gitignore +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/LICENSE +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/README.md +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/credentials.example.json +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/docs/agent-installation.md +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/docs/architecture.md +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/docs/assets/answer42-logo.png +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/docs/assets/platform42-logo.svg +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/docs/installation.md +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/scripts/build_cf.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/scripts/build_pages.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/scripts/e2e_stable.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/scripts/openclaw_mcp_autoreload.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/scripts/rag_cli.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/ConfigDumpInfo.xml +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/Configuration.xml +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Catalogs//320/237/320/241_/320/222/320/273/320/260/320/264/320/265/320/273/320/265/321/206.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Catalogs//320/237/320/241_/320/241/320/277/321/200/320/260/320/262/320/276/321/207/320/275/320/270/320/2721.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/ConfigDumpInfo.xml +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Configuration.xml +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ManagerModule.bsl" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ObjectModule.bsl" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205/Ext/Template.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262.xml" +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/__init__.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/__init__.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/bridge.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/credentials.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/os_support.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/platform.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/__init__.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/detect.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/model.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/parsers.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/service.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/store.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/recorder.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/release_helper.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/runtime.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/skill_installer.py +0 -0
- {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/window_control.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: answer42
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.30
|
|
4
4
|
Summary: Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API
|
|
5
5
|
Author: Marvin (AI Assistant), 42Clouds, and contributors
|
|
6
6
|
Author-email: "Kosolapov Stanislav (proDOOMman)" <prodoomman@gmail.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "answer42"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.30"
|
|
4
4
|
description = "Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
|
|
241
241
|
&НаКлиенте
|
|
242
242
|
Функция ВерсияПротоколаМенеджераMCP()
|
|
243
|
-
Возврат "2026.07.02.
|
|
243
|
+
Возврат "2026.07.02.4";
|
|
244
244
|
КонецФункции
|
|
245
245
|
|
|
246
246
|
&НаКлиенте
|
|
@@ -294,6 +294,8 @@
|
|
|
294
294
|
Возврат НажатьКнопку(Параметры);
|
|
295
295
|
ИначеЕсли Метод = "find_object" Тогда
|
|
296
296
|
Возврат НайтиОбъектИнтерфейса(Параметры);
|
|
297
|
+
ИначеЕсли Метод = "search_objects" Тогда
|
|
298
|
+
Возврат НайтиВсеОбъектыИнтерфейса(Параметры);
|
|
297
299
|
ИначеЕсли Метод = "get_object" Тогда
|
|
298
300
|
Возврат ПолучитьОбъектПриложенияMCP(Параметры);
|
|
299
301
|
ИначеЕсли Метод = "activate_object" Тогда
|
|
@@ -2565,6 +2567,51 @@
|
|
|
2565
2567
|
Возврат Неопределено;
|
|
2566
2568
|
КонецФункции
|
|
2567
2569
|
|
|
2570
|
+
&НаКлиенте
|
|
2571
|
+
Функция НайтиВсеОбъектыИнтерфейса(Параметры)
|
|
2572
|
+
Если ТестируемоеПриложение = Неопределено Тогда
|
|
2573
|
+
ВызватьИсключение "Тестируемое приложение не подключено.";
|
|
2574
|
+
КонецЕсли;
|
|
2575
|
+
АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
|
|
2576
|
+
Имя = СвойствоСтруктуры(Параметры, "name", "");
|
|
2577
|
+
Заголовок = СвойствоСтруктуры(Параметры, "title", "");
|
|
2578
|
+
ТипОбъекта = СвойствоСтруктуры(Параметры, "type", "");
|
|
2579
|
+
Глубина = СвойствоСтруктуры(Параметры, "max_depth", 8);
|
|
2580
|
+
Лимит = СвойствоСтруктуры(Параметры, "limit", 50);
|
|
2581
|
+
Найденные = Новый Массив;
|
|
2582
|
+
НайтиВсеОбъектыРекурсивно(АктивноеОкно, Имя, Заголовок, ТипОбъекта, 0, Глубина, Лимит, Найденные);
|
|
2583
|
+
Возврат Новый Структура("found,count,limit,objects", Найденные.Количество() > 0, Найденные.Количество(), Лимит, Найденные);
|
|
2584
|
+
КонецФункции
|
|
2585
|
+
|
|
2586
|
+
&НаКлиенте
|
|
2587
|
+
Процедура НайтиВсеОбъектыРекурсивно(РодительскийОбъект, Имя, Заголовок, ТипОбъекта, ТекущаяГлубина, МаксимальнаяГлубина, Лимит, Найденные)
|
|
2588
|
+
Если ТекущаяГлубина > МаксимальнаяГлубина Тогда
|
|
2589
|
+
Возврат;
|
|
2590
|
+
КонецЕсли;
|
|
2591
|
+
Если Найденные.Количество() >= Лимит Тогда
|
|
2592
|
+
Возврат;
|
|
2593
|
+
КонецЕсли;
|
|
2594
|
+
Попытка
|
|
2595
|
+
ИмяТекущего = БезопасноеСвойство(РодительскийОбъект, "Имя", "");
|
|
2596
|
+
ЗаголовокТекущего = БезопасныйЗаголовок(РодительскийОбъект);
|
|
2597
|
+
ТипТекущего = Строка(ТипЗнч(РодительскийОбъект));
|
|
2598
|
+
НормализованныйТип = НормализованноеИмяТипа(ТипТекущего);
|
|
2599
|
+
Если ОбъектСоответствует(ИмяТекущего, ЗаголовокТекущего, НормализованныйТип, Имя, Заголовок, ТипОбъекта) Тогда
|
|
2600
|
+
Найденные.Добавить(ОбъектВСтруктуру(РодительскийОбъект, 0, 0));
|
|
2601
|
+
КонецЕсли;
|
|
2602
|
+
Подчиненные = БезопасныеПодчиненные(РодительскийОбъект);
|
|
2603
|
+
Для Каждого Подчиненный Из Подчиненные Цикл
|
|
2604
|
+
Если Найденные.Количество() >= Лимит Тогда
|
|
2605
|
+
Прервать;
|
|
2606
|
+
КонецЕсли;
|
|
2607
|
+
НайтиВсеОбъектыРекурсивно(Подчиненный, Имя, Заголовок, ТипОбъекта, ТекущаяГлубина + 1, МаксимальнаяГлубина, Лимит, Найденные);
|
|
2608
|
+
КонецЦикла;
|
|
2609
|
+
Исключение
|
|
2610
|
+
Инфо = ИнформацияОбОшибке();
|
|
2611
|
+
Лог("Подавленное исключение: " + ПодробноеПредставлениеОшибки(Инфо));
|
|
2612
|
+
КонецПопытки;
|
|
2613
|
+
КонецПроцедуры
|
|
2614
|
+
|
|
2568
2615
|
&НаКлиенте
|
|
2569
2616
|
Функция ОбъектСоответствует(ИмяОбъекта, ЗаголовокОбъекта, ТипОбъекта, ИскомоеИмя, ИскомыйЗаголовок, ИскомыйТип)
|
|
2570
2617
|
НормализованноеИмя = НРег(СокрЛП(ИмяОбъекта));
|
|
@@ -3727,7 +3774,7 @@
|
|
|
3727
3774
|
Если Представления.Количество() > 0 Тогда
|
|
3728
3775
|
ИндексВыбора = ИндексРелевантногоВариантаСпискаВыбораMCP(Представления, Значение);
|
|
3729
3776
|
Если ИндексВыбора = Неопределено Тогда
|
|
3730
|
-
|
|
3777
|
+
ОтменитьРедактированиеПоляЕслиНужноMCP(Поле);
|
|
3731
3778
|
ВызватьИсключение "Выпадающий список открыт, но релевантный вариант для значения " + Значение + " не найден";
|
|
3732
3779
|
КонецЕсли;
|
|
3733
3780
|
ПредставлениеВыбираемогоВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представления[ИндексВыбора]);
|
|
@@ -3783,7 +3830,13 @@
|
|
|
3783
3830
|
КонецПопытки;
|
|
3784
3831
|
|
|
3785
3832
|
Если ТекстовыйВводВыполнен Тогда
|
|
3786
|
-
|
|
3833
|
+
// Для полей выбора, особенно редакторов ячеек таблиц настроек, ВвестиТекст()
|
|
3834
|
+
// может не менять представление напрямую, но после этого остаются доступны
|
|
3835
|
+
// штатные методы выбора из списка/меню. Не отменяем редактирование сразу:
|
|
3836
|
+
// сначала пробуем choice/menu fallback-и ниже. Если они тоже не сработают,
|
|
3837
|
+
// финальный обработчик ошибок отменит редактирование ячейки через
|
|
3838
|
+
// ЗакончитьРедактированиеСтроки(Истина).
|
|
3839
|
+
Ошибки.Добавить("ВвестиТекст=выполнен, но значение не подтвердилось; текущее представление: " + НеподтвержденноеПредставлениеПослеВвода);
|
|
3787
3840
|
КонецЕсли;
|
|
3788
3841
|
|
|
3789
3842
|
// Переключатели/радиокнопки и поля со списком выбора: после активации
|
|
@@ -3878,6 +3931,7 @@
|
|
|
3878
3931
|
Ошибки.Добавить("УстановитьЗначение=" + ОписаниеОшибки());
|
|
3879
3932
|
КонецПопытки;
|
|
3880
3933
|
|
|
3934
|
+
ОтменитьРедактированиеПоляЕслиНужноMCP(Поле);
|
|
3881
3935
|
ВызватьИсключение "Не удалось установить поле " + ИмяПоля + ": " + СтрСоединить(Ошибки, "; ");
|
|
3882
3936
|
КонецФункции
|
|
3883
3937
|
|
|
@@ -4210,9 +4264,76 @@
|
|
|
4210
4264
|
Возврат Новый Структура("found,name,title,added", Ложь, ИмяТаблицы, ЗаголовокТаблицы, Ложь);
|
|
4211
4265
|
КонецЕсли;
|
|
4212
4266
|
Таблица.Активизировать();
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4267
|
+
ТекущаяСтрокаДоJSON = "";
|
|
4268
|
+
Попытка
|
|
4269
|
+
ТекущаяСтрокаДоJSON = ЗначениеВJSON(СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
|
|
4270
|
+
Исключение
|
|
4271
|
+
ТекущаяСтрокаДоJSON = "";
|
|
4272
|
+
КонецПопытки;
|
|
4273
|
+
ОшибкаДобавленияСтроки = "";
|
|
4274
|
+
Попытка
|
|
4275
|
+
Таблица.ДобавитьСтроку();
|
|
4276
|
+
ТекущаяСтрока = Таблица.ПолучитьТекущуюСтроку();
|
|
4277
|
+
ТекущаяСтрокаПослеJSON = ЗначениеВJSON(СоответствиеВСтруктуру(ТекущаяСтрока));
|
|
4278
|
+
РежимРедактирования = ТаблицаВРежимеРедактированияMCP(Таблица);
|
|
4279
|
+
Если РежимРедактирования Или ТекущаяСтрокаДоJSON <> ТекущаяСтрокаПослеJSON Тогда
|
|
4280
|
+
Возврат Новый Структура("found,added,method,editing,current_row", Истина, Истина, "api_add_row", РежимРедактирования, СоответствиеВСтруктуру(ТекущаяСтрока));
|
|
4281
|
+
КонецЕсли;
|
|
4282
|
+
Исключение
|
|
4283
|
+
ОшибкаДобавленияСтроки = ОписаниеОшибки();
|
|
4284
|
+
КонецПопытки;
|
|
4285
|
+
|
|
4286
|
+
КнопкаДобавления = НайтиКнопкуДобавленияСтрокиТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, Глубина);
|
|
4287
|
+
Если КнопкаДобавления <> Неопределено Тогда
|
|
4288
|
+
ИмяКнопки = БезопасноеСвойство(КнопкаДобавления, "Имя", "");
|
|
4289
|
+
ЗаголовокКнопки = БезопасныйЗаголовок(КнопкаДобавления);
|
|
4290
|
+
КнопкаДобавления.Нажать();
|
|
4291
|
+
НовоеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
|
|
4292
|
+
МодальноеОкноОткрыто = НовоеОкно <> Неопределено И НовоеОкно <> АктивноеОкно;
|
|
4293
|
+
Возврат Новый Структура("found,added,method,button_name,button_title,modal_opened,modal_title,api_add_error", Истина, Истина, "command_button", ИмяКнопки, ЗаголовокКнопки, МодальноеОкноОткрыто, ?(МодальноеОкноОткрыто, БезопасноеСвойство(НовоеОкно, "Заголовок", ""), ""), ОшибкаДобавленияСтроки);
|
|
4294
|
+
КонецЕсли;
|
|
4295
|
+
Если Не ПустаяСтрока(ОшибкаДобавленияСтроки) Тогда
|
|
4296
|
+
ВызватьИсключение ОшибкаДобавленияСтроки;
|
|
4297
|
+
КонецЕсли;
|
|
4298
|
+
Возврат Новый Структура("found,added,method,editing,current_row", Истина, Ложь, "api_add_row_no_effect", ТаблицаВРежимеРедактированияMCP(Таблица), СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
|
|
4299
|
+
КонецФункции
|
|
4300
|
+
|
|
4301
|
+
&НаКлиенте
|
|
4302
|
+
Функция НайтиКнопкуДобавленияСтрокиТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, Глубина)
|
|
4303
|
+
Кандидаты = Новый Массив;
|
|
4304
|
+
Если Не ПустаяСтрока(ИмяТаблицы) Тогда
|
|
4305
|
+
Кандидаты.Добавить(ИмяТаблицы + "ДобавитьЭлементПорядка");
|
|
4306
|
+
Кандидаты.Добавить(ИмяТаблицы + "ДобавитьЭлемент");
|
|
4307
|
+
Кандидаты.Добавить(ИмяТаблицы + "ДобавитьСтроку");
|
|
4308
|
+
Кандидаты.Добавить(ИмяТаблицы + "Добавить");
|
|
4309
|
+
КонецЕсли;
|
|
4310
|
+
Кандидаты.Добавить("ДобавитьЭлементПорядка");
|
|
4311
|
+
Кандидаты.Добавить("Добавить новый элемент порядка");
|
|
4312
|
+
Кандидаты.Добавить("Добавить");
|
|
4313
|
+
|
|
4314
|
+
Для Каждого ИмяКнопки Из Кандидаты Цикл
|
|
4315
|
+
Попытка
|
|
4316
|
+
Кнопка = НайтиКнопку(Таблица, ИмяКнопки, "", 0, Глубина);
|
|
4317
|
+
Если Кнопка <> Неопределено Тогда
|
|
4318
|
+
Возврат Кнопка;
|
|
4319
|
+
КонецЕсли;
|
|
4320
|
+
Исключение
|
|
4321
|
+
КонецПопытки;
|
|
4322
|
+
КонецЦикла;
|
|
4323
|
+
|
|
4324
|
+
Если АктивноеОкно <> Неопределено Тогда
|
|
4325
|
+
Для Каждого ИмяКнопки Из Кандидаты Цикл
|
|
4326
|
+
Попытка
|
|
4327
|
+
Кнопка = НайтиКнопку(АктивноеОкно, ИмяКнопки, "", 0, Глубина);
|
|
4328
|
+
Если Кнопка <> Неопределено Тогда
|
|
4329
|
+
Возврат Кнопка;
|
|
4330
|
+
КонецЕсли;
|
|
4331
|
+
Исключение
|
|
4332
|
+
КонецПопытки;
|
|
4333
|
+
КонецЦикла;
|
|
4334
|
+
КонецЕсли;
|
|
4335
|
+
|
|
4336
|
+
Возврат Неопределено;
|
|
4216
4337
|
КонецФункции
|
|
4217
4338
|
|
|
4218
4339
|
&НаКлиенте
|
|
@@ -4324,15 +4445,93 @@
|
|
|
4324
4445
|
КонецФункции
|
|
4325
4446
|
|
|
4326
4447
|
&НаКлиенте
|
|
4327
|
-
|
|
4448
|
+
Функция ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица)
|
|
4328
4449
|
Таблица.Активизировать();
|
|
4450
|
+
УжеРедактируется = ТаблицаВРежимеРедактированияMCP(Таблица);
|
|
4451
|
+
Если УжеРедактируется Тогда
|
|
4452
|
+
Возврат Новый Структура("editing,changed,error", Истина, Ложь, "");
|
|
4453
|
+
КонецЕсли;
|
|
4329
4454
|
Попытка
|
|
4330
|
-
|
|
4331
|
-
|
|
4455
|
+
Таблица.ИзменитьСтроку();
|
|
4456
|
+
Возврат Новый Структура("editing,changed,error", ТаблицаВРежимеРедактированияMCP(Таблица), Истина, "");
|
|
4457
|
+
Исключение
|
|
4458
|
+
// После ДобавитьСтроку() таблица может быть в промежуточном состоянии
|
|
4459
|
+
// добавления/редактирования, где ИзменитьСтроку() уже неприменима
|
|
4460
|
+
// ("Неподходящее состояние элемента управления"), но редактор текущей
|
|
4461
|
+
// строки уже доступен. Не падаем здесь: дальше ищем поле редактора;
|
|
4462
|
+
// если его нет — вернём точную ошибку поиска/установки.
|
|
4463
|
+
Возврат Новый Структура("editing,changed,error", ТаблицаВРежимеРедактированияMCP(Таблица), Ложь, ОписаниеОшибки());
|
|
4464
|
+
КонецПопытки;
|
|
4465
|
+
КонецФункции
|
|
4466
|
+
|
|
4467
|
+
&НаКлиенте
|
|
4468
|
+
Функция ТаблицаВРежимеРедактированияMCP(Таблица)
|
|
4469
|
+
Попытка
|
|
4470
|
+
Возврат Таблица.ТекущийРежимРедактирование();
|
|
4471
|
+
Исключение
|
|
4472
|
+
Возврат Ложь;
|
|
4473
|
+
КонецПопытки;
|
|
4474
|
+
КонецФункции
|
|
4475
|
+
|
|
4476
|
+
&НаКлиенте
|
|
4477
|
+
Функция ЗавершитьРедактированиеСтрокиТаблицыMCP(Таблица)
|
|
4478
|
+
ОшибкаЗавершения = "";
|
|
4479
|
+
БылаВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
|
|
4480
|
+
Попытка
|
|
4481
|
+
Если БылаВРедактировании Тогда
|
|
4482
|
+
Таблица.ЗакончитьРедактированиеСтроки(Ложь);
|
|
4483
|
+
КонецЕсли;
|
|
4484
|
+
Исключение
|
|
4485
|
+
ОшибкаЗавершения = ОписаниеОшибки();
|
|
4486
|
+
КонецПопытки;
|
|
4487
|
+
|
|
4488
|
+
ОсталасьВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
|
|
4489
|
+
ОтменаВыполнена = Ложь;
|
|
4490
|
+
ОшибкаОтмены = "";
|
|
4491
|
+
Если ОсталасьВРедактировании Тогда
|
|
4492
|
+
Попытка
|
|
4493
|
+
Таблица.ЗакончитьРедактированиеСтроки(Истина);
|
|
4494
|
+
ОтменаВыполнена = Истина;
|
|
4495
|
+
Исключение
|
|
4496
|
+
ОшибкаОтмены = ОписаниеОшибки();
|
|
4497
|
+
КонецПопытки;
|
|
4498
|
+
КонецЕсли;
|
|
4499
|
+
|
|
4500
|
+
ВсеЕщеВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
|
|
4501
|
+
Возврат Новый Структура("was_editing,commit_error,remained_editing,cancel_done,cancel_error,still_editing", БылаВРедактировании, ОшибкаЗавершения, ОсталасьВРедактировании, ОтменаВыполнена, ОшибкаОтмены, ВсеЕщеВРедактировании);
|
|
4502
|
+
КонецФункции
|
|
4503
|
+
|
|
4504
|
+
&НаКлиенте
|
|
4505
|
+
Функция НайтиРодительскуюТаблицуMCP(Объект)
|
|
4506
|
+
Текущий = Объект;
|
|
4507
|
+
Пока Текущий <> Неопределено Цикл
|
|
4508
|
+
Если НормализованноеИмяТипа(ТипЗнч(Текущий)) = "тестируемаятаблицаформы" Тогда
|
|
4509
|
+
Возврат Текущий;
|
|
4510
|
+
КонецЕсли;
|
|
4511
|
+
Попытка
|
|
4512
|
+
Текущий = Текущий.ПолучитьРодителя();
|
|
4513
|
+
Исключение
|
|
4514
|
+
Возврат Неопределено;
|
|
4515
|
+
КонецПопытки;
|
|
4516
|
+
КонецЦикла;
|
|
4517
|
+
Возврат Неопределено;
|
|
4518
|
+
КонецФункции
|
|
4519
|
+
|
|
4520
|
+
&НаКлиенте
|
|
4521
|
+
Процедура ОтменитьРедактированиеПоляЕслиНужноMCP(Поле)
|
|
4522
|
+
Попытка
|
|
4523
|
+
Если ОткрытВыпадающийСписокПоляMCP(Поле) Тогда
|
|
4524
|
+
Поле.ЗакрытьВыпадающийСписок();
|
|
4332
4525
|
КонецЕсли;
|
|
4333
4526
|
Исключение
|
|
4334
|
-
Таблица.ИзменитьСтроку();
|
|
4335
4527
|
КонецПопытки;
|
|
4528
|
+
Таблица = НайтиРодительскуюТаблицуMCP(Поле);
|
|
4529
|
+
Если Таблица <> Неопределено И ТаблицаВРежимеРедактированияMCP(Таблица) Тогда
|
|
4530
|
+
Попытка
|
|
4531
|
+
Таблица.ЗакончитьРедактированиеСтроки(Истина);
|
|
4532
|
+
Исключение
|
|
4533
|
+
КонецПопытки;
|
|
4534
|
+
КонецЕсли;
|
|
4336
4535
|
КонецПроцедуры
|
|
4337
4536
|
|
|
4338
4537
|
&НаКлиенте
|
|
@@ -4407,11 +4606,12 @@
|
|
|
4407
4606
|
ВызватьИсключение "Таблица не найдена: " + ИмяТаблицы;
|
|
4408
4607
|
КонецЕсли;
|
|
4409
4608
|
|
|
4410
|
-
ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица);
|
|
4609
|
+
РезультатНачалаРедактирования = ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица);
|
|
4411
4610
|
АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
|
|
4412
4611
|
Поле = НайтиПолеРедактораТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, ИмяПоля, ИскатьПоЗаголовку, МаксимальнаяГлубина);
|
|
4413
4612
|
Если Поле = Неопределено Тогда
|
|
4414
|
-
|
|
4613
|
+
ДополнениеОшибкиРедактирования = ?(ПустаяСтрока(РезультатНачалаРедактирования.error), "", "; ошибка начала редактирования: " + РезультатНачалаРедактирования.error);
|
|
4614
|
+
ВызватьИсключение "Поле редактора таблицы не найдено: " + ИмяПоля + ДополнениеОшибкиРедактирования;
|
|
4415
4615
|
КонецЕсли;
|
|
4416
4616
|
ИмяРедактора = БезопасноеСвойство(Поле, "Имя", "");
|
|
4417
4617
|
Поле.Активизировать();
|
|
@@ -4429,14 +4629,13 @@
|
|
|
4429
4629
|
ОшибкаУстановки = ОписаниеОшибки();
|
|
4430
4630
|
КонецПопытки;
|
|
4431
4631
|
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
КонецПопытки;
|
|
4632
|
+
РезультатЗавершенияРедактирования = ЗавершитьРедактированиеСтрокиТаблицыMCP(Таблица);
|
|
4633
|
+
Если Не ПустаяСтрока(РезультатЗавершенияРедактирования.commit_error) Тогда
|
|
4634
|
+
ВызватьИсключение "Не удалось завершить редактирование строки таблицы " + ИмяТаблицы + ": " + РезультатЗавершенияРедактирования.commit_error;
|
|
4635
|
+
КонецЕсли;
|
|
4636
|
+
Если РезультатЗавершенияРедактирования.still_editing Тогда
|
|
4637
|
+
ВызватьИсключение "Таблица " + ИмяТаблицы + " осталась в режиме редактирования после попытки завершить и отменить редактирование строки";
|
|
4638
|
+
КонецЕсли;
|
|
4440
4639
|
|
|
4441
4640
|
Проверка = ЗначениеКолонкиТекущейСтрокиMCP(Таблица, ИмяПоля, ИскатьПоЗаголовку);
|
|
4442
4641
|
Проверено = Проверка.found И ЗначенияСовпадаютДляПроверкиMCP(Проверка.value, Значение);
|
|
@@ -4449,7 +4648,7 @@
|
|
|
4449
4648
|
Если Не ПустаяСтрока(ОшибкаУстановки) Тогда
|
|
4450
4649
|
МетодУстановки = МетодУстановки + "+verified_after_exception";
|
|
4451
4650
|
КонецЕсли;
|
|
4452
|
-
Возврат Новый Структура("set,table,field,value,editor_field,verified,verified_key,verified_value,method,current_row", Истина, ИмяТаблицы, ИмяПоля, Значение, ИмяРедактора, Проверено, Проверка.key, Проверка.value, МетодУстановки, СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
|
|
4651
|
+
Возврат Новый Структура("set,table,field,value,editor_field,verified,verified_key,verified_value,method,edit_start,edit_finish,current_row", Истина, ИмяТаблицы, ИмяПоля, Значение, ИмяРедактора, Проверено, Проверка.key, Проверка.value, МетодУстановки, РезультатНачалаРедактирования, РезультатЗавершенияРедактирования, СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
|
|
4453
4652
|
КонецФункции
|
|
4454
4653
|
|
|
4455
4654
|
&НаКлиенте
|
|
@@ -4466,9 +4665,10 @@
|
|
|
4466
4665
|
Значения = СтруктураИзJSONПараметраMCP(Параметры, "values");
|
|
4467
4666
|
ИмяТаблицы = СвойствоСтруктуры(Параметры, "table", "ДополнительнаяИнформация");
|
|
4468
4667
|
ИскатьПоЗаголовку = СвойствоСтруктуры(Параметры, "search_by_title", Ложь);
|
|
4668
|
+
МаксимальнаяГлубина = СвойствоСтруктуры(Параметры, "max_depth", 20);
|
|
4469
4669
|
Результаты = Новый Массив;
|
|
4470
4670
|
Для Каждого КЗ Из Значения Цикл
|
|
4471
|
-
ПараметрыПоля = Новый Структура("table,field,value,search_by_title", ИмяТаблицы, Строка(КЗ.Ключ), Строка(КЗ.Значение),
|
|
4671
|
+
ПараметрыПоля = Новый Структура("table,field,value,search_by_title,max_depth", ИмяТаблицы, Строка(КЗ.Ключ), Строка(КЗ.Значение), ИскатьПоЗаголовку, МаксимальнаяГлубина);
|
|
4472
4672
|
Результаты.Добавить(УстановитьПолеТаблицыMCP(ПараметрыПоля));
|
|
4473
4673
|
КонецЦикла;
|
|
4474
4674
|
Возврат Новый Структура("set,table,values,results", Истина, ИмяТаблицы, Значения, Результаты);
|
|
@@ -4588,6 +4788,18 @@
|
|
|
4588
4788
|
СписокКнопок.Добавить(Новый Структура("name,title", "", "Вывести список..."));
|
|
4589
4789
|
СписокКнопок.Добавить(Новый Структура("name,title", "", "Вывести список"));
|
|
4590
4790
|
РезультатКнопки = НажатьПервуюКнопкуОкна(СписокКнопок, СвойствоСтруктуры(Параметры, "max_depth", 8));
|
|
4791
|
+
|
|
4792
|
+
// After pressing "Вывести список", a dialog opens. Confirm it.
|
|
4793
|
+
КнопкиПодтверждения = Новый Массив;
|
|
4794
|
+
КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "Сформировать"));
|
|
4795
|
+
КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "ОК"));
|
|
4796
|
+
КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "Ok"));
|
|
4797
|
+
Попытка
|
|
4798
|
+
НажатьПервуюКнопкуОкна(КнопкиПодтверждения, СвойствоСтруктуры(Параметры, "max_depth", 8));
|
|
4799
|
+
Исключение
|
|
4800
|
+
// Dialog may not appear or may auto-close; proceed anyway.
|
|
4801
|
+
КонецПопытки;
|
|
4802
|
+
|
|
4591
4803
|
Пауза = ТекущаяДата() + СвойствоСтруктуры(Параметры, "wait_seconds", 2);
|
|
4592
4804
|
Пока ТекущаяДата() < Пауза Цикл КонецЦикла;
|
|
4593
4805
|
Документы = ТабличныеДокументы(Новый Структура("include_text,max_depth", Истина, СвойствоСтруктуры(Параметры, "max_depth", 8)));
|
|
@@ -4614,7 +4826,7 @@
|
|
|
4614
4826
|
КонецЕсли;
|
|
4615
4827
|
Поле = СвойствоСтруктуры(Параметры, "field", "");
|
|
4616
4828
|
Значение = СвойствоСтруктуры(Параметры, "value", "");
|
|
4617
|
-
ВидСравнения = СвойствоСтруктуры(Параметры, "comparison", "Equal");
|
|
4829
|
+
ВидСравненияMCP = СвойствоСтруктуры(Параметры, "comparison", "Equal");
|
|
4618
4830
|
Если ПустаяСтрока(Поле) Тогда
|
|
4619
4831
|
ВызватьИсключение "Не задано поле отбора dynamic list.";
|
|
4620
4832
|
КонецЕсли;
|
|
@@ -4643,7 +4855,7 @@
|
|
|
4643
4855
|
Ошибки.Добавить("Левое значение: " + ОписаниеОшибки());
|
|
4644
4856
|
КонецПопытки;
|
|
4645
4857
|
Попытка
|
|
4646
|
-
УстановитьЗначениеПоля(Новый Структура("field_name,value,search_by_title", "Вид сравнения",
|
|
4858
|
+
УстановитьЗначениеПоля(Новый Структура("field_name,value,search_by_title", "Вид сравнения", ВидСравненияMCP, Истина));
|
|
4647
4859
|
Исключение
|
|
4648
4860
|
Ошибки.Добавить("Вид сравнения: " + ОписаниеОшибки());
|
|
4649
4861
|
КонецПопытки;
|
|
@@ -4659,9 +4871,9 @@
|
|
|
4659
4871
|
КонецПопытки;
|
|
4660
4872
|
|
|
4661
4873
|
Если Ошибки.Количество() > 0 Тогда
|
|
4662
|
-
Возврат Новый Структура("applied,field,value,comparison,errors,settings_tree", Ложь, Поле, Значение,
|
|
4874
|
+
Возврат Новый Структура("applied,field,value,comparison,errors,settings_tree", Ложь, Поле, Значение, ВидСравненияMCP, Ошибки, ДиагностикаДо);
|
|
4663
4875
|
КонецЕсли;
|
|
4664
|
-
Возврат Новый Структура("applied,field,value,comparison", Истина, Поле, Значение, ВидСравнения);
|
|
4876
|
+
Возврат Новый Структура("applied,field,value,comparison", Истина, Поле, Значение, ВидСравненияMCP);
|
|
4665
4877
|
КонецФункции
|
|
4666
4878
|
|
|
4667
4879
|
&НаКлиенте
|
|
@@ -4961,7 +5173,13 @@
|
|
|
4961
5173
|
Возврат Неопределено;
|
|
4962
5174
|
КонецЕсли;
|
|
4963
5175
|
Если ТекущаяГлубина > МаксимальнаяГлубина Тогда
|
|
4964
|
-
|
|
5176
|
+
РезультатОбрезки = Новый Структура;
|
|
5177
|
+
РезультатОбрезки.Вставить("truncated", Истина);
|
|
5178
|
+
РезультатОбрезки.Вставить("truncated_reason", "max_depth");
|
|
5179
|
+
РезультатОбрезки.Вставить("type", Строка(ТипЗнч(Объект)));
|
|
5180
|
+
РезультатОбрезки.Вставить("name", БезопасноеСвойство(Объект, "Имя", ""));
|
|
5181
|
+
РезультатОбрезки.Вставить("title", БезопасныйЗаголовок(Объект));
|
|
5182
|
+
Возврат РезультатОбрезки;
|
|
4965
5183
|
КонецЕсли;
|
|
4966
5184
|
СобственнаяВидимость = БезопасноеЗначениеМетода(Объект, "ТекущаяВидимость", БезопасноеСвойство(Объект, "Видимость", Истина));
|
|
4967
5185
|
СобственнаяДоступность = БезопасноеЗначениеМетода(Объект, "ТекущаяДоступность", БезопасноеСвойство(Объект, "Доступность", Истина));
|
|
@@ -5036,18 +5254,25 @@
|
|
|
5036
5254
|
КонецЕсли;
|
|
5037
5255
|
Исключение
|
|
5038
5256
|
КонецПопытки;
|
|
5257
|
+
СкрытыхДетей = 0;
|
|
5039
5258
|
Для Каждого Ребенок Из Дети Цикл
|
|
5040
5259
|
ИмяРебенка = БезопасноеСвойство(Ребенок, "Имя", "");
|
|
5041
5260
|
Если Не ПоказыватьСкрытые И Не ПустаяСтрока(ИмяТекущейСтраницы) И ИмяРебенка <> ИмяТекущейСтраницы Тогда
|
|
5261
|
+
СкрытыхДетей = СкрытыхДетей + 1;
|
|
5042
5262
|
Продолжить;
|
|
5043
5263
|
КонецЕсли;
|
|
5044
5264
|
РебенокДляСериализации = ?(Не ПустаяСтрока(ИмяТекущейСтраницы) И ИмяРебенка = ИмяТекущейСтраницы И ТекущаяСтраницаГруппы <> Неопределено, ТекущаяСтраницаГруппы, Ребенок);
|
|
5045
5265
|
СтруктураРебенка = ОбъектВСтруктуру(РебенокДляСериализации, ТекущаяГлубина + 1, МаксимальнаяГлубина, ПоказыватьСкрытые, Видимость, Доступность, ТолькоПросмотр);
|
|
5046
5266
|
Если СтруктураРебенка <> Неопределено Тогда
|
|
5047
5267
|
МассивДетей.Добавить(СтруктураРебенка);
|
|
5268
|
+
Иначе
|
|
5269
|
+
СкрытыхДетей = СкрытыхДетей + 1;
|
|
5048
5270
|
КонецЕсли;
|
|
5049
5271
|
КонецЦикла;
|
|
5050
5272
|
Результат.Вставить("children", МассивДетей);
|
|
5273
|
+
Если СкрытыхДетей > 0 Тогда
|
|
5274
|
+
Результат.Вставить("hidden_children_count", СкрытыхДетей);
|
|
5275
|
+
КонецЕсли;
|
|
5051
5276
|
КонецЕсли;
|
|
5052
5277
|
|
|
5053
5278
|
Возврат Результат;
|
|
Binary file
|
|
@@ -44,7 +44,7 @@ _RAG = RagService()
|
|
|
44
44
|
_RAG_ENABLED = os.getenv("ONEC_MCP_DISABLE_RAG", "").strip().lower() not in {"1", "true", "yes", "on"}
|
|
45
45
|
_CREDENTIAL_STORE_ENABLED = os.getenv("ONEC_MCP_DISABLE_CREDENTIAL_STORE", "").strip().lower() not in {"1", "true", "yes", "on"}
|
|
46
46
|
_DEV_TOOLS_ENABLED = os.getenv("ONEC_MCP_DEV_TOOLS", "").strip().lower() in {"1", "true", "yes", "on"}
|
|
47
|
-
DEFAULT_SESSION = "default"
|
|
47
|
+
DEFAULT_SESSION = os.getenv("ONEC_MCP_DEFAULT_SESSION", f"default-{os.getpid()}")
|
|
48
48
|
STATE_FILE = os_support.runtime_path("ONEC_MCP_SESSION_STATE", "sessions.json")
|
|
49
49
|
AUTORELOAD_PID_FILE = os_support.runtime_path("ONEC_MCP_AUTORELOAD_PID_FILE", "answer42-autoreload.pid")
|
|
50
50
|
AUTORELOAD_LOG_FILE = os_support.runtime_path("ONEC_MCP_AUTORELOAD_LOG_FILE", "answer42-autoreload.log")
|
|
@@ -978,6 +978,29 @@ def _walk_ui_tree(node: Any):
|
|
|
978
978
|
yield from _walk_ui_tree(child)
|
|
979
979
|
|
|
980
980
|
|
|
981
|
+
def _summarize_ui_tree(tree: Any, max_depth: int, include_hidden: bool) -> dict[str, Any]:
|
|
982
|
+
"""Return compact diagnostics about omitted/truncated UI tree parts."""
|
|
983
|
+
nodes = list(_walk_ui_tree(tree))
|
|
984
|
+
truncated_nodes = [node for node in nodes if node.get("truncated")]
|
|
985
|
+
hidden_children_total = 0
|
|
986
|
+
hidden_nodes = []
|
|
987
|
+
for node in nodes:
|
|
988
|
+
hidden_count = node.get("hidden_children_count", 0)
|
|
989
|
+
if isinstance(hidden_count, int):
|
|
990
|
+
hidden_children_total += hidden_count
|
|
991
|
+
if node.get("visible") is False:
|
|
992
|
+
hidden_nodes.append(node)
|
|
993
|
+
return {
|
|
994
|
+
"max_depth": max_depth,
|
|
995
|
+
"include_hidden": include_hidden,
|
|
996
|
+
"nodes_count": len(nodes),
|
|
997
|
+
"truncated": bool(truncated_nodes),
|
|
998
|
+
"truncated_nodes_count": len(truncated_nodes),
|
|
999
|
+
"hidden_children_count": hidden_children_total,
|
|
1000
|
+
"hidden_nodes_count": len(hidden_nodes),
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
|
|
981
1004
|
def _metadata_name_from_navigation_url(url: str) -> str:
|
|
982
1005
|
"""Best-effort conversion from e1cib/list/... to a metadata full name."""
|
|
983
1006
|
value = str(url or "")
|
|
@@ -3120,6 +3143,8 @@ async def ui_tree(max_depth: Annotated[int, "Maximum UI tree depth"] = 4,
|
|
|
3120
3143
|
omitted by default; pass include_hidden=true to inspect the full 1C form tree.
|
|
3121
3144
|
"""
|
|
3122
3145
|
tree = await _bridge_call_recorded(session_id, "ui_tree", {"max_depth": max_depth, "include_hidden": include_hidden})
|
|
3146
|
+
if isinstance(tree, dict):
|
|
3147
|
+
tree["tree_summary"] = _summarize_ui_tree(tree, max_depth=max_depth, include_hidden=include_hidden)
|
|
3123
3148
|
if include_rag_types:
|
|
3124
3149
|
sess = _session(session_id)
|
|
3125
3150
|
if _RAG_ENABLED and sess.rag_enabled:
|
|
@@ -3143,11 +3168,12 @@ async def command_bar(*,
|
|
|
3143
3168
|
async def click_button(
|
|
3144
3169
|
name: Annotated[str, "Button name or title text"],
|
|
3145
3170
|
search_by_title: Annotated[bool, "If true, match visible title instead of object name"] = False,
|
|
3171
|
+
max_depth: Annotated[int, "Maximum search depth"] = 8,
|
|
3146
3172
|
*,
|
|
3147
3173
|
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
3148
3174
|
) -> Any:
|
|
3149
3175
|
"""Find a button in the active UI tree and press it."""
|
|
3150
|
-
return await _bridge_call_recorded(session_id, "click_button", {"name": name, "search_by_title": search_by_title})
|
|
3176
|
+
return await _bridge_call_recorded(session_id, "click_button", {"name": name, "search_by_title": search_by_title, "max_depth": max_depth})
|
|
3151
3177
|
|
|
3152
3178
|
|
|
3153
3179
|
@mcp.tool()
|
|
@@ -3163,6 +3189,20 @@ async def find_object(
|
|
|
3163
3189
|
return await _bridge_call_recorded(session_id, "find_object", {"name": name, "title": title, "type": type, "max_depth": max_depth})
|
|
3164
3190
|
|
|
3165
3191
|
|
|
3192
|
+
@mcp.tool()
|
|
3193
|
+
async def search_objects(
|
|
3194
|
+
name: Annotated[str, "Object technical name, exact match when specified"] = "",
|
|
3195
|
+
title: Annotated[str, "Object visible title/caption, exact match when specified"] = "",
|
|
3196
|
+
type: Annotated[str, "1C test object type, e.g. ТестируемаяФорма"] = "",
|
|
3197
|
+
limit: Annotated[int, "Maximum number of matched objects to return"] = 50,
|
|
3198
|
+
max_depth: Annotated[int, "Maximum search depth"] = 8,
|
|
3199
|
+
*,
|
|
3200
|
+
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
3201
|
+
) -> Any:
|
|
3202
|
+
"""Find all UI objects matching name, title and/or 1C test object type."""
|
|
3203
|
+
return await _bridge_call_recorded(session_id, "search_objects", {"name": name, "title": title, "type": type, "limit": limit, "max_depth": max_depth})
|
|
3204
|
+
|
|
3205
|
+
|
|
3166
3206
|
@mcp.tool()
|
|
3167
3207
|
async def get_object(
|
|
3168
3208
|
name: Annotated[str, "Object name pattern"] = "",
|
|
@@ -3297,7 +3337,11 @@ async def tabular_documents(
|
|
|
3297
3337
|
*,
|
|
3298
3338
|
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
3299
3339
|
) -> Any:
|
|
3300
|
-
"""List tabular document fields with dimensions; optionally include a cells range.
|
|
3340
|
+
"""List tabular document fields with dimensions; optionally include a cells range.
|
|
3341
|
+
|
|
3342
|
+
include_text is intended for small/partial ranges. For large tabular
|
|
3343
|
+
documents, save/export the report to a file and analyze that file instead.
|
|
3344
|
+
"""
|
|
3301
3345
|
return await _bridge_call_recorded(session_id, "tabular_documents", {"include_text": include_text, "max_depth": max_depth, "row_start": row_start, "row_end": row_end, "column_start": column_start, "column_end": column_end, "max_rows": max_rows, "max_columns": max_columns})
|
|
3302
3346
|
|
|
3303
3347
|
|
|
@@ -3315,7 +3359,13 @@ async def tabular_document_text(
|
|
|
3315
3359
|
*,
|
|
3316
3360
|
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
3317
3361
|
) -> Any:
|
|
3318
|
-
"""Return dimensions and a cells range of a tabular document field in the active window.
|
|
3362
|
+
"""Return dimensions and a cells range of a tabular document field in the active window.
|
|
3363
|
+
|
|
3364
|
+
Use this for small tables or partial reads (headers, totals, spot checks). For
|
|
3365
|
+
large reports/tabular documents, prefer tabular_document_save/report export
|
|
3366
|
+
and analyze the saved PDF/XLSX/TXT/HTML file instead of reading UI cells one
|
|
3367
|
+
by one.
|
|
3368
|
+
"""
|
|
3319
3369
|
return await _bridge_call_recorded(session_id, "tabular_document_text", {"name": name, "title": title, "max_depth": max_depth, "row_start": row_start, "row_end": row_end, "column_start": column_start, "column_end": column_end, "max_rows": max_rows, "max_columns": max_columns})
|
|
3320
3370
|
|
|
3321
3371
|
|
|
@@ -3885,30 +3935,88 @@ async def configuration_name_version_from_subsystems(
|
|
|
3885
3935
|
) -> dict[str, Any]:
|
|
3886
3936
|
"""Open the 'Subsystem versions' information register and return the main configuration name/version.
|
|
3887
3937
|
|
|
3888
|
-
The tool reads
|
|
3889
|
-
|
|
3938
|
+
The tool reads `РегистрСведений.ВерсииПодсистем` via the list output
|
|
3939
|
+
command so that columns available to the dynamic list, including
|
|
3940
|
+
`Это основная конфигурация`, are visible even when they are not displayed
|
|
3941
|
+
in the default form table.
|
|
3890
3942
|
"""
|
|
3891
3943
|
navigation_link = "e1cib/list/РегистрСведений.ВерсииПодсистем"
|
|
3892
3944
|
await _bridge_call_recorded(session_id, "open_navigation_link", {"navigation_link": navigation_link})
|
|
3893
|
-
rows_result = await _bridge_call_recorded(session_id, "table_rows", {"name": "Список", "title": "", "limit": 200, "max_depth": 8})
|
|
3894
|
-
rows = rows_result.get("rows", []) if isinstance(rows_result, dict) else []
|
|
3895
3945
|
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3946
|
+
output_result = await _bridge_call_recorded(session_id, "dynamic_list_output", {
|
|
3947
|
+
"button": "",
|
|
3948
|
+
"wait_seconds": 3,
|
|
3949
|
+
"max_depth": 8,
|
|
3950
|
+
})
|
|
3951
|
+
tabular_docs = output_result.get("tabular_documents", {}) if isinstance(output_result, dict) else {}
|
|
3952
|
+
items = tabular_docs.get("items", []) if isinstance(tabular_docs, dict) else []
|
|
3953
|
+
|
|
3954
|
+
main: dict[str, str] = {}
|
|
3955
|
+
cells: list[list[str]] = []
|
|
3956
|
+
if items:
|
|
3957
|
+
doc_name = items[0].get("name", "")
|
|
3958
|
+
text_result = await _bridge_call_recorded(session_id, "tabular_document_text", {
|
|
3959
|
+
"name": doc_name,
|
|
3960
|
+
"max_rows": 50,
|
|
3961
|
+
"max_columns": 20,
|
|
3962
|
+
})
|
|
3963
|
+
cells = text_result.get("cells", []) if isinstance(text_result, dict) else []
|
|
3964
|
+
main = _find_main_configuration_in_tabular_cells(cells)
|
|
3899
3965
|
|
|
3900
3966
|
return {
|
|
3901
3967
|
"found": bool(main),
|
|
3902
|
-
"configuration_name":
|
|
3903
|
-
"configuration_version":
|
|
3968
|
+
"configuration_name": main.get("configuration_name", ""),
|
|
3969
|
+
"configuration_version": main.get("configuration_version", ""),
|
|
3904
3970
|
"main_configuration_row": main,
|
|
3905
|
-
"main_configuration_rows_count":
|
|
3906
|
-
"rows_count": len(
|
|
3971
|
+
"main_configuration_rows_count": 1 if main else 0,
|
|
3972
|
+
"rows_count": max(0, len(cells) - 1),
|
|
3907
3973
|
"navigation_link": navigation_link,
|
|
3908
3974
|
"source": "РегистрСведений.ВерсииПодсистем",
|
|
3909
3975
|
}
|
|
3910
3976
|
|
|
3911
3977
|
|
|
3978
|
+
def _find_main_configuration_in_tabular_cells(cells: list[list[str]]) -> dict[str, str]:
|
|
3979
|
+
"""Scan tabular document cells for the main configuration row."""
|
|
3980
|
+
if not cells or len(cells) < 2:
|
|
3981
|
+
return {}
|
|
3982
|
+
|
|
3983
|
+
header_row_idx = -1
|
|
3984
|
+
name_col = -1
|
|
3985
|
+
version_col = -1
|
|
3986
|
+
main_cfg_col = -1
|
|
3987
|
+
for i, row in enumerate(cells):
|
|
3988
|
+
local_name_col = local_version_col = local_main_cfg_col = -1
|
|
3989
|
+
for j, cell in enumerate(row):
|
|
3990
|
+
cell_norm = str(cell).strip()
|
|
3991
|
+
if cell_norm == "Имя подсистемы":
|
|
3992
|
+
local_name_col = j
|
|
3993
|
+
elif cell_norm == "Версия":
|
|
3994
|
+
local_version_col = j
|
|
3995
|
+
elif cell_norm == "Это основная конфигурация":
|
|
3996
|
+
local_main_cfg_col = j
|
|
3997
|
+
if local_name_col >= 0 and local_version_col >= 0 and local_main_cfg_col >= 0:
|
|
3998
|
+
header_row_idx = i
|
|
3999
|
+
name_col = local_name_col
|
|
4000
|
+
version_col = local_version_col
|
|
4001
|
+
main_cfg_col = local_main_cfg_col
|
|
4002
|
+
break
|
|
4003
|
+
|
|
4004
|
+
if header_row_idx < 0:
|
|
4005
|
+
return {}
|
|
4006
|
+
|
|
4007
|
+
for row in cells[header_row_idx + 1:]:
|
|
4008
|
+
if len(row) <= max(name_col, version_col, main_cfg_col):
|
|
4009
|
+
continue
|
|
4010
|
+
if _is_truthy_1c(str(row[main_cfg_col])):
|
|
4011
|
+
return {
|
|
4012
|
+
"configuration_name": str(row[name_col]).strip(),
|
|
4013
|
+
"configuration_version": str(row[version_col]).strip(),
|
|
4014
|
+
"Это основная конфигурация": str(row[main_cfg_col]).strip(),
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
return {}
|
|
4018
|
+
|
|
4019
|
+
|
|
3912
4020
|
@mcp.tool()
|
|
3913
4021
|
async def installed_extensions_from_catalog(
|
|
3914
4022
|
*,
|
|
@@ -4025,11 +4133,12 @@ async def table_set_row_values(
|
|
|
4025
4133
|
table: Annotated[str, "Table technical name"],
|
|
4026
4134
|
values: Annotated[str, "JSON object mapping column names/titles to values"],
|
|
4027
4135
|
search_by_title: Annotated[bool, "If true, match visible title instead of object name"] = False,
|
|
4136
|
+
max_depth: Annotated[int, "Maximum UI search depth"] = 20,
|
|
4028
4137
|
*,
|
|
4029
4138
|
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
4030
4139
|
) -> Any:
|
|
4031
4140
|
"""Set multiple fields in the current row of a form table and verify each value."""
|
|
4032
|
-
return await _bridge_call_recorded(session_id, "table_set_row_values", {"table": table, "values": values, "search_by_title": search_by_title})
|
|
4141
|
+
return await _bridge_call_recorded(session_id, "table_set_row_values", {"table": table, "values": values, "search_by_title": search_by_title, "max_depth": max_depth})
|
|
4033
4142
|
|
|
4034
4143
|
|
|
4035
4144
|
@mcp.tool()
|
|
@@ -4468,7 +4577,13 @@ def screenshot(
|
|
|
4468
4577
|
*,
|
|
4469
4578
|
session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
|
|
4470
4579
|
) -> dict[str, Any]:
|
|
4471
|
-
"""Capture a screenshot from the MCP host. Defaults to the active visible 1C window.
|
|
4580
|
+
"""Capture a screenshot from the MCP host. Defaults to the active visible 1C window.
|
|
4581
|
+
|
|
4582
|
+
Screenshots are for user evidence and UI diagnostics only. Do not use them
|
|
4583
|
+
to analyze report/table/tabular-document/list data; use structured tools
|
|
4584
|
+
such as tabular_document_text, tabular_documents, table_rows,
|
|
4585
|
+
dynamic_list_output, or report export/save tools instead.
|
|
4586
|
+
"""
|
|
4472
4587
|
output_path = Path(path)
|
|
4473
4588
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
4474
4589
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|