@geekbeer/minion 2.58.1 → 2.60.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/core/lib/permissions.js +279 -0
- package/core/routes/permissions.js +93 -0
- package/docs/api-reference.md +40 -0
- package/linux/minion-cli.sh +3 -0
- package/linux/server.js +2 -0
- package/package.json +1 -1
- package/rules/core.md +22 -1
- package/win/server.js +2 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Permission Abstraction Layer
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for reading/writing permission configurations
|
|
5
|
+
* across multiple AI coding CLI tools:
|
|
6
|
+
* - Claude Code: ~/.claude/settings.local.json (JSON)
|
|
7
|
+
* - Gemini CLI: ~/.gemini/settings.json (JSON)
|
|
8
|
+
* - Codex CLI: ~/.codex/config.toml (TOML)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs')
|
|
12
|
+
const path = require('path')
|
|
13
|
+
|
|
14
|
+
// ── CLI Definitions ──────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const CLI_DEFS = {
|
|
17
|
+
'claude-code': {
|
|
18
|
+
label: 'Claude Code',
|
|
19
|
+
configDir: '.claude',
|
|
20
|
+
// Read from settings.json (bundled sync target) + merge settings.local.json (user overrides)
|
|
21
|
+
readFiles: ['settings.json', 'settings.local.json'],
|
|
22
|
+
// Write to settings.local.json to survive server restarts (settings.json is overwritten by syncPermissions)
|
|
23
|
+
writeFile: 'settings.local.json',
|
|
24
|
+
format: 'json',
|
|
25
|
+
},
|
|
26
|
+
'gemini': {
|
|
27
|
+
label: 'Gemini CLI',
|
|
28
|
+
configDir: '.gemini',
|
|
29
|
+
readFiles: ['settings.json'],
|
|
30
|
+
writeFile: 'settings.json',
|
|
31
|
+
format: 'json',
|
|
32
|
+
},
|
|
33
|
+
'codex': {
|
|
34
|
+
label: 'Codex CLI',
|
|
35
|
+
configDir: '.codex',
|
|
36
|
+
readFiles: ['config.toml'],
|
|
37
|
+
writeFile: 'config.toml',
|
|
38
|
+
format: 'toml',
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── TOML Helpers (minimal, for Codex config.toml) ────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse a minimal TOML file to extract permissions.allow and permissions.deny arrays.
|
|
46
|
+
* Only supports the subset needed for Codex CLI config.
|
|
47
|
+
*/
|
|
48
|
+
function parseTomlPermissions(content) {
|
|
49
|
+
const allow = []
|
|
50
|
+
const deny = []
|
|
51
|
+
|
|
52
|
+
let currentSection = ''
|
|
53
|
+
for (const rawLine of content.split('\n')) {
|
|
54
|
+
const line = rawLine.trim()
|
|
55
|
+
if (!line || line.startsWith('#')) continue
|
|
56
|
+
|
|
57
|
+
// Section header: [permissions] or [permissions.default] etc.
|
|
58
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/)
|
|
59
|
+
if (sectionMatch) {
|
|
60
|
+
currentSection = sectionMatch[1]
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (currentSection !== 'permissions') continue
|
|
65
|
+
|
|
66
|
+
// Key = value
|
|
67
|
+
const kvMatch = line.match(/^(\w+)\s*=\s*(.+)$/)
|
|
68
|
+
if (!kvMatch) continue
|
|
69
|
+
const [, key, rawValue] = kvMatch
|
|
70
|
+
|
|
71
|
+
if (key === 'allow' || key === 'deny') {
|
|
72
|
+
const arr = parseTomlArray(rawValue)
|
|
73
|
+
if (key === 'allow') allow.push(...arr)
|
|
74
|
+
else deny.push(...arr)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { allow, deny }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse a TOML inline array: ["a", "b", "c"]
|
|
83
|
+
*/
|
|
84
|
+
function parseTomlArray(raw) {
|
|
85
|
+
const trimmed = raw.trim()
|
|
86
|
+
if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) return []
|
|
87
|
+
const inner = trimmed.slice(1, -1)
|
|
88
|
+
const items = []
|
|
89
|
+
// Match quoted strings
|
|
90
|
+
const re = /"([^"]*?)"|'([^']*?)'/g
|
|
91
|
+
let m
|
|
92
|
+
while ((m = re.exec(inner)) !== null) {
|
|
93
|
+
items.push(m[1] ?? m[2])
|
|
94
|
+
}
|
|
95
|
+
return items
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Write permissions into a TOML file, preserving non-permissions content.
|
|
100
|
+
*/
|
|
101
|
+
function writeTomlPermissions(existingContent, { allow, deny }) {
|
|
102
|
+
const lines = existingContent ? existingContent.split('\n') : []
|
|
103
|
+
const outputLines = []
|
|
104
|
+
let inPermissionsSection = false
|
|
105
|
+
let permissionsWritten = false
|
|
106
|
+
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
const trimmed = line.trim()
|
|
109
|
+
const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/)
|
|
110
|
+
|
|
111
|
+
if (sectionMatch) {
|
|
112
|
+
if (sectionMatch[1] === 'permissions') {
|
|
113
|
+
inPermissionsSection = true
|
|
114
|
+
// Write our new permissions section
|
|
115
|
+
outputLines.push('[permissions]')
|
|
116
|
+
outputLines.push(`allow = [${allow.map(s => `"${s}"`).join(', ')}]`)
|
|
117
|
+
outputLines.push(`deny = [${deny.map(s => `"${s}"`).join(', ')}]`)
|
|
118
|
+
permissionsWritten = true
|
|
119
|
+
continue
|
|
120
|
+
} else {
|
|
121
|
+
inPermissionsSection = false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (inPermissionsSection) continue // skip old permission lines
|
|
126
|
+
outputLines.push(line)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// If no existing [permissions] section, append one
|
|
130
|
+
if (!permissionsWritten) {
|
|
131
|
+
if (outputLines.length > 0 && outputLines[outputLines.length - 1] !== '') {
|
|
132
|
+
outputLines.push('')
|
|
133
|
+
}
|
|
134
|
+
outputLines.push('[permissions]')
|
|
135
|
+
outputLines.push(`allow = [${allow.map(s => `"${s}"`).join(', ')}]`)
|
|
136
|
+
outputLines.push(`deny = [${deny.map(s => `"${s}"`).join(', ')}]`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return outputLines.join('\n')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── JSON Helpers ─────────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
function readJsonPermissions(filePath) {
|
|
145
|
+
try {
|
|
146
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
147
|
+
const data = JSON.parse(content)
|
|
148
|
+
const perms = data.permissions || {}
|
|
149
|
+
return {
|
|
150
|
+
allow: Array.isArray(perms.allow) ? perms.allow : [],
|
|
151
|
+
deny: Array.isArray(perms.deny) ? perms.deny : [],
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
return { allow: [], deny: [] }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function writeJsonPermissions(filePath, { allow, deny }) {
|
|
159
|
+
let data = {}
|
|
160
|
+
try {
|
|
161
|
+
data = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
|
|
162
|
+
} catch {
|
|
163
|
+
// File doesn't exist or is invalid — start fresh
|
|
164
|
+
}
|
|
165
|
+
data.permissions = { allow, deny }
|
|
166
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
167
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Detect which CLIs have config directories present.
|
|
174
|
+
* @param {string} homeDir
|
|
175
|
+
* @returns {string[]} Array of cli_type strings
|
|
176
|
+
*/
|
|
177
|
+
function detectInstalledClis(homeDir) {
|
|
178
|
+
return Object.entries(CLI_DEFS)
|
|
179
|
+
.filter(([, def]) => fs.existsSync(path.join(homeDir, def.configDir)))
|
|
180
|
+
.map(([type]) => type)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Read effective permissions for a specific CLI.
|
|
185
|
+
* For Claude Code, merges settings.json + settings.local.json.
|
|
186
|
+
* @param {string} homeDir
|
|
187
|
+
* @param {string} cliType
|
|
188
|
+
* @returns {{ cli_type: string, label: string, allow: string[], deny: string[], config_path: string } | null}
|
|
189
|
+
*/
|
|
190
|
+
function getPermissions(homeDir, cliType) {
|
|
191
|
+
const def = CLI_DEFS[cliType]
|
|
192
|
+
if (!def) return null
|
|
193
|
+
|
|
194
|
+
const configDir = path.join(homeDir, def.configDir)
|
|
195
|
+
if (!fs.existsSync(configDir)) return null
|
|
196
|
+
|
|
197
|
+
let mergedAllow = []
|
|
198
|
+
let mergedDeny = []
|
|
199
|
+
|
|
200
|
+
for (const file of def.readFiles) {
|
|
201
|
+
const filePath = path.join(configDir, file)
|
|
202
|
+
if (!fs.existsSync(filePath)) continue
|
|
203
|
+
|
|
204
|
+
if (def.format === 'json') {
|
|
205
|
+
const perms = readJsonPermissions(filePath)
|
|
206
|
+
// Later files override earlier ones (settings.local.json overrides settings.json)
|
|
207
|
+
if (perms.allow.length > 0 || perms.deny.length > 0) {
|
|
208
|
+
mergedAllow = perms.allow
|
|
209
|
+
mergedDeny = perms.deny
|
|
210
|
+
}
|
|
211
|
+
} else if (def.format === 'toml') {
|
|
212
|
+
try {
|
|
213
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
214
|
+
const perms = parseTomlPermissions(content)
|
|
215
|
+
if (perms.allow.length > 0 || perms.deny.length > 0) {
|
|
216
|
+
mergedAllow = perms.allow
|
|
217
|
+
mergedDeny = perms.deny
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
// Skip unreadable files
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
cli_type: cliType,
|
|
227
|
+
label: def.label,
|
|
228
|
+
allow: mergedAllow,
|
|
229
|
+
deny: mergedDeny,
|
|
230
|
+
config_path: path.join(configDir, def.writeFile),
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Read permissions for all detected CLIs.
|
|
236
|
+
* @param {string} homeDir
|
|
237
|
+
* @returns {Array<{ cli_type: string, label: string, allow: string[], deny: string[], config_path: string }>}
|
|
238
|
+
*/
|
|
239
|
+
function getAllPermissions(homeDir) {
|
|
240
|
+
const results = []
|
|
241
|
+
for (const cliType of Object.keys(CLI_DEFS)) {
|
|
242
|
+
const perms = getPermissions(homeDir, cliType)
|
|
243
|
+
if (perms) results.push(perms)
|
|
244
|
+
}
|
|
245
|
+
return results
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Update permissions for a specific CLI.
|
|
250
|
+
* @param {string} homeDir
|
|
251
|
+
* @param {string} cliType
|
|
252
|
+
* @param {{ allow: string[], deny: string[] }} permissions
|
|
253
|
+
*/
|
|
254
|
+
function setPermissions(homeDir, cliType, { allow, deny }) {
|
|
255
|
+
const def = CLI_DEFS[cliType]
|
|
256
|
+
if (!def) throw new Error(`Unsupported CLI type: ${cliType}`)
|
|
257
|
+
|
|
258
|
+
const configDir = path.join(homeDir, def.configDir)
|
|
259
|
+
const filePath = path.join(configDir, def.writeFile)
|
|
260
|
+
|
|
261
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
262
|
+
|
|
263
|
+
if (def.format === 'json') {
|
|
264
|
+
writeJsonPermissions(filePath, { allow, deny })
|
|
265
|
+
} else if (def.format === 'toml') {
|
|
266
|
+
let existing = ''
|
|
267
|
+
try { existing = fs.readFileSync(filePath, 'utf-8') } catch {}
|
|
268
|
+
const newContent = writeTomlPermissions(existing, { allow, deny })
|
|
269
|
+
fs.writeFileSync(filePath, newContent, 'utf-8')
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
module.exports = {
|
|
274
|
+
detectInstalledClis,
|
|
275
|
+
getPermissions,
|
|
276
|
+
getAllPermissions,
|
|
277
|
+
setPermissions,
|
|
278
|
+
CLI_DEFS,
|
|
279
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission management endpoints
|
|
3
|
+
*
|
|
4
|
+
* Provides read/write access to CLI tool permission configurations.
|
|
5
|
+
* Supports Claude Code, Gemini CLI, and Codex CLI.
|
|
6
|
+
*
|
|
7
|
+
* Endpoints:
|
|
8
|
+
* GET /api/config/permissions - Get permissions for all detected CLIs
|
|
9
|
+
* GET /api/config/permissions/:cli_type - Get permissions for a specific CLI
|
|
10
|
+
* POST /api/config/permissions - Update permissions for a specific CLI
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { verifyToken } = require('../lib/auth')
|
|
14
|
+
const { config } = require('../config')
|
|
15
|
+
const { getAllPermissions, getPermissions, setPermissions, CLI_DEFS } = require('../lib/permissions')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register permission routes as Fastify plugin
|
|
19
|
+
* @param {import('fastify').FastifyInstance} fastify
|
|
20
|
+
*/
|
|
21
|
+
async function permissionRoutes(fastify) {
|
|
22
|
+
|
|
23
|
+
// GET /api/config/permissions - Get permissions for all detected CLIs
|
|
24
|
+
fastify.get('/api/config/permissions', async (request, reply) => {
|
|
25
|
+
if (!verifyToken(request)) {
|
|
26
|
+
reply.code(401)
|
|
27
|
+
return { success: false, error: 'Unauthorized' }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const permissions = getAllPermissions(config.HOME_DIR)
|
|
31
|
+
return { success: true, permissions }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// GET /api/config/permissions/:cli_type - Get permissions for a specific CLI
|
|
35
|
+
fastify.get('/api/config/permissions/:cli_type', async (request, reply) => {
|
|
36
|
+
if (!verifyToken(request)) {
|
|
37
|
+
reply.code(401)
|
|
38
|
+
return { success: false, error: 'Unauthorized' }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { cli_type } = request.params
|
|
42
|
+
if (!CLI_DEFS[cli_type]) {
|
|
43
|
+
reply.code(400)
|
|
44
|
+
return { success: false, error: `Unsupported CLI type: ${cli_type}. Supported: ${Object.keys(CLI_DEFS).join(', ')}` }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const perms = getPermissions(config.HOME_DIR, cli_type)
|
|
48
|
+
if (!perms) {
|
|
49
|
+
reply.code(404)
|
|
50
|
+
return { success: false, error: `CLI ${cli_type} is not installed on this minion` }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { success: true, ...perms }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// POST /api/config/permissions - Update permissions for a specific CLI
|
|
57
|
+
fastify.post('/api/config/permissions', async (request, reply) => {
|
|
58
|
+
if (!verifyToken(request)) {
|
|
59
|
+
reply.code(401)
|
|
60
|
+
return { success: false, error: 'Unauthorized' }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { cli_type, allow, deny } = request.body || {}
|
|
64
|
+
|
|
65
|
+
if (!cli_type) {
|
|
66
|
+
reply.code(400)
|
|
67
|
+
return { success: false, error: 'cli_type is required' }
|
|
68
|
+
}
|
|
69
|
+
if (!CLI_DEFS[cli_type]) {
|
|
70
|
+
reply.code(400)
|
|
71
|
+
return { success: false, error: `Unsupported CLI type: ${cli_type}. Supported: ${Object.keys(CLI_DEFS).join(', ')}` }
|
|
72
|
+
}
|
|
73
|
+
if (!Array.isArray(allow) || !Array.isArray(deny)) {
|
|
74
|
+
reply.code(400)
|
|
75
|
+
return { success: false, error: 'allow and deny must be arrays' }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
setPermissions(config.HOME_DIR, cli_type, { allow, deny })
|
|
80
|
+
|
|
81
|
+
// Read back effective permissions to confirm
|
|
82
|
+
const updated = getPermissions(config.HOME_DIR, cli_type)
|
|
83
|
+
console.log(`[Permissions] Updated ${cli_type}: allow=${allow.length}, deny=${deny.length}`)
|
|
84
|
+
|
|
85
|
+
return { success: true, ...updated }
|
|
86
|
+
} catch (err) {
|
|
87
|
+
reply.code(500)
|
|
88
|
+
return { success: false, error: err.message }
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { permissionRoutes }
|
package/docs/api-reference.md
CHANGED
|
@@ -204,6 +204,46 @@ Changes via the config API take effect immediately (no restart required).
|
|
|
204
204
|
|
|
205
205
|
Allowed keys: `LLM_COMMAND`, `REFLECTION_TIME`
|
|
206
206
|
|
|
207
|
+
### Permissions
|
|
208
|
+
|
|
209
|
+
CLIツール(Claude Code, Gemini CLI, Codex CLI)のパーミッション管理。
|
|
210
|
+
CLIツールは自身の設定ファイルを直接編集できないため、このAPIを経由して更新する。
|
|
211
|
+
|
|
212
|
+
| Method | Endpoint | Description |
|
|
213
|
+
|--------|----------|-------------|
|
|
214
|
+
| GET | `/api/config/permissions` | Get permissions for all detected CLIs |
|
|
215
|
+
| GET | `/api/config/permissions/:cli_type` | Get permissions for a specific CLI |
|
|
216
|
+
| POST | `/api/config/permissions` | Update permissions. Body: `{cli_type, allow, deny}` |
|
|
217
|
+
|
|
218
|
+
Supported `cli_type`: `claude-code`, `gemini`, `codex`
|
|
219
|
+
|
|
220
|
+
**GET response**:
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"success": true,
|
|
224
|
+
"permissions": [
|
|
225
|
+
{
|
|
226
|
+
"cli_type": "claude-code",
|
|
227
|
+
"label": "Claude Code",
|
|
228
|
+
"allow": ["Bash", "Read", "Write", "Edit"],
|
|
229
|
+
"deny": ["Bash(sudo *)"],
|
|
230
|
+
"config_path": "/home/minion/.claude/settings.local.json"
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**POST body**:
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"cli_type": "claude-code",
|
|
240
|
+
"allow": ["Bash", "Read", "Write", "Edit", "WebSearch"],
|
|
241
|
+
"deny": ["Bash(sudo *)", "Bash(rm -rf *)"]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Note: Claude Code の場合、書き込み先は `settings.local.json`(サーバー再起動時に上書きされない)。
|
|
246
|
+
|
|
207
247
|
### Commands
|
|
208
248
|
|
|
209
249
|
| Method | Endpoint | Description |
|
package/linux/minion-cli.sh
CHANGED
|
@@ -320,6 +320,9 @@ do_setup() {
|
|
|
320
320
|
local SUDOERS_FILE="/etc/sudoers.d/minion-agent"
|
|
321
321
|
|
|
322
322
|
{
|
|
323
|
+
# Fail immediately instead of prompting for password (safety net for LLM agents)
|
|
324
|
+
echo "Defaults:${TARGET_USER} passwd_tries=0"
|
|
325
|
+
echo ""
|
|
323
326
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: ${NPM_BIN} install -g @geekbeer/minion@latest"
|
|
324
327
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: ${NPM_BIN} install -g @geekbeer/minion@latest --registry *"
|
|
325
328
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt-get install *"
|
package/linux/server.js
CHANGED
|
@@ -67,6 +67,7 @@ const { variableRoutes } = require('../core/routes/variables')
|
|
|
67
67
|
const { memoryRoutes } = require('../core/routes/memory')
|
|
68
68
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
69
69
|
const { sudoersRoutes } = require('../core/routes/sudoers')
|
|
70
|
+
const { permissionRoutes } = require('../core/routes/permissions')
|
|
70
71
|
|
|
71
72
|
// Linux-specific routes
|
|
72
73
|
const { commandRoutes, getProcessManager, getAllowedCommands } = require('./routes/commands')
|
|
@@ -268,6 +269,7 @@ async function registerAllRoutes(app) {
|
|
|
268
269
|
await app.register(memoryRoutes)
|
|
269
270
|
await app.register(dailyLogRoutes)
|
|
270
271
|
await app.register(sudoersRoutes)
|
|
272
|
+
await app.register(permissionRoutes)
|
|
271
273
|
|
|
272
274
|
// Linux-specific routes
|
|
273
275
|
await app.register(commandRoutes)
|
package/package.json
CHANGED
package/rules/core.md
CHANGED
|
@@ -7,6 +7,7 @@ You are an AI agent running on a Minion VPS, managed by @geekbeer/minion.
|
|
|
7
7
|
- **HQサーバーのソースコードを読みに行く必要はありません。** 必要なAPI仕様はすべてドキュメントに記載されています。
|
|
8
8
|
- API の詳細仕様が必要な場合は `~/.minion/docs/api-reference.md` を参照してください。
|
|
9
9
|
- タスクの手順が不明な場合は `~/.minion/docs/task-guides.md` を参照してください。
|
|
10
|
+
- **`sudo` を使用しないこと。** 必要なツール・ライブラリはすべてセットアップ済みです。`sudo` はパスワード未設定のため実行できません。パッケージのインストールや環境構築が必要に見える場合でも、既にインストール済みであることを前提に作業を進めてください。
|
|
10
11
|
|
|
11
12
|
## Architecture
|
|
12
13
|
|
|
@@ -44,7 +45,27 @@ minion-cli --version # バージョン確認
|
|
|
44
45
|
|
|
45
46
|
`http://localhost:8080` — 認証: `Authorization: Bearer $API_TOKEN`
|
|
46
47
|
|
|
47
|
-
主なカテゴリ: Health, Skills, Workflows, Executions, Terminal, Files, Commands
|
|
48
|
+
主なカテゴリ: Health, Skills, Workflows, Executions, Terminal, Files, Commands, Permissions
|
|
49
|
+
|
|
50
|
+
#### Permission Management
|
|
51
|
+
|
|
52
|
+
CLIツール(Claude Code, Gemini CLI, Codex CLI)のパーミッション(allow/denyリスト)をAPI経由で読み書きできる。
|
|
53
|
+
CLIツールは自身の設定ファイルを直接編集できないため、このAPIを使用すること。
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 全CLIのパーミッション取得
|
|
57
|
+
curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8080/api/config/permissions
|
|
58
|
+
|
|
59
|
+
# 特定CLIのパーミッション取得
|
|
60
|
+
curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8080/api/config/permissions/claude-code
|
|
61
|
+
|
|
62
|
+
# パーミッション更新
|
|
63
|
+
curl -X POST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
|
|
64
|
+
http://localhost:8080/api/config/permissions \
|
|
65
|
+
-d '{"cli_type": "claude-code", "allow": ["Bash", "Read", "Write", "Edit"], "deny": ["Bash(sudo *)"]}'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
対応CLI: `claude-code` (.claude/settings.local.json), `gemini` (.gemini/settings.json), `codex` (.codex/config.toml)
|
|
48
69
|
|
|
49
70
|
### HQ API
|
|
50
71
|
|
package/win/server.js
CHANGED
|
@@ -50,6 +50,7 @@ const { authRoutes } = require('../core/routes/auth')
|
|
|
50
50
|
const { variableRoutes } = require('../core/routes/variables')
|
|
51
51
|
const { memoryRoutes } = require('../core/routes/memory')
|
|
52
52
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
53
|
+
const { permissionRoutes } = require('../core/routes/permissions')
|
|
53
54
|
|
|
54
55
|
// Validate configuration
|
|
55
56
|
validate()
|
|
@@ -201,6 +202,7 @@ async function registerRoutes(app) {
|
|
|
201
202
|
await app.register(variableRoutes)
|
|
202
203
|
await app.register(memoryRoutes)
|
|
203
204
|
await app.register(dailyLogRoutes)
|
|
205
|
+
await app.register(permissionRoutes)
|
|
204
206
|
|
|
205
207
|
// Windows-specific routes
|
|
206
208
|
await app.register(commandRoutes)
|