@hailer/mcp 0.1.15 → 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.
Files changed (112) hide show
  1. package/.claude/agents/agent-giuseppe-app-builder.md +7 -6
  2. package/.claude/agents/agent-lars-code-inspector.md +26 -14
  3. package/dist/agents/bot-manager.d.ts +48 -0
  4. package/dist/agents/bot-manager.js +254 -0
  5. package/dist/agents/factory.d.ts +150 -0
  6. package/dist/agents/factory.js +650 -0
  7. package/dist/agents/giuseppe/ai.d.ts +83 -0
  8. package/dist/agents/giuseppe/ai.js +466 -0
  9. package/dist/agents/giuseppe/bot.d.ts +110 -0
  10. package/dist/agents/giuseppe/bot.js +780 -0
  11. package/dist/agents/giuseppe/config.d.ts +25 -0
  12. package/dist/agents/giuseppe/config.js +227 -0
  13. package/dist/agents/giuseppe/files.d.ts +52 -0
  14. package/dist/agents/giuseppe/files.js +338 -0
  15. package/dist/agents/giuseppe/git.d.ts +48 -0
  16. package/dist/agents/giuseppe/git.js +298 -0
  17. package/dist/agents/giuseppe/index.d.ts +97 -0
  18. package/dist/agents/giuseppe/index.js +258 -0
  19. package/dist/agents/giuseppe/lsp.d.ts +113 -0
  20. package/dist/agents/giuseppe/lsp.js +485 -0
  21. package/dist/agents/giuseppe/monitor.d.ts +118 -0
  22. package/dist/agents/giuseppe/monitor.js +621 -0
  23. package/dist/agents/giuseppe/prompt.d.ts +5 -0
  24. package/dist/agents/giuseppe/prompt.js +94 -0
  25. package/dist/agents/giuseppe/registries/pending-classification.d.ts +28 -0
  26. package/dist/agents/giuseppe/registries/pending-classification.js +50 -0
  27. package/dist/agents/giuseppe/registries/pending-fix.d.ts +30 -0
  28. package/dist/agents/giuseppe/registries/pending-fix.js +42 -0
  29. package/dist/agents/giuseppe/registries/pending.d.ts +27 -0
  30. package/dist/agents/giuseppe/registries/pending.js +49 -0
  31. package/dist/agents/giuseppe/specialist.d.ts +47 -0
  32. package/dist/agents/giuseppe/specialist.js +237 -0
  33. package/dist/agents/giuseppe/types.d.ts +123 -0
  34. package/dist/agents/giuseppe/types.js +9 -0
  35. package/dist/agents/hailer-expert/index.d.ts +8 -0
  36. package/dist/agents/hailer-expert/index.js +14 -0
  37. package/dist/agents/hal/daemon.d.ts +142 -0
  38. package/dist/agents/hal/daemon.js +1103 -0
  39. package/dist/agents/hal/definitions.d.ts +55 -0
  40. package/dist/agents/hal/definitions.js +263 -0
  41. package/dist/agents/hal/index.d.ts +3 -0
  42. package/dist/agents/hal/index.js +8 -0
  43. package/dist/agents/index.d.ts +18 -0
  44. package/dist/agents/index.js +48 -0
  45. package/dist/agents/shared/base.d.ts +216 -0
  46. package/dist/agents/shared/base.js +846 -0
  47. package/dist/agents/shared/services/agent-registry.d.ts +107 -0
  48. package/dist/agents/shared/services/agent-registry.js +629 -0
  49. package/dist/agents/shared/services/conversation-manager.d.ts +50 -0
  50. package/dist/agents/shared/services/conversation-manager.js +136 -0
  51. package/dist/agents/shared/services/mcp-client.d.ts +56 -0
  52. package/dist/agents/shared/services/mcp-client.js +124 -0
  53. package/dist/agents/shared/services/message-classifier.d.ts +37 -0
  54. package/dist/agents/shared/services/message-classifier.js +187 -0
  55. package/dist/agents/shared/services/message-formatter.d.ts +89 -0
  56. package/dist/agents/shared/services/message-formatter.js +371 -0
  57. package/dist/agents/shared/services/session-logger.d.ts +106 -0
  58. package/dist/agents/shared/services/session-logger.js +446 -0
  59. package/dist/agents/shared/services/tool-executor.d.ts +41 -0
  60. package/dist/agents/shared/services/tool-executor.js +169 -0
  61. package/dist/agents/shared/services/workspace-schema-cache.d.ts +125 -0
  62. package/dist/agents/shared/services/workspace-schema-cache.js +578 -0
  63. package/dist/agents/shared/specialist.d.ts +91 -0
  64. package/dist/agents/shared/specialist.js +399 -0
  65. package/dist/agents/shared/tool-schema-loader.d.ts +62 -0
  66. package/dist/agents/shared/tool-schema-loader.js +232 -0
  67. package/dist/agents/shared/types.d.ts +327 -0
  68. package/dist/agents/shared/types.js +121 -0
  69. package/dist/app.js +21 -4
  70. package/dist/cli.js +0 -0
  71. package/dist/client/agents/orchestrator.d.ts +1 -0
  72. package/dist/client/agents/orchestrator.js +12 -1
  73. package/dist/commands/seed-config.d.ts +9 -0
  74. package/dist/commands/seed-config.js +372 -0
  75. package/dist/config.d.ts +10 -0
  76. package/dist/config.js +61 -1
  77. package/dist/core.d.ts +8 -0
  78. package/dist/core.js +137 -6
  79. package/dist/lib/discussion-lock.d.ts +42 -0
  80. package/dist/lib/discussion-lock.js +110 -0
  81. package/dist/mcp/UserContextCache.js +2 -2
  82. package/dist/mcp/hailer-clients.d.ts +15 -0
  83. package/dist/mcp/hailer-clients.js +100 -6
  84. package/dist/mcp/signal-handler.d.ts +16 -5
  85. package/dist/mcp/signal-handler.js +173 -122
  86. package/dist/mcp/tools/activity.js +9 -1
  87. package/dist/mcp/tools/bot-config.d.ts +184 -9
  88. package/dist/mcp/tools/bot-config.js +2177 -163
  89. package/dist/mcp/tools/giuseppe-tools.d.ts +21 -0
  90. package/dist/mcp/tools/giuseppe-tools.js +525 -0
  91. package/dist/mcp/utils/hailer-api-client.d.ts +42 -1
  92. package/dist/mcp/utils/hailer-api-client.js +128 -2
  93. package/dist/mcp/webhook-handler.d.ts +87 -0
  94. package/dist/mcp/webhook-handler.js +343 -0
  95. package/dist/mcp/workspace-cache.d.ts +5 -0
  96. package/dist/mcp/workspace-cache.js +11 -0
  97. package/dist/mcp-server.js +55 -5
  98. package/dist/modules/bug-reports/giuseppe-agent.d.ts +58 -0
  99. package/dist/modules/bug-reports/giuseppe-agent.js +467 -0
  100. package/dist/modules/bug-reports/giuseppe-ai.d.ts +25 -1
  101. package/dist/modules/bug-reports/giuseppe-ai.js +133 -2
  102. package/dist/modules/bug-reports/giuseppe-bot.d.ts +2 -2
  103. package/dist/modules/bug-reports/giuseppe-bot.js +66 -42
  104. package/dist/modules/bug-reports/giuseppe-daemon.d.ts +80 -0
  105. package/dist/modules/bug-reports/giuseppe-daemon.js +617 -0
  106. package/dist/modules/bug-reports/giuseppe-files.d.ts +12 -0
  107. package/dist/modules/bug-reports/giuseppe-files.js +37 -0
  108. package/dist/modules/bug-reports/giuseppe-lsp.d.ts +84 -13
  109. package/dist/modules/bug-reports/giuseppe-lsp.js +403 -61
  110. package/dist/modules/bug-reports/index.d.ts +1 -0
  111. package/dist/modules/bug-reports/index.js +31 -29
  112. package/package.json +3 -2
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
- console.log('Registering tools...');
15
- console.log(`Nuclear tools ${config_1.environment.ENABLE_NUCLEAR_TOOLS ? 'ENABLED' : 'DISABLED'} (set ENABLE_NUCLEAR_TOOLS=true in .env.local to enable)`);
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
- console.log('All tools registered successfully!');
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
- console.error('Failed to start Hailer MCP application:', error);
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?.linkedActivityId) {
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 (no defaults - CLIENT_CONFIGS is required)
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)