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.
Files changed (44) hide show
  1. package/README.md +69 -48
  2. package/dist/agents/claude-code/types.d.ts +2 -2
  3. package/dist/agents/copilot/stream-session.js +8 -7
  4. package/dist/agents/copilot/watch-session.js +3 -1
  5. package/dist/agents/gemini/types.d.ts +4 -4
  6. package/dist/agents/opencode/adapter.js +8 -1
  7. package/dist/agents/opencode/build-permission-environment.d.ts +17 -0
  8. package/dist/agents/opencode/build-permission-environment.js +31 -0
  9. package/dist/agents/opencode/parse-sse-event.js +3 -1
  10. package/dist/agents/opencode/spawn-server.js +12 -1
  11. package/dist/build-agent-environment.js +8 -0
  12. package/dist/build-execution-metadata.js +1 -1
  13. package/dist/cli.d.ts +5 -6
  14. package/dist/cli.js +82 -61
  15. package/dist/credentials/get-credential-environment.d.ts +2 -2
  16. package/dist/credentials/get-credential-environment.js +5 -3
  17. package/dist/credentials/install-credentials.d.ts +3 -3
  18. package/dist/credentials/install-credentials.js +8 -8
  19. package/dist/credentials/write-agent-credentials.d.ts +1 -3
  20. package/dist/credentials/write-agent-credentials.js +1 -2
  21. package/dist/execute-agent.js +1 -0
  22. package/dist/format-zod-error.d.ts +6 -0
  23. package/dist/format-zod-error.js +12 -0
  24. package/dist/parse-credentials.js +1 -18
  25. package/dist/read-credentials-file.d.ts +13 -0
  26. package/dist/read-credentials-file.js +33 -0
  27. package/dist/read-stdin.d.ts +5 -0
  28. package/dist/read-stdin.js +11 -0
  29. package/dist/resolve-credentials.d.ts +20 -0
  30. package/dist/resolve-credentials.js +22 -0
  31. package/dist/resolve-output-mode.d.ts +15 -1
  32. package/dist/resolve-output-mode.js +16 -1
  33. package/dist/resolve-prompt.d.ts +22 -0
  34. package/dist/resolve-prompt.js +23 -0
  35. package/dist/run-agent.d.ts +4 -1
  36. package/dist/run-agent.js +32 -17
  37. package/dist/types/run-result.d.ts +4 -0
  38. package/dist/validate-cwd.d.ts +11 -0
  39. package/dist/validate-cwd.js +24 -0
  40. package/dist/validate-opencode-options.d.ts +18 -0
  41. package/dist/validate-opencode-options.js +50 -0
  42. package/dist/validate-stdin-usage.d.ts +18 -0
  43. package/dist/validate-stdin-usage.js +28 -0
  44. 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 in complete isolation, and normalizes output to a standard event stream.
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
- - **Complete isolation**: Agents run in temp directories with no access to user config or global credentials
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 -a codex "Fix the bug in main.ts"
47
- axexec -a gemini "Refactor the utils module"
48
- axexec -a opencode "Add logging"
49
- axexec -a copilot "Write tests"
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 -a claude --model opus "Review this PR"
53
- axexec -a gemini --model gemini-2.5-pro "Refactor utils"
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 -a claude --allow 'read,glob,bash:git *' "Check git history"
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 -a claude -f jsonl "Add tests" | jq 'select(.type == "tool.call")'
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 -a claude "Summarize the change" | cut -f2 | sort | uniq -c | sort -rn
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 -a claude -f jsonl "Audit dependencies" | jq 'select(.type == "tool.call")'
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> Permission rules to allow (comma-separated)
93
- --deny <perms> Permission rules to deny (comma-separated)
94
- -f, --format <fmt> Output format: jsonl, tsv (default: tsv, truncated on TTY)
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
- | Agent | Package | API Key Env Var |
107
- | -------- | ------------------------- | --------------------- |
108
- | claude | @anthropic-ai/claude-code | ANTHROPIC_API_KEY |
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
- (†) OpenCode supports multiple providers via `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GEMINI_API_KEY`.
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 -a claude "Review code"
160
- OPENAI_API_KEY=sk-... axexec -a codex "Fix bug"
161
- GEMINI_API_KEY=... axexec -a gemini "Refactor"
162
- GITHUB_TOKEN=ghp_... axexec -a copilot "Write tests"
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 (provider defaults to "anthropic" if omitted)
174
- AX_OPENCODE_CREDENTIALS='{"agent":"opencode","type":"api-key","data":{"apiKey":"sk-ant-..."}}' \
175
- axexec -a opencode --provider anthropic -m claude-sonnet-4 "Hello"
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 -a opencode --provider openai -m gpt-4.1 "Hello"
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 -a opencode --provider opencode -m glm-4.7-free "Hello"
201
+ axexec --agent opencode --provider opencode --model glm-4.7-free "Hello"
184
202
  ```
185
203
 
186
204
  ## Isolation
187
205
 
188
- axexec provides complete environment isolation:
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
- Codex runs with `--dangerously-bypass-approvals-and-sandbox`, so `--allow` /
197
- `--deny` do not constrain Codex. Run axexec inside an external sandbox if you
198
- need enforcement.
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 **never** access:
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 in a fully isolated environment and consume a normalized event stream. It translates credentials and permissions to agent-specific formats so your automation stays consistent across providers.
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
- "--allow-all-tools",
27
- // Enable network access without confirmation prompts.
28
- // Copilot requires user approval for URL access by default, but other
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 = path.join(homedir(), ".copilot", "session-state");
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 server = await spawnServer({ cwd, signal, env: options.configEnv });
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}. Pre-configure permissions in OpenCode settings.`,
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: { ...process.env, ...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: credentials.agent === "opencode" ? credentials.provider : undefined,
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
- * Executes AI coding agents with complete environment isolation:
6
- * - Credentials written to temp directory
7
- * - Config/permissions written to temp directory
8
- * - Agent pointed at temp directory via env vars
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";