github-issue-tower-defence-management 1.146.5 → 1.146.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/.github/dependabot.yml +2 -0
- package/.github/workflows/commit-lint.yml +20 -0
- package/.github/workflows/umino-project.yml +0 -1
- package/CHANGELOG.md +14 -0
- package/bin/adapter/entry-points/cli/index.js +2 -2
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.js +4 -3
- package/bin/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js +4 -3
- package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js +3 -2
- package/bin/adapter/entry-points/handlers/notifySilentTmuxSessions.js.map +1 -1
- package/bin/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.js +1 -1
- package/bin/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.js.map +1 -1
- package/bin/adapter/repositories/FileSystemSubAgentLivenessResolver.js +19 -1
- package/bin/adapter/repositories/FileSystemSubAgentLivenessResolver.js.map +1 -1
- package/bin/adapter/repositories/TmuxSilentSessionNotificationRepository.js +73 -2
- package/bin/adapter/repositories/TmuxSilentSessionNotificationRepository.js.map +1 -1
- package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js +3 -1
- package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js.map +1 -1
- package/bin/domain/usecases/OauthTokenSelectUseCase.js +13 -4
- package/bin/domain/usecases/OauthTokenSelectUseCase.js.map +1 -1
- package/package.json +1 -1
- package/renovate.json +5 -0
- package/src/adapter/entry-points/cli/index.ts +2 -2
- package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.test.ts +7 -4
- package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts +4 -1
- package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +6 -2
- package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +8 -2
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +1 -0
- package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +14 -2
- package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts +4 -1
- package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.test.ts +49 -1
- package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts +21 -1
- package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.test.ts +157 -15
- package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts +115 -1
- package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +77 -5
- package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +10 -1
- package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +39 -8
- package/src/domain/usecases/OauthTokenSelectUseCase.ts +25 -5
- package/types/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.d.ts +3 -1
- package/types/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts +3 -2
- package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts +1 -0
- package/types/adapter/entry-points/handlers/notifySilentTmuxSessions.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.d.ts.map +1 -1
- package/types/adapter/repositories/FileSystemSubAgentLivenessResolver.d.ts +1 -0
- package/types/adapter/repositories/FileSystemSubAgentLivenessResolver.d.ts.map +1 -1
- package/types/adapter/repositories/TmuxSilentSessionNotificationRepository.d.ts +14 -1
- package/types/adapter/repositories/TmuxSilentSessionNotificationRepository.d.ts.map +1 -1
- package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts.map +1 -1
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +4 -1
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts.map +1 -1
|
@@ -1,11 +1,66 @@
|
|
|
1
1
|
import { SilentSessionNotificationRepository } from '../../domain/usecases/adapter-interfaces/SilentSessionNotificationRepository';
|
|
2
2
|
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
import { Sleeper } from '../../domain/usecases/adapter-interfaces/Sleeper';
|
|
3
4
|
|
|
4
5
|
const BRACKETED_PASTE_START = '\x1b[200~';
|
|
5
6
|
const BRACKETED_PASTE_END = '\x1b[201~';
|
|
6
7
|
|
|
8
|
+
export const DEFAULT_SUBMIT_PUSH_OUT_ATTEMPT_LIMIT = 3;
|
|
9
|
+
export const DEFAULT_SUBMIT_PUSH_OUT_WAIT_MILLISECONDS = 2500;
|
|
10
|
+
|
|
11
|
+
const INPUT_BOX_BORDER_LINE_PATTERN = /^[\s─━╌┄┈│╭╮╰╯]+$/u;
|
|
12
|
+
const INPUT_PROMPT_PREFIX_PATTERN = /^[\s>❯│]+/u;
|
|
13
|
+
const INPUT_BOX_MESSAGE_PROBE_LENGTH = 24;
|
|
14
|
+
|
|
15
|
+
type InputBoxState = 'holdsMessage' | 'clearedMessage' | 'unreadable';
|
|
16
|
+
|
|
17
|
+
const withoutWhitespace = (value: string): string => value.replace(/\s+/gu, '');
|
|
18
|
+
|
|
19
|
+
export const extractTmuxInputBoxContent = (paneText: string): string | null => {
|
|
20
|
+
const lines = paneText.split('\n');
|
|
21
|
+
const borderLineIndexes = lines.flatMap((line, index) =>
|
|
22
|
+
line.trim().length > 0 && INPUT_BOX_BORDER_LINE_PATTERN.test(line)
|
|
23
|
+
? [index]
|
|
24
|
+
: [],
|
|
25
|
+
);
|
|
26
|
+
if (borderLineIndexes.length < 2) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const closingBorderIndex = borderLineIndexes[borderLineIndexes.length - 1];
|
|
30
|
+
const openingBorderIndex = borderLineIndexes[borderLineIndexes.length - 2];
|
|
31
|
+
return lines
|
|
32
|
+
.slice(openingBorderIndex + 1, closingBorderIndex)
|
|
33
|
+
.map((line) => line.replace(INPUT_PROMPT_PREFIX_PATTERN, ''))
|
|
34
|
+
.join('');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const resolveInputBoxStateFromPane = (
|
|
38
|
+
paneText: string,
|
|
39
|
+
singleLineMessage: string,
|
|
40
|
+
): InputBoxState => {
|
|
41
|
+
const inputBoxContent = extractTmuxInputBoxContent(paneText);
|
|
42
|
+
if (inputBoxContent === null) {
|
|
43
|
+
return 'unreadable';
|
|
44
|
+
}
|
|
45
|
+
const messageProbe = withoutWhitespace(singleLineMessage).slice(
|
|
46
|
+
0,
|
|
47
|
+
INPUT_BOX_MESSAGE_PROBE_LENGTH,
|
|
48
|
+
);
|
|
49
|
+
if (messageProbe.length === 0) {
|
|
50
|
+
return 'clearedMessage';
|
|
51
|
+
}
|
|
52
|
+
return withoutWhitespace(inputBoxContent).includes(messageProbe)
|
|
53
|
+
? 'holdsMessage'
|
|
54
|
+
: 'clearedMessage';
|
|
55
|
+
};
|
|
56
|
+
|
|
7
57
|
export class TmuxSilentSessionNotificationRepository implements SilentSessionNotificationRepository {
|
|
8
|
-
constructor(
|
|
58
|
+
constructor(
|
|
59
|
+
private readonly localCommandRunner: LocalCommandRunner,
|
|
60
|
+
private readonly sleeper: Sleeper,
|
|
61
|
+
private readonly submitPushOutAttemptLimit: number = DEFAULT_SUBMIT_PUSH_OUT_ATTEMPT_LIMIT,
|
|
62
|
+
private readonly submitPushOutWaitMilliseconds: number = DEFAULT_SUBMIT_PUSH_OUT_WAIT_MILLISECONDS,
|
|
63
|
+
) {}
|
|
9
64
|
|
|
10
65
|
sendSelfCheckNotification = async (
|
|
11
66
|
sessionName: string,
|
|
@@ -27,6 +82,11 @@ export class TmuxSilentSessionNotificationRepository implements SilentSessionNot
|
|
|
27
82
|
}`,
|
|
28
83
|
);
|
|
29
84
|
}
|
|
85
|
+
await this.submitInputBox(sessionName);
|
|
86
|
+
await this.pushOutUnsubmittedMessage(sessionName, singleLineMessage);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
private submitInputBox = async (sessionName: string): Promise<void> => {
|
|
30
90
|
const submitResult = await this.localCommandRunner.runCommand('tmux', [
|
|
31
91
|
'send-keys',
|
|
32
92
|
'-t',
|
|
@@ -41,4 +101,58 @@ export class TmuxSilentSessionNotificationRepository implements SilentSessionNot
|
|
|
41
101
|
);
|
|
42
102
|
}
|
|
43
103
|
};
|
|
104
|
+
|
|
105
|
+
private pushOutUnsubmittedMessage = async (
|
|
106
|
+
sessionName: string,
|
|
107
|
+
singleLineMessage: string,
|
|
108
|
+
): Promise<void> => {
|
|
109
|
+
for (
|
|
110
|
+
let attempt = 0;
|
|
111
|
+
attempt < this.submitPushOutAttemptLimit;
|
|
112
|
+
attempt += 1
|
|
113
|
+
) {
|
|
114
|
+
await this.sleeper.sleep(this.submitPushOutWaitMilliseconds);
|
|
115
|
+
const inputBoxState = await this.readInputBoxState(
|
|
116
|
+
sessionName,
|
|
117
|
+
singleLineMessage,
|
|
118
|
+
);
|
|
119
|
+
if (inputBoxState === 'clearedMessage') {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (inputBoxState === 'unreadable') {
|
|
123
|
+
console.log(
|
|
124
|
+
`Could not read back the input box of tmux session "${sessionName}"; leaving the notification as sent without pushing it out.`,
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await this.submitInputBox(sessionName);
|
|
129
|
+
}
|
|
130
|
+
console.log(
|
|
131
|
+
`Notification to tmux session "${sessionName}" was still held in the input box after ${this.submitPushOutAttemptLimit} push-out attempt(s).`,
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
private readInputBoxState = async (
|
|
136
|
+
sessionName: string,
|
|
137
|
+
singleLineMessage: string,
|
|
138
|
+
): Promise<InputBoxState> => {
|
|
139
|
+
const captureResult = await this.localCommandRunner.runCommand('tmux', [
|
|
140
|
+
'capture-pane',
|
|
141
|
+
'-p',
|
|
142
|
+
'-t',
|
|
143
|
+
sessionName,
|
|
144
|
+
]);
|
|
145
|
+
if (captureResult.exitCode !== 0) {
|
|
146
|
+
console.log(
|
|
147
|
+
`Failed to capture the pane of tmux session "${sessionName}": exit code ${captureResult.exitCode}${
|
|
148
|
+
captureResult.stderr ? `: ${captureResult.stderr}` : ''
|
|
149
|
+
}`,
|
|
150
|
+
);
|
|
151
|
+
return 'unreadable';
|
|
152
|
+
}
|
|
153
|
+
return resolveInputBoxStateFromPane(
|
|
154
|
+
captureResult.stdout,
|
|
155
|
+
singleLineMessage,
|
|
156
|
+
);
|
|
157
|
+
};
|
|
44
158
|
}
|
|
@@ -47,6 +47,7 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
47
47
|
[candidate('busy', snapshot({})), candidate('idle', snapshot({}))],
|
|
48
48
|
[session('busy', 'session-a')],
|
|
49
49
|
NOW,
|
|
50
|
+
() => 0.5,
|
|
50
51
|
);
|
|
51
52
|
|
|
52
53
|
expect(result.selected?.name).toBe('idle');
|
|
@@ -63,12 +64,13 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
63
64
|
],
|
|
64
65
|
[session('far', 'session-a'), session('soon', 'session-b')],
|
|
65
66
|
NOW,
|
|
67
|
+
() => 0.5,
|
|
66
68
|
);
|
|
67
69
|
|
|
68
70
|
expect(result.selected?.name).toBe('soon');
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
it('
|
|
73
|
+
it('falls back to fewer live sessions when occupancy exactly cancels the urgency gap', () => {
|
|
72
74
|
const result = useCase.run(
|
|
73
75
|
[
|
|
74
76
|
candidate('soonButBusy', snapshot({ sevenDayReset: NOW + 2 * DAY })),
|
|
@@ -79,6 +81,7 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
79
81
|
session('soonButBusy', 'session-b'),
|
|
80
82
|
],
|
|
81
83
|
NOW,
|
|
84
|
+
throwingRandom,
|
|
82
85
|
);
|
|
83
86
|
|
|
84
87
|
expect(result.selected?.name).toBe('farButIdle');
|
|
@@ -114,6 +117,7 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
114
117
|
session('twoSessions', 'session-c'),
|
|
115
118
|
],
|
|
116
119
|
NOW,
|
|
120
|
+
() => 0.5,
|
|
117
121
|
);
|
|
118
122
|
|
|
119
123
|
expect(result.selected?.name).toBe('oneSession');
|
|
@@ -136,6 +140,7 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
136
140
|
session('fresh', 'session-fresh'),
|
|
137
141
|
],
|
|
138
142
|
NOW,
|
|
143
|
+
() => 0.99,
|
|
139
144
|
);
|
|
140
145
|
|
|
141
146
|
expect(result.selected?.name).toBe('fresh');
|
|
@@ -231,15 +236,18 @@ const withSelectionWeight = (
|
|
|
231
236
|
describe('LiveSessionOauthTokenSelectUseCase selectionWeight', () => {
|
|
232
237
|
const useCase = new LiveSessionOauthTokenSelectUseCase();
|
|
233
238
|
|
|
234
|
-
it('keeps the deterministic
|
|
239
|
+
it('keeps the deterministic selection and never consults random when the urgency weights are identical', () => {
|
|
235
240
|
const result = useCase.run(
|
|
236
|
-
[
|
|
237
|
-
|
|
241
|
+
[
|
|
242
|
+
candidate('firstOfEqualPair', snapshot({})),
|
|
243
|
+
candidate('secondOfEqualPair', snapshot({})),
|
|
244
|
+
],
|
|
245
|
+
[],
|
|
238
246
|
NOW,
|
|
239
247
|
throwingRandom,
|
|
240
248
|
);
|
|
241
249
|
|
|
242
|
-
expect(result.selected?.name).toBe('
|
|
250
|
+
expect(result.selected?.name).toBe('firstOfEqualPair');
|
|
243
251
|
});
|
|
244
252
|
|
|
245
253
|
it('selects a sole eligible low-weight token without consulting random (no starvation)', () => {
|
|
@@ -283,3 +291,67 @@ describe('LiveSessionOauthTokenSelectUseCase selectionWeight', () => {
|
|
|
283
291
|
expect(Math.abs(light / count - 1 / 3)).toBeLessThan(0.02);
|
|
284
292
|
});
|
|
285
293
|
});
|
|
294
|
+
|
|
295
|
+
describe('LiveSessionOauthTokenSelectUseCase seven day urgency weighting', () => {
|
|
296
|
+
const useCase = new LiveSessionOauthTokenSelectUseCase();
|
|
297
|
+
|
|
298
|
+
it('prefers the token whose seven day window resets soonest with capacity left over an idle token with a distant reset', () => {
|
|
299
|
+
const soonResetting = candidate(
|
|
300
|
+
'soon-resetting',
|
|
301
|
+
snapshot({
|
|
302
|
+
sevenDayUtilization: 0.5,
|
|
303
|
+
sevenDayReset: NOW + 8 * HOUR,
|
|
304
|
+
}),
|
|
305
|
+
);
|
|
306
|
+
const distantReset = candidate(
|
|
307
|
+
'distant-reset',
|
|
308
|
+
snapshot({
|
|
309
|
+
sevenDayUtilization: 0,
|
|
310
|
+
sevenDayReset: NOW + 160 * HOUR,
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const result = useCase.run(
|
|
315
|
+
[soonResetting, distantReset],
|
|
316
|
+
[
|
|
317
|
+
session('soon-resetting', 'session-a'),
|
|
318
|
+
session('soon-resetting', 'session-b'),
|
|
319
|
+
session('soon-resetting', 'session-c'),
|
|
320
|
+
],
|
|
321
|
+
NOW,
|
|
322
|
+
() => 0.5,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
expect(result.selected?.name).toBe('soon-resetting');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('leaves the token with a distant reset selectable when the random draw falls in its share', () => {
|
|
329
|
+
const soonResetting = candidate(
|
|
330
|
+
'soon-resetting',
|
|
331
|
+
snapshot({
|
|
332
|
+
sevenDayUtilization: 0.5,
|
|
333
|
+
sevenDayReset: NOW + 8 * HOUR,
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
const distantReset = candidate(
|
|
337
|
+
'distant-reset',
|
|
338
|
+
snapshot({
|
|
339
|
+
sevenDayUtilization: 0,
|
|
340
|
+
sevenDayReset: NOW + 160 * HOUR,
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const result = useCase.run(
|
|
345
|
+
[soonResetting, distantReset],
|
|
346
|
+
[
|
|
347
|
+
session('soon-resetting', 'session-a'),
|
|
348
|
+
session('soon-resetting', 'session-b'),
|
|
349
|
+
session('soon-resetting', 'session-c'),
|
|
350
|
+
],
|
|
351
|
+
NOW,
|
|
352
|
+
() => 0.9,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
expect(result.selected?.name).toBe('distant-reset');
|
|
356
|
+
});
|
|
357
|
+
});
|
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
OauthTokenSelectUseCase,
|
|
5
5
|
SelectionRandom,
|
|
6
6
|
selectWeightedCandidate,
|
|
7
|
+
selectionWeightOf,
|
|
8
|
+
sevenDayUrgencyFactor,
|
|
7
9
|
} from './OauthTokenSelectUseCase';
|
|
8
10
|
|
|
9
11
|
export type LiveSessionOauthTokenCandidateMetrics = {
|
|
@@ -72,7 +74,14 @@ export class LiveSessionOauthTokenSelectUseCase {
|
|
|
72
74
|
|
|
73
75
|
const selected = selectWeightedCandidate(
|
|
74
76
|
eligible,
|
|
75
|
-
(entry) =>
|
|
77
|
+
(entry) =>
|
|
78
|
+
(selectionWeightOf(entry.candidate) *
|
|
79
|
+
sevenDayUrgencyFactor(
|
|
80
|
+
entry.metric.sevenDayFreeRatio,
|
|
81
|
+
entry.metric.sevenDayEndEpoch,
|
|
82
|
+
nowEpochSeconds,
|
|
83
|
+
)) /
|
|
84
|
+
(1 + entry.metric.liveSessionCount),
|
|
76
85
|
deterministicBest,
|
|
77
86
|
random,
|
|
78
87
|
);
|
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
OauthTokenCandidate,
|
|
3
3
|
OauthTokenSelectUseCase,
|
|
4
4
|
OauthTokenWindowSnapshot,
|
|
5
|
+
SEVEN_DAY_WINDOW_HOURS,
|
|
6
|
+
sevenDayUrgencyFactor,
|
|
5
7
|
} from './OauthTokenSelectUseCase';
|
|
6
8
|
|
|
7
9
|
const NOW = 1_000_000;
|
|
@@ -53,6 +55,7 @@ describe('OauthTokenSelectUseCase', () => {
|
|
|
53
55
|
),
|
|
54
56
|
],
|
|
55
57
|
NOW,
|
|
58
|
+
() => 0.5,
|
|
56
59
|
);
|
|
57
60
|
|
|
58
61
|
expect(result.selected?.name).toBe('soon');
|
|
@@ -176,6 +179,7 @@ describe('OauthTokenSelectUseCase', () => {
|
|
|
176
179
|
),
|
|
177
180
|
],
|
|
178
181
|
NOW,
|
|
182
|
+
() => 0.5,
|
|
179
183
|
);
|
|
180
184
|
|
|
181
185
|
expect(result.selected?.name).toBe('activeSoon');
|
|
@@ -291,15 +295,15 @@ const withSelectionWeight = (
|
|
|
291
295
|
describe('OauthTokenSelectUseCase selectionWeight', () => {
|
|
292
296
|
const useCase = new OauthTokenSelectUseCase();
|
|
293
297
|
|
|
294
|
-
it('keeps the deterministic
|
|
298
|
+
it('keeps the deterministic selection and never consults random when the urgency weights are identical', () => {
|
|
295
299
|
const result = useCase.run(
|
|
296
300
|
[
|
|
297
301
|
candidate(
|
|
298
|
-
'
|
|
299
|
-
snapshot({ sevenDayUtilization: 0.1, sevenDayReset: NOW +
|
|
302
|
+
'firstOfEqualPair',
|
|
303
|
+
snapshot({ sevenDayUtilization: 0.1, sevenDayReset: NOW + 2 * DAY }),
|
|
300
304
|
),
|
|
301
305
|
candidate(
|
|
302
|
-
'
|
|
306
|
+
'secondOfEqualPair',
|
|
303
307
|
snapshot({ sevenDayUtilization: 0.1, sevenDayReset: NOW + 2 * DAY }),
|
|
304
308
|
),
|
|
305
309
|
],
|
|
@@ -307,23 +311,26 @@ describe('OauthTokenSelectUseCase selectionWeight', () => {
|
|
|
307
311
|
throwingRandom,
|
|
308
312
|
);
|
|
309
313
|
|
|
310
|
-
expect(result.selected?.name).toBe('
|
|
314
|
+
expect(result.selected?.name).toBe('firstOfEqualPair');
|
|
311
315
|
});
|
|
312
316
|
|
|
313
317
|
it('treats an absent selectionWeight the same as weight 1 (uniform, deterministic)', () => {
|
|
314
318
|
const result = useCase.run(
|
|
315
319
|
[
|
|
316
320
|
withSelectionWeight(
|
|
317
|
-
candidate(
|
|
321
|
+
candidate(
|
|
322
|
+
'explicitWeightOne',
|
|
323
|
+
snapshot({ sevenDayReset: NOW + 2 * DAY }),
|
|
324
|
+
),
|
|
318
325
|
1,
|
|
319
326
|
),
|
|
320
|
-
candidate('
|
|
327
|
+
candidate('absentWeight', snapshot({ sevenDayReset: NOW + 2 * DAY })),
|
|
321
328
|
],
|
|
322
329
|
NOW,
|
|
323
330
|
throwingRandom,
|
|
324
331
|
);
|
|
325
332
|
|
|
326
|
-
expect(result.selected?.name).toBe('
|
|
333
|
+
expect(result.selected?.name).toBe('explicitWeightOne');
|
|
327
334
|
});
|
|
328
335
|
|
|
329
336
|
it('selects a sole eligible low-weight token without consulting random (no starvation)', () => {
|
|
@@ -431,3 +438,27 @@ describe('OauthTokenSelectUseCase selectionWeight', () => {
|
|
|
431
438
|
expect(result.selected?.name).toBe('soon');
|
|
432
439
|
});
|
|
433
440
|
});
|
|
441
|
+
|
|
442
|
+
describe('sevenDayUrgencyFactor', () => {
|
|
443
|
+
const now = 1_000_000;
|
|
444
|
+
|
|
445
|
+
it('grows as the seven day window gets closer to its reset', () => {
|
|
446
|
+
const nearReset = sevenDayUrgencyFactor(0.5, now + 8 * 3600, now);
|
|
447
|
+
const farReset = sevenDayUrgencyFactor(0.5, now + 160 * 3600, now);
|
|
448
|
+
|
|
449
|
+
expect(nearReset).toBeGreaterThan(farReset);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('scales with the free ratio of the seven day window', () => {
|
|
453
|
+
const halfFree = sevenDayUrgencyFactor(0.5, now + 24 * 3600, now);
|
|
454
|
+
const fullyFree = sevenDayUrgencyFactor(1, now + 24 * 3600, now);
|
|
455
|
+
|
|
456
|
+
expect(fullyFree).toBeCloseTo(halfFree * 2);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('clips the remaining hours at one hour so an imminent reset does not produce an unbounded factor', () => {
|
|
460
|
+
const almostReset = sevenDayUrgencyFactor(1, now + 60, now);
|
|
461
|
+
|
|
462
|
+
expect(almostReset).toBe(SEVEN_DAY_WINDOW_HOURS);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
@@ -44,18 +44,32 @@ const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
|
|
|
44
44
|
export const FIVE_HOUR_MIN_FREE_RATIO = 0.25;
|
|
45
45
|
export const SEVEN_DAY_MIN_FREE_RATIO = 0.03;
|
|
46
46
|
|
|
47
|
+
export const SEVEN_DAY_WINDOW_HOURS = 168;
|
|
48
|
+
export const MIN_HOURS_TO_RESET = 1;
|
|
49
|
+
const SECONDS_PER_HOUR = 3600;
|
|
50
|
+
|
|
51
|
+
export const sevenDayUrgencyFactor = (
|
|
52
|
+
sevenDayFreeRatio: number,
|
|
53
|
+
sevenDayEndEpoch: number,
|
|
54
|
+
nowEpochSeconds: number,
|
|
55
|
+
): number => {
|
|
56
|
+
const hoursToReset = Math.max(
|
|
57
|
+
(sevenDayEndEpoch - nowEpochSeconds) / SECONDS_PER_HOUR,
|
|
58
|
+
MIN_HOURS_TO_RESET,
|
|
59
|
+
);
|
|
60
|
+
return sevenDayFreeRatio * (SEVEN_DAY_WINDOW_HOURS / hoursToReset);
|
|
61
|
+
};
|
|
62
|
+
|
|
47
63
|
export const selectWeightedCandidate = <Entry>(
|
|
48
64
|
eligible: Entry[],
|
|
49
|
-
|
|
65
|
+
weightOf: (entry: Entry) => number,
|
|
50
66
|
deterministicBest: Entry,
|
|
51
67
|
random: SelectionRandom,
|
|
52
68
|
): Entry => {
|
|
53
69
|
if (eligible.length <= 1) {
|
|
54
70
|
return deterministicBest;
|
|
55
71
|
}
|
|
56
|
-
const weights = eligible.map((entry) =>
|
|
57
|
-
selectionWeightOf(candidateOf(entry)),
|
|
58
|
-
);
|
|
72
|
+
const weights = eligible.map((entry) => Math.max(weightOf(entry), 0));
|
|
59
73
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
60
74
|
const uniform = weights.every((weight) => weight === weights[0]);
|
|
61
75
|
if (uniform || totalWeight <= 0) {
|
|
@@ -97,7 +111,13 @@ export class OauthTokenSelectUseCase {
|
|
|
97
111
|
|
|
98
112
|
const selected = selectWeightedCandidate(
|
|
99
113
|
eligible,
|
|
100
|
-
(entry) =>
|
|
114
|
+
(entry) =>
|
|
115
|
+
selectionWeightOf(entry.candidate) *
|
|
116
|
+
sevenDayUrgencyFactor(
|
|
117
|
+
entry.metric.sevenDayFreeRatio,
|
|
118
|
+
entry.metric.sevenDayEndEpoch,
|
|
119
|
+
nowEpochSeconds,
|
|
120
|
+
),
|
|
101
121
|
deterministicBest,
|
|
102
122
|
random,
|
|
103
123
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClaudeLiveSessionRepository } from '../../../domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository';
|
|
2
2
|
import { LiveSessionOauthTokenSelectUseCase } from '../../../domain/usecases/LiveSessionOauthTokenSelectUseCase';
|
|
3
|
+
import { SelectionRandom } from '../../../domain/usecases/OauthTokenSelectUseCase';
|
|
3
4
|
export type LiveSessionOauthTokenSelectHandlerInput = {
|
|
4
5
|
tokenListJsonPath: string | null;
|
|
5
6
|
cacheDirectory: string | null;
|
|
@@ -13,7 +14,8 @@ export type LiveSessionOauthTokenSelectHandlerOutput = {
|
|
|
13
14
|
export declare class LiveSessionOauthTokenSelectHandler {
|
|
14
15
|
private readonly useCase;
|
|
15
16
|
private readonly liveSessionRepository;
|
|
16
|
-
|
|
17
|
+
private readonly random;
|
|
18
|
+
constructor(useCase?: LiveSessionOauthTokenSelectUseCase, liveSessionRepository?: ClaudeLiveSessionRepository, random?: SelectionRandom);
|
|
17
19
|
handle: (input: LiveSessionOauthTokenSelectHandlerInput) => LiveSessionOauthTokenSelectHandlerOutput;
|
|
18
20
|
private formatDiagnostics;
|
|
19
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveSessionOauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yEAAyE,CAAC;AACtH,OAAO,EAEL,kCAAkC,EACnC,MAAM,6DAA6D,CAAC;
|
|
1
|
+
{"version":3,"file":"LiveSessionOauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yEAAyE,CAAC;AACtH,OAAO,EAEL,kCAAkC,EACnC,MAAM,6DAA6D,CAAC;AACrE,OAAO,EAKL,eAAe,EAChB,MAAM,kDAAkD,CAAC;AAS1D,MAAM,MAAM,uCAAuC,GAAG;IACpD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,wCAAwC,GAAG;IACrD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,OAAO,GAAE,kCAA6E,EACtF,qBAAqB,GAAE,2BAAmE,EAC1F,MAAM,GAAE,eAA6B;IAGxD,MAAM,GACJ,OAAO,uCAAuC,KAC7C,wCAAwC,CAmEzC;IAEF,OAAO,CAAC,iBAAiB,CAyBvB;CACH"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OauthTokenSelectUseCase } from '../../../domain/usecases/OauthTokenSelectUseCase';
|
|
1
|
+
import { OauthTokenSelectUseCase, SelectionRandom } from '../../../domain/usecases/OauthTokenSelectUseCase';
|
|
2
2
|
export type OauthTokenSelectHandlerInput = {
|
|
3
3
|
tokenListJsonPath: string | null;
|
|
4
4
|
cacheDirectory: string | null;
|
|
@@ -13,7 +13,8 @@ export declare const resolveTokenListJsonPath: (explicitPath: string | null) =>
|
|
|
13
13
|
export declare const resolveCacheDirectory: (explicitDirectory: string | null) => string;
|
|
14
14
|
export declare class OauthTokenSelectHandler {
|
|
15
15
|
private readonly useCase;
|
|
16
|
-
|
|
16
|
+
private readonly random;
|
|
17
|
+
constructor(useCase?: OauthTokenSelectUseCase, random?: SelectionRandom);
|
|
17
18
|
handle: (input: OauthTokenSelectHandlerInput) => OauthTokenSelectHandlerOutput;
|
|
18
19
|
private formatDiagnostics;
|
|
19
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,uBAAuB,
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,uBAAuB,EACvB,eAAe,EAChB,MAAM,kDAAkD,CAAC;AAQ1D,MAAM,MAAM,4BAA4B,GAAG;IACzC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,GAAG,IAAI,KAC1B,MAAM,GAAG,IASX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,GAAG,IAAI,KAC/B,MASF,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,GAAE,uBAAuD,EAChE,MAAM,GAAE,eAA6B;IAGxD,MAAM,GACJ,OAAO,4BAA4B,KAClC,6BAA6B,CAgE9B;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;CACH"}
|
|
@@ -23,6 +23,7 @@ export type NotifySilentTmuxSessionsParams = {
|
|
|
23
23
|
hubTaskStatusCacheStateFilePath: string | null;
|
|
24
24
|
hubTaskStatusCacheTtlSeconds: number;
|
|
25
25
|
messageTemplates: SilentSessionMessageTemplates;
|
|
26
|
+
submitPushOutWaitMilliseconds?: number;
|
|
26
27
|
now: Date;
|
|
27
28
|
};
|
|
28
29
|
export declare const notifySilentTmuxSessions: (params: NotifySilentTmuxSessionsParams) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAQtB,MAAM,0DAA0D,CAAC;
|
|
1
|
+
{"version":3,"file":"notifySilentTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAGpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAEL,qBAAqB,EAQtB,MAAM,0DAA0D,CAAC;AAoBlE,OAAO,EAEL,6BAA6B,EAC9B,MAAM,6DAA6D,CAAC;AAKrE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,0BAA0B,EAAE,MAAM,CAAC;IACnC,+BAA+B,EAAE,MAAM,CAAC;IACxC,8BAA8B,EAAE,MAAM,CAAC;IACvC,+BAA+B,EAAE,MAAM,CAAC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC,EAAE,MAAM,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACpD,+BAA+B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,4BAA4B,EAAE,MAAM,CAAC;IACrC,gBAAgB,EAAE,6BAA6B,CAAC;IAChD,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAuCF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,8BAA8B,KACrC,OAAO,CAAC,IAAI,CAoFd,CAAC;AAEF,eAAO,MAAM,0CAA0C;;;;;;;;CAS7C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resetDegeneratedTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AACpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AAgBxG,MAAM,MAAM,kCAAkC,GAAG;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,QAAQ,kCAAkC,KACzC,OAAO,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"resetDegeneratedTmuxSessions.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AACpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kEAAkE,CAAC;AAgBxG,MAAM,MAAM,kCAAkC,GAAG;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,QAAQ,kCAAkC,KACzC,OAAO,CAAC,IAAI,CAqCd,CAAC;AAEF,eAAO,MAAM,8CAA8C;;;;CAIjD,CAAC"}
|
|
@@ -6,6 +6,7 @@ export declare class FileSystemSubAgentLivenessResolver implements SubAgentLiven
|
|
|
6
6
|
sessionName: string;
|
|
7
7
|
mainTranscriptPath: string | null;
|
|
8
8
|
}) => Promise<Set<string> | null>;
|
|
9
|
+
private hasReadableRuntimeRootDirectory;
|
|
9
10
|
private resolveRunningSubAgentsFilePath;
|
|
10
11
|
}
|
|
11
12
|
//# sourceMappingURL=FileSystemSubAgentLivenessResolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSystemSubAgentLivenessResolver.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mEAAmE,CAAC;
|
|
1
|
+
{"version":3,"file":"FileSystemSubAgentLivenessResolver.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mEAAmE,CAAC;AAU7G,qBAAa,kCAAmC,YAAW,wBAAwB;IACrE,OAAO,CAAC,QAAQ,CAAC,oBAAoB;gBAApB,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAEhE,sBAAsB,GAAU,QAAQ;QACtC,WAAW,EAAE,MAAM,CAAC;QACpB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,KAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAwB7B;IAEF,OAAO,CAAC,+BAA+B,CASrC;IAEF,OAAO,CAAC,+BAA+B,CAoBrC;CACH"}
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { SilentSessionNotificationRepository } from '../../domain/usecases/adapter-interfaces/SilentSessionNotificationRepository';
|
|
2
2
|
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
import { Sleeper } from '../../domain/usecases/adapter-interfaces/Sleeper';
|
|
4
|
+
export declare const DEFAULT_SUBMIT_PUSH_OUT_ATTEMPT_LIMIT = 3;
|
|
5
|
+
export declare const DEFAULT_SUBMIT_PUSH_OUT_WAIT_MILLISECONDS = 2500;
|
|
6
|
+
type InputBoxState = 'holdsMessage' | 'clearedMessage' | 'unreadable';
|
|
7
|
+
export declare const extractTmuxInputBoxContent: (paneText: string) => string | null;
|
|
8
|
+
export declare const resolveInputBoxStateFromPane: (paneText: string, singleLineMessage: string) => InputBoxState;
|
|
3
9
|
export declare class TmuxSilentSessionNotificationRepository implements SilentSessionNotificationRepository {
|
|
4
10
|
private readonly localCommandRunner;
|
|
5
|
-
|
|
11
|
+
private readonly sleeper;
|
|
12
|
+
private readonly submitPushOutAttemptLimit;
|
|
13
|
+
private readonly submitPushOutWaitMilliseconds;
|
|
14
|
+
constructor(localCommandRunner: LocalCommandRunner, sleeper: Sleeper, submitPushOutAttemptLimit?: number, submitPushOutWaitMilliseconds?: number);
|
|
6
15
|
sendSelfCheckNotification: (sessionName: string, message: string) => Promise<void>;
|
|
16
|
+
private submitInputBox;
|
|
17
|
+
private pushOutUnsubmittedMessage;
|
|
18
|
+
private readInputBoxState;
|
|
7
19
|
}
|
|
20
|
+
export {};
|
|
8
21
|
//# sourceMappingURL=TmuxSilentSessionNotificationRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TmuxSilentSessionNotificationRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,8EAA8E,CAAC;AACnI,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;
|
|
1
|
+
{"version":3,"file":"TmuxSilentSessionNotificationRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,8EAA8E,CAAC;AACnI,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,MAAM,kDAAkD,CAAC;AAK3E,eAAO,MAAM,qCAAqC,IAAI,CAAC;AACvD,eAAO,MAAM,yCAAyC,OAAO,CAAC;AAM9D,KAAK,aAAa,GAAG,cAAc,GAAG,gBAAgB,GAAG,YAAY,CAAC;AAItE,eAAO,MAAM,0BAA0B,GAAI,UAAU,MAAM,KAAG,MAAM,GAAG,IAgBtE,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,UAAU,MAAM,EAChB,mBAAmB,MAAM,KACxB,aAeF,CAAC;AAEF,qBAAa,uCAAwC,YAAW,mCAAmC;IAE/F,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAC1C,OAAO,CAAC,QAAQ,CAAC,6BAA6B;gBAH7B,kBAAkB,EAAE,kBAAkB,EACtC,OAAO,EAAE,OAAO,EAChB,yBAAyB,GAAE,MAA8C,EACzE,6BAA6B,GAAE,MAAkD;IAGpG,yBAAyB,GACvB,aAAa,MAAM,EACnB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAAC,CAmBd;IAEF,OAAO,CAAC,cAAc,CAcpB;IAEF,OAAO,CAAC,yBAAyB,CA4B/B;IAEF,OAAO,CAAC,iBAAiB,CAsBvB;CACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,
|
|
1
|
+
{"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EAIhB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,qCAAqC,GAAG;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,qCAAqC,EAAE,CAAC;CAClD,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBAAtB,sBAAsB,GAAE,uBAAuD;IAGlG,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,cAAc,iBAAiB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,KACpC,iCAAiC,CAsDlC;IAEF,OAAO,CAAC,SAAS,CAUf;IAEF,OAAO,CAAC,uBAAuB,CAe7B;CACH"}
|
|
@@ -30,7 +30,10 @@ export type OauthTokenSelectResult = {
|
|
|
30
30
|
};
|
|
31
31
|
export declare const FIVE_HOUR_MIN_FREE_RATIO = 0.25;
|
|
32
32
|
export declare const SEVEN_DAY_MIN_FREE_RATIO = 0.03;
|
|
33
|
-
export declare const
|
|
33
|
+
export declare const SEVEN_DAY_WINDOW_HOURS = 168;
|
|
34
|
+
export declare const MIN_HOURS_TO_RESET = 1;
|
|
35
|
+
export declare const sevenDayUrgencyFactor: (sevenDayFreeRatio: number, sevenDayEndEpoch: number, nowEpochSeconds: number) => number;
|
|
36
|
+
export declare const selectWeightedCandidate: <Entry>(eligible: Entry[], weightOf: (entry: Entry) => number, deterministicBest: Entry, random: SelectionRandom) => Entry;
|
|
34
37
|
export declare class OauthTokenSelectUseCase {
|
|
35
38
|
run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number, random?: SelectionRandom) => OauthTokenSelectResult;
|
|
36
39
|
private evaluate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC;AAE3C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,iBAAiB,GAAI,WAAW,mBAAmB,KAAG,MAGlE,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAKF,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC;AAE3C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,iBAAiB,GAAI,WAAW,mBAAmB,KAAG,MAGlE,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAKF,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,EACzB,kBAAkB,MAAM,EACxB,iBAAiB,MAAM,KACtB,MAMF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,UAAU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,EAClC,mBAAmB,KAAK,EACxB,QAAQ,eAAe,KACtB,KAkBF,CAAC;AAEF,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,KACpC,sBAAsB,CAiCvB;IAEF,OAAO,CAAC,QAAQ,CAiCd;IAEF,OAAO,CAAC,eAAe,CAuBrB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,gBAAgB,CActB;IAEF,OAAO,CAAC,aAAa,CAGwC;IAE7D,OAAO,CAAC,wBAAwB,CAG9B;IAEF,OAAO,CAAC,SAAS,CAAsD;CACxE"}
|