@volter/supercode-terminal 0.2.63 → 0.2.65
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/index.d.ts +1 -1
- package/index.mjs +30 -12
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -253,6 +253,6 @@ export type { TerminalInputDelivery, TmuxCommand, TmuxWindowSize };
|
|
|
253
253
|
|
|
254
254
|
/** The bounded live composer text, or null for an unrecognized screen. */
|
|
255
255
|
export function composerText(kind: string, screen: string | undefined): string | null;
|
|
256
|
-
export function restoreComposerInput(host: TmuxTerminalHost | PtyTerminalHost | PaneServerHost, sessionId: string, kind: string, text: string): Promise<{ restored: boolean; reason?: string; remaining?: string }>;
|
|
256
|
+
export function restoreComposerInput(host: TmuxTerminalHost | PtyTerminalHost | PaneServerHost, sessionId: string, kind: string, text: string, options?: { pasted?: string | null }): Promise<{ restored: boolean; reason?: string; remaining?: string }>;
|
|
257
257
|
/** Failed input is restored or returns an explicit cleanup obligation; never blindly resend. */
|
|
258
258
|
export function deliverComposerInput(host: TmuxTerminalHost | PtyTerminalHost | PaneServerHost, sessionId: string, kind: string, text: string, options?: { deadlineMs?: number; signal?: AbortSignal }): Promise<{ delivered: boolean; reason?: string; restored?: boolean; cleanup_required?: boolean; cleanup_reason?: string; cleanup_remaining?: string; delivery?: TerminalInputDelivery }>;
|
package/index.mjs
CHANGED
|
@@ -1723,9 +1723,15 @@ const COMPOSER_READY = Object.freeze({
|
|
|
1723
1723
|
* and anchoring on column 0 instead would read a drawn-in prompt as absent.
|
|
1724
1724
|
*/
|
|
1725
1725
|
function composerLine(screen, caret) {
|
|
1726
|
-
|
|
1726
|
+
// Read as text: a capture keeps its escapes (the daemon's readiness wait reads one), and Grok draws its composer
|
|
1727
|
+
// line after a reset (`ESC[0m❯`), so a caret test on the raw line never matched and Grok never read as ready
|
|
1728
|
+
// (yuerans, 2026-10-06: `open --new grok --input` dropped its brief; mail to an idle Grok waited for ever).
|
|
1729
|
+
return screen.split('\n').some((line) => caret.test(line.replace(ANSI_SEQUENCE, '').trim()));
|
|
1727
1730
|
}
|
|
1728
1731
|
|
|
1732
|
+
/** A terminal escape sequence: CSI (`ESC[…x`), OSC (`ESC]…BEL` or `ESC]…ESC\\`), or a two-character escape. */
|
|
1733
|
+
const ANSI_SEQUENCE = /\u001b\[[0-?]*[ -\/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)|\u001b[@-Z\\-_]/gu;
|
|
1734
|
+
|
|
1729
1735
|
/**
|
|
1730
1736
|
* Is the program of this kind showing its idle composer — ready to be given a
|
|
1731
1737
|
* first message — on this captured screen?
|
|
@@ -1844,22 +1850,26 @@ function composerMatches(value, wanted) {
|
|
|
1844
1850
|
}
|
|
1845
1851
|
|
|
1846
1852
|
/** Claude collapses a multi-line paste to `[Pasted text #N +K lines]`, and its composer then shows that placeholder,
|
|
1847
|
-
* never the text.
|
|
1848
|
-
* payload's with its closing newline)
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1853
|
+
* never the text. The placeholder (as shown) when it can stand for `text`: K is the pasted text's line breaks (the
|
|
1854
|
+
* trimmed text's, or the sent payload's with its closing newline). Which paste it is, #N, only the paste itself
|
|
1855
|
+
* tells: a caller holds the placeholder its own paste produced and accepts no other. */
|
|
1856
|
+
function pastePlaceholder(composer, text) {
|
|
1857
|
+
const shown=String(composer??'').trim();
|
|
1858
|
+
const placeholder=/^\[Pasted text #\d+ \+(\d+) lines?\]$/u.exec(shown);
|
|
1859
|
+
if(!placeholder)return null;
|
|
1852
1860
|
const breaks=value=>(value.match(/\n/gu)??[]).length;
|
|
1853
|
-
return [breaks(String(text).trim()),breaks(`${text}\n`)].includes(Number(placeholder[1]));
|
|
1861
|
+
return [breaks(String(text).trim()),breaks(`${text}\n`)].includes(Number(placeholder[1]))?shown:null;
|
|
1854
1862
|
}
|
|
1855
1863
|
|
|
1856
1864
|
/** Undo only text this input wrote, including when a turn started meanwhile.
|
|
1857
|
-
* Never Enter, Escape, or Ctrl-C: restoration must not submit or stop work.
|
|
1858
|
-
|
|
1865
|
+
* Never Enter, Escape, or Ctrl-C: restoration must not submit or stop work.
|
|
1866
|
+
* `pasted`: the `[Pasted text #N …]` placeholder this input's own paste produced. Only that one is cleared: another
|
|
1867
|
+
* of the same size is a person's own paste. */
|
|
1868
|
+
export async function restoreComposerInput(host, sessionId, kind, text, { pasted = null } = {}) {
|
|
1859
1869
|
const read=async()=>composerText(kind,await host.capture(sessionId,{preserveEscapes:true}));
|
|
1860
1870
|
let remaining=await read();
|
|
1861
1871
|
if(remaining==='')return {restored:true};
|
|
1862
|
-
if(!composerMatches(remaining,String(text).trim())&&!
|
|
1872
|
+
if(!composerMatches(remaining,String(text).trim())&&!(pasted&&remaining.trim()===pasted))return {restored:false,reason:'composer-changed-or-unreadable'};
|
|
1863
1873
|
const lines=remaining.split('\n').length+2;
|
|
1864
1874
|
for(let line=0;line<lines;line++) {
|
|
1865
1875
|
if(await read()!==remaining)return {restored:false,reason:'composer-changed',remaining};
|
|
@@ -1886,15 +1896,23 @@ export async function deliverComposerInput(host, sessionId, kind, text, { deadli
|
|
|
1886
1896
|
const read=async()=>composerText(kind,await capture());
|
|
1887
1897
|
const expired=()=>signal?.aborted||Date.now()>=deadlineMs;
|
|
1888
1898
|
let touched=false;
|
|
1899
|
+
// The placeholder this paste produced: the first one seen after pasting into a composer read empty twice. From then
|
|
1900
|
+
// on only that one (its #N) is this text, and only that one is restored away.
|
|
1901
|
+
let pasted=null;
|
|
1889
1902
|
const failed=async (reason, evidence={})=>{
|
|
1890
1903
|
if(!touched)return {delivered:false,reason,...evidence};
|
|
1891
1904
|
let restored;
|
|
1892
|
-
try{restored=await restoreComposerInput(host,sessionId,kind,text);}catch(error){restored={restored:false,reason:error.message};}
|
|
1905
|
+
try{restored=await restoreComposerInput(host,sessionId,kind,text,{pasted});}catch(error){restored={restored:false,reason:error.message};}
|
|
1893
1906
|
return {delivered:false,reason,...evidence,restored:restored.restored,cleanup_required:!restored.restored,cleanup_reason:restored.reason,cleanup_remaining:restored.remaining};
|
|
1894
1907
|
};
|
|
1895
1908
|
if(expired())return failed('deadline');
|
|
1896
1909
|
const wanted=String(text).trim();
|
|
1897
|
-
const holds=composer=>
|
|
1910
|
+
const holds=composer=>{
|
|
1911
|
+
if(composerMatches(composer,wanted))return true;
|
|
1912
|
+
const placeholder=pastePlaceholder(composer,text);
|
|
1913
|
+
if(placeholder&&touched&&pasted===null)pasted=placeholder;
|
|
1914
|
+
return placeholder!==null&&placeholder===pasted;
|
|
1915
|
+
};
|
|
1898
1916
|
if(!wanted||/[\x00-\x08\x0b-\x1f\x7f]/u.test(text))return failed('unsupported-text');
|
|
1899
1917
|
for(let check=0;check<2;check++) {
|
|
1900
1918
|
const screen=await capture(),before=composerText(kind,screen);
|