@anthonyhaussman/opencode-agy-auth 1.2.3-alpha.3 → 1.2.3-alpha.5

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/dist/index.js CHANGED
@@ -1581,7 +1581,11 @@ function createOAuthAuthorizeMethod(options) {
1581
1581
  const isHeadless = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY || process.env.OPENCODE_HEADLESS);
1582
1582
  const authorization = await authorizeAgy();
1583
1583
  if (!isHeadless) {
1584
- openBrowserUrl(authorization.url);
1584
+ if (options?.openBrowser) {
1585
+ options.openBrowser(authorization.url);
1586
+ } else {
1587
+ openBrowserUrl(authorization.url);
1588
+ }
1585
1589
  }
1586
1590
  return {
1587
1591
  url: authorization.url,
@@ -1635,16 +1639,23 @@ function parseOAuthCallbackInput(input) {
1635
1639
  }
1636
1640
  return { code: trimmed };
1637
1641
  }
1642
+ function defaultOpenBrowserLauncher(command, args) {
1643
+ const child = spawn(command, args, {
1644
+ stdio: "ignore",
1645
+ detached: true
1646
+ });
1647
+ child.unref?.();
1648
+ }
1649
+ var openBrowserLauncher = defaultOpenBrowserLauncher;
1638
1650
  function openBrowserUrl(url2) {
1651
+ if ((process.env.NODE_ENV === "test" || process.env.VITEST) && openBrowserLauncher === defaultOpenBrowserLauncher) {
1652
+ return;
1653
+ }
1639
1654
  try {
1640
1655
  const platform2 = process.platform;
1641
1656
  const command = platform2 === "darwin" ? "open" : platform2 === "win32" ? "rundll32" : "xdg-open";
1642
1657
  const args = platform2 === "win32" ? ["url.dll,FileProtocolHandler", url2] : [url2];
1643
- const child = spawn(command, args, {
1644
- stdio: "ignore",
1645
- detached: true
1646
- });
1647
- child.unref?.();
1658
+ openBrowserLauncher(command, args);
1648
1659
  } catch {
1649
1660
  }
1650
1661
  }
@@ -20664,6 +20675,10 @@ function defineOpenCodeV2Plugin(def) {
20664
20675
  }
20665
20676
 
20666
20677
  // src/plugin/v2.ts
20678
+ import { homedir as homedir4 } from "os";
20679
+ import { join as join6 } from "path";
20680
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
20681
+ import "http";
20667
20682
  var AGY_V2_QUOTA_COMMAND = "agy-quota";
20668
20683
  var AGY_V2_QUOTA_SUMMARY_COMMAND = "agy-quota-summary";
20669
20684
  var AGY_V2_QUOTA_COMMAND_TEMPLATE = `Retrieve Agy Code Assist quota usage for the current authenticated account.
@@ -20676,6 +20691,207 @@ var AGY_V2_QUOTA_SUMMARY_COMMAND_TEMPLATE = `Retrieve Agy Code Assist quota summ
20676
20691
  Immediately call \`${AGY_QUOTA_SUMMARY_TOOL_NAME}\` with no arguments and return its output verbatim.
20677
20692
  Do not call other tools.
20678
20693
  `;
20694
+ function getSafeHeader2(headers, key) {
20695
+ if (!headers) {
20696
+ return void 0;
20697
+ }
20698
+ const targetKey = key.toLowerCase();
20699
+ if (typeof headers.get === "function") {
20700
+ try {
20701
+ return headers.get(targetKey) || void 0;
20702
+ } catch {
20703
+ }
20704
+ }
20705
+ if (Array.isArray(headers)) {
20706
+ const found = headers.find((item) => {
20707
+ if (Array.isArray(item) && typeof item[0] === "string") {
20708
+ return item[0].toLowerCase() === targetKey;
20709
+ }
20710
+ return false;
20711
+ });
20712
+ return found ? String(found[1]) : void 0;
20713
+ }
20714
+ if (typeof headers === "object") {
20715
+ const foundKey = Object.keys(headers).find((k) => k.toLowerCase() === targetKey);
20716
+ return foundKey ? headers[foundKey] !== void 0 ? String(headers[foundKey]) : void 0 : void 0;
20717
+ }
20718
+ return void 0;
20719
+ }
20720
+ function setSafeHeaders2(initHeaders, newHeaders) {
20721
+ if (typeof globalThis.Headers !== "undefined") {
20722
+ const headers = new globalThis.Headers(initHeaders ?? {});
20723
+ for (const [k, v] of Object.entries(newHeaders)) {
20724
+ headers.set(k, v);
20725
+ }
20726
+ return headers;
20727
+ }
20728
+ if (Array.isArray(initHeaders)) {
20729
+ const nextHeaders2 = [...initHeaders];
20730
+ for (const [k, v] of Object.entries(newHeaders)) {
20731
+ const idx = nextHeaders2.findIndex((item) => Array.isArray(item) && typeof item[0] === "string" && item[0].toLowerCase() === k.toLowerCase());
20732
+ if (idx !== -1) {
20733
+ nextHeaders2[idx] = [k, v];
20734
+ } else {
20735
+ nextHeaders2.push([k, v]);
20736
+ }
20737
+ }
20738
+ return nextHeaders2;
20739
+ }
20740
+ const nextHeaders = {};
20741
+ if (initHeaders && typeof initHeaders === "object") {
20742
+ for (const [k, v] of Object.entries(initHeaders)) {
20743
+ nextHeaders[k] = String(v);
20744
+ }
20745
+ }
20746
+ for (const [k, v] of Object.entries(newHeaders)) {
20747
+ const existingKey = Object.keys(nextHeaders).find((key) => key.toLowerCase() === k.toLowerCase());
20748
+ if (existingKey) {
20749
+ nextHeaders[existingKey] = v;
20750
+ } else {
20751
+ nextHeaders[k] = v;
20752
+ }
20753
+ }
20754
+ return nextHeaders;
20755
+ }
20756
+ function toUrlString2(value) {
20757
+ if (typeof value === "string") {
20758
+ return value;
20759
+ }
20760
+ const candidate = value.url;
20761
+ if (candidate) {
20762
+ return candidate;
20763
+ }
20764
+ return value.toString();
20765
+ }
20766
+ function resolveModelTier2(baseModelId, init) {
20767
+ const parts = baseModelId.split("@");
20768
+ const base = parts[0] || "";
20769
+ const suffixTier = parts[1]?.toLowerCase();
20770
+ const mapping = TIER_MAPPING[base];
20771
+ if (!mapping) {
20772
+ return baseModelId;
20773
+ }
20774
+ const headerTier = getSafeHeader2(init?.headers, "x-agy-tier")?.toLowerCase() || null;
20775
+ const requestedTier = headerTier || suffixTier;
20776
+ if (requestedTier && Object.prototype.hasOwnProperty.call(mapping, requestedTier)) {
20777
+ return mapping[requestedTier] || baseModelId;
20778
+ }
20779
+ return mapping["medium"] ?? mapping["high"];
20780
+ }
20781
+ var storedAuthOverrideForTesting = void 0;
20782
+ function loadStoredAuthFromJson() {
20783
+ if (storedAuthOverrideForTesting !== void 0) {
20784
+ return storedAuthOverrideForTesting;
20785
+ }
20786
+ const authPath = join6(homedir4(), ".local", "share", "opencode", "auth.json");
20787
+ try {
20788
+ if (existsSync6(authPath)) {
20789
+ const content = readFileSync5(authPath, "utf8");
20790
+ const data = JSON.parse(content);
20791
+ return data?.[AGY_PROVIDER_ID];
20792
+ }
20793
+ } catch {
20794
+ }
20795
+ return void 0;
20796
+ }
20797
+ function createV2FetchInterceptor(getAuthSnapshot) {
20798
+ return async function agyV2Fetch(input, init) {
20799
+ const isGL = isGenerativeLanguageRequest(input);
20800
+ const isInternal = toUrlString2(input).includes("cloudcode-pa.googleapis.com");
20801
+ if (!isGL && !isInternal) {
20802
+ return agyFetch(input, init);
20803
+ }
20804
+ let rawAuth = getAuthSnapshot ? await getAuthSnapshot() : void 0;
20805
+ if (!rawAuth) {
20806
+ rawAuth = loadStoredAuthFromJson();
20807
+ }
20808
+ if (!rawAuth || !isOAuthAuth(rawAuth)) {
20809
+ return agyFetch(input, init);
20810
+ }
20811
+ let authRecord = resolveCachedAuth(rawAuth);
20812
+ if (accessTokenExpired(authRecord)) {
20813
+ const refreshed = await refreshAccessToken(authRecord, { auth: { set: async () => {
20814
+ } } });
20815
+ if (refreshed) {
20816
+ authRecord = refreshed;
20817
+ }
20818
+ }
20819
+ if (!authRecord.access) {
20820
+ return agyFetch(input, init);
20821
+ }
20822
+ if (isInternal) {
20823
+ const hasAuth = getSafeHeader2(init?.headers, "Authorization") !== void 0;
20824
+ if (hasAuth) {
20825
+ return agyFetch(input, init);
20826
+ }
20827
+ const userAgent = buildAgyCliUserAgent();
20828
+ const headers = setSafeHeaders2(init?.headers, {
20829
+ Authorization: `Bearer ${authRecord.access}`,
20830
+ "User-Agent": userAgent
20831
+ });
20832
+ return agyFetch(input, {
20833
+ ...init,
20834
+ headers
20835
+ });
20836
+ }
20837
+ const requestTarget = parseGenerativeLanguageRequest(input);
20838
+ const requestUserAgentModel = requestTarget?.effectiveModel;
20839
+ let projectContext;
20840
+ try {
20841
+ projectContext = await ensureProjectContext(
20842
+ authRecord,
20843
+ { auth: { set: async () => {
20844
+ } } },
20845
+ void 0,
20846
+ requestUserAgentModel
20847
+ );
20848
+ } catch {
20849
+ projectContext = {
20850
+ effectiveProjectId: parseRefreshParts(authRecord.refresh).projectId || "antigravity",
20851
+ auth: authRecord
20852
+ };
20853
+ }
20854
+ const originalRequestedModel = parseGenerativeLanguageRequest(input)?.effectiveModel;
20855
+ let modifiedInput = input;
20856
+ if (isGL && originalRequestedModel) {
20857
+ const originalBase = originalRequestedModel.replace("google-agy/", "");
20858
+ const resolvedBase = resolveModelTier2(originalBase, init);
20859
+ if (originalBase !== resolvedBase) {
20860
+ if (typeof modifiedInput === "string") {
20861
+ modifiedInput = modifiedInput.replace(`models/${originalBase}`, `models/${resolvedBase}`);
20862
+ } else if (typeof Request !== "undefined" && modifiedInput instanceof Request) {
20863
+ const newUrl = modifiedInput.url.replace(`models/${originalBase}`, `models/${resolvedBase}`);
20864
+ modifiedInput = new Request(newUrl, modifiedInput);
20865
+ }
20866
+ }
20867
+ }
20868
+ const transformed = prepareAgyRequest(
20869
+ modifiedInput,
20870
+ init,
20871
+ authRecord.access,
20872
+ projectContext.effectiveProjectId,
20873
+ void 0
20874
+ );
20875
+ const chatLogger = createChatLogger();
20876
+ if (chatLogger) {
20877
+ chatLogger.logRequest(
20878
+ toUrlString2(transformed.request),
20879
+ transformed.init.method || "GET",
20880
+ transformed.init.headers,
20881
+ transformed.init.body
20882
+ );
20883
+ }
20884
+ const response = await fetchWithRetry(transformed.request, transformed.init);
20885
+ return transformAgyResponse(
20886
+ response,
20887
+ transformed.streaming,
20888
+ null,
20889
+ transformed.requestedModel,
20890
+ transformed.sessionId,
20891
+ chatLogger
20892
+ );
20893
+ };
20894
+ }
20679
20895
  async function setupOpenCodeV2(ctx) {
20680
20896
  ctx.catalog.transform(async (editor) => {
20681
20897
  if (!editor) return;
@@ -20853,14 +21069,65 @@ async function setupOpenCodeV2(ctx) {
20853
21069
  label: "Antigravity CLI (OAuth)"
20854
21070
  },
20855
21071
  authorize: async () => {
21072
+ const authorization = await authorizeAgy();
20856
21073
  return {
20857
- mode: "auto",
20858
- url: "https://accounts.google.com/o/oauth2/auth",
20859
- instructions: "Login with Google Cloud / Antigravity CLI credentials"
21074
+ mode: "code",
21075
+ url: authorization.url,
21076
+ instructions: "Please complete Google account authorization in your browser. After authorization, copy the full redirect URL or code and paste it below:",
21077
+ callback: async (code) => {
21078
+ const trimmed = code.trim();
21079
+ const parsed = parseOAuthCallbackInput(trimmed);
21080
+ const authCode = parsed.code || trimmed;
21081
+ if (!authCode) {
21082
+ return { type: "failed", error: "Missing authorization code in callback input" };
21083
+ }
21084
+ if (parsed.state && parsed.state !== authorization.state) {
21085
+ return { type: "failed", error: "State mismatch in callback input (possible CSRF attempt)" };
21086
+ }
21087
+ const exchangeResult = await exchangeAgyWithVerifier(authCode, authorization.verifier);
21088
+ if (exchangeResult.type !== "success") {
21089
+ return exchangeResult;
21090
+ }
21091
+ const initialRefresh = formatRefreshParts({
21092
+ refreshToken: exchangeResult.refresh
21093
+ });
21094
+ let authRecord = {
21095
+ type: "oauth",
21096
+ refresh: initialRefresh,
21097
+ access: exchangeResult.access,
21098
+ expires: exchangeResult.expires
21099
+ };
21100
+ try {
21101
+ const projectContext = await ensureProjectContext(
21102
+ authRecord,
21103
+ { auth: { set: async () => {
21104
+ } } }
21105
+ );
21106
+ authRecord = {
21107
+ ...authRecord,
21108
+ refresh: projectContext.auth.refresh
21109
+ };
21110
+ } catch {
21111
+ }
21112
+ return {
21113
+ type: "success",
21114
+ refresh: authRecord.refresh,
21115
+ access: authRecord.access,
21116
+ expires: authRecord.expires
21117
+ };
21118
+ }
20860
21119
  };
20861
21120
  }
20862
21121
  });
20863
21122
  });
21123
+ ctx.aisdk?.hook("sdk", async (event) => {
21124
+ if (!event) return;
21125
+ const isMatchingProvider = event.providerID === AGY_PROVIDER_ID || event.model?.providerID === AGY_PROVIDER_ID;
21126
+ if (!isMatchingProvider) return;
21127
+ event.options = event.options || {};
21128
+ event.options.apiKey = "dummy";
21129
+ event.options.fetch = createV2FetchInterceptor();
21130
+ });
20864
21131
  ctx.session.hook(
20865
21132
  "http.request",
20866
21133
  async (event) => {
@@ -20887,6 +21154,19 @@ async function setupOpenCodeV2(ctx) {
20887
21154
  }
20888
21155
  }
20889
21156
  }
21157
+ if (event.request?.headers) {
21158
+ if (typeof event.request.headers.delete === "function") {
21159
+ event.request.headers.delete("x-goog-api-key");
21160
+ event.request.headers.delete("api-key");
21161
+ } else {
21162
+ for (const key of Object.keys(event.request.headers)) {
21163
+ const lower = key.toLowerCase();
21164
+ if (lower === "x-goog-api-key" || lower === "api-key") {
21165
+ delete event.request.headers[key];
21166
+ }
21167
+ }
21168
+ }
21169
+ }
20890
21170
  try {
20891
21171
  const parsedUrl = new URL(rawUrl);
20892
21172
  let urlChanged = false;
@@ -20909,18 +21189,29 @@ async function setupOpenCodeV2(ctx) {
20909
21189
  }
20910
21190
  } catch {
20911
21191
  }
20912
- const hasAuth = typeof event.headers.get === "function" ? !!event.headers.get("Authorization") : Object.keys(event.headers).some((k) => k.toLowerCase() === "authorization");
21192
+ const hasAuth = typeof event.headers?.get === "function" ? !!event.headers.get("Authorization") : event.headers && Object.keys(event.headers).some((k) => k.toLowerCase() === "authorization");
20913
21193
  if (!hasAuth) {
20914
21194
  let token;
20915
21195
  if (typeof event.auth?.access === "string" && event.auth.access) {
20916
21196
  token = event.auth.access;
20917
21197
  } else if (typeof event.auth?.token === "string" && event.auth.token) {
20918
21198
  token = event.auth.token;
20919
- }
20920
- if (typeof event.headers.set === "function") {
20921
- event.headers.set("Authorization", `Bearer ${token || "dummy"}`);
20922
21199
  } else {
20923
- event.headers["Authorization"] = `Bearer ${token || "dummy"}`;
21200
+ const storedAuth = loadStoredAuthFromJson();
21201
+ if (storedAuth && isOAuthAuth(storedAuth)) {
21202
+ const resolved = resolveCachedAuth(storedAuth);
21203
+ token = resolved.access;
21204
+ }
21205
+ }
21206
+ if (token) {
21207
+ if (typeof event.headers?.set === "function") {
21208
+ event.headers.set("Authorization", `Bearer ${token}`);
21209
+ } else if (event.headers) {
21210
+ event.headers["Authorization"] = `Bearer ${token}`;
21211
+ }
21212
+ if (event.request?.headers && typeof event.request.headers.set === "function") {
21213
+ event.request.headers.set("Authorization", `Bearer ${token}`);
21214
+ }
20924
21215
  }
20925
21216
  }
20926
21217
  }