@velaro/cli 1.4.93 → 1.4.94

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.
@@ -238,6 +238,34 @@ const testCommand = {
238
238
 
239
239
  // ── export ────────────────────────────────────────────────────────────────────
240
240
 
241
+ // -- create (Velaro staff only) -------------------------------------------------
242
+ // POST KnowledgeSetup/Staff/{siteId}/create-index on velaro-messaging. Idempotent: an existing
243
+ // index with the same (sanitized) name is returned unchanged.
244
+
245
+ const createCommand = {
246
+ command: 'create',
247
+ describe: 'Create a named virtual index on any site (Velaro staff only; idempotent)',
248
+ builder: (y) =>
249
+ y
250
+ .option('site', { type: 'number', demandOption: true, describe: 'Target site ID' })
251
+ .option('name', { type: 'string', demandOption: true, describe: 'Index name, e.g. 100140-troubleshooting' })
252
+ .option('purpose', {
253
+ type: 'string', default: 'general', choices: ['general', 'troubleshooting', 'catalog'],
254
+ describe: 'Index purpose',
255
+ })
256
+ .option('exclude-from-all-sources', { type: 'boolean', describe: 'Keep this index out of the ["*"] all-sources search' })
257
+ .option('json', { type: 'boolean', describe: 'Print raw JSON' }),
258
+ handler: async (argv) => {
259
+ const body = { indexName: argv.name, purpose: argv.purpose };
260
+ if (argv['exclude-from-all-sources'] !== undefined) body.excludeFromAllSources = argv['exclude-from-all-sources'];
261
+ const idx = await messagingPost(`/KnowledgeSetup/Staff/${encodeURIComponent(argv.site)}/create-index`, body);
262
+ if (argv.json) { console.log(JSON.stringify(idx, null, 2)); return; }
263
+ console.log(`
264
+ Index ready on site ${argv.site}: ${idx.name} (id: ${idx.id}, purpose: ${idx.purpose}, excludeFromAllSources: ${!!idx.excludeFromAllSources})
265
+ `);
266
+ },
267
+ };
268
+
241
269
  export const indexCommand = {
242
270
  command: 'index <subcommand>',
243
271
  describe: 'Manage search indexes — view quota, physical index health, and reingest content',
@@ -248,6 +276,7 @@ export const indexCommand = {
248
276
  .command(provisionCommand)
249
277
  .command(reingestCommand)
250
278
  .command(testCommand)
251
- .demandCommand(1, 'Specify a subcommand: list, site, provision, reingest, test'),
279
+ .command(createCommand)
280
+ .demandCommand(1, 'Specify a subcommand: list, site, provision, reingest, test, create'),
252
281
  handler: () => {},
253
282
  };
@@ -10,7 +10,9 @@ export const migrateCommand = {
10
10
  .command(migrateLivefluenceCommand)
11
11
  .command(migrateUrlRulesCommand)
12
12
  .command(migrateTeamsCommand)
13
- .demandCommand(1, 'Specify a subcommand: export, livefluence, url-rules, teams'),
13
+ .command(migrateWorkflowsCommand)
14
+ .command(migrateRulesCommand)
15
+ .demandCommand(1, 'Specify a subcommand: export, livefluence, url-rules, teams, workflows, rules'),
14
16
  handler: () => {},
15
17
  };
16
18
 
@@ -195,3 +197,127 @@ const migrateTeamsCommand = {
195
197
  console.log('\n Members are not copied; assign v20 agents to the new teams.\n');
196
198
  }),
197
199
  };
200
+
201
+ // -- staff, site-targeted workflow + rule ops (superadmin only) ----------------
202
+ // Act on ANOTHER v20 site's workflows/Rules after `migrate livefluence` created them disabled.
203
+
204
+ const csv = (v) => (v ? String(v).split(',').map(x => x.trim()).filter(Boolean) : []);
205
+ const printItems = (result) => {
206
+ for (const i of result.items ?? []) console.log(` ${String(i.status).padEnd(13)} [${i.id}] ${i.name ?? ''}${i.reason ? ` -- ${i.reason}` : ''}`);
207
+ for (const n of result.notFound ?? []) console.log(` not-found ${n}`);
208
+ console.log(`\n ${result.dryRun ? 'DRY RUN: ' : ''}changed ${result.changed}${result.refused !== undefined ? `, refused ${result.refused}` : ''}\n`);
209
+ };
210
+
211
+ const migrateWorkflowsCommand = {
212
+ command: 'workflows <op>',
213
+ describe: 'Staff: list / enable / disable / create / re-target workflows on a v20 site',
214
+ builder: (y) =>
215
+ y
216
+ .command({
217
+ command: 'list',
218
+ describe: 'List a v20 site workflows (id, name, enabled, priority, action, team/AI target)',
219
+ builder: (b) => b.option('site', { type: 'number', alias: 's', demandOption: true, describe: 'V20 site ID' }),
220
+ handler: runCommand(async (argv) => {
221
+ const r = await get(`/DatabaseTool/SiteWorkflows/${argv.site}`);
222
+ for (const w of r.workflows ?? []) {
223
+ const target = w.teamId ? `team ${w.teamId} ${w.teamName ?? ''}` : w.aiConfigId ? `ai ${w.aiConfigId} ${w.aiConfigName ?? ''}` : '-';
224
+ console.log(` [${w.id}] ${w.enabled ? 'ON ' : 'off'} p=${w.priority ?? '-'} ${String(w.action ?? '-').padEnd(12)} ${target.padEnd(28)} groups=${w.conditionGroups} ${w.name}`);
225
+ }
226
+ console.log(`\n ${r.count} workflow(s) on site ${r.siteId}\n`);
227
+ }),
228
+ })
229
+ .command({
230
+ command: 'set-enabled',
231
+ describe: 'Enable or disable workflows by ids or names. Enabling refuses partially converted triggers unless --force-partial.',
232
+ builder: (b) => b
233
+ .option('site', { type: 'number', alias: 's', demandOption: true, describe: 'V20 site ID' })
234
+ .option('ids', { type: 'string', describe: 'Comma-separated workflow ids' })
235
+ .option('names', { type: 'string', describe: 'Comma-separated workflow names' })
236
+ .option('enable', { type: 'boolean', demandOption: true, describe: '--enable to turn on, --no-enable to turn off' })
237
+ .option('v10-site-id', { type: 'number', alias: 'v', describe: 'Check each workflow against its same-named v10 rule conversion' })
238
+ .option('force-partial', { type: 'boolean', default: false, describe: 'Enable even if the v10 expression was not fully converted' })
239
+ .option('dry-run', { type: 'boolean', default: false }),
240
+ handler: runCommand(async (argv) => {
241
+ const r = await post('/DatabaseTool/SiteWorkflows/SetEnabled', {
242
+ siteId: argv.site, workflowIds: csv(argv.ids).map(Number), names: csv(argv.names),
243
+ enabled: argv.enable, dryRun: argv['dry-run'], forcePartial: argv['force-partial'], v10SiteId: argv['v10-site-id'] ?? null,
244
+ });
245
+ printItems(r);
246
+ }),
247
+ })
248
+ .command({
249
+ command: 'create',
250
+ describe: 'Create a workflow on a site from a nodes-blob JSON file (disabled unless --enable)',
251
+ builder: (b) => b
252
+ .option('site', { type: 'number', alias: 's', demandOption: true })
253
+ .option('name', { type: 'string', alias: 'n', demandOption: true })
254
+ .option('file', { type: 'string', alias: 'f', demandOption: true, describe: 'JSON file: the nodes object, e.g. {"1":{"type":"trigger",...},"2":{"type":"action",...}}' })
255
+ .option('trigger-type', { type: 'string', default: 'conversation_opened' })
256
+ .option('enable', { type: 'boolean', default: false })
257
+ .option('dry-run', { type: 'boolean', default: false }),
258
+ handler: runCommand(async (argv) => {
259
+ const { readFileSync } = await import('fs');
260
+ const nodesBlob = JSON.stringify(JSON.parse(readFileSync(argv.file, 'utf8')));
261
+ const r = await post('/DatabaseTool/SiteWorkflows/Create', {
262
+ siteId: argv.site, name: argv.name, triggerType: argv['trigger-type'], nodesBlob, enabled: argv.enable, dryRun: argv['dry-run'],
263
+ });
264
+ console.log(`\n ${r.status}${r.workflowId ? ` (workflow id=${r.workflowId})` : ''}\n`);
265
+ }),
266
+ })
267
+ .command({
268
+ command: 'set-action',
269
+ describe: 'Set a workflow action node to transferToAI {aiConfigId} or routeToTeam {teamId}',
270
+ builder: (b) => b
271
+ .option('site', { type: 'number', alias: 's', demandOption: true })
272
+ .option('workflow-id', { type: 'number', alias: 'w', demandOption: true })
273
+ .option('ai-config-id',{ type: 'number', describe: 'Set action to transferToAI with this AI config' })
274
+ .option('team-id', { type: 'number', describe: 'Set action to routeToTeam with this team' })
275
+ .check((a) => (!!a['ai-config-id'] !== !!a['team-id']) || 'Pass exactly one of --ai-config-id or --team-id')
276
+ .option('dry-run', { type: 'boolean', default: false }),
277
+ handler: runCommand(async (argv) => {
278
+ const r = await post('/DatabaseTool/SiteWorkflows/SetAction', {
279
+ siteId: argv.site, workflowId: argv['workflow-id'],
280
+ actionType: argv['ai-config-id'] ? 'transferToAI' : 'routeToTeam',
281
+ targetId: argv['ai-config-id'] || argv['team-id'], dryRun: argv['dry-run'],
282
+ });
283
+ console.log(`\n ${r.status} (workflow ${r.workflowId})\n`);
284
+ }),
285
+ })
286
+ .demandCommand(1, 'Specify: list, set-enabled, create, set-action'),
287
+ handler: () => {},
288
+ };
289
+
290
+ const migrateRulesCommand = {
291
+ command: 'rules <op>',
292
+ describe: 'Staff: list / switch off v20 Rules on a site (e.g. the #1253 use-ai rules) without deleting them',
293
+ builder: (y) =>
294
+ y
295
+ .command({
296
+ command: 'list',
297
+ describe: 'List a v20 site Rules with enabled state',
298
+ builder: (b) => b.option('site', { type: 'number', alias: 's', demandOption: true }),
299
+ handler: runCommand(async (argv) => {
300
+ const r = await get(`/DatabaseTool/SiteRules/${argv.site}`);
301
+ for (const x of r.rules ?? []) console.log(` [${x.id}] ${x.enabled ? 'ON ' : 'off'} p=${x.priority} ${x.trigger} ${x.valueJson ?? ''}`);
302
+ console.log(`\n ${r.count} rule(s) on site ${r.siteId}\n`);
303
+ }),
304
+ })
305
+ .command({
306
+ command: 'set-enabled',
307
+ describe: 'Switch Rules off/on (reversible; a "disabled:" prefix on the trigger). Select by ids or --trigger use-ai.',
308
+ builder: (b) => b
309
+ .option('site', { type: 'number', alias: 's', demandOption: true })
310
+ .option('ids', { type: 'string', describe: 'Comma-separated rule ids' })
311
+ .option('trigger', { type: 'string', describe: 'Every rule with this trigger, e.g. use-ai' })
312
+ .option('enable', { type: 'boolean', demandOption: true, describe: '--enable to turn on, --no-enable to turn off' })
313
+ .option('dry-run', { type: 'boolean', default: false }),
314
+ handler: runCommand(async (argv) => {
315
+ const r = await post('/DatabaseTool/SiteRules/SetEnabled', {
316
+ siteId: argv.site, ruleIds: csv(argv.ids).map(Number), trigger: argv.trigger ?? null, enabled: argv.enable, dryRun: argv['dry-run'],
317
+ });
318
+ printItems(r);
319
+ }),
320
+ })
321
+ .demandCommand(1, 'Specify: list, set-enabled'),
322
+ handler: () => {},
323
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/cli",
3
- "version": "1.4.93",
3
+ "version": "1.4.94",
4
4
  "description": "Velaro Workspace v20 — command-line interface for managing bots, knowledge base ingestion, MCP API keys, search indexes, and ops health.",
5
5
  "type": "module",
6
6
  "bin": {