@adrrr/tarmac 0.1.1 → 0.1.2
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/dist/cli.js +1 -1
- package/dist/reap.js +2 -1
- package/dist/wrapper.js +88 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -99,7 +99,7 @@ try {
|
|
|
99
99
|
if (args.command === 'serve') {
|
|
100
100
|
// Unattended for hours, so it opens by saying what it decided and on whose authority.
|
|
101
101
|
process.stdout.write(renderSettings(config, p.config));
|
|
102
|
-
// The one place
|
|
102
|
+
// The one place the CLI deletes anything: temp files its own wrapper left behind when a
|
|
103
103
|
// terminal died mid-write. Best effort, and it says what it did rather than doing it
|
|
104
104
|
// quietly — this is the user's directory.
|
|
105
105
|
const { reaped, failed } = reapOrphanedTemps(snapshotsDir);
|
package/dist/reap.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// Wrapper hygiene — the one
|
|
1
|
+
// Wrapper hygiene — the litter, which is one of the two things tarmac deletes (the other is
|
|
2
|
+
// the wrapper's own amortized prune of dead sessions' snapshots, see `src/wrapper.ts`).
|
|
2
3
|
//
|
|
3
4
|
// The generated wrapper writes `<dir>/.tarmac-<session_id>.<pid>.tmp` and renames it over
|
|
4
5
|
// `<session_id>.json`, so the snapshot a reader sees is never half-written. Kill the
|
package/dist/wrapper.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// P2 — the generated statusline wrapper.
|
|
2
2
|
//
|
|
3
3
|
// Claude Code calls `statusLine.command` on EVERY frame of its TUI, passing a documented
|
|
4
|
-
// JSON payload on stdin. The wrapper does exactly
|
|
4
|
+
// JSON payload on stdin. The wrapper does exactly three things:
|
|
5
5
|
// 1. drop that payload as-is under <snapshotDir>/<session_id>.json (the telemetry);
|
|
6
|
-
// 2.
|
|
6
|
+
// 2. prune the snapshots of sessions that stopped rendering — amortized, because every
|
|
7
|
+
// line here is paid on every frame (see PRUNE_MARKER below);
|
|
8
|
+
// 3. hand stdin to the command that was already configured, so the user's display is
|
|
7
9
|
// untouched.
|
|
8
10
|
//
|
|
9
11
|
// Written as POSIX sh rather than Node on purpose: this sits in the render path of every
|
|
@@ -38,6 +40,20 @@ export const TEMP_PREFIX = '.tarmac-';
|
|
|
38
40
|
* is, in itself, whatever the spelling that reached it.
|
|
39
41
|
*/
|
|
40
42
|
export const WRAPPER_MARKER = 'tarmac statusline wrapper — GENERATED';
|
|
43
|
+
/**
|
|
44
|
+
* Bookkeeping file of the amortized prune below — its mtime is the date of the last sweep.
|
|
45
|
+
* A dotfile, because `readSnapshots` skips those: the wrapper's own paperwork must never be
|
|
46
|
+
* read back as if it were a session's telemetry.
|
|
47
|
+
*/
|
|
48
|
+
export const PRUNE_MARKER = '.tarmac-last-prune';
|
|
49
|
+
/** At most one sweep per hour, whatever the frame rate. R3's number, and its whole point. */
|
|
50
|
+
export const PRUNE_EVERY_MIN = 60;
|
|
51
|
+
/**
|
|
52
|
+
* How long a snapshot nobody rewrites survives. A live session restamps its own file on
|
|
53
|
+
* every frame, so 48h without one is a session that is gone — and far beyond `--stale-after`,
|
|
54
|
+
* so nothing was reading that file as current anyway.
|
|
55
|
+
*/
|
|
56
|
+
export const SNAPSHOT_TTL_MIN = 48 * 60;
|
|
41
57
|
/** Single-quotes a string for POSIX sh. */
|
|
42
58
|
function shQuote(s) {
|
|
43
59
|
return `'${String(s).replace(/'/g, `'\\''`)}'`;
|
|
@@ -90,13 +106,82 @@ fi
|
|
|
90
106
|
# --- drop the snapshot (best effort, atomic: temp file + rename in the same dir) ---
|
|
91
107
|
if [ -n "$sid" ] && mkdir -p "$TARMAC_DIR" 2>/dev/null; then
|
|
92
108
|
tmp="$TARMAC_DIR/${TEMP_PREFIX}$sid.$$.tmp"
|
|
93
|
-
|
|
109
|
+
# \`2>/dev/null\` comes FIRST, and the order is the whole point: redirections are applied
|
|
110
|
+
# left to right, so \`> "$tmp" 2>/dev/null\` opens the temp file while stderr is STILL the
|
|
111
|
+
# user's terminal — the shell prints its own \`cannot create …: Permission denied\` there,
|
|
112
|
+
# and the \`2>\` that was meant to swallow it only takes effect afterwards. On a snapshot
|
|
113
|
+
# directory that has become read-only that is one line of noise per FRAME, on the terminal
|
|
114
|
+
# of a script whose first rule is to be invisible. Exit code and display are untouched
|
|
115
|
+
# (\`printf\` is a regular built-in, so a failed redirection only fails the command), which
|
|
116
|
+
# is exactly why nothing but stderr itself catches this. RULE 1.
|
|
117
|
+
if printf '%s\\n' "$payload" 2>/dev/null > "$tmp"; then
|
|
94
118
|
mv -f "$tmp" "$TARMAC_DIR/$sid.json" 2>/dev/null || rm -f "$tmp" 2>/dev/null
|
|
95
119
|
else
|
|
96
120
|
rm -f "$tmp" 2>/dev/null
|
|
97
121
|
fi
|
|
98
122
|
fi
|
|
99
123
|
|
|
124
|
+
# --- prune the snapshots of sessions that stopped rendering (amortized) ---
|
|
125
|
+
# Nothing else would ever remove them: \`reap.ts\` collects this script's own temp litter and
|
|
126
|
+
# refuses to touch a \`<sid>.json\`. A fleet that recycles its sessions nightly leaves one dead
|
|
127
|
+
# file behind per session per night, forever. A LIVE session restamps its own snapshot on
|
|
128
|
+
# every frame, so mtime is what tells the dead from the living.
|
|
129
|
+
#
|
|
130
|
+
# Amortized, because this is the render path: the frame pays one \`find\` on ONE file — the
|
|
131
|
+
# marker — and the DIRECTORY is walked at most once an hour. The marker is stamped BEFORE the
|
|
132
|
+
# sweep and only if stamping works, so a sweep that cannot finish is not retried on the next
|
|
133
|
+
# frame. (A directory where the stamp itself keeps failing does pay one \`touch\` per frame,
|
|
134
|
+
# forever — a fork, not a directory walk, and it means nothing can be written there anyway.)
|
|
135
|
+
#
|
|
136
|
+
# Two known holes, both of them the safe way round, and both inherited from the fleet script
|
|
137
|
+
# this transposes:
|
|
138
|
+
# • a marker dated in the FUTURE (clock skew, a restored backup, a network mount) is never
|
|
139
|
+
# stale, so pruning stops until the wall clock catches up. Silent, and it fails towards
|
|
140
|
+
# "keep files" rather than "delete files".
|
|
141
|
+
# • \`-mmin\` is the one thing here POSIX does not require. GNU, BSD and toybox find have it,
|
|
142
|
+
# busybox has it unless the build dropped CONFIG_FEATURE_FIND_MMIN; \`-mtime\` could not
|
|
143
|
+
# replace it (it cannot express hours, and it rounds UP on BSD and DOWN on GNU, so one
|
|
144
|
+
# expression would mean 24h on macOS and 48h on Linux). Where it is missing \`find\` fails,
|
|
145
|
+
# the substitution is empty, and the sweep simply never runs.
|
|
146
|
+
# In both cases snapshots pile up exactly as they did before this block existed, and the
|
|
147
|
+
# display is never at risk.
|
|
148
|
+
#
|
|
149
|
+
# One race, priced and accepted: a sweep can stat a cold snapshot in the microseconds before
|
|
150
|
+
# another session's frame renames a fresh one over that same name, and then unlink the fresh
|
|
151
|
+
# inode. The session writes another one on its next frame — seconds later, on a live session
|
|
152
|
+
# — whereas a lock would cost every frame of every session, forever.
|
|
153
|
+
if [ -d "$TARMAC_DIR" ]; then
|
|
154
|
+
marker="$TARMAC_DIR/${PRUNE_MARKER}"
|
|
155
|
+
# Only a plain file, or nothing at all, is a marker — and the braces are load-bearing,
|
|
156
|
+
# since \`&&\` and \`||\` have EQUAL precedence in sh. Both refusals are real:
|
|
157
|
+
# • a symlink — \`touch\` follows it, so a dangling one has the status line CREATE the
|
|
158
|
+
# link's target, a file outside the snapshot directory. RULE 2.
|
|
159
|
+
# • a directory — \`touch\` SUCCEEDS on one, so every frame reads as freshly stamped while
|
|
160
|
+
# \`-mmin\` never gets a regular file to judge: the sweep would run on every frame and
|
|
161
|
+
# the amortization would be gone, silently.
|
|
162
|
+
if [ ! -h "$marker" ] && { [ ! -e "$marker" ] || { [ -f "$marker" ] && [ -n "$(find "$marker" -mmin +${PRUNE_EVERY_MIN} 2>/dev/null)" ]; }; }; then
|
|
163
|
+
# \`touch\`, and NOT \`: > "$marker"\`: POSIX says a redirection error on a special
|
|
164
|
+
# built-in — and \`:\` is one — shall abort a non-interactive shell. On a snapshot
|
|
165
|
+
# directory that has become read-only that spelling exits dash 2 and ksh 1 with the
|
|
166
|
+
# status line never printed, while bash carries on: RULE 1, broken on Debian and Ubuntu
|
|
167
|
+
# only. An ordinary utility just reports a non-zero status, which is what this \`if\` is
|
|
168
|
+
# asking about.
|
|
169
|
+
if touch "$marker" 2>/dev/null; then
|
|
170
|
+
# \`<dir>/.\` with \`! -name . -prune\` is how POSIX spells \`-maxdepth 1\`, and \`-type f\`
|
|
171
|
+
# keeps a directory or a symlink wearing a session id's name out of it.
|
|
172
|
+
#
|
|
173
|
+
# The glob is the sid SHAPE, not \`*.json\`, and that is the same rule \`reap.ts\` states
|
|
174
|
+
# for the temp files: only what we wrote. A bare \`*.json\` would take \`settings.json\`
|
|
175
|
+
# or \`fleet.json\` with it — data this script never wrote, deleted from inside a status
|
|
176
|
+
# line. 8-4-4-4-12 is the UUID Claude Code emits (every fixture here, and the live
|
|
177
|
+
# fleet directory). The wrapper accepts wider ids than that because it refuses to guess
|
|
178
|
+
# what an id may look like; a sid outside this shape is therefore written and never
|
|
179
|
+
# pruned, which is the direction this trade has to fail in.
|
|
180
|
+
find "$TARMAC_DIR"/. ! -name . -prune -name '????????-????-????-????-????????????.json' -type f -mmin +${SNAPSHOT_TTL_MIN} -exec rm -f {} + 2>/dev/null
|
|
181
|
+
fi
|
|
182
|
+
fi
|
|
183
|
+
fi
|
|
184
|
+
|
|
100
185
|
# --- hand over to the status line that was already there ---
|
|
101
186
|
if [ -n "$TARMAC_CHAIN" ]; then
|
|
102
187
|
printf '%s\\n' "$payload" | sh -c "$TARMAC_CHAIN"
|