sessiontools 0.3.1__tar.gz → 0.3.2__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.
- {sessiontools-0.3.1 → sessiontools-0.3.2}/PKG-INFO +88 -31
- {sessiontools-0.3.1 → sessiontools-0.3.2}/README.md +87 -30
- {sessiontools-0.3.1 → sessiontools-0.3.2}/pyproject.toml +1 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/cli.py +52 -13
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/limits.py +6 -4
- sessiontools-0.3.2/src/sessiontools/account/profile.py +147 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/status.py +139 -35
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/use.py +26 -6
- sessiontools-0.3.2/src/sessiontools/account/verify.py +419 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/band.py +1 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/cli.py +25 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/default_model_cli.py +1 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/default_model_watch.py +6 -3
- sessiontools-0.3.2/src/sessiontools/identity_block.py +169 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/listing.py +101 -6
- sessiontools-0.3.2/src/sessiontools/migrate.py +572 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/pane_state_agent.py +8 -3
- sessiontools-0.3.2/src/sessiontools/paths.py +165 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/reap.py +7 -5
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/session_info.py +52 -5
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/settings.py +214 -13
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/snapshot.py +5 -7
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/sound/__init__.py +6 -8
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/sound/engine.swift +1 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/sound/service.py +29 -8
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/__init__.py +135 -26
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/agents.py +23 -15
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/args.py +5 -3
- sessiontools-0.3.2/src/sessiontools/start/boot_identity.py +427 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/claude_settings.py +71 -5
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/default_seat.py +5 -2
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/environment.py +10 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/holders.py +89 -13
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/placement.py +35 -3
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/plan.py +69 -20
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/run.py +33 -1
- {sessiontools-0.3.1 → sessiontools-0.3.2}/LICENSE +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/__init__.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/__init__.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/keychain.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/leakscan.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/movehome.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/new.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/registry.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/setuptoken.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/tokenfile.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/account/tokenstore.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/busy.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/claude_code.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/clockline.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/completion.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/__init__.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/boot.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/breakdown.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/hooks.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/nnls.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/render.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/slash_context.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/tools.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/context_parts/transcript.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/default_model.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/get.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/ledger/__init__.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/ledger/_tiktoken_child.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/ledger/count.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/ledger/render.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/ledger/transcript.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/operator.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/pane_state.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/park.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/procs.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/py.typed +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/send.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/settle.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/sound/event.swift +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/agentfile.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/context.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/default_seat.md +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/errors.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/homefolder.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/identity.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/policy.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/record.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/resume.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/secrets.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/start/vendor.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/statusline/__init__.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/statusline/sample.json +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/statusline/statusline.sh +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/tmux.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/turn.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/verbs.py +0 -0
- {sessiontools-0.3.1 → sessiontools-0.3.2}/src/sessiontools/window.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sessiontools
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: The harness layer: sessions, status and idle, one driver per harness - a library first
|
|
5
5
|
Author: Mike Bailey
|
|
6
6
|
Author-email: Mike Bailey <mike@sessionkit.ai>
|
|
@@ -61,6 +61,8 @@ eval "$(session completion bash)"
|
|
|
61
61
|
category.
|
|
62
62
|
- `session ledger --short`: each file read and command run, by tokens.
|
|
63
63
|
- `session account status`: which Claude account this Mac is on.
|
|
64
|
+
- `session account verify`: which account the API bills for this seat's
|
|
65
|
+
token, from one 1-token request.
|
|
64
66
|
- `session start --list-agents`: the agents it can start.
|
|
65
67
|
- `session reap`: seats idle past 1h, each CLOSE or KEEP, and why. A dry
|
|
66
68
|
run.
|
|
@@ -129,8 +131,10 @@ it. Each line says what puts it there and what takes it away.
|
|
|
129
131
|
sessiontools` drops this package's entries, and `uv cache prune` drops
|
|
130
132
|
the ones nothing uses.
|
|
131
133
|
- **Folders, each made by a verb you run:**
|
|
132
|
-
- `~/.
|
|
133
|
-
`list.lock`, at the first `session list`.
|
|
134
|
+
- `~/.sessionkit/tools/state/list/`: the list cache, `snapshot.json`
|
|
135
|
+
and `list.lock`, at the first `session list`. The state folders here
|
|
136
|
+
and below are on a fresh host; a host that still has
|
|
137
|
+
`~/.session/state/` keeps using it until it is moved.
|
|
134
138
|
- `~/.session/sessions/<session-id>/`: one for each `session start`,
|
|
135
139
|
holding `launch/`, `log/settings.jsonl` and `session-id`. It also
|
|
136
140
|
gets `settings/<tool>.d/50-env.json`, one for each tool whose
|
|
@@ -139,14 +143,19 @@ it. Each line says what puts it there and what takes it away.
|
|
|
139
143
|
`log/settings.jsonl` names each variable it read, never its value.
|
|
140
144
|
The plugin's hooks and `session park` write only inside a folder
|
|
141
145
|
that is already there.
|
|
142
|
-
- `~/.
|
|
143
|
-
|
|
144
|
-
- `~/.
|
|
145
|
-
first time `session default-model enforce` changes
|
|
146
|
+
- `~/.sessionkit/tools/state/pane-state/watch.lock`: the pane signal's
|
|
147
|
+
service.
|
|
148
|
+
- `~/.sessionkit/tools/state/default-model/`: `backups/` and
|
|
149
|
+
`log.jsonl`, the first time `session default-model enforce` changes
|
|
150
|
+
settings.json.
|
|
146
151
|
- `~/.sessionkit/tools/accounts/`: `session account new` and `use`.
|
|
147
|
-
- `~/.
|
|
148
|
-
builds the sound engine there with `swiftc
|
|
149
|
-
|
|
152
|
+
- `~/.sessionkit/tools/state/sound/bin/`: `session sound engine on`
|
|
153
|
+
builds the sound engine there with `swiftc` (in
|
|
154
|
+
`~/.local/share/sessiontools/sound/bin/` while that old folder is
|
|
155
|
+
still there).
|
|
156
|
+
- `~/.sessionkit/tools/log/`: the services' logs and `reap.log`
|
|
157
|
+
(Logs, below). An older release wrote them elsewhere, and those
|
|
158
|
+
files stay where they were.
|
|
150
159
|
- `SESSIONS_DIR`, `SESSION_STATE_DIR` and `SESSIONTOOLS_SOUND_HOME`
|
|
151
160
|
move these folders when set.
|
|
152
161
|
- **Services (launchd agents):** none until you turn one on. Each `on`
|
|
@@ -188,6 +197,12 @@ it. Each line says what puts it there and what takes it away.
|
|
|
188
197
|
With `model.default` set, `session default-model enforce` (and the
|
|
189
198
|
unit or `watch` that runs it) rewrites the top-level `"model"` in
|
|
190
199
|
`~/.claude/settings.json`, after a backup.
|
|
200
|
+
- **Not changed by an upgrade: a live tmux server.** One born inside the
|
|
201
|
+
default seat by sessiontools 0.3.1 or earlier still hands
|
|
202
|
+
`CLAUDE_CODE_AGENT=claude` to every pane it makes, so a raw `claude`
|
|
203
|
+
there hears the seat's mail. Restarting that server, or running
|
|
204
|
+
`tmux set-environment -g -u CLAUDE_CODE_AGENT` on it, is yours to do
|
|
205
|
+
([the default seat][default-seat]).
|
|
191
206
|
|
|
192
207
|
## Uninstall
|
|
193
208
|
|
|
@@ -208,23 +223,27 @@ uv tool uninstall sessiontools
|
|
|
208
223
|
```
|
|
209
224
|
|
|
210
225
|
That leaves what the verbs made, listed above. The plugin's cached
|
|
211
|
-
copy
|
|
212
|
-
alone. To delete them:
|
|
226
|
+
copy and the engine's folder are sessiontools' alone. To delete them:
|
|
213
227
|
|
|
214
228
|
```bash
|
|
215
229
|
rm -rf "${HOME:?}/.claude/plugins/cache/sessiontools"
|
|
216
|
-
rm -rf "${HOME:?}/Library/Logs/sessiontools"
|
|
217
230
|
rm -rf "${HOME:?}/.local/share/sessiontools"
|
|
231
|
+
rm -rf "${HOME:?}/.sessionkit/tools/state/sound"
|
|
218
232
|
```
|
|
219
233
|
|
|
234
|
+
The logs are left in place: the new ones in `~/.sessionkit/tools/log/`,
|
|
235
|
+
and an older release's in `~/Library/Logs/sessiontools/`. Nothing here
|
|
236
|
+
deletes from `~/Library/Logs`.
|
|
237
|
+
|
|
220
238
|
uv's cache stays too, with the other things uv installed; `uv cache
|
|
221
239
|
clean sessiontools` drops this package's entries.
|
|
222
240
|
|
|
223
241
|
The rest is your data: delete it by hand only if you want it gone.
|
|
224
|
-
`~/.sessionkit/tools/` holds your settings
|
|
225
|
-
accounts folder keeps the backups of every token `session
|
|
226
|
-
replaced. `~/.session/` holds the session folders and
|
|
227
|
-
|
|
242
|
+
`~/.sessionkit/tools/` holds your settings, accounts, state and logs,
|
|
243
|
+
and the accounts folder keeps the backups of every token `session
|
|
244
|
+
account use` replaced. `~/.session/` holds the session folders (and, on
|
|
245
|
+
a host that has not moved it, the old state folder), and the kit's other
|
|
246
|
+
packages keep their files in it too.
|
|
228
247
|
|
|
229
248
|
## Docs
|
|
230
249
|
|
|
@@ -236,6 +255,10 @@ and the kit's other packages keep their files in it too.
|
|
|
236
255
|
- [The `session list` cache][cache]: one computation, many readers
|
|
237
256
|
- [Operator and privileged seats][operator]: who seats ask
|
|
238
257
|
- [Closing idle seats][reap]: `session reap`, a dry run by default
|
|
258
|
+
- [Where tools keeps its state][state-paths]: each folder, old and new,
|
|
259
|
+
the rule, and `session path`, the verb peers ask
|
|
260
|
+
- [Moving tools' state][state-migrate]: `session path migrate`, one host
|
|
261
|
+
at a time, dry run first
|
|
239
262
|
- [Shell completion][completion]: `session completion bash|zsh`, a static
|
|
240
263
|
script built from the verbs' own parsers
|
|
241
264
|
- [The Claude Code plugin][plugin]: the skill and the hooks
|
|
@@ -244,6 +267,8 @@ and the kit's other packages keep their files in it too.
|
|
|
244
267
|
- [The busy sound][busy]: a sound while a tool batch runs long
|
|
245
268
|
- [The session-info hooks][session-info]: the short session id as the
|
|
246
269
|
session title, and the session's facts in context, both off by default
|
|
270
|
+
- [The identity block][identity]: who a launched session is, in its
|
|
271
|
+
context on turn one and from `session identity`
|
|
247
272
|
- [The sound engine][engine]: the busy sound from one resident process
|
|
248
273
|
- [The status line][status-line]: `session statusline install` puts it in the
|
|
249
274
|
kit's claude-code layer, never `~/.claude`. `session start` seats show it;
|
|
@@ -266,11 +291,28 @@ be set, and where each one lives ([configuration][configuration]):
|
|
|
266
291
|
- **Settings files**, JSON in layers: `~/.sessionkit/tools/settings/session.json`
|
|
267
292
|
(the operator, the list cache, the pane signal, accounts, a session's
|
|
268
293
|
home, the default model), `~/.sessionkit/tools/settings/sounds.json` (the busy sound), and
|
|
269
|
-
`~/.sessionkit/tools/settings/claude-code.json` (the model, effort
|
|
270
|
-
|
|
294
|
+
`~/.sessionkit/tools/settings/claude-code.json` (the model, effort,
|
|
295
|
+
status line and renderer `session start` passes on). Every key, its default and
|
|
271
296
|
which layers it is read from: [settings][settings].
|
|
272
|
-
- **Folders of its own:** `~/.
|
|
273
|
-
state
|
|
297
|
+
- **Folders of its own:** `~/.sessionkit/tools/accounts/` (`session
|
|
298
|
+
account`), and its state, each leaf with an old and a new folder. A
|
|
299
|
+
fresh host starts `state` and `sound` in the new folder; an existing
|
|
300
|
+
one stays put until it is moved, and a moved one is followed
|
|
301
|
+
([where tools keeps its state][state-paths]):
|
|
302
|
+
- `state`: `~/.session/state/`, new `~/.sessionkit/tools/state/`.
|
|
303
|
+
- `sessions`, `seats`, `agent` and `backups`: `~/.session/<leaf>/`, new
|
|
304
|
+
`~/.sessionkit/tools/state/<leaf>/`.
|
|
305
|
+
- `sound`: `~/.local/share/sessiontools/sound/`, new
|
|
306
|
+
`~/.sessionkit/tools/state/sound/`.
|
|
307
|
+
- **The rule, per leaf:** its variable when set; else the old folder
|
|
308
|
+
while it is real; else the new one when it exists; else the new one
|
|
309
|
+
for `state` and `sound`, the old one for the rest.
|
|
310
|
+
- **To see what is used:** `session path LEAF`, or `session path --json`.
|
|
311
|
+
- **To move `state` and `sound`:** `session path migrate --dry-run`,
|
|
312
|
+
then a real run with that host's go ([moving tools' state][state-migrate]).
|
|
313
|
+
- **Upgrade every copy of sessiontools on a host together,** other
|
|
314
|
+
packages' copies included: an older copy writes the old `state`
|
|
315
|
+
folder, and `session path --json` shows `stranded` when one lags.
|
|
274
316
|
- **Environment variables:** the settle alert's `SESSION_SETTLE_AT`, the
|
|
275
317
|
clock line's `SESSION_CLOCK_WARN_AT`, the hooks' `SESSIONTOOLS_HOOK_*`
|
|
276
318
|
switches (`SESSIONTOOLS_HOOK_CLOCK=on` switches the clock line on), and
|
|
@@ -280,31 +322,43 @@ be set, and where each one lives ([configuration][configuration]):
|
|
|
280
322
|
|
|
281
323
|
## Logs
|
|
282
324
|
|
|
283
|
-
- **The sound engine:**
|
|
325
|
+
- **The sound engine:** `~/.sessionkit/tools/log/sound-engine.log`,
|
|
284
326
|
once `session sound engine on` has run.
|
|
285
|
-
- **The pane signal's agent:**
|
|
327
|
+
- **The pane signal's agent:** `~/.sessionkit/tools/log/pane-state.log`,
|
|
286
328
|
once `session pane-state agent on` has run.
|
|
287
|
-
- **The default model:** `~/.
|
|
288
|
-
|
|
289
|
-
|
|
329
|
+
- **The default model:** `~/.sessionkit/tools/state/default-model/log.jsonl`
|
|
330
|
+
(`~/.session/state/default-model/log.jsonl` on a Mac that still has
|
|
331
|
+
its old state folder), one line per write to settings.json, and
|
|
332
|
+
`~/.sessionkit/tools/log/default-model.log`, once you have loaded
|
|
290
333
|
the launchd agent `session default-model unit launchd` prints.
|
|
291
|
-
- **`session reap --yes`:** `~/.
|
|
292
|
-
seat it closed or failed to close.
|
|
334
|
+
- **`session reap --yes`:** `~/.sessionkit/tools/log/reap.log`, one line
|
|
335
|
+
for each seat it closed or failed to close.
|
|
293
336
|
- **Each session's settings at birth:**
|
|
294
337
|
`~/.session/sessions/<session-id>/log/settings.jsonl`, what `session
|
|
295
338
|
start` read into that session's settings: each environment variable
|
|
296
339
|
by name, never its value.
|
|
297
340
|
- **The status line:** `~/.config/session/statusline-log.jsonl`, one line
|
|
298
341
|
per render, only when `~/.config/session/` already exists.
|
|
342
|
+
- **Old logs, 0.3.1 and before:** the three services logged to
|
|
343
|
+
`~/Library/Logs/sessiontools/`, and `session reap --yes` to
|
|
344
|
+
`$SESSION_STATE_DIR/reap.log` (`~/.session/state/reap.log`). Those
|
|
345
|
+
files are left in place. A service an older release installed keeps
|
|
346
|
+
writing its old log, at every login too, until its plist is written
|
|
347
|
+
again: `on` for the sound engine and the pane signal, the steps in
|
|
348
|
+
[the default model][default-model] for its agent. The sound engine's
|
|
349
|
+
and the pane signal's `status` name an old log while it is still there.
|
|
299
350
|
|
|
300
351
|
Follow one as it grows with `tail -F`, for example
|
|
301
|
-
`tail -F
|
|
352
|
+
`tail -F ~/.sessionkit/tools/log/pane-state.log`.
|
|
302
353
|
|
|
303
354
|
- **Health:** `session list` prints a row for each live session, with its
|
|
304
|
-
state and context
|
|
305
|
-
|
|
355
|
+
state and context, and SEAT names the seat a session holds: `claude`
|
|
356
|
+
for the default seat, or an agent's name for its hand-launched
|
|
357
|
+
holder. A session you know is running that is not listed is one
|
|
358
|
+
sessiontools cannot see.
|
|
306
359
|
|
|
307
360
|
[verbs]: docs/session.md
|
|
361
|
+
[default-seat]: docs/session.md#the-default-seat
|
|
308
362
|
[start]: docs/session-start-launcher.md
|
|
309
363
|
[credentials]: docs/session-start-launcher.md#credentials-are-never-handed-down
|
|
310
364
|
[account]: docs/account.md
|
|
@@ -312,11 +366,14 @@ Follow one as it grows with `tail -F`, for example
|
|
|
312
366
|
[operator]: docs/operator-privileged-seats.md
|
|
313
367
|
[reap]: docs/session-reap.md
|
|
314
368
|
[completion]: docs/shell-completion.md
|
|
369
|
+
[state-paths]: docs/tools-state-paths.md
|
|
370
|
+
[state-migrate]: docs/tools-state-migrate.md
|
|
315
371
|
[plugin]: docs/claude-code-plugin.md
|
|
316
372
|
[settle]: docs/settle-alert-hook.md
|
|
317
373
|
[clock]: docs/clock-line-hook.md
|
|
318
374
|
[busy]: docs/busy-sound-hook.md
|
|
319
375
|
[session-info]: docs/session-info-hook.md
|
|
376
|
+
[identity]: docs/identity-block.md
|
|
320
377
|
[engine]: docs/sound-engine-agent.md
|
|
321
378
|
[status-line]: docs/status-line-script.md
|
|
322
379
|
[pane-state]: docs/pane-state-signal.md
|
|
@@ -45,6 +45,8 @@ eval "$(session completion bash)"
|
|
|
45
45
|
category.
|
|
46
46
|
- `session ledger --short`: each file read and command run, by tokens.
|
|
47
47
|
- `session account status`: which Claude account this Mac is on.
|
|
48
|
+
- `session account verify`: which account the API bills for this seat's
|
|
49
|
+
token, from one 1-token request.
|
|
48
50
|
- `session start --list-agents`: the agents it can start.
|
|
49
51
|
- `session reap`: seats idle past 1h, each CLOSE or KEEP, and why. A dry
|
|
50
52
|
run.
|
|
@@ -113,8 +115,10 @@ it. Each line says what puts it there and what takes it away.
|
|
|
113
115
|
sessiontools` drops this package's entries, and `uv cache prune` drops
|
|
114
116
|
the ones nothing uses.
|
|
115
117
|
- **Folders, each made by a verb you run:**
|
|
116
|
-
- `~/.
|
|
117
|
-
`list.lock`, at the first `session list`.
|
|
118
|
+
- `~/.sessionkit/tools/state/list/`: the list cache, `snapshot.json`
|
|
119
|
+
and `list.lock`, at the first `session list`. The state folders here
|
|
120
|
+
and below are on a fresh host; a host that still has
|
|
121
|
+
`~/.session/state/` keeps using it until it is moved.
|
|
118
122
|
- `~/.session/sessions/<session-id>/`: one for each `session start`,
|
|
119
123
|
holding `launch/`, `log/settings.jsonl` and `session-id`. It also
|
|
120
124
|
gets `settings/<tool>.d/50-env.json`, one for each tool whose
|
|
@@ -123,14 +127,19 @@ it. Each line says what puts it there and what takes it away.
|
|
|
123
127
|
`log/settings.jsonl` names each variable it read, never its value.
|
|
124
128
|
The plugin's hooks and `session park` write only inside a folder
|
|
125
129
|
that is already there.
|
|
126
|
-
- `~/.
|
|
127
|
-
|
|
128
|
-
- `~/.
|
|
129
|
-
first time `session default-model enforce` changes
|
|
130
|
+
- `~/.sessionkit/tools/state/pane-state/watch.lock`: the pane signal's
|
|
131
|
+
service.
|
|
132
|
+
- `~/.sessionkit/tools/state/default-model/`: `backups/` and
|
|
133
|
+
`log.jsonl`, the first time `session default-model enforce` changes
|
|
134
|
+
settings.json.
|
|
130
135
|
- `~/.sessionkit/tools/accounts/`: `session account new` and `use`.
|
|
131
|
-
- `~/.
|
|
132
|
-
builds the sound engine there with `swiftc
|
|
133
|
-
|
|
136
|
+
- `~/.sessionkit/tools/state/sound/bin/`: `session sound engine on`
|
|
137
|
+
builds the sound engine there with `swiftc` (in
|
|
138
|
+
`~/.local/share/sessiontools/sound/bin/` while that old folder is
|
|
139
|
+
still there).
|
|
140
|
+
- `~/.sessionkit/tools/log/`: the services' logs and `reap.log`
|
|
141
|
+
(Logs, below). An older release wrote them elsewhere, and those
|
|
142
|
+
files stay where they were.
|
|
134
143
|
- `SESSIONS_DIR`, `SESSION_STATE_DIR` and `SESSIONTOOLS_SOUND_HOME`
|
|
135
144
|
move these folders when set.
|
|
136
145
|
- **Services (launchd agents):** none until you turn one on. Each `on`
|
|
@@ -172,6 +181,12 @@ it. Each line says what puts it there and what takes it away.
|
|
|
172
181
|
With `model.default` set, `session default-model enforce` (and the
|
|
173
182
|
unit or `watch` that runs it) rewrites the top-level `"model"` in
|
|
174
183
|
`~/.claude/settings.json`, after a backup.
|
|
184
|
+
- **Not changed by an upgrade: a live tmux server.** One born inside the
|
|
185
|
+
default seat by sessiontools 0.3.1 or earlier still hands
|
|
186
|
+
`CLAUDE_CODE_AGENT=claude` to every pane it makes, so a raw `claude`
|
|
187
|
+
there hears the seat's mail. Restarting that server, or running
|
|
188
|
+
`tmux set-environment -g -u CLAUDE_CODE_AGENT` on it, is yours to do
|
|
189
|
+
([the default seat][default-seat]).
|
|
175
190
|
|
|
176
191
|
## Uninstall
|
|
177
192
|
|
|
@@ -192,23 +207,27 @@ uv tool uninstall sessiontools
|
|
|
192
207
|
```
|
|
193
208
|
|
|
194
209
|
That leaves what the verbs made, listed above. The plugin's cached
|
|
195
|
-
copy
|
|
196
|
-
alone. To delete them:
|
|
210
|
+
copy and the engine's folder are sessiontools' alone. To delete them:
|
|
197
211
|
|
|
198
212
|
```bash
|
|
199
213
|
rm -rf "${HOME:?}/.claude/plugins/cache/sessiontools"
|
|
200
|
-
rm -rf "${HOME:?}/Library/Logs/sessiontools"
|
|
201
214
|
rm -rf "${HOME:?}/.local/share/sessiontools"
|
|
215
|
+
rm -rf "${HOME:?}/.sessionkit/tools/state/sound"
|
|
202
216
|
```
|
|
203
217
|
|
|
218
|
+
The logs are left in place: the new ones in `~/.sessionkit/tools/log/`,
|
|
219
|
+
and an older release's in `~/Library/Logs/sessiontools/`. Nothing here
|
|
220
|
+
deletes from `~/Library/Logs`.
|
|
221
|
+
|
|
204
222
|
uv's cache stays too, with the other things uv installed; `uv cache
|
|
205
223
|
clean sessiontools` drops this package's entries.
|
|
206
224
|
|
|
207
225
|
The rest is your data: delete it by hand only if you want it gone.
|
|
208
|
-
`~/.sessionkit/tools/` holds your settings
|
|
209
|
-
accounts folder keeps the backups of every token `session
|
|
210
|
-
replaced. `~/.session/` holds the session folders and
|
|
211
|
-
|
|
226
|
+
`~/.sessionkit/tools/` holds your settings, accounts, state and logs,
|
|
227
|
+
and the accounts folder keeps the backups of every token `session
|
|
228
|
+
account use` replaced. `~/.session/` holds the session folders (and, on
|
|
229
|
+
a host that has not moved it, the old state folder), and the kit's other
|
|
230
|
+
packages keep their files in it too.
|
|
212
231
|
|
|
213
232
|
## Docs
|
|
214
233
|
|
|
@@ -220,6 +239,10 @@ and the kit's other packages keep their files in it too.
|
|
|
220
239
|
- [The `session list` cache][cache]: one computation, many readers
|
|
221
240
|
- [Operator and privileged seats][operator]: who seats ask
|
|
222
241
|
- [Closing idle seats][reap]: `session reap`, a dry run by default
|
|
242
|
+
- [Where tools keeps its state][state-paths]: each folder, old and new,
|
|
243
|
+
the rule, and `session path`, the verb peers ask
|
|
244
|
+
- [Moving tools' state][state-migrate]: `session path migrate`, one host
|
|
245
|
+
at a time, dry run first
|
|
223
246
|
- [Shell completion][completion]: `session completion bash|zsh`, a static
|
|
224
247
|
script built from the verbs' own parsers
|
|
225
248
|
- [The Claude Code plugin][plugin]: the skill and the hooks
|
|
@@ -228,6 +251,8 @@ and the kit's other packages keep their files in it too.
|
|
|
228
251
|
- [The busy sound][busy]: a sound while a tool batch runs long
|
|
229
252
|
- [The session-info hooks][session-info]: the short session id as the
|
|
230
253
|
session title, and the session's facts in context, both off by default
|
|
254
|
+
- [The identity block][identity]: who a launched session is, in its
|
|
255
|
+
context on turn one and from `session identity`
|
|
231
256
|
- [The sound engine][engine]: the busy sound from one resident process
|
|
232
257
|
- [The status line][status-line]: `session statusline install` puts it in the
|
|
233
258
|
kit's claude-code layer, never `~/.claude`. `session start` seats show it;
|
|
@@ -250,11 +275,28 @@ be set, and where each one lives ([configuration][configuration]):
|
|
|
250
275
|
- **Settings files**, JSON in layers: `~/.sessionkit/tools/settings/session.json`
|
|
251
276
|
(the operator, the list cache, the pane signal, accounts, a session's
|
|
252
277
|
home, the default model), `~/.sessionkit/tools/settings/sounds.json` (the busy sound), and
|
|
253
|
-
`~/.sessionkit/tools/settings/claude-code.json` (the model, effort
|
|
254
|
-
|
|
278
|
+
`~/.sessionkit/tools/settings/claude-code.json` (the model, effort,
|
|
279
|
+
status line and renderer `session start` passes on). Every key, its default and
|
|
255
280
|
which layers it is read from: [settings][settings].
|
|
256
|
-
- **Folders of its own:** `~/.
|
|
257
|
-
state
|
|
281
|
+
- **Folders of its own:** `~/.sessionkit/tools/accounts/` (`session
|
|
282
|
+
account`), and its state, each leaf with an old and a new folder. A
|
|
283
|
+
fresh host starts `state` and `sound` in the new folder; an existing
|
|
284
|
+
one stays put until it is moved, and a moved one is followed
|
|
285
|
+
([where tools keeps its state][state-paths]):
|
|
286
|
+
- `state`: `~/.session/state/`, new `~/.sessionkit/tools/state/`.
|
|
287
|
+
- `sessions`, `seats`, `agent` and `backups`: `~/.session/<leaf>/`, new
|
|
288
|
+
`~/.sessionkit/tools/state/<leaf>/`.
|
|
289
|
+
- `sound`: `~/.local/share/sessiontools/sound/`, new
|
|
290
|
+
`~/.sessionkit/tools/state/sound/`.
|
|
291
|
+
- **The rule, per leaf:** its variable when set; else the old folder
|
|
292
|
+
while it is real; else the new one when it exists; else the new one
|
|
293
|
+
for `state` and `sound`, the old one for the rest.
|
|
294
|
+
- **To see what is used:** `session path LEAF`, or `session path --json`.
|
|
295
|
+
- **To move `state` and `sound`:** `session path migrate --dry-run`,
|
|
296
|
+
then a real run with that host's go ([moving tools' state][state-migrate]).
|
|
297
|
+
- **Upgrade every copy of sessiontools on a host together,** other
|
|
298
|
+
packages' copies included: an older copy writes the old `state`
|
|
299
|
+
folder, and `session path --json` shows `stranded` when one lags.
|
|
258
300
|
- **Environment variables:** the settle alert's `SESSION_SETTLE_AT`, the
|
|
259
301
|
clock line's `SESSION_CLOCK_WARN_AT`, the hooks' `SESSIONTOOLS_HOOK_*`
|
|
260
302
|
switches (`SESSIONTOOLS_HOOK_CLOCK=on` switches the clock line on), and
|
|
@@ -264,31 +306,43 @@ be set, and where each one lives ([configuration][configuration]):
|
|
|
264
306
|
|
|
265
307
|
## Logs
|
|
266
308
|
|
|
267
|
-
- **The sound engine:**
|
|
309
|
+
- **The sound engine:** `~/.sessionkit/tools/log/sound-engine.log`,
|
|
268
310
|
once `session sound engine on` has run.
|
|
269
|
-
- **The pane signal's agent:**
|
|
311
|
+
- **The pane signal's agent:** `~/.sessionkit/tools/log/pane-state.log`,
|
|
270
312
|
once `session pane-state agent on` has run.
|
|
271
|
-
- **The default model:** `~/.
|
|
272
|
-
|
|
273
|
-
|
|
313
|
+
- **The default model:** `~/.sessionkit/tools/state/default-model/log.jsonl`
|
|
314
|
+
(`~/.session/state/default-model/log.jsonl` on a Mac that still has
|
|
315
|
+
its old state folder), one line per write to settings.json, and
|
|
316
|
+
`~/.sessionkit/tools/log/default-model.log`, once you have loaded
|
|
274
317
|
the launchd agent `session default-model unit launchd` prints.
|
|
275
|
-
- **`session reap --yes`:** `~/.
|
|
276
|
-
seat it closed or failed to close.
|
|
318
|
+
- **`session reap --yes`:** `~/.sessionkit/tools/log/reap.log`, one line
|
|
319
|
+
for each seat it closed or failed to close.
|
|
277
320
|
- **Each session's settings at birth:**
|
|
278
321
|
`~/.session/sessions/<session-id>/log/settings.jsonl`, what `session
|
|
279
322
|
start` read into that session's settings: each environment variable
|
|
280
323
|
by name, never its value.
|
|
281
324
|
- **The status line:** `~/.config/session/statusline-log.jsonl`, one line
|
|
282
325
|
per render, only when `~/.config/session/` already exists.
|
|
326
|
+
- **Old logs, 0.3.1 and before:** the three services logged to
|
|
327
|
+
`~/Library/Logs/sessiontools/`, and `session reap --yes` to
|
|
328
|
+
`$SESSION_STATE_DIR/reap.log` (`~/.session/state/reap.log`). Those
|
|
329
|
+
files are left in place. A service an older release installed keeps
|
|
330
|
+
writing its old log, at every login too, until its plist is written
|
|
331
|
+
again: `on` for the sound engine and the pane signal, the steps in
|
|
332
|
+
[the default model][default-model] for its agent. The sound engine's
|
|
333
|
+
and the pane signal's `status` name an old log while it is still there.
|
|
283
334
|
|
|
284
335
|
Follow one as it grows with `tail -F`, for example
|
|
285
|
-
`tail -F
|
|
336
|
+
`tail -F ~/.sessionkit/tools/log/pane-state.log`.
|
|
286
337
|
|
|
287
338
|
- **Health:** `session list` prints a row for each live session, with its
|
|
288
|
-
state and context
|
|
289
|
-
|
|
339
|
+
state and context, and SEAT names the seat a session holds: `claude`
|
|
340
|
+
for the default seat, or an agent's name for its hand-launched
|
|
341
|
+
holder. A session you know is running that is not listed is one
|
|
342
|
+
sessiontools cannot see.
|
|
290
343
|
|
|
291
344
|
[verbs]: docs/session.md
|
|
345
|
+
[default-seat]: docs/session.md#the-default-seat
|
|
292
346
|
[start]: docs/session-start-launcher.md
|
|
293
347
|
[credentials]: docs/session-start-launcher.md#credentials-are-never-handed-down
|
|
294
348
|
[account]: docs/account.md
|
|
@@ -296,11 +350,14 @@ Follow one as it grows with `tail -F`, for example
|
|
|
296
350
|
[operator]: docs/operator-privileged-seats.md
|
|
297
351
|
[reap]: docs/session-reap.md
|
|
298
352
|
[completion]: docs/shell-completion.md
|
|
353
|
+
[state-paths]: docs/tools-state-paths.md
|
|
354
|
+
[state-migrate]: docs/tools-state-migrate.md
|
|
299
355
|
[plugin]: docs/claude-code-plugin.md
|
|
300
356
|
[settle]: docs/settle-alert-hook.md
|
|
301
357
|
[clock]: docs/clock-line-hook.md
|
|
302
358
|
[busy]: docs/busy-sound-hook.md
|
|
303
359
|
[session-info]: docs/session-info-hook.md
|
|
360
|
+
[identity]: docs/identity-block.md
|
|
304
361
|
[engine]: docs/sound-engine-agent.md
|
|
305
362
|
[status-line]: docs/status-line-script.md
|
|
306
363
|
[pane-state]: docs/pane-state-signal.md
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
"""`session account list | status | use | new | add | remove`: the
|
|
2
|
-
argparse `main(argv)`.
|
|
1
|
+
"""`session account list | status | verify | use | new | add | remove`: the
|
|
2
|
+
group's argparse `main(argv)`.
|
|
3
3
|
|
|
4
|
-
`list`, `status`, `use` and `new` live in their own modules
|
|
5
|
-
`use`, `new`); this module parses their flags and
|
|
6
|
-
`remove` live here.
|
|
4
|
+
`list`, `status`, `verify`, `use` and `new` live in their own modules
|
|
5
|
+
(`status`, `verify`, `use`, `new`); this module parses their flags and
|
|
6
|
+
calls them. `add` and `remove` live here.
|
|
7
7
|
|
|
8
8
|
session account list
|
|
9
9
|
session account status [--json] [--no-resets]
|
|
10
|
+
session account verify [NAME] [--json]
|
|
10
11
|
session account use NAME --witness WHO [--yes]
|
|
11
12
|
session account new NAME --email E
|
|
12
13
|
session account add NAME --email E --source BACKEND:REF [--source ...] [--no-fp]
|
|
@@ -55,12 +56,12 @@ Two deliberate calls:
|
|
|
55
56
|
|
|
56
57
|
Exit codes: 0 done; 2 usage, unknown or duplicate name, a bad source; 3
|
|
57
58
|
`remove` of the host's account; 4 (`add`) no source resolved; 5 a refused
|
|
58
|
-
registry or config. `use` and `new` keep their own tables
|
|
59
|
-
(`use.EXIT_CODES`, `new.EXIT_CODES`).
|
|
59
|
+
registry or config. `verify`, `use` and `new` keep their own tables
|
|
60
|
+
(`verify.EXIT_CODES`, `use.EXIT_CODES`, `new.EXIT_CODES`).
|
|
60
61
|
|
|
61
62
|
Seams: every external is a keyword of `main` (the Keychain, the preflight,
|
|
62
|
-
the seat scan, the usage fetcher,
|
|
63
|
-
`new`'s token store, capture and terminal), so
|
|
63
|
+
the seat scan, the usage fetcher, `verify`'s transport, the backends, the
|
|
64
|
+
environment, HOME, and `new`'s token store, capture and terminal), so
|
|
64
65
|
the end-to-end test drives every verb on fakes. Left as None, each is the
|
|
65
66
|
production default, chosen by the module that owns it.
|
|
66
67
|
"""
|
|
@@ -80,6 +81,7 @@ from sessiontools.account import keychain as kc
|
|
|
80
81
|
from sessiontools.account import registry as reg
|
|
81
82
|
from sessiontools.account import status as st
|
|
82
83
|
from sessiontools.account import use as u
|
|
84
|
+
from sessiontools.account import verify as vf
|
|
83
85
|
|
|
84
86
|
if TYPE_CHECKING:
|
|
85
87
|
from sessiontools.account.setuptoken import Run
|
|
@@ -93,6 +95,17 @@ EXIT_HOST = 3 # remove: the host is on it
|
|
|
93
95
|
EXIT_UNRESOLVED = reg.EXIT_UNRESOLVED # 4
|
|
94
96
|
EXIT_REGISTRY = reg.EXIT_REGISTRY # 5
|
|
95
97
|
|
|
98
|
+
#: The group's `-h` leads with how it knows the accounts and which one is in
|
|
99
|
+
#: use (registry.default_path; status.host_of), and why `/login` moves every
|
|
100
|
+
#: seat on a Keychain host (keychain.SERVICE). Raw: the line breaks are kept.
|
|
101
|
+
DESCRIPTION = """\
|
|
102
|
+
Which Claude account this Mac is on, and the switch. Never prints a token.
|
|
103
|
+
Available: the accounts `new` or `add` put in ~/.sessionkit/tools/accounts/.
|
|
104
|
+
In use: the host's token, fingerprinted and looked up in that list: the
|
|
105
|
+
Keychain item's, or the file's when the setting account.token_file names one.
|
|
106
|
+
/login moves every seat on a Keychain host: all with no env token read the item.
|
|
107
|
+
"""
|
|
108
|
+
|
|
96
109
|
EPILOG = """\
|
|
97
110
|
By default the host signs in from the Keychain item all seats read: `use`
|
|
98
111
|
moves every seat with no env token within about 50 s, and each one's
|
|
@@ -108,6 +121,12 @@ file unless its agent file says inherit: [CLAUDE_CODE_OAUTH_TOKEN];
|
|
|
108
121
|
without it, it signs in from Claude's stored credentials, and `status`
|
|
109
122
|
says so.
|
|
110
123
|
|
|
124
|
+
`use` changes the token, never ~/.claude.json: Claude's /status and
|
|
125
|
+
`claude auth status` name the account from that file's oauthAccount,
|
|
126
|
+
so after a switch they can name the old one. `status` prints that name
|
|
127
|
+
on its own profile line, and `verify` makes one 1-token request to show
|
|
128
|
+
which account the API bills for a token.
|
|
129
|
+
|
|
111
130
|
`new` runs `claude setup-token` in a masked copy of its screen, saves the
|
|
112
131
|
token to passage at <PREFIX>/<E>, reads it back, and registers it. PREFIX
|
|
113
132
|
is the `session` setting account.passage_prefix (default
|
|
@@ -123,6 +142,7 @@ backup or ledger (or `use`: a bad account.token_file or token file; or
|
|
|
123
142
|
restored the item or file; 7 `use` could not restore it; 8 `new` did not save the
|
|
124
143
|
token; 9 `new` saved it but did not register it.
|
|
125
144
|
"""
|
|
145
|
+
EPILOG += "\n" + vf.EPILOG_LINE + "\n"
|
|
126
146
|
|
|
127
147
|
|
|
128
148
|
class AccountError(Exception):
|
|
@@ -261,8 +281,7 @@ def remove(name: str, *, keychain: kc.Keychain | None = None,
|
|
|
261
281
|
def parser() -> argparse.ArgumentParser:
|
|
262
282
|
p = argparse.ArgumentParser(
|
|
263
283
|
prog=PROG, epilog=EPILOG, formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
264
|
-
description=
|
|
265
|
-
"between them. Never prints a token.")
|
|
284
|
+
description=DESCRIPTION)
|
|
266
285
|
verbs = p.add_subparsers(dest="verb", metavar="VERB")
|
|
267
286
|
verbs.required = True
|
|
268
287
|
|
|
@@ -272,13 +291,28 @@ def parser() -> argparse.ArgumentParser:
|
|
|
272
291
|
|
|
273
292
|
s = verbs.add_parser("status", help="the host's account, this seat's, plan usage",
|
|
274
293
|
description="Which account the host is on, this seat's route, "
|
|
275
|
-
"
|
|
294
|
+
"the plan usage of the live account (a window past "
|
|
295
|
+
"its reset is STALE, its figure not shown), and the "
|
|
296
|
+
"account ~/.claude.json names (display only: the "
|
|
297
|
+
"token decides billing).")
|
|
276
298
|
s.add_argument("--json", action="store_true", help="one JSON object")
|
|
277
299
|
s.add_argument("--no-resets", action="store_true",
|
|
278
300
|
help="hide when each usage window resets (shown by default)")
|
|
279
301
|
s.add_argument("--resets", action="store_true",
|
|
280
302
|
help="accepted and ignored: resets are shown by default")
|
|
281
303
|
|
|
304
|
+
v = verbs.add_parser("verify", help="ask the API which account a token bills",
|
|
305
|
+
description="Make ONE 1-token request to the API with a token "
|
|
306
|
+
"and print the account-wide usage windows it "
|
|
307
|
+
"reports: the 7d reset identifies the account. "
|
|
308
|
+
"NAME: that registry account's token. No NAME: "
|
|
309
|
+
"the token this seat bills (the env token, else "
|
|
310
|
+
"the host's token file or Keychain item). Never "
|
|
311
|
+
"prints a token.")
|
|
312
|
+
v.add_argument("name", metavar="NAME", nargs="?",
|
|
313
|
+
help="a registered account (default: this seat's token)")
|
|
314
|
+
v.add_argument("--json", action="store_true", help="one JSON object")
|
|
315
|
+
|
|
282
316
|
# The usage line is written by hand: --witness is required to write, but
|
|
283
317
|
# argparse's `required=True` would exit before `use` shows the plan.
|
|
284
318
|
w = verbs.add_parser("use", help="move the HOST's sign-in to NAME",
|
|
@@ -329,6 +363,7 @@ def main(argv: Sequence[str] | None = None, *,
|
|
|
329
363
|
preflight: u.Preflight | None = None,
|
|
330
364
|
seat_scan: u.SeatScan | None = None,
|
|
331
365
|
fetch: st.Fetcher | None = None,
|
|
366
|
+
post: vf.Post | None = None,
|
|
332
367
|
backends: Mapping[str, reg.Backend] | None = None,
|
|
333
368
|
env: Mapping[str, str] | None = None,
|
|
334
369
|
home: str | os.PathLike | None = None,
|
|
@@ -340,7 +375,8 @@ def main(argv: Sequence[str] | None = None, *,
|
|
|
340
375
|
out: TextIO | None = None, err: TextIO | None = None) -> int:
|
|
341
376
|
"""`session account VERB ...`. Returns the exit code. Every keyword is
|
|
342
377
|
a seam; None is the production default. `store`, `capture` and `tty`
|
|
343
|
-
(stdin's fd, stdout's fd) are `new`'s; so is `preflight`, as `use`'s.
|
|
378
|
+
(stdin's fd, stdout's fd) are `new`'s; so is `preflight`, as `use`'s.
|
|
379
|
+
`post` is `verify`'s transport."""
|
|
344
380
|
out = sys.stdout if out is None else out
|
|
345
381
|
err = sys.stderr if err is None else err
|
|
346
382
|
try:
|
|
@@ -355,6 +391,9 @@ def main(argv: Sequence[str] | None = None, *,
|
|
|
355
391
|
if args.verb == "status":
|
|
356
392
|
return st.status(as_json=args.json, resets=not args.no_resets, keychain=keychain,
|
|
357
393
|
fetch=fetch, env=env, home=home, clock=clock, out=out, err=err)
|
|
394
|
+
if args.verb == "verify":
|
|
395
|
+
return vf.verify(args.name, as_json=args.json, keychain=keychain, backends=backends,
|
|
396
|
+
env=env, home=home, post=post, clock=clock, out=out, err=err)
|
|
358
397
|
if args.verb == "use":
|
|
359
398
|
return u.use(args.name, witness=args.witness, yes=args.yes, keychain=keychain,
|
|
360
399
|
preflight=preflight, seat_scan=seat_scan, home=home,
|
|
@@ -26,8 +26,9 @@ What is read, and what is refused:
|
|
|
26
26
|
- `written_at` is required: without it the file's age cannot be told, and an
|
|
27
27
|
old file must never read as current. Past `STALE_AFTER` seconds the file
|
|
28
28
|
is marked stale; its numbers are still shown.
|
|
29
|
-
- A window whose `resets_at` has passed measured a window that is over.
|
|
30
|
-
|
|
29
|
+
- A window whose `resets_at` has passed measured a window that is over. It
|
|
30
|
+
reads as the exporter's does (`status.stale_text`): STALE, when it ended,
|
|
31
|
+
its percent not shown (`expired`, `stale`), never guessed to be 0.
|
|
31
32
|
- `account` must be absent or `"default"`, the host's own sign-in: any other
|
|
32
33
|
account's window is not the host's usage.
|
|
33
34
|
|
|
@@ -204,6 +205,7 @@ def read_limits(path: str | os.PathLike, now: float) -> st.Usage:
|
|
|
204
205
|
if age < -FUTURE_SLACK:
|
|
205
206
|
return _malformed(path, "written_at is in the future")
|
|
206
207
|
|
|
208
|
+
age_s = max(0, int(age))
|
|
207
209
|
windows = []
|
|
208
210
|
for key, label in WINDOWS:
|
|
209
211
|
raw = data.get(key)
|
|
@@ -217,10 +219,10 @@ def read_limits(path: str | os.PathLike, now: float) -> st.Usage:
|
|
|
217
219
|
resets_at = _number(raw.get("resets_at"))
|
|
218
220
|
expired = resets_at is not None and resets_at <= now
|
|
219
221
|
windows.append(st.Window(label, None, None if expired else percent, resets_at,
|
|
220
|
-
expired=expired
|
|
222
|
+
expired=expired, age_s=age_s,
|
|
223
|
+
ended_s=max(0, int(now - resets_at)) if expired else None))
|
|
221
224
|
if not windows:
|
|
222
225
|
return st.Usage(None, unavailable=f"{path} has no usage windows")
|
|
223
|
-
age_s = max(0, int(age))
|
|
224
226
|
return st.Usage(None, tuple(windows), source=st.SOURCE_FILE, file=str(path),
|
|
225
227
|
written_at=written_at, age_s=age_s, stale=age_s > STALE_AFTER)
|
|
226
228
|
|