gufi-cli 0.1.49 → 0.1.51

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 (36) hide show
  1. package/dist/commands/docs.js +1 -5
  2. package/dist/lib/docs-resolver.d.ts +8 -0
  3. package/dist/lib/docs-resolver.js +27 -0
  4. package/dist/lib/security.js +5 -0
  5. package/dist/mcp.js +56 -43
  6. package/docs/dev-guide/1-01-architecture.md +358 -0
  7. package/docs/dev-guide/1-02-multi-tenant.md +415 -0
  8. package/docs/dev-guide/1-03-column-types.md +594 -0
  9. package/docs/dev-guide/1-04-json-config.md +442 -0
  10. package/docs/dev-guide/1-05-authentication.md +427 -0
  11. package/docs/dev-guide/2-01-api-reference.md +564 -0
  12. package/docs/dev-guide/2-02-automations.md +508 -0
  13. package/docs/dev-guide/2-03-gufi-cli.md +568 -0
  14. package/docs/dev-guide/2-04-realtime.md +401 -0
  15. package/docs/dev-guide/2-05-permissions.md +497 -0
  16. package/docs/dev-guide/2-06-integrations-overview.md +104 -0
  17. package/docs/dev-guide/2-07-stripe.md +173 -0
  18. package/docs/dev-guide/2-08-nayax.md +297 -0
  19. package/docs/dev-guide/2-09-ourvend.md +226 -0
  20. package/docs/dev-guide/2-10-tns.md +177 -0
  21. package/docs/dev-guide/2-11-custom-http.md +268 -0
  22. package/docs/dev-guide/3-01-custom-views.md +555 -0
  23. package/docs/dev-guide/3-02-webhooks-api.md +446 -0
  24. package/docs/mcp/00-overview.md +329 -0
  25. package/docs/mcp/01-architecture.md +226 -0
  26. package/docs/mcp/02-modules.md +285 -0
  27. package/docs/mcp/03-fields.md +357 -0
  28. package/docs/mcp/04-views.md +613 -0
  29. package/docs/mcp/05-automations.md +461 -0
  30. package/docs/mcp/06-api.md +531 -0
  31. package/docs/mcp/07-packages.md +246 -0
  32. package/docs/mcp/08-common-errors.md +284 -0
  33. package/docs/mcp/09-examples.md +453 -0
  34. package/docs/mcp/README.md +71 -0
  35. package/docs/mcp/tool-descriptions.json +64 -0
  36. package/package.json +3 -2
@@ -0,0 +1,508 @@
1
+ ---
2
+ id: automations
3
+ title: "Automations System"
4
+ description: "Automate business logic with scripts"
5
+ icon: Zap
6
+ category: dev
7
+ part: 2
8
+ ---
9
+
10
+ # Automations System
11
+
12
+ Automate business logic with scripts
13
+
14
+ ## Overview
15
+
16
+ Gufi's automation system lets you run JavaScript code in response to events. Automations can:
17
+
18
+ - Send emails and notifications
19
+ - Update records across entities
20
+ - Call external APIs
21
+ - Perform calculations
22
+ - Enforce business rules
23
+
24
+ ## Architecture
25
+
26
+ ### Components
27
+
28
+ ```
29
+ ┌─────────────────────────────────────────────────────────┐
30
+ │ Automation System │
31
+ ├─────────────────────────────────────────────────────────┤
32
+ │ │
33
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
34
+ │ │ Trigger │───▶│ Queue │───▶│ Worker │ │
35
+ │ │ Events │ │ (pg-boss) │ │ (Node) │ │
36
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
37
+ │ │ │ │
38
+ │ │ │ │
39
+ │ ▼ ▼ │
40
+ │ ┌─────────────────────────────────────────────────┐ │
41
+ │ │ core.automation_scripts │ │
42
+ │ │ (JavaScript code) │ │
43
+ │ └─────────────────────────────────────────────────┘ │
44
+ │ │ │
45
+ │ ▼ │
46
+ │ ┌─────────────────────────────────────────────────┐ │
47
+ │ │ core.automation_executions │ │
48
+ │ │ (execution history) │ │
49
+ │ └─────────────────────────────────────────────────┘ │
50
+ │ │
51
+ └───────────────────────────────────────────────────────┘
52
+ ```
53
+
54
+ ### Data Model
55
+
56
+ **core.automation_scripts** - The code
57
+
58
+ ```sql
59
+ id, company_id, name, code, created_at, updated_at
60
+ ```
61
+
62
+ **core.entities.automations** - Triggers per entity (JSONB column)
63
+
64
+ ```json
65
+ [
66
+ {
67
+ "trigger": "insert",
68
+ "function_name": "send_welcome_email",
69
+ "script_id": 42,
70
+ "label": "Send Welcome Email",
71
+ "enabled": true
72
+ }
73
+ ]
74
+ ```
75
+
76
+ **core.automation_meta** - Worker index (synced from entities.automations)
77
+
78
+ **core.automation_executions** - Execution log
79
+
80
+ ## Trigger Types
81
+
82
+ ### insert
83
+
84
+ Fires when a new record is created.
85
+
86
+ ```json
87
+ {
88
+ "trigger": "insert",
89
+ "function_name": "on_order_created"
90
+ }
91
+ ```
92
+
93
+ **Context available:**
94
+ - `row` - The new record
95
+ - `old_row` - null
96
+
97
+ ### update
98
+
99
+ Fires when a record is modified.
100
+
101
+ ```json
102
+ {
103
+ "trigger": "update",
104
+ "function_name": "on_status_change"
105
+ }
106
+ ```
107
+
108
+ **Context available:**
109
+ - `row` - The updated record
110
+ - `old_row` - Previous values
111
+
112
+ ### delete
113
+
114
+ Fires when a record is deleted.
115
+
116
+ ```json
117
+ {
118
+ "trigger": "delete",
119
+ "function_name": "on_customer_deleted"
120
+ }
121
+ ```
122
+
123
+ **Context available:**
124
+ - `row` - The deleted record
125
+ - `old_row` - Same as row
126
+
127
+ ### click
128
+
129
+ Manual button trigger (user clicks).
130
+
131
+ ```json
132
+ {
133
+ "trigger": "click",
134
+ "function_name": "send_invoice",
135
+ "label": "Send Invoice"
136
+ }
137
+ ```
138
+
139
+ Requires `label` for the UI button.
140
+
141
+ ### scheduled
142
+
143
+ Cron-based scheduling.
144
+
145
+ ```json
146
+ {
147
+ "trigger": "scheduled",
148
+ "function_name": "daily_report",
149
+ "cron_pattern": "0 8 * * *"
150
+ }
151
+ ```
152
+
153
+ Cron patterns: `minute hour day month weekday`
154
+
155
+ ## Writing Automation Scripts
156
+
157
+ ### Basic Structure
158
+
159
+ ```javascript
160
+ async function my_automation(gufi) {
161
+ // Your code here
162
+
163
+ return { success: true };
164
+ }
165
+ ```
166
+
167
+ ### Context Object
168
+
169
+ ```javascript
170
+ const {
171
+ company_id, // Company ID
172
+ module_id, // Module ID
173
+ entity_id, // Entity ID
174
+ table, // Physical table name
175
+ row, // Current record data
176
+ old_row, // Previous data (update only)
177
+ input, // Custom input (click trigger)
178
+ env // Environment variables
179
+ } = context;
180
+ ```
181
+
182
+ ### API Object
183
+
184
+ The `api` object provides:
185
+
186
+ #### Query Database
187
+
188
+ ```javascript
189
+ // IMPORTANT: api.query returns rows directly, NOT { rows }
190
+ const customers = await gufi.query(
191
+ "SELECT * FROM customers WHERE status = $1",
192
+ ["active"]
193
+ );
194
+
195
+ // Access results directly
196
+ if (customers.length === 0) {
197
+ throw new Error("No customers found");
198
+ }
199
+ const firstCustomer = customers[0];
200
+ ```
201
+
202
+ #### Send Email
203
+
204
+ ```javascript
205
+ await gufi.integrations.notifications.email({
206
+ to: "customer@example.com",
207
+ subject: "Order Confirmation",
208
+ body: "Your order has been confirmed.",
209
+ html: "<h1>Order Confirmed</h1><p>Thank you!</p>"
210
+ });
211
+ ```
212
+
213
+ #### HTTP Requests
214
+
215
+ ```javascript
216
+ const response = await gufi.http({
217
+ method: "POST",
218
+ url: "https://external-api.com/webhook",
219
+ headers: {
220
+ "Authorization": "Bearer " + context.env.API_KEY
221
+ },
222
+ body: { order_id: context.row.id }
223
+ });
224
+ ```
225
+
226
+ ### Logger Object
227
+
228
+ ```javascript
229
+ logger.info("Processing order", { orderId: row.id });
230
+ logger.warn("Low stock detected");
231
+ logger.error("Failed to send email", { error: err.message });
232
+ ```
233
+
234
+ Logs appear in execution history.
235
+
236
+ ## Complete Examples
237
+
238
+ ### Send Email on Order
239
+
240
+ ```javascript
241
+ async function send_order_confirmation(gufi) {
242
+ const { row, env } = gufi.context;
243
+
244
+ gufi.logger.info("Sending order confirmation", { orderId: row.id });
245
+
246
+ // Get customer details
247
+ const customers = await gufi.query(
248
+ "SELECT * FROM customers WHERE id = $1",
249
+ [row.customer_id]
250
+ );
251
+
252
+ if (customers.length === 0) {
253
+ throw new Error("Customer not found");
254
+ }
255
+
256
+ const customer = customers[0];
257
+
258
+ // Send email
259
+ await gufi.integrations.notifications.email({
260
+ to: customer.email,
261
+ subject: `Order #${row.id} Confirmed`,
262
+ html: `
263
+ <h1>Thank you for your order!</h1>
264
+ <p>Order #${row.id} has been confirmed.</p>
265
+ <p>Total: ${row.total.currency} ${row.total.amount}</p>
266
+ `
267
+ });
268
+
269
+ gufi.logger.info("Email sent successfully");
270
+
271
+ return { success: true, emailSent: true };
272
+ }
273
+ ```
274
+
275
+ ### Update Inventory on Sale
276
+
277
+ ```javascript
278
+ async function update_stock_on_sale(gufi) {
279
+ const { row } = gufi.context;
280
+
281
+ // Get order items
282
+ const items = await gufi.query(
283
+ "SELECT * FROM order_items WHERE order_id = $1",
284
+ [row.id]
285
+ );
286
+
287
+ // Update each product's stock
288
+ for (const item of items) {
289
+ await gufi.query(
290
+ "UPDATE products SET stock = stock - $1 WHERE id = $2",
291
+ [item.quantity, item.product_id]
292
+ );
293
+
294
+ gufi.logger.info("Updated stock", {
295
+ productId: item.product_id,
296
+ quantity: item.quantity
297
+ });
298
+ }
299
+
300
+ return { success: true, itemsUpdated: items.length };
301
+ }
302
+ ```
303
+
304
+ ### Call External API
305
+
306
+ ```javascript
307
+ async function sync_to_external_system(gufi) {
308
+ const { row, env } = gufi.context;
309
+
310
+ try {
311
+ const response = await gufi.http({
312
+ method: "POST",
313
+ url: env.EXTERNAL_API_URL + "/customers",
314
+ headers: {
315
+ "Authorization": "Bearer " + env.EXTERNAL_API_KEY,
316
+ "Content-Type": "application/json"
317
+ },
318
+ body: {
319
+ external_id: row.id,
320
+ name: row.name,
321
+ email: row.email
322
+ }
323
+ });
324
+
325
+ gufi.logger.info("Synced to external system", {
326
+ externalId: response.body.id
327
+ });
328
+
329
+ return { success: true, externalId: response.body.id };
330
+
331
+ } catch (error) {
332
+ gufi.logger.error("Sync failed", { error: error.message });
333
+ throw error;
334
+ }
335
+ }
336
+ ```
337
+
338
+ ### Scheduled Daily Report
339
+
340
+ ```javascript
341
+ async function daily_sales_report(gufi) {
342
+ const { env } = gufi.context;
343
+
344
+ // Get yesterday's sales
345
+ const sales = await gufi.query(`
346
+ SELECT
347
+ COUNT(*) as count,
348
+ SUM((total->>'amount')::numeric) as total
349
+ FROM orders
350
+ WHERE created_at >= CURRENT_DATE - INTERVAL '1 day'
351
+ AND created_at < CURRENT_DATE
352
+ `);
353
+
354
+ const report = sales[0];
355
+
356
+ // Send report email
357
+ await gufi.integrations.notifications.email({
358
+ to: env.REPORT_EMAIL,
359
+ subject: "Daily Sales Report",
360
+ html: `
361
+ <h1>Daily Sales Report</h1>
362
+ <p>Orders: ${report.count}</p>
363
+ <p>Total Revenue: €${report.total || 0}</p>
364
+ `
365
+ });
366
+
367
+ gufi.logger.info("Report sent", report);
368
+
369
+ return { success: true, ...report };
370
+ }
371
+ ```
372
+
373
+ ## Managing Automations
374
+
375
+ ### CLI Commands
376
+
377
+ ```bash
378
+ # List scripts
379
+ gufi automations -c 146
380
+
381
+ # View script code
382
+ gufi automation 42
383
+
384
+ # Edit script
385
+ gufi automation 42 --edit
386
+
387
+ # Create script
388
+ gufi automation:create send_email script.js -c 146
389
+
390
+ # View entity triggers
391
+ gufi entity:automations 4589
392
+
393
+ # Edit triggers
394
+ gufi entity:automations 4589 --edit
395
+
396
+ # View execution history
397
+ gufi automations:executions -c 146 --limit 50
398
+
399
+ # Filter by script
400
+ gufi automations:executions -c 146 --script send_email
401
+ ```
402
+
403
+ ### API Endpoints
404
+
405
+ ```http
406
+ # List scripts
407
+ GET /automations/scripts?company_id=146
408
+
409
+ # Create/update script
410
+ POST /automations/scripts
411
+ { "company_id": 146, "name": "my_script", "code": "..." }
412
+
413
+ # Get entity automations
414
+ GET /entities/:entityId/automations
415
+
416
+ # Update entity automations
417
+ PUT /entities/:entityId/automations
418
+ [{ "trigger": "insert", "function_name": "my_script" }]
419
+
420
+ # Run click automation
421
+ POST /automations/click
422
+ { "company_id": 146, "table_id": 4589, "function_name": "send_email", "input": {...} }
423
+ ```
424
+
425
+ ## Environment Variables
426
+
427
+ Store secrets and configuration:
428
+
429
+ ```bash
430
+ # Set variable
431
+ gufi env:set SMTP_HOST smtp.gmail.com
432
+ gufi env:set API_KEY sk_live_xxxxx
433
+
434
+ # Access in automation
435
+ const { env } = gufi.context;
436
+ const apiKey = env.API_KEY;
437
+ ```
438
+
439
+ ## Error Handling
440
+
441
+ ### Best Practices
442
+
443
+ ```javascript
444
+ async function robust_automation(gufi) {
445
+ try {
446
+ // Main logic
447
+ const result = await gufi.query("...");
448
+
449
+ if (!result.length) {
450
+ gufi.logger.warn("No data found, skipping");
451
+ return { success: true, skipped: true };
452
+ }
453
+
454
+ // Continue processing...
455
+
456
+ } catch (error) {
457
+ gufi.logger.error("Automation failed", {
458
+ error: error.message,
459
+ stack: error.stack
460
+ });
461
+
462
+ // Re-throw to mark execution as failed
463
+ throw error;
464
+ }
465
+ }
466
+ ```
467
+
468
+ ### Retry Configuration
469
+
470
+ Failed automations retry automatically:
471
+
472
+ | Attempt | Delay |
473
+ |---|---|
474
+ | 1 | Immediate |
475
+ | 2 | 1 minute |
476
+ | 3 | 5 minutes |
477
+ | 4 | 30 minutes |
478
+ | 5 | Failed permanently |
479
+
480
+ ## Security
481
+
482
+ ### Sandboxed Execution
483
+
484
+ Automations run in isolated-vm:
485
+
486
+ - No filesystem access
487
+ - No require/import
488
+ - Limited memory (128MB)
489
+ - Timeout (30 seconds)
490
+
491
+ ### What's Available
492
+
493
+ | Available | Not Available |
494
+ |---|---|
495
+ | api.query | require() |
496
+ | api.email | import |
497
+ | api.http | fs, path |
498
+ | logger | process |
499
+ | context | child_process |
500
+ | JSON, Date, Math | eval |
501
+
502
+ ### Data Access
503
+
504
+ Automations can only access:
505
+
506
+ - Data within the same company
507
+ - Tables the trigger is attached to
508
+ - Environment variables set for that company