feat: add interactive WIP terminal client
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# wip-tui
|
||||
|
||||
`wip-tui` is an interactive terminal client for exploring and operating a WIP
|
||||
Worldspace. It is a presentation and terminal-interaction layer over
|
||||
`wip-client`: Known Space, request identity, validators, immutable operation call
|
||||
contexts, stale transitions, response classification, and bounded histories remain
|
||||
owned by the client runtime.
|
||||
|
||||
## Start
|
||||
|
||||
HTTPS is the normal mode:
|
||||
|
||||
```console
|
||||
cargo run -p wip-tui -- --endpoint https://wip.example.test/api/wip
|
||||
```
|
||||
|
||||
The endpoint may instead be supplied as `WIP_ENDPOINT`. A non-secret cache
|
||||
partition identity may be supplied with `--security-context NAME` or
|
||||
`WIP_SECURITY_CONTEXT`. The default request timeout is 30 seconds and can be
|
||||
changed with `--timeout SECONDS`.
|
||||
|
||||
An Authorization header can be read from an environment variable without placing
|
||||
its value in arguments, application state, errors, snapshots, or debug output:
|
||||
|
||||
```console
|
||||
export WIP_AUTHORIZATION='Bearer …'
|
||||
cargo run -p wip-tui -- https://wip.example.test/wip
|
||||
```
|
||||
|
||||
Use `--authorization-env OTHER_VARIABLE` to select another variable. The value is
|
||||
redacted from `Debug` and is never rendered. Shell and process environment security
|
||||
remain the operator's responsibility.
|
||||
|
||||
Plain HTTP is rejected by default. It is available only as an explicit loopback
|
||||
development mode:
|
||||
|
||||
```console
|
||||
cargo run -p wip-tui -- http://127.0.0.1:8080/wip --allow-http
|
||||
```
|
||||
|
||||
Non-loopback HTTP remains rejected even with `--allow-http`. For production, use a
|
||||
real HTTPS endpoint and a trusted TLS deployment.
|
||||
|
||||
### Filesystem server example
|
||||
|
||||
In one terminal:
|
||||
|
||||
```console
|
||||
mkdir -p /tmp/wip-example
|
||||
printf 'hello world' >/tmp/wip-example/notes.txt
|
||||
cargo run -p wip-fs-server -- \
|
||||
--root /tmp/wip-example \
|
||||
--listen 127.0.0.1:8080 \
|
||||
--base-path /wip \
|
||||
--development-http \
|
||||
--read-write
|
||||
```
|
||||
|
||||
In another:
|
||||
|
||||
```console
|
||||
cargo run -p wip-tui -- http://127.0.0.1:8080/wip --allow-http
|
||||
```
|
||||
|
||||
Expand `/`, select `notes.txt`, focus the Interface/Operation pane, load the read
|
||||
interface, and choose `read`. The editor labels `offset` and `length` as optional;
|
||||
leave both blank to read the bounded file. `Bytes` results are shown as a bounded
|
||||
hex preview. For `replace`, enter `contents` as RFC 4648 base64—for example,
|
||||
`dXBkYXRlZA==` for `updated`—then review the target Entry, interface reference,
|
||||
operation, and value on the confirmation screen before pressing Enter again.
|
||||
|
||||
## Keyboard bindings
|
||||
|
||||
All required actions are keyboard-accessible.
|
||||
|
||||
| Key | Action |
|
||||
| --- | --- |
|
||||
| `↑`/`k`, `↓`/`j` | Move within the focused tree, operation list, history, or editor |
|
||||
| `→`/`l`, `Enter` | Expand a tree node lazily; load/select an interface or operation |
|
||||
| `←`/`h` | Collapse a node or move to its parent |
|
||||
| `Tab` | Cycle Tree → Selected Entry → Operations → History |
|
||||
| `r` | Reobserve the selected Entry and its direct subtree |
|
||||
| `+` | Double the selected node limit and retry an incomplete/truncated tree |
|
||||
| `c` | Cancel the newest retrieval; a dispatched operation is never presented as canceled |
|
||||
| `R` | Reconnect after connection failure |
|
||||
| `?` | Open help |
|
||||
| `Esc` | Close a modal or return to the tree |
|
||||
| `q`, `Ctrl-C` | Exit and restore the terminal |
|
||||
|
||||
Inside an argument editor, printable keys are field input, arrows move fields,
|
||||
Backspace edits, Enter validates, and Esc cancels. The confirmation screen requires
|
||||
a second explicit Enter (or `e`) to execute once. There is no operation auto-retry.
|
||||
|
||||
## Screen model
|
||||
|
||||
- **Worldspace tree** starts at `/` and materializes direct children only when a
|
||||
node is expanded. Text labels distinguish unloaded, loading, empty, stale,
|
||||
failed, and truncated state; color is supplemental. A truncated node is never
|
||||
presented as a complete tree.
|
||||
- **Selected Entry** shows only public WIP path, object name, description, ordered
|
||||
interfaces, and freshness. Validators, credentials, Host internals, and backend
|
||||
paths are deliberately hidden. If current metadata disappears, old text is not
|
||||
presented as current.
|
||||
- **Interfaces / Operations** identifies every operation by `(interface reference,
|
||||
operation name)`. It shows descriptor documentation, ordered parameters,
|
||||
required/optional status, and result types.
|
||||
- **Argument editor** supports Unit, Boolean, Integer, Number, String, base64 Bytes,
|
||||
Record, List, Entry, Enum, Union, Json, and descriptor-local named types. Record,
|
||||
List, Union, and Json values use JSON syntax. Union input is
|
||||
`{"$case":"name","value":…}` when the selected case has a payload. Diagnostics
|
||||
remain attached to the failing field.
|
||||
- **Recent results / diagnostics** explicitly distinguishes success, Host failure,
|
||||
invalid protocol response, transport failure, client-local failure, and unknown
|
||||
operation outcome. Large values, bytes, nesting, and control characters are
|
||||
bounded before rendering. Only descriptor-declared `Entry` results become
|
||||
navigation targets; path-looking String or Json values do not.
|
||||
|
||||
Terminals below 48×14 show a minimum-size screen instead of attempting the full
|
||||
layout.
|
||||
|
||||
## Validator and failure behavior
|
||||
|
||||
Object and interface validators are observed and attached automatically by
|
||||
`wip-client`; users neither copy nor edit them. A successful operation conservatively
|
||||
marks related object observations stale because metadata may have changed.
|
||||
Validator mismatch, interface mismatch, `OperationOutcomeUnknown`, undecodable
|
||||
post-dispatch responses, and post-dispatch timeout/disconnect are displayed using
|
||||
the runtime's authoritative transition.
|
||||
|
||||
For an unknown outcome, the UI states that execution or side effects may have
|
||||
occurred, marks the related Entry stale, and tells the user to refresh. Refreshing
|
||||
does not recreate the call: the user must select the operation, review fresh input,
|
||||
and explicitly execute again. The same no-auto-retry rule applies after validator
|
||||
or interface mismatch.
|
||||
|
||||
Retrieval work runs on I/O workers while the input/render loop continues. Responses
|
||||
return with the original `RequestId`, so an old completion cannot populate another
|
||||
Entry's details. Canceling a retrieval records a local cancellation and drops its
|
||||
late response. Once an operation crosses dispatch, cancellation is never described
|
||||
as undoing execution.
|
||||
|
||||
## Tests
|
||||
|
||||
The library form keeps reducers and rendering independent of a real terminal.
|
||||
`ratatui::TestBackend` snapshots cover tree/detail/result states and the minimum-size
|
||||
fallback. Unit tests cover reducer focus, selection, modal confirmation, key
|
||||
translation, descriptor inputs, bounded values, and descriptor-directed Entry
|
||||
results. The real HTTP integration test launches `wip-fs-server` on a temporary
|
||||
root and covers tree selection, reads, validator-protected replacement, external
|
||||
stale-validator rejection without retry, explicit refresh/re-execution, and
|
||||
symlink confinement.
|
||||
Reference in New Issue
Block a user