@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.
Files changed (163) hide show
  1. package/.claude/commands/tool-builder.md +37 -0
  2. package/.claude/commands/ws-pull.md +44 -0
  3. package/.claude/settings.json +8 -0
  4. package/.claude/settings.local.json +49 -0
  5. package/.claude/skills/activity-api/SKILL.md +96 -0
  6. package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
  7. package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
  8. package/.claude/skills/agent-building/SKILL.md +243 -0
  9. package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
  10. package/.claude/skills/agent-building/references/code-examples.md +587 -0
  11. package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
  12. package/.claude/skills/app-api/SKILL.md +219 -0
  13. package/.claude/skills/app-api/references/app-endpoints.md +759 -0
  14. package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
  15. package/.claude/skills/create-app-skill/SKILL.md +1101 -0
  16. package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
  17. package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
  18. package/.claude/skills/hailer-api/SKILL.md +283 -0
  19. package/.claude/skills/hailer-api/references/activities.md +620 -0
  20. package/.claude/skills/hailer-api/references/authentication.md +216 -0
  21. package/.claude/skills/hailer-api/references/datasets.md +437 -0
  22. package/.claude/skills/hailer-api/references/files.md +301 -0
  23. package/.claude/skills/hailer-api/references/insights.md +469 -0
  24. package/.claude/skills/hailer-api/references/workflows.md +720 -0
  25. package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
  26. package/.claude/skills/insight-api/SKILL.md +185 -0
  27. package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
  28. package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
  29. package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
  30. package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
  31. package/.claude/skills/local-first-skill/SKILL.md +570 -0
  32. package/.claude/skills/mcp-tools/SKILL.md +419 -0
  33. package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
  34. package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
  35. package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
  36. package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
  37. package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
  38. package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
  39. package/.claude/skills/remove-app-skill/SKILL.md +985 -0
  40. package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
  41. package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
  42. package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
  43. package/.claude/skills/skill-testing/README.md +137 -0
  44. package/.claude/skills/skill-testing/SKILL.md +348 -0
  45. package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
  46. package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
  47. package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
  48. package/.claude/skills/tool-builder/SKILL.md +328 -0
  49. package/.claude/skills/update-app-skill/SKILL.md +970 -0
  50. package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
  51. package/.env.example +81 -0
  52. package/.mcp.json +13 -0
  53. package/README.md +297 -0
  54. package/dist/app.d.ts +4 -0
  55. package/dist/app.js +74 -0
  56. package/dist/cli.d.ts +3 -0
  57. package/dist/cli.js +5 -0
  58. package/dist/client/adaptive-documentation-bot.d.ts +108 -0
  59. package/dist/client/adaptive-documentation-bot.js +475 -0
  60. package/dist/client/adaptive-documentation-types.d.ts +66 -0
  61. package/dist/client/adaptive-documentation-types.js +9 -0
  62. package/dist/client/agent-activity-bot.d.ts +51 -0
  63. package/dist/client/agent-activity-bot.js +166 -0
  64. package/dist/client/agent-tracker.d.ts +499 -0
  65. package/dist/client/agent-tracker.js +659 -0
  66. package/dist/client/description-updater.d.ts +56 -0
  67. package/dist/client/description-updater.js +259 -0
  68. package/dist/client/log-parser.d.ts +72 -0
  69. package/dist/client/log-parser.js +387 -0
  70. package/dist/client/mcp-client.d.ts +50 -0
  71. package/dist/client/mcp-client.js +532 -0
  72. package/dist/client/message-processor.d.ts +35 -0
  73. package/dist/client/message-processor.js +352 -0
  74. package/dist/client/multi-bot-manager.d.ts +24 -0
  75. package/dist/client/multi-bot-manager.js +74 -0
  76. package/dist/client/providers/anthropic-provider.d.ts +19 -0
  77. package/dist/client/providers/anthropic-provider.js +631 -0
  78. package/dist/client/providers/llm-provider.d.ts +47 -0
  79. package/dist/client/providers/llm-provider.js +367 -0
  80. package/dist/client/providers/openai-provider.d.ts +23 -0
  81. package/dist/client/providers/openai-provider.js +621 -0
  82. package/dist/client/simple-llm-caller.d.ts +19 -0
  83. package/dist/client/simple-llm-caller.js +100 -0
  84. package/dist/client/skill-generator.d.ts +81 -0
  85. package/dist/client/skill-generator.js +386 -0
  86. package/dist/client/test-adaptive-bot.d.ts +9 -0
  87. package/dist/client/test-adaptive-bot.js +82 -0
  88. package/dist/client/token-pricing.d.ts +38 -0
  89. package/dist/client/token-pricing.js +127 -0
  90. package/dist/client/token-tracker.d.ts +232 -0
  91. package/dist/client/token-tracker.js +457 -0
  92. package/dist/client/token-usage-bot.d.ts +53 -0
  93. package/dist/client/token-usage-bot.js +153 -0
  94. package/dist/client/tool-executor.d.ts +69 -0
  95. package/dist/client/tool-executor.js +159 -0
  96. package/dist/client/tool-schema-loader.d.ts +60 -0
  97. package/dist/client/tool-schema-loader.js +178 -0
  98. package/dist/client/types.d.ts +69 -0
  99. package/dist/client/types.js +7 -0
  100. package/dist/config.d.ts +162 -0
  101. package/dist/config.js +296 -0
  102. package/dist/core.d.ts +26 -0
  103. package/dist/core.js +147 -0
  104. package/dist/lib/context-manager.d.ts +111 -0
  105. package/dist/lib/context-manager.js +431 -0
  106. package/dist/lib/logger.d.ts +74 -0
  107. package/dist/lib/logger.js +277 -0
  108. package/dist/lib/materialize.d.ts +3 -0
  109. package/dist/lib/materialize.js +101 -0
  110. package/dist/lib/normalizedName.d.ts +7 -0
  111. package/dist/lib/normalizedName.js +48 -0
  112. package/dist/lib/prompt-length-manager.d.ts +81 -0
  113. package/dist/lib/prompt-length-manager.js +457 -0
  114. package/dist/lib/terminal-prompt.d.ts +9 -0
  115. package/dist/lib/terminal-prompt.js +108 -0
  116. package/dist/mcp/UserContextCache.d.ts +56 -0
  117. package/dist/mcp/UserContextCache.js +163 -0
  118. package/dist/mcp/auth.d.ts +2 -0
  119. package/dist/mcp/auth.js +29 -0
  120. package/dist/mcp/hailer-clients.d.ts +42 -0
  121. package/dist/mcp/hailer-clients.js +246 -0
  122. package/dist/mcp/signal-handler.d.ts +45 -0
  123. package/dist/mcp/signal-handler.js +317 -0
  124. package/dist/mcp/tool-registry.d.ts +100 -0
  125. package/dist/mcp/tool-registry.js +306 -0
  126. package/dist/mcp/tools/activity.d.ts +15 -0
  127. package/dist/mcp/tools/activity.js +955 -0
  128. package/dist/mcp/tools/app.d.ts +20 -0
  129. package/dist/mcp/tools/app.js +1488 -0
  130. package/dist/mcp/tools/discussion.d.ts +19 -0
  131. package/dist/mcp/tools/discussion.js +950 -0
  132. package/dist/mcp/tools/file.d.ts +15 -0
  133. package/dist/mcp/tools/file.js +119 -0
  134. package/dist/mcp/tools/insight.d.ts +17 -0
  135. package/dist/mcp/tools/insight.js +806 -0
  136. package/dist/mcp/tools/skill.d.ts +10 -0
  137. package/dist/mcp/tools/skill.js +279 -0
  138. package/dist/mcp/tools/user.d.ts +10 -0
  139. package/dist/mcp/tools/user.js +108 -0
  140. package/dist/mcp/tools/workflow-template.d.ts +19 -0
  141. package/dist/mcp/tools/workflow-template.js +822 -0
  142. package/dist/mcp/tools/workflow.d.ts +18 -0
  143. package/dist/mcp/tools/workflow.js +1362 -0
  144. package/dist/mcp/utils/api-errors.d.ts +45 -0
  145. package/dist/mcp/utils/api-errors.js +160 -0
  146. package/dist/mcp/utils/data-transformers.d.ts +102 -0
  147. package/dist/mcp/utils/data-transformers.js +194 -0
  148. package/dist/mcp/utils/file-upload.d.ts +33 -0
  149. package/dist/mcp/utils/file-upload.js +148 -0
  150. package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
  151. package/dist/mcp/utils/hailer-api-client.js +323 -0
  152. package/dist/mcp/utils/index.d.ts +13 -0
  153. package/dist/mcp/utils/index.js +39 -0
  154. package/dist/mcp/utils/logger.d.ts +42 -0
  155. package/dist/mcp/utils/logger.js +103 -0
  156. package/dist/mcp/utils/types.d.ts +286 -0
  157. package/dist/mcp/utils/types.js +7 -0
  158. package/dist/mcp/workspace-cache.d.ts +42 -0
  159. package/dist/mcp/workspace-cache.js +97 -0
  160. package/dist/mcp-server.d.ts +42 -0
  161. package/dist/mcp-server.js +280 -0
  162. package/package.json +56 -0
  163. 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