@tiens.nguyen/gonext-local-worker 1.0.187 → 1.0.188
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 -3
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -53,6 +53,11 @@ const isCodeCloseLine = (line) => line.trim() === "</code>";
|
|
|
53
53
|
// "unzip_file(...) | → Unzipped 256 files…") duplicates what's already visible in the
|
|
54
54
|
// gonext-local-worker daemon's own terminal — swallow it here to keep the REPL terse.
|
|
55
55
|
const isToolStepSummary = (line) => / \| → /.test(line);
|
|
56
|
+
// The router's internal play-by-play ("Routing your request…", "→ Agent mode (needs
|
|
57
|
+
// tools)", "→ Chat reply", "Choosing a tool…") is implementation detail, not something a
|
|
58
|
+
// user needs — also visible in the daemon's own terminal. Swallowed; see routingAnnounced.
|
|
59
|
+
const isRoutingLine = (line) =>
|
|
60
|
+
/^(Routing your request…|Choosing a tool…|→ Agent mode.*|→ Chat reply)$/.test(line.trim());
|
|
56
61
|
|
|
57
62
|
// Playful words for the local "thinking…" ticker, reused verbatim from the worker's
|
|
58
63
|
// thinking_words.txt sibling (falls back to a tiny built-in list if it's missing).
|
|
@@ -106,7 +111,17 @@ if (!workerKey || !apiBase) {
|
|
|
106
111
|
// awaits (workspace prompt fetch, payload probe) are queued instead of lost — vital
|
|
107
112
|
// for piped/scripted input, harmless for interactive TTYs. Both ask() and the main
|
|
108
113
|
// loop consume from this queue (never rl.question, to avoid double-capture).
|
|
109
|
-
|
|
114
|
+
//
|
|
115
|
+
// terminal: false — we draw our OWN prompt (cyan ">> ") and colorize output ourselves;
|
|
116
|
+
// readline's default terminal:true (auto-enabled whenever output is a TTY) makes it ALSO
|
|
117
|
+
// track cursor position and redraw the input line on every keystroke using ITS OWN
|
|
118
|
+
// (empty, since we never call rl.setPrompt/rl.prompt) prompt — that tracking goes stale
|
|
119
|
+
// the moment we write anything readline doesn't know about (which is everything we print
|
|
120
|
+
// between prompts), and its next redraw emits a stray leading "[" (a malformed/partial
|
|
121
|
+
// cursor-position escape) right before typed input, e.g. "[>> hi". With terminal:false,
|
|
122
|
+
// readline stops managing the line/cursor itself; the OS's own canonical-mode terminal
|
|
123
|
+
// still echoes keystrokes normally, and 'line' events fire exactly the same.
|
|
124
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: false });
|
|
110
125
|
const _lineQueue = [];
|
|
111
126
|
let _lineWaiter = null;
|
|
112
127
|
let _stdinClosed = false;
|
|
@@ -256,6 +271,7 @@ async function runAgentTurn(history) {
|
|
|
256
271
|
let statusShown = false; // a transient status line is currently on screen
|
|
257
272
|
let warnedPending = false;
|
|
258
273
|
let jobRunning = false; // only tick "thinking…" once the worker has claimed the job
|
|
274
|
+
let routingAnnounced = false; // one friendly line replaces the whole routing play-by-play
|
|
259
275
|
|
|
260
276
|
// --- Live "thinking…" ticker ---------------------------------------------------
|
|
261
277
|
// Between visible tokens (prompt-eval / awaiting the first token) we show ONE in-place
|
|
@@ -326,6 +342,7 @@ async function runAgentTurn(history) {
|
|
|
326
342
|
// them prints in blue (not dim, so it stands out from "Thought:" prose); blank lines
|
|
327
343
|
// keep spacing; everything else commits dim to scrollback.
|
|
328
344
|
let inCode = false;
|
|
345
|
+
let lastWasBlank = true; // suppresses a leading blank line and collapses repeats
|
|
329
346
|
const consume = (chunk) => {
|
|
330
347
|
carry += stripThinkTags(chunk);
|
|
331
348
|
let nl;
|
|
@@ -334,6 +351,19 @@ async function runAgentTurn(history) {
|
|
|
334
351
|
carry = carry.slice(nl + 1);
|
|
335
352
|
if (isHeartbeatLine(line) || isFenceLine(line)) continue; // swallow, no trace
|
|
336
353
|
if (isToolStepSummary(line)) { lastContentAt = Date.now(); continue; } // swallow
|
|
354
|
+
if (isRoutingLine(line)) {
|
|
355
|
+
// Replace the whole routing play-by-play with ONE friendly line, shown once
|
|
356
|
+
// per turn — the raw labels are implementation detail (visible in the daemon's
|
|
357
|
+
// own terminal if you need them).
|
|
358
|
+
if (!routingAnnounced) {
|
|
359
|
+
routingAnnounced = true;
|
|
360
|
+
onContent();
|
|
361
|
+
process.stdout.write(dim(`${pickWord()}…`) + "\n");
|
|
362
|
+
lastWasBlank = false;
|
|
363
|
+
}
|
|
364
|
+
lastContentAt = Date.now();
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
337
367
|
if (!inCode && isCodeOpenLine(line)) {
|
|
338
368
|
stampIfThinking(); // hold/refresh the stamp; the code body commits it below
|
|
339
369
|
inCode = true;
|
|
@@ -344,14 +374,21 @@ async function runAgentTurn(history) {
|
|
|
344
374
|
if (line.trim() === "") { lastContentAt = Date.now(); continue; } // skip blank lines in code
|
|
345
375
|
onContent();
|
|
346
376
|
process.stdout.write(blue(line) + "\n");
|
|
377
|
+
lastWasBlank = false;
|
|
347
378
|
continue;
|
|
348
379
|
}
|
|
349
380
|
if (line.trim() === "") {
|
|
350
|
-
|
|
381
|
+
// Several sources (swallowed routing lines, swallowed step summaries) each leave
|
|
382
|
+
// their own blank-line companion behind — collapse consecutive blanks into one.
|
|
383
|
+
if (!thinking && !statusShown && !lastWasBlank) {
|
|
384
|
+
process.stdout.write("\n");
|
|
385
|
+
lastWasBlank = true;
|
|
386
|
+
}
|
|
351
387
|
continue;
|
|
352
388
|
}
|
|
353
389
|
onContent();
|
|
354
390
|
process.stdout.write(dim(line) + "\n");
|
|
391
|
+
lastWasBlank = false;
|
|
355
392
|
}
|
|
356
393
|
// A non-empty remainder is a partial CONTENT line (heartbeats always arrive whole with
|
|
357
394
|
// a newline), so tokens are actively flowing — keep the ticker asleep.
|
|
@@ -430,7 +467,10 @@ async function main() {
|
|
|
430
467
|
console.log(dim("Ask about this repo, or /help. Ctrl-C aborts a running turn.\n"));
|
|
431
468
|
|
|
432
469
|
const history = [];
|
|
433
|
-
|
|
470
|
+
// With terminal:false, Ctrl-C is no longer intercepted by readline's raw-mode byte
|
|
471
|
+
// scanning (rl.on("SIGINT", …) would never fire) — it now delivers a real OS SIGINT to
|
|
472
|
+
// the process instead, which we catch directly here. Same behavior as before.
|
|
473
|
+
process.on("SIGINT", () => {
|
|
434
474
|
if (following) {
|
|
435
475
|
followAborted = true;
|
|
436
476
|
process.stdout.write(red("\n^C "));
|
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.188",
|
|
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",
|