zames_pro 2.45.0 → 2.47.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/dist/browser.js CHANGED
@@ -87,6 +87,65 @@ export function askPassword(question) {
87
87
  }
88
88
  const USER_DATA_DIR = path.join(os.homedir(), '.zames', 'profile');
89
89
  const CHAT_URL = 'https://chat.deepseek.com/';
90
+ // Cache of the sanitized (non-Headless) User-Agent, keyed by the browser
91
+ // engine path. On the FIRST headless run we detect the real UA, strip the
92
+ // "Headless" marker and store it here; later runs pass it straight to
93
+ // _launchOnce, so the browser starts with a good UA and the "launch → read UA →
94
+ // relaunch" dance happens only once (it otherwise doubled every headless
95
+ // start). Keying by the executable path means a Playwright update (new engine
96
+ // path) invalidates the cache and we re-derive the UA once for the new build.
97
+ const HEADLESS_UA_CACHE = path.join(os.homedir(), '.zames', 'headless-ua.json');
98
+ // Pure: pick a reusable UA from the cache file's text for the given engine, or
99
+ // null. Exported so the policy is unit-tested without touching ~/.zames.
100
+ export function parseHeadlessUACache(raw, engine) {
101
+ try {
102
+ const j = JSON.parse(raw);
103
+ if (j &&
104
+ j.engine === engine &&
105
+ typeof j.ua === 'string' &&
106
+ j.ua &&
107
+ !/Headless/i.test(j.ua)) {
108
+ return j.ua;
109
+ }
110
+ }
111
+ catch { }
112
+ return null;
113
+ }
114
+ function headlessEngineKey() {
115
+ try {
116
+ return chromium.executablePath();
117
+ }
118
+ catch {
119
+ return '';
120
+ }
121
+ }
122
+ async function readCachedHeadlessUA(engine) {
123
+ if (!engine)
124
+ return null;
125
+ try {
126
+ const raw = await fs.readFile(HEADLESS_UA_CACHE, 'utf-8');
127
+ return parseHeadlessUACache(raw, engine);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ async function writeCachedHeadlessUA(engine, ua) {
134
+ if (!engine || !ua)
135
+ return;
136
+ try {
137
+ await fs.mkdir(path.dirname(HEADLESS_UA_CACHE), { recursive: true });
138
+ await fs.writeFile(HEADLESS_UA_CACHE, JSON.stringify({ engine, ua }, null, 2), 'utf-8');
139
+ }
140
+ catch { }
141
+ }
142
+ // For /doctor: is a sanitized UA cached for the CURRENT engine build? A missing
143
+ // cache only means the next headless start relaunches once (not an error), so
144
+ // this is informational. Best-effort.
145
+ export async function headlessUACacheReady() {
146
+ const ua = await readCachedHeadlessUA(headlessEngineKey());
147
+ return !!ua;
148
+ }
90
149
  // A headless Chrome advertises "HeadlessChrome/..." in its User-Agent, and
91
150
  // DeepSeek's CDN (CloudFront/WAF) rejects that UA with a plain "403 ERROR"
92
151
  // page before the app is even served — so a headless login looked broken while
@@ -542,6 +601,14 @@ export class DeepSeekBrowser {
542
601
  console.error('profile: удаляю Singleton-файлы');
543
602
  await cleanSingletonFiles();
544
603
  }
604
+ // Headless: if we already know the sanitized UA for THIS engine build, use
605
+ // it from the start — that skips the "launch, read UA, relaunch" dance
606
+ // (which otherwise doubled every headless start).
607
+ if (this.headless && !this.userAgent) {
608
+ const cached = await readCachedHeadlessUA(headlessEngineKey());
609
+ if (cached)
610
+ this.userAgent = cached;
611
+ }
545
612
  await this._launchOnce();
546
613
  await this._fixHeadlessUserAgent();
547
614
  }
@@ -563,6 +630,9 @@ export class DeepSeekBrowser {
563
630
  if (fixed === ua)
564
631
  return;
565
632
  this.userAgent = fixed;
633
+ // Remember the good UA for this engine build so the NEXT headless start
634
+ // uses it immediately (no relaunch). Keyed by executable path.
635
+ void writeCachedHeadlessUA(headlessEngineKey(), fixed);
566
636
  if (this.debug)
567
637
  console.error('profile: headless UA → перезапуск с обычным Chrome UA');
568
638
  await this.context.close().catch(() => { });
package/dist/commands.js CHANGED
@@ -199,6 +199,11 @@ export function renderDoctor(d) {
199
199
  if (typeof d.sshRemote === 'boolean') {
200
200
  row(true, 'session', d.sshRemote ? 'SSH remote' : 'local terminal');
201
201
  }
202
+ if (d.headless) {
203
+ row(!!d.headlessUaCached, 'headless', d.headlessUaCached
204
+ ? 'UA cached (no relaunch)'
205
+ : 'no cached UA yet (first start relaunches once)');
206
+ }
202
207
  return 'Doctor:' + NL + rows.join(NL);
203
208
  }
204
209
  export function renderPermissions(p) {
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import fs from 'fs/promises';
4
4
  import { existsSync, statSync } from 'fs';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { theme } from './theme.js';
7
- import { DeepSeekBrowser } from './browser.js';
7
+ import { DeepSeekBrowser, headlessUACacheReady } from './browser.js';
8
8
  import { createTools } from './tools.js';
9
9
  import { runAgentLoop } from './agent-loop.js';
10
10
  import { createSpinner } from './spinner.js';
@@ -2830,6 +2830,8 @@ async function main() {
2830
2830
  : undefined,
2831
2831
  minSendIntervalMs: config.browser.minSendIntervalMs,
2832
2832
  sshRemote: !!(process.env.SSH_CONNECTION || process.env.SSH_TTY),
2833
+ headless: !!headless,
2834
+ headlessUaCached: await headlessUACacheReady().catch(() => false),
2833
2835
  })));
2834
2836
  continue;
2835
2837
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.45.0",
3
+ "version": "2.47.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {