fix: remove Codex API key auth path, add secret redaction and d7 auto-rotation
- Remove OPENAI_API_KEY from .env and Codex child process env to prevent API billing when subscription quota is exhausted - Remove writeCodexApiKeyAuth entirely — Codex now uses OAuth only - Add 9-pattern secret redaction in formatOutbound() to prevent key leaks - Fix Codex usage bucket aggregation: use 'codex' bucket only instead of max across all buckets (bengalfox = Codex Spark, not needed) - Add d7≥100% auto-rotation in updateCodexAccountUsage to skip exhausted accounts and prevent API billing fallback - Add findNextCodexAvailable that checks both rate-limits and d7 usage - Clean stale apikey auth.json files from all session directories - Update tests: OAuth-only auth, d7 auto-skip (3 new tests), dashboard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import { EventEmitter } from 'events';
|
||||
import fs from 'fs';
|
||||
|
||||
import { GroupQueue } from './group-queue.js';
|
||||
import { GroupQueue, type GroupRunContext } from './group-queue.js';
|
||||
|
||||
// Mock config to control concurrency limit
|
||||
vi.mock('./config.js', () => ({
|
||||
@@ -121,6 +121,100 @@ describe('GroupQueue', () => {
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
});
|
||||
|
||||
it('force-terminates a lingering process after output was delivered', async () => {
|
||||
class StubbornProcess extends EventEmitter {
|
||||
exitCode: number | null = null;
|
||||
signalCode: NodeJS.Signals | null = null;
|
||||
kill = vi.fn(() => true);
|
||||
}
|
||||
|
||||
const ipcDir = '/tmp/ejclaw-test-data/ipc/group-folder';
|
||||
let releaseRun!: (value: boolean) => void;
|
||||
let runId: string | undefined;
|
||||
const blocker = new Promise<boolean>((resolve) => {
|
||||
releaseRun = resolve;
|
||||
});
|
||||
|
||||
const processMessages = vi.fn(
|
||||
async (_groupJid: string, context: GroupRunContext) => {
|
||||
runId = context.runId;
|
||||
return await blocker;
|
||||
},
|
||||
);
|
||||
|
||||
queue.setProcessMessagesFn(processMessages);
|
||||
queue.enqueueMessageCheck('group1@g.us', ipcDir);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
|
||||
expect(runId).toEqual(expect.any(String));
|
||||
|
||||
const proc = new StubbornProcess();
|
||||
queue.registerProcess(
|
||||
'group1@g.us',
|
||||
proc as unknown as import('child_process').ChildProcess,
|
||||
'proc-1',
|
||||
ipcDir,
|
||||
);
|
||||
|
||||
queue.closeStdin('group1@g.us', {
|
||||
runId,
|
||||
reason: 'output-delivered-close',
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(60_000);
|
||||
expect(proc.kill).toHaveBeenCalledWith('SIGTERM');
|
||||
|
||||
await vi.advanceTimersByTimeAsync(15_000);
|
||||
expect(proc.kill).toHaveBeenCalledWith('SIGKILL');
|
||||
|
||||
releaseRun(true);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
});
|
||||
|
||||
it('clears post-close termination timers once the run exits', async () => {
|
||||
class FakeProcess extends EventEmitter {
|
||||
exitCode: number | null = null;
|
||||
signalCode: NodeJS.Signals | null = null;
|
||||
kill = vi.fn(() => true);
|
||||
}
|
||||
|
||||
const ipcDir = '/tmp/ejclaw-test-data/ipc/group-folder';
|
||||
let releaseRun!: (value: boolean) => void;
|
||||
const blocker = new Promise<boolean>((resolve) => {
|
||||
releaseRun = resolve;
|
||||
});
|
||||
|
||||
const proc = new FakeProcess();
|
||||
const processMessages = vi.fn(
|
||||
async (_groupJid: string, context: GroupRunContext) => {
|
||||
queue.registerProcess(
|
||||
'group1@g.us',
|
||||
proc as unknown as import('child_process').ChildProcess,
|
||||
'proc-1',
|
||||
ipcDir,
|
||||
);
|
||||
|
||||
queue.closeStdin('group1@g.us', {
|
||||
runId: context.runId,
|
||||
reason: 'output-delivered-close',
|
||||
});
|
||||
|
||||
await blocker;
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
queue.setProcessMessagesFn(processMessages);
|
||||
queue.enqueueMessageCheck('group1@g.us', ipcDir);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
|
||||
releaseRun(true);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(75_000);
|
||||
expect(proc.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// --- Global concurrency limit ---
|
||||
|
||||
it('respects global concurrency limit', async () => {
|
||||
|
||||
Reference in New Issue
Block a user