@velaro/mcp-server 0.6.95 → 0.6.97
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/package.json +1 -1
- package/server.js +268 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.97",
|
|
4
4
|
"description": "Velaro MCP server — connect Claude and other AI agents directly to your Velaro account: KB, workflows, bots, conversations, contacts, routing, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/server.js
CHANGED
|
@@ -160,9 +160,17 @@ function avatarOverrideBody(args) {
|
|
|
160
160
|
for (const k of ['providerSlug', 'avatarId', 'personalityPresetSlug', 'voiceId', 'displayName', 'backgroundMode', 'backgroundValue']) {
|
|
161
161
|
if (args[k] !== undefined) body[k] = args[k];
|
|
162
162
|
}
|
|
163
|
+
if (args.motionLevel !== undefined) body.motionLevel = parseAvatarMotionLevel(args.motionLevel);
|
|
163
164
|
return body;
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
/** Closed vocabulary for avatar body motion; "" resets/inherits. Fails readably instead of a bare 400. */
|
|
168
|
+
function parseAvatarMotionLevel(raw) {
|
|
169
|
+
const level = String(raw).trim().toLowerCase();
|
|
170
|
+
if (!['', 'off', 'subtle', 'natural', 'expressive'].includes(level)) throw new Error('motionLevel must be off, subtle, natural or expressive (empty string resets/inherits).');
|
|
171
|
+
return level;
|
|
172
|
+
}
|
|
173
|
+
|
|
166
174
|
async function messagingRequest(method, path, body) {
|
|
167
175
|
if (!MESSAGING_API) throw new Error(
|
|
168
176
|
'VELARO_MESSAGING_API is not configured. Add "VELARO_MESSAGING_API": "https://velaro-messaging-api-staging.azurewebsites.net" to your MCP env config.'
|
|
@@ -1699,6 +1707,79 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
1699
1707
|
},
|
|
1700
1708
|
|
|
1701
1709
|
// ── Livefluence Migration ─────────────────────────────────────────────────
|
|
1710
|
+
{
|
|
1711
|
+
name: 'livefluence_site_workflows_list',
|
|
1712
|
+
description: 'Velaro staff only (superadmin). List ANOTHER v20 site\'s workflows: id, name, enabled, priority, action, team/AI target. Use after livefluence_migrate to see what exists on the destination site (e.g. 100140).',
|
|
1713
|
+
inputSchema: { type: 'object', properties: { site_id: { type: 'number', description: 'V20 site ID to inspect' } }, required: ['site_id'] },
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
name: 'livefluence_site_workflows_set_enabled',
|
|
1717
|
+
description: 'Velaro staff only (superadmin). Enable or disable workflows on a v20 site by ids or names. Idempotent, audit-logged. Enabling REFUSES a workflow whose trigger is not fully converted from v10 unless force_partial=true; pass v10_site_id to check each against its same-named v10 rule. Use dry_run=true first.',
|
|
1718
|
+
inputSchema: {
|
|
1719
|
+
type: 'object',
|
|
1720
|
+
properties: {
|
|
1721
|
+
site_id: { type: 'number', description: 'V20 site ID' },
|
|
1722
|
+
workflow_ids: { type: 'array', items: { type: 'number' } },
|
|
1723
|
+
names: { type: 'array', items: { type: 'string' } },
|
|
1724
|
+
enabled: { type: 'boolean', description: 'true = enable, false = disable' },
|
|
1725
|
+
v10_site_id: { type: 'number', description: 'Verify conversion against this v10 site rules' },
|
|
1726
|
+
force_partial: { type: 'boolean', description: 'Enable even if v10 expression terms were not converted' },
|
|
1727
|
+
dry_run: { type: 'boolean' },
|
|
1728
|
+
},
|
|
1729
|
+
required: ['site_id', 'enabled'],
|
|
1730
|
+
},
|
|
1731
|
+
},
|
|
1732
|
+
{
|
|
1733
|
+
name: 'livefluence_site_workflow_create',
|
|
1734
|
+
description: 'Velaro staff only (superadmin). Create a workflow on a v20 site from a nodes-blob JSON (object of nodes with a trigger node). Disabled unless enabled=true. Idempotent by name; validates that referenced AI configs/teams exist on the site. Use dry_run=true first.',
|
|
1735
|
+
inputSchema: {
|
|
1736
|
+
type: 'object',
|
|
1737
|
+
properties: {
|
|
1738
|
+
site_id: { type: 'number' },
|
|
1739
|
+
name: { type: 'string' },
|
|
1740
|
+
nodes_blob: { type: 'string', description: 'JSON string of the nodes object' },
|
|
1741
|
+
trigger_type: { type: 'string', description: 'Default conversation_opened' },
|
|
1742
|
+
enabled: { type: 'boolean' },
|
|
1743
|
+
dry_run: { type: 'boolean' },
|
|
1744
|
+
},
|
|
1745
|
+
required: ['site_id', 'name', 'nodes_blob'],
|
|
1746
|
+
},
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
name: 'livefluence_site_workflow_set_action',
|
|
1750
|
+
description: 'Velaro staff only (superadmin). Set a workflow action node on a v20 site to transferToAI {aiConfigId} or routeToTeam {teamId}. Validates the target exists on that site. Idempotent, audit-logged.',
|
|
1751
|
+
inputSchema: {
|
|
1752
|
+
type: 'object',
|
|
1753
|
+
properties: {
|
|
1754
|
+
site_id: { type: 'number' },
|
|
1755
|
+
workflow_id: { type: 'number' },
|
|
1756
|
+
action_type: { type: 'string', enum: ['transferToAI', 'routeToTeam'] },
|
|
1757
|
+
target_id: { type: 'number', description: 'aiConfigId for transferToAI, teamId for routeToTeam' },
|
|
1758
|
+
dry_run: { type: 'boolean' },
|
|
1759
|
+
},
|
|
1760
|
+
required: ['site_id', 'workflow_id', 'action_type', 'target_id'],
|
|
1761
|
+
},
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
name: 'livefluence_site_rules_list',
|
|
1765
|
+
description: 'Velaro staff only (superadmin). List a v20 site Rules (e.g. the use-ai rules seeded by livefluence_seed_url_rules) with enabled state.',
|
|
1766
|
+
inputSchema: { type: 'object', properties: { site_id: { type: 'number' } }, required: ['site_id'] },
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
name: 'livefluence_site_rules_set_enabled',
|
|
1770
|
+
description: 'Velaro staff only (superadmin). Switch v20 Rules off/on without deleting them (reversible), by rule ids or by trigger (e.g. use-ai). Use to turn off the url-rules seeded earlier so they do not double-route with migrated workflows. Idempotent, audit-logged.',
|
|
1771
|
+
inputSchema: {
|
|
1772
|
+
type: 'object',
|
|
1773
|
+
properties: {
|
|
1774
|
+
site_id: { type: 'number' },
|
|
1775
|
+
rule_ids: { type: 'array', items: { type: 'number' } },
|
|
1776
|
+
trigger: { type: 'string', description: 'Every rule with this trigger, e.g. use-ai' },
|
|
1777
|
+
enabled: { type: 'boolean' },
|
|
1778
|
+
dry_run: { type: 'boolean' },
|
|
1779
|
+
},
|
|
1780
|
+
required: ['site_id', 'enabled'],
|
|
1781
|
+
},
|
|
1782
|
+
},
|
|
1702
1783
|
{
|
|
1703
1784
|
name: 'livefluence_seed_url_rules',
|
|
1704
1785
|
description: 'Velaro staff only. Seed v20 use-ai routing rules on a target site from v10 URL rules. Each rule matches "url contains <path>" with scheme/host stripped (path-only), so a demo page path triggers the same rule as the customer real URL. Idempotent. Domain-only URLs are skipped. Use dry_run=true first.',
|
|
@@ -2160,6 +2241,20 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
2160
2241
|
required: ['jobId'],
|
|
2161
2242
|
},
|
|
2162
2243
|
},
|
|
2244
|
+
{
|
|
2245
|
+
name: 'index_create',
|
|
2246
|
+
description: 'Velaro staff only. Create a named virtual knowledge index on ANY site (e.g. "100140-troubleshooting", "100140-manual-catalog") so uploads/publishes with that indexName work. Idempotent: returns the existing index if the name already exists. Respects the site index quota. Wraps POST KnowledgeSetup/Staff/{siteId}/create-index.',
|
|
2247
|
+
inputSchema: {
|
|
2248
|
+
type: 'object',
|
|
2249
|
+
properties: {
|
|
2250
|
+
siteId: { type: 'number', description: 'Target site ID' },
|
|
2251
|
+
indexName: { type: 'string', description: 'Index name (lowercased, a-z 0-9 and dashes)' },
|
|
2252
|
+
purpose: { type: 'string', enum: ['general', 'troubleshooting', 'catalog'], description: 'Index purpose (default general)' },
|
|
2253
|
+
excludeFromAllSources: { type: 'boolean', description: 'Keep out of the ["*"] all-sources search' },
|
|
2254
|
+
},
|
|
2255
|
+
required: ['siteId', 'indexName'],
|
|
2256
|
+
},
|
|
2257
|
+
},
|
|
2163
2258
|
{
|
|
2164
2259
|
name: 'uploaded_file_reindex',
|
|
2165
2260
|
description: 'Re-index an already-uploaded AI training file from blob storage — use when a file shows 0 chunks (e.g. PDF uploaded before PDF support was added). Reads from blob and re-runs full ingestion. Use index_status after ~30s to verify chunks appear.',
|
|
@@ -3299,7 +3394,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3299
3394
|
// ── AI Video Avatar management (superadmin) ────────────────────────────────
|
|
3300
3395
|
{
|
|
3301
3396
|
name: 'avatar_get_config',
|
|
3302
|
-
description: 'Get the AI Video Avatar (Simli/D-ID/HeyGen) configuration for any customer site — provider, avatar/face ID, voice ID, enabled state, "call me" option, callback confirm window. Velaro admin only. Never returns provider API keys (Velaro uses its own master keys, never per-site).',
|
|
3397
|
+
description: 'Get the AI Video Avatar (Simli/D-ID/HeyGen) configuration for any customer site — provider, avatar/face ID, voice ID, enabled state, "call me" option, callback confirm window, body motion level and strength. Velaro admin only. Never returns provider API keys (Velaro uses its own master keys, never per-site).',
|
|
3303
3398
|
inputSchema: {
|
|
3304
3399
|
type: 'object',
|
|
3305
3400
|
properties: {
|
|
@@ -3325,6 +3420,8 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3325
3420
|
showBadge: { type: 'boolean', description: 'Show the name badge over the avatar video. Default true.' },
|
|
3326
3421
|
sizePx: { type: 'number', description: 'Max avatar panel width in px (160-640, square). Default 360.' },
|
|
3327
3422
|
allowVisitorCollapse: { type: 'boolean', description: 'Let visitors hide or collapse the avatar panel (up/down arrow and X). Default true.' },
|
|
3423
|
+
motionLevel: { type: 'string', enum: ['', 'off', 'subtle', 'natural', 'expressive'], description: 'Body motion preset layered over the lip-sync (shoulders, breathing, head, brows). Empty string resets to the platform default (natural). Visitors with reduced-motion enabled always get a still avatar.' },
|
|
3424
|
+
motionIntensityPercent: { type: 'integer', minimum: 25, maximum: 150, description: 'Body motion strength, whole percent 25-150 (default 100); multiplies every motion amplitude.' },
|
|
3328
3425
|
maxAvatarMinutesPerMonth: { type: 'number', description: 'Monthly avatar video minutes cap for the site. Whole number 1-100000; never unlimited (0/negative rejected).' },
|
|
3329
3426
|
maxAvatarTtsChars: { type: 'number', description: 'Monthly avatar TTS character cap (avatar_tts_chars) for the site. Whole number 1-100000000; never unlimited (0/negative rejected). Overage pricing is a platform-wide ladder, not per site.' },
|
|
3330
3427
|
},
|
|
@@ -3374,6 +3471,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3374
3471
|
displayName: { type: 'string', description: 'Visitor-facing avatar name.' },
|
|
3375
3472
|
backgroundMode: { type: 'string', description: 'none | color | gradient | image. "none" = explicitly no backdrop.' },
|
|
3376
3473
|
backgroundValue: { type: 'string', description: 'Hex color, "#a,#b" gradient, or https image URL.' },
|
|
3474
|
+
motionLevel: { type: 'string', enum: ['', 'off', 'subtle', 'natural', 'expressive'], description: 'Body motion preset for this group (perWorkflow ?? perGroup ?? perSite ?? natural). Empty string inherits.' },
|
|
3377
3475
|
replace: { type: 'boolean', description: 'true = replace the whole override (omitted fields inherit) instead of merging.' },
|
|
3378
3476
|
},
|
|
3379
3477
|
required: ['groupId'],
|
|
@@ -3403,6 +3501,7 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
3403
3501
|
displayName: { type: 'string' },
|
|
3404
3502
|
backgroundMode: { type: 'string', description: 'none | color | gradient | image' },
|
|
3405
3503
|
backgroundValue: { type: 'string' },
|
|
3504
|
+
motionLevel: { type: 'string', enum: ['', 'off', 'subtle', 'natural', 'expressive'], description: 'Body motion preset for this workflow. Empty string inherits.' },
|
|
3406
3505
|
replace: { type: 'boolean' },
|
|
3407
3506
|
},
|
|
3408
3507
|
required: ['workflowId'],
|
|
@@ -5246,6 +5345,11 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
5246
5345
|
description: 'Hosted public job pages for this site: is the package included, are the pages published, and the opaque board link, sitemap, Indeed feed and Google feed URLs (the feed URLs contain a secret key). Returns { entitled, enabled, boardUrl, sitemapUrl, indeedFeedUrl, googleFeedUrl, publicApplyEnabled, eeocSelfIdAvailable, liveListings }; { entitled:false } without the package.',
|
|
5247
5346
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5248
5347
|
},
|
|
5348
|
+
{
|
|
5349
|
+
name: 'jobboard_applicant_accounts_status',
|
|
5350
|
+
description: 'Applicant accounts on the hosted job board (applicant sign-in with an emailed one-time code, my applications, saved jobs, resume library). Returns { entitled, hostedEnabled, signInUrl, accounts, signedInLast30Days, deletionPending, savedJobs, linkedApplications } (counts only, never emails or resumes); { entitled:false } without the Job Board applicant accounts package.',
|
|
5351
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5352
|
+
},
|
|
5249
5353
|
{
|
|
5250
5354
|
name: 'jobboard_hosted_set',
|
|
5251
5355
|
description: 'Publish or unpublish the hosted job pages (apply-if-present). enabled=true issues the opaque board link and feed key the first time. regenerateLink=true issues a NEW board link and every old hosted URL answers 404 (administrators only). Requires the Job Board hosted pages package (403 otherwise).',
|
|
@@ -5292,6 +5396,60 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
5292
5396
|
required: ['applicationId', 'stage'],
|
|
5293
5397
|
},
|
|
5294
5398
|
},
|
|
5399
|
+
// -- Job Board employers + moderation (package "job-board-employers", default OFF: 403 job_board_employers_not_entitled without it) --
|
|
5400
|
+
{
|
|
5401
|
+
name: 'jobboard_employers_list',
|
|
5402
|
+
description: 'List the employers on this site\'s job board with status, trusted flag (first posting approved), member count, live/total listings and how many listings wait for moderation. Requires the Job board employers package (403 otherwise).',
|
|
5403
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5404
|
+
},
|
|
5405
|
+
{
|
|
5406
|
+
name: 'jobboard_employer_create',
|
|
5407
|
+
description: 'Create an employer on the job board (name 2-200 characters, unique per site; optional verified email domain). Administrator only. Invite its first member with jobboard_employer_invite.',
|
|
5408
|
+
inputSchema: {
|
|
5409
|
+
type: 'object',
|
|
5410
|
+
properties: { name: { type: 'string' }, verifiedDomain: { type: 'string', description: 'Optional, e.g. example.com' } },
|
|
5411
|
+
required: ['name'],
|
|
5412
|
+
},
|
|
5413
|
+
},
|
|
5414
|
+
{
|
|
5415
|
+
name: 'jobboard_employer_invite',
|
|
5416
|
+
description: 'Email a single-use invitation (valid 7 days) to join an employer as Owner, Recruiter (default) or Viewer. Employers manage only their own listings and applicants and never see EEO data. Administrator only; 409 if the email already belongs to another employer or the hosted board is off.',
|
|
5417
|
+
inputSchema: {
|
|
5418
|
+
type: 'object',
|
|
5419
|
+
properties: {
|
|
5420
|
+
employerId: { type: 'number' },
|
|
5421
|
+
email: { type: 'string' },
|
|
5422
|
+
displayName: { type: 'string' },
|
|
5423
|
+
role: { type: 'string', description: 'Owner | Recruiter | Viewer (default Recruiter)' },
|
|
5424
|
+
},
|
|
5425
|
+
required: ['employerId', 'email'],
|
|
5426
|
+
},
|
|
5427
|
+
},
|
|
5428
|
+
{
|
|
5429
|
+
name: 'jobboard_employer_suspend',
|
|
5430
|
+
description: 'Suspend an employer: its members are signed out at once and every published listing is paused (off the public board). A reason of 3-500 characters is required. Administrator only.',
|
|
5431
|
+
inputSchema: { type: 'object', properties: { employerId: { type: 'number' }, reason: { type: 'string' } }, required: ['employerId', 'reason'] },
|
|
5432
|
+
},
|
|
5433
|
+
{
|
|
5434
|
+
name: 'jobboard_employer_reactivate',
|
|
5435
|
+
description: 'Reactivate a suspended employer. Its paused listings stay paused until the employer resumes them. Administrator only.',
|
|
5436
|
+
inputSchema: { type: 'object', properties: { employerId: { type: 'number' } }, required: ['employerId'] },
|
|
5437
|
+
},
|
|
5438
|
+
{
|
|
5439
|
+
name: 'jobboard_moderation_list',
|
|
5440
|
+
description: 'The moderation queue: employer listings waiting for a decision (an employer\'s first posting, or any posting that matched a scam signal such as fee requests, SSN/bank requests, gift cards, wire transfers, WhatsApp-only contact). Each item carries the matched flags. Pending listings are invisible on the public board.',
|
|
5441
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
5442
|
+
},
|
|
5443
|
+
{
|
|
5444
|
+
name: 'jobboard_moderation_approve',
|
|
5445
|
+
description: 'Approve a pending employer listing: it goes live, the employer becomes trusted (later clean postings publish at once), and the decision is audited. Administrator only; 409 unless the listing is waiting for moderation.',
|
|
5446
|
+
inputSchema: { type: 'object', properties: { listingId: { type: 'number' } }, required: ['listingId'] },
|
|
5447
|
+
},
|
|
5448
|
+
{
|
|
5449
|
+
name: 'jobboard_moderation_reject',
|
|
5450
|
+
description: 'Reject a pending employer listing with a reason (3-500 characters) the employer will see; the listing stays hidden and the decision is audited. Administrator only.',
|
|
5451
|
+
inputSchema: { type: 'object', properties: { listingId: { type: 'number' }, reason: { type: 'string' } }, required: ['listingId', 'reason'] },
|
|
5452
|
+
},
|
|
5295
5453
|
// -- Job Board pipeline (package "job-board-pipeline", default OFF: 403 job_board_pipeline_not_entitled without it, except jobboard_pipeline_get) --
|
|
5296
5454
|
{
|
|
5297
5455
|
name: 'jobboard_pipeline_get',
|
|
@@ -7145,6 +7303,47 @@ async function handleTool(name, args) {
|
|
|
7145
7303
|
return JSON.stringify(result, null, 2);
|
|
7146
7304
|
}
|
|
7147
7305
|
|
|
7306
|
+
case 'livefluence_site_workflows_list': {
|
|
7307
|
+
const result = await api('GET', `/DatabaseTool/SiteWorkflows/${args.site_id}`);
|
|
7308
|
+
return JSON.stringify(result, null, 2);
|
|
7309
|
+
}
|
|
7310
|
+
|
|
7311
|
+
case 'livefluence_site_workflows_set_enabled': {
|
|
7312
|
+
const result = await api('POST', '/DatabaseTool/SiteWorkflows/SetEnabled', {
|
|
7313
|
+
siteId: args.site_id, workflowIds: args.workflow_ids ?? [], names: args.names ?? [],
|
|
7314
|
+
enabled: !!args.enabled, dryRun: !!args.dry_run, forcePartial: !!args.force_partial, v10SiteId: args.v10_site_id ?? null,
|
|
7315
|
+
});
|
|
7316
|
+
return JSON.stringify(result, null, 2);
|
|
7317
|
+
}
|
|
7318
|
+
|
|
7319
|
+
case 'livefluence_site_workflow_create': {
|
|
7320
|
+
const result = await api('POST', '/DatabaseTool/SiteWorkflows/Create', {
|
|
7321
|
+
siteId: args.site_id, name: args.name, triggerType: args.trigger_type ?? 'conversation_opened',
|
|
7322
|
+
nodesBlob: typeof args.nodes_blob === 'string' ? args.nodes_blob : JSON.stringify(args.nodes_blob),
|
|
7323
|
+
enabled: !!args.enabled, dryRun: !!args.dry_run,
|
|
7324
|
+
});
|
|
7325
|
+
return JSON.stringify(result, null, 2);
|
|
7326
|
+
}
|
|
7327
|
+
|
|
7328
|
+
case 'livefluence_site_workflow_set_action': {
|
|
7329
|
+
const result = await api('POST', '/DatabaseTool/SiteWorkflows/SetAction', {
|
|
7330
|
+
siteId: args.site_id, workflowId: args.workflow_id, actionType: args.action_type, targetId: args.target_id, dryRun: !!args.dry_run,
|
|
7331
|
+
});
|
|
7332
|
+
return JSON.stringify(result, null, 2);
|
|
7333
|
+
}
|
|
7334
|
+
|
|
7335
|
+
case 'livefluence_site_rules_list': {
|
|
7336
|
+
const result = await api('GET', `/DatabaseTool/SiteRules/${args.site_id}`);
|
|
7337
|
+
return JSON.stringify(result, null, 2);
|
|
7338
|
+
}
|
|
7339
|
+
|
|
7340
|
+
case 'livefluence_site_rules_set_enabled': {
|
|
7341
|
+
const result = await api('POST', '/DatabaseTool/SiteRules/SetEnabled', {
|
|
7342
|
+
siteId: args.site_id, ruleIds: args.rule_ids ?? [], trigger: args.trigger ?? null, enabled: !!args.enabled, dryRun: !!args.dry_run,
|
|
7343
|
+
});
|
|
7344
|
+
return JSON.stringify(result, null, 2);
|
|
7345
|
+
}
|
|
7346
|
+
|
|
7148
7347
|
case 'livefluence_seed_url_rules': {
|
|
7149
7348
|
const result = await api('POST', '/DatabaseTool/V10SeedUrlRules', {
|
|
7150
7349
|
siteId: args.site_id, aiConfigId: args.ai_config_id, dryRun: !!args.dry_run, rules: args.rules,
|
|
@@ -7626,6 +7825,15 @@ async function handleTool(name, args) {
|
|
|
7626
7825
|
return lines.join('\n');
|
|
7627
7826
|
}
|
|
7628
7827
|
|
|
7828
|
+
case 'index_create': {
|
|
7829
|
+
if (!args.siteId || !args.indexName) return 'siteId and indexName are required.';
|
|
7830
|
+
const body = { indexName: args.indexName, purpose: args.purpose ?? 'general' };
|
|
7831
|
+
if (args.excludeFromAllSources !== undefined) body.excludeFromAllSources = args.excludeFromAllSources;
|
|
7832
|
+
const idx = await messagingRequest('POST', `/KnowledgeSetup/Staff/${encodeURIComponent(args.siteId)}/create-index`, body);
|
|
7833
|
+
if (idx?.error) return `Create index error: ${idx.error}`;
|
|
7834
|
+
return `Index ready on site ${args.siteId}: ${idx?.name} (id=${idx?.id}, purpose=${idx?.purpose}, excludeFromAllSources=${!!idx?.excludeFromAllSources}).`;
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7629
7837
|
case 'uploaded_files_list': {
|
|
7630
7838
|
const files = await messagingRequest('GET', '/uploadedFiles/list/AI');
|
|
7631
7839
|
if (!Array.isArray(files) || !files.length) return 'No AI training files uploaded for this site.';
|
|
@@ -8937,6 +9145,7 @@ async function handleTool(name, args) {
|
|
|
8937
9145
|
` Display name: ${data.displayName || '(AI)'}`,
|
|
8938
9146
|
` Badge shown: ${data.showBadge}`,
|
|
8939
9147
|
` Size: ${data.sizePx}px`,
|
|
9148
|
+
` Body motion: ${data.motionLevel || '(default: natural)'} @ ${data.motionIntensityPercent ?? 100}%`,
|
|
8940
9149
|
].join('\n');
|
|
8941
9150
|
}
|
|
8942
9151
|
|
|
@@ -8952,6 +9161,11 @@ async function handleTool(name, args) {
|
|
|
8952
9161
|
if (args.showBadge !== undefined) body.showBadge = args.showBadge;
|
|
8953
9162
|
if (args.sizePx !== undefined) body.sizePx = args.sizePx;
|
|
8954
9163
|
if (args.allowVisitorCollapse !== undefined) body.allowVisitorCollapse = args.allowVisitorCollapse;
|
|
9164
|
+
if (args.motionLevel !== undefined) body.motionLevel = parseAvatarMotionLevel(args.motionLevel);
|
|
9165
|
+
if (args.motionIntensityPercent !== undefined) {
|
|
9166
|
+
if (!Number.isInteger(args.motionIntensityPercent) || args.motionIntensityPercent < 25 || args.motionIntensityPercent > 150) throw new Error('motionIntensityPercent must be a whole number between 25 and 150.');
|
|
9167
|
+
body.motionIntensityPercent = args.motionIntensityPercent;
|
|
9168
|
+
}
|
|
8955
9169
|
for (const [arg, max] of [['maxAvatarMinutesPerMonth', 100000], ['maxAvatarTtsChars', 100000000]]) {
|
|
8956
9170
|
if (args[arg] === undefined) continue;
|
|
8957
9171
|
if (!Number.isInteger(args[arg]) || args[arg] < 1 || args[arg] > max) throw new Error(`${arg} must be a whole number between 1 and ${max} (never unlimited).`);
|
|
@@ -10720,6 +10934,10 @@ async function handleTool(name, args) {
|
|
|
10720
10934
|
const r = await messagingGet('/Recruiting/Hosted');
|
|
10721
10935
|
return JSON.stringify(r, null, 2);
|
|
10722
10936
|
}
|
|
10937
|
+
case 'jobboard_applicant_accounts_status': {
|
|
10938
|
+
const r = await messagingGet('/Recruiting/Applicants');
|
|
10939
|
+
return JSON.stringify(r, null, 2);
|
|
10940
|
+
}
|
|
10723
10941
|
case 'jobboard_hosted_set': {
|
|
10724
10942
|
const body = {};
|
|
10725
10943
|
if (args.enabled !== undefined) body.enabled = Boolean(args.enabled);
|
|
@@ -10758,6 +10976,55 @@ async function handleTool(name, args) {
|
|
|
10758
10976
|
return JSON.stringify(r, null, 2);
|
|
10759
10977
|
}
|
|
10760
10978
|
|
|
10979
|
+
case 'jobboard_employers_list': {
|
|
10980
|
+
return JSON.stringify(await messagingGet('/Recruiting/Employers'), null, 2);
|
|
10981
|
+
}
|
|
10982
|
+
case 'jobboard_employer_create': {
|
|
10983
|
+
const name = String(args.name ?? '').trim();
|
|
10984
|
+
if (name.length < 2 || name.length > 200) throw new Error('name must be 2 to 200 characters.');
|
|
10985
|
+
const body = { name };
|
|
10986
|
+
if (args.verifiedDomain !== undefined && String(args.verifiedDomain).trim()) body.verifiedDomain = String(args.verifiedDomain).trim();
|
|
10987
|
+
return JSON.stringify(await messagingPost('/Recruiting/Employers', body), null, 2);
|
|
10988
|
+
}
|
|
10989
|
+
case 'jobboard_employer_invite': {
|
|
10990
|
+
const id = Number(args.employerId);
|
|
10991
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('employerId must be a positive integer.');
|
|
10992
|
+
const email = String(args.email ?? '').trim();
|
|
10993
|
+
if (!/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(email)) throw new Error('email must be a valid email address.');
|
|
10994
|
+
const role = ['Owner', 'Recruiter', 'Viewer'].find((r) => r.toLowerCase() === String(args.role ?? 'Recruiter').trim().toLowerCase());
|
|
10995
|
+
if (!role) throw new Error('role must be Owner, Recruiter or Viewer.');
|
|
10996
|
+
const body = { email, role };
|
|
10997
|
+
if (args.displayName !== undefined && String(args.displayName).trim()) body.displayName = String(args.displayName).trim();
|
|
10998
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Employers/${id}/Invite`, body), null, 2);
|
|
10999
|
+
}
|
|
11000
|
+
case 'jobboard_employer_suspend': {
|
|
11001
|
+
const id = Number(args.employerId);
|
|
11002
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('employerId must be a positive integer.');
|
|
11003
|
+
const reason = String(args.reason ?? '').trim();
|
|
11004
|
+
if (reason.length < 3 || reason.length > 500) throw new Error('reason must be 3 to 500 characters.');
|
|
11005
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Employers/${id}/Suspend`, { reason }), null, 2);
|
|
11006
|
+
}
|
|
11007
|
+
case 'jobboard_employer_reactivate': {
|
|
11008
|
+
const id = Number(args.employerId);
|
|
11009
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('employerId must be a positive integer.');
|
|
11010
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Employers/${id}/Reactivate`, {}), null, 2);
|
|
11011
|
+
}
|
|
11012
|
+
case 'jobboard_moderation_list': {
|
|
11013
|
+
return JSON.stringify(await messagingGet('/Recruiting/Moderation'), null, 2);
|
|
11014
|
+
}
|
|
11015
|
+
case 'jobboard_moderation_approve': {
|
|
11016
|
+
const id = Number(args.listingId);
|
|
11017
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
11018
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Moderation/${id}/Approve`, {}), null, 2);
|
|
11019
|
+
}
|
|
11020
|
+
case 'jobboard_moderation_reject': {
|
|
11021
|
+
const id = Number(args.listingId);
|
|
11022
|
+
if (!Number.isInteger(id) || id <= 0) throw new Error('listingId must be a positive integer.');
|
|
11023
|
+
const reason = String(args.reason ?? '').trim();
|
|
11024
|
+
if (reason.length < 3 || reason.length > 500) throw new Error('reason must be 3 to 500 characters.');
|
|
11025
|
+
return JSON.stringify(await messagingPost(`/Recruiting/Moderation/${id}/Reject`, { reason }), null, 2);
|
|
11026
|
+
}
|
|
11027
|
+
|
|
10761
11028
|
case 'jobboard_move_application_stage': {
|
|
10762
11029
|
const id = Number(args.applicationId);
|
|
10763
11030
|
if (!Number.isInteger(id) || id <= 0) throw new Error('applicationId must be a positive integer.');
|