botforje 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +381 -0
  3. package/dist/action/action.js +44 -0
  4. package/dist/action/catalog.js +40 -0
  5. package/dist/action/cooldown.js +60 -0
  6. package/dist/action/executor.js +23 -0
  7. package/dist/action/template.js +13 -0
  8. package/dist/action/types.js +2 -0
  9. package/dist/action/webhook.js +36 -0
  10. package/dist/actions/action.js +26 -0
  11. package/dist/actions/cooldown.js +66 -0
  12. package/dist/actions/request.js +40 -0
  13. package/dist/api/routes/auth.js +56 -0
  14. package/dist/api/routes/bots.js +51 -0
  15. package/dist/api/routes/config.js +24 -0
  16. package/dist/api/routes/health.js +15 -0
  17. package/dist/api/routes/index.js +14 -0
  18. package/dist/api/routes/messages.js +69 -0
  19. package/dist/api/routes/sessions.js +97 -0
  20. package/dist/api/routes/status.js +38 -0
  21. package/dist/api/server.js +153 -0
  22. package/dist/auth/service.js +102 -0
  23. package/dist/boot/fleet.js +207 -0
  24. package/dist/bot/bot.js +30 -0
  25. package/dist/bot/fleet.js +211 -0
  26. package/dist/bot/fuzzy.js +20 -0
  27. package/dist/bot/mapper.js +47 -0
  28. package/dist/bot/types.js +2 -0
  29. package/dist/bot/validation.js +38 -0
  30. package/dist/bot.js +31 -0
  31. package/dist/cli/create-bot.js +84 -0
  32. package/dist/cli.js +138 -0
  33. package/dist/commands/auth.js +200 -0
  34. package/dist/commands/create-bot.js +64 -0
  35. package/dist/commands/guide.js +563 -0
  36. package/dist/commands/lock.js +73 -0
  37. package/dist/commands/status.js +145 -0
  38. package/dist/commands/unlock.js +88 -0
  39. package/dist/commands/validate.js +19 -0
  40. package/dist/config/mapper.js +152 -0
  41. package/dist/config/schema.js +2 -0
  42. package/dist/config/validation.js +705 -0
  43. package/dist/config/watcher.js +145 -0
  44. package/dist/config/yaml.js +197 -0
  45. package/dist/fleet.js +247 -0
  46. package/dist/flow/executor.js +286 -0
  47. package/dist/flow/flow.js +2 -0
  48. package/dist/flow/mapper.js +72 -0
  49. package/dist/flow/state.js +115 -0
  50. package/dist/flow/types.js +2 -0
  51. package/dist/graph/executor.js +294 -0
  52. package/dist/graph/graph.js +2 -0
  53. package/dist/graph/state.js +118 -0
  54. package/dist/helpers/data.js +42 -0
  55. package/dist/helpers/fuzzy.js +30 -0
  56. package/dist/helpers/logger.js +89 -0
  57. package/dist/helpers/validation.js +38 -0
  58. package/dist/index.js +92 -0
  59. package/dist/messages/contracts.js +2 -0
  60. package/dist/messages/inbox.js +58 -0
  61. package/dist/messages/outbox.js +136 -0
  62. package/dist/services/auto-response.js +62 -0
  63. package/dist/services/cooldown.js +60 -0
  64. package/dist/services/fleet.js +207 -0
  65. package/dist/services/flow-executor.js +195 -0
  66. package/dist/services/flow-state.js +118 -0
  67. package/dist/services/inbox.js +50 -0
  68. package/dist/services/message-handler.js +78 -0
  69. package/dist/services/message-queue.js +138 -0
  70. package/dist/services/outbox.js +138 -0
  71. package/dist/services/session.js +118 -0
  72. package/dist/services/webhook.js +87 -0
  73. package/dist/utils/logger.js +89 -0
  74. package/dist/utils/webhook.js +36 -0
  75. package/dist/validation/validate.js +592 -0
  76. package/dist/whatsapp/client.js +293 -0
  77. package/dist/whatsapp/session.js +158 -0
  78. package/dist/whatsapp/types.js +109 -0
  79. package/dist/whatsapp/whatsapp.js +109 -0
  80. package/package.json +97 -0
  81. package/scripts/postinstall.js +45 -0
  82. package/scripts/preuninstall.js +48 -0
  83. package/scripts/setup-systemd.js +91 -0
  84. package/service/botforje.service.template +25 -0
@@ -0,0 +1,563 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runGuide = runGuide;
4
+ function runGuide() {
5
+ const guide = `# Botforje — AI Agent Configuration Guide
6
+
7
+ Framework for running config-driven WhatsApp bots. All behavior is defined in YAML — no code required.
8
+
9
+ ## Architecture
10
+
11
+ Three core concepts: **Actions** (what to do), **Graphs** (how to respond), **Bots** (who responds).
12
+
13
+ \`\`\`
14
+ Bot
15
+ └─ Graph (one per bot)
16
+ └─ Nodes
17
+ └─ Edges (transitions based on fuzzy-matched user input)
18
+ └─ Actions
19
+ \`\`\`
20
+
21
+ A bot owns exactly one graph. A graph represents the complete conversation for that bot. Nodes belong to their graph and are not reusable across graphs. Actions remain globally reusable.
22
+
23
+ \`\`\`
24
+ WhatsApp message
25
+ -> InboxService (filters: ignored senders, groups, self-messages)
26
+ -> GraphExecutor.handleMessage()
27
+ -> active session? -> match edge from current/visited nodes -> transition -> executeAction()
28
+ -> new sender? -> create session at root, execute root action, then re-resolve the original message
29
+ against the root edges (and any visited nodes)
30
+ -> executeAction()
31
+ -> resolve template variables ({{sender}}, {{message}}, etc.)
32
+ -> check cooldown
33
+ -> enqueue reply -> OutboxService -> WhatsAppChannel.send()
34
+ -> fire request -> sendRequestRequest()
35
+ \`\`\`
36
+
37
+ ---
38
+
39
+ ## Modular Configuration (Recommended)
40
+
41
+ Place config files in a directory structure. File names become IDs.
42
+
43
+ \`\`\`
44
+ ~/.config/botforje/
45
+ config.yml # global settings only
46
+ actions/ # each file = one action, filename = action ID
47
+ greet.yml
48
+ menu.yml
49
+ escalate.yml
50
+ graphs/ # each file = one graph, filename = graph ID
51
+ support.yml
52
+ ping-pong.yml
53
+ bots/ # each file = one bot, filename = bot ID
54
+ support-bot.yml
55
+ sales-bot.yml
56
+ \`\`\`
57
+
58
+ The loader merges directory files with any inline definitions in \`config.yml\`. Inline definitions take precedence.
59
+
60
+ ### config.yml (global settings only, actions/graphs/bots are separate files)
61
+
62
+ \`\`\`yaml
63
+ chromium_path: "/usr/bin/chromium"
64
+ port: 3000
65
+ address: "127.0.0.1"
66
+ log_level: "info"
67
+ default_timeout: 300
68
+ \`\`\`
69
+
70
+ ---
71
+
72
+ ## Schema Reference
73
+
74
+ ### Global Settings
75
+
76
+ | Field | Type | Default | Description |
77
+ |---|---|---|---|---|
78
+ | chromium_path | string | - | Path to Chromium/Chrome |
79
+ | port | number | 3000 | REST API port (if set, API runs on this port) |
80
+ | key | string | - | REST API key (optional — if set, all /api/* requests require \`Authorization: Bearer <key>\`) |
81
+ | address | string | "127.0.0.1" | Bind address (127.0.0.1 = localhost only, 0.0.0.0 = all interfaces) |
82
+ | log_level | string | "info" | info, debug, warn, error |
83
+ | default_timeout | number | 300 | Default graph session TTL (seconds) |
84
+
85
+ ### Actions
86
+
87
+ Each action has an ID (the key). Stored in \`actions/<id>.yml\`. An action is a **pipeline of ordered steps** with optional **guards** for rate limiting.
88
+
89
+ \`\`\`yaml
90
+ # Simple message step
91
+ steps:
92
+ - message:
93
+ body: "Hello!"
94
+
95
+ # Pipeline: message + request
96
+ steps:
97
+ - message:
98
+ body: "Processing..."
99
+ - request:
100
+ name: my-request
101
+ url: "https://api.example.com/hook"
102
+ method: POST
103
+ headers:
104
+ Authorization: "Bearer token"
105
+ timeout: 10000
106
+ retry: 3
107
+
108
+ # Cooldown guard — per-sender rate limiting
109
+ guards:
110
+ cooldown:
111
+ duration: 120
112
+ on_blocked:
113
+ - message:
114
+ body: "Please wait before requesting again."
115
+ steps:
116
+ - message:
117
+ body: "Processing request..."
118
+
119
+ # Request-only (no message)
120
+ steps:
121
+ - request:
122
+ url: "https://crm.example.com/leads"
123
+ method: POST
124
+ \`\`\`
125
+
126
+ **Step types:**
127
+
128
+ | Step | Field | Type | Required | Description |
129
+ |---|---|---|---|---|
130
+ | message | body | string | yes | Message body (supports templates) |
131
+ | message | to | string | no | Recipient override (default = sender) |
132
+ | request | url | string | yes | Target URL |
133
+ | request | method | string | "POST" | GET, POST, PUT, PATCH |
134
+ | request | headers | object | {} | HTTP headers |
135
+ | request | timeout | number | 5000 | Request timeout (ms) |
136
+ | request | retry | number | 3 | Retry count |
137
+ | location | latitude | number | yes | -90 to 90 |
138
+ | location | longitude | number | yes | -180 to 180 |
139
+ | location | name | string | no | Location name |
140
+ | location | address | string | no | Location address |
141
+
142
+ **Guards:**
143
+
144
+ | Guard | Field | Type | Required | Description |
145
+ |---|---|---|---|---|
146
+ | cooldown | duration | number | yes | Cooldown seconds per sender |
147
+ | cooldown | on_blocked | array | no | Pipeline to run when blocked |
148
+
149
+ An action must define \`steps\` (at least one) or a \`cooldown\` guard with \`on_blocked\`.
150
+
151
+ ### Graphs
152
+
153
+ Each graph has an ID (the key). Stored in \`graphs/<id>.yml\`.
154
+
155
+ \`\`\`yaml
156
+ root: menu
157
+ timeout: 300
158
+ fallback: invalid
159
+ nodes:
160
+ menu:
161
+ action: menu
162
+ edges:
163
+ - match: "1, hour, schedule, hours"
164
+ goto: hours
165
+ - match: "2, catalog, products"
166
+ goto: catalog
167
+ - match: "0, exit, bye"
168
+ goto: end
169
+ - goto: invalid
170
+ hours:
171
+ action: hours
172
+ edges:
173
+ - match: "menu, back"
174
+ goto: menu
175
+ - goto: invalid
176
+ end:
177
+ action: farewell
178
+ edges: []
179
+ \`\`\`
180
+
181
+ | Field | Type | Required | Description |
182
+ |---|---|---|---|
183
+ | root | string | yes | Starting node ID |
184
+ | timeout | number | global default | Session TTL (seconds) |
185
+ | fallback | string | - | Node for unmatched input |
186
+
187
+ Graphs have **no entry triggers**. When a sender has no active session, the bot automatically creates a session at \`root\`, executes the root action, and then re-applies the original message against the root's edges.
188
+
189
+ **Nodes:**
190
+
191
+ | Field | Type | Required | Description |
192
+ |---|---|---|---|
193
+ | action | string | yes | Action ID to execute |
194
+ | edges | array | - | Edge conditions for user input |
195
+
196
+ **Edges:**
197
+
198
+ | Field | Type | Description |
199
+ |---|---|---|
200
+ | match | string or string[] | Comma-separated fuzzy-matched phrases |
201
+ | fuzzy_threshold | number | Fuse.js threshold (0.3=strict, 0.6=moderate default, 0.9=loose) |
202
+ | goto | string | **Target node ID** |
203
+
204
+ - **Default edge**: omit \`match\` to catch unmatched input.
205
+ - **No edges (\`edges: []\`)**: session stays alive; the user can still navigate via edges from any previously visited node. Sessions only end via timeout.
206
+
207
+ ### Bots
208
+
209
+ Each bot has an ID (the key). Stored in \`bots/<id>.yml\`.
210
+
211
+ \`\`\`yaml
212
+ graph: support
213
+ settings:
214
+ queue_delay: 1500
215
+ ignore_groups: true
216
+ ignored_senders:
217
+ - "status@broadcast"
218
+ \`\`\`
219
+
220
+ | Field | Type | Default | Description |
221
+ |---|---|---|---|
222
+ | graph | string | - | Graph ID this bot owns |
223
+ | settings | object | - | Bot behavior |
224
+ | settings.queue_delay | number | 1000 | ms between outgoing messages |
225
+ | settings.ignore_groups | boolean | true | Ignore group messages |
226
+ | settings.ignored_senders | string[] | [] | Senders to ignore |
227
+
228
+
229
+ A bot references exactly one graph. There is no priority or list of graphs per bot.
230
+
231
+ ---
232
+
233
+ ## Template Variables
234
+
235
+ Action replies support variable interpolation:
236
+
237
+ | Variable | Description |
238
+ |---|---|
239
+ | \`{{senderPhone}}\` | Sender phone number |
240
+ | \`{{senderName}}\` | Sender pushname (if available) |
241
+ | \`{{message}}\` | Raw message body |
242
+ | \`{{bot.id}}\` | Bot ID |
243
+ | \`{{variables.remaining}}\` | Cooldown remaining seconds |
244
+
245
+ ---
246
+
247
+ ## !include Directive
248
+
249
+ Explicit file inclusion from within \`config.yml\`:
250
+
251
+ \`\`\`yaml
252
+ actions: !include actions/main.yml
253
+ graphs: !include graphs/main.yml
254
+
255
+ bots:
256
+ support: !include bots/support.yml
257
+ sales: !include bots/sales.yml
258
+ \`\`\`
259
+
260
+ ---
261
+
262
+ ## Best Practices
263
+
264
+ 1. **Use directory-based config** — one file per action/graph/bot instead of one giant \`config.yml\`. File name = ID.
265
+ 2. **One graph per bot** — keep the whole conversation for a single bot inside one graph.
266
+ 3. **Cooldown on human-escalation actions** — prevent spam-triggering manual agent handoffs.
267
+ 4. **Use edges: [] for end-of-conversation nodes** — sessions will still expire on timeout, but a user can still reach previously visited nodes.
268
+ 5. **Fallback node** — always configure a \`fallback\` to handle unexpected user input.
269
+ 6. **Fuzzy thresholds** — \`0.3\` for commands (strict), \`0.6\` default for conversation, \`0.9\` for loose matching.
270
+
271
+ ---
272
+
273
+ ## Complete Modular Example
274
+
275
+ \`~/.config/botforje/config.yml\`:
276
+
277
+ \`\`\`yaml
278
+ chromium_path: "/usr/bin/chromium"
279
+ log_level: "info"
280
+ \`\`\`
281
+
282
+ \`~/.config/botforje/actions/greet.yml\`:
283
+
284
+ \`\`\`yaml
285
+ steps:
286
+ - message:
287
+ body: "Hev {{senderName}}! How can I help you today?"
288
+ \`\`\`
289
+
290
+ \`~/.config/botforje/actions/menu.yml\`:
291
+
292
+ \`\`\`yaml
293
+ steps:
294
+ - message:
295
+ body: "Main menu:\\n1. Hours\\n2. Contact\\n0. Exit"
296
+ \`\`\`
297
+
298
+ \`~/.config/botforje/actions/hours.yml\`:
299
+
300
+ \`\`\`yaml
301
+ steps:
302
+ - message:
303
+ body: "Mon-Fri 9am-6pm"
304
+ \`\`\`
305
+
306
+ \`~/.config/botforje/actions/farewell.yml\`:
307
+
308
+ \`\`\`yaml
309
+ steps:
310
+ - message:
311
+ body: "Thanks, have a great day!"
312
+ \`\`\`
313
+
314
+ \`~/.config/botforje/actions/invalid.yml\`:
315
+
316
+ \`\`\`yaml
317
+ steps:
318
+ - message:
319
+ body: "Invalid option. Choose a number from the menu."
320
+ \`\`\`
321
+
322
+ \`~/.config/botforje/graphs/support.yml\`:
323
+
324
+ \`\`\`yaml
325
+ root: greet
326
+ timeout: 300
327
+ fallback: invalid
328
+ nodes:
329
+ greet:
330
+ action: greet
331
+ edges:
332
+ - match: "menu, help, continue"
333
+ goto: main_menu
334
+ - goto: invalid
335
+ main_menu:
336
+ action: menu
337
+ edges:
338
+ - match: "1, hours, schedule"
339
+ goto: hours
340
+ - match: "0, exit, bye"
341
+ goto: farewell
342
+ - goto: invalid
343
+ hours:
344
+ action: hours
345
+ edges:
346
+ - match: "menu, back"
347
+ goto: main_menu
348
+ - match: "0, exit"
349
+ goto: farewell
350
+ - goto: invalid
351
+ invalid:
352
+ action: invalid
353
+ edges:
354
+ - goto: main_menu
355
+ farewell:
356
+ action: farewell
357
+ edges: []
358
+ \`\`\`
359
+
360
+ \`~/.config/botforje/bots/support.yml\`:
361
+
362
+ \`\`\`yaml
363
+ graph: support
364
+ settings:
365
+ queue_delay: 1000
366
+ ignore_groups: true
367
+ ignored_senders:
368
+ - "status@broadcast"
369
+ \`\`\`
370
+
371
+ ---
372
+
373
+ ## REST API Reference
374
+
375
+ When \`port\` is set in config, the daemon starts an HTTP API bound to the configured \`address\` (default \`127.0.0.1\`).
376
+
377
+ ### Authentication
378
+
379
+ If \`key\` is configured in \`config.yml\`, every \`/api/*\` request (except \`/api/health\`) must include:
380
+
381
+ \`\`\`
382
+ Authorization: Bearer <key>
383
+ \`\`\`
384
+
385
+ If \`key\` is not set, the API is open (suitable for localhost-only or reverse-proxy-controlled setups).
386
+
387
+ ### Endpoints
388
+
389
+ #### GET /api/health
390
+
391
+ Liveness probe. Always responds without authentication.
392
+
393
+ \`\`\`json
394
+ { "status": "ok", "timestamp": "2025-01-01T00:00:00.000Z", "service": "Botforje API" }
395
+ \`\`\`
396
+
397
+ #### GET /api/status
398
+
399
+ Detailed status of all bots with session state.
400
+
401
+ \`\`\`bash
402
+ curl http://localhost:3000/api/status
403
+ \`\`\`
404
+
405
+ \`\`\`json
406
+ {
407
+ "running": true,
408
+ "bots": [
409
+ {
410
+ "id": "support-bot",
411
+ "graph": "faq-support",
412
+ "phone": "521234567890@c.us",
413
+ "session": { "state": "connected", "lastQR": false, "error": null }
414
+ }
415
+ ],
416
+ "total": 1
417
+ }
418
+ \`\`\`
419
+
420
+ #### GET /api/bots
421
+
422
+ List all bots with their configuration.
423
+
424
+ \`\`\`bash
425
+ curl http://localhost:3000/api/bots
426
+ \`\`\`
427
+
428
+ \`\`\`json
429
+ { "bots": [{ "id": "support-bot", "phone": "...", "graph": "faq-support", "settings": {} }], "total": 1 }
430
+ \`\`\`
431
+
432
+ #### GET /api/bots/:botId
433
+
434
+ Single bot info.
435
+
436
+ \`\`\`bash
437
+ curl http://localhost:3000/api/bots/support-bot
438
+ \`\`\`
439
+
440
+ #### POST /api/messages/send
441
+
442
+ Enqueue an outgoing WhatsApp message.
443
+
444
+ \`\`\`bash
445
+ curl -X POST http://localhost:3000/api/messages/send \\
446
+ -H 'Content-Type: application/json' \\
447
+ -d '{"botId": "support-bot", "to": "521234567890@c.us", "content": "Hello from the API!", "metadata": {"source": "webhook"}}'
448
+ \`\`\`
449
+
450
+ \`\`\`json
451
+ { "success": true, "messageId": "uuid", "botId": "support-bot", "queued": true }
452
+ \`\`\`
453
+
454
+ | Field | Type | Required | Description |
455
+ |---|---|---|---|
456
+ | botId | string | yes | Bot ID to send from |
457
+ | to | string | yes | Recipient WhatsApp ID (number@c.us) |
458
+ | content | string | yes | Message body (supports templates) |
459
+ | metadata | object | no | Arbitrary metadata attached to the message |
460
+
461
+ #### GET /api/messages/queue
462
+
463
+ All message queues status.
464
+
465
+ \`\`\`bash
466
+ curl http://localhost:3000/api/messages/queue
467
+ \`\`\`
468
+
469
+ #### GET /api/messages/queue/:botId
470
+
471
+ Per-bot queue status.
472
+
473
+ \`\`\`bash
474
+ curl http://localhost:3000/api/messages/queue/support-bot
475
+ \`\`\`
476
+
477
+ #### GET /api/sessions
478
+
479
+ List all registered WhatsApp sessions.
480
+
481
+ \`\`\`bash
482
+ curl http://localhost:3000/api/sessions
483
+ \`\`\`
484
+
485
+ #### POST /api/sessions/:id
486
+
487
+ Register/initiate a WhatsApp session. If the session already exists and is pending, returns current state. If already connected, returns 409.
488
+
489
+ \`\`\`bash
490
+ curl -X POST http://localhost:3000/api/sessions/support-bot
491
+ \`\`\`
492
+
493
+ \`\`\`json
494
+ { "success": true, "id": "support-bot", "session": { "state": "pending" } }
495
+ \`\`\`
496
+
497
+ #### GET /api/sessions/:id
498
+
499
+ Get session info by bot ID.
500
+
501
+ \`\`\`bash
502
+ curl http://localhost:3000/api/sessions/support-bot
503
+ \`\`\`
504
+
505
+ #### DELETE /api/sessions/:id
506
+
507
+ Remove/disconnect a WhatsApp session.
508
+
509
+ \`\`\`bash
510
+ curl -X DELETE http://localhost:3000/api/sessions/support-bot
511
+ \`\`\`
512
+
513
+ #### GET /api/sessions/:id/events
514
+
515
+ Server-Sent Events (SSE) stream for session lifecycle. Useful for QR authentication programmatically.
516
+
517
+ \`\`\`bash
518
+ curl -N http://localhost:3000/api/sessions/support-bot/events
519
+ \`\`\`
520
+
521
+ Events:
522
+
523
+ | event type | data | description |
524
+ |---|---|---|
525
+ | \`qr\` | \`{ "qr": "base64..." }\` | QR code for authentication |
526
+ | \`ready\` | \`{ "phone": "521234567890@c.us" }\` | Session connected |
527
+ | \`auth_failure\` | \`{ "error": "..." }\` | Authentication failed |
528
+ | \`disconnected\` | \`{}\` | Session disconnected |
529
+
530
+ #### POST /api/config/reload
531
+
532
+ Trigger a hot reload of the configuration from disk.
533
+
534
+ \`\`\`bash
535
+ curl -X POST http://localhost:3000/api/config/reload
536
+ \`\`\`
537
+
538
+ #### GET /api/config/status
539
+
540
+ Check if the config file watcher is active.
541
+
542
+ \`\`\`bash
543
+ curl http://localhost:3000/api/config/status
544
+ \`\`\`
545
+
546
+ \`\`\`json
547
+ { "watching": true }
548
+ \`\`\`
549
+
550
+ ---
551
+
552
+ ## Quick Start CLI Commands
553
+
554
+ \`\`\`
555
+ botforje daemon # Start the bot daemon
556
+ botforje status # Show bot session status
557
+ botforje auth <botId> # Authenticate a bot
558
+ botforje setup # Setup systemd service
559
+ botforje guide # Show this guide
560
+ \`\`\`
561
+ `;
562
+ console.log(guide);
563
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runLock = runLock;
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs"));
39
+ const service_1 = require("../auth/service");
40
+ const yaml_1 = require("../config/yaml");
41
+ const data_1 = require("../helpers/data");
42
+ async function runLock(key, configPath) {
43
+ try {
44
+ if (configPath)
45
+ (0, yaml_1.setConfigPath)(configPath);
46
+ await (0, yaml_1.loadConfig)(configPath);
47
+ const dataDir = (0, data_1.getDataDir)();
48
+ fs.mkdirSync(dataDir, { recursive: true });
49
+ const dbPath = path.join(dataDir, 'botforje.db');
50
+ const auth = new service_1.AuthService(dbPath);
51
+ if (auth.isLocked()) {
52
+ console.error('Botforje is already locked. Run `botforje unlock` first to reset the key.');
53
+ process.exit(1);
54
+ }
55
+ if (key && key.length < 32) {
56
+ console.error('Key must be at least 32 characters long.');
57
+ process.exit(1);
58
+ }
59
+ const secret = auth.lock(key);
60
+ console.log('\n' + '='.repeat(60));
61
+ console.log('BOTFORJE AUTH KEY');
62
+ console.log('='.repeat(60));
63
+ console.log(`\n${secret}\n`);
64
+ console.log('-'.repeat(60));
65
+ console.log('IMPORTANT: Save this key somewhere safe. It CANNOT be recovered.');
66
+ console.log('='.repeat(60) + '\n');
67
+ process.exit(0);
68
+ }
69
+ catch (error) {
70
+ console.error(`\nError: ${error.message || error}`);
71
+ process.exit(1);
72
+ }
73
+ }