apcore-cli 0.1.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.1.0/.github/CODEOWNERS +3 -0
- apcore_cli-0.1.0/.github/copilot-ignore +13 -0
- apcore_cli-0.1.0/.github/workflows/ci.yml +46 -0
- apcore_cli-0.1.0/.gitignore +18 -0
- apcore_cli-0.1.0/.gitmessage +60 -0
- apcore_cli-0.1.0/.pre-commit-config.yaml +27 -0
- apcore_cli-0.1.0/CHANGELOG.md +37 -0
- apcore_cli-0.1.0/PKG-INFO +459 -0
- apcore_cli-0.1.0/README.md +422 -0
- apcore_cli-0.1.0/examples/extensions/math/add.py +23 -0
- apcore_cli-0.1.0/examples/extensions/math/multiply.py +23 -0
- apcore_cli-0.1.0/examples/extensions/sysutil/disk.py +47 -0
- apcore_cli-0.1.0/examples/extensions/sysutil/env.py +30 -0
- apcore_cli-0.1.0/examples/extensions/sysutil/info.py +40 -0
- apcore_cli-0.1.0/examples/extensions/text/reverse.py +22 -0
- apcore_cli-0.1.0/examples/extensions/text/upper.py +22 -0
- apcore_cli-0.1.0/examples/extensions/text/wordcount.py +29 -0
- apcore_cli-0.1.0/examples/run_examples.sh +111 -0
- apcore_cli-0.1.0/planning/approval-gate.md +95 -0
- apcore_cli-0.1.0/planning/config-resolver.md +90 -0
- apcore_cli-0.1.0/planning/core-dispatcher.md +148 -0
- apcore_cli-0.1.0/planning/discovery.md +81 -0
- apcore_cli-0.1.0/planning/output-formatter.md +100 -0
- apcore_cli-0.1.0/planning/overview.md +85 -0
- apcore_cli-0.1.0/planning/schema-parser.md +164 -0
- apcore_cli-0.1.0/planning/security-manager.md +126 -0
- apcore_cli-0.1.0/planning/shell-integration.md +92 -0
- apcore_cli-0.1.0/planning/state.json +65 -0
- apcore_cli-0.1.0/pyproject.toml +85 -0
- apcore_cli-0.1.0/src/apcore_cli/__init__.py +3 -0
- apcore_cli-0.1.0/src/apcore_cli/__main__.py +142 -0
- apcore_cli-0.1.0/src/apcore_cli/_sandbox_runner.py +25 -0
- apcore_cli-0.1.0/src/apcore_cli/approval.py +167 -0
- apcore_cli-0.1.0/src/apcore_cli/cli.py +315 -0
- apcore_cli-0.1.0/src/apcore_cli/config.py +94 -0
- apcore_cli-0.1.0/src/apcore_cli/discovery.py +75 -0
- apcore_cli-0.1.0/src/apcore_cli/output.py +190 -0
- apcore_cli-0.1.0/src/apcore_cli/ref_resolver.py +113 -0
- apcore_cli-0.1.0/src/apcore_cli/schema_parser.py +168 -0
- apcore_cli-0.1.0/src/apcore_cli/security/__init__.py +8 -0
- apcore_cli-0.1.0/src/apcore_cli/security/audit.py +53 -0
- apcore_cli-0.1.0/src/apcore_cli/security/auth.py +37 -0
- apcore_cli-0.1.0/src/apcore_cli/security/config_encryptor.py +94 -0
- apcore_cli-0.1.0/src/apcore_cli/security/sandbox.py +60 -0
- apcore_cli-0.1.0/src/apcore_cli/shell.py +185 -0
- apcore_cli-0.1.0/tests/__init__.py +0 -0
- apcore_cli-0.1.0/tests/conftest.py +28 -0
- apcore_cli-0.1.0/tests/test_approval.py +173 -0
- apcore_cli-0.1.0/tests/test_bugfixes.py +150 -0
- apcore_cli-0.1.0/tests/test_cli.py +370 -0
- apcore_cli-0.1.0/tests/test_config.py +141 -0
- apcore_cli-0.1.0/tests/test_discovery.py +148 -0
- apcore_cli-0.1.0/tests/test_e2e.py +468 -0
- apcore_cli-0.1.0/tests/test_integration.py +287 -0
- apcore_cli-0.1.0/tests/test_output.py +195 -0
- apcore_cli-0.1.0/tests/test_ref_resolver.py +191 -0
- apcore_cli-0.1.0/tests/test_schema_parser.py +224 -0
- apcore_cli-0.1.0/tests/test_security/__init__.py +0 -0
- apcore_cli-0.1.0/tests/test_security/test_audit.py +78 -0
- apcore_cli-0.1.0/tests/test_security/test_auth.py +62 -0
- apcore_cli-0.1.0/tests/test_security/test_config_encryptor.py +56 -0
- apcore_cli-0.1.0/tests/test_security/test_sandbox.py +50 -0
- apcore_cli-0.1.0/tests/test_shell.py +126 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ "main" ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ "main" ]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: Test on Python ${{ matrix.python-version }}
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
# container:
|
|
17
|
+
# image: python:${{ matrix.python-version }}
|
|
18
|
+
strategy:
|
|
19
|
+
fail-fast: false
|
|
20
|
+
matrix:
|
|
21
|
+
# Pin CI to a single, consistent Python version to reduce version-related
|
|
22
|
+
# test flakiness. Use 3.12 for CI stability; expand matrix later if desired.
|
|
23
|
+
python-version: ["3.12"]
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
27
|
+
|
|
28
|
+
- name: Set up Python
|
|
29
|
+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
30
|
+
with:
|
|
31
|
+
python-version: ${{ matrix.python-version }}
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v5
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: |
|
|
38
|
+
uv sync --extra dev
|
|
39
|
+
|
|
40
|
+
- name: Lint with Ruff
|
|
41
|
+
run: |
|
|
42
|
+
uv run ruff check src/ tests/
|
|
43
|
+
|
|
44
|
+
- name: Run tests
|
|
45
|
+
run: |
|
|
46
|
+
uv run pytest
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# <type>(<scope>): <subject>
|
|
2
|
+
#
|
|
3
|
+
# <body>
|
|
4
|
+
#
|
|
5
|
+
# <footer>
|
|
6
|
+
|
|
7
|
+
# Type must be one of the following:
|
|
8
|
+
# feat: A new feature
|
|
9
|
+
# fix: A bug fix
|
|
10
|
+
# docs: Documentation only changes
|
|
11
|
+
# style: Changes that do not affect the meaning of the code
|
|
12
|
+
# refactor: A code change that neither fixes a bug nor adds a feature
|
|
13
|
+
# perf: A code change that improves performance
|
|
14
|
+
# test: Adding missing tests or correcting existing tests
|
|
15
|
+
# chore: Changes to the build process or auxiliary tools
|
|
16
|
+
# ci: Changes to CI configuration files and scripts
|
|
17
|
+
|
|
18
|
+
# Scope is optional and can be anything specifying the place of the commit change.
|
|
19
|
+
# Examples: api, core, storage, cli, stdio, etc.
|
|
20
|
+
|
|
21
|
+
# Subject should be:
|
|
22
|
+
# - Use imperative, present tense: "change" not "changed" nor "changes"
|
|
23
|
+
# - Don't capitalize first letter
|
|
24
|
+
# - No dot (.) at the end
|
|
25
|
+
# - Maximum 72 characters
|
|
26
|
+
|
|
27
|
+
# Body should include:
|
|
28
|
+
# - Motivation for the change and contrast with previous behavior
|
|
29
|
+
# - What changed and why
|
|
30
|
+
# - Any breaking changes
|
|
31
|
+
|
|
32
|
+
# Footer should contain:
|
|
33
|
+
# - Breaking changes (start with BREAKING CHANGE:)
|
|
34
|
+
# - Issue references (Closes #123, Fixes #456)
|
|
35
|
+
|
|
36
|
+
# Examples:
|
|
37
|
+
# feat(stdio): add stdio executor for process execution
|
|
38
|
+
#
|
|
39
|
+
# Implement a new stdio executor that allows executing system commands
|
|
40
|
+
# and processes via stdin/stdout communication, similar to MCP stdio
|
|
41
|
+
# transport mode. This enables flexible task execution through shell
|
|
42
|
+
# commands and Python scripts.
|
|
43
|
+
#
|
|
44
|
+
# - Add StdioExecutor class with command execution support
|
|
45
|
+
# - Add system resource monitoring (CPU, memory, disk)
|
|
46
|
+
# - Support async process communication
|
|
47
|
+
# - Add comprehensive error handling and logging
|
|
48
|
+
#
|
|
49
|
+
# Closes #123
|
|
50
|
+
|
|
51
|
+
# refactor(core): extract shared types to core.types module
|
|
52
|
+
#
|
|
53
|
+
# Move common type definitions from various modules to a centralized
|
|
54
|
+
# core.types module to avoid circular dependencies and improve code
|
|
55
|
+
# organization.
|
|
56
|
+
#
|
|
57
|
+
# - Add TaskPreHook and TaskPostHook type aliases
|
|
58
|
+
# - Move TaskStatus enum to core.types
|
|
59
|
+
# - Update imports across affected modules
|
|
60
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
|
|
3
|
+
# apdev hooks
|
|
4
|
+
- repo: local
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-chars
|
|
7
|
+
name: apdev check-chars
|
|
8
|
+
entry: apdev check-chars
|
|
9
|
+
language: system
|
|
10
|
+
types_or: [text, python]
|
|
11
|
+
|
|
12
|
+
- id: check-imports
|
|
13
|
+
name: apdev check-imports
|
|
14
|
+
entry: apdev check-imports
|
|
15
|
+
language: system
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
always_run: true
|
|
18
|
+
|
|
19
|
+
# Ruff linter and formatter
|
|
20
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
21
|
+
rev: v0.8.4
|
|
22
|
+
hooks:
|
|
23
|
+
- id: ruff
|
|
24
|
+
args: [--fix]
|
|
25
|
+
files: ^(src|tests)/
|
|
26
|
+
- id: ruff-format
|
|
27
|
+
files: ^(src|tests)/
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to apcore-cli (Python SDK) will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-03-15
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `--sandbox` flag for subprocess-isolated module execution (FE-05)
|
|
12
|
+
- `ModuleExecutionError` exception class for sandbox failures
|
|
13
|
+
- Windows approval timeout support via `threading.Timer` + `ctypes` (FE-03)
|
|
14
|
+
- Approval timeout clamping to 1..3600 seconds range (FE-03)
|
|
15
|
+
- Tag format validation (`^[a-z][a-z0-9_-]*$`) in `list --tag` (FE-04)
|
|
16
|
+
- `cli.auto_approve` config key with `False` default (FE-07)
|
|
17
|
+
- Extensions directory readability check with exit code 47 (FE-01)
|
|
18
|
+
- Missing required property warning in schema parser (FE-02)
|
|
19
|
+
- DEBUG log `"Loading extensions from {path}"` before registry discovery (FE-01)
|
|
20
|
+
- `TYPE_CHECKING` imports for proper type annotations (`Registry`, `Executor`, `ModuleDescriptor`, `ConfigResolver`, `AuditLogger`)
|
|
21
|
+
- `_get_module_id()` helper for `canonical_id`/`module_id` resolution
|
|
22
|
+
- `APCORE_AUTH_API_KEY` and `APCORE_CLI_SANDBOX` to README environment variables table
|
|
23
|
+
- `--sandbox` to README module execution options table
|
|
24
|
+
- CHANGELOG.md
|
|
25
|
+
- Core Dispatcher (FE-01): `LazyModuleGroup`, `build_module_command`, `collect_input`, `validate_module_id`
|
|
26
|
+
- Schema Parser (FE-02): `schema_to_click_options`, `_map_type`, `_extract_help`, `reconvert_enum_values`
|
|
27
|
+
- Ref Resolver (FE-02): `resolve_refs`, `_resolve_node` with `$ref`, `allOf`, `anyOf`, `oneOf` support
|
|
28
|
+
- Config Resolver (FE-07): `ConfigResolver` with 4-tier precedence (CLI > Env > File > Default)
|
|
29
|
+
- Approval Gate (FE-03): `check_approval`, `_prompt_with_timeout` with TTY detection and Unix SIGALRM
|
|
30
|
+
- Discovery (FE-04): `list` and `describe` commands with tag filtering and TTY-adaptive output
|
|
31
|
+
- Output Formatter (FE-08): `format_module_list`, `format_module_detail`, `format_exec_result` with Rich rendering
|
|
32
|
+
- Security Manager (FE-05): `AuthProvider`, `ConfigEncryptor` (keyring + AES-256-GCM), `AuditLogger` (JSON Lines), `Sandbox` (subprocess isolation)
|
|
33
|
+
- Shell Integration (FE-06): bash/zsh/fish completion generators, roff man page generator
|
|
34
|
+
- 8 example modules: `math.add`, `math.multiply`, `text.upper`, `text.reverse`, `text.wordcount`, `sysutil.info`, `sysutil.env`, `sysutil.disk`
|
|
35
|
+
- 244 tests (unit, integration, end-to-end)
|
|
36
|
+
- CI workflow with pytest and coverage
|
|
37
|
+
- Pre-commit hooks configuration
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apcore-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Terminal adapter for apcore — execute AI-Perceivable modules from the command line
|
|
5
|
+
Project-URL: Homepage, https://aipartnerup.com
|
|
6
|
+
Project-URL: Repository, https://github.com/aipartnerup/apcore-cli-python
|
|
7
|
+
Project-URL: Documentation, https://github.com/aipartnerup/apcore-cli-python#readme
|
|
8
|
+
Project-URL: Issues, https://github.com/aipartnerup/apcore-cli-python/issues
|
|
9
|
+
Author-email: aipartnerup <team@aipartnerup.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: agent,ai,apcore,automation,cli,command-line,llm,shell,terminal,unix
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: apcore>=0.13.0
|
|
24
|
+
Requires-Dist: click>=8.1
|
|
25
|
+
Requires-Dist: cryptography>=41.0
|
|
26
|
+
Requires-Dist: jsonschema>=4.20
|
|
27
|
+
Requires-Dist: keyring>=24.0
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Requires-Dist: rich>=13.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: apdev[dev]>=0.2.3; extra == 'dev'
|
|
32
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<div align="center">
|
|
39
|
+
<img src="https://raw.githubusercontent.com/aipartnerup/apcore-cli/main/apcore-cli-logo.svg" alt="apcore-cli logo" width="200"/>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
# apcore-cli
|
|
43
|
+
|
|
44
|
+
Terminal adapter for apcore. Execute AI-Perceivable modules from the command line.
|
|
45
|
+
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[](https://python.org)
|
|
48
|
+
[]()
|
|
49
|
+
|
|
50
|
+
| | |
|
|
51
|
+
|---|---|
|
|
52
|
+
| **Python SDK** | [github.com/aipartnerup/apcore-cli-python](https://github.com/aipartnerup/apcore-cli-python) |
|
|
53
|
+
| **Spec repo** | [github.com/aipartnerup/apcore-cli](https://github.com/aipartnerup/apcore-cli) |
|
|
54
|
+
| **apcore core** | [github.com/aipartnerup/apcore](https://github.com/aipartnerup/apcore) |
|
|
55
|
+
|
|
56
|
+
**apcore-cli** turns any [apcore](https://github.com/aipartnerup/apcore)-based project into a fully featured CLI tool — with **zero code changes** to your existing modules.
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
┌──────────────────┐
|
|
60
|
+
│ django-apcore │ <- your existing apcore project (unchanged)
|
|
61
|
+
│ flask-apcore │
|
|
62
|
+
│ ... │
|
|
63
|
+
└────────┬─────────┘
|
|
64
|
+
│ extensions directory
|
|
65
|
+
v
|
|
66
|
+
┌──────────────────┐
|
|
67
|
+
│ apcore-cli │ <- just install & point to extensions dir
|
|
68
|
+
└───┬──────────┬───┘
|
|
69
|
+
│ │
|
|
70
|
+
v v
|
|
71
|
+
Terminal Unix
|
|
72
|
+
Commands Pipes
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Design Philosophy
|
|
76
|
+
|
|
77
|
+
- **Zero intrusion** -- your apcore project needs no code changes, no imports, no dependencies on apcore-cli
|
|
78
|
+
- **Zero configuration** -- point to an extensions directory, everything is auto-discovered
|
|
79
|
+
- **Pure adapter** -- apcore-cli reads from the apcore Registry; it never modifies your modules
|
|
80
|
+
- **Unix-native** -- JSON output for pipes, rich tables for terminals, STDIN input, shell completions
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install apcore-cli
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Requires Python 3.11+ and `apcore >= 0.13.0`.
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
### Try it now
|
|
93
|
+
|
|
94
|
+
The repo includes 8 example modules you can run immediately:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
git clone https://github.com/aipartnerup/apcore-cli-python.git
|
|
98
|
+
cd apcore-cli-python
|
|
99
|
+
pip install -e ".[dev]"
|
|
100
|
+
|
|
101
|
+
# Run a module
|
|
102
|
+
apcore-cli --extensions-dir examples/extensions math.add --a 5 --b 10
|
|
103
|
+
# {"sum": 15}
|
|
104
|
+
|
|
105
|
+
# List all modules
|
|
106
|
+
apcore-cli --extensions-dir examples/extensions list --format json
|
|
107
|
+
|
|
108
|
+
# Run all examples
|
|
109
|
+
bash examples/run_examples.sh
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
See [Examples](#examples) for the full list of example modules and usage patterns.
|
|
113
|
+
|
|
114
|
+
### Zero-code approach
|
|
115
|
+
|
|
116
|
+
If you already have an apcore-based project with an extensions directory:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Execute a module
|
|
120
|
+
apcore-cli --extensions-dir ./extensions math.add --a 42 --b 58
|
|
121
|
+
|
|
122
|
+
# Or set the env var once
|
|
123
|
+
export APCORE_EXTENSIONS_ROOT=./extensions
|
|
124
|
+
apcore-cli math.add --a 42 --b 58
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
All modules are auto-discovered. CLI flags are auto-generated from each module's JSON Schema.
|
|
128
|
+
|
|
129
|
+
### Programmatic approach (Python API)
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from apcore import Registry, Executor
|
|
133
|
+
from apcore_cli.__main__ import create_cli
|
|
134
|
+
|
|
135
|
+
# Build the CLI from your registry
|
|
136
|
+
cli = create_cli(extensions_dir="./extensions")
|
|
137
|
+
cli(standalone_mode=True)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Or use the `LazyModuleGroup` directly with Click:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
import click
|
|
144
|
+
from apcore import Registry, Executor
|
|
145
|
+
from apcore_cli.cli import LazyModuleGroup
|
|
146
|
+
|
|
147
|
+
registry = Registry(extensions_dir="./extensions")
|
|
148
|
+
registry.discover()
|
|
149
|
+
executor = Executor(registry)
|
|
150
|
+
|
|
151
|
+
@click.group(cls=LazyModuleGroup, registry=registry, executor=executor)
|
|
152
|
+
def cli():
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
cli()
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Integration with Existing Projects
|
|
159
|
+
|
|
160
|
+
### Typical apcore project structure
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
your-project/
|
|
164
|
+
├── extensions/ <- modules live here
|
|
165
|
+
│ ├── math/
|
|
166
|
+
│ │ └── add.py
|
|
167
|
+
│ ├── text/
|
|
168
|
+
│ │ └── upper.py
|
|
169
|
+
│ └── ...
|
|
170
|
+
├── your_app.py <- your existing code (untouched)
|
|
171
|
+
└── ...
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Adding CLI support
|
|
175
|
+
|
|
176
|
+
No changes to your project. Just install and run:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
pip install apcore-cli
|
|
180
|
+
apcore-cli --extensions-dir ./extensions list
|
|
181
|
+
apcore-cli --extensions-dir ./extensions math.add --a 5 --b 10
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### STDIN piping (Unix pipes)
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Pipe JSON input
|
|
188
|
+
echo '{"a": 100, "b": 200}' | apcore-cli math.add --input -
|
|
189
|
+
# {"sum": 300}
|
|
190
|
+
|
|
191
|
+
# CLI flags override STDIN values
|
|
192
|
+
echo '{"a": 1, "b": 2}' | apcore-cli math.add --input - --a 999
|
|
193
|
+
# {"sum": 1001}
|
|
194
|
+
|
|
195
|
+
# Chain with other tools
|
|
196
|
+
apcore-cli sysutil.info | jq '.os, .hostname'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## CLI Reference
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
apcore-cli [OPTIONS] COMMAND [ARGS]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Global Options
|
|
206
|
+
|
|
207
|
+
| Option | Default | Description |
|
|
208
|
+
|--------|---------|-------------|
|
|
209
|
+
| `--extensions-dir` | `./extensions` | Path to apcore extensions directory |
|
|
210
|
+
| `--log-level` | `INFO` | Logging: `DEBUG`, `INFO`, `WARN`, `ERROR` |
|
|
211
|
+
| `--version` | | Show version and exit |
|
|
212
|
+
| `--help` | | Show help and exit |
|
|
213
|
+
|
|
214
|
+
### Built-in Commands
|
|
215
|
+
|
|
216
|
+
| Command | Description |
|
|
217
|
+
|---------|-------------|
|
|
218
|
+
| `list` | List available modules with optional tag filtering |
|
|
219
|
+
| `describe <module_id>` | Show full module metadata and schemas |
|
|
220
|
+
| `completion <shell>` | Generate shell completion script (bash/zsh/fish) |
|
|
221
|
+
| `man <command>` | Generate man page in roff format |
|
|
222
|
+
|
|
223
|
+
### Module Execution Options
|
|
224
|
+
|
|
225
|
+
When executing a module (e.g. `apcore-cli math.add`), these built-in options are always available:
|
|
226
|
+
|
|
227
|
+
| Option | Description |
|
|
228
|
+
|--------|-------------|
|
|
229
|
+
| `--input -` | Read JSON input from STDIN |
|
|
230
|
+
| `--yes` / `-y` | Bypass approval prompts |
|
|
231
|
+
| `--large-input` | Allow STDIN input larger than 10MB |
|
|
232
|
+
| `--format` | Output format: `json` or `table` |
|
|
233
|
+
| `--sandbox` | Run module in subprocess sandbox |
|
|
234
|
+
|
|
235
|
+
Schema-generated flags (e.g. `--a`, `--b`) are added automatically from the module's `input_schema`.
|
|
236
|
+
|
|
237
|
+
### Exit Codes
|
|
238
|
+
|
|
239
|
+
| Code | Meaning |
|
|
240
|
+
|------|---------|
|
|
241
|
+
| `0` | Success |
|
|
242
|
+
| `1` | Module execution error |
|
|
243
|
+
| `2` | Invalid CLI input |
|
|
244
|
+
| `44` | Module not found / disabled / load error |
|
|
245
|
+
| `45` | Schema validation error |
|
|
246
|
+
| `46` | Approval denied or timed out |
|
|
247
|
+
| `47` | Configuration error |
|
|
248
|
+
| `48` | Schema circular reference |
|
|
249
|
+
| `77` | ACL denied |
|
|
250
|
+
| `130` | Execution cancelled (Ctrl+C) |
|
|
251
|
+
|
|
252
|
+
## Configuration
|
|
253
|
+
|
|
254
|
+
apcore-cli uses a 4-tier configuration precedence:
|
|
255
|
+
|
|
256
|
+
1. **CLI flag** (highest): `--extensions-dir ./custom`
|
|
257
|
+
2. **Environment variable**: `APCORE_EXTENSIONS_ROOT=./custom`
|
|
258
|
+
3. **Config file**: `apcore.yaml`
|
|
259
|
+
4. **Default** (lowest): `./extensions`
|
|
260
|
+
|
|
261
|
+
### Environment Variables
|
|
262
|
+
|
|
263
|
+
| Variable | Description | Default |
|
|
264
|
+
|----------|-------------|---------|
|
|
265
|
+
| `APCORE_EXTENSIONS_ROOT` | Path to extensions directory | `./extensions` |
|
|
266
|
+
| `APCORE_CLI_AUTO_APPROVE` | Set to `1` to bypass all approval prompts | *(unset)* |
|
|
267
|
+
| `APCORE_LOGGING_LEVEL` | Log level | `INFO` |
|
|
268
|
+
| `APCORE_AUTH_API_KEY` | API key for remote registry authentication | *(unset)* |
|
|
269
|
+
| `APCORE_CLI_SANDBOX` | Set to `1` to enable subprocess sandboxing | *(unset)* |
|
|
270
|
+
|
|
271
|
+
### Config File (`apcore.yaml`)
|
|
272
|
+
|
|
273
|
+
```yaml
|
|
274
|
+
extensions:
|
|
275
|
+
root: ./extensions
|
|
276
|
+
logging:
|
|
277
|
+
level: DEBUG
|
|
278
|
+
sandbox:
|
|
279
|
+
enabled: false
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Features
|
|
283
|
+
|
|
284
|
+
- **Auto-discovery** -- all modules in the extensions directory are found and exposed as CLI commands
|
|
285
|
+
- **Auto-generated flags** -- JSON Schema `input_schema` is converted to `--flag value` CLI options with type validation
|
|
286
|
+
- **Boolean flag pairs** -- `--verbose` / `--no-verbose` from `"type": "boolean"` schema properties
|
|
287
|
+
- **Enum choices** -- `"enum": ["json", "csv"]` becomes `--format json` with Click validation
|
|
288
|
+
- **STDIN piping** -- `--input -` reads JSON from STDIN, CLI flags override for duplicate keys
|
|
289
|
+
- **TTY-adaptive output** -- rich tables for terminals, JSON for pipes (configurable via `--format`)
|
|
290
|
+
- **Approval gate** -- TTY-aware HITL prompts for modules with `requires_approval: true`, with `--yes` bypass and 60s timeout
|
|
291
|
+
- **Schema validation** -- inputs validated against JSON Schema before execution, with `$ref`/`allOf`/`anyOf`/`oneOf` resolution
|
|
292
|
+
- **Security** -- API key auth (keyring + AES-256-GCM), append-only audit logging, subprocess sandboxing
|
|
293
|
+
- **Shell completions** -- `apcore-cli completion bash|zsh|fish` generates completion scripts with dynamic module ID completion
|
|
294
|
+
- **Man pages** -- `apcore-cli man <command>` generates roff-formatted man pages
|
|
295
|
+
- **Audit logging** -- all executions logged to `~/.apcore-cli/audit.jsonl` with SHA-256 input hashing
|
|
296
|
+
|
|
297
|
+
## How It Works
|
|
298
|
+
|
|
299
|
+
### Mapping: apcore to CLI
|
|
300
|
+
|
|
301
|
+
| apcore | CLI |
|
|
302
|
+
|--------|-----|
|
|
303
|
+
| `module_id` (`math.add`) | Command name (`apcore-cli math.add`) |
|
|
304
|
+
| `description` | `--help` text |
|
|
305
|
+
| `input_schema.properties` | CLI flags (`--a`, `--b`) |
|
|
306
|
+
| `input_schema.required` | Required flag enforcement |
|
|
307
|
+
| `annotations.requires_approval` | HITL approval prompt |
|
|
308
|
+
|
|
309
|
+
### Architecture
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
User / AI Agent (terminal)
|
|
313
|
+
|
|
|
314
|
+
v
|
|
315
|
+
apcore-cli (the adapter)
|
|
316
|
+
|
|
|
317
|
+
+-- ConfigResolver 4-tier config precedence
|
|
318
|
+
+-- LazyModuleGroup Dynamic Click command generation
|
|
319
|
+
+-- SchemaParser JSON Schema -> Click options
|
|
320
|
+
+-- RefResolver $ref / allOf / anyOf / oneOf
|
|
321
|
+
+-- ApprovalGate TTY-aware HITL approval
|
|
322
|
+
+-- OutputFormatter TTY-adaptive JSON/table output
|
|
323
|
+
+-- AuditLogger JSON Lines execution logging
|
|
324
|
+
+-- Sandbox Subprocess isolation
|
|
325
|
+
|
|
|
326
|
+
v
|
|
327
|
+
apcore Registry + Executor (your modules, unchanged)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Examples
|
|
331
|
+
|
|
332
|
+
The `examples/extensions/` directory contains 8 runnable modules:
|
|
333
|
+
|
|
334
|
+
| Module | Description | Usage |
|
|
335
|
+
|--------|-------------|-------|
|
|
336
|
+
| `math.add` | Add two integers | `apcore-cli math.add --a 5 --b 10` |
|
|
337
|
+
| `math.multiply` | Multiply two integers | `apcore-cli math.multiply --a 6 --b 7` |
|
|
338
|
+
| `text.upper` | Uppercase a string | `apcore-cli text.upper --text hello` |
|
|
339
|
+
| `text.reverse` | Reverse a string | `apcore-cli text.reverse --text abcdef` |
|
|
340
|
+
| `text.wordcount` | Count words/chars/lines | `apcore-cli text.wordcount --text "hello world"` |
|
|
341
|
+
| `sysutil.info` | OS, hostname, Python version | `apcore-cli sysutil.info` |
|
|
342
|
+
| `sysutil.env` | Read environment variables | `apcore-cli sysutil.env --name HOME` |
|
|
343
|
+
| `sysutil.disk` | Disk usage statistics | `apcore-cli sysutil.disk --path /` |
|
|
344
|
+
|
|
345
|
+
### Running examples
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Set extensions path (one time)
|
|
349
|
+
export APCORE_EXTENSIONS_ROOT=examples/extensions
|
|
350
|
+
|
|
351
|
+
# Execute modules
|
|
352
|
+
apcore-cli math.add --a 42 --b 58
|
|
353
|
+
apcore-cli text.upper --text "hello apcore"
|
|
354
|
+
apcore-cli sysutil.info
|
|
355
|
+
apcore-cli sysutil.disk --path /
|
|
356
|
+
|
|
357
|
+
# Discovery
|
|
358
|
+
apcore-cli list --format json
|
|
359
|
+
apcore-cli list --tag math --format json
|
|
360
|
+
apcore-cli describe math.add --format json
|
|
361
|
+
|
|
362
|
+
# STDIN piping
|
|
363
|
+
echo '{"a": 100, "b": 200}' | apcore-cli math.add --input -
|
|
364
|
+
|
|
365
|
+
# Shell completion
|
|
366
|
+
apcore-cli completion bash >> ~/.bashrc
|
|
367
|
+
apcore-cli completion zsh >> ~/.zshrc
|
|
368
|
+
apcore-cli completion fish > ~/.config/fish/completions/apcore-cli.fish
|
|
369
|
+
|
|
370
|
+
# Man pages
|
|
371
|
+
apcore-cli man list | man -l -
|
|
372
|
+
|
|
373
|
+
# Run all examples at once
|
|
374
|
+
bash examples/run_examples.sh
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Writing your own module
|
|
378
|
+
|
|
379
|
+
Create a Python file in your extensions directory:
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
# extensions/greet/hello.py
|
|
383
|
+
from pydantic import BaseModel
|
|
384
|
+
|
|
385
|
+
class Input(BaseModel):
|
|
386
|
+
name: str
|
|
387
|
+
greeting: str = "Hello"
|
|
388
|
+
|
|
389
|
+
class Output(BaseModel):
|
|
390
|
+
message: str
|
|
391
|
+
|
|
392
|
+
class GreetHello:
|
|
393
|
+
input_schema = Input
|
|
394
|
+
output_schema = Output
|
|
395
|
+
description = "Greet someone by name"
|
|
396
|
+
|
|
397
|
+
def execute(self, inputs, context=None):
|
|
398
|
+
return {"message": f"{inputs['greeting']}, {inputs['name']}!"}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Then run it:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
apcore-cli --extensions-dir ./extensions greet.hello --name World
|
|
405
|
+
# {"message": "Hello, World!"}
|
|
406
|
+
|
|
407
|
+
apcore-cli --extensions-dir ./extensions greet.hello --name Alice --greeting Hi
|
|
408
|
+
# {"message": "Hi, Alice!"}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Development
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
git clone https://github.com/aipartnerup/apcore-cli-python.git
|
|
415
|
+
cd apcore-cli-python
|
|
416
|
+
pip install -e ".[dev]"
|
|
417
|
+
pytest # 244 tests
|
|
418
|
+
pytest --cov # with coverage report
|
|
419
|
+
bash examples/run_examples.sh # run all examples
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Project Structure
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
src/apcore_cli/
|
|
426
|
+
├── __init__.py # Package version
|
|
427
|
+
├── __main__.py # CLI entry point, wiring
|
|
428
|
+
├── cli.py # LazyModuleGroup, build_module_command, collect_input
|
|
429
|
+
├── config.py # ConfigResolver (4-tier precedence)
|
|
430
|
+
├── schema_parser.py # JSON Schema -> Click options
|
|
431
|
+
├── ref_resolver.py # $ref / allOf / anyOf / oneOf resolution
|
|
432
|
+
├── output.py # TTY-adaptive output formatting (rich)
|
|
433
|
+
├── discovery.py # list / describe commands
|
|
434
|
+
├── approval.py # HITL approval gate with timeout
|
|
435
|
+
├── shell.py # bash/zsh/fish completion + man pages
|
|
436
|
+
├── _sandbox_runner.py # Subprocess entry point for sandboxed execution
|
|
437
|
+
└── security/
|
|
438
|
+
├── __init__.py # Exports
|
|
439
|
+
├── auth.py # API key authentication
|
|
440
|
+
├── config_encryptor.py # Keyring + AES-256-GCM encrypted config
|
|
441
|
+
├── audit.py # JSON Lines audit logging
|
|
442
|
+
└── sandbox.py # Subprocess-based execution isolation
|
|
443
|
+
|
|
444
|
+
examples/
|
|
445
|
+
├── run_examples.sh # Run all examples end-to-end
|
|
446
|
+
└── extensions/
|
|
447
|
+
├── math/ # math.add, math.multiply
|
|
448
|
+
├── text/ # text.upper, text.reverse, text.wordcount
|
|
449
|
+
└── sysutil/ # sysutil.info, sysutil.env, sysutil.disk
|
|
450
|
+
|
|
451
|
+
planning/ # Implementation plans (TDD task breakdowns)
|
|
452
|
+
├── overview.md
|
|
453
|
+
├── state.json
|
|
454
|
+
└── *.md # Per-feature plans
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## License
|
|
458
|
+
|
|
459
|
+
Apache-2.0
|