agent-yes 1.106.0 → 1.108.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/dist/SUPPORTED_CLIS-B_gPkhav.js +8 -0
- package/dist/{SUPPORTED_CLIS-O3atAbo5.js → SUPPORTED_CLIS-BzuJvKuH.js} +2 -2
- package/dist/cli.js +3 -3
- package/dist/index.js +2 -2
- package/dist/{serve-BHk9nQm-.js → serve-D4puI0b2.js} +5 -5
- package/dist/subcommands-4tanf24s.js +6 -0
- package/dist/{subcommands-BImrR-R6.js → subcommands-BLPtg1xN.js} +171 -28
- package/dist/{ts-CFnUatY1.js → ts-D_sIq4Yv.js} +2 -2
- package/dist/{versionChecker-CRaHzP0C.js → versionChecker-osmGP6ly.js} +2 -2
- package/lab/ui/index.html +15 -1
- package/package.json +1 -1
- package/ts/subcommands.spec.ts +39 -0
- package/ts/subcommands.ts +222 -24
- package/dist/SUPPORTED_CLIS-BkWXFAho.js +0 -8
- package/dist/subcommands-DO3qf62r.js +0 -6
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-D_sIq4Yv.js";
|
|
2
|
+
import "./logger-B9h0djqx.js";
|
|
3
|
+
import "./versionChecker-osmGP6ly.js";
|
|
4
|
+
import "./pidStore-DBjlqzo8.js";
|
|
5
|
+
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BzuJvKuH.js";
|
|
7
|
+
|
|
8
|
+
export { SUPPORTED_CLIS };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-D_sIq4Yv.js";
|
|
2
2
|
|
|
3
3
|
//#region ts/SUPPORTED_CLIS.ts
|
|
4
4
|
const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
5
5
|
|
|
6
6
|
//#endregion
|
|
7
7
|
export { SUPPORTED_CLIS as t };
|
|
8
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
8
|
+
//# sourceMappingURL=SUPPORTED_CLIS-BzuJvKuH.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { n as logger } from "./logger-B9h0djqx.js";
|
|
3
|
-
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-
|
|
3
|
+
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-osmGP6ly.js";
|
|
4
4
|
import { argv } from "process";
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
6
|
import ms from "ms";
|
|
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
482
482
|
{
|
|
483
483
|
const rawArg = process.argv[2];
|
|
484
484
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
485
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
485
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-4tanf24s.js");
|
|
486
486
|
if (isHelpFlag && process.argv.length === 3) {
|
|
487
487
|
cmdHelp();
|
|
488
488
|
process.exit(0);
|
|
@@ -515,7 +515,7 @@ if (config.useRust) {
|
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
if (rustBinary) {
|
|
518
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
518
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-B_gPkhav.js");
|
|
519
519
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
520
520
|
if (config.verbose) {
|
|
521
521
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-D_sIq4Yv.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-osmGP6ly.js";
|
|
4
4
|
import "./pidStore-DBjlqzo8.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
6
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-D_sIq4Yv.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-osmGP6ly.js";
|
|
4
4
|
import "./pidStore-DBjlqzo8.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BzuJvKuH.js";
|
|
7
7
|
import "./remotes-C3xPRtfg.js";
|
|
8
|
-
import { c as
|
|
8
|
+
import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-BLPtg1xN.js";
|
|
9
9
|
import yargs from "yargs";
|
|
10
10
|
import { mkdir, open, readFile, writeFile } from "fs/promises";
|
|
11
11
|
import { homedir } from "os";
|
|
@@ -552,4 +552,4 @@ Options:
|
|
|
552
552
|
|
|
553
553
|
//#endregion
|
|
554
554
|
export { cmdServe };
|
|
555
|
-
//# sourceMappingURL=serve-
|
|
555
|
+
//# sourceMappingURL=serve-D4puI0b2.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "./logger-B9h0djqx.js";
|
|
2
|
+
import "./globalPidIndex-yVd3mbsV.js";
|
|
3
|
+
import "./remotes-C3xPRtfg.js";
|
|
4
|
+
import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-BLPtg1xN.js";
|
|
5
|
+
|
|
6
|
+
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -163,7 +163,7 @@ async function runSubcommand(argv) {
|
|
|
163
163
|
case "restart": return await cmdRestart(rest);
|
|
164
164
|
case "note": return await cmdNote(rest);
|
|
165
165
|
case "serve": {
|
|
166
|
-
const { cmdServe } = await import("./serve-
|
|
166
|
+
const { cmdServe } = await import("./serve-D4puI0b2.js");
|
|
167
167
|
return cmdServe(rest);
|
|
168
168
|
}
|
|
169
169
|
case "setup": {
|
|
@@ -361,7 +361,7 @@ async function runRemoteLs(remote, opts) {
|
|
|
361
361
|
}
|
|
362
362
|
return 0;
|
|
363
363
|
}
|
|
364
|
-
async function runRemoteRead(remote, mode, follow, n, reconnectTimeoutMs = 12e4) {
|
|
364
|
+
async function runRemoteRead(remote, mode, follow, n, reconnectTimeoutMs = 12e4, _plain = false) {
|
|
365
365
|
const keyword = remote.keyword ?? "";
|
|
366
366
|
if (!keyword) {
|
|
367
367
|
process.stderr.write("remote tail/cat/head requires a keyword (e.g. token@host:port:keyword)\n");
|
|
@@ -369,7 +369,8 @@ async function runRemoteRead(remote, mode, follow, n, reconnectTimeoutMs = 12e4)
|
|
|
369
369
|
}
|
|
370
370
|
if (mode === "tail" && follow) {
|
|
371
371
|
const ac = new AbortController();
|
|
372
|
-
|
|
372
|
+
const disposeSignals = installStreamSignals(() => ac.abort());
|
|
373
|
+
ac.signal.addEventListener("abort", disposeSignals, { once: true });
|
|
373
374
|
const deadline = Date.now() + reconnectTimeoutMs;
|
|
374
375
|
let delay = 1e3;
|
|
375
376
|
let attempt = 0;
|
|
@@ -718,6 +719,10 @@ async function cmdRead(rest, { mode }) {
|
|
|
718
719
|
}).option("n", {
|
|
719
720
|
type: "number",
|
|
720
721
|
description: "Number of lines (default: 96 for tail/head)"
|
|
722
|
+
}).option("plain", {
|
|
723
|
+
type: "boolean",
|
|
724
|
+
default: false,
|
|
725
|
+
description: "Line-buffered plain text for pipes/scripts (no ANSI redraws or spinner). Auto-enabled when stdout is not a TTY."
|
|
721
726
|
}).option("all", {
|
|
722
727
|
type: "boolean",
|
|
723
728
|
default: false,
|
|
@@ -734,6 +739,8 @@ async function cmdRead(rest, { mode }) {
|
|
|
734
739
|
default: 120,
|
|
735
740
|
description: "Seconds before giving up reconnecting remote SSE (default: 120)"
|
|
736
741
|
}).help(false).version(false).exitProcess(false).parseAsync();
|
|
742
|
+
ensureEpipeExit();
|
|
743
|
+
const plain = Boolean(argv.plain) || !process.stdout.isTTY;
|
|
737
744
|
const opts = {
|
|
738
745
|
all: argv.all,
|
|
739
746
|
active: false,
|
|
@@ -747,7 +754,7 @@ async function cmdRead(rest, { mode }) {
|
|
|
747
754
|
const nFlag2 = argv.n;
|
|
748
755
|
const n2 = nFlag2 !== void 0 && Number.isFinite(nFlag2) && nFlag2 > 0 ? Math.floor(nFlag2) : mode === "cat" ? 0 : 96;
|
|
749
756
|
const reconnectTimeoutMs = (argv["reconnect-timeout"] ?? 120) * 1e3;
|
|
750
|
-
if (remote) return runRemoteRead(remote, mode, argv.follow, n2, reconnectTimeoutMs);
|
|
757
|
+
if (remote) return runRemoteRead(remote, mode, argv.follow, n2, reconnectTimeoutMs, plain);
|
|
751
758
|
}
|
|
752
759
|
const follow = argv.follow;
|
|
753
760
|
const nFlag = argv.n;
|
|
@@ -772,34 +779,170 @@ async function cmdRead(rest, { mode }) {
|
|
|
772
779
|
process.stderr.write(header + "\n");
|
|
773
780
|
process.stdout.write(rendered);
|
|
774
781
|
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
775
|
-
if (follow)
|
|
776
|
-
process.stderr.write(`following... (Ctrl-C to stop)\n`);
|
|
777
|
-
let offset = buf.length;
|
|
778
|
-
const { watch } = await import("fs");
|
|
779
|
-
const ansiRe = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
|
|
780
|
-
const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
781
|
-
await new Promise((resolve) => {
|
|
782
|
-
const watcher = watch(logPath, async () => {
|
|
783
|
-
const full = await readFile(logPath);
|
|
784
|
-
if (full.length <= offset) return;
|
|
785
|
-
const chunk = full.slice(offset);
|
|
786
|
-
offset = full.length;
|
|
787
|
-
const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
|
|
788
|
-
if (text.trim()) process.stdout.write(text.trimStart());
|
|
789
|
-
});
|
|
790
|
-
process.on("SIGINT", () => {
|
|
791
|
-
watcher.close();
|
|
792
|
-
resolve();
|
|
793
|
-
});
|
|
794
|
-
});
|
|
795
|
-
return 0;
|
|
796
|
-
}
|
|
782
|
+
if (follow) return plain ? followPlainLocal(logPath, buf) : followRawLocal(logPath, buf);
|
|
797
783
|
process.stderr.write(`
|
|
798
784
|
ay ls # list all agents
|
|
799
785
|
ay tail -f ${record.pid} # follow live output\n ay send ${record.pid} "next: ..." # send a prompt\n ay send ${record.pid} "" --code=ctrl-c # interrupt\n`);
|
|
800
786
|
return 0;
|
|
801
787
|
}
|
|
802
788
|
/**
|
|
789
|
+
* Exit cleanly when stdout's downstream closes (EPIPE). Node ignores SIGPIPE and
|
|
790
|
+
* surfaces a broken pipe as a stream 'error'; with no listener it throws, and in
|
|
791
|
+
* follow mode the watch loop would otherwise hang. Idempotent — one listener for
|
|
792
|
+
* the life of the process, tagged on stdout so repeated calls (and module
|
|
793
|
+
* reloads in tests) don't pile up listeners.
|
|
794
|
+
*/
|
|
795
|
+
function ensureEpipeExit() {
|
|
796
|
+
const TAG = "__ayEpipeExit";
|
|
797
|
+
if (process.stdout[TAG]) return;
|
|
798
|
+
process.stdout[TAG] = true;
|
|
799
|
+
process.stdout.on("error", (e) => {
|
|
800
|
+
if (e?.code === "EPIPE") process.exit(0);
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Install signal handlers for a streaming follower so it terminates promptly
|
|
805
|
+
* under automation, not just on an interactive Ctrl-C. SIGINT/SIGTERM/SIGHUP all
|
|
806
|
+
* run `stop` (so `timeout … ay tail -f` and `kill` both work); a closed stdout
|
|
807
|
+
* (EPIPE) exits cleanly via ensureEpipeExit. Returns a disposer that removes the
|
|
808
|
+
* signal listeners.
|
|
809
|
+
*/
|
|
810
|
+
function installStreamSignals(stop) {
|
|
811
|
+
ensureEpipeExit();
|
|
812
|
+
const onSig = () => stop();
|
|
813
|
+
process.on("SIGINT", onSig);
|
|
814
|
+
process.on("SIGTERM", onSig);
|
|
815
|
+
process.on("SIGHUP", onSig);
|
|
816
|
+
return () => {
|
|
817
|
+
process.off("SIGINT", onSig);
|
|
818
|
+
process.off("SIGTERM", onSig);
|
|
819
|
+
process.off("SIGHUP", onSig);
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Coalescing file watcher: re-reads `logPath` on every change, hands each newly
|
|
824
|
+
* appended byte range to `onChunk`, and never overlaps reads (a change that
|
|
825
|
+
* arrives mid-read is serviced once the current read finishes). `startOffset`
|
|
826
|
+
* is where the already-emitted prefix ends. Resolves when `stop` is signalled.
|
|
827
|
+
*/
|
|
828
|
+
async function watchAppend(logPath, startOffset, onChunk, onStop) {
|
|
829
|
+
const { watch } = await import("fs");
|
|
830
|
+
let offset = startOffset;
|
|
831
|
+
let reading = false;
|
|
832
|
+
let pending = false;
|
|
833
|
+
await new Promise((resolve) => {
|
|
834
|
+
let done = false;
|
|
835
|
+
const finish = () => {
|
|
836
|
+
if (done) return;
|
|
837
|
+
done = true;
|
|
838
|
+
try {
|
|
839
|
+
watcher.close();
|
|
840
|
+
} catch {}
|
|
841
|
+
dispose();
|
|
842
|
+
onStop();
|
|
843
|
+
resolve();
|
|
844
|
+
};
|
|
845
|
+
const dispose = installStreamSignals(finish);
|
|
846
|
+
const pump = async () => {
|
|
847
|
+
if (reading) {
|
|
848
|
+
pending = true;
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
reading = true;
|
|
852
|
+
do {
|
|
853
|
+
pending = false;
|
|
854
|
+
let full;
|
|
855
|
+
try {
|
|
856
|
+
full = await readFile(logPath);
|
|
857
|
+
} catch {
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
if (full.length > offset) {
|
|
861
|
+
const chunk = full.slice(offset);
|
|
862
|
+
offset = full.length;
|
|
863
|
+
await onChunk(chunk);
|
|
864
|
+
}
|
|
865
|
+
} while (pending && !done);
|
|
866
|
+
reading = false;
|
|
867
|
+
};
|
|
868
|
+
const watcher = watch(logPath, () => void pump());
|
|
869
|
+
pump();
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Default (interactive) follow: append each new byte range with ANSI/control
|
|
874
|
+
* sequences stripped. Mirrors the historical behaviour, plus prompt signal /
|
|
875
|
+
* pipe-close handling.
|
|
876
|
+
*/
|
|
877
|
+
async function followRawLocal(logPath, buf) {
|
|
878
|
+
process.stderr.write(`following... (Ctrl-C to stop)\n`);
|
|
879
|
+
const ansiRe = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
|
|
880
|
+
const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
881
|
+
await watchAppend(logPath, buf.length, (chunk) => {
|
|
882
|
+
const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
|
|
883
|
+
if (text.trim()) process.stdout.write(text.trimStart());
|
|
884
|
+
}, () => {});
|
|
885
|
+
return 0;
|
|
886
|
+
}
|
|
887
|
+
/** Absolute index (scrollback + viewport row) of the row the cursor sits on. */
|
|
888
|
+
function cursorAbs(term) {
|
|
889
|
+
return term.buffer.active.baseY + term.buffer.active.cursorY;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* The lines in [fromAbs, cursorRow) — rows the cursor has moved PAST, i.e.
|
|
893
|
+
* finalized text. A row still being rewritten in place (spinner, progress bar,
|
|
894
|
+
* TUI repaint) is the cursor's own row and is excluded until the cursor leaves
|
|
895
|
+
* it, which is what keeps redraw churn out of the plain stream.
|
|
896
|
+
*/
|
|
897
|
+
function finalizedLines(term, fromAbs) {
|
|
898
|
+
const a = term.buffer.active;
|
|
899
|
+
const cur = a.baseY + a.cursorY;
|
|
900
|
+
const out = [];
|
|
901
|
+
for (let i = Math.max(0, fromAbs); i < cur; i++) {
|
|
902
|
+
const l = a.getLine(i);
|
|
903
|
+
out.push(l ? l.translateToString(false).trimEnd() : "");
|
|
904
|
+
}
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Plain (pipe/script) follow: feed the live PTY stream through @xterm/headless
|
|
909
|
+
* and emit each line only once it's finalized — i.e. once the cursor has moved
|
|
910
|
+
* off it. In-place redraws (spinners, progress bars that rewrite the current
|
|
911
|
+
* line, full-screen TUI repaints) churn the cursor's row and never emit until
|
|
912
|
+
* settled, so the output is clean, newline-terminated, line-buffered text a
|
|
913
|
+
* script can read. On stop, flush the line the cursor is still sitting on.
|
|
914
|
+
*/
|
|
915
|
+
async function followPlainLocal(logPath, buf) {
|
|
916
|
+
process.stderr.write(`following... (plain; Ctrl-C / SIGTERM to stop)\n`);
|
|
917
|
+
const { Terminal } = await import("@xterm/headless");
|
|
918
|
+
const term = new Terminal({
|
|
919
|
+
cols: 200,
|
|
920
|
+
rows: 50,
|
|
921
|
+
scrollback: 5e4,
|
|
922
|
+
allowProposedApi: true
|
|
923
|
+
});
|
|
924
|
+
const feed = (b) => new Promise((r) => term.write(b, () => r()));
|
|
925
|
+
const lineAt = (i) => {
|
|
926
|
+
const l = term.buffer.active.getLine(i);
|
|
927
|
+
return l ? l.translateToString(false).trimEnd() : "";
|
|
928
|
+
};
|
|
929
|
+
await feed(buf);
|
|
930
|
+
let emitted = cursorAbs(term);
|
|
931
|
+
const flushCommitted = () => {
|
|
932
|
+
for (const line of finalizedLines(term, emitted)) process.stdout.write(line + "\n");
|
|
933
|
+
emitted = cursorAbs(term);
|
|
934
|
+
};
|
|
935
|
+
await watchAppend(logPath, buf.length, async (chunk) => {
|
|
936
|
+
await feed(chunk);
|
|
937
|
+
flushCommitted();
|
|
938
|
+
}, () => {
|
|
939
|
+
flushCommitted();
|
|
940
|
+
const last = lineAt(cursorAbs(term));
|
|
941
|
+
if (last) process.stdout.write(last + "\n");
|
|
942
|
+
});
|
|
943
|
+
return 0;
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
803
946
|
* Feed the raw PTY bytes through @xterm/headless and emit plain text.
|
|
804
947
|
* Same approach as koho's renderTerminalBuffer + agent-yes's XtermProxy.
|
|
805
948
|
*/
|
|
@@ -1451,5 +1594,5 @@ async function cmdStatus(rest) {
|
|
|
1451
1594
|
}
|
|
1452
1595
|
|
|
1453
1596
|
//#endregion
|
|
1454
|
-
export {
|
|
1455
|
-
//# sourceMappingURL=subcommands-
|
|
1597
|
+
export { finalizedLines as a, listRecords as c, renderRawLog as d, resolveOne as f, writeToIpc as g, stopTipForCli as h, cursorAbs as i, matchKeyword as l, snapshotStatus as m, cmdHelp as n, isPidAlive as o, runSubcommand as p, controlCodeFromName as r, isSubcommand as s, GRACEFUL_EXIT_COMMANDS as t, readNotes as u };
|
|
1598
|
+
//# sourceMappingURL=subcommands-BLPtg1xN.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-osmGP6ly.js";
|
|
3
3
|
import { n as agentYesHome, t as PidStore } from "./pidStore-DBjlqzo8.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
|
|
5
5
|
import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
|
|
@@ -1714,4 +1714,4 @@ function sleep(ms) {
|
|
|
1714
1714
|
|
|
1715
1715
|
//#endregion
|
|
1716
1716
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1717
|
-
//# sourceMappingURL=ts-
|
|
1717
|
+
//# sourceMappingURL=ts-D_sIq4Yv.js.map
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
|
|
8
8
|
//#region package.json
|
|
9
9
|
var name = "agent-yes";
|
|
10
|
-
var version = "1.
|
|
10
|
+
var version = "1.108.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -221,4 +221,4 @@ async function displayVersion() {
|
|
|
221
221
|
|
|
222
222
|
//#endregion
|
|
223
223
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
224
|
-
//# sourceMappingURL=versionChecker-
|
|
224
|
+
//# sourceMappingURL=versionChecker-osmGP6ly.js.map
|
package/lab/ui/index.html
CHANGED
|
@@ -1181,7 +1181,11 @@
|
|
|
1181
1181
|
// that agent as soon as it shows up in the list, then forgets itself. With
|
|
1182
1182
|
// no ?pid, fall back to the agent this device last had selected — so a
|
|
1183
1183
|
// refresh lands you back on the same agent instead of the placeholder.
|
|
1184
|
+
// autoPidExplicit distinguishes the two: an explicit link opens the agent
|
|
1185
|
+
// even on mobile (it's a deliberate "take me here"), whereas a merely
|
|
1186
|
+
// restored selection only re-highlights the row on a phone — see loadList.
|
|
1184
1187
|
let autoPid = new URLSearchParams(location.search).get("pid");
|
|
1188
|
+
let autoPidExplicit = !!autoPid;
|
|
1185
1189
|
if (autoPid) {
|
|
1186
1190
|
history.replaceState(null, document.title, location.pathname + location.hash);
|
|
1187
1191
|
} else {
|
|
@@ -1202,7 +1206,17 @@
|
|
|
1202
1206
|
if (autoPid && entries.some((x) => String(x.pid) === String(autoPid))) {
|
|
1203
1207
|
const pid = autoPid;
|
|
1204
1208
|
autoPid = null;
|
|
1205
|
-
|
|
1209
|
+
// On a phone, a restored selection re-highlights the row but stays on
|
|
1210
|
+
// the list — opening it would flip straight into the full-screen
|
|
1211
|
+
// terminal (show-detail) and hide the list. An explicit ?pid= link is
|
|
1212
|
+
// a deliberate jump, so it still opens. Desktop shows both panes, so
|
|
1213
|
+
// there's nothing to scope — always open.
|
|
1214
|
+
if (autoPidExplicit || window.innerWidth > 720) {
|
|
1215
|
+
select(pid);
|
|
1216
|
+
} else {
|
|
1217
|
+
sel = pid;
|
|
1218
|
+
renderList();
|
|
1219
|
+
}
|
|
1206
1220
|
}
|
|
1207
1221
|
}
|
|
1208
1222
|
|
package/package.json
CHANGED
package/ts/subcommands.spec.ts
CHANGED
|
@@ -458,6 +458,45 @@ describe("subcommands.cmdRead renders raw log via xterm-headless", () => {
|
|
|
458
458
|
});
|
|
459
459
|
});
|
|
460
460
|
|
|
461
|
+
// The plain (pipe/script) follow mode emits a line only once the cursor has
|
|
462
|
+
// moved off it, so in-place redraws (spinners, progress bars, TUI repaints) stay
|
|
463
|
+
// out of the stream. finalizedLines() is that rule; drive it with a real
|
|
464
|
+
// @xterm/headless terminal so the assertions reflect actual PTY semantics.
|
|
465
|
+
describe("subcommands.finalizedLines (plain follow line discipline)", () => {
|
|
466
|
+
async function newTerm() {
|
|
467
|
+
const { Terminal } = await import("@xterm/headless");
|
|
468
|
+
return new Terminal({ cols: 80, rows: 10, scrollback: 1000, allowProposedApi: true });
|
|
469
|
+
}
|
|
470
|
+
const feed = (term: any, s: string) =>
|
|
471
|
+
new Promise<void>((r) => term.write(new TextEncoder().encode(s), () => r()));
|
|
472
|
+
|
|
473
|
+
it("emits newline-finalized lines and suppresses in-place redraws", async () => {
|
|
474
|
+
const { finalizedLines, cursorAbs } = await loadModule();
|
|
475
|
+
const term = await newTerm();
|
|
476
|
+
|
|
477
|
+
await feed(term, "line A\r\nline B\r\n");
|
|
478
|
+
// Cursor is now on the empty row 2; rows 0–1 are finalized.
|
|
479
|
+
expect(finalizedLines(term as any, 0)).toEqual(["line A", "line B"]);
|
|
480
|
+
|
|
481
|
+
// A spinner rewrites the current row in place (CR, no newline) — not finalized.
|
|
482
|
+
let mark = cursorAbs(term as any);
|
|
483
|
+
await feed(term, "\x1b[33mWorking |\x1b[0m");
|
|
484
|
+
expect(finalizedLines(term as any, mark)).toEqual([]);
|
|
485
|
+
await feed(term, "\rWorking /"); // redraw same row
|
|
486
|
+
expect(finalizedLines(term as any, mark)).toEqual([]);
|
|
487
|
+
|
|
488
|
+
// Once the line is overwritten with real content AND terminated, it commits.
|
|
489
|
+
await feed(term, "\rdownload complete\r\n");
|
|
490
|
+
expect(finalizedLines(term as any, mark)).toEqual(["download complete"]);
|
|
491
|
+
|
|
492
|
+
// Advancing the high-water mark, nothing new is finalized until more arrives.
|
|
493
|
+
mark = cursorAbs(term as any);
|
|
494
|
+
expect(finalizedLines(term as any, mark)).toEqual([]);
|
|
495
|
+
await feed(term, "next line\r\n");
|
|
496
|
+
expect(finalizedLines(term as any, mark)).toEqual(["next line"]);
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
461
500
|
describe("subcommands.cmdSend writes bytes to FIFO", () => {
|
|
462
501
|
// Skip on non-unix because FIFO creation requires mkfifo
|
|
463
502
|
const itUnix = process.platform === "linux" || process.platform === "darwin";
|
package/ts/subcommands.ts
CHANGED
|
@@ -524,6 +524,7 @@ async function runRemoteRead(
|
|
|
524
524
|
follow: boolean,
|
|
525
525
|
n: number,
|
|
526
526
|
reconnectTimeoutMs = 120_000,
|
|
527
|
+
_plain = false,
|
|
527
528
|
): Promise<number> {
|
|
528
529
|
const keyword = remote.keyword ?? "";
|
|
529
530
|
if (!keyword) {
|
|
@@ -535,7 +536,12 @@ async function runRemoteRead(
|
|
|
535
536
|
|
|
536
537
|
if (mode === "tail" && follow) {
|
|
537
538
|
const ac = new AbortController();
|
|
538
|
-
|
|
539
|
+
// SIGINT/SIGTERM/SIGHUP and a closed pipe all abort the stream, so
|
|
540
|
+
// `timeout … ay tail -f` and `kill` terminate promptly (was SIGINT-only,
|
|
541
|
+
// which let `timeout` run the full --reconnect-timeout window). The server
|
|
542
|
+
// already sends rendered, newline-delimited text, so the wire is plain.
|
|
543
|
+
const disposeSignals = installStreamSignals(() => ac.abort());
|
|
544
|
+
ac.signal.addEventListener("abort", disposeSignals, { once: true });
|
|
539
545
|
const deadline = Date.now() + reconnectTimeoutMs;
|
|
540
546
|
let delay = 1_000;
|
|
541
547
|
let attempt = 0;
|
|
@@ -988,6 +994,13 @@ async function cmdRead(rest: string[], { mode }: ReadOpts): Promise<number> {
|
|
|
988
994
|
description: "Follow log output (Ctrl-C to stop)",
|
|
989
995
|
})
|
|
990
996
|
.option("n", { type: "number", description: "Number of lines (default: 96 for tail/head)" })
|
|
997
|
+
.option("plain", {
|
|
998
|
+
type: "boolean",
|
|
999
|
+
default: false,
|
|
1000
|
+
description:
|
|
1001
|
+
"Line-buffered plain text for pipes/scripts (no ANSI redraws or spinner). " +
|
|
1002
|
+
"Auto-enabled when stdout is not a TTY.",
|
|
1003
|
+
})
|
|
991
1004
|
.option("all", { type: "boolean", default: false, description: "Include exited agents" })
|
|
992
1005
|
.option("latest", {
|
|
993
1006
|
type: "boolean",
|
|
@@ -1005,6 +1018,12 @@ async function cmdRead(rest: string[], { mode }: ReadOpts): Promise<number> {
|
|
|
1005
1018
|
.exitProcess(false);
|
|
1006
1019
|
|
|
1007
1020
|
const argv = await y.parseAsync();
|
|
1021
|
+
// A closed downstream pipe (e.g. `… | head -3`) makes stdout writes fail with
|
|
1022
|
+
// EPIPE. Treat it as a clean exit — the reader is gone, our job is done.
|
|
1023
|
+
ensureEpipeExit();
|
|
1024
|
+
// Pipes/scripts get line-buffered plain text by default; an explicit --plain
|
|
1025
|
+
// forces it even on a TTY. See followPlainLocal / runRemoteRead.
|
|
1026
|
+
const plain = Boolean(argv.plain) || !process.stdout.isTTY;
|
|
1008
1027
|
const opts: CommonOpts = {
|
|
1009
1028
|
all: argv.all,
|
|
1010
1029
|
active: false,
|
|
@@ -1023,7 +1042,7 @@ async function cmdRead(rest: string[], { mode }: ReadOpts): Promise<number> {
|
|
|
1023
1042
|
? 0
|
|
1024
1043
|
: 96;
|
|
1025
1044
|
const reconnectTimeoutMs = ((argv["reconnect-timeout"] as number) ?? 120) * 1000;
|
|
1026
|
-
if (remote) return runRemoteRead(remote, mode, argv.follow, n2, reconnectTimeoutMs);
|
|
1045
|
+
if (remote) return runRemoteRead(remote, mode, argv.follow, n2, reconnectTimeoutMs, plain);
|
|
1027
1046
|
}
|
|
1028
1047
|
const follow = argv.follow;
|
|
1029
1048
|
const nFlag = argv.n;
|
|
@@ -1062,28 +1081,7 @@ async function cmdRead(rest: string[], { mode }: ReadOpts): Promise<number> {
|
|
|
1062
1081
|
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
1063
1082
|
|
|
1064
1083
|
if (follow) {
|
|
1065
|
-
|
|
1066
|
-
let offset = buf.length;
|
|
1067
|
-
const { watch } = await import("fs");
|
|
1068
|
-
// oxlint-disable-next-line no-control-regex -- intentional: strip ANSI/control
|
|
1069
|
-
const ansiRe = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
|
|
1070
|
-
// oxlint-disable-next-line no-control-regex -- intentional: strip control chars
|
|
1071
|
-
const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
1072
|
-
await new Promise<void>((resolve) => {
|
|
1073
|
-
const watcher = watch(logPath, async () => {
|
|
1074
|
-
const full = await readFile(logPath);
|
|
1075
|
-
if (full.length <= offset) return;
|
|
1076
|
-
const chunk = full.slice(offset);
|
|
1077
|
-
offset = full.length;
|
|
1078
|
-
const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
|
|
1079
|
-
if (text.trim()) process.stdout.write(text.trimStart());
|
|
1080
|
-
});
|
|
1081
|
-
process.on("SIGINT", () => {
|
|
1082
|
-
watcher.close();
|
|
1083
|
-
resolve();
|
|
1084
|
-
});
|
|
1085
|
-
});
|
|
1086
|
-
return 0;
|
|
1084
|
+
return plain ? followPlainLocal(logPath, buf) : followRawLocal(logPath, buf);
|
|
1087
1085
|
}
|
|
1088
1086
|
|
|
1089
1087
|
process.stderr.write(
|
|
@@ -1096,6 +1094,206 @@ async function cmdRead(rest: string[], { mode }: ReadOpts): Promise<number> {
|
|
|
1096
1094
|
return 0;
|
|
1097
1095
|
}
|
|
1098
1096
|
|
|
1097
|
+
/**
|
|
1098
|
+
* Exit cleanly when stdout's downstream closes (EPIPE). Node ignores SIGPIPE and
|
|
1099
|
+
* surfaces a broken pipe as a stream 'error'; with no listener it throws, and in
|
|
1100
|
+
* follow mode the watch loop would otherwise hang. Idempotent — one listener for
|
|
1101
|
+
* the life of the process, tagged on stdout so repeated calls (and module
|
|
1102
|
+
* reloads in tests) don't pile up listeners.
|
|
1103
|
+
*/
|
|
1104
|
+
function ensureEpipeExit(): void {
|
|
1105
|
+
const TAG = "__ayEpipeExit";
|
|
1106
|
+
if ((process.stdout as unknown as Record<string, boolean>)[TAG]) return;
|
|
1107
|
+
(process.stdout as unknown as Record<string, boolean>)[TAG] = true;
|
|
1108
|
+
process.stdout.on("error", (e: NodeJS.ErrnoException) => {
|
|
1109
|
+
if (e?.code === "EPIPE") process.exit(0);
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Install signal handlers for a streaming follower so it terminates promptly
|
|
1115
|
+
* under automation, not just on an interactive Ctrl-C. SIGINT/SIGTERM/SIGHUP all
|
|
1116
|
+
* run `stop` (so `timeout … ay tail -f` and `kill` both work); a closed stdout
|
|
1117
|
+
* (EPIPE) exits cleanly via ensureEpipeExit. Returns a disposer that removes the
|
|
1118
|
+
* signal listeners.
|
|
1119
|
+
*/
|
|
1120
|
+
function installStreamSignals(stop: () => void): () => void {
|
|
1121
|
+
ensureEpipeExit();
|
|
1122
|
+
const onSig = () => stop();
|
|
1123
|
+
process.on("SIGINT", onSig);
|
|
1124
|
+
process.on("SIGTERM", onSig);
|
|
1125
|
+
process.on("SIGHUP", onSig);
|
|
1126
|
+
return () => {
|
|
1127
|
+
process.off("SIGINT", onSig);
|
|
1128
|
+
process.off("SIGTERM", onSig);
|
|
1129
|
+
process.off("SIGHUP", onSig);
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* Coalescing file watcher: re-reads `logPath` on every change, hands each newly
|
|
1135
|
+
* appended byte range to `onChunk`, and never overlaps reads (a change that
|
|
1136
|
+
* arrives mid-read is serviced once the current read finishes). `startOffset`
|
|
1137
|
+
* is where the already-emitted prefix ends. Resolves when `stop` is signalled.
|
|
1138
|
+
*/
|
|
1139
|
+
async function watchAppend(
|
|
1140
|
+
logPath: string,
|
|
1141
|
+
startOffset: number,
|
|
1142
|
+
onChunk: (chunk: Uint8Array) => Promise<void> | void,
|
|
1143
|
+
onStop: () => void,
|
|
1144
|
+
): Promise<void> {
|
|
1145
|
+
const { watch } = await import("fs");
|
|
1146
|
+
let offset = startOffset;
|
|
1147
|
+
let reading = false;
|
|
1148
|
+
let pending = false;
|
|
1149
|
+
await new Promise<void>((resolve) => {
|
|
1150
|
+
let done = false;
|
|
1151
|
+
const finish = () => {
|
|
1152
|
+
if (done) return;
|
|
1153
|
+
done = true;
|
|
1154
|
+
try {
|
|
1155
|
+
watcher.close();
|
|
1156
|
+
} catch {}
|
|
1157
|
+
dispose();
|
|
1158
|
+
onStop();
|
|
1159
|
+
resolve();
|
|
1160
|
+
};
|
|
1161
|
+
const dispose = installStreamSignals(finish);
|
|
1162
|
+
const pump = async () => {
|
|
1163
|
+
if (reading) {
|
|
1164
|
+
pending = true;
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
reading = true;
|
|
1168
|
+
do {
|
|
1169
|
+
pending = false;
|
|
1170
|
+
let full: Uint8Array;
|
|
1171
|
+
try {
|
|
1172
|
+
full = await readFile(logPath);
|
|
1173
|
+
} catch {
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
if (full.length > offset) {
|
|
1177
|
+
const chunk = full.slice(offset);
|
|
1178
|
+
offset = full.length;
|
|
1179
|
+
await onChunk(chunk);
|
|
1180
|
+
}
|
|
1181
|
+
} while (pending && !done);
|
|
1182
|
+
reading = false;
|
|
1183
|
+
};
|
|
1184
|
+
const watcher = watch(logPath, () => void pump());
|
|
1185
|
+
// The file may have grown between our initial read and the watch starting.
|
|
1186
|
+
void pump();
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Default (interactive) follow: append each new byte range with ANSI/control
|
|
1192
|
+
* sequences stripped. Mirrors the historical behaviour, plus prompt signal /
|
|
1193
|
+
* pipe-close handling.
|
|
1194
|
+
*/
|
|
1195
|
+
async function followRawLocal(logPath: string, buf: Uint8Array): Promise<number> {
|
|
1196
|
+
process.stderr.write(`following... (Ctrl-C to stop)\n`);
|
|
1197
|
+
// oxlint-disable-next-line no-control-regex -- intentional: strip ANSI/control
|
|
1198
|
+
const ansiRe = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
|
|
1199
|
+
// oxlint-disable-next-line no-control-regex -- intentional: strip control chars
|
|
1200
|
+
const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
1201
|
+
await watchAppend(
|
|
1202
|
+
logPath,
|
|
1203
|
+
buf.length,
|
|
1204
|
+
(chunk) => {
|
|
1205
|
+
const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
|
|
1206
|
+
if (text.trim()) process.stdout.write(text.trimStart());
|
|
1207
|
+
},
|
|
1208
|
+
() => {},
|
|
1209
|
+
);
|
|
1210
|
+
return 0;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Minimal view of an @xterm/headless buffer — just what the line-finalization
|
|
1215
|
+
* logic needs, so it can be unit-tested against a real Terminal or a stub.
|
|
1216
|
+
*/
|
|
1217
|
+
export interface PlainTermView {
|
|
1218
|
+
buffer: {
|
|
1219
|
+
active: {
|
|
1220
|
+
baseY: number;
|
|
1221
|
+
cursorY: number;
|
|
1222
|
+
getLine(i: number): { translateToString(trim: boolean): string } | undefined;
|
|
1223
|
+
};
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/** Absolute index (scrollback + viewport row) of the row the cursor sits on. */
|
|
1228
|
+
export function cursorAbs(term: PlainTermView): number {
|
|
1229
|
+
return term.buffer.active.baseY + term.buffer.active.cursorY;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* The lines in [fromAbs, cursorRow) — rows the cursor has moved PAST, i.e.
|
|
1234
|
+
* finalized text. A row still being rewritten in place (spinner, progress bar,
|
|
1235
|
+
* TUI repaint) is the cursor's own row and is excluded until the cursor leaves
|
|
1236
|
+
* it, which is what keeps redraw churn out of the plain stream.
|
|
1237
|
+
*/
|
|
1238
|
+
export function finalizedLines(term: PlainTermView, fromAbs: number): string[] {
|
|
1239
|
+
const a = term.buffer.active;
|
|
1240
|
+
const cur = a.baseY + a.cursorY;
|
|
1241
|
+
const out: string[] = [];
|
|
1242
|
+
for (let i = Math.max(0, fromAbs); i < cur; i++) {
|
|
1243
|
+
const l = a.getLine(i);
|
|
1244
|
+
out.push(l ? l.translateToString(false).trimEnd() : "");
|
|
1245
|
+
}
|
|
1246
|
+
return out;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Plain (pipe/script) follow: feed the live PTY stream through @xterm/headless
|
|
1251
|
+
* and emit each line only once it's finalized — i.e. once the cursor has moved
|
|
1252
|
+
* off it. In-place redraws (spinners, progress bars that rewrite the current
|
|
1253
|
+
* line, full-screen TUI repaints) churn the cursor's row and never emit until
|
|
1254
|
+
* settled, so the output is clean, newline-terminated, line-buffered text a
|
|
1255
|
+
* script can read. On stop, flush the line the cursor is still sitting on.
|
|
1256
|
+
*/
|
|
1257
|
+
async function followPlainLocal(logPath: string, buf: Uint8Array): Promise<number> {
|
|
1258
|
+
process.stderr.write(`following... (plain; Ctrl-C / SIGTERM to stop)\n`);
|
|
1259
|
+
const { Terminal } = await import("@xterm/headless");
|
|
1260
|
+
const term = new Terminal({ cols: 200, rows: 50, scrollback: 50000, allowProposedApi: true });
|
|
1261
|
+
const feed = (b: Uint8Array) => new Promise<void>((r) => term.write(b, () => r()));
|
|
1262
|
+
const lineAt = (i: number) => {
|
|
1263
|
+
const l = term.buffer.active.getLine(i);
|
|
1264
|
+
return l ? l.translateToString(false).trimEnd() : "";
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
// Seed with the existing log so we start streaming from the live frontier —
|
|
1268
|
+
// the recent context was already printed by the static tail above.
|
|
1269
|
+
await feed(buf);
|
|
1270
|
+
let emitted = cursorAbs(term);
|
|
1271
|
+
|
|
1272
|
+
// `emitted` only advances, so a redraw that moves the cursor back up doesn't
|
|
1273
|
+
// re-emit lines it then rewrites.
|
|
1274
|
+
const flushCommitted = () => {
|
|
1275
|
+
for (const line of finalizedLines(term, emitted)) process.stdout.write(line + "\n");
|
|
1276
|
+
emitted = cursorAbs(term);
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
await watchAppend(
|
|
1280
|
+
logPath,
|
|
1281
|
+
buf.length,
|
|
1282
|
+
async (chunk) => {
|
|
1283
|
+
await feed(chunk);
|
|
1284
|
+
flushCommitted();
|
|
1285
|
+
},
|
|
1286
|
+
() => {
|
|
1287
|
+
// Final flush: include the cursor's own row if it has content, so the last
|
|
1288
|
+
// partial line isn't lost when we're killed mid-stream.
|
|
1289
|
+
flushCommitted();
|
|
1290
|
+
const last = lineAt(cursorAbs(term));
|
|
1291
|
+
if (last) process.stdout.write(last + "\n");
|
|
1292
|
+
},
|
|
1293
|
+
);
|
|
1294
|
+
return 0;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1099
1297
|
/**
|
|
1100
1298
|
* Feed the raw PTY bytes through @xterm/headless and emit plain text.
|
|
1101
1299
|
* Same approach as koho's renderTerminalBuffer + agent-yes's XtermProxy.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-CFnUatY1.js";
|
|
2
|
-
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-CRaHzP0C.js";
|
|
4
|
-
import "./pidStore-DBjlqzo8.js";
|
|
5
|
-
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-O3atAbo5.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import "./logger-B9h0djqx.js";
|
|
2
|
-
import "./globalPidIndex-yVd3mbsV.js";
|
|
3
|
-
import "./remotes-C3xPRtfg.js";
|
|
4
|
-
import { a as isSubcommand, c as readNotes, d as runSubcommand, f as snapshotStatus, i as isPidAlive, l as renderRawLog, m as writeToIpc, n as cmdHelp, o as listRecords, p as stopTipForCli, r as controlCodeFromName, s as matchKeyword, t as GRACEFUL_EXIT_COMMANDS, u as resolveOne } from "./subcommands-BImrR-R6.js";
|
|
5
|
-
|
|
6
|
-
export { cmdHelp, isSubcommand, runSubcommand };
|