@velaro/cli 1.4.105 → 1.4.106

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
@@ -12,6 +12,7 @@ import { ingestCommand } from '../lib/commands/ingest.js';
12
12
  import { indexMoveCommand } from '../lib/commands/index-move.js';
13
13
  import { safetyCommand } from '../lib/commands/safety.js';
14
14
  import { knowledgeAdvisorCommand } from '../lib/commands/knowledge-advisor.js';
15
+ import { knowledgeCommand } from '../lib/commands/knowledge.js';
15
16
  import { productCommand } from '../lib/commands/product.js';
16
17
  import { mcpKeyCommand } from '../lib/commands/mcp-key.js';
17
18
  import { opsCommand } from '../lib/commands/ops.js';
@@ -63,6 +64,7 @@ import { keywordIntelligenceCommand } from '../lib/commands/keyword-intelligence
63
64
  import { aiFieldExtractionCommand } from '../lib/commands/ai-field-extraction.js';
64
65
  import { sentimentCommand, qualityCommand, chatIntelligenceThemesCommand } from '../lib/commands/quality-reports.js';
65
66
  import { cannedLearningCommand } from '../lib/commands/canned-learning.js';
67
+ import { csatOverrideUnapproveCommand } from '../lib/commands/csat-override.js';
66
68
  import { chatIntelligenceTopicsCommand } from '../lib/commands/chat-intelligence-topics.js';
67
69
  import { focusCommand } from '../lib/commands/focus.js';
68
70
  import { triageCommand } from '../lib/commands/triage.js';
@@ -108,6 +110,7 @@ import { supportCommand } from '../lib/commands/support.js';
108
110
  import { routingCommand } from '../lib/commands/routing.js';
109
111
  import { diagnosticsCommand } from '../lib/commands/diagnostics.js';
110
112
  import { aiSpendCommand } from '../lib/commands/ai-spend.js';
113
+ import { skillUsageCommand } from '../lib/commands/skill-usage.js';
111
114
  import { aiFederationCommand } from '../lib/commands/ai-federation.js';
112
115
  import { statusPageCommand } from '../lib/commands/status-page.js';
113
116
  import { statusNotifyCommand } from '../lib/commands/status-notify.js';
@@ -116,6 +119,7 @@ import { emailInboxCommand } from '../lib/commands/email-inbox.js';
116
119
  import { callbackCommand } from '../lib/commands/callback.js';
117
120
  import { caseCommand } from '../lib/commands/case.js';
118
121
  import { pickupCommand } from '../lib/commands/pickup.js';
122
+ import { visitorMonitorCommand } from '../lib/commands/visitor-monitor.js';
119
123
  import { visitorLinkCommand } from '../lib/commands/visitor-link.js';
120
124
  import { applePayCommand } from '../lib/commands/apple-pay.js';
121
125
  import { siteBuilderCommand } from '../lib/commands/site-builder.js';
@@ -159,6 +163,7 @@ await yargs(hideBin(process.argv))
159
163
  .command(indexMoveCommand)
160
164
  .command(safetyCommand)
161
165
  .command(knowledgeAdvisorCommand)
166
+ .command(knowledgeCommand)
162
167
  .command(productCommand)
163
168
  .command(mcpKeyCommand)
164
169
  .command(opsCommand)
@@ -213,6 +218,7 @@ await yargs(hideBin(process.argv))
213
218
  .command(chatIntelligenceThemesCommand)
214
219
  .command(cannedLearningCommand)
215
220
  .command(chatIntelligenceTopicsCommand)
221
+ .command(csatOverrideUnapproveCommand)
216
222
  .command(focusCommand)
217
223
  .command(triageCommand)
218
224
  .command(automationDraftCommand)
@@ -257,6 +263,7 @@ await yargs(hideBin(process.argv))
257
263
  .command(routingCommand)
258
264
  .command(diagnosticsCommand)
259
265
  .command(aiSpendCommand)
266
+ .command(skillUsageCommand)
260
267
  .command(aiFederationCommand)
261
268
  .command(statusPageCommand)
262
269
  .command(statusNotifyCommand)
@@ -265,6 +272,7 @@ await yargs(hideBin(process.argv))
265
272
  .command(callbackCommand)
266
273
  .command(caseCommand)
267
274
  .command(pickupCommand)
275
+ .command(visitorMonitorCommand)
268
276
  .command(visitorLinkCommand)
269
277
  .command(applePayCommand)
270
278
  .command(siteBuilderCommand)
@@ -84,7 +84,7 @@ const aiConfigCreateCommand = {
84
84
  y
85
85
  .option('name', { type: 'string', describe: 'Configuration name' })
86
86
  .option('prompt', { type: 'string', describe: 'System prompt text' })
87
- .option('model', { type: 'string', describe: 'Model (e.g. gpt-4o-mini, claude-haiku-4-5)' })
87
+ .option('model', { type: 'string', describe: 'Model (e.g. gpt-4o-mini, claude-haiku-5-5)' })
88
88
  .option('tone', { type: 'string', describe: 'Tone (e.g. friendly, professional)' })
89
89
  .option('language', { type: 'string', default: 'en', describe: 'Language code' })
90
90
  .option('reply-length',{ type: 'string', describe: 'Reply length (e.g. short, medium, long)' })
@@ -0,0 +1,14 @@
1
+ import { messagingPost } from '../api.js';
2
+ import { runCommand } from '../run.js';
3
+
4
+ // velaro csat-override-unapprove <id>
5
+ // REST: messaging POST /api/intelligence/csat-override-candidates/{id}/unapprove (site comes from the CLI session)
6
+ export const csatOverrideUnapproveCommand = {
7
+ command: 'csat-override-unapprove <id>',
8
+ describe: 'Return an approved Chat Intelligence improvement candidate to Pending (deactivates the override, removes any vectorized copy)',
9
+ builder: (y) => y.positional('id', { type: 'number', describe: 'Approved override candidate id' }),
10
+ handler: runCommand(async (a) => {
11
+ if (!Number.isInteger(a.id) || a.id <= 0) throw new Error('id must be a positive integer');
12
+ console.log(JSON.stringify(await messagingPost(`/api/intelligence/csat-override-candidates/${a.id}/unapprove`, {}), null, 2));
13
+ }),
14
+ };
@@ -0,0 +1,114 @@
1
+ // velaro knowledge clinical-text get|set -- emergency and safety wording per channel and language.
2
+ // Backend: velaro-messaging KnowledgeSetupController. Customer routes take the site from auth
3
+ // (GET KnowledgeSetup/state, PUT KnowledgeSetup/clinical-text); --site switches to the staff twins
4
+ // (GET/PUT KnowledgeSetup/Staff/{siteId}/...), Velaro super admin only.
5
+ import fs from 'node:fs';
6
+ import { messagingGet, messagingPut } from '../api.js';
7
+ import { runCommand } from '../run.js';
8
+
9
+ const CHANNELS = ['chat', 'voice', 'sms', 'whatsapp', 'email'];
10
+
11
+ export function basePath(site) {
12
+ return site ? `/KnowledgeSetup/Staff/${encodeURIComponent(site)}` : '/KnowledgeSetup';
13
+ }
14
+
15
+ /** Key is default | channel | lang | lang.channel (es.voice). Value is the wording. */
16
+ export function parseEntries(list) {
17
+ const out = {};
18
+ for (const raw of [].concat(list ?? [])) {
19
+ const i = String(raw).indexOf('=');
20
+ if (i < 1) throw new Error(`Expected key=text (for example es.voice="..."), got: ${raw}`);
21
+ out[String(raw).slice(0, i).trim().toLowerCase()] = String(raw).slice(i + 1);
22
+ }
23
+ return out;
24
+ }
25
+
26
+ export function buildBody(argv) {
27
+ if (argv.reset) return { resetToDefault: true };
28
+ let body = {};
29
+ if (argv.file) body = JSON.parse(fs.readFileSync(argv.file, 'utf8'));
30
+ const em = parseEntries(argv.emergency);
31
+ const rf = parseEntries(argv.referral);
32
+ if (Object.keys(em).length) body.emergency = { ...(body.emergency ?? {}), ...em };
33
+ if (Object.keys(rf).length) body.referral = { ...(body.referral ?? {}), ...rf };
34
+ if (argv['emergency-number'] !== undefined) body.emergencyNumber = argv['emergency-number'];
35
+ if (argv['service-mode'] !== undefined || argv['service-mode-text'] !== undefined) {
36
+ body.emergencyServiceMode = {};
37
+ if (argv['service-mode'] !== undefined) body.emergencyServiceMode.enabled = argv['service-mode'];
38
+ if (argv['service-mode-text'] !== undefined) body.emergencyServiceMode.text = argv['service-mode-text'];
39
+ if (argv.confirm) body.emergencyServiceMode.confirm = true;
40
+ }
41
+ return body;
42
+ }
43
+
44
+ export function printState(ct) {
45
+ console.log(`\nEmergency number: ${ct.overrides?.emergencyNumber || `(default) ${ct.defaultEmergencyNumber}`}`);
46
+ console.log(`Limits: ${ct.limits?.minLength}-${ct.limits?.maxLength} characters. Emergency text must keep ${ct.placeholder}.`);
47
+ console.log(`Supported languages: ${(ct.supportedLanguages ?? []).join(', ') || '-'}`);
48
+ const m = ct.emergencyServiceMode;
49
+ console.log(`Emergency-service mode: ${m?.enabled ? 'ON' : 'off'}${m?.text ? ` | text: ${m.text}` : ''} (staff only)`);
50
+ console.log('Custom wording (key = text):');
51
+ for (const kind of ['emergency', 'referral']) {
52
+ const e = Object.entries(ct.overrides?.[kind] ?? {});
53
+ if (!e.length) console.log(` ${kind}: none, built-in wording`);
54
+ for (const [k, v] of e) console.log(` ${kind}.${k} = ${v}`);
55
+ }
56
+ console.log('Effective English wording per channel:');
57
+ for (const c of CHANNELS) {
58
+ const x = ct.effective?.[c];
59
+ if (x) console.log(` ${c}: emergency "${x.emergency}" | referral "${x.referral}"`);
60
+ }
61
+ }
62
+
63
+ const clinicalGet = {
64
+ command: 'get',
65
+ describe: 'Show emergency/safety wording, supported languages and emergency-service mode',
66
+ builder: (y) => y
67
+ .option('site', { type: 'number', describe: 'Target site ID (Velaro staff only; omit for your own site)' })
68
+ .option('json', { type: 'boolean', default: false }),
69
+ handler: runCommand(async (argv) => {
70
+ const state = await messagingGet(`${basePath(argv.site)}/state`);
71
+ const ct = state?.clinicalText;
72
+ if (!ct) throw new Error('The setup state did not include clinicalText (backend too old?).');
73
+ if (argv.json) { console.log(JSON.stringify(ct, null, 2)); return; }
74
+ printState(ct);
75
+ }),
76
+ };
77
+
78
+ const clinicalSet = {
79
+ command: 'set',
80
+ describe: 'Set emergency/safety wording. Entries replace the stored set: pass every override you want to keep (use get --json).',
81
+ builder: (y) => y
82
+ .option('site', { type: 'number', describe: 'Target site ID (Velaro staff only)' })
83
+ .option('emergency', { type: 'array', string: true, describe: 'key=text, key is default|chat|voice|sms|whatsapp|email|<lang>|<lang>.<channel>; must contain {emergencyNumber}' })
84
+ .option('referral', { type: 'array', string: true, describe: 'key=text, same keys as --emergency' })
85
+ .option('emergency-number', { type: 'string', describe: 'Replaces {emergencyNumber}' })
86
+ .option('file', { type: 'string', describe: 'JSON body { emergency, referral, emergencyNumber }' })
87
+ .option('reset', { type: 'boolean', default: false, describe: 'Clear all custom wording' })
88
+ .option('service-mode', { type: 'boolean', describe: 'Staff only (--site): turn emergency-service mode on/off' })
89
+ .option('service-mode-text', { type: 'string', describe: 'Staff only: required emergency text for service mode (20-600 chars)' })
90
+ .option('confirm', { type: 'boolean', default: false, describe: 'Required to turn service mode on' })
91
+ .option('json', { type: 'boolean', default: false }),
92
+ handler: runCommand(async (argv) => {
93
+ const body = buildBody(argv);
94
+ if (body.emergencyServiceMode && !argv.site) throw new Error('Emergency-service mode is staff only: pass --site <id>.');
95
+ const r = await messagingPut(`${basePath(argv.site)}/clinical-text`, body);
96
+ if (argv.json) { console.log(JSON.stringify(r, null, 2)); return; }
97
+ console.log(r.changed ? 'Saved.' : 'No change.');
98
+ if (r.clinicalText) printState(r.clinicalText);
99
+ }),
100
+ };
101
+
102
+ export const knowledgeCommand = {
103
+ command: 'knowledge <subcommand>',
104
+ describe: 'Knowledge setup safety settings',
105
+ builder: (y) => y
106
+ .command({
107
+ command: 'clinical-text <action>',
108
+ describe: 'Emergency and safety messages per channel and language',
109
+ builder: (yy) => yy.command(clinicalGet).command(clinicalSet).demandCommand(1, 'Specify get or set'),
110
+ handler: () => {},
111
+ })
112
+ .demandCommand(1, 'Specify a subcommand: clinical-text'),
113
+ handler: () => {},
114
+ };
@@ -0,0 +1,39 @@
1
+ import { messagingGet } from '../api.js';
2
+ import { runCommand } from '../run.js';
3
+
4
+ const usd = (n) => `$${Number(n ?? 0).toFixed(2)}`;
5
+
6
+ // Pure formatter so the output shape is unit-testable without a network call.
7
+ export function formatSkillUsage(r) {
8
+ const lines = [];
9
+ lines.push(`Site ${r.siteId}, ${r.month}${r.isTrial ? ' (trial)' : ''}, overage: ${r.overageBehavior}`);
10
+ lines.push('');
11
+ lines.push('Billable usage (same numbers as the customer Billing page):');
12
+ for (const u of r.usage ?? []) {
13
+ const limit = u.unlimited ? 'unlimited' : u.limit.toLocaleString();
14
+ lines.push(` ${u.name.padEnd(24)} ${String(u.used.toLocaleString()).padStart(9)} / ${limit.padStart(9)} ${u.unlimited ? '' : `${u.percent}%`.padStart(5)} gate: ${u.mode ?? 'n/a'}`);
15
+ }
16
+ lines.push('');
17
+ lines.push('Calls by surface (staff, copilot and composite calls are never billed to the customer):');
18
+ const surfaces = r.callsBySurface ?? [];
19
+ if (!surfaces.length) lines.push(' (none recorded)');
20
+ for (const s of surfaces) lines.push(` ${String(s.surface).padEnd(20)} ${s.calls.toLocaleString()}`);
21
+ lines.push('');
22
+ lines.push('LLM cost this month by key source (BYOK removes this cost only; skill usage above still counts):');
23
+ const llm = r.llmCostThisMonth ?? [];
24
+ if (!llm.length) lines.push(' (none logged)');
25
+ for (const l of llm) lines.push(` ${String(l.keySource ?? 'unknown').padEnd(12)} ${usd(l.costUsd)} / ${l.calls} calls`);
26
+ return lines.join('\n');
27
+ }
28
+
29
+ export const skillUsageCommand = {
30
+ command: 'skill-usage <siteId>',
31
+ describe: 'Per-customer skill/integration usage vs plan limits, by surface, with LLM cost split (Velaro staff only)',
32
+ builder: (y) => y
33
+ .positional('siteId', { type: 'number', describe: 'Target site ID' })
34
+ .option('json', { type: 'boolean', default: false }),
35
+ handler: runCommand(async (argv) => {
36
+ const r = await messagingGet(`/SupportTools/sites/${argv.siteId}/skill-usage`);
37
+ console.log(argv.json ? JSON.stringify(r, null, 2) : formatSkillUsage(r));
38
+ }),
39
+ };
@@ -4,7 +4,7 @@
4
4
  * velaro teams-phone status Show current configuration
5
5
  * velaro teams-phone set --tenant-id ... Create or update the configuration
6
6
  * velaro teams-phone remove Delete the configuration
7
- * velaro teams-phone test Test the Graph subscription connectivity
7
+ * velaro teams-phone test Run the inbound-calling readiness checks
8
8
  *
9
9
  * Backend contract: server/Velaro.Messaging/Controllers/Voice/TeamsPhoneConfigController.cs
10
10
  * (velaro-messaging). Site is resolved server-side from the auth context — never
@@ -34,6 +34,9 @@ function printConfig(c) {
34
34
  console.log(` Assigned phone number: ${c.assignedPhoneNumber ?? '(none)'}`);
35
35
  console.log(` Webhook secret configured: ${c.webhookSecretConfigured ? `${GRN}yes${RST}` : `${DIM}no${RST}`}`);
36
36
  const subColor = c.graphSubscriptionActive ? GRN : YEL;
37
+ if (typeof c.callingReady === 'boolean') {
38
+ console.log(` Inbound calling ready: ${c.callingReady ? GRN : YEL}${c.callingReady}${RST}`);
39
+ }
37
40
  console.log(` Graph subscription active: ${subColor}${c.graphSubscriptionActive}${RST}` +
38
41
  (c.graphSubscriptionExpiresAt ? ` (expires ${c.graphSubscriptionExpiresAt})` : ''));
39
42
  console.log('');
@@ -82,7 +85,16 @@ async function removeHandler() {
82
85
  async function testHandler() {
83
86
  const result = await messagingPost('/TeamsPhone/Config/Test', {});
84
87
  const color = result?.success ? GRN : RED;
85
- console.log(`\n${color}${result?.success ? 'Success' : 'Failed'}${RST}: ${result?.message ?? '(no message)'}\n`);
88
+ console.log(`\n${color}${result?.success ? 'Success' : 'Failed'}${RST}: ${result?.message ?? '(no message)'}`);
89
+ if (typeof result?.callingReady === 'boolean') {
90
+ console.log(` Inbound calling ready: ${result.callingReady ? GRN : RED}${result.callingReady}${RST}`);
91
+ }
92
+ if (Array.isArray(result?.checks)) {
93
+ for (const ck of result.checks) {
94
+ console.log(` ${ck?.ok ? GRN + 'PASS' : RED + 'FAIL'}${RST} ${ck?.name ?? ''}${ck?.message ? ': ' + ck.message : ''}`);
95
+ }
96
+ }
97
+ console.log('');
86
98
  if (!result?.success) process.exitCode = 1;
87
99
  }
88
100
 
@@ -121,7 +133,7 @@ export const teamsPhoneCommand = {
121
133
  })
122
134
  .command({
123
135
  command: 'test',
124
- describe: 'Test the Teams Phone connection by creating/renewing the Graph /communications/calls subscription',
136
+ describe: 'Run the Teams Phone readiness checks (consent, resource account, Graph reachability) and print each check',
125
137
  builder: yy => yy,
126
138
  handler: testHandler,
127
139
  })
@@ -0,0 +1,61 @@
1
+ import { messagingGet, messagingPut } from '../api.js';
2
+ import { runCommand } from '../run.js';
3
+
4
+ // ── velaro visitor-monitor ────────────────────────────────────────────────────
5
+ // Visitor Monitor view preferences (per user) and site settings. siteId is applied
6
+ // server-side from the JWT (see cli/lib/api.js messagingRequest), never a CLI arg.
7
+ //
8
+ // Backend contract: velaro-messaging VisitorController (PR 2798)
9
+ // GET/PUT Visitor/preferences { columns[], groupBy, sortBy, sortDir, regionGrouping }
10
+ // GET/PUT Visitor/settings { storeLocationDetail?, activeWindowMinutes? } (window 5-240; >30 needs Advanced plan, else 403 feature_not_in_plan)
11
+ // GET Visitor/active live sessions + activeWindowMinutes + locationDetailEnabled
12
+
13
+ const show = data => console.log(JSON.stringify(data, null, 2));
14
+
15
+ export const visitorMonitorCommand = {
16
+ command: 'visitor-monitor <subcommand>',
17
+ describe: 'Visitor Monitor preferences, settings and live visitors',
18
+ builder: yargs => yargs
19
+ .command({
20
+ command: 'active',
21
+ describe: 'List live visitors',
22
+ handler: runCommand(async () => show(await messagingGet('/Visitor/active'))),
23
+ })
24
+ .command({
25
+ command: 'preferences <action>',
26
+ describe: 'get or set your saved Visitor Monitor view',
27
+ builder: y => y
28
+ .positional('action', { choices: ['get', 'set'] })
29
+ .option('columns', { type: 'string', describe: 'Comma-separated column keys (set)' })
30
+ .option('group-by', { type: 'string', describe: 'Group by key (set)' })
31
+ .option('sort-by', { type: 'string', describe: 'Sort key (set)' })
32
+ .option('sort-dir', { type: 'string', choices: ['asc', 'desc'], describe: 'Sort direction (set)' })
33
+ .option('region-grouping', { type: 'string', describe: 'region or city (set)' }),
34
+ handler: runCommand(async argv => {
35
+ if (argv.action === 'get') return show(await messagingGet('/Visitor/preferences'));
36
+ const body = {};
37
+ if (argv.columns !== undefined) body.columns = String(argv.columns).split(',').map(s => s.trim()).filter(Boolean);
38
+ if (argv['group-by'] !== undefined) body.groupBy = argv['group-by'];
39
+ if (argv['sort-by'] !== undefined) body.sortBy = argv['sort-by'];
40
+ if (argv['sort-dir'] !== undefined) body.sortDir = argv['sort-dir'];
41
+ if (argv['region-grouping'] !== undefined) body.regionGrouping = argv['region-grouping'];
42
+ show(await messagingPut('/Visitor/preferences', body));
43
+ }),
44
+ })
45
+ .command({
46
+ command: 'settings <action>',
47
+ describe: 'get or set site Visitor Monitor settings',
48
+ builder: y => y
49
+ .positional('action', { choices: ['get', 'set'] })
50
+ .option('store-location-detail', { type: 'boolean', describe: 'Store region/city + truncated IP (Advanced plan) (set)' })
51
+ .option('active-window-minutes', { type: 'number', describe: 'Minutes visitors stay listed, 5-240 (above 30 needs Advanced) (set)' }),
52
+ handler: runCommand(async argv => {
53
+ if (argv.action === 'get') return show(await messagingGet('/Visitor/settings'));
54
+ const body = {};
55
+ if (argv['store-location-detail'] !== undefined) body.storeLocationDetail = argv['store-location-detail'];
56
+ if (argv['active-window-minutes'] !== undefined) body.activeWindowMinutes = argv['active-window-minutes'];
57
+ show(await messagingPut('/Visitor/settings', body));
58
+ }),
59
+ })
60
+ .demandCommand(1),
61
+ };
@@ -0,0 +1,36 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { formatSkillUsage } from './commands/skill-usage.js';
4
+
5
+ const sample = {
6
+ siteId: 1032,
7
+ month: '2026-10',
8
+ isTrial: true,
9
+ overageBehavior: 'block',
10
+ usage: [
11
+ { metric: 'ai_tool_calls', name: 'AI Tool Calls', used: 120, limit: 500, unlimited: false, percent: 24, mode: 'Enforce' },
12
+ { metric: 'api_calls', name: 'REST API Calls', used: 9, limit: 0, unlimited: true, percent: 0, mode: 'Observe' },
13
+ ],
14
+ callsBySurface: [{ surface: 'McpStaff', calls: 4 }],
15
+ llmCostThisMonth: [{ keySource: 'byok', costUsd: 1.5, calls: 3 }],
16
+ };
17
+
18
+ test('shows usage against the limit with the gate mode, and marks unlimited metrics', () => {
19
+ const out = formatSkillUsage(sample);
20
+ assert.match(out, /Site 1032, 2026-10 \(trial\), overage: block/);
21
+ assert.match(out, /AI Tool Calls\s+120 \/\s+500\s+24%\s+gate: Enforce/);
22
+ assert.match(out, /REST API Calls\s+9 \/\s+unlimited/);
23
+ });
24
+
25
+ test('lists calls by surface and the LLM cost split, saying BYOK removes LLM cost only', () => {
26
+ const out = formatSkillUsage(sample);
27
+ assert.match(out, /McpStaff\s+4/);
28
+ assert.match(out, /byok\s+\$1\.50 \/ 3 calls/);
29
+ assert.match(out, /BYOK removes this cost only/);
30
+ });
31
+
32
+ test('handles a site with no recorded usage without throwing', () => {
33
+ const out = formatSkillUsage({ siteId: 5, month: '2026-10', overageBehavior: 'warn' });
34
+ assert.match(out, /\(none recorded\)/);
35
+ assert.match(out, /\(none logged\)/);
36
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velaro/cli",
3
- "version": "1.4.105",
3
+ "version": "1.4.106",
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": {