axexec 1.7.0 → 2.0.1
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 +47 -36
- package/dist/build-execution-metadata.js +1 -1
- package/dist/cli.js +3 -3
- package/dist/credentials/get-credential-environment.d.ts +2 -2
- package/dist/credentials/get-credential-environment.js +5 -3
- package/dist/credentials/install-credentials.d.ts +1 -1
- package/dist/credentials/install-credentials.js +6 -6
- package/dist/credentials/write-agent-credentials.d.ts +1 -3
- package/dist/credentials/write-agent-credentials.js +1 -2
- package/dist/parse-credentials.js +0 -10
- package/dist/read-credentials-file.d.ts +1 -1
- package/dist/read-credentials-file.js +1 -7
- package/dist/resolve-credentials.d.ts +1 -2
- package/dist/resolve-credentials.js +2 -3
- package/dist/run-agent.js +1 -27
- package/dist/validate-opencode-options.d.ts +2 -3
- package/dist/validate-opencode-options.js +4 -12
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -18,6 +18,12 @@ axexec is **not** a security boundary. It does not restrict filesystem or networ
|
|
|
18
18
|
npm install -g axexec
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- Node.js 22.14+
|
|
24
|
+
- npx (included with Node.js) for one-off runs; pnpm (optional) for local development workflows
|
|
25
|
+
- jq for JSONL pipeline examples
|
|
26
|
+
|
|
21
27
|
## Requirements
|
|
22
28
|
|
|
23
29
|
Install the agent CLIs you plan to use:
|
|
@@ -45,40 +51,53 @@ export AXEXEC_COPILOT_PATH=/opt/copilot/bin/copilot
|
|
|
45
51
|
```bash
|
|
46
52
|
# Run a prompt with an agent
|
|
47
53
|
axexec --agent claude "Add error handling to auth.ts"
|
|
48
|
-
axexec
|
|
49
|
-
axexec
|
|
50
|
-
axexec
|
|
51
|
-
axexec
|
|
54
|
+
axexec --agent codex "Fix the bug in main.ts"
|
|
55
|
+
axexec --agent gemini "Refactor the utils module"
|
|
56
|
+
axexec --agent opencode "Add logging"
|
|
57
|
+
axexec --agent copilot "Write tests"
|
|
52
58
|
|
|
53
59
|
# Run with explicit credentials JSON (from axauth export)
|
|
54
|
-
axexec
|
|
60
|
+
axexec --agent claude --credentials-file ./creds.json "Review this PR"
|
|
55
61
|
|
|
56
62
|
# Specify a model
|
|
57
|
-
axexec
|
|
58
|
-
axexec
|
|
63
|
+
axexec --agent claude --model opus "Review this PR"
|
|
64
|
+
axexec --agent gemini --model gemini-2.5-pro "Refactor utils"
|
|
59
65
|
|
|
60
66
|
# Set permissions
|
|
61
|
-
axexec
|
|
67
|
+
axexec --agent claude --allow 'read,glob,bash:git *' "Check git history" # best-effort
|
|
62
68
|
|
|
63
69
|
# Output normalized JSONL event stream
|
|
64
|
-
axexec
|
|
70
|
+
axexec --agent claude --format jsonl "Add tests" | jq 'select(.type == "tool.call")'
|
|
65
71
|
|
|
66
72
|
# List available agents
|
|
67
73
|
axexec --list-agents
|
|
68
74
|
```
|
|
69
75
|
|
|
76
|
+
Examples use long flags for clarity; short flags like `-a`, `-m`, and `-f` are still supported.
|
|
77
|
+
|
|
78
|
+
## Output Formats
|
|
79
|
+
|
|
80
|
+
axexec supports `--format jsonl|tsv`:
|
|
81
|
+
|
|
82
|
+
- `jsonl`: one JSON object per line (machine-friendly)
|
|
83
|
+
- `tsv`: tab-separated values (human-friendly)
|
|
84
|
+
- Default is TSV; when stdout is a TTY and `--format` is omitted, output is truncated for readability
|
|
85
|
+
- Use `--format tsv` to force full TSV (not applicable to `--list-agents`, which always outputs TSV)
|
|
86
|
+
- Always set `--format` explicitly in pipelines
|
|
87
|
+
- TSV output starts with `time` and `event type` as the first two columns for all events
|
|
88
|
+
|
|
70
89
|
## Pipeline Examples
|
|
71
90
|
|
|
72
91
|
### Count event types
|
|
73
92
|
|
|
74
93
|
```bash
|
|
75
|
-
axexec
|
|
94
|
+
axexec --agent claude --format tsv "Summarize the change" | cut -f2 | sort | uniq -c | sort -rn
|
|
76
95
|
```
|
|
77
96
|
|
|
78
97
|
### Filter tool calls
|
|
79
98
|
|
|
80
99
|
```bash
|
|
81
|
-
axexec
|
|
100
|
+
axexec --agent claude --format jsonl "Audit dependencies" | jq 'select(.type == "tool.call")'
|
|
82
101
|
```
|
|
83
102
|
|
|
84
103
|
### Filter available agents by package
|
|
@@ -98,7 +117,7 @@ axexec --list-agents | tail -n +2 | awk -F'\t' '$3 ~ /openai/ {print $1}'
|
|
|
98
117
|
--provider <provider> Provider for OpenCode (e.g., anthropic, openai, google, opencode)
|
|
99
118
|
--allow <perms> Allow permissions (best-effort, comma-separated)
|
|
100
119
|
--deny <perms> Deny permissions (best-effort, comma-separated)
|
|
101
|
-
-f, --format <fmt> Output format: jsonl, tsv (default: tsv,
|
|
120
|
+
-f, --format <fmt> Output format: jsonl, tsv (default: tsv; if stdout is TTY and --format is omitted, output is truncated)
|
|
102
121
|
--raw-log <file> Write raw agent output to file
|
|
103
122
|
--debug Enable debug mode (logs unknown events)
|
|
104
123
|
--verbose Show agent stderr output
|
|
@@ -110,15 +129,14 @@ axexec --list-agents | tail -n +2 | awk -F'\t' '$3 ~ /openai/ {print $1}'
|
|
|
110
129
|
|
|
111
130
|
## Supported Agents
|
|
112
131
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
| codex | @openai/codex | OPENAI_API_KEY |
|
|
117
|
-
| gemini | @google/gemini-cli | GEMINI_API_KEY |
|
|
118
|
-
| opencode | opencode-ai | AX_OPENCODE_CREDENTIALS |
|
|
119
|
-
| copilot | @github/copilot | GITHUB_TOKEN |
|
|
132
|
+
`axexec` supports: `claude`, `codex`, `gemini`, `opencode`, `copilot`.
|
|
133
|
+
|
|
134
|
+
Canonical references:
|
|
120
135
|
|
|
121
|
-
|
|
136
|
+
- Agent/package metadata: `axshared` (`axshared/README.md`)
|
|
137
|
+
- Credential environment variable mapping: `axauth/README.md`
|
|
138
|
+
|
|
139
|
+
OpenCode requires `AX_OPENCODE_CREDENTIALS` with provider-specific credentials (see [CI/CD Usage](#cicd-usage)).
|
|
122
140
|
|
|
123
141
|
## Event Stream
|
|
124
142
|
|
|
@@ -138,7 +156,8 @@ axexec normalizes all agent output to a standard event stream:
|
|
|
138
156
|
|
|
139
157
|
### Using Credential Environment Variables
|
|
140
158
|
|
|
141
|
-
For CI/CD pipelines, credentials can be passed via environment variables
|
|
159
|
+
For CI/CD pipelines, credentials can be passed via `AX_*_CREDENTIALS` environment variables.
|
|
160
|
+
Canonical variable names are documented in `axauth/README.md`.
|
|
142
161
|
|
|
143
162
|
```bash
|
|
144
163
|
# Export credentials locally (one-time setup)
|
|
@@ -150,23 +169,15 @@ axauth export --agent claude --output creds.json --no-password
|
|
|
150
169
|
axexec --agent claude --prompt "Review this PR"
|
|
151
170
|
```
|
|
152
171
|
|
|
153
|
-
| Agent | Credential Env Var |
|
|
154
|
-
| -------- | ----------------------- |
|
|
155
|
-
| claude | AX_CLAUDE_CREDENTIALS |
|
|
156
|
-
| codex | AX_CODEX_CREDENTIALS |
|
|
157
|
-
| gemini | AX_GEMINI_CREDENTIALS |
|
|
158
|
-
| opencode | AX_OPENCODE_CREDENTIALS |
|
|
159
|
-
| copilot | AX_COPILOT_CREDENTIALS |
|
|
160
|
-
|
|
161
172
|
### Using Standard API Keys
|
|
162
173
|
|
|
163
174
|
You can also use standard environment variables directly:
|
|
164
175
|
|
|
165
176
|
```bash
|
|
166
|
-
ANTHROPIC_API_KEY=sk-... axexec
|
|
167
|
-
OPENAI_API_KEY=sk-... axexec
|
|
168
|
-
GEMINI_API_KEY=... axexec
|
|
169
|
-
GITHUB_TOKEN=ghp_... axexec
|
|
177
|
+
ANTHROPIC_API_KEY=sk-... axexec --agent claude "Review code"
|
|
178
|
+
OPENAI_API_KEY=sk-... axexec --agent codex "Fix bug"
|
|
179
|
+
GEMINI_API_KEY=... axexec --agent gemini "Refactor"
|
|
180
|
+
GITHUB_TOKEN=ghp_... axexec --agent copilot "Write tests"
|
|
170
181
|
```
|
|
171
182
|
|
|
172
183
|
For Claude, `CLAUDE_CODE_OAUTH_TOKEN` (generated via `claude setup-token`) also works.
|
|
@@ -179,15 +190,15 @@ field must match your `--provider` flag:
|
|
|
179
190
|
```bash
|
|
180
191
|
# Anthropic
|
|
181
192
|
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"anthropic","data":{"apiKey":"sk-ant-..."}}' \
|
|
182
|
-
axexec
|
|
193
|
+
axexec --agent opencode --provider anthropic --model claude-sonnet-4 "Hello"
|
|
183
194
|
|
|
184
195
|
# OpenAI (provider must be specified in credentials)
|
|
185
196
|
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"openai","data":{"apiKey":"sk-..."}}' \
|
|
186
|
-
axexec
|
|
197
|
+
axexec --agent opencode --provider openai --model gpt-4.1 "Hello"
|
|
187
198
|
|
|
188
199
|
# OpenCode hosted models (provider must be "opencode")
|
|
189
200
|
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"opencode","data":{"apiKey":"..."}}' \
|
|
190
|
-
axexec
|
|
201
|
+
axexec --agent opencode --provider opencode --model glm-4.7-free "Hello"
|
|
191
202
|
```
|
|
192
203
|
|
|
193
204
|
## Isolation
|
|
@@ -19,7 +19,7 @@ function buildExecutionMetadata(agentId, options, credentialResult, credentials,
|
|
|
19
19
|
if (credentials) {
|
|
20
20
|
execution.credentials = {
|
|
21
21
|
type: credentials.type,
|
|
22
|
-
provider:
|
|
22
|
+
provider: options.provider,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
return execution;
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ const program = new Command()
|
|
|
40
40
|
.option("--provider <provider>", "Provider for OpenCode (required for OpenCode)")
|
|
41
41
|
.option("--allow <perms>", "Allow permissions (best-effort, comma-separated)")
|
|
42
42
|
.option("--deny <perms>", "Deny permissions (best-effort, comma-separated)")
|
|
43
|
-
.option("-f, --format <fmt>", "Output format: jsonl, tsv (default: tsv,
|
|
43
|
+
.option("-f, --format <fmt>", "Output format: jsonl, tsv (default when omitted: truncated tsv on TTY, full tsv otherwise)")
|
|
44
44
|
.option("--raw-log <file>", "Write raw agent output to file")
|
|
45
45
|
.option("--debug", "Enable debug mode")
|
|
46
46
|
.option("--verbose", "Show agent stderr output")
|
|
@@ -130,7 +130,7 @@ Examples:
|
|
|
130
130
|
format = formatResult.format;
|
|
131
131
|
}
|
|
132
132
|
// Resolve credentials from --credentials-file if specified.
|
|
133
|
-
const credentialsResult = await resolveCredentials(options.credentialsFile, readStdin
|
|
133
|
+
const credentialsResult = await resolveCredentials(options.credentialsFile, readStdin);
|
|
134
134
|
if (!credentialsResult.ok) {
|
|
135
135
|
process.stderr.write(`Error: ${credentialsResult.error}\n`);
|
|
136
136
|
process.exitCode = 2;
|
|
@@ -138,7 +138,7 @@ Examples:
|
|
|
138
138
|
}
|
|
139
139
|
const { credentials } = credentialsResult;
|
|
140
140
|
// Validate provider options
|
|
141
|
-
const providerResult = validateProviderOptions(options.agent, options.model, options.provider
|
|
141
|
+
const providerResult = validateProviderOptions(options.agent, options.model, options.provider);
|
|
142
142
|
if (!providerResult.ok) {
|
|
143
143
|
process.stderr.write(`Error: ${providerResult.error}\n`);
|
|
144
144
|
process.exitCode = 2;
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* Each agent has its own env var requirements. This module extracts
|
|
5
5
|
* the appropriate values from Credentials and returns them as env vars.
|
|
6
6
|
*/
|
|
7
|
-
import type { Credentials } from "axshared";
|
|
7
|
+
import type { AgentCli, Credentials } from "axshared";
|
|
8
8
|
/**
|
|
9
9
|
* Gets additional environment variables for credential-based auth.
|
|
10
10
|
*
|
|
11
11
|
* Some agents use environment variables for API keys or OAuth tokens
|
|
12
12
|
* in addition to or instead of file-based credentials.
|
|
13
13
|
*/
|
|
14
|
-
declare function getCredentialEnvironment(credentials: Credentials): Record<string, string>;
|
|
14
|
+
declare function getCredentialEnvironment(agentId: AgentCli, credentials: Credentials, provider?: string): Record<string, string>;
|
|
15
15
|
export { getCredentialEnvironment };
|
|
@@ -11,7 +11,7 @@ import { resolveStringField } from "./resolve-string-field.js";
|
|
|
11
11
|
* Some agents use environment variables for API keys or OAuth tokens
|
|
12
12
|
* in addition to or instead of file-based credentials.
|
|
13
13
|
*/
|
|
14
|
-
function getCredentialEnvironment(credentials) {
|
|
14
|
+
function getCredentialEnvironment(agentId, credentials, provider) {
|
|
15
15
|
const environment = {};
|
|
16
16
|
// Extract API key from credentials data
|
|
17
17
|
const apiKey = resolveStringField(credentials.data, "apiKey", "key");
|
|
@@ -19,7 +19,7 @@ function getCredentialEnvironment(credentials) {
|
|
|
19
19
|
// axauth uses "accessToken", but also check legacy "oauthToken" and "token" keys
|
|
20
20
|
const oauthToken = resolveStringField(credentials.data, "accessToken", "oauthToken") ??
|
|
21
21
|
resolveStringField(credentials.data, "token", "token");
|
|
22
|
-
switch (
|
|
22
|
+
switch (agentId) {
|
|
23
23
|
case "claude": {
|
|
24
24
|
if (credentials.type === "api-key" && apiKey) {
|
|
25
25
|
environment["ANTHROPIC_API_KEY"] = apiKey;
|
|
@@ -51,7 +51,6 @@ function getCredentialEnvironment(credentials) {
|
|
|
51
51
|
}
|
|
52
52
|
case "opencode": {
|
|
53
53
|
// OpenCode supports multiple providers, env var depends on provider
|
|
54
|
-
const provider = credentials.provider;
|
|
55
54
|
if (credentials.type === "api-key" && apiKey) {
|
|
56
55
|
switch (provider) {
|
|
57
56
|
case "anthropic": {
|
|
@@ -71,6 +70,9 @@ function getCredentialEnvironment(credentials) {
|
|
|
71
70
|
environment["OPENCODE_API_KEY"] = apiKey;
|
|
72
71
|
break;
|
|
73
72
|
}
|
|
73
|
+
case undefined: {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
break;
|
|
@@ -19,7 +19,7 @@ type WarningWriter = (message: string) => void;
|
|
|
19
19
|
*
|
|
20
20
|
* The caller is responsible for cleaning up the temp directory after use.
|
|
21
21
|
*/
|
|
22
|
-
declare function installCredentials(agentId: AgentCli, credentials?: Credentials, warn?: WarningWriter): Promise<InstallResult>;
|
|
22
|
+
declare function installCredentials(agentId: AgentCli, credentials?: Credentials, provider?: string, warn?: WarningWriter): Promise<InstallResult>;
|
|
23
23
|
/**
|
|
24
24
|
* Cleanup for use in finally blocks.
|
|
25
25
|
*/
|
|
@@ -42,7 +42,7 @@ async function createTemporaryDirectories(agentId) {
|
|
|
42
42
|
/**
|
|
43
43
|
* Installs agent-specific credentials to the appropriate directory.
|
|
44
44
|
*/
|
|
45
|
-
function installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, warn) {
|
|
45
|
+
function installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, provider, warn) {
|
|
46
46
|
switch (agentId) {
|
|
47
47
|
case "claude": {
|
|
48
48
|
installClaudeCredentials(configDirectory, credentials);
|
|
@@ -57,8 +57,8 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
57
57
|
break;
|
|
58
58
|
}
|
|
59
59
|
case "opencode": {
|
|
60
|
-
if (
|
|
61
|
-
installOpenCodeCredentials(dataDirectory, credentials, warn);
|
|
60
|
+
if (provider) {
|
|
61
|
+
installOpenCodeCredentials(dataDirectory, credentials, provider, warn);
|
|
62
62
|
}
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
@@ -78,15 +78,15 @@ function installAgentCredentials(agentId, configDirectory, dataDirectory, creden
|
|
|
78
78
|
*
|
|
79
79
|
* The caller is responsible for cleaning up the temp directory after use.
|
|
80
80
|
*/
|
|
81
|
-
async function installCredentials(agentId, credentials, warn) {
|
|
81
|
+
async function installCredentials(agentId, credentials, provider, warn) {
|
|
82
82
|
const { baseDirectory, configDirectory, dataDirectory } = await createTemporaryDirectories(agentId);
|
|
83
83
|
try {
|
|
84
84
|
if (credentials) {
|
|
85
|
-
installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, warn);
|
|
85
|
+
installAgentCredentials(agentId, configDirectory, dataDirectory, credentials, provider, warn);
|
|
86
86
|
}
|
|
87
87
|
const runtimeEnvironment = buildAgentRuntimeEnvironment(agentId, configDirectory, dataDirectory);
|
|
88
88
|
const credentialEnvironment = credentials
|
|
89
|
-
? getCredentialEnvironment(credentials)
|
|
89
|
+
? getCredentialEnvironment(agentId, credentials, provider)
|
|
90
90
|
: {};
|
|
91
91
|
return {
|
|
92
92
|
env: { ...runtimeEnvironment, ...credentialEnvironment },
|
|
@@ -54,7 +54,5 @@ declare function installGeminiCredentials(configDirectory: string, credentials:
|
|
|
54
54
|
* - oauth-credentials: Written as { [provider]: { type: "oauth", ...data } }
|
|
55
55
|
* - api-key: Written as { [provider]: { type: "api", key: "..." } }
|
|
56
56
|
*/
|
|
57
|
-
declare function installOpenCodeCredentials(dataDirectory: string, credentials:
|
|
58
|
-
agent: "opencode";
|
|
59
|
-
}>, warn?: WarningWriter): void;
|
|
57
|
+
declare function installOpenCodeCredentials(dataDirectory: string, credentials: Credentials, provider: string, warn?: WarningWriter): void;
|
|
60
58
|
export { installClaudeCredentials, installCodexCredentials, installGeminiCredentials, installOpenCodeCredentials, };
|
|
@@ -100,9 +100,8 @@ function installGeminiCredentials(configDirectory, credentials) {
|
|
|
100
100
|
* - oauth-credentials: Written as { [provider]: { type: "oauth", ...data } }
|
|
101
101
|
* - api-key: Written as { [provider]: { type: "api", key: "..." } }
|
|
102
102
|
*/
|
|
103
|
-
function installOpenCodeCredentials(dataDirectory, credentials, warn = defaultWarningWriter) {
|
|
103
|
+
function installOpenCodeCredentials(dataDirectory, credentials, provider, warn = defaultWarningWriter) {
|
|
104
104
|
const authPath = path.join(dataDirectory, "auth.json");
|
|
105
|
-
const provider = credentials.provider;
|
|
106
105
|
switch (credentials.type) {
|
|
107
106
|
case "oauth-credentials": {
|
|
108
107
|
saveJsonFile(authPath, {
|
|
@@ -25,14 +25,6 @@ function parseCredentialsFromEnvironment(agentId) {
|
|
|
25
25
|
const parsed = JSON.parse(credEnvironmentValue);
|
|
26
26
|
const parseResult = parseCredentialsResult(parsed);
|
|
27
27
|
if (parseResult.ok) {
|
|
28
|
-
if (parseResult.value.agent !== agentId) {
|
|
29
|
-
return {
|
|
30
|
-
ok: false,
|
|
31
|
-
exitCode: 2,
|
|
32
|
-
message: `Error: ${credEnvironmentVariable} agent mismatch. ` +
|
|
33
|
-
`Expected ${agentId}, got ${parseResult.value.agent}.`,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
28
|
return { ok: true, credentials: parseResult.value };
|
|
37
29
|
}
|
|
38
30
|
return {
|
|
@@ -67,7 +59,6 @@ function parseApiKeyFromEnvironment(agentId) {
|
|
|
67
59
|
return {
|
|
68
60
|
ok: true,
|
|
69
61
|
credentials: {
|
|
70
|
-
agent: agentId,
|
|
71
62
|
type: "oauth-token",
|
|
72
63
|
data: { oauthToken },
|
|
73
64
|
},
|
|
@@ -102,7 +93,6 @@ function parseApiKeyFromEnvironment(agentId) {
|
|
|
102
93
|
return {
|
|
103
94
|
ok: true,
|
|
104
95
|
credentials: {
|
|
105
|
-
agent: agentId,
|
|
106
96
|
type: "api-key",
|
|
107
97
|
data: { apiKey },
|
|
108
98
|
},
|
|
@@ -9,5 +9,5 @@ type ReadCredentialsResult = {
|
|
|
9
9
|
ok: false;
|
|
10
10
|
error: string;
|
|
11
11
|
};
|
|
12
|
-
declare function readCredentialsFile(path: string, readStdin: () => Promise<string
|
|
12
|
+
declare function readCredentialsFile(path: string, readStdin: () => Promise<string>): Promise<ReadCredentialsResult>;
|
|
13
13
|
export { readCredentialsFile };
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { parseCredentialsResult } from "axshared";
|
|
6
6
|
import { formatZodError } from "./format-zod-error.js";
|
|
7
|
-
async function readCredentialsFile(path, readStdin
|
|
7
|
+
async function readCredentialsFile(path, readStdin) {
|
|
8
8
|
let raw;
|
|
9
9
|
try {
|
|
10
10
|
raw = path === "-" ? await readStdin() : await readFile(path, "utf8");
|
|
@@ -28,12 +28,6 @@ async function readCredentialsFile(path, readStdin, expectedAgent) {
|
|
|
28
28
|
error: `Invalid credentials format: ${formatZodError(parseResult.error)}`,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
if (parseResult.value.agent !== expectedAgent) {
|
|
32
|
-
return {
|
|
33
|
-
ok: false,
|
|
34
|
-
error: `Credentials agent mismatch. Expected ${expectedAgent}, got ${parseResult.value.agent}.`,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
31
|
return { ok: true, credentials: parseResult.value };
|
|
38
32
|
}
|
|
39
33
|
export { readCredentialsFile };
|
|
@@ -14,8 +14,7 @@ type ResolveCredentialsResult = {
|
|
|
14
14
|
*
|
|
15
15
|
* @param credentialsFile - Path to credentials file or "-" for stdin
|
|
16
16
|
* @param readStdin - Function to read stdin
|
|
17
|
-
* @param agentId - Expected agent ID for validation
|
|
18
17
|
* @returns Credentials or undefined if no file specified
|
|
19
18
|
*/
|
|
20
|
-
declare function resolveCredentials(credentialsFile: string | undefined, readStdin: () => Promise<string
|
|
19
|
+
declare function resolveCredentials(credentialsFile: string | undefined, readStdin: () => Promise<string>): Promise<ResolveCredentialsResult>;
|
|
21
20
|
export { resolveCredentials };
|
|
@@ -7,14 +7,13 @@ import { readCredentialsFile } from "./read-credentials-file.js";
|
|
|
7
7
|
*
|
|
8
8
|
* @param credentialsFile - Path to credentials file or "-" for stdin
|
|
9
9
|
* @param readStdin - Function to read stdin
|
|
10
|
-
* @param agentId - Expected agent ID for validation
|
|
11
10
|
* @returns Credentials or undefined if no file specified
|
|
12
11
|
*/
|
|
13
|
-
async function resolveCredentials(credentialsFile, readStdin
|
|
12
|
+
async function resolveCredentials(credentialsFile, readStdin) {
|
|
14
13
|
if (!credentialsFile) {
|
|
15
14
|
return { ok: true, credentials: undefined };
|
|
16
15
|
}
|
|
17
|
-
const result = await readCredentialsFile(credentialsFile, readStdin
|
|
16
|
+
const result = await readCredentialsFile(credentialsFile, readStdin);
|
|
18
17
|
if (!result.ok) {
|
|
19
18
|
return { ok: false, error: result.error };
|
|
20
19
|
}
|
package/dist/run-agent.js
CHANGED
|
@@ -83,32 +83,6 @@ async function runAgent(agentId, options) {
|
|
|
83
83
|
}
|
|
84
84
|
// Credentials are optional; isolation still applies even when none are found.
|
|
85
85
|
const credentials = credentialsResult.credentials;
|
|
86
|
-
if (credentials && credentials.agent !== validation.agentId) {
|
|
87
|
-
diagnostics.error("Error: Credentials agent mismatch. " +
|
|
88
|
-
`Expected ${validation.agentId}, got ${credentials.agent}.`);
|
|
89
|
-
setExitCode(2);
|
|
90
|
-
return {
|
|
91
|
-
events: [],
|
|
92
|
-
success: false,
|
|
93
|
-
execution: { agent: validation.agentId, model: options.model },
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
// Validate OpenCode credentials provider matches the requested provider.
|
|
97
|
-
// This duplicates validation in validateOpenCodeOptions() (CLI layer) but is
|
|
98
|
-
// necessary for programmatic API consumers who call runAgent() directly.
|
|
99
|
-
if (validation.agentId === "opencode" &&
|
|
100
|
-
credentials?.agent === "opencode" &&
|
|
101
|
-
options.provider &&
|
|
102
|
-
credentials.provider.toLowerCase() !== options.provider.toLowerCase()) {
|
|
103
|
-
diagnostics.error("Error: Credentials provider mismatch. " +
|
|
104
|
-
`Expected ${options.provider}, got ${credentials.provider}.`);
|
|
105
|
-
setExitCode(2);
|
|
106
|
-
return {
|
|
107
|
-
events: [],
|
|
108
|
-
success: false,
|
|
109
|
-
execution: { agent: validation.agentId, model: options.model },
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
86
|
// Normalize provider to lowercase for OpenCode (all OpenCode provider IDs are lowercase).
|
|
113
87
|
// This ensures consistent behavior for both CLI and programmatic API users.
|
|
114
88
|
const normalizedOptions = validation.agentId === "opencode" && options.provider
|
|
@@ -117,7 +91,7 @@ async function runAgent(agentId, options) {
|
|
|
117
91
|
// Always create isolated runtime directories; install credentials if provided.
|
|
118
92
|
let credentialResult;
|
|
119
93
|
try {
|
|
120
|
-
credentialResult = await installCredentials(validation.agentId, credentials, diagnostics.warn);
|
|
94
|
+
credentialResult = await installCredentials(validation.agentId, credentials, normalizedOptions.provider, diagnostics.warn);
|
|
121
95
|
}
|
|
122
96
|
catch (error) {
|
|
123
97
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validate OpenCode-specific CLI options.
|
|
3
3
|
*/
|
|
4
|
-
import type { Credentials } from "axshared";
|
|
5
4
|
type ProviderValidationResult = {
|
|
6
5
|
ok: true;
|
|
7
6
|
normalizedProvider: string | undefined;
|
|
@@ -13,7 +12,7 @@ type ProviderValidationResult = {
|
|
|
13
12
|
* Validate provider usage and OpenCode-specific options.
|
|
14
13
|
*
|
|
15
14
|
* - For non-OpenCode agents: provider must not be specified
|
|
16
|
-
* - For OpenCode: validates model format, requires provider
|
|
15
|
+
* - For OpenCode: validates model format, requires provider
|
|
17
16
|
*/
|
|
18
|
-
declare function validateProviderOptions(agentId: string, model: string | undefined, provider: string | undefined
|
|
17
|
+
declare function validateProviderOptions(agentId: string, model: string | undefined, provider: string | undefined): ProviderValidationResult;
|
|
19
18
|
export { validateProviderOptions };
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Validate provider usage and OpenCode-specific options.
|
|
6
6
|
*
|
|
7
7
|
* - For non-OpenCode agents: provider must not be specified
|
|
8
|
-
* - For OpenCode: validates model format, requires provider
|
|
8
|
+
* - For OpenCode: validates model format, requires provider
|
|
9
9
|
*/
|
|
10
|
-
function validateProviderOptions(agentId, model, provider
|
|
10
|
+
function validateProviderOptions(agentId, model, provider) {
|
|
11
11
|
// --provider is only valid for OpenCode
|
|
12
12
|
if (provider && agentId !== "opencode") {
|
|
13
13
|
return {
|
|
@@ -20,9 +20,9 @@ function validateProviderOptions(agentId, model, provider, credentials) {
|
|
|
20
20
|
return { ok: true, normalizedProvider: undefined };
|
|
21
21
|
}
|
|
22
22
|
// OpenCode-specific validation
|
|
23
|
-
return validateOpenCodeOptions(model, provider
|
|
23
|
+
return validateOpenCodeOptions(model, provider);
|
|
24
24
|
}
|
|
25
|
-
function validateOpenCodeOptions(model, provider
|
|
25
|
+
function validateOpenCodeOptions(model, provider) {
|
|
26
26
|
// Require --provider for OpenCode
|
|
27
27
|
if (!provider) {
|
|
28
28
|
// If model contains "/" and no provider given, suggest they might be using
|
|
@@ -45,14 +45,6 @@ function validateOpenCodeOptions(model, provider, credentials) {
|
|
|
45
45
|
// Normalize to lowercase to match OpenCode's provider ID format.
|
|
46
46
|
// OpenCode stores all provider IDs in lowercase (anthropic, openai, etc).
|
|
47
47
|
const normalizedProvider = provider.toLowerCase();
|
|
48
|
-
// Validate credentials provider matches
|
|
49
|
-
if (credentials?.agent === "opencode" &&
|
|
50
|
-
credentials.provider.toLowerCase() !== normalizedProvider) {
|
|
51
|
-
return {
|
|
52
|
-
ok: false,
|
|
53
|
-
error: `Credentials provider mismatch. Expected ${normalizedProvider}, got ${credentials.provider}.`,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
48
|
return { ok: true, normalizedProvider };
|
|
57
49
|
}
|
|
58
50
|
export { validateProviderOptions };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "axexec",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2.0.1",
|
|
6
6
|
"description": "Unified CLI runner for AI coding agents with normalized event streaming",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -59,27 +59,27 @@
|
|
|
59
59
|
"automation",
|
|
60
60
|
"coding-assistant"
|
|
61
61
|
],
|
|
62
|
-
"packageManager": "pnpm@10.28.
|
|
62
|
+
"packageManager": "pnpm@10.28.1",
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=22.14.0"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@commander-js/extra-typings": "^14.0.0",
|
|
68
68
|
"axconfig": "^3.6.3",
|
|
69
|
-
"axshared": "^
|
|
69
|
+
"axshared": "^5.0.0",
|
|
70
70
|
"commander": "^14.0.2",
|
|
71
71
|
"zod": "^4.3.5"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
75
|
-
"@types/node": "^25.0.
|
|
75
|
+
"@types/node": "^25.0.10",
|
|
76
76
|
"@vitest/coverage-v8": "^4.0.17",
|
|
77
77
|
"eslint": "^9.39.2",
|
|
78
78
|
"eslint-config-axkit": "^1.1.0",
|
|
79
79
|
"fta-check": "^1.5.1",
|
|
80
80
|
"fta-cli": "^3.0.0",
|
|
81
|
-
"knip": "^5.82.
|
|
82
|
-
"prettier": "3.8.
|
|
81
|
+
"knip": "^5.82.1",
|
|
82
|
+
"prettier": "3.8.1",
|
|
83
83
|
"semantic-release": "^25.0.2",
|
|
84
84
|
"typescript": "^5.9.3",
|
|
85
85
|
"vitest": "^4.0.17"
|