@cortexkit/anthropic-auth-core 1.15.1 → 1.17.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/dist/accounts.d.ts +48 -3
- package/dist/accounts.js +506 -54
- package/dist/cachekeep-registry.d.ts +21 -0
- package/dist/cachekeep-registry.js +124 -0
- package/dist/cachekeep.d.ts +16 -1
- package/dist/cachekeep.js +93 -14
- package/dist/commands/account.d.ts +1 -0
- package/dist/commands/account.js +7 -1
- package/dist/dump.d.ts +19 -0
- package/dist/dump.js +162 -10
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/oauth-profile.d.ts +11 -0
- package/dist/oauth-profile.js +48 -0
- package/dist/quota-headers.d.ts +4 -0
- package/dist/quota-headers.js +66 -0
- package/dist/quota-manager.d.ts +7 -9
- package/dist/quota-manager.js +72 -24
- package/dist/quotas.d.ts +5 -2
- package/dist/quotas.js +35 -5
- package/dist/relay.d.ts +7 -0
- package/dist/relay.js +37 -8
- package/dist/routing.d.ts +3 -1
- package/dist/routing.js +20 -2
- package/dist/sticky-routing.d.ts +86 -0
- package/dist/sticky-routing.js +450 -0
- package/dist/token-fingerprint.d.ts +5 -0
- package/dist/token-fingerprint.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const CACHE_KEEP_REGISTRY_LEASE_MS: number;
|
|
2
|
+
export type CacheKeepTrackedSession = {
|
|
3
|
+
id: string;
|
|
4
|
+
cacheExpiresAt: number;
|
|
5
|
+
nextPrewarmAt: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function getDefaultCacheKeepRegistryDirectory(scope: 'opencode' | 'pi'): string;
|
|
8
|
+
export declare class CacheKeepSessionRegistry {
|
|
9
|
+
private readonly options;
|
|
10
|
+
private readonly filePath;
|
|
11
|
+
private writeChain;
|
|
12
|
+
constructor(options: {
|
|
13
|
+
directory: string;
|
|
14
|
+
now?: () => number;
|
|
15
|
+
leaseMs?: number;
|
|
16
|
+
instanceId?: string;
|
|
17
|
+
});
|
|
18
|
+
publish(sessions: readonly CacheKeepTrackedSession[]): Promise<void>;
|
|
19
|
+
list(localSessions?: readonly CacheKeepTrackedSession[]): Promise<CacheKeepTrackedSession[]>;
|
|
20
|
+
dispose(): Promise<void>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readdir, readFile, rename, rm, writeFile, } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
export const CACHE_KEEP_REGISTRY_LEASE_MS = 3 * 60_000;
|
|
6
|
+
function normalizeSession(value) {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
8
|
+
return;
|
|
9
|
+
const record = value;
|
|
10
|
+
if (typeof record.id !== 'string' ||
|
|
11
|
+
record.id.length === 0 ||
|
|
12
|
+
typeof record.cacheExpiresAt !== 'number' ||
|
|
13
|
+
!Number.isFinite(record.cacheExpiresAt) ||
|
|
14
|
+
typeof record.nextPrewarmAt !== 'number' ||
|
|
15
|
+
!Number.isFinite(record.nextPrewarmAt)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
id: record.id,
|
|
20
|
+
cacheExpiresAt: record.cacheExpiresAt,
|
|
21
|
+
nextPrewarmAt: record.nextPrewarmAt,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function normalizeRecord(value) {
|
|
25
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
26
|
+
return;
|
|
27
|
+
const record = value;
|
|
28
|
+
if (record.version !== 1 ||
|
|
29
|
+
typeof record.updatedAt !== 'number' ||
|
|
30
|
+
!Number.isFinite(record.updatedAt) ||
|
|
31
|
+
!Array.isArray(record.sessions)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
version: 1,
|
|
36
|
+
updatedAt: record.updatedAt,
|
|
37
|
+
sessions: record.sessions.flatMap((session) => {
|
|
38
|
+
const normalized = normalizeSession(session);
|
|
39
|
+
return normalized ? [normalized] : [];
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function getDefaultCacheKeepRegistryDirectory(scope) {
|
|
44
|
+
return join(tmpdir(), 'opencode-anthropic-auth', 'cachekeep-sessions', scope);
|
|
45
|
+
}
|
|
46
|
+
export class CacheKeepSessionRegistry {
|
|
47
|
+
options;
|
|
48
|
+
filePath;
|
|
49
|
+
writeChain = Promise.resolve();
|
|
50
|
+
constructor(options) {
|
|
51
|
+
this.options = options;
|
|
52
|
+
const instanceId = options.instanceId ?? `${process.pid}-${randomUUID()}`;
|
|
53
|
+
this.filePath = join(options.directory, `${instanceId}.json`);
|
|
54
|
+
}
|
|
55
|
+
publish(sessions) {
|
|
56
|
+
const snapshot = sessions.map((session) => ({ ...session }));
|
|
57
|
+
this.writeChain = this.writeChain
|
|
58
|
+
.catch(() => { })
|
|
59
|
+
.then(async () => {
|
|
60
|
+
if (snapshot.length === 0) {
|
|
61
|
+
await rm(this.filePath, { force: true });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await mkdir(this.options.directory, { recursive: true, mode: 0o700 });
|
|
65
|
+
const tempPath = `${this.filePath}.${randomUUID()}.tmp`;
|
|
66
|
+
const record = {
|
|
67
|
+
version: 1,
|
|
68
|
+
updatedAt: this.options.now?.() ?? Date.now(),
|
|
69
|
+
sessions: snapshot,
|
|
70
|
+
};
|
|
71
|
+
await writeFile(tempPath, `${JSON.stringify(record)}\n`, {
|
|
72
|
+
mode: 0o600,
|
|
73
|
+
});
|
|
74
|
+
await rename(tempPath, this.filePath);
|
|
75
|
+
});
|
|
76
|
+
return this.writeChain;
|
|
77
|
+
}
|
|
78
|
+
async list(localSessions = []) {
|
|
79
|
+
await this.writeChain.catch(() => { });
|
|
80
|
+
const now = this.options.now?.() ?? Date.now();
|
|
81
|
+
const leaseMs = this.options.leaseMs ?? CACHE_KEEP_REGISTRY_LEASE_MS;
|
|
82
|
+
let names;
|
|
83
|
+
try {
|
|
84
|
+
names = (await readdir(this.options.directory)).filter((name) => name.endsWith('.json'));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
names = [];
|
|
88
|
+
}
|
|
89
|
+
const records = await Promise.all(names.map(async (name) => {
|
|
90
|
+
try {
|
|
91
|
+
const raw = await readFile(join(this.options.directory, name), 'utf8');
|
|
92
|
+
return normalizeRecord(JSON.parse(raw));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
const sessions = new Map();
|
|
99
|
+
for (const record of records) {
|
|
100
|
+
if (!record ||
|
|
101
|
+
now - record.updatedAt > leaseMs ||
|
|
102
|
+
record.updatedAt > now) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
for (const session of record.sessions) {
|
|
106
|
+
const existing = sessions.get(session.id);
|
|
107
|
+
if (!existing || session.cacheExpiresAt > existing.cacheExpiresAt) {
|
|
108
|
+
sessions.set(session.id, session);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const session of localSessions) {
|
|
113
|
+
const existing = sessions.get(session.id);
|
|
114
|
+
if (!existing || session.cacheExpiresAt > existing.cacheExpiresAt) {
|
|
115
|
+
sessions.set(session.id, { ...session });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return [...sessions.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
119
|
+
}
|
|
120
|
+
async dispose() {
|
|
121
|
+
await this.writeChain.catch(() => { });
|
|
122
|
+
await rm(this.filePath, { force: true });
|
|
123
|
+
}
|
|
124
|
+
}
|
package/dist/cachekeep.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AccountStorage } from './accounts.ts';
|
|
2
|
+
import type { CacheKeepTrackedSession } from './cachekeep-registry.ts';
|
|
2
3
|
export declare const CLAUDE_CACHE_KEEP_COMMAND_NAME = "claude-cachekeep";
|
|
3
4
|
export declare const CACHE_KEEP_TTL_MS: number;
|
|
4
5
|
export declare const CACHE_KEEP_PREWARM_LEAD_MS: number;
|
|
@@ -16,6 +17,8 @@ export type CacheKeepCommandAction = {
|
|
|
16
17
|
type: 'status';
|
|
17
18
|
} | {
|
|
18
19
|
type: 'disable';
|
|
20
|
+
} | {
|
|
21
|
+
type: 'always';
|
|
19
22
|
} | {
|
|
20
23
|
type: 'window';
|
|
21
24
|
startHour: number;
|
|
@@ -28,23 +31,29 @@ export type CacheKeepCommandAction = {
|
|
|
28
31
|
};
|
|
29
32
|
export type CacheKeepStatus = {
|
|
30
33
|
enabled: boolean;
|
|
34
|
+
always?: boolean;
|
|
31
35
|
window?: CacheKeepWindow;
|
|
32
36
|
trackedSessions?: number;
|
|
37
|
+
trackedSessionDetails?: CacheKeepTrackedSession[];
|
|
33
38
|
nextPrewarmAt?: number;
|
|
34
39
|
hybridActive?: boolean;
|
|
35
40
|
};
|
|
36
41
|
export declare function parseCacheKeepCommandAction(input: string): CacheKeepCommandAction;
|
|
37
42
|
export declare function getCacheKeepWindow(storage: AccountStorage | null): CacheKeepWindow | undefined;
|
|
43
|
+
export declare function isCacheKeepAlways(storage: AccountStorage | null): boolean;
|
|
38
44
|
export declare function isCacheKeepPersistentlyEnabled(storage: AccountStorage | null): boolean;
|
|
39
45
|
export declare function isCacheKeepHybridActive(storage: AccountStorage | null): boolean;
|
|
40
46
|
export declare function isWithinCacheKeepWindow(window: CacheKeepWindow | undefined, now?: Date): boolean;
|
|
47
|
+
export declare function isCacheKeepActiveNow(storage: AccountStorage | null, now?: Date): boolean;
|
|
41
48
|
export declare function localDayKey(now?: Date): string;
|
|
42
49
|
export declare function buildCacheKeepStatusSummary(status: CacheKeepStatus): string;
|
|
43
50
|
export declare function executeCacheKeepCommand(input: {
|
|
44
51
|
argumentsText: string;
|
|
45
52
|
enabled?: boolean;
|
|
53
|
+
always?: boolean;
|
|
46
54
|
window?: CacheKeepWindow;
|
|
47
55
|
trackedSessions?: number;
|
|
56
|
+
trackedSessionDetails?: CacheKeepTrackedSession[];
|
|
48
57
|
nextPrewarmAt?: number;
|
|
49
58
|
hybridActive?: boolean;
|
|
50
59
|
}): string;
|
|
@@ -86,14 +95,20 @@ export declare class CacheKeepManager {
|
|
|
86
95
|
now?: () => number;
|
|
87
96
|
prewarmTimeoutMs?: number;
|
|
88
97
|
prepareHeaders?: (headers: Headers, target: CacheKeepTarget) => Promise<Headers> | Headers;
|
|
98
|
+
onTrackedSessionsChanged?: (sessions: readonly CacheKeepTrackedSession[]) => Promise<void> | void;
|
|
89
99
|
});
|
|
90
100
|
start(): void;
|
|
91
101
|
stop(): void;
|
|
92
|
-
stats(window?: CacheKeepWindow, now?: number): {
|
|
102
|
+
stats(window?: CacheKeepWindow, now?: number, always?: boolean): {
|
|
93
103
|
trackedSessions: number;
|
|
94
104
|
nextPrewarmAt: number | undefined;
|
|
95
105
|
};
|
|
106
|
+
trackedSessions(): CacheKeepTrackedSession[];
|
|
107
|
+
trackedOAuthRoute(sessionId: string): {
|
|
108
|
+
oauthAccountId?: string;
|
|
109
|
+
} | undefined;
|
|
96
110
|
trackedCount(): number;
|
|
111
|
+
private publishTrackedSessions;
|
|
97
112
|
private totalBodyBytes;
|
|
98
113
|
private evictOldestTarget;
|
|
99
114
|
private pruneTargets;
|
package/dist/cachekeep.js
CHANGED
|
@@ -14,7 +14,7 @@ const STATUS_TITLE = '## Claude Cache Keep Status';
|
|
|
14
14
|
const ENABLED_TITLE = '## Claude Cache Keep Enabled';
|
|
15
15
|
const DISABLED_TITLE = '## Claude Cache Keep Disabled';
|
|
16
16
|
const USAGE_TITLE = '## Claude Cache Keep Usage';
|
|
17
|
-
const USAGE = 'Usage: `/claude-cachekeep`, `/claude-cachekeep off`, or `/claude-cachekeep HH-HH`.';
|
|
17
|
+
const USAGE = 'Usage: `/claude-cachekeep`, `/claude-cachekeep always`, `/claude-cachekeep off`, or `/claude-cachekeep HH-HH`.';
|
|
18
18
|
function padHour(hour) {
|
|
19
19
|
return String(hour).padStart(2, '0');
|
|
20
20
|
}
|
|
@@ -24,6 +24,8 @@ export function parseCacheKeepCommandAction(input) {
|
|
|
24
24
|
return { type: 'status' };
|
|
25
25
|
if (trimmed === 'off')
|
|
26
26
|
return { type: 'disable' };
|
|
27
|
+
if (trimmed === 'always')
|
|
28
|
+
return { type: 'always' };
|
|
27
29
|
if (trimmed === 'subagents on')
|
|
28
30
|
return { type: 'subagents', enabled: true };
|
|
29
31
|
if (trimmed === 'subagents off')
|
|
@@ -58,8 +60,12 @@ export function getCacheKeepWindow(storage) {
|
|
|
58
60
|
}
|
|
59
61
|
return { startHour, endHour };
|
|
60
62
|
}
|
|
63
|
+
export function isCacheKeepAlways(storage) {
|
|
64
|
+
return (storage?.cacheKeep?.enabled === true && storage.cacheKeep.always === true);
|
|
65
|
+
}
|
|
61
66
|
export function isCacheKeepPersistentlyEnabled(storage) {
|
|
62
|
-
return storage?.cacheKeep?.enabled === true &&
|
|
67
|
+
return (storage?.cacheKeep?.enabled === true &&
|
|
68
|
+
(isCacheKeepAlways(storage) || !!getCacheKeepWindow(storage)));
|
|
63
69
|
}
|
|
64
70
|
export function isCacheKeepHybridActive(storage) {
|
|
65
71
|
return (isCacheKeepPersistentlyEnabled(storage) &&
|
|
@@ -75,6 +81,12 @@ export function isWithinCacheKeepWindow(window, now = new Date()) {
|
|
|
75
81
|
}
|
|
76
82
|
return hour >= window.startHour || hour < window.endHour;
|
|
77
83
|
}
|
|
84
|
+
export function isCacheKeepActiveNow(storage, now = new Date()) {
|
|
85
|
+
if (storage?.cacheKeep?.enabled !== true)
|
|
86
|
+
return false;
|
|
87
|
+
return (isCacheKeepAlways(storage) ||
|
|
88
|
+
isWithinCacheKeepWindow(getCacheKeepWindow(storage), now));
|
|
89
|
+
}
|
|
78
90
|
export function localDayKey(now = new Date()) {
|
|
79
91
|
return `${now.getFullYear()}-${padHour(now.getMonth() + 1)}-${padHour(now.getDate())}`;
|
|
80
92
|
}
|
|
@@ -88,13 +100,18 @@ function localWindowKey(window, now = new Date()) {
|
|
|
88
100
|
}
|
|
89
101
|
return localDayKey(now);
|
|
90
102
|
}
|
|
103
|
+
function cacheKeepPeriodKey(storage, window, now = new Date()) {
|
|
104
|
+
return isCacheKeepAlways(storage) ? 'always' : localWindowKey(window, now);
|
|
105
|
+
}
|
|
91
106
|
export function buildCacheKeepStatusSummary(status) {
|
|
92
|
-
const
|
|
93
|
-
?
|
|
94
|
-
:
|
|
107
|
+
const schedule = status.always
|
|
108
|
+
? 'always (while this process is running)'
|
|
109
|
+
: status.window
|
|
110
|
+
? `${padHour(status.window.startHour)}-${padHour(status.window.endHour)}`
|
|
111
|
+
: 'not configured';
|
|
95
112
|
const lines = [
|
|
96
113
|
`Cache keep: ${status.enabled ? 'enabled' : 'disabled'}`,
|
|
97
|
-
`
|
|
114
|
+
`Schedule: ${schedule}`,
|
|
98
115
|
'Mode requirement: `/claude-cache mode hybrid` must be active.',
|
|
99
116
|
];
|
|
100
117
|
if (typeof status.hybridActive === 'boolean') {
|
|
@@ -103,6 +120,12 @@ export function buildCacheKeepStatusSummary(status) {
|
|
|
103
120
|
if (typeof status.trackedSessions === 'number') {
|
|
104
121
|
lines.push(`Tracked sessions: ${status.trackedSessions}`);
|
|
105
122
|
}
|
|
123
|
+
if (status.trackedSessionDetails?.length) {
|
|
124
|
+
lines.push('Sessions:');
|
|
125
|
+
for (const session of status.trackedSessionDetails) {
|
|
126
|
+
lines.push(`- ${session.id}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
106
129
|
if (status.nextPrewarmAt) {
|
|
107
130
|
lines.push(`Next prewarm: ${new Date(status.nextPrewarmAt).toLocaleString()}`);
|
|
108
131
|
}
|
|
@@ -112,8 +135,10 @@ export function executeCacheKeepCommand(input) {
|
|
|
112
135
|
const action = parseCacheKeepCommandAction(input.argumentsText);
|
|
113
136
|
const status = {
|
|
114
137
|
enabled: input.enabled ?? false,
|
|
138
|
+
always: input.always,
|
|
115
139
|
window: input.window,
|
|
116
140
|
trackedSessions: input.trackedSessions,
|
|
141
|
+
trackedSessionDetails: input.trackedSessionDetails,
|
|
117
142
|
nextPrewarmAt: input.nextPrewarmAt,
|
|
118
143
|
hybridActive: input.hybridActive,
|
|
119
144
|
};
|
|
@@ -127,6 +152,18 @@ export function executeCacheKeepCommand(input) {
|
|
|
127
152
|
buildCacheKeepStatusSummary({ ...status, enabled: false }),
|
|
128
153
|
].join('\n');
|
|
129
154
|
}
|
|
155
|
+
if (action.type === 'always') {
|
|
156
|
+
return [
|
|
157
|
+
ENABLED_TITLE,
|
|
158
|
+
'',
|
|
159
|
+
buildCacheKeepStatusSummary({
|
|
160
|
+
...status,
|
|
161
|
+
enabled: true,
|
|
162
|
+
always: true,
|
|
163
|
+
window: undefined,
|
|
164
|
+
}),
|
|
165
|
+
].join('\n');
|
|
166
|
+
}
|
|
130
167
|
if (action.type === 'window') {
|
|
131
168
|
return [
|
|
132
169
|
ENABLED_TITLE,
|
|
@@ -134,6 +171,7 @@ export function executeCacheKeepCommand(input) {
|
|
|
134
171
|
buildCacheKeepStatusSummary({
|
|
135
172
|
...status,
|
|
136
173
|
enabled: true,
|
|
174
|
+
always: false,
|
|
137
175
|
window: { startHour: action.startHour, endHour: action.endHour },
|
|
138
176
|
}),
|
|
139
177
|
].join('\n');
|
|
@@ -218,17 +256,47 @@ export class CacheKeepManager {
|
|
|
218
256
|
clearInterval(this.timer);
|
|
219
257
|
this.timer = null;
|
|
220
258
|
}
|
|
221
|
-
stats(window, now = this.options.now?.() ?? Date.now()) {
|
|
222
|
-
const today = localWindowKey(window, new Date(now));
|
|
259
|
+
stats(window, now = this.options.now?.() ?? Date.now(), always = false) {
|
|
260
|
+
const today = always ? 'always' : localWindowKey(window, new Date(now));
|
|
223
261
|
const targets = [...this.targets.values()].filter((target) => target.dayKey === today);
|
|
224
262
|
const nextPrewarmAt = targets.length
|
|
225
263
|
? Math.min(...targets.map((target) => target.cacheExpiresAt - CACHE_KEEP_PREWARM_LEAD_MS))
|
|
226
264
|
: undefined;
|
|
227
265
|
return { trackedSessions: targets.length, nextPrewarmAt };
|
|
228
266
|
}
|
|
267
|
+
trackedSessions() {
|
|
268
|
+
return [...this.targets.values()]
|
|
269
|
+
.map((target) => ({
|
|
270
|
+
id: target.id,
|
|
271
|
+
cacheExpiresAt: target.cacheExpiresAt,
|
|
272
|
+
nextPrewarmAt: target.cacheExpiresAt - CACHE_KEEP_PREWARM_LEAD_MS,
|
|
273
|
+
}))
|
|
274
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
275
|
+
}
|
|
276
|
+
trackedOAuthRoute(sessionId) {
|
|
277
|
+
const target = this.targets.get(sessionId);
|
|
278
|
+
return target ? { oauthAccountId: target.oauthAccountId } : undefined;
|
|
279
|
+
}
|
|
229
280
|
trackedCount() {
|
|
230
281
|
return this.targets.size;
|
|
231
282
|
}
|
|
283
|
+
publishTrackedSessions() {
|
|
284
|
+
const callback = this.options.onTrackedSessionsChanged;
|
|
285
|
+
if (!callback)
|
|
286
|
+
return;
|
|
287
|
+
try {
|
|
288
|
+
void Promise.resolve(callback(this.trackedSessions())).catch((error) => {
|
|
289
|
+
logger.warn('cachekeep', 'failed to publish tracked sessions', {
|
|
290
|
+
error: error instanceof Error ? error.message : String(error),
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
logger.warn('cachekeep', 'failed to publish tracked sessions', {
|
|
296
|
+
error: error instanceof Error ? error.message : String(error),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
232
300
|
totalBodyBytes() {
|
|
233
301
|
return [...this.targets.values()].reduce((sum, target) => sum + target.bodyText.length, 0);
|
|
234
302
|
}
|
|
@@ -270,10 +338,10 @@ export class CacheKeepManager {
|
|
|
270
338
|
}
|
|
271
339
|
const now = this.options.now?.() ?? Date.now();
|
|
272
340
|
const window = getCacheKeepWindow(input.storage);
|
|
273
|
-
if (!
|
|
274
|
-
return { tracked: false, reason: 'outside configured
|
|
341
|
+
if (!isCacheKeepActiveNow(input.storage, new Date(now))) {
|
|
342
|
+
return { tracked: false, reason: 'outside configured schedule' };
|
|
275
343
|
}
|
|
276
|
-
const today =
|
|
344
|
+
const today = cacheKeepPeriodKey(input.storage, window, new Date(now));
|
|
277
345
|
this.pruneTargets(now, today);
|
|
278
346
|
if (this.targets.has(input.sessionId))
|
|
279
347
|
this.targets.delete(input.sessionId);
|
|
@@ -291,6 +359,7 @@ export class CacheKeepManager {
|
|
|
291
359
|
oauthAccountId: input.oauthAccountId,
|
|
292
360
|
});
|
|
293
361
|
this.pruneTargets(now, today);
|
|
362
|
+
this.publishTrackedSessions();
|
|
294
363
|
this.start();
|
|
295
364
|
return { tracked: true };
|
|
296
365
|
}
|
|
@@ -314,16 +383,25 @@ export class CacheKeepManager {
|
|
|
314
383
|
const storage = await this.options.loadStorage();
|
|
315
384
|
const window = getCacheKeepWindow(storage);
|
|
316
385
|
const now = this.options.now?.() ?? Date.now();
|
|
317
|
-
const today =
|
|
386
|
+
const today = cacheKeepPeriodKey(storage, window, new Date(now));
|
|
387
|
+
if (isCacheKeepAlways(storage)) {
|
|
388
|
+
for (const target of this.targets.values())
|
|
389
|
+
target.dayKey = 'always';
|
|
390
|
+
}
|
|
318
391
|
this.pruneTargets(now, today);
|
|
319
392
|
if (!this.targets.size) {
|
|
393
|
+
this.publishTrackedSessions();
|
|
320
394
|
this.stop();
|
|
321
395
|
return;
|
|
322
396
|
}
|
|
323
|
-
if (!isCacheKeepHybridActive(storage))
|
|
397
|
+
if (!isCacheKeepHybridActive(storage)) {
|
|
398
|
+
this.publishTrackedSessions();
|
|
324
399
|
return;
|
|
325
|
-
|
|
400
|
+
}
|
|
401
|
+
if (!isCacheKeepActiveNow(storage, new Date(now))) {
|
|
402
|
+
this.publishTrackedSessions();
|
|
326
403
|
return;
|
|
404
|
+
}
|
|
327
405
|
logger.debug('cachekeep', 'fired', { targets: this.targets.size });
|
|
328
406
|
const dueAt = now + CACHE_KEEP_PREWARM_LEAD_MS;
|
|
329
407
|
for (const target of this.targets.values()) {
|
|
@@ -331,6 +409,7 @@ export class CacheKeepManager {
|
|
|
331
409
|
continue;
|
|
332
410
|
await this.prewarm(target, now);
|
|
333
411
|
}
|
|
412
|
+
this.publishTrackedSessions();
|
|
334
413
|
}
|
|
335
414
|
async sendPrewarm(target) {
|
|
336
415
|
const prewarm = await buildCacheKeepPrewarmBody(target.bodyText);
|
|
@@ -39,6 +39,7 @@ export interface AccountListItem {
|
|
|
39
39
|
role: 'main' | 'fallback';
|
|
40
40
|
enabled: boolean;
|
|
41
41
|
quotaPercent: number | null;
|
|
42
|
+
tierLabel?: string;
|
|
42
43
|
}
|
|
43
44
|
export declare function buildAccountList(storage: AccountStorage): AccountListItem[];
|
|
44
45
|
export declare function executeAccountCommand(input: {
|
package/dist/commands/account.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatOAuthAccountTier } from "../oauth-profile.js";
|
|
1
2
|
export const CLAUDE_ACCOUNT_COMMAND_NAME = 'claude-account';
|
|
2
3
|
export function parseAccountCommandAction(argumentsText) {
|
|
3
4
|
const parts = argumentsText.trim().split(/\s+/).filter(Boolean);
|
|
@@ -89,6 +90,7 @@ export function buildAccountList(storage) {
|
|
|
89
90
|
role: 'main',
|
|
90
91
|
enabled: true,
|
|
91
92
|
quotaPercent: fiveHour?.usedPercent ?? null,
|
|
93
|
+
tierLabel: formatOAuthAccountTier(storage.main?.profile),
|
|
92
94
|
});
|
|
93
95
|
for (const account of storage.accounts) {
|
|
94
96
|
const fiveHourQuota = account.quota?.five_hour;
|
|
@@ -98,6 +100,9 @@ export function buildAccountList(storage) {
|
|
|
98
100
|
role: 'fallback',
|
|
99
101
|
enabled: account.enabled !== false,
|
|
100
102
|
quotaPercent: fiveHourQuota?.usedPercent ?? null,
|
|
103
|
+
tierLabel: account.type === 'oauth'
|
|
104
|
+
? formatOAuthAccountTier(account.profile)
|
|
105
|
+
: undefined,
|
|
101
106
|
});
|
|
102
107
|
}
|
|
103
108
|
return list;
|
|
@@ -124,7 +129,8 @@ export function executeAccountCommand(input) {
|
|
|
124
129
|
for (const a of list) {
|
|
125
130
|
const pct = a.quotaPercent != null ? ` ${Math.round(a.quotaPercent)}%` : '';
|
|
126
131
|
const status = !a.enabled ? ' (disabled)' : '';
|
|
127
|
-
|
|
132
|
+
const tier = a.tierLabel ? ` · ${a.tierLabel}` : '';
|
|
133
|
+
lines.push(`- **${a.label}** [${a.role}]${tier}${status}${pct}`);
|
|
128
134
|
}
|
|
129
135
|
lines.push('', USAGE_TEXT);
|
|
130
136
|
return { text: lines.join('\n') };
|
package/dist/dump.d.ts
CHANGED
|
@@ -13,6 +13,25 @@ export declare function isDumpEnabled(): boolean;
|
|
|
13
13
|
export declare function setDumpEnabled(enabled: boolean): void;
|
|
14
14
|
export declare function resetDumpState(): void;
|
|
15
15
|
export declare function getDumpDirectory(): string;
|
|
16
|
+
export declare function writeDumpFile(path: string, contents: string): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Caps completed dump artifacts in the configured directory. A zero-byte cap
|
|
19
|
+
* disables sweeping. Custom directories may contain unrelated files: only
|
|
20
|
+
* recognized final artifact names and stale partials are eligible. Symlinks,
|
|
21
|
+
* fresh partial writes, and files younger than the newness floor remain
|
|
22
|
+
* protected.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sweepDumpDirectory(options: {
|
|
25
|
+
dumpDir?: string;
|
|
26
|
+
maxBytes?: number;
|
|
27
|
+
protectedPaths?: readonly string[];
|
|
28
|
+
now?: number;
|
|
29
|
+
minAgeMs?: number;
|
|
30
|
+
partialStaleMs?: number;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
removed: number;
|
|
33
|
+
freedBytes: number;
|
|
34
|
+
}>;
|
|
16
35
|
export declare function parseDumpCommandAction(argumentsText: string): DumpCommandAction;
|
|
17
36
|
export declare function buildDumpStatusSummary(input?: {
|
|
18
37
|
enabled?: boolean;
|