4top 0.2.0a1__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.
- 4top-0.2.0a1/CHANGELOG.md +165 -0
- 4top-0.2.0a1/CONTRIBUTING.md +96 -0
- 4top-0.2.0a1/LICENSE +21 -0
- 4top-0.2.0a1/MANIFEST.in +5 -0
- 4top-0.2.0a1/PKG-INFO +233 -0
- 4top-0.2.0a1/README.md +206 -0
- 4top-0.2.0a1/README.zh-CN.md +134 -0
- 4top-0.2.0a1/SECURITY.md +15 -0
- 4top-0.2.0a1/docs/compatibility.md +83 -0
- 4top-0.2.0a1/docs/demo/demo.svg +158 -0
- 4top-0.2.0a1/docs/design.md +88 -0
- 4top-0.2.0a1/docs/privacy.md +46 -0
- 4top-0.2.0a1/docs/remote-design.md +126 -0
- 4top-0.2.0a1/docs/troubleshooting.md +46 -0
- 4top-0.2.0a1/docs/validation/README.md +104 -0
- 4top-0.2.0a1/docs/validation/install-summary.json +57 -0
- 4top-0.2.0a1/docs/validation/linux-remote-2026-09-26.md +58 -0
- 4top-0.2.0a1/docs/validation/macos-0.1.0a2.md +56 -0
- 4top-0.2.0a1/docs/validation/macos-fresh-install-0.1.0a2.json +57 -0
- 4top-0.2.0a1/docs/validation/macos-suite-dev.json +18 -0
- 4top-0.2.0a1/pyproject.toml +55 -0
- 4top-0.2.0a1/requirements-dev.lock +22 -0
- 4top-0.2.0a1/scripts/acceptance.py +48 -0
- 4top-0.2.0a1/scripts/check_docs.py +173 -0
- 4top-0.2.0a1/scripts/check_release.py +154 -0
- 4top-0.2.0a1/scripts/export_demo.py +30 -0
- 4top-0.2.0a1/scripts/remote_update.py +136 -0
- 4top-0.2.0a1/scripts/verify_install.py +61 -0
- 4top-0.2.0a1/setup.cfg +4 -0
- 4top-0.2.0a1/src/4top.egg-info/PKG-INFO +233 -0
- 4top-0.2.0a1/src/4top.egg-info/SOURCES.txt +55 -0
- 4top-0.2.0a1/src/4top.egg-info/dependency_links.txt +1 -0
- 4top-0.2.0a1/src/4top.egg-info/entry_points.txt +2 -0
- 4top-0.2.0a1/src/4top.egg-info/requires.txt +9 -0
- 4top-0.2.0a1/src/4top.egg-info/top_level.txt +1 -0
- 4top-0.2.0a1/src/fourtop/__init__.py +3 -0
- 4top-0.2.0a1/src/fourtop/__main__.py +3 -0
- 4top-0.2.0a1/src/fourtop/agents.py +150 -0
- 4top-0.2.0a1/src/fourtop/app.py +824 -0
- 4top-0.2.0a1/src/fourtop/cli.py +251 -0
- 4top-0.2.0a1/src/fourtop/config.py +167 -0
- 4top-0.2.0a1/src/fourtop/doctor.py +90 -0
- 4top-0.2.0a1/src/fourtop/errors.py +27 -0
- 4top-0.2.0a1/src/fourtop/hosts.py +167 -0
- 4top-0.2.0a1/src/fourtop/models.py +76 -0
- 4top-0.2.0a1/src/fourtop/services.py +269 -0
- 4top-0.2.0a1/src/fourtop/state.py +53 -0
- 4top-0.2.0a1/tests/conftest.py +83 -0
- 4top-0.2.0a1/tests/fixtures/fake_agent.py +96 -0
- 4top-0.2.0a1/tests/integration/test_launch.py +122 -0
- 4top-0.2.0a1/tests/tui/test_app.py +377 -0
- 4top-0.2.0a1/tests/unit/test_cli_config.py +242 -0
- 4top-0.2.0a1/tests/unit/test_docs.py +73 -0
- 4top-0.2.0a1/tests/unit/test_history.py +337 -0
- 4top-0.2.0a1/tests/unit/test_hosts.py +299 -0
- 4top-0.2.0a1/tests/unit/test_native_roots.py +44 -0
- 4top-0.2.0a1/tests/unit/test_release.py +49 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 4top 0.2.0a1 — 2026-09-26
|
|
4
|
+
|
|
5
|
+
The first release of the session-first line: 4top tracks transcripts rather than
|
|
6
|
+
processes, and reaches other machines over ssh. The previous line owned a process
|
|
7
|
+
per launch and reported liveness; that layer and its vocabulary are gone.
|
|
8
|
+
|
|
9
|
+
### Breaking: 4top tracks sessions, not processes
|
|
10
|
+
|
|
11
|
+
A native agent is recoverable from its transcript alone, so the transcript is the
|
|
12
|
+
durable object and the process is the ephemeral one. 4top now keeps only
|
|
13
|
+
transcripts: it does not own, supervise or report on a process, and it drives no
|
|
14
|
+
terminal multiplexer of its own.
|
|
15
|
+
|
|
16
|
+
- Deleted: the process-ownership layer and everything that existed to support it
|
|
17
|
+
— launch records, reservations, locks, the operation log, ownership markers,
|
|
18
|
+
exit-code and zombie evidence, the liveness states, and the commands, options
|
|
19
|
+
and `[runtime]` configuration section that reached into it.
|
|
20
|
+
- `new` runs the agent in the calling terminal: the CLI replaces itself with the
|
|
21
|
+
agent (`execvpe`), and the TUI suspends, waits and returns to the panel. Keeping
|
|
22
|
+
work alive across a disconnect belongs to whatever the user already runs.
|
|
23
|
+
- `list --json` rows are `schema_version` 2 and carry `key`, `agent`, `host`,
|
|
24
|
+
`cwd`, `title`, `started`, `last`, `source`, `status`, `problems` and
|
|
25
|
+
`can_resume`. Consumers of the previous row schema must be updated.
|
|
26
|
+
- Local history is shown by default. The old view-schema `history` flag is
|
|
27
|
+
ignored and the view file is written as schema 3.
|
|
28
|
+
- Nothing needs migrating: state is now a local identity plus the last selection.
|
|
29
|
+
Earlier files under `$XDG_STATE_HOME/4top` are ignored and can be deleted. A new
|
|
30
|
+
local identity changes history keys, so anything that copied a key must copy it
|
|
31
|
+
again.
|
|
32
|
+
|
|
33
|
+
### Remote hosts over SSH
|
|
34
|
+
|
|
35
|
+
- Add `[hosts.NAME]` with `ssh`, `command`, `refresh_seconds` and
|
|
36
|
+
`timeout_seconds`, plus the global `--host` option. Views are isolated:
|
|
37
|
+
`--host` replaces the local scope instead of merging machines into one table.
|
|
38
|
+
- The remote side is the same CLI, with no daemon and no new port. It runs with
|
|
39
|
+
`BatchMode=yes` (a missing key fails fast), a `ControlMaster` socket inside
|
|
40
|
+
private state, and a hard timeout; every remote argument is quoted for the
|
|
41
|
+
remote shell.
|
|
42
|
+
- A row whose `schema_version` differs is refused instead of partially parsed. A
|
|
43
|
+
remote row is relabelled with the configured host name, and its history key
|
|
44
|
+
stays opaque so it is never recomputed on the wrong machine.
|
|
45
|
+
- `resume` and `new` with `--host` hand the terminal to `ssh -t`, so the process
|
|
46
|
+
is created on the machine that owns the history.
|
|
47
|
+
- `H` switches the running panel between this machine and a configured host, so
|
|
48
|
+
comparing two machines no longer means quitting and relaunching. Rows, selection
|
|
49
|
+
and search state are dropped with the old scope, an in-flight refresh for the old
|
|
50
|
+
scope is discarded, and the saved selection stays local.
|
|
51
|
+
|
|
52
|
+
### Releases
|
|
53
|
+
|
|
54
|
+
- Publish one project per repository. The first attempt to publish both from this
|
|
55
|
+
workflow failed with `403 Invalid API Token: OIDC scoped token is not valid for
|
|
56
|
+
project '4top'`: one job means one OIDC exchange, and the token is scoped to the
|
|
57
|
+
project its publisher matched. Rather than disambiguate two publishers with two
|
|
58
|
+
environments, this workflow now publishes only `4top` and consumes `session-ls`
|
|
59
|
+
from PyPI, which is the layout the two packages actually have: separate projects,
|
|
60
|
+
separate version lines, separate pipelines.
|
|
61
|
+
- Publish from CI on a `v<version>` tag: the suite runs, `scripts/check_release.py`
|
|
62
|
+
gates the release, both wheels are built and installed into a fresh environment,
|
|
63
|
+
`session-ls` is published before `4top`, and the GitHub release is opened. Uploads
|
|
64
|
+
use PyPI trusted publishing, so no token is stored in the repository, and the
|
|
65
|
+
manual trigger defaults to a dry run.
|
|
66
|
+
- The gate refuses a tag that does not match the version, artifacts that are not the
|
|
67
|
+
versions being released, and a root requirement the library cannot satisfy. That
|
|
68
|
+
last one is real: the root requires `session-ls>=0.2.0` while PyPI only ever held
|
|
69
|
+
0.1.0, so publishing the root alone would have produced an uninstallable package.
|
|
70
|
+
|
|
71
|
+
### Interface
|
|
72
|
+
|
|
73
|
+
- The host picker (`H`) switches the panel between this machine and a configured
|
|
74
|
+
host, so comparing two machines no longer means relaunching.
|
|
75
|
+
- The selection line is context and one action: `agent · directory` and what Enter
|
|
76
|
+
does. The history key moved to the details screen, where it is needed, and the
|
|
77
|
+
status line says nothing when there is nothing to report.
|
|
78
|
+
- A recorded directory that no longer exists now names itself, is marked in the
|
|
79
|
+
details screen, and asks for a directory to resume in instead of failing with
|
|
80
|
+
"not accessible".
|
|
81
|
+
- The mouse wheel moves the highlight with the view instead of scrolling the
|
|
82
|
+
viewport away from it.
|
|
83
|
+
- Resume preflights first. `4top check KEY` reports whether a session can resume
|
|
84
|
+
here, and the panel asks before it hands over the terminal, so a host without
|
|
85
|
+
that agent installed is a visible message instead of a failure that flashes past
|
|
86
|
+
under a repainted screen. A hand-over that still fails reports its exit code, and
|
|
87
|
+
a dropped ssh connection says that the session is unchanged in its transcript.
|
|
88
|
+
- The ssh connection keeps a liveness probe and a warm control connection, so an
|
|
89
|
+
unstable link fails instead of hanging.
|
|
90
|
+
- `doctor` reports the `revision` of the code it is running, and `--host NAME doctor`
|
|
91
|
+
reports both, so two machines on different revisions is visible instead of turning
|
|
92
|
+
into a confusing error later.
|
|
93
|
+
- `scripts/remote_update.py NAME` brings a remote's 4top forward. It uses the host's
|
|
94
|
+
own egress first and, only if that fails, lends this machine's proxy through a
|
|
95
|
+
reverse tunnel that lives exactly as long as the update. Both hosts were observed
|
|
96
|
+
with a proxy that answers on one port and fails on another, or works and then stops,
|
|
97
|
+
so the fallback is a real case and it says which one it used.
|
|
98
|
+
- `scripts/check_docs.py` fails when the documentation stops describing the code,
|
|
99
|
+
and runs in a pre-commit hook, the test suite and CI.
|
|
100
|
+
|
|
101
|
+
### Fixes in this line
|
|
102
|
+
|
|
103
|
+
- A large history no longer costs a full re-render on every refresh. Rows whose
|
|
104
|
+
inputs are unchanged keep their rendered cells and their derived labels, so a
|
|
105
|
+
2.7k-row store renders in under two milliseconds per tick instead of about 50 ms.
|
|
106
|
+
- Recognize Claude 2.1.x metadata-only session files. Claude prepends records such
|
|
107
|
+
as `last-prompt`, `mode`, `attachment` and `cost-state`, and a session that was
|
|
108
|
+
opened, renamed and quit never writes a user or assistant message; those files
|
|
109
|
+
were reported as `ValueError` instead of being listed. Identity now comes from
|
|
110
|
+
the `sessionId` these records carry, and the native `ai-title`/`agent-name` is
|
|
111
|
+
used as the title only when the session has no user text. `PARSER_VERSION` is 2
|
|
112
|
+
in both parsers, so the first scan after upgrading re-reads every file once.
|
|
113
|
+
- Report why a history file was rejected, not only the exception class, and keep
|
|
114
|
+
OSError text (which contains the private path) out of the message.
|
|
115
|
+
- Print the human table without the terminal UI stack. Display width is computed
|
|
116
|
+
with `unicodedata`, so `4top list` works where only the standard library is
|
|
117
|
+
installed. Found by running the CLI on a host that had neither the UI dependency
|
|
118
|
+
nor a package manager to add it.
|
|
119
|
+
- Stop reporting a non-directory that a history pattern matched as an
|
|
120
|
+
unavailable directory. A real store keeps a marker file inside its project
|
|
121
|
+
directory, so every scan reported an issue and every list exited 6. A configured
|
|
122
|
+
root that is not a directory is still reported once, and a genuinely unreadable
|
|
123
|
+
directory is still reported.
|
|
124
|
+
- Stop reporting a remote login banner as a query issue. Only diagnostics prefixed
|
|
125
|
+
by the remote CLI count, so a healthy host no longer looks broken and no longer
|
|
126
|
+
exits 6. Found by pointing `--host` at a machine whose ssh shell prints a banner.
|
|
127
|
+
- Correct the acceptance record: Pi 0.87.0 is installed on the acceptance Mac and
|
|
128
|
+
its capability probe passes. No authenticated Pi smoke check was run, and the
|
|
129
|
+
previous "not installed" statement was wrong.
|
|
130
|
+
|
|
131
|
+
## 4top 0.1.0a2 — 2026-09-24
|
|
132
|
+
|
|
133
|
+
- Preserve absent native store environment overrides. In particular, launching
|
|
134
|
+
Claude no longer relocates its default configuration and triggers onboarding
|
|
135
|
+
for an already-authenticated installation. Explicit profiles still take priority.
|
|
136
|
+
- Add 13 native-root regression cases covering all three agent drivers.
|
|
137
|
+
- Explain that resume uses current native configuration, not replayed launch flags.
|
|
138
|
+
- Validate 141 automated tests on macOS 27.0 arm64, plus a separate authenticated
|
|
139
|
+
Codex 0.155.1 exact-resume smoke check.
|
|
140
|
+
- Add 32 regression cases hardening the then-current process-ownership layer
|
|
141
|
+
against exit races, PID reuse and malformed ownership data.
|
|
142
|
+
- Establish the independent `4ier/4top` repository and fresh-wheel installation.
|
|
143
|
+
- Keep Pi, other native versions, physical SSH loss and Linux native validation
|
|
144
|
+
explicitly outside this release's compatibility evidence.
|
|
145
|
+
|
|
146
|
+
See [the Mac acceptance record](docs/validation/macos-0.1.0a2.md).
|
|
147
|
+
|
|
148
|
+
## 4top 0.1.0a1 / session-ls 0.2.0
|
|
149
|
+
|
|
150
|
+
- Initial keyboard-first TUI and scriptable CLI over a shared service layer.
|
|
151
|
+
- Exact-target process management, bounded previews, safe destructive actions,
|
|
152
|
+
explicit history linking and same-history reservations. That layer is not part
|
|
153
|
+
of the current line; see the unreleased section above.
|
|
154
|
+
- Experimental Claude/Codex/Pi exact resume drivers; read-only Cursor history.
|
|
155
|
+
- Private atomic state, no extra daemon, no account.
|
|
156
|
+
- Literal Unicode full search, cancellation, stable selection, no-color, isolated demo.
|
|
157
|
+
- Real process and terminal tests alongside core and headless UI tests.
|
|
158
|
+
- session-ls keeps its six-field JSON interface and independent stdlib-only package.
|
|
159
|
+
Imports no longer change SIGPIPE; full search now follows documented literal
|
|
160
|
+
semantics instead of accidentally interpreting a grep regular expression.
|
|
161
|
+
- Cached file identity includes parser version and inode metadata; cache writes use
|
|
162
|
+
unique temporary names. Corrupt caches are rebuilt rather than silently trusted.
|
|
163
|
+
|
|
164
|
+
This alpha does not certify authenticated native CLI compatibility, all minimum
|
|
165
|
+
platform versions, cross-machine aggregation, or external-user usability gates.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Contributing to 4top
|
|
2
|
+
|
|
3
|
+
Start with a reproducible bug or a small adapter fixture. The project intentionally
|
|
4
|
+
leaves process ownership, persistence and multiplexing to the user and to the
|
|
5
|
+
original CLI; proposals for agent loops, cloud accounts, semantic state guesses,
|
|
6
|
+
process supervisors or automatic privilege bypasses are outside this release.
|
|
7
|
+
|
|
8
|
+
## Development
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
python3 -m venv .venv
|
|
12
|
+
.venv/bin/python -m pip install -e ./packages/session-ls -e '.[dev]'
|
|
13
|
+
.venv/bin/python -m pytest
|
|
14
|
+
.venv/bin/python -m ruff check .
|
|
15
|
+
git config core.hooksPath .githooks # once per clone: run the gates before commit
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`scripts/check_docs.py` fails when the documentation stops describing the code: an
|
|
19
|
+
undocumented command or key, a documented command that no longer exists, a missing
|
|
20
|
+
configuration option, a broken relative link, or vocabulary from a design that has
|
|
21
|
+
been removed. It runs in the pre-commit hook, in the test suite and in CI, so a
|
|
22
|
+
stale README blocks the change instead of misleading the next reader.
|
|
23
|
+
|
|
24
|
+
To make the `4top` command itself run this checkout, install it as an editable
|
|
25
|
+
tool. Otherwise a previously installed copy keeps running older code, which looks
|
|
26
|
+
like a bug in the change you just made.
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
uv tool install --force --editable . # both packages stay live
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
No multiplexer is required. `tests/integration` starts real fake agents as real
|
|
33
|
+
processes and asserts their argv, cwd, environment and written session file; the
|
|
34
|
+
remote tests use a fake `ssh` on `PATH` instead of a network. Tests never access the
|
|
35
|
+
developer's native HOME/store or the real state directory. Unexpected external
|
|
36
|
+
pytest plugins can be excluded with
|
|
37
|
+
`PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest -p pytest_asyncio.plugin`.
|
|
38
|
+
|
|
39
|
+
Use `requirements-dev.lock` to reproduce the tested development dependency set.
|
|
40
|
+
The root package and session-ls have independent distribution metadata; do not put
|
|
41
|
+
both console scripts into the root wheel or create a circular dependency.
|
|
42
|
+
|
|
43
|
+
## Required evidence
|
|
44
|
+
|
|
45
|
+
A PR explains the user-visible change, safety boundary, tests, actual environment,
|
|
46
|
+
uncovered cases and rollback. Adapter PRs include synthetic/redacted fixtures and
|
|
47
|
+
an exact native CLI version with capabilities split into history/new/resume.
|
|
48
|
+
No arbitrary plugin code is loaded from history or configuration.
|
|
49
|
+
|
|
50
|
+
Acceptance must distinguish unit tests, headless Textual, real-process launch,
|
|
51
|
+
remote transport and human usability tests. Do not turn a mock result into an authenticated smoke-test
|
|
52
|
+
claim. Performance results name the dataset, machine, versions and measurement.
|
|
53
|
+
No recorded workload needs to contain real customer data.
|
|
54
|
+
|
|
55
|
+
Run and update the [acceptance guide](docs/validation/README.md) before a release.
|
|
56
|
+
Export the synthetic UI with `python scripts/export_demo.py`; the result must say
|
|
57
|
+
DEMO. It is not a real-CLI demonstration or a performance benchmark.
|
|
58
|
+
|
|
59
|
+
## Release
|
|
60
|
+
|
|
61
|
+
Publishing is a tag; CI does the rest. PyPI versions are immutable, so a gate runs
|
|
62
|
+
before anything is uploaded.
|
|
63
|
+
|
|
64
|
+
1. Set the version in `pyproject.toml`, and in `packages/session-ls/pyproject.toml`
|
|
65
|
+
when the parser changed, and update the changelog.
|
|
66
|
+
2. Check locally: `python scripts/check_release.py v<version>`.
|
|
67
|
+
3. Tag and push: `git tag v<version> && git push origin v<version>`.
|
|
68
|
+
4. CI runs the suite, gates the release, builds both wheels, installs them into a
|
|
69
|
+
fresh virtual environment, publishes `4top`, and opens the GitHub release.
|
|
70
|
+
|
|
71
|
+
The gate is what stops the release that cannot work: the root wheel requires
|
|
72
|
+
`session-ls>=0.2.0`, PyPI only ever held 0.1.0, so publishing the root alone would
|
|
73
|
+
have produced a package nobody could install. A version already on PyPI is skipped
|
|
74
|
+
rather than treated as an error, so re-running a release is safe;
|
|
75
|
+
`python scripts/check_release.py --decide` says what would be uploaded.
|
|
76
|
+
|
|
77
|
+
Uploads use PyPI trusted publishing, so no token is stored here. It needs one
|
|
78
|
+
publisher on PyPI for the project `4top`, pointing at owner `4ier`, repository
|
|
79
|
+
`4top`, workflow `publish.yml` and environment `pypi`.
|
|
80
|
+
|
|
81
|
+
This repository publishes one project. `session-ls` has its own repository and its
|
|
82
|
+
own pipeline and is consumed here as an ordinary PyPI dependency; publishing two
|
|
83
|
+
projects from one workflow would need two jobs and two distinguishable publishers,
|
|
84
|
+
because one job means one OIDC exchange and the minted token is scoped to the project
|
|
85
|
+
its publisher matched. The
|
|
86
|
+
manual trigger of that workflow defaults to a dry run, which rehearses the whole
|
|
87
|
+
path without uploading.
|
|
88
|
+
|
|
89
|
+
## Reporting
|
|
90
|
+
|
|
91
|
+
Use the bug template and redact paths, prompts and credentials. For security issues,
|
|
92
|
+
see SECURITY.md. Do not attach native auth files, raw production transcripts, or an
|
|
93
|
+
unreviewed screen recording. No automated issue upload is performed by 4top.
|
|
94
|
+
|
|
95
|
+
Code is MIT-licensed. Preserve attribution to the existing session-ls core. Keep
|
|
96
|
+
English and Chinese README commands and limitations in sync.
|
4top-0.2.0a1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 4ier
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
4top-0.2.0a1/MANIFEST.in
ADDED
4top-0.2.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: 4top
|
|
3
|
+
Version: 0.2.0a1
|
|
4
|
+
Summary: Your coding agents, one terminal. Find, search and resume native sessions, locally or over SSH.
|
|
5
|
+
Author: 4ier
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/4ier/4top
|
|
8
|
+
Project-URL: Issues, https://github.com/4ier/4top/issues
|
|
9
|
+
Keywords: coding-agents,terminal,session-history,claude,codex,pi,ssh
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console :: Curses
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: session-ls<0.3,>=0.2.0
|
|
19
|
+
Requires-Dist: textual<9,>=8.2.8
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest<10,>=8; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-asyncio<2,>=0.23; extra == "dev"
|
|
23
|
+
Requires-Dist: pexpect<5,>=4.9; extra == "dev"
|
|
24
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == "dev"
|
|
25
|
+
Requires-Dist: build<2,>=1; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# 4top
|
|
29
|
+
|
|
30
|
+
**Your coding agents, one terminal.**
|
|
31
|
+
|
|
32
|
+
Find your work. Resume an exact native session. Reach your other machines over SSH.
|
|
33
|
+
A keyboard-first terminal dashboard built on **session-ls**, with no extra 4top
|
|
34
|
+
daemon, account, model calls, or telemetry.
|
|
35
|
+
|
|
36
|
+
[中文](README.zh-CN.md) · [Compatibility](docs/compatibility.md) · [Remote hosts](docs/remote-design.md) · [Acceptance](docs/validation/README.md)
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
> **0.2.0a1 — alpha.** The current development line owns no process and drives no
|
|
41
|
+
> multiplexer of its own. Codex **0.155.1** passed an authenticated exact-resume
|
|
42
|
+
> smoke check against an earlier build;
|
|
43
|
+
> Claude Code, Pi and other native versions are not certified.
|
|
44
|
+
> [Evidence](docs/validation/macos-0.1.0a2.md). This release is not on PyPI.
|
|
45
|
+
|
|
46
|
+
## Install from this checkout
|
|
47
|
+
|
|
48
|
+
macOS or Linux; **Python 3.11+**. No multiplexer is required. Current validation
|
|
49
|
+
records, rather than this minimum target, determine which versions were tested.
|
|
50
|
+
Windows users need a Linux environment such as WSL; native Windows is unsupported.
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
git clone https://github.com/4ier/4top.git
|
|
54
|
+
cd 4top
|
|
55
|
+
python3 -m venv .venv
|
|
56
|
+
.venv/bin/python -m pip install ./packages/session-ls .
|
|
57
|
+
.venv/bin/4top --demo
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Both local packages are intentionally supplied to pip: `session-ls 0.2.0` is not
|
|
61
|
+
assumed to exist on a package registry. The root wheel contains only `fourtop`;
|
|
62
|
+
the independent `session-ls` package retains its small, stdlib-only CLI.
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
. .venv/bin/activate
|
|
66
|
+
4top # browse every session on this machine
|
|
67
|
+
4top new codex # start an agent here, in this terminal
|
|
68
|
+
4top --host build-box # view another machine over ssh
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
4top never installs or authenticates agents for you. Install the original Claude
|
|
72
|
+
Code, Codex, or Pi CLI separately and keep its existing authentication flow.
|
|
73
|
+
The panel also reads Cursor transcripts, but does not launch or resume Cursor.
|
|
74
|
+
|
|
75
|
+
## The daily loop
|
|
76
|
+
|
|
77
|
+
Open `4top` and you get every session on the machine, most recent first. Select a
|
|
78
|
+
row and press **Enter**: 4top asks for confirmation and then runs the native CLI
|
|
79
|
+
in this terminal to resume that exact session. Leave the agent and you are back in
|
|
80
|
+
the panel. **`q` closes only the panel.**
|
|
81
|
+
|
|
82
|
+
A plain terminal or any multiplexer you already run is equally fine: 4top starts
|
|
83
|
+
the agent in the terminal it was launched from and never allocates a terminal of
|
|
84
|
+
its own. If you want a session to survive closing your laptop, run 4top inside the
|
|
85
|
+
multiplexer you already use.
|
|
86
|
+
|
|
87
|
+
| Key | Action |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `↑` / `↓`, `Enter` | Select and resume |
|
|
90
|
+
| `H` | Switch the panel between this machine and a configured host |
|
|
91
|
+
| `/`, `Enter`, `Esc` | Search metadata, return to table, clear/cancel |
|
|
92
|
+
| `Ctrl-F` | Explicit literal full-content search; `Esc` cancels |
|
|
93
|
+
| `Space`, `i` | Read-only preview, details |
|
|
94
|
+
| `n`, `r`, `?` | New agent, refresh, help |
|
|
95
|
+
| `q`, `Ctrl-C` | Close only the panel |
|
|
96
|
+
|
|
97
|
+
Search supports case-insensitive words and quoted phrases; all terms must match.
|
|
98
|
+
Full search decodes JSON text, including Chinese escaped as `\u....`. It reads
|
|
99
|
+
only configured sources, reports partial scans, and never executes transcript
|
|
100
|
+
content. The UI renders titles and previews as plain, sanitized text.
|
|
101
|
+
|
|
102
|
+
## Sessions, not processes
|
|
103
|
+
|
|
104
|
+
A native agent is a file. `pi --session <path>`, `claude --resume <id>` and
|
|
105
|
+
`codex resume <id>` all work from the transcript alone, so the transcript is the
|
|
106
|
+
thing 4top tracks, and the process is the ephemeral part.
|
|
107
|
+
|
|
108
|
+
**Resume** starts a new native process from an exact ID or source path. It cannot
|
|
109
|
+
restore lost memory, network connections, shell children, or a destroyed machine.
|
|
110
|
+
Resume uses the CLI's **current native configuration**; 4top does not replay the
|
|
111
|
+
original launch flags. Review native permissions before sending another task.
|
|
112
|
+
|
|
113
|
+
Because 4top owns no process it claims nothing about liveness either: a row is a
|
|
114
|
+
session you can resume, and that is all it says. Failed queries are reported as
|
|
115
|
+
issues and never rendered as an empty machine.
|
|
116
|
+
|
|
117
|
+
Two consequences worth knowing. A resumed agent is a **new** process; two agents in
|
|
118
|
+
one directory still have **no code/worktree isolation**. And `4top new` runs the
|
|
119
|
+
agent in the foreground of the terminal you launched it from: outside a multiplexer
|
|
120
|
+
it ends with that terminal.
|
|
121
|
+
|
|
122
|
+
## Remote hosts over SSH
|
|
123
|
+
|
|
124
|
+
Point 4top at any machine you can already `ssh` into. There is no daemon to
|
|
125
|
+
install, no port to open, and no credential store: the remote side is the same
|
|
126
|
+
CLI, and the local 4top only runs it.
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
# ~/.config/4top/config.toml
|
|
130
|
+
[hosts.build-box]
|
|
131
|
+
ssh = "me@build-box" # any ssh destination, including a tailnet name
|
|
132
|
+
# command = "/opt/4top/bin/4top" # if a non-login PATH does not include 4top
|
|
133
|
+
# refresh_seconds = 15.0 # slower than local: each tick is a round trip
|
|
134
|
+
# timeout_seconds = 10.0
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
4top --host build-box # the whole panel, scoped to that host
|
|
139
|
+
4top --host build-box list --json
|
|
140
|
+
4top --host me@10.0.0.4 doctor # an unconfigured target works too
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Views stay isolated: the default scope is this machine, and a host replaces it
|
|
144
|
+
rather than merging machines into one table. `H` switches the panel between them
|
|
145
|
+
without leaving it; `--host NAME` starts the panel already scoped. Anything that starts a process runs
|
|
146
|
+
**on that host** through `ssh -t`, so the resumed agent lives where its history
|
|
147
|
+
lives; the remote CLI does the work and the local side only hands over the
|
|
148
|
+
terminal. Connection reuse (`ControlMaster`) keeps refreshes cheap, `BatchMode`
|
|
149
|
+
means a missing key fails fast instead of prompting, and a remote that speaks a
|
|
150
|
+
different row schema is refused instead of partially parsed.
|
|
151
|
+
|
|
152
|
+
Before it hands over the terminal, the panel asks the host that owns the session
|
|
153
|
+
whether the resume can work there (`4top check`). A host without that agent
|
|
154
|
+
installed, or a session whose directory is gone, is reported in the panel instead
|
|
155
|
+
of failing during the hand-over, where the message would be painted over. The ssh
|
|
156
|
+
connection also keeps a liveness probe, so a link that dies becomes an error rather
|
|
157
|
+
than a hang, and the session stays in its transcript to be resumed again.
|
|
158
|
+
|
|
159
|
+
## Command line
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
4top list --json # one JSON object per row
|
|
163
|
+
4top list --agent pi --project 4top
|
|
164
|
+
4top search 'retry "database timeout"' # metadata match
|
|
165
|
+
4top search '中文' --full # decoded full-content search
|
|
166
|
+
4top preview h_<key> # one bounded read-only page
|
|
167
|
+
4top check h_<key> --json # would a resume work here, and why not
|
|
168
|
+
4top new codex -- --model MODEL # native arguments after --
|
|
169
|
+
4top resume h_<key> --yes # restore this process as the agent
|
|
170
|
+
4top doctor --json
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`doctor` also reports `revision`, and `4top --host NAME doctor` reports it for both
|
|
174
|
+
sides, so a remote running older code is visible instead of failing later. Bring a
|
|
175
|
+
remote forward with `scripts/remote_update.py NAME`; it uses the host's own egress
|
|
176
|
+
first and falls back to a tunnel from this machine.
|
|
177
|
+
|
|
178
|
+
`--config`, `--host` and `--no-color` work before or after the subcommand.
|
|
179
|
+
`check` exits 0 when the session can resume here and 3 when it cannot. Keys
|
|
180
|
+
may be shortened only when their prefixes are unambiguous (at least four
|
|
181
|
+
characters). Row numbers are never execution targets. `list --json` rows carry
|
|
182
|
+
`schema_version`, `key`, `agent`, `host`, `cwd`, `title`, `started`, `last`,
|
|
183
|
+
`source`, `status` and `can_resume`.
|
|
184
|
+
|
|
185
|
+
## Configuration and privacy
|
|
186
|
+
|
|
187
|
+
Optional configuration: `$XDG_CONFIG_HOME/4top/config.toml` (default
|
|
188
|
+
`~/.config/4top/config.toml`). No setup file is needed for standard stores.
|
|
189
|
+
|
|
190
|
+
```toml
|
|
191
|
+
[ui]
|
|
192
|
+
refresh_seconds = 1.0
|
|
193
|
+
history_refresh_seconds = 5.0
|
|
194
|
+
color = "auto" # or "none"; NO_COLOR is also supported
|
|
195
|
+
|
|
196
|
+
[history]
|
|
197
|
+
metadata_max_bytes = 2097152
|
|
198
|
+
metadata_max_lines = 2000
|
|
199
|
+
preview_max_lines = 200
|
|
200
|
+
|
|
201
|
+
[agents.codex]
|
|
202
|
+
# root = "/absolute/path/to/codex-home"
|
|
203
|
+
# executable = "/absolute/path/to/a-real-wrapper"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Agent store roots respect `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, and
|
|
207
|
+
`PI_CODING_AGENT_DIR`; an explicit configured root wins. Selected history and
|
|
208
|
+
launch profile must agree. Local state is private: `$XDG_STATE_HOME/4top` holds a
|
|
209
|
+
local identity and your last selection, and `$XDG_CACHE_HOME/4top` holds
|
|
210
|
+
rebuildable metadata. No environment values, prompt text, or transcripts are
|
|
211
|
+
retained. The only network access is the ssh you configured.
|
|
212
|
+
|
|
213
|
+
[Privacy](docs/privacy.md) · [Troubleshooting](docs/troubleshooting.md) · [Design](docs/design.md)
|
|
214
|
+
|
|
215
|
+
## Develop and contribute
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
.venv/bin/python -m pip install -e ./packages/session-ls -e '.[dev]'
|
|
219
|
+
.venv/bin/python -m pytest
|
|
220
|
+
.venv/bin/python -m ruff check .
|
|
221
|
+
uv tool install --force --editable . # optional: `4top` runs this checkout
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Tests use private temporary HOME/state directories and synthetic agents; the ssh
|
|
225
|
+
tests use a fake `ssh` on `PATH`. No account credentials, network access or model
|
|
226
|
+
calls are required. Record your OS, Python and native CLI versions when reporting
|
|
227
|
+
compatibility. **Never post raw transcripts or tokens.**
|
|
228
|
+
|
|
229
|
+
For fresh installation, CI, reproducible demo export, native smoke testing, and
|
|
230
|
+
release gates, see [CONTRIBUTING](CONTRIBUTING.md) and the [acceptance guide](docs/validation/README.md).
|
|
231
|
+
|
|
232
|
+
4top builds on 4ier's `session-ls` parsers and uses Textual. It is not affiliated
|
|
233
|
+
with the vendors of the supported coding agents. **MIT licensed.**
|