@autoclawd/autoclawd 1.1.14 → 1.1.16
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 +73 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1083,7 +1083,10 @@ var RATE_LIMIT_PATTERNS = [
|
|
|
1083
1083
|
/\b429\b.*(?:error|too many|rate|limit|retry)/i,
|
|
1084
1084
|
/(?:error|status|code|http)[:\s]*429\b/i,
|
|
1085
1085
|
/(?:api|server|model)\s+(?:is\s+)?overloaded/i,
|
|
1086
|
-
/over\s*capacity/i
|
|
1086
|
+
/over\s*capacity/i,
|
|
1087
|
+
// Claude Code subscription limit: "You've hit your limit · resets 9am (UTC)"
|
|
1088
|
+
/hit your (?:usage )?limit.*resets?\s+\d+/i,
|
|
1089
|
+
/you've\s+(?:reached|hit).*limit/i
|
|
1087
1090
|
];
|
|
1088
1091
|
var COMPLETION_PATTERNS = [
|
|
1089
1092
|
/\[autoclawd:done\]/i,
|
|
@@ -1096,8 +1099,30 @@ function isCompleted(output) {
|
|
|
1096
1099
|
return COMPLETION_PATTERNS.some((p) => p.test(output));
|
|
1097
1100
|
}
|
|
1098
1101
|
function estimateResetMs(output) {
|
|
1099
|
-
const
|
|
1100
|
-
if (
|
|
1102
|
+
const clockMatch = output.match(/resets?\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)\s*\(?utc\)?/i);
|
|
1103
|
+
if (clockMatch) {
|
|
1104
|
+
let targetHour = parseInt(clockMatch[1]);
|
|
1105
|
+
const targetMin = clockMatch[2] ? parseInt(clockMatch[2]) : 0;
|
|
1106
|
+
const ampm = clockMatch[3].toLowerCase();
|
|
1107
|
+
if (ampm === "pm" && targetHour !== 12) targetHour += 12;
|
|
1108
|
+
if (ampm === "am" && targetHour === 12) targetHour = 0;
|
|
1109
|
+
const now = /* @__PURE__ */ new Date();
|
|
1110
|
+
const target = new Date(Date.UTC(
|
|
1111
|
+
now.getUTCFullYear(),
|
|
1112
|
+
now.getUTCMonth(),
|
|
1113
|
+
now.getUTCDate(),
|
|
1114
|
+
targetHour,
|
|
1115
|
+
targetMin,
|
|
1116
|
+
0,
|
|
1117
|
+
0
|
|
1118
|
+
));
|
|
1119
|
+
if (target.getTime() <= now.getTime()) {
|
|
1120
|
+
target.setUTCDate(target.getUTCDate() + 1);
|
|
1121
|
+
}
|
|
1122
|
+
return target.getTime() - now.getTime() + 6e4;
|
|
1123
|
+
}
|
|
1124
|
+
const retryAfter = output.match(/retry.?after[:\s]*(\d+)/i);
|
|
1125
|
+
if (retryAfter) return parseInt(retryAfter[1]) * 1e3;
|
|
1101
1126
|
return 6e4;
|
|
1102
1127
|
}
|
|
1103
1128
|
var MAX_RATE_LIMIT_RETRIES = 10;
|
|
@@ -1158,12 +1183,17 @@ async function runAgentLoop(opts) {
|
|
|
1158
1183
|
return { iterations: i, success: true, lastOutput };
|
|
1159
1184
|
}
|
|
1160
1185
|
if (rateLimited) {
|
|
1186
|
+
const waitMs = estimateResetMs(combined);
|
|
1187
|
+
if (waitMs > 5 * 60 * 1e3) {
|
|
1188
|
+
const pauseUntil = Date.now() + waitMs;
|
|
1189
|
+
log.ticket(ticketId, `Rate limited with long reset (${Math.round(waitMs / 1e3)}s) \u2014 pausing watcher globally`);
|
|
1190
|
+
return { iterations: i, success: false, lastOutput, pauseUntil };
|
|
1191
|
+
}
|
|
1161
1192
|
rateLimitRetries++;
|
|
1162
1193
|
if (rateLimitRetries > MAX_RATE_LIMIT_RETRIES) {
|
|
1163
1194
|
log.ticket(ticketId, `Rate limited ${rateLimitRetries} times, giving up`);
|
|
1164
1195
|
return { iterations: i, success: false, lastOutput };
|
|
1165
1196
|
}
|
|
1166
|
-
const waitMs = estimateResetMs(combined);
|
|
1167
1197
|
log.ticket(ticketId, `Rate limited (${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES}) \u2014 pausing ${Math.round(waitMs / 1e3)}s`);
|
|
1168
1198
|
await sleep2(waitMs);
|
|
1169
1199
|
i--;
|
|
@@ -1706,6 +1736,14 @@ async function executeTicket(opts) {
|
|
|
1706
1736
|
prompt: agentPrompt,
|
|
1707
1737
|
ticketId: ticket.identifier
|
|
1708
1738
|
});
|
|
1739
|
+
if (agentResult.pauseUntil) {
|
|
1740
|
+
return {
|
|
1741
|
+
ticketId: ticket.identifier,
|
|
1742
|
+
success: false,
|
|
1743
|
+
error: `REQUEUE_PAUSE:${agentResult.pauseUntil}`,
|
|
1744
|
+
iterations: agentResult.iterations
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1709
1747
|
const gitResult = await commitAndPush(container, {
|
|
1710
1748
|
branchName,
|
|
1711
1749
|
ticketId: ticket.identifier,
|
|
@@ -1984,6 +2022,7 @@ var WebhookServer = class {
|
|
|
1984
2022
|
server;
|
|
1985
2023
|
// Track in-memory active set for concurrency counting (fast path)
|
|
1986
2024
|
activeInMemory = /* @__PURE__ */ new Set();
|
|
2025
|
+
pausedUntil = 0;
|
|
1987
2026
|
start() {
|
|
1988
2027
|
getDb();
|
|
1989
2028
|
this.resumeQueue();
|
|
@@ -2140,7 +2179,15 @@ var WebhookServer = class {
|
|
|
2140
2179
|
octokit: this.octokit
|
|
2141
2180
|
}).then(
|
|
2142
2181
|
(result) => {
|
|
2143
|
-
if (result.error?.startsWith("
|
|
2182
|
+
if (result.error?.startsWith("REQUEUE_PAUSE:")) {
|
|
2183
|
+
const until = parseInt(result.error.slice("REQUEUE_PAUSE:".length), 10);
|
|
2184
|
+
this.pausedUntil = Math.max(this.pausedUntil, until);
|
|
2185
|
+
const waitSec = Math.round((until - Date.now()) / 1e3);
|
|
2186
|
+
log.ticket(ticket.identifier, `Rate limit hit \u2014 pausing webhook dispatch for ${waitSec}s`);
|
|
2187
|
+
finishRun(ticket.id, "failed", result.error);
|
|
2188
|
+
removeFromProcessed(ticket.id);
|
|
2189
|
+
enqueue(ticket);
|
|
2190
|
+
} else if (result.error?.startsWith("REQUEUE:")) {
|
|
2144
2191
|
log.ticket(ticket.identifier, "Re-queued (waiting for dependency branch)");
|
|
2145
2192
|
finishRun(ticket.id, "failed", result.error);
|
|
2146
2193
|
removeFromProcessed(ticket.id);
|
|
@@ -2160,6 +2207,7 @@ var WebhookServer = class {
|
|
|
2160
2207
|
});
|
|
2161
2208
|
}
|
|
2162
2209
|
drainQueue() {
|
|
2210
|
+
if (this.pausedUntil > Date.now()) return;
|
|
2163
2211
|
while (this.activeInMemory.size < this.config.maxConcurrent) {
|
|
2164
2212
|
const next = dequeue();
|
|
2165
2213
|
if (!next) break;
|
|
@@ -2233,6 +2281,7 @@ var Watcher = class {
|
|
|
2233
2281
|
stopped = false;
|
|
2234
2282
|
pollCount = 0;
|
|
2235
2283
|
rebaseRunning = false;
|
|
2284
|
+
pausedUntil = 0;
|
|
2236
2285
|
async start(intervalSeconds, once) {
|
|
2237
2286
|
getDb();
|
|
2238
2287
|
this.resumeQueue();
|
|
@@ -2278,6 +2327,15 @@ var Watcher = class {
|
|
|
2278
2327
|
}
|
|
2279
2328
|
}
|
|
2280
2329
|
async poll() {
|
|
2330
|
+
if (this.pausedUntil > Date.now()) {
|
|
2331
|
+
const remainingSec = Math.round((this.pausedUntil - Date.now()) / 1e3);
|
|
2332
|
+
log.info(`Paused (rate limit) \u2014 ${remainingSec}s remaining until retry`);
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
if (this.pausedUntil > 0) {
|
|
2336
|
+
log.info("Rate limit pause ended, resuming polls");
|
|
2337
|
+
this.pausedUntil = 0;
|
|
2338
|
+
}
|
|
2281
2339
|
this.recentlyRequeued.clear();
|
|
2282
2340
|
try {
|
|
2283
2341
|
const tickets = await pollTickets(this.linearClient, this.config);
|
|
@@ -2351,7 +2409,15 @@ var Watcher = class {
|
|
|
2351
2409
|
octokit: this.octokit
|
|
2352
2410
|
}).then(
|
|
2353
2411
|
(result) => {
|
|
2354
|
-
if (result.error?.startsWith("
|
|
2412
|
+
if (result.error?.startsWith("REQUEUE_PAUSE:")) {
|
|
2413
|
+
const until = parseInt(result.error.slice("REQUEUE_PAUSE:".length), 10);
|
|
2414
|
+
this.pausedUntil = Math.max(this.pausedUntil, until);
|
|
2415
|
+
const waitSec = Math.round((until - Date.now()) / 1e3);
|
|
2416
|
+
log.ticket(ticket.identifier, `Rate limit hit \u2014 pausing watcher for ${waitSec}s`);
|
|
2417
|
+
finishRun(ticket.id, "failed", result.error);
|
|
2418
|
+
removeFromProcessed(ticket.id);
|
|
2419
|
+
enqueue(ticket);
|
|
2420
|
+
} else if (result.error?.startsWith("REQUEUE:")) {
|
|
2355
2421
|
log.ticket(ticket.identifier, "Re-queued (waiting for dependency branch)");
|
|
2356
2422
|
finishRun(ticket.id, "failed", result.error);
|
|
2357
2423
|
removeFromProcessed(ticket.id);
|
|
@@ -2372,6 +2438,7 @@ var Watcher = class {
|
|
|
2372
2438
|
});
|
|
2373
2439
|
}
|
|
2374
2440
|
drainQueue() {
|
|
2441
|
+
if (this.pausedUntil > Date.now()) return;
|
|
2375
2442
|
const queued = [];
|
|
2376
2443
|
let next;
|
|
2377
2444
|
while (next = dequeue()) {
|