@velaro/cli 1.4.74 → 1.4.75
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/velaro.js
CHANGED
|
@@ -48,6 +48,10 @@ import { reportExplainCommand } from '../lib/commands/report-explain.js';
|
|
|
48
48
|
import { conversationEfficiencyCommand } from '../lib/commands/conversation-efficiency.js';
|
|
49
49
|
import { executiveInsightsCommand } from '../lib/commands/executive-insights.js';
|
|
50
50
|
import { dbCommand } from '../lib/commands/db.js';
|
|
51
|
+
import { skillRoutingCommand } from '../lib/commands/skill-routing.js';
|
|
52
|
+
import { keywordIntelligenceCommand } from '../lib/commands/keyword-intelligence.js';
|
|
53
|
+
import { aiFieldExtractionCommand } from '../lib/commands/ai-field-extraction.js';
|
|
54
|
+
import { sentimentCommand, qualityCommand, chatIntelligenceThemesCommand } from '../lib/commands/quality-reports.js';
|
|
51
55
|
import { focusCommand } from '../lib/commands/focus.js';
|
|
52
56
|
import { triageCommand } from '../lib/commands/triage.js';
|
|
53
57
|
import { automationDraftCommand } from '../lib/commands/automation-draft.js';
|
|
@@ -156,6 +160,12 @@ await yargs(hideBin(process.argv))
|
|
|
156
160
|
.command({ command: 'staff', desc: 'Velaro staff: cross-site tools (backfill)', builder: staffCommand, handler: () => {} })
|
|
157
161
|
.command(reportExplainCommand)
|
|
158
162
|
.command(dbCommand)
|
|
163
|
+
.command(skillRoutingCommand)
|
|
164
|
+
.command(keywordIntelligenceCommand)
|
|
165
|
+
.command(aiFieldExtractionCommand)
|
|
166
|
+
.command(sentimentCommand)
|
|
167
|
+
.command(qualityCommand)
|
|
168
|
+
.command(chatIntelligenceThemesCommand)
|
|
159
169
|
.command(focusCommand)
|
|
160
170
|
.command(triageCommand)
|
|
161
171
|
.command(automationDraftCommand)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { messagingGet, messagingPost, messagingPut, messagingDel } from '../api.js';
|
|
2
|
+
import { runCommand } from '../run.js';
|
|
3
|
+
|
|
4
|
+
// -- velaro ai-field-extraction -------------------------------------------------------
|
|
5
|
+
// Post-resolve AI field extraction settings and agent corrections
|
|
6
|
+
// (velaro-messaging /AiFieldExtraction/*). Site comes from auth.
|
|
7
|
+
|
|
8
|
+
const print = (data) => console.log(JSON.stringify(data, null, 2));
|
|
9
|
+
|
|
10
|
+
const get = {
|
|
11
|
+
command: 'get',
|
|
12
|
+
describe: 'Show the extraction config',
|
|
13
|
+
handler: runCommand(async () => print(await messagingGet('/AiFieldExtraction/Config'))),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const FLAG_KEYS = {
|
|
17
|
+
enabled: 'Enabled',
|
|
18
|
+
extractLineOfBusiness: 'ExtractLineOfBusiness',
|
|
19
|
+
extractIntent: 'ExtractIntent',
|
|
20
|
+
extractState: 'ExtractState',
|
|
21
|
+
extractIdentifiers: 'ExtractIdentifiers',
|
|
22
|
+
createThemeTags: 'CreateThemeTags',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Partial update: only fields the caller passed are sent.
|
|
26
|
+
function body(a) {
|
|
27
|
+
const b = {};
|
|
28
|
+
for (const [key, pascal] of Object.entries(FLAG_KEYS)) {
|
|
29
|
+
if (a[key] !== undefined) b[pascal] = a[key];
|
|
30
|
+
}
|
|
31
|
+
if (a.monthlyCap !== undefined) b.MonthlyCap = a.monthlyCap;
|
|
32
|
+
if (a.minConfidence !== undefined) b.MinConfidence = a.minConfidence;
|
|
33
|
+
return b;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const builder = (y) => {
|
|
37
|
+
y.option('enabled', { type: 'boolean', describe: 'Opt-in switch (default off)' });
|
|
38
|
+
y.option('extract-line-of-business', { type: 'boolean' });
|
|
39
|
+
y.option('extract-intent', { type: 'boolean' });
|
|
40
|
+
y.option('extract-state', { type: 'boolean' });
|
|
41
|
+
y.option('extract-identifiers', { type: 'boolean' });
|
|
42
|
+
y.option('create-theme-tags', { type: 'boolean' });
|
|
43
|
+
y.option('monthly-cap', { type: 'number', describe: '1..50000' });
|
|
44
|
+
y.option('min-confidence', { type: 'number', describe: '0..1' });
|
|
45
|
+
return y;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const create = {
|
|
49
|
+
command: 'create',
|
|
50
|
+
describe: 'Create the extraction config (opt-in; extraction stays off until --enabled)',
|
|
51
|
+
builder,
|
|
52
|
+
handler: runCommand(async (a) => print(await messagingPost('/AiFieldExtraction/Config', body(a)))),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const update = {
|
|
56
|
+
command: 'update',
|
|
57
|
+
describe: 'Partial update of the extraction config (only passed fields change)',
|
|
58
|
+
builder,
|
|
59
|
+
handler: runCommand(async (a) => print(await messagingPut('/AiFieldExtraction/Config', body(a)))),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const del = {
|
|
63
|
+
command: 'delete',
|
|
64
|
+
describe: 'Delete the extraction config',
|
|
65
|
+
handler: runCommand(async () => {
|
|
66
|
+
await messagingDel('/AiFieldExtraction/Config');
|
|
67
|
+
console.log('Deleted');
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const fields = {
|
|
72
|
+
command: 'fields <conversationId>',
|
|
73
|
+
describe: 'Show extracted fields for a conversation',
|
|
74
|
+
builder: (y) => y.positional('conversationId', { type: 'number' }),
|
|
75
|
+
handler: runCommand(async (a) => print(await messagingGet(`/AiFieldExtraction/Conversation/${a.conversationId}/Fields`))),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const correct = {
|
|
79
|
+
command: 'correct <conversationId> <fieldName>',
|
|
80
|
+
describe: 'Agent correction of one extracted field',
|
|
81
|
+
builder: (y) => y
|
|
82
|
+
.positional('conversationId', { type: 'number' })
|
|
83
|
+
.positional('fieldName', { type: 'string' })
|
|
84
|
+
.option('value', { type: 'string', demandOption: true }),
|
|
85
|
+
handler: runCommand(async (a) => {
|
|
86
|
+
await messagingPut(`/AiFieldExtraction/Conversation/${a.conversationId}/Fields/${encodeURIComponent(a.fieldName)}`, { Value: a.value });
|
|
87
|
+
console.log('Corrected');
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const extract = {
|
|
92
|
+
command: 'extract <conversationId>',
|
|
93
|
+
describe: 'Run extraction now for one conversation',
|
|
94
|
+
builder: (y) => y.positional('conversationId', { type: 'number' }),
|
|
95
|
+
handler: runCommand(async (a) => print(await messagingPost(`/AiFieldExtraction/Conversation/${a.conversationId}/Extract`, {}))),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const aiFieldExtractionCommand = {
|
|
99
|
+
command: 'ai-field-extraction',
|
|
100
|
+
describe: 'AI field extraction: opt-in settings, monthly cap, extracted fields and corrections',
|
|
101
|
+
builder: (y) => y
|
|
102
|
+
.command(get).command(create).command(update).command(del)
|
|
103
|
+
.command(fields).command(correct).command(extract)
|
|
104
|
+
.demandCommand(1, 'Specify a subcommand. Run "velaro ai-field-extraction --help".'),
|
|
105
|
+
handler: () => {},
|
|
106
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { messagingGet } from '../api.js';
|
|
2
|
+
import { runCommand } from '../run.js';
|
|
3
|
+
|
|
4
|
+
// -- velaro keyword-intelligence ----------------------------------------------------
|
|
5
|
+
// Top customer keywords/phrases and conversation drill-down
|
|
6
|
+
// (velaro-messaging /api/keyword-intelligence/*). Site comes from auth.
|
|
7
|
+
|
|
8
|
+
const print = (data) => console.log(JSON.stringify(data, null, 2));
|
|
9
|
+
|
|
10
|
+
const top = {
|
|
11
|
+
command: 'top',
|
|
12
|
+
describe: 'Top keywords/phrases in a date range',
|
|
13
|
+
builder: (y) => y
|
|
14
|
+
.option('from', { type: 'string', demandOption: true, describe: 'ISO start date' })
|
|
15
|
+
.option('to', { type: 'string', demandOption: true, describe: 'ISO end date' })
|
|
16
|
+
.option('group-by', { type: 'string', default: 'none' })
|
|
17
|
+
.option('ngram', { type: 'number', default: 3 })
|
|
18
|
+
.option('limit', { type: 'number', default: 50 })
|
|
19
|
+
.option('team', { type: 'number' })
|
|
20
|
+
.option('agent', { type: 'number' })
|
|
21
|
+
.option('website', { type: 'string' }),
|
|
22
|
+
handler: runCommand(async (a) => {
|
|
23
|
+
const qs = new URLSearchParams({ from: a.from, to: a.to, groupBy: a.groupBy, ngram: String(a.ngram), limit: String(a.limit) });
|
|
24
|
+
if (a.team != null) qs.set('teamId', String(a.team));
|
|
25
|
+
if (a.agent != null) qs.set('agentId', String(a.agent));
|
|
26
|
+
if (a.website) qs.set('website', a.website);
|
|
27
|
+
print(await messagingGet(`/api/keyword-intelligence/top?${qs}`));
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const conversations = {
|
|
32
|
+
command: 'conversations',
|
|
33
|
+
describe: 'Conversations containing a keyword',
|
|
34
|
+
builder: (y) => y
|
|
35
|
+
.option('keyword', { type: 'string', demandOption: true })
|
|
36
|
+
.option('from', { type: 'string', demandOption: true })
|
|
37
|
+
.option('to', { type: 'string', demandOption: true })
|
|
38
|
+
.option('team', { type: 'number' })
|
|
39
|
+
.option('agent', { type: 'number' }),
|
|
40
|
+
handler: runCommand(async (a) => {
|
|
41
|
+
const qs = new URLSearchParams({ keyword: a.keyword, from: a.from, to: a.to });
|
|
42
|
+
if (a.team != null) qs.set('teamId', String(a.team));
|
|
43
|
+
if (a.agent != null) qs.set('agentId', String(a.agent));
|
|
44
|
+
print(await messagingGet(`/api/keyword-intelligence/conversations?${qs}`));
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const keywordIntelligenceCommand = {
|
|
49
|
+
command: 'keyword-intelligence',
|
|
50
|
+
describe: 'Keyword Intelligence: what customers talk about, with drill-down',
|
|
51
|
+
builder: (y) => y.command(top).command(conversations).demandCommand(1, 'Specify a subcommand. Run "velaro keyword-intelligence --help".'),
|
|
52
|
+
handler: () => {},
|
|
53
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { messagingGet, messagingPost, messagingPut, messagingDel } from '../api.js';
|
|
2
|
+
import { runCommand } from '../run.js';
|
|
3
|
+
|
|
4
|
+
// -- velaro sentiment | quality | chat-intelligence ---------------------------------
|
|
5
|
+
// Report CLI with the optional Website (deploymentId) and team filters, plus QA
|
|
6
|
+
// rubric/alert commands. Endpoints live in velaro-messaging; site comes from auth and
|
|
7
|
+
// deploymentId/teamId are verified server-side against that site.
|
|
8
|
+
|
|
9
|
+
const print = (data) => console.log(JSON.stringify(data, null, 2));
|
|
10
|
+
const scope = (y) => y
|
|
11
|
+
.option('deployment', { type: 'number', describe: 'Website (deployment row id)' })
|
|
12
|
+
.option('team', { type: 'number', describe: 'Team id' });
|
|
13
|
+
|
|
14
|
+
const SENTIMENT_ROUTES = {
|
|
15
|
+
overview: 'sentiment-overview',
|
|
16
|
+
breakdown: 'sentiment-breakdown',
|
|
17
|
+
'conversation-overview': 'conversation-sentiment-overview',
|
|
18
|
+
'conversation-breakdown': 'conversation-sentiment-breakdown',
|
|
19
|
+
'contact-overview': 'contact-sentiment-overview',
|
|
20
|
+
'agent-breakdown': 'agent-sentiment-breakdown',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const sentimentCommand = {
|
|
24
|
+
command: 'sentiment [kind]',
|
|
25
|
+
describe: 'Sentiment report, optionally per website/team',
|
|
26
|
+
builder: (y) => scope(y
|
|
27
|
+
.positional('kind', { choices: Object.keys(SENTIMENT_ROUTES), default: 'overview' })
|
|
28
|
+
.option('from', { type: 'string' })
|
|
29
|
+
.option('to', { type: 'string' })
|
|
30
|
+
.option('group-by', { type: 'string', default: 'day' })),
|
|
31
|
+
handler: runCommand(async (a) => {
|
|
32
|
+
const end = a.to ? new Date(a.to) : new Date();
|
|
33
|
+
const start = a.from ? new Date(a.from) : new Date(end.getTime() - 30 * 86400000);
|
|
34
|
+
const body = { Start: start.toISOString(), End: end.toISOString(), GroupBy: a.groupBy };
|
|
35
|
+
if (a.deployment != null) body.DeploymentId = a.deployment;
|
|
36
|
+
if (a.team != null) body.TeamId = a.team;
|
|
37
|
+
print(await messagingPost(`/Reports/${SENTIMENT_ROUTES[a.kind]}`, body));
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const agentPerformance = {
|
|
42
|
+
command: 'agent-performance',
|
|
43
|
+
describe: 'AI quality scores per agent, optionally per website/team',
|
|
44
|
+
builder: (y) => scope(y.option('days', { type: 'number', default: 30 })),
|
|
45
|
+
handler: runCommand(async (a) => {
|
|
46
|
+
const qs = new URLSearchParams({ days: String(a.days) });
|
|
47
|
+
if (a.deployment != null) qs.set('deploymentId', String(a.deployment));
|
|
48
|
+
if (a.team != null) qs.set('teamId', String(a.team));
|
|
49
|
+
print(await messagingGet(`/api/admin/Reports/AgentPerformance?${qs}`));
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const lowScores = {
|
|
54
|
+
command: 'low-scores',
|
|
55
|
+
describe: 'Conversations below the QA threshold',
|
|
56
|
+
builder: (y) => y.option('threshold', { type: 'number' }).option('days', { type: 'number', default: 30 }).option('agent', { type: 'number' }).option('take', { type: 'number', default: 50 }),
|
|
57
|
+
handler: runCommand(async (a) => {
|
|
58
|
+
const qs = new URLSearchParams({ days: String(a.days), take: String(a.take) });
|
|
59
|
+
if (a.threshold != null) qs.set('threshold', String(a.threshold));
|
|
60
|
+
if (a.agent != null) qs.set('agentId', String(a.agent));
|
|
61
|
+
print(await messagingGet(`/api/admin/QualityQa/low-scores?${qs}`));
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const calibration = {
|
|
66
|
+
command: 'calibration',
|
|
67
|
+
describe: 'AI score vs human review calibration',
|
|
68
|
+
builder: (y) => y.option('days', { type: 'number', default: 90 }),
|
|
69
|
+
handler: runCommand(async (a) => print(await messagingGet(`/api/admin/QualityQa/calibration?days=${a.days}`))),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const rubric = {
|
|
73
|
+
command: 'rubric <action>',
|
|
74
|
+
describe: 'QA rubrics: list | get | default | create | update | delete (--json for the rubric body)',
|
|
75
|
+
builder: (y) => y.positional('action', { choices: ['list', 'get', 'default', 'create', 'update', 'delete'] })
|
|
76
|
+
.option('id', { type: 'number' }).option('json', { type: 'string', describe: 'Rubric JSON body' }),
|
|
77
|
+
handler: runCommand(async (a) => {
|
|
78
|
+
const base = '/api/admin/QualityQa/rubrics';
|
|
79
|
+
const body = a.json ? JSON.parse(a.json) : {};
|
|
80
|
+
if (a.action === 'list') return print(await messagingGet(base));
|
|
81
|
+
if (a.action === 'default') return print(await messagingGet(`${base}/default`));
|
|
82
|
+
if (a.action === 'create') return print(await messagingPost(base, body));
|
|
83
|
+
if (a.id == null) throw new Error('--id is required');
|
|
84
|
+
if (a.action === 'get') return print(await messagingGet(`${base}/${a.id}`));
|
|
85
|
+
if (a.action === 'update') return print(await messagingPut(`${base}/${a.id}`, body));
|
|
86
|
+
print(await messagingDel(`${base}/${a.id}`));
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const alerts = {
|
|
91
|
+
command: 'alerts',
|
|
92
|
+
describe: 'Quality alert settings (read; pass --json to update)',
|
|
93
|
+
builder: (y) => y.option('json', { type: 'string' }),
|
|
94
|
+
handler: runCommand(async (a) => {
|
|
95
|
+
const path = '/api/admin/QualityQa/alert-settings';
|
|
96
|
+
print(a.json ? await messagingPut(path, JSON.parse(a.json)) : await messagingGet(path));
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const qualityCommand = {
|
|
101
|
+
command: 'quality',
|
|
102
|
+
describe: 'Conversation Quality: agent performance, QA rubrics, low scores, calibration, alerts',
|
|
103
|
+
builder: (y) => y.command(agentPerformance).command(lowScores).command(calibration).command(rubric).command(alerts)
|
|
104
|
+
.demandCommand(1, 'Specify a subcommand. Run "velaro quality --help".'),
|
|
105
|
+
handler: () => {},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const chatIntelligenceThemesCommand = {
|
|
109
|
+
command: 'chat-intelligence',
|
|
110
|
+
describe: 'Chat Intelligence themes, optionally per website/team',
|
|
111
|
+
builder: (y) => scope(y.option('from', { type: 'string' }).option('to', { type: 'string' }).option('group-by', { type: 'string', default: 'month' })),
|
|
112
|
+
handler: runCommand(async (a) => {
|
|
113
|
+
const qs = new URLSearchParams({ groupBy: a.groupBy });
|
|
114
|
+
if (a.from) qs.set('startDate', a.from);
|
|
115
|
+
if (a.to) qs.set('endDate', a.to);
|
|
116
|
+
if (a.deployment != null) qs.set('deploymentId', String(a.deployment));
|
|
117
|
+
if (a.team != null) qs.set('teamId', String(a.team));
|
|
118
|
+
print(await messagingGet(`/Reports/chat-intelligence/themes?${qs}`));
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { messagingGet, messagingPost, messagingPut, messagingDel } from '../api.js';
|
|
2
|
+
import { runCommand } from '../run.js';
|
|
3
|
+
|
|
4
|
+
// -- velaro skill-routing ---------------------------------------------------------
|
|
5
|
+
// CLI for skill-based routing (velaro-messaging SkillRoutingController, route
|
|
6
|
+
// /SkillRouting). Site comes from the caller's auth, never from CLI args.
|
|
7
|
+
// Gated by the EnableSkillBasedRouting entitlement (403 when not entitled).
|
|
8
|
+
// Requirements are disabled by default and PUT is partial (omitted fields unchanged).
|
|
9
|
+
|
|
10
|
+
const print = (data) => console.log(JSON.stringify(data, null, 2));
|
|
11
|
+
const ids = (v) => (v == null ? undefined : String(v).split(',').map((x) => Number(x.trim())).filter((n) => !Number.isNaN(n)));
|
|
12
|
+
const strip = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
|
|
13
|
+
|
|
14
|
+
function crud(resource, noun, addOpts, toBody) {
|
|
15
|
+
return {
|
|
16
|
+
command: `${resource.toLowerCase()} <action>`,
|
|
17
|
+
describe: `${noun}: list | create | update | delete`,
|
|
18
|
+
builder: (y) => addOpts(y.positional('action', { choices: ['list', 'create', 'update', 'delete'] }).option('id', { type: 'number', describe: 'Row id (update/delete)' })),
|
|
19
|
+
handler: runCommand(async (argv) => {
|
|
20
|
+
const base = `/SkillRouting/${resource}`;
|
|
21
|
+
if (argv.action === 'list') return print(await messagingGet(base));
|
|
22
|
+
if (argv.action === 'create') return print(await messagingPost(base, toBody(argv)));
|
|
23
|
+
if (argv.id == null) throw new Error('--id is required for update/delete');
|
|
24
|
+
if (argv.action === 'update') return print(await messagingPut(`${base}/${argv.id}`, toBody(argv)));
|
|
25
|
+
print(await messagingDel(`${base}/${argv.id}`));
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const skills = crud('Skills', 'Skill catalog',
|
|
31
|
+
(y) => y.option('name', { type: 'string' }).option('description', { type: 'string' }),
|
|
32
|
+
(a) => strip({ Name: a.name, Description: a.description }));
|
|
33
|
+
|
|
34
|
+
const assignments = crud('Assignments', 'Agent skill assignments',
|
|
35
|
+
(y) => y.option('agent', { type: 'number', describe: 'workspaceUserId' }).option('skill', { type: 'number', describe: 'agentSkillId' }).option('proficiency', { type: 'number', describe: '1-5' }),
|
|
36
|
+
(a) => strip({ WorkspaceUserId: a.agent, AgentSkillId: a.skill, Proficiency: a.proficiency }));
|
|
37
|
+
|
|
38
|
+
const requirements = crud('Requirements', 'Required/preferred skills per deployment/team',
|
|
39
|
+
(y) => y.option('deployment', { type: 'number' }).option('team', { type: 'number' })
|
|
40
|
+
.option('required', { type: 'string', describe: 'Comma-separated skill ids' })
|
|
41
|
+
.option('preferred', { type: 'string', describe: 'Comma-separated skill ids' })
|
|
42
|
+
.option('overflow-wait', { type: 'number', describe: 'Seconds before falling back to all agents' })
|
|
43
|
+
.option('enabled', { type: 'boolean', describe: 'Turn the requirement on/off (default off)' }),
|
|
44
|
+
(a) => strip({ DeploymentId: a.deployment, TeamId: a.team, RequiredSkillIds: ids(a.required), PreferredSkillIds: ids(a.preferred), OverflowWaitSeconds: a.overflowWait, IsEnabled: a.enabled }));
|
|
45
|
+
|
|
46
|
+
const simulate = {
|
|
47
|
+
command: 'simulate',
|
|
48
|
+
describe: 'Dry-run skill routing for a team (never assigns, never writes the decision log)',
|
|
49
|
+
builder: (y) => y.option('team', { type: 'number' }).option('waited', { type: 'number', describe: 'Seconds the conversation has waited' }),
|
|
50
|
+
handler: runCommand(async (argv) => {
|
|
51
|
+
print(await messagingPost('/SkillRouting/Simulate', { TeamId: argv.team ?? null, WaitedSeconds: argv.waited ?? null }));
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const skillRoutingCommand = {
|
|
56
|
+
command: 'skill-routing',
|
|
57
|
+
describe: 'Skill-based routing: skills, agent assignments, requirements, simulator',
|
|
58
|
+
builder: (y) => y.command(skills).command(assignments).command(requirements).command(simulate)
|
|
59
|
+
.demandCommand(1, 'Specify a subcommand. Run "velaro skill-routing --help".'),
|
|
60
|
+
handler: () => {},
|
|
61
|
+
};
|
package/package.json
CHANGED