@geekbeer/minion 2.46.1 → 2.48.3
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/config.js +7 -0
- package/core/lib/end-of-day.js +113 -0
- package/core/lib/reflection-scheduler.js +157 -0
- package/core/routes/daily-logs.js +122 -0
- package/core/routes/memory.js +112 -0
- package/core/stores/daily-log-store.js +182 -0
- package/core/stores/memory-store.js +256 -0
- package/docs/api-reference.md +128 -0
- package/linux/minion-cli.sh +20 -0
- package/linux/routes/chat.js +48 -11
- package/linux/routes/config.js +15 -3
- package/linux/server.js +11 -2
- package/package.json +1 -1
- package/win/lib/process-manager.js +85 -8
- package/win/minion-cli.ps1 +8 -0
- package/win/routes/chat.js +47 -8
- package/win/routes/config.js +16 -3
- package/win/server.js +10 -1
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const { execSync } = require('child_process')
|
|
11
|
+
const fs = require('fs')
|
|
11
12
|
const path = require('path')
|
|
12
13
|
const os = require('os')
|
|
13
14
|
|
|
@@ -18,7 +19,6 @@ const os = require('os')
|
|
|
18
19
|
*/
|
|
19
20
|
function detectProcessManager() {
|
|
20
21
|
// Check for user-process mode (PID file or start-agent.ps1 exists)
|
|
21
|
-
const fs = require('fs')
|
|
22
22
|
const dataDir = path.join(os.homedir(), '.minion')
|
|
23
23
|
const startScript = path.join(dataDir, 'start-agent.ps1')
|
|
24
24
|
if (fs.existsSync(startScript)) {
|
|
@@ -40,6 +40,56 @@ function detectProcessManager() {
|
|
|
40
40
|
return 'user-process'
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Generate a temporary PowerShell script that:
|
|
45
|
+
* 1. Stops the running agent process (releasing file locks)
|
|
46
|
+
* 2. Runs npm install -g
|
|
47
|
+
* 3. Restarts the agent
|
|
48
|
+
*
|
|
49
|
+
* This is necessary because node-pty's conpty.node DLL is locked by the
|
|
50
|
+
* running process, causing EBUSY errors if npm tries to overwrite it in-place.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} npmInstallCmd - The npm install command to run
|
|
53
|
+
* @param {string} stopCmd - Command/script block to stop the agent
|
|
54
|
+
* @param {string} startCmd - Command/script block to start the agent
|
|
55
|
+
* @returns {string} - Shell command that writes and launches the update script
|
|
56
|
+
*/
|
|
57
|
+
function buildUpdateScript(npmInstallCmd, stopCmd, startCmd) {
|
|
58
|
+
const dataDir = path.join(os.homedir(), '.minion')
|
|
59
|
+
const scriptPath = path.join(dataDir, 'update-agent.ps1')
|
|
60
|
+
const logPath = path.join(dataDir, 'update-agent.log')
|
|
61
|
+
|
|
62
|
+
// PowerShell script content: stop → install → start, with logging
|
|
63
|
+
const ps1 = [
|
|
64
|
+
`$ErrorActionPreference = 'Stop'`,
|
|
65
|
+
`$logFile = '${logPath.replace(/\\/g, '\\\\')}'`,
|
|
66
|
+
`function Log($msg) { "$(Get-Date -Format o) $msg" | Out-File -Append $logFile }`,
|
|
67
|
+
`Log 'Update started'`,
|
|
68
|
+
`try {`,
|
|
69
|
+
` Log 'Stopping agent...'`,
|
|
70
|
+
` ${stopCmd}`,
|
|
71
|
+
` Start-Sleep -Seconds 3`,
|
|
72
|
+
` Log 'Installing package...'`,
|
|
73
|
+
` $out = & cmd /c "${npmInstallCmd} 2>&1"`,
|
|
74
|
+
` Log "npm output: $out"`,
|
|
75
|
+
` if ($LASTEXITCODE -ne 0) { throw "npm install failed (exit code $LASTEXITCODE)" }`,
|
|
76
|
+
` Log 'Starting agent...'`,
|
|
77
|
+
` ${startCmd}`,
|
|
78
|
+
` Log 'Update completed successfully'`,
|
|
79
|
+
`} catch {`,
|
|
80
|
+
` Log "Update failed: $_"`,
|
|
81
|
+
` Log 'Attempting to restart agent anyway...'`,
|
|
82
|
+
` ${startCmd}`,
|
|
83
|
+
`}`,
|
|
84
|
+
].join('\n')
|
|
85
|
+
|
|
86
|
+
// Write the script to disk and launch it detached
|
|
87
|
+
try { fs.mkdirSync(dataDir, { recursive: true }) } catch { /* exists */ }
|
|
88
|
+
fs.writeFileSync(scriptPath, ps1, 'utf-8')
|
|
89
|
+
|
|
90
|
+
return `powershell -ExecutionPolicy Bypass -Command "Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${scriptPath}\\"' -WindowStyle Hidden"`
|
|
91
|
+
}
|
|
92
|
+
|
|
43
93
|
/**
|
|
44
94
|
* Build allowed commands for the detected process manager.
|
|
45
95
|
* @param {string} procMgr - Process manager type
|
|
@@ -53,19 +103,30 @@ function buildAllowedCommands(procMgr) {
|
|
|
53
103
|
const pidFile = path.join(dataDir, 'minion-agent.pid')
|
|
54
104
|
const startScript = path.join(dataDir, 'start-agent.ps1')
|
|
55
105
|
|
|
106
|
+
const stopBlock = `$pid = Get-Content '${pidFile}' -ErrorAction SilentlyContinue; if ($pid) { Get-CimInstance Win32_Process -Filter \\"ParentProcessId = $pid\\" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue }`
|
|
107
|
+
const startBlock = `Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${startScript}\\"' -WindowStyle Hidden`
|
|
108
|
+
|
|
56
109
|
commands['restart-agent'] = {
|
|
57
110
|
description: 'Restart the minion agent process',
|
|
58
|
-
command: `powershell -Command "$
|
|
111
|
+
command: `powershell -Command "${stopBlock}; Start-Sleep -Seconds 2; ${startBlock}"`,
|
|
59
112
|
deferred: true,
|
|
60
113
|
}
|
|
61
114
|
commands['update-agent'] = {
|
|
62
115
|
description: 'Update @geekbeer/minion to latest version and restart',
|
|
63
|
-
command:
|
|
116
|
+
command: buildUpdateScript(
|
|
117
|
+
'npm install -g @geekbeer/minion@latest',
|
|
118
|
+
stopBlock,
|
|
119
|
+
startBlock,
|
|
120
|
+
),
|
|
64
121
|
deferred: true,
|
|
65
122
|
}
|
|
66
123
|
commands['update-agent-dev'] = {
|
|
67
124
|
description: 'Update @geekbeer/minion from Verdaccio (dev) and restart',
|
|
68
|
-
command:
|
|
125
|
+
command: buildUpdateScript(
|
|
126
|
+
'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873',
|
|
127
|
+
stopBlock,
|
|
128
|
+
startBlock,
|
|
129
|
+
),
|
|
69
130
|
deferred: true,
|
|
70
131
|
}
|
|
71
132
|
commands['status-services'] = {
|
|
@@ -80,12 +141,20 @@ function buildAllowedCommands(procMgr) {
|
|
|
80
141
|
}
|
|
81
142
|
commands['update-agent'] = {
|
|
82
143
|
description: 'Update @geekbeer/minion to latest version and restart',
|
|
83
|
-
command:
|
|
144
|
+
command: buildUpdateScript(
|
|
145
|
+
'npm install -g @geekbeer/minion@latest',
|
|
146
|
+
'nssm stop minion-agent',
|
|
147
|
+
'nssm start minion-agent',
|
|
148
|
+
),
|
|
84
149
|
deferred: true,
|
|
85
150
|
}
|
|
86
151
|
commands['update-agent-dev'] = {
|
|
87
152
|
description: 'Update @geekbeer/minion from Verdaccio (dev) and restart',
|
|
88
|
-
command:
|
|
153
|
+
command: buildUpdateScript(
|
|
154
|
+
'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873',
|
|
155
|
+
'nssm stop minion-agent',
|
|
156
|
+
'nssm start minion-agent',
|
|
157
|
+
),
|
|
89
158
|
deferred: true,
|
|
90
159
|
}
|
|
91
160
|
commands['restart-display'] = {
|
|
@@ -104,12 +173,20 @@ function buildAllowedCommands(procMgr) {
|
|
|
104
173
|
}
|
|
105
174
|
commands['update-agent'] = {
|
|
106
175
|
description: 'Update @geekbeer/minion to latest version and restart',
|
|
107
|
-
command:
|
|
176
|
+
command: buildUpdateScript(
|
|
177
|
+
'npm install -g @geekbeer/minion@latest',
|
|
178
|
+
'net stop minion-agent',
|
|
179
|
+
'net start minion-agent',
|
|
180
|
+
),
|
|
108
181
|
deferred: true,
|
|
109
182
|
}
|
|
110
183
|
commands['update-agent-dev'] = {
|
|
111
184
|
description: 'Update @geekbeer/minion from Verdaccio (dev) and restart',
|
|
112
|
-
command:
|
|
185
|
+
command: buildUpdateScript(
|
|
186
|
+
'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873',
|
|
187
|
+
'net stop minion-agent',
|
|
188
|
+
'net start minion-agent',
|
|
189
|
+
),
|
|
113
190
|
deferred: true,
|
|
114
191
|
}
|
|
115
192
|
commands['status-services'] = {
|
package/win/minion-cli.ps1
CHANGED
|
@@ -390,9 +390,17 @@ function Invoke-Setup {
|
|
|
390
390
|
Write-Step 4 $totalSteps "Creating config directory and .env..."
|
|
391
391
|
New-Item -Path $DataDir -ItemType Directory -Force | Out-Null
|
|
392
392
|
New-Item -Path $LogDir -ItemType Directory -Force | Out-Null
|
|
393
|
+
# Detect system timezone (IANA format)
|
|
394
|
+
$sysTz = 'Asia/Tokyo'
|
|
395
|
+
try {
|
|
396
|
+
$tzInfo = Get-TimeZone
|
|
397
|
+
if ($tzInfo.Id) { $sysTz = $tzInfo.Id }
|
|
398
|
+
} catch {}
|
|
393
399
|
$envValues = @{
|
|
394
400
|
'AGENT_PORT' = '8080'
|
|
395
401
|
'MINION_USER' = $env:USERNAME
|
|
402
|
+
'REFLECTION_TIME' = '03:00'
|
|
403
|
+
'TIMEZONE' = $sysTz
|
|
396
404
|
}
|
|
397
405
|
if ($HqUrl) { $envValues['HQ_URL'] = $HqUrl }
|
|
398
406
|
if ($ApiToken) { $envValues['API_TOKEN'] = $ApiToken }
|
package/win/routes/chat.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Same as linux/routes/chat.js but with Windows-compatible PATH and shell construction.
|
|
5
5
|
*
|
|
6
6
|
* Endpoints:
|
|
7
|
-
* POST /api/chat
|
|
8
|
-
* GET /api/chat/session
|
|
9
|
-
* POST /api/chat/clear
|
|
10
|
-
* POST /api/chat/abort
|
|
11
|
-
* POST /api/chat/reset
|
|
7
|
+
* POST /api/chat - Send message, get SSE stream
|
|
8
|
+
* GET /api/chat/session - Get active session (messages + session_id)
|
|
9
|
+
* POST /api/chat/clear - Clear session and start fresh
|
|
10
|
+
* POST /api/chat/abort - Kill the active LLM CLI process
|
|
11
|
+
* POST /api/chat/reset - Summarize conversation and start fresh session
|
|
12
|
+
* POST /api/chat/end-of-day - Generate daily log + extract memories from conversation
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
const { spawn } = require('child_process')
|
|
@@ -17,7 +18,10 @@ const path = require('path')
|
|
|
17
18
|
const { verifyToken } = require('../../core/lib/auth')
|
|
18
19
|
const { config } = require('../../core/config')
|
|
19
20
|
const chatStore = require('../../core/stores/chat-store')
|
|
21
|
+
const memoryStore = require('../../core/stores/memory-store')
|
|
22
|
+
const dailyLogStore = require('../../core/stores/daily-log-store')
|
|
20
23
|
const { buildExtendedPath } = require('../../core/lib/platform')
|
|
24
|
+
const { runEndOfDay } = require('../../core/lib/end-of-day')
|
|
21
25
|
|
|
22
26
|
let activeChatChild = null
|
|
23
27
|
|
|
@@ -34,7 +38,7 @@ async function chatRoutes(fastify) {
|
|
|
34
38
|
return { success: false, error: 'message is required' }
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
const prompt =
|
|
41
|
+
const prompt = await buildContextPrefix(message, context, session_id)
|
|
38
42
|
const currentSessionId = session_id || null
|
|
39
43
|
|
|
40
44
|
if (currentSessionId) {
|
|
@@ -142,10 +146,45 @@ async function chatRoutes(fastify) {
|
|
|
142
146
|
console.log(`[Chat] session reset with summary (${summary?.length || 0} chars)`)
|
|
143
147
|
return { success: true, summary }
|
|
144
148
|
})
|
|
149
|
+
|
|
150
|
+
// POST /api/chat/end-of-day - Generate daily log + extract memories
|
|
151
|
+
fastify.post('/api/chat/end-of-day', async (request, reply) => {
|
|
152
|
+
if (!verifyToken(request)) {
|
|
153
|
+
reply.code(401)
|
|
154
|
+
return { success: false, error: 'Unauthorized' }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { clear_session = false } = request.body || {}
|
|
158
|
+
|
|
159
|
+
const result = await runEndOfDay({
|
|
160
|
+
runQuickLlmCall,
|
|
161
|
+
clearSession: clear_session,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return { success: true, ...result }
|
|
165
|
+
})
|
|
145
166
|
}
|
|
146
167
|
|
|
147
|
-
function buildContextPrefix(message, context) {
|
|
168
|
+
async function buildContextPrefix(message, context, sessionId) {
|
|
148
169
|
const parts = []
|
|
170
|
+
|
|
171
|
+
// Inject memory + daily logs on new sessions only (not on --resume)
|
|
172
|
+
if (!sessionId) {
|
|
173
|
+
try {
|
|
174
|
+
const memorySnippet = await memoryStore.getContextSnippet(2000)
|
|
175
|
+
const dailyLogSnippet = await dailyLogStore.getContextSnippet(3, 1500)
|
|
176
|
+
|
|
177
|
+
if (memorySnippet) {
|
|
178
|
+
parts.push('[ミニオンメモリ(長期記憶)]', memorySnippet, '')
|
|
179
|
+
}
|
|
180
|
+
if (dailyLogSnippet) {
|
|
181
|
+
parts.push('[最近のデイリーログ]', dailyLogSnippet, '')
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error('[Chat] Failed to load memory/daily-log context:', err.message)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
149
188
|
if (context) {
|
|
150
189
|
switch (context.type) {
|
|
151
190
|
case 'skill':
|
|
@@ -445,4 +484,4 @@ function runQuickLlmCall(prompt) {
|
|
|
445
484
|
})
|
|
446
485
|
}
|
|
447
486
|
|
|
448
|
-
module.exports = { chatRoutes }
|
|
487
|
+
module.exports = { chatRoutes, runQuickLlmCall }
|
package/win/routes/config.js
CHANGED
|
@@ -13,7 +13,12 @@ const { clearLlmCache } = require('../../core/lib/llm-checker')
|
|
|
13
13
|
const { config, updateConfig } = require('../../core/config')
|
|
14
14
|
const { resolveEnvFilePath } = require('../../core/lib/platform')
|
|
15
15
|
|
|
16
|
-
const
|
|
16
|
+
const reflectionScheduler = require('../../core/lib/reflection-scheduler')
|
|
17
|
+
|
|
18
|
+
const ALLOWED_ENV_KEYS = ['LLM_COMMAND', 'REFLECTION_TIME', 'TIMEZONE']
|
|
19
|
+
|
|
20
|
+
/** Keys that trigger a reflection scheduler reschedule when changed */
|
|
21
|
+
const REFLECTION_KEYS = ['REFLECTION_TIME', 'TIMEZONE']
|
|
17
22
|
|
|
18
23
|
const BACKUP_PATHS = [
|
|
19
24
|
'~/.claude',
|
|
@@ -183,8 +188,10 @@ function configRoutes(fastify, _opts, done) {
|
|
|
183
188
|
return reply.code(403).send({ error: `Key '${key}' is not allowed` })
|
|
184
189
|
}
|
|
185
190
|
const envPath = resolveEnvPath()
|
|
186
|
-
const
|
|
187
|
-
|
|
191
|
+
const fileValue = readEnvKey(envPath, key)
|
|
192
|
+
// Fall back to in-memory config default when .env has no entry
|
|
193
|
+
const value = fileValue !== null ? fileValue : (config[key] ?? '')
|
|
194
|
+
return { key, value, configured: fileValue !== null }
|
|
188
195
|
})
|
|
189
196
|
|
|
190
197
|
fastify.put('/api/config/env', async (request, reply) => {
|
|
@@ -217,6 +224,12 @@ function configRoutes(fastify, _opts, done) {
|
|
|
217
224
|
updateConfig(key, value)
|
|
218
225
|
|
|
219
226
|
clearLlmCache()
|
|
227
|
+
|
|
228
|
+
// Reschedule reflection if relevant key changed
|
|
229
|
+
if (REFLECTION_KEYS.includes(key)) {
|
|
230
|
+
reflectionScheduler.reschedule()
|
|
231
|
+
}
|
|
232
|
+
|
|
220
233
|
return { success: true, restart_required: false }
|
|
221
234
|
} catch (err) {
|
|
222
235
|
console.error(`[Config] Failed to update ${key} in ${envPath}:`, err.message)
|
package/win/server.js
CHANGED
|
@@ -31,12 +31,13 @@ const routineRunner = require('./routine-runner')
|
|
|
31
31
|
// Pull-model daemons (from core/)
|
|
32
32
|
const stepPoller = require('../core/lib/step-poller')
|
|
33
33
|
const revisionWatcher = require('../core/lib/revision-watcher')
|
|
34
|
+
const reflectionScheduler = require('../core/lib/reflection-scheduler')
|
|
34
35
|
const { commandRoutes, getProcessManager, getAllowedCommands } = require('./routes/commands')
|
|
35
36
|
const { terminalRoutes, cleanupSessions } = require('./routes/terminal')
|
|
36
37
|
const { startTerminalServer, stopTerminalServer } = require('./terminal-server')
|
|
37
38
|
const { fileRoutes } = require('./routes/files')
|
|
38
39
|
const { directiveRoutes } = require('./routes/directives')
|
|
39
|
-
const { chatRoutes } = require('./routes/chat')
|
|
40
|
+
const { chatRoutes, runQuickLlmCall } = require('./routes/chat')
|
|
40
41
|
const { configRoutes } = require('./routes/config')
|
|
41
42
|
|
|
42
43
|
// Compatible route modules (reused from Linux)
|
|
@@ -47,6 +48,8 @@ const { workflowRoutes } = require('../core/routes/workflows')
|
|
|
47
48
|
const { routineRoutes } = require('../core/routes/routines')
|
|
48
49
|
const { authRoutes } = require('../core/routes/auth')
|
|
49
50
|
const { variableRoutes } = require('../core/routes/variables')
|
|
51
|
+
const { memoryRoutes } = require('../core/routes/memory')
|
|
52
|
+
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
50
53
|
|
|
51
54
|
// Validate configuration
|
|
52
55
|
validate()
|
|
@@ -81,6 +84,7 @@ async function shutdown(signal) {
|
|
|
81
84
|
|
|
82
85
|
stepPoller.stop()
|
|
83
86
|
revisionWatcher.stop()
|
|
87
|
+
reflectionScheduler.stop()
|
|
84
88
|
workflowRunner.stopAll()
|
|
85
89
|
routineRunner.stopAll()
|
|
86
90
|
stopTerminalServer()
|
|
@@ -195,6 +199,8 @@ async function registerRoutes(app) {
|
|
|
195
199
|
await app.register(routineRoutes, { routineRunner })
|
|
196
200
|
await app.register(authRoutes)
|
|
197
201
|
await app.register(variableRoutes)
|
|
202
|
+
await app.register(memoryRoutes)
|
|
203
|
+
await app.register(dailyLogRoutes)
|
|
198
204
|
|
|
199
205
|
// Windows-specific routes
|
|
200
206
|
await app.register(commandRoutes)
|
|
@@ -252,6 +258,9 @@ async function start() {
|
|
|
252
258
|
console.error('[Server] Failed to load cached routines:', err.message)
|
|
253
259
|
}
|
|
254
260
|
|
|
261
|
+
// Start reflection scheduler (self-reflection time)
|
|
262
|
+
reflectionScheduler.start(runQuickLlmCall)
|
|
263
|
+
|
|
255
264
|
if (isHqConfigured()) {
|
|
256
265
|
console.log(`[Server] HQ URL: ${config.HQ_URL}`)
|
|
257
266
|
|