@wenrwa/marketplace-cli 0.8.4 → 0.8.5
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.
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
/**
|
|
4
37
|
* tchp-02 — per-session working-tree change detector (`changes` frame).
|
|
@@ -703,4 +736,69 @@ describe('execGit — opts.env (tchp-21)', () => {
|
|
|
703
736
|
expect(res.code).toBe(0);
|
|
704
737
|
expect(res.stdout).toBe('true');
|
|
705
738
|
});
|
|
739
|
+
// Not-a-repo model (live report 2026-10-11): a plain folder used to burn
|
|
740
|
+
// three "failures", get disabled, and leave the panel on "Waiting for
|
|
741
|
+
// changes…" forever.
|
|
742
|
+
describe('a folder that is not a git repo', () => {
|
|
743
|
+
const notGitRun = () => jest.fn().mockImplementation(async (_cmd, args) => ({ stdout: '', code: 128, args }));
|
|
744
|
+
it('emits ONE changes-untracked frame and keeps polling (never disabled)', async () => {
|
|
745
|
+
const run = notGitRun();
|
|
746
|
+
const build = jest.fn();
|
|
747
|
+
const { reporter, sent } = makeReporter({ run, buildChangesReport: build, now: () => 7 });
|
|
748
|
+
const sessions = [{ sessionId: 's1', workspaceId: 'w1', cwd: '/plain' }];
|
|
749
|
+
for (let i = 0; i < 5; i++)
|
|
750
|
+
await reporter.tick(sessions);
|
|
751
|
+
expect(build).not.toHaveBeenCalled();
|
|
752
|
+
expect(sent).toEqual([
|
|
753
|
+
{ t: 'changes-untracked', reason: 'not-git', workspaceId: 'w1', agentId: 's1', sessionId: 's1', ts: 7 },
|
|
754
|
+
]);
|
|
755
|
+
// still probing every tick — a later `git init` must be noticed
|
|
756
|
+
const statusCalls = run.mock.calls.filter((c) => c[1].includes('status'));
|
|
757
|
+
expect(statusCalls).toHaveLength(5);
|
|
758
|
+
});
|
|
759
|
+
it('replays the untracked notice to a fresh connection', async () => {
|
|
760
|
+
const { reporter, sent } = makeReporter({ run: notGitRun() });
|
|
761
|
+
const sessions = [{ sessionId: 's1', workspaceId: 'w1', cwd: '/plain' }];
|
|
762
|
+
await reporter.tick(sessions);
|
|
763
|
+
reporter.requestReplay();
|
|
764
|
+
await reporter.tick(sessions);
|
|
765
|
+
expect(sent).toHaveLength(2);
|
|
766
|
+
expect(sent[1]).toBe(sent[0]);
|
|
767
|
+
});
|
|
768
|
+
it('a later git init switches to a real report immediately', async () => {
|
|
769
|
+
let isRepo = false;
|
|
770
|
+
const run = jest.fn().mockImplementation(async () => (isRepo ? statusOk('?? a.R\n') : { stdout: '', code: 128 }));
|
|
771
|
+
const build = jest.fn().mockResolvedValue(fakeReport('sha-1'));
|
|
772
|
+
const { reporter, sent } = makeReporter({ run, buildChangesReport: build });
|
|
773
|
+
const sessions = [{ sessionId: 's1', workspaceId: 'w1', cwd: '/plain' }];
|
|
774
|
+
await reporter.tick(sessions);
|
|
775
|
+
isRepo = true;
|
|
776
|
+
await reporter.tick(sessions);
|
|
777
|
+
expect(sent.map((f) => f.t)).toEqual(['changes-untracked', 'changes']);
|
|
778
|
+
});
|
|
779
|
+
it('a non-128 git failure is still an ordinary failure (no untracked frame)', async () => {
|
|
780
|
+
const run = jest.fn().mockResolvedValue({ stdout: '', code: 1 });
|
|
781
|
+
const { reporter, sent } = makeReporter({ run });
|
|
782
|
+
const sessions = [{ sessionId: 's1', workspaceId: 'w1', cwd: '/x' }];
|
|
783
|
+
for (let i = 0; i < 4; i++)
|
|
784
|
+
await reporter.tick(sessions);
|
|
785
|
+
expect(sent).toEqual([]);
|
|
786
|
+
expect(run).toHaveBeenCalledTimes(3); // 3 strikes then disabled
|
|
787
|
+
});
|
|
788
|
+
it('real git: a temp plain folder is reported as not-git', async () => {
|
|
789
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
790
|
+
const os = await Promise.resolve().then(() => __importStar(require('os')));
|
|
791
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
792
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'plain-'));
|
|
793
|
+
try {
|
|
794
|
+
const { reporter, sent } = makeReporter({ run: changesReporter_1.execGit });
|
|
795
|
+
await reporter.tick([{ sessionId: 's1', workspaceId: 'w1', cwd: dir }]);
|
|
796
|
+
expect(sent).toHaveLength(1);
|
|
797
|
+
expect(sent[0]).toMatchObject({ t: 'changes-untracked', reason: 'not-git' });
|
|
798
|
+
}
|
|
799
|
+
finally {
|
|
800
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
});
|
|
706
804
|
});
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const termSnapshot_1 = require("../bridge/termSnapshot");
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
5
|
+
const { Terminal } = require('@xterm/headless');
|
|
6
|
+
/** A fresh client terminal at (cols, rows) with `data` replayed into it —
|
|
7
|
+
* what a reattaching pane sees. */
|
|
8
|
+
async function replay(data, cols, rows) {
|
|
9
|
+
const t = new Terminal({ cols, rows, allowProposedApi: true });
|
|
10
|
+
await new Promise((r) => t.write(data, () => r()));
|
|
11
|
+
return t;
|
|
12
|
+
}
|
|
13
|
+
function screenRows(t) {
|
|
14
|
+
const b = t.buffer.active;
|
|
15
|
+
const out = [];
|
|
16
|
+
for (let y = b.viewportY; y < b.viewportY + t.rows; y++)
|
|
17
|
+
out.push(b.getLine(y)?.translateToString(true) ?? '');
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
4
20
|
describe('TermSnapshot', () => {
|
|
5
21
|
it('write then immediately serialize includes the written content', async () => {
|
|
6
22
|
const snap = (0, termSnapshot_1.createTermSnapshot)(80, 24);
|
|
@@ -77,4 +93,124 @@ describe('TermSnapshot', () => {
|
|
|
77
93
|
// without callers ever having held a reference to xterm internals).
|
|
78
94
|
expect(Object.keys(snap).sort()).toEqual(['dispose', 'resize', 'serialize', 'write']);
|
|
79
95
|
});
|
|
96
|
+
describe('reattach after the terminal shrank (OpenCode garble)', () => {
|
|
97
|
+
/** A full-screen TUI frame on the alt screen, every row full width, with
|
|
98
|
+
* a coloured span — then the pane shrinks. */
|
|
99
|
+
async function wideFrameThenShrink(snap) {
|
|
100
|
+
await snap.write('\x1b[?1049h\x1b[H');
|
|
101
|
+
for (let r = 1; r <= 6; r++) {
|
|
102
|
+
const row = `${r}`.repeat(10) + '\x1b[31m' + 'R'.repeat(10) + '\x1b[0m' + `${r}`.repeat(20);
|
|
103
|
+
await snap.write(`\x1b[${r};1H${row}`);
|
|
104
|
+
}
|
|
105
|
+
snap.resize(20, 4);
|
|
106
|
+
}
|
|
107
|
+
it('clips every row to the new cols — no row overflows into the next', async () => {
|
|
108
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(40, 6);
|
|
109
|
+
await wideFrameThenShrink(snap);
|
|
110
|
+
const out = await snap.serialize();
|
|
111
|
+
const client = await replay(out, 20, 4);
|
|
112
|
+
expect(client.buffer.active.type).toBe('alternate');
|
|
113
|
+
// The live 20x4 screen after the shrink shows rows 3..6, first 20 cells.
|
|
114
|
+
const live = ['3', '4', '5', '6'].map((d) => d.repeat(10) + 'R'.repeat(10));
|
|
115
|
+
expect(screenRows(client)).toEqual(live);
|
|
116
|
+
// SGR state survives the clip: the red span is still red, the digits not.
|
|
117
|
+
const line = client.buffer.active.getLine(client.buffer.active.viewportY);
|
|
118
|
+
expect(line.getCell(12).getFgColor()).toBe(1);
|
|
119
|
+
expect(line.getCell(2).getFgColor()).not.toBe(1);
|
|
120
|
+
client.dispose();
|
|
121
|
+
snap.dispose();
|
|
122
|
+
});
|
|
123
|
+
it('replays to exactly the live screen of an emulator that saw the same bytes and shrink', async () => {
|
|
124
|
+
const bytes = ['\x1b[?1049h\x1b[H'];
|
|
125
|
+
for (let r = 1; r <= 6; r++)
|
|
126
|
+
bytes.push(`\x1b[${r};1H` + `${r}abcdefghi`.repeat(4));
|
|
127
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(40, 6);
|
|
128
|
+
const live = new Terminal({ cols: 40, rows: 6, allowProposedApi: true });
|
|
129
|
+
for (const chunk of bytes) {
|
|
130
|
+
await snap.write(chunk);
|
|
131
|
+
await new Promise((r) => live.write(chunk, () => r()));
|
|
132
|
+
}
|
|
133
|
+
snap.resize(20, 4);
|
|
134
|
+
live.resize(20, 4);
|
|
135
|
+
const client = await replay(await snap.serialize(), 20, 4);
|
|
136
|
+
// What the live pane displays: each row's first `cols` cells.
|
|
137
|
+
const b = live.buffer.active;
|
|
138
|
+
const liveScreen = [];
|
|
139
|
+
for (let y = b.viewportY; y < b.viewportY + live.rows; y++) {
|
|
140
|
+
liveScreen.push(b.getLine(y).translateToString(true, 0, live.cols));
|
|
141
|
+
}
|
|
142
|
+
expect(screenRows(client)).toEqual(liveScreen);
|
|
143
|
+
expect(client.buffer.active.length).toBe(4);
|
|
144
|
+
[client, live].forEach((t) => t.dispose());
|
|
145
|
+
snap.dispose();
|
|
146
|
+
});
|
|
147
|
+
it('never splits a wide character that straddles the new right edge', async () => {
|
|
148
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(40, 3);
|
|
149
|
+
await snap.write('\x1b[?1049h\x1b[H');
|
|
150
|
+
await snap.write('\x1b[1;1H' + 'a'.repeat(19) + '漢' + 'b'.repeat(19));
|
|
151
|
+
await snap.write('\x1b[2;1H' + 'c'.repeat(40));
|
|
152
|
+
snap.resize(20, 3);
|
|
153
|
+
const out = await snap.serialize();
|
|
154
|
+
const client = await replay(out, 20, 3);
|
|
155
|
+
const rows = screenRows(client);
|
|
156
|
+
expect(rows[0]).toBe('a'.repeat(19));
|
|
157
|
+
expect(rows[1]).toBe('c'.repeat(20));
|
|
158
|
+
client.dispose();
|
|
159
|
+
snap.dispose();
|
|
160
|
+
});
|
|
161
|
+
it('a soft-wrapped row of the wider frame does not glue onto its clipped predecessor', async () => {
|
|
162
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(30, 4);
|
|
163
|
+
await snap.write('\x1b[?1049h\x1b[H');
|
|
164
|
+
// 45 chars at 30 cols: row 0 full, row 1 is a soft wrap of it.
|
|
165
|
+
await snap.write('x'.repeat(30) + 'y'.repeat(15));
|
|
166
|
+
snap.resize(20, 4);
|
|
167
|
+
const out = await snap.serialize();
|
|
168
|
+
const client = await replay(out, 20, 4);
|
|
169
|
+
expect(screenRows(client).slice(0, 3)).toEqual(['x'.repeat(20), 'y'.repeat(15), '']);
|
|
170
|
+
client.dispose();
|
|
171
|
+
snap.dispose();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe('terminal modes survive a reattach', () => {
|
|
175
|
+
it('re-emits mouse tracking + SGR encoding, bracketed paste, app cursor keys, alt screen and a hidden cursor', async () => {
|
|
176
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(40, 6);
|
|
177
|
+
await snap.write('\x1b[?1049h\x1b[?1002h\x1b[?1006h\x1b[?2004h\x1b[?1h\x1b[?25l');
|
|
178
|
+
await snap.write('\x1b[1;1Hopencode');
|
|
179
|
+
const out = await snap.serialize();
|
|
180
|
+
const client = await replay(out, 40, 6);
|
|
181
|
+
expect(client.buffer.active.type).toBe('alternate');
|
|
182
|
+
expect(client.modes.mouseTrackingMode).toBe('drag');
|
|
183
|
+
expect(client._core.coreMouseService.activeEncoding).toBe('SGR');
|
|
184
|
+
expect(client.modes.bracketedPasteMode).toBe(true);
|
|
185
|
+
expect(client.modes.applicationCursorKeysMode).toBe(true);
|
|
186
|
+
expect(client._core.coreService.isCursorHidden).toBe(true);
|
|
187
|
+
expect(screenRows(client)[0]).toBe('opencode');
|
|
188
|
+
// Modes come AFTER the screen content, so the replayed content can't
|
|
189
|
+
// be misread under them.
|
|
190
|
+
expect(out.lastIndexOf('opencode')).toBeLessThan(out.indexOf('\x1b[?1006h'));
|
|
191
|
+
client.dispose();
|
|
192
|
+
snap.dispose();
|
|
193
|
+
});
|
|
194
|
+
it('re-emits any-motion tracking (?1003) and SGR-pixels (?1016)', async () => {
|
|
195
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(40, 6);
|
|
196
|
+
await snap.write('\x1b[?1003h\x1b[?1016h');
|
|
197
|
+
const client = await replay(await snap.serialize(), 40, 6);
|
|
198
|
+
expect(client.modes.mouseTrackingMode).toBe('any');
|
|
199
|
+
expect(client._core.coreMouseService.activeEncoding).toBe('SGR_PIXELS');
|
|
200
|
+
client.dispose();
|
|
201
|
+
snap.dispose();
|
|
202
|
+
});
|
|
203
|
+
it('emits nothing for modes the app turned back off', async () => {
|
|
204
|
+
const snap = (0, termSnapshot_1.createTermSnapshot)(40, 6);
|
|
205
|
+
await snap.write('\x1b[?1000h\x1b[?1006h\x1b[?25l\x1b[?2004h');
|
|
206
|
+
await snap.write('\x1b[?1000l\x1b[?1006l\x1b[?25h\x1b[?2004l');
|
|
207
|
+
const out = await snap.serialize();
|
|
208
|
+
expect(out).not.toMatch(/\x1b\[\?(1000|1006|25|2004)[hl]/);
|
|
209
|
+
const client = await replay(out, 40, 6);
|
|
210
|
+
expect(client.modes.mouseTrackingMode).toBe('none');
|
|
211
|
+
expect(client._core.coreMouseService.activeEncoding).toBe('DEFAULT');
|
|
212
|
+
client.dispose();
|
|
213
|
+
snap.dispose();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
80
216
|
});
|
|
Binary file
|
|
@@ -36,7 +36,10 @@ function createTermSnapshot(cols, rows, opts) {
|
|
|
36
36
|
// stable public API).
|
|
37
37
|
const term = new Terminal({ cols, rows, scrollback, allowProposedApi: true });
|
|
38
38
|
const serializer = new SerializeAddon();
|
|
39
|
-
|
|
39
|
+
// The addon reads the terminal through a CLIPPED VIEW, not the terminal
|
|
40
|
+
// itself — see clippedView below. activate() only stores the reference
|
|
41
|
+
// (the addon has no other loadAddon-time wiring); dispose() releases it.
|
|
42
|
+
serializer.activate(clippedView(term));
|
|
40
43
|
// Terminal.write() is async: the parser runs on an internal write queue
|
|
41
44
|
// and only calls the callback once that write has actually been applied
|
|
42
45
|
// to the buffer. Resolving our promise from that callback means callers
|
|
@@ -56,7 +59,7 @@ function createTermSnapshot(cols, rows, opts) {
|
|
|
56
59
|
},
|
|
57
60
|
serialize: async () => {
|
|
58
61
|
await flush();
|
|
59
|
-
return serializer.serialize({ scrollback });
|
|
62
|
+
return serializer.serialize({ scrollback }) + extraModes(term);
|
|
60
63
|
},
|
|
61
64
|
dispose: () => {
|
|
62
65
|
serializer.dispose();
|
|
@@ -64,3 +67,112 @@ function createTermSnapshot(cols, rows, opts) {
|
|
|
64
67
|
},
|
|
65
68
|
};
|
|
66
69
|
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Narrow-after-wide clipping.
|
|
72
|
+
//
|
|
73
|
+
// xterm does not truncate buffer lines when the terminal shrinks: the
|
|
74
|
+
// alternate buffer (where full-screen TUIs like OpenCode draw) never reflows,
|
|
75
|
+
// so after a 40→20 col shrink every alt row still holds 40 cells. The
|
|
76
|
+
// serialize addon walks `line.length` cells per row, so the snapshot wrote
|
|
77
|
+
// each row at its OLD width, the replay terminal (at the new width)
|
|
78
|
+
// auto-wrapped the tail onto the next row, and the frame came back garbled
|
|
79
|
+
// on reattach. The live screen only ever shows the first `cols` cells, so the
|
|
80
|
+
// snapshot clips every row to `cols` — via a read-only view, never by
|
|
81
|
+
// mutating the emulator (growing back must still reveal what xterm kept).
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
84
|
+
/** Forward every property of `target`, with functions bound to it, except the overrides. */
|
|
85
|
+
function forwardingProxy(target, override) {
|
|
86
|
+
return new Proxy(target, {
|
|
87
|
+
get(t, prop) {
|
|
88
|
+
const o = override(prop);
|
|
89
|
+
if (o)
|
|
90
|
+
return o.value;
|
|
91
|
+
const v = Reflect.get(t, prop, t);
|
|
92
|
+
return typeof v === 'function' ? v.bind(t) : v;
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** The number of cells of `line` that are visible at `cols` — never splits a
|
|
97
|
+
* wide (2-cell) character: one that straddles the edge is dropped whole. */
|
|
98
|
+
function visibleLength(line, cols) {
|
|
99
|
+
if (!line || line.length <= cols)
|
|
100
|
+
return line ? line.length : 0;
|
|
101
|
+
const edge = line.getCell(cols - 1);
|
|
102
|
+
return edge && edge.getWidth() === 2 ? cols - 1 : cols;
|
|
103
|
+
}
|
|
104
|
+
function clippedBuffer(buffer, term) {
|
|
105
|
+
return forwardingProxy(buffer, (prop) => {
|
|
106
|
+
if (prop !== 'getLine')
|
|
107
|
+
return null;
|
|
108
|
+
return {
|
|
109
|
+
hit: true,
|
|
110
|
+
value: (y) => {
|
|
111
|
+
const line = buffer.getLine(y);
|
|
112
|
+
if (!line)
|
|
113
|
+
return line;
|
|
114
|
+
const cols = term.cols;
|
|
115
|
+
const len = visibleLength(line, cols);
|
|
116
|
+
// A row that continued a now-clipped wider row is no longer a soft
|
|
117
|
+
// wrap of it at this width — the serializer must end the previous
|
|
118
|
+
// row with a hard CRLF, not rely on auto-wrap.
|
|
119
|
+
const prev = y > 0 ? buffer.getLine(y - 1) : undefined;
|
|
120
|
+
const wrapped = line.isWrapped && !(prev && prev.length > cols);
|
|
121
|
+
if (len === line.length && wrapped === line.isWrapped)
|
|
122
|
+
return line;
|
|
123
|
+
return forwardingProxy(line, (p) => {
|
|
124
|
+
if (p === 'length')
|
|
125
|
+
return { hit: true, value: len };
|
|
126
|
+
if (p === 'isWrapped')
|
|
127
|
+
return { hit: true, value: wrapped };
|
|
128
|
+
return null;
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/** The terminal as the serialize addon sees it: same object, but every
|
|
135
|
+
* buffer row is clipped to the CURRENT cols. Rows need no clipping: the
|
|
136
|
+
* alt buffer is exactly `rows` long after a resize, and the normal buffer
|
|
137
|
+
* pushes the overflow into scrollback (which the replay scrolls too). */
|
|
138
|
+
function clippedView(term) {
|
|
139
|
+
// Resolved NOW, not on first serialize: xterm creates + registers the
|
|
140
|
+
// buffer namespace lazily, and doing that on a terminal already disposed
|
|
141
|
+
// (a serialize racing dispose) leaks a disposable with a warning.
|
|
142
|
+
const ns = term.buffer;
|
|
143
|
+
const view = forwardingProxy(ns, (prop) => {
|
|
144
|
+
if (prop === 'normal' || prop === 'alternate' || prop === 'active') {
|
|
145
|
+
return { hit: true, value: clippedBuffer(ns[prop], term) };
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
});
|
|
149
|
+
return forwardingProxy(term, (prop) => (prop === 'buffer' ? { hit: true, value: view } : null));
|
|
150
|
+
}
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Terminal modes the serialize addon drops.
|
|
153
|
+
//
|
|
154
|
+
// The addon re-emits mouse TRACKING (?9/?1000/?1002/?1003), bracketed paste,
|
|
155
|
+
// application cursor/keypad, focus, insert/origin/wrap and the alt screen
|
|
156
|
+
// (?1049) — but not the mouse ENCODING (?1006 SGR / ?1016 SGR-pixels) nor a
|
|
157
|
+
// hidden cursor (?25l). Without ?1006 a reattached client reports wheel and
|
|
158
|
+
// clicks in the legacy X10 byte encoding, which an SGR-expecting TUI
|
|
159
|
+
// (OpenCode) ignores — so mouse-wheel scroll silently died after a reattach,
|
|
160
|
+
// and the hidden cursor reappeared as a stray block.
|
|
161
|
+
//
|
|
162
|
+
// Read from the emulator's own state (the same `_core` internals the addon
|
|
163
|
+
// itself reads), so resets (RIS/DECSTR) are honoured exactly as xterm
|
|
164
|
+
// applies them — no shadow parser that could drift.
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
/** Escape sequences restoring the modes the serialize addon does not emit. */
|
|
167
|
+
function extraModes(term) {
|
|
168
|
+
const core = term?._core;
|
|
169
|
+
let out = '';
|
|
170
|
+
const encoding = core?.coreMouseService?.activeEncoding;
|
|
171
|
+
if (encoding === 'SGR')
|
|
172
|
+
out += '\x1b[?1006h';
|
|
173
|
+
else if (encoding === 'SGR_PIXELS')
|
|
174
|
+
out += '\x1b[?1016h';
|
|
175
|
+
if (core?.coreService?.isCursorHidden === true)
|
|
176
|
+
out += '\x1b[?25l';
|
|
177
|
+
return out;
|
|
178
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenrwa/marketplace-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "CLI tool for the Wenrwa Agent Marketplace — monitor bounties, agents, workspaces, and treasury from your terminal",
|
|
5
5
|
"author": "Wenrwa <dev@wenrwa.com>",
|
|
6
6
|
"license": "MIT",
|