@startanaicompany/crm 2.5.1 → 2.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/index.js +176 -19
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -438,6 +438,8 @@ leadsCmd
|
|
|
438
438
|
.option('--status <status>', 'Filter by status')
|
|
439
439
|
.option('--email <email>', 'Filter by email (partial match)')
|
|
440
440
|
.option('--company <company>', 'Filter by company (partial match)')
|
|
441
|
+
.option('--name <name>', 'Search by name or company (partial match)')
|
|
442
|
+
.option('--pipeline-stage <stage>', 'Filter by pipeline stage (exact match)')
|
|
441
443
|
.option('--tag <tag>', 'Filter by tag — repeatable, AND logic', (v, prev) => prev.concat([v]), [])
|
|
442
444
|
.option('--api-key-id <id>', 'Filter by the API key ID that created the lead')
|
|
443
445
|
.option('--self', 'Filter leads created by the current API key (shorthand for --api-key-id self)')
|
|
@@ -456,6 +458,8 @@ leadsCmd
|
|
|
456
458
|
...(opts.status && { status: opts.status }),
|
|
457
459
|
...(opts.email && { email: opts.email }),
|
|
458
460
|
...(opts.company && { company: opts.company }),
|
|
461
|
+
...(opts.name && { name: opts.name }),
|
|
462
|
+
...(opts.pipelineStage && { pipeline_stage: opts.pipelineStage }),
|
|
459
463
|
...(apiKeyIdFilter && { api_key_id: apiKeyIdFilter }),
|
|
460
464
|
...(opts.createdAfter && { created_after: opts.createdAfter }),
|
|
461
465
|
...(opts.createdBefore && { created_before: opts.createdBefore }),
|
|
@@ -501,27 +505,46 @@ leadsCmd
|
|
|
501
505
|
.option('--deal-value <amount>', 'Deal value (numeric)')
|
|
502
506
|
.option('--notes <notes>', 'New notes')
|
|
503
507
|
.option('--assigned-to <assignedTo>', 'New assigned-to')
|
|
504
|
-
.option('--tag <tag>', 'Replace tags (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
508
|
+
.option('--tag <tag>', 'Replace ALL tags (repeatable). Use --add-tag / --remove-tag for surgical edits.', (v, prev) => prev.concat([v]), [])
|
|
509
|
+
.option('--add-tag <tag>', 'Add a single tag without replacing others (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
510
|
+
.option('--remove-tag <tag>', 'Remove a single tag (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
505
511
|
.option('--external-id <externalId>', 'New external ID')
|
|
506
512
|
.option('--version <version>', 'Optimistic lock version')
|
|
507
513
|
.action(async (id, opts) => {
|
|
508
514
|
const globalOpts = program.opts();
|
|
509
515
|
const client = getClient(globalOpts);
|
|
510
|
-
const body = {
|
|
511
|
-
...(opts.name && { name: opts.name }),
|
|
512
|
-
...(opts.email && { email: opts.email }),
|
|
513
|
-
...(opts.phone !== undefined && { phone: opts.phone }),
|
|
514
|
-
...(opts.company !== undefined && { company: opts.company }),
|
|
515
|
-
...(opts.status && { status: opts.status }),
|
|
516
|
-
...(opts.source && { source: opts.source }),
|
|
517
|
-
...(opts.dealValue !== undefined && { deal_value: parseFloat(opts.dealValue) }),
|
|
518
|
-
...(opts.notes !== undefined && { notes: opts.notes }),
|
|
519
|
-
...(opts.assignedTo !== undefined && { assigned_to: opts.assignedTo }),
|
|
520
|
-
...(opts.tag.length > 0 && { tags: opts.tag }),
|
|
521
|
-
...(opts.externalId !== undefined && { external_id: opts.externalId }),
|
|
522
|
-
...(opts.version !== undefined && { version: parseInt(opts.version) })
|
|
523
|
-
};
|
|
524
516
|
try {
|
|
517
|
+
// Surgical tag ops: call PATCH /leads/:id/tags first if add/remove specified
|
|
518
|
+
if (opts.addTag.length > 0 || opts.removeTag.length > 0) {
|
|
519
|
+
const tagRes = await client.patch(`/leads/${id}/tags`, {
|
|
520
|
+
...(opts.addTag.length > 0 && { add_tags: opts.addTag }),
|
|
521
|
+
...(opts.removeTag.length > 0 && { remove_tags: opts.removeTag })
|
|
522
|
+
});
|
|
523
|
+
// If no other fields to update, return tag result
|
|
524
|
+
const hasOtherFields = opts.name || opts.email || opts.phone !== undefined ||
|
|
525
|
+
opts.company !== undefined || opts.status || opts.source ||
|
|
526
|
+
opts.dealValue !== undefined || opts.notes !== undefined ||
|
|
527
|
+
opts.assignedTo !== undefined || opts.tag.length > 0 ||
|
|
528
|
+
opts.externalId !== undefined;
|
|
529
|
+
if (!hasOtherFields) {
|
|
530
|
+
printJSON(tagRes.data);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const body = {
|
|
535
|
+
...(opts.name && { name: opts.name }),
|
|
536
|
+
...(opts.email && { email: opts.email }),
|
|
537
|
+
...(opts.phone !== undefined && { phone: opts.phone }),
|
|
538
|
+
...(opts.company !== undefined && { company: opts.company }),
|
|
539
|
+
...(opts.status && { status: opts.status }),
|
|
540
|
+
...(opts.source && { source: opts.source }),
|
|
541
|
+
...(opts.dealValue !== undefined && { deal_value: parseFloat(opts.dealValue) }),
|
|
542
|
+
...(opts.notes !== undefined && { notes: opts.notes }),
|
|
543
|
+
...(opts.assignedTo !== undefined && { assigned_to: opts.assignedTo }),
|
|
544
|
+
...(opts.tag.length > 0 && { tags: opts.tag }),
|
|
545
|
+
...(opts.externalId !== undefined && { external_id: opts.externalId }),
|
|
546
|
+
...(opts.version !== undefined && { version: parseInt(opts.version) })
|
|
547
|
+
};
|
|
525
548
|
const res = await client.put(`/leads/${id}`, body);
|
|
526
549
|
printJSON(res.data);
|
|
527
550
|
} catch (err) {
|
|
@@ -1825,6 +1848,61 @@ contractsCmd
|
|
|
1825
1848
|
});
|
|
1826
1849
|
|
|
1827
1850
|
|
|
1851
|
+
contractsCmd
|
|
1852
|
+
.command('esign <id>')
|
|
1853
|
+
.description('Send e-signing email(s) to all pending signatories on a contract')
|
|
1854
|
+
.action(async (id) => {
|
|
1855
|
+
const globalOpts = program.opts();
|
|
1856
|
+
const client = getClient(globalOpts);
|
|
1857
|
+
try {
|
|
1858
|
+
const res = await client.post(`/contracts/${id}/sign`, {});
|
|
1859
|
+
const d = res.data.data;
|
|
1860
|
+
console.log(`Signing emails sent to ${d.sent_to} signatory(ies).`);
|
|
1861
|
+
printJSON(res.data);
|
|
1862
|
+
} catch (err) {
|
|
1863
|
+
handleError(err);
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1867
|
+
contractsCmd
|
|
1868
|
+
.command('esign-status <id>')
|
|
1869
|
+
.description('Check e-signing status for a contract')
|
|
1870
|
+
.action(async (id) => {
|
|
1871
|
+
const globalOpts = program.opts();
|
|
1872
|
+
const client = getClient(globalOpts);
|
|
1873
|
+
try {
|
|
1874
|
+
const res = await client.get(`/contracts/${id}/sign/status`);
|
|
1875
|
+
printJSON(res.data);
|
|
1876
|
+
} catch (err) {
|
|
1877
|
+
handleError(err);
|
|
1878
|
+
}
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
contractsCmd
|
|
1882
|
+
.command('export <id>')
|
|
1883
|
+
.description('Export a contract as PDF and get a download URL')
|
|
1884
|
+
.option('--format <format>', 'Export format (pdf)', 'pdf')
|
|
1885
|
+
.option('--output <file>', 'Save PDF to local file (optional)')
|
|
1886
|
+
.action(async (id, opts) => {
|
|
1887
|
+
const globalOpts = program.opts();
|
|
1888
|
+
const client = getClient(globalOpts);
|
|
1889
|
+
try {
|
|
1890
|
+
const res = await client.get(`/contracts/${id}/export`, { params: { format: opts.format } });
|
|
1891
|
+
const d = res.data.data;
|
|
1892
|
+
console.log(`Download URL: ${d.download_url}`);
|
|
1893
|
+
console.log(`Expires: ${d.expires_at}`);
|
|
1894
|
+
if (opts.output) {
|
|
1895
|
+
// Download the PDF and save locally
|
|
1896
|
+
const baseUrl = globalOpts.apiUrl || process.env.SAAC_CRM_API_URL || loadConfig().apiUrl;
|
|
1897
|
+
const dlRes = await axios.get(`${baseUrl.replace('/api/v1', '')}${d.download_url}`, { responseType: 'arraybuffer' });
|
|
1898
|
+
fs.writeFileSync(opts.output, dlRes.data);
|
|
1899
|
+
console.log(`Saved to ${opts.output}`);
|
|
1900
|
+
}
|
|
1901
|
+
} catch (err) {
|
|
1902
|
+
handleError(err);
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1828
1906
|
// ============================================================
|
|
1829
1907
|
// BUDGETS commands — Sprint 5
|
|
1830
1908
|
// ============================================================
|
|
@@ -2162,14 +2240,27 @@ const supportCmd = program.command('support').description('Submit and manage bug
|
|
|
2162
2240
|
.description(`Submit a new ${singular} report`)
|
|
2163
2241
|
.requiredOption('--title <title>', `${singular} title (max 200 chars)`)
|
|
2164
2242
|
.requiredOption('--description <desc>', 'Full description (max 2000 chars)')
|
|
2243
|
+
.option('--priority <p>', 'Priority: low|medium|high|critical (default: medium)', 'medium')
|
|
2244
|
+
.option('--cli-version <v>', 'Your CLI version (e.g. 2.5.1)')
|
|
2245
|
+
.option('--tags <tags>', 'Comma-separated tags (e.g. auth,login,ui)')
|
|
2246
|
+
.option('--severity <s>', type === 'bugs' ? 'Severity: cosmetic|minor|major|critical (bugs only)' : '(ignored for features)')
|
|
2247
|
+
.option('--from-agent-name <name>', 'Agent name for attribution (falls back to config defaultAgentName)')
|
|
2165
2248
|
.action(async (opts) => {
|
|
2166
2249
|
const globalOpts = program.opts();
|
|
2167
|
-
const
|
|
2250
|
+
const agentName = resolveAgentName(opts.fromAgentName);
|
|
2251
|
+
const client = getClient(globalOpts, agentName);
|
|
2168
2252
|
try {
|
|
2169
|
-
const
|
|
2253
|
+
const body = {
|
|
2170
2254
|
title: opts.title,
|
|
2171
|
-
description: opts.description
|
|
2172
|
-
|
|
2255
|
+
description: opts.description,
|
|
2256
|
+
priority: opts.priority
|
|
2257
|
+
};
|
|
2258
|
+
if (opts.cliVersion) body.cli_version = opts.cliVersion;
|
|
2259
|
+
if (opts.tags) body.tags = opts.tags.split(',').map(t => t.trim()).filter(Boolean);
|
|
2260
|
+
if (opts.severity && type === 'bugs') body.severity = opts.severity;
|
|
2261
|
+
// GAP-S10-2: send agent_name in body as well (belt-and-suspenders alongside X-Agent-Name header)
|
|
2262
|
+
if (agentName) body.agent_name = agentName;
|
|
2263
|
+
const res = await client.post(`/support/${type}`, body);
|
|
2173
2264
|
console.log(`${singular.charAt(0).toUpperCase() + singular.slice(1)} submitted. ID: ${res.data.data.id}`);
|
|
2174
2265
|
printJSON(res.data);
|
|
2175
2266
|
} catch (err) {
|
|
@@ -2206,6 +2297,21 @@ const supportCmd = program.command('support').description('Submit and manage bug
|
|
|
2206
2297
|
}
|
|
2207
2298
|
});
|
|
2208
2299
|
|
|
2300
|
+
typeCmd
|
|
2301
|
+
.command('comments')
|
|
2302
|
+
.description(`List all comments on your ${singular} ticket`)
|
|
2303
|
+
.requiredOption('--id <id>', 'Ticket ID')
|
|
2304
|
+
.action(async (opts) => {
|
|
2305
|
+
const globalOpts = program.opts();
|
|
2306
|
+
const client = getClient(globalOpts);
|
|
2307
|
+
try {
|
|
2308
|
+
const res = await client.get(`/support/${type}/${opts.id}/comments`);
|
|
2309
|
+
printJSON(res.data);
|
|
2310
|
+
} catch (err) {
|
|
2311
|
+
handleError(err);
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
|
|
2209
2315
|
typeCmd
|
|
2210
2316
|
.command('comment')
|
|
2211
2317
|
.description(`Add a comment to your own ${singular} ticket`)
|
|
@@ -2224,6 +2330,57 @@ const supportCmd = program.command('support').description('Submit and manage bug
|
|
|
2224
2330
|
handleError(err);
|
|
2225
2331
|
}
|
|
2226
2332
|
});
|
|
2333
|
+
|
|
2334
|
+
typeCmd
|
|
2335
|
+
.command('close')
|
|
2336
|
+
.description(`Close/withdraw your own ${singular} ticket (requires reason)`)
|
|
2337
|
+
.requiredOption('--id <id>', 'Ticket ID')
|
|
2338
|
+
.requiredOption('--reason <reason>', 'Reason for closing')
|
|
2339
|
+
.action(async (opts) => {
|
|
2340
|
+
const globalOpts = program.opts();
|
|
2341
|
+
const client = getClient(globalOpts);
|
|
2342
|
+
try {
|
|
2343
|
+
const res = await client.patch(`/support/${type}/${opts.id}/close`, { reason: opts.reason });
|
|
2344
|
+
console.log('Ticket closed.');
|
|
2345
|
+
printJSON(res.data);
|
|
2346
|
+
} catch (err) {
|
|
2347
|
+
handleError(err);
|
|
2348
|
+
}
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
typeCmd
|
|
2352
|
+
.command('reopen')
|
|
2353
|
+
.description(`Reopen a resolved ${singular} ticket (requires note)`)
|
|
2354
|
+
.requiredOption('--id <id>', 'Ticket ID')
|
|
2355
|
+
.requiredOption('--note <note>', 'Explanation for reopening')
|
|
2356
|
+
.action(async (opts) => {
|
|
2357
|
+
const globalOpts = program.opts();
|
|
2358
|
+
const client = getClient(globalOpts);
|
|
2359
|
+
try {
|
|
2360
|
+
const res = await client.patch(`/support/${type}/${opts.id}/reopen`, { note: opts.note });
|
|
2361
|
+
console.log('Ticket reopened.');
|
|
2362
|
+
printJSON(res.data);
|
|
2363
|
+
} catch (err) {
|
|
2364
|
+
handleError(err);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
typeCmd
|
|
2369
|
+
.command('update')
|
|
2370
|
+
.description(`Add a note to your ${singular} ticket (use when status is needs_more_info)`)
|
|
2371
|
+
.requiredOption('--id <id>', 'Ticket ID')
|
|
2372
|
+
.requiredOption('--note <note>', 'Additional information')
|
|
2373
|
+
.action(async (opts) => {
|
|
2374
|
+
const globalOpts = program.opts();
|
|
2375
|
+
const client = getClient(globalOpts);
|
|
2376
|
+
try {
|
|
2377
|
+
const res = await client.patch(`/support/${type}/${opts.id}/update`, { note: opts.note });
|
|
2378
|
+
console.log('Note added.');
|
|
2379
|
+
printJSON(res.data);
|
|
2380
|
+
} catch (err) {
|
|
2381
|
+
handleError(err);
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2227
2384
|
});
|
|
2228
2385
|
|
|
2229
2386
|
program.parse(process.argv);
|