zames_pro 2.49.2 → 2.49.4

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 CHANGED
@@ -605,7 +605,7 @@ export class DeepSeekBrowser {
605
605
  // busy, by a clear error in _launchOnce().
606
606
  if (await profileLooksLocked()) {
607
607
  if (this.debug)
608
- console.error('profile: удаляю Singleton-файлы');
608
+ console.error('profile: removing Singleton files');
609
609
  await cleanSingletonFiles();
610
610
  }
611
611
  // Headless: if we already know the sanitized UA for THIS engine build, use
@@ -641,13 +641,13 @@ export class DeepSeekBrowser {
641
641
  // uses it immediately (no relaunch). Keyed by executable path.
642
642
  void writeCachedHeadlessUA(headlessEngineKey(), fixed);
643
643
  if (this.debug)
644
- console.error('profile: headless UA → перезапуск с обычным Chrome UA');
644
+ console.error('profile: headless UA -> relaunching with a plain Chrome UA');
645
645
  await this.context.close().catch(() => { });
646
646
  await this._launchOnce();
647
647
  }
648
648
  catch (e) {
649
649
  if (this.debug)
650
- console.error('profile: UA-фикс не удался:', e.message);
650
+ console.error('profile: UA fix failed:', e.message);
651
651
  }
652
652
  }
653
653
  async _launchOnce() {
@@ -669,7 +669,7 @@ export class DeepSeekBrowser {
669
669
  catch (e) {
670
670
  if (/Target page, context or browser has been closed|profile.*in use/i.test(e.message)) {
671
671
  if (this.debug)
672
- console.error('profile: занят, перезапускаю после очистки');
672
+ console.error('profile: busy, relaunching after cleanup');
673
673
  // Do NOT kill a foreign chrome (see AGENTS.md): only clear the
674
674
  // Singleton files and retry. If the profile is genuinely in use by a
675
675
  // live browser, nukeProfile() below fails safely and the error
@@ -680,7 +680,7 @@ export class DeepSeekBrowser {
680
680
  }
681
681
  catch {
682
682
  if (this.debug)
683
- console.error('profile: сношу целиком');
683
+ console.error('profile: nuking the profile');
684
684
  await nukeProfile();
685
685
  this.context = await chromium.launchPersistentContext(USER_DATA_DIR, options);
686
686
  }
@@ -826,7 +826,7 @@ export class DeepSeekBrowser {
826
826
  // 1) Already signed in (session cookie in the persistent profile)?
827
827
  if (await this.isLoggedIn()) {
828
828
  if (this.debug)
829
- console.error('auth: сессия уже активна');
829
+ console.error('auth: session already active');
830
830
  return;
831
831
  }
832
832
  // 2) Try to sign in automatically with the saved credentials. The
@@ -906,7 +906,7 @@ export class DeepSeekBrowser {
906
906
  // Prompt only when we have a login field to fill.
907
907
  if (!(await this._loginFormVisible())) {
908
908
  if (this.debug)
909
- console.error('auth: форма входа не найдена');
909
+ console.error('auth: login form not found');
910
910
  return false;
911
911
  }
912
912
  console.log('\n' + theme.warn(this._t('auth.need_login')));
package/dist/config.js CHANGED
@@ -4,7 +4,12 @@ import os from 'os';
4
4
  import { DEFAULT_LOCALE } from './i18n.js';
5
5
  const ZAMES_HOME = path.join(os.homedir(), '.zames');
6
6
  const HOME_CONFIG = path.join(ZAMES_HOME, 'config.json');
7
- const PROJECT_CONFIG = path.join(process.cwd(), '.zamesrc.json');
7
+ // The PROJECT config path is resolved LAZILY: `/cd` changes process.cwd() at
8
+ // runtime, so freezing it at import time meant a `.zamesrc.json` in the new
9
+ // working directory was never read (and writes went to the OLD directory).
10
+ export function projectConfigPath() {
11
+ return path.join(process.cwd(), '.zamesrc.json');
12
+ }
8
13
  export const DEFAULTS = {
9
14
  maxIterations: 0,
10
15
  // Headless by default: the browser runs without a visible window. Set
@@ -68,7 +73,7 @@ export const DEFAULTS = {
68
73
  };
69
74
  export function loadConfig() {
70
75
  const merged = JSON.parse(JSON.stringify(DEFAULTS));
71
- for (const file of [HOME_CONFIG, PROJECT_CONFIG]) {
76
+ for (const file of [HOME_CONFIG, projectConfigPath()]) {
72
77
  try {
73
78
  const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
74
79
  deepMerge(merged, data);
@@ -76,7 +81,7 @@ export function loadConfig() {
76
81
  catch (e) {
77
82
  const err = e;
78
83
  if (err.code !== 'ENOENT') {
79
- console.error(`config: не удалось прочитать ${file}: ${err.message}`);
84
+ console.error(`config: failed to read ${file}: ${err.message}`);
80
85
  }
81
86
  }
82
87
  }
@@ -395,7 +400,7 @@ export function validateConfigValue(field, raw) {
395
400
  return parseConfigValue(field, raw);
396
401
  }
397
402
  export function configPathFor(scope) {
398
- return scope === 'project' ? PROJECT_CONFIG : HOME_CONFIG;
403
+ return scope === 'project' ? projectConfigPath() : HOME_CONFIG;
399
404
  }
400
405
  /**
401
406
  * Read only the specified config file (without merging with defaults), so that
@@ -455,5 +460,12 @@ export function resetConfigValue(scope, key) {
455
460
  fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n', 'utf-8');
456
461
  return file;
457
462
  }
458
- export const CONFIG_PATHS = { HOME_CONFIG, PROJECT_CONFIG };
463
+ // 'PROJECT_CONFIG' stays a getter so existing callers that only DISPLAY the
464
+ // path (help, /config) keep working while still reflecting the current cwd.
465
+ export const CONFIG_PATHS = {
466
+ HOME_CONFIG,
467
+ get PROJECT_CONFIG() {
468
+ return projectConfigPath();
469
+ },
470
+ };
459
471
  export { ZAMES_HOME };
package/dist/input.js CHANGED
@@ -739,11 +739,19 @@ export class LineEditor {
739
739
  // status stays on screen, only the input area is redrawn. This removes the
740
740
  // full-screen flicker on terminals like Tabby, where erasing the whole block
741
741
  // on every character made the display blink.
742
- _eraseInputOnly(inputRows) {
742
+ //
743
+ // `cursorRowInInput` is the cursor's row WITHIN the input area (0-based) as
744
+ // it is currently on screen: we move up exactly that many rows to the input's
745
+ // TOP and clear downward (ESC[J), which also wipes the hint lines below the
746
+ // input. Passing the input's full HEIGHT here (an earlier version did)
747
+ // overshot by `cursorRow` rows whenever the cursor was not on the last visual
748
+ // line, so every keystroke redrew the block one row higher — the input line
749
+ // visibly climbed from the bottom to the top of the screen.
750
+ _eraseInputOnly(cursorRowInInput) {
743
751
  if (!this.rendered)
744
752
  return;
745
753
  const rows = process.stdout.rows || 24;
746
- const up = Math.min(Math.max(0, inputRows - 1), Math.max(0, rows - 1));
754
+ const up = Math.min(Math.max(0, cursorRowInInput), Math.max(0, rows - 1));
747
755
  if (up > 0)
748
756
  process.stdout.write(ESC + '[' + up + 'A');
749
757
  process.stdout.write(CR + ESC + '[J');
@@ -833,13 +841,14 @@ export class LineEditor {
833
841
  if (statusUnchanged) {
834
842
  // Count the real input rows via layout (wrapping aware).
835
843
  const layOnly = layoutInput(this.promptStr, this.buf, this.cursor, cols);
836
- const inputLineRows = layOnly.rows.length;
837
844
  const suggOnly = this._suggestions();
838
845
  const shownOnly = suggOnly.slice(0, 8);
839
846
  const linesBelowOnly = shownOnly.length
840
847
  ? shownOnly.length + (suggOnly.length > shownOnly.length ? 1 : 0)
841
848
  : 0;
842
- this._eraseInputOnly(inputLineRows + linesBelowOnly);
849
+ // Move up to the INPUT top using the OLD cursor row (the position that is
850
+ // on screen right now), never the input's height.
851
+ this._eraseInputOnly(this.cursorRowFromTop - this._statusTop);
843
852
  let outOnly = '';
844
853
  outOnly += layOnly.rows.map((r) => r.prefix + r.text).join(NL);
845
854
  if (shownOnly.length) {
@@ -941,14 +950,14 @@ export class LineEditor {
941
950
  }
942
951
  const cols = process.stdout.columns || 80;
943
952
  const lay = layoutInput(this.promptStr, this.buf, this.cursor, cols);
944
- const inputRows = lay.rows.length;
945
953
  const sugg = this._suggestions();
946
954
  const shown = sugg.slice(0, 8);
947
955
  this._suggestCount = shown.length;
948
956
  const linesBelow = shown.length
949
957
  ? shown.length + (sugg.length > shown.length ? 1 : 0)
950
958
  : 0;
951
- this._eraseInputOnly(inputRows + linesBelow);
959
+ // Move up to the INPUT top using the OLD cursor row (on screen right now).
960
+ this._eraseInputOnly(this.cursorRowFromTop - this._statusTop);
952
961
  let out = lay.rows.map((r) => r.prefix + r.text).join(NL);
953
962
  if (shown.length) {
954
963
  const maxName = Math.max(...shown.map((c) => c.name.length));
package/dist/mcp.js CHANGED
@@ -1,3 +1,21 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ // The MCP client advertises a name+version to servers. Read the real version
5
+ // from package.json (the previous hardcoded '1.0.0' silently drifted from the
6
+ // package). Best-effort: an unreadable package.json must never break MCP.
7
+ function packageVersion() {
8
+ try {
9
+ const here = path.dirname(fileURLToPath(import.meta.url));
10
+ const pkg = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf-8'));
11
+ if (pkg && typeof pkg.version === 'string')
12
+ return pkg.version;
13
+ }
14
+ catch {
15
+ // fall through
16
+ }
17
+ return '0.0.0';
18
+ }
1
19
  /** Names of the servers found in a config, before any connection. */
2
20
  export async function listConfiguredServers(workdir) {
3
21
  const files = await discoverConfigFiles(workdir);
@@ -226,7 +244,7 @@ export function qualifyToolName(server, tool) {
226
244
  async function connectServer(name, cfg, debug) {
227
245
  try {
228
246
  const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
229
- const client = new Client({ name: 'zames', version: '1.0.0' }, { capabilities: {} });
247
+ const client = new Client({ name: 'zames', version: packageVersion() }, { capabilities: {} });
230
248
  let transport;
231
249
  if (cfg.url) {
232
250
  if (cfg.transport === 'sse') {
package/dist/sessions.js CHANGED
@@ -7,6 +7,12 @@ import os from 'os';
7
7
  // opened session for a specific working directory.
8
8
  const SESSIONS_DIR = path.join(os.homedir(), '.zames', '.sessions');
9
9
  const INDEX_FILE = path.join(SESSIONS_DIR, 'last.json');
10
+ export const SESSIONS_FORMAT_VERSION = 1;
11
+ function atomicWriteJson(file, value) {
12
+ const tmp = file + '.tmp-' + process.pid;
13
+ fs.writeFileSync(tmp, JSON.stringify(value, null, 2), 'utf-8');
14
+ fs.renameSync(tmp, file);
15
+ }
10
16
  function ensureDir() {
11
17
  fs.mkdirSync(SESSIONS_DIR, { recursive: true });
12
18
  }
@@ -33,13 +39,14 @@ export function saveSession({ id, title = '', workdir = '', todos, }) {
33
39
  workdir: workdir || prev.workdir || '',
34
40
  createdAt: prev.createdAt || now,
35
41
  updatedAt: now,
42
+ version: SESSIONS_FORMAT_VERSION,
36
43
  };
37
44
  // Keep the previous checklist unless a new one is explicitly passed —
38
45
  // saveLastChat() calls this for title/workdir only and must not wipe it.
39
46
  const nextTodos = todos !== undefined ? todos : prev.todos;
40
47
  if (nextTodos !== undefined)
41
48
  data.todos = nextTodos;
42
- fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8');
49
+ atomicWriteJson(file, data);
43
50
  writeIndex({ id, workdir: data.workdir });
44
51
  return data;
45
52
  }
@@ -64,7 +71,7 @@ function writeIndex({ id, workdir }) {
64
71
  index.byWorkdir[key] = id;
65
72
  index.last = id;
66
73
  index.updatedAt = new Date().toISOString();
67
- fs.writeFileSync(INDEX_FILE, JSON.stringify(index, null, 2), 'utf-8');
74
+ atomicWriteJson(INDEX_FILE, index);
68
75
  }
69
76
  catch { }
70
77
  }
@@ -20,12 +20,12 @@ export class Transcript {
20
20
  // Write errors (disk full, file deleted, etc.) arrive as an 'error'
21
21
  // event; without a listener this is an uncaught exception.
22
22
  this.stream.on('error', (e) => {
23
- console.error(`transcript: ошибка записи: ${e.message}`);
23
+ console.error(`transcript: write error: ${e.message}`);
24
24
  this.enabled = false;
25
25
  });
26
26
  }
27
27
  catch (e) {
28
- console.error(`transcript: не удалось создать файл: ${e.message}`);
28
+ console.error(`transcript: failed to create file: ${e.message}`);
29
29
  this.enabled = false;
30
30
  }
31
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.49.2",
3
+ "version": "2.49.4",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "license": "MIT",
40
40
  "engines": {
41
- "node": ">=18"
41
+ "node": ">=20"
42
42
  },
43
43
  "dependencies": {
44
44
  "@modelcontextprotocol/sdk": "^1.30.1",