axauth 1.0.0 → 1.1.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
@@ -1,46 +1,35 @@
1
1
  # axauth
2
2
 
3
- Authentication management library and CLI for AI coding agents.
3
+ Unified authentication management for AI coding agents.
4
4
 
5
5
  ## Overview
6
6
 
7
- axauth manages credentials for AI coding agents. It can be used:
7
+ axauth provides a consistent interface for managing credentials across multiple AI coding agent CLIs. It abstracts away the differences between agent-specific credential storage mechanisms (macOS Keychain, file-based storage, environment variables) and provides:
8
8
 
9
- 1. **As a library** - imported to check auth status, extract tokens, and manage credentials programmatically
10
- 2. **As a CLI** - standalone tool for managing agent credentials
9
+ - **Auth status detection** - Check which agents are authenticated and via which method
10
+ - **Credential extraction** - Extract tokens for API calls or export/import workflows
11
+ - **Portable credential export** - Encrypted credential files for CI/CD and backup
12
+ - **Multi-storage support** - Keychain (macOS), file storage, and environment variables
11
13
 
12
14
  ## Supported Agents
13
15
 
14
- - **claude-code** - Claude Code (Anthropic)
15
- - **codex** - Codex CLI (OpenAI)
16
- - **gemini** - Gemini CLI (Google)
17
- - **opencode** - OpenCode
16
+ | Agent | CLI | Provider | Auth Methods |
17
+ | ----------- | ---------- | --------- | ------------------------------------------ |
18
+ | Claude Code | `claude` | Anthropic | OAuth (keychain/file), `ANTHROPIC_API_KEY` |
19
+ | Codex CLI | `codex` | OpenAI | ChatGPT OAuth, `OPENAI_API_KEY` |
20
+ | Gemini CLI | `gemini` | Google | OAuth, `GEMINI_API_KEY` |
21
+ | OpenCode | `opencode` | Sst | Multi-provider OAuth |
22
+ | Copilot CLI | `copilot` | GitHub | OAuth, `GH_TOKEN`/`GITHUB_TOKEN` |
18
23
 
19
- ## Library API
20
-
21
- ```typescript
22
- import { checkAuth, getAgentAccessToken, extractCredentials } from "axauth";
23
-
24
- // Check auth status for an agent
25
- const status = checkAuth("claude-code");
26
- if (status.authenticated) {
27
- console.log(`Authenticated via ${status.method}`);
28
- }
24
+ ## Installation
29
25
 
30
- // Get access token for API calls
31
- const token = getAgentAccessToken("claude-code");
32
- if (token) {
33
- // Use token for API calls
34
- }
35
-
36
- // Extract full credentials for export
37
- const creds = extractCredentials("claude-code");
38
- if (creds) {
39
- // creds.accessToken, creds.refreshToken, etc.
40
- }
26
+ ```bash
27
+ npm install axauth
28
+ # or
29
+ pnpm add axauth
41
30
  ```
42
31
 
43
- ## CLI Commands
32
+ ## CLI Usage
44
33
 
45
34
  ```bash
46
35
  # List agents and their auth status
@@ -48,29 +37,31 @@ axauth list
48
37
  axauth list --json
49
38
 
50
39
  # Get access token for an agent (outputs raw token for piping)
51
- axauth token --agent claude-code
40
+ axauth token --agent claude
52
41
 
53
42
  # Export credentials to encrypted file
54
- axauth export --agent claude-code --output creds.json
55
- axauth export --agent claude-code --output creds.json --no-password
56
-
57
- # Remove credentials (agent will prompt for login)
58
- axauth remove-credentials --agent claude-code
43
+ axauth export --agent claude --output creds.json
44
+ axauth export --agent claude --output creds.json --no-password
59
45
 
60
46
  # Install credentials from exported file
61
- axauth install-credentials --agent claude-code --input creds.json
47
+ axauth install-credentials --agent claude --input creds.json
48
+ axauth install-credentials --agent claude --input creds.json --config-dir /tmp/config
49
+
50
+ # Remove credentials (agent will prompt for login on next use)
51
+ axauth remove-credentials --agent claude
52
+ axauth remove-credentials --agent claude --config-dir /tmp/config
62
53
  ```
63
54
 
64
- ## Pipeline Examples
55
+ ### Pipeline Examples
65
56
 
66
57
  The CLI outputs TSV format for easy processing with standard Unix tools:
67
58
 
68
59
  ```bash
69
60
  # List all agents and their auth status
70
61
  axauth list
71
- # AGENT STATUS METHOD
72
- # claude-code authenticated OAuth (max)
73
- # codex authenticated ChatGPT OAuth
62
+ # AGENT STATUS METHOD
63
+ # claude authenticated OAuth (max)
64
+ # codex authenticated ChatGPT OAuth
74
65
  # ...
75
66
 
76
67
  # Filter to show only authenticated agents
@@ -79,68 +70,194 @@ axauth list | tail -n +2 | awk -F'\t' '$2 == "authenticated"'
79
70
  # Count agents by status
80
71
  axauth list | tail -n +2 | cut -f2 | sort | uniq -c
81
72
 
82
- # Extract agent names as a list
83
- axauth list | tail -n +2 | cut -f1
84
-
85
73
  # Check if a specific agent is authenticated
86
- axauth list --json | jq -e '.[] | select(.agentId == "claude-code") | .authenticated'
74
+ axauth list --json | jq -e '.[] | select(.agentId == "claude") | .authenticated'
87
75
 
88
- # Check Claude Code usage via OAuth endpoint
89
- curl -s -H "Authorization: Bearer $(axauth token --agent claude-code)" \
76
+ # Use token with curl
77
+ curl -s -H "Authorization: Bearer $(axauth token --agent claude)" \
90
78
  -H "anthropic-beta: oauth-2025-04-20" \
91
79
  https://api.anthropic.com/api/oauth/usage | jq .
92
80
  ```
93
81
 
94
- ## Module Structure
82
+ ## Library API
95
83
 
84
+ ```typescript
85
+ import {
86
+ // Core operations
87
+ checkAuth, // Check single agent auth status
88
+ checkAllAuth, // Check all agents' auth status
89
+ getAgentAccessToken, // Get access token for an agent
90
+ extractRawCredentials, // Extract full credentials for export
91
+ installCredentials, // Install credentials to storage
92
+ removeCredentials, // Remove credentials from storage
93
+
94
+ // Credential utilities
95
+ getAccessToken, // Extract token from credential object
96
+ credentialsToEnvironment, // Convert credentials to env vars
97
+ getCredentialsEnvironmentVariableName, // Get AX_*_CREDENTIALS var name
98
+ installCredentialsFromEnvironmentVariable, // Install from env var (CI/CD)
99
+
100
+ // Adapter access
101
+ getAdapter, // Get adapter for an agent
102
+ getAllAdapters, // Get all adapters
103
+ getCapabilities, // Check adapter capabilities
104
+ } from "axauth";
105
+
106
+ // Types
107
+ import type {
108
+ AgentCli, // "claude" | "codex" | "gemini" | "opencode" | "copilot"
109
+ AuthStatus, // { agentId, authenticated, method?, details? }
110
+ Credentials, // { agent, type, data }
111
+ AuthAdapter, // Adapter interface
112
+ AdapterCapabilities, // { keychain, file, environment, installApiKey }
113
+ } from "axauth";
96
114
  ```
97
- src/
98
- ├── types.ts # AgentId type definition
99
- ├── crypto.ts # AES-256-GCM encryption for credentials
100
- ├── cli.ts # CLI entry point
101
- ├── commands/
102
- │ └── auth.ts # CLI command handlers
103
- └── auth/
104
- ├── check-auth.ts # Auth status detection
105
- ├── extract-credentials.ts # Credential extraction
106
- ├── get-access-token.ts # Token retrieval
107
- ├── install-credentials.ts # Credential installation
108
- ├── remove-credentials.ts # Credential removal
109
- ├── keychain.ts # macOS Keychain utilities
110
- ├── file-storage.ts # File I/O utilities
111
- ├── types.ts # Auth types
112
- └── agents/ # Agent-specific implementations
113
- ├── claude-code.ts
114
- ├── codex.ts
115
- ├── gemini.ts
116
- └── opencode.ts
115
+
116
+ ### Examples
117
+
118
+ ```typescript
119
+ import { checkAuth, getAgentAccessToken, getCapabilities } from "axauth";
120
+
121
+ // Check auth status for an agent
122
+ const status = checkAuth("claude");
123
+ if (status.authenticated) {
124
+ console.log(`Authenticated via ${status.method}`);
125
+ }
126
+
127
+ // Get access token for API calls
128
+ const token = getAgentAccessToken("claude");
129
+ if (token) {
130
+ // Use token for API calls
131
+ }
132
+
133
+ // Check adapter capabilities
134
+ const caps = getCapabilities("gemini");
135
+ if (!caps.keychain) {
136
+ console.log("Gemini requires file storage on this platform");
137
+ }
138
+ ```
139
+
140
+ ### Adapter Pattern
141
+
142
+ Each agent implements the `AuthAdapter` interface, hiding storage complexity:
143
+
144
+ ```typescript
145
+ import { getAdapter } from "axauth";
146
+
147
+ const adapter = getAdapter("claude");
148
+
149
+ // All adapters have the same interface
150
+ const status = adapter.checkAuth();
151
+ const creds = adapter.extractRawCredentials();
152
+ const token = adapter.getAccessToken(creds);
153
+ const envVars = adapter.credentialsToEnvironment(creds);
154
+
155
+ // Check what the adapter supports
156
+ console.log(adapter.capabilities);
157
+ // { keychain: true, file: true, environment: true, installApiKey: false }
158
+ ```
159
+
160
+ ## Adapter Capabilities
161
+
162
+ Each agent adapter declares its storage capabilities:
163
+
164
+ | Agent | Keychain | File | Environment | Install API Key |
165
+ | -------- | :------: | :--: | :---------: | :-------------: |
166
+ | claude | macOS | Yes | Yes | No (env-only) |
167
+ | codex | macOS | Yes | Yes | No (env-only) |
168
+ | gemini | macOS | Yes | Yes | No (env-only) |
169
+ | opencode | No | Yes | Yes | No |
170
+ | copilot | macOS | Yes | Yes | No (env-only) |
171
+
172
+ **Notes:**
173
+
174
+ - **Keychain**: macOS Keychain support (not available on Linux/Windows)
175
+ - **File**: File-based credential storage
176
+ - **Environment**: Can read credentials from environment variables
177
+ - **Install API Key**: Whether API keys can be installed (vs. read from env only)
178
+
179
+ ## Credential Export Format
180
+
181
+ Exported credentials are encrypted with AES-256-GCM:
182
+
183
+ ```json
184
+ {
185
+ "version": 1,
186
+ "agent": "claude",
187
+ "ciphertext": "<base64>",
188
+ "salt": "<base64>",
189
+ "iv": "<base64>",
190
+ "tag": "<base64>"
191
+ }
117
192
  ```
118
193
 
119
- ## Authentication Methods
194
+ - Key derivation: PBKDF2 with SHA-256, 100,000 iterations
195
+ - Use `--no-password` for CI/CD (uses a default password)
196
+ - Files are written with `0o600` permissions
197
+
198
+ ## Environment Variables
199
+
200
+ For CI/CD workflows, credentials can be passed via environment variables:
201
+
202
+ | Agent | Credential Env Var |
203
+ | -------- | ------------------------- |
204
+ | claude | `AX_CLAUDE_CREDENTIALS` |
205
+ | codex | `AX_CODEX_CREDENTIALS` |
206
+ | gemini | `AX_GEMINI_CREDENTIALS` |
207
+ | copilot | `AX_COPILOT_CREDENTIALS` |
208
+ | opencode | `AX_OPENCODE_CREDENTIALS` |
209
+
210
+ Use `installCredentialsFromEnvironmentVariable()` to install credentials from these variables programmatically.
120
211
 
121
- | Agent | Methods |
122
- | ----------- | ------------------------------------------ |
123
- | claude-code | OAuth (keychain/file) or ANTHROPIC_API_KEY |
124
- | codex | ChatGPT OAuth or OPENAI_API_KEY |
125
- | gemini | OAuth or GEMINI_API_KEY |
126
- | opencode | Multi-provider OAuth |
212
+ ## Config Directory Requirements
127
213
 
128
- ## Agent Rule
214
+ Some agents require specific directory name suffixes:
129
215
 
130
- Add to your `CLAUDE.md` or `AGENTS.md`:
216
+ | Agent | Directory Requirement | Example |
217
+ | -------- | ------------------------ | -------------------- |
218
+ | claude | Any name | `/tmp/my-config` |
219
+ | codex | Any name | `/tmp/my-config` |
220
+ | gemini | Must end with `.gemini` | `/tmp/home/.gemini` |
221
+ | copilot | Must end with `.copilot` | `/tmp/home/.copilot` |
222
+ | opencode | Must end with `opencode` | `/tmp/data/opencode` |
131
223
 
132
- ```markdown
133
- # Rule: `axauth` Usage
224
+ ## Architecture
134
225
 
135
- Run `npx -y axauth --help` to learn available options.
226
+ axauth follows the adapter pattern with a functional core:
136
227
 
137
- Use `axauth` to manage AI agent credentials. Check auth status with `axauth list`,
138
- get tokens with `axauth token`, and export/import credentials for CI/CD workflows.
139
228
  ```
229
+ src/
230
+ ├── index.ts # Public API exports
231
+ ├── cli.ts # CLI entry point
232
+ ├── crypto.ts # AES-256-GCM encryption
233
+ ├── commands/
234
+ │ └── auth.ts # CLI command handlers
235
+ └── auth/
236
+ ├── adapter.ts # AuthAdapter interface
237
+ ├── types.ts # AuthStatus, Credentials types
238
+ ├── registry.ts # Adapter registry and unified operations
239
+ └── agents/ # Agent-specific adapters
240
+ ├── claude-code.ts
241
+ ├── claude-code-storage.ts
242
+ ├── codex.ts
243
+ ├── codex-storage.ts
244
+ ├── codex-config.ts
245
+ ├── gemini.ts
246
+ ├── gemini-storage.ts
247
+ ├── gemini-auth-check.ts
248
+ ├── copilot.ts
249
+ ├── copilot-storage.ts
250
+ ├── copilot-auth-check.ts
251
+ └── opencode.ts
252
+ ```
253
+
254
+ ## Related Packages
140
255
 
141
- ## Development Status
256
+ axauth is part of the [a╳point](https://axpoint.dev) ecosystem:
142
257
 
143
- 🚧 **Work in Progress**
258
+ - **axshared** - Shared types and agent metadata
259
+ - **axconfig** - Permission and configuration management
260
+ - **axrun** - Agent execution and output normalization
144
261
 
145
262
  ## License
146
263
 
@@ -25,23 +25,34 @@ interface OperationResult {
25
25
  * - `undefined`: Use the `_source` marker in credentials, or default to file
26
26
  */
27
27
  interface InstallOptions {
28
- /** Storage type for default location (ignored if path is set) */
28
+ /** Storage type for default location (ignored if configDir is set) */
29
29
  storage?: StorageType;
30
- /** Custom file path (forces file storage, keychain not available) */
31
- path?: string;
30
+ /** Custom config directory (forces file storage, keychain not available) */
31
+ configDir?: string;
32
32
  }
33
33
  /**
34
34
  * Options for credential removal.
35
35
  *
36
- * When `path` is provided, only the file at that path is removed.
37
- * Keychain credentials are not affected (keychain is only for default location).
36
+ * When `configDir` is provided, only the credentials file in that directory
37
+ * is removed. Keychain credentials are not affected (keychain is only for
38
+ * default location).
38
39
  *
39
- * When `path` is not provided, credentials are removed from all default
40
+ * When `configDir` is not provided, credentials are removed from all default
40
41
  * locations (keychain and/or default file path).
41
42
  */
42
43
  interface RemoveOptions {
43
- /** Custom file path to remove (only removes this file, not keychain) */
44
- path?: string;
44
+ /** Custom config directory (removes only this location, not keychain) */
45
+ configDir?: string;
46
+ }
47
+ /**
48
+ * Options for token extraction.
49
+ *
50
+ * Some agents (like opencode) support multiple providers. Use the `provider`
51
+ * option to specify which provider's token to extract.
52
+ */
53
+ interface TokenOptions {
54
+ /** Provider ID for multi-provider agents (e.g., "anthropic", "openai") */
55
+ provider?: string;
45
56
  }
46
57
  /** Capabilities that an adapter supports */
47
58
  interface AdapterCapabilities {
@@ -95,10 +106,10 @@ interface AuthAdapter {
95
106
  /**
96
107
  * Remove credentials from storage.
97
108
  *
98
- * When `options.path` is provided, only the file at that path is removed.
99
- * Keychain credentials are not affected.
109
+ * When `options.configDir` is provided, only the credentials file in that
110
+ * directory is removed. Keychain credentials are not affected.
100
111
  *
101
- * When `options.path` is not provided, credentials are removed from all
112
+ * When `options.configDir` is not provided, credentials are removed from all
102
113
  * default locations (keychain and/or default file path).
103
114
  */
104
115
  removeCredentials(options?: RemoveOptions): OperationResult;
@@ -107,8 +118,11 @@ interface AuthAdapter {
107
118
  *
108
119
  * Returns the primary access token (OAuth token or API key) from
109
120
  * the credential data. Returns undefined if no token found.
121
+ *
122
+ * For multi-provider agents (like opencode), use `options.provider`
123
+ * to specify which provider's token to extract.
110
124
  */
111
- getAccessToken(creds: Credentials): string | undefined;
125
+ getAccessToken(creds: Credentials, options?: TokenOptions): string | undefined;
112
126
  /**
113
127
  * Convert credentials to environment variables.
114
128
  *
@@ -118,4 +132,4 @@ interface AuthAdapter {
118
132
  */
119
133
  credentialsToEnvironment(creds: Credentials): Record<string, string>;
120
134
  }
121
- export type { AdapterCapabilities, AuthAdapter, InstallOptions, OperationResult, RemoveOptions, };
135
+ export type { AdapterCapabilities, AuthAdapter, InstallOptions, OperationResult, RemoveOptions, TokenOptions, };
@@ -4,7 +4,6 @@
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { userInfo } from "node:os";
6
6
  import path from "node:path";
7
- import { claudeCodeConfigReader } from "axconfig";
8
7
  import { deleteFile, loadJsonFile, saveJsonFile } from "../file-storage.js";
9
8
  import { getResolvedConfigDirectory } from "../resolve-config-directory.js";
10
9
  import { deleteFromKeychain, loadFromKeychain, saveToKeychain, } from "../keychain.js";
@@ -15,7 +14,7 @@ function getUsername() {
15
14
  }
16
15
  /** Get default credentials file path */
17
16
  function getDefaultCredsFilePath() {
18
- return path.join(getResolvedConfigDirectory(claudeCodeConfigReader), ".credentials.json");
17
+ return path.join(getResolvedConfigDirectory("claude"), ".credentials.json");
19
18
  }
20
19
  /** Load OAuth credentials from keychain */
21
20
  function loadKeychainCreds() {
@@ -3,9 +3,11 @@
3
3
  *
4
4
  * Supports OAuth via keychain (macOS) or file, and API key via env var.
5
5
  */
6
+ import path from "node:path";
6
7
  import { isMacOS } from "../keychain.js";
7
8
  import { deleteFileCreds, deleteKeychainCreds, getDefaultCredsFilePath, loadFileCreds, loadKeychainCreds, saveFileCreds, saveKeychainCreds, } from "./claude-code-storage.js";
8
9
  const AGENT_ID = "claude";
10
+ const CREDS_FILE_NAME = ".credentials.json";
9
11
  /** Claude Code authentication adapter */
10
12
  const claudeCodeAdapter = {
11
13
  agentId: AGENT_ID,
@@ -77,17 +79,18 @@ const claudeCodeAdapter = {
77
79
  };
78
80
  }
79
81
  const { _source, ...oauthData } = creds.data;
80
- // Custom path forces file storage (keychain only for default location)
81
- if (options?.path) {
82
- if (saveFileCreds(oauthData, options.path)) {
82
+ // Custom config directory forces file storage (keychain only for default location)
83
+ if (options?.configDir) {
84
+ const targetPath = path.join(options.configDir, CREDS_FILE_NAME);
85
+ if (saveFileCreds(oauthData, targetPath)) {
83
86
  return {
84
87
  ok: true,
85
- message: `Installed credentials to ${options.path}`,
88
+ message: `Installed credentials to ${targetPath}`,
86
89
  };
87
90
  }
88
91
  return {
89
92
  ok: false,
90
- message: `Failed to install credentials to ${options.path}`,
93
+ message: `Failed to install credentials to ${targetPath}`,
91
94
  };
92
95
  }
93
96
  // Default location: use storage option or _source marker
@@ -114,10 +117,11 @@ const claudeCodeAdapter = {
114
117
  };
115
118
  },
116
119
  removeCredentials(options) {
117
- // Custom path: only remove that specific file (no keychain)
118
- if (options?.path) {
119
- if (deleteFileCreds(options.path)) {
120
- return { ok: true, message: `Removed ${options.path}` };
120
+ // Custom config directory: only remove that specific file (no keychain)
121
+ if (options?.configDir) {
122
+ const targetPath = path.join(options.configDir, CREDS_FILE_NAME);
123
+ if (deleteFileCreds(targetPath)) {
124
+ return { ok: true, message: `Removed ${targetPath}` };
121
125
  }
122
126
  return {
123
127
  ok: true,
@@ -8,7 +8,7 @@ import { codexConfigReader } from "axconfig";
8
8
  import { getResolvedConfigDirectory } from "../resolve-config-directory.js";
9
9
  /** Get the codex home directory */
10
10
  function getCodexHome() {
11
- return getResolvedConfigDirectory(codexConfigReader);
11
+ return getResolvedConfigDirectory("codex");
12
12
  }
13
13
  /** Get auth file path */
14
14
  function getAuthFilePath() {
@@ -10,6 +10,7 @@ import { checkAuth, extractRawCredentials } from "./codex-auth-check.js";
10
10
  import { getAuthFilePath, getCodexHome, updateConfigStorage, } from "./codex-config.js";
11
11
  import { buildAuthContent, deleteFileCreds, deleteKeychainCreds, saveFileCreds, saveKeychainCreds, } from "./codex-storage.js";
12
12
  const AGENT_ID = "codex";
13
+ const CREDS_FILE_NAME = "auth.json";
13
14
  /** Codex authentication adapter */
14
15
  const codexAdapter = {
15
16
  agentId: AGENT_ID,
@@ -23,24 +24,24 @@ const codexAdapter = {
23
24
  extractRawCredentials,
24
25
  installCredentials(creds, options) {
25
26
  const authContent = buildAuthContent(creds.type, creds.data);
26
- // Custom path forces file storage (keychain only for default location)
27
- if (options?.path) {
28
- const parentDirectory = path.dirname(options.path);
29
- if (!ensureDirectory(parentDirectory)) {
27
+ // Custom config directory forces file storage (keychain only for default location)
28
+ if (options?.configDir) {
29
+ if (!ensureDirectory(options.configDir)) {
30
30
  return {
31
31
  ok: false,
32
- message: `Failed to create directory ${parentDirectory}`,
32
+ message: `Failed to create directory ${options.configDir}`,
33
33
  };
34
34
  }
35
- if (saveFileCreds(authContent, options.path)) {
35
+ const targetPath = path.join(options.configDir, CREDS_FILE_NAME);
36
+ if (saveFileCreds(authContent, targetPath)) {
36
37
  return {
37
38
  ok: true,
38
- message: `Installed credentials to ${options.path}`,
39
+ message: `Installed credentials to ${targetPath}`,
39
40
  };
40
41
  }
41
42
  return {
42
43
  ok: false,
43
- message: `Failed to install credentials to ${options.path}`,
44
+ message: `Failed to install credentials to ${targetPath}`,
44
45
  };
45
46
  }
46
47
  // Default location: use storage option or _source marker
@@ -72,10 +73,11 @@ const codexAdapter = {
72
73
  return { ok: false, message: "Failed to install credentials to file" };
73
74
  },
74
75
  removeCredentials(options) {
75
- // Custom path: only remove that specific file (no keychain)
76
- if (options?.path) {
77
- if (deleteFileCreds(options.path)) {
78
- return { ok: true, message: `Removed ${options.path}` };
76
+ // Custom config directory: only remove that specific file (no keychain)
77
+ if (options?.configDir) {
78
+ const targetPath = path.join(options.configDir, CREDS_FILE_NAME);
79
+ if (deleteFileCreds(targetPath)) {
80
+ return { ok: true, message: `Removed ${targetPath}` };
79
81
  }
80
82
  return {
81
83
  ok: true,
@@ -7,7 +7,6 @@
7
7
  */
8
8
  import { existsSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
9
9
  import path from "node:path";
10
- import { copilotConfigReader } from "axconfig";
11
10
  import { ensureDirectory, loadJsonFile, saveJsonFile, } from "../file-storage.js";
12
11
  import { deleteFromKeychain, isMacOS, loadFromKeychain, saveToKeychain, } from "../keychain.js";
13
12
  import { getResolvedConfigDirectory } from "../resolve-config-directory.js";
@@ -15,7 +14,7 @@ const KEYCHAIN_SERVICE = "copilot-cli";
15
14
  const DEFAULT_HOST = "https://github.com";
16
15
  /** Get the copilot config directory */
17
16
  function getConfigDirectory() {
18
- return getResolvedConfigDirectory(copilotConfigReader);
17
+ return getResolvedConfigDirectory("copilot");
19
18
  }
20
19
  /** Get the default config file path */
21
20
  function getConfigFilePath() {
@@ -4,11 +4,13 @@
4
4
  * Supports OAuth via keychain (macOS) or file, and GitHub token via env var.
5
5
  * Also supports GitHub CLI (`gh auth login`) as a fallback.
6
6
  */
7
+ import path from "node:path";
7
8
  import { ensureDirectory } from "../file-storage.js";
8
9
  import { isMacOS } from "../keychain.js";
9
10
  import { findFirstAvailableToken } from "./copilot-auth-check.js";
10
11
  import { deleteFileToken, deleteKeychainToken, deleteTokenFromPath, getConfigDirectory, getConfigFilePath, saveFileToken, saveKeychainToken, saveTokenToPath, } from "./copilot-storage.js";
11
12
  const AGENT_ID = "copilot";
13
+ const CREDS_FILE_NAME = "token.json";
12
14
  /** Copilot CLI authentication adapter */
13
15
  const copilotAdapter = {
14
16
  agentId: AGENT_ID,
@@ -58,17 +60,18 @@ const copilotAdapter = {
58
60
  if (!token) {
59
61
  return { ok: false, message: "No access token found in credentials" };
60
62
  }
61
- // Custom path forces file storage
62
- if (options?.path) {
63
- if (saveTokenToPath(token, options.path)) {
63
+ // Custom config directory forces file storage
64
+ if (options?.configDir) {
65
+ const targetPath = path.join(options.configDir, CREDS_FILE_NAME);
66
+ if (saveTokenToPath(token, targetPath)) {
64
67
  return {
65
68
  ok: true,
66
- message: `Installed credentials to ${options.path}`,
69
+ message: `Installed credentials to ${targetPath}`,
67
70
  };
68
71
  }
69
72
  return {
70
73
  ok: false,
71
- message: `Failed to install credentials to ${options.path}`,
74
+ message: `Failed to install credentials to ${targetPath}`,
72
75
  };
73
76
  }
74
77
  // Default location: use storage option or _source marker
@@ -102,10 +105,11 @@ const copilotAdapter = {
102
105
  return { ok: false, message: "Failed to install credentials to file" };
103
106
  },
104
107
  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}` };
108
+ // Custom config directory: only remove that specific file
109
+ if (options?.configDir) {
110
+ const targetPath = path.join(options.configDir, CREDS_FILE_NAME);
111
+ if (deleteTokenFromPath(targetPath)) {
112
+ return { ok: true, message: `Removed ${targetPath}` };
109
113
  }
110
114
  return {
111
115
  ok: true,
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * Gemini CLI authentication checking.
3
3
  *
4
- * Determines auth status based on configured auth type and available credentials.
4
+ * Uses priority-ordered credential discovery: env keychain file.
5
5
  */
6
6
  /** Credential source for auth method display */
7
- type CredentialSource = "keychain" | "file" | "api-key" | "vertex-ai" | "adc";
7
+ type CredentialSource = "keychain" | "file" | "api-key" | "vertex-ai";
8
8
  /** Result of finding available credentials */
9
9
  interface CredentialResult {
10
10
  source: CredentialSource;
11
11
  method: string;
12
12
  data?: Record<string, unknown>;
13
13
  }
14
- /** Find credentials based on configured auth type */
14
+ /** Find credentials using priority-ordered discovery: env keychain → file */
15
15
  declare function findCredentials(): CredentialResult | undefined;
16
16
  export { findCredentials };