@anthonyhaussman/opencode-agy-auth 1.1.18 → 1.1.19-alpha.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/README.md CHANGED
@@ -9,6 +9,16 @@ An [OpenCode](https://opencode.ai/) authentication plugin that enables seamless
9
9
  - **Quota Tracking**: Injects the `agy_quota` tool into OpenCode to check usage limits directly.
10
10
  - **Traffic Simulation**: Maintains background heartbeat with `agy` servers.
11
11
 
12
+ ## Prerequisites
13
+
14
+ The plugin dynamically resolves Google OAuth credentials at runtime using one of the following methods:
15
+
16
+ 1. **Installed `agy` / `antigravity` CLI binary (recommended)**:
17
+ - Ensure `agy` or `antigravity` is installed and available in your `PATH`.
18
+ - Alternatively, specify the binary path using the `AGY_BIN_PATH` environment variable.
19
+ 2. **Environment variables (headless/CI or standalone environments)**:
20
+ - Set `AGY_CLIENT_ID` (or `GOOGLE_AGY_CLIENT_ID`) and `AGY_CLIENT_SECRET` (or `GOOGLE_AGY_CLIENT_SECRET`).
21
+
12
22
  ## Installation
13
23
 
14
24
  Install the plugin from npm (or directly using local file configurations if developing):
@@ -42,6 +52,9 @@ You can switch your active OpenCode provider to `agy` or let the plugin inject a
42
52
 
43
53
  If you are running OpenCode in environments with specific requirements for Antigravity, you can use the following environment variables:
44
54
 
55
+ - `AGY_BIN_PATH`: Explicit path to the `agy` / `antigravity` CLI executable.
56
+ - `AGY_CLIENT_ID` / `GOOGLE_AGY_CLIENT_ID`: Override or explicitly provide the Google OAuth Client ID.
57
+ - `AGY_CLIENT_SECRET` / `GOOGLE_AGY_CLIENT_SECRET`: Override or explicitly provide the Google OAuth Client Secret.
45
58
  - `OPENCODE_AGY_PROJECT_ID`: Specify your Google Cloud Project ID manually.
46
59
  - `OPENCODE_AGY_AUTH_PROXY`: Define a proxy if accessing authentication endpoints behind a corporate firewall.
47
60
  - `OPENCODE_AGY_ENDPOINT`: Override the default internal `daily-cloudcode-pa.googleapis.com` API endpoint.
package/dist/index.js CHANGED
@@ -102,10 +102,106 @@ function updateStaticModelsWithPricing(staticModels) {
102
102
  });
103
103
  }
104
104
 
105
+ // src/sdk/credentials.ts
106
+ import { execSync } from "child_process";
107
+ import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "fs";
108
+ import { homedir } from "os";
109
+ import { join as join2 } from "path";
110
+ var cachedCredentials = null;
111
+ function findAgyBinary() {
112
+ if (process.env.AGY_BIN_PATH && existsSync2(process.env.AGY_BIN_PATH)) {
113
+ try {
114
+ return realpathSync(process.env.AGY_BIN_PATH);
115
+ } catch {
116
+ return process.env.AGY_BIN_PATH;
117
+ }
118
+ }
119
+ try {
120
+ const which = execSync("which agy 2>/dev/null || which antigravity 2>/dev/null", {
121
+ encoding: "utf8",
122
+ stdio: ["pipe", "pipe", "ignore"]
123
+ }).trim();
124
+ if (which && existsSync2(which)) {
125
+ return realpathSync(which);
126
+ }
127
+ } catch {
128
+ }
129
+ const home = homedir();
130
+ const candidatePaths = [
131
+ join2(home, ".local/share/mise/installs/antigravity-cli/latest/agy"),
132
+ join2(home, ".local/share/mise/installs/antigravity-cli/latest/antigravity"),
133
+ join2(home, ".local/bin/agy"),
134
+ join2(home, ".local/bin/antigravity"),
135
+ join2(home, "bin/agy"),
136
+ join2(home, "bin/antigravity"),
137
+ "/usr/local/bin/agy",
138
+ "/usr/local/bin/antigravity",
139
+ "/usr/bin/agy",
140
+ "/usr/bin/antigravity"
141
+ ];
142
+ for (const candidate of candidatePaths) {
143
+ if (existsSync2(candidate)) {
144
+ try {
145
+ return realpathSync(candidate);
146
+ } catch {
147
+ return candidate;
148
+ }
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ function extractAgyCredentialsFromBinary(binaryPath) {
154
+ try {
155
+ const buffer = readFileSync2(binaryPath);
156
+ const content = buffer.toString("latin1");
157
+ const clientIds = [...new Set(content.match(/[0-9]+-[a-z0-9_]+\.apps\.googleusercontent\.com/g) || [])];
158
+ const clientSecrets = [...new Set(content.match(/GOCSPX-[A-Za-z0-9_-]{28}/g) || [])];
159
+ if (clientIds.length === 0 || clientSecrets.length === 0) {
160
+ return null;
161
+ }
162
+ const preferredClientId = clientIds.find((id) => id.startsWith("1071")) || clientIds[clientIds.length - 1];
163
+ const preferredClientSecret = clientSecrets[0];
164
+ if (!preferredClientId || !preferredClientSecret) {
165
+ return null;
166
+ }
167
+ return {
168
+ clientId: preferredClientId,
169
+ clientSecret: preferredClientSecret
170
+ };
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
175
+ function getAgyCredentials() {
176
+ const envClientId = process.env.AGY_CLIENT_ID || process.env.GOOGLE_AGY_CLIENT_ID;
177
+ const envClientSecret = process.env.AGY_CLIENT_SECRET || process.env.GOOGLE_AGY_CLIENT_SECRET;
178
+ if (envClientId && envClientSecret) {
179
+ return { clientId: envClientId, clientSecret: envClientSecret };
180
+ }
181
+ if (cachedCredentials) {
182
+ return cachedCredentials;
183
+ }
184
+ const binaryPath = findAgyBinary();
185
+ if (binaryPath) {
186
+ const extracted = extractAgyCredentialsFromBinary(binaryPath);
187
+ if (extracted) {
188
+ cachedCredentials = extracted;
189
+ return extracted;
190
+ }
191
+ }
192
+ throw new Error(
193
+ 'Antigravity OAuth credentials not found. Ensure the "agy" CLI is installed and in PATH, or set the AGY_CLIENT_ID and AGY_CLIENT_SECRET environment variables.'
194
+ );
195
+ }
196
+ function getAgyClientId() {
197
+ return getAgyCredentials().clientId;
198
+ }
199
+ function getAgyClientSecret() {
200
+ return getAgyCredentials().clientSecret;
201
+ }
202
+
105
203
  // src/constants.ts
106
204
  var AGY_PROVIDER_ID = "google-agy";
107
- var AGY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
108
- var AGY_CLIENT_SECRET = "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";
109
205
  var AGY_SCOPES = [
110
206
  "openid",
111
207
  "https://www.googleapis.com/auth/cloud-platform",
@@ -194,7 +290,7 @@ async function authorizeAgy() {
194
290
  const pkce = await generatePKCE();
195
291
  const state = randomBytes(32).toString("hex");
196
292
  const url2 = new URL("https://accounts.google.com/o/oauth2/v2/auth");
197
- url2.searchParams.set("client_id", AGY_CLIENT_ID);
293
+ url2.searchParams.set("client_id", getAgyClientId());
198
294
  url2.searchParams.set("response_type", "code");
199
295
  url2.searchParams.set("redirect_uri", AGY_REDIRECT_URI);
200
296
  url2.searchParams.set("scope", AGY_SCOPES.join(" "));
@@ -227,8 +323,8 @@ async function exchangeAgyWithVerifierInternal(code, verifier) {
227
323
  "Content-Type": "application/x-www-form-urlencoded"
228
324
  },
229
325
  body: new URLSearchParams({
230
- client_id: AGY_CLIENT_ID,
231
- client_secret: AGY_CLIENT_SECRET,
326
+ client_id: getAgyClientId(),
327
+ client_secret: getAgyClientSecret(),
232
328
  code,
233
329
  grant_type: "authorization_code",
234
330
  redirect_uri: AGY_REDIRECT_URI,
@@ -720,29 +816,29 @@ function clampDelay(delayMs) {
720
816
  }
721
817
 
722
818
  // src/sdk/retry/cooldown-store.ts
723
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
724
- import { join as join2, dirname } from "path";
725
- import { homedir, tmpdir as tmpdir2 } from "os";
819
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
820
+ import { join as join3, dirname } from "path";
821
+ import { homedir as homedir2, tmpdir as tmpdir2 } from "os";
726
822
  var WRITE_THROTTLE_MS = 5e3;
727
823
  function getConfigDir() {
728
824
  const platform2 = process.platform;
729
825
  if (platform2 === "win32") {
730
- return join2(process.env.APPDATA || join2(homedir(), "AppData", "Roaming"), "opencode");
826
+ return join3(process.env.APPDATA || join3(homedir2(), "AppData", "Roaming"), "opencode");
731
827
  }
732
- const xdgConfig = process.env.XDG_CONFIG_HOME || join2(homedir(), ".config");
733
- return join2(xdgConfig, "opencode");
828
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config");
829
+ return join3(xdgConfig, "opencode");
734
830
  }
735
831
  function getCooldownFilePath() {
736
- return join2(getConfigDir(), "antigravity-retry-cooldowns.json");
832
+ return join3(getConfigDir(), "antigravity-retry-cooldowns.json");
737
833
  }
738
834
  function loadCooldowns() {
739
835
  const result = /* @__PURE__ */ new Map();
740
836
  try {
741
837
  const filePath = getCooldownFilePath();
742
- if (!existsSync2(filePath)) {
838
+ if (!existsSync3(filePath)) {
743
839
  return result;
744
840
  }
745
- const content = readFileSync2(filePath, "utf-8");
841
+ const content = readFileSync3(filePath, "utf-8");
746
842
  const data = JSON.parse(content);
747
843
  if (data.version !== "1.0") {
748
844
  return result;
@@ -761,7 +857,7 @@ function saveCooldowns(entries) {
761
857
  try {
762
858
  const filePath = getCooldownFilePath();
763
859
  const dir = dirname(filePath);
764
- if (!existsSync2(dir)) {
860
+ if (!existsSync3(dir)) {
765
861
  mkdirSync(dir, { recursive: true });
766
862
  }
767
863
  const now = Date.now();
@@ -776,12 +872,12 @@ function saveCooldowns(entries) {
776
872
  entries: serializable,
777
873
  updatedAt: now
778
874
  };
779
- const tmpPath = join2(tmpdir2(), `antigravity-cooldowns-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
875
+ const tmpPath = join3(tmpdir2(), `antigravity-cooldowns-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
780
876
  writeFileSync2(tmpPath, JSON.stringify(data), "utf-8");
781
877
  try {
782
878
  renameSync(tmpPath, filePath);
783
879
  } catch {
784
- writeFileSync2(filePath, readFileSync2(tmpPath));
880
+ writeFileSync2(filePath, readFileSync3(tmpPath));
785
881
  try {
786
882
  unlinkSync(tmpPath);
787
883
  } catch {
@@ -1647,27 +1743,27 @@ function openBrowserUrl(url2) {
1647
1743
  import { createHash } from "crypto";
1648
1744
 
1649
1745
  // src/sdk/cache/signature-cache.ts
1650
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, appendFileSync } from "fs";
1651
- import { join as join3, dirname as dirname2 } from "path";
1652
- import { homedir as homedir2, tmpdir as tmpdir3 } from "os";
1746
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, appendFileSync } from "fs";
1747
+ import { join as join4, dirname as dirname2 } from "path";
1748
+ import { homedir as homedir3, tmpdir as tmpdir3 } from "os";
1653
1749
  function getConfigDir2() {
1654
1750
  const platform2 = process.platform;
1655
1751
  if (platform2 === "win32") {
1656
- return join3(process.env.APPDATA || join3(homedir2(), "AppData", "Roaming"), "opencode");
1752
+ return join4(process.env.APPDATA || join4(homedir3(), "AppData", "Roaming"), "opencode");
1657
1753
  }
1658
- const xdgConfig = process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config");
1659
- return join3(xdgConfig, "opencode");
1754
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
1755
+ return join4(xdgConfig, "opencode");
1660
1756
  }
1661
1757
  function getCacheFilePath() {
1662
- return join3(getConfigDir2(), "antigravity-signature-cache.json");
1758
+ return join4(getConfigDir2(), "antigravity-signature-cache.json");
1663
1759
  }
1664
1760
  function ensureGitignoreSync(configDir) {
1665
- const gitignorePath = join3(configDir, ".gitignore");
1761
+ const gitignorePath = join4(configDir, ".gitignore");
1666
1762
  const entries = [".gitignore", "antigravity-signature-cache.json"];
1667
1763
  try {
1668
1764
  let content = "";
1669
- if (existsSync3(gitignorePath)) {
1670
- content = readFileSync3(gitignorePath, "utf-8");
1765
+ if (existsSync4(gitignorePath)) {
1766
+ content = readFileSync4(gitignorePath, "utf-8");
1671
1767
  }
1672
1768
  const existingLines = content.split("\n").map((line) => line.trim());
1673
1769
  const missing = entries.filter((e) => !existingLines.includes(e));
@@ -1849,10 +1945,10 @@ var SignatureCache = class {
1849
1945
  */
1850
1946
  loadFromDisk() {
1851
1947
  try {
1852
- if (!existsSync3(this.cacheFilePath)) {
1948
+ if (!existsSync4(this.cacheFilePath)) {
1853
1949
  return;
1854
1950
  }
1855
- const content = readFileSync3(this.cacheFilePath, "utf-8");
1951
+ const content = readFileSync4(this.cacheFilePath, "utf-8");
1856
1952
  const data = JSON.parse(content);
1857
1953
  if (data.version !== "1.0") {
1858
1954
  return;
@@ -1880,15 +1976,15 @@ var SignatureCache = class {
1880
1976
  saveToDisk() {
1881
1977
  try {
1882
1978
  const dir = dirname2(this.cacheFilePath);
1883
- if (!existsSync3(dir)) {
1979
+ if (!existsSync4(dir)) {
1884
1980
  mkdirSync2(dir, { recursive: true });
1885
1981
  }
1886
1982
  ensureGitignoreSync(dir);
1887
1983
  const now = Date.now();
1888
1984
  let existingEntries = {};
1889
- if (existsSync3(this.cacheFilePath)) {
1985
+ if (existsSync4(this.cacheFilePath)) {
1890
1986
  try {
1891
- const content = readFileSync3(this.cacheFilePath, "utf-8");
1987
+ const content = readFileSync4(this.cacheFilePath, "utf-8");
1892
1988
  const data = JSON.parse(content);
1893
1989
  existingEntries = data.entries || {};
1894
1990
  } catch {
@@ -1924,12 +2020,12 @@ var SignatureCache = class {
1924
2020
  last_write: now
1925
2021
  }
1926
2022
  };
1927
- const tmpPath = join3(tmpdir3(), `antigravity-cache-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
2023
+ const tmpPath = join4(tmpdir3(), `antigravity-cache-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
1928
2024
  writeFileSync3(tmpPath, JSON.stringify(cacheData, null, 2), "utf-8");
1929
2025
  try {
1930
2026
  renameSync2(tmpPath, this.cacheFilePath);
1931
2027
  } catch {
1932
- writeFileSync3(this.cacheFilePath, readFileSync3(tmpPath));
2028
+ writeFileSync3(this.cacheFilePath, readFileSync4(tmpPath));
1933
2029
  try {
1934
2030
  unlinkSync2(tmpPath);
1935
2031
  } catch {
@@ -2083,9 +2179,9 @@ function getLatestSignature(sessionId) {
2083
2179
  }
2084
2180
 
2085
2181
  // src/sdk/request/turn-state-tracker.ts
2086
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync3 } from "fs";
2087
- import { join as join4, dirname as dirname3 } from "path";
2088
- import { homedir as homedir3, tmpdir as tmpdir4 } from "os";
2182
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync3 } from "fs";
2183
+ import { join as join5, dirname as dirname3 } from "path";
2184
+ import { homedir as homedir4, tmpdir as tmpdir4 } from "os";
2089
2185
 
2090
2186
  // src/sdk/request/thinking.ts
2091
2187
  import { createHash as createHash2 } from "crypto";
@@ -2508,22 +2604,22 @@ var WRITE_THROTTLE_MS2 = 5e3;
2508
2604
  function getConfigDir3() {
2509
2605
  const platform2 = process.platform;
2510
2606
  if (platform2 === "win32") {
2511
- return join4(process.env.APPDATA || join4(homedir3(), "AppData", "Roaming"), "opencode");
2607
+ return join5(process.env.APPDATA || join5(homedir4(), "AppData", "Roaming"), "opencode");
2512
2608
  }
2513
- const xdgConfig = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
2514
- return join4(xdgConfig, "opencode");
2609
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join5(homedir4(), ".config");
2610
+ return join5(xdgConfig, "opencode");
2515
2611
  }
2516
2612
  function getTurnStateFilePath() {
2517
- return join4(getConfigDir3(), "antigravity-turn-states.json");
2613
+ return join5(getConfigDir3(), "antigravity-turn-states.json");
2518
2614
  }
2519
2615
  function loadTurnStatesFromDisk() {
2520
2616
  const result = /* @__PURE__ */ new Map();
2521
2617
  try {
2522
2618
  const filePath = getTurnStateFilePath();
2523
- if (!existsSync4(filePath)) {
2619
+ if (!existsSync5(filePath)) {
2524
2620
  return result;
2525
2621
  }
2526
- const content = readFileSync4(filePath, "utf-8");
2622
+ const content = readFileSync5(filePath, "utf-8");
2527
2623
  const data = JSON.parse(content);
2528
2624
  if (data.version !== "1.0") {
2529
2625
  return result;
@@ -2543,7 +2639,7 @@ function saveTurnStatesToDisk(entries) {
2543
2639
  try {
2544
2640
  const filePath = getTurnStateFilePath();
2545
2641
  const dir = dirname3(filePath);
2546
- if (!existsSync4(dir)) {
2642
+ if (!existsSync5(dir)) {
2547
2643
  mkdirSync3(dir, { recursive: true });
2548
2644
  }
2549
2645
  const now = Date.now();
@@ -2559,12 +2655,12 @@ function saveTurnStatesToDisk(entries) {
2559
2655
  entries: serializable,
2560
2656
  updatedAt: now
2561
2657
  };
2562
- const tmpPath = join4(tmpdir4(), `antigravity-turn-states-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
2658
+ const tmpPath = join5(tmpdir4(), `antigravity-turn-states-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
2563
2659
  writeFileSync4(tmpPath, JSON.stringify(data), "utf-8");
2564
2660
  try {
2565
2661
  renameSync3(tmpPath, filePath);
2566
2662
  } catch {
2567
- writeFileSync4(filePath, readFileSync4(tmpPath));
2663
+ writeFileSync4(filePath, readFileSync5(tmpPath));
2568
2664
  try {
2569
2665
  unlinkSync3(tmpPath);
2570
2666
  } catch {
@@ -15221,8 +15317,8 @@ async function refreshAccessTokenInternal(auth, client, parts) {
15221
15317
  }
15222
15318
  async function fetchTokenRefresh(refreshToken) {
15223
15319
  const tokenUrl = "https://oauth2.googleapis.com/token";
15224
- const clientId = AGY_CLIENT_ID;
15225
- const clientSecret = AGY_CLIENT_SECRET;
15320
+ const clientId = getAgyClientId();
15321
+ const clientSecret = getAgyClientSecret();
15226
15322
  const init = {
15227
15323
  method: "POST",
15228
15324
  headers: {
@@ -18942,20 +19038,20 @@ function transformStreamingPayloadStream(stream, sessionId, chatLogger) {
18942
19038
  }
18943
19039
 
18944
19040
  // src/sdk/chat-logger.ts
18945
- import { createWriteStream, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
18946
- import { join as join5 } from "path";
19041
+ import { createWriteStream, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
19042
+ import { join as join6 } from "path";
18947
19043
  import { cwd } from "process";
18948
19044
  function createChatLogger() {
18949
19045
  if (process.env.AGY_LOG !== "1") {
18950
19046
  return null;
18951
19047
  }
18952
19048
  try {
18953
- const logDir = join5(cwd(), "agy_chat_log");
18954
- if (!existsSync5(logDir)) {
19049
+ const logDir = join6(cwd(), "agy_chat_log");
19050
+ if (!existsSync6(logDir)) {
18955
19051
  mkdirSync4(logDir, { recursive: true });
18956
19052
  }
18957
19053
  const timestamp = Date.now();
18958
- const logFile = join5(logDir, `${timestamp}.log`);
19054
+ const logFile = join6(logDir, `${timestamp}.log`);
18959
19055
  const stream = createWriteStream(logFile, { flags: "w", encoding: "utf8" });
18960
19056
  return new ChatLoggerImpl(stream);
18961
19057
  } catch (error45) {