Skip to content

Commit 53ee43d

Browse files
committed
feat: implement module exposure filtering and update AuthProvider to support lazy ConfigEncryptor injection
1 parent 34b0e03 commit 53ee43d

26 files changed

+1542
-397
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88

9+
## [0.7.0] - 2026-04-12
10+
11+
### Added
12+
13+
- **FE-12: Module Exposure Filtering** — Declarative control over which discovered modules are exposed as CLI commands.
14+
- `ExposureFilter` class in `exposure.py` with `is_exposed(module_id)` and `filter_modules(ids)` methods.
15+
- Three modes: `all` (default), `include` (whitelist), `exclude` (blacklist) with glob-pattern matching.
16+
- `ExposureFilter.from_config(dict)` classmethod for loading from `apcore.yaml` `expose` section.
17+
- `create_cli(expose=...)` parameter accepting `dict` or `ExposureFilter` instance.
18+
- `list --exposure {exposed,hidden,all}` filter flag in discovery commands.
19+
- `GroupedModuleGroup._build_group_map()` integration: calls `ExposureFilter.is_exposed()` to filter command registration.
20+
- `ConfigResolver` gains `expose.*` config keys.
21+
- 4-tier config precedence: `CliConfig.expose` > `--expose-mode` CLI flag > env var > `apcore.yaml`.
22+
- Hidden modules remain invocable via `exec <module_id>`.
23+
- New file: `exposure.py`.
24+
25+
---
26+
927
## [0.6.0] - 2026-04-06
1028

1129
### Changed

CLAUDE.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,28 @@
2727
## Environment
2828

2929
- Python >= 3.11
30-
- Key dependencies: click >= 8.0, rich >= 13.0, jsonschema >= 4.0, pyyaml >= 6.0
30+
- Key dependencies: click >= 8.1, rich >= 13.0, jsonschema >= 4.20, pyyaml >= 6.0, keyring >= 24, cryptography >= 41
31+
- Runtime: apcore >= 0.17.1 (v0.6.0 bump, was 0.15.1)
32+
- Optional: apcore-toolkit >= 0.4 (install via `pip install apcore-cli[toolkit]`)
33+
- Dev: pytest, pytest-asyncio, pytest-cov, mypy, ruff
34+
35+
## v0.6.0 Conventions
36+
37+
- Public surface (`__init__.py`): minimal — `__version__`, `create_cli`, `ExposureFilter`,
38+
plus re-exported error classes (AuthenticationError, ConfigDecryptionError,
39+
ModuleExecutionError, ApprovalTimeoutError, ApprovalDeniedError). All other symbols
40+
must be imported via full submodule path (e.g., `from apcore_cli.cli import GroupedModuleGroup`).
41+
- ExposureFilter + `expose=` kwarg on create_cli (FE-12).
42+
- `extra_commands=[...]` kwarg on create_cli as the FE-11 extension point (with
43+
collision detection against BUILTIN_COMMANDS).
44+
- Default click Group class is `GroupedModuleGroup` (multi-level grouping since v0.3.0).
45+
- `system_cmd` module registers runtime system commands (health/usage/enable/disable/
46+
reload/config) — FE-11.
47+
- `strategy` module registers describe-pipeline + --strategy flag — FE-11.
48+
- `validate` module + --dry-run flag — FE-11.
49+
- `CliApprovalHandler` async protocol (request_approval/check_approval) — FE-11 §3.5.1.
50+
- Config Bus namespace registration at package import time (apcore >= 0.15.0).
51+
- New env vars (v0.6.0): APCORE_CLI_APPROVAL_TIMEOUT, APCORE_CLI_STRATEGY, APCORE_CLI_GROUP_DEPTH.
52+
- New config keys (v0.6.0): cli.approval_timeout, cli.strategy, cli.group_depth.
53+
- New exit codes from apcore 0.17.1: CONFIG_BIND_ERROR (65), CONFIG_MOUNT_ERROR (66),
54+
ERROR_FORMATTER_DUPLICATE (70), CONFIG_NAMESPACE_* (78).

LICENSE

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Apache License
2+
Version 2.0, January 2004
3+
http://www.apache.org/licenses/
4+
5+
Copyright 2024 aiperceivable <tercel.yi@gmail.com>
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.

README.md

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
88

99
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
1010
[![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://python.org)
11-
[![Tests](https://img.shields.io/badge/tests-319%2B%20passed-brightgreen.svg)]()
11+
[![Tests](https://img.shields.io/badge/tests-394%2B%20passed-brightgreen.svg)]()
1212

1313
| | |
1414
|---|---|
@@ -48,7 +48,7 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
4848
pip install apcore-cli
4949
```
5050

51-
Requires Python 3.11+ and `apcore >= 0.14.0`.
51+
Requires Python 3.11+ and `apcore >= 0.17.1`.
5252

5353
## Quick Start
5454

@@ -115,6 +115,44 @@ cli(standalone_mode=True)
115115
cli = create_cli(registry=registry, executor=executor, prog_name="myapp")
116116
```
117117

118+
#### Python API
119+
120+
The `apcore_cli` package exposes three public symbols:
121+
122+
| Export | Description |
123+
|--------|-------------|
124+
| `__version__` | Package version string |
125+
| `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 8-parameter signature (`extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `expose`). |
126+
| `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
127+
128+
**Exposure filtering** — restrict which modules are visible at the CLI layer without touching the underlying registry:
129+
130+
```python
131+
from apcore_cli import create_cli, ExposureFilter
132+
133+
# Only expose admin.* modules
134+
cli = create_cli(
135+
registry=registry,
136+
executor=executor,
137+
expose=ExposureFilter(mode="include", include=["admin.*"]),
138+
)
139+
140+
# Or pass a config dict (equivalent)
141+
cli = create_cli(
142+
registry=registry,
143+
executor=executor,
144+
expose={"mode": "exclude", "exclude": ["debug.*", "test.*"]},
145+
)
146+
```
147+
148+
`ExposureFilter` supports `mode="all"` (default), `"include"`, and `"exclude"`, plus `ExposureFilter.from_config(config)` for dict-based construction, `.is_exposed(module_id)`, and `.filter_modules(module_ids)`.
149+
150+
**Injecting custom commands** — pass `extra_commands=[...]` to attach arbitrary Click commands alongside the auto-generated module commands (FE-11 extension point):
151+
152+
```python
153+
cli = create_cli(registry=registry, executor=executor, extra_commands=[my_custom_click_cmd])
154+
```
155+
118156
Or use the `LazyModuleGroup` directly with Click:
119157

120158
```python
@@ -224,12 +262,41 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
224262

225263
### Built-in Commands
226264

265+
apcore-cli ships with 14 built-in commands covering module invocation, runtime system management, workflow, and shell integration.
266+
267+
**Module invocation**
268+
269+
| Command | Description |
270+
|---------|-------------|
271+
| `exec <module_id>` | Execute a module (delegates to `Executor.call()`) |
272+
| `list` | List registered modules (supports `--tag`/`--search`/`--status`/`--annotation`/`--sort`/`--reverse`/`--deprecated`/`--deps` filters) |
273+
| `describe <module_id>` | Show full module metadata and schema |
274+
275+
**System management** (FE-11, v0.6.0)
276+
277+
| Command | Description |
278+
|---------|-------------|
279+
| `config get/set <key>` | Read or update runtime config values |
280+
| `health [<module_id>]` | Show module health status |
281+
| `usage [<module_id>]` | Show module usage statistics |
282+
| `enable <module_id>` | Enable a disabled module |
283+
| `disable <module_id>` | Disable a module at runtime |
284+
| `reload <module_id>` | Hot-reload a module from disk |
285+
286+
**Workflow**
287+
288+
| Command | Description |
289+
|---------|-------------|
290+
| `validate <module_id>` | Preflight-check a module without executing it (`--dry-run`) |
291+
| `describe-pipeline` | Show execution pipeline steps for a strategy (FE-11) |
292+
| `init module <module_id>` | Scaffold a new module (`--style decorator\|convention\|binding`) |
293+
294+
**Shell integration**
295+
227296
| Command | Description |
228297
|---------|-------------|
229-
| `list` | List available modules with optional tag filtering |
230-
| `describe <module_id>` | Show full module metadata and schemas |
231298
| `completion <shell>` | Generate shell completion script (bash/zsh/fish) |
232-
| `man <command>` | Generate man page in roff format |
299+
| `man <command>` | Generate roff man page for a command |
233300

234301
### Module Execution Options
235302

@@ -240,11 +307,20 @@ When executing a module (e.g. `apcore-cli math.add`), these built-in options are
240307
| `--input -` | Read JSON input from STDIN |
241308
| `--yes` / `-y` | Bypass approval prompts |
242309
| `--large-input` | Allow STDIN input larger than 10MB |
243-
| `--format` | Output format: `json` or `table` |
310+
| `--format` | Output format: `{json, table, csv, yaml, jsonl}` |
244311
| `--sandbox` | Run module in subprocess sandbox *(not yet implemented)* |
312+
| `--dry-run` | Run preflight checks without executing (FE-11, v0.6.0) |
313+
| `--trace` | Emit execution pipeline trace (v0.6.0) |
314+
| `--stream` | Stream results line-by-line for stream-capable modules (v0.6.0) |
315+
| `--strategy <name>` | Override execution strategy: `standard`/`internal`/`testing`/`performance`/`minimal` (v0.6.0) |
316+
| `--fields <csv>` | Select specific output fields using dot-path notation (v0.6.0) |
317+
| `--approval-timeout <seconds>` | Override approval timeout (default 60) (v0.6.0) |
318+
| `--approval-token <token>` | Provide a pre-obtained approval token (v0.6.0) |
245319

246320
Schema-generated flags (e.g. `--a`, `--b`) are added automatically from the module's `input_schema`.
247321

322+
The `list` command gained several discovery filters in v0.6.0: `--search`, `--status`, `--annotation`, `--sort`, `--reverse`, `--deprecated`, and `--deps`.
323+
248324
### Exit Codes
249325

250326
| Code | Meaning |
@@ -280,6 +356,9 @@ apcore-cli uses a 4-tier configuration precedence:
280356
| `APCORE_AUTH_API_KEY` | API key for remote registry authentication | *(unset)* |
281357
| `APCORE_CLI_SANDBOX` | Set to `1` to enable subprocess sandboxing | *(unset)* |
282358
| `APCORE_CLI_HELP_TEXT_MAX_LENGTH` | Maximum characters for CLI option help text before truncation | `1000` |
359+
| `APCORE_CLI_APPROVAL_TIMEOUT` | Approval-gate timeout in seconds (v0.6.0) | `60` |
360+
| `APCORE_CLI_STRATEGY` | Default execution strategy (v0.6.0) | `standard` |
361+
| `APCORE_CLI_GROUP_DEPTH` | Depth of automatic command grouping by `.` segments (v0.6.0) | `1` |
283362

284363
### Config File (`apcore.yaml`)
285364

@@ -292,6 +371,9 @@ sandbox:
292371
enabled: false
293372
cli:
294373
help_text_max_length: 1000
374+
approval_timeout: 60 # v0.6.0
375+
strategy: standard # v0.6.0
376+
group_depth: 1 # v0.6.0
295377
```
296378
297379
## Features
@@ -332,18 +414,24 @@ User / AI Agent (terminal)
332414
v
333415
apcore-cli (the adapter)
334416
|
335-
+-- ConfigResolver 4-tier config precedence
336-
+-- LazyModuleGroup Dynamic Click command generation
337-
+-- set_verbose_help Toggle built-in option visibility
338-
+-- set_docs_url Set base URL for online docs
339-
+-- build_program_man_page Full-program roff man page
340-
+-- configure_man_help Add --help --man support to any CLI
341-
+-- schema_parser JSON Schema -> Click options
342-
+-- ref_resolver $ref / allOf / anyOf / oneOf
343-
+-- approval TTY-aware HITL approval
344-
+-- output TTY-adaptive JSON/table output
345-
+-- AuditLogger JSON Lines execution logging
346-
+-- Sandbox Subprocess isolation
417+
+-- ConfigResolver 4-tier config precedence
418+
+-- GroupedModuleGroup (default) Multi-level command grouping (wraps LazyModuleGroup)
419+
+-- LazyModuleGroup Lazy per-module Click command construction (base class)
420+
+-- ExposureFilter Declarative module exposure filtering (FE-12)
421+
+-- CliApprovalHandler Async approval handler protocol implementation (FE-11)
422+
+-- system_cmd Runtime system-management (health/usage/enable/disable/reload/config)
423+
+-- strategy Execution strategy dispatch (--strategy flag and describe-pipeline)
424+
+-- init_cmd Module scaffolding (init subcommand)
425+
+-- set_verbose_help Toggle built-in option visibility
426+
+-- set_docs_url Set base URL for online docs
427+
+-- build_program_man_page Full-program roff man page
428+
+-- configure_man_help Add --help --man support to any CLI
429+
+-- schema_parser JSON Schema -> Click options
430+
+-- ref_resolver $ref / allOf / anyOf / oneOf
431+
+-- approval TTY-aware HITL approval
432+
+-- output TTY-adaptive JSON/table output
433+
+-- AuditLogger JSON Lines execution logging
434+
+-- Sandbox Subprocess isolation
347435
|
348436
v
349437
apcore Registry + Executor (your modules, unchanged)
@@ -436,7 +524,7 @@ apcore-cli --extensions-dir ./extensions greet.hello --name Alice --greeting Hi
436524
git clone https://github.com/aiperceivable/apcore-cli-python.git
437525
cd apcore-cli-python
438526
pip install -e ".[dev]"
439-
pytest # 319+ tests
527+
pytest # 394+ tests
440528
pytest --cov # with coverage report
441529
bash examples/run_examples.sh # run all examples
442530
```

planning/approval-gate.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
- `test_env_var_not_one_warns`: `APCORE_CLI_AUTO_APPROVE=true` → WARNING logged, NOT bypassed.
2525
- `test_yes_flag_priority_over_env`: Both `--yes` and env var set → `--yes` logged as bypass source.
2626

27-
**GREEN** — Implement `check_approval(module_def, auto_approve, ctx)` with bypass logic.
27+
**GREEN** — Implement `check_approval(module_def, auto_approve, timeout=60)` with bypass logic.
28+
29+
> Note: Signature updated v0.2.0 (dropped `ctx`), v0.6.0 (added `timeout`).
2830
2931
**REFACTOR** — None expected.
3032

0 commit comments

Comments
 (0)