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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/bin/axauth +17 -0
  4. package/dist/auth/adapter.d.ts +121 -0
  5. package/dist/auth/adapter.js +7 -0
  6. package/dist/auth/agents/claude-code-storage.d.ts +18 -0
  7. package/dist/auth/agents/claude-code-storage.js +66 -0
  8. package/dist/auth/agents/claude-code.d.ts +9 -0
  9. package/dist/auth/agents/claude-code.js +162 -0
  10. package/dist/auth/agents/codex-auth-check.d.ts +9 -0
  11. package/dist/auth/agents/codex-auth-check.js +101 -0
  12. package/dist/auth/agents/codex-config.d.ts +12 -0
  13. package/dist/auth/agents/codex-config.js +40 -0
  14. package/dist/auth/agents/codex-storage.d.ts +43 -0
  15. package/dist/auth/agents/codex-storage.js +82 -0
  16. package/dist/auth/agents/codex.d.ts +9 -0
  17. package/dist/auth/agents/codex.js +126 -0
  18. package/dist/auth/agents/copilot-auth-check.d.ts +17 -0
  19. package/dist/auth/agents/copilot-auth-check.js +46 -0
  20. package/dist/auth/agents/copilot-storage.d.ts +32 -0
  21. package/dist/auth/agents/copilot-storage.js +139 -0
  22. package/dist/auth/agents/copilot.d.ts +10 -0
  23. package/dist/auth/agents/copilot.js +144 -0
  24. package/dist/auth/agents/gemini-auth-check.d.ts +16 -0
  25. package/dist/auth/agents/gemini-auth-check.js +73 -0
  26. package/dist/auth/agents/gemini-storage.d.ts +24 -0
  27. package/dist/auth/agents/gemini-storage.js +69 -0
  28. package/dist/auth/agents/gemini.d.ts +9 -0
  29. package/dist/auth/agents/gemini.js +157 -0
  30. package/dist/auth/agents/opencode.d.ts +9 -0
  31. package/dist/auth/agents/opencode.js +128 -0
  32. package/dist/auth/file-storage.d.ts +14 -0
  33. package/dist/auth/file-storage.js +63 -0
  34. package/dist/auth/keychain.d.ts +12 -0
  35. package/dist/auth/keychain.js +56 -0
  36. package/dist/auth/registry.d.ts +109 -0
  37. package/dist/auth/registry.js +145 -0
  38. package/dist/auth/resolve-config-directory.d.ts +14 -0
  39. package/dist/auth/resolve-config-directory.js +16 -0
  40. package/dist/auth/types.d.ts +19 -0
  41. package/dist/auth/types.js +4 -0
  42. package/dist/cli.d.ts +7 -0
  43. package/dist/cli.js +74 -0
  44. package/dist/commands/auth.d.ts +34 -0
  45. package/dist/commands/auth.js +155 -0
  46. package/dist/crypto.d.ts +39 -0
  47. package/dist/crypto.js +78 -0
  48. package/dist/index.d.ts +37 -0
  49. package/dist/index.js +39 -0
  50. package/package.json +96 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Łukasz Jerciński
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # axauth
2
+
3
+ Authentication management library and CLI for AI coding agents.
4
+
5
+ ## Overview
6
+
7
+ axauth manages credentials for AI coding agents. It can be used:
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
11
+
12
+ ## Supported Agents
13
+
14
+ - **claude-code** - Claude Code (Anthropic)
15
+ - **codex** - Codex CLI (OpenAI)
16
+ - **gemini** - Gemini CLI (Google)
17
+ - **opencode** - OpenCode
18
+
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
+ }
29
+
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
+ }
41
+ ```
42
+
43
+ ## CLI Commands
44
+
45
+ ```bash
46
+ # List agents and their auth status
47
+ axauth list
48
+ axauth list --json
49
+
50
+ # Get access token for an agent (outputs raw token for piping)
51
+ axauth token --agent claude-code
52
+
53
+ # 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
59
+
60
+ # Install credentials from exported file
61
+ axauth install-credentials --agent claude-code --input creds.json
62
+ ```
63
+
64
+ ## Pipeline Examples
65
+
66
+ The CLI outputs TSV format for easy processing with standard Unix tools:
67
+
68
+ ```bash
69
+ # List all agents and their auth status
70
+ axauth list
71
+ # AGENT STATUS METHOD
72
+ # claude-code authenticated OAuth (max)
73
+ # codex authenticated ChatGPT OAuth
74
+ # ...
75
+
76
+ # Filter to show only authenticated agents
77
+ axauth list | tail -n +2 | awk -F'\t' '$2 == "authenticated"'
78
+
79
+ # Count agents by status
80
+ axauth list | tail -n +2 | cut -f2 | sort | uniq -c
81
+
82
+ # Extract agent names as a list
83
+ axauth list | tail -n +2 | cut -f1
84
+
85
+ # Check if a specific agent is authenticated
86
+ axauth list --json | jq -e '.[] | select(.agentId == "claude-code") | .authenticated'
87
+
88
+ # Check Claude Code usage via OAuth endpoint
89
+ curl -s -H "Authorization: Bearer $(axauth token --agent claude-code)" \
90
+ -H "anthropic-beta: oauth-2025-04-20" \
91
+ https://api.anthropic.com/api/oauth/usage | jq .
92
+ ```
93
+
94
+ ## Module Structure
95
+
96
+ ```
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
117
+ ```
118
+
119
+ ## Authentication Methods
120
+
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 |
127
+
128
+ ## Agent Rule
129
+
130
+ Add to your `CLAUDE.md` or `AGENTS.md`:
131
+
132
+ ```markdown
133
+ # Rule: `axauth` Usage
134
+
135
+ Run `npx -y axauth --help` to learn available options.
136
+
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
+ ```
140
+
141
+ ## Development Status
142
+
143
+ 🚧 **Work in Progress**
144
+
145
+ ## License
146
+
147
+ MIT
package/bin/axauth ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point that dynamically imports the compiled TypeScript.
4
+ *
5
+ * Uses top-level await to ensure module evaluation errors are handled
6
+ * properly. Without await, errors during import would surface as unhandled
7
+ * rejections instead of clean CLI failures with appropriate exit codes.
8
+ */
9
+ try {
10
+ await import("../dist/cli.js");
11
+ } catch (error) {
12
+ console.error(
13
+ "Failed to start axauth:",
14
+ error instanceof Error ? error.message : error,
15
+ );
16
+ process.exitCode = 1;
17
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Unified authentication adapter interface.
3
+ *
4
+ * All agents implement this interface, providing a consistent API
5
+ * for auth operations regardless of underlying storage mechanism.
6
+ */
7
+ import type { AgentCli } from "axshared";
8
+ import type { AuthStatus, Credentials } from "./types.js";
9
+ /** Storage type preference for credential installation */
10
+ type StorageType = "keychain" | "file";
11
+ /** Result of an auth operation (install, remove) */
12
+ interface OperationResult {
13
+ ok: boolean;
14
+ message: string;
15
+ }
16
+ /**
17
+ * Options for credential installation.
18
+ *
19
+ * When `path` is provided, credentials are always written as a file to that
20
+ * path (keychain is only for default location). The `storage` option is ignored.
21
+ *
22
+ * When `path` is not provided, `storage` controls where credentials go:
23
+ * - `"keychain"`: Store in macOS Keychain (if supported)
24
+ * - `"file"`: Store in agent's default file location
25
+ * - `undefined`: Use the `_source` marker in credentials, or default to file
26
+ */
27
+ interface InstallOptions {
28
+ /** Storage type for default location (ignored if path is set) */
29
+ storage?: StorageType;
30
+ /** Custom file path (forces file storage, keychain not available) */
31
+ path?: string;
32
+ }
33
+ /**
34
+ * Options for credential removal.
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).
38
+ *
39
+ * When `path` is not provided, credentials are removed from all default
40
+ * locations (keychain and/or default file path).
41
+ */
42
+ interface RemoveOptions {
43
+ /** Custom file path to remove (only removes this file, not keychain) */
44
+ path?: string;
45
+ }
46
+ /** Capabilities that an adapter supports */
47
+ interface AdapterCapabilities {
48
+ /** Whether keychain storage is supported (macOS only) */
49
+ keychain: boolean;
50
+ /** Whether file storage is supported */
51
+ file: boolean;
52
+ /** Whether environment variable auth is supported */
53
+ environment: boolean;
54
+ /** Whether API key credentials can be installed (vs env-only) */
55
+ installApiKey: boolean;
56
+ }
57
+ /**
58
+ * Authentication adapter for an agent.
59
+ *
60
+ * Encapsulates all auth operations for a single agent, hiding the
61
+ * complexity of different storage mechanisms and credential formats.
62
+ */
63
+ interface AuthAdapter {
64
+ /** Agent identifier */
65
+ readonly agentId: AgentCli;
66
+ /** Capabilities of this adapter */
67
+ readonly capabilities: AdapterCapabilities;
68
+ /**
69
+ * Check authentication status.
70
+ *
71
+ * Checks all supported auth methods (env vars, keychain, files)
72
+ * and returns the status without making API calls.
73
+ */
74
+ checkAuth(): AuthStatus;
75
+ /**
76
+ * Extract raw credentials for export.
77
+ *
78
+ * Returns the full credential data from the first available source,
79
+ * suitable for encrypted export. Returns undefined if not authenticated.
80
+ *
81
+ * Note: The `data` field format is agent-specific and not standardized.
82
+ * Use {@link getAccessToken} to extract the token in a uniform way.
83
+ */
84
+ extractRawCredentials(): Credentials | undefined;
85
+ /**
86
+ * Install credentials to storage.
87
+ *
88
+ * When `options.path` is provided, credentials are written as a file to that
89
+ * path. Keychain storage is not available for custom paths.
90
+ *
91
+ * When `options.path` is not provided, credentials are written to the default
92
+ * location. The `storage` option or `_source` marker controls keychain vs file.
93
+ */
94
+ installCredentials(creds: Credentials, options?: InstallOptions): OperationResult;
95
+ /**
96
+ * Remove credentials from storage.
97
+ *
98
+ * When `options.path` is provided, only the file at that path is removed.
99
+ * Keychain credentials are not affected.
100
+ *
101
+ * When `options.path` is not provided, credentials are removed from all
102
+ * default locations (keychain and/or default file path).
103
+ */
104
+ removeCredentials(options?: RemoveOptions): OperationResult;
105
+ /**
106
+ * Extract access token from credentials.
107
+ *
108
+ * Returns the primary access token (OAuth token or API key) from
109
+ * the credential data. Returns undefined if no token found.
110
+ */
111
+ getAccessToken(creds: Credentials): string | undefined;
112
+ /**
113
+ * Convert credentials to environment variables.
114
+ *
115
+ * Returns a record of environment variable names to values that can
116
+ * be used to authenticate the agent. Returns empty object if the
117
+ * credential type doesn't support env var authentication.
118
+ */
119
+ credentialsToEnvironment(creds: Credentials): Record<string, string>;
120
+ }
121
+ export type { AdapterCapabilities, AuthAdapter, InstallOptions, OperationResult, RemoveOptions, };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Unified authentication adapter interface.
3
+ *
4
+ * All agents implement this interface, providing a consistent API
5
+ * for auth operations regardless of underlying storage mechanism.
6
+ */
7
+ export {};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Claude Code credential storage operations.
3
+ */
4
+ /** Get default credentials file path */
5
+ declare function getDefaultCredsFilePath(): string;
6
+ /** Load OAuth credentials from keychain */
7
+ declare function loadKeychainCreds(): Record<string, unknown> | undefined;
8
+ /** Load OAuth credentials from file */
9
+ declare function loadFileCreds(): Record<string, unknown> | undefined;
10
+ /** Save credentials to keychain */
11
+ declare function saveKeychainCreds(oauthData: Record<string, unknown>): boolean;
12
+ /** Save credentials to file */
13
+ declare function saveFileCreds(oauthData: Record<string, unknown>, filePath: string): boolean;
14
+ /** Delete credentials from keychain */
15
+ declare function deleteKeychainCreds(): boolean;
16
+ /** Delete credentials from file */
17
+ declare function deleteFileCreds(filePath?: string): boolean;
18
+ export { deleteFileCreds, deleteKeychainCreds, getDefaultCredsFilePath, loadFileCreds, loadKeychainCreds, saveFileCreds, saveKeychainCreds, };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Claude Code credential storage operations.
3
+ */
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { userInfo } from "node:os";
6
+ import path from "node:path";
7
+ import { claudeCodeConfigReader } from "axconfig";
8
+ import { deleteFile, loadJsonFile, saveJsonFile } from "../file-storage.js";
9
+ import { getResolvedConfigDirectory } from "../resolve-config-directory.js";
10
+ import { deleteFromKeychain, loadFromKeychain, saveToKeychain, } from "../keychain.js";
11
+ const KEYCHAIN_SERVICE = "Claude Code-credentials";
12
+ /** Get the current username for keychain operations */
13
+ function getUsername() {
14
+ return userInfo().username;
15
+ }
16
+ /** Get default credentials file path */
17
+ function getDefaultCredsFilePath() {
18
+ return path.join(getResolvedConfigDirectory(claudeCodeConfigReader), ".credentials.json");
19
+ }
20
+ /** Load OAuth credentials from keychain */
21
+ function loadKeychainCreds() {
22
+ const raw = loadFromKeychain(KEYCHAIN_SERVICE, getUsername());
23
+ if (!raw)
24
+ return undefined;
25
+ try {
26
+ const parsed = JSON.parse(raw);
27
+ return parsed.claudeAiOauth;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ /** Load OAuth credentials from file */
34
+ function loadFileCreds() {
35
+ const creds = loadJsonFile(getDefaultCredsFilePath());
36
+ return creds?.claudeAiOauth;
37
+ }
38
+ /** Save credentials to keychain */
39
+ function saveKeychainCreds(oauthData) {
40
+ const data = JSON.stringify({ claudeAiOauth: oauthData });
41
+ return saveToKeychain(KEYCHAIN_SERVICE, getUsername(), data);
42
+ }
43
+ /** Save credentials to file */
44
+ function saveFileCreds(oauthData, filePath) {
45
+ // Merge with existing credentials if file exists
46
+ let existingCreds = {};
47
+ if (existsSync(filePath)) {
48
+ try {
49
+ existingCreds = JSON.parse(readFileSync(filePath, "utf8"));
50
+ }
51
+ catch {
52
+ // Start fresh if invalid
53
+ }
54
+ }
55
+ const newCreds = { ...existingCreds, claudeAiOauth: oauthData };
56
+ return saveJsonFile(filePath, newCreds, { mode: 0o600 });
57
+ }
58
+ /** Delete credentials from keychain */
59
+ function deleteKeychainCreds() {
60
+ return deleteFromKeychain(KEYCHAIN_SERVICE, getUsername());
61
+ }
62
+ /** Delete credentials from file */
63
+ function deleteFileCreds(filePath) {
64
+ return deleteFile(filePath ?? getDefaultCredsFilePath());
65
+ }
66
+ export { deleteFileCreds, deleteKeychainCreds, getDefaultCredsFilePath, loadFileCreds, loadKeychainCreds, saveFileCreds, saveKeychainCreds, };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Claude Code auth adapter.
3
+ *
4
+ * Supports OAuth via keychain (macOS) or file, and API key via env var.
5
+ */
6
+ import type { AuthAdapter } from "../adapter.js";
7
+ /** Claude Code authentication adapter */
8
+ declare const claudeCodeAdapter: AuthAdapter;
9
+ export { claudeCodeAdapter };
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Claude Code auth adapter.
3
+ *
4
+ * Supports OAuth via keychain (macOS) or file, and API key via env var.
5
+ */
6
+ import { isMacOS } from "../keychain.js";
7
+ import { deleteFileCreds, deleteKeychainCreds, getDefaultCredsFilePath, loadFileCreds, loadKeychainCreds, saveFileCreds, saveKeychainCreds, } from "./claude-code-storage.js";
8
+ const AGENT_ID = "claude";
9
+ /** Claude Code authentication adapter */
10
+ const claudeCodeAdapter = {
11
+ agentId: AGENT_ID,
12
+ capabilities: {
13
+ keychain: true,
14
+ file: true,
15
+ environment: true,
16
+ installApiKey: false,
17
+ },
18
+ checkAuth() {
19
+ if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
20
+ return { agentId: AGENT_ID, authenticated: true, method: "OAuth (env)" };
21
+ }
22
+ const keychainCreds = loadKeychainCreds();
23
+ if (keychainCreds) {
24
+ const subType = keychainCreds.subscriptionType;
25
+ return {
26
+ agentId: AGENT_ID,
27
+ authenticated: true,
28
+ method: `OAuth (${subType ?? "keychain"})`,
29
+ };
30
+ }
31
+ if (loadFileCreds()) {
32
+ return { agentId: AGENT_ID, authenticated: true, method: "OAuth (file)" };
33
+ }
34
+ if (process.env.ANTHROPIC_API_KEY) {
35
+ return { agentId: AGENT_ID, authenticated: true, method: "API key" };
36
+ }
37
+ return { agentId: AGENT_ID, authenticated: false };
38
+ },
39
+ extractRawCredentials() {
40
+ if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
41
+ return {
42
+ agent: AGENT_ID,
43
+ type: "oauth",
44
+ data: { accessToken: process.env.CLAUDE_CODE_OAUTH_TOKEN },
45
+ };
46
+ }
47
+ const keychainCreds = loadKeychainCreds();
48
+ if (keychainCreds) {
49
+ return {
50
+ agent: AGENT_ID,
51
+ type: "oauth",
52
+ data: { ...keychainCreds, _source: "keychain" },
53
+ };
54
+ }
55
+ const fileCreds = loadFileCreds();
56
+ if (fileCreds) {
57
+ return {
58
+ agent: AGENT_ID,
59
+ type: "oauth",
60
+ data: { ...fileCreds, _source: "file" },
61
+ };
62
+ }
63
+ if (process.env.ANTHROPIC_API_KEY) {
64
+ return {
65
+ agent: AGENT_ID,
66
+ type: "api-key",
67
+ data: { apiKey: process.env.ANTHROPIC_API_KEY },
68
+ };
69
+ }
70
+ return undefined;
71
+ },
72
+ installCredentials(creds, options) {
73
+ if (creds.type === "api-key") {
74
+ return {
75
+ ok: false,
76
+ message: "API key credentials cannot be installed (use ANTHROPIC_API_KEY env var)",
77
+ };
78
+ }
79
+ 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)) {
83
+ return {
84
+ ok: true,
85
+ message: `Installed credentials to ${options.path}`,
86
+ };
87
+ }
88
+ return {
89
+ ok: false,
90
+ message: `Failed to install credentials to ${options.path}`,
91
+ };
92
+ }
93
+ // Default location: use storage option or _source marker
94
+ const targetStorage = options?.storage ?? (_source === "keychain" ? "keychain" : "file");
95
+ if (targetStorage === "keychain") {
96
+ if (!isMacOS()) {
97
+ return {
98
+ ok: false,
99
+ message: "Keychain storage is only available on macOS",
100
+ };
101
+ }
102
+ if (saveKeychainCreds(oauthData)) {
103
+ return { ok: true, message: "Installed credentials to macOS Keychain" };
104
+ }
105
+ return { ok: false, message: "Failed to save to macOS Keychain" };
106
+ }
107
+ const defaultPath = getDefaultCredsFilePath();
108
+ if (saveFileCreds(oauthData, defaultPath)) {
109
+ return { ok: true, message: `Installed credentials to ${defaultPath}` };
110
+ }
111
+ return {
112
+ ok: false,
113
+ message: `Failed to install credentials to ${defaultPath}`,
114
+ };
115
+ },
116
+ 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}` };
121
+ }
122
+ return {
123
+ ok: true,
124
+ message: "No credentials file found at specified path",
125
+ };
126
+ }
127
+ // Default location: remove from both keychain and default file
128
+ const removedFrom = [];
129
+ if (deleteKeychainCreds()) {
130
+ removedFrom.push("macOS Keychain");
131
+ }
132
+ if (deleteFileCreds()) {
133
+ removedFrom.push(getDefaultCredsFilePath());
134
+ }
135
+ if (removedFrom.length === 0) {
136
+ return { ok: true, message: "No credentials found" };
137
+ }
138
+ return { ok: true, message: `Removed from ${removedFrom.join(" and ")}` };
139
+ },
140
+ getAccessToken(creds) {
141
+ const data = creds.data;
142
+ if (creds.type === "oauth" && typeof data.accessToken === "string") {
143
+ return data.accessToken;
144
+ }
145
+ if (creds.type === "api-key" && typeof data.apiKey === "string") {
146
+ return data.apiKey;
147
+ }
148
+ return undefined;
149
+ },
150
+ credentialsToEnvironment(creds) {
151
+ const environment = {};
152
+ const data = creds.data;
153
+ if (creds.type === "oauth" && typeof data.accessToken === "string") {
154
+ environment.CLAUDE_CODE_OAUTH_TOKEN = data.accessToken;
155
+ }
156
+ else if (creds.type === "api-key" && typeof data.apiKey === "string") {
157
+ environment.ANTHROPIC_API_KEY = data.apiKey;
158
+ }
159
+ return environment;
160
+ },
161
+ };
162
+ export { claudeCodeAdapter };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Codex auth status checking and credential extraction.
3
+ */
4
+ import type { AuthStatus, Credentials } from "../types.js";
5
+ /** Check auth status across all sources */
6
+ declare function checkAuth(): AuthStatus;
7
+ /** Extract raw credentials from first available source */
8
+ declare function extractRawCredentials(): Credentials | undefined;
9
+ export { checkAuth, extractRawCredentials };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Codex auth status checking and credential extraction.
3
+ */
4
+ import { getEnvironmentApiKey, hasFileApiKey, hasFileOAuth, hasKeychainApiKey, hasKeychainOAuth, loadFileCreds, loadKeychainCreds, } from "./codex-storage.js";
5
+ const AGENT_ID = "codex";
6
+ /** Check if authenticated via environment variables */
7
+ function checkEnvironmentAuth() {
8
+ if (getEnvironmentApiKey()) {
9
+ return { agentId: AGENT_ID, authenticated: true, method: "API key (env)" };
10
+ }
11
+ return undefined;
12
+ }
13
+ /** Check if authenticated via keychain */
14
+ function checkKeychainAuth() {
15
+ const keychainAuth = loadKeychainCreds();
16
+ if (hasKeychainApiKey(keychainAuth)) {
17
+ return {
18
+ agentId: AGENT_ID,
19
+ authenticated: true,
20
+ method: "API key (keychain)",
21
+ };
22
+ }
23
+ if (hasKeychainOAuth(keychainAuth)) {
24
+ return {
25
+ agentId: AGENT_ID,
26
+ authenticated: true,
27
+ method: "ChatGPT OAuth (keychain)",
28
+ };
29
+ }
30
+ return undefined;
31
+ }
32
+ /** Check if authenticated via file */
33
+ function checkFileAuth() {
34
+ const fileAuth = loadFileCreds();
35
+ if (hasFileApiKey(fileAuth)) {
36
+ return { agentId: AGENT_ID, authenticated: true, method: "API key" };
37
+ }
38
+ if (hasFileOAuth(fileAuth)) {
39
+ return { agentId: AGENT_ID, authenticated: true, method: "ChatGPT OAuth" };
40
+ }
41
+ return undefined;
42
+ }
43
+ /** Check auth status across all sources */
44
+ function checkAuth() {
45
+ return (checkEnvironmentAuth() ??
46
+ checkKeychainAuth() ??
47
+ checkFileAuth() ?? { agentId: AGENT_ID, authenticated: false });
48
+ }
49
+ /** Extract credentials from environment */
50
+ function extractEnvironmentCredentials() {
51
+ const environmentKey = getEnvironmentApiKey();
52
+ if (environmentKey) {
53
+ return {
54
+ agent: AGENT_ID,
55
+ type: "api-key",
56
+ data: { apiKey: environmentKey },
57
+ };
58
+ }
59
+ return undefined;
60
+ }
61
+ /** Extract credentials from keychain */
62
+ function extractKeychainCredentials() {
63
+ const keychainAuth = loadKeychainCreds();
64
+ if (keychainAuth?.OPENAI_API_KEY) {
65
+ return {
66
+ agent: AGENT_ID,
67
+ type: "api-key",
68
+ data: { apiKey: keychainAuth.OPENAI_API_KEY, _source: "keychain" },
69
+ };
70
+ }
71
+ if (keychainAuth?.tokens) {
72
+ return {
73
+ agent: AGENT_ID,
74
+ type: "oauth",
75
+ data: { ...keychainAuth, _source: "keychain" },
76
+ };
77
+ }
78
+ return undefined;
79
+ }
80
+ /** Extract credentials from file */
81
+ function extractFileCredentials() {
82
+ const fileAuth = loadFileCreds();
83
+ if (fileAuth?.OPENAI_API_KEY) {
84
+ return {
85
+ agent: AGENT_ID,
86
+ type: "api-key",
87
+ data: { apiKey: fileAuth.OPENAI_API_KEY },
88
+ };
89
+ }
90
+ if (fileAuth?.tokens) {
91
+ return { agent: AGENT_ID, type: "oauth", data: fileAuth };
92
+ }
93
+ return undefined;
94
+ }
95
+ /** Extract raw credentials from first available source */
96
+ function extractRawCredentials() {
97
+ return (extractEnvironmentCredentials() ??
98
+ extractKeychainCredentials() ??
99
+ extractFileCredentials());
100
+ }
101
+ export { checkAuth, extractRawCredentials };