@baleen37/ars 1.1.0 → 2.0.0

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.
package/README.md CHANGED
@@ -1,9 +1,14 @@
1
1
  # ars
2
2
 
3
- `ars` lists Claude Code and Codex sessions from configured SSH hosts, combines
4
- them in one searchable picker, and resumes the selected native session. It is a
5
- single local Go binary: remote hosts do not need an `ars` install, daemon,
6
- cache, or background process.
3
+ `ars` is a full-screen Claude Code and Codex session navigator for the current
4
+ computer and explicitly configured SSH peers. Selecting a row creates or
5
+ attaches to a provider process in an ARS-owned tmux server. `Ctrl+Q` detaches
6
+ the terminal client, keeps that provider process alive on its original host,
7
+ and returns to the same refreshed TUI.
8
+
9
+ ARS is one local Go binary. Peers need no `ars` install, daemon, database,
10
+ cache, index, or background helper. Collection uses a private, one-shot,
11
+ version-matched ARS/2 helper over the user's existing OpenSSH configuration.
7
12
 
8
13
  ## Install
9
14
 
@@ -22,11 +27,13 @@ Prerequisites:
22
27
 
23
28
  - Go as declared by `go.mod`, for building from source
24
29
  - OpenSSH `ssh` locally and an OpenSSH-compatible server on each remote host
25
- - `fzf` locally for interactive selection; `ars list --json` does not use it
30
+ - `tmux` on every managed computer, including the current one
26
31
  - a POSIX `/bin/sh` plus `uname`, `mkdir`, `cat`, `chmod`, `rm`, and `rmdir` on
27
32
  each remote host
28
- - `claude` and/or `codex` on the remote `PATH`, with their native session
29
- metadata under the remote user's home directory
33
+ - `claude` and/or `codex` on each node's `PATH`, with native session metadata
34
+ under that user's home directory
35
+
36
+ `fzf` is not used or required.
30
37
 
31
38
  Build the three collector assets and the local executable:
32
39
 
@@ -40,11 +47,14 @@ local `ars` build: `darwin/arm64`, `linux/amd64`, and `linux/arm64`. Generated
40
47
  collector blobs and the root `ars` build artifact are local build outputs and
41
48
  must not be committed.
42
49
 
43
- ## Inventory
50
+ ## Localhost and remote inventory
44
51
 
45
52
  The default inventory is `${XDG_CONFIG_HOME}/ars/hosts` when
46
- `XDG_CONFIG_HOME` is set, otherwise `~/.config/ars/hosts`. Put one OpenSSH
47
- target on each line, in the order it should be reported:
53
+ `XDG_CONFIG_HOME` is set, otherwise `~/.config/ars/hosts`. The current computer
54
+ is always included as `localhost`; it requires no configuration. The hosts file
55
+ contains only OpenSSH peers. A missing hosts file is a valid local-only
56
+ configuration. Put one peer target on each line, in the order it should be
57
+ reported:
48
58
 
49
59
  ```text
50
60
  # ~/.config/ars/hosts
@@ -53,35 +63,71 @@ deploy@example.internal
53
63
  agent-mac
54
64
  ```
55
65
 
56
- Blank lines and comments are ignored. Targets may use names and aliases from
57
- the user's SSH config. Duplicates, whitespace, control characters, a leading
58
- dash, and targets over 255 bytes make the entire inventory invalid. `ars` does
59
- not infer `localhost` or discover hosts.
66
+ `ars localhost` opens only current-computer sessions. `localhost` is reserved
67
+ and cannot be added with `ars remote add`.
68
+
69
+ When upgrading an existing installation, read the exact value from its
70
+ `${XDG_CONFIG_HOME}/ars/local-host` file, or `~/.config/ars/local-host` when
71
+ `XDG_CONFIG_HOME` is unset. Remove that exact entry and any literal `localhost`
72
+ entry from `hosts`, leaving only SSH peers. ARS does not read or delete the
73
+ legacy `local-host` file.
74
+
75
+ Blank lines and comments in `hosts` are ignored. Targets may use names and
76
+ aliases from the user's SSH config. Duplicates, whitespace, control characters,
77
+ a leading dash, and targets over 255 bytes make the entire inventory invalid.
60
78
 
61
79
  `ars remote add <host>` creates the inventory and its parent directory when
62
80
  missing, preserves existing comments, entries, and order, and rejects invalid
63
- or duplicate targets. A target beginning with `#` is rejected because inventory
64
- loading would interpret it as a comment. The command does not edit `~/.ssh/config`.
81
+ or duplicate targets. It also rejects the reserved `localhost` target. A target
82
+ beginning with `#` is rejected because inventory loading would interpret it as
83
+ a comment. The command does not edit `~/.ssh/config`.
65
84
 
66
85
  ## Commands
67
86
 
68
87
  The supported command forms are:
69
88
 
70
89
  ```sh
71
- ars # search sessions from every configured host
72
- ars devbox # search one configured host
90
+ ars # search localhost and every configured SSH peer
91
+ ars localhost # search only current-computer sessions
92
+ ars devbox # search one configured SSH peer
73
93
  ars list --json # return all hosts, sessions, and errors as JSON
74
94
  ars remote add devbox # add an SSH target to the ARS inventory
75
95
  ars --help # show all command forms
76
96
  ars remote --help # show remote command help
77
97
  ```
78
98
 
79
- Interactive rows contain a private numeric index followed by display-only
80
- metadata. `fzf` returns only that opaque index; titles, paths, and host names
81
- are never parsed back into identity. Enter resumes the selected session.
82
- Canceling `fzf` exits zero without starting the resume SSH connection. A
83
- healthy result with no sessions prints `No sessions found.` and does not open
84
- `fzf`.
99
+ `ars` and `ars <host>` require a TTY. Use `ars list --json` for a headless,
100
+ one-shot result.
101
+
102
+ ## TUI
103
+
104
+ The screen has one line per session:
105
+
106
+ ```text
107
+ state | title | provider | location | project | runtime | activity age
108
+ ```
109
+
110
+ The location column is blank for current-computer sessions and shows the
111
+ configured SSH target for peers.
112
+
113
+ `Active` contains ARS-owned runtimes: `attached(n)` has one or more terminal
114
+ clients and `running` has none. `Recent` contains saved provider histories with
115
+ no ARS runtime. The footer shows the selected row's full CWD, native ID, and
116
+ exact update time. Search is a Unicode-aware, case-insensitive substring match;
117
+ there is no fuzzy ranker.
118
+
119
+ Keys:
120
+
121
+ - `Up`, `Down`, `j`, `k`: move
122
+ - `/`: search
123
+ - `Enter`: start or attach
124
+ - `r`: refresh
125
+ - `q`, `Ctrl+C`: quit ARS
126
+ - `Ctrl+Q`: detach only while inside an attached ARS tmux client
127
+
128
+ The screen collects at startup, on `r`, and after attach returns. It does not
129
+ poll, watch, or cache in the background. Canonical host/provider/native-ID data,
130
+ never rendered row text, determines the attach command.
85
131
 
86
132
  ## Session inclusion
87
133
 
@@ -102,6 +148,26 @@ metadata file modification time as `updated_at`, validate canonical UUIDs, and
102
148
  deduplicate by host, provider, and native ID. A missing executable or metadata
103
149
  tree is a healthy absent-provider result, not a host failure.
104
150
 
151
+ ## Persistent provider runtime
152
+
153
+ ARS uses a dedicated versioned, per-user tmux server (`ars-v1`) with
154
+ `-f /dev/null`. It does not use, configure, rename, attach, or bind keys in the
155
+ user's default tmux server. It creates exactly one hashed runtime for the
156
+ selected `(provider, native ID)` and rechecks after a concurrent create instead
157
+ of starting a duplicate provider process.
158
+
159
+ On first selection, ARS starts the adapter's fixed native resume command in the
160
+ saved CWD. Later selections attach that same runtime. `Ctrl+Q` is a prefix-free
161
+ binding in the ARS server and runs `detach-client`; it is not delivered to
162
+ Claude or Codex. When another computer attaches with `ssh -tt`, tmux detaches
163
+ the previous client back to its TUI and hands the same provider process to the
164
+ new client.
165
+
166
+ If the provider exits, its tmux session ends and the native history remains in
167
+ `Recent`. A host reboot or loss of the ARS tmux server ends the live process but
168
+ does not remove native history. ARS does not discover or adopt providers that
169
+ were started outside its own tmux server.
170
+
105
171
  ## JSON schema version 1
106
172
 
107
173
  Public JSON uses dedicated DTOs and always ends with a newline:
@@ -110,12 +176,12 @@ Public JSON uses dedicated DTOs and always ends with a newline:
110
176
  {
111
177
  "schema_version": 1,
112
178
  "hosts": [
113
- {"target": "devbox", "status": "ok"},
179
+ {"target": "localhost", "status": "ok"},
114
180
  {"target": "down", "status": "error"}
115
181
  ],
116
182
  "sessions": [
117
183
  {
118
- "host": "devbox",
184
+ "host": "localhost",
119
185
  "provider": "claude",
120
186
  "native_id": "123e4567-e89b-42d3-a456-426614174000",
121
187
  "updated_at": "2026-07-19T01:02:03Z",
@@ -130,14 +196,14 @@ Public JSON uses dedicated DTOs and always ends with a newline:
130
196
  ```
131
197
 
132
198
  `schema_version` is `1`. Timestamps are UTC-capable RFC3339Nano strings.
133
- `hosts` records every selected inventory target exactly once. `status=ok` with
199
+ `hosts` records every selected target exactly once. `status=ok` with
134
200
  an empty `sessions` array distinguishes a healthy empty host from an
135
201
  unreachable host. Stable error codes are `ssh_timeout`, `ssh_failed`,
136
202
  `unsupported_target`, `protocol_error`, and `resource_limit`.
137
203
 
138
- ## SSH and failure behavior
204
+ ## SSH and partial-failure behavior
139
205
 
140
- Collection and resume deliberately use separate SSH modes:
206
+ Collection and attach deliberately use separate SSH modes:
141
207
 
142
208
  - Collection is non-interactive (`BatchMode=yes`, no agent/X11/port
143
209
  forwarding), uses separate probe and upload invocations that each make one
@@ -145,23 +211,25 @@ Collection and resume deliberately use separate SSH modes:
145
211
  `StrictHostKeyChecking=yes`, uses a 5-second connect timeout, and gives the
146
212
  full probe/upload/protocol flow 60 seconds per host. Unknown hosts fail; ars
147
213
  never accepts or rewrites `known_hosts` entries.
148
- - Resume runs `ssh -tt` with the user's normal SSH configuration,
149
- authentication, and forwarding behavior. It executes only
150
- `cd <saved-cwd> && exec claude --resume <uuid>` or
151
- `cd <saved-cwd> && exec codex resume <uuid>`. Resume preserves the SSH exit
152
- code when available.
214
+ - Attach runs `ssh -tt` with the user's normal OpenSSH configuration,
215
+ authentication, and host-key verification. The bounded fixed remote script
216
+ checks or creates the exact ARS tmux runtime, reapplies `Ctrl+Q`, and attaches
217
+ with previous-client detachment. ARS does not weaken host-key checking or
218
+ write SSH configuration.
153
219
 
154
220
  Hosts are collected with at most four workers. One failed host produces a
155
- structured error while healthy peer sessions remain usable. If every selected
156
- host fails, `ars` exits non-zero and never opens `fzf`. A healthy empty host is
157
- success. Provider-level corrupt records can yield a partial provider result
158
- without discarding its valid sessions.
221
+ bounded visible error while healthy local and peer sessions remain searchable
222
+ and attachable. A provider or runtime warning is shown beside otherwise healthy
223
+ sessions. If every selected host fails, the TUI still permits refresh and quit;
224
+ JSON mode writes the structured result and exits non-zero. A healthy empty host
225
+ is success. Provider-level corrupt records can yield a warning without
226
+ discarding valid sessions.
159
227
 
160
228
  ## Bounds and cleanup
161
229
 
162
230
  Collection is bounded and fail-closed:
163
231
 
164
- - 64 KiB startup noise and 64 KiB per ARS/1 protocol line
232
+ - 64 KiB startup noise and 64 KiB per ARS/2 protocol line
165
233
  - 16 MiB total collector stdout and 64 KiB diagnostic stderr
166
234
  - 10,000 sessions total; provider traversal also caps discovered sessions at
167
235
  10,000
@@ -178,13 +246,16 @@ that exact leftover.
178
246
 
179
247
  ## Privacy
180
248
 
181
- The collector returns validated metadata only: provider, native UUID,
182
- modification time, saved CWD, and native title. Prompts, responses, tool input
183
- or output, credentials, raw transcript lines, provider source paths, and
184
- filenames never cross the ARS/1 boundary. Host diagnostics are bounded and
185
- sanitized before public output. The saved CWD and native title are still
186
- potentially sensitive metadata, so treat JSON output and terminal history
187
- accordingly.
249
+ The private ARS/2 collector returns validated metadata only: provider, native
250
+ UUID, modification time, saved CWD, native title, runtime state, attached-client
251
+ count, and runtime start time. Prompts, responses, tool input or output,
252
+ credentials, raw transcript lines, provider source paths, filenames, pane
253
+ content, and attached terminal output never cross the collection boundary.
254
+ Host diagnostics are bounded and sanitized before display.
255
+
256
+ Public JSON schema v1 deliberately omits all runtime fields and remains the
257
+ provider-history view. Saved CWD and native title are still potentially
258
+ sensitive metadata, so protect JSON output and terminal history accordingly.
188
259
 
189
260
  ## Verification
190
261
 
@@ -196,20 +267,54 @@ go test ./...
196
267
  go test -race ./...
197
268
  go vet ./...
198
269
  go run ./cmd/ars-build
270
+ go test ./internal/runtime -run TestDisposableTmux -count=1 -v
271
+ go test ./internal/tui -run TestPTYAttachDetachRestoresTUI -count=1 -v
272
+ npm test
273
+ git diff --check
199
274
  ```
200
275
 
201
276
  The ephemeral loopback sshd integration is disposable and opt-in:
202
277
 
203
278
  ```sh
204
- ARS_RUN_SSHD_INTEGRATION=1 go test ./internal/ssh -run TestEphemeralSSHDCollectsAndResumes -v
279
+ ARS_RUN_SSHD_INTEGRATION=1 go test ./internal/ssh -run TestEphemeralSSHDCollectsAndAttaches -count=1 -v
205
280
  ```
206
281
 
207
282
  It generates temporary host/client keys, `authorized_keys`, `known_hosts`, and
208
- configs, and does not modify a system sshd or persistent SSH configuration.
209
- Before release, also use two explicitly configured real hosts to verify one
210
- Claude and one Codex resume, an unreachable peer beside a healthy host, a
211
- healthy empty host, fzf cancellation, and nonce-specific cleanup after an
212
- interrupt.
283
+ configs, logs, provider fixture, and isolated tmux socket. It verifies strict
284
+ host-key checking, remote create/detach with the same provider PID, and
285
+ second-client handoff without modifying system sshd, persistent SSH state, or
286
+ the default tmux server.
287
+
288
+ ### Real two-host acceptance
289
+
290
+ Automated evidence is separate from real-host acceptance. Before release, use
291
+ two genuinely ready hosts with the same ARS build and reciprocal peer
292
+ inventories: host A's `hosts` file contains only host B's configured SSH target,
293
+ and host B's contains only host A's. Neither file lists its own computer.
294
+
295
+ Identity is intentionally observer-relative: a computer is `localhost` to
296
+ itself and its configured SSH target to its peer. Verify:
297
+
298
+ 1. Compare the two computers' visible session sets by `(provider, native ID)`,
299
+ not by host label or canonical session key.
300
+ 2. On host A, start a local Claude session, press `Ctrl+Q`, and confirm its PID
301
+ remains alive after A returns to its TUI.
302
+ 3. From A's TUI, attach to that local Claude runtime so A is actually attached.
303
+ While A is attached, select the same provider and native ID on B under A's
304
+ configured SSH target. Confirm A is forced back to its TUI and B attaches to
305
+ the same runtime and PID.
306
+ 4. While B remains attached, select that same local runtime on A again. Confirm
307
+ B is forced back to its TUI and A attaches to the same PID.
308
+ 5. Repeat steps 2–4 for a B-hosted Claude runtime, with A and B reversed.
309
+ 6. Repeat steps 2–5 for Codex.
310
+ 7. Network loss returns to the TUI without killing the provider, and an
311
+ unreachable peer remains visible beside healthy sessions.
312
+ 8. The user's default tmux server, configuration, keys, and sessions are
313
+ unchanged.
314
+
315
+ If inventory, SSH, DNS, tmux, or provider readiness is missing, record this
316
+ manual gate as incomplete. Disposable or simulated results are never a real
317
+ two-host pass.
213
318
 
214
319
  ## Release
215
320
 
package/package.json CHANGED
@@ -23,5 +23,5 @@
23
23
  "type": "git",
24
24
  "url": "git+https://github.com/baleen37/agent-remote-sessions.git"
25
25
  },
26
- "version": "1.1.0"
26
+ "version": "2.0.0"
27
27
  }
Binary file
Binary file
Binary file