zames_pro 2.90.14 → 2.90.16
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/CHANGELOG.md +12 -0
- package/dist/browser-pure.js +16 -0
- package/dist/browser.js +23 -6
- package/dist/glm-browser.js +9 -7
- package/dist/qwen-browser.js +15 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.90.16] - 2026-10-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **browser:** apply the sanitized headless UA to the relaunch explicitly
|
|
15
|
+
|
|
16
|
+
## [2.90.15] - 2026-10-29
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- test(browser): pin the headless user-agent sanitizer contract
|
|
21
|
+
|
|
10
22
|
## [2.90.14] - 2026-10-29
|
|
11
23
|
|
|
12
24
|
### Fixed
|
package/dist/browser-pure.js
CHANGED
|
@@ -383,3 +383,19 @@ export async function nukeProfile(dir = USER_DATA_DIR) {
|
|
|
383
383
|
}
|
|
384
384
|
catch { }
|
|
385
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* PURE: does this browser still need the headless User-Agent fix?
|
|
388
|
+
*
|
|
389
|
+
* `_fixHeadlessUserAgent()` bails out for an "already configured" UA — but
|
|
390
|
+
* using `if (this.userAgent) return` was wrong: the field is also the ENGINE's
|
|
391
|
+
* own UA captured from the page, and a config UA that merely CONTAINS the
|
|
392
|
+
* `HeadlessChrome` marker needs the fix just as much. The only value that can
|
|
393
|
+
* skip the fix is one that is already sanitized (and non-empty — an empty
|
|
394
|
+
* string means "ask the engine", not "use a blank UA").
|
|
395
|
+
*/
|
|
396
|
+
export function needsHeadlessUaFix(ua) {
|
|
397
|
+
const value = String(ua ?? '');
|
|
398
|
+
if (!value)
|
|
399
|
+
return true;
|
|
400
|
+
return sanitizeHeadlessUA(value) !== value;
|
|
401
|
+
}
|
package/dist/browser.js
CHANGED
|
@@ -23,7 +23,7 @@ export { STOP_NAME_RE, CONTINUE_NAME_RE };
|
|
|
23
23
|
// imported for the class below and RE-EXPORTED here, so the public API of this
|
|
24
24
|
// module is unchanged (tests and other modules import these names from
|
|
25
25
|
// './browser.js').
|
|
26
|
-
import { askPassword, USER_DATA_DIR, parseHeadlessUACache, headlessEngineKey, readCachedHeadlessUA, writeCachedHeadlessUA, headlessUACacheReady, sanitizeHeadlessUA, writeAuthMarker, isGenerationIncompleteText, isRateLimitText, isServerBusyText, isContextLimitText, normText, cleanAnswer, RateLimitError, ServerBusyError, GenerationIncompleteError, ContextLimitError, cleanSingletonFiles, profileLooksLocked, nukeProfile, } from './browser-pure.js';
|
|
26
|
+
import { askPassword, USER_DATA_DIR, parseHeadlessUACache, headlessEngineKey, readCachedHeadlessUA, writeCachedHeadlessUA, headlessUACacheReady, sanitizeHeadlessUA, needsHeadlessUaFix, writeAuthMarker, isGenerationIncompleteText, isRateLimitText, isServerBusyText, isContextLimitText, normText, cleanAnswer, RateLimitError, ServerBusyError, GenerationIncompleteError, ContextLimitError, cleanSingletonFiles, profileLooksLocked, nukeProfile, } from './browser-pure.js';
|
|
27
27
|
export { askPassword, parseHeadlessUACache, headlessUACacheReady, sanitizeHeadlessUA, isGenerationIncompleteText, isRateLimitText, isServerBusyText, isContextLimitText, normText, RateLimitError, ServerBusyError, GenerationIncompleteError, ContextLimitError, };
|
|
28
28
|
export class DeepSeekBrowser {
|
|
29
29
|
providerName = 'deepseek';
|
|
@@ -330,6 +330,12 @@ export class DeepSeekBrowser {
|
|
|
330
330
|
async _fixHeadlessUserAgent() {
|
|
331
331
|
if (!this.headless)
|
|
332
332
|
return;
|
|
333
|
+
// An already-sanitized UA (from the cache or a previous fix) needs no
|
|
334
|
+
// relaunch; an EMPTY field or a config UA that still carries the marker
|
|
335
|
+
// must be fixed, which the old blanket `if (this.userAgent) return` skipped
|
|
336
|
+
// on Qwen/GLM.
|
|
337
|
+
if (!needsHeadlessUaFix(this.userAgent))
|
|
338
|
+
return;
|
|
333
339
|
try {
|
|
334
340
|
const ua = await this.page.evaluate(readUserAgentInDom);
|
|
335
341
|
const fixed = sanitizeHeadlessUA(ua);
|
|
@@ -338,18 +344,28 @@ export class DeepSeekBrowser {
|
|
|
338
344
|
this.userAgent = fixed;
|
|
339
345
|
// Remember the good UA for this engine build so the NEXT headless start
|
|
340
346
|
// uses it immediately (no relaunch). Keyed by executable path.
|
|
341
|
-
|
|
347
|
+
await writeCachedHeadlessUA(headlessEngineKey(), fixed);
|
|
342
348
|
if (this.debug)
|
|
343
349
|
console.error('profile: headless UA -> relaunching with a plain Chrome UA');
|
|
344
350
|
await this.context.close().catch(() => { });
|
|
345
|
-
|
|
351
|
+
// Pass the UA EXPLICITLY: it was just sanitized, and relying on the field
|
|
352
|
+
// meant the relaunch could read the page's own UA back and keep the
|
|
353
|
+
// HeadlessChrome marker (a 403 / login wall from DeepSeek's CDN).
|
|
354
|
+
await this._launchOnce(fixed);
|
|
346
355
|
}
|
|
347
356
|
catch (e) {
|
|
348
357
|
if (this.debug)
|
|
349
358
|
console.error('profile: UA fix failed:', e.message);
|
|
350
359
|
}
|
|
351
360
|
}
|
|
352
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Launch the persistent context. `uaOverride` applies a User-Agent to THIS
|
|
363
|
+
* launch without storing it first: the UA fix reads the engine UA from the
|
|
364
|
+
* page it is about to close, so passing the value explicitly is the only way
|
|
365
|
+
* the relaunch cannot fall back to the un-sanitized UA (a stored field was
|
|
366
|
+
* read by the OLD page and then re-read by the new one).
|
|
367
|
+
*/
|
|
368
|
+
async _launchOnce(uaOverride) {
|
|
353
369
|
const options = {
|
|
354
370
|
headless: this.headless,
|
|
355
371
|
// 30ms slowMo per Playwright action added up over thousands of DOM
|
|
@@ -360,8 +376,9 @@ export class DeepSeekBrowser {
|
|
|
360
376
|
};
|
|
361
377
|
if (this.channel)
|
|
362
378
|
options.channel = this.channel;
|
|
363
|
-
|
|
364
|
-
|
|
379
|
+
const ua = uaOverride || this.userAgent;
|
|
380
|
+
if (ua)
|
|
381
|
+
options.userAgent = ua;
|
|
365
382
|
try {
|
|
366
383
|
this.context = await chromium.launchPersistentContext(USER_DATA_DIR, options);
|
|
367
384
|
}
|
package/dist/glm-browser.js
CHANGED
|
@@ -26,7 +26,7 @@ import { recoverFromContextLimit } from './context-limit.js';
|
|
|
26
26
|
import { awaitUploadPreview, readAttachBuffers } from './attach-flow.js';
|
|
27
27
|
import { theme } from './theme.js';
|
|
28
28
|
import { scrapeGlmChatList, scrapeGlmChatMessages, parseGlmChatList, parseGlmHistory, } from './glm-chats.js';
|
|
29
|
-
import { cleanAnswer, headlessEngineKey, readCachedHeadlessUA, writeCachedHeadlessUA, sanitizeHeadlessUA, cleanSingletonFiles, profileLooksLocked, nukeProfile, writeAuthMarker, } from './browser-pure.js';
|
|
29
|
+
import { cleanAnswer, headlessEngineKey, readCachedHeadlessUA, writeCachedHeadlessUA, sanitizeHeadlessUA, needsHeadlessUaFix, cleanSingletonFiles, profileLooksLocked, nukeProfile, writeAuthMarker, } from './browser-pure.js';
|
|
30
30
|
import { readLastAnswerInDom, readPageToastsInDom, readUserAgentInDom, readInputTrimmedTextInDom, isElementEnabledInDom, setBusyTitleInDom, chatSignalInDom, } from './browser-dom.js';
|
|
31
31
|
import { probeUiHealth, } from './ui-health.js';
|
|
32
32
|
import { CHAT_URL, LOGIN_URL, CHAT_ID_RE, chatUrl, INPUT_SELECTORS, UPLOAD_BUTTON_SELECTOR, SEND_SELECTORS, STOP_SELECTORS, STOP_NAME_RE, NEW_CHAT_SELECTOR, MODELS_URL, MODEL_TRIGGER_SELECTOR, modelIdFromTriggerId, CHAT_LIST_URL, chatHistoryUrl, AUTH_URL, ANSWER_URL_RE, ANSWER_SELECTORS, THINK_CLASS_RE, MESSAGE_CONTAINER_SELECTORS, USER_CLASS_RE, CHAT_SIGNAL_NODES_SELECTOR, MARKDOWN_SELECTOR, } from './glm-ui.js';
|
|
@@ -261,7 +261,8 @@ export class GlmBrowser {
|
|
|
261
261
|
async _fixHeadlessUserAgent() {
|
|
262
262
|
if (!this.headless)
|
|
263
263
|
return;
|
|
264
|
-
|
|
264
|
+
// See QwenBrowser: only an ALREADY sanitized UA means "nothing to fix".
|
|
265
|
+
if (!needsHeadlessUaFix(this.userAgent))
|
|
265
266
|
return;
|
|
266
267
|
try {
|
|
267
268
|
const ua = await this.page.evaluate(readUserAgentInDom);
|
|
@@ -269,18 +270,18 @@ export class GlmBrowser {
|
|
|
269
270
|
if (fixed === ua)
|
|
270
271
|
return;
|
|
271
272
|
this.userAgent = fixed;
|
|
272
|
-
|
|
273
|
+
await writeCachedHeadlessUA(headlessEngineKey(), fixed);
|
|
273
274
|
if (this.debug)
|
|
274
275
|
console.error('glm profile: headless UA -> relaunching plain Chrome UA');
|
|
275
276
|
await this.context.close().catch(() => { });
|
|
276
|
-
await this._launchOnce();
|
|
277
|
+
await this._launchOnce(fixed);
|
|
277
278
|
}
|
|
278
279
|
catch (e) {
|
|
279
280
|
if (this.debug)
|
|
280
281
|
console.error('glm profile: UA fix failed:', e.message);
|
|
281
282
|
}
|
|
282
283
|
}
|
|
283
|
-
async _launchOnce() {
|
|
284
|
+
async _launchOnce(uaOverride) {
|
|
284
285
|
const options = {
|
|
285
286
|
headless: this.headless,
|
|
286
287
|
slowMo: 10,
|
|
@@ -288,8 +289,9 @@ export class GlmBrowser {
|
|
|
288
289
|
};
|
|
289
290
|
if (this.channel)
|
|
290
291
|
options.channel = this.channel;
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
const ua = uaOverride || this.userAgent;
|
|
293
|
+
if (ua)
|
|
294
|
+
options.userAgent = ua;
|
|
293
295
|
try {
|
|
294
296
|
this.context = await chromium.launchPersistentContext(GLM_USER_DATA_DIR, options);
|
|
295
297
|
}
|
package/dist/qwen-browser.js
CHANGED
|
@@ -27,7 +27,7 @@ import { awaitUploadPreview, readAttachBuffers } from './attach-flow.js';
|
|
|
27
27
|
import { theme } from './theme.js';
|
|
28
28
|
import { recoverFromContextLimit } from './context-limit.js';
|
|
29
29
|
import { scrapeQwenChatList, scrapeQwenChatMessages, parseQwenChatList, parseQwenHistory, } from './qwen-chats.js';
|
|
30
|
-
import { cleanAnswer, headlessEngineKey, readCachedHeadlessUA, writeCachedHeadlessUA, sanitizeHeadlessUA, cleanSingletonFiles, profileLooksLocked, nukeProfile, writeAuthMarker, } from './browser-pure.js';
|
|
30
|
+
import { cleanAnswer, headlessEngineKey, readCachedHeadlessUA, writeCachedHeadlessUA, sanitizeHeadlessUA, needsHeadlessUaFix, cleanSingletonFiles, profileLooksLocked, nukeProfile, writeAuthMarker, } from './browser-pure.js';
|
|
31
31
|
import { chatSignalInDom, dumpDomCountsInDom, isElementEnabledInDom, readInputTrimmedTextInDom, readLastAnswerInDom, readPageToastsInDom, readUserAgentInDom, setBusyTitleInDom, clickStopInDom, stopButtonVisibleInDom, } from './browser-dom.js';
|
|
32
32
|
import { probeUiHealth, } from './ui-health.js';
|
|
33
33
|
import { CHAT_URL, CHAT_ID_RE, chatUrl, INPUT_SELECTORS, SEND_SELECTORS, STOP_SELECTORS, STOP_NAME_RE, NEW_CHAT_SELECTOR, MODEL_TRIGGER_SELECTOR, MODELS_URL, CHAT_LIST_URL, chatHistoryUrl, AGE_GATE_MODAL, AGE_GATE_CONTINUE, AGE_GATE_YEAR_SELECT, AGE_GATE_YEAR_OPTION, COOKIE_ACCEPT_RE, ATTACH_SELECTORS, ATTACH_MENU_TRIGGER, ATTACH_MENU_ITEM_RE, THINKING_SELECTOR, THINKING_MODE_RE, FAST_MODE_RE, WEB_SEARCH_MENU_RE, ANSWER_SELECTORS, THINK_CLASS_RE, MESSAGE_CONTAINER_SELECTORS, ASSISTANT_CLASS_RE, CHAT_SIGNAL_NODES_SELECTOR, MARKDOWN_SELECTOR, TOGGLE_SELECTORS, } from './qwen-ui.js';
|
|
@@ -264,7 +264,12 @@ export class QwenBrowser {
|
|
|
264
264
|
async _fixHeadlessUserAgent() {
|
|
265
265
|
if (!this.headless)
|
|
266
266
|
return;
|
|
267
|
-
|
|
267
|
+
// A UA that is ALREADY sanitized came from the cache; there is nothing to
|
|
268
|
+
// fix and nothing to relaunch for. Any other value (including an empty
|
|
269
|
+
// one) is the engine's own UA, which may still carry the marker — the old
|
|
270
|
+
// blanket `if (this.userAgent) return` skipped the fix for exactly that
|
|
271
|
+
// case, leaving `HeadlessChrome` in the live browser.
|
|
272
|
+
if (!needsHeadlessUaFix(this.userAgent))
|
|
268
273
|
return;
|
|
269
274
|
try {
|
|
270
275
|
const ua = await this.page.evaluate(readUserAgentInDom);
|
|
@@ -272,18 +277,20 @@ export class QwenBrowser {
|
|
|
272
277
|
if (fixed === ua)
|
|
273
278
|
return;
|
|
274
279
|
this.userAgent = fixed;
|
|
275
|
-
|
|
280
|
+
await writeCachedHeadlessUA(headlessEngineKey(), fixed);
|
|
276
281
|
if (this.debug)
|
|
277
282
|
console.error('qwen profile: headless UA -> relaunching plain Chrome UA');
|
|
278
283
|
await this.context.close().catch(() => { });
|
|
279
|
-
|
|
284
|
+
// Apply the UA to THIS launch explicitly: re-reading it from the fresh
|
|
285
|
+
// page would keep the marker the fix exists to remove.
|
|
286
|
+
await this._launchOnce(fixed);
|
|
280
287
|
}
|
|
281
288
|
catch (e) {
|
|
282
289
|
if (this.debug)
|
|
283
290
|
console.error('qwen profile: UA fix failed:', e.message);
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
|
-
async _launchOnce() {
|
|
293
|
+
async _launchOnce(uaOverride) {
|
|
287
294
|
const options = {
|
|
288
295
|
headless: this.headless,
|
|
289
296
|
slowMo: 10,
|
|
@@ -291,8 +298,9 @@ export class QwenBrowser {
|
|
|
291
298
|
};
|
|
292
299
|
if (this.channel)
|
|
293
300
|
options.channel = this.channel;
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
const ua = uaOverride || this.userAgent;
|
|
302
|
+
if (ua)
|
|
303
|
+
options.userAgent = ua;
|
|
296
304
|
try {
|
|
297
305
|
this.context = await chromium.launchPersistentContext(QWEN_USER_DATA_DIR, options);
|
|
298
306
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.90.
|
|
3
|
+
"version": "2.90.16",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|