axconfig 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/bin/axconfig +17 -0
- package/dist/agents/claude-code.d.ts +14 -0
- package/dist/agents/claude-code.js +98 -0
- package/dist/agents/codex.d.ts +15 -0
- package/dist/agents/codex.js +164 -0
- package/dist/agents/gemini.d.ts +14 -0
- package/dist/agents/gemini.js +156 -0
- package/dist/agents/opencode.d.ts +14 -0
- package/dist/agents/opencode.js +152 -0
- package/dist/auth/agents/claude-code.d.ts +9 -0
- package/dist/auth/agents/claude-code.js +106 -0
- package/dist/auth/agents/codex.d.ts +9 -0
- package/dist/auth/agents/codex.js +75 -0
- package/dist/auth/agents/gemini.d.ts +9 -0
- package/dist/auth/agents/gemini.js +97 -0
- package/dist/auth/agents/opencode.d.ts +9 -0
- package/dist/auth/agents/opencode.js +62 -0
- package/dist/auth/check-auth.d.ts +13 -0
- package/dist/auth/check-auth.js +24 -0
- package/dist/auth/extract-credentials.d.ts +11 -0
- package/dist/auth/extract-credentials.js +20 -0
- package/dist/auth/get-access-token.d.ts +35 -0
- package/dist/auth/get-access-token.js +116 -0
- package/dist/auth/types.d.ts +19 -0
- package/dist/auth/types.js +4 -0
- package/dist/build-agent-config.d.ts +41 -0
- package/dist/build-agent-config.js +79 -0
- package/dist/builder.d.ts +19 -0
- package/dist/builder.js +27 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +79 -0
- package/dist/commands/auth.d.ts +19 -0
- package/dist/commands/auth.js +87 -0
- package/dist/commands/create.d.ts +14 -0
- package/dist/commands/create.js +101 -0
- package/dist/crypto.d.ts +39 -0
- package/dist/crypto.js +78 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +53 -0
- package/dist/parse-permissions.d.ts +50 -0
- package/dist/parse-permissions.js +125 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +8 -0
- package/package.json +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Łukasz Jerciński
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# axconfig
|
|
2
|
+
|
|
3
|
+
Configuration management library and CLI for AI coding agents.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
axconfig is the **single source of truth** for all AI coding agent configuration logic. It can be used:
|
|
8
|
+
|
|
9
|
+
1. **As a library** - imported by [axrun](https://github.com/anthropics/axrun) to build agent configs programmatically
|
|
10
|
+
2. **As a CLI** - standalone tool for managing, inspecting, and creating agent configs
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
16
|
+
│ axrun │
|
|
17
|
+
│ (Agent execution: spawns agents, streams events) │
|
|
18
|
+
│ │
|
|
19
|
+
│ axrun -a claude-code --allow "read,bash:git *" "prompt" │
|
|
20
|
+
│ │ │
|
|
21
|
+
│ ▼ │
|
|
22
|
+
│ ┌────────────────────────┐ │
|
|
23
|
+
│ │ axconfig │ │
|
|
24
|
+
│ │ (Config translation) │ │
|
|
25
|
+
│ └────────────────────────┘ │
|
|
26
|
+
│ │ │
|
|
27
|
+
│ ▼ │
|
|
28
|
+
│ { env, settings } │
|
|
29
|
+
│ │ │
|
|
30
|
+
│ ▼ │
|
|
31
|
+
│ spawn(claude, { env }) │
|
|
32
|
+
└─────────────────────────────────────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Core Responsibilities
|
|
36
|
+
|
|
37
|
+
### Permission Parsing
|
|
38
|
+
|
|
39
|
+
Unified syntax for all agents:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Tool permissions
|
|
43
|
+
read, write, edit, bash, glob, grep, web
|
|
44
|
+
|
|
45
|
+
# Bash command patterns
|
|
46
|
+
bash:git *
|
|
47
|
+
bash:npm run build
|
|
48
|
+
|
|
49
|
+
# Path restrictions (agent-dependent)
|
|
50
|
+
read:src/**
|
|
51
|
+
write:.env
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Config Translation
|
|
55
|
+
|
|
56
|
+
Translates unified permissions to agent-specific formats:
|
|
57
|
+
|
|
58
|
+
| Agent | Output Format |
|
|
59
|
+
| ----------- | --------------------------------------------------------- |
|
|
60
|
+
| claude-code | JSON `settings.json` with `permissions.allow/deny` arrays |
|
|
61
|
+
| codex | TOML `config.toml` + Starlark `.rules` files |
|
|
62
|
+
| gemini | TOML policy files with `[[rule]]` entries |
|
|
63
|
+
| opencode | JSON with `permission.{edit,bash,webfetch}` |
|
|
64
|
+
|
|
65
|
+
### Capability Validation
|
|
66
|
+
|
|
67
|
+
Each agent has different capabilities:
|
|
68
|
+
|
|
69
|
+
| Agent | Tool Perms | Bash Patterns | Path Restrictions | Can Deny Read |
|
|
70
|
+
| ----------- | ----------- | ------------- | ----------------- | ------------- |
|
|
71
|
+
| claude-code | ✓ | ✓ | ✓ | ✓ |
|
|
72
|
+
| codex | ✗ (sandbox) | ✓ | ✗ | ✗ |
|
|
73
|
+
| gemini | ✓ | ✓ | ✗ | ✓ |
|
|
74
|
+
| opencode | ✓ | ✓ | ✗ | ✓ |
|
|
75
|
+
|
|
76
|
+
axconfig validates permissions against agent capabilities:
|
|
77
|
+
|
|
78
|
+
- **Unsupported allow rules** → warning, rule dropped (safe: fewer permissions)
|
|
79
|
+
- **Unsupported deny rules** → error, abort (unsafe: can't enforce restriction)
|
|
80
|
+
|
|
81
|
+
## Library API
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { parsePermissions, getConfigBuilder, buildAgentConfig } from "axconfig";
|
|
85
|
+
|
|
86
|
+
// Parse CLI-style permission strings
|
|
87
|
+
const permissions = parsePermissions(
|
|
88
|
+
["read,glob,bash:git *"], // allow
|
|
89
|
+
["bash:rm *"], // deny
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Build agent-specific config
|
|
93
|
+
const result = buildAgentConfig({
|
|
94
|
+
agentId: "claude-code",
|
|
95
|
+
allow: "read,glob,bash:git *",
|
|
96
|
+
deny: "bash:rm *",
|
|
97
|
+
output: "/tmp/my-config", // directory for config files
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (result.ok) {
|
|
101
|
+
// result.env = { CLAUDE_CONFIG_DIR: "/tmp/my-config" }
|
|
102
|
+
// result.warnings = [...]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## CLI Commands
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# List agents and their auth status
|
|
110
|
+
axconfig auth list
|
|
111
|
+
axconfig auth list --json
|
|
112
|
+
|
|
113
|
+
# Export credentials to encrypted file
|
|
114
|
+
axconfig auth export --agent claude-code --output creds.json
|
|
115
|
+
axconfig auth export --agent claude-code --output creds.json --no-password
|
|
116
|
+
|
|
117
|
+
# Create config with permissions (outputs env vars)
|
|
118
|
+
axconfig create --agent claude-code --output /tmp/config \
|
|
119
|
+
--allow "read,glob,bash:git *" \
|
|
120
|
+
--deny "bash:rm *"
|
|
121
|
+
|
|
122
|
+
# Create config with exported credentials
|
|
123
|
+
axconfig create --agent claude-code --output /tmp/config \
|
|
124
|
+
--allow read \
|
|
125
|
+
--with-credentials creds.json
|
|
126
|
+
|
|
127
|
+
# Export for shell usage
|
|
128
|
+
eval $(axconfig create --agent claude-code --output /tmp/config --allow read)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Pipeline Examples
|
|
132
|
+
|
|
133
|
+
The CLI outputs TSV format for easy processing with standard Unix tools:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# List all agents and their auth status
|
|
137
|
+
axconfig auth list
|
|
138
|
+
# AGENT STATUS METHOD
|
|
139
|
+
# claude-code authenticated OAuth (max)
|
|
140
|
+
# codex authenticated ChatGPT OAuth
|
|
141
|
+
# ...
|
|
142
|
+
|
|
143
|
+
# Filter to show only authenticated agents
|
|
144
|
+
axconfig auth list | tail -n +2 | awk -F'\t' '$2 == "authenticated"'
|
|
145
|
+
|
|
146
|
+
# Count agents by status
|
|
147
|
+
axconfig auth list | tail -n +2 | cut -f2 | sort | uniq -c
|
|
148
|
+
|
|
149
|
+
# Extract agent names as a list
|
|
150
|
+
axconfig auth list | tail -n +2 | cut -f1
|
|
151
|
+
|
|
152
|
+
# Export config as JSON and extract specific fields with jq
|
|
153
|
+
axconfig create --agent claude-code --output /tmp/cfg --allow read --format json | jq '.env'
|
|
154
|
+
|
|
155
|
+
# Check if a specific agent is authenticated
|
|
156
|
+
axconfig auth list --json | jq -e '.[] | select(.agentId == "claude-code") | .authenticated'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Module Structure
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
src/
|
|
163
|
+
├── types.ts # PermissionRule, AxrunConfig, BuildResult, etc.
|
|
164
|
+
├── parse-permissions.ts # parseRule, parseRuleList, parsePermissions
|
|
165
|
+
├── builder.ts # ConfigBuilder registry
|
|
166
|
+
├── build-agent-config.ts # High-level buildAgentConfig function
|
|
167
|
+
├── check-auth.ts # Auth status detection for agents
|
|
168
|
+
├── extract-credentials.ts # Credential extraction for export
|
|
169
|
+
├── crypto.ts # AES-256-GCM encryption for credentials
|
|
170
|
+
├── cli.ts # CLI entry point
|
|
171
|
+
└── agents/
|
|
172
|
+
├── claude-code.ts # Claude Code config builder
|
|
173
|
+
├── codex.ts # Codex config builder
|
|
174
|
+
├── gemini.ts # Gemini CLI config builder
|
|
175
|
+
└── opencode.ts # OpenCode config builder
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Integration with axrun
|
|
179
|
+
|
|
180
|
+
axrun imports axconfig as a dependency:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// In axrun/src/cli.ts
|
|
184
|
+
import { buildAgentConfig } from "axconfig";
|
|
185
|
+
|
|
186
|
+
const configResult = buildAgentConfig({
|
|
187
|
+
agentId: options.agent,
|
|
188
|
+
allow: options.allow,
|
|
189
|
+
deny: options.deny,
|
|
190
|
+
output: "/tmp/axrun-config",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (!configResult.ok) {
|
|
194
|
+
// Handle errors
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Spawn agent with config
|
|
198
|
+
spawn(agentBin, args, { env: { ...process.env, ...configResult.env } });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Auth Preservation
|
|
202
|
+
|
|
203
|
+
A key requirement is preserving agent authentication when injecting permissions.
|
|
204
|
+
|
|
205
|
+
### Problem
|
|
206
|
+
|
|
207
|
+
Agents store auth in their config directories. Naively overriding config paths breaks auth:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# This breaks auth!
|
|
211
|
+
CLAUDE_CONFIG_DIR=/tmp/custom claude "prompt"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Solution
|
|
215
|
+
|
|
216
|
+
Each agent has a preferred method for layering config:
|
|
217
|
+
|
|
218
|
+
| Agent | Method |
|
|
219
|
+
| ----------- | ---------------------------------------------------- |
|
|
220
|
+
| claude-code | `--settings <file>` flag merges with existing config |
|
|
221
|
+
| codex | `CODEX_HOME` with copied/symlinked auth files |
|
|
222
|
+
| gemini | `GEMINI_CLI_SYSTEM_SETTINGS_PATH` for settings |
|
|
223
|
+
| opencode | `OPENCODE_CONFIG` points to merged config |
|
|
224
|
+
|
|
225
|
+
axconfig handles these differences internally.
|
|
226
|
+
|
|
227
|
+
## Agent Rule
|
|
228
|
+
|
|
229
|
+
Add to your `CLAUDE.md` or `AGENTS.md`:
|
|
230
|
+
|
|
231
|
+
```markdown
|
|
232
|
+
# Rule: `axconfig` Usage
|
|
233
|
+
|
|
234
|
+
Run `npx -y axconfig --help` to learn available options.
|
|
235
|
+
|
|
236
|
+
Use `axconfig` to manage AI agent configurations with unified permission syntax.
|
|
237
|
+
It translates `--allow` and `--deny` rules to agent-specific formats (Claude Code,
|
|
238
|
+
Codex, Gemini CLI, OpenCode) and handles auth preservation automatically.
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Development Status
|
|
242
|
+
|
|
243
|
+
🚧 **Work in Progress**
|
|
244
|
+
|
|
245
|
+
Migrating config logic from axrun to axconfig.
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT
|
package/bin/axconfig
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point that dynamically imports the compiled TypeScript.
|
|
4
|
+
*
|
|
5
|
+
* Uses top-level await to ensure module evaluation errors are handled
|
|
6
|
+
* properly. Without await, errors during import would surface as unhandled
|
|
7
|
+
* rejections instead of clean CLI failures with appropriate exit codes.
|
|
8
|
+
*/
|
|
9
|
+
try {
|
|
10
|
+
await import("../dist/cli.js");
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(
|
|
13
|
+
"Failed to start axconfig:",
|
|
14
|
+
error instanceof Error ? error.message : error,
|
|
15
|
+
);
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code ConfigBuilder.
|
|
3
|
+
*
|
|
4
|
+
* Translates AxrunConfig into Claude Code's settings.json format.
|
|
5
|
+
*
|
|
6
|
+
* Claude Code supports:
|
|
7
|
+
* - Tool permissions via permissions.allow/deny
|
|
8
|
+
* - Bash patterns like "Bash(git:*)"
|
|
9
|
+
* - Path patterns like "Read(src/**)"
|
|
10
|
+
*/
|
|
11
|
+
import type { ConfigBuilder } from "../types.js";
|
|
12
|
+
/** Claude Code ConfigBuilder */
|
|
13
|
+
declare const claudeCodeConfigBuilder: ConfigBuilder;
|
|
14
|
+
export { claudeCodeConfigBuilder };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code ConfigBuilder.
|
|
3
|
+
*
|
|
4
|
+
* Translates AxrunConfig into Claude Code's settings.json format.
|
|
5
|
+
*
|
|
6
|
+
* Claude Code supports:
|
|
7
|
+
* - Tool permissions via permissions.allow/deny
|
|
8
|
+
* - Bash patterns like "Bash(git:*)"
|
|
9
|
+
* - Path patterns like "Read(src/**)"
|
|
10
|
+
*/
|
|
11
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { registerConfigBuilder } from "../builder.js";
|
|
14
|
+
/** Claude Code tool name mapping */
|
|
15
|
+
const TOOL_MAP = {
|
|
16
|
+
read: "Read",
|
|
17
|
+
write: "Write",
|
|
18
|
+
edit: "Edit",
|
|
19
|
+
bash: "Bash",
|
|
20
|
+
glob: "Glob",
|
|
21
|
+
grep: "Grep",
|
|
22
|
+
web: "WebFetch",
|
|
23
|
+
};
|
|
24
|
+
/** Claude Code capabilities */
|
|
25
|
+
const CAPABILITIES = {
|
|
26
|
+
toolPermissions: true,
|
|
27
|
+
bashPatterns: true,
|
|
28
|
+
pathRestrictions: true,
|
|
29
|
+
canDenyRead: true,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Translate a permission rule to Claude Code format.
|
|
33
|
+
*/
|
|
34
|
+
function translateRule(rule) {
|
|
35
|
+
switch (rule.type) {
|
|
36
|
+
case "tool": {
|
|
37
|
+
return TOOL_MAP[rule.name];
|
|
38
|
+
}
|
|
39
|
+
case "bash": {
|
|
40
|
+
// Claude Code uses "Bash(pattern:*)" for command patterns
|
|
41
|
+
return `Bash(${rule.pattern}:*)`;
|
|
42
|
+
}
|
|
43
|
+
case "path": {
|
|
44
|
+
// Claude Code uses "Tool(path/**)" for path patterns
|
|
45
|
+
const toolName = rule.tool.charAt(0).toUpperCase() + rule.tool.slice(1);
|
|
46
|
+
return `${toolName}(${rule.pattern})`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build Claude Code configuration.
|
|
52
|
+
*/
|
|
53
|
+
function build(config, output) {
|
|
54
|
+
mkdirSync(output, { recursive: true });
|
|
55
|
+
const warnings = [];
|
|
56
|
+
const permissions = config.permissions;
|
|
57
|
+
const settingsPath = path.join(output, "settings.json");
|
|
58
|
+
// If no permissions specified, deny all
|
|
59
|
+
if (!permissions) {
|
|
60
|
+
const settings = {
|
|
61
|
+
permissions: {
|
|
62
|
+
allow: [],
|
|
63
|
+
deny: [],
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
writeFileSync(settingsPath, JSON.stringify(settings, undefined, 2));
|
|
67
|
+
return {
|
|
68
|
+
ok: true,
|
|
69
|
+
env: { CLAUDE_CONFIG_DIR: output },
|
|
70
|
+
warnings: [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Claude Code supports all permission types, so no warnings/errors needed
|
|
74
|
+
// All rules can be translated directly
|
|
75
|
+
const allowRules = permissions.allow.map((rule) => translateRule(rule));
|
|
76
|
+
const denyRules = permissions.deny.map((rule) => translateRule(rule));
|
|
77
|
+
const settings = {
|
|
78
|
+
permissions: {
|
|
79
|
+
allow: allowRules,
|
|
80
|
+
deny: denyRules,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
writeFileSync(settingsPath, JSON.stringify(settings, undefined, 2));
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
env: { CLAUDE_CONFIG_DIR: output },
|
|
87
|
+
warnings,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Claude Code ConfigBuilder */
|
|
91
|
+
const claudeCodeConfigBuilder = {
|
|
92
|
+
agentId: "claude-code",
|
|
93
|
+
capabilities: CAPABILITIES,
|
|
94
|
+
build,
|
|
95
|
+
};
|
|
96
|
+
// Self-register on import
|
|
97
|
+
registerConfigBuilder(claudeCodeConfigBuilder);
|
|
98
|
+
export { claudeCodeConfigBuilder };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex ConfigBuilder.
|
|
3
|
+
*
|
|
4
|
+
* Translates AxrunConfig into Codex's config.toml and execpolicy rules.
|
|
5
|
+
*
|
|
6
|
+
* Codex has unique characteristics:
|
|
7
|
+
* - Uses OS-level sandbox for file access (read-only, workspace-write)
|
|
8
|
+
* - Uses execpolicy .rules files for bash command patterns
|
|
9
|
+
* - CANNOT deny read access (sandbox always allows reads)
|
|
10
|
+
* - Does NOT support path restrictions
|
|
11
|
+
*/
|
|
12
|
+
import type { ConfigBuilder } from "../types.js";
|
|
13
|
+
/** Codex ConfigBuilder */
|
|
14
|
+
declare const codexConfigBuilder: ConfigBuilder;
|
|
15
|
+
export { codexConfigBuilder };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex ConfigBuilder.
|
|
3
|
+
*
|
|
4
|
+
* Translates AxrunConfig into Codex's config.toml and execpolicy rules.
|
|
5
|
+
*
|
|
6
|
+
* Codex has unique characteristics:
|
|
7
|
+
* - Uses OS-level sandbox for file access (read-only, workspace-write)
|
|
8
|
+
* - Uses execpolicy .rules files for bash command patterns
|
|
9
|
+
* - CANNOT deny read access (sandbox always allows reads)
|
|
10
|
+
* - Does NOT support path restrictions
|
|
11
|
+
*/
|
|
12
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { registerConfigBuilder } from "../builder.js";
|
|
15
|
+
/** Codex capabilities */
|
|
16
|
+
const CAPABILITIES = {
|
|
17
|
+
toolPermissions: false, // Uses sandbox mode instead
|
|
18
|
+
bashPatterns: true,
|
|
19
|
+
pathRestrictions: false,
|
|
20
|
+
canDenyRead: false, // Sandbox always allows reads
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Infer sandbox mode from permissions.
|
|
24
|
+
*
|
|
25
|
+
* - If write or edit is allowed → workspace-write
|
|
26
|
+
* - Otherwise → read-only
|
|
27
|
+
*/
|
|
28
|
+
function inferSandboxMode(permissions) {
|
|
29
|
+
const allowsWrite = permissions.allow.some((r) => r.type === "tool" && (r.name === "write" || r.name === "edit"));
|
|
30
|
+
return allowsWrite ? "workspace-write" : "read-only";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate Starlark prefix_rule for execpolicy.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* generatePrefixRule("git", "status", "allow")
|
|
37
|
+
* // Returns: prefix_rule(pattern = ["git", "status"], decision = "allow")
|
|
38
|
+
*/
|
|
39
|
+
function generatePrefixRule(pattern, decision) {
|
|
40
|
+
// Split the pattern into tokens
|
|
41
|
+
// "git status" → ["git", "status"]
|
|
42
|
+
// "npm run build" → ["npm", "run", "build"]
|
|
43
|
+
const tokens = pattern.trim().split(/\s+/u);
|
|
44
|
+
// Handle wildcards at the end
|
|
45
|
+
// "git *" → pattern = ["git"], which matches all git subcommands
|
|
46
|
+
const filteredTokens = tokens.filter((t) => t !== "*");
|
|
47
|
+
const patternString = JSON.stringify(filteredTokens);
|
|
48
|
+
return `prefix_rule(pattern = ${patternString}, decision = "${decision}")`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build Codex configuration.
|
|
52
|
+
*/
|
|
53
|
+
function build(config, output) {
|
|
54
|
+
mkdirSync(output, { recursive: true });
|
|
55
|
+
const warnings = [];
|
|
56
|
+
const errors = [];
|
|
57
|
+
const permissions = config.permissions;
|
|
58
|
+
// Check for unsupported rules
|
|
59
|
+
if (permissions) {
|
|
60
|
+
// Check for --deny "read" which is not possible in Codex
|
|
61
|
+
for (const rule of permissions.deny) {
|
|
62
|
+
if (rule.type === "tool" && rule.name === "read") {
|
|
63
|
+
errors.push({
|
|
64
|
+
rule,
|
|
65
|
+
reason: "Codex sandbox mode always permits file reads",
|
|
66
|
+
suggestions: [
|
|
67
|
+
"Use a different agent that supports denying reads (e.g., claude-code)",
|
|
68
|
+
'Remove the --deny "read" rule',
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Path restrictions not supported
|
|
73
|
+
if (rule.type === "path") {
|
|
74
|
+
errors.push({
|
|
75
|
+
rule,
|
|
76
|
+
reason: "Codex does not support path restrictions",
|
|
77
|
+
suggestions: [
|
|
78
|
+
`Use "${rule.tool}" to deny all ${rule.tool} operations`,
|
|
79
|
+
"Use a different agent that supports path restrictions (e.g., claude-code)",
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Path restrictions in allow rules - warn and drop
|
|
85
|
+
for (const rule of permissions.allow) {
|
|
86
|
+
if (rule.type === "path") {
|
|
87
|
+
warnings.push({
|
|
88
|
+
rule,
|
|
89
|
+
reason: "Codex does not support path restrictions",
|
|
90
|
+
suggestions: [
|
|
91
|
+
`Use "${rule.tool}" to allow all ${rule.tool} operations`,
|
|
92
|
+
"Remove the path restriction",
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Tool permissions other than read/write/edit - warn as Codex uses sandbox
|
|
98
|
+
const nonFileTools = permissions.allow.filter((r) => r.type === "tool" &&
|
|
99
|
+
!["read", "write", "edit", "bash"].includes(r.name));
|
|
100
|
+
for (const rule of nonFileTools) {
|
|
101
|
+
if (rule.type === "tool") {
|
|
102
|
+
warnings.push({
|
|
103
|
+
rule,
|
|
104
|
+
reason: `Codex does not have a dedicated "${rule.name}" tool permission`,
|
|
105
|
+
suggestions: [
|
|
106
|
+
`Tool "${rule.name}" may not be available in Codex`,
|
|
107
|
+
"Check Codex documentation for available tools",
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// If there are errors, abort
|
|
114
|
+
if (errors.length > 0) {
|
|
115
|
+
return { ok: false, errors };
|
|
116
|
+
}
|
|
117
|
+
// Create CODEX_HOME directory structure
|
|
118
|
+
const rulesDirectory = path.join(output, "rules");
|
|
119
|
+
mkdirSync(rulesDirectory, { recursive: true });
|
|
120
|
+
// Infer sandbox mode
|
|
121
|
+
const sandboxMode = permissions ? inferSandboxMode(permissions) : "read-only";
|
|
122
|
+
// Generate config.toml
|
|
123
|
+
const configToml = `# Generated by axconfig
|
|
124
|
+
approval_policy = "never"
|
|
125
|
+
sandbox_mode = "${sandboxMode}"
|
|
126
|
+
`;
|
|
127
|
+
writeFileSync(path.join(output, "config.toml"), configToml);
|
|
128
|
+
// Generate execpolicy rules
|
|
129
|
+
const rules = ["# Generated by axconfig"];
|
|
130
|
+
if (permissions) {
|
|
131
|
+
// Collect bash patterns
|
|
132
|
+
const allowBash = permissions.allow
|
|
133
|
+
.filter((r) => r.type === "bash")
|
|
134
|
+
.map((r) => r.pattern);
|
|
135
|
+
const denyBash = permissions.deny
|
|
136
|
+
.filter((r) => r.type === "bash")
|
|
137
|
+
.map((r) => r.pattern);
|
|
138
|
+
// Generate allow rules
|
|
139
|
+
for (const pattern of allowBash) {
|
|
140
|
+
rules.push(generatePrefixRule(pattern, "allow"));
|
|
141
|
+
}
|
|
142
|
+
// Generate deny rules
|
|
143
|
+
for (const pattern of denyBash) {
|
|
144
|
+
rules.push(generatePrefixRule(pattern, "forbidden"));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Write rules file
|
|
148
|
+
const rulesPath = path.join(rulesDirectory, "axconfig.rules");
|
|
149
|
+
writeFileSync(rulesPath, rules.join("\n"));
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
env: { CODEX_HOME: output },
|
|
153
|
+
warnings,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** Codex ConfigBuilder */
|
|
157
|
+
const codexConfigBuilder = {
|
|
158
|
+
agentId: "codex",
|
|
159
|
+
capabilities: CAPABILITIES,
|
|
160
|
+
build,
|
|
161
|
+
};
|
|
162
|
+
// Self-register on import
|
|
163
|
+
registerConfigBuilder(codexConfigBuilder);
|
|
164
|
+
export { codexConfigBuilder };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI ConfigBuilder.
|
|
3
|
+
*
|
|
4
|
+
* Translates AxrunConfig into Gemini CLI's TOML policy format.
|
|
5
|
+
*
|
|
6
|
+
* Gemini CLI supports:
|
|
7
|
+
* - Tool permissions via [[rule]] with toolName
|
|
8
|
+
* - Bash patterns via commandPrefix
|
|
9
|
+
* - Does NOT support path restrictions
|
|
10
|
+
*/
|
|
11
|
+
import type { ConfigBuilder } from "../types.js";
|
|
12
|
+
/** Gemini CLI ConfigBuilder */
|
|
13
|
+
declare const geminiConfigBuilder: ConfigBuilder;
|
|
14
|
+
export { geminiConfigBuilder };
|