apcore-cli 0.8.0__tar.gz → 0.9.0__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.
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/CHANGELOG.md +46 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/CLAUDE.md +13 -5
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/PKG-INFO +10 -11
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/README.md +8 -8
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/pyproject.toml +6 -5
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/__init__.py +19 -3
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/approval.py +52 -2
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/cli.py +23 -11
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/discovery.py +9 -3
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/exit_codes.py +1 -3
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/factory.py +10 -10
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/init_cmd.py +1 -1
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/output.py +30 -31
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/ref_resolver.py +25 -11
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/schema_parser.py +1 -1
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/audit.py +8 -2
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/config_encryptor.py +18 -5
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/shell.py +7 -7
- apcore_cli-0.9.0/tests/conformance/test_snake_case_kwargs.py +82 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_apcli_integration.py +3 -3
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_cli.py +11 -11
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_output_format_exec.py +31 -3
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_ref_resolver.py +72 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_config_encryptor.py +85 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_shell.py +1 -1
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.github/CODEOWNERS +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.github/copilot-ignore +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.gitignore +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.gitmessage +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.pre-commit-config.yaml +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/LICENSE +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/Makefile +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/README.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/math/add.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/math/multiply.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/disk.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/env.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/info.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/text/reverse.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/text/upper.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/text/wordcount.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/run_examples.sh +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/approval-gate.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/config-resolver.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/core-dispatcher.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/discovery.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/exposure-filtering.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/grouped-commands.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/output-formatter.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/overview.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/schema-parser.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/security-manager.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/shell-integration.md +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/state.json +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/__main__.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/_sandbox_runner.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/builtin_group.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/config.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/display_helpers.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/exposure.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/__init__.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/auth.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/sandbox.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/strategy.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/system_cmd.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/system_usage.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/validate.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/__init__.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/conformance/__init__.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/conformance/test_apcli_visibility.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/conftest.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_approval.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_bugfixes.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_builtin_group.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_config.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_discovery.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_discovery_fe13.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_display_helpers.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_e2e.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_exit_codes.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_exposure.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_factory_fe13.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_init_cmd.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_integration.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_list_command_filters.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_output.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_output_format_markdown_skill.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_public_api.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_sandbox_runner.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_schema_parser.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/__init__.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_audit.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_auth.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_sandbox.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_strategy.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_system_cmd.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_system_usage.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_toolkit_integration.py +0 -0
- {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_validate.py +0 -0
|
@@ -5,6 +5,52 @@ 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.9.0] - 2026-05-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`tests/conformance/test_snake_case_kwargs.py`** — runs the cross-language Algorithm C-SNAKE fixture (`apcore-cli/conformance/fixtures/snake-case-kwargs/cases.json`) against `build_module_command` via `click.testing.CliRunner`. Five cases verify that schema property names with underscores (`has_solution`, `sort_by`, `sort_order`) survive the round trip from CLI parse to the input dict received by `executor.call`. No source change required — click natively maps `--has-solution` to `has_solution`; the Python SDK is the parity reference for the parallel TypeScript fix. Surfaced as part of the cross-SDK regression coverage gap audit.
|
|
13
|
+
|
|
14
|
+
### Fixed (2026-05-13 — cross-SDK audit D10/D11/D1)
|
|
15
|
+
|
|
16
|
+
- **`ConfigEncryptor` LOGNAME key-derivation chain** (D10-001 / D11-003) — PBKDF2 username fallback was `USER → USERNAME → "unknown"` (3-tier); now `USER → LOGNAME → USERNAME → "unknown"` (4-tier) matching the spec and Rust. On hosts where `USER` is unset but `LOGNAME` is set (cron, `sudo -i`, container init), ciphertext written by the Python SDK now round-trips correctly with the Rust SDK. `src/apcore_cli/security/config_encryptor.py:96, 165`.
|
|
17
|
+
- **`ConfigEncryptor.store` keyring write-failure not wrapped** (D11-004) — raw `keyring.set_password` exceptions now caught and re-raised as `ConfigDecryptionError`, matching TypeScript and Rust. `src/apcore_cli/security/config_encryptor.py:31`.
|
|
18
|
+
- **`ref_resolver` only descended into `properties`** (D11-001) — recursive schema walk now visits every dict-valued child (items, additionalProperties, patternProperties, if/then/else, not, contains, propertyNames), matching TypeScript and Rust. `$ref` under array schemas and conditional schemas is now resolved. `src/apcore_cli/ref_resolver.py:142`.
|
|
19
|
+
- **`ref_resolver` copy-on-write visited-set** (D11-002) — now uses a single mutable set with remove-on-unwind, allowing diamond `$ref` patterns (two sibling schemas referencing the same `$def`) to resolve correctly. `src/apcore_cli/ref_resolver.py:71`.
|
|
20
|
+
- **`AuditLogger._get_user` uses real UID instead of effective UID** (D11-010) — switched from `os.getuid()` to `os.geteuid()` so audit records reflect the privileges the process actually runs with under `sudo` / setuid binaries. Matches Rust (`geteuid`) and TypeScript (`os.userInfo`). `src/apcore_cli/security/audit.py:77`.
|
|
21
|
+
- **`check_approval` ignores `APCORE_CLI_APPROVAL_TIMEOUT` env var** (D11-012) — `CliApprovalHandler` and the legacy `check_approval()` wrapper now honor the env var when no explicit timeout is passed (precedence: constructor arg > env var > 60 s default). Matches TypeScript. `src/apcore_cli/approval.py`.
|
|
22
|
+
- **`exec --dry-run` crashes with `AttributeError` when executor lacks `validate`** (D11-013) — guarded with `hasattr(executor, "validate")`; falls back to synthetic `{"valid": True}` matching TypeScript. `src/apcore_cli/discovery.py:378`.
|
|
23
|
+
- **`CliApprovalHandler.request_approval` missing `requires_approval=False` short-circuit** (D11-014) — now returns `approved/not_required` when the request explicitly carries `requires_approval=False`, matching Rust. `src/apcore_cli/approval.py:66`.
|
|
24
|
+
- **CLI brand string in auth error messages** (D11-006) — remediation strings now say `apcli config set auth.api_key` (canonical FE-13 name) instead of `apcore-cli config set auth.api_key`. `src/apcore_cli/security/auth.py`.
|
|
25
|
+
- **`reconvert_enum_values` missing from public re-export** (D1-W2) — added to `__init__.py` import block and `__all__`. Embedders can now `from apcore_cli import reconvert_enum_values` for parity with TypeScript and Rust.
|
|
26
|
+
- **Ref-resolver error hierarchy missing from public re-export** (D1-W3) — `CircularRefError`, `MaxDepthExceededError`, `UnresolvableRefError`, `RefResolverError` added to `__init__.py` import block and `__all__`. Parity with TypeScript `index.ts:82-84`.
|
|
27
|
+
- **`DEFAULT_BUILTIN_GROUP_NAME` missing from public re-export** (D1 re-audit) — added to `__init__.py`. Parity with Rust `lib.rs:190`.
|
|
28
|
+
- **Dead exit-code constants removed** (D9-W1) — `EXIT_CONFIG_ENV_PREFIX_CONFLICT` and `EXIT_CONFIG_ENV_MAP_CONFLICT` (both = 78, zero callers) deleted from `src/apcore_cli/exit_codes.py`.
|
|
29
|
+
- **Unused `pytest-asyncio` dev dependency removed** (D6) — the package was declared but never exercised; no async tests exist. Removed from `[project.optional-dependencies].dev`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **CSV `--format csv` Python-repr bug** — `csv.DictWriter` was called with `{k: str(v) for k, v in row.items()}` which emitted Python repr `{'k': 'v'}` (single quotes) for nested dict/list values. The output was not valid JSON and any downstream JSON parser would fail. Now delegates to `apcore_toolkit.format_csv(rows)` which emits canonical compact JSON. `src/apcore_cli/output.py:149, 378`.
|
|
34
|
+
- **CSV heterogeneous-keys data loss** — header is now the union of keys across all rows (was first-row only via `list(rows[0].keys())`).
|
|
35
|
+
- **CSV line terminator** — now `\r\n` per RFC 4180.
|
|
36
|
+
- **JSONL canonical form** — now compact (no spaces between separators), matching the cross-SDK contract. Tests updated.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **User-visible help/man/completion text no longer leaks the `apcore` framework name** to end users of downstream CLIs built on apcore-cli. Affected strings: `init` group description (`Scaffold new apcore modules` → `Scaffold new modules`, `init_cmd.py:45`), `--extensions-dir` option help (`Path to apcore extensions directory.` → `Path to extensions directory.`, `factory.py:460`), zsh/fish completion descriptions for `exec` (`Execute an apcore module` → `Execute a module`, `shell.py:130, 211`), and man-page `ENVIRONMENT` section text (`shell.py:299, 314, 319, 458`) — drops `apcore` from the descriptive copy (`Path to the apcore extensions directory` → `Path to the extensions directory`, `Global apcore logging verbosity` → `Global logging verbosity`, `API key for authenticating with the apcore registry` → `API key for authenticating with the registry`). Logger names, source comments, module docstrings, and environment-variable identifiers (`APCORE_*`) are unchanged — only descriptive copy that appears in `--help`, shell completion, and `man` output. Cross-SDK parity with TypeScript 0.8.2 and Rust 0.8.1.
|
|
41
|
+
|
|
42
|
+
### Changed (breaking CLI surface)
|
|
43
|
+
|
|
44
|
+
- **Global `--verbose` flag renamed to `--all-options`** — The help-display flag is now `--all-options`; use `apcore-cli module --help --all-options` to reveal hidden built-in options. `verbose` is removed from the reserved schema property names set — module schemas may now freely define `verbose: boolean` for runtime output control. Internal API: `set_verbose_help()` renamed to `set_all_options_help()`; module-level global `_verbose_help` renamed to `_all_options_help`. Tracked in [apcore-cli#21](https://github.com/aiperceivable/apcore-cli/issues/21).
|
|
45
|
+
|
|
46
|
+
### Changed (breaking dependency semantics)
|
|
47
|
+
|
|
48
|
+
- **`apcore-toolkit` promoted from optional extra to REQUIRED runtime dependency** (`>=0.7.0`). The previous `pip install 'apcore-cli[toolkit]'` extras pattern is retained as a no-op for backward compat with install scripts, but the toolkit is now always installed alongside apcore-cli. All `--format` operations route through the toolkit's reference implementation for csv/jsonl/markdown/skill.
|
|
49
|
+
|
|
50
|
+
### Why
|
|
51
|
+
|
|
52
|
+
See ADR-09 in `apcore-cli/docs/tech-design.md` for the byte-equivalent toolkit-delegated tier rationale.
|
|
53
|
+
|
|
8
54
|
|
|
9
55
|
## [0.8.0] - 2026-05-08
|
|
10
56
|
|
|
@@ -28,15 +28,16 @@
|
|
|
28
28
|
|
|
29
29
|
- Python >= 3.11
|
|
30
30
|
- Key dependencies: click >= 8.1, rich >= 13.0, jsonschema >= 4.20, pyyaml >= 6.0, keyring >= 24, cryptography >= 41
|
|
31
|
-
- Runtime: apcore >= 0.
|
|
32
|
-
-
|
|
31
|
+
- Runtime: apcore >= 0.21.0 (v0.9.0 bump, was 0.19.0 at v0.7.0)
|
|
32
|
+
- Runtime (required, v0.9.0+): apcore-toolkit >= 0.7.0. The `[toolkit]` extras group is retained as a no-op for downstream install scripts.
|
|
33
33
|
- Dev: pytest, pytest-asyncio, pytest-cov, mypy, ruff
|
|
34
34
|
|
|
35
|
-
## v0.
|
|
35
|
+
## v0.9.0 Conventions
|
|
36
36
|
|
|
37
37
|
- Public surface (`__init__.py`): `__version__`, `create_cli`, `ExposureFilter`,
|
|
38
38
|
`ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES`, `CliApprovalHandler`,
|
|
39
39
|
`resolve_refs`, `schema_to_click_options`, `format_exec_result`,
|
|
40
|
+
`set_verbose_help`, `set_docs_url`, `set_audit_logger`,
|
|
40
41
|
`ConfigResolver`, `AuditLogger`, `AuthProvider`, `ConfigEncryptor`, `Sandbox`,
|
|
41
42
|
plus error classes (AuthenticationError, ConfigDecryptionError,
|
|
42
43
|
ModuleExecutionError, ApprovalTimeoutError, ApprovalDeniedError).
|
|
@@ -45,9 +46,16 @@
|
|
|
45
46
|
- ExposureFilter + `expose=` kwarg on create_cli (FE-12).
|
|
46
47
|
- `extra_commands=[...]` kwarg on create_cli as the FE-11 extension point (with
|
|
47
48
|
collision detection against RESERVED_GROUP_NAMES — `BUILTIN_COMMANDS` retired v0.7.0).
|
|
49
|
+
- `builtin_group_name="apcli"` kwarg on create_cli (v0.8.0+) for embed-API renaming (FE-13).
|
|
48
50
|
- Default click Group class is `GroupedModuleGroup` (multi-level grouping since v0.3.0).
|
|
49
|
-
- All apcore-cli commands live under the `apcli` group (FE-13).
|
|
50
|
-
|
|
51
|
+
- All apcore-cli commands live exclusively under the `apcli` group (FE-13). Root-level
|
|
52
|
+
deprecation shims were removed in v0.9.0 (deprecated in v0.8.x).
|
|
53
|
+
- `--verbose` global flag renamed to `--all-options` in v0.9.0; `set_verbose_help(bool)` is
|
|
54
|
+
the programmatic equivalent (internal name retained for back-compat; Rust uses `set_all_options_help`).
|
|
55
|
+
- `verbose` removed from reserved schema property names set in v0.9.0; modules may now
|
|
56
|
+
define `verbose: boolean` freely.
|
|
57
|
+
- apcore-toolkit promoted from optional to REQUIRED runtime dep in v0.9.0; csv/jsonl/markdown/skill
|
|
58
|
+
output formats now toolkit-delegated (ADR-09).
|
|
51
59
|
- `system_cmd` module registers runtime system commands (health/usage/enable/disable/
|
|
52
60
|
reload/config) — FE-11.
|
|
53
61
|
- `strategy` module registers describe-pipeline + --strategy flag — FE-11.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apcore-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
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,6 +21,7 @@ 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>=0.7.0
|
|
24
25
|
Requires-Dist: apcore>=0.21.0
|
|
25
26
|
Requires-Dist: click>=8.1
|
|
26
27
|
Requires-Dist: cryptography>=41.0
|
|
@@ -32,12 +33,10 @@ Provides-Extra: dev
|
|
|
32
33
|
Requires-Dist: apdev[dev]>=0.2.3; extra == 'dev'
|
|
33
34
|
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
34
35
|
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
35
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
36
36
|
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
37
37
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
38
38
|
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
39
39
|
Provides-Extra: toolkit
|
|
40
|
-
Requires-Dist: apcore-toolkit>=0.6; extra == 'toolkit'
|
|
41
40
|
Description-Content-Type: text/markdown
|
|
42
41
|
|
|
43
42
|
<div align="center">
|
|
@@ -90,10 +89,10 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
|
|
|
90
89
|
pip install apcore-cli
|
|
91
90
|
```
|
|
92
91
|
|
|
93
|
-
Requires Python 3.11
|
|
92
|
+
Requires Python 3.11+, `apcore >= 0.21.0`, and `apcore-toolkit >= 0.7.0` (now a **required** runtime dependency as of v0.9.0 — previously optional; see the [tech-design ADR-09](https://github.com/aiperceivable/apcore-cli/blob/main/docs/tech-design.md) for the byte-equivalent toolkit-delegated tier rationale). The `[toolkit]` extras group is retained as a no-op for backward compat:
|
|
94
93
|
|
|
95
94
|
```bash
|
|
96
|
-
pip install "apcore-cli[toolkit]"
|
|
95
|
+
pip install "apcore-cli[toolkit]" # equivalent to plain `pip install apcore-cli`
|
|
97
96
|
```
|
|
98
97
|
|
|
99
98
|
## Quick Start
|
|
@@ -168,7 +167,7 @@ The `apcore_cli` package re-exports the following public surface (see [`src/apco
|
|
|
168
167
|
| Export | Description |
|
|
169
168
|
|--------|-------------|
|
|
170
169
|
| `__version__` | Package version string |
|
|
171
|
-
| `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full
|
|
170
|
+
| `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full 14-parameter signature: `extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `app`, `expose`, **`apcli`** (FE-13 P0 break), `allowed_prefixes`, `version`, `description`, **`builtin_group_name`** (default `"apcli"`, v0.8.0+). |
|
|
172
171
|
| `ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES` | FE-13 built-in `apcli` group surface (P0 break in v0.8.0). |
|
|
173
172
|
| `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
|
|
174
173
|
| `CliApprovalHandler`, `check_approval` | TTY-aware HITL approval handler / helper (FE-11). |
|
|
@@ -314,12 +313,12 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
|
|
|
314
313
|
| `--log-level` | `WARNING` | Logging: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
315
314
|
| `--version` | | Show version and exit |
|
|
316
315
|
| `--help` | | Show help and exit |
|
|
317
|
-
| `--
|
|
316
|
+
| `--all-options` | | Show hidden built-in options in `--help` output |
|
|
318
317
|
| `--man` | | Print man page to stdout (use with `--help`) |
|
|
319
318
|
|
|
320
319
|
### Built-in Commands
|
|
321
320
|
|
|
322
|
-
apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims
|
|
321
|
+
apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims were removed in v0.9.0 (they emitted deprecation warnings in v0.8.x). Use `apcore-cli apcli <subcommand>`.
|
|
323
322
|
|
|
324
323
|
**Module invocation**
|
|
325
324
|
|
|
@@ -358,14 +357,14 @@ The full-program man page is reachable via the root flag combination `apcore-cli
|
|
|
358
357
|
|
|
359
358
|
### Module Execution Options
|
|
360
359
|
|
|
361
|
-
When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --
|
|
360
|
+
When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --all-options` to display them):
|
|
362
361
|
|
|
363
362
|
| Option | Description |
|
|
364
363
|
|--------|-------------|
|
|
365
364
|
| `--input -` | Read JSON input from STDIN |
|
|
366
365
|
| `--yes` / `-y` | Bypass approval prompts |
|
|
367
366
|
| `--large-input` | Allow STDIN input larger than 10MB |
|
|
368
|
-
| `--format` | Output format: `{json, table, csv, yaml, jsonl}` |
|
|
367
|
+
| `--format` | Output format: `{json, table, csv, yaml, jsonl, markdown, skill}`. **v0.9.0:** `csv` and `jsonl` are byte-identical across SDKs (delegated to `apcore-toolkit.format_csv` / `format_jsonl`); fixes the prior `str(v)` Python-repr bug for nested values. |
|
|
369
368
|
| `--sandbox` | Run module in subprocess sandbox *(not yet implemented)* |
|
|
370
369
|
| `--dry-run` | Run preflight checks without executing (FE-11, v0.6.0) |
|
|
371
370
|
| `--trace` | Emit execution pipeline trace (v0.6.0) |
|
|
@@ -505,7 +504,7 @@ apcore-cli (the adapter)
|
|
|
505
504
|
+-- system_cmd Runtime system-management (health/usage/enable/disable/reload/config)
|
|
506
505
|
+-- strategy Execution strategy dispatch (--strategy flag and describe-pipeline)
|
|
507
506
|
+-- init_cmd Module scaffolding (init subcommand)
|
|
508
|
-
+-- set_verbose_help Toggle built-in option visibility
|
|
507
|
+
+-- set_verbose_help Toggle built-in option visibility (internal name; controls --all-options behaviour)
|
|
509
508
|
+-- set_docs_url Set base URL for online docs
|
|
510
509
|
+-- build_program_man_page Full-program roff man page
|
|
511
510
|
+-- configure_man_help Add --help --man support to any CLI
|
|
@@ -48,10 +48,10 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
|
|
|
48
48
|
pip install apcore-cli
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
Requires Python 3.11
|
|
51
|
+
Requires Python 3.11+, `apcore >= 0.21.0`, and `apcore-toolkit >= 0.7.0` (now a **required** runtime dependency as of v0.9.0 — previously optional; see the [tech-design ADR-09](https://github.com/aiperceivable/apcore-cli/blob/main/docs/tech-design.md) for the byte-equivalent toolkit-delegated tier rationale). The `[toolkit]` extras group is retained as a no-op for backward compat:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
pip install "apcore-cli[toolkit]"
|
|
54
|
+
pip install "apcore-cli[toolkit]" # equivalent to plain `pip install apcore-cli`
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## Quick Start
|
|
@@ -126,7 +126,7 @@ The `apcore_cli` package re-exports the following public surface (see [`src/apco
|
|
|
126
126
|
| Export | Description |
|
|
127
127
|
|--------|-------------|
|
|
128
128
|
| `__version__` | Package version string |
|
|
129
|
-
| `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full
|
|
129
|
+
| `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full 14-parameter signature: `extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `app`, `expose`, **`apcli`** (FE-13 P0 break), `allowed_prefixes`, `version`, `description`, **`builtin_group_name`** (default `"apcli"`, v0.8.0+). |
|
|
130
130
|
| `ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES` | FE-13 built-in `apcli` group surface (P0 break in v0.8.0). |
|
|
131
131
|
| `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
|
|
132
132
|
| `CliApprovalHandler`, `check_approval` | TTY-aware HITL approval handler / helper (FE-11). |
|
|
@@ -272,12 +272,12 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
|
|
|
272
272
|
| `--log-level` | `WARNING` | Logging: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
273
273
|
| `--version` | | Show version and exit |
|
|
274
274
|
| `--help` | | Show help and exit |
|
|
275
|
-
| `--
|
|
275
|
+
| `--all-options` | | Show hidden built-in options in `--help` output |
|
|
276
276
|
| `--man` | | Print man page to stdout (use with `--help`) |
|
|
277
277
|
|
|
278
278
|
### Built-in Commands
|
|
279
279
|
|
|
280
|
-
apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims
|
|
280
|
+
apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims were removed in v0.9.0 (they emitted deprecation warnings in v0.8.x). Use `apcore-cli apcli <subcommand>`.
|
|
281
281
|
|
|
282
282
|
**Module invocation**
|
|
283
283
|
|
|
@@ -316,14 +316,14 @@ The full-program man page is reachable via the root flag combination `apcore-cli
|
|
|
316
316
|
|
|
317
317
|
### Module Execution Options
|
|
318
318
|
|
|
319
|
-
When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --
|
|
319
|
+
When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --all-options` to display them):
|
|
320
320
|
|
|
321
321
|
| Option | Description |
|
|
322
322
|
|--------|-------------|
|
|
323
323
|
| `--input -` | Read JSON input from STDIN |
|
|
324
324
|
| `--yes` / `-y` | Bypass approval prompts |
|
|
325
325
|
| `--large-input` | Allow STDIN input larger than 10MB |
|
|
326
|
-
| `--format` | Output format: `{json, table, csv, yaml, jsonl}` |
|
|
326
|
+
| `--format` | Output format: `{json, table, csv, yaml, jsonl, markdown, skill}`. **v0.9.0:** `csv` and `jsonl` are byte-identical across SDKs (delegated to `apcore-toolkit.format_csv` / `format_jsonl`); fixes the prior `str(v)` Python-repr bug for nested values. |
|
|
327
327
|
| `--sandbox` | Run module in subprocess sandbox *(not yet implemented)* |
|
|
328
328
|
| `--dry-run` | Run preflight checks without executing (FE-11, v0.6.0) |
|
|
329
329
|
| `--trace` | Emit execution pipeline trace (v0.6.0) |
|
|
@@ -463,7 +463,7 @@ apcore-cli (the adapter)
|
|
|
463
463
|
+-- system_cmd Runtime system-management (health/usage/enable/disable/reload/config)
|
|
464
464
|
+-- strategy Execution strategy dispatch (--strategy flag and describe-pipeline)
|
|
465
465
|
+-- init_cmd Module scaffolding (init subcommand)
|
|
466
|
-
+-- set_verbose_help Toggle built-in option visibility
|
|
466
|
+
+-- set_verbose_help Toggle built-in option visibility (internal name; controls --all-options behaviour)
|
|
467
467
|
+-- set_docs_url Set base URL for online docs
|
|
468
468
|
+-- build_program_man_page Full-program roff man page
|
|
469
469
|
+-- configure_man_help Add --help --man support to any CLI
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apcore-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.9.0"
|
|
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"
|
|
@@ -27,6 +27,7 @@ classifiers = [
|
|
|
27
27
|
]
|
|
28
28
|
dependencies = [
|
|
29
29
|
"apcore>=0.21.0",
|
|
30
|
+
"apcore-toolkit>=0.7.0",
|
|
30
31
|
"click>=8.1",
|
|
31
32
|
"jsonschema>=4.20",
|
|
32
33
|
"rich>=13.0",
|
|
@@ -39,15 +40,15 @@ dependencies = [
|
|
|
39
40
|
dev = [
|
|
40
41
|
"apdev[dev]>=0.2.3",
|
|
41
42
|
"pytest>=7.0",
|
|
42
|
-
"pytest-asyncio>=0.23",
|
|
43
43
|
"pytest-cov>=4.0",
|
|
44
44
|
"mypy>=1.0",
|
|
45
45
|
"ruff>=0.1",
|
|
46
46
|
"pre-commit>=3.5",
|
|
47
47
|
]
|
|
48
|
-
toolkit
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
# `toolkit` is kept as a no-op extras group for backward compat with downstream
|
|
49
|
+
# `apcore-cli[toolkit]` install commands; apcore-toolkit is now a required
|
|
50
|
+
# runtime dependency (see ADR-09).
|
|
51
|
+
toolkit = []
|
|
51
52
|
|
|
52
53
|
[project.scripts]
|
|
53
54
|
apcore-cli = "apcore_cli.__main__:main"
|
|
@@ -17,6 +17,7 @@ from apcore_cli.approval import (
|
|
|
17
17
|
)
|
|
18
18
|
from apcore_cli.builtin_group import (
|
|
19
19
|
APCLI_SUBCOMMAND_NAMES,
|
|
20
|
+
DEFAULT_BUILTIN_GROUP_NAME,
|
|
20
21
|
RESERVED_GROUP_NAMES,
|
|
21
22
|
ApcliConfig,
|
|
22
23
|
ApcliGroup,
|
|
@@ -26,6 +27,7 @@ from apcore_cli.builtin_group import (
|
|
|
26
27
|
from apcore_cli.cli import (
|
|
27
28
|
build_module_command,
|
|
28
29
|
collect_input,
|
|
30
|
+
set_all_options_help,
|
|
29
31
|
set_audit_logger,
|
|
30
32
|
set_docs_url,
|
|
31
33
|
set_verbose_help,
|
|
@@ -77,8 +79,14 @@ from apcore_cli.output import (
|
|
|
77
79
|
format_module_list,
|
|
78
80
|
resolve_format,
|
|
79
81
|
)
|
|
80
|
-
from apcore_cli.ref_resolver import
|
|
81
|
-
|
|
82
|
+
from apcore_cli.ref_resolver import (
|
|
83
|
+
CircularRefError,
|
|
84
|
+
MaxDepthExceededError,
|
|
85
|
+
RefResolverError,
|
|
86
|
+
UnresolvableRefError,
|
|
87
|
+
resolve_refs,
|
|
88
|
+
)
|
|
89
|
+
from apcore_cli.schema_parser import reconvert_enum_values, schema_to_click_options
|
|
82
90
|
from apcore_cli.security.audit import AuditLogger
|
|
83
91
|
from apcore_cli.security.auth import AuthenticationError, AuthProvider
|
|
84
92
|
from apcore_cli.security.config_encryptor import ConfigDecryptionError, ConfigEncryptor
|
|
@@ -114,6 +122,7 @@ __all__ = [
|
|
|
114
122
|
"ApcliGroupError",
|
|
115
123
|
"ApcliMode",
|
|
116
124
|
"APCLI_SUBCOMMAND_NAMES",
|
|
125
|
+
"DEFAULT_BUILTIN_GROUP_NAME",
|
|
117
126
|
"RESERVED_GROUP_NAMES",
|
|
118
127
|
# FE-11 approval
|
|
119
128
|
"CliApprovalHandler",
|
|
@@ -123,6 +132,7 @@ __all__ = [
|
|
|
123
132
|
# Schema / output / ref resolution
|
|
124
133
|
"resolve_refs",
|
|
125
134
|
"schema_to_click_options",
|
|
135
|
+
"reconvert_enum_values", # parity with TS reconvertEnumValues / Rust reconvert_enum_values
|
|
126
136
|
"format_exec_result",
|
|
127
137
|
"format_module_list",
|
|
128
138
|
"format_module_detail",
|
|
@@ -139,8 +149,9 @@ __all__ = [
|
|
|
139
149
|
"build_module_command",
|
|
140
150
|
"collect_input",
|
|
141
151
|
"validate_module_id",
|
|
152
|
+
"set_all_options_help",
|
|
142
153
|
"set_audit_logger",
|
|
143
|
-
"set_verbose_help",
|
|
154
|
+
"set_verbose_help", # deprecated alias for set_all_options_help
|
|
144
155
|
"set_docs_url",
|
|
145
156
|
# Per-subcommand registrar factories (parity with TS/Rust embedder API).
|
|
146
157
|
"register_list_command",
|
|
@@ -164,6 +175,11 @@ __all__ = [
|
|
|
164
175
|
"ConfigDecryptionError",
|
|
165
176
|
"ModuleExecutionError",
|
|
166
177
|
"ModuleNotFoundError",
|
|
178
|
+
# Ref-resolver error hierarchy (parity with TS index.ts:82-84).
|
|
179
|
+
"RefResolverError",
|
|
180
|
+
"CircularRefError",
|
|
181
|
+
"MaxDepthExceededError",
|
|
182
|
+
"UnresolvableRefError",
|
|
167
183
|
# Deprecated alias kept for backward compat — prefer ModuleNotFoundError.
|
|
168
184
|
# Will be removed in v0.10.0.
|
|
169
185
|
"CliModuleNotFoundError",
|
|
@@ -44,6 +44,33 @@ def _get_annotation(annotations: Any, key: str, default: Any = None) -> Any:
|
|
|
44
44
|
return getattr(annotations, key, default)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def _read_timeout_from_env() -> int | None:
|
|
48
|
+
"""Parse APCORE_CLI_APPROVAL_TIMEOUT, warn on malformed, return None on absent/invalid.
|
|
49
|
+
|
|
50
|
+
D11-012 cross-SDK parity: TS's `readTimeoutFromEnv()` reads the same
|
|
51
|
+
variable; Rust will be aligned in a follow-up. Documented precedence is
|
|
52
|
+
`CLI flag > env var > 60s default`.
|
|
53
|
+
"""
|
|
54
|
+
raw = os.environ.get("APCORE_CLI_APPROVAL_TIMEOUT")
|
|
55
|
+
if raw is None or raw == "":
|
|
56
|
+
return None
|
|
57
|
+
try:
|
|
58
|
+
parsed = int(raw)
|
|
59
|
+
except ValueError:
|
|
60
|
+
print(
|
|
61
|
+
f"Warning: APCORE_CLI_APPROVAL_TIMEOUT='{raw}' is not an integer. Ignoring.",
|
|
62
|
+
file=sys.stderr,
|
|
63
|
+
)
|
|
64
|
+
return None
|
|
65
|
+
if parsed <= 0:
|
|
66
|
+
print(
|
|
67
|
+
f"Warning: APCORE_CLI_APPROVAL_TIMEOUT='{raw}' must be > 0. Ignoring.",
|
|
68
|
+
file=sys.stderr,
|
|
69
|
+
)
|
|
70
|
+
return None
|
|
71
|
+
return parsed
|
|
72
|
+
|
|
73
|
+
|
|
47
74
|
# ---------------------------------------------------------------------------
|
|
48
75
|
# CliApprovalHandler — implements apcore ApprovalHandler protocol (FE-11 §3.5)
|
|
49
76
|
# ---------------------------------------------------------------------------
|
|
@@ -59,8 +86,13 @@ class CliApprovalHandler:
|
|
|
59
86
|
Pass to Executor via ``executor.set_approval_handler(handler)``.
|
|
60
87
|
"""
|
|
61
88
|
|
|
62
|
-
def __init__(self, auto_approve: bool = False, timeout: int =
|
|
89
|
+
def __init__(self, auto_approve: bool = False, timeout: int | None = None) -> None:
|
|
63
90
|
self.auto_approve = auto_approve
|
|
91
|
+
# D11-012: default-timeout resolution honors APCORE_CLI_APPROVAL_TIMEOUT
|
|
92
|
+
# env var when caller did not pass an explicit timeout. Precedence:
|
|
93
|
+
# constructor arg > env var > 60s default. Matches TS readTimeoutFromEnv.
|
|
94
|
+
if timeout is None:
|
|
95
|
+
timeout = _read_timeout_from_env() or 60
|
|
64
96
|
self.timeout = max(1, min(timeout, 3600))
|
|
65
97
|
|
|
66
98
|
async def request_approval(self, request: Any) -> Any:
|
|
@@ -72,6 +104,20 @@ class CliApprovalHandler:
|
|
|
72
104
|
"""
|
|
73
105
|
module_id = getattr(request, "module_id", "unknown")
|
|
74
106
|
|
|
107
|
+
# D11-014: defense-in-depth — short-circuit when the request explicitly
|
|
108
|
+
# declares it does not require approval. Matches Rust approval.rs:421
|
|
109
|
+
# (`if !get_requires_approval(module_def) { return Approved::not_required }`).
|
|
110
|
+
requires_attr = getattr(request, "requires_approval", None)
|
|
111
|
+
if requires_attr is False:
|
|
112
|
+
return {"status": "approved", "approved_by": "not_required"}
|
|
113
|
+
module_def = getattr(request, "module_def", None)
|
|
114
|
+
if module_def is not None:
|
|
115
|
+
annotations = getattr(module_def, "annotations", None)
|
|
116
|
+
if annotations is not None:
|
|
117
|
+
requires = _get_annotation(annotations, "requires_approval", None)
|
|
118
|
+
if requires is False:
|
|
119
|
+
return {"status": "approved", "approved_by": "not_required"}
|
|
120
|
+
|
|
75
121
|
# Bypass: auto_approve flag
|
|
76
122
|
if self.auto_approve:
|
|
77
123
|
logger.info("Approval bypassed via --yes flag for module '%s'.", module_id)
|
|
@@ -130,7 +176,7 @@ class CliApprovalHandler:
|
|
|
130
176
|
# ---------------------------------------------------------------------------
|
|
131
177
|
|
|
132
178
|
|
|
133
|
-
def check_approval(module_def: Any, auto_approve: bool, timeout: int =
|
|
179
|
+
def check_approval(module_def: Any, auto_approve: bool, timeout: int | None = None) -> None:
|
|
134
180
|
"""Check if module requires approval and handle accordingly.
|
|
135
181
|
|
|
136
182
|
Returns None if approved (or approval not required).
|
|
@@ -155,6 +201,10 @@ def check_approval(module_def: Any, auto_approve: bool, timeout: int = 60) -> No
|
|
|
155
201
|
if requires is not True:
|
|
156
202
|
return
|
|
157
203
|
|
|
204
|
+
# D11-012: resolve timeout precedence — explicit arg > APCORE_CLI_APPROVAL_TIMEOUT env > 60s.
|
|
205
|
+
if timeout is None:
|
|
206
|
+
timeout = _read_timeout_from_env() or 60
|
|
207
|
+
|
|
158
208
|
module_id = getattr(module_def, "module_id", getattr(module_def, "canonical_id", "unknown"))
|
|
159
209
|
|
|
160
210
|
# Bypass: --yes flag (highest priority)
|
|
@@ -46,14 +46,26 @@ logger = logging.getLogger("apcore_cli.cli")
|
|
|
46
46
|
# Module-level audit logger, set during CLI init
|
|
47
47
|
_audit_logger: AuditLogger | None = None
|
|
48
48
|
|
|
49
|
-
# Module-level
|
|
50
|
-
|
|
49
|
+
# Module-level all-options help flag, set during CLI init
|
|
50
|
+
_all_options_help: bool = False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def set_all_options_help(all_options: bool) -> None:
|
|
54
|
+
"""Set the all-options help flag. When False, built-in options are hidden.
|
|
55
|
+
|
|
56
|
+
Controls the ``--all-options`` flag introduced in FR-DISP-007 (v0.9.0).
|
|
57
|
+
"""
|
|
58
|
+
global _all_options_help
|
|
59
|
+
_all_options_help = all_options
|
|
51
60
|
|
|
52
61
|
|
|
53
62
|
def set_verbose_help(verbose: bool) -> None:
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
"""Deprecated alias for :func:`set_all_options_help`.
|
|
64
|
+
|
|
65
|
+
The name ``set_verbose_help`` is kept for backward compatibility.
|
|
66
|
+
Use ``set_all_options_help`` in new code.
|
|
67
|
+
"""
|
|
68
|
+
set_all_options_help(verbose)
|
|
57
69
|
|
|
58
70
|
|
|
59
71
|
# Module-level docs URL, set by downstream projects
|
|
@@ -411,7 +423,7 @@ class GroupedModuleGroup(LazyModuleGroup):
|
|
|
411
423
|
# Footer hints for discoverability
|
|
412
424
|
formatter.write_paragraph()
|
|
413
425
|
formatter.write(
|
|
414
|
-
"Use --help --
|
|
426
|
+
"Use --help --all-options to show all options (including built-in options).\n"
|
|
415
427
|
"Use --help --man to display a formatted man page."
|
|
416
428
|
)
|
|
417
429
|
|
|
@@ -807,8 +819,8 @@ def build_module_command(
|
|
|
807
819
|
|
|
808
820
|
# Build the command with schema-generated options + built-in options
|
|
809
821
|
_epilog_parts: list[str] = []
|
|
810
|
-
if not
|
|
811
|
-
_epilog_parts.append("Use --
|
|
822
|
+
if not _all_options_help:
|
|
823
|
+
_epilog_parts.append("Use --all-options to show all options (including built-in options).")
|
|
812
824
|
if _docs_url:
|
|
813
825
|
_epilog_parts.append(f"Docs: {_docs_url}/commands/{effective_cmd_name}")
|
|
814
826
|
_epilog = "\n".join(_epilog_parts) if _epilog_parts else None
|
|
@@ -819,8 +831,8 @@ def build_module_command(
|
|
|
819
831
|
epilog=_epilog,
|
|
820
832
|
)
|
|
821
833
|
|
|
822
|
-
# Add built-in options (hidden unless --
|
|
823
|
-
_hide = not
|
|
834
|
+
# Add built-in options (hidden unless --all-options is passed with --help)
|
|
835
|
+
_hide = not _all_options_help
|
|
824
836
|
cmd.params.append(
|
|
825
837
|
click.Option(
|
|
826
838
|
["--input"],
|
|
@@ -936,7 +948,7 @@ def build_module_command(
|
|
|
936
948
|
"format",
|
|
937
949
|
"fields",
|
|
938
950
|
"sandbox",
|
|
939
|
-
"
|
|
951
|
+
"all_options",
|
|
940
952
|
"dry_run",
|
|
941
953
|
"trace",
|
|
942
954
|
"stream",
|
|
@@ -371,11 +371,17 @@ def register_exec_command(
|
|
|
371
371
|
|
|
372
372
|
audit_start = time.monotonic()
|
|
373
373
|
try:
|
|
374
|
-
|
|
375
|
-
|
|
374
|
+
# D11-012: pass approval_timeout through unchanged; check_approval
|
|
375
|
+
# internally resolves explicit arg > APCORE_CLI_APPROVAL_TIMEOUT env > 60s.
|
|
376
|
+
check_approval(module_def, auto_approve=auto_approve, timeout=approval_timeout)
|
|
376
377
|
|
|
377
378
|
if dry_run:
|
|
378
|
-
|
|
379
|
+
# D11-013: defensive guard mirroring TS discovery.ts:334. Some
|
|
380
|
+
# embedder-provided Executor subclasses lack `validate`; emit a
|
|
381
|
+
# synthetic `{valid: True}` preflight instead of crashing with
|
|
382
|
+
# AttributeError. Rust uses build_preflight_result for the same
|
|
383
|
+
# synthetic fallback.
|
|
384
|
+
preflight = executor.validate(module_id, merged) if hasattr(executor, "validate") else {"valid": True}
|
|
379
385
|
format_preflight_result(preflight, output_format)
|
|
380
386
|
return
|
|
381
387
|
|
|
@@ -40,11 +40,9 @@ EXIT_CONFIG_BIND_ERROR = 65
|
|
|
40
40
|
EXIT_CONFIG_MOUNT_ERROR = 66
|
|
41
41
|
EXIT_ERROR_FORMATTER_DUPLICATE = 70
|
|
42
42
|
EXIT_ACL_DENIED = 77
|
|
43
|
-
#
|
|
43
|
+
# Both namespace errors share exit code 78 per protocol spec.
|
|
44
44
|
EXIT_CONFIG_NAMESPACE_RESERVED = 78
|
|
45
45
|
EXIT_CONFIG_NAMESPACE_DUPLICATE = 78
|
|
46
|
-
EXIT_CONFIG_ENV_PREFIX_CONFLICT = 78
|
|
47
|
-
EXIT_CONFIG_ENV_MAP_CONFLICT = 78
|
|
48
46
|
EXIT_SIGINT = 130
|
|
49
47
|
|
|
50
48
|
# ---------------------------------------------------------------------------
|
|
@@ -55,10 +55,10 @@ logger = logging.getLogger("apcore_cli")
|
|
|
55
55
|
EXIT_CONFIG_NOT_FOUND = 47
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
"""Check if --
|
|
58
|
+
def _has_all_options_flag(argv: list[str] | None = None) -> bool:
|
|
59
|
+
"""Check if --all-options is present in argv (pre-parse, before Click)."""
|
|
60
60
|
args = argv if argv is not None else sys.argv[1:]
|
|
61
|
-
return "--
|
|
61
|
+
return "--all-options" in args
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
def create_cli(
|
|
@@ -166,9 +166,9 @@ def create_cli(
|
|
|
166
166
|
if prog_name is None:
|
|
167
167
|
prog_name = os.path.basename(sys.argv[0]) or "apcore-cli"
|
|
168
168
|
|
|
169
|
-
# Pre-parse --
|
|
169
|
+
# Pre-parse --all-options before Click runs so build_module_command knows
|
|
170
170
|
# whether to hide built-in options.
|
|
171
|
-
verbose =
|
|
171
|
+
verbose = _has_all_options_flag()
|
|
172
172
|
set_verbose_help(verbose)
|
|
173
173
|
|
|
174
174
|
# Resolve CLI log level (3-tier precedence, evaluated before Click runs):
|
|
@@ -423,8 +423,8 @@ def create_cli(
|
|
|
423
423
|
help="Log verbosity. Overrides APCORE_CLI_LOGGING_LEVEL and APCORE_LOGGING_LEVEL env vars.",
|
|
424
424
|
)
|
|
425
425
|
@click.option(
|
|
426
|
-
"--
|
|
427
|
-
"
|
|
426
|
+
"--all-options",
|
|
427
|
+
"all_options_help",
|
|
428
428
|
is_flag=True,
|
|
429
429
|
default=False,
|
|
430
430
|
help="Show all options in help output (including built-in options).",
|
|
@@ -433,7 +433,7 @@ def create_cli(
|
|
|
433
433
|
def cli(
|
|
434
434
|
ctx: click.Context,
|
|
435
435
|
log_level: str | None = None,
|
|
436
|
-
|
|
436
|
+
all_options_help: bool = False,
|
|
437
437
|
**_discovery_opts: Any, # --extensions-dir/--commands-dir/--binding when standalone
|
|
438
438
|
) -> None:
|
|
439
439
|
if log_level is not None:
|
|
@@ -443,7 +443,7 @@ def create_cli(
|
|
|
443
443
|
logging.getLogger("apcore").setLevel(apcore_level)
|
|
444
444
|
ctx.ensure_object(dict)
|
|
445
445
|
ctx.obj["extensions_dir"] = ext_dir
|
|
446
|
-
ctx.obj["
|
|
446
|
+
ctx.obj["all_options_help"] = all_options_help
|
|
447
447
|
ctx.obj["exposure_filter"] = exposure_filter
|
|
448
448
|
|
|
449
449
|
# FE-13 §4.1 / FR-13-13: --extensions-dir, --commands-dir, --binding are
|
|
@@ -457,7 +457,7 @@ def create_cli(
|
|
|
457
457
|
click.Option(
|
|
458
458
|
["--extensions-dir", "extensions_dir_opt"],
|
|
459
459
|
default=None,
|
|
460
|
-
help="Path to
|
|
460
|
+
help="Path to extensions directory.",
|
|
461
461
|
),
|
|
462
462
|
click.Option(
|
|
463
463
|
["--commands-dir", "commands_dir_opt"],
|