@thinksoftai/cli 1.5.0 → 1.6.0

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.
@@ -0,0 +1,762 @@
1
+ "use strict";
2
+ /**
3
+ * Rules command - CRUD operations for agent business rules
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.list = list;
43
+ exports.show = show;
44
+ exports.create = create;
45
+ exports.update = update;
46
+ exports.deleteRule = deleteRule;
47
+ exports.enable = enable;
48
+ exports.disable = disable;
49
+ const ora_1 = __importDefault(require("ora"));
50
+ const inquirer_1 = __importDefault(require("inquirer"));
51
+ const chalk_1 = __importDefault(require("chalk"));
52
+ const api = __importStar(require("../utils/api"));
53
+ const config = __importStar(require("../utils/config"));
54
+ const logger = __importStar(require("../utils/logger"));
55
+ // Trigger options
56
+ const TRIGGERS = [
57
+ { name: 'Before Create - Runs before a new record is saved', value: 'before_create' },
58
+ { name: 'After Create - Runs after a new record is saved', value: 'after_create' },
59
+ { name: 'Before Update - Runs before a record is updated', value: 'before_update' },
60
+ { name: 'After Update - Runs after a record is updated', value: 'after_update' }
61
+ ];
62
+ // Action types
63
+ const ACTION_TYPES = [
64
+ { name: 'Set Field - Set a field to a specific value', value: 'set_field' },
65
+ { name: 'Calculate - Compute a field using Liquid formula', value: 'calculate' },
66
+ { name: 'Reject - Block the operation with an error', value: 'reject' },
67
+ { name: 'Validate - Conditional rejection', value: 'validate' },
68
+ { name: 'Lookup & Set - Query another table and set field', value: 'lookup_and_set' }
69
+ ];
70
+ // Lookup selection strategies
71
+ const LOOKUP_SELECT_OPTIONS = [
72
+ { name: 'First - Return first matching record', value: 'first' },
73
+ { name: 'Last - Return last matching record', value: 'last' },
74
+ { name: 'Random - Return random match', value: 'random' },
75
+ { name: 'Min - Return record with minimum value', value: 'min' },
76
+ { name: 'Max - Return record with maximum value', value: 'max' }
77
+ ];
78
+ // No match handling options
79
+ const NO_MATCH_OPTIONS = [
80
+ { name: 'Skip - Leave field empty', value: 'skip' },
81
+ { name: 'Reject - Block operation with error', value: 'reject' },
82
+ { name: 'Default - Use a default value', value: 'default' }
83
+ ];
84
+ /**
85
+ * Check authentication
86
+ */
87
+ function checkAuth() {
88
+ if (!config.isLoggedIn()) {
89
+ logger.error('Not logged in');
90
+ logger.info('Use "thinksoft login" to authenticate');
91
+ return false;
92
+ }
93
+ return true;
94
+ }
95
+ /**
96
+ * Get app ID from argument or project config
97
+ */
98
+ function getAppId(appId) {
99
+ return appId || config.getProjectConfig()?.appId;
100
+ }
101
+ /**
102
+ * List all rules for an agent
103
+ */
104
+ async function list(agentSlug, appId) {
105
+ if (!checkAuth())
106
+ return;
107
+ const targetAppId = getAppId(appId);
108
+ if (!targetAppId) {
109
+ logger.error('App ID is required');
110
+ logger.info('Usage: thinksoft rules <agentSlug> [appId]');
111
+ logger.info('Or run from a directory with thinksoft.json');
112
+ return;
113
+ }
114
+ if (!agentSlug) {
115
+ logger.error('Agent slug is required');
116
+ logger.info('Usage: thinksoft rules <agentSlug> [appId]');
117
+ return;
118
+ }
119
+ logger.header('Rules - List');
120
+ logger.keyValue('App ID', targetAppId);
121
+ logger.keyValue('Agent', agentSlug);
122
+ const spinner = (0, ora_1.default)('Fetching rules...').start();
123
+ const result = await api.getRules(targetAppId, agentSlug);
124
+ if (result.error) {
125
+ spinner.fail('Failed to fetch rules');
126
+ handleError(result.error);
127
+ return;
128
+ }
129
+ spinner.succeed('Rules retrieved');
130
+ const rules = result.data || result.rules || [];
131
+ if (rules.length === 0) {
132
+ logger.newLine();
133
+ logger.info('No rules found for this agent');
134
+ logger.info('Use "thinksoft rules:create <agentSlug>" to create a rule');
135
+ return;
136
+ }
137
+ logger.newLine();
138
+ logger.log(`Found ${rules.length} rule(s):`);
139
+ logger.newLine();
140
+ rules.forEach((rule, index) => {
141
+ const status = rule.enabled !== false ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
142
+ const disabledText = rule.enabled === false ? chalk_1.default.gray(' [DISABLED]') : '';
143
+ logger.log(` ${index + 1}. ${status} ${rule.name} (${rule.id})${disabledText}`);
144
+ logger.log(` Trigger: ${rule.trigger} | Table: ${rule.table || '*'} | Priority: ${rule.priority || 10}`);
145
+ const actionTypes = (rule.actions || []).map((a) => a.type).join(', ');
146
+ logger.log(` Actions: ${actionTypes || 'none'}`);
147
+ if (rule.description) {
148
+ logger.log(chalk_1.default.gray(` ${rule.description}`));
149
+ }
150
+ logger.newLine();
151
+ });
152
+ logger.info('Tip: Use "thinksoft rules:show <agentSlug> <ruleId>" for details');
153
+ }
154
+ /**
155
+ * Show details for a specific rule
156
+ */
157
+ async function show(agentSlug, ruleId, appId) {
158
+ if (!checkAuth())
159
+ return;
160
+ const targetAppId = getAppId(appId);
161
+ if (!targetAppId || !agentSlug || !ruleId) {
162
+ logger.error('App ID, agent slug, and rule ID are required');
163
+ logger.info('Usage: thinksoft rules:show <agentSlug> <ruleId> [appId]');
164
+ return;
165
+ }
166
+ logger.header('Rule Details');
167
+ logger.keyValue('App ID', targetAppId);
168
+ logger.keyValue('Agent', agentSlug);
169
+ logger.keyValue('Rule ID', ruleId);
170
+ const spinner = (0, ora_1.default)('Fetching rule...').start();
171
+ const result = await api.getRule(targetAppId, agentSlug, ruleId);
172
+ if (result.error) {
173
+ spinner.fail('Failed to fetch rule');
174
+ handleError(result.error);
175
+ return;
176
+ }
177
+ spinner.succeed('Rule retrieved');
178
+ const rule = result.data || result.rule || result;
179
+ logger.newLine();
180
+ logger.log(chalk_1.default.bold(`Name: ${rule.name}`));
181
+ if (rule.description) {
182
+ logger.log(`Description: ${rule.description}`);
183
+ }
184
+ logger.log(`Status: ${rule.enabled !== false ? chalk_1.default.green('Enabled') : chalk_1.default.red('Disabled')}`);
185
+ logger.log(`Trigger: ${rule.trigger}`);
186
+ logger.log(`Table: ${rule.table || '* (all tables)'}`);
187
+ logger.log(`Priority: ${rule.priority || 10}`);
188
+ logger.newLine();
189
+ logger.log(chalk_1.default.bold('Condition:'));
190
+ logger.log(chalk_1.default.cyan(` ${rule.condition || 'true'}`));
191
+ const actions = rule.actions || [];
192
+ logger.newLine();
193
+ logger.log(chalk_1.default.bold(`Actions (${actions.length}):`));
194
+ if (actions.length === 0) {
195
+ logger.log(' No actions defined');
196
+ }
197
+ else {
198
+ actions.forEach((action, index) => {
199
+ logger.newLine();
200
+ logger.log(` ${index + 1}. ${chalk_1.default.yellow(action.type)}`);
201
+ switch (action.type) {
202
+ case 'set_field':
203
+ logger.log(` Field: ${action.field}`);
204
+ logger.log(` Value: ${action.value}`);
205
+ break;
206
+ case 'calculate':
207
+ logger.log(` Field: ${action.field}`);
208
+ logger.log(` Formula: ${action.value}`);
209
+ break;
210
+ case 'reject':
211
+ logger.log(` Message: ${action.message}`);
212
+ break;
213
+ case 'validate':
214
+ logger.log(` Condition: ${action.condition}`);
215
+ logger.log(` Message: ${action.message}`);
216
+ break;
217
+ case 'lookup_and_set':
218
+ logger.log(` Field: ${action.field}`);
219
+ logger.log(` Lookup Table: ${action.lookupTable}`);
220
+ logger.log(` Select By: ${action.selectBy || 'first'}`);
221
+ if (action.selectField) {
222
+ logger.log(` Select Field: ${action.selectField}`);
223
+ }
224
+ logger.log(` Return Field: ${action.returnField}`);
225
+ logger.log(` No Match: ${action.noMatchAction || 'skip'}`);
226
+ if (action.lookupFilter) {
227
+ logger.log(` Filter: ${JSON.stringify(action.lookupFilter)}`);
228
+ }
229
+ break;
230
+ }
231
+ });
232
+ }
233
+ logger.newLine();
234
+ }
235
+ /**
236
+ * Create a new rule
237
+ */
238
+ async function create(agentSlug, appId, options = {}) {
239
+ if (!checkAuth())
240
+ return;
241
+ const targetAppId = getAppId(appId);
242
+ if (!targetAppId || !agentSlug) {
243
+ logger.error('App ID and agent slug are required');
244
+ logger.info('Usage: thinksoft rules:create <agentSlug> [appId]');
245
+ return;
246
+ }
247
+ let ruleData;
248
+ // If --data provided, use it (non-interactive)
249
+ if (options.data) {
250
+ try {
251
+ ruleData = JSON.parse(options.data);
252
+ }
253
+ catch {
254
+ logger.error('Invalid JSON data');
255
+ logger.info('Use valid JSON: --data \'{"name":"Rule Name", ...}\'');
256
+ return;
257
+ }
258
+ }
259
+ else {
260
+ // Interactive mode
261
+ ruleData = await promptForRule(targetAppId);
262
+ }
263
+ logger.header('Rules - Create');
264
+ logger.keyValue('App ID', targetAppId);
265
+ logger.keyValue('Agent', agentSlug);
266
+ logger.keyValue('Rule Name', ruleData.name);
267
+ const spinner = (0, ora_1.default)('Creating rule...').start();
268
+ const result = await api.createRule(targetAppId, agentSlug, ruleData);
269
+ if (result.error) {
270
+ spinner.fail('Failed to create rule');
271
+ handleError(result.error);
272
+ return;
273
+ }
274
+ spinner.succeed(`Rule "${ruleData.name}" created`);
275
+ const createdRule = result.data || result.rule || result;
276
+ if (createdRule.id) {
277
+ logger.newLine();
278
+ logger.log(`Rule ID: ${createdRule.id}`);
279
+ }
280
+ }
281
+ /**
282
+ * Update an existing rule
283
+ */
284
+ async function update(agentSlug, ruleId, appId, options = {}) {
285
+ if (!checkAuth())
286
+ return;
287
+ const targetAppId = getAppId(appId);
288
+ if (!targetAppId || !agentSlug || !ruleId) {
289
+ logger.error('App ID, agent slug, and rule ID are required');
290
+ logger.info('Usage: thinksoft rules:update <agentSlug> <ruleId> [appId]');
291
+ return;
292
+ }
293
+ let updates;
294
+ // If --data provided, use it
295
+ if (options.data) {
296
+ try {
297
+ updates = JSON.parse(options.data);
298
+ }
299
+ catch {
300
+ logger.error('Invalid JSON data');
301
+ return;
302
+ }
303
+ }
304
+ else if (options.enabled !== undefined || options.priority) {
305
+ // Quick update for enabled/priority
306
+ updates = {};
307
+ if (options.enabled !== undefined) {
308
+ updates.enabled = options.enabled;
309
+ }
310
+ if (options.priority) {
311
+ updates.priority = parseInt(options.priority);
312
+ }
313
+ }
314
+ else {
315
+ // Interactive mode - fetch current rule first
316
+ const spinner = (0, ora_1.default)('Fetching current rule...').start();
317
+ const currentResult = await api.getRule(targetAppId, agentSlug, ruleId);
318
+ spinner.stop();
319
+ if (currentResult.error) {
320
+ logger.error('Could not fetch current rule');
321
+ handleError(currentResult.error);
322
+ return;
323
+ }
324
+ const currentRule = currentResult.data || currentResult.rule || currentResult;
325
+ updates = await promptForRuleUpdate(targetAppId, currentRule);
326
+ }
327
+ logger.header('Rules - Update');
328
+ logger.keyValue('App ID', targetAppId);
329
+ logger.keyValue('Agent', agentSlug);
330
+ logger.keyValue('Rule ID', ruleId);
331
+ const spinner = (0, ora_1.default)('Updating rule...').start();
332
+ const result = await api.updateRule(targetAppId, agentSlug, ruleId, updates);
333
+ if (result.error) {
334
+ spinner.fail('Failed to update rule');
335
+ handleError(result.error);
336
+ return;
337
+ }
338
+ spinner.succeed('Rule updated');
339
+ }
340
+ /**
341
+ * Delete a rule
342
+ */
343
+ async function deleteRule(agentSlug, ruleId, appId, options = {}) {
344
+ if (!checkAuth())
345
+ return;
346
+ const targetAppId = getAppId(appId);
347
+ if (!targetAppId || !agentSlug || !ruleId) {
348
+ logger.error('App ID, agent slug, and rule ID are required');
349
+ logger.info('Usage: thinksoft rules:delete <agentSlug> <ruleId> [appId]');
350
+ return;
351
+ }
352
+ // Confirm deletion
353
+ if (!options.yes) {
354
+ const { confirm } = await inquirer_1.default.prompt([{
355
+ type: 'confirm',
356
+ name: 'confirm',
357
+ message: `Delete rule "${ruleId}"? This cannot be undone.`,
358
+ default: false
359
+ }]);
360
+ if (!confirm) {
361
+ logger.info('Cancelled');
362
+ return;
363
+ }
364
+ }
365
+ logger.header('Rules - Delete');
366
+ logger.keyValue('App ID', targetAppId);
367
+ logger.keyValue('Agent', agentSlug);
368
+ logger.keyValue('Rule ID', ruleId);
369
+ const spinner = (0, ora_1.default)('Deleting rule...').start();
370
+ const result = await api.deleteRule(targetAppId, agentSlug, ruleId);
371
+ if (result.error) {
372
+ spinner.fail('Failed to delete rule');
373
+ handleError(result.error);
374
+ return;
375
+ }
376
+ spinner.succeed('Rule deleted');
377
+ }
378
+ /**
379
+ * Enable a rule
380
+ */
381
+ async function enable(agentSlug, ruleId, appId) {
382
+ if (!checkAuth())
383
+ return;
384
+ const targetAppId = getAppId(appId);
385
+ if (!targetAppId || !agentSlug || !ruleId) {
386
+ logger.error('App ID, agent slug, and rule ID are required');
387
+ logger.info('Usage: thinksoft rules:enable <agentSlug> <ruleId> [appId]');
388
+ return;
389
+ }
390
+ const spinner = (0, ora_1.default)('Enabling rule...').start();
391
+ const result = await api.updateRule(targetAppId, agentSlug, ruleId, { enabled: true });
392
+ if (result.error) {
393
+ spinner.fail('Failed to enable rule');
394
+ handleError(result.error);
395
+ return;
396
+ }
397
+ spinner.succeed(`Rule "${ruleId}" enabled`);
398
+ }
399
+ /**
400
+ * Disable a rule
401
+ */
402
+ async function disable(agentSlug, ruleId, appId) {
403
+ if (!checkAuth())
404
+ return;
405
+ const targetAppId = getAppId(appId);
406
+ if (!targetAppId || !agentSlug || !ruleId) {
407
+ logger.error('App ID, agent slug, and rule ID are required');
408
+ logger.info('Usage: thinksoft rules:disable <agentSlug> <ruleId> [appId]');
409
+ return;
410
+ }
411
+ const spinner = (0, ora_1.default)('Disabling rule...').start();
412
+ const result = await api.updateRule(targetAppId, agentSlug, ruleId, { enabled: false });
413
+ if (result.error) {
414
+ spinner.fail('Failed to disable rule');
415
+ handleError(result.error);
416
+ return;
417
+ }
418
+ spinner.succeed(`Rule "${ruleId}" disabled`);
419
+ }
420
+ /**
421
+ * Interactive prompt for creating a new rule
422
+ */
423
+ async function promptForRule(appId) {
424
+ // Get available tables for selection
425
+ const schemaResult = await api.getSchema(appId);
426
+ const tables = schemaResult.data || [];
427
+ const tableChoices = [
428
+ { name: '* (All tables)', value: '*' },
429
+ ...tables.map((t) => ({ name: `${t.icon || ''} ${t.name} (${t.slug})`, value: t.slug }))
430
+ ];
431
+ // Basic info
432
+ const basicInfo = await inquirer_1.default.prompt([
433
+ {
434
+ type: 'input',
435
+ name: 'name',
436
+ message: 'Rule name:',
437
+ validate: (input) => input.trim() ? true : 'Rule name is required'
438
+ },
439
+ {
440
+ type: 'input',
441
+ name: 'description',
442
+ message: 'Description (optional):'
443
+ },
444
+ {
445
+ type: 'list',
446
+ name: 'trigger',
447
+ message: 'When should this rule run?',
448
+ choices: TRIGGERS
449
+ },
450
+ {
451
+ type: 'list',
452
+ name: 'table',
453
+ message: 'Which table does this rule apply to?',
454
+ choices: tableChoices
455
+ },
456
+ {
457
+ type: 'input',
458
+ name: 'condition',
459
+ message: 'Condition (Liquid template, leave empty for always):',
460
+ default: 'true'
461
+ },
462
+ {
463
+ type: 'number',
464
+ name: 'priority',
465
+ message: 'Priority (lower runs first):',
466
+ default: 10
467
+ }
468
+ ]);
469
+ // Actions
470
+ const actions = [];
471
+ let addMoreActions = true;
472
+ console.log();
473
+ console.log(chalk_1.default.cyan('Now let\'s add actions for this rule:'));
474
+ console.log();
475
+ while (addMoreActions) {
476
+ const action = await promptForAction(appId, tables);
477
+ actions.push(action);
478
+ const { more } = await inquirer_1.default.prompt([{
479
+ type: 'confirm',
480
+ name: 'more',
481
+ message: 'Add another action?',
482
+ default: false
483
+ }]);
484
+ addMoreActions = more;
485
+ }
486
+ return {
487
+ name: basicInfo.name,
488
+ description: basicInfo.description || undefined,
489
+ trigger: basicInfo.trigger,
490
+ table: basicInfo.table,
491
+ condition: basicInfo.condition || 'true',
492
+ actions,
493
+ priority: basicInfo.priority || 10,
494
+ enabled: true
495
+ };
496
+ }
497
+ /**
498
+ * Interactive prompt for updating a rule
499
+ */
500
+ async function promptForRuleUpdate(appId, currentRule) {
501
+ const schemaResult = await api.getSchema(appId);
502
+ const tables = schemaResult.data || [];
503
+ const tableChoices = [
504
+ { name: '* (All tables)', value: '*' },
505
+ ...tables.map((t) => ({ name: `${t.icon || ''} ${t.name} (${t.slug})`, value: t.slug }))
506
+ ];
507
+ const updates = await inquirer_1.default.prompt([
508
+ {
509
+ type: 'input',
510
+ name: 'name',
511
+ message: 'Rule name:',
512
+ default: currentRule.name
513
+ },
514
+ {
515
+ type: 'input',
516
+ name: 'description',
517
+ message: 'Description:',
518
+ default: currentRule.description || ''
519
+ },
520
+ {
521
+ type: 'list',
522
+ name: 'trigger',
523
+ message: 'Trigger:',
524
+ choices: TRIGGERS,
525
+ default: currentRule.trigger
526
+ },
527
+ {
528
+ type: 'list',
529
+ name: 'table',
530
+ message: 'Table:',
531
+ choices: tableChoices,
532
+ default: currentRule.table
533
+ },
534
+ {
535
+ type: 'input',
536
+ name: 'condition',
537
+ message: 'Condition:',
538
+ default: currentRule.condition || 'true'
539
+ },
540
+ {
541
+ type: 'number',
542
+ name: 'priority',
543
+ message: 'Priority:',
544
+ default: currentRule.priority || 10
545
+ },
546
+ {
547
+ type: 'confirm',
548
+ name: 'enabled',
549
+ message: 'Enabled?',
550
+ default: currentRule.enabled !== false
551
+ },
552
+ {
553
+ type: 'confirm',
554
+ name: 'updateActions',
555
+ message: 'Update actions?',
556
+ default: false
557
+ }
558
+ ]);
559
+ const result = {
560
+ name: updates.name,
561
+ description: updates.description || undefined,
562
+ trigger: updates.trigger,
563
+ table: updates.table,
564
+ condition: updates.condition,
565
+ priority: updates.priority,
566
+ enabled: updates.enabled
567
+ };
568
+ if (updates.updateActions) {
569
+ const actions = [];
570
+ let addMoreActions = true;
571
+ console.log();
572
+ console.log(chalk_1.default.cyan('Enter new actions (this will replace existing actions):'));
573
+ console.log();
574
+ while (addMoreActions) {
575
+ const action = await promptForAction(appId, tables);
576
+ actions.push(action);
577
+ const { more } = await inquirer_1.default.prompt([{
578
+ type: 'confirm',
579
+ name: 'more',
580
+ message: 'Add another action?',
581
+ default: false
582
+ }]);
583
+ addMoreActions = more;
584
+ }
585
+ result.actions = actions;
586
+ }
587
+ return result;
588
+ }
589
+ /**
590
+ * Interactive prompt for a single action
591
+ */
592
+ async function promptForAction(appId, tables) {
593
+ const { actionType } = await inquirer_1.default.prompt([{
594
+ type: 'list',
595
+ name: 'actionType',
596
+ message: 'Action type:',
597
+ choices: ACTION_TYPES
598
+ }]);
599
+ switch (actionType) {
600
+ case 'set_field': {
601
+ const answers = await inquirer_1.default.prompt([
602
+ {
603
+ type: 'input',
604
+ name: 'field',
605
+ message: 'Field name to set:',
606
+ validate: (input) => input.trim() ? true : 'Field name is required'
607
+ },
608
+ {
609
+ type: 'input',
610
+ name: 'value',
611
+ message: 'Value (can use Liquid: {{ user.id }}):',
612
+ validate: (input) => input.trim() ? true : 'Value is required'
613
+ }
614
+ ]);
615
+ return { type: 'set_field', field: answers.field, value: answers.value };
616
+ }
617
+ case 'calculate': {
618
+ const answers = await inquirer_1.default.prompt([
619
+ {
620
+ type: 'input',
621
+ name: 'field',
622
+ message: 'Field name to calculate:',
623
+ validate: (input) => input.trim() ? true : 'Field name is required'
624
+ },
625
+ {
626
+ type: 'input',
627
+ name: 'value',
628
+ message: 'Formula (Liquid: {{ price | times: quantity }}):',
629
+ validate: (input) => input.trim() ? true : 'Formula is required'
630
+ }
631
+ ]);
632
+ return { type: 'calculate', field: answers.field, value: answers.value };
633
+ }
634
+ case 'reject': {
635
+ const answers = await inquirer_1.default.prompt([{
636
+ type: 'input',
637
+ name: 'message',
638
+ message: 'Error message (can use Liquid):',
639
+ validate: (input) => input.trim() ? true : 'Message is required'
640
+ }]);
641
+ return { type: 'reject', message: answers.message };
642
+ }
643
+ case 'validate': {
644
+ const answers = await inquirer_1.default.prompt([
645
+ {
646
+ type: 'input',
647
+ name: 'condition',
648
+ message: 'Validation condition (Liquid, should output "true" when valid):',
649
+ validate: (input) => input.trim() ? true : 'Condition is required'
650
+ },
651
+ {
652
+ type: 'input',
653
+ name: 'message',
654
+ message: 'Error message if validation fails:',
655
+ validate: (input) => input.trim() ? true : 'Message is required'
656
+ }
657
+ ]);
658
+ return { type: 'validate', condition: answers.condition, message: answers.message };
659
+ }
660
+ case 'lookup_and_set': {
661
+ const tableChoices = tables.map((t) => ({
662
+ name: `${t.icon || ''} ${t.name} (${t.slug})`,
663
+ value: t.slug
664
+ }));
665
+ const answers = await inquirer_1.default.prompt([
666
+ {
667
+ type: 'input',
668
+ name: 'field',
669
+ message: 'Field to populate:',
670
+ validate: (input) => input.trim() ? true : 'Field is required'
671
+ },
672
+ {
673
+ type: 'list',
674
+ name: 'lookupTable',
675
+ message: 'Table to query:',
676
+ choices: tableChoices
677
+ },
678
+ {
679
+ type: 'input',
680
+ name: 'lookupFilter',
681
+ message: 'Filter (JSON, e.g., {"location": "{{ location }}"}):',
682
+ default: '{}'
683
+ },
684
+ {
685
+ type: 'list',
686
+ name: 'selectBy',
687
+ message: 'How to select from multiple matches:',
688
+ choices: LOOKUP_SELECT_OPTIONS
689
+ },
690
+ {
691
+ type: 'input',
692
+ name: 'selectField',
693
+ message: 'Field for min/max selection (if applicable):',
694
+ when: (ans) => ans.selectBy === 'min' || ans.selectBy === 'max'
695
+ },
696
+ {
697
+ type: 'input',
698
+ name: 'returnField',
699
+ message: 'Field to return from matched record:',
700
+ default: 'id'
701
+ },
702
+ {
703
+ type: 'list',
704
+ name: 'noMatchAction',
705
+ message: 'What to do if no match found:',
706
+ choices: NO_MATCH_OPTIONS
707
+ },
708
+ {
709
+ type: 'input',
710
+ name: 'defaultValue',
711
+ message: 'Default value:',
712
+ when: (ans) => ans.noMatchAction === 'default'
713
+ },
714
+ {
715
+ type: 'input',
716
+ name: 'noMatchMessage',
717
+ message: 'Error message if rejected:',
718
+ when: (ans) => ans.noMatchAction === 'reject'
719
+ }
720
+ ]);
721
+ let lookupFilter = {};
722
+ try {
723
+ lookupFilter = JSON.parse(answers.lookupFilter || '{}');
724
+ }
725
+ catch {
726
+ // Keep empty object
727
+ }
728
+ return {
729
+ type: 'lookup_and_set',
730
+ field: answers.field,
731
+ lookupTable: answers.lookupTable,
732
+ lookupFilter,
733
+ selectBy: answers.selectBy,
734
+ selectField: answers.selectField,
735
+ returnField: answers.returnField,
736
+ noMatchAction: answers.noMatchAction,
737
+ defaultValue: answers.defaultValue,
738
+ noMatchMessage: answers.noMatchMessage
739
+ };
740
+ }
741
+ default:
742
+ return { type: 'set_field', field: '', value: '' };
743
+ }
744
+ }
745
+ function handleError(error) {
746
+ const errorLower = error.toLowerCase();
747
+ if (errorLower.includes('session expired') ||
748
+ errorLower.includes('token expired') ||
749
+ errorLower.includes('unauthorized')) {
750
+ config.clearAuth();
751
+ logger.error('Session expired. Credentials cleared.');
752
+ logger.info('Run: thinksoft login');
753
+ return;
754
+ }
755
+ if (errorLower.includes('not found')) {
756
+ logger.error('Agent or rule not found');
757
+ logger.info('Use "thinksoft agents" to list available agents');
758
+ return;
759
+ }
760
+ logger.error(error);
761
+ }
762
+ //# sourceMappingURL=rules.js.map