agent-first-data 0.34.0__tar.gz → 0.35.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.
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/PKG-INFO +3 -3
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/README.md +2 -2
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/cli.py +22 -4
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/format.py +13 -12
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data.egg-info/PKG-INFO +3 -3
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/pyproject.toml +1 -1
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/tests/test_cli.py +25 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/__init__.py +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/cli-help-v2.schema.json +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/cli-spec-v1.schema.json +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/protocol-v1.schema.json +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/registry.json +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data.egg-info/SOURCES.txt +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data.egg-info/dependency_links.txt +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data.egg-info/top_level.txt +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/setup.cfg +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/tests/test_format.py +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/tests/test_no_stderr_policy.py +0 -0
- {agent_first_data-0.34.0 → agent_first_data-0.35.0}/tests/test_requires_python_floor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-first-data
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
4
4
|
Summary: A naming convention that lets AI agents understand your data without being told what it means, plus a CLI and library for reading Markdown structure and safely editing structured JSON, TOML, YAML, dotenv, and INI documents.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/agentfirstkit/agent-first-data
|
|
@@ -33,11 +33,11 @@ Useful names use Python casing: `render` (the single value x format x options ->
|
|
|
33
33
|
## Behavior Notes
|
|
34
34
|
|
|
35
35
|
- Default redaction replaces every `_secret` or configured secret-name subtree with `***`, including objects and arrays.
|
|
36
|
-
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed and internal whitespace redacts the whole field.
|
|
36
|
+
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed, and internal whitespace or a `://` that is not the start of a clean URL redacts the whole field, where whitespace is the explicit Unicode `White_Space` set the spec enumerates, not the language's native predicate.
|
|
37
37
|
- `OutputOptions.url_names` applies URL treatment to exact legacy field names
|
|
38
38
|
and recurses through collections. `redact_urls_in_text` is explicit and scans
|
|
39
39
|
only complete scheme URLs, never arbitrary prose secrets.
|
|
40
|
-
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON.
|
|
40
|
+
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON; it quotes a value only when it contains ASCII space, tab, newline, carriage return, form feed, vertical tab, NBSP, `=`, `"`, or `\`, and escapes `\`, `"`, newline, carriage return, tab, form feed, and vertical tab inside the quotes.
|
|
41
41
|
- Logging records use `kind:"log"` with a nested `log` payload and a separate `level` field, so error-level logs are not terminal protocol errors.
|
|
42
42
|
- `build_cli_error(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
|
|
43
43
|
- Do not add a raw version/help pre-parser. Until the Python `CliSpec` compiler lands, applications should keep lifecycle parsing in their own parser rather than claim AFDATA closed-world help compatibility.
|
|
@@ -24,11 +24,11 @@ Useful names use Python casing: `render` (the single value x format x options ->
|
|
|
24
24
|
## Behavior Notes
|
|
25
25
|
|
|
26
26
|
- Default redaction replaces every `_secret` or configured secret-name subtree with `***`, including objects and arrays.
|
|
27
|
-
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed and internal whitespace redacts the whole field.
|
|
27
|
+
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed, and internal whitespace or a `://` that is not the start of a clean URL redacts the whole field, where whitespace is the explicit Unicode `White_Space` set the spec enumerates, not the language's native predicate.
|
|
28
28
|
- `OutputOptions.url_names` applies URL treatment to exact legacy field names
|
|
29
29
|
and recurses through collections. `redact_urls_in_text` is explicit and scans
|
|
30
30
|
only complete scheme URLs, never arbitrary prose secrets.
|
|
31
|
-
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON.
|
|
31
|
+
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON; it quotes a value only when it contains ASCII space, tab, newline, carriage return, form feed, vertical tab, NBSP, `=`, `"`, or `\`, and escapes `\`, `"`, newline, carriage return, tab, form feed, and vertical tab inside the quotes.
|
|
32
32
|
- Logging records use `kind:"log"` with a nested `log` payload and a separate `level` field, so error-level logs are not terminal protocol errors.
|
|
33
33
|
- `build_cli_error(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
|
|
34
34
|
- Do not add a raw version/help pre-parser. Until the Python `CliSpec` compiler lands, applications should keep lifecycle parsing in their own parser rather than claim AFDATA closed-world help compatibility.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import enum
|
|
6
|
+
import os
|
|
6
7
|
import sys
|
|
7
8
|
from typing import Any, Iterator
|
|
8
9
|
|
|
@@ -298,10 +299,27 @@ class CliEmitter:
|
|
|
298
299
|
# mode (no diagnostic sink) keeps every event on the single writer.
|
|
299
300
|
use_diagnostic = kind != "result" and self._diagnostic is not None
|
|
300
301
|
sink = self._diagnostic if use_diagnostic else self._writer
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
flush
|
|
302
|
+
try:
|
|
303
|
+
sink.write(render(envelope, self._format, options=self._output_options) + "\n")
|
|
304
|
+
flush = getattr(sink, "flush", None)
|
|
305
|
+
if flush is not None:
|
|
306
|
+
flush()
|
|
307
|
+
except BrokenPipeError:
|
|
308
|
+
# A failed flush leaves buffered bytes for interpreter shutdown to
|
|
309
|
+
# retry. Redirect the closed descriptor before finish returns 0.
|
|
310
|
+
fileno = getattr(sink, "fileno", None)
|
|
311
|
+
if fileno is not None:
|
|
312
|
+
try:
|
|
313
|
+
descriptor = fileno()
|
|
314
|
+
except (OSError, ValueError):
|
|
315
|
+
descriptor = None
|
|
316
|
+
if descriptor is not None:
|
|
317
|
+
null_descriptor = os.open(os.devnull, os.O_WRONLY)
|
|
318
|
+
try:
|
|
319
|
+
os.dup2(null_descriptor, descriptor)
|
|
320
|
+
finally:
|
|
321
|
+
os.close(null_descriptor)
|
|
322
|
+
raise
|
|
305
323
|
if kind in ("result", "error"):
|
|
306
324
|
self._terminal_emitted = True
|
|
307
325
|
|
|
@@ -1273,17 +1273,19 @@ def _redact_url_field_value(s: str, context: _RedactionContext) -> str:
|
|
|
1273
1273
|
redacted = _redact_url_in_str(s, context)
|
|
1274
1274
|
if redacted is not None:
|
|
1275
1275
|
return redacted
|
|
1276
|
-
trimmed = s.strip()
|
|
1276
|
+
trimmed = s.strip("".join(_SPAN_TERMINATOR_WHITESPACE))
|
|
1277
1277
|
if trimmed != s:
|
|
1278
1278
|
redacted = _redact_url_in_str(trimmed, context)
|
|
1279
1279
|
if redacted is not None:
|
|
1280
1280
|
return redacted
|
|
1281
1281
|
# Fail closed: a _url value we could not parse as a clean scheme-prefixed
|
|
1282
|
-
# URL, yet which carries a credential sigil ('@' userinfo)
|
|
1283
|
-
# whitespace, is redacted wholesale rather than
|
|
1284
|
-
# connection string like user:pass@host/db
|
|
1285
|
-
# surgical span logic above,
|
|
1286
|
-
|
|
1282
|
+
# URL, yet which carries a credential sigil ('@' userinfo), internal
|
|
1283
|
+
# whitespace, or a '://' scheme separator, is redacted wholesale rather than
|
|
1284
|
+
# passed through. A schemeless connection string like user:pass@host/db
|
|
1285
|
+
# has no scheme anchor for the surgical span logic above, and a URL behind
|
|
1286
|
+
# a non-whitespace prefix (a BOM, a zero-width space, a label) has one the
|
|
1287
|
+
# span logic cannot reach, so blanket redaction is the safe default.
|
|
1288
|
+
if any(_is_span_terminator_whitespace(c) for c in s) or "@" in s or "://" in s:
|
|
1287
1289
|
return REDACTED_MARKER
|
|
1288
1290
|
return s
|
|
1289
1291
|
|
|
@@ -1840,11 +1842,10 @@ def _yaml_key(key: str) -> str:
|
|
|
1840
1842
|
def _is_ambiguous_yaml_key(key: str) -> bool:
|
|
1841
1843
|
if key.lower() in {"true", "false", "null", "~", ".nan", ".inf", "+.inf", "-.inf"}:
|
|
1842
1844
|
return True
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
return False
|
|
1845
|
+
unsigned = key[1:] if key.startswith(("+", "-")) else key
|
|
1846
|
+
return unsigned.lower() in {"inf", "infinity", "nan"} or re.fullmatch(
|
|
1847
|
+
r"[-+]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][-+]?[0-9]+)?", key
|
|
1848
|
+
) is not None
|
|
1848
1849
|
|
|
1849
1850
|
|
|
1850
1851
|
def _yaml_scalar(value: Any) -> str:
|
|
@@ -1945,7 +1946,7 @@ def _plain_scalar_raw(value: Any) -> str:
|
|
|
1945
1946
|
def _quote_logfmt_value(value: str) -> str:
|
|
1946
1947
|
if value == "":
|
|
1947
1948
|
return ""
|
|
1948
|
-
needs_quote = any(c
|
|
1949
|
+
needs_quote = any(c in ' \t\n\r\f\v\u00a0="\\' for c in value)
|
|
1949
1950
|
if not needs_quote:
|
|
1950
1951
|
return value
|
|
1951
1952
|
escaped = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-first-data
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
4
4
|
Summary: A naming convention that lets AI agents understand your data without being told what it means, plus a CLI and library for reading Markdown structure and safely editing structured JSON, TOML, YAML, dotenv, and INI documents.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/agentfirstkit/agent-first-data
|
|
@@ -33,11 +33,11 @@ Useful names use Python casing: `render` (the single value x format x options ->
|
|
|
33
33
|
## Behavior Notes
|
|
34
34
|
|
|
35
35
|
- Default redaction replaces every `_secret` or configured secret-name subtree with `***`, including objects and arrays.
|
|
36
|
-
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed and internal whitespace redacts the whole field.
|
|
36
|
+
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed, and internal whitespace or a `://` that is not the start of a clean URL redacts the whole field, where whitespace is the explicit Unicode `White_Space` set the spec enumerates, not the language's native predicate.
|
|
37
37
|
- `OutputOptions.url_names` applies URL treatment to exact legacy field names
|
|
38
38
|
and recurses through collections. `redact_urls_in_text` is explicit and scans
|
|
39
39
|
only complete scheme URLs, never arbitrary prose secrets.
|
|
40
|
-
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON.
|
|
40
|
+
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON; it quotes a value only when it contains ASCII space, tab, newline, carriage return, form feed, vertical tab, NBSP, `=`, `"`, or `\`, and escapes `\`, `"`, newline, carriage return, tab, form feed, and vertical tab inside the quotes.
|
|
41
41
|
- Logging records use `kind:"log"` with a nested `log` payload and a separate `level` field, so error-level logs are not terminal protocol errors.
|
|
42
42
|
- `build_cli_error(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
|
|
43
43
|
- Do not add a raw version/help pre-parser. Until the Python `CliSpec` compiler lands, applications should keep lifecycle parsing in their own parser rather than claim AFDATA closed-world help compatibility.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agent-first-data"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.35.0"
|
|
4
4
|
description = "A naming convention that lets AI agents understand your data without being told what it means, plus a CLI and library for reading Markdown structure and safely editing structured JSON, TOML, YAML, dotenv, and INI documents."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
readme = "README.md"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Tests for agent_first_data CLI helpers."""
|
|
2
2
|
import json
|
|
3
|
+
import sys
|
|
3
4
|
|
|
4
5
|
import pytest
|
|
5
6
|
from io import StringIO
|
|
@@ -500,3 +501,27 @@ def test_cli_render_version_renders_json():
|
|
|
500
501
|
assert '"code":"version"' in out
|
|
501
502
|
assert '"name":"agent-cli"' in out
|
|
502
503
|
assert '"version":"1.2.3"' in out
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
@pytest.mark.skipif(
|
|
507
|
+
sys.platform == "win32",
|
|
508
|
+
reason="closed-pipe writes surface as OSError(EINVAL) on Windows, not "
|
|
509
|
+
"BrokenPipeError; the reader-closed contract is asserted on POSIX only",
|
|
510
|
+
)
|
|
511
|
+
def test_real_stdout_closed_pipe_has_clean_process_exit():
|
|
512
|
+
import os
|
|
513
|
+
import subprocess
|
|
514
|
+
import sys
|
|
515
|
+
|
|
516
|
+
reader, writer = os.pipe()
|
|
517
|
+
os.close(reader)
|
|
518
|
+
try:
|
|
519
|
+
child = subprocess.run(
|
|
520
|
+
[sys.executable, "-c", "from agent_first_data import CliEmitter, OutputFormat; "
|
|
521
|
+
"raise SystemExit(CliEmitter.finite(OutputFormat.JSON).finish_result({'ok': True}))"],
|
|
522
|
+
stdout=writer, stderr=subprocess.PIPE, timeout=10,
|
|
523
|
+
)
|
|
524
|
+
finally:
|
|
525
|
+
os.close(writer)
|
|
526
|
+
assert child.returncode == 0, child.stderr
|
|
527
|
+
assert child.stderr == b""
|
|
File without changes
|
{agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/cli-help-v2.schema.json
RENAMED
|
File without changes
|
{agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/cli-spec-v1.schema.json
RENAMED
|
File without changes
|
{agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data/assets/protocol-v1.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_first_data-0.34.0 → agent_first_data-0.35.0}/agent_first_data.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|