@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 +1 -1
- package/crm.ts +30 -9
- package/host.ts +1 -1
- package/lib/api-schemas.ts +101 -5
- package/lib/command-registry.ts +103 -1
- package/lib/enrichment-gateway.ts +61 -0
- package/lib/exa-companies.ts +389 -0
- package/lib/exa-people.ts +682 -0
- package/lib/exa-search-constraints.ts +70 -0
- package/package.json +1 -1
- package/search.ts +204 -9
package/README.md
CHANGED
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
|
|
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?:
|
|
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
|
-
|
|
2881
|
-
|
|
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?:
|
|
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://
|
|
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
|
|
package/lib/api-schemas.ts
CHANGED
|
@@ -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
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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
|
package/lib/command-registry.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
// ---------------------------------------------------------------------------
|