conduyt 1.72.0 → 1.73.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/dist/index.js +66 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { addPoolFilterSet, listPoolFilterSets, removePoolFilterSet, updatePoolFilterSet } from "./pool-filter-sets.js";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
|
-
import { Command, CommanderError } from "commander";
|
|
6
|
+
import { Command, CommanderError, Option } from "commander";
|
|
7
7
|
import { ConduytClient, buildQuery } from "./client.js";
|
|
8
8
|
import { minimumGradeOf, minimumGradePatch, parseGradeFlag } from "./message-check-minimum.js";
|
|
9
9
|
import { splitList, stageRulePatch, validateCardFields, validateRuleChange } from "./board-settings.js";
|
|
@@ -2670,57 +2670,104 @@ companies
|
|
|
2670
2670
|
}));
|
|
2671
2671
|
// ---- tags ----
|
|
2672
2672
|
// ---- saved filters (personal, Velocify-style) ----
|
|
2673
|
+
// Smart View icons (app branch fable/smart-view-icons): the 30 current ids in picker order. The API also accepts 17 legacy ids as
|
|
2674
|
+
// deprecated inputs (saved as the mapped new id); they are deliberately not listed here.
|
|
2675
|
+
const SMART_VIEW_ICON_IDS = [
|
|
2676
|
+
"heart", "star", "trophy", "badge", "target", "eye", "clock", "hourglass", "siren", "bolt",
|
|
2677
|
+
"fire", "rain", "funnel", "pyramid", "flag", "pin", "push-pin", "thumbs-up", "thumbs-down", "coffee",
|
|
2678
|
+
"rocket", "number-1", "number-2", "number-3", "number-4", "number-5", "number-6", "number-7", "number-8", "number-9",
|
|
2679
|
+
];
|
|
2680
|
+
const SMART_VIEW_ICON_HELP = (omitted) => `the view's icon, one of (picker order): ${SMART_VIEW_ICON_IDS.join(", ")}. ${omitted}. Legacy icon ids are accepted but saved as the new id; anything else is 400 invalid_icon`;
|
|
2681
|
+
const SAVED_FILTER_ENTITY_ERROR = "saved filters are contact Smart Views; --entity is no longer supported";
|
|
2682
|
+
function rejectEntity(body) {
|
|
2683
|
+
if ("entity" in body || "entityType" in body)
|
|
2684
|
+
throw new Error(SAVED_FILTER_ENTITY_ERROR);
|
|
2685
|
+
}
|
|
2686
|
+
/** saved-filters update/delete take a Smart View id, but the generic routes also edit/delete SHARED views for admin keys: read the view first and
|
|
2687
|
+
* refuse anything that is not one of the caller's personal views (the GET is the only request made in the refusal path). A missing view throws the server's reason. */
|
|
2688
|
+
async function requirePersonalSmartView(client, id) {
|
|
2689
|
+
const res = (await client.get(`/api/v1/smart-views/${encodeURIComponent(id)}`));
|
|
2690
|
+
if (res?.data?.isPersonal !== true) {
|
|
2691
|
+
throw new Error("this is a shared Smart View; saved filters are personal views only (use `conduyt api PATCH /smart-views/<id> --body '{...}'` or `conduyt api DELETE /smart-views/<id>`)");
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2673
2694
|
const savedFilters = program
|
|
2674
2695
|
.command("saved-filters")
|
|
2675
2696
|
.description("Manage YOUR personal saved contact filters (the web app's Saved dropdown; requires the 'saved-filters' key scope)");
|
|
2676
2697
|
savedFilters
|
|
2677
2698
|
.command("list")
|
|
2678
|
-
.description("List your saved filters")
|
|
2679
|
-
.
|
|
2680
|
-
|
|
2699
|
+
.description("List your saved filters: your PERSONAL Smart Views (GET /smart-views, every page, filtered to isPersonal). The printed id is the Smart View id that update and delete take")
|
|
2700
|
+
.action(run(async (client) => {
|
|
2701
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2702
|
+
const first = (await client.get("/api/v1/smart-views?counts=false"));
|
|
2703
|
+
const rows = [...first.data.data];
|
|
2704
|
+
let meta = first.data.meta;
|
|
2705
|
+
let shifted = false;
|
|
2706
|
+
for (let page = 2; meta.has_more && page <= 50; page += 1) {
|
|
2707
|
+
const next = (await client.get(`/api/v1/smart-views?page=${page}&counts=false`));
|
|
2708
|
+
if (next.data.meta.order_stamp !== first.data.meta.order_stamp) {
|
|
2709
|
+
shifted = true;
|
|
2710
|
+
break;
|
|
2711
|
+
}
|
|
2712
|
+
rows.push(...next.data.data);
|
|
2713
|
+
meta = next.data.meta;
|
|
2714
|
+
}
|
|
2715
|
+
if (!shifted)
|
|
2716
|
+
return rows.filter((v) => v.isPersonal === true);
|
|
2717
|
+
}
|
|
2718
|
+
throw new Error("Smart Views kept changing while they were listed; try again.");
|
|
2719
|
+
}));
|
|
2681
2720
|
savedFilters
|
|
2682
2721
|
.command("create")
|
|
2683
|
-
.description("Save a named filter (grouped payload via --filters JSON)")
|
|
2722
|
+
.description("Save a named filter (a personal Smart View, private to you; grouped payload via --filters JSON)")
|
|
2684
2723
|
.option("--name <name>", "display name (required, unique per user, max 60 chars)")
|
|
2685
2724
|
.option("--filters <json>", `grouped filter payload JSON, e.g. [{"conjunction":"AND","rules":[{"field":"source","operator":"equals","value":"facebook"}]}]`)
|
|
2686
2725
|
.option("--sort-field <field>", "sort field (default createdAt)")
|
|
2687
2726
|
.option("--sort-order <dir>", "asc | desc (default desc)")
|
|
2688
|
-
.
|
|
2689
|
-
.option("--
|
|
2727
|
+
.addOption(new Option("--entity <type>", "removed").hideHelp()) // not in the help; passing it exits with SAVED_FILTER_ENTITY_ERROR instead of being ignored
|
|
2728
|
+
.option("--icon <id>", SMART_VIEW_ICON_HELP("Omitted: heart (the default)"))
|
|
2729
|
+
.option("--json <json>", "full JSON body, merged over the flags (may carry `icon`: same ids, same default and same 400 invalid_icon as --icon)")
|
|
2690
2730
|
.action(run(async (client, opts) => {
|
|
2731
|
+
if (opts.entity !== undefined)
|
|
2732
|
+
throw new Error(SAVED_FILTER_ENTITY_ERROR);
|
|
2691
2733
|
const body = {};
|
|
2692
2734
|
if (opts.name !== undefined)
|
|
2693
2735
|
body.name = opts.name;
|
|
2736
|
+
if (opts.icon !== undefined)
|
|
2737
|
+
body.icon = opts.icon;
|
|
2694
2738
|
if (opts.filters !== undefined)
|
|
2695
2739
|
body.filters = jsonArrayArg(opts.filters, "--filters");
|
|
2696
2740
|
if (opts.sortField !== undefined)
|
|
2697
2741
|
body.sortField = opts.sortField;
|
|
2698
2742
|
if (opts.sortOrder !== undefined)
|
|
2699
2743
|
body.sortOrder = opts.sortOrder;
|
|
2700
|
-
if (opts.entity !== undefined)
|
|
2701
|
-
body.entityType = opts.entity;
|
|
2702
2744
|
if (opts.json !== undefined)
|
|
2703
2745
|
Object.assign(body, jsonArg(opts.json, "--json"));
|
|
2746
|
+
rejectEntity(body);
|
|
2704
2747
|
// A --json merge can clobber filters with a non-array; re-assert the
|
|
2705
2748
|
// shape after ALL merges so a malformed payload fails locally.
|
|
2706
2749
|
if (body.filters !== undefined && !Array.isArray(body.filters)) {
|
|
2707
2750
|
throw new Error("filters must be a JSON ARRAY (grouped payload or flat rules).");
|
|
2708
2751
|
}
|
|
2709
|
-
|
|
2752
|
+
// a saved filter IS a personal Smart View (saved-filters write routes retired 2026-09-24, 410): personal:true keeps it private for every key
|
|
2753
|
+
return client.post("/api/v1/smart-views", { ...body, personal: true });
|
|
2710
2754
|
}));
|
|
2711
2755
|
savedFilters
|
|
2712
2756
|
.command("update <id>")
|
|
2713
|
-
.description("Rename a saved filter or replace its filters/sort (owner-only)")
|
|
2757
|
+
.description("Rename a saved filter (a personal Smart View) or replace its filters/sort (owner-only). The id must be one of YOUR personal views (see `saved-filters list`); a shared Smart View id is refused after one GET, nothing is changed (edit a shared view with `conduyt api PATCH /smart-views/<id> --body '{...}'`)")
|
|
2714
2758
|
.option("--name <name>", "new display name")
|
|
2715
2759
|
.option("--filters <json>", "grouped filter payload JSON")
|
|
2716
2760
|
.option("--sort-field <field>", "sort field")
|
|
2717
2761
|
.option("--sort-order <dir>", "asc | desc")
|
|
2718
|
-
.option("--
|
|
2762
|
+
.option("--icon <id>", SMART_VIEW_ICON_HELP("Omitted: unchanged"))
|
|
2763
|
+
.option("--json <json>", "full JSON body, merged over the flags (may carry `icon`: same ids and same 400 invalid_icon as --icon)")
|
|
2719
2764
|
.action(run(async (client, id, opts) => {
|
|
2720
2765
|
assertUuid(id, "saved filter id");
|
|
2721
2766
|
const body = {};
|
|
2722
2767
|
if (opts.name !== undefined)
|
|
2723
2768
|
body.name = opts.name;
|
|
2769
|
+
if (opts.icon !== undefined)
|
|
2770
|
+
body.icon = opts.icon;
|
|
2724
2771
|
if (opts.filters !== undefined)
|
|
2725
2772
|
body.filters = jsonArrayArg(opts.filters, "--filters");
|
|
2726
2773
|
if (opts.sortField !== undefined)
|
|
@@ -2729,18 +2776,21 @@ savedFilters
|
|
|
2729
2776
|
body.sortOrder = opts.sortOrder;
|
|
2730
2777
|
if (opts.json !== undefined)
|
|
2731
2778
|
Object.assign(body, jsonArg(opts.json, "--json"));
|
|
2779
|
+
rejectEntity(body);
|
|
2732
2780
|
// Same post-merge shape assertion as create — see above.
|
|
2733
2781
|
if (body.filters !== undefined && !Array.isArray(body.filters)) {
|
|
2734
2782
|
throw new Error("filters must be a JSON ARRAY (grouped payload or flat rules).");
|
|
2735
2783
|
}
|
|
2736
|
-
|
|
2784
|
+
await requirePersonalSmartView(client, id);
|
|
2785
|
+
return client.patch(`/api/v1/smart-views/${encodeURIComponent(id)}`, body);
|
|
2737
2786
|
}));
|
|
2738
2787
|
savedFilters
|
|
2739
2788
|
.command("delete <id>")
|
|
2740
|
-
.description("Delete one of your saved filters (owner-only)")
|
|
2789
|
+
.description("Delete one of your saved filters (a personal Smart View; owner-only). The id must be one of YOUR personal views (see `saved-filters list`); a shared Smart View id is refused after one GET, nothing is deleted (delete a shared view with `conduyt api DELETE /smart-views/<id>`)")
|
|
2741
2790
|
.action(run(async (client, id) => {
|
|
2742
2791
|
assertUuid(id, "saved filter id");
|
|
2743
|
-
|
|
2792
|
+
await requirePersonalSmartView(client, id);
|
|
2793
|
+
return client.del(`/api/v1/smart-views/${encodeURIComponent(id)}`);
|
|
2744
2794
|
}));
|
|
2745
2795
|
const tags = program.command("tags").description("Manage tags");
|
|
2746
2796
|
tags
|
|
@@ -5233,7 +5283,7 @@ reports
|
|
|
5233
5283
|
.command("deliveries <sid>")
|
|
5234
5284
|
.description("The delivery history of a schedule with download URLs")
|
|
5235
5285
|
.action(run(async (client, sid) => { assertUuid(sid, "schedule id"); return client.get(`/api/v1/reports/subscriptions/${sid}/deliveries`); }));
|
|
5236
|
-
const smartViews = program.command("smart-views").description(
|
|
5286
|
+
const smartViews = program.command("smart-views").description(`Smart Views and their Smart Dialing priorities. Creating or updating a view (conduyt api POST /smart-views, PATCH /smart-views/<id>, or saved-filters create/update) takes an optional icon, one of (picker order): ${SMART_VIEW_ICON_IDS.join(", ")}. heart is the default on create. Legacy icon ids are accepted but saved as the new id; anything else is 400 invalid_icon`);
|
|
5237
5287
|
smartViews
|
|
5238
5288
|
.command("list")
|
|
5239
5289
|
.description("List Smart Views in sidebar order (= Smart Dialing priority) with lead counts, audience (groupId / members / isPersonal) and dialing settings")
|
package/package.json
CHANGED