zames_pro 2.67.0 → 2.67.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/CHANGELOG.md +24 -22
- package/README.md +2 -1
- package/dist/input.js +17 -15
- package/dist/web.js +34 -5
- package/package.json +1 -2
- package/scripts/build-readme.mts +30 -51
- package/README.ru.md +0 -971
package/CHANGELOG.md
CHANGED
|
@@ -7,16 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [2.67.
|
|
10
|
+
## [2.67.2] - 2026-10-08
|
|
11
11
|
|
|
12
|
-
###
|
|
12
|
+
### Fixed
|
|
13
13
|
|
|
14
|
-
- **
|
|
14
|
+
- **input:** a terminal resize no longer leaves a DUPLICATE of the status/
|
|
15
|
+
input block on screen. After a resize the terminal re-flows the lines
|
|
16
|
+
above the block, so the old code re-pinned by padding to the bottom and
|
|
17
|
+
scrolled the previous copy of the block back into view. The repaint now
|
|
18
|
+
clears the visible viewport (`ESC[2J`) before re-pinning; this clears the
|
|
19
|
+
SCREEN, not the scrollback, so the history above is preserved.
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
## [2.67.1] - 2026-10-08
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
17
24
|
|
|
18
|
-
- **
|
|
19
|
-
|
|
25
|
+
- **web:** the headless renderer reuses ONE BrowserContext for every
|
|
26
|
+
`WebFetch` with `render=true` (it used to build a fresh context per call);
|
|
27
|
+
the page is closed after each fetch and the context in `closeWeb()`.
|
|
28
|
+
- **docs:** `npm run self-smoke` is now documented as a REQUIRED manual
|
|
29
|
+
pre-release step in DEVELOPING.md — it is the only gate that catches an
|
|
30
|
+
integration regression (login, send, attachments) and needs live creds,
|
|
31
|
+
so it stays out of CI.
|
|
32
|
+
- **packaging:** `README.ru.md` is no longer generated or published. The
|
|
33
|
+
Russian translation already lives inside README.md as a `<details>` block
|
|
34
|
+
(visible on npm), and the language switcher is now an in-page `#readme-ru`
|
|
35
|
+
anchor — the second ~15 KB file only duplicated it and could drift.
|
|
36
|
+
|
|
37
|
+
## [2.67.0] - 2026-10-08
|
|
20
38
|
|
|
21
39
|
### Fixed
|
|
22
40
|
|
|
@@ -99,8 +117,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
99
117
|
|
|
100
118
|
- **release:** add scripts/release.mts for one-command release prep (G4)
|
|
101
119
|
|
|
102
|
-
### Fixed
|
|
103
|
-
|
|
104
120
|
- **reload:** include commands, run-task and attach-refs in hot-reload (G2)
|
|
105
121
|
|
|
106
122
|
### Changed
|
|
@@ -109,8 +125,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
109
125
|
|
|
110
126
|
## [2.65.1] - 2026-10-07
|
|
111
127
|
|
|
112
|
-
### Fixed
|
|
113
|
-
|
|
114
128
|
- Long preview lines no longer create a horizontal scrollbar in the terminal:
|
|
115
129
|
`toolCall`/`toolResult` (LineEditor and spinner) now clip the preview by the
|
|
116
130
|
VISIBLE terminal width (`truncateToWidth`) instead of by character count. A
|
|
@@ -143,8 +157,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
143
157
|
README) and the `docs/demo.tape` draft (the VHS render was never generated).
|
|
144
158
|
`logo.jpg` was also removed from the `files` list in package.json.
|
|
145
159
|
|
|
146
|
-
### Fixed
|
|
147
|
-
|
|
148
160
|
- Terminal repaint after a window resize: the relative erase `ESC[n A` after a
|
|
149
161
|
size change landed on a stale row (the terminal re-flowed the wraps), so the
|
|
150
162
|
status/input were drawn in the wrong place. After a resize the block is
|
|
@@ -191,8 +203,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
191
203
|
|
|
192
204
|
## [2.63.1] - 2026-10-06
|
|
193
205
|
|
|
194
|
-
### Fixed
|
|
195
|
-
|
|
196
206
|
- Timer flicker in the pre-send pause status: `sendPause()` (both `LineEditor`
|
|
197
207
|
and the non-TTY `SpinnerUI`) repainted the status WITHOUT the
|
|
198
208
|
"elapsed"/task-badge tail, while the animation tick drew it WITH it — once a
|
|
@@ -377,8 +387,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
377
387
|
- config, sessions and undo now share the single atomic writer (the
|
|
378
388
|
temp-file+rename logic used to be copy-pasted in three places).
|
|
379
389
|
|
|
380
|
-
### Fixed
|
|
381
|
-
|
|
382
390
|
- BACKLOG.md is now git-ignored and untracked: it is the agent's own
|
|
383
391
|
improvement-notes file, rewritten constantly, and used to be published with
|
|
384
392
|
the package.
|
|
@@ -415,8 +423,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
415
423
|
- `npm run build` now wipes `dist/` first (`scripts/clean-dist.mjs`), so a
|
|
416
424
|
removed source file no longer leaves an orphan `.js` in the package.
|
|
417
425
|
|
|
418
|
-
### Fixed
|
|
419
|
-
|
|
420
426
|
- `/undo` failure reasons are localized: `src/undo.ts` returned hardcoded
|
|
421
427
|
Russian strings that showed up in an English UI. It now returns machine codes
|
|
422
428
|
('empty' / 'disabled') localized by the caller.
|
|
@@ -451,8 +457,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
451
457
|
(diff/apply) stays `.ts`-only.
|
|
452
458
|
- `package.json` gained `packageManager: npm@11.19.0`.
|
|
453
459
|
|
|
454
|
-
### Fixed
|
|
455
|
-
|
|
456
460
|
- The status line no longer prefixes a running-tool indicator with the
|
|
457
461
|
previous answer's "done" phase (e.g. `✓ done: running Bash`): `toolCall()`
|
|
458
462
|
clears the send-phase state before starting the tool animation.
|
|
@@ -488,8 +492,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
488
492
|
|
|
489
493
|
## [2.53.1]
|
|
490
494
|
|
|
491
|
-
### Fixed
|
|
492
|
-
|
|
493
495
|
- `require('fs')` in ESM modules (`src/attachments.ts`, `src/self-review.ts`)
|
|
494
496
|
threw `ReferenceError` at runtime: WSL clipboard detection always returned
|
|
495
497
|
`false` and `/self-review` found no sources when run from the built `dist/`.
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
22
|
-
<strong>English</strong> | <a href="
|
|
22
|
+
<strong>English</strong> | <a href="#readme-ru">Русский</a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
A terminal coding agent that works on top of [chat.deepseek.com](https://chat.deepseek.com/) through Playwright.
|
|
@@ -486,6 +486,7 @@ first-party API client.
|
|
|
486
486
|
|
|
487
487
|
MIT
|
|
488
488
|
|
|
489
|
+
<a id="readme-ru" name="readme-ru"></a>
|
|
489
490
|
<details>
|
|
490
491
|
<summary>🇷🇺 Читать по-русски (Russian)</summary>
|
|
491
492
|
|
package/dist/input.js
CHANGED
|
@@ -213,16 +213,12 @@ export class LineEditor {
|
|
|
213
213
|
clearTimeout(this._resizeTimer);
|
|
214
214
|
this._resizeTimer = setTimeout(() => {
|
|
215
215
|
this._resizeTimer = null;
|
|
216
|
-
// Repaint
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
// / duplicated status). The next block write re-pins from scratch
|
|
223
|
-
// (home + pad to bottom) instead of relying on the relative move.
|
|
224
|
-
// The scrollback above is still preserved — we only move the cursor
|
|
225
|
-
// and pad, and never emit ESC[2J ("losing the history is far worse").
|
|
216
|
+
// Repaint the status + input block at the NEW width. The relative
|
|
217
|
+
// `ESC[n A` erase cannot be trusted after a resize: the terminal
|
|
218
|
+
// re-flowed the longer wrapped lines and the old block row is stale,
|
|
219
|
+
// so the block would be redrawn at the wrong row. The block is
|
|
220
|
+
// therefore re-pinned from scratch on the next write (clear + home +
|
|
221
|
+
// pad to bottom).
|
|
226
222
|
this._resizeRepin = true;
|
|
227
223
|
this._render();
|
|
228
224
|
}, 120);
|
|
@@ -687,16 +683,22 @@ export class LineEditor {
|
|
|
687
683
|
const cols = process.stdout.columns || 80;
|
|
688
684
|
// After a resize the relative erase is unreliable (the terminal
|
|
689
685
|
// re-flowed everything and the cursor row changed). Re-DRAW the block
|
|
690
|
-
// from a known position instead:
|
|
691
|
-
//
|
|
692
|
-
//
|
|
693
|
-
// emit ESC[2J here, so the operator's history is preserved.
|
|
686
|
+
// from a known position instead: clear the visible viewport, go HOME,
|
|
687
|
+
// fill it with blank lines, then draw at the bottom. ESC[2J clears the
|
|
688
|
+
// SCREEN, not the scrollback, so the history above is preserved.
|
|
694
689
|
if (this._resizeRepin) {
|
|
695
690
|
this._resizeRepin = false;
|
|
696
691
|
this._eraseBlock();
|
|
697
692
|
this._lastStatusBlock = '';
|
|
698
693
|
this._blockRows = 0;
|
|
699
|
-
|
|
694
|
+
// Clear the VISIBLE viewport before re-pinning. After a resize the
|
|
695
|
+
// terminal re-flowed the lines above the block, and padding to the
|
|
696
|
+
// bottom scrolls the OLD copy of the block back into view — without the
|
|
697
|
+
// clear the old status/input stays on screen as a DUPLICATE of the
|
|
698
|
+
// freshly drawn one (the artifact seen on resize). The scrollback buffer
|
|
699
|
+
// is untouched: ESC[2J clears the screen, NOT the scrollback, so the
|
|
700
|
+
// history printed above is still reachable by scrolling up.
|
|
701
|
+
process.stdout.write(ESC + '[2J' + ESC + '[H');
|
|
700
702
|
this._padToBottom();
|
|
701
703
|
}
|
|
702
704
|
// Status line above the input: the spinner/answer text on the left and the
|
package/dist/web.js
CHANGED
|
@@ -222,7 +222,38 @@ async function getHeadless() {
|
|
|
222
222
|
});
|
|
223
223
|
return _headless;
|
|
224
224
|
}
|
|
225
|
+
// ONE reused context for every render-fetch. Creating a fresh context per call
|
|
226
|
+
// (as before) re-allocated the user-agent/network stack each time, which is
|
|
227
|
+
// heavy for a series of render=true fetches. Pages are closed after each fetch,
|
|
228
|
+
// the context itself is reused and closed in closeWeb().
|
|
229
|
+
//
|
|
230
|
+
// The cache is a tiny pure factory so the reuse/reset behaviour can be unit-
|
|
231
|
+
// tested without launching a real browser.
|
|
232
|
+
export function createReusable(factory, dispose) {
|
|
233
|
+
let cached = null;
|
|
234
|
+
return {
|
|
235
|
+
async get() {
|
|
236
|
+
if (cached === null)
|
|
237
|
+
cached = await factory();
|
|
238
|
+
return cached;
|
|
239
|
+
},
|
|
240
|
+
async reset() {
|
|
241
|
+
if (cached === null)
|
|
242
|
+
return;
|
|
243
|
+
const v = cached;
|
|
244
|
+
cached = null;
|
|
245
|
+
await dispose(v).catch(() => { });
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const _headlessCtxCache = createReusable(async () => {
|
|
250
|
+
const browser = await getHeadless();
|
|
251
|
+
return browser.newContext({
|
|
252
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
253
|
+
});
|
|
254
|
+
}, (ctx) => ctx.close());
|
|
225
255
|
export async function closeWeb() {
|
|
256
|
+
await _headlessCtxCache.reset();
|
|
226
257
|
if (_headless) {
|
|
227
258
|
try {
|
|
228
259
|
await _headless.close();
|
|
@@ -232,10 +263,7 @@ export async function closeWeb() {
|
|
|
232
263
|
}
|
|
233
264
|
}
|
|
234
265
|
async function renderWithHeadless(url, { timeout = DEFAULT_TIMEOUT } = {}) {
|
|
235
|
-
const
|
|
236
|
-
const ctx = await browser.newContext({
|
|
237
|
-
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
238
|
-
});
|
|
266
|
+
const ctx = await _headlessCtxCache.get();
|
|
239
267
|
const page = await ctx.newPage();
|
|
240
268
|
try {
|
|
241
269
|
// Validate the pre-redirect URL (page.goto follows redirects internally,
|
|
@@ -261,7 +289,8 @@ async function renderWithHeadless(url, { timeout = DEFAULT_TIMEOUT } = {}) {
|
|
|
261
289
|
return { status, url: finalUrl, html };
|
|
262
290
|
}
|
|
263
291
|
finally {
|
|
264
|
-
|
|
292
|
+
// Close the PAGE, not the shared context.
|
|
293
|
+
await page.close().catch(() => { });
|
|
265
294
|
}
|
|
266
295
|
}
|
|
267
296
|
// ---------- tools ----------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.67.
|
|
3
|
+
"version": "2.67.2",
|
|
4
4
|
"description": "Terminal coding agent that drives chat.deepseek.com through Playwright — 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": {
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"dist",
|
|
11
11
|
"scripts",
|
|
12
12
|
"README.md",
|
|
13
|
-
"README.ru.md",
|
|
14
13
|
"CHANGELOG.md",
|
|
15
14
|
"LICENSE",
|
|
16
15
|
"logo-small.jpg"
|
package/scripts/build-readme.mts
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Why: npm renders exactly ONE readme per package (README.md in the root) and
|
|
4
4
|
// has no language tabs, so a separate README.ru.md is invisible on the npm
|
|
5
|
-
// page. But npm renders the readme with GitHub Flavored Markdown (via
|
|
6
|
-
// API), so a <details> block DOES work there -- and on GitHub too.
|
|
7
|
-
// keep the English text on top and fold the other language into
|
|
8
|
-
// block at the bottom.
|
|
5
|
+
// page. But npm renders the readme with GitHub Flavored Markdown (via
|
|
6
|
+
// GitHub's API), so a <details> block DOES work there -- and on GitHub too.
|
|
7
|
+
// We therefore keep the English text on top and fold the other language into
|
|
8
|
+
// a collapsible block at the bottom. README.ru.md is generated too so the
|
|
9
|
+
// GitHub switcher can point at it directly, but it is NOT published to npm
|
|
10
|
+
// (see "files" in package.json):
|
|
11
|
+
// it would just duplicate the folded block.
|
|
9
12
|
//
|
|
10
13
|
// The two sources (docs/readme.en.md, docs/readme.ru.md) are the single point
|
|
11
14
|
// of edit for each language; this script wires the switcher and the collapsible
|
|
@@ -13,7 +16,7 @@
|
|
|
13
16
|
// generated files are committed; test/readme-built.test.ts re-runs buildReadmes()
|
|
14
17
|
// and fails if they drift from the sources.
|
|
15
18
|
//
|
|
16
|
-
// Usage:
|
|
19
|
+
// Usage: npm run build:readme
|
|
17
20
|
|
|
18
21
|
import fs from 'node:fs'
|
|
19
22
|
import path from 'node:path'
|
|
@@ -23,17 +26,13 @@ const NL = String.fromCharCode(10)
|
|
|
23
26
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
24
27
|
export const SWITCHER = '<!-- SWITCHER -->'
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
// In-page anchor the switcher links to. GitHub strips `id` on some elements but
|
|
30
|
+
// keeps `name` on an <a>, so both are set.
|
|
31
|
+
export const RU_ANCHOR = '<a id="readme-ru" name="readme-ru"></a>'
|
|
32
|
+
|
|
27
33
|
const EN_SWITCH = [
|
|
28
34
|
'<p align="center">',
|
|
29
|
-
' <strong>English</strong> | <a href="'
|
|
30
|
-
REPO +
|
|
31
|
-
'/README.ru.md">Русский</a>',
|
|
32
|
-
'</p>',
|
|
33
|
-
].join(NL)
|
|
34
|
-
const RU_SWITCH = [
|
|
35
|
-
'<p align="center">',
|
|
36
|
-
' <a href="' + REPO + '/README.md">English</a> | <strong>Русский</strong>',
|
|
35
|
+
' <strong>English</strong> | <a href="#readme-ru">Русский</a>',
|
|
37
36
|
'</p>',
|
|
38
37
|
].join(NL)
|
|
39
38
|
|
|
@@ -71,50 +70,30 @@ function details(summary: string, body: string): string {
|
|
|
71
70
|
].join(NL)
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
function assemble(opts: {
|
|
75
|
-
head: string
|
|
76
|
-
switchLine: string
|
|
77
|
-
body: string
|
|
78
|
-
foldSummary: string
|
|
79
|
-
foldBody: string
|
|
80
|
-
}): string {
|
|
81
|
-
const { head, switchLine, body, foldSummary, foldBody } = opts
|
|
82
|
-
return [
|
|
83
|
-
head,
|
|
84
|
-
'',
|
|
85
|
-
switchLine,
|
|
86
|
-
'',
|
|
87
|
-
body,
|
|
88
|
-
'',
|
|
89
|
-
details(foldSummary, foldBody),
|
|
90
|
-
'',
|
|
91
|
-
].join(NL)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
73
|
// Returns the two generated readmes WITHOUT touching the disk, so a test can
|
|
95
|
-
// compare them against the committed files.
|
|
74
|
+
// compare them against the committed files. Both files carry the SAME content
|
|
75
|
+
// (English on top, Russian folded): README.md is what npm publishes, and
|
|
76
|
+
// README.ru.md exists only so the GitHub language switcher has a target. The
|
|
77
|
+
// switcher is an IN-PAGE `#readme-ru` anchor, so it works from either file
|
|
78
|
+
// without fetching the other one.
|
|
96
79
|
export function buildReadmes(rootDir: string): {
|
|
97
80
|
readmeEn: string
|
|
98
81
|
readmeRu: string
|
|
99
82
|
} {
|
|
100
83
|
const en = readSource(rootDir, 'readme.en.md')
|
|
101
84
|
const ru = readSource(rootDir, 'readme.ru.md')
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
foldSummary: '🇬🇧 Read in English',
|
|
115
|
-
foldBody: en.body,
|
|
116
|
-
}),
|
|
117
|
-
}
|
|
85
|
+
const doc = [
|
|
86
|
+
en.head,
|
|
87
|
+
'',
|
|
88
|
+
EN_SWITCH,
|
|
89
|
+
'',
|
|
90
|
+
en.body,
|
|
91
|
+
'',
|
|
92
|
+
RU_ANCHOR,
|
|
93
|
+
details('🇷🇺 Читать по-русски (Russian)', ru.body),
|
|
94
|
+
'',
|
|
95
|
+
].join(NL)
|
|
96
|
+
return { readmeEn: doc, readmeRu: doc }
|
|
118
97
|
}
|
|
119
98
|
|
|
120
99
|
// Run only when invoked directly (not when imported by a test).
|