axauth 1.0.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/LICENSE +21 -0
- package/README.md +147 -0
- package/bin/axauth +17 -0
- package/dist/auth/adapter.d.ts +121 -0
- package/dist/auth/adapter.js +7 -0
- package/dist/auth/agents/claude-code-storage.d.ts +18 -0
- package/dist/auth/agents/claude-code-storage.js +66 -0
- package/dist/auth/agents/claude-code.d.ts +9 -0
- package/dist/auth/agents/claude-code.js +162 -0
- package/dist/auth/agents/codex-auth-check.d.ts +9 -0
- package/dist/auth/agents/codex-auth-check.js +101 -0
- package/dist/auth/agents/codex-config.d.ts +12 -0
- package/dist/auth/agents/codex-config.js +40 -0
- package/dist/auth/agents/codex-storage.d.ts +43 -0
- package/dist/auth/agents/codex-storage.js +82 -0
- package/dist/auth/agents/codex.d.ts +9 -0
- package/dist/auth/agents/codex.js +126 -0
- package/dist/auth/agents/copilot-auth-check.d.ts +17 -0
- package/dist/auth/agents/copilot-auth-check.js +46 -0
- package/dist/auth/agents/copilot-storage.d.ts +32 -0
- package/dist/auth/agents/copilot-storage.js +139 -0
- package/dist/auth/agents/copilot.d.ts +10 -0
- package/dist/auth/agents/copilot.js +144 -0
- package/dist/auth/agents/gemini-auth-check.d.ts +16 -0
- package/dist/auth/agents/gemini-auth-check.js +73 -0
- package/dist/auth/agents/gemini-storage.d.ts +24 -0
- package/dist/auth/agents/gemini-storage.js +69 -0
- package/dist/auth/agents/gemini.d.ts +9 -0
- package/dist/auth/agents/gemini.js +157 -0
- package/dist/auth/agents/opencode.d.ts +9 -0
- package/dist/auth/agents/opencode.js +128 -0
- package/dist/auth/file-storage.d.ts +14 -0
- package/dist/auth/file-storage.js +63 -0
- package/dist/auth/keychain.d.ts +12 -0
- package/dist/auth/keychain.js +56 -0
- package/dist/auth/registry.d.ts +109 -0
- package/dist/auth/registry.js +145 -0
- package/dist/auth/resolve-config-directory.d.ts +14 -0
- package/dist/auth/resolve-config-directory.js +16 -0
- package/dist/auth/types.d.ts +19 -0
- package/dist/auth/types.js +4 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +74 -0
- package/dist/commands/auth.d.ts +34 -0
- package/dist/commands/auth.js +155 -0
- package/dist/crypto.d.ts +39 -0
- package/dist/crypto.js +78 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +39 -0
- package/package.json +96 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copilot CLI auth adapter.
|
|
3
|
+
*
|
|
4
|
+
* Supports OAuth via keychain (macOS) or file, and GitHub token via env var.
|
|
5
|
+
* Also supports GitHub CLI (`gh auth login`) as a fallback.
|
|
6
|
+
*/
|
|
7
|
+
import { ensureDirectory } from "../file-storage.js";
|
|
8
|
+
import { isMacOS } from "../keychain.js";
|
|
9
|
+
import { findFirstAvailableToken } from "./copilot-auth-check.js";
|
|
10
|
+
import { deleteFileToken, deleteKeychainToken, deleteTokenFromPath, getConfigDirectory, getConfigFilePath, saveFileToken, saveKeychainToken, saveTokenToPath, } from "./copilot-storage.js";
|
|
11
|
+
const AGENT_ID = "copilot";
|
|
12
|
+
/** Copilot CLI authentication adapter */
|
|
13
|
+
const copilotAdapter = {
|
|
14
|
+
agentId: AGENT_ID,
|
|
15
|
+
capabilities: {
|
|
16
|
+
keychain: true,
|
|
17
|
+
file: true,
|
|
18
|
+
environment: true,
|
|
19
|
+
installApiKey: false,
|
|
20
|
+
},
|
|
21
|
+
checkAuth() {
|
|
22
|
+
const result = findFirstAvailableToken();
|
|
23
|
+
if (!result) {
|
|
24
|
+
return { agentId: AGENT_ID, authenticated: false };
|
|
25
|
+
}
|
|
26
|
+
const methodMap = {
|
|
27
|
+
environment: `Token (${result.environmentVariable})`,
|
|
28
|
+
keychain: "Token (keychain)",
|
|
29
|
+
file: "Token (file)",
|
|
30
|
+
"gh-cli": "GitHub CLI",
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
agentId: AGENT_ID,
|
|
34
|
+
authenticated: true,
|
|
35
|
+
method: methodMap[result.source],
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
extractRawCredentials() {
|
|
39
|
+
const result = findFirstAvailableToken();
|
|
40
|
+
if (!result)
|
|
41
|
+
return undefined;
|
|
42
|
+
return {
|
|
43
|
+
agent: AGENT_ID,
|
|
44
|
+
type: "oauth",
|
|
45
|
+
data: { accessToken: result.token, _source: result.source },
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
installCredentials(creds, options) {
|
|
49
|
+
if (creds.type === "api-key") {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
message: "API key credentials cannot be installed (use COPILOT_GITHUB_TOKEN env var)",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const token = typeof creds.data.accessToken === "string"
|
|
56
|
+
? creds.data.accessToken
|
|
57
|
+
: undefined;
|
|
58
|
+
if (!token) {
|
|
59
|
+
return { ok: false, message: "No access token found in credentials" };
|
|
60
|
+
}
|
|
61
|
+
// Custom path forces file storage
|
|
62
|
+
if (options?.path) {
|
|
63
|
+
if (saveTokenToPath(token, options.path)) {
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
message: `Installed credentials to ${options.path}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
message: `Failed to install credentials to ${options.path}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Default location: use storage option or _source marker
|
|
75
|
+
const sourceMarker = creds.data._source;
|
|
76
|
+
const targetStorage = options?.storage ?? (sourceMarker === "keychain" ? "keychain" : "file");
|
|
77
|
+
if (targetStorage === "keychain") {
|
|
78
|
+
if (!isMacOS()) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
message: "Keychain storage is only available on macOS",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (saveKeychainToken(token)) {
|
|
85
|
+
return { ok: true, message: "Installed credentials to macOS Keychain" };
|
|
86
|
+
}
|
|
87
|
+
return { ok: false, message: "Failed to save to macOS Keychain" };
|
|
88
|
+
}
|
|
89
|
+
// File storage
|
|
90
|
+
if (!ensureDirectory(getConfigDirectory())) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
message: "Failed to create copilot config directory",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (saveFileToken(token)) {
|
|
97
|
+
return {
|
|
98
|
+
ok: true,
|
|
99
|
+
message: `Installed credentials to ${getConfigFilePath()}`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { ok: false, message: "Failed to install credentials to file" };
|
|
103
|
+
},
|
|
104
|
+
removeCredentials(options) {
|
|
105
|
+
// Custom path: only remove that specific file
|
|
106
|
+
if (options?.path) {
|
|
107
|
+
if (deleteTokenFromPath(options.path)) {
|
|
108
|
+
return { ok: true, message: `Removed ${options.path}` };
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
ok: true,
|
|
112
|
+
message: "No credentials file found at specified path",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// Default location: remove from both keychain and config file
|
|
116
|
+
const removedFrom = [];
|
|
117
|
+
if (deleteKeychainToken()) {
|
|
118
|
+
removedFrom.push("macOS Keychain");
|
|
119
|
+
}
|
|
120
|
+
if (deleteFileToken()) {
|
|
121
|
+
removedFrom.push(getConfigFilePath());
|
|
122
|
+
}
|
|
123
|
+
if (removedFrom.length === 0) {
|
|
124
|
+
return { ok: true, message: "No credentials found" };
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, message: `Removed from ${removedFrom.join(" and ")}` };
|
|
127
|
+
},
|
|
128
|
+
getAccessToken(creds) {
|
|
129
|
+
const data = creds.data;
|
|
130
|
+
if (typeof data.accessToken === "string") {
|
|
131
|
+
return data.accessToken;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
},
|
|
135
|
+
credentialsToEnvironment(creds) {
|
|
136
|
+
const environment = {};
|
|
137
|
+
const data = creds.data;
|
|
138
|
+
if (typeof data.accessToken === "string") {
|
|
139
|
+
environment.COPILOT_GITHUB_TOKEN = data.accessToken;
|
|
140
|
+
}
|
|
141
|
+
return environment;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
export { copilotAdapter };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI authentication checking.
|
|
3
|
+
*
|
|
4
|
+
* Determines auth status based on configured auth type and available credentials.
|
|
5
|
+
*/
|
|
6
|
+
/** Credential source for auth method display */
|
|
7
|
+
type CredentialSource = "keychain" | "file" | "api-key" | "vertex-ai" | "adc";
|
|
8
|
+
/** Result of finding available credentials */
|
|
9
|
+
interface CredentialResult {
|
|
10
|
+
source: CredentialSource;
|
|
11
|
+
method: string;
|
|
12
|
+
data?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
/** Find credentials based on configured auth type */
|
|
15
|
+
declare function findCredentials(): CredentialResult | undefined;
|
|
16
|
+
export { findCredentials };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI authentication checking.
|
|
3
|
+
*
|
|
4
|
+
* Determines auth status based on configured auth type and available credentials.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { getAuthType, getDefaultOAuthPath, loadKeychainCreds, loadOAuthCreds, } from "./gemini-storage.js";
|
|
8
|
+
/** Check OAuth credentials (keychain first, then file) */
|
|
9
|
+
function checkOAuthCredentials() {
|
|
10
|
+
const keychainCreds = loadKeychainCreds();
|
|
11
|
+
if (keychainCreds) {
|
|
12
|
+
return {
|
|
13
|
+
source: "keychain",
|
|
14
|
+
method: "Google OAuth (keychain)",
|
|
15
|
+
data: keychainCreds,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (existsSync(getDefaultOAuthPath())) {
|
|
19
|
+
const fileCreds = loadOAuthCreds();
|
|
20
|
+
if (fileCreds) {
|
|
21
|
+
return {
|
|
22
|
+
source: "file",
|
|
23
|
+
method: "Google OAuth (file)",
|
|
24
|
+
data: fileCreds,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/** Check API key credentials */
|
|
31
|
+
function checkApiKeyCredentials() {
|
|
32
|
+
if (process.env.GEMINI_API_KEY) {
|
|
33
|
+
return {
|
|
34
|
+
source: "api-key",
|
|
35
|
+
method: "API key",
|
|
36
|
+
data: { apiKey: process.env.GEMINI_API_KEY },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Check Vertex AI credentials */
|
|
42
|
+
function checkVertexAiCredentials() {
|
|
43
|
+
const hasVertexEnvironment = (process.env.GOOGLE_CLOUD_PROJECT && process.env.GOOGLE_CLOUD_LOCATION) ||
|
|
44
|
+
process.env.GOOGLE_API_KEY;
|
|
45
|
+
if (hasVertexEnvironment) {
|
|
46
|
+
return {
|
|
47
|
+
source: "vertex-ai",
|
|
48
|
+
method: "Vertex AI",
|
|
49
|
+
data: process.env.GOOGLE_API_KEY
|
|
50
|
+
? { apiKey: process.env.GOOGLE_API_KEY }
|
|
51
|
+
: undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
/** Find credentials based on configured auth type */
|
|
57
|
+
function findCredentials() {
|
|
58
|
+
const authType = getAuthType();
|
|
59
|
+
if (authType === "oauth-personal") {
|
|
60
|
+
return checkOAuthCredentials();
|
|
61
|
+
}
|
|
62
|
+
if (authType === "gemini-api-key") {
|
|
63
|
+
return checkApiKeyCredentials();
|
|
64
|
+
}
|
|
65
|
+
if (authType === "vertex-ai") {
|
|
66
|
+
return checkVertexAiCredentials();
|
|
67
|
+
}
|
|
68
|
+
if (authType === "compute-default-credentials") {
|
|
69
|
+
return { source: "adc", method: "Compute ADC" };
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
export { findCredentials };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini credential storage operations.
|
|
3
|
+
*/
|
|
4
|
+
/** Get default OAuth credentials file path */
|
|
5
|
+
declare function getDefaultOAuthPath(): string;
|
|
6
|
+
/** Get current auth type from settings */
|
|
7
|
+
declare function getAuthType(): string | undefined;
|
|
8
|
+
/** Clear auth type from settings */
|
|
9
|
+
declare function clearAuthTypeFromSettings(): boolean;
|
|
10
|
+
/** Set auth type in settings */
|
|
11
|
+
declare function setAuthTypeInSettings(authType: string): boolean;
|
|
12
|
+
/** Load OAuth credentials from file */
|
|
13
|
+
declare function loadOAuthCreds(): Record<string, unknown> | undefined;
|
|
14
|
+
/** Load OAuth credentials from keychain */
|
|
15
|
+
declare function loadKeychainCreds(): Record<string, unknown> | undefined;
|
|
16
|
+
/** Save OAuth credentials to keychain */
|
|
17
|
+
declare function saveKeychainCreds(data: Record<string, unknown>): boolean;
|
|
18
|
+
/** Delete OAuth credentials from keychain */
|
|
19
|
+
declare function deleteKeychainCreds(): boolean;
|
|
20
|
+
/** Save OAuth credentials to file */
|
|
21
|
+
declare function saveOAuthCreds(data: Record<string, unknown>, filePath?: string): boolean;
|
|
22
|
+
/** Delete OAuth credentials file */
|
|
23
|
+
declare function deleteOAuthCreds(filePath?: string): boolean;
|
|
24
|
+
export { clearAuthTypeFromSettings, deleteKeychainCreds, deleteOAuthCreds, getAuthType, getDefaultOAuthPath, loadKeychainCreds, loadOAuthCreds, saveKeychainCreds, saveOAuthCreds, setAuthTypeInSettings, };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini credential storage operations.
|
|
3
|
+
*/
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { geminiConfigReader } from "axconfig";
|
|
6
|
+
import { deleteFile, loadJsonFile, saveJsonFile } from "../file-storage.js";
|
|
7
|
+
import { deleteFromKeychain, loadFromKeychain, saveToKeychain, } from "../keychain.js";
|
|
8
|
+
import { getResolvedConfigDirectory } from "../resolve-config-directory.js";
|
|
9
|
+
const KEYCHAIN_SERVICE = "gemini-cli-oauth";
|
|
10
|
+
const KEYCHAIN_ACCOUNT = "main-account";
|
|
11
|
+
/** Get the Gemini config directory */
|
|
12
|
+
function getConfigDirectory() {
|
|
13
|
+
return getResolvedConfigDirectory(geminiConfigReader);
|
|
14
|
+
}
|
|
15
|
+
/** Get default OAuth credentials file path */
|
|
16
|
+
function getDefaultOAuthPath() {
|
|
17
|
+
return path.join(getConfigDirectory(), "oauth_creds.json");
|
|
18
|
+
}
|
|
19
|
+
/** Get current auth type from settings */
|
|
20
|
+
function getAuthType() {
|
|
21
|
+
const result = geminiConfigReader.readRaw(getConfigDirectory(), "security.auth.selectedType");
|
|
22
|
+
if (result.ok && typeof result.value === "string") {
|
|
23
|
+
return result.value;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
/** Clear auth type from settings */
|
|
28
|
+
function clearAuthTypeFromSettings() {
|
|
29
|
+
const result = geminiConfigReader.deleteRaw(getConfigDirectory(), "security.auth.selectedType");
|
|
30
|
+
return result.ok && result.deleted;
|
|
31
|
+
}
|
|
32
|
+
/** Set auth type in settings */
|
|
33
|
+
function setAuthTypeInSettings(authType) {
|
|
34
|
+
const result = geminiConfigReader.writeRaw(getConfigDirectory(), "security.auth.selectedType", authType);
|
|
35
|
+
return result.ok;
|
|
36
|
+
}
|
|
37
|
+
/** Load OAuth credentials from file */
|
|
38
|
+
function loadOAuthCreds() {
|
|
39
|
+
return loadJsonFile(getDefaultOAuthPath());
|
|
40
|
+
}
|
|
41
|
+
/** Load OAuth credentials from keychain */
|
|
42
|
+
function loadKeychainCreds() {
|
|
43
|
+
const raw = loadFromKeychain(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
44
|
+
if (!raw)
|
|
45
|
+
return undefined;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(raw);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Save OAuth credentials to keychain */
|
|
54
|
+
function saveKeychainCreds(data) {
|
|
55
|
+
return saveToKeychain(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, JSON.stringify(data));
|
|
56
|
+
}
|
|
57
|
+
/** Delete OAuth credentials from keychain */
|
|
58
|
+
function deleteKeychainCreds() {
|
|
59
|
+
return deleteFromKeychain(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
60
|
+
}
|
|
61
|
+
/** Save OAuth credentials to file */
|
|
62
|
+
function saveOAuthCreds(data, filePath) {
|
|
63
|
+
return saveJsonFile(filePath ?? getDefaultOAuthPath(), data, { mode: 0o600 });
|
|
64
|
+
}
|
|
65
|
+
/** Delete OAuth credentials file */
|
|
66
|
+
function deleteOAuthCreds(filePath) {
|
|
67
|
+
return deleteFile(filePath ?? getDefaultOAuthPath());
|
|
68
|
+
}
|
|
69
|
+
export { clearAuthTypeFromSettings, deleteKeychainCreds, deleteOAuthCreds, getAuthType, getDefaultOAuthPath, loadKeychainCreds, loadOAuthCreds, saveKeychainCreds, saveOAuthCreds, setAuthTypeInSettings, };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini auth adapter.
|
|
3
|
+
*
|
|
4
|
+
* Supports OAuth via keychain (macOS) or file. API key auth is env-var only.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthAdapter } from "../adapter.js";
|
|
7
|
+
/** Gemini authentication adapter */
|
|
8
|
+
declare const geminiAdapter: AuthAdapter;
|
|
9
|
+
export { geminiAdapter };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini auth adapter.
|
|
3
|
+
*
|
|
4
|
+
* Supports OAuth via keychain (macOS) or file. API key auth is env-var only.
|
|
5
|
+
*/
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { ensureDirectory } from "../file-storage.js";
|
|
8
|
+
import { isMacOS } from "../keychain.js";
|
|
9
|
+
import { findCredentials } from "./gemini-auth-check.js";
|
|
10
|
+
import { clearAuthTypeFromSettings, deleteKeychainCreds, deleteOAuthCreds, getDefaultOAuthPath, saveKeychainCreds, saveOAuthCreds, setAuthTypeInSettings, } from "./gemini-storage.js";
|
|
11
|
+
const AGENT_ID = "gemini";
|
|
12
|
+
/** Gemini authentication adapter */
|
|
13
|
+
const geminiAdapter = {
|
|
14
|
+
agentId: AGENT_ID,
|
|
15
|
+
capabilities: {
|
|
16
|
+
keychain: true,
|
|
17
|
+
file: true,
|
|
18
|
+
environment: true,
|
|
19
|
+
installApiKey: false,
|
|
20
|
+
},
|
|
21
|
+
checkAuth() {
|
|
22
|
+
const result = findCredentials();
|
|
23
|
+
if (!result) {
|
|
24
|
+
return { agentId: AGENT_ID, authenticated: false };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
agentId: AGENT_ID,
|
|
28
|
+
authenticated: true,
|
|
29
|
+
method: result.method,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
extractRawCredentials() {
|
|
33
|
+
const result = findCredentials();
|
|
34
|
+
if (!result || !result.data)
|
|
35
|
+
return undefined;
|
|
36
|
+
const credType = result.source === "api-key" || result.source === "vertex-ai"
|
|
37
|
+
? "api-key"
|
|
38
|
+
: "oauth";
|
|
39
|
+
return {
|
|
40
|
+
agent: AGENT_ID,
|
|
41
|
+
type: credType,
|
|
42
|
+
data: {
|
|
43
|
+
...result.data,
|
|
44
|
+
_source: result.source === "keychain" ? "keychain" : "file",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
installCredentials(creds, options) {
|
|
49
|
+
if (creds.type === "api-key") {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
message: "API key credentials cannot be installed (use GEMINI_API_KEY env var)",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const { _source, ...oauthData } = creds.data;
|
|
56
|
+
// Custom path forces file storage (keychain only for default location)
|
|
57
|
+
if (options?.path) {
|
|
58
|
+
const targetDirectory = path.dirname(options.path);
|
|
59
|
+
if (!ensureDirectory(targetDirectory)) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
message: `Failed to create directory ${targetDirectory}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (saveOAuthCreds(oauthData, options.path)) {
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
message: `Installed credentials to ${options.path}`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
message: `Failed to install credentials to ${options.path}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Default location: use storage option or _source marker
|
|
77
|
+
const targetStorage = options?.storage ?? (_source === "keychain" ? "keychain" : "file");
|
|
78
|
+
if (targetStorage === "keychain") {
|
|
79
|
+
if (!isMacOS()) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
message: "Keychain storage is only available on macOS",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (saveKeychainCreds(oauthData)) {
|
|
86
|
+
if (!setAuthTypeInSettings("oauth-personal")) {
|
|
87
|
+
return { ok: false, message: "Failed to update settings" };
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, message: "Installed credentials to macOS Keychain" };
|
|
90
|
+
}
|
|
91
|
+
return { ok: false, message: "Failed to save to macOS Keychain" };
|
|
92
|
+
}
|
|
93
|
+
// File storage
|
|
94
|
+
const targetPath = getDefaultOAuthPath();
|
|
95
|
+
const targetDirectory = path.dirname(targetPath);
|
|
96
|
+
if (!ensureDirectory(targetDirectory)) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
message: `Failed to create directory ${targetDirectory}`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (!saveOAuthCreds(oauthData, targetPath)) {
|
|
103
|
+
return { ok: false, message: "Failed to write OAuth credentials" };
|
|
104
|
+
}
|
|
105
|
+
if (!setAuthTypeInSettings("oauth-personal")) {
|
|
106
|
+
return { ok: false, message: "Failed to update settings" };
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, message: `Installed credentials to ${targetPath}` };
|
|
109
|
+
},
|
|
110
|
+
removeCredentials(options) {
|
|
111
|
+
// Custom path: only remove that specific file (no settings update)
|
|
112
|
+
if (options?.path) {
|
|
113
|
+
if (deleteOAuthCreds(options.path)) {
|
|
114
|
+
return { ok: true, message: `Removed ${options.path}` };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
message: "No credentials file found at specified path",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// Default location: remove from keychain, file, and clear settings
|
|
122
|
+
const removedFrom = [];
|
|
123
|
+
if (deleteKeychainCreds()) {
|
|
124
|
+
removedFrom.push("macOS Keychain");
|
|
125
|
+
}
|
|
126
|
+
if (deleteOAuthCreds()) {
|
|
127
|
+
removedFrom.push(getDefaultOAuthPath());
|
|
128
|
+
}
|
|
129
|
+
if (clearAuthTypeFromSettings()) {
|
|
130
|
+
removedFrom.push("auth type from settings.json");
|
|
131
|
+
}
|
|
132
|
+
if (removedFrom.length === 0) {
|
|
133
|
+
return { ok: true, message: "No credentials found" };
|
|
134
|
+
}
|
|
135
|
+
return { ok: true, message: `Removed from ${removedFrom.join(", ")}` };
|
|
136
|
+
},
|
|
137
|
+
getAccessToken(creds) {
|
|
138
|
+
const data = creds.data;
|
|
139
|
+
if (creds.type === "api-key" && typeof data.apiKey === "string") {
|
|
140
|
+
return data.apiKey;
|
|
141
|
+
}
|
|
142
|
+
if (creds.type === "oauth" && typeof data.access_token === "string") {
|
|
143
|
+
return data.access_token;
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
},
|
|
147
|
+
credentialsToEnvironment(creds) {
|
|
148
|
+
const environment = {};
|
|
149
|
+
const data = creds.data;
|
|
150
|
+
if (creds.type === "api-key" && typeof data.apiKey === "string") {
|
|
151
|
+
environment.GEMINI_API_KEY = data.apiKey;
|
|
152
|
+
}
|
|
153
|
+
// OAuth tokens for Gemini require oauth_creds.json file
|
|
154
|
+
return environment;
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
export { geminiAdapter };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode auth adapter.
|
|
3
|
+
*
|
|
4
|
+
* Supports multi-provider auth via file only. No keychain or env var support.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthAdapter } from "../adapter.js";
|
|
7
|
+
/** OpenCode authentication adapter */
|
|
8
|
+
declare const opencodeAdapter: AuthAdapter;
|
|
9
|
+
export { opencodeAdapter };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode auth adapter.
|
|
3
|
+
*
|
|
4
|
+
* Supports multi-provider auth via file only. No keychain or env var support.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
const AGENT_ID = "opencode";
|
|
10
|
+
function getDefaultAuthFilePath() {
|
|
11
|
+
const xdgData = process.env.XDG_DATA_HOME ?? path.join(homedir(), ".local/share");
|
|
12
|
+
return path.join(xdgData, "opencode", "auth.json");
|
|
13
|
+
}
|
|
14
|
+
/** OpenCode authentication adapter */
|
|
15
|
+
const opencodeAdapter = {
|
|
16
|
+
agentId: AGENT_ID,
|
|
17
|
+
capabilities: {
|
|
18
|
+
keychain: false,
|
|
19
|
+
file: true,
|
|
20
|
+
environment: false,
|
|
21
|
+
installApiKey: true,
|
|
22
|
+
},
|
|
23
|
+
checkAuth() {
|
|
24
|
+
const authFile = getDefaultAuthFilePath();
|
|
25
|
+
if (!existsSync(authFile)) {
|
|
26
|
+
return { agentId: AGENT_ID, authenticated: false };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const auth = JSON.parse(readFileSync(authFile, "utf8"));
|
|
30
|
+
const providers = Object.keys(auth);
|
|
31
|
+
const firstProvider = providers[0];
|
|
32
|
+
if (firstProvider !== undefined) {
|
|
33
|
+
const firstAuth = auth[firstProvider];
|
|
34
|
+
const method = firstAuth?.type === "oauth"
|
|
35
|
+
? "OAuth"
|
|
36
|
+
: firstAuth?.type === "api"
|
|
37
|
+
? "API key"
|
|
38
|
+
: "Well-known";
|
|
39
|
+
return {
|
|
40
|
+
agentId: AGENT_ID,
|
|
41
|
+
authenticated: true,
|
|
42
|
+
method: providers.length > 1
|
|
43
|
+
? `${method} (${providers.length} providers)`
|
|
44
|
+
: method,
|
|
45
|
+
details: { providers },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Invalid JSON
|
|
51
|
+
}
|
|
52
|
+
return { agentId: AGENT_ID, authenticated: false };
|
|
53
|
+
},
|
|
54
|
+
extractRawCredentials() {
|
|
55
|
+
const authFile = getDefaultAuthFilePath();
|
|
56
|
+
if (!existsSync(authFile)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const auth = JSON.parse(readFileSync(authFile, "utf8"));
|
|
61
|
+
const providers = Object.keys(auth);
|
|
62
|
+
if (providers.length > 0) {
|
|
63
|
+
return { agent: AGENT_ID, type: "oauth", data: auth };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Invalid JSON
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
},
|
|
71
|
+
installCredentials(creds, options) {
|
|
72
|
+
// OpenCode only supports file storage (no keychain)
|
|
73
|
+
const targetPath = options?.path ?? getDefaultAuthFilePath();
|
|
74
|
+
const targetDirectory = path.dirname(targetPath);
|
|
75
|
+
try {
|
|
76
|
+
// Ensure directory exists
|
|
77
|
+
if (!existsSync(targetDirectory)) {
|
|
78
|
+
mkdirSync(targetDirectory, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
// OpenCode stores auth data directly as the file content
|
|
81
|
+
const temporaryFile = `${targetPath}.tmp.${Date.now()}`;
|
|
82
|
+
writeFileSync(temporaryFile, JSON.stringify(creds.data, undefined, 2), {
|
|
83
|
+
mode: 0o600,
|
|
84
|
+
});
|
|
85
|
+
renameSync(temporaryFile, targetPath);
|
|
86
|
+
return { ok: true, message: `Installed credentials to ${targetPath}` };
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
message: `Failed to install credentials: ${message}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
removeCredentials(options) {
|
|
97
|
+
const targetPath = options?.path ?? getDefaultAuthFilePath();
|
|
98
|
+
if (!existsSync(targetPath)) {
|
|
99
|
+
return { ok: true, message: "No credentials file found" };
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
unlinkSync(targetPath);
|
|
103
|
+
return { ok: true, message: `Removed ${targetPath}` };
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
+
return { ok: false, message: `Failed to remove credentials: ${message}` };
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
getAccessToken(creds) {
|
|
111
|
+
const data = creds.data;
|
|
112
|
+
// OpenCode stores provider tokens; return first available
|
|
113
|
+
for (const provider of Object.values(data)) {
|
|
114
|
+
if (provider &&
|
|
115
|
+
typeof provider === "object" &&
|
|
116
|
+
"access_token" in provider &&
|
|
117
|
+
typeof provider.access_token === "string") {
|
|
118
|
+
return provider.access_token;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
},
|
|
123
|
+
credentialsToEnvironment() {
|
|
124
|
+
// OpenCode requires auth.json file, no env var support
|
|
125
|
+
return {};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
export { opencodeAdapter };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based credential storage utilities.
|
|
3
|
+
*/
|
|
4
|
+
/** Load JSON data from a file and cast to expected type */
|
|
5
|
+
declare function loadJsonFile<T extends object>(filePath: string): T | undefined;
|
|
6
|
+
/** Save JSON data to a file atomically */
|
|
7
|
+
declare function saveJsonFile(filePath: string, data: unknown, options?: {
|
|
8
|
+
mode?: number;
|
|
9
|
+
}): boolean;
|
|
10
|
+
/** Delete a file if it exists */
|
|
11
|
+
declare function deleteFile(filePath: string): boolean;
|
|
12
|
+
/** Ensure a directory exists */
|
|
13
|
+
declare function ensureDirectory(directoryPath: string): boolean;
|
|
14
|
+
export { deleteFile, ensureDirectory, loadJsonFile, saveJsonFile };
|