@dench.com/cli 2.8.1-staging.110 → 2.8.3-staging.117
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/crm.ts +41 -1
- package/dench.ts +92 -14
- package/leads.ts +1035 -0
- package/lib/api-schemas.ts +163 -14
- package/lib/command-registry.ts +105 -2
- package/lib/crm-enrichment.ts +24 -1
- package/lib/enrichment-gateway.ts +6 -0
- package/lib/find-filter-options.generated.ts +206 -0
- package/package.json +2 -1
- package/tools.ts +13 -13
package/crm.ts
CHANGED
|
@@ -659,6 +659,9 @@ const api = {
|
|
|
659
659
|
saveView: makeFunctionReference<"mutation">(
|
|
660
660
|
"functions/crm/objects:saveView",
|
|
661
661
|
),
|
|
662
|
+
renameView: makeFunctionReference<"mutation">(
|
|
663
|
+
"functions/crm/objects:renameView",
|
|
664
|
+
),
|
|
662
665
|
deleteView: makeFunctionReference<"mutation">(
|
|
663
666
|
"functions/crm/objects:deleteView",
|
|
664
667
|
),
|
|
@@ -1621,6 +1624,29 @@ async function runViewsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
1621
1624
|
);
|
|
1622
1625
|
return;
|
|
1623
1626
|
}
|
|
1627
|
+
// Rename keeps the whole view definition (filters, pins, sidebar
|
|
1628
|
+
// slot) and just swaps the name — unlike `update`, which upserts by
|
|
1629
|
+
// name and so cannot change one.
|
|
1630
|
+
case "rename": {
|
|
1631
|
+
const objectName = shift(ctx.args, "object name");
|
|
1632
|
+
const viewName = shift(ctx.args, "view name");
|
|
1633
|
+
const newName = getFlag(ctx.args, "--to") ?? shift(ctx.args, "new name");
|
|
1634
|
+
assertNoUnknownFlags(ctx.args, "crm views rename");
|
|
1635
|
+
if (!newName.trim()) {
|
|
1636
|
+
throw new CrmCliError(
|
|
1637
|
+
'crm views rename requires the new name: dench crm views rename <object> "<view>" "<new name>"',
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
out(
|
|
1641
|
+
ctx,
|
|
1642
|
+
await callMutation(ctx, api.objects.renameView, {
|
|
1643
|
+
objectName,
|
|
1644
|
+
viewName,
|
|
1645
|
+
newName: newName.trim(),
|
|
1646
|
+
}),
|
|
1647
|
+
);
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1624
1650
|
case "delete": {
|
|
1625
1651
|
const objectName = shift(ctx.args, "object name");
|
|
1626
1652
|
const viewName = shift(ctx.args, "view name");
|
|
@@ -3093,6 +3119,13 @@ async function resolveEnrichmentTarget(
|
|
|
3093
3119
|
* Email (no first/last name reached the gateway, so the validator
|
|
3094
3120
|
* rejected the request before any lookup ran).
|
|
3095
3121
|
*/
|
|
3122
|
+
const CONTACT_REQUIRED_FIELDS = new Set([
|
|
3123
|
+
"email",
|
|
3124
|
+
"work_email",
|
|
3125
|
+
"personal_email",
|
|
3126
|
+
"phone",
|
|
3127
|
+
]);
|
|
3128
|
+
|
|
3096
3129
|
async function callEnrichmentGatewayForEntry(args: {
|
|
3097
3130
|
target: EnrichmentTarget;
|
|
3098
3131
|
entry: CrmEntryForEnrichment;
|
|
@@ -3118,7 +3151,11 @@ async function callEnrichmentGatewayForEntry(args: {
|
|
|
3118
3151
|
// Contact cells stay on /person/contact. A LinkedIn URL for a profile
|
|
3119
3152
|
// field (name, title, headline) uses /person so the gateway waterfall
|
|
3120
3153
|
// can fill graph data without a contact job.
|
|
3121
|
-
const isContactField =
|
|
3154
|
+
const isContactField =
|
|
3155
|
+
Boolean(target.column.enrichFields?.length) ||
|
|
3156
|
+
target.column.requiredFields.some((field) =>
|
|
3157
|
+
CONTACT_REQUIRED_FIELDS.has(field),
|
|
3158
|
+
);
|
|
3122
3159
|
if (!isContactField && personBody.linkedinUrl) {
|
|
3123
3160
|
return aviatoEnrichPerson(
|
|
3124
3161
|
{
|
|
@@ -3616,6 +3653,9 @@ Views (saved CRM subsets):
|
|
|
3616
3653
|
dench crm views update <object> <view-name> [same flags as create] [--set-active]
|
|
3617
3654
|
Merges the provided flags over the existing view definition;
|
|
3618
3655
|
untouched keys survive.
|
|
3656
|
+
dench crm views rename <object> <view-name> <new-name>
|
|
3657
|
+
Renames in place: filters, pinned rows, sidebar slot, and the
|
|
3658
|
+
active-view pointer all follow the new name.
|
|
3619
3659
|
dench crm views delete <object> <view-name>
|
|
3620
3660
|
Idempotent; also clears the active-view pointer if it referenced
|
|
3621
3661
|
the deleted view.
|
package/dench.ts
CHANGED
|
@@ -44,7 +44,11 @@ import {
|
|
|
44
44
|
withCurrentSessionSelection,
|
|
45
45
|
withSavedSession,
|
|
46
46
|
} from "./session";
|
|
47
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
fetchConnectedAppsSummary,
|
|
49
|
+
fetchUnipileConnections,
|
|
50
|
+
runToolCommand,
|
|
51
|
+
} from "./tools";
|
|
48
52
|
|
|
49
53
|
type JsonRecord = Record<string, unknown>;
|
|
50
54
|
|
|
@@ -474,9 +478,15 @@ Usage:
|
|
|
474
478
|
dench artifacts list [--limit 25] [--json]
|
|
475
479
|
dench artifacts show <id> [--json]
|
|
476
480
|
dench artifacts import <id> --to lead|company|job_posting [--view "Q3 outbound"] [--pin] [--json]
|
|
477
|
-
dench
|
|
478
|
-
dench
|
|
479
|
-
dench
|
|
481
|
+
dench lists [--limit 25] [--json]
|
|
482
|
+
dench lists show <id> [--json]
|
|
483
|
+
dench lists rename <id> "New name" [--json]
|
|
484
|
+
dench lists update <id> [--title "New name"] [--filters-json '{...}'] [--json]
|
|
485
|
+
dench lists delete <id> [--json]
|
|
486
|
+
dench leads find --titles "VP Sales" [--locations "San Francisco"] [--save-as "Q3 targets"] [--limit 500] [--page-token <token>] [--json] (rich filters: dench leads help)
|
|
487
|
+
dench leads filters [--filter titles|locations|company-hq|...] [--search "query"] [--json]
|
|
488
|
+
dench companies find --name "Acme" [--domain acme.com] [--keywords fintech] [--save-as "Target accounts"] [--limit 500] [--page-token <token>] [--json]
|
|
489
|
+
dench jobs find --titles "Account Executive" [--locations "San Francisco"] [--save-as "AE openings"] [--limit 500] [--page-token <token>] [--json]
|
|
480
490
|
dench suggested-work [--json]
|
|
481
491
|
dench approval request "message" [--json]
|
|
482
492
|
dench approval approve <approvalId> [--evidence "User said yes in chat"] [--json]
|
|
@@ -749,7 +759,25 @@ Usage:
|
|
|
749
759
|
dench suggested-work [--json]
|
|
750
760
|
|
|
751
761
|
\`list\` / \`show\` / \`import\` operate on find results. Bare \`dench artifacts\`
|
|
752
|
-
lists agent suggested-work artifacts.
|
|
762
|
+
lists agent suggested-work artifacts. Rename/delete saved searches with
|
|
763
|
+
\`dench lists rename|delete\`.
|
|
764
|
+
`);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function listsCliHelp() {
|
|
768
|
+
console.log(`Dench lists
|
|
769
|
+
|
|
770
|
+
Usage:
|
|
771
|
+
dench lists [--limit 25] [--json]
|
|
772
|
+
dench lists show <id> [--json]
|
|
773
|
+
dench lists rename <id> "New name" [--json]
|
|
774
|
+
dench lists update <id> [--title "New name"] [--filters-json '{"titles":["CTO"]}'] [--json]
|
|
775
|
+
dench lists delete <id> [--json]
|
|
776
|
+
|
|
777
|
+
Lists are saved searches from Find (people / companies / job postings).
|
|
778
|
+
Create one with \`dench leads|companies|jobs find --save-as "Name"\`.
|
|
779
|
+
Delete removes the list, its exported CSV/JSONL files, and any linked watch.
|
|
780
|
+
CRM lists (saved views) are managed with \`dench crm views ...\`.
|
|
753
781
|
`);
|
|
754
782
|
}
|
|
755
783
|
|
|
@@ -757,7 +785,15 @@ function leadsHelp() {
|
|
|
757
785
|
console.log(`Dench leads
|
|
758
786
|
|
|
759
787
|
Usage:
|
|
760
|
-
dench leads find --titles "VP Sales" --locations "San Francisco" [--company-domain acme.com] [--keywords fintech] [--limit 500] [--page-token <token>] [--json]
|
|
788
|
+
dench leads find --titles "VP Sales" --locations "San Francisco" [--company-domain acme.com] [--keywords fintech] [--save-as "Q3 targets"] [--limit 500] [--page-token <token>] [--json]
|
|
789
|
+
dench leads filters [--filter titles|locations|company-hq|universities|skills|keywords|...] [--search "query"] [--json]
|
|
790
|
+
|
|
791
|
+
Rich filter flags: --past-titles --person-names --seniorities --functions
|
|
792
|
+
--sub-functions --languages --universities --skills --company-names
|
|
793
|
+
--past-company-names --company-domains --company-hq --company-types
|
|
794
|
+
--industries --headcount, plus min:max ranges --years-in-role
|
|
795
|
+
--years-at-company --days-since-job-change --founded-years.
|
|
796
|
+
Run \`dench leads filters\` for accepted enum values and curated suggestions.
|
|
761
797
|
`);
|
|
762
798
|
}
|
|
763
799
|
|
|
@@ -765,7 +801,7 @@ function companiesHelp() {
|
|
|
765
801
|
console.log(`Dench companies
|
|
766
802
|
|
|
767
803
|
Usage:
|
|
768
|
-
dench companies find --name "Acme" [--domain acme.com] [--keywords fintech] [--industries SaaS] [--locations "San Francisco"] [--limit 500] [--page-token <token>] [--json]
|
|
804
|
+
dench companies find --name "Acme" [--domain acme.com] [--keywords fintech] [--industries SaaS] [--locations "San Francisco"] [--save-as "Target accounts"] [--limit 500] [--page-token <token>] [--json]
|
|
769
805
|
`);
|
|
770
806
|
}
|
|
771
807
|
|
|
@@ -773,7 +809,7 @@ function jobsHelp() {
|
|
|
773
809
|
console.log(`Dench jobs
|
|
774
810
|
|
|
775
811
|
Usage:
|
|
776
|
-
dench jobs find --titles "Account Executive" --locations "San Francisco" [--company-domain acme.com] [--keywords remote] [--limit 500] [--page-token <token>] [--json]
|
|
812
|
+
dench jobs find --titles "Account Executive" --locations "San Francisco" [--company-domain acme.com] [--keywords remote] [--save-as "AE openings"] [--limit 500] [--page-token <token>] [--json]
|
|
777
813
|
`);
|
|
778
814
|
}
|
|
779
815
|
|
|
@@ -2735,7 +2771,8 @@ async function resolveGatewayCommandAuth(
|
|
|
2735
2771
|
return {
|
|
2736
2772
|
bearerToken: gatewayKey,
|
|
2737
2773
|
gatewayBaseUrl:
|
|
2738
|
-
envGatewayBaseUrlOverride() ??
|
|
2774
|
+
envGatewayBaseUrlOverride() ??
|
|
2775
|
+
gatewayBaseUrlForCli(body.gatewayBaseUrl),
|
|
2739
2776
|
gatewayHeaders:
|
|
2740
2777
|
body.gatewayHeaders &&
|
|
2741
2778
|
typeof body.gatewayHeaders === "object" &&
|
|
@@ -2879,11 +2916,14 @@ async function connectedAppsContext(runtime: Runtime) {
|
|
|
2879
2916
|
...unipileQueryAuth(runtime),
|
|
2880
2917
|
});
|
|
2881
2918
|
} catch (error) {
|
|
2919
|
+
const unipile = await fetchUnipileConnections(unipileQueryAuth(runtime));
|
|
2882
2920
|
return {
|
|
2883
2921
|
available: false,
|
|
2884
2922
|
reason: errorMessage(error),
|
|
2885
|
-
count:
|
|
2886
|
-
connections:
|
|
2923
|
+
count: unipile.connections.length,
|
|
2924
|
+
connections: unipile.connections,
|
|
2925
|
+
unipileAvailable: unipile.available,
|
|
2926
|
+
...(unipile.error ? { unipileError: unipile.error } : {}),
|
|
2887
2927
|
};
|
|
2888
2928
|
}
|
|
2889
2929
|
}
|
|
@@ -4251,6 +4291,11 @@ async function main() {
|
|
|
4251
4291
|
return;
|
|
4252
4292
|
}
|
|
4253
4293
|
|
|
4294
|
+
if (command === "lists" && (subcommand === "help" || hasFlag("--help"))) {
|
|
4295
|
+
listsCliHelp();
|
|
4296
|
+
return;
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4254
4299
|
if (command === "leads" && (subcommand === "help" || hasFlag("--help"))) {
|
|
4255
4300
|
leadsHelp();
|
|
4256
4301
|
return;
|
|
@@ -4347,8 +4392,14 @@ async function main() {
|
|
|
4347
4392
|
}
|
|
4348
4393
|
|
|
4349
4394
|
if (command === "leads") {
|
|
4350
|
-
const { runLeadsCommand, assertLeadsFindArgs } =
|
|
4395
|
+
const { runLeadsCommand, assertLeadsFindArgs, runFindFiltersCommand } =
|
|
4396
|
+
await import("./leads");
|
|
4351
4397
|
const leadArgs = stripRuntimeFlags(args.slice(args.indexOf("leads") + 1));
|
|
4398
|
+
// `filters` prints bundled catalogs — no session or network needed.
|
|
4399
|
+
if (leadArgs[0] === "filters") {
|
|
4400
|
+
runFindFiltersCommand({ args: leadArgs.slice(1), jsonOutput: json });
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4352
4403
|
assertLeadsFindArgs(leadArgs);
|
|
4353
4404
|
const auth = await resolveFindCommandAuth("dench leads");
|
|
4354
4405
|
await runLeadsCommand({
|
|
@@ -4362,7 +4413,9 @@ async function main() {
|
|
|
4362
4413
|
}
|
|
4363
4414
|
|
|
4364
4415
|
if (command === "companies") {
|
|
4365
|
-
const { runCompaniesCommand, assertCompaniesFindArgs } = await import(
|
|
4416
|
+
const { runCompaniesCommand, assertCompaniesFindArgs } = await import(
|
|
4417
|
+
"./leads"
|
|
4418
|
+
);
|
|
4366
4419
|
const companyArgs = stripRuntimeFlags(
|
|
4367
4420
|
args.slice(args.indexOf("companies") + 1),
|
|
4368
4421
|
);
|
|
@@ -4979,6 +5032,7 @@ async function main() {
|
|
|
4979
5032
|
args.slice(args.indexOf("artifacts") + 1),
|
|
4980
5033
|
);
|
|
4981
5034
|
if (
|
|
5035
|
+
runtime.mode === "dev" ||
|
|
4982
5036
|
runtime.mode === "session" ||
|
|
4983
5037
|
runtime.mode === "apiKey"
|
|
4984
5038
|
) {
|
|
@@ -4987,7 +5041,10 @@ async function main() {
|
|
|
4987
5041
|
convex: runtime.client,
|
|
4988
5042
|
args: artifactArgs,
|
|
4989
5043
|
jsonOutput: json,
|
|
4990
|
-
sessionToken:
|
|
5044
|
+
sessionToken:
|
|
5045
|
+
runtime.mode === "dev"
|
|
5046
|
+
? encodeDevCrmSessionToken(runtime.workspaceArgs)
|
|
5047
|
+
: runtime.sessionToken,
|
|
4991
5048
|
});
|
|
4992
5049
|
if (handled) return;
|
|
4993
5050
|
}
|
|
@@ -4995,6 +5052,27 @@ async function main() {
|
|
|
4995
5052
|
return;
|
|
4996
5053
|
}
|
|
4997
5054
|
|
|
5055
|
+
if (command === "lists") {
|
|
5056
|
+
const runtime = await getRuntime();
|
|
5057
|
+
const listsArgs = stripRuntimeFlags(args.slice(args.indexOf("lists") + 1));
|
|
5058
|
+
const { runListsCommand } = await import("./leads");
|
|
5059
|
+
// Same auth surface as `dench crm`: agent session tokens and unified
|
|
5060
|
+
// API keys hit requireCrmAccess directly; dev mode rides the encoded
|
|
5061
|
+
// dev CRM token so local workspaces work without a signin.
|
|
5062
|
+
const sessionToken =
|
|
5063
|
+
runtime.mode === "dev"
|
|
5064
|
+
? encodeDevCrmSessionToken(runtime.workspaceArgs)
|
|
5065
|
+
: requireAuthenticatedRuntime(runtime, "dench lists").sessionToken;
|
|
5066
|
+
const handled = await runListsCommand({
|
|
5067
|
+
convex: runtime.client,
|
|
5068
|
+
args: listsArgs,
|
|
5069
|
+
jsonOutput: json,
|
|
5070
|
+
sessionToken,
|
|
5071
|
+
});
|
|
5072
|
+
if (!handled) listsCliHelp();
|
|
5073
|
+
return;
|
|
5074
|
+
}
|
|
5075
|
+
|
|
4998
5076
|
if (command === "suggested-work") {
|
|
4999
5077
|
print(await listSuggestedWork(runtime));
|
|
5000
5078
|
return;
|