wotann 0.5.92 → 0.5.93
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/ui/raw-mode-guard.d.ts +26 -6
- package/dist/ui/raw-mode-guard.js +33 -19
- package/package.json +1 -1
|
@@ -46,10 +46,26 @@ export interface ResolveStdinOptions {
|
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* Resolve an input stream Ink can drive `useInput()` with:
|
|
49
|
-
* 1. `
|
|
50
|
-
* 2. else
|
|
49
|
+
* 1. controlling terminal (`/dev/tty`) if raw-capable — preferred.
|
|
50
|
+
* 2. else `stdin` if it is raw-capable — fallback for CI / Windows /
|
|
51
|
+
* detached processes that have no controlling terminal.
|
|
51
52
|
* 3. else `null` — caller MUST NOT mount the interactive Ink app
|
|
52
53
|
* (it would hang); print guidance + exit instead.
|
|
54
|
+
*
|
|
55
|
+
* Why /dev/tty FIRST (changed 2026-05-28 from stdin-first):
|
|
56
|
+
* Under `npx wotann`, `npm exec`, `sudo`, and other launchers, the
|
|
57
|
+
* subprocess's `process.stdin` can pass the static shape check
|
|
58
|
+
* (`isTTY === true`, `setRawMode` is a function) yet behave oddly
|
|
59
|
+
* when Ink actually drives it — the probe's no-op `setRawMode(false)`
|
|
60
|
+
* succeeds while Ink's `setRawMode(true)` then fails, the stream is in
|
|
61
|
+
* flowing mode because some boot import attached a `data` listener,
|
|
62
|
+
* the launcher transformed the descriptor, etc. The controlling
|
|
63
|
+
* terminal is the canonical "user's keyboard" source and is
|
|
64
|
+
* unaffected by any of that — the same pattern `less`, `vim`, `sudo`,
|
|
65
|
+
* and `git rebase -i` use for stdin-needs-tty scenarios. The fallback
|
|
66
|
+
* to `stdin` covers the no-`/dev/tty` cases (CI containers, Windows
|
|
67
|
+
* without a console, detached processes).
|
|
68
|
+
*
|
|
53
69
|
* Never throws.
|
|
54
70
|
*/
|
|
55
71
|
export declare function resolveInteractiveStdin(stdin?: unknown, opts?: ResolveStdinOptions): NodeJS.ReadStream | null;
|
|
@@ -77,10 +93,14 @@ export interface StdinDiagnosis {
|
|
|
77
93
|
readonly ttyProbeResult?: string;
|
|
78
94
|
}
|
|
79
95
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
96
|
+
* Records the full state of BOTH potential input sources — /dev/tty
|
|
97
|
+
* (the preferred source after the 2026-05-28 reorder) and `stdin`
|
|
98
|
+
* (the fallback). Always walks both regardless of whether the
|
|
99
|
+
* resolver would have short-circuited, because the diagnostic is most
|
|
100
|
+
* useful when something looks wrong with one source: the user / next
|
|
101
|
+
* agent gets the full picture, not "stdin worked, didn't look at tty".
|
|
102
|
+
*
|
|
103
|
+
* Never throws.
|
|
84
104
|
*/
|
|
85
105
|
export declare function diagnoseStdin(stdin?: unknown, opts?: ResolveStdinOptions): StdinDiagnosis;
|
|
86
106
|
/**
|
|
@@ -72,24 +72,42 @@ function defaultOpenControllingTty() {
|
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Resolve an input stream Ink can drive `useInput()` with:
|
|
75
|
-
* 1. `
|
|
76
|
-
* 2. else
|
|
75
|
+
* 1. controlling terminal (`/dev/tty`) if raw-capable — preferred.
|
|
76
|
+
* 2. else `stdin` if it is raw-capable — fallback for CI / Windows /
|
|
77
|
+
* detached processes that have no controlling terminal.
|
|
77
78
|
* 3. else `null` — caller MUST NOT mount the interactive Ink app
|
|
78
79
|
* (it would hang); print guidance + exit instead.
|
|
80
|
+
*
|
|
81
|
+
* Why /dev/tty FIRST (changed 2026-05-28 from stdin-first):
|
|
82
|
+
* Under `npx wotann`, `npm exec`, `sudo`, and other launchers, the
|
|
83
|
+
* subprocess's `process.stdin` can pass the static shape check
|
|
84
|
+
* (`isTTY === true`, `setRawMode` is a function) yet behave oddly
|
|
85
|
+
* when Ink actually drives it — the probe's no-op `setRawMode(false)`
|
|
86
|
+
* succeeds while Ink's `setRawMode(true)` then fails, the stream is in
|
|
87
|
+
* flowing mode because some boot import attached a `data` listener,
|
|
88
|
+
* the launcher transformed the descriptor, etc. The controlling
|
|
89
|
+
* terminal is the canonical "user's keyboard" source and is
|
|
90
|
+
* unaffected by any of that — the same pattern `less`, `vim`, `sudo`,
|
|
91
|
+
* and `git rebase -i` use for stdin-needs-tty scenarios. The fallback
|
|
92
|
+
* to `stdin` covers the no-`/dev/tty` cases (CI containers, Windows
|
|
93
|
+
* without a console, detached processes).
|
|
94
|
+
*
|
|
79
95
|
* Never throws.
|
|
80
96
|
*/
|
|
81
97
|
export function resolveInteractiveStdin(stdin = process.stdin, opts = {}) {
|
|
82
|
-
if (isRawModeCapable(stdin))
|
|
83
|
-
return stdin;
|
|
84
98
|
const open = opts.openControllingTty ?? defaultOpenControllingTty;
|
|
85
99
|
let tty = null;
|
|
86
100
|
try {
|
|
87
101
|
tty = open();
|
|
88
102
|
}
|
|
89
103
|
catch {
|
|
90
|
-
|
|
104
|
+
tty = null;
|
|
91
105
|
}
|
|
92
|
-
|
|
106
|
+
if (tty !== null && isRawModeCapable(tty))
|
|
107
|
+
return tty;
|
|
108
|
+
if (isRawModeCapable(stdin))
|
|
109
|
+
return stdin;
|
|
110
|
+
return null;
|
|
93
111
|
}
|
|
94
112
|
/**
|
|
95
113
|
* Same observation pattern as `isRawModeCapable` but captures the
|
|
@@ -123,21 +141,16 @@ function probeStdinDetail(stream) {
|
|
|
123
141
|
}
|
|
124
142
|
}
|
|
125
143
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
144
|
+
* Records the full state of BOTH potential input sources — /dev/tty
|
|
145
|
+
* (the preferred source after the 2026-05-28 reorder) and `stdin`
|
|
146
|
+
* (the fallback). Always walks both regardless of whether the
|
|
147
|
+
* resolver would have short-circuited, because the diagnostic is most
|
|
148
|
+
* useful when something looks wrong with one source: the user / next
|
|
149
|
+
* agent gets the full picture, not "stdin worked, didn't look at tty".
|
|
150
|
+
*
|
|
151
|
+
* Never throws.
|
|
130
152
|
*/
|
|
131
153
|
export function diagnoseStdin(stdin = process.stdin, opts = {}) {
|
|
132
|
-
const stdinProbe = probeStdinDetail(stdin);
|
|
133
|
-
if (stdinProbe.probeResult === "ok") {
|
|
134
|
-
return {
|
|
135
|
-
stdinIsTTY: stdinProbe.isTTY,
|
|
136
|
-
stdinHasSetRawMode: stdinProbe.hasSetRawMode,
|
|
137
|
-
stdinProbeResult: stdinProbe.probeResult,
|
|
138
|
-
ttyOpenResult: "not-attempted",
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
154
|
const open = opts.openControllingTty ?? defaultOpenControllingTty;
|
|
142
155
|
let tty = null;
|
|
143
156
|
let ttyOpenResult;
|
|
@@ -148,6 +161,7 @@ export function diagnoseStdin(stdin = process.stdin, opts = {}) {
|
|
|
148
161
|
catch (e) {
|
|
149
162
|
ttyOpenResult = e instanceof Error ? e.message : String(e);
|
|
150
163
|
}
|
|
164
|
+
const stdinProbe = probeStdinDetail(stdin);
|
|
151
165
|
if (tty === null) {
|
|
152
166
|
return {
|
|
153
167
|
stdinIsTTY: stdinProbe.isTTY,
|