antigravity-usage 0.2.6 → 0.2.8
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/README.md +16 -2
- package/dist/index.js +236 -98
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ function success(message) {
|
|
|
48
48
|
import { createServer } from "http";
|
|
49
49
|
import { URL as URL2, URLSearchParams } from "url";
|
|
50
50
|
import open from "open";
|
|
51
|
+
import inquirer from "inquirer";
|
|
51
52
|
|
|
52
53
|
// src/accounts/types.ts
|
|
53
54
|
var DEFAULT_CONFIG = {
|
|
@@ -722,6 +723,33 @@ async function resolveProjectId(accessToken) {
|
|
|
722
723
|
return { projectId: void 0, tierId: void 0 };
|
|
723
724
|
}
|
|
724
725
|
}
|
|
726
|
+
async function completeLogin(code, redirectUri) {
|
|
727
|
+
const tokenResponse = await exchangeCodeForTokens(code, redirectUri);
|
|
728
|
+
const email = await getUserEmail(tokenResponse.access_token);
|
|
729
|
+
let projectId;
|
|
730
|
+
try {
|
|
731
|
+
const projectResult = await resolveProjectId(tokenResponse.access_token);
|
|
732
|
+
projectId = projectResult.projectId;
|
|
733
|
+
if (projectId) {
|
|
734
|
+
debug("oauth", `Project ID resolved: ${projectId}`);
|
|
735
|
+
} else {
|
|
736
|
+
debug("oauth", "No project ID obtained (will fetch on demand)");
|
|
737
|
+
}
|
|
738
|
+
} catch (err) {
|
|
739
|
+
debug("oauth", "Failed to resolve project ID during login (will fetch on demand)", err);
|
|
740
|
+
}
|
|
741
|
+
const tokens = {
|
|
742
|
+
accessToken: tokenResponse.access_token,
|
|
743
|
+
refreshToken: tokenResponse.refresh_token || "",
|
|
744
|
+
expiresAt: Date.now() + tokenResponse.expires_in * 1e3,
|
|
745
|
+
email,
|
|
746
|
+
projectId
|
|
747
|
+
};
|
|
748
|
+
if (email) {
|
|
749
|
+
getAccountManager().addAccount(tokens, email);
|
|
750
|
+
}
|
|
751
|
+
return { success: true, email };
|
|
752
|
+
}
|
|
725
753
|
async function startOAuthFlow(options = {}) {
|
|
726
754
|
const port = await getAvailablePort(options.port);
|
|
727
755
|
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
@@ -737,6 +765,43 @@ async function startOAuthFlow(options = {}) {
|
|
|
737
765
|
state
|
|
738
766
|
});
|
|
739
767
|
const authUrl = `${OAUTH_CONFIG.authUrl}?${authParams.toString()}`;
|
|
768
|
+
if (options.manual) {
|
|
769
|
+
info("");
|
|
770
|
+
info("MANUAL LOGIN MODE");
|
|
771
|
+
info("1. Copy this URL and open it in your browser:");
|
|
772
|
+
info(authUrl);
|
|
773
|
+
info("");
|
|
774
|
+
info("2. Login with your Google account.");
|
|
775
|
+
info("3. You will be redirected to a localhost URL (which may fail to load).");
|
|
776
|
+
info("4. Copy that ENTIRE localhost URL and paste it below.");
|
|
777
|
+
info("");
|
|
778
|
+
const { pastedUrl } = await inquirer.prompt([
|
|
779
|
+
{
|
|
780
|
+
type: "input",
|
|
781
|
+
name: "pastedUrl",
|
|
782
|
+
message: "Paste the full redirect URL here:",
|
|
783
|
+
validate: (input) => input.trim().length > 0 ? true : "Please paste the URL"
|
|
784
|
+
}
|
|
785
|
+
]);
|
|
786
|
+
try {
|
|
787
|
+
const url = new URL2(pastedUrl.trim());
|
|
788
|
+
const code = url.searchParams.get("code");
|
|
789
|
+
const returnedState = url.searchParams.get("state");
|
|
790
|
+
const errorParam = url.searchParams.get("error");
|
|
791
|
+
if (errorParam) {
|
|
792
|
+
return { success: false, error: errorParam };
|
|
793
|
+
}
|
|
794
|
+
if (!code || returnedState !== state) {
|
|
795
|
+
return { success: false, error: "Invalid URL: Missing code or state mismatch" };
|
|
796
|
+
}
|
|
797
|
+
return await completeLogin(code, redirectUri);
|
|
798
|
+
} catch (err) {
|
|
799
|
+
if (err instanceof Error) {
|
|
800
|
+
return { success: false, error: err.message };
|
|
801
|
+
}
|
|
802
|
+
return { success: false, error: "Invalid URL format" };
|
|
803
|
+
}
|
|
804
|
+
}
|
|
740
805
|
return new Promise((resolve) => {
|
|
741
806
|
let resolved = false;
|
|
742
807
|
const server = createServer(async (req, res) => {
|
|
@@ -763,43 +828,20 @@ async function startOAuthFlow(options = {}) {
|
|
|
763
828
|
return;
|
|
764
829
|
}
|
|
765
830
|
try {
|
|
766
|
-
const
|
|
767
|
-
const email = await getUserEmail(tokenResponse.access_token);
|
|
768
|
-
let projectId;
|
|
769
|
-
try {
|
|
770
|
-
const projectResult = await resolveProjectId(tokenResponse.access_token);
|
|
771
|
-
projectId = projectResult.projectId;
|
|
772
|
-
if (projectId) {
|
|
773
|
-
debug("oauth", `Project ID resolved: ${projectId}`);
|
|
774
|
-
} else {
|
|
775
|
-
debug("oauth", "No project ID obtained (will fetch on demand)");
|
|
776
|
-
}
|
|
777
|
-
} catch (err) {
|
|
778
|
-
debug("oauth", "Failed to resolve project ID during login (will fetch on demand)", err);
|
|
779
|
-
}
|
|
780
|
-
const tokens = {
|
|
781
|
-
accessToken: tokenResponse.access_token,
|
|
782
|
-
refreshToken: tokenResponse.refresh_token || "",
|
|
783
|
-
expiresAt: Date.now() + tokenResponse.expires_in * 1e3,
|
|
784
|
-
email,
|
|
785
|
-
projectId
|
|
786
|
-
};
|
|
787
|
-
if (email) {
|
|
788
|
-
getAccountManager().addAccount(tokens, email);
|
|
789
|
-
}
|
|
831
|
+
const result = await completeLogin(code, redirectUri);
|
|
790
832
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
791
833
|
res.end(`
|
|
792
834
|
<html>
|
|
793
835
|
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
794
836
|
<h1>Login Successful!</h1>
|
|
795
|
-
<p>You are now logged in${email ? ` as <strong>${email}</strong>` : ""}.</p>
|
|
837
|
+
<p>You are now logged in${result.email ? ` as <strong>${result.email}</strong>` : ""}.</p>
|
|
796
838
|
<p>You can close this window and return to the terminal.</p>
|
|
797
839
|
</body>
|
|
798
840
|
</html>
|
|
799
841
|
`);
|
|
800
842
|
resolved = true;
|
|
801
843
|
server.close();
|
|
802
|
-
resolve(
|
|
844
|
+
resolve(result);
|
|
803
845
|
} catch (err) {
|
|
804
846
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
805
847
|
res.end("<html><body><h1>Login Failed</h1><p>Token exchange failed.</p></body></html>");
|
|
@@ -1203,7 +1245,8 @@ async function loginCommand(options) {
|
|
|
1203
1245
|
}
|
|
1204
1246
|
const result = await startOAuthFlow({
|
|
1205
1247
|
noBrowser: options.noBrowser,
|
|
1206
|
-
port: options.port
|
|
1248
|
+
port: options.port,
|
|
1249
|
+
manual: options.manual
|
|
1207
1250
|
});
|
|
1208
1251
|
if (result.success) {
|
|
1209
1252
|
resetTokenManager();
|
|
@@ -1730,7 +1773,8 @@ function parseModelInfo(modelId, model) {
|
|
|
1730
1773
|
remainingPercentage: quotaInfo?.remainingFraction,
|
|
1731
1774
|
isExhausted: quotaInfo?.isExhausted ?? quotaInfo?.remainingFraction === 0,
|
|
1732
1775
|
resetTime: quotaInfo?.resetTime,
|
|
1733
|
-
timeUntilResetMs: parseResetTime(quotaInfo?.resetTime)
|
|
1776
|
+
timeUntilResetMs: parseResetTime(quotaInfo?.resetTime),
|
|
1777
|
+
isAutocompleteOnly: modelId.includes("gemini-2.5") || (model.displayName || "").includes("Gemini 2.5")
|
|
1734
1778
|
};
|
|
1735
1779
|
}
|
|
1736
1780
|
function parsePromptCredits(response) {
|
|
@@ -1808,12 +1852,21 @@ async function detectOnUnix() {
|
|
|
1808
1852
|
const { stdout } = await execAsync("ps aux");
|
|
1809
1853
|
const lines = stdout.split("\n");
|
|
1810
1854
|
for (const line of lines) {
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1855
|
+
const lower = line.toLowerCase();
|
|
1856
|
+
if (!lower.includes("antigravity")) {
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
if (lower.includes("server installation script")) {
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
const hasServerSignal = line.includes("language-server") || line.includes("lsp") || line.includes("--csrf_token") || line.includes("--extension_server_port") || line.includes("exa.language_server_pb");
|
|
1863
|
+
if (!hasServerSignal) {
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
debug("process-detector", `Found potential Antigravity process: ${line}`);
|
|
1867
|
+
const processInfo = parseUnixProcessLine(line);
|
|
1868
|
+
if (processInfo) {
|
|
1869
|
+
return processInfo;
|
|
1817
1870
|
}
|
|
1818
1871
|
}
|
|
1819
1872
|
debug("process-detector", "No Antigravity process found");
|
|
@@ -1850,24 +1903,27 @@ async function detectOnWindows() {
|
|
|
1850
1903
|
// 10MB buffer for long command lines
|
|
1851
1904
|
);
|
|
1852
1905
|
const lines = stdout.split("\n").filter((line) => line.trim() && !line.includes("Node,CommandLine,ProcessId"));
|
|
1906
|
+
const candidates = [];
|
|
1853
1907
|
for (const line of lines) {
|
|
1854
1908
|
const parts = line.split(",");
|
|
1855
1909
|
if (parts.length >= 3) {
|
|
1856
1910
|
const commandLine = parts.slice(1, -1).join(",");
|
|
1857
1911
|
const pid = parseInt(parts[parts.length - 1].trim(), 10);
|
|
1858
1912
|
if (!isNaN(pid) && commandLine.toLowerCase().includes("antigravity")) {
|
|
1859
|
-
|
|
1860
|
-
const csrfToken = extractArgument(commandLine, "--csrf_token");
|
|
1861
|
-
const extensionServerPort = extractArgument(commandLine, "--extension_server_port");
|
|
1862
|
-
return {
|
|
1913
|
+
candidates.push({
|
|
1863
1914
|
pid,
|
|
1864
|
-
csrfToken:
|
|
1865
|
-
extensionServerPort:
|
|
1915
|
+
csrfToken: extractArgument(commandLine, "--csrf_token") || void 0,
|
|
1916
|
+
extensionServerPort: parsePortValue(extractArgument(commandLine, "--extension_server_port")),
|
|
1866
1917
|
commandLine
|
|
1867
|
-
};
|
|
1918
|
+
});
|
|
1868
1919
|
}
|
|
1869
1920
|
}
|
|
1870
1921
|
}
|
|
1922
|
+
const selected = selectBestWindowsCandidate(candidates);
|
|
1923
|
+
if (selected) {
|
|
1924
|
+
debug("process-detector", `Selected Antigravity process on Windows: PID ${selected.pid}`);
|
|
1925
|
+
return selected;
|
|
1926
|
+
}
|
|
1871
1927
|
return await detectOnWindowsPowerShell();
|
|
1872
1928
|
} catch (err) {
|
|
1873
1929
|
debug("process-detector", "Error detecting process on Windows with WMIC, trying PowerShell", err);
|
|
@@ -1884,28 +1940,73 @@ async function detectOnWindowsPowerShell() {
|
|
|
1884
1940
|
}
|
|
1885
1941
|
const processes = JSON.parse(stdout);
|
|
1886
1942
|
const processList = Array.isArray(processes) ? processes : [processes];
|
|
1943
|
+
const candidates = [];
|
|
1887
1944
|
for (const proc of processList) {
|
|
1888
1945
|
if (proc.Id) {
|
|
1889
1946
|
const { stdout: cmdLine } = await execAsync(
|
|
1890
1947
|
`powershell -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId = ${proc.Id}').CommandLine"`
|
|
1891
1948
|
);
|
|
1892
1949
|
const commandLine = cmdLine.trim();
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1950
|
+
if (!commandLine.toLowerCase().includes("antigravity")) {
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1953
|
+
candidates.push({
|
|
1896
1954
|
pid: proc.Id,
|
|
1897
|
-
csrfToken:
|
|
1898
|
-
extensionServerPort:
|
|
1955
|
+
csrfToken: extractArgument(commandLine, "--csrf_token") || void 0,
|
|
1956
|
+
extensionServerPort: parsePortValue(extractArgument(commandLine, "--extension_server_port")),
|
|
1899
1957
|
commandLine
|
|
1900
|
-
};
|
|
1958
|
+
});
|
|
1901
1959
|
}
|
|
1902
1960
|
}
|
|
1961
|
+
const selected = selectBestWindowsCandidate(candidates);
|
|
1962
|
+
if (selected) {
|
|
1963
|
+
debug("process-detector", `Selected Antigravity process on Windows (PowerShell): PID ${selected.pid}`);
|
|
1964
|
+
return selected;
|
|
1965
|
+
}
|
|
1903
1966
|
return null;
|
|
1904
1967
|
} catch (err) {
|
|
1905
1968
|
debug("process-detector", "Error detecting process on Windows with PowerShell", err);
|
|
1906
1969
|
return null;
|
|
1907
1970
|
}
|
|
1908
1971
|
}
|
|
1972
|
+
function parsePortValue(rawPort) {
|
|
1973
|
+
if (!rawPort) {
|
|
1974
|
+
return void 0;
|
|
1975
|
+
}
|
|
1976
|
+
const parsed = parseInt(rawPort, 10);
|
|
1977
|
+
return isNaN(parsed) ? void 0 : parsed;
|
|
1978
|
+
}
|
|
1979
|
+
function scoreWindowsCandidate(candidate) {
|
|
1980
|
+
const lower = candidate.commandLine.toLowerCase();
|
|
1981
|
+
let score = 0;
|
|
1982
|
+
if (lower.includes("antigravity")) score += 1;
|
|
1983
|
+
if (lower.includes("lsp")) score += 5;
|
|
1984
|
+
if (candidate.extensionServerPort) score += 10;
|
|
1985
|
+
if (candidate.csrfToken) score += 20;
|
|
1986
|
+
if (lower.includes("language_server") || lower.includes("language-server") || lower.includes("exa.language_server_pb")) {
|
|
1987
|
+
score += 50;
|
|
1988
|
+
}
|
|
1989
|
+
return score;
|
|
1990
|
+
}
|
|
1991
|
+
function selectBestWindowsCandidate(candidates) {
|
|
1992
|
+
if (candidates.length === 0) {
|
|
1993
|
+
return null;
|
|
1994
|
+
}
|
|
1995
|
+
debug("process-detector", `Found ${candidates.length} Antigravity candidate process(es) on Windows`);
|
|
1996
|
+
let best = null;
|
|
1997
|
+
let bestScore = -1;
|
|
1998
|
+
for (const candidate of candidates) {
|
|
1999
|
+
const score = scoreWindowsCandidate(candidate);
|
|
2000
|
+
if (score > bestScore) {
|
|
2001
|
+
best = candidate;
|
|
2002
|
+
bestScore = score;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
if (best) {
|
|
2006
|
+
debug("process-detector", `Selected PID ${best.pid} with score ${bestScore}`);
|
|
2007
|
+
}
|
|
2008
|
+
return best;
|
|
2009
|
+
}
|
|
1909
2010
|
function extractArgument(commandLine, argName) {
|
|
1910
2011
|
const eqRegex = new RegExp(`${argName}=([^\\s"']+|"[^"]*"|'[^']*')`, "i");
|
|
1911
2012
|
const eqMatch = commandLine.match(eqRegex);
|
|
@@ -2032,6 +2133,8 @@ async function discoverPortsOnWindows(pid) {
|
|
|
2032
2133
|
// src/local/port-prober.ts
|
|
2033
2134
|
import https from "https";
|
|
2034
2135
|
import http from "http";
|
|
2136
|
+
var CONNECT_RPC_PATH = "/exa.language_server_pb.LanguageServerService/GetUnleashData";
|
|
2137
|
+
var VALID_CONNECT_STATUSES = /* @__PURE__ */ new Set([200, 401]);
|
|
2035
2138
|
async function probeForConnectAPI(ports, csrfToken, timeout = 500) {
|
|
2036
2139
|
debug("port-prober", `Probing ${ports.length} ports: ${ports.join(", ")}`);
|
|
2037
2140
|
const probePromises = ports.map((port) => probePort(port, csrfToken, timeout));
|
|
@@ -2051,7 +2154,7 @@ async function probePort(port, csrfToken, timeout = 500) {
|
|
|
2051
2154
|
if (httpsResult) {
|
|
2052
2155
|
return httpsResult;
|
|
2053
2156
|
}
|
|
2054
|
-
const httpResult = await probeHttp(port, timeout);
|
|
2157
|
+
const httpResult = await probeHttp(port, timeout, csrfToken);
|
|
2055
2158
|
if (httpResult) {
|
|
2056
2159
|
return httpResult;
|
|
2057
2160
|
}
|
|
@@ -2062,7 +2165,7 @@ function probeHttps(port, timeout, csrfToken) {
|
|
|
2062
2165
|
const options = {
|
|
2063
2166
|
hostname: "127.0.0.1",
|
|
2064
2167
|
port,
|
|
2065
|
-
path:
|
|
2168
|
+
path: CONNECT_RPC_PATH,
|
|
2066
2169
|
method: "POST",
|
|
2067
2170
|
timeout,
|
|
2068
2171
|
rejectUnauthorized: false,
|
|
@@ -2074,7 +2177,7 @@ function probeHttps(port, timeout, csrfToken) {
|
|
|
2074
2177
|
}
|
|
2075
2178
|
};
|
|
2076
2179
|
const req = https.request(options, (res) => {
|
|
2077
|
-
if (res.statusCode
|
|
2180
|
+
if (res.statusCode && VALID_CONNECT_STATUSES.has(res.statusCode)) {
|
|
2078
2181
|
debug("port-prober", `HTTPS Connect RPC probe on port ${port}: status ${res.statusCode} - valid connect port`);
|
|
2079
2182
|
resolve({
|
|
2080
2183
|
baseUrl: `https://127.0.0.1:${port}`,
|
|
@@ -2100,23 +2203,43 @@ function probeHttps(port, timeout, csrfToken) {
|
|
|
2100
2203
|
req.end();
|
|
2101
2204
|
});
|
|
2102
2205
|
}
|
|
2103
|
-
function probeHttp(port, timeout) {
|
|
2206
|
+
function probeHttp(port, timeout, csrfToken) {
|
|
2104
2207
|
return new Promise((resolve) => {
|
|
2105
2208
|
const options = {
|
|
2106
|
-
hostname: "
|
|
2209
|
+
hostname: "127.0.0.1",
|
|
2107
2210
|
port,
|
|
2108
|
-
path:
|
|
2109
|
-
method: "
|
|
2110
|
-
timeout
|
|
2211
|
+
path: CONNECT_RPC_PATH,
|
|
2212
|
+
method: "POST",
|
|
2213
|
+
timeout,
|
|
2214
|
+
headers: {
|
|
2215
|
+
"Content-Type": "application/json",
|
|
2216
|
+
"Connect-Protocol-Version": "1",
|
|
2217
|
+
...csrfToken ? { "X-Codeium-Csrf-Token": csrfToken } : {}
|
|
2218
|
+
}
|
|
2111
2219
|
};
|
|
2112
2220
|
const req = http.request(options, (res) => {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2221
|
+
let data = "";
|
|
2222
|
+
res.on("data", (chunk) => {
|
|
2223
|
+
data += chunk.toString();
|
|
2224
|
+
});
|
|
2225
|
+
res.on("end", () => {
|
|
2226
|
+
if (data.toLowerCase().includes("client sent an http request to an https server")) {
|
|
2227
|
+
debug("port-prober", `HTTP probe on port ${port}: protocol mismatch response, rejecting`);
|
|
2228
|
+
resolve(null);
|
|
2229
|
+
return;
|
|
2230
|
+
}
|
|
2231
|
+
if (res.statusCode && VALID_CONNECT_STATUSES.has(res.statusCode)) {
|
|
2232
|
+
debug("port-prober", `HTTP Connect RPC probe on port ${port}: status ${res.statusCode} - valid connect port`);
|
|
2233
|
+
resolve({
|
|
2234
|
+
baseUrl: `http://127.0.0.1:${port}`,
|
|
2235
|
+
protocol: "http",
|
|
2236
|
+
port
|
|
2237
|
+
});
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
debug("port-prober", `HTTP probe on port ${port}: status ${res.statusCode} - not connect port`);
|
|
2241
|
+
resolve(null);
|
|
2118
2242
|
});
|
|
2119
|
-
res.resume();
|
|
2120
2243
|
});
|
|
2121
2244
|
req.on("error", (err) => {
|
|
2122
2245
|
debug("port-prober", `HTTP probe on port ${port} failed: ${err.message}`);
|
|
@@ -2127,6 +2250,7 @@ function probeHttp(port, timeout) {
|
|
|
2127
2250
|
req.destroy();
|
|
2128
2251
|
resolve(null);
|
|
2129
2252
|
});
|
|
2253
|
+
req.write(JSON.stringify({ wrapper_data: {} }));
|
|
2130
2254
|
req.end();
|
|
2131
2255
|
});
|
|
2132
2256
|
}
|
|
@@ -2366,7 +2490,8 @@ function parseModelQuota(model) {
|
|
|
2366
2490
|
remainingPercentage: quota?.remainingPercentage,
|
|
2367
2491
|
isExhausted: model.isExhausted ?? quota?.remainingPercentage === 0,
|
|
2368
2492
|
resetTime: quota?.resetTime,
|
|
2369
|
-
timeUntilResetMs: quota?.timeUntilResetMs
|
|
2493
|
+
timeUntilResetMs: quota?.timeUntilResetMs,
|
|
2494
|
+
isAutocompleteOnly: model.modelId.includes("gemini-2.5") || (model.label || "").includes("Gemini 2.5") || (model.displayName || "").includes("Gemini 2.5")
|
|
2370
2495
|
};
|
|
2371
2496
|
}
|
|
2372
2497
|
|
|
@@ -2423,7 +2548,11 @@ async function fetchQuotaLocal() {
|
|
|
2423
2548
|
throw new AntigravityNotRunningError();
|
|
2424
2549
|
}
|
|
2425
2550
|
debug("service", `Found Antigravity process: PID ${processInfo.pid}`);
|
|
2426
|
-
|
|
2551
|
+
let ports = await discoverPorts(processInfo.pid);
|
|
2552
|
+
if (ports.length === 0 && processInfo.extensionServerPort) {
|
|
2553
|
+
debug("service", `Falling back to extension_server_port: ${processInfo.extensionServerPort}`);
|
|
2554
|
+
ports = [processInfo.extensionServerPort];
|
|
2555
|
+
}
|
|
2427
2556
|
if (ports.length === 0) {
|
|
2428
2557
|
throw new PortDetectionError();
|
|
2429
2558
|
}
|
|
@@ -2465,7 +2594,7 @@ function formatRemaining(model) {
|
|
|
2465
2594
|
if (pct >= 25) return `\u{1F7E0} ${pct}%`;
|
|
2466
2595
|
return `\u{1F534} ${pct}%`;
|
|
2467
2596
|
}
|
|
2468
|
-
function printQuotaTable(snapshot) {
|
|
2597
|
+
function printQuotaTable(snapshot, options = {}) {
|
|
2469
2598
|
const timestamp = new Date(snapshot.timestamp).toLocaleString();
|
|
2470
2599
|
console.log();
|
|
2471
2600
|
console.log(`\u{1F4CA} Antigravity Quota Status (via ${snapshot.method.toUpperCase()})`);
|
|
@@ -2480,8 +2609,8 @@ function printQuotaTable(snapshot) {
|
|
|
2480
2609
|
}
|
|
2481
2610
|
console.log(` ${userParts.join(" | ")}`);
|
|
2482
2611
|
}
|
|
2483
|
-
|
|
2484
|
-
if (
|
|
2612
|
+
const visibleModels = options.allModels ? snapshot.models : snapshot.models.filter((m) => !m.isAutocompleteOnly);
|
|
2613
|
+
if (visibleModels.length > 0) {
|
|
2485
2614
|
const table = new Table2({
|
|
2486
2615
|
head: ["Model", "Remaining", "Resets In"],
|
|
2487
2616
|
style: {
|
|
@@ -2489,7 +2618,7 @@ function printQuotaTable(snapshot) {
|
|
|
2489
2618
|
border: ["gray"]
|
|
2490
2619
|
}
|
|
2491
2620
|
});
|
|
2492
|
-
for (const model of
|
|
2621
|
+
for (const model of visibleModels) {
|
|
2493
2622
|
table.push([
|
|
2494
2623
|
model.label,
|
|
2495
2624
|
formatRemaining(model),
|
|
@@ -2499,6 +2628,9 @@ function printQuotaTable(snapshot) {
|
|
|
2499
2628
|
console.log(table.toString());
|
|
2500
2629
|
} else {
|
|
2501
2630
|
console.log("No model quota information available.");
|
|
2631
|
+
if (!options.allModels && snapshot.models.some((m) => m.isAutocompleteOnly)) {
|
|
2632
|
+
console.log("Tip: Use --all-models to see autocomplete models.");
|
|
2633
|
+
}
|
|
2502
2634
|
}
|
|
2503
2635
|
console.log();
|
|
2504
2636
|
}
|
|
@@ -2575,13 +2707,16 @@ function renderAccountsTable(accounts) {
|
|
|
2575
2707
|
}
|
|
2576
2708
|
function formatQuotaRemainingBar(remainingPercentage) {
|
|
2577
2709
|
const width = 10;
|
|
2578
|
-
const filled = Math.round(remainingPercentage / 100 * width);
|
|
2579
|
-
const empty = width - filled;
|
|
2580
2710
|
const filledChar = "\u2588";
|
|
2581
2711
|
const emptyChar = "\u2591";
|
|
2712
|
+
if (remainingPercentage === void 0) {
|
|
2713
|
+
return `${emptyChar.repeat(width)} N/A`;
|
|
2714
|
+
}
|
|
2715
|
+
const filled = Math.round(remainingPercentage / 100 * width);
|
|
2716
|
+
const empty = width - filled;
|
|
2582
2717
|
return `${filledChar.repeat(filled)}${emptyChar.repeat(empty)} ${Math.round(remainingPercentage)}%`;
|
|
2583
2718
|
}
|
|
2584
|
-
function renderAllQuotaTable(results) {
|
|
2719
|
+
function renderAllQuotaTable(results, options = {}) {
|
|
2585
2720
|
if (results.length === 0) {
|
|
2586
2721
|
console.log("\n\u{1F4ED} No accounts found.");
|
|
2587
2722
|
console.log("\n\u{1F4A1} Run `antigravity-usage login` to add an account.\n");
|
|
@@ -2592,7 +2727,8 @@ function renderAllQuotaTable(results) {
|
|
|
2592
2727
|
if (a.status !== "error" && b.status === "error") return -1;
|
|
2593
2728
|
if (a.status === "error" && b.status === "error") return 0;
|
|
2594
2729
|
const getRemaining = (result) => {
|
|
2595
|
-
const
|
|
2730
|
+
const models = options.allModels ? result.snapshot?.models : result.snapshot?.models?.filter((m) => !m.isAutocompleteOnly);
|
|
2731
|
+
const firstModel = models?.[0];
|
|
2596
2732
|
if (!firstModel) return -1;
|
|
2597
2733
|
if (firstModel.isExhausted) return 0;
|
|
2598
2734
|
return firstModel.remainingPercentage ?? -1;
|
|
@@ -2643,15 +2779,17 @@ function renderAllQuotaTable(results) {
|
|
|
2643
2779
|
credits = `${pc.available} / ${pc.monthly}`;
|
|
2644
2780
|
}
|
|
2645
2781
|
let quotaRemaining = "-";
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
);
|
|
2650
|
-
if (
|
|
2651
|
-
const
|
|
2652
|
-
quotaRemaining = formatQuotaRemainingBar(
|
|
2653
|
-
} else if (
|
|
2782
|
+
const models = snapshot?.models || [];
|
|
2783
|
+
const relevantModels = options.allModels ? models : models.filter((m) => !m.isAutocompleteOnly);
|
|
2784
|
+
if (relevantModels.length > 0) {
|
|
2785
|
+
const percentages = relevantModels.filter((m) => m.remainingPercentage !== void 0).map((m) => m.remainingPercentage);
|
|
2786
|
+
if (percentages.length > 0) {
|
|
2787
|
+
const minRemaining = Math.min(...percentages);
|
|
2788
|
+
quotaRemaining = formatQuotaRemainingBar(minRemaining * 100);
|
|
2789
|
+
} else if (relevantModels.some((m) => m.isExhausted)) {
|
|
2654
2790
|
quotaRemaining = "\u274C EXHAUSTED";
|
|
2791
|
+
} else {
|
|
2792
|
+
quotaRemaining = formatQuotaRemainingBar(void 0);
|
|
2655
2793
|
}
|
|
2656
2794
|
}
|
|
2657
2795
|
table.push([
|
|
@@ -2713,7 +2851,7 @@ async function fetchSingleAccountQuota(options) {
|
|
|
2713
2851
|
if (options.json) {
|
|
2714
2852
|
printQuotaJson(snapshot);
|
|
2715
2853
|
} else {
|
|
2716
|
-
printQuotaTable(snapshot);
|
|
2854
|
+
printQuotaTable(snapshot, { allModels: options.allModels });
|
|
2717
2855
|
}
|
|
2718
2856
|
} finally {
|
|
2719
2857
|
if (accountSwitched && originalActiveEmail) {
|
|
@@ -2787,7 +2925,7 @@ async function fetchAllAccountsQuota(options) {
|
|
|
2787
2925
|
if (options.json) {
|
|
2788
2926
|
console.log(JSON.stringify(results, null, 2));
|
|
2789
2927
|
} else {
|
|
2790
|
-
renderAllQuotaTable(results);
|
|
2928
|
+
renderAllQuotaTable(results, { allModels: options.allModels });
|
|
2791
2929
|
}
|
|
2792
2930
|
}
|
|
2793
2931
|
async function fetchQuotaForAccount(email, method) {
|
|
@@ -3137,7 +3275,7 @@ async function accountsCommand(subcommand, args, options) {
|
|
|
3137
3275
|
}
|
|
3138
3276
|
|
|
3139
3277
|
// src/commands/wakeup.ts
|
|
3140
|
-
import
|
|
3278
|
+
import inquirer2 from "inquirer";
|
|
3141
3279
|
import Table4 from "cli-table3";
|
|
3142
3280
|
|
|
3143
3281
|
// src/wakeup/types.ts
|
|
@@ -3835,7 +3973,7 @@ async function configureWakeup() {
|
|
|
3835
3973
|
console.log(" antigravity-usage login\n");
|
|
3836
3974
|
return;
|
|
3837
3975
|
}
|
|
3838
|
-
const { enabled } = await
|
|
3976
|
+
const { enabled } = await inquirer2.prompt([{
|
|
3839
3977
|
type: "confirm",
|
|
3840
3978
|
name: "enabled",
|
|
3841
3979
|
message: "Enable auto wake-up?",
|
|
@@ -3847,7 +3985,7 @@ async function configureWakeup() {
|
|
|
3847
3985
|
console.log("\n\u2705 Auto wake-up disabled");
|
|
3848
3986
|
return;
|
|
3849
3987
|
}
|
|
3850
|
-
const { triggerMode } = await
|
|
3988
|
+
const { triggerMode } = await inquirer2.prompt([{
|
|
3851
3989
|
type: "list",
|
|
3852
3990
|
name: "triggerMode",
|
|
3853
3991
|
message: "Trigger mode:",
|
|
@@ -3859,7 +3997,7 @@ async function configureWakeup() {
|
|
|
3859
3997
|
}]);
|
|
3860
3998
|
config.wakeOnReset = triggerMode === "reset";
|
|
3861
3999
|
if (!config.wakeOnReset) {
|
|
3862
|
-
const { scheduleMode } = await
|
|
4000
|
+
const { scheduleMode } = await inquirer2.prompt([{
|
|
3863
4001
|
type: "list",
|
|
3864
4002
|
name: "scheduleMode",
|
|
3865
4003
|
message: "Schedule type:",
|
|
@@ -3872,7 +4010,7 @@ async function configureWakeup() {
|
|
|
3872
4010
|
}]);
|
|
3873
4011
|
config.scheduleMode = scheduleMode;
|
|
3874
4012
|
if (scheduleMode === "interval") {
|
|
3875
|
-
const { intervalHours } = await
|
|
4013
|
+
const { intervalHours } = await inquirer2.prompt([{
|
|
3876
4014
|
type: "number",
|
|
3877
4015
|
name: "intervalHours",
|
|
3878
4016
|
message: "Trigger every N hours:",
|
|
@@ -3881,7 +4019,7 @@ async function configureWakeup() {
|
|
|
3881
4019
|
}]);
|
|
3882
4020
|
config.intervalHours = intervalHours;
|
|
3883
4021
|
} else if (scheduleMode === "daily") {
|
|
3884
|
-
const { dailyTime } = await
|
|
4022
|
+
const { dailyTime } = await inquirer2.prompt([{
|
|
3885
4023
|
type: "input",
|
|
3886
4024
|
name: "dailyTime",
|
|
3887
4025
|
message: "Time to trigger (HH:MM):",
|
|
@@ -3890,7 +4028,7 @@ async function configureWakeup() {
|
|
|
3890
4028
|
}]);
|
|
3891
4029
|
config.dailyTimes = [dailyTime];
|
|
3892
4030
|
} else if (scheduleMode === "custom") {
|
|
3893
|
-
const { cronExpression } = await
|
|
4031
|
+
const { cronExpression } = await inquirer2.prompt([{
|
|
3894
4032
|
type: "input",
|
|
3895
4033
|
name: "cronExpression",
|
|
3896
4034
|
message: "Cron expression (min hour day month weekday):",
|
|
@@ -3899,7 +4037,7 @@ async function configureWakeup() {
|
|
|
3899
4037
|
config.cronExpression = cronExpression;
|
|
3900
4038
|
}
|
|
3901
4039
|
} else {
|
|
3902
|
-
const { resetCooldown } = await
|
|
4040
|
+
const { resetCooldown } = await inquirer2.prompt([{
|
|
3903
4041
|
type: "number",
|
|
3904
4042
|
name: "resetCooldown",
|
|
3905
4043
|
message: "Cooldown between triggers (minutes):",
|
|
@@ -3912,7 +4050,7 @@ async function configureWakeup() {
|
|
|
3912
4050
|
console.log("\n \u{1F4E6} Models: claude-sonnet-4-5, gemini-3-flash, gemini-3-pro-low");
|
|
3913
4051
|
console.log(" (Triggers both Claude and Gemini families)");
|
|
3914
4052
|
if (accounts.length > 1) {
|
|
3915
|
-
const { selectedAccounts } = await
|
|
4053
|
+
const { selectedAccounts } = await inquirer2.prompt([{
|
|
3916
4054
|
type: "checkbox",
|
|
3917
4055
|
name: "selectedAccounts",
|
|
3918
4056
|
message: "Select accounts to use:",
|
|
@@ -3926,14 +4064,14 @@ async function configureWakeup() {
|
|
|
3926
4064
|
} else {
|
|
3927
4065
|
config.selectedAccounts = void 0;
|
|
3928
4066
|
}
|
|
3929
|
-
const { customPrompt } = await
|
|
4067
|
+
const { customPrompt } = await inquirer2.prompt([{
|
|
3930
4068
|
type: "input",
|
|
3931
4069
|
name: "customPrompt",
|
|
3932
4070
|
message: 'Custom wake-up prompt (leave empty for default "hi"):',
|
|
3933
4071
|
default: config.customPrompt || ""
|
|
3934
4072
|
}]);
|
|
3935
4073
|
config.customPrompt = customPrompt || void 0;
|
|
3936
|
-
const { maxTokens } = await
|
|
4074
|
+
const { maxTokens } = await inquirer2.prompt([{
|
|
3937
4075
|
type: "number",
|
|
3938
4076
|
name: "maxTokens",
|
|
3939
4077
|
message: "Max output tokens (0 = no limit):",
|
|
@@ -3947,7 +4085,7 @@ async function configureWakeup() {
|
|
|
3947
4085
|
console.log(` Models: ${config.selectedModels.join(", ")}`);
|
|
3948
4086
|
console.log(` Accounts: ${config.selectedAccounts?.join(", ") || "Active account"}`);
|
|
3949
4087
|
if (!config.wakeOnReset && isCronSupported()) {
|
|
3950
|
-
const { installNow } = await
|
|
4088
|
+
const { installNow } = await inquirer2.prompt([{
|
|
3951
4089
|
type: "confirm",
|
|
3952
4090
|
name: "installNow",
|
|
3953
4091
|
message: "Install to system cron now?",
|
|
@@ -4059,7 +4197,7 @@ async function runTestTrigger() {
|
|
|
4059
4197
|
}
|
|
4060
4198
|
let accountEmail = accounts[0];
|
|
4061
4199
|
if (accounts.length > 1) {
|
|
4062
|
-
const { selectedAccount } = await
|
|
4200
|
+
const { selectedAccount } = await inquirer2.prompt([{
|
|
4063
4201
|
type: "list",
|
|
4064
4202
|
name: "selectedAccount",
|
|
4065
4203
|
message: "Select account:",
|
|
@@ -4068,13 +4206,13 @@ async function runTestTrigger() {
|
|
|
4068
4206
|
accountEmail = selectedAccount;
|
|
4069
4207
|
}
|
|
4070
4208
|
const config = loadWakeupConfig();
|
|
4071
|
-
const { modelId } = await
|
|
4209
|
+
const { modelId } = await inquirer2.prompt([{
|
|
4072
4210
|
type: "input",
|
|
4073
4211
|
name: "modelId",
|
|
4074
4212
|
message: "Model ID to test:",
|
|
4075
4213
|
default: config?.selectedModels[0] || "claude-sonnet-4-5"
|
|
4076
4214
|
}]);
|
|
4077
|
-
const { prompt } = await
|
|
4215
|
+
const { prompt } = await inquirer2.prompt([{
|
|
4078
4216
|
type: "input",
|
|
4079
4217
|
name: "prompt",
|
|
4080
4218
|
message: "Test prompt:",
|
|
@@ -4195,10 +4333,10 @@ program.name("antigravity-usage").description("CLI tool to check Antigravity mod
|
|
|
4195
4333
|
setDebugMode(true);
|
|
4196
4334
|
}
|
|
4197
4335
|
});
|
|
4198
|
-
program.command("login").description("Authenticate with Google (adds a new account)").option("--no-browser", "Do not open browser, print URL instead").option("-p, --port <port>", "Port for OAuth callback server", parseInt).action(loginCommand);
|
|
4336
|
+
program.command("login").description("Authenticate with Google (adds a new account)").option("--no-browser", "Do not open browser, print URL instead").option("--manual", "Manual login flow (copy-paste URL)").option("-p, --port <port>", "Port for OAuth callback server", parseInt).action(loginCommand);
|
|
4199
4337
|
program.command("logout [email]").description("Remove stored credentials").option("--all", "Logout from all accounts").action((email, options) => logoutCommand(options, email));
|
|
4200
4338
|
program.command("status").description("Show current authentication status").option("--all", "Show status for all accounts").option("-a, --account <email>", "Show status for specific account").action(statusCommand);
|
|
4201
|
-
program.command("quota", { isDefault: true }).description("Fetch and display quota information").option("--json", "Output as JSON").option("-m, --method <method>", "Method to use: auto (default), local, or google", "auto").option("--all", "Show quota for all accounts").option("-a, --account <email>", "Show quota for specific account").option("--refresh", "Force refresh (ignore cache)").action(quotaCommand);
|
|
4339
|
+
program.command("quota", { isDefault: true }).description("Fetch and display quota information").option("--json", "Output as JSON").option("-m, --method <method>", "Method to use: auto (default), local, or google", "auto").option("--all", "Show quota for all accounts").option("-a, --account <email>", "Show quota for specific account").option("--refresh", "Force refresh (ignore cache)").option("--all-models", "Include autocomplete models (Gemini 2.5) in quota display").action(quotaCommand);
|
|
4202
4340
|
var accountsCmd = program.command("accounts").description("Manage multiple accounts");
|
|
4203
4341
|
accountsCmd.command("list").description("List all accounts").option("--refresh", "Show refresh tip").action((options) => accountsCommand("list", [], options));
|
|
4204
4342
|
accountsCmd.command("add").description("Add a new account (triggers OAuth login)").action(() => accountsCommand("add", [], {}));
|