zames_pro 2.4.1 → 2.4.2
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/dist/browser.js +70 -4
- package/dist/input.js +1 -1
- package/dist/spinner.js +1 -1
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -316,20 +316,78 @@ export class DeepSeekBrowser {
|
|
|
316
316
|
return '';
|
|
317
317
|
return raw;
|
|
318
318
|
}
|
|
319
|
+
// Найти кнопку Stop в интерфейсе DeepSeek. Полагаться только на класс
|
|
320
|
+
// нельзя: во время генерации кнопка отправки (та же circle-кнопка)
|
|
321
|
+
// меняет иконку на «квадрат» (stop), сохраняя классы. Поэтому:
|
|
322
|
+
// 1) проверяем явные селекторы (aria-label/текст Stop);
|
|
323
|
+
// 2) ищем primary circle-кнопку и смотрим на её иконку: rect/квадрат =
|
|
324
|
+
// stop, path/стрелка = send.
|
|
325
|
+
async _stopButtonVisible() {
|
|
326
|
+
const explicit = await this._findVisible(STOP_SELECTORS, 250);
|
|
327
|
+
if (explicit)
|
|
328
|
+
return true;
|
|
329
|
+
return await this.page
|
|
330
|
+
.evaluate(() => {
|
|
331
|
+
const btns = Array.from(document.querySelectorAll('div[role="button"], button'));
|
|
332
|
+
for (const b of btns) {
|
|
333
|
+
const cls = (b.className || '').toString();
|
|
334
|
+
if (!/ds-button--(circle|primary|filled)/i.test(cls))
|
|
335
|
+
continue;
|
|
336
|
+
const label = ((b.getAttribute('aria-label') || '') +
|
|
337
|
+
' ' +
|
|
338
|
+
(b.getAttribute('title') || '') +
|
|
339
|
+
' ' +
|
|
340
|
+
(b.textContent || '')).toLowerCase();
|
|
341
|
+
if (/stop|останов/.test(label))
|
|
342
|
+
return true;
|
|
343
|
+
// Иконка-квадрат = кнопка Stop; стрелка (path без rect) = отправка.
|
|
344
|
+
const svg = b.querySelector('svg');
|
|
345
|
+
if (svg && svg.querySelector('rect'))
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
})
|
|
350
|
+
.catch(() => false);
|
|
351
|
+
}
|
|
319
352
|
async _isGenerating() {
|
|
320
|
-
|
|
321
|
-
return !!stop;
|
|
353
|
+
return await this._stopButtonVisible();
|
|
322
354
|
}
|
|
323
355
|
async stopGeneration() {
|
|
324
356
|
this._abort = true;
|
|
357
|
+
// 1) Пробуем явные селекторы Stop.
|
|
325
358
|
const btn = await this._findVisible(STOP_SELECTORS, 500);
|
|
326
359
|
if (btn) {
|
|
327
360
|
try {
|
|
328
|
-
await btn.click({ timeout:
|
|
361
|
+
await btn.click({ timeout: 1500 });
|
|
329
362
|
return true;
|
|
330
363
|
}
|
|
331
364
|
catch { }
|
|
332
365
|
}
|
|
366
|
+
// 2) Кликаем по primary circle-кнопке, если у неё иконка-квадрат.
|
|
367
|
+
const clicked = await this.page
|
|
368
|
+
.evaluate(() => {
|
|
369
|
+
const btns = Array.from(document.querySelectorAll('div[role="button"], button'));
|
|
370
|
+
for (const b of btns) {
|
|
371
|
+
const cls = (b.className || '').toString();
|
|
372
|
+
if (!/ds-button--(circle|primary|filled)/i.test(cls))
|
|
373
|
+
continue;
|
|
374
|
+
const svg = b.querySelector('svg');
|
|
375
|
+
if (svg && svg.querySelector('rect')) {
|
|
376
|
+
b.click();
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return false;
|
|
381
|
+
})
|
|
382
|
+
.catch(() => false);
|
|
383
|
+
if (clicked)
|
|
384
|
+
return true;
|
|
385
|
+
// 3) Крайний случай — нажать Esc в поле ввода (иногда останавливает).
|
|
386
|
+
try {
|
|
387
|
+
await this.page.keyboard.press('Escape');
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
catch { }
|
|
333
391
|
return false;
|
|
334
392
|
}
|
|
335
393
|
async ask(prompt, { timeout = this.answerTimeoutMs } = {}) {
|
|
@@ -337,6 +395,8 @@ export class DeepSeekBrowser {
|
|
|
337
395
|
let attempt = 0;
|
|
338
396
|
let rateLimitRetries = 0;
|
|
339
397
|
while (attempt < this.askRetries) {
|
|
398
|
+
if (this._abort)
|
|
399
|
+
return '(прервано пользователем)';
|
|
340
400
|
attempt++;
|
|
341
401
|
try {
|
|
342
402
|
return await this._askOnce(prompt, { timeout });
|
|
@@ -430,6 +490,10 @@ export class DeepSeekBrowser {
|
|
|
430
490
|
await this.page.waitForTimeout(gap);
|
|
431
491
|
}
|
|
432
492
|
async _askOnce(prompt, { timeout }) {
|
|
493
|
+
// Сбрасываем флаг прерывания ТОЛЬКО в самом начале отправки. Раньше он
|
|
494
|
+
// сбрасывался перед циклом ожидания — и Esc/Ctrl+C, нажатые во время
|
|
495
|
+
// старта генерации, терялись, запрос не останавливался.
|
|
496
|
+
this._abort = false;
|
|
433
497
|
const input = await this._findVisible(INPUT_SELECTORS, 10_000);
|
|
434
498
|
if (!input) {
|
|
435
499
|
throw new Error('Не найдено поле ввода. Запустите /debug-dom и поправьте INPUT_SELECTORS.');
|
|
@@ -468,6 +532,9 @@ export class DeepSeekBrowser {
|
|
|
468
532
|
.catch(() => 0);
|
|
469
533
|
let started = false;
|
|
470
534
|
while (Date.now() < startDeadline) {
|
|
535
|
+
// Прервали (Esc/Ctrl+C) ещё до старта генерации — выходим сразу.
|
|
536
|
+
if (this._abort)
|
|
537
|
+
return '(прервано пользователем)';
|
|
471
538
|
const pageText = await this._readPageText();
|
|
472
539
|
if (isRateLimitText(pageText)) {
|
|
473
540
|
throw new RateLimitError(pageText.slice(0, 300));
|
|
@@ -499,7 +566,6 @@ export class DeepSeekBrowser {
|
|
|
499
566
|
const deadline = Date.now() + timeout;
|
|
500
567
|
let last = '';
|
|
501
568
|
let stable = 0;
|
|
502
|
-
this._abort = false;
|
|
503
569
|
while (Date.now() < deadline) {
|
|
504
570
|
if (this._abort) {
|
|
505
571
|
return last || '(прервано пользователем)';
|
package/dist/input.js
CHANGED
|
@@ -29,7 +29,7 @@ const PASTE_END = ESC + '[201~';
|
|
|
29
29
|
// Ширину выравниваем по максимуму (3), чтобы подсказка не смещалась.
|
|
30
30
|
const DOTS = ['', '.', '..', '...'];
|
|
31
31
|
const DOTS_PAD = ' ';
|
|
32
|
-
const HINT = theme.dim(' ·
|
|
32
|
+
const HINT = theme.dim(' · Esc — стоп');
|
|
33
33
|
function safeJson(v) {
|
|
34
34
|
try {
|
|
35
35
|
return JSON.stringify(v);
|
package/dist/spinner.js
CHANGED
|
@@ -66,7 +66,7 @@ export function createSpinner() {
|
|
|
66
66
|
};
|
|
67
67
|
// Подсказка в строке статуса: во время работы агента терминал живой,
|
|
68
68
|
// можно печатать следующее сообщение. Без неё это неочевидно.
|
|
69
|
-
const HINT = theme.dim(' ·
|
|
69
|
+
const HINT = theme.dim(' · Esc — стоп');
|
|
70
70
|
// Запуск анимированного статуса: коричневый текст + «бегущие» точки.
|
|
71
71
|
const startThinking = () => {
|
|
72
72
|
const base = theme.brown(stripEllipsis(randomThinkingPhrase()));
|