answer42 0.4.98__tar.gz → 0.4.99__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.98 → answer42-0.4.99}/PKG-INFO +4 -4
  2. {answer42-0.4.98 → answer42-0.4.99}/README.md +3 -3
  3. {answer42-0.4.98 → answer42-0.4.99}/pyproject.toml +1 -1
  4. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
  5. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
  6. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/runtime.py +33 -6
  7. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/server.py +8 -11
  8. {answer42-0.4.98 → answer42-0.4.99}/.gitignore +0 -0
  9. {answer42-0.4.98 → answer42-0.4.99}/LICENSE +0 -0
  10. {answer42-0.4.98 → answer42-0.4.99}/credentials.example.json +0 -0
  11. {answer42-0.4.98 → answer42-0.4.99}/docs/agent-installation.md +0 -0
  12. {answer42-0.4.98 → answer42-0.4.99}/docs/architecture.md +0 -0
  13. {answer42-0.4.98 → answer42-0.4.99}/docs/assets/answer42-logo.png +0 -0
  14. {answer42-0.4.98 → answer42-0.4.99}/docs/assets/platform42-logo.svg +0 -0
  15. {answer42-0.4.98 → answer42-0.4.99}/docs/installation.md +0 -0
  16. {answer42-0.4.98 → answer42-0.4.99}/scripts/build_cf.py +0 -0
  17. {answer42-0.4.98 → answer42-0.4.99}/scripts/build_pages.py +0 -0
  18. {answer42-0.4.98 → answer42-0.4.99}/scripts/e2e_http_smoke.py +0 -0
  19. {answer42-0.4.98 → answer42-0.4.99}/scripts/e2e_stable.py +0 -0
  20. {answer42-0.4.98 → answer42-0.4.99}/scripts/openclaw_mcp_autoreload.py +0 -0
  21. {answer42-0.4.98 → answer42-0.4.99}/scripts/rag_cli.py +0 -0
  22. {answer42-0.4.98 → answer42-0.4.99}/src/cf/ConfigDumpInfo.xml +0 -0
  23. {answer42-0.4.98 → answer42-0.4.99}/src/cf/Configuration.xml +0 -0
  24. {answer42-0.4.98 → answer42-0.4.99}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
  25. {answer42-0.4.98 → answer42-0.4.99}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl" +0 -0
  26. {answer42-0.4.98 → answer42-0.4.99}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
  27. {answer42-0.4.98 → answer42-0.4.99}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
  28. {answer42-0.4.98 → answer42-0.4.99}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
  29. {answer42-0.4.98 → answer42-0.4.99}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
  30. {answer42-0.4.98 → answer42-0.4.99}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  31. {answer42-0.4.98 → answer42-0.4.99}/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
  32. {answer42-0.4.98 → answer42-0.4.99}/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
  33. {answer42-0.4.98 → answer42-0.4.99}/src/client_cf/ConfigDumpInfo.xml +0 -0
  34. {answer42-0.4.98 → answer42-0.4.99}/src/client_cf/Configuration.xml +0 -0
  35. {answer42-0.4.98 → answer42-0.4.99}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  36. {answer42-0.4.98 → answer42-0.4.99}/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
  37. {answer42-0.4.98 → answer42-0.4.99}/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
  38. {answer42-0.4.98 → answer42-0.4.99}/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
  39. {answer42-0.4.98 → answer42-0.4.99}/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
  40. {answer42-0.4.98 → answer42-0.4.99}/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
  41. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/__init__.py +0 -0
  42. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/assets/__init__.py +0 -0
  43. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
  44. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
  45. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/assets/skills/answer42-universal-report/SKILL.md +0 -0
  46. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/auth.py +0 -0
  47. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/bridge.py +0 -0
  48. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/credentials.py +0 -0
  49. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/os_support.py +0 -0
  50. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/platform.py +0 -0
  51. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/protocol.py +0 -0
  52. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/__init__.py +0 -0
  53. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/detect.py +0 -0
  54. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/dump.py +0 -0
  55. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/model.py +0 -0
  56. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/parsers.py +0 -0
  57. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/service.py +0 -0
  58. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/store.py +0 -0
  59. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/summary_backend.py +0 -0
  60. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/rag/summary_vector.py +0 -0
  61. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/recorder.py +0 -0
  62. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/release_helper.py +0 -0
  63. {answer42-0.4.98 → answer42-0.4.99}/src/mcp_1c/skill_installer.py +0 -0
  64. {answer42-0.4.98 → answer42-0.4.99}/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.98
3
+ Version: 0.4.99
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>
@@ -164,9 +164,9 @@ answer42 install-skills --dry-run --agent all
164
164
 
165
165
  ## Linux display prerequisite
166
166
 
167
- На Linux `start_session` **всегда** запускает два изолированных Xvfb-дисплея: один для test manager и второй для test client. Это намеренно не использует существующий `DISPLAY`, даже если Answer42 запущен в живой X11-сессии: автоматизация и её скриншоты не должны захватывать пользовательский рабочий стол.
167
+ На Linux `start_session` использует живую X11-сессию, если процесс Answer42 унаследовал непустой `DISPLAY`: test manager и test client запускаются на этом дисплее, поэтому их окна и скриншоты видны на desktop.
168
168
 
169
- Поэтому пакет `xvfb` обязателен и для desktop-, и для headless-установок:
169
+ Если `DISPLAY` не задан (headless service, SSH без X11 forwarding и т. п.), Answer42 поднимает два собственных изолированных Xvfb-дисплея: один для test manager и второй для test client. В этом режиме пакет `xvfb` обязателен:
170
170
 
171
171
  ```bash
172
172
  # Debian / Ubuntu
@@ -176,7 +176,7 @@ sudo apt install xvfb
176
176
  sudo dnf install xorg-x11-server-Xvfb
177
177
  ```
178
178
 
179
- Если Xvfb отсутствует, `start_session` завершается до создания ресурсов с понятной подсказкой по установке.
179
+ Если не задан ни доступный `DISPLAY`, ни Xvfb, `start_session` завершается до создания ресурсов с понятной подсказкой по установке. Чтобы desktop-сервис видел живой X11, его launcher/service должен передать корректный `DISPLAY` и права доступа к X server (обычно через `XAUTHORITY`).
180
180
 
181
181
  ## Скриншоты: stdio и StreamableHTTP
182
182
 
@@ -130,9 +130,9 @@ answer42 install-skills --dry-run --agent all
130
130
 
131
131
  ## Linux display prerequisite
132
132
 
133
- На Linux `start_session` **всегда** запускает два изолированных Xvfb-дисплея: один для test manager и второй для test client. Это намеренно не использует существующий `DISPLAY`, даже если Answer42 запущен в живой X11-сессии: автоматизация и её скриншоты не должны захватывать пользовательский рабочий стол.
133
+ На Linux `start_session` использует живую X11-сессию, если процесс Answer42 унаследовал непустой `DISPLAY`: test manager и test client запускаются на этом дисплее, поэтому их окна и скриншоты видны на desktop.
134
134
 
135
- Поэтому пакет `xvfb` обязателен и для desktop-, и для headless-установок:
135
+ Если `DISPLAY` не задан (headless service, SSH без X11 forwarding и т. п.), Answer42 поднимает два собственных изолированных Xvfb-дисплея: один для test manager и второй для test client. В этом режиме пакет `xvfb` обязателен:
136
136
 
137
137
  ```bash
138
138
  # Debian / Ubuntu
@@ -142,7 +142,7 @@ sudo apt install xvfb
142
142
  sudo dnf install xorg-x11-server-Xvfb
143
143
  ```
144
144
 
145
- Если Xvfb отсутствует, `start_session` завершается до создания ресурсов с понятной подсказкой по установке.
145
+ Если не задан ни доступный `DISPLAY`, ни Xvfb, `start_session` завершается до создания ресурсов с понятной подсказкой по установке. Чтобы desktop-сервис видел живой X11, его launcher/service должен передать корректный `DISPLAY` и права доступа к X server (обычно через `XAUTHORITY`).
146
146
 
147
147
  ## Скриншоты: stdio и StreamableHTTP
148
148
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "answer42"
3
- version = "0.4.98"
3
+ version = "0.4.99"
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"
@@ -118,26 +118,53 @@ def find_free_port(start: int = DEFAULT_WS_PORT, end: int | None = None) -> int:
118
118
  raise RuntimeError(f"No free port found in range {start}..{end}")
119
119
 
120
120
 
121
+ def inherited_x11_display() -> str:
122
+ """Return the caller's live X11 display, if one was intentionally inherited."""
123
+ if os_support.IS_WINDOWS:
124
+ return ""
125
+ return os.getenv("DISPLAY", "").strip()
126
+
127
+
121
128
  def require_xvfb() -> str:
122
- """Return the Xvfb executable or raise an actionable Linux prerequisite error.
129
+ """Return the Xvfb executable or raise an actionable headless prerequisite error.
123
130
 
124
- Answer42 deliberately runs the test manager and test client on two isolated
125
- X displays. It must therefore have Xvfb even when the MCP process already
126
- has access to a live desktop ``DISPLAY``.
131
+ Xvfb is needed only when Answer42 did not inherit a live X11 ``DISPLAY``.
127
132
  """
128
133
  if os_support.IS_WINDOWS:
129
134
  raise RuntimeError("Xvfb is not available on Windows")
130
135
  xvfb_bin = shutil.which("Xvfb") or "/usr/bin/Xvfb"
131
136
  if not Path(xvfb_bin).is_file():
132
137
  raise RuntimeError(
133
- "Xvfb is required to start an Answer42 session on Linux, including on a host with an active X11 desktop. "
134
- "Answer42 starts isolated displays for the test manager and test client. "
138
+ "Xvfb is required to start an Answer42 session on Linux when DISPLAY is not set. "
139
+ "Set DISPLAY to an accessible live X11 session, or install Xvfb for headless operation. "
135
140
  "Install the 'xvfb' package (Debian/Ubuntu: sudo apt install xvfb; Fedora/RHEL: sudo dnf install xorg-x11-server-Xvfb) "
136
141
  "and retry start_session."
137
142
  )
138
143
  return xvfb_bin
139
144
 
140
145
 
146
+ def session_displays(display_offset: int) -> tuple[str, str, bool]:
147
+ """Choose inherited desktop display or two Answer42-owned Xvfb displays.
148
+
149
+ Returns ``(manager_display, client_display, starts_xvfb)``. A live X11
150
+ display is deliberately shared by manager and client: it is the caller's
151
+ explicit request to run visibly on that desktop. Otherwise the historical
152
+ isolated two-Xvfb setup is retained for headless operation.
153
+ """
154
+ desktop_display = inherited_x11_display()
155
+ if desktop_display:
156
+ return desktop_display, desktop_display, False
157
+ if os_support.IS_WINDOWS:
158
+ return "", "", False
159
+ require_xvfb()
160
+ manager_display = find_free_display(DEFAULT_DISPLAY_START + display_offset)
161
+ # The first X socket does not exist until start_xvfb() is called, so derive
162
+ # the second search position from the selected number instead of asking the
163
+ # same free-display scan twice.
164
+ client_display = find_free_display(int(manager_display.removeprefix(":")) + 1)
165
+ return manager_display, client_display, True
166
+
167
+
141
168
  def start_xvfb(display: str, resolution: str = DEFAULT_DISPLAY_RES) -> subprocess.Popen:
142
169
  """Start Xvfb for the given display and return the Popen handle."""
143
170
  xvfb_bin = require_xvfb()
@@ -5215,18 +5215,18 @@ async def start_session(
5215
5215
  if sid in _SESSIONS:
5216
5216
  raise ValueError(f"Session {sid!r} already exists.")
5217
5217
 
5218
- # A desktop DISPLAY is intentionally not reused: two isolated displays
5219
- # prevent an interactive user desktop from becoming part of automation or
5220
- # screenshots. Check the prerequisite before allocating any session state
5221
- # so a missing package produces a clear error rather than FileNotFoundError.
5222
- if not os_support.IS_WINDOWS:
5218
+ # A caller that inherited DISPLAY explicitly requests visible automation
5219
+ # on that live X11 session. In a headless process, retain two private Xvfb
5220
+ # displays and validate the prerequisite before allocating session state.
5221
+ inherited_display = runtime.inherited_x11_display()
5222
+ if not os_support.IS_WINDOWS and not inherited_display:
5223
5223
  runtime.require_xvfb()
5224
5224
 
5225
5225
  data_dir = os_support.default_data_dir() / "sessions" / sid
5226
5226
  with _port_allocation_lock():
5227
5227
  port_offset = _stable_session_port_offset(sid)
5228
5228
  display_offset = port_offset // _PORT_BAND_SIZE
5229
- display = "" if os_support.IS_WINDOWS else runtime.find_free_display(runtime.DEFAULT_DISPLAY_START + display_offset)
5229
+ display, client_display, starts_xvfb = runtime.session_displays(display_offset)
5230
5230
  ws_port = _find_session_port(runtime.DEFAULT_WS_PORT, port_offset, sub_offset=0)
5231
5231
  ibsrv_port = _find_session_port(runtime.DEFAULT_IBSRV_HTTP_PORT, port_offset, sub_offset=0)
5232
5232
  ibsrv_direct_regport = _find_session_port(runtime.DEFAULT_IBSRV_DIRECT_PORT, port_offset, sub_offset=0)
@@ -5256,13 +5256,10 @@ async def start_session(
5256
5256
  bound_snapshot = _snapshot_for_base_url(base_url)
5257
5257
  if bound_snapshot:
5258
5258
  sess.rag_snapshot = bound_snapshot
5259
- if display:
5259
+ sess.client_display = client_display
5260
+ if starts_xvfb:
5260
5261
  sess.xvfb_process = runtime.start_xvfb(display)
5261
5262
  sess.xvfb_pid = sess.xvfb_process.pid
5262
- # Separate display for the test client so screenshots capture only the client window
5263
- client_display = "" if os_support.IS_WINDOWS else runtime.find_free_display(runtime.DEFAULT_DISPLAY_START + display_offset + 1)
5264
- sess.client_display = client_display
5265
- if client_display:
5266
5263
  sess.client_xvfb_process = runtime.start_xvfb(client_display)
5267
5264
  sess.client_xvfb_pid = sess.client_xvfb_process.pid
5268
5265
  await sess.bridge.start()
File without changes
File without changes
File without changes
File without changes
File without changes