zames_pro 2.16.0 → 2.18.0
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 +40 -1
- package/dist/agent-loop.js +22 -6
- package/dist/browser.js +828 -29
- package/dist/commands.js +73 -0
- package/dist/config-menu.js +4 -0
- package/dist/config.js +11 -1
- package/dist/i18n.js +119 -1
- package/dist/index.js +111 -5
- package/dist/input.js +51 -9
- package/dist/spinner.js +39 -6
- package/package.json +2 -2
package/dist/input.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { theme } from './theme.js';
|
|
2
2
|
import { renderMarkdown } from './markdown.js';
|
|
3
3
|
import { randomThinkingPhrase, stripEllipsis } from './spinner.js';
|
|
4
|
+
import { translate } from './i18n.js';
|
|
4
5
|
import { AttachmentStore, parseImagePaste, looksLikeFilePath, } from './attachments.js';
|
|
5
6
|
// A permanent input line at the bottom of the terminal + a status/output area above it.
|
|
6
7
|
//
|
|
@@ -70,7 +71,6 @@ export function expandPastes(pastes, text) {
|
|
|
70
71
|
// We align the width to the maximum (3) so the hint doesn't shift.
|
|
71
72
|
const DOTS = ['', '.', '..', '...'];
|
|
72
73
|
const DOTS_PAD = ' ';
|
|
73
|
-
const HINT = theme.dim(' · Esc — стоп');
|
|
74
74
|
function safeJson(v) {
|
|
75
75
|
try {
|
|
76
76
|
return JSON.stringify(v);
|
|
@@ -184,6 +184,9 @@ export class LineEditor {
|
|
|
184
184
|
onCtrlC;
|
|
185
185
|
_inPaste;
|
|
186
186
|
_dotTimer;
|
|
187
|
+
// True while the dot-animation timer is running. Lets sendPause() update
|
|
188
|
+
// only the base text (remaining seconds) without restarting the timer.
|
|
189
|
+
_animating;
|
|
187
190
|
_dotPhase;
|
|
188
191
|
_thinkBase;
|
|
189
192
|
_wasRaw;
|
|
@@ -212,7 +215,11 @@ export class LineEditor {
|
|
|
212
215
|
// user could type and send a message mid-operation, and it would be queued
|
|
213
216
|
// and sent right after the operation, breaking the restored session.
|
|
214
217
|
locked;
|
|
215
|
-
|
|
218
|
+
// Interface language for the editor's own labels (hint, answer marker,
|
|
219
|
+
// pause status). Everything the OPERATOR sees must be localized.
|
|
220
|
+
locale;
|
|
221
|
+
constructor({ prompt = '> ', commands = [], locale = 'ru' } = {}) {
|
|
222
|
+
this.locale = locale;
|
|
216
223
|
this.promptStr = prompt;
|
|
217
224
|
this.buf = '';
|
|
218
225
|
this.cursor = 0;
|
|
@@ -226,6 +233,7 @@ export class LineEditor {
|
|
|
226
233
|
this.onCtrlC = null;
|
|
227
234
|
this._inPaste = false;
|
|
228
235
|
this._dotTimer = null;
|
|
236
|
+
this._animating = false;
|
|
229
237
|
this._dotPhase = 0;
|
|
230
238
|
this._thinkBase = '';
|
|
231
239
|
this._wasRaw = false;
|
|
@@ -362,6 +370,11 @@ export class LineEditor {
|
|
|
362
370
|
this.promptStr = str;
|
|
363
371
|
this._render();
|
|
364
372
|
}
|
|
373
|
+
// Update the interface language (labels: hint, answer marker, pause).
|
|
374
|
+
setLocale(locale) {
|
|
375
|
+
this.locale = locale;
|
|
376
|
+
this._render();
|
|
377
|
+
}
|
|
365
378
|
// Update slash-command descriptions (interface language change).
|
|
366
379
|
setCommands(commands) {
|
|
367
380
|
this.slashCommands = commands;
|
|
@@ -412,7 +425,7 @@ export class LineEditor {
|
|
|
412
425
|
out += NL + lines.join(NL);
|
|
413
426
|
const hidden = sugg.length - shown.length;
|
|
414
427
|
if (hidden > 0)
|
|
415
|
-
out += NL + theme.dim('
|
|
428
|
+
out += NL + theme.dim(' ' + translate(this.locale)('editor.more', { n: hidden }));
|
|
416
429
|
}
|
|
417
430
|
process.stdout.write(out);
|
|
418
431
|
const lastRow = lay.rows.length - 1;
|
|
@@ -449,36 +462,65 @@ export class LineEditor {
|
|
|
449
462
|
clearInterval(this._dotTimer);
|
|
450
463
|
this._dotTimer = null;
|
|
451
464
|
}
|
|
465
|
+
this._animating = false;
|
|
452
466
|
}
|
|
453
467
|
_dots(n) {
|
|
454
468
|
// The dots are the same color as the base and of fixed width — otherwise
|
|
455
469
|
// the hint on the right "jumps" when the animation phase changes.
|
|
456
470
|
return theme.brown(DOTS[n] + DOTS_PAD.slice(DOTS[n].length));
|
|
457
471
|
}
|
|
472
|
+
// Status-line hint ("Esc — stop"), localized.
|
|
473
|
+
_hint() {
|
|
474
|
+
return theme.dim(' · ' + translate(this.locale)('spinner.hint'));
|
|
475
|
+
}
|
|
458
476
|
_startThinking() {
|
|
459
477
|
if (this.pendingText) {
|
|
460
|
-
this.setStatus(theme.prompt('✎ ') + this.pendingText +
|
|
478
|
+
this.setStatus(theme.prompt('✎ ') + this.pendingText + this._hint());
|
|
461
479
|
return;
|
|
462
480
|
}
|
|
463
|
-
this.
|
|
481
|
+
this._startAnimated(randomThinkingPhrase());
|
|
482
|
+
}
|
|
483
|
+
// Animated status: a brown base text plus a growing "running" dot sequence.
|
|
484
|
+
// Shared by the thinking spinner and the send-pause indicator, so the pause
|
|
485
|
+
// is animated too (previously the pause was a static console line and the
|
|
486
|
+
// dots stayed frozen).
|
|
487
|
+
_startAnimated(baseText) {
|
|
488
|
+
this._thinkBase = theme.brown(stripEllipsis(baseText));
|
|
464
489
|
this._dotPhase = 0;
|
|
465
|
-
this.setStatus(this._thinkBase + this._dots(0) +
|
|
490
|
+
this.setStatus(this._thinkBase + this._dots(0) + this._hint());
|
|
466
491
|
this._stopDots();
|
|
467
492
|
this._dotTimer = setInterval(() => {
|
|
468
493
|
this._dotPhase = (this._dotPhase + 1) % DOTS.length;
|
|
469
|
-
this.setStatus(this._thinkBase + this._dots(this._dotPhase) +
|
|
494
|
+
this.setStatus(this._thinkBase + this._dots(this._dotPhase) + this._hint());
|
|
470
495
|
}, 400);
|
|
496
|
+
this._animating = true;
|
|
471
497
|
if (this._dotTimer.unref)
|
|
472
498
|
this._dotTimer.unref();
|
|
473
499
|
}
|
|
474
500
|
thinking() {
|
|
475
501
|
this._startThinking();
|
|
476
502
|
}
|
|
503
|
+
// The agent is waiting out the send-interval pause before a real send.
|
|
504
|
+
// Show it as an ANIMATED status with the remaining seconds. The browser
|
|
505
|
+
// calls this repeatedly (once per second), so we update only the base text
|
|
506
|
+
// and keep the running dot timer — otherwise the dots would restart from
|
|
507
|
+
// zero on every update and look frozen on a single dot.
|
|
508
|
+
sendPause(seconds) {
|
|
509
|
+
if (this.pendingText)
|
|
510
|
+
return;
|
|
511
|
+
const label = translate(this.locale)('spinner.pause', { n: seconds });
|
|
512
|
+
if (this._animating && this._dotTimer) {
|
|
513
|
+
this._thinkBase = theme.brown(stripEllipsis(label));
|
|
514
|
+
this.setStatus(this._thinkBase + this._dots(this._dotPhase) + this._hint());
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
this._startAnimated(label);
|
|
518
|
+
}
|
|
477
519
|
setPending(text) {
|
|
478
520
|
this.pendingText = text && String(text).length ? String(text) : null;
|
|
479
521
|
if (this.pendingText) {
|
|
480
522
|
this._stopDots();
|
|
481
|
-
this.setStatus(theme.prompt('✎ ') + this.pendingText +
|
|
523
|
+
this.setStatus(theme.prompt('✎ ') + this.pendingText + this._hint());
|
|
482
524
|
}
|
|
483
525
|
else {
|
|
484
526
|
this._startThinking();
|
|
@@ -500,7 +542,7 @@ export class LineEditor {
|
|
|
500
542
|
assistant(msg) {
|
|
501
543
|
this.stop();
|
|
502
544
|
const rendered = renderMarkdown(msg);
|
|
503
|
-
this.printAbove(NL + theme.assistant('
|
|
545
|
+
this.printAbove(NL + theme.assistant(translate(this.locale)('editor.answer')) + NL + rendered + NL + theme.dim('─'.repeat(60)));
|
|
504
546
|
}
|
|
505
547
|
// Warning to the operator (e.g. the agent stopped suspiciously).
|
|
506
548
|
// Printed above the input line without overwriting it.
|
package/dist/spinner.js
CHANGED
|
@@ -20,6 +20,12 @@ export function createSpinner(locale = 'ru') {
|
|
|
20
20
|
let dotTimer = null;
|
|
21
21
|
let dotPhase = 0;
|
|
22
22
|
let pending = null;
|
|
23
|
+
// Current animated base text and its dot renderer. Kept in variables (not
|
|
24
|
+
// captured in the interval closure) so sendPause() can update the text
|
|
25
|
+
// without restarting the dot animation — otherwise the dots would reset to
|
|
26
|
+
// zero every second and look frozen on a single dot.
|
|
27
|
+
let animBase = '';
|
|
28
|
+
let animating = false;
|
|
23
29
|
// Dot animation: start from an empty string (0 dots), then grow.
|
|
24
30
|
// We align the width to the maximum (3) so the hint doesn't shift.
|
|
25
31
|
const DOTS = ['', '.', '..', '...'];
|
|
@@ -35,19 +41,23 @@ export function createSpinner(locale = 'ru') {
|
|
|
35
41
|
clearInterval(dotTimer);
|
|
36
42
|
dotTimer = null;
|
|
37
43
|
}
|
|
44
|
+
animating = false;
|
|
38
45
|
if (spinner)
|
|
39
46
|
spinner.stop();
|
|
40
47
|
};
|
|
41
48
|
// Hint in the status line: while the agent works the terminal is live,
|
|
42
49
|
// so you can type the next message. Without it this is not obvious.
|
|
43
50
|
const HINT = theme.dim(' · ' + translate(locale)('spinner.hint'));
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
// Run the animated status line: a brown base text plus a growing/shrinking
|
|
52
|
+
// "running" dot sequence. Shared by the thinking spinner and the send-pause
|
|
53
|
+
// indicator, so the pause is animated too (before, it was a static line and
|
|
54
|
+
// the dots did not move — the operator saw a frozen spinner).
|
|
55
|
+
const startAnimated = (baseText) => {
|
|
56
|
+
animBase = theme.brown(stripEllipsis(baseText));
|
|
47
57
|
// The dots are the same color as the base and of fixed width — otherwise
|
|
48
58
|
// the hint on the right "jumps" when the animation phase changes.
|
|
49
59
|
const dots = (n) => theme.brown(DOTS[n] + DOTS_PAD.slice(DOTS[n].length));
|
|
50
|
-
start(
|
|
60
|
+
start(animBase + dots(0) + HINT);
|
|
51
61
|
dotPhase = 0;
|
|
52
62
|
if (dotTimer)
|
|
53
63
|
clearInterval(dotTimer);
|
|
@@ -55,11 +65,16 @@ export function createSpinner(locale = 'ru') {
|
|
|
55
65
|
if (!spinner)
|
|
56
66
|
return;
|
|
57
67
|
dotPhase = (dotPhase + 1) % DOTS.length;
|
|
58
|
-
spinner.text =
|
|
68
|
+
spinner.text = animBase + dots(dotPhase) + HINT;
|
|
59
69
|
}, 400);
|
|
70
|
+
animating = true;
|
|
60
71
|
if (dotTimer.unref)
|
|
61
72
|
dotTimer.unref();
|
|
62
73
|
};
|
|
74
|
+
// Start the animated status: brown text + "running" dots.
|
|
75
|
+
const startThinking = () => {
|
|
76
|
+
startAnimated(randomThinkingPhrase(locale));
|
|
77
|
+
};
|
|
63
78
|
// Show the typed but not yet sent text instead of the spinner.
|
|
64
79
|
// Lets you type a message right while the agent is working.
|
|
65
80
|
const showPending = () => {
|
|
@@ -67,6 +82,7 @@ export function createSpinner(locale = 'ru') {
|
|
|
67
82
|
clearInterval(dotTimer);
|
|
68
83
|
dotTimer = null;
|
|
69
84
|
}
|
|
85
|
+
animating = false;
|
|
70
86
|
if (!spinner)
|
|
71
87
|
spinner = ora('');
|
|
72
88
|
spinner.start();
|
|
@@ -80,6 +96,23 @@ export function createSpinner(locale = 'ru') {
|
|
|
80
96
|
}
|
|
81
97
|
startThinking();
|
|
82
98
|
},
|
|
99
|
+
// The agent is waiting out the send-interval pause before a real send.
|
|
100
|
+
// Show it as an ANIMATED status line with the remaining seconds, so the
|
|
101
|
+
// spinner keeps moving during the pause (previously a static console line
|
|
102
|
+
// was printed and the dots stayed frozen).
|
|
103
|
+
sendPause: (seconds) => {
|
|
104
|
+
if (pending)
|
|
105
|
+
return;
|
|
106
|
+
const label = translate(locale)('spinner.pause', { n: seconds });
|
|
107
|
+
// Update only the base text while the dot animation is already running,
|
|
108
|
+
// so the countdown refreshes without resetting the dots.
|
|
109
|
+
if (animating && dotTimer && spinner) {
|
|
110
|
+
animBase = theme.brown(stripEllipsis(label));
|
|
111
|
+
spinner.text = animBase + theme.brown(DOTS[dotPhase] + DOTS_PAD.slice(DOTS[dotPhase].length)) + HINT;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
startAnimated(label);
|
|
115
|
+
},
|
|
83
116
|
// The text the user types while the agent is working.
|
|
84
117
|
// Empty string / null — restore the regular spinner.
|
|
85
118
|
setPending: (text) => {
|
|
@@ -106,7 +139,7 @@ export function createSpinner(locale = 'ru') {
|
|
|
106
139
|
const rendered = renderMarkdown(msg);
|
|
107
140
|
// Model answer marker: helps visually separate it from the user's input
|
|
108
141
|
// (which is highlighted by the prompt with a golden arrow).
|
|
109
|
-
console.log(NL + theme.assistant('
|
|
142
|
+
console.log(NL + theme.assistant(translate(locale)('editor.answer')) + NL);
|
|
110
143
|
console.log(rendered);
|
|
111
144
|
console.log(theme.dim('─'.repeat(60)) + NL);
|
|
112
145
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"description": "Terminal coding agent over chat.deepseek.com via Playwright",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"start": "node ./dist/index.js",
|
|
18
|
-
"dev": "tsx ./src/index.ts --dev",
|
|
18
|
+
"dev": "ZAMES_NET_DEBUG=1 tsx ./src/index.ts --dev",
|
|
19
19
|
"build": "tsc -p tsconfig.build.json",
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
21
21
|
"prepublishOnly": "npm run build",
|