@triflux/core 10.50.2 → 10.52.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/hooks/agy-session-hook.mjs +11 -56
- package/hooks/codex-session-hook.mjs +11 -101
- package/hub/bridge.mjs +65 -1250
- package/hub/cli-adapter-base.mjs +12 -90
- package/hub/codex-adapter.mjs +0 -1
- package/hub/codex-preflight.mjs +5 -28
- package/hub/index.mjs +3 -19
- package/hub/lib/process-utils.mjs +1 -156
- package/hub/lib/spawn-trace.mjs +3 -2
- package/hub/lib/timeout-defaults.mjs +0 -1
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/nativeProxy.mjs +759 -0
- package/hub/team/session-context.mjs +1 -1
- package/hud/cli-policy.mjs +0 -22
- package/hud/constants.mjs +13 -26
- package/hud/hud-qos-status.mjs +42 -54
- package/hud/providers/gemini.mjs +125 -20
- package/hud/renderers.mjs +101 -122
- package/hud/utils.mjs +10 -6
- package/package.json +2 -6
- package/scripts/lib/agent-route-policy.mjs +143 -62
- package/scripts/lib/cli-claude.mjs +0 -14
- package/scripts/lib/env-probe.mjs +2 -131
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +11 -59
- package/scripts/lib/session-presence.mjs +2 -100
- package/scripts/lib/skill-template.mjs +0 -245
- 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/dynamic-routing-engine.mjs +0 -511
- 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/risk-tier.mjs +0 -53
- package/hub/lib/ssh-command.mjs +0 -231
- package/hub/lib/staged-file-detect.mjs +0 -43
- package/hub/lib/state-snapshot.mjs +0 -293
- package/hub/lib/tfx-route-args.mjs +0 -353
- 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 -314
- package/hub/router.mjs +0 -2026
- package/hub/routing-snapshot.mjs +0 -261
- 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/mesh/index.mjs +0 -69
- package/mesh/mesh-budget.mjs +0 -137
- package/mesh/mesh-heartbeat.mjs +0 -100
- package/mesh/mesh-protocol.mjs +0 -96
- package/mesh/mesh-queue.mjs +0 -163
- package/mesh/mesh-registry.mjs +0 -78
- package/mesh/mesh-router.mjs +0 -77
- package/scripts/lib/claudemd-scanner.mjs +0 -223
- package/scripts/lib/context.mjs +0 -67
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- 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/cli-policy.mjs
CHANGED
|
@@ -41,25 +41,3 @@ export function resolveHudCliVisibility(env = process.env, opts = {}) {
|
|
|
41
41
|
return { showCodex: true, antigravityAllowed: true, policy: null };
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* antigravity / gemini 슬롯 행을 그릴지 판정한다.
|
|
47
|
-
*
|
|
48
|
-
* antigravity 가 허용이면 기존 동작 그대로 항상 그린다. 정책으로 꺼졌을 때만
|
|
49
|
-
* gemini 폴백 정보가 하나라도 있는지 보고, 전부 비어 있으면 행을 생략한다.
|
|
50
|
-
*
|
|
51
|
-
* @param {{
|
|
52
|
-
* antigravityAllowed?: boolean,
|
|
53
|
-
* antigravityReady?: boolean,
|
|
54
|
-
* geminiEmail?: unknown,
|
|
55
|
-
* }} [args]
|
|
56
|
-
* @returns {boolean}
|
|
57
|
-
*/
|
|
58
|
-
export function shouldRenderGeminiFallbackRow({
|
|
59
|
-
antigravityAllowed = true,
|
|
60
|
-
antigravityReady = false,
|
|
61
|
-
geminiEmail = null,
|
|
62
|
-
} = {}) {
|
|
63
|
-
if (antigravityAllowed || antigravityReady) return true;
|
|
64
|
-
return Boolean(geminiEmail);
|
|
65
|
-
}
|
package/hud/constants.mjs
CHANGED
|
@@ -31,9 +31,6 @@ export const CLAUDE_BAND_MARKER_DIR = join(
|
|
|
31
31
|
);
|
|
32
32
|
export const CLAUDE_BAND_MARKER_TTL_MS = 12 * 60 * 60 * 1000;
|
|
33
33
|
|
|
34
|
-
// tfx-multi 세션 상태 디렉터리
|
|
35
|
-
export const TEAM_STATE_DIR =
|
|
36
|
-
process.env.TFX_HUB_PID_DIR || join(homedir(), ".claude", "cache", "tfx-hub");
|
|
37
34
|
export const CONTEXT_MONITOR_CACHE_PATH = join(
|
|
38
35
|
homedir(),
|
|
39
36
|
".claude",
|
|
@@ -149,11 +146,9 @@ export const CODEX_REFRESH_LOCK_PATH = join(
|
|
|
149
146
|
);
|
|
150
147
|
export const SPAWN_LOCK_TTL_MS = 30 * 1000; // 30초 spawn dedup
|
|
151
148
|
|
|
152
|
-
export const GEMINI_OAUTH_PATH = join(homedir(), ".gemini", "oauth_creds.json");
|
|
153
149
|
export const ANTIGRAVITY_OAUTH_PATHS = [
|
|
154
150
|
join(homedir(), ".gemini", "antigravity-cli", "oauth_creds.json"),
|
|
155
151
|
join(homedir(), ".gemini", "antigravity-cli", "credentials.json"),
|
|
156
|
-
GEMINI_OAUTH_PATH,
|
|
157
152
|
];
|
|
158
153
|
export const ANTIGRAVITY_KEYCHAIN_SERVICE = "gemini";
|
|
159
154
|
export const ANTIGRAVITY_KEYCHAIN_ACCOUNT = "antigravity";
|
|
@@ -163,33 +158,25 @@ export const ANTIGRAVITY_SETTINGS_PATH = join(
|
|
|
163
158
|
"antigravity-cli",
|
|
164
159
|
"settings.json",
|
|
165
160
|
);
|
|
166
|
-
|
|
167
|
-
export const ANTIGRAVITY_MODEL_ABBREV = {
|
|
168
|
-
// Flash 세대와 등급을 구분한다.
|
|
169
|
-
"Gemini 3.8 Flash (High)": "8h",
|
|
170
|
-
"Gemini 3.8 Flash (Medium)": "8m",
|
|
171
|
-
"Gemini 3.8 Flash (Low)": "8l",
|
|
172
|
-
"Gemini 3.7 Flash (High)": "7h",
|
|
173
|
-
"Gemini 3.7 Flash (Medium)": "7m",
|
|
174
|
-
"Gemini 3.7 Flash (Low)": "7l",
|
|
175
|
-
"Gemini 3.6 Flash (High)": "6h",
|
|
176
|
-
"Gemini 3.6 Flash (Medium)": "6m",
|
|
177
|
-
"Gemini 3.6 Flash (Low)": "6l",
|
|
178
|
-
"Gemini 3.5 Flash (High)": "Fh",
|
|
179
|
-
"Gemini 3.5 Flash (Medium)": "Fm",
|
|
180
|
-
"Gemini 3.5 Flash (Low)": "Fl",
|
|
181
|
-
};
|
|
182
|
-
export const TFX_PREFLIGHT_CACHE_PATH = join(
|
|
161
|
+
export const ANTIGRAVITY_QUOTA_CACHE_PATH = join(
|
|
183
162
|
homedir(),
|
|
184
163
|
".claude",
|
|
185
164
|
"cache",
|
|
186
|
-
"
|
|
165
|
+
"antigravity-quota-cache.json",
|
|
187
166
|
);
|
|
188
|
-
export const
|
|
189
|
-
|
|
167
|
+
export const ANTIGRAVITY_REFRESH_LOCK_PATH = join(
|
|
168
|
+
homedir(),
|
|
169
|
+
".claude",
|
|
170
|
+
"cache",
|
|
171
|
+
".antigravity-refresh-lock",
|
|
172
|
+
);
|
|
173
|
+
export const ANTIGRAVITY_QUOTA_STALE_MS = 5 * 60 * 1000;
|
|
174
|
+
// 조회가 잠시 실패해도 이 시간 안에 성공한 적이 있으면 로그인 상태로 본다.
|
|
175
|
+
export const ANTIGRAVITY_SIGNED_IN_GRACE_MS = 60 * 60 * 1000;
|
|
176
|
+
export const ANTIGRAVITY_REFRESH_FLAG = "--refresh-antigravity-quota";
|
|
190
177
|
export const ACCOUNT_LABEL_WIDTH = 10;
|
|
191
178
|
export const PROVIDER_PREFIX_WIDTH = 2;
|
|
192
|
-
export const PERCENT_CELL_WIDTH =
|
|
179
|
+
export const PERCENT_CELL_WIDTH = 4;
|
|
193
180
|
export const TIME_CELL_INNER_WIDTH = 6;
|
|
194
181
|
|
|
195
182
|
export const CLAUDE_REFRESH_FLAG = "--refresh-claude-usage";
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
resolveHudCliVisibility,
|
|
8
|
-
shouldRenderGeminiFallbackRow,
|
|
9
|
-
} from "./cli-policy.mjs";
|
|
6
|
+
import { resolveHudCliVisibility } from "./cli-policy.mjs";
|
|
10
7
|
import {
|
|
11
8
|
bold,
|
|
12
9
|
claudeOrange,
|
|
@@ -19,12 +16,12 @@ import {
|
|
|
19
16
|
import {
|
|
20
17
|
ACCOUNTS_CONFIG_PATH,
|
|
21
18
|
ACCOUNTS_STATE_PATH,
|
|
19
|
+
ANTIGRAVITY_REFRESH_FLAG,
|
|
22
20
|
CLAUDE_BAND_MARKER_DIR,
|
|
23
21
|
CLAUDE_BAND_MARKER_TTL_MS,
|
|
24
22
|
CLAUDE_REFRESH_FLAG,
|
|
23
|
+
CODEX_AUTH_PATH,
|
|
25
24
|
CODEX_REFRESH_FLAG,
|
|
26
|
-
TFX_PREFLIGHT_CACHE_PATH,
|
|
27
|
-
TFX_PREFLIGHT_CACHE_STALE_MS,
|
|
28
25
|
} from "./constants.mjs";
|
|
29
26
|
import { buildContextUsageView } from "./context-monitor.mjs";
|
|
30
27
|
import {
|
|
@@ -41,19 +38,24 @@ import {
|
|
|
41
38
|
} from "./providers/codex.mjs";
|
|
42
39
|
import {
|
|
43
40
|
getAntigravityAccountLabel,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
getAntigravityAuthKind,
|
|
42
|
+
readAntigravityQuotaSnapshot,
|
|
43
|
+
refreshAntigravityQuotaCache,
|
|
44
|
+
scheduleAntigravityQuotaRefresh,
|
|
47
45
|
} from "./providers/gemini.mjs";
|
|
48
46
|
import {
|
|
49
47
|
getClaudeRows,
|
|
50
48
|
getMicroLine,
|
|
51
49
|
getProviderRow,
|
|
52
|
-
getTeamRow,
|
|
53
50
|
renderAlignedRows,
|
|
54
51
|
} from "./renderers.mjs";
|
|
55
52
|
import { selectTier } from "./terminal.mjs";
|
|
56
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
formatTimeCell,
|
|
55
|
+
formatTimeCellDH,
|
|
56
|
+
readJson,
|
|
57
|
+
readStdinJson,
|
|
58
|
+
} from "./utils.mjs";
|
|
57
59
|
|
|
58
60
|
async function main() {
|
|
59
61
|
if (process.argv.includes(CLAUDE_REFRESH_FLAG)) {
|
|
@@ -64,22 +66,21 @@ async function main() {
|
|
|
64
66
|
await refreshCodexRateLimitsCache();
|
|
65
67
|
return;
|
|
66
68
|
}
|
|
69
|
+
if (process.argv.includes(ANTIGRAVITY_REFRESH_FLAG)) {
|
|
70
|
+
refreshAntigravityQuotaCache();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
67
73
|
|
|
68
74
|
const stdinPromise = readStdinJson();
|
|
69
|
-
const preflightCache = readJson(TFX_PREFLIGHT_CACHE_PATH, null);
|
|
70
|
-
const preflightTimestamp = Number(preflightCache?.timestamp);
|
|
71
|
-
const preflightFresh =
|
|
72
|
-
Number.isFinite(preflightTimestamp) &&
|
|
73
|
-
Date.now() - preflightTimestamp <= TFX_PREFLIGHT_CACHE_STALE_MS;
|
|
74
75
|
const { showCodex, antigravityAllowed } = resolveHudCliVisibility();
|
|
75
|
-
const antigravityReady =
|
|
76
|
-
antigravityAllowed &&
|
|
77
|
-
preflightFresh &&
|
|
78
|
-
preflightCache?.antigravity?.ok === true;
|
|
79
76
|
const accountsConfig = readJson(ACCOUNTS_CONFIG_PATH, { providers: {} });
|
|
80
77
|
const accountsState = readJson(ACCOUNTS_STATE_PATH, { providers: {} });
|
|
81
78
|
const claudeUsageSnapshot = readClaudeUsageSnapshot();
|
|
82
79
|
const codexSnapshot = readCodexRateLimitSnapshot();
|
|
80
|
+
const antigravitySnapshot = antigravityAllowed
|
|
81
|
+
? readAntigravityQuotaSnapshot()
|
|
82
|
+
: null;
|
|
83
|
+
if (antigravitySnapshot?.shouldRefresh) scheduleAntigravityQuotaRefresh();
|
|
83
84
|
// 설정이 없는 홈에서는 갱신 프로세스를 시작하지 않는다.
|
|
84
85
|
if (
|
|
85
86
|
claudeUsageSnapshot.shouldRefresh &&
|
|
@@ -101,22 +102,16 @@ async function main() {
|
|
|
101
102
|
? { ...claudeUsageSnapshot.data, stale: claudeUsageSnapshot.isStale }
|
|
102
103
|
: null;
|
|
103
104
|
const codexBuckets = codexSnapshot.buckets;
|
|
105
|
+
const antigravityQuota = antigravityAllowed
|
|
106
|
+
? { ...antigravitySnapshot?.data, auth: getAntigravityAuthKind() }
|
|
107
|
+
: null;
|
|
104
108
|
const currentTier = selectTier();
|
|
105
|
-
const geminiEmail = getGeminiEmail();
|
|
106
|
-
const showGeminiRow = shouldRenderGeminiFallbackRow({
|
|
107
|
-
antigravityAllowed,
|
|
108
|
-
antigravityReady,
|
|
109
|
-
geminiEmail,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
109
|
if (currentTier === "nano") {
|
|
113
|
-
const microLine = getMicroLine(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{ showCodex, showGemini: showGeminiRow },
|
|
119
|
-
);
|
|
110
|
+
const microLine = getMicroLine(contextView, claudeUsage, codexBuckets, {
|
|
111
|
+
showCodex,
|
|
112
|
+
showAntigravity: antigravityAllowed,
|
|
113
|
+
antigravityQuota,
|
|
114
|
+
});
|
|
120
115
|
process.stdout.write(`\x1b[0m${microLine}\n`);
|
|
121
116
|
return;
|
|
122
117
|
}
|
|
@@ -140,38 +135,31 @@ async function main() {
|
|
|
140
135
|
),
|
|
141
136
|
);
|
|
142
137
|
}
|
|
143
|
-
|
|
144
|
-
if (showGeminiRow) {
|
|
145
|
-
geminiRowIndex = rows.length;
|
|
138
|
+
if (antigravityAllowed) {
|
|
146
139
|
rows.push(
|
|
147
140
|
getProviderRow(
|
|
148
141
|
currentTier,
|
|
149
|
-
|
|
150
|
-
|
|
142
|
+
"antigravity",
|
|
143
|
+
"a",
|
|
151
144
|
geminiBlue,
|
|
152
145
|
accountsConfig,
|
|
153
146
|
accountsState,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
currentAbbrev: getAntigravityModelAbbrev(
|
|
157
|
-
getAntigravityCurrentModel(),
|
|
158
|
-
),
|
|
159
|
-
}
|
|
160
|
-
: null,
|
|
161
|
-
antigravityReady ? getAntigravityAccountLabel() : geminiEmail,
|
|
147
|
+
antigravityQuota,
|
|
148
|
+
getAntigravityAccountLabel(),
|
|
162
149
|
),
|
|
163
150
|
);
|
|
164
151
|
}
|
|
165
|
-
const teamRow = getTeamRow(currentTier);
|
|
166
|
-
if (teamRow) rows.push(teamRow);
|
|
167
152
|
|
|
168
153
|
const outputLines = renderAlignedRows(rows);
|
|
169
|
-
|
|
154
|
+
// 회색은 로그인 안 된 경우에만 쓴다. 로그인 상태의 조회 공백은 --% 로만 보인다.
|
|
155
|
+
if (
|
|
156
|
+
outputLines[codexRowIndex] != null &&
|
|
157
|
+
!codexBuckets &&
|
|
158
|
+
!existsSync(CODEX_AUTH_PATH) &&
|
|
159
|
+
!hasBrokerCodexAccounts()
|
|
160
|
+
) {
|
|
170
161
|
outputLines[codexRowIndex] = `${DIM}${outputLines[codexRowIndex]}${RESET}`;
|
|
171
162
|
}
|
|
172
|
-
if (!antigravityReady && outputLines[geminiRowIndex] != null) {
|
|
173
|
-
outputLines[geminiRowIndex] = dim(outputLines[geminiRowIndex]);
|
|
174
|
-
}
|
|
175
163
|
// 알림 배너와 TUI 스타일이 HUD 내용에 겹치지 않도록 한다.
|
|
176
164
|
const leadingBreaks = contextView.percent >= 85 ? "\n\n" : "\n";
|
|
177
165
|
const resetLines = outputLines.map((line) => `\x1b[0m${line}`);
|
|
@@ -180,7 +168,7 @@ async function main() {
|
|
|
180
168
|
|
|
181
169
|
main().catch(() => {
|
|
182
170
|
process.stdout.write(
|
|
183
|
-
`\x1b[0m${bold(claudeOrange("c"))}: ${dim(
|
|
171
|
+
`\x1b[0m${bold(claudeOrange("c"))}: ${dim(`5h:--% ${formatTimeCell("")} 1w:--% ${formatTimeCellDH("")} | CTX:--%`)}\n`,
|
|
184
172
|
);
|
|
185
173
|
});
|
|
186
174
|
|
package/hud/providers/gemini.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
1
3
|
import {
|
|
2
|
-
ANTIGRAVITY_MODEL_ABBREV,
|
|
3
4
|
ANTIGRAVITY_OAUTH_PATHS,
|
|
5
|
+
ANTIGRAVITY_QUOTA_CACHE_PATH,
|
|
6
|
+
ANTIGRAVITY_QUOTA_STALE_MS,
|
|
7
|
+
ANTIGRAVITY_REFRESH_FLAG,
|
|
8
|
+
ANTIGRAVITY_REFRESH_LOCK_PATH,
|
|
4
9
|
ANTIGRAVITY_SETTINGS_PATH,
|
|
5
|
-
|
|
10
|
+
ANTIGRAVITY_SIGNED_IN_GRACE_MS,
|
|
11
|
+
SPAWN_LOCK_TTL_MS,
|
|
6
12
|
} from "../constants.mjs";
|
|
7
|
-
import { decodeJwtEmail, readJson } from "../utils.mjs";
|
|
13
|
+
import { decodeJwtEmail, readJson, writeJsonSafe } from "../utils.mjs";
|
|
8
14
|
|
|
9
15
|
function getCredentialEmail(credential) {
|
|
10
16
|
return (
|
|
@@ -17,10 +23,6 @@ function getCredentialEmail(credential) {
|
|
|
17
23
|
);
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
export function getGeminiEmail() {
|
|
21
|
-
return getCredentialEmail(readJson(GEMINI_OAUTH_PATH, null));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
26
|
// GCP 프로젝트 인증이면 계정 이메일 대신 프로젝트 ID를 보여 준다.
|
|
25
27
|
export function getAntigravityAccountLabel() {
|
|
26
28
|
const project = readJson(ANTIGRAVITY_SETTINGS_PATH, null)?.gcp?.project;
|
|
@@ -36,20 +38,123 @@ export function getAntigravityAccountLabel() {
|
|
|
36
38
|
return null;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
function isGcpProjectAuth() {
|
|
42
|
+
return Boolean(readJson(ANTIGRAVITY_SETTINGS_PATH, null)?.gcp?.project);
|
|
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
|
+
|
|
57
|
+
export function readAntigravityQuotaSnapshot() {
|
|
58
|
+
// 프로젝트 과금의 기본 응답을 개인 플랜의 0% 사용률로 표시하지 않는다.
|
|
59
|
+
if (isGcpProjectAuth()) return { data: null, shouldRefresh: false };
|
|
60
|
+
const cache = readJson(ANTIGRAVITY_QUOTA_CACHE_PATH, null);
|
|
61
|
+
const matched = cache?.accountLabel === getAntigravityAccountLabel();
|
|
62
|
+
const stale =
|
|
63
|
+
!cache?.timestamp ||
|
|
64
|
+
Date.now() - cache.timestamp >= ANTIGRAVITY_QUOTA_STALE_MS;
|
|
65
|
+
const model = readJson(ANTIGRAVITY_SETTINGS_PATH, null)?.model;
|
|
66
|
+
const buckets = matched && Array.isArray(cache?.buckets) ? cache.buckets : [];
|
|
67
|
+
const bucket = buckets
|
|
68
|
+
.filter(
|
|
69
|
+
(item) =>
|
|
70
|
+
model &&
|
|
71
|
+
item &&
|
|
72
|
+
(item?.name === model || item?.id === model) &&
|
|
73
|
+
Number.isFinite(item.remaining_fraction) &&
|
|
74
|
+
item.remaining_fraction >= 0 &&
|
|
75
|
+
item.remaining_fraction <= 1 &&
|
|
76
|
+
Date.parse(item.reset_time) > Date.now(),
|
|
77
|
+
)
|
|
78
|
+
.sort((a, b) => a.remaining_fraction - b.remaining_fraction)[0];
|
|
79
|
+
return {
|
|
80
|
+
data: bucket
|
|
81
|
+
? {
|
|
82
|
+
usedPercent: Math.round((1 - bucket.remaining_fraction) * 100),
|
|
83
|
+
resetTime: bucket.reset_time,
|
|
84
|
+
stale,
|
|
85
|
+
}
|
|
86
|
+
: null,
|
|
87
|
+
shouldRefresh: !matched || stale,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// /usage는 모델 턴을 시작하지 않는 공식 print-mode 조회다.
|
|
92
|
+
export function refreshAntigravityQuotaCache(execFileSyncFn = execFileSync) {
|
|
93
|
+
if (isGcpProjectAuth()) return null;
|
|
94
|
+
const accountLabel = getAntigravityAccountLabel();
|
|
95
|
+
let buckets = null;
|
|
96
|
+
try {
|
|
97
|
+
const result = JSON.parse(
|
|
98
|
+
execFileSyncFn("agy", ["-p", "/usage", "--output-format", "json"], {
|
|
99
|
+
encoding: "utf8",
|
|
100
|
+
timeout: 15_000,
|
|
101
|
+
maxBuffer: 1024 * 1024,
|
|
102
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
103
|
+
windowsHide: true,
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
if (
|
|
107
|
+
result.status === "SUCCESS" &&
|
|
108
|
+
result.command?.name === "usage" &&
|
|
109
|
+
Array.isArray(result.command.data?.groups)
|
|
110
|
+
) {
|
|
111
|
+
buckets = result.command.data.groups.flatMap((group) =>
|
|
112
|
+
Array.isArray(group?.buckets) ? group.buckets : [],
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
// 실패도 캐시해 인증 오류나 CLI 부재 시 매 렌더마다 실행하지 않는다.
|
|
117
|
+
}
|
|
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;
|
|
125
|
+
writeJsonSafe(ANTIGRAVITY_QUOTA_CACHE_PATH, {
|
|
126
|
+
timestamp: Date.now(),
|
|
127
|
+
accountLabel,
|
|
128
|
+
buckets,
|
|
129
|
+
signedInAt,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return buckets;
|
|
42
133
|
}
|
|
43
134
|
|
|
44
|
-
export function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return
|
|
135
|
+
export function scheduleAntigravityQuotaRefresh() {
|
|
136
|
+
const scriptPath = process.argv[1];
|
|
137
|
+
if (
|
|
138
|
+
!scriptPath ||
|
|
139
|
+
!existsSync(ANTIGRAVITY_SETTINGS_PATH) ||
|
|
140
|
+
isGcpProjectAuth()
|
|
141
|
+
)
|
|
142
|
+
return;
|
|
143
|
+
const lock = readJson(ANTIGRAVITY_REFRESH_LOCK_PATH, null);
|
|
144
|
+
if (Date.now() - Number(lock?.t || 0) < SPAWN_LOCK_TTL_MS) return;
|
|
145
|
+
writeJsonSafe(ANTIGRAVITY_REFRESH_LOCK_PATH, { t: Date.now() });
|
|
146
|
+
try {
|
|
147
|
+
const child = spawn(
|
|
148
|
+
process.execPath,
|
|
149
|
+
[scriptPath, ANTIGRAVITY_REFRESH_FLAG],
|
|
150
|
+
{
|
|
151
|
+
detached: true,
|
|
152
|
+
stdio: "ignore",
|
|
153
|
+
windowsHide: true,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
child.unref();
|
|
157
|
+
} catch {
|
|
158
|
+
// 다음 렌더에서 다시 시도한다.
|
|
52
159
|
}
|
|
53
|
-
if (head) return head[1].slice(0, 2);
|
|
54
|
-
return "??";
|
|
55
160
|
}
|