axexec 1.6.1 → 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 +69 -48
- package/dist/agents/claude-code/types.d.ts +2 -2
- package/dist/agents/copilot/stream-session.js +8 -7
- package/dist/agents/copilot/watch-session.js +3 -1
- package/dist/agents/gemini/types.d.ts +4 -4
- package/dist/agents/opencode/adapter.js +8 -1
- package/dist/agents/opencode/build-permission-environment.d.ts +17 -0
- package/dist/agents/opencode/build-permission-environment.js +31 -0
- package/dist/agents/opencode/parse-sse-event.js +3 -1
- package/dist/agents/opencode/spawn-server.js +12 -1
- package/dist/build-agent-environment.js +8 -0
- package/dist/build-execution-metadata.js +1 -1
- package/dist/cli.d.ts +5 -6
- package/dist/cli.js +82 -61
- 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 +3 -3
- package/dist/credentials/install-credentials.js +8 -8
- package/dist/credentials/write-agent-credentials.d.ts +1 -3
- package/dist/credentials/write-agent-credentials.js +1 -2
- package/dist/execute-agent.js +1 -0
- package/dist/format-zod-error.d.ts +6 -0
- package/dist/format-zod-error.js +12 -0
- package/dist/parse-credentials.js +1 -18
- package/dist/read-credentials-file.d.ts +13 -0
- package/dist/read-credentials-file.js +33 -0
- package/dist/read-stdin.d.ts +5 -0
- package/dist/read-stdin.js +11 -0
- package/dist/resolve-credentials.d.ts +20 -0
- package/dist/resolve-credentials.js +22 -0
- package/dist/resolve-output-mode.d.ts +15 -1
- package/dist/resolve-output-mode.js +16 -1
- package/dist/resolve-prompt.d.ts +22 -0
- package/dist/resolve-prompt.js +23 -0
- package/dist/run-agent.d.ts +4 -1
- package/dist/run-agent.js +32 -17
- package/dist/types/run-result.d.ts +4 -0
- package/dist/validate-cwd.d.ts +11 -0
- package/dist/validate-cwd.js +24 -0
- package/dist/validate-opencode-options.d.ts +18 -0
- package/dist/validate-opencode-options.js +50 -0
- package/dist/validate-stdin-usage.d.ts +18 -0
- package/dist/validate-stdin-usage.js +28 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# axexec
|
|
2
2
|
|
|
3
|
-
Execution engine for AI coding agents. Translates abstract inputs to agent-specific formats, runs agents
|
|
3
|
+
Execution engine for AI coding agents. Translates abstract inputs to agent-specific formats, runs agents with credential/config isolation (not an OS sandbox), and normalizes output to a standard event stream.
|
|
4
4
|
|
|
5
5
|
## What axexec Does
|
|
6
6
|
|
|
@@ -8,7 +8,9 @@ axexec is the **translation layer** between high-level tooling and agent-specifi
|
|
|
8
8
|
|
|
9
9
|
- **Input translation**: Credentials → agent-specific files, permissions → config files, model → CLI flags
|
|
10
10
|
- **Output normalization**: Claude JSONL, Codex items, Gemini events, OpenCode SSE → unified `AxexecEvent` stream
|
|
11
|
-
- **
|
|
11
|
+
- **Credential/config isolation**: Agents are pointed at temp directories instead of your local agent config/credential locations
|
|
12
|
+
|
|
13
|
+
axexec is **not** a security boundary. It does not restrict filesystem or network access; many adapters run in permissive non-interactive modes to avoid prompts. For real enforcement, run inside an external sandbox (container/VM).
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
|
|
@@ -16,6 +18,12 @@ axexec is the **translation layer** between high-level tooling and agent-specifi
|
|
|
16
18
|
npm install -g axexec
|
|
17
19
|
```
|
|
18
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
|
+
|
|
19
27
|
## Requirements
|
|
20
28
|
|
|
21
29
|
Install the agent CLIs you plan to use:
|
|
@@ -43,37 +51,53 @@ export AXEXEC_COPILOT_PATH=/opt/copilot/bin/copilot
|
|
|
43
51
|
```bash
|
|
44
52
|
# Run a prompt with an agent
|
|
45
53
|
axexec --agent claude "Add error handling to auth.ts"
|
|
46
|
-
axexec
|
|
47
|
-
axexec
|
|
48
|
-
axexec
|
|
49
|
-
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"
|
|
58
|
+
|
|
59
|
+
# Run with explicit credentials JSON (from axauth export)
|
|
60
|
+
axexec --agent claude --credentials-file ./creds.json "Review this PR"
|
|
50
61
|
|
|
51
62
|
# Specify a model
|
|
52
|
-
axexec
|
|
53
|
-
axexec
|
|
63
|
+
axexec --agent claude --model opus "Review this PR"
|
|
64
|
+
axexec --agent gemini --model gemini-2.5-pro "Refactor utils"
|
|
54
65
|
|
|
55
66
|
# Set permissions
|
|
56
|
-
axexec
|
|
67
|
+
axexec --agent claude --allow 'read,glob,bash:git *' "Check git history" # best-effort
|
|
57
68
|
|
|
58
69
|
# Output normalized JSONL event stream
|
|
59
|
-
axexec
|
|
70
|
+
axexec --agent claude --format jsonl "Add tests" | jq 'select(.type == "tool.call")'
|
|
60
71
|
|
|
61
72
|
# List available agents
|
|
62
73
|
axexec --list-agents
|
|
63
74
|
```
|
|
64
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
|
+
|
|
65
89
|
## Pipeline Examples
|
|
66
90
|
|
|
67
91
|
### Count event types
|
|
68
92
|
|
|
69
93
|
```bash
|
|
70
|
-
axexec
|
|
94
|
+
axexec --agent claude --format tsv "Summarize the change" | cut -f2 | sort | uniq -c | sort -rn
|
|
71
95
|
```
|
|
72
96
|
|
|
73
97
|
### Filter tool calls
|
|
74
98
|
|
|
75
99
|
```bash
|
|
76
|
-
axexec
|
|
100
|
+
axexec --agent claude --format jsonl "Audit dependencies" | jq 'select(.type == "tool.call")'
|
|
77
101
|
```
|
|
78
102
|
|
|
79
103
|
### Filter available agents by package
|
|
@@ -86,12 +110,14 @@ axexec --list-agents | tail -n +2 | awk -F'\t' '$3 ~ /openai/ {print $1}'
|
|
|
86
110
|
|
|
87
111
|
```
|
|
88
112
|
-a, --agent <id> Agent to use (claude, codex, gemini, opencode, copilot)
|
|
113
|
+
--cwd <path> Working directory for the agent process
|
|
114
|
+
--credentials-file <path|-> Read credentials JSON from file (or '-' for stdin)
|
|
89
115
|
-p, --prompt <text> Prompt text (alternative to positional argument)
|
|
90
116
|
-m, --model <model> Model to use (agent-specific)
|
|
91
|
-
--provider <provider> Provider for OpenCode (anthropic, openai, google)
|
|
92
|
-
--allow <perms>
|
|
93
|
-
--deny <perms>
|
|
94
|
-
-f, --format <fmt> Output format: jsonl, tsv (default: tsv,
|
|
117
|
+
--provider <provider> Provider for OpenCode (e.g., anthropic, openai, google, opencode)
|
|
118
|
+
--allow <perms> Allow permissions (best-effort, comma-separated)
|
|
119
|
+
--deny <perms> Deny permissions (best-effort, comma-separated)
|
|
120
|
+
-f, --format <fmt> Output format: jsonl, tsv (default: tsv; if stdout is TTY and --format is omitted, output is truncated)
|
|
95
121
|
--raw-log <file> Write raw agent output to file
|
|
96
122
|
--debug Enable debug mode (logs unknown events)
|
|
97
123
|
--verbose Show agent stderr output
|
|
@@ -103,15 +129,14 @@ axexec --list-agents | tail -n +2 | awk -F'\t' '$3 ~ /openai/ {print $1}'
|
|
|
103
129
|
|
|
104
130
|
## Supported Agents
|
|
105
131
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
| codex | @openai/codex | OPENAI_API_KEY |
|
|
110
|
-
| gemini | @google/gemini-cli | GEMINI_API_KEY |
|
|
111
|
-
| opencode | opencode-ai | ANTHROPIC_API_KEY (†) |
|
|
112
|
-
| copilot | @github/copilot | GITHUB_TOKEN |
|
|
132
|
+
`axexec` supports: `claude`, `codex`, `gemini`, `opencode`, `copilot`.
|
|
133
|
+
|
|
134
|
+
Canonical references:
|
|
113
135
|
|
|
114
|
-
|
|
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)).
|
|
115
140
|
|
|
116
141
|
## Event Stream
|
|
117
142
|
|
|
@@ -131,7 +156,8 @@ axexec normalizes all agent output to a standard event stream:
|
|
|
131
156
|
|
|
132
157
|
### Using Credential Environment Variables
|
|
133
158
|
|
|
134
|
-
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`.
|
|
135
161
|
|
|
136
162
|
```bash
|
|
137
163
|
# Export credentials locally (one-time setup)
|
|
@@ -143,23 +169,15 @@ axauth export --agent claude --output creds.json --no-password
|
|
|
143
169
|
axexec --agent claude --prompt "Review this PR"
|
|
144
170
|
```
|
|
145
171
|
|
|
146
|
-
| Agent | Credential Env Var |
|
|
147
|
-
| -------- | ----------------------- |
|
|
148
|
-
| claude | AX_CLAUDE_CREDENTIALS |
|
|
149
|
-
| codex | AX_CODEX_CREDENTIALS |
|
|
150
|
-
| gemini | AX_GEMINI_CREDENTIALS |
|
|
151
|
-
| opencode | AX_OPENCODE_CREDENTIALS |
|
|
152
|
-
| copilot | AX_COPILOT_CREDENTIALS |
|
|
153
|
-
|
|
154
172
|
### Using Standard API Keys
|
|
155
173
|
|
|
156
174
|
You can also use standard environment variables directly:
|
|
157
175
|
|
|
158
176
|
```bash
|
|
159
|
-
ANTHROPIC_API_KEY=sk-... axexec
|
|
160
|
-
OPENAI_API_KEY=sk-... axexec
|
|
161
|
-
GEMINI_API_KEY=... axexec
|
|
162
|
-
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"
|
|
163
181
|
```
|
|
164
182
|
|
|
165
183
|
For Claude, `CLAUDE_CODE_OAUTH_TOKEN` (generated via `claude setup-token`) also works.
|
|
@@ -170,22 +188,22 @@ For OpenCode, set `AX_OPENCODE_CREDENTIALS` with your credentials. The `provider
|
|
|
170
188
|
field must match your `--provider` flag:
|
|
171
189
|
|
|
172
190
|
```bash
|
|
173
|
-
# Anthropic
|
|
174
|
-
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","data":{"apiKey":"sk-ant-..."}}' \
|
|
175
|
-
axexec
|
|
191
|
+
# Anthropic
|
|
192
|
+
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"anthropic","data":{"apiKey":"sk-ant-..."}}' \
|
|
193
|
+
axexec --agent opencode --provider anthropic --model claude-sonnet-4 "Hello"
|
|
176
194
|
|
|
177
195
|
# OpenAI (provider must be specified in credentials)
|
|
178
196
|
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"openai","data":{"apiKey":"sk-..."}}' \
|
|
179
|
-
axexec
|
|
197
|
+
axexec --agent opencode --provider openai --model gpt-4.1 "Hello"
|
|
180
198
|
|
|
181
199
|
# OpenCode hosted models (provider must be "opencode")
|
|
182
200
|
AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","provider":"opencode","data":{"apiKey":"..."}}' \
|
|
183
|
-
axexec
|
|
201
|
+
axexec --agent opencode --provider opencode --model glm-4.7-free "Hello"
|
|
184
202
|
```
|
|
185
203
|
|
|
186
204
|
## Isolation
|
|
187
205
|
|
|
188
|
-
axexec provides
|
|
206
|
+
axexec provides credential/config isolation:
|
|
189
207
|
|
|
190
208
|
1. Creates a temp directory for each session
|
|
191
209
|
2. Installs credentials to the temp directory
|
|
@@ -193,11 +211,14 @@ axexec provides complete environment isolation:
|
|
|
193
211
|
4. Sets environment variables to point agents at the temp directory
|
|
194
212
|
5. Cleans up the temp directory after execution
|
|
195
213
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
214
|
+
axexec is intentionally **permissive** and should not be treated as a sandbox:
|
|
215
|
+
|
|
216
|
+
- It does not enforce filesystem or network restrictions
|
|
217
|
+
- `--allow` / `--deny` is best-effort and may be ignored or weakened by adapter "YOLO" switches
|
|
218
|
+
- Agent-internal sandbox env vars (`GEMINI_SANDBOX`, `SEATBELT_PROFILE`) are excluded from subprocess environments
|
|
219
|
+
- For enforcement, run axexec inside an external sandbox (container/VM)
|
|
199
220
|
|
|
200
|
-
Agents
|
|
221
|
+
Agents are configured to avoid using your local agent config/credential locations:
|
|
201
222
|
|
|
202
223
|
- User's home directory config (`~/.claude`, `~/.codex`, `~/.gemini`)
|
|
203
224
|
- System keychain or credential managers
|
|
@@ -212,7 +233,7 @@ Add to your `CLAUDE.md` or `AGENTS.md`:
|
|
|
212
233
|
|
|
213
234
|
Run `npx -y axexec --help` to learn available options.
|
|
214
235
|
|
|
215
|
-
Use `axexec` when you need to run a supported agent
|
|
236
|
+
Use `axexec` when you need to run a supported agent with credential/config isolation and consume a normalized event stream. It translates credentials and permissions to agent-specific formats so your automation stays consistent across providers.
|
|
216
237
|
```
|
|
217
238
|
|
|
218
239
|
## License
|
|
@@ -77,8 +77,8 @@ type ClaudeUserEvent = z.infer<typeof ClaudeUserEvent>;
|
|
|
77
77
|
declare const ClaudeResultEvent: z.ZodObject<{
|
|
78
78
|
type: z.ZodLiteral<"result">;
|
|
79
79
|
subtype: z.ZodEnum<{
|
|
80
|
-
success: "success";
|
|
81
80
|
error: "error";
|
|
81
|
+
success: "success";
|
|
82
82
|
cancelled: "cancelled";
|
|
83
83
|
}>;
|
|
84
84
|
timestamp: z.ZodOptional<z.ZodString>;
|
|
@@ -153,8 +153,8 @@ declare const ClaudeEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
153
153
|
}, z.core.$strip>, z.ZodObject<{
|
|
154
154
|
type: z.ZodLiteral<"result">;
|
|
155
155
|
subtype: z.ZodEnum<{
|
|
156
|
-
success: "success";
|
|
157
156
|
error: "error";
|
|
157
|
+
success: "success";
|
|
158
158
|
cancelled: "cancelled";
|
|
159
159
|
}>;
|
|
160
160
|
timestamp: z.ZodOptional<z.ZodString>;
|
|
@@ -23,13 +23,9 @@ async function main() {
|
|
|
23
23
|
const copilotArguments = [
|
|
24
24
|
"-p",
|
|
25
25
|
prompt,
|
|
26
|
-
"
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
// agents (Gemini, OpenCode) allow it by default. For unified behavior,
|
|
30
|
-
// enable it. This matches how --allow-all-tools works for other operations.
|
|
31
|
-
// See: copilot-cli-decompiled/copilot-source/index.js line 418918
|
|
32
|
-
"--allow-all-urls",
|
|
26
|
+
// Always run in "YOLO" mode (no prompts). This is equivalent to
|
|
27
|
+
// --allow-all-tools --allow-all-paths --allow-all-urls.
|
|
28
|
+
"--yolo",
|
|
33
29
|
...extraArguments,
|
|
34
30
|
];
|
|
35
31
|
// Record existing session files
|
|
@@ -55,6 +51,11 @@ async function main() {
|
|
|
55
51
|
const copilotBin = process.env["AXEXEC_COPILOT_PATH"] ?? "copilot";
|
|
56
52
|
const child = spawn(copilotBin, copilotArguments, {
|
|
57
53
|
stdio: ["ignore", "pipe", "pipe"],
|
|
54
|
+
env: {
|
|
55
|
+
...process.env,
|
|
56
|
+
// Auto-trust folders (avoids interactive trust prompts).
|
|
57
|
+
COPILOT_ALLOW_ALL: "true",
|
|
58
|
+
},
|
|
58
59
|
});
|
|
59
60
|
copilotProcess = child;
|
|
60
61
|
// Pipe Copilot's stdout/stderr to our stderr (for debugging)
|
|
@@ -13,7 +13,9 @@ import { existsSync, readdirSync, statSync, watch, } from "node:fs";
|
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
/** Copilot session state directory */
|
|
16
|
-
const SESSION_STATE_DIR =
|
|
16
|
+
const SESSION_STATE_DIR = process.env["XDG_STATE_HOME"]
|
|
17
|
+
? path.join(process.env["XDG_STATE_HOME"], ".copilot", "session-state")
|
|
18
|
+
: path.join(homedir(), ".copilot", "session-state");
|
|
17
19
|
/** Session events filename inside session directories (new format) */
|
|
18
20
|
const SESSION_EVENTS_FILE = "events.jsonl";
|
|
19
21
|
/**
|
|
@@ -41,8 +41,8 @@ declare const GeminiToolResultEvent: z.ZodObject<{
|
|
|
41
41
|
type: z.ZodLiteral<"tool_result">;
|
|
42
42
|
tool_id: z.ZodString;
|
|
43
43
|
status: z.ZodEnum<{
|
|
44
|
-
success: "success";
|
|
45
44
|
error: "error";
|
|
45
|
+
success: "success";
|
|
46
46
|
}>;
|
|
47
47
|
output: z.ZodOptional<z.ZodString>;
|
|
48
48
|
error: z.ZodOptional<z.ZodObject<{
|
|
@@ -67,8 +67,8 @@ type GeminiErrorEvent = z.infer<typeof GeminiErrorEvent>;
|
|
|
67
67
|
declare const GeminiResultEvent: z.ZodObject<{
|
|
68
68
|
type: z.ZodLiteral<"result">;
|
|
69
69
|
status: z.ZodEnum<{
|
|
70
|
-
success: "success";
|
|
71
70
|
error: "error";
|
|
71
|
+
success: "success";
|
|
72
72
|
}>;
|
|
73
73
|
error: z.ZodOptional<z.ZodObject<{
|
|
74
74
|
type: z.ZodString;
|
|
@@ -109,8 +109,8 @@ declare const GeminiEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
109
109
|
type: z.ZodLiteral<"tool_result">;
|
|
110
110
|
tool_id: z.ZodString;
|
|
111
111
|
status: z.ZodEnum<{
|
|
112
|
-
success: "success";
|
|
113
112
|
error: "error";
|
|
113
|
+
success: "success";
|
|
114
114
|
}>;
|
|
115
115
|
output: z.ZodOptional<z.ZodString>;
|
|
116
116
|
error: z.ZodOptional<z.ZodObject<{
|
|
@@ -129,8 +129,8 @@ declare const GeminiEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
129
129
|
}, z.core.$strip>, z.ZodObject<{
|
|
130
130
|
type: z.ZodLiteral<"result">;
|
|
131
131
|
status: z.ZodEnum<{
|
|
132
|
-
success: "success";
|
|
133
132
|
error: "error";
|
|
133
|
+
success: "success";
|
|
134
134
|
}>;
|
|
135
135
|
error: z.ZodOptional<z.ZodObject<{
|
|
136
136
|
type: z.ZodString;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* 7. Clean up server on completion
|
|
15
15
|
*/
|
|
16
16
|
import { registerAdapter } from "../registry.js";
|
|
17
|
+
import { buildPermissionEnvironment } from "./build-permission-environment.js";
|
|
17
18
|
import { cleanupSession } from "./cleanup-session.js";
|
|
18
19
|
import { createSessionStartEvent } from "./create-session-start-event.js";
|
|
19
20
|
import { determineSessionSuccess } from "../../determine-session-success.js";
|
|
@@ -49,7 +50,13 @@ async function* streamSession(options) {
|
|
|
49
50
|
process.on("SIGTERM", forwardSignal);
|
|
50
51
|
try {
|
|
51
52
|
// 1. Spawn server
|
|
52
|
-
const
|
|
53
|
+
const configEnvironment = options.configEnv ?? {};
|
|
54
|
+
const permissionEnvironment = buildPermissionEnvironment(configEnvironment);
|
|
55
|
+
const server = await spawnServer({
|
|
56
|
+
cwd,
|
|
57
|
+
signal,
|
|
58
|
+
env: { ...configEnvironment, ...permissionEnvironment },
|
|
59
|
+
});
|
|
53
60
|
serverProcess = server.process;
|
|
54
61
|
serverUrl = server.url;
|
|
55
62
|
// 2. Connect to SSE and start streaming
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds permissive OpenCode environment variables for headless execution.
|
|
3
|
+
*
|
|
4
|
+
* When axconfig hasn't generated an explicit OPENCODE_CONFIG_DIR (via
|
|
5
|
+
* --allow/--deny), this injects allow-all permissions to avoid interactive
|
|
6
|
+
* prompts. OpenCode's default config includes "ask" permissions that would
|
|
7
|
+
* block headless execution.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Returns environment variables for permissive OpenCode execution.
|
|
11
|
+
*
|
|
12
|
+
* Returns an empty object (don't override) if:
|
|
13
|
+
* - `OPENCODE_CONFIG_DIR` is set (axconfig's explicit permissions via --allow/--deny)
|
|
14
|
+
* - `OPENCODE_PERMISSION` is already set (caller's explicit permissions)
|
|
15
|
+
*/
|
|
16
|
+
declare function buildPermissionEnvironment(configEnvironment: Record<string, string>): Record<string, string>;
|
|
17
|
+
export { buildPermissionEnvironment };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds permissive OpenCode environment variables for headless execution.
|
|
3
|
+
*
|
|
4
|
+
* When axconfig hasn't generated an explicit OPENCODE_CONFIG_DIR (via
|
|
5
|
+
* --allow/--deny), this injects allow-all permissions to avoid interactive
|
|
6
|
+
* prompts. OpenCode's default config includes "ask" permissions that would
|
|
7
|
+
* block headless execution.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Returns environment variables for permissive OpenCode execution.
|
|
11
|
+
*
|
|
12
|
+
* Returns an empty object (don't override) if:
|
|
13
|
+
* - `OPENCODE_CONFIG_DIR` is set (axconfig's explicit permissions via --allow/--deny)
|
|
14
|
+
* - `OPENCODE_PERMISSION` is already set (caller's explicit permissions)
|
|
15
|
+
*/
|
|
16
|
+
function buildPermissionEnvironment(configEnvironment) {
|
|
17
|
+
if (configEnvironment["OPENCODE_CONFIG_DIR"] !== undefined ||
|
|
18
|
+
configEnvironment["OPENCODE_PERMISSION"] !== undefined) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
OPENCODE_PERMISSION: JSON.stringify({
|
|
23
|
+
"*": "allow",
|
|
24
|
+
doom_loop: "allow",
|
|
25
|
+
external_directory: "allow",
|
|
26
|
+
read: "allow",
|
|
27
|
+
question: "allow",
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export { buildPermissionEnvironment };
|
|
@@ -110,7 +110,9 @@ function parseSSEEvent(event, state) {
|
|
|
110
110
|
{
|
|
111
111
|
type: "session.error",
|
|
112
112
|
code: "PERMISSION_REQUIRED",
|
|
113
|
-
message: `OpenCode requires permission: ${tool}. ${title}.
|
|
113
|
+
message: `OpenCode requires permission: ${tool}. ${title}. ` +
|
|
114
|
+
`axexec runs OpenCode non-interactively, so permission prompts are treated as errors. ` +
|
|
115
|
+
`Use axexec --allow/--deny to pre-configure permissions.`,
|
|
114
116
|
timestamp: Date.now(),
|
|
115
117
|
},
|
|
116
118
|
];
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Handles spawning the OpenCode server process and waiting for it to start.
|
|
5
5
|
*/
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
|
+
import { buildBaseEnvironment } from "../../build-agent-environment.js";
|
|
7
8
|
import { resolveBinary } from "../../resolve-binary.js";
|
|
8
9
|
/** Error thrown when server fails to start */
|
|
9
10
|
class ServerStartError extends Error {
|
|
@@ -27,10 +28,20 @@ async function spawnServer(options) {
|
|
|
27
28
|
environmentVariable: "AXEXEC_OPENCODE_PATH",
|
|
28
29
|
installHint: "npm install -g opencode-ai",
|
|
29
30
|
});
|
|
31
|
+
// OpenCode supports several env-based configuration overrides (OPENCODE_*).
|
|
32
|
+
// Exclude them from the host environment so axexec can run with isolated
|
|
33
|
+
// temp config/data directories instead of inheriting user settings.
|
|
34
|
+
const baseEnvironment = buildBaseEnvironment([
|
|
35
|
+
"OPENCODE_CONFIG",
|
|
36
|
+
"OPENCODE_CONFIG_CONTENT",
|
|
37
|
+
"OPENCODE_CONFIG_DIR",
|
|
38
|
+
"OPENCODE_DATA_DIR",
|
|
39
|
+
"OPENCODE_PERMISSION",
|
|
40
|
+
]);
|
|
30
41
|
const child = spawn(bin, ["serve", "--port", "0"], {
|
|
31
42
|
cwd,
|
|
32
43
|
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
-
env: { ...
|
|
44
|
+
env: { ...baseEnvironment, ...env },
|
|
34
45
|
});
|
|
35
46
|
let stderrBuffer = "";
|
|
36
47
|
let resolved = false;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
const VAULT_ENV_EXCLUSIONS = ["AXVAULT", "AXVAULT_URL", "AXVAULT_API_KEY"];
|
|
2
|
+
// Prevent upstream CLIs from enabling their own internal sandboxing based on
|
|
3
|
+
// host environment variables. axexec is intentionally insecure-by-default and
|
|
4
|
+
// does not rely on agent-internal sandboxing for enforcement.
|
|
5
|
+
const AGENT_INTERNAL_SANDBOX_ENV_EXCLUSIONS = [
|
|
6
|
+
"GEMINI_SANDBOX",
|
|
7
|
+
"SEATBELT_PROFILE",
|
|
8
|
+
];
|
|
2
9
|
function isAxCredentialEnvironment(key) {
|
|
3
10
|
return key.startsWith("AX_") && key.endsWith("_CREDENTIALS");
|
|
4
11
|
}
|
|
5
12
|
function buildBaseEnvironment(additionalExclusions = []) {
|
|
6
13
|
const allExclusions = new Set([
|
|
7
14
|
...VAULT_ENV_EXCLUSIONS,
|
|
15
|
+
...AGENT_INTERNAL_SANDBOX_ENV_EXCLUSIONS,
|
|
8
16
|
...additionalExclusions,
|
|
9
17
|
]);
|
|
10
18
|
const entries = Object.entries(process.env).flatMap(([key, value]) => {
|
|
@@ -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.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* axexec CLI - Unified agent execution with isolation.
|
|
3
|
+
* axexec CLI - Unified agent execution with credential/config isolation.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - Normalized event stream output
|
|
5
|
+
* axexec is a headless runner and normalization layer. It isolates agent
|
|
6
|
+
* credentials/config from your local environment via temp directories, but it
|
|
7
|
+
* is not an OS sandbox and is intentionally permissive/non-interactive by
|
|
8
|
+
* default. Use an external sandbox (container/VM) for real enforcement.
|
|
10
9
|
*/
|
|
11
10
|
import "./agents/claude-code/adapter.js";
|
|
12
11
|
import "./agents/codex/adapter.js";
|