@hailer/mcp 1.1.12 → 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.
- package/CHANGELOG.md +0 -7
- package/{.claude → dist}/CLAUDE.md +2 -2
- package/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +10 -1
- package/dist/bot/bot-config.js +64 -3
- package/dist/bot/bot-manager.d.ts +2 -0
- package/dist/bot/bot-manager.js +9 -2
- package/dist/bot/bot.d.ts +33 -0
- package/dist/bot/bot.js +461 -160
- package/dist/bot/services/message-classifier.js +17 -0
- package/dist/bot/services/permission-guard.d.ts +52 -0
- package/dist/bot/services/permission-guard.js +149 -0
- package/dist/bot/services/types.d.ts +5 -0
- package/dist/bot/services/typing-indicator.d.ts +6 -1
- package/dist/bot/services/typing-indicator.js +19 -3
- package/dist/cli.js +0 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/mcp/UserContextCache.d.ts +5 -0
- package/dist/mcp/UserContextCache.js +51 -19
- package/dist/mcp/hailer-clients.d.ts +19 -1
- package/dist/mcp/hailer-clients.js +158 -24
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +2 -0
- package/dist/mcp/tool-registry.d.ts +17 -4
- package/dist/mcp/tool-registry.js +37 -7
- package/dist/mcp/tools/activity.js +99 -7
- package/dist/mcp/tools/app-scaffold.js +304 -336
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
- package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
- package/dist/mcp/tools/company.d.ts +9 -0
- package/dist/mcp/tools/company.js +88 -0
- package/dist/mcp/tools/discussion.js +68 -0
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
- package/dist/mcp/tools/workflow-permissions.js +204 -0
- package/dist/mcp/tools/workflow.js +57 -18
- package/dist/mcp/utils/index.d.ts +2 -0
- package/dist/mcp/utils/index.js +12 -1
- package/dist/mcp/utils/role-utils.d.ts +74 -0
- package/dist/mcp/utils/role-utils.js +151 -0
- package/dist/mcp/utils/types.d.ts +43 -1
- package/dist/mcp/utils/types.js +14 -0
- package/dist/mcp/webhook-handler.d.ts +4 -0
- package/dist/mcp/webhook-handler.js +8 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -127
- package/dist/plugins/vipunen/client.d.ts +150 -0
- package/dist/plugins/vipunen/client.js +535 -0
- package/dist/plugins/vipunen/config/schema-config.json +19 -0
- package/dist/plugins/vipunen/config/schema-doc.json +22 -0
- package/dist/plugins/vipunen/index.d.ts +41 -0
- package/dist/plugins/vipunen/index.js +88 -0
- package/dist/plugins/vipunen/tools.d.ts +26 -0
- package/dist/plugins/vipunen/tools.js +501 -0
- package/dist/stdio-server.d.ts +14 -0
- package/dist/stdio-server.js +101 -0
- package/package.json +2 -1
- package/.claude/agents/agent-ada-skill-builder.md +0 -94
- package/.claude/agents/agent-alejandro-function-fields.md +0 -342
- package/.claude/agents/agent-bjorn-config-audit.md +0 -103
- package/.claude/agents/agent-builder-agent-creator.md +0 -130
- package/.claude/agents/agent-code-simplifier.md +0 -53
- package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
- package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
- package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
- package/.claude/agents/agent-helga-workflow-config.md +0 -204
- package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
- package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
- package/.claude/agents/agent-ivan-monolith.md +0 -154
- package/.claude/agents/agent-kenji-data-reader.md +0 -86
- package/.claude/agents/agent-lars-code-inspector.md +0 -102
- package/.claude/agents/agent-marco-mockup-builder.md +0 -110
- package/.claude/agents/agent-marcus-api-documenter.md +0 -323
- package/.claude/agents/agent-marketplace-publisher.md +0 -280
- package/.claude/agents/agent-marketplace-reviewer.md +0 -309
- package/.claude/agents/agent-permissions-handler.md +0 -208
- package/.claude/agents/agent-simple-writer.md +0 -48
- package/.claude/agents/agent-svetlana-code-review.md +0 -171
- package/.claude/agents/agent-tanya-test-runner.md +0 -333
- package/.claude/agents/agent-ui-designer.md +0 -100
- package/.claude/agents/agent-viktor-sql-insights.md +0 -212
- package/.claude/agents/agent-web-search.md +0 -55
- package/.claude/agents/agent-yevgeni-discussions.md +0 -45
- package/.claude/agents/agent-zara-zapier.md +0 -159
- package/.claude/commands/app-squad.md +0 -135
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/autoplan.md +0 -563
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/debug-squad.md +0 -303
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/handoff.md +0 -137
- package/.claude/commands/health.md +0 -49
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -151
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -93
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/learn-auto.md +0 -120
- package/.claude/commands/learn.md +0 -120
- package/.claude/commands/mcp-list.md +0 -27
- package/.claude/commands/onboard-squad.md +0 -140
- package/.claude/commands/plan-workspace.md +0 -732
- package/.claude/commands/prd.md +0 -130
- package/.claude/commands/project-status.md +0 -82
- package/.claude/commands/publish.md +0 -138
- package/.claude/commands/recap.md +0 -69
- package/.claude/commands/restore.md +0 -64
- package/.claude/commands/review-squad.md +0 -152
- package/.claude/commands/save.md +0 -24
- package/.claude/commands/stats.md +0 -19
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/commands/ws-pull.md +0 -44
- package/.claude/hooks/_shared-memory.cjs +0 -305
- package/.claude/hooks/_utils.cjs +0 -108
- package/.claude/hooks/agent-failure-detector.cjs +0 -383
- package/.claude/hooks/agent-usage-logger.cjs +0 -204
- package/.claude/hooks/app-edit-guard.cjs +0 -494
- package/.claude/hooks/auto-learn.cjs +0 -304
- package/.claude/hooks/bash-guard.cjs +0 -272
- package/.claude/hooks/builder-mode-manager.cjs +0 -354
- package/.claude/hooks/bulk-activity-guard.cjs +0 -271
- package/.claude/hooks/context-watchdog.cjs +0 -230
- package/.claude/hooks/delegation-reminder.cjs +0 -465
- package/.claude/hooks/design-system-lint.cjs +0 -271
- package/.claude/hooks/post-scaffold-hook.cjs +0 -181
- package/.claude/hooks/prompt-guard.cjs +0 -354
- package/.claude/hooks/publish-template-guard.cjs +0 -147
- package/.claude/hooks/session-start.cjs +0 -35
- package/.claude/hooks/shared-memory-writer.cjs +0 -147
- package/.claude/hooks/skill-injector.cjs +0 -140
- package/.claude/hooks/skill-usage-logger.cjs +0 -258
- package/.claude/hooks/src-edit-guard.cjs +0 -240
- package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
- package/.claude/settings.json +0 -257
- package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
- package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
- package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
- package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
- package/.claude/skills/agent-structure/SKILL.md +0 -98
- package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
- package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
- package/.claude/skills/delegation-routing/SKILL.md +0 -202
- package/.claude/skills/frontend-design/SKILL.md +0 -254
- package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
- package/.claude/skills/hailer-api-client/SKILL.md +0 -518
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
- package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
- package/.claude/skills/hailer-design-system/SKILL.md +0 -235
- package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
- package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
- package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
- package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
- package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
- package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
- package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
- package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
- package/.claude/skills/integration-patterns/SKILL.md +0 -421
- package/.claude/skills/json-only-output/SKILL.md +0 -72
- package/.claude/skills/lsp-setup/SKILL.md +0 -160
- package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
- package/.claude/skills/optional-parameters/SKILL.md +0 -72
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
- package/.claude/skills/testing-patterns/SKILL.md +0 -630
- package/.claude/skills/tool-builder/SKILL.md +0 -250
- package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
- package/.claude/skills/tool-response-verification/SKILL.md +0 -92
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
- package/.mcp.json +0 -13
- package/.opencode/agent/agent-ada-skill-builder.md +0 -35
- package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
- package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
- package/.opencode/agent/agent-builder-agent-creator.md +0 -39
- package/.opencode/agent/agent-code-simplifier.md +0 -31
- package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
- package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
- package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
- package/.opencode/agent/agent-helga-workflow-config.md +0 -203
- package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
- package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
- package/.opencode/agent/agent-ivan-monolith.md +0 -46
- package/.opencode/agent/agent-kenji-data-reader.md +0 -53
- package/.opencode/agent/agent-lars-code-inspector.md +0 -28
- package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
- package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
- package/.opencode/agent/agent-marketplace-publisher.md +0 -44
- package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
- package/.opencode/agent/agent-permissions-handler.md +0 -50
- package/.opencode/agent/agent-simple-writer.md +0 -45
- package/.opencode/agent/agent-svetlana-code-review.md +0 -39
- package/.opencode/agent/agent-tanya-test-runner.md +0 -57
- package/.opencode/agent/agent-ui-designer.md +0 -56
- package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
- package/.opencode/agent/agent-web-search.md +0 -42
- package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
- package/.opencode/agent/agent-zara-zapier.md +0 -53
- package/.opencode/commands/app-squad.md +0 -135
- package/.opencode/commands/audit-squad.md +0 -158
- package/.opencode/commands/autoplan.md +0 -563
- package/.opencode/commands/cleanup-squad.md +0 -98
- package/.opencode/commands/config-squad.md +0 -106
- package/.opencode/commands/crud-squad.md +0 -87
- package/.opencode/commands/data-squad.md +0 -97
- package/.opencode/commands/debug-squad.md +0 -303
- package/.opencode/commands/doc-squad.md +0 -65
- package/.opencode/commands/handoff.md +0 -137
- package/.opencode/commands/health.md +0 -49
- package/.opencode/commands/help-agents.md +0 -151
- package/.opencode/commands/help-commands.md +0 -32
- package/.opencode/commands/help-faq.md +0 -29
- package/.opencode/commands/help-plugins.md +0 -28
- package/.opencode/commands/help-skills.md +0 -7
- package/.opencode/commands/help-tools.md +0 -40
- package/.opencode/commands/help.md +0 -28
- package/.opencode/commands/hotfix-squad.md +0 -112
- package/.opencode/commands/integration-squad.md +0 -82
- package/.opencode/commands/janitor-squad.md +0 -167
- package/.opencode/commands/learn-auto.md +0 -120
- package/.opencode/commands/learn.md +0 -120
- package/.opencode/commands/mcp-list.md +0 -27
- package/.opencode/commands/onboard-squad.md +0 -140
- package/.opencode/commands/plan-workspace.md +0 -732
- package/.opencode/commands/prd.md +0 -131
- package/.opencode/commands/project-status.md +0 -82
- package/.opencode/commands/publish.md +0 -138
- package/.opencode/commands/recap.md +0 -69
- package/.opencode/commands/restore.md +0 -64
- package/.opencode/commands/review-squad.md +0 -152
- package/.opencode/commands/save.md +0 -24
- package/.opencode/commands/stats.md +0 -19
- package/.opencode/commands/swarm.md +0 -210
- package/.opencode/commands/tool-builder.md +0 -39
- package/.opencode/commands/ws-pull.md +0 -44
- package/.opencode/opencode.json +0 -28
- package/SESSION-HANDOFF.md +0 -68
- package/inbox/2026-03-04-bot-config-patterns.md +0 -24
- package/scripts/postinstall.cjs +0 -64
- 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)
|