feat(littleblue): blue tool registry (list/propose action via local API) + run_turn extraEnv

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-04 21:42:27 +10:00
parent 3aa8dc578b
commit ff681847ed
5 changed files with 67 additions and 4 deletions

View File

@@ -0,0 +1,29 @@
// Little Blue's action tools. They run inside the MCP child, which holds NO infra
// creds — only a scoped little-blue bearer + the local API URL. The main server
// (which has the Proxmox/SSH creds) does the actual work behind /api/actions.
function api(env = process.env) { return { base: env.VOID_API_URL, token: env.VOID_AGENT_TOKEN }; }
export const listActionsTool = {
name: 'list_actions',
description: 'List the whitelisted fix-it actions you may take (id, label, tier).',
input_schema: { type: 'object', properties: {} },
async handler(_args, _ctx, { fetchImpl = fetch } = {}) {
const { base, token } = api();
const res = await fetchImpl(`${base}/api/actions`, { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) return { error: `list_actions ${res.status}` };
return res.json();
}
};
export const proposeActionTool = {
name: 'propose_action',
description: 'Take a whitelisted action by id. SAFE actions run immediately; RISKY ones queue for the owner to approve. You can only name an id from list_actions — never a command.',
input_schema: { type: 'object', properties: { action_id: { type: 'string' } }, required: ['action_id'] },
async handler({ action_id }, _ctx, { fetchImpl = fetch } = {}) {
const { base, token } = api();
const res = await fetchImpl(`${base}/api/actions/${encodeURIComponent(action_id)}/run`,
{ method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } });
if (!res.ok) return { error: `propose_action ${res.status}` };
return res.json();
}
};

View File

@@ -0,0 +1,9 @@
import { createRegistry } from '../../registry.js';
import { searchTool } from '../search.js';
import { listActionsTool, proposeActionTool } from './actions.js';
// read (search) + her action tools. No propose_change (she fixes infra, not content).
export const blueRegistry = createRegistry();
blueRegistry.registerTool(searchTool);
blueRegistry.registerTool(listActionsTool);
blueRegistry.registerTool(proposeActionTool);