axconfig 3.5.1 → 3.6.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/dist/agents/claude-reader.js +4 -4
- package/dist/agents/claude.js +9 -2
- package/dist/agents/codex-reader.js +4 -2
- package/dist/agents/codex.js +7 -3
- package/dist/agents/gemini-reader.js +3 -1
- package/dist/agents/gemini.js +26 -11
- package/dist/parse-permissions.d.ts +5 -2
- package/dist/parse-permissions.js +10 -2
- package/package.json +1 -1
|
@@ -45,11 +45,11 @@ function parseClaudeRule(rule) {
|
|
|
45
45
|
if (parenIndex > 0 && rule.endsWith(")")) {
|
|
46
46
|
const toolName = rule.slice(0, parenIndex);
|
|
47
47
|
const pattern = rule.slice(parenIndex + 1, -1);
|
|
48
|
-
// Bash pattern: "Bash(git:*)" → pattern is "git:*", we want "git"
|
|
48
|
+
// Bash pattern: "Bash(git:*)" → pattern is "git:*", we want "git*"
|
|
49
|
+
// Append * to indicate prefix matching (required by axconfig's canonical format)
|
|
49
50
|
if (toolName === "Bash") {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
: pattern;
|
|
51
|
+
const prefix = pattern.endsWith(":*") ? pattern.slice(0, -2) : pattern;
|
|
52
|
+
const bashPattern = prefix === "" ? "*" : `${prefix}*`;
|
|
53
53
|
return { type: "bash", pattern: bashPattern };
|
|
54
54
|
}
|
|
55
55
|
// Path pattern: "Read(src/**)" → tool is "read", pattern is "src/**"
|
package/dist/agents/claude.js
CHANGED
|
@@ -40,8 +40,15 @@ function translateRule(rule) {
|
|
|
40
40
|
return TOOL_MAP[rule.name];
|
|
41
41
|
}
|
|
42
42
|
case "bash": {
|
|
43
|
-
//
|
|
44
|
-
|
|
43
|
+
// User provides pattern with explicit trailing *
|
|
44
|
+
// Strip it to get the prefix for Claude format
|
|
45
|
+
const prefix = rule.pattern.slice(0, -1);
|
|
46
|
+
// bash:* (all commands) → Bash (without parentheses)
|
|
47
|
+
// bash:git* → Bash(git:*)
|
|
48
|
+
if (prefix === "" || prefix.trim() === "") {
|
|
49
|
+
return "Bash";
|
|
50
|
+
}
|
|
51
|
+
return `Bash(${prefix}:*)`;
|
|
45
52
|
}
|
|
46
53
|
case "path": {
|
|
47
54
|
// Claude Code uses "Tool(path/**)" for path patterns
|
|
@@ -90,12 +90,14 @@ function readPermissions(configDirectory) {
|
|
|
90
90
|
// Read is always allowed in Codex sandbox
|
|
91
91
|
allowRules.push({ type: "tool", name: "read" });
|
|
92
92
|
// Parse bash patterns from rules files
|
|
93
|
+
// Append * to indicate prefix matching (required by axconfig's canonical format)
|
|
93
94
|
for (const rule of rules) {
|
|
95
|
+
const pattern = rule.pattern === "" ? "*" : `${rule.pattern}*`;
|
|
94
96
|
if (rule.decision === "allow") {
|
|
95
|
-
allowRules.push({ type: "bash", pattern
|
|
97
|
+
allowRules.push({ type: "bash", pattern });
|
|
96
98
|
}
|
|
97
99
|
else {
|
|
98
|
-
denyRules.push({ type: "bash", pattern
|
|
100
|
+
denyRules.push({ type: "bash", pattern });
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
const permConfig = {
|
package/dist/agents/codex.js
CHANGED
|
@@ -146,13 +146,17 @@ function build(config, output) {
|
|
|
146
146
|
// Generate execpolicy rules
|
|
147
147
|
const rules = ["# Generated by axconfig"];
|
|
148
148
|
if (permissions) {
|
|
149
|
-
// Collect bash patterns
|
|
149
|
+
// Collect bash patterns, stripping trailing * since Codex does prefix matching natively
|
|
150
|
+
// Filter out empty patterns (from bash:*) - Codex doesn't support empty prefix
|
|
151
|
+
// bash:* means "all commands" which is the default when no rules exist
|
|
150
152
|
const allowBash = permissions.allow
|
|
151
153
|
.filter((r) => r.type === "bash")
|
|
152
|
-
.map((r) => r.pattern)
|
|
154
|
+
.map((r) => r.pattern.slice(0, -1).trim())
|
|
155
|
+
.filter((p) => p !== "");
|
|
153
156
|
const denyBash = permissions.deny
|
|
154
157
|
.filter((r) => r.type === "bash")
|
|
155
|
-
.map((r) => r.pattern)
|
|
158
|
+
.map((r) => r.pattern.slice(0, -1).trim())
|
|
159
|
+
.filter((p) => p !== "");
|
|
156
160
|
// Generate allow rules
|
|
157
161
|
for (const pattern of allowBash) {
|
|
158
162
|
rules.push(generatePrefixRule(pattern, "allow"));
|
|
@@ -73,12 +73,14 @@ function readPermissions(configDirectory) {
|
|
|
73
73
|
? rule.toolName
|
|
74
74
|
: [rule.toolName];
|
|
75
75
|
// Check for bash command patterns
|
|
76
|
+
// Append * to indicate prefix matching (required by axconfig's canonical format)
|
|
76
77
|
if (rule.commandPrefix) {
|
|
77
78
|
const prefixes = Array.isArray(rule.commandPrefix)
|
|
78
79
|
? rule.commandPrefix
|
|
79
80
|
: [rule.commandPrefix];
|
|
80
81
|
for (const prefix of prefixes) {
|
|
81
|
-
|
|
82
|
+
const pattern = prefix === "" ? "*" : `${prefix}*`;
|
|
83
|
+
targetList.push({ type: "bash", pattern });
|
|
82
84
|
}
|
|
83
85
|
continue;
|
|
84
86
|
}
|
package/dist/agents/gemini.js
CHANGED
|
@@ -112,23 +112,38 @@ function build(config, output) {
|
|
|
112
112
|
const denyTools = permissions.deny
|
|
113
113
|
.filter((r) => r.type === "tool")
|
|
114
114
|
.map((r) => TOOL_MAP[r.name]);
|
|
115
|
-
// Collect bash patterns
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
// Collect bash patterns, stripping trailing * since Gemini does prefix matching natively
|
|
116
|
+
// bash:* (empty prefix after strip) → allow run_shell_command tool entirely
|
|
117
|
+
// bash:git* → commandPrefix = "git"
|
|
118
|
+
const allowBashPatterns = permissions.allow.filter((r) => r.type === "bash");
|
|
119
|
+
const denyBashPatterns = permissions.deny.filter((r) => r.type === "bash");
|
|
120
|
+
// Separate bash:* (all commands) from specific patterns
|
|
121
|
+
const allowAllBash = allowBashPatterns.some((r) => r.pattern.slice(0, -1).trim() === "");
|
|
122
|
+
const denyAllBash = denyBashPatterns.some((r) => r.pattern.slice(0, -1).trim() === "");
|
|
123
|
+
const allowBash = allowBashPatterns
|
|
124
|
+
.map((r) => r.pattern.slice(0, -1).trim())
|
|
125
|
+
.filter((p) => p !== "");
|
|
126
|
+
const denyBash = denyBashPatterns
|
|
127
|
+
.map((r) => r.pattern.slice(0, -1).trim())
|
|
128
|
+
.filter((p) => p !== "");
|
|
122
129
|
// Generate allow rules (high priority)
|
|
123
|
-
if
|
|
124
|
-
|
|
130
|
+
// Include run_shell_command if bash:* is in allow list
|
|
131
|
+
const effectiveAllowTools = allowAllBash
|
|
132
|
+
? [...allowTools, TOOL_MAP.bash]
|
|
133
|
+
: allowTools;
|
|
134
|
+
if (effectiveAllowTools.length > 0) {
|
|
135
|
+
rules.push(generateToolRule(effectiveAllowTools, "allow", 999));
|
|
125
136
|
}
|
|
126
137
|
if (allowBash.length > 0) {
|
|
127
138
|
rules.push(generateBashRule(allowBash, "allow", 998));
|
|
128
139
|
}
|
|
129
140
|
// Generate deny rules (medium priority)
|
|
130
|
-
if
|
|
131
|
-
|
|
141
|
+
// Include run_shell_command if bash:* is in deny list
|
|
142
|
+
const effectiveDenyTools = denyAllBash
|
|
143
|
+
? [...denyTools, TOOL_MAP.bash]
|
|
144
|
+
: denyTools;
|
|
145
|
+
if (effectiveDenyTools.length > 0) {
|
|
146
|
+
rules.push(generateToolRule(effectiveDenyTools, "deny", 500));
|
|
132
147
|
}
|
|
133
148
|
if (denyBash.length > 0) {
|
|
134
149
|
rules.push(generateBashRule(denyBash, "deny", 499));
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Parses --allow and --deny CLI arguments into PermissionConfig.
|
|
5
5
|
*
|
|
6
|
+
* Bash patterns require an explicit trailing wildcard (*) to indicate
|
|
7
|
+
* prefix matching. Patterns without wildcards will throw an error.
|
|
8
|
+
*
|
|
6
9
|
* @example
|
|
7
10
|
* parsePermissions(
|
|
8
11
|
* ["read,glob,bash:git *"],
|
|
9
|
-
* ["bash:rm
|
|
12
|
+
* ["bash:rm*"]
|
|
10
13
|
* )
|
|
11
14
|
* // Returns:
|
|
12
15
|
* // {
|
|
@@ -16,7 +19,7 @@
|
|
|
16
19
|
* // { type: "bash", pattern: "git *" }
|
|
17
20
|
* // ],
|
|
18
21
|
* // deny: [
|
|
19
|
-
* // { type: "bash", pattern: "rm
|
|
22
|
+
* // { type: "bash", pattern: "rm*" }
|
|
20
23
|
* // ]
|
|
21
24
|
* // }
|
|
22
25
|
*/
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Parses --allow and --deny CLI arguments into PermissionConfig.
|
|
5
5
|
*
|
|
6
|
+
* Bash patterns require an explicit trailing wildcard (*) to indicate
|
|
7
|
+
* prefix matching. Patterns without wildcards will throw an error.
|
|
8
|
+
*
|
|
6
9
|
* @example
|
|
7
10
|
* parsePermissions(
|
|
8
11
|
* ["read,glob,bash:git *"],
|
|
9
|
-
* ["bash:rm
|
|
12
|
+
* ["bash:rm*"]
|
|
10
13
|
* )
|
|
11
14
|
* // Returns:
|
|
12
15
|
* // {
|
|
@@ -16,7 +19,7 @@
|
|
|
16
19
|
* // { type: "bash", pattern: "git *" }
|
|
17
20
|
* // ],
|
|
18
21
|
* // deny: [
|
|
19
|
-
* // { type: "bash", pattern: "rm
|
|
22
|
+
* // { type: "bash", pattern: "rm*" }
|
|
20
23
|
* // ]
|
|
21
24
|
* // }
|
|
22
25
|
*/
|
|
@@ -51,6 +54,11 @@ function parseRule(rule) {
|
|
|
51
54
|
if (pattern === "") {
|
|
52
55
|
throw new Error('Invalid bash pattern: "bash:" requires a command pattern');
|
|
53
56
|
}
|
|
57
|
+
if (!pattern.endsWith("*")) {
|
|
58
|
+
throw new Error(`Bash pattern "${pattern}" requires a trailing wildcard.\n` +
|
|
59
|
+
`Use "bash:${pattern}*" to match commands starting with "${pattern}".\n` +
|
|
60
|
+
`All bash patterns are prefix matches.`);
|
|
61
|
+
}
|
|
54
62
|
return { type: "bash", pattern };
|
|
55
63
|
}
|
|
56
64
|
// Check for path restriction: "read:src/**"
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "axconfig",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.6.0",
|
|
6
6
|
"description": "Unified configuration management for AI coding agents - common API for permissions, settings, and config across Claude Code, Codex, Gemini CLI, and OpenCode",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|