zames_pro 2.75.5 → 2.75.6
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 +19 -0
- package/README.md +10 -4
- package/dist/agent-loop.js +61 -1
- package/dist/browser.js +40 -8
- package/dist/commands.js +0 -1
- package/dist/config.js +8 -3
- package/dist/deepseek-ui.js +1 -3
- package/dist/i18n/common.js +13 -16
- package/dist/i18n/config.js +2 -2
- package/dist/i18n/help.js +2 -6
- package/dist/i18n/spinner.js +7 -1
- package/dist/index.js +0 -49
- package/dist/net-capture.js +43 -11
- package/dist/provider-handoff.js +0 -4
- package/dist/qwen-ui.js +0 -8
- package/dist/run-task.js +24 -1
- package/dist/shell.js +0 -6
- package/dist/system-prompt.js +52 -2
- package/dist/tools.js +33 -0
- package/dist/tui/AskQuestion.js +108 -0
- package/dist/tui/FullScreenApp.js +2 -1
- package/dist/tui/InputLine.js +0 -3
- package/dist/tui/StatusBar.js +0 -9
- package/dist/tui/TuiEditor.js +0 -43
- package/dist/tui/keys.js +0 -2
- package/dist/tui/run-ask.js +22 -0
- package/dist/tui/store.js +0 -2
- package/dist/tui/widgets.js +3 -9
- package/package.json +2 -2
- package/scripts/build-readme.mts +0 -101
- package/scripts/changelog-draft.mts +0 -93
- package/scripts/clean-dist.mts +0 -17
- package/scripts/coverage-gate.mts +0 -112
- package/scripts/ctd-live-driver.mts +0 -51
- package/scripts/ctd-live.tsx +0 -33
- package/scripts/escd-live-driver.mts +0 -37
- package/scripts/eslint-ts6.mts +0 -58
- package/scripts/inline-status-driver.mts +0 -31
- package/scripts/inline-status.tsx +0 -33
- package/scripts/mouse-churn.mts +0 -34
- package/scripts/qwen-models-smoke.mts +0 -117
- package/scripts/qwen-smoke.mts +0 -221
- package/scripts/release.mts +0 -199
- package/scripts/render-demo.mts +0 -154
- package/scripts/render-how-it-works.mts +0 -229
- package/scripts/ru-shot.mts +0 -20
- package/scripts/sched-smoke.mts +0 -150
- package/scripts/self-smoke.mts +0 -309
- package/scripts/tui-comp-shot.mts +0 -63
- package/scripts/tui-components.tsx +0 -62
- package/scripts/tui-config-driver.mts +0 -82
- package/scripts/tui-config-probe.tsx +0 -74
- package/scripts/tui-edit-driver.mts +0 -100
- package/scripts/tui-edit-probe.tsx +0 -38
- package/scripts/tui-editor-probe.tsx +0 -24
- package/scripts/tui-input-driver.mts +0 -54
- package/scripts/tui-input-shot.tsx +0 -59
- package/scripts/tui-log-driver.mts +0 -35
- package/scripts/tui-log-shot.tsx +0 -48
- package/scripts/tui-longpaste-driver.mts +0 -56
- package/scripts/tui-longpaste.tsx +0 -35
- package/scripts/tui-manyselect-shot.mts +0 -34
- package/scripts/tui-manyselect.tsx +0 -17
- package/scripts/tui-mouse-churn-probe.tsx +0 -28
- package/scripts/tui-paste-driver.mts +0 -88
- package/scripts/tui-paste-kinds.tsx +0 -76
- package/scripts/tui-pastetest.mts +0 -38
- package/scripts/tui-probe.tsx +0 -62
- package/scripts/tui-repro.tsx +0 -64
- package/scripts/tui-resize.mts +0 -70
- package/scripts/tui-scroll-driver.mts +0 -53
- package/scripts/tui-scroll-probe.tsx +0 -46
- package/scripts/tui-shot.mts +0 -106
- package/scripts/tui-status-driver.mts +0 -24
- package/scripts/tui-status-shot.tsx +0 -27
- package/scripts/tui-submit.tsx +0 -57
- package/scripts/tui-wheel-driver.mts +0 -61
- package/scripts/tui-wheel-probe.tsx +0 -48
- package/scripts/tui-widgets-driver.mts +0 -20
- package/scripts/tui-widgets.tsx +0 -44
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process'
|
|
2
|
-
|
|
3
|
-
// Soft coverage FLOOR for the PURE logic modules — the ones a regression would
|
|
4
|
-
// silently break without any UI symptom (command formatting, schedule parsing,
|
|
5
|
-
// the raw SSE/model-answer parsing). The floors are deliberately a few points
|
|
6
|
-
// BELOW the current values so ordinary churn does not fail the build; they
|
|
7
|
-
// only catch a real drop (a module losing its tests).
|
|
8
|
-
//
|
|
9
|
-
// Node's test runner has no built-in coverage threshold, so we parse the
|
|
10
|
-
// per-file line % from the coverage report it prints and compare here. This is
|
|
11
|
-
// informational in the sense that the FLOORS are conservative, but a drop
|
|
12
|
-
// below one is a hard failure.
|
|
13
|
-
const FLOORS = {
|
|
14
|
-
'commands.ts': 85,
|
|
15
|
-
'scheduler.ts': 90,
|
|
16
|
-
'net-capture.ts': 90,
|
|
17
|
-
'xml-toolcall.ts': 90,
|
|
18
|
-
'diff.ts': 90,
|
|
19
|
-
'theme.ts': 95,
|
|
20
|
-
// Added later: core logic or already well-tested, so a silent regression
|
|
21
|
-
// that deletes their tests should be caught too. Floors sit a few points
|
|
22
|
-
// below the current values.
|
|
23
|
-
'extraTools.ts': 85,
|
|
24
|
-
'fsutil.ts': 90,
|
|
25
|
-
'sessions.ts': 90,
|
|
26
|
-
'context.ts': 85,
|
|
27
|
-
'markdown.ts': 90,
|
|
28
|
-
'tools.ts': 90,
|
|
29
|
-
'system-prompt.ts': 90,
|
|
30
|
-
'undo.ts': 85,
|
|
31
|
-
'checkpoint.ts': 80,
|
|
32
|
-
'permissions.ts': 85,
|
|
33
|
-
// changelog.ts is mostly comments + type/interface declarations (lines
|
|
34
|
-
// 1-30) that carry no runtime statements, so its LINE % caps well below the
|
|
35
|
-
// other modules even with every executable branch tested. The floor reflects
|
|
36
|
-
// that ceiling (a real drop below it still fails).
|
|
37
|
-
'changelog.ts': 78,
|
|
38
|
-
'attach-refs.ts': 85,
|
|
39
|
-
// C3: the pure /config subcommand dispatcher — silent routing regressions
|
|
40
|
-
// (a wrong subcommand branch) are invisible without tests.
|
|
41
|
-
'config-command.ts': 85,
|
|
42
|
-
// The Ink TUI's PURE halves: the editor state transitions (tui/edit.ts) and
|
|
43
|
-
// the key dispatcher (tui/keys.ts). A silent regression there breaks the
|
|
44
|
-
// operator's editing without any UI test catching it. NOTE: the gate keys on
|
|
45
|
-
// the BASENAME (its regex has no slash), so these are 'edit.ts' / 'keys.ts' /
|
|
46
|
-
// 'store.ts' — unique in the tree today.
|
|
47
|
-
'edit.ts': 85,
|
|
48
|
-
'keys.ts': 85,
|
|
49
|
-
'store.ts': 85,
|
|
50
|
-
'banner.ts': 85,
|
|
51
|
-
'tool-summary.ts': 85,
|
|
52
|
-
// N12: web.ts holds the SSRF guard and the DuckDuckGo/HTML parsers — a
|
|
53
|
-
// regression there is silent. Floors sit below the current values.
|
|
54
|
-
'web.ts': 40,
|
|
55
|
-
'spinner.ts': 55,
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const run = spawnSync(
|
|
59
|
-
'npx',
|
|
60
|
-
['tsx', '--test', '--experimental-test-coverage', 'test/*.test.ts'],
|
|
61
|
-
{
|
|
62
|
-
encoding: 'utf-8',
|
|
63
|
-
shell: process.platform === 'win32',
|
|
64
|
-
maxBuffer: 1024 * 1024 * 64,
|
|
65
|
-
},
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
const out = (run.stdout || '') + (run.stderr || '')
|
|
69
|
-
|
|
70
|
-
if (run.status !== 0) {
|
|
71
|
-
console.error('coverage-gate: test run failed (exit ' + run.status + ')')
|
|
72
|
-
process.exit(1)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const linePct = new Map()
|
|
76
|
-
for (const line of out.split('\n')) {
|
|
77
|
-
// Per-file rows look like:
|
|
78
|
-
// ℹ commands.ts | 94.14 | 78.70 | 93.88 | 40-56 ...
|
|
79
|
-
const m = line.match(/^\s*\S?\s*([A-Za-z0-9._-]+\.ts)\s+\|\s+([\d.]+)\s+\|/)
|
|
80
|
-
if (m) linePct.set(m[1], Number(m[2]))
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const failures = []
|
|
84
|
-
for (const [file, floor] of Object.entries(FLOORS)) {
|
|
85
|
-
const pct = linePct.get(file)
|
|
86
|
-
if (pct === undefined) {
|
|
87
|
-
failures.push(file + ': no coverage row found')
|
|
88
|
-
continue
|
|
89
|
-
}
|
|
90
|
-
if (pct < floor) {
|
|
91
|
-
failures.push(file + ': ' + pct + '% < floor ' + floor + '%')
|
|
92
|
-
} else {
|
|
93
|
-
console.log('coverage-gate: ' + file + ' ' + pct + '% >= ' + floor + '%')
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Print the lowest-covered modules, so a dead or untested module is visible
|
|
98
|
-
// in the CI log even when it has no floor yet. Informational only.
|
|
99
|
-
const sorted = [...linePct.entries()].sort((a, b) => a[1] - b[1]).slice(0, 8)
|
|
100
|
-
if (sorted.length) {
|
|
101
|
-
console.log('coverage-gate: lowest covered modules (informational):')
|
|
102
|
-
for (const [name, pct] of sorted) {
|
|
103
|
-
console.log(' ' + name + ' ' + pct + '%')
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (failures.length) {
|
|
108
|
-
console.error('coverage-gate: FAILED')
|
|
109
|
-
for (const f of failures) console.error(' ' + f)
|
|
110
|
-
process.exit(1)
|
|
111
|
-
}
|
|
112
|
-
console.log('coverage-gate: OK')
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// Drive the live-like probe: type a seed, then send Ctrl+Delete in EVERY
|
|
2
|
-
// encoding a terminal might use, and print the raw bytes + the buffer.
|
|
3
|
-
import { spawn } from 'node:child_process'
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import path from 'node:path'
|
|
6
|
-
|
|
7
|
-
const ESC = String.fromCharCode(27)
|
|
8
|
-
const LOG = '/tmp/ctd-live.log'
|
|
9
|
-
try {
|
|
10
|
-
fs.unlinkSync(LOG)
|
|
11
|
-
} catch {
|
|
12
|
-
/* ignore */
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const child = spawn(
|
|
16
|
-
'script',
|
|
17
|
-
['-qec', 'npx tsx scripts/ctd-live.tsx', '/dev/null'],
|
|
18
|
-
{
|
|
19
|
-
cwd: path.resolve('.'),
|
|
20
|
-
env: { ...process.env, COLUMNS: '80', LINES: '24', PROBE_CTD_LOG: LOG },
|
|
21
|
-
},
|
|
22
|
-
)
|
|
23
|
-
child.stdout.on('data', () => {})
|
|
24
|
-
child.stderr.on('data', () => {})
|
|
25
|
-
|
|
26
|
-
const seq = process.argv[2] || 'xterm'
|
|
27
|
-
const enc: Record<string, string> = {
|
|
28
|
-
xterm: ESC + '[3;5~',
|
|
29
|
-
kitty: ESC + '[3;5u',
|
|
30
|
-
vt: ESC + '[3;5:1~',
|
|
31
|
-
}
|
|
32
|
-
const bytes = enc[seq] ?? seq
|
|
33
|
-
|
|
34
|
-
setTimeout(() => {
|
|
35
|
-
child.stdin.write('foo bar baz') // seed
|
|
36
|
-
setTimeout(() => {
|
|
37
|
-
child.stdin.write(bytes) // the Ctrl+Delete encoding
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
const log = fs.readFileSync(LOG, 'utf-8')
|
|
40
|
-
process.stdout.write('=== ' + seq + ' ===' + String.fromCharCode(10))
|
|
41
|
-
process.stdout.write(
|
|
42
|
-
log
|
|
43
|
-
.split(String.fromCharCode(10))
|
|
44
|
-
.filter((l) => l.startsWith('RAW') || l.startsWith('BUF'))
|
|
45
|
-
.join(String.fromCharCode(10)) + String.fromCharCode(10),
|
|
46
|
-
)
|
|
47
|
-
child.kill('SIGTERM')
|
|
48
|
-
}, 500)
|
|
49
|
-
}, 400)
|
|
50
|
-
}, 5000)
|
|
51
|
-
child.on('close', () => process.exit(0))
|
package/scripts/ctd-live.tsx
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// Minimal LIVE-like probe: the REAL TuiEditor exactly as index.ts builds it
|
|
2
|
-
// (default options), plus a raw-stdin TAP that logs every byte the terminal
|
|
3
|
-
// sends, so we can see EXACTLY what Ctrl+Delete produces on this terminal.
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import { TuiEditor } from '../src/tui/TuiEditor.js'
|
|
6
|
-
|
|
7
|
-
const LOG = process.env.PROBE_CTD_LOG || '/tmp/ctd-live.log'
|
|
8
|
-
fs.writeFileSync(LOG, '')
|
|
9
|
-
const w = (s: string): void => {
|
|
10
|
-
fs.appendFileSync(LOG, s + String.fromCharCode(10))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const ed = new TuiEditor({
|
|
14
|
-
prompt: '❯ › ',
|
|
15
|
-
commands: [],
|
|
16
|
-
locale: 'ru',
|
|
17
|
-
alternateScreen: true,
|
|
18
|
-
})
|
|
19
|
-
ed.onSubmit = () => {}
|
|
20
|
-
ed.onAttach = () => null
|
|
21
|
-
ed.onClipboard = () => Promise.resolve(null)
|
|
22
|
-
ed.start()
|
|
23
|
-
|
|
24
|
-
// Tap raw stdin: hex-dump every chunk so we see the exact bytes.
|
|
25
|
-
process.stdin.on('data', (d: Buffer) => {
|
|
26
|
-
w('RAW ' + d.toString('hex') + ' ' + JSON.stringify(d.toString('utf-8')))
|
|
27
|
-
})
|
|
28
|
-
// Log the buffer after every change.
|
|
29
|
-
ed.store.subscribe(() => {
|
|
30
|
-
const s = ed.store.getSnapshot().edit
|
|
31
|
-
w('BUF ' + JSON.stringify(s.buf) + ' cur=' + s.cursor)
|
|
32
|
-
})
|
|
33
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Send `ESC d` (what VSCode sends for Ctrl+Delete) to the REAL TuiEditor and
|
|
2
|
-
// print the resulting buffer.
|
|
3
|
-
import { spawn } from 'node:child_process'
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import path from 'node:path'
|
|
6
|
-
const ESC = String.fromCharCode(27)
|
|
7
|
-
const LOG = '/tmp/ctd-live.log'
|
|
8
|
-
try {
|
|
9
|
-
fs.unlinkSync(LOG)
|
|
10
|
-
} catch {}
|
|
11
|
-
const child = spawn(
|
|
12
|
-
'script',
|
|
13
|
-
['-qec', 'npx tsx scripts/ctd-live.tsx', '/dev/null'],
|
|
14
|
-
{
|
|
15
|
-
cwd: path.resolve('.'),
|
|
16
|
-
env: { ...process.env, COLUMNS: '80', LINES: '24', PROBE_CTD_LOG: LOG },
|
|
17
|
-
},
|
|
18
|
-
)
|
|
19
|
-
child.stdout.on('data', () => {})
|
|
20
|
-
child.stderr.on('data', () => {})
|
|
21
|
-
setTimeout(() => {
|
|
22
|
-
child.stdin.write('foo bar baz')
|
|
23
|
-
setTimeout(() => {
|
|
24
|
-
child.stdin.write(ESC + 'd')
|
|
25
|
-
setTimeout(() => {
|
|
26
|
-
const log = fs.readFileSync(LOG, 'utf-8')
|
|
27
|
-
process.stdout.write(
|
|
28
|
-
log
|
|
29
|
-
.split(String.fromCharCode(10))
|
|
30
|
-
.filter((l) => l.startsWith('RAW') || l.startsWith('BUF'))
|
|
31
|
-
.join(String.fromCharCode(10)) + String.fromCharCode(10),
|
|
32
|
-
)
|
|
33
|
-
child.kill('SIGTERM')
|
|
34
|
-
}, 500)
|
|
35
|
-
}, 400)
|
|
36
|
-
}, 5000)
|
|
37
|
-
child.on('close', () => process.exit(0))
|
package/scripts/eslint-ts6.mts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// Run ESLint so that typescript-eslint uses the TypeScript 6 API.
|
|
2
|
-
//
|
|
3
|
-
// The project is built with TypeScript 7, but typescript-eslint (8.x) does
|
|
4
|
-
// not support the TS 7 API yet and refuses to start. Its own recommendation is
|
|
5
|
-
// to run "side by side with TypeScript 6": load the TS 6 compiler just for the
|
|
6
|
-
// linter. We do that by aliasing the `typescript` module to the `typescript-6`
|
|
7
|
-
// package for the duration of the ESLint process, without touching the
|
|
8
|
-
// compiler the project actually builds with.
|
|
9
|
-
import { createRequire } from 'module'
|
|
10
|
-
import Module from 'module'
|
|
11
|
-
import path from 'path'
|
|
12
|
-
import { fileURLToPath } from 'url'
|
|
13
|
-
|
|
14
|
-
// Module._resolveFilename is a private Node field (not in @types/node), so it
|
|
15
|
-
// is reached through a small structural type instead of `any`.
|
|
16
|
-
type ModuleWithResolve = typeof Module & {
|
|
17
|
-
_resolveFilename: (request: string, ...rest: unknown[]) => string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const require = createRequire(import.meta.url)
|
|
21
|
-
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
22
|
-
const ts6 = path.join(here, '..', 'node_modules', 'typescript-6')
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
require.resolve(path.join(ts6, 'lib', 'typescript.js'))
|
|
26
|
-
} catch {
|
|
27
|
-
console.error(
|
|
28
|
-
'eslint-ts6: the typescript-6 package is missing. Run: npm i -D typescript-6@npm:typescript@6',
|
|
29
|
-
)
|
|
30
|
-
process.exit(1)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Patch Module._resolveFilename so any require('typescript') resolves to the
|
|
34
|
-
// TS 6 package instead of the TS 7 the project depends on.
|
|
35
|
-
const M = Module as ModuleWithResolve
|
|
36
|
-
const origResolve = M._resolveFilename
|
|
37
|
-
M._resolveFilename = (request: string, ...rest: unknown[]) => {
|
|
38
|
-
if (request === 'typescript') {
|
|
39
|
-
try {
|
|
40
|
-
return require.resolve(path.join(ts6, 'lib', 'typescript.js'))
|
|
41
|
-
} catch {
|
|
42
|
-
return origResolve(request, ...rest)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return origResolve(request, ...rest)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { ESLint } = await import('eslint')
|
|
49
|
-
const eslint = new ESLint({ fix: process.argv.includes('--fix') })
|
|
50
|
-
const results = await eslint.lintFiles(['.'])
|
|
51
|
-
if (process.argv.includes('--fix')) {
|
|
52
|
-
await ESLint.outputFixes(results)
|
|
53
|
-
}
|
|
54
|
-
const formatter = await eslint.loadFormatter('stylish')
|
|
55
|
-
const out = await formatter.format(results)
|
|
56
|
-
if (out) process.stdout.write(out)
|
|
57
|
-
const errors = results.reduce((n, r) => n + r.errorCount, 0)
|
|
58
|
-
process.exit(errors > 0 ? 1 : 0)
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { Term } from '../test/vt100.mts'
|
|
4
|
-
const NL = String.fromCharCode(10)
|
|
5
|
-
const term = new Term(12, 60)
|
|
6
|
-
const child = spawn(
|
|
7
|
-
'script',
|
|
8
|
-
['-qec', 'npx tsx scripts/inline-status.tsx', '/dev/null'],
|
|
9
|
-
{
|
|
10
|
-
cwd: path.resolve('.'),
|
|
11
|
-
env: { ...process.env, COLUMNS: '60', LINES: '12' },
|
|
12
|
-
},
|
|
13
|
-
)
|
|
14
|
-
child.stdout.on('data', (d) => term.write(d.toString('utf-8')))
|
|
15
|
-
child.stderr.on('data', (d) => term.write(d.toString('utf-8')))
|
|
16
|
-
setTimeout(() => {
|
|
17
|
-
const g = term.grid
|
|
18
|
-
if (g[term.r]) g[term.r][term.c] = String.fromCharCode(9673)
|
|
19
|
-
process.stdout.write(
|
|
20
|
-
g
|
|
21
|
-
.map((r) => r.join('').replace(/\s+$/, ''))
|
|
22
|
-
.map((l, n) => String(n).padStart(2) + '| ' + l)
|
|
23
|
-
.join(NL) +
|
|
24
|
-
NL +
|
|
25
|
-
'CURSOR row=' +
|
|
26
|
-
term.r +
|
|
27
|
-
NL,
|
|
28
|
-
)
|
|
29
|
-
child.kill('SIGTERM')
|
|
30
|
-
}, 5000)
|
|
31
|
-
child.on('close', () => process.exit(0))
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { render } from 'ink'
|
|
3
|
-
import { TuiStore } from '../src/tui/store.js'
|
|
4
|
-
import { TuiApp } from '../src/tui/TuiApp.js'
|
|
5
|
-
import { initialEditState } from '../src/tui/edit.js'
|
|
6
|
-
const store = new TuiStore()
|
|
7
|
-
store.set({
|
|
8
|
-
title: 'z',
|
|
9
|
-
version: '1',
|
|
10
|
-
workdir: '/tmp',
|
|
11
|
-
prompt: '> ',
|
|
12
|
-
locale: 'en',
|
|
13
|
-
commands: [],
|
|
14
|
-
edit: { ...initialEditState(), buf: 'abc' },
|
|
15
|
-
statusText: 'STATUS HERE',
|
|
16
|
-
})
|
|
17
|
-
const p = {
|
|
18
|
-
onSubmit: () => {},
|
|
19
|
-
onEscape: () => {},
|
|
20
|
-
onCtrlC: () => {},
|
|
21
|
-
onCopyCode: () => {},
|
|
22
|
-
onAttach: () => null,
|
|
23
|
-
onClipboard: () => Promise.resolve(null),
|
|
24
|
-
onHistoryChange: () => {},
|
|
25
|
-
onClearScreen: () => {},
|
|
26
|
-
attachments: [],
|
|
27
|
-
onPastesChange: () => {},
|
|
28
|
-
}
|
|
29
|
-
render(React.createElement(TuiApp, { store, inputProps: p }), {
|
|
30
|
-
exitOnCtrlC: false,
|
|
31
|
-
patchConsole: false,
|
|
32
|
-
})
|
|
33
|
-
setInterval(() => {}, 1 << 30)
|
package/scripts/mouse-churn.mts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Prove the mouse-mode churn is ZERO: mount the REAL TuiEditor, force several
|
|
2
|
-
// store updates (like the 1s badge refresh does), and COUNT how many times the
|
|
3
|
-
// raw stdout receives the mouse-mode sequences. With the effect depending on
|
|
4
|
-
// an inline onWheel, the count grew per render (the wheel-vs-history bug);
|
|
5
|
-
// with the ref fix, each mode byte is written ONCE.
|
|
6
|
-
//
|
|
7
|
-
// Exit code 0 = OK, 1 = churn detected. Prints a one-line verdict so the pty
|
|
8
|
-
// test can assert on it.
|
|
9
|
-
|
|
10
|
-
import { spawn } from 'node:child_process'
|
|
11
|
-
import path from 'node:path'
|
|
12
|
-
|
|
13
|
-
const child = spawn(
|
|
14
|
-
'script',
|
|
15
|
-
['-qec', 'npx tsx scripts/tui-mouse-churn-probe.tsx', '/dev/null'],
|
|
16
|
-
{
|
|
17
|
-
cwd: path.resolve('.'),
|
|
18
|
-
env: { ...process.env, COLUMNS: '80', LIMES: '24' },
|
|
19
|
-
},
|
|
20
|
-
)
|
|
21
|
-
let out = ''
|
|
22
|
-
child.stdout.on('data', (d) => (out += d.toString('utf-8')))
|
|
23
|
-
child.stderr.on('data', (d) => (out += d.toString('utf-8')))
|
|
24
|
-
setTimeout(() => child.kill('SIGTERM'), 8000)
|
|
25
|
-
child.on('close', () => {
|
|
26
|
-
const count = (seq: string): number => out.split(seq).length - 1
|
|
27
|
-
const on = count('\u001b[?1000h')
|
|
28
|
-
const altOff = count('\u001b[?1007l')
|
|
29
|
-
const altOn = count('\u001b[?1007h')
|
|
30
|
-
console.log('MOUSE_CHURN 1000h=' + on + ' 1007l=' + altOff + ' 1007h=' + altOn)
|
|
31
|
-
const ok = on === 1 && altOff === 1 && altOn === 0
|
|
32
|
-
console.log(ok ? 'MOUSE_CHURN_OK' : 'MOUSE_CHURN_FAIL')
|
|
33
|
-
process.exit(ok ? 0 : 1)
|
|
34
|
-
})
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// Qwen model-switching self-smoke: launch a REAL isolated QwenBrowser, list
|
|
2
|
-
// the model catalogue, then SWITCH to several models and ask a question with
|
|
3
|
-
// each — the operator explicitly asked that model switching be exercised.
|
|
4
|
-
//
|
|
5
|
-
// Isolation mirrors qwen-smoke.mts: a throwaway HOME + a read-only COPY of the
|
|
6
|
-
// real qwen-profile (the live profile is never opened).
|
|
7
|
-
//
|
|
8
|
-
// Usage: npx tsx scripts/qwen-models-smoke.mts [--headed] [--all]
|
|
9
|
-
|
|
10
|
-
import fs from 'fs'
|
|
11
|
-
import path from 'path'
|
|
12
|
-
import os from 'os'
|
|
13
|
-
|
|
14
|
-
const HEADED = process.argv.includes('--headed')
|
|
15
|
-
const ALL = process.argv.includes('--all')
|
|
16
|
-
const REAL_HOME = os.homedir()
|
|
17
|
-
const PROFILE_SRC = path.join(REAL_HOME, '.zames', 'qwen-profile')
|
|
18
|
-
|
|
19
|
-
if (!process.env.ZAMES_QWEN_MODELS_HOME) {
|
|
20
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'zames-qwen-models-'))
|
|
21
|
-
process.env.HOME = tmp
|
|
22
|
-
process.env.ZAMES_QWEN_MODELS_HOME = tmp
|
|
23
|
-
fs.mkdirSync(path.join(tmp, '.zames'), { recursive: true })
|
|
24
|
-
fs.mkdirSync(path.join(tmp, '.cache'), { recursive: true })
|
|
25
|
-
const realCfg = path.join(REAL_HOME, '.zames', 'config.json')
|
|
26
|
-
if (fs.existsSync(realCfg)) {
|
|
27
|
-
fs.copyFileSync(realCfg, path.join(tmp, '.zames', 'config.json'))
|
|
28
|
-
}
|
|
29
|
-
const realPw = path.join(REAL_HOME, '.cache', 'ms-playwright')
|
|
30
|
-
if (fs.existsSync(realPw)) {
|
|
31
|
-
fs.symlinkSync(realPw, path.join(tmp, '.cache', 'ms-playwright'))
|
|
32
|
-
}
|
|
33
|
-
if (fs.existsSync(PROFILE_SRC)) {
|
|
34
|
-
fs.cpSync(PROFILE_SRC, path.join(tmp, '.zames', 'qwen-profile'), {
|
|
35
|
-
recursive: true,
|
|
36
|
-
filter: (src) => !/^Singleton/.test(path.basename(src)),
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { QwenBrowser } = await import('../src/qwen-browser.ts')
|
|
42
|
-
const { loadConfig } = await import('../src/config.ts')
|
|
43
|
-
const cfg = loadConfig()
|
|
44
|
-
|
|
45
|
-
const results: Array<{ name: string; ok: boolean; note?: string }> = []
|
|
46
|
-
const record = (name: string, ok: boolean, note?: string): void => {
|
|
47
|
-
results.push({ name, ok, note })
|
|
48
|
-
console.log((ok ? 'PASS ' : 'FAIL ') + name + (note ? ' — ' + note : ''))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const browser = new QwenBrowser({
|
|
52
|
-
headless: !HEADED,
|
|
53
|
-
debug: false,
|
|
54
|
-
channel: cfg.browserChannel,
|
|
55
|
-
auth: cfg.browser.auth,
|
|
56
|
-
locale: 'ru',
|
|
57
|
-
minSendIntervalMs: 0,
|
|
58
|
-
answerTimeoutMs: 180000,
|
|
59
|
-
webSearch: false,
|
|
60
|
-
deepThinking: false,
|
|
61
|
-
})
|
|
62
|
-
browser.onNotice = (t: string) => console.log(' [notice] ' + t)
|
|
63
|
-
|
|
64
|
-
async function main(): Promise<void> {
|
|
65
|
-
await browser.launch()
|
|
66
|
-
const timedOut = await Promise.race([
|
|
67
|
-
browser.waitForLogin().then(() => false),
|
|
68
|
-
new Promise<boolean>((r) => setTimeout(() => r(true), 60000)),
|
|
69
|
-
])
|
|
70
|
-
if (timedOut) {
|
|
71
|
-
record('login', false, 'timed out')
|
|
72
|
-
await browser.close().catch(() => {})
|
|
73
|
-
process.exit(2)
|
|
74
|
-
}
|
|
75
|
-
record('login', true)
|
|
76
|
-
|
|
77
|
-
const models = await browser.listModels()
|
|
78
|
-
record(
|
|
79
|
-
'listModels',
|
|
80
|
-
models.length > 0,
|
|
81
|
-
'n=' + models.length + ' ' + models.map((m) => m.id).join(','),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
// Switch through a few models and ask with each. Prefer distinct vendors so
|
|
85
|
-
// the dropdown selection really changes the backend.
|
|
86
|
-
const wanted = ALL ? models : models.slice(0, 3)
|
|
87
|
-
for (const m of wanted) {
|
|
88
|
-
try {
|
|
89
|
-
await browser.newChat()
|
|
90
|
-
browser.model = m.id
|
|
91
|
-
const ans = await browser.ask('Reply with exactly: OK', { agent: true })
|
|
92
|
-
const ok = /OK/i.test(ans)
|
|
93
|
-
record('model:' + m.id, ok, ok ? '' : 'got: ' + ans.slice(0, 60))
|
|
94
|
-
} catch (e) {
|
|
95
|
-
record('model:' + m.id, false, (e as Error).message)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
await browser.close().catch(() => {})
|
|
100
|
-
const failed = results.filter((r) => !r.ok)
|
|
101
|
-
console.log(
|
|
102
|
-
'\nqwen-models-smoke: ' +
|
|
103
|
-
(results.length - failed.length) +
|
|
104
|
-
'/' +
|
|
105
|
-
results.length +
|
|
106
|
-
' passed',
|
|
107
|
-
)
|
|
108
|
-
if (!process.env.ZAMES_QWEN_MODELS_KEEP) {
|
|
109
|
-
fs.rmSync(process.env.ZAMES_QWEN_MODELS_HOME as string, {
|
|
110
|
-
recursive: true,
|
|
111
|
-
force: true,
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
process.exit(failed.length ? 1 : 0)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
void main()
|