@hailer/mcp 0.0.1
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/commands/tool-builder.md +37 -0
- package/.claude/commands/ws-pull.md +44 -0
- package/.claude/settings.json +8 -0
- package/.claude/settings.local.json +49 -0
- package/.claude/skills/activity-api/SKILL.md +96 -0
- package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
- package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
- package/.claude/skills/agent-building/SKILL.md +243 -0
- package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
- package/.claude/skills/agent-building/references/code-examples.md +587 -0
- package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
- package/.claude/skills/app-api/SKILL.md +219 -0
- package/.claude/skills/app-api/references/app-endpoints.md +759 -0
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
- package/.claude/skills/create-app-skill/SKILL.md +1101 -0
- package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
- package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
- package/.claude/skills/hailer-api/SKILL.md +283 -0
- package/.claude/skills/hailer-api/references/activities.md +620 -0
- package/.claude/skills/hailer-api/references/authentication.md +216 -0
- package/.claude/skills/hailer-api/references/datasets.md +437 -0
- package/.claude/skills/hailer-api/references/files.md +301 -0
- package/.claude/skills/hailer-api/references/insights.md +469 -0
- package/.claude/skills/hailer-api/references/workflows.md +720 -0
- package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
- package/.claude/skills/insight-api/SKILL.md +185 -0
- package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
- package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
- package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
- package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
- package/.claude/skills/local-first-skill/SKILL.md +570 -0
- package/.claude/skills/mcp-tools/SKILL.md +419 -0
- package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
- package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
- package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
- package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
- package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
- package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
- package/.claude/skills/remove-app-skill/SKILL.md +985 -0
- package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
- package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
- package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
- package/.claude/skills/skill-testing/README.md +137 -0
- package/.claude/skills/skill-testing/SKILL.md +348 -0
- package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
- package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
- package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
- package/.claude/skills/tool-builder/SKILL.md +328 -0
- package/.claude/skills/update-app-skill/SKILL.md +970 -0
- package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
- package/.env.example +81 -0
- package/.mcp.json +13 -0
- package/README.md +297 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +5 -0
- package/dist/client/adaptive-documentation-bot.d.ts +108 -0
- package/dist/client/adaptive-documentation-bot.js +475 -0
- package/dist/client/adaptive-documentation-types.d.ts +66 -0
- package/dist/client/adaptive-documentation-types.js +9 -0
- package/dist/client/agent-activity-bot.d.ts +51 -0
- package/dist/client/agent-activity-bot.js +166 -0
- package/dist/client/agent-tracker.d.ts +499 -0
- package/dist/client/agent-tracker.js +659 -0
- package/dist/client/description-updater.d.ts +56 -0
- package/dist/client/description-updater.js +259 -0
- package/dist/client/log-parser.d.ts +72 -0
- package/dist/client/log-parser.js +387 -0
- package/dist/client/mcp-client.d.ts +50 -0
- package/dist/client/mcp-client.js +532 -0
- package/dist/client/message-processor.d.ts +35 -0
- package/dist/client/message-processor.js +352 -0
- package/dist/client/multi-bot-manager.d.ts +24 -0
- package/dist/client/multi-bot-manager.js +74 -0
- package/dist/client/providers/anthropic-provider.d.ts +19 -0
- package/dist/client/providers/anthropic-provider.js +631 -0
- package/dist/client/providers/llm-provider.d.ts +47 -0
- package/dist/client/providers/llm-provider.js +367 -0
- package/dist/client/providers/openai-provider.d.ts +23 -0
- package/dist/client/providers/openai-provider.js +621 -0
- package/dist/client/simple-llm-caller.d.ts +19 -0
- package/dist/client/simple-llm-caller.js +100 -0
- package/dist/client/skill-generator.d.ts +81 -0
- package/dist/client/skill-generator.js +386 -0
- package/dist/client/test-adaptive-bot.d.ts +9 -0
- package/dist/client/test-adaptive-bot.js +82 -0
- package/dist/client/token-pricing.d.ts +38 -0
- package/dist/client/token-pricing.js +127 -0
- package/dist/client/token-tracker.d.ts +232 -0
- package/dist/client/token-tracker.js +457 -0
- package/dist/client/token-usage-bot.d.ts +53 -0
- package/dist/client/token-usage-bot.js +153 -0
- package/dist/client/tool-executor.d.ts +69 -0
- package/dist/client/tool-executor.js +159 -0
- package/dist/client/tool-schema-loader.d.ts +60 -0
- package/dist/client/tool-schema-loader.js +178 -0
- package/dist/client/types.d.ts +69 -0
- package/dist/client/types.js +7 -0
- package/dist/config.d.ts +162 -0
- package/dist/config.js +296 -0
- package/dist/core.d.ts +26 -0
- package/dist/core.js +147 -0
- package/dist/lib/context-manager.d.ts +111 -0
- package/dist/lib/context-manager.js +431 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +277 -0
- package/dist/lib/materialize.d.ts +3 -0
- package/dist/lib/materialize.js +101 -0
- package/dist/lib/normalizedName.d.ts +7 -0
- package/dist/lib/normalizedName.js +48 -0
- package/dist/lib/prompt-length-manager.d.ts +81 -0
- package/dist/lib/prompt-length-manager.js +457 -0
- package/dist/lib/terminal-prompt.d.ts +9 -0
- package/dist/lib/terminal-prompt.js +108 -0
- package/dist/mcp/UserContextCache.d.ts +56 -0
- package/dist/mcp/UserContextCache.js +163 -0
- package/dist/mcp/auth.d.ts +2 -0
- package/dist/mcp/auth.js +29 -0
- package/dist/mcp/hailer-clients.d.ts +42 -0
- package/dist/mcp/hailer-clients.js +246 -0
- package/dist/mcp/signal-handler.d.ts +45 -0
- package/dist/mcp/signal-handler.js +317 -0
- package/dist/mcp/tool-registry.d.ts +100 -0
- package/dist/mcp/tool-registry.js +306 -0
- package/dist/mcp/tools/activity.d.ts +15 -0
- package/dist/mcp/tools/activity.js +955 -0
- package/dist/mcp/tools/app.d.ts +20 -0
- package/dist/mcp/tools/app.js +1488 -0
- package/dist/mcp/tools/discussion.d.ts +19 -0
- package/dist/mcp/tools/discussion.js +950 -0
- package/dist/mcp/tools/file.d.ts +15 -0
- package/dist/mcp/tools/file.js +119 -0
- package/dist/mcp/tools/insight.d.ts +17 -0
- package/dist/mcp/tools/insight.js +806 -0
- package/dist/mcp/tools/skill.d.ts +10 -0
- package/dist/mcp/tools/skill.js +279 -0
- package/dist/mcp/tools/user.d.ts +10 -0
- package/dist/mcp/tools/user.js +108 -0
- package/dist/mcp/tools/workflow-template.d.ts +19 -0
- package/dist/mcp/tools/workflow-template.js +822 -0
- package/dist/mcp/tools/workflow.d.ts +18 -0
- package/dist/mcp/tools/workflow.js +1362 -0
- package/dist/mcp/utils/api-errors.d.ts +45 -0
- package/dist/mcp/utils/api-errors.js +160 -0
- package/dist/mcp/utils/data-transformers.d.ts +102 -0
- package/dist/mcp/utils/data-transformers.js +194 -0
- package/dist/mcp/utils/file-upload.d.ts +33 -0
- package/dist/mcp/utils/file-upload.js +148 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
- package/dist/mcp/utils/hailer-api-client.js +323 -0
- package/dist/mcp/utils/index.d.ts +13 -0
- package/dist/mcp/utils/index.js +39 -0
- package/dist/mcp/utils/logger.d.ts +42 -0
- package/dist/mcp/utils/logger.js +103 -0
- package/dist/mcp/utils/types.d.ts +286 -0
- package/dist/mcp/utils/types.js +7 -0
- package/dist/mcp/workspace-cache.d.ts +42 -0
- package/dist/mcp/workspace-cache.js +97 -0
- package/dist/mcp-server.d.ts +42 -0
- package/dist/mcp-server.js +280 -0
- package/package.json +56 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API error handling utilities for Hailer MCP Server
|
|
3
|
+
* Provides consistent error handling and response processing
|
|
4
|
+
*/
|
|
5
|
+
import { LogContext } from '../../lib/logger';
|
|
6
|
+
export declare class HailerApiError extends Error {
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly statusText: string;
|
|
9
|
+
readonly operation: string;
|
|
10
|
+
readonly endpoint: string;
|
|
11
|
+
readonly responseBody?: string | undefined;
|
|
12
|
+
constructor(status: number, statusText: string, operation: string, endpoint: string, responseBody?: string | undefined);
|
|
13
|
+
get isAuthError(): boolean;
|
|
14
|
+
get isNotFound(): boolean;
|
|
15
|
+
get isServerError(): boolean;
|
|
16
|
+
get isClientError(): boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface ApiCallOptions {
|
|
19
|
+
operation: string;
|
|
20
|
+
endpoint: string;
|
|
21
|
+
method?: string;
|
|
22
|
+
body?: any;
|
|
23
|
+
timeout?: number;
|
|
24
|
+
context?: LogContext;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Handles API response with consistent error handling and logging
|
|
28
|
+
*/
|
|
29
|
+
export declare function handleApiResponse<T>(response: Response, options: ApiCallOptions, startTime?: number): Promise<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Makes an API call with consistent error handling and logging
|
|
32
|
+
*/
|
|
33
|
+
export declare function makeApiCall<T>(url: string, options: ApiCallOptions & {
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}): Promise<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Helper function to create MCP error responses
|
|
39
|
+
*/
|
|
40
|
+
export declare function createErrorResponse(error: unknown, operation: string): any;
|
|
41
|
+
/**
|
|
42
|
+
* Helper function to create success responses
|
|
43
|
+
*/
|
|
44
|
+
export declare function createSuccessResponse(data: any, operation: string, summary?: string): any;
|
|
45
|
+
//# sourceMappingURL=api-errors.d.ts.map
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* API error handling utilities for Hailer MCP Server
|
|
4
|
+
* Provides consistent error handling and response processing
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.HailerApiError = void 0;
|
|
8
|
+
exports.handleApiResponse = handleApiResponse;
|
|
9
|
+
exports.makeApiCall = makeApiCall;
|
|
10
|
+
exports.createErrorResponse = createErrorResponse;
|
|
11
|
+
exports.createSuccessResponse = createSuccessResponse;
|
|
12
|
+
const logger_1 = require("../../lib/logger");
|
|
13
|
+
const logger = (0, logger_1.getDefaultLogger)();
|
|
14
|
+
class HailerApiError extends Error {
|
|
15
|
+
status;
|
|
16
|
+
statusText;
|
|
17
|
+
operation;
|
|
18
|
+
endpoint;
|
|
19
|
+
responseBody;
|
|
20
|
+
constructor(status, statusText, operation, endpoint, responseBody) {
|
|
21
|
+
super(`${operation} failed: ${status} ${statusText}${responseBody ? ` - ${responseBody}` : ''}`);
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.statusText = statusText;
|
|
24
|
+
this.operation = operation;
|
|
25
|
+
this.endpoint = endpoint;
|
|
26
|
+
this.responseBody = responseBody;
|
|
27
|
+
this.name = 'HailerApiError';
|
|
28
|
+
}
|
|
29
|
+
get isAuthError() {
|
|
30
|
+
return this.status === 401 || this.status === 403;
|
|
31
|
+
}
|
|
32
|
+
get isNotFound() {
|
|
33
|
+
return this.status === 404;
|
|
34
|
+
}
|
|
35
|
+
get isServerError() {
|
|
36
|
+
return this.status >= 500;
|
|
37
|
+
}
|
|
38
|
+
get isClientError() {
|
|
39
|
+
return this.status >= 400 && this.status < 500;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.HailerApiError = HailerApiError;
|
|
43
|
+
/**
|
|
44
|
+
* Handles API response with consistent error handling and logging
|
|
45
|
+
*/
|
|
46
|
+
async function handleApiResponse(response, options, startTime) {
|
|
47
|
+
const duration = startTime ? Date.now() - startTime : undefined;
|
|
48
|
+
const logContext = { ...options.context, duration };
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
let errorBody;
|
|
51
|
+
try {
|
|
52
|
+
errorBody = await response.text();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
errorBody = 'Unable to read error response';
|
|
56
|
+
}
|
|
57
|
+
logger.apiError(options.operation, options.endpoint, new HailerApiError(response.status, response.statusText, options.operation, options.endpoint, errorBody), duration);
|
|
58
|
+
throw new HailerApiError(response.status, response.statusText, options.operation, options.endpoint, errorBody);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
logger.apiSuccess(options.operation, options.endpoint, duration);
|
|
63
|
+
logger.debug(`API response data`, {
|
|
64
|
+
...logContext,
|
|
65
|
+
responseSize: JSON.stringify(data).length
|
|
66
|
+
});
|
|
67
|
+
return data;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logger.apiError(options.operation, options.endpoint, error, duration);
|
|
71
|
+
throw new Error(`Failed to parse JSON response for ${options.operation}: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Makes an API call with consistent error handling and logging
|
|
76
|
+
*/
|
|
77
|
+
async function makeApiCall(url, options) {
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
logger.apiCall(options.operation, options.endpoint, {
|
|
80
|
+
method: options.method || 'GET',
|
|
81
|
+
bodySize: options.body ? JSON.stringify(options.body).length : undefined,
|
|
82
|
+
...options.context
|
|
83
|
+
});
|
|
84
|
+
try {
|
|
85
|
+
const fetchOptions = {
|
|
86
|
+
method: options.method || 'GET',
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
...options.headers,
|
|
90
|
+
},
|
|
91
|
+
signal: options.signal,
|
|
92
|
+
};
|
|
93
|
+
if (options.body) {
|
|
94
|
+
fetchOptions.body = JSON.stringify(options.body);
|
|
95
|
+
}
|
|
96
|
+
// Add timeout if specified
|
|
97
|
+
const controller = new AbortController();
|
|
98
|
+
let timeoutId;
|
|
99
|
+
if (options.timeout) {
|
|
100
|
+
timeoutId = setTimeout(() => controller.abort(), options.timeout);
|
|
101
|
+
fetchOptions.signal = controller.signal;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetch(url, fetchOptions);
|
|
105
|
+
return await handleApiResponse(response, options, startTime);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
if (timeoutId) {
|
|
109
|
+
clearTimeout(timeoutId);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const duration = Date.now() - startTime;
|
|
115
|
+
if (error instanceof HailerApiError) {
|
|
116
|
+
throw error; // Re-throw our custom errors
|
|
117
|
+
}
|
|
118
|
+
logger.apiError(options.operation, options.endpoint, error, duration);
|
|
119
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
120
|
+
throw new Error(`${options.operation} timed out after ${options.timeout}ms`);
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`${options.operation} failed: ${error}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Helper function to create MCP error responses
|
|
127
|
+
*/
|
|
128
|
+
function createErrorResponse(error, operation) {
|
|
129
|
+
let message;
|
|
130
|
+
let details = '';
|
|
131
|
+
if (error instanceof HailerApiError) {
|
|
132
|
+
message = `${operation} failed: ${error.status} ${error.statusText}`;
|
|
133
|
+
details = error.responseBody || '';
|
|
134
|
+
}
|
|
135
|
+
else if (error instanceof Error) {
|
|
136
|
+
message = `${operation} failed: ${error.message}`;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
message = `${operation} failed: ${String(error)}`;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
content: [{
|
|
143
|
+
type: "text",
|
|
144
|
+
text: `❌ Error: ${message}${details ? `\n\nDetails: ${details}` : ''}\n\n💡 Tips:\n- Check your permissions and credentials\n- Verify the provided IDs are correct\n- Ensure the resource exists and is accessible`,
|
|
145
|
+
}],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Helper function to create success responses
|
|
150
|
+
*/
|
|
151
|
+
function createSuccessResponse(data, operation, summary) {
|
|
152
|
+
const baseMessage = summary || `Successfully completed ${operation}`;
|
|
153
|
+
return {
|
|
154
|
+
content: [{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: `✅ ${baseMessage}\n\n${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}`,
|
|
157
|
+
}],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=api-errors.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data transformation utilities for Hailer MCP Server
|
|
3
|
+
* Provides consistent data cleaning and formatting across tools
|
|
4
|
+
*/
|
|
5
|
+
export interface CleanActivity {
|
|
6
|
+
_id: string;
|
|
7
|
+
process: string;
|
|
8
|
+
currentPhase: string;
|
|
9
|
+
createdHumanReadable: string;
|
|
10
|
+
updatedHumanReadable: string;
|
|
11
|
+
uid: string;
|
|
12
|
+
userName: string;
|
|
13
|
+
name: string;
|
|
14
|
+
priority: number;
|
|
15
|
+
discussion?: string;
|
|
16
|
+
followers: string[];
|
|
17
|
+
fields?: any;
|
|
18
|
+
fieldsAndValues: FieldValue[];
|
|
19
|
+
}
|
|
20
|
+
export interface FieldValue {
|
|
21
|
+
fieldName: string;
|
|
22
|
+
value: any;
|
|
23
|
+
}
|
|
24
|
+
export interface WorkflowInfo {
|
|
25
|
+
_id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
phases: Record<string, PhaseInfo>;
|
|
28
|
+
fields: Record<string, FieldInfo>;
|
|
29
|
+
}
|
|
30
|
+
export interface PhaseInfo {
|
|
31
|
+
_id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
isEndpoint?: boolean;
|
|
34
|
+
possibleNextPhase?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface FieldInfo {
|
|
37
|
+
_id?: string;
|
|
38
|
+
label: string;
|
|
39
|
+
type: string;
|
|
40
|
+
key?: string;
|
|
41
|
+
required?: boolean;
|
|
42
|
+
data?: any[];
|
|
43
|
+
}
|
|
44
|
+
export interface UserInfo {
|
|
45
|
+
_id: string;
|
|
46
|
+
firstname: string;
|
|
47
|
+
lastname: string;
|
|
48
|
+
email?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Transforms raw activity data into clean, structured format
|
|
52
|
+
*/
|
|
53
|
+
export declare function transformActivity(activity: any, workflow: WorkflowInfo, users: Record<string, UserInfo>): CleanActivity;
|
|
54
|
+
/**
|
|
55
|
+
* Transforms multiple activities
|
|
56
|
+
*/
|
|
57
|
+
export declare function transformActivities(activities: any[], workflow: WorkflowInfo, users: Record<string, UserInfo>): CleanActivity[];
|
|
58
|
+
/**
|
|
59
|
+
* Transforms field data with proper type handling
|
|
60
|
+
*/
|
|
61
|
+
export declare function transformFields(activityFields: Record<string, any> | undefined, workflowFields: Record<string, FieldInfo>): FieldValue[];
|
|
62
|
+
/**
|
|
63
|
+
* Transforms a single field value based on field type
|
|
64
|
+
*/
|
|
65
|
+
export declare function transformFieldValue(activityFieldValue: any, field?: FieldInfo): any;
|
|
66
|
+
/**
|
|
67
|
+
* Finds a field by key or ID in workflow fields
|
|
68
|
+
*/
|
|
69
|
+
export declare function findFieldByKey(key: string, workflowFields: Record<string, FieldInfo>): FieldInfo | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Transforms raw workflow data into structured format
|
|
72
|
+
*/
|
|
73
|
+
export declare function transformWorkflow(process: any): WorkflowInfo;
|
|
74
|
+
/**
|
|
75
|
+
* Transforms workflow phases
|
|
76
|
+
*/
|
|
77
|
+
export declare function transformPhases(phases: Record<string, any>): Record<string, PhaseInfo>;
|
|
78
|
+
/**
|
|
79
|
+
* Transforms workflow fields
|
|
80
|
+
*/
|
|
81
|
+
export declare function transformWorkflowFields(fields: Record<string, any>): Record<string, FieldInfo>;
|
|
82
|
+
/**
|
|
83
|
+
* Formats timestamp to human readable string
|
|
84
|
+
*/
|
|
85
|
+
export declare function formatTimestamp(timestamp: number | string | Date): string;
|
|
86
|
+
/**
|
|
87
|
+
* Formats user name from user info
|
|
88
|
+
*/
|
|
89
|
+
export declare function formatUserName(user: UserInfo | undefined, fallbackId: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* Gets the first phase ID from workflow (for defaults)
|
|
92
|
+
*/
|
|
93
|
+
export declare function getFirstPhaseId(workflow: WorkflowInfo): string | null;
|
|
94
|
+
/**
|
|
95
|
+
* Formats activity data for MCP response
|
|
96
|
+
*/
|
|
97
|
+
export declare function formatActivityListResponse(activities: CleanActivity[], workflow: WorkflowInfo, phaseName?: string): string;
|
|
98
|
+
/**
|
|
99
|
+
* Creates a simplified activity format for search results
|
|
100
|
+
*/
|
|
101
|
+
export declare function transformActivityForSearch(activity: any): any;
|
|
102
|
+
//# sourceMappingURL=data-transformers.d.ts.map
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Data transformation utilities for Hailer MCP Server
|
|
4
|
+
* Provides consistent data cleaning and formatting across tools
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.transformActivity = transformActivity;
|
|
8
|
+
exports.transformActivities = transformActivities;
|
|
9
|
+
exports.transformFields = transformFields;
|
|
10
|
+
exports.transformFieldValue = transformFieldValue;
|
|
11
|
+
exports.findFieldByKey = findFieldByKey;
|
|
12
|
+
exports.transformWorkflow = transformWorkflow;
|
|
13
|
+
exports.transformPhases = transformPhases;
|
|
14
|
+
exports.transformWorkflowFields = transformWorkflowFields;
|
|
15
|
+
exports.formatTimestamp = formatTimestamp;
|
|
16
|
+
exports.formatUserName = formatUserName;
|
|
17
|
+
exports.getFirstPhaseId = getFirstPhaseId;
|
|
18
|
+
exports.formatActivityListResponse = formatActivityListResponse;
|
|
19
|
+
exports.transformActivityForSearch = transformActivityForSearch;
|
|
20
|
+
/**
|
|
21
|
+
* Transforms raw activity data into clean, structured format
|
|
22
|
+
*/
|
|
23
|
+
function transformActivity(activity, workflow, users) {
|
|
24
|
+
const user = users[activity.uid];
|
|
25
|
+
return {
|
|
26
|
+
_id: activity._id || activity.id,
|
|
27
|
+
process: activity.process,
|
|
28
|
+
currentPhase: activity.currentPhase,
|
|
29
|
+
createdHumanReadable: formatTimestamp(activity.created),
|
|
30
|
+
updatedHumanReadable: formatTimestamp(activity.updated),
|
|
31
|
+
uid: activity.uid,
|
|
32
|
+
userName: formatUserName(user, activity.uid),
|
|
33
|
+
name: activity.name,
|
|
34
|
+
priority: activity.priority || 0,
|
|
35
|
+
discussion: activity.discussion,
|
|
36
|
+
followers: Array.isArray(activity.followers) ? activity.followers : [],
|
|
37
|
+
fields: activity.field,
|
|
38
|
+
fieldsAndValues: transformFields(activity.fields, workflow.fields),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Transforms multiple activities
|
|
43
|
+
*/
|
|
44
|
+
function transformActivities(activities, workflow, users) {
|
|
45
|
+
return activities.map(activity => transformActivity(activity, workflow, users));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Transforms field data with proper type handling
|
|
49
|
+
*/
|
|
50
|
+
function transformFields(activityFields, workflowFields) {
|
|
51
|
+
if (!activityFields)
|
|
52
|
+
return [];
|
|
53
|
+
return Object.entries(activityFields).map(([fieldKey, activityFieldValue]) => {
|
|
54
|
+
const field = findFieldByKey(fieldKey, workflowFields);
|
|
55
|
+
return {
|
|
56
|
+
fieldName: field?.label || fieldKey,
|
|
57
|
+
value: transformFieldValue(activityFieldValue, field),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Transforms a single field value based on field type
|
|
63
|
+
*/
|
|
64
|
+
function transformFieldValue(activityFieldValue, field) {
|
|
65
|
+
// Handle nested value objects
|
|
66
|
+
if (typeof activityFieldValue === 'object' && activityFieldValue?.value !== undefined) {
|
|
67
|
+
if (field?.type === 'activitylink' && activityFieldValue.value._id) {
|
|
68
|
+
return {
|
|
69
|
+
_id: activityFieldValue.value._id,
|
|
70
|
+
name: activityFieldValue.value.name || 'n/a'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return activityFieldValue.value;
|
|
74
|
+
}
|
|
75
|
+
// Handle direct values
|
|
76
|
+
if (field?.type === 'activitylink' && typeof activityFieldValue === 'object' && activityFieldValue._id) {
|
|
77
|
+
return {
|
|
78
|
+
_id: activityFieldValue._id,
|
|
79
|
+
name: activityFieldValue.name || 'n/a'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return activityFieldValue;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Finds a field by key or ID in workflow fields
|
|
86
|
+
*/
|
|
87
|
+
function findFieldByKey(key, workflowFields) {
|
|
88
|
+
// First try to find by key
|
|
89
|
+
const fieldByKey = Object.values(workflowFields).find(f => f.key === key);
|
|
90
|
+
if (fieldByKey)
|
|
91
|
+
return fieldByKey;
|
|
92
|
+
// Fallback to direct field ID lookup
|
|
93
|
+
return workflowFields[key];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Transforms raw workflow data into structured format
|
|
97
|
+
*/
|
|
98
|
+
function transformWorkflow(process) {
|
|
99
|
+
return {
|
|
100
|
+
_id: process._id,
|
|
101
|
+
name: process.name,
|
|
102
|
+
phases: transformPhases(process.phases || {}),
|
|
103
|
+
fields: transformWorkflowFields(process.fields || {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Transforms workflow phases
|
|
108
|
+
*/
|
|
109
|
+
function transformPhases(phases) {
|
|
110
|
+
const transformed = {};
|
|
111
|
+
Object.entries(phases).forEach(([phaseId, phase]) => {
|
|
112
|
+
transformed[phaseId] = {
|
|
113
|
+
_id: phaseId,
|
|
114
|
+
name: phase.name,
|
|
115
|
+
isEndpoint: phase.isEndpoint,
|
|
116
|
+
possibleNextPhase: phase.possibleNextPhase,
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
return transformed;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Transforms workflow fields
|
|
123
|
+
*/
|
|
124
|
+
function transformWorkflowFields(fields) {
|
|
125
|
+
const transformed = {};
|
|
126
|
+
Object.entries(fields).forEach(([fieldId, field]) => {
|
|
127
|
+
transformed[fieldId] = {
|
|
128
|
+
_id: fieldId,
|
|
129
|
+
label: field.label || fieldId,
|
|
130
|
+
type: field.type || 'text',
|
|
131
|
+
key: field.key,
|
|
132
|
+
required: field.required,
|
|
133
|
+
data: field.data,
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
return transformed;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Formats timestamp to human readable string
|
|
140
|
+
*/
|
|
141
|
+
function formatTimestamp(timestamp) {
|
|
142
|
+
if (!timestamp)
|
|
143
|
+
return 'Unknown';
|
|
144
|
+
try {
|
|
145
|
+
return new Date(timestamp).toString();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return String(timestamp);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Formats user name from user info
|
|
153
|
+
*/
|
|
154
|
+
function formatUserName(user, fallbackId) {
|
|
155
|
+
if (!user)
|
|
156
|
+
return `User ${fallbackId}`;
|
|
157
|
+
const fullName = `${user.firstname || ''} ${user.lastname || ''}`.trim();
|
|
158
|
+
return fullName || user.email || `User ${fallbackId}`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Gets the first phase ID from workflow (for defaults)
|
|
162
|
+
*/
|
|
163
|
+
function getFirstPhaseId(workflow) {
|
|
164
|
+
const phases = Object.values(workflow.phases);
|
|
165
|
+
return phases.length > 0 ? phases[0]._id : null;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Formats activity data for MCP response
|
|
169
|
+
*/
|
|
170
|
+
function formatActivityListResponse(activities, workflow, phaseName) {
|
|
171
|
+
const count = activities.length;
|
|
172
|
+
const phaseInfo = phaseName ? ` (phase: ${phaseName})` : '';
|
|
173
|
+
return `✅ Found ${count} activities in workflow "${workflow.name}"${phaseInfo}\n\n${JSON.stringify(activities, null, 2)}`;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Creates a simplified activity format for search results
|
|
177
|
+
*/
|
|
178
|
+
function transformActivityForSearch(activity) {
|
|
179
|
+
return {
|
|
180
|
+
_id: activity._id,
|
|
181
|
+
name: activity.name,
|
|
182
|
+
workflow: {
|
|
183
|
+
_id: activity.process?._id,
|
|
184
|
+
name: activity.process?.name,
|
|
185
|
+
},
|
|
186
|
+
currentPhase: {
|
|
187
|
+
_id: activity.currentPhase?._id,
|
|
188
|
+
name: activity.currentPhase?.name,
|
|
189
|
+
},
|
|
190
|
+
created: activity.created?.humanReadable,
|
|
191
|
+
updated: activity.updated?.humanReadable,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=data-transformers.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Upload Utility
|
|
3
|
+
*
|
|
4
|
+
* Handles file uploads from URLs or filesystem paths with streaming support
|
|
5
|
+
*/
|
|
6
|
+
import { HailerClient } from '../hailer-clients';
|
|
7
|
+
/**
|
|
8
|
+
* File specification interface
|
|
9
|
+
*/
|
|
10
|
+
export interface FileSpec {
|
|
11
|
+
url?: string;
|
|
12
|
+
path?: string;
|
|
13
|
+
filename?: string;
|
|
14
|
+
isPublic?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Upload result interface
|
|
18
|
+
*/
|
|
19
|
+
export interface UploadResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
fileId?: string;
|
|
22
|
+
filename?: string;
|
|
23
|
+
source?: string;
|
|
24
|
+
type?: 'url' | 'path';
|
|
25
|
+
sizeKB?: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Upload a single file with streaming support
|
|
30
|
+
* Handles both URL and filesystem uploads with 100MB size limit
|
|
31
|
+
*/
|
|
32
|
+
export declare function uploadSingleFile(fileSpec: FileSpec, client: HailerClient): Promise<UploadResult>;
|
|
33
|
+
//# sourceMappingURL=file-upload.d.ts.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File Upload Utility
|
|
4
|
+
*
|
|
5
|
+
* Handles file uploads from URLs or filesystem paths with streaming support
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.uploadSingleFile = uploadSingleFile;
|
|
42
|
+
/**
|
|
43
|
+
* Upload a single file with streaming support
|
|
44
|
+
* Handles both URL and filesystem uploads with 100MB size limit
|
|
45
|
+
*/
|
|
46
|
+
async function uploadSingleFile(fileSpec, client) {
|
|
47
|
+
const FormData = (await Promise.resolve().then(() => __importStar(require('form-data')))).default;
|
|
48
|
+
const formData = new FormData();
|
|
49
|
+
let filename;
|
|
50
|
+
let source;
|
|
51
|
+
let type;
|
|
52
|
+
let fileSize;
|
|
53
|
+
if (fileSpec.url) {
|
|
54
|
+
// URL upload with streaming
|
|
55
|
+
type = 'url';
|
|
56
|
+
source = fileSpec.url;
|
|
57
|
+
const response = await fetch(fileSpec.url);
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);
|
|
60
|
+
}
|
|
61
|
+
const contentLength = response.headers.get('content-length');
|
|
62
|
+
fileSize = contentLength ? parseInt(contentLength, 10) : undefined;
|
|
63
|
+
const maxSize = 100 * 1024 * 1024;
|
|
64
|
+
if (fileSize && fileSize > maxSize) {
|
|
65
|
+
throw new Error(`File too large: ${(fileSize / 1024 / 1024).toFixed(2)}MB (max 100MB)`);
|
|
66
|
+
}
|
|
67
|
+
filename = fileSpec.filename || fileSpec.url.split('/').pop()?.split('?')[0] || 'file';
|
|
68
|
+
// Convert Web Stream to Node.js stream for form-data compatibility
|
|
69
|
+
if (!response.body) {
|
|
70
|
+
throw new Error('Response body is null');
|
|
71
|
+
}
|
|
72
|
+
const { Readable } = await Promise.resolve().then(() => __importStar(require('stream')));
|
|
73
|
+
const nodeStream = Readable.fromWeb(response.body);
|
|
74
|
+
formData.append('file', nodeStream, { filename, ...(fileSize && { knownLength: fileSize }) });
|
|
75
|
+
}
|
|
76
|
+
else if (fileSpec.path) {
|
|
77
|
+
// Filesystem upload with streaming
|
|
78
|
+
type = 'path';
|
|
79
|
+
source = fileSpec.path;
|
|
80
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
81
|
+
const fsPromises = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
82
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
83
|
+
const stats = await fsPromises.stat(fileSpec.path);
|
|
84
|
+
if (!stats.isFile()) {
|
|
85
|
+
throw new Error('Path is not a file');
|
|
86
|
+
}
|
|
87
|
+
fileSize = stats.size;
|
|
88
|
+
const maxSize = 100 * 1024 * 1024;
|
|
89
|
+
if (fileSize > maxSize) {
|
|
90
|
+
throw new Error(`File too large: ${(fileSize / 1024 / 1024).toFixed(2)}MB (max 100MB)`);
|
|
91
|
+
}
|
|
92
|
+
filename = fileSpec.filename || path.basename(fileSpec.path);
|
|
93
|
+
const readStream = fs.createReadStream(fileSpec.path);
|
|
94
|
+
formData.append('file', readStream, { filename, knownLength: fileSize });
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
throw new Error('Neither url nor path provided');
|
|
98
|
+
}
|
|
99
|
+
if (fileSpec.isPublic) {
|
|
100
|
+
formData.append('isPublic', 'true');
|
|
101
|
+
}
|
|
102
|
+
// Upload to Hailer using node's https module (fetch doesn't handle form-data well)
|
|
103
|
+
const apiBaseUrl = client.socket.host;
|
|
104
|
+
const sessionKey = client.sessionKey;
|
|
105
|
+
const url = new URL(`${apiBaseUrl}/upload`);
|
|
106
|
+
const https = await Promise.resolve().then(() => __importStar(require('https')));
|
|
107
|
+
const http = await Promise.resolve().then(() => __importStar(require('http')));
|
|
108
|
+
const uploadResponse = await new Promise((resolve, reject) => {
|
|
109
|
+
const options = {
|
|
110
|
+
hostname: url.hostname,
|
|
111
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
112
|
+
path: url.pathname,
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
'hlrkey': sessionKey,
|
|
116
|
+
...formData.getHeaders()
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const req = (url.protocol === 'https:' ? https : http).request(options, (res) => {
|
|
120
|
+
const chunks = [];
|
|
121
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
122
|
+
res.on('end', () => {
|
|
123
|
+
resolve({
|
|
124
|
+
statusCode: res.statusCode || 500,
|
|
125
|
+
body: Buffer.concat(chunks).toString('utf-8')
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
req.on('error', reject);
|
|
130
|
+
formData.pipe(req);
|
|
131
|
+
});
|
|
132
|
+
if (uploadResponse.statusCode < 200 || uploadResponse.statusCode >= 300) {
|
|
133
|
+
throw new Error(`Upload failed: ${uploadResponse.statusCode} - ${uploadResponse.body}`);
|
|
134
|
+
}
|
|
135
|
+
const result = JSON.parse(uploadResponse.body);
|
|
136
|
+
if (!result._id) {
|
|
137
|
+
throw new Error('No file ID returned');
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
success: true,
|
|
141
|
+
fileId: result._id,
|
|
142
|
+
filename,
|
|
143
|
+
source,
|
|
144
|
+
type,
|
|
145
|
+
sizeKB: fileSize ? (fileSize / 1024).toFixed(2) : 'unknown'
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=file-upload.js.map
|