@hailer/mcp 0.1.11 → 0.1.12

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.
Files changed (62) hide show
  1. package/.claude/settings.json +12 -0
  2. package/CLAUDE.md +37 -1
  3. package/ai-hub/dist/assets/index-8ce6041d.css +1 -0
  4. package/ai-hub/dist/assets/index-930f01ca.js +348 -0
  5. package/ai-hub/dist/index.html +15 -0
  6. package/ai-hub/dist/manifest.json +14 -0
  7. package/ai-hub/dist/vite.svg +1 -0
  8. package/dist/app.js +5 -0
  9. package/dist/client/agents/base.d.ts +5 -0
  10. package/dist/client/agents/base.js +9 -2
  11. package/dist/client/agents/definitions.js +85 -0
  12. package/dist/client/agents/orchestrator.d.ts +21 -0
  13. package/dist/client/agents/orchestrator.js +292 -1
  14. package/dist/client/bot-entrypoint.d.ts +7 -0
  15. package/dist/client/bot-entrypoint.js +103 -0
  16. package/dist/client/bot-runner.d.ts +35 -0
  17. package/dist/client/bot-runner.js +188 -0
  18. package/dist/client/factory.d.ts +4 -0
  19. package/dist/client/factory.js +10 -0
  20. package/dist/client/server.d.ts +8 -0
  21. package/dist/client/server.js +251 -0
  22. package/dist/client/types.d.ts +29 -0
  23. package/dist/client/types.js +4 -1
  24. package/dist/core.d.ts +3 -0
  25. package/dist/core.js +72 -0
  26. package/dist/mcp/hailer-clients.d.ts +4 -0
  27. package/dist/mcp/hailer-clients.js +16 -1
  28. package/dist/mcp/tools/app-scaffold.js +127 -5
  29. package/dist/mcp/tools/bot-config.d.ts +78 -0
  30. package/dist/mcp/tools/bot-config.js +442 -0
  31. package/dist/mcp-server.js +109 -1
  32. package/dist/modules/bug-reports/bug-config.d.ts +25 -0
  33. package/dist/modules/bug-reports/bug-config.js +187 -0
  34. package/dist/modules/bug-reports/bug-monitor.d.ts +108 -0
  35. package/dist/modules/bug-reports/bug-monitor.js +510 -0
  36. package/dist/modules/bug-reports/giuseppe-ai.d.ts +59 -0
  37. package/dist/modules/bug-reports/giuseppe-ai.js +335 -0
  38. package/dist/modules/bug-reports/giuseppe-bot.d.ts +109 -0
  39. package/dist/modules/bug-reports/giuseppe-bot.js +765 -0
  40. package/dist/modules/bug-reports/giuseppe-files.d.ts +52 -0
  41. package/dist/modules/bug-reports/giuseppe-files.js +338 -0
  42. package/dist/modules/bug-reports/giuseppe-git.d.ts +48 -0
  43. package/dist/modules/bug-reports/giuseppe-git.js +298 -0
  44. package/dist/modules/bug-reports/giuseppe-prompt.d.ts +5 -0
  45. package/dist/modules/bug-reports/giuseppe-prompt.js +94 -0
  46. package/dist/modules/bug-reports/index.d.ts +76 -0
  47. package/dist/modules/bug-reports/index.js +213 -0
  48. package/dist/modules/bug-reports/pending-classification-registry.d.ts +28 -0
  49. package/dist/modules/bug-reports/pending-classification-registry.js +50 -0
  50. package/dist/modules/bug-reports/pending-fix-registry.d.ts +30 -0
  51. package/dist/modules/bug-reports/pending-fix-registry.js +42 -0
  52. package/dist/modules/bug-reports/pending-registry.d.ts +27 -0
  53. package/dist/modules/bug-reports/pending-registry.js +49 -0
  54. package/dist/modules/bug-reports/types.d.ts +123 -0
  55. package/dist/modules/bug-reports/types.js +9 -0
  56. package/dist/services/bug-monitor.d.ts +23 -0
  57. package/dist/services/bug-monitor.js +275 -0
  58. package/lineup-manager/dist/assets/index-b30c809f.js +600 -0
  59. package/lineup-manager/dist/index.html +1 -1
  60. package/lineup-manager/dist/manifest.json +5 -5
  61. package/package.json +6 -2
  62. package/lineup-manager/dist/assets/index-e168f265.js +0 -600
@@ -0,0 +1,442 @@
1
+ "use strict";
2
+ /**
3
+ * Bot Configuration MCP Tools
4
+ *
5
+ * Manages which bots are enabled/disabled in the workspace.
6
+ * Config is stored in the Agent Directory workflow - agents are activities.
7
+ * - Deployed agents phase = enabled
8
+ * - Retired agents phase = disabled
9
+ *
10
+ * Architecture:
11
+ * - In-memory state is primary (for speed)
12
+ * - Agent Directory workflow is persistence layer (agents as activities, phases for state)
13
+ * - On startup: Load config from Hailer by checking which phase each agent is in
14
+ * - On change: Move agent activity between phases
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.botConfigTools = exports.disableBotTool = exports.enableBotTool = exports.listBotsConfigTool = exports.AVAILABLE_BOTS = void 0;
18
+ exports.onBotStateChange = onBotStateChange;
19
+ exports.getBotState = getBotState;
20
+ exports.setBotEnabled = setBotEnabled;
21
+ exports.initBotConfigPersistence = initBotConfigPersistence;
22
+ exports.isPersistenceInitialized = isPersistenceInitialized;
23
+ exports.getPersistenceStatus = getPersistenceStatus;
24
+ const zod_1 = require("zod");
25
+ const tool_registry_1 = require("../tool-registry");
26
+ const index_1 = require("../utils/index");
27
+ const logger = (0, index_1.createLogger)({ component: 'bot-config-tools' });
28
+ // ============================================================================
29
+ // Agent Directory Workflow Configuration
30
+ // ============================================================================
31
+ // Agent Directory workflow config
32
+ const AGENT_DIRECTORY_WORKFLOW_ID = '695784898d347a6c707ee365';
33
+ const DEPLOYED_PHASE_ID = '695784898d347a6c707ee37d'; // enabled
34
+ const RETIRED_PHASE_ID = '695784898d347a6c707ee37e'; // disabled
35
+ // Mapping from bot IDs to their activity IDs in the Agent Directory workflow
36
+ const AGENT_ACTIVITY_IDS = {
37
+ hal: '6957848b8d347a6c707ee3f6',
38
+ hailerExpert: '695784913c659fc4d8fe0e02',
39
+ giuseppe: '695e42deca61319424bc8b23',
40
+ vastuullisuus: '695e42dfca61319424bc8b2e'
41
+ };
42
+ // Persistence state
43
+ let persistenceInitialized = false;
44
+ let hailerClient = null; // Will be set during initialization
45
+ /**
46
+ * Available bots - single source of truth
47
+ *
48
+ * All bots that can be enabled/disabled in the workspace.
49
+ * This list should match the AI Hub app and agent definitions.
50
+ */
51
+ exports.AVAILABLE_BOTS = [
52
+ {
53
+ id: 'hal',
54
+ name: 'HAL',
55
+ description: 'Main orchestrator - handles general chat and coordinates specialists',
56
+ icon: '🤖'
57
+ },
58
+ {
59
+ id: 'giuseppe',
60
+ name: 'Giuseppe',
61
+ description: 'Bug fixing specialist - analyzes and repairs Hailer app issues',
62
+ icon: '🔧'
63
+ },
64
+ {
65
+ id: 'hailerExpert',
66
+ name: 'Hailer Expert',
67
+ description: 'Workflow & data specialist - bulk operations, insights, reports',
68
+ icon: '📊'
69
+ },
70
+ {
71
+ id: 'vastuullisuus',
72
+ name: 'Vastuullisuus',
73
+ description: 'Sustainability analysis for SMEs',
74
+ icon: '🌱'
75
+ }
76
+ ];
77
+ // In-memory bot state (will be synced with workflow)
78
+ const botState = {
79
+ hal: true, // HAL enabled by default
80
+ giuseppe: false,
81
+ hailerExpert: false,
82
+ vastuullisuus: false
83
+ };
84
+ // Callbacks for when bot state changes
85
+ const stateChangeCallbacks = [];
86
+ function onBotStateChange(callback) {
87
+ stateChangeCallbacks.push(callback);
88
+ }
89
+ function getBotState() {
90
+ return { ...botState };
91
+ }
92
+ /**
93
+ * Set bot enabled state
94
+ *
95
+ * Updates in-memory state and persists to Agent Directory by moving
96
+ * the agent activity to the appropriate phase (deployed/retired).
97
+ */
98
+ function setBotEnabled(botId, enabled) {
99
+ if (!exports.AVAILABLE_BOTS.find(b => b.id === botId)) {
100
+ throw new Error(`Unknown bot: ${botId}`);
101
+ }
102
+ botState[botId] = enabled;
103
+ // Notify listeners
104
+ stateChangeCallbacks.forEach(cb => cb(botId, enabled));
105
+ logger.info('Bot state changed', { botId, enabled });
106
+ // Persist single bot to Hailer asynchronously (more efficient than full sync)
107
+ persistBotToHailer(botId, enabled).catch(err => logger.warn('Persistence failed', { botId, err: err instanceof Error ? err.message : String(err) }));
108
+ }
109
+ // ============================================================================
110
+ // Persistence Layer Functions
111
+ // ============================================================================
112
+ /**
113
+ * Initialize persistence layer
114
+ * Called once during MCP server startup
115
+ *
116
+ * Loads agent enabled state from the Agent Directory workflow:
117
+ * - Agents in DEPLOYED_PHASE_ID are enabled
118
+ * - Agents in RETIRED_PHASE_ID are disabled
119
+ *
120
+ * @param client - HailerApiClient instance for API calls
121
+ */
122
+ async function initBotConfigPersistence(client) {
123
+ if (persistenceInitialized) {
124
+ logger.debug('Bot config persistence already initialized');
125
+ return;
126
+ }
127
+ hailerClient = client;
128
+ try {
129
+ logger.info('Initializing bot config persistence from Agent Directory...', {
130
+ workflowId: AGENT_DIRECTORY_WORKFLOW_ID,
131
+ deployedPhaseId: DEPLOYED_PHASE_ID,
132
+ retiredPhaseId: RETIRED_PHASE_ID
133
+ });
134
+ // Load agent state from Agent Directory workflow
135
+ const existingConfig = await loadConfigFromHailer();
136
+ if (existingConfig) {
137
+ // Update in-memory state with persisted values
138
+ Object.keys(existingConfig).forEach(botId => {
139
+ if (exports.AVAILABLE_BOTS.find(b => b.id === botId)) {
140
+ botState[botId] = existingConfig[botId];
141
+ }
142
+ });
143
+ logger.info('Bot config loaded from Agent Directory', {
144
+ config: botState
145
+ });
146
+ }
147
+ logger.info('Bot config persistence initialized');
148
+ persistenceInitialized = true;
149
+ }
150
+ catch (error) {
151
+ logger.warn('Failed to initialize bot config persistence, using defaults', {
152
+ error: error instanceof Error ? error.message : String(error)
153
+ });
154
+ // Still mark as initialized to prevent repeated failures
155
+ persistenceInitialized = true;
156
+ }
157
+ }
158
+ /**
159
+ * Load config from Agent Directory workflow
160
+ *
161
+ * Checks which phase each agent activity is in:
162
+ * - DEPLOYED_PHASE_ID = enabled
163
+ * - RETIRED_PHASE_ID = disabled
164
+ *
165
+ * @returns bot config or null if not found
166
+ */
167
+ async function loadConfigFromHailer() {
168
+ if (!hailerClient) {
169
+ logger.debug('Cannot load config: missing Hailer client');
170
+ return null;
171
+ }
172
+ try {
173
+ const config = {};
174
+ // Create reverse mapping: activityId -> botId
175
+ const activityIdToBotId = {};
176
+ for (const [botId, activityId] of Object.entries(AGENT_ACTIVITY_IDS)) {
177
+ activityIdToBotId[activityId] = botId;
178
+ }
179
+ // Fetch activities from deployed phase (enabled agents)
180
+ logger.debug('Fetching deployed agents...', { phaseId: DEPLOYED_PHASE_ID });
181
+ const deployedActivities = await hailerClient.fetchActivityList(AGENT_DIRECTORY_WORKFLOW_ID, DEPLOYED_PHASE_ID, 100, // limit
182
+ { returnFlat: true });
183
+ // Mark agents in deployed phase as enabled
184
+ const deployedList = Array.isArray(deployedActivities)
185
+ ? deployedActivities
186
+ : (deployedActivities?.activities || []);
187
+ for (const activity of deployedList) {
188
+ const botId = activityIdToBotId[activity._id];
189
+ if (botId) {
190
+ config[botId] = true;
191
+ logger.debug('Agent found in deployed phase (enabled)', { botId, activityId: activity._id });
192
+ }
193
+ }
194
+ // Fetch activities from retired phase (disabled agents)
195
+ logger.debug('Fetching retired agents...', { phaseId: RETIRED_PHASE_ID });
196
+ const retiredActivities = await hailerClient.fetchActivityList(AGENT_DIRECTORY_WORKFLOW_ID, RETIRED_PHASE_ID, 100, // limit
197
+ { returnFlat: true });
198
+ // Mark agents in retired phase as disabled
199
+ const retiredList = Array.isArray(retiredActivities)
200
+ ? retiredActivities
201
+ : (retiredActivities?.activities || []);
202
+ for (const activity of retiredList) {
203
+ const botId = activityIdToBotId[activity._id];
204
+ if (botId) {
205
+ config[botId] = false;
206
+ logger.debug('Agent found in retired phase (disabled)', { botId, activityId: activity._id });
207
+ }
208
+ }
209
+ // For any bots not found in either phase, default based on AVAILABLE_BOTS defaults
210
+ for (const bot of exports.AVAILABLE_BOTS) {
211
+ if (config[bot.id] === undefined) {
212
+ // Default: HAL enabled, others disabled
213
+ config[bot.id] = bot.id === 'hal';
214
+ logger.debug('Agent not found in any phase, using default', {
215
+ botId: bot.id,
216
+ default: config[bot.id]
217
+ });
218
+ }
219
+ }
220
+ logger.info('Bot config loaded from Agent Directory', {
221
+ deployedCount: deployedList.length,
222
+ retiredCount: retiredList.length,
223
+ config
224
+ });
225
+ return config;
226
+ }
227
+ catch (error) {
228
+ logger.warn('Failed to load bot config from Agent Directory', {
229
+ error: error instanceof Error ? error.message : String(error)
230
+ });
231
+ return null;
232
+ }
233
+ }
234
+ /**
235
+ * Persist a single bot's state to Hailer
236
+ *
237
+ * Moves the bot's agent activity between phases:
238
+ * - enabled = move to DEPLOYED_PHASE_ID
239
+ * - disabled = move to RETIRED_PHASE_ID
240
+ *
241
+ * @param botId - The bot ID to persist
242
+ * @param enabled - Whether the bot is enabled
243
+ */
244
+ async function persistBotToHailer(botId, enabled) {
245
+ if (!hailerClient || !persistenceInitialized) {
246
+ logger.debug('Persistence not initialized, skipping save');
247
+ return;
248
+ }
249
+ const activityId = AGENT_ACTIVITY_IDS[botId];
250
+ if (!activityId) {
251
+ logger.warn('No activity ID found for bot', { botId });
252
+ return;
253
+ }
254
+ const targetPhaseId = enabled ? DEPLOYED_PHASE_ID : RETIRED_PHASE_ID;
255
+ try {
256
+ logger.debug('Moving agent to phase...', {
257
+ botId,
258
+ activityId,
259
+ enabled,
260
+ targetPhaseId
261
+ });
262
+ // Use updateActivities to move the agent activity to the appropriate phase
263
+ await hailerClient.updateActivities([{ _id: activityId }], { phaseId: targetPhaseId });
264
+ logger.info('Agent moved to phase', {
265
+ botId,
266
+ activityId,
267
+ phase: enabled ? 'deployed' : 'retired',
268
+ phaseId: targetPhaseId
269
+ });
270
+ }
271
+ catch (error) {
272
+ logger.warn('Failed to persist bot state to Hailer', {
273
+ botId,
274
+ activityId,
275
+ error: error instanceof Error ? error.message : String(error)
276
+ });
277
+ }
278
+ }
279
+ /**
280
+ * Save current bot state to Hailer (legacy function for full sync)
281
+ * Called asynchronously after state changes
282
+ *
283
+ * Note: This syncs all bots. For single bot changes, persistBotToHailer is more efficient.
284
+ */
285
+ async function persistToHailer() {
286
+ if (!hailerClient || !persistenceInitialized) {
287
+ logger.debug('Persistence not initialized, skipping save');
288
+ return;
289
+ }
290
+ try {
291
+ logger.debug('Persisting all bot config to Hailer...', { state: botState });
292
+ // Build updates for all bots
293
+ const enabledUpdates = [];
294
+ const disabledUpdates = [];
295
+ for (const [botId, enabled] of Object.entries(botState)) {
296
+ const activityId = AGENT_ACTIVITY_IDS[botId];
297
+ if (!activityId) {
298
+ logger.debug('No activity ID for bot, skipping', { botId });
299
+ continue;
300
+ }
301
+ if (enabled) {
302
+ enabledUpdates.push({ _id: activityId });
303
+ }
304
+ else {
305
+ disabledUpdates.push({ _id: activityId });
306
+ }
307
+ }
308
+ // Move enabled agents to deployed phase
309
+ if (enabledUpdates.length > 0) {
310
+ await hailerClient.updateActivities(enabledUpdates, { phaseId: DEPLOYED_PHASE_ID });
311
+ logger.debug('Moved agents to deployed phase', {
312
+ count: enabledUpdates.length,
313
+ phaseId: DEPLOYED_PHASE_ID
314
+ });
315
+ }
316
+ // Move disabled agents to retired phase
317
+ if (disabledUpdates.length > 0) {
318
+ await hailerClient.updateActivities(disabledUpdates, { phaseId: RETIRED_PHASE_ID });
319
+ logger.debug('Moved agents to retired phase', {
320
+ count: disabledUpdates.length,
321
+ phaseId: RETIRED_PHASE_ID
322
+ });
323
+ }
324
+ logger.info('Bot config persisted to Agent Directory', {
325
+ enabledCount: enabledUpdates.length,
326
+ disabledCount: disabledUpdates.length
327
+ });
328
+ }
329
+ catch (error) {
330
+ logger.warn('Failed to persist bot config to Hailer', {
331
+ error: error instanceof Error ? error.message : String(error)
332
+ });
333
+ }
334
+ }
335
+ /**
336
+ * Check if persistence is initialized
337
+ */
338
+ function isPersistenceInitialized() {
339
+ return persistenceInitialized;
340
+ }
341
+ /**
342
+ * Get persistence status info (for debugging)
343
+ */
344
+ function getPersistenceStatus() {
345
+ return {
346
+ initialized: persistenceInitialized,
347
+ workflowId: AGENT_DIRECTORY_WORKFLOW_ID,
348
+ deployedPhaseId: DEPLOYED_PHASE_ID,
349
+ retiredPhaseId: RETIRED_PHASE_ID,
350
+ agentActivityIds: { ...AGENT_ACTIVITY_IDS },
351
+ hasClient: hailerClient !== null
352
+ };
353
+ }
354
+ /**
355
+ * List available bots and their status
356
+ */
357
+ exports.listBotsConfigTool = {
358
+ name: 'list_bots_config',
359
+ group: tool_registry_1.ToolGroup.READ,
360
+ description: 'List available AI bots and their enabled/disabled status',
361
+ schema: zod_1.z.object({}),
362
+ execute: async (_params, _context) => {
363
+ const bots = exports.AVAILABLE_BOTS.map(bot => ({
364
+ ...bot,
365
+ enabled: botState[bot.id] || false
366
+ }));
367
+ return {
368
+ content: [{
369
+ type: 'text',
370
+ text: JSON.stringify({ bots }, null, 2)
371
+ }]
372
+ };
373
+ }
374
+ };
375
+ /**
376
+ * Enable a bot
377
+ */
378
+ exports.enableBotTool = {
379
+ name: 'enable_bot',
380
+ group: tool_registry_1.ToolGroup.WRITE,
381
+ description: 'Enable an AI bot in this workspace',
382
+ schema: zod_1.z.object({
383
+ botId: zod_1.z.string().describe('Bot ID to enable (hal, giuseppe, hailerExpert, vastuullisuus)')
384
+ }),
385
+ execute: async (params, _context) => {
386
+ const { botId } = params;
387
+ if (!exports.AVAILABLE_BOTS.find(b => b.id === botId)) {
388
+ return {
389
+ content: [{
390
+ type: 'text',
391
+ text: `Unknown bot: ${botId}. Available: ${exports.AVAILABLE_BOTS.map(b => b.id).join(', ')}`
392
+ }],
393
+ isError: true
394
+ };
395
+ }
396
+ // Use setBotEnabled to trigger persistence
397
+ setBotEnabled(botId, true);
398
+ return {
399
+ content: [{
400
+ type: 'text',
401
+ text: `${botId} bot enabled`
402
+ }]
403
+ };
404
+ }
405
+ };
406
+ /**
407
+ * Disable a bot
408
+ */
409
+ exports.disableBotTool = {
410
+ name: 'disable_bot',
411
+ group: tool_registry_1.ToolGroup.WRITE,
412
+ description: 'Disable an AI bot in this workspace',
413
+ schema: zod_1.z.object({
414
+ botId: zod_1.z.string().describe('Bot ID to disable (hal, giuseppe, hailerExpert, vastuullisuus)')
415
+ }),
416
+ execute: async (params, _context) => {
417
+ const { botId } = params;
418
+ if (!exports.AVAILABLE_BOTS.find(b => b.id === botId)) {
419
+ return {
420
+ content: [{
421
+ type: 'text',
422
+ text: `Unknown bot: ${botId}. Available: ${exports.AVAILABLE_BOTS.map(b => b.id).join(', ')}`
423
+ }],
424
+ isError: true
425
+ };
426
+ }
427
+ // Use setBotEnabled to trigger persistence
428
+ setBotEnabled(botId, false);
429
+ return {
430
+ content: [{
431
+ type: 'text',
432
+ text: `${botId} bot disabled`
433
+ }]
434
+ };
435
+ }
436
+ };
437
+ exports.botConfigTools = [
438
+ exports.listBotsConfigTool,
439
+ exports.enableBotTool,
440
+ exports.disableBotTool
441
+ ];
442
+ //# sourceMappingURL=bot-config.js.map
@@ -5,6 +5,39 @@
5
5
  * Implements JSON-RPC 2.0 MCP protocol over HTTP with Server-Sent Events (SSE)
6
6
  * for LLM clients (Claude Desktop, etc.)
7
7
  */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
8
41
  var __importDefault = (this && this.__importDefault) || function (mod) {
9
42
  return (mod && mod.__esModule) ? mod : { "default": mod };
10
43
  };
@@ -218,11 +251,86 @@ class MCPServerService {
218
251
  this.sendMcpError(res, req.body?.id || null, -32000, `Server error: ${error instanceof Error ? error.message : String(error)}`, 500);
219
252
  }
220
253
  });
254
+ // ===== Bot Configuration API =====
255
+ // GET /api/bots - List all bots and their status
256
+ this.app.get('/api/bots', async (req, res) => {
257
+ req.logger.debug('List bots requested');
258
+ try {
259
+ const { AVAILABLE_BOTS, getBotState } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
260
+ const state = getBotState();
261
+ const bots = AVAILABLE_BOTS.map(bot => ({
262
+ ...bot,
263
+ enabled: state[bot.id] || false
264
+ }));
265
+ res.json({ bots });
266
+ }
267
+ catch (error) {
268
+ req.logger.error('Failed to list bots', { error });
269
+ res.status(500).json({ error: 'Failed to list bots' });
270
+ }
271
+ });
272
+ // POST /api/bots/:id/enable - Enable a bot
273
+ this.app.post('/api/bots/:id/enable', async (req, res) => {
274
+ const { id } = req.params;
275
+ req.logger.info('Enable bot requested', { botId: id });
276
+ try {
277
+ const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
278
+ const bot = AVAILABLE_BOTS.find(b => b.id === id);
279
+ if (!bot) {
280
+ return res.status(404).json({ error: `Unknown bot: ${id}` });
281
+ }
282
+ setBotEnabled(id, true);
283
+ res.json({ success: true, botId: id, enabled: true });
284
+ }
285
+ catch (error) {
286
+ req.logger.error('Failed to enable bot', { botId: id, error });
287
+ res.status(500).json({ error: 'Failed to enable bot' });
288
+ }
289
+ });
290
+ // POST /api/bots/:id/disable - Disable a bot
291
+ this.app.post('/api/bots/:id/disable', async (req, res) => {
292
+ const { id } = req.params;
293
+ req.logger.info('Disable bot requested', { botId: id });
294
+ try {
295
+ const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
296
+ const bot = AVAILABLE_BOTS.find(b => b.id === id);
297
+ if (!bot) {
298
+ return res.status(404).json({ error: `Unknown bot: ${id}` });
299
+ }
300
+ setBotEnabled(id, false);
301
+ res.json({ success: true, botId: id, enabled: false });
302
+ }
303
+ catch (error) {
304
+ req.logger.error('Failed to disable bot', { botId: id, error });
305
+ res.status(500).json({ error: 'Failed to disable bot' });
306
+ }
307
+ });
308
+ // POST /api/bots/:id/toggle - Toggle a bot
309
+ this.app.post('/api/bots/:id/toggle', async (req, res) => {
310
+ const { id } = req.params;
311
+ req.logger.info('Toggle bot requested', { botId: id });
312
+ try {
313
+ const { AVAILABLE_BOTS, getBotState, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
314
+ const bot = AVAILABLE_BOTS.find(b => b.id === id);
315
+ if (!bot) {
316
+ return res.status(404).json({ error: `Unknown bot: ${id}` });
317
+ }
318
+ const currentState = getBotState();
319
+ const newState = !currentState[id];
320
+ setBotEnabled(id, newState);
321
+ res.json({ success: true, botId: id, enabled: newState });
322
+ }
323
+ catch (error) {
324
+ req.logger.error('Failed to toggle bot', { botId: id, error });
325
+ res.status(500).json({ error: 'Failed to toggle bot' });
326
+ }
327
+ });
221
328
  this.logger.info('Routes configured', {
222
329
  routes: [
223
330
  '/health - Server health check',
224
331
  '/daemon/status - Daemon context monitor',
225
- '/api/mcp - MCP protocol (JSON-RPC 2.0)'
332
+ '/api/mcp - MCP protocol (JSON-RPC 2.0)',
333
+ '/api/bots - Bot configuration API'
226
334
  ]
227
335
  });
228
336
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Bug Reports Module - Configuration Discovery
3
+ *
4
+ * Discovers Bug Reports workflow by name pattern.
5
+ * No hardcoded IDs - works in any workspace.
6
+ */
7
+ import type { UserContext } from '../../mcp/UserContextCache';
8
+ import type { BugReportsConfig, WorkflowDiscoveryResult } from './types';
9
+ /**
10
+ * Discover Bug Reports workflow in a workspace
11
+ */
12
+ export declare function discoverWorkflow(userContext: UserContext, config: BugReportsConfig): Promise<WorkflowDiscoveryResult | null>;
13
+ /**
14
+ * Load config from MCP Config workflow or environment
15
+ */
16
+ export declare function loadConfig(userContext: UserContext): Promise<BugReportsConfig>;
17
+ /**
18
+ * Get default configuration
19
+ */
20
+ export declare function getDefaultConfig(): BugReportsConfig;
21
+ /**
22
+ * Save config back to Hailer (creates/updates config activity)
23
+ */
24
+ export declare function saveConfig(userContext: UserContext, config: BugReportsConfig): Promise<boolean>;
25
+ //# sourceMappingURL=bug-config.d.ts.map