aicodeman 0.9.6 → 0.9.8
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/LICENSE +1 -1
- package/README.md +112 -16
- package/README.zh-CN.md +664 -0
- package/dist/file-stream-manager.d.ts.map +1 -1
- package/dist/file-stream-manager.js +6 -2
- package/dist/file-stream-manager.js.map +1 -1
- package/dist/mux-interface.d.ts +8 -0
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/session.d.ts +29 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +48 -2
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +30 -2
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +372 -14
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/api.d.ts +10 -17
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js +32 -3
- package/dist/types/api.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/push-endpoint-validation.d.ts +6 -0
- package/dist/utils/push-endpoint-validation.d.ts.map +1 -0
- package/dist/utils/push-endpoint-validation.js +80 -0
- package/dist/utils/push-endpoint-validation.js.map +1 -0
- package/dist/web/public/{api-client.3adebdc2.js → api-client.c9b1cddc.js} +10 -1
- package/dist/web/public/api-client.c9b1cddc.js.br +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.a2f053a8.js +35 -0
- package/dist/web/public/app.a2f053a8.js.br +0 -0
- package/dist/web/public/app.a2f053a8.js.gz +0 -0
- package/dist/web/public/{constants.5b68d2de.js → constants.74211deb.js} +1 -0
- package/dist/web/public/constants.74211deb.js.br +0 -0
- package/dist/web/public/constants.74211deb.js.gz +0 -0
- package/dist/web/public/{image-input.7cade6a8.js → image-input.0ea86695.js} +1 -1
- package/dist/web/public/{image-input.7cade6a8.js.br → image-input.0ea86695.js.br} +0 -0
- package/dist/web/public/image-input.0ea86695.js.gz +0 -0
- package/dist/web/public/index.html +23 -19
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.b8686b5e.js +1 -0
- package/dist/web/public/input-cjk.b8686b5e.js.br +0 -0
- package/dist/web/public/input-cjk.b8686b5e.js.gz +0 -0
- package/dist/web/public/{keyboard-accessory.cdfd8c04.js → keyboard-accessory.bc753cc7.js} +3 -2
- package/dist/web/public/keyboard-accessory.bc753cc7.js.br +0 -0
- package/dist/web/public/keyboard-accessory.bc753cc7.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.1e2a8ef8.js → mobile-handlers.d54d97d6.js} +26 -10
- package/dist/web/public/mobile-handlers.d54d97d6.js.br +0 -0
- package/dist/web/public/mobile-handlers.d54d97d6.js.gz +0 -0
- package/dist/web/public/mobile.959f6fe2.css +1 -0
- package/dist/web/public/mobile.959f6fe2.css.br +0 -0
- package/dist/web/public/mobile.959f6fe2.css.gz +0 -0
- package/dist/web/public/notification-manager.9c984ac2.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js +3 -3
- package/dist/web/public/orchestrator-panel.js.br +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.5192a2c0.js → panels-ui.6bb3169f.js} +4 -4
- package/dist/web/public/panels-ui.6bb3169f.js.br +0 -0
- package/dist/web/public/panels-ui.6bb3169f.js.gz +0 -0
- package/dist/web/public/{ralph-panel.61076370.js → ralph-panel.6de2d0f8.js} +2 -2
- package/dist/web/public/{ralph-panel.61076370.js.br → ralph-panel.6de2d0f8.js.br} +0 -0
- package/dist/web/public/{ralph-panel.61076370.js.gz → ralph-panel.6de2d0f8.js.gz} +0 -0
- package/dist/web/public/{ralph-wizard.52d533d2.js → ralph-wizard.a6b2d36b.js} +6 -6
- package/dist/web/public/ralph-wizard.a6b2d36b.js.br +0 -0
- package/dist/web/public/ralph-wizard.a6b2d36b.js.gz +0 -0
- package/dist/web/public/{respawn-ui.5377f958.js → respawn-ui.2d249da9.js} +1 -1
- package/dist/web/public/{respawn-ui.5377f958.js.br → respawn-ui.2d249da9.js.br} +0 -0
- package/dist/web/public/{respawn-ui.5377f958.js.gz → respawn-ui.2d249da9.js.gz} +0 -0
- package/dist/web/public/session-ui.7e2dbbdd.js +36 -0
- package/dist/web/public/session-ui.7e2dbbdd.js.br +0 -0
- package/dist/web/public/session-ui.7e2dbbdd.js.gz +0 -0
- package/dist/web/public/settings-ui.cbedc88a.js +55 -0
- package/dist/web/public/settings-ui.cbedc88a.js.br +0 -0
- package/dist/web/public/settings-ui.cbedc88a.js.gz +0 -0
- package/dist/web/public/styles.d978a628.css +1 -0
- package/dist/web/public/styles.d978a628.css.br +0 -0
- package/dist/web/public/styles.d978a628.css.gz +0 -0
- package/dist/web/public/subagent-windows.a366a4ad.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.6ce91b0b.js +3 -0
- package/dist/web/public/terminal-ui.6ce91b0b.js.br +0 -0
- package/dist/web/public/terminal-ui.6ce91b0b.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +1 -2
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/clipboard-routes.d.ts.map +1 -1
- package/dist/web/routes/clipboard-routes.js +3 -2
- package/dist/web/routes/clipboard-routes.js.map +1 -1
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +5 -2
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/hook-event-routes.js +1 -1
- package/dist/web/routes/hook-event-routes.js.map +1 -1
- package/dist/web/routes/mux-routes.js +3 -3
- package/dist/web/routes/mux-routes.js.map +1 -1
- package/dist/web/routes/plan-routes.js +1 -1
- package/dist/web/routes/plan-routes.js.map +1 -1
- package/dist/web/routes/push-routes.js +2 -2
- package/dist/web/routes/push-routes.js.map +1 -1
- package/dist/web/routes/ralph-routes.js +2 -2
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/respawn-routes.d.ts.map +1 -1
- package/dist/web/routes/respawn-routes.js +7 -8
- package/dist/web/routes/respawn-routes.js.map +1 -1
- package/dist/web/routes/scheduled-routes.js +2 -2
- package/dist/web/routes/scheduled-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +28 -22
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +13 -17
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/ws-routes.d.ts.map +1 -1
- package/dist/web/routes/ws-routes.js +21 -1
- package/dist/web/routes/ws-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +5 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +7 -2
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +59 -5
- package/dist/web/server.js.map +1 -1
- package/package.json +6 -3
- package/dist/web/public/api-client.3adebdc2.js.br +0 -0
- package/dist/web/public/api-client.3adebdc2.js.gz +0 -0
- package/dist/web/public/app.c860ea08.js +0 -34
- package/dist/web/public/app.c860ea08.js.br +0 -0
- package/dist/web/public/app.c860ea08.js.gz +0 -0
- package/dist/web/public/constants.5b68d2de.js.br +0 -0
- package/dist/web/public/constants.5b68d2de.js.gz +0 -0
- package/dist/web/public/image-input.7cade6a8.js.gz +0 -0
- package/dist/web/public/input-cjk.88082175.js +0 -1
- package/dist/web/public/input-cjk.88082175.js.br +0 -0
- package/dist/web/public/input-cjk.88082175.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.cdfd8c04.js.br +0 -0
- package/dist/web/public/keyboard-accessory.cdfd8c04.js.gz +0 -0
- package/dist/web/public/mobile-handlers.1e2a8ef8.js.br +0 -0
- package/dist/web/public/mobile-handlers.1e2a8ef8.js.gz +0 -0
- package/dist/web/public/mobile.26dc30d6.css +0 -1
- package/dist/web/public/mobile.26dc30d6.css.br +0 -0
- package/dist/web/public/mobile.26dc30d6.css.gz +0 -0
- package/dist/web/public/panels-ui.5192a2c0.js.br +0 -0
- package/dist/web/public/panels-ui.5192a2c0.js.gz +0 -0
- package/dist/web/public/ralph-wizard.52d533d2.js.br +0 -0
- package/dist/web/public/ralph-wizard.52d533d2.js.gz +0 -0
- package/dist/web/public/session-ui.3e0cf024.js +0 -36
- package/dist/web/public/session-ui.3e0cf024.js.br +0 -0
- package/dist/web/public/session-ui.3e0cf024.js.gz +0 -0
- package/dist/web/public/settings-ui.2b70e2c8.js +0 -55
- package/dist/web/public/settings-ui.2b70e2c8.js.br +0 -0
- package/dist/web/public/settings-ui.2b70e2c8.js.gz +0 -0
- package/dist/web/public/styles.e87cb785.css +0 -1
- package/dist/web/public/styles.e87cb785.css.br +0 -0
- package/dist/web/public/styles.e87cb785.css.gz +0 -0
- package/dist/web/public/terminal-ui.37caa926.js +0 -3
- package/dist/web/public/terminal-ui.37caa926.js.br +0 -0
- package/dist/web/public/terminal-ui.37caa926.js.gz +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux-manager.d.ts","sourceRoot":"","sources":["../src/tmux-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS3C,OAAO,EACL,YAAY,EACZ,sBAAsB,EAGtB,KAAK,QAAQ,EAKd,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EACV,mBAAmB,EACnB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AA2B5B,wFAAwF;AACxF,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAoDnD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,SAA2B,GAAG,MAAM,CAGtF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAajE;
|
|
1
|
+
{"version":3,"file":"tmux-manager.d.ts","sourceRoot":"","sources":["../src/tmux-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/tmux-manager.js
CHANGED
|
@@ -126,6 +126,259 @@ export function parsePaneList(output) {
|
|
|
126
126
|
}
|
|
127
127
|
return result;
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Resolve a target pane id from `tmux list-panes -F '#{pane_id}:#{pane_active}'`.
|
|
131
|
+
* Prefers the active pane and falls back to the first valid pane.
|
|
132
|
+
*/
|
|
133
|
+
export function resolveTmuxPaneTarget(muxName, paneTarget) {
|
|
134
|
+
if (!isValidMuxName(muxName)) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
if (paneTarget === undefined || paneTarget === 'active') {
|
|
138
|
+
return muxName;
|
|
139
|
+
}
|
|
140
|
+
if (!SAFE_PANE_TARGET_PATTERN.test(paneTarget)) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return `${muxName}.${paneTarget}`;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Pick the active pane id from `tmux list-panes -F '#{pane_id}:#{pane_active}'`
|
|
147
|
+
* output (lines like `%0:1`). Returns the pane id whose active flag is 1.
|
|
148
|
+
*/
|
|
149
|
+
export function resolveActivePaneTarget(output) {
|
|
150
|
+
for (const line of output.split('\n')) {
|
|
151
|
+
const sep = line.indexOf(':');
|
|
152
|
+
if (sep === -1)
|
|
153
|
+
continue;
|
|
154
|
+
const paneId = line.slice(0, sep).trim();
|
|
155
|
+
const active = line.slice(sep + 1).trim();
|
|
156
|
+
if (paneId && active === '1')
|
|
157
|
+
return paneId;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const GRAPHEME_SEGMENTER = (() => {
|
|
162
|
+
try {
|
|
163
|
+
const Segmenter = Intl.Segmenter;
|
|
164
|
+
return Segmenter ? new Segmenter(undefined, { granularity: 'grapheme' }) : null;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
})();
|
|
170
|
+
function findEscapeEnd(text, start) {
|
|
171
|
+
const type = text[start + 1];
|
|
172
|
+
if (type === '[') {
|
|
173
|
+
for (let i = start + 2; i < text.length; i++) {
|
|
174
|
+
const code = text.charCodeAt(i);
|
|
175
|
+
if (code >= 0x40 && code <= 0x7e)
|
|
176
|
+
return i;
|
|
177
|
+
}
|
|
178
|
+
return text.length - 1;
|
|
179
|
+
}
|
|
180
|
+
if (type === ']') {
|
|
181
|
+
for (let i = start + 2; i < text.length; i++) {
|
|
182
|
+
if (text.charCodeAt(i) === 0x07)
|
|
183
|
+
return i;
|
|
184
|
+
if (text[i] === '\x1b' && text[i + 1] === '\\')
|
|
185
|
+
return i + 1;
|
|
186
|
+
}
|
|
187
|
+
return text.length - 1;
|
|
188
|
+
}
|
|
189
|
+
if (type === 'P' || type === '^' || type === '_' || type === 'X') {
|
|
190
|
+
for (let i = start + 2; i < text.length; i++) {
|
|
191
|
+
if (text.charCodeAt(i) === 0x07)
|
|
192
|
+
return i;
|
|
193
|
+
if (text[i] === '\x1b' && text[i + 1] === '\\')
|
|
194
|
+
return i + 1;
|
|
195
|
+
}
|
|
196
|
+
return text.length - 1;
|
|
197
|
+
}
|
|
198
|
+
return Math.min(start + 1, text.length - 1);
|
|
199
|
+
}
|
|
200
|
+
function sanitizePaneLineStyles(line) {
|
|
201
|
+
let result = '';
|
|
202
|
+
for (let i = 0; i < line.length; i++) {
|
|
203
|
+
if (line[i] !== '\x1b') {
|
|
204
|
+
result += line[i];
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const end = findEscapeEnd(line, i);
|
|
208
|
+
const sequence = line.slice(i, end + 1);
|
|
209
|
+
if (isSgrSequence(sequence)) {
|
|
210
|
+
result += sequence;
|
|
211
|
+
}
|
|
212
|
+
i = end;
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
function isSgrSequence(sequence) {
|
|
217
|
+
return (sequence.length >= 3 &&
|
|
218
|
+
sequence.charCodeAt(0) === 27 &&
|
|
219
|
+
sequence[1] === '[' &&
|
|
220
|
+
sequence.endsWith('m') &&
|
|
221
|
+
/^[0-9;:]*$/.test(sequence.slice(2, -1)));
|
|
222
|
+
}
|
|
223
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
224
|
+
return (codePoint === 0x00ad ||
|
|
225
|
+
codePoint === 0x034f ||
|
|
226
|
+
codePoint === 0x061c ||
|
|
227
|
+
codePoint === 0x115f ||
|
|
228
|
+
codePoint === 0x1160 ||
|
|
229
|
+
codePoint === 0x17b4 ||
|
|
230
|
+
codePoint === 0x17b5 ||
|
|
231
|
+
codePoint === 0x180e ||
|
|
232
|
+
codePoint === 0x200b ||
|
|
233
|
+
codePoint === 0x200c ||
|
|
234
|
+
codePoint === 0x200d ||
|
|
235
|
+
codePoint === 0x2060 ||
|
|
236
|
+
codePoint === 0xfeff ||
|
|
237
|
+
(codePoint >= 0x0300 && codePoint <= 0x036f) ||
|
|
238
|
+
(codePoint >= 0x0483 && codePoint <= 0x0489) ||
|
|
239
|
+
(codePoint >= 0x0591 && codePoint <= 0x05bd) ||
|
|
240
|
+
codePoint === 0x05bf ||
|
|
241
|
+
(codePoint >= 0x05c1 && codePoint <= 0x05c2) ||
|
|
242
|
+
(codePoint >= 0x05c4 && codePoint <= 0x05c5) ||
|
|
243
|
+
codePoint === 0x05c7 ||
|
|
244
|
+
(codePoint >= 0x0610 && codePoint <= 0x061a) ||
|
|
245
|
+
(codePoint >= 0x064b && codePoint <= 0x065f) ||
|
|
246
|
+
codePoint === 0x0670 ||
|
|
247
|
+
(codePoint >= 0x06d6 && codePoint <= 0x06dc) ||
|
|
248
|
+
(codePoint >= 0x06df && codePoint <= 0x06e4) ||
|
|
249
|
+
(codePoint >= 0x06e7 && codePoint <= 0x06e8) ||
|
|
250
|
+
(codePoint >= 0x06ea && codePoint <= 0x06ed) ||
|
|
251
|
+
codePoint === 0x0711 ||
|
|
252
|
+
(codePoint >= 0x0730 && codePoint <= 0x074a) ||
|
|
253
|
+
(codePoint >= 0x07a6 && codePoint <= 0x07b0) ||
|
|
254
|
+
(codePoint >= 0x07eb && codePoint <= 0x07f3) ||
|
|
255
|
+
(codePoint >= 0x0816 && codePoint <= 0x0819) ||
|
|
256
|
+
(codePoint >= 0x081b && codePoint <= 0x0823) ||
|
|
257
|
+
(codePoint >= 0x0825 && codePoint <= 0x0827) ||
|
|
258
|
+
(codePoint >= 0x0829 && codePoint <= 0x082d) ||
|
|
259
|
+
(codePoint >= 0x0859 && codePoint <= 0x085b) ||
|
|
260
|
+
(codePoint >= 0x08d3 && codePoint <= 0x08e1) ||
|
|
261
|
+
(codePoint >= 0x08e3 && codePoint <= 0x0902) ||
|
|
262
|
+
(codePoint >= 0x093a && codePoint <= 0x093c) ||
|
|
263
|
+
codePoint === 0x094d ||
|
|
264
|
+
(codePoint >= 0x0951 && codePoint <= 0x0957) ||
|
|
265
|
+
(codePoint >= 0x0962 && codePoint <= 0x0963) ||
|
|
266
|
+
(codePoint >= 0x1ab0 && codePoint <= 0x1aff) ||
|
|
267
|
+
(codePoint >= 0x1dc0 && codePoint <= 0x1dff) ||
|
|
268
|
+
(codePoint >= 0x20d0 && codePoint <= 0x20ff) ||
|
|
269
|
+
(codePoint >= 0xfe00 && codePoint <= 0xfe0f) ||
|
|
270
|
+
(codePoint >= 0xfe20 && codePoint <= 0xfe2f) ||
|
|
271
|
+
(codePoint >= 0xe0100 && codePoint <= 0xe01ef));
|
|
272
|
+
}
|
|
273
|
+
function isWideCodePoint(codePoint) {
|
|
274
|
+
return (codePoint >= 0x1100 &&
|
|
275
|
+
(codePoint <= 0x115f ||
|
|
276
|
+
codePoint === 0x2329 ||
|
|
277
|
+
codePoint === 0x232a ||
|
|
278
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
279
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
280
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
281
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
282
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
283
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
284
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
285
|
+
(codePoint >= 0x1f300 && codePoint <= 0x1faff) ||
|
|
286
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd)));
|
|
287
|
+
}
|
|
288
|
+
function nextGrapheme(text, start) {
|
|
289
|
+
if (GRAPHEME_SEGMENTER) {
|
|
290
|
+
const iterator = GRAPHEME_SEGMENTER.segment(text.slice(start))[Symbol.iterator]();
|
|
291
|
+
const next = iterator.next();
|
|
292
|
+
if (!next.done && next.value.segment) {
|
|
293
|
+
return { value: next.value.segment, nextIndex: start + next.value.segment.length };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const first = text.codePointAt(start);
|
|
297
|
+
if (first === undefined)
|
|
298
|
+
return { value: '', nextIndex: start + 1 };
|
|
299
|
+
let value = String.fromCodePoint(first);
|
|
300
|
+
let nextIndex = start + value.length;
|
|
301
|
+
while (nextIndex < text.length) {
|
|
302
|
+
const codePoint = text.codePointAt(nextIndex);
|
|
303
|
+
if (codePoint === undefined || !isZeroWidthCodePoint(codePoint))
|
|
304
|
+
break;
|
|
305
|
+
const mark = String.fromCodePoint(codePoint);
|
|
306
|
+
value += mark;
|
|
307
|
+
nextIndex += mark.length;
|
|
308
|
+
}
|
|
309
|
+
return { value, nextIndex };
|
|
310
|
+
}
|
|
311
|
+
function terminalCellWidth(grapheme) {
|
|
312
|
+
let hasVisible = false;
|
|
313
|
+
let hasWide = false;
|
|
314
|
+
for (let i = 0; i < grapheme.length; i++) {
|
|
315
|
+
const codePoint = grapheme.codePointAt(i);
|
|
316
|
+
if (codePoint === undefined)
|
|
317
|
+
continue;
|
|
318
|
+
if (codePoint > 0xffff)
|
|
319
|
+
i++;
|
|
320
|
+
if (isZeroWidthCodePoint(codePoint) || codePoint < 0x20 || (codePoint >= 0x7f && codePoint < 0xa0)) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
hasVisible = true;
|
|
324
|
+
if (isWideCodePoint(codePoint))
|
|
325
|
+
hasWide = true;
|
|
326
|
+
}
|
|
327
|
+
if (!hasVisible)
|
|
328
|
+
return 0;
|
|
329
|
+
return hasWide ? 2 : 1;
|
|
330
|
+
}
|
|
331
|
+
function truncatePaneLineByVisibleColumns(line, maxColumns) {
|
|
332
|
+
let result = '';
|
|
333
|
+
let visibleColumns = 0;
|
|
334
|
+
let sawSgr = false;
|
|
335
|
+
for (let i = 0; i < line.length; i++) {
|
|
336
|
+
if (line[i] === '\x1b') {
|
|
337
|
+
const end = findEscapeEnd(line, i);
|
|
338
|
+
const sequence = line.slice(i, end + 1);
|
|
339
|
+
if (isSgrSequence(sequence)) {
|
|
340
|
+
result += sequence;
|
|
341
|
+
sawSgr = true;
|
|
342
|
+
}
|
|
343
|
+
i = end;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const grapheme = nextGrapheme(line, i);
|
|
347
|
+
const width = terminalCellWidth(grapheme.value);
|
|
348
|
+
if (width === 0) {
|
|
349
|
+
result += grapheme.value;
|
|
350
|
+
}
|
|
351
|
+
else if (visibleColumns + width <= maxColumns) {
|
|
352
|
+
result += grapheme.value;
|
|
353
|
+
visibleColumns += width;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
i = grapheme.nextIndex - 1;
|
|
359
|
+
if (visibleColumns >= maxColumns) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (sawSgr) {
|
|
364
|
+
result += '\x1b[0m';
|
|
365
|
+
}
|
|
366
|
+
return result;
|
|
367
|
+
}
|
|
368
|
+
export function formatPaneSnapshot(lines, geometry) {
|
|
369
|
+
const cols = Math.max(1, geometry.cols);
|
|
370
|
+
const paintCols = Math.max(1, cols - 1);
|
|
371
|
+
const rows = Math.max(1, geometry.rows);
|
|
372
|
+
const parts = [];
|
|
373
|
+
for (let row = 0; row < Math.min(lines.length, rows); row++) {
|
|
374
|
+
const safeLine = truncatePaneLineByVisibleColumns(sanitizePaneLineStyles(lines[row]), paintCols);
|
|
375
|
+
parts.push(`\x1b[${row + 1};1H${safeLine}`);
|
|
376
|
+
}
|
|
377
|
+
const cursorX = Math.max(0, Math.min(cols - 1, geometry.cursorX));
|
|
378
|
+
const cursorY = Math.max(0, Math.min(rows - 1, geometry.cursorY));
|
|
379
|
+
parts.push(`\x1b[${cursorY + 1};${cursorX + 1}H`);
|
|
380
|
+
return parts.join('');
|
|
381
|
+
}
|
|
129
382
|
/** Characters unsafe in paths — shell metacharacters, quotes, and control chars */
|
|
130
383
|
const UNSAFE_PATH_CHARS = /[;&|$`(){}<>'"\n\r]/;
|
|
131
384
|
/**
|
|
@@ -135,6 +388,9 @@ const UNSAFE_PATH_CHARS = /[;&|$`(){}<>'"\n\r]/;
|
|
|
135
388
|
function isValidMuxName(name) {
|
|
136
389
|
return SAFE_MUX_NAME_PATTERN.test(name) || LEGACY_MUX_NAME_PATTERN.test(name);
|
|
137
390
|
}
|
|
391
|
+
function isValidTerminalDimension(value) {
|
|
392
|
+
return Number.isSafeInteger(value) && value > 0 && value <= 1000;
|
|
393
|
+
}
|
|
138
394
|
/**
|
|
139
395
|
* Validates that a path contains only safe characters.
|
|
140
396
|
* Prevents command injection via malformed paths.
|
|
@@ -600,14 +856,17 @@ export class TmuxManager extends EventEmitter {
|
|
|
600
856
|
// (Production uses systemd which has a clean env, but dev/test may be nested.)
|
|
601
857
|
const cleanEnv = { ...process.env };
|
|
602
858
|
delete cleanEnv.TMUX;
|
|
603
|
-
//
|
|
604
|
-
//
|
|
859
|
+
// Create the session on the dedicated socket (${this.tmux()} = `tmux -L <socket>`),
|
|
860
|
+
// launched in TMUX_LAUNCH_CWD (/tmp) rather than the real workingDir: a FUSE/rclone
|
|
861
|
+
// mount that isn't ready yet makes `getcwd` fail and breaks the spawn (see #110). The
|
|
862
|
+
// pane cd's into workingDir below via respawn-pane.
|
|
605
863
|
execSync(`${this.tmux()} new-session -ds "${muxName}" -c ${TMUX_LAUNCH_CWD}`, {
|
|
606
864
|
cwd: TMUX_LAUNCH_CWD,
|
|
607
865
|
timeout: EXEC_TIMEOUT_MS,
|
|
608
866
|
stdio: 'ignore',
|
|
609
867
|
env: cleanEnv,
|
|
610
868
|
});
|
|
869
|
+
this.resizeWindow(muxName, 120, 40);
|
|
611
870
|
// Set remain-on-exit now that the server is running — must be before respawn-pane
|
|
612
871
|
try {
|
|
613
872
|
execSync(`${this.tmux()} set-option -t "${muxName}" remain-on-exit on`, {
|
|
@@ -804,6 +1063,8 @@ export class TmuxManager extends EventEmitter {
|
|
|
804
1063
|
sessionExists(muxName) {
|
|
805
1064
|
if (IS_TEST_MODE)
|
|
806
1065
|
return false;
|
|
1066
|
+
if (!isValidMuxName(muxName))
|
|
1067
|
+
return false;
|
|
807
1068
|
try {
|
|
808
1069
|
execSync(`${this.tmux()} has-session -t "${muxName}" 2>/dev/null`, {
|
|
809
1070
|
encoding: 'utf-8',
|
|
@@ -932,14 +1193,16 @@ export class TmuxManager extends EventEmitter {
|
|
|
932
1193
|
// Process group may not exist or already terminated
|
|
933
1194
|
}
|
|
934
1195
|
}
|
|
935
|
-
// Strategy 3: Kill tmux session by name
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1196
|
+
// Strategy 3: Kill tmux session by name (guard the name before it reaches the shell)
|
|
1197
|
+
if (isValidMuxName(session.muxName)) {
|
|
1198
|
+
try {
|
|
1199
|
+
execSync(`${this.tmux()} kill-session -t "${session.muxName}" 2>/dev/null`, {
|
|
1200
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
catch {
|
|
1204
|
+
// Session may already be dead
|
|
1205
|
+
}
|
|
943
1206
|
}
|
|
944
1207
|
// Strategy 4: Direct kill by PID as final fallback
|
|
945
1208
|
if (this.isProcessAlive(currentPid)) {
|
|
@@ -1031,6 +1294,14 @@ export class TmuxManager extends EventEmitter {
|
|
|
1031
1294
|
for (const [sessionName, pid] of active) {
|
|
1032
1295
|
if (!sessionName.startsWith('codeman-') && !sessionName.startsWith('claudeman-'))
|
|
1033
1296
|
continue;
|
|
1297
|
+
// Only admit names that pass the safe-name pattern. A foreign process on the
|
|
1298
|
+
// shared `tmux -L codeman` socket could create a `codeman-…` session whose name
|
|
1299
|
+
// contains shell metacharacters; rejecting it here keeps it out of this.sessions
|
|
1300
|
+
// and away from the name-interpolating tmux call sites (M1).
|
|
1301
|
+
if (!isValidMuxName(sessionName)) {
|
|
1302
|
+
console.warn(`[TmuxManager] Skipping discovered tmux session with unsafe name: ${sessionName}`);
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1034
1305
|
if (knownMuxNames.has(sessionName))
|
|
1035
1306
|
continue;
|
|
1036
1307
|
const fragment = sessionName.replace(/^(?:codeman|claudeman)-/, '');
|
|
@@ -1488,8 +1759,11 @@ export class TmuxManager extends EventEmitter {
|
|
|
1488
1759
|
}
|
|
1489
1760
|
}
|
|
1490
1761
|
/**
|
|
1491
|
-
* Capture the current
|
|
1492
|
-
*
|
|
1762
|
+
* Capture the current visible text and SGR styles of a specific pane.
|
|
1763
|
+
*
|
|
1764
|
+
* `capture-pane -e` is sanitized by `formatPaneSnapshot`: SGR color/style
|
|
1765
|
+
* codes are preserved, while cursor/erase/scroll-region controls are stripped
|
|
1766
|
+
* before rows are repainted at absolute positions in browser xterm.
|
|
1493
1767
|
*/
|
|
1494
1768
|
capturePaneBuffer(muxName, paneTarget) {
|
|
1495
1769
|
if (IS_TEST_MODE)
|
|
@@ -1504,16 +1778,63 @@ export class TmuxManager extends EventEmitter {
|
|
|
1504
1778
|
}
|
|
1505
1779
|
const target = paneTarget.startsWith('%') ? `${muxName}.${paneTarget}` : `${muxName}.%${paneTarget}`;
|
|
1506
1780
|
try {
|
|
1507
|
-
|
|
1781
|
+
const buffer = execSync(`${this.tmux()} capture-pane -p -e -t ${shellescape(target)}`, {
|
|
1508
1782
|
encoding: 'utf-8',
|
|
1509
1783
|
timeout: EXEC_TIMEOUT_MS,
|
|
1510
|
-
});
|
|
1784
|
+
}).replace(/\n+$/g, '');
|
|
1785
|
+
try {
|
|
1786
|
+
const cursor = execSync(`${this.tmux()} display-message -p -t ${shellescape(target)} '#{cursor_x} #{cursor_y} #{pane_width} #{pane_height}'`, {
|
|
1787
|
+
encoding: 'utf-8',
|
|
1788
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
1789
|
+
}).trim();
|
|
1790
|
+
const [cursorX, cursorY, cols, rows] = cursor.split(/\s+/).map((value) => parseInt(value, 10));
|
|
1791
|
+
if (Number.isFinite(cursorX) &&
|
|
1792
|
+
Number.isFinite(cursorY) &&
|
|
1793
|
+
Number.isFinite(cols) &&
|
|
1794
|
+
Number.isFinite(rows) &&
|
|
1795
|
+
cursorX >= 0 &&
|
|
1796
|
+
cursorY >= 0 &&
|
|
1797
|
+
cols > 0 &&
|
|
1798
|
+
rows > 0) {
|
|
1799
|
+
return formatPaneSnapshot(buffer.split('\n'), { cols, rows, cursorX, cursorY });
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
catch (cursorErr) {
|
|
1803
|
+
console.error('[TmuxManager] Failed to query pane cursor after capture:', cursorErr);
|
|
1804
|
+
}
|
|
1805
|
+
return buffer;
|
|
1511
1806
|
}
|
|
1512
1807
|
catch (err) {
|
|
1513
1808
|
console.error('[TmuxManager] Failed to capture pane buffer:', err);
|
|
1514
1809
|
return null;
|
|
1515
1810
|
}
|
|
1516
1811
|
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Capture the active pane for a tmux session.
|
|
1814
|
+
*
|
|
1815
|
+
* Pane ids are not stable across respawns or restores, so callers should not
|
|
1816
|
+
* assume the first pane remains `%0`.
|
|
1817
|
+
*/
|
|
1818
|
+
captureActivePaneBuffer(muxName) {
|
|
1819
|
+
if (IS_TEST_MODE)
|
|
1820
|
+
return '';
|
|
1821
|
+
if (!isValidMuxName(muxName)) {
|
|
1822
|
+
console.error('[TmuxManager] Invalid session name in captureActivePaneBuffer:', muxName);
|
|
1823
|
+
return null;
|
|
1824
|
+
}
|
|
1825
|
+
try {
|
|
1826
|
+
const output = execSync(`${this.tmux()} list-panes -t ${shellescape(muxName)} -F '#{pane_id}:#{pane_active}'`, {
|
|
1827
|
+
encoding: 'utf-8',
|
|
1828
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
1829
|
+
}).trim();
|
|
1830
|
+
const target = resolveActivePaneTarget(output);
|
|
1831
|
+
return target ? this.capturePaneBuffer(muxName, target) : null;
|
|
1832
|
+
}
|
|
1833
|
+
catch (err) {
|
|
1834
|
+
console.error('[TmuxManager] Failed to resolve active pane for capture:', err);
|
|
1835
|
+
return null;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1517
1838
|
/**
|
|
1518
1839
|
* Start piping pane output to a file using tmux pipe-pane.
|
|
1519
1840
|
* Only pipes output direction (-O) to avoid echoing input.
|
|
@@ -1579,6 +1900,43 @@ export class TmuxManager extends EventEmitter {
|
|
|
1579
1900
|
getAttachArgs(muxName) {
|
|
1580
1901
|
return ['-L', this.tmuxSocket, 'attach-session', '-t', muxName];
|
|
1581
1902
|
}
|
|
1903
|
+
setManualWindowSize(muxName) {
|
|
1904
|
+
if (!isValidMuxName(muxName)) {
|
|
1905
|
+
console.error('[TmuxManager] Invalid session name in setManualWindowSize:', muxName);
|
|
1906
|
+
return false;
|
|
1907
|
+
}
|
|
1908
|
+
try {
|
|
1909
|
+
execSync(`${this.tmux()} set-window-option -t ${shellescape(muxName)} window-size manual`, {
|
|
1910
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
1911
|
+
stdio: 'ignore',
|
|
1912
|
+
});
|
|
1913
|
+
return true;
|
|
1914
|
+
}
|
|
1915
|
+
catch (err) {
|
|
1916
|
+
console.error('[TmuxManager] Failed to set manual window size:', err);
|
|
1917
|
+
return false;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
resizeWindow(muxName, cols, rows) {
|
|
1921
|
+
if (!isValidMuxName(muxName)) {
|
|
1922
|
+
console.error('[TmuxManager] Invalid session name in resizeWindow:', muxName);
|
|
1923
|
+
return false;
|
|
1924
|
+
}
|
|
1925
|
+
if (!isValidTerminalDimension(cols) || !isValidTerminalDimension(rows)) {
|
|
1926
|
+
console.error('[TmuxManager] Invalid resize dimensions:', { cols, rows });
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
1929
|
+
// Fire-and-forget: this runs on the interactive resize path (WS {t:'z'} and
|
|
1930
|
+
// HTTP /resize), so use a non-blocking exec — a slow/hung tmux must not stall
|
|
1931
|
+
// the Fastify event loop while other sessions' input/SSE are served. The sole
|
|
1932
|
+
// caller (Session.resize) ignores the result, and under `window-size manual`
|
|
1933
|
+
// the subsequent ptyProcess.resize is subordinate to this authoritative size.
|
|
1934
|
+
exec(`${this.tmux()} resize-window -t ${shellescape(muxName)} -x ${cols} -y ${rows}`, { timeout: EXEC_TIMEOUT_MS }, (err) => {
|
|
1935
|
+
if (err)
|
|
1936
|
+
console.error('[TmuxManager] Failed to resize tmux window:', err);
|
|
1937
|
+
});
|
|
1938
|
+
return true;
|
|
1939
|
+
}
|
|
1582
1940
|
isAvailable() {
|
|
1583
1941
|
return TmuxManager.isTmuxAvailable();
|
|
1584
1942
|
}
|