@geekbeer/minion 2.51.1 → 2.53.2

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.
@@ -11,6 +11,7 @@ const path = require('path')
11
11
  const { execSync } = require('child_process')
12
12
  const { config } = require('../config')
13
13
  const { IS_WINDOWS, buildExtendedPath } = require('./platform')
14
+ const variableStore = require('../stores/variable-store')
14
15
 
15
16
  const CACHE_TTL_MS = 300000 // 5 minutes
16
17
 
@@ -79,8 +80,19 @@ function checkTool(name) {
79
80
  const TOOL_NAMES = ['git', 'node', 'npx', 'claude', 'docker', 'tmux']
80
81
 
81
82
  /**
82
- * Get all capabilities (MCP servers + CLI tools), cached for 5 minutes.
83
- * @returns {{ mcp_servers: { name: string, configured: boolean }[], cli_tools: { name: string, available: boolean, version?: string }[] }}
83
+ * Get all available environment variable keys (variables + secrets).
84
+ * Returns deduplicated key names without values (secrets stay hidden).
85
+ * @returns {string[]}
86
+ */
87
+ function getEnvVarKeys() {
88
+ const varKeys = variableStore.listKeys('variables')
89
+ const secretKeys = variableStore.listKeys('secrets')
90
+ return [...new Set([...varKeys, ...secretKeys])]
91
+ }
92
+
93
+ /**
94
+ * Get all capabilities (MCP servers + CLI tools + env var keys), cached for 5 minutes.
95
+ * @returns {{ mcp_servers: { name: string, configured: boolean }[], cli_tools: { name: string, available: boolean, version?: string }[], env_var_keys: string[] }}
84
96
  */
85
97
  function getCapabilities() {
86
98
  const now = Date.now()
@@ -90,8 +102,9 @@ function getCapabilities() {
90
102
 
91
103
  const mcp_servers = getMcpServers()
92
104
  const cli_tools = TOOL_NAMES.map(name => checkTool(name))
105
+ const env_var_keys = getEnvVarKeys()
93
106
 
94
- cachedResult = { mcp_servers, cli_tools }
107
+ cachedResult = { mcp_servers, cli_tools, env_var_keys }
95
108
  cachedAt = now
96
109
  return cachedResult
97
110
  }
@@ -102,4 +115,14 @@ function clearCapabilityCache() {
102
115
  cachedAt = 0
103
116
  }
104
117
 
105
- module.exports = { getCapabilities, clearCapabilityCache }
118
+ /**
119
+ * Check arbitrary tool names on-demand (not cached).
120
+ * Used by readiness checks to verify skill-declared cli_tools.
121
+ * @param {string[]} names - Tool names to check
122
+ * @returns {{ name: string, available: boolean, version?: string }[]}
123
+ */
124
+ function checkTools(names) {
125
+ return names.map(name => checkTool(name))
126
+ }
127
+
128
+ module.exports = { getCapabilities, clearCapabilityCache, checkTools }
@@ -5,13 +5,14 @@
5
5
  * - GET /api/health - Health check
6
6
  * - GET /api/status - Get current status
7
7
  * - POST /api/status - Update status
8
+ * - POST /api/capabilities/check-tools - Check arbitrary CLI tools on-demand
8
9
  */
9
10
 
10
11
  const { version } = require('../../package.json')
11
12
  const { config, isHqConfigured } = require('../config')
12
13
  const { sendHeartbeat } = require('../api')
13
14
  const { getLlmServices, isLlmCommandConfigured } = require('../lib/llm-checker')
14
- const { getCapabilities } = require('../lib/capability-checker')
15
+ const { getCapabilities, checkTools } = require('../lib/capability-checker')
15
16
 
16
17
  function maskToken(token) {
17
18
  if (!token || token.length < 8) return token ? '***' : ''
@@ -105,6 +106,18 @@ async function healthRoutes(fastify) {
105
106
 
106
107
  return { success: true }
107
108
  })
109
+
110
+ // Check arbitrary CLI tools on-demand
111
+ fastify.post('/api/capabilities/check-tools', async (request, reply) => {
112
+ const { tools } = request.body || {}
113
+ if (!Array.isArray(tools) || tools.length === 0) {
114
+ reply.code(400)
115
+ return { error: 'tools must be a non-empty array of tool names' }
116
+ }
117
+ // Limit to 20 tools per request to avoid abuse
118
+ const limited = tools.slice(0, 20).filter(t => typeof t === 'string' && t.length > 0)
119
+ return { cli_tools: checkTools(limited) }
120
+ })
108
121
  }
109
122
 
110
123
  module.exports = {
@@ -58,17 +58,24 @@ async function writeSkillToLocal(name, { content, description, display_name, typ
58
58
  await fs.mkdir(skillDir, { recursive: true })
59
59
  await fs.mkdir(filesDir, { recursive: true })
60
60
 
61
- // Build frontmatter with all available metadata
62
- const frontmatterLines = [
63
- `name: ${name}`,
64
- display_name ? `display_name: ${display_name}` : null,
65
- type ? `type: ${type}` : null,
66
- `description: ${description || ''}`,
67
- ].filter(Boolean).join('\n')
61
+ // If content already has frontmatter, write as-is; otherwise build frontmatter
62
+ const hasFrontmatter = content.startsWith('---\n')
63
+ let fileContent
64
+ if (hasFrontmatter) {
65
+ fileContent = content
66
+ } else {
67
+ const frontmatterLines = [
68
+ `name: ${name}`,
69
+ display_name ? `display_name: ${display_name}` : null,
70
+ type ? `type: ${type}` : null,
71
+ `description: ${description || ''}`,
72
+ ].filter(Boolean).join('\n')
73
+ fileContent = `---\n${frontmatterLines}\n---\n\n${content}`
74
+ }
68
75
 
69
76
  await fs.writeFile(
70
77
  path.join(skillDir, 'SKILL.md'),
71
- `---\n${frontmatterLines}\n---\n\n${content}`,
78
+ fileContent,
72
79
  'utf-8'
73
80
  )
74
81
 
@@ -128,7 +135,7 @@ async function pushSkillToHQ(name) {
128
135
  name: metadata.name || name,
129
136
  display_name: metadata.display_name || metadata.name || name,
130
137
  description: metadata.description || '',
131
- content: body,
138
+ content: rawContent,
132
139
  type: metadata.type || 'workflow',
133
140
  files,
134
141
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "2.51.1",
3
+ "version": "2.53.2",
4
4
  "description": "AI Agent runtime for Minion - manages status and skill deployment on VPS",
5
5
  "main": "linux/server.js",
6
6
  "bin": {