zames_pro 2.75.5 → 2.76.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 +25 -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/context.js +99 -0
- 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 +48 -6
- package/dist/i18n/spinner.js +7 -1
- package/dist/index.js +10 -50
- 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 +4 -43
- package/dist/tui/edit.js +13 -22
- package/dist/tui/keys.js +12 -14
- 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,38 +0,0 @@
|
|
|
1
|
-
// Mount the REAL TuiEditor and log the editor BUFFER after every change, so a
|
|
2
|
-
// pty driver can send keys and assert what the buffer became (word deletion,
|
|
3
|
-
// newlines, history) instead of guessing from bytes. The log is one JSON line
|
|
4
|
-
// per change: {buf, cursor}.
|
|
5
|
-
|
|
6
|
-
import fs from 'node:fs'
|
|
7
|
-
import { TuiEditor } from '../src/tui/TuiEditor.js'
|
|
8
|
-
|
|
9
|
-
const LOG = process.env.PROBE_EDIT_LOG || '/tmp/edit-probe.log'
|
|
10
|
-
const SEED = process.env.PROBE_EDIT_SEED || ''
|
|
11
|
-
fs.writeFileSync(LOG, '')
|
|
12
|
-
|
|
13
|
-
const ed = new TuiEditor({
|
|
14
|
-
prompt: '> ',
|
|
15
|
-
commands: [],
|
|
16
|
-
locale: 'en',
|
|
17
|
-
alternateScreen: process.env.PROBE_INLINE !== '1',
|
|
18
|
-
})
|
|
19
|
-
ed.onSubmit = () => {}
|
|
20
|
-
ed.onAttach = () => null
|
|
21
|
-
ed.onClipboard = () => Promise.resolve(null)
|
|
22
|
-
|
|
23
|
-
ed.store.subscribe(() => {
|
|
24
|
-
const s = ed.store.getSnapshot().edit
|
|
25
|
-
fs.appendFileSync(
|
|
26
|
-
LOG,
|
|
27
|
-
JSON.stringify({ buf: s.buf, cursor: s.cursor }) + String.fromCharCode(10),
|
|
28
|
-
)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
ed.start()
|
|
32
|
-
if (SEED) {
|
|
33
|
-
// Type the seed through the store so the buffer starts non-empty.
|
|
34
|
-
ed.store.update((snap) => ({
|
|
35
|
-
edit: { ...snap.edit, buf: SEED, cursor: Array.from(SEED).length },
|
|
36
|
-
}))
|
|
37
|
-
}
|
|
38
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// Mount the REAL TuiEditor (the adapter index.ts uses), so the pty resize test
|
|
2
|
-
// exercises TuiEditor.installResizeHandler (unmount+remount), NOT just Ink's
|
|
3
|
-
// built-in erase. The raw FullScreenApp probe bypasses that handler.
|
|
4
|
-
|
|
5
|
-
import { TuiEditor } from '../src/tui/TuiEditor.js'
|
|
6
|
-
|
|
7
|
-
const ed = new TuiEditor({
|
|
8
|
-
prompt: '\u2794 zames_pro \u203a ',
|
|
9
|
-
commands: [
|
|
10
|
-
{ name: '/help', description: 'show help' },
|
|
11
|
-
{ name: '/new', description: 'new chat' },
|
|
12
|
-
],
|
|
13
|
-
locale: 'en',
|
|
14
|
-
alternateScreen: true,
|
|
15
|
-
})
|
|
16
|
-
ed.setHeader?.({
|
|
17
|
-
title: 'zames_pro',
|
|
18
|
-
version: '2.74.0',
|
|
19
|
-
workdir: '/workspaces/zames/zames_pro',
|
|
20
|
-
})
|
|
21
|
-
ed.start()
|
|
22
|
-
ed.printAbove('log line one')
|
|
23
|
-
ed.printAbove('log line two')
|
|
24
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// Screenshot the REAL FullScreenApp input box for a given buffer/cursor, and
|
|
2
|
-
// print the VT100 grid WITH a cursor marker (the real terminal cursor cell).
|
|
3
|
-
import { spawn } from 'node:child_process'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { Term } from '../test/vt100.mts'
|
|
6
|
-
|
|
7
|
-
const NL = String.fromCharCode(10)
|
|
8
|
-
function arg(name: string, def = ''): string {
|
|
9
|
-
const hit = process.argv.find((a) => a.startsWith('--' + name + '='))
|
|
10
|
-
return hit ? hit.slice(name.length + 3) : def
|
|
11
|
-
}
|
|
12
|
-
const cols = Number(arg('cols', '80'))
|
|
13
|
-
const rows = Number(arg('rows', '24'))
|
|
14
|
-
const buf = arg('buf', '')
|
|
15
|
-
.replace(/\\n/g, NL)
|
|
16
|
-
.replace(/\\t/g, String.fromCharCode(9))
|
|
17
|
-
const cursor = arg('cursor', '')
|
|
18
|
-
|
|
19
|
-
const term = new Term(rows, cols)
|
|
20
|
-
let stderr = ''
|
|
21
|
-
const child = spawn(
|
|
22
|
-
'script',
|
|
23
|
-
['-qec', 'npx tsx scripts/tui-input-shot.tsx', '/dev/null'],
|
|
24
|
-
{
|
|
25
|
-
cwd: path.resolve('.'),
|
|
26
|
-
env: {
|
|
27
|
-
...process.env,
|
|
28
|
-
COLUMNS: String(cols),
|
|
29
|
-
LINES: String(rows),
|
|
30
|
-
PROBE_BUF: buf,
|
|
31
|
-
...(cursor ? { PROBE_CURSOR: cursor } : {}),
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
)
|
|
35
|
-
child.stdout.on('data', (d) => term.write(d.toString('utf-8')))
|
|
36
|
-
child.stderr.on('data', (d) => (stderr += d.toString('utf-8')))
|
|
37
|
-
setTimeout(() => {
|
|
38
|
-
const cr = term.r
|
|
39
|
-
const cc = term.c
|
|
40
|
-
// Mark by GRID CELL, not by string index: a wide char occupies two grid
|
|
41
|
-
// cells, so slicing the rendered string would place the marker wrong.
|
|
42
|
-
const grid = term.grid
|
|
43
|
-
if (grid[cr] && grid[cr][cc] !== undefined)
|
|
44
|
-
grid[cr][cc] = String.fromCharCode(9673)
|
|
45
|
-
const lines = grid.map((row) => row.join('').replace(/\s+$/, ''))
|
|
46
|
-
process.stdout.write(
|
|
47
|
-
lines.map((l, n) => String(n).padStart(2) + '| ' + l).join(NL) + NL,
|
|
48
|
-
)
|
|
49
|
-
process.stdout.write('CURSOR row=' + cr + ' col=' + cc + NL)
|
|
50
|
-
const lay = stderr.split(NL).find((l) => l.startsWith('LAYOUT'))
|
|
51
|
-
if (lay) process.stdout.write(lay + NL)
|
|
52
|
-
child.kill('SIGTERM')
|
|
53
|
-
}, 5000)
|
|
54
|
-
child.on('close', () => process.exit(0))
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// Mount the REAL FullScreenApp with a buffer set from env, so the input box
|
|
2
|
-
// rendering (wrapping, cursor, CJK width, paste markers) can be screenshotted.
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { render } from 'ink'
|
|
5
|
-
import { TuiStore } from '../src/tui/store.js'
|
|
6
|
-
import { FullScreenApp } from '../src/tui/FullScreenApp.js'
|
|
7
|
-
import { initialEditState } from '../src/tui/edit.js'
|
|
8
|
-
import { layoutInput, inputInnerCols } from '../src/input/layout.js'
|
|
9
|
-
|
|
10
|
-
const buf = process.env.PROBE_BUF || ''
|
|
11
|
-
const cursor = process.env.PROBE_CURSOR
|
|
12
|
-
? Number(process.env.PROBE_CURSOR)
|
|
13
|
-
: buf.length
|
|
14
|
-
const store = new TuiStore()
|
|
15
|
-
store.set({
|
|
16
|
-
title: 'zames',
|
|
17
|
-
version: '2.74',
|
|
18
|
-
workdir: '/workspaces/zames/zames_pro',
|
|
19
|
-
prompt: '\u2794 zames \u203a ',
|
|
20
|
-
locale: 'en',
|
|
21
|
-
commands: [],
|
|
22
|
-
edit: { ...initialEditState(), buf, cursor },
|
|
23
|
-
})
|
|
24
|
-
store.pushLog('system', 'log line one')
|
|
25
|
-
const inputProps = {
|
|
26
|
-
onSubmit: () => {},
|
|
27
|
-
onEscape: () => {},
|
|
28
|
-
onCtrlC: () => {},
|
|
29
|
-
onCopyCode: () => {},
|
|
30
|
-
onAttach: () => null,
|
|
31
|
-
onClipboard: () => Promise.resolve(null),
|
|
32
|
-
onHistoryChange: () => {},
|
|
33
|
-
onClearScreen: () => {},
|
|
34
|
-
attachments: [],
|
|
35
|
-
onPastesChange: () => {},
|
|
36
|
-
}
|
|
37
|
-
// Print the expected layout to stderr so it can be compared with the screen.
|
|
38
|
-
const cols = Number(process.env.COLUMNS || 80)
|
|
39
|
-
const lay = layoutInput(
|
|
40
|
-
'\u2794 zames \u203a ',
|
|
41
|
-
buf,
|
|
42
|
-
cursor,
|
|
43
|
-
inputInnerCols(cols),
|
|
44
|
-
)
|
|
45
|
-
process.stderr.write(
|
|
46
|
-
'LAYOUT rows=' +
|
|
47
|
-
lay.rows.length +
|
|
48
|
-
' cursorRow=' +
|
|
49
|
-
lay.cursorRow +
|
|
50
|
-
' cursorCol=' +
|
|
51
|
-
lay.cursorCol +
|
|
52
|
-
String.fromCharCode(10),
|
|
53
|
-
)
|
|
54
|
-
render(React.createElement(FullScreenApp, { store, inputProps }), {
|
|
55
|
-
exitOnCtrlC: false,
|
|
56
|
-
patchConsole: false,
|
|
57
|
-
alternateScreen: true,
|
|
58
|
-
})
|
|
59
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// Screenshot the FullScreenApp log for a given content spec.
|
|
2
|
-
import { spawn } from 'node:child_process'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { Term } from '../test/vt100.mts'
|
|
5
|
-
|
|
6
|
-
const NL = String.fromCharCode(10)
|
|
7
|
-
function arg(n: string, d = ''): string {
|
|
8
|
-
const h = process.argv.find((a) => a.startsWith('--' + n + '='))
|
|
9
|
-
return h ? h.slice(n.length + 3) : d
|
|
10
|
-
}
|
|
11
|
-
const cols = Number(arg('cols', '80'))
|
|
12
|
-
const rows = Number(arg('rows', '24'))
|
|
13
|
-
const spec = arg('log', 'empty')
|
|
14
|
-
|
|
15
|
-
const term = new Term(rows, cols)
|
|
16
|
-
const child = spawn(
|
|
17
|
-
'script',
|
|
18
|
-
['-qec', 'npx tsx scripts/tui-log-shot.tsx', '/dev/null'],
|
|
19
|
-
{
|
|
20
|
-
cwd: path.resolve('.'),
|
|
21
|
-
env: {
|
|
22
|
-
...process.env,
|
|
23
|
-
COLUMNS: String(cols),
|
|
24
|
-
LINES: String(rows),
|
|
25
|
-
PROBE_LOG: spec,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
)
|
|
29
|
-
child.stdout.on('data', (d) => term.write(d.toString('utf-8')))
|
|
30
|
-
child.stderr.on('data', (d) => term.write(d.toString('utf-8')))
|
|
31
|
-
setTimeout(() => {
|
|
32
|
-
process.stdout.write(term.render() + NL)
|
|
33
|
-
child.kill('SIGTERM')
|
|
34
|
-
}, 5000)
|
|
35
|
-
child.on('close', () => process.exit(0))
|
package/scripts/tui-log-shot.tsx
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// Mount the REAL FullScreenApp with a log driven by PROBE_LOG (JSON entries),
|
|
2
|
-
// so empty/overflowing/long-wrapped logs can be screenshotted.
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { render } from 'ink'
|
|
5
|
-
import { TuiStore } from '../src/tui/store.js'
|
|
6
|
-
import { FullScreenApp } from '../src/tui/FullScreenApp.js'
|
|
7
|
-
import { initialEditState } from '../src/tui/edit.js'
|
|
8
|
-
|
|
9
|
-
const store = new TuiStore()
|
|
10
|
-
store.set({
|
|
11
|
-
title: 'zames',
|
|
12
|
-
version: '2.74',
|
|
13
|
-
workdir: '/tmp',
|
|
14
|
-
prompt: '\u2794 zames \u203a ',
|
|
15
|
-
locale: 'en',
|
|
16
|
-
commands: [],
|
|
17
|
-
edit: { ...initialEditState(), buf: '' },
|
|
18
|
-
})
|
|
19
|
-
const spec = process.env.PROBE_LOG || 'empty'
|
|
20
|
-
if (spec === 'empty') {
|
|
21
|
-
// nothing
|
|
22
|
-
} else if (spec === 'few') {
|
|
23
|
-
store.pushLog('user', 'hello')
|
|
24
|
-
store.pushLog('assistant', 'hi there')
|
|
25
|
-
} else if (spec === 'many') {
|
|
26
|
-
for (let i = 0; i < 100; i++) store.pushLog('system', 'log line ' + i)
|
|
27
|
-
} else if (spec === 'long') {
|
|
28
|
-
store.pushLog('assistant', 'L'.repeat(300))
|
|
29
|
-
store.pushLog('system', 'after long')
|
|
30
|
-
}
|
|
31
|
-
const inputProps = {
|
|
32
|
-
onSubmit: () => {},
|
|
33
|
-
onEscape: () => {},
|
|
34
|
-
onCtrlC: () => {},
|
|
35
|
-
onCopyCode: () => {},
|
|
36
|
-
onAttach: () => null,
|
|
37
|
-
onClipboard: () => Promise.resolve(null),
|
|
38
|
-
onHistoryChange: () => {},
|
|
39
|
-
onClearScreen: () => {},
|
|
40
|
-
attachments: [],
|
|
41
|
-
onPastesChange: () => {},
|
|
42
|
-
}
|
|
43
|
-
render(React.createElement(FullScreenApp, { store, inputProps }), {
|
|
44
|
-
exitOnCtrlC: false,
|
|
45
|
-
patchConsole: false,
|
|
46
|
-
alternateScreen: true,
|
|
47
|
-
})
|
|
48
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Paste a long bracketed block into the REAL TuiEditor under a pty, screenshot
|
|
2
|
-
// the screen (marker shown), press Enter, and print the submit log.
|
|
3
|
-
import { spawn } from 'node:child_process'
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import path from 'node:path'
|
|
6
|
-
import { Term } from '../test/vt100.mts'
|
|
7
|
-
const ESC = String.fromCharCode(27)
|
|
8
|
-
const CR = String.fromCharCode(13)
|
|
9
|
-
const NL = String.fromCharCode(10)
|
|
10
|
-
const LOG = '/tmp/longpaste.log'
|
|
11
|
-
try {
|
|
12
|
-
fs.unlinkSync(LOG)
|
|
13
|
-
} catch {
|
|
14
|
-
/* ignore */
|
|
15
|
-
}
|
|
16
|
-
const lines = Number(process.argv[2] || '25')
|
|
17
|
-
const body = Array.from(
|
|
18
|
-
{ length: lines },
|
|
19
|
-
(_, i) => 'line ' + i + ' of the pasted block',
|
|
20
|
-
).join(NL)
|
|
21
|
-
const cols = Number(process.argv[3] || '80')
|
|
22
|
-
const rows = Number(process.argv[4] || '24')
|
|
23
|
-
const term = new Term(rows, cols)
|
|
24
|
-
const child = spawn(
|
|
25
|
-
'script',
|
|
26
|
-
['-qec', 'npx tsx scripts/tui-longpaste.tsx', '/dev/null'],
|
|
27
|
-
{
|
|
28
|
-
cwd: path.resolve('.'),
|
|
29
|
-
env: {
|
|
30
|
-
...process.env,
|
|
31
|
-
COLUMNS: String(cols),
|
|
32
|
-
LINES: String(rows),
|
|
33
|
-
PROBE_PASTE_LOG: LOG,
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
)
|
|
37
|
-
child.stdout.on('data', (d) => term.write(d.toString('utf-8')))
|
|
38
|
-
child.stderr.on('data', (d) => term.write(d.toString('utf-8')))
|
|
39
|
-
setTimeout(() => {
|
|
40
|
-
child.stdin.write(ESC + '[200~' + body + ESC + '[201~')
|
|
41
|
-
setTimeout(() => {
|
|
42
|
-
process.stdout.write('=== SCREEN (after paste) ===' + NL)
|
|
43
|
-
process.stdout.write(term.render() + NL)
|
|
44
|
-
child.stdin.write(CR)
|
|
45
|
-
setTimeout(() => {
|
|
46
|
-
process.stdout.write('=== LOG ===' + NL)
|
|
47
|
-
try {
|
|
48
|
-
process.stdout.write(fs.readFileSync(LOG, 'utf-8'))
|
|
49
|
-
} catch {
|
|
50
|
-
/* ignore */
|
|
51
|
-
}
|
|
52
|
-
child.kill('SIGTERM')
|
|
53
|
-
}, 600)
|
|
54
|
-
}, 600)
|
|
55
|
-
}, 5000)
|
|
56
|
-
child.on('close', () => process.exit(0))
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// Mount the REAL TuiEditor and log every onSubmit (the EXPANDED text), so a pty
|
|
2
|
-
// can paste a LONG multi-line block and prove the agent receives all of it —
|
|
3
|
-
// not the [Pasted lines#N] marker.
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import { TuiEditor } from '../src/tui/TuiEditor.js'
|
|
6
|
-
const LOG = process.env.PROBE_PASTE_LOG || '/tmp/longpaste.log'
|
|
7
|
-
fs.writeFileSync(LOG, '')
|
|
8
|
-
const ed = new TuiEditor({
|
|
9
|
-
prompt: '> ',
|
|
10
|
-
commands: [],
|
|
11
|
-
locale: 'en',
|
|
12
|
-
alternateScreen: true,
|
|
13
|
-
})
|
|
14
|
-
ed.onSubmit = (text: string) => {
|
|
15
|
-
fs.appendFileSync(
|
|
16
|
-
LOG,
|
|
17
|
-
'SUBMIT_LEN=' +
|
|
18
|
-
text.length +
|
|
19
|
-
' LINES=' +
|
|
20
|
-
text.split(String.fromCharCode(10)).length +
|
|
21
|
-
String.fromCharCode(10),
|
|
22
|
-
)
|
|
23
|
-
fs.appendFileSync(
|
|
24
|
-
LOG,
|
|
25
|
-
'HEAD=' + JSON.stringify(text.slice(0, 40)) + String.fromCharCode(10),
|
|
26
|
-
)
|
|
27
|
-
fs.appendFileSync(
|
|
28
|
-
LOG,
|
|
29
|
-
'TAIL=' + JSON.stringify(text.slice(-40)) + String.fromCharCode(10),
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
ed.onAttach = () => null
|
|
33
|
-
ed.onClipboard = () => Promise.resolve(null)
|
|
34
|
-
ed.start()
|
|
35
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,34 +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 ESC = String.fromCharCode(27)
|
|
6
|
-
const keys = (process.argv[2] || '')
|
|
7
|
-
.split(',')
|
|
8
|
-
.filter(Boolean)
|
|
9
|
-
.map((k) => (k === 'UP' ? ESC + '[A' : k === 'DOWN' ? ESC + '[B' : k))
|
|
10
|
-
const term = new Term(20, 60)
|
|
11
|
-
const child = spawn(
|
|
12
|
-
'script',
|
|
13
|
-
['-qec', 'npx tsx scripts/tui-manyselect.tsx', '/dev/null'],
|
|
14
|
-
{
|
|
15
|
-
cwd: path.resolve('.'),
|
|
16
|
-
env: { ...process.env, COLUMNS: '60', LINES: '20' },
|
|
17
|
-
},
|
|
18
|
-
)
|
|
19
|
-
child.stdout.on('data', (d) => term.write(d.toString('utf-8')))
|
|
20
|
-
child.stderr.on('data', (d) => term.write(d.toString('utf-8')))
|
|
21
|
-
let i = 0
|
|
22
|
-
const tick = (): void => {
|
|
23
|
-
if (i < keys.length) {
|
|
24
|
-
child.stdin.write(keys[i])
|
|
25
|
-
i++
|
|
26
|
-
setTimeout(tick, 250)
|
|
27
|
-
} else
|
|
28
|
-
setTimeout(() => {
|
|
29
|
-
process.stdout.write(term.render() + NL)
|
|
30
|
-
child.kill('SIGTERM')
|
|
31
|
-
}, 400)
|
|
32
|
-
}
|
|
33
|
-
setTimeout(tick, 4500)
|
|
34
|
-
child.on('close', () => process.exit(0))
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { render } from 'ink'
|
|
3
|
-
import { SelectList } from '../src/tui/SelectList.js'
|
|
4
|
-
const items = Array.from({ length: 30 }, (_, i) => ({
|
|
5
|
-
value: 'm' + i,
|
|
6
|
-
label: 'модель-' + i,
|
|
7
|
-
}))
|
|
8
|
-
render(
|
|
9
|
-
React.createElement(SelectList, {
|
|
10
|
-
title: 'Много',
|
|
11
|
-
items,
|
|
12
|
-
initial: 'm15',
|
|
13
|
-
onSelect: () => {},
|
|
14
|
-
}),
|
|
15
|
-
{ exitOnCtrlC: false, patchConsole: false },
|
|
16
|
-
)
|
|
17
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Mount the REAL TuiEditor and tick the store every 150ms (like the 1s badge
|
|
2
|
-
// timer does), so the raw stdout can be scanned for how many times the
|
|
3
|
-
// mouse-mode sequences are written. A well-behaved hook writes them ONCE on
|
|
4
|
-
// mount; a hook whose effect depends on an inline `onWheel` re-runs on every
|
|
5
|
-
// render and writes `1007h` (alternate-scroll ON => wheel becomes history)
|
|
6
|
-
// again and again.
|
|
7
|
-
|
|
8
|
-
import { TuiEditor } from '../src/tui/TuiEditor.js'
|
|
9
|
-
|
|
10
|
-
const ed = new TuiEditor({
|
|
11
|
-
prompt: '> ',
|
|
12
|
-
commands: [],
|
|
13
|
-
locale: 'en',
|
|
14
|
-
alternateScreen: true,
|
|
15
|
-
})
|
|
16
|
-
ed.onSubmit = () => {}
|
|
17
|
-
ed.onAttach = () => null
|
|
18
|
-
ed.onClipboard = () => Promise.resolve(null)
|
|
19
|
-
ed.start()
|
|
20
|
-
|
|
21
|
-
let n = 0
|
|
22
|
-
const timer = setInterval(() => {
|
|
23
|
-
// A no-op-ish store change: bump the queue badge, like refreshBadges does.
|
|
24
|
-
ed.store.set({ queueLength: n++ })
|
|
25
|
-
if (n > 20) clearInterval(timer)
|
|
26
|
-
}, 150)
|
|
27
|
-
|
|
28
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// Feed a scripted set of bracketed pastes + Enter into the REAL TuiEditor
|
|
2
|
-
// probe under a pty, then print /tmp/paste-kinds.log (what onSubmit received).
|
|
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 CR = String.fromCharCode(13)
|
|
9
|
-
const LOG = '/tmp/paste-kinds.log'
|
|
10
|
-
const TMP = '/tmp/paste-probe-tmp'
|
|
11
|
-
for (const f of [LOG]) {
|
|
12
|
-
try {
|
|
13
|
-
fs.unlinkSync(f)
|
|
14
|
-
} catch {
|
|
15
|
-
/* ignore */
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
fs.mkdirSync(TMP, { recursive: true })
|
|
19
|
-
|
|
20
|
-
const png =
|
|
21
|
-
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+M8AAAMBAQDJ/pLvAAAAAElFTkSuQmCC'
|
|
22
|
-
const dataUrl = 'data:image/png;base64,' + png
|
|
23
|
-
const existingFile = path.resolve('README.md')
|
|
24
|
-
|
|
25
|
-
const pastes = [
|
|
26
|
-
'hello plain text',
|
|
27
|
-
'line1' +
|
|
28
|
-
String.fromCharCode(10) +
|
|
29
|
-
'line2' +
|
|
30
|
-
String.fromCharCode(10) +
|
|
31
|
-
'line3' +
|
|
32
|
-
String.fromCharCode(10) +
|
|
33
|
-
'line4',
|
|
34
|
-
existingFile,
|
|
35
|
-
dataUrl,
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
const child = spawn(
|
|
39
|
-
'script',
|
|
40
|
-
['-qec', 'npx tsx scripts/tui-paste-kinds.tsx', '/dev/null'],
|
|
41
|
-
{
|
|
42
|
-
cwd: path.resolve('.'),
|
|
43
|
-
env: {
|
|
44
|
-
...process.env,
|
|
45
|
-
COLUMNS: '90',
|
|
46
|
-
LINES: '28',
|
|
47
|
-
PROBE_PASTE_LOG: LOG,
|
|
48
|
-
PROBE_PASTE_TMP: TMP,
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
)
|
|
52
|
-
child.stdout.on('data', () => {})
|
|
53
|
-
child.stderr.on('data', () => {})
|
|
54
|
-
|
|
55
|
-
const enc = (s: string): string => ESC + '[200~' + s + ESC + '[201~'
|
|
56
|
-
let i = 0
|
|
57
|
-
const step = (): void => {
|
|
58
|
-
if (i < pastes.length) {
|
|
59
|
-
child.stdin.write(enc(pastes[i]))
|
|
60
|
-
setTimeout(() => {
|
|
61
|
-
child.stdin.write(CR)
|
|
62
|
-
i++
|
|
63
|
-
setTimeout(step, 500)
|
|
64
|
-
}, 500)
|
|
65
|
-
} else {
|
|
66
|
-
setTimeout(() => child.kill('SIGTERM'), 600)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
setTimeout(step, 5000)
|
|
70
|
-
child.on('close', () => {
|
|
71
|
-
let out = ''
|
|
72
|
-
try {
|
|
73
|
-
out = fs.readFileSync(LOG, 'utf-8')
|
|
74
|
-
} catch {
|
|
75
|
-
out = '(no log)'
|
|
76
|
-
}
|
|
77
|
-
process.stdout.write(out + String.fromCharCode(10))
|
|
78
|
-
try {
|
|
79
|
-
process.stdout.write('--- tmp files ---' + String.fromCharCode(10))
|
|
80
|
-
process.stdout.write(
|
|
81
|
-
fs.readdirSync(TMP).join(String.fromCharCode(10)) +
|
|
82
|
-
String.fromCharCode(10),
|
|
83
|
-
)
|
|
84
|
-
} catch {
|
|
85
|
-
/* ignore */
|
|
86
|
-
}
|
|
87
|
-
process.exit(0)
|
|
88
|
-
})
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// Drive the REAL TuiEditor under a pty and log every onSubmit (text +
|
|
2
|
-
// attachments) so all paste kinds can be exercised: plain text, a big
|
|
3
|
-
// multi-line paste, a file path, and a data: URL image. The attach/clipboard
|
|
4
|
-
// callbacks mirror index.ts (parseImagePaste -> saveToTemp; resolveAttachPath
|
|
5
|
-
// -> readFile) so the probe exercises the same code path the live agent does.
|
|
6
|
-
|
|
7
|
-
import fs from 'node:fs'
|
|
8
|
-
import fsp from 'node:fs/promises'
|
|
9
|
-
import path from 'node:path'
|
|
10
|
-
import { TuiEditor } from '../src/tui/TuiEditor.js'
|
|
11
|
-
import {
|
|
12
|
-
contentHash,
|
|
13
|
-
extForMime,
|
|
14
|
-
guessMime,
|
|
15
|
-
parseImagePaste,
|
|
16
|
-
saveToTemp,
|
|
17
|
-
} from '../src/attachments.js'
|
|
18
|
-
import { resolveAttachPath } from '../src/attach-refs.js'
|
|
19
|
-
|
|
20
|
-
const LOG = process.env.PROBE_PASTE_LOG || '/tmp/paste-kinds.log'
|
|
21
|
-
const TMP = process.env.PROBE_PASTE_TMP || '/tmp/paste-probe-tmp'
|
|
22
|
-
const WORKDIR = process.cwd()
|
|
23
|
-
const line = (s: string): void => {
|
|
24
|
-
fs.appendFileSync(LOG, s + String.fromCharCode(10))
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const ed = new TuiEditor({
|
|
28
|
-
prompt: '> ',
|
|
29
|
-
commands: [],
|
|
30
|
-
locale: 'en',
|
|
31
|
-
alternateScreen: true,
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
ed.onSubmit = (text, atts) => {
|
|
35
|
-
line(
|
|
36
|
-
'SUBMIT text=' +
|
|
37
|
-
JSON.stringify(text) +
|
|
38
|
-
' atts=' +
|
|
39
|
-
JSON.stringify(
|
|
40
|
-
atts.map((a) => ({ marker: a.marker, image: a.image, name: a.name })),
|
|
41
|
-
),
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
ed.onAttach = async (raw: string) => {
|
|
45
|
-
const image = parseImagePaste(raw)
|
|
46
|
-
if (image) {
|
|
47
|
-
const name = 'paste' + extForMime(image.mime)
|
|
48
|
-
const p = await saveToTemp(TMP, name, image.data)
|
|
49
|
-
const att = ed.attachments.add({
|
|
50
|
-
path: p,
|
|
51
|
-
name,
|
|
52
|
-
mime: image.mime,
|
|
53
|
-
size: image.data.length,
|
|
54
|
-
hash: contentHash(image.data),
|
|
55
|
-
})
|
|
56
|
-
line('ATTACH_IMAGE ' + att.marker + ' ' + p)
|
|
57
|
-
return att
|
|
58
|
-
}
|
|
59
|
-
const filePath = await resolveAttachPath(WORKDIR, raw)
|
|
60
|
-
if (!filePath) return null
|
|
61
|
-
const data = await fsp.readFile(filePath).catch(() => null)
|
|
62
|
-
if (!data) return null
|
|
63
|
-
const name = path.basename(filePath)
|
|
64
|
-
const att = ed.attachments.add({
|
|
65
|
-
path: filePath,
|
|
66
|
-
name,
|
|
67
|
-
mime: guessMime(name),
|
|
68
|
-
size: data.length,
|
|
69
|
-
})
|
|
70
|
-
line('ATTACH_FILE ' + att.marker + ' ' + filePath)
|
|
71
|
-
return att
|
|
72
|
-
}
|
|
73
|
-
ed.onClipboard = () => Promise.resolve(null)
|
|
74
|
-
|
|
75
|
-
ed.start()
|
|
76
|
-
setInterval(() => {}, 1 << 30)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Reproduce the [Pasted lines#N] path end to end under a pty: send ONE
|
|
2
|
-
// bracketed-paste chunk with several lines, press Enter, and print what the
|
|
3
|
-
// onSubmit callback actually received (that is what reaches the agent).
|
|
4
|
-
import { spawn } from 'node:child_process'
|
|
5
|
-
import path from 'node:path'
|
|
6
|
-
import fs from 'node:fs'
|
|
7
|
-
|
|
8
|
-
const ESC = String.fromCharCode(27)
|
|
9
|
-
const LF = String.fromCharCode(10)
|
|
10
|
-
const CR = String.fromCharCode(13)
|
|
11
|
-
const LOG = '/tmp/paste-submit.log'
|
|
12
|
-
fs.writeFileSync(LOG, '')
|
|
13
|
-
|
|
14
|
-
const child = spawn(
|
|
15
|
-
'script',
|
|
16
|
-
['-qec', 'npx tsx scripts/tui-submit.tsx', '/dev/null'],
|
|
17
|
-
{
|
|
18
|
-
cwd: path.resolve('.'),
|
|
19
|
-
env: { ...process.env, COLUMNS: '80', LINES: '24', PROBE_SUBMIT_LOG: LOG },
|
|
20
|
-
},
|
|
21
|
-
)
|
|
22
|
-
child.stdout.on('data', () => {})
|
|
23
|
-
child.stderr.on('data', () => {})
|
|
24
|
-
|
|
25
|
-
setTimeout(() => {
|
|
26
|
-
const paste =
|
|
27
|
-
ESC + '[200~' + ['l1', 'l2', 'l3', 'l4', 'l5'].join(LF) + ESC + '[201~'
|
|
28
|
-
child.stdin.write(paste)
|
|
29
|
-
setTimeout(() => {
|
|
30
|
-
child.stdin.write(CR)
|
|
31
|
-
setTimeout(() => child.kill('SIGTERM'), 800)
|
|
32
|
-
}, 800)
|
|
33
|
-
}, 4500)
|
|
34
|
-
|
|
35
|
-
child.on('close', () => {
|
|
36
|
-
process.stdout.write(fs.readFileSync(LOG, 'utf-8'))
|
|
37
|
-
process.exit(0)
|
|
38
|
-
})
|