oh-my-claude-sisyphus 3.6.3 → 3.7.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/README.md +16 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts +9 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts.map +1 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js +550 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -0
- package/dist/__tests__/installer.test.js +1 -1
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js +313 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts +8 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js +329 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js +167 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +295 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js.map +1 -0
- package/dist/cli/commands/wait.d.ts +52 -0
- package/dist/cli/commands/wait.d.ts.map +1 -0
- package/dist/cli/commands/wait.js +229 -0
- package/dist/cli/commands/wait.js.map +1 -0
- package/dist/cli/index.js +54 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/features/rate-limit-wait/daemon.d.ts +52 -0
- package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/daemon.js +545 -0
- package/dist/features/rate-limit-wait/daemon.js.map +1 -0
- package/dist/features/rate-limit-wait/index.d.ts +16 -0
- package/dist/features/rate-limit-wait/index.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/index.js +18 -0
- package/dist/features/rate-limit-wait/index.js.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts +22 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js +99 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts +59 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.js +304 -0
- package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -0
- package/dist/features/rate-limit-wait/types.d.ts +121 -0
- package/dist/features/rate-limit-wait/types.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/types.js +8 -0
- package/dist/features/rate-limit-wait/types.js.map +1 -0
- package/dist/hooks/bridge.d.ts +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +50 -4
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts +2 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.js.map +1 -1
- package/dist/hooks/omc-orchestrator/index.d.ts +7 -0
- package/dist/hooks/omc-orchestrator/index.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/index.js +95 -8
- package/dist/hooks/omc-orchestrator/index.js.map +1 -1
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js +244 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -0
- package/dist/hooks/permission-handler/index.d.ts +42 -0
- package/dist/hooks/permission-handler/index.d.ts.map +1 -0
- package/dist/hooks/permission-handler/index.js +111 -0
- package/dist/hooks/permission-handler/index.js.map +1 -0
- package/dist/hooks/pre-compact/index.d.ts +82 -0
- package/dist/hooks/pre-compact/index.d.ts.map +1 -0
- package/dist/hooks/pre-compact/index.js +265 -0
- package/dist/hooks/pre-compact/index.js.map +1 -0
- package/dist/hooks/session-end/index.d.ts +50 -0
- package/dist/hooks/session-end/index.d.ts.map +1 -0
- package/dist/hooks/session-end/index.js +207 -0
- package/dist/hooks/session-end/index.js.map +1 -0
- package/dist/hooks/setup/index.d.ts +66 -0
- package/dist/hooks/setup/index.d.ts.map +1 -0
- package/dist/hooks/setup/index.js +299 -0
- package/dist/hooks/setup/index.js.map +1 -0
- package/dist/hooks/setup/types.d.ts +25 -0
- package/dist/hooks/setup/types.d.ts.map +1 -0
- package/dist/hooks/setup/types.js +5 -0
- package/dist/hooks/setup/types.js.map +1 -0
- package/dist/hooks/subagent-tracker/index.d.ts +68 -29
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +301 -131
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.js +1 -1
- package/hooks/hooks.json +83 -1
- package/package.json +3 -1
- package/scripts/permission-handler.mjs +23 -0
- package/scripts/pre-compact.mjs +23 -0
- package/scripts/session-end.mjs +23 -0
- package/scripts/setup-init.mjs +23 -0
- package/scripts/setup-maintenance.mjs +23 -0
- package/scripts/subagent-tracker.mjs +35 -0
- package/templates/hooks/keyword-detector.mjs +198 -0
- package/templates/hooks/keyword-detector.sh +102 -0
- package/templates/hooks/persistent-mode.mjs +249 -0
- package/templates/hooks/persistent-mode.sh +187 -0
- package/templates/hooks/post-tool-use.mjs +133 -0
- package/templates/hooks/post-tool-use.sh +90 -0
- package/templates/hooks/pre-tool-use.mjs +145 -0
- package/templates/hooks/pre-tool-use.sh +113 -0
- package/templates/hooks/session-start.mjs +100 -0
- package/templates/hooks/session-start.sh +62 -0
- package/templates/hooks/stop-continuation.mjs +80 -0
- package/templates/hooks/stop-continuation.sh +40 -0
- package/templates/rules/README.md +40 -0
- package/templates/rules/coding-style.md +74 -0
- package/templates/rules/git-workflow.md +41 -0
- package/templates/rules/performance.md +40 -0
- package/templates/rules/security.md +41 -0
- package/templates/rules/testing.md +42 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for daemon.ts
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { mkdirSync, writeFileSync, existsSync, rmSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { tmpdir } from 'os';
|
|
8
|
+
import { readDaemonState, isDaemonRunning, getDaemonStatus, formatDaemonState, } from '../../features/rate-limit-wait/daemon.js';
|
|
9
|
+
describe('daemon', () => {
|
|
10
|
+
const testDir = join(tmpdir(), 'omc-daemon-test-' + Date.now());
|
|
11
|
+
const testConfig = {
|
|
12
|
+
stateFilePath: join(testDir, 'state.json'),
|
|
13
|
+
pidFilePath: join(testDir, 'daemon.pid'),
|
|
14
|
+
logFilePath: join(testDir, 'daemon.log'),
|
|
15
|
+
pollIntervalMs: 1000,
|
|
16
|
+
};
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
mkdirSync(testDir, { recursive: true });
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
try {
|
|
22
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Ignore cleanup errors
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
describe('readDaemonState', () => {
|
|
29
|
+
it('should return null when state file does not exist', () => {
|
|
30
|
+
const state = readDaemonState(testConfig);
|
|
31
|
+
expect(state).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
it('should read and parse state file', () => {
|
|
34
|
+
const testState = {
|
|
35
|
+
isRunning: true,
|
|
36
|
+
pid: 1234,
|
|
37
|
+
startedAt: new Date('2024-01-01T00:00:00Z'),
|
|
38
|
+
lastPollAt: new Date('2024-01-01T00:01:00Z'),
|
|
39
|
+
rateLimitStatus: {
|
|
40
|
+
fiveHourLimited: false,
|
|
41
|
+
weeklyLimited: false,
|
|
42
|
+
isLimited: false,
|
|
43
|
+
fiveHourResetsAt: null,
|
|
44
|
+
weeklyResetsAt: null,
|
|
45
|
+
nextResetAt: null,
|
|
46
|
+
timeUntilResetMs: null,
|
|
47
|
+
lastCheckedAt: new Date('2024-01-01T00:01:00Z'),
|
|
48
|
+
},
|
|
49
|
+
blockedPanes: [],
|
|
50
|
+
resumedPaneIds: [],
|
|
51
|
+
totalResumeAttempts: 5,
|
|
52
|
+
successfulResumes: 3,
|
|
53
|
+
errorCount: 0,
|
|
54
|
+
};
|
|
55
|
+
writeFileSync(testConfig.stateFilePath, JSON.stringify(testState));
|
|
56
|
+
const state = readDaemonState(testConfig);
|
|
57
|
+
expect(state).not.toBeNull();
|
|
58
|
+
expect(state.isRunning).toBe(true);
|
|
59
|
+
expect(state.pid).toBe(1234);
|
|
60
|
+
expect(state.totalResumeAttempts).toBe(5);
|
|
61
|
+
expect(state.successfulResumes).toBe(3);
|
|
62
|
+
expect(state.startedAt).toBeInstanceOf(Date);
|
|
63
|
+
});
|
|
64
|
+
it('should handle invalid JSON gracefully', () => {
|
|
65
|
+
writeFileSync(testConfig.stateFilePath, 'invalid json{');
|
|
66
|
+
const state = readDaemonState(testConfig);
|
|
67
|
+
expect(state).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('isDaemonRunning', () => {
|
|
71
|
+
it('should return false when PID file does not exist', () => {
|
|
72
|
+
const running = isDaemonRunning(testConfig);
|
|
73
|
+
expect(running).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
it('should return false for stale PID file', () => {
|
|
76
|
+
// Write a PID that definitely doesn't exist
|
|
77
|
+
writeFileSync(testConfig.pidFilePath, '999999');
|
|
78
|
+
const running = isDaemonRunning(testConfig);
|
|
79
|
+
expect(running).toBe(false);
|
|
80
|
+
// PID file should be cleaned up
|
|
81
|
+
expect(existsSync(testConfig.pidFilePath)).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
it('should return true for current process PID', () => {
|
|
84
|
+
// Write current process PID
|
|
85
|
+
writeFileSync(testConfig.pidFilePath, String(process.pid));
|
|
86
|
+
const running = isDaemonRunning(testConfig);
|
|
87
|
+
expect(running).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('getDaemonStatus', () => {
|
|
91
|
+
it('should return not started status', () => {
|
|
92
|
+
const result = getDaemonStatus(testConfig);
|
|
93
|
+
expect(result.success).toBe(true);
|
|
94
|
+
expect(result.message).toBe('Daemon has never been started');
|
|
95
|
+
});
|
|
96
|
+
it('should return not running status when state exists but no PID', () => {
|
|
97
|
+
const testState = {
|
|
98
|
+
isRunning: false,
|
|
99
|
+
pid: null,
|
|
100
|
+
startedAt: new Date(),
|
|
101
|
+
lastPollAt: new Date(),
|
|
102
|
+
rateLimitStatus: null,
|
|
103
|
+
blockedPanes: [],
|
|
104
|
+
resumedPaneIds: [],
|
|
105
|
+
totalResumeAttempts: 0,
|
|
106
|
+
successfulResumes: 0,
|
|
107
|
+
errorCount: 0,
|
|
108
|
+
};
|
|
109
|
+
writeFileSync(testConfig.stateFilePath, JSON.stringify(testState));
|
|
110
|
+
const result = getDaemonStatus(testConfig);
|
|
111
|
+
expect(result.success).toBe(true);
|
|
112
|
+
expect(result.message).toBe('Daemon is not running');
|
|
113
|
+
expect(result.state).toBeDefined();
|
|
114
|
+
});
|
|
115
|
+
it('should return running status when PID file exists with valid PID', () => {
|
|
116
|
+
const testState = {
|
|
117
|
+
isRunning: true,
|
|
118
|
+
pid: process.pid,
|
|
119
|
+
startedAt: new Date(),
|
|
120
|
+
lastPollAt: new Date(),
|
|
121
|
+
rateLimitStatus: null,
|
|
122
|
+
blockedPanes: [],
|
|
123
|
+
resumedPaneIds: [],
|
|
124
|
+
totalResumeAttempts: 0,
|
|
125
|
+
successfulResumes: 0,
|
|
126
|
+
errorCount: 0,
|
|
127
|
+
};
|
|
128
|
+
writeFileSync(testConfig.stateFilePath, JSON.stringify(testState));
|
|
129
|
+
writeFileSync(testConfig.pidFilePath, String(process.pid));
|
|
130
|
+
const result = getDaemonStatus(testConfig);
|
|
131
|
+
expect(result.success).toBe(true);
|
|
132
|
+
expect(result.message).toBe('Daemon is running');
|
|
133
|
+
expect(result.state).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('formatDaemonState', () => {
|
|
137
|
+
it('should format running daemon state', () => {
|
|
138
|
+
const state = {
|
|
139
|
+
isRunning: true,
|
|
140
|
+
pid: 1234,
|
|
141
|
+
startedAt: new Date(),
|
|
142
|
+
lastPollAt: new Date(),
|
|
143
|
+
rateLimitStatus: {
|
|
144
|
+
fiveHourLimited: false,
|
|
145
|
+
weeklyLimited: false,
|
|
146
|
+
isLimited: false,
|
|
147
|
+
fiveHourResetsAt: null,
|
|
148
|
+
weeklyResetsAt: null,
|
|
149
|
+
nextResetAt: null,
|
|
150
|
+
timeUntilResetMs: null,
|
|
151
|
+
lastCheckedAt: new Date(),
|
|
152
|
+
},
|
|
153
|
+
blockedPanes: [],
|
|
154
|
+
resumedPaneIds: [],
|
|
155
|
+
totalResumeAttempts: 10,
|
|
156
|
+
successfulResumes: 8,
|
|
157
|
+
errorCount: 2,
|
|
158
|
+
};
|
|
159
|
+
const output = formatDaemonState(state);
|
|
160
|
+
expect(output).toContain('Daemon running');
|
|
161
|
+
expect(output).toContain('PID: 1234');
|
|
162
|
+
expect(output).toContain('Not rate limited');
|
|
163
|
+
expect(output).toContain('Resume attempts: 10');
|
|
164
|
+
expect(output).toContain('Successful: 8');
|
|
165
|
+
expect(output).toContain('Errors: 2');
|
|
166
|
+
});
|
|
167
|
+
it('should format rate limited state', () => {
|
|
168
|
+
const state = {
|
|
169
|
+
isRunning: true,
|
|
170
|
+
pid: 1234,
|
|
171
|
+
startedAt: new Date(),
|
|
172
|
+
lastPollAt: new Date(),
|
|
173
|
+
rateLimitStatus: {
|
|
174
|
+
fiveHourLimited: true,
|
|
175
|
+
weeklyLimited: false,
|
|
176
|
+
isLimited: true,
|
|
177
|
+
fiveHourResetsAt: new Date(Date.now() + 3600000),
|
|
178
|
+
weeklyResetsAt: null,
|
|
179
|
+
nextResetAt: new Date(Date.now() + 3600000),
|
|
180
|
+
timeUntilResetMs: 3600000,
|
|
181
|
+
lastCheckedAt: new Date(),
|
|
182
|
+
},
|
|
183
|
+
blockedPanes: [],
|
|
184
|
+
resumedPaneIds: [],
|
|
185
|
+
totalResumeAttempts: 0,
|
|
186
|
+
successfulResumes: 0,
|
|
187
|
+
errorCount: 0,
|
|
188
|
+
};
|
|
189
|
+
const output = formatDaemonState(state);
|
|
190
|
+
expect(output).toContain('5-hour limit reached');
|
|
191
|
+
});
|
|
192
|
+
it('should format state with blocked panes', () => {
|
|
193
|
+
const state = {
|
|
194
|
+
isRunning: true,
|
|
195
|
+
pid: 1234,
|
|
196
|
+
startedAt: new Date(),
|
|
197
|
+
lastPollAt: new Date(),
|
|
198
|
+
rateLimitStatus: null,
|
|
199
|
+
blockedPanes: [
|
|
200
|
+
{
|
|
201
|
+
id: '%0',
|
|
202
|
+
session: 'main',
|
|
203
|
+
windowIndex: 0,
|
|
204
|
+
windowName: 'dev',
|
|
205
|
+
paneIndex: 0,
|
|
206
|
+
isActive: true,
|
|
207
|
+
analysis: {
|
|
208
|
+
hasClaudeCode: true,
|
|
209
|
+
hasRateLimitMessage: true,
|
|
210
|
+
isBlocked: true,
|
|
211
|
+
confidence: 0.9,
|
|
212
|
+
},
|
|
213
|
+
firstDetectedAt: new Date(),
|
|
214
|
+
resumeAttempted: false,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
resumedPaneIds: [],
|
|
218
|
+
totalResumeAttempts: 0,
|
|
219
|
+
successfulResumes: 0,
|
|
220
|
+
errorCount: 0,
|
|
221
|
+
};
|
|
222
|
+
const output = formatDaemonState(state);
|
|
223
|
+
expect(output).toContain('Found 1 blocked');
|
|
224
|
+
});
|
|
225
|
+
it('should format state with last error', () => {
|
|
226
|
+
const state = {
|
|
227
|
+
isRunning: true,
|
|
228
|
+
pid: 1234,
|
|
229
|
+
startedAt: new Date(),
|
|
230
|
+
lastPollAt: new Date(),
|
|
231
|
+
rateLimitStatus: null,
|
|
232
|
+
blockedPanes: [],
|
|
233
|
+
resumedPaneIds: [],
|
|
234
|
+
totalResumeAttempts: 0,
|
|
235
|
+
successfulResumes: 0,
|
|
236
|
+
errorCount: 1,
|
|
237
|
+
lastError: 'Test error message',
|
|
238
|
+
};
|
|
239
|
+
const output = formatDaemonState(state);
|
|
240
|
+
expect(output).toContain('Last error: Test error message');
|
|
241
|
+
});
|
|
242
|
+
it('should format not running state', () => {
|
|
243
|
+
const state = {
|
|
244
|
+
isRunning: false,
|
|
245
|
+
pid: null,
|
|
246
|
+
startedAt: null,
|
|
247
|
+
lastPollAt: null,
|
|
248
|
+
rateLimitStatus: null,
|
|
249
|
+
blockedPanes: [],
|
|
250
|
+
resumedPaneIds: [],
|
|
251
|
+
totalResumeAttempts: 0,
|
|
252
|
+
successfulResumes: 0,
|
|
253
|
+
errorCount: 0,
|
|
254
|
+
};
|
|
255
|
+
const output = formatDaemonState(state);
|
|
256
|
+
expect(output).toContain('Daemon not running');
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
describe('security: file permissions', () => {
|
|
260
|
+
it('should create state file with restrictive permissions', () => {
|
|
261
|
+
const testState = {
|
|
262
|
+
isRunning: true,
|
|
263
|
+
pid: 1234,
|
|
264
|
+
startedAt: new Date(),
|
|
265
|
+
lastPollAt: new Date(),
|
|
266
|
+
rateLimitStatus: null,
|
|
267
|
+
blockedPanes: [],
|
|
268
|
+
resumedPaneIds: [],
|
|
269
|
+
totalResumeAttempts: 0,
|
|
270
|
+
successfulResumes: 0,
|
|
271
|
+
errorCount: 0,
|
|
272
|
+
};
|
|
273
|
+
writeFileSync(testConfig.stateFilePath, JSON.stringify(testState));
|
|
274
|
+
// Read state back (this exercises the read path)
|
|
275
|
+
const state = readDaemonState(testConfig);
|
|
276
|
+
expect(state).not.toBeNull();
|
|
277
|
+
});
|
|
278
|
+
it('should not store sensitive data in state file', () => {
|
|
279
|
+
const testState = {
|
|
280
|
+
isRunning: true,
|
|
281
|
+
pid: 1234,
|
|
282
|
+
startedAt: new Date(),
|
|
283
|
+
lastPollAt: new Date(),
|
|
284
|
+
rateLimitStatus: {
|
|
285
|
+
fiveHourLimited: false,
|
|
286
|
+
weeklyLimited: false,
|
|
287
|
+
isLimited: false,
|
|
288
|
+
fiveHourResetsAt: null,
|
|
289
|
+
weeklyResetsAt: null,
|
|
290
|
+
nextResetAt: null,
|
|
291
|
+
timeUntilResetMs: null,
|
|
292
|
+
lastCheckedAt: new Date(),
|
|
293
|
+
},
|
|
294
|
+
blockedPanes: [],
|
|
295
|
+
resumedPaneIds: [],
|
|
296
|
+
totalResumeAttempts: 0,
|
|
297
|
+
successfulResumes: 0,
|
|
298
|
+
errorCount: 0,
|
|
299
|
+
};
|
|
300
|
+
writeFileSync(testConfig.stateFilePath, JSON.stringify(testState));
|
|
301
|
+
// Verify no tokens or credentials in state file
|
|
302
|
+
const { readFileSync } = require('fs');
|
|
303
|
+
const content = readFileSync(testConfig.stateFilePath, 'utf-8');
|
|
304
|
+
// State should not contain sensitive fields
|
|
305
|
+
expect(content).not.toContain('accessToken');
|
|
306
|
+
expect(content).not.toContain('apiKey');
|
|
307
|
+
expect(content).not.toContain('password');
|
|
308
|
+
expect(content).not.toContain('secret');
|
|
309
|
+
expect(content).not.toContain('credential');
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
//# sourceMappingURL=daemon.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.test.js","sourceRoot":"","sources":["../../../src/__tests__/rate-limit-wait/daemon.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/rate-limit-wait/integration.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Tests for Rate Limit Wait Feature
|
|
3
|
+
*
|
|
4
|
+
* These tests simulate real-world scenarios without hitting actual rate limits.
|
|
5
|
+
* They verify the full flow from detection to resume.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import { mkdirSync, rmSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { tmpdir } from 'os';
|
|
11
|
+
// Mock modules
|
|
12
|
+
vi.mock('../../hud/usage-api.js', () => ({
|
|
13
|
+
getUsage: vi.fn(),
|
|
14
|
+
}));
|
|
15
|
+
vi.mock('child_process', () => ({
|
|
16
|
+
execSync: vi.fn(),
|
|
17
|
+
spawnSync: vi.fn(),
|
|
18
|
+
spawn: vi.fn(),
|
|
19
|
+
}));
|
|
20
|
+
import { getUsage } from '../../hud/usage-api.js';
|
|
21
|
+
import { execSync, spawnSync } from 'child_process';
|
|
22
|
+
import { checkRateLimitStatus, analyzePaneContent, scanForBlockedPanes, formatDaemonState, } from '../../features/rate-limit-wait/index.js';
|
|
23
|
+
describe('Rate Limit Wait Integration Tests', () => {
|
|
24
|
+
const testDir = join(tmpdir(), 'omc-integration-test-' + Date.now());
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
mkdirSync(testDir, { recursive: true });
|
|
28
|
+
});
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
try {
|
|
31
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Ignore cleanup errors
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
describe('Scenario: Rate limit detection and tracking', () => {
|
|
38
|
+
it('should detect when 5-hour limit is reached', async () => {
|
|
39
|
+
// Simulate rate limit API response
|
|
40
|
+
vi.mocked(getUsage).mockResolvedValue({
|
|
41
|
+
fiveHourPercent: 100,
|
|
42
|
+
weeklyPercent: 75,
|
|
43
|
+
fiveHourResetsAt: new Date(Date.now() + 3600000),
|
|
44
|
+
weeklyResetsAt: null,
|
|
45
|
+
});
|
|
46
|
+
const status = await checkRateLimitStatus();
|
|
47
|
+
expect(status).not.toBeNull();
|
|
48
|
+
expect(status.isLimited).toBe(true);
|
|
49
|
+
expect(status.fiveHourLimited).toBe(true);
|
|
50
|
+
expect(status.weeklyLimited).toBe(false);
|
|
51
|
+
expect(status.timeUntilResetMs).toBeGreaterThan(0);
|
|
52
|
+
expect(status.timeUntilResetMs).toBeLessThanOrEqual(3600000);
|
|
53
|
+
});
|
|
54
|
+
it('should detect when weekly limit is reached', async () => {
|
|
55
|
+
vi.mocked(getUsage).mockResolvedValue({
|
|
56
|
+
fiveHourPercent: 50,
|
|
57
|
+
weeklyPercent: 100,
|
|
58
|
+
fiveHourResetsAt: null,
|
|
59
|
+
weeklyResetsAt: new Date(Date.now() + 86400000),
|
|
60
|
+
});
|
|
61
|
+
const status = await checkRateLimitStatus();
|
|
62
|
+
expect(status).not.toBeNull();
|
|
63
|
+
expect(status.isLimited).toBe(true);
|
|
64
|
+
expect(status.fiveHourLimited).toBe(false);
|
|
65
|
+
expect(status.weeklyLimited).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it('should handle transition from limited to not limited', async () => {
|
|
68
|
+
// First call: limited
|
|
69
|
+
vi.mocked(getUsage).mockResolvedValueOnce({
|
|
70
|
+
fiveHourPercent: 100,
|
|
71
|
+
weeklyPercent: 50,
|
|
72
|
+
fiveHourResetsAt: new Date(Date.now() + 1000),
|
|
73
|
+
weeklyResetsAt: null,
|
|
74
|
+
});
|
|
75
|
+
const limitedStatus = await checkRateLimitStatus();
|
|
76
|
+
expect(limitedStatus.isLimited).toBe(true);
|
|
77
|
+
// Second call: no longer limited
|
|
78
|
+
vi.mocked(getUsage).mockResolvedValueOnce({
|
|
79
|
+
fiveHourPercent: 0,
|
|
80
|
+
weeklyPercent: 50,
|
|
81
|
+
fiveHourResetsAt: null,
|
|
82
|
+
weeklyResetsAt: null,
|
|
83
|
+
});
|
|
84
|
+
const clearedStatus = await checkRateLimitStatus();
|
|
85
|
+
expect(clearedStatus.isLimited).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('Scenario: tmux pane analysis accuracy', () => {
|
|
89
|
+
it('should correctly identify Claude Code rate limit message', () => {
|
|
90
|
+
const realWorldContent = `
|
|
91
|
+
╭─────────────────────────────────────────────────────────────────╮
|
|
92
|
+
│ Claude Code │
|
|
93
|
+
╰─────────────────────────────────────────────────────────────────╯
|
|
94
|
+
|
|
95
|
+
You've reached your usage limit for the 5-hour period.
|
|
96
|
+
Your limit will reset at 3:45 PM.
|
|
97
|
+
|
|
98
|
+
What would you like to do?
|
|
99
|
+
|
|
100
|
+
[1] Wait and continue automatically when limit resets
|
|
101
|
+
[2] Switch to a different conversation
|
|
102
|
+
[3] Exit
|
|
103
|
+
|
|
104
|
+
> `;
|
|
105
|
+
const result = analyzePaneContent(realWorldContent);
|
|
106
|
+
expect(result.hasClaudeCode).toBe(true);
|
|
107
|
+
expect(result.hasRateLimitMessage).toBe(true);
|
|
108
|
+
expect(result.isBlocked).toBe(true);
|
|
109
|
+
expect(result.rateLimitType).toBe('five_hour');
|
|
110
|
+
expect(result.confidence).toBeGreaterThanOrEqual(0.8);
|
|
111
|
+
});
|
|
112
|
+
it('should correctly identify weekly rate limit message', () => {
|
|
113
|
+
const weeklyLimitContent = `
|
|
114
|
+
Claude Code v1.0.0
|
|
115
|
+
|
|
116
|
+
⚠️ Weekly usage limit reached
|
|
117
|
+
|
|
118
|
+
You've used your weekly allocation of tokens.
|
|
119
|
+
Limit resets on Monday at 12:00 AM UTC.
|
|
120
|
+
|
|
121
|
+
Options:
|
|
122
|
+
[1] Continue when limit resets
|
|
123
|
+
[2] Exit
|
|
124
|
+
|
|
125
|
+
Enter choice: `;
|
|
126
|
+
const result = analyzePaneContent(weeklyLimitContent);
|
|
127
|
+
expect(result.hasClaudeCode).toBe(true);
|
|
128
|
+
expect(result.hasRateLimitMessage).toBe(true);
|
|
129
|
+
expect(result.isBlocked).toBe(true);
|
|
130
|
+
expect(result.rateLimitType).toBe('weekly');
|
|
131
|
+
});
|
|
132
|
+
it('should NOT flag normal Claude Code output as blocked', () => {
|
|
133
|
+
const normalContent = `
|
|
134
|
+
Claude Code
|
|
135
|
+
|
|
136
|
+
> What would you like to build today?
|
|
137
|
+
|
|
138
|
+
I can help you with:
|
|
139
|
+
- Writing code
|
|
140
|
+
- Debugging
|
|
141
|
+
- Refactoring
|
|
142
|
+
- Documentation
|
|
143
|
+
|
|
144
|
+
Just describe what you need!
|
|
145
|
+
`;
|
|
146
|
+
const result = analyzePaneContent(normalContent);
|
|
147
|
+
expect(result.hasClaudeCode).toBe(true);
|
|
148
|
+
expect(result.hasRateLimitMessage).toBe(false);
|
|
149
|
+
expect(result.isBlocked).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
it('should NOT flag unrelated rate limit messages', () => {
|
|
152
|
+
const unrelatedContent = `
|
|
153
|
+
$ curl https://api.github.com/users/test
|
|
154
|
+
{
|
|
155
|
+
"message": "API rate limit exceeded for IP",
|
|
156
|
+
"documentation_url": "https://docs.github.com"
|
|
157
|
+
}
|
|
158
|
+
$ `;
|
|
159
|
+
const result = analyzePaneContent(unrelatedContent);
|
|
160
|
+
expect(result.hasClaudeCode).toBe(false);
|
|
161
|
+
expect(result.hasRateLimitMessage).toBe(true);
|
|
162
|
+
expect(result.isBlocked).toBe(false); // No Claude context
|
|
163
|
+
});
|
|
164
|
+
it('should handle edge case: old rate limit message scrolled up', () => {
|
|
165
|
+
// Only last 15 lines should be analyzed
|
|
166
|
+
// Rate limit message from earlier should be ignored if not in recent content
|
|
167
|
+
const scrolledContent = `
|
|
168
|
+
User: fix the bug
|
|
169
|
+
Assistant: I'll fix that for you.
|
|
170
|
+
[Edit] src/main.ts
|
|
171
|
+
Done! The bug is fixed.
|
|
172
|
+
|
|
173
|
+
User: thanks
|
|
174
|
+
Assistant: You're welcome!
|
|
175
|
+
|
|
176
|
+
User: what else?
|
|
177
|
+
Assistant: I can help with more tasks.
|
|
178
|
+
|
|
179
|
+
> `;
|
|
180
|
+
const result = analyzePaneContent(scrolledContent);
|
|
181
|
+
expect(result.isBlocked).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe('Scenario: Full daemon state lifecycle', () => {
|
|
185
|
+
it('should format daemon state correctly for user display', () => {
|
|
186
|
+
const state = {
|
|
187
|
+
isRunning: true,
|
|
188
|
+
pid: 12345,
|
|
189
|
+
startedAt: new Date('2024-01-01T10:00:00Z'),
|
|
190
|
+
lastPollAt: new Date('2024-01-01T10:05:00Z'),
|
|
191
|
+
rateLimitStatus: {
|
|
192
|
+
fiveHourLimited: true,
|
|
193
|
+
weeklyLimited: false,
|
|
194
|
+
isLimited: true,
|
|
195
|
+
fiveHourResetsAt: new Date('2024-01-01T15:00:00Z'),
|
|
196
|
+
weeklyResetsAt: null,
|
|
197
|
+
nextResetAt: new Date('2024-01-01T15:00:00Z'),
|
|
198
|
+
timeUntilResetMs: 3600000,
|
|
199
|
+
lastCheckedAt: new Date('2024-01-01T10:05:00Z'),
|
|
200
|
+
},
|
|
201
|
+
blockedPanes: [
|
|
202
|
+
{
|
|
203
|
+
id: '%0',
|
|
204
|
+
session: 'dev',
|
|
205
|
+
windowIndex: 0,
|
|
206
|
+
windowName: 'claude',
|
|
207
|
+
paneIndex: 0,
|
|
208
|
+
isActive: true,
|
|
209
|
+
analysis: {
|
|
210
|
+
hasClaudeCode: true,
|
|
211
|
+
hasRateLimitMessage: true,
|
|
212
|
+
isBlocked: true,
|
|
213
|
+
rateLimitType: 'five_hour',
|
|
214
|
+
confidence: 0.95,
|
|
215
|
+
},
|
|
216
|
+
firstDetectedAt: new Date('2024-01-01T10:01:00Z'),
|
|
217
|
+
resumeAttempted: false,
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
resumedPaneIds: [],
|
|
221
|
+
totalResumeAttempts: 0,
|
|
222
|
+
successfulResumes: 0,
|
|
223
|
+
errorCount: 0,
|
|
224
|
+
};
|
|
225
|
+
const output = formatDaemonState(state);
|
|
226
|
+
// Verify key information is present
|
|
227
|
+
expect(output).toContain('Daemon running');
|
|
228
|
+
expect(output).toContain('12345');
|
|
229
|
+
expect(output).toContain('5-hour limit');
|
|
230
|
+
expect(output).toContain('Found 1 blocked');
|
|
231
|
+
expect(output).toContain('%0');
|
|
232
|
+
});
|
|
233
|
+
it('should track resume attempts correctly', () => {
|
|
234
|
+
const stateAfterResume = {
|
|
235
|
+
isRunning: true,
|
|
236
|
+
pid: 12345,
|
|
237
|
+
startedAt: new Date(),
|
|
238
|
+
lastPollAt: new Date(),
|
|
239
|
+
rateLimitStatus: {
|
|
240
|
+
fiveHourLimited: false,
|
|
241
|
+
weeklyLimited: false,
|
|
242
|
+
isLimited: false,
|
|
243
|
+
fiveHourResetsAt: null,
|
|
244
|
+
weeklyResetsAt: null,
|
|
245
|
+
nextResetAt: null,
|
|
246
|
+
timeUntilResetMs: null,
|
|
247
|
+
lastCheckedAt: new Date(),
|
|
248
|
+
},
|
|
249
|
+
blockedPanes: [],
|
|
250
|
+
resumedPaneIds: ['%0', '%1'],
|
|
251
|
+
totalResumeAttempts: 2,
|
|
252
|
+
successfulResumes: 2,
|
|
253
|
+
errorCount: 0,
|
|
254
|
+
};
|
|
255
|
+
const output = formatDaemonState(stateAfterResume);
|
|
256
|
+
expect(output).toContain('Resume attempts: 2');
|
|
257
|
+
expect(output).toContain('Successful: 2');
|
|
258
|
+
expect(output).toContain('Not rate limited');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
describe('Scenario: Error handling and edge cases', () => {
|
|
262
|
+
it('should handle OAuth credentials not available', async () => {
|
|
263
|
+
vi.mocked(getUsage).mockResolvedValue(null);
|
|
264
|
+
const status = await checkRateLimitStatus();
|
|
265
|
+
expect(status).toBeNull();
|
|
266
|
+
});
|
|
267
|
+
it('should handle API timeout gracefully', async () => {
|
|
268
|
+
vi.mocked(getUsage).mockRejectedValue(new Error('ETIMEDOUT'));
|
|
269
|
+
const status = await checkRateLimitStatus();
|
|
270
|
+
expect(status).toBeNull();
|
|
271
|
+
});
|
|
272
|
+
it('should handle tmux not installed', () => {
|
|
273
|
+
vi.mocked(spawnSync).mockReturnValue({
|
|
274
|
+
status: 1,
|
|
275
|
+
stdout: '',
|
|
276
|
+
stderr: 'tmux: command not found',
|
|
277
|
+
signal: null,
|
|
278
|
+
pid: 0,
|
|
279
|
+
output: [],
|
|
280
|
+
});
|
|
281
|
+
// scanForBlockedPanes should return empty array, not throw
|
|
282
|
+
const blocked = scanForBlockedPanes();
|
|
283
|
+
expect(blocked).toEqual([]);
|
|
284
|
+
});
|
|
285
|
+
it('should handle malformed tmux output', () => {
|
|
286
|
+
vi.mocked(spawnSync).mockReturnValue({
|
|
287
|
+
status: 0,
|
|
288
|
+
stdout: '/usr/bin/tmux',
|
|
289
|
+
stderr: '',
|
|
290
|
+
signal: null,
|
|
291
|
+
pid: 1234,
|
|
292
|
+
output: [],
|
|
293
|
+
});
|
|
294
|
+
vi.mocked(execSync).mockReturnValue('malformed output without proper format');
|
|
295
|
+
// Should not throw, just return empty
|
|
296
|
+
const blocked = scanForBlockedPanes();
|
|
297
|
+
expect(blocked).toEqual([]);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
describe('Scenario: Confidence scoring', () => {
|
|
301
|
+
it('should give higher confidence for multiple indicators', () => {
|
|
302
|
+
const highConfidenceContent = `
|
|
303
|
+
Claude Code
|
|
304
|
+
Rate limit reached
|
|
305
|
+
5-hour usage limit
|
|
306
|
+
[1] Continue
|
|
307
|
+
[2] Exit
|
|
308
|
+
`;
|
|
309
|
+
const lowConfidenceContent = `
|
|
310
|
+
Claude
|
|
311
|
+
rate limit
|
|
312
|
+
`;
|
|
313
|
+
const highResult = analyzePaneContent(highConfidenceContent);
|
|
314
|
+
const lowResult = analyzePaneContent(lowConfidenceContent);
|
|
315
|
+
expect(highResult.confidence).toBeGreaterThan(lowResult.confidence);
|
|
316
|
+
});
|
|
317
|
+
it('should require minimum confidence to mark as blocked', () => {
|
|
318
|
+
const ambiguousContent = `
|
|
319
|
+
some claude reference
|
|
320
|
+
limit mentioned
|
|
321
|
+
`;
|
|
322
|
+
const result = analyzePaneContent(ambiguousContent);
|
|
323
|
+
// Even if some patterns match, confidence should be too low
|
|
324
|
+
expect(result.confidence).toBeLessThan(0.6);
|
|
325
|
+
expect(result.isBlocked).toBe(false);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
//# sourceMappingURL=integration.test.js.map
|