axauth 1.8.0 → 1.9.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
@@ -253,7 +253,7 @@ src/
253
253
 
254
254
  ## Related Packages
255
255
 
256
- axauth is part of the [a╳point](https://axpoint.dev) ecosystem:
256
+ axauth is part of the [a╳kit](https://axkit.dev) ecosystem:
257
257
 
258
258
  - **axshared** - Shared types and agent metadata
259
259
  - **axconfig** - Permission and configuration management
@@ -111,6 +111,18 @@ interface AdapterCapabilities {
111
111
  *
112
112
  * Encapsulates all auth operations for a single agent, hiding the
113
113
  * complexity of different storage mechanisms and credential formats.
114
+ *
115
+ * ## Credential Source Priority
116
+ *
117
+ * When resolving credentials, sources are checked in this order:
118
+ *
119
+ * 1. **Environment variables** (via `extractFromEnvironment`)
120
+ * 2. **Vault** (external, via axvault server when configured)
121
+ * 3. **Local storage** (keychain/file via `extractRawCredentials`)
122
+ *
123
+ * Environment variables always take precedence to allow easy overrides
124
+ * in CI/CD and development. Vault provides centralized credential
125
+ * management. Local storage is the fallback for interactive use.
114
126
  */
115
127
  interface AuthAdapter {
116
128
  /** Agent identifier */
@@ -124,6 +136,19 @@ interface AuthAdapter {
124
136
  * and returns the status without making API calls.
125
137
  */
126
138
  checkAuth(): AuthStatus;
139
+ /**
140
+ * Extract credentials from environment variables only.
141
+ *
142
+ * This is called before checking vault or local storage to ensure
143
+ * environment variables have highest priority. Returns undefined if
144
+ * no credentials are set via environment variables.
145
+ *
146
+ * @example
147
+ * // For Claude, checks CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY
148
+ * const envCreds = adapter.extractFromEnvironment?.();
149
+ * if (envCreds) return envCreds; // Env vars win over vault
150
+ */
151
+ extractFromEnvironment?(): Credentials | undefined;
127
152
  /**
128
153
  * Extract raw credentials for export.
129
154
  *
@@ -18,9 +18,19 @@ declare function checkAuth(): {
18
18
  method?: string;
19
19
  details?: Record<string, unknown>;
20
20
  };
21
+ /**
22
+ * Extract credentials from environment variables only.
23
+ *
24
+ * This is called before checking vault or local storage to ensure
25
+ * environment variables have highest priority.
26
+ */
27
+ declare function extractFromEnvironment(): {
28
+ type: "oauth-credentials" | "oauth-token" | "api-key";
29
+ data: Record<string, unknown>;
30
+ } | undefined;
21
31
  /** Extract raw credentials from available sources */
22
32
  declare function extractRawCredentials(): {
23
- type: "oauth" | "api-key";
33
+ type: "oauth-credentials" | "oauth-token" | "api-key";
24
34
  data: Record<string, unknown>;
25
35
  } | undefined;
26
- export { AGENT_ID, checkAuth, deleteFileCreds, deleteKeychainCreds, extractRawCredentials, getDefaultCredsFilePath, saveFileCreds, saveKeychainCreds, };
36
+ export { AGENT_ID, checkAuth, deleteFileCreds, deleteKeychainCreds, extractFromEnvironment, extractRawCredentials, getDefaultCredsFilePath, saveFileCreds, saveKeychainCreds, };
@@ -81,34 +81,57 @@ function checkAuth() {
81
81
  }
82
82
  return { authenticated: false };
83
83
  }
84
- /** Extract raw credentials from available sources */
85
- function extractRawCredentials() {
84
+ /**
85
+ * Extract credentials from environment variables only.
86
+ *
87
+ * This is called before checking vault or local storage to ensure
88
+ * environment variables have highest priority.
89
+ */
90
+ function extractFromEnvironment() {
86
91
  if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
87
92
  return {
88
- type: "oauth",
93
+ type: "oauth-token",
89
94
  data: { accessToken: process.env.CLAUDE_CODE_OAUTH_TOKEN },
90
95
  };
91
96
  }
97
+ if (process.env.ANTHROPIC_API_KEY) {
98
+ return {
99
+ type: "api-key",
100
+ data: { apiKey: process.env.ANTHROPIC_API_KEY },
101
+ };
102
+ }
103
+ return undefined;
104
+ }
105
+ /**
106
+ * Extract credentials from local storage only (keychain + file).
107
+ *
108
+ * This skips environment variable checks and only looks at local storage.
109
+ * Used as fallback after vault fetch fails.
110
+ */
111
+ function extractFromLocalStorage() {
92
112
  const keychainCreds = loadKeychainCreds();
93
113
  if (keychainCreds) {
94
114
  return {
95
- type: "oauth",
115
+ type: "oauth-credentials",
96
116
  data: { ...keychainCreds, _source: "keychain" },
97
117
  };
98
118
  }
99
119
  const fileCreds = loadFileCreds();
100
120
  if (fileCreds) {
101
121
  return {
102
- type: "oauth",
122
+ type: "oauth-credentials",
103
123
  data: { ...fileCreds, _source: "file" },
104
124
  };
105
125
  }
106
- if (process.env.ANTHROPIC_API_KEY) {
107
- return {
108
- type: "api-key",
109
- data: { apiKey: process.env.ANTHROPIC_API_KEY },
110
- };
111
- }
112
126
  return undefined;
113
127
  }
114
- export { AGENT_ID, checkAuth, deleteFileCreds, deleteKeychainCreds, extractRawCredentials, getDefaultCredsFilePath, saveFileCreds, saveKeychainCreds, };
128
+ /** Extract raw credentials from available sources */
129
+ function extractRawCredentials() {
130
+ // Check env vars first
131
+ const environmentCreds = extractFromEnvironment();
132
+ if (environmentCreds)
133
+ return environmentCreds;
134
+ // Fall back to local storage
135
+ return extractFromLocalStorage();
136
+ }
137
+ export { AGENT_ID, checkAuth, deleteFileCreds, deleteKeychainCreds, extractFromEnvironment, extractRawCredentials, getDefaultCredsFilePath, saveFileCreds, saveKeychainCreds, };
@@ -7,7 +7,7 @@ import path from "node:path";
7
7
  import { extractCredsFromDirectory } from "../extract-creds-from-directory.js";
8
8
  import { isMacOS } from "../keychain.js";
9
9
  import { resolveCustomDirectory } from "../validate-directories.js";
10
- import { AGENT_ID, checkAuth, deleteFileCreds, deleteKeychainCreds, extractRawCredentials, getDefaultCredsFilePath, saveFileCreds, saveKeychainCreds, } from "./claude-storage.js";
10
+ import { AGENT_ID, checkAuth, deleteFileCreds, deleteKeychainCreds, extractFromEnvironment, extractRawCredentials, getDefaultCredsFilePath, saveFileCreds, saveKeychainCreds, } from "./claude-storage.js";
11
11
  const CREDS_FILE_NAME = ".credentials.json";
12
12
  /** Claude Code authentication adapter */
13
13
  const claudeCodeAdapter = {
@@ -22,6 +22,12 @@ const claudeCodeAdapter = {
22
22
  const result = checkAuth();
23
23
  return { agentId: AGENT_ID, ...result };
24
24
  },
25
+ extractFromEnvironment() {
26
+ const result = extractFromEnvironment();
27
+ if (!result)
28
+ return undefined;
29
+ return { agent: AGENT_ID, ...result };
30
+ },
25
31
  extractRawCredentials() {
26
32
  const result = extractRawCredentials();
27
33
  if (!result)
@@ -121,7 +127,8 @@ const claudeCodeAdapter = {
121
127
  },
122
128
  getAccessToken(creds) {
123
129
  const data = creds.data;
124
- if (creds.type === "oauth" && typeof data.accessToken === "string") {
130
+ if ((creds.type === "oauth-credentials" || creds.type === "oauth-token") &&
131
+ typeof data.accessToken === "string") {
125
132
  return data.accessToken;
126
133
  }
127
134
  if (creds.type === "api-key" && typeof data.apiKey === "string") {
@@ -132,7 +139,8 @@ const claudeCodeAdapter = {
132
139
  credentialsToEnvironment(creds) {
133
140
  const environment = {};
134
141
  const data = creds.data;
135
- if (creds.type === "oauth" && typeof data.accessToken === "string") {
142
+ if ((creds.type === "oauth-credentials" || creds.type === "oauth-token") &&
143
+ typeof data.accessToken === "string") {
136
144
  environment.CLAUDE_CODE_OAUTH_TOKEN = data.accessToken;
137
145
  }
138
146
  else if (creds.type === "api-key" && typeof data.apiKey === "string") {
@@ -4,6 +4,8 @@
4
4
  import type { AuthStatus, Credentials } from "../types.js";
5
5
  /** Check auth status across all sources */
6
6
  declare function checkAuth(): AuthStatus;
7
+ /** Extract credentials from environment */
8
+ declare function extractEnvironmentCredentials(): Credentials | undefined;
7
9
  /** Extract raw credentials from first available source */
8
10
  declare function extractRawCredentials(): Credentials | undefined;
9
- export { checkAuth, extractRawCredentials };
11
+ export { checkAuth, extractEnvironmentCredentials, extractRawCredentials };
@@ -71,7 +71,7 @@ function extractKeychainCredentials() {
71
71
  if (keychainAuth?.tokens) {
72
72
  return {
73
73
  agent: AGENT_ID,
74
- type: "oauth",
74
+ type: "oauth-credentials",
75
75
  data: { ...keychainAuth, _source: "keychain" },
76
76
  };
77
77
  }
@@ -88,7 +88,7 @@ function extractFileCredentials() {
88
88
  };
89
89
  }
90
90
  if (fileAuth?.tokens) {
91
- return { agent: AGENT_ID, type: "oauth", data: fileAuth };
91
+ return { agent: AGENT_ID, type: "oauth-credentials", data: fileAuth };
92
92
  }
93
93
  return undefined;
94
94
  }
@@ -98,4 +98,4 @@ function extractRawCredentials() {
98
98
  extractKeychainCredentials() ??
99
99
  extractFileCredentials());
100
100
  }
101
- export { checkAuth, extractRawCredentials };
101
+ export { checkAuth, extractEnvironmentCredentials, extractRawCredentials };
@@ -39,5 +39,5 @@ interface CredentialsData {
39
39
  [key: string]: unknown;
40
40
  }
41
41
  /** Build auth content from credentials data */
42
- declare function buildAuthContent(type: "oauth" | "api-key", data: CredentialsData): CodexAuthJson;
42
+ declare function buildAuthContent(type: "oauth-credentials" | "oauth-token" | "api-key", data: CredentialsData): CodexAuthJson;
43
43
  export { buildAuthContent, deleteFileCreds, deleteKeychainCreds, getEnvironmentApiKey, hasFileApiKey, hasFileOAuth, hasKeychainApiKey, hasKeychainOAuth, loadFileCreds, loadKeychainCreds, saveFileCreds, saveKeychainCreds, };
@@ -8,7 +8,7 @@ import path from "node:path";
8
8
  import { ensureDirectory } from "../file-storage.js";
9
9
  import { isMacOS } from "../keychain.js";
10
10
  import { resolveCustomDirectory } from "../validate-directories.js";
11
- import { checkAuth, extractRawCredentials } from "./codex-auth-check.js";
11
+ import { checkAuth, extractEnvironmentCredentials, extractRawCredentials, } from "./codex-auth-check.js";
12
12
  import { getAuthFilePath, getCodexHome, updateConfigStorage, } from "./codex-config.js";
13
13
  import { buildAuthContent, deleteFileCreds, deleteKeychainCreds, saveFileCreds, saveKeychainCreds, } from "./codex-storage.js";
14
14
  const AGENT_ID = "codex";
@@ -23,6 +23,7 @@ const codexAdapter = {
23
23
  installApiKey: true,
24
24
  },
25
25
  checkAuth,
26
+ extractFromEnvironment: extractEnvironmentCredentials,
26
27
  extractRawCredentials,
27
28
  extractRawCredentialsFromDirectory(options) {
28
29
  // Codex doesn't separate config/data - use either directory
@@ -38,7 +39,7 @@ const codexAdapter = {
38
39
  return undefined;
39
40
  const data = parsed;
40
41
  // Determine type from data structure
41
- const type = data.api_key ? "api-key" : "oauth";
42
+ const type = data.api_key ? "api-key" : "oauth-credentials";
42
43
  return { agent: AGENT_ID, type, data };
43
44
  }
44
45
  catch {
@@ -133,7 +134,7 @@ const codexAdapter = {
133
134
  if (creds.type === "api-key" && typeof data.apiKey === "string") {
134
135
  return data.apiKey;
135
136
  }
136
- if (creds.type === "oauth") {
137
+ if (creds.type === "oauth-credentials") {
137
138
  // Direct access_token
138
139
  if (typeof data.access_token === "string") {
139
140
  return data.access_token;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copilot credential installation helpers.
3
+ *
4
+ * Extracted from copilot.ts to reduce file complexity.
5
+ */
6
+ import type { InstallOptions, OperationResult } from "../adapter.js";
7
+ import type { Credentials } from "../types.js";
8
+ import type { ResolveResult } from "../validate-directories.js";
9
+ declare const CREDS_FILE_NAME = "token.json";
10
+ /**
11
+ * Install Copilot credentials.
12
+ *
13
+ * Handles keychain vs file storage and custom directories.
14
+ */
15
+ declare function installCopilotCredentials(creds: Credentials, resolved: Extract<ResolveResult, {
16
+ ok: true;
17
+ }>, options?: InstallOptions): OperationResult;
18
+ export { CREDS_FILE_NAME, installCopilotCredentials };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copilot credential installation helpers.
3
+ *
4
+ * Extracted from copilot.ts to reduce file complexity.
5
+ */
6
+ import path from "node:path";
7
+ import { ensureDirectory } from "../file-storage.js";
8
+ import { isMacOS } from "../keychain.js";
9
+ import { getConfigDirectory, getConfigFilePath, saveFileToken, saveKeychainToken, saveTokenToPath, } from "./copilot-storage.js";
10
+ const CREDS_FILE_NAME = "token.json";
11
+ /**
12
+ * Install Copilot credentials.
13
+ *
14
+ * Handles keychain vs file storage and custom directories.
15
+ */
16
+ function installCopilotCredentials(creds, resolved, options) {
17
+ if (creds.type === "api-key") {
18
+ return {
19
+ ok: false,
20
+ message: "API key credentials cannot be installed (use COPILOT_GITHUB_TOKEN env var)",
21
+ };
22
+ }
23
+ const token = typeof creds.data.accessToken === "string"
24
+ ? creds.data.accessToken
25
+ : undefined;
26
+ if (!token) {
27
+ return { ok: false, message: "No access token found in credentials" };
28
+ }
29
+ // Custom directory forces file storage
30
+ if (resolved.customDir) {
31
+ const targetPath = path.join(resolved.customDir, CREDS_FILE_NAME);
32
+ if (saveTokenToPath(token, targetPath)) {
33
+ return {
34
+ ok: true,
35
+ message: `Installed credentials to ${targetPath}`,
36
+ };
37
+ }
38
+ return {
39
+ ok: false,
40
+ message: `Failed to install credentials to ${targetPath}`,
41
+ };
42
+ }
43
+ // Default location: use storage option or _source marker
44
+ const sourceMarker = creds.data._source;
45
+ const targetStorage = options?.storage ?? (sourceMarker === "keychain" ? "keychain" : "file");
46
+ if (targetStorage === "keychain") {
47
+ if (!isMacOS()) {
48
+ return {
49
+ ok: false,
50
+ message: "Keychain storage is only available on macOS",
51
+ };
52
+ }
53
+ if (saveKeychainToken(token)) {
54
+ return { ok: true, message: "Installed credentials to macOS Keychain" };
55
+ }
56
+ return { ok: false, message: "Failed to save to macOS Keychain" };
57
+ }
58
+ // File storage
59
+ if (!ensureDirectory(getConfigDirectory())) {
60
+ return {
61
+ ok: false,
62
+ message: "Failed to create copilot config directory",
63
+ };
64
+ }
65
+ if (saveFileToken(token)) {
66
+ return {
67
+ ok: true,
68
+ message: `Installed credentials to ${getConfigFilePath()}`,
69
+ };
70
+ }
71
+ return { ok: false, message: "Failed to install credentials to file" };
72
+ }
73
+ export { CREDS_FILE_NAME, installCopilotCredentials };
@@ -6,13 +6,11 @@
6
6
  */
7
7
  import path from "node:path";
8
8
  import { extractCredsFromDirectory } from "../extract-creds-from-directory.js";
9
- import { ensureDirectory } from "../file-storage.js";
10
- import { isMacOS } from "../keychain.js";
11
9
  import { resolveCustomDirectory } from "../validate-directories.js";
12
10
  import { findFirstAvailableToken } from "./copilot-auth-check.js";
13
- import { deleteFileToken, deleteKeychainToken, deleteTokenFromPath, getConfigDirectory, getConfigFilePath, saveFileToken, saveKeychainToken, saveTokenToPath, } from "./copilot-storage.js";
11
+ import { CREDS_FILE_NAME, installCopilotCredentials, } from "./copilot-install.js";
12
+ import { deleteFileToken, deleteKeychainToken, deleteTokenFromPath, getConfigFilePath, getEnvironmentToken, } from "./copilot-storage.js";
14
13
  const AGENT_ID = "copilot";
15
- const CREDS_FILE_NAME = "token.json";
16
14
  /** Copilot CLI authentication adapter */
17
15
  const copilotAdapter = {
18
16
  agentId: AGENT_ID,
@@ -39,13 +37,23 @@ const copilotAdapter = {
39
37
  method: methodMap[result.source],
40
38
  };
41
39
  },
40
+ extractFromEnvironment() {
41
+ const token = getEnvironmentToken();
42
+ if (!token)
43
+ return undefined;
44
+ return {
45
+ agent: AGENT_ID,
46
+ type: "oauth-token",
47
+ data: { accessToken: token },
48
+ };
49
+ },
42
50
  extractRawCredentials() {
43
51
  const result = findFirstAvailableToken();
44
52
  if (!result)
45
53
  return undefined;
46
54
  return {
47
55
  agent: AGENT_ID,
48
- type: "oauth",
56
+ type: "oauth-token",
49
57
  data: { accessToken: result.token, _source: result.source },
50
58
  };
51
59
  },
@@ -57,65 +65,10 @@ const copilotAdapter = {
57
65
  return extractCredsFromDirectory(AGENT_ID, directory, CREDS_FILE_NAME);
58
66
  },
59
67
  installCredentials(creds, options) {
60
- // Resolve custom directory with validation
61
68
  const resolved = resolveCustomDirectory(AGENT_ID, options?.configDir, options?.dataDir);
62
69
  if (!resolved.ok)
63
70
  return resolved.error;
64
- if (creds.type === "api-key") {
65
- return {
66
- ok: false,
67
- message: "API key credentials cannot be installed (use COPILOT_GITHUB_TOKEN env var)",
68
- };
69
- }
70
- const token = typeof creds.data.accessToken === "string"
71
- ? creds.data.accessToken
72
- : undefined;
73
- if (!token) {
74
- return { ok: false, message: "No access token found in credentials" };
75
- }
76
- // Custom directory forces file storage
77
- if (resolved.customDir) {
78
- const targetPath = path.join(resolved.customDir, CREDS_FILE_NAME);
79
- if (saveTokenToPath(token, targetPath)) {
80
- return {
81
- ok: true,
82
- message: `Installed credentials to ${targetPath}`,
83
- };
84
- }
85
- return {
86
- ok: false,
87
- message: `Failed to install credentials to ${targetPath}`,
88
- };
89
- }
90
- // Default location: use storage option or _source marker
91
- const sourceMarker = creds.data._source;
92
- const targetStorage = options?.storage ?? (sourceMarker === "keychain" ? "keychain" : "file");
93
- if (targetStorage === "keychain") {
94
- if (!isMacOS()) {
95
- return {
96
- ok: false,
97
- message: "Keychain storage is only available on macOS",
98
- };
99
- }
100
- if (saveKeychainToken(token)) {
101
- return { ok: true, message: "Installed credentials to macOS Keychain" };
102
- }
103
- return { ok: false, message: "Failed to save to macOS Keychain" };
104
- }
105
- // File storage
106
- if (!ensureDirectory(getConfigDirectory())) {
107
- return {
108
- ok: false,
109
- message: "Failed to create copilot config directory",
110
- };
111
- }
112
- if (saveFileToken(token)) {
113
- return {
114
- ok: true,
115
- message: `Installed credentials to ${getConfigFilePath()}`,
116
- };
117
- }
118
- return { ok: false, message: "Failed to install credentials to file" };
71
+ return installCopilotCredentials(creds, resolved, options);
119
72
  },
120
73
  removeCredentials(options) {
121
74
  // Resolve custom directory with validation
@@ -11,6 +11,8 @@ interface CredentialResult {
11
11
  method: string;
12
12
  data?: Record<string, unknown>;
13
13
  }
14
+ /** Check environment variable credentials (API key and Vertex AI) */
15
+ declare function checkEnvironmentAuth(): CredentialResult | undefined;
14
16
  /** Find credentials using priority-ordered discovery: env → keychain → file */
15
17
  declare function findCredentials(): CredentialResult | undefined;
16
- export { findCredentials };
18
+ export { checkEnvironmentAuth, findCredentials };
@@ -57,4 +57,4 @@ function checkFileAuth() {
57
57
  function findCredentials() {
58
58
  return checkEnvironmentAuth() ?? checkKeychainAuth() ?? checkFileAuth();
59
59
  }
60
- export { findCredentials };
60
+ export { checkEnvironmentAuth, findCredentials };
@@ -4,7 +4,7 @@
4
4
  * Supports OAuth via keychain (macOS) or file. API key auth is env-var only.
5
5
  */
6
6
  import { extractCredsFromDirectory } from "../extract-creds-from-directory.js";
7
- import { findCredentials } from "./gemini-auth-check.js";
7
+ import { checkEnvironmentAuth, findCredentials } from "./gemini-auth-check.js";
8
8
  import { installOAuthCredentials, removeGeminiCredentials, } from "./gemini-install.js";
9
9
  const AGENT_ID = "gemini";
10
10
  /** Gemini authentication adapter */
@@ -27,6 +27,16 @@ const geminiAdapter = {
27
27
  method: result.method,
28
28
  };
29
29
  },
30
+ extractFromEnvironment() {
31
+ const result = checkEnvironmentAuth();
32
+ if (!result || !result.data)
33
+ return undefined;
34
+ return {
35
+ agent: AGENT_ID,
36
+ type: "api-key",
37
+ data: result.data,
38
+ };
39
+ },
30
40
  extractRawCredentials() {
31
41
  const result = findCredentials();
32
42
  if (!result || !result.data)
@@ -42,7 +52,7 @@ const geminiAdapter = {
42
52
  // OAuth credentials from keychain or file include _source for round-tripping
43
53
  return {
44
54
  agent: AGENT_ID,
45
- type: "oauth",
55
+ type: "oauth-credentials",
46
56
  data: {
47
57
  ...result.data,
48
58
  _source: result.source === "keychain" ? "keychain" : "file",
@@ -74,7 +84,8 @@ const geminiAdapter = {
74
84
  if (creds.type === "api-key" && typeof data.apiKey === "string") {
75
85
  return data.apiKey;
76
86
  }
77
- if (creds.type === "oauth" && typeof data.access_token === "string") {
87
+ if (creds.type === "oauth-credentials" &&
88
+ typeof data.access_token === "string") {
78
89
  return data.access_token;
79
90
  }
80
91
  return undefined;
@@ -77,7 +77,7 @@ const opencodeAdapter = {
77
77
  const auth = readAuthFile(getDefaultAuthFilePath());
78
78
  if (!auth || Object.keys(auth).length === 0)
79
79
  return undefined;
80
- return { agent: AGENT_ID, type: "oauth", data: auth };
80
+ return { agent: AGENT_ID, type: "oauth-credentials", data: auth };
81
81
  },
82
82
  extractRawCredentialsFromDirectory(options) {
83
83
  // OpenCode stores credentials in dataDir only - configDir is for settings
@@ -28,7 +28,7 @@ function extractCredsFromDirectory(agentId, directory, fileName, transform) {
28
28
  const data = transform ? transform(record) : record;
29
29
  if (!data)
30
30
  return undefined;
31
- return { agent: agentId, type: "oauth", data };
31
+ return { agent: agentId, type: "oauth-credentials", data };
32
32
  }
33
33
  catch {
34
34
  return undefined;
@@ -51,9 +51,17 @@ declare function checkAllAuth(): AuthStatus[];
51
51
  /**
52
52
  * Extract raw credentials for an agent.
53
53
  *
54
+ * Checks credentials from all available sources:
55
+ * - Environment variables
56
+ * - Keychain (macOS)
57
+ * - File storage
58
+ *
54
59
  * Note: The `data` field format is agent-specific and not standardized.
55
60
  * Use {@link getAccessToken} to extract the token in a uniform way.
56
61
  *
62
+ * For vault credentials, use {@link fetchVaultCredentials} from the vault module
63
+ * or the `axauth vault fetch` CLI command.
64
+ *
57
65
  * @example
58
66
  * const creds = extractRawCredentials("codex");
59
67
  * if (creds) { await exportCredentials(creds); }
@@ -75,9 +75,17 @@ function checkAllAuth() {
75
75
  /**
76
76
  * Extract raw credentials for an agent.
77
77
  *
78
+ * Checks credentials from all available sources:
79
+ * - Environment variables
80
+ * - Keychain (macOS)
81
+ * - File storage
82
+ *
78
83
  * Note: The `data` field format is agent-specific and not standardized.
79
84
  * Use {@link getAccessToken} to extract the token in a uniform way.
80
85
  *
86
+ * For vault credentials, use {@link fetchVaultCredentials} from the vault module
87
+ * or the `axauth vault fetch` CLI command.
88
+ *
81
89
  * @example
82
90
  * const creds = extractRawCredentials("codex");
83
91
  * if (creds) { await exportCredentials(creds); }
@@ -21,8 +21,9 @@ declare const Credentials: z.ZodObject<{
21
21
  copilot: "copilot";
22
22
  }>;
23
23
  type: z.ZodEnum<{
24
- oauth: "oauth";
25
24
  "api-key": "api-key";
25
+ "oauth-token": "oauth-token";
26
+ "oauth-credentials": "oauth-credentials";
26
27
  }>;
27
28
  data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
28
29
  }, z.core.$strip>;
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Shared types for auth module.
3
3
  */
4
- import { AGENT_CLIS } from "axshared";
4
+ import { AGENT_CLIS, CredentialType } from "axshared";
5
5
  import { z } from "zod";
6
6
  /** Zod schema for AgentCli */
7
7
  const AgentCliSchema = z.enum(AGENT_CLIS);
8
8
  /** Extracted credential data schema */
9
9
  const Credentials = z.object({
10
10
  agent: AgentCliSchema,
11
- type: z.enum(["oauth", "api-key"]),
11
+ type: CredentialType,
12
12
  data: z.record(z.string(), z.unknown()),
13
13
  });
14
14
  /**
@@ -22,4 +22,5 @@ type ResolveResult = {
22
22
  * @see resolveCustomDirectories in axshared for behavior details
23
23
  */
24
24
  declare function resolveCustomDirectory(agentId: AgentCli, configDirectory: string | undefined, dataDirectory: string | undefined): ResolveResult;
25
+ export type { ResolveResult };
25
26
  export { resolveCustomDirectory };
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@
7
7
  import { Command } from "@commander-js/extra-typings";
8
8
  import packageJson from "../package.json" with { type: "json" };
9
9
  import { handleAuthExport, handleAuthInstall, handleAuthList, handleAuthRemove, handleAuthToken, } from "./commands/auth.js";
10
+ import { handleVaultFetch } from "./commands/vault.js";
10
11
  import { AGENT_CLIS } from "axshared";
11
12
  // Handle SIGINT gracefully
12
13
  process.on("SIGINT", () => {
@@ -38,7 +39,11 @@ Examples:
38
39
  axauth remove-credentials --agent claude
39
40
 
40
41
  # Install credentials from exported file
41
- axauth install-credentials --agent claude --input creds.json`);
42
+ axauth install-credentials --agent claude --input creds.json
43
+
44
+ # Fetch credentials from vault (JSON config)
45
+ AXVAULT='{"url":"https://vault.example.com","apiKey":"axv_sk_xxx"}' \
46
+ axauth vault fetch --agent claude --name ci --install`);
42
47
  program
43
48
  .command("list")
44
49
  .description("List agents and their auth status")
@@ -87,4 +92,51 @@ program
87
92
  dataDir: options.dataDir,
88
93
  });
89
94
  });
95
+ // Vault subcommand
96
+ const vault = program
97
+ .command("vault")
98
+ .description("Manage credentials stored in axvault server");
99
+ vault
100
+ .command("fetch")
101
+ .description("Fetch credentials from vault server")
102
+ .requiredOption("-a, --agent <agent>", `Agent to fetch credentials for (${AGENT_CLIS.join(", ")})`)
103
+ .requiredOption("-n, --name <name>", "Credential name in vault (e.g., ci, prod)")
104
+ .option("-i, --install", "Install fetched credentials to local storage")
105
+ .option("-e, --env", "Output shell export commands for environment variables")
106
+ .option("--config-dir <dir>", "Custom config directory (for agents without separation)")
107
+ .option("--data-dir <dir>", "Custom data directory for credentials")
108
+ .option("--json", "Pretty-print JSON output")
109
+ .addHelpText("after", `
110
+ Environment variables (option 1 - single JSON):
111
+ AXVAULT JSON config: {"url":"...","apiKey":"...","credentialName":"..."}
112
+
113
+ Environment variables (option 2 - individual):
114
+ AXVAULT_URL Vault server URL (required)
115
+ AXVAULT_API_KEY API key for authentication (required)
116
+ AXVAULT_CREDENTIAL Default credential name (optional)
117
+
118
+ Output modes:
119
+ (default) Output credentials as JSON
120
+ --install Write credentials to local storage (file/keychain)
121
+ --env Output shell export commands for environment variables
122
+
123
+ Examples:
124
+ # Fetch and output credentials as JSON
125
+ axauth vault fetch --agent claude --name ci
126
+
127
+ # Fetch and install OAuth credentials locally
128
+ axauth vault fetch --agent claude --name ci --install
129
+
130
+ # Fetch and set environment variables (works for all credential types)
131
+ eval "$(axauth vault fetch --agent claude --name ci --env)"
132
+
133
+ # GitHub Actions: append to $GITHUB_ENV
134
+ axauth vault fetch --agent claude --name ci --env | sed 's/^export //' >> $GITHUB_ENV`)
135
+ .action((options) => {
136
+ void handleVaultFetch({
137
+ ...options,
138
+ configDir: options.configDir,
139
+ dataDir: options.dataDir,
140
+ });
141
+ });
90
142
  await program.parseAsync(process.argv);
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Vault commands - fetch credentials from axvault server.
3
+ */
4
+ interface VaultFetchOptions {
5
+ agent: string;
6
+ name: string;
7
+ install?: boolean;
8
+ env?: boolean;
9
+ configDir?: string;
10
+ dataDir?: string;
11
+ json?: boolean;
12
+ }
13
+ /**
14
+ * Handle vault fetch command.
15
+ *
16
+ * Fetches credentials from the vault server and either:
17
+ * - Outputs them as JSON (default) for piping to other commands
18
+ * - Installs them locally with --install flag (writes to file)
19
+ * - Outputs shell export commands with --env flag (for eval/source)
20
+ */
21
+ declare function handleVaultFetch(options: VaultFetchOptions): Promise<void>;
22
+ export { handleVaultFetch };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Vault commands - fetch credentials from axvault server.
3
+ */
4
+ import { credentialsToEnvironment, installCredentials, } from "../auth/registry.js";
5
+ import { fetchVaultCredentials, } from "../vault/vault-client.js";
6
+ import { getVaultConfig } from "../vault/vault-config.js";
7
+ import { validateAgent } from "./validate-agent.js";
8
+ /** Human-readable error messages for vault failures */
9
+ const FAILURE_MESSAGES = {
10
+ "not-configured": "Vault not configured. Set AXVAULT (JSON) or AXVAULT_URL + AXVAULT_API_KEY.",
11
+ unreachable: "Vault server is unreachable. Check vault URL and network.",
12
+ unauthorized: "Invalid API key. Check apiKey in vault config.",
13
+ forbidden: "Access denied. API key doesn't have read access to this credential.",
14
+ "not-found": "Credential not found in vault.",
15
+ "legacy-credential": "Credential uses legacy encryption. Re-upload it to vault.",
16
+ "client-error": "Invalid request. Check credential name and try again.",
17
+ "server-error": "Vault server error. Check server logs.",
18
+ };
19
+ /**
20
+ * Escape a value for safe use in shell export statement.
21
+ * Uses single quotes and escapes any single quotes in the value.
22
+ *
23
+ * @throws Error if value contains newlines or null bytes (cannot be safely exported)
24
+ */
25
+ function shellEscape(value) {
26
+ if (value.includes("\n") || value.includes("\0")) {
27
+ throw new Error("Credential values cannot contain newlines or null bytes");
28
+ }
29
+ // Replace single quotes with '\'' (end quote, escaped quote, start quote)
30
+ return `'${value.replaceAll("'", String.raw `'\''`)}'`;
31
+ }
32
+ /**
33
+ * Handle vault fetch command.
34
+ *
35
+ * Fetches credentials from the vault server and either:
36
+ * - Outputs them as JSON (default) for piping to other commands
37
+ * - Installs them locally with --install flag (writes to file)
38
+ * - Outputs shell export commands with --env flag (for eval/source)
39
+ */
40
+ async function handleVaultFetch(options) {
41
+ // Validate agent
42
+ const agentId = validateAgent(options.agent);
43
+ if (!agentId)
44
+ return;
45
+ // Validate mutually exclusive options
46
+ if (options.install && options.env) {
47
+ console.error("Error: --install and --env are mutually exclusive");
48
+ process.exitCode = 2;
49
+ return;
50
+ }
51
+ // Check vault is configured
52
+ const vaultConfig = getVaultConfig();
53
+ if (!vaultConfig) {
54
+ console.error(`Error: ${FAILURE_MESSAGES["not-configured"]}`);
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ // Fetch from vault
59
+ const result = await fetchVaultCredentials({
60
+ agentId,
61
+ name: options.name,
62
+ });
63
+ if (!result.ok) {
64
+ console.error(`Error: ${FAILURE_MESSAGES[result.reason]}`);
65
+ process.exitCode = 1;
66
+ return;
67
+ }
68
+ const { credentials, refreshed } = result;
69
+ // Log refresh status to stderr (not stdout, to allow piping)
70
+ if (refreshed) {
71
+ console.error("Note: Credentials were auto-refreshed by vault");
72
+ }
73
+ if (options.env) {
74
+ // Output shell export commands (for eval/source)
75
+ const environmentVariables = credentialsToEnvironment(credentials);
76
+ const environmentEntries = Object.entries(environmentVariables);
77
+ if (environmentEntries.length === 0) {
78
+ console.error("Warning: No environment variables to export for this credential type");
79
+ return;
80
+ }
81
+ try {
82
+ for (const [key, value] of environmentEntries) {
83
+ console.log(`export ${key}=${shellEscape(value)}`);
84
+ }
85
+ }
86
+ catch (error) {
87
+ console.error(`Error: ${error instanceof Error ? error.message : "Failed to escape credential value"}`);
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+ }
92
+ else if (options.install) {
93
+ // Install credentials locally (writes to file)
94
+ const installResult = installCredentials(credentials, {
95
+ configDir: options.configDir,
96
+ dataDir: options.dataDir,
97
+ });
98
+ if (!installResult.ok) {
99
+ // For env-only credentials, suggest using --env instead
100
+ if (installResult.message.includes("cannot be installed")) {
101
+ console.error(`Error: ${installResult.message}`);
102
+ console.error("Hint: Use --env to get shell export commands instead");
103
+ }
104
+ else {
105
+ console.error(`Error: ${installResult.message}`);
106
+ }
107
+ process.exitCode = 1;
108
+ return;
109
+ }
110
+ console.error(installResult.message);
111
+ }
112
+ else {
113
+ // Output credentials as JSON (default)
114
+ if (options.json) {
115
+ console.log(JSON.stringify(credentials, undefined, 2));
116
+ }
117
+ else {
118
+ // Compact JSON for piping
119
+ console.log(JSON.stringify(credentials));
120
+ }
121
+ }
122
+ }
123
+ export { handleVaultFetch };
package/dist/index.d.ts CHANGED
@@ -35,5 +35,7 @@ export { AGENT_CLIS } from "axshared";
35
35
  export type { AdapterCapabilities, AuthAdapter, InstallOptions, OperationResult, RemoveOptions, } from "./auth/adapter.js";
36
36
  export type { AuthStatus, Credentials } from "./auth/types.js";
37
37
  export { checkAllAuth, checkAuth, credentialsToEnvironment, extractRawCredentials, extractRawCredentialsFromDirectory, getAccessToken, getAgentAccessToken, getCredentialsEnvironmentVariableName, installCredentials, installCredentialsFromEnvironmentVariable, removeCredentials, getAdapter, getAllAdapters, getCapabilities, type ExtractOptions, type InstallFromEnvironmentOptions, } from "./auth/registry.js";
38
+ export { getVaultConfig, isVaultConfigured, type VaultConfig, } from "./vault/vault-config.js";
39
+ export { fetchVaultCredentials, type VaultFailureReason, type VaultFetchOptions, type VaultResult, } from "./vault/vault-client.js";
38
40
  export { isCredentialExpired, isTokenExpired, } from "./auth/is-token-expired.js";
39
41
  export { refreshAndPersist, refreshCredentials, type RefreshAndPersistResult, type RefreshOptions, type RefreshResult, } from "./auth/refresh-credentials.js";
package/dist/index.js CHANGED
@@ -37,6 +37,9 @@ export {
37
37
  checkAllAuth, checkAuth, credentialsToEnvironment, extractRawCredentials, extractRawCredentialsFromDirectory, getAccessToken, getAgentAccessToken, getCredentialsEnvironmentVariableName, installCredentials, installCredentialsFromEnvironmentVariable, removeCredentials,
38
38
  // Adapter access
39
39
  getAdapter, getAllAdapters, getCapabilities, } from "./auth/registry.js";
40
+ // Vault utilities
41
+ export { getVaultConfig, isVaultConfigured, } from "./vault/vault-config.js";
42
+ export { fetchVaultCredentials, } from "./vault/vault-client.js";
40
43
  // Token refresh utilities
41
44
  export { isCredentialExpired, isTokenExpired, } from "./auth/is-token-expired.js";
42
45
  export { refreshAndPersist, refreshCredentials, } from "./auth/refresh-credentials.js";
@@ -0,0 +1,48 @@
1
+ /**
2
+ * HTTP client for fetching credentials from axvault server.
3
+ *
4
+ * The vault client provides a simple interface to fetch credentials from
5
+ * a remote axvault server. It handles authentication, error responses,
6
+ * and returns a typed result.
7
+ */
8
+ import type { AgentCli } from "axshared";
9
+ import type { Credentials } from "../auth/types.js";
10
+ /** Reason why vault fetch failed */
11
+ type VaultFailureReason = "not-configured" | "unreachable" | "unauthorized" | "forbidden" | "not-found" | "legacy-credential" | "client-error" | "server-error";
12
+ /** Result of a vault fetch operation */
13
+ type VaultResult = {
14
+ ok: true;
15
+ credentials: Credentials;
16
+ refreshed: boolean;
17
+ } | {
18
+ ok: false;
19
+ reason: VaultFailureReason;
20
+ };
21
+ /** Options for fetching from vault */
22
+ interface VaultFetchOptions {
23
+ /** Agent ID (e.g., "claude", "codex") */
24
+ agentId: AgentCli;
25
+ /** Credential name in vault (e.g., "ci", "prod") */
26
+ name: string;
27
+ }
28
+ /**
29
+ * Fetch credentials from the axvault server.
30
+ *
31
+ * This function attempts to fetch credentials for a specific agent and name
32
+ * from the configured vault server. If the vault is not configured, it returns
33
+ * a "not-configured" result without making any network requests.
34
+ *
35
+ * @example
36
+ * const result = await fetchVaultCredentials({ agentId: "claude", name: "ci" });
37
+ * if (result.ok) {
38
+ * console.log("Got credentials:", result.credentials);
39
+ * if (result.refreshed) {
40
+ * console.log("Credentials were auto-refreshed by vault");
41
+ * }
42
+ * } else {
43
+ * console.log("Failed:", result.reason);
44
+ * }
45
+ */
46
+ declare function fetchVaultCredentials(options: VaultFetchOptions): Promise<VaultResult>;
47
+ export type { VaultFailureReason, VaultFetchOptions, VaultResult };
48
+ export { fetchVaultCredentials };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * HTTP client for fetching credentials from axvault server.
3
+ *
4
+ * The vault client provides a simple interface to fetch credentials from
5
+ * a remote axvault server. It handles authentication, error responses,
6
+ * and returns a typed result.
7
+ */
8
+ import { z } from "zod";
9
+ import { getVaultConfig } from "./vault-config.js";
10
+ /** Zod schema for vault API response */
11
+ const VaultCredentialResponse = z.object({
12
+ agent: z.string(),
13
+ name: z.string(),
14
+ type: z.enum(["oauth-credentials", "oauth-token", "api-key"]),
15
+ data: z.record(z.string(), z.unknown()),
16
+ expiresAt: z.string().nullable(),
17
+ updatedAt: z.string(),
18
+ });
19
+ /**
20
+ * Fetch credentials from the axvault server.
21
+ *
22
+ * This function attempts to fetch credentials for a specific agent and name
23
+ * from the configured vault server. If the vault is not configured, it returns
24
+ * a "not-configured" result without making any network requests.
25
+ *
26
+ * @example
27
+ * const result = await fetchVaultCredentials({ agentId: "claude", name: "ci" });
28
+ * if (result.ok) {
29
+ * console.log("Got credentials:", result.credentials);
30
+ * if (result.refreshed) {
31
+ * console.log("Credentials were auto-refreshed by vault");
32
+ * }
33
+ * } else {
34
+ * console.log("Failed:", result.reason);
35
+ * }
36
+ */
37
+ async function fetchVaultCredentials(options) {
38
+ const config = getVaultConfig();
39
+ if (!config) {
40
+ return { ok: false, reason: "not-configured" };
41
+ }
42
+ const url = `${config.url}/api/v1/credentials/${encodeURIComponent(options.agentId)}/${encodeURIComponent(options.name)}`;
43
+ try {
44
+ const response = await fetch(url, {
45
+ method: "GET",
46
+ headers: {
47
+ Authorization: `Bearer ${config.apiKey}`,
48
+ Accept: "application/json",
49
+ "User-Agent": "axauth-vault-client",
50
+ },
51
+ });
52
+ // Handle error responses
53
+ if (response.status === 401) {
54
+ return { ok: false, reason: "unauthorized" };
55
+ }
56
+ if (response.status === 403) {
57
+ return { ok: false, reason: "forbidden" };
58
+ }
59
+ if (response.status === 404) {
60
+ return { ok: false, reason: "not-found" };
61
+ }
62
+ if (response.status === 410) {
63
+ return { ok: false, reason: "legacy-credential" };
64
+ }
65
+ if (response.status >= 500) {
66
+ return { ok: false, reason: "server-error" };
67
+ }
68
+ if (!response.ok) {
69
+ // Other 4xx errors (400 Bad Request, 429 Rate Limited, etc.)
70
+ return { ok: false, reason: "client-error" };
71
+ }
72
+ // Parse and validate response body
73
+ const json = await response.json();
74
+ const parsed = VaultCredentialResponse.safeParse(json);
75
+ if (!parsed.success) {
76
+ return { ok: false, reason: "server-error" };
77
+ }
78
+ const body = parsed.data;
79
+ // Check refresh headers
80
+ const wasRefreshed = response.headers.get("X-Axvault-Refreshed") === "true";
81
+ const refreshFailed = response.headers.get("X-Axvault-Refresh-Failed") === "true";
82
+ // Log warning if refresh failed (stale credentials returned)
83
+ if (refreshFailed) {
84
+ console.error(`[axauth] Vault returned stale credentials for ${options.agentId}/${options.name} (refresh failed)`);
85
+ }
86
+ const credentials = {
87
+ agent: options.agentId,
88
+ type: body.type,
89
+ data: body.data,
90
+ };
91
+ return {
92
+ ok: true,
93
+ credentials,
94
+ refreshed: wasRefreshed,
95
+ };
96
+ }
97
+ catch {
98
+ // Network errors (DNS failure, connection refused, timeout, etc.)
99
+ return { ok: false, reason: "unreachable" };
100
+ }
101
+ }
102
+ export { fetchVaultCredentials };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Vault configuration from environment variables.
3
+ *
4
+ * Vault is an optional credential source. When configured via environment
5
+ * variables, axauth will attempt to fetch credentials from the vault server
6
+ * before falling back to local storage.
7
+ */
8
+ /** Vault configuration parsed from environment */
9
+ interface VaultConfig {
10
+ /** Vault server URL (e.g., "https://vault.example.com") */
11
+ url: string;
12
+ /** API key for authentication */
13
+ apiKey: string;
14
+ /** Default credential name to fetch (e.g., "ci", "prod") */
15
+ credentialName?: string;
16
+ }
17
+ /**
18
+ * Get vault configuration from environment variables.
19
+ *
20
+ * Supports two configuration methods (checked in order):
21
+ *
22
+ * 1. Single JSON env var:
23
+ * - `AXVAULT`: JSON object with url, apiKey, and optional credentialName
24
+ *
25
+ * 2. Individual env vars:
26
+ * - `AXVAULT_URL`: Vault server URL (required)
27
+ * - `AXVAULT_API_KEY`: API key for authentication (required)
28
+ * - `AXVAULT_CREDENTIAL`: Default credential name (optional)
29
+ *
30
+ * @returns Vault configuration if configured, undefined otherwise
31
+ *
32
+ * @example
33
+ * // Option 1: Single JSON env var
34
+ * // AXVAULT='{"url":"https://vault.axkit.dev","apiKey":"axv_sk_xxx","credentialName":"ci"}'
35
+ *
36
+ * // Option 2: Individual env vars
37
+ * // AXVAULT_URL=https://vault.axkit.dev
38
+ * // AXVAULT_API_KEY=axv_sk_xxx
39
+ * // AXVAULT_CREDENTIAL=ci
40
+ *
41
+ * const config = getVaultConfig();
42
+ * if (config) {
43
+ * // Vault is configured, can fetch credentials
44
+ * }
45
+ */
46
+ declare function getVaultConfig(): VaultConfig | undefined;
47
+ /**
48
+ * Check if vault is configured.
49
+ *
50
+ * @returns true if AXVAULT_URL and AXVAULT_API_KEY are both set
51
+ */
52
+ declare function isVaultConfigured(): boolean;
53
+ export type { VaultConfig };
54
+ export { getVaultConfig, isVaultConfigured };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Vault configuration from environment variables.
3
+ *
4
+ * Vault is an optional credential source. When configured via environment
5
+ * variables, axauth will attempt to fetch credentials from the vault server
6
+ * before falling back to local storage.
7
+ */
8
+ import { z } from "zod";
9
+ /** Zod schema for vault config JSON */
10
+ const VaultConfigJson = z.object({
11
+ url: z.string(),
12
+ apiKey: z.string(),
13
+ credentialName: z.string().optional(),
14
+ });
15
+ /**
16
+ * Validate and normalize a vault URL.
17
+ *
18
+ * @returns Normalized URL (trailing slashes removed) or undefined if invalid
19
+ */
20
+ function validateVaultUrl(url) {
21
+ try {
22
+ new URL(url);
23
+ return url.replaceAll(/\/+$/gu, "");
24
+ }
25
+ catch {
26
+ console.error(`Warning: Invalid vault URL "${url}", ignoring`);
27
+ return undefined;
28
+ }
29
+ }
30
+ /**
31
+ * Parse vault config from JSON environment variable.
32
+ *
33
+ * @returns Parsed config or undefined if invalid/not set
34
+ */
35
+ function parseVaultConfigJson() {
36
+ const json = process.env.AXVAULT;
37
+ if (!json)
38
+ return undefined;
39
+ try {
40
+ const parsed = VaultConfigJson.parse(JSON.parse(json));
41
+ const url = validateVaultUrl(parsed.url);
42
+ if (!url)
43
+ return undefined;
44
+ return {
45
+ url,
46
+ apiKey: parsed.apiKey,
47
+ credentialName: parsed.credentialName,
48
+ };
49
+ }
50
+ catch {
51
+ console.error("Warning: AXVAULT env var contains invalid JSON, ignoring");
52
+ return undefined;
53
+ }
54
+ }
55
+ /**
56
+ * Get vault configuration from environment variables.
57
+ *
58
+ * Supports two configuration methods (checked in order):
59
+ *
60
+ * 1. Single JSON env var:
61
+ * - `AXVAULT`: JSON object with url, apiKey, and optional credentialName
62
+ *
63
+ * 2. Individual env vars:
64
+ * - `AXVAULT_URL`: Vault server URL (required)
65
+ * - `AXVAULT_API_KEY`: API key for authentication (required)
66
+ * - `AXVAULT_CREDENTIAL`: Default credential name (optional)
67
+ *
68
+ * @returns Vault configuration if configured, undefined otherwise
69
+ *
70
+ * @example
71
+ * // Option 1: Single JSON env var
72
+ * // AXVAULT='{"url":"https://vault.axkit.dev","apiKey":"axv_sk_xxx","credentialName":"ci"}'
73
+ *
74
+ * // Option 2: Individual env vars
75
+ * // AXVAULT_URL=https://vault.axkit.dev
76
+ * // AXVAULT_API_KEY=axv_sk_xxx
77
+ * // AXVAULT_CREDENTIAL=ci
78
+ *
79
+ * const config = getVaultConfig();
80
+ * if (config) {
81
+ * // Vault is configured, can fetch credentials
82
+ * }
83
+ */
84
+ function getVaultConfig() {
85
+ // Try JSON config first
86
+ const jsonConfig = parseVaultConfigJson();
87
+ if (jsonConfig)
88
+ return jsonConfig;
89
+ // Fall back to individual env vars
90
+ const rawUrl = process.env.AXVAULT_URL;
91
+ const apiKey = process.env.AXVAULT_API_KEY;
92
+ if (!rawUrl || !apiKey) {
93
+ return undefined;
94
+ }
95
+ const url = validateVaultUrl(rawUrl);
96
+ if (!url)
97
+ return undefined;
98
+ return {
99
+ url,
100
+ apiKey,
101
+ credentialName: process.env.AXVAULT_CREDENTIAL,
102
+ };
103
+ }
104
+ /**
105
+ * Check if vault is configured.
106
+ *
107
+ * @returns true if AXVAULT_URL and AXVAULT_API_KEY are both set
108
+ */
109
+ function isVaultConfigured() {
110
+ return getVaultConfig() !== undefined;
111
+ }
112
+ export { getVaultConfig, isVaultConfigured };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "axauth",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "1.8.0",
5
+ "version": "1.9.0",
6
6
  "description": "Authentication management library and CLI for AI coding agents",
7
7
  "repository": {
8
8
  "type": "git",
@@ -62,7 +62,7 @@
62
62
  "automation",
63
63
  "coding-assistant"
64
64
  ],
65
- "packageManager": "pnpm@10.26.1",
65
+ "packageManager": "pnpm@10.28.0",
66
66
  "engines": {
67
67
  "node": ">=22.14.0"
68
68
  },
@@ -70,19 +70,19 @@
70
70
  "@commander-js/extra-typings": "^14.0.0",
71
71
  "@inquirer/password": "^5.0.3",
72
72
  "axconfig": "^3.6.0",
73
- "axshared": "^1.8.0",
73
+ "axshared": "^1.9.0",
74
74
  "commander": "^14.0.2",
75
75
  "zod": "^4.3.5"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@total-typescript/ts-reset": "^0.6.1",
79
- "@types/node": "^25.0.3",
79
+ "@types/node": "^25.0.5",
80
80
  "@vitest/coverage-v8": "^4.0.16",
81
81
  "eslint": "^9.39.2",
82
- "eslint-config-axpoint": "^1.0.0",
82
+ "eslint-config-axkit": "^1.0.0",
83
83
  "fta-check": "^1.5.1",
84
84
  "fta-cli": "^3.0.0",
85
- "knip": "^5.80.0",
85
+ "knip": "^5.80.2",
86
86
  "prettier": "3.7.4",
87
87
  "semantic-release": "^25.0.2",
88
88
  "typescript": "^5.9.3",