elemctl 0.43.0__tar.gz → 0.43.2__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 (71) hide show
  1. {elemctl-0.43.0 → elemctl-0.43.2}/PKG-INFO +2 -2
  2. {elemctl-0.43.0 → elemctl-0.43.2}/README.md +1 -1
  3. {elemctl-0.43.0 → elemctl-0.43.2}/docs/SPEC.md +2 -2
  4. {elemctl-0.43.0 → elemctl-0.43.2}/docs/SPEC.ru.md +3 -2
  5. {elemctl-0.43.0 → elemctl-0.43.2}/docs/changelog.md +16 -0
  6. {elemctl-0.43.0 → elemctl-0.43.2}/docs/changelog.ru.md +17 -0
  7. {elemctl-0.43.0 → elemctl-0.43.2}/docs/config.md +1 -1
  8. {elemctl-0.43.0 → elemctl-0.43.2}/docs/config.ru.md +1 -1
  9. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/__init__.py +2 -2
  10. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/build.py +14 -14
  11. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/cli.py +29 -29
  12. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/client.py +27 -27
  13. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/config.py +8 -6
  14. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/deploy.py +12 -12
  15. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/errors.py +1 -1
  16. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/i18n.py +12 -8
  17. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/mcp_server.py +82 -82
  18. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/plugins.py +18 -18
  19. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/probe.py +14 -14
  20. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/selfupdate.py +4 -3
  21. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/transport.py +9 -9
  22. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/versions.py +1 -1
  23. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl.egg-info/PKG-INFO +2 -2
  24. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_build.py +8 -8
  25. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_cli.py +43 -28
  26. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_cli_docs.py +3 -3
  27. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_client.py +10 -10
  28. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_config.py +18 -3
  29. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_deploy.py +2 -2
  30. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_i18n.py +6 -6
  31. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_mcp.py +37 -37
  32. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_plugins.py +4 -4
  33. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_probe.py +4 -4
  34. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_selfupdate.py +51 -7
  35. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_transport.py +4 -4
  36. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_user_lists.py +8 -8
  37. {elemctl-0.43.0 → elemctl-0.43.2}/tools/extract_adapter.py +2 -2
  38. {elemctl-0.43.0 → elemctl-0.43.2}/.env.example +0 -0
  39. {elemctl-0.43.0 → elemctl-0.43.2}/LICENSE +0 -0
  40. {elemctl-0.43.0 → elemctl-0.43.2}/MANIFEST.in +0 -0
  41. {elemctl-0.43.0 → elemctl-0.43.2}/NOTICE +0 -0
  42. {elemctl-0.43.0 → elemctl-0.43.2}/ORIGIN.md +0 -0
  43. {elemctl-0.43.0 → elemctl-0.43.2}/docs/cli.md +0 -0
  44. {elemctl-0.43.0 → elemctl-0.43.2}/docs/cli.ru.md +0 -0
  45. {elemctl-0.43.0 → elemctl-0.43.2}/docs/index.md +0 -0
  46. {elemctl-0.43.0 → elemctl-0.43.2}/docs/index.ru.md +0 -0
  47. {elemctl-0.43.0 → elemctl-0.43.2}/docs/library.md +0 -0
  48. {elemctl-0.43.0 → elemctl-0.43.2}/docs/library.ru.md +0 -0
  49. {elemctl-0.43.0 → elemctl-0.43.2}/docs/mcp.md +0 -0
  50. {elemctl-0.43.0 → elemctl-0.43.2}/docs/mcp.ru.md +0 -0
  51. {elemctl-0.43.0 → elemctl-0.43.2}/docs/platform.md +0 -0
  52. {elemctl-0.43.0 → elemctl-0.43.2}/docs/platform.ru.md +0 -0
  53. {elemctl-0.43.0 → elemctl-0.43.2}/pyproject.toml +0 -0
  54. {elemctl-0.43.0 → elemctl-0.43.2}/setup.cfg +0 -0
  55. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/__main__.py +0 -0
  56. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/auth.py +0 -0
  57. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl/schema.py +0 -0
  58. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl.egg-info/SOURCES.txt +0 -0
  59. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl.egg-info/dependency_links.txt +0 -0
  60. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl.egg-info/entry_points.txt +0 -0
  61. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl.egg-info/requires.txt +0 -0
  62. {elemctl-0.43.0 → elemctl-0.43.2}/src/elemctl.egg-info/top_level.txt +0 -0
  63. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_changelog_link.py +0 -0
  64. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_conventions.py +0 -0
  65. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_docs_coverage.py +0 -0
  66. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_extract_adapter.py +0 -0
  67. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_rebuild_docs.py +0 -0
  68. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_release_notes.py +0 -0
  69. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_schema.py +0 -0
  70. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_versions.py +0 -0
  71. {elemctl-0.43.0 → elemctl-0.43.2}/tests/test_workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elemctl
3
- Version: 0.43.0
3
+ Version: 0.43.2
4
4
  Summary: CLI, MCP server and library for the Console API v2 of the 1C:Enterprise.Element platform (1cmycloud): applications, builds from source, one-command deploy with verification, a compile probe
5
5
  Author: KeyFire
6
6
  License-Expression: MIT
@@ -98,7 +98,7 @@ elemctl takes the connection credentials from environment variables or from a `.
98
98
 
99
99
  | Variable | Purpose |
100
100
  |---|---|
101
- | `ELEMENT_BASE_URL` | the platform base URL, e.g. `https://1cmycloud.com` |
101
+ | `ELEMENT_BASE_URL` | the platform base URL starting with `http://` or `https://`, e.g. `https://1cmycloud.com` |
102
102
  | `ELEMENT_CLIENT_ID` | Client-Id used to obtain a token |
103
103
  | `ELEMENT_CLIENT_SECRET` | Client-Secret |
104
104
  | `ELEMENT_APP_ID` | default application (optional) |
@@ -66,7 +66,7 @@ elemctl takes the connection credentials from environment variables or from a `.
66
66
 
67
67
  | Variable | Purpose |
68
68
  |---|---|
69
- | `ELEMENT_BASE_URL` | the platform base URL, e.g. `https://1cmycloud.com` |
69
+ | `ELEMENT_BASE_URL` | the platform base URL starting with `http://` or `https://`, e.g. `https://1cmycloud.com` |
70
70
  | `ELEMENT_CLIENT_ID` | Client-Id used to obtain a token |
71
71
  | `ELEMENT_CLIENT_SECRET` | Client-Secret |
72
72
  | `ELEMENT_APP_ID` | default application (optional) |
@@ -26,7 +26,7 @@ Environment variables:
26
26
 
27
27
  | Variable | Meaning | Required |
28
28
  |---|---|---|
29
- | `ELEMENT_BASE_URL` | platform base URL, e.g. `https://1cmycloud.com` | yes |
29
+ | `ELEMENT_BASE_URL` | platform base URL starting with `http://` or `https://`, e.g. `https://1cmycloud.com` | yes |
30
30
  | `ELEMENT_CLIENT_ID` | Client-Id for obtaining the token | yes |
31
31
  | `ELEMENT_CLIENT_SECRET` | Client-Secret | yes |
32
32
  | `ELEMENT_APP_ID` | default application | no |
@@ -36,7 +36,7 @@ Environment variables:
36
36
  | `ELEMENT_TLS_STRICT` | strict RFC 5280 certificate checks; `true` by default | no |
37
37
  | `ELEMENT_TLS_VERIFY` | certificate and hostname verification; `true` by default | no |
38
38
 
39
- .env format: `KEY=VALUE` lines. Empty lines and lines starting with `#` are skipped. A leading `export ` prefix is allowed, and the value may be wrapped in single or double quotes. The encoding is UTF-8 and a BOM is possible, so read the file as `utf-8-sig`. A trailing slash in `ELEMENT_BASE_URL` is trimmed.
39
+ .env format: `KEY=VALUE` lines. Empty lines and lines starting with `#` are skipped. A leading `export ` prefix is allowed, and the value may be wrapped in single or double quotes. The encoding is UTF-8 and a BOM is possible, so read the file as `utf-8-sig`. A trailing slash in `ELEMENT_BASE_URL` is trimmed. A nonempty base URL must start with `http://` or `https://`.
40
40
 
41
41
  ## 3. Authentication
42
42
 
@@ -37,7 +37,7 @@ README.md, .env.example и .gitignore заданы и не меняются.
37
37
 
38
38
  | Переменная | Смысл | Обязательна |
39
39
  |---|---|---|
40
- | `ELEMENT_BASE_URL` | базовый URL платформы, например `https://1cmycloud.com` | да |
40
+ | `ELEMENT_BASE_URL` | базовый URL платформы с `http://` или `https://`, например `https://1cmycloud.com` | да |
41
41
  | `ELEMENT_CLIENT_ID` | Client-Id для получения токена | да |
42
42
  | `ELEMENT_CLIENT_SECRET` | Client-Secret | да |
43
43
  | `ELEMENT_APP_ID` | приложение по умолчанию | нет |
@@ -50,7 +50,8 @@ README.md, .env.example и .gitignore заданы и не меняются.
50
50
  Формат .env: строки `KEY=VALUE`. Пустые строки и строки, начинающиеся с `#`,
51
51
  пропускаются. Перед именем допускается префикс `export `, а значение можно взять
52
52
  в одинарные или двойные кавычки. Кодировка UTF-8, возможен BOM, поэтому читать
53
- надо как `utf-8-sig`. Хвостовой слэш у `ELEMENT_BASE_URL` обрезается.
53
+ надо как `utf-8-sig`. Хвостовой слэш у `ELEMENT_BASE_URL` обрезается. Непустой базовый URL
54
+ должен начинаться с `http://` или `https://`.
54
55
 
55
56
  ## 3. Аутентификация
56
57
 
@@ -21,6 +21,22 @@ entry either – say what the behaviour was, not which id or response field was
21
21
  The link is written by `python scripts/changelog-link.py <number>`, which rebuilds the generated
22
22
  pages of the site in the same run – writing it by hand is how the mirrors get left behind.
23
23
 
24
+ ## 2026-09-23 – 0.43.2
25
+
26
+ ### Changed
27
+ - Python comments and docstrings consistently use hyphens. Ordinary string literals remain unchanged.
28
+ ([#40](https://github.com/keyfire/elemctl/pull/40))
29
+
30
+ ## 2026-09-22 – 0.43.1
31
+
32
+ ### Fixed
33
+ - **`self-update` reports an incomplete PyPI response without touching the installed package.**
34
+ The simple index, metadata and wheel downloads missed HTTP parser failures, so an interrupted
35
+ response could end in a traceback. ([#38](https://github.com/keyfire/elemctl/pull/38))
36
+ - **Connection setup rejects a base URL without an HTTP scheme.** An address without `http://`
37
+ or `https://` used to reach `urllib` and fail with a raw `ValueError`.
38
+ ([#38](https://github.com/keyfire/elemctl/pull/38))
39
+
24
40
  ## 2026-09-19 – 0.43.0
25
41
 
26
42
  ### Added
@@ -21,6 +21,23 @@ sidebar:
21
21
  Ссылку пишет `python scripts/changelog-link.py <номер>` – он же тут же пересобирает
22
22
  сгенерированные страницы сайта: правка руками как раз и оставляет зеркала позади.
23
23
 
24
+ ## 23.09.2026 – 0.43.2
25
+
26
+ ### Изменено
27
+ - В комментариях и документирующих строках Python используется обычный дефис. Остальные строковые литералы сохранены.
28
+ ([#40](https://github.com/keyfire/elemctl/pull/40))
29
+
30
+ ## 22.09.2026 – 0.43.1
31
+
32
+ ### Исправлено
33
+ - **`self-update` сообщает об оборванном ответе PyPI и не затрагивает установленный пакет.**
34
+ Чтение индекса Simple, метаданных и колеса не ловило ошибки синтаксического анализатора HTTP,
35
+ поэтому оборванный ответ мог закончиться трассировкой.
36
+ ([#38](https://github.com/keyfire/elemctl/pull/38))
37
+ - **Настройка подключения отказывает базовому URL без схемы HTTP.** Адрес без `http://` или
38
+ `https://` доходил до `urllib` и заканчивался необработанным `ValueError`.
39
+ ([#38](https://github.com/keyfire/elemctl/pull/38))
40
+
24
41
  ## 19.09.2026 – 0.43.0
25
42
 
26
43
  ### Добавлено
@@ -13,7 +13,7 @@ elemctl takes the connection credentials from environment variables or from a `.
13
13
 
14
14
  | Variable | Purpose |
15
15
  |---|---|
16
- | `ELEMENT_BASE_URL` | the platform base URL, e.g. `https://1cmycloud.com` |
16
+ | `ELEMENT_BASE_URL` | the platform base URL starting with `http://` or `https://`, e.g. `https://1cmycloud.com` |
17
17
  | `ELEMENT_CLIENT_ID` | Client-Id used to obtain a token |
18
18
  | `ELEMENT_CLIENT_SECRET` | Client-Secret |
19
19
  | `ELEMENT_APP_ID` | default application (optional) |
@@ -14,7 +14,7 @@ sidebar:
14
14
 
15
15
  | Переменная | Назначение |
16
16
  |---|---|
17
- | `ELEMENT_BASE_URL` | базовый URL платформы, например `https://1cmycloud.com` |
17
+ | `ELEMENT_BASE_URL` | базовый URL платформы с `http://` или `https://`, например `https://1cmycloud.com` |
18
18
  | `ELEMENT_CLIENT_ID` | Client-Id для получения токена |
19
19
  | `ELEMENT_CLIENT_SECRET` | Client-Secret |
20
20
  | `ELEMENT_APP_ID` | приложение по умолчанию (необязательно) |
@@ -1,4 +1,4 @@
1
- """elemctl – a CLI, an MCP server and a library for Console API v2 of the
1
+ """elemctl - a CLI, an MCP server and a library for Console API v2 of the
2
2
  1C:Enterprise.Element platform (1cmycloud).
3
3
 
4
4
  Public API of the library:
@@ -18,7 +18,7 @@ from .errors import (
18
18
  TransportError,
19
19
  )
20
20
 
21
- __version__ = "0.43.0"
21
+ __version__ = "0.43.2"
22
22
 
23
23
  __all__ = [
24
24
  "ApiError",
@@ -60,7 +60,7 @@ SOAP_CLIENT_KINDS = ("КлиентSoapСервиса", "SoapServiceClient")
60
60
  # by extension.
61
61
  RESOURCES_DIR = "Ресурсы"
62
62
 
63
- # Directories excluded entirely (plus every hidden one – starting with a dot).
63
+ # Directories excluded entirely (plus every hidden one - starting with a dot).
64
64
  EXCLUDED_DIRS = {".git", ".claude", ".github", "__pycache__", "node_modules", ".venv"}
65
65
 
66
66
  # Files excluded by exact name.
@@ -69,7 +69,7 @@ EXCLUDED_FILES = {".gitignore", ".env", ".DS_Store"}
69
69
  # Files excluded by extension (prebuilt build archives).
70
70
  EXCLUDED_SUFFIXES = (".xasm", ".xlib")
71
71
 
72
- # The CI environment variables carrying the run number – the source of the build
72
+ # The CI environment variables carrying the run number - the source of the build
73
73
  # version suffix when neither an explicit version nor a last build is given: every
74
74
  # CI run happens in a clean working directory, so local numbering would always
75
75
  # yield "-1".
@@ -105,9 +105,9 @@ class LibraryRef:
105
105
  class BuildResult:
106
106
  """The result of a local archive build.
107
107
 
108
- version_source – where the build version came from: "flag" (set explicitly),
108
+ version_source - where the build version came from: "flag" (set explicitly),
109
109
  "last-build" (auto-increment from the last build), the name of a CI variable
110
- (the run number from the environment) or "default". dirty_files – the files
110
+ (the run number from the environment) or "default". dirty_files - the files
111
111
  with uncommitted changes in the project directory; None when git is
112
112
  unavailable or the directory is not inside a repository.
113
113
  """
@@ -132,7 +132,7 @@ class BuildResult:
132
132
  def parse_flat_yaml(text):
133
133
  """Parse the flat top-level "key: value" pairs of a YAML text.
134
134
 
135
- Nested lines (the indented ones), blank lines and comments are skipped –
135
+ Nested lines (the indented ones), blank lines and comments are skipped -
136
136
  for Проект.yaml that is enough.
137
137
  """
138
138
  values = {}
@@ -334,7 +334,7 @@ def collect_project_files(project_dir):
334
334
 
335
335
  Outside the resource directories an allowlist of extensions applies; inside a
336
336
  `Ресурсы` directory (at any level, including its subdirectories) files of any
337
- extension are taken – a resource may be an arbitrary file: .pdf, .htm, .mxl etc.
337
+ extension are taken - a resource may be an arbitrary file: .pdf, .htm, .mxl etc.
338
338
  """
339
339
  return collect_with_skipped(project_dir)[0]
340
340
 
@@ -432,7 +432,7 @@ def _detached_branch(environ=None):
432
432
  def git_metadata(project_dir, environ=None):
433
433
  """The commit hash and the branch name of the git repository holding the project.
434
434
 
435
- When git is unavailable (no command, not a repository) – empty strings.
435
+ When git is unavailable (no command, not a repository) - empty strings.
436
436
 
437
437
  A detached checkout is answered by the CI, not by git: `rev-parse --abbrev-ref HEAD`
438
438
  says the literal `HEAD` there, and that is what every assembly built on a runner used
@@ -452,7 +452,7 @@ def git_dirty_files(project_dir):
452
452
 
453
453
  A build captures the disk as it is at the moment it starts, so a divergence
454
454
  from HEAD has to be visible to the caller. Returned is the list of paths from
455
- git status --porcelain limited to the directory; None – when git is unavailable
455
+ git status --porcelain limited to the directory; None - when git is unavailable
456
456
  or the directory is not inside a repository (as opposed to an empty list,
457
457
  meaning "clean").
458
458
  """
@@ -510,8 +510,8 @@ def build_assembly(
510
510
 
511
511
  The version: the explicit version, otherwise an auto-increment from
512
512
  last_build_version, otherwise a suffix taken from the CI run number (the
513
- CI_BUILD_NUMBER_VARS variables), and with none of that – "{base version}-1".
514
- branch and commit override the git metadata (None – take it from git). kind
513
+ CI_BUILD_NUMBER_VARS variables), and with none of that - "{base version}-1".
514
+ branch and commit override the git metadata (None - take it from git). kind
515
515
  overrides the project kind ("application"/"library").
516
516
  """
517
517
  directory = find_project_dir(project_dir) if project_dir else find_project_dir()
@@ -643,10 +643,10 @@ def read_assembly_project(path):
643
643
 
644
644
 
645
645
  def inspect_assembly(path):
646
- """Inspect a prebuilt assembly archive (.xasm/.xlib) – the inverse of build_assembly.
646
+ """Inspect a prebuilt assembly archive (.xasm/.xlib) - the inverse of build_assembly.
647
647
 
648
648
  Returns the manifest, the project properties, its subsystems and the types
649
- available to the project that plugged it in (ОбластьВидимости: Глобально) –
649
+ available to the project that plugged it in (ОбластьВидимости: Глобально) -
650
650
  with their qualified names. The namespace of a type is
651
651
  {vendor}::{name}::{subsystem}[::{package}], where a package is a nested
652
652
  directory of a subsystem (a package has no descriptor file of its own).
@@ -686,7 +686,7 @@ def inspect_assembly(path):
686
686
  "vendor": vendor,
687
687
  "name": name,
688
688
  "version": manifest.get("Version", ""),
689
- # There is no ВерсияТехнологии in Проект.yaml – compatibility is set by
689
+ # There is no ВерсияТехнологии in Проект.yaml - compatibility is set by
690
690
  # РежимСовместимости, and it is exactly what is matched against the target project.
691
691
  "compatibility": descriptor_value(project, "РежимСовместимости", "CompatibilityMode"),
692
692
  "representation": descriptor_value(project, "Представление", "Presentation"),
@@ -759,7 +759,7 @@ def _archive_elements(archive, names, prefix):
759
759
 
760
760
 
761
761
  def _subsystems(elements, vendor, name):
762
- """The project's subsystems and their packages – from the directory layout.
762
+ """The project's subsystems and their packages - from the directory layout.
763
763
 
764
764
  A subsystem is a first-level directory; Подсистема.yaml is optional (a library
765
765
  may have none at all), so it cannot be relied upon.
@@ -63,7 +63,7 @@ _answer_stream = None
63
63
  def _emit(data):
64
64
  """Print the answer and flush it immediately.
65
65
 
66
- Off a terminal Python block-buffers stdout, while stderr goes through right away – a
66
+ Off a terminal Python block-buffers stdout, while stderr goes through right away - a
67
67
  caller who merges the streams (`2>&1`, or stderr=STDOUT, a plain way to capture "everything
68
68
  the tool printed" for a log) then saw a later _progress line overtake an _emit that ran
69
69
  first in the source, and a parser hunting for the first brace found a summary line instead.
@@ -80,7 +80,7 @@ def _json_only():
80
80
  """--json: only the answer reaches stdout, everything else goes to stderr.
81
81
 
82
82
  The promise is kept by the streams themselves rather than by discipline: a
83
- print of a plugin command, a warning of a library, a stray debug line – all of
83
+ print of a plugin command, a warning of a library, a stray debug line - all of
84
84
  them land on stderr, because for the duration of the call sys.stdout IS stderr.
85
85
  _emit keeps writing to the real stdout, which is remembered here.
86
86
  """
@@ -95,7 +95,7 @@ def _json_only():
95
95
 
96
96
  def _progress(message):
97
97
  # stderr is already line-buffered on every supported Python, so this changes nothing in
98
- # practice – it is here so the order _emit relies on is an explicit guarantee in the code,
98
+ # practice - it is here so the order _emit relies on is an explicit guarantee in the code,
99
99
  # not an assumption about the interpreter's default that the next reader has to go verify.
100
100
  print(message, file=sys.stderr)
101
101
  sys.stderr.flush()
@@ -108,7 +108,7 @@ def _fail(payload):
108
108
 
109
109
 
110
110
  def _reconfigure_streams():
111
- """Switch the console output to UTF-8 – otherwise Cyrillic breaks on Windows."""
111
+ """Switch the console output to UTF-8 - otherwise Cyrillic breaks on Windows."""
112
112
  for stream in (sys.stdout, sys.stderr):
113
113
  reconfigure = getattr(stream, "reconfigure", None)
114
114
  if reconfigure is not None:
@@ -169,14 +169,14 @@ def _refuse_connection_options(args, command):
169
169
 
170
170
  `build` and `inspect` never reach the platform: the archive is assembled from
171
171
  the sources on disk, and no version number is reserved on the server. The
172
- options used to be accepted and quietly dropped – and a call that carries
172
+ options used to be accepted and quietly dropped - and a call that carries
173
173
  `--env-file` LOOKS like a build bound to a stand, which twice left the reader
174
174
  asking whether the build talks to the server after all.
175
175
 
176
176
  A refusal rather than a warning, for three reasons. Nothing stops working:
177
177
  the options changed nothing, so the only calls it breaks are the ones that
178
178
  already meant something else. A warning would land on stderr, next to the
179
- progress lines, where a CI log buries it – and silence of exactly that kind is
179
+ progress lines, where a CI log buries it - and silence of exactly that kind is
180
180
  what caused the doubt. And the refusal answers the question the caller really
181
181
  had: it says outright that a local build does not go to the platform, and
182
182
  names the commands that do.
@@ -262,7 +262,7 @@ def cmd_apps_find(args):
262
262
 
263
263
  The exit code is 0 in both cases: the found field carries the verdict. A non-zero exit
264
264
  code means the request itself failed (no access, network, configuration) and comes with
265
- JSON carrying an error field on stderr – otherwise the caller cannot tell "there is no
265
+ JSON carrying an error field on stderr - otherwise the caller cannot tell "there is no
266
266
  stand" from "we failed to ask".
267
267
 
268
268
  Deleted applications (status Deleted) are skipped by default, so that the id found is fit
@@ -401,7 +401,7 @@ def _report_sign_in(card):
401
401
  A stand nobody can get into is not a ready stand, and the way in is not
402
402
  guessable: the accounts used elsewhere do not work in a fresh application
403
403
  (client.sign_in_hint). The text goes to stderr, where the rest
404
- of the progress goes, and the machine-readable twin goes into the JSON – an
404
+ of the progress goes, and the machine-readable twin goes into the JSON - an
405
405
  agent reading only stdout must learn it too.
406
406
  """
407
407
  hint = sign_in_hint(card)
@@ -500,8 +500,8 @@ def cmd_apps_ensure(args):
500
500
  """Idempotently bring an application with the given name into existence.
501
501
 
502
502
  It looks the application up by the rules of apps find (deleted ones, in
503
- status Deleted, do not count): if it is already there – it does nothing and
504
- returns created: false; if it is not – it creates one from the creation flags
503
+ status Deleted, do not count): if it is already there - it does nothing and
504
+ returns created: false; if it is not - it creates one from the creation flags
505
505
  and returns created: true. An existing application is never re-created:
506
506
  delete + create give a new URL and break the external links to the previous
507
507
  one. The exit code is 0 in both cases; a failed request is JSON with an error
@@ -690,9 +690,9 @@ def cmd_builds_list(args):
690
690
 
691
691
  Neither cut is silent, and there are two of them. The tool's own is the limit.
692
692
  The platform's is its housekeeping: it deletes the builds nobody uses, whatever
693
- their age, so a listing is not the project's history – it is what survived. The
693
+ their age, so a listing is not the project's history - it is what survived. The
694
694
  count line says which of the two the reader is looking at (builds_summary reads
695
- that off the gaps in the build numbering), and it is printed always – after the
695
+ that off the gaps in the build numbering), and it is printed always - after the
696
696
  answer, the same order apps list uses: a listing without it was read as "the
697
697
  project has exactly these builds", and a caller capturing the two streams as one
698
698
  must still find the answer first.
@@ -707,7 +707,7 @@ def cmd_builds_list(args):
707
707
  truncated = args.limit > 0 and len(assemblies) > args.limit
708
708
  if truncated:
709
709
  shown = assemblies[: args.limit]
710
- # Counted before --brief reshapes the cards: the whole answer, not the cut one –
710
+ # Counted before --brief reshapes the cards: the whole answer, not the cut one -
711
711
  # what the platform left out is judged by the numbering of everything it did
712
712
  # return, and the count of a card does not change when its fields do.
713
713
  shown_count = len(shown)
@@ -733,8 +733,8 @@ def cmd_builds_get(args):
733
733
  def _upload_target(args, config):
734
734
  """The target project of the upload and the source of that choice.
735
735
 
736
- The sources: "flag" – the --project-id flag, "env" – ELEMENT_PROJECT_ID from
737
- the environment or the .env file, None – no project is set, the platform will
736
+ The sources: "flag" - the --project-id flag, "env" - ELEMENT_PROJECT_ID from
737
+ the environment or the .env file, None - no project is set, the platform will
738
738
  create a new one. The --new-project flag switches the binding from the
739
739
  environment off; together with --project-id it is contradictory, which is a
740
740
  call error.
@@ -950,7 +950,7 @@ def _user_list_id(client, args):
950
950
  """The id of the target user list: from the argument or from the application.
951
951
 
952
952
  Exactly one of the two is expected. --app takes the application's own list
953
- (default-user-list of its card) – that is the everyday case: the settings of
953
+ (default-user-list of its card) - that is the everyday case: the settings of
954
954
  the checklist are made on the list of a particular application, and its
955
955
  presentation is named after the application anyway.
956
956
  """
@@ -1026,8 +1026,8 @@ def _calculation_rules(args):
1026
1026
  def cmd_user_lists_calculation_rules(args):
1027
1027
  """The rules that build a user from the provider's answer: show the target or write them.
1028
1028
 
1029
- Without values the command does not pretend to show the rules – the platform does not
1030
- return them – it names the service they would go to and says why there is nothing to
1029
+ Without values the command does not pretend to show the rules - the platform does not
1030
+ return them - it names the service they would go to and says why there is nothing to
1031
1031
  show. Writing reports honestly what was confirmed and what could not be.
1032
1032
  """
1033
1033
  client = make_client(_config(args))
@@ -1061,7 +1061,7 @@ def cmd_user_lists_calculation_rules(args):
1061
1061
 
1062
1062
 
1063
1063
  def cmd_user_lists_password_login(args):
1064
- """Signing in with a login and a password – the account service of type Local.
1064
+ """Signing in with a login and a password - the account service of type Local.
1065
1065
 
1066
1066
  Without a flag the command answers with the current state; the answer says
1067
1067
  `enabled: null` when the list has no such service at all (nothing to sign in
@@ -1209,7 +1209,7 @@ def cmd_tech_set(args):
1209
1209
  def cmd_debug_adapter(args):
1210
1210
  """The path to the platform debug adapter directory brought by the plugin.
1211
1211
 
1212
- The directory holds a repo/ subdirectory with the jar files of the adapter – that is
1212
+ The directory holds a repo/ subdirectory with the jar files of the adapter - that is
1213
1213
  the ready value of the xbsl.debug.adapterPath setting for the VS Code extension. A
1214
1214
  missing plugin is an answer (found: false, code 0), not an error: a non-zero code
1215
1215
  would mean a failure.
@@ -1223,10 +1223,10 @@ def cmd_debug_adapter(args):
1223
1223
 
1224
1224
 
1225
1225
  def cmd_plugins(args):
1226
- """Plugin diagnostics: what the plugins bring – adapter directories and commands.
1226
+ """Plugin diagnostics: what the plugins bring - adapter directories and commands.
1227
1227
 
1228
1228
  The adapter directories are listed jar-less ones included (that is exactly
1229
- what a diagnostic is for), the commands – with the entry point they arrived
1229
+ what a diagnostic is for), the commands - with the entry point they arrived
1230
1230
  through and whether they are exposed to MCP. The failures name the plugins
1231
1231
  left out and the reason: a plugin that did not load, or a command that would
1232
1232
  have taken over a name of the core.
@@ -1271,15 +1271,15 @@ def cmd_mcp(args):
1271
1271
  from . import mcp_server
1272
1272
  except ImportError:
1273
1273
  raise ElemctlError(i18n.t("cli.mcp-extra-required"))
1274
- # A bad --env-file still fails right here, before the server starts – the same check
1274
+ # A bad --env-file still fails right here, before the server starts - the same check
1275
1275
  # Config.from_env would raise on the first call, just not deferred to it: a typo in a
1276
1276
  # path given explicitly on this command line is worth knowing about immediately, not
1277
1277
  # minutes into a session. Other default-stand configuration problems (a bad
1278
1278
  # ELEMENT_TLS_VERIFY, credentials missing entirely) are NOT resolved here on purpose: not
1279
1279
  # _config(args), which used to build a whole Config up front and hand it to mcp_server as
1280
- # a ready object – that pinned it for the life of the process, so the default stand's
1280
+ # a ready object - that pinned it for the life of the process, so the default stand's
1281
1281
  # .env, edited after the server started, was never re-read. Only the genuine overrides
1282
- # (the flags actually given – base URL, client id and the rest) and --env-file, if any,
1282
+ # (the flags actually given - base URL, client id and the rest) and --env-file, if any,
1283
1283
  # are passed instead; every call still without its own env_file rebuilds from the file on
1284
1284
  # an edit, the overrides applied on top each time.
1285
1285
  if args.env_file:
@@ -1348,7 +1348,7 @@ class _AliasedPositional(argparse.Action):
1348
1348
 
1349
1349
  argparse hands an optional positional its own default when the command line carried
1350
1350
  none, and that default would land on the dest the key form had just filled. Here the
1351
- default is _MISSING, and seeing it means write nothing at all – the dest keeps what
1351
+ default is _MISSING, and seeing it means write nothing at all - the dest keeps what
1352
1352
  the key form put there, or stays out of the namespace when neither form was given.
1353
1353
  """
1354
1354
 
@@ -1363,7 +1363,7 @@ class _AliasedPositional(argparse.Action):
1363
1363
  def _add_aliased_positional(parser, argument):
1364
1364
  """A positional plugin argument and its CLI-only key synonym (Argument.cli_alias).
1365
1365
 
1366
- The MCP tool keeps the one parameter it always had (see mcp_server._plugin_tool) –
1366
+ The MCP tool keeps the one parameter it always had (see mcp_server._plugin_tool) -
1367
1367
  the alias changes what THIS parser accepts, nothing about the declared arguments.
1368
1368
  A mutually exclusive group lets either form fill the same dest: both at once is a
1369
1369
  parser refusal ("not allowed with argument ..."), and required carries over to the
@@ -1371,7 +1371,7 @@ def _add_aliased_positional(parser, argument):
1371
1371
 
1372
1372
  Neither half writes a default: an option left at its own implicit default of None
1373
1373
  would win the namespace the moment the parser builds it (the option is added second,
1374
- and it is not the positional's nargs="?" default that argparse falls back to first) –
1374
+ and it is not the positional's nargs="?" default that argparse falls back to first) -
1375
1375
  silently overriding a non-None default the plugin declared. With both halves quiet the
1376
1376
  dest stays out of the namespace whenever neither form is given, and _plugin_handler
1377
1377
  already falls back to Argument.value_default for exactly that case, the same way it
@@ -1612,7 +1612,7 @@ def build_parser():
1612
1612
  version=f"elemctl {__version__}")
1613
1613
 
1614
1614
  # title= renders the list under a "commands:" heading instead of argparse's default
1615
- # "positional arguments: command" – the same heading the sibling tools use.
1615
+ # "positional arguments: command" - the same heading the sibling tools use.
1616
1616
  sub = parser.add_subparsers(
1617
1617
  dest="command",
1618
1618
  metavar=i18n.t("cli.help.command-metavar"),