@sheepbun/yips 0.1.1 → 0.1.46

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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/bin/yips.js +15 -0
  4. package/package.json +21 -128
  5. package/postinstall.js +50 -0
  6. package/dist/agent/commands/command-catalog.js +0 -243
  7. package/dist/agent/commands/commands.js +0 -418
  8. package/dist/agent/conductor.js +0 -118
  9. package/dist/agent/context/code-context.js +0 -68
  10. package/dist/agent/context/memory-store.js +0 -159
  11. package/dist/agent/context/session-store.js +0 -211
  12. package/dist/agent/protocol/tool-protocol.js +0 -160
  13. package/dist/agent/skills/skills.js +0 -327
  14. package/dist/agent/tools/tool-executor.js +0 -415
  15. package/dist/agent/tools/tool-safety.js +0 -52
  16. package/dist/app/index.js +0 -35
  17. package/dist/app/repl.js +0 -105
  18. package/dist/app/update-check.js +0 -132
  19. package/dist/app/version.js +0 -51
  20. package/dist/code-context.js +0 -68
  21. package/dist/colors.js +0 -204
  22. package/dist/command-catalog.js +0 -242
  23. package/dist/commands.js +0 -350
  24. package/dist/conductor.js +0 -94
  25. package/dist/config/config.js +0 -335
  26. package/dist/config/hooks.js +0 -187
  27. package/dist/config.js +0 -335
  28. package/dist/downloader-state.js +0 -302
  29. package/dist/downloader-ui.js +0 -289
  30. package/dist/gateway/adapters/discord.js +0 -108
  31. package/dist/gateway/adapters/formatting.js +0 -96
  32. package/dist/gateway/adapters/telegram.js +0 -106
  33. package/dist/gateway/adapters/types.js +0 -2
  34. package/dist/gateway/adapters/whatsapp.js +0 -124
  35. package/dist/gateway/auth-policy.js +0 -66
  36. package/dist/gateway/core.js +0 -87
  37. package/dist/gateway/headless-conductor.js +0 -328
  38. package/dist/gateway/message-router.js +0 -23
  39. package/dist/gateway/rate-limiter.js +0 -48
  40. package/dist/gateway/runtime/backend-policy.js +0 -18
  41. package/dist/gateway/runtime/discord-bot.js +0 -104
  42. package/dist/gateway/runtime/discord-main.js +0 -69
  43. package/dist/gateway/session-manager.js +0 -77
  44. package/dist/gateway/types.js +0 -2
  45. package/dist/hardware.js +0 -92
  46. package/dist/hooks.js +0 -187
  47. package/dist/index.js +0 -34
  48. package/dist/input-engine.js +0 -250
  49. package/dist/llama-client.js +0 -227
  50. package/dist/llama-server.js +0 -620
  51. package/dist/llm/llama-client.js +0 -227
  52. package/dist/llm/llama-server.js +0 -620
  53. package/dist/llm/token-counter.js +0 -47
  54. package/dist/memory-store.js +0 -159
  55. package/dist/messages.js +0 -59
  56. package/dist/model-downloader.js +0 -382
  57. package/dist/model-manager-state.js +0 -118
  58. package/dist/model-manager-ui.js +0 -194
  59. package/dist/model-manager.js +0 -190
  60. package/dist/models/hardware.js +0 -92
  61. package/dist/models/model-downloader.js +0 -382
  62. package/dist/models/model-manager.js +0 -190
  63. package/dist/prompt-box.js +0 -78
  64. package/dist/prompt-composer.js +0 -498
  65. package/dist/repl.js +0 -105
  66. package/dist/session-store.js +0 -211
  67. package/dist/spinner.js +0 -76
  68. package/dist/title-box.js +0 -388
  69. package/dist/token-counter.js +0 -47
  70. package/dist/tool-executor.js +0 -415
  71. package/dist/tool-protocol.js +0 -121
  72. package/dist/tool-safety.js +0 -52
  73. package/dist/tui/app.js +0 -2553
  74. package/dist/tui/startup.js +0 -56
  75. package/dist/tui-input-routing.js +0 -53
  76. package/dist/tui.js +0 -51
  77. package/dist/types/app-types.js +0 -2
  78. package/dist/types.js +0 -2
  79. package/dist/ui/colors.js +0 -204
  80. package/dist/ui/downloader/downloader-state.js +0 -302
  81. package/dist/ui/downloader/downloader-ui.js +0 -289
  82. package/dist/ui/input/input-engine.js +0 -250
  83. package/dist/ui/input/tui-input-routing.js +0 -53
  84. package/dist/ui/input/vt-session.js +0 -168
  85. package/dist/ui/messages.js +0 -59
  86. package/dist/ui/model-manager/model-manager-state.js +0 -118
  87. package/dist/ui/model-manager/model-manager-ui.js +0 -194
  88. package/dist/ui/prompt/prompt-box.js +0 -78
  89. package/dist/ui/prompt/prompt-composer.js +0 -498
  90. package/dist/ui/spinner.js +0 -76
  91. package/dist/ui/title-box.js +0 -388
  92. package/dist/ui/tui/app.js +0 -6
  93. package/dist/ui/tui/autocomplete.js +0 -85
  94. package/dist/ui/tui/constants.js +0 -18
  95. package/dist/ui/tui/history.js +0 -29
  96. package/dist/ui/tui/layout.js +0 -341
  97. package/dist/ui/tui/runtime-core.js +0 -2584
  98. package/dist/ui/tui/runtime-utils.js +0 -53
  99. package/dist/ui/tui/start-tui.js +0 -54
  100. package/dist/ui/tui/startup.js +0 -56
  101. package/dist/version.js +0 -51
  102. package/dist/vt-session.js +0 -168
  103. package/install.sh +0 -457
@@ -1,289 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renderDownloaderLines = renderDownloaderLines;
4
- const colors_1 = require("#ui/colors");
5
- const TAB_INACTIVE = { r: 0x88, g: 0x88, b: 0x88 };
6
- const BLACK = { r: 0x00, g: 0x00, b: 0x00 };
7
- const FOCUS_ACCENT_BG = { r: 0xff, g: 0xcc, b: 0xff };
8
- const DOWNLOADER_MODEL_BODY_ROWS = 10;
9
- const DOWNLOADER_FILE_BODY_ROWS = 10;
10
- const ANSI_BOLD_ON = "\u001b[1m";
11
- const ANSI_BOLD_OFF = "\u001b[22m";
12
- function charLength(text) {
13
- return Array.from((0, colors_1.stripAnsi)(text)).length;
14
- }
15
- function fitRight(text, width) {
16
- const chars = Array.from(text);
17
- if (chars.length <= width) {
18
- return text;
19
- }
20
- return chars.slice(chars.length - width).join("");
21
- }
22
- function fitLeft(text, width) {
23
- const chars = Array.from(text);
24
- if (chars.length <= width) {
25
- return text;
26
- }
27
- if (width <= 3) {
28
- return chars.slice(0, width).join("");
29
- }
30
- return `${chars.slice(0, width - 3).join("")}...`;
31
- }
32
- function formatCount(value) {
33
- if (value >= 1_000_000) {
34
- return `${(value / 1_000_000).toFixed(1)}M`;
35
- }
36
- if (value >= 1_000) {
37
- return `${(value / 1_000).toFixed(1)}k`;
38
- }
39
- return String(value);
40
- }
41
- function formatSize(bytes) {
42
- if (bytes === null || bytes <= 0) {
43
- return "?";
44
- }
45
- const gib = bytes / (1024 * 1024 * 1024);
46
- return `${gib.toFixed(1)}G`;
47
- }
48
- function tabText(tab, activeTab) {
49
- const label = ` ${tab} `;
50
- if (tab === activeTab) {
51
- return `${ANSI_BOLD_ON}${(0, colors_1.horizontalGradientBackground)(label, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, BLACK)}${ANSI_BOLD_OFF}`;
52
- }
53
- return `${ANSI_BOLD_ON}${(0, colors_1.colorText)(label, TAB_INACTIVE)}${ANSI_BOLD_OFF}`;
54
- }
55
- function makeBorderTop(width) {
56
- if (width <= 1)
57
- return "╭";
58
- const prefix = "╭─── ";
59
- const titleBrand = "Yips";
60
- const titleDetail = " Model Downloader";
61
- const titleTail = " ";
62
- const prefixLen = charLength(prefix);
63
- const titleBrandLen = charLength(titleBrand);
64
- const titleDetailLen = charLength(titleDetail);
65
- const titleTailLen = charLength(titleTail);
66
- const plainTitleLen = prefixLen + titleBrandLen + titleDetailLen + titleTailLen;
67
- const fill = "─".repeat(Math.max(0, width - plainTitleLen - 1));
68
- const fillOffset = prefixLen + titleBrandLen + titleDetailLen + titleTailLen;
69
- const cornerOffset = width - 1;
70
- return `${(0, colors_1.horizontalGradientAtOffset)(prefix, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, 0, width)}${ANSI_BOLD_ON}${(0, colors_1.horizontalGradient)(titleBrand, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW)}${(0, colors_1.colorText)(titleDetail, colors_1.GRADIENT_BLUE)}${ANSI_BOLD_OFF}${(0, colors_1.horizontalGradientAtOffset)(titleTail, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, prefixLen + titleBrandLen + titleDetailLen, width)}${(0, colors_1.horizontalGradientAtOffset)(fill, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, fillOffset, width)}${(0, colors_1.horizontalGradientAtOffset)("╮", colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, cornerOffset, width)}`;
71
- }
72
- function makeBorderBottom(width) {
73
- if (width <= 1)
74
- return "╰";
75
- const mid = "─".repeat(Math.max(0, width - 2));
76
- return `${(0, colors_1.colorText)("╰", colors_1.GRADIENT_PINK)}${(0, colors_1.horizontalGradient)(mid, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW)}${(0, colors_1.colorText)("╯", colors_1.GRADIENT_YELLOW)}`;
77
- }
78
- function lineWithBorders(inner, innerWidth) {
79
- let fitted = inner;
80
- if (charLength(fitted) > innerWidth) {
81
- fitted = fitLeft((0, colors_1.stripAnsi)(inner), innerWidth);
82
- }
83
- const padding = " ".repeat(Math.max(0, innerWidth - charLength(fitted)));
84
- return `${(0, colors_1.colorText)("│", colors_1.GRADIENT_PINK)}${fitted}${padding}${(0, colors_1.colorText)("│", colors_1.GRADIENT_YELLOW)}`;
85
- }
86
- function composeSplitFooter(left, right, innerWidth) {
87
- const leftLen = charLength(left);
88
- const rightLen = charLength(right);
89
- if (leftLen + rightLen + 1 > innerWidth) {
90
- return `${left} ${fitRight(right, Math.max(1, innerWidth - leftLen - 1))}`;
91
- }
92
- const gap = " ".repeat(Math.max(1, innerWidth - leftLen - rightLen));
93
- return `${left}${gap}${right}`;
94
- }
95
- function highlightedRow(row) {
96
- const chars = Array.from(row);
97
- if (chars.length === 0)
98
- return row;
99
- const first = (0, colors_1.bgColorText)(chars[0] ?? "", FOCUS_ACCENT_BG, BLACK);
100
- const rest = chars.slice(1).join("");
101
- if (rest.length === 0) {
102
- return first;
103
- }
104
- return `${first}${(0, colors_1.horizontalGradientBackground)(rest, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, BLACK)}`;
105
- }
106
- function buildModelRows(state, contentWidth, rowCount) {
107
- const rows = [];
108
- if (state.models.length === 0) {
109
- rows.push((0, colors_1.colorText)("No compatible models found for current RAM+VRAM.", colors_1.WARNING_YELLOW));
110
- return rows;
111
- }
112
- const dlWidth = 9;
113
- const likesWidth = 6;
114
- const sizeWidth = 5;
115
- const dateWidth = 10;
116
- const staticWidth = 2 + 3 + dlWidth + 3 + likesWidth + 3 + sizeWidth + 3 + dateWidth;
117
- const modelWidth = Math.max(8, contentWidth - staticWidth);
118
- const header = ` ${"Model".padEnd(modelWidth, " ")} | ${"Downloads".padStart(dlWidth, " ")} | ${"Likes".padStart(likesWidth, " ")} | ${"Size".padStart(sizeWidth, " ")} | ${"Updated".padStart(dateWidth, " ")}`;
119
- rows.push((0, colors_1.horizontalGradient)(header, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW));
120
- const visibleRowCount = Math.max(0, rowCount - 1);
121
- const start = state.modelScrollOffset;
122
- const end = Math.min(state.models.length, start + visibleRowCount);
123
- for (let index = start; index < end; index++) {
124
- const model = state.models[index];
125
- if (!model)
126
- continue;
127
- const prefix = index === state.selectedModelIndex ? ">" : " ";
128
- const modelCell = fitLeft(model.id, modelWidth).padEnd(modelWidth, " ");
129
- const downloadsCell = `${formatCount(model.downloads)}↓`.padStart(dlWidth, " ");
130
- const likesCell = `${formatCount(model.likes)}♥`.padStart(likesWidth, " ");
131
- const sizeCell = formatSize(model.sizeBytes).padStart(sizeWidth, " ");
132
- const dateCell = model.lastModified.slice(0, 10).padStart(dateWidth, " ");
133
- const row = `${prefix} ${modelCell} | ${downloadsCell} | ${likesCell} | ${sizeCell} | ${dateCell}`;
134
- rows.push(index === state.selectedModelIndex ? highlightedRow(row) : row);
135
- }
136
- return rows;
137
- }
138
- function toFileFitLabel(state, file) {
139
- if (!file.canRun) {
140
- return file.reason;
141
- }
142
- if (file.sizeBytes === null || file.sizeBytes <= 0) {
143
- return file.reason;
144
- }
145
- if (!Number.isFinite(state.vramGb) || state.vramGb <= 0) {
146
- return file.reason;
147
- }
148
- const vramBytes = state.vramGb * 1024 ** 3;
149
- return file.sizeBytes <= vramBytes ? "Fits on GPU" : "Fits on GPU+CPU";
150
- }
151
- function buildFileRows(state, contentWidth, rowCount) {
152
- const rows = [];
153
- if (state.files.length === 0) {
154
- rows.push((0, colors_1.colorText)("No GGUF files found for this model.", colors_1.WARNING_YELLOW));
155
- return rows;
156
- }
157
- const quantWidth = 21;
158
- const sizeWidth = 5;
159
- const fitWidth = 16;
160
- const staticWidth = 2 + 3 + quantWidth + 3 + sizeWidth + 3 + fitWidth;
161
- const fileWidth = Math.max(8, contentWidth - staticWidth);
162
- const header = ` ${"File".padEnd(fileWidth, " ")} | ${"Quant".padEnd(quantWidth, " ")} | ${"Size".padStart(sizeWidth, " ")} | ${"Fit".padEnd(fitWidth, " ")}`;
163
- rows.push((0, colors_1.horizontalGradient)(header, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW));
164
- const visibleRowCount = Math.max(0, rowCount - 1);
165
- const start = state.fileScrollOffset;
166
- const end = Math.min(state.files.length, start + visibleRowCount);
167
- for (let index = start; index < end; index++) {
168
- const file = state.files[index];
169
- if (!file)
170
- continue;
171
- const prefix = index === state.selectedFileIndex ? ">" : " ";
172
- const basename = file.path.includes("/")
173
- ? (file.path.split("/").pop() ?? file.path)
174
- : file.path;
175
- const fileCell = fitLeft(basename, fileWidth).padEnd(fileWidth, " ");
176
- const quantCell = fitLeft(file.quant, quantWidth).padEnd(quantWidth, " ");
177
- const sizeCell = formatSize(file.sizeBytes).padStart(sizeWidth, " ");
178
- const fitCell = fitLeft(toFileFitLabel(state, file), fitWidth).padEnd(fitWidth, " ");
179
- const row = `${prefix} ${fileCell} | ${quantCell} | ${sizeCell} | ${fitCell}`;
180
- if (index === state.selectedFileIndex) {
181
- rows.push(highlightedRow(row));
182
- continue;
183
- }
184
- rows.push((0, colors_1.colorText)(row, file.canRun ? colors_1.SUCCESS_GREEN : colors_1.ERROR_RED));
185
- }
186
- return rows;
187
- }
188
- function fillRows(rows, rowCount) {
189
- if (rows.length >= rowCount) {
190
- return rows.slice(0, rowCount);
191
- }
192
- return [...rows, ...new Array(rowCount - rows.length).fill("")];
193
- }
194
- function buildProgressBar(innerWidth, bytesDownloaded, totalBytes) {
195
- if (totalBytes === null || totalBytes <= 0) {
196
- return "Progress: downloading...";
197
- }
198
- const suffix = ` ${((bytesDownloaded / totalBytes) * 100).toFixed(1)}%`;
199
- const barWidth = Math.max(1, innerWidth - suffix.length - 2);
200
- const progress = Math.max(0, Math.min(1, bytesDownloaded / totalBytes));
201
- const filled = Math.round(barWidth * progress);
202
- const empty = Math.max(0, barWidth - filled);
203
- return `[${"█".repeat(filled)}${"░".repeat(empty)}]${suffix}`;
204
- }
205
- function buildDownloadingRows(state, innerWidth) {
206
- if (!state.download) {
207
- return [`Loading: ${state.loadingMessage}`];
208
- }
209
- const filename = state.download.filename.includes("/")
210
- ? (state.download.filename.split("/").pop() ?? state.download.filename)
211
- : state.download.filename;
212
- const rows = [];
213
- rows.push((0, colors_1.colorText)(`Downloading ${fitLeft(filename, Math.max(1, innerWidth - 12))}`, colors_1.GRADIENT_BLUE));
214
- rows.push((0, colors_1.colorText)(buildProgressBar(innerWidth, state.download.bytesDownloaded, state.download.totalBytes), colors_1.SUCCESS_GREEN));
215
- return rows;
216
- }
217
- function buildCancelConfirmRows(innerWidth) {
218
- const message = fitLeft("Cancel download and delete partial file?", Math.max(1, innerWidth));
219
- return [(0, colors_1.colorText)(message, colors_1.WARNING_YELLOW), ""];
220
- }
221
- function renderDownloaderLines(options) {
222
- const width = Math.max(20, options.width);
223
- const state = options.state;
224
- const innerWidth = Math.max(1, width - 2);
225
- const rows = [];
226
- rows.push(makeBorderTop(width));
227
- if (state.phase !== "downloading") {
228
- const tabs = [
229
- tabText("Most Downloaded", state.tab),
230
- tabText("Top Rated", state.tab),
231
- tabText("Newest", state.tab)
232
- ].join(" ");
233
- const memText = `RAM: ${state.ramGb.toFixed(1)}GB | VRAM: ${state.vramGb.toFixed(1)}GB | Disk: ${state.diskFreeGb.toFixed(1)}GB`;
234
- const memWidth = Math.min(charLength(memText), Math.max(24, innerWidth - 10));
235
- const tabsWidth = Math.max(1, innerWidth - memWidth - 1);
236
- let tabsCell = tabs;
237
- if (charLength(tabsCell) > tabsWidth) {
238
- tabsCell = fitLeft(" Most Downloaded Top Rated Newest ", tabsWidth);
239
- }
240
- const tabsPadding = " ".repeat(Math.max(0, tabsWidth - charLength(tabsCell)));
241
- rows.push(lineWithBorders(`${tabsCell}${tabsPadding} ${fitRight(memText, memWidth)}`, innerWidth));
242
- }
243
- let bodyRows = [];
244
- if (state.view === "models") {
245
- if (state.phase === "loading-models") {
246
- bodyRows = fillRows([`Loading: ${state.loadingMessage}`], DOWNLOADER_MODEL_BODY_ROWS);
247
- }
248
- else if (state.phase === "error" && state.errorMessage.length > 0) {
249
- bodyRows = fillRows([`Error: ${state.errorMessage}`], DOWNLOADER_MODEL_BODY_ROWS);
250
- }
251
- else {
252
- bodyRows = fillRows(buildModelRows(state, innerWidth, DOWNLOADER_MODEL_BODY_ROWS), DOWNLOADER_MODEL_BODY_ROWS);
253
- }
254
- }
255
- else if (state.phase === "downloading") {
256
- bodyRows = state.cancelConfirmOpen
257
- ? buildCancelConfirmRows(innerWidth)
258
- : buildDownloadingRows(state, innerWidth);
259
- }
260
- else if (state.phase === "loading-files") {
261
- bodyRows = fillRows([`Loading: ${state.loadingMessage}`], DOWNLOADER_FILE_BODY_ROWS);
262
- }
263
- else if (state.phase === "error" && state.errorMessage.length > 0) {
264
- bodyRows = fillRows([`Error: ${state.errorMessage}`], DOWNLOADER_FILE_BODY_ROWS);
265
- }
266
- else {
267
- const fileRows = [
268
- (0, colors_1.colorText)(`Files for ${fitLeft(state.selectedRepoId, Math.max(1, innerWidth - 10))}`, colors_1.GRADIENT_BLUE),
269
- ...buildFileRows(state, innerWidth, DOWNLOADER_FILE_BODY_ROWS - 1)
270
- ];
271
- bodyRows = fillRows(fileRows, DOWNLOADER_FILE_BODY_ROWS);
272
- }
273
- rows.push(...bodyRows.map((row) => lineWithBorders(row, innerWidth)));
274
- if (state.phase === "downloading") {
275
- const status = state.download?.statusText ?? "Downloading...";
276
- const left = state.cancelConfirmOpen ? "[Enter] Yes [Esc] No" : "[Esc] Cancel";
277
- const statusWidth = Math.max(1, innerWidth - Array.from(left).length - 1);
278
- const footer = composeSplitFooter(left, fitRight(status, statusWidth), innerWidth);
279
- rows.push(lineWithBorders((0, colors_1.horizontalGradient)(footer, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW), innerWidth));
280
- }
281
- else {
282
- const footer = state.view === "models"
283
- ? "[Enter] Select [↑/↓] Move [←/→] Sort [Esc] Close"
284
- : "[Enter] Download [↑/↓] Move [Esc] Back";
285
- rows.push(lineWithBorders((0, colors_1.horizontalGradient)(footer, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW), innerWidth));
286
- }
287
- rows.push(makeBorderBottom(width));
288
- return rows;
289
- }
@@ -1,250 +0,0 @@
1
- "use strict";
2
- /** Raw-stdin input parser for deterministic prompt editing actions. */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.InputEngine = void 0;
5
- exports.parseCsiSequence = parseCsiSequence;
6
- const node_string_decoder_1 = require("node:string_decoder");
7
- function parseInteger(value) {
8
- const parsed = Number(value);
9
- return Number.isInteger(parsed) ? parsed : null;
10
- }
11
- function isEnterKeyCode(code) {
12
- return code === 1 || code === 10 || code === 13 || code === 57414;
13
- }
14
- function toEnterAction(modifier) {
15
- return modifier > 1 ? { type: "newline" } : { type: "submit" };
16
- }
17
- function parseEnterActionFromCsiBody(body, final) {
18
- if (final === "u" || final === "~" || final === "M") {
19
- const keyOnly = body.match(/^(\d+)$/);
20
- if (keyOnly) {
21
- const keyCode = parseInteger(keyOnly[1] ?? "");
22
- if (keyCode !== null && isEnterKeyCode(keyCode)) {
23
- return { type: "submit" };
24
- }
25
- }
26
- const keyAndModifier = body.match(/^(\d+);(\d+)$/);
27
- if (keyAndModifier) {
28
- const keyCode = parseInteger(keyAndModifier[1] ?? "");
29
- const modifier = parseInteger(keyAndModifier[2] ?? "");
30
- if (keyCode !== null && modifier !== null && isEnterKeyCode(keyCode) && modifier >= 1) {
31
- return toEnterAction(modifier);
32
- }
33
- }
34
- }
35
- if (final === "~") {
36
- const legacyModifyOtherKeys = body.match(/^27;(\d+);(\d+)$/);
37
- if (legacyModifyOtherKeys) {
38
- const first = parseInteger(legacyModifyOtherKeys[1] ?? "");
39
- const second = parseInteger(legacyModifyOtherKeys[2] ?? "");
40
- if (first === null || second === null) {
41
- return null;
42
- }
43
- if (isEnterKeyCode(first) && second >= 1) {
44
- return toEnterAction(second);
45
- }
46
- if (isEnterKeyCode(second) && first >= 1) {
47
- return toEnterAction(first);
48
- }
49
- }
50
- }
51
- return null;
52
- }
53
- function parseMouseActionFromCsiBody(body, final) {
54
- if (final !== "M" && final !== "m") {
55
- return null;
56
- }
57
- if (!body.startsWith("<")) {
58
- return null;
59
- }
60
- const params = body.slice(1).split(";");
61
- const buttonCode = parseInteger(params[0] ?? "");
62
- if (buttonCode === null) {
63
- return null;
64
- }
65
- // SGR mouse wheel encodes bit 6 for wheel + bit 0 for direction.
66
- // Modifier bits may be present (shift/alt/ctrl), so match by bitmask.
67
- if ((buttonCode & 0x40) !== 0) {
68
- return (buttonCode & 0x01) === 0
69
- ? { type: "scroll-line-up" }
70
- : { type: "scroll-line-down" };
71
- }
72
- return null;
73
- }
74
- function parseCsiSequence(sequence) {
75
- if (!sequence.startsWith("\x1b[")) {
76
- return null;
77
- }
78
- if (sequence.length < 3) {
79
- return null;
80
- }
81
- const final = sequence[sequence.length - 1] ?? "";
82
- const body = sequence.slice(2, -1);
83
- const enterAction = parseEnterActionFromCsiBody(body, final);
84
- if (enterAction) {
85
- return enterAction;
86
- }
87
- const mouseAction = parseMouseActionFromCsiBody(body, final);
88
- if (mouseAction) {
89
- return mouseAction;
90
- }
91
- if (final === "A")
92
- return { type: "move-up" };
93
- if (final === "B")
94
- return { type: "move-down" };
95
- if (final === "C")
96
- return { type: "move-right" };
97
- if (final === "D")
98
- return { type: "move-left" };
99
- if (final === "H")
100
- return { type: "home" };
101
- if (final === "F")
102
- return { type: "end" };
103
- if (final === "Z")
104
- return { type: "tab" };
105
- if (final === "~") {
106
- const firstParam = body.split(";")[0] ?? "";
107
- if (firstParam === "1" || firstParam === "7")
108
- return { type: "home" };
109
- if (firstParam === "4" || firstParam === "8")
110
- return { type: "end" };
111
- if (firstParam === "3")
112
- return { type: "delete" };
113
- if (firstParam === "5")
114
- return { type: "scroll-page-up" };
115
- if (firstParam === "6")
116
- return { type: "scroll-page-down" };
117
- }
118
- return null;
119
- }
120
- function isCsiFinalByte(byte) {
121
- return byte >= 0x40 && byte <= 0x7e;
122
- }
123
- function parseEscapeSequence(buffer, startIndex) {
124
- if (startIndex + 1 >= buffer.length) {
125
- return null;
126
- }
127
- const second = buffer[startIndex + 1] ?? 0;
128
- if (second === 0x5b) {
129
- let index = startIndex + 2;
130
- while (index < buffer.length) {
131
- const byte = buffer[index] ?? 0;
132
- if (isCsiFinalByte(byte)) {
133
- const sequence = buffer.subarray(startIndex, index + 1).toString("latin1");
134
- return { action: parseCsiSequence(sequence), nextIndex: index + 1 };
135
- }
136
- index += 1;
137
- }
138
- return null;
139
- }
140
- if (second === 0x4f) {
141
- if (startIndex + 2 >= buffer.length) {
142
- return null;
143
- }
144
- const third = buffer[startIndex + 2] ?? 0;
145
- if (third === 0x4d) {
146
- return { action: { type: "submit" }, nextIndex: startIndex + 3 };
147
- }
148
- return { action: null, nextIndex: startIndex + 3 };
149
- }
150
- if (second === 0x0d || second === 0x0a) {
151
- return { action: { type: "newline" }, nextIndex: startIndex + 2 };
152
- }
153
- return { action: null, nextIndex: startIndex + 2 };
154
- }
155
- class InputEngine {
156
- pending = Buffer.alloc(0);
157
- decoder = new node_string_decoder_1.StringDecoder("utf8");
158
- reset() {
159
- this.pending = Buffer.alloc(0);
160
- this.decoder.end();
161
- }
162
- pushChunk(chunk) {
163
- const incoming = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "latin1");
164
- if (incoming.length === 0) {
165
- return [];
166
- }
167
- this.pending =
168
- this.pending.length > 0 ? Buffer.concat([this.pending, incoming]) : Buffer.from(incoming);
169
- const actions = [];
170
- let index = 0;
171
- let textStart = null;
172
- const flushText = (endIndex) => {
173
- if (textStart === null || endIndex <= textStart) {
174
- textStart = null;
175
- return;
176
- }
177
- const text = this.decoder.write(this.pending.subarray(textStart, endIndex));
178
- if (text.length > 0) {
179
- actions.push({ type: "insert", text });
180
- }
181
- textStart = null;
182
- };
183
- while (index < this.pending.length) {
184
- const byte = this.pending[index] ?? 0;
185
- if (byte === 0x1b) {
186
- flushText(index);
187
- const parsed = parseEscapeSequence(this.pending, index);
188
- if (!parsed) {
189
- break;
190
- }
191
- if (parsed.action) {
192
- actions.push(parsed.action);
193
- }
194
- index = parsed.nextIndex;
195
- continue;
196
- }
197
- if (byte === 0x03) {
198
- flushText(index);
199
- actions.push({ type: "cancel" });
200
- index += 1;
201
- continue;
202
- }
203
- if (byte === 0x7f || byte === 0x08) {
204
- flushText(index);
205
- actions.push({ type: "backspace" });
206
- index += 1;
207
- continue;
208
- }
209
- if (byte === 0x0d) {
210
- flushText(index);
211
- actions.push({ type: "submit" });
212
- index += 1;
213
- if (this.pending[index] === 0x0a) {
214
- index += 1;
215
- }
216
- continue;
217
- }
218
- if (byte === 0x0a) {
219
- flushText(index);
220
- actions.push({ type: "newline" });
221
- index += 1;
222
- continue;
223
- }
224
- if (byte === 0x09) {
225
- flushText(index);
226
- actions.push({ type: "tab" });
227
- index += 1;
228
- continue;
229
- }
230
- if (byte < 0x20) {
231
- flushText(index);
232
- index += 1;
233
- continue;
234
- }
235
- if (textStart === null) {
236
- textStart = index;
237
- }
238
- index += 1;
239
- }
240
- // Treat lone ESC as cancel (close modal/back) when it is not part of a CSI sequence.
241
- if (index < this.pending.length && this.pending[index] === 0x1b && this.pending.length - index === 1) {
242
- actions.push({ type: "cancel" });
243
- index += 1;
244
- }
245
- flushText(index);
246
- this.pending = this.pending.subarray(index);
247
- return actions;
248
- }
249
- }
250
- exports.InputEngine = InputEngine;
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decideConfirmationAction = decideConfirmationAction;
4
- exports.routeVtInput = routeVtInput;
5
- function decideConfirmationAction(actions) {
6
- for (const action of actions) {
7
- if (action.type === "cancel") {
8
- return "deny";
9
- }
10
- if (action.type === "submit") {
11
- return "approve";
12
- }
13
- if (action.type === "insert") {
14
- const normalized = action.text.trim().toLowerCase();
15
- if (normalized === "y" || normalized === "yes") {
16
- return "approve";
17
- }
18
- if (normalized === "n" || normalized === "no") {
19
- return "deny";
20
- }
21
- }
22
- }
23
- return null;
24
- }
25
- function routeVtInput(sequence, escapePending) {
26
- const bytes = Buffer.from(sequence, "latin1");
27
- if (bytes.includes(0x11)) {
28
- return {
29
- exitToChat: true,
30
- nextEscapePending: false,
31
- passthrough: null
32
- };
33
- }
34
- if (sequence === "\u001b") {
35
- if (escapePending) {
36
- return {
37
- exitToChat: true,
38
- nextEscapePending: false,
39
- passthrough: null
40
- };
41
- }
42
- return {
43
- exitToChat: false,
44
- nextEscapePending: true,
45
- passthrough: null
46
- };
47
- }
48
- return {
49
- exitToChat: false,
50
- nextEscapePending: false,
51
- passthrough: sequence
52
- };
53
- }