@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 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 = Boolean(target.column.enrichFields?.length);
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 { fetchConnectedAppsSummary, runToolCommand } from "./tools";
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 leads find --titles "VP Sales" [--locations "San Francisco"] [--limit 500] [--page-token <token>] [--json]
478
- dench companies find --name "Acme" [--domain acme.com] [--keywords fintech] [--limit 500] [--page-token <token>] [--json]
479
- dench jobs find --titles "Account Executive" [--locations "San Francisco"] [--limit 500] [--page-token <token>] [--json]
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() ?? gatewayBaseUrlForCli(body.gatewayBaseUrl),
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: 0,
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 } = await import("./leads");
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("./leads");
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: runtime.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;