claude-overnight 1.13.1 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +128 -0
- package/dist/render.js +1 -1
- package/dist/run.js +5 -1
- package/dist/swarm.d.ts +3 -1
- package/dist/swarm.js +12 -0
- package/dist/ui.d.ts +2 -0
- package/dist/ui.js +18 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,6 +21,133 @@ function countTasksInFile(path) {
|
|
|
21
21
|
return 0;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
+
async function promptResumeOverrides(state, cliFlags, argv, noTTY, runDir) {
|
|
25
|
+
// ── Apply CLI flag overrides first ──
|
|
26
|
+
if (cliFlags.model)
|
|
27
|
+
state.workerModel = cliFlags.model;
|
|
28
|
+
if (cliFlags.concurrency) {
|
|
29
|
+
const n = parseInt(cliFlags.concurrency);
|
|
30
|
+
if (n >= 1)
|
|
31
|
+
state.concurrency = n;
|
|
32
|
+
}
|
|
33
|
+
if (cliFlags.budget) {
|
|
34
|
+
const n = parseInt(cliFlags.budget);
|
|
35
|
+
if (n > 0) {
|
|
36
|
+
state.remaining = n;
|
|
37
|
+
state.budget = state.accCompleted + state.accFailed + n;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (cliFlags["usage-cap"] != null) {
|
|
41
|
+
const v = parseFloat(cliFlags["usage-cap"]);
|
|
42
|
+
if (!isNaN(v) && v >= 0 && v <= 100)
|
|
43
|
+
state.usageCap = v > 0 ? v / 100 : undefined;
|
|
44
|
+
}
|
|
45
|
+
if (cliFlags["extra-usage-budget"] != null) {
|
|
46
|
+
const v = parseFloat(cliFlags["extra-usage-budget"]);
|
|
47
|
+
if (!isNaN(v) && v > 0) {
|
|
48
|
+
state.extraUsageBudget = v;
|
|
49
|
+
state.allowExtraUsage = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (argv.includes("--allow-extra-usage"))
|
|
53
|
+
state.allowExtraUsage = true;
|
|
54
|
+
if (cliFlags.perm)
|
|
55
|
+
state.permissionMode = cliFlags.perm;
|
|
56
|
+
if (noTTY) {
|
|
57
|
+
try {
|
|
58
|
+
saveRunState(runDir, state);
|
|
59
|
+
}
|
|
60
|
+
catch { }
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// ── Interactive review ──
|
|
64
|
+
const fmtSummary = () => {
|
|
65
|
+
const tier = detectModelTier(state.workerModel);
|
|
66
|
+
const remaining = Math.max(1, state.remaining);
|
|
67
|
+
const capStr = state.usageCap != null ? `${Math.round(state.usageCap * 100)}%` : "unlimited";
|
|
68
|
+
const extraStr = state.allowExtraUsage
|
|
69
|
+
? (state.extraUsageBudget ? `$${state.extraUsageBudget}` : "unlimited")
|
|
70
|
+
: "off";
|
|
71
|
+
console.log();
|
|
72
|
+
console.log(` ${chalk.dim("Resume settings")}`);
|
|
73
|
+
console.log(` ${chalk.dim("─".repeat(40))}`);
|
|
74
|
+
console.log(` ${chalk.dim("model ")}${chalk.white(state.workerModel)} ${chalk.dim(`(${tier})`)}`);
|
|
75
|
+
console.log(` ${chalk.dim("remaining ")}${chalk.white(String(remaining))} ${chalk.dim("sessions")}`);
|
|
76
|
+
console.log(` ${chalk.dim("concur ")}${chalk.white(String(state.concurrency))}`);
|
|
77
|
+
console.log(` ${chalk.dim("usage cap ")}${chalk.white(capStr)}`);
|
|
78
|
+
console.log(` ${chalk.dim("extra ")}${chalk.white(extraStr)}`);
|
|
79
|
+
};
|
|
80
|
+
fmtSummary();
|
|
81
|
+
const action = await selectKey("", [
|
|
82
|
+
{ key: "r", desc: "esume" },
|
|
83
|
+
{ key: "e", desc: "dit" },
|
|
84
|
+
{ key: "q", desc: "uit" },
|
|
85
|
+
]);
|
|
86
|
+
if (action === "q")
|
|
87
|
+
process.exit(0);
|
|
88
|
+
if (action === "r")
|
|
89
|
+
return;
|
|
90
|
+
// ── Edit walk ──
|
|
91
|
+
const models = await fetchModels(5_000);
|
|
92
|
+
if (models.length > 0) {
|
|
93
|
+
const currentIdx = Math.max(0, models.findIndex(m => m.value === state.workerModel));
|
|
94
|
+
state.workerModel = await select(`${chalk.cyan("①")} Worker model:`, models.map(m => ({ name: m.displayName, value: m.value, hint: m.description })), currentIdx);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const ans = await ask(`\n ${chalk.cyan("①")} Worker model ${chalk.dim(`[${state.workerModel}]:`)} `);
|
|
98
|
+
if (ans.trim())
|
|
99
|
+
state.workerModel = ans.trim();
|
|
100
|
+
}
|
|
101
|
+
const remAns = await ask(`\n ${chalk.cyan("②")} Remaining sessions ${chalk.dim(`[${state.remaining}]:`)} `);
|
|
102
|
+
const parsedRem = parseInt(remAns);
|
|
103
|
+
if (!isNaN(parsedRem) && parsedRem > 0) {
|
|
104
|
+
state.remaining = parsedRem;
|
|
105
|
+
state.budget = state.accCompleted + state.accFailed + parsedRem;
|
|
106
|
+
}
|
|
107
|
+
const concAns = await ask(`\n ${chalk.cyan("③")} Concurrency ${chalk.dim(`[${state.concurrency}]:`)} `);
|
|
108
|
+
const parsedConc = parseInt(concAns);
|
|
109
|
+
if (!isNaN(parsedConc) && parsedConc >= 1)
|
|
110
|
+
state.concurrency = parsedConc;
|
|
111
|
+
const currentCap = state.usageCap != null ? String(Math.round(state.usageCap * 100)) : "off";
|
|
112
|
+
const capAns = await ask(`\n ${chalk.cyan("④")} Usage cap % ${chalk.dim(`[${currentCap}]`)} ${chalk.dim("(0 = off):")} `);
|
|
113
|
+
if (capAns.trim()) {
|
|
114
|
+
const v = parseFloat(capAns);
|
|
115
|
+
if (!isNaN(v) && v >= 0 && v <= 100)
|
|
116
|
+
state.usageCap = v > 0 ? v / 100 : undefined;
|
|
117
|
+
}
|
|
118
|
+
const currentExtra = state.allowExtraUsage
|
|
119
|
+
? (state.extraUsageBudget ? `$${state.extraUsageBudget}` : "unlimited")
|
|
120
|
+
: "off";
|
|
121
|
+
const extraChoice = await select(`${chalk.cyan("⑤")} Extra usage ${chalk.dim(`[current: ${currentExtra}]`)}:`, [
|
|
122
|
+
{ name: "Keep current", value: "keep" },
|
|
123
|
+
{ name: "Off", value: "off", hint: "stop at plan limit" },
|
|
124
|
+
{ name: "With $ cap", value: "budget", hint: "set a spending cap" },
|
|
125
|
+
{ name: "Unlimited", value: "unlimited", hint: "no cap, billed as overage" },
|
|
126
|
+
]);
|
|
127
|
+
if (extraChoice === "off") {
|
|
128
|
+
state.allowExtraUsage = false;
|
|
129
|
+
state.extraUsageBudget = undefined;
|
|
130
|
+
}
|
|
131
|
+
else if (extraChoice === "budget") {
|
|
132
|
+
const bAns = await ask(` ${chalk.dim("Max extra $:")} `);
|
|
133
|
+
const bVal = parseFloat(bAns);
|
|
134
|
+
if (!isNaN(bVal) && bVal > 0) {
|
|
135
|
+
state.extraUsageBudget = bVal;
|
|
136
|
+
state.allowExtraUsage = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (extraChoice === "unlimited") {
|
|
140
|
+
state.allowExtraUsage = true;
|
|
141
|
+
state.extraUsageBudget = undefined;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
saveRunState(runDir, state);
|
|
145
|
+
}
|
|
146
|
+
catch { }
|
|
147
|
+
console.log(chalk.green("\n ✓ Settings updated"));
|
|
148
|
+
fmtSummary();
|
|
149
|
+
console.log();
|
|
150
|
+
}
|
|
24
151
|
async function main() {
|
|
25
152
|
const argv = process.argv.slice(2);
|
|
26
153
|
if (argv.includes("-v") || argv.includes("--version")) {
|
|
@@ -310,6 +437,7 @@ async function main() {
|
|
|
310
437
|
}
|
|
311
438
|
catch { }
|
|
312
439
|
}
|
|
440
|
+
await promptResumeOverrides(resumeState, cliFlags, argv, noTTY, resumeRunDir);
|
|
313
441
|
}
|
|
314
442
|
}
|
|
315
443
|
// ── Config resolution ──
|
package/dist/render.js
CHANGED
|
@@ -196,7 +196,7 @@ export function renderFrame(swarm, showHotkeys, runInfo) {
|
|
|
196
196
|
const chip = pending > 0 ? chalk.cyan(` \u270E ${pending} steer queued`) : "";
|
|
197
197
|
const fixChip = swarm.failed > 0 && swarm.active > 0 ? chalk.yellow(" [f] fix") : "";
|
|
198
198
|
const pauseLabel = swarm.paused ? "[p] resume" : "[p] pause";
|
|
199
|
-
out.push(chalk.dim(` [b] budget [t]
|
|
199
|
+
out.push(chalk.dim(` [b] budget [t] cap [c] conc [e] extra ${pauseLabel} [s] steer [?] ask [q] stop`) + fixChip + chip);
|
|
200
200
|
if (swarm.blocked > 0 && swarm.blocked === swarm.active) {
|
|
201
201
|
out.push(chalk.yellow(` all workers rate-limited — press [c] to reduce concurrency, [p] to pause, [q] to quit`));
|
|
202
202
|
}
|
package/dist/run.js
CHANGED
|
@@ -25,7 +25,10 @@ export async function executeRun(cfg) {
|
|
|
25
25
|
let currentSwarm;
|
|
26
26
|
let remaining;
|
|
27
27
|
let currentTasks;
|
|
28
|
-
const liveConfig = {
|
|
28
|
+
const liveConfig = {
|
|
29
|
+
remaining: 0, usageCap, concurrency, paused: false, dirty: false,
|
|
30
|
+
extraUsageBudget: cfg.extraUsageBudget,
|
|
31
|
+
};
|
|
29
32
|
let waveNum;
|
|
30
33
|
const waveHistory = [];
|
|
31
34
|
let accCost, accCompleted, accFailed, accTools;
|
|
@@ -341,6 +344,7 @@ export async function executeRun(cfg) {
|
|
|
341
344
|
if (liveConfig.dirty) {
|
|
342
345
|
remaining = liveConfig.remaining;
|
|
343
346
|
usageCap = liveConfig.usageCap;
|
|
347
|
+
cfg.extraUsageBudget = liveConfig.extraUsageBudget;
|
|
344
348
|
liveConfig.dirty = false;
|
|
345
349
|
}
|
|
346
350
|
liveConfig.remaining = remaining;
|
package/dist/swarm.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export declare class Swarm {
|
|
|
65
65
|
readonly model: string | undefined;
|
|
66
66
|
usageCap: number | undefined;
|
|
67
67
|
readonly allowExtraUsage: boolean;
|
|
68
|
-
|
|
68
|
+
extraUsageBudget: number | undefined;
|
|
69
69
|
readonly baseCostUsd: number;
|
|
70
70
|
mergeBranch?: string;
|
|
71
71
|
constructor(config: SwarmConfig);
|
|
@@ -76,6 +76,8 @@ export declare class Swarm {
|
|
|
76
76
|
setConcurrency(n: number): void;
|
|
77
77
|
/** Freeze/resume dispatch without killing the run. Paused workers block at the top of their loop. */
|
|
78
78
|
setPaused(b: boolean): void;
|
|
79
|
+
/** Live-adjust the overage spend cap. `undefined` = unlimited. If already over the new cap, stop dispatch. */
|
|
80
|
+
setExtraUsageBudget(n: number | undefined): void;
|
|
79
81
|
run(): Promise<void>;
|
|
80
82
|
abort(): void;
|
|
81
83
|
/** Re-queue all errored agents' tasks for retry within this wave. */
|
package/dist/swarm.js
CHANGED
|
@@ -109,6 +109,18 @@ export class Swarm {
|
|
|
109
109
|
this.paused = b;
|
|
110
110
|
this.log(-1, b ? "Dispatch paused" : "Dispatch resumed");
|
|
111
111
|
}
|
|
112
|
+
/** Live-adjust the overage spend cap. `undefined` = unlimited. If already over the new cap, stop dispatch. */
|
|
113
|
+
setExtraUsageBudget(n) {
|
|
114
|
+
if (this.extraUsageBudget === n)
|
|
115
|
+
return;
|
|
116
|
+
const prev = this.extraUsageBudget;
|
|
117
|
+
this.extraUsageBudget = n;
|
|
118
|
+
const fmt = (v) => v != null ? `$${v}` : "unlimited";
|
|
119
|
+
this.log(-1, `Extra usage budget: ${fmt(prev)} → ${fmt(n)}`);
|
|
120
|
+
if (n != null && this.isUsingOverage && this.overageCostUsd >= n) {
|
|
121
|
+
this.capForOverage(`Extra usage budget $${n} exceeded ($${this.overageCostUsd.toFixed(2)} spent) — stopping dispatch`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
112
124
|
async run() {
|
|
113
125
|
try {
|
|
114
126
|
if (this.config.useWorktrees) {
|
package/dist/ui.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface LiveConfig {
|
|
|
33
33
|
concurrency: number;
|
|
34
34
|
paused: boolean;
|
|
35
35
|
dirty: boolean;
|
|
36
|
+
/** Overage spend cap ($) — undefined = unlimited. Synced from the [e] hotkey. */
|
|
37
|
+
extraUsageBudget?: number;
|
|
36
38
|
}
|
|
37
39
|
/** State of an in-flight or recently-completed ask side query. */
|
|
38
40
|
export interface AskState {
|
package/dist/ui.js
CHANGED
|
@@ -164,6 +164,9 @@ export class RunDisplay {
|
|
|
164
164
|
if (this.inputMode === "concurrency") {
|
|
165
165
|
return `\n ${chalk.cyan(">")} New concurrency (min 1): ${rendered}\u2588`;
|
|
166
166
|
}
|
|
167
|
+
if (this.inputMode === "extra") {
|
|
168
|
+
return `\n ${chalk.cyan(">")} Extra usage $ cap (0 = stop on overage): ${rendered}\u2588`;
|
|
169
|
+
}
|
|
167
170
|
if (this.inputMode === "steer") {
|
|
168
171
|
return `\n ${chalk.cyan(">")} ${chalk.bold("Steer next wave")} ${chalk.dim("(Enter to queue, Esc to cancel)")}\n ${rendered}\u2588`;
|
|
169
172
|
}
|
|
@@ -229,7 +232,7 @@ export class RunDisplay {
|
|
|
229
232
|
}
|
|
230
233
|
/** Handle a pasted block. Returns true if the frame needs a redraw. */
|
|
231
234
|
handlePaste(text) {
|
|
232
|
-
if (this.inputMode === "budget" || this.inputMode === "threshold" || this.inputMode === "concurrency") {
|
|
235
|
+
if (this.inputMode === "budget" || this.inputMode === "threshold" || this.inputMode === "concurrency" || this.inputMode === "extra") {
|
|
233
236
|
const clean = text.replace(/[^0-9.]/g, "");
|
|
234
237
|
if (clean)
|
|
235
238
|
appendCharToSegments(this.inputSegs, clean);
|
|
@@ -246,7 +249,7 @@ export class RunDisplay {
|
|
|
246
249
|
/** Handle a typed (non-pasted) chunk. Returns true if the frame needs a redraw. */
|
|
247
250
|
handleTyped(s) {
|
|
248
251
|
const lc = this.liveConfig;
|
|
249
|
-
if (this.inputMode === "budget" || this.inputMode === "threshold" || this.inputMode === "concurrency") {
|
|
252
|
+
if (this.inputMode === "budget" || this.inputMode === "threshold" || this.inputMode === "concurrency" || this.inputMode === "extra") {
|
|
250
253
|
let dirty = false;
|
|
251
254
|
for (const ch of s) {
|
|
252
255
|
if (ch === "\r" || ch === "\n") {
|
|
@@ -270,6 +273,11 @@ export class RunDisplay {
|
|
|
270
273
|
lc.dirty = true;
|
|
271
274
|
this.swarm?.setConcurrency(n);
|
|
272
275
|
}
|
|
276
|
+
else if (this.inputMode === "extra" && !isNaN(val) && val >= 0) {
|
|
277
|
+
lc.extraUsageBudget = val;
|
|
278
|
+
lc.dirty = true;
|
|
279
|
+
this.swarm?.setExtraUsageBudget(val);
|
|
280
|
+
}
|
|
273
281
|
this.inputMode = "none";
|
|
274
282
|
this.inputSegs = [];
|
|
275
283
|
return true;
|
|
@@ -357,6 +365,14 @@ export class RunDisplay {
|
|
|
357
365
|
}
|
|
358
366
|
return false;
|
|
359
367
|
}
|
|
368
|
+
if (s === "e" || s === "E") {
|
|
369
|
+
if (this.swarm) {
|
|
370
|
+
this.inputMode = "extra";
|
|
371
|
+
this.inputSegs = [];
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
360
376
|
if (s === "p" || s === "P") {
|
|
361
377
|
if (this.swarm) {
|
|
362
378
|
const next = !this.swarm.paused;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Run 10, 100, or 1000 Claude agents overnight. Parallel autonomous AI coding with thinking waves, iterative quality steering, crash recovery, and rate limit handling. Built on the Claude Agent SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|