@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
|
-
|
|
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
|
-
|
|
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: "
|
|
20858
|
-
url:
|
|
20859
|
-
instructions: "
|
|
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
|
|
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
|
-
|
|
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
|
}
|