@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1
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/LICENSE +57744 -0
- package/native-manifest.json +1414 -0
- package/package.json +254 -0
- package/plugins/abacus-ai/icon.svg +18 -0
- package/plugins/abacus-ai/plugin.js +121 -0
- package/plugins/abacus-ai/plugin.json +17 -0
- package/plugins/alibaba/icon-color.svg +1 -0
- package/plugins/alibaba/icon.svg +1 -0
- package/plugins/alibaba/plugin.js +322 -0
- package/plugins/alibaba/plugin.json +20 -0
- package/plugins/alibaba-token-plan/icon-color.svg +1 -0
- package/plugins/alibaba-token-plan/icon.svg +1 -0
- package/plugins/alibaba-token-plan/plugin.js +544 -0
- package/plugins/alibaba-token-plan/plugin.json +19 -0
- package/plugins/amp/icon-color.svg +1 -0
- package/plugins/amp/icon-conflict.svg +6 -0
- package/plugins/amp/icon.svg +1 -0
- package/plugins/amp/plugin.js +175 -0
- package/plugins/amp/plugin.json +16 -0
- package/plugins/antigravity/icon-color.svg +1 -0
- package/plugins/antigravity/icon-conflict.svg +3 -0
- package/plugins/antigravity/icon.svg +1 -0
- package/plugins/antigravity/plugin.js +739 -0
- package/plugins/antigravity/plugin.json +31 -0
- package/plugins/antigravity-cli/icon.svg +3 -0
- package/plugins/antigravity-cli/plugin.js +570 -0
- package/plugins/antigravity-cli/plugin.json +16 -0
- package/plugins/antigravity-ide/icon.svg +3 -0
- package/plugins/antigravity-ide/plugin.js +257 -0
- package/plugins/antigravity-ide/plugin.json +29 -0
- package/plugins/augment/icon.svg +14 -0
- package/plugins/augment/plugin.js +59 -0
- package/plugins/augment/plugin.json +18 -0
- package/plugins/aws-bedrock/icon-color.svg +1 -0
- package/plugins/aws-bedrock/icon.svg +1 -0
- package/plugins/aws-bedrock/plugin.js +206 -0
- package/plugins/aws-bedrock/plugin.json +18 -0
- package/plugins/azure-openai/icon-color.svg +1 -0
- package/plugins/azure-openai/icon.svg +1 -0
- package/plugins/azure-openai/plugin.js +145 -0
- package/plugins/azure-openai/plugin.json +17 -0
- package/plugins/chutes/icon.svg +7 -0
- package/plugins/chutes/plugin.js +236 -0
- package/plugins/chutes/plugin.json +19 -0
- package/plugins/claude/icon-color.svg +1 -0
- package/plugins/claude/icon.svg +1 -0
- package/plugins/claude/plugin.js +1723 -0
- package/plugins/claude/plugin.json +30 -0
- package/plugins/codebuff/icon.svg +92 -0
- package/plugins/codebuff/plugin.js +113 -0
- package/plugins/codebuff/plugin.json +17 -0
- package/plugins/codex/icon-color.svg +5 -0
- package/plugins/codex/icon.svg +1 -0
- package/plugins/codex/plugin.js +1009 -0
- package/plugins/codex/plugin.json +82 -0
- package/plugins/command-code/icon.svg +3 -0
- package/plugins/command-code/plugin.js +184 -0
- package/plugins/command-code/plugin.json +20 -0
- package/plugins/copilot/icon-color.svg +1 -0
- package/plugins/copilot/icon.svg +1 -0
- package/plugins/copilot/plugin.js +684 -0
- package/plugins/copilot/plugin.json +21 -0
- package/plugins/crof/icon.svg +3 -0
- package/plugins/crof/plugin.js +53 -0
- package/plugins/crof/plugin.json +17 -0
- package/plugins/crofai/icon.svg +16 -0
- package/plugins/crofai/plugin.js +81 -0
- package/plugins/crofai/plugin.json +18 -0
- package/plugins/cursor/icon-conflict.svg +3 -0
- package/plugins/cursor/icon.svg +1 -0
- package/plugins/cursor/plugin.js +1379 -0
- package/plugins/cursor/plugin.json +23 -0
- package/plugins/cursor-nightly/icon.png +0 -0
- package/plugins/cursor-nightly/plugin.js +1379 -0
- package/plugins/cursor-nightly/plugin.json +23 -0
- package/plugins/deepgram/icon.svg +1 -0
- package/plugins/deepgram/plugin.js +20 -0
- package/plugins/deepgram/plugin.json +17 -0
- package/plugins/deepseek/icon-color.svg +1 -0
- package/plugins/deepseek/icon.svg +1 -0
- package/plugins/deepseek/plugin.js +71 -0
- package/plugins/deepseek/plugin.json +17 -0
- package/plugins/devin/icon.svg +5 -0
- package/plugins/devin/plugin.js +341 -0
- package/plugins/devin/plugin.json +16 -0
- package/plugins/doubao/icon-color.svg +1 -0
- package/plugins/doubao/icon.svg +1 -0
- package/plugins/doubao/plugin.js +92 -0
- package/plugins/doubao/plugin.json +17 -0
- package/plugins/droid/icon.svg +1 -0
- package/plugins/droid/plugin.json +20 -0
- package/plugins/elevenlabs/icon.svg +4 -0
- package/plugins/elevenlabs/plugin.js +48 -0
- package/plugins/elevenlabs/plugin.json +18 -0
- package/plugins/factory/icon-conflict.svg +1 -0
- package/plugins/factory/icon.svg +1 -0
- package/plugins/factory/plugin.js +487 -0
- package/plugins/factory/plugin.json +15 -0
- package/plugins/fireworks-ai/icon.svg +6 -0
- package/plugins/fireworks-ai/plugin.js +370 -0
- package/plugins/fireworks-ai/plugin.json +19 -0
- package/plugins/gemini/icon-color.svg +1 -0
- package/plugins/gemini/icon.svg +1 -0
- package/plugins/gemini/plugin.js +460 -0
- package/plugins/gemini/plugin.json +16 -0
- package/plugins/grok/icon.svg +1 -0
- package/plugins/grok/plugin.js +297 -0
- package/plugins/grok/plugin.json +19 -0
- package/plugins/groqcloud/icon.svg +1 -0
- package/plugins/groqcloud/plugin.js +117 -0
- package/plugins/groqcloud/plugin.json +19 -0
- package/plugins/jetbrains-ai-assistant/icon-color.svg +14 -0
- package/plugins/jetbrains-ai-assistant/icon-conflict.svg +3 -0
- package/plugins/jetbrains-ai-assistant/icon.svg +14 -0
- package/plugins/jetbrains-ai-assistant/plugin.js +313 -0
- package/plugins/jetbrains-ai-assistant/plugin.json +16 -0
- package/plugins/kilo/icon.svg +1 -0
- package/plugins/kilo/plugin.js +111 -0
- package/plugins/kilo/plugin.json +19 -0
- package/plugins/kimi/icon-color.svg +1 -0
- package/plugins/kimi/icon-conflict.svg +3 -0
- package/plugins/kimi/icon.svg +1 -0
- package/plugins/kimi/plugin.js +358 -0
- package/plugins/kimi/plugin.json +15 -0
- package/plugins/kimi-k2/icon-color.svg +1 -0
- package/plugins/kimi-k2/icon.svg +1 -0
- package/plugins/kimi-k2/plugin.js +58 -0
- package/plugins/kimi-k2/plugin.json +18 -0
- package/plugins/kiro/icon-color.svg +5 -0
- package/plugins/kiro/icon-conflict.svg +1 -0
- package/plugins/kiro/icon.svg +4 -0
- package/plugins/kiro/plugin.js +390 -0
- package/plugins/kiro/plugin.json +16 -0
- package/plugins/litellm/icon.svg +16 -0
- package/plugins/litellm/plugin.js +199 -0
- package/plugins/litellm/plugin.json +16 -0
- package/plugins/llm-proxy/icon.svg +1 -0
- package/plugins/llm-proxy/plugin.js +40 -0
- package/plugins/llm-proxy/plugin.json +17 -0
- package/plugins/manus/icon.svg +6 -0
- package/plugins/manus/plugin.js +50 -0
- package/plugins/manus/plugin.json +17 -0
- package/plugins/mimo/icon.svg +1 -0
- package/plugins/mimo/plugin.js +177 -0
- package/plugins/mimo/plugin.json +19 -0
- package/plugins/minimax/icon-color.svg +1 -0
- package/plugins/minimax/icon-conflict.svg +4 -0
- package/plugins/minimax/icon.svg +1 -0
- package/plugins/minimax/plugin.js +453 -0
- package/plugins/minimax/plugin.json +14 -0
- package/plugins/mistral/icon-color.svg +1 -0
- package/plugins/mistral/icon.svg +1 -0
- package/plugins/mistral/plugin.js +152 -0
- package/plugins/mistral/plugin.json +18 -0
- package/plugins/moonshot/icon.svg +1 -0
- package/plugins/moonshot/plugin.js +43 -0
- package/plugins/moonshot/plugin.json +16 -0
- package/plugins/nanogpt/icon-color.svg +17 -0
- package/plugins/nanogpt/icon.svg +10 -0
- package/plugins/nanogpt/plugin.js +80 -0
- package/plugins/nanogpt/plugin.json +17 -0
- package/plugins/neuralwatt/icon.svg +3 -0
- package/plugins/neuralwatt/plugin.js +152 -0
- package/plugins/neuralwatt/plugin.json +17 -0
- package/plugins/ollama/icon.svg +1 -0
- package/plugins/ollama/plugin.js +92 -0
- package/plugins/ollama/plugin.json +20 -0
- package/plugins/openai-api/icon.svg +1 -0
- package/plugins/openai-api/plugin.js +533 -0
- package/plugins/openai-api/plugin.json +26 -0
- package/plugins/opencode/icon.svg +1 -0
- package/plugins/opencode/plugin.js +342 -0
- package/plugins/opencode/plugin.json +19 -0
- package/plugins/opencode-go/icon-conflict.svg +1 -0
- package/plugins/opencode-go/icon.svg +1 -0
- package/plugins/opencode-go/plugin.js +617 -0
- package/plugins/opencode-go/plugin.json +50 -0
- package/plugins/openrouter/icon.svg +1 -0
- package/plugins/openrouter/plugin.js +156 -0
- package/plugins/openrouter/plugin.json +8 -0
- package/plugins/perplexity/icon-color.svg +1 -0
- package/plugins/perplexity/icon-conflict.svg +1 -0
- package/plugins/perplexity/icon.svg +1 -0
- package/plugins/perplexity/plugin.js +425 -0
- package/plugins/perplexity/plugin.json +19 -0
- package/plugins/poe/icon.svg +1 -0
- package/plugins/poe/plugin.js +190 -0
- package/plugins/poe/plugin.json +19 -0
- package/plugins/stepfun/icon-color.svg +1 -0
- package/plugins/stepfun/icon.svg +1 -0
- package/plugins/stepfun/plugin.js +155 -0
- package/plugins/stepfun/plugin.json +18 -0
- package/plugins/synthetic/icon-conflict.svg +12 -0
- package/plugins/synthetic/icon.svg +14 -0
- package/plugins/synthetic/plugin.js +331 -0
- package/plugins/synthetic/plugin.json +23 -0
- package/plugins/t3chat/icon-color.svg +28 -0
- package/plugins/t3chat/icon.svg +4 -0
- package/plugins/t3chat/plugin.js +243 -0
- package/plugins/t3chat/plugin.json +20 -0
- package/plugins/venice/icon-color.svg +1 -0
- package/plugins/venice/icon.svg +1 -0
- package/plugins/venice/plugin.js +69 -0
- package/plugins/venice/plugin.json +16 -0
- package/plugins/vertex-ai/icon-color.svg +1 -0
- package/plugins/vertex-ai/icon.svg +1 -0
- package/plugins/vertex-ai/plugin.js +172 -0
- package/plugins/vertex-ai/plugin.json +18 -0
- package/plugins/warp/icon-color.svg +10 -0
- package/plugins/warp/icon.svg +6 -0
- package/plugins/warp/plugin.js +108 -0
- package/plugins/warp/plugin.json +18 -0
- package/plugins/windsurf/icon-conflict.svg +3 -0
- package/plugins/windsurf/icon.svg +1 -0
- package/plugins/windsurf/plugin.js +208 -0
- package/plugins/windsurf/plugin.json +16 -0
- package/plugins/zai/icon-conflict.svg +5 -0
- package/plugins/zai/icon.svg +1 -0
- package/plugins/zai/plugin.js +188 -0
- package/plugins/zai/plugin.json +16 -0
- package/plugins/zed/icon.svg +4 -0
- package/plugins/zed/plugin.js +270 -0
- package/plugins/zed/plugin.json +19 -0
- package/usagestat +0 -0
- package/usagestatd +0 -0
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var LS_SERVICE = "exa.language_server_pb.LanguageServerService"
|
|
3
|
+
var AGY_KEYCHAIN_SERVICE = "gemini"
|
|
4
|
+
var AGY_KEYCHAIN_ACCOUNT = "antigravity"
|
|
5
|
+
var CLOUD_CODE_URLS = [
|
|
6
|
+
"https://daily-cloudcode-pa.googleapis.com",
|
|
7
|
+
"https://cloudcode-pa.googleapis.com",
|
|
8
|
+
]
|
|
9
|
+
var LOAD_CODE_ASSIST_PATH = "/v1internal:loadCodeAssist"
|
|
10
|
+
var FETCH_MODELS_PATH = "/v1internal:fetchAvailableModels"
|
|
11
|
+
var RETRIEVE_QUOTA_PATH = "/v1internal:retrieveUserQuota"
|
|
12
|
+
var LOGIN_MESSAGE = "Start Antigravity or run `agy` and try again."
|
|
13
|
+
var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token"
|
|
14
|
+
var GOOGLE_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
|
|
15
|
+
var GOOGLE_CLIENT_SECRET = "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"
|
|
16
|
+
var OAUTH_TOKEN_KEY = "antigravityUnifiedStateSync.oauthToken"
|
|
17
|
+
var OAUTH_TOKEN_SENTINEL = "oauthTokenInfoSentinelKey"
|
|
18
|
+
var CC_MODEL_BLACKLIST = {
|
|
19
|
+
"MODEL_CHAT_20706": true,
|
|
20
|
+
"MODEL_CHAT_23310": true,
|
|
21
|
+
"MODEL_GOOGLE_GEMINI_2_5_FLASH": true,
|
|
22
|
+
"MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING": true,
|
|
23
|
+
"MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE": true,
|
|
24
|
+
"MODEL_GOOGLE_GEMINI_2_5_PRO": true,
|
|
25
|
+
"MODEL_PLACEHOLDER_M19": true,
|
|
26
|
+
"MODEL_PLACEHOLDER_M9": true,
|
|
27
|
+
"MODEL_PLACEHOLDER_M12": true,
|
|
28
|
+
}
|
|
29
|
+
// --- Protobuf wire-format decoder ---
|
|
30
|
+
|
|
31
|
+
function readVarint(s, pos) {
|
|
32
|
+
var v = 0
|
|
33
|
+
var shift = 0
|
|
34
|
+
while (pos < s.length) {
|
|
35
|
+
var b = s.charCodeAt(pos++)
|
|
36
|
+
v += (b & 0x7f) * Math.pow(2, shift)
|
|
37
|
+
if ((b & 0x80) === 0) return { v: v, p: pos }
|
|
38
|
+
shift += 7
|
|
39
|
+
}
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readFields(s) {
|
|
44
|
+
var fields = {}
|
|
45
|
+
var pos = 0
|
|
46
|
+
while (pos < s.length) {
|
|
47
|
+
var tag = readVarint(s, pos)
|
|
48
|
+
if (!tag) break
|
|
49
|
+
pos = tag.p
|
|
50
|
+
var fieldNum = Math.floor(tag.v / 8)
|
|
51
|
+
var wireType = tag.v % 8
|
|
52
|
+
if (wireType === 0) {
|
|
53
|
+
var val = readVarint(s, pos)
|
|
54
|
+
if (!val) break
|
|
55
|
+
fields[fieldNum] = { type: 0, value: val.v }
|
|
56
|
+
pos = val.p
|
|
57
|
+
} else if (wireType === 1) {
|
|
58
|
+
if (pos + 8 > s.length) break
|
|
59
|
+
pos += 8
|
|
60
|
+
} else if (wireType === 2) {
|
|
61
|
+
var len = readVarint(s, pos)
|
|
62
|
+
if (!len) break
|
|
63
|
+
pos = len.p
|
|
64
|
+
if (pos + len.v > s.length) break
|
|
65
|
+
fields[fieldNum] = { type: 2, data: s.substring(pos, pos + len.v) }
|
|
66
|
+
pos += len.v
|
|
67
|
+
} else if (wireType === 5) {
|
|
68
|
+
if (pos + 4 > s.length) break
|
|
69
|
+
pos += 4
|
|
70
|
+
} else {
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return fields
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// --- SQLite credential reading ---
|
|
78
|
+
|
|
79
|
+
function providerSettings(ctx) { return (ctx.provider && ctx.provider.settings) || {} }
|
|
80
|
+
|
|
81
|
+
function antigravityStateDbPaths(ctx) {
|
|
82
|
+
var opts = providerSettings(ctx)
|
|
83
|
+
if (opts.userDataDir) return [opts.userDataDir.replace(/[\\/]+$/, "") + "/User/globalStorage/state.vscdb"]
|
|
84
|
+
var variants = {"antigravity": "Antigravity", "antigravity-ide": "Antigravity IDE"}
|
|
85
|
+
if (opts.ideVariant && !variants[opts.ideVariant]) throw {code: "failed", message: "Unknown Antigravity ideVariant; use antigravity or antigravity-ide."}
|
|
86
|
+
return (opts.ideVariant ? [opts.ideVariant] : Object.keys(variants)).map(function(id) {
|
|
87
|
+
return ctx.host.fs.appSupportPath(variants[id] + "/User/globalStorage/state.vscdb")
|
|
88
|
+
}).filter(function(path) { return !!path && ctx.host.fs.exists(path) })
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Antigravity wraps OAuth state in a double-base64 envelope:
|
|
92
|
+
// b64(outer.f1 = wrapper{ f1=sentinel, f2=payload{ f1=b64(inner proto) } }).
|
|
93
|
+
// The inner base64 layer is the unusual part - it's a UTF-8 string field, not raw bytes.
|
|
94
|
+
function unwrapOAuthSentinel(ctx, base64Text) {
|
|
95
|
+
var trimmed = String(base64Text || "").replace(/^\s+|\s+$/g, "")
|
|
96
|
+
if (!trimmed) return null
|
|
97
|
+
var outer = ctx.base64.decode(trimmed)
|
|
98
|
+
var outerFields = readFields(outer)
|
|
99
|
+
if (!outerFields[1] || outerFields[1].type !== 2) return null
|
|
100
|
+
var wrapper = readFields(outerFields[1].data)
|
|
101
|
+
var sentinel = (wrapper[1] && wrapper[1].type === 2) ? wrapper[1].data : null
|
|
102
|
+
var payload = (wrapper[2] && wrapper[2].type === 2) ? wrapper[2].data : null
|
|
103
|
+
if (sentinel !== OAUTH_TOKEN_SENTINEL || !payload) return null
|
|
104
|
+
var payloadFields = readFields(payload)
|
|
105
|
+
if (!payloadFields[1] || payloadFields[1].type !== 2) return null
|
|
106
|
+
var innerText = payloadFields[1].data.replace(/^\s+|\s+$/g, "")
|
|
107
|
+
if (!innerText) return null
|
|
108
|
+
return ctx.base64.decode(innerText)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function loadOAuthTokensFromDb(ctx, dbPath) {
|
|
112
|
+
try {
|
|
113
|
+
var rows = ctx.host.sqlite.query(
|
|
114
|
+
dbPath,
|
|
115
|
+
"SELECT value FROM ItemTable WHERE key = '" + OAUTH_TOKEN_KEY + "' LIMIT 1"
|
|
116
|
+
)
|
|
117
|
+
var parsed = ctx.util.tryParseJson(rows)
|
|
118
|
+
if (!parsed || !parsed.length || !parsed[0].value) return null
|
|
119
|
+
var inner = unwrapOAuthSentinel(ctx, parsed[0].value)
|
|
120
|
+
if (!inner) return null
|
|
121
|
+
var fields = readFields(inner)
|
|
122
|
+
var accessToken = (fields[1] && fields[1].type === 2) ? fields[1].data : null
|
|
123
|
+
var refreshToken = (fields[3] && fields[3].type === 2) ? fields[3].data : null
|
|
124
|
+
var expirySeconds = null
|
|
125
|
+
if (fields[4] && fields[4].type === 2) {
|
|
126
|
+
var ts = readFields(fields[4].data)
|
|
127
|
+
if (ts[1] && ts[1].type === 0) expirySeconds = ts[1].value
|
|
128
|
+
}
|
|
129
|
+
if (!accessToken && !refreshToken) return null
|
|
130
|
+
return { accessToken: accessToken, refreshToken: refreshToken, expirySeconds: expirySeconds }
|
|
131
|
+
} catch (e) {
|
|
132
|
+
ctx.host.log.warn("failed to read unified oauth token from " + dbPath + ": " + String(e))
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function loadOAuthTokenCandidates(ctx) {
|
|
138
|
+
var candidates = []
|
|
139
|
+
var paths = antigravityStateDbPaths(ctx)
|
|
140
|
+
for (var i = 0; i < paths.length; i++) {
|
|
141
|
+
var tokens = loadOAuthTokensFromDb(ctx, paths[i])
|
|
142
|
+
if (tokens) {
|
|
143
|
+
tokens.profileKey = ctx.host.crypto.sha256Hex(paths[i] + "\n" + (tokens.refreshToken || tokens.accessToken))
|
|
144
|
+
candidates.push(tokens)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (candidates.length > 1) throw {code: "failed", message: "Multiple Antigravity credential profiles found; set settings.ideVariant or userDataDir."}
|
|
148
|
+
return candidates
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- Google OAuth token refresh ---
|
|
152
|
+
|
|
153
|
+
function refreshAccessToken(ctx, refreshTokenValue, profileKey) {
|
|
154
|
+
if (!refreshTokenValue) {
|
|
155
|
+
ctx.host.log.warn("refresh skipped: no refresh token")
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
ctx.host.log.info("attempting Google OAuth token refresh")
|
|
159
|
+
try {
|
|
160
|
+
var resp = ctx.host.http.request({
|
|
161
|
+
method: "POST",
|
|
162
|
+
url: GOOGLE_OAUTH_URL,
|
|
163
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
164
|
+
bodyText:
|
|
165
|
+
"client_id=" + encodeURIComponent(GOOGLE_CLIENT_ID) +
|
|
166
|
+
"&client_secret=" + encodeURIComponent(GOOGLE_CLIENT_SECRET) +
|
|
167
|
+
"&refresh_token=" + encodeURIComponent(refreshTokenValue) +
|
|
168
|
+
"&grant_type=refresh_token",
|
|
169
|
+
timeoutMs: 15000,
|
|
170
|
+
})
|
|
171
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
172
|
+
ctx.host.log.warn("Google OAuth refresh returned status: " + resp.status)
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
var body = ctx.util.tryParseJson(resp.bodyText)
|
|
176
|
+
if (!body || !body.access_token) {
|
|
177
|
+
ctx.host.log.warn("Google OAuth refresh response missing access_token")
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
var expiresIn = (typeof body.expires_in === "number") ? body.expires_in : 3600
|
|
181
|
+
cacheToken(ctx, body.access_token, expiresIn, profileKey)
|
|
182
|
+
return body.access_token
|
|
183
|
+
} catch (e) {
|
|
184
|
+
ctx.host.log.warn("Google OAuth refresh failed: " + String(e))
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// --- Token cache ---
|
|
190
|
+
|
|
191
|
+
function loadCachedToken(ctx, profileKey) {
|
|
192
|
+
var path = ctx.app.pluginDataDir + "/auth.json"
|
|
193
|
+
try {
|
|
194
|
+
if (!ctx.host.fs.exists(path)) return null
|
|
195
|
+
var data = ctx.util.tryParseJson(ctx.host.fs.readText(path))
|
|
196
|
+
if (!data || !profileKey || data.profileKey !== profileKey || !data.accessToken || !data.expiresAtMs) return null
|
|
197
|
+
if (data.expiresAtMs <= Date.now()) return null
|
|
198
|
+
return data.accessToken
|
|
199
|
+
} catch (e) {
|
|
200
|
+
ctx.host.log.warn("failed to read cached token: " + String(e))
|
|
201
|
+
return null
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function cacheToken(ctx, accessToken, expiresInSeconds, profileKey) {
|
|
206
|
+
var path = ctx.app.pluginDataDir + "/auth.json"
|
|
207
|
+
try {
|
|
208
|
+
ctx.host.fs.writeText(path, JSON.stringify({
|
|
209
|
+
accessToken: accessToken,
|
|
210
|
+
profileKey: profileKey,
|
|
211
|
+
expiresAtMs: Date.now() + (expiresInSeconds || 3600) * 1000,
|
|
212
|
+
}))
|
|
213
|
+
} catch (e) {
|
|
214
|
+
ctx.host.log.warn("failed to cache refreshed token: " + String(e))
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// --- agy keychain token ---
|
|
219
|
+
|
|
220
|
+
function trimString(value) {
|
|
221
|
+
return typeof value === "string" ? value.trim() : ""
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function decodeBase64(ctx, text) {
|
|
225
|
+
try {
|
|
226
|
+
return ctx.base64.decode(text)
|
|
227
|
+
} catch (e) {
|
|
228
|
+
return null
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function unwrapAgyKeychainText(ctx, raw) {
|
|
233
|
+
var text = trimString(raw)
|
|
234
|
+
if (!text) return null
|
|
235
|
+
if (text.indexOf("go-keyring-base64:") === 0) {
|
|
236
|
+
text = trimString(decodeBase64(ctx, text.slice("go-keyring-base64:".length)))
|
|
237
|
+
}
|
|
238
|
+
return text || null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function extractTokenFromObject(obj) {
|
|
242
|
+
if (!obj || typeof obj !== "object") return null
|
|
243
|
+
|
|
244
|
+
var directKeys = [
|
|
245
|
+
"access_token",
|
|
246
|
+
"accessToken",
|
|
247
|
+
"token",
|
|
248
|
+
"id_token",
|
|
249
|
+
"idToken",
|
|
250
|
+
"bearerToken",
|
|
251
|
+
"auth_token",
|
|
252
|
+
"authToken",
|
|
253
|
+
]
|
|
254
|
+
for (var i = 0; i < directKeys.length; i++) {
|
|
255
|
+
var value = obj[directKeys[i]]
|
|
256
|
+
if (typeof value === "string" && value.trim()) return value.trim()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
var nestedKeys = ["token", "tokens", "oauth", "oauth2", "credentials", "auth"]
|
|
260
|
+
for (var j = 0; j < nestedKeys.length; j++) {
|
|
261
|
+
var nested = extractTokenFromObject(obj[nestedKeys[j]])
|
|
262
|
+
if (nested) return nested
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return null
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function extractAgyAccessToken(ctx, raw) {
|
|
269
|
+
var text = unwrapAgyKeychainText(ctx, raw)
|
|
270
|
+
if (!text) return null
|
|
271
|
+
|
|
272
|
+
var parsed = ctx.util.tryParseJson(text)
|
|
273
|
+
if (typeof parsed === "string" && parsed.trim()) return parsed.trim()
|
|
274
|
+
if (parsed) return extractTokenFromObject(parsed)
|
|
275
|
+
|
|
276
|
+
if (text.indexOf("Bearer ") === 0) return text.slice("Bearer ".length).trim() || null
|
|
277
|
+
return text
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function loadAgyKeychainToken(ctx) {
|
|
281
|
+
if (!ctx.host.keychain || typeof ctx.host.keychain.readGenericPassword !== "function") {
|
|
282
|
+
return null
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
var raw = ctx.host.keychain.readGenericPassword(AGY_KEYCHAIN_SERVICE, AGY_KEYCHAIN_ACCOUNT)
|
|
286
|
+
return extractAgyAccessToken(ctx, raw)
|
|
287
|
+
} catch (e) {
|
|
288
|
+
ctx.host.log.info("agy keychain read failed: " + String(e))
|
|
289
|
+
return null
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// --- LS discovery ---
|
|
294
|
+
|
|
295
|
+
function discoverNative(ctx, options) {
|
|
296
|
+
if (typeof ctx.host.ls.discoverStatus !== "function") return ctx.host.ls.discover(options)
|
|
297
|
+
var report = ctx.host.ls.discoverStatus(options)
|
|
298
|
+
if (report.status === "ready") return report.result
|
|
299
|
+
if (report.status === "missing") return null
|
|
300
|
+
if (report.status === "ambiguous") throw {code: "failed", message: "Multiple Antigravity processes found; select settings.ideProcessId or use the separate CLI provider."}
|
|
301
|
+
throw {code: report.status === "unsupported" ? "unsupported" : report.reasonCode === "ide-discovery-timed-out" ? "timed-out" : "failed", message: "Antigravity process discovery is unavailable; run the backend and app as the same signed-in user."}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function discoverLs(ctx) {
|
|
305
|
+
var options = {processName: "language_server", markers: ["antigravity", "antigravity-ide"], csrfFlag: "--csrf_token", portFlag: "--extension_server_port"}
|
|
306
|
+
var pid = providerSettings(ctx).ideProcessId
|
|
307
|
+
if (pid != null) {
|
|
308
|
+
if (!Number.isInteger(Number(pid)) || Number(pid) <= 0 || Number(pid) > 4294967295) throw {code: "failed", message: "Invalid Antigravity ideProcessId."}
|
|
309
|
+
options.pid = Number(pid)
|
|
310
|
+
}
|
|
311
|
+
return discoverNative(ctx, options)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function discoverAgyLs(ctx) {
|
|
315
|
+
return discoverNative(ctx, {processName: "agy", markers: [], csrfFlag: "", portFlag: null})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function probePort(ctx, scheme, port, csrf) {
|
|
319
|
+
ctx.host.http.request({
|
|
320
|
+
method: "POST",
|
|
321
|
+
url: scheme + "://127.0.0.1:" + port + "/" + LS_SERVICE + "/GetUnleashData",
|
|
322
|
+
headers: {
|
|
323
|
+
"Content-Type": "application/json",
|
|
324
|
+
"Connect-Protocol-Version": "1",
|
|
325
|
+
"x-codeium-csrf-token": csrf,
|
|
326
|
+
},
|
|
327
|
+
bodyText: JSON.stringify({
|
|
328
|
+
context: {
|
|
329
|
+
properties: {
|
|
330
|
+
devMode: "false",
|
|
331
|
+
extensionVersion: "unknown",
|
|
332
|
+
ide: "antigravity",
|
|
333
|
+
ideVersion: "unknown",
|
|
334
|
+
os: "macos",
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
}),
|
|
338
|
+
timeoutMs: 5000,
|
|
339
|
+
dangerouslyIgnoreTls: scheme === "https",
|
|
340
|
+
})
|
|
341
|
+
// Any HTTP response means this port is alive (even 400 validation errors).
|
|
342
|
+
return true
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function findWorkingPort(ctx, discovery) {
|
|
346
|
+
var ports = discovery.ports || []
|
|
347
|
+
for (var i = 0; i < ports.length; i++) {
|
|
348
|
+
var port = ports[i]
|
|
349
|
+
// Try HTTPS first (LS may use self-signed cert), then HTTP
|
|
350
|
+
try { if (probePort(ctx, "https", port, discovery.csrf)) return { port: port, scheme: "https" } } catch (e) { /* ignore */ }
|
|
351
|
+
try { if (probePort(ctx, "http", port, discovery.csrf)) return { port: port, scheme: "http" } } catch (e) { /* ignore */ }
|
|
352
|
+
ctx.host.log.info("port " + port + " probe failed on both schemes")
|
|
353
|
+
}
|
|
354
|
+
if (discovery.extensionPort) return { port: discovery.extensionPort, scheme: "http" }
|
|
355
|
+
return null
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function callLs(ctx, port, scheme, csrf, method, body) {
|
|
359
|
+
var resp = ctx.host.http.request({
|
|
360
|
+
method: "POST",
|
|
361
|
+
url: scheme + "://127.0.0.1:" + port + "/" + LS_SERVICE + "/" + method,
|
|
362
|
+
headers: {
|
|
363
|
+
"Content-Type": "application/json",
|
|
364
|
+
"Connect-Protocol-Version": "1",
|
|
365
|
+
"x-codeium-csrf-token": csrf,
|
|
366
|
+
},
|
|
367
|
+
bodyText: JSON.stringify(body || {}),
|
|
368
|
+
timeoutMs: 10000,
|
|
369
|
+
dangerouslyIgnoreTls: scheme === "https",
|
|
370
|
+
})
|
|
371
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
372
|
+
ctx.host.log.warn("callLs " + method + " returned " + resp.status)
|
|
373
|
+
return null
|
|
374
|
+
}
|
|
375
|
+
return ctx.util.tryParseJson(resp.bodyText)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// --- Line builders ---
|
|
379
|
+
|
|
380
|
+
function normalizeLabel(label) {
|
|
381
|
+
// "Gemini 3 Pro (High)" -> "Gemini 3 Pro"
|
|
382
|
+
return label.replace(/\s*\([^)]*\)\s*$/, "").trim()
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function poolLabel(normalizedLabel) {
|
|
386
|
+
var lower = normalizedLabel.toLowerCase()
|
|
387
|
+
if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "Gemini Pro"
|
|
388
|
+
if (lower.indexOf("gemini") !== -1 && lower.indexOf("flash") !== -1) return "Gemini Flash"
|
|
389
|
+
// All non-Gemini models (Claude, GPT-OSS, etc.) share a single quota pool
|
|
390
|
+
return "Claude"
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function modelSortKey(label) {
|
|
394
|
+
var lower = label.toLowerCase()
|
|
395
|
+
// Gemini Pro variants first, then other Gemini, then Claude Opus, then other Claude, then rest
|
|
396
|
+
if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "0a_" + label
|
|
397
|
+
if (lower.indexOf("gemini") !== -1) return "0b_" + label
|
|
398
|
+
if (lower.indexOf("claude") !== -1 && lower.indexOf("opus") !== -1) return "1a_" + label
|
|
399
|
+
if (lower.indexOf("claude") !== -1) return "1b_" + label
|
|
400
|
+
return "2_" + label
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
var QUOTA_PERIOD_MS = 5 * 60 * 60 * 1000 // 5 hours
|
|
404
|
+
|
|
405
|
+
function modelLine(ctx, label, remainingFraction, resetTime) {
|
|
406
|
+
var clamped = Math.max(0, Math.min(1, remainingFraction))
|
|
407
|
+
var used = Math.round((1 - clamped) * 100)
|
|
408
|
+
return ctx.line.progress({
|
|
409
|
+
label: label,
|
|
410
|
+
used: used,
|
|
411
|
+
limit: 100,
|
|
412
|
+
format: { kind: "percent" },
|
|
413
|
+
resetsAt: resetTime || undefined,
|
|
414
|
+
periodDurationMs: QUOTA_PERIOD_MS,
|
|
415
|
+
})
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function buildModelLines(ctx, configs) {
|
|
419
|
+
var deduped = {}
|
|
420
|
+
for (var i = 0; i < configs.length; i++) {
|
|
421
|
+
var c = configs[i]
|
|
422
|
+
var label = (typeof c.label === "string") ? c.label.trim() : ""
|
|
423
|
+
if (!label) continue
|
|
424
|
+
var qi = c.quotaInfo
|
|
425
|
+
var frac = (qi && typeof qi.remainingFraction === "number") ? qi.remainingFraction : 0
|
|
426
|
+
var rtime = (qi && qi.resetTime) || undefined
|
|
427
|
+
var pool = poolLabel(normalizeLabel(label))
|
|
428
|
+
if (!deduped[pool] || frac < deduped[pool].remainingFraction) {
|
|
429
|
+
deduped[pool] = {
|
|
430
|
+
label: pool,
|
|
431
|
+
remainingFraction: frac,
|
|
432
|
+
resetTime: rtime,
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
var models = []
|
|
438
|
+
var keys = Object.keys(deduped)
|
|
439
|
+
for (var i = 0; i < keys.length; i++) {
|
|
440
|
+
var m = deduped[keys[i]]
|
|
441
|
+
m.sortKey = modelSortKey(m.label)
|
|
442
|
+
models.push(m)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
models.sort(function (a, b) {
|
|
446
|
+
return a.sortKey < b.sortKey ? -1 : a.sortKey > b.sortKey ? 1 : 0
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
var lines = []
|
|
450
|
+
for (var i = 0; i < models.length; i++) {
|
|
451
|
+
lines.push(modelLine(ctx, models[i].label, models[i].remainingFraction, models[i].resetTime))
|
|
452
|
+
}
|
|
453
|
+
return lines
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// --- Cloud Code API ---
|
|
457
|
+
|
|
458
|
+
function requestCloudCodeJson(ctx, path, token, userAgent, body) {
|
|
459
|
+
for (var i = 0; i < CLOUD_CODE_URLS.length; i++) {
|
|
460
|
+
try {
|
|
461
|
+
var resp = ctx.host.http.request({
|
|
462
|
+
method: "POST",
|
|
463
|
+
url: CLOUD_CODE_URLS[i] + path,
|
|
464
|
+
headers: {
|
|
465
|
+
Accept: "application/json",
|
|
466
|
+
"Content-Type": "application/json",
|
|
467
|
+
Authorization: "Bearer " + token,
|
|
468
|
+
"User-Agent": userAgent || "antigravity",
|
|
469
|
+
},
|
|
470
|
+
bodyText: JSON.stringify(body || {}),
|
|
471
|
+
timeoutMs: 15000,
|
|
472
|
+
})
|
|
473
|
+
if (!resp || typeof resp.status !== "number" || !Number.isFinite(resp.status)) {
|
|
474
|
+
ctx.host.log.warn("Cloud Code returned invalid response shape (" + CLOUD_CODE_URLS[i] + ")")
|
|
475
|
+
continue
|
|
476
|
+
}
|
|
477
|
+
if (ctx.util.isAuthStatus(resp.status)) return { _authFailed: true }
|
|
478
|
+
if (resp.status >= 200 && resp.status < 300) {
|
|
479
|
+
var json = ctx.util.tryParseJson(resp.bodyText)
|
|
480
|
+
if (!json || typeof json !== "object") {
|
|
481
|
+
ctx.host.log.warn("Cloud Code returned invalid JSON (" + CLOUD_CODE_URLS[i] + ")")
|
|
482
|
+
continue
|
|
483
|
+
}
|
|
484
|
+
return json
|
|
485
|
+
}
|
|
486
|
+
} catch (e) {
|
|
487
|
+
ctx.host.log.warn("Cloud Code request failed (" + CLOUD_CODE_URLS[i] + "): " + String(e))
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return null
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function probeCloudCode(ctx, token, userAgent) {
|
|
494
|
+
return requestCloudCodeJson(ctx, FETCH_MODELS_PATH, token, userAgent, {})
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function parseCloudCodeModels(data) {
|
|
498
|
+
var modelsObj = data && data.models
|
|
499
|
+
if (!modelsObj || typeof modelsObj !== "object") return []
|
|
500
|
+
var keys = Object.keys(modelsObj)
|
|
501
|
+
var configs = []
|
|
502
|
+
for (var i = 0; i < keys.length; i++) {
|
|
503
|
+
var m = modelsObj[keys[i]]
|
|
504
|
+
if (!m || typeof m !== "object") continue
|
|
505
|
+
if (m.isInternal) continue
|
|
506
|
+
var modelId = m.model || keys[i]
|
|
507
|
+
if (CC_MODEL_BLACKLIST[modelId]) continue
|
|
508
|
+
var displayName =
|
|
509
|
+
(typeof m.displayName === "string" && m.displayName.trim()) ||
|
|
510
|
+
(typeof m.label === "string" && m.label.trim()) ||
|
|
511
|
+
""
|
|
512
|
+
if (!displayName) continue
|
|
513
|
+
var qi = m.quotaInfo
|
|
514
|
+
var frac = (qi && typeof qi.remainingFraction === "number") ? qi.remainingFraction : 0
|
|
515
|
+
var rtime = (qi && qi.resetTime) || undefined
|
|
516
|
+
configs.push({
|
|
517
|
+
label: displayName,
|
|
518
|
+
quotaInfo: { remainingFraction: frac, resetTime: rtime },
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
return configs
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function readAgyPlan(loadData) {
|
|
525
|
+
var paidTier = loadData && loadData.paidTier
|
|
526
|
+
if (paidTier && typeof paidTier.name === "string" && paidTier.name.trim()) {
|
|
527
|
+
return paidTier.name.trim()
|
|
528
|
+
}
|
|
529
|
+
var currentTier = loadData && loadData.currentTier
|
|
530
|
+
if (currentTier && typeof currentTier.name === "string" && currentTier.name.trim()) {
|
|
531
|
+
return currentTier.name.trim()
|
|
532
|
+
}
|
|
533
|
+
return null
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function parseAgyQuotaBuckets(data) {
|
|
537
|
+
var buckets = data && data.buckets
|
|
538
|
+
if (!Array.isArray(buckets)) return []
|
|
539
|
+
var configs = []
|
|
540
|
+
for (var i = 0; i < buckets.length; i++) {
|
|
541
|
+
var bucket = buckets[i]
|
|
542
|
+
if (!bucket || typeof bucket !== "object") continue
|
|
543
|
+
var modelId = (typeof bucket.modelId === "string" && bucket.modelId.trim()) || ""
|
|
544
|
+
if (!modelId) continue
|
|
545
|
+
var frac = (typeof bucket.remainingFraction === "number") ? bucket.remainingFraction : 0
|
|
546
|
+
configs.push({
|
|
547
|
+
label: modelId,
|
|
548
|
+
quotaInfo: { remainingFraction: frac, resetTime: bucket.resetTime || undefined },
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
return configs
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function probeAgyCloudCode(ctx, token) {
|
|
555
|
+
var loadData = requestCloudCodeJson(ctx, LOAD_CODE_ASSIST_PATH, token, "agy", {})
|
|
556
|
+
if (!loadData || loadData._authFailed) return loadData
|
|
557
|
+
|
|
558
|
+
var project =
|
|
559
|
+
typeof loadData.cloudaicompanionProject === "string" && loadData.cloudaicompanionProject.trim()
|
|
560
|
+
? loadData.cloudaicompanionProject.trim()
|
|
561
|
+
: null
|
|
562
|
+
var quotaData = null
|
|
563
|
+
if (project) {
|
|
564
|
+
quotaData = requestCloudCodeJson(ctx, RETRIEVE_QUOTA_PATH, token, "agy", { project: project })
|
|
565
|
+
}
|
|
566
|
+
if (!quotaData || quotaData._authFailed) {
|
|
567
|
+
quotaData = requestCloudCodeJson(ctx, RETRIEVE_QUOTA_PATH, token, "agy", {})
|
|
568
|
+
}
|
|
569
|
+
if (!quotaData || quotaData._authFailed) return quotaData
|
|
570
|
+
|
|
571
|
+
var lines = buildModelLines(ctx, parseAgyQuotaBuckets(quotaData))
|
|
572
|
+
if (lines.length === 0) return null
|
|
573
|
+
return { plan: readAgyPlan(loadData), lines: lines }
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// --- LS probe ---
|
|
577
|
+
|
|
578
|
+
function probeDiscovery(ctx, discovery) {
|
|
579
|
+
if (!discovery) return null
|
|
580
|
+
|
|
581
|
+
var found = findWorkingPort(ctx, discovery)
|
|
582
|
+
if (!found) return null
|
|
583
|
+
|
|
584
|
+
ctx.host.log.info("using LS at " + found.scheme + "://127.0.0.1:" + found.port)
|
|
585
|
+
|
|
586
|
+
var metadata = {
|
|
587
|
+
ideName: "antigravity",
|
|
588
|
+
extensionName: "antigravity",
|
|
589
|
+
ideVersion: "unknown",
|
|
590
|
+
locale: "en",
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Try GetUserStatus first, fall back to GetCommandModelConfigs
|
|
594
|
+
var data = null
|
|
595
|
+
try {
|
|
596
|
+
data = callLs(ctx, found.port, found.scheme, discovery.csrf, "GetUserStatus", { metadata: metadata })
|
|
597
|
+
} catch (e) {
|
|
598
|
+
ctx.host.log.warn("GetUserStatus threw: " + String(e))
|
|
599
|
+
}
|
|
600
|
+
var hasUserStatus = data && data.userStatus
|
|
601
|
+
|
|
602
|
+
if (!hasUserStatus) {
|
|
603
|
+
ctx.host.log.warn("GetUserStatus failed, trying GetCommandModelConfigs")
|
|
604
|
+
data = callLs(ctx, found.port, found.scheme, discovery.csrf, "GetCommandModelConfigs", { metadata: metadata })
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Parse model configs
|
|
608
|
+
var configs
|
|
609
|
+
if (hasUserStatus) {
|
|
610
|
+
configs = (data.userStatus.cascadeModelConfigData || {}).clientModelConfigs || []
|
|
611
|
+
} else if (data && data.clientModelConfigs) {
|
|
612
|
+
configs = data.clientModelConfigs
|
|
613
|
+
} else {
|
|
614
|
+
return null
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
var filtered = []
|
|
618
|
+
for (var j = 0; j < configs.length; j++) {
|
|
619
|
+
var mid = configs[j].modelOrAlias && configs[j].modelOrAlias.model
|
|
620
|
+
if (mid && CC_MODEL_BLACKLIST[mid]) continue
|
|
621
|
+
filtered.push(configs[j])
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
var lines = buildModelLines(ctx, filtered)
|
|
625
|
+
if (lines.length === 0) return null
|
|
626
|
+
|
|
627
|
+
var plan = null
|
|
628
|
+
if (hasUserStatus) {
|
|
629
|
+
// Prefer userTier.name (Google's own subscription system) over the legacy
|
|
630
|
+
// planInfo.planName field inherited from Windsurf/Codeium, which always
|
|
631
|
+
// returns "Pro" for all paid tiers including Google AI Ultra.
|
|
632
|
+
var ut = data.userStatus.userTier
|
|
633
|
+
var userTierName =
|
|
634
|
+
ut && typeof ut.name === "string" && ut.name.trim() ? ut.name.trim() : null
|
|
635
|
+
if (userTierName) {
|
|
636
|
+
plan = userTierName
|
|
637
|
+
} else {
|
|
638
|
+
var ps = data.userStatus.planStatus || {}
|
|
639
|
+
var pi = ps.planInfo || {}
|
|
640
|
+
plan =
|
|
641
|
+
typeof pi.planName === "string" && pi.planName.trim() ? pi.planName.trim() : null
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return { plan: plan, lines: lines }
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function probeLs(ctx) {
|
|
649
|
+
return probeDiscovery(ctx, discoverLs(ctx))
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function probeAgyLs(ctx) {
|
|
653
|
+
return probeDiscovery(ctx, discoverAgyLs(ctx))
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// --- Probe ---
|
|
657
|
+
|
|
658
|
+
function probe(ctx) {
|
|
659
|
+
var opts = providerSettings(ctx)
|
|
660
|
+
var explicitDb = !!(opts.userDataDir || opts.ideVariant)
|
|
661
|
+
if (explicitDb && opts.ideProcessId != null) throw {code: "failed", message: "Select an Antigravity database profile or a process ID, not both."}
|
|
662
|
+
if (!explicitDb) {
|
|
663
|
+
var lsResult = probeLs(ctx)
|
|
664
|
+
if (lsResult) return lsResult
|
|
665
|
+
if (opts.ideProcessId != null) throw LOGIN_MESSAGE
|
|
666
|
+
}
|
|
667
|
+
var dbTokenCandidates = loadOAuthTokenCandidates(ctx)
|
|
668
|
+
if (explicitDb && !dbTokenCandidates.length) throw LOGIN_MESSAGE
|
|
669
|
+
if (!dbTokenCandidates.length) {
|
|
670
|
+
var agyLsResult = probeAgyLs(ctx)
|
|
671
|
+
if (agyLsResult) return agyLsResult
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
var tokens = []
|
|
675
|
+
var nowSec = Math.floor(Date.now() / 1000)
|
|
676
|
+
for (var i = 0; i < dbTokenCandidates.length; i++) {
|
|
677
|
+
var dbTokens = dbTokenCandidates[i]
|
|
678
|
+
if (dbTokens.accessToken && (!dbTokens.expirySeconds || dbTokens.expirySeconds > nowSec)) {
|
|
679
|
+
if (tokens.indexOf(dbTokens.accessToken) === -1) tokens.push(dbTokens.accessToken)
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
var profileKey = dbTokenCandidates.length ? dbTokenCandidates[0].profileKey : null
|
|
684
|
+
var cached = loadCachedToken(ctx, profileKey)
|
|
685
|
+
if (cached && tokens.indexOf(cached) === -1) tokens.push(cached)
|
|
686
|
+
|
|
687
|
+
var ccData = null
|
|
688
|
+
var sawAuthFailure = false
|
|
689
|
+
for (var i = 0; i < tokens.length; i++) {
|
|
690
|
+
var nextData = probeCloudCode(ctx, tokens[i])
|
|
691
|
+
if (nextData && !nextData._authFailed) {
|
|
692
|
+
ccData = nextData
|
|
693
|
+
break
|
|
694
|
+
}
|
|
695
|
+
if (nextData && nextData._authFailed) sawAuthFailure = true
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Only refresh on evidence of an auth failure, or when there were no tokens to try.
|
|
699
|
+
// probeCloudCode returns null for transient failures (5xx/timeouts); without this
|
|
700
|
+
// guard a Cloud Code incident would trigger a Google OAuth refresh every probe cycle
|
|
701
|
+
// instead of ~once per token lifetime - risking refresh-token throttling or rotation.
|
|
702
|
+
if (!ccData && (sawAuthFailure || tokens.length === 0)) {
|
|
703
|
+
var refreshTokens = []
|
|
704
|
+
for (var j = 0; j < dbTokenCandidates.length; j++) {
|
|
705
|
+
var refreshToken = dbTokenCandidates[j].refreshToken
|
|
706
|
+
if (refreshToken && refreshTokens.indexOf(refreshToken) === -1) refreshTokens.push(refreshToken)
|
|
707
|
+
}
|
|
708
|
+
for (var k = 0; k < refreshTokens.length; k++) {
|
|
709
|
+
var refreshed = refreshAccessToken(ctx, refreshTokens[k], profileKey)
|
|
710
|
+
if (!refreshed) continue
|
|
711
|
+
var refreshedData = probeCloudCode(ctx, refreshed)
|
|
712
|
+
if (refreshedData && !refreshedData._authFailed) {
|
|
713
|
+
ccData = refreshedData
|
|
714
|
+
break
|
|
715
|
+
}
|
|
716
|
+
if (refreshedData && refreshedData._authFailed) ccData = refreshedData
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (!dbTokenCandidates.length && (!ccData || ccData._authFailed)) {
|
|
721
|
+
var agyToken = loadAgyKeychainToken(ctx)
|
|
722
|
+
if (agyToken) {
|
|
723
|
+
var agyResult = probeAgyCloudCode(ctx, agyToken)
|
|
724
|
+
if (agyResult && !agyResult._authFailed) return agyResult
|
|
725
|
+
if (agyResult && agyResult._authFailed) ccData = agyResult
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (ccData && !ccData._authFailed) {
|
|
730
|
+
var configs = parseCloudCodeModels(ccData)
|
|
731
|
+
var lines = buildModelLines(ctx, configs)
|
|
732
|
+
if (lines.length > 0) return { plan: null, lines: lines }
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
throw LOGIN_MESSAGE
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
globalThis.__openusage_plugin = { id: "antigravity", probe: probe }
|
|
739
|
+
})()
|