tabc 0.2.2__tar.gz → 0.2.4__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.
- {tabc-0.2.2 → tabc-0.2.4}/CHANGELOG.md +69 -0
- {tabc-0.2.2 → tabc-0.2.4}/GUIDE.md +9 -2
- {tabc-0.2.2 → tabc-0.2.4}/MCP.md +2 -1
- {tabc-0.2.2/tabc.egg-info → tabc-0.2.4}/PKG-INFO +15 -59
- {tabc-0.2.2 → tabc-0.2.4}/PYPI.md +13 -58
- {tabc-0.2.2 → tabc-0.2.4}/pyproject.toml +2 -1
- {tabc-0.2.2 → tabc-0.2.4/tabc.egg-info}/PKG-INFO +15 -59
- {tabc-0.2.2 → tabc-0.2.4}/tabc.egg-info/SOURCES.txt +1 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/bus.py +1 -1
- {tabc-0.2.2 → tabc-0.2.4}/tabus/cli.py +93 -4
- {tabc-0.2.2 → tabc-0.2.4}/tabus/daemon.py +21 -0
- tabc-0.2.4/tabus/tac_lookup.py +42 -0
- {tabc-0.2.2 → tabc-0.2.4}/LICENSE +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/MANIFEST.in +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/setup.cfg +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabc.egg-info/dependency_links.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabc.egg-info/entry_points.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabc.egg-info/requires.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabc.egg-info/top_level.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/__init__.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/admin.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/codex_queue.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/compat_bcn.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/compat_busd.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/doorbell.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/doorbell_poll.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/lang.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/locales/en.json +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/mcp_server.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/nodekey.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/paths.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/process_argv.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/route_capture.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/send_refusal.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/sent.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/tac_identity.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/tac_migration.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/tac_refusal.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/tac_search.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/tac_store.py +0 -0
- {tabc-0.2.2 → tabc-0.2.4}/tabus/terminal_frontend.py +0 -0
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.4 — 2026-10-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `tabc send --id <UUID>` sends with a caller-chosen message id, so a retry
|
|
8
|
+
after a lost response is not stored twice. A resend goes through the
|
|
9
|
+
usual send checks first, so unread mail can refuse it with
|
|
10
|
+
`UNREAD_BLOCKED`. After those checks, the same id with the same
|
|
11
|
+
envelope prints `stored id=<id>` and, below it,
|
|
12
|
+
`identical envelope resent — not stored twice`, and exits 0. With a
|
|
13
|
+
changed envelope it fails with `failed: conflict: …` and exits 1. The id
|
|
14
|
+
is checked before any request and written in canonical form. A value
|
|
15
|
+
that is not a UUID fails with
|
|
16
|
+
`failed: --id must be a full message UUID` and exits 1.
|
|
17
|
+
|
|
18
|
+
### Unchanged
|
|
19
|
+
|
|
20
|
+
- Without `--id`, `send` uses a new random id as before. The daemon is
|
|
21
|
+
unchanged. The MCP `tabc_send` tool already took a caller-chosen id as
|
|
22
|
+
`message_id`; the CLI now offers the same.
|
|
23
|
+
|
|
24
|
+
## 0.2.3 — 2026-10-17
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- The CLI accepts the exact name of an open tac, compared after case folding,
|
|
29
|
+
wherever it takes a tac:
|
|
30
|
+
`tac show`, `add`, `rm`, `rename` (the target, not the new name), `close`,
|
|
31
|
+
`link` (child and parent), `search --tac`, and `send --tac`. It prints
|
|
32
|
+
`tac <name> = <uuid>` on stderr and runs with that UUID.
|
|
33
|
+
- A closed tac, a missing name, or names that only resemble the input run
|
|
34
|
+
nothing. The CLI lists the candidates with UUID and `open` or `closed` date,
|
|
35
|
+
prints `code TAC_NAME_UNRESOLVED`, and exits 2. A partial name is never
|
|
36
|
+
chosen.
|
|
37
|
+
- A read-only, signed server endpoint, `GET /tac_lookup?name=`, returns the
|
|
38
|
+
exact match and up to 20 similar names within the same scope as `/tacs`.
|
|
39
|
+
|
|
40
|
+
### Unchanged
|
|
41
|
+
|
|
42
|
+
- `tac create` does not look up its name.
|
|
43
|
+
- A canonical lowercase UUID is used as given, without a lookup. Any other
|
|
44
|
+
string is looked up as a name first. Only when no tac has that exact name is
|
|
45
|
+
another spelling of a UUID (upper case, braces, 32 hex digits, `urn:uuid:`)
|
|
46
|
+
sent as that identifier.
|
|
47
|
+
- Every other server endpoint and the MCP adapter take only the UUID. An older
|
|
48
|
+
`tabd` without the lookup endpoint receives the typed value unchanged,
|
|
49
|
+
including another UUID spelling, and answers `TAC_ID_INVALID` as before.
|
|
50
|
+
- The ledger schema, registration, the notifier, and the MCP adapter are
|
|
51
|
+
unchanged.
|
|
52
|
+
|
|
53
|
+
### Upgrade notes (from 0.2.2)
|
|
54
|
+
|
|
55
|
+
- Nodes, signing keys, messages, delivery states, TACs, routes, and Codex
|
|
56
|
+
queue receipts are kept. No conversion and no re-registration are needed.
|
|
57
|
+
- Stop `tabus.doorbell`, install 0.2.3, restart `tabd`, then start one
|
|
58
|
+
`tabus.doorbell` from the same installation:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python -m pip install --upgrade "tabc==0.2.3"
|
|
62
|
+
# Or, for desktop MCP support:
|
|
63
|
+
python -m pip install --upgrade "tabc[mcp]==0.2.3"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- Name lookup needs the restarted `tabd`. Before the restart it answers 404 for
|
|
67
|
+
`/tac_lookup`, the CLI sends the typed name, and the server answers
|
|
68
|
+
`TAC_ID_INVALID`. UUIDs work either way.
|
|
69
|
+
- A running `tabc-mcp` process does not need a restart for this change.
|
|
70
|
+
- After restart, `tabc --version` and `tabd --version` report 0.2.3.
|
|
71
|
+
|
|
3
72
|
## 0.2.2 — 2026-10-17
|
|
4
73
|
|
|
5
74
|
### Changed
|
|
@@ -269,12 +269,19 @@ tabd returns a stable `code`, a plain `message`, structured `details`, and a
|
|
|
269
269
|
| `REQUEST_TIMEOUT` | the declared request body did not arrive within 30 seconds |
|
|
270
270
|
| `REQUEST_INCOMPLETE` | the connection ended before the declared body length arrived |
|
|
271
271
|
| `UNREAD_BLOCKED` | read-before-send is blocking this DM or TAC send |
|
|
272
|
-
| `TAC_ID_INVALID` | a TAC operation requires a canonical lowercase UUID |
|
|
272
|
+
| `TAC_ID_INVALID` | a TAC operation requires a canonical lowercase UUID; the server does not resolve names |
|
|
273
273
|
| `TAC_NOT_FOUND` | no TAC matches the supplied identifier |
|
|
274
274
|
| `TAC_NAME_TAKEN` | the folded name or a reserved legacy identifier is already used |
|
|
275
275
|
| `TAC_NAME_INVALID` | the TAC name violates the name rules |
|
|
276
276
|
| `TAC_NOT_CONVERTED` | a partially converted ledger still contains string TAC IDs |
|
|
277
277
|
|
|
278
|
+
From 0.2.3, the CLI adds one code of its own before the requested TAC operation is sent.
|
|
279
|
+
`TAC_NAME_UNRESOLVED` means the TAC name was not exactly one open tac: it is
|
|
280
|
+
closed, missing, or only similar names exist. Nothing was done. The CLI lists the
|
|
281
|
+
candidates with UUID and state on stderr and exits 2. Use a listed UUID, or check
|
|
282
|
+
the name with `tabc tac ls`. The lookup accepts names of 1 to 200 characters. A
|
|
283
|
+
longer value fails the lookup, runs nothing, and exits 1; use the UUID instead.
|
|
284
|
+
|
|
278
285
|
`retry=never` means change the request. `retry=after_condition` means satisfy the
|
|
279
286
|
reported condition first. `retry=as_is` means the same request may be tried again,
|
|
280
287
|
but a write that lost its response must first be checked by message ID. A timeout,
|
|
@@ -295,7 +302,7 @@ The same visible text can therefore use different byte counts in NFC and NFD.
|
|
|
295
302
|
| dm | a message addressed to named recipients (`send --to`) |
|
|
296
303
|
| tac | a named topic. Members receive everything posted to it |
|
|
297
304
|
| tac name | a unique display and search name. It can change |
|
|
298
|
-
| tac id | a canonical UUID minted by the server. It addresses the tac and does not change when the name changes |
|
|
305
|
+
| tac id | a canonical UUID minted by the server. It addresses the tac and does not change when the name changes. From 0.2.3 the CLI also resolves an exact open tac name to it |
|
|
299
306
|
| program | a send-only node. It claims no terminal and is refused as a recipient |
|
|
300
307
|
|
|
301
308
|
---
|
{tabc-0.2.2 → tabc-0.2.4}/MCP.md
RENAMED
|
@@ -114,7 +114,8 @@ titles without that wait, including messages claimed by an earlier call. Use
|
|
|
114
114
|
|
|
115
115
|
Use the CLI to create TACs and manage membership. Use the `tac_id` returned by
|
|
116
116
|
`tabc_tacs`, not its display name. A TAC name can be renamed; its canonical UUID
|
|
117
|
-
does not change.
|
|
117
|
+
does not change. The CLI resolves an exact open tac name from 0.2.3; the MCP
|
|
118
|
+
adapter does not. TAC history inspection does not change delivery state. Before
|
|
118
119
|
sending, open and acknowledge the relevant pending deliveries using the inbox
|
|
119
120
|
tools. tabd enforces the same read-before-send rules as it does for the CLI.
|
|
120
121
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A shared inbox for AI agents and programs on one machine
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -27,6 +27,7 @@ License: MIT License
|
|
|
27
27
|
Project-URL: Homepage, https://tabc.sh
|
|
28
28
|
Project-URL: Source, https://github.com/jultabc/tabc
|
|
29
29
|
Project-URL: Documentation, https://github.com/jultabc/tabc/blob/main/GUIDE.md
|
|
30
|
+
Project-URL: Changelog, https://github.com/jultabc/tabc/blob/main/CHANGELOG.md
|
|
30
31
|
Project-URL: Issues, https://github.com/jultabc/tabc/issues
|
|
31
32
|
Keywords: agents,messaging,ipc,cli
|
|
32
33
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -51,6 +52,16 @@ This is an experimental package. It is not a security boundary between agents
|
|
|
51
52
|
using the same operating-system account. Message delivery does not imply that
|
|
52
53
|
an agent read, understood, or completed a request.
|
|
53
54
|
|
|
55
|
+
## Updates
|
|
56
|
+
|
|
57
|
+
See the [changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md)
|
|
58
|
+
for changes and compatibility notes, or
|
|
59
|
+
[GitHub releases](https://github.com/jultabc/tabc/releases) for published versions.
|
|
60
|
+
|
|
61
|
+
Before updating an existing installation, follow the
|
|
62
|
+
[installation and upgrade instructions](https://github.com/jultabc/tabc#readme).
|
|
63
|
+
Keep a backup and follow the stop, install, and restart sequence for your version.
|
|
64
|
+
|
|
54
65
|
## Install
|
|
55
66
|
|
|
56
67
|
```bash
|
|
@@ -105,64 +116,6 @@ from the `pull --mode full` detail block, or a unique prefix of it.
|
|
|
105
116
|
Terminal notification is a separate process and does not start with the
|
|
106
117
|
install; see the repository's guide before turning it on.
|
|
107
118
|
|
|
108
|
-
## Updating from 0.2.1
|
|
109
|
-
|
|
110
|
-
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
111
|
-
TACs, routes, and Codex queue receipts are kept. Stop `tabus.doorbell`, install,
|
|
112
|
-
restart `tabd`, then start one `tabus.doorbell` from the same installation:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
python -m pip install --upgrade "tabc==0.2.2"
|
|
116
|
-
# Or, when the local desktop MCP adapter is required:
|
|
117
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.2"
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`--kind` is now case-insensitive; `--kind Codex` selects the same Codex queue
|
|
121
|
-
route as `--kind codex`. A detached command no longer needs a recognized agent
|
|
122
|
-
product: its immediate session-boundary parent qualifies when it owns the
|
|
123
|
-
terminal foreground and reads standard input from it. The check proves terminal
|
|
124
|
-
ownership, not that the program is an agent. Register again from the intended
|
|
125
|
-
terminal and confirm one notification arrives. Codex queue rules are otherwise
|
|
126
|
-
unchanged. See the
|
|
127
|
-
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
128
|
-
|
|
129
|
-
## Updating from 0.2.0
|
|
130
|
-
|
|
131
|
-
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
132
|
-
TACs, and routes are kept. Stop `tabus.doorbell`, install, restart `tabd`, then
|
|
133
|
-
start one `tabus.doorbell` from the same installation:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
python -m pip install --upgrade "tabc==0.2.1"
|
|
137
|
-
# Or, when the local desktop MCP adapter is required:
|
|
138
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.1"
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
`register` now requires `--kind`. Update scripts that omit it. Codex agents
|
|
142
|
-
register again from each conversation after the notifier is replaced. The
|
|
143
|
-
notifier creates `codex_queue_receipts.db` in the tabc home directory on its
|
|
144
|
-
first Codex queue submission. See the
|
|
145
|
-
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
146
|
-
|
|
147
|
-
## Updating from 0.1.6
|
|
148
|
-
|
|
149
|
-
The upgrade preserves nodes, messages, delivery states, TAC membership, names,
|
|
150
|
-
and labels. Stop `tabus.doorbell` and `tabd`. Confirm that the ledger's `-wal`
|
|
151
|
-
file is absent or empty (0 bytes), then keep a backup before installing. A
|
|
152
|
-
remaining empty WAL or SHM file is safe. Do not continue while the WAL is
|
|
153
|
-
non-empty:
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
python -m pip install --upgrade "tabc==0.2.0"
|
|
157
|
-
# Or, when the local desktop MCP adapter is required:
|
|
158
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.0"
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Existing string TAC IDs are not converted during installation. Follow the
|
|
162
|
-
repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
|
|
163
|
-
to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
|
|
164
|
-
Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
165
|
-
|
|
166
119
|
## Commands
|
|
167
120
|
|
|
168
121
|
- `tabc register --node alice --kind codex`: register an agent.
|
|
@@ -175,6 +128,9 @@ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
|
175
128
|
Register each recipient before sending. tac creators must also join their topic.
|
|
176
129
|
Since 0.2.0, the server mints a UUID for each new tac. Use that UUID for membership,
|
|
177
130
|
sending, reading, renaming and closing; use the unique name for display and search.
|
|
131
|
+
From 0.2.3, the CLI also accepts the exact name of an open tac and prints the UUID
|
|
132
|
+
it used. A closed, missing or only similar name runs nothing and lists candidates
|
|
133
|
+
with `TAC_NAME_UNRESOLVED`. The MCP adapter still takes only the UUID.
|
|
178
134
|
Run `tabc --help` for available commands. The local server is `tabd`; management
|
|
179
135
|
commands are provided separately by `tabm`.
|
|
180
136
|
The source repository carries the operating guide and the security notes.
|
|
@@ -9,6 +9,16 @@ This is an experimental package. It is not a security boundary between agents
|
|
|
9
9
|
using the same operating-system account. Message delivery does not imply that
|
|
10
10
|
an agent read, understood, or completed a request.
|
|
11
11
|
|
|
12
|
+
## Updates
|
|
13
|
+
|
|
14
|
+
See the [changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md)
|
|
15
|
+
for changes and compatibility notes, or
|
|
16
|
+
[GitHub releases](https://github.com/jultabc/tabc/releases) for published versions.
|
|
17
|
+
|
|
18
|
+
Before updating an existing installation, follow the
|
|
19
|
+
[installation and upgrade instructions](https://github.com/jultabc/tabc#readme).
|
|
20
|
+
Keep a backup and follow the stop, install, and restart sequence for your version.
|
|
21
|
+
|
|
12
22
|
## Install
|
|
13
23
|
|
|
14
24
|
```bash
|
|
@@ -63,64 +73,6 @@ from the `pull --mode full` detail block, or a unique prefix of it.
|
|
|
63
73
|
Terminal notification is a separate process and does not start with the
|
|
64
74
|
install; see the repository's guide before turning it on.
|
|
65
75
|
|
|
66
|
-
## Updating from 0.2.1
|
|
67
|
-
|
|
68
|
-
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
69
|
-
TACs, routes, and Codex queue receipts are kept. Stop `tabus.doorbell`, install,
|
|
70
|
-
restart `tabd`, then start one `tabus.doorbell` from the same installation:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
python -m pip install --upgrade "tabc==0.2.2"
|
|
74
|
-
# Or, when the local desktop MCP adapter is required:
|
|
75
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.2"
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
`--kind` is now case-insensitive; `--kind Codex` selects the same Codex queue
|
|
79
|
-
route as `--kind codex`. A detached command no longer needs a recognized agent
|
|
80
|
-
product: its immediate session-boundary parent qualifies when it owns the
|
|
81
|
-
terminal foreground and reads standard input from it. The check proves terminal
|
|
82
|
-
ownership, not that the program is an agent. Register again from the intended
|
|
83
|
-
terminal and confirm one notification arrives. Codex queue rules are otherwise
|
|
84
|
-
unchanged. See the
|
|
85
|
-
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
86
|
-
|
|
87
|
-
## Updating from 0.2.0
|
|
88
|
-
|
|
89
|
-
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
90
|
-
TACs, and routes are kept. Stop `tabus.doorbell`, install, restart `tabd`, then
|
|
91
|
-
start one `tabus.doorbell` from the same installation:
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
python -m pip install --upgrade "tabc==0.2.1"
|
|
95
|
-
# Or, when the local desktop MCP adapter is required:
|
|
96
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.1"
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
`register` now requires `--kind`. Update scripts that omit it. Codex agents
|
|
100
|
-
register again from each conversation after the notifier is replaced. The
|
|
101
|
-
notifier creates `codex_queue_receipts.db` in the tabc home directory on its
|
|
102
|
-
first Codex queue submission. See the
|
|
103
|
-
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
104
|
-
|
|
105
|
-
## Updating from 0.1.6
|
|
106
|
-
|
|
107
|
-
The upgrade preserves nodes, messages, delivery states, TAC membership, names,
|
|
108
|
-
and labels. Stop `tabus.doorbell` and `tabd`. Confirm that the ledger's `-wal`
|
|
109
|
-
file is absent or empty (0 bytes), then keep a backup before installing. A
|
|
110
|
-
remaining empty WAL or SHM file is safe. Do not continue while the WAL is
|
|
111
|
-
non-empty:
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
python -m pip install --upgrade "tabc==0.2.0"
|
|
115
|
-
# Or, when the local desktop MCP adapter is required:
|
|
116
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.0"
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Existing string TAC IDs are not converted during installation. Follow the
|
|
120
|
-
repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
|
|
121
|
-
to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
|
|
122
|
-
Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
123
|
-
|
|
124
76
|
## Commands
|
|
125
77
|
|
|
126
78
|
- `tabc register --node alice --kind codex`: register an agent.
|
|
@@ -133,6 +85,9 @@ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
|
133
85
|
Register each recipient before sending. tac creators must also join their topic.
|
|
134
86
|
Since 0.2.0, the server mints a UUID for each new tac. Use that UUID for membership,
|
|
135
87
|
sending, reading, renaming and closing; use the unique name for display and search.
|
|
88
|
+
From 0.2.3, the CLI also accepts the exact name of an open tac and prints the UUID
|
|
89
|
+
it used. A closed, missing or only similar name runs nothing and lists candidates
|
|
90
|
+
with `TAC_NAME_UNRESOLVED`. The MCP adapter still takes only the UUID.
|
|
136
91
|
Run `tabc --help` for available commands. The local server is `tabd`; management
|
|
137
92
|
commands are provided separately by `tabm`.
|
|
138
93
|
The source repository carries the operating guide and the security notes.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tabc"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "A shared inbox for AI agents and programs on one machine"
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -40,6 +40,7 @@ mcp = ["mcp>=1.27.1,<2; python_version >= '3.10'"]
|
|
|
40
40
|
Homepage = "https://tabc.sh"
|
|
41
41
|
Source = "https://github.com/jultabc/tabc"
|
|
42
42
|
Documentation = "https://github.com/jultabc/tabc/blob/main/GUIDE.md"
|
|
43
|
+
Changelog = "https://github.com/jultabc/tabc/blob/main/CHANGELOG.md"
|
|
43
44
|
Issues = "https://github.com/jultabc/tabc/issues"
|
|
44
45
|
|
|
45
46
|
[project.scripts]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A shared inbox for AI agents and programs on one machine
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -27,6 +27,7 @@ License: MIT License
|
|
|
27
27
|
Project-URL: Homepage, https://tabc.sh
|
|
28
28
|
Project-URL: Source, https://github.com/jultabc/tabc
|
|
29
29
|
Project-URL: Documentation, https://github.com/jultabc/tabc/blob/main/GUIDE.md
|
|
30
|
+
Project-URL: Changelog, https://github.com/jultabc/tabc/blob/main/CHANGELOG.md
|
|
30
31
|
Project-URL: Issues, https://github.com/jultabc/tabc/issues
|
|
31
32
|
Keywords: agents,messaging,ipc,cli
|
|
32
33
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -51,6 +52,16 @@ This is an experimental package. It is not a security boundary between agents
|
|
|
51
52
|
using the same operating-system account. Message delivery does not imply that
|
|
52
53
|
an agent read, understood, or completed a request.
|
|
53
54
|
|
|
55
|
+
## Updates
|
|
56
|
+
|
|
57
|
+
See the [changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md)
|
|
58
|
+
for changes and compatibility notes, or
|
|
59
|
+
[GitHub releases](https://github.com/jultabc/tabc/releases) for published versions.
|
|
60
|
+
|
|
61
|
+
Before updating an existing installation, follow the
|
|
62
|
+
[installation and upgrade instructions](https://github.com/jultabc/tabc#readme).
|
|
63
|
+
Keep a backup and follow the stop, install, and restart sequence for your version.
|
|
64
|
+
|
|
54
65
|
## Install
|
|
55
66
|
|
|
56
67
|
```bash
|
|
@@ -105,64 +116,6 @@ from the `pull --mode full` detail block, or a unique prefix of it.
|
|
|
105
116
|
Terminal notification is a separate process and does not start with the
|
|
106
117
|
install; see the repository's guide before turning it on.
|
|
107
118
|
|
|
108
|
-
## Updating from 0.2.1
|
|
109
|
-
|
|
110
|
-
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
111
|
-
TACs, routes, and Codex queue receipts are kept. Stop `tabus.doorbell`, install,
|
|
112
|
-
restart `tabd`, then start one `tabus.doorbell` from the same installation:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
python -m pip install --upgrade "tabc==0.2.2"
|
|
116
|
-
# Or, when the local desktop MCP adapter is required:
|
|
117
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.2"
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`--kind` is now case-insensitive; `--kind Codex` selects the same Codex queue
|
|
121
|
-
route as `--kind codex`. A detached command no longer needs a recognized agent
|
|
122
|
-
product: its immediate session-boundary parent qualifies when it owns the
|
|
123
|
-
terminal foreground and reads standard input from it. The check proves terminal
|
|
124
|
-
ownership, not that the program is an agent. Register again from the intended
|
|
125
|
-
terminal and confirm one notification arrives. Codex queue rules are otherwise
|
|
126
|
-
unchanged. See the
|
|
127
|
-
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
128
|
-
|
|
129
|
-
## Updating from 0.2.0
|
|
130
|
-
|
|
131
|
-
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
132
|
-
TACs, and routes are kept. Stop `tabus.doorbell`, install, restart `tabd`, then
|
|
133
|
-
start one `tabus.doorbell` from the same installation:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
python -m pip install --upgrade "tabc==0.2.1"
|
|
137
|
-
# Or, when the local desktop MCP adapter is required:
|
|
138
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.1"
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
`register` now requires `--kind`. Update scripts that omit it. Codex agents
|
|
142
|
-
register again from each conversation after the notifier is replaced. The
|
|
143
|
-
notifier creates `codex_queue_receipts.db` in the tabc home directory on its
|
|
144
|
-
first Codex queue submission. See the
|
|
145
|
-
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
146
|
-
|
|
147
|
-
## Updating from 0.1.6
|
|
148
|
-
|
|
149
|
-
The upgrade preserves nodes, messages, delivery states, TAC membership, names,
|
|
150
|
-
and labels. Stop `tabus.doorbell` and `tabd`. Confirm that the ledger's `-wal`
|
|
151
|
-
file is absent or empty (0 bytes), then keep a backup before installing. A
|
|
152
|
-
remaining empty WAL or SHM file is safe. Do not continue while the WAL is
|
|
153
|
-
non-empty:
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
python -m pip install --upgrade "tabc==0.2.0"
|
|
157
|
-
# Or, when the local desktop MCP adapter is required:
|
|
158
|
-
python -m pip install --upgrade "tabc[mcp]==0.2.0"
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Existing string TAC IDs are not converted during installation. Follow the
|
|
162
|
-
repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
|
|
163
|
-
to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
|
|
164
|
-
Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
165
|
-
|
|
166
119
|
## Commands
|
|
167
120
|
|
|
168
121
|
- `tabc register --node alice --kind codex`: register an agent.
|
|
@@ -175,6 +128,9 @@ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
|
175
128
|
Register each recipient before sending. tac creators must also join their topic.
|
|
176
129
|
Since 0.2.0, the server mints a UUID for each new tac. Use that UUID for membership,
|
|
177
130
|
sending, reading, renaming and closing; use the unique name for display and search.
|
|
131
|
+
From 0.2.3, the CLI also accepts the exact name of an open tac and prints the UUID
|
|
132
|
+
it used. A closed, missing or only similar name runs nothing and lists candidates
|
|
133
|
+
with `TAC_NAME_UNRESOLVED`. The MCP adapter still takes only the UUID.
|
|
178
134
|
Run `tabc --help` for available commands. The local server is `tabd`; management
|
|
179
135
|
commands are provided separately by `tabm`.
|
|
180
136
|
The source repository carries the operating guide and the security notes.
|
|
@@ -74,7 +74,7 @@ DB_PATH = os.environ.get(
|
|
|
74
74
|
# drifted once already — 0.1.0.post1 shipped while this said 0.1.0. Bump
|
|
75
75
|
# both together; tests/test_version_agreement.py fails the build otherwise.
|
|
76
76
|
# Do not copy the number into documentation.
|
|
77
|
-
__version__ = "0.2.
|
|
77
|
+
__version__ = "0.2.4"
|
|
78
78
|
PROTOCOL_VERSION = "tabus.v1"
|
|
79
79
|
|
|
80
80
|
# TTLs. Past these, a derived status counts as old.
|
|
@@ -94,6 +94,7 @@ MAPPING = {
|
|
|
94
94
|
"tac_link": "/tac_link",
|
|
95
95
|
"tac_rename": "/tac_rename",
|
|
96
96
|
"tac_check": "/tac_check",
|
|
97
|
+
"tac_lookup": "/tac_lookup",
|
|
97
98
|
# name uses only the register endpoint; setting the session title is local
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -436,10 +437,27 @@ def _refusal_line(r):
|
|
|
436
437
|
|
|
437
438
|
|
|
438
439
|
def fn_send(a):
|
|
440
|
+
# 🔴 --id lets a caller retry without storing twice. The daemon treats the same id
|
|
441
|
+
# with the same envelope as a resend and the same id with a changed envelope as
|
|
442
|
+
# a conflict, so the caller keeps the id and the whole envelope from before the
|
|
443
|
+
# first attempt. The daemon does not compare the sending mode itself (tac or
|
|
444
|
+
# broadcast), only the resolved recipients, so the caller keeps that fixed too.
|
|
445
|
+
# Full UUIDs only, written in canonical form, so that one id typed two ways
|
|
446
|
+
# cannot become two messages. Checked first, before the tac lookup or any
|
|
447
|
+
# other request.
|
|
448
|
+
mid = str(uuid.uuid4())
|
|
449
|
+
if getattr(a, "id", None) is not None:
|
|
450
|
+
try:
|
|
451
|
+
mid = str(uuid.UUID(a.id))
|
|
452
|
+
except ValueError:
|
|
453
|
+
print("failed: --id must be a full message UUID")
|
|
454
|
+
sys.exit(1)
|
|
439
455
|
body = a.body
|
|
440
456
|
if a.body_file:
|
|
441
457
|
body = open(a.body_file, encoding="utf-8").read()
|
|
442
458
|
group = getattr(a, "tac", None)
|
|
459
|
+
if group:
|
|
460
|
+
group = _designate_tac(group, a.sender)
|
|
443
461
|
bcast = getattr(a, "broadcast", False)
|
|
444
462
|
requested = _recipients(a.to)
|
|
445
463
|
# 🔴 Recipients come from --to, --tac, or --broadcast. With none of the three
|
|
@@ -451,7 +469,6 @@ def fn_send(a):
|
|
|
451
469
|
# send is authenticated by the request signature (call() signs every request),
|
|
452
470
|
# and the daemon requires `from` to equal the acting node, so no message-level
|
|
453
471
|
# signature is sent — a forged sender cannot get past the request auth.
|
|
454
|
-
mid = str(uuid.uuid4())
|
|
455
472
|
payload = {
|
|
456
473
|
"from": a.sender,
|
|
457
474
|
"to": requested,
|
|
@@ -806,6 +823,64 @@ def fn_restore(a):
|
|
|
806
823
|
sys.exit(1)
|
|
807
824
|
|
|
808
825
|
|
|
826
|
+
def _tac_state(row):
|
|
827
|
+
closed = row.get("closed_at")
|
|
828
|
+
return f"closed {closed[:10]}" if closed else "open"
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
def _designate_tac(given, node):
|
|
832
|
+
"""A tac argument → the identifier to send. A name is looked up here, in the client.
|
|
833
|
+
|
|
834
|
+
🔴 The server still takes only the identifier. A name is turned into one before the
|
|
835
|
+
request, and the identifier used is printed on stderr, so a later rename cannot
|
|
836
|
+
make the record ambiguous. Only an exact name of an open tac is used. A closed tac,
|
|
837
|
+
or no exact match, prints the candidates with identifier and open/closed and
|
|
838
|
+
exits 2 without doing anything. A partial name is never chosen for the caller.
|
|
839
|
+
"""
|
|
840
|
+
from .tac_identity import is_uuid_id
|
|
841
|
+
|
|
842
|
+
if not isinstance(given, str) or not given.strip() or is_uuid_id(given):
|
|
843
|
+
return given
|
|
844
|
+
try:
|
|
845
|
+
# Another UUID spelling (uppercase, braces, 32 hex digits) of an identifier.
|
|
846
|
+
spelled = str(uuid.UUID(given.strip()))
|
|
847
|
+
except ValueError:
|
|
848
|
+
spelled = None
|
|
849
|
+
from urllib.parse import urlencode
|
|
850
|
+
|
|
851
|
+
# 🔴 The name is asked first, even for a string uuid.UUID can read: a tac may be
|
|
852
|
+
# named "0123456789abcdef0123456789abcdef", and that name must not turn into
|
|
853
|
+
# some other tac's identifier (jack 4c84df2c). Only with no tac of that exact
|
|
854
|
+
# name is the string read as an identifier spelling.
|
|
855
|
+
code, r = call("GET", MAPPING["tac_lookup"] + "?" + urlencode({"name": given}), node=node)
|
|
856
|
+
if code == 404:
|
|
857
|
+
# 🔴 A server before this lookup existed: send the string as typed, and the
|
|
858
|
+
# server answers TAC_ID_INVALID as it always did.
|
|
859
|
+
return given
|
|
860
|
+
if code != 200 or r.get("error"):
|
|
861
|
+
print(f"failed: tac lookup: {r.get('error') or r}", file=sys.stderr)
|
|
862
|
+
sys.exit(1)
|
|
863
|
+
exact, similar = r.get("exact"), r.get("similar") or []
|
|
864
|
+
if exact is None and spelled is not None:
|
|
865
|
+
return spelled
|
|
866
|
+
if exact and not exact.get("closed_at"):
|
|
867
|
+
print(f"tac {exact['name']} = {exact['tac_id']}", file=sys.stderr)
|
|
868
|
+
return exact["tac_id"]
|
|
869
|
+
rows = ([exact] if exact else []) + similar
|
|
870
|
+
if exact:
|
|
871
|
+
print(f"tac {exact['name']} is closed; nothing was done. Use its identifier to read it:",
|
|
872
|
+
file=sys.stderr)
|
|
873
|
+
elif rows:
|
|
874
|
+
print(f"no tac is named {given!r}; nothing was done. Similar names:", file=sys.stderr)
|
|
875
|
+
else:
|
|
876
|
+
print(f"no tac is named {given!r}; nothing was done (list them with: tabc tac ls)",
|
|
877
|
+
file=sys.stderr)
|
|
878
|
+
for row in rows:
|
|
879
|
+
print(f" {row['name']:<24} {row['tac_id']} {_tac_state(row)}", file=sys.stderr)
|
|
880
|
+
print(" code TAC_NAME_UNRESOLVED", file=sys.stderr)
|
|
881
|
+
sys.exit(2)
|
|
882
|
+
|
|
883
|
+
|
|
809
884
|
def fn_tac(a):
|
|
810
885
|
"""tac — a named topic for group delivery.
|
|
811
886
|
|
|
@@ -818,6 +893,12 @@ def fn_tac(a):
|
|
|
818
893
|
from another, which covers direction, forks, and merges.
|
|
819
894
|
"""
|
|
820
895
|
action = a.action
|
|
896
|
+
# 🔴 Every subcommand that takes an existing tac accepts a name here and sends the
|
|
897
|
+
# identifier. create takes a new name and is not looked up.
|
|
898
|
+
if action in ("show", "add", "rm", "rename", "close", "link") or (action == "search" and a.tac):
|
|
899
|
+
a.tac = _designate_tac(a.tac, a.node)
|
|
900
|
+
if action == "link":
|
|
901
|
+
a.member = _designate_tac(a.member, a.node)
|
|
821
902
|
if action == "search":
|
|
822
903
|
from urllib.parse import urlencode
|
|
823
904
|
|
|
@@ -1135,7 +1216,7 @@ COMMANDS = {
|
|
|
1135
1216
|
("--tac",),
|
|
1136
1217
|
dict(
|
|
1137
1218
|
default=None,
|
|
1138
|
-
help="send to a tac, fanning out to every member (instead of --to)",
|
|
1219
|
+
help="send to a tac, by identifier or exact name, fanning out to every member (instead of --to)",
|
|
1139
1220
|
),
|
|
1140
1221
|
),
|
|
1141
1222
|
(
|
|
@@ -1149,6 +1230,13 @@ COMMANDS = {
|
|
|
1149
1230
|
(("--body",), dict()),
|
|
1150
1231
|
(("--body-file",), dict()),
|
|
1151
1232
|
(("--priority",), dict(default="next", choices=["now", "next", "batch"])),
|
|
1233
|
+
(
|
|
1234
|
+
("--id",),
|
|
1235
|
+
dict(
|
|
1236
|
+
default=None,
|
|
1237
|
+
help="message UUID for a retry-safe send. The same id with the same envelope is not stored twice; with a changed envelope it is refused as a conflict",
|
|
1238
|
+
),
|
|
1239
|
+
),
|
|
1152
1240
|
],
|
|
1153
1241
|
),
|
|
1154
1242
|
"who": (fn_who, "who is present, and how much is pending", [(("--node",), dict(default=None, help="acting node; falls back to TABC_NODE"))]),
|
|
@@ -1235,8 +1323,9 @@ COMMANDS = {
|
|
|
1235
1323
|
"close <tac> --summary | link <child> <parent>",
|
|
1236
1324
|
),
|
|
1237
1325
|
),
|
|
1238
|
-
(("tac",), dict(nargs="?", help="the tac, by its identifier (not needed for ls); "
|
|
1239
|
-
"
|
|
1326
|
+
(("tac",), dict(nargs="?", help="the tac, by its identifier or its exact name (not needed for ls); "
|
|
1327
|
+
"a name is looked up and the identifier used is printed; "
|
|
1328
|
+
"for create this is the new name; for link this is the child")),
|
|
1240
1329
|
(("member",), dict(nargs="?", help="node for add and rm; for link this is the parent; "
|
|
1241
1330
|
"for rename this is the new name")),
|
|
1242
1331
|
(("--description",), dict(default=None, help="what the tac is for, for create")),
|
|
@@ -498,6 +498,27 @@ class BusHandler(BaseHTTPRequestHandler):
|
|
|
498
498
|
con.close()
|
|
499
499
|
return self._json(200, {"hits": hits})
|
|
500
500
|
|
|
501
|
+
if path == "/tac_lookup":
|
|
502
|
+
# 🔴 Read-only: which tac carries this name now. Same scope as /tacs. The
|
|
503
|
+
# client sends the identifier this returns; every other endpoint still
|
|
504
|
+
# takes only the identifier.
|
|
505
|
+
if not self._authorized():
|
|
506
|
+
return self._json(401, {"error": "token"})
|
|
507
|
+
from urllib.parse import parse_qs
|
|
508
|
+
from .tac_lookup import lookup
|
|
509
|
+
|
|
510
|
+
params = parse_qs(query, keep_blank_values=True)
|
|
511
|
+
if any(len(values) != 1 for values in params.values()):
|
|
512
|
+
return self._json(400, {"error": "duplicate query parameters"})
|
|
513
|
+
con = self._connect()
|
|
514
|
+
try:
|
|
515
|
+
exact, similar = lookup(con, params.get("name", [None])[0])
|
|
516
|
+
except (ValueError, TypeError) as exc:
|
|
517
|
+
return self._json(400, {"error": str(exc)})
|
|
518
|
+
finally:
|
|
519
|
+
con.close()
|
|
520
|
+
return self._json(200, {"exact": exact, "similar": similar})
|
|
521
|
+
|
|
501
522
|
if path == "/tac_messages":
|
|
502
523
|
# 🔴 tac contents, read-only, and member-only: a tac is a conversation you
|
|
503
524
|
# read by joining it, so only its members see the body. A non-member is
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Find a tac by name, read-only. The client turns a typed name into an identifier here.
|
|
2
|
+
|
|
3
|
+
🔴 Designation stays by identifier. This endpoint does not make a name a designation:
|
|
4
|
+
every other endpoint still refuses a name with TAC_ID_INVALID. It answers one question,
|
|
5
|
+
"which tac carries this name now", so the client can print the identifier it is about
|
|
6
|
+
to use and then send that identifier. A rename moves the name; the identifier the
|
|
7
|
+
client printed is what the request used.
|
|
8
|
+
|
|
9
|
+
Names are unique on the folded key (ux_tacs_name_key), closed tacs included, so an
|
|
10
|
+
exact match is at most one row. `similar` lists the rows whose folded name contains
|
|
11
|
+
the folded query, for a caller who typed part of a name. Neither changes state.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .tac_identity import name_key
|
|
15
|
+
|
|
16
|
+
SIMILAR_LIMIT = 20
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _row(r):
|
|
20
|
+
return {"tac_id": r["tac_id"], "name": r["name"], "label": r["label"],
|
|
21
|
+
"closed_at": r["closed_at"]}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def lookup(con, name, limit=SIMILAR_LIMIT):
|
|
25
|
+
"""(exact row or None, similar rows). Raises ValueError on a bad query."""
|
|
26
|
+
if not isinstance(name, str) or not name.strip() or len(name) > 200:
|
|
27
|
+
raise ValueError("name must contain 1..200 characters")
|
|
28
|
+
columns = {row[1] for row in con.execute("PRAGMA table_info(tacs)")}
|
|
29
|
+
# 🔴 A ledger before the identity migration has no name column. There is nothing
|
|
30
|
+
# to look up there: the identifier is the string a caller types.
|
|
31
|
+
if not {"name", "name_key"} <= columns:
|
|
32
|
+
return None, []
|
|
33
|
+
key = name_key(name.strip())
|
|
34
|
+
rows = con.execute(
|
|
35
|
+
"SELECT tac_id, name, label, closed_at, name_key FROM tacs ORDER BY name"
|
|
36
|
+
).fetchall()
|
|
37
|
+
exact = next((r for r in rows if r["name_key"] == key), None)
|
|
38
|
+
# 🔴 Compared on name_key in Python, as tac_store.search_name does: sqlite lower()
|
|
39
|
+
# folds ASCII only.
|
|
40
|
+
similar = [r for r in rows if r["name_key"] and key in r["name_key"]
|
|
41
|
+
and (exact is None or r["tac_id"] != exact["tac_id"])][:limit]
|
|
42
|
+
return (_row(exact) if exact else None), [_row(r) for r in similar]
|
|
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
|
|
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
|