@tigorhutasuhut/claude-retry 0.1.5 → 0.1.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/monitor.js +48 -32
- package/package.json +1 -1
package/dist/monitor.js
CHANGED
|
@@ -47,33 +47,29 @@ async function stepState(state, screenText, now, injectContinue, marginSeconds,
|
|
|
47
47
|
const limited = match(screenText).limited;
|
|
48
48
|
const { usage } = await resolveAccountUsage(snapshot, resolvePaneAccount, target);
|
|
49
49
|
const marginMs = (marginSeconds ?? 60) * 1000;
|
|
50
|
-
//
|
|
50
|
+
// Banner absent → claude exited / user already continued / pane id reused → nothing to continue.
|
|
51
51
|
if (!limited) {
|
|
52
52
|
state.status = 'monitoring';
|
|
53
53
|
state.waitUntil = 0;
|
|
54
54
|
logger(`${label} wait abandoned (banner gone)`);
|
|
55
55
|
return 'monitoring';
|
|
56
56
|
}
|
|
57
|
-
//
|
|
58
|
-
if (usage !== undefined && !usage.limited) {
|
|
59
|
-
state.status = 'monitoring';
|
|
60
|
-
state.waitUntil = 0;
|
|
61
|
-
logger(`${label} wait abandoned (account not limited)`);
|
|
62
|
-
return 'monitoring';
|
|
63
|
-
}
|
|
64
|
-
// 3. Account known, still limited, with a fresh resetsAtMs → refresh waitUntil
|
|
57
|
+
// Account still limited with a known reset → keep waitUntil aligned to the live reset time.
|
|
65
58
|
if (usage !== undefined && usage.limited && usage.resetsAtMs !== null) {
|
|
66
59
|
state.waitUntil = usage.resetsAtMs + marginMs;
|
|
67
60
|
}
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
// The limit is over when the account quota has cleared (early/real reset) OR the timer elapsed.
|
|
62
|
+
const accountCleared = usage !== undefined && !usage.limited;
|
|
63
|
+
const timerElapsed = now >= state.waitUntil;
|
|
64
|
+
if (accountCleared || timerElapsed) {
|
|
65
|
+
await injectContinue();
|
|
66
|
+
state.status = 'monitoring';
|
|
67
|
+
state.waitUntil = 0;
|
|
68
|
+
logger(`${label} reset reached — injected continue`);
|
|
69
|
+
return 'retried';
|
|
71
70
|
}
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
state.status = 'monitoring';
|
|
75
|
-
state.waitUntil = 0;
|
|
76
|
-
return 'retried';
|
|
71
|
+
// Still limited, before reset → keep waiting.
|
|
72
|
+
return 'rate-limited';
|
|
77
73
|
}
|
|
78
74
|
// state.status === 'monitoring'
|
|
79
75
|
const result = match(screenText);
|
|
@@ -115,8 +111,7 @@ export async function tick(paneId, state, deps, marginSeconds, fallbackHours) {
|
|
|
115
111
|
const screenText = await deps.capture(paneId);
|
|
116
112
|
return stepState(state, screenText, deps.now(), () => deps.inject(paneId, 'continue'), marginSeconds, fallbackHours);
|
|
117
113
|
}
|
|
118
|
-
async function tickTarget(target, state, deps, marginSeconds, fallbackHours, snapshot) {
|
|
119
|
-
const screenText = await deps.capture(target);
|
|
114
|
+
async function tickTarget(target, state, screenText, deps, marginSeconds, fallbackHours, snapshot) {
|
|
120
115
|
return stepState(state, screenText, deps.now(), () => deps.inject(target, 'continue'), marginSeconds, fallbackHours, snapshot, deps.resolvePaneAccount, target, deps.log);
|
|
121
116
|
}
|
|
122
117
|
export async function runMonitor(paneId, deps, pollIntervalMs, marginSeconds, fallbackHours) {
|
|
@@ -150,16 +145,6 @@ export async function multiTick(states, deps, marginSeconds, fallbackHours) {
|
|
|
150
145
|
log('scan failed: could not list sessions/panes (will retry)');
|
|
151
146
|
return;
|
|
152
147
|
}
|
|
153
|
-
// Fetch account snapshot once per pass (swallow errors → undefined).
|
|
154
|
-
let snapshot;
|
|
155
|
-
if (deps.getAccountSnapshot !== undefined) {
|
|
156
|
-
try {
|
|
157
|
-
snapshot = await deps.getAccountSnapshot();
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
snapshot = undefined;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
148
|
// Prune state for panes that no longer exist, using miss counter to tolerate
|
|
164
149
|
// transient list-panes failures.
|
|
165
150
|
const live = new Set(targets.map((t) => t.label));
|
|
@@ -179,7 +164,38 @@ export async function multiTick(states, deps, marginSeconds, fallbackHours) {
|
|
|
179
164
|
log(targets.length === 0
|
|
180
165
|
? 'scan: no Claude panes found'
|
|
181
166
|
: `scan: watching ${targets.length} Claude pane(s) [${targets.map((t) => t.label).join(', ')}]`);
|
|
167
|
+
// Capture each target's screen once, collecting successes into a map.
|
|
168
|
+
// Capture failures are logged and that pane is skipped this round.
|
|
169
|
+
const screens = new Map();
|
|
170
|
+
for (const target of targets) {
|
|
171
|
+
try {
|
|
172
|
+
screens.set(target.label, await deps.capture(target));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
log(`${target.label} — capture error (skipped this round)`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Decide whether usage API is needed this pass:
|
|
179
|
+
// - any pane already in 'waiting' state (among current targets), OR
|
|
180
|
+
// - any captured screen has a limit banner.
|
|
181
|
+
const anyWaiting = targets.some((t) => states.get(t.label)?.status === 'waiting');
|
|
182
|
+
const anyBanner = [...screens.values()].some((s) => match(s).limited);
|
|
183
|
+
const needUsage = anyWaiting || anyBanner;
|
|
184
|
+
// Fetch account snapshot only when needed (swallow errors → undefined).
|
|
185
|
+
let snapshot;
|
|
186
|
+
if (needUsage && deps.getAccountSnapshot !== undefined) {
|
|
187
|
+
try {
|
|
188
|
+
snapshot = await deps.getAccountSnapshot();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
snapshot = undefined;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
182
194
|
for (const target of targets) {
|
|
195
|
+
const screenText = screens.get(target.label);
|
|
196
|
+
// Skip panes whose capture failed this round.
|
|
197
|
+
if (screenText === undefined)
|
|
198
|
+
continue;
|
|
183
199
|
let state = states.get(target.label);
|
|
184
200
|
if (!state) {
|
|
185
201
|
state = createState();
|
|
@@ -190,12 +206,12 @@ export async function multiTick(states, deps, marginSeconds, fallbackHours) {
|
|
|
190
206
|
state.missCount = 0;
|
|
191
207
|
const before = state.status;
|
|
192
208
|
try {
|
|
193
|
-
const status = await tickTarget(target, state, deps, marginSeconds, fallbackHours, snapshot);
|
|
209
|
+
const status = await tickTarget(target, state, screenText, deps, marginSeconds, fallbackHours, snapshot);
|
|
194
210
|
logPaneStatus(log, target.label, before, state, status);
|
|
195
211
|
}
|
|
196
212
|
catch {
|
|
197
|
-
// This pane's
|
|
198
|
-
log(`${target.label} —
|
|
213
|
+
// This pane's inject failed — leave its state, keep going.
|
|
214
|
+
log(`${target.label} — inject error (skipped this round)`);
|
|
199
215
|
}
|
|
200
216
|
}
|
|
201
217
|
}
|