@triflux/core 10.51.0 → 10.53.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/hub/bridge.mjs +69 -1197
- package/hub/cli-adapter-base.mjs +4 -104
- package/hub/codex-adapter.mjs +7 -116
- package/hub/index.mjs +3 -20
- package/hub/lib/process-utils.mjs +1 -33
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/claude-transcript.mjs +19 -0
- package/hub/team/nativeProxy.mjs +759 -0
- package/hub/team/session-context.mjs +1 -1
- package/hud/constants.mjs +9 -22
- package/hud/hud-qos-status.mjs +16 -19
- package/hud/providers/codex-probe.mjs +2 -31
- package/hud/providers/codex.mjs +16 -58
- package/hud/providers/gemini.mjs +20 -0
- package/hud/renderers.mjs +101 -77
- package/hud/utils.mjs +9 -13
- package/package.json +3 -6
- package/scripts/lib/agent-route-policy.mjs +0 -67
- package/scripts/lib/cli-claude.mjs +0 -12
- package/scripts/lib/env-probe.mjs +0 -70
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/mcp-guard-engine.mjs +42 -101
- package/scripts/lib/session-presence.mjs +1 -99
- package/hub/account-broker.mjs +0 -1221
- package/hub/adaptive-diagnostic.mjs +0 -350
- package/hub/adaptive-inject.mjs +0 -224
- package/hub/adaptive-memory.mjs +0 -277
- package/hub/adaptive.mjs +0 -172
- package/hub/assign-callbacks.mjs +0 -170
- package/hub/delegator/contracts.mjs +0 -38
- package/hub/delegator/index.mjs +0 -14
- package/hub/delegator/schema/delegator-tools.schema.json +0 -250
- package/hub/delegator/service.mjs +0 -336
- package/hub/delegator/tool-definitions.mjs +0 -35
- package/hub/hitl.mjs +0 -202
- package/hub/lib/known-errors.json +0 -72
- package/hub/lib/memory-store.mjs +0 -1001
- package/hub/lib/phase-manager.mjs +0 -493
- package/hub/lib/state-snapshot.mjs +0 -293
- package/hub/lib/trace-recorder.mjs +0 -153
- package/hub/lib/uuidv7.mjs +0 -45
- package/hub/memory-doctor.mjs +0 -640
- package/hub/middleware/quota-middleware.mjs +0 -70
- package/hub/middleware/request-logger.mjs +0 -191
- package/hub/paths.mjs +0 -24
- package/hub/pipeline/index.mjs +0 -163
- package/hub/pipeline/state.mjs +0 -191
- package/hub/pipeline/transitions.mjs +0 -131
- package/hub/reflexion.mjs +0 -290
- package/hub/role-contract.mjs +0 -312
- package/hub/router.mjs +0 -2026
- package/hub/session-fingerprint.mjs +0 -418
- package/hub/state.mjs +0 -300
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team-bridge.mjs +0 -27
- package/scripts/lib/claudemd-scanner.mjs +0 -26
- package/scripts/lib/context.mjs +0 -51
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
|
@@ -96,7 +96,7 @@ export function contextGuard(
|
|
|
96
96
|
: {};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async function findCodexRollout(threadId) {
|
|
99
|
+
export async function findCodexRollout(threadId) {
|
|
100
100
|
if (!/^[a-zA-Z0-9-]+$/u.test(threadId || "")) return null;
|
|
101
101
|
const root = path.join(
|
|
102
102
|
process.env.CODEX_HOME || path.join(homedir(), ".codex"),
|
package/hud/constants.mjs
CHANGED
|
@@ -6,21 +6,6 @@ import { join } from "node:path";
|
|
|
6
6
|
|
|
7
7
|
export const VERSION = "2.0";
|
|
8
8
|
|
|
9
|
-
export const ACCOUNTS_CONFIG_PATH = join(
|
|
10
|
-
homedir(),
|
|
11
|
-
".claude",
|
|
12
|
-
"cache",
|
|
13
|
-
"tfx-hub",
|
|
14
|
-
"accounts.json",
|
|
15
|
-
);
|
|
16
|
-
export const ACCOUNTS_STATE_PATH = join(
|
|
17
|
-
homedir(),
|
|
18
|
-
".claude",
|
|
19
|
-
"cache",
|
|
20
|
-
"tfx-hub",
|
|
21
|
-
"cli_accounts_state.json",
|
|
22
|
-
);
|
|
23
|
-
|
|
24
9
|
// triflux-mods 의 Claude band 가 세션마다 갱신하는 표식. 있으면 Claude 행을 뺀다.
|
|
25
10
|
export const CLAUDE_BAND_MARKER_DIR = join(
|
|
26
11
|
homedir(),
|
|
@@ -107,19 +92,19 @@ export const SEVEN_DAY_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
107
92
|
export const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
108
93
|
export const DEFAULT_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
109
94
|
|
|
110
|
-
|
|
95
|
+
// Codex CLI 는 CODEX_HOME 이 있으면 그 디렉터리를 쓴다. 호출 시점에 읽어 환경 변화를 따른다.
|
|
96
|
+
export function getCodexHome() {
|
|
97
|
+
return process.env.CODEX_HOME || join(homedir(), ".codex");
|
|
98
|
+
}
|
|
99
|
+
export function getCodexAuthPath() {
|
|
100
|
+
return join(getCodexHome(), "auth.json");
|
|
101
|
+
}
|
|
111
102
|
export const CODEX_PROBE_STATE_PATH = join(
|
|
112
103
|
homedir(),
|
|
113
104
|
".claude",
|
|
114
105
|
"cache",
|
|
115
106
|
"codex-probe-state.json",
|
|
116
107
|
);
|
|
117
|
-
export const CODEX_BROKER_AUTH_CACHE_DIR = join(
|
|
118
|
-
homedir(),
|
|
119
|
-
".claude",
|
|
120
|
-
"cache",
|
|
121
|
-
"tfx-hub",
|
|
122
|
-
);
|
|
123
108
|
export const CODEX_PROBE_TTL_MS = 5 * 60 * 1000;
|
|
124
109
|
export const CODEX_PROBE_TIMEOUT_MS = 10_000;
|
|
125
110
|
export const CODEX_QUOTA_CACHE_PATH = join(
|
|
@@ -171,6 +156,8 @@ export const ANTIGRAVITY_REFRESH_LOCK_PATH = join(
|
|
|
171
156
|
".antigravity-refresh-lock",
|
|
172
157
|
);
|
|
173
158
|
export const ANTIGRAVITY_QUOTA_STALE_MS = 5 * 60 * 1000;
|
|
159
|
+
// 조회가 잠시 실패해도 이 시간 안에 성공한 적이 있으면 로그인 상태로 본다.
|
|
160
|
+
export const ANTIGRAVITY_SIGNED_IN_GRACE_MS = 60 * 60 * 1000;
|
|
174
161
|
export const ANTIGRAVITY_REFRESH_FLAG = "--refresh-antigravity-quota";
|
|
175
162
|
export const ACCOUNT_LABEL_WIDTH = 10;
|
|
176
163
|
export const PROVIDER_PREFIX_WIDTH = 2;
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
RESET,
|
|
15
15
|
} from "./colors.mjs";
|
|
16
16
|
import {
|
|
17
|
-
ACCOUNTS_CONFIG_PATH,
|
|
18
|
-
ACCOUNTS_STATE_PATH,
|
|
19
17
|
ANTIGRAVITY_REFRESH_FLAG,
|
|
20
18
|
CLAUDE_BAND_MARKER_DIR,
|
|
21
19
|
CLAUDE_BAND_MARKER_TTL_MS,
|
|
22
20
|
CLAUDE_REFRESH_FLAG,
|
|
23
21
|
CODEX_REFRESH_FLAG,
|
|
22
|
+
getCodexAuthPath,
|
|
23
|
+
getCodexHome,
|
|
24
24
|
} from "./constants.mjs";
|
|
25
25
|
import { buildContextUsageView } from "./context-monitor.mjs";
|
|
26
26
|
import {
|
|
@@ -30,13 +30,13 @@ import {
|
|
|
30
30
|
} from "./providers/claude.mjs";
|
|
31
31
|
import {
|
|
32
32
|
getCodexEmail,
|
|
33
|
-
hasBrokerCodexAccounts,
|
|
34
33
|
readCodexRateLimitSnapshot,
|
|
35
34
|
refreshCodexRateLimitsCache,
|
|
36
35
|
scheduleCodexRateLimitRefresh,
|
|
37
36
|
} from "./providers/codex.mjs";
|
|
38
37
|
import {
|
|
39
38
|
getAntigravityAccountLabel,
|
|
39
|
+
getAntigravityAuthKind,
|
|
40
40
|
readAntigravityQuotaSnapshot,
|
|
41
41
|
refreshAntigravityQuotaCache,
|
|
42
42
|
scheduleAntigravityQuotaRefresh,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
renderAlignedRows,
|
|
49
49
|
} from "./renderers.mjs";
|
|
50
50
|
import { selectTier } from "./terminal.mjs";
|
|
51
|
-
import {
|
|
51
|
+
import { formatTimeCell, formatTimeCellDH, readStdinJson } from "./utils.mjs";
|
|
52
52
|
|
|
53
53
|
async function main() {
|
|
54
54
|
if (process.argv.includes(CLAUDE_REFRESH_FLAG)) {
|
|
@@ -66,8 +66,6 @@ async function main() {
|
|
|
66
66
|
|
|
67
67
|
const stdinPromise = readStdinJson();
|
|
68
68
|
const { showCodex, antigravityAllowed } = resolveHudCliVisibility();
|
|
69
|
-
const accountsConfig = readJson(ACCOUNTS_CONFIG_PATH, { providers: {} });
|
|
70
|
-
const accountsState = readJson(ACCOUNTS_STATE_PATH, { providers: {} });
|
|
71
69
|
const claudeUsageSnapshot = readClaudeUsageSnapshot();
|
|
72
70
|
const codexSnapshot = readCodexRateLimitSnapshot();
|
|
73
71
|
const antigravitySnapshot = antigravityAllowed
|
|
@@ -81,11 +79,7 @@ async function main() {
|
|
|
81
79
|
) {
|
|
82
80
|
scheduleClaudeUsageRefresh();
|
|
83
81
|
}
|
|
84
|
-
if (
|
|
85
|
-
showCodex &&
|
|
86
|
-
codexSnapshot.shouldRefresh &&
|
|
87
|
-
(existsSync(join(homedir(), ".codex")) || hasBrokerCodexAccounts())
|
|
88
|
-
) {
|
|
82
|
+
if (showCodex && codexSnapshot.shouldRefresh && existsSync(getCodexHome())) {
|
|
89
83
|
scheduleCodexRateLimitRefresh();
|
|
90
84
|
}
|
|
91
85
|
|
|
@@ -95,12 +89,19 @@ async function main() {
|
|
|
95
89
|
? { ...claudeUsageSnapshot.data, stale: claudeUsageSnapshot.isStale }
|
|
96
90
|
: null;
|
|
97
91
|
const codexBuckets = codexSnapshot.buckets;
|
|
92
|
+
const antigravityQuota = antigravityAllowed
|
|
93
|
+
? { ...antigravitySnapshot?.data, auth: getAntigravityAuthKind() }
|
|
94
|
+
: null;
|
|
98
95
|
const currentTier = selectTier();
|
|
96
|
+
// 회색은 로그인 안 된 경우에만 쓴다. 로그인 상태의 조회 공백은 --% 로만 보인다.
|
|
97
|
+
const codexLoggedOut =
|
|
98
|
+
showCodex && !codexBuckets && !existsSync(getCodexAuthPath());
|
|
99
99
|
if (currentTier === "nano") {
|
|
100
100
|
const microLine = getMicroLine(contextView, claudeUsage, codexBuckets, {
|
|
101
101
|
showCodex,
|
|
102
|
+
codexLoggedOut,
|
|
102
103
|
showAntigravity: antigravityAllowed,
|
|
103
|
-
antigravityQuota
|
|
104
|
+
antigravityQuota,
|
|
104
105
|
});
|
|
105
106
|
process.stdout.write(`\x1b[0m${microLine}\n`);
|
|
106
107
|
return;
|
|
@@ -118,8 +119,6 @@ async function main() {
|
|
|
118
119
|
"codex",
|
|
119
120
|
"x",
|
|
120
121
|
codexWhite,
|
|
121
|
-
accountsConfig,
|
|
122
|
-
accountsState,
|
|
123
122
|
codexBuckets ? { type: "codex", buckets: codexBuckets } : null,
|
|
124
123
|
getCodexEmail(),
|
|
125
124
|
),
|
|
@@ -132,16 +131,14 @@ async function main() {
|
|
|
132
131
|
"antigravity",
|
|
133
132
|
"a",
|
|
134
133
|
geminiBlue,
|
|
135
|
-
|
|
136
|
-
accountsState,
|
|
137
|
-
antigravitySnapshot?.data,
|
|
134
|
+
antigravityQuota,
|
|
138
135
|
getAntigravityAccountLabel(),
|
|
139
136
|
),
|
|
140
137
|
);
|
|
141
138
|
}
|
|
142
139
|
|
|
143
140
|
const outputLines = renderAlignedRows(rows);
|
|
144
|
-
if (
|
|
141
|
+
if (outputLines[codexRowIndex] != null && codexLoggedOut) {
|
|
145
142
|
outputLines[codexRowIndex] = `${DIM}${outputLines[codexRowIndex]}${RESET}`;
|
|
146
143
|
}
|
|
147
144
|
// 알림 배너와 TUI 스타일이 HUD 내용에 겹치지 않도록 한다.
|
|
@@ -152,7 +149,7 @@ async function main() {
|
|
|
152
149
|
|
|
153
150
|
main().catch(() => {
|
|
154
151
|
process.stdout.write(
|
|
155
|
-
`\x1b[0m${bold(claudeOrange("c"))}: ${dim(
|
|
152
|
+
`\x1b[0m${bold(claudeOrange("c"))}: ${dim(`5h:--% ${formatTimeCell("")} 1w:--% ${formatTimeCellDH("")} | CTX:--%`)}\n`,
|
|
156
153
|
);
|
|
157
154
|
});
|
|
158
155
|
|
|
@@ -2,16 +2,8 @@
|
|
|
2
2
|
// 세션 이벤트(snake_case) 파이프라인에 합류 가능한 스냅샷을 만든다.
|
|
3
3
|
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
mkdirSync,
|
|
8
|
-
mkdtempSync,
|
|
9
|
-
readdirSync,
|
|
10
|
-
readFileSync,
|
|
11
|
-
writeFileSync,
|
|
12
|
-
} from "node:fs";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
import { dirname, join } from "node:path";
|
|
5
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { dirname } from "node:path";
|
|
15
7
|
|
|
16
8
|
function readJwtExpSec(token) {
|
|
17
9
|
try {
|
|
@@ -54,7 +46,6 @@ export function recordProbe(stateFilePath, key, nowMs) {
|
|
|
54
46
|
|
|
55
47
|
export function listProbeTargets({
|
|
56
48
|
codexAuthPath,
|
|
57
|
-
brokerCacheDir,
|
|
58
49
|
stateFilePath,
|
|
59
50
|
ttlMs,
|
|
60
51
|
nowMs,
|
|
@@ -68,26 +59,6 @@ export function listProbeTargets({
|
|
|
68
59
|
if (currentAuth && accessTokenUsable(currentAuth, nowMs) && due("current")) {
|
|
69
60
|
targets.push({ key: "current", codexHome: dirname(codexAuthPath) });
|
|
70
61
|
}
|
|
71
|
-
|
|
72
|
-
if (brokerCacheDir && existsSync(brokerCacheDir)) {
|
|
73
|
-
let files = [];
|
|
74
|
-
try {
|
|
75
|
-
files = readdirSync(brokerCacheDir).filter((file) =>
|
|
76
|
-
/^codex-auth-.+\.json$/.test(file),
|
|
77
|
-
);
|
|
78
|
-
} catch {
|
|
79
|
-
files = [];
|
|
80
|
-
}
|
|
81
|
-
for (const file of files) {
|
|
82
|
-
const key = file.replace(/^codex-auth-/, "").replace(/\.json$/, "");
|
|
83
|
-
if (!due(key)) continue;
|
|
84
|
-
const auth = readJsonFile(join(brokerCacheDir, file));
|
|
85
|
-
if (!auth || !accessTokenUsable(auth, nowMs)) continue;
|
|
86
|
-
const home = mkdtempSync(join(tmpdir(), `tfx-probe-home-${key}-`));
|
|
87
|
-
writeFileSync(join(home, "auth.json"), JSON.stringify(auth));
|
|
88
|
-
targets.push({ key, codexHome: home });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
62
|
return targets;
|
|
92
63
|
}
|
|
93
64
|
|
package/hud/providers/codex.mjs
CHANGED
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
7
|
import { join } from "node:path";
|
|
9
8
|
import {
|
|
10
|
-
CODEX_AUTH_PATH,
|
|
11
|
-
CODEX_BROKER_AUTH_CACHE_DIR,
|
|
12
9
|
CODEX_MIN_BUCKETS,
|
|
13
10
|
CODEX_PROBE_STATE_PATH,
|
|
14
11
|
CODEX_PROBE_TIMEOUT_MS,
|
|
@@ -17,6 +14,8 @@ import {
|
|
|
17
14
|
CODEX_QUOTA_STALE_MS,
|
|
18
15
|
CODEX_REFRESH_FLAG,
|
|
19
16
|
CODEX_REFRESH_LOCK_PATH,
|
|
17
|
+
getCodexAuthPath,
|
|
18
|
+
getCodexHome,
|
|
20
19
|
SPAWN_LOCK_TTL_MS,
|
|
21
20
|
} from "../constants.mjs";
|
|
22
21
|
import { decodeJwtEmail, readJson, writeJsonSafe } from "../utils.mjs";
|
|
@@ -59,7 +58,7 @@ export function normalizeBuckets(rl) {
|
|
|
59
58
|
|
|
60
59
|
export function getCodexEmail() {
|
|
61
60
|
try {
|
|
62
|
-
const auth = JSON.parse(readFileSync(
|
|
61
|
+
const auth = JSON.parse(readFileSync(getCodexAuthPath(), "utf-8"));
|
|
63
62
|
return decodeJwtEmail(auth?.tokens?.id_token);
|
|
64
63
|
} catch {
|
|
65
64
|
return null;
|
|
@@ -117,8 +116,8 @@ function positiveWindowMinutes(bucket) {
|
|
|
117
116
|
return minutes > 0 ? minutes : null;
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
// 계정 하나에는 같은 종류의 창이 한 번에 하나만 열린다.
|
|
121
|
-
//
|
|
119
|
+
// 계정 하나에는 같은 종류의 창이 한 번에 하나만 열린다. Codex 홈(CODEX_HOME)의
|
|
120
|
+
// sessions 와 HUD 라벨은 모두 auth.json 계정 하나의 것이다. 그래서
|
|
122
121
|
// 세션 로그에 더 늦게 리셋되는 창이 나타났고 옛 창이 그 창이 열리기 전에만 관측됐다면,
|
|
123
122
|
// 옛 창은 조기 리셋(2026-09-23 실측)이나 계정 전환으로 끝난 창이다. 프로브로만 본 창은
|
|
124
123
|
// 다른 계정일 수 있어 근거로 쓰지 않는다. 새 창이 열린 뒤에도 관측된 창은 함께 도는
|
|
@@ -135,12 +134,7 @@ function isReplacedWindowGroup(group, other) {
|
|
|
135
134
|
return lastSeenMs <= otherOpenedAtMs;
|
|
136
135
|
}
|
|
137
136
|
|
|
138
|
-
function selectCodexWindowSnapshot(
|
|
139
|
-
snapshots,
|
|
140
|
-
nowSec,
|
|
141
|
-
windowKey,
|
|
142
|
-
replaceEndedWindows,
|
|
143
|
-
) {
|
|
137
|
+
function selectCodexWindowSnapshot(snapshots, nowSec, windowKey) {
|
|
144
138
|
const groups = [];
|
|
145
139
|
const ungrouped = [];
|
|
146
140
|
|
|
@@ -179,12 +173,9 @@ function selectCodexWindowSnapshot(
|
|
|
179
173
|
const openGroups = groups.filter(
|
|
180
174
|
(group) => Number(group.latest[windowKey]?.resets_at) > nowSec,
|
|
181
175
|
);
|
|
182
|
-
const activeGroups =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
!openGroups.some((other) => isReplacedWindowGroup(group, other)),
|
|
186
|
-
)
|
|
187
|
-
: openGroups;
|
|
176
|
+
const activeGroups = openGroups.filter(
|
|
177
|
+
(group) => !openGroups.some((other) => isReplacedWindowGroup(group, other)),
|
|
178
|
+
);
|
|
188
179
|
let selected = null;
|
|
189
180
|
if (activeGroups.length > 0) {
|
|
190
181
|
selected = activeGroups
|
|
@@ -214,18 +205,16 @@ function selectCodexWindowSnapshot(
|
|
|
214
205
|
return { snapshot: selected, activeGroupCount: activeGroups.length };
|
|
215
206
|
}
|
|
216
207
|
|
|
217
|
-
function selectCodexSnapshot(snapshots, nowSec
|
|
208
|
+
function selectCodexSnapshot(snapshots, nowSec) {
|
|
218
209
|
const primarySelection = selectCodexWindowSnapshot(
|
|
219
210
|
snapshots,
|
|
220
211
|
nowSec,
|
|
221
212
|
"primary",
|
|
222
|
-
replaceEndedWindows,
|
|
223
213
|
);
|
|
224
214
|
const secondarySelection = selectCodexWindowSnapshot(
|
|
225
215
|
snapshots,
|
|
226
216
|
nowSec,
|
|
227
217
|
"secondary",
|
|
228
|
-
replaceEndedWindows,
|
|
229
218
|
);
|
|
230
219
|
const primarySnapshot = primarySelection.snapshot;
|
|
231
220
|
if (!primarySnapshot) return null;
|
|
@@ -246,7 +235,7 @@ function selectCodexSnapshot(snapshots, nowSec, replaceEndedWindows) {
|
|
|
246
235
|
return selected;
|
|
247
236
|
}
|
|
248
237
|
|
|
249
|
-
function mergeRateLimitSnapshots(snapshots, nowSec
|
|
238
|
+
function mergeRateLimitSnapshots(snapshots, nowSec) {
|
|
250
239
|
const merged = {};
|
|
251
240
|
const codexSnapshots = [];
|
|
252
241
|
for (const snapshot of snapshots) {
|
|
@@ -259,11 +248,7 @@ function mergeRateLimitSnapshots(snapshots, nowSec, replaceEndedWindows) {
|
|
|
259
248
|
}
|
|
260
249
|
}
|
|
261
250
|
|
|
262
|
-
const codex = selectCodexSnapshot(
|
|
263
|
-
codexSnapshots,
|
|
264
|
-
nowSec,
|
|
265
|
-
replaceEndedWindows,
|
|
266
|
-
);
|
|
251
|
+
const codex = selectCodexSnapshot(codexSnapshots, nowSec);
|
|
267
252
|
if (codex) merged.codex = codex;
|
|
268
253
|
return merged;
|
|
269
254
|
}
|
|
@@ -275,12 +260,10 @@ function mergeRateLimitSnapshots(snapshots, nowSec, replaceEndedWindows) {
|
|
|
275
260
|
// 합성 버킷(token_count 기반)은 2일 이내 데이터만 허용하여 stale 방지.
|
|
276
261
|
// ============================================================================
|
|
277
262
|
export function getCodexRateLimits({
|
|
278
|
-
sessionsRoot = join(
|
|
263
|
+
sessionsRoot = join(getCodexHome(), "sessions"),
|
|
279
264
|
now = new Date(),
|
|
280
265
|
maxLinesPerFile = 800,
|
|
281
266
|
extraSnapshots = [],
|
|
282
|
-
// 교체된 창 판정(isReplacedWindowGroup). 브로커 Codex 계정이 있으면 끈다.
|
|
283
|
-
replaceEndedWindows = true,
|
|
284
267
|
} = {}) {
|
|
285
268
|
let syntheticBucket = null; // 최근 token_count에서 합성 (행 활성화 + 토큰 데이터용)
|
|
286
269
|
// 프로브도 세션 로그와 같은 window_minutes 기준으로 5h/1w 슬롯을 정한다.
|
|
@@ -372,11 +355,7 @@ export function getCodexRateLimits({
|
|
|
372
355
|
}
|
|
373
356
|
|
|
374
357
|
const snapshots = dayOffset <= 1 ? recentSnapshots : daySnapshots;
|
|
375
|
-
const mergedBuckets = mergeRateLimitSnapshots(
|
|
376
|
-
snapshots,
|
|
377
|
-
nowSec,
|
|
378
|
-
replaceEndedWindows,
|
|
379
|
-
);
|
|
358
|
+
const mergedBuckets = mergeRateLimitSnapshots(snapshots, nowSec);
|
|
380
359
|
if (Object.keys(mergedBuckets).length > 0) {
|
|
381
360
|
if (syntheticBucket) {
|
|
382
361
|
const main =
|
|
@@ -407,8 +386,7 @@ export async function collectProbeSnapshots(now = new Date()) {
|
|
|
407
386
|
let targets = [];
|
|
408
387
|
try {
|
|
409
388
|
targets = listProbeTargets({
|
|
410
|
-
codexAuthPath:
|
|
411
|
-
brokerCacheDir: CODEX_BROKER_AUTH_CACHE_DIR,
|
|
389
|
+
codexAuthPath: getCodexAuthPath(),
|
|
412
390
|
stateFilePath: CODEX_PROBE_STATE_PATH,
|
|
413
391
|
ttlMs: Number(process.env.TFX_CODEX_PROBE_TTL_MS) || CODEX_PROBE_TTL_MS,
|
|
414
392
|
nowMs: now.getTime(),
|
|
@@ -432,30 +410,10 @@ export async function collectProbeSnapshots(now = new Date()) {
|
|
|
432
410
|
);
|
|
433
411
|
}
|
|
434
412
|
|
|
435
|
-
// 브로커 계정은 자기 CODEX_HOME 에서 돌기 때문에, 옛 창이 전환 전 계정의 창이어도 그
|
|
436
|
-
// 계정이 아직 쓰이고 있을 수 있다. accounts.json 의 codex 항목과 프로브용 인증 캐시로 판정한다.
|
|
437
|
-
export function hasBrokerCodexAccounts({
|
|
438
|
-
brokerDir = CODEX_BROKER_AUTH_CACHE_DIR,
|
|
439
|
-
} = {}) {
|
|
440
|
-
const accounts = readJson(join(brokerDir, "accounts.json"), null);
|
|
441
|
-
if (Array.isArray(accounts?.codex) && accounts.codex.length > 0) return true;
|
|
442
|
-
try {
|
|
443
|
-
return readdirSync(brokerDir).some((file) =>
|
|
444
|
-
/^codex-auth-.+\.json$/.test(file),
|
|
445
|
-
);
|
|
446
|
-
} catch {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
413
|
export async function refreshCodexRateLimitsCache() {
|
|
452
414
|
const now = new Date();
|
|
453
415
|
const extraSnapshots = await collectProbeSnapshots(now);
|
|
454
|
-
const buckets = getCodexRateLimits({
|
|
455
|
-
now,
|
|
456
|
-
extraSnapshots,
|
|
457
|
-
replaceEndedWindows: !hasBrokerCodexAccounts(),
|
|
458
|
-
});
|
|
416
|
+
const buckets = getCodexRateLimits({ now, extraSnapshots });
|
|
459
417
|
// buckets가 null이어도 캐시 갱신 (stale 데이터 제거)
|
|
460
418
|
writeJsonSafe(CODEX_QUOTA_CACHE_PATH, { timestamp: Date.now(), buckets });
|
|
461
419
|
return buckets;
|
package/hud/providers/gemini.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ANTIGRAVITY_REFRESH_FLAG,
|
|
8
8
|
ANTIGRAVITY_REFRESH_LOCK_PATH,
|
|
9
9
|
ANTIGRAVITY_SETTINGS_PATH,
|
|
10
|
+
ANTIGRAVITY_SIGNED_IN_GRACE_MS,
|
|
10
11
|
SPAWN_LOCK_TTL_MS,
|
|
11
12
|
} from "../constants.mjs";
|
|
12
13
|
import { decodeJwtEmail, readJson, writeJsonSafe } from "../utils.mjs";
|
|
@@ -41,6 +42,18 @@ function isGcpProjectAuth() {
|
|
|
41
42
|
return Boolean(readJson(ANTIGRAVITY_SETTINGS_PATH, null)?.gcp?.project);
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// Keychain 인증은 파일을 남기지 않으므로 최근 쿼터 조회 성공도 로그인 근거로 본다.
|
|
46
|
+
export function getAntigravityAuthKind() {
|
|
47
|
+
if (isGcpProjectAuth()) return "project";
|
|
48
|
+
if (getAntigravityAccountLabel()) return "account";
|
|
49
|
+
const cache = readJson(ANTIGRAVITY_QUOTA_CACHE_PATH, null);
|
|
50
|
+
const signedInAge = Date.now() - Number(cache?.signedInAt);
|
|
51
|
+
return cache?.accountLabel == null &&
|
|
52
|
+
signedInAge < ANTIGRAVITY_SIGNED_IN_GRACE_MS
|
|
53
|
+
? "account"
|
|
54
|
+
: null;
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
export function readAntigravityQuotaSnapshot() {
|
|
45
58
|
// 프로젝트 과금의 기본 응답을 개인 플랜의 0% 사용률로 표시하지 않는다.
|
|
46
59
|
if (isGcpProjectAuth()) return { data: null, shouldRefresh: false };
|
|
@@ -103,10 +116,17 @@ export function refreshAntigravityQuotaCache(execFileSyncFn = execFileSync) {
|
|
|
103
116
|
// 실패도 캐시해 인증 오류나 CLI 부재 시 매 렌더마다 실행하지 않는다.
|
|
104
117
|
}
|
|
105
118
|
if (accountLabel === getAntigravityAccountLabel()) {
|
|
119
|
+
const previous = readJson(ANTIGRAVITY_QUOTA_CACHE_PATH, null);
|
|
120
|
+
const signedInAt = buckets
|
|
121
|
+
? Date.now()
|
|
122
|
+
: previous?.accountLabel === accountLabel
|
|
123
|
+
? previous?.signedInAt
|
|
124
|
+
: undefined;
|
|
106
125
|
writeJsonSafe(ANTIGRAVITY_QUOTA_CACHE_PATH, {
|
|
107
126
|
timestamp: Date.now(),
|
|
108
127
|
accountLabel,
|
|
109
128
|
buckets,
|
|
129
|
+
signedInAt,
|
|
110
130
|
});
|
|
111
131
|
}
|
|
112
132
|
return buckets;
|