@hailer/mcp 1.1.11 → 1.1.13

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 (252) hide show
  1. package/dist/app.js +18 -5
  2. package/dist/bot/bot-config.d.ts +12 -1
  3. package/dist/bot/bot-config.js +98 -14
  4. package/dist/bot/bot-manager.d.ts +13 -3
  5. package/dist/bot/bot-manager.js +80 -25
  6. package/dist/bot/bot.d.ts +46 -0
  7. package/dist/bot/bot.js +542 -166
  8. package/dist/bot/services/message-classifier.js +17 -0
  9. package/dist/bot/services/permission-guard.d.ts +52 -0
  10. package/dist/bot/services/permission-guard.js +149 -0
  11. package/dist/bot/services/types.d.ts +5 -0
  12. package/dist/bot/services/typing-indicator.d.ts +6 -1
  13. package/dist/bot/services/typing-indicator.js +19 -3
  14. package/dist/config.d.ts +6 -1
  15. package/dist/config.js +43 -0
  16. package/dist/core.js +3 -6
  17. package/dist/mcp/UserContextCache.d.ts +5 -0
  18. package/dist/mcp/UserContextCache.js +51 -19
  19. package/dist/mcp/hailer-clients.d.ts +19 -1
  20. package/dist/mcp/hailer-clients.js +157 -20
  21. package/dist/mcp/session-store.d.ts +68 -0
  22. package/dist/mcp/session-store.js +169 -0
  23. package/dist/mcp/signal-handler.js +12 -12
  24. package/dist/mcp/tool-registry.d.ts +17 -4
  25. package/dist/mcp/tool-registry.js +37 -7
  26. package/dist/mcp/tools/activity.js +99 -7
  27. package/dist/mcp/tools/app-scaffold.js +304 -336
  28. package/dist/mcp/tools/company.d.ts +9 -0
  29. package/dist/mcp/tools/company.js +88 -0
  30. package/dist/mcp/tools/discussion.js +68 -0
  31. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  32. package/dist/mcp/tools/workflow-permissions.js +204 -0
  33. package/dist/mcp/tools/workflow.js +57 -18
  34. package/dist/mcp/utils/index.d.ts +2 -0
  35. package/dist/mcp/utils/index.js +12 -1
  36. package/dist/mcp/utils/role-utils.d.ts +74 -0
  37. package/dist/mcp/utils/role-utils.js +151 -0
  38. package/dist/mcp/utils/types.d.ts +43 -1
  39. package/dist/mcp/utils/types.js +14 -0
  40. package/dist/mcp/webhook-handler.d.ts +6 -0
  41. package/dist/mcp/webhook-handler.js +11 -0
  42. package/dist/mcp-server.d.ts +23 -2
  43. package/dist/mcp-server.js +639 -111
  44. package/dist/plugins/vipunen/client.d.ts +150 -0
  45. package/dist/plugins/vipunen/client.js +535 -0
  46. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  47. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  48. package/dist/plugins/vipunen/index.d.ts +41 -0
  49. package/dist/plugins/vipunen/index.js +88 -0
  50. package/dist/plugins/vipunen/tools.d.ts +26 -0
  51. package/dist/plugins/vipunen/tools.js +501 -0
  52. package/package.json +2 -1
  53. package/.claude/.context-watchdog.json +0 -1
  54. package/.claude/.session-checked +0 -1
  55. package/.claude/CLAUDE.md +0 -370
  56. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  57. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  58. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  59. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  60. package/.claude/agents/agent-code-simplifier.md +0 -53
  61. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  62. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  63. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  64. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  65. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  66. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  67. package/.claude/agents/agent-ivan-monolith.md +0 -154
  68. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  69. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  70. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  71. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  72. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  73. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  74. package/.claude/agents/agent-permissions-handler.md +0 -208
  75. package/.claude/agents/agent-simple-writer.md +0 -48
  76. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  77. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  78. package/.claude/agents/agent-ui-designer.md +0 -100
  79. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  80. package/.claude/agents/agent-web-search.md +0 -55
  81. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  82. package/.claude/agents/agent-zara-zapier.md +0 -159
  83. package/.claude/commands/app-squad.md +0 -135
  84. package/.claude/commands/audit-squad.md +0 -158
  85. package/.claude/commands/autoplan.md +0 -563
  86. package/.claude/commands/cleanup-squad.md +0 -98
  87. package/.claude/commands/config-squad.md +0 -106
  88. package/.claude/commands/crud-squad.md +0 -87
  89. package/.claude/commands/data-squad.md +0 -97
  90. package/.claude/commands/debug-squad.md +0 -303
  91. package/.claude/commands/doc-squad.md +0 -65
  92. package/.claude/commands/handoff.md +0 -137
  93. package/.claude/commands/health.md +0 -49
  94. package/.claude/commands/help.md +0 -29
  95. package/.claude/commands/help:agents.md +0 -151
  96. package/.claude/commands/help:commands.md +0 -78
  97. package/.claude/commands/help:faq.md +0 -79
  98. package/.claude/commands/help:plugins.md +0 -50
  99. package/.claude/commands/help:skills.md +0 -93
  100. package/.claude/commands/help:tools.md +0 -75
  101. package/.claude/commands/hotfix-squad.md +0 -112
  102. package/.claude/commands/integration-squad.md +0 -82
  103. package/.claude/commands/janitor-squad.md +0 -167
  104. package/.claude/commands/learn-auto.md +0 -120
  105. package/.claude/commands/learn.md +0 -120
  106. package/.claude/commands/mcp-list.md +0 -27
  107. package/.claude/commands/onboard-squad.md +0 -140
  108. package/.claude/commands/plan-workspace.md +0 -732
  109. package/.claude/commands/prd.md +0 -130
  110. package/.claude/commands/project-status.md +0 -82
  111. package/.claude/commands/publish.md +0 -138
  112. package/.claude/commands/recap.md +0 -69
  113. package/.claude/commands/restore.md +0 -64
  114. package/.claude/commands/review-squad.md +0 -152
  115. package/.claude/commands/save.md +0 -24
  116. package/.claude/commands/stats.md +0 -19
  117. package/.claude/commands/swarm.md +0 -210
  118. package/.claude/commands/tool-builder.md +0 -39
  119. package/.claude/commands/ws-pull.md +0 -44
  120. package/.claude/hooks/_shared-memory.cjs +0 -305
  121. package/.claude/hooks/_utils.cjs +0 -108
  122. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  123. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  124. package/.claude/hooks/app-edit-guard.cjs +0 -494
  125. package/.claude/hooks/auto-learn.cjs +0 -304
  126. package/.claude/hooks/bash-guard.cjs +0 -272
  127. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  128. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  129. package/.claude/hooks/context-watchdog.cjs +0 -230
  130. package/.claude/hooks/delegation-reminder.cjs +0 -465
  131. package/.claude/hooks/design-system-lint.cjs +0 -271
  132. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  133. package/.claude/hooks/prompt-guard.cjs +0 -354
  134. package/.claude/hooks/publish-template-guard.cjs +0 -147
  135. package/.claude/hooks/session-start.cjs +0 -35
  136. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  137. package/.claude/hooks/skill-injector.cjs +0 -140
  138. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  139. package/.claude/hooks/src-edit-guard.cjs +0 -240
  140. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  141. package/.claude/settings.json +0 -257
  142. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  143. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  144. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  145. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  146. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  147. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  148. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  149. package/.claude/skills/agent-structure/SKILL.md +0 -98
  150. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  151. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  152. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  153. package/.claude/skills/frontend-design/SKILL.md +0 -254
  154. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  155. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  156. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  157. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  158. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  159. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  160. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  161. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  162. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  163. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  164. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  165. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  166. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  167. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  168. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  169. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  170. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  171. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  172. package/.claude/skills/json-only-output/SKILL.md +0 -72
  173. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  174. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  175. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  176. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  177. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  178. package/.claude/skills/tool-builder/SKILL.md +0 -250
  179. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  180. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  181. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  182. package/.hailer-mcp-port +0 -1
  183. package/.mcp.json +0 -13
  184. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  185. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  186. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  187. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  188. package/.opencode/agent/agent-code-simplifier.md +0 -31
  189. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  190. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  191. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  192. package/.opencode/agent/agent-helga-workflow-config.md +0 -204
  193. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  194. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  195. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  196. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  197. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  198. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  199. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  200. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  201. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  202. package/.opencode/agent/agent-permissions-handler.md +0 -50
  203. package/.opencode/agent/agent-simple-writer.md +0 -45
  204. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  205. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  206. package/.opencode/agent/agent-ui-designer.md +0 -56
  207. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  208. package/.opencode/agent/agent-web-search.md +0 -42
  209. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  210. package/.opencode/agent/agent-zara-zapier.md +0 -53
  211. package/.opencode/commands/app-squad.md +0 -135
  212. package/.opencode/commands/audit-squad.md +0 -158
  213. package/.opencode/commands/autoplan.md +0 -563
  214. package/.opencode/commands/cleanup-squad.md +0 -98
  215. package/.opencode/commands/config-squad.md +0 -106
  216. package/.opencode/commands/crud-squad.md +0 -87
  217. package/.opencode/commands/data-squad.md +0 -97
  218. package/.opencode/commands/debug-squad.md +0 -303
  219. package/.opencode/commands/doc-squad.md +0 -65
  220. package/.opencode/commands/handoff.md +0 -137
  221. package/.opencode/commands/health.md +0 -49
  222. package/.opencode/commands/help-agents.md +0 -151
  223. package/.opencode/commands/help-commands.md +0 -32
  224. package/.opencode/commands/help-faq.md +0 -29
  225. package/.opencode/commands/help-plugins.md +0 -28
  226. package/.opencode/commands/help-skills.md +0 -7
  227. package/.opencode/commands/help-tools.md +0 -40
  228. package/.opencode/commands/help.md +0 -28
  229. package/.opencode/commands/hotfix-squad.md +0 -112
  230. package/.opencode/commands/integration-squad.md +0 -82
  231. package/.opencode/commands/janitor-squad.md +0 -167
  232. package/.opencode/commands/learn-auto.md +0 -120
  233. package/.opencode/commands/learn.md +0 -120
  234. package/.opencode/commands/mcp-list.md +0 -27
  235. package/.opencode/commands/onboard-squad.md +0 -140
  236. package/.opencode/commands/plan-workspace.md +0 -732
  237. package/.opencode/commands/prd.md +0 -131
  238. package/.opencode/commands/project-status.md +0 -82
  239. package/.opencode/commands/publish.md +0 -138
  240. package/.opencode/commands/recap.md +0 -69
  241. package/.opencode/commands/restore.md +0 -64
  242. package/.opencode/commands/review-squad.md +0 -152
  243. package/.opencode/commands/save.md +0 -24
  244. package/.opencode/commands/stats.md +0 -19
  245. package/.opencode/commands/swarm.md +0 -210
  246. package/.opencode/commands/tool-builder.md +0 -39
  247. package/.opencode/commands/ws-pull.md +0 -44
  248. package/.opencode/opencode.json +0 -21
  249. package/inbox/failures.log +0 -1
  250. package/inbox/usage.jsonl +0 -4
  251. package/scripts/postinstall.cjs +0 -64
  252. package/scripts/test-hal-tools.ts +0 -154
@@ -1,686 +0,0 @@
1
- ---
2
- name: hailer-monolith-automations
3
- description: Understanding and building automations in the Hailer project-monolith
4
- version: 1.2.0
5
- triggers: monolith, automation, scheduled task, webhook automation, invoicing, data sync
6
- ---
7
-
8
- # Hailer Monolith Automations
9
-
10
- The project-monolith is a centralized backend service for running customer automations without deploying separate microservices.
11
-
12
- ## Architecture Overview
13
-
14
- ```
15
- ┌─────────────────────────────────────────────────────────────┐
16
- │ Internal Network Only │
17
- │ ┌─────────────────┐ ┌─────────────────┐ │
18
- │ │ Hailer API │───▶│ project-monolith │───▶ External │
19
- │ │ (webhooks) │ │ (Express.js) │ APIs │
20
- │ └─────────────────┘ └─────────────────┘ (Netvisor, │
21
- │ │ Procountor, │
22
- │ ▼ etc.) │
23
- │ ┌─────────────────┐ │
24
- │ │ node-schedule │ │
25
- │ │ (cron jobs) │ │
26
- │ └─────────────────┘ │
27
- └─────────────────────────────────────────────────────────────┘
28
-
29
- Config: AWS Secrets Manager
30
- Logs: Winston structured logging
31
- ```
32
-
33
- **Key Points:**
34
- - Internal-only access (no internet-facing endpoints)
35
- - NAT Gateway for outgoing requests to external APIs
36
- - Single server handles 50+ automations
37
- - Config stored in AWS Secrets Manager
38
-
39
- ---
40
-
41
- ## Automation Types
42
-
43
- ### 1. Webhook-Triggered Automations
44
-
45
- Receive Hailer activity data via POST request when activities are created/updated.
46
-
47
- **Trigger:** Hailer webhook on activity update → POST to monolith endpoint
48
-
49
- **⚠️ CRITICAL: Never use SDK enums in automations.** Webhooks receive raw MongoDB ObjectIds, not SDK enum names. Use real IDs from config or extract from payload.
50
-
51
- **SDK enums ARE available in monolith context** via imports from workspace, but webhook payloads use real ObjectIds. Example:
52
- ```typescript
53
- // Available: Import SDK enums for clarity
54
- import { WorkflowIds, FieldIds } from '../workspace/enums';
55
-
56
- // But webhook payload has real IDs
57
- const workflowId = request.process; // "507f1f77bcf86cd799439011"
58
- const fieldValue = request.fields.find(f => f.id === FieldIds.customer); // OK to use enum here
59
- ```
60
-
61
- **Request payload:**
62
- ```typescript
63
- interface WebhookPayload {
64
- _id: string; // Activity ID (MongoDB ObjectId)
65
- cid: string; // Workspace ID
66
- process: string; // Workflow ID (MongoDB ObjectId)
67
- currentPhase: string; // Phase ID (MongoDB ObjectId)
68
- name: string;
69
- fields: Array<{
70
- id: string; // Field ID (MongoDB ObjectId)
71
- type: string; // Field type
72
- value: any; // Field value
73
- key?: string; // Optional field key (human-readable)
74
- }>;
75
- // ... other activity fields
76
- }
77
-
78
- // Finding field values - two methods:
79
- // Method 1: By key (if field has key property - preferred when available)
80
- const tagValue = payload.fields.find(f => f.key === 'tag')?.value;
81
-
82
- // Method 2: By fieldId (always works)
83
- const tagValue = payload.fields.find(f => f.id === config.tagFieldId)?.value;
84
- ```
85
-
86
- **Example endpoint:**
87
- ```typescript
88
- router.post('/my-automation/trigger', jsonParser, async (req, res) => {
89
- if (req.headers['content-type'] !== 'application/json') {
90
- return res.status(415).send('Content-Type must be application/json');
91
- }
92
-
93
- const config = await fetchConfig('monolith-my-automation');
94
-
95
- // Validate trigger conditions
96
- if (config.triggerProcessId !== req.body.process) {
97
- return res.status(200).send('Not applicable');
98
- }
99
-
100
- // Run automation (async, don't block response)
101
- void myAutomation(req.body, config);
102
-
103
- res.status(200).send('Ok');
104
- });
105
- ```
106
-
107
- **Use Cases:**
108
- - Activity data sync to external system on phase change
109
- - Create linked activities when parent updated
110
- - Send notifications on specific field changes
111
- - XML/file import when activity created
112
-
113
- ---
114
-
115
- ### 2. Scheduled Automations
116
-
117
- Run on a schedule using node-schedule (cron-like).
118
-
119
- **Schedule patterns:**
120
- ```typescript
121
- // Run at 3:02 AM on 1st of month
122
- scheduleJob('Monthly Task', { date: 1, hour: 3, minute: 2 }, async () => {});
123
-
124
- // Run at 5:45 AM every day
125
- scheduleJob('Daily Task', { hour: 5, minute: 45 }, async () => {});
126
-
127
- // Run at specific dates
128
- scheduleJob('Bi-weekly', { date: [1, 15], hour: 3, minute: 0 }, async () => {});
129
-
130
- // Run every hour
131
- scheduleJob('Hourly', { minute: 0 }, async () => {});
132
- ```
133
-
134
- **Example:**
135
- ```typescript
136
- scheduleJob('Invoice Subscriptions', { date: 1, hour: 3, minute: 10 }, async () => {
137
- const config = await fetchConfig('monolith-subscription-invoicing');
138
- const result = await invoiceSubscriptions(config);
139
- Logger.info('Subscription invoicing complete', { content: result });
140
- });
141
- ```
142
-
143
- **Use Cases:**
144
- - Monthly invoicing runs
145
- - Daily data sync with external systems
146
- - Activity archiving (cleanup old completed activities)
147
- - Alarm/status checks
148
- - Currency rate updates
149
-
150
- ---
151
-
152
- ### 3. Third-Party Integration Automations
153
-
154
- Sync data between Hailer and external systems.
155
-
156
- **Supported Systems:**
157
- | System | Type | Direction |
158
- |--------|------|-----------|
159
- | Netvisor | Accounting | Hailer ↔ Netvisor |
160
- | Procountor | Accounting | Hailer → Procountor |
161
- | Severa | Project Management | Hailer ↔ Severa |
162
- | SignSpace | E-signatures | Hailer → SignSpace |
163
- | INTU | HR/Payroll | Hailer ↔ INTU |
164
- | Logiapp | Logistics | Hailer → Logiapp |
165
- | Torna | Forestry | Torna → Hailer |
166
-
167
- **Pattern:**
168
- 1. Authenticate with external API
169
- 2. Fetch data from Hailer (or external system)
170
- 3. Transform data format
171
- 4. Push to destination
172
- 5. Update status fields in Hailer
173
-
174
- ---
175
-
176
- ## Common Patterns
177
-
178
- ### Config from AWS Secrets Manager
179
-
180
- ```typescript
181
- import { fetchConfig } from './fetch-secrets';
182
-
183
- // Config name matches AWS secret: "monolith-{name}"
184
- const config = await fetchConfig('monolith-my-automation');
185
-
186
- // Config structure example
187
- interface AutomationConfig {
188
- credentials: {
189
- email: string;
190
- password: string;
191
- };
192
- triggerProcessId: string;
193
- targetProcessId: string;
194
- fieldMappings: Record<string, string>;
195
- // ... custom config
196
- }
197
- ```
198
-
199
- ### Hailer Client Connection
200
-
201
- ```typescript
202
- import { Client } from '@hailer/cli';
203
-
204
- // Connect with retry helper
205
- const client = await connectWithRetry(config, 5);
206
-
207
- // Or direct connection
208
- const client = await Client.create({
209
- host: 'https://api.hailer.com',
210
- username: config.credentials.email,
211
- password: config.credentials.password
212
- });
213
-
214
- // Always disconnect when done
215
- client.disconnect();
216
- ```
217
-
218
- ### Activity Operations
219
-
220
- ```typescript
221
- // List activities
222
- const { activities } = await client.request('v3.activity.list', [
223
- { processId: workflowId, phaseId: phaseId },
224
- { limit: 1000, filters: { status: { equalTo: 'Active' } }, returnFlat: true }
225
- ]);
226
-
227
- // Create activities
228
- const created = await client.request('v3.activity.createMany', [
229
- { processId: workflowId, phaseId: phaseId, name: 'New Activity', fields: { fieldId: value } }
230
- ]);
231
-
232
- // Update activities
233
- const updated = await client.request('v3.activity.updateMany', [
234
- { _id: activityId, fields: { fieldId: newValue }, phaseId: newPhaseId }
235
- ]);
236
- ```
237
-
238
- ### Structured Logging
239
-
240
- ```typescript
241
- // Local log array for aggregated logging
242
- const logArray: any[] = [];
243
- const log = (level: string, message: string, data?) => {
244
- logArray.push({ level, message, data, time: Date.now() });
245
- };
246
-
247
- log('info', 'Process started');
248
- log('info', 'Fetched activities', { count: activities.length });
249
-
250
- // Final structured log
251
- Logger.info('Automation complete', {
252
- content: logArray,
253
- workspaceId: req.body.cid,
254
- workflowId: req.body.process,
255
- activityId: req.body._id
256
- });
257
- ```
258
-
259
- ### Deduplication (Prevent Double Processing)
260
-
261
- ```typescript
262
- const processingActivities = new Set();
263
-
264
- router.post('/my-automation', jsonParser, async (req, res) => {
265
- const lockKey = `${req.body.cid}-${req.body._id}`;
266
-
267
- if (processingActivities.has(lockKey)) {
268
- return res.status(200).send('Already processing');
269
- }
270
-
271
- processingActivities.add(lockKey);
272
- try {
273
- await runAutomation(req.body, config);
274
- } finally {
275
- processingActivities.delete(lockKey);
276
- }
277
-
278
- res.status(200).send('Ok');
279
- });
280
- ```
281
-
282
- ---
283
-
284
- ## File Structure
285
-
286
- ```
287
- project-monolith/
288
- ├── src/
289
- │ ├── run.ts # Express server, all endpoints
290
- │ ├── schedules.ts # All scheduled jobs
291
- │ ├── fetch-secrets.ts # AWS Secrets Manager
292
- │ ├── logger.ts # Winston logger
293
- │ ├── types.ts # Common types
294
- │ ├── utils/
295
- │ │ └── hailer-utils.ts # Helper functions
296
- │ └── automations/
297
- │ ├── generic-handle-activities.ts # Reusable CRUD
298
- │ ├── netvisor/ # Netvisor integrations
299
- │ ├── procountor/ # Procountor integrations
300
- │ ├── hailer/ # Hailer-internal automations
301
- │ └── {customer}/ # Customer-specific automations
302
- └── package.json
303
- ```
304
-
305
- ---
306
-
307
- ## When to Use Monolith vs Other Options
308
-
309
- | Scenario | Solution |
310
- |----------|----------|
311
- | Simple webhook trigger → Hailer CRUD | **Monolith** |
312
- | Scheduled data sync | **Monolith** |
313
- | Accounting integration (Netvisor, Procountor) | **Monolith** (existing code) |
314
- | Real-time activity sync | **activity-mover** |
315
- | SCIM user provisioning | **microsoft-entra** |
316
- | Kafka event processing | **digione-integration** |
317
- | Complex stateful integration | **Dedicated microservice** |
318
-
319
- ---
320
-
321
- ## Adding a New Automation
322
-
323
- ### 1. Create automation file
324
-
325
- ```typescript
326
- // src/automations/{customer}/{automation-name}.ts
327
- import { Client } from '@hailer/cli';
328
- import { Logger } from '../../logger';
329
- import { connectWithRetry } from '../../utils/hailer-utils';
330
-
331
- export const myAutomation = async (request: any, config: any): Promise<boolean> => {
332
- const logArray: any[] = [];
333
- const log = (level: string, message: string, data?) => {
334
- logArray.push({ level, message, data, time: Date.now() });
335
- };
336
-
337
- try {
338
- log('info', 'Starting automation');
339
-
340
- const client = await connectWithRetry(config, 5);
341
-
342
- // Your automation logic here
343
-
344
- client.disconnect();
345
- Logger.info('Automation success', { content: logArray });
346
- return true;
347
- } catch (error) {
348
- log('error', 'Automation failed', error);
349
- Logger.error('Automation failed', { content: logArray, error });
350
- return false;
351
- }
352
- };
353
- ```
354
-
355
- ### 2. Add endpoint (webhook) or schedule
356
-
357
- **Webhook (run.ts):**
358
- ```typescript
359
- import { myAutomation } from './automations/{customer}/{automation-name}';
360
-
361
- router.post('/{customer}/my-automation', jsonParser, async (req, res) => {
362
- const config = await fetchConfig('monolith-my-automation');
363
- if (config.triggerProcessId === req.body.process) {
364
- void myAutomation(req.body, config);
365
- }
366
- res.status(200).send('Ok');
367
- });
368
- ```
369
-
370
- **Scheduled (schedules.ts):**
371
- ```typescript
372
- import { myAutomation } from './automations/{customer}/{automation-name}';
373
-
374
- scheduleJob('My Scheduled Task', { hour: 3, minute: 0 }, async () => {
375
- const config = await fetchConfig('monolith-my-automation');
376
- await myAutomation(null, config);
377
- });
378
- ```
379
-
380
- ### 3. Generate AWS Config (User Deploys)
381
-
382
- Claude generates the config JSON. User uploads to AWS Secrets Manager manually.
383
-
384
- **Secret name:** `monolith-{automation-name}`
385
-
386
- ```json
387
- {
388
- "credentials": {
389
- "email": "integration@customer.com",
390
- "password": "USER_PROVIDES_PASSWORD"
391
- },
392
- "triggerProcessId": "workflow-id",
393
- "targetProcessId": "target-workflow-id",
394
- "fieldMappings": {
395
- "sourceField": "targetField"
396
- }
397
- }
398
- ```
399
-
400
- **User action:**
401
- 1. Fill in the integration user password
402
- 2. Upload to AWS Secrets Manager as `monolith-{automation-name}`
403
-
404
- ### 4. Git Workflow with Symlink
405
-
406
- The automation file lives in hailer-integration (for git). The project workspace gets a symlink (for context).
407
-
408
- **Step 1: Create feature branch in hailer-integration**
409
- ```bash
410
- cd ~/hailer-integration
411
- git checkout -b feature/{customer}-{automation-name}
412
- mkdir -p packages/project-monolith/src/automations/{customer}
413
- ```
414
-
415
- **Step 2: Write the automation file**
416
- ```bash
417
- # Claude writes to:
418
- ~/hailer-integration/packages/project-monolith/src/automations/{customer}/{automation-name}.ts
419
- ```
420
-
421
- **Step 3: Symlink to project workspace**
422
- ```bash
423
- cd ~/path/to/project-workspace
424
- mkdir -p integrations
425
- ln -s ~/hailer-integration/packages/project-monolith/src/automations/{customer}/{automation-name}.ts \
426
- integrations/{automation-name}.ts
427
- ```
428
-
429
- **Step 4: Commit and push**
430
- ```bash
431
- cd ~/hailer-integration
432
- git add packages/project-monolith/src/automations/{customer}/
433
- git commit -m "Add {automation-name} automation for {customer}"
434
- git push -u origin feature/{customer}-{automation-name}
435
- ```
436
-
437
- **Result:**
438
- - File in hailer-integration → can push to git, create PR
439
- - Symlink in project workspace → Claude has context when working on project
440
- - After PR merge → CI/CD deploys to monolith
441
-
442
- ### 5. Configure Hailer webhook (for webhook-triggered)
443
-
444
- In Hailer admin:
445
- 1. Go to Integrations → Webhooks
446
- 2. Add webhook URL: `https://internal-lb/api/v1/{customer}/my-automation`
447
- 3. Select trigger workflow and events (create, update)
448
-
449
- ---
450
-
451
- ## Existing Automation Categories
452
-
453
- ### Invoicing Automations
454
- - `hailer-invoicing-subscription-v2` - Scheduled subscription billing
455
- - `hailer-invoicing-billable-work` - Bill tracked time/expenses
456
- - `hailer-invoicing-project-won` - Invoice on project completion
457
- - `3pl-invoicing-v2` - 3PL storage billing
458
- - `kspt-invoicing-v2-webhook` - KSPT rental invoicing
459
-
460
- ### Data Sync Automations
461
- - `netvisor-integration` - Netvisor ↔ Hailer sync
462
- - `procountor-integration-main` - Procountor invoicing
463
- - `severa-integration` - Severa project sync
464
- - `fin-forelia-*` - Forelia logistics/forestry
465
-
466
- ### Activity Management
467
- - `generic-handle-activities` - Reusable create/update
468
- - `activity-archiver` - Archive old activities
469
- - `porvoo-archive` - City-specific archiving
470
-
471
- ### Notifications & Alarms
472
- - `porvoo-alarms` - Status checks and alerts
473
- - `porvoo-notifications` - Send notification messages
474
- - `3pl-saldo-alarms` - Inventory level alerts
475
-
476
- ### HR/Employee Automations
477
- - `siskon-siivous-intu-*` - INTU HR sync
478
- - `pohjaset-employee-task-automation` - Onboarding tasks
479
- - `siskon-siivous-synttarionnittelut` - Birthday greetings
480
-
481
- ---
482
-
483
- ## Built-in Automation Reference
484
-
485
- These are reusable, config-driven automations already in the monolith.
486
-
487
- ### Email Automation
488
-
489
- Webhook-based endpoints for sending emails triggered by Hailer workflow events.
490
-
491
- **Source:** `packages/project-monolith/src/automations/hailer/email-automation.ts`
492
- **AWS Secret:** `monolith-hailer-email-automation-{workspaceId}`
493
-
494
- **Endpoints:**
495
- | Endpoint | Method | Purpose |
496
- |----------|--------|---------|
497
- | `/api/email-automation/send-emails` | POST | Send emails to mailing list |
498
- | `/api/email-automation/form-response` | POST | Handle form responses (unsubscribe) |
499
-
500
- **Flow:**
501
- 1. Activity enters trigger phase in Hailer
502
- 2. Webhook fires → POST to `/email-automation/send-emails`
503
- 3. Monolith reads email content from activity fields
504
- 4. Fetches mailing list from referenced workflow
505
- 5. Filters out blocklisted and unsubscribed recipients
506
- 6. Sends emails in batches via Mailgun or Nodemailer
507
- 7. Creates batch result activities for tracking
508
-
509
- **Config Structure:**
510
- ```typescript
511
- {
512
- email: string; // Hailer login
513
- password: string;
514
- senderEmail: string; // From address
515
- actions: {
516
- [phaseId: string]: string; // Phase → action ("send email" | "unsubscribe")
517
- };
518
- processes: {
519
- blockList: { processId, phaseId }; // Global unsubscribe
520
- batchResults: { processId, phaseId }; // Result tracking
521
- unsubscribe?: { phaseId }; // Unsubscribe target
522
- };
523
- sendWith?: 'nodemailer'; // Default: Mailgun
524
- mailgun?: { url, apiKey };
525
- nodemailer?: { host, port, username, password };
526
- emailBatchSize: number; // Default: 250
527
- }
528
- ```
529
-
530
- **Required Workflow Fields:**
531
-
532
- | Workflow | Field Key | Type | Purpose |
533
- |----------|-----------|------|---------|
534
- | Email Campaign | `subject` | TEXT | Email subject |
535
- | Email Campaign | `html` | TEXT | HTML body |
536
- | Email Campaign | `plaintext` | TEXT | Plain text body |
537
- | Email Campaign | `process` | TEXT | Mailing list workflow ID |
538
- | Email Campaign | `phase` | TEXT | Mailing list phase ID |
539
- | Mailing List | `email` | TEXT | Recipient email |
540
- | Mailing List | `unsubscribed` | NUMBER | 1 = unsubscribed |
541
- | Block List | `email` | TEXT | Blocked email |
542
-
543
- **Personalization (Nodemailer only):** Use `%recipient.id%` in HTML - replaced with activity ID.
544
-
545
- **Setup Steps:**
546
-
547
- 1. **Create AWS Config** - Add `monolith-hailer-email-automation-{workspaceId}` to AWS Secrets Manager with config structure above.
548
-
549
- 2. **Create Required Workflows** in Hailer:
550
- - Email Campaign workflow (with trigger phase, fields: subject, html, plaintext, process, phase)
551
- - Mailing List workflow (fields: email, unsubscribed)
552
- - Block List workflow (field: email)
553
- - Batch Results workflow (for tracking)
554
-
555
- 3. **Configure Webhook**:
556
- - URL: `https://monolith-url/api/email-automation/send-emails`
557
- - Trigger: Activity enters "Ready to Send" phase
558
- - Phase ID must match key in `config.actions`
559
-
560
- ---
561
-
562
- ### Generic Activity CRUD
563
-
564
- Webhook-based endpoints for bulk creating/updating activities via function fields.
565
-
566
- **Source:** `packages/project-monolith/src/automations/generic-handle-activities.ts`
567
- **AWS Secret:** `monolith-generic-create-update`
568
-
569
- **Endpoints:**
570
- | Endpoint | Method | Purpose |
571
- |----------|--------|---------|
572
- | `/api/create-activities` | POST | Bulk create activities |
573
- | `/api/update-activities` | POST | Bulk update activities |
574
-
575
- **Flow:**
576
- 1. User changes Activity A in Hailer
577
- 2. Function field calculates JSON payload for createMany/updateMany
578
- 3. Webhook fires → POST to endpoint
579
- 4. Monolith reads the function field's JSON value
580
- 5. Executes `v3.activity.createMany` or `v3.activity.updateMany`
581
-
582
- **Critical Constraints:**
583
-
584
- - **Single-workflow per call:** `updateMany` operates on ONE workflow at a time. For cascading updates to multiple target workflows, create separate function fields and separate config entries (one per target).
585
- - **Config key uniqueness:** When one source workflow triggers updates to multiple target workflows, use pattern `"workflowId_suffix"` (e.g., `"68ca89ca..._tila"`, `"68ca89ca..._henkilo"`). Each config entry points to a different function field.
586
-
587
- **Config Structure:**
588
- ```typescript
589
- {
590
- "[workspaceId]": {
591
- "credentials": { email, password },
592
- "[workflowId_suffix]": {
593
- "fieldId": "field-id-containing-json-payload",
594
- "description": "Human-readable description"
595
- }
596
- }
597
- }
598
- ```
599
-
600
- **Field Payload Format:**
601
-
602
- For `/create-activities`:
603
- ```json
604
- [
605
- { "name": "New Activity", "fields": { "amount": 100 } }
606
- ]
607
- ```
608
-
609
- For `/update-activities`:
610
- ```json
611
- [
612
- { "_id": "activity-id", "fields": { "status": "completed" } }
613
- ]
614
- ```
615
-
616
- **CRITICAL: Double-Wrapped Array for Updates**
617
-
618
- The monolith generic handler spreads parsed JSON as arguments to `client.request()`. Function fields must output **double-wrapped array** `[[{_id, phaseId}, ...]]` so the first argument to `updateMany` is the array of updates.
619
-
620
- **Correct (double-wrapped):**
621
- ```javascript
622
- const updates = linkedFrom('tasks-workflow').map(task => ({
623
- _id: task._id,
624
- fields: { parentStatus: field('status') }
625
- }));
626
- return JSON.stringify([updates]); // ← double wrap
627
- ```
628
-
629
- **Wrong (single-wrapped):** Causes "must be an array" validation error
630
- ```javascript
631
- return JSON.stringify(updates); // ← single wrap - FAILS
632
- ```
633
-
634
- **Example function field (builds payload):**
635
- ```javascript
636
- const tasks = linkedFrom('tasks-workflow');
637
- const newStatus = field('status');
638
- return JSON.stringify([
639
- tasks.map(task => ({
640
- _id: task._id,
641
- fields: { parentStatus: newStatus }
642
- }))
643
- ]);
644
- ```
645
-
646
- **Setup Steps:**
647
-
648
- 1. **Create AWS Config** - Add workspace to `monolith-generic-create-update` in AWS Secrets Manager:
649
- ```json
650
- {
651
- "your-workspace-id": {
652
- "credentials": { "email": "bot@company.com", "password": "xxx" },
653
- "your-workflow-id": {
654
- "fieldId": "your-json-field-id",
655
- "description": "What this automation does"
656
- }
657
- }
658
- }
659
- ```
660
-
661
- 2. **Create Function Field** - In Hailer workflow, create a TEXT or function field that outputs the JSON payload (the `fieldId` in config must match this field).
662
-
663
- 3. **Configure Webhook** - In Hailer workflow settings:
664
- - URL: `https://monolith-url/api/create-activities` or `/api/update-activities`
665
- - Trigger: On activity update (or specific phase)
666
- - Method: POST
667
-
668
- **Use Cases:**
669
- - Create child records (order → line items)
670
- - Cascade updates (project status → task statuses)
671
- - Sync calculated data (pricing → quotes)
672
-
673
- ---
674
-
675
- ## Reference
676
-
677
- **Location:** `~/hailer-integration/packages/project-monolith/`
678
-
679
- **Run locally:**
680
- ```bash
681
- npm run start:local
682
- ```
683
-
684
- **Deploy:** Via GitLab CI/CD pipeline
685
-
686
- **Logs:** CloudWatch (production), console (local)