zames_pro 2.82.141 → 2.82.142
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 +6 -0
- package/dist/attachments.js +4 -7
- package/dist/backlog.js +2 -2
- package/dist/browser-pure.js +2 -2
- package/dist/commands.js +2 -2
- package/dist/compact.js +1 -1
- package/dist/config.js +1 -1
- package/dist/glm-browser.js +1 -1
- package/dist/glm-net-capture.js +1 -1
- package/dist/glm-ui.js +0 -20
- package/dist/hooks.js +1 -1
- package/dist/mcp.js +2 -2
- package/dist/qwen-browser.js +1 -1
- package/dist/qwen-ui.js +0 -2
- package/dist/run-task.js +1 -1
- package/dist/scheduler.js +1 -1
- package/dist/sessions.js +1 -1
- package/dist/spinner.js +1 -1
- package/dist/statusline.js +2 -2
- package/dist/subagent.js +3 -3
- package/dist/system-prompt.js +1 -1
- package/dist/tui/FullScreenApp.js +4 -15
- package/dist/tui/LogView.js +1 -1
- package/dist/tui/edit.js +1 -1
- package/dist/tui/layout.js +1 -1
- package/dist/tui/widgets.js +1 -27
- package/dist/web.js +1 -1
- package/dist/width.js +1 -1
- package/package.json +1 -1
- package/dist/code-blocks.js +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.82.142] - 2026-10-27
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **release:** bump package-lock.json root versions too
|
|
15
|
+
|
|
10
16
|
## [2.82.141] - 2026-10-26
|
|
11
17
|
|
|
12
18
|
### Fixed
|
package/dist/attachments.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createHash } from 'crypto';
|
|
|
6
6
|
import { execFileSync } from 'child_process';
|
|
7
7
|
import { extractPathToken } from './path-token.js';
|
|
8
8
|
export { extractPathToken };
|
|
9
|
-
|
|
9
|
+
const IMAGE_EXTS = [
|
|
10
10
|
'.png',
|
|
11
11
|
'.jpg',
|
|
12
12
|
'.jpeg',
|
|
@@ -16,7 +16,7 @@ export const IMAGE_EXTS = [
|
|
|
16
16
|
'.svg',
|
|
17
17
|
'.ico',
|
|
18
18
|
];
|
|
19
|
-
|
|
19
|
+
const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
|
|
20
20
|
// Stable content hash for de-duplicating identical attachments (a pasted image
|
|
21
21
|
// is saved to a NEW temp file every time, so its path is not a stable identity).
|
|
22
22
|
export function contentHash(data) {
|
|
@@ -286,7 +286,7 @@ export function hasClipboardTool() {
|
|
|
286
286
|
}
|
|
287
287
|
// True when the Windows filesystem is visible from here, i.e. a
|
|
288
288
|
// powershell.exe call can reach the Windows clipboard.
|
|
289
|
-
|
|
289
|
+
function windowsReachable() {
|
|
290
290
|
if (process.platform === 'win32')
|
|
291
291
|
return true;
|
|
292
292
|
if (process.platform !== 'linux')
|
|
@@ -410,9 +410,6 @@ export async function readClipboardImageDetailed() {
|
|
|
410
410
|
}
|
|
411
411
|
return { data: null, via: 'unsupported-platform' };
|
|
412
412
|
}
|
|
413
|
-
export async function readClipboardImage() {
|
|
414
|
-
return (await readClipboardImageDetailed()).data;
|
|
415
|
-
}
|
|
416
413
|
function tryCommand(bin, args) {
|
|
417
414
|
try {
|
|
418
415
|
const out = execFileSync(bin, args, {
|
|
@@ -501,7 +498,7 @@ export function winPathToWsl(winPath) {
|
|
|
501
498
|
return '/mnt/' + drive + '/' + rest;
|
|
502
499
|
}
|
|
503
500
|
// Run the embedded PowerShell script and return the clipboard image bytes.
|
|
504
|
-
|
|
501
|
+
async function readWindowsClipboardImage() {
|
|
505
502
|
if (!isWsl())
|
|
506
503
|
return null;
|
|
507
504
|
const out = tryCommand('powershell.exe', [
|
package/dist/backlog.js
CHANGED
|
@@ -13,8 +13,8 @@ const NL = String.fromCharCode(10);
|
|
|
13
13
|
const NEW_ITEM_PREFIX = 'N';
|
|
14
14
|
// Past these thresholds BACKLOG.md costs more tokens (on /improve) than it is
|
|
15
15
|
// worth, so the operator is nudged to collapse it.
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const BACKLOG_WARN_LINES = 500;
|
|
17
|
+
const BACKLOG_WARN_CHARS = 60_000;
|
|
18
18
|
// Parse a `/backlog <text>` note into the shape appendBacklogItem() wants. An
|
|
19
19
|
// optional leading `P0..P3` token sets the priority; the first line becomes the
|
|
20
20
|
// title and the rest the body. PURE so the operator's note is placed exactly as
|
package/dist/browser-pure.js
CHANGED
|
@@ -214,7 +214,7 @@ export function sanitizeHeadlessUA(ua) {
|
|
|
214
214
|
// reported "no session" for whichever provider had signed in second. A legacy
|
|
215
215
|
// record without `providers` still reads: `{provider:'qwen'}` means Qwen and a
|
|
216
216
|
// bare `{at}` means DeepSeek (the only writer before the second backend).
|
|
217
|
-
|
|
217
|
+
const AUTH_MARKER_FILE = path.join(os.homedir(), '.zames', 'auth.json');
|
|
218
218
|
/**
|
|
219
219
|
* PURE: does the marker text say `provider` has a stored session? Legacy
|
|
220
220
|
* shapes are honored (see the comment above AUTH_MARKER_FILE).
|
|
@@ -269,7 +269,7 @@ export function mergeAuthMarker(raw, provider, at) {
|
|
|
269
269
|
return JSON.stringify({ providers }, null, 2);
|
|
270
270
|
}
|
|
271
271
|
// Best-effort: read the marker text from disk ('' when absent/unreadable).
|
|
272
|
-
|
|
272
|
+
async function readAuthMarker() {
|
|
273
273
|
try {
|
|
274
274
|
return await fs.readFile(AUTH_MARKER_FILE, 'utf-8');
|
|
275
275
|
}
|
package/dist/commands.js
CHANGED
|
@@ -161,7 +161,7 @@ export function parseTranscript(body) {
|
|
|
161
161
|
}
|
|
162
162
|
return out;
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
const EN_DURATION_UNITS = { h: 'h', m: 'm', s: 's' };
|
|
165
165
|
// Human-readable duration: "8s", "5m 12s", "1h 01m 01s". Seconds are padded
|
|
166
166
|
// only when a larger unit is present, so a lone "45s" is not "45s" padded.
|
|
167
167
|
export function formatDuration(ms, units = EN_DURATION_UNITS) {
|
|
@@ -421,7 +421,7 @@ export const RESTORED_HISTORY_LIMIT = 20;
|
|
|
421
421
|
* history) is far bigger and is filtered by the noise checks below; a real
|
|
422
422
|
* answer above this size is shortened instead of being dropped.
|
|
423
423
|
*/
|
|
424
|
-
|
|
424
|
+
const RESTORED_MESSAGE_MAX = 100_000;
|
|
425
425
|
/**
|
|
426
426
|
* DeepSeek stores the assistant turn VERBATIM: either a raw tool-call JSON
|
|
427
427
|
* ({"tool":"Read","args":{...}}, frequently dirty/truncated) or a `respond`
|
package/dist/compact.js
CHANGED
|
@@ -3,7 +3,7 @@ import { buildCompactPrompt, buildCompactCarryover, isUsableCompactSummary, trim
|
|
|
3
3
|
import { loadHooks, runLifecycleHooks } from './hooks.js';
|
|
4
4
|
const NL = String.fromCharCode(10);
|
|
5
5
|
/** How many times the summary call is retried before the local fallback. */
|
|
6
|
-
|
|
6
|
+
const COMPACT_SUMMARY_ATTEMPTS = 3;
|
|
7
7
|
/** How many restored messages the local fallback keeps. */
|
|
8
8
|
export const COMPACT_FALLBACK_LIMIT = 40;
|
|
9
9
|
/**
|
package/dist/config.js
CHANGED
|
@@ -748,7 +748,7 @@ function parseConfigValue(field, raw) {
|
|
|
748
748
|
export function validateConfigValue(field, raw) {
|
|
749
749
|
return parseConfigValue(field, raw);
|
|
750
750
|
}
|
|
751
|
-
|
|
751
|
+
function configPathFor(scope) {
|
|
752
752
|
return scope === 'project' ? projectConfigPath() : HOME_CONFIG;
|
|
753
753
|
}
|
|
754
754
|
/**
|
package/dist/glm-browser.js
CHANGED
|
@@ -33,7 +33,7 @@ import { extractAnswer, extractTokenUsage, isFinishedWithoutAnswer, isRateLimitT
|
|
|
33
33
|
import { SendRuntime } from './send-runtime.js';
|
|
34
34
|
import { readGlmBodyFeatures, unsafeGlmModes, glmFlagsMismatch, } from './glm-flags.js';
|
|
35
35
|
import { interactiveBrowserLogin, loginIsInteractive, canShowWindow, } from './browser-login.js';
|
|
36
|
-
|
|
36
|
+
const GLM_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'glm-profile');
|
|
37
37
|
const MAX_NET_BODY = 64 * 1024;
|
|
38
38
|
function capNetBody(body) {
|
|
39
39
|
if (body.length <= MAX_NET_BODY)
|
package/dist/glm-net-capture.js
CHANGED
|
@@ -224,7 +224,7 @@ export function extractTokenUsage(body) {
|
|
|
224
224
|
return null;
|
|
225
225
|
}
|
|
226
226
|
/** True when a chunk marks the end of the stream (`done: true`). */
|
|
227
|
-
|
|
227
|
+
function isDoneChunk(obj) {
|
|
228
228
|
const d = completionData(obj);
|
|
229
229
|
if (!d)
|
|
230
230
|
return false;
|
package/dist/glm-ui.js
CHANGED
|
@@ -49,8 +49,6 @@ export function chatHistoryUrl(chatId) {
|
|
|
49
49
|
}
|
|
50
50
|
/** GET - the current user (or guest) record. */
|
|
51
51
|
export const AUTH_URL = 'https://chat.z.ai/api/v1/auths/';
|
|
52
|
-
/** POST - creates a chat and returns { id }. */
|
|
53
|
-
export const NEW_CHAT_URL = 'https://chat.z.ai/api/v1/chats/new';
|
|
54
52
|
/** POST - the SSE completion endpoint (the answer stream). */
|
|
55
53
|
export const ANSWER_URL_RE = /\/api\/v2\/chat\/completions/i;
|
|
56
54
|
// ---------- input ----------
|
|
@@ -142,30 +140,12 @@ export function glmContextWindow(model) {
|
|
|
142
140
|
return null;
|
|
143
141
|
}
|
|
144
142
|
// ---------- service statuses / errors ----------
|
|
145
|
-
// A placeholder that must never be taken for an answer.
|
|
146
|
-
export const STATUS_RE = /^(thinking|generating|thinking\s+process|thought\s+process)[\s.…]*$/i;
|
|
147
|
-
// z.ai's captcha gate (an Aliyun slider). Visible text on the modal.
|
|
148
|
-
export const CAPTCHA_RE = /security verification|drag the slider/i;
|
|
149
|
-
export const CAPTCHA_SELECTORS = [
|
|
150
|
-
'.aliyun-captcha',
|
|
151
|
-
'[class*="aliyunCaptcha"]',
|
|
152
|
-
'[id*="aliyunCaptcha"]',
|
|
153
|
-
];
|
|
154
143
|
// Rate limit / busy notices. Kept NARROW: the classifier additionally requires
|
|
155
144
|
// an error payload or an answer-less body (same rule as qwen-net-capture.ts),
|
|
156
145
|
// so a normal answer that merely mentions "try again" is not mistaken for a
|
|
157
146
|
// limit.
|
|
158
147
|
export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
|
|
159
148
|
export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later|high demand/i;
|
|
160
|
-
export const GUEST_LIMIT_RE = /guest.*limit|log ?in to continue|sign ?in to continue|гостевой лимит/i;
|
|
161
|
-
// ---------- mode flags ----------
|
|
162
|
-
// The completion body carries the mode flags. `preview_mode` and the
|
|
163
|
-
// `general_agent` flag change the ANSWER FORMAT (they enable z.ai's own agent
|
|
164
|
-
// runtime, which emits its own tool calls), and that would break the zames
|
|
165
|
-
// tool-call protocol. The request body is rewritten before the send and then
|
|
166
|
-
// verified (glm-browser's _installRuntimeRewrite/_verifyModes + glm-flags).
|
|
167
|
-
export const SAFE_PREVIEW_MODE = false;
|
|
168
|
-
export const SAFE_ENABLE_THINKING = true;
|
|
169
149
|
// ---------- attachments ----------
|
|
170
150
|
// The observed upload control (a real filechooser on click).
|
|
171
151
|
export const UPLOAD_BUTTON_SELECTOR = '#upload-file-button, button[class*="upload"]';
|
package/dist/hooks.js
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -110,7 +110,7 @@ export function normalizeServerConfig(raw) {
|
|
|
110
110
|
return out;
|
|
111
111
|
}
|
|
112
112
|
/** Candidate config paths, ordered from global to project-local. */
|
|
113
|
-
|
|
113
|
+
async function discoverConfigFiles(workdir) {
|
|
114
114
|
const path = await import('path');
|
|
115
115
|
const os = await import('os');
|
|
116
116
|
const fsMod = await import('fs/promises');
|
|
@@ -127,7 +127,7 @@ export async function discoverConfigFiles(workdir) {
|
|
|
127
127
|
return out;
|
|
128
128
|
}
|
|
129
129
|
/** Read and merge every config file. Later files override earlier ones. */
|
|
130
|
-
|
|
130
|
+
async function loadConfigFiles(files) {
|
|
131
131
|
const merged = {};
|
|
132
132
|
const fsMod = await import('fs/promises');
|
|
133
133
|
for (const f of files) {
|
package/dist/qwen-browser.js
CHANGED
|
@@ -35,7 +35,7 @@ import { extractAnswer, extractTokenUsage, isFinishedWithoutAnswer, isRateLimitT
|
|
|
35
35
|
// contexts on ONE --user-data-dir cannot coexist (SingletonLock), and keeping
|
|
36
36
|
// Qwen's cookies apart also means switching providers never logs the other
|
|
37
37
|
// one out.
|
|
38
|
-
|
|
38
|
+
const QWEN_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'qwen-profile');
|
|
39
39
|
// Escape a string for use inside a RegExp (the model id may carry dots).
|
|
40
40
|
function escapeRegExp(s) {
|
|
41
41
|
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
package/dist/qwen-ui.js
CHANGED
|
@@ -169,8 +169,6 @@ export const LOGIN_SELECTORS = [
|
|
|
169
169
|
'input[autocomplete="username"]',
|
|
170
170
|
];
|
|
171
171
|
// ---------- service statuses / errors ----------
|
|
172
|
-
// A generic "generating" label that must never be taken for an answer.
|
|
173
|
-
export const STATUS_RE = /^(thinking|searching|generating|thinking completed|остановить|stop)[\s.…]*$/i;
|
|
174
172
|
// Qwen's rate-limit / busy notices (best-effort; the toast may be short-lived).
|
|
175
173
|
export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
|
|
176
174
|
export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later/i;
|
package/dist/run-task.js
CHANGED
|
@@ -123,7 +123,7 @@ async function promptOnce(question) {
|
|
|
123
123
|
// Enter sends the buffer to onQueue; an empty Enter is ignored. Backspace,
|
|
124
124
|
// Ctrl+U, Esc sequences (arrows/Home/End/Delete are ignored) and bracketed
|
|
125
125
|
// paste are supported. Returns a cleanup function.
|
|
126
|
-
|
|
126
|
+
function watchInput({ onEscape, onChange, onQueue, } = {}) {
|
|
127
127
|
const stdin = process.stdin;
|
|
128
128
|
if (!stdin.isTTY || !stdin.setRawMode)
|
|
129
129
|
return () => { };
|
package/dist/scheduler.js
CHANGED
|
@@ -26,7 +26,7 @@ export function parseInterval(text) {
|
|
|
26
26
|
return ms;
|
|
27
27
|
}
|
|
28
28
|
/** Largest delay setInterval accepts (32-bit signed milliseconds). */
|
|
29
|
-
|
|
29
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
30
30
|
// Render an interval back to the shortest human form (used in listings).
|
|
31
31
|
// Non-finite input (a cron job whose next fire could not be computed sets
|
|
32
32
|
// nextAt to Infinity) renders as "-" instead of "Infinityh".
|
package/dist/sessions.js
CHANGED
|
@@ -68,7 +68,7 @@ function safeName(id) {
|
|
|
68
68
|
// Save/update a session. workdir is needed so that when launched from the same
|
|
69
69
|
// project we restore exactly its last chat.
|
|
70
70
|
/** Hard cap on the todo checklist stored with a session. */
|
|
71
|
-
|
|
71
|
+
const SESSION_TODOS_CAP = 1000;
|
|
72
72
|
export function saveSession({ id, title = '', workdir = '', todos, provider, }) {
|
|
73
73
|
if (!id)
|
|
74
74
|
return null;
|
package/dist/spinner.js
CHANGED
|
@@ -22,7 +22,7 @@ export function stripEllipsis(phrase) {
|
|
|
22
22
|
// (3 dots) so the trailing hint does not "jump" when the phase changes. Kept in
|
|
23
23
|
// one place so the two status lines animate identically.
|
|
24
24
|
export const DOTS = ['', '.', '..', '...'];
|
|
25
|
-
|
|
25
|
+
const DOTS_PAD = ' ';
|
|
26
26
|
// The colored dot string for a phase (brown, matching the base text).
|
|
27
27
|
export function renderDots(n) {
|
|
28
28
|
const d = DOTS[n] ?? '';
|
package/dist/statusline.js
CHANGED
|
@@ -9,10 +9,10 @@ import { spawn } from 'node:child_process';
|
|
|
9
9
|
// hidden), never an error in the UI.
|
|
10
10
|
// Longest status text that still fits a typical terminal row together with
|
|
11
11
|
// the rest of the status bar.
|
|
12
|
-
|
|
12
|
+
const STATUS_BADGE_MAX = 80;
|
|
13
13
|
/** Clip a status badge to STATUS_BADGE_MAX with an ellipsis (never splits a
|
|
14
14
|
* surrogate pair). */
|
|
15
|
-
|
|
15
|
+
function clipBadge(text, max = STATUS_BADGE_MAX) {
|
|
16
16
|
const s = String(text ?? '');
|
|
17
17
|
if (s.length <= max)
|
|
18
18
|
return s;
|
package/dist/subagent.js
CHANGED
|
@@ -14,11 +14,11 @@ import { markInternalChat } from './sessions.js';
|
|
|
14
14
|
// subagents would need a second page and a shared send scheduler (a change to
|
|
15
15
|
// browser.ts, which owns the live Playwright context) — out of scope here.
|
|
16
16
|
/** Hard cap on iterations for one subagent run. */
|
|
17
|
-
|
|
17
|
+
const SUBAGENT_MAX_ITER = 30;
|
|
18
18
|
// The report is fed back into the PARENT chat as a tool result, so a runaway
|
|
19
19
|
// subagent must not be able to blow up the parent's context with one answer.
|
|
20
20
|
// The tail is trimmed with a marker, not silently dropped.
|
|
21
|
-
|
|
21
|
+
const SUBAGENT_REPORT_CAP = 20_000;
|
|
22
22
|
/** Cap a subagent report before it becomes a parent-context tool result. */
|
|
23
23
|
export function capReport(text, cap = SUBAGENT_REPORT_CAP) {
|
|
24
24
|
if (text.length <= cap)
|
|
@@ -41,7 +41,7 @@ export function capReport(text, cap = SUBAGENT_REPORT_CAP) {
|
|
|
41
41
|
* standard system prompt itself (tool list + call format), and the model needs
|
|
42
42
|
* exactly that to work. The persona only adds role + output contract.
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
function subagentPersona(type) {
|
|
45
45
|
const base = 'You are a SUBAGENT launched by a main coding agent to complete ONE ' +
|
|
46
46
|
'self-contained sub-task in an ISOLATED chat. You do NOT see the main ' +
|
|
47
47
|
"conversation's history, so rely only on the instructions below and on " +
|
package/dist/system-prompt.js
CHANGED
|
@@ -199,7 +199,7 @@ export function renderContextSection(context) {
|
|
|
199
199
|
// of truth for `### <Tool>` blocks; the prose that cannot be derived
|
|
200
200
|
// ("Choosing the right tool", the call formats) stays hard-coded below.
|
|
201
201
|
// Guarded by test/system-prompt-tools.test.ts.
|
|
202
|
-
|
|
202
|
+
function renderToolSection(tools) {
|
|
203
203
|
return tools
|
|
204
204
|
.map((t2) => `### ${t2.name}\n${t2.description}\nParameters: ${JSON.stringify(t2.parameters)}`)
|
|
205
205
|
.join('\n\n');
|
|
@@ -12,10 +12,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
import { useSyncExternalStore } from 'react';
|
|
13
13
|
import { Box, Text, useWindowSize, useInput } from 'ink';
|
|
14
14
|
import { LogList } from './LogView.js';
|
|
15
|
-
import { windowByRows, totalRows
|
|
15
|
+
import { windowByRows, totalRows } from './log-window.js';
|
|
16
16
|
import { StatusBar } from './StatusBar.js';
|
|
17
17
|
import { InputLine } from './InputLine.js';
|
|
18
|
-
import { contentWidth } from '../width.js';
|
|
19
18
|
import { translate } from '../i18n.js';
|
|
20
19
|
import { suggestRowCount } from '../input/suggest.js';
|
|
21
20
|
import { truncateToWidth, visLen } from '../input/layout.js';
|
|
@@ -69,22 +68,13 @@ export function FullScreenApp({ store, inputProps, }) {
|
|
|
69
68
|
// it half empty. `scroll` is measured in ROWS here (0 = newest at the
|
|
70
69
|
// bottom).
|
|
71
70
|
const all = state.log;
|
|
72
|
-
// The
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// short window, so the input rows fell below the last screen row and the
|
|
76
|
-
// caret drifted under the box while a long multi-line buffer was typed.
|
|
77
|
-
const totalFloor = totalRows(all, cols, 'floor');
|
|
71
|
+
// The scroll bound uses the EXACT row count: it is what the content really
|
|
72
|
+
// renders, so the newest row stays reachable. (Reserving the viewport with
|
|
73
|
+
// the floor is a separate concern, handled in `fullScreenLayout`.)
|
|
78
74
|
const total = totalRows(all, cols);
|
|
79
75
|
const maxScroll = Math.max(0, total - logHeight);
|
|
80
76
|
const scroll = Math.max(0, Math.min(state.scroll, maxScroll));
|
|
81
77
|
const visible = windowByRows(all, logHeight, scroll, cols);
|
|
82
|
-
// Rows the WINDOWED content really needs. The reservation above rounds DOWN
|
|
83
|
-
// per entry (floor), so the difference is the slack the content needs. The
|
|
84
|
-
// box takes at most `logHeight` and at least one row; never more, because
|
|
85
|
-
// that is exactly what pushed the input rows off a short screen.
|
|
86
|
-
const filledRows = visible.reduce((n, e) => n + entryRows(e, cols), 0);
|
|
87
|
-
const logBoxHeight = Math.max(1, Math.min(logHeight, Math.max(filledRows, 1)));
|
|
88
78
|
// Scroll the log with PgUp/PgDn; Home/End jump to the very top/bottom. The
|
|
89
79
|
// input line OWNS Home/End while its buffer is non-empty (they move the
|
|
90
80
|
// caret), so the jump only applies to an EMPTY buffer — which is exactly
|
|
@@ -126,4 +116,3 @@ export function FullScreenApp({ store, inputProps, }) {
|
|
|
126
116
|
cursorTop: fsLayout.cursorTop, prompt: state.prompt, locale: state.locale, commands: state.commands, locked: state.locked }), _jsx(Text, { color: "#5b616e", wrap: "truncate", children: ' ' +
|
|
127
117
|
translate(state.locale)(state.mouseScroll ? 'editor.keys_hint' : 'editor.keys_hint_select') })] }));
|
|
128
118
|
}
|
|
129
|
-
void contentWidth;
|
package/dist/tui/LogView.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Static, Box, Text } from 'ink';
|
|
|
3
3
|
import { theme } from '../theme.js';
|
|
4
4
|
/** One log entry. `raw` items already carry ANSI colors and are printed
|
|
5
5
|
* verbatim; structured items get a themed prefix. */
|
|
6
|
-
|
|
6
|
+
function LogRow({ entry }) {
|
|
7
7
|
if (entry.raw) {
|
|
8
8
|
// Pre-rendered (markdown answer, tool preview). Print as-is; Ink passes
|
|
9
9
|
// embedded ANSI escape sequences through to the terminal.
|
package/dist/tui/edit.js
CHANGED
|
@@ -292,7 +292,7 @@ export function searchStart(s) {
|
|
|
292
292
|
};
|
|
293
293
|
return searchRefresh(st, true);
|
|
294
294
|
}
|
|
295
|
-
|
|
295
|
+
function searchRefresh(s, resetToNewest) {
|
|
296
296
|
const q = s.searchQuery;
|
|
297
297
|
const index = resetToNewest ? s.history.length - 1 : s.searchIndex;
|
|
298
298
|
for (let i = index; i >= 0; i--) {
|
package/dist/tui/layout.js
CHANGED
|
@@ -7,7 +7,7 @@ import { inputBoxRows } from '../input/layout.js';
|
|
|
7
7
|
/** Rows of the fixed header: the title row + the divider. */
|
|
8
8
|
export const HEADER_ROWS = 2;
|
|
9
9
|
/** Rows of the status bar above the input box. */
|
|
10
|
-
|
|
10
|
+
const STATUS_ROWS = 1;
|
|
11
11
|
/** Rows of the key hint UNDER the input box. */
|
|
12
12
|
export const HINT_ROWS = 1;
|
|
13
13
|
export function fullScreenLayout(input) {
|
package/dist/tui/widgets.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Text } from 'ink';
|
|
3
3
|
export const UI = {
|
|
4
4
|
accent: '#c8b06a',
|
|
5
5
|
accentDim: '#7a6b45',
|
|
@@ -23,33 +23,7 @@ export function Checkbox({ checked, focused, }) {
|
|
|
23
23
|
export function Select({ value, focused, }) {
|
|
24
24
|
return (_jsxs(Text, { children: [_jsx(Text, { color: UI.off, children: '‹ ' }), _jsx(Text, { color: focused ? UI.accent : UI.text, bold: focused, children: value }), _jsx(Text, { color: UI.off, children: ' ›' })] }));
|
|
25
25
|
}
|
|
26
|
-
/** A framed panel (rounded border) with an optional title bar and footer. */
|
|
27
|
-
export function Panel({ title, subtitle, footer, width, children, }) {
|
|
28
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: UI.border, paddingX: 1, width: width, children: [title ? (_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: UI.title, bold: true, children: title }), subtitle ? _jsx(Text, { color: UI.dim, children: subtitle }) : null] })) : null, title ? (_jsx(Text, { color: "#3a3f4b", children: '─'.repeat(panelInnerCols(width)) })) : null, children, footer ? footer : null] }));
|
|
29
|
-
}
|
|
30
|
-
/** Inner width (minus border + paddingX) the panel divider should span. */
|
|
31
|
-
function panelInnerCols(width) {
|
|
32
|
-
const cols = width || process.stdout.columns || 80;
|
|
33
|
-
return Math.max(1, cols - 6);
|
|
34
|
-
}
|
|
35
|
-
/** A single hint chip: `↑/↓ select`. */
|
|
36
|
-
export function Hint({ keys, label, }) {
|
|
37
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: UI.accent, children: keys }), _jsx(Text, { color: UI.dim, children: ' ' + label })] }));
|
|
38
|
-
}
|
|
39
26
|
/** A row of hint chips separated by ` · `. */
|
|
40
27
|
export function HintBar({ hints, }) {
|
|
41
28
|
return (_jsx(Text, { children: hints.map(([k, l], i) => (_jsxs(Text, { children: [i > 0 ? _jsx(Text, { color: UI.dim, children: ' · ' }) : null, _jsx(Text, { color: UI.accent, children: k }), _jsx(Text, { color: UI.dim, children: ' ' + l })] }, i))) }));
|
|
42
29
|
}
|
|
43
|
-
/**
|
|
44
|
-
* A CODE BLOCK with a syntax-highlighted body and an optional title label.
|
|
45
|
-
*
|
|
46
|
-
* The agent's answers are printed as pre-rendered ANSI (markdown.ts already
|
|
47
|
-
* highlights with cli-highlight), so the live log does NOT use this component —
|
|
48
|
-
* it exists for menus/panels that show a snippet inline. No call site today:
|
|
49
|
-
* the Ctrl+Y / `/copy-code` feature that used it was removed, and plain `/copy`
|
|
50
|
-
* copies the whole answer, so `extractCodeBlocks` (src/code-blocks.ts) is only
|
|
51
|
-
* reached from its own tests.
|
|
52
|
-
*/
|
|
53
|
-
export function CodeBlock({ code, lang, highlighted, width, hint, focused, }) {
|
|
54
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: focused ? UI.accent : UI.border, paddingX: 1, width: width, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: UI.dim, children: lang || 'code' }), hint ? _jsx(Text, { color: UI.accent, children: hint }) : null] }), (highlighted || code).split(String.fromCharCode(10)).map((line, i) => (_jsx(Text, { children: line || ' ' }, i)))] }));
|
|
55
|
-
}
|
package/dist/web.js
CHANGED
|
@@ -97,7 +97,7 @@ export function htmlToText(html) {
|
|
|
97
97
|
// accept) into a uint32, or null when it is not one. Rules: 1-4 parts; each
|
|
98
98
|
// part may be hex (0x…), octal (leading 0) or decimal; the LAST part fills all
|
|
99
99
|
// remaining bytes, so `127.1` == 127.0.0.1 and `2130706433` == 127.0.0.1.
|
|
100
|
-
|
|
100
|
+
function parseLegacyIpv4(host) {
|
|
101
101
|
const h = String(host || '').trim();
|
|
102
102
|
if (!h)
|
|
103
103
|
return null;
|
package/dist/width.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// A longer line is harder to read and the eye loses the start of the next
|
|
9
9
|
// line on a wide terminal, so there is a cap.
|
|
10
10
|
// > 0 = an explicit cap (still never wider than the terminal).
|
|
11
|
-
|
|
11
|
+
const DEFAULT_CONTENT_WIDTH = 100;
|
|
12
12
|
let configured = 0;
|
|
13
13
|
/** Set the configured content width (0 or a non-positive value = auto). */
|
|
14
14
|
export function setContentWidth(width) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.82.
|
|
3
|
+
"version": "2.82.142",
|
|
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": {
|
package/dist/code-blocks.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// PURE extraction of fenced code blocks from a Markdown answer. It returns the
|
|
2
|
-
// RAW fenced text: the highlighted render is full of ANSI and must never be
|
|
3
|
-
// what gets copied out of the TUI.
|
|
4
|
-
/**
|
|
5
|
-
* Extract every fenced code block (``` or ~~~) from `md`, in order. Handles a
|
|
6
|
-
* longer closing fence than the opening one and an unterminated final block
|
|
7
|
-
* (the answer was truncated) — a half block is still copyable.
|
|
8
|
-
*/
|
|
9
|
-
export function extractCodeBlocks(md) {
|
|
10
|
-
const lines = String(md ?? '').split(String.fromCharCode(10));
|
|
11
|
-
const out = [];
|
|
12
|
-
let i = 0;
|
|
13
|
-
let n = 0;
|
|
14
|
-
while (i < lines.length) {
|
|
15
|
-
// The info string may contain a BACKTICK (CommonMark forbids it, models
|
|
16
|
-
// write it anyway: ```` ```ts` ````, ```` ```c++ `std::vector` ````). The
|
|
17
|
-
// old `[^`]*` rejected such a line, so the block's own CLOSING fence was
|
|
18
|
-
// read as an OPENING one and the following prose became a bogus block.
|
|
19
|
-
const m = /^(\s*)(`{3,}|~{3,})\s*(.*)$/.exec(lines[i]);
|
|
20
|
-
if (!m) {
|
|
21
|
-
i++;
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
// A BACKTICK fence cannot carry a backtick in its info (that is what makes
|
|
25
|
-
// it unparseable for CommonMark); treat the trailing text as prose after a
|
|
26
|
-
// plain fence only when the line is exactly a fence. For our purpose the
|
|
27
|
-
// pragmatic rule is: any trailing text is the language label, backticks
|
|
28
|
-
// stripped.
|
|
29
|
-
const fence = m[2];
|
|
30
|
-
const marker = fence[0];
|
|
31
|
-
const minLen = fence.length;
|
|
32
|
-
const lang = (m[3] || '').replace(/`/g, '').trim();
|
|
33
|
-
i++;
|
|
34
|
-
const body = [];
|
|
35
|
-
let closed = false;
|
|
36
|
-
while (i < lines.length) {
|
|
37
|
-
// A closing fence is the SAME marker, at least as long, with nothing
|
|
38
|
-
// else on the line (whitespace allowed).
|
|
39
|
-
const c = new RegExp('^\\s*' + marker + '{' + minLen + ',}\\s*$');
|
|
40
|
-
if (c.test(lines[i])) {
|
|
41
|
-
closed = true;
|
|
42
|
-
i++;
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
body.push(lines[i]);
|
|
46
|
-
i++;
|
|
47
|
-
}
|
|
48
|
-
if (body.length === 0 && closed)
|
|
49
|
-
continue;
|
|
50
|
-
n++;
|
|
51
|
-
out.push({ lang, text: body.join(String.fromCharCode(10)), index: n });
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
}
|