ai-agent-ledger-py 0.2.1__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.
- ai_agent_ledger_py-0.2.1/.github/ISSUE_TEMPLATE/bug_report.yml +69 -0
- ai_agent_ledger_py-0.2.1/.github/ISSUE_TEMPLATE/config.yml +11 -0
- ai_agent_ledger_py-0.2.1/.github/ISSUE_TEMPLATE/feature_request.yml +61 -0
- ai_agent_ledger_py-0.2.1/.github/PULL_REQUEST_TEMPLATE.md +42 -0
- ai_agent_ledger_py-0.2.1/.github/workflows/ci.yml +128 -0
- ai_agent_ledger_py-0.2.1/.github/workflows/release.yml +105 -0
- ai_agent_ledger_py-0.2.1/.gitignore +147 -0
- ai_agent_ledger_py-0.2.1/CHANGELOG.md +602 -0
- ai_agent_ledger_py-0.2.1/CODE_OF_CONDUCT.md +124 -0
- ai_agent_ledger_py-0.2.1/CONTRIBUTING.md +169 -0
- ai_agent_ledger_py-0.2.1/LICENSE +201 -0
- ai_agent_ledger_py-0.2.1/PKG-INFO +546 -0
- ai_agent_ledger_py-0.2.1/README.md +513 -0
- ai_agent_ledger_py-0.2.1/README.zh-CN.md +417 -0
- ai_agent_ledger_py-0.2.1/SECURITY.md +76 -0
- ai_agent_ledger_py-0.2.1/docs/DESIGN.md +204 -0
- ai_agent_ledger_py-0.2.1/pyproject.toml +107 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/__init__.py +204 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/__main__.py +14 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/_identity.py +27 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/a2a.py +1058 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/adapters.py +369 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/ard.py +402 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/bundle.py +400 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/cli.py +780 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/conform.py +745 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/demo.py +634 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/ledger.py +1401 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/matcher.py +343 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/models.py +834 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/otlp.py +356 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/policy.py +249 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/py.typed +0 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/render.py +196 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/router.py +600 -0
- ai_agent_ledger_py-0.2.1/src/agent_ledger/signing.py +553 -0
- ai_agent_ledger_py-0.2.1/tests/__init__.py +5 -0
- ai_agent_ledger_py-0.2.1/tests/conftest.py +87 -0
- ai_agent_ledger_py-0.2.1/tests/test_a2a.py +545 -0
- ai_agent_ledger_py-0.2.1/tests/test_a2a_streaming.py +431 -0
- ai_agent_ledger_py-0.2.1/tests/test_adapters_and_otlp.py +499 -0
- ai_agent_ledger_py-0.2.1/tests/test_ard.py +295 -0
- ai_agent_ledger_py-0.2.1/tests/test_bundle.py +340 -0
- ai_agent_ledger_py-0.2.1/tests/test_cli_and_demo.py +287 -0
- ai_agent_ledger_py-0.2.1/tests/test_conform.py +372 -0
- ai_agent_ledger_py-0.2.1/tests/test_keyring.py +334 -0
- ai_agent_ledger_py-0.2.1/tests/test_ledger_and_grid.py +720 -0
- ai_agent_ledger_py-0.2.1/tests/test_ledger_backend.py +479 -0
- ai_agent_ledger_py-0.2.1/tests/test_ledger_integrity.py +854 -0
- ai_agent_ledger_py-0.2.1/tests/test_matching_and_policy.py +381 -0
- ai_agent_ledger_py-0.2.1/tests/test_models.py +267 -0
- ai_agent_ledger_py-0.2.1/tests/test_signing.py +850 -0
- ai_agent_ledger_py-0.2.1/tools/audit_dist.py +128 -0
- ai_agent_ledger_py-0.2.1/tools/check_readme.py +204 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something behaved differently from what the documentation says.
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Thanks for taking the time. Two things make a bug report far easier to act on:
|
|
9
|
+
|
|
10
|
+
1. The output of `al demo` — it exercises the real code paths end to end.
|
|
11
|
+
2. If the problem is about *routing* (the wrong agent chosen, or none chosen),
|
|
12
|
+
the candidate list from `grid.candidates(task)`. Every signal is kept
|
|
13
|
+
visible precisely so routing can be diagnosed without guesswork.
|
|
14
|
+
|
|
15
|
+
- type: textarea
|
|
16
|
+
id: what-happened
|
|
17
|
+
attributes:
|
|
18
|
+
label: What happened
|
|
19
|
+
description: What you observed, and what you expected instead.
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
|
|
23
|
+
- type: textarea
|
|
24
|
+
id: reproduce
|
|
25
|
+
attributes:
|
|
26
|
+
label: Steps to reproduce
|
|
27
|
+
description: A minimal script or command sequence. If it needs a registry, describe the response shape rather than a private URL.
|
|
28
|
+
placeholder: |
|
|
29
|
+
1. `al demo`
|
|
30
|
+
2. ...
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
|
|
34
|
+
- type: textarea
|
|
35
|
+
id: traceback
|
|
36
|
+
attributes:
|
|
37
|
+
label: Full traceback or output
|
|
38
|
+
description: Paste it verbatim. Do not trim the middle — the frame that matters is often the one that looks irrelevant.
|
|
39
|
+
render: shell
|
|
40
|
+
validations:
|
|
41
|
+
required: false
|
|
42
|
+
|
|
43
|
+
- type: input
|
|
44
|
+
id: version
|
|
45
|
+
attributes:
|
|
46
|
+
label: Version
|
|
47
|
+
description: Output of `al --version`.
|
|
48
|
+
validations:
|
|
49
|
+
required: true
|
|
50
|
+
|
|
51
|
+
- type: input
|
|
52
|
+
id: python
|
|
53
|
+
attributes:
|
|
54
|
+
label: Python and OS
|
|
55
|
+
placeholder: "3.12.1 on Windows 11 / 3.11.7 on Ubuntu 24.04"
|
|
56
|
+
validations:
|
|
57
|
+
required: true
|
|
58
|
+
|
|
59
|
+
- type: checkboxes
|
|
60
|
+
id: integrity
|
|
61
|
+
attributes:
|
|
62
|
+
label: If this is about receipts, verification or the ledger
|
|
63
|
+
description: |
|
|
64
|
+
Known and already documented — please check these before filing, so the
|
|
65
|
+
issue tracker stays useful. `docs/DESIGN.md` has the honest limitations
|
|
66
|
+
list, and `SECURITY.md` repeats it as a table of what is already known.
|
|
67
|
+
options:
|
|
68
|
+
- label: I read the Limitations section of docs/DESIGN.md and this is not one of them.
|
|
69
|
+
required: false
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Security vulnerability
|
|
4
|
+
url: https://github.com/yaoyuxiang-gnn/agent-ledger/security/advisories/new
|
|
5
|
+
about: Report privately. Do not open a public issue for anything exploitable.
|
|
6
|
+
- name: Question or idea
|
|
7
|
+
url: https://github.com/yaoyuxiang-gnn/agent-ledger/discussions
|
|
8
|
+
about: For "how do I…" and design discussion, which is not an issue.
|
|
9
|
+
- name: Known limitations
|
|
10
|
+
url: https://github.com/yaoyuxiang-gnn/agent-ledger/blob/main/docs/DESIGN.md
|
|
11
|
+
about: What each layer does and does not prove, and what is deliberately not built yet. Check here first.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose a change in behaviour, an adapter, or an integration.
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Before filing, `CONTRIBUTING.md` has a "What would help most" list, and
|
|
9
|
+
`docs/DESIGN.md` has the honest limitations. If your idea is already on
|
|
10
|
+
either, a comment there is more useful than a new issue.
|
|
11
|
+
|
|
12
|
+
Two constraints shape what can be merged, and proposals that respect them
|
|
13
|
+
move much faster:
|
|
14
|
+
|
|
15
|
+
- **Zero runtime dependencies.** The `dependencies` list must stay empty;
|
|
16
|
+
a CI job fails the build if it does not. Optional extras are fine —
|
|
17
|
+
`[sign]` is the expected shape for anything that needs a library.
|
|
18
|
+
- **Denials stay data.** A refusal is a `PolicyDecision`, not an exception.
|
|
19
|
+
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: problem
|
|
22
|
+
attributes:
|
|
23
|
+
label: What problem does this solve
|
|
24
|
+
description: Describe the situation you are in, not the API you want. The API is usually easier to get right once the problem is clear.
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
|
|
28
|
+
- type: textarea
|
|
29
|
+
id: proposal
|
|
30
|
+
attributes:
|
|
31
|
+
label: What you propose
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
|
|
35
|
+
- type: dropdown
|
|
36
|
+
id: area
|
|
37
|
+
attributes:
|
|
38
|
+
label: Area
|
|
39
|
+
multiple: true
|
|
40
|
+
options:
|
|
41
|
+
- Discovery (ARD)
|
|
42
|
+
- Matching and reputation
|
|
43
|
+
- Policy
|
|
44
|
+
- Delegation and receipts
|
|
45
|
+
- Ledger and storage
|
|
46
|
+
- Execution / A2A executor
|
|
47
|
+
- CLI
|
|
48
|
+
- Docs
|
|
49
|
+
- Other
|
|
50
|
+
validations:
|
|
51
|
+
required: true
|
|
52
|
+
|
|
53
|
+
- type: checkboxes
|
|
54
|
+
id: constraints
|
|
55
|
+
attributes:
|
|
56
|
+
label: Constraints
|
|
57
|
+
options:
|
|
58
|
+
- label: This can be done without adding a runtime dependency (or as an opt-in extra).
|
|
59
|
+
required: false
|
|
60
|
+
- label: I am willing to open a pull request for this.
|
|
61
|
+
required: false
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
## What this changes
|
|
2
|
+
|
|
3
|
+
<!-- One or two sentences. If it fixes an issue, link it. -->
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
<!-- The problem, not the patch. What goes wrong today? -->
|
|
8
|
+
|
|
9
|
+
## Breaking changes
|
|
10
|
+
|
|
11
|
+
<!--
|
|
12
|
+
Receipt digests are deterministic on purpose. If you changed `Receipt.body()` or
|
|
13
|
+
the canonicalisation, existing ledgers will report as tampered. Say so here, and
|
|
14
|
+
add it to CHANGELOG.md under Unreleased.
|
|
15
|
+
-->
|
|
16
|
+
|
|
17
|
+
- [ ] No breaking change
|
|
18
|
+
- [ ] Breaking change, described above and in `CHANGELOG.md`
|
|
19
|
+
|
|
20
|
+
## Checklist
|
|
21
|
+
|
|
22
|
+
- [ ] `python -m pytest` passes (no install needed — `tests/conftest.py` shims `src` onto `sys.path`)
|
|
23
|
+
- [ ] `python -m agent_ledger.cli demo --no-color` still runs — the demo is the README's front door
|
|
24
|
+
- [ ] `ruff check src tests` and `ruff format --check src tests` pass
|
|
25
|
+
- [ ] A test fails without this change
|
|
26
|
+
- [ ] `CHANGELOG.md` updated under `## [Unreleased]`
|
|
27
|
+
|
|
28
|
+
## Constraints this project keeps
|
|
29
|
+
|
|
30
|
+
- [ ] **No runtime dependency added.** `dependencies` stays empty; a CI job fails the build if it does not. Anything needing a library ships as an opt-in extra (`[sign]` is the expected shape).
|
|
31
|
+
- [ ] **No network in tests.** Everything goes through the `Transport` protocol; use `StaticTransport`.
|
|
32
|
+
- [ ] **The demo stays offline and key-free.**
|
|
33
|
+
- [ ] **Denials stay data.** A refusal is a `PolicyDecision`, not an exception.
|
|
34
|
+
- [ ] If this touches `verify()`, `content_digest()` or `Receipt`, I added a **negative** test — tampered digest, wrong key, missing field, hostile line — not only the happy path.
|
|
35
|
+
|
|
36
|
+
## How it was verified
|
|
37
|
+
|
|
38
|
+
<!--
|
|
39
|
+
Commands you ran and what they printed. If you changed the CLI, the demo
|
|
40
|
+
transcript, or anything the README quotes, say whether the README needed
|
|
41
|
+
updating — a README that describes the old behaviour is a bug.
|
|
42
|
+
-->
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: test (py${{ matrix.python }} / ${{ matrix.os }})
|
|
19
|
+
runs-on: ${{ matrix.os }}
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
24
|
+
python: ["3.10", "3.11", "3.12", "3.13"]
|
|
25
|
+
exclude:
|
|
26
|
+
# Keep the matrix honest without spending minutes on redundant runs.
|
|
27
|
+
- os: windows-latest
|
|
28
|
+
python: "3.11"
|
|
29
|
+
- os: macos-latest
|
|
30
|
+
python: "3.11"
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python }}
|
|
37
|
+
|
|
38
|
+
- name: Install
|
|
39
|
+
run: |
|
|
40
|
+
python -m pip install --upgrade pip
|
|
41
|
+
python -m pip install -e ".[dev]"
|
|
42
|
+
|
|
43
|
+
- name: Test
|
|
44
|
+
run: python -m pytest -q
|
|
45
|
+
|
|
46
|
+
- name: Demo must run on a clean checkout
|
|
47
|
+
# The offline demo is the project's front door. If it breaks, the
|
|
48
|
+
# README breaks, so it is a CI gate rather than a smoke test.
|
|
49
|
+
run: python -m agent_ledger.cli demo --no-color
|
|
50
|
+
env:
|
|
51
|
+
NO_COLOR: "1"
|
|
52
|
+
|
|
53
|
+
- name: READMEs must still describe this software
|
|
54
|
+
# Documentation drifts in one direction: it describes the version someone
|
|
55
|
+
# was proud of. This checks the claims that can be checked — every
|
|
56
|
+
# documented command and flag exists, the quoted test count is real, the
|
|
57
|
+
# demo transcript still matches, and the Chinese README has not fallen
|
|
58
|
+
# behind the English one.
|
|
59
|
+
run: python tools/check_readme.py
|
|
60
|
+
env:
|
|
61
|
+
NO_COLOR: "1"
|
|
62
|
+
|
|
63
|
+
lint:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
- uses: actions/setup-python@v5
|
|
68
|
+
with:
|
|
69
|
+
python-version: "3.12"
|
|
70
|
+
- run: python -m pip install --upgrade pip ruff
|
|
71
|
+
- run: ruff check src tests
|
|
72
|
+
- run: ruff format --check src tests
|
|
73
|
+
|
|
74
|
+
zero-dependency:
|
|
75
|
+
name: no runtime dependencies
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- uses: actions/setup-python@v5
|
|
80
|
+
with:
|
|
81
|
+
python-version: "3.12"
|
|
82
|
+
- name: Assert the package declares no runtime dependencies
|
|
83
|
+
# Zero dependencies is a headline promise in the README. This job is
|
|
84
|
+
# what keeps it true.
|
|
85
|
+
run: |
|
|
86
|
+
python - <<'PY'
|
|
87
|
+
import pathlib, re, sys
|
|
88
|
+
|
|
89
|
+
text = pathlib.Path("pyproject.toml").read_text(encoding="utf-8")
|
|
90
|
+
match = re.search(r"^dependencies\s*=\s*\[(.*?)\]", text, re.S | re.M)
|
|
91
|
+
if match is None:
|
|
92
|
+
sys.exit("could not find a dependencies key in pyproject.toml")
|
|
93
|
+
body = match.group(1).strip()
|
|
94
|
+
if body:
|
|
95
|
+
sys.exit(f"runtime dependencies must stay empty, found: {body!r}")
|
|
96
|
+
print("ok: no runtime dependencies declared")
|
|
97
|
+
PY
|
|
98
|
+
|
|
99
|
+
build:
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
- uses: actions/setup-python@v5
|
|
104
|
+
with:
|
|
105
|
+
python-version: "3.12"
|
|
106
|
+
- run: python -m pip install --upgrade pip build twine
|
|
107
|
+
- run: python -m build
|
|
108
|
+
- name: Assert the artefacts contain only what they should
|
|
109
|
+
# The checks live in tools/audit_dist.py rather than inline so that this
|
|
110
|
+
# job and the release workflow run the *same* ones — a release that is
|
|
111
|
+
# stricter or laxer than CI is a release nobody checked. The audit also
|
|
112
|
+
# asserts what an inline snippet cannot: that the version in the wheel
|
|
113
|
+
# matches pyproject.toml and _identity.py, and that the console scripts
|
|
114
|
+
# survived whatever the distribution name is this month.
|
|
115
|
+
run: python tools/audit_dist.py
|
|
116
|
+
- name: The metadata must render on PyPI
|
|
117
|
+
# README.md is the PyPI long description; a malformed one is rejected at
|
|
118
|
+
# upload time, long after the build that could have caught it.
|
|
119
|
+
run: python -m twine check --strict dist/*
|
|
120
|
+
- name: Install the built wheel in isolation
|
|
121
|
+
run: |
|
|
122
|
+
python -m venv /tmp/clean
|
|
123
|
+
/tmp/clean/bin/python -m pip install dist/*.whl
|
|
124
|
+
/tmp/clean/bin/al demo --no-color > /dev/null
|
|
125
|
+
- uses: actions/upload-artifact@v4
|
|
126
|
+
with:
|
|
127
|
+
name: dist
|
|
128
|
+
path: dist/
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishing is triggered by a *published GitHub release*, not by a tag push.
|
|
4
|
+
#
|
|
5
|
+
# A tag is a ref, and a ref can be moved; a release is a deliberate act with a
|
|
6
|
+
# human on the other end of it. The distinction matters here because a version
|
|
7
|
+
# number on PyPI can be yanked but never reused — the one mistake in this
|
|
8
|
+
# workflow that cannot be repaired — so the trigger is the event with the most
|
|
9
|
+
# intent behind it.
|
|
10
|
+
on:
|
|
11
|
+
release:
|
|
12
|
+
types: [published]
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
inputs:
|
|
15
|
+
target:
|
|
16
|
+
description: "Where to upload. Use testpypi to rehearse the whole path."
|
|
17
|
+
type: choice
|
|
18
|
+
options: [pypi, testpypi]
|
|
19
|
+
default: testpypi
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
name: build and check the artefacts
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
|
|
35
|
+
- name: Install build tooling
|
|
36
|
+
run: python -m pip install --upgrade pip build twine
|
|
37
|
+
|
|
38
|
+
- name: The tag must name the version being published
|
|
39
|
+
# PyPI will reject a second upload of an existing version, and the version
|
|
40
|
+
# number is burned either way. Catching the mismatch here — before the
|
|
41
|
+
# build — is the difference between a red X and a release nobody can redo.
|
|
42
|
+
if: github.event_name == 'release'
|
|
43
|
+
run: |
|
|
44
|
+
python - <<'PY'
|
|
45
|
+
import os, pathlib, re, sys
|
|
46
|
+
|
|
47
|
+
tag = os.environ["GITHUB_REF_NAME"]
|
|
48
|
+
text = pathlib.Path("pyproject.toml").read_text(encoding="utf-8")
|
|
49
|
+
version = re.search(r'^version\s*=\s*"([^"]+)"', text, re.M).group(1)
|
|
50
|
+
if tag.lstrip("v") != version:
|
|
51
|
+
sys.exit(f"tag {tag!r} does not match pyproject version {version!r}")
|
|
52
|
+
print(f"ok: tag {tag} == version {version}")
|
|
53
|
+
PY
|
|
54
|
+
|
|
55
|
+
- name: Build
|
|
56
|
+
run: python -m build
|
|
57
|
+
|
|
58
|
+
- name: Check the artefacts, and that the metadata renders
|
|
59
|
+
# The same audit CI runs, plus the twine check that only matters at
|
|
60
|
+
# upload time — README.md is the PyPI long description, and a malformed
|
|
61
|
+
# one is rejected after the build rather than during it.
|
|
62
|
+
run: |
|
|
63
|
+
python tools/audit_dist.py
|
|
64
|
+
python -m twine check --strict dist/*
|
|
65
|
+
|
|
66
|
+
- name: The wheel must run on its own
|
|
67
|
+
# Everything above inspects the artefact. This runs it — installed into a
|
|
68
|
+
# clean interpreter, with no `src` on the path and no source tree in
|
|
69
|
+
# sight, which is the only configuration a PyPI user will ever have.
|
|
70
|
+
run: |
|
|
71
|
+
python -m venv /tmp/clean
|
|
72
|
+
/tmp/clean/bin/python -m pip install dist/*.whl
|
|
73
|
+
/tmp/clean/bin/al demo --no-color > /dev/null
|
|
74
|
+
|
|
75
|
+
- uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: dist
|
|
78
|
+
path: dist/
|
|
79
|
+
|
|
80
|
+
publish:
|
|
81
|
+
name: publish to ${{ github.event_name == 'release' && 'PyPI' || inputs.target }}
|
|
82
|
+
needs: build
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
|
|
85
|
+
# Must match the environment named in the PyPI Trusted Publisher. It is also
|
|
86
|
+
# what lets you require a reviewer before a release reaches the world, which
|
|
87
|
+
# is the only remaining human gate in this path.
|
|
88
|
+
environment:
|
|
89
|
+
name: ${{ github.event_name == 'release' && 'pypi' || 'testpypi' }}
|
|
90
|
+
url: ${{ github.event_name == 'release' && 'https://pypi.org/p/ai-agent-ledger-py' || 'https://test.pypi.org/p/ai-agent-ledger-py' }}
|
|
91
|
+
|
|
92
|
+
permissions:
|
|
93
|
+
# The OIDC token that replaces an API token. Without this the upload cannot
|
|
94
|
+
# authenticate, and there is deliberately no secret to fall back on.
|
|
95
|
+
id-token: write
|
|
96
|
+
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/download-artifact@v4
|
|
99
|
+
with:
|
|
100
|
+
name: dist
|
|
101
|
+
path: dist/
|
|
102
|
+
|
|
103
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
104
|
+
with:
|
|
105
|
+
repository-url: ${{ github.event_name == 'release' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# What may be published
|
|
2
|
+
#
|
|
3
|
+
# This file is deliberately explicit rather than exhaustive. A deny-list only
|
|
4
|
+
# stops the leaks somebody already thought of; the next stray file — a release
|
|
5
|
+
# plan, a strategy note, a scratch analysis — walks straight past it. So the
|
|
6
|
+
# rules below name the *few* local-only directories instead, and everything else
|
|
7
|
+
# here is the ordinary build and editor cruft.
|
|
8
|
+
#
|
|
9
|
+
# The rule this encodes: **anything that is not the product or its public
|
|
10
|
+
# documentation does not get committed.** If you are unsure whether a file is
|
|
11
|
+
# product, it is not.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# --------------------------------------------------------------------------- #
|
|
15
|
+
# Local-only working material. NEVER published.
|
|
16
|
+
# --------------------------------------------------------------------------- #
|
|
17
|
+
|
|
18
|
+
# Strategy, planning, launch notes, competitive analysis, anything written for
|
|
19
|
+
# us rather than for a reader. Kept on disk because it is useful; kept out of
|
|
20
|
+
# git because a repository is a publication, not a folder.
|
|
21
|
+
.internal/
|
|
22
|
+
.notes/
|
|
23
|
+
.planning/
|
|
24
|
+
private/
|
|
25
|
+
|
|
26
|
+
# Security audits and threat models. These are the one category where publishing
|
|
27
|
+
# is actively harmful: a document that lists weaknesses by file and line is a
|
|
28
|
+
# map for anyone who wants to use them, and "the issues are fixed" is not a
|
|
29
|
+
# reason to hand over the map.
|
|
30
|
+
AUDIT-*.md
|
|
31
|
+
THREAT-MODEL*.md
|
|
32
|
+
SECURITY-AUDIT*.md
|
|
33
|
+
|
|
34
|
+
# Release and launch plans. How a project intends to earn attention is not part
|
|
35
|
+
# of what it does, and it reads badly to the people whose attention is wanted.
|
|
36
|
+
RELEASE*.md
|
|
37
|
+
LAUNCH*.md
|
|
38
|
+
STRATEGY*.md
|
|
39
|
+
ROADMAP-INTERNAL*.md
|
|
40
|
+
|
|
41
|
+
# Scratch, drafts, and one-off analysis scripts
|
|
42
|
+
_scratch*.*
|
|
43
|
+
_dbg*.*
|
|
44
|
+
_tmp*.*
|
|
45
|
+
*.draft.md
|
|
46
|
+
TODO.local.md
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# --------------------------------------------------------------------------- #
|
|
50
|
+
# Python build and cache artefacts
|
|
51
|
+
# --------------------------------------------------------------------------- #
|
|
52
|
+
|
|
53
|
+
__pycache__/
|
|
54
|
+
*.py[cod]
|
|
55
|
+
*$py.class
|
|
56
|
+
*.so
|
|
57
|
+
.Python
|
|
58
|
+
|
|
59
|
+
build/
|
|
60
|
+
dist/
|
|
61
|
+
*.egg-info/
|
|
62
|
+
.eggs/
|
|
63
|
+
wheels/
|
|
64
|
+
sdist/
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# --------------------------------------------------------------------------- #
|
|
68
|
+
# Virtual environments
|
|
69
|
+
#
|
|
70
|
+
# `.venv-*/` is listed as well as `.venv/` because `.venv/` does NOT match
|
|
71
|
+
# `.venv-test/`. That distinction is not cosmetic: hatchling honours .gitignore
|
|
72
|
+
# when assembling an sdist, so one missing pattern once produced a 16 MB sdist
|
|
73
|
+
# containing an entire virtualenv, `python.exe` included. Every environment
|
|
74
|
+
# directory name in use must be covered here.
|
|
75
|
+
# --------------------------------------------------------------------------- #
|
|
76
|
+
|
|
77
|
+
.venv/
|
|
78
|
+
.venv-*/
|
|
79
|
+
venv/
|
|
80
|
+
venv-*/
|
|
81
|
+
env/
|
|
82
|
+
ENV/
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# --------------------------------------------------------------------------- #
|
|
86
|
+
# Test, coverage and lint caches
|
|
87
|
+
# --------------------------------------------------------------------------- #
|
|
88
|
+
|
|
89
|
+
.pytest_cache/
|
|
90
|
+
.ruff_cache/
|
|
91
|
+
.mypy_cache/
|
|
92
|
+
.dmypy.json
|
|
93
|
+
.tox/
|
|
94
|
+
.nox/
|
|
95
|
+
.coverage
|
|
96
|
+
.coverage.*
|
|
97
|
+
htmlcov/
|
|
98
|
+
coverage.xml
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# --------------------------------------------------------------------------- #
|
|
102
|
+
# Ledgers and key material produced by running the thing
|
|
103
|
+
#
|
|
104
|
+
# Receipts are evidence and keys are keys; neither belongs in a source
|
|
105
|
+
# repository. Fixtures are the exception, and are re-included explicitly.
|
|
106
|
+
# --------------------------------------------------------------------------- #
|
|
107
|
+
|
|
108
|
+
*.jsonl
|
|
109
|
+
!tests/fixtures/*.jsonl
|
|
110
|
+
|
|
111
|
+
# Trust stores and signed bundles. A keyring holds public keys, but publishing
|
|
112
|
+
# one is an operational decision, not a side effect of running a command.
|
|
113
|
+
keyring.json
|
|
114
|
+
*keyring*.json
|
|
115
|
+
*.bundle.json
|
|
116
|
+
secrets.json
|
|
117
|
+
|
|
118
|
+
# Never, under any circumstance
|
|
119
|
+
.env
|
|
120
|
+
.env.*
|
|
121
|
+
*.pem
|
|
122
|
+
*.key
|
|
123
|
+
id_rsa*
|
|
124
|
+
id_ed25519*
|
|
125
|
+
*.p12
|
|
126
|
+
*.pfx
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# --------------------------------------------------------------------------- #
|
|
130
|
+
# Documentation build output
|
|
131
|
+
# --------------------------------------------------------------------------- #
|
|
132
|
+
|
|
133
|
+
docs/_build/
|
|
134
|
+
site/
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# --------------------------------------------------------------------------- #
|
|
138
|
+
# Editors and operating systems
|
|
139
|
+
# --------------------------------------------------------------------------- #
|
|
140
|
+
|
|
141
|
+
.idea/
|
|
142
|
+
.vscode/
|
|
143
|
+
*.swp
|
|
144
|
+
*.swo
|
|
145
|
+
.DS_Store
|
|
146
|
+
Thumbs.db
|
|
147
|
+
desktop.ini
|