@cydm/pie 2.6.8 → 2.6.9
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.
|
@@ -35,25 +35,38 @@ tui-use daemon stop
|
|
|
35
35
|
|
|
36
36
|
- Use `wait --text <marker>` or `wait --debounce <ms>` rather than guessing with sleeps.
|
|
37
37
|
- Always pass `--session <id>` on session operations. `use <id>` is a legacy
|
|
38
|
-
single-session convenience
|
|
39
|
-
|
|
38
|
+
single-session convenience that only matters while exactly one session
|
|
39
|
+
exists: with multiple sessions its `[current]` mark changes nothing, and
|
|
40
|
+
operations without an explicit ID fail instead of risking another task's session.
|
|
40
41
|
- `--text` and `find` use literal text by default. Add `--regex` explicitly for
|
|
41
42
|
regular expressions; a bounded worker prevents regex from hanging the daemon.
|
|
43
|
+
`find` searches only the current viewport rows, never the scrollback buffer:
|
|
44
|
+
`scrollup`/`scrolldown` first, then `find` again.
|
|
42
45
|
- `snapshot` reports the rendered screen, cursor, title, fullscreen state, and selected spans.
|
|
43
46
|
- `list`, `use <id>`, and `rename <label>` support multiple sessions.
|
|
44
47
|
- `start --cwd <dir> --cols <n> --rows <n>` controls the working directory and terminal size.
|
|
48
|
+
`start` prints the bare session ID on stdout (plain text for shell capture, no
|
|
49
|
+
`--format json` option).
|
|
45
50
|
- `resize <cols> <rows>` updates a live session. `keys` lists the supported
|
|
46
51
|
modifiers and function keys, including Shift-Tab and F11/F12.
|
|
47
52
|
- Snapshot rows are preserved. Cursor, highlights and `find` use zero-based
|
|
48
|
-
viewport rows and terminal-cell columns;
|
|
49
|
-
|
|
53
|
+
viewport rows and terminal-cell columns; spans are measured in terminal cells,
|
|
54
|
+
so a wide character (CJK, emoji) occupies two cells and `col_end` is the last
|
|
55
|
+
occupied cell, not a character count. The cursor is offscreen exactly when its
|
|
56
|
+
`y` is outside `0 <= y < rows` (scrolling reports scrollback-absolute rows, so
|
|
57
|
+
`y >= rows` means offscreen); an offscreen cursor is not a screen text index.
|
|
50
58
|
- Send an explicit `press enter` after typing when reliable submission matters.
|
|
59
|
+
`press` accepts only the special key names listed by `keys`; single printable
|
|
60
|
+
characters must be sent with `type` instead.
|
|
51
61
|
- `wait --text` exits non-zero if the requested pattern is absent at the deadline.
|
|
52
62
|
Read the returned screen and fix the input instead of assuming the wait succeeded.
|
|
53
63
|
Do not rerun the identical failing wait; poll with `--poll` (below) or change the input.
|
|
54
64
|
- Use `wait --exit` to confirm process termination. Seeing the last output or
|
|
55
65
|
leaving fullscreen does not by itself mean the process has exited.
|
|
56
66
|
- Kill only sessions you created. Do not restart or stop a daemon that owns another task's live sessions.
|
|
67
|
+
A killed session is retained like a naturally exited one: `info`, `snapshot`
|
|
68
|
+
and `wait --exit` (reporting the observed `exit_code`) keep working for it
|
|
69
|
+
until exited-session retention prunes it.
|
|
57
70
|
|
|
58
71
|
## Bounded Polling For Long Tasks
|
|
59
72
|
|
|
@@ -117,9 +130,11 @@ node <baseDir>/tui-use-cli.js --session <id> press enter
|
|
|
117
130
|
node <baseDir>/tui-use-cli.js --session <id> wait 3000 --text 'Error: EXPECTED_ERROR'
|
|
118
131
|
```
|
|
119
132
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
Text passed as `type`/`paste` arguments expands the escape sequences `\n`, `\r`
|
|
134
|
+
and `\t` (`\n` acts as Enter for most programs). `--stdin` reads UTF-8
|
|
135
|
+
literally, without expanding `\n`, `\r` or `\t`, and removes one trailing
|
|
136
|
+
pipeline line terminator. `paste --stdin` supports multiline input and submits
|
|
137
|
+
each line.
|
|
123
138
|
|
|
124
139
|
PowerShell pipelines containing Unicode must use UTF-8:
|
|
125
140
|
|
|
@@ -140,7 +155,8 @@ tui-use. `PIE_TUI_USE_STATE_DIR` can isolate a test or task.
|
|
|
140
155
|
Windows uses a named pipe; Unix hosts use a local socket. Daemon logs are
|
|
141
156
|
written to the state directory instead of inherited command output.
|
|
142
157
|
Use a dedicated state directory per task when possible, even with explicit IDs.
|
|
143
|
-
At most 32 sessions run concurrently; up to 16 exited sessions
|
|
158
|
+
At most 32 sessions run concurrently; up to 16 exited sessions (naturally exited
|
|
159
|
+
or killed) are retained, each for at most five minutes.
|
|
144
160
|
An empty/exited daemon shuts down after five minutes without session activity.
|
|
145
161
|
|
|
146
162
|
If startup fails, inspect `daemon status` and the state directory's
|
|
@@ -10320,6 +10320,48 @@ var SUPPORTED_KEYS = Object.keys(KEY_MAP);
|
|
|
10320
10320
|
var import_node_fs2 = require("node:fs");
|
|
10321
10321
|
var import_node_path3 = __toESM(require("node:path"));
|
|
10322
10322
|
var program2 = new Command();
|
|
10323
|
+
function isWideCodePoint(cp) {
|
|
10324
|
+
return cp >= 4352 && (cp <= 4447 || // Hangul Jamo
|
|
10325
|
+
cp === 9001 || cp === 9002 || // angle brackets
|
|
10326
|
+
cp >= 11904 && cp <= 12350 || // CJK radicals … CJK symbols
|
|
10327
|
+
cp >= 12353 && cp <= 13311 || // Hiragana … CJK compatibility
|
|
10328
|
+
cp >= 13312 && cp <= 19903 || // CJK ext A
|
|
10329
|
+
cp >= 19968 && cp <= 40959 || // CJK Unified Ideographs
|
|
10330
|
+
cp >= 40960 && cp <= 42191 || // Yi
|
|
10331
|
+
cp >= 44032 && cp <= 55203 || // Hangul syllables
|
|
10332
|
+
cp >= 63744 && cp <= 64255 || // CJK compatibility ideographs
|
|
10333
|
+
cp >= 65072 && cp <= 65135 || // CJK compatibility forms
|
|
10334
|
+
cp >= 65280 && cp <= 65376 || // Fullwidth forms
|
|
10335
|
+
cp >= 65504 && cp <= 65510 || // Fullwidth signs
|
|
10336
|
+
cp >= 127744 && cp <= 128591 || // emoji
|
|
10337
|
+
cp >= 129280 && cp <= 129535 || // supplemental emoji
|
|
10338
|
+
cp >= 131072 && cp <= 262141);
|
|
10339
|
+
}
|
|
10340
|
+
function charCellWidth(char) {
|
|
10341
|
+
return isWideCodePoint(char.codePointAt(0) ?? 0) ? 2 : 1;
|
|
10342
|
+
}
|
|
10343
|
+
function stringCellWidth(value) {
|
|
10344
|
+
let width = 0;
|
|
10345
|
+
for (const char of value) width += charCellWidth(char);
|
|
10346
|
+
return width;
|
|
10347
|
+
}
|
|
10348
|
+
function truncateToCells(value, maxCells) {
|
|
10349
|
+
if (stringCellWidth(value) <= maxCells) return value;
|
|
10350
|
+
const budget = Math.max(1, maxCells - 1);
|
|
10351
|
+
let used = 0;
|
|
10352
|
+
let out = "";
|
|
10353
|
+
for (const char of value) {
|
|
10354
|
+
const w = charCellWidth(char);
|
|
10355
|
+
if (used + w > budget) break;
|
|
10356
|
+
out += char;
|
|
10357
|
+
used += w;
|
|
10358
|
+
}
|
|
10359
|
+
return out + "\u2026";
|
|
10360
|
+
}
|
|
10361
|
+
function padEndCells(value, width) {
|
|
10362
|
+
const missing = width - stringCellWidth(value);
|
|
10363
|
+
return missing > 0 ? value + " ".repeat(missing) : value;
|
|
10364
|
+
}
|
|
10323
10365
|
function callerEnv() {
|
|
10324
10366
|
const env = {};
|
|
10325
10367
|
for (const [key, value] of Object.entries(process.env)) {
|
|
@@ -10356,12 +10398,12 @@ program2.command("start [args...]").description("Start an interactive program in
|
|
|
10356
10398
|
}
|
|
10357
10399
|
});
|
|
10358
10400
|
});
|
|
10359
|
-
program2.command("snapshot").description("Return the
|
|
10401
|
+
program2.command("snapshot").description("Return the rendered screen snapshot (pass --session <id>; in a single-session daemon, tui-use use <id> first also works)").option("--format <fmt>", "Output format: pretty, json (default: pretty)", "pretty").option("--color", "Preserve ANSI color/style escape sequences in output lines").action(async (opts) => {
|
|
10360
10402
|
const res = await sendRequest({ type: "snapshot", color: opts.color ?? false, session_id: program2.opts().session });
|
|
10361
10403
|
handleResponse(res, (r) => printScreen(r, opts.format));
|
|
10362
10404
|
});
|
|
10363
10405
|
program2.command("wait [duration]").description(
|
|
10364
|
-
'Wait for screen to change or timeout.\n wait \u2192 wait for screen to change (default 3000ms)\n wait 5000 \u2192 wait for 5000ms\n wait --text "pattern" \u2192 wait until screen contains text\n wait --debounce 300 \u2192
|
|
10406
|
+
'Wait for screen to change or timeout.\n wait \u2192 wait for screen to change and become stable (default 3000ms)\n wait 5000 \u2192 wait for 5000ms\n wait --text "pattern" \u2192 wait until screen contains text\n wait --debounce 300 \u2192 return once the screen has been stable for 300ms, even if it never changed (default: 100ms)\n wait --text "pattern" --poll \u2192 exit 0 with wait_state "pending" if the deadline passes while the session is still running'
|
|
10365
10407
|
).option("--text <text>", "Wait until screen contains literal text").option("--regex", "Interpret --text as a regex in a bounded worker").option("--exit", "Wait for process exit instead of just a screen change").option("--poll", 'Bounded polling: a deadline on a still-running session reports wait_state "pending" and exits 0; an exited session that never matched --text, and errors, exit 1. With --exit, "satisfied" means termination was observed, not success: check exit_code').option("--debounce <ms>", "Idle time after last change before resolving, in ms (default: 100)", "100").option("--format <fmt>", "Output format: pretty, json (default: pretty)", "pretty").option("--color", "Preserve ANSI color/style escape sequences in output lines").action(async (duration, opts) => {
|
|
10366
10408
|
let timeoutMs = 3e3;
|
|
10367
10409
|
if (duration) {
|
|
@@ -10396,7 +10438,7 @@ program2.command("wait [duration]").description(
|
|
|
10396
10438
|
});
|
|
10397
10439
|
});
|
|
10398
10440
|
program2.command("type [input...]").description(
|
|
10399
|
-
"Type text to
|
|
10441
|
+
"Type text to a session.\n Use \\n for Enter, \\t for Tab\n Pass --session <id> to target a session (single-session legacy: tui-use use <id> first)"
|
|
10400
10442
|
).option("--stdin", "Read literal UTF-8 input from stdin (safe for quotes and Unicode in PowerShell)").action(async (inputParts, opts) => {
|
|
10401
10443
|
const input = inputText(inputParts, opts.stdin);
|
|
10402
10444
|
const res = await sendRequest({ type: "type", input, literal: !!opts.stdin, session_id: program2.opts().session });
|
|
@@ -10416,7 +10458,7 @@ program2.command("press <key>").description(
|
|
|
10416
10458
|
}
|
|
10417
10459
|
});
|
|
10418
10460
|
});
|
|
10419
|
-
program2.command("kill").description("Terminate
|
|
10461
|
+
program2.command("kill").description("Terminate a session (pass --session <id>; single-session legacy: tui-use use <id> first). The killed session stays inspectable like an exited one").action(async () => {
|
|
10420
10462
|
const res = await sendRequest({ type: "kill", session_id: program2.opts().session });
|
|
10421
10463
|
handleResponse(res, (r) => {
|
|
10422
10464
|
if (r.type === "kill") {
|
|
@@ -10531,17 +10573,21 @@ program2.command("list").description("List all active sessions").option("--forma
|
|
|
10531
10573
|
console.log("No active sessions");
|
|
10532
10574
|
return;
|
|
10533
10575
|
}
|
|
10576
|
+
const LABEL_MAX = 24;
|
|
10577
|
+
const COMMAND_MAX = 40;
|
|
10578
|
+
const labels = r.sessions.map((s) => truncateToCells(s.label, LABEL_MAX));
|
|
10579
|
+
const commands = r.sessions.map((s) => truncateToCells(s.command, COMMAND_MAX));
|
|
10534
10580
|
const idWidth = Math.max(10, ...r.sessions.map((s) => s.session_id.length));
|
|
10535
|
-
const labelWidth = Math.max(5, ...
|
|
10536
|
-
const cmdWidth = Math.max(7, ...
|
|
10537
|
-
const header = `${"SESSION ID".padEnd(idWidth)} ${"LABEL"
|
|
10581
|
+
const labelWidth = Math.min(LABEL_MAX, Math.max(5, ...labels.map((label) => stringCellWidth(label))));
|
|
10582
|
+
const cmdWidth = Math.min(COMMAND_MAX, Math.max(7, ...commands.map((command) => stringCellWidth(command))));
|
|
10583
|
+
const header = `${"SESSION ID".padEnd(idWidth)} ${padEndCells("LABEL", labelWidth)} ${padEndCells("COMMAND", cmdWidth)} STATUS`;
|
|
10538
10584
|
console.log(header);
|
|
10539
10585
|
console.log("-".repeat(header.length));
|
|
10540
|
-
for (const s of r.sessions) {
|
|
10586
|
+
for (const [index, s] of r.sessions.entries()) {
|
|
10541
10587
|
const currentMark = s.session_id === r.current ? " [current]" : "";
|
|
10542
10588
|
const status = s.status + currentMark;
|
|
10543
10589
|
console.log(
|
|
10544
|
-
`${s.session_id.padEnd(idWidth)} ${
|
|
10590
|
+
`${s.session_id.padEnd(idWidth)} ${padEndCells(labels[index], labelWidth)} ${padEndCells(commands[index], cmdWidth)} ${status}`
|
|
10545
10591
|
);
|
|
10546
10592
|
}
|
|
10547
10593
|
}
|
|
@@ -7327,8 +7327,9 @@ var Session = class {
|
|
|
7327
7327
|
}
|
|
7328
7328
|
if (mapped === void 0) {
|
|
7329
7329
|
const supported = Object.keys(KEY_MAP).join(", ");
|
|
7330
|
+
const hint = key.length === 1 && key >= " " && key !== "\x7F" ? " Printable characters should be sent with `type`, not `press`." : "";
|
|
7330
7331
|
throw new Error(
|
|
7331
|
-
`Unknown key: "${key}"
|
|
7332
|
+
`Unknown key: "${key}".${hint} Run \`tui-use keys\` to see all supported key names.
|
|
7332
7333
|
Supported keys: ${supported}`
|
|
7333
7334
|
);
|
|
7334
7335
|
}
|
|
@@ -7384,7 +7385,6 @@ Supported keys: ${supported}`
|
|
|
7384
7385
|
async wait(timeoutMs = 3e3, text, debounceMs = 100, options) {
|
|
7385
7386
|
let satisfied = false;
|
|
7386
7387
|
let timedOut = false;
|
|
7387
|
-
const before = this.lastSnapshotState;
|
|
7388
7388
|
const controller = new AbortController();
|
|
7389
7389
|
const matches = async (screen) => text === void 0 || (await matchLines([screen], text, options?.regex, controller.signal))[0] !== null;
|
|
7390
7390
|
if (this._status === "exited" && this.pendingWrites === 0) {
|
|
@@ -7435,7 +7435,7 @@ Supported keys: ${supported}`
|
|
|
7435
7435
|
done();
|
|
7436
7436
|
return;
|
|
7437
7437
|
}
|
|
7438
|
-
} else if (!options?.exit
|
|
7438
|
+
} else if (!options?.exit) {
|
|
7439
7439
|
if (idleTimer2) clearTimeout(idleTimer2);
|
|
7440
7440
|
idleTimer2 = setTimeout(() => {
|
|
7441
7441
|
satisfied = true;
|
|
@@ -7787,8 +7787,7 @@ async function handleRequest(req, signal) {
|
|
|
7787
7787
|
return { type: "paste", ok: true };
|
|
7788
7788
|
case "kill":
|
|
7789
7789
|
await session.kill();
|
|
7790
|
-
|
|
7791
|
-
exitedAt.delete(id);
|
|
7790
|
+
exitedAt.set(id, Date.now());
|
|
7792
7791
|
if (currentSession === id) currentSession = null;
|
|
7793
7792
|
return { type: "kill", ok: true };
|
|
7794
7793
|
case "find":
|