answer42 0.4.63__tar.gz → 0.4.64__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.
Files changed (64) hide show
  1. {answer42-0.4.63 → answer42-0.4.64}/PKG-INFO +1 -1
  2. {answer42-0.4.63 → answer42-0.4.64}/pyproject.toml +1 -1
  3. {answer42-0.4.63 → answer42-0.4.64}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl +123 -20
  4. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/server.py +37 -17
  5. {answer42-0.4.63 → answer42-0.4.64}/.gitignore +0 -0
  6. {answer42-0.4.63 → answer42-0.4.64}/LICENSE +0 -0
  7. {answer42-0.4.63 → answer42-0.4.64}/README.md +0 -0
  8. {answer42-0.4.63 → answer42-0.4.64}/credentials.example.json +0 -0
  9. {answer42-0.4.63 → answer42-0.4.64}/docs/agent-installation.md +0 -0
  10. {answer42-0.4.63 → answer42-0.4.64}/docs/architecture.md +0 -0
  11. {answer42-0.4.63 → answer42-0.4.64}/docs/assets/answer42-logo.png +0 -0
  12. {answer42-0.4.63 → answer42-0.4.64}/docs/assets/platform42-logo.svg +0 -0
  13. {answer42-0.4.63 → answer42-0.4.64}/docs/installation.md +0 -0
  14. {answer42-0.4.63 → answer42-0.4.64}/scripts/build_cf.py +0 -0
  15. {answer42-0.4.63 → answer42-0.4.64}/scripts/build_pages.py +0 -0
  16. {answer42-0.4.63 → answer42-0.4.64}/scripts/e2e_http_smoke.py +0 -0
  17. {answer42-0.4.63 → answer42-0.4.64}/scripts/e2e_stable.py +0 -0
  18. {answer42-0.4.63 → answer42-0.4.64}/scripts/openclaw_mcp_autoreload.py +0 -0
  19. {answer42-0.4.63 → answer42-0.4.64}/scripts/rag_cli.py +0 -0
  20. {answer42-0.4.63 → answer42-0.4.64}/src/cf/ConfigDumpInfo.xml +0 -0
  21. {answer42-0.4.63 → answer42-0.4.64}/src/cf/Configuration.xml +0 -0
  22. {answer42-0.4.63 → answer42-0.4.64}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
  23. {answer42-0.4.63 → answer42-0.4.64}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
  24. {answer42-0.4.63 → answer42-0.4.64}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
  25. {answer42-0.4.63 → answer42-0.4.64}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
  26. {answer42-0.4.63 → answer42-0.4.64}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
  27. {answer42-0.4.63 → answer42-0.4.64}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  28. {answer42-0.4.63 → answer42-0.4.64}/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
  29. {answer42-0.4.63 → answer42-0.4.64}/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
  30. {answer42-0.4.63 → answer42-0.4.64}/src/client_cf/ConfigDumpInfo.xml +0 -0
  31. {answer42-0.4.63 → answer42-0.4.64}/src/client_cf/Configuration.xml +0 -0
  32. {answer42-0.4.63 → answer42-0.4.64}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  33. {answer42-0.4.63 → answer42-0.4.64}/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
  34. {answer42-0.4.63 → answer42-0.4.64}/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
  35. {answer42-0.4.63 → answer42-0.4.64}/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
  36. {answer42-0.4.63 → answer42-0.4.64}/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
  37. {answer42-0.4.63 → answer42-0.4.64}/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
  38. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/__init__.py +0 -0
  39. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
  40. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
  41. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/assets/__init__.py +0 -0
  42. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
  43. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
  44. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/assets/skills/answer42-universal-report/SKILL.md +0 -0
  45. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/auth.py +0 -0
  46. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/bridge.py +0 -0
  47. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/credentials.py +0 -0
  48. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/os_support.py +0 -0
  49. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/platform.py +0 -0
  50. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/protocol.py +0 -0
  51. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/__init__.py +0 -0
  52. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/detect.py +0 -0
  53. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/dump.py +0 -0
  54. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/model.py +0 -0
  55. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/parsers.py +0 -0
  56. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/service.py +0 -0
  57. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/store.py +0 -0
  58. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/summary_backend.py +0 -0
  59. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/rag/summary_vector.py +0 -0
  60. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/recorder.py +0 -0
  61. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/release_helper.py +0 -0
  62. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/runtime.py +0 -0
  63. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/skill_installer.py +0 -0
  64. {answer42-0.4.63 → answer42-0.4.64}/src/mcp_1c/window_control.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: answer42
3
- Version: 0.4.63
3
+ Version: 0.4.64
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.4.63"
3
+ version = "0.4.64"
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"
@@ -1890,10 +1890,10 @@
1890
1890
  АктивноеОкно = КомандныйИнтерфейсЦелевоеОкно(Параметры);
1891
1891
  Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
1892
1892
  Если Интерфейс = Неопределено Тогда
1893
- РезультатКоманды = Новый Структура("available,reason,window", Ложь, "not_available", ОбъектВСтруктуру(АктивноеОкно, 0));
1893
+ РезультатКоманды = Новый Структура("available,reason,window", Ложь, "not_available", КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1894
1894
  Иначе
1895
1895
  Дерево = КомандныйИнтерфейсОбъектВСтруктуру(Интерфейс, 0);
1896
- РезультатКоманды = Новый Структура("available,interface,window", Истина, Дерево, ОбъектВСтруктуру(АктивноеОкно, 0));
1896
+ РезультатКоманды = Новый Структура("available,interface,window", Истина, Дерево, КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1897
1897
  КонецЕсли;
1898
1898
  ЗакрытьКомандныйИнтерфейсПослеРаботыMCP(АктивноеОкно);
1899
1899
  Возврат РезультатКоманды;
@@ -1912,10 +1912,10 @@
1912
1912
  АктивноеОкно = КомандныйИнтерфейсЦелевоеОкно(Параметры);
1913
1913
  Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
1914
1914
  Если Интерфейс = Неопределено Тогда
1915
- РезультатКоманды = Новый Структура("available,reason,window", Ложь, "not_available", ОбъектВСтруктуру(АктивноеОкно, 0));
1915
+ РезультатКоманды = Новый Структура("available,reason,window", Ложь, "not_available", КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1916
1916
  Иначе
1917
1917
  Разделы = РазделыКомандногоИнтерфейса(Интерфейс);
1918
- РезультатКоманды = Новый Структура("available,sections,window", Истина, Разделы, ОбъектВСтруктуру(АктивноеОкно, 0));
1918
+ РезультатКоманды = Новый Структура("available,sections,window", Истина, Разделы, КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1919
1919
  КонецЕсли;
1920
1920
  ЗакрытьКомандныйИнтерфейсПослеРаботыMCP(АктивноеОкно);
1921
1921
  Возврат РезультатКоманды;
@@ -1937,25 +1937,25 @@
1937
1937
  АктивноеОкно = КомандныйИнтерфейсЦелевоеОкно(Параметры);
1938
1938
  Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
1939
1939
  Если Интерфейс = Неопределено Тогда
1940
- РезультатКоманды = Новый Структура("available,reason,window", Ложь, "not_available", ОбъектВСтруктуру(АктивноеОкно, 0));
1940
+ РезультатКоманды = Новый Структура("available,reason,window", Ложь, "not_available", КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1941
1941
  Иначе
1942
1942
  Корень = Интерфейс;
1943
1943
  Если Не ПустаяСтрока(ЗаголовокРаздела) Тогда
1944
1944
  КнопкаРаздела = КомандныйИнтерфейсНайтиРаздел(Интерфейс, ЗаголовокРаздела);
1945
1945
  Если КнопкаРаздела = Неопределено Тогда
1946
- РезультатКоманды = Новый Структура("available,found,reason,section,window", Истина, Ложь, "section_not_found", ЗаголовокРаздела, ОбъектВСтруктуру(АктивноеОкно, 0));
1946
+ РезультатКоманды = Новый Структура("available,found,reason,section,window", Истина, Ложь, "section_not_found", ЗаголовокРаздела, КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1947
1947
  Иначе
1948
1948
  КнопкаРаздела.Нажать();
1949
1949
  Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
1950
1950
  Корень = Интерфейс;
1951
1951
  Дерево = КомандныйИнтерфейсОбъектВСтруктуру(Корень, 0);
1952
1952
  ЛокКоманды = КомандыКомандногоИнтерфейса(Корень, Истина);
1953
- РезультатКоманды = Новый Структура("available,found,section,tree,commands,window", Истина, Истина, ЗаголовокРаздела, Дерево, ЛокКоманды, ОбъектВСтруктуру(АктивноеОкно, 0));
1953
+ РезультатКоманды = Новый Структура("available,found,section,tree,commands,window", Истина, Истина, ЗаголовокРаздела, Дерево, ЛокКоманды, КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1954
1954
  КонецЕсли;
1955
1955
  Иначе
1956
1956
  Дерево = КомандныйИнтерфейсОбъектВСтруктуру(Корень, 0);
1957
1957
  ЛокКоманды = КомандыКомандногоИнтерфейса(Корень, Истина);
1958
- РезультатКоманды = Новый Структура("available,found,section,tree,commands,window", Истина, Истина, ЗаголовокРаздела, Дерево, ЛокКоманды, ОбъектВСтруктуру(АктивноеОкно, 0));
1958
+ РезультатКоманды = Новый Структура("available,found,section,tree,commands,window", Истина, Истина, ЗаголовокРаздела, Дерево, ЛокКоманды, КраткоеОписаниеОбъектаMCP(АктивноеОкно));
1959
1959
  КонецЕсли;
1960
1960
  КонецЕсли;
1961
1961
  ЗакрытьКомандныйИнтерфейсПослеРаботыMCP(АктивноеОкно, КнопкаРаздела);
@@ -1974,6 +1974,8 @@
1974
1974
  ВызватьИсключение "Тестируемое приложение не подключено.";
1975
1975
  КонецЕсли;
1976
1976
 
1977
+ Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
1978
+ Тайминги = Новый Структура;
1977
1979
  АктивноеОкно = Неопределено;
1978
1980
  КнопкаРаздела = Неопределено;
1979
1981
  Попытка
@@ -1984,35 +1986,70 @@
1984
1986
 
1985
1987
  ЗаголовокРаздела = СвойствоСтруктуры(Параметры, "section", "");
1986
1988
  Максимум = СвойствоСтруктуры(Параметры, "max_results", 20);
1989
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
1987
1990
  АктивноеОкно = КомандныйИнтерфейсЦелевоеОкно(Параметры);
1991
+ Тайминги.Вставить("target_window_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
1992
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
1988
1993
  Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
1994
+ Тайминги.Вставить("get_interface_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
1989
1995
  Если Интерфейс = Неопределено Тогда
1990
- РезультатПоиска = Новый Структура("available,reason,window", Ложь, "not_available", ОбъектВСтруктуру(АктивноеОкно, 0));
1996
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
1997
+ ОписаниеОкна = КраткоеОписаниеОбъектаMCP(АктивноеОкно);
1998
+ Тайминги.Вставить("window_summary_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
1999
+ РезультатПоиска = Новый Структура("available,reason,window", Ложь, "not_available", ОписаниеОкна);
1991
2000
  Иначе
1992
2001
  Если Не ПустаяСтрока(ЗаголовокРаздела) Тогда
2002
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
1993
2003
  КнопкаРаздела = КомандныйИнтерфейсНайтиРаздел(Интерфейс, ЗаголовокРаздела);
2004
+ Тайминги.Вставить("find_section_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
1994
2005
  Если КнопкаРаздела = Неопределено Тогда
1995
- РезультатПоиска = Новый Структура("available,found,reason,query,section,matches,window", Истина, Ложь, "section_not_found", СтрокаПоиска, ЗаголовокРаздела, Новый Массив, ОбъектВСтруктуру(АктивноеОкно, 0));
2006
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2007
+ ОписаниеОкна = КраткоеОписаниеОбъектаMCP(АктивноеОкно);
2008
+ Тайминги.Вставить("window_summary_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2009
+ РезультатПоиска = Новый Структура("available,found,reason,query,section,matches,window", Истина, Ложь, "section_not_found", СтрокаПоиска, ЗаголовокРаздела, Новый Массив, ОписаниеОкна);
1996
2010
  Иначе
2011
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
1997
2012
  КнопкаРаздела.Нажать();
1998
2013
  Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
2014
+ Тайминги.Вставить("activate_section_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2015
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
1999
2016
  ВсеКоманды = КомандыКомандногоИнтерфейса(Интерфейс, Истина);
2017
+ Тайминги.Вставить("collect_commands_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2018
+ Тайминги.Вставить("commands_count", ВсеКоманды.Количество());
2019
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2000
2020
  Совпадения = ПоискВКомандахКомандногоИнтерфейса(ВсеКоманды, СтрокаПоиска, Максимум);
2001
- РезультатПоиска = Новый Структура("available,found,query,section,count,matches,window", Истина, Совпадения.Количество() > 0, СтрокаПоиска, ЗаголовокРаздела, Совпадения.Количество(), Совпадения, ОбъектВСтруктуру(АктивноеОкно, 0));
2021
+ Тайминги.Вставить("match_commands_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2022
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2023
+ ОписаниеОкна = КраткоеОписаниеОбъектаMCP(АктивноеОкно);
2024
+ Тайминги.Вставить("window_summary_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2025
+ РезультатПоиска = Новый Структура("available,found,query,section,count,matches,window", Истина, Совпадения.Количество() > 0, СтрокаПоиска, ЗаголовокРаздела, Совпадения.Количество(), Совпадения, ОписаниеОкна);
2002
2026
  КонецЕсли;
2003
2027
  Иначе
2004
- ВсеКоманды = КомандыКомандногоИнтерфейса(Интерфейс, Истина);
2028
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2029
+ ВсеКоманды = КомандыВсехРазделовКомандногоИнтерфейса(АктивноеОкно, Интерфейс, СтрокаПоиска, Максимум, Тайминги);
2030
+ Тайминги.Вставить("collect_commands_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2031
+ Тайминги.Вставить("commands_count", ВсеКоманды.Количество());
2032
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2005
2033
  Совпадения = ПоискВКомандахКомандногоИнтерфейса(ВсеКоманды, СтрокаПоиска, Максимум);
2006
- РезультатПоиска = Новый Структура("available,found,query,section,count,matches,window", Истина, Совпадения.Количество() > 0, СтрокаПоиска, ЗаголовокРаздела, Совпадения.Количество(), Совпадения, ОбъектВСтруктуру(АктивноеОкно, 0));
2034
+ Тайминги.Вставить("match_commands_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2035
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2036
+ ОписаниеОкна = КраткоеОписаниеОбъектаMCP(АктивноеОкно);
2037
+ Тайминги.Вставить("window_summary_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2038
+ РезультатПоиска = Новый Структура("available,found,query,section,count,matches,window", Истина, Совпадения.Количество() > 0, СтрокаПоиска, ЗаголовокРаздела, Совпадения.Количество(), Совпадения, ОписаниеОкна);
2007
2039
  КонецЕсли;
2008
2040
  КонецЕсли;
2041
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2009
2042
  ЗакрытьКомандныйИнтерфейсПослеРаботыMCP(АктивноеОкно, КнопкаРаздела);
2043
+ Тайминги.Вставить("cleanup_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2044
+ Тайминги.Вставить("total_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
2045
+ РезультатПоиска.Вставить("_timings", Тайминги);
2010
2046
  Возврат РезультатПоиска;
2011
2047
  Исключение
2012
2048
  Если АктивноеОкно <> Неопределено Тогда
2013
2049
  ЗакрытьКомандныйИнтерфейсПослеРаботыMCP(АктивноеОкно, КнопкаРаздела);
2014
2050
  КонецЕсли;
2015
- Возврат Новый Структура("available,reason,error", Ложь, "not_supported", ОписаниеОшибки());
2051
+ Тайминги.Вставить("total_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
2052
+ Возврат Новый Структура("available,reason,error,_timings", Ложь, "not_supported", ОписаниеОшибки(), Тайминги);
2016
2053
  КонецПопытки;
2017
2054
  КонецФункции
2018
2055
 
@@ -2187,6 +2224,39 @@
2187
2224
  Возврат Результат;
2188
2225
  КонецФункции
2189
2226
 
2227
+ &НаКлиенте
2228
+ Функция КомандыВсехРазделовКомандногоИнтерфейса(АктивноеОкно, Интерфейс, СтрокаПоиска, Максимум, Тайминги)
2229
+ Результат = Новый Массив;
2230
+ ПросмотреноРазделов = 0;
2231
+ Разделы = РазделыКомандногоИнтерфейса(Интерфейс);
2232
+ Тайминги.Вставить("sections_count", Разделы.Количество());
2233
+ Для Каждого Раздел Из Разделы Цикл
2234
+ ЗаголовокРаздела = СвойствоСтруктуры(Раздел, "title", "");
2235
+ Если ПустаяСтрока(ЗаголовокРаздела) Тогда
2236
+ Продолжить;
2237
+ КонецЕсли;
2238
+ КнопкаРаздела = КомандныйИнтерфейсНайтиРаздел(Интерфейс, ЗаголовокРаздела);
2239
+ Если КнопкаРаздела = Неопределено Тогда
2240
+ Продолжить;
2241
+ КонецЕсли;
2242
+ Попытка
2243
+ КнопкаРаздела.Нажать();
2244
+ Интерфейс = АктивноеОкно.ПолучитьКомандныйИнтерфейс();
2245
+ ПросмотреноРазделов = ПросмотреноРазделов + 1;
2246
+ КомандыРаздела = КомандыКомандногоИнтерфейса(Интерфейс, Истина);
2247
+ Для Каждого Команда Из КомандыРаздела Цикл
2248
+ Команда.Вставить("section", ЗаголовокРаздела);
2249
+ Результат.Добавить(Команда);
2250
+ КонецЦикла;
2251
+ Исключение
2252
+ // Отдельный раздел командного интерфейса может быть недоступен в текущем состоянии UI.
2253
+ КонецПопытки;
2254
+ КонецЦикла;
2255
+ Тайминги.Вставить("sections_scanned", ПросмотреноРазделов);
2256
+ Возврат Результат;
2257
+ КонецФункции
2258
+
2259
+
2190
2260
  &НаКлиенте
2191
2261
  Процедура КомандыКомандногоИнтерфейсаРекурсивно(Объект, Результат, Путь, ИсключатьСлужебныеПанели)
2192
2262
  Если Объект = Неопределено Тогда
@@ -2250,10 +2320,18 @@
2250
2320
  Совпадение.Вставить("navigation_url", Ссылка);
2251
2321
  Совпадение.Вставить("path", Путь);
2252
2322
  Совпадение.Вставить("path_text", ТекстПути);
2253
- Совпадение.Вставить("score", КомандныйИнтерфейсОценкаСовпадения(Заголовок, ТекстПути, Ссылка, НормПоиск));
2254
- Результат.Добавить(Совпадение);
2255
- Если Максимум > 0 И Результат.Количество() >= Максимум Тогда
2256
- Прервать;
2323
+ Оценка = КомандныйИнтерфейсОценкаСовпадения(Заголовок, ТекстПути, Ссылка, НормПоиск);
2324
+ Совпадение.Вставить("score", Оценка);
2325
+ ИндексВставки = Результат.Количество();
2326
+ Для НомерСовпадения = 0 По Результат.Количество() - 1 Цикл
2327
+ Если СвойствоСтруктуры(Результат[НомерСовпадения], "score", 0) < Оценка Тогда
2328
+ ИндексВставки = НомерСовпадения;
2329
+ Прервать;
2330
+ КонецЕсли;
2331
+ КонецЦикла;
2332
+ Результат.Вставить(ИндексВставки, Совпадение);
2333
+ Если Максимум > 0 И Результат.Количество() > Максимум Тогда
2334
+ Результат.Удалить(Результат.Количество() - 1);
2257
2335
  КонецЕсли;
2258
2336
  КонецЕсли;
2259
2337
  КонецЦикла;
@@ -2899,19 +2977,44 @@
2899
2977
  Возврат СтрНайти(Поиск, "команднаяпанель") > 0;
2900
2978
  КонецФункции
2901
2979
 
2980
+ &НаКлиенте
2981
+ Функция КраткоеОписаниеОбъектаMCP(Объект)
2982
+ // Для диагностических полей window/object не сериализуем всё дерево формы:
2983
+ // на больших стартовых страницах БП полная ОбъектВСтруктуру() занимает десятки секунд.
2984
+ Возврат ОбъектВСтруктуру(Объект, 0, Истина, Истина, Истина, Ложь, Неопределено, Ложь, Ложь, Неопределено, Ложь);
2985
+ КонецФункции
2986
+
2902
2987
  &НаКлиенте
2903
2988
  Функция КоманднаяПанель()
2904
2989
  Если ТестируемоеПриложение = Неопределено Тогда
2905
2990
  ВызватьИсключение "Тестируемое приложение не подключено.";
2906
2991
  КонецЕсли;
2992
+ Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
2993
+ Тайминги = Новый Структура;
2994
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2907
2995
  АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
2996
+ Тайминги.Вставить("active_window_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
2908
2997
  Попытка
2998
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
2909
2999
  Панель = АктивноеОкно.ПолучитьКоманднуюПанель();
2910
- Возврат ОбъектВСтруктуру(Панель, 0);
3000
+ Тайминги.Вставить("get_command_panel_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
3001
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
3002
+ Результат = ОбъектВСтруктуру(Панель, 0);
3003
+ Тайминги.Вставить("serialize_panel_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
3004
+ Тайминги.Вставить("total_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
3005
+ Если Результат <> Неопределено И Результат <> "__skip__" Тогда
3006
+ Результат.Вставить("_timings", Тайминги);
3007
+ КонецЕсли;
3008
+ Возврат Результат;
2911
3009
  Исключение
2912
3010
  // Не все объекты API тест-клиента поддерживают ПолучитьКоманднуюПанель.
2913
3011
  // Возвращаем диагностику окна вместо пользовательской ошибки.
2914
- Возврат Новый Структура("available,reason,window", Ложь, "not_supported", ОбъектВСтруктуру(АктивноеОкно, 0));
3012
+ Тайминги.Вставить("error_before_window_serialize_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
3013
+ НачалоЭтапа = ТекущаяУниверсальнаяДатаВМиллисекундах();
3014
+ ОписаниеОкна = КраткоеОписаниеОбъектаMCP(АктивноеОкно);
3015
+ Тайминги.Вставить("serialize_window_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоЭтапа);
3016
+ Тайминги.Вставить("total_ms", ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало);
3017
+ Возврат Новый Структура("available,reason,window,_timings", Ложь, "not_supported", ОписаниеОкна, Тайминги);
2915
3018
  КонецПопытки;
2916
3019
  КонецФункции
2917
3020
 
@@ -2897,6 +2897,15 @@ def _rag_fields_for_object(object_full_name: str, snapshot: str | int | None = N
2897
2897
  return list(dedup.values())
2898
2898
 
2899
2899
 
2900
+
2901
+
2902
+ def _strip_bridge_timings(result: Any) -> Any:
2903
+ if not isinstance(result, dict) or "_timings" not in result:
2904
+ return result
2905
+ cleaned = dict(result)
2906
+ cleaned.pop("_timings", None)
2907
+ return cleaned
2908
+
2900
2909
  async def _bridge_call_recorded(
2901
2910
  session_id: str,
2902
2911
  method: str,
@@ -5557,15 +5566,17 @@ async def command_interface_search(
5557
5566
  section: Annotated[str, "Optional top-level section title substring to activate before searching"] = "",
5558
5567
  max_results: Annotated[int, "Maximum number of matches to return; 0 means no limit"] = 20,
5559
5568
  window_title: Annotated[str, "Optional window title substring to activate before reading command interface"] = "",
5569
+ include_timings: Annotated[bool, "Include diagnostic timing breakdown in the returned _timings field"] = False,
5560
5570
  *,
5561
5571
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
5562
5572
  ) -> Any:
5563
5573
  """Search live command-interface commands by title, path or navigation URL."""
5564
- return await _bridge_call_recorded(
5574
+ result = await _bridge_call_recorded(
5565
5575
  session_id,
5566
5576
  "command_interface_search",
5567
5577
  {"query": query, "section": section, "max_results": max_results, "window_title": window_title},
5568
5578
  )
5579
+ return result if include_timings else _strip_bridge_timings(result)
5569
5580
 
5570
5581
 
5571
5582
  @mcp.tool()
@@ -6084,7 +6095,9 @@ async def ui_tree_resource_action(
6084
6095
  raise ValueError(f"Unsupported ui_tree_resource_action action: {action!r}. Expected focus, click_button, activate_field, field_value_text, or field_info.")
6085
6096
 
6086
6097
  @mcp.tool()
6087
- async def command_bar(*,
6098
+ async def command_bar(
6099
+ include_timings: Annotated[bool, "Include diagnostic timing breakdown in the returned _timings field"] = False,
6100
+ *,
6088
6101
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
6089
6102
  ) -> Any:
6090
6103
  """Low-level diagnostic: return active window/form command-bar data when available.
@@ -6092,7 +6105,8 @@ async def command_bar(*,
6092
6105
  Prefer command_interface_sections/commands/search for application navigation commands,
6093
6106
  and object_command_panel/object_context_menu for commands of a specific UI object.
6094
6107
  """
6095
- return await _bridge_call_recorded(session_id, "command_bar", {})
6108
+ result = await _bridge_call_recorded(session_id, "command_bar", {})
6109
+ return result if include_timings else _strip_bridge_timings(result)
6096
6110
 
6097
6111
 
6098
6112
 
@@ -8412,34 +8426,40 @@ async def configuration_name_version_from_subsystems(
8412
8426
  warnings: list[str] = []
8413
8427
  main: dict[str, str] = {}
8414
8428
  cells: list[list[str]] = []
8415
- source_method = "sorted_first_row"
8429
+ source_method = "sorted_edge_row"
8416
8430
  current_row: dict[str, str] = {}
8417
8431
  sort_attempts: list[dict[str, Any]] = []
8418
8432
 
8419
- for attempt in range(1, 3):
8420
- sort_result = await _bridge_call_recorded(session_id, "dynamic_list_sort_by_column", {
8433
+ sort_result = await _bridge_call_recorded(session_id, "dynamic_list_sort_by_column", {
8434
+ "name": "Список",
8435
+ "title": "",
8436
+ "column_title": "Это основная конфигурация",
8437
+ })
8438
+ ordered = bool(sort_result.get("ordered")) if isinstance(sort_result, dict) else False
8439
+ for direction, label in (("first", "first_after_sort"), ("last", "last_after_sort")):
8440
+ move_result = await _bridge_call_recorded(session_id, "table_move_row", {
8421
8441
  "name": "Список",
8422
8442
  "title": "",
8423
- "column_title": "Это основная конфигурация",
8443
+ "direction": direction,
8444
+ "steps": 1,
8424
8445
  })
8425
- # УстановитьПорядок() toggles the sort direction if the list is already
8426
- # sorted by the same column. Read the current/top row after sorting; if
8427
- # the first direction puts Нет first, toggle once and read again.
8428
8446
  row_result = await _bridge_call_recorded(session_id, "table_current_row", {"name": "Список", "title": ""})
8429
8447
  current_row = _table_row_pairs(row_result)
8430
8448
  main = _configuration_from_sorted_first_row(current_row)
8431
8449
  sort_attempts.append({
8432
- "attempt": attempt,
8433
- "ordered": bool(sort_result.get("ordered")) if isinstance(sort_result, dict) else False,
8434
- "read_method": "current_after_sort",
8435
- "first_row_is_main": bool(main),
8436
- "first_row_main_marker": _row_value(current_row, "Это основная конфигурация", "ЭтоОсновнаяКонфигурация"),
8450
+ "attempt": len(sort_attempts) + 1,
8451
+ "ordered": ordered,
8452
+ "move_direction": direction,
8453
+ "move_result": move_result if isinstance(move_result, dict) else {},
8454
+ "read_method": label,
8455
+ "row_is_main": bool(main),
8456
+ "row_main_marker": _row_value(current_row, "Это основная конфигурация", "ЭтоОсновнаяКонфигурация"),
8437
8457
  })
8438
8458
  if main:
8439
8459
  break
8440
8460
 
8441
8461
  if not main:
8442
- warnings.append("Sorted first list row did not expose the main configuration; falling back to MXL list output")
8462
+ warnings.append("Sorted edge list rows did not expose the main configuration; falling back to MXL list output")
8443
8463
  source_method = "list_output_mxl"
8444
8464
  output_result = await _bridge_call_recorded(session_id, "dynamic_list_output", {
8445
8465
  "button": "",
@@ -8465,7 +8485,7 @@ async def configuration_name_version_from_subsystems(
8465
8485
  "configuration_version": main.get("configuration_version", ""),
8466
8486
  "main_configuration_row": main,
8467
8487
  "main_configuration_rows_count": 1 if main else 0,
8468
- "rows_count": 1 if source_method == "sorted_first_row" and current_row else max(0, len(cells) - 1),
8488
+ "rows_count": 1 if source_method == "sorted_edge_row" and current_row else max(0, len(cells) - 1),
8469
8489
  "warnings": warnings,
8470
8490
  "navigation_link": navigation_link,
8471
8491
  "source": "РегистрСведений.ВерсииПодсистем",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes