@hailer/mcp 0.1.14 → 0.1.16
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/agents/agent-giuseppe-app-builder.md +7 -6
- package/.claude/agents/agent-lars-code-inspector.md +26 -14
- package/dist/agents/bot-manager.d.ts +48 -0
- package/dist/agents/bot-manager.js +254 -0
- package/dist/agents/factory.d.ts +150 -0
- package/dist/agents/factory.js +650 -0
- package/dist/agents/giuseppe/ai.d.ts +83 -0
- package/dist/agents/giuseppe/ai.js +466 -0
- package/dist/agents/giuseppe/bot.d.ts +110 -0
- package/dist/agents/giuseppe/bot.js +780 -0
- package/dist/agents/giuseppe/config.d.ts +25 -0
- package/dist/agents/giuseppe/config.js +227 -0
- package/dist/agents/giuseppe/files.d.ts +52 -0
- package/dist/agents/giuseppe/files.js +338 -0
- package/dist/agents/giuseppe/git.d.ts +48 -0
- package/dist/agents/giuseppe/git.js +298 -0
- package/dist/agents/giuseppe/index.d.ts +97 -0
- package/dist/agents/giuseppe/index.js +258 -0
- package/dist/agents/giuseppe/lsp.d.ts +113 -0
- package/dist/agents/giuseppe/lsp.js +485 -0
- package/dist/agents/giuseppe/monitor.d.ts +118 -0
- package/dist/agents/giuseppe/monitor.js +621 -0
- package/dist/agents/giuseppe/prompt.d.ts +5 -0
- package/dist/agents/giuseppe/prompt.js +94 -0
- package/dist/agents/giuseppe/registries/pending-classification.d.ts +28 -0
- package/dist/agents/giuseppe/registries/pending-classification.js +50 -0
- package/dist/agents/giuseppe/registries/pending-fix.d.ts +30 -0
- package/dist/agents/giuseppe/registries/pending-fix.js +42 -0
- package/dist/agents/giuseppe/registries/pending.d.ts +27 -0
- package/dist/agents/giuseppe/registries/pending.js +49 -0
- package/dist/agents/giuseppe/specialist.d.ts +47 -0
- package/dist/agents/giuseppe/specialist.js +237 -0
- package/dist/agents/giuseppe/types.d.ts +123 -0
- package/dist/agents/giuseppe/types.js +9 -0
- package/dist/agents/hailer-expert/index.d.ts +8 -0
- package/dist/agents/hailer-expert/index.js +14 -0
- package/dist/agents/hal/daemon.d.ts +142 -0
- package/dist/agents/hal/daemon.js +1103 -0
- package/dist/agents/hal/definitions.d.ts +55 -0
- package/dist/agents/hal/definitions.js +263 -0
- package/dist/agents/hal/index.d.ts +3 -0
- package/dist/agents/hal/index.js +8 -0
- package/dist/agents/index.d.ts +18 -0
- package/dist/agents/index.js +48 -0
- package/dist/agents/shared/base.d.ts +216 -0
- package/dist/agents/shared/base.js +846 -0
- package/dist/agents/shared/services/agent-registry.d.ts +107 -0
- package/dist/agents/shared/services/agent-registry.js +629 -0
- package/dist/agents/shared/services/conversation-manager.d.ts +50 -0
- package/dist/agents/shared/services/conversation-manager.js +136 -0
- package/dist/agents/shared/services/mcp-client.d.ts +56 -0
- package/dist/agents/shared/services/mcp-client.js +124 -0
- package/dist/agents/shared/services/message-classifier.d.ts +37 -0
- package/dist/agents/shared/services/message-classifier.js +187 -0
- package/dist/agents/shared/services/message-formatter.d.ts +89 -0
- package/dist/agents/shared/services/message-formatter.js +371 -0
- package/dist/agents/shared/services/session-logger.d.ts +106 -0
- package/dist/agents/shared/services/session-logger.js +446 -0
- package/dist/agents/shared/services/tool-executor.d.ts +41 -0
- package/dist/agents/shared/services/tool-executor.js +169 -0
- package/dist/agents/shared/services/workspace-schema-cache.d.ts +125 -0
- package/dist/agents/shared/services/workspace-schema-cache.js +578 -0
- package/dist/agents/shared/specialist.d.ts +91 -0
- package/dist/agents/shared/specialist.js +399 -0
- package/dist/agents/shared/tool-schema-loader.d.ts +62 -0
- package/dist/agents/shared/tool-schema-loader.js +232 -0
- package/dist/agents/shared/types.d.ts +327 -0
- package/dist/agents/shared/types.js +121 -0
- package/dist/app.js +21 -4
- package/dist/cli.js +0 -0
- package/dist/client/agents/orchestrator.d.ts +1 -0
- package/dist/client/agents/orchestrator.js +12 -1
- package/dist/commands/seed-config.d.ts +9 -0
- package/dist/commands/seed-config.js +372 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +61 -1
- package/dist/core.d.ts +8 -0
- package/dist/core.js +137 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/mcp/UserContextCache.js +2 -2
- package/dist/mcp/hailer-clients.d.ts +15 -0
- package/dist/mcp/hailer-clients.js +100 -6
- package/dist/mcp/signal-handler.d.ts +16 -5
- package/dist/mcp/signal-handler.js +173 -122
- package/dist/mcp/tools/activity.js +9 -1
- package/dist/mcp/tools/bot-config.d.ts +184 -9
- package/dist/mcp/tools/bot-config.js +2177 -163
- package/dist/mcp/tools/giuseppe-tools.d.ts +21 -0
- package/dist/mcp/tools/giuseppe-tools.js +525 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +42 -1
- package/dist/mcp/utils/hailer-api-client.js +128 -2
- package/dist/mcp/webhook-handler.d.ts +87 -0
- package/dist/mcp/webhook-handler.js +343 -0
- package/dist/mcp/workspace-cache.d.ts +5 -0
- package/dist/mcp/workspace-cache.js +11 -0
- package/dist/mcp-server.js +55 -5
- package/dist/modules/bug-reports/giuseppe-agent.d.ts +58 -0
- package/dist/modules/bug-reports/giuseppe-agent.js +467 -0
- package/dist/modules/bug-reports/giuseppe-ai.d.ts +25 -1
- package/dist/modules/bug-reports/giuseppe-ai.js +133 -2
- package/dist/modules/bug-reports/giuseppe-bot.d.ts +3 -2
- package/dist/modules/bug-reports/giuseppe-bot.js +75 -36
- package/dist/modules/bug-reports/giuseppe-daemon.d.ts +80 -0
- package/dist/modules/bug-reports/giuseppe-daemon.js +617 -0
- package/dist/modules/bug-reports/giuseppe-files.d.ts +12 -0
- package/dist/modules/bug-reports/giuseppe-files.js +37 -0
- package/dist/modules/bug-reports/giuseppe-lsp.d.ts +113 -0
- package/dist/modules/bug-reports/giuseppe-lsp.js +485 -0
- package/dist/modules/bug-reports/index.d.ts +1 -0
- package/dist/modules/bug-reports/index.js +31 -29
- package/package.json +5 -4
package/dist/app.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("./core");
|
|
4
4
|
const config_1 = require("./config");
|
|
5
|
+
const logger_1 = require("./lib/logger");
|
|
6
|
+
const logger = (0, logger_1.createLogger)({ component: 'app' });
|
|
5
7
|
const file_1 = require("./mcp/tools/file");
|
|
6
8
|
const activity_1 = require("./mcp/tools/activity");
|
|
7
9
|
const discussion_1 = require("./mcp/tools/discussion");
|
|
@@ -10,9 +12,10 @@ const workflow_1 = require("./mcp/tools/workflow");
|
|
|
10
12
|
const insight_1 = require("./mcp/tools/insight");
|
|
11
13
|
const app_1 = require("./mcp/tools/app");
|
|
12
14
|
const bot_config_1 = require("./mcp/tools/bot-config");
|
|
15
|
+
const giuseppe_tools_1 = require("./mcp/tools/giuseppe-tools");
|
|
13
16
|
const core = new core_1.Core();
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
logger.info('Registering tools...');
|
|
18
|
+
logger.info('Nuclear tools status', { enabled: config_1.environment.ENABLE_NUCLEAR_TOOLS });
|
|
16
19
|
core.addTool(file_1.uploadFilesTool);
|
|
17
20
|
core.addTool(activity_1.listActivitiesTool);
|
|
18
21
|
core.addTool(activity_1.showActivityByIdTool);
|
|
@@ -77,10 +80,24 @@ core.addTool(app_1.installMarketplaceAppTool);
|
|
|
77
80
|
core.addTool(bot_config_1.listBotsConfigTool);
|
|
78
81
|
core.addTool(bot_config_1.enableBotTool);
|
|
79
82
|
core.addTool(bot_config_1.disableBotTool);
|
|
80
|
-
|
|
83
|
+
core.addTool(bot_config_1.checkSpecialistStatusTool);
|
|
84
|
+
// Giuseppe bug-fixing tools
|
|
85
|
+
core.addTool(giuseppe_tools_1.giuseppeFindAppTool);
|
|
86
|
+
core.addTool(giuseppe_tools_1.giuseppeListFilesTool);
|
|
87
|
+
core.addTool(giuseppe_tools_1.giuseppeReadFileTool);
|
|
88
|
+
core.addTool(giuseppe_tools_1.giuseppeWriteFileTool);
|
|
89
|
+
core.addTool(giuseppe_tools_1.giuseppeApplyFixTool);
|
|
90
|
+
core.addTool(giuseppe_tools_1.giuseppeRunBuildTool);
|
|
91
|
+
core.addTool(giuseppe_tools_1.giuseppeGitStatusTool);
|
|
92
|
+
core.addTool(giuseppe_tools_1.giuseppeGitPullTool);
|
|
93
|
+
core.addTool(giuseppe_tools_1.giuseppeGitCommitTool);
|
|
94
|
+
core.addTool(giuseppe_tools_1.giuseppeGitPushTool);
|
|
95
|
+
core.addTool(giuseppe_tools_1.giuseppeGitRevertTool);
|
|
96
|
+
core.addTool(giuseppe_tools_1.giuseppePublishAppTool);
|
|
97
|
+
logger.info('All tools registered successfully');
|
|
81
98
|
// Start the application
|
|
82
99
|
core.start().catch((error) => {
|
|
83
|
-
|
|
100
|
+
logger.error('Failed to start Hailer MCP application', error);
|
|
84
101
|
process.exit(1);
|
|
85
102
|
});
|
|
86
103
|
// Export core for testing and external access
|
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -80,6 +80,7 @@ export declare class OrchestratorDaemon extends ChatAgentDaemon {
|
|
|
80
80
|
private updateContextMemory;
|
|
81
81
|
/**
|
|
82
82
|
* Override to update cross-discussion memory when entering activity discussions
|
|
83
|
+
* Also checks if a specialist bot (like Giuseppe) has locked this discussion
|
|
83
84
|
*/
|
|
84
85
|
protected extractIncomingMessage(signal: HailerSignal): Promise<IncomingMessage | null>;
|
|
85
86
|
/**
|
|
@@ -19,6 +19,7 @@ const definitions_1 = require("./definitions");
|
|
|
19
19
|
const logger_1 = require("../../lib/logger");
|
|
20
20
|
const pending_fix_registry_1 = require("../../modules/bug-reports/pending-fix-registry");
|
|
21
21
|
const pending_classification_registry_1 = require("../../modules/bug-reports/pending-classification-registry");
|
|
22
|
+
const discussion_lock_1 = require("../../lib/discussion-lock");
|
|
22
23
|
class OrchestratorDaemon extends base_1.ChatAgentDaemon {
|
|
23
24
|
orchestratorLogger;
|
|
24
25
|
specialists = new Map();
|
|
@@ -369,11 +370,21 @@ class OrchestratorDaemon extends base_1.ChatAgentDaemon {
|
|
|
369
370
|
}
|
|
370
371
|
/**
|
|
371
372
|
* Override to update cross-discussion memory when entering activity discussions
|
|
373
|
+
* Also checks if a specialist bot (like Giuseppe) has locked this discussion
|
|
372
374
|
*/
|
|
373
375
|
async extractIncomingMessage(signal) {
|
|
374
376
|
const message = await super.extractIncomingMessage(signal);
|
|
377
|
+
if (!message)
|
|
378
|
+
return null;
|
|
379
|
+
// Check if this discussion is locked by a specialist bot (e.g., Giuseppe handling a bug)
|
|
380
|
+
if ((0, discussion_lock_1.isDiscussionLocked)(message.discussionId, 'hal')) {
|
|
381
|
+
this.orchestratorLogger.debug('Discussion locked by specialist, skipping', {
|
|
382
|
+
discussionId: message.discussionId
|
|
383
|
+
});
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
375
386
|
// Update context memory if this message is from an activity discussion
|
|
376
|
-
if (message
|
|
387
|
+
if (message.linkedActivityId) {
|
|
377
388
|
this.updateContextMemory(message.linkedActivityId, message.linkedActivityName || null);
|
|
378
389
|
}
|
|
379
390
|
return message;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Seed Bot Configuration
|
|
4
|
+
*
|
|
5
|
+
* Connects as master user, discovers Agent Directory workflows across all workspaces,
|
|
6
|
+
* and writes bot configuration to .bot-config/{workspaceId}.json
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=seed-config.d.ts.map
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Seed Bot Configuration
|
|
5
|
+
*
|
|
6
|
+
* Connects as master user, discovers Agent Directory workflows across all workspaces,
|
|
7
|
+
* and writes bot configuration to .bot-config/{workspaceId}.json
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
const dotenv = __importStar(require("dotenv"));
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const hailer_clients_1 = require("../mcp/hailer-clients");
|
|
47
|
+
const hailer_api_client_1 = require("../mcp/utils/hailer-api-client");
|
|
48
|
+
// Load environment variables
|
|
49
|
+
dotenv.config();
|
|
50
|
+
// Constants
|
|
51
|
+
const BOT_CONFIG_DIR = '.bot-config';
|
|
52
|
+
const AGENT_DIRECTORY_PATTERNS = ['Agent Directory', 'AI Agents', '🤖 Agent Directory'];
|
|
53
|
+
const DEPLOYED_PHASE_PATTERNS = ['deployed', 'active', 'enabled'];
|
|
54
|
+
const RETIRED_PHASE_PATTERNS = ['retired', 'disabled', 'inactive'];
|
|
55
|
+
const FIELD_KEY_HAILER_PROFILE = ['hailerProfile', 'Agent Hailer profile'];
|
|
56
|
+
const FIELD_KEY_EMAIL = ['agentEmailInHailer', 'email', 'Email of Hailer profile'];
|
|
57
|
+
const FIELD_KEY_PASSWORD = ['password'];
|
|
58
|
+
const FIELD_KEY_BOT_TYPE = ['botType', 'bot_type', 'type'];
|
|
59
|
+
// Helper functions
|
|
60
|
+
function findFieldId(fields, patterns) {
|
|
61
|
+
for (const pattern of patterns) {
|
|
62
|
+
if (fields[pattern])
|
|
63
|
+
return fields[pattern];
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function findPhaseByPattern(phases, patterns) {
|
|
68
|
+
for (const pattern of patterns) {
|
|
69
|
+
const match = phases.find(p => p.name.toLowerCase().includes(pattern.toLowerCase()));
|
|
70
|
+
if (match)
|
|
71
|
+
return match.id;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function looksLikeUserId(value) {
|
|
76
|
+
return /^[a-f0-9]{24}$/i.test(value);
|
|
77
|
+
}
|
|
78
|
+
function extractCredentialsFromActivity(activity, schema) {
|
|
79
|
+
const fields = activity.fields || {};
|
|
80
|
+
let email = null;
|
|
81
|
+
let password = null;
|
|
82
|
+
// Try fields object format - by field KEY first (from returnFlat: true)
|
|
83
|
+
if (fields.agentEmailInHailer) {
|
|
84
|
+
email = typeof fields.agentEmailInHailer === 'string' ? fields.agentEmailInHailer : (fields.agentEmailInHailer?.value || null);
|
|
85
|
+
}
|
|
86
|
+
if (fields.password) {
|
|
87
|
+
password = typeof fields.password === 'string' ? fields.password : (fields.password?.value || null);
|
|
88
|
+
}
|
|
89
|
+
// Try fields object format - by field ID
|
|
90
|
+
if (!email && schema.emailFieldId) {
|
|
91
|
+
const emailField = fields[schema.emailFieldId];
|
|
92
|
+
if (emailField) {
|
|
93
|
+
email = emailField.value || emailField;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!password && schema.passwordFieldId) {
|
|
97
|
+
const passwordField = fields[schema.passwordFieldId];
|
|
98
|
+
if (passwordField) {
|
|
99
|
+
password = passwordField.value || passwordField;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Fallback: find email by pattern
|
|
103
|
+
if (!email) {
|
|
104
|
+
for (const [, fieldValue] of Object.entries(fields)) {
|
|
105
|
+
const strValue = typeof fieldValue === 'string' ? fieldValue : fieldValue?.value;
|
|
106
|
+
if (typeof strValue === 'string' && strValue.includes('@') && strValue.includes('.')) {
|
|
107
|
+
email = strValue.replace(/['"]/g, '').trim();
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (email && password) {
|
|
113
|
+
return { email, password };
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
function extractUserIdFromActivity(activity, schema) {
|
|
118
|
+
const fields = activity.fields || {};
|
|
119
|
+
// Try fields object format - by field KEY first
|
|
120
|
+
if (fields.hailerProfile) {
|
|
121
|
+
const value = fields.hailerProfile;
|
|
122
|
+
return typeof value === 'string' ? value : (Array.isArray(value) ? value[0] : null);
|
|
123
|
+
}
|
|
124
|
+
// Try fields object format - by field ID
|
|
125
|
+
if (schema.hailerProfileFieldId) {
|
|
126
|
+
const profileField = fields[schema.hailerProfileFieldId];
|
|
127
|
+
if (profileField) {
|
|
128
|
+
const value = profileField.value || profileField;
|
|
129
|
+
return typeof value === 'string' ? value : (Array.isArray(value) ? value[0] : null);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Fallback: iterate ALL fields and look for users field type
|
|
133
|
+
for (const [, fieldValue] of Object.entries(fields)) {
|
|
134
|
+
if (fieldValue && typeof fieldValue === 'object' && !Array.isArray(fieldValue)) {
|
|
135
|
+
const typedField = fieldValue;
|
|
136
|
+
if (typedField.type === 'users' && typedField.value) {
|
|
137
|
+
const value = typedField.value;
|
|
138
|
+
const userId = typeof value === 'string' ? value : (Array.isArray(value) ? value[0] : null);
|
|
139
|
+
if (userId && looksLikeUserId(userId)) {
|
|
140
|
+
return userId;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (Array.isArray(fieldValue) && fieldValue.length > 0) {
|
|
145
|
+
const firstVal = fieldValue[0];
|
|
146
|
+
if (typeof firstVal === 'string' && looksLikeUserId(firstVal)) {
|
|
147
|
+
return firstVal;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function extractBotTypeFromActivity(activity, schema) {
|
|
154
|
+
const fields = activity.fields || {};
|
|
155
|
+
// Try fields object format - by field KEY first
|
|
156
|
+
if (fields.botType) {
|
|
157
|
+
return typeof fields.botType === 'string' ? fields.botType : (fields.botType?.value || null);
|
|
158
|
+
}
|
|
159
|
+
// Try fields object format - by field ID
|
|
160
|
+
if (schema.botTypeFieldId) {
|
|
161
|
+
const botTypeField = fields[schema.botTypeFieldId];
|
|
162
|
+
if (botTypeField) {
|
|
163
|
+
return botTypeField.value || botTypeField;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
async function discoverAgentDirectory(apiClient, workspaceId) {
|
|
169
|
+
// Get list of workflows
|
|
170
|
+
const workflows = await apiClient.listWorkflows(workspaceId);
|
|
171
|
+
// Find Agent Directory workflow
|
|
172
|
+
const agentDir = workflows.find((w) => AGENT_DIRECTORY_PATTERNS.some(p => w.name?.includes(p)));
|
|
173
|
+
if (!agentDir) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const workflowId = agentDir._id;
|
|
177
|
+
// Extract phases
|
|
178
|
+
const phases = [];
|
|
179
|
+
if (agentDir.phases) {
|
|
180
|
+
for (const [phaseId, phase] of Object.entries(agentDir.phases)) {
|
|
181
|
+
phases.push({ id: phaseId, name: phase.name || phaseId });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const deployedPhaseId = findPhaseByPattern(phases, DEPLOYED_PHASE_PATTERNS);
|
|
185
|
+
const retiredPhaseId = findPhaseByPattern(phases, RETIRED_PHASE_PATTERNS);
|
|
186
|
+
// Extract field IDs by key
|
|
187
|
+
const fieldMap = {};
|
|
188
|
+
if (agentDir.fields) {
|
|
189
|
+
for (const [fieldId, field] of Object.entries(agentDir.fields)) {
|
|
190
|
+
const key = field.key;
|
|
191
|
+
if (key)
|
|
192
|
+
fieldMap[key] = fieldId;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
workflowId,
|
|
197
|
+
deployedPhaseId,
|
|
198
|
+
retiredPhaseId,
|
|
199
|
+
hailerProfileFieldId: findFieldId(fieldMap, FIELD_KEY_HAILER_PROFILE),
|
|
200
|
+
emailFieldId: findFieldId(fieldMap, FIELD_KEY_EMAIL),
|
|
201
|
+
passwordFieldId: findFieldId(fieldMap, FIELD_KEY_PASSWORD),
|
|
202
|
+
botTypeFieldId: findFieldId(fieldMap, FIELD_KEY_BOT_TYPE),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
async function fetchBotActivities(apiClient, workflowId, phaseId) {
|
|
206
|
+
const result = await apiClient.fetchActivityList(workflowId, phaseId, 100, { returnFlat: true });
|
|
207
|
+
return result.data || result.activities || result.list || [];
|
|
208
|
+
}
|
|
209
|
+
async function processWorkspace(apiClient, workspaceId, workspaceName) {
|
|
210
|
+
console.log(`\n Discovering Agent Directory...`);
|
|
211
|
+
const schema = await discoverAgentDirectory(apiClient, workspaceId);
|
|
212
|
+
if (!schema) {
|
|
213
|
+
console.log(` No Agent Directory found`);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
console.log(` Found workflow: ${schema.workflowId}`);
|
|
217
|
+
if (!schema.deployedPhaseId) {
|
|
218
|
+
console.log(` No deployed phase found`);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
// Fetch deployed activities
|
|
222
|
+
console.log(` Fetching deployed bots...`);
|
|
223
|
+
const deployedActivities = await fetchBotActivities(apiClient, schema.workflowId, schema.deployedPhaseId);
|
|
224
|
+
// Fetch retired activities if available
|
|
225
|
+
let retiredActivities = [];
|
|
226
|
+
if (schema.retiredPhaseId) {
|
|
227
|
+
retiredActivities = await fetchBotActivities(apiClient, schema.workflowId, schema.retiredPhaseId);
|
|
228
|
+
}
|
|
229
|
+
console.log(` Found ${deployedActivities.length} deployed, ${retiredActivities.length} retired bots`);
|
|
230
|
+
const config = {
|
|
231
|
+
workspaceId,
|
|
232
|
+
workspaceName,
|
|
233
|
+
specialists: [],
|
|
234
|
+
lastSynced: new Date().toISOString(),
|
|
235
|
+
};
|
|
236
|
+
// Process deployed activities
|
|
237
|
+
for (const activity of deployedActivities) {
|
|
238
|
+
const creds = extractCredentialsFromActivity(activity, schema);
|
|
239
|
+
const userId = extractUserIdFromActivity(activity, schema);
|
|
240
|
+
const botType = extractBotTypeFromActivity(activity, schema);
|
|
241
|
+
if (!creds || !userId) {
|
|
242
|
+
console.log(` Skipping ${activity.name} - missing credentials or userId`);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (botType === 'orchestrator') {
|
|
246
|
+
config.orchestrator = {
|
|
247
|
+
activityId: activity._id,
|
|
248
|
+
userId,
|
|
249
|
+
email: creds.email,
|
|
250
|
+
password: creds.password,
|
|
251
|
+
};
|
|
252
|
+
console.log(` Orchestrator: ${activity.name}`);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
config.specialists.push({
|
|
256
|
+
activityId: activity._id,
|
|
257
|
+
userId,
|
|
258
|
+
email: creds.email,
|
|
259
|
+
password: creds.password,
|
|
260
|
+
botType: botType || 'unknown',
|
|
261
|
+
enabled: true,
|
|
262
|
+
});
|
|
263
|
+
console.log(` Specialist: ${activity.name} (${botType || 'unknown'})`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Process retired activities (disabled specialists)
|
|
267
|
+
for (const activity of retiredActivities) {
|
|
268
|
+
const creds = extractCredentialsFromActivity(activity, schema);
|
|
269
|
+
const userId = extractUserIdFromActivity(activity, schema);
|
|
270
|
+
const botType = extractBotTypeFromActivity(activity, schema);
|
|
271
|
+
if (!creds || !userId)
|
|
272
|
+
continue;
|
|
273
|
+
if (botType === 'orchestrator')
|
|
274
|
+
continue; // Skip retired orchestrators
|
|
275
|
+
config.specialists.push({
|
|
276
|
+
activityId: activity._id,
|
|
277
|
+
userId,
|
|
278
|
+
email: creds.email,
|
|
279
|
+
password: creds.password,
|
|
280
|
+
botType: botType || 'unknown',
|
|
281
|
+
enabled: false,
|
|
282
|
+
});
|
|
283
|
+
console.log(` Retired: ${activity.name} (${botType || 'unknown'})`);
|
|
284
|
+
}
|
|
285
|
+
// Skip workspaces without orchestrator
|
|
286
|
+
if (!config.orchestrator) {
|
|
287
|
+
console.log(` No orchestrator found - skipping workspace`);
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
return config;
|
|
291
|
+
}
|
|
292
|
+
async function main() {
|
|
293
|
+
console.log('\n Bot Config Seeder');
|
|
294
|
+
console.log('==================\n');
|
|
295
|
+
// Check environment variables
|
|
296
|
+
const email = process.env.HAILER_EMAIL;
|
|
297
|
+
const password = process.env.HAILER_PASSWORD;
|
|
298
|
+
const host = process.env.HAILER_API_URL || 'https://app.hailer.com';
|
|
299
|
+
if (!email || !password) {
|
|
300
|
+
console.error(' Error: HAILER_EMAIL and HAILER_PASSWORD environment variables are required');
|
|
301
|
+
console.error('\nSet them in your .env file or export them:');
|
|
302
|
+
console.error(' export HAILER_EMAIL=your@email.com');
|
|
303
|
+
console.error(' export HAILER_PASSWORD=yourpassword');
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
console.log(` Connecting to ${host} as ${email}...`);
|
|
307
|
+
// Connect to Hailer
|
|
308
|
+
const clientManager = new hailer_clients_1.HailerClientManager(host, email, password);
|
|
309
|
+
let hailerClient;
|
|
310
|
+
try {
|
|
311
|
+
hailerClient = await clientManager.connect();
|
|
312
|
+
console.log(' Connected successfully\n');
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
console.error(' Failed to connect:', error instanceof Error ? error.message : error);
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
const apiClient = new hailer_api_client_1.HailerApiClient(hailerClient);
|
|
319
|
+
// Get all workspaces
|
|
320
|
+
console.log(' Fetching workspaces...');
|
|
321
|
+
const init = await apiClient.request('v2.core.init', [['networks']]);
|
|
322
|
+
const workspaces = init.networks || {};
|
|
323
|
+
const workspaceIds = Object.keys(workspaces);
|
|
324
|
+
console.log(` Found ${workspaceIds.length} workspace(s)\n`);
|
|
325
|
+
// Create config directory
|
|
326
|
+
const configDir = path.join(process.cwd(), BOT_CONFIG_DIR);
|
|
327
|
+
if (!fs.existsSync(configDir)) {
|
|
328
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
329
|
+
console.log(` Created ${BOT_CONFIG_DIR}/ directory\n`);
|
|
330
|
+
}
|
|
331
|
+
let successCount = 0;
|
|
332
|
+
let skipCount = 0;
|
|
333
|
+
// Process each workspace
|
|
334
|
+
for (const wsId of workspaceIds) {
|
|
335
|
+
const ws = workspaces[wsId];
|
|
336
|
+
const wsName = ws.name || wsId;
|
|
337
|
+
console.log(` [${wsName}]`);
|
|
338
|
+
try {
|
|
339
|
+
// Switch to workspace
|
|
340
|
+
await apiClient.request('v2.network.switch', [wsId]);
|
|
341
|
+
const config = await processWorkspace(apiClient, wsId, wsName);
|
|
342
|
+
if (config) {
|
|
343
|
+
// Write config file
|
|
344
|
+
const configPath = path.join(configDir, `${wsId}.json`);
|
|
345
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
346
|
+
console.log(` Saved to ${BOT_CONFIG_DIR}/${wsId}.json`);
|
|
347
|
+
successCount++;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
skipCount++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
console.error(` Error: ${error instanceof Error ? error.message : error}`);
|
|
355
|
+
skipCount++;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Disconnect
|
|
359
|
+
clientManager.disconnect();
|
|
360
|
+
// Summary
|
|
361
|
+
console.log('\n==================');
|
|
362
|
+
console.log(` Done!`);
|
|
363
|
+
console.log(` ${successCount} workspace(s) configured`);
|
|
364
|
+
console.log(` ${skipCount} workspace(s) skipped`);
|
|
365
|
+
console.log(`\nConfig files saved to: ${configDir}/\n`);
|
|
366
|
+
}
|
|
367
|
+
// Run
|
|
368
|
+
main().catch(error => {
|
|
369
|
+
console.error('\n Unexpected error:', error);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
});
|
|
372
|
+
//# sourceMappingURL=seed-config.js.map
|
package/dist/config.d.ts
CHANGED
|
@@ -54,6 +54,16 @@ export declare const environment: {
|
|
|
54
54
|
OPENAI_MODEL?: string | undefined;
|
|
55
55
|
ANTHROPIC_API_KEY?: string | undefined;
|
|
56
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Mask sensitive data for safe logging
|
|
59
|
+
* Shows first 4 and last 4 characters, hiding the rest
|
|
60
|
+
*/
|
|
61
|
+
export declare function maskSensitiveData(value: string | undefined): string;
|
|
62
|
+
/**
|
|
63
|
+
* Mask email for safe logging
|
|
64
|
+
* Shows first 2 chars of local part + domain: user@domain.com -> us***@domain.com
|
|
65
|
+
*/
|
|
66
|
+
export declare function maskEmail(email: string | undefined): string;
|
|
57
67
|
export interface HailerAccount {
|
|
58
68
|
email: string;
|
|
59
69
|
password: string;
|
package/dist/config.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.ApplicationConfig = exports.environment = void 0;
|
|
19
|
+
exports.maskSensitiveData = maskSensitiveData;
|
|
20
|
+
exports.maskEmail = maskEmail;
|
|
19
21
|
exports.createApplicationConfig = createApplicationConfig;
|
|
20
22
|
const zod_1 = require("zod");
|
|
21
23
|
const dotenv_1 = require("dotenv");
|
|
@@ -35,8 +37,11 @@ const hailerAccountSchema = zod_1.z.object({
|
|
|
35
37
|
/**
|
|
36
38
|
* Transform CLIENT_CONFIGS to efficient Map format
|
|
37
39
|
* Supports both legacy array and new object formats
|
|
40
|
+
* Optional with default empty object for hot-reload support
|
|
38
41
|
*/
|
|
39
42
|
const clientConfigsSchema = zod_1.z.string()
|
|
43
|
+
.optional()
|
|
44
|
+
.default('{}')
|
|
40
45
|
.transform((value, ctx) => {
|
|
41
46
|
try {
|
|
42
47
|
const parsed = JSON.parse(value);
|
|
@@ -129,7 +134,7 @@ const environmentSchema = zod_1.z.object({
|
|
|
129
134
|
CONTEXT_MAX_SUMMARIZATION_CHUNKS: zod_1.z.string().transform(v => parseInt(v) || 10).default('10'),
|
|
130
135
|
});
|
|
131
136
|
/**
|
|
132
|
-
* Get process environment
|
|
137
|
+
* Get process environment
|
|
133
138
|
*/
|
|
134
139
|
function getProcessEnv() {
|
|
135
140
|
return { ...process.env };
|
|
@@ -139,6 +144,61 @@ function getProcessEnv() {
|
|
|
139
144
|
*/
|
|
140
145
|
exports.environment = environmentSchema.parse(getProcessEnv());
|
|
141
146
|
// ================================================================================
|
|
147
|
+
// SECURITY UTILITIES
|
|
148
|
+
// ================================================================================
|
|
149
|
+
/**
|
|
150
|
+
* Mask sensitive data for safe logging
|
|
151
|
+
* Shows first 4 and last 4 characters, hiding the rest
|
|
152
|
+
*/
|
|
153
|
+
function maskSensitiveData(value) {
|
|
154
|
+
if (!value || value.length < 8)
|
|
155
|
+
return '***';
|
|
156
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Mask email for safe logging
|
|
160
|
+
* Shows first 2 chars of local part + domain: user@domain.com -> us***@domain.com
|
|
161
|
+
*/
|
|
162
|
+
function maskEmail(email) {
|
|
163
|
+
if (!email)
|
|
164
|
+
return '***';
|
|
165
|
+
const [local, domain] = email.split('@');
|
|
166
|
+
if (!domain)
|
|
167
|
+
return maskSensitiveData(email);
|
|
168
|
+
const maskedLocal = local.length > 2 ? `${local.slice(0, 2)}***` : '***';
|
|
169
|
+
return `${maskedLocal}@${domain}`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Validate CORS origins for security issues
|
|
173
|
+
* Returns warnings for potentially insecure configurations
|
|
174
|
+
*/
|
|
175
|
+
function validateCorsOrigins(origins) {
|
|
176
|
+
const warnings = [];
|
|
177
|
+
for (const origin of origins) {
|
|
178
|
+
// Check for wildcard in subdomain (e.g., *.example.com patterns in URLs)
|
|
179
|
+
if (origin.includes('*')) {
|
|
180
|
+
warnings.push(`CORS origin contains wildcard: ${origin} - consider using explicit origins`);
|
|
181
|
+
}
|
|
182
|
+
// Check for localhost/127.0.0.1 in production
|
|
183
|
+
if (process.env.NODE_ENV === 'production') {
|
|
184
|
+
if (origin.includes('localhost') || origin.includes('127.0.0.1')) {
|
|
185
|
+
warnings.push(`CORS origin allows localhost in production: ${origin}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Check for HTTP (non-HTTPS) origins in production
|
|
189
|
+
if (process.env.NODE_ENV === 'production' && origin.startsWith('http://')) {
|
|
190
|
+
warnings.push(`CORS origin uses HTTP instead of HTTPS in production: ${origin}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return warnings;
|
|
194
|
+
}
|
|
195
|
+
// Validate CORS origins and log warnings
|
|
196
|
+
const corsWarnings = validateCorsOrigins(exports.environment.CORS_ORIGINS);
|
|
197
|
+
if (corsWarnings.length > 0) {
|
|
198
|
+
console.warn('[Security] CORS configuration warnings:');
|
|
199
|
+
corsWarnings.forEach(warning => console.warn(` - ${warning}`));
|
|
200
|
+
}
|
|
201
|
+
// ================================================================================
|
|
142
202
|
// UNIVERSAL APPLICATION CONFIG
|
|
143
203
|
// ================================================================================
|
|
144
204
|
/**
|
package/dist/core.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare class Core {
|
|
|
12
12
|
private daemonManager;
|
|
13
13
|
private statusLogInterval?;
|
|
14
14
|
private bugReportsModule?;
|
|
15
|
+
private daemonInitInProgress;
|
|
16
|
+
private initialDaemonReady;
|
|
15
17
|
constructor();
|
|
16
18
|
/**
|
|
17
19
|
* Public API for external tool registration
|
|
@@ -23,6 +25,12 @@ export declare class Core {
|
|
|
23
25
|
private startMCPClient;
|
|
24
26
|
private startBugMonitor;
|
|
25
27
|
private initBotConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Attempt to restart daemon mode when bot state changes
|
|
30
|
+
* Stops existing daemons and starts fresh with updated configuration
|
|
31
|
+
* Includes retry logic for transient connection failures
|
|
32
|
+
*/
|
|
33
|
+
private attemptDaemonRestart;
|
|
26
34
|
private initializeDaemonMode;
|
|
27
35
|
/**
|
|
28
36
|
* Get daemon status (for HTTP endpoint)
|