fix: improve foreground process detection for macOS compatibility
Expand default foreground command list beyond node/claude to include npx, tsx, bun, deno. Log actual detected process name for debugging. Add configurable foregroundCommands in ~/.claude-auto-retry.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-auto-retry",
|
"name": "claude-auto-retry",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"description": "Automatically retry Claude Code sessions when hitting Anthropic subscription rate limits",
|
"description": "Automatically retry Claude Code sessions when hitting Anthropic subscription rate limits",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ function validate(cfg) {
|
|||||||
if (!Array.isArray(cfg.customPatterns)) {
|
if (!Array.isArray(cfg.customPatterns)) {
|
||||||
cfg.customPatterns = DEFAULT_CONFIG.customPatterns;
|
cfg.customPatterns = DEFAULT_CONFIG.customPatterns;
|
||||||
}
|
}
|
||||||
|
if (cfg.foregroundCommands !== undefined) {
|
||||||
|
if (!Array.isArray(cfg.foregroundCommands) || cfg.foregroundCommands.length === 0) {
|
||||||
|
delete cfg.foregroundCommands;
|
||||||
|
}
|
||||||
|
}
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -4,7 +4,7 @@ import { capturePane, sendKeys, getPaneCommand } from './tmux.js';
|
|||||||
import { loadConfig } from './config.js';
|
import { loadConfig } from './config.js';
|
||||||
import { createLogger } from './logger.js';
|
import { createLogger } from './logger.js';
|
||||||
|
|
||||||
const CLAUDE_COMMANDS = ['node', 'claude'];
|
const DEFAULT_FOREGROUND_COMMANDS = ['node', 'claude', 'npx', 'tsx', 'bun', 'deno'];
|
||||||
|
|
||||||
export function createMonitorState() {
|
export function createMonitorState() {
|
||||||
return { status: 'monitoring', waitUntil: 0, attempts: 0, lastRateLimitMessage: null };
|
return { status: 'monitoring', waitUntil: 0, attempts: 0, lastRateLimitMessage: null };
|
||||||
@@ -35,9 +35,11 @@ export async function processOneTick(state, tmuxAdapter, pane, config, isAlive)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fg = await tmuxAdapter.getPaneCommand(pane);
|
const fg = await tmuxAdapter.getPaneCommand(pane);
|
||||||
if (!CLAUDE_COMMANDS.some(c => fg.toLowerCase().includes(c))) {
|
const fgCommands = config.foregroundCommands || DEFAULT_FOREGROUND_COMMANDS;
|
||||||
|
if (!fgCommands.some(c => fg.toLowerCase().includes(c))) {
|
||||||
// Push waitUntil forward to avoid tight-loop polling every tick
|
// Push waitUntil forward to avoid tight-loop polling every tick
|
||||||
state.waitUntil = Date.now() + (config.pollIntervalSeconds * 1000 * 6);
|
state.waitUntil = Date.now() + (config.pollIntervalSeconds * 1000 * 6);
|
||||||
|
state._lastForeground = fg;
|
||||||
return 'skipped-not-claude';
|
return 'skipped-not-claude';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +91,7 @@ export async function startMonitor(pane, pid) {
|
|||||||
if (result === 'retried') await logger.info(`Sent retry message (attempt ${state.attempts})`);
|
if (result === 'retried') await logger.info(`Sent retry message (attempt ${state.attempts})`);
|
||||||
if (result === 'user-continued') await logger.info('User already continued. Attempt counter reset.');
|
if (result === 'user-continued') await logger.info('User already continued. Attempt counter reset.');
|
||||||
if (result === 'max-retries') await logger.warn(`Max retries (${config.maxRetries}) reached. Monitor still active but will not send further retries until rate limit clears.`);
|
if (result === 'max-retries') await logger.warn(`Max retries (${config.maxRetries}) reached. Monitor still active but will not send further retries until rate limit clears.`);
|
||||||
if (result === 'skipped-not-claude') await logger.warn('Foreground is not Claude. Skipping send-keys.');
|
if (result === 'skipped-not-claude') await logger.warn(`Foreground is "${state._lastForeground}", not Claude. Skipping send-keys. (Add to foregroundCommands in ~/.claude-auto-retry.json if this is wrong)`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
consecutiveErrors++;
|
consecutiveErrors++;
|
||||||
await logger.error(`Monitor tick error: ${err.message}`).catch(() => {});
|
await logger.error(`Monitor tick error: ${err.message}`).catch(() => {});
|
||||||
|
|||||||
@@ -55,6 +55,21 @@ describe('processOneTick', () => {
|
|||||||
s.waitUntil = Date.now() - 1000; s.status = 'waiting';
|
s.waitUntil = Date.now() - 1000; s.status = 'waiting';
|
||||||
assert.equal(await processOneTick(s, t, '%0', DEFAULT_CONFIG, () => true), 'skipped-not-claude');
|
assert.equal(await processOneTick(s, t, '%0', DEFAULT_CONFIG, () => true), 'skipped-not-claude');
|
||||||
assert.equal(t._sent.length, 0);
|
assert.equal(t._sent.length, 0);
|
||||||
|
assert.equal(s._lastForeground, 'vim');
|
||||||
|
});
|
||||||
|
it('accepts custom foregroundCommands from config', async () => {
|
||||||
|
const t = mockTmux('5-hour limit reached - resets 3pm (UTC)', 'my-claude-wrapper');
|
||||||
|
const s = createMonitorState();
|
||||||
|
s.waitUntil = Date.now() - 1000; s.status = 'waiting';
|
||||||
|
const config = { ...DEFAULT_CONFIG, foregroundCommands: ['my-claude-wrapper'] };
|
||||||
|
assert.equal(await processOneTick(s, t, '%0', config, () => true), 'retried');
|
||||||
|
assert.equal(t._sent.length, 1);
|
||||||
|
});
|
||||||
|
it('matches npx as default foreground command', async () => {
|
||||||
|
const t = mockTmux('5-hour limit reached - resets 3pm (UTC)', 'npx');
|
||||||
|
const s = createMonitorState();
|
||||||
|
s.waitUntil = Date.now() - 1000; s.status = 'waiting';
|
||||||
|
assert.equal(await processOneTick(s, t, '%0', DEFAULT_CONFIG, () => true), 'retried');
|
||||||
});
|
});
|
||||||
it('resets counter when rate limit disappears', async () => {
|
it('resets counter when rate limit disappears', async () => {
|
||||||
const t = mockTmux('Claude is working normally');
|
const t = mockTmux('Claude is working normally');
|
||||||
|
|||||||
Reference in New Issue
Block a user