@hailer/mcp 1.0.29 → 1.1.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/.claude/.session-checked +1 -0
- package/.claude/agents/agent-ada-skill-builder.md +10 -2
- package/.claude/agents/agent-alejandro-function-fields.md +104 -37
- package/.claude/agents/agent-bjorn-config-audit.md +41 -21
- package/.claude/agents/agent-builder-agent-creator.md +13 -3
- package/.claude/agents/agent-code-simplifier.md +53 -0
- package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
- package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
- package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
- package/.claude/agents/agent-helga-workflow-config.md +75 -10
- package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
- package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
- package/.claude/agents/agent-ivan-monolith.md +154 -0
- package/.claude/agents/agent-kenji-data-reader.md +15 -8
- package/.claude/agents/agent-lars-code-inspector.md +56 -8
- package/.claude/agents/agent-marco-mockup-builder.md +110 -0
- package/.claude/agents/agent-marcus-api-documenter.md +323 -0
- package/.claude/agents/agent-marketplace-publisher.md +232 -72
- package/.claude/agents/agent-marketplace-reviewer.md +255 -79
- package/.claude/agents/agent-permissions-handler.md +208 -0
- package/.claude/agents/agent-simple-writer.md +48 -0
- package/.claude/agents/agent-svetlana-code-review.md +127 -14
- package/.claude/agents/agent-tanya-test-runner.md +333 -0
- package/.claude/agents/agent-ui-designer.md +100 -0
- package/.claude/agents/agent-viktor-sql-insights.md +19 -6
- package/.claude/agents/agent-web-search.md +55 -0
- package/.claude/agents/agent-yevgeni-discussions.md +7 -1
- package/.claude/agents/agent-zara-zapier.md +159 -0
- package/.claude/commands/app-squad.md +135 -0
- package/.claude/commands/audit-squad.md +158 -0
- package/.claude/commands/autoplan.md +563 -0
- package/.claude/commands/cleanup-squad.md +98 -0
- package/.claude/commands/config-squad.md +106 -0
- package/.claude/commands/crud-squad.md +87 -0
- package/.claude/commands/data-squad.md +97 -0
- package/.claude/commands/debug-squad.md +303 -0
- package/.claude/commands/doc-squad.md +65 -0
- package/.claude/commands/handoff.md +137 -0
- package/.claude/commands/health.md +49 -0
- package/.claude/commands/help.md +2 -1
- package/.claude/commands/help:agents.md +96 -16
- package/.claude/commands/help:commands.md +55 -11
- package/.claude/commands/help:faq.md +16 -1
- package/.claude/commands/help:skills.md +93 -0
- package/.claude/commands/hotfix-squad.md +112 -0
- package/.claude/commands/integration-squad.md +82 -0
- package/.claude/commands/janitor-squad.md +167 -0
- package/.claude/commands/learn-auto.md +120 -0
- package/.claude/commands/learn.md +120 -0
- package/.claude/commands/mcp-list.md +27 -0
- package/.claude/commands/onboard-squad.md +140 -0
- package/.claude/commands/plan-workspace.md +732 -0
- package/.claude/commands/prd.md +131 -0
- package/.claude/commands/project-status.md +82 -0
- package/.claude/commands/publish.md +138 -0
- package/.claude/commands/recap.md +69 -0
- package/.claude/commands/restore.md +64 -0
- package/.claude/commands/review-squad.md +152 -0
- package/.claude/commands/save.md +24 -0
- package/.claude/commands/stats.md +19 -0
- package/.claude/commands/swarm.md +210 -0
- package/.claude/commands/tool-builder.md +3 -1
- package/.claude/commands/ws-pull.md +1 -1
- package/.claude/commands/yolo-off.md +17 -0
- package/.claude/commands/yolo.md +82 -0
- package/.claude/hooks/_shared-memory.cjs +305 -0
- package/.claude/hooks/_utils.cjs +134 -0
- package/.claude/hooks/agent-failure-detector.cjs +164 -79
- package/.claude/hooks/agent-usage-logger.cjs +204 -0
- package/.claude/hooks/app-edit-guard.cjs +20 -4
- package/.claude/hooks/auto-learn.cjs +316 -0
- package/.claude/hooks/bash-guard.cjs +282 -0
- package/.claude/hooks/builder-mode-manager.cjs +183 -54
- package/.claude/hooks/bulk-activity-guard.cjs +283 -0
- package/.claude/hooks/context-watchdog.cjs +292 -0
- package/.claude/hooks/delegation-reminder.cjs +478 -0
- package/.claude/hooks/design-system-lint.cjs +283 -0
- package/.claude/hooks/post-scaffold-hook.cjs +16 -3
- package/.claude/hooks/prompt-guard.cjs +366 -0
- package/.claude/hooks/publish-template-guard.cjs +16 -0
- package/.claude/hooks/session-start.cjs +35 -0
- package/.claude/hooks/shared-memory-writer.cjs +147 -0
- package/.claude/hooks/skill-injector.cjs +140 -0
- package/.claude/hooks/skill-usage-logger.cjs +258 -0
- package/.claude/hooks/src-edit-guard.cjs +16 -1
- package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
- package/.claude/scripts/yolo-toggle.cjs +142 -0
- package/.claude/settings.json +141 -14
- package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
- package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
- package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
- package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
- package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
- package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
- package/.claude/skills/agent-structure/SKILL.md +98 -0
- package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
- package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
- package/.claude/skills/delegation-routing/SKILL.md +202 -0
- package/.claude/skills/frontend-design/SKILL.md +254 -0
- package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
- package/.claude/skills/hailer-api-client/SKILL.md +518 -0
- package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
- package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
- package/.claude/skills/hailer-design-system/SKILL.md +235 -0
- package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
- package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
- package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
- package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
- package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
- package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
- package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
- package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
- package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
- package/.claude/skills/integration-patterns/SKILL.md +421 -0
- package/.claude/skills/json-only-output/SKILL.md +52 -12
- package/.claude/skills/lsp-setup/SKILL.md +160 -0
- package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
- package/.claude/skills/optional-parameters/SKILL.md +32 -23
- package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
- package/.claude/skills/testing-patterns/SKILL.md +630 -0
- package/.claude/skills/tool-builder/SKILL.md +250 -0
- package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
- package/.claude/skills/tool-response-verification/SKILL.md +82 -48
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
- package/.env.example +26 -7
- package/CLAUDE.md +290 -224
- package/dist/CLAUDE.md +370 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +101 -101
- package/dist/bot/bot-config.d.ts +26 -0
- package/dist/bot/bot-config.js +135 -0
- package/dist/bot/bot-manager.d.ts +40 -0
- package/dist/bot/bot-manager.js +137 -0
- package/dist/bot/bot.d.ts +127 -0
- package/dist/bot/bot.js +1328 -0
- package/dist/bot/operation-logger.d.ts +28 -0
- package/dist/bot/operation-logger.js +132 -0
- package/dist/bot/services/conversation-manager.d.ts +60 -0
- package/dist/bot/services/conversation-manager.js +246 -0
- package/dist/bot/services/index.d.ts +9 -0
- package/dist/bot/services/index.js +18 -0
- package/dist/bot/services/message-classifier.d.ts +42 -0
- package/dist/bot/services/message-classifier.js +228 -0
- package/dist/bot/services/message-formatter.d.ts +88 -0
- package/dist/bot/services/message-formatter.js +411 -0
- package/dist/bot/services/session-logger.d.ts +162 -0
- package/dist/bot/services/session-logger.js +724 -0
- package/dist/bot/services/token-billing.d.ts +78 -0
- package/dist/bot/services/token-billing.js +233 -0
- package/dist/bot/services/types.d.ts +169 -0
- package/dist/bot/services/types.js +12 -0
- package/dist/bot/services/typing-indicator.d.ts +23 -0
- package/dist/bot/services/typing-indicator.js +60 -0
- package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
- package/dist/bot/services/workspace-schema-cache.js +506 -0
- package/dist/bot/tool-executor.d.ts +28 -0
- package/dist/bot/tool-executor.js +48 -0
- package/dist/bot/workspace-overview.d.ts +12 -0
- package/dist/bot/workspace-overview.js +94 -0
- package/dist/cli.d.ts +1 -8
- package/dist/cli.js +1 -253
- package/dist/config.d.ts +96 -3
- package/dist/config.js +148 -37
- package/dist/core.d.ts +5 -0
- package/dist/core.js +61 -8
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/lib/logger.d.ts +0 -1
- package/dist/lib/logger.js +39 -23
- package/dist/lib/request-logger.d.ts +77 -0
- package/dist/lib/request-logger.js +147 -0
- package/dist/mcp/UserContextCache.js +16 -13
- package/dist/mcp/hailer-clients.js +18 -17
- package/dist/mcp/signal-handler.js +43 -13
- package/dist/mcp/tool-registry.d.ts +4 -15
- package/dist/mcp/tool-registry.js +94 -32
- package/dist/mcp/tools/activity.js +28 -69
- package/dist/mcp/tools/app-core.js +9 -4
- package/dist/mcp/tools/app-marketplace.js +22 -12
- package/dist/mcp/tools/app-member.js +5 -2
- package/dist/mcp/tools/app-scaffold.js +32 -18
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/discussion.js +107 -77
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/file.js +5 -2
- package/dist/mcp/tools/insight.js +36 -12
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/user.d.ts +2 -4
- package/dist/mcp/tools/user.js +9 -50
- package/dist/mcp/tools/workflow.d.ts +1 -0
- package/dist/mcp/tools/workflow.js +164 -52
- package/dist/mcp/utils/hailer-api-client.js +26 -17
- package/dist/mcp/webhook-handler.d.ts +64 -3
- package/dist/mcp/webhook-handler.js +227 -9
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +237 -25
- package/dist/plugins/bug-fixer/index.d.ts +2 -0
- package/dist/plugins/bug-fixer/index.js +18 -0
- package/dist/plugins/bug-fixer/tools.d.ts +45 -0
- package/dist/plugins/bug-fixer/tools.js +1096 -0
- package/package.json +10 -10
- package/scripts/test-hal-tools.ts +154 -0
- package/.claude/agents/agent-nora-name-functions.md +0 -123
- package/.claude/assistant-knowledge.md +0 -23
- package/.claude/commands/install-plugin.md +0 -261
- package/.claude/commands/list-plugins.md +0 -42
- package/.claude/commands/marketplace-setup.md +0 -33
- package/.claude/commands/publish-plugin.md +0 -55
- package/.claude/commands/uninstall-plugin.md +0 -87
- package/.claude/hooks/interactive-mode.cjs +0 -87
- package/.claude/hooks/mcp-server-guard.cjs +0 -108
- package/.claude/skills/marketplace-publishing.md +0 -155
- package/dist/bot/chat-bot.d.ts +0 -31
- package/dist/bot/chat-bot.js +0 -357
- package/dist/mcp/tools/metrics.d.ts +0 -13
- package/dist/mcp/tools/metrics.js +0 -546
- package/dist/stdio-server.d.ts +0 -14
- package/dist/stdio-server.js +0 -114
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Webhook Handler
|
|
3
|
+
* Webhook Handler for Bot Config Updates
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Receives activity updates from Hailer workflow webhooks and updates
|
|
6
|
+
* local .bot-config/{workspaceId}.json files.
|
|
6
7
|
*/
|
|
7
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -42,10 +43,23 @@ exports.generateWebhookSignature = generateWebhookSignature;
|
|
|
42
43
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
43
44
|
exports.getWebhookToken = getWebhookToken;
|
|
44
45
|
exports.getWebhookPath = getWebhookPath;
|
|
46
|
+
exports.onBotUpdate = onBotUpdate;
|
|
47
|
+
exports.handleBotConfigWebhook = handleBotConfigWebhook;
|
|
48
|
+
exports.getWorkspaceConfig = getWorkspaceConfig;
|
|
49
|
+
exports.listWorkspaceConfigs = listWorkspaceConfigs;
|
|
45
50
|
const fs = __importStar(require("fs"));
|
|
46
51
|
const path = __importStar(require("path"));
|
|
47
52
|
const crypto = __importStar(require("crypto"));
|
|
48
53
|
const logger_1 = require("../lib/logger");
|
|
54
|
+
const config_1 = require("../config");
|
|
55
|
+
// Optional: bot-config only available in bot server mode, not MCP terminal
|
|
56
|
+
let invalidateConfigCache = null;
|
|
57
|
+
try {
|
|
58
|
+
invalidateConfigCache = require('../bot-config').invalidateConfigCache;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Not available in MCP-only installs — safe to skip
|
|
62
|
+
}
|
|
49
63
|
const logger = (0, logger_1.createLogger)({ component: 'webhook-handler' });
|
|
50
64
|
const BOT_CONFIG_DIR = '.bot-config';
|
|
51
65
|
const WEBHOOK_SECRET_FILE = 'webhook-secret.txt';
|
|
@@ -56,12 +70,15 @@ const WEBHOOK_SECRET_FILE = 'webhook-secret.txt';
|
|
|
56
70
|
* Constant-time string comparison to prevent timing attacks
|
|
57
71
|
*/
|
|
58
72
|
function timingSafeEqual(a, b) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
const bufA = Buffer.from(a);
|
|
74
|
+
const bufB = Buffer.from(b);
|
|
75
|
+
if (bufA.length !== bufB.length) {
|
|
76
|
+
// Compare against dummy buffer of same length to maintain constant time
|
|
77
|
+
const dummy = Buffer.alloc(bufA.length);
|
|
78
|
+
crypto.timingSafeEqual(bufA, dummy);
|
|
62
79
|
return false;
|
|
63
80
|
}
|
|
64
|
-
return crypto.timingSafeEqual(
|
|
81
|
+
return crypto.timingSafeEqual(bufA, bufB);
|
|
65
82
|
}
|
|
66
83
|
/**
|
|
67
84
|
* Generate HMAC-SHA256 signature for webhook payload
|
|
@@ -103,7 +120,7 @@ function getWebhookToken() {
|
|
|
103
120
|
}
|
|
104
121
|
// Production without WEBHOOK_TOKEN: webhooks disabled (optional feature)
|
|
105
122
|
if (process.env.NODE_ENV === 'production') {
|
|
106
|
-
logger.
|
|
123
|
+
logger.debug('WEBHOOK_TOKEN not set - webhook endpoint disabled');
|
|
107
124
|
return null;
|
|
108
125
|
}
|
|
109
126
|
const configDir = path.join(process.cwd(), BOT_CONFIG_DIR);
|
|
@@ -128,14 +145,215 @@ function getWebhookToken() {
|
|
|
128
145
|
}
|
|
129
146
|
fs.writeFileSync(secretPath, token, { mode: 0o600 });
|
|
130
147
|
const maskedToken = `${token.slice(0, 4)}...${token.slice(-4)}`;
|
|
131
|
-
logger.
|
|
148
|
+
logger.debug('Generated dev webhook token', { maskedToken, path: secretPath });
|
|
132
149
|
return token;
|
|
133
150
|
}
|
|
134
151
|
/**
|
|
135
|
-
* Get the full webhook path with token
|
|
152
|
+
* Get the full webhook path with token (no prefix for security through obscurity)
|
|
136
153
|
*/
|
|
137
154
|
function getWebhookPath() {
|
|
138
155
|
const token = getWebhookToken();
|
|
139
156
|
return token ? `/${token}` : null;
|
|
140
157
|
}
|
|
158
|
+
let botUpdateCallback = null;
|
|
159
|
+
function onBotUpdate(callback) {
|
|
160
|
+
botUpdateCallback = callback;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get field value from webhook payload by key
|
|
164
|
+
*/
|
|
165
|
+
function getFieldValue(fields, key) {
|
|
166
|
+
const field = fields.find((f) => f.key === key);
|
|
167
|
+
return field?.value ?? null;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Load existing workspace config or create empty one
|
|
171
|
+
*/
|
|
172
|
+
function loadWorkspaceConfig(workspaceId) {
|
|
173
|
+
const configDir = path.join(process.cwd(), BOT_CONFIG_DIR);
|
|
174
|
+
const configPath = path.join(configDir, `${workspaceId}.json`);
|
|
175
|
+
if (fs.existsSync(configPath)) {
|
|
176
|
+
try {
|
|
177
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
logger.warn('Failed to load workspace config', { workspaceId, error: String(error) });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
workspaceId,
|
|
185
|
+
workspaceName: workspaceId,
|
|
186
|
+
specialists: [],
|
|
187
|
+
lastSynced: new Date().toISOString(),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Save workspace config to file
|
|
192
|
+
*/
|
|
193
|
+
function saveWorkspaceConfig(config) {
|
|
194
|
+
const configDir = path.join(process.cwd(), BOT_CONFIG_DIR);
|
|
195
|
+
// Ensure directory exists
|
|
196
|
+
if (!fs.existsSync(configDir)) {
|
|
197
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
const configPath = path.join(configDir, `${config.workspaceId}.json`);
|
|
200
|
+
config.lastSynced = new Date().toISOString();
|
|
201
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
202
|
+
logger.info('Saved workspace config', {
|
|
203
|
+
workspaceId: config.workspaceId,
|
|
204
|
+
path: configPath,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Process webhook payload and update workspace config
|
|
209
|
+
*/
|
|
210
|
+
function handleBotConfigWebhook(payload) {
|
|
211
|
+
const workspaceId = payload.cid;
|
|
212
|
+
logger.debug('Processing bot config webhook', {
|
|
213
|
+
activityId: payload._id,
|
|
214
|
+
activityName: payload.name,
|
|
215
|
+
workspaceId,
|
|
216
|
+
phase: payload.currentPhase,
|
|
217
|
+
});
|
|
218
|
+
// Extract fields
|
|
219
|
+
const email = getFieldValue(payload.fields, 'agentEmailInHailer');
|
|
220
|
+
const password = getFieldValue(payload.fields, 'password');
|
|
221
|
+
const botType = getFieldValue(payload.fields, 'botType');
|
|
222
|
+
const userId = getFieldValue(payload.fields, 'hailerProfile');
|
|
223
|
+
const schemaConfigStr = getFieldValue(payload.fields, 'schemaConfig');
|
|
224
|
+
// Validate required fields
|
|
225
|
+
if (!email || !password) {
|
|
226
|
+
logger.warn('Webhook missing credentials', {
|
|
227
|
+
activityId: payload._id,
|
|
228
|
+
hasEmail: !!email,
|
|
229
|
+
hasPassword: !!password,
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
action: 'skip',
|
|
234
|
+
workspaceId,
|
|
235
|
+
botType,
|
|
236
|
+
error: 'Missing email or password',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
// Parse schema config to determine if deployed or retired
|
|
240
|
+
let deployedPhaseId = null;
|
|
241
|
+
let retiredPhaseId = null;
|
|
242
|
+
if (schemaConfigStr) {
|
|
243
|
+
try {
|
|
244
|
+
const schemaConfig = JSON.parse(schemaConfigStr);
|
|
245
|
+
deployedPhaseId = schemaConfig.deployedPhaseId;
|
|
246
|
+
retiredPhaseId = schemaConfig.retiredPhaseId;
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
logger.warn('Failed to parse schemaConfig', { schemaConfigStr });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const isDeployed = deployedPhaseId ? payload.currentPhase === deployedPhaseId : true;
|
|
253
|
+
const isRetired = retiredPhaseId ? payload.currentPhase === retiredPhaseId : false;
|
|
254
|
+
const enabled = isDeployed && !isRetired;
|
|
255
|
+
// Load existing config
|
|
256
|
+
const config = loadWorkspaceConfig(workspaceId);
|
|
257
|
+
const botEntry = {
|
|
258
|
+
activityId: payload._id,
|
|
259
|
+
userId: userId || null,
|
|
260
|
+
email,
|
|
261
|
+
password,
|
|
262
|
+
botType: botType || 'unknown',
|
|
263
|
+
enabled,
|
|
264
|
+
displayName: payload.name, // Activity name from Agent Directory
|
|
265
|
+
};
|
|
266
|
+
let action;
|
|
267
|
+
// Handle orchestrator
|
|
268
|
+
if (botType === 'orchestrator') {
|
|
269
|
+
if (enabled) {
|
|
270
|
+
config.orchestrator = {
|
|
271
|
+
activityId: payload._id,
|
|
272
|
+
userId: userId || '',
|
|
273
|
+
email,
|
|
274
|
+
password,
|
|
275
|
+
displayName: payload.name,
|
|
276
|
+
};
|
|
277
|
+
action = 'update';
|
|
278
|
+
logger.info('Updated orchestrator', { workspaceId, email: (0, config_1.maskEmail)(email), displayName: payload.name });
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Orchestrator disabled - remove it
|
|
282
|
+
if (config.orchestrator?.activityId === payload._id) {
|
|
283
|
+
delete config.orchestrator;
|
|
284
|
+
action = 'remove';
|
|
285
|
+
logger.info('Removed orchestrator', { workspaceId, email: (0, config_1.maskEmail)(email) });
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
action = 'update';
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// Handle specialist
|
|
294
|
+
const existingIndex = config.specialists.findIndex((s) => s.activityId === payload._id);
|
|
295
|
+
if (existingIndex >= 0) {
|
|
296
|
+
// Update existing
|
|
297
|
+
config.specialists[existingIndex] = botEntry;
|
|
298
|
+
action = enabled ? 'update' : 'remove';
|
|
299
|
+
}
|
|
300
|
+
else if (enabled) {
|
|
301
|
+
// Add new
|
|
302
|
+
config.specialists.push(botEntry);
|
|
303
|
+
action = 'add';
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
action = 'update';
|
|
307
|
+
}
|
|
308
|
+
logger.info('Updated specialist', {
|
|
309
|
+
workspaceId,
|
|
310
|
+
email,
|
|
311
|
+
botType,
|
|
312
|
+
enabled,
|
|
313
|
+
action,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// Save config
|
|
317
|
+
saveWorkspaceConfig(config);
|
|
318
|
+
// Invalidate config cache so loadBotConfigs() reads fresh data
|
|
319
|
+
if (invalidateConfigCache)
|
|
320
|
+
invalidateConfigCache();
|
|
321
|
+
// Trigger callback for hot reload
|
|
322
|
+
if (botUpdateCallback) {
|
|
323
|
+
botUpdateCallback(workspaceId, botEntry, action);
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
success: true,
|
|
327
|
+
action,
|
|
328
|
+
workspaceId,
|
|
329
|
+
botType,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get workspace config (for debugging/status)
|
|
334
|
+
*/
|
|
335
|
+
function getWorkspaceConfig(workspaceId) {
|
|
336
|
+
const config = loadWorkspaceConfig(workspaceId);
|
|
337
|
+
return config.orchestrator || config.specialists.length > 0 ? config : null;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* List all workspace configs
|
|
341
|
+
*/
|
|
342
|
+
function listWorkspaceConfigs() {
|
|
343
|
+
const configDir = path.join(process.cwd(), BOT_CONFIG_DIR);
|
|
344
|
+
if (!fs.existsSync(configDir))
|
|
345
|
+
return [];
|
|
346
|
+
const files = fs.readdirSync(configDir).filter((f) => f.endsWith('.json'));
|
|
347
|
+
const configs = [];
|
|
348
|
+
for (const file of files) {
|
|
349
|
+
try {
|
|
350
|
+
const content = fs.readFileSync(path.join(configDir, file), 'utf-8');
|
|
351
|
+
configs.push(JSON.parse(content));
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
logger.warn('Failed to load workspace config', { file, error: String(error) });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return configs;
|
|
358
|
+
}
|
|
141
359
|
//# sourceMappingURL=webhook-handler.js.map
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export interface MCPServerConfig {
|
|
|
16
16
|
port: number;
|
|
17
17
|
corsOrigins: string[];
|
|
18
18
|
toolRegistry: ToolRegistry;
|
|
19
|
+
getDaemonStatus?: () => Record<string, Array<{
|
|
20
|
+
botId: string;
|
|
21
|
+
state: any;
|
|
22
|
+
}>>;
|
|
19
23
|
}
|
|
20
24
|
export declare class MCPServerService {
|
|
21
25
|
private app;
|
package/dist/mcp-server.js
CHANGED
|
@@ -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
|
};
|
|
@@ -16,12 +49,13 @@ const logger_1 = require("./lib/logger");
|
|
|
16
49
|
const config_1 = require("./config");
|
|
17
50
|
const UserContextCache_1 = require("./mcp/UserContextCache");
|
|
18
51
|
const tool_registry_1 = require("./mcp/tool-registry");
|
|
52
|
+
const webhook_handler_1 = require("./mcp/webhook-handler");
|
|
19
53
|
class MCPServerService {
|
|
20
54
|
app;
|
|
21
55
|
server;
|
|
22
56
|
logger;
|
|
23
57
|
config;
|
|
24
|
-
toolRegistry;
|
|
58
|
+
toolRegistry; // ← Injected
|
|
25
59
|
constructor(config) {
|
|
26
60
|
this.config = config;
|
|
27
61
|
this.toolRegistry = config.toolRegistry;
|
|
@@ -32,7 +66,7 @@ class MCPServerService {
|
|
|
32
66
|
this.app = (0, express_1.default)();
|
|
33
67
|
this.setupMiddleware();
|
|
34
68
|
this.setupRoutes();
|
|
35
|
-
this.logger.
|
|
69
|
+
this.logger.debug('MCP Server initialized', {
|
|
36
70
|
port: config.port,
|
|
37
71
|
corsOrigins: config.corsOrigins
|
|
38
72
|
});
|
|
@@ -48,16 +82,17 @@ class MCPServerService {
|
|
|
48
82
|
}));
|
|
49
83
|
}
|
|
50
84
|
this.app.use(express_1.default.json());
|
|
51
|
-
// Request logging middleware (skip health
|
|
85
|
+
// Request logging middleware (skip noise: health checks, OAuth discovery probes)
|
|
52
86
|
this.app.use((req, res, next) => {
|
|
53
87
|
const isHealthCheck = req.path === '/health';
|
|
88
|
+
const isOAuthProbe = req.path.startsWith('/.well-known/') || (req.method === 'GET' && req.path === '/api/mcp');
|
|
54
89
|
const start = Date.now();
|
|
55
90
|
const requestLogger = this.logger.child({
|
|
56
91
|
requestId: `req-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
|
|
57
92
|
method: req.method,
|
|
58
93
|
path: req.path
|
|
59
94
|
});
|
|
60
|
-
if (!isHealthCheck) {
|
|
95
|
+
if (!isHealthCheck && !isOAuthProbe) {
|
|
61
96
|
requestLogger.debug('Incoming request', {
|
|
62
97
|
url: req.url,
|
|
63
98
|
headers: req.headers['content-type'],
|
|
@@ -87,15 +122,31 @@ class MCPServerService {
|
|
|
87
122
|
status: 'ok',
|
|
88
123
|
timestamp: new Date().toISOString(),
|
|
89
124
|
service: 'hailer-mcp-server',
|
|
90
|
-
version:
|
|
125
|
+
version: config_1.APP_VERSION
|
|
91
126
|
};
|
|
92
127
|
res.json(health);
|
|
93
128
|
});
|
|
94
|
-
//
|
|
95
|
-
this.app.
|
|
96
|
-
|
|
129
|
+
// Daemon status endpoint - monitor LLM context
|
|
130
|
+
this.app.get('/daemon/status', (_, res) => {
|
|
131
|
+
if (!this.config.getDaemonStatus) {
|
|
132
|
+
res.status(404).json({ error: 'Daemon mode not enabled' });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const status = this.config.getDaemonStatus();
|
|
136
|
+
if (!status) {
|
|
137
|
+
res.status(503).json({ error: 'Daemon not running' });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
res.json({
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
daemons: status
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
// MCP Protocol handler - shared by both routes
|
|
146
|
+
const mcpHandler = async (req, res, apiKeyOverride) => {
|
|
147
|
+
const apiKey = apiKeyOverride || req.query.apiKey;
|
|
148
|
+
req.logger.debug('MCP request received', { method: req.body?.method, apiKey: apiKey?.slice(0, 8) + '...' });
|
|
97
149
|
try {
|
|
98
|
-
const apiKey = req.query.apiKey;
|
|
99
150
|
const mcpRequest = req.body;
|
|
100
151
|
let result;
|
|
101
152
|
if (mcpRequest.method === 'tools/list') {
|
|
@@ -118,19 +169,23 @@ class MCPServerService {
|
|
|
118
169
|
}
|
|
119
170
|
// Apply default tool group filtering
|
|
120
171
|
// - NUCLEAR: Only if ENABLE_NUCLEAR_TOOLS=true
|
|
172
|
+
// - BOT_INTERNAL: Only if explicitly requested via params.includeBotInternal (for daemons)
|
|
173
|
+
const includeBotInternal = mcpRequest.params?.includeBotInternal === true;
|
|
121
174
|
if (!filterConfig) {
|
|
122
|
-
// No filter yet - create default excluding NUCLEAR
|
|
175
|
+
// No filter yet - create default excluding NUCLEAR and BOT_INTERNAL
|
|
123
176
|
filterConfig = {
|
|
124
177
|
allowedGroups: [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.PLAYGROUND]
|
|
125
178
|
};
|
|
126
|
-
req.logger.debug('Using default tool filter (excludes NUCLEAR)');
|
|
179
|
+
req.logger.debug('Using default tool filter (excludes NUCLEAR and BOT_INTERNAL)');
|
|
127
180
|
}
|
|
128
181
|
else if (filterConfig.allowedGroups) {
|
|
129
|
-
// Filter groups - remove NUCLEAR unless enabled
|
|
130
|
-
filterConfig.allowedGroups = filterConfig.allowedGroups.filter(g => (
|
|
182
|
+
// Filter groups - remove BOT_INTERNAL unless explicitly requested, remove NUCLEAR unless enabled
|
|
183
|
+
filterConfig.allowedGroups = filterConfig.allowedGroups.filter(g => (includeBotInternal || g !== tool_registry_1.ToolGroup.BOT_INTERNAL) &&
|
|
184
|
+
(config_1.environment.ENABLE_NUCLEAR_TOOLS || g !== tool_registry_1.ToolGroup.NUCLEAR));
|
|
131
185
|
req.logger.debug('Filtered tool groups', {
|
|
132
186
|
allowedGroups: filterConfig.allowedGroups,
|
|
133
|
-
nuclearEnabled: config_1.environment.ENABLE_NUCLEAR_TOOLS
|
|
187
|
+
nuclearEnabled: config_1.environment.ENABLE_NUCLEAR_TOOLS,
|
|
188
|
+
includeBotInternal
|
|
134
189
|
});
|
|
135
190
|
}
|
|
136
191
|
result = {
|
|
@@ -154,7 +209,7 @@ class MCPServerService {
|
|
|
154
209
|
return this.sendMcpError(res, mcpRequest.id, -32602, 'API key required for tools/call', 400);
|
|
155
210
|
}
|
|
156
211
|
const { name, arguments: args = {} } = mcpRequest.params;
|
|
157
|
-
req.logger.
|
|
212
|
+
req.logger.info('Tool call', { tool: name });
|
|
158
213
|
// Check access control
|
|
159
214
|
if (!this.canAccessTool(name, apiKey, appConfig)) {
|
|
160
215
|
return this.sendMcpError(res, mcpRequest.id, -32603, `Access denied to tool: ${name}`, 403);
|
|
@@ -163,20 +218,20 @@ class MCPServerService {
|
|
|
163
218
|
result = await this.toolRegistry.executeTool(name, args, userContext);
|
|
164
219
|
}
|
|
165
220
|
else if (mcpRequest.method === 'initialize') {
|
|
166
|
-
req.logger.info('MCP initialize request received');
|
|
167
221
|
const sessionId = `session-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
168
222
|
res.setHeader('Mcp-Session-Id', sessionId);
|
|
223
|
+
req.logger.info('Client connected', { sessionId, clientInfo: mcpRequest.params?.clientInfo });
|
|
169
224
|
result = {
|
|
170
225
|
protocolVersion: '2024-11-05',
|
|
171
226
|
capabilities: { tools: {} },
|
|
172
227
|
serverInfo: {
|
|
173
228
|
name: 'hailer-mcp-server',
|
|
174
|
-
version:
|
|
229
|
+
version: config_1.APP_VERSION
|
|
175
230
|
}
|
|
176
231
|
};
|
|
177
232
|
}
|
|
178
233
|
else if (mcpRequest.method === 'notifications/initialized') {
|
|
179
|
-
req.logger.
|
|
234
|
+
req.logger.debug('MCP handshake completed - client initialized');
|
|
180
235
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
181
236
|
res.status(204).end();
|
|
182
237
|
return;
|
|
@@ -210,13 +265,170 @@ class MCPServerService {
|
|
|
210
265
|
this.sendMcpError(res, req.body?.id || null, -32000, `Server error: ${errorMessage}`, 500);
|
|
211
266
|
}
|
|
212
267
|
}
|
|
268
|
+
};
|
|
269
|
+
// MCP Protocol endpoint - JSON-RPC 2.0 over SSE
|
|
270
|
+
// Route 1: /api/mcp?apiKey=xxx (standard format)
|
|
271
|
+
this.app.post('/api/mcp', (req, res) => mcpHandler(req, res));
|
|
272
|
+
// Route 2: /:apiKey (simplified format - API key as path)
|
|
273
|
+
// Matches 16-64 char alphanumeric keys, but ONLY for MCP requests (has jsonrpc field)
|
|
274
|
+
// Non-MCP requests (webhooks) pass through to later routes
|
|
275
|
+
this.app.post('/:apiKey([a-zA-Z0-9_-]{16,64})', (req, res, next) => {
|
|
276
|
+
if (req.body?.jsonrpc) {
|
|
277
|
+
// MCP request - handle it
|
|
278
|
+
mcpHandler(req, res, req.params.apiKey);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Not MCP (likely webhook) - pass to next route
|
|
282
|
+
next();
|
|
283
|
+
}
|
|
213
284
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
285
|
+
// ===== Bot Configuration API (only when MCP_CLIENT_ENABLED=true) =====
|
|
286
|
+
if (config_1.environment.MCP_CLIENT_ENABLED) {
|
|
287
|
+
// GET /api/bots - List all bots and their status
|
|
288
|
+
this.app.get('/api/bots', async (req, res) => {
|
|
289
|
+
req.logger.debug('List bots requested');
|
|
290
|
+
try {
|
|
291
|
+
const { AVAILABLE_BOTS, getBotState } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
|
|
292
|
+
const state = getBotState();
|
|
293
|
+
const bots = AVAILABLE_BOTS.map(bot => ({
|
|
294
|
+
...bot,
|
|
295
|
+
enabled: state[bot.id] || false
|
|
296
|
+
}));
|
|
297
|
+
res.json({ bots });
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
req.logger.error('Failed to list bots', { error });
|
|
301
|
+
res.status(500).json({ error: 'Failed to list bots' });
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
// POST /api/bots/:id/enable - Enable a bot
|
|
305
|
+
this.app.post('/api/bots/:id/enable', async (req, res) => {
|
|
306
|
+
const { id } = req.params;
|
|
307
|
+
req.logger.debug('Enable bot requested', { botId: id });
|
|
308
|
+
try {
|
|
309
|
+
const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
|
|
310
|
+
const bot = AVAILABLE_BOTS.find(b => b.id === id);
|
|
311
|
+
if (!bot) {
|
|
312
|
+
return res.status(404).json({ error: `Unknown bot: ${id}` });
|
|
313
|
+
}
|
|
314
|
+
setBotEnabled(id, true);
|
|
315
|
+
res.json({ success: true, botId: id, enabled: true });
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
req.logger.error('Failed to enable bot', { botId: id, error });
|
|
319
|
+
res.status(500).json({ error: 'Failed to enable bot' });
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
// POST /api/bots/:id/disable - Disable a bot
|
|
323
|
+
this.app.post('/api/bots/:id/disable', async (req, res) => {
|
|
324
|
+
const { id } = req.params;
|
|
325
|
+
req.logger.debug('Disable bot requested', { botId: id });
|
|
326
|
+
try {
|
|
327
|
+
const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
|
|
328
|
+
const bot = AVAILABLE_BOTS.find(b => b.id === id);
|
|
329
|
+
if (!bot) {
|
|
330
|
+
return res.status(404).json({ error: `Unknown bot: ${id}` });
|
|
331
|
+
}
|
|
332
|
+
setBotEnabled(id, false);
|
|
333
|
+
res.json({ success: true, botId: id, enabled: false });
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
req.logger.error('Failed to disable bot', { botId: id, error });
|
|
337
|
+
res.status(500).json({ error: 'Failed to disable bot' });
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
// POST /api/bots/:id/toggle - Toggle a bot
|
|
341
|
+
this.app.post('/api/bots/:id/toggle', async (req, res) => {
|
|
342
|
+
const { id } = req.params;
|
|
343
|
+
req.logger.debug('Toggle bot requested', { botId: id });
|
|
344
|
+
try {
|
|
345
|
+
const { AVAILABLE_BOTS, getBotState, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
|
|
346
|
+
const bot = AVAILABLE_BOTS.find(b => b.id === id);
|
|
347
|
+
if (!bot) {
|
|
348
|
+
return res.status(404).json({ error: `Unknown bot: ${id}` });
|
|
349
|
+
}
|
|
350
|
+
const currentState = getBotState();
|
|
351
|
+
const newState = !currentState[id];
|
|
352
|
+
setBotEnabled(id, newState);
|
|
353
|
+
res.json({ success: true, botId: id, enabled: newState });
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
req.logger.error('Failed to toggle bot', { botId: id, error });
|
|
357
|
+
res.status(500).json({ error: 'Failed to toggle bot' });
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
// ===== Bot Config Webhook API =====
|
|
361
|
+
// Get secure webhook path (auto-generated token) - null if disabled
|
|
362
|
+
const webhookPath = (0, webhook_handler_1.getWebhookPath)();
|
|
363
|
+
if (webhookPath) {
|
|
364
|
+
// POST /webhook/{token} - Receives updates from Hailer workflow webhooks
|
|
365
|
+
this.app.post(webhookPath, (req, res) => {
|
|
366
|
+
req.logger.debug('Bot config webhook received', {
|
|
367
|
+
activityId: req.body?._id,
|
|
368
|
+
activityName: req.body?.name,
|
|
369
|
+
workspaceId: req.body?.cid,
|
|
370
|
+
});
|
|
371
|
+
try {
|
|
372
|
+
const result = (0, webhook_handler_1.handleBotConfigWebhook)(req.body);
|
|
373
|
+
if (result.success) {
|
|
374
|
+
req.logger.debug('Bot config updated via webhook', {
|
|
375
|
+
action: result.action,
|
|
376
|
+
workspaceId: result.workspaceId,
|
|
377
|
+
botType: result.botType,
|
|
378
|
+
});
|
|
379
|
+
res.status(200).json(result);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
req.logger.warn('Bot config webhook failed', { error: result.error });
|
|
383
|
+
res.status(400).json(result);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
req.logger.error('Bot config webhook error', { error });
|
|
388
|
+
res.status(500).json({
|
|
389
|
+
success: false,
|
|
390
|
+
error: error instanceof Error ? error.message : 'Internal error',
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
// GET /webhook/{token}/status - Status endpoint to see all workspace configs
|
|
395
|
+
this.app.get(`${webhookPath}/status`, (_req, res) => {
|
|
396
|
+
const configs = (0, webhook_handler_1.listWorkspaceConfigs)();
|
|
397
|
+
res.json({
|
|
398
|
+
timestamp: new Date().toISOString(),
|
|
399
|
+
workspaceCount: configs.length,
|
|
400
|
+
workspaces: configs.map((c) => ({
|
|
401
|
+
workspaceId: c.workspaceId,
|
|
402
|
+
workspaceName: c.workspaceName,
|
|
403
|
+
hasOrchestrator: !!c.orchestrator,
|
|
404
|
+
specialistCount: c.specialists.length,
|
|
405
|
+
enabledSpecialists: c.specialists.filter((s) => s.enabled).length,
|
|
406
|
+
lastSynced: c.lastSynced,
|
|
407
|
+
})),
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
this.logger.debug('Webhook endpoint disabled (no WEBHOOK_TOKEN)');
|
|
413
|
+
}
|
|
414
|
+
this.logger.debug('Routes configured', {
|
|
415
|
+
routes: [
|
|
416
|
+
'/health',
|
|
417
|
+
'/daemon/status',
|
|
418
|
+
'/api/mcp',
|
|
419
|
+
'/api/bots'
|
|
420
|
+
]
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
this.logger.debug('Routes configured', {
|
|
425
|
+
routes: [
|
|
426
|
+
'/health',
|
|
427
|
+
'/daemon/status',
|
|
428
|
+
'/api/mcp'
|
|
429
|
+
]
|
|
430
|
+
});
|
|
431
|
+
}
|
|
220
432
|
}
|
|
221
433
|
/**
|
|
222
434
|
* Check if agent has access to a specific tool
|
|
@@ -275,7 +487,7 @@ class MCPServerService {
|
|
|
275
487
|
const server = this.server;
|
|
276
488
|
return new Promise((resolve) => {
|
|
277
489
|
server.close(() => {
|
|
278
|
-
this.logger.
|
|
490
|
+
this.logger.debug('MCP Server stopped gracefully');
|
|
279
491
|
resolve();
|
|
280
492
|
});
|
|
281
493
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./tools"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|