zames_pro 2.75.6 → 2.77.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/CHANGELOG.md +46 -0
- package/README.md +9 -6
- package/dist/answer-parse.js +19 -3
- package/dist/changelog.js +6 -0
- package/dist/commands.js +50 -5
- package/dist/config.js +7 -0
- package/dist/context.js +104 -1
- package/dist/diagnostics.js +8 -1
- package/dist/goal.js +13 -4
- package/dist/i18n/deepseek.js +13 -0
- package/dist/i18n/help.js +54 -0
- package/dist/i18n/self-review.js +2 -2
- package/dist/index.js +33 -5
- package/dist/input.js +7 -1
- package/dist/mcp.js +9 -1
- package/dist/net-capture.js +37 -0
- package/dist/qwen-browser.js +6 -3
- package/dist/qwen-chats.js +9 -2
- package/dist/run-task.js +8 -2
- package/dist/scheduler.js +28 -3
- package/dist/shell.js +10 -2
- package/dist/subagent.js +17 -4
- package/dist/tool-summary.js +14 -1
- package/dist/tui/ConfigMenu.js +11 -7
- package/dist/tui/ConfirmDialog.js +5 -3
- package/dist/tui/SelectList.js +6 -4
- package/dist/tui/TuiEditor.js +4 -0
- package/dist/tui/edit.js +13 -22
- package/dist/tui/keys.js +12 -12
- package/dist/tui/useMouseWheel.js +9 -0
- package/dist/web.js +95 -3
- package/package.json +1 -1
|
@@ -48,7 +48,16 @@ export function parseWheel(chunk) {
|
|
|
48
48
|
let m;
|
|
49
49
|
while ((m = re.exec(chunk)) !== null) {
|
|
50
50
|
const cb = Number(m[1]);
|
|
51
|
+
// SGR reports a RELEASE as the same button number with a lowercase `m`.
|
|
52
|
+
// Only press events are wheels: a terminal that sends 1000/1002/1003
|
|
53
|
+
// release reports (xterm does for button events) would otherwise count
|
|
54
|
+
// every notch TWICE — once for the press and once for its release.
|
|
55
|
+
if (m[0].endsWith('m'))
|
|
56
|
+
continue;
|
|
51
57
|
// Button code 64 = wheel up (scroll back), 65 = wheel down (scroll fwd).
|
|
58
|
+
// Modifier bits (4=shift, 8=alt, 16=ctrl) must be masked OFF first: the
|
|
59
|
+
// mask below used to keep bit 0, so Ctrl/Alt-wheel (Cb 80/72/... — odd or
|
|
60
|
+
// even) fell through and that notch was lost.
|
|
52
61
|
const button = cb & 0b11000011;
|
|
53
62
|
if (button === 64)
|
|
54
63
|
delta += 1;
|
package/dist/web.js
CHANGED
|
@@ -5,7 +5,37 @@ const DEFAULT_TIMEOUT = 20_000;
|
|
|
5
5
|
const MAX_TEXT = 12_000;
|
|
6
6
|
// Removes scripts, styles, and turns HTML into readable text. Exported for
|
|
7
7
|
// tests — the entity/whitespace/collapse branches are pure and worth pinning.
|
|
8
|
+
// Decode ONE numeric HTML entity to its character. A hostile/broken page can
|
|
9
|
+
// carry `�` (beyond Unicode), a surrogate (`�`) or a huge
|
|
10
|
+
// hex value; String.fromCodePoint THROWS RangeError on those, and the throw
|
|
11
|
+
// escaped htmlToText into the WebFetch handler — the whole page came back as
|
|
12
|
+
// "Error fetching <url>: Invalid code point 999999999". An undecodable
|
|
13
|
+
// reference is kept VERBATIM instead.
|
|
14
|
+
//
|
|
15
|
+
// A surrogate PAIR written as two references (`��`) is ONE
|
|
16
|
+
// astral character (an emoji): decoding each half alone left two lone
|
|
17
|
+
// surrogates in the text. A lone high surrogate is remembered and JOINED with
|
|
18
|
+
// the low surrogate that immediately follows it (the replacer runs in order).
|
|
19
|
+
let pendingHighSurrogate = null;
|
|
20
|
+
function decodeEntityCode(cp) {
|
|
21
|
+
if (!Number.isInteger(cp) || cp < 0 || cp > 0x10ffff)
|
|
22
|
+
return '';
|
|
23
|
+
if (cp >= 0xd800 && cp <= 0xdbff) {
|
|
24
|
+
pendingHighSurrogate = String.fromCharCode(cp);
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
if (cp >= 0xdc00 && cp <= 0xdfff) {
|
|
28
|
+
const high = pendingHighSurrogate;
|
|
29
|
+
pendingHighSurrogate = null;
|
|
30
|
+
return high ? high + String.fromCharCode(cp) : String.fromCharCode(cp);
|
|
31
|
+
}
|
|
32
|
+
pendingHighSurrogate = null;
|
|
33
|
+
return String.fromCodePoint(cp);
|
|
34
|
+
}
|
|
8
35
|
export function htmlToText(html) {
|
|
36
|
+
// Reset the cross-reference surrogate state: it must never leak from one
|
|
37
|
+
// document to the next.
|
|
38
|
+
pendingHighSurrogate = null;
|
|
9
39
|
// Remove blocks we don't need
|
|
10
40
|
let s = html
|
|
11
41
|
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
@@ -35,8 +65,8 @@ export function htmlToText(html) {
|
|
|
35
65
|
.replace(/—/g, '—')
|
|
36
66
|
.replace(/–/g, '–')
|
|
37
67
|
.replace(/…/g, '…')
|
|
38
|
-
.replace(/&#(\d+);/g, (_, n) =>
|
|
39
|
-
.replace(/&#x([0-9a-f]+);/gi, (_, n) =>
|
|
68
|
+
.replace(/&#(\d+);/g, (_, n) => decodeEntityCode(Number(n)))
|
|
69
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, n) => decodeEntityCode(parseInt(n, 16)));
|
|
40
70
|
// Collapse blank lines
|
|
41
71
|
s = s
|
|
42
72
|
.split('\n')
|
|
@@ -52,6 +82,49 @@ export function htmlToText(html) {
|
|
|
52
82
|
// or localhost services. We resolve the hostname and refuse private / loopback
|
|
53
83
|
// / link-local addresses. Best-effort: a DNS rebind between the check and the
|
|
54
84
|
// fetch is out of scope for a CLI tool, but the common cases are covered.
|
|
85
|
+
// Parse an inet_aton-style IPv4 literal (the form WHATWG URL and the OS also
|
|
86
|
+
// accept) into a uint32, or null when it is not one. Rules: 1-4 parts; each
|
|
87
|
+
// part may be hex (0x…), octal (leading 0) or decimal; the LAST part fills all
|
|
88
|
+
// remaining bytes, so `127.1` == 127.0.0.1 and `2130706433` == 127.0.0.1.
|
|
89
|
+
export function parseLegacyIpv4(host) {
|
|
90
|
+
const h = String(host || '').trim();
|
|
91
|
+
if (!h)
|
|
92
|
+
return null;
|
|
93
|
+
const parts = h.split('.');
|
|
94
|
+
if (parts.length < 1 || parts.length > 4)
|
|
95
|
+
return null;
|
|
96
|
+
const nums = [];
|
|
97
|
+
for (const p of parts) {
|
|
98
|
+
if (p === '')
|
|
99
|
+
return null;
|
|
100
|
+
let v;
|
|
101
|
+
if (/^0[xX][0-9a-fA-F]+$/.test(p))
|
|
102
|
+
v = parseInt(p.slice(2), 16);
|
|
103
|
+
else if (/^0[0-7]+$/.test(p))
|
|
104
|
+
v = parseInt(p.slice(1), 8);
|
|
105
|
+
else if (/^\d+$/.test(p))
|
|
106
|
+
v = parseInt(p, 10);
|
|
107
|
+
else
|
|
108
|
+
return null;
|
|
109
|
+
if (!Number.isFinite(v) || v < 0)
|
|
110
|
+
return null;
|
|
111
|
+
nums.push(v);
|
|
112
|
+
}
|
|
113
|
+
// Every part except the last must fit one byte; the last fills the rest.
|
|
114
|
+
for (let i = 0; i < nums.length - 1; i++) {
|
|
115
|
+
if (nums[i] > 0xff)
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const lastMax = Math.pow(256, 5 - nums.length) - 1;
|
|
119
|
+
if (nums[nums.length - 1] > lastMax)
|
|
120
|
+
return null;
|
|
121
|
+
let n = 0;
|
|
122
|
+
for (let i = 0; i < nums.length - 1; i++) {
|
|
123
|
+
n += nums[i] * Math.pow(256, 3 - i);
|
|
124
|
+
}
|
|
125
|
+
n += nums[nums.length - 1];
|
|
126
|
+
return n >>> 0;
|
|
127
|
+
}
|
|
55
128
|
export function isPrivateIp(ip) {
|
|
56
129
|
const v4 = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
|
|
57
130
|
if (v4) {
|
|
@@ -68,6 +141,22 @@ export function isPrivateIp(ip) {
|
|
|
68
141
|
return true; // CGNAT
|
|
69
142
|
return false;
|
|
70
143
|
}
|
|
144
|
+
// A NON-canonical IPv4 literal is still a number: WHATWG URL and inet_aton
|
|
145
|
+
// accept 1-4 parts with decimal/octal/hex digits (`2130706433`,
|
|
146
|
+
// `0x7f.1`, `0177.0.0.1`, `127.1` all mean 127.0.0.1). Recognising only the
|
|
147
|
+
// dotted-quad form let those through the SSRF guard (a classic bypass).
|
|
148
|
+
if (/^[0-9a-fx.]+$/i.test(ip) && !ip.includes(':')) {
|
|
149
|
+
const n = parseLegacyIpv4(ip);
|
|
150
|
+
if (n !== null) {
|
|
151
|
+
return isPrivateIp(((n >>> 24) & 0xff) +
|
|
152
|
+
'.' +
|
|
153
|
+
((n >>> 16) & 0xff) +
|
|
154
|
+
'.' +
|
|
155
|
+
((n >>> 8) & 0xff) +
|
|
156
|
+
'.' +
|
|
157
|
+
(n & 0xff));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
71
160
|
const low = ip.toLowerCase();
|
|
72
161
|
if (low === '::1' || low === '::')
|
|
73
162
|
return true;
|
|
@@ -123,7 +212,10 @@ export async function assertPublicUrl(rawUrl) {
|
|
|
123
212
|
if (u.protocol !== 'http:' && u.protocol !== 'https:') {
|
|
124
213
|
throw new Error(`unsupported protocol: ${u.protocol}`);
|
|
125
214
|
}
|
|
126
|
-
|
|
215
|
+
// The URL API keeps the BRACKETS of an IPv6 literal (`[::ffff:7f00:1]`),
|
|
216
|
+
// and net.isIP() rejects a bracketed host — so the address fell through to
|
|
217
|
+
// the DNS lookup branch and the literal was never checked as an address.
|
|
218
|
+
const host = u.hostname.replace(/^\[(.*)\]$/, '$1');
|
|
127
219
|
if (isPrivateHostname(host)) {
|
|
128
220
|
throw new Error(`blocked private host: ${host}`);
|
|
129
221
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.77.0",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen and other web LLMs — 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": {
|