@aragon-agent/cli 0.6.3 → 0.6.7
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/CHANGELOG.md +142 -0
- package/README.md +203 -13
- package/dist/agent/controller.d.ts +158 -2
- package/dist/agent/controller.js +400 -1
- package/dist/agent/reducer.d.ts +102 -19
- package/dist/agent/reducer.js +162 -71
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/agent/usage.d.ts +34 -0
- package/dist/agent/usage.js +43 -0
- package/dist/cli.js +132 -23
- package/dist/commands/builtins.js +101 -1
- package/dist/compaction/child.js +5 -0
- package/dist/compaction/command.js +14 -1
- package/dist/compaction/compactor.d.ts +94 -24
- package/dist/compaction/compactor.js +247 -98
- package/dist/compaction/context-command.d.ts +33 -0
- package/dist/compaction/context-command.js +166 -0
- package/dist/compaction/meter.d.ts +251 -0
- package/dist/compaction/meter.js +428 -0
- package/dist/compaction/pressure.d.ts +6 -0
- package/dist/compaction/pressure.js +4 -0
- package/dist/compaction/types.d.ts +77 -0
- package/dist/compaction/wiring.d.ts +56 -0
- package/dist/compaction/wiring.js +107 -25
- package/dist/config/cli-commands.js +5 -0
- package/dist/config/env.d.ts +1 -1
- package/dist/config/env.js +63 -2
- package/dist/config/load.d.ts +28 -0
- package/dist/config/load.js +83 -1
- package/dist/config/schema.d.ts +129 -0
- package/dist/config/schema.js +69 -0
- package/dist/config/store.js +47 -1
- package/dist/exec/events.d.ts +138 -1
- package/dist/exec/events.js +42 -0
- package/dist/exec/index.js +43 -6
- package/dist/exec/progress-reporter.d.ts +23 -0
- package/dist/exec/progress-reporter.js +88 -0
- package/dist/exec/runner.d.ts +34 -0
- package/dist/exec/runner.js +137 -25
- package/dist/fast/reviewer.d.ts +19 -1
- package/dist/fast/reviewer.js +22 -0
- package/dist/fast/types.d.ts +15 -0
- package/dist/fast/wiring.d.ts +10 -1
- package/dist/fast/wiring.js +18 -1
- package/dist/input/limits.d.ts +72 -0
- package/dist/input/limits.js +63 -0
- package/dist/input/paste-parse.d.ts +71 -0
- package/dist/input/paste-parse.js +95 -0
- package/dist/input/stdin-filter.d.ts +81 -0
- package/dist/input/stdin-filter.js +379 -0
- package/dist/logging/install.d.ts +2 -0
- package/dist/logging/install.js +53 -1
- package/dist/proc/classify.d.ts +52 -0
- package/dist/proc/classify.js +143 -0
- package/dist/proc/kill-tree.d.ts +42 -0
- package/dist/proc/kill-tree.js +119 -0
- package/dist/proc/limits.d.ts +84 -0
- package/dist/proc/limits.js +84 -0
- package/dist/proc/log-ring.d.ts +68 -0
- package/dist/proc/log-ring.js +102 -0
- package/dist/proc/prompt.d.ts +24 -0
- package/dist/proc/prompt.js +49 -0
- package/dist/proc/readiness.d.ts +102 -0
- package/dist/proc/readiness.js +219 -0
- package/dist/proc/supervisor.d.ts +103 -0
- package/dist/proc/supervisor.js +465 -0
- package/dist/proc/types.d.ts +175 -0
- package/dist/proc/types.js +15 -0
- package/dist/session/persist.js +36 -1
- package/dist/todo/limits.d.ts +4 -17
- package/dist/todo/limits.js +4 -17
- package/dist/todo/panel-rows.d.ts +8 -6
- package/dist/todo/panel-rows.js +19 -6
- package/dist/tools/bash-tool.d.ts +51 -2
- package/dist/tools/bash-tool.js +321 -51
- package/dist/tools/fs-tools.d.ts +17 -0
- package/dist/tools/index.d.ts +55 -2
- package/dist/tools/index.js +39 -1
- package/dist/tools/proc-tools.d.ts +25 -0
- package/dist/tools/proc-tools.js +102 -0
- package/dist/ui/App.d.ts +15 -2
- package/dist/ui/App.js +330 -69
- package/dist/ui/AutocompletePopup.d.ts +3 -1
- package/dist/ui/AutocompletePopup.js +9 -11
- package/dist/ui/BottomStatusRow.d.ts +1 -1
- package/dist/ui/Composer.d.ts +56 -1
- package/dist/ui/Composer.js +50 -6
- package/dist/ui/PromptInput.d.ts +52 -3
- package/dist/ui/PromptInput.js +201 -55
- package/dist/ui/StatusBar.d.ts +40 -15
- package/dist/ui/StatusBar.js +51 -9
- package/dist/ui/TeamPanel.d.ts +3 -1
- package/dist/ui/TeamPanel.js +13 -9
- package/dist/ui/TodoPanel.d.ts +5 -5
- package/dist/ui/TodoPanel.js +56 -24
- package/dist/ui/Transcript.js +29 -1
- package/dist/ui/UpdateLine.d.ts +1 -1
- package/dist/ui/capabilities.js +5 -5
- package/dist/ui/composer-limits.d.ts +31 -0
- package/dist/ui/composer-limits.js +50 -0
- package/dist/ui/composer-rows.d.ts +73 -0
- package/dist/ui/composer-rows.js +201 -0
- package/dist/ui/editor-reducer.d.ts +37 -0
- package/dist/ui/editor-reducer.js +88 -12
- package/dist/ui/entries/ServiceCard.d.ts +66 -0
- package/dist/ui/entries/ServiceCard.js +116 -0
- package/dist/ui/entries/UserEntry.d.ts +17 -3
- package/dist/ui/entries/UserEntry.js +34 -5
- package/dist/ui/exit-snapshot.d.ts +9 -0
- package/dist/ui/layout/ScrollViewport.d.ts +1 -1
- package/dist/ui/layout/autocomplete.d.ts +18 -0
- package/dist/ui/layout/autocomplete.js +26 -0
- package/dist/ui/layout/budget.d.ts +34 -6
- package/dist/ui/layout/budget.js +32 -7
- package/dist/ui/layout/rail.d.ts +4 -53
- package/dist/ui/layout/rail.js +10 -62
- package/dist/ui/layout/team-panel.d.ts +18 -0
- package/dist/ui/layout/team-panel.js +22 -0
- package/dist/ui/layout/todo-layout.d.ts +24 -0
- package/dist/ui/layout/todo-layout.js +27 -0
- package/dist/ui/layout/virtual-window.js +51 -1
- package/dist/ui/overlays/HelpOverlay.js +3 -0
- package/dist/ui/overlays/PlanReviewOverlay.js +10 -2
- package/dist/ui/overlays/QuestionOverlay.js +5 -1
- package/dist/ui/overlays/SettingsScreen.js +14 -1
- package/dist/ui/paste-frames.d.ts +87 -0
- package/dist/ui/paste-frames.js +112 -0
- package/dist/ui/paste-tokens.d.ts +90 -0
- package/dist/ui/paste-tokens.js +147 -0
- package/dist/ui/screen.d.ts +16 -0
- package/dist/ui/screen.js +45 -2
- package/dist/ui/selection/selection-controller.d.ts +1 -1
- package/dist/ui/transcript-text.js +55 -2
- package/dist/ui/use-wheel-routing.d.ts +1 -1
- package/package.json +2 -2
- package/dist/input/stdin-mouse-filter.d.ts +0 -47
- package/dist/input/stdin-mouse-filter.js +0 -153
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* transcript in the terminal's native scrollback, and replaying there would
|
|
7
7
|
* simply print everything twice.
|
|
8
8
|
*/
|
|
9
|
-
import { formatCost, formatDuration, formatTokens } from '../agent/usage.js';
|
|
9
|
+
import { formatCost, formatDuration, formatTokens, promptTokensOf } from '../agent/usage.js';
|
|
10
10
|
import { formatRetryLine } from '../agent/retry-view.js';
|
|
11
11
|
const DEFAULT_MAX_ENTRIES = 200;
|
|
12
12
|
const CONTINUATION_INDENT = ' ';
|
|
@@ -51,6 +51,34 @@ function safeParse(raw) {
|
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* The replay's glyph for a service, MIRRORING `ServiceCard::serviceGlyph`.
|
|
56
|
+
*
|
|
57
|
+
* The two ladders have to agree, and the cheap-looking shortcut here — "ready or
|
|
58
|
+
* running is a tick, everything else is an error" — disagrees on the two states
|
|
59
|
+
* a user reaches ON PURPOSE: a service they stopped with `Ctrl+C`, and one that
|
|
60
|
+
* exited cleanly. Marking either as a failure in the record the user is left
|
|
61
|
+
* with after the alternate screen is torn down is a lie about their own action.
|
|
62
|
+
*
|
|
63
|
+
* Duplicated rather than imported, as every other case in this file duplicates
|
|
64
|
+
* its card's ladder: this module is deliberately free of React and ink, and
|
|
65
|
+
* `serviceGlyph` lives in a `.tsx`.
|
|
66
|
+
*/
|
|
67
|
+
function serviceTextGlyph(status, exitCode, glyphs) {
|
|
68
|
+
switch (status) {
|
|
69
|
+
case 'ready':
|
|
70
|
+
return glyphs.toolDone;
|
|
71
|
+
case 'running':
|
|
72
|
+
return glyphs.toolRunning;
|
|
73
|
+
case 'starting':
|
|
74
|
+
case 'stopped':
|
|
75
|
+
return glyphs.toolPending;
|
|
76
|
+
case 'exited':
|
|
77
|
+
return exitCode === 0 ? glyphs.toolDone : glyphs.toolError;
|
|
78
|
+
default:
|
|
79
|
+
return glyphs.toolError;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
54
82
|
function renderEntry(entry, glyphs) {
|
|
55
83
|
switch (entry.kind) {
|
|
56
84
|
case 'user':
|
|
@@ -134,6 +162,27 @@ function renderEntry(entry, glyphs) {
|
|
|
134
162
|
...entry.summary.split('\n').map((l) => `${TOOL_INDENT}${CONTINUATION_INDENT}${l}`),
|
|
135
163
|
];
|
|
136
164
|
}
|
|
165
|
+
case 'service': {
|
|
166
|
+
// A service the agent started is real work with a real outcome, and an
|
|
167
|
+
// exported transcript that omitted it would be unaccountable for both —
|
|
168
|
+
// the argument the `team`, `retry`, `fast` and `compaction` cases above
|
|
169
|
+
// each make in their own words. It matters more here than for any of them:
|
|
170
|
+
// this replay is the user's ONLY record once the alternate screen is torn
|
|
171
|
+
// down (`cli.tsx`'s exit snapshot), and a service card dropped from it
|
|
172
|
+
// takes the URL the agent reported with it. This file's
|
|
173
|
+
// `default: return []` means the addition is a CHOICE rather than a
|
|
174
|
+
// compile requirement, which is exactly why it is easy to leave out
|
|
175
|
+
// (P0-3).
|
|
176
|
+
const glyph = serviceTextGlyph(entry.status, entry.exitCode, glyphs);
|
|
177
|
+
const where = entry.url ? ` ${entry.url}` : '';
|
|
178
|
+
const code = entry.exitCode === null || entry.exitCode === undefined
|
|
179
|
+
? ''
|
|
180
|
+
: ` (exit code ${entry.exitCode})`;
|
|
181
|
+
return [
|
|
182
|
+
`${TOOL_INDENT}${glyph} service ${entry.serviceId} ${entry.command} ` +
|
|
183
|
+
`${entry.status}${where}${code}`.trimEnd(),
|
|
184
|
+
];
|
|
185
|
+
}
|
|
137
186
|
case 'notice': {
|
|
138
187
|
const glyph = entry.level === 'error'
|
|
139
188
|
? glyphs.error
|
|
@@ -150,7 +199,11 @@ function renderEntry(entry, glyphs) {
|
|
|
150
199
|
export function renderTranscriptText(entries, opts) {
|
|
151
200
|
const turns = entries.filter((e) => e.kind === 'user').length;
|
|
152
201
|
const g = opts.glyphs;
|
|
153
|
-
|
|
202
|
+
// THE SAME PROMPT-SIDE TOTAL THE STATUS BAR SHOWS (context-usage-gauge-accuracy
|
|
203
|
+
// §3.6). This line is what a user compares against the bar they were watching a
|
|
204
|
+
// second earlier, so a different unit here would look like the exit summary
|
|
205
|
+
// losing tokens.
|
|
206
|
+
const tokens = `${formatTokens(promptTokensOf(opts.usageTotal))}${g.arrowUp} ${formatTokens(opts.usageTotal.outputTokens)}${g.arrowDown}`;
|
|
154
207
|
const summary = [
|
|
155
208
|
'aragon',
|
|
156
209
|
`${opts.provider}:${opts.model}`,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* the wheel spins: one gesture, one `setState`, one render.
|
|
21
21
|
*/
|
|
22
22
|
import type { Overlay } from '../agent/reducer.js';
|
|
23
|
-
import type { MouseSource } from '../input/stdin-
|
|
23
|
+
import type { MouseSource } from '../input/stdin-filter.js';
|
|
24
24
|
import type { ScrollIntent } from './layout/scroll.js';
|
|
25
25
|
/**
|
|
26
26
|
* Rows a PgUp/PgDn — and a Shift+wheel notch — moves inside a controlled
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aragon-agent/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AragonAgent contributors",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepublishOnly": "npm run build"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aragon-agent/core": "^0.2.
|
|
59
|
+
"@aragon-agent/core": "^0.2.19",
|
|
60
60
|
"chalk": "^5.6.2",
|
|
61
61
|
"cli-highlight": "^2.0.0",
|
|
62
62
|
"commander": "^12.1.0",
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stdin mouse filter (mouse-wheel-region-routing §4.2 / §5.2).
|
|
3
|
-
*
|
|
4
|
-
* WHY A WRAPPER AND NOT A SECOND LISTENER. Ink 5.2.1 owns stdin in a way that
|
|
5
|
-
* forbids a second reader: `components/App.js` calls `setEncoding('utf8')`,
|
|
6
|
-
* `ref()`, `setRawMode(true)`, subscribes to `'readable'` and drains with
|
|
7
|
-
* `read()`. Attaching our own `'data'` listener to the same `process.stdin`
|
|
8
|
-
* would switch the stream into flowing mode and RACE Ink's `read()` for chunks
|
|
9
|
-
* — some keystrokes would reach Ink, some would not, non-deterministically
|
|
10
|
-
* (D-3). So the filter REPLACES the stream Ink is given: it reads the real
|
|
11
|
-
* stdin, strips every mouse report, and writes the remainder into a
|
|
12
|
-
* `PassThrough` that carries the surface Ink requires.
|
|
13
|
-
*
|
|
14
|
-
* ORDERING IS AN INVARIANT, NOT A CONVENTION (I-8). The caller must build this
|
|
15
|
-
* filter BEFORE anything writes `\x1b[?1000h`. Reporting enabled while Ink
|
|
16
|
-
* still holds the raw stream does not degrade the feature — it types
|
|
17
|
-
* `[<0;12;5M` into the user's message on every click, which is strictly worse
|
|
18
|
-
* than the bug this feature fixes. `cli.tsx::runInteractive` is the only caller
|
|
19
|
-
* and passes `filter !== null` to `enterAltScreen`.
|
|
20
|
-
*/
|
|
21
|
-
import { type MouseEvent } from './mouse-events.js';
|
|
22
|
-
export interface MouseSource {
|
|
23
|
-
/**
|
|
24
|
-
* Returns an unsubscribe function. Multiple subscribers are allowed — the
|
|
25
|
-
* wheel router and (since tui-selection-and-scroll-follow) the selection
|
|
26
|
-
* controller both take this one channel, and each ignores the kinds it does
|
|
27
|
-
* not own.
|
|
28
|
-
*/
|
|
29
|
-
subscribe(listener: (event: MouseEvent) => void): () => void;
|
|
30
|
-
}
|
|
31
|
-
export interface MouseFilter {
|
|
32
|
-
/** Hand this to Ink: `render(<App/>, { stdin: filter.stdin })`. */
|
|
33
|
-
readonly stdin: NodeJS.ReadStream;
|
|
34
|
-
readonly source: MouseSource;
|
|
35
|
-
/** Idempotent. Detaches from the real stdin and clears the flush timer (I-7). */
|
|
36
|
-
dispose(): void;
|
|
37
|
-
}
|
|
38
|
-
export declare function createMouseFilter(real: NodeJS.ReadStream): MouseFilter;
|
|
39
|
-
/**
|
|
40
|
-
* Build a filter, or `null` if construction throws.
|
|
41
|
-
*
|
|
42
|
-
* The `null` return is the whole point (P0-1): `enterAltScreen` derives whether
|
|
43
|
-
* to enable mouse reporting from `filter !== null`, so a construction failure
|
|
44
|
-
* degrades to "the wheel is inert" — a supported, tested rung of the fail-safe
|
|
45
|
-
* ladder — rather than leaving reporting on with no filter in front of Ink.
|
|
46
|
-
*/
|
|
47
|
-
export declare function tryCreateMouseFilter(real: NodeJS.ReadStream, onError?: (reason: string) => void): MouseFilter | null;
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stdin mouse filter (mouse-wheel-region-routing §4.2 / §5.2).
|
|
3
|
-
*
|
|
4
|
-
* WHY A WRAPPER AND NOT A SECOND LISTENER. Ink 5.2.1 owns stdin in a way that
|
|
5
|
-
* forbids a second reader: `components/App.js` calls `setEncoding('utf8')`,
|
|
6
|
-
* `ref()`, `setRawMode(true)`, subscribes to `'readable'` and drains with
|
|
7
|
-
* `read()`. Attaching our own `'data'` listener to the same `process.stdin`
|
|
8
|
-
* would switch the stream into flowing mode and RACE Ink's `read()` for chunks
|
|
9
|
-
* — some keystrokes would reach Ink, some would not, non-deterministically
|
|
10
|
-
* (D-3). So the filter REPLACES the stream Ink is given: it reads the real
|
|
11
|
-
* stdin, strips every mouse report, and writes the remainder into a
|
|
12
|
-
* `PassThrough` that carries the surface Ink requires.
|
|
13
|
-
*
|
|
14
|
-
* ORDERING IS AN INVARIANT, NOT A CONVENTION (I-8). The caller must build this
|
|
15
|
-
* filter BEFORE anything writes `\x1b[?1000h`. Reporting enabled while Ink
|
|
16
|
-
* still holds the raw stream does not degrade the feature — it types
|
|
17
|
-
* `[<0;12;5M` into the user's message on every click, which is strictly worse
|
|
18
|
-
* than the bug this feature fixes. `cli.tsx::runInteractive` is the only caller
|
|
19
|
-
* and passes `filter !== null` to `enterAltScreen`.
|
|
20
|
-
*/
|
|
21
|
-
import { PassThrough } from 'node:stream';
|
|
22
|
-
import { splitMouseEvents } from './mouse-events.js';
|
|
23
|
-
/**
|
|
24
|
-
* How long a possible-prefix tail is held before being flushed as text.
|
|
25
|
-
*
|
|
26
|
-
* This is what stops a lone `\x1b` — the Esc key, which aborts a run — from
|
|
27
|
-
* being held hostage waiting for a `[` that will never arrive. Worst-case Esc
|
|
28
|
-
* latency becomes 12 ms, and only for the keystroke that is literally a bare
|
|
29
|
-
* ESC; every other key resolves `isMousePrefix` to false immediately.
|
|
30
|
-
*/
|
|
31
|
-
const PENDING_FLUSH_MS = 12;
|
|
32
|
-
/** Assign a method onto the PassThrough without widening its public type. */
|
|
33
|
-
function define(target, name, value) {
|
|
34
|
-
Object.defineProperty(target, name, { value, writable: true, configurable: true });
|
|
35
|
-
}
|
|
36
|
-
export function createMouseFilter(real) {
|
|
37
|
-
const wrapper = new PassThrough();
|
|
38
|
-
const listeners = new Set();
|
|
39
|
-
let pending = '';
|
|
40
|
-
let flushTimer = null;
|
|
41
|
-
let disposed = false;
|
|
42
|
-
// --- The surface Ink reads off the stream it is handed. ------------------
|
|
43
|
-
// `isRawModeSupported()` reads `props.stdin.isTTY` (App.js:34), so this has
|
|
44
|
-
// to be a DELEGATING GETTER rather than a snapshot: a stdin that is not a TTY
|
|
45
|
-
// must still report so through the wrapper, or Ink throws instead of falling
|
|
46
|
-
// back.
|
|
47
|
-
Object.defineProperty(wrapper, 'isTTY', {
|
|
48
|
-
get: () => real.isTTY,
|
|
49
|
-
configurable: true,
|
|
50
|
-
});
|
|
51
|
-
define(wrapper, 'setRawMode', (mode) => {
|
|
52
|
-
real.setRawMode?.(mode);
|
|
53
|
-
return wrapper;
|
|
54
|
-
});
|
|
55
|
-
define(wrapper, 'ref', () => {
|
|
56
|
-
real.ref?.();
|
|
57
|
-
return wrapper;
|
|
58
|
-
});
|
|
59
|
-
define(wrapper, 'unref', () => {
|
|
60
|
-
real.unref?.();
|
|
61
|
-
return wrapper;
|
|
62
|
-
});
|
|
63
|
-
const emit = (event) => {
|
|
64
|
-
for (const listener of [...listeners])
|
|
65
|
-
listener(event);
|
|
66
|
-
};
|
|
67
|
-
const clearFlushTimer = () => {
|
|
68
|
-
if (!flushTimer)
|
|
69
|
-
return;
|
|
70
|
-
clearTimeout(flushTimer);
|
|
71
|
-
flushTimer = null;
|
|
72
|
-
};
|
|
73
|
-
const flushPending = () => {
|
|
74
|
-
clearFlushTimer();
|
|
75
|
-
if (pending.length === 0 || disposed)
|
|
76
|
-
return;
|
|
77
|
-
const text = pending;
|
|
78
|
-
pending = '';
|
|
79
|
-
wrapper.write(text);
|
|
80
|
-
};
|
|
81
|
-
const onData = (chunk) => {
|
|
82
|
-
if (disposed)
|
|
83
|
-
return;
|
|
84
|
-
clearFlushTimer();
|
|
85
|
-
const decoded = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
86
|
-
const split = splitMouseEvents(pending + decoded);
|
|
87
|
-
pending = split.pending;
|
|
88
|
-
if (split.text.length > 0)
|
|
89
|
-
wrapper.write(split.text);
|
|
90
|
-
for (const event of split.events)
|
|
91
|
-
emit(event);
|
|
92
|
-
if (pending.length > 0) {
|
|
93
|
-
// `.unref()` so a held tail can never keep the process alive on its own.
|
|
94
|
-
flushTimer = setTimeout(flushPending, PENDING_FLUSH_MS);
|
|
95
|
-
flushTimer.unref?.();
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
// P2-7: a closed stdin must still terminate Ink's reader, or it waits forever
|
|
99
|
-
// on a stream that will never produce another byte.
|
|
100
|
-
const onEnd = () => {
|
|
101
|
-
flushPending();
|
|
102
|
-
if (!wrapper.writableEnded)
|
|
103
|
-
wrapper.end();
|
|
104
|
-
};
|
|
105
|
-
// `setEncoding('utf8')` on the REAL stream so Node's StringDecoder handles
|
|
106
|
-
// multi-byte boundaries for us — the parser works on characters, and a UTF-8
|
|
107
|
-
// sequence split across two chunks would otherwise become two mojibake
|
|
108
|
-
// characters before it ever reached `splitMouseEvents`.
|
|
109
|
-
real.setEncoding?.('utf8');
|
|
110
|
-
real.on('data', onData);
|
|
111
|
-
real.once('end', onEnd);
|
|
112
|
-
real.once('close', onEnd);
|
|
113
|
-
return {
|
|
114
|
-
stdin: wrapper,
|
|
115
|
-
source: {
|
|
116
|
-
subscribe(listener) {
|
|
117
|
-
listeners.add(listener);
|
|
118
|
-
return () => listeners.delete(listener);
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
dispose() {
|
|
122
|
-
if (disposed)
|
|
123
|
-
return;
|
|
124
|
-
disposed = true;
|
|
125
|
-
clearFlushTimer();
|
|
126
|
-
real.removeListener('data', onData);
|
|
127
|
-
real.removeListener('end', onEnd);
|
|
128
|
-
real.removeListener('close', onEnd);
|
|
129
|
-
listeners.clear();
|
|
130
|
-
// I-7: a live `'data'` listener on stdin keeps the event loop alive, and
|
|
131
|
-
// `aragon` would hang on exit instead of returning to the shell.
|
|
132
|
-
real.pause?.();
|
|
133
|
-
real.unref?.();
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Build a filter, or `null` if construction throws.
|
|
139
|
-
*
|
|
140
|
-
* The `null` return is the whole point (P0-1): `enterAltScreen` derives whether
|
|
141
|
-
* to enable mouse reporting from `filter !== null`, so a construction failure
|
|
142
|
-
* degrades to "the wheel is inert" — a supported, tested rung of the fail-safe
|
|
143
|
-
* ladder — rather than leaving reporting on with no filter in front of Ink.
|
|
144
|
-
*/
|
|
145
|
-
export function tryCreateMouseFilter(real, onError) {
|
|
146
|
-
try {
|
|
147
|
-
return createMouseFilter(real);
|
|
148
|
-
}
|
|
149
|
-
catch (err) {
|
|
150
|
-
onError?.(err instanceof Error ? err.message : String(err));
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
}
|