axor-cli 0.2.0__tar.gz → 0.3.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.
- axor_cli-0.3.0/CHANGELOG.md +40 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/PKG-INFO +2 -2
- axor_cli-0.3.0/axor_cli/_version.py +1 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/adapters.py +2 -2
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/auth.py +54 -6
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/main.py +1 -1
- {axor_cli-0.2.0 → axor_cli-0.3.0}/pyproject.toml +4 -4
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/unit/test_auth.py +37 -0
- axor_cli-0.2.0/CHANGELOG.md +0 -17
- axor_cli-0.2.0/axor_cli/_version.py +0 -1
- {axor_cli-0.2.0 → axor_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/.gitignore +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/CONTRIBUTING.md +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/LICENSE +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/README.md +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/__init__.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/display.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/streaming.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/axor_cli/telemetry.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/__init__.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/conftest.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/unit/test_adapters.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/unit/test_display.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/unit/test_smoke.py +0 -0
- {axor_cli-0.2.0 → axor_cli-0.3.0}/tests/unit/test_telemetry_bridge.py +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 — 2026-04-29
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `ConfigCorruptError` raised when `~/.axor/config.toml` exists but cannot
|
|
7
|
+
be parsed. The previous behaviour silently returned `{}` and let the
|
|
8
|
+
next save **overwrite the broken file**, dropping any other adapter's
|
|
9
|
+
saved key. Refusing to write preserves user data; the user is told to
|
|
10
|
+
fix or delete the file.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- TOML escaping made spec-compliant. The old version only escaped `\` and
|
|
14
|
+
`"`; pasting an API key that contained a newline (or any control byte)
|
|
15
|
+
produced a file that crashed the next `tomllib.load()`. Now handles
|
|
16
|
+
`\n`, `\r`, `\t`, `\b`, `\f`, plus a `\uXXXX` fallback for any other
|
|
17
|
+
control char.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Model registry refreshed: `claude-sonnet-4-6`, `claude-opus-4-7`,
|
|
21
|
+
`claude-haiku-4-5`. Default is `claude-sonnet-4-6`.
|
|
22
|
+
|
|
23
|
+
### Constraints
|
|
24
|
+
- Pin bump: `axor-core>=0.4.0,<0.5` (was `>=0.3.0`).
|
|
25
|
+
|
|
26
|
+
## 0.2.0 — 2026-04-24
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- `axor-telemetry` integration. New `/telemetry` slash command:
|
|
30
|
+
`status` / `on [--remote]` / `off` / `preview` / `consent`.
|
|
31
|
+
- One-time stderr opt-in banner. Marker file
|
|
32
|
+
`~/.axor/.telemetry_notice_shown` suppresses subsequent prints;
|
|
33
|
+
`AXOR_NO_BANNER=1` suppresses on-demand.
|
|
34
|
+
- `build_session` wires a `TelemetryPipeline` into `GovernedSession`.
|
|
35
|
+
- Optional `[telemetry]` extra: `pip install axor-cli[telemetry]`.
|
|
36
|
+
- 11 new bridge tests + smoke-test fix (44 total).
|
|
37
|
+
|
|
38
|
+
## 0.1.0 — 2026-04-14
|
|
39
|
+
|
|
40
|
+
Initial release of the `axor` CLI shell.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: axor-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: CLI for axor-core governance kernel — governed agent sessions in your terminal
|
|
5
5
|
Project-URL: Bug Tracker, https://github.com/Bucha11/axor-cli/issues
|
|
6
6
|
Project-URL: Changelog, https://github.com/Bucha11/axor-cli/releases
|
|
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries
|
|
18
18
|
Classifier: Topic :: Terminals
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
|
-
Requires-Dist: axor-core
|
|
20
|
+
Requires-Dist: axor-core<0.5,>=0.4.0
|
|
21
21
|
Provides-Extra: all
|
|
22
22
|
Requires-Dist: axor-claude>=0.1.0; extra == 'all'
|
|
23
23
|
Requires-Dist: axor-openai>=0.1.0; extra == 'all'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -17,8 +17,8 @@ _REGISTRY: dict[str, dict[str, Any]] = {
|
|
|
17
17
|
"module": "axor_claude",
|
|
18
18
|
"install": "pip install axor-claude",
|
|
19
19
|
"env_var": "ANTHROPIC_API_KEY",
|
|
20
|
-
"models": ["claude-sonnet-4-
|
|
21
|
-
"default_model": "claude-sonnet-4-
|
|
20
|
+
"models": ["claude-sonnet-4-6", "claude-opus-4-7", "claude-haiku-4-5"],
|
|
21
|
+
"default_model": "claude-sonnet-4-6",
|
|
22
22
|
},
|
|
23
23
|
"openai": {
|
|
24
24
|
"module": "axor_openai",
|
|
@@ -222,21 +222,69 @@ def _read_config_file() -> dict[str, Any]:
|
|
|
222
222
|
return tomllib.load(f) # type: ignore
|
|
223
223
|
|
|
224
224
|
|
|
225
|
+
class ConfigCorruptError(RuntimeError):
|
|
226
|
+
"""Raised when an existing config exists but cannot be parsed."""
|
|
227
|
+
|
|
228
|
+
|
|
225
229
|
def _load_existing_config() -> dict[str, Any]:
|
|
226
|
-
"""Load existing config
|
|
230
|
+
"""Load existing config.
|
|
231
|
+
|
|
232
|
+
- Returns an empty dict when the file does not exist (first-time setup).
|
|
233
|
+
- Returns an empty dict when tomllib is unavailable (best effort).
|
|
234
|
+
- Raises `ConfigCorruptError` when the file exists but cannot be parsed.
|
|
235
|
+
The previous behaviour (silently returning {}) caused `save_to_config`
|
|
236
|
+
to overwrite the broken file, dropping any *other* adapter's saved key.
|
|
237
|
+
Refusing to write preserves user data.
|
|
238
|
+
"""
|
|
227
239
|
if not CONFIG_FILE.exists() or tomllib is None:
|
|
228
240
|
return {}
|
|
229
|
-
|
|
241
|
+
|
|
230
242
|
try:
|
|
231
243
|
return _read_config_file()
|
|
232
244
|
except Exception as e:
|
|
233
|
-
logger.
|
|
234
|
-
|
|
245
|
+
logger.error(
|
|
246
|
+
"Refusing to overwrite unreadable config %s (%s). "
|
|
247
|
+
"Fix or delete the file before retrying.",
|
|
248
|
+
CONFIG_FILE, e,
|
|
249
|
+
)
|
|
250
|
+
raise ConfigCorruptError(str(e)) from e
|
|
235
251
|
|
|
236
252
|
|
|
237
253
|
def _escape_toml_value(val: str) -> str:
|
|
238
|
-
"""Escape a string for TOML double-quoted
|
|
239
|
-
|
|
254
|
+
"""Escape a string for a TOML basic (double-quoted) string.
|
|
255
|
+
|
|
256
|
+
Per the TOML spec, a basic string may contain `\\b`, `\\t`, `\\n`, `\\f`,
|
|
257
|
+
`\\r`, `\\"`, and `\\\\` literal escapes; any other control character
|
|
258
|
+
(U+0000–U+001F except those listed) must be `\\uXXXX`-escaped. Plain
|
|
259
|
+
`replace("\\\\", "\\\\\\\\").replace('"', '\\\\"')` drops a newline or
|
|
260
|
+
NUL into the TOML output as a literal byte, producing a file that
|
|
261
|
+
crashes the next `tomllib.load()`.
|
|
262
|
+
|
|
263
|
+
Pasted API keys generally do not contain newlines, but if a user pastes
|
|
264
|
+
"sk-...\\n" by accident the previous escape would brick the config.
|
|
265
|
+
"""
|
|
266
|
+
out: list[str] = []
|
|
267
|
+
for ch in val:
|
|
268
|
+
code = ord(ch)
|
|
269
|
+
if ch == "\\":
|
|
270
|
+
out.append("\\\\")
|
|
271
|
+
elif ch == '"':
|
|
272
|
+
out.append('\\"')
|
|
273
|
+
elif ch == "\n":
|
|
274
|
+
out.append("\\n")
|
|
275
|
+
elif ch == "\r":
|
|
276
|
+
out.append("\\r")
|
|
277
|
+
elif ch == "\t":
|
|
278
|
+
out.append("\\t")
|
|
279
|
+
elif ch == "\b":
|
|
280
|
+
out.append("\\b")
|
|
281
|
+
elif ch == "\f":
|
|
282
|
+
out.append("\\f")
|
|
283
|
+
elif code < 0x20 or code == 0x7F:
|
|
284
|
+
out.append(f"\\u{code:04X}")
|
|
285
|
+
else:
|
|
286
|
+
out.append(ch)
|
|
287
|
+
return "".join(out)
|
|
240
288
|
|
|
241
289
|
|
|
242
290
|
def _serialize_config_to_toml(data: dict[str, Any]) -> str:
|
|
@@ -8,7 +8,7 @@ Usage:
|
|
|
8
8
|
axor claude "refactor auth module" # single task and exit
|
|
9
9
|
axor claude --policy readonly # with preset policy
|
|
10
10
|
axor claude --limit 100000 # with soft token limit
|
|
11
|
-
axor claude --model claude-opus-4-
|
|
11
|
+
axor claude --model claude-opus-4-7 # specific model
|
|
12
12
|
axor --list-adapters # show available adapters
|
|
13
13
|
"""
|
|
14
14
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "axor-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "CLI for axor-core governance kernel — governed agent sessions in your terminal"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -22,10 +22,10 @@ classifiers = [
|
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
# axor-core required; adapters are optional — install what you need.
|
|
25
|
-
# >=0.
|
|
26
|
-
#
|
|
25
|
+
# >=0.4.0 because PolicyComposer federation invariant + keyword_relevance
|
|
26
|
+
# public API are used by the model/adapter wiring.
|
|
27
27
|
dependencies = [
|
|
28
|
-
"axor-core>=0.
|
|
28
|
+
"axor-core>=0.4.0,<0.5",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
[project.optional-dependencies]
|
|
@@ -127,3 +127,40 @@ def test_load_corrupted_file_returns_none(tmp_home):
|
|
|
127
127
|
auth_mod.CONFIG_FILE.write_text("not valid [[[ toml")
|
|
128
128
|
result = auth_mod.load_from_config("claude")
|
|
129
129
|
assert result is None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ── escape / corruption ──────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_key_with_newline_survives_roundtrip(tmp_home):
|
|
136
|
+
"""Regression: previously a `\\n` in the key produced a TOML file that
|
|
137
|
+
parsed as a broken multi-line string, bricking the config until manual fix.
|
|
138
|
+
"""
|
|
139
|
+
weird = "sk-line1\nline2\rline3\twith-tab"
|
|
140
|
+
auth_mod.save_to_config("claude", weird)
|
|
141
|
+
assert auth_mod.load_from_config("claude") == weird
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_key_with_nul_byte_survives_roundtrip(tmp_home):
|
|
145
|
+
"""NUL is invalid in TOML basic strings; must be \\u-escaped."""
|
|
146
|
+
weird = "sk-with\x00nul"
|
|
147
|
+
auth_mod.save_to_config("claude", weird)
|
|
148
|
+
assert auth_mod.load_from_config("claude") == weird
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_key_with_control_chars_survives_roundtrip(tmp_home):
|
|
152
|
+
weird = "sk-\x01\x02\x1ftest"
|
|
153
|
+
auth_mod.save_to_config("claude", weird)
|
|
154
|
+
assert auth_mod.load_from_config("claude") == weird
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_save_refuses_to_overwrite_unparseable_config(tmp_home):
|
|
158
|
+
"""If the existing config is corrupt, refuse to save — overwriting
|
|
159
|
+
silently would drop other adapters' keys.
|
|
160
|
+
"""
|
|
161
|
+
auth_mod.CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
auth_mod.CONFIG_FILE.write_text("[[ this is broken")
|
|
163
|
+
with pytest.raises(auth_mod.ConfigCorruptError):
|
|
164
|
+
auth_mod.save_to_config("claude", "sk-new")
|
|
165
|
+
# File contents preserved.
|
|
166
|
+
assert "[[ this is broken" in auth_mod.CONFIG_FILE.read_text()
|
axor_cli-0.2.0/CHANGELOG.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.1.0 — 2025-04-12
|
|
4
|
-
|
|
5
|
-
Initial release.
|
|
6
|
-
|
|
7
|
-
### Added
|
|
8
|
-
- Core governance kernel (axor-core)
|
|
9
|
-
- Claude Code adapter (axor-claude)
|
|
10
|
-
- CLI with interactive REPL (axor-cli)
|
|
11
|
-
- Dynamic policy selection (7-policy matrix)
|
|
12
|
-
- ContextManager with 11 waste categories
|
|
13
|
-
- ToolResultBus for async tool loop
|
|
14
|
-
- Federation via spawn_child with child_executor
|
|
15
|
-
- CancelToken cooperative cancellation
|
|
16
|
-
- BudgetPolicyEngine (60/80/90/95% thresholds)
|
|
17
|
-
- TraceCollector with lineage (17 event kinds)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|