@anthonyhaussman/opencode-agy-auth 1.2.3-alpha.2 → 1.2.3-alpha.4

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
@@ -104,6 +104,8 @@ function updateStaticModelsWithPricing(staticModels) {
104
104
 
105
105
  // src/constants.ts
106
106
  var AGY_PROVIDER_ID = "google-agy";
107
+ var AGY_AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
108
+ var AGY_TOKEN_URL = "https://oauth2.googleapis.com/token";
107
109
  var AGY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
108
110
  var AGY_CLIENT_SECRET = "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";
109
111
  var AGY_SCOPES = [
@@ -20664,6 +20666,9 @@ function defineOpenCodeV2Plugin(def) {
20664
20666
  }
20665
20667
 
20666
20668
  // src/plugin/v2.ts
20669
+ import { homedir as homedir4 } from "os";
20670
+ import { join as join6 } from "path";
20671
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
20667
20672
  var AGY_V2_QUOTA_COMMAND = "agy-quota";
20668
20673
  var AGY_V2_QUOTA_SUMMARY_COMMAND = "agy-quota-summary";
20669
20674
  var AGY_V2_QUOTA_COMMAND_TEMPLATE = `Retrieve Agy Code Assist quota usage for the current authenticated account.
@@ -20676,6 +20681,203 @@ var AGY_V2_QUOTA_SUMMARY_COMMAND_TEMPLATE = `Retrieve Agy Code Assist quota summ
20676
20681
  Immediately call \`${AGY_QUOTA_SUMMARY_TOOL_NAME}\` with no arguments and return its output verbatim.
20677
20682
  Do not call other tools.
20678
20683
  `;
20684
+ function getSafeHeader2(headers, key) {
20685
+ if (!headers) {
20686
+ return void 0;
20687
+ }
20688
+ const targetKey = key.toLowerCase();
20689
+ if (typeof headers.get === "function") {
20690
+ try {
20691
+ return headers.get(targetKey) || void 0;
20692
+ } catch {
20693
+ }
20694
+ }
20695
+ if (Array.isArray(headers)) {
20696
+ const found = headers.find((item) => {
20697
+ if (Array.isArray(item) && typeof item[0] === "string") {
20698
+ return item[0].toLowerCase() === targetKey;
20699
+ }
20700
+ return false;
20701
+ });
20702
+ return found ? String(found[1]) : void 0;
20703
+ }
20704
+ if (typeof headers === "object") {
20705
+ const foundKey = Object.keys(headers).find((k) => k.toLowerCase() === targetKey);
20706
+ return foundKey ? headers[foundKey] !== void 0 ? String(headers[foundKey]) : void 0 : void 0;
20707
+ }
20708
+ return void 0;
20709
+ }
20710
+ function setSafeHeaders2(initHeaders, newHeaders) {
20711
+ if (typeof globalThis.Headers !== "undefined") {
20712
+ const headers = new globalThis.Headers(initHeaders ?? {});
20713
+ for (const [k, v] of Object.entries(newHeaders)) {
20714
+ headers.set(k, v);
20715
+ }
20716
+ return headers;
20717
+ }
20718
+ if (Array.isArray(initHeaders)) {
20719
+ const nextHeaders2 = [...initHeaders];
20720
+ for (const [k, v] of Object.entries(newHeaders)) {
20721
+ const idx = nextHeaders2.findIndex((item) => Array.isArray(item) && typeof item[0] === "string" && item[0].toLowerCase() === k.toLowerCase());
20722
+ if (idx !== -1) {
20723
+ nextHeaders2[idx] = [k, v];
20724
+ } else {
20725
+ nextHeaders2.push([k, v]);
20726
+ }
20727
+ }
20728
+ return nextHeaders2;
20729
+ }
20730
+ const nextHeaders = {};
20731
+ if (initHeaders && typeof initHeaders === "object") {
20732
+ for (const [k, v] of Object.entries(initHeaders)) {
20733
+ nextHeaders[k] = String(v);
20734
+ }
20735
+ }
20736
+ for (const [k, v] of Object.entries(newHeaders)) {
20737
+ const existingKey = Object.keys(nextHeaders).find((key) => key.toLowerCase() === k.toLowerCase());
20738
+ if (existingKey) {
20739
+ nextHeaders[existingKey] = v;
20740
+ } else {
20741
+ nextHeaders[k] = v;
20742
+ }
20743
+ }
20744
+ return nextHeaders;
20745
+ }
20746
+ function toUrlString2(value) {
20747
+ if (typeof value === "string") {
20748
+ return value;
20749
+ }
20750
+ const candidate = value.url;
20751
+ if (candidate) {
20752
+ return candidate;
20753
+ }
20754
+ return value.toString();
20755
+ }
20756
+ function resolveModelTier2(baseModelId, init) {
20757
+ const parts = baseModelId.split("@");
20758
+ const base = parts[0] || "";
20759
+ const suffixTier = parts[1]?.toLowerCase();
20760
+ const mapping = TIER_MAPPING[base];
20761
+ if (!mapping) {
20762
+ return baseModelId;
20763
+ }
20764
+ const headerTier = getSafeHeader2(init?.headers, "x-agy-tier")?.toLowerCase() || null;
20765
+ const requestedTier = headerTier || suffixTier;
20766
+ if (requestedTier && Object.prototype.hasOwnProperty.call(mapping, requestedTier)) {
20767
+ return mapping[requestedTier] || baseModelId;
20768
+ }
20769
+ return mapping["medium"] ?? mapping["high"];
20770
+ }
20771
+ function loadStoredAuthFromJson() {
20772
+ const authPath = join6(homedir4(), ".local", "share", "opencode", "auth.json");
20773
+ try {
20774
+ if (existsSync6(authPath)) {
20775
+ const content = readFileSync5(authPath, "utf8");
20776
+ const data = JSON.parse(content);
20777
+ return data?.[AGY_PROVIDER_ID];
20778
+ }
20779
+ } catch {
20780
+ }
20781
+ return void 0;
20782
+ }
20783
+ function createV2FetchInterceptor(getAuthSnapshot) {
20784
+ return async function agyV2Fetch(input, init) {
20785
+ const isGL = isGenerativeLanguageRequest(input);
20786
+ const isInternal = toUrlString2(input).includes("cloudcode-pa.googleapis.com");
20787
+ if (!isGL && !isInternal) {
20788
+ return agyFetch(input, init);
20789
+ }
20790
+ let rawAuth = getAuthSnapshot ? await getAuthSnapshot() : void 0;
20791
+ if (!rawAuth) {
20792
+ rawAuth = loadStoredAuthFromJson();
20793
+ }
20794
+ if (!rawAuth || !isOAuthAuth(rawAuth)) {
20795
+ return agyFetch(input, init);
20796
+ }
20797
+ let authRecord = resolveCachedAuth(rawAuth);
20798
+ if (accessTokenExpired(authRecord)) {
20799
+ const refreshed = await refreshAccessToken(authRecord, { auth: { set: async () => {
20800
+ } } });
20801
+ if (refreshed) {
20802
+ authRecord = refreshed;
20803
+ }
20804
+ }
20805
+ if (!authRecord.access) {
20806
+ return agyFetch(input, init);
20807
+ }
20808
+ if (isInternal) {
20809
+ const hasAuth = getSafeHeader2(init?.headers, "Authorization") !== void 0;
20810
+ if (hasAuth) {
20811
+ return agyFetch(input, init);
20812
+ }
20813
+ const userAgent = buildAgyCliUserAgent();
20814
+ const headers = setSafeHeaders2(init?.headers, {
20815
+ Authorization: `Bearer ${authRecord.access}`,
20816
+ "User-Agent": userAgent
20817
+ });
20818
+ return agyFetch(input, {
20819
+ ...init,
20820
+ headers
20821
+ });
20822
+ }
20823
+ const requestTarget = parseGenerativeLanguageRequest(input);
20824
+ const requestUserAgentModel = requestTarget?.effectiveModel;
20825
+ let projectContext;
20826
+ try {
20827
+ projectContext = await ensureProjectContext(
20828
+ authRecord,
20829
+ { auth: { set: async () => {
20830
+ } } },
20831
+ void 0,
20832
+ requestUserAgentModel
20833
+ );
20834
+ } catch {
20835
+ projectContext = {
20836
+ effectiveProjectId: parseRefreshParts(authRecord.refresh).projectId || "antigravity",
20837
+ auth: authRecord
20838
+ };
20839
+ }
20840
+ const originalRequestedModel = parseGenerativeLanguageRequest(input)?.effectiveModel;
20841
+ let modifiedInput = input;
20842
+ if (isGL && originalRequestedModel) {
20843
+ const originalBase = originalRequestedModel.replace("google-agy/", "");
20844
+ const resolvedBase = resolveModelTier2(originalBase, init);
20845
+ if (originalBase !== resolvedBase) {
20846
+ if (typeof modifiedInput === "string") {
20847
+ modifiedInput = modifiedInput.replace(`models/${originalBase}`, `models/${resolvedBase}`);
20848
+ } else if (typeof Request !== "undefined" && modifiedInput instanceof Request) {
20849
+ const newUrl = modifiedInput.url.replace(`models/${originalBase}`, `models/${resolvedBase}`);
20850
+ modifiedInput = new Request(newUrl, modifiedInput);
20851
+ }
20852
+ }
20853
+ }
20854
+ const transformed = prepareAgyRequest(
20855
+ modifiedInput,
20856
+ init,
20857
+ authRecord.access,
20858
+ projectContext.effectiveProjectId,
20859
+ void 0
20860
+ );
20861
+ const chatLogger = createChatLogger();
20862
+ if (chatLogger) {
20863
+ chatLogger.logRequest(
20864
+ toUrlString2(transformed.request),
20865
+ transformed.init.method || "GET",
20866
+ transformed.init.headers,
20867
+ transformed.init.body
20868
+ );
20869
+ }
20870
+ const response = await fetchWithRetry(transformed.request, transformed.init);
20871
+ return transformAgyResponse(
20872
+ response,
20873
+ transformed.streaming,
20874
+ null,
20875
+ transformed.requestedModel,
20876
+ transformed.sessionId,
20877
+ chatLogger
20878
+ );
20879
+ };
20880
+ }
20679
20881
  async function setupOpenCodeV2(ctx) {
20680
20882
  ctx.catalog.transform(async (editor) => {
20681
20883
  if (!editor) return;
@@ -20695,6 +20897,8 @@ async function setupOpenCodeV2(ctx) {
20695
20897
  p.activation = "enabled";
20696
20898
  p.package = "aisdk:@ai-sdk/google";
20697
20899
  p.description = "Google Gemini Antigravity Code Assist OAuth provider";
20900
+ p.settings = { ...p.settings || {}, apiKey: "dummy" };
20901
+ p.integrationID = AGY_PROVIDER_ID;
20698
20902
  });
20699
20903
  if (typeof editor?.model?.update === "function") {
20700
20904
  for (const [modelId, simple] of Object.entries(STATIC_MODELS_SIMPLE)) {
@@ -20728,9 +20932,20 @@ async function setupOpenCodeV2(ctx) {
20728
20932
  id: AGY_PROVIDER_ID,
20729
20933
  name: "Antigravity CLI",
20730
20934
  npm: "aisdk:@ai-sdk/google",
20935
+ settings: { apiKey: "dummy" },
20936
+ integrationID: AGY_PROVIDER_ID,
20731
20937
  models: { ...catalog.providers[AGY_PROVIDER_ID]?.models || {} },
20732
20938
  ...catalog.providers[AGY_PROVIDER_ID]
20733
20939
  };
20940
+ if (catalog.providers[AGY_PROVIDER_ID].settings) {
20941
+ catalog.providers[AGY_PROVIDER_ID].settings = {
20942
+ ...catalog.providers[AGY_PROVIDER_ID].settings,
20943
+ apiKey: "dummy"
20944
+ };
20945
+ } else {
20946
+ catalog.providers[AGY_PROVIDER_ID].settings = { apiKey: "dummy" };
20947
+ }
20948
+ catalog.providers[AGY_PROVIDER_ID].integrationID = AGY_PROVIDER_ID;
20734
20949
  const targetModels = catalog.providers[AGY_PROVIDER_ID].models;
20735
20950
  for (const [modelId, simple] of Object.entries(STATIC_MODELS_SIMPLE)) {
20736
20951
  const isClaude = modelId.startsWith("claude-");
@@ -20827,61 +21042,212 @@ async function setupOpenCodeV2(ctx) {
20827
21042
  editor[AGY_V2_QUOTA_SUMMARY_COMMAND] = commands[AGY_V2_QUOTA_SUMMARY_COMMAND];
20828
21043
  }
20829
21044
  });
20830
- ctx.session.hook("http.request", async (event) => {
20831
- if (!event) return;
20832
- const url2 = typeof event.url === "string" ? event.url : event.request?.url || "";
20833
- const isGL = isGenerativeLanguageRequest(url2);
20834
- const isInternal = url2.includes("cloudcode-pa.googleapis.com");
20835
- if (!isGL && !isInternal) {
20836
- return;
20837
- }
20838
- event.headers = event.headers || {};
20839
- let modelName;
20840
- if (isGL) {
20841
- const parsed = parseGenerativeLanguageRequest(url2);
20842
- modelName = parsed?.effectiveModel;
20843
- }
20844
- const userAgent = buildAgyCliUserAgent(modelName);
20845
- if (typeof event.headers.set === "function") {
20846
- if (!event.headers.get("User-Agent")) {
20847
- event.headers.set("User-Agent", userAgent);
20848
- }
20849
- } else {
20850
- if (!event.headers["User-Agent"] && !event.headers["user-agent"]) {
20851
- event.headers["User-Agent"] = userAgent;
21045
+ ctx.integration?.transform?.((editor) => {
21046
+ if (!editor) return;
21047
+ editor.update?.(AGY_PROVIDER_ID, (integration) => {
21048
+ integration.name = "Antigravity CLI (plugin)";
21049
+ });
21050
+ editor.method?.update?.({
21051
+ integrationID: AGY_PROVIDER_ID,
21052
+ method: {
21053
+ id: "oauth",
21054
+ type: "oauth",
21055
+ label: "Antigravity CLI (OAuth)"
21056
+ },
21057
+ authorize: async () => {
21058
+ const authUrl = new URL(AGY_AUTH_URL);
21059
+ authUrl.searchParams.set("client_id", AGY_CLIENT_ID);
21060
+ authUrl.searchParams.set("response_type", "code");
21061
+ authUrl.searchParams.set("redirect_uri", "http://localhost");
21062
+ authUrl.searchParams.set("scope", AGY_SCOPES.join(" "));
21063
+ authUrl.searchParams.set("access_type", "offline");
21064
+ authUrl.searchParams.set("prompt", "consent");
21065
+ return {
21066
+ mode: "code",
21067
+ url: authUrl.toString(),
21068
+ instructions: "Please complete Google account authorization in your browser. After authorization, copy the full redirect URL or code and paste it below:",
21069
+ callback: async (code) => {
21070
+ const trimmed = code.trim();
21071
+ const parsed = parseOAuthCallbackInput(trimmed);
21072
+ const authCode = parsed.code || trimmed;
21073
+ if (!authCode) {
21074
+ return { type: "failed", error: "Missing authorization code in callback input" };
21075
+ }
21076
+ const tokenResponse = await agyFetch(AGY_TOKEN_URL, {
21077
+ method: "POST",
21078
+ headers: {
21079
+ "Content-Type": "application/x-www-form-urlencoded"
21080
+ },
21081
+ body: new URLSearchParams({
21082
+ client_id: AGY_CLIENT_ID,
21083
+ client_secret: AGY_CLIENT_SECRET,
21084
+ code: authCode,
21085
+ grant_type: "authorization_code",
21086
+ redirect_uri: "http://localhost"
21087
+ })
21088
+ });
21089
+ if (!tokenResponse.ok) {
21090
+ const errorText = await tokenResponse.text();
21091
+ return { type: "failed", error: errorText };
21092
+ }
21093
+ const tokenPayload = await tokenResponse.json();
21094
+ if (!tokenPayload.refresh_token) {
21095
+ return { type: "failed", error: "Missing refresh token in response" };
21096
+ }
21097
+ const initialRefresh = formatRefreshParts({
21098
+ refreshToken: tokenPayload.refresh_token
21099
+ });
21100
+ let authRecord = {
21101
+ type: "oauth",
21102
+ refresh: initialRefresh,
21103
+ access: tokenPayload.access_token,
21104
+ expires: Date.now() + tokenPayload.expires_in * 1e3
21105
+ };
21106
+ try {
21107
+ const projectContext = await ensureProjectContext(
21108
+ authRecord,
21109
+ { auth: { set: async () => {
21110
+ } } }
21111
+ );
21112
+ authRecord = {
21113
+ ...authRecord,
21114
+ refresh: projectContext.auth.refresh
21115
+ };
21116
+ } catch {
21117
+ }
21118
+ return {
21119
+ type: "success",
21120
+ refresh: authRecord.refresh,
21121
+ access: authRecord.access,
21122
+ expires: authRecord.expires
21123
+ };
21124
+ }
21125
+ };
20852
21126
  }
20853
- }
21127
+ });
20854
21128
  });
20855
- ctx.session.hook("http.response", async (event) => {
21129
+ ctx.aisdk?.hook("sdk", async (event) => {
20856
21130
  if (!event) return;
20857
- if (event.response?.status === 429) {
20858
- return;
20859
- }
21131
+ const isMatchingProvider = event.providerID === AGY_PROVIDER_ID || event.model?.providerID === AGY_PROVIDER_ID;
21132
+ if (!isMatchingProvider) return;
21133
+ event.options = event.options || {};
21134
+ event.options.apiKey = "dummy";
21135
+ event.options.fetch = createV2FetchInterceptor();
20860
21136
  });
20861
- ctx.session.hook("retry", async (event) => {
20862
- if (!event) return;
20863
- const response = event.response;
20864
- if (response instanceof Response) {
20865
- if (response.status === 429 || response.status === 503) {
20866
- const quota = await classifyQuotaResponse(response);
20867
- if (quota?.retryDelayMs) {
20868
- event.retryDelayMs = quota.retryDelayMs;
21137
+ ctx.session.hook(
21138
+ "http.request",
21139
+ async (event) => {
21140
+ if (!event) return;
21141
+ const rawUrl = typeof event.url === "string" ? event.url : event.request?.url || "";
21142
+ const isGL = isGenerativeLanguageRequest(rawUrl);
21143
+ const isInternal = rawUrl.includes("cloudcode-pa.googleapis.com");
21144
+ if (!isGL && !isInternal) {
21145
+ return;
21146
+ }
21147
+ event.headers = event.headers || {};
21148
+ let modelName;
21149
+ if (isGL) {
21150
+ const parsed = parseGenerativeLanguageRequest(rawUrl);
21151
+ modelName = parsed?.effectiveModel;
21152
+ if (typeof event.headers.delete === "function") {
21153
+ event.headers.delete("x-goog-api-key");
21154
+ event.headers.delete("api-key");
20869
21155
  } else {
20870
- event.retryDelayMs = await resolveRetryDelayMs(response, event.attempt || 1);
21156
+ for (const key of Object.keys(event.headers)) {
21157
+ const lower = key.toLowerCase();
21158
+ if (lower === "x-goog-api-key" || lower === "api-key") {
21159
+ delete event.headers[key];
21160
+ }
21161
+ }
21162
+ }
21163
+ try {
21164
+ const parsedUrl = new URL(rawUrl);
21165
+ let urlChanged = false;
21166
+ for (const param of ["key", "x-goog-api-key", "api-key"]) {
21167
+ if (parsedUrl.searchParams.has(param)) {
21168
+ parsedUrl.searchParams.delete(param);
21169
+ urlChanged = true;
21170
+ }
21171
+ }
21172
+ if (urlChanged) {
21173
+ const cleanedUrl = parsedUrl.toString();
21174
+ if (typeof event.url === "string") {
21175
+ event.url = cleanedUrl;
21176
+ }
21177
+ if (event.request && typeof event.request === "object") {
21178
+ if (typeof event.request.url === "string") {
21179
+ event.request.url = cleanedUrl;
21180
+ }
21181
+ }
21182
+ }
21183
+ } catch {
21184
+ }
21185
+ const hasAuth = typeof event.headers.get === "function" ? !!event.headers.get("Authorization") : Object.keys(event.headers).some((k) => k.toLowerCase() === "authorization");
21186
+ if (!hasAuth) {
21187
+ let token;
21188
+ if (typeof event.auth?.access === "string" && event.auth.access) {
21189
+ token = event.auth.access;
21190
+ } else if (typeof event.auth?.token === "string" && event.auth.token) {
21191
+ token = event.auth.token;
21192
+ }
21193
+ if (typeof event.headers.set === "function") {
21194
+ event.headers.set("Authorization", `Bearer ${token || "dummy"}`);
21195
+ } else {
21196
+ event.headers["Authorization"] = `Bearer ${token || "dummy"}`;
21197
+ }
20871
21198
  }
20872
21199
  }
20873
- } else if (event.details && Array.isArray(event.details)) {
20874
- const retryInfo = event.details.find(
20875
- (d) => d && d["@type"] === "type.googleapis.com/google.rpc.RetryInfo"
20876
- );
20877
- if (retryInfo?.retryDelay) {
20878
- const delayMs = retryInternals.parseRetryDelayValue(retryInfo.retryDelay);
20879
- if (delayMs !== null) {
20880
- event.retryDelayMs = delayMs;
21200
+ const userAgent = buildAgyCliUserAgent(modelName);
21201
+ if (typeof event.headers.set === "function") {
21202
+ if (!event.headers.get("User-Agent")) {
21203
+ event.headers.set("User-Agent", userAgent);
21204
+ }
21205
+ } else {
21206
+ if (!event.headers["User-Agent"] && !event.headers["user-agent"]) {
21207
+ event.headers["User-Agent"] = userAgent;
20881
21208
  }
20882
21209
  }
20883
- }
20884
- });
21210
+ },
21211
+ { providerID: AGY_PROVIDER_ID }
21212
+ );
21213
+ ctx.session.hook(
21214
+ "http.response",
21215
+ async (event) => {
21216
+ if (!event) return;
21217
+ if (event.response?.status === 429) {
21218
+ return;
21219
+ }
21220
+ },
21221
+ { providerID: AGY_PROVIDER_ID }
21222
+ );
21223
+ ctx.session.hook(
21224
+ "retry",
21225
+ async (event) => {
21226
+ if (!event) return;
21227
+ const response = event.response;
21228
+ if (response instanceof Response) {
21229
+ if (response.status === 429 || response.status === 503) {
21230
+ const quota = await classifyQuotaResponse(response);
21231
+ if (quota?.retryDelayMs) {
21232
+ event.retryDelayMs = quota.retryDelayMs;
21233
+ } else {
21234
+ event.retryDelayMs = await resolveRetryDelayMs(response, event.attempt || 1);
21235
+ }
21236
+ }
21237
+ } else if (event.details && Array.isArray(event.details)) {
21238
+ const retryInfo = event.details.find(
21239
+ (d) => d && d["@type"] === "type.googleapis.com/google.rpc.RetryInfo"
21240
+ );
21241
+ if (retryInfo?.retryDelay) {
21242
+ const delayMs = retryInternals.parseRetryDelayValue(retryInfo.retryDelay);
21243
+ if (delayMs !== null) {
21244
+ event.retryDelayMs = delayMs;
21245
+ }
21246
+ }
21247
+ }
21248
+ },
21249
+ { providerID: AGY_PROVIDER_ID }
21250
+ );
20885
21251
  }
20886
21252
  var v2PluginDefinition = defineOpenCodeV2Plugin({
20887
21253
  id: AGY_PROVIDER_ID,