@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.
- package/bin/thinksoft.js +1 -1
- package/dist/commands/agents.d.ts +7 -0
- package/dist/commands/agents.js +162 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/chat.d.ts +12 -0
- package/dist/commands/chat.js +217 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/create.js +13 -5
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/login.js +7 -4
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/records.d.ts +37 -0
- package/dist/commands/records.js +434 -0
- package/dist/commands/records.js.map +1 -0
- package/dist/commands/rules.d.ts +38 -0
- package/dist/commands/rules.js +762 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/schema.d.ts +53 -0
- package/dist/commands/schema.js +631 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/studio.d.ts +13 -0
- package/dist/commands/studio.js +205 -0
- package/dist/commands/studio.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +543 -8
- package/dist/index.js.map +1 -1
- package/dist/utils/api.d.ts +103 -0
- package/dist/utils/api.js +245 -5
- package/dist/utils/api.js.map +1 -1
- package/package.json +2 -2
|
@@ -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
|