@tiens.nguyen/gonext-local-worker 1.0.262 → 1.0.264
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/gonext-repl.mjs +66 -38
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -48,6 +48,9 @@ const white = (s) => `\x1b[37m${s}\x1b[0m`;
|
|
|
48
48
|
// code 34 (blue) which many dark-theme palettes render as purple/indigo instead.
|
|
49
49
|
const codeColor = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
50
50
|
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
51
|
+
// Hover highlight for the clickable thinking line (task #96): bold + underline + bright
|
|
52
|
+
// green so it reads as an interactive "link" the moment the mouse is over it.
|
|
53
|
+
const hoverThought = (s) => `\x1b[1;4;92m${s}\x1b[0m`;
|
|
51
54
|
|
|
52
55
|
// The worker's "still thinking" heartbeat lines look like "Caffeinating… (90s)" — we
|
|
53
56
|
// SWALLOW them from the terminal (the local ticker below drives progress instead), and
|
|
@@ -383,13 +386,15 @@ rl.on("close", () => {
|
|
|
383
386
|
w(null);
|
|
384
387
|
}
|
|
385
388
|
});
|
|
386
|
-
// Mouse click
|
|
387
|
-
//
|
|
388
|
-
// accepted: with tracking on, the terminal's
|
|
389
|
-
// user holds Option/Shift to select text
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
389
|
+
// Mouse hover+click on the thinking line (task #96): while a turn runs we turn ON xterm
|
|
390
|
+
// ANY-MOTION tracking so we can both HIGHLIGHT the line as the mouse hovers it AND expand it
|
|
391
|
+
// on click (see runAgentTurn). Trade-off the user accepted: with tracking on, the terminal's
|
|
392
|
+
// own drag-to-select/copy is captured by us — the user holds Option/Shift to select text
|
|
393
|
+
// meanwhile. ?1003 = report every button press/release AND all mouse MOTION (needed for hover
|
|
394
|
+
// with no button held); ?1006 = SGR extended coords (no 223-col cap). We disable ?1000/?1002
|
|
395
|
+
// too on teardown in case a terminal had them latched.
|
|
396
|
+
const MOUSE_ON = "\x1b[?1003h\x1b[?1006h";
|
|
397
|
+
const MOUSE_OFF = "\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?1006l";
|
|
393
398
|
let _mouseEnabled = false;
|
|
394
399
|
const enableMouse = () => {
|
|
395
400
|
if (_mouseEnabled || !process.stdin.isTTY) return;
|
|
@@ -404,9 +409,15 @@ const disableMouse = () => {
|
|
|
404
409
|
// Safety net: never leave the user's terminal in mouse-reporting mode (would break their
|
|
405
410
|
// shell's selection) if we exit ungracefully.
|
|
406
411
|
process.on("exit", disableMouse);
|
|
407
|
-
// SGR mouse report: ESC [ < btn ; col ; row (M=press, m=release).
|
|
408
|
-
//
|
|
412
|
+
// SGR mouse report: ESC [ < btn ; col ; row (M=press, m=release). btn bit 32 = MOTION
|
|
413
|
+
// (hover/drag), bit 64 = wheel; low 2 bits = which button (0=left). A left CLICK is a press
|
|
414
|
+
// (M) with motion+wheel bits clear and low bits 0; a HOVER is any event with the motion bit.
|
|
409
415
|
const MOUSE_RE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
416
|
+
// Cursor Position Report reply to our `\x1b[6n` probe: ESC [ row ; col R. We probe right after
|
|
417
|
+
// clearing the status (cursor is then at the status block's TOP row) to learn the thought
|
|
418
|
+
// line's ABSOLUTE viewport row — so hover/click hit-testing is exact even when the block isn't
|
|
419
|
+
// at the bottom of the screen (task #96 fix). No `<`, so it never collides with MOUSE_RE.
|
|
420
|
+
const DSR_RE = /\x1b\[(\d+);(\d+)R/g;
|
|
410
421
|
const nextLine = () =>
|
|
411
422
|
_lineQueue.length > 0
|
|
412
423
|
? Promise.resolve(_lineQueue.shift())
|
|
@@ -1213,6 +1224,9 @@ async function runAgentTurn(history) {
|
|
|
1213
1224
|
// whether the user has clicked to expand it. Both reset when the step's Thought changes.
|
|
1214
1225
|
let fullLiveThought = "";
|
|
1215
1226
|
let thoughtExpanded = false;
|
|
1227
|
+
let hoverRow = -1; // last mouse viewport row (from motion events) → highlight when it's on the thought line
|
|
1228
|
+
let thoughtRow = -1; // absolute viewport row of the thought line (line 1), learned via \x1b[6n
|
|
1229
|
+
let lastDsrAt = 0; // throttle the cursor-position probe
|
|
1216
1230
|
const thinkSecs = () => Math.max(1, Math.round((Date.now() - thinkingSince) / 1000));
|
|
1217
1231
|
|
|
1218
1232
|
// The status is normally TWO in-place lines — the in-progress action, then the playful
|
|
@@ -1267,28 +1281,33 @@ async function runAgentTurn(history) {
|
|
|
1267
1281
|
: liveThought || (almostDone ? "Almost done" : "Thinking");
|
|
1268
1282
|
const max = Math.max(24, (process.stdout.columns || 80) - 14);
|
|
1269
1283
|
const fit = (s) => (s.length > max ? s.slice(0, max - 1) + "…" : s);
|
|
1270
|
-
clearStatus();
|
|
1271
|
-
// Line 1: green ● + green phase/seconds (matches the web's green streaming label).
|
|
1272
|
-
// Line 2 (indented): the SPINNER + the playful word together, both in the cycling
|
|
1273
|
-
// color — the spinner belongs with the word (dot on the label line, spinner leading
|
|
1274
|
-
// the flavor line).
|
|
1275
|
-
// Task #83/#84: after the playful word, show the running agent-code-model token
|
|
1276
|
-
// count (dim) plus the per-workspace PEAK single-request count (orange), e.g.
|
|
1277
|
-
// " ⠙ Caffeinating… · 12.3k tok · 8.1k max". Both start at 0 and climb.
|
|
1278
1284
|
const tokLabel =
|
|
1279
1285
|
dim(` · ${fmtTokens(latestCodeTokens)} tok`) +
|
|
1280
1286
|
orange(` · ${fmtTokens(sessionMaxCodeTokens)} max`);
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
//
|
|
1286
|
-
//
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1287
|
+
// Task #96: the thought line is "clickable" only when there's a fuller thought to reveal
|
|
1288
|
+
// (a live command / bare "Thinking" has nothing to expand). When clicked, the fuller
|
|
1289
|
+
// thought unfolds as up to 4 wrapped GREY lines BETWEEN the thought and the playful-word
|
|
1290
|
+
// line. Hover-highlight only the THOUGHT line (line 1) — its exact viewport row comes from
|
|
1291
|
+
// the \x1b[6n probe below (`thoughtRow`); we fall back to the viewport-bottom assumption
|
|
1292
|
+
// only when the terminal never answered the probe.
|
|
1293
|
+
const clickable = !!(fullLiveThought && !runningCmd);
|
|
1294
|
+
const expLines = thoughtExpanded && clickable ? wrapThought(fullLiveThought, max, 4) : [];
|
|
1295
|
+
const drawn = 2 + expLines.length;
|
|
1296
|
+
const rows = process.stdout.rows || 24;
|
|
1297
|
+
const tRow = thoughtRow >= 1 ? thoughtRow : rows - drawn + 1;
|
|
1298
|
+
const hovering = clickable && hoverRow === tRow;
|
|
1299
|
+
const label = `${fit(primary)}… (${secs}s)`;
|
|
1300
|
+
clearStatus();
|
|
1301
|
+
// Right after clearing, the cursor sits at the status block's TOP-LEFT = the thought line's
|
|
1302
|
+
// row. Probe for it (throttled) so hover/click hit-testing is exact wherever the block is.
|
|
1303
|
+
if (process.stdin.isTTY && now - lastDsrAt > 400) { lastDsrAt = now; process.stdout.write("\x1b[6n"); }
|
|
1304
|
+
// Line 1: green ● + green phase/seconds (matches the web's green streaming label), or the
|
|
1305
|
+
// underlined bright-green hover style when the mouse is over a clickable thought. Then the
|
|
1306
|
+
// GREY expanded lines (if any), then LAST the SPINNER + playful word + token labels
|
|
1307
|
+
// (task #83/#84), e.g. " ⠙ Caffeinating… · 12.3k tok · 8.1k max".
|
|
1308
|
+
let out = `${green(BULLET)} ${hovering ? hoverThought(label) : green(label)}`;
|
|
1309
|
+
for (const wl of expLines) out += "\n " + dim(wl);
|
|
1310
|
+
out += "\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`;
|
|
1292
1311
|
process.stdout.write(out);
|
|
1293
1312
|
statusLines = drawn;
|
|
1294
1313
|
statusShown = true;
|
|
@@ -1297,17 +1316,20 @@ async function runAgentTurn(history) {
|
|
|
1297
1316
|
// seconds still tick once/sec). Only redraws when the model has gone quiet >QUIET_MS.
|
|
1298
1317
|
const ticker = setInterval(tick, 120);
|
|
1299
1318
|
|
|
1300
|
-
// ---
|
|
1301
|
-
//
|
|
1302
|
-
//
|
|
1303
|
-
//
|
|
1304
|
-
//
|
|
1305
|
-
//
|
|
1319
|
+
// --- Hover + click on the thinking line (task #96) -------------------------------------
|
|
1320
|
+
// HOVER: motion events keep `hoverRow`; the tick highlights the thought line (underlined
|
|
1321
|
+
// bright-green "link") whenever the mouse is on its row. CLICK: a left-click on the status
|
|
1322
|
+
// block toggles the fuller thought (3-4 grey lines). The block sits at the bottom of the
|
|
1323
|
+
// viewport (new output keeps scrolling it there), so we hit-test rows against the bottom
|
|
1324
|
+
// `statusLines` rows. Mouse reporting is on only for the turn and always turned back off in
|
|
1325
|
+
// the finally + on process exit, so the user's shell is never left in mouse-capture mode.
|
|
1306
1326
|
const onStatusClick = (row) => {
|
|
1307
1327
|
if (!statusShown) return;
|
|
1308
1328
|
const rows = process.stdout.rows || 24;
|
|
1309
|
-
const top = rows - statusLines + 1; //
|
|
1310
|
-
|
|
1329
|
+
const top = thoughtRow >= 1 ? thoughtRow : rows - statusLines + 1; // thought line row
|
|
1330
|
+
// Toggle only on the thought line + its grey expansion — NOT the last row (the playful
|
|
1331
|
+
// word / token line), so a click there doesn't expand what it isn't part of.
|
|
1332
|
+
if (row < top || row > top + statusLines - 2) return;
|
|
1311
1333
|
// Only meaningful when there IS a fuller thought to reveal; otherwise ignore the click
|
|
1312
1334
|
// so we don't flicker an empty expand.
|
|
1313
1335
|
if (!thoughtExpanded && !(fullLiveThought && !runningCmd)) return;
|
|
@@ -1317,14 +1339,19 @@ async function runAgentTurn(history) {
|
|
|
1317
1339
|
const onMouseData = (chunk) => {
|
|
1318
1340
|
if (!following) return;
|
|
1319
1341
|
const s = chunk.toString("latin1");
|
|
1320
|
-
if (s.indexOf("\x1b[
|
|
1342
|
+
if (s.indexOf("\x1b[") === -1) return; // fast reject: no CSI (mouse report OR cursor reply) here
|
|
1343
|
+
// Cursor-position reply → the thought line's absolute row (see the \x1b[6n probe in tick).
|
|
1344
|
+
DSR_RE.lastIndex = 0;
|
|
1345
|
+
let d;
|
|
1346
|
+
while ((d = DSR_RE.exec(s))) thoughtRow = parseInt(d[1], 10);
|
|
1321
1347
|
MOUSE_RE.lastIndex = 0;
|
|
1322
1348
|
let m;
|
|
1323
1349
|
while ((m = MOUSE_RE.exec(s))) {
|
|
1324
1350
|
const btn = parseInt(m[1], 10);
|
|
1325
1351
|
const row = parseInt(m[3], 10);
|
|
1326
1352
|
const isPress = m[4] === "M";
|
|
1327
|
-
if (
|
|
1353
|
+
if (btn & 32) { hoverRow = row; continue; } // MOTION (hover/drag) → track for the highlight
|
|
1354
|
+
if (isPress && (btn & 0b11) === 0 && (btn & 64) === 0) onStatusClick(row); // left click → expand
|
|
1328
1355
|
}
|
|
1329
1356
|
};
|
|
1330
1357
|
if (process.stdin.isTTY) process.stdin.on("data", onMouseData);
|
|
@@ -1343,6 +1370,7 @@ async function runAgentTurn(history) {
|
|
|
1343
1370
|
fenceThought = "";
|
|
1344
1371
|
fullLiveThought = ""; // #96: the fuller thought is spent too…
|
|
1345
1372
|
thoughtExpanded = false; // …and each new thinking phase starts collapsed.
|
|
1373
|
+
lastDsrAt = 0; // …content just scrolled → re-probe the thought row next tick.
|
|
1346
1374
|
lastContentAt = Date.now();
|
|
1347
1375
|
};
|
|
1348
1376
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.264",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|