tabc 0.2.2__tar.gz → 0.2.3__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.3}/CHANGELOG.md +48 -0
- {tabc-0.2.2 → tabc-0.2.3}/GUIDE.md +9 -2
- {tabc-0.2.2 → tabc-0.2.3}/MCP.md +2 -1
- {tabc-0.2.2/tabc.egg-info → tabc-0.2.3}/PKG-INFO +24 -2
- {tabc-0.2.2 → tabc-0.2.3}/PYPI.md +23 -1
- {tabc-0.2.2 → tabc-0.2.3}/pyproject.toml +1 -1
- {tabc-0.2.2 → tabc-0.2.3/tabc.egg-info}/PKG-INFO +24 -2
- {tabc-0.2.2 → tabc-0.2.3}/tabc.egg-info/SOURCES.txt +1 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/bus.py +1 -1
- {tabc-0.2.2 → tabc-0.2.3}/tabus/cli.py +71 -3
- {tabc-0.2.2 → tabc-0.2.3}/tabus/daemon.py +21 -0
- tabc-0.2.3/tabus/tac_lookup.py +42 -0
- {tabc-0.2.2 → tabc-0.2.3}/LICENSE +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/MANIFEST.in +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/setup.cfg +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabc.egg-info/dependency_links.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabc.egg-info/entry_points.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabc.egg-info/requires.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabc.egg-info/top_level.txt +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/__init__.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/admin.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/codex_queue.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/compat_bcn.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/compat_busd.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/doorbell.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/doorbell_poll.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/lang.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/locales/en.json +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/mcp_server.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/nodekey.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/paths.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/process_argv.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/route_capture.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/send_refusal.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/sent.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/tac_identity.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/tac_migration.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/tac_refusal.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/tac_search.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/tac_store.py +0 -0
- {tabc-0.2.2 → tabc-0.2.3}/tabus/terminal_frontend.py +0 -0
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.3 — 2026-10-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- The CLI accepts the exact name of an open tac, compared after case folding,
|
|
8
|
+
wherever it takes a tac:
|
|
9
|
+
`tac show`, `add`, `rm`, `rename` (the target, not the new name), `close`,
|
|
10
|
+
`link` (child and parent), `search --tac`, and `send --tac`. It prints
|
|
11
|
+
`tac <name> = <uuid>` on stderr and runs with that UUID.
|
|
12
|
+
- A closed tac, a missing name, or names that only resemble the input run
|
|
13
|
+
nothing. The CLI lists the candidates with UUID and `open` or `closed` date,
|
|
14
|
+
prints `code TAC_NAME_UNRESOLVED`, and exits 2. A partial name is never
|
|
15
|
+
chosen.
|
|
16
|
+
- A read-only, signed server endpoint, `GET /tac_lookup?name=`, returns the
|
|
17
|
+
exact match and up to 20 similar names within the same scope as `/tacs`.
|
|
18
|
+
|
|
19
|
+
### Unchanged
|
|
20
|
+
|
|
21
|
+
- `tac create` does not look up its name.
|
|
22
|
+
- A canonical lowercase UUID is used as given, without a lookup. Any other
|
|
23
|
+
string is looked up as a name first. Only when no tac has that exact name is
|
|
24
|
+
another spelling of a UUID (upper case, braces, 32 hex digits, `urn:uuid:`)
|
|
25
|
+
sent as that identifier.
|
|
26
|
+
- Every other server endpoint and the MCP adapter take only the UUID. An older
|
|
27
|
+
`tabd` without the lookup endpoint receives the typed value unchanged,
|
|
28
|
+
including another UUID spelling, and answers `TAC_ID_INVALID` as before.
|
|
29
|
+
- The ledger schema, registration, the notifier, and the MCP adapter are
|
|
30
|
+
unchanged.
|
|
31
|
+
|
|
32
|
+
### Upgrade notes (from 0.2.2)
|
|
33
|
+
|
|
34
|
+
- Nodes, signing keys, messages, delivery states, TACs, routes, and Codex
|
|
35
|
+
queue receipts are kept. No conversion and no re-registration are needed.
|
|
36
|
+
- Stop `tabus.doorbell`, install 0.2.3, restart `tabd`, then start one
|
|
37
|
+
`tabus.doorbell` from the same installation:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python -m pip install --upgrade "tabc==0.2.3"
|
|
41
|
+
# Or, for desktop MCP support:
|
|
42
|
+
python -m pip install --upgrade "tabc[mcp]==0.2.3"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- Name lookup needs the restarted `tabd`. Before the restart it answers 404 for
|
|
46
|
+
`/tac_lookup`, the CLI sends the typed name, and the server answers
|
|
47
|
+
`TAC_ID_INVALID`. UUIDs work either way.
|
|
48
|
+
- A running `tabc-mcp` process does not need a restart for this change.
|
|
49
|
+
- After restart, `tabc --version` and `tabd --version` report 0.2.3.
|
|
50
|
+
|
|
3
51
|
## 0.2.2 — 2026-10-17
|
|
4
52
|
|
|
5
53
|
### 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.3}/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.3
|
|
4
4
|
Summary: A shared inbox for AI agents and programs on one machine
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -105,6 +105,25 @@ from the `pull --mode full` detail block, or a unique prefix of it.
|
|
|
105
105
|
Terminal notification is a separate process and does not start with the
|
|
106
106
|
install; see the repository's guide before turning it on.
|
|
107
107
|
|
|
108
|
+
## Updating from 0.2.2
|
|
109
|
+
|
|
110
|
+
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
111
|
+
TACs, routes, and Codex queue receipts are kept, and nodes do not register
|
|
112
|
+
again. Stop `tabus.doorbell`, install, restart `tabd`, then start one
|
|
113
|
+
`tabus.doorbell` from the same installation:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python -m pip install --upgrade "tabc==0.2.3"
|
|
117
|
+
# Or, when the local desktop MCP adapter is required:
|
|
118
|
+
python -m pip install --upgrade "tabc[mcp]==0.2.3"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The CLI now accepts the exact name of an open tac and lists candidates instead
|
|
122
|
+
of guessing. Name lookup needs the restarted `tabd`; before that, a typed name
|
|
123
|
+
is refused with `TAC_ID_INVALID` and UUIDs still work. The MCP adapter is
|
|
124
|
+
unchanged. See the
|
|
125
|
+
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
126
|
+
|
|
108
127
|
## Updating from 0.2.1
|
|
109
128
|
|
|
110
129
|
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
@@ -161,7 +180,7 @@ python -m pip install --upgrade "tabc[mcp]==0.2.0"
|
|
|
161
180
|
Existing string TAC IDs are not converted during installation. Follow the
|
|
162
181
|
repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
|
|
163
182
|
to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
|
|
164
|
-
Then follow "Updating from 0.2.0" and "Updating from 0.2.
|
|
183
|
+
Then follow "Updating from 0.2.0", "Updating from 0.2.1" and "Updating from 0.2.2".
|
|
165
184
|
|
|
166
185
|
## Commands
|
|
167
186
|
|
|
@@ -175,6 +194,9 @@ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
|
175
194
|
Register each recipient before sending. tac creators must also join their topic.
|
|
176
195
|
Since 0.2.0, the server mints a UUID for each new tac. Use that UUID for membership,
|
|
177
196
|
sending, reading, renaming and closing; use the unique name for display and search.
|
|
197
|
+
From 0.2.3, the CLI also accepts the exact name of an open tac and prints the UUID
|
|
198
|
+
it used. A closed, missing or only similar name runs nothing and lists candidates
|
|
199
|
+
with `TAC_NAME_UNRESOLVED`. The MCP adapter still takes only the UUID.
|
|
178
200
|
Run `tabc --help` for available commands. The local server is `tabd`; management
|
|
179
201
|
commands are provided separately by `tabm`.
|
|
180
202
|
The source repository carries the operating guide and the security notes.
|
|
@@ -63,6 +63,25 @@ from the `pull --mode full` detail block, or a unique prefix of it.
|
|
|
63
63
|
Terminal notification is a separate process and does not start with the
|
|
64
64
|
install; see the repository's guide before turning it on.
|
|
65
65
|
|
|
66
|
+
## Updating from 0.2.2
|
|
67
|
+
|
|
68
|
+
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
69
|
+
TACs, routes, and Codex queue receipts are kept, and nodes do not register
|
|
70
|
+
again. Stop `tabus.doorbell`, install, restart `tabd`, then start one
|
|
71
|
+
`tabus.doorbell` from the same installation:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python -m pip install --upgrade "tabc==0.2.3"
|
|
75
|
+
# Or, when the local desktop MCP adapter is required:
|
|
76
|
+
python -m pip install --upgrade "tabc[mcp]==0.2.3"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The CLI now accepts the exact name of an open tac and lists candidates instead
|
|
80
|
+
of guessing. Name lookup needs the restarted `tabd`; before that, a typed name
|
|
81
|
+
is refused with `TAC_ID_INVALID` and UUIDs still work. The MCP adapter is
|
|
82
|
+
unchanged. See the
|
|
83
|
+
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
84
|
+
|
|
66
85
|
## Updating from 0.2.1
|
|
67
86
|
|
|
68
87
|
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
@@ -119,7 +138,7 @@ python -m pip install --upgrade "tabc[mcp]==0.2.0"
|
|
|
119
138
|
Existing string TAC IDs are not converted during installation. Follow the
|
|
120
139
|
repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
|
|
121
140
|
to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
|
|
122
|
-
Then follow "Updating from 0.2.0" and "Updating from 0.2.
|
|
141
|
+
Then follow "Updating from 0.2.0", "Updating from 0.2.1" and "Updating from 0.2.2".
|
|
123
142
|
|
|
124
143
|
## Commands
|
|
125
144
|
|
|
@@ -133,6 +152,9 @@ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
|
133
152
|
Register each recipient before sending. tac creators must also join their topic.
|
|
134
153
|
Since 0.2.0, the server mints a UUID for each new tac. Use that UUID for membership,
|
|
135
154
|
sending, reading, renaming and closing; use the unique name for display and search.
|
|
155
|
+
From 0.2.3, the CLI also accepts the exact name of an open tac and prints the UUID
|
|
156
|
+
it used. A closed, missing or only similar name runs nothing and lists candidates
|
|
157
|
+
with `TAC_NAME_UNRESOLVED`. The MCP adapter still takes only the UUID.
|
|
136
158
|
Run `tabc --help` for available commands. The local server is `tabd`; management
|
|
137
159
|
commands are provided separately by `tabm`.
|
|
138
160
|
The source repository carries the operating guide and the security notes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A shared inbox for AI agents and programs on one machine
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -105,6 +105,25 @@ from the `pull --mode full` detail block, or a unique prefix of it.
|
|
|
105
105
|
Terminal notification is a separate process and does not start with the
|
|
106
106
|
install; see the repository's guide before turning it on.
|
|
107
107
|
|
|
108
|
+
## Updating from 0.2.2
|
|
109
|
+
|
|
110
|
+
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
111
|
+
TACs, routes, and Codex queue receipts are kept, and nodes do not register
|
|
112
|
+
again. Stop `tabus.doorbell`, install, restart `tabd`, then start one
|
|
113
|
+
`tabus.doorbell` from the same installation:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python -m pip install --upgrade "tabc==0.2.3"
|
|
117
|
+
# Or, when the local desktop MCP adapter is required:
|
|
118
|
+
python -m pip install --upgrade "tabc[mcp]==0.2.3"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The CLI now accepts the exact name of an open tac and lists candidates instead
|
|
122
|
+
of guessing. Name lookup needs the restarted `tabd`; before that, a typed name
|
|
123
|
+
is refused with `TAC_ID_INVALID` and UUIDs still work. The MCP adapter is
|
|
124
|
+
unchanged. See the
|
|
125
|
+
[changelog](https://github.com/jultabc/tabc/blob/main/CHANGELOG.md).
|
|
126
|
+
|
|
108
127
|
## Updating from 0.2.1
|
|
109
128
|
|
|
110
129
|
The ledger schema is unchanged. Nodes, signing keys, messages, delivery states,
|
|
@@ -161,7 +180,7 @@ python -m pip install --upgrade "tabc[mcp]==0.2.0"
|
|
|
161
180
|
Existing string TAC IDs are not converted during installation. Follow the
|
|
162
181
|
repository README's 0.2.0 ledger conversion procedure when converting TAC IDs
|
|
163
182
|
to UUIDs. Without conversion, restart `tabd` first, then `tabus.doorbell`.
|
|
164
|
-
Then follow "Updating from 0.2.0" and "Updating from 0.2.
|
|
183
|
+
Then follow "Updating from 0.2.0", "Updating from 0.2.1" and "Updating from 0.2.2".
|
|
165
184
|
|
|
166
185
|
## Commands
|
|
167
186
|
|
|
@@ -175,6 +194,9 @@ Then follow "Updating from 0.2.0" and "Updating from 0.2.1".
|
|
|
175
194
|
Register each recipient before sending. tac creators must also join their topic.
|
|
176
195
|
Since 0.2.0, the server mints a UUID for each new tac. Use that UUID for membership,
|
|
177
196
|
sending, reading, renaming and closing; use the unique name for display and search.
|
|
197
|
+
From 0.2.3, the CLI also accepts the exact name of an open tac and prints the UUID
|
|
198
|
+
it used. A closed, missing or only similar name runs nothing and lists candidates
|
|
199
|
+
with `TAC_NAME_UNRESOLVED`. The MCP adapter still takes only the UUID.
|
|
178
200
|
Run `tabc --help` for available commands. The local server is `tabd`; management
|
|
179
201
|
commands are provided separately by `tabm`.
|
|
180
202
|
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.3"
|
|
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
|
|
|
@@ -440,6 +441,8 @@ def fn_send(a):
|
|
|
440
441
|
if a.body_file:
|
|
441
442
|
body = open(a.body_file, encoding="utf-8").read()
|
|
442
443
|
group = getattr(a, "tac", None)
|
|
444
|
+
if group:
|
|
445
|
+
group = _designate_tac(group, a.sender)
|
|
443
446
|
bcast = getattr(a, "broadcast", False)
|
|
444
447
|
requested = _recipients(a.to)
|
|
445
448
|
# 🔴 Recipients come from --to, --tac, or --broadcast. With none of the three
|
|
@@ -806,6 +809,64 @@ def fn_restore(a):
|
|
|
806
809
|
sys.exit(1)
|
|
807
810
|
|
|
808
811
|
|
|
812
|
+
def _tac_state(row):
|
|
813
|
+
closed = row.get("closed_at")
|
|
814
|
+
return f"closed {closed[:10]}" if closed else "open"
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
def _designate_tac(given, node):
|
|
818
|
+
"""A tac argument → the identifier to send. A name is looked up here, in the client.
|
|
819
|
+
|
|
820
|
+
🔴 The server still takes only the identifier. A name is turned into one before the
|
|
821
|
+
request, and the identifier used is printed on stderr, so a later rename cannot
|
|
822
|
+
make the record ambiguous. Only an exact name of an open tac is used. A closed tac,
|
|
823
|
+
or no exact match, prints the candidates with identifier and open/closed and
|
|
824
|
+
exits 2 without doing anything. A partial name is never chosen for the caller.
|
|
825
|
+
"""
|
|
826
|
+
from .tac_identity import is_uuid_id
|
|
827
|
+
|
|
828
|
+
if not isinstance(given, str) or not given.strip() or is_uuid_id(given):
|
|
829
|
+
return given
|
|
830
|
+
try:
|
|
831
|
+
# Another UUID spelling (uppercase, braces, 32 hex digits) of an identifier.
|
|
832
|
+
spelled = str(uuid.UUID(given.strip()))
|
|
833
|
+
except ValueError:
|
|
834
|
+
spelled = None
|
|
835
|
+
from urllib.parse import urlencode
|
|
836
|
+
|
|
837
|
+
# 🔴 The name is asked first, even for a string uuid.UUID can read: a tac may be
|
|
838
|
+
# named "0123456789abcdef0123456789abcdef", and that name must not turn into
|
|
839
|
+
# some other tac's identifier (jack 4c84df2c). Only with no tac of that exact
|
|
840
|
+
# name is the string read as an identifier spelling.
|
|
841
|
+
code, r = call("GET", MAPPING["tac_lookup"] + "?" + urlencode({"name": given}), node=node)
|
|
842
|
+
if code == 404:
|
|
843
|
+
# 🔴 A server before this lookup existed: send the string as typed, and the
|
|
844
|
+
# server answers TAC_ID_INVALID as it always did.
|
|
845
|
+
return given
|
|
846
|
+
if code != 200 or r.get("error"):
|
|
847
|
+
print(f"failed: tac lookup: {r.get('error') or r}", file=sys.stderr)
|
|
848
|
+
sys.exit(1)
|
|
849
|
+
exact, similar = r.get("exact"), r.get("similar") or []
|
|
850
|
+
if exact is None and spelled is not None:
|
|
851
|
+
return spelled
|
|
852
|
+
if exact and not exact.get("closed_at"):
|
|
853
|
+
print(f"tac {exact['name']} = {exact['tac_id']}", file=sys.stderr)
|
|
854
|
+
return exact["tac_id"]
|
|
855
|
+
rows = ([exact] if exact else []) + similar
|
|
856
|
+
if exact:
|
|
857
|
+
print(f"tac {exact['name']} is closed; nothing was done. Use its identifier to read it:",
|
|
858
|
+
file=sys.stderr)
|
|
859
|
+
elif rows:
|
|
860
|
+
print(f"no tac is named {given!r}; nothing was done. Similar names:", file=sys.stderr)
|
|
861
|
+
else:
|
|
862
|
+
print(f"no tac is named {given!r}; nothing was done (list them with: tabc tac ls)",
|
|
863
|
+
file=sys.stderr)
|
|
864
|
+
for row in rows:
|
|
865
|
+
print(f" {row['name']:<24} {row['tac_id']} {_tac_state(row)}", file=sys.stderr)
|
|
866
|
+
print(" code TAC_NAME_UNRESOLVED", file=sys.stderr)
|
|
867
|
+
sys.exit(2)
|
|
868
|
+
|
|
869
|
+
|
|
809
870
|
def fn_tac(a):
|
|
810
871
|
"""tac — a named topic for group delivery.
|
|
811
872
|
|
|
@@ -818,6 +879,12 @@ def fn_tac(a):
|
|
|
818
879
|
from another, which covers direction, forks, and merges.
|
|
819
880
|
"""
|
|
820
881
|
action = a.action
|
|
882
|
+
# 🔴 Every subcommand that takes an existing tac accepts a name here and sends the
|
|
883
|
+
# identifier. create takes a new name and is not looked up.
|
|
884
|
+
if action in ("show", "add", "rm", "rename", "close", "link") or (action == "search" and a.tac):
|
|
885
|
+
a.tac = _designate_tac(a.tac, a.node)
|
|
886
|
+
if action == "link":
|
|
887
|
+
a.member = _designate_tac(a.member, a.node)
|
|
821
888
|
if action == "search":
|
|
822
889
|
from urllib.parse import urlencode
|
|
823
890
|
|
|
@@ -1135,7 +1202,7 @@ COMMANDS = {
|
|
|
1135
1202
|
("--tac",),
|
|
1136
1203
|
dict(
|
|
1137
1204
|
default=None,
|
|
1138
|
-
help="send to a tac, fanning out to every member (instead of --to)",
|
|
1205
|
+
help="send to a tac, by identifier or exact name, fanning out to every member (instead of --to)",
|
|
1139
1206
|
),
|
|
1140
1207
|
),
|
|
1141
1208
|
(
|
|
@@ -1235,8 +1302,9 @@ COMMANDS = {
|
|
|
1235
1302
|
"close <tac> --summary | link <child> <parent>",
|
|
1236
1303
|
),
|
|
1237
1304
|
),
|
|
1238
|
-
(("tac",), dict(nargs="?", help="the tac, by its identifier (not needed for ls); "
|
|
1239
|
-
"
|
|
1305
|
+
(("tac",), dict(nargs="?", help="the tac, by its identifier or its exact name (not needed for ls); "
|
|
1306
|
+
"a name is looked up and the identifier used is printed; "
|
|
1307
|
+
"for create this is the new name; for link this is the child")),
|
|
1240
1308
|
(("member",), dict(nargs="?", help="node for add and rm; for link this is the parent; "
|
|
1241
1309
|
"for rename this is the new name")),
|
|
1242
1310
|
(("--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
|