conduyt 1.78.0 → 1.80.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/README.md +2 -0
- package/dist/index.js +88 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ conduyt chat mark-read <channelId> <messageId> # readUpTo: marker moves to tha
|
|
|
47
47
|
conduyt notifications list [--page n] [--per-page 1-200] # prints { data, meta }: meta.latestByType = { type: { latest, count } } over the last 24 h, next to unreadCount / unreadByType
|
|
48
48
|
conduyt deals list --pipeline <id> # list deals
|
|
49
49
|
conduyt deals list --health-summary --pipeline <id> # Pipeline health strip for the filtered set (totalOpenDeals, atRiskValue, checks[]); add --health-check stale --health-limit 20 for one check's deals, --health-cursor <nextCursor> to page, --health-rows-only for just the page
|
|
50
|
+
conduyt deals list --board-snapshot --pipeline <id> --board-columns <stageId>[:<limit>],... # every named column of one pipeline from ONE database snapshot (a moved deal is in exactly one column); prints { columns: [{ stageId, limit, deals }], meta: { board_snapshot: true } }; --page / --all / --stage / --stage-name / --health-* refused before any request; 503 board_snapshot_busy or gate_busy exits 1 with Retry-After
|
|
50
51
|
conduyt deals health --pipeline <id> --check needs_action # GET /deals/inspection: the six checks over open deals; --check adds its deals, --cursor/--offset pages
|
|
51
52
|
conduyt deals health-fix <dealId> --check stale --expected-updated-at <iso> --stage-id <uuid> # Pipeline health quick fix (PATCH /deals/{id} with checkOrigin): stale = --stage-id, unassigned = --assigned, missing_close_date = --close-date, missing_contact = --contact; 409 deal_changed / deal_check_cleared and 400 health_fix_mismatch exit 1 verbatim, never retried
|
|
52
53
|
conduyt deals health-next-action <dealId> --check needs_action --expected-updated-at <iso> --title "Call back" --idempotency-key <key> # "Set next action" (POST /tasks with checkOrigin): one non-done quick-fix task per deal, 409 deal_has_next_action / deal_changed / deal_check_cleared exit 1 verbatim
|
|
@@ -65,6 +66,7 @@ conduyt status-metadata list --stage-id <id> # the status codes + buckets atta
|
|
|
65
66
|
conduyt status-metadata import --file rows.json --mode replace # set-based import (max 1000; an item key outside stageId, code, bucket, lifecycleStage, dripEligible, dripTrackName, isTerminal, notes is refused before any request); per-item errors print and exit 1; `--mode replace` with no items is refused unless `--confirm-empty-replace` (it deletes every row). Timeouts: archive 75 s, import and the Mailchimp write / action commands 60 s; on a timeout the server may have applied it, so read the state first
|
|
66
67
|
conduyt integrations mailchimp settings # the Mailchimp audience sync (API key scope integrations); `set | audiences | push | retry-unsubscribes | reconcile-statuses [--restart] | confirm-reconciliation`. Connecting is browser-only (OAuth): do it in the app
|
|
67
68
|
conduyt pipelines update <id> --automation-only true # only automations, API keys and owners / admins move or edit its cards (owner / admin key); also --color <palette hex> and --sort-order
|
|
69
|
+
conduyt automations dry-run <id> [--contact <id>] [--deal <id>] [--company <id>] [--execution <id>] # preview one trigger occurrence without side effects; --deal for a deal occurrence, --company for a company occurrence, --execution (admin or owner only) replays a stored run of this automation and supplies the company its payload carried; no test-node command exists in this CLI
|
|
68
70
|
conduyt search "acme corp" # search across the CRM
|
|
69
71
|
conduyt insights summary # run an AI insight query
|
|
70
72
|
conduyt insights saved_report --report "Lead Activity" --period last_7_days # run any saved report as a tool
|
package/dist/index.js
CHANGED
|
@@ -516,6 +516,38 @@ function rejectBlankFlags(flags) {
|
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
|
+
/** GET /deals board_snapshot=true limits (the app's lib/deal-board-snapshot-limits, contract at 5729bc7b4). */
|
|
520
|
+
const BOARD_SNAPSHOT_MAX_COLUMNS = 50;
|
|
521
|
+
const BOARD_SNAPSHOT_MAX_COLUMN_ROWS = 1000;
|
|
522
|
+
const BOARD_SNAPSHOT_MAX_TOTAL_ROWS = 5000;
|
|
523
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
524
|
+
/** --board-columns <stageId>[:<limit>],...: the server's own rules checked before any request (1-50 columns, UUID stage
|
|
525
|
+
* ids, no stage twice, limit a whole number 1-1000, default --limit, at most 5000 rows in total); the wire value. */
|
|
526
|
+
function parseBoardColumnsFlag(raw, defaultLimit) {
|
|
527
|
+
const entries = raw.split(",").map((e) => e.trim());
|
|
528
|
+
if (raw.trim() === "" || entries.some((e) => e === ""))
|
|
529
|
+
throw new Error("--board-columns must be <stageId>[:<limit>],... with no blank entry.");
|
|
530
|
+
if (entries.length > BOARD_SNAPSHOT_MAX_COLUMNS)
|
|
531
|
+
throw new Error(`--board-columns names at most ${BOARD_SNAPSHOT_MAX_COLUMNS} columns (got ${entries.length}).`);
|
|
532
|
+
const seen = new Set();
|
|
533
|
+
let total = 0;
|
|
534
|
+
const out = [];
|
|
535
|
+
for (const entry of entries) {
|
|
536
|
+
const parts = entry.split(":");
|
|
537
|
+
const id = parts[0].trim();
|
|
538
|
+
if (parts.length > 2 || !UUID_RE.test(id))
|
|
539
|
+
throw new Error(`--board-columns expects <stageId>[:<limit>] entries with a stage UUID; got ${JSON.stringify(entry)}.`);
|
|
540
|
+
const limit = parts.length === 2 ? strictIntFlag(parts[1], `--board-columns limit for ${id}`, 1, BOARD_SNAPSHOT_MAX_COLUMN_ROWS) : defaultLimit;
|
|
541
|
+
if (seen.has(id.toLowerCase()))
|
|
542
|
+
throw new Error(`--board-columns names stage ${id} more than once.`);
|
|
543
|
+
seen.add(id.toLowerCase());
|
|
544
|
+
total += limit;
|
|
545
|
+
out.push(parts.length === 2 ? `${id}:${limit}` : id);
|
|
546
|
+
}
|
|
547
|
+
if (total > BOARD_SNAPSHOT_MAX_TOTAL_ROWS)
|
|
548
|
+
throw new Error(`--board-columns asks for ${total} rows; one snapshot answers at most ${BOARD_SNAPSHOT_MAX_TOTAL_ROWS}. Read deeper columns with --stage.`);
|
|
549
|
+
return out.join(",");
|
|
550
|
+
}
|
|
519
551
|
/** The six Pipeline health checks (GET /deals health_check, GET /deals/inspection check); the API validates the value. */
|
|
520
552
|
const PIPELINE_HEALTH_CHECKS = ["high_value_idle", "stale", "unassigned", "needs_action", "missing_close_date", "missing_contact"];
|
|
521
553
|
deals
|
|
@@ -534,6 +566,8 @@ deals
|
|
|
534
566
|
.option("--health-offset <n>", "with --health-summary and --health-check: deals to skip (default 0); prefer --health-cursor")
|
|
535
567
|
.option("--health-cursor <token>", "with --health-summary and --health-check: continue after this cursor, the nextCursor of the previous page, passed back UNCHANGED (opaque signed token; a blank, forged or foreign one is a 400 with health_cursor_blank / health_cursor_invalid / health_cursor_context / health_cursor_needs_check)")
|
|
536
568
|
.option("--health-rows-only", "with --health-summary and --health-check: answer only that page of the check's deals ({check, offset, items, hasMore, nextCursor}), without the summary")
|
|
569
|
+
.option("--board-snapshot", "read every column in --board-columns from ONE database snapshot (a deal moved during the read is in exactly one column). Needs --pipeline or --pipeline-name and --board-columns; refused (exit 1, no request) with --page, --all, --stage, --stage-name or any --health-* flag. Prints { columns: [{ stageId, limit, deals }], meta: { board_snapshot: true } }: columns in the order named, each column's deals exactly what --stage <stageId> --limit <limit> rows-only answers, no totals; deals.length equal to limit means the column may hold more. A 503 board_snapshot_busy (the snapshot could not finish in its budget: read the columns one by one or retry) or gate_busy (any deals list call: the access check could not finish in time) exits 1 with the code and Retry-After; never retried")
|
|
570
|
+
.option("--board-columns <columns>", `with --board-snapshot: <stageId>[:<limit>],... in answer order; 1-${BOARD_SNAPSHOT_MAX_COLUMNS} live stages of the pipeline, no stage twice, limit 1-${BOARD_SNAPSHOT_MAX_COLUMN_ROWS} (default --limit, itself default 50), at most ${BOARD_SNAPSHOT_MAX_TOTAL_ROWS} rows in total`)
|
|
537
571
|
.action(run(async (client, opts) => {
|
|
538
572
|
// A provided-but-blank filter must error, not silently become "no filter":
|
|
539
573
|
// buildQuery drops empty strings, so a blank flag would never reach the
|
|
@@ -551,6 +585,44 @@ deals
|
|
|
551
585
|
throw new Error(`${flag} was provided but is blank. Omit it to skip the filter, or pass a real value.`);
|
|
552
586
|
}
|
|
553
587
|
}
|
|
588
|
+
// Board snapshot (#116 for app #111, contract 5729bc7b4): one pipeline's columns from ONE database snapshot. Its
|
|
589
|
+
// conflicts are refused here, before any request; the answer is { columns, meta } (no data.data / meta.total), so it
|
|
590
|
+
// never goes through the health branch or the --all / truncation logic below.
|
|
591
|
+
if (opts.boardSnapshot || opts.boardColumns !== undefined) {
|
|
592
|
+
if (!opts.boardSnapshot)
|
|
593
|
+
throw new Error("--board-columns needs --board-snapshot.");
|
|
594
|
+
const conflicts = [
|
|
595
|
+
["--page", opts.page !== undefined], ["--all", opts.all === true],
|
|
596
|
+
["--stage", opts.stage !== undefined], ["--stage-name", opts.stageName !== undefined],
|
|
597
|
+
["--health-summary", opts.healthSummary === true], ["--health-check", opts.healthCheck !== undefined],
|
|
598
|
+
["--health-limit", opts.healthLimit !== undefined], ["--health-offset", opts.healthOffset !== undefined],
|
|
599
|
+
["--health-cursor", opts.healthCursor !== undefined], ["--health-rows-only", opts.healthRowsOnly === true],
|
|
600
|
+
].filter(([, hit]) => hit).map(([flag]) => flag);
|
|
601
|
+
if (conflicts.length > 0) {
|
|
602
|
+
throw new Error(`--board-snapshot cannot be combined with ${conflicts.join(", ")}: it answers every named column's first rows, rows only, from one snapshot.`);
|
|
603
|
+
}
|
|
604
|
+
if (opts.pipeline === undefined && opts.pipelineName === undefined)
|
|
605
|
+
throw new Error("--board-snapshot needs --pipeline or --pipeline-name.");
|
|
606
|
+
if (opts.boardColumns === undefined)
|
|
607
|
+
throw new Error("--board-snapshot needs --board-columns <stageId>[:<limit>],...");
|
|
608
|
+
const perPage = opts.limit === undefined ? 50 : strictIntFlag(opts.limit, "--limit", 1, BOARD_SNAPSHOT_MAX_COLUMN_ROWS);
|
|
609
|
+
const columns = parseBoardColumnsFlag(opts.boardColumns, perPage);
|
|
610
|
+
const usp = new URLSearchParams();
|
|
611
|
+
if (opts.pipeline !== undefined)
|
|
612
|
+
usp.set("pipeline", opts.pipeline);
|
|
613
|
+
if (opts.pipelineName !== undefined)
|
|
614
|
+
usp.set("pipeline_name", opts.pipelineName);
|
|
615
|
+
if (opts.limit !== undefined)
|
|
616
|
+
usp.set("per_page", String(perPage));
|
|
617
|
+
usp.set("board_snapshot", "true");
|
|
618
|
+
usp.set("board_columns", columns);
|
|
619
|
+
const answer = await client.get(`/api/v1/deals?${usp.toString()}`);
|
|
620
|
+
const data = answer?.data;
|
|
621
|
+
if (!data || !Array.isArray(data.columns) || data.meta?.board_snapshot !== true) {
|
|
622
|
+
throw new Error("--board-snapshot: the API answered without { columns, meta: { board_snapshot: true } }. Aborting rather than print another shape.");
|
|
623
|
+
}
|
|
624
|
+
return answer;
|
|
625
|
+
}
|
|
554
626
|
// Pipeline health (app 10-08): a different answer shape (no data.data / meta.total), so it never goes through the
|
|
555
627
|
// --all / truncation logic below. The API validates the check, the paging and the cursor (400 + code).
|
|
556
628
|
const healthFlags = [
|
|
@@ -2469,18 +2541,33 @@ automations
|
|
|
2469
2541
|
}));
|
|
2470
2542
|
automations
|
|
2471
2543
|
.command("dry-run <id>")
|
|
2472
|
-
.description("Test a workflow without side effects")
|
|
2544
|
+
.description("Test a workflow without side effects. A deal occurrence needs --deal and a company occurrence needs --company. --execution (admin or owner only) replays a stored run of this automation and supplies ONE fact, the company carried by that run's payload (same account, automation, trigger event, contact and deal as the preview; runs carrying a deal are hidden from API keys without the deals scope); without it, on a non-company occurrence, the preview is a synthetic sample and company rules evaluate as unknown/absent, which the response says")
|
|
2473
2545
|
.option("--contact <id>", "run against this contact UUID")
|
|
2474
2546
|
.option("--appointment <id>", "seed with this appointment UUID")
|
|
2547
|
+
.option("--deal <id>", "the event's deal UUID: REQUIRED for a deal occurrence (400 without it), 404 for a deal you cannot see, refused (400) on any other occurrence")
|
|
2548
|
+
.option("--company <id>", "the event's company UUID: REQUIRED for a company occurrence (400 without it); on any other occurrence accepted only when it equals the company the --execution payload carries")
|
|
2549
|
+
.option("--execution <id>", "UUID of a stored run of THIS automation to replay (admin or owner only); supplies the company its payload carried, nothing else; it does not replace --company on a company occurrence")
|
|
2475
2550
|
.option("--source <source>", "draft | published (default draft)")
|
|
2476
2551
|
.option("--mode <mode>", "single | all-paths (default single)")
|
|
2477
2552
|
.action(run(async (client, id, opts) => {
|
|
2478
2553
|
assertUuid(id, "automation id");
|
|
2554
|
+
if (opts.deal !== undefined)
|
|
2555
|
+
assertUuid(opts.deal, "--deal");
|
|
2556
|
+
if (opts.company !== undefined)
|
|
2557
|
+
assertUuid(opts.company, "--company");
|
|
2558
|
+
if (opts.execution !== undefined)
|
|
2559
|
+
assertUuid(opts.execution, "--execution");
|
|
2479
2560
|
const body = {};
|
|
2480
2561
|
if (opts.contact !== undefined)
|
|
2481
2562
|
body.contactId = opts.contact;
|
|
2482
2563
|
if (opts.appointment !== undefined)
|
|
2483
2564
|
body.appointmentId = opts.appointment;
|
|
2565
|
+
if (opts.deal !== undefined)
|
|
2566
|
+
body.dealId = opts.deal.trim();
|
|
2567
|
+
if (opts.company !== undefined)
|
|
2568
|
+
body.companyId = opts.company.trim();
|
|
2569
|
+
if (opts.execution !== undefined)
|
|
2570
|
+
body.executionId = opts.execution.trim();
|
|
2484
2571
|
return client.post(`/api/v1/automations/${encodeURIComponent(id)}/dry-run${buildQuery({ source: opts.source, mode: opts.mode })}`, body);
|
|
2485
2572
|
}));
|
|
2486
2573
|
automations
|
package/package.json
CHANGED