@tiens.nguyen/gonext-local-worker 1.0.203 → 1.0.204
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 +43 -26
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -43,11 +43,11 @@ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
|
43
43
|
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
44
44
|
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
45
45
|
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
46
|
+
const white = (s) => `\x1b[37m${s}\x1b[0m`;
|
|
46
47
|
// ANSI 90 ("bright black") — a reliable neutral grey across terminal themes, unlike
|
|
47
48
|
// code 34 (blue) which many dark-theme palettes render as purple/indigo instead.
|
|
48
49
|
const codeColor = (s) => `\x1b[90m${s}\x1b[0m`;
|
|
49
50
|
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
50
|
-
const CLEAR_LINE = "\r\x1b[K"; // carriage-return + erase-to-end-of-line
|
|
51
51
|
|
|
52
52
|
// The worker's "still thinking" heartbeat lines look like "Caffeinating… (90s)" — we
|
|
53
53
|
// SWALLOW them from the terminal (the local ticker below drives progress instead), and
|
|
@@ -400,7 +400,6 @@ async function runAgentTurn(history) {
|
|
|
400
400
|
let warnedPending = false;
|
|
401
401
|
let jobRunning = false; // only tick "thinking…" once the worker has claimed the job
|
|
402
402
|
let answerShownLive = false; // plain-reply content already printed — don't re-print it
|
|
403
|
-
let routingAnnounced = false; // one friendly line replaces the whole routing play-by-play
|
|
404
403
|
// True once we've SPECIFICALLY seen "→ Chat reply" (the plain-reply/trivial-chat
|
|
405
404
|
// branch), as opposed to "→ Agent mode…" (the tool-use branch). Only the plain-reply
|
|
406
405
|
// path streams its answer via answer_stream with NO heartbeat/tool-summary/code lines
|
|
@@ -419,11 +418,18 @@ async function runAgentTurn(history) {
|
|
|
419
418
|
let lastContentAt = Date.now();
|
|
420
419
|
let thinking = false;
|
|
421
420
|
let thinkingSince = 0;
|
|
421
|
+
let thinkWord = pickWord(); // playful word shown UNDER the in-progress line (flavor)
|
|
422
422
|
let almostDone = false; // set from the worker's "…almost completed thinking…" heartbeat
|
|
423
|
+
let runningCmd = null; // the command currently executing (from a "Running → …" event)
|
|
423
424
|
const thinkSecs = () => Math.max(1, Math.round((Date.now() - thinkingSince) / 1000));
|
|
424
425
|
|
|
426
|
+
// The status is TWO in-place lines — the in-progress action, then the playful word
|
|
427
|
+
// under it. Cursor sits at the end of line 2, so clearing = wipe line 2, move up, wipe
|
|
428
|
+
// line 1, leaving the cursor back at the status's origin for a redraw or real content.
|
|
425
429
|
const clearStatus = () => {
|
|
426
|
-
if (statusShown)
|
|
430
|
+
if (!statusShown) return;
|
|
431
|
+
process.stdout.write("\r\x1b[K\x1b[1A\r\x1b[K");
|
|
432
|
+
statusShown = false;
|
|
427
433
|
};
|
|
428
434
|
|
|
429
435
|
const tick = () => {
|
|
@@ -433,22 +439,33 @@ async function runAgentTurn(history) {
|
|
|
433
439
|
// done — overwriting the ticker there corrupts the visible answer mid-word. Stay
|
|
434
440
|
// silent once any of THIS line has already been shown (see carryFlushedLen).
|
|
435
441
|
if (plainReplyFlow && carryFlushedLen > 0) return;
|
|
436
|
-
// Count from when
|
|
437
|
-
if (!thinking) { thinking = true; thinkingSince = lastContentAt; }
|
|
442
|
+
// Count from when this phase went quiet, so the seconds reflect the current wait/run.
|
|
443
|
+
if (!thinking) { thinking = true; thinkingSince = lastContentAt; thinkWord = pickWord(); }
|
|
438
444
|
const secs = thinkSecs();
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
445
|
+
if (secs % 12 === 0) thinkWord = pickWord(); // rotate the word on long waits
|
|
446
|
+
const glyph = SPINNER[secs % SPINNER.length]; // rotates → reads as "in progress"
|
|
447
|
+
// Line 1 = WHAT is happening now: the running command, else the phase (Thinking /
|
|
448
|
+
// Almost done). Line 2 = the playful word (flavor). Truncate so neither wraps.
|
|
449
|
+
const primary = runningCmd
|
|
450
|
+
? `Running ${runningCmd}`
|
|
451
|
+
: almostDone ? "Almost done" : "Thinking";
|
|
452
|
+
const max = Math.max(24, (process.stdout.columns || 80) - 14);
|
|
453
|
+
const fit = (s) => (s.length > max ? s.slice(0, max - 1) + "…" : s);
|
|
454
|
+
clearStatus();
|
|
455
|
+
process.stdout.write(
|
|
456
|
+
yellow(`${glyph} ${fit(primary)}… (${secs}s)`) + "\n" + dim(` ${thinkWord}…`)
|
|
457
|
+
);
|
|
442
458
|
statusShown = true;
|
|
443
459
|
};
|
|
444
460
|
const ticker = setInterval(tick, 1000);
|
|
445
461
|
|
|
446
462
|
// Called right before any real (bullet/edit-card/answer) content prints: drop the
|
|
447
|
-
//
|
|
448
|
-
//
|
|
463
|
+
// status lines so content lands cleanly, and end the current phase (so the next phase's
|
|
464
|
+
// seconds count fresh, and any running command is considered finished).
|
|
449
465
|
const onContent = () => {
|
|
450
466
|
clearStatus();
|
|
451
|
-
|
|
467
|
+
thinking = false;
|
|
468
|
+
runningCmd = null;
|
|
452
469
|
lastContentAt = Date.now();
|
|
453
470
|
};
|
|
454
471
|
|
|
@@ -494,17 +511,10 @@ async function runAgentTurn(history) {
|
|
|
494
511
|
if (inStreamFence) continue;
|
|
495
512
|
if (isToolStepSummary(line)) { lastContentAt = Date.now(); continue; } // swallow
|
|
496
513
|
if (isRoutingLine(line)) {
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
//
|
|
514
|
+
// Swallow the router's play-by-play entirely — the blinking-bullet ticker (which
|
|
515
|
+
// already shows the playful word + seconds) is the single in-progress indicator,
|
|
516
|
+
// so a separate static "Booting the big brain…" line here would just duplicate it.
|
|
500
517
|
if (line.trim() === "→ Chat reply") plainReplyFlow = true;
|
|
501
|
-
if (!routingAnnounced) {
|
|
502
|
-
routingAnnounced = true;
|
|
503
|
-
onContent();
|
|
504
|
-
process.stdout.write(dim(`${pickWord()}…`) + "\n");
|
|
505
|
-
lastWasBlank = false;
|
|
506
|
-
}
|
|
507
|
-
lastContentAt = Date.now();
|
|
508
518
|
continue;
|
|
509
519
|
}
|
|
510
520
|
// Edit card (a file change made by the agent's edit tools) — rendered as a
|
|
@@ -593,6 +603,14 @@ async function runAgentTurn(history) {
|
|
|
593
603
|
}
|
|
594
604
|
continue;
|
|
595
605
|
}
|
|
606
|
+
// Agent flow: a "Running → <cmd>" is the START of a command — show it as the live
|
|
607
|
+
// in-progress status (the blinking bullet names it) rather than a solid bullet; its
|
|
608
|
+
// completion ("Command passed/failed → …" / "Server up → …") prints the done bullet.
|
|
609
|
+
// So the pair collapses to: "◑ Running <cmd>…" while it runs, then "● <result>".
|
|
610
|
+
if (!plainReplyFlow) {
|
|
611
|
+
const mRun = /^Running → (.+)$/.exec(line.trim());
|
|
612
|
+
if (mRun) { runningCmd = mRun[1]; thinking = false; lastContentAt = Date.now(); continue; }
|
|
613
|
+
}
|
|
596
614
|
if (alreadyFlushed === 0) onContent();
|
|
597
615
|
if (plainReplyFlow) {
|
|
598
616
|
// Plain-reply content IS the answer — print it in the default color and remember
|
|
@@ -600,11 +618,10 @@ async function runAgentTurn(history) {
|
|
|
600
618
|
process.stdout.write(line.slice(alreadyFlushed) + "\n");
|
|
601
619
|
answerShownLive = true;
|
|
602
620
|
} else {
|
|
603
|
-
//
|
|
604
|
-
//
|
|
605
|
-
//
|
|
606
|
-
|
|
607
|
-
process.stdout.write(green(BULLET) + " " + line.trim() + "\n");
|
|
621
|
+
// The remaining out-of-fence ACTION events ("Command passed → …", "Searching code
|
|
622
|
+
// → …", "Server up → …", "Composing answer…") each print as ONE solid bullet —
|
|
623
|
+
// the clean, summarized action list (task #41).
|
|
624
|
+
process.stdout.write(white(BULLET) + " " + line.trim() + "\n");
|
|
608
625
|
}
|
|
609
626
|
lastWasBlank = false;
|
|
610
627
|
}
|
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.204",
|
|
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",
|