sigrank 0.17.3 → 0.17.5
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/README.md +1 -1
- package/package.json +1 -1
- package/tui.mjs +58 -8
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigrank",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.5",
|
|
4
4
|
"author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
|
|
5
5
|
"description": "SigRank MCP server — the yield cascade + live leaderboard as MCP tools any agent can call",
|
|
6
6
|
"mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
|
package/tui.mjs
CHANGED
|
@@ -2136,8 +2136,17 @@ export async function runTui({
|
|
|
2136
2136
|
process.stdin.resume();
|
|
2137
2137
|
process.stdin.setEncoding("utf8");
|
|
2138
2138
|
|
|
2139
|
+
// Esc-sequence buffer: some terminals (SSH, tmux, certain emulators) split an
|
|
2140
|
+
// arrow key (\x1b[C) into two data events: \x1b then [C. Without buffering, the
|
|
2141
|
+
// lone \x1b triggers the Esc handler (clearing the code buffer / leaving the tab)
|
|
2142
|
+
// before the [C arrives. We hold \x1b for 25ms; if more bytes follow, we
|
|
2143
|
+
// reassemble and deliver the full sequence. If nothing follows, it's a real Esc.
|
|
2144
|
+
let escBuf = null;
|
|
2145
|
+
let escTimer = null;
|
|
2146
|
+
const ESC_HOLD_MS = 25;
|
|
2147
|
+
|
|
2139
2148
|
await new Promise((resolve) => {
|
|
2140
|
-
|
|
2149
|
+
const handleKey = async (key) => {
|
|
2141
2150
|
const k = key.toLowerCase();
|
|
2142
2151
|
|
|
2143
2152
|
if (k === "q" || k === "\x03") {
|
|
@@ -2183,9 +2192,11 @@ export async function runTui({
|
|
|
2183
2192
|
"sign-in is temporarily unavailable — try again shortly.",
|
|
2184
2193
|
};
|
|
2185
2194
|
connectMsg = `${red("✗")} ${reasons[out.reason] || `sign-in failed (${out.reason || "unknown"}).`}`;
|
|
2195
|
+
codeBuf = ""; // auto-clear on failure — the code is consumed/invalid
|
|
2186
2196
|
}
|
|
2187
2197
|
} catch (e) {
|
|
2188
2198
|
connectMsg = `${red("✗")} ${e.message}`;
|
|
2199
|
+
codeBuf = ""; // auto-clear on error too
|
|
2189
2200
|
}
|
|
2190
2201
|
await redraw();
|
|
2191
2202
|
return;
|
|
@@ -2216,13 +2227,13 @@ export async function runTui({
|
|
|
2216
2227
|
await redraw();
|
|
2217
2228
|
return;
|
|
2218
2229
|
} // backspace
|
|
2219
|
-
// [X] sign out / reset — when the code field is empty (so
|
|
2220
|
-
// still feeds the buffer
|
|
2221
|
-
//
|
|
2222
|
-
//
|
|
2223
|
-
//
|
|
2224
|
-
//
|
|
2225
|
-
// (
|
|
2230
|
+
// [X] sign out / reset — only when the code field is empty (so 'x'/'X' typed
|
|
2231
|
+
// mid-code still feeds the buffer — codes can contain X). The auto-clear on
|
|
2232
|
+
// sign-in failure (above) ensures the buffer is empty after a failed attempt,
|
|
2233
|
+
// so X works immediately when the user wants to reset. Drops the local
|
|
2234
|
+
// credential so a server-revoked OR server-enrolled-but-locally-unbound device
|
|
2235
|
+
// stops holding a stale device_id, and the next enroll provisions a fresh
|
|
2236
|
+
// device_id (escapes the Frankenstein-identity / 409-re-enroll deadlock).
|
|
2226
2237
|
if (k === "x" && !codeBuf) {
|
|
2227
2238
|
const wasSignedIn = isSignedIn(loadIdentity());
|
|
2228
2239
|
clearIdentity();
|
|
@@ -2589,6 +2600,45 @@ export async function runTui({
|
|
|
2589
2600
|
|
|
2590
2601
|
if (switched) submitMsg = ""; // submit result is tied to the tab it was sent from
|
|
2591
2602
|
if (switched || k === "w") await redraw();
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
// Buffered stdin listener: reassembles split Esc sequences before dispatch.
|
|
2606
|
+
process.stdin.on("data", (chunk) => {
|
|
2607
|
+
// If we have a pending \x1b held from a previous chunk, combine it with
|
|
2608
|
+
// this chunk to reassemble the full escape sequence (\x1b + [C = \x1b[C).
|
|
2609
|
+
if (escBuf) {
|
|
2610
|
+
clearTimeout(escTimer);
|
|
2611
|
+
const combined = escBuf + chunk;
|
|
2612
|
+
escBuf = null;
|
|
2613
|
+
escTimer = null;
|
|
2614
|
+
// If the combined result is \x1b followed by more \x1b (rare), just
|
|
2615
|
+
// deliver the first as Esc and re-buffer the second. Otherwise deliver
|
|
2616
|
+
// the combined sequence.
|
|
2617
|
+
if (combined.length > 1) {
|
|
2618
|
+
handleKey(combined);
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
// combined is still just \x1b (chunk was empty?) → fall through to hold
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
// Fast path: chunk doesn't start with \x1b → deliver immediately.
|
|
2625
|
+
if (chunk[0] !== "\x1b") {
|
|
2626
|
+
handleKey(chunk);
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
// Chunk starts with \x1b and is longer than 1 byte → complete escape
|
|
2630
|
+
// sequence → deliver immediately.
|
|
2631
|
+
if (chunk.length > 1) {
|
|
2632
|
+
handleKey(chunk);
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2635
|
+
// Chunk is exactly \x1b → hold for ESC_HOLD_MS to see if more bytes follow.
|
|
2636
|
+
escBuf = chunk;
|
|
2637
|
+
escTimer = setTimeout(() => {
|
|
2638
|
+
handleKey(escBuf);
|
|
2639
|
+
escBuf = null;
|
|
2640
|
+
escTimer = null;
|
|
2641
|
+
}, ESC_HOLD_MS);
|
|
2592
2642
|
});
|
|
2593
2643
|
});
|
|
2594
2644
|
|