claude-overnight 1.16.5 → 1.16.7
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/dist/cli.js +38 -17
- package/dist/index.js +1 -1
- package/dist/ui.js +44 -16
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -144,6 +144,9 @@ export function backspaceSegments(segs) {
|
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
function stripAnsi(s) {
|
|
148
|
+
return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
149
|
+
}
|
|
147
150
|
// ── Interactive primitives ──
|
|
148
151
|
/**
|
|
149
152
|
* Read a line from the user with bracketed-paste awareness.
|
|
@@ -158,12 +161,22 @@ export function ask(question) {
|
|
|
158
161
|
}
|
|
159
162
|
return new Promise((resolve) => {
|
|
160
163
|
const segs = [];
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
const tail = question.split("\n").pop() ?? "";
|
|
165
|
+
const tailVisibleLen = stripAnsi(tail).length;
|
|
166
|
+
let prevWrapRows = 0;
|
|
167
|
+
// Only rewrite the input line (and any wrapped continuation rows). The
|
|
168
|
+
// question header above is never touched, so redraws can't stack copies
|
|
169
|
+
// even if the initial write scrolled the viewport.
|
|
163
170
|
const redraw = () => {
|
|
164
|
-
stdout.
|
|
171
|
+
const cols = stdout.columns || 80;
|
|
172
|
+
if (prevWrapRows > 0)
|
|
173
|
+
stdout.write(`\x1B[${prevWrapRows}A`);
|
|
174
|
+
stdout.write("\r\x1B[J");
|
|
175
|
+
const rendered = renderSegments(segs);
|
|
176
|
+
stdout.write(tail + rendered);
|
|
177
|
+
const visible = tailVisibleLen + stripAnsi(rendered).length;
|
|
178
|
+
prevWrapRows = visible > 0 ? Math.floor((visible - 1) / cols) : 0;
|
|
165
179
|
};
|
|
166
|
-
stdout.write("\x1B7");
|
|
167
180
|
stdout.write(question);
|
|
168
181
|
stdout.write("\x1B[?2004h");
|
|
169
182
|
try {
|
|
@@ -188,7 +201,8 @@ export function ask(question) {
|
|
|
188
201
|
redraw();
|
|
189
202
|
continue;
|
|
190
203
|
}
|
|
191
|
-
for (
|
|
204
|
+
for (let ci = 0; ci < seg.text.length; ci++) {
|
|
205
|
+
const ch = seg.text[ci];
|
|
192
206
|
if (ch === "\r" || ch === "\n") {
|
|
193
207
|
stdout.write("\n");
|
|
194
208
|
cleanup();
|
|
@@ -202,11 +216,20 @@ export function ask(question) {
|
|
|
202
216
|
}
|
|
203
217
|
if (ch === "\x7F" || ch === "\b") {
|
|
204
218
|
backspaceSegments(segs);
|
|
219
|
+
redraw();
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
// Skip ESC and any bytes that are part of an ANSI escape sequence
|
|
223
|
+
// (arrow keys, function keys, etc. arrive as \x1B [ ... letter)
|
|
224
|
+
if (ch === "\x1B") {
|
|
205
225
|
continue;
|
|
206
226
|
}
|
|
207
227
|
const code = ch.charCodeAt(0);
|
|
208
|
-
if (
|
|
209
|
-
|
|
228
|
+
if (code < 0x20)
|
|
229
|
+
continue; // control chars
|
|
230
|
+
if (code >= 0x7F && code < 0xA0)
|
|
231
|
+
continue; // DEL + C1 controls
|
|
232
|
+
appendCharToSegments(segs, ch);
|
|
210
233
|
}
|
|
211
234
|
redraw();
|
|
212
235
|
}
|
|
@@ -241,15 +264,10 @@ export async function select(label, items, defaultIdx = 0) {
|
|
|
241
264
|
};
|
|
242
265
|
const handler = (buf) => {
|
|
243
266
|
const s = buf.toString();
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
else if (s === "\x1B[B") {
|
|
249
|
-
idx = (idx + 1) % items.length;
|
|
250
|
-
draw();
|
|
251
|
-
}
|
|
252
|
-
else if (s === "\r")
|
|
267
|
+
// Ignore ANSI escape sequences (arrow keys etc.)
|
|
268
|
+
if (s[0] === "\x1B")
|
|
269
|
+
return;
|
|
270
|
+
if (s === "\r")
|
|
253
271
|
done(items[idx].value);
|
|
254
272
|
else if (s === "\x03") {
|
|
255
273
|
stdin.setRawMode(false);
|
|
@@ -277,6 +295,9 @@ export async function selectKey(label, options) {
|
|
|
277
295
|
stdin.resume();
|
|
278
296
|
const handler = (buf) => {
|
|
279
297
|
const s = buf.toString().toLowerCase();
|
|
298
|
+
// Ignore ANSI escape sequences
|
|
299
|
+
if (s[0] === "\x1B")
|
|
300
|
+
return;
|
|
280
301
|
if (s === "\x03") {
|
|
281
302
|
stdin.setRawMode(false);
|
|
282
303
|
process.exit(0);
|
|
@@ -288,7 +309,7 @@ export async function selectKey(label, options) {
|
|
|
288
309
|
resolve(keys[0]);
|
|
289
310
|
return;
|
|
290
311
|
}
|
|
291
|
-
if (keys.includes(s)) {
|
|
312
|
+
if (s.length === 1 && keys.includes(s)) {
|
|
292
313
|
stdin.setRawMode(false);
|
|
293
314
|
stdin.removeListener("data", handler);
|
|
294
315
|
stdin.pause();
|
package/dist/index.js
CHANGED
|
@@ -166,7 +166,7 @@ async function main() {
|
|
|
166
166
|
}
|
|
167
167
|
if (argv.includes("-h") || argv.includes("--help")) {
|
|
168
168
|
console.log(`
|
|
169
|
-
${chalk.bold("🌙 claude-overnight")} ${chalk.dim("—
|
|
169
|
+
${chalk.bold("🌙 claude-overnight")} ${chalk.dim("— background lane for your Claude Max plan")}
|
|
170
170
|
${chalk.dim("─".repeat(60))}
|
|
171
171
|
|
|
172
172
|
${chalk.cyan("Usage")}
|
package/dist/ui.js
CHANGED
|
@@ -282,7 +282,13 @@ export class RunDisplay {
|
|
|
282
282
|
this.inputSegs = [];
|
|
283
283
|
return true;
|
|
284
284
|
}
|
|
285
|
-
if (ch === "\
|
|
285
|
+
if (ch === "\x03") {
|
|
286
|
+
this.inputMode = "none";
|
|
287
|
+
this.inputSegs = [];
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
// ESC cancels input mode
|
|
291
|
+
if (ch === "\x1B") {
|
|
286
292
|
this.inputMode = "none";
|
|
287
293
|
this.inputSegs = [];
|
|
288
294
|
return true;
|
|
@@ -301,7 +307,8 @@ export class RunDisplay {
|
|
|
301
307
|
}
|
|
302
308
|
if (this.inputMode === "steer" || this.inputMode === "ask") {
|
|
303
309
|
let dirty = false;
|
|
304
|
-
for (
|
|
310
|
+
for (let ci = 0; ci < s.length; ci++) {
|
|
311
|
+
const ch = s[ci];
|
|
305
312
|
if (ch === "\r" || ch === "\n") {
|
|
306
313
|
const text = segmentsToString(this.inputSegs).trim();
|
|
307
314
|
const wasAsk = this.inputMode === "ask";
|
|
@@ -320,10 +327,18 @@ export class RunDisplay {
|
|
|
320
327
|
this.inputSegs = [];
|
|
321
328
|
return true;
|
|
322
329
|
}
|
|
323
|
-
//
|
|
324
|
-
if (ch === "\x1B"
|
|
330
|
+
// ESC cancels — consume this byte and any following ANSI sequence bytes
|
|
331
|
+
if (ch === "\x1B") {
|
|
325
332
|
this.inputMode = "none";
|
|
326
333
|
this.inputSegs = [];
|
|
334
|
+
// Skip any remaining ANSI sequence bytes (e.g. [A for arrow keys)
|
|
335
|
+
while (ci + 1 < s.length) {
|
|
336
|
+
const next = s[ci + 1];
|
|
337
|
+
const nc = next.charCodeAt(0);
|
|
338
|
+
ci++;
|
|
339
|
+
if ((nc >= 0x40 && nc <= 0x7E) || nc === 0x7F)
|
|
340
|
+
break; // final byte
|
|
341
|
+
}
|
|
327
342
|
return true;
|
|
328
343
|
}
|
|
329
344
|
if (ch === "\x7F" || ch === "\b") {
|
|
@@ -332,6 +347,10 @@ export class RunDisplay {
|
|
|
332
347
|
continue;
|
|
333
348
|
}
|
|
334
349
|
const code = ch.charCodeAt(0);
|
|
350
|
+
if (code < 0x20)
|
|
351
|
+
continue; // control chars
|
|
352
|
+
if (code >= 0x7F && code < 0xA0)
|
|
353
|
+
continue; // DEL + C1 controls
|
|
335
354
|
if (code >= 0x20 && code <= 0x7E && segmentsToString(this.inputSegs).length < MAX_INPUT_LEN) {
|
|
336
355
|
appendCharToSegments(this.inputSegs, ch);
|
|
337
356
|
dirty = true;
|
|
@@ -339,17 +358,26 @@ export class RunDisplay {
|
|
|
339
358
|
}
|
|
340
359
|
return dirty;
|
|
341
360
|
}
|
|
342
|
-
// Hotkey mode
|
|
343
|
-
|
|
361
|
+
// Hotkey mode — only accept single printable ASCII characters
|
|
362
|
+
// Skip ESC and ANSI sequences entirely
|
|
363
|
+
if (s.length > 1 && (s[0] === "\x1B" || s.charCodeAt(0) < 0x20))
|
|
364
|
+
return false;
|
|
365
|
+
if (s.length !== 1)
|
|
366
|
+
return false;
|
|
367
|
+
const key = s[0];
|
|
368
|
+
const code = key.charCodeAt(0);
|
|
369
|
+
if (code < 0x20 || code > 0x7E)
|
|
370
|
+
return false;
|
|
371
|
+
if (key === "\x1B" && this.askState && !this.askState.streaming) {
|
|
344
372
|
this.askState = undefined;
|
|
345
373
|
return false;
|
|
346
374
|
}
|
|
347
|
-
if (
|
|
375
|
+
if (key === "b" || key === "B") {
|
|
348
376
|
this.inputMode = "budget";
|
|
349
377
|
this.inputSegs = [];
|
|
350
378
|
return true;
|
|
351
379
|
}
|
|
352
|
-
if (
|
|
380
|
+
if (key === "t" || key === "T") {
|
|
353
381
|
if (this.swarm) {
|
|
354
382
|
this.inputMode = "threshold";
|
|
355
383
|
this.inputSegs = [];
|
|
@@ -357,7 +385,7 @@ export class RunDisplay {
|
|
|
357
385
|
}
|
|
358
386
|
return false;
|
|
359
387
|
}
|
|
360
|
-
if (
|
|
388
|
+
if (key === "c" || key === "C") {
|
|
361
389
|
if (this.swarm) {
|
|
362
390
|
this.inputMode = "concurrency";
|
|
363
391
|
this.inputSegs = [];
|
|
@@ -365,7 +393,7 @@ export class RunDisplay {
|
|
|
365
393
|
}
|
|
366
394
|
return false;
|
|
367
395
|
}
|
|
368
|
-
if (
|
|
396
|
+
if (key === "e" || key === "E") {
|
|
369
397
|
if (this.swarm) {
|
|
370
398
|
this.inputMode = "extra";
|
|
371
399
|
this.inputSegs = [];
|
|
@@ -373,7 +401,7 @@ export class RunDisplay {
|
|
|
373
401
|
}
|
|
374
402
|
return false;
|
|
375
403
|
}
|
|
376
|
-
if (
|
|
404
|
+
if (key === "p" || key === "P") {
|
|
377
405
|
if (this.swarm) {
|
|
378
406
|
const next = !this.swarm.paused;
|
|
379
407
|
this.swarm.setPaused(next);
|
|
@@ -383,20 +411,20 @@ export class RunDisplay {
|
|
|
383
411
|
}
|
|
384
412
|
return false;
|
|
385
413
|
}
|
|
386
|
-
if ((
|
|
414
|
+
if ((key === "f" || key === "F") && this.swarm && this.swarm.failed > 0 && this.swarm.active > 0) {
|
|
387
415
|
this.swarm.requeueFailed();
|
|
388
416
|
return false;
|
|
389
417
|
}
|
|
390
|
-
if ((
|
|
418
|
+
if ((key === "r" || key === "R") && this.swarm && this.swarm.rateLimitPaused > 0) {
|
|
391
419
|
this.swarm.retryRateLimitNow();
|
|
392
420
|
return true;
|
|
393
421
|
}
|
|
394
|
-
if ((
|
|
422
|
+
if ((key === "s" || key === "S") && this.onSteer) {
|
|
395
423
|
this.inputMode = "steer";
|
|
396
424
|
this.inputSegs = [];
|
|
397
425
|
return true;
|
|
398
426
|
}
|
|
399
|
-
if (
|
|
427
|
+
if (key === "?" && this.onAsk && this.swarm && !this.askBusy) {
|
|
400
428
|
if (this.askState && !this.askState.streaming) {
|
|
401
429
|
this.askState = undefined;
|
|
402
430
|
return false;
|
|
@@ -405,7 +433,7 @@ export class RunDisplay {
|
|
|
405
433
|
this.inputSegs = [];
|
|
406
434
|
return true;
|
|
407
435
|
}
|
|
408
|
-
if (
|
|
436
|
+
if (key === "q" || key === "Q" || key === "\x03") {
|
|
409
437
|
if (this.swarm) {
|
|
410
438
|
if (this.swarm.aborted)
|
|
411
439
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.7",
|
|
4
4
|
"description": "Background lane for your Claude Max plan. Parallel Claude Agent SDK sessions in git worktrees with a usage cap that reserves headroom for your interactive Claude Code. Crash-safe resume. Opus/Sonnet/Haiku + Qwen/OpenRouter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|