apcore-cli 0.12.0__tar.gz → 0.12.1__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 (109) hide show
  1. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/CHANGELOG.md +12 -0
  2. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/PKG-INFO +3 -3
  3. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/pyproject.toml +3 -3
  4. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/acl_loader.py +14 -40
  5. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_acl_loader.py +14 -8
  6. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/.github/CODEOWNERS +0 -0
  7. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/.github/copilot-ignore +0 -0
  8. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/.github/workflows/ci.yml +0 -0
  9. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/.gitignore +0 -0
  10. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/.gitmessage +0 -0
  11. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/.pre-commit-config.yaml +0 -0
  12. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/CLAUDE.md +0 -0
  13. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/LICENSE +0 -0
  14. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/Makefile +0 -0
  15. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/README.md +0 -0
  16. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/README.md +0 -0
  17. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/math/add.py +0 -0
  18. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/math/multiply.py +0 -0
  19. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/sysutil/disk.py +0 -0
  20. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/sysutil/env.py +0 -0
  21. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/sysutil/info.py +0 -0
  22. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/text/reverse.py +0 -0
  23. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/text/upper.py +0 -0
  24. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/extensions/text/wordcount.py +0 -0
  25. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/examples/run_examples.sh +0 -0
  26. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/approval-gate.md +0 -0
  27. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/config-resolver.md +0 -0
  28. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/core-dispatcher.md +0 -0
  29. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/discovery.md +0 -0
  30. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/exposure-filtering.md +0 -0
  31. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/grouped-commands.md +0 -0
  32. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/output-formatter.md +0 -0
  33. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/overview.md +0 -0
  34. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/schema-parser.md +0 -0
  35. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/security-manager.md +0 -0
  36. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/shell-integration.md +0 -0
  37. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/planning/state.json +0 -0
  38. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/__init__.py +0 -0
  39. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/__main__.py +0 -0
  40. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/_sandbox_runner.py +0 -0
  41. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/acl_cmd.py +0 -0
  42. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/approval.py +0 -0
  43. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/builtin_group.py +0 -0
  44. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/cli.py +0 -0
  45. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/config.py +0 -0
  46. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/discovery.py +0 -0
  47. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/display_helpers.py +0 -0
  48. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/exit_codes.py +0 -0
  49. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/exposure.py +0 -0
  50. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/factory.py +0 -0
  51. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/init_cmd.py +0 -0
  52. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/openapi_cmd.py +0 -0
  53. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/openapi_source.py +0 -0
  54. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/output.py +0 -0
  55. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/ref_resolver.py +0 -0
  56. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/schema_parser.py +0 -0
  57. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/security/__init__.py +0 -0
  58. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/security/audit.py +0 -0
  59. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/security/auth.py +0 -0
  60. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/security/config_encryptor.py +0 -0
  61. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/security/sandbox.py +0 -0
  62. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/shell.py +0 -0
  63. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/strategy.py +0 -0
  64. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/system_cmd.py +0 -0
  65. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/system_usage.py +0 -0
  66. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/src/apcore_cli/validate.py +0 -0
  67. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/__init__.py +0 -0
  68. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/conformance/__init__.py +0 -0
  69. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/conformance/test_apcli_visibility.py +0 -0
  70. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/conformance/test_snake_case_kwargs.py +0 -0
  71. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/conftest.py +0 -0
  72. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/shell_test_utils.py +0 -0
  73. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_acl_cmd.py +0 -0
  74. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_apcli_integration.py +0 -0
  75. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_approval.py +0 -0
  76. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_bugfixes.py +0 -0
  77. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_builtin_group.py +0 -0
  78. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_cli.py +0 -0
  79. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_config.py +0 -0
  80. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_discovery.py +0 -0
  81. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_discovery_fe13.py +0 -0
  82. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_display_helpers.py +0 -0
  83. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_e2e.py +0 -0
  84. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_exit_codes.py +0 -0
  85. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_exposure.py +0 -0
  86. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_factory_fe13.py +0 -0
  87. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_init_cmd.py +0 -0
  88. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_integration.py +0 -0
  89. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_list_command_filters.py +0 -0
  90. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_openapi_cmd.py +0 -0
  91. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_openapi_source.py +0 -0
  92. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_output.py +0 -0
  93. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_output_format_exec.py +0 -0
  94. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_output_format_markdown_skill.py +0 -0
  95. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_public_api.py +0 -0
  96. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_ref_resolver.py +0 -0
  97. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_sandbox_runner.py +0 -0
  98. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_schema_parser.py +0 -0
  99. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_security/__init__.py +0 -0
  100. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_security/test_audit.py +0 -0
  101. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_security/test_auth.py +0 -0
  102. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_security/test_config_encryptor.py +0 -0
  103. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_security/test_sandbox.py +0 -0
  104. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_shell.py +0 -0
  105. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_strategy.py +0 -0
  106. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_system_cmd.py +0 -0
  107. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_system_usage.py +0 -0
  108. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_toolkit_integration.py +0 -0
  109. {apcore_cli-0.12.0 → apcore_cli-0.12.1}/tests/test_validate.py +0 -0
@@ -5,6 +5,18 @@ All notable changes to apcore-cli (Python SDK) will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.12.1] - 2026-09-24
9
+
10
+ Patch release. Bumps the required `apcore` floor to `0.31.0` and `apcore-toolkit` to `[http-proxy]>=0.12.0`. All 1021 tests pass (6 xfailed).
11
+
12
+ ### Changed
13
+
14
+ - **`load_cli_acl` no longer rebuilds the ACL to attach the §4.8 audit callback.** apcore 0.31.0 (decision D-66) makes `ACL.load(path, audit_logger=…)` accept the callback directly; the `ACL(src.rules, src.default_effect, audit_logger=…)` rebuild this SDK relied on — the only lossless way to attach one before this release — is gone. `reload()` is now available on the ACL `load_cli_acl` returns whether or not `acl.audit.enabled` is set; previously it raised `ACLRuleError` on the auditing path.
15
+
16
+ ### Security
17
+
18
+ - **`apcli openapi scan|generate` inherits apcore-toolkit 0.12.0's `$ref` sibling-key fix.** `OpenAPIScanner` (imported, never reimplemented, by `openapi_cmd.py`/`openapi_source.py`) previously discarded a `$ref` node's sibling keys during schema resolution, dropping `x-sensitive` and letting a field an OpenAPI document marked sensitive reach apcore with nothing to redact on. Fixed upstream; no code change was needed here.
19
+
8
20
  ## [0.12.0] - 2026-09-05
9
21
 
10
22
  Two features: **FE-14 ACL Governance** and **FE-15a OpenAPI Import**. Bumps the required `apcore` floor to `0.30.0` and `apcore-toolkit` to `[http-proxy]>=0.11.1`. Full suite: 1004 passed, 5 xfailed (815 → 1004; 189 new tests). `APCLI_SUBCOMMAND_NAMES` grows from 13 to 15.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: apcore-cli
3
- Version: 0.12.0
3
+ Version: 0.12.1
4
4
  Summary: Terminal adapter for apcore — execute AI-Perceivable modules from the command line
5
5
  Project-URL: Homepage, https://aiperceivable.com
6
6
  Project-URL: Repository, https://github.com/aiperceivable/apcore-cli-python
@@ -21,8 +21,8 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Python: >=3.11
24
- Requires-Dist: apcore-toolkit[http-proxy]>=0.11.1
25
- Requires-Dist: apcore>=0.30.0
24
+ Requires-Dist: apcore-toolkit[http-proxy]>=0.12.0
25
+ Requires-Dist: apcore>=0.31.0
26
26
  Requires-Dist: click>=8.1
27
27
  Requires-Dist: cryptography>=41.0
28
28
  Requires-Dist: jsonschema>=4.20
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "apcore-cli"
7
- version = "0.12.0"
7
+ version = "0.12.1"
8
8
  description = "Terminal adapter for apcore — execute AI-Perceivable modules from the command line"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -26,8 +26,8 @@ classifiers = [
26
26
  "Environment :: Console",
27
27
  ]
28
28
  dependencies = [
29
- "apcore>=0.30.0",
30
- "apcore-toolkit[http-proxy]>=0.11.1",
29
+ "apcore>=0.31.0",
30
+ "apcore-toolkit[http-proxy]>=0.12.0",
31
31
  "click>=8.1",
32
32
  "jsonschema>=4.20",
33
33
  "rich>=13.0",
@@ -210,32 +210,13 @@ def make_acl_audit_logger(*, include_denied: bool = True) -> Callable[[AuditEntr
210
210
  def load_cli_acl(root: str, *, audit_enabled: bool = True, include_denied: bool = True) -> ACL | None:
211
211
  """Load an :class:`~apcore.ACL` from *root*, or ``None`` for no enforcement.
212
212
 
213
- When *audit_enabled* is true the ACL is **rebuilt** so it can carry the
214
- §4.8 audit callback: ``ACL.load`` takes no ``audit_logger``, and the
215
- callback is a constructor argument, so ::
216
-
217
- src = ACL.load(resolved_path)
218
- acl = ACL(src.rules, src.default_effect, audit_logger=…)
219
-
220
- is the only lossless way to attach one. Two things about that sequence are
221
- load-bearing:
222
-
223
- 1. ``default_effect`` is carried from ``src``, **never** a literal. A file
224
- may legitimately declare ``default_effect: allow``, and passing the
225
- constructor's own ``"deny"`` default would silently invert the governing
226
- verdict for every call no rule matched — a rule set that grants by
227
- default would start denying, with nothing in the output saying so.
228
- 2. The rebuilt ACL **loses** :meth:`~apcore.ACL.reload`, which needs the
229
- ``_yaml_path`` only ``ACL.load`` sets. This is accepted rather than
230
- worked around: no apcore-cli SDK calls ``reload()`` on any path, and
231
- writing the private attribute to fake the provenance would make
232
- ``reload()`` claim a file the object was not in fact loaded from. An
233
- embedder that needs reloading passes its own ACL to
234
- ``create_cli(acl=…)``, which §4.2 attaches unchanged.
235
-
236
- With auditing disabled the ``ACL.load`` result is returned **directly** —
237
- no rebuild, no callback — so the ``reload()`` caveat applies only to the
238
- auditing path.
213
+ ``ACL.load`` takes ``audit_logger`` directly (apcore 0.31.0, D-66), so the
214
+ §4.8 callback is attached in the same call that parses the file — no
215
+ rebuild, and :meth:`~apcore.ACL.reload` stays available on the returned
216
+ object. (An earlier revision rebuilt the ACL via
217
+ ``ACL(src.rules, src.default_effect, audit_logger=…)`` to attach the
218
+ callback after the fact, which lost ``reload()`` in the process; that
219
+ workaround is gone now that apcore accepts the callback at load time.)
239
220
 
240
221
  Args:
241
222
  root: Resolved ACL root (file or directory).
@@ -259,20 +240,13 @@ def load_cli_acl(root: str, *, audit_enabled: bool = True, include_denied: bool
259
240
  if acl_file is None:
260
241
  logger.debug("No ACL at '%s' — enforcement stays off.", root)
261
242
  return None
262
- src = _ACL.load(acl_file)
263
- logger.info("ACL loaded from %s (%d rules).", acl_file, len(src.rules))
264
- if not audit_enabled:
265
- logger.debug("acl.audit.enabled is false — attaching the loaded ACL directly, no audit callback.")
266
- return src
267
- acl = _ACL(
268
- src.rules,
269
- src.default_effect,
270
- audit_logger=make_acl_audit_logger(include_denied=include_denied),
271
- )
272
- logger.debug(
273
- "ACL audit callback installed (include_denied=%s); reload() is unavailable on the rebuilt ACL.",
274
- include_denied,
275
- )
243
+ audit_logger = make_acl_audit_logger(include_denied=include_denied) if audit_enabled else None
244
+ acl = _ACL.load(acl_file, audit_logger=audit_logger)
245
+ logger.info("ACL loaded from %s (%d rules).", acl_file, len(acl.rules))
246
+ if audit_enabled:
247
+ logger.debug("ACL audit callback installed (include_denied=%s).", include_denied)
248
+ else:
249
+ logger.debug("acl.audit.enabled is false — no audit callback installed.")
276
250
  return acl
277
251
 
278
252
 
@@ -659,8 +659,8 @@ class TestAuditWiring:
659
659
  real_load = _ACL.load
660
660
  captured: dict[str, object] = {}
661
661
 
662
- def _spy(path: str):
663
- result = real_load(path)
662
+ def _spy(path: str, audit_logger=None):
663
+ result = real_load(path, audit_logger=audit_logger)
664
664
  captured["acl"] = result
665
665
  return result
666
666
 
@@ -678,14 +678,20 @@ class TestAuditWiring:
678
678
  assert acl.check_access("@external", "db.migrate", None).access == "allow"
679
679
  assert _audit_records(audit_path) == []
680
680
 
681
- def test_auditing_path_rebuilds_and_documents_the_reload_cost(self, workdir):
682
- """§4.8 requirement 2 — accepted, and pinned so it cannot be silent."""
683
- from apcore.errors import ACLRuleError
681
+ def test_auditing_path_reload_works(self, workdir, audit_path):
682
+ """§4.8 requirement 2, updated for apcore 0.31.0 (D-66).
684
683
 
684
+ ``ACL.load`` now takes ``audit_logger`` directly, so the auditing path
685
+ no longer rebuilds the ACL — ``reload()`` stays available, and the
686
+ installed audit callback survives it (apcore's own §6.3.2
687
+ requirement 7).
688
+ """
685
689
  _write_acl_dir(workdir)
686
- rebuilt = load_cli_acl(str(workdir / "acl"), audit_enabled=True)
687
- with pytest.raises(ACLRuleError, match="was not loaded from a YAML file"):
688
- rebuilt.reload()
690
+ acl = load_cli_acl(str(workdir / "acl"), audit_enabled=True)
691
+ acl.reload()
692
+
693
+ acl.check_access("@external", "system.control.disable", None)
694
+ assert [r["decision"] for r in _audit_records(audit_path)] == ["deny"]
689
695
 
690
696
  def test_allow_default_survives_the_rebuild(self, workdir, audit_path):
691
697
  """T-ACL-27b: `default_effect: allow` + auditing enabled.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes