@dench.com/cli 2.7.5 → 2.7.6-staging.1

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 CHANGED
@@ -198,7 +198,7 @@ For staging:
198
198
 
199
199
  ```bash
200
200
  dench signin --staging
201
- dench signin --host https://workspace-staging.dench.com
201
+ dench signin --host https://staging.dench.com
202
202
  ```
203
203
 
204
204
  ## Sandbox / API key auth (no `dench signin` required)
package/crm.ts CHANGED
@@ -2785,7 +2785,8 @@ type CrmFieldForEnrichment = FieldCandidate & {
2785
2785
  category: EnrichmentCategory;
2786
2786
  key: string;
2787
2787
  apolloPath: string;
2788
- inputFieldName: string;
2788
+ inputFieldName?: string;
2789
+ strategy?: Record<string, unknown>;
2789
2790
  };
2790
2791
  };
2791
2792
 
@@ -2794,6 +2795,8 @@ type CrmEntryForEnrichment = {
2794
2795
  fields: Record<string, unknown>;
2795
2796
  };
2796
2797
 
2798
+ type CliEnrichmentProvider = "fullenrich" | "aviato";
2799
+
2797
2800
  type EnrichmentTarget = {
2798
2801
  category: EnrichmentCategory;
2799
2802
  column: EnrichmentColumnDef;
@@ -2805,7 +2808,7 @@ type CellEnrichmentArgs = {
2805
2808
  objectName: string;
2806
2809
  entryId: string;
2807
2810
  fieldName: string;
2808
- provider?: string;
2811
+ provider?: CliEnrichmentProvider;
2809
2812
  category?: EnrichmentCategory;
2810
2813
  inputFieldName?: string;
2811
2814
  apolloPath?: string;
@@ -2825,6 +2828,16 @@ function parseCategoryFlag(
2825
2828
  throw new CrmCliError("--category must be people or company");
2826
2829
  }
2827
2830
 
2831
+ function parseProviderFlag(
2832
+ value: string | undefined,
2833
+ ): CliEnrichmentProvider | undefined {
2834
+ if (value === undefined) return undefined;
2835
+ if (value === "fullenrich" || value === "aviato") return value;
2836
+ throw new CrmCliError(
2837
+ `--provider must be fullenrich or aviato; received "${value}"`,
2838
+ );
2839
+ }
2840
+
2828
2841
  function parsePositiveInt(
2829
2842
  flag: string,
2830
2843
  value: string | undefined,
@@ -2877,8 +2890,16 @@ function readFieldEnrichment(
2877
2890
  typeof candidate.inputFieldName === "string"
2878
2891
  ? candidate.inputFieldName
2879
2892
  : undefined;
2880
- if (!category || !key || !apolloPath || !inputFieldName) return undefined;
2881
- return { category, key, apolloPath, inputFieldName };
2893
+ const strategy =
2894
+ candidate.strategy &&
2895
+ typeof candidate.strategy === "object" &&
2896
+ !Array.isArray(candidate.strategy)
2897
+ ? (candidate.strategy as Record<string, unknown>)
2898
+ : undefined;
2899
+ if (!category || !key || !apolloPath || (!inputFieldName && !strategy)) {
2900
+ return undefined;
2901
+ }
2902
+ return { category, key, apolloPath, inputFieldName, strategy };
2882
2903
  }
2883
2904
 
2884
2905
  function asEntry(value: unknown): CrmEntryForEnrichment | null {
@@ -3003,7 +3024,7 @@ async function callEnrichmentGatewayForEntry(args: {
3003
3024
  target: EnrichmentTarget;
3004
3025
  entry: CrmEntryForEnrichment;
3005
3026
  /** Explicit provider override. Defaults: people=fullenrich, company=aviato. */
3006
- provider?: string;
3027
+ provider?: CliEnrichmentProvider;
3007
3028
  gateway?: Pick<EnrichmentGatewayOptions, "apiKey" | "baseUrl">;
3008
3029
  }): Promise<Record<string, unknown>> {
3009
3030
  const { entry, target } = args;
@@ -3340,7 +3361,7 @@ async function runPeopleCommand(ctx: CrmCliContext): Promise<void> {
3340
3361
  }
3341
3362
  case "enrich": {
3342
3363
  const entryId = shift(ctx.args, "person entry id");
3343
- const provider = getFlag(ctx.args, "--provider");
3364
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3344
3365
  const fieldName = getFlag(ctx.args, "--field") ?? "LinkedIn URL";
3345
3366
  const inputFieldName = getFlag(ctx.args, "--input-field");
3346
3367
  const overwrite = hasFlag(ctx.args, "--overwrite");
@@ -3405,7 +3426,7 @@ async function runCompaniesCommand(ctx: CrmCliContext): Promise<void> {
3405
3426
  }
3406
3427
  case "enrich": {
3407
3428
  const entryId = shift(ctx.args, "company entry id");
3408
- const provider = getFlag(ctx.args, "--provider");
3429
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3409
3430
  const fieldName = getFlag(ctx.args, "--field") ?? "Company Name";
3410
3431
  const inputFieldName = getFlag(ctx.args, "--input-field") ?? "Domain";
3411
3432
  const overwrite = hasFlag(ctx.args, "--overwrite");
@@ -3438,7 +3459,7 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
3438
3459
  const objectName = shift(ctx.args, "object name");
3439
3460
  const entryId = shift(ctx.args, "entry id");
3440
3461
  const fieldName = shift(ctx.args, "field name");
3441
- const provider = getFlag(ctx.args, "--provider");
3462
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3442
3463
  const inputFieldName = getFlag(ctx.args, "--input-field");
3443
3464
  const category = parseCategoryFlag(getFlag(ctx.args, "--category"));
3444
3465
  const apolloPath = getFlag(ctx.args, "--apollo-path");
@@ -3463,7 +3484,7 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
3463
3484
  const objectName = shift(ctx.args, "object name");
3464
3485
  const fieldName = getFlag(ctx.args, "--field");
3465
3486
  const missingOnly = !hasFlag(ctx.args, "--all");
3466
- const provider = getFlag(ctx.args, "--provider");
3487
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3467
3488
  const inputFieldName = getFlag(ctx.args, "--input-field");
3468
3489
  const category = parseCategoryFlag(getFlag(ctx.args, "--category"));
3469
3490
  const apolloPath = getFlag(ctx.args, "--apollo-path");
package/host.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PRODUCTION_HOST = "https://dench.com";
2
- export const STAGING_HOST = "https://workspace-staging.dench.com";
2
+ export const STAGING_HOST = "https://staging.dench.com";
3
3
  export const LOCAL_HOST = "http://localhost:3000";
4
4
  export const DEFAULT_HOST = PRODUCTION_HOST;
5
5
 
@@ -791,6 +791,12 @@ const exaResultItem = z
791
791
  image: z.string().optional(),
792
792
  favicon: z.string().optional(),
793
793
  id: z.string().optional(),
794
+ entities: z
795
+ .array(z.record(z.string(), z.unknown()))
796
+ .optional()
797
+ .describe(
798
+ "Structured entity metadata returned by category=people or category=company.",
799
+ ),
794
800
  })
795
801
  .passthrough()
796
802
  .describe("An Exa search/contents result (provider passthrough).");
@@ -1058,11 +1064,26 @@ const exaSearch = z.object({
1058
1064
  "financial report",
1059
1065
  "people",
1060
1066
  ])
1061
- .optional(),
1062
- includeDomains: z.array(z.string()).optional(),
1063
- excludeDomains: z.array(z.string()).optional(),
1064
- startPublishedDate: z.string().optional(),
1065
- endPublishedDate: z.string().optional(),
1067
+ .optional()
1068
+ .describe(
1069
+ "Entity categories return structured entities. Domain filters are unsupported for people; published-date filters are unsupported for people and company.",
1070
+ ),
1071
+ includeDomains: z
1072
+ .array(z.string())
1073
+ .optional()
1074
+ .describe("Unsupported for category=people."),
1075
+ excludeDomains: z
1076
+ .array(z.string())
1077
+ .optional()
1078
+ .describe("Unsupported for category=people."),
1079
+ startPublishedDate: z
1080
+ .string()
1081
+ .optional()
1082
+ .describe("Unsupported for category=people and category=company."),
1083
+ endPublishedDate: z
1084
+ .string()
1085
+ .optional()
1086
+ .describe("Unsupported for category=people and category=company."),
1066
1087
  includeText: z.array(z.string()).optional(),
1067
1088
  excludeText: z.array(z.string()).optional(),
1068
1089
  userLocation: z.string().optional(),
@@ -1384,6 +1405,19 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1384
1405
  to: z.string().describe("New object name."),
1385
1406
  }),
1386
1407
  "crm.objects.delete": z.object({ name: z.string() }),
1408
+ "crm.objects.archive": z.object({ name: z.string() }),
1409
+ "crm.objects.unarchive": z.object({ name: z.string() }),
1410
+
1411
+ // The workspace app rail
1412
+ "crm.sidebar.list": z.object({}),
1413
+ "crm.sidebar.move": z.object({
1414
+ navId: z
1415
+ .string()
1416
+ .describe("Nav id from `sidebar list`, e.g. crm-object:task."),
1417
+ position: z
1418
+ .union([z.literal("top"), z.literal("bottom"), z.number().int().min(1)])
1419
+ .describe("Where to land among the visible rows; N is 1-based."),
1420
+ }),
1387
1421
 
1388
1422
  // CRM saved views
1389
1423
  "crm.views.list": z.object({ objectName: z.string() }),
@@ -1424,6 +1458,14 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1424
1458
  objectName: z.string(),
1425
1459
  viewName: z.string(),
1426
1460
  }),
1461
+ "crm.views.archive": z.object({
1462
+ objectName: z.string(),
1463
+ viewName: z.string(),
1464
+ }),
1465
+ "crm.views.unarchive": z.object({
1466
+ objectName: z.string(),
1467
+ viewName: z.string(),
1468
+ }),
1427
1469
 
1428
1470
  // CRM fields
1429
1471
  "crm.fields.list": z.object({ objectName: z.string() }),
@@ -1991,6 +2033,12 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1991
2033
  "tool.search": composioToolSearch,
1992
2034
  "tool.run": composioToolRun,
1993
2035
  "tool.disconnect": z.object({ connectionId: z.string() }),
2036
+ "tool.label.set": z.object({
2037
+ connectionId: z.string(),
2038
+ displayName: z
2039
+ .string()
2040
+ .describe("Human name for the account. Empty clears it."),
2041
+ }),
1994
2042
  };
1995
2043
 
1996
2044
  // ---------------------------------------------------------------------------
@@ -2177,6 +2225,22 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2177
2225
  "crm.objects.update": okResponse,
2178
2226
  "crm.objects.rename": okResponse,
2179
2227
  "crm.objects.delete": okResponse,
2228
+ "crm.objects.archive": okResponse,
2229
+ "crm.objects.unarchive": okResponse,
2230
+
2231
+ // The workspace app rail
2232
+ "crm.sidebar.list": z.object({
2233
+ visible: z.array(z.record(z.string(), z.unknown())),
2234
+ hidden: z.array(z.record(z.string(), z.unknown())),
2235
+ }),
2236
+ "crm.sidebar.move": z.object({
2237
+ ok: z.boolean(),
2238
+ navId: z.string(),
2239
+ /** 1-based slot the row landed in, or null when the move was refused. */
2240
+ position: z.number().nullable(),
2241
+ visibleOrder: z.array(z.string()).optional(),
2242
+ error: z.string().optional(),
2243
+ }),
2180
2244
 
2181
2245
  // CRM saved views
2182
2246
  "crm.views.list": z.object({
@@ -2205,6 +2269,8 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2205
2269
  viewName: z.string(),
2206
2270
  removed: z.boolean(),
2207
2271
  }),
2272
+ "crm.views.archive": okResponse,
2273
+ "crm.views.unarchive": okResponse,
2208
2274
 
2209
2275
  // CRM fields
2210
2276
  "crm.fields.list": z.array(crmFieldDoc),
@@ -2676,6 +2742,15 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2676
2742
  })
2677
2743
  .passthrough()
2678
2744
  .describe("Composio delete response (provider passthrough)."),
2745
+ "tool.labels.list": z.object({
2746
+ labels: z.array(
2747
+ z.object({ composioConnectionId: z.string(), displayName: z.string() }),
2748
+ ),
2749
+ }),
2750
+ "tool.label.set": z.object({
2751
+ ok: z.boolean(),
2752
+ displayName: z.string().nullable(),
2753
+ }),
2679
2754
 
2680
2755
  // Agent config
2681
2756
  "agentConfig.identity.show": agentConfigPayload,
@@ -2714,6 +2789,27 @@ export type ConvexExtras = {
2714
2789
  export const convexExtras: Record<string, ConvexExtras> = {
2715
2790
  "cron.enable": { fixedArgs: { enabled: true } },
2716
2791
  "cron.disable": { fixedArgs: { enabled: false } },
2792
+ // Archive/unarchive are one mutation behind two paths, so the flag comes
2793
+ // from the route rather than the body — a caller cannot un-archive by
2794
+ // POSTing `{ archived: false }` to `/archive`.
2795
+ "crm.objects.archive": { fixedArgs: { archived: true } },
2796
+ "crm.objects.unarchive": { fixedArgs: { archived: false } },
2797
+ // Same mutation for a saved view, but the path calls the object
2798
+ // `objectName` while `setArchived` calls it `name`.
2799
+ "crm.views.archive": {
2800
+ transformArgs: (input) => ({
2801
+ name: input.objectName,
2802
+ viewName: input.viewName,
2803
+ }),
2804
+ fixedArgs: { archived: true },
2805
+ },
2806
+ "crm.views.unarchive": {
2807
+ transformArgs: (input) => ({
2808
+ name: input.objectName,
2809
+ viewName: input.viewName,
2810
+ }),
2811
+ fixedArgs: { archived: false },
2812
+ },
2717
2813
  // PATCH /crm/objects/{objectName}/views/{viewName} — the path's
2718
2814
  // viewName is the upsert key: fold it into `view.name` and force
2719
2815
  // replaceExisting so the mutation overwrites instead of erroring on
@@ -31,6 +31,7 @@ export type CustomOperation = {
31
31
  | "chatFollow"
32
32
  | "cronDelete"
33
33
  | "cronRunNow"
34
+ | "toolDisconnect"
34
35
  | "billingTopup"
35
36
  | "upgrade"
36
37
  | "filesMove"
@@ -583,6 +584,29 @@ const rawApiOperations = [
583
584
  responseSchema: anyResponse,
584
585
  backend: convex("mutation", "functions/crm/objects:remove"),
585
586
  }),
587
+ op({
588
+ id: "crm.objects.archive",
589
+ group: "crm",
590
+ summary:
591
+ "Hide a CRM object's workspace sidebar row. Not a delete — the table keeps working.",
592
+ cli: "dench crm objects archive",
593
+ method: "POST",
594
+ path: "/crm/objects/{name}/archive",
595
+ requestSchema: anyObject,
596
+ responseSchema: anyResponse,
597
+ backend: convex("mutation", "functions/crm/objects:setArchived"),
598
+ }),
599
+ op({
600
+ id: "crm.objects.unarchive",
601
+ group: "crm",
602
+ summary: "Restore a CRM object's workspace sidebar row.",
603
+ cli: "dench crm objects unarchive",
604
+ method: "POST",
605
+ path: "/crm/objects/{name}/unarchive",
606
+ requestSchema: anyObject,
607
+ responseSchema: anyResponse,
608
+ backend: convex("mutation", "functions/crm/objects:setArchived"),
609
+ }),
586
610
 
587
611
  op({
588
612
  id: "crm.views.list",
@@ -652,6 +676,56 @@ const rawApiOperations = [
652
676
  responseSchema: anyResponse,
653
677
  backend: convex("mutation", "functions/crm/objects:deleteView"),
654
678
  }),
679
+ op({
680
+ id: "crm.views.archive",
681
+ group: "crm",
682
+ summary: "Move a pinned view's sidebar row into the Archived flyout.",
683
+ cli: "dench crm views archive",
684
+ method: "POST",
685
+ path: "/crm/objects/{objectName}/views/{viewName}/archive",
686
+ requestSchema: anyObject,
687
+ responseSchema: anyResponse,
688
+ backend: convex("mutation", "functions/crm/objects:setArchived"),
689
+ }),
690
+ op({
691
+ id: "crm.views.unarchive",
692
+ group: "crm",
693
+ summary: "Restore a view's sidebar row (re-asserts its pin).",
694
+ cli: "dench crm views unarchive",
695
+ method: "POST",
696
+ path: "/crm/objects/{objectName}/views/{viewName}/unarchive",
697
+ requestSchema: anyObject,
698
+ responseSchema: anyResponse,
699
+ backend: convex("mutation", "functions/crm/objects:setArchived"),
700
+ }),
701
+
702
+ // The workspace app rail. Row VISIBILITY is team-shared and lives on the
703
+ // CRM records above (archive / view pins); row ORDER is a per-member
704
+ // preference, so `move` needs a caller that resolves to a user — an
705
+ // org-scoped API key cannot reorder somebody's sidebar.
706
+ op({
707
+ id: "crm.sidebar.list",
708
+ group: "crm",
709
+ summary:
710
+ "Describe the workspace sidebar: visible rows in order, plus what is hidden and why.",
711
+ cli: "dench crm sidebar list",
712
+ method: "GET",
713
+ path: "/crm/sidebar",
714
+ requestSchema: noBody,
715
+ responseSchema: anyResponse,
716
+ backend: convex("query", "functions/workspaceNav:describeMySidebar"),
717
+ }),
718
+ op({
719
+ id: "crm.sidebar.move",
720
+ group: "crm",
721
+ summary: "Reposition one sidebar row among the visible rows.",
722
+ cli: "dench crm sidebar move",
723
+ method: "POST",
724
+ path: "/crm/sidebar/move",
725
+ requestSchema: anyObject,
726
+ responseSchema: anyResponse,
727
+ backend: convex("mutation", "functions/workspaceNav:repositionMyNavItem"),
728
+ }),
655
729
 
656
730
  op({
657
731
  id: "crm.fields.list",
@@ -2256,7 +2330,35 @@ const rawApiOperations = [
2256
2330
  path: "/tools/connections/{connectionId}",
2257
2331
  requestSchema: anyObject,
2258
2332
  responseSchema: anyResponse,
2259
- backend: gateway("DELETE", "/composio/connections/{connectionId}"),
2333
+ // Proxies the same gateway route, then drops the account's label; the
2334
+ // name lives in Convex and would otherwise outlive the connection.
2335
+ backend: custom("toolDisconnect", "bearer"),
2336
+ }),
2337
+ // Composio reports no account identity for most toolkits, so a workspace
2338
+ // with three Gmail accounts sees three identical rows. These let a human
2339
+ // name a connection once; the label is presentational and grants nothing.
2340
+ op({
2341
+ id: "tool.labels.list",
2342
+ group: "gateway",
2343
+ summary: "List human names given to connected accounts.",
2344
+ cli: "dench tool labels",
2345
+ method: "GET",
2346
+ path: "/tools/labels",
2347
+ requestSchema: noBody,
2348
+ responseSchema: anyResponse,
2349
+ backend: convex("query", "functions/composioConnectionLabels:list"),
2350
+ }),
2351
+ op({
2352
+ id: "tool.label.set",
2353
+ group: "gateway",
2354
+ summary:
2355
+ "Name a connected account, or clear its name with an empty string.",
2356
+ cli: "dench tool label",
2357
+ method: "PUT",
2358
+ path: "/tools/connections/{connectionId}/label",
2359
+ requestSchema: anyObject,
2360
+ responseSchema: anyResponse,
2361
+ backend: convex("mutation", "functions/composioConnectionLabels:set"),
2260
2362
  }),
2261
2363
 
2262
2364
  // Browser automation (Dench Browser). The provider stays server-side; the
@@ -1215,6 +1215,67 @@ export async function searchPeople(
1215
1215
  });
1216
1216
  }
1217
1217
 
1218
+ /**
1219
+ * Number of Exa candidates to pull when resolving ONE person for enrichment.
1220
+ *
1221
+ * Kept small on purpose. Exa's per-request price includes the first 10
1222
+ * results and charges per result beyond that, and column enrichment issues
1223
+ * one search PER ROW — so a bulk fill over 500 rows is 500 searches. Five is
1224
+ * enough for the match gate to find the right person when they are in the
1225
+ * index, and stays inside the included tier.
1226
+ */
1227
+ export const EXA_ENRICHMENT_NUM_RESULTS = 5;
1228
+
1229
+ /**
1230
+ * Exa People Search through the gateway's `/v1/search` route.
1231
+ *
1232
+ * Unlike the other helpers in this file this is a SEARCH, not an identifier
1233
+ * lookup — it returns relevance-ranked strangers, so callers must run the
1234
+ * result through `matchExaPerson` before writing anything. See
1235
+ * cli/lib/exa-people.ts.
1236
+ *
1237
+ * `type` is pinned to "auto": "deep"/"deep-reasoning" cost roughly 2x and buy
1238
+ * nothing when we are resolving a person we can already name.
1239
+ */
1240
+ export async function searchExaPeople(
1241
+ body: { query: string; numResults?: number },
1242
+ options: EnrichmentGatewayOptions = {},
1243
+ ): Promise<Record<string, unknown>> {
1244
+ return callGatewayJson("/v1/search", {
1245
+ method: "POST",
1246
+ body: {
1247
+ query: body.query,
1248
+ category: "people",
1249
+ type: "auto",
1250
+ numResults: body.numResults ?? EXA_ENRICHMENT_NUM_RESULTS,
1251
+ contents: { highlights: { maxCharacters: 200 } },
1252
+ },
1253
+ options,
1254
+ });
1255
+ }
1256
+
1257
+ /**
1258
+ * Exa Company Search through the gateway's `/v1/search` route. Same
1259
+ * search-not-lookup caveat as `searchExaPeople`: run the result through
1260
+ * `matchExaCompany` before writing anything. See cli/lib/exa-companies.ts.
1261
+ */
1262
+ export async function searchExaCompanies(
1263
+ body: { query: string; numResults?: number },
1264
+ options: EnrichmentGatewayOptions = {},
1265
+ ): Promise<Record<string, unknown>> {
1266
+ return callGatewayJson("/v1/search", {
1267
+ method: "POST",
1268
+ body: {
1269
+ query: body.query,
1270
+ category: "company",
1271
+ type: "auto",
1272
+ numResults: body.numResults ?? EXA_ENRICHMENT_NUM_RESULTS,
1273
+ contents: { highlights: { maxCharacters: 200 } },
1274
+ },
1275
+ options,
1276
+ });
1277
+ }
1278
+
1218
1279
  // ---------------------------------------------------------------------------
1219
1280
  // Shared HTTP helpers
1220
1281
  // ---------------------------------------------------------------------------